自己封装的在Windows平台上的录音
支持:
1.打开录音设备
2.关闭录音设备
3.开始录音
4.停止录音
5.重置录音
6.保存录音为wav音频文件
7.保存录音为pcm裸流音频文件
8.可回调录音状态与录音数据

1 头文件 AudioRecordWindows.h
#ifndef _AUDIO_RECORD_WINDOWS_H_
#define _AUDIO_RECORD_WINDOWS_H_
#include <Windows.h>
#include <iostream>
#include <array>
#include <vector>
#pragma comment(lib,"winmm.lib")
#define CHANNEL_NUM  1                                           // 声道数
#define SAMPLE_RATE 16000                                        // 每秒采样率
#define SAMPLE_BITS 16                                           // 采样位深
#define AUDIO_DATA_BLOCK_SIZE (SAMPLE_RATE*SAMPLE_BITS / 8 * 1)  // 缓存数据块大小 = 采样率*位深/2*秒(字节)                                
#define BUFFER_NUM 10                                            // 缓冲区层数
//!
//! @brief 录音状态枚举 
//! 
enum RecordStatus
{
OpenDevice,
RecordStart,
RecordWriteData,
RecordStop,
CloseDevice
};
//!
//! @brief 回调函数
//! 
typedef void( *AudioRecordCallback)(std::array <char, AUDIO_DATA_BLOCK_SIZE> audioDataBlock, RecordStatus recordStatus);
namespace AudioRecordSpace
{
//!
//! @brief wav文件头
//! 
typedef struct WavHeader
{
char            riff[4];                    // = "RIFF"
UINT32          size_8;                     // = FileSize - 8
char            wave[4];                    // = "WAVE"
char            fmt[4];                     // = "fmt "
UINT32          fmt_size;                   // = PCMWAVEFORMAT的大小 : 
//PCMWAVEFORMAT
UINT16          format_tag;                 // = PCM : 1
UINT16          channels;                   // = 通道数 : 1
UINT32          samples_per_sec;            // = 采样率 : 8000 | 6000 | 11025 | 16000
UINT32          avg_bytes_per_sec;          // = 每秒平均字节数 : samples_per_sec * bits_per_sample / 8
UINT16          block_align;                // = 每采样点字节数 : wBitsPerSample / 8
UINT16          bits_per_sample;            // = 量化精度: 8 | 16
char            data[4];                    // = "data";
//DATA
UINT32          data_size;                  // = 裸数据长度 
};
class AudioRecordWindows
{
public:
AudioRecordWindows();
virtual~AudioRecordWindows();
//!
//! @brief 打开录音设备
//!
bool OpenAudioDevice(); 
//!
//! @brief 关闭录音设备
//!
void CloseAudioDevice();
//!
//! @brief 初始化录音参数
//!
void InitWaveFormat(LPWAVEFORMATEX WaveFormat, WORD ChannelNum, DWORD SampleRate, WORD BitsPerSample);
//!
//! @brief 开始录音
//!
void StartRecord();
//!
//! @brief 停止录音
//!
void StopRecord();
//!
//! @brief 重置录音
//!
void ResetRecord();
//!
//! @brief 设置需要录音wav文件名称
//!
void SetWavFileName(const char* filePath);
//!
//! @brief 设置需要录音wav文件名称
//!
void SetPcmFileName(const char* filePath);
//!
//! @brief 写录音wav文件
//!
void WriteWavFile();
//!
//! @brief 写录音pcm文件
//!
void WritePcmFile();
//!
//! @brief 注册回调函数
//!
void RegisterCallback(AudioRecordCallback audioCallback);
//!
//! @brief 系统录音回调函数
//!
static DWORD(CALLBACK WaveAPI_Callback)(        // WaveAPI回调函数
HWAVEIN hWaveIn,                            //  输入设备
UINT uMsg,                                  //  消息
DWORD_PTR dwInstance,                       //  保留
DWORD_PTR dwParam1,                         //  刚填充好的缓冲区指针
DWORD_PTR dwParam2                          //  保留
);                      
private:
HWAVEIN m_AudioDevice;                                                   // 音频输入设备
WAVEHDR m_WaveHdrBuffer[BUFFER_NUM];                                     // 声明缓冲区
static std::array<char, AUDIO_DATA_BLOCK_SIZE> m_AudioDataBlock;       // 当前录制一帧音频数据
static std::vector<std::array<char, AUDIO_DATA_BLOCK_SIZE>> m_AudioData; // 存储所有录制的音频数据
static bool m_bStopRecord;                                               // 是否停止录音
static bool m_bPushData;                                                 // 是否向m_AudioDataBlock中push数据
std::string m_WavFilePath;                                               // Wav录音文件名称
std::string m_PcmFilePath;                                               // Pcm录音文件名称
bool m_bSaveWavFile;                                                     // 是否保存wav文件
FILE* m_WavFileOpen;                                                     // 录音wav文件指针
bool m_bSavePcmFile;                                                     // 是否保存Pcm文件
FILE* m_PcmFileOpen;                                                     // 录音Pcm文件指针
WavHeader m_WavHeader;                                                   // wav文件头
static AudioRecordCallback m_Callback;                                   // 外部回调函数
static bool m_bCallback;                                                 // 是否回调外部回调函数
};
}
#endif // !_AUDIO_RECORD_WINDOWS_H_
2 实现文件 AudioRecordWindows.cpp
#include "AudioRecordWindows.h"
namespace AudioRecordSpace
{
// 静态变量初始化
std::array <char, AUDIO_DATA_BLOCK_SIZE> AudioRecordWindows::m_AudioDataBlock = {};
std::vector<std::array<char, AUDIO_DATA_BLOCK_SIZE>> AudioRecordWindows::m_AudioData = { {} };
bool AudioRecordWindows::m_bStopRecord = false;
bool AudioRecordWindows::m_bPushData = true;
bool AudioRecordWindows::m_bCallback = false;;
AudioRecordCallback AudioRecordWindows::m_Callback = NULL;
AudioRecordWindows::AudioRecordWindows()
{
m_WavFileOpen = NULL;
m_PcmFileOpen = NULL;
m_bSaveWavFile = false;
m_bSavePcmFile = false;
m_WavFilePath = "";
m_PcmFilePath = "";
m_WavHeader =
{
{ 'R', 'I', 'F', 'F' },
0,
{ 'W', 'A', 'V', 'E' },
{ 'f', 'm', 't', ' ' },
sizeof(PCMWAVEFORMAT) ,
WAVE_FORMAT_PCM,
1,
SAMPLE_RATE,
SAMPLE_RATE*(SAMPLE_BITS / 8),
SAMPLE_BITS / 8,
SAMPLE_BITS,
{ 'd', 'a', 't', 'a' },
0
};
}
AudioRecordWindows::~AudioRecordWindows()
{
}
bool AudioRecordWindows::OpenAudioDevice()
{
int audioDeviceNum = waveInGetNumDevs();
if (audioDeviceNum <= 0)
{
std::cout << "Windows没有找到录音设备,请确认Windows找到了录音设备" << std::endl;
return false;
}
else
{
for (unsigned int i = 0; i < audioDeviceNum; ++i)
{
WAVEINCAPS waveInCaps;
MMRESULT mmResult = waveInGetDevCaps(i, &waveInCaps, sizeof(WAVEINCAPS));
if (i == 0)
{
std::cout << "当前默认的录音设备信息描述:" << waveInCaps.szPname << std::endl;
}
else
{
std::cout << "其他录音设备信息描述" << waveInCaps.szPname << std::endl;
}
}
}
WAVEFORMATEX waveFormate;
InitWaveFormat(&waveFormate, CHANNEL_NUM, SAMPLE_RATE, SAMPLE_BITS);
//#ifndef _WIN64
//  waveInOpen(&m_AudioDevice, WAVE_MAPPER, &waveFormate, (DWORD)WaveAPI_Callback, DWORD(this), CALLBACK_FUNCTION);
//#else
//  waveInOpen(&m_AudioDevice, WAVE_MAPPER, &waveFormate, (DWORD_PTR)WaveAPI_Callback, DWORD_PTR(this), CALLBACK_FUNCTION);
//#endif // !1
waveInOpen(&m_AudioDevice, WAVE_MAPPER, &waveFormate, (DWORD_PTR)WaveAPI_Callback, DWORD_PTR(this), CALLBACK_FUNCTION);
if (m_bCallback)
{
m_Callback(m_AudioDataBlock, RecordStatus::OpenDevice);
}
return true;
}
void AudioRecordWindows::CloseAudioDevice()
{
if (m_bCallback)
{
m_Callback(m_AudioDataBlock, RecordStatus::CloseDevice);
}
waveInClose(m_AudioDevice);
}
void AudioRecordWindows::InitWaveFormat(LPWAVEFORMATEX WaveFormat, WORD ChannelNum, DWORD SampleRate, WORD BitsPerSample)
{
// 配置音频波形参数
WaveFormat->wFormatTag = WAVE_FORMAT_PCM;
WaveFormat->nChannels = ChannelNum;
WaveFormat->nSamplesPerSec = SampleRate;
WaveFormat->nAvgBytesPerSec = SampleRate * ChannelNum * BitsPerSample / 8;
WaveFormat->nBlockAlign = ChannelNum * BitsPerSample / 8;
WaveFormat->wBitsPerSample = BitsPerSample;
WaveFormat->cbSize = 0;
std::cout << "采样参数:" << std::endl;
std::cout << "声道数" << ChannelNum << std::endl;
std::cout << "采样率" << SampleRate << "Hz" << std::endl;
std::cout << "位深" << BitsPerSample << std::endl;
}
void AudioRecordWindows::StartRecord()
{
for (unsigned int i = 0; i < BUFFER_NUM; ++i)
{
m_WaveHdrBuffer[i].lpData = new char[AUDIO_DATA_BLOCK_SIZE];
m_WaveHdrBuffer[i].dwBufferLength = AUDIO_DATA_BLOCK_SIZE;
m_WaveHdrBuffer[i].dwBytesRecorded = 0;
m_WaveHdrBuffer[i].dwUser = i;
m_WaveHdrBuffer[i].dwFlags = 0;
m_WaveHdrBuffer[i].dwLoops = 0;
m_WaveHdrBuffer[i].lpNext = NULL;
m_WaveHdrBuffer[i].reserved = 0;
// 排进缓冲区
waveInPrepareHeader(m_AudioDevice, &m_WaveHdrBuffer[i], sizeof(WAVEHDR));
waveInAddBuffer(m_AudioDevice, &m_WaveHdrBuffer[i], sizeof(WAVEHDR));
}
// 清除视频缓冲
m_AudioData.clear();
m_AudioData.shrink_to_fit();
m_AudioData.resize(0);
// 开始录音
waveInStart(m_AudioDevice);
if (m_bCallback)
{
m_Callback(m_AudioDataBlock, RecordStatus::RecordStart);
}
}
void AudioRecordWindows::StopRecord()
{
m_bStopRecord = true;
// 停止录音设备
waveInStop(m_AudioDevice);
waveInReset(m_AudioDevice);
if (m_bCallback)
{
m_Callback(m_AudioDataBlock, RecordStatus::RecordStop);
}
// 释放缓冲区
for (unsigned int i = 0; i < BUFFER_NUM; ++i)
{
waveInUnprepareHeader(m_AudioDevice,&m_WaveHdrBuffer[i], sizeof(WAVEHDR));
delete m_WaveHdrBuffer[i].lpData;
m_WaveHdrBuffer[i].lpData = NULL;
}
// 保存wav文件
if (m_bSaveWavFile)
{
WriteWavFile();
}
// 保存pcm文件
if (m_bSavePcmFile)
{
WritePcmFile();
}
}
void AudioRecordWindows::ResetRecord()
{
m_AudioData.clear();
m_AudioData.shrink_to_fit();
m_bSaveWavFile = false;
m_bSavePcmFile = false;
m_bPushData = true;
m_bStopRecord = false;
m_bCallback = false;
m_Callback = NULL;
m_WavFileOpen = NULL;
m_PcmFileOpen = NULL;
m_WavFilePath = "";
m_PcmFilePath = "";
}
void AudioRecordWindows::SetWavFileName(const char * filePath)
{
m_bSaveWavFile = true;
m_WavFilePath = filePath;
// 尝试打开文件,创建文件
errno_t err = fopen_s(&m_WavFileOpen, m_WavFilePath.c_str(), "wb");
if (err > 0)
{
std::cout << "文件创建失败:" << err << " 检查文件名和占用" << std::endl;
m_bSaveWavFile = false;
}
}
void AudioRecordWindows::SetPcmFileName(const char * filePath)
{
m_bSavePcmFile = true;
m_PcmFilePath = filePath;
// 尝试打开文件,创建文件
errno_t err = fopen_s(&m_PcmFileOpen, m_PcmFilePath.c_str(), "wb");
if (err > 0)
{
std::cout << "文件创建失败:" << err << " 检查文件名和占用" << std::endl;
m_bSavePcmFile = false;
}
}
void AudioRecordWindows::WriteWavFile()
{
// 编辑并写入Wave头信息
m_WavHeader.data_size = AUDIO_DATA_BLOCK_SIZE * m_AudioData.size();
m_WavHeader.size_8 = m_WavHeader.data_size + 32;
fwrite(&m_WavHeader, sizeof(m_WavHeader), 1, m_WavFileOpen);
// 追加RawData
fwrite(m_AudioData.data(), AUDIO_DATA_BLOCK_SIZE * m_AudioData.size(), 1, m_WavFileOpen);
// 写入结束
fclose(m_WavFileOpen);
}
void AudioRecordWindows::WritePcmFile()
{
// 追加RawData
fwrite(m_AudioData.data(), AUDIO_DATA_BLOCK_SIZE * m_AudioData.size(), 1, m_PcmFileOpen);
// 写入结束
fclose(m_PcmFileOpen);
}
void AudioRecordWindows::RegisterCallback(AudioRecordCallback audioCallback)
{
m_bCallback = true;
m_Callback = audioCallback;
}
DWORD(AudioRecordWindows::WaveAPI_Callback)(HWAVEIN hWaveIn, UINT uMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2)
{
// 消息switch
switch (uMsg)
{
case WIM_OPEN:      // 设备成功已打开
std::cout << "设备成功打开" << std::endl;
break;
case WIM_DATA:      // 缓冲区数据填充完毕
// 停止后会频繁发出WIM_DATA,已经将数据转移所以不必理会后继数据【后继数据在这里来看是是重复的】
if (m_bPushData)
{
std::cout << "缓冲区数据填充完毕" << std::endl;
// 把缓冲区数据拷贝出来
memcpy(m_AudioDataBlock.data(), ((LPWAVEHDR)dwParam1)->lpData, AUDIO_DATA_BLOCK_SIZE);
// 没有录进去的被填充为0xcd,改成0来避免末尾出现爆音【只在结束录音时进行,不影响添加缓存效率】
if (((LPWAVEHDR)dwParam1)->dwBytesRecorded < AUDIO_DATA_BLOCK_SIZE)
{
for (size_t i = ((LPWAVEHDR)dwParam1)->dwBytesRecorded; i < AUDIO_DATA_BLOCK_SIZE; i++)
{
m_AudioDataBlock.at(i) = 0;
}
}
// 添加这一帧
m_AudioData.push_back(m_AudioDataBlock);
// 如果你设置了回调函数
if (m_bCallback)
{
m_Callback(m_AudioDataBlock,RecordStatus::RecordWriteData);
}
}
// 如果需要停止录音则不继续添加缓存
if (!m_bStopRecord)
{
waveInAddBuffer(hWaveIn, (LPWAVEHDR)dwParam1, sizeof(WAVEHDR));//添加到缓冲区
}
else
{
// 如果已经停止了录制,就不要再写入数据
m_bPushData = false;
}
break;
case WIM_CLOSE:
// 操作成功完成
std::cout << "录音设备已经关闭..." << std::endl;
break;
default:
break;
}
return 0;
}
}
3 调用Demo AudioRecordWindowsTest.cpp
#include "AudioRecordWindows.h"
#include "conio.h"
using namespace std;
using namespace AudioRecordSpace;
void Callback(std::array <char, AUDIO_DATA_BLOCK_SIZE> audioDataBlock, RecordStatus recordStatus)
{
if (recordStatus == RecordStatus::OpenDevice)
{
cout << "回调 打开设备" << endl;
}
else if (recordStatus == RecordStatus::RecordStart)
{
cout << "回调 开始录音" << endl;
}
else if (recordStatus == RecordStatus::RecordWriteData)
{
cout << "回调 正在写入数据" << endl;
}
else if (recordStatus == RecordStatus::RecordStop)
{
cout << "回调 停止录音" << endl;
}
else if (recordStatus == RecordStatus::CloseDevice)
{
cout << "回调 关闭设备" << endl;
}
}
int main()
{
char ch = 0;
AudioRecordWindows sound_gbr;
while (ch != 'q')
{
ch = _getch();
switch (ch)
{
case 's':
cout << "开始录音" << endl;
sound_gbr.RegisterCallback(Callback);
sound_gbr.OpenAudioDevice();
sound_gbr.SetPcmFileName("test.pcm");
sound_gbr.StartRecord();
break;
case 'q':
cout << "结束录音" << endl;
sound_gbr.StopRecord();
sound_gbr.CloseAudioDevice();
sound_gbr.ResetRecord();
break;
default:
break;
}
}
getchar();
return 0;
}

测试结果如下:
Windows平台录音类封装:AudioRecordWindows-StubbornHuang Blog