rein
Простая среда для разработки и запуска программ, написанных на Lua. Для экспериментов, обучения программированию и быстрой разработки.
Цели rein:
- стимулировать творчество за счёт простоты подходов;
- быть независимой от ОС платформой для разработки;
- дать возможность легко экспериментировать;
- применяться для обучения.
Возможности:
- растровая графика rgba (с возможностью работать с палитрой);
- развитый синтез звука;
- поддержка мыши, клавиатуры и геймпадов;
- инструменты разработки разрабатываются как обычные приложения rein;
- поддержка текстовых самодокументируемых форматов графики, музыки, муз. инструментов и шрифтов;
- поддержка потоков;
- работа с tcp/ip;
- возможность работать с форматами ttf/jpg/png.
- pico8/tic80 и другие фэнтези консоли
rein - не фэнтези консоль. Хотя творчество стимулируется простотой подходов, ограничения не вводятся специально. Например, в rein отсутствует привязка к конкретному разрешению, а программы могут работать с файлами напрямую (этот текст пишется в редакторе rein edit). Все существующие (и будущие) инструменты создаются как приложения rein. При этом rein не чужда "ретро" эстетика.
- uxn
rein не является виртуальной архитектурой. Использует Lua в качестве языка программирования. Как и uxn тоже является средой для написания программ не зависимых от особенностей конкретной операционной системы.
- Love2d
rein гораздо проще, но при этом содержит в себе интересные и самобытные решения. Он пытается быть не только полезным, но и интересным для экспериментов и обучения. Например, rein содержит в себе синтезатор.
- INSTEAD
rein появился в результате эволюции: INSTEAD-> reinstead-> rein. rein содержит функции которые было невозможно добавить в INSTEAD и которые были бы неуместны в reinstead. rein сложнее reinstead, но гораздо проще INSTEAD. Кроме того, rein не содержит библиотеки для поддержки игр в жанре "интерактивная литература".
- графика 256x256;
- палитра из 16 цветов (Pico8);
- фиксированный шрифт 7x8.
Программа rein это текстовый файл - Lua-скрипт. Программа может работать с внешними данными и загружать ресурсы из файлов, но предпочтение отдаётся текстовым форматам, которые "вшиваются" прямо в код.
Запуск программы: rein <путь к файлу>.
ВНИМАНИЕ! При запуске rein не переходит в каталог, в котором находится скрипт!
В каталоге data/apps/ находятся скрипты, которые rein может вызывать по имени "приложения". Например:
- edit - редактор;
- irc - irc клиент;
- sprited - редактор графики;
- voiced - редактор инструментов и трекер.
Пример запуска инструмента:
rein edit doc/api-ru.md
Редактор edit представляет из себя простую среду для разработки. Вы можете запускать файл на выполнение, нажав F5. Запускать редакторы графики (F8) и музыки (F9).
Внимание! Для возврата в редактор воспользуйтесь shift-esc.
Редактор графики и музыки ищет в тексте скрипта строчки:
local __spr__ = ... -- спрайты
local __map__ = ... -- карта
local __voices__ = .. -- инструменты
local __songs__ = ... -- мелодии
Если такие фрагменты присутствуют, то они будут загружены в редакторы графики/музыки (f8/f9) и встроены обратно при сохранении. Вы также можете работать с редакторами и независимо от edit, копируя нужные данные вручную, или загружая их из файлов.
Попробуйте:
rein edit demo/aadv.lua
Нажмите F8, поменяйте какой-то спрайт, нажмите на название файла для сохранения, затем shift-esc чтобы вернуться в редактор и F5, чтобы увидеть свои изменения! Потом, shift-esc чтобы снова вернуться в редактор.
В редакторе помощь доступна по F1. В редакторах графики/музыки - нажмите на "?".
Выполните: rein irc чтобы подключиться к чату #rein и задайте вопрос там.
- ARGS - массив с аргументами. Первый элемент - путь к выполняемому скрипту;
- DATADIR - путь к каталогу data/ rein;
- VERSION - версия rein (строка);
- PLATFORM - название системы в которой запущена игра ('Linux', 'Windows' и т.п.);
- LANGUAGE - язык установленый в системе (например 'ru');
- screen - объект-пиксели экрана;
- font - системный шрифт;
- sys - модуль системных функций;
- gfx - функции работы с графикой;
- synth - функции работы с синтезатором;
- mixer - функции работы с микшером;
- input - устройства ввода;
- thread - работа с потоками;
- utf - работа со строками UTF-8;
- net - TCP/IP.
При работе с gfx функциями в качестве цвета может быть указано:
- { r, g, b, a }, например { 127, 127, 0, 255 }. Если a (прозрачность) не указана, то подразумевается 255.
- число-индекс цвета в палитре.
Палитра может содержать 256 цветов (0..255). -1 - означает прозрачный цвет.
Иногда в качестве "цвета" можно указать пиксели (заливка шаблоном).
gfx.pal(число, цвет) -- установить цвет палитры
local r, g, b, a = gfx.pal(цвет) -- взять цвет из палитры и вернуть r, g, b, a
gfx.new(w, h) -- создать объект с пикселями WxH
gfx.new(файл) -- создать объект с пикселями из файла
gfx.new [[многострочный текст]] -- создать объект с пикселями из текстового формата.
Например, напишите в edit строчку:
local pic = gfx.new [[ - ]]
Выделите "-" между [[ ]] и нажмите F8. Нарисуйте что-нибудь в редакторе, и нажмите на название файла справа внизу. Затем, shift-esc. Вы увидите встроенную картинку.
gfx.icon(пиксели) -- сменить иконку приложения
gfx.win(w, h) -- изменить/задать разрешение экрана. По умолчанию размер области 256x256. Размер шрифта выбирается автоматически (pico8, 7x8, 7x10), однако для более высоких разрешений может понадобиться другой "системный" шрифт. В таком случае, его можно задать 3м параметром:
gfx.win(w, h, 'myfont.ttf', 12)
gfx.win(пиксели) -- заменить экран на другой (вернёт старый экран)
gfx.font(файл) -- загрузить шрифт (.ttf или .fnt) -- вы можете загружать и использовать свои не системные шрифты в любое время. Формат .fnt это простой текстовый формат. См. data/fonts. gfx.font() вернёт объект типа font, с которым можно работать.
Методы объекта "шрифт":
:size(текст) - вернёт размер области, который будет занимать данный текст при отрисовке;
:text(текст, цвет) - вернёт пиксели - отрисованный текст.
Системный шрифт
Системный шрифт доступен как font и не доступен для изменения. Он используется для выдачи диагностических сообщений. Кроме того, вам доступна функция gfx.print. Она рисует текст в screen системным шрифтом.
gfx.print("текст", [x, y, цвет])
gfx.printf(x, y, цвет, "форматная строка", ...)
gfx.flip(время) - обновлять экран с заданной частотой
Все изменения экранной области осуществляются в теневом буфере, чтобы "показать" его используется gfx.flip(). Обычно, цикл программы выглядит так:
while sys.running() do
-- ввод через sys.input()
-- изменение изображения на экране
gfx.flip(1/50) -- поддерживаем частоту 50Hz!
end
sys.running() - возвращает true, пока пользователь не выполняет выход из программы.
gfx.flip(1/50) сама поддерживает нужную частоту кадров за счёт ожидания. Это нормальное поведение для игр, однако для более низких частот это может быть проблемой, так как пока flip "спит" пользователь может осуществлять ввод. Для этого есть специальный режим: прерывать сон при вводе.
gfx.flip(1/10, true) - в таком случае, сон будет прерываться во время ввода.
gfx.framedrop() - gfx.flip() может пропускать кадры если не успевает достичь нужной частоты кадров. В этом случае нет смысла рисовать кадр, ведь он всё-равно не будет отрисован. Типовой сценарий использования:
if not gfx.framedrop() then -- узнать, будет ли
-- пропущен кадр?
draw() -- отрисовка кадра
end
gfx.flip(1/60)
gfx.render() - отрисовать кадр немедленно и безусловно. Не спит, не обрабатывает ввод.
gfx.border(цвет) - задать цвет бордюра
gfx.fg(цвет) - цвет по умолчанию для системных нужд (gfx.print)
gfx.bg(цвет) - цвет по умолчанию для системных нужд (gfx.print)
gfx.loadmap(текст или файл) -- чтение карты спрайтов
Карту спрайтов вы можете посмотреть на примере:
rein edit demo/aadv.lua затем f8, затем "m".
gfx.spr(пиксели, nr, x, y, [w, [h, [flipx, [flipy]]]])
- рисование спрайта из атласа спрайтов по 8x8
Атлас спрайтов это просто большая картинка в которой размещены изображения в сетке 8x8. Изображения нумеруются последовательно слева-направо и сверху-вниз.
Так gfx.spr(spr, 2, 128, 128) берёт 3е (0.. 1.. 2) изображение из атласа и рисует его по координатам 128, 128.
Внимание! При работе в sprited предполагается, что атлас занимает 256 пикселей по ширине!
Экран представлен объектом с пикселями screen. Нет разницы работаете ли вы с загруженными пикселями или с экраном. Все методы доступные для пикселей так же точно доступны и для scree. Например: screen:clear(15)
Методы пикселей
Данные методы могут выполняться на любых пикселях. Например, на экране или изображениях, загруженных gfx.new.
:val(x, y) - получить значения r, g, b, a
:val(x, y, цвет) - установить цвет
:clip(x1, y1, w, h) - установить границы рисования
:noclip() - убрать границы рисования
:offset(xoff, yoff) -- установить смещение рисования (при рисовании в этот объект к координатам добавятся xoff, yoff)
offset не действует для методов: buff, val.
:nooffset() -- установить смещение рисования в 0, 0
:pixel(x, y, цвет) -- нарисовать пиксель (с учётом альфы)
:pixel(x, y) -- получить значение пикселя
:buff(таблица с числами, [x, y, w, h]) -- быстрое заполнение буфера с пикселями из таблицы. Значение таблицы:
r*0x1000000 + g*0x10000 + b*0x100 + a
:buff() - получить буфер с пикселями
:size() - получить ширину, высоту
:fill([x, y, w, h,] цвет) - заливка цветом
:fill([x, y, w, h,] пиксели) - заливка пикселями
:clear - как fill но просто затирание одного цвета другим, без учёта прозрачности. Быстрее.
:copy([fx, fy, fw, fh, ]пиксели, [x, y]) -- копирование пикселей из одного объекта в другой
:blend -- как copy но с учётом прозрачности
:line(x1, y1, x2, y2, цвет) - линия
:line(x1, y1, x2, y2, пиксели) - линия по трафарету
:lineAA - как line, но с AA
:fill_triangle(x1, y1, x2, y2, x3, y3, цвет или пиксели) - заливка треугольника
:circle(xc, yc, r) - окружность
:circle(xc, yc, пиксели) - окружность по трафарету
:circleAA - как circle, но с AA
:fill_circle(xc, yc, r, цвет или пиксели) - заливка круга
:fill_poly({вершины}, цвет) - заливка полигона
:fill_rect(x1, y1, x2, y2, цвет) - заливка прямоугольника
:fill_rect(x1, y1, x2, y2, пиксели) - заливка прямоугольника пикселями
:poly({вершины}, цвет) - полигон
:poly({вершины}, пиксели) - полигон по трафарету
:polyAA - как poly, но с AA
:rect(x1, y1, x2, y2, цвет) - прямоугольник
:rect(x1, y1, x2, y2, пиксели) - прямоугольник по трафарету
:rectAA - как rect, но с AA
:scale(xs, ys, smooth) - вернёт новые пиксели после масштабирования
:flip(h, v) - создать отображённый по горизонтали и/или вертикали спрайт (быстрее scale)
:stretch(пиксели, x, y, w, h) - растянуть или сжать изображение и поместить его в пиксели в указанную область. быстрее scale
sys.input() - возвращает события ввода. первое значение - тип события, остальные - аргументы.
sys.input(false) - возвращает true если есть события ввода
sys.input(true) - обнуляет очередь ввода Тип события представлен строкой: mousedown, mouseup, mousemotion, keydown, keyup, mousewheel, quit.
sys.time() - время в секундах после запуска игры
sys.title(текст) - задать заголовок окна
sys.log(строка) - записать в диагностический лог
sys.readdir(путь) - прочитать содержимое каталога (вернёт массив строк)
sys.chdir(путь) - сменить каталог
sys.dirname(путь) - вернёт каталог для пути. Может быть использована с ARGS[1] чтобы узнать путь к запущенному скрипту, чтобы прочитать данные, хранящиеся во внешних файлах.
sys.mkdir(путь) - создать каталог
sys.sleep(секунд) - подождать
sys.go(функция) - создать Lua корутину и запустить в рамках ядра, вернёт корутину
sys.stop(корутина) - остановить и убрать из списка планируемых.
sys.exec(файл) - запустить .lua файл или app приложение как корутину. За exec обычно следует вызывать sys.stop или sys.suspend
sys.stop() - остановить текущую корутину
sys.suspend() - текущая корутина убирается из списка планируемых, но после того как список останется пустым (все корутины завершатся), возвращается в него.
sys.yield() - синоним coroutine.yiled()
sys.newrand([зерно]) - создать экземпляр датчика случайных чисел на основе xoshiro128.
Единственный метод созданного sys.newrand датчика случайных чисел это:
:rnd([старт,[конец]])
main = sys.newrand(12345)
main:rnd() -- нормированное случайное от 0 до 1
main:rnd(1, 5) -- от 1 до 5
main:rnd(3) -- от 1 до 3
sys.clipboard([текст]) -- получить или установить текст в буфере обмена
sys.appdir(имя) -- получить каталог для сохранения данных
input.mouse() вернёт x, y и mb таблицу состояния кнопок.
local x, y, mb = input.mouse()
if mb.left then...
input.keydown(клавиша) - вернёт true если клавиша нажата.
input.keypress(клавиша) - как keydown, но сработает один раз (до следующего нажатия).
thread.start(функция) - запустить поток (вернёт объект - поток)
Методы потоков:
:wait() - ждать завершения
:read() - прочитать данные
:write() - передать данные
:poll(to) - есть ли пишущий, читающий (два boolean) на той стороне? to - макс. время ожидания В качестве данных можно передавать примитивные типы Lua и пиксели.
Внимание! Поток запускается в чистом контексте Lua, в котором доступен объект thead для синхронизации и методы gfx/sys/utf/bit/net/synth. Пример работы с потоками см. demo/plasmax.lua
net.dial(хост, порт) -- создать tcp соединение и вернуть неблокирующий сокет
:send(строка, [смещение,[длина]])
:recv()
:close()
Есть библиотека sock, которая может быть включена в приложение и использована в блокирующем режиме. Пример использования см. data/apps/irc.lua
Работа с utf строками.
utf.chars(строка) - вернёт массив символов unicode
utf.next(строка, [индекс]) - вернёт длину символа utf в байтах (вперёд)
utf.prev(строка, [индекс]) - вернёт длину символа utf в байтах (назад)
utf.len(строка) - длина строки в символах unicode
utf.codepoint(строка, [индекс]) - вернёт числовое значение unicode символа в строке
utf.sym(строка, [индекс]) - вернёт один символ unicode и длину символа в байтах в кодировке utf (как utf.next)
Битовые операции (см. документацию по luabitops)
mixer - отдельный поток который занимается проигрыванием мелодий. Для работы с mixer необходимо сначала загрузить банк с инструментами и банк с мелодиями.
Звуковые эффекты тоже могут восприниматься как мелодии.
mixer.voices(данные или файл) - загрузка инструментов (создавать в voiced);
mixer.new(имя, данные) - загрузка отдельной мелодии;
mixer.songs(данные) - загрузка всех мелодий (создавать в voiced);
mixer.volume(v) - общая громкость;
mixer.play(имя) - проиграть мелодию (вернёт идентификатор);
mixer.status(идентификатор) - вернёт позицию проигрывания или false, если мелодия не проигрывается;
mixer.stop(идентификатор, [время затухания]) - остановить проигрывание мелодии.
mixer.reserve(число) - зарезервировать каналы для прямого использования. Если вы собираетесь работать с каналами напрямую через synth, то эти каналы надо отобрать у mixer.
Синтезатор. 32 канала. synth считается низкоуровневым интерфейсом, однако его удобно использовать для более высокого уровня контроля воспроизведения эффектов.
synth.on(канал) - включить канал.
synth.push(канал, имя) - добавит sfx генератор/эффект на канал ("synth", "delay", "dist", "filter"). Первый вызов всегда должен добавлять генератор "synth", иначе на канале просто не будет звука.
synth.drop(канал) - снять все генераторы/эффекты с канала.
synth.vol(канал, громкость) - громкость на канале (нормированная 0-1)
synth.stop([канал]) - остановить и выключить канал (все каналы)
synth.pan(канал, panning) - настроить центр стерео (panning)
synth.chan_change(канал, параметр, [элемент], значение)
synth.change(канал, позиция sfx в стеке, параметр, [элемент], значение)
Эти методы меняют параметры генераторов/эффектов (далее - коробок) на канале. synth.change() позволяет менять параметр адресно с указанием позиции "коробки". Так, позиция 0 это та коробка, которая была первой задвинута в стек с помощью push. Позиция -n означает n с конца. Так, -1 это последняя добавленная коробка с помощью push.
Метод synth.chan_change() изменяет одну и ту же настройку у всех коробок в стеке канала, если эти коробки понимают данную настройку.
Далее перечислены параметры в зависимости от типа коробки.
synth.VOLUME - громкость коробки
synth.DELAY_TIME - время в сек (0..1)
synth.DELAY_LEVEL - уровень (0.5)
synth.DELAY_FB - фидбек (0.5)
synth.DIST_GAIN - сила дисторшена (1)
synth.FILTER_MODE - тип фильтра
Типы фильтров:
- lowpass - synth.FILTER_LP (по умолчанию)
- highpass - synth.FILTER_HP
synth.FILTER_WIDTH - полоса пропускания (0.5)
synth.NOTE_ON <частота в ГЦ> - Эту команду шлёт трекер для каждой ноты.
synth.NOTE_OFF - Эту команду шлёт трекер когда встречает команду "отпустить клавишу".
synth.TYPE <тип> - тип генератора
Возможные типы:
- synth.OSC_SIN
- synth.OSC_SAW
- synth.OSC_SQUARE
- synth.OSC_DSF
- synth.OSC_DSF2
- synth.OSC_NOISE
- synth.OSC_LIN_NOISE
- synth.OSC_BAND_NOISE
- synth.OSC_LIN_BAND_NOISE
synth.SET_FM (0,1) - включить режим частотной модуляции. По умолчанию если включать synth коробки одну за другой, то выход предыдущей коробки складывается с сигналом текущей. Но в режиме SET_FM выход предыдущей коробки (модулятор) складывается с частотой текущей (несущая). Громкость модулятора должна быть высокой (>=1000).
synth.AMP <значение> - сила (скорость нажатия клавиши). Эта команда посылается трекером когда у ноты задана "громкость".
synth.WIDTH - параметр, который для разных типов генераторов может иметь (или не иметь) разное значение.
synth.OFFSET - параметр, который для разных типов генераторов может иметь (или не иметь) разное значение.
synth.ATTACK, synth.DECAY, synth.SUSTAIN, synth.RELEASE - настройки огибающей (ADSR)
synth.SET_SUSTAIN (0, 1) - включить режим, при котором нота звучит пока не пришло событие SYNTH.NOTE_OFF. При этом уровень звучания задаётся synth.SUSTAIN.
synth.SET_GLIDE (0, 1) - включить режим эффекта glide.
synth.GLIDE_RATE (Гц) - задать частоту эффекта glide.
synth.LFO_TYPE <номер LFO>, режим
Всего доступны 4 LFO от 0 до 3. Каждый может работать в следующих режимах:
- synth.LFO_ZERO
- synth.LFO_SIN
- synth.LFO_SAW
- synth.LFO_SQUARE
- synth.LFO_TRIANGLE
- synth.LFO_SEQ
- synth.LFO_LIN_SEQ
synth.LFO_FREQ <номер LFO>, частота
synth.LFO_LOW/synth.LFO_HIGH <номер LFO>, значение - границы в которых будет масштабироваться значение LFO.
synth.LFO_SET_RESET <номер LFO>, (0,1) - сбрасывать ли состояние LFO при NOTE_ON.
synth.LFO_SET_LOOP <номер LFO>, (0,1) - "зацикливать" ли LFO.
synth.LFO_ASSIGN <номер LFO>, параметр - подключить выбранное LFO к одному из параметров:
- synth.OSC_FREQ
- synth.OSC_FMUL
- synth.OSC_AMP
- synth.OSC_WIDTH
- synth.OSC_OFFSET
synth.LFO_SEQ_POS <номер LFO>, позиция - для настройки режимов LFO_SEQ и LFO_LIN_SEQ (секвенсера). Сначала подаётся команда LFO_SEQ_POS с указанием позиции (0-128). Затем происходит запись значения по позиции:
synth.LFO_SEQ_VAL <номер LFO>, значение
synth.LFO_SEQ_SIZE <номер LFO>, длина - настройка длины последовательности секвенсера (от 0 до 128).
synth.FMUL <элемент> <множитель> - множитель частоты
Данный параметр имеет несколько элементов настройки, которые могут задаваться независимо друг от друга. Элементы адресуются:
- synth.OSC_FREQ
- synth.OSC_FMUL
- synth.OSC_AMP
- synth.OSC_WIDTH
- synth.OSC.OFFSET
При задания FMUL частота умноженная на множитель будет складываться с параметром. Таким образом можно добиться изменения тембра в зависимости от ноты.
synth.mix(число сэмплов, громкость) - генерировать данные аудио, вернёт число сэмплов которые удалось записать до переполнения буфера или все сэмплы. Эта функция вызывается из mixer с частотой 100 раз в секунду.
Внимание! Когда вы работаете в voiced, то все параметры сохраняют свои имена (за небольшими исключениями) но переводятся в нижний регистр.
Например:
lfo_type triangle
lfo_assign 0 width
В режиме трекера вы можете вводить команды между строками с нотами. Команды не занимают такт, выполняясь по мере появления одна за другой. Доступны следующие команды:
@vol <канал> <громкость> -- громкость канала
@pan <канал> <позиция-стерео>
@voice <канал> <имя инструмента>
@tempo <темп> 1 - самый быстрый
@push [счётчик] - начало цикла
@pop - конец цикла
@play <имя мелодии> - проиграть другую мелодию
@tracks <число> - задать число дорожек.
@set <канал> <позиция в стеке> <коробка> параметры - изменить параметр коробки.
@synth параметры - частный случай set, меняет параметр у всех коробок synth на канале.
В качестве канала можно указать -1 или *, что будет означать все дорожки композиции.
Вспомогательная библиотека (требуется загрузка с помощью require). Позволяет загружать банки инструментов и мелодий. Удобно использовать совместно с synth.
sfx.voices(инструментов) - загрузить банк инструментов.
sfx.apply(канал, имя инструмента) - применить инструмент на канал.
Вспомогательная библиотека (требуется загрузка с помощью require).
dump.save(файл, таблица) -- сохранить данные таблицы в файл
dump.load(файл) -- загрузить таблицу из файла
Вспомогательная библиотека (требуется загрузка с помощью require). Расширяет стандартные возможности Lua. См. data/lib/std.lua
- math.round(число, точность)
- table.unpack()
- string.empty()
- string.strip()
- string.stripnl()
- string.split()
- string.startswith()
- string.endswith()
- string.lines() - итератор по строкам
- io.file(имя, [данные]) - чтение, запись файла целиком
Вспомогательная библиотека (требуется загрузка с помощью require). Определяет простые функции в "духе" pico-8. См. data/lib/tiny.lua
Кроме привычных ttf/png поддерживаются свои простые текстовые форматы, которые удобно встраивать прямо в код. Например:
local s = gfx.new [[
--*
-*-*-*
*-*-*-
-*-*-*
*-*-*-
]]
В данном случае создастся изображение 6x3. Состоящее из "сеточки" цвета с индексом 2.
Вы можете сделать screen:fill(s) и посмотреть, что будет :)
1я строка текста - соответствия символов ascii в цвета палитры. Например: --* означает, что символом * кодируется цвет 2. 0123456789abcdef -- все 16 цветов кодируются символами от 0 до f Далее идут строки с изображением. - -- прозрачный. Остальные символы соответствуют кодированию палитры.
Формат шрифтов можно посмотреть открыв файл data/fonts/7x8.fnt
0x044F
-------
-------
-####--
#---#--
-####--
--#-#--
-#--#--
-------
Пример формата мелодии:
local __songs__ = [[
song title
@tempo 16
@voice 1 bass
@voice 2 snare
@voice 3 square
@voice 4 saw
@vol -1 0.5
@pan -1 0
@pan 3 -0.75
@pan 4 0.75
| c-3 a0 | ... .. | c-5 .. | c-4 64
| ... .. | ... .. | g-4 45 | ... ..
| c-3 80 | ... .. | c-5 .. | ... ..
| ... .. | ... .. | c-5 45 | ... ..
| ... .. | c-4 80 | d-5 .. | ... ..
| ... .. | ... .. | c-5 45 | ... ..
| ... .. | ... .. | d-5 .. | ... ..
| ... .. | c-4 80 | d-5 45 | ... ..
| c-3 a0 | ... .. | d#5 .. | ... ..
| ... .. | c-4 80 | d-5 45 | ... ..
| c-3 80 | ... .. | d#5 .. | ... ..
| ... .. | ... .. | d#5 45 | ... ..
| ... .. | c-4 80 | f-5 .. | ... ..
| ... .. | ... .. | d#5 45 | ... ..
| ... .. | ... .. | g-5 .. | ... ..
]]
Пример формата инструментов:
local __voices__ = [[
voice bass
box synth
fmul freq 0.5
type square
width 0
decay 0.2
sustain 0
release 0.01
volume 0.5
lfo_assign 0 freq
lfo_type 0 saw
lfo_freq 0 15
lfo_low 0 100
lfo_high 0 -100
lfo_set_loop 0 0
lfo_assign 1 width
lfo_type 1 saw
lfo_freq 1 15
lfo_low 1 0.5
lfo_high 1 -0.5
lfo_set_loop 1 0
voice snare
box synth
type lin_band_noise
volume 0.5
offset 10000
width 10000
fmul freq 0.4
decay 0.15
sustain 0
release 0
lfo_assign 1 freq
lfo_type 1 saw
lfo_freq 1 5
lfo_low 1 9500
lfo_high 1 5000
lfo_set_loop 1 0
box synth
type sin
decay 0.15
sustain 0
release 0
lfo_assign 0 freq
lfo_type 0 saw
lfo_freq 0 10
lfo_low 0 200
lfo_high 0 -70
lfo_set_loop 0 0
]]