SimpleSDAudio: воспроизведение с помощью Arduino аудиофайлов с SD карты
Содержание
О библиотеке
Воспроизведение в хорошем качестве с помощью Arduino аудиофайлов с SD карты. Понадобится всего лишь несколько дополнительных комплектующих.
Особенности
- 8-битный ШИМ выход – не требуется внешний ЦАП;
- эмуляция 16 разрядности с помощью 2 x 8-битного ШИМ выхода – потребуется только два резистора;
- частота дискретизации 62,500 кГц (полная скорость) / 31,250 кГц (половинная скорость) @ 16 МГц;
- частота дискретизации 31,250 кГц (полная скорость) / 15,625 кГц (половинная скорость) @ 8 МГц;
- ШИМ выход работает всегда на полной скорости для упрощения фильтрации;
- режимы моно, мост, стеро;
- использование RAM ~1,3 КБ;
- использование ROM ~6,1 КБ;
- встроенная библиотека SD (только минимальная FAT, оптимизированная для небольшого использования RAM и высокой производительности);
- работает с большинством плат расширения SD карт, которые подключены к SPI порту;
- простой в использовании API: 1) инициализация библиотеки, 2) выбор аудиофайла, 3) вызов проигрывания;
- поддержка SD и SDHC карт, отформатированных в FAT16 или FAT32.
Ограничения
- аудиофайл перед использованием должен быть конвертирован;
- аудиофайлы должны быть помещены в корневой каталог карты;
- имя аудиофайла должно соответствовать формату 8.3;
- аудиофайл должен быть помещен на карту не фрагментированным;
- комбинация полной скорости и счетверенного выхода приводит к опустошению буфера;
- минимально требуемый микроконтроллер: ATmega368. ATmega168 не подходит из-за малого объема RAM.
Альтернативы
В данное время существует альтернативная библиотека под названием TMRpcm, которая делает что-то похожее: https://github.com/TMRh20/TMRpcm/wiki. Она немного проще в использовании (не требуется конвертирование WAV файлов). Ниже приведена таблица различий.
Особенность | SimpleSDAudio | TMRpcm |
---|---|---|
Максимальная частота дискретизации | X | - |
Воспроизведение обычного WAV (не требуется конвертирование файлов) | - | X |
Высокоскоростной оптимизированный код (использование ассемблера в ядре аудио) | X | - |
Использование оптимизированной по размеру маленькой библиотеки SD-FAT | X | - |
Использование универсальной SD библиотеки, поддерживающей фрагментированные файлы и многие другие виды использования | - | X |
Возможность работы в режиме стерео | X | X |
Возможность работы с четырехканальным звуком | X | X |
Поддержка установки громкости | - | X |
Поэтому, в зависимости от ваших потребностей, попробуйте библиотеку, которая наилучшим образом соответствует вашим требованиям. TMRpcm может быть проще для новичков. Обе библиотеки должны работать с одними и теми же подключениями. Информацию о подключениях вы можете получить ниже и использовать с ними библиотеку TMRpcm.
Вам нужно большее: воспроизведение многоканального звука, цифровые фильтры, микшеры, функции DSP? Тогда посмотрите на Teensy 3.1 Audio Library, использующую серьезный (но всё еще доступный) микроконтроллер, и всё еще использующую Arduino IDE.
Загрузка
Последняя версия:
Использование
Руководство по быстрому старту
- Установите библиотеку: распакуйте всё в ваш каталог /libraries/.
- Скопируйте файл /libraries/SimpleSDAudio/examples/EXAMPLE.AFM в корневой каталог только что отформатированной SD карты (не используйте быстрое форматирование!).
- Подключите SD карту к вашей плате Arduino (с помощью платы расширения или чего-то еще, вывод выбора чипа SD карты должне быть подключен к выводу 4, остальные выводы – к выводам SPI).
- Подключите динамик или наушник последовательно через 100-омный резистор к выводу аудиовыхода (вывод 9 на Arduino с ATmega328, вывод 44 на Arduino с ATmega1280/2560). Подключите второй вывод динамика к выводу GND.
- Запустите Arduino IDE и попробуйте сначала пример "BareMinimumWithDebug".
- Вы должны услышать звук (проигрывается только несколько секунд). Если что-то не работает, то откройте монитор последовательного порта для получения дополнительной информации. Возможно, вам придется настроить вывод CS SD карты.
Руководство по установке ПО
Перейдите к месту хранения скетчей (смотрите Arduino - File - Preferences). Там должна быть папка с именем /libraries/, если нет, то создайте ее. Затем распакуйте в нее zip-архив. В итоге путь к примеру должен получиться следующим /libraries/SimpleSDAudio/examples/. Теперь вы можете запустить Arduino IDE и найти примеры библиотеки в разделе File - Examples - SimpleSDAudio.
Чтобы узнать, как преобразовать аудиофайлы для использования в данной библиотеке, смотрите следующий раздел.
Подготовка SD карты и конвертирование аудиофайлов
Аудиобиблиотека использует очень урезанную библиотеку SD, которая использует FAT только для поиска начального сектора файлов. Поэтому файл должен быть полностью не фрагментирован на SD карте. Лучший способ обеспечить это – заново выполнить полное форматирование карты (не используйте быстрое форматирование!). После форматирования SD просто скопируйте на нее новые файлы. Не удаляйте файлы и избегайте операций переименования, которые создают новые имена файлов, не соответствующие формату 8.3 (смотрите https://ru.wikipedia.org/wiki/8.3). Все файлы должны размещаться в корневом каталоге, так как каталоги не поддерживаются данной аудиобиблиотекой.
Для конвертирования аудиофайлов я предлагаю использовать SoX из http://sox.sourceforge.net/.
Для пользователей Windows
Для пользователей Windows бинарные файлы SoX уже являются частью библиотеки и расположены в каталоге libraries/SimpleSDAudio/tools. Просто перетащите .wav файлы на соответствующие bat файлы, чтобы начать конвертирование. Преобразованные файлы попадут в каталог "converted".
Для пользователей Linux
Пользователи Linux должны скомпилировать SoX из исходников или использовать свой любимый менеджер пакетов для установки SoX. Для конвертирования я рекомендую использовать следующую командную строку:
sox inputfile.wav --norm=-1 -e unsigned-integer -b 8 -r 31250 -c 1 -t raw outputfile.raw
Исходя из своих потребностей, вы можете изменить следующие параметры:
- для стерео измените
-c 1
на-c 2
- для полной скорости используйте
-r 62500
@ 16MHz,-r 31250
@ 8 MHz - для половинной скорости используйте
-r 31250
@ 16MHz,-r 15625
@ 8 MHz
Опция --norm=-1
используется для избегания не очень хороших звуковых эффектов клиппинга.
Соглашения об именах файлов
Хотя вы можете выбрать любые имя файла и расширение, я предлагаю использовать расширение, которое показывает разрядность, частоту дискретизации и режим стерео или моно. В bat-файлах я использую следующие расширения:
- 8 бит
- .AFM - полная скорость, моно
- .AFS - полная скорость, стерео
- .AHM - половинная скорость, моно
- .AHS - половинная скорость, стерео
- 16 бит
- .BFM - полная скорость, моно
- .BFS - полная скорость, стерео
- .BHM - половинная скорость, моно
- .BHS - половинная скорость, стерео
Сборка аппаратных комплектующих
Подключение SD карты
SD карта должна быть подключена к SPI порту микроконтроллера. Вывод выбора микросхемы от карты может быть подключен к любому свободному цифровому выводу, но если воспользоваться для этого выводом 4 Arduino, то никаких изменений в исходном коде не понадобится. Здесь будут работать многие платы расширения с поддержкой SD карт, например, Ethernet-Shield. Для большинства плат Arduino (кроме тех, которые работают с напряжением 3,3В) вам понадобятся преобразователи уровней от 5 до 3,3 вольт – достаточно будет трех резисторных делителей. На схеме ниже показано, как это сделать.
В соответствии с вашей конфигурацией режима один или два вывода используются для аудиовыхода. Эти выводы не могут быть изменены и различаются в зависимости от модели платы Arduino. Для платформ на базе ATmega168/328 это выводы 9 и 10. Для плат на базе ATmega1280/2560 это выводы 44 и 45. Для других платформ смотрите файлы SimpleSDAudio.h.
Подключение аудиовыхода
Будьте осторожны, так как выводы аудиовыхода являются цифровыми портами, на которые выдается постоянное положительное напряжение между 0В и 5В. Подача постоянного напряжения на линейный выход или небольшие громкоговорители является не очень хорошей идеей, поскольку через них будет протекать постоянный ток. Поэтому последовательно с нагрузкой должен быть включен конденсатор. Для начала используйте конденсатор от 100 нФ до 100 мкФ. Для линейных выходов используйте делитель напряжения или потенциометр для уменьшения напряжения.
16-разрядный выход выполняется путем одновременного управления двумя 8-разрядными выходами: один будет обеспечивать сигнал, содержащий старшие 8 бит, а второй – сигнал, содержащий младшие 8 бит. Если вы послушаете каждый из этих каналов отдельно, то на выходе старших 8 бит услышите обычный звук, но с заметным 8-битным шумом, особенно на тихих звуковых участках. На выходе младших 8 бит вы услышите только шум. Но когда вы добавите этот шум (уменьшенный по громкости с помощью резисторов в 256 раз) к старшим 8 битам, произойдет что-то волшебное: 8-битный шум исчезнет (но, к сожалению, часто большое количество шума, исходящего от источника питания, может всё равно остаться).
Аудиоусилитель для громкоговорителей
Если вам необходима большая мощность, то можете собрать дешевый ШИМ усилитель класса D на 74AC14 (шесть инверторов с триггерами Шмитта на входах). Катушки индуктивности необязательны, но должны использоваться для непропускания ВЧ помех при использовании длинных кабелей.
Собираем собственный макетный адаптер для SD карты
Следуйте следующим инструкциям с картинками, чтобы собрать очень дешевый адаптер SD карты.
Справка по API
Константы
Ниже приведен обзор констант, используемых в библиотеке.
#define SSDA_VERSIONSTRING "1.03"
// Sound Mode Flags
#define SSDA_MODE_FULLRATE 0x00 // 62.500 кГц @ 16 МГц, 31.250 кГц @ 8 МГц
#define SSDA_MODE_HALFRATE 0x10 // 31.250 кГц @ 16 МГц, 15.625 кГц @ 8 МГц
#define SSDA_MODE_MONO 0x00 // Использовать только первый ШИМ вывод
#define SSDA_MODE_STEREO 0x01 // Использовать оба ШИМ вывода для вывода стерео
#define SSDA_MODE_QUADRO 0x04 // Использовать четыре ШИМ вывода либо для 4 динамиков, либо для стерео 16 бит
#define SSDA_MODE_MONO_BRIDGE 0x02 // Использовать оба ШИМ вывода для получения большей выходной мощности
#define SSDA_MODE_AUTOWORKER 0x80 // Используйте это значение и исполнитель будет вызван автоматически
// Коды ошибок от SimpleSDAudio, остальные смотрите в файле sd_l*.h
#define SSDA_ERROR_NULL 0x80 // Нулевой указатель
#define SSDA_ERROR_BUFTOSMALL 0x81 // Буфер слишком мал
#define SSDA_ERROR_NOT_INIT 0x82 // Система не была правильно инициализирована
Имя класса и экземпляр по умолчанию
class SdPlayClass { ... }
extern SdPlayClass SdPlay;
Имя класса – SdPlayClass
. Один экземпляр этого класса уже создан и готов к использованию, его имя SdPlay
.
Публичные методы класса
init()
boolean init(uint8_t soundMode);
Вызовите этот метод, чтобы инициализировать библиотеку и установить режим звука. Данная функция также получит необходимый буфер (если он еще не установлен вручную с помощью
setWorkBuffer
), инициализирует SD карту и настроит все используемые выводы.В качестве аргумента должна быть передана комбинация следующих флагов (комбинируются с помощью оператора ИЛИ):
- флаги установки частоты дискретизации:
SSDA_MODE_FULLRATE
– 62.500 кГц @ 16 МГц, 31.250 кГц @ 8 МГцSSDA_MODE_HALFRATE
– 31.250 кГц @ 16 МГц, 15.625 кГц @ 8 МГц
- флаги настройки выходных каналов:
SSDA_MODE_MONO
– использовать только первый ШИМ выводSSDA_MODE_STEREO
– использовать оба ШИМ вывода для вывода стереоSSDA_MODE_QUADRO
– использовать четыре ШИМ вывода либо для 4 динамиков, либо для стерео 16 битSSDA_MODE_MONO_BRIDGE
– использовать оба ШИМ вывода для получения большей выходной мощности
- флаг автоматического вызова исполнителя:
SSDA_MODE_AUTOWORKER
– добавьте этот флаг, и у вас не будет необходимости вызывать исполнитель для воспроизведения
Данная функция возвращает
true
в случае успеха иfalse
в случае возникновения ошибки. Вы можете использоватьgetLastError()
для получения кода ошибки. Типовыми причинами возникновения ошибок являются неправильное подключение SD карты или слишком малый размер RAM (требуется куча размером 1 килобайт), доступный для внутреннего буфера.- флаги установки частоты дискретизации:
setFile()
boolean setFile(char *fileName);
После успешной инициализации вызовите данный метод, чтобы выбрать аудиофайл, предоставив имя файла в формате 8.3.
Данная функция возвращает
true
в случае успеха иfalse
в случае возникновения ошибки. Вы можете использоватьgetLastError()
для получения кода ошибки. Типовой причиной возникновения ошибок является то, что файл не был найден.worker()
void worker(void);
Вызывайте этот метод постоянно, по крайней мере, пока выполняется воспроизведение аудиофайла. Данная функция заполняет внутренний буфер, считывая следующие сектора с SD карты. Вы не можете вызывать эту функцию слишком часто, но и при слишком большом времени между вызовами данной функции может произойти опустошение буфера.
Начиная с версии 1.03: Добавьте при инициализации флаг
SSDA_MODE_AUTOWORKER
и исполнитель будет вызываться автоматически в фоновом прерывании. У вас больше не будет необходимости вызывать его самому. Это делает воспроизведение звука более надежным, когда используются методы
Serial
. Однако это не работает совместно с платой расширения Ethernet Shield, так как доступ к шине SPI должен быть правильно распределен между SD картой и микросхемой Ethernet.play()
void play(void);
Если звук не проигрывается, то воспроизведение будет запущено. Если звук уже проигрывается, то воспроизведение начнется снова с нуля.
stop()
void play(void);
Останавливает воспроизведение, если оно запущено, и устанавливает позицию воспроизведения на ноль.
pause()
void pause(void);
Ставит воспроизведение на паузу, если оно запущено, или возобновляет его, если была пауза.
setSDCSPin()
void setSDCSPin(uint8_t csPin);
Вызовите этот метод перед инициализацией, чтобы выбрать вывод, подключенный к CS выводу SD карты, отличающийся от значения по умолчанию.
setWorkBuffer()
void setWorkBuffer(uint8_t *pBuf, uint16_t bufSize);
Вызовите этот метод, если хотите использовать собственный буфер (минимальный размер 1024 байта, размер должен быть кратен 512). Вызовите его перед инициализацией, после чего инициализация будет использовать этот буфер вместо использования
malloc
для создания собственного буфера.deInit()
void deInit(void);
Вызовите этот метод, чтобы освободить ресурсы, такие как буфер, освободить выводы SD карты, аудио прерывания и аудио выводы.
dir()
void dir(void (*callback)(char *));
Вывод каталога SD карты. Методу должен быть передан указатель на функцию обратного вызова. Функция обратного вызова вызывается один раз для каждого файла, найденного в корневом каталоге карты. Строки содержат нулевое завершение, но в них нет перевода строки.
Пример использования:
... // определение глобальной функции обратного вызова void dir_callback(char *buf) { Serial.println(buf); } ... // где-то после инициализации SdPlay.dir(&dir_callback);
isStopped(), isPlaying(), isPaused()
boolean isStopped(void); boolean isPlaying(void); boolean isPaused(void);
Возвращают текущее состояние воспроизведения. Функция
isStopped()
может использоваться после вызоваplay()
, чтобы определить, когда воспроизведение будет завершено.isUnderrunOccured()
boolean isUnderrunOccured(void);
Возвращает
true
, если установлен внутренний флаг опустошения буфера, и сбрасывает его. Если звук звучит странно, или появляются пропадания, проверьте не является ли причиной опустошение буфера.getLastError()
uint8_t getLastError(void);
Возвращает код последней ошибки и очищает ее. Чтобы проанализировать причину ошибки, посмотрите файлы библиотеки sd_l1.h и sd_l2.h, чтобы найти значение возвращенного кода.
Примеры
Абсолютный минимум:
#include <SimpleSDAudio.h>
void setup()
{
// SdPlay.setSDCSPin(10); // Раскомментируйте, если SC вывод на вашей SD карте не 4...
SdPlay.init(SSDA_MODE_FULLRATE | SSDA_MODE_MONO | SSDA_MODE_AUTOWORKER);
SdPlay.setFile("EXAMPLE.AFM");
SdPlay.play();
}
void loop(void)
{
}
Пустой минимум:
#include <SimpleSDAudio.h>
void setup()
{
// Если на вашей SD карте CS вывод не 4, раскомментируйте и подправьте следующую строку:
// SdPlay.setSDCSPin(10);
// Инициализация SdPlay и настройка режима аудио и активация автоисполнителя
if (!SdPlay.init(SSDA_MODE_FULLRATE | SSDA_MODE_MONO | SSDA_MODE_AUTOWORKER))
{
while(1); // Ошибка при инициализации SD карты -> стоп.
}
// Выбрать файл для проигрывания
if(!SdPlay.setFile("EXAMPLE.AFM"))
{
while(1); // Ошибка (файл не найден) -> стоп.
}
}
void loop(void)
{
// Начать воспроизведение
SdPlay.play();
// Необязательно: ждать, пока воспроизведение не закончится
while(!SdPlay.isStopped())
{
; // SdPlay.worker() больше не требуется :-)
}
}
Пустой минимум с исполнителем:
#include <SimpleSDAudio.h>
void setup()
{
// Если на вашей SD карте CS вывод не 4, раскомментируйте и подправьте следующую строку:
// SdPlay.setSDCSPin(10);
// Инициализация SdPlay и настройка режима аудио
if (!SdPlay.init(SSDA_MODE_FULLRATE | SSDA_MODE_MONO))
{
while(1); // Ошибка при инициализации SD карты -> стоп.
}
// Выбрать файл для проигрывания
if(!SdPlay.setFile("EXAMPLE.AFM"))
{
while(1); // Ошибка (файл не найден) -> стоп.
}
// Начать воспроизведение
SdPlay.play();
// Пусть исполнитель работает до завершения воспроизведения
while(!SdPlay.isStopped())
{
SdPlay.worker();
}
}
void loop(void) {
}
FAQ
Не компилируется
- Вы используете последнюю версию Arduino IDE?
- SimpleSDAudio_V1.00 требует, как минимум, V1.0.1 среды Arduino IDE. Также эта версия библиотеки была протестирована и хорошо работает со средой версии 1.6.1.
- SimpleSDAudio_V1.01 требует, как минимум, V1.0 среды Arduino IDE.
Неудача SdPlay.init
- Вы правильно выбрали CS вывод для вашей SD карты? Раскомментируйте следующую строку
в примерах и введите сюда правильный номер вывода.// SdPlay.setSDCSPin(10);
- Ваша плата расширения с SD картой может быть хреновой, и SD связь может быть возможна только при ограничении скорости. Попробуйте раскомментировать строку
в функцииSPSR |= (1 << SPI2X);
SD_L0_SpiSetHighSpeed()
. Если это не помогло, то раскомментируйте и первую строку в этой функции и попробуйте снова. Если после этого инициализация заработала, то у вас плохая плата расширения.
Как использовать 16-битное аудио?
Если вы хотите использовать 16-битное аудио, то вам необходимо объединить 2 ШИМ выхода вместе с помощью резистора (смотрите выше). Выберите SSDA_MODE_STEREO
, если вам необходим один 16-битный выходной канал, или выберите SSDA_MODE_QUAD
, если вам необходимы два 16-битных выходных канала (работает только на Arduino Mega или с патчем Timer2).