The source code shown below is a simple, working wav file player application. The program work perfectly under Windows 2000, but fails in a repeatable fashion under Windows 98.
Let me briefly describe the functionality, then describe a mode of operation that works properly under both OS'es, then a mode of operation that fails only under Windows 98.
When the program is started, the wav memory structures are initialized (WM_CREATE calls CreatePlayer). When a file is to be played, Play() is called. This routine opens the file “test.wav” (a 44.1 kHz sampled, stereo file), prepares the headers and fills the first set of buffers. As the wav device renders each buffer, it send a MM_WOM_DONE message to its callback routine. This routine sends a WM_COMMAND message to the main program, which then calls ReadBuffer() to refill the just-emptied buffer. When there is no more data to be read from the file, ReadBuffer() returns a 1, signaling the main program that the file has been completely rendered. The main program then sends a Stop(), which closes the wav device. Note that Stop() does not free any memory as Play() can be called again.
Under both OS'es, if the a file is Play()'ed then Stop()'ed, it can be Play()'ed again without any problems. This is as expected.
Under Win2k, if a file is allowed to Play() to completion, clicking Play() again causes the file to be Play()'ed. This is as expected. However, under Win98, when a file is allowed to Play() to completion, clicking Play() results in waveOutPrepareHeader() returning with an error, MMSYSERR_NOMEM, and the file does not Play().
I have tried a number of ways to solve this problem, including using memory locking, freeing and reallocating memory each time the player Stop()'s, etc. I haven't a clue as to why this is failing in this manner. Your thoughts and suggestions are gratefully accepted!
=== source code ===
#include <windows.h>
#include <winuser.h>
#include <mmsystem.h>
#include <stdio.h>
#define NBUFS_OUT 4L
#define OUTBUFSIZE 0x4000L
#define ID_DONEBUFFER 40000
#define ID_CLOSED 40001
#define ID_PLAY_BUT 40002
#define ID_STOP_BUT 40003
#define WAVE_PLAYING 1
#define WAVE_READY 2
long doneBufCnt = 0;
FILE *fp = NULL;
HWND hWnd = NULL;
HWAVEOUT hWaveOutDevice;
WAVEFORMATEX waveOutFormatEx;
WAVEHDR waveOutHdrs[NBUFS_OUT];
HINSTANCE myhInstance;
///////////////////////////////////////////////////////////////////////////////
void CALLBACK WaveOutCallback (HWAVEOUT hwo, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2)
///////////////////////////////////////////////////////////////////////////////
{
switch (uMsg)
{
case MM_WOM_OPEN: // device opened via waveOutOpen(), nothing to do
break;
case MM_WOM_DONE: // device done playing data sent via waveOutWrite()
SendNotifyMessage (hWnd, WM_COMMAND, ID_DONEBUFFER, ((WAVEHDR *)dwParam1)->dwUser);
break;
case MM_WOM_CLOSE: // device closed via waveOutClose()
SendNotifyMessage (hWnd, WM_COMMAND, ID_CLOSED, 0L);
break;
}
}
///////////////////////////////////////////////////////////////////////////////
long ReadBuffer (long curBuf)
///////////////////////////////////////////////////////////////////////////////
{
if ((waveOutHdrs[curBuf].dwBufferLength = fread (waveOutHdrs[curBuf].lpData, 1, OUTBUFSIZE, fp)) != OUTBUFSIZE)
{
if (feof (fp))
doneBufCnt++;
}
else
waveOutWrite (hWaveOutDevice, &waveOutHdrs[curBuf], sizeof (WAVEHDR));
if (doneBufCnt == NBUFS_OUT)
{
doneBufCnt = 0;
return 1;
}
return 0;
}
///////////////////////////////////////////////////////////////////////////////
void CreatePlayer (void)
///////////////////////////////////////////////////////////////////////////////
{
long myInt;
fp = fopen ("test.wav", "rb"
waveOutFormatEx.wFormatTag = WAVE_FORMAT_PCM;
waveOutFormatEx.nChannels = 2;
waveOutFormatEx.nSamplesPerSec = 44100;
waveOutFormatEx.nAvgBytesPerSec = 176400;
waveOutFormatEx.nBlockAlign = 4;
waveOutFormatEx.wBitsPerSample = 16;
waveOutFormatEx.cbSize = (WORD)0;
for (myInt = 0; myInt < NBUFS_OUT; ++myInt) {
memset (&waveOutHdrs[myInt], 0, sizeof (WAVEHDR));
waveOutHdrs[myInt].lpData = (char *)calloc (OUTBUFSIZE, sizeof(char));
waveOutHdrs[myInt].dwUser = myInt;
waveOutHdrs[myInt].dwBufferLength = OUTBUFSIZE;
}
}
/////////////////////////////////////////////////////////////////////////
void Play (void)
///////////////////////////////////////////////////////////////////////////////
{
long myInt;
DWORD dwInstance = 0;
fseek (fp, 44, SEEK_SET);
waveOutOpen ((LPHWAVEOUT)&hWaveOutDevice, WAVE_MAPPER, &waveOutFormatEx, (DWORD)WaveOutCallback, dwInstance, CALLBACK_FUNCTION);
waveOutPause (hWaveOutDevice);
for (myInt=0; myInt<NBUFS_OUT; myInt++)
{
waveOutPrepareHeader (hWaveOutDevice, &waveOutHdrs[myInt], sizeof (WAVEHDR));
memset (waveOutHdrs[myInt].lpData, 0, OUTBUFSIZE);
ReadBuffer (myInt);
waveOutWrite (hWaveOutDevice, &waveOutHdrs[myInt], sizeof (WAVEHDR));
}
waveOutRestart (hWaveOutDevice);
}
///////////////////////////////////////////////////////////////////////////////
void Stop (void)
///////////////////////////////////////////////////////////////////////////////
{
long myInt;
waveOutReset (hWaveOutDevice);
for (myInt = 0; myInt < NBUFS_OUT; ++myInt)
{
waveOutUnprepareHeader (hWaveOutDevice, &waveOutHdrs[myInt], sizeof (WAVEHDR));
waveOutHdrs[myInt].dwFlags = 0;
}
waveOutClose (hWaveOutDevice);
}
///////////////////////////////////////////////////////////////////////////////
LRESULT CALLBACK MainWndProc (HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
///////////////////////////////////////////////////////////////////////////////
{
static lineNo = 0;
static long wvState;
static HWND pbutton, sbutton;
switch (msg)
{
case WM_CREATE:
pbutton = CreateWindow ("button", "PLAY", WS_CHILD | WS_VISIBLE, 0, 0, 100, 100, hWnd, (HMENU)ID_PLAY_BUT, myhInstance, NULL);
sbutton = CreateWindow ("button", "STOP", WS_CHILD | WS_VISIBLE, 100, 0, 100, 100, hWnd, (HMENU)ID_STOP_BUT, myhInstance, NULL);
CreatePlayer ();
EnableWindow (pbutton, TRUE);
EnableWindow (sbutton, FALSE);
wvState = WAVE_PLAYING;
break;
case WM_PAINT:
break;
case WM_COMMAND:
switch (wParam)
{
case ID_PLAY_BUT:
wvState = WAVE_PLAYING;
EnableWindow (pbutton, FALSE);
EnableWindow (sbutton, TRUE);
Play ();
break;
case ID_STOP_BUT:
wvState = WAVE_READY;
EnableWindow (pbutton, TRUE);
EnableWindow (sbutton, FALSE);
Stop ();
break;
case ID_CLOSED:
wvState = WAVE_READY;
EnableWindow (pbutton, TRUE);
EnableWindow (sbutton, FALSE);
break;
case ID_DONEBUFFER:
if (wvState == WAVE_PLAYING)
if (ReadBuffer (lParam))
Stop ();
break;
}
break;
case WM_DESTROY:
if (wvState == WAVE_PLAYING)
Stop ();
PostQuitMessage (0);
break;
default:
return (DefWindowProc (hWnd, msg, wParam, lParam));
}
return TRUE;
}
///////////////////////////////////////////////////////////////////////////////
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow)
///////////////////////////////////////////////////////////////////////////////
{
MSG msg;
WNDCLASS wc;
char *szWindowClass = "WaveTester";
char *szTitle = "Tester";
wc.style = CS_OWNDC;
wc.lpfnWndProc = (WNDPROC)MainWndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon (hInstance, IDI_APPLICATION);
wc.hCursor = LoadCursor (NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH) (COLOR_BACKGROUND);
wc.lpszMenuName = NULL;
wc.lpszClassName = szWindowClass;
RegisterClass (&wc);
myhInstance = hInstance;
hWnd = CreateWindow (szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, 200, 200, 200, 100, NULL, NULL, hInstance, NULL);
ShowWindow (hWnd, nCmdShow);
UpdateWindow (hWnd);
for (; {
if (GetMessage (&msg, NULL, 0, 0) == 0) {
return msg.wParam;
} else {
TranslateMessage (&msg);
DispatchMessage (&msg);
}
}
}
Let me briefly describe the functionality, then describe a mode of operation that works properly under both OS'es, then a mode of operation that fails only under Windows 98.
When the program is started, the wav memory structures are initialized (WM_CREATE calls CreatePlayer). When a file is to be played, Play() is called. This routine opens the file “test.wav” (a 44.1 kHz sampled, stereo file), prepares the headers and fills the first set of buffers. As the wav device renders each buffer, it send a MM_WOM_DONE message to its callback routine. This routine sends a WM_COMMAND message to the main program, which then calls ReadBuffer() to refill the just-emptied buffer. When there is no more data to be read from the file, ReadBuffer() returns a 1, signaling the main program that the file has been completely rendered. The main program then sends a Stop(), which closes the wav device. Note that Stop() does not free any memory as Play() can be called again.
Under both OS'es, if the a file is Play()'ed then Stop()'ed, it can be Play()'ed again without any problems. This is as expected.
Under Win2k, if a file is allowed to Play() to completion, clicking Play() again causes the file to be Play()'ed. This is as expected. However, under Win98, when a file is allowed to Play() to completion, clicking Play() results in waveOutPrepareHeader() returning with an error, MMSYSERR_NOMEM, and the file does not Play().
I have tried a number of ways to solve this problem, including using memory locking, freeing and reallocating memory each time the player Stop()'s, etc. I haven't a clue as to why this is failing in this manner. Your thoughts and suggestions are gratefully accepted!
=== source code ===
#include <windows.h>
#include <winuser.h>
#include <mmsystem.h>
#include <stdio.h>
#define NBUFS_OUT 4L
#define OUTBUFSIZE 0x4000L
#define ID_DONEBUFFER 40000
#define ID_CLOSED 40001
#define ID_PLAY_BUT 40002
#define ID_STOP_BUT 40003
#define WAVE_PLAYING 1
#define WAVE_READY 2
long doneBufCnt = 0;
FILE *fp = NULL;
HWND hWnd = NULL;
HWAVEOUT hWaveOutDevice;
WAVEFORMATEX waveOutFormatEx;
WAVEHDR waveOutHdrs[NBUFS_OUT];
HINSTANCE myhInstance;
///////////////////////////////////////////////////////////////////////////////
void CALLBACK WaveOutCallback (HWAVEOUT hwo, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2)
///////////////////////////////////////////////////////////////////////////////
{
switch (uMsg)
{
case MM_WOM_OPEN: // device opened via waveOutOpen(), nothing to do
break;
case MM_WOM_DONE: // device done playing data sent via waveOutWrite()
SendNotifyMessage (hWnd, WM_COMMAND, ID_DONEBUFFER, ((WAVEHDR *)dwParam1)->dwUser);
break;
case MM_WOM_CLOSE: // device closed via waveOutClose()
SendNotifyMessage (hWnd, WM_COMMAND, ID_CLOSED, 0L);
break;
}
}
///////////////////////////////////////////////////////////////////////////////
long ReadBuffer (long curBuf)
///////////////////////////////////////////////////////////////////////////////
{
if ((waveOutHdrs[curBuf].dwBufferLength = fread (waveOutHdrs[curBuf].lpData, 1, OUTBUFSIZE, fp)) != OUTBUFSIZE)
{
if (feof (fp))
doneBufCnt++;
}
else
waveOutWrite (hWaveOutDevice, &waveOutHdrs[curBuf], sizeof (WAVEHDR));
if (doneBufCnt == NBUFS_OUT)
{
doneBufCnt = 0;
return 1;
}
return 0;
}
///////////////////////////////////////////////////////////////////////////////
void CreatePlayer (void)
///////////////////////////////////////////////////////////////////////////////
{
long myInt;
fp = fopen ("test.wav", "rb"
waveOutFormatEx.wFormatTag = WAVE_FORMAT_PCM;
waveOutFormatEx.nChannels = 2;
waveOutFormatEx.nSamplesPerSec = 44100;
waveOutFormatEx.nAvgBytesPerSec = 176400;
waveOutFormatEx.nBlockAlign = 4;
waveOutFormatEx.wBitsPerSample = 16;
waveOutFormatEx.cbSize = (WORD)0;
for (myInt = 0; myInt < NBUFS_OUT; ++myInt) {
memset (&waveOutHdrs[myInt], 0, sizeof (WAVEHDR));
waveOutHdrs[myInt].lpData = (char *)calloc (OUTBUFSIZE, sizeof(char));
waveOutHdrs[myInt].dwUser = myInt;
waveOutHdrs[myInt].dwBufferLength = OUTBUFSIZE;
}
}
/////////////////////////////////////////////////////////////////////////
void Play (void)
///////////////////////////////////////////////////////////////////////////////
{
long myInt;
DWORD dwInstance = 0;
fseek (fp, 44, SEEK_SET);
waveOutOpen ((LPHWAVEOUT)&hWaveOutDevice, WAVE_MAPPER, &waveOutFormatEx, (DWORD)WaveOutCallback, dwInstance, CALLBACK_FUNCTION);
waveOutPause (hWaveOutDevice);
for (myInt=0; myInt<NBUFS_OUT; myInt++)
{
waveOutPrepareHeader (hWaveOutDevice, &waveOutHdrs[myInt], sizeof (WAVEHDR));
memset (waveOutHdrs[myInt].lpData, 0, OUTBUFSIZE);
ReadBuffer (myInt);
waveOutWrite (hWaveOutDevice, &waveOutHdrs[myInt], sizeof (WAVEHDR));
}
waveOutRestart (hWaveOutDevice);
}
///////////////////////////////////////////////////////////////////////////////
void Stop (void)
///////////////////////////////////////////////////////////////////////////////
{
long myInt;
waveOutReset (hWaveOutDevice);
for (myInt = 0; myInt < NBUFS_OUT; ++myInt)
{
waveOutUnprepareHeader (hWaveOutDevice, &waveOutHdrs[myInt], sizeof (WAVEHDR));
waveOutHdrs[myInt].dwFlags = 0;
}
waveOutClose (hWaveOutDevice);
}
///////////////////////////////////////////////////////////////////////////////
LRESULT CALLBACK MainWndProc (HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
///////////////////////////////////////////////////////////////////////////////
{
static lineNo = 0;
static long wvState;
static HWND pbutton, sbutton;
switch (msg)
{
case WM_CREATE:
pbutton = CreateWindow ("button", "PLAY", WS_CHILD | WS_VISIBLE, 0, 0, 100, 100, hWnd, (HMENU)ID_PLAY_BUT, myhInstance, NULL);
sbutton = CreateWindow ("button", "STOP", WS_CHILD | WS_VISIBLE, 100, 0, 100, 100, hWnd, (HMENU)ID_STOP_BUT, myhInstance, NULL);
CreatePlayer ();
EnableWindow (pbutton, TRUE);
EnableWindow (sbutton, FALSE);
wvState = WAVE_PLAYING;
break;
case WM_PAINT:
break;
case WM_COMMAND:
switch (wParam)
{
case ID_PLAY_BUT:
wvState = WAVE_PLAYING;
EnableWindow (pbutton, FALSE);
EnableWindow (sbutton, TRUE);
Play ();
break;
case ID_STOP_BUT:
wvState = WAVE_READY;
EnableWindow (pbutton, TRUE);
EnableWindow (sbutton, FALSE);
Stop ();
break;
case ID_CLOSED:
wvState = WAVE_READY;
EnableWindow (pbutton, TRUE);
EnableWindow (sbutton, FALSE);
break;
case ID_DONEBUFFER:
if (wvState == WAVE_PLAYING)
if (ReadBuffer (lParam))
Stop ();
break;
}
break;
case WM_DESTROY:
if (wvState == WAVE_PLAYING)
Stop ();
PostQuitMessage (0);
break;
default:
return (DefWindowProc (hWnd, msg, wParam, lParam));
}
return TRUE;
}
///////////////////////////////////////////////////////////////////////////////
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow)
///////////////////////////////////////////////////////////////////////////////
{
MSG msg;
WNDCLASS wc;
char *szWindowClass = "WaveTester";
char *szTitle = "Tester";
wc.style = CS_OWNDC;
wc.lpfnWndProc = (WNDPROC)MainWndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon (hInstance, IDI_APPLICATION);
wc.hCursor = LoadCursor (NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH) (COLOR_BACKGROUND);
wc.lpszMenuName = NULL;
wc.lpszClassName = szWindowClass;
RegisterClass (&wc);
myhInstance = hInstance;
hWnd = CreateWindow (szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, 200, 200, 200, 100, NULL, NULL, hInstance, NULL);
ShowWindow (hWnd, nCmdShow);
UpdateWindow (hWnd);
for (; {
if (GetMessage (&msg, NULL, 0, 0) == 0) {
return msg.wParam;
} else {
TranslateMessage (&msg);
DispatchMessage (&msg);
}
}
}