Bits of audio missing from recordings.

Status
Not open for further replies.

inimidi

Member
Hello everyone~

I am trying the audio shield to record 10s of stereo audio. A while back the initial code was written (as can be read about here: https://forum.pjrc.com/threads/46150-Recording-Stereo-Audio-to-SD-Card)

The recordings initially seemed fine as I was testing it by snapping my fingers. In other words short impulses of sound. However, I noticed that when recording a bit of music bits of the recording were missing.
When I pop the recordings into audacity I can also see that the recordings tend to be around 8.4 - 9.2 seconds long. This means that basically between 8-16% of the recording is lossed.

When checking the serial monitor I observe in an increase time it takes to buffer sound. Whereas it takes roughly 2600us sometimes it pop ups to 8000us for a bit.

Based on this I make the assumption that:
- The buffer is not emptied fast enough. New data gets thrown away when the buffer is full. One way to solve this is to increase the buffer size. I tried this as well as increasing the audioMemory, sadly bearing no positive results.
- Find a way to speed up writing from the buffer to SD card. For example getting a faster SD card. I am currently using an: Class 10 8GB card, what should be fast enough.

This problem appears to be present when recording mono as well (tested it with the example library).

My code:

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

#define recPin 2
long recDelay;
long runTime;

String bestandsNaam = "RECORD11.RAW";

// The file where data is recorded
File frec;

void setup() {

  pinMode(recPin, INPUT_PULLUP);

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

}


void loop() {
  runTime = millis();
  if (mode == 1) {
    continueRecording();
    if (runTime > recDelay) {
      stopRecording();
    }
  } else {
    if (digitalRead(recPin) == 0 && mode == 0) {
      startRecording();
      recDelay = runTime + 10000;
    }
  }
}

void startRecording() {
  Serial.println("StartRecording");
  if (SD.exists(bestandsNaam.c_str())) {
    SD.remove(bestandsNaam.c_str());
  }
  frec = SD.open(bestandsNaam.c_str(), 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);
    //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 have an idea how to prevent this ' skipping' within the recording?
 
if (queue1.available() >= 2 && queue2.available() >= 2) {
Why are you waiting for both queues to have at least two buffers available when you only use one of each inside the if statement?

Inside the if statement, it would be more efficient to make the arrays 'short' instead of 'byte'. There'll be less data moving. Something like this:
Code:
    short buffer[256];
    short bufferL[128];
    short bufferR[128];
    memcpy(bufferL, queue1.readBuffer(), 256);
    memcpy(bufferR, queue2.readBuffer(), 256);
    queue1.freeBuffer();
    queue2.freeBuffer();
    for (int i = 0; i < 128; i++) {
      buffer[2*i] = bufferL[i];
      buffer[2*i + 1] = bufferR[i];
    }

Pete
 
uSD cards can easily have write delays of up to 150 ms (rare but possible)
your buffering may be therefore not adequate.
Also, writing large chunks to disk (>8kB) helps
 
Also,
use exFAT formatted disks (can be used with SdFS from Bill Greiman and uSDFS ported from Elm ChaN's SW)
 
Thanks for the replies~

Why are you waiting for both queues to have at least two buffers available when you only use one of each inside the if statement?

Inside the if statement, it would be more efficient to make the arrays 'short' instead of 'byte'. There'll be less data moving. Something like this:
Code:
    short buffer[256];
    short bufferL[128];
    short bufferR[128];
    memcpy(bufferL, queue1.readBuffer(), 256);
    memcpy(bufferR, queue2.readBuffer(), 256);
    queue1.freeBuffer();
    queue2.freeBuffer();
    for (int i = 0; i < 128; i++) {
      buffer[2*i] = bufferL[i];
      buffer[2*i + 1] = bufferR[i];
    }

Pete

I changed the if-statement to:

Code:
if (queue1.available() >= 1 && queue2.available() >= 1)

Removing the if-statement would introduce clicks in the recording.



Using short instead of byte in the continuerecording() function gave an error:

/Users/td0014/Documents/02_code/Recorder/01_retry code/01.2_forum_explorations_-_unstable/01.2_forum_explorations_-_unstable.ino:122:27: error: no matching function for call to 'File::write(short int [512], int)'
frec.write(buffer, 512);
^
In file included from /Applications/Arduino.app/Contents/Java/hardware/teensy/avr/libraries/Audio/play_sd_raw.h:32:0,
from /Applications/Arduino.app/Contents/Java/hardware/teensy/avr/libraries/Audio/Audio.h:103,
from /Users/td0014/Documents/02_code/Recorder/01_retry code/01.2_forum_explorations_-_unstable/01.2_forum_explorations_-_unstable.ino:1:
/Applications/Arduino.app/Contents/Java/hardware/teensy/avr/libraries/SD/SD.h:38:18: note: candidate: virtual size_t File::write(uint8_t)
virtual size_t write(uint8_t);
^
/Applications/Arduino.app/Contents/Java/hardware/teensy/avr/libraries/SD/SD.h:38:18: note: candidate expects 1 argument, 2 provided
/Applications/Arduino.app/Contents/Java/hardware/teensy/avr/libraries/SD/SD.h:39:18: note: candidate: virtual size_t File::write(const uint8_t*, size_t)
virtual size_t write(const uint8_t *buf, size_t size);
^
/Applications/Arduino.app/Contents/Java/hardware/teensy/avr/libraries/SD/SD.h:39:18: note: no known conversion for argument 1 from 'short int [512]' to 'const uint8_t* {aka const unsigned char*}'
In file included from /Applications/Arduino.app/Contents/Java/hardware/teensy/avr/cores/teensy3/Stream.h:24:0,
from /Applications/Arduino.app/Contents/Java/hardware/teensy/avr/cores/teensy3/HardwareSerial.h:246,
from /Applications/Arduino.app/Contents/Java/hardware/teensy/avr/cores/teensy3/WProgram.h:46,
from /Applications/Arduino.app/Contents/Java/hardware/teensy/avr/cores/teensy3/Arduino.h:3,
from /var/folders/rz/5mq9_gw547j5lvp7n48nc0f40000gn/T/arduino_build_539164/sketch/01.2_forum_explorations_-_unstable.ino.cpp:1:
/Applications/Arduino.app/Contents/Java/hardware/teensy/avr/cores/teensy3/Print.h:63:9: note: candidate: size_t Print::write(const char*, size_t)
size_t write(const char *buffer, size_t size) { return write((const uint8_t *)buffer, size); }
^
/Applications/Arduino.app/Contents/Java/hardware/teensy/avr/cores/teensy3/Print.h:63:9: note: no known conversion for argument 1 from 'short int [512]' to 'const char*'
/Applications/Arduino.app/Contents/Java/hardware/teensy/avr/cores/teensy3/Print.h:59:9: note: candidate: size_t Print::write(const char*)
size_t write(const char *str) { return write((const uint8_t *)str, strlen(str)); }
^
/Applications/Arduino.app/Contents/Java/hardware/teensy/avr/cores/teensy3/Print.h:59:9: note: candidate expects 1 argument, 2 provided
Multiple libraries were found for "SD.h"
Used: /Applications/Arduino.app/Contents/Java/hardware/teensy/avr/libraries/SD
Not used: /Applications/Arduino.app/Contents/Java/libraries/SD
Error compiling for board Teensy 3.2 / 3.1.

I belief because a short is an 16bit data-type and a byte is an 8byte data-type. The SD library seems to require 8bit data types.

So I dug into the SD library and changed:
Code:
size_t File::write(uint8_t val) {
  return write(&val, 1);
}

size_t File::write(const uint8_t *buf, size_t size) {
  size_t t;
  if (!_file) {
    setWriteError();
    return 0;
  }
  _file->clearWriteError();
  t = _file->write(buf, size);
  if (_file->getWriteError()) {
    setWriteError();
    return 0;
  }
  return t;
}

into:

Code:
size_t File::write(uint16_t val) {
  return write(&val, 1);
}


//16->8
size_t File::write(const uint16_t *buf, size_t size) {
  size_t t;
  if (!_file) {
    setWriteError();
    return 0;
  }
  _file->clearWriteError();
  t = _file->write(buf, size);
  if (_file->getWriteError()) {
    setWriteError();
    return 0;
  }
  return t;
}

However it resulted in a long string of error messages spawning across different documents of the library.

/Applications/Arduino.app/Contents/Java/hardware/teensy/avr/libraries/SD/SD.h:39:18: note: no known conversion for argument 1 from 'short int [512]' to 'const uint8_t* {aka const unsigned char*}'

What would be the best course of action from this point? Any thoughts?
 
Removing the if-statement would introduce clicks in the recording.
Yes, but expecting there to be two buffers in each of the left and right queues wasn't good either.

error: no matching function for call to 'File::write(short int [512], int)'
frec.write(buffer, 512);
There's no need to rewrite the SD library. You just cast the short to whichever type is expected. In this case, the compiler tells you:
note: candidate: virtual size_t File::write(const uint8_t*, size_t)
virtual size_t write(const uint8_t *buf, size_t size);
so you cast it:
Code:
  frec.write((const uint8_t *)buffer, 512);

Or, for now, put it back the way it was. My main concern was that the if statement might have caused you to lose buffers or get out of sync.

Pete
 
Status
Not open for further replies.
Back
Top