recording/playing back audio on multiple SD cards

mattkuebrich

Active member
I've got a Teensy 4.1 with the Audio Adapter hooked up. I want to be able to address the SD card reader on the Teensy and the one on the Audio Adapter separately.
I've modified the Recorder.ino code below to create instances for both SD readers. I can successfully write the audio (right now just to sd1), but AudioPlaySdRaw isn't playing it back. It's very likely I'm doing this all wrong. Any ideas?

Code:
// Record sound as raw data to a SD card, and play it back.

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

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

SdFs sd1;
SdFs sd2;
FsFile frec;

// GUItool: begin automatically generated code
AudioInputI2S            i2s2;           //xy=105,63
AudioAnalyzePeak         peak1;          //xy=278,108
AudioRecordQueue         queue1;         //xy=281,63
AudioPlaySdRaw           playRaw1;       //xy=302,157
AudioOutputI2S           i2s1;           //xy=470,120
AudioConnection          patchCord1(i2s2, 0, queue1, 0);
AudioConnection          patchCord2(i2s2, 0, peak1, 0);
AudioConnection          patchCord3(playRaw1, 0, i2s1, 0);
AudioConnection          patchCord4(playRaw1, 0, i2s1, 1);
AudioControlSGTL5000     sgtl5000_1;     //xy=265,212
// GUItool: end automatically generated code

// Bounce objects to easily and reliably read the buttons
Bounce buttonRecord = Bounce(0, 8);
Bounce buttonStop =   Bounce(1, 8);  // 8 = 8 ms debounce time
Bounce buttonPlay =   Bounce(2, 8);

// which input on the audio shield will be used?
const int myInput = AUDIO_INPUT_LINEIN;

// Remember which mode we're doing
int mode = 0;  // 0=stopped, 1=recording, 2=playing

void setup() {


  // Configure the pushbutton pins
  pinMode(0, INPUT_PULLUP);
  pinMode(1, INPUT_PULLUP);
  pinMode(2, INPUT_PULLUP);

  // Audio connections require memory, and the record queue
  // uses this memory to buffer incoming audio.
  AudioMemory(60);

  // Enable the audio shield, select input, and enable output
  sgtl5000_1.enable();
  sgtl5000_1.inputSelect(myInput);
  sgtl5000_1.volume(0.7);

  // initialize the internal Teensy 4.1 SD card
  //if (!sd1.begin(SdioConfig(DMA_SDIO))) {
  if (!sd1.begin(SdioConfig(FIFO_SDIO))) {
    Serial.println("error sd1.begin");
  }

  // initialize the Audio Adapter SD card
  if (!sd2.begin(SdSpiConfig(SDCARD_CS_PIN, DEDICATED_SPI, SD_SCK_MHZ(16)))) {
    Serial.println("error sd2.begin");
  }

  // Initialize the SD card
  SPI.setMOSI(SDCARD_MOSI_PIN);
  SPI.setSCK(SDCARD_SCK_PIN);

  //sd1.chvol(); 

}


void loop() {
  // First, read the buttons
  buttonRecord.update();
  buttonStop.update();
  buttonPlay.update();

  // Respond to button presses
  if (buttonRecord.fallingEdge()) {
    Serial.println("Record Button Press");
    if (mode == 2) stopPlaying();
    if (mode == 0) startRecording();
  }
  if (buttonStop.fallingEdge()) {
    Serial.println("Stop Button Press");
    if (mode == 1) stopRecording();
    if (mode == 2) stopPlaying();
  }
  if (buttonPlay.fallingEdge()) {
    Serial.println("Play Button Press");
    if (mode == 1) stopRecording();
    if (mode == 0) startPlaying();
  }

  // If we're playing or recording, carry on...
  if (mode == 1) {
    continueRecording();
  }
  if (mode == 2) {
    continuePlaying();
  }
}


void startRecording() {
  Serial.println("startRecording");
  if (sd1.exists("RECORD.RAW")) {
    // The SD library writes new data to the end of the
    // file, so to start a new recording, the old file
    // must be deleted before new data is written.
    sd1.remove("RECORD.RAW");
  }
  frec = sd1.open("RECORD.RAW", O_WRITE | O_CREAT);

  if (frec) {
    //Serial.println("frec!");
    queue1.begin();
    mode = 1;
  }
}

void continueRecording() {
  if (queue1.available() >= 2) {
    byte buffer[512];
    // Fetch 2 blocks from the audio library and copy
    // into a 512 byte buffer.  The Arduino SD library
    // is most efficient when full 512 byte sector size
    // writes are used.
    memcpy(buffer, queue1.readBuffer(), 256);
    queue1.freeBuffer();
    memcpy(buffer + 256, queue1.readBuffer(), 256);
    queue1.freeBuffer();
    // write all 512 bytes to the SD card
    frec.write(buffer, 512);
  }
}

void stopRecording() {
  Serial.println("stopRecording");
  queue1.end();
  if (mode == 1) {
    while (queue1.available() > 0) {
      frec.write((byte*)queue1.readBuffer(), 256);
      queue1.freeBuffer();
    }
    frec.close();
  }
  mode = 0;
}

void startPlaying() {
  Serial.println("startPlaying");
  playRaw1.play("RECORD.RAW");
  mode = 2;
}

void continuePlaying() {
  if (!playRaw1.isPlaying()) {
    playRaw1.stop();
    mode = 0;
  }
}

void stopPlaying() {
  Serial.println("stopPlaying");
  if (mode == 2) playRaw1.stop();
  mode = 0;
}
 
Any ideas on this? I took a look at play_sd_raw.cpp and it contains:
Code:
rawfile = SD.open(filename);

I assume that's why the above doesn't work, as I'm using instances called sd1 and sd2. But I don't know how to remedy that.
 
@KurtE
That PR is a little more than a year old and I forgot all about it. The PR did 2 things added file.open support for both raw files as well as wav files.
I remember :D. That is why I assumed it was in already... But I think it got lost in the thought that it was somehow intertwined with adding support for some other stuff, where the code had a different privacy...

Maybe we should bump that PR and/or for the code in this thread I tried a minimalist change to make it build for different Fs...

In the header file:
Code:
class AudioPlaySdRaw : public AudioStream
{
public:
	AudioPlaySdRaw(void) : AudioStream(0, NULL) { begin(); }
	void begin(void);
	bool play(const char *filename[COLOR="#FF0000"], FS &fs=SD[/COLOR]);
and .cpp file:
Code:
bool AudioPlaySdRaw::play(const char *filename[COLOR="#FF0000"], FS &fs[/COLOR])
{
	stop();
#if defined(HAS_KINETIS_SDHC)
	if (!(SIM_SCGC3 & SIM_SCGC3_SDHC)) AudioStartUsingSPI();
#else
	AudioStartUsingSPI();
#endif
	__disable_irq();
	rawfile =[COLOR="#FF0000"] fs.[/COLOR]open(filename);
	__enable_irq();
Probably would do same for .wav file...

However the code that was posted at beginning of the thread will not work passing in sd1 or sd2 like: playRaw1.play("RECORD.RAW", sd1);
As sd1 and sd2 are not derived from FS, they are SdFs... I edited it make it compile like:
Code:
// Record sound as raw data to a SD card, and play it back.

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

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

SDClass sd1;
SDClass sd2;
File frec;

// GUItool: begin automatically generated code
AudioInputI2S            i2s2;           //xy=105,63
AudioAnalyzePeak         peak1;          //xy=278,108
AudioRecordQueue         queue1;         //xy=281,63
AudioPlaySdRaw           playRaw1;       //xy=302,157
AudioOutputI2S           i2s1;           //xy=470,120
AudioConnection          patchCord1(i2s2, 0, queue1, 0);
AudioConnection          patchCord2(i2s2, 0, peak1, 0);
AudioConnection          patchCord3(playRaw1, 0, i2s1, 0);
AudioConnection          patchCord4(playRaw1, 0, i2s1, 1);
AudioControlSGTL5000     sgtl5000_1;     //xy=265,212
// GUItool: end automatically generated code

// Bounce objects to easily and reliably read the buttons
Bounce buttonRecord = Bounce(0, 8);
Bounce buttonStop =   Bounce(1, 8);  // 8 = 8 ms debounce time
Bounce buttonPlay =   Bounce(2, 8);

// which input on the audio shield will be used?
const int myInput = AUDIO_INPUT_LINEIN;

// Remember which mode we're doing
int mode = 0;  // 0=stopped, 1=recording, 2=playing

void setup() {


  // Configure the pushbutton pins
  pinMode(0, INPUT_PULLUP);
  pinMode(1, INPUT_PULLUP);
  pinMode(2, INPUT_PULLUP);

  // Audio connections require memory, and the record queue
  // uses this memory to buffer incoming audio.
  AudioMemory(60);

  // Enable the audio shield, select input, and enable output
  sgtl5000_1.enable();
  sgtl5000_1.inputSelect(myInput);
  sgtl5000_1.volume(0.7);

  // initialize the internal Teensy 4.1 SD card
  //if (!sd1.begin(SdioConfig(DMA_SDIO))) {
  if (!sd1.sdfs.begin(SdioConfig(FIFO_SDIO))) {
    Serial.println("error sd1.begin");
  }

  // initialize the Audio Adapter SD card
  if (!sd2.sdfs.begin(SdSpiConfig(SDCARD_CS_PIN, DEDICATED_SPI, SD_SCK_MHZ(16)))) {
    Serial.println("error sd2.begin");
  }

  // Initialize the SD card
  SPI.setMOSI(SDCARD_MOSI_PIN);
  SPI.setSCK(SDCARD_SCK_PIN);

  //sd1.chvol(); 

}


void loop() {
  // First, read the buttons
  buttonRecord.update();
  buttonStop.update();
  buttonPlay.update();

  // Respond to button presses
  if (buttonRecord.fallingEdge()) {
    Serial.println("Record Button Press");
    if (mode == 2) stopPlaying();
    if (mode == 0) startRecording();
  }
  if (buttonStop.fallingEdge()) {
    Serial.println("Stop Button Press");
    if (mode == 1) stopRecording();
    if (mode == 2) stopPlaying();
  }
  if (buttonPlay.fallingEdge()) {
    Serial.println("Play Button Press");
    if (mode == 1) stopRecording();
    if (mode == 0) startPlaying();
  }

  // If we're playing or recording, carry on...
  if (mode == 1) {
    continueRecording();
  }
  if (mode == 2) {
    continuePlaying();
  }
}


void startRecording() {
  Serial.println("startRecording");
  if (sd1.exists("RECORD.RAW")) {
    // The SD library writes new data to the end of the
    // file, so to start a new recording, the old file
    // must be deleted before new data is written.
    sd1.remove("RECORD.RAW");
  }
  frec = sd1.open("RECORD.RAW", FILE_WRITE_BEGIN);

  if (frec) {
    //Serial.println("frec!");
    queue1.begin();
    mode = 1;
  }
}

void continueRecording() {
  if (queue1.available() >= 2) {
    byte buffer[512];
    // Fetch 2 blocks from the audio library and copy
    // into a 512 byte buffer.  The Arduino SD library
    // is most efficient when full 512 byte sector size
    // writes are used.
    memcpy(buffer, queue1.readBuffer(), 256);
    queue1.freeBuffer();
    memcpy(buffer + 256, queue1.readBuffer(), 256);
    queue1.freeBuffer();
    // write all 512 bytes to the SD card
    frec.write(buffer, 512);
  }
}

void stopRecording() {
  Serial.println("stopRecording");
  queue1.end();
  if (mode == 1) {
    while (queue1.available() > 0) {
      frec.write((byte*)queue1.readBuffer(), 256);
      queue1.freeBuffer();
    }
    frec.close();
  }
  mode = 0;
}

void startPlaying() {
  Serial.println("startPlaying");
  playRaw1.play("RECORD.RAW", sd1);
  mode = 2;
}

void continuePlaying() {
  if (!playRaw1.isPlaying()) {
    playRaw1.stop();
    mode = 0;
  }
}

void stopPlaying() {
  Serial.println("stopPlaying");
  if (mode == 2) playRaw1.stop();
  mode = 0;
}

But I did not upload or try it as I did not have anything setup with Audio board especially one with microphone.
 
@KurtE

Good point. When I did the PR it was focused on using FS and not SDFs. Unfortunately I am not set up either. I do have the board with the microphone on it but currently in process of updating windows on a SBC. May get a chance and test later.
 
Wow, thanks all for the help! Last night I was struggling to adapt mjs513's original updates to AudioPlaySdRaw for my situation (using SdFs).

I'll be able to test this new code tonight and report back.
 
@All - I have an audio board with a mic on it so I tested @KurtE's modified sketch:
Code:
// Record sound as raw data to a SD card, and play it back.

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

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

SDClass sd1;
SDClass sd2;
File frec;

// GUItool: begin automatically generated code
AudioInputI2S            i2s2;           //xy=105,63
AudioAnalyzePeak         peak1;          //xy=278,108
AudioRecordQueue         queue1;         //xy=281,63
AudioPlaySdRaw           playRaw1;       //xy=302,157
AudioOutputI2S           i2s1;           //xy=470,120
AudioConnection          patchCord1(i2s2, 0, queue1, 0);
AudioConnection          patchCord2(i2s2, 0, peak1, 0);
AudioConnection          patchCord3(playRaw1, 0, i2s1, 0);
AudioConnection          patchCord4(playRaw1, 0, i2s1, 1);
AudioControlSGTL5000     sgtl5000_1;     //xy=265,212
// GUItool: end automatically generated code

// Bounce objects to easily and reliably read the buttons
Bounce buttonRecord = Bounce(34, 8);
Bounce buttonStop =   Bounce(33, 8);  // 8 = 8 ms debounce time
Bounce buttonPlay =   Bounce(35, 8);

// which input on the audio shield will be used?
//const int myInput = AUDIO_INPUT_LINEIN;
const int myInput = AUDIO_INPUT_MIC; // I only had the mic available

// Remember which mode we're doing
int mode = 0;  // 0=stopped, 1=recording, 2=playing

void setup() {

  // Configure the pushbutton pins
  pinMode(33, INPUT_PULLUP);
  pinMode(34, INPUT_PULLUP);
  pinMode(35, INPUT_PULLUP);

  // Audio connections require memory, and the record queue
  // uses this memory to buffer incoming audio.
  AudioMemory(60);

  // Enable the audio shield, select input, and enable output
  sgtl5000_1.enable();
  sgtl5000_1.inputSelect(myInput);
  sgtl5000_1.volume(0.7);

  // initialize the internal Teensy 4.1 SD card
  if (!sd1.sdfs.begin(SdioConfig(FIFO_SDIO))) {
    Serial.println("error sd1.begin");
  }

  // initialize the Audio Adapter SD card
  if (!sd2.sdfs.begin(SdSpiConfig(SDCARD_CS_PIN, DEDICATED_SPI, SD_SCK_MHZ(16)))) {
    Serial.println("error sd2.begin");
  }

  // Initialize the SD card
  SPI.setMOSI(SDCARD_MOSI_PIN);
  SPI.setSCK(SDCARD_SCK_PIN);

  //sd1.chvol(); 

}


void loop() {
  // First, read the buttons
  buttonRecord.update();
  buttonStop.update();
  buttonPlay.update();

  // Respond to button presses
  if (buttonRecord.fallingEdge()) {
    Serial.println("Record Button Press");
    if (mode == 2) stopPlaying();
    if (mode == 0) startRecording();
  }
  if (buttonStop.fallingEdge()) {
    Serial.println("Stop Button Press");
    if (mode == 1) stopRecording();
    if (mode == 2) stopPlaying();
  }
  if (buttonPlay.fallingEdge()) {
    Serial.println("Play Button Press");
    if (mode == 1) stopRecording();
    if (mode == 0) startPlaying();
  }

  // If we're playing or recording, carry on...
  if (mode == 1) {
    continueRecording();
  }
  if (mode == 2) {
    continuePlaying();
  }
}


void startRecording() {
  Serial.println("startRecording");
//  if (sd1.exists("RECORD.RAW")) {
    // The SD library writes new data to the end of the
    // file, so to start a new recording, the old file
    // must be deleted before new data is written.
//    sd1.remove("RECORD.RAW");
//  }
// FILE_WRITE_BEGIN actually starts at beginning of file (Position 0).
  frec = sd1.open("RECORD.RAW", FILE_WRITE_BEGIN); // SDSPI
//  frec = sd2.open("RECORD.RAW", FILE_WRITE_BEGIN);   // SDIO - WILL NOT PLAY BACK

  if (frec) {
    //Serial.println("frec!");
    queue1.begin();
    mode = 1;
  }
}

void continueRecording() {
  if (queue1.available() >= 2) {
    byte buffer[512];
    // Fetch 2 blocks from the audio library and copy
    // into a 512 byte buffer.  The Arduino SD library
    // is most efficient when full 512 byte sector size
    // writes are used.
    memcpy(buffer, queue1.readBuffer(), 256);
    queue1.freeBuffer();
    memcpy(buffer + 256, queue1.readBuffer(), 256);
    queue1.freeBuffer();
    // write all 512 bytes to the SD card
    frec.write(buffer, 512);
  }
}

void stopRecording() {
  Serial.println("stopRecording");
  queue1.end();
  if (mode == 1) {
    while (queue1.available() > 0) {
      frec.write((byte*)queue1.readBuffer(), 256);
      queue1.freeBuffer();
    }
    frec.close();
  }
  mode = 0;
}

void startPlaying() {
  Serial.println("startPlaying");
  playRaw1.play([COLOR="#FF0000"]&[/COLOR]sd1, "RECORD.RAW");
  mode = 2;
}

void continuePlaying() {
  if (!playRaw1.isPlaying()) {
    playRaw1.stop();
    mode = 0;
  }
}

void stopPlaying() {
  Serial.println("stopPlaying");
  if (mode == 2) playRaw1.stop();
  mode = 0;
}
It would not compile at first because I needed to send the address of sd1 "&sd1" (shown in red above). After that It compiled.
Now it worked as expected using sd1 (SDIO) but for some reason sd2 (SDSPI) only shows it recording as indicated by the builtin LED. This is the SD card slot on the audio board. It will not play back the recorded file like SDIO does. The builtin LED on the T41 blips once and that is all. No playback.

I'll attach the zipped modified play_sd_wav and play_sd_raw files I used. I just renamed them in the Audio library and copied in the replacements.

Ran out of time to test further:(
 

Attachments

  • play_sd_wav_raw.zip
    9.1 KB · Views: 30
Just tried this out. With @KurtE's edits to AudioPlaySdRaw, it compiled for me with no problems. But I had the same issue as @wwatson when I tried to play audio back from the Audio Adapter's SD reader (sd2). I just hear a click in my headphones and the LED blinks, but no playback.
 
Thanks,

Looks like the simple version does not work. Not sure why yet, will take a look
 
I changed mine to use pointers instead of references. Did not appear to make any difference...

But: I hacked up the wave player example audio sketch:
Code:
// Simple WAV file player example
//
// Three types of output may be used, by configuring the code below.
//
//   1: Digital I2S - Normally used with the audio shield:
//         http://www.pjrc.com/store/teensy3_audio.html
//
//   2: Digital S/PDIF - Connect pin 22 to a S/PDIF transmitter
//         https://www.oshpark.com/shared_projects/KcDBKHta
//
//   3: Analog DAC - Connect the DAC pin to an amplified speaker
//         http://www.pjrc.com/teensy/gui/?info=AudioOutputAnalog
//
// To configure the output type, first uncomment one of the three
// output objects.  If not using the audio shield, comment out
// the sgtl5000_1 lines in setup(), so it does not wait forever
// trying to configure the SGTL5000 codec chip.
//
// The SD card may connect to different pins, depending on the
// hardware you are using.  Uncomment or configure the SD card
// pins to match your hardware.
//
// Data files to put on your SD card can be downloaded here:
//   http://www.pjrc.com/teensy/td_libs_AudioDataFiles.html
//
// This example code is in the public domain.

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

AudioPlaySdWav           playWav1;
// Use one of these 3 output types: Digital I2S, Digital S/PDIF, or Analog DAC
AudioOutputI2S           audioOutput;
//AudioOutputSPDIF       audioOutput;
//AudioOutputAnalog      audioOutput;
//On Teensy LC, use this for the Teensy Audio Shield:
//AudioOutputI2Sslave    audioOutput;

AudioConnection          patchCord1(playWav1, 0, audioOutput, 0);
AudioConnection          patchCord2(playWav1, 1, audioOutput, 1);
AudioControlSGTL5000     sgtl5000_1;

// Use these with the Teensy Audio Shield
#define SDCARD2_CS_PIN    10
#define SDCARD_CS_PIN   BUILTIN_SDCARD
//#define SDCARD_MOSI_PIN  7
//#define SDCARD_SCK_PIN   14

// Use these with the Teensy 3.5 & 3.6 SD card
//#define SDCARD_CS_PIN    BUILTIN_SDCARD
//#define SDCARD_MOSI_PIN  11  // not actually used
//#define SDCARD_SCK_PIN   13  // not actually used

// Use these for the SD+Wiz820 or other adaptors
//#define SDCARD_CS_PIN    4
//#define SDCARD_MOSI_PIN  11
//#define SDCARD_SCK_PIN   13
SDClass sd1;
SDClass sd2;

void setup() {
  while(!Serial && millis() < 4000);
  Serial.begin(9600);
  Serial.println("\nWave File Player test");

  // Audio connections require memory to work.  For more
  // detailed information, see the MemoryAndCpuUsage example
  AudioMemory(8);

  // Comment these out if not using the audio adaptor board.
  // This may wait forever if the SDA & SCL pins lack
  // pullup resistors
  sgtl5000_1.enable();
  sgtl5000_1.volume(0.5);

  if (!(sd1.begin(SDCARD_CS_PIN))) {
    // stop here, but print a message repetitively
    while (1) {
      Serial.println("Unable to access the SD card");
      delay(500);
    }
  }
  sd2.begin(SDCARD2_CS_PIN);
}

void playFile(const char *filename, FS* pfs)
{
  Serial.print("Playing file: ");
  Serial.print(filename);
  if (pfs == &sd1) Serial.print(" on sd1");
  else if (pfs == &sd2) Serial.print(" on sd2");

  // Start playing the file.  This sketch continues to
  // run while the file plays.
  if (playWav1.play(filename, pfs)) Serial.println();
  else Serial.println("** failed **");

  // A brief delay for the library read WAV info
  delay(25);

  // Simply wait for the file to finish playing.
  while (playWav1.isPlaying()) {
    // uncomment these lines if you audio shield
    // has the optional volume pot soldered
    //float vol = analogRead(15);
    //vol = vol / 1024;
    // sgtl5000_1.volume(vol);
  }
  if (Serial.available()) {
    Serial.println("Paused");
    while(Serial.read() != -1);
    while(Serial.read() == -1);
    while(Serial.read() != -1);
  }
}


void loop() {
  playFile("/2001/calculations.WAV", &sd1);  // filenames are always uppercase 8.3 format
  delay(500);
  playFile("/2001/dangerous_to_remain.WAV", &sd1);
  delay(500);
  playFile("/2001/sorry_dave.WAV", &sd2);
  delay(500);
  playFile("/2001/functional.WAV", &sd1);
  delay(1500);
  playFile("/odd1.WAV", &sd2);
  delay(1500);
}
And this one is handling the two different SD cards

Code:
Wave File Player test
Playing file: /2001/calculations.WAV on sd1
Playing file: /2001/dangerous_to_remain.WAV on sd1
Playing file: /2001/sorry_dave.WAV on sd2
Playing file: /2001/functional.WAV on sd1
Playing file: /odd1.WAV on sd2
Playing file: /2001/calculations.WAV on sd1
Playing file: /2001/dangerous_to_remain.WAV on sd1
Playing file: /2001/sorry_dave.WAV on sd2
Playing file: /2001/functional.WAV on sd1
Paused
 
Excellent and most inspirational: thanks @KurtE. I've made an equivalent change and similar example for my buffered player (latest commit here) - button pins and filenames will need changing to suit your cards!
C++:
// Play back sounds from multiple SD cards
// Tested on Teensy 4.1, changes may be needed for 3.x

#include <SD.h>
#include <Bounce.h>
#include <Audio.h>

#define SDCARD_CS_PIN 10 // audio adaptor

// #define sd1 SD // can do this for one card
SDClass sd1;
SDClass sd2;
File frec;

// GUItool: begin automatically generated code
AudioInputI2S            i2s2;           //xy=105,63
AudioAnalyzePeak         peak1;          //xy=278,108
AudioRecordQueue         queue1;         //xy=281,63
AudioPlayWAVstereo           playRaw1;       //xy=302,157
AudioPlayWAVstereo           playRaw2;       //xy=302,157
AudioOutputI2S           i2s1;           //xy=470,120
AudioConnection          patchCord1(i2s2, 0, queue1, 0);
AudioConnection          patchCord2(i2s2, 0, peak1, 0);
AudioConnection          patchCord3(playRaw1, 0, i2s1, 0);
AudioConnection          patchCord4(playRaw2, 0, i2s1, 1);
AudioControlSGTL5000     sgtl5000_1;     //xy=265,212
// GUItool: end automatically generated code

// Bounce objects to easily and reliably read the buttons
Bounce buttonPlay1 =   Bounce(30, 8);
Bounce buttonPlay2 =   Bounce(31, 8);

//==================================================================
void setup() {

  // Configure the pushbutton pins
  pinMode(30, INPUT_PULLUP);
  pinMode(31, INPUT_PULLUP);

  // Audio connections require memory
  AudioMemory(10);

  while (!Serial)
    ;
  Serial.println("Started!");

  // Enable the audio shield, select input, and enable output
  // sgtl5000_1.setAddress(HIGH); // not needed - see post #15 below
  sgtl5000_1.enable();
  sgtl5000_1.volume(0.07);

  playRaw1.createBuffer(2048,AudioBuffer::inHeap);
  playRaw2.createBuffer(2048,AudioBuffer::inHeap);

  // initialize the internal Teensy 4.1 SD card
  if (!sd1.begin(BUILTIN_SDCARD)) {
    Serial.println("error sd1.begin");
  }

  // initialize the Audio Adapter SD card
  if (!sd2.begin(SDCARD_CS_PIN)) {
    Serial.println("error sd2.begin");
  }
}


//==================================================================
void loop() {
  buttonPlay1.update();
  buttonPlay2.update();

  if (buttonPlay1.fallingEdge()) {
    Serial.println("Play 1");
    // playRaw1.play("sine220.wav"); // if sd1 is #defined as SD, this works
    playRaw1.play("sine220.wav", sd1);
  }
  if (buttonPlay2.fallingEdge()) {
    Serial.println("Play 2");
    playRaw2.play("sine330.wav", sd2);
  }
}
This quick test does allow simultaneous playback from both cards, but I didn't implement recording because I've only got two buttons ... and I'm lazy.
 
Last edited:
@mattkuebrich - I figured out what was causing the SD card on the audio shield to fail playing back a recorded RAW file. Based on @KurtE's new sketch in post #12. Initializing the audio shield SD card:
Code:
  // initialize the Audio Adapter SD card
  if (!sd2.sdfs.begin(SdSpiConfig(SDCARD_CS_PIN, DEDICATED_SPI, SD_SCK_MHZ(16)))) {
    Serial.println("error sd2.begin");
  }
Would allow recording but no play back.

Initializing this way:
Code:
  // initialize the Audio Adapter SD card
  if (!sd2.begin(SDCARD2_CS_PIN)) {
    Serial.println("error sd2.begin");
  }
Allows recording and play back to work. Still curious as to why this is:) Anyway here is the modified working sketch:
Code:
// Record sound as raw data to a SD card, and play it back.

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

#define SDCARD_CS_PIN BUILTIN_SDCARD
#define SDCARD2_CS_PIN 10

SDClass sd1;
SDClass sd2;
File frec;

// GUItool: begin automatically generated code
AudioInputI2S            i2s2;           //xy=105,63
AudioAnalyzePeak         peak1;          //xy=278,108
AudioRecordQueue         queue1;         //xy=281,63
AudioPlaySdRaw           playRaw1;       //xy=302,157
AudioOutputI2S           i2s1;           //xy=470,120
AudioConnection          patchCord1(i2s2, 0, queue1, 0);
AudioConnection          patchCord2(i2s2, 0, peak1, 0);
AudioConnection          patchCord3(playRaw1, 0, i2s1, 0);
AudioConnection          patchCord4(playRaw1, 0, i2s1, 1);
AudioControlSGTL5000     sgtl5000_1;     //xy=265,212
// GUItool: end automatically generated code

// Bounce objects to easily and reliably read the buttons
Bounce buttonRecord = Bounce(34, 8);
Bounce buttonStop =   Bounce(33, 8);  // 8 = 8 ms debounce time
Bounce buttonPlay =   Bounce(35, 8);

// which input on the audio shield will be used?
//const int myInput = AUDIO_INPUT_LINEIN;
const int myInput = AUDIO_INPUT_MIC;

// Remember which mode we're doing
int mode = 0;  // 0=stopped, 1=recording, 2=playing

void setup() {
//while(!Serial) {;}
//Serial.println("Initializing SD devices\n");

  // Configure the pushbutton pins
  pinMode(33, INPUT_PULLUP);
  pinMode(34, INPUT_PULLUP);
  pinMode(35, INPUT_PULLUP);

  // Audio connections require memory, and the record queue
  // uses this memory to buffer incoming audio.
  AudioMemory(60);

  // Enable the audio shield, select input, and enable output
  sgtl5000_1.enable();
  sgtl5000_1.inputSelect(myInput);
  sgtl5000_1.volume(0.8);

  // initialize the internal Teensy 4.1 SD card
  if (!sd1.begin(SDCARD_CS_PIN)) {
    Serial.println("error sd1.begin");
  }

  // initialize the Audio Adapter SD card
  if (!sd2.begin(SDCARD2_CS_PIN)) {
    Serial.println("error sd2.begin");
  }
  //sd1.chvol(); 

}


void loop() {
  // First, read the buttons
  buttonRecord.update();
  buttonStop.update();
  buttonPlay.update();

  // Respond to button presses
  if (buttonRecord.fallingEdge()) {
    Serial.println("Record Button Press");
    if (mode == 2) stopPlaying();
    if (mode == 0) startRecording();
  }
  if (buttonStop.fallingEdge()) {
    Serial.println("Stop Button Press");
    if (mode == 1) stopRecording();
    if (mode == 2) stopPlaying();
  }
  if (buttonPlay.fallingEdge()) {
    Serial.println("Play Button Press");
    if (mode == 1) stopRecording();
    if (mode == 0) startPlaying();
  }

  // If we're playing or recording, carry on...
  if (mode == 1) {
    continueRecording();
  }
  if (mode == 2) {
    continuePlaying();
  }
}

void startRecording() {
  Serial.println("startRecording");
// FILE_WRITE_BEGIN actually starts at beginning of file (Position 0).
//  frec = sd1.open("RECORD.RAW", FILE_WRITE_BEGIN); // SDIO
  frec = sd2.open("RECORD.RAW", FILE_WRITE_BEGIN);   // SDSPI

  if (frec) {
    //Serial.println("frec!");
    queue1.begin();
    mode = 1;
  }
}

void continueRecording() {
  if (queue1.available() >= 2) {
    byte buffer[512];
    // Fetch 2 blocks from the audio library and copy
    // into a 512 byte buffer.  The Arduino SD library
    // is most efficient when full 512 byte sector size
    // writes are used.
    memcpy(buffer, queue1.readBuffer(), 256);
    queue1.freeBuffer();
    memcpy(buffer + 256, queue1.readBuffer(), 256);
    queue1.freeBuffer();
    // write all 512 bytes to the SD card
    frec.write(buffer, 512);
  }
}

void stopRecording() {
  Serial.println("stopRecording");
  queue1.end();
  if (mode == 1) {
    while (queue1.available() > 0) {
      frec.write((byte*)queue1.readBuffer(), 256);
      queue1.freeBuffer();
    }
    frec.close();
  }
  mode = 0;
}

void startPlaying() {
  Serial.println("startPlaying");
  playRaw1.play("RECORD.RAW", &sd2);
  mode = 2;
}

void continuePlaying() {
  if (!playRaw1.isPlaying()) {
    playRaw1.stop();
    mode = 0;
  }
}

void stopPlaying() {
  Serial.println("stopPlaying");
  if (mode == 2) playRaw1.stop();
  mode = 0;
}

Both Builtin and external SD cards record and play back raw files. Also I have fixed and reattached the play_sd_wav and play_sd raw zipped files. The parameters for the play functions were reversed. Somewhere along the line they have been reversed from my originals...
 

Attachments

  • play_sd_wav_raw.zip
    9.1 KB · Views: 27
Sorry folks, spotted a problem in post #13 - I've modified my audio adaptor's I²C address, so I need sgtl5000_1.setAddress(HIGH);, but I'd guess most people don't - just delete that line! Fixed in the repo.
 
Thanks @all - it's working! @wwatson's last tweak to the initialization of the Audio Adapter SD reader fixed it for me. Glad to see @h4yn0nnym0u5e's updates to the buffered player as well.
 
Back
Top