-
-
Notifications
You must be signed in to change notification settings - Fork 277
Playing Multiple Files in Sequence
With this library you have usually many ways to provide the same functionality. This chapter gives a short overview of the alternatives how you can multiple files (or streams) in a defined sequence:
You can just store the file names in an array and play them one after the other:
#include <SPI.h>
#include <SD.h>
#include "AudioTools.h"
#include "AudioTools/AudioCodecs/CodecAACHelix.h"
// files from https://filesamples.com/formats/aac
const char* filesNames[] = {
"/aac/sample1.aac",
"/aac/sample2.aac",
"/aac/sample3.aac",
"/aac/sample4.aac",
nullptr,
};
int fileIdx = 0;
const int chipSelect = PIN_AUDIO_KIT_SD_CARD_CS;
I2SStream i2s; // final output of decoded stream
EncodedAudioStream decoder(&i2s, new AACDecoderHelix()); // Decoding stream
File audioFile;
StreamCopy copier(decoder, audioFile);
void setup() {
Serial.begin(115200);
AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info);
// setup i2s
auto config = i2s.defaultConfig(TX_MODE);
config.sd_active = true;
i2s.begin(config);
// setup file
SD.begin(chipSelect);
audioFile = SD.open(filesNames[fileIdx++]);
if (!audioFile){
Serial.println("file does not exist");
stop();
}
// setup I2S based on sampling rate provided by decoder
decoder.begin();
}
void loop() {
if (!copier.copy()) {
// move to beginning on end
if (filesNames[fileIdx]==nullptr) fileIdx = 0;
// move to next file
audioFile = SD.open(filesNames[fileIdx++]);
}
}
We just manage the fileIdx variable to select the file to be played next.
If you use an audio format that can be concatenated (e.g. aac or mp3) you can use a CatStream to concatenate them. If you use a WAV file, you need to make sure that all have the same format and you need to set the initial file position past the header (e.g. with file.seek(44)); except for the first file.
#include <SPI.h>
#include <SD.h>
#include "AudioTools.h"
#include "AudioTools/AudioCodecs/CodecAACHelix.h"
const int chipSelect = PIN_AUDIO_KIT_SD_CARD_CS;
I2SStream i2s; // final output of decoded stream
EncodedAudioStream decoder(&i2s, new AACDecoderHelix()); // Decoding stream
File file[4];
CatStream cat;
StreamCopy copier(decoder, cat);
void setup() {
Serial.begin(115200);
AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info);
// setup i2s
auto config = i2s.defaultConfig(TX_MODE);
config.sd_active = true;
i2s.begin(config);
// setup file
SD.begin(chipSelect);
// define concatenated files
file[0] = SD.open("/aac/sample1.aac");
file[1] = SD.open("/aac/sample2.aac");
file[2] = SD.open("/aac/sample3.aac");
file[3] = SD.open("/aac/sample4.aac");
for (int j=0;j < 4; j++){
cat.add(file[j]);
}
// start decoder
decoder.begin();
}
void loop() {
if (!copier.copy()) {
stop();
}
}
If you select an index based AudioSource, you can define (or edit) the index file to define the files that should be played in sequence
- AudioSourceIdxSD: SD Library with an index file
- AudioSourceIdxSDFAT: SDFAT library with index file
- AudioSourceIdxSDMMC: SDMMC library with index file
Further information can be found in the AudioPlayer Wiki.
You can just implement an AudioSourceCallback that returns the files (or streams) in your desired sequence:
#include "Arduino.h"
#include "AudioTools.h"
#include "AudioTools/AudioCodecs/CodecMP3Helix.h"
#include <SPI.h>
#include <SD.h>
// files
const char* files[]{
"/file1.mp3",
"/file2.mp3"
};
int fileIndex = 0;
File actualFile;
// forward declarations
void callbackInit();
Stream* callbackNextStream(int offset);
// data
const int chipSelect=PIN_CS;
AudioSourceCallback source(callbackNextStream, callbackInit);
I2SStream i2s;
MP3DecoderHelix decoder;
AudioPlayer player(source, i2s, decoder);
void callbackInit() {
fileIndex = 0;
}
Stream* callbackNextStream(int offset) {
if (fileIndex >= sizeof(files)) return nullptr;
actualFile = SD.open(files[fileIndex]);
fileIndex += offset;
return &actualFile;
}
void setup() {
Serial.begin(115200);
AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info);
// setup SD
SD.begin(chipSelect);
// setup output
auto cfg = i2s.defaultConfig(TX_MODE);
i2s.begin(cfg);
// setup player
player.begin();
}
void loop() {
player.copy();
}
A cleaner approach would be to use your custom subclass of AudioSource where you can store all related data in the class instead of using global data and callback methods in your sketch.