Newer
Older
Correlator / PipeGallery / Correlator / WaveOut.cs
using System;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;

namespace PipeGallery.Correlator
{
    internal class WaveOutHelper
    {
        public static void Try(int err)
        {
            if (err != WaveOutBase.MMSYSERR_NOERROR)
                throw new Exception(err.ToString());
        }
    }

    public delegate void BufferFillEventHandler(IntPtr data, int size);

    internal class WaveOutBuffer : IDisposable
    {
        public WaveOutBuffer NextBuffer;

        private AutoResetEvent m_PlayEvent = new AutoResetEvent(false);
        private IntPtr m_WaveOut;

        private WaveOutBase.WaveHdr m_Header;
        private byte[] m_HeaderData;
        private GCHandle m_HeaderHandle;
        private GCHandle m_HeaderDataHandle;

        private int mark;

        private bool m_Playing;

        internal static void WaveOutProc(IntPtr hdrvr, int uMsg, int dwUser, ref WaveOutBase.WaveHdr wavhdr,
            int dwParam2)
        {
            if (uMsg == WaveOutBase.MM_WOM_DONE)
            {
                try
                {
                    GCHandle h = (GCHandle)wavhdr.dwUser;
                    WaveOutBuffer buf = (WaveOutBuffer)h.Target;
                    buf.OnCompleted();
                }
                catch
                {
                }
            }
        }

        public WaveOutBuffer(IntPtr waveOutHandle, int size)
        {
            m_WaveOut = waveOutHandle;

            m_HeaderHandle = GCHandle.Alloc(m_Header, GCHandleType.Pinned);
            m_Header.dwUser = (IntPtr)GCHandle.Alloc(this);
            m_HeaderData = new byte[size];
            m_HeaderDataHandle = GCHandle.Alloc(m_HeaderData, GCHandleType.Pinned);
            m_Header.lpData = m_HeaderDataHandle.AddrOfPinnedObject();
            m_Header.dwBufferLength = size;
            WaveOutHelper.Try(WaveOutBase.waveOutPrepareHeader(m_WaveOut, ref m_Header, Marshal.SizeOf(m_Header)));
        }

        public void set(int mark)
        {
            this.mark = mark;
        }

        ~WaveOutBuffer()
        {
            Dispose();
        }

        public void Dispose()
        {
            if (m_Header.lpData != IntPtr.Zero)
            {
                WaveOutBase.waveOutUnprepareHeader(m_WaveOut, ref m_Header, Marshal.SizeOf(m_Header));
                m_HeaderHandle.Free();
                m_Header.lpData = IntPtr.Zero;
            }

            m_PlayEvent.Close();
            if (m_HeaderDataHandle.IsAllocated)
                m_HeaderDataHandle.Free();
            GC.SuppressFinalize(this);
        }

        public int Size
        {
            get { return m_Header.dwBufferLength; }
        }

        public IntPtr Data
        {
            get { return m_Header.lpData; }
        }

        public void getMemory(string title)
        {
            Console.WriteLine("{0}: {1}, {2}", title, m_Playing, mark);
        }

        public bool Play()
        {
            lock (this)
            {
                getMemory("Play");
                m_PlayEvent.Reset();
                m_Playing = WaveOutBase.waveOutWrite(m_WaveOut, ref m_Header, Marshal.SizeOf(m_Header)) ==
                            WaveOutBase.MMSYSERR_NOERROR;
                return m_Playing;
            }
        }

        public void WaitFor()
        {
            getMemory("WaitFor");
            if (m_Playing)
            {
                m_Playing = m_PlayEvent.WaitOne();
            }
            else
            {
                //Thread.Sleep(0);
            }
        }

        public void OnCompleted()
        {
            getMemory("OnCompleted");
            m_PlayEvent.Set();
            m_Playing = false;
        }
    }

    public class WaveOut : IDisposable
    {
        private IntPtr m_WaveOut;
        private WaveOutBuffer m_Buffers; // linked list
        private WaveOutBuffer m_CurrentBuffer;
        private Task m_Thread;
        private BufferFillEventHandler m_FillProc;
        private bool m_Finished;

        private bool m_Pause;

        //public bool bPause
        //{
        //    get { return m_Pause; }
        //    set { m_Pause = value; }
        //}
        private byte m_zero;

        private WaveOutBase.WaveDelegate m_BufferProc = new WaveOutBase.WaveDelegate(WaveOutBuffer.WaveOutProc);

        public static int DeviceCount
        {
            get { return WaveOutBase.waveOutGetNumDevs(); }
        }

        public WaveOut(int device, WaveFormat format, int bufferSize, int bufferCount, BufferFillEventHandler fillProc)
        {
            m_zero = format.wBitsPerSample == 8 ? (byte)128 : (byte)0;
            m_FillProc = fillProc;
            WaveOutHelper.Try(WaveOutBase.waveOutOpen(out m_WaveOut, device, format, m_BufferProc, 0,
                WaveOutBase.CALLBACK_FUNCTION));
            AllocateBuffers(bufferSize, bufferCount);
            /* 线程优化1   
            
            m_Thread = new Thread(new ThreadStart(ThreadProc));
            m_Thread.Start();
             */
            m_Thread = new Task(() => ThreadProc());
            m_Thread.Start();
        }

        ~WaveOut()
        {
            Dispose();
        }

        public void Pause()
        {
            m_Pause = true;
            WaveOutBase.waveOutPause(m_WaveOut);
            //WaveOutBase.waveOutReset(m_WaveOut);
        }

        public void Resume()
        {
            m_Pause = false;
            WaveOutBase.waveOutRestart(m_WaveOut);
        }

        public void Dispose()
        {
            if (m_Thread != null)
                try
                {
                    m_Pause = false;
                    m_Finished = true;
                    if (m_WaveOut != IntPtr.Zero)
                        WaveOutBase.waveOutReset(m_WaveOut);

                    //m_Thread.Abort();
                    //m_Thread.Join();

                    m_FillProc = null;
                    FreeBuffers();
                    if (m_WaveOut != IntPtr.Zero)
                        WaveOutBase.waveOutClose(m_WaveOut);
                }
                finally
                {
                    m_Thread = null;
                    m_WaveOut = IntPtr.Zero;
                }

            GC.SuppressFinalize(this);
        }

        private void ThreadProc()
        {
            m_Pause = false;
            while (!m_Finished)
            {
                // pause
                while (m_Pause)
                {
                    Thread.Sleep(10);
                }

                Advance();
                if (m_FillProc != null && !m_Finished)
                {
                    m_FillProc(m_CurrentBuffer.Data, m_CurrentBuffer.Size);
                }
                else if (m_CurrentBuffer != null)
                {
                    // zero out buffer
                    byte v = m_zero;
                    byte[] b = new byte[m_CurrentBuffer.Size];
                    for (int i = 0; i < b.Length; i++)
                        b[i] = v;
                    Marshal.Copy(b, 0, m_CurrentBuffer.Data, b.Length);
                }

                if (m_CurrentBuffer != null)
                {
                    m_CurrentBuffer.Play();
                }
            }

            WaitForAllBuffers();
        }

        private void AllocateBuffers(int bufferSize, int bufferCount)
        {
            FreeBuffers();
            if (bufferCount > 0)
            {
                m_Buffers = new WaveOutBuffer(m_WaveOut, bufferSize);
                m_Buffers.set(0);
                WaveOutBuffer Prev = m_Buffers;
                try
                {
                    for (int i = 1; i < bufferCount; i++)
                    {
                        WaveOutBuffer Buf = new WaveOutBuffer(m_WaveOut, bufferSize);
                        Buf.set(i);
                        Prev.NextBuffer = Buf;
                        Prev = Buf;
                    }
                }
                finally
                {
                    Prev.NextBuffer = m_Buffers;
                }
            }
        }

        private void FreeBuffers()
        {
            m_CurrentBuffer = null;
            if (m_Buffers != null)
            {
                WaveOutBuffer First = m_Buffers;
                m_Buffers = null;

                WaveOutBuffer Current = First;
                do
                {
                    WaveOutBuffer Next = Current.NextBuffer;
                    Current.Dispose();
                    Current = Next;
                } while (Current != First);
            }
        }

        private void Advance()
        {
            m_CurrentBuffer = m_CurrentBuffer == null ? m_Buffers : m_CurrentBuffer.NextBuffer;
            if (m_CurrentBuffer != null)
            {
                m_CurrentBuffer.WaitFor();
            }
        }

        private void WaitForAllBuffers()
        {
            WaveOutBuffer Buf = m_Buffers;
            while (m_Buffers != null && Buf.NextBuffer != m_Buffers)
            {
                Buf.WaitFor();
                Buf = Buf.NextBuffer;
            }
        }
    }
}