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 и
задайте вопрос там.

# Описание API

## Глобальные переменные

- 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

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) - вернёт новые пиксели после
масштабирования

:resize(w, h, smooth) - вернёт новые пиксели после
изменения размера

:flip(h, v) - создать отображённый по горизонтали
и/или вертикали спрайт (быстрее scale)

:stretch(пиксели, x, y, w, h) - растянуть или сжать
изображение и поместить его в пиксели в указанную
область. быстрее scale

## sys

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

input.mouse() вернёт x, y и mb таблицу состояния
кнопок.

```
local x, y, mb = input.mouse()
if mb.left then...
```

input.keydown(клавиша) - вернёт true если клавиша
нажата.

input.keypress(клавиша) - как keydown, но сработает
один раз (до следующего нажатия).

## thread

thread.start(функция) - запустить поток (вернёт
объект - поток)

Методы потоков:

:wait() - ждать завершения

:read() - прочитать данные

:write() - передать данные

:poll(to) - есть ли пишущий, читающий (два boolean)
на той стороне? to - макс. время ожидания
В качестве данных можно передавать примитивные типы
Lua и пиксели.

Внимание! Поток запускается в чистом контексте Lua, в
котором доступен объект thead для синхронизации и
методы gfx/sys/utf/bit/net/synth. Пример работы с
потоками см. demo/plasmax.lua

## net

net.dial(хост, порт) -- создать tcp соединение и
вернуть неблокирующий сокет

:send(строка, [смещение,[длина]])

:recv()

:close()

Есть библиотека sock, которая может быть включена в
приложение и использована в блокирующем режиме. Пример
использования см. data/apps/irc.lua

## utf

Работа с utf строками.

utf.chars(строка) - вернёт массив символов unicode

utf.next(строка, [индекс]) - вернёт длину символа utf
в байтах (вперёд)

utf.prev(строка, [индекс]) - вернёт длину символа utf
в байтах (назад)

utf.len(строка) - длина строки в символах unicode

utf.codepoint(строка, [индекс]) - вернёт числовое
значение unicode символа в строке

utf.sym(строка, [индекс]) - вернёт один символ unicode
и длину символа в байтах в кодировке utf (как
utf.next)

## bit

Битовые операции (см. документацию по luabitops)

## mixer

mixer - отдельный поток который занимается
проигрыванием мелодий. Для работы с mixer необходимо
сначала загрузить банк с инструментами и банк с
мелодиями.

Звуковые эффекты тоже могут восприниматься как
мелодии.

mixer.voices(данные или файл) - загрузка инструментов
(создавать в voiced);

mixer.new(имя, данные) - загрузка отдельной мелодии;

mixer.songs(данные) - загрузка всех мелодий (создавать
в voiced);

mixer.volume(v) - общая громкость;

mixer.play(имя) - проиграть мелодию (вернёт
идентификатор);

mixer.status(идентификатор) - вернёт позицию
проигрывания или false, если мелодия не проигрывается;

mixer.stop(идентификатор, [время затухания]) -
остановить проигрывание мелодии.

mixer.reserve(число) - зарезервировать каналы для
прямого использования. Если вы собираетесь работать
с каналами напрямую через synth, то эти каналы надо
отобрать у mixer.

## synth

Синтезатор. 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 - громкость коробки

### delay

synth.DELAY_TIME - время в сек (0..1)

synth.DELAY_LEVEL - уровень (0.5)

synth.DELAY_FB - фидбек (0.5)

### dist

synth.DIST_GAIN - сила дисторшена (1)

### filter

synth.FILTER_MODE - тип фильтра

Типы фильтров:

  - lowpass - synth.FILTER_LP (по умолчанию)
  - highpass - synth.FILTER_HP

synth.FILTER_WIDTH - полоса пропускания (0.5)

### synth

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

Внимание! Когда вы работаете в voiced, то все
параметры сохраняют свои имена (за небольшими
исключениями) но переводятся в нижний регистр.

Например:

```
lfo_type triangle
lfo_assign 0 width
```

В режиме трекера вы можете вводить команды между
строками с нотами. Команды не занимают такт,
выполняясь по мере появления одна за другой.
Доступны следующие команды:

@vol <канал> <громкость> -- громкость канала

@pan <канал> <позиция-стерео>

@voice <канал> <имя инструмента>

@tempo <темп> 1 - самый быстрый

@push [счётчик] - начало цикла

@pop - конец цикла

@play <имя мелодии> - проиграть другую мелодию

@tracks <число> - задать число дорожек.

@set <канал> <позиция в стеке> <коробка>
  параметры - изменить параметр коробки.

@synth параметры - частный случай set, меняет
параметр у всех коробок synth на канале.

В качестве канала можно указать -1 или \*, что будет
означать все дорожки композиции.

## sfx

Вспомогательная библиотека (требуется загрузка с
помощью require). Позволяет загружать банки
инструментов и мелодий. Удобно использовать совместно
с synth.

sfx.voices(инструментов) - загрузить банк инструментов.

sfx.apply(канал, имя инструмента) - применить
инструмент на канал.

## dump

Вспомогательная библиотека (требуется загрузка с
помощью require).

dump.save(файл, таблица) -- сохранить данные таблицы в
файл

dump.load(файл) -- загрузить таблицу из файла

## std

Вспомогательная библиотека (требуется загрузка с
помощью 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(имя, [данные]) - чтение, запись файла целиком

## tiny

Вспомогательная библиотека (требуется загрузка с
помощью 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
]]
```
