原文地址:https://blog.csdn.net/gongluck93/article/details/53096013

  1、WaveInOpen

  1. waveInOpen
  2. MMRESULT waveInOpen(
  3. LPHWAVEIN phwi,             // phwi是返回的句柄存放地址
  4. UINT uDeviceID,             // uDeviceID是要打开的音频设备ID号,一般都指定为WAVE_MAPPER
  5. LPWAVEFORMATEX pwfx,
  6. DWORD dwCallback,           // dwCallback则为指定的回调函数或线程,窗口等的地址
  7. DWORD dwCallbackInstance,   // dwCallbackInstance为需要向回调函数或线程送入的用户参数
  8. DWORD fdwOpen               // fdwOpen指定回调方式:CALLBACK_FUNCTION, CALLBACK_THREAD和CALLBACK_WINDOW
  9. );

  2、至于pwfx,则比较关键,它指定了要以什么音频格式打开音频输入设备,它是一个结构WAVEFORMATEX:

  1. typedef struct {
  2. WORD wFormatTag;        //可以在wFormatTag中指定一些压缩的音频格式,如G723.1,TURE DSP,等之类。不过一般都是选用WAVEFORMAT_PCM格式,
  3. //即未压缩的音频格式,至于压缩,可以在录完后调用下面将要谈到的ACM单独进行。
  4. WORD nChannels;         //nChannels为声道数,1或者2。
  5. DWORD nSamplesPerSec;   //nSamplesPerSec为每秒采样数,8000、11025、22050、44100为几个标准值。
  6. DWORD nAvgBytesPerSec;  //每秒平均的字节数,在PCM方式中就等于nChannels*nSamplesPerSec*wBitsPerSample/8,
  7. //但对于其它的压缩的音频格式,由于很多压缩方式是按时间片进行的,如G723.1,就是以30ms为一个压缩单位,
  8. //这样,nAvgBytesPerSec只是一个大概的数字,并不准确,程序中的计算是不应该以这个量为准的。
  9. //这一点在下面的压缩音频输出和ACM音频压缩中非常重要。
  10. WORD nBlockAlign;       //nBlockAlign是一个比较特殊的值,表示对音频处理时的最小处理单位,对于PCM非压缩,它就是wBitsPerSample*nChannels/8,
  11. //而对于非压缩格式,则表示压缩/解压处理的最小单位了,如G723.1,就是30ms的数据大小(20bytes或者24bytes)。
  12. WORD wBitsPerSample;    //wBitsPerSample就是每采样值的位数,8或者16。
  13. WORD cbSize;            //cbSize则是表示该WAVEFORMATEX的结构在标准的头部之后还有多少字节数,对于很多非PCM的音频格式,
  14. //有一些自己的定义格式参数,这些就紧跟在标准的WAVEFORMATEX后面,其大小就由cbSize指定。对于PCM格式而言,为0,或者忽略不检查。
  15. } WAVEFORMATEX;

  1)从声卡获取的数据格式一般设置为WAVE_FORMAT_PCM,得到的是一种最原始的音频数值。采样率,采样位宽和采样通道数比较重要,尤其采样位宽--1此采样数据量的大小,这里采用16bits。

  2)录音得到的数据是存放在自己设定的内存块处的,为了录音的连续,通常是用多个内存数据块。当一块数据内存满了之后,是通过消息响应的方式给指定的窗口或者回调函数。在回调函数中处理这些数据。

  开启录音的代码段如下:

  1. //启动录音
  2. BOOL CAudioRec::StartRec()
  3. {
  4. BOOL bRet = FALSE;
  5.  
  6. //启动线程
  7. if(!StartThread())
  8. {
  9. goto Exit;
  10. }
  11.  
  12. //打开音频设备
  13. if(!OpenDev())
  14. {
  15. goto Exit1;
  16. }
  17.  
  18. //准备缓存
  19. if(!PrepareBuffer())
  20. {
  21. goto Exit2;
  22. }
  23.  
  24. //开始录音
  25. if(!OpenRecord())
  26. {
  27. goto Exit3;
  28. }
  29.  
  30. bRet = TRUE;
  31. goto Exit;
  32.  
  33. Exit3:
  34. //释放缓存
  35. FreeBuffer();
  36.  
  37. Exit2:
  38. //关闭录音设备
  39. CloseDev();
  40.  
  41. Exit1:
  42. //停止线程
  43. StopThread();
  44.  
  45. Exit:
  46. return bRet;
  47. }

  停止录音的代码段如下:

  1. //终止录音
  2. BOOL CAudioRec::StopRec()
  3. {
  4. //停止录音
  5. if(!CloseRecord())
  6. {
  7. return FALSE;
  8. }
  9.  
  10. //暂停一会
  11. Sleep();
  12.  
  13. //停止线程
  14. if(!StopThread())
  15. {
  16. return FALSE;
  17. }
  18.  
  19. //释放录音分配内存
  20. if(!FreeBuffer())
  21. {
  22. return FALSE;
  23. }
  24.  
  25. //关闭设备
  26. if(!CloseDev())
  27. {
  28. return FALSE;
  29. }
  30.  
  31. return TRUE;
  32. }

  线程内数据处理的代码如下:

  1. //线程回调函数
  2. DWORD WINAPI CAudioRec::AudioInThreadProc(LPVOID lpParameter)
  3. {
  4. CAudioRec *pAudioRec = (CAudioRec *)lpParameter;
  5.  
  6. // char buffer[1024];
  7.  
  8. MSG msg;
  9. while(GetMessage(&msg,,,))
  10. {
  11. switch(msg.message)
  12. {
  13. //打开音频设备
  14. case MM_WIM_OPEN:
  15. break;
  16.  
  17. //关闭录音设备
  18. case MM_WIM_CLOSE:
  19. break;
  20.  
  21. //录音设备返回数据
  22. case MM_WIM_DATA:
  23. WAVEHDR *pWH = (WAVEHDR *)msg.lParam;
  24.  
  25. waveInUnprepareHeader((HWAVEIN)msg.wParam,pWH,sizeof(WAVEHDR));
  26.  
  27. //dwBytesRecord表示此缓存中已有的数据大小
  28. if(pWH->dwBytesRecorded != AUDIO_BUF_SIZE)
  29. break;
  30.  
  31. if(pAudioRec->m_pCallBackProc != NULL)
  32. pAudioRec->m_pCallBackProc((BYTE *)pWH->lpData,AUDIO_BUF_SIZE,pAudioRec->m_dwUser);
  33.  
  34. //获得的数据就在pWH->lpData,长度为pWH->dwBytesRecord
  35. waveInPrepareHeader((HWAVEIN)msg.wParam,pWH,sizeof(WAVEHDR));
  36. waveInAddBuffer((HWAVEIN)msg.wParam,pWH,sizeof(WAVEHDR));
  37. }
  38. }
  39.  
  40. return msg.wParam;
  41. }

  其实数据处理的关键语句只有下面这一条:pAudioRec->m_pCallBackProc((BYTE *)pWH->lpData,AUDIO_BUF_SIZE,pAudioRec->m_dwUser);

  它利用了一个函数指针,由调用者指定相应的处理函数。

  今天下午调试了半天,卡在结束录音这个地方。其中语句的执行顺序很重要。

  1)先停止录音即调用waveInStop()函数  2)停止线程StopThread()  3)释放为录音准备的缓存 4)关闭录音设备

  我自己写的时候将关闭录音设备放在了停止线程的前面,在这里结束录音总是出错。

  

  2018/4/28日增加以下内容:

  封装更好的示例参见github项目,nadernt/whistle-recognizer,github地址:https://github.com/nadernt/whistle-recognizer/tree/7bfab2839150b0df7404886a4cf60db1b7be866b

  参见此项目中WaveIOLib文件夹内容。

用WaveX实现音频文件的录音的更多相关文章

  1. C# NAudio录音和播放音频文件及实时绘制音频波形图(从音频流数据获取,而非设备获取)

    下午写了一篇关于NAudio的录音.播放和波形图的博客,不太满意,感觉写的太乱,又总结了下 NAudio是个相对成熟.开源的C#音频开发工具,它包含录音.播放录音.格式转换.混音调整等功能.本次介绍主 ...

  2. iOS开发系列--音频播放、录音、视频播放、拍照、视频录制

    --iOS多媒体 概览 随着移动互联网的发展,如今的手机早已不是打电话.发短信那么简单了,播放音乐.视频.录音.拍照等都是很常用的功能.在iOS中对于多媒体的支持是非常强大的,无论是音视频播放.录制, ...

  3. iOS开发----音频播放、录音、视频播放、拍照、视频录制

    随着移动互联网的发展,如今的手机早已不是打电话.发短信那么简单了,播放音乐.视频.录音.拍照等都是很常用的功能.在iOS中对于多媒体的支持是非常强大的,无论是音视频播放.录制,还是对麦克风.摄像头的操 ...

  4. 音频播放、录音、视频播放、拍照、视频录制-b

    随着移动互联网的发展,如今的手机早已不是打电话.发短信那么简单了,播放音乐.视频.录音.拍照等都是很常用的功能.在iOS中对于多媒体的支持是非常强大的,无论是音视频播放.录制,还是对麦克风.摄像头的操 ...

  5. (原创)speex与wav格式音频文件的互相转换

    我们的司信项目又有了新的需求,就是要做会议室.然而需求却很纠结,要继续按照原来发语音消息那样的形式来实现这个会议的功能,还要实现语音播放的计时,暂停,语音的拼接,还要绘制频谱图等等. 如果是wav,m ...

  6. S3C2416裸机开发系列19_Fatfs播放录像wav音频文件

    S3C2416裸机开发系列19 Fatfs播放录像wav音频文件 国际象棋男孩    1048272975 多媒体资源,一般都是以文件的形式存储在固化存储器中.Fatfs所支持的fat32为windo ...

  7. iOS音频播放、录音、视频播放、拍照、视频录制

    随着移动互联网的发展,如今的手机早已不是打电话.发短信那么简单了,播放音乐.视频.录音.拍照等都是很常用的功能.在iOS中对于多媒体的支持是非常强大的,无论是音视频播放.录制,还是对麦克风.摄像头的操 ...

  8. DxPackNet 3.音频捕捉(录音)

    用DxpackNet捕捉音频其实很简单 1.初始化控件 IDxMicrophCapture microphone; private void Form1_Load(object sender, Eve ...

  9. IOS音频1:之采用四种方式播放音频文件(一)AudioToolbox AVFoundation OpenAL AUDIO QUEUE

    本文转载至 http://blog.csdn.net/u014011807/article/details/40187737 在本卷你可以学到什么? 采用四种方法设计应用于各种场合的音频播放器: 基于 ...

随机推荐

  1. spring 如何动态加载properties文件的内容

    1. 在xml中配置你的properties路径: <bean id="messageSource" class="org.springframework.cont ...

  2. iOS 收款计算器算法

    一个收款计算器算法,从之前高仿有赞Demo里面抽离的一个界面 demo 在这里 https://github.com/L-vinCent/calculView_function 显示计算记录 不能连续 ...

  3. hdu 1189 并查集

    Farm Irrigation Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)T ...

  4. DOM对象之查找标签&属性操作

    HTML DOM (文档对象模型) DOM(Document Object Model)是一套对文档的内容进行抽象和概念化的方法. JavaScript对DOM进行了实现,对应于JavaScript中 ...

  5. android layout

    android的视图分为两类,一类是布局,另一个类是控件 一.LinearLayout(线性布局) 最常用布局之一,线性布局的特性是每添加一个控件默认会在上个控件的下面占一行. <LinearL ...

  6. 记一次ORM的权衡和取舍

    面对ORM的选型,有些人是根据自己熟悉程度来评判,有些人是根据他人的推荐来抉择,有些人觉得都差不多,随便了.当自己要真正做选择的时候,以上的这些依据都无法真正说服自己,因为不同的业务需求,不同的团队构 ...

  7. CODEVS_1074 食物链

    #include<iostream> #include<algorithm> #include<cstring> #include<string> #d ...

  8. SpringBoot中mybatis的自动生成

    1.在pom文件中加入自动生成的插件 <!-- mybatis generator 自动生成代码插件 --> <plugin> <groupId>org.mybat ...

  9. Java并发编程实战 读书笔记(二)

    关于发布和逸出 并发编程实践中,this引用逃逸("this"escape)是指对象还没有构造完成,它的this引用就被发布出去了.这是危及到线程安全的,因为其他线程有可能通过这个 ...

  10. 深入GCD(三): Dispatch Sources

    何为Dispatch Sources简单来说,dispatch source是一个监视某些类型事件的对象.当这些事件发生时,它自动将一个block放入一个dispatch queue的执行例程中.说的 ...