audio record to SD only recording half the data, 10 seconds = <5 seconds recorded

donperryjm

Well-known member
My setup:

Audio --> Biquad-->amp -->record & USB

Symptom is as described:

when recording an input I can hear the audio properly coming in on the USB but the file is not saving everything. Only a portion of the file is being saved on the SD. Almost like it's fast forwarded.

I'm going to post the code I'm using:




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

//You may substitute the is2stereoIn for your audioboard or other i2s device
AudioInputI2S            is2stereoIn;    //xy=84,1033
AudioAmplifier           amp1R;          //xy=292,1087
AudioAmplifier           amp1L;          //xy=295,962
AudioFilterBiquad        biquad1L;       //xy=447,799
 
AudioFilterBiquad        biquad1R;       //xy=522,1103
AudioMixer4              mixerL;         //xy=738,834
AudioMixer4              mixerR;         //xy=739,1155
AudioAnalyzePeak         peakR;          //xy=899,1230
AudioAnalyzePeak         peakL;          //xy=924,707
AudioOutputUSB           usb2;           //xy=969,987
AudioRecordQueue         record1;         //xy=1005,849
AudioRecordQueue         record2;         //xy=1005,849
AudioConnection          patchCord1(is2stereoIn, 0, amp1L, 0);
AudioConnection          patchCord2(is2stereoIn, 1, amp1R, 0);
AudioConnection          patchCord3(amp1R, biquad1R);
AudioConnection          patchCord4(amp1L, biquad1L);
AudioConnection          patchCord5(biquad1L, 0, mixerL, 0);
 
AudioConnection          patchCord8(biquad1R, 0, mixerR, 0);
AudioConnection          patchCord9(mixerL, 0, usb2, 0);
AudioConnection          patchCord10(mixerL, peakL);
AudioConnection          patchCord11(mixerL, 0, record1, 0);
AudioConnection          patchCord12(mixerR, 0, usb2, 1);
AudioConnection          patchCord13(mixerR, peakR);
AudioConnection          patchCord14(mixerR, 0, record2, 0);
 
#define SDCARD_CS_PIN    254
#define SDCARD_MOSI_PIN  7
#define SDCARD_SCK_PIN   14
bool sdSetupCalled = false;

void SDsetup() {
  // 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
  
    delay(600);
  }
  else
  {
    sdSetupCalled = true;
    // queue1.
  }
  // startRecording();
}

//write wav
unsigned long ChunkSize = 0L;
unsigned long Subchunk1Size = 16;
unsigned short AudioFormat = 1;
unsigned short numChannels = 1;
unsigned long sampleRate = 44100;
unsigned short bitsPerSample = 16;
unsigned long byteRate = sampleRate * numChannels * (bitsPerSample / 8);// samplerate x channels x (bitspersample / 8)
unsigned short blockAlign = numChannels * bitsPerSample / 8;
unsigned long Subchunk2Size = 0L;
unsigned long recByteSaved = 0L;
unsigned long NumSamples = 0L;
byte byte1, byte2, byte3, byte4;
elapsedMillis  msecs;
// Use these with the Teensy Audio Shield


// Remember which fileMode we're doing
short fileMode = 0;  // 0=stopped, 1=recording, 2=playing
const char wav[] PROGMEM = ".wav";
const char _log[] PROGMEM = "Log_";
const char dat[] PROGMEM = ".dat";

// The file where data is recorded
File f_rec;
File f_log;
File f_cfg;
void RecordingLoop();
void startPlaying();
void continuePlaying();
void writeOutHeader();
void stopPlaying();
void startRecording();
void continueRecording();
void stopRecording();
uint32_t VolumeSize();
uint32_t SDFreeSpace();
elapsedMillis recordPlaybackTimer;
elapsedMillis logFrameTimer; //take frameshot every 20ms

String FormatNumber(long number);

 
#define RECORD_LENGTH 10 //10 second 


String fileName, fileName_log;
void setup()
{
  AudioMemory(20);
  amp1L.gain(1);  //used to be 30
  amp1R.gain(1);  //u/sed to be 30
   
  biquad1L.setHighpass(0, 200, 1); //1.5 was 2.0
  biquad1L.setLowpass(1, 10000, 1); //1.5 was 2.0
  biquad1R.setHighpass(0, 200, 1); //1.5 was 2.0
  biquad1R.setLowpass(1, 10000, 1); //1.5 was 2.0
}
void loop()
{
  if (millis() < RECORD_LENGTH && fileMode == 0)//auto start recording
    startRecording();

  if (fileMode == 1)
    RecordingLoop();

}
void RecordingLoop() {

  if (record1.available() && record2.available()&& recordPlaybackTimer < (RECORD_LENGTH * 1000))
    continueRecording();
  else
    stopRecording();

   
}
inline void stopPlaying()
{
  
}
void startRecording() {
  recordPlaybackTimer = 0;
  if (!sdSetupCalled)
  {
    SDsetup();
  }
  if (!sdSetupCalled)
    return;

  if ( fileMode == 1)
    return;
  short nameIncrement = 0;
  fileName = _log + String(nameIncrement) + wav;
  //find last file in sequence, create new
  while (SD.exists(fileName.c_str())) {
    nameIncrement += 1;
    fileName = _log + String(nameIncrement) + wav;
    //fileName_log = _log+ String(nameIncrement) + dat;
  }
  
  recByteSaved = 0;
  f_rec = SD.open((fileName  ).c_str(), FILE_WRITE_BEGIN);
  //record.record(f_rec, APW_16BIT_SIGNED, 2);
  record1.begin();
  record2.begin();
  fileMode = 1;
}
void continueRecording() {

  int16_t  buffer[512]; // data to write
  
  memcpy_tointerleaveLR(buffer, record1.readBuffer(), record2.readBuffer());
  record1.freeBuffer(); record2.freeBuffer();  // free buffer

  f_rec.write(buffer, 512);
  recByteSaved += 512;

}

void stopRecording() {
  if (fileMode == 0)
    return;
  fileMode = 0;
  record1.end(); //queue L
  record2.end(); //queue R
  // buffer arrays for left and right channels, 
  // and one for LR interleaved
  byte bufferL[256];
  byte bufferR[256];
  byte bufferLR[512];

  // Copy one block from each channel into 
  // a buffer, then free the buffer
  while (record2.available() > 0) {
    memcpy(bufferL, record1.readBuffer(), 256);
    memcpy(bufferR, record2.readBuffer(), 256);
    record1.freeBuffer();
    record2.freeBuffer();


    //Interleave two bytes (one 16bit sample) from the 
    //left and right buffers one after another

    int b = 0;
    for (int i = 0; i <= 511; i = i + 4) {
      bufferLR[i] = bufferL[b];
      bufferLR[i + 1] = bufferL[b + 1];
      bufferLR[i + 2] = bufferR[b];
      bufferLR[i + 3] = bufferR[b + 1];
      b = b + 2;
    }
    // write all 512 bytes to the SD card

    elapsedMicros usec = 0;
    f_rec.write(bufferLR, 512);
    recByteSaved += 512;
    Serial.print("SD write, us=");
    Serial.println(usec);

  }
  writeOutHeader();
  f_rec.close();
}


 

void writeOutHeader() { // update WAV header with final filesize/datasize

//  NumSamples = (recByteSaved*8)/bitsPerSample/numChannels;
//  Subchunk2Size = NumSamples*numChannels*bitsPerSample/8; // number of samples x number of channels x number of bytes per sample
  Subchunk2Size = recByteSaved;
  ChunkSize = Subchunk2Size + 36;
  f_rec.seek(0);

  f_rec.write("RIFF");
  byte1 = ChunkSize & 0xff;
  byte2 = (ChunkSize >> 8) & 0xff;
  byte3 = (ChunkSize >> 16) & 0xff;
  byte4 = (ChunkSize >> 24) & 0xff;
  f_rec.write(byte1);  f_rec.write(byte2);  f_rec.write(byte3);  f_rec.write(byte4);
  f_rec.write("WAVE");
  f_rec.write("fmt ");
  byte1 = Subchunk1Size & 0xff;
  byte2 = (Subchunk1Size >> 8) & 0xff;
  byte3 = (Subchunk1Size >> 16) & 0xff;
  byte4 = (Subchunk1Size >> 24) & 0xff;
  f_rec.write(byte1);  f_rec.write(byte2);  f_rec.write(byte3);  f_rec.write(byte4);
  byte1 = AudioFormat & 0xff;
  byte2 = (AudioFormat >> 8) & 0xff;
  f_rec.write(byte1);  f_rec.write(byte2);
  byte1 = numChannels & 0xff;
  byte2 = (numChannels >> 8) & 0xff;
  f_rec.write(byte1);  f_rec.write(byte2);
  byte1 = sampleRate & 0xff;
  byte2 = (sampleRate >> 8) & 0xff;
  byte3 = (sampleRate >> 16) & 0xff;
  byte4 = (sampleRate >> 24) & 0xff;
  f_rec.write(byte1);  f_rec.write(byte2);  f_rec.write(byte3);  f_rec.write(byte4);
  byte1 = byteRate & 0xff;
  byte2 = (byteRate >> 8) & 0xff;
  byte3 = (byteRate >> 16) & 0xff;
  byte4 = (byteRate >> 24) & 0xff;
  f_rec.write(byte1);  f_rec.write(byte2);  f_rec.write(byte3);  f_rec.write(byte4);
  byte1 = blockAlign & 0xff;
  byte2 = (blockAlign >> 8) & 0xff;
  f_rec.write(byte1);  f_rec.write(byte2);
  byte1 = bitsPerSample & 0xff;
  byte2 = (bitsPerSample >> 8) & 0xff;
  f_rec.write(byte1);  f_rec.write(byte2);
  f_rec.write("data");
  byte1 = Subchunk2Size & 0xff;
  byte2 = (Subchunk2Size >> 8) & 0xff;
  byte3 = (Subchunk2Size >> 16) & 0xff;
  byte4 = (Subchunk2Size >> 24) & 0xff;
  f_rec.write(byte1);  f_rec.write(byte2);  f_rec.write(byte3);  f_rec.write(byte4);
  f_rec.close();
  Serial.println("header written");
  Serial.print("recbytesaved ");
  Serial.println(recByteSaved);
  Serial.print("Subchunk2");
  Serial.println(Subchunk2Size);

}



audio record code:
 
Sorry,

I do almost nothing with Audio. Mostly have simply done things like beeps and the like, to give feedback that a button has been pressed or the like.

So don't really have any good advice with this.

The one thing I would be curious about is it looks like you are writing 512 bytes at a time. Wonder if it would help (or hurt) to do something like 4096 bytes. (Or probably more specifically a multiple of the block size for the SD object.

Also note: that the writes like: f_rec.write(buffer, 512);
Will block until the write completes. So if you still having data coming in, maybe stuff being lost then?

You might do a forum search to see what hopefully others have done.

Good luck
 
+1 for writing in bigger blocks. It would be a good idea to increase the AudioMemory() allocation as well, since this is used for the AudioRecordQueue objects. Note that AudioRecordQueue::available() returns a count of available blocks, so if you wanted to write the file in 4k chunks, you can just wait until available() returns a value >=8 and then process all 16 blocks (8 left and 8 right) at once. As USB also uses AudioMemory for its buffering, it's worth being fairly generous with the allocation, and using AudioMemoryUsageMax() to see if you can cut back once it's working.

It could also help to print the values of millis() and record1.available() either side of the SD write. If the write takes >3ms, and the available() count doesn't increase, then the write is blocking the audio updates. It's fairly unlikely, as otherwise I'd expect the USB audio to be affected, and you say it's OK.

If that doesn't help, you could post a bad SD WAV file and a simultaneous recording from the USB for comparison (you'll probably need to zip them in order to post them).

Oh .. and tell us what hardware you're using, please.
 
teensy 4.1 with pcm1860

I tried putting the audio memory to 240, nothing changed. I also changed the file write and L & R buffer sizes, doubled them and now getting noise.
still looking into it.

here is an audio sample of the SD recording (bad) and good USB recording

View attachment this_is_a_test.zip
 
I'm convinced something is off with my libraries. Ran the example recorder that comes with teensy and it's giving same results.
 
Last edited:
One more thing I've spotted, though it's almost certainly not relevant. The example SD recording didn't have the header written, so it doesn't appear as a stereo WAV file. Did you let the recording run to completion? writeOutHeader() is only called from stopRecording(). I did manage to load the file anyway, and it looks sort of OK but as you say is fast and garbled.

It may be helpful to temporarily replace the I²S audio source with a Teensy-generated waveform, as it'll make it easier to spot incorrect data.

"Something off with the libraries" could indeed cause mayhem, so best fixed ... verbose compiler output will show you which ones are in use so you can pinpoint any oddities. I've also used a temporary "portable" install of Arduino to good effect in the past, as it can be completely isolated from your normal Arduino environment.
 
Did you let the recording run to completion? Yes. The wave format is written afterwards, tested.
Yeah, fast and garbled. Almost as if the buffer is still using 128 bytes instead of the 256 that is set in AUDIO_BLOCK_SAMPLES of audiostream.H
So it sounds like 128 is getting saved instead of 256.

Would be nice to get this working as my next step is to interleave the data into the audio stream.
 
Is this a case of mixing up shorts (int16_t) and bytes? The continueRecording function uses a int16_t[512] array (1024 bytes) while stopRecording uses a byte[512] array (512 bytes). Both cases call f_rec.write with an argument of 512, so they're both writing out 512 bytes which means continueRecording is dropping half the data on the floor, and stopRecording is only handling the lower 8-bits of each sample.
 
Last edited:
Back
Top