[DllImport("winmm.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern uint waveOutPrepareHeader(IntPtr hWaveOut, IntPtr pwh, int uSize);
// Need to use IntPtr as WAVEHEADER struct must be in a fixed memory location. See Tips & Tricks below
Declare Function waveOutPrepareHeader Lib "winmm.dll" (TODO) As TODO
None.
Do you know one? Please contribute it!
None.
The same WAVEHDR structure used with waveOutPrepareHeader() is also used with
the waveOutWrite() and waveOutUnPrepareHeader() functions. The latter is called
after the audio playback has been stopped with a call to waveOutReset(); The
audio driver will asynchronously set the WHDR_DONE bit in WAVEHDR.dwflags when
it has released the audio data block.
This means that the WAVEHDR struct passed to the waveOutWrite() function must be
allocated in unmanaged memory so it will survive after the call to waveOutWrite().
What I do is preallocate a block of unmanaged memory in my class's Open() method:
waveHdrPtr = Marshal.AllocHGlobal(Marshal.SizeOf(waveHdr));
Then in the output method I use this:
waveHdr.lpData = wavedata; // audio buffer
waveHdr.dwBufferLength = wf.nSamplesPerSec * wf.nBlockAlign; // it's size
waveHdr.dwFlags = 0; // clear before waveOutPrepareHeader()
Marshal.StructureToPtr(waveHdr, waveHdrPtr, true); // copy to unmanaged memory
if ((MMRESULT = waveOutPrepareHeader(waveDevice, waveHdrPtr, Marshal.SizeOf(waveHdr))) != MMSYSERR_NOERROR)
{
waveOutGetErrorText(MMRESULT, errmsg, MAXERRORLENGTH);
MessageBox.Show(
"waveOutPrepareHeader(): " + errmsg.ToString(),
this.Text,
MessageBoxButtons.OK,
MessageBoxIcon.Error);
return;
}
WAVEHDR wh = (WAVEHDR)Marshal.PtrToStructure(waveHdrPtr, typeof(WAVEHDR)); // copy struct back from unmanaged memory
waveHdr = wh; // reset managed struct
waveHdr.dwFlags |= (WHDR_BEGINLOOP | WHDR_ENDLOOP); // add the looping flag bits
waveHdr.dwLoops = MINUS_ONE; ;
Marshal.StructureToPtr(waveHdr, waveHdrPtr, true); // and update the unmanaged struct.
Now we are ready to write:
if ((MMRESULT = waveOutWrite(waveDevice, waveHdrPtr, Marshal.SizeOf(waveHdr))) != MMSYSERR_NOERROR)
[snipped]
When we are done listening to this playback:
while ((waveHdr.dwFlags & WHDR_DONE) == 0) // wait for it
{
if ((MMRESULT = waveOutReset(waveDevice)) != MMSYSERR_NOERROR)
{
waveOutGetErrorText(MMRESULT, errmsg, MAXERRORLENGTH);
MessageBox.Show(
"waveOutReset(): " + errmsg.ToString(),
this.Text,
MessageBoxButtons.OK,
MessageBoxIcon.Error);
}
WAVEHDR wh = (WAVEHDR)Marshal.PtrToStructure(waveHdrPtr, typeof(WAVEHDR)); // copy unmanaged struct
waveHdr = wh; // update managed struct
}
When the WHDR_DONE bit sets we can call the waveOutUnPrepareHeader() function to release
the resources previously set up:
if ((MMRESULT = waveOutUnprepareHeader(waveDevice, waveHdrPtr, Marshal.SizeOf(waveHdr))) != MMSYSERR_NOERROR)
[snipped]
I release the unmanaged memory allocated in the Open() method in the class's Close method:
Marshal.FreeHGlobal(waveHdrPtr);
Please add some!