Creating a .wav file from PCM data - c

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

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

Related

Quantization noise during writing wav file using C

I'm just trying to write a library for read and write Wav files (just need it for audio processing), just as test, I read samples from a Wave File convert them to double (just standardize them to -1 ~ 1), and do nothing but transform them back to integer, according to the bit per sample (assume the Wav file have N bits per sample, I divided them through 2^(N-1)-1 and multiply with the same factor after to restore it)
But the problem is, I get a wav file with background noise (id say it seems like quantisization noise) and I don't know why, can you help me find it out?
the library is here: https://pastebin.com/mz5TWMPN
the header file is: https://pastebin.com/Lr2tbmnv
and a demo main function is like:
#include <stdio.h>
#include <math.h>
#include "wavreader.h"
#define FRAMESIZE 512
int main()
{
FILE *fh;
FILE *fhWrite;
struct WavHeader * header;
struct WavHeader * newHeader;
double frame[FRAMESIZE];
int iBytesWritten;
int i;
char test;
fh = fopen("D:/ArbeitsOrdner/advanced_pacev/AudioSample/spfg.wav", "rb+");
if (fh == NULL)
{
printf("Failed to open organ.wav\n");
return 1;
}
fhWrite = fopen("D:/ArbeitsOrdner/MyC/test_organ.wav", "wb+");
if (fhWrite == NULL)
{
printf("Failed to create test_organ.wav\n");
return 1;
}
header = readWaveHeader(fh);
printWaveHeader(header);
newHeader = createWaveHeader(header->iChannels, header->iSampleRate, header->iBitsPerSample);
WaveWriteHeader(fhWrite, newHeader);
while (WaveReadFrame(fh, header, FRAMESIZE, frame) != -1)
{
iBytesWritten = WaveWriteFrame(fhWrite, newHeader, FRAMESIZE, frame);
if (iBytesWritten < 0)
{
printf("Error occured while writing to new file\n");
return 1;
}
}
WaveWriteHeader(fhWrite, newHeader);
fclose(fhWrite);
fclose(fh);
return 0;
}
thx for viewing this post. I have found the problem myself, it is that, i used char instead of unsigned char for raw data (raw bytes). By converting them to int16 or int32, i haven't considered the sign bit. that means they are not exact the same value during convertion as it except to be.
the solution for this is:
either stay with signed char and use:
buffer[i] & 0xff
to get the correct raw data for convertion, or change the char types into unsigned char:
unsgiend char * buffer;

Why is my bitmap drawing function plotting at an offset from the position I give it? (C VGA Mode 12h)

Hello I´m working on creating a bitmap drawing function on C using VGA in mode 12h using DOSBOX to run the program. I´m getting the image on the screen, but the start of the image is being drawn on the middle on the screen instead of (0,0). Can anyone tell me why I´m getting this behavior?
My plot_pixel function works fine. I´m able to draw lines and plot pixels on the screen without getting the weird offset I´m getting now.
This shows the problem.
Original Image:
Result:
And this is my code:
Load BMP:
/**************************************************************************
* load_bmp *
* Loads a bitmap file into memory. *
**************************************************************************/
void load_bmp(char *file, BITMAP *b){
FILE *fp;
long index;
byte a;
word num_colors;
int x;
//SetGfxMode(0x3);
/*Opening file */
if((fp = fopen(file,"rb")) == NULL){
printf("Error al abrir el archivo %s.\n",file);
exit(1);
}
/*Validating if the image is a valid bitmap*/
if(fgetc(fp) != 'B' || fgetc(fp) != 'M'){
fclose(fp);
printf("%s is not a bitmap file. \n", file);
exit(1);
}
/*Height and width of the image
*/
fskip(fp,16);
fread(&b->width, sizeof(word),1 , fp);
fskip(fp,2);
fread(&b->height, sizeof(word),1,fp);
fskip(fp,22);
fread(&num_colors,sizeof(word),1,fp);
fskip(fp,6);
/* We are loading a 16 color image */
if(num_colors ==0) num_colors = 16;
/*Intentamos alojar memoria para la data del bitmap*/
if((b->data = (byte *) malloc((b->width*b->height))) == NULL)
{
fclose(fp);
printf("Error allocating memory for file %s.\n",file);
exit(1);
}
/*Reading pallete info*/
for(index=0;index<num_colors;index++){
b->pallete[(int)(index*3+2)] = fgetc(fp) >> 2;
b->pallete[(int)(index*3+1)] = fgetc(fp) >> 2;
b->pallete[(int)(index*3+0)] = fgetc(fp) >> 2;
//fskip(fp,240);
x = fgetc(fp);
}
/* Leyendo el bitmap*/
for(index=(b->height-1)*b->width;index>=0;index-=b->width){
for(x=0;x<b->width;x++){
b->data[index+x]=(byte)fgetc(fp);
}
}
fclose(fp);
}
Draw bitmap:
/**************************************************************************
* draw_transparent_bitmap *
* Draws a transparent bitmap. *
**************************************************************************/
void draw_transparent_bitmap(BITMAP *bmp,int x,int y)
{
int i,j;
unsigned long bitmap_offset = 0;
byte data;
copyMemory(double_buffer,VGA);
printf("sum");
getch();
for(j=0;j<bmp->height;j++)
{
for(i=0;i<bmp->width;i++)
{
data = bmp->data[bitmap_offset];
//if (data) double_buffer[screen_offset+x+i] = data;
if(data) plot_pixel(x+i,y+j,data);
bitmap_offset++;
}
}
}
Set Pallete
void set_pallete(byte *pallete){
int i;
outp(PALETTE_INDEX,0);
for(i=0;i<16*3;i++){
outp(PALETTE_DATA,pallete[i]);
}
}
Main:
typedef struct
{
word width;
word height;
byte pallete[256*3];
byte *data;
} BITMAP;
BITMAP fondo_inicio;
load_bmp("home16.bmp",&fondo_inicio);
set_pallete(fondo_inicio.pallete);
draw_transparent_bitmap(&fondo_inicio,0,0);
I'm not persuaded you're loading the BMP correctly. Per Wikipedia, which hopefully managed to get this right as a rare all-but-objective fact, your code, after you've checked for 'BM', assuming fskip is some sort of spin on fseek, takes these steps:
skip the 4 bytes telling you BMP size;
skip the 4 reserved bytes;
skip the 4 bytes telling you where you should load pixel data from (which you really should consume and obey);
assume you're getting a Windows 3.1 secondary header and skip the 4 bytes tell you its length (you shouldn't);
read the lower two bytes of width;
skip the upper two bytes of width;
read the lower two bytes of height;
skip the upper two bytes of height;
skip: number of colour planes (+ 2 bytes), bits per pixel (+ 2 bytes), compression method (+ 4 = 10), image size (+ 4 = 14), horizontal density (+ 4 = 18), vertical density (+4 = 22);
read first two bytes of colour palette size;
skip next two bytes of colour palette size;
skip number of important colours;
assume the headers have then ended (but you should instead have read the header size and skipped appropriate here);
reads an RGBA palette, assuming it knows the image to be 16-colour, discarding the alpha and mapping from 8 bits-per-channel to VGA-style 6 bits;
assume the image data comes straight after the palette (you shouldn't, you should have read its file offset earlier);
read one byte per pixel of image data. Even though you've assumed 4 bits per pixel for reading the palette.
Likely your BMP file isn't 4-bit if reading a whole byte per pixel is providing the correct width of image. That means your assumptions about header size are definitely wrong. Almost certainly what you have stored as the image data is a chunk of header and then the image. Start by not skipping the header entry that tells you where image data begins — read it and use it. Otherwise if your plot_pixel automatically maps eight bits to four then it's not a big problem if you're loading a 256-colour image and assuming that only the lowest sixteen colours are used, assuming that holds true of your source imagery and storage space isn't a concern.

Encoding a wav file using G711 encoding

I am trying to encode a PCM uncompressed Wav file using A law encoding.
I have written a function which takes in the 16 bit PCM data and returns 8 bit encoded data..After encoding, my file does not play properly..I feel that there is something I am not doing correctly to handle the files.I have separated the header information of the file and written the same header to output file.
// Code for compressing data is below
short inbuff;
unsigned char outbuff;
while (!feof(inp))
{
fread(inbuff, 2 , BUFSIZE, inp);
for (i=0; i < BUFSIZE; ++i)
{
temp_16 = inbuff[i];
temp_8 = Lin2Alaw(temp_16);
outbuff[i] = temp_8;
}
fwrite(outbuff, 1 , (BUFSIZE), out);
}
You are writing the data with the same header, which means that any audio program will think the data inside the WAV file is still PCM. Check the file format for WAV and change it accordingly.
Mainly you need to change audio format at 0x0014-0x0015 to a-law and other values also to mark the proper bytes per second, block size etc.
Easiest way to make sure they're correct might be to convert the file with an audio editor and then checking for the differences in the values.
How did your code even compile when you are not using arrays? Even so, your use of feof isn't good, please see Why is “while ( !feof (file) )” always wrong?
#include <stdio.h>
#define BUFSIZE 512
int main(void) {
short inbuff[BUFSIZE]; // must be an array
unsigned char outbuff[BUFSIZE]; // must be an array
size_t bread, i;
unsigned char temp_8;
short temp_16;
FILE *inp, *out;
// ... open the file
// ... transcribe the header
// rewrite the data
while ((bread = fread(inbuff, 2 , BUFSIZE, inp)) > 0)
{
for (i=0; i < bread; ++i) // only the data actually read
{
temp_16 = inbuff[i];
temp_8 = Lin2Alaw(temp_16);
outbuff[i] = temp_8;
}
fwrite(outbuff, 1 , bread, out); // only the data actually read
}
// ... finish off and close the file
return 0;
}
I notice too you are using signed short for the 16-bit data - should that be unsigned short?
See the format of wave file is at
http://www.topherlee.com/software/pcm-tut-wavformat.html
Now check all bytes of header and make sure all information about bit rate,sample rate etc are correct.
If your code for compressing is correct then issue should be with header file only.

Writing multichannel audio for MATLAB with libsndfile

I am trying to use libsndfile to write a multichannel wav that can be read by MATLAB 2010+.
the following code writes a 4 channel interleaved wav. all samples on channel 1 should be 0.1, on channel 2 they are 0.2, on channel 3 ... etc.
Each channel is 44100 samples in length.
I drag the wave file onto the MATLAB workspace and unfortunately MATLAB keeps returning "File contains uninterpretable data".
It may also be worth noting that when all samples are set to 0.0, MATLAB successfully reads the file, although very slowly.
I have successfully used libsndfile to read multichannel data written by MATLAB's wavwrite.m, so the library is setup up correctly I believe.
Audacity can read the resulting file from the code below.
VS 2012 64 bit compiler,
Win7 64bit, MATLAB 2015a
ref: the code has been adapted from http://www.labbookpages.co.uk/audio/wavFiles.html
Any suggestions, I presume i'm making a simple error here?
Thanks
#include <sndfile.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
// Create interleaved audio data
int numFrames_out = 44100;
int channels = 4;
float *int_y;
int_y = (float*)malloc(channels*numFrames_out*sizeof(float));
long q=0;
for (long i = 0; i<numFrames_out; i++)
{
for (int j = 0; j<channels; j++)
{
int_y[q+j] = ((float)(j+1))/10.0;
}
q+=channels;
}
// Set multichannel file settings
SF_INFO info;
info.format = SF_FORMAT_WAV | SF_FORMAT_PCM_32;
info.channels = channels;
info.samplerate = 44100;
// Open sound file for writing
char out_filename[] = "out_audio.wav";
SNDFILE *sndFile = sf_open(out_filename, SFM_WRITE, &info);
if (sndFile == NULL)
{
fprintf(stderr, "Error opening sound file '%s': %s\n", out_filename, sf_strerror(sndFile));
return -1;
}
// Write frames
long writtenFrames = sf_writef_float(sndFile, int_y, numFrames_out);
// Check correct number of frames saved
if (writtenFrames != numFrames_out) {
fprintf(stderr, "Did not write enough frames for source\n");
sf_close(sndFile);
free(int_y);
return -1;
}
sf_close (sndFile);
}
It looks like you are only closing the output file (using sf_close()) in the error case. The output file will not be a well formed WAV file unless you call sf_close() at the end of your program.

Uncompress a gzip File in Memory Using zlib Version 1.1.3

I have a gzip file that is in memory, and I would like to uncompress it using zlib, version 1.1.3. Uncompress() is returning -3, Z_DATA_ERROR, indicating the source data is corrupt.
I know that my in memory buffer is correct - if I write the buffer out to a file, it is the same as my source gzip file.
The gzip file format indicates that there is a 10 byte header, optional headers, the data, and a footer. Is it possible to determine where the data starts, and strip that portion out? I performed a search on this topic, and a couple people have suggested using inflateInit2(). However, in my version of zlib, that function is oddly commented out. Is there any other options?
I came across the same problem, other zlib version (1.2.7)
I don't know why inflateInit2() is commented out.
Without calling inflateInit2 you can do the following:
err = inflateInit(&d_stream);
err = inflateReset2(&d_stream, 31);
the inflateReset2 is also called by inflateInit. Inside of inflateInit the WindowBits are set to 15 (1111 binary). But you have to set them to 31 (11111) to get gzip working.
The reason is here:
inside of inflateReset2 the following is done:
wrap = (windowBits >> 4) + 1;
which leads to 1 if window bits are set 15 (1111 binary) and to 2 if window bits are set 31 (11111)
Now if you call inflate() the following line in the HEAD state checks the state->wrap value along with the magic number for gzip
if ((state->wrap & 2) && hold == 0x8b1f) { /* gzip header */
So with the following code I was able to do in-memory gzip decompression:
(Note: this code presumes that the complete data to be decompressed is in memory and that the buffer for decompressed data is large enough)
int err;
z_stream d_stream; // decompression stream
d_stream.zalloc = (alloc_func)0;
d_stream.zfree = (free_func)0;
d_stream.opaque = (voidpf)0;
d_stream.next_in = deflated; // where deflated is a pointer the the compressed data buffer
d_stream.avail_in = deflatedLen; // where deflatedLen is the length of the compressed data
d_stream.next_out = inflated; // where inflated is a pointer to the resulting uncompressed data buffer
d_stream.avail_out = inflatedLen; // where inflatedLen is the size of the uncompressed data buffer
err = inflateInit(&d_stream);
err = inflateReset2(&d_stream, 31);
err = inflateEnd(&d_stream);
Just commenting in inflateInit2() is the oder solution. Here you can set WindowBits directly
Is it possible to determine where the data starts, and strip that portion out?
Gzip has the following magic number:
static const unsigned char gzipMagicBytes[] = { 0x1f, 0x8b, 0x08, 0x00 };
You can read through a file stream and look for these bytes:
static const int testElemSize = sizeof(unsigned char);
static const int testElemCount = sizeof(gzipMagicBytes);
const char *fn = "foo.bar";
FILE *fp = fopen(fn, "rbR");
char testMagicBuffer[testElemCount] = {0};
unsigned long long testMagicOffset = 0ULL;
if (fp != NULL) {
do {
if (memcmp(testMagicBuffer, gzipMagicBytes, sizeof(gzipMagicBytes)) == 0) {
/* we found gzip magic bytes, do stuff here... */
fprintf(stdout, "gzip stream found at byte offset: %llu\n", testMagicOffset);
break;
}
testMagicOffset += testElemSize * testElemCount;
fseek(fp, testMagicOffset - testElemCount + 1, SEEK_SET);
testMagicOffset -= testElemCount + 1;
} while (fread(testMagicBuffer, testElemSize, testElemCount, fp));
}
fclose(fp);
Once you have the offset, you could do copy and paste operations, or overwrite other bytes, etc.

Resources