Audio Recording / Logging to SD card --> microSoundRecorder

WMXZ, you are a gentleman and a scholar.

Not so quickly, as you may have to do the debugging!
CAVEAT, I'm in the field, with very limited internet access. I will try to post the results, but only if I have connection.
 
Not so quickly, as you may have to do the debugging!

It would be my pleasure. I have the setup ready to go. It currently works well for very short clips. I have also used an example sketch to record 11 second clips (but only with a single mic).
 
It would be my pleasure. I have the setup ready to go.
Here you go. I just uploaded an update adding the TDM mode (change in config.h) and correcting the hibernate issue discussed in other thread.
As said, I have no means to test TDM mode here, but at least it compiles and runs without crashing.
Note: when using 5 Microphones, you may have channel rotation issues (128 does not divide by 5 without remainder) So, every 5 audio blocks you should have the same channel sequence. let me know if there are any issues.
 
I was able to compile, flash and test the code, this is what it sounds like:
https://drive.google.com/open?id=1T0upPwHEpG7QWA1hZ_GyRmXPEAW1zk2X

It sounds pretty jumbled. Could this be the channel rotation issue you mentioned?
could a number of mics be defined in the configuration file and frames divided up into 32 bit blocks?
also there were no menu configuration items, I'm still trying to read and understand how the code is setup, so maybe this is just how it is supposed to be. I checked the timing with an oscilloscope and it looks good. So it is probably just bit shuffling issues. Where should I investigate?
Also, I noticed that the Teensy shuts down every so often, is this the hibernation issue?
 
I was able to compile, flash and test the code, this is what it sounds like:
https://drive.google.com/open?id=1T0upPwHEpG7QWA1hZ_GyRmXPEAW1zk2X

It sounds pretty jumbled. Could this be the channel rotation issue you mentioned?
could a number of mics be defined in the configuration file and frames divided up into 32 bit blocks?
also there were no menu configuration items, I'm still trying to read and understand how the code is setup, so maybe this is just how it is supposed to be. I checked the timing with an oscilloscope and it looks good. So it is probably just bit shuffling issues. Where should I investigate?
Also, I noticed that the Teensy shuts down every so often, is this the hibernation issue?

Thanks for file,
I will have look into it
 
Thanks for file,
I will have look into it


@dreggory,
there are a couple of issues:
1) wav header is not correct (will change that); best is to skip the first 50 bytes, then chan 1 should be first.
2) the microphones are clipping, looks like bad power connection, and also data connection; what is exactly your setup, can you post a picture?

channel 1 is for half the file some low level signal, than it goes to clipping, so I think there is no rotation going on, at least there is no indication in the data.
 
during the same test session I used a different recording program and I verified that the microphones were configured properly. you can hear that the audio is okay.
https://drive.google.com/open?id=1Fxbap6MjVFYcz8dbWkVdYIoXzFHhGc6p

this is the code I used:
Code:
// Record TDM input sound as raw data (16ch, Signed 24-bit little-endian PCM, 44.1kHz, little-endian) to a SD card.
// Note: Format is modified so that mics align their 24 bit data to 32 bit slots, half of the 16 bit
// channels end up being the 8 low bits, and 8 more zeros which are discarded.
//
// Hardware:
//   Mic Pin 6  (SD_O)  to Teensy Pin  9 SCLK  (Output, 11.3 MHz, Checked)
//   Mic Pin 8  (SCK_I) to Teensy Pin 13 SDATA (Input, 11.3 Mbit/sec)
//   Mic Pin 10 (WS_I)  to Teensy Pin 23 FS    (Output, 44100 Hz, Checked)
//
//   Mic GND Pins (1, 3, 5, 7 and 9) to Teensy GND
//   Mic VDD to Teensy 3.3V

//   At startup of the ICS-52000, the start of the frame sync (WS_I) signal should be delayed from the start of the serial clock (SCK_I) by at
//   least 10 ms. This enables the microphone’s internal circuits to completely initialize before starting the synchronization sequence
//   with other microphones in the TDM array. This delay can be implemented either by enabling the WS output (FS) on the clock master at
//   least 10 ms after the SCK_I is enabled, or by externally controlling the signals given to the ICS-52000s.
//   
//
// This example code is in the public domain.

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

// GUItool: begin automatically generated code
AudioInputTDM            tdm1;           //xy=359,317
//AudioRecordQueue         queue13;        //xy=620,615
//AudioRecordQueue         queue14;        //xy=620,649
//AudioRecordQueue         queue15;        //xy=620,682
//AudioRecordQueue         queue12;        //xy=621,581
//AudioRecordQueue         queue16;        //xy=622,716
//AudioRecordQueue         queue9;         //xy=623,480
//AudioRecordQueue         queue10;        //xy=623,514
//AudioRecordQueue         queue11;        //xy=623,547
//AudioRecordQueue         queue4;         //xy=624,315
AudioRecordQueue         queue5;         //xy=624,348
AudioRecordQueue         queue6;         //xy=624,381
//AudioRecordQueue         queue7;         //xy=624,414
//AudioRecordQueue         queue3;         //xy=625,281
//AudioRecordQueue         queue8;         //xy=625,447
//AudioRecordQueue         queue2;         //xy=626,248
//AudioRecordQueue         queue1;         //xy=627,214
//AudioConnection          patchCord1(tdm1, 0, queue1, 0);
//AudioConnection          patchCord2(tdm1, 1, queue2, 0);
//AudioConnection          patchCord3(tdm1, 2, queue3, 0);
//AudioConnection          patchCord4(tdm1, 3, queue4, 0);
AudioConnection          patchCord5(tdm1, 4, queue5, 0);
AudioConnection          patchCord6(tdm1, 5, queue6, 0);
//AudioConnection          patchCord7(tdm1, 6, queue7, 0);
//AudioConnection          patchCord8(tdm1, 7, queue8, 0);
//AudioConnection          patchCord9(tdm1, 8, queue9, 0);
//AudioConnection          patchCord10(tdm1, 9, queue10, 0);
//AudioConnection          patchCord11(tdm1, 10, queue11, 0);
//AudioConnection          patchCord12(tdm1, 11, queue12, 0);
//AudioConnection          patchCord13(tdm1, 12, queue13, 0);
//AudioConnection          patchCord14(tdm1, 13, queue14, 0);
//AudioConnection          patchCord15(tdm1, 14, queue15, 0);
//AudioConnection          patchCord16(tdm1, 15, queue16, 0);
//AudioControlSGTL5000     sgtl5000_1;     //xy=369,479
// GUItool: end automatically generated code

// 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
#define SDCARD_MOSI_PIN  11  // not actually used
#define SDCARD_SCK_PIN   13  // not actually used

// Use these for the SD+Wiz820 or other adaptors
//#define SDCARD_CS_PIN    4
//#define SDCARD_MOSI_PIN  11
//#define SDCARD_SCK_PIN   13

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

unsigned int tsamplemillis = 11000;

String typeofsound;

// The file where data is recorded
File frec;

void setup() {
  
  Serial.begin(9600);

  // Audio connections require memory, and the record queue
  // uses this memory to buffer incoming audio.
  AudioMemory(512);

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

  while(!Serial);

  Serial.println("Welcome to the beta version of Viband's TDM sound recorder, 11 second clips are going to be recorded, be \ncareful introducing a new type of sound already introduced since previous samples will be overwritten.");
  Serial.println("\nNote: SD Library uses short 8.3 names (12 characters). Filename (i.e baby/baby24) can't be longer.");
}

void loop() {

  if(choice==0){
  Serial.println("\nIntroduce new type of sound (i.e baby): ");
  while(!Serial.available());
  typeofsound = Serial.readString();
  sample_number = 0;
  record(typeofsound,sample_number);
  } else if(choice==1){
    sample_number++;
    record(typeofsound,sample_number);
  } else {
    //Do nothing
  }

  Serial.print("\nIntroduce 1 to take another sample or 0 to introduce a new type of sound: ");
  while(!Serial.available());
  choice = Serial.readString().toInt();
  Serial.println(choice);
    
}

void record(String type, int number){
  Serial.println("Recording " + type + String(number) + "...");
  elapsedMillis recordingTime = 0;
  String sname = type + number + ".RAW";
  String path = type + "/" + type + number + ".RAW";
  startRecording(sname, path, type);  
  while(recordingTime<tsamplemillis) continueRecording();
  stopRecording(); 
}

void startRecording(String sname, String path, String type) {
  
  int str_len = path.length()+1;
  char charpath[str_len];
  path.toCharArray(charpath,str_len);

  if (SD.exists(charpath)) {
    // The SD library writes new data to the end of the
    // file, so to start a new recording, the old file
    // must be deleted before new data is written.
    SD.remove(charpath);
  }

  str_len = type.length()+1;
  char chartype[str_len];
  type.toCharArray(chartype,str_len);

  if(!SD.exists(chartype)){
    SD.mkdir(chartype);
  }
  
  frec = SD.open(charpath, FILE_WRITE);
  if (frec) {
    Serial.println("File Open");
//    queue1.begin();
//    queue2.begin();
//    queue3.begin();
//    queue4.begin();
    queue5.begin();
    queue6.begin();
//    queue7.begin();
//    queue8.begin();
//    queue9.begin();
//    queue10.begin();
//    queue11.begin();
//    queue12.begin();
//    queue13.begin();
//    queue14.begin();
//    queue15.begin();
//    queue16.begin();
    mode = 1;
  }
}

void continueRecording() {
  if (queue5.available() >= 2 && queue6.available() >=2) {
    uint16_t buffer1[128];
    uint16_t buffer2[128];
    memcpy(buffer1, queue5.readBuffer(), 256);
    memcpy(buffer2, queue6.readBuffer(), 256);
    queue5.freeBuffer();
    queue6.freeBuffer();
    for(int i = 0; i < 128; i ++){
        frec.write(highByte(buffer2[i])); // LSB
        frec.write(lowByte(buffer1[i])); // Middle Byte
        frec.write(highByte(buffer1[i])); // MSB       
        //frec.write(lowByte(buffer2[i])); // Zeros
    }
  }
}

void stopRecording() {
  Serial.println("Finished recording.");
  queue5.end();
  queue6.end();
  queue5.clear();
  queue6.clear();
  frec.close();  
  mode = 0;
}
this was provided by a user here on this forum named Pedro. I think it misses samples and is limited by what it can capture. But it shows that my setup is valid.
 
I copied the program and will compare.
I will have no internet the next 2/3 days so you have to wait for an answer
 
It actually only uses one channel. It stitches the two 16 bit queues together after shifting out the 8 least significant bits making a 24 bit mono recording. This program also has issues, it seems to miss samples and only record 11 seconds or so. But it does show that my setup works. I also used a program that I wrote that just records 1280 samples for each of the 5 mics, it works perfectly so this shows that my microphone array is working. now I would like to record longer clips and for all 5 mics, that's where your program is needed.
 
Maybe useful for others to know about the signal-to-noise-ratio of the whole setup:

microSoundRecorder based on Teensy 3.6 @96MHz F_CPU without audio board using one ICS43434 digital microphone:

* imported the recorded mono WAV-file into audacity [16-bit, little endian, mono, 48000ksps sample rate]
* highpass filtered the file [cutoff frequency 150Hz, 48dB/octave]
* loudest signal has an amplitude of about 1 without audible clipping/distortion
* noise/weakest signal at an average amplitude of about 0.0007
* 1 / 0.0007 = 1428
* 20 * log (1428) = 63dB SNR
* this is very close to the physically achievable SNR of the microphone, which is given in the datasheet as 65dBA

--> so my conclusion would be that with the ICS43434 the microSoundRecorder records everything that the ICS43434 is able to pick up.

Very nice!

Have fun with the Teensy,

Frank DD4WH
 
Frank B
post#2
One could try to add a SPI-RAM as buffer for the SD-Card to the audio-shield. It has the same pinout as FLASH, so the existing pads will work.
WMXZ
post#3
I have thought about it and have done it on another project (using T3.2), but this limits the overall bit rate to half the SPI-RAM clock rate (write to RAM and read from RAM).
The T3.6 allows already for about 200 kB buffer, so for the 23LC1024 to be useful one needs data rates > 2 MB/s while covering 0.1 s uSD delays. As the 23LC1024 access is limited to 20 MHz, this supports up to 10 MBit/s or 1.25 MB/s data rate (write and read)
In the absence of quad SPI (SQI) on Teensy, this results to an interesting design. I may think about it, as I have an application where 200 kB buffer is not large enough (high speed multichannel acquisition totalling a datarate of 30 MBit/s), but I still have some 23LC1024 available, so I even may try it.

Just curious how this memory would work for a buffer: Everspin MR20H40 - 50MHz 4Mb SPI Interface MRAM https://www.everspin.com/serial-peripheral-interface
4,194,304-bit (524.288 KB) magnetoresistive random access memory.
@digikey Unit Price $16.17 https://www.digikey.com/product-detail/en/everspin-technologies-inc/MR20H40CDF/819-1043-ND/4047456

FEATURES:
No write delays
Unlimited write endurance
Automatic data protection on power loss
Fast, simple SPI interface, up to 50 MHz clock rate with MR20H40.
3.0 to 3.6 Volt power supply range
Direct replacement for serial EEPROM, Flash, and FeRAM
 
Just curious how this memory would work for a buffer: Everspin MR20H40 - 50MHz 4Mb SPI Interface MRAM https://www.everspin.com/serial-peripheral-interface
4,194,304-bit (524.288 KB) magnetoresistive random access memory.
@digikey Unit Price $16.17 https://www.digikey.com/product-detail/en/everspin-technologies-inc/MR20H40CDF/819-1043-ND/4047456

FEATURES:
No write delays
Unlimited write endurance
Automatic data protection on power loss
Fast, simple SPI interface, up to 50 MHz clock rate with MR20H40.
3.0 to 3.6 Volt power supply range
Direct replacement for serial EEPROM, Flash, and FeRAM

It could work up to say 20 MHz (<50 MHz/2) bit clock (that is aggregated sampling rate of 1.25 MHz @16 bit, or 625 kHz @ 32 bit data words)

how it would work:
ACQ ISR would directly write data to MRAM
uSD disk write part would access data von MRAM and copy to uSD
So, for each data word the MRAM has to be accessed twice (that why throughput is half max clock rate)

I did this with multiple 1 MBit 23LC1024 but access sped was limited to 10 MHz
 
Okay, I think I see improvement. Now when I import the audio to Audacity, I can see all 5 channels look like real data.
-However, it sounds terrible. I'm just guessing, but I think that it sounds like the MSB's and the LSB's got switched or maybe shifted out.
-I noticed that the import only worked if 32 bit sample setting was used. Just to clarify, these mics send the data from each mic in 32bit frames but only the first 24 bits are the actual sample, so there are 8 bits following each sample that must be discarded.
-Here is a link to the folder that has the recorded audio using your latest code:
https://drive.google.com/open?id=1gS9NHI1svh2kgKPZxycA21dsd9Kk0mXS
 
Okay, I think I see improvement. Now when I import the audio to Audacity, I can see all 5 channels look like real data.
-However, it sounds terrible. I'm just guessing, but I think that it sounds like the MSB's and the LSB's got switched or maybe shifted out.
I was off internet for a couple of days.
I found an error that would rotate data around. I did also some minor refacturing.
Could you please try the latest GitHub in microSoundRecorder_dev branch?
 
Okay sounds great. I'm still not sure why the audio has to be imported to Audacity as 32 bit float, but it works. There is some popping in each mic's tracks (isolated to a track or two, not evenly spread throughout). I think the popping may be a problem on my end, unless there is something in the code that could cause this?
I was wondering, when I import the audio in audacity and it splits the tracks up, does it keep them in order or does it mix them up? could you could explain how Audacity splits the tracks?
here are the latest recordings:
https://drive.google.com/open?id=1rZXidZGqnewUqFUIAEMwFFbqbXJfdJrK

Thank you for your excellent work. Now I can start processing!
 
Hi WMXZ. What a wonderful project you have going here. It’s impressive how many of us are doing these things and interested in solutions that are extensible and affordable. I am really pleased to see the weather sensing in conjunction with sound recording - it will help me immensely.

I have a system running with a single MEMS mic on a 3.6. All good. It is outdoors now to test battery life over the next few days.

On the other hand I have had no luck getting the Tympan version to work. It compiles but if I turn it off and on again it never boots. Even if I compile to the Teensy and leave connected on USB, mothering is ever recorded and that is when leaving the time window to cover 24 hrs and making the setup standard 120, 60, 180. The console reports the loop in this case but the numbers re mostly 0. I have a Rev C board and I checked to see if the pins are correct. I am not cleare where they re defined though?
 
Okay sounds great. I'm still not sure why the audio has to be imported to Audacity as 32 bit float, but it works. There is some popping in each mic's tracks (isolated to a track or two, not evenly spread throughout). I think the popping may be a problem on my end, unless there is something in the code that could cause this?
I was wondering, when I import the audio in audacity and it splits the tracks up, does it keep them in order or does it mix them up? could you could explain how Audacity splits the tracks?

- IMO this is a bug Audacity, or better when using 'open' (or import wav file) then the sampling frequency is correct, the data (16 bit integer) are also correct displayed (10 s audio data), but format is shown as 32 bit float (I used Audacity 2.1.3).
- Popping, no idea where this is from, the first 3 channels are fine, the last two have similar but not identical spikes (you can see that if you zoom into a single spike)
- splitting channels, they should be in the order they are written to disk. No, I have no idea how Audacity works. If you wanted to know which microphone is which channel, you should do the standard test, touching gently a single microphone and note on which channel it shows up.
 
I have a system running with a single MEMS mic on a 3.6. All good. It is outdoors now to test battery life over the next few days.
You surely tell use the results, right?
On the other hand I have had no luck getting the Tympan version to work. It compiles but if I turn it off and on again it never boots. Even if I compile to the Teensy and leave connected on USB, mothering is ever recorded and that is when leaving the time window to cover 24 hrs and making the setup standard 120, 60, 180. The console reports the loop in this case but the numbers re mostly 0. I have a Rev C board and I checked to see if the pins are correct. I am not cleare where they re defined though?
I just ordered a Tympan, but it will need some time to arrive here.
 
- IMO this is a bug Audacity, or better when using 'open' (or import wav file) then the sampling frequency is correct, the data (16 bit integer) are also correct displayed (10 s audio data), but format is shown as 32 bit float (I used Audacity 2.1.3).

I did see that it was 16 bit samples when I imported the data to octave. I am wondering why you went with 16 bit values instead of 24 bit that the ics-52000 mics send. Though it does look like it is not an issue, since I don't see any clipping. Anyway I am grateful for what I can do with your code, thank you.
 
If you wanted to know which microphone is which channel, you should do the standard test, touching gently a single microphone and note on which channel it shows up.
I tried touching each mic to see what order they are in, and it looks like there is a problem of mixed tracks. I recorded myself saying the mic number, before touching each mic in order ("mic 1" *touch 1*, "mic 2" *touch 2*, "mic 3" *touch 3*, "mic 4" *touch 4*, "mic 5" *touch 5*).
Here is the recorded audio:
https://drive.google.com/open?id=1fq0I9zfdzmVIb_AEb3pwomRa5ce7YrEK
 
I am wondering why you went with 16 bit values instead of 24 bit that the ics-52000 mics send.
because it was supposed to be based on the audio-library, which is 16 bit.
depending what you wanted to do, there is often no need to go to 32 bit or if you wanted 24 bit mode, so 16 bit could be sufficient.
 
Back
Top