使用winmm.lib库对windows录音设备采集录音数据

  1. 首先包含头文件以及库目录
    #include <mmsystem.h>
    #pragma comment(lib, "winmm.lib")  
  2. 初始化录音所需变量
    WAVEHDR wHdr1[4]; // 采集音频时包括数据缓存的结构体
    HWAVEIN hWaveIn;  // 输入设备句柄
    hWaveIn = NULL;   // 录音设备句柄初始化
  3. 设置音频采集所需参数,具体包括声音格式、采样率、声道等
    // *************** 录音参数设置 ********************
    	WAVEFORMATEX waveform;                 // 采集音频的格式,结构体
    	waveform.wFormatTag = WAVE_FORMAT_PCM; // 声音格式为PCM
    	waveform.nSamplesPerSec = 16000;       // 采样率,16000
    	waveform.wBitsPerSample = 16;          // 采样比特(即采样深度),16bits
    	waveform.nChannels = 1;                // 采样声道数,2声道
    	waveform.nAvgBytesPerSec = 32000;      // 每秒的数据率,就是每秒能采集多少字节的数据
    	waveform.nBlockAlign = 2;              // 一个块的大小,采样bit的字节数*声道数
    	waveform.cbSize = 0;                   // 一般为0
  4. 打开音频采集设备
    //使用waveInOpen函数开启音频采集
    waveInOpen(&hWaveIn, WAVE_MAPPER, &waveform, (DWORD_PTR)AudioRecodeProc, (DWORD_PTR)this, CALLBACK_FUNCTION);
    
    /*
    MMRESULT waveInOpen(
       LPHWAVEIN       phwi,
       UINT            uDeviceID,
       LPCWAVEFORMATEX pwfx,
       DWORD_PTR       dwCallback,
       DWORD_PTR       dwCallbackInstance,
       DWORD           fdwOpen
    );
    
    参数phwi :指向一个句柄,作为其他函数的调用参数
    参数uDeviceID:录音设备ID号,用来辨别已经打开的录音设备
    参数pwfx:音频采集格式结构体
    参数dwCallback:回调函数或者线程函数等
    参数dwCallbackInstance:传入回调函数的参数
    参数fdwOpen:决定dwCallback回调函数的类型,主要类型如下
    
    Value	             Meaning
    CALLBACK_EVENT	     The dwCallback parameter is an event handle.
    CALLBACK_FUNCTION    The dwCallback parameter is a callback procedure address.
    CALLBACK_NULL	     No callback mechanism. This is the default setting.
    CALLBACK_THREAD	     The dwCallback parameter is a thread identifier.
    CALLBACK_WINDOW	     The dwCallback parameter is a window handle.
    */
  5. 建立音频缓存并初始化
    //建立两个数组(这里能够建立多个数组)用来缓冲音频数据,
    //这里每次开辟SAMPLENUMS Byte的缓存存储录音数据
    
        for (int i = 0; i<4; i++)
    	{
    		wHdr1[i].lpData = new char[SAMPLENUMS];
    		wHdr1[i].dwBufferLength = SAMPLENUMS;
    		wHdr1[i].dwBytesRecorded = 0;
    		wHdr1[i].dwUser = NULL;
    		wHdr1[i].dwFlags = 0;
    		wHdr1[i].dwLoops = 1;
    		wHdr1[i].lpNext = NULL;
    		wHdr1[i].reserved = 0;
    
    		//为录音设备准备缓存函数(为音频输入设备准备一个缓冲区):
    		//MMRESULT waveInPrepareHeader(  HWAVEIN hwi,  LPWAVEHDR pwh, UINT bwh );
    		waveInPrepareHeader(hWaveIn, &wHdr1[i], sizeof(WAVEHDR));
    
    		//给输入设备增加一个缓存(将缓冲区发送给设备,若缓冲区填满,则不起作用):
    		//MMRESULT waveInAddBuffer(  HWAVEIN hwi, LPWAVEHDR pwh, UINT cbwh );
    		waveInAddBuffer(hWaveIn, &wHdr1[i], sizeof(WAVEHDR));
    	}
    
    /*
    MMRESULT waveInPrepareHeader(
     HWAVEIN hwi,     //音频输入设备句柄
     LPWAVEHDR pwh,   //指向WAVEHDR结构的指针,标识准备的缓冲区
     UINT cbwh        //WAVEHDR结构的大小,使用sizeof即可
    );
    
    typedef struct wavehdr_tag { 
      LPSTR   lpData;         //指向波形格式的缓冲区
      DWORD   dwBufferLength; //缓冲区的大小
      DWORD   dwBytesRecorded;//当前存储了多少数据
      DWORD_PTR dwUser;       //用户数据
      DWORD   dwFlags;        //为缓冲区提供的信息,在waveInPrepareHeader函数中使用WHDR_PREPARED
      DWORD   dwLoops;        //输出时使用,标识播放次数
      struct wavehdr_tag * lpNext;//reserved
      DWORD_PTR reserved;         //reserved
    } WAVEHDR, *LPWAVEHDR; 
    
    */
  6. 开始录音
    waveInStart(hWaveIn); //开始录音
  7. 关闭录音
    //关闭录音设备函数:
    waveInClose(hWaveIn);
  8. 最重要的函数是回调函数,即waveInOpen()函数中注册的回调函数,从中可接收到采集到的音频数据并进行音频处理
    void CALLBACK CAudioTestDomeDlg::AudioRecodeProc(HWAVEIN hwi, UINT uMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2)
    {
    	LPWAVEHDR pwh = (LPWAVEHDR)dwParam1;
    	CAudioTestDomeDlg* pObj = (CAudioTestDomeDlg*)dwInstance;
    	short buffer[SAMPLENUMS];
    	if (pObj->m_bIsRun_Recode && WIM_DATA == uMsg)
    	{
    		memcpy(buffer, pwh->lpData, SAMPLES_BYTES);
    
    		// 录制按钮对应处理
    		if (pObj->m_nRecode && pObj->pfRecodePC)
    		{
    			int write_length = 0;
    			write_length = fwrite(buffer, 1, SAMPLES_BYTES, pObj->pfRecodePC);   // 写入原始音频数据
    		}
    
    		// 波形显示按钮对应处理
    		if (pObj->m_nShowWave || pObj->m_nPlay)
    		{
    			memcpy(pObj->m_nzValues, buffer, SAMPLES_BYTES);
    		}
    
    		// 波形显示按钮对应处理
    		if (pObj->m_nShowWave)
    		{
    			// 获取绘图控件的客户区坐标   
    			// (客户区坐标以窗口的左上角为原点,这区别于以屏幕左上角为原点的屏幕坐标)   
    			pObj->m_picDraw.GetClientRect(&pObj->rectPicture);
    
    			CDC *pDC = pObj->m_picDraw.GetDC();
    			// 绘制波形图   
    			if (!pDC)
    				return;
    			pObj->DrawWave(pDC, pObj->rectPicture);
    			pObj->m_picDraw.ReleaseDC(pDC);
    		}
    
    		// 播放按钮对应处理
    		if (pObj->m_nPlay)
    		{
    			LPVOID buf = NULL;
    			DWORD  buf_len11 = 0;
    			static DWORD offset = BUFFERNOTIFYSIZE;
    			pObj->m_pDSBuffer8->Lock(offset, BUFFERNOTIFYSIZE, &buf, &buf_len11, NULL, NULL, 0);
    
    	       // 如果是实时音频播放,那么下面的数据就可以把内存中buf_len大小的数据复制到buf指向的地址即可
    			memcpy(buf, pObj->m_nzValues, buf_len11);
    
    			offset += buf_len11;
    			offset %= (BUFFERNOTIFYSIZE * MAX_AUDIO_BUF);
    			pObj->m_pDSBuffer8->Unlock(buf, buf_len11, NULL, 0);
    		}
    
    		waveInAddBuffer(hwi, pwh, sizeof(WAVEHDR));  //给输入设备增加一个缓存
    	}
    	else
    		Sleep(10);
    }

    至此,完整的windows下使用winmm.lib库采集音频数据的过程就结束了。

猜你喜欢

转载自blog.csdn.net/lifei092/article/details/81355384