Code:
/*
Code to retrieve sample values from a WAVE file.
Mostly based on code from http://www.borg.com/~jglatt/tech/mmio.htm
If not using MSVC, you must link with winmm.lib. In Dev-C++ do this
by adding "-lwinmm" to the linker box under Project->Project Options->Parameters
(you need to create a new project and add the code as a C file, if you haven't already).
*/
#include <windows.h>
#include <stdio.h> /* Only required for sample code. */
#if defined(_MSC_VER)
#pragma comment(lib, "winmm.lib")
#endif
/*
* Definition for the HXWAVE handle type. This structure should
* be considered opaque.
*/
typedef struct
{
WAVEFORMATEX WaveFormat; /* Format of the wave file. */
HMMIO hMmio; /* Handle to the wave file. */
DWORD cbDataChunk; /* Size of the wave data chunk. */
} *HXWAVE;
/*
* Structure to contain a wave sample.
*/
typedef struct
{
USHORT left;
USHORT right;
} XSAMPLE;
/*
* Open the wave file. The format must be one of the following:
* "PCM, 16 bit stereo"
* "PCM, 8 bit stereo"
* "PCM, 16 bit mono"
* "PCM, 8 bit mono"
*/
HXWAVE xWaveOpen(LPCTSTR szFileName)
{
MMCKINFO mmckinfoParent; /* parent chunk information structure */
MMCKINFO mmckinfoSubchunk; /* subchunk information structure */
HXWAVE h; /* HXWAVE data structure */
/* Allocate the wave handle. */
if (NULL == (h = (HXWAVE) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*h))))
{
/* Oops, memory failure! */
goto error;
}
/* Open the file for reading with buffered I/O. Let windows use its default internal buffer */
if (NULL == (h->hMmio = mmioOpen( (LPTSTR) szFileName, NULL, MMIO_READ | MMIO_ALLOCBUF | MMIO_DENYWRITE)))
{
/* Oops, file doesn't exist or access denied! */
goto error;
}
/* Tell Windows to locate a WAVE FileType chunk header somewhere in the file.
* This marks the start of any embedded WAVE format within the file */
mmckinfoParent.fccType = mmioFOURCC('W', 'A', 'V', 'E');
if (MMSYSERR_NOERROR != mmioDescend(h->hMmio, (LPMMCKINFO) &mmckinfoParent, 0, MMIO_FINDRIFF))
{
/* Oops! No embedded WAVE format within this file */
goto error;
}
/* Tell Windows to locate the WAVE's "fmt " chunk, and read in its size field */
mmckinfoSubchunk.ckid = mmioFOURCC('f', 'm', 't', ' ');
if (MMSYSERR_NOERROR != mmioDescend(h->hMmio, &mmckinfoSubchunk, &mmckinfoParent, MMIO_FINDCHUNK))
{
/* Oops! The required fmt chunk was not found! */
goto error;
}
/* Tell Windows to read in the "fmt " chunk into our WAVEFORMATEX structure */
if (mmioRead(h->hMmio, (HPSTR) &h->WaveFormat,
min(mmckinfoSubchunk.cksize, sizeof(h->WaveFormat))) !=
min(mmckinfoSubchunk.cksize, sizeof(h->WaveFormat)))
{
/* Oops, couldn't read the format info! */
goto error;
}
/* Check that we support the format. */
if (h->WaveFormat.wFormatTag != WAVE_FORMAT_PCM ||
(h->WaveFormat.wBitsPerSample != 16 && h->WaveFormat.wBitsPerSample != 8) ||
(h->WaveFormat.nChannels != 1 && h->WaveFormat.nChannels != 2))
{
/* Oops, we don't handle this format! */
goto error;
}
/* Ascend out of the "fmt " subchunk. If you plan to parse any other chunks in the file, you need to
* "ascend" out of any chunk that you've mmioDescend()'ed into */
mmioAscend(h->hMmio, &mmckinfoSubchunk, 0);
/* Tell Windows to locate the data chunk. Upon return, the file
* pointer will be ready to read in the actual waveform data within
* the data chunk */
mmckinfoSubchunk.ckid = mmioFOURCC('d', 'a', 't', 'a');
if (MMSYSERR_NOERROR != mmioDescend(h->hMmio, &mmckinfoSubchunk, &mmckinfoParent, MMIO_FINDCHUNK))
{
/* Oops, couldn't find the data chunk! */
goto error;
}
/* Get the size of the data chunk (ie, the size of the waveform data) */
h->cbDataChunk = mmckinfoSubchunk.cksize;
return h;
error:
if (h && h->hMmio) mmioClose(h->hMmio, 0);
if (h) HeapFree(GetProcessHeap(), 0, h);
return NULL;
}
/*
* Close the WAVE file.
*/
VOID xWaveClose(HXWAVE hWave)
{
mmioClose(hWave->hMmio, 0);
HeapFree(GetProcessHeap(), 0, hWave);
}
/*
* Return the WAVEFORMATEX info for the specified file.
*/
VOID xWaveGetFormat(HXWAVE hWave, WAVEFORMATEX* pFormat)
{
CopyMemory(pFormat, &hWave->WaveFormat, sizeof(*pFormat));
}
/*
* Read a sample into the structure pointed to by pSample.
* All sample values are normalized to 16bit stereo.
* Return Values:
* a return value greater than 0 indicates success.
* a return value of 0 indicates end of file.
* a return value of less than 0 indicates error.
*/
LONG xWaveGetNextSample(HXWAVE hWave, XSAMPLE* pSample)
{
/* Read in left channel. */
LONG ret = mmioRead(hWave->hMmio, (HPSTR) &pSample->left, hWave->WaveFormat.wBitsPerSample / 8);
if (hWave->WaveFormat.nChannels == 2)
{
/* Read in right channel, if it exists. */
ret = mmioRead(hWave->hMmio, (HPSTR) &pSample->right, hWave->WaveFormat.wBitsPerSample / 8);
}
else
{
/* Mono file - give the right sample the same value as the left sample. */
pSample->right = pSample->left;
}
if (hWave->WaveFormat.wBitsPerSample == 8)
{
/* Normalize unsigned 8 bit samples to 16 bit unsigned. */
pSample->left *= 257;
pSample->right *= 257;
}
else
{
/* Convert 16 bit signed samples to unsigned. */
pSample->left = (SHORT) pSample->left + 32768;
pSample->right = (SHORT) pSample->right + 32768;
}
return ret;
}
/*
* Sample code (no error checking).
* Dump samples to stdout in comma seperated values format.
*/
int main(void)
{
HXWAVE hWave = NULL;
XSAMPLE sample = { 0 };
hWave = xWaveOpen(TEXT("C:\\WINDOWS\\MEDIA\\TADA.WAV"));
while (xWaveGetNextSample(hWave, &sample) > 0)
{
printf("%u,%u\n", sample.left, sample.right);
}
xWaveClose(hWave);
return 0;
}
Testing was fairly minimal, so if you have any problems with the code, please post. The attached image shows the result of graphing every 40th sample of TADA.WAV with Excel.