public WaveOutBuffer(int size)
 {
     _hdr                = new Native.WAVEHDR();
     _gchThis            = GCHandle.Alloc(this);
     _gchHdr             = GCHandle.Alloc(_hdr, GCHandleType.Pinned);
     _hdr.dwUser         = (IntPtr)_gchThis;
     _data               = new byte[size];
     _gchData            = GCHandle.Alloc(_data, GCHandleType.Pinned);
     _hdr.lpData         = _gchData.AddrOfPinnedObject();
     _hdr.dwBufferLength = size;
 }
        internal static void WaveOutProc(IntPtr hdrvr, int uMsg, IntPtr dwInstance, IntPtr dwParam1, IntPtr dwParam2)
        {
            if (uMsg == Native.MM_WOM_DONE)
            {
                try
                {
                    System.Diagnostics.Debug.Assert(dwInstance != IntPtr.Zero);
                    WinMMAudioRender instance = (WinMMAudioRender)((GCHandle)dwInstance).Target;

                    System.Diagnostics.Debug.Assert(dwParam1 != IntPtr.Zero);
                    Native.WAVEHDR hdr = (Native.WAVEHDR)Marshal.PtrToStructure(dwParam1, typeof(Native.WAVEHDR));

                    System.Diagnostics.Debug.Assert(hdr.dwUser != IntPtr.Zero);
                    WaveOutBuffer waveOutBuffer = (WaveOutBuffer)((GCHandle)hdr.dwUser).Target;

                    if (hdr.dwBufferLength > 0)
                    {
                        instance._totalDuration += hdr.dwBufferLength;
                        instance._callback.PlaybackProgress(instance._totalDuration, hdr.dwBufferLength, instance._fmt.nAvgBytesPerSec);
                    }

                    // if we are still playing get a new audio buffer
                    if (instance._started)
                    {
                        int length = 0;

                        if (instance._callback.NextAudioBuffer(waveOutBuffer._data, ref length))
                        {
                            System.Diagnostics.Debug.Assert(length <= waveOutBuffer._data.Length);

                            waveOutBuffer._hdr.dwBufferLength = length;

                            int result = Native.waveOutWrite(instance._hWaveout, waveOutBuffer._gchHdr.AddrOfPinnedObject(), Marshal.SizeOf(waveOutBuffer._hdr));
                            System.Diagnostics.Debug.Assert(result == Native.MMSYSERR_NOERROR);
                        }
                        else
                        {
                            // end of stream read
                            instance._started = false;
                        }
                    }
                }
                catch (Exception ex)
                {
                    System.Diagnostics.Debug.WriteLine(ex.ToString());
                }
            }
        }