When you call waveOutOpen to open a device driver for playback or to query if the driver supports a particular data format, use the lpFormat parameter to specify a pointer to a structure containing the requested waveform data format.
The WAVEFORMAT structure specifies format information common to all types of waveform data formats. Currently, the only format supported is PCM, but in the future, other types such as ADPCM might be supported. The MMSYSTEM.H file defines the WAVEFORMAT structure as follows:
typedef struct waveformat_tag {
UINT wFormatTag; /* format type */
UINT nChannels; /* number of channels */
DWORD nSamplesPerSec; /* number of samples per second */
DWORD nAvgBytesPerSec; /* average data rate */
UINT nBlockAlign; /* block alignment */
} WAVEFORMAT;
The wFormatTag field specifies the format type for the data. Currently, the only flag defined for this field is WAVE_FORMAT_PCM for PCM waveform data.
The nChannels field specifies the number of discrete channels in the format. Use a value of 1 for mono data and 2 for stereo data.
The nSamplesPerSec field specifies the sample rate.
The nAvgBytesPerSec field specifies the average data rate in bytes per second. For example, 16-bit stereo at 44.1 kHz has an average data rate of 176400 bytes per second (2 channels ´ 2 bytes per sample per channel ´ 44100 samples per second).
The nBlockAlign field specifies the minimum atomic unit of data that can be passed to a driver. For PCM data, the block alignment is the number of bytes used by a single sample, including data for both channels if the data is stereo. For example, the block alignment for 16-bit stereo PCM is 4 bytes (2 channels ´ 2 bytes per sample).
In addition to the general information in the WAVEFORMAT structure, specific information is needed to describe a PCM waveform data format completely. For PCM waveform data, the PCMWAVEFORMAT structure includes a WAVEFORMAT structure along with an additional field containing PCM-specific information as follows.
typedef struct pcmwaveformat_tag {
WAVEFORMAT wf; /* general format information */
UINT wBitsPerSample; /* number of bits per sample */
} PCMWAVEFORMAT;
The wf field specifies general format information. The wBitsPerSample field specifies the number of bits per sample for PCM data.
For PCM audio data, use the PCMWAVEFORMAT structure to specify the data format. The following code fragment shows how to set up a PCMWAVEFORMAT structure for 11.025 kHz 8-bit mono and for 44.1 kHz 16-bit stereo. After setting up PCMWAVEFORMAT, the example calls the IsFormatSupported function to verify that the PCM waveform output device supports the format. The source for IsFormatSupported is given in an example in “Determining Non-Standard Format Support,” earlier in this chapter.
UINT wReturn;
PCMWAVEFORMAT pcmWaveFormat;
/* Set up PCMWAVEFORMAT for 11 kHz 8-bit mono
*/
pcmWaveFormat.wf.wFormatTag = WAVE_FORMAT_PCM;
pcmWaveFormat.wf.nChannels = 1;
pcmWaveFormat.wf.nSamplesPerSec = 11025L;
pcmWaveFormat.wf.nAvgBytesPerSec = 11025L;
pcmWaveFormat.wf.nBlockAlign = 1;
pcmWaveFormat.wBitsPerSample = 8;
/* See if format is supported by any device in system
*/
wReturn = IsFormatSupported(&pcmWaveFormat, WAVE_MAPPER);
/* Report results
*/
if (wReturn == 0)
MessageBox(hMainWnd, "11 kHz 8-bit mono is supported.",
"", MB_ICONINFORMATION);
else if (wReturn == WAVERR_BADFORMAT)
MessageBox(hMainWnd, "11 kHz 8-bit mono is NOT supported.",
"", MB_ICONINFORMATION);
else
MessageBox(hMainWnd, "Error opening waveform device.",
"Error", MB_ICONEXCLAMATION);
/* Set up PCMWAVEFORMAT for 44.1 kHz 16-bit stereo
*/
pcmWaveFormat.wf.wFormatTag = WAVE_FORMAT_PCM;
pcmWaveFormat.wf.nChannels = 2;
pcmWaveFormat.wf.nSamplesPerSec = 44100L;
pcmWaveFormat.wf.nAvgBytesPerSec = 176400L;
pcmWaveFormat.wf.nBlockAlign = 4;
pcmWaveFormat.wBitsPerSample = 32;
/* See if format is supported by any device in the system
*/
wReturn = IsFormatSupported(&pcmWaveFormat, WAVE_MAPPER);
/* Report results
*/
If (wReturn == 0)
MessageBox(hMainWnd, "44.1 kHz 16-bit stereo is supported.",
"", MB_ICONINFORMATION);
else if (wReturn == WAVERR_BADFORMAT)
MessageBox(hMainWnd, "44.1 kHz 16-bit stereo is NOT supported.",
"", MB_ICONINFORMATION);
else
MessageBox(hMainWnd, "Error opening waveform device.",
"Error", MB_ICONEXCLAMATION);
The easiest way to get waveform-format information from a WAVE file is by using the multimedia file I/O services. To do this, use mmioDescend to locate the “fmt ” chunk containing the format information and then mmioRead to read the format chunk directly into the proper format structure (chunks are the basic building blocks of RIFF files). The following code fragment illustrates this technique. For more information about multimedia file I/O, see Chapter 82, “Multimedia File I/O.”
void ReversePlay()
{
HMMIO hmmio;
MMCKINFO mmckinfoParent;
MMCKINFO mmckinfoSubchunk;
DWORD dwFmtSize;
char szFileName[ MAX_FILENAME_SIZE ];
HANDLE hFormat;
WAVEFORMAT *pFormat;
.
.
.
/* Open the given file for reading using buffered I/O.
*/
...
/* Locate a "RIFF" chunk with a "WAVE" form type
* to make sure it's a WAVE file.
*/
...
/* Now, find the format chunk (form type "fmt "). It should be
* a subchunk of the "RIFF" parent chunk.
*/
mmckinfoSubchunk.ckid = mmioFOURCC('f', 'm', 't', ' ');
if (mmioDescend(hmmio, &mmckinfoSubchunk, &mmckinfoParent,
MMIO_FINDCHUNK))
{
MessageBox(hwndApp, "WAVE file is corrupted.",
NULL, MB_OK | MB_ICONEXCLAMATION);
mmioClose(hmmio, 0);
return;
}
/* Get the size of the format chunk, allocate and lock memory for it.
*/
dwFmtSize = mmckinfoSubchunk.cksize;
hFormat = LocalAlloc(LMEM_MOVEABLE, LOWORD(dwFmtSize));
if (!hFormat)
{
MessageBox(hwndApp, "Out of memory.",
NULL, MB_OK | MB_ICONEXCLAMATION);
mmioClose(hmmio, 0);
return;
}
pFormat = (WAVEFORMAT *) LocalLock(hFormat);
if (!pFormat)
{
MessageBox(hwndApp, "Failed to lock memory for format chunk.",
NULL, MB_OK | MB_ICONEXCLAMATION);
LocalFree(hFormat);
mmioClose(hmmio, 0);
return;
}
/* Read the format chunk.
*/
if (mmioRead(hmmio, pFormat, dwFmtSize) != dwFmtSize)
{
MessageBox(hwndApp, "Failed to read format chunk.",
NULL, MB_OK | MB_ICONEXCLAMATION);
LocalUnlock(hFormat);
LocalFree(hFormat);
mmioClose(hmmio, 0);
return;
}
/* Make sure it's a PCM file.
*/
if (pFormat->wFormatTag != WAVE_FORMAT_PCM)
{
LocalUnlock(hFormat);
LocalFree(hFormat);
mmioClose(hmmio, 0);
MessageBox(hwndApp, "The file is not a PCM file.",
NULL, MB_OK | MB_ICONEXCLAMATION);
return;
}
/* Make sure the system has a waveform output
* device capable of playing this format.
*/
if (waveOutOpen(&hWaveOut, WAVE_MAPPER, (LPWAVEFORMAT)pFormat, NULL,
0L, WAVE_FORMAT_QUERY))
{
LocalUnlock(hFormat);
LocalFree(hFormat);
mmioClose(hmmio, 0);
MessageBox(hwndApp, "The waveform device can't play this format.",
NULL, MB_OK | MB_ICONEXCLAMATION);
return;
}
.
.
.
}