PDA

View Full Version : Playing two (or more) RAW files



grubba
02-08-2016, 04:08 AM
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:



#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:


#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);
}

PaulStoffregen
02-08-2016, 09:30 AM
Try editing hardware/teensy/avr/libraries/SD/SD_t3.h. Find any uncomment this:



// This Teensy 3.x optimized version is a work-in-progress.
// Uncomment this line to use the Teensy version. Otherwise,
// the normal SD library is used.
//#define USE_TEENSY3_OPTIMIZED_CODE


Please let me know if this solves the problem? As you can probably guess, this optimization work is still somewhat experimental. Its main limitation is you can't write at all to the SD card with it enabled.

grubba
02-08-2016, 05:42 PM
Paul,

Thanks for the quick response. It turned out to be the MicroSD card. I had one exactly like the one you suggested (an 8G SanDisk Ultra) and given that the tested seek times were within reason (around 800us), it didn't occur to me to test a different one. I found another, newer one (16G), formatted it and made sure to copy the files one at a time from the shell this time, so they would be contiguous (copying from the Finder ends up with multiple simultaneous copies and the writes are all over the place). The seek/read block time now is down to around 480us.

The code is usually running one loop continuously (the file is about a minute long) and the second instance plays "alarms" and "announcements" (adjusting the mixer gains accordingly in the process). It now all works as expected.

Thanks again for such a cool dev board!

NewtonBIV
04-21-2020, 05:08 AM
Try editing hardware/teensy/avr/libraries/SD/SD_t3.h. Find any uncomment this:



// This Teensy 3.x optimized version is a work-in-progress.
// Uncomment this line to use the Teensy version. Otherwise,
// the normal SD library is used.
//#define USE_TEENSY3_OPTIMIZED_CODE


Please let me know if this solves the problem? As you can probably guess, this optimization work is still somewhat experimental. Its main limitation is you can't write at all to the SD card with it enabled.

Hey there everyone! Listen, hate to be a bother here but i've tried this several different ways and it won't let me save the file with the change. Is that what we are supposed to do? Save it once we uncomment it? Or do we just type in the USE_TEENSY3_OPTIMIZED_CODE in whichever sketches we want to run the optimized alternative with? Thank you and man i'm super psyched to try this out grubba i've been searching high and low for a way to do pretty much exactly what your code says it does (i'm sure it works as advertised, just saying i haven't gotten it to yet and am giddy with anticipation).

thanks!

AlanK
04-21-2020, 08:28 AM
Hi NewtonBIV,
Try running the editor as Administrator if you're using Windows - then you should be able to save the changes.

NewtonBIV
04-21-2020, 08:50 PM
Hi NewtonBIV,
Try running the editor as Administrator if you're using Windows - then you should be able to save the changes.

Aha! Of course, i was getting mixed up since it was an h file I kept trying to open the file as an administrator. I just opened up an instance of notepad as admin and changed it there. For some reason now none of my sketches are working. You've already helped bigtime, and thanks for that if I don't hear back. If you were still curious about the saga of Newton and his ridiculously simple (should be at least) mission here is the error code i'm getting, which honestly seems completely unrelated to my actual code though i went ahead and put in the code that got this particular error code (same with any sketch I run pretty much so surely has to be the SD_t3.h adjustment and worst case i'll just change it back, was playing two files close enough to simultaneously i was able to do what I needed sort of anyway).

ERROR CODE:

C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\SD\car d_t3.cpp:31:29: warning: declaration of 'volatile unsigned int* SDClass::csreg' outside of class is not definition [-fpermissive]

volatile uint8_t * SDClass::csreg;

^

C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\SD\car d_t3.cpp:32:18: error: conflicting declaration 'uint8_t SDClass::csmask'

uint8_t SDClass::csmask;

^

In file included from C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\SD\car d_t3.cpp:28:0:

C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\SD\SD_ t3.h:86:21: note: previous declaration as 'unsigned int SDClass::csmask'

static IO_REG_TYPE csmask;

^

C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\SD\car d_t3.cpp:32:18: warning: declaration of 'unsigned int SDClass::csmask' outside of class is not definition [-fpermissive]

uint8_t SDClass::csmask;

^

C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\SD\car d_t3.cpp: In static member function 'static bool SDClass::sd_read(uint32_t, void*)':

C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\SD\car d_t3.cpp:113:2: error: 'SPI0_PUSHR' was not declared in this scope

SPI0_PUSHR = 0xFFFF | SPI_PUSHR_CTAS(1);

^

C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\SD\car d_t3.cpp:113:40: error: 'SPI_PUSHR_CTAS' was not declared in this scope

SPI0_PUSHR = 0xFFFF | SPI_PUSHR_CTAS(1);

^

C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\SD\car d_t3.cpp:116:12: error: 'SPI0_SR' was not declared in this scope

while (!(SPI0_SR & 0xF0)) ;

^

C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\SD\car d_t3.cpp:118:17: error: 'SPI0_POPR' was not declared in this scope

uint32_t in = SPI0_POPR;

^

C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\SD\car d_t3.cpp:122:11: error: 'SPI0_SR' was not declared in this scope

while (!(SPI0_SR & 0xF0)) ;

^

C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\SD\car d_t3.cpp:123:16: error: 'SPI0_POPR' was not declared in this scope

uint32_t in = SPI0_POPR;

^

C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\SD\car d_t3.cpp:126:11: error: 'SPI0_SR' was not declared in this scope

while (!(SPI0_SR & 0xF0)) ;

^

Multiple libraries were found for "SD.h"
Used: C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\SD
Not used: C:\Program Files (x86)\Arduino\libraries\SD
Using library Audio at version 1.3 in folder: C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\Audio
Using library SPI at version 1.0 in folder: C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\SPI
Using library SD at version 1.2.2 in folder: C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\SD
Using library SerialFlash at version 0.5 in folder: C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\Serial Flash
Using library Wire at version 1.0 in folder: C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\Wire
Using library Bounce in folder: C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\Bounce (legacy)
Error compiling for board Teensy 4.0.




AND HERE WAS THE CODE THAT GOT THIS ERROR:


#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>
#include <Bounce.h>

AudioPlaySdWav playSdWav2;
AudioPlaySdWav playSdWav1;
AudioMixer4 mixer1;
AudioMixer4 mixer2;
AudioOutputI2S i2s1;
AudioConnection patchCord1(playSdWav2, 0, mixer2, 0);
AudioConnection patchCord2(playSdWav2, 1, mixer1, 1);
AudioConnection patchCord3(playSdWav1, 0, mixer1, 0);
AudioConnection patchCord4(playSdWav1, 1, mixer2, 1);
AudioConnection patchCord5(mixer1, 0, i2s1, 0);
AudioConnection patchCord6(mixer2, 0, i2s1, 1);
AudioControlSGTL5000 sgtl5000_1;

Bounce button0 = Bounce(0, 15); //this is the GLOBAL AUTO STOP & START
Bounce button1 = Bounce(1, 15); //DECK 1 SKIP TO NEXT TRACK
Bounce button2 = Bounce(2, 15); //DECK 1 GO BACK TO PREVIOUS TRACK
Bounce button3 = Bounce(3, 15); //DECK 2 SKIP TO NEXT TRACK
Bounce button4 = Bounce(4, 15); //DECK 2 GO BACK TO PREVIOUS TRACK

#define SDCARD_CS_PIN 10
#define SDCARD_MOSI_PIN 11
#define SDCARD_SCK_PIN 13

void setup() {
Serial.begin(9600);
AudioMemory(8);
sgtl5000_1.enable();
sgtl5000_1.volume(0.5);
SPI.setMOSI(SDCARD_MOSI_PIN);
SPI.setSCK(SDCARD_SCK_PIN);
if (!(SD.begin(SDCARD_CS_PIN))) {
while (1) {
Serial.println("Unable to access the SD card");
delay(500);
}
}
pinMode(0, INPUT_PULLUP); //STOP ALL & START OVER AS IS
pinMode(1, INPUT_PULLUP); //DECK 1 SKIP
pinMode(2, INPUT_PULLUP); //DECK 1 REWIND
pinMode(3, INPUT_PULLUP); //DECK 2 SKIP
pinMode(4, INPUT_PULLUP); //DECK 2 REWIND
delay(1000);
}
int filenumber = 0; //track playing on deck1
int filenumber1 = 0; //track playing on deck2
const char * filelist[4] = {
"DRUM87B1.WAV", "DRUM87B3.WAV", "800001.WAV", "800003.WAV"
};
const char * filelist1[4] = {
"BASS87A#.WAV", "BASS87E1.WAV", "80000006.WAV", "80000008.WAV"
};
void loop() {
if (playSdWav1.isPlaying() == false) {
const char *filename = filelist[filenumber];
const char *filename1 = filelist1[filenumber1];
playSdWav1.play(filename);
playSdWav2.play(filename1);
Serial.println("both played");
delay(10);
}
button0.update();
button1.update();
button2.update();
button3.update();
button4.update();
if (button0.fallingEdge()) {
playSdWav1.stop();
playSdWav2.stop();
Serial.println("Starting Over");
delay(5);
}
if (button1.fallingEdge()) {
filenumber = filenumber + 1;
if (filenumber >= 3) filenumber = 0;
Serial.println("Next Track Deck1");
}
if (button2.fallingEdge()) {
filenumber = filenumber - 1;
if (filenumber < 0) filenumber = filenumber + 4;
Serial.println("Rewind Track Deck1");
}
if (button3.fallingEdge()) {
filenumber1 = filenumber1 + 1;
if (filenumber1 >= 3) filenumber1 = 0;
Serial.println("Next Track Deck2");
}
if (button4.fallingEdge()) {
filenumber1 = filenumber1 - 1;
if (filenumber1 < 0) filenumber1 = filenumber1 + 4;
Serial.println("Rewind Track Deck2");
}
}