文章目录

  • 1 头文件 AudioRecordWindows.h
  • 2 实现文件 AudioRecordWindows.cpp
  • 3 调用Demo AudioRecordWindowsTest.cpp

自己封装的在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 - 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相关推荐

  1. Windows进程管理类封装

    头文件 #include <Windows.h> #include <iostream> using namespace std; #include "stdafx. ...

  2. 简单的C++线程类实现, windows平台

    一个抽象的线程基类, 再来个具体的线程类并实现相关接口,再写个主函数来调用下.上代码: Thread.h /* Windows平台线程类实现 开发环境: Win7_x64 + VC2012 */ #i ...

  3. Windows平台类毕业论文文献包含哪些?

    本文是为大家整理的Windows平台主题相关的10篇毕业论文文献,包括5篇期刊论文和5篇学位论文,为Windows平台选题相关人员撰写毕业论文提供参考. 1.[期刊论文]Windows平台恶意软件智能 ...

  4. Windows平台最佳免费音频和视频软件

    转载:http://xkonglong.com/best-audio-video-for-windows/ Windows平台最佳免费音频和视频软件 2016年4月13日2016年4月13日 小恐龙 ...

  5. 基于科大讯飞语音云windows平台开发

    前记: 前段时间公司没事干,突发奇想想做一个语音识别系统,看起来应该很简单的,但做起来却是各种问题,这个对电气毕业的我,却是挺为难的.谷姐已经离我们而去,感谢度娘,感谢CSDN各位大神,好歹也做的是那 ...

  6. java h5在线音频_[语音技术]java+H5的录音类实例(1)

    看了某位仁兄的分享的关于C#的window平台的录音分装,有兴趣同学请访问楼下地址 https://ai.baidu.com/forum/topic/show/492634 于是,我也整理了自己项目中 ...

  7. python编写的程序可以在任何平台中执行-在Windows平台上编写的Python程序无法在Unix平台运行。...

    [单选题]在中华人民共和国境内飞行的民用航空器必须具有: ( ) [单选题]属于发热激活物的是: [单选题]年满( )周岁可以独立实施民事法律行为视为完全民事行为能力人 [单选题]在DIC的原发病中, ...

  8. Fast DDS入门二、Fast DDS在Windows平台的编译安装

    Fast DDS入门五.在Windows平台创建一个简单的Fast DDS示例程序 1 Fast DDS动态库的编译安装 本节提供了在Windows环境中从源代码安装Fast DDS的说明.将安装以下 ...

  9. Unity接入百度语音识别SDK windows平台

    1.先注册百度开放平台的账号,然后按文档申请试用资格及创建应用:https://ai.baidu.com/ai-doc/SPEECH/qknh9i8ed Windows平台选择"不需要&qu ...

最新文章

  1. Spring基础16——使用FactoryBean来创建
  2. javascript 回调函数
  3. opencv-python视频处理之录制视频
  4. 乐鑫ESP32完美对标AP6212,国产芯片不缺货,不涨价!牛
  5. Linux系列开坑记(二)-神的编辑器Vim
  6. python保存和加载数组
  7. 系统卡 服务器cpu 内存不足,电脑很卡 系统提示内存不足的解决办法
  8. 经典人生感悟 看看你少了那一条
  9. EndnoteX7/8/9参考文献不按顺序出现
  10. Appium的工作原理
  11. Linux Emacs 配置c++
  12. 7.spring之Bean的作用域
  13. potato电脑版连接不上_土豆电脑版-potato chat下载 v2.13.200323 电脑版 - 安下载
  14. 记录一次idea因为虚拟内存不足而闪退的经历
  15. Shell ${!shuzu[@]} 获取数组的所有下标
  16. Mars3D开发教程学习步骤(不定时更新
  17. android 9.0 SystemUI状态栏下拉快捷添加截图快捷开关
  18. 个人项目---音乐视频播放器
  19. windows cmd字典
  20. 链路追踪工具 skywalking

热门文章

  1. 《SpringBoot2.0 实战》系列-整合thymeleaf 实现模板文件转word打印
  2. 观古之四大名著《水浒传》有感
  3. 项目二 黄金矿工 1
  4. QQ相关(一)【导出所有QQ好友】
  5. SQL中destinct的用法
  6. 服务器怎么买,腾讯云服务器购买三种流程介绍
  7. 国产手机干翻苹果?原来是靠百元机和猛降价实现的
  8. 解决win10注册错误 错误代码0x8002801c
  9. APP切换到后台时的运行规则以及如何实现后台运行
  10. 蚂蚁微贷互动营销技术体系实践