Choppy WAV audio recording

Status
Not open for further replies.

letNic

Member
Hi,

When I record from this script, more the recording is long (maximum rec time is set to 1:30), more it becomes choppy. It's like if some slices are missing. For example, a 90 seconds of recording produce a 75 seconds file.

Not sure, but I suspect it is the WAV header procedure in the script.

Still searching...

What do you think?

Code:
//===============================================================
//------------------- VoxPHONE PROTO - 021 ----------------------
//---------------------- 2018 - 02 - 09 -------------------------
//--------------------- NICOLAS__LETARTE ------------------------
//===============================================================

#include <Bounce.h>
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SD_t3.h>
#include <SerialFlash.h>
//#include <Time.h>
//#include <TimeLib.h>

// write wav
unsigned long ChunkSize = 0L;
unsigned long Subchunk1Size = 16;
unsigned int AudioFormat = 1;
unsigned int numChannels = 1;
unsigned long sampleRate = 44100;
unsigned int bitsPerSample = 16;
unsigned long byteRate = sampleRate * numChannels * (bitsPerSample / 8); // samplerate x channels x (bitspersample / 8)
unsigned int blockAlign = numChannels * bitsPerSample / 8;
unsigned long Subchunk2Size = 0L;
unsigned long recByteSaved = 0L;
unsigned long NumSamples = 0L;
byte byte1, byte2, byte3, byte4;

// Audio input on Teensy
const int myInput = AUDIO_INPUT_MIC;


// GUItool: begin automatically generated code
AudioInputI2S            i2s2;           //xy=188,136
AudioSynthWaveform       waveform1;      //xy=233,360
AudioPlaySdWav           playSdWav1;     //xy=235,318
AudioMixer4              mixer1;         //xy=398,343
AudioRecordQueue         queue1;         //xy=411,93
AudioAnalyzePeak         peak1;          //xy=413,178
AudioOutputI2S           i2s1;           //xy=658,347
AudioConnection          patchCord1(i2s2, 0, queue1, 0);
AudioConnection          patchCord2(i2s2, 0, peak1, 0);
AudioConnection          patchCord6(i2s2, 0, mixer1, 1);
AudioConnection          patchCord3(waveform1, 0, mixer1, 2);
AudioConnection          patchCord4(waveform1, 0, mixer1, 3);
AudioConnection          patchCord5(playSdWav1, 0, mixer1, 0);
//AudioConnection          patchCord6(playSdWav1, 1, mixer1, 1);
AudioConnection          patchCord7(mixer1, 0, i2s1, 0);
AudioConnection          patchCord8(mixer1, 0, i2s1, 1);
AudioControlSGTL5000     audioShield;     //xy=103,62
// GUItool: end automatically generated code

///const int HOME_MSG = "HOME/ACCU.WAV";

// 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

#define    b1Pin       0
//#define    b1ON        digitalWrite(b1Pin,HIGH)
//#define    b1OFF       digitalWrite(b1Pin,LOW)
#define    b2Pin       1
//#define    b2ON        digitalWrite(b2Pin,HIGH)
//#define    b2OFF       digitalWrite(b2Pin,LOW)
#define    vPin        2
#define    vLedON      digitalWrite(vPin,HIGH)
#define    vLedOFF     digitalWrite(vPin,LOW)
#define    rPin        3
#define    rLedON      digitalWrite(rPin,HIGH)
#define    rLedOFF     digitalWrite(rPin,LOW)
#define    aPin        4
#define    aLedON      digitalWrite(aPin,HIGH)
#define    aLedOFF     digitalWrite(aPin,LOW)

// Bounce objects to easily and reliably read the buttons
Bounce lineUpButton = Bounce(b1Pin, 12);
Bounce lineDownButton = Bounce(b2Pin, 12);

// Variables globales
int mode = 0;  // 0=Stby, 1=homeMsg, 2=beep1, 3=recording, 4=beep2, 5=stopAll
int line; // 0=Down, 1=Up

// Timers
elapsedMillis recTime;
elapsedMillis UpTime;
elapsedMillis AlertTime;

// The file where data is recorded
File frec;


//===============================================================
//===============================================================
//===============================================================

void setup() {
  Serial.begin(9600);
  AudioMemory(60);
  pinMode(b1Pin, INPUT_PULLUP);
  pinMode(b2Pin, INPUT_PULLUP);
  pinMode(vPin, OUTPUT);
  pinMode(rPin, OUTPUT);
  pinMode(aPin, OUTPUT);
  audioShield.enable();
  audioShield.inputSelect(myInput);
  audioShield.micGain(10);  //0-63
  audioShield.volume(0.6);
  mixer1.gain(0, 0.9); // Accueil
  mixer1.gain(1, 0.4); // Micro
  mixer1.gain(2, 0.4); // Synth L
  //  mixer1.gain(3, 0.4); // Synth R
  waveform1.begin(WAVEFORM_SINE);
  aLedON;
  SPI.setMOSI(SDCARD_MOSI_PIN);
  SPI.setSCK(SDCARD_SCK_PIN);
  if (!(SD.begin(SDCARD_CS_PIN))) {
    while (1) {
      Serial.println("Impossible d\'acceder à la carte SD");
      rLedON;
      vLedOFF;
      delay(500);
      rLedOFF;
      vLedON;
      delay(500);
    }
  }
  vLedON;
  Serial.println("Bienvenue dans VoxPHONE");
  delay(500);
  Serial.println("VoxPHONE en Stand by");
  delay(1000);
}

void loop() {
  lineDownButton.update();
  if (lineDownButton.fallingEdge()) {
    line = 0;
    audioShield.volume(0);
    Serial.println("La ligne est fermee");
    if (mode > 0) stopAll();
  }
  lineUpButton.update();
  if (lineUpButton.fallingEdge()) {
    line = 1;
    audioShield.volume(0.6);
    Serial.println("La ligne est ouverte");
    vLedOFF;
    rLedON;
    if (mode == 0) startHomeMsg();
  }
  if (line == 1) {
    if (mode == 1) continueHomeMsg();
    if (mode == 2) playBeepOne();
    if (mode == 3) recordUser();
    if (mode == 4) continueRecordUser();
    if (mode == 5) playBeepTwo();
    if (mode == 6) alert();
    if (mode == 8) alertLoop();
  }
  //  // read the knob position (analog input A1 ou 15) =========================== VOLUME GLOBAL
  //  int knob = analogRead(15);
  //  float vol = (float)knob / 1280.0;
  //  audioShield.volume(vol);
}





//=======================================================================================
//=======================================================================================
//=======================================================================================

void alert() { //================================================================ ALERT!
  lineDownButton.update();
  if (lineDownButton.fallingEdge()) {
    waveform1.amplitude(0);
    stopAll();
  } else {
    if (UpTime >= 10000) {
      Serial.println("Veuillez raccrocher");
      mode = 8;
      waveform1.frequency(1320);
      waveform1.amplitude(0.1);
      delay(1000);
      AlertTime = 0;
    }
  }
}

void alertLoop() {
  if (AlertTime >= 15000) {
    waveform1.amplitude(0);
    rLedON;
    vLedON;
    line = 0;
    Serial.println("S.V.P. Veuillez raccrocher...");
  } else {
    waveform1.frequency(1000);
    waveform1.amplitude(0.1);
    rLedOFF;
    vLedON;
    delay(100);
    waveform1.amplitude(0);
    rLedON;
    vLedOFF;
    delay(100);
  }
}


void startHomeMsg() { //===================================================== HOME MSG
  delay(1000);
  Serial.println("Message d\'accueil");
  playSdWav1.play("HOME/ACCU.WAV");
  mode = 1;
  delay(10); // wait for library to parse WAV info
}

void continueHomeMsg() {
  if (playSdWav1.isPlaying() == 0) {
    playSdWav1.stop();
    mode = 2;
  }
}

void stopHomeMsg() {
  if (mode == 1) playSdWav1.stop();
}


void playBeepOne() { //===================================================== BEEP 1
  lineDownButton.update();
  if (lineDownButton.fallingEdge()) {
    stopAll();
  } else {
    waveform1.frequency(880);
    waveform1.amplitude(0.1);
    delay(1000);
    waveform1.amplitude(0);
    mode = 3;
  }
}


void recordUser() { //===================================================== RECORD
  Serial.println("Enregistrement du message");
  String fileName = String();
  unsigned int filenumber = 1;
  while (!filenumber == 0) {
    fileName = "VP_";
    fileName += filenumber;
    fileName += ".WAV";
    char charFileName[fileName.length() + 1];
    fileName.toCharArray(charFileName, sizeof(charFileName));
    if (SD.exists(charFileName)) {
      filenumber++;
    }
    else {
      //File dataFile = SD.open(charFileName, FILE_WRITE);
      frec = SD.open(charFileName, FILE_WRITE);
      filenumber = 0;
    }
    //Serial.println("Nouveau fichier audio créé");
  }
  if (frec) {
    queue1.begin();
    recTime = 0;
    mode = 4;
    recByteSaved = 0L;
  }
}

void continueRecordUser() {
  if (recTime  >= 90000) { // 90000 ou 1:30
    stopRecording();
  } else {
    if (queue1.available() >= 2) {
      byte buffer[512];
      memcpy(buffer, queue1.readBuffer(), 256);
      queue1.freeBuffer();
      memcpy(buffer + 256, queue1.readBuffer(), 256);
      queue1.freeBuffer();
      //elapsedMicros usec = 0;
      // write all 512 bytes to the SD card
      frec.write(buffer, 512);
      //Serial.print("SD write, us=");
      //Serial.println(usec);
      recByteSaved += 512;
    }
  }
}

void stopRecording() {
  Serial.println("Fin de l\'enregistrement");
  queue1.end();
  if (mode == 4) {
    while (queue1.available() > 0) {
      frec.write((byte*)queue1.readBuffer(), 256);
      queue1.freeBuffer();
      recByteSaved += 256;
    }
    writeOutHeader();
    frec.close();
  }
  mode = 5;
}


void playBeepTwo() { //===================================================== BEEP 2
  lineDownButton.update();
  if (lineDownButton.fallingEdge()) {
    stopAll();
  } else {
    waveform1.frequency(880);
    waveform1.amplitude(0.1);
    delay(1000);
    waveform1.frequency(440);
    delay(125);
    waveform1.frequency(220);
    delay(500);
    waveform1.amplitude(0);
    mode = 6;
    UpTime = 0;
  }
}


void stopAll() { //====================================================================================== STOP-ALL
  if (mode == 1) playSdWav1.stop();
  if ((mode == 3) | (mode == 4)) stopRecording();
  if ((mode == 2) | (mode == 5)) waveform1.amplitude(0);
  rLedOFF;
  vLedON;
  mode = 0;
  Serial.println("VoxPHONE en Stand by");
}


void writeOutHeader() { //============================================== RAW TO WAV
  Subchunk2Size = recByteSaved;
  ChunkSize = Subchunk2Size + 36;
  frec.seek(0);
  frec.write("RIFF");
  byte1 = ChunkSize & 0xff;
  byte2 = (ChunkSize >> 8) & 0xff;
  byte3 = (ChunkSize >> 16) & 0xff;
  byte4 = (ChunkSize >> 24) & 0xff;
  frec.write(byte1);  frec.write(byte2);  frec.write(byte3);  frec.write(byte4);
  frec.write("WAVE");
  frec.write("fmt ");
  byte1 = Subchunk1Size & 0xff;
  byte2 = (Subchunk1Size >> 8) & 0xff;
  byte3 = (Subchunk1Size >> 16) & 0xff;
  byte4 = (Subchunk1Size >> 24) & 0xff;
  frec.write(byte1);  frec.write(byte2);  frec.write(byte3);  frec.write(byte4);
  byte1 = AudioFormat & 0xff;
  byte2 = (AudioFormat >> 8) & 0xff;
  frec.write(byte1);  frec.write(byte2);
  byte1 = numChannels & 0xff;
  byte2 = (numChannels >> 8) & 0xff;
  frec.write(byte1);  frec.write(byte2);
  byte1 = sampleRate & 0xff;
  byte2 = (sampleRate >> 8) & 0xff;
  byte3 = (sampleRate >> 16) & 0xff;
  byte4 = (sampleRate >> 24) & 0xff;
  frec.write(byte1);  frec.write(byte2);  frec.write(byte3);  frec.write(byte4);
  byte1 = byteRate & 0xff;
  byte2 = (byteRate >> 8) & 0xff;
  byte3 = (byteRate >> 16) & 0xff;
  byte4 = (byteRate >> 24) & 0xff;
  frec.write(byte1);  frec.write(byte2);  frec.write(byte3);  frec.write(byte4);
  byte1 = blockAlign & 0xff;
  byte2 = (blockAlign >> 8) & 0xff;
  frec.write(byte1);  frec.write(byte2);
  byte1 = bitsPerSample & 0xff;
  byte2 = (bitsPerSample >> 8) & 0xff;
  frec.write(byte1);  frec.write(byte2);
  frec.write("data");
  byte1 = Subchunk2Size & 0xff;
  byte2 = (Subchunk2Size >> 8) & 0xff;
  byte3 = (Subchunk2Size >> 16) & 0xff;
  byte4 = (Subchunk2Size >> 24) & 0xff;
  frec.write(byte1);  frec.write(byte2);  frec.write(byte3);  frec.write(byte4);
  frec.close();
  //  Serial.println("header written");
  //  Serial.print("Subchunk2: ");
  //  Serial.println(Subchunk2Size);
}


//=====================================================
//===================== TODOS =========================
//=====================================================
/*

  √- Indexer les fichiers enregistres

  √ - Traduire en WAV les enregistrements RAW

  √- Connecteurs électriques slims + rallonger le fil

  - Bouton On/Off ext

  √- Bouton reset ext

  - Potentiometre de volume ext

  - LED différent lorsqu'un utilisateur enregistre son message

  - Pouvoir enregistrer le message d'accueil à même le VoxPhone

  - Pouvoir toujours détecter la présence de la carte SD !!!!!

  - Port Micro USB F 

*/
 
Salut Nicolas!

I tested your sketch on my Teensy 3.5 and it worked without problems to record an audio file.

I recorded some music with the mic connected to the audio board and did not recognize any missing parts. [SD card used: SanDisk Ultra 64GB XC1]

However, I have that same phenomenon dependent on the micro SD card I am using.

With some cards, there are missing parts or clicks in the recordings. And my observation was, that using the SD card slot in the audio board worked better than the Teeny 3.5 SD card slot.

So, it seems to be a matter of the quality of the SD card, but sometimes my feeling is that there is also a stochastic component reacting on the local weather ;-).

I also set the mic gain to 40, that makes the sound much better and the drizzling artefacts are gone.

Have fun !

All the best,

Frank

P.S.: we seem to be working on very similar issues at the moment, I am also working on a stand-alone audio recorder, see a very early version on my github:
https://github.com/DD4WH/quakophon
 
Did you manage to play back a WAV-file that you recorded on the SD card directly from the Teensy?

I failed to do that up to now . . .

Maybe you have a hint.

All the best,

Frank
 
Hi Nicolas,

recorded some more minutes and made longer recordings:

* I can confirm that the recordings are choppy (some parts are being missed on the recordings)
* it is apparent in both versions of the recorder: your Voxphone or my Quakophon

It would be nice if the SD card or audio experts have some hints how to fix this!

All the best,

Frank
 
Hi DD4WH,

Yes, I thought it was the SD card Quality, but the problem shows also on high speed too.

As you seen, it is present on more longer recording (my client wanted to have a max of 1:30 which is problematic with this issue).

It look to me that the problem is already there on short rec but the gaps are very thin so almost inaudible, but they'll becomes bigger exponentially.

Thanks for your tests on this code.

We both need help here...
 
Status
Not open for further replies.
Back
Top