Windows平台录音类封装:AudioRecordWindows
文章目录
- 1 头文件 AudioRecordWindows.h
- 2 实现文件 AudioRecordWindows.cpp
- 3 调用Demo AudioRecordWindowsTest.cpp
自己封装的在Windows平台上的录音类
支持:
- 打开录音设备
- 关闭录音设备
- 开始录音
- 停止录音
- 重置录音
- 保存录音为wav音频文件
- 保存录音为pcm裸流音频文件
- 可回调录音状态与录音数据
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 - 8char wave[4]; // = "WAVE"char fmt[4]; // = "fmt "UINT32 fmt_size; // = PCMWAVEFORMAT的大小 : //PCMWAVEFORMATUINT16 format_tag; // = PCM : 1UINT16 channels; // = 通道数 : 1UINT32 samples_per_sec; // = 采样率 : 8000 | 6000 | 11025 | 16000UINT32 avg_bytes_per_sec; // = 每秒平均字节数 : samples_per_sec * bits_per_sample / 8UINT16 block_align; // = 每采样点字节数 : wBitsPerSample / 8UINT16 bits_per_sample; // = 量化精度: 8 | 16char data[4]; // = "data";//DATAUINT32 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 // !1waveInOpen(&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);// 追加RawDatafwrite(m_AudioData.data(), AUDIO_DATA_BLOCK_SIZE * m_AudioData.size(), 1, m_WavFileOpen);// 写入结束fclose(m_WavFileOpen);}void AudioRecordWindows::WritePcmFile(){// 追加RawDatafwrite(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){// 消息switchswitch (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;
}
如果您觉得这篇博文有用,请访问我的个人站:http://www.stubbornhuang.com/,更多博文干货等着您。
Windows平台录音类封装:AudioRecordWindows相关推荐
- Windows进程管理类封装
头文件 #include <Windows.h> #include <iostream> using namespace std; #include "stdafx. ...
- 简单的C++线程类实现, windows平台
一个抽象的线程基类, 再来个具体的线程类并实现相关接口,再写个主函数来调用下.上代码: Thread.h /* Windows平台线程类实现 开发环境: Win7_x64 + VC2012 */ #i ...
- Windows平台类毕业论文文献包含哪些?
本文是为大家整理的Windows平台主题相关的10篇毕业论文文献,包括5篇期刊论文和5篇学位论文,为Windows平台选题相关人员撰写毕业论文提供参考. 1.[期刊论文]Windows平台恶意软件智能 ...
- Windows平台最佳免费音频和视频软件
转载:http://xkonglong.com/best-audio-video-for-windows/ Windows平台最佳免费音频和视频软件 2016年4月13日2016年4月13日 小恐龙 ...
- 基于科大讯飞语音云windows平台开发
前记: 前段时间公司没事干,突发奇想想做一个语音识别系统,看起来应该很简单的,但做起来却是各种问题,这个对电气毕业的我,却是挺为难的.谷姐已经离我们而去,感谢度娘,感谢CSDN各位大神,好歹也做的是那 ...
- java h5在线音频_[语音技术]java+H5的录音类实例(1)
看了某位仁兄的分享的关于C#的window平台的录音分装,有兴趣同学请访问楼下地址 https://ai.baidu.com/forum/topic/show/492634 于是,我也整理了自己项目中 ...
- python编写的程序可以在任何平台中执行-在Windows平台上编写的Python程序无法在Unix平台运行。...
[单选题]在中华人民共和国境内飞行的民用航空器必须具有: ( ) [单选题]属于发热激活物的是: [单选题]年满( )周岁可以独立实施民事法律行为视为完全民事行为能力人 [单选题]在DIC的原发病中, ...
- Fast DDS入门二、Fast DDS在Windows平台的编译安装
Fast DDS入门五.在Windows平台创建一个简单的Fast DDS示例程序 1 Fast DDS动态库的编译安装 本节提供了在Windows环境中从源代码安装Fast DDS的说明.将安装以下 ...
- Unity接入百度语音识别SDK windows平台
1.先注册百度开放平台的账号,然后按文档申请试用资格及创建应用:https://ai.baidu.com/ai-doc/SPEECH/qknh9i8ed Windows平台选择"不需要&qu ...
最新文章
- Spring基础16——使用FactoryBean来创建
- javascript 回调函数
- opencv-python视频处理之录制视频
- 乐鑫ESP32完美对标AP6212,国产芯片不缺货,不涨价!牛
- Linux系列开坑记(二)-神的编辑器Vim
- python保存和加载数组
- 系统卡 服务器cpu 内存不足,电脑很卡 系统提示内存不足的解决办法
- 经典人生感悟 看看你少了那一条
- EndnoteX7/8/9参考文献不按顺序出现
- Appium的工作原理
- Linux Emacs 配置c++
- 7.spring之Bean的作用域
- potato电脑版连接不上_土豆电脑版-potato chat下载 v2.13.200323 电脑版 - 安下载
- 记录一次idea因为虚拟内存不足而闪退的经历
- Shell ${!shuzu[@]} 获取数组的所有下标
- Mars3D开发教程学习步骤(不定时更新
- android 9.0 SystemUI状态栏下拉快捷添加截图快捷开关
- 个人项目---音乐视频播放器
- windows cmd字典
- 链路追踪工具 skywalking