Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 9 of 9

Thread: Bits of audio missing from recordings.

  1. #1
    Junior Member
    Join Date
    Oct 2017
    Posts
    18

    Bits of audio missing from recordings.

    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...dio-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?

  2. #2
    Senior Member
    Join Date
    Nov 2012
    Posts
    939
    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

  3. #3
    Senior Member
    Join Date
    Jul 2014
    Posts
    1,813
    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

  4. #4
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany NRW
    Posts
    4,451
    Yes, one buffer is 2.9ms. Too less.

  5. #5
    Senior Member
    Join Date
    Jul 2014
    Posts
    1,813
    Also,
    use exFAT formatted disks (can be used with SdFS from Bill Greiman and uSDFS ported from Elm ChaN's SW)

  6. #6
    Junior Member
    Join Date
    Oct 2017
    Posts
    18
    Thanks for the replies~

    Quote Originally Posted by el_supremo View Post
    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?

  7. #7
    Senior Member
    Join Date
    Nov 2012
    Posts
    939
    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

  8. #8
    Senior Member DD4WH's Avatar
    Join Date
    Oct 2015
    Location
    Central Europe
    Posts
    302
    Hi!

    maybe it helps to know that you are not the only one having missing bits in recordings ;-).

    https://forum.pjrc.com/threads/52175...oSoundRecorder

    All the best, have fun with the Teensy,

    Frank DD4WH

  9. #9
    Junior Member
    Join Date
    Oct 2017
    Posts
    18
    Quote Originally Posted by DD4WH View Post
    Hi!

    maybe it helps to know that you are not the only one having missing bits in recordings ;-).

    https://forum.pjrc.com/threads/52175...oSoundRecorder

    All the best, have fun with the Teensy,

    Frank DD4WH

    Hey Frank,

    I forgot to thank you for your reply and the extensive documentation.
    I currently don't have a 3.6 at hand, but will order one and play around with it~

    inimidi

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •