为了识别一个短元音,比如,啊(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,故有机会取水泡茶,哈哈!

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

  1. 基于RTMP实现Linux|麒麟操作系统下屏幕|系统声音采集推送

    背景 Windows操作系统自问世以来,以其简单易用的图形化界面操作受到大众追捧,为计算机的普及.科技的发展做出了不可磨灭的功绩,也慢慢的成为人们最依赖的操作系统.在中国,90%以上的办公环境都是Wi ...

  2. Android手机直播(三)声音采集

    一.文章说明 开始写文章了,才知道写文章真心耗费心力,希望自己尽量做到快速更新,也希望这些文章真心能帮助到开发者们. 这篇文章主要讲述Android声音采集相关的知识,首先介绍声音的基础知识,然后介绍 ...

  3. 多看Android版本WIFI传书的实现

    多看Android版本WIFI传书的实现 参照<多看>的WIFI传书功能 * 手机端的HttpServer采用开源项目AndroidAsync实现的. * 网页端采用jQuery实现,文件 ...

  4. 爱剪辑导出视频显示服务器繁忙,声音采集与视频剪辑(第二届广东省普通高中信息技术优质课评选活动二等奖).doc...

    声音采集与视频剪辑(第二届广东省普通高中信息技术优质课评选活动二等奖) 声音采集与视频剪辑(第二届广东省普通高中信息技术优质课评选活动二等奖) 教学分析与教学设计思路 ? 一.教学对象分析与教学设计 ...

  5. 麦克风采样率设置导致视频会议中声音采集异常问题分享

    目录 1.问题描述 2.麦克风音频采集频率引发的声音采集异常 3.修改麦克风采集频率的详细说明 4.最后 这几年,大家已经习惯于使用视频会议软件(比如免费的腾讯会议软件)进行线上沟通与交流,通过摄像头 ...

  6. WCF把书读薄(4)——事务编程与可靠会话

    WCF把书读薄(3)--数据契约.消息契约与错误契约 真不愧是老A的书,例子多,而且也讲了不少原理方面的内容,不过越读越觉得压力山大--这次来稍微整理整理事务和可靠会话的内容. 十八.事务编程 WCF ...

  7. 从混合云到云原生 KubeSphere 3.0先把书读厚,再把书读薄

    KubeSphere把云原生的书"读厚",客户才能把云原生的书"读薄". 出品 | 常言道 作者 | 丁常彦 从2018年7月青云QingCloud正式发布Ku ...

  8. 传感器测试--声音采集与FFT 分析

    三天不学习,就不知道如何用声卡了. 1 MATLAB-- Audio record for specified time 做了一个简单的demo,仅采集1次的声音,并做FFT 分析 记得存为.m文件, ...

  9. 编译Linux版本飞鸽传书的不完全解决办法

     前几天想起在局域网常用的飞鸽传书,想知道在Ubuntu有没有办法运行或者有类似的软件可用,首先想到了Wine,马上Wine一个Windows最新版本的IPMSG,一试就成功了,完美模拟运行. 在 ...

最新文章

  1. solaris11学习必用工具及ISO
  2. c语言多线程mysql_多线程读写mysql数据库
  3. 脑电情绪识别:脑功能连接网络与局部激活信息结合
  4. 虚拟机上使用ghost xp
  5. pdo mysql连接类_PHP PDO-MYSQL:如何在不同类之间使用数据库连接
  6. 通过Cookie存取用户游览记录的代码示例
  7. 通过Socket configuration controls 获取网络设备地址
  8. Windows7 简体中文旗舰版下载 (MSDN官方发布正式版原版镜像)
  9. IMX6ULL开发板,系统移植——第一步Uboot移植
  10. layer的iframe用法整理
  11. 计算机ps相框怎么做,教你用PS给相片加上很漂亮的相框
  12. 新唐单片机选型手册_Nuvoton新唐8位8051单片机选型指南
  13. c#的chart标题_C#之Chart篇
  14. 事件绑定-addEventListener()和attachEvent()的区别及用法
  15. 抖音快手短视频功能解读
  16. 用户协议栈之协议设计
  17. 可信数字版权保护解决方案Sky Engine—重塑原创价值,构建数字版权新生态
  18. php 接口token生成验证
  19. 实验一OpenGL图形编程入门
  20. 法线贴图呈蓝紫色的原因

热门文章

  1. Archicad二次开发——MessageBox、USstring转字符、打开保存文件的对话框获取选择的地址、获取文件信息
  2. 软件开发项目管理检查清单:天气晴雨表
  3. ActiveStorage. 英文书Learnrails5.2的案例,看如何放到云上。
  4. java.lang.UnsatisfiedLinkError: no rxtxSerial in java.library.path
  5. 如何实现扫描条码自动打印标签?
  6. tplink软件升级有用吗_tp-link无线路由器软件如何升级【教程图解】
  7. 计算机组成原理学习-实验四 模型机实验(简易版)(详细、人话)
  8. Unity连接KBE云服务器:登录网关(baseapp)异常
  9. github下载的matlab代码,如何从GitHub存储库下载代码
  10. 用Matlab和SPM批量处理被试的经验总结