esp32-audio-player
Audio player engine for ESP32: FLAC, MP3, Vorbis, WAV, AIFF, DSD, sample-exact gapless, CUE, ReplayGain, AutoEQ parametric EQ, I2S DAC and Bluetooth output
C1пуш
README из репозиторияesp32-audio-player
Движок аудиоплеера для ESP32 одной библиотекой: FLAC, MP3, Ogg Vorbis, WAV, AIFF и DSD с SD-карты, воспроизведение без пауз с точностью до сэмпла, CUE, ReplayGain, 10-полосный параметрический эквалайзер с импортом пресетов AutoEQ, кроссфид, очередь с перемешиванием и продолжением с места остановки, вывод в I2S ЦАП или в Bluetooth-наушники.
English · Справочник API · История изменений

Движок написан на переносимом C17 и собирается также на компьютере. На записи работает консольный плеер из tools/player_cli: два FLAC-файла, разрезанные на сэмпле 22383 внутри FLAC-кадра, играют подряд и совпадают с неразрезанным исходником во всех 44100 сэмплах. Та же проверка идёт в CI (ctest -R gapless_cli).
Состояние
Версию 0.1.0 я ещё не запускал на ESP32. Фото тестовой платы и первые замеры (загрузка процессора по форматам, RAM) я добавлю после проверки на железе.
Что проверено:
- 16 тестовых программ на компьютере с 84981 проверкой проходят на Windows с
zig cc, в CI они идут на Linux с AddressSanitizer и UndefinedBehaviorSanitizer и на macOS. Тесты сверяют каждый декодер с эталонными CRC 29 сгенерированных файлов, гоняют фаззинг по 200 испорченных копий каждого файла, проверяют стыки FLAC и MP3 без пауз, нарезку по CUE, передискретизацию, цепочку DSP, чтение тегов ID3, APE, Vorbis comments, RIFF, AIFF, DSF, DFF, WavPack и MP4, индекс фонотеки на 5000 сгенерированных файлов, очередь и файлы продолжения. - CI собирает примеры для ESP-IDF под ESP32 на ESP-IDF 5.3, 5.5, 6.0 и 6.1, пример с I2S также под ESP32-S3. Предупреждение в коде библиотеки останавливает сборку.
- CI собирает примеры для Arduino на ядре 3.1.3 (ESP-IDF 5.3) и на последнем ядре, а также через PlatformIO. На ядре 3.3.12 скетч SD-карта в I2S занимает 613 КБ флеш-памяти и 25,8 КБ статической RAM.
Сравнение
| esp32-audio-player | ESP32-audioI2S | |
|---|---|---|
| FLAC, MP3, Ogg Vorbis, WAV | Да | Да |
| AAC, M4A, Opus | Нет | Да |
| AIFF, DSF и DFF (как DoP) | Да | Не указаны |
| Интернет-радио | Нет | Да |
| Частота дискретизации на выходе | 8-192 кГц, по частоте файла | 16-48 кГц |
| Без пауз с точностью до сэмпла | Да, для MP3 с задержкой и добивкой LAME | Не описано |
| Эквалайзер | 10 полос, параметрический, импорт AutoEQ | 3 полосы, регулировка тембра |
| Кроссфид, ReplayGain, CUE | Да | Нет |
| Bluetooth-наушники | Да, через esp32-a2dp-xq | Нет |
| Индекс фонотеки, продолжение, лог скробблинга | Да | Нет |
| Проверено на железе | Пока нет | Да |
Для интернет-радио и AAC подходит ESP32-audioI2S. Эта библиотека сделана для музыки на SD-карте.
Быстрый старт
Arduino, PCM5102A на BCK 26, LRCK 25, DIN 22 и SD-карта на стандартных выводах SPI с CS 5:
#include <SD.h>
#include <audio_player.h>
void setup() {
SD.begin(5); // монтируется в /sd
audio_i2s_config_t i2s = AUDIO_I2S_CONFIG_DEFAULT();
i2s.bck_gpio = 26; i2s.ws_gpio = 25; i2s.dout_gpio = 22;
audio_player_esp32_config_t cfg = AUDIO_PLAYER_ESP32_CONFIG_DEFAULT();
cfg.sink = audio_i2s_sink_create(&i2s);
player_t *player = audio_player_start(&cfg); // работает в своей задаче
player_cmd_play_folder(player, "/sd/music", 0, false);
}
void loop() {}
Та же программа для ESP-IDF лежит в examples/idf/sd_i2s_player.
Установка
ESP-IDF 5.3 и новее:
idf.py add-dependency "olerast67/esp32-audio-player^0.1.0"
PlatformIO (platformio.ini):
lib_deps = https://github.com/olerast67/esp32-audio-player.git#v0.1.0
Arduino IDE с ядром ESP32 3.1 и новее: скачайте esp32-audio-player-0.1.0.zip со страницы Releases и добавьте через «Скетч > Подключить библиотеку > Добавить .ZIP библиотеку». Для вывода в Bluetooth установите ещё esp32-a2dp-xq.
Я рекомендую плату с PSRAM (ESP32-WROVER, ESP32-S3 N8R2 или N16R8). Состояние декодеров, буферы чтения и выходной буфер на 200 мс уходят в PSRAM, если она есть; без PSRAM выходной буфер рассчитан на 100 мс при частоте до 48 кГц (38 КБ внутренней RAM).
Возможности
Форматы
| Формат | Подробности |
|---|---|
| FLAC | 4-32 бита, моно и стерео, обычный FLAC и Ogg FLAC, FLAC после тега ID3v2, перемотка с точностью до сэмпла (dr_flac) |
| MP3 | MPEG-1, 2 и 2.5 Layer I, II, III; заголовки Xing, Info и VBRI; задержка и добивка кодера LAME вырезаются для стыков без пауз (minimp3) |
| Ogg Vorbis | Перемотка с точностью до сэмпла; пакет комментариев любого размера пропускается (stb_vorbis) |
| WAV | PCM 8-32 бита, IEEE float 32 и 64 бита, WAVE_FORMAT_EXTENSIBLE, RF64 и BW64 |
| AIFF, AIFF-C | PCM с порядком байтов big-endian, sowt, fl32, fl64 |
| DSF, DFF | DSD64 как DoP на 176,4 кГц и DSD128 как DoP на 352,8 кГц; DFF со сжатием DST не поддерживается |
Внутри движка каждый сэмпл хранится как 32-битное целое со звуком в старших битах. Пока все ступени DSP выключены, 16-, 24- и 32-битные файлы доходят до I2S ЦАП без изменений, и статус плеера сообщает об этом флагом bitperfect.
Воспроизведение
- Без пауз: следующий файл открывается примерно за 2 секунды до конца текущего. Если у обоих файлов одинаковый формат на выходе, сэмплы идут подряд, а DSP и передискретизатор сохраняют состояние на стыке. При смене формата плеер доигрывает хвост и переоткрывает выход.
- CUE (
FILE,TRACK,INDEX 01,TITLE,PERFORMER,REM GAIN) в CP1251 или UTF-8. Соседние треки одного образа играют через один декодер. - Очередь до 20000 записей с перемешиванием, повтором трека и всей очереди, «играть следующим», переходом и удалением. При перемешивании текущий трек остаётся первым, а при выключении возвращается исходный порядок.
- Продолжение с места остановки:
player_save_state()записывает очередь вqueue.m3u8в UTF-8, а позицию, громкость и режимы вplayer.ini, оба файла заменяются атомарно.player_restore_state()продолжает на паузе или сразу играет. - Таймер сна с затуханием за 30 секунд, ограничение громкости, лог
.scrobbler.logв формате Rockbox.
Обработка звука
Цепочка считает во float на частоте выхода: ReplayGain, предусиление, параметрический эквалайзер, кроссфид, программная громкость, лимитер, дизеринг.
- ReplayGain из тегов: по треку, по альбому или по альбому только при воспроизведении альбома по порядку; предусиление, усиление для файлов без тегов и защита от клиппинга по пику.
- Параметрический эквалайзер до 10 полос: пик, низкочастотная и высокочастотная полки, фильтры НЧ и ВЧ (биквады RBJ).
eq_preset_parse_autoeq()читает файлыParametricEQ.txtиз AutoEQ,eq_preset_format_autoeq()пишет в том же формате. Встроенные пресеты: Flat, Bass, Treble, Vocal, Loudness. - Кроссфид для наушников: частота среза 300-2000 Гц, уровень 1-15 дБ.
- Лимитер с упреждением около 1 мс, TPDF-дизеринг до разрядности выхода.
- Передискретизация для выходов с фиксированной частотой: каскад полуполосных дециматоров для 2:1 и 4:1, многофазный фильтр windowed-sinc для остальных соотношений, например 48 в 44,1 кГц. Число сэмплов на выходе зависит только от числа сэмплов на входе, поэтому стыки без пауз остаются точными и после передискретизации.
Если все ступени выключены и громкость стоит на 0 дБ, сэмплы проходят без изменений.
Фонотека и метаданные
- Теги: ID3v2.2, 2.3 и 2.4, ID3v1, APEv2, Vorbis comments с обложками FLAC, RIFF INFO и блоки
id3, атомы MP4 и M4A. Однобайтовый текст в неизвестной кодировке определяется как UTF-8, CP1251 или Latin-1, и теги в CP1251 из старых русских MP3 читаются правильно. - Просмотр папок без индекса (
fs_list_dir) с сортировкой для английского и русского. - Индекс фонотеки на SD-карте: исполнители, альбомы, треки, жанры, недавно добавленное и аудиокниги. Сканирование идёт инкрементально в новые файлы, которые атомарно заменяют старые. Тесты индексируют 5000 сгенерированных файлов.
- Плейлисты M3U и M3U8, чтение и запись.
Выходы
- I2S ЦАП (
audio_i2s_sink_create) без шины управления, например PCM5102A, UDA1334A, ES9023 или MAX98357A. Слоты по 32 бита, 8-192 кГц по частоте каждого файла, выводы MCLK, приглушения и включения усилителя по желанию. Приглушение включается до остановки тактов и снимается после их запуска. DoP передаётся ЦАП, которые его распознают, если заданaccept_dop. - Bluetooth-наушники через esp32-a2dp-xq (
audio_player/a2dp_sink.h): SBC-XQ на 452 кбит/с с ESP-IDF 6.0 и новее, обычный SBC на старых версиях. Плеер пересчитывает звук в 44,1 кГц, кнопки наушников управляют плеером. - Собственные выходы через интерфейс
audio_sink_tизaudio_player/sink.h.
Примеры
| Пример | ESP-IDF | Arduino |
|---|---|---|
| Папка с SD-карты в I2S ЦАП, продолжение после сброса | sd_i2s_player | SdI2sPlayer |
| Папка с SD-карты в Bluetooth-наушники | bluetooth_headphones | BluetoothHeadphones |
| Пресет AutoEQ и кроссфид | AutoEQ | |
| Любой файл в WAV на компьютере | tools/player_cli |
Устройство
Ядро не создаёт потоков. Команды (player_cmd_*) может отправлять каждая задача, они только ставятся в очередь. Одна аудиозадача вызывает player_run_once() в цикле: декодирует около 1024 кадров, обрабатывает их и пишет в выход, и запись в выход задаёт темп. audio_player_start() создаёт эту задачу на втором ядре с приоритетом 18. Память, лог, блокировки и часы ядро берёт через функции из audio_player/base.h; audio_player/esp32.h подключает их к ESP-IDF, а тесты на компьютере используют стандартную библиотеку C.
Сборка и тесты на компьютере
cmake -S test -B build/test
cmake --build build/test
ctest --test-dir build/test --output-on-failure
Сборка даёт player_cli, который проигрывает файл, папку, плейлист M3U или CUE через движок в WAV-файл (player_cli album/ out.wav --gapless-folder --eq autoeq.txt --rate 44100). На Windows без компилятора C выполните python -m pip install ziglang cmake ninja и добавьте -G Ninja -DCMAKE_TOOLCHAIN_FILE=<абсолютный путь>/cmake/zig-toolchain.cmake. Флаг -DAUDIO_PLAYER_SANITIZE=ON включает AddressSanitizer и UndefinedBehaviorSanitizer.
Лицензия
Apache License 2.0, текст в LICENSE. Встроенные декодеры: dr_flac (Unlicense или MIT-0), minimp3 (CC0-1.0), stb_vorbis (MIT или Unlicense), подробности в NOTICE и src/third_party. Тестовые файлы в test/data генерирует tools/gen_test_vectors.py, они распространяются под CC0-1.0.
Движок взят из музыкального плеера на ESP32, который я сейчас делаю. Плеер я опубликую после его собственных проверок на железе. Большую часть кода написал Claude (Anthropic) по моим заданиям, я ставил задачи и проверял результат тестами и сборками из раздела «Состояние».