Recording Stereo Audio to SD Card

algomusic

New member
I'm testing the ability of the Teensy (3.2 with Audio Board and 3.5) to record two channels of audio to the SD card and get write interruptions in the recorded stream. Mono works fine. Any hints on how to do this are welcome.

I'm writing tow files concurrently to the SD card, each using its own queue object from the teensy Audio Library.

The occasional write timing increases can be seen in this sample print output.

SD write L, us=1260
SD write R, us=1249
SD write L, us=1268
SD write R, us=1263
SD write R, us=20907
SD write L, us=91518
SD write R, us=89989
SD write L, us=91151
SD write R, us=89601

Below is my test code FYI.

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

// GUItool: begin automatically generated code
AudioPlaySdRaw           playRaw1;     //xy=264,355
AudioSynthWaveformSine   sine1;          //xy=273,465
AudioSynthWaveformSine   sine2; 
//AudioSynthNoiseWhite     noise1;         //xy=282,522
AudioRecordQueue         queue1;         //xy=425,489
AudioRecordQueue         queue2; 
AudioOutputI2S           i2s1;           //xy=451,351
AudioConnection          patchCord1(playRaw1, 0, i2s1, 0);
AudioConnection          patchCord2(playRaw1, 0, i2s1, 1);
AudioConnection          patchCord3(sine1, queue1);
AudioConnection          patchCord4(sine2, queue2);
//AudioConnection          patchCord4(noise1, queue1);
// GUItool: end automatically generated code

AudioControlSGTL5000     sgtl5000_1;     //xy=265,212

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

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

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

unsigned long Timer;

// The file where data is recorded
File frecL;
File frecR;

void setup() {

  // 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.5);

   // set sine osc 
  sine1.amplitude(1.0);
  sine1.frequency(440);
  sine2.amplitude(1.0);
  sine2.frequency(261);
  // set up ch 2 noise
  //noise1.amplitude(1.0);

 // Initialize the SD card
  SPI.setMOSI(SDCARD_MOSI_PIN);
  SPI.setSCK(SDCARD_SCK_PIN);
  if (!(SD.begin(SDCARD_CS_PIN))) {
    // stop here if no SD card, but print a message
    while (1) {
      Serial.println("Unable to access the SD card");
      delay(500);
    }
  }
  
  Timer = millis();
  startRecording();
}


void loop() {
  if((millis()-Timer)>5000) {
    stopRecording();
    startPlaying();
    while(playRaw1.isPlaying()) {
    }
    stopPlaying();
    Timer = millis();
    startRecording();
  }
  else {
    continueRecording();
  }
 
}


void startRecording() {
  Serial.println("startRecording");
  if (SD.exists("RECORD_L.RAW")) {
    SD.remove("RECORD_L.RAW");
  }
  if (SD.exists("RECORD_R.RAW")) {
    SD.remove("RECORD_R.RAW");
  }
  frecL = SD.open("RECORD_L.RAW", FILE_WRITE);
  frecR = SD.open("RECORD_R.RAW", FILE_WRITE);
  if (frecL) {
    queue1.begin();
    mode = 1;
  }
  if (frecR) {
    queue2.begin();
    mode = 1;
  }
}

void continueRecording() {
   
  if (queue1.available() >= 2) {
    byte buffer[512];

    memcpy(buffer, queue1.readBuffer(), 256);
    queue1.freeBuffer();
    memcpy(buffer+256, queue1.readBuffer(), 256);
    queue1.freeBuffer();
    // write all 512 bytes to the SD card
    elapsedMicros usec = 0;
    frecL.write(buffer, 512);

    Serial.print("SD write L, us=");
    Serial.println(usec);
  }
  if (queue2.available() >= 2) {
    byte buffer[512];
    memcpy(buffer, queue2.readBuffer(), 256);
    queue2.freeBuffer();
    memcpy(buffer+256, queue2.readBuffer(), 256);
    elapsedMicros usec = 0;
    queue2.freeBuffer();
    // write all 512 bytes to the SD card
    //elapsedMicros usec = 0;
    frecR.write(buffer, 512);
    Serial.print("SD write R, us=");
    Serial.println(usec);
  } 
}

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


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

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

void stopPlaying() {
  Serial.println("stopPlaying");
  if (mode == 2) playRaw1.stop();
  //mode = 0;
}
 
Last edited:
I solved my own problem raised here, by interleaving the audio data from the two queues and doing a single file write to the SD card. This was a little more complicated than I'd hoped, and it might be nice to have a stereo-queue object that does the interleaving for us :)

Here's the source code for a working solution for those that want it (there is some oddity in buffer sizes, but it seems to work).

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

// GUItool: begin automatically generated code
AudioPlaySdRaw           playRaw1;     //xy=264,355
AudioSynthWaveformSine   sine1;          //xy=273,465
AudioSynthKarplusStrong  string1;        //xy=144,430
AudioRecordQueue         queue1;         //xy=425,489
AudioRecordQueue         queue2; 
AudioOutputI2S           i2s1;           //xy=451,351
AudioConnection          patchCord3(string1, queue1);
AudioConnection          patchCord4(sine1, queue2);
// GUItool: end automatically generated code

AudioControlSGTL5000     sgtl5000_1;     //xy=265,212

const int myInput = AUDIO_INPUT_MIC;

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

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

unsigned long Timer;

// The file where data is recorded
File frec;

void setup() {
  // record queue uses this memory to buffer incoming audio.
  AudioMemory(120); // 60

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

   // set sine osc 
  sine1.amplitude(1.0);
  sine1.frequency(261);

 // Initialize the SD card
  SPI.setMOSI(SDCARD_MOSI_PIN);
  SPI.setSCK(SDCARD_SCK_PIN);
  if (!(SD.begin(SDCARD_CS_PIN))) {
    // stop here if no SD card, but print a message
    while (1) {
      Serial.println("Unable to access the SD card");
      delay(500);
    }
  }
  Timer = millis();
  startRecording();
}


void loop() {
  if((millis()-Timer)>5000 && mode == 1) {
    stopRecording();
  }
  else {
    if (mode == 1) continueRecording();
  }
}

void startRecording() {
  Serial.println("StartRecording");
  if (SD.exists("RECORD.RAW")) {
    SD.remove("RECORD.RAW");
  }
  frec = SD.open("RECORD.RAW", FILE_WRITE);
  if (frec) {
    Serial.println("File Open");
    queue1.begin();
    queue2.begin();
    mode = 1;
  }
  string1.noteOn(440, 1.0);
}

void continueRecording() {
  if (queue1.available() >= 2 && queue2.available() >= 2) {
    byte buffer[512];
    byte bufferL[256];
    byte bufferR[256];
    memcpy(bufferL, queue1.readBuffer(), 256);
    memcpy(bufferR, queue2.readBuffer(), 256);
    queue1.freeBuffer();
    queue2.freeBuffer();
    for (int i=0; i<512; i+=4) {
      buffer[i] = bufferL[i];
      buffer[i+1] = bufferL[i+1];
      buffer[i+2] = bufferR[i];
      buffer[i+3] = bufferR[i+1];
    }
    frec.write(buffer, 256);
  }
}

void stopRecording() {
  Serial.println("StopRecording");
  queue1.end();
  queue2.end();
  // flush buffer
  while (queue1.available() > 0 && queue2.available() > 0) {
    queue1.readBuffer();
    queue1.freeBuffer();
    queue2.readBuffer();
    queue2.freeBuffer();
  }
  frec.close(); // close file
  mode = 0;
}
 
I solved my own problem raised here, by interleaving the audio data from the two queues and doing a single file write to the SD card. This was a little more complicated than I'd hoped, and it might be nice to have a stereo-queue object that does the interleaving for us :)

Here's the source code for a working solution for those that want it (there is some oddity in buffer sizes, but it seems to work).


Hey Algomusic,

I did some recordings today with two mics attached to the line-inputs of the audio shield. When I imported the raw-file to Audacity (to playback and convert) it turned out to be a mono recording (I could import as a stereo but that resulted in two identical tracks). For a project I am doing I am looking for stereo sound, so your code looks really interesting; I just have some initial questions:

I see you make two AudioRecorderQueue objects, but where do you assign which line inputs goes to which Queue?
I assume it outputs two files one with the left channel and one with the right channel that can be joined together in other software?

Kind regards,

inimidi
 
Code:
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>

// GUItool: begin automatically generated code

AudioInputI2S            i2s2;           //xy=105,63

AudioRecordQueue         queue1;         //xy=425,489
AudioAnalyzePeak         peak1;          //xy=278,108
AudioConnection          patchCord1(i2s2, 0, queue1, 0);
AudioConnection          patchCord2(i2s2, 0, peak1, 0);

AudioRecordQueue         queue2;         //xy=425,489
AudioAnalyzePeak         peak2;          //xy=278,108
AudioConnection          patchCord5(i2s2, 1, queue2, 0);
AudioConnection          patchCord6(i2s2, 1, peak2, 0);

AudioPlaySdRaw           playRaw1;     //xy=264,355
AudioSynthWaveformSine   sine1;          //xy=273,465
AudioSynthKarplusStrong  string1;        //xy=144,430
AudioOutputI2S           i2s1;           //xy=451,351
AudioConnection          patchCord3(string1, queue1);
AudioConnection          patchCord4(sine1, queue2);
// GUItool: end automatically generated code

AudioControlSGTL5000     sgtl5000_1;     //xy=265,212


const int myInput = AUDIO_INPUT_LINEIN;   //THIS IS CHANGED---------------

// Use these with the Teensy Audio Shield
#define SDCARD_CS_PIN    10               //THIS IS CHANGED---------------
#define SDCARD_MOSI_PIN  7                //THIS IS CHANGED---------------
#define SDCARD_SCK_PIN   14               //THIS IS CHANGED---------------

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

unsigned long Timer;

// The file where data is recorded
File frec;

void setup() {
  //Serial.println("ghello ghello");

  // record queue uses this memory to buffer incoming audio.
  AudioMemory(120); // 60

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

  // set sine osc
  sine1.amplitude(1.0);
  sine1.frequency(261);

  // Initialize the SD card
  SPI.setMOSI(SDCARD_MOSI_PIN);
  SPI.setSCK(SDCARD_SCK_PIN);
  if (!(SD.begin(SDCARD_CS_PIN))) {
    // stop here if no SD card, but print a message
    while (1) {
      Serial.println("Unable to access the SD card");
      delay(500);
    }
  }
  // Serial.println("ghello");
  //Timer = millis();
  Serial.println("go");
  Serial.println(millis());

  // Serial.println(millis());
}


void loop() {
  
  if (mode == 0) {
    startRecording();
  }

  if (mode == 1) {
    continueRecording();
    if (millis() > 5000) {
      Serial.println(millis());
      stopRecording();
      mode = 2;
    }
  }

  /*if((millis())>15000 && mode == 1) {
    stopRecording();
    //Serial.println(millis());
  }
  else {
    if (mode == 1) continueRecording();
  }*/
}

void startRecording() {
  Serial.println("StartRecording");
  if (SD.exists("RECORDS.RAW")) {
    SD.remove("RECORDS.RAW");
  }
  frec = SD.open("RECORDS.RAW", FILE_WRITE);
  if (frec) {
    Serial.println("File Open");
    queue1.begin();
    queue2.begin();
    mode = 1;
  }
  string1.noteOn(440, 1.0);
}

void continueRecording() {
  if (queue1.available() >= 2 && queue2.available() >= 2) {
    byte buffer[512];
    byte bufferL[256];
    byte bufferR[256];
    memcpy(bufferL, queue1.readBuffer(), 256);
    memcpy(bufferR, queue2.readBuffer(), 256);
    queue1.freeBuffer();
    queue2.freeBuffer();
    for (int i = 0; i < 512; i += 4) {
      buffer[i] = bufferL[i];
      buffer[i + 1] = bufferL[i + 1];
      buffer[i + 2] = bufferR[i];
      buffer[i + 3] = bufferR[i + 1];
    }
    frec.write(buffer, 256);
  }
}

void stopRecording() {
  Serial.println("StopRecording");
  queue1.end();
  queue2.end();
  // flush buffer
  while (queue1.available() > 0 && queue2.available() > 0) {
    queue1.readBuffer();
    queue1.freeBuffer();
    queue2.readBuffer();
    queue2.freeBuffer();
  }
  frec.close(); // close file
  mode = 0;
}

I modified the code in an attempt to get it working on a teensy3.2 + audio adapter

however it does something weird . . . It waits till the 10s recording time has emitted and then apparently quickly opens and closes the file writing nothing in it.
Did youencounter similar issues when writing your code?
 
I'm glad you found the code useful. In answer to your questions.

1. The code records a single file which Audacity can read as a stereo file.

2. The loop function does start and stop short recordings, this was my testing process. Modify the time to do recordings of different length and remove the loop callback to only record once.

It would be more convenient to add connected buttons to start and stop the recording if you prefer.
 
Hey algomusic,


Thanks for the answers :D
Your stereo-recording code really helps set the audio board set itself apart from competitor audio-recording boards. That's why I am determined to get it working for the teensy3.2 as well :)

1. The code records a single file which Audacity can read as a stereo file.
Awesome~ I have been studying your code andit looks like you create two separate files that are presumably merged at the end; where in the code are they merged (so I understand the code better)?


2. The loop function does start and stop short recordings, this was my testing process. Modify the time to do recordings of different length and remove the loop callback to only record once.
I tried different times ranging from 5 to 100 seconds (5000-100000 millis) yet none of them work. I always get an empty file. In the serial monitor I observe it opening and closing the file instantly.

kind regards~
 
Working code for teensy3.2 + audio shield

I got it working~

Here is the code:

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

// GUItool: begin automatically generated code
AudioInputI2S            i2s2;           //xy=484.6000061035156,295
AudioAnalyzePeak         peak2;          //xy=669.1999969482422,350.1999969482422
AudioRecordQueue         queue1;         //xy=672.5999755859375,182
AudioAnalyzePeak         peak1;          //xy=675.5999755859375,227
AudioPlaySdRaw           playRaw1;       //xy=681.6000061035156,389
AudioRecordQueue         queue2;         //xy=698.1999969482422,307.1999969482422
AudioOutputI2S           i2s1;           //xy=849.6000061035156,352
AudioConnection          patchCord1(i2s2, 0, queue1, 0);
AudioConnection          patchCord2(i2s2, 0, peak1, 0);
AudioConnection          patchCord3(i2s2, 1, queue2, 0);
AudioConnection          patchCord4(i2s2, 1, peak2, 0);
AudioConnection          patchCord5(playRaw1, 0, i2s1, 0);
AudioConnection          patchCord6(playRaw1, 0, i2s1, 1);
// GUItool: end automatically generated code

AudioControlSGTL5000     sgtl5000_1;     //xy=265,212


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


// Use these with the Teensy Audio Shield
#define SDCARD_CS_PIN    10
#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 // 254?
//#define SDCARD_MOSI_PIN  11  // not actually used
//#define SDCARD_SCK_PIN   13  // not actually used

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



// The file where data is recorded
File frec;

void setup() {
  // record queue uses this memory to buffer incoming audio.
  AudioMemory(120); // 60

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



  // Initialize the SD card
  SPI.setMOSI(SDCARD_MOSI_PIN);
  SPI.setSCK(SDCARD_SCK_PIN);
  if (!(SD.begin(SDCARD_CS_PIN))) {
    // stop here if no SD card, but print a message
    while (1) {
      Serial.println("Unable to access the SD card");
      delay(500);
    }
  }

  startRecording();
}


void loop() {
  if (millis() > 10000 && mode == 1) {
    stopRecording();
  }
  else {
    if (mode == 1) continueRecording();
  }
}

void startRecording() {
  Serial.println("StartRecording");
  if (SD.exists("RECORD3.RAW")) {
    SD.remove("RECORD3.RAW");
  }
  frec = SD.open("RECORD3.RAW", FILE_WRITE);
  if (frec) {
    Serial.println("File Open");
    queue1.begin();
    queue2.begin();
    mode = 1;
  }

}

void continueRecording() {
  if (queue1.available() >= 2 && queue2.available() >= 2) {
    byte buffer[512];
    byte bufferL[256];
    byte bufferR[256];
    memcpy(bufferL, queue1.readBuffer(), 256);
    memcpy(bufferR, queue2.readBuffer(), 256);
    queue1.freeBuffer();
    queue2.freeBuffer();
    for (int i = 0; i < 512; i += 4) {
      buffer[i] = bufferL[i];
      buffer[i + 1] = bufferL[i + 1];
      buffer[i + 2] = bufferR[i];
      buffer[i + 3] = bufferR[i + 1];
    }
    elapsedMicros usec = 0;
    frec.write(buffer, 256);  //256 or 512 (dudes code)
    Serial.print("SD write, us=");
    Serial.println(usec);
  }
}

void stopRecording() {
  Serial.println("StopRecording");
  queue1.end();
  queue2.end();
  // flush buffer
  while (queue1.available() > 0 && queue2.available() > 0) {
    queue1.readBuffer();
    queue1.freeBuffer();
    queue2.readBuffer();
    queue2.freeBuffer();
  }
  frec.close(); // close file
  mode = 4;
}

If you want you can add

Code:
sgtl5000_1.lineInLevel(2,2);

in the setup() to get better quality audio.

On the line in I have connected two electret microphone break out boards by adafruit:
https://www.adafruit.com/product/1713

They come with a nifty tutorial to connect:
https://learn.adafruit.com/adafruit-agc-electret-microphone-amplifier-max9814/overview
Don't forget that you need to add some capacitors.
 
I got it working~

I am able to make a stereo recording, but the audio rate is 2X when I convert from raw to wav at 44100. If I convert at 22050, the rate is good, but I'm missing all the content above 11K. Is anyone else encountering this?

My conversion code:

Code:
sox -t raw -b 16 -e signed-integer -r 44100 -c 2 input.raw output.wav

I do not have this problem when making a mono recording.
 
if anyone else encounters sample rate issues, see my code below. i made some changes to inimidi's code based on panphonic's post here: https://forum.pjrc.com/threads/33852-Stereo-Queue-Interleave

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

// GUItool: begin automatically generated code
AudioInputI2S            i2s1;           //xy=168,145
AudioRecordQueue         queue1;         //xy=360,62
AudioRecordQueue         queue2;         //xy=389,145
AudioConnection          patchCord1(i2s1, 0, queue1, 0);
AudioConnection          patchCord2(i2s1, 1, queue2, 0);
// GUItool: end automatically generated code

AudioControlSGTL5000     sgtl5000_1;     //xy=265,212


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


// Use these with the Teensy Audio Shield
#define SDCARD_CS_PIN    10
#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 // 254?
//#define SDCARD_MOSI_PIN  11  // not actually used
//#define SDCARD_SCK_PIN   13  // not actually used

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



// The file where data is recorded
File frec;

void setup() {
  // record queue uses this memory to buffer incoming audio.
  AudioMemory(120); // 60

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



  // Initialize the SD card
  SPI.setMOSI(SDCARD_MOSI_PIN);
  SPI.setSCK(SDCARD_SCK_PIN);
  if (!(SD.begin(SDCARD_CS_PIN))) {
    // stop here if no SD card, but print a message
    while (1) {
      Serial.println("Unable to access the SD card");
      delay(500);
    }
  }

  startRecording();
}


void loop() {
  if (millis() > 10000 && mode == 1) {
    stopRecording();
  }
  else {
    if (mode == 1) continueRecording();
  }
}

void startRecording() {
  Serial.println("StartRecording");
  if (SD.exists("RECORD.RAW")) {
    SD.remove("RECORD.RAW");
  }
  frec = SD.open("RECORD.RAW", FILE_WRITE);
  if (frec) {
    Serial.println("File Open");
    queue1.begin();
    queue2.begin();
    mode = 1;
  }

}

// write all 512 bytes to the SD card   
void continueRecording() {
  if (queue1.available() >= 2 && queue2.available() >= 2) {
    byte buffer[512];
    byte bufferL[256];
    byte bufferR[256];
    memcpy(bufferL, queue1.readBuffer(), 256);
    memcpy(bufferR, queue2.readBuffer(), 256);
    queue1.freeBuffer();
    queue2.freeBuffer();
    int b = 0;
    for (int i = 0; i < 512; i += 4) {
      buffer[i] = bufferL[b];
      buffer[i + 1] = bufferL[b + 1];
      buffer[i + 2] = bufferR[b];
      buffer[i + 3] = bufferR[b + 1];
      b = b+2;
    }
    elapsedMicros usec = 0;
    frec.write(buffer, 512);  //256 or 512 (dudes code)
    Serial.print("SD write, us=");
    Serial.println(usec);
  }
}

void stopRecording() {
  Serial.println("StopRecording");
  queue1.end();
  queue2.end();
  // flush buffer
  while (queue1.available() > 0 && queue2.available() > 0) {
    queue1.readBuffer();
    queue1.freeBuffer();
    queue2.readBuffer();
    queue2.freeBuffer();
  }
  frec.close(); // close file
  mode = 4;
}
 
Any idea how to record mono channel using ADC ? Not from using Audio shield. Also anyone have experience recording with .wav header extension ?
 
Easy, just connect the adc object in the design tool, rather than the i2s one.

If using the examples, also delete any sgtl5000 config stuff.

Paul i able achieve that. But it store as .raw file. Any idea how to convert it into .wav and store it on the SD card?
 
the .raw file is basically a .wav file without file header. You will find both file formats well documented on the www when you ask the good old Lady Google. Then you'll have to write some code which will read and analyze your .raw file, generate the corresponding .wav header, and write out the latter again to the SD card.

Alternatively, you might peek the Adacity open source code which is either on GitHub or on Sourceforge, and find your inspiration there.
 
You can interleave much simpler

Code:
inline void mxLR(byte *dst, const int16_t *srcL, const int16_t *srcR)
  {
    byte cnt = 128;
    int16_t *d = (int16_t *)dst;
    const int16_t *l = srcL;
    const int16_t *r = srcR;

    while (cnt--)
    {
      *(d++) = *l++;
      *(d++) = *r++;
    }
  }

Code:
                byte buffer[512]; // data to write
                mxLR(buffer, queue1.readBuffer(), queue2.readBuffer()); // interleave 
                queue1.freeBuffer(); queue2.freeBuffer();  // free buffer
                frec.write(buffer, 512); // write block
 
Audio skips when stereo recording

if anyone else encounters sample rate issues, see my code below. i made some changes to inimidi's code based on panphonic's post here: https://forum.pjrc.com/threads/33852-Stereo-Queue-Interleave

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

// GUItool: begin automatically generated code
AudioInputI2S            i2s1;           //xy=168,145
AudioRecordQueue         queue1;         //xy=360,62
AudioRecordQueue         queue2;         //xy=389,145
AudioConnection          patchCord1(i2s1, 0, queue1, 0);
AudioConnection          patchCord2(i2s1, 1, queue2, 0);
// GUItool: end automatically generated code

AudioControlSGTL5000     sgtl5000_1;     //xy=265,212


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


// Use these with the Teensy Audio Shield
#define SDCARD_CS_PIN    10
#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 // 254?
//#define SDCARD_MOSI_PIN  11  // not actually used
//#define SDCARD_SCK_PIN   13  // not actually used

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



// The file where data is recorded
File frec;

void setup() {
  // record queue uses this memory to buffer incoming audio.
  AudioMemory(120); // 60

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



  // Initialize the SD card
  SPI.setMOSI(SDCARD_MOSI_PIN);
  SPI.setSCK(SDCARD_SCK_PIN);
  if (!(SD.begin(SDCARD_CS_PIN))) {
    // stop here if no SD card, but print a message
    while (1) {
      Serial.println("Unable to access the SD card");
      delay(500);
    }
  }

  startRecording();
}


void loop() {
  if (millis() > 10000 && mode == 1) {
    stopRecording();
  }
  else {
    if (mode == 1) continueRecording();
  }
}

void startRecording() {
  Serial.println("StartRecording");
  if (SD.exists("RECORD.RAW")) {
    SD.remove("RECORD.RAW");
  }
  frec = SD.open("RECORD.RAW", FILE_WRITE);
  if (frec) {
    Serial.println("File Open");
    queue1.begin();
    queue2.begin();
    mode = 1;
  }

}

// write all 512 bytes to the SD card   
void continueRecording() {
  if (queue1.available() >= 2 && queue2.available() >= 2) {
    byte buffer[512];
    byte bufferL[256];
    byte bufferR[256];
    memcpy(bufferL, queue1.readBuffer(), 256);
    memcpy(bufferR, queue2.readBuffer(), 256);
    queue1.freeBuffer();
    queue2.freeBuffer();
    int b = 0;
    for (int i = 0; i < 512; i += 4) {
      buffer[i] = bufferL[b];
      buffer[i + 1] = bufferL[b + 1];
      buffer[i + 2] = bufferR[b];
      buffer[i + 3] = bufferR[b + 1];
      b = b+2;
    }
    elapsedMicros usec = 0;
    frec.write(buffer, 512);  //256 or 512 (dudes code)
    Serial.print("SD write, us=");
    Serial.println(usec);
  }
}

void stopRecording() {
  Serial.println("StopRecording");
  queue1.end();
  queue2.end();
  // flush buffer
  while (queue1.available() > 0 && queue2.available() > 0) {
    queue1.readBuffer();
    queue1.freeBuffer();
    queue2.readBuffer();
    queue2.freeBuffer();
  }
  frec.close(); // close file
  mode = 4;
}

Does anyone else have that the recordings "skip" when recording in stereo? In a 10s recording there is consistently a chunk missing of around 800ms. Which is clearly noticeable with continuous speech or music. I tried it with two different teensy+audio boards and the problem seems to persist.

When looking at the serial monitor when recording the "usec" seems to have a moment in which it significantly increases for a bit. Any one an idea on how to fix this?

Things I tried that did not seem to work for me:
- Assigning more AudioMemory (from 120 -> 180)
- Doubling buffersizes
 
if anyone else encounters sample rate issues, see my code below. i made some changes to inimidi's code based on panphonic's post here: https://forum.pjrc.com/threads/33852-Stereo-Queue-Interleave

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

// GUItool: begin automatically generated code
AudioInputI2S            i2s1;           //xy=168,145
AudioRecordQueue         queue1;         //xy=360,62
AudioRecordQueue         queue2;         //xy=389,145
AudioConnection          patchCord1(i2s1, 0, queue1, 0);
AudioConnection          patchCord2(i2s1, 1, queue2, 0);
// GUItool: end automatically generated code

AudioControlSGTL5000     sgtl5000_1;     //xy=265,212


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


// Use these with the Teensy Audio Shield
#define SDCARD_CS_PIN    10
#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 // 254?
//#define SDCARD_MOSI_PIN  11  // not actually used
//#define SDCARD_SCK_PIN   13  // not actually used

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



// The file where data is recorded
File frec;

void setup() {
  // record queue uses this memory to buffer incoming audio.
  AudioMemory(120); // 60

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



  // Initialize the SD card
  SPI.setMOSI(SDCARD_MOSI_PIN);
  SPI.setSCK(SDCARD_SCK_PIN);
  if (!(SD.begin(SDCARD_CS_PIN))) {
    // stop here if no SD card, but print a message
    while (1) {
      Serial.println("Unable to access the SD card");
      delay(500);
    }
  }

  startRecording();
}


void loop() {
  if (millis() > 10000 && mode == 1) {
    stopRecording();
  }
  else {
    if (mode == 1) continueRecording();
  }
}

void startRecording() {
  Serial.println("StartRecording");
  if (SD.exists("RECORD.RAW")) {
    SD.remove("RECORD.RAW");
  }
  frec = SD.open("RECORD.RAW", FILE_WRITE);
  if (frec) {
    Serial.println("File Open");
    queue1.begin();
    queue2.begin();
    mode = 1;
  }

}

// write all 512 bytes to the SD card   
void continueRecording() {
  if (queue1.available() >= 2 && queue2.available() >= 2) {
    byte buffer[512];
    byte bufferL[256];
    byte bufferR[256];
    memcpy(bufferL, queue1.readBuffer(), 256);
    memcpy(bufferR, queue2.readBuffer(), 256);
    queue1.freeBuffer();
    queue2.freeBuffer();
    int b = 0;
    for (int i = 0; i < 512; i += 4) {
      buffer[i] = bufferL[b];
      buffer[i + 1] = bufferL[b + 1];
      buffer[i + 2] = bufferR[b];
      buffer[i + 3] = bufferR[b + 1];
      b = b+2;
    }
    elapsedMicros usec = 0;
    frec.write(buffer, 512);  //256 or 512 (dudes code)
    Serial.print("SD write, us=");
    Serial.println(usec);
  }
}

void stopRecording() {
  Serial.println("StopRecording");
  queue1.end();
  queue2.end();
  // flush buffer
  while (queue1.available() > 0 && queue2.available() > 0) {
    queue1.readBuffer();
    queue1.freeBuffer();
    queue2.readBuffer();
    queue2.freeBuffer();
  }
  frec.close(); // close file
  mode = 4;
}

Hello guys. Im using this code and im experiencing some periodic pop sounds in the recordings. Has anyone else had this problem?


Im using teensy 3.6 with audioshield.


Capture.PNG

Capture2.PNG


Thank you
 
How can I play the raw file stereo? The play object is mono..

I'm interested to know how to do this as well. It sounds like the only way is to convert the .raw to a .wav. I've seen folks do that with Audacity, but it'd be nice to do it all onboard the Teensy.

I'm going to dig into it further but I'm curious if anyone has any experience with making the header file for a .raw to a .wav
 
I'm interested to know how to do this as well. It sounds like the only way is to convert the .raw to a .wav. I've seen folks do that with Audacity, but it'd be nice to do it all onboard the Teensy.

I'm going to dig into it further but I'm curious if anyone has any experience with making the header file for a .raw to a .wav

Try this: https://github.com/FrankBoesing/Teensy-WavePlayer
(Can record to wav, despite of the name)
 
I'm going to dig into it further but I'm curious if anyone has any experience with making the header file for a .raw to a .wav

Not a big deal:
To speed up recording and simplify procedures, extend wav header to 512 bytes by inserting a new chunk with real info or dummy data.
before closing file, estimate file size etc, rewind file to header, write header, advance again to EOF and close file.

Edit:
Obviously to record stereo you must multiplex the data again after audio object demultiplexed stereo data into single channel queues.
 
Last edited:
Try this: https://github.com/FrankBoesing/Teensy-WavePlayer
(Can record to wav, despite of the name)

Hey Frank-

I finally got around to trying out the WavePlayer with a Teensy 3.2 and the Audio Shield. I had to change a few things on the I2S record example but it seemed to work. I did run into a loud buzzing noise when playing back the recording, but I'm thinking that's likely related to my janky test setup. I read a bit about ground loops but I couldn't find any root cause. I tried grounding the mic input and running the Teensy off of a battery rather than USB but didn't notice any difference, still a loud harsh buzzing during playback.

I read on the documentation that you recommend using the Teensy 4.0 with the on-board SD reader rather than SPI; is this mainly related to speed concerns? I have the 3.2 and I'd like to use the audio shield as the project I'm working on needs stereo audio. I'm not opposed to picking up a Teensy 4.0 as well, but was just curious why you recommended that.
 
Hey Frank-

I read on the documentation that you recommend using the Teensy 4.0 with the on-board SD reader rather than SPI; is this mainly related to speed concerns? I have the 3.2 and I'd like to use the audio shield as the project I'm working on needs stereo audio. I'm not opposed to picking up a Teensy 4.0 as well, but was just curious why you recommended that.

I recommend a Teensy 4.1, to use its inbuilt SD Slot (not the one from the shield) and a really good SD Card (There is a thread about good cards here). Note, both: Teensy 4.1 PLUS fast card :)
The inbuilt slot has the faster 4 bit SDIO interface which is *way* faster than SPI. The CPU is not that important.
And it's the only Teensy where I played a bit with recording. However, it's not tested much. There is a chance that it will not work with your program, too. No guarantees.
I'm using other systems now, so it's unlikely that I do much with that anytime soon.
 
Last edited:
Hey I tried the code you posted, and it works well but I have issues with the recording on the SD card. Sometimes the usec time drastically increases from 5us to random numbers higher than, 50000us. Did anyone encounter that also ? It is problematic because the audio is slightly cut.
Screenshot 2023-02-27 181627.jpg
 
Back
Top