声音采集程序(二)

声音处理程序使用 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)]

  public class WaveFormat
    {
        public short wFormatTag;
        public short nChannels;
        public int nSamplesPerSec;
        public int nAvgBytesPerSec;
        public short nBlockAlign;
        public short wBitsPerSample;
        public short cbSize;

        public WaveFormat()//c++中你也可以看到,它是一个固定的选项,波形格式(不懂,我们不改变它)
        {
            wFormatTag = (short)WaveFormats.Pcm;
            nChannels = 1;
            nSamplesPerSec = 8000;//仍然是每秒8000的样本采样
            nAvgBytesPerSec = 8000;
            nBlockAlign = 1;
            wBitsPerSample = 8;
            cbSize = 0;
        }
    }

///////////////////再来 

 // callbacks回调函数被委托替代
    public delegate void BufferDoneEventHandler(IntPtr data, int size);

 internal class 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;

//这里使用第二个委托替代回调函数,可以对照着看,加深印象

        public 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;
            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;
            }
        }  

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

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

我的邮箱[email protected];[email protected]

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

发布了66 篇原创文章 · 获赞 12 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/ganggangwawa/article/details/102509683