How to Read and Write Wav Files

DSP, Plugin and Host development discussion.
RELATED
PRODUCTS

Post

after reading my code (when posting the first time) i decided it should be cleaned up, and i've done that now. anybody who is interested can take another look

http://xhip.cjb.net/temp/public/riff.cpp
http://xhip.cjb.net/temp/public/riff.h

if you do not need the ability to load loop points or the sample name, or the ability to save files you need only a few of those functions and structs, and can remove the rest of the code. in that case it would be simple enough to post here directly as a single c file, however i've left everything in in case somebody does want the loop/name/save stuff.

you can clean up the code more if you implement the file i/o functions somewhere else in your code - here i've inlined them. the 'pcm' class is also unnecessary if you want to load the data into some other object.

Post

Dear friend, Where can find C program to split wav files?.I use Linux(RED HAT).

Post

are you an indian student?

i suspect you are supposed to learn to do this on your own. if my position is going to be outsourced to you, you'd better at least be able to write your own code. bad codemonkey, bad.

Post

aciddose wrote:after reading my code (when posting the first time) i decided it should be cleaned up, and i've done that now. anybody who is interested can take another look

http://xhip.cjb.net/temp/public/riff.cpp
http://xhip.cjb.net/temp/public/riff.h
Hi acidose,

This code is actually quite similar to what I came up with (I'm however NOT a student but mostly a web programmer and of course a musician)...
I'm having trouble reading wave files and OUTPUTTING the sample data in a VSTi context.
I am just trying to make a VERY simple wave sample player.

The thing is that when I play back the samples in the processReplacing method then something is really wrong.
So a very simple example of how to write a stereo wave file to the **outputs pointers as a VST example would be very appreciated.
Right now (REALLY scaled down version with no midi and time handling - so this code is complete with errors and everything with just ripped names from your code and without complete context.
I'm doing something similar to the following:

Create an array of sampleloaders (in your code it is objects of the ADRIFF::PCM class) - for simplicity it could just be one instance of the ADRIFF:PCM class with a loaded file.

ADRIFF:PCM mypcm; // lets call it mypcm
long currentSample;

The processReplacing signature (VST2.4):
processReplacing (float **inputs, float **outputs, VstInt32 sampleFrames) {
float* out1 = outputs[0];
float* out2 = outputs[1];

// for ultra simplicity just write the next sample on the left and right output channels, with no bounds checking etc...
(*out1++) = pcm->data[currentSample]; // first left channel
(*out2++) = pcm->data[currentSample++]; // then right channel
currentSample++; // just counting for next run

---- NOTE ----
It does NOT work like I expected, hmmm, I just realized that your code uses a char* pointer and mine is a long* pointer... it just might be something like that, which is causing problems - not sure why though but any insight is appreciated.
Back to hacking away :P

Sincerely,
Dennis P

Post

the joke with my question about being a student was directed to "knrnjn", who joined kvr specifically just to ask for instructions on doing his homework.

well you'll need to convert the sample data to floats first. my code simply loads the data, as bytes, into a block of memory. you must take the information in the object and learn which format that data is presented in. you might want to add a "int compression" member to the object if you want to load compressed/encoded files.

generally, you can expect that if the bits = 8, you can take the sample data as unsigned char and do:

while (samples--) { floatdata[sample] = ((((unsigned char *)pcm.data)[sample]) - 0x80) / 128.0f; sample++; }

for bits = 16, the data is signed short:

while (samples--) { floatdata[sample] = ((signed short *)pcm.data)[sample] / 32768.0f; sample++; }

for bits = 24, the data is probably signed long, but this format isnt common so you shouldn't worry about this case.

for bits = 32, the data is going to be float:

while (samples--) { floatdata[sample] = ((float *)pcm.data)[sample]; sample++; }

by type-casting the char pointer, our [sample] offset automatically gets scaled to [sample * sizeof(datatype)].

after you've converted the sample data into floats, your code above will work fine.

Post

i should clarify this:

the pcm object is designed as a temporary format for loading/decoding the data only. it isnt designed to be used and shouldnt be designed in that way. you should have a seperate object designed specifically to be used for playback. you can consider the three formats riff/wave, pcm and playback to be "storage", "transit" and "functional" objects.

Post

In addition to the conversion to floating point, your code contains a serious error -
assuming the output has 2 channels,

Code: Select all

// for ultra simplicity just write the next sample on the left and right output channels, with no bounds checking etc...
(*out1++) = pcm->data[currentSample]; // first left channel
(*out2++) = pcm->data[currentSample++]; // then right channel
currentSample++; // just counting for next run
should read

Code: Select all

(*out1++) = pcm->data[currentSample++]; // first left channel
(*out2++) = pcm->data[currentSample++]; // then right channel
if the input .wav file is stereo or

Code: Select all

(*out1++) = pcm->data[currentSample]; // first left channel
(*out2++) = pcm->data[currentSample++]; // then right channel
(without the superfluous increment) if the input file is mono.
"Until you spread your wings, you'll have no idea how far you can walk." Image

Post

arakula wrote:In addition to the conversion to floating point, your code contains a serious error -
assuming the output has 2 channels,

Code: Select all

// for ultra simplicity just write the next sample on the left and right output channels, with no bounds checking etc...
(*out1++) = pcm->data[currentSample]; // first left channel
(*out2++) = pcm->data[currentSample++]; // then right channel
currentSample++; // just counting for next run
should read

Code: Select all

(*out1++) = pcm->data[currentSample++]; // first left channel
(*out2++) = pcm->data[currentSample++]; // then right channel
if the input .wav file is stereo or

Code: Select all

(*out1++) = pcm->data[currentSample]; // first left channel
(*out2++) = pcm->data[currentSample++]; // then right channel
(without the superfluous increment) if the input file is mono.
Ah - of course - you need to enter the pointer array first, so of course you need to increment first...

However the mono example is wrong isn't it?
Now that I am that wiser I would think this would be correct:

Code: Select all

(*out1++) = pcm->data[currentSample++]; // Increament for first left channel
(*out2++) = pcm->data[currentSample]; // then right channel uses the same sample as the left channel
I do think I've spottet my problem - the unsigned short conversion - I am loading a 16 bit PCM wave and using "short" instead of "unsigned short"...

Thank you for your clarifying help! :)

Post

fessorman wrote:However the mono example is wrong isn't it?
Now that I am that wiser I would think this would be correct:

Code: Select all

(*out1++) = pcm->data[currentSample++]; // Increament for first left channel
(*out2++) = pcm->data[currentSample]; // then right channel uses the same sample as the left channel
No. You should read up on what pre- and post-increment operators do.
"Until you spread your wings, you'll have no idea how far you can walk." Image

Post

...and in case you can't be bothered...

Code: Select all

x = currentSample++
is equivalent to

Code: Select all

x = currentSample;
currentSample++;
whereas...

Code: Select all

x = ++currentSample;
is equivalent to:

Code: Select all

currentSample++;
x = currentSample;
Kick, punch, it's all in the mind.

Post

Oh - I'm such an idiot - of course it's a post increment... hmmm. been far too long since I've done some real coding, as I've been doing mostly software architecture these days - bad habbit, gotta get back to serious coding again!

Thank you very much for pointing that out to me - I gotta get my priorities straight! :)

One question though - Am I needing to do any samplerate conversion when say the wave file is at 44.1Khz 16bit and the host project is running at say 48Khz and 24bit - or does the host handle any of that? (I guessing not :roll: ).

Post

Unfortunately, that's entirely your job - the host tells you the sample rate it's using, and it is the PlugIn's responsibility to deliver output matching this rate.
"Until you spread your wings, you'll have no idea how far you can walk." Image

Post

a naive nearest-neighbor resampling where you merely keep a fractional index and increment by a factional amount each sample sounds terrible, but no need to fear, captain spline is here:

you'll need access to one sample behind and two samples ahead, you can make sure of this by filling your sample buffer with an extra three zeros, one at the beginning (start from offset +1) and two at the end (stop playing at bufferlength-2.)

Code: Select all

template<class T, class FT>
T hermite(const T &A, const T &B, const T &C, const T &D, const FT &X)
{
 T E = (((B - C) * (FT)3.0 - A) + D) * (FT)0.5;
 T F = ((C * (FT)2.0) + A) - ((((B * (FT)4.0) + B) + D) * (FT)0.5);
 T G = (C - A) * (FT)0.5;
 return B + (((E * X) + F) * X + G) * X;
} 
you can try these bits too, but nothing beats hermite in speed/quality until you start doing higher order firs.

Code: Select all

template<class T, class FT>
T sinc(const FT &X)
{
 return sin(X) / (1E-20 + X);
}

template<class T, class FT>
T lerp(const T &A, const T &B, const FT &X)
{
 return A + (B - A) * X;
} 

template<class T, class FT>
T lerpcos(const T &A, const T &B, const FT &X)
{
 return A + (B - A) * ((FT)1.0 - cos(X * (FT)3.14159)) * (FT)0.5;
}

template<class T, class FT>
T lerpcos2(const T &A, const T &B, const FT &X)
{
 return A + (B - A) * X*X * ((FT)2.0 - X*X);
}

template<class T, class FT>
T lerpsinc(const T &A, const T &B, const FT &X)
{
 return A + (B - A) * ((FT)1.0 - sinc<FT>(X * (FT)3.14159 * (FT)2.0)) * (FT)0.5;
}


template<class T, class FT>
T cubic(const T &A, const T &B, const T &C, const T &D, const FT &X)
{
 T E = D - C - A + B;
 T F = A - B - E;
 T G = C - A;
 T H = B;
 return E*X*X*X + F*X*X + G*X + H;
}

template<class T, class FT>
T catmull(const T &A, const T &B, const T &C, const T &D, const FT &X)
{
 return X*X*X*(-B*A + (2-B)*B + (B-2)*C + B*D) + X*X*(2*B*A + (B-3)*B + (3-2*B)*C - B*D) + X * (-B*A + B*C) + B;
}

template<class T, class FT>
T kochanek_bartels(const T &A, const T &B, const T &C, const T &D, const FT &X, const FT &tens = 0.0, const FT &cont = 0.0, const FT &bias = 0.0)
{
 FT M[16];
 //Tension    T=+1-->Tight            T=-1--> Round
 //Continuity C=+1-->Inverted corners C=-1--> Box corners
 //Bias       B=+1-->Post Shoot       B=-1--> Pre shoot

 //When T=B=C=0       this is the Catmul-Rom.
 //When T=1   & B=C=0 this is the Simple Cubic.
 //When T=B=0 & C=-1  this is the linear interp.

 FT FFA = (1 - tens) * (1 + cont) * (1 + bias);
 FT FFB = (1 - tens) * (1 - cont) * (1 - bias);
 FT FFC = (1 - tens) * (1 - cont) * (1 + bias);
 FT FFD = (1 - tens) * (1 + cont) * (1 - bias);

 M[ 0] = -FFA / (float)2.0;
 M[ 1] = (4 + FFA - FFB - FFC) / (float)2.0;
 M[ 2] = (-4 + FFB + FFC - FFD) / (float)2.0;
 M[ 3] = FFD / (float)2.0;
 
 M[ 4] = 2 * FFA / (float)2.0;
 M[ 5] = (-6 - 2 * FFA + 2 * FFB + FFC) / (float)2.0;
 M[ 6] = (6 - 2 * FFB - FFC + FFD) / (float)2.0;
 M[ 7] = -FFD / (float)2.0;
 
 M[ 8] = -FFA / (float)2.0;
 M[ 9] = (FFA - FFB) / (float)2.0;
 M[10] = FFB / (float)2.0;
 M[11] = 0.0;
 
 M[12] = 0.0;
 M[13] = 1.0;
 M[14] = 0.0;
 M[15] = 0.0;

 T E = A*M[ 0] + B*M[ 1] + C*M[ 2] + D*M[ 3];
 T F = A*M[ 4] + B*M[ 5] + C*M[ 6] + D*M[ 7];
 T G = A*M[ 8] + B*M[ 9] + C*M[10] + D*M[11];
 T H = A*M[12] + B*M[13] + C*M[14] + D*M[15];

 return ((E * X + F) * X + G) * X + H;
} 
the kochanek_bartels function should help you start to understand that these splines are actually linear matrix transformations.

Post

Hmm,
started an empty project in visual studio 2005, copied and pasted the code, built the program, and when i run it, it crashes

Post

which code?

you shouldnt trust any code (do not copy-paste) you find online on a board like this. read the code and use it to write your own version. that way you should notice serious bugs during that process.

otherwise.. no program has been posted here which was designed to do anything specific, so i'm not sure what you are trying to accomplish?

Post Reply

Return to “DSP and Plugin Development”