using System; using System.Threading; using System.Runtime.InteropServices; namespace SafeMobileLib { internal class WaveInHelper { public static void Try(int err) { if (err != WaveNative.MMSYSERR_NOERROR) throw new Exception(err.ToString()); } } public delegate void BufferDoneEventHandler(IntPtr data, int size); internal class WaveInBuffer : IDisposable { public WaveInBuffer NextBuffer; private AutoResetEvent m_RecordEvent = new AutoResetEvent(false); private IntPtr m_WaveIn; private WaveNative.WaveHdr m_Header; private byte[] m_HeaderData; private GCHandle m_HeaderHandle; private GCHandle m_HeaderDataHandle; private bool m_Recording; internal static void WaveInProc(IntPtr hdrvr, int uMsg, int dwUser, ref WaveNative.WaveHdr wavhdr, int dwParam2) { if (uMsg == WaveNative.MM_WIM_DATA) { try { GCHandle h = (GCHandle)wavhdr.dwUser; WaveInBuffer buf = (WaveInBuffer)h.Target; buf.OnCompleted(); } catch(Exception ee) { Utils.WriteLine(ee.ToString(), ConsoleColor.Red); } } } public WaveInBuffer(IntPtr waveInHandle, int size) { m_WaveIn = waveInHandle; 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; WaveInHelper.Try(WaveNative.waveInPrepareHeader(m_WaveIn, ref m_Header, Marshal.SizeOf(m_Header))); } ~WaveInBuffer() { Dispose(); } public void Dispose() { if (m_Header.lpData != IntPtr.Zero) { WaveNative.waveInUnprepareHeader(m_WaveIn, ref m_Header, Marshal.SizeOf(m_Header)); m_HeaderHandle.Free(); m_Header.lpData = IntPtr.Zero; } m_RecordEvent.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 bool Record() { lock(this) { m_RecordEvent.Reset(); m_Recording = WaveNative.waveInAddBuffer(m_WaveIn, ref m_Header, Marshal.SizeOf(m_Header)) == WaveNative.MMSYSERR_NOERROR; return m_Recording; } } public void WaitFor() { if (m_Recording) m_Recording = m_RecordEvent.WaitOne(); else Thread.Sleep(0); } public void WaitForLimit500() { if (m_Recording) m_Recording = m_RecordEvent.WaitOne(500); else Thread.Sleep(0); } private void OnCompleted() { m_RecordEvent.Set(); m_Recording = false; } public void SetRecordingOFF() { m_Recording = false; } } public class WaveInRecorder : IDisposable { private IntPtr m_WaveIn; private WaveInBuffer m_Buffers; // linked list private WaveInBuffer m_CurrentBuffer; private Thread m_Thread; private BufferDoneEventHandler m_DoneProc; private bool m_Finished; private WaveNative.WaveDelegate m_BufferProc = new WaveNative.WaveDelegate(WaveInBuffer.WaveInProc); public static int DeviceCount { get { return WaveNative.waveInGetNumDevs(); } } public WaveInRecorder(int device, WaveFormat format, int bufferSize, int bufferCount, BufferDoneEventHandler doneProc) { m_DoneProc = doneProc; WaveInHelper.Try(WaveNative.waveInOpen(out m_WaveIn, device, format, m_BufferProc, 0, WaveNative.CALLBACK_FUNCTION)); AllocateBuffers(bufferSize, bufferCount); for (int i = 0; i < bufferCount; i++) { SelectNextBuffer(); m_CurrentBuffer.Record(); } WaveInHelper.Try(WaveNative.waveInStart(m_WaveIn)); m_Thread = new Thread(new ThreadStart(ThreadProc)); m_Thread.Start(); } ~WaveInRecorder() { Dispose(); } public void Dispose() { if (m_Thread != null) try { m_Finished = true; Console.WriteLine("Dispose WaveIN STEP1"); if (m_WaveIn != IntPtr.Zero) WaveNative.waveInReset(m_WaveIn); Console.WriteLine("Dispose WaveIN STEP2"); WaitForAllBuffers(); Console.WriteLine("Dispose WaveIN STEP3"); m_Thread.Join(500); Console.WriteLine("Dispose WaveIN STEP4"); m_DoneProc = null; Console.WriteLine("Dispose WaveIN STEP5"); FreeBuffers(); Console.WriteLine("Dispose WaveIN STEP6"); if (m_WaveIn != IntPtr.Zero) WaveNative.waveInClose(m_WaveIn); Console.WriteLine("Dispose WaveIN STEP7"); } finally { m_Thread = null; Console.WriteLine("Dispose WaveIN STEP8"); m_WaveIn = IntPtr.Zero; Console.WriteLine("Dispose WaveIN STEP9"); } GC.SuppressFinalize(this); } private void ThreadProc() { while (!m_Finished) { Advance(); if (m_DoneProc != null && !m_Finished) m_DoneProc(m_CurrentBuffer.Data, m_CurrentBuffer.Size); m_CurrentBuffer.Record(); Thread.Sleep(1); } } private void AllocateBuffers(int bufferSize, int bufferCount) { FreeBuffers(); if (bufferCount > 0) { m_Buffers = new WaveInBuffer(m_WaveIn, bufferSize); WaveInBuffer Prev = m_Buffers; try { for (int i = 1; i < bufferCount; i++) { WaveInBuffer Buf = new WaveInBuffer(m_WaveIn, bufferSize); Prev.NextBuffer = Buf; Prev = Buf; } } finally { Prev.NextBuffer = m_Buffers; } } } private void FreeBuffers() { m_CurrentBuffer = null; if (m_Buffers != null) { WaveInBuffer First = m_Buffers; m_Buffers = null; Int32 deadin20steps = 0; WaveInBuffer Current = First; do { deadin20steps++; WaveInBuffer Next = Current.NextBuffer; Current.Dispose(); Current = Next; } while((Current != First)&& (deadin20steps < 20)); } } private void Advance() { SelectNextBuffer(); m_CurrentBuffer.WaitFor(); } private void SelectNextBuffer() { m_CurrentBuffer = m_CurrentBuffer == null ? m_Buffers : m_CurrentBuffer.NextBuffer; } private void WaitForAllBuffers() { WaveInBuffer Buf = m_Buffers; Int32 maxim20Buffer = 0; while ((Buf.NextBuffer != m_Buffers)&&(maxim20Buffer<20)) { maxim20Buffer++; Console.WriteLine("Count of buffers:"+maxim20Buffer); Buf.SetRecordingOFF(); Buf.WaitForLimit500(); Buf = Buf.NextBuffer; } } } }