声音处理程序使用 C#,把声音采集程序合并到一块,不用在界面运行两个程序了。图个简洁。以下是改造网上的c#声音采集程序,运行ok。今天晚上加把劲赶出来吧,天晴了,明天要干活了。可以先看一看当时实现的心情:有调试记录记载如下:

16:33 2018-3-20 其实,这只是一个音wu(钨)的传说。(为什么是钨呢?爱迪生的灯泡尝试了多少失败,以此勉励)
10:09 2018-3-25 去噪后,效果明显。可遇见的未来(给自己的孩子看)。一个是发音准确了,一个是噪音控制住了。另外尝试统计后,总结,应再推进一步。程序已经真正的合为一体了,仅有c#表达。距离自己的一堂课(拿得出手)还有些距离。(注:虽然实现了,但是前面已经评价过了,半桶水)

好,下面是程序,c#比起c++,简单多了,如释负重。这个程序的名字叫soundctrlzoom(注:声音控制图像缩放),我们只摘抄采集声音部分,剩下的以后会在项目中再续。

首先,声明: 

/// <summary>
         ///  //for audio record
         /// </summary>
         public const short device = (-1);
         public WaveFormat format = new WaveFormat();
         private WaveInRecorder m_WIR;
         public byte[] m_RecBuffer;

有WaveFormat和WaveInRecorder两个类型不认识,继续

public enum WaveFormats
     {
         Pcm = 1,
         Float = 3
     }
     [StructLayout(LayoutKind.Sequential)]WaveFormat
     {
         public short wFormatTag;
         public short nChannels;
         public int nSamplesPerSec;
         public int nAvgBytesPerSec;
         public short nBlockAlign;
         public short wBitsPerSample;
         public short cbSize;c++中你也可以看到,它是一个固定的选项,波形格式(不懂,我们不改变它)
         {
             wFormatTag = (short)WaveFormats.Pcm;
             nChannels = 1;
             nSamplesPerSec = 8000;//仍然是每秒8000的样本采样
             nAvgBytesPerSec = 8000;
             nBlockAlign = 1;
             wBitsPerSample = 8;
             cbSize = 0;
         }
     }///再来 
回调函数被委托替代
     public delegate void BufferDoneEventHandler(IntPtr data, int size);WaveInRecorder : IDisposable//声音采集过程,也封装成类了,c#中思路很清晰,有没有这种感觉?
     {
         private IntPtr m_WaveIn;
         public int bufferSize;
         public int bufferCount;
         private WaveInBuffer m_Buffers; // linked list
         private WaveInBuffer m_CurrentBuffer;
         private Thread m_Thread;
         private BufferDoneEventHandler m_DoneProc;
         private bool m_Finished = false;//这里使用第二个委托替代回调函数,可以对照着看,加深印象
WaveNative.WaveDelegate m_BufferProc = new WaveNative.WaveDelegate(WaveInBuffer.WaveInProc);
        public static int DeviceCount
         {
             get { return WaveNative.waveInGetNumDevs(); }
         } BufferDoneEventHandler doneProc)
         {
             m_DoneProc = doneProc;
             this.bufferCount = bufferCount;
             this.bufferSize = bufferSize;
             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;
                     if (m_WaveIn != IntPtr.Zero)
                         WaveNative.waveInReset(m_WaveIn);
                     WaitForAllBuffers();
                     m_Thread.Join();
                     m_DoneProc = null;
                     FreeBuffers();
                     if (m_WaveIn != IntPtr.Zero)
                         WaveNative.waveInClose(m_WaveIn);
                 }
                 finally
                 {
                     m_Thread = null;
                     m_WaveIn = IntPtr.Zero;
                 }
             GC.SuppressFinalize(this);
         }
         private void WaitForAllBuffers()
         {
             WaveInBuffer Buf = m_Buffers;
             while (Buf.NextBuffer != m_Buffers)
             {
                 Buf.WaitFor();
                 Buf = Buf.NextBuffer;
             }
         }
         private void ThreadProc()
         {
             while (!m_Finished)
             {
                 Advance();
                 if (m_DoneProc != null && !m_Finished)
                     m_DoneProc(m_CurrentBuffer.Data, m_CurrentBuffer.Size);
                 m_CurrentBuffer.Record();
             }
         }
         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;                WaveInBuffer Current = First;
                 do
                 {
                     WaveInBuffer Next = Current.NextBuffer;
                     Current.Dispose();
                     Current = Next;
                 } while (Current != First);
             }
         }
         private void Advance()
         {
             SelectNextBuffer();
             m_CurrentBuffer.WaitFor();
         }        private void SelectNextBuffer()
         {
             m_CurrentBuffer = m_CurrentBuffer == null ? m_Buffers : m_CurrentBuffer.NextBuffer;
         }    }

其实可以对比C++版本,更容易理解。是

WaveInBuffer是什么鬼?实质相当于记录声音的内存块,在这里是一个类,封装了对内存块的操作,这不是更好,更安全吗?

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
                 {
                 }
             }
         }        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;
                 //  System.Diagnostics.Debug.Write(string.Format("\n m_WaveIn: {0}, m_Header.dwBytesRecorded: {1}", m_WaveIn, m_Header.dwBytesRecorded));
                 //m_WaveIn.ToPointer();
                 return m_Recording;
             }
         }        public void WaitFor()
         {
             if (m_Recording)
                 m_Recording = m_RecordEvent.WaitOne();
             else
                 Thread.Sleep(0);
         }        private void OnCompleted()
         {
             m_RecordEvent.Set();
             m_Recording = false;
         }
     }

WaveNative是什么鬼?实质是封装的微软采集声音的API函数,这里也封装成类了。

internal class WaveNative //: IDisposable
     {
         // consts
         public const int MMSYSERR_NOERROR = 0; // no error        public const int MM_WOM_OPEN = 0x3BB;
         public const int MM_WOM_CLOSE = 0x3BC;
         public const int MM_WOM_DONE = 0x3BD;        public const int MM_WIM_OPEN = 0x3BE;
         public const int MM_WIM_CLOSE = 0x3BF;
         public const int MM_WIM_DATA = 0x3C0;        public const int CALLBACK_FUNCTION = 0x00030000;    // dwCallback is a FARPROC
         // callbacks
         public delegate void WaveDelegate(IntPtr hdrvr, int uMsg, int dwUser, ref WaveHdr wavhdr, int dwParam2);
         private WaveDelegate m_BufferProc = new WaveDelegate(WaveInBuffer.WaveInProc);        [StructLayout(LayoutKind.Sequential)]
         public struct WaveHdr
         {
             public IntPtr lpData; // pointer to locked data buffer
             public int dwBufferLength; // length of data buffer
             public int dwBytesRecorded; // used for input only
             public IntPtr dwUser; // for client's use
             public int dwFlags; // assorted flags (see defines)
             public int dwLoops; // loop control counter
             public IntPtr lpNext; // PWaveHdr, reserved for driver
             public int reserved; // reserved for driver
         }
         [DllImport("winmm.dll")]
         public static extern int waveInOpen(out IntPtr phwi, int uDeviceID, WaveFormat lpFormat, WaveDelegate dwCallback, int dwInstance, int dwFlags);
         [DllImport("winmm.dll")]
         public static extern int waveInAddBuffer(IntPtr hwi, ref WaveHdr pwh, int cbwh);
         [DllImport("winmm.dll")]
         public static extern int waveInPrepareHeader(IntPtr hWaveIn, ref WaveHdr lpWaveInHdr, int uSize);
         [DllImport("winmm.dll")]
         public static extern int waveInUnprepareHeader(IntPtr hWaveIn, ref WaveHdr lpWaveInHdr, int uSize);
         [DllImport("winmm.dll")]
         public static extern int waveInStart(IntPtr hwi);
         [DllImport("winmm.dll")]
         public static extern int waveInGetNumDevs();
         [DllImport("winmm.dll")]
         public static extern int waveInReset(IntPtr hwi);
         [DllImport("winmm.dll")]
         public static extern int waveInClose(IntPtr hwi);
     }
     internal class WaveInHelper
     {
         public static void Try(int err)
         {
             if (err != WaveNative.MMSYSERR_NOERROR)
                 throw new Exception(err.ToString());
         }
     }

好,所有准备工作完成,哎哎哎,有一个委托怎么回事?好,该他上场了:在form中,增加一个按钮用来触发声音采集:

private void startRec_Click(object sender, EventArgs e)
         {          
             m_WIR = new WaveInRecorder(device, format, 1920, 5, new BufferDoneEventHandler(DataArrived));
         }

看见这个委托了吧,他的实现就是DataArrived,以下就是实现:先声明一个1920字节的数组,用来接收波形,在form中到来的波形会显示在picturebox1中。

// public byte[] m_RecBuffer;//开头已经声明过了
   public void DataArrived(IntPtr data, int size)//此处为委托,不同于DefWndProc201803201439
         {
             try
             {
                 if (m_RecBuffer == null || m_RecBuffer.Length < size)
                 {
                     m_RecBuffer = new byte[size];
                 }
                      
                 int _RoiW = 480;//图像分成四段显示480*4=1920
                 int _RoiH = 768;//也没有什么特殊原因,只是屏幕小,还有其他占用,你也可以一次显示1920,不用如此麻烦
            
                 //显示
                 byte[] cutvalues = new byte[_RoiW * _RoiH * 3];                int bytes = _RoiW * _RoiH * 3;
                Bitmap cutPic24 = new Bitmap(_RoiW, _RoiH, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
                BitmapData _cutPic = cutPic24.LockBits(new Rectangle(0, 0, _RoiW, _RoiH), ImageLockMode.ReadWrite,
                                                      cutPic24.PixelFormat);                IntPtr ptr = _cutPic.Scan0;//得到首地址
                 int n = 0;
                 for (int i = 0; i < _RoiH; i++)//图像复原,即由8位图变成24位图像
                 {
                     for (int j = 0; j < _RoiW; j++)
                     {
                         int m = 3 * (i * _RoiW + j);
                         if (i <= 255)
                         {
                             n = 0 * _RoiW + j;
                         }
                         else
                         {
                             n = 1 * _RoiW + j;
                         }
                         cutvalues[m] = 0;
                         cutvalues[m + 1] = 0;
                         cutvalues[m + 2] = 0;
                     }
                 }                for (int j = 0; j < _RoiW; j++)
                 {                 
                         int m = 3 * ((m_RecBuffer[j]) * _RoiW + j);                    
                         cutvalues[m + 2] = 255;                      }      
                 for (int j = 0; j < _RoiW; j++)
                 {
                     int m = 3 * ((128) * _RoiW + j);                    cutvalues[m] = 255;
                     cutvalues[m + 1] = 255;
                     cutvalues[m + 2] = 255;
                 }//第一段
                 for (int j = 0; j < _RoiW; j++)
                 {
                     int m = 3 * ((m_RecBuffer[j + 480] + 192) * _RoiW + j);                    //cutvalues[m] = 0;
                     //cutvalues[m + 1] = 0;
                     cutvalues[m + 2] = 255;                }//第二段                    
                for (int j = 0; j < _RoiW; j++)
                 {
                     int m = 3 * ((m_RecBuffer[j + 960] + 384) * _RoiW + j);                    //cutvalues[m] = 0;
                     //cutvalues[m + 1] = 0;
                     cutvalues[m + 2] = 255;                }//第三段
                 for (int j = 0; j < _RoiW; j++)
                 {
                     int m = 3 * ((m_RecBuffer[j + 960+480] + 576) * _RoiW + j);                    //cutvalues[m] = 0;
                     //cutvalues[m + 1] = 0;
                     cutvalues[m + 2] = 255;                }//第四段
                 System.Runtime.InteropServices.Marshal.Copy(cutvalues, 0, ptr, _RoiH * _RoiW * 3);
                 cutPic24.UnlockBits(_cutPic);
                 pictureBox1.Image = cutPic24;
             }
             catch (Exception ee)
             {
                 return;
             }
         }

好,搞定,收工,回头谈采集到的声音波形处理,第一制作,观察波形(现在是人做,以后交给计算机算法),找到特征波形,保存,用来匹配,第二应用,加载特征波形,实时匹配,响应图形缩放,或者开关等,也可以用来声控关闭计算机。

待续(慢慢来!...........)每天一点小改变☺

补充:以下是C#采集声音程序运行图(富士通T901,xp),480*4=1920字节:

MediaRecorder声音采集_Layout