![]() |
![]() |
||||||||||||
Полифонический MIDI проигрыватель на контроллере AVR
Автор: Engineer_Keen Поздравляем нашего Кота с шестнадцатилетием! Шерсти пушистой и мокрого кончика носа))) Ну а для ушек, вот эта статья... Предисловие В данной статье речь пойдет о несложном с точки зрения схемотехники музыкальном устройстве, которое может воспроизводить файлы стандарта MIDI. Для начала немного истории… Формат MIDI файлов Кратко (на сколько это возможно) опишу формат MIDI-файла. 00 92 3c 6a 08 99 2a 00 00 23 00 44 95 3c 00 00 37 00 01 92 37 00 00 3c 00 0b 98 39 00 01 45 66 Зеленым цветом выделены дельта-времена, красным коды событий, в бирюзовым – данные. Например: $00 – дельта, $92 – взять ноту в канале 2, $3с – номер ноты, $6a – громкость, $08 – дельта, $99 – взять ноту в канале 9, $2a – номер ноты, $00 – громкость (выключить эту ноту), и т.д. Кроме этого есть еще некоторые особенности. Например, дельта-время в файле записывается в формате переменной длины от 0 до 4 байт, я проверил множество своих файлов, нигде не было дельты больше чем в 2 байта, это очень большая задержка! Само управление проигрыванием нот тоже не очень оптимально. По стандарту для взятия и прекращения ноты существуют две отдельные команды – NOTE ON и NOTE OFF, но по факту вместо NOTE OFF часто используется NOTE ON но с нулевой громкостью, помимо этого встречаются ноты с нулевой длительностью. Чтобы избавиться от этой неопределенности нулевые ноты можно игнорировать, а вместо использования пар команд NOTE ON-OFF, использовать одну команду NOTE ON но с дополнительным параметром – длительностью ноты, которая тоже записывается в формате переменной длины размером 1 или 2 байта. В файле есть еще много событий, которые не нужны для воспроизведения, их необходимо исключить перед передачей на проигрыватель. Помимо этого есть еще одна особенность описание которой я нигде не нашел и пришлось вникать в него самому: если следующее событие имеет такой же канал и такой же код как и текущее, то команда этого события пропускается и сразу за дельта-временем идут данные события. Такой вариант приведен во фрагменте выше: после второго события «08 99 2a 00», сразу же по этому каналу выключается нота $23 («23 00»)Сделано это скорее всего с целью уменьшить размер файла, но для нас это только усложнит проигрыватель, так как придется запоминать лишнюю информацию. События, которые используются проигрывателем кроме нот, это: установка темпа, установка громкости, баланса и инструмента, управление эффектом Pitch Wheel. Последнее событие есть далеко не во всех композициях, его смысл состоит в том, чтобы организовать изменение тона уже взятых на канале нот. Отличие можно услышать в следующем примере:
Чтобы разгрузить контроллер от лишней работы по сортировке событий по времени, исключению лишних событий и прочей оптимизации, мы применим уже использованный другими авторами подход – преобразуем мелодию на компьютере специальной программой-конвертером. Конвертер файлов
Кнопка загрузить - открывает файл. Программа поддерживает файлы *.mid формата 0 (1 трек) или 1 (несколько треков), *.rmi и *.kar (файлы с текстами песен для караоке). После открытия события автоматически сортируются по времени, заполняется таблица событий, параметры темпа. В процессе работы программы, в окно лога выводятся некоторые сообщения, в основном диагностического или справочного характера. Например там будет отображаться открытие com-порта и информация о проигрывателе при успешном подключении. В окне «Мелодия» отображаются ноты и события по каждому каналу в графическом виде вместе с названием инструмента. Обратите внимание, в отличии от стандарта MIDI, каналы нумеруются не с 1 по 16, а с 0 по 15! Помимо нот, отображаемых бело-салатовым цветом в графическом виде полосками отображаются и другие события: темно-зеленым – изменения громкости и баланса, красным – изменение инструмента, фиолетовым – эффект Pitch Wheel.Подходящий инструмент из банка проигрывателя автоматически выводится в колонке «инструмент». Если в файле находится событие с именем мелодии, то оно вписывается в окно «Название». Колонка «Инструмент» показывает для каждого канала проигрывателя текущий инструмент, его можно сменить вручную (прямо во время воспроизведения), но данный выбор будет работать только до получения проигрывателем события смены инструмента, смены мелодии или до сброса. Рядом с этой колонкой расположены регуляторы предусилителя, они служат для усиления или подавления определенных каналов, например если кажется что нужно увеличить громкость определенного канала, то нужно передвинуть регулятор для этого канала вправо и переконвертировать мелодию. Окно «Проигрыватель» – управляет работой проигрывателя, и служит для записи в него мелодий. Перед записью или тестовым прослушиванием нужно сконвертировать мелодию, при этом готовая мелодия будет находиться в буфере программы. При нажатии кнопки воспроизведения «из ПК», мелодия будет воспроизводиться проигрывателем с заполнением буфера через COM порт. В этом режиме не стоит открывать новую мелодию или держать нажатой кнопку мыши на каких-либо регуляторах программы, иначе произойдет сбой воспроизведения, т.к. программа не сможет обработать таймер заполнения буфера проигрывателя! При нажатии кнопки воспроизведения «из флеш», будет воспроизведена мелодия, которая находится во флеш-памяти под определенным номером. Буфер проигрывателя при этом заполняется без участия компьютера. Кнопка «Тест» воспроизводит мелодию без использования буферов конвертера или проигрывателя, просто подавая ноты из таблицы по темпу. Такой вариант воспроизведения может быть использован вместо воспроизведения «из ПК», например если программа не успевает заполнять буфер проигрывателя. Любой режим воспроизведения останавливается кнопкой стоп «[]». Кнопка «загрузить» сохраняет мелодию из буфера во флеш-память под определенным номером. Кнопка «список» - выводит список всех мелодий во флеш-памяти. Окно «Отладка» служит, естествено, для целей отладки, а так же для начального форматирования флеш-памяти. По умолчанию, ИМС DataFlash на 2МБ идет с размером страницы в 528 байт, это удобно при использовании каких-либо файловых систем, но не удобно для простой адресации контроллером, поэтому для работы проигрывателя нужно «отформатировать» размер страницы до 512 байт. Это быстрая однократная необратимая процедура, при которой контроллер просто посылает во флеш-память определенную команду. Окно «Клавиатура» – показывает распределение кнопок на клавиатуре ПК, которые можно использовать как MIDI-клавиатуру. При нажатии на них, в проигрыватель посылается команда взять соответствующую ноту (длительностью 250мс). В нижней части можно выбрать октаву, а инструмент выбирается в первой строке колонки «инструмент» окна «мелодия». На рисунке ниже представлено это распределение кнопок на клавишах инструмента. При запуске программы, она попытается открыть COM-порт, использованный в последний раз, после чего попытается подключиться к проигрывателю. Если все пройдет успешно, в лог будет выведено имя проигрывателя, версия ПО и данные о флеш-памяти. Вместе с исполняемым файлом я выложу исходный код конвертора, но он весьма корявый и без комментариев, если кто-то всерьез заинтересеутся его модифицированием, я постараюсь привести его в порядок. ПО Проигрывателя. Синтез звука. Теперь о самом главном, как работает проигрыватель MIDI файлов! Даже простейший синус, обернутый в такую огибающую уже будет больше похож на музыкальный инструмент, чем на обычную пищалку. Таким образом, можно взять всего один период колебания любого (почти) инструмента, и обернув его в огибающую, получить ВСЮ ноту целиком! Так я и поступил в своем проигрывателе. Размер семпла взят в 128 байт, так 16 инструментов поместились всего в 2кБ памяти контроллера. Откуда я взял семплы? Я просто включил редактор MIDI, брал в нем ноту A2 для каждого инструмента (ниже объяснение), параллельно включив запись, запоминал форму огибающей, вырезал из записи все до одного колебания, сохранял и уже бинарным редактором из wav файла копировал 128 байт в виде «0x81,0x8e,0x9c,0xa9…». Эти последовательности сохранялись в inc-файле проекта в виде таблицы констант с директивой «.db». Нота A2 взята не случайно, ее частота – 110Гц. А, так как частота выборки проигрывателя 13951 Гц, то при размере выборки в 128 байт, мы можем получаем сигнал с частотой 109 Гц, очень близкий к ноте A2, это позволяет легко выделить один период колебания нужного размера что в звуковом редакторе, что в бинарном. Дальше необходимо было определить параметры ADSR-огибающей для каждого инструмента. Тут пришлось просто воспроизводить ноту проигрывателем и по осциллографу смотреть результат, сравнивая его с запомненной при редактировании семпла формой. Подогнанные параметры сохранялись в тотже .inc-файл, образуя следующую после семплов таблицу. Структура получилась следующая: по 2 байта для A,D и R составляющих, 1 байт для уровня S, и 1 байт со специальным битом, который говорит о том, что в этом инструменте не используется S-фаза. Т.е. всего по 8 байт на инструмент. Итак, у нас есть нота A2 для каждого из 16 инструментов, но нельзя играть мелодию одной нотой A2!
Вся информация о нотах хранится в массиве NOTE, который делится на слоты – по количеству нот, заданных при компиляции. В процедуре обновления семпла:
Немного остановимся на п.10. В вышеупомянутой статье (Midi player для AVR), был поднят вопрос, касающийся сложения (микширования) нескольких аудиосигналов. С одной стороны, по правилам акустики, чтобы сложить несколько (n) сигналов, нужно хитро складывать их логарифмы. Можно упростить задачу и просто складывать сами семплы, но при этом есть проблема. Чтобы уложиться в динамический диапазон контроллера, нужно разделять громкость поровну между всеми звуками (256/n), при этом, каждый звук будет звучать тише в n-раз. Поэкспериментировав с предыдущими проектами, а так же в этот раз я убедился, что для устройства такого уровня достаточно просто СКЛАДЫВАТЬ семплы между собой, при этом обрезая выход за границы байта. С точки зрения теории это должно выглядеть как обычная перегрузка, но учитывая большое количество умножений с округлением и потерей при этом некоторых бит, а также специфики MIDI-мелодий, сильных искажений я не заметил даже в редакторе, после записи с линейного входа. Выше приведен фрагмент довольно сложной, с точки зрения одновременного воспроизведения множества звуков, мелодии. Как видно пики нигде сильно не обрезаны. На случай появления сильных искажений (перегрузки) можно воспользоваться регуляторами предусилителя в программе-конвертере. ПО Проигрывателя. Воспроизведение мелодии Теперь у нас есть разные инструменты и ими можно играть любую мелодию, чем и займемся. Если воспроизведение происходит по команде конвертера «Тест», то проигрыватель просто воспроизводит ноты, которые ему в определенное время выдает ПК через COM-порт, при этом самим проигрывателем не отмеряются никакие временные интервалы, эта задача полностью лежит на конвертере. В таком режиме проигрыватель можно использовать как музыкальный инструмент, если использовать клавиатуру компьютера или сделать отдельную клавиатуру и соединить её через UART с проигрывателем. При использовании буфера процесс немного сложнее. Ниже представлен фрагмент мелодии в таком виде, в каком он хранится во флеш памяти, таким он и читается в буфер проигрывателя. События в буфере идут один за другим и представляются в таком формате: Команды состоят из непосредственно кода операции (старшие 4 бита, самый старший всегда 1) и канала, к которому относится команда (младшие 4 бита). Ну то есть примерно также как и в файле MIDI. Всего используется 8 команд с соответствующими данными:
Темп представляет из себя количество миллисекунд, на которое умножаются значения дельта-времен и длительности нот. Старший байт этого числа – целая часть, младший – 1/256 часть миллисекунды. Дробное значение используется чтобы увеличить точность временных интервалов. При взятии ноты проверяется несколько условий:
После определения слота под ноту, происходит запись данных ноты в массив NOTE: канал, инструмент, громкость, параметры ADSR и прочее и нота начинает играть. Ниже представлен фрагмен вступления одной из мелодий (Settlers II), третья нота взята повторно (первый пункт в списке условий), поэтому ее огибающая резко прерывается и начинается сначала, причем для этого инструмента баланс лево-право выставлен не посередине. Команды установки громкости, баланса и инструмента являются простыми аналогами таковых из стандарта MIDI. Команда установки эффекта Pitch Wheel отличается от оригинальной из MIDI-файла тем, что значение эффекта зависит от положения соответствующего регулятора в программе-конвертере. Независимо от источника данных (конвертер или флеш-память), при старте воспроизведения происходит первичное заполнение буфера, после чего из него считывается название мелодии (32 байта), которое копируется в буфер экрана, далее из буфера читаются данные мелодии (например длительность), после чего первый раз запускается процедура чтения событий. Первым событием обязательно должна быть команда установки темпа мелодии. В процедуре по очереди читаются все события мелодии, если у события нулевое дельта-время, то оно немедленно выполняется, иначе это дельта-время умножается на темп и сохраняется в переменной, при этом процедура чтения событий прерывается. В программе присутствует процедура таймера, которая вызывается каждую миллисекунду. В этой процедуре дельта-время ноты уменьшается на 1 мс, и при достижении нулевого значения, вновь вызывается процедура чтения событий. После чтения каждого события, буфер автоматически заполняется новыми данными. Размер буфера составляет 128 байт, этого хватает для 99% проверенных мною мелодий. Одно событие может занимать максимум 7 байт если это нота (2 дельта-время + 1 команда + 1 нота + 1 громкость + 2 длительность) и минимум 4, если это события типа громкости или инструмента (2 дельта-время + 1 команда + 1 параметр). Таким образом всего буфера хватает минимум на 18 нот. Самое тяжелое время для буфера это обычно начало мелодии, так как там идет много событий первоначальной настройки каналов (инструмент, громкость, баланс) с минимальным или даже нулевым дельта-временем (см. картинку ниже). (Пока писал статью, немного переделал конвертер, теперь, если это возможно, он расставляет события в начале мелодии равномерно, от нулевого, до первого ненулевого дельта-времени) При воспроизведении из флеш-памяти, буфер заполняется данными по мере вычитывания каждого события. После считывания одного события, несмотря на его размер, в буфере обновляется только 1 байт, это сделано с целью ускорения процедуры чтения в выполнения событий. При этом адрес буфера, из которого читаются данные, неизбежно приближается к адресу, в который данные записываются и если они пересекутся, то произойдет ошибка воспроизведения, т.к. очередной считанный байт будет устаревшим и структура события нарушится. Чтобы этого избежать, процедура обновления буфера вызывается постоянно еще и из главного цикла программы, т.е. когда контроллер ничем больше не занимается. При воспроизведении с использованием COM-порта инициатором заполнения буфера является программа-конвертер, она проверяет и по необходимости обновляет данные в буфере. Если по каким-либо причинам она не успеет обновить буфер (например при зависании компьютера или обрыве связи), то произойдет ошибка воспроизведения. Управлять воспроизведением можно собственными кнопками проигрывателя:
Если при подаче питания была нажата кнопка Play, то проигрыватель сразу начнет играть первую мелодию. В таком варианте устройство можно использовать как музыкальную шкатулку, установив выключатель питания, активируемый при её открывании. Кратко о других модулях проигрывателя. Системный таймер Системный таймер задает все временные интервалы в программе кроме непосредственно вывода семплов. Основной процедурой таймера является обработка интервала в 1 мс, все остальные интервалы получаются из этого интервала через различные делители. С интервалом в 1 мс также вызывается фоновое обновление экрана (после ее запуска раз в 250 мс), и управление светодиодным индикатором статуса. С интервалом в 50 мс, помимо возраста ноты, определяется количество одновременно играющих нот, а так же организовано сканирование клавиатуры и управление длительностью горения индикатора статуса. С интервалом в 250 мс, запускается процедура фонового обновления экрана. С интервалом в 1 секунду происходит мигание статусного индикатора, обновление времени воспроизведения и скроллинг названия мелодии. Модуль UART UART используется для управления работой проигрывателя и загрузки мелодий во флеш-память. Команды, поступающие в проигрыватель всегда имеют размер 41 байт, размеры ответа зависят от конкретной команды, но не более 41 байта. Байты одной команды должны поступать без перерыва более 5 мс, иначе команда будет считаться ошибочной и проигнорируется. Всего поддерживается более 20 различных команд, более подробно они описаны в тексте программы. Модули SPI и DataFlash Эти модули используются для работы с внешней флеш-памятью, интерфейс SPI тактируется с максимально возможной скоростью – в половину тактовой частоты контроллера. Модуль экрана Модуль экрана управляет выводом текстовой информации на экран, если это необходимо. При включении на верхнюю строку выводится имя и версия прошивки. При воспроизведении на этой строке прокручивается имя мелодии, размером до 32 символов. На нижней строке выводится следующая информация: количество одновременно воспроизводящихся нот, счетчик прерванных нот, счетчик времени воспроизведения и длительность мелодии. В прошивке остается свободным еще более килобайта программной памяти и 300 байт ОЗУ, поэтому потенциал для усовершенствования далеко не исчерпан. Исходный код с полными комментариями и готовый HEX приложены к статье. Проект разрабатывался на ассемблере в AVR Studio 4.0. Схемотехника Изначально в схеме был только один контроллер ATMega8, с тактированием от кварца 20МГц, программа занимала всего пару килобайт и на мелодию оставалось целых 6кБ, но это было не очень интересно, т.к. более-менее сложные и длинные мелодии занимали все равно немного больше места. Тогда я решил добавить в схему отдельную микросхему флеш-памяти. Использование памяти типа Atmel DataFlash обусловлено тем, что я с ними уже работал и они были в домашних запасах, естественно после небольшой доработки программы, возможно использование других микросхем SPI-памяти. Принципиальные схемы основных частей проигрывателя приведены на рисунках 1-3. Создание схемы и разводка платы велись в ПО Proteus. Питание Схема блока питания построена на обычных линейных стабилизаторах серии 78xx и 1117 (low-drop), при этом стабилизатор DA2 (3.3В) можно использовать в самом маленьком корпусе, т.к. от него питается только микросхема памяти, а ее потребление не превышает 20мА в худшем случае (при записи). А вот от цепи питания 5В питается помимо контроллера и экрана еще и стереоусилитель, поэтому стабилизатор DA1 лучше брать в корпусе побольше (можно с радиатором). Впрочем, при питании от стабилизированного источника, напряжением 5В, стабилизатор DA1 можно не использовать и установить перемычку JP1. Корпуса DA1 - TO-220, DA2 - sot223. Конденсаторы C1, C2, C3 - тантал, тип B, остальные X7R 0805. Номинальное напряжение С1 зависит от входного и должно быть минимум в 2 раза выше, у С2 и С3 напряжение от 10В. Контроллер Основа устройства – микроконтроллер ATMega8 (можно перенести на ATMega88) в корпусе TQFP, слегка разогнанный до частоты 25 МГц. Кварц на плате типа HC49. Разгон не обязателен, но от тактовой частоты зависит количество одновременно играемых нот. При 20 МГц это 9 нот, при 25 уже 11. Т.е примерно по 2.2МГц на ноту. Конечно помимо вывода звука, контроллер занимается и другими делами: читает мелодию из флеш-памяти, выводит информацию на экран, сканирует кнопки, но все эти функции занимают гораздо меньше времени на обработку. Так как контроллер питается от 5В, а флеш-память от 3.3В, то уровни на линиях SPI лучше всего согласовывать. В линии данных от МК к памяти достаточно установить резисторы, а вот на единственную линию от памяти к МК лучше поставить буфер, т.к. при питании памяти от 3.3В, ее выходные уровни получаются на грани распознавания «0» и «1» для МК с питанием 5В. В качестве буфера используется одногейтовый элемент типа SN74LVC1G125. Дисплей используется с контроллером типа HD44780, двухстрочный, с подключением по 4 битной шине (конкретно в моем случае – Blaze BCB1602-03). На одной из линий данных висит светодиодный индикатор, вывод на экран и управление индикатором разведены во времени программно, поэтому друг другу они работать не мешают. Индикатор мигает раз в секунду, либо при приеме команд по UART. Клавиатура организована матрицей 2х3. Диоды импульсные, например LL4148 в корпусе mimiMELF. Связь с компьютером для тестирования, управления и загрузки мелодий осуществляется через UART, на скорости 115200 бод. Можно использовать переходники USB-UART, COM-UART или BlueTooth адаптеры, в зависимости от имеющихся в компьютере портов. Резисторы и конденсаторы размера 0805, тип конденсаторов любой, кроме C8-C8 - их лучше взять с диэлектриком C0G. Подстроечный резистор RV1 служит для регулировки контраста экрана. Аудиовыходы и усилитель Сигнал в ШИМ-выходов контроллера можно воспроизводить через разные аудиоустройства. Без регулировки громкости, аудиосигнал выведен через делитель на разъем линейного выхода, таким образом устройство можно подключать к любой стереосистеме. После регуляторов громкости (переменные резисторы VL-VR), аудиосигнал выводится на наушники либо на встроенный стереоусилитель небольшой мощности. Фильтры для подавления частоты ШИМ использовать не обязательно, т.к. она во много раз превышает верхний диапазон слышимых человеком частот. Усилитель собран на микросхемах LM4871 в корпусе SOIC-8. Эта микросхема представляет из себя одноканальный усилитель, работающий в мостовом режиме, с выходной мощностью до 3Вт при нагрузке 4Ома. Можно конечно использовать и варианты усилителей попроще, например те же мостовые усилители как в вышеупомянутой статье. Усилитель можно отключить, разомкнув цепь от входов 1 на землю. Конденсатор C20 электролитический на напряжение 16В, на плате предусмотрен его монтаж в SMD или выводном варианте. Конструкция Печатная плата сделана размерами 60х60 мм2. Все SMD-элементы находятся с одной стороны, разъемы и другие элементы для монтажа в отверстие с другой. Проводники с обратной стороны можно выполнить перемычками. В архиве есть PDF файлы с чертежами дорожек и изображением корпусов элементов, файлы для производства (формат gerber) и сам проект для Proteus (схема и плата), поэтому можно менять что угодно. Файл midi_bot_wiresonly.pdf - для варианта односторонней платы (с обратной стороны дорожки - перемычками). Можно переписать программу на воспроизведение из внутренней EEPROM, тогда буферная микросхема, флешка и стабилизатор со своими обвязками не нужны. Вообще, в минимальной конфигурации, например для звонка или музыкальной шкатулки с питанием от батарейки можно оставить только контроллер с кварцем и динамик, тогда всю схему можно вообще собрать навесным монтажем на Меге в DIP-корпусе, конечно в этом случае размер мелодии ограничен 512 байтами внутренней памяти контроллера. Я собрал максимальный вариант схемы с внешней памятью и стереоусилителем, экраном и кнопками. Вот как выглядела плата в редакторе (с текстом в 3D режиме у протеуса обычно проблемы): Сейчас дома химичить возможности нет, мелкие помошники будут пытаться помочь :) Поэтому пришлось заказывать производство платы, вот такая получилась: В передней панели из оргстекла я сделал полторы сотни отверстий, почти ровно... Остальные стенки корпуса - 6мм фанера. Устанавливаем все органы управления, экран и плату на стойках на переднюю панель. И соединяем все проводами (белая коробочка - преобразователь COM-UART, пока разместил внутри коробки): Прошивка При прошивке указать следующие фьюзы (для AVR Studio): В отличии от заводских, изменены: источник тактирования на ВЧ-кварц, BOD на 4В и сохраниение EEPROM (не обязательно). Не забывайте перед изменением и записью прочитать фьюз-биты, если ваша среда для прошивки не делает этого автоматически, иначе есть шанс залочить контроллер. Если все сделано верно, то после прошивки файлом MIDI.HEX, начнет моргать светодиод HL1, а если используется и экран, то на него выведется название устройства. После этого можно подключать проигрыватель к компьютеру, загружать и слушать старые-добрые MIDI-файлы. Пока писал статью вспомнил, как в детстве играл с сестрой в "Угадай мелодию". Если кто помнит, была такая игра для ПК, представляла собой набор файлов с незамысловатыми именами 1.mid, 2.mid,... и т. д. и непосредственно исполняемый файл. Программа была выполнена в стиле одноименной тв-передачи и предлагала для каждой мелодии несколько вариантов ответов. Так вот можно огранизовать нечто похожее с моим проигрывателем, например посадить ведущего за программу-конвертор и пусть воспроизводит какие-нибудь треки, а участники отгадывают. Тут многое конечно зависит еще от изначального качества файла MIDI, но ведь чем сложнее, тем интереснее! Удачи в сборке, вопросы как всегда в форум!
Файлы: Все вопросы в Форум.
|
|
||||||||||||
![]() |
![]() |


![]() |
![]() |
|||
|
||||
![]() |
![]() |