/// <summary>
 /// Cleans up any resources being used.
 /// </summary>
 public void Dispose()
 {
     if (m_IsDisposed)
     {
         return;
     }
     m_IsDisposed = true;
     lock (m_pPlayItems) {
         try {
             // If playing, we need to reset wav device first.
             WavMethods.waveOutReset(m_pWavDevHandle);
             // If there are unprepared wav headers, we need to unprepare these.
             foreach (PlayItem item in m_pPlayItems)
             {
                 WavMethods.waveOutUnprepareHeader(m_pWavDevHandle, item.HeaderHandle.AddrOfPinnedObject(), Marshal.SizeOf(item.Header));
                 item.Dispose();
             }
             // Close output device.
             WavMethods.waveOutClose(m_pWavDevHandle);
             m_pOutDevice    = null;
             m_pWavDevHandle = IntPtr.Zero;
             m_pPlayItems    = null;
             m_pWaveOutProc  = null;
         } catch {
         }
     }
 }
        /// <summary>
        /// Default constructor.
        /// </summary>
        /// <param name="outputDevice">Output device.</param>
        /// <param name="samplesPerSec">Sample rate, in samples per second (hertz). For PCM common values are
        /// 8.0 kHz, 11.025 kHz, 22.05 kHz, and 44.1 kHz.</param>
        /// <param name="bitsPerSample">Bits per sample. For PCM 8 or 16 are the only valid values.</param>
        /// <param name="channels">Number of channels.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>outputDevice</b> is null.</exception>
        /// <exception cref="ArgumentException">Is raised when any of the aruments has invalid value.</exception>
        public WaveOut(WavOutDevice outputDevice, int samplesPerSec, int bitsPerSample, int channels)
        {
            if (outputDevice == null)
            {
                throw new ArgumentNullException("outputDevice");
            }
            if (samplesPerSec < 8000)
            {
                throw new ArgumentException("Argument 'samplesPerSec' value must be >= 8000.");
            }
            if (bitsPerSample < 8)
            {
                throw new ArgumentException("Argument 'bitsPerSample' value must be >= 8.");
            }
            if (channels < 1)
            {
                throw new ArgumentException("Argument 'channels' value must be >= 1.");
            }

            m_pOutDevice    = outputDevice;
            m_SamplesPerSec = samplesPerSec;
            m_BitsPerSample = bitsPerSample;
            m_Channels      = channels;
            m_BlockSize     = m_Channels * (m_BitsPerSample / 8);
            m_pPlayItems    = new List <PlayItem>();

            // Try to open wav device.
            WAVEFORMATEX format = new WAVEFORMATEX();

            format.wFormatTag     = WavFormat.PCM;
            format.nChannels      = (ushort)m_Channels;
            format.nSamplesPerSec = (uint)samplesPerSec;
            //1秒钟数据量(字节/秒)= (采样频率(Hz)*采样位数(bit)*声道数)/ 8
            //换算成20ms的数据量则再除50
            format.nAvgBytesPerSec = (uint)((m_SamplesPerSec * m_Channels * (m_BitsPerSample / 8))) / 50;

            format.nBlockAlign    = (ushort)m_BlockSize;
            format.wBitsPerSample = (ushort)m_BitsPerSample;
            format.cbSize         = 0;

            m_MinBuffer = (int)format.nAvgBytesPerSec * 10;
            // We must delegate reference, otherwise GC will collect it.
            m_pWaveOutProc = new waveOutProc(this.OnWaveOutProc);
            int result = WavMethods.waveOutOpen(out m_pWavDevHandle, m_pOutDevice.Index, format, m_pWaveOutProc, 0, WavConstants.CALLBACK_FUNCTION);

            if (result != MMSYSERR.NOERROR)
            {
                throw new Exception("Failed to open wav device, error: " + result.ToString() + ".");
            }
        }