TMRpcm: Arduino библиотека для воспроизведения PCM/WAV аудиофайлов напрямую с SD карты
Содержание
О библиотеке
TMRpcm – Arduino библиотека для асинхронного воспроизведения PCM/WAV файлов напрямую с SD карты.
Использует стандартную библиотеку SD Arduino, SD карту и выходное устройство (громкоговоритель, наушники, усилитель и т.д.).
Поддерживаемые платы:
- все платы на базе ATmega328: Arduino Uno, Nano, Duemilanove и т.д.;
- платы Mega: 1280, 2560 и т.д..
Особенности
- Воспроизведение PCM/WAV напрямую с SD карты.
- Основные форматы: WAV файлы, 8-бит, частота дискретизации 8–32 кГц, моно.
- Асинхронное воспроизведение: позволяет работать коду в основном цикле программы во время воспроизведения звука.
- Работа на одном таймере: TIMER1 (Uno, Mega) или TIMER3, 4 или 5 (Mega).
- Двухтактный выход или подключение двух динамиков.
- Двойное увеличение частоты дискретизации.
- Поддерживаемые устройства: Arduino Uno, Nano, Mega и т.д.
Подготовка аудиофайлов
Файлы конвертируются легко и просто:
- с помощью iTunes:
- кликнуть Edit > Preferences > Import Settings;
- изменить значение в выпадающем списке на WAV Encoder и Setting: Custom > 16.000kHz to 32kHz, 8-bit, Mono;
- правый клик на любом файле в in iTunes и выбрать "Create WAV Version";
- с помощью Audacity:
- Tracks > Stereo Track to Mono;
- Project Rate (HZ) > установить 32000, 22050, 16000 или 11025;
- File > Export > Save as type: Other uncompressed files > Options...;
- выбрать WAV, Unsigned 8 bit PCM.
Затем с помощью компьютера скопируйте файл на SD карту.
Известные ограничения
Данная библиотека сильно нагружает процессор, и выполнение кода во время воспроизведения будет медленнее, чем обычно. Нагрузка от обработки аудиофайлов может быть уменьшена за счет использования аудио более низкого качества, кодированного с меньшей частотой дискретизации (с минимальным значением 8 кГц).
Возможно влияние на другие библиотеки, которые используют прерывания. Функции isPlaying()
, disable()
или noInterrupts()
могут использоваться для предотвращения параллельного выполнения кода.
Управление громкостью допускает хороший диапазон регулировки громкости, но при большой громкости возможно появление искажений.
Загрузка
Также проект можно найти на GitHub: ссылка.
Функции
TMRpcm audio;
audio.play("filename"); // воспроизвести файл
audio.play("filename",30); // воспроизвести файл, начиная с 30-ой секунды
audio.speakerPin = 11; // установить в значение 5, 6, 11 или 46 для Mega, 9 для Uno, Nano и т.д.
audio.disable(); // выключить таймер на выходном выводе и остановить воспроизведение
audio.stopPlayback(); // остановить воспроизведение, но таймер остается запущенным
audio.isPlaying(); // возвращает 1, если идет воспроизведение, в противном случае - 0
audio.pause(); // поставить воспроизведение на паузу / снять с паузы
audio.quality(1); // установить 1 для удвоения частоты дискретизации
audio.volume(0); // 1 (включить) или 0 ( выключить) для управления громкостью
audio.setVolume(0); // от 0 до 7. Устанавливает уровень громкости.
audio.loop(1); // 0 или 1. Может быть изменено во время воспроизведения для полного управления зацикливанием.
Пример использования
Это простая схема для проигрывания wav файлов с помощью Arduino Nano v.3.0, она содержит 4 кнопки, при нажатии каждой из которых воспроизводится заданный wav файл, загруженный на SD карту.
Комплектующие
- Arduino Nano v3.0 (я использовал китайскую версию под названием Funduino Nano);
- модуль SD карты;
- SD карта;
- макетная плата;
- четыре кнопки;
- четыре резистора 22 кОм;
- один резистор 4,7 кОм;
- NPN транзистор BC546B;
- динамик;
- перемычки.
Подготовка SD карты
- Отформатируйте SD карту (убедитесь, что настройки форматирования совпадают с приведенными на скриншоте выше).
- Конвертируйте ваши аудиофайлы в .WAV файлы (я использовал программу Wav Sample rate converter) со следующими параметрами:
- частота дискретизации: 16000 Гц;
- количество каналов: моно;
- количество бит на отсчет: 8.
- Wav файлы для примера приведены ниже.
Схема
Код
Перед написанием кода вам необходимо скачать (ссылка выше в разделе «Загрузка») и установить библиотеку TMRpcm.
#include <SD.h> // необходимо включить SD библиотеку
#define SD_ChipSelectPin 4 // использование цифрового вывода 4 на arduino nano 328
#include <TMRpcm.h> // также необходимо включить данную библиотеку...
TMRpcm tmrpcm; // создать объект для использования в данном скетче
int SW1;
int SW2;
int SW3;
int SW4;
void setup()
{
pinMode(14,INPUT); // Определить A0 как цифровой вход.
pinMode(15,INPUT); // Определить A1 как цифровой вход.
pinMode(16,INPUT); // Определить A2 как цифровой вход.
pinMode(17,INPUT); // Определить A3 как цифровой вход.
tmrpcm.speakerPin = 9; // 11 на Mega, 9 на Uno, Nano и т.д.
if (!SD.begin(SD_ChipSelectPin)) // проверить, есть ли карта, и может ли она быть инициализирована:
{
return; // если нет, то ничего не делать
}
tmrpcm.volume(1);
tmrpcm.play("1.wav"); // звуковой файл "1" будет проигрываться при каждом включении или перезагрузке arduino
}
void loop()
{
SW1=digitalRead(14);
SW2=digitalRead(15);
SW3=digitalRead(16);
SW4=digitalRead(17);
if (SW1 == LOW)
{ // если SW1 нажата, то воспроизвести файл "6.wav"
tmrpcm.play("6.wav");
}
else if(SW2 == LOW)
{ // если SW2 нажата, то воспроизвести файл "4.wav"
tmrpcm.play("4.wav");
}
else if(SW3 == LOW)
{ // если SW3 нажата, то воспроизвести файл "5.wav"
tmrpcm.play("5.wav");
}
else if(SW4 == LOW)
{ // если SW4 нажата, то воспроизвести файл "3.wav"
tmrpcm.play("3.wav");
}
}
Поиск неисправностей
Если у вас не получилось заставить эту схему работать, то ниже приведены два тестовых скетча для поиска неисправностей. Код был протестирован на Arduino Nano, Uno и Mega.
Подключать кнопки нет необходимости, файлы будут воспроизводится автоматически.
Нет необходимости подключать транзистор, подключите небольшой динамик или наушники напрямую к выводу 9 платы Arduino Nano или к выводу 11 платы Arduino Mega.
Скачанные по ссылке выше аудиофайлы скопируйте в корневой каталог SD карты.
Arduinio Nano
Arduino Nano | SD карта |
---|---|
12 | MISO |
11 | MOSI |
13 | SCK |
4 | CS |
9 | динамик |
GND | GND |
5V | VCC |
#include <SD.h> // необходимо включить SD библиотеку
#define SD_ChipSelectPin 4 // использование цифрового вывода 4 на arduino nano 328
#include <TMRpcm.h> // также необходимо включить данную библиотеку...
#include <SPI.h>
TMRpcm tmrpcm; // создать объект для использования в данном скетче
void setup()
{
tmrpcm.speakerPin = 9; // 11 на Mega, 9 на Uno, Nano и т.д.
if (!SD.begin(SD_ChipSelectPin)) // проверить, есть ли карта, и может ли она быть инициализирована:
{
return; // если нет, то ничего не делать
}
tmrpcm.volume(1);
}
void loop()
{
tmrpcm.play("6.wav");
delay(1000);
tmrpcm.play("4.wav");
delay(3000);
tmrpcm.play("5.wav");
delay(4000);
tmrpcm.play("3.wav");
delay(2000);
}
Arduino Mega
Arduino Mega | SD карта |
---|---|
50 | MISO |
51 | MOSI |
52 | SCK |
53 | CS |
11 | динамик |
GND | GND |
5V | VCC |
#include <SD.h> // необходимо включить SD библиотеку
#define SD_ChipSelectPin 53 // использование цифрового вывода 4 на arduino nano 328
#include <TMRpcm.h> // также необходимо включить данную библиотеку...
#include <SPI.h>
TMRpcm tmrpcm; // создать объект для использования в данном скетче
void setup()
{
tmrpcm.speakerPin = 11; //11 on Mega, 9 on Uno, Nano, etc
if (!SD.begin(SD_ChipSelectPin)) // проверить, есть ли карта, и может ли она быть инициализирована:
{
return; // если нет, то ничего не делать
}
tmrpcm.volume(1);
}
void loop()
{
tmrpcm.play("6.wav");
delay(1000);
tmrpcm.play("4.wav");
delay(3000);
tmrpcm.play("5.wav");
delay(4000);
tmrpcm.play("3.wav");
delay(2000);
}
Если тестовый код работает, то добавьте строку #include <SPI.h>
в программу с кнопками.
Если звука всё равно нет, то добавьте задержку между командами воспроизведения delay(1000);
.
Дополнительные возможности
Данная библиотека предназначалась для простого и удобного пользователю использования в качестве проигрывателя wav аудиофайлов, использующего стандартные библиотеки Arduino и воспроизводящего файлы в простейшем wav формате. Многие дополнительные функции были добавлены по запросам пользователей и включены в pcmConfig.h, чтобы сохранить изначальную простоту.
Большинство дополнительных функций требуют больше памяти RAM, больше программной памяти и, в некоторых случаях, большей вычислительной мощности для воспроизведения. Некоторые из них до сих пор еще отлажены не полностью. Помните об этом при включении данных функций.
Смотрите pcmConfig.h для настройки следующих параметров:
Пользовательские определения
В pcmConfig.h настраиваются следующие параметры:
Параметр | Описание |
---|---|
#define buffSize 128 | Управляет размером двух буферов (или четырех в режиме MULTI). |
#define DISABLE_SPEAKER2 | Выключает вывод по умолчанию второго динамика для совместимости с другими библиотеками (вывод 10 на Uno). |
#define ENABLE_MULTI | Включает режим воспроизведения нескольких треков (по умолчанию на одном таймере). |
#define STEREO_OR_16BIT | Включает воспроизведение стерео или 16-битных файлов. |
#define MODE2 | Включает режим двойного таймера для воспроизведения нескольких треков. Не доступен с TIMER2. |
#define SDFAT | Библиотека SdFat использует меньше памяти программ и RAM. Смотрите пример SDFAT в составе данной библиотеки. |
#define HANDLE_TAGS | Пропускает теги WAV файлов, которые содержат метаданные. |
#define USE_TIMER2 | Использует 8-разрядный TIMER2, вместо 16-разрядных таймеров. |
#define rampMega | Принудительный ручной выбор включения/выключения метода линейного изменения ШИМ. |
#define ENABLE_RF | Включает стриминг аудио через радиоканал (NRF24L01+). |
Второй громкоговоритель / двухтактный режим
Данная библиотека по умолчанию выводит данные на два вывода таймер, хотя по умолчанию полностью включен только один.
Для включения дополнительный вывод должен быть настроен на выход, например:
- Arduino Uno (один выход):
audio.speakerPin = 9;
- Arduino Uno (двухтактный выход):
audio.speakerPin = 9; pinMode(10,OUTPUT);
Чтобы полностью выключить второй выход, раскомментируйте в pcmConfig.h строку #define DISABLE_SPEAKER2
.
Режим Multi
Режим Multi включает одновременное воспроизведение двух треков.
Функции множественного режима немного отличаются от функций стандартного режима:
audio.speakerPin2 = 5; // То же действие, что и в стандартном режиме, используется только с 4-выводным выходом
audio.play("sound.wav"); // Воспроизвести файл на выходе 0
audio.play("sound.wav",30,1); // Воспроизвести файл, начиная с 30-ой секунды, на выходе 1
audio.play("sound.wav",0); // play(имя_файла, выход 0 или 1) определяется speakerpin или speakerpin2
audio.stopPlayback(0); // Может принимать номер выхода 0 или 1
audio.isPlaying(0); // Может принимать номер выхода 0 или 1
audio.setVolume(4,0); // Установить громкость на выходе 0
audio.volume(0,1); // Уменьшить громкость на выходе 1
Режимы:
- по умолчанию: использует тот же таймер и выводы, что и в обычном режиме с двухтактным выходом;
- MODE2: использует два 16-разрядных таймера и до 4-х выводов.
Использование:
- рекомендуемая частота дискретизации 16–20 кГц;
- размер буфера может быть увеличен для улучшения производительности;
- звуки, воспроизводимые одновременно, должны иметь одинаковые частоты дискретизации;
- для включения режима multi раскомментируйте определение в pcmConfig.h;
- переменная audio.speakerPin2 должна быть установлена в 4-выводном режиме для выбора дополнительного таймера / вывода, которые будут использоваться.
Упрощенные режимы:
Стандартный режим (2 вывода, один трек) | 1 или 2 динамика |
Стандартный режим стерео (2 вывода, один трек) | 2 динамика, не двухтактные (нагрузка между выводом и корпусом) |
Стандартный режим стерео MODE2 (4 вывода, один трек) | 4 динамика ИЛИ двухтактные выходы (нагрузка между двумя выводами) на 2 динамика |
Режим Multi (2 вывода, два трека) | 1 или 2 динамика |
Режим Multi стерео (4 вывода, два трека) | 2 или 4 динамика, не двухтактные |
Режим Multi MODE2 (4 вывода, два трека) | 2 динамика на двухтактных выходах или 4 динамика на не двухтактных выходах |
Примечание: все 4-выводные режимы требуют платы с двумя и более 16-разрядными таймерами.
Воспроизведение стерео и 16-битного аудио
Эти режимы требуют дополнительных ресурсов и вычислительной мощности, так как с SD карты должны считываться удвоенные данные. Треки моно могут воспроизводиться в режиме стерео, но не наоборот.
В стандартном режиме:
#define STEREO_OR_16BIT
- В этом режиме стерео и 16-битные файлы обрабатываются одинаково, причем первый байт считывает на один выход, а второй байт – на другой. Это дает в результате стерео выход на двух динамиках, подключенных между выводом (выводами) динамика и корпусом, или один 16-разрядный выход, использующий резисторную матрицу.
#define MODE2
- В стандартном режиме MODE2 позволяет выводить стерео или 16-разрядный звук, используя два дополнительных вывода таймера. Таймер и выводы указываются переменой
speakerPin2
. Двухтактные выводы таймера должны быть настроены на выход вручную.
В режиме Multi:
#define STEREO_OR_16BIT
- Включение этой опции вместе с режимом MULTI позволит воспроизводить два стерео или 16-разрядных трека на отдельных выводах таймера. Это обеспечивает выход для четырех громкоговорителей, подключенных между выводом (выводами) и корпусом, или один 16-разрядный выход для каждого трека.
#define MODE2
- Включение этой опции с режимом MULTI и
STEREO_OR_16BIT
ни на что не влияет.
Использование SDFAT
Библиотека SDFAT может использоваться для уменьшения использования оперативной памяти и памяти программ и для повышения производительности. Файлы должны быть включены в скетч. Смотрите пример в архиве с библиотекой.
Метаданные (теги ID3v2.3 и LIST)
Функции были добавлены для чтения данных о песне, артисте и альбоме из тегов ID3v2.3 и LIST в WAV файлах.
Примечание: добавление, модифицирование и редактирование метаданных поддерживает программа Audacity.
Функции:
listInfo
- Прочитать теги LIST в символьный буфер, возвращает длину тега.
id3Info
- Прочитать теги ID3 в символьный буфер, возвращает длину тега.
getInfo
- Ищет оба тега и считывает в символьный буфер, возвращает длину тега. Первым ищется ID3.
Теги для запросов:
- 0 = название песни;
- 1 = имя артиста;
- 2 = название альбома.
Синтаксис:
byte length = audio.listInfo(<Song Name>,<buffer[]>,<tagToRequest>);
byte length = audio.id3Info(<Song Name>,<buffer[]>,<tagToRequest>);
byte length = audio.getInfo(<Song Name>,<buffer[]>,<tagToRequest>);
Примеры:
- Найти только информацию LIST и напечать название песни через последовательный порт.
char info[32]; audio.listInfo("song.wav",info,0); Serial.print(info);Serial.println(":");
- Найти информацию ID3v2.3 и LIST и напечатать информацию о песне, артисте и альбоме через последовательный порт.
char info[40]; char* titles[3] = {"Now Playing: ", "by: ", "Album: "}; for(int i=0; i<3; i++) { if(audio.getInfo("song1.wav",info,i) > 0) { Serial.print(titles[i]); Serial.println(info); } } audio.play("song1.wav");
Использование TIMER2
Часто данная библиотека используется с платами Uno, Nano и т.д. только с одним 16-разрядным таймером. Когда TIMER1 необходим для других целей, для воспроизведения аудио может быть использован TIMER2.
Выводы динамика – на Uno, Nano и т.д. только вывод 3.
Для включения использования 8-разрядного TIMER2 раскомментируйте строку #define USE_TIMER2
в секции пользовательских определений.
Примечания:
- этот вариант, как правило, является не самым лучшим решением;
- скорость воспроизведения будет слегка отличаться от использования 16-разрядных таймеров;
- воспроизведение на TIMER2 поддерживает нестандартные частоты дискретизации: 31,4 кГц, 23,5 кГц и 15,7 кГц;
- увеличение частоты дискретизации включено по умолчанию и в этом режиме не может быть изменено;
- рекомендуются частоты дискретизации 24–32 кГц при размере буфера 128.
ШИМ и опция rampMega
В попытках уменьшить шумы треска, создаваемые ШИМ, было опеределено четыре основных источника проблемы:
- включение ШИМ/таймеров на Arduino;
- выключение ШИМ/таймеров на Arduino;
- разница в значениях между треками;
- парсинг незвуковых данных.
Решения:
- 1 и 2. При тестировании на платах Arduino Duemianove и Mega я обнаружил необходимость в разных методах линейного изменения при включении и выключении. Надеюсь, это решит проблему на большинстве других плат.
- 3. Код линейного изменения между треками не меняется. При воспроизведении треков с разными частотами дискретизации используйте функцию
disable()
для выключения таймеров между изменениями. Смотрите также #4. - 4. Опция
HANDLE_TAGS
включена в pcmConfig.h, чтобы обеспечить правильное воспроизведение wav-файлов с включенными метаданными (ID3 или LIST).
Простое цифровое создание WAV файлов
Данные функции будут генерировать стандартные WAV файлы. Исходные данные с аналоговых входов или других датчиков могут быть записаны в файл для генерации цифрового звука, который может быть воспроизведен на любом устройстве, поддерживающем WAV файлы, или легко конвертирован в другие форматы.
Примечание: в дальнейшем будут добавлены и другие форматы wav.
Синтаксис:
audio.createWavTemplate(<Song Name>,<Sample Rate>);
audio.finalizeWavTemplate(<Song Name>);
Использование:
Смотрите пример, включенный в библиотеку. Создайте файл шаблона, затем запишите в него данные, начиная с 44 байта. Используйте команду finalizeWavTemplate
, чтобы добавить данные о размере файла перед воспроизведением.
Примечания: Если указанный файл существует, он будет перезаписан при создании, но обновлен только при завершении. Эти функции при использовании функций записи вызываются автоматически.
Запись звука
Запись звука находится всё еще в тестировании и может работать не так, как ожидается.
Функции:
startRecording
- Начать запись с указанного аналогового вывода.
stopRecording
- Остановить запись и финализировать wav файл.
Проходной режим – во время записи выдает звук на динамик.
Режимы: 0 – нормальный/нет; 1 – динамик включен; 2 – записи нет, вывод только на динамик.
Синтаксис:
audio.startRecording(<Song Name>,<Sample Rate>, <analog pin>);
audio.startRecording(<Song Name>,<Sample Rate>, <analog pin>, <passthrough mode>);
audio.stopRecording(<Song Name>);
Использование:
- раскомментируйте
#define buffSize 128
и#define ENABLE_RECORDING
в pcmConfig.h; - начните запись
audio.startRecording("sound.wav",16000,A0);
- остановите запись
audio.stopRecording("sound.wav");
Примечания:
- для записи рекомендуется библиотека SdFat;
- рекомендуются SD карты минимум класса 4;
- возможно, понадобится увеличение
buffSize
.