Go Down

Topic: Only noise during wav recording (Read 285 times) previous topic - next topic

memel182

Mar 05, 2015, 09:23 pm Last Edit: Mar 05, 2015, 09:29 pm by memel182
Hello everyone,

After parsing this forum and reading the Atmel ATmega2560 doc, I can't figure this out : why am I recording almost only noise in my WAV file?

My hardware : Arduino Mega 2560 + Ethernet shield + SD card + Grove Sound sensor from Seeedstudio (with LM358 amp).

My code :

Code: [Select]

#include <SD.h>

// Value to store analog result
long samples = 0;

volatile uint8_t stream1 = 0;
volatile uint8_t stream2 = 0;
unsigned int bufByteCount;
byte bufWrite;
uint8_t buffer1[512];
uint8_t buffer2[512];

char filename[] = "test.wav";
byte wavheader[44];
File wavFile;

volatile int analogVal;
volatile int writeCount = 0;

void setup() {
  pinMode(A8, INPUT);
  Serial.begin(9600);

  // wavheader setup
  // little endian (lowest byte 1st)
  wavheader[0] = 'R';
  wavheader[1] = 'I';
  wavheader[2] = 'F';
  wavheader[3] = 'F';
  //wavheader[4] to wavheader[7] size of data + header -8
  wavheader[8] = 'W';
  wavheader[9] = 'A';
  wavheader[10] = 'V';
  wavheader[11] = 'E';
  wavheader[12] = 'f';
  wavheader[13] = 'm';
  wavheader[14] = 't';
  wavheader[15] = ' ';
  wavheader[16] = 16;
  wavheader[17] = 0;
  wavheader[18] = 0;
  wavheader[19] = 0;
  wavheader[20] = 1;
  wavheader[21] = 0;
  wavheader[22] = 1;
  wavheader[23] = 0;
  // wavheader[24] to wavheader[27] samplerate hz
  // wavheader[28] to wavheader[31] samplerate*1*1
  // optional bytes can be added here
  wavheader[32] = 1;
  wavheader[33] = 0;
  wavheader[34] = 8;
  wavheader[35] = 0;
  wavheader[36] = 'd';
  wavheader[37] = 'a';
  wavheader[38] = 't';
  wavheader[39] = 'a';
  //wavheader[40] to wavheader[43] sample number


  if (!SD.begin(chipSelect)) {
    Serial.println("Card failed, or not present");
    // don't do anything more:
    return;
  } else {
    Serial.println("SD OK");
  }
  if (SD.exists(filename)) {
    SD.remove(filename);
  }
  wavFile = SD.open(filename, FILE_WRITE);
  wavFile.write(wavheader, 44); // write wav header
  wavFile.seek(44); //set data start


  cli();
  //clear ADCSRA and ADCSRB registers
  ADCSRA = 0;
  ADCSRB = 0;

  //ADMUX |= (1 << REFS0); //set reference voltage
  ADMUX |= (1 << ADLAR); //left align the ADC value- so we can read highest 8 bits from ADCH register only

  ADCSRB |= (1 << MUX5); // activate port ADC7 & ADC8

  ADCSRA |= (1 << ADPS2) | (1 << ADPS0); //set ADC clock with 32 prescaler- 16mHz/32=500kHz
  ADCSRA |= (1 << ADATE); //enabble auto trigger
  ADCSRA |= (1 << ADIE); //enable interrupts when measurement complete
  ADCSRA |= (1 << ADEN); //enable ADC
  ADCSRA |= (1 << ADSC); //start ADC measurements
  sei();
}

// Interrupt service routine for the ADC completion
ISR(ADC_vect) {
  // Must read low first
  analogVal = ADCH; //ADCL | (ADCH << 8);

  bufByteCount++;
  if (bufByteCount == 512 && bufWrite == 0) {
    bufByteCount = 0;
    bufWrite = 1;
    stream1 = 1;
  } else if (bufByteCount == 512 & bufWrite == 1) {
    bufByteCount = 0;
    bufWrite = 0;
    stream2 = 1;
  }

  if (bufWrite == 0) {
    buffer1[bufByteCount] = analogVal;
  }
  if (bufWrite == 1) {
    buffer2[bufByteCount] = analogVal;
  }

}

void headmod(long value, byte location) {
  // write four bytes for a long
  wavFile.seek(location); // find the location in the file
  byte tbuf[4];
  tbuf[0] = value & 0xFF; // lo byte
  tbuf[1] = (value >> 8) & 0xFF;
  tbuf[2] = (value >> 16) & 0xFF;
  tbuf[3] = (value >> 24) & 0xFF; // hi byte
  wavFile.write(tbuf, 4); // write the 4 byte buffer
}

void loop() {
  if (writeCount < 500) {
    if (stream1 == 1) {
      wavFile.write(buffer1, sizeof(buffer1));
      stream1 = 0;
      ++writeCount;
      samples += sizeof(buffer1);
    }
    if (stream2 == 1) {
      wavFile.write(buffer2, sizeof(buffer2));
      stream2 = 0;
      ++writeCount;
      samples += sizeof(buffer2);
    }
  }
  if (writeCount == 501) {
    // update wav header
    float freq = (samples / millis()) * 1000;
    Serial.println(samples);
    Serial.println(freq);
    headmod(samples + 36, 4); //set size of data +44-8
    headmod(freq, 24); //set sample rate Hz
    headmod(freq, 28); //set sample rate Hz
    headmod(samples, 40); // set data size
    wavFile.close();
    Serial.println("end");
    writeCount++;
  }
}


So, according to documentation and some forums readings :
- I write the WAV headers
- I write 500 samples
- I am sampling at about 37KHz (500KHz prescaled / 13), printed out by serial at the end of the 500 samples
- Reading pin A8, left aligned
- AREF wired to 5V like the microphone
- I use interrupts and double buffering

But when I open the wav file (attached) in Audacity, it looks like the image attached, and sounds really badly : essentially noise, with difficulties to distinguish what was recorded (here SUM41's "In Too Deep"). Even if it contains all the samples, the calculated frequency and the right duration, I can't succed in getting a correct quality...

I tried to print out the sensed values on analog pin in the interrupt, and discovered like even in complete silence (relative to my room), I get values around 50. Of course it falls down to 0 if I unplug the wire. Is this normal?

Clearly, I missed something... It the first time I fight with sound on Arduino :) But I need some help. Does it need some gain? Am I wrong with registers? Why the signal's values are not around 0 (in Audacity)? Is my AREF incorrect?

Thank you very much for your enlightments

el_supremo

That code doesn't compile. chipSelect is not defined


You are missing an ampersand here - should be &&
Code: [Select]
  } else if (bufByteCount == 512 & bufWrite == 1) {

Pete

DVDdoug

Quote
- Reading pin A8, left aligned
Did you DC bias the input at 2.5V?  The Arduino can't read the negative half of the AC waveform.

Did you subtract-out the DC bias (512) from the ADC reading?   With the bias, the ADC should read ~512 with silence.

Did you down-sample from 10-bits to 8-bits?

Do you know that 8-bit WAVs use unsigned integers?   i.e. Silence should be 127.  The data is biased at 50%, so if you down-sample the from biased 10-bits to 8-bits, the WAV shouldn't need additional bias.  (16-bit WAVs use signed integers.)



Quote
- AREF wired to 5V like the microphone
You don't need to wire anything to AREF to use the internal default 5V reference.

el_supremo

P.S. it is also best to parenthesize multiple comparisons to avoid ambiguity and make it more obvious to the reader what you intended:
Code: [Select]
 } else if ((bufByteCount == 512) && (bufWrite == 1)) {

And I forgot to mention the most important thing. You must declare as volatile any variable that is used in the ISR, bufByteCount, bufWrite, etc.


Pete

memel182

Thank you el_supremo for the code review, I did not even notice these errors...  :smiley-sad-blue:

DVDdoug : your questions seem to point some interesting things. I have to admit I did not heard about this "bias" and I am a little bit lost in the concepts you mentionned : down-sample, substract bias, input bias at 2.5V... Could you give more detailed hints? Or what can I read about this?

I thought I did not have to do a lot more that the mic hardware gives me (taking into account its amp), but maybe I am confusing things...

Grumpy_Mike

From the web site:-
Quote
Grove - Sound Sensor can detect the sound strength of the environment.
So it is not suitable for recording sounds as it only presents the sound envelope to the Arduino. You need something that gives you the waveform.

memel182

Ok, do you mean I can't use this module to record ambient sound and try to make some recognition? (this is my goal).

I also bought http://www.robotshop.com/en/phidgets-sound-sensor.html

Would it be more appropriate?

Grumpy_Mike

#7
Mar 05, 2015, 11:30 pm Last Edit: Mar 05, 2015, 11:31 pm by Grumpy_Mike
Quote
do you mean I can't use this module to record ambient sound
Correct.

Quote
and try to make some recognition? (this is my goal).
That is a step beyond what the Arduino can do with any great success even given the correct input.

Quote
I also bought http://www.robotshop.com/en/phidgets-sound-sensor.html
Would it be more appropriate?
No down load the product manual, look at the graph on page 8. You will see a 1KHz wave going in and just a voltage level coming out. It is just like the Grove sound sensor in this respect.

DVDdoug

#8
Mar 06, 2015, 03:53 am Last Edit: Mar 06, 2015, 04:00 am by DVDdoug
I didn't research the Groove sensor and I didn't realize you weren't getting audio...


Quote
DVDdoug : your questions seem to point some interesting things. I have to admit I did not heard about this "bias" and I am a little bit lost in the concepts you mentionned :
An audio waveform is AC.   For example, when the positive half of the waveform/voltage goes into a speaker it pushes-out.  When the waveform/voltage swings negative the speaker pulls-in.   (The polarity of the speaker isn't mandatory and some speakers may be wired the opposite way.)

Since the Arduino can't read the negative half of the waveform, the standard method is to put 2.5VDC on the input.   Then if the audio voltage goes to +1V this will be added to the bias and you'll get 3.5V.  If the audio voltage swings to -1V, the negative voltage will be added to the bias and you'll have 1.5V at the ADC input.

With silence, you just get the 2.5V bias and the ADC reads ~512 (half of the 5V range).  Without the bias, silence will read (approximately) zero on the ADC and there's no way to read the negative half of the signal.

This circuit (the parts inside the box) shows you how to bias the input.   The two equal value resistors create a voltage divider that divides 5V in half.   The 10uF capacitor keeps the DC bias from getting back into your audio circuit and it prevents the impedance of the audio circuit from messing-up the voltage divider.   (The AC signal passes-through the capacitor, but DC is blocked.)    You can ignore the 47nF capacitor (I assume it's there for noise reduction.)

This board has a microphone, preamp, and a bias circuit.  But, it doesn't have a variable gain control and that might be an issue.   If you look at the schematic you'll see two 10K resistors biasing the input of the op-amp instead of directly biasing the Arduino's input.  (In this case, there is no capacitor used between the amplifier's output and the Arduino input, so the DC bias can get to the Arduino.)

Quote
down-sample
The ADC is 10-bits (0-1023 unsigned).  You've got an 8-bit WAV file (0-255 unsigned).   You need to divide by 4.   Otherwise, you are going to loose the two most significant bits and you'll basically have garbage.

Quote
substract bias
The "pure" PCM data would have the bias subtracted-out so silence is zero and there are negative values for the negative half of the waveform.   However, the WAV spec says 8-bit WAV files use unsigned bytes.    So, it also must be biased.   But since it's 8-bits instead of 10, the WAV file needs to be biased at 128 instead of 512.

If the input data is biased, and you simply divide by 4 the WAV data will also be properly biased.

If you are going to do any digital signal processing (such as digitally adjusting the volume or mixing two sounds, etc.) you need non-biased data with positive and  negative numbers.   Any software that "reads" WAV files should automatically handle signed 8-bit files as well as unsigned 16 or 24-bit files.   For example, Audacity knows that 8-bit WAV files are biased and 16 and 24-bit files are not and the user doesn't normally have to know or care as long as the files are formatted properly.

 








memel182

@Grumpy_Mike : concerning sound recognition, I know that the Arduino can't do this. This is why I just want to use it to record the audio signal that will be sent to PC for processing. After that, I understood that I have to check for another kind of mic, I will look for that. Do you have any hints or ideas of what will fit my needs?

@DVDdoug : that you so much for taking the time to describe this in details!! I realized that I have still a lot to learn! It really makes sense but I did not know where to start from.  :smiley-lol:

So, I am going back to my researches!

Thank you guys

Grumpy_Mike

Do you also know that the arduino only has enough memory to store a fraction of a seconds worth of sound? And that the arduino's normal sample rate for analogue signals is 10KHz?

memel182

#11
Mar 06, 2015, 09:03 am Last Edit: Mar 06, 2015, 09:03 am by memel182
Do you also know that the arduino only has enough memory to store a fraction of a seconds worth of sound? And that the arduino's normal sample rate for analogue signals is 10KHz?
Yes, this is why I am writing a WAV file on SD card, helped by double buffering.

Do you also know that the arduino only has enough memory to store a fraction of a seconds worth of sound? And that the arduino's normal sample rate for analogue signals is 10KHz?
I don't really understand this... With my registers config, I reached 37KHz (calculated on number of records / time) : the produced WAV file weights 250Ko for about 3 sec recording.

memel182

#12
Mar 06, 2015, 10:46 am Last Edit: Mar 06, 2015, 10:47 am by memel182
Do I rather need this kind of circuit?

http://www.adafruit.com/product/1063

el_supremo

Quote
produced WAV file weights 250Ko for about 3 sec recording.
But if recording 3 seconds at 37kHz produces "only noise", it's not much use it?

Pete

Grumpy_Mike

Do I rather need this kind of circuit?

http://www.adafruit.com/product/1063
Yes

Go Up
 


Please enter a valid email to subscribe

Confirm your email address

We need to confirm your email address.
To complete the subscription, please click the link in the email we just sent you.

Thank you for subscribing!

Arduino
via Egeo 16
Torino, 10131
Italy