连续声音采集最好版本(c#),把书读薄了

为了识别一个短元音,比如,啊(a),钨(u),诸如此类,抄来了别人的声音采集程序,visual c++和c#两个版本,在采集短元音的过程中,发现,c#版本不如c++版本好,一直未找到原因,耿耿于怀(人生的烦恼诞生于此,放不下!)。

当能进行mfcc语音识别这些短元音后,很想用自己的方式去存储一段自己的录音,而非.wav格式,语音识别不能总是停留在短元音识别是吧?

近期尝试了c++版本录音,是ok的,具体是这样,每次采集1920字节声音,满了,再添加1920字节buffer备用继续录音,已满的1920字节buffer写入磁盘,命名为.mch文件,然后重复以上过程,把已满的1920字节buffer源源不断的追加写入到这个.mch文件中。

可惜,c#尝试,始终不成功,再次想起前面的芥蒂,两重放不下,你现在是靠c#吃饭的,底层怎么可以不牢靠呢?

有一种精神叫做“要把书读薄了!”,七月初,机器视觉很多算法都在整理,就是在继承这种精神,只有这样,你的机器视觉才能稳定,准确,高效。

近期,抄了不下四个版本一步一步来精简这个c#版本的连续声音采集程序,终于等到了该来的,放下了该放下的。

抄别人的东西,就应该有所自己的收获,否则,掌控不了,而心生烦恼!

以下是c#版本精简后连续声音采集最好程序,c++版本不再累述。代码如下,你可以去翻看我前面博客有关于此,做个对比。

虽然你还能看到网络程序的影子,但他在你手中,已经脱胎换骨。(删掉了不必要的线程thread以及线程事件掌控AutoResetEvent,这是一种误导,他并不是一种好的方式,除非你更热衷于c#编程技术,这种方式对付短元音采集,虽然绰绰有余,但在连续采集上,反复debug,你会发现缺陷,只要你保证了录音buffer的添加不中断,微软已经保证了录音的连续性,不需要再来一个线程去掌控

第一,创建一个form,声明变量

  public const short device = (-1);
        public WaveFormat format = new WaveFormat();      
        private WaveInRecorder m_WIR;

第二,在formload函数中初始化

   m_WIR = new WaveInRecorder(device, format, 1920, 1, new BufferDoneEventHandler(DataArrived));//委托函数

好,结束了!

不懂向下看:(保证最精简,无关全删掉)

第一解释,WaveFormat

  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()
        {
            wFormatTag = (short)WaveFormats.Pcm;
            nChannels = 1;
            nSamplesPerSec = 8000;
            nAvgBytesPerSec = 8000;
            nBlockAlign = 1;
            wBitsPerSample = 8;
            cbSize = 0;
        }
    }

第二解释,internal class WaveInRecorder
    {
        private IntPtr m_WaveIn;

        private WaveInBuffer m_Buffers = null; 
        private WaveInBuffer m_CurrentBuffer = null;     
        private BufferDoneEventHandler m_DoneProc;
        public WaveNative.WaveDelegate m_BufferProc = new WaveNative.WaveDelegate(WaveInBuffer.WaveInProc);   //委托函数    
        int buffersize = 0;

        public void wimdatacomplete(IntPtr data, int size)
        {
            m_DoneProc(data, size);//dataarrived         
        }
        public WaveInRecorder(int device, WaveFormat format, int bufferSize, int bufferCount, BufferDoneEventHandler doneProc)
        {           
            this.buffersize = bufferSize;
            m_DoneProc = doneProc;//dataarrived传进来          
         
            WaveInHelper.Try(WaveNative.waveInOpen(out m_WaveIn, device, format, m_BufferProc, 0, WaveNative.CALLBACK_FUNCTION));    //录音第一步waveInOpen       
            if (bufferCount > 0)
            {
                m_Buffers = new WaveInBuffer(m_WaveIn, bufferSize);//录音第二步waveInPrepareHeader

                WaveInBuffer Prev = m_Buffers;
         //因为只有一个1920字节buffer,其他不相关删除了
                    Prev.NextBuffer = m_Buffers;          
            }
            for (int i = 0; i < bufferCount; i++)
            {
                m_CurrentBuffer = m_CurrentBuffer == null ? m_Buffers : m_CurrentBuffer.NextBuffer;
              bool temp=  m_CurrentBuffer.Record();//waveinaddbuffer,录音第三步
                m_CurrentBuffer.m_DoneProc = wimdatacomplete;
            }
            WaveInHelper.Try(WaveNative.waveInStart(m_WaveIn));//开始录音,录音第四步          
        }    
    }

第三解释,internal class WaveInBuffer 
    {
        private IntPtr m_WaveIn;
        private WaveNative.WaveHdr m_Header;
        private byte[] m_HeaderData;
        private GCHandle m_HeaderDataHandle;     
     
        public WaveInBuffer NextBuffer;       
        public BufferDoneEventdowith m_DoneProc;
        public int Size
        {
            get { return m_Header.dwBufferLength; }
        }

        public IntPtr Data
        {
            get { return m_Header.lpData; }
        }
        public WaveInBuffer(IntPtr waveInHandle, int size)
        {
            m_WaveIn = waveInHandle;

            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)));
        }
        public bool Record()
        {//录音第六步,添加一个1920字节buffer,若添加不上,录音失败
            bool m_Recording = false;
            lock (this)
            {
               m_Recording = WaveNative.waveInAddBuffer(m_WaveIn, ref m_Header, Marshal.SizeOf(m_Header)) == WaveNative.MMSYSERR_NOERROR;
            }
            return m_Recording;
        }     
        private void OnCompleted(IntPtr Data, int size)
        {
            m_DoneProc(Data, size); //录音第七步  ,录满1920字节buffer 取出来存盘  
        }

        internal static void WaveInProc(IntPtr hdrvr, int uMsg, int dwUser, ref WaveNative.WaveHdr wavhdr, int dwParam2)
        {//录音第五步  ,   MM_WIM_DATA到了,说明录音满了一个1920字节buffer   
            if (uMsg == WaveNative.MM_WIM_DATA)
            {
                try
                {
                    GCHandle h = (GCHandle)wavhdr.dwUser;
                    WaveInBuffer buf = (WaveInBuffer)h.Target;          
                    bool ret = buf.Record();

                    buf.OnCompleted(buf.Data, buf.Size);
                }
                catch
                {
                }
            }
        }    
    }

第四解释,相关其他:

 internal class WaveInHelper
    {
        public static void Try(int err)
        {
            if (err != WaveNative.MMSYSERR_NOERROR)
            {
                // throw new Exception(err.ToString());
                Thread.Sleep(250);
            }
        }
    }

  internal class WaveNative 
    {   
        public const int MM_WIM_DATA = 0x3C0;

        public const int CALLBACK_FUNCTION = 0x00030000;    // dwCallback is a FARPROC
    
        public delegate void WaveDelegate(IntPtr hdrvr, int uMsg, int dwUser, ref WaveHdr wavhdr, int dwParam2);
       
        [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
        }        
        //尝试简单录音20200724
        [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 waveInPrepareHeader(IntPtr hWaveIn, ref WaveHdr lpWaveInHdr, int uSize);

        [DllImport("winmm.dll")]
        public static extern int waveInStart(IntPtr hwi);
    
        [DllImport("winmm.dll")]
        public static extern int waveInAddBuffer(IntPtr hwi, ref WaveHdr pwh, int cbwh);
    }

另外就是两个委托函数的使用,把录音数据传到form层面,form下的委托函数是:

   public void DataArrived(IntPtr data, int size)
        {       //每写满一个1920字节buffer,都会给到这里来  
                bt2 = new byte[1920];          
                Marshal.Copy(data, bt2, 0, bt2.Length);

//取出即可,显示也好,存盘也好

        }

所以要在form类之外,项目namespace空间之内声明:

  // callbacks
    public delegate void BufferDoneEventHandler(IntPtr data, int size);//dataarrived  ,在WaveInRecorder中
    public delegate void BufferDoneEventdowith(IntPtr data, int size);//testsucess,在waveinbuffer中

正如你所看到的,DataArrived函数的地址给了WaveInRecorder.m_DoneProc

而WaveInRecorder.m_DoneProc又给了   waveinbuffer.m_DoneProc (通过 wimdatacomplete函数);

所以WaveInProc中的录音数据(每一个录满的1920字节)都是通过 waveinbuffer.m_DoneProc给出的。

如果不这样,你还有什么好方法?

WaveInProc也是一个委托函数(回调函数),只不过函数格式微软已经定义好(waveinopen函数要求),这是源头活水。

在WaveInProc调用了waveinbuffer.m_DoneProc,故有机会取水泡茶,哈哈!

 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值