C++ 采集音频流(PCM裸流)实现录音功能(双缓存版本)

上一次要用到音频已经是好几年前的事情了(毕竟本人是搞图象处理的)。当时写了一篇总结博客,没想到很多人看了,其中还有些在下载的地方和博客中问了些问题,虽然很想作答,但因为实在是忘了所以就没说什么。

  最近又有项目要用到录制音频的功能,索性就把之前留下的双缓存坑填了。其实原理跟之前的差不多,最大的不同在于用到了回调函数。另外,本篇的代码基本上是参考这篇博客的,本人只修改了一部分,如有雷同,绝非偶然。

  下面直接上代码,然后最后有整个工程的下载。

  头文件和链接库如下:

#include<mmsystem.h>
#include<mmreg.h>
#pragma  comment(lib, "winmm.lib")
  • 1
  • 2
  • 3

  变量和函数声明如下:

bool win;//采集数据标志位
FILE *f;//存储采集到的音频文件
HWAVEIN hWaveIn;  //输入设备
WAVEFORMATEX waveform; //采集音频的格式,结构体
BYTE *pBuffer1, *pBuffer2;//采集音频时的数据缓存
WAVEHDR wHdr1, wHdr2; //采集音频时包含数据缓存的结构体
static DWORD CALLBACK MicCallback(HWAVEIN hWaveIn,UINT uMsg,DWORD dwInstance,DWORD dwParam1,DWORD dwParam2);//消息回调函数
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

  初始化并开始录音的代码如下:

fopen_s(&f, "录音测试.pcm", "wb");//打开录音文件
win = true;
waveform.wFormatTag = WAVE_FORMAT_PCM;//声音格式为PCM
waveform.nSamplesPerSec = 8000;//采样率,16000次/秒
waveform.wBitsPerSample = 16;//采样比特,16bits/次
waveform.nChannels = 1;//采样声道数,1声道
waveform.nAvgBytesPerSec = 16000 * 4;//每秒的数据率,就是每秒能采集多少字节的数据
waveform.nBlockAlign = 2;//一个块的大小,采样bit的字节数乘以声道数
waveform.cbSize = 0;//一般为0
//使用waveInOpen函数开启音频采集
MMRESULT mmr = waveInOpen(&hWaveIn, WAVE_MAPPER, &waveform,
    (DWORD)(MicCallback), DWORD(this), CALLBACK_FUNCTION);
if (mmr != MMSYSERR_NOERROR)
    return;
//建立两个数组(这里可以建立多个数组)用来缓冲音频数据
DWORD bufsize = 8 * 1024;
pBuffer1 = new BYTE[bufsize];
pBuffer2 = new BYTE[bufsize];
wHdr1.lpData = (LPSTR)pBuffer1;
wHdr1.dwBufferLength = bufsize;
wHdr1.dwBytesRecorded = 0;
wHdr1.dwUser = 0;
wHdr1.dwFlags = 0;
wHdr1.dwLoops = 1;
wHdr1.lpNext = NULL;
wHdr1.reserved = 0;
//将建立好的wHdr1做为备用
waveInPrepareHeader(hWaveIn, &wHdr1, sizeof(WAVEHDR));
wHdr2.lpData = (LPSTR)pBuffer2;
wHdr2.dwBufferLength = bufsize;
wHdr2.dwBytesRecorded = 0;
wHdr2.dwUser = 0;
wHdr2.dwFlags = 0;
wHdr2.dwLoops = 1;
wHdr2.lpNext = NULL;
wHdr2.reserved = 0;
//将建立好的wHdr2做为备用
waveInPrepareHeader(hWaveIn, &wHdr2, sizeof(WAVEHDR));
//将两个wHdr添加到waveIn中去
waveInAddBuffer(hWaveIn, &wHdr1, sizeof(WAVEHDR));
waveInAddBuffer(hWaveIn, &wHdr2, sizeof(WAVEHDR));
//开始音频采集
waveInStart(hWaveIn);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43

  回调函数如下:

DWORD CALLBACK CPCMCollectProgramDouBufDlg::MicCallback(HWAVEIN hwavein, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2)
{
    //这个CIMAADPCMDlg就是你的音频采集类
    CPCMCollectProgramDouBufDlg*pWnd = (CPCMCollectProgramDouBufDlg*)dwInstance;
    switch (uMsg)
    {
    case WIM_OPEN:
        TRACE("WIM_OPEN\n");
        break;
    case WIM_DATA:
        TRACE("WIM_DATA\n");
        //采集到的数据做处理
        //((PWAVEHDR)dwParam1)->lpData是采集到的数据指针
        //((PWAVEHDR)dwParam1)->dwBytesRecorded是采集到的数据长度
        //将采集到的数据写入文件中
        fwrite(((PWAVEHDR)dwParam1)->lpData, ((PWAVEHDR)dwParam1)->dwBytesRecorded, 1, pWnd->f);
        //处理完了之后还要再把这个缓冲数组加回去
        //pWnd->win代表是否继续采集,因为当停止采集的时候,只有当所有的
        //缓冲数组都腾出来之后才能close,所以需要停止采集时就不要再waveInAddBuffer了
        if (pWnd->win)
            waveInAddBuffer(hwavein, (PWAVEHDR)dwParam1, sizeof(WAVEHDR));
        break;
    case WIM_CLOSE:
        TRACE("WIM_CLOSE\n");
        break;
    default:
        break;
    }
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

  关闭代码如下:

win = false;
Sleep(2000);
//在这里可以等待一定的时间,确保所有缓冲数组全都退出来了
//也可以在WIM_DATA消息处理中加个计数器,计算全部buffer都
//退出来了,就可以调用下面的close了
waveInClose(hWaveIn);
fclose(f);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

  如果是初学者看不懂,可以直接下载整个工程慢慢研究。

  地址在这

  录制出来的PCM文件如果不知道怎么播放出来,请移步这里

转自:https://blog.csdn.net/oHanTanYanYing/article/details/76158998

上一次要用到音频已经是好几年前的事情了(毕竟本人是搞图象处理的)。当时写了一篇总结博客,没想到很多人看了,其中还有些在下载的地方和博客中问了些问题,虽然很想作答,但因为实在是忘了所以就没说什么。

  最近又有项目要用到录制音频的功能,索性就把之前留下的双缓存坑填了。其实原理跟之前的差不多,最大的不同在于用到了回调函数。另外,本篇的代码基本上是参考这篇博客的,本人只修改了一部分,如有雷同,绝非偶然。

  下面直接上代码,然后最后有整个工程的下载。

  头文件和链接库如下:

#include<mmsystem.h>
#include<mmreg.h>
#pragma  comment(lib, "winmm.lib")
  • 1
  • 2
  • 3

  变量和函数声明如下:

bool win;//采集数据标志位
FILE *f;//存储采集到的音频文件
HWAVEIN hWaveIn;  //输入设备
WAVEFORMATEX waveform; //采集音频的格式,结构体
BYTE *pBuffer1, *pBuffer2;//采集音频时的数据缓存
WAVEHDR wHdr1, wHdr2; //采集音频时包含数据缓存的结构体
static DWORD CALLBACK MicCallback(HWAVEIN hWaveIn,UINT uMsg,DWORD dwInstance,DWORD dwParam1,DWORD dwParam2);//消息回调函数
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

  初始化并开始录音的代码如下:

fopen_s(&f, "录音测试.pcm", "wb");//打开录音文件
win = true;
waveform.wFormatTag = WAVE_FORMAT_PCM;//声音格式为PCM
waveform.nSamplesPerSec = 8000;//采样率,16000次/秒
waveform.wBitsPerSample = 16;//采样比特,16bits/次
waveform.nChannels = 1;//采样声道数,1声道
waveform.nAvgBytesPerSec = 16000 * 4;//每秒的数据率,就是每秒能采集多少字节的数据
waveform.nBlockAlign = 2;//一个块的大小,采样bit的字节数乘以声道数
waveform.cbSize = 0;//一般为0
//使用waveInOpen函数开启音频采集
MMRESULT mmr = waveInOpen(&hWaveIn, WAVE_MAPPER, &waveform,
    (DWORD)(MicCallback), DWORD(this), CALLBACK_FUNCTION);
if (mmr != MMSYSERR_NOERROR)
    return;
//建立两个数组(这里可以建立多个数组)用来缓冲音频数据
DWORD bufsize = 8 * 1024;
pBuffer1 = new BYTE[bufsize];
pBuffer2 = new BYTE[bufsize];
wHdr1.lpData = (LPSTR)pBuffer1;
wHdr1.dwBufferLength = bufsize;
wHdr1.dwBytesRecorded = 0;
wHdr1.dwUser = 0;
wHdr1.dwFlags = 0;
wHdr1.dwLoops = 1;
wHdr1.lpNext = NULL;
wHdr1.reserved = 0;
//将建立好的wHdr1做为备用
waveInPrepareHeader(hWaveIn, &wHdr1, sizeof(WAVEHDR));
wHdr2.lpData = (LPSTR)pBuffer2;
wHdr2.dwBufferLength = bufsize;
wHdr2.dwBytesRecorded = 0;
wHdr2.dwUser = 0;
wHdr2.dwFlags = 0;
wHdr2.dwLoops = 1;
wHdr2.lpNext = NULL;
wHdr2.reserved = 0;
//将建立好的wHdr2做为备用
waveInPrepareHeader(hWaveIn, &wHdr2, sizeof(WAVEHDR));
//将两个wHdr添加到waveIn中去
waveInAddBuffer(hWaveIn, &wHdr1, sizeof(WAVEHDR));
waveInAddBuffer(hWaveIn, &wHdr2, sizeof(WAVEHDR));
//开始音频采集
waveInStart(hWaveIn);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43

  回调函数如下:

DWORD CALLBACK CPCMCollectProgramDouBufDlg::MicCallback(HWAVEIN hwavein, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2)
{
    //这个CIMAADPCMDlg就是你的音频采集类
    CPCMCollectProgramDouBufDlg*pWnd = (CPCMCollectProgramDouBufDlg*)dwInstance;
    switch (uMsg)
    {
    case WIM_OPEN:
        TRACE("WIM_OPEN\n");
        break;
    case WIM_DATA:
        TRACE("WIM_DATA\n");
        //采集到的数据做处理
        //((PWAVEHDR)dwParam1)->lpData是采集到的数据指针
        //((PWAVEHDR)dwParam1)->dwBytesRecorded是采集到的数据长度
        //将采集到的数据写入文件中
        fwrite(((PWAVEHDR)dwParam1)->lpData, ((PWAVEHDR)dwParam1)->dwBytesRecorded, 1, pWnd->f);
        //处理完了之后还要再把这个缓冲数组加回去
        //pWnd->win代表是否继续采集,因为当停止采集的时候,只有当所有的
        //缓冲数组都腾出来之后才能close,所以需要停止采集时就不要再waveInAddBuffer了
        if (pWnd->win)
            waveInAddBuffer(hwavein, (PWAVEHDR)dwParam1, sizeof(WAVEHDR));
        break;
    case WIM_CLOSE:
        TRACE("WIM_CLOSE\n");
        break;
    default:
        break;
    }
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

  关闭代码如下:

win = false;
Sleep(2000);
//在这里可以等待一定的时间,确保所有缓冲数组全都退出来了
//也可以在WIM_DATA消息处理中加个计数器,计算全部buffer都
//退出来了,就可以调用下面的close了
waveInClose(hWaveIn);
fclose(f);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

  如果是初学者看不懂,可以直接下载整个工程慢慢研究。

  地址在这

  录制出来的PCM文件如果不知道怎么播放出来,请移步这里

猜你喜欢

转载自blog.csdn.net/lbc2100/article/details/80944626