Главная > AVR > Звуковой синтезатор на ATmega48

Звуковой синтезатор на ATmega48

Мир всем, товарищи! Это опять скучающий я. Кто там меня терял? Возрадуйтесь, друзья мои, ибо ныне я здесь и хочу предложить вниманию общественности свою новую поделку, произведенную на свет по вдохновению и от скуки — синтезатор на ATmega48.

Все началось с того, что я был в существенной степени вдохновлен «маленькой фугой» Баха. Потом пришли выходные, которые, как уже известно, нагоняют на меня скуку более всех остальных дней недели, тем самым подвигая на изготовление разнообразных девайсов. И ныне я решил от нечего делать накидать на макетке маленький синтезатор, играющий маленькую фугу. Итак, к делу!

Сперва я решил определиться с диапазоном будущего инструмента, для чего спросил у мамы (она у меня музыкант ), какие октавы наиболее употребительны в большинстве мелодий. Оказалось, что их три — малая, первая и вторая. Конечно, более всего меня интересовали количественные характеристики нот, т. е. частоты. К счастью, страница со всем необходимым нашлась очень быстро. Оказалось, что самая высокая ожидаемая нота — си второй октавы — имеет частоту всего лишь 988 Гц (приблизительно), так что весь звукоряд можно с успехом генерировать ШИМ ЦАП-ом (да, это будет не прямоугольник, как в большинстве подобных конструкций, а произвольный семпл). Из этих соображений вырисовывается следующая схема:

Аппаратный канал ШИМ подключен к простейшему фильтру — интегрирующей RC-цепочке, имеющей частоту среза около полутора килогерц (на самом деле ставил то, что было под рукой — вышло удачно). Дальше стоит классический двухтактный повторитель, раскачивающий пятидесятиомный динамик (он тоже валялся в закромах с незапамятных времен). С4 удаляет постоянную составляющую, чтобы ничего ненароком не задымилось. Светодиод был воткнут для отладочных целей, да так и оставлен — теперь он отмечает считывание очередной ноты.

Ну вот, теперь можно приступать непосредственно к генерированию звуков. Как уже говорилось, я выбрал WT-синтез — т. е., используется массив выборок, задающий форму сигнала. В принципе, в планах добыть органный семпл и использовать его, но на этапе отладки я зашил обычный синус.

Семпл один, нот много. Как менять высоту тона? Есть два классических метода — частичная выборка из массива при неизменной частоте дискретизации и изменение частоты дискретизации при последовательной выборке. В картинках:

Выбираем каждую вторую точку, чтобы увеличить частоту в два раза:

Увеличиваем частоту дискретизации в два раза, чтобы увеличить частоту в два раза:


Очевидно, что изменять частоту в целое число раз проще всего первым методом. Но отношение нот в октаве не целое. И потому для формирования гаммы не остается ничего иного, как применить второй — частота дискретизации задается таймером, и менять ее можно гораздо свободнее. А вот для перехода между октавами вполне логично применять как раз частичную выборку из массива — ведь частоты нот в соседних октавах различаются именно в два раза. Таким образом, для проигрывания заданной ноты нужна следующая информация:

1. Инкремент счетчика массива. Выбираем каждый элемент — получаем ноту исходной октавы, выбираем каждый второй — получаем ту же ноту на октаву выше, каждый четвертый — на две октавы выше, и т. п.
2. Собственно, номер ноты. Я задал в памяти массив, где по номерам нот записаны значения задержки для таймера, контролирующего вывод значений в ЦАП.
3. Задержка до обработки следующей ноты.

Видно, что семпл нужно брать для самой низкой планируемой октавы, ибо проще всего повышать его частоту. Чтобы понизить ее на октаву, необходима интерполяция, а это уже не так просто.

В самой же октаве надо ореинтироваться на самую высокую ноту, и вот почему. Частота ШИМа составляет примерно 32КГц. Откуда следует, что делать частоту дискретизации выше 16КГц, в общем, бессмысленно. Но изменение частоты дискретизации используется для перехода между нотами в октаве, при этом самая высокая частота соответствует самой высокой ноте. Это и является ограничивающим фактором. Т.е., образцовый семпл надо формировать для си малой октавы. Расчет показывает, что для максимальной частоты дискретизации в районе 16КГц он должен содержать 64 точки.

Итого, я использовал три таймера: восьмибитный T/C0 используется как ШИМ-генератор, T/C2 им управляет (его период задает частоту дискретизации), а шестнадцатибитный T/C1 занимается чтением нот и конфигурированием первых двух таймеров и себя самого (устанавливает инкремент массива семпла, соответствующий октаве, нужный период для T/C2 и для себя самого). Прошивка написана на C в AVR-Studio + WinAVR (AVR-GCC).

Все в сборе (печатку не разрабатывал):

Октав я использую всего три, а нот в каждой их них (считая диезы и бемоли) двенадцать. Т.е., для нумерации и того, и другого можно уложиться в четыре бита, что дает возможность организовать примитивное сжатие, упаковав все это в один байт. А вот под длительность ноты я отдал два честных байта. Т.о., мелодия имеет вид массива, разбитого на блоки по три байта — в старшей тетраде инкремент, соответствующий октаве, в младшей номер ноты. Следующие два байта — длительность в тиках. Маркер конца — три нуля.

Однако очевидно, что руками переводить мелодию в вышеописанный формат довольно затруднительно, поэтому я написал простенький парсер, генерирующий сишный массив из упрощенной латинской записи нот (консольное приложение в Code Blocks + MinGW). Например, начало маленькой фуги выглядит так (тут мне тоже помогла мама ):

1G4 2D4 1Bb4. 1A8 
1G8 1Bb8 1A8 1G8 1F#8 1A8 1D4
1G8 1D8 1A8 1D8 1Bb8 1A16 1G16 1A8 1D8
1G8 1D16 1G16 1A8 1D16 1A16 1Bb8 1A16 1G16 1A16 1D16 2D16 2C16
1Bb16 1A16 1G16 1Bb16 1A16 1G16 1F#16 1A16 1G16 1D16 1G16 1A16 1Bb16 2C16 2D16 2E16
2F16 2E16 2D16 2F16 2E16 2D16 2C#16 2E16 2D8 1A8 2D8 2E8 …

Т.е., <номер октавы><нота>[диез/бемоль]<знаменатель длительности>[точка (увеличение длительности в полтора раза)]

На выходе —

const uint8_t melody[] PROGMEM = {
0x27,0xE3,0x16,
0x42,0xE3,0x16,
0x2A,0x54,0x22,
0x29,0x71,0xB,
0x27,0x71,0xB,
0x2A,0x71,0xB,
0x29,0x71,0xB,
0x27,0x71,0xB,
0x26,0x71,0xB,
0x29,0x71,0xB,
0x22,0xE3,0x16,
0x27,0x71,0xB,
0x22,0x71,0xB,
0x29,0x71,0xB,
0x22,0x71,0xB,
0x2A,0x71,0xB,

Исходники/бинарники прошивки и конвертера.

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

P.S.

Благодаря тов. Episcop‘у я имею возможность вставить видео работы:

Рубрики:AVR
  1. Комментариев нет.
  1. No trackbacks yet.

Добавить комментарий

Заполните поля или щелкните по значку, чтобы оставить свой комментарий:

Логотип WordPress.com

Для комментария используется ваша учётная запись WordPress.com. Выход / Изменить )

Фотография Twitter

Для комментария используется ваша учётная запись Twitter. Выход / Изменить )

Фотография Facebook

Для комментария используется ваша учётная запись Facebook. Выход / Изменить )

Google+ photo

Для комментария используется ваша учётная запись Google+. Выход / Изменить )

Connecting to %s