zoukankan      html  css  js  c++  java
  • 用WaveX实现音频文件的录音

      原文地址: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 }
    View Code

      停止录音的代码段如下:

     1 //终止录音
     2 BOOL CAudioRec::StopRec()
     3 {
     4     //停止录音
     5     if(!CloseRecord())
     6     {
     7         return FALSE;     
     8     }
     9 
    10     //暂停一会
    11     Sleep(500);
    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 
    32 
    33     return TRUE;
    34 }
    View Code

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

     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,0,0,0))
    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 }
    View Code

      其实数据处理的关键语句只有下面这一条: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文件夹内容。

     

  • 相关阅读:
    Robot Framework的日期处理
    让IIS7和IIS6识别PUT和DELETE请求
    《微服务设计》读书笔记大纲
    规模化微服务——《微服务设计》读书笔记
    Uva 11396 爪分解
    Uva 10765 鸽子和炸弹
    Gym
    Gym
    Gym 101334C 无向仙人掌
    汇编实验十:编写子程序
  • 原文地址:https://www.cnblogs.com/kanite/p/3364101.html
Copyright © 2011-2022 走看看