Из однобитной музыки — мяубитную
audioio
либо audiopwmio
, реализующий фоновый звук родными для платы способами. К сожалению, для Meowbit (и вообще для плат на основе STM32) не реализован ни тот ни другой модуль;, но в opensource-проекте это дело поправимое.
Найдите пасхалку в фото
Прежде всего: почему для проигрывания звука есть два разных модуля с полностью одинаковыми API, и на разных платах поддерживается либо тот, либо другой?
Вот как в аудиоредакторе (например Audacity) выглядит ⅒ секунды обычного (16-битного)
WAV-файлa:
Значение плавно меняется в пределах примерно от −0.2 до +0.2 «условной единицы». Если таким же образом менять напряжение, подаваемое на электродинамический громкоговоритель, то так же плавно будет колебаться мембрана — примерно от 0.2 своего максимально возможного отклонения в одну сторону, до 0.2 отклонения в другую сторону. Модуль
audioio
реализует именно такое проигрывание звука — через ЦАП плавно меняет напряжение на выводе, соединённом с динамиком.Но в Meowbit вместо динамика стоит дешёвая пьезопищалка, неспособная отклонять мембрану в промежуточные положения: она из одного крайнего положения очень быстро переходит в другое крайнее, и остаётся там до следующего перехода. Это можно представить как звук с разрешением в один бит на сэмпл:
Таким способом невозможно передавать изменение громкости звука, но теоретически возможно передать все имеющиеся в нём гармоники — если параллельно с 32768-кратным понижением разрешения увеличить во столько же раз (т.е. до сотен мегагерц) частоту дискретизации. Маловероятно, что мембрана пьезопищалки сможет колебаться с такой частотой;, но это можно использовать и в свою пользу — если научиться переключать напряжение на пищалке, когда мембрана на полпути, то можно издавать звуки промежуточной громкости! Поиск по патентам подтверждает, что люди действительно исследуют возможности использовать пьезопищалку таким образом. Мы в эти дебри углубляться не будем, и оставим обычную для WAV частоту дискретизации в десятки килогерц. Для музыки, где основные гармоники в районе килогерца, этого достаточно; речь, однако, превращается в едва разборчивый шум. Можете сравнить, как воспринимается использованный мной восьмисекундный образец звука, воспроизведённый на однобитной пьезопищалке: вначале оригинал, затем однобитная версия, затем запись Meowbit-а микрофоном.
Модуль
audiopwmio
реализует проигрывание звука через цифровой вывод посредством ШИМ: однобитная аудиозапись превращается в последовательность задержек между переключениемвывода на противоположное значение.
Итак, общий план реализации audiopwmio
для Meowbit таков:
- Переводим переданную пользователем аудиозапись в ШИМ-формат (список задержек между переключениями);
- В обработчике прерывания от таймера переключаем вывод и настраиваем таймер на задержку до следующего переключения. Этот код можно с минимальными изменениями позаимствовать из стандартного модуля
pulseio
, реализующего в точности то, что нам нужно — переключение вывода в соответствии с переданным списком задержек –, но не позволяющего коду на Python выполняться параллельно с переключением.
Не сразу было очевидно, что надо позаботиться ещё об одном аспекте реализации — буферизации аудио. Мой тестовый восьмисекундный образец занимает 8·22050·2 ≈ 340 КБ — это втрое больше, чем всё ОЗУ Meowbit; следовательно, загружать его в память придётся по кускам. Стандартная реализация
audiocore.WaveFile
загружает WAV-файл кусочками по 256 байт, что соответствует 128 сэмплам или 5.8 мс времени проигрывания. Это значит, что в среднем каждые 5.8 мс audiopwmio
должен будет запрашивать повторное наполнение буфера; нет выхода, кроме как разместить этот вызов в том же самом обработчике прерывания от таймера — иначе перерисовка экрана может задержать наполнение буфера на добрую сотню миллисекунд. Проблему это, однако, не решает до конца: что произойдёт, если прерывание от таймера случится во время перерисовки экрана? Экран Meowbit подключён через шину SPI, флеш-диск — через неё же, значит обращение ко флешу во время перерисовки экрана всё равно невозможно! В результате получилась реализация audiopwmio
, способная воспроизводить аудиозаписи из памяти (или процедурно генерируемые) в максимально достижимом на Meowbit качестве;, но аудиозаписи из файлов воспроизводятся только при отсутствии одновременных с этим обращений к экрану и ко флешу. Для звукового сопровождения несложных игр этого вполне хватит. PR с моей реализацией уже больше недели дожидается ревью, и когда audiopwmio
для Meowbit появится в официальной версии CircuitPython — неизвестно;, но это не мешает любым желающим самостоятельно скомпилировать себе CircuitPython с моей добавкой.