I'm having a bit of a hard time playing more than one (raw) file at a time. Even using the Part_2_02_Mixers.ino tutorial code shows the same problem. Most of the time SD.open() fails or when it works, the sound is garbled. Playing one file at a time (one from each instance of the SD raw file player) works fine. Only when playing with two simultaneously this happens.
My project is a simple Sound Machine (noise machine), which plays an audio loop indefinitely along with an alarm clock. It works in conjunction with an ESP8266, which handles the TCP/IP stack for getting the time, integration with the rest of my automation system, and also handling the (clock) LED array (more on that (OTA) later on a different thread -- in fact, the reason I'm doing the bulk on the work on the ESP8266 is the ease of uploading updates).
For that I created a new derivation of AudioStream, using AudioPlaySdRaw as a starting point.
The main question: Is it really possible to play more than one file at a time? The SD header file has a comment on the open method that says otherwise:
"Note that currently only one file can be open at a time."
For what's worth, here is my AudioPlaySdRawLoop class, which plays mono or stereo RAW files and optionally loops them.
Header:
And the implementation:
My project is a simple Sound Machine (noise machine), which plays an audio loop indefinitely along with an alarm clock. It works in conjunction with an ESP8266, which handles the TCP/IP stack for getting the time, integration with the rest of my automation system, and also handling the (clock) LED array (more on that (OTA) later on a different thread -- in fact, the reason I'm doing the bulk on the work on the ESP8266 is the ease of uploading updates).
For that I created a new derivation of AudioStream, using AudioPlaySdRaw as a starting point.
The main question: Is it really possible to play more than one file at a time? The SD header file has a comment on the open method that says otherwise:
"Note that currently only one file can be open at a time."
For what's worth, here is my AudioPlaySdRawLoop class, which plays mono or stereo RAW files and optionally loops them.
Header:
Code:
#ifndef play_raw_h_
#define play_raw_h_
#include "AudioStream.h"
#include "SD.h"
class AudioPlaySdRawLoop : public AudioStream
{
public:
AudioPlaySdRawLoop();
void begin ();
bool play (const char *filename, bool stereo = false, bool loop = true);
void stop ();
bool isPlaying () { return _playing; }
uint32_t positionMillis ();
uint32_t lengthMillis ();
void update ();
void endLoop () { _loop = false; }
//-- For debugging
uint32_t rewindTime () { return _rewindTime; }
private:
uint32_t _rewind (uint32_t n);
void _transmit (audio_block_t* blockL, audio_block_t* blockR = NULL);
private:
bool _stereo;
bool _loop;
File _rawfile;
uint32_t _read_size;
uint32_t _file_size;
volatile uint32_t _file_offset;
volatile bool _playing;
uint8_t _buffer[(AUDIO_BLOCK_SAMPLES * 2) * 2];
volatile uint32_t _rewindTime;
};
#endif
And the implementation:
Code:
#include "soundmachine.h"
#include "play_raw.h"
#include "spi_interrupt.h"
#define B2M (uint32_t)((double)4294967296000.0 / AUDIO_SAMPLE_RATE_EXACT)
AudioPlaySdRawLoop::AudioPlaySdRawLoop()
: AudioStream(0, NULL)
{
begin();
}
//-----------------------------------------------------------------------------
void
AudioPlaySdRawLoop::begin()
{
_stereo = false;
_playing = false;
_file_offset = 0;
_file_size = 0;
_read_size = ((AUDIO_BLOCK_SAMPLES * 2));
_rewindTime = 0;
}
//-----------------------------------------------------------------------------
bool
AudioPlaySdRawLoop::play(const char *filename, bool stereo, bool loop)
{
stop();
Serial.print("Playing ");
Serial.print(filename);
if(stereo)
Serial.println(" Stereo");
else
Serial.println(" Mono");
AudioStartUsingSPI();
__disable_irq();
_rawfile = SD.open(filename);
__enable_irq();
if (!_rawfile) {
AudioStopUsingSPI();
return false;
}
_stereo = stereo;
_loop = loop;
if(_stereo)
_read_size = ((AUDIO_BLOCK_SAMPLES * 2) * 2);
else
_read_size = (AUDIO_BLOCK_SAMPLES * 2);
_file_size = _rawfile.size();
_file_offset = 0;
_rewindTime = 0;
_playing = true;
return true;
}
//-----------------------------------------------------------------------------
void
AudioPlaySdRawLoop::stop()
{
__disable_irq();
if (_playing) {
_playing = false;
_rawfile.close();
__enable_irq();
AudioStopUsingSPI();
} else {
__enable_irq();
}
}
//-----------------------------------------------------------------------------
void
AudioPlaySdRawLoop::update()
{
if (!_playing)
return;
bool close = false;
audio_block_t* blockL = NULL;
audio_block_t* blockR = NULL;
blockL = allocate();
if (!blockL)
return;
if(_stereo) {
blockR = allocate();
if (!blockR) {
release(blockL);
return;
}
}
if (_rawfile.available()) {
uint32_t n = _rawfile.read(_buffer, _read_size);
_file_offset += n;
if (_file_offset >= _file_size || n < _read_size) {
if(_loop)
_rewind(n);
else {
//-- Zero out rest of buffer and be done with it.
memset(&_buffer[n], 0, _read_size - n);
close = true;
}
}
_transmit(blockL, blockR);
} else {
if(_loop) {
if(_rewind(0))
_transmit(blockL, blockR);
} else
close = true;
}
release(blockL);
if(blockR)
release(blockR);
if(close) {
_rawfile.close();
AudioStopUsingSPI();
_playing = false;
}
}
//-----------------------------------------------------------------------------
uint32_t
AudioPlaySdRawLoop::positionMillis()
{
if(_stereo)
return ((uint64_t)_file_offset * B2M) >> 32;
else
return ((uint64_t)_file_offset * (B2M >> 1)) >> 32;
}
//-----------------------------------------------------------------------------
uint32_t
AudioPlaySdRawLoop::lengthMillis()
{
if(_stereo)
return ((uint64_t)_file_size * B2M) >> 32;
else
return ((uint64_t)_file_size * (B2M >> 1)) >> 32;
}
//-----------------------------------------------------------------------------
uint32_t
AudioPlaySdRawLoop::_rewind(uint32_t n)
{
uint32_t t = micros();
_rawfile.seek(0);
_file_offset = 0;
n = _rawfile.read(&_buffer[n], _read_size - n);
_file_offset += n;
_rewindTime = micros() - t;
return n;
}
//-----------------------------------------------------------------------------
void
AudioPlaySdRawLoop::_transmit(audio_block_t* blockL, audio_block_t* blockR)
{
if(_stereo) {
uint8_t lsb, msb;
uint8_t* ptr = _buffer;
for (int i = 0; i < AUDIO_BLOCK_SAMPLES; i++) {
lsb = *ptr++;
msb = *ptr++;
blockL->data[i] = (msb << 8) | lsb;
lsb = *ptr++;
msb = *ptr++;
blockR->data[i] = (msb << 8) | lsb;
}
} else {
memcpy(blockL->data, _buffer, (AUDIO_BLOCK_SAMPLES * 2));
}
transmit(blockL, 0);
if(_stereo)
transmit(blockR, 1);
else
transmit(blockL, 1);
}