wav file playback speed issue

Hi,

I have seen some really good suggestions for working with wav file on this forum, Currently i am having i
issue while playing back wav files on media player. This issue is not on arduino platform, i am using a board from silicon labs and a analog mems microphone INMP401, i am recording wav files on sdcard interfaced to the board. Previously i have successfully recorded 8bit-mono wav file on arduino mkr zero without playback issue. So with the silicon labs board when i record 20sec wav file(8bit-mono) at 44100 hz sample rate, file is created with 20sec audio data with correct speed but voice is distorted and when i record 20sec wav file(16bit-mono) at 44100 hz sample rate, file is created with 10sec audio data with double the playback speed but voice is not distorted. I have attached 2 audio file 8bit-mono and 16bit-mono, by looking at it can anyone tell me what i might be doing wrong while recording.

Audio4.wav is 8bit-mono and sound2.wav is 16bit-mono.

Zip.zip (903 KB)

I can't listen to your files right now 'cause I'm at work.

But, every-other byte in AUDIO4 is zero.

Double-speed playback (half-speed recording) means you are only saving samples at half the sample-rate. Like, you're writing 41,100 bytes per second instead of 441,00 samples per second.

I guess you have a mess with mono and stereo, like recording in stereo but playing it back as a mono recording.

Maybe look at the headers of the file to confirm the recording format: Microsoft WAVE soundfile format

Thanks DVDdoug, As the Audio4.wav is recorded for 20sec at 44100hz(8bit-mono), it should have file size of around 882000 bytes (882kb) and as the actual file size is 863KB, doesnt that mean data is sampled correctly.

Hi 6v6gt, i have already gone through this document and in my wav header i am defining number of channel as 1, which is mono.

as the actual file size is 863KB, doesnt that mean data is sampled correctly

No. Neither of them have the data recorded correctly. As @DVDdoug pointed out, "every-other byte in AUDIO4 is zero". The data in the stereo file are also incorrect. Also, both your files have an incorrect file size recorded in the wav header.

Post your code (in code tags using the </> icon).

Pete

I am posting only the wav header part as code is not on arduino platform,

typedef struct {
    char id[RIFF_ID_LENGTH];
    uint32_t size;
} chunk_t;

typedef struct {
    uint16_t format;
    uint16_t numberOfChannels;
    uint32_t samplesPerSecond;
    uint32_t bytesPerSecond;
    uint16_t bytesPerCapture;
    uint16_t bitsPerSample;
} wavFormat_t;

typedef struct {
    chunk_t riff;
    char format[RIFF_ID_LENGTH];
    chunk_t fmt;
    wavFormat_t wavFormat;
    chunk_t data;
} wavHeader_t;

static wavHeader_t wavHeader = {
    .riff = {.id = "RIFF", .size = 0},
    .format = "WAVE",
    .fmt = {.id = "fmt ", .size = 16},
    .wavFormat = {.format = 1, .numberOfChannels = 1, .samplesPerSecond = 0, .bytesPerSecond = 0, .bytesPerCapture = 2, .bitsPerSample = 16},
    .data = {.id = "data", .size = 0}
};

void setHeaderDetails(uint32_t sampleRate, uint32_t numberOfSamples)
{
    wavHeader.wavFormat.samplesPerSecond = sampleRate;
    wavHeader.wavFormat.bytesPerSecond = sampleRate;
    wavHeader.riff.size = sizeof(wavHeader_t) + numberOfSamples- sizeof(chunk_t);
    wavHeader.data.size = sizeof(wavHeader_t) + numberOfSamples;
}

Basically i am using timer to trigger ADC at desired sample rate and after that through DMA i am collecting samples and writing data to sdcard. At the start of recording i will write wav header to file and update final file size using setHeaderDetails() function at end of recording.

Also when i recorded wav file on mkr zero, there's slight background noise. I cant figure out is it due to power supply or other factor.
(Zip file contains file recorded from mkr zero)

D.zip (73.3 KB)

Here's some info and comments on the header of one of your files:

-a----       1999-12-31   1:30 PM         203660 SOUND38.WAV

.\wav_header .\Downloads\SOUND38.WAV
wav_header V1.02
riff       = RIFF
flength    = 203652      //This is correct = length of whole file minus 8
wave       = WAVE
fmt        = fmt
blocksize  = 16          // PCM = 16
formattag  = 1           // PCM = 1
numchans   = 1           // mono = 1
srate      = 18750       // sample rate
bytes/sec  = 18750       //
bytes/samp = 1           // 1 byte per sample
bits/samp  = 8           // 8 bits per sample
data       = data        // data chunk
dlength    = 203652      // WRONG - This should be flength-36 = 203608

Your data is incorrect too. 8-bit data are unsigned offset by 128 (=0x80). Yours don't appear to have the offset applied - all the values are above 0x80. But if you don't post your code, I can't help you with that.

Pete

This is the code for my recording with mkr zero and analog mic

#include <SD.h>
#include <SPI.h>

#define TIME_LIMIT    15  // (seconds)
#define PIN                 A3

File rec;
uint8_t buffer[512],buffer1[512];
volatile int available = 0;
volatile int read = 0;
unsigned long recordStart = 0;
bool recordInProgress = false;
char filename[16];
unsigned long fileSize = 0L;
unsigned long waveChunk = 16;             //16 for PCM
unsigned int waveType = 1;
unsigned int numChannels = 1;            //1:Mono 2:Stereo
unsigned long sampleRate = 18750;        //Sample_rate
unsigned long bytesPerSec = 18750;       //(Sample_rate * BitsPerSample * Channels) / 8
unsigned int blockAlign = 1;             //(BitsPerSample * Channels) / 8
unsigned int bitsPerSample = 8;          
unsigned long dataSize = 0L;
unsigned long recByteSaved = 0L;
byte byte1, byte2, byte3, byte4;
byte bufferToWrite[2048];
byte buffer_val[2048];
int lenOfBufferToWrite = 0;
int temp = 0;
int16_t analog_mic;

static int n = 0;              //variable to increment file number

const byte gClk = 0; //used to define which generic clock we will use for ADC
const int cDiv = 1; //divide factor for generic clock

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

    // see if the card is present and can ben initialized
    if (!SD.begin())
    {
       Serial.println("Card SD failed or not present");     
       return; // do nothing
    }
    setMeasPin();
    genericClockSetup(gClk,cDiv); //setup generic clock and routed it to ADC
    aDCSetup();
}

void loop()
{
  if(!recordInProgress)
   {
    StartRec();
    while (ADC->INTFLAG.bit.RESRDY == 0);   // Waiting for conversion to complete
    analog_mic = ADC->RESULT.reg;
   }
    if (millis() - recordStart <= TIME_LIMIT * 1000)
    {
      
           for (int i = 0; i < 96; i++)
            {
                while (ADC->INTFLAG.bit.RESRDY == 0);   // Waiting for conversion to complete
                analog_mic = ADC->RESULT.reg;
                bufferToWrite[lenOfBufferToWrite + i] = analog_mic;
            }
           lenOfBufferToWrite += 96;
            if (lenOfBufferToWrite == 2016)
            {
                // Write on SD card by batches of 2048 bits for better performance
                rec.write(bufferToWrite, 2016);
                lenOfBufferToWrite = 0;
                recByteSaved += 2016;
            }
    }
    else if (recordInProgress)
    {

        Serial.println(recByteSaved); 
        StopRec();
    }
}

void StartRec()
{ // begin recording process
    writeWavHeader();
    Serial.println(F("Starting recording"));
    recordStart = millis();
    recordInProgress = true;
}

void StopRec()
{ // stop recording process, update WAV header, close file
    Serial.println(F("Ending recording"));
    writeOutHeader();
    recordInProgress = false;
}

void writeOutHeader()
{ // update WAV header with final filesize/datasize
    rec.seek(4);
    recByteSaved = recByteSaved + 36;
    byte1 = recByteSaved & 0xff;
    byte2 = (recByteSaved >> 8) & 0xff;
    byte3 = (recByteSaved >> 16) & 0xff;
    byte4 = (recByteSaved >> 24) & 0xff;
    rec.write(byte1);
    rec.write(byte2);
    rec.write(byte3);
    rec.write(byte4);

    recByteSaved = recByteSaved - 36;
    rec.seek(40);
    rec.write(byte1);
    rec.write(byte2);
    rec.write(byte3);
    rec.write(byte4);
    rec.close();
    Serial.println("File closed!");

}

void writeWavHeader()
{ // write out original WAV header to file
    recByteSaved = 0;
    snprintf(filename, sizeof(filename), "audio%d.wav", n); 
    if (SD.exists(filename))
    {
      //SD.remove(filename);
      n++;
      snprintf(filename, sizeof(filename), "audio%d.wav", n);
      Serial.println(filename);
    }
    rec = SD.open(filename, FILE_WRITE);
    Serial.println("Open file soundfile!");
    rec.write("RIFF");
    byte1 = fileSize & 0xff;
    byte2 = (fileSize >> 8) & 0xff;
    byte3 = (fileSize >> 16) & 0xff;
    byte4 = (fileSize >> 24) & 0xff;
    rec.write(byte1);
    rec.write(byte2);
    rec.write(byte3);
    rec.write(byte4);
    rec.write("WAVE");
    rec.write("fmt ");
    byte1 = waveChunk & 0xff;
    byte2 = (waveChunk >> 8) & 0xff;
    byte3 = (waveChunk >> 16) & 0xff;
    byte4 = (waveChunk >> 24) & 0xff;
    rec.write(byte1);
    rec.write(byte2);
    rec.write(byte3);
    rec.write(byte4);
    byte1 = waveType & 0xff;
    byte2 = (waveType >> 8) & 0xff;
    rec.write(byte1);
    rec.write(byte2);
    byte1 = numChannels & 0xff;
    byte2 = (numChannels >> 8) & 0xff;
    rec.write(byte1);
    rec.write(byte2);
    byte1 = sampleRate & 0xff;
    byte2 = (sampleRate >> 8) & 0xff;
    byte3 = (sampleRate >> 16) & 0xff;
    byte4 = (sampleRate >> 24) & 0xff;
    rec.write(byte1);
    rec.write(byte2);
    rec.write(byte3);
    rec.write(byte4);
    byte1 = bytesPerSec & 0xff;
    byte2 = (bytesPerSec >> 8) & 0xff;
    byte3 = (bytesPerSec >> 16) & 0xff;
    byte4 = (bytesPerSec >> 24) & 0xff;
    rec.write(byte1);
    rec.write(byte2);
    rec.write(byte3);
    rec.write(byte4);
    byte1 = blockAlign & 0xff;
    byte2 = (blockAlign >> 8) & 0xff;
    rec.write(byte1);
    rec.write(byte2);
    byte1 = bitsPerSample & 0xff;
    byte2 = (bitsPerSample >> 8) & 0xff;
    rec.write(byte1);
    rec.write(byte2);
    rec.write("data");
    byte1 = dataSize & 0xff;
    byte2 = (dataSize >> 8) & 0xff;
    byte3 = (dataSize >> 16) & 0xff;
    byte4 = (dataSize >> 24) & 0xff;
    rec.write(byte1);
    rec.write(byte2);
    rec.write(byte3);
    rec.write(byte4);
}

static void ADCsync() 
{
  while (ADC->STATUS.bit.SYNCBUSY == 1); //Just wait till the ADC is free
}

void aDCSetup() {
  // Select reference
  REG_ADC_REFCTRL = 0; //set vref for ADC to VCC

  // Average control 1 sample, no right-shift
  REG_ADC_AVGCTRL |= ADC_AVGCTRL_SAMPLENUM_1;

  // Sampling time, no extra sampling half clock-cycles
  REG_ADC_SAMPCTRL = ADC_SAMPCTRL_SAMPLEN(0);
  
  // Input control and input scan
  REG_ADC_INPUTCTRL |= ADC_INPUTCTRL_GAIN_1X | ADC_INPUTCTRL_MUXNEG_GND | ADC_INPUTCTRL_MUXPOS_PIN3;
  // Wait for synchronization
  while (REG_ADC_STATUS & ADC_STATUS_SYNCBUSY);

  ADC->CTRLB.reg |= ADC_CTRLB_RESSEL_8BIT | ADC_CTRLB_PRESCALER_DIV512 | ADC_CTRLB_FREERUN; //This is where you set the divide factor, note that the divide call has no effect until you change Arduino wire.c
  //Wait for synchronization
  while (REG_ADC_STATUS & ADC_STATUS_SYNCBUSY);

  ADC->WINCTRL.reg = ADC_WINCTRL_WINMODE_DISABLE; // Disable window monitor mode
  while(ADC->STATUS.bit.SYNCBUSY);

  ADC->INPUTCTRL.bit.MUXPOS = g_APinDescription[PIN].ulADCChannelNumber; // Selection for the positive ADC input
  while(ADC->STATUS.bit.SYNCBUSY);

  ADC->CTRLA.reg |= ADC_CTRLA_ENABLE; //set ADC to run in standby
  while (ADC->STATUS.bit.SYNCBUSY);
}

//setup measurement pin, using Arduino ADC pin A3
void setMeasPin() {
  // Input pin for ADC Arduino A3/PA04
  REG_PORT_DIRCLR1 = PORT_PA04;

  // Enable multiplexing on PA04
  PORT->Group[0].PINCFG[4].bit.PMUXEN = 1;
  PORT->Group[0].PMUX[1].reg = PORT_PMUX_PMUXE_B | PORT_PMUX_PMUXO_B;
}

//this function sets up the generic clock that will be used for the ADC unit
//by default it uses the 48M system clock, input arguments set divide factor for generic clock and choose which generic clock
void genericClockSetup(int clk, int dFactor) {
  // Enable the APBC clock for the ADC
  REG_PM_APBCMASK |= PM_APBCMASK_ADC;
  
  //This allows you to setup a div factor for the selected clock certain clocks allow certain division factors: Generic clock generators 3 - 8 8 division factor bits - DIV[7:0]
  GCLK->GENDIV.reg |= GCLK_GENDIV_ID(clk)| GCLK_GENDIV_DIV(dFactor);
  while (GCLK->STATUS.reg & GCLK_STATUS_SYNCBUSY);  

  //configure the generator of the generic clock with 48MHz clock
  GCLK->GENCTRL.reg |= GCLK_GENCTRL_GENEN | GCLK_GENCTRL_SRC_DFLL48M | GCLK_GENCTRL_ID(clk); // GCLK_GENCTRL_DIVSEL don't need this, it makes divide based on power of two
  while (GCLK->STATUS.reg & GCLK_STATUS_SYNCBUSY);
  
  //enable clock, set gen clock number, and ID to where the clock goes (30 is ADC)
  GCLK->CLKCTRL.reg |= GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN(clk) | GCLK_CLKCTRL_ID(30);
  while (GCLK->STATUS.bit.SYNCBUSY);
}

I guess it is a 10 bit ADC so you should probably handle any normalisation (like compensating for the DC offset on the microphone and adjusting the volume) in an unsigned 16 bit integer before writing the value to an element of the unsigned 8 bit array.

I don't know the STM32 very well, but maybe you are able to query the ADC, do the normalisation of the ADC value and write the value to the buffer all in the timer call back routine. In the loop(), just do the dump to the SD card.

Using audacity is quite good for visualising the sound files. Here is a view of yours before and after normalisation.

    recByteSaved = recByteSaved - 36;
    rec.seek(40);
    rec.write(byte1);
    rec.write(byte2);
    rec.write(byte3);
    rec.write(byte4);

When you write recByteSaved the second time, you haven't converted it into byte1, byte2 etc. Therefore you write out the same number as before.
You need to insert this after the seek:

    byte1 = recByteSaved & 0xff;
    byte2 = (recByteSaved >> 8) & 0xff;
    byte3 = (recByteSaved >> 16) & 0xff;
    byte4 = (recByteSaved >> 24) & 0xff;

For the ADC value itself, your code is only storing the low eight bits of the ADC value. Assuming that it is a 10-bit ADC and that your audio input is correctly offset to the centre of the ADC range (512), you can modify the line which stores the value in the array to this:

bufferToWrite[lenOfBufferToWrite + i] = analog_mic >> 2;

If it is a 12-bit ADC, shift right 4 places, >> 4.

What is going on here?

           if (lenOfBufferToWrite == 2016)
            {
                // Write on SD card by batches of 2048 bits for better performance
                rec.write(bufferToWrite, 2016);

You are writing 2016 bytes but the comment says 2048.

Pete

MohitB:
but voice is distorted

Looking closely with Audacity at your files, I noticed this in AUDIO4.WAV (I had to amplify, otherwise is hard to tell):
Chopped wave.png
This is not normal, if the wave becomes chopped like this; it means some samples are missing. I bet this is the culprit of your distortion problem (SOUND2.WAV has this same issue too).

Sampling at 44100 Hz might be too precise for some applications; you have only aprox. 22.68 microseconds to spare between samples. If some process takes longer than that, you'll definitely skip some and probably create a little of jitter.

I can say there may be two reasons why this is happening:

  • An interrupt request (most likely by a timer) is delaying the time-critical sample capture.
  • Lack of DMA in the SPI controller. If this transfer is CPU-driven, it will block the program execution; it even postpones any pending IRQ.

To make matters worse, the SD's library doesn't transfer data byte by byte, but in blocks of 512 bytes (aka 1 sector); plus a read back of the next block and some filesystem overhead (if the file grows surpassing the cluster boundary defined in such filesystem). In the worst case (file fragmentation), a simple 512-byte block transfer ends up in 4096 bytes worth of SPI I/O due to filesystem overhead.
If DMA were available, the program execution will be blocked once every 512 bytes, and not every byte 512 times (obviating any other IRQ).

Delays caused by interrupts are inavoidable, even when using DMA (data transfer completion triggers an IRQ as well); thus I suggest you the double and maybe even the triple buffer technique, if RAM allows it.
When the SPI controller lacks of DMA, the only way to make double/triple buffer useful is by making the sampling process interrupt-driven. This way, while the main program takes care of the samples transfer (to the SD card) in one buffer, the interrupt samples new data and stores it in another buffer; minimizing losses if not eliminating them.

@6v6gt I did normalisation on audacity, still i can hear some background buzz sound. Which technique will be better for implementing noise reduction.

  ADC->CTRLB.reg |= ADC_CTRLB_RESSEL_8BIT | ADC_CTRLB_PRESCALER_DIV512 | ADC_CTRLB_FREERUN;

@el_supremo I have set ADC resolution for 8bit in the program, so i dont think i need to shift any bits. But i will try with 10 and 12bit resolution as well.

  // Write on SD card by batches of 2048 bits for better performance

and here i was playing with number of data to be written in sdcard at once, i forgot to change that. It was 2016 instead of 2048.

@Lucario448 I havent tried double buffer technique,but will try to implement that.

If the original sound recording is of poor quality, Audacity is of only limited help in improving it. From the pictures, you can see a large proportion of noise in the signal before your spoken digits and it is that which you have to address first. Anyway, Audacity was suggested only as an analysis tool.

Are you using such a module which produces around 200 mV (I guess offset by half of 3.3 volts) at "normal" volume ? SparkFun Analog MEMS Microphone Breakout - ICS-40180 - BOB-18011 - SparkFun Electronics
And you have connected the output directly to the ADC pin and the devices share a common ground ?

The raw data in your file shows a level of around 0xD0 with little movement so it does look like you have captured the 8 most significant bits (somehow) but the dynamic range is very limited.

I don't know the ADC on the device you are using, but the basic configuration for static items such as voltage reference, gain and the 8 bit resolution which you appear to have set, you can test by connecting a linear potentiometer (say 10k) across the power rails with the slider connected to the ADC input. You should get a clean sweep from 0x00 to 0xFF. Clearly, that can't test the dynamic configuration like sample interval etc.

Edit:

Incidentally, does the code which you have posted in #7 run on the Arduino MKRZero ? It looks remarkably arduino like with setup() and loop() , but I can't see where the object ADC is defined. Is something missing here ?

6v6gt:
Edit:

Incidentally, does the code which you have posted in #7 run on the Arduino MKRZero ? It looks remarkably arduino like with setup() and loop() , but I can't see where the object ADC is defined. Is something missing here ?

I was wondering about the same; but I guess it's for the Zero since no other architecture implements such thing in the Arduino core (up to my knowledge). Another proof is when you notice that there is no additional libraries other than SD and SPI.

Are you using such a module which produces around 200 mV (I guess offset by half of 3.3 volts) at "normal" volume  ?  https://www.sparkfun.com/products/9868 
And you have connected the output directly to the ADC pin and the devices share a common ground ?

@6v6gt Yes, this is the module i am using and output of mic is connected directly to A3 pin of mkr zero.

Incidentally, does the code which you have posted in #7 run on the Arduino MKRZero ? It looks remarkably arduino like with setup() and loop() , but I can't see where the object ADC is defined. Is something missing here ?

Initially this post was not for arduino platform, files uploaded in zip.zip is from a board from silicon labs. I was having playback issue in those file. But later in #5 i uploaded files i recorded on mkr zero as i didnt have playback issue but there was constant background buzz noise.

I can't say much more on the subject. My experience with ADCs is limited to the AVR 8bit architecture and some external SPI controlled ADCs.

However, it looks like you have to address the issue of audio quality of the raw data before looking at getting the headers right. You can, incidentally, import the raw data, that is without headers, directly into Audacity to check it.

Here is a decsription of the SAMD ADC (as in the Arduino Zero) with the native Atmel code.

The Arduino Zero core has presumably built a layer on top of this code base to abstract it.

There is an example of configuring the ADC to collect a number of samples in a buffer (you appear to want 2048), then initiating a callback routine (see 9.2) which could replace what you are doing in the loop. You should look at something like that seriously because, the way you have structured your code, you appear to be losing samples during the time you are writing to the SD card. @Lucario448 has mentioned this also. Your sampling interval at 18750 sps is about 50 microseconds and your code appears to assume that, in between samples, you can write 2048 bytes to the SD card. You probably would need to switch between two (or more) buffers, so the ADC could fill one during the time you are writing the other to the SD card.

However, you'd have to convert the example to use the Arduino Zero equivalent statements.

Anyway, good luck with it all.

MohitB:
But later in #5 i uploaded files i recorded on mkr zero as i didnt have playback issue but there was constant background buzz noise.

I'm imagining the Zero can operate the SD card at its maximum clock frequency, so I guess it takes less than 22 microseconds to write blocks of 2 KB? Or there you are sampling at lower frequency; I don't know, didn't checked the other files just yet.

The buzzing sound even I find difficult to get rid off. I know that's called a "ground loop"; and the only fix I think is effective, is by passing the AC input of the power supply through a galvanic isolator (it's just a 1 to 1 AC transformer), instead of plugging it directly into the outlet.
Another way is by completely isolating the system from the AC power, that's by powering it with a battery; although you may not have a very long run before you have to use an AC-powered charger anyway (unless the solar energy or a DC generator is used).
It's important to note that if you have to share ground with a device powered by a non-isolated power supply, you'll end up fixing nothing because that connection will bring back the ground loop as well.

6v6gt:
You should look at something like that seriously because, the way you have structured your code, you appear to be losing samples during the time you are writing to the SD card. @Lucario448 has mentioned this also. Your sampling interval at 18750 sps is about 50 microseconds and your code appears to assume that, in between samples, you can write 2048 bytes to the SD card. You probably would need to switch between two (or more) buffers, so the ADC could fill one during the time you are writing the other to the SD card.

Indeed that's the problem, and I'm not the only one who agrees.

It appears whatever microcontroller you were using, consumes CPU time to handle SPI transfers (blocking I/O); either due to lack of DMA or bitbanging (software emulation). I'm also thinking the system's clock frequency isn't high enough to operate the SD card at 20 MHz; or it is but can't go that fast due to bitbanging (lack of hardware SPI controller).

Either way, you have a delay problem (caused by the SD card write process).
If you think about it, sampling data is very time-sensitive; if there was a way to do this always in the exact moment and over anything else... Here's where "timed interrupts" come into place.

Interrupts always have higher priority of execution over the main program; this means sampling will occur even if the main program is stuck writting data to the SD card.
You can figure interrupts as "background programs"; although still is not true multitasking, but in practice it's pretty close.

Since interrupts change the code flow in a "unpredictible" manner, they also suffer the typical problems of the concurrence; specially with shared access. In this punctual case, means it's a bad idea that sampling and SD writting operate over the same buffer.
If saving the samples is slower than getting them, for sure you will end up overwritting unsaved data; and thus losing samples anyway.

Since is not a good idea to make those processes operate on the same buffer, you'll need two. This is the so called "double buffer"; where one works in one buffer, and the other in the second one; alternating buffers when needed, but never working on the same one.
I've also mentioned "triple buffer", this is only necessary if the writting process ends up being even slower than anticipated.
You can think the buffers as a way to compensate for the difference in speed of two processes.

Obviously the downside of this technique is RAM usage. If there's not enough available, you'll only have two options:

  • Deal with such distortion.
  • Decrease the data rate (number of channels and/or sample rate) up to the point where sampling is no longer faster than saving (writting to the SD card).

You should investigate about interrupt capabilities on your microcontroller, along with available timers (aka counters since it's a piece of hardware that counts clock cycles) and the amount of RAM; those are some of the ingredients for a more reliable digital audio recorder recipe.