How to Read and Write Wav Files
- KVRAF
- 12615 posts since 7 Dec, 2004
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.
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.
- KVRAF
- 12615 posts since 7 Dec, 2004
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.
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.
-
- KVRer
- 17 posts since 23 Mar, 2005
Hi acidose,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
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
Sincerely,
Dennis P
- KVRAF
- 12615 posts since 7 Dec, 2004
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.
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.
- KVRAF
- 12615 posts since 7 Dec, 2004
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.
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.
-
- KVRAF
- 1940 posts since 16 Aug, 2004 from Vienna, Austria
In addition to the conversion to floating point, your code contains a serious error -
assuming the output has 2 channels, should read
if the input .wav file is stereo or
(without the superfluous increment) if the input file is mono.
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 runCode: Select all
(*out1++) = pcm->data[currentSample++]; // first left channel
(*out2++) = pcm->data[currentSample++]; // then right channelCode: Select all
(*out1++) = pcm->data[currentSample]; // first left channel
(*out2++) = pcm->data[currentSample++]; // then right channel-
- KVRer
- 17 posts since 23 Mar, 2005
Ah - of course - you need to enter the pointer array first, so of course you need to increment first...arakula wrote:In addition to the conversion to floating point, your code contains a serious error -
assuming the output has 2 channels,should readCode: 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 runif the input .wav file is stereo orCode: 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.Code: Select all
(*out1++) = pcm->data[currentSample]; // first left channel (*out2++) = pcm->data[currentSample++]; // then right channel
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 channelThank you for your clarifying help!
-
- KVRAF
- 1940 posts since 16 Aug, 2004 from Vienna, Austria
No. You should read up on what pre- and post-increment operators do.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
-
- KVRAF
- 3364 posts since 16 Feb, 2004 from atop a katamari
...and in case you can't be bothered...
is equivalent to
whereas...
is equivalent to:
Code: Select all
x = currentSample++Code: Select all
x = currentSample;
currentSample++;Code: Select all
x = ++currentSample;Code: Select all
currentSample++;
x = currentSample;Kick, punch, it's all in the mind.
-
- KVRer
- 17 posts since 23 Mar, 2005
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
).
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
-
- KVRAF
- 1940 posts since 16 Aug, 2004 from Vienna, Austria
- KVRAF
- 12615 posts since 7 Dec, 2004
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.)
you can try these bits too, but nothing beats hermite in speed/quality until you start doing higher order firs.
the kochanek_bartels function should help you start to understand that these splines are actually linear matrix transformations.
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;
}
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;
}
-
- KVRian
- 940 posts since 11 Mar, 2001 from nyc
Hmm,
started an empty project in visual studio 2005, copied and pasted the code, built the program, and when i run it, it crashes
started an empty project in visual studio 2005, copied and pasted the code, built the program, and when i run it, it crashes
- KVRAF
- 12615 posts since 7 Dec, 2004
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?
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?
