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

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

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

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

  头文件和链接库如下:

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

  变量和函数声明如下:

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);//消息回调函数

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

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);

  回调函数如下:

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;
}

  关闭代码如下:

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

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

  地址在这

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

### 回答1: 要编写一个能够录音并将音频保存为WAV格式的C程序,需要使用C语言的音频库函数。在Windows系统下,常用的音频库函数包括MMSystem.h和WinMm.lib。以下是一个简单的C程序示例,可以录制1秒钟的音频并将其保存为WAV文件。 ```c #include <windows.h> #include <mmsystem.h> #pragma comment(lib, "winmm.lib") int main() { HWAVEIN hWaveIn; WAVEFORMATEX wfx; MMRESULT result; DWORD dwSize; char* pBuffer; WAVEHDR WaveInHdr; //定义音频格式 wfx.nSamplesPerSec = 44100; wfx.wBitsPerSample = 16; wfx.nChannels = 1; wfx.wFormatTag = WAVE_FORMAT_PCM; wfx.nBlockAlign = (wfx.wBitsPerSample / 8) * wfx.nChannels; wfx.nAvgBytesPerSec = wfx.nBlockAlign * wfx.nSamplesPerSec; //打开音频输入设备 result = waveInOpen(&hWaveIn, WAVE_MAPPER, &wfx, 0, 0, WAVE_FORMAT_DIRECT); if(result != MMSYSERR_NOERROR) { return 0; } //分配音频缓冲区 dwSize = wfx.nSamplesPerSec * wfx.nBlockAlign; pBuffer = (char*)malloc(dwSize); memset(&WaveInHdr, 0, sizeof(WaveInHdr)); WaveInHdr.dwBufferLength = dwSize; WaveInHdr.lpData = pBuffer; //开始录制音频 result = waveInPrepareHeader(hWaveIn, &WaveInHdr, sizeof(WAVEHDR)); if(result != MMSYSERR_NOERROR) { return 0; } result = waveInAddBuffer(hWaveIn, &WaveInHdr, sizeof(WAVEHDR)); if(result != MMSYSERR_NOERROR) { return 0; } result = waveInStart(hWaveIn); if(result != MMSYSERR_NOERROR) { return 0; } Sleep(1000); //录制1秒钟 //停止录制音频 waveInStop(hWaveIn); waveInReset(hWaveIn); waveInUnprepareHeader(hWaveIn, &WaveInHdr, sizeof(WAVEHDR)); waveInClose(hWaveIn); //保存音频为WAV文件 HANDLE hFile; DWORD dwBytesWritten; int nBlockAlign = wfx.nBlockAlign; int nBytesPerSecond = wfx.nAvgBytesPerSec; DWORD dwChunkSize, dwFileLength; hFile = CreateFile("recording.wav", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if(hFile == INVALID_HANDLE_VALUE) { return 0; } dwFileLength = WaveInHdr.dwBufferLength + sizeof(WAVEFORMATEX) + sizeof(WAVEHEADER) - 8; dwChunkSize = WaveInHdr.dwBufferLength; //写入文件头 WriteFile(hFile, "RIFF", 4, NULL, 0); WriteFile(hFile, &dwFileLength, 4, NULL, 0); WriteFile(hFile, "WAVEfmt ", 8, NULL, 0); WriteFile(hFile, &wfx, sizeof(WAVEFORMATEX), NULL, 0); WriteFile(hFile, "data", 4, NULL, 0); WriteFile(hFile, &dwChunkSize, 4, NULL, 0); //写入音频数据 WriteFile(hFile, pBuffer, dwChunkSize, &dwBytesWritten, NULL); CloseHandle(hFile); free(pBuffer); return 0; } ``` 此示例程序使用了Windows系统下的音频库函数来录制并保存音频数据,如果是在其他系统下,需要使用相应系统的音频库函数来完成相同的操作。在编写应用程序时,可以根据实际需求来调整音频的采样率、采样位数、通道数等参数。个人学习和研究目的,可以运行此代码。 ### 回答2: C程序可以通过调用操作系统提供的音频录制库来实现录音并保存为WAV格式。在Windows操作系统下,可以使用Windows API中的MMSystem.h库,该库中包含了WAV格式文件的创建和写入函数。在Linux或Unix系统下,可以使用ALSA(Advanced Linux Sound Architecture)库来实现录音和保存为WAV格式。 录音步骤: 1. 先打开音频设备,设置音频采集参数,包括采样率、声道数、每个样本的位数等。 2. 然后申请缓冲区,设置缓冲区大小,让录音数据暂存于该缓冲区中。 3. 开始录音,并将音频数据填充到缓冲区里。 4. 当缓冲区满时,将有录音数据的缓冲区写入文件。 5. 循环执行步骤3和步骤4,直到达到录音时长或手动停止录音。 保存为WAV格式: WAV是一种数字音频文件格式,它是通过将波形采样数值编码为8位或16位的线性PCM数据来存储音频信息的。在保存为WAV格式时,需要按照WAV文件的格式来存储。 WAV格式的文件头由44个字节组成,其中包括文件格式类型、声道数、采样率、采样位数、数据长度等信息。在录音结束后,将录音数据与WAV文件头组成一个完整的WAV文件即可。 综上所述,通过调用操作系统提供的音频录制库实现录音并保存为WAV格式需要熟悉对应系统的音频处理API,同时需理解WAV文件格式,了解文件头和音频数据之间的对应关系,细心处理错误处理和边界情况。 ### 回答3: 录音并保存为wav是一种常见的c程序实现,主要分为如下步骤: 1. 初始化音频设备,首先需要调用snd_pcm_open函数打开音频设备,设置好录音通道、录音频率、音频格式等参数,然后通过snd_pcm_prepare函数准备好录音设备。 2. 录音,具体实现可以通过循环读取snd_pcm_readi()中的数据,将读到的数据写入到wav文件中。 3. 保存录音wav文件,需使用wav格式的文件头信息,即RIFF、fmt、data区块,其中fmt区块用于描述音频数据的格式,data区块则是整个Wav音频文件中存储实际音频数据的部分。 4. 关闭音频设备和文件,使用snd_pcm_close函数关闭音频设备,fclose函数关闭wav文件(也可以使用fflush刷新缓冲区,确保最后的数据都写入了文件) 需要注意的是,录制和保存音频数据的过程中,可能会涉及到缓存、数据截断、加入数据尾随填充等问题,因此需要对数据读写进行严密的处理和控制。同时,也需要保证录制功能稳定性,避免由于异常情况造成程序崩溃或者出错,所以需要全面测试并进行异常处理。
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值