Discussion:
create a wave file (riff)
(too old to reply)
Larry
2009-12-27 00:51:36 UTC
Permalink
Hi,

I was reading something about RIFF and wave format file here:
http://www.codeguru.com/cpp/g-m/multimedia/audio/article.php/c8935/
http://www.codeguru.com/cpp/g-m/multimedia/audio/article.php/c8935__2/

"...a WAVE file is a collection of a number of different types of chunks.
But, there are three chunks that are required to be present in a valid wave
file, RIFF, FMT and DATA"

Now my question is: are they sort of tags like in mp3 (idv2 tags) or they
are part of each chunk?

I mean, If I do some capturing with the waveForm API and I'd like to save
the buffers (4096 bytes each) should I put those 3 tags at the very
beginning fo the file or each every chunk of raw data? (RIFF,FMT,DATA+4096
bytes...again RIFF,FMT,DATA+4096...) ????

thanks
ScottMcP [MVP]
2009-12-27 01:34:10 UTC
Permalink
Hi,
   I was reading something about RIFF and wave format file here:http://www.codeguru.com/cpp/g-m/multimedia/audio/article.php/c8935/http://www.codeguru.com/cpp/g-m/multimedia/audio/article.php/c8935__2/
"...a WAVE file is a collection of a number of different types of chunks.
But, there are three chunks that are required to be present in a valid wave
file, RIFF, FMT and DATA"
Now my question is: are they sort of tags like in mp3 (idv2 tags) or they
are part of each chunk?
I mean, If I do some capturing with the waveForm API and I'd like to save
the buffers (4096 bytes each) should I put those 3 tags at the very
beginning fo the file or each every chunk of raw data? (RIFF,FMT,DATA+4096
bytes...again RIFF,FMT,DATA+4096...) ????
thanks
Writing a WAV file using ordinary file APIs is difficult. But Windows
has functions that handle most of it for you. Download these SDK
audio samples. The one named LOWPASS is a good example of writing a
WAV file.

<http://www.microsoft.com/downloads/details.aspx?
displaylang=en&FamilyID=7aae2273-0320-4419-a0c3-a8c063df9d36>
Larry
2009-12-27 01:49:18 UTC
Permalink
Post by ScottMcP [MVP]
Writing a WAV file using ordinary file APIs is difficult. But Windows
has functions that handle most of it for you. Download these SDK
audio samples. The one named LOWPASS is a good example of writing a
WAV file.
Ok. By now, I have taken the difficult path so I cannot get out till I get
it sorted out!

Right now I am codign what should be put ahead of any chunk of raw data, the
thing is, I have to get a binary string out of all of this (is there anyway
to pack several structs in a single string?) thanks

#include <windows.h>
#include <stdlib.h>

const unsigned int STANDARD_WAVEFORMAT_SIZE = 16;

struct WAVE_FORMAT
{
WORD wFormatTag;
WORD wChannels;
DWORD dwSamplesPerSec;
DWORD dwAvgBytesPerSec;
WORD wBlockAlign;
WORD wBitsPerSample;
};

struct RIFF_HEADER
{
TCHAR szRiffID[4]; // 'R','I','F','F'
DWORD dwRiffSize;
TCHAR szRiffFormat[4]; // 'W','A','V','E'
};

struct FMT_BLOCK
{
TCHAR szFmtID[4]; // 'f','m','t',' '
DWORD dwFmtSize;
WAVE_FORMAT wavFormat;
};

struct DATA_BLOCK
{
TCHAR szDataID[4]; // 'd','a','t','a'
DWORD dwDataSize;
};

void GetARiffChunk(RIFF_HEADER& rh);
void GetADataBlock(DATA_BLOCK& db);
void GetAWaveFormat(WAVE_FORMAT& wf);
void GetAFmtBlock(FMT_BLOCK& fb);

void main()
{
RIFF_HEADER rh;
FMT_BLOCK fb;
DATA_BLOCK db;

GetARiffChunk(rh);
GetAFmtBlock(fb);
GetADataBlock(db);

system("pause");
}

void GetARiffChunk(RIFF_HEADER& rh)
{
rh.dwRiffSize = sizeof(rh);
rh.szRiffID[0] = 'R';
rh.szRiffID[1] = 'I';
rh.szRiffID[2] = 'F';
rh.szRiffID[3] = 'F';
// Lets add the wave tag id.
rh.szRiffFormat[0] = 'W';
rh.szRiffFormat[1] = 'A';
rh.szRiffFormat[2] = 'V';
rh.szRiffFormat[3] = 'E';
rh.dwRiffSize = sizeof(rh);
};

void GetADataBlock(DATA_BLOCK& db)
{
db.szDataID[0] = 'd';
db.szDataID[1] = 'a';
db.szDataID[2] = 't';
db.szDataID[3] = 'a';
db.dwDataSize = 4096;
}

void GetAWaveFormat(WAVE_FORMAT& wf)
{
wf.dwAvgBytesPerSec = 0;
wf.dwSamplesPerSec = 0;
wf.wBitsPerSample = 0;
wf.wBlockAlign = 0;
wf.wChannels = 0;
wf.wFormatTag = 1;
}

void GetAFmtBlock(FMT_BLOCK& fb)
{
WAVE_FORMAT wf;
GetAWaveFormat(wf);
fb.szFmtID[0] = 'f';
fb.szFmtID[1] = 'm';
fb.szFmtID[2] = 't';
fb.szFmtID[3] = ' ';
fb.wavFormat = wf;
fb.dwFmtSize = sizeof(fb);
}
Larry
2009-12-27 12:57:04 UTC
Permalink
Post by ScottMcP [MVP]
Writing a WAV file using ordinary file APIs is difficult. But Windows
has functions that handle most of it for you. Download these SDK
audio samples. The one named LOWPASS is a good example of writing a
WAV file.
Actually I have just found this:
http://msdn.microsoft.com/en-us/library/dd757663%28VS.85%29.aspx

It basically uses the mmioOpen to create a riff chunk in a file! So I coded
this for the moment:

HMMIO hfile;
MMCKINFO mmckinfo;
mmckinfo.fccType = mmioFOURCC('W', 'A', 'V', 'E');
hfile = mmioOpen(file, NULL, MMIO_CREATE|MMIO_WRITE);
mmioCreateChunk(hfile, &mmckinfo, MMIO_CREATERIFF);

I wonder how I can add the "fmt" with the formatex so that I have a real
chuck!

thanks
Bob Masta
2009-12-27 14:14:59 UTC
Permalink
On Sun, 27 Dec 2009 01:51:36 +0100, "Larry"
Post by Larry
Hi,
http://www.codeguru.com/cpp/g-m/multimedia/audio/article.php/c8935/
http://www.codeguru.com/cpp/g-m/multimedia/audio/article.php/c8935__2/
"...a WAVE file is a collection of a number of different types of chunks.
But, there are three chunks that are required to be present in a valid wave
file, RIFF, FMT and DATA"
Now my question is: are they sort of tags like in mp3 (idv2 tags) or they
are part of each chunk?
I mean, If I do some capturing with the waveForm API and I'd like to save
the buffers (4096 bytes each) should I put those 3 tags at the very
beginning fo the file or each every chunk of raw data? (RIFF,FMT,DATA+4096
bytes...again RIFF,FMT,DATA+4096...) ????
Begging to differ with Scott, but writing WAV
files with standard file APIs need not be too
difficult. You need to create one RIFF chunk, one
WAVE chunk, and one DATA chunk for all the data,
no matter how much it is (up to 2 GB or so limit
of RIFF format.)

The DATA section is just what you are already
recording. The WAVE format header includes sample
rate plus a few arcane details that you can
usually boiler-plate if you are running at fixed
sample rate. The only thing that's at all tricky
is that the RIFF and DATA chunk size dwords are
written last, after the recording is done (when
you know what the final size is). So you have to
move the file pointer back and fill those in after
initially writing dummy placeholder values.

Best regards,


Bob Masta

DAQARTA v5.00
Data AcQuisition And Real-Time Analysis
www.daqarta.com
Scope, Spectrum, Spectrogram, Sound Level Meter
Frequency Counter, FREE Signal Generator
Pitch Track, Pitch-to-MIDI
DaqMusic - FREE MUSIC, Forever!
(Some assembly required)
Science (and fun!) with your sound card!
Larry
2009-12-28 05:20:02 UTC
Permalink
Post by Bob Masta
Begging to differ with Scott, but writing WAV
files with standard file APIs need not be too
difficult. You need to create one RIFF chunk, one
WAVE chunk, and one DATA chunk for all the data,
no matter how much it is (up to 2 GB or so limit
of RIFF format.)
The DATA section is just what you are already
recording. The WAVE format header includes sample
rate plus a few arcane details that you can
usually boiler-plate if you are running at fixed
sample rate. The only thing that's at all tricky
is that the RIFF and DATA chunk size dwords are
written last, after the recording is done (when
you know what the final size is). So you have to
move the file pointer back and fill those in after
initially writing dummy placeholder values.
I have found this great piece of code!
http://www.nerdmodo.com/2009/07/creating-recording-sound-in-wav-file-using-mimos-with-cc/

I would like to understand why is he using:

//memset(&mmckinfo, 0, sizeof(mmckinfo));
//memset(&mmckinfoSubchunk, 0, sizeof(mmckinfoSubchunk));
//memset(&mmckinfoData, 0, sizeof(mmckinfoData));

so if comment that code and I recoder the first 32768 bytes I will get a
32KB file, if I uncomment I'll get a 46KB file...

Also, is it code to file with 0x00s the buffer? like this:

WAVEHDR buffer;
ZeroMemory(&buffer, sizeof(buffer));
buffer.dwBufferLength = BUFFER_LEN;
buffer.lpData = (LPSTR) malloc(BUFFER_LEN);

thanks
Bob Masta
2009-12-28 13:34:45 UTC
Permalink
On Mon, 28 Dec 2009 06:20:02 +0100, "Larry"
Post by Larry
Post by Bob Masta
Begging to differ with Scott, but writing WAV
files with standard file APIs need not be too
difficult. You need to create one RIFF chunk, one
WAVE chunk, and one DATA chunk for all the data,
no matter how much it is (up to 2 GB or so limit
of RIFF format.)
The DATA section is just what you are already
recording. The WAVE format header includes sample
rate plus a few arcane details that you can
usually boiler-plate if you are running at fixed
sample rate. The only thing that's at all tricky
is that the RIFF and DATA chunk size dwords are
written last, after the recording is done (when
you know what the final size is). So you have to
move the file pointer back and fill those in after
initially writing dummy placeholder values.
I have found this great piece of code!
http://www.nerdmodo.com/2009/07/creating-recording-sound-in-wav-file-using-mimos-with-cc/
//memset(&mmckinfo, 0, sizeof(mmckinfo));
//memset(&mmckinfoSubchunk, 0, sizeof(mmckinfoSubchunk));
//memset(&mmckinfoData, 0, sizeof(mmckinfoData));
so if comment that code and I recoder the first 32768 bytes I will get a
32KB file, if I uncomment I'll get a 46KB file...
WAVEHDR buffer;
ZeroMemory(&buffer, sizeof(buffer));
buffer.dwBufferLength = BUFFER_LEN;
buffer.lpData = (LPSTR) malloc(BUFFER_LEN);
thanks
Check out:
https://ccrma.stanford.edu/courses/422/projects/WaveFormat/
This shows the full layout of the RIFF WAVE
format. Or Google 'RIFF WAVE format', there are
plenty of examples. Again, assuming that you
always record at the same sample, most of this is
boilerplate. You don't need to fill anything with
zeroes; it's all format stuff or your data (or
chunk sizes).

Best regards,


Bob Masta

DAQARTA v5.00
Data AcQuisition And Real-Time Analysis
www.daqarta.com
Scope, Spectrum, Spectrogram, Sound Level Meter
Frequency Counter, FREE Signal Generator
Pitch Track, Pitch-to-MIDI
DaqMusic - FREE MUSIC, Forever!
(Some assembly required)
Science (and fun!) with your sound card!
Larry
2009-12-28 22:57:22 UTC
Permalink
Post by Bob Masta
https://ccrma.stanford.edu/courses/422/projects/WaveFormat/
This shows the full layout of the RIFF WAVE
format. Or Google 'RIFF WAVE format', there are
plenty of examples. Again, assuming that you
always record at the same sample, most of this is
boilerplate. You don't need to fill anything with
zeroes; it's all format stuff or your data (or
chunk sizes).
Ok, this is the gist of that web page:

"A RIFF file starts out with a file header followed by a sequence of data
chunks. A WAVE file is /often/ just a RIFF file with a single "WAVE" chunk
which consists of two sub-chunks -- a "fmt " chunk specifying the data
format and a "data" chunk containing the actual sample data."

so, in a nutshell, my file must be made up of a RIFF chunk, a FMT chunk and
a DATA chunk followed up with the entire raw binary data.

If I am not mistaken the RIFF header makes up the first 44 Bytes of the WAVE
file (always), does not that?
Post by Bob Masta
The only thing that's at all tricky
is that the RIFF and DATA chunk size dwords are
written last, after the recording is done (when
you know what the final size is).
Since I am doing a streaming recorder (real time) I won't be able to know
beforehand what the DWORD values for RIFF and DATA are!

My idea was to fill with zeroes the first 44 Bytes of the .wav file then
append the actual raw data to it, real time. Finally, when I stop the
recording I will add the RIFF header at the beginning of the file with the
correct DWORD values for the RIFF and DATA chunck!

is that good to you?

**
By the way, I am a little bit struck by this: "The sample data must end on
an even byte boundary. Whatever that means."
Bob Masta
2009-12-29 13:03:46 UTC
Permalink
On Mon, 28 Dec 2009 23:57:22 +0100, "Larry"
Post by Larry
Post by Bob Masta
https://ccrma.stanford.edu/courses/422/projects/WaveFormat/
This shows the full layout of the RIFF WAVE
format. Or Google 'RIFF WAVE format', there are
plenty of examples. Again, assuming that you
always record at the same sample, most of this is
boilerplate. You don't need to fill anything with
zeroes; it's all format stuff or your data (or
chunk sizes).
"A RIFF file starts out with a file header followed by a sequence of data
chunks. A WAVE file is /often/ just a RIFF file with a single "WAVE" chunk
which consists of two sub-chunks -- a "fmt " chunk specifying the data
format and a "data" chunk containing the actual sample data."
so, in a nutshell, my file must be made up of a RIFF chunk, a FMT chunk and
a DATA chunk followed up with the entire raw binary data.
If I am not mistaken the RIFF header makes up the first 44 Bytes of the WAVE
file (always), does not that?
Post by Bob Masta
The only thing that's at all tricky
is that the RIFF and DATA chunk size dwords are
written last, after the recording is done (when
you know what the final size is).
Since I am doing a streaming recorder (real time) I won't be able to know
beforehand what the DWORD values for RIFF and DATA are!
My idea was to fill with zeroes the first 44 Bytes of the .wav file then
append the actual raw data to it, real time. Finally, when I stop the
recording I will add the RIFF header at the beginning of the file with the
correct DWORD values for the RIFF and DATA chunck!
is that good to you?
As mentioned in my first post, you can fill in
everything but the RIFF and DATA sizes at the
start, since you know the sample rate, number of
bits, and mono/stereo already. All you need to do
at the end of the recording is fill in the sizes.
But it won't hurt to write the entire header at
that time.
Post by Larry
**
By the way, I am a little bit struck by this: "The sample data must end on
an even byte boundary. Whatever that means."
This only pertains to mono 8-bit data, where there
is the possibility of having an odd number of
bytes. Otherwise, everything is even
automatically.

Best regards,



Bob Masta

DAQARTA v5.00
Data AcQuisition And Real-Time Analysis
www.daqarta.com
Scope, Spectrum, Spectrogram, Sound Level Meter
Frequency Counter, FREE Signal Generator
Pitch Track, Pitch-to-MIDI
DaqMusic - FREE MUSIC, Forever!
(Some assembly required)
Science (and fun!) with your sound card!
Larry
2009-12-30 03:15:06 UTC
Permalink
Post by Bob Masta
As mentioned in my first post, you can fill in
everything but the RIFF and DATA sizes at the
start, since you know the sample rate, number of
bits, and mono/stereo already. All you need to do
at the end of the recording is fill in the sizes.
But it won't hurt to write the entire header at
that time.
ok, so supposed I have a .wav file containg:

1) the first 44 bytes padded with zeroes (0x00)
2) 100000 bytes of actual raw data

what values should I set RIFF and DATA dwords?

Also, what is the use of mmioAscend() function?

thanks
Larry
2009-12-30 04:21:40 UTC
Permalink
Post by Larry
1) the first 44 bytes padded with zeroes (0x00)
2) 100000 bytes of actual raw data
what values should I set RIFF and DATA dwords?
I have great difficulties understanding this:
https://ccrma.stanford.edu/courses/422/projects/WaveFormat/

"(offset 4, size 4) ChunkSize: 36 + SubChunk2Size, or more precisely: 4 + (8
+ SubChunk1Size) + (8 + SubChunk2Size) This is the size of the rest of the
chunk following this number. This is the size of the entire file in bytes
minus 8 bytes for the two fields not included in this count: ChunkID and
ChunkSize."

So it should basically be: 36 + SubChunk2Size

Now,

"(offset 40, size 4) Subchunk2Size == NumSamples * NumChannels *
BitsPerSample/8 This is the number of bytes in the data. You can also think
of this as the size of the read of the subchunk following this number."

I don't really get that!

Does it mean how long one single sample is, or the whole file?

thanks
Bob Masta
2009-12-30 13:33:40 UTC
Permalink
On Wed, 30 Dec 2009 05:21:40 +0100, "Larry"
Post by Bob Masta
Post by Larry
1) the first 44 bytes padded with zeroes (0x00)
2) 100000 bytes of actual raw data
what values should I set RIFF and DATA dwords?
https://ccrma.stanford.edu/courses/422/projects/WaveFormat/
"(offset 4, size 4) ChunkSize: 36 + SubChunk2Size, or more precisely: 4 + (8
+ SubChunk1Size) + (8 + SubChunk2Size) This is the size of the rest of the
chunk following this number. This is the size of the entire file in bytes
minus 8 bytes for the two fields not included in this count: ChunkID and
ChunkSize."
So it should basically be: 36 + SubChunk2Size
Now,
"(offset 40, size 4) Subchunk2Size == NumSamples * NumChannels *
BitsPerSample/8 This is the number of bytes in the data. You can also think
of this as the size of the read of the subchunk following this number."
I don't really get that!
Does it mean how long one single sample is, or the whole file?
thanks
Think about the 'data' subchunk size first, before
'RIFF'. The explanation makes it clear that this
is the size of the entire data by giving that
little formula. So in your case it would be
100000.

Then the 'RIFF' chunk size is (as they say in
words) the size of everything after that, which
includes the data plus all the header stuff. So
add up all the header bytes after the RIFF size
dword, (36 total, as you note above) and then add
100000 to that for the data.

I've never used MMIO and never understood why I'd
want to. But that's probably just me... <g>

Best regards,



Bob Masta

DAQARTA v5.00
Data AcQuisition And Real-Time Analysis
www.daqarta.com
Scope, Spectrum, Spectrogram, Sound Level Meter
Frequency Counter, FREE Signal Generator
Pitch Track, Pitch-to-MIDI
DaqMusic - FREE MUSIC, Forever!
(Some assembly required)
Science (and fun!) with your sound card!
Larry
2009-12-31 00:17:26 UTC
Permalink
Post by Bob Masta
Think about the 'data' subchunk size first, before
'RIFF'. The explanation makes it clear that this
is the size of the entire data by giving that
little formula. So in your case it would be
100000.
Then the 'RIFF' chunk size is (as they say in
words) the size of everything after that, which
includes the data plus all the header stuff. So
add up all the header bytes after the RIFF size
dword, (36 total, as you note above) and then add
100000 to that for the data.
the app finally works!!!!!!! the whole source code is avaible here:
http://theartofweb.net/cpp/waveform_recorder_07.txt

I'd like to record MONO at 16bit 44,1KHZ, so I passed 3 buffers of 65535
Bytes each to the waveInPrepareHeader() and waveInAddBuffer()
functions...well sometimes I still get some choppies in the audio when I
playback...I have to founf a good buffer size! without having too much delay
as well...

thanks
ScottMcP [MVP]
2009-12-31 04:31:21 UTC
Permalink
Post by Larry
I'd like to record MONO at 16bit 44,1KHZ, so I passed 3 buffers of 65535
Bytes each to the waveInPrepareHeader() and waveInAddBuffer()
functions...well sometimes I still get some choppies in the audio when I
playback...I have to founf a good buffer size! without having too much delay
as well...
thanks
That's an "odd" buffer size choose. Make the buffer an even number of
bytes. After all, you are recording 2 bytes per sample.

Congratulations on getting it working.
Larry
2009-12-31 09:44:40 UTC
Permalink
Post by ScottMcP [MVP]
That's an "odd" buffer size choose. Make the buffer an even number of
bytes. After all, you are recording 2 bytes per sample.
I guess "nAvgBytesPerSec" is the value for the perfect buffer lenght!!

so I a set this structure for the WAVEFORMATEX:

wf.wBitsPerSample = 16;
wf.nChannels = 1;
wf.nSamplesPerSec = 44100;
wf.nBlockAlign = (wf.nChannels * wf.wBitsPerSample) / 8;
wf.nAvgBytesPerSec = (wf.nSamplesPerSec * wf.nBlockAlign);

"nAvgBytesPerSec" will be: 88200 !!! that's the perfect buffer!!!
Post by ScottMcP [MVP]
Congratulations on getting it working.
thanks!!

Larry
2009-12-30 12:13:14 UTC
Permalink
Post by Bob Masta
As mentioned in my first post, you can fill in
everything but the RIFF and DATA sizes at the
start, since you know the sample rate, number of
bits, and mono/stereo already. All you need to do
at the end of the recording is fill in the sizes.
But it won't hurt to write the entire header at
that time.
Also, Is there anyway to have mmioOpen() open a file with the APPEND flag
and seek to the very start of it?

thanks
Loading...