///
/// Returns the next header we should write to, or blocks until
/// one is available. The returned header is done and prepared.
///
void _GetFreeHeader( out GCHandle nextIntPtr,
out WAVEHDR nextAsHeader )
{
while (true)
{
// Clear the wave event before checking "done" to
// reduce the race condition between checking and
// waiting on the event. (it's manual reset...)
_playEvent.Reset();
// Note that the header memory fields will be updated
// by the wave engine at the same time. Oooh, scary!
// OK, so we're just checking one bit in an int, it's not
// really dangerous.
nextFree = _waveHdr[_nextFreeHeader];
nextAsHeader = (WAVEHDR)nextFree.Target;
nextIntPtr = nextIntPtr.ToIntPtr();
if (nextAsHeader.dwFlags & WHDR_DONE)
{
if (nextAsHeader.dwFlags & WHDR_PREPARED)
{
int status = waveOutUnprepareHeader
( _waveOutHandle,
nextIntPtr,
Marshal.sizeof(typeof(WAVEHDR)) );
if (status != MMSYSERR_NOERROR)
{
// Ugh? Still playing? But WHDR_DONE is set!
throw new ApplicationException(
String.Format( "Error '{0}' unpreparing wave header",
status )
);
}
}
// Switch buffers. Whee.
_nextFreeHeader = _nextFreeHeader ^ 0x01;
return;
}
// The next buffer is not done. Wait for it to be done.
// Note there is a race condition here. :(
_playEvent.WaitOne( 500, false );
// Could check here to avoid non-buffer-done type events,
// but really it doesn't matter.
}
}