Writing multichannel audio for MATLAB with libsndfile - c

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.

Related

How can I read a large set of data from a file into either a pointer to a structure or array of a structure in C

I have a data file with a known key, that is, it has many entries (devices) with the same properties and I have this structure in code to capture it.
struct deviceData{
int id;
char serial[10];
float temperature;
float speed;
long timestamp;
}
struct deviceData fileItems;
It's 4 bytes for the ID, 10 bytes for the serial code, 4 bytes for both the temperature and speed and 8 bytes for the timestamp. 30 bytes in total.
What I would like to achieve is to be able to read all those entries and run a calculation in the quickest way I can.
What I initially thought of doing was to simply create a giant array to capture all the entries but that causes errors.
Secondly I thought of allocating space from a pointer to that structure and reading the whole file to that. That worked in execution but I had trouble processing the data. Possibly a gap in fundamentals on my part.
The way I'm currently looking at is to loop through readings where I capture a single entry using fread(), process that and then move the file to put the next entry into the buffer.
Something like this:
fread(&fileItems, 30, 1, filename)
What happens though is that when I view what actually gets read I see that the ID and the serial code were read correctly but the following data points are garbage. Reading a little bit about it I came across something about padding which I don't fully understand but the fix seems to be to make my char array 100 which seems to work for the first entry but I suspect it's causing problems with subsequent readings because it's throwing my calculations off.
I'm kind of at a wall here because every strategy I try seems to have something that works strangely. If I could at least be pointed in the right direction I'll at least know I'm putting effort in the right thing.
If you are just wanting to process a group of data record by record, you probably can utilize the methodology of defining a structure, then reading the data from a file into the structure, and then processing the data. To make things consistent, it would make sense to store the data as a structure in a binary file. Following is a code snippet creating some sample data and then processing it following the spirit of your project.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct deviceData{
int id;
char serial[10];
float temperature;
float speed;
long timestamp;
};
struct deviceData fileItems;
void create_data(char * file_name)
{
FILE* fp = fopen(file_name, "wb");
if(!fp)
{
printf("\n\tFile open error\n");
return;
}
for (int i = 0; i < 10; i++)
{
fileItems.id = i + 20000 + i * 3;
sprintf(fileItems.serial, "SN%d", fileItems.id);
fileItems.temperature = 166.0 + i;
fileItems.speed = 2400.0;
fileItems.timestamp = 20220830;
fwrite(&fileItems, sizeof(struct deviceData), 1, fp);
}
fclose(fp);
}
void read_data(char *file_name)
{
FILE* fp = fopen(file_name, "rb");
if(!fp)
{
printf("\n\tFile open error\n");
return;
}
while(fread(&fileItems, sizeof(struct deviceData), 1, fp))
{
printf("ID. . . . . . .: %d\n", fileItems.id);
printf("Serial number. .: %s\n", fileItems.serial);
printf("Temparature. . .: %f\n", fileItems.temperature);
printf("Speed. . . . . .: %f\n", fileItems.speed);
printf("Timestamp. . . .: %ld\n", fileItems.timestamp);
}
}
int main()
{
create_data("device.dat"); /* Create some sample data to be read */
read_data("device.dat"); /* Read and print the data to the terminal */
return 0;
}
The storage of device data probably would occur in some other monitor program, but for this snippet a one-off function was included to produce some data to process.
Analyze that code and see if it meets the spirit of your project.

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;

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.

reading a raw audio file as Matlab does in C

I have the following small script that I want to write in C :
`%% getting the spectgrum
clear, clc ;
fileName ='M0.raw'
[x,fs] = audioread(fileName);
[xPSD,f] = pwelch(x,hanning(8192),0,8192*4 ,fs);
plot(f,10*log10(abs(xPSD)));
xlim([0 22e3]);
absxPSD = abs(xPSD);
save('absXPSD.txt','absxPSD','-ascii');
save('xPSD.txt','xPSD','-ascii');
save('xValues.txt','x','-ascii');
save('frequency.txt','f','-ascii');`
without goning in details, I have a problem getting the correct result, when I checked I figured out that the data that I read is wrong here's sample that read the raw file to compare it with what Matalb reads :
#include <stdio.h>
#include <stdlib.h>
int main (){
FILE* inp =NULL;
FILE* oup =NULL;
double value =0;
inp = fopen("M0.raw","r");
oup = fopen("checks.txt","w+");
UPDATE
after LoPiTaL's answer I've tried to jump over the RIFF header which is 44Byte length0 using fseek
fseek (inp,352,SEEK_SET);// that didn't help getting the right result !!
if( inp == NULL || oup==NULL){
printf(" error at file opning \n");
return -1;
}
while (!(feof(inp))){
fread(&value,sizeof(double),1,inp);
printf(" %f \n ",value);
fprintf(oup,"%f\n",value);
}
fclose(inp);
fclose(oup);
return 0;
}
and the result that I get is :
-28083683309813134333858080554409220100578902032859386180468433149049781495379346137536863936326139303879846829175766826833343673613788446579155215033623707200818670767132304934425064429529496303287641688697019947073799877821581901737052884168025721481955133510652655692037990001524306465271815108431928360960.000000
0.000000
0.000000
0.000000
0.000000
0.000000
-20701636078248669570005757343846586744027511881225108933223144646890577802102653022204406730988428912367583701134782419138464527797567258583836429190479797597328678189654150340845........................................................................
and my aim is to get those value :
-1.0162354e-02
-9.3688965e-03
-7.5073242e-03
-1.9531250e-03
3.7231445e-03
1.3549805e-02
2.3223877e-02
3.2867432e-02
4.4830322e-02
5.5114746e-02
6.7291260e-02
7.7636719e-02
8.8562012e-02
9.5794678e-02
1.0055542e-01
1.0415649e-01
1.0351563e-01
1.0235596e-01
9.8785400e-02
9.1796875e-02
8.3648682e-02
7.1594238e-02
the audio file is mono an is 16bit resolution , any idea how can solve this ? thanks for any help
You must open the file in binary mode, for starters. Otherwise you get text mode, which can do translations of line endings, for instance. Not good with binary data.
Binary mode:
inp = fopen("M0.raw", "rb");
^
|
muy
importante
Sure, you cannot read an audio file as is and hope that the data is as you think it is.
Ignoring any coded audio file, which of course you have to decode prior to read it, lets focus in the RAW audio files:
RAW audio files usually are WAV files. WAV files have a .RIFF header at the beginning of the file, which obviously you would have to ignore before reading audio data.
http://en.wikipedia.org/wiki/Resource_Interchange_File_Format
After you have removed the RIFF header, then the data starts.
As you stated, the data is encoded as 16 bit resolution. 16 bit resolution means that 0x0000 is 0.0 and 0xFFFF is 1.0, and the size of the data is only two bytes!
So you have to read two bytes at a time (i.e. with a signed short) and then do the conversion to the range 0 to 1:
signed short ss;
double value;
FILE* inp =NULL;
inp = fopen("M0.raw","rb"); //As stated in other answer, use binary mode!
fseek (inp,44,SEEK_SET); // Only 44 bytes!!
//We already have discarded the header here....
while (fread(&ss, sizeof(signed short) ,1 , inp) == 1){
//Now we have to convert from signed short to double:
value=((double)ss)/(unsigned)0xFFFF;
//Print the results:
printf(" %f \n ",value);
fprintf(oup,"%f\n",value);
}
Of course, the function "audioread" from Matlab already does all of this for you, so you don't have to care about the encoding, as in your example, your particular data is in 16 bit, but if you use any other file, it could be 8, 16, 24 or 32, even could be differential or be encoded despite being a WAV file (see the RIFF header for more information).

Audio samplerate converter using libsndfile and libsamplerate. Not sure if using function src_simple correctly

I have been building a simple samplerate converter in c using libsndfile and libsamplerate. I just cant seem to get the src_simple function of libsamplerate to work, whatever I try. I have striped back my code to be as simple as possible and it now just outputs a silent audio file of identical sampling rate:
#include <stdio.h>
#include <sndfile.h>
#include <samplerate.h>
#define BUFFER_LEN 1024
#define MAX_CHANNELS 6
int main ()
{
static double datain [BUFFER_LEN];
static double dataout [BUFFER_LEN];
SNDFILE *infile, *outfile;
SF_INFO sfinfo, sfinfo2 ;
int readcount ;
const char *infilename = "C:/Users/Oli/Desktop/MARTYTHM.wav" ;
const char *outfilename = "C:/Users/Oli/Desktop/Done.wav" ;
SRC_DATA src_data;
infile = sf_open (infilename, SFM_READ, &sfinfo);
outfile = sf_open (outfilename, SFM_WRITE, &sfinfo);
src_data.data_in = datain
src_data.input_frames = BUFFER_LEN;
src_data.data_out = dataout;
src_data.output_frames = BUFFER_LEN;
src_data.src_ratio = 0.5;
src_simple (&src_data, SRC_SINC_BEST_QUALITY, 1);
while ((readcount = sf_read_double (infile, datain, BUFFER_LEN)))
{
src_simple (&src_data, SRC_SINC_BEST_QUALITY, 1);
sf_write_double (outfile, dataout, readcount) ;
};
sf_close (infile);
sf_close (outfile);
sf_open ("C:/Users/Oli/Desktop/Done.wav", SFM_READ, &sfinfo2);
printf("%d", sfinfo2.samplerate);
return 0;
}
It's really starting to stress me out. The program is a uni project and is due very soon, it is making me very anxious as whatever I try seems to result in failure. Can anyone please help me?
I'm not an expert on this particular library, but just from looking at the online documentation I see a few problems with your code:
src_simple apparently works with floats, yet your buffers are doubles - I think you need to change the buffers to float and use sf_read_float/sf_write_float for I/O.
src_simple is the "simple" interface and is intended to be applied to an entire waveform in one call, not in chunks as you are doing - see http://www.mega-nerd.com/SRC/faq.html#Q004 - you should first get the input file size, then allocate sufficient memory, read in the whole file, convert it in one go, then write the converted output data to your output file.
when changing sample rate you will get a different number of samples in the output file than in the output file (around half as many in for case), yet you're writing the same number of samples that you read (readcount). You should probably be using src_data.output_frames_gen as the number of frames to write, not readcount.

Resources