1

I am trying to read the data from SPH0645LM4H-B https://www.knowles.com/docs/default-source/model-downloads/sph0645lm4h-b-datasheet-rev-c.pdf?sfvrsn=4 and convert it to .wav format, so that I can verify whether my sensor is working correctly. So far, I have managed to interface the sensor and read data samples via i2s-dma transfer.

Also, I found that most mainstream computer soundfile formats consist of a header (containing the length, etc.) followed by 16-bit two's-complement PCM https://www.dsprelated.com/freebooks/mdft/Number_Systems_Digital_Audio.html

For converting the PCM data to wave format, I have referred converting PCM to wav file

Now, the sensor data format is I2S, 24-bit, 2’s compliment, MSB first. The data precision is 18 bits; unused bits are zeros. I have converted the sensor data to 32-bit (MSB is the sign bit)

I have used the code from the following link (and the links mentioned in it) to create the .wav file:- Append .pcm audio raw data into wav (in C)

//.wav file header data

struct wavfile
{
char            id[4];          // should always contain "RIFF"
int             totallength;    // total file length minus 8
char            wavefmt[8];     // should be "WAVEfmt "
int             format;         // 16 for PCM format
short           pcm;            // WAVE_FORMAT_IEEE_FLOAT (3).
short           channels;       // channels
int             frequency;      // sampling frequency, 16000 in this case
int             bytes_per_second;
short           bytes_by_capture;
short           bits_per_sample;
char            data[4];        // should always contain "data"
int             bytes_in_data;
};

//Writing the header to output .wav file

void write_wav_header(char* name, int samples, int channels)
{
struct wavfile filler;
FILE *pFile;
strcpy(filler.id, "RIFF");
filler.totallength = (samples * channels) + sizeof(struct wavfile) - 8;
strcpy(filler.wavefmt, "WAVEfmt ");
filler.format = 16;
filler.pcm = 3; // WAVE_FORMAT_IEEE_FLOAT (3).
filler.channels = channels;
filler.frequency = 32000;
filler.bits_per_sample = 32;
filler.bytes_per_second = filler.channels * filler.frequency * filler.bits_per_sample/8;
filler.bytes_by_capture = filler.channels*filler.bits_per_sample/8;
filler.bytes_in_data = samples * filler.channels * filler.bits_per_sample/8;
strcpy(filler.data, "data");
pFile = fopen(name, "wb");
fwrite(&filler, 1, sizeof(filler), pFile);
fclose(pFile);
}

//Appending the audio sensor data to this .wav file

void write_pcm_data_to_file(char* inFile, char* outFile)
{
char buffer[SAMPLE_SIZE];
size_t n;
FILE *fin,*fout;
fin = fopen(inFile,"r");
fout = fopen(outFile,"a");
while((n = fread(buffer, 1, sizeof(buffer), fin)) > 0)
{
    if(n != fwrite(buffer, 1, n, fout))
    {
        perror("fwrite");
        exit(1);
    }
}
fclose(fin);
fclose(fout);
}

This is how the resulting .wav file looks in hex editor:- .wav file in hex editor I can see that the wav header values are set correctly, followed by my PCM data from Audio Sensor.

However, when I play the .wav file, I am not able to play the input audio. Instead, I hear a constant tone. I tried changing the input audio (played different music, songs), yet the resulting .wav file plays the same constant tone.

In order to recreate the input music played to the microphone into a wav file, what modifications should I do?

Thanks in advance.

Edit:-

According to 42LeapsOfFaith 's answer, to convert my samples to hex, I used the following code:-

hexValue = strtoll(sample, NULL, 16);

I converted each value in the buffer, then wrote it into my .wav file. Now my .wav file looks like modified .wav file in hex editor

However, even this wav file does not play the audio. Any further suggestions to recreate the input music played to the microphone into a wav file?

Help is very much appreciated

Sandrocottus
  • 521
  • 3
  • 8
  • 31

1 Answers1

2

For starters, you are storing the PCM data in the file as ascii-hex. I hope that helps.

This is a 'hack' you can use...

char c[2];
while((n = fread(&c[0], 2, 1, fin)) > 0)
 { 
    if (c[0] > 0x39) c[0] -= 7;
    c[0] &= 0x0F;
    if (c[1] > 0x39) c[1] -= 7;
    c[1] &= 0x0F;
    c[1] |= c[0] << 4;
    if(1 != fwrite(&c[1], 1, 1, fout))
42LeapsOfFaith
  • 146
  • 1
  • 9
  • Hi @42LeapsOfFaith. I have been playing a lot with my data, trying to convert it (strtoll(), atoi() etc.). However, I am still not able to get what you meant. Could you please elaborate a bit on how should I modify the data before copying it to to my .wav file? – Sandrocottus Nov 27 '18 at 15:47
  • The answer is in your ".wav file in hex editor" image. The data from your infile is in the wrong format - it is ascii-hex format (string). You need to convert it back to hex (int) before you use it (i.e. "A5" > 0xA5). Or, store it in the infile with the correct format first... I hope this helps. – 42LeapsOfFaith Nov 28 '18 at 00:56
  • char c[1]; while((n = fread(&c[0], 2, 1, fin)) > 0) { if (c[0] > 0x39) c[0] -= 7; c[0] &= 0x0F; if (c[1] > 0x39) c[1] -= 7; c[1] &= 0x0F; c[0] |= c[1] << 4; if(n != fwrite(&c[0], 1, 1, fout)) – 42LeapsOfFaith Nov 28 '18 at 10:53
  • Why is "filler.pcm = 3; // WAVE_FORMAT_IEEE_FLOAT (3)." when you are are writing INT. Your number of channels, and number of bits are unclear... Do you know the endian'ness of your data? – 42LeapsOfFaith Nov 30 '18 at 03:08