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

arduinoaudio-playerautoeqdacdsdequalizeresp-idfesp32

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) по моим заданиям, я ставил задачи и проверял результат тестами и сборками из раздела «Состояние».