目录

接口头文件

接口实现文件

依赖模块头文件

依赖模块实现


接口头文件

#pragma once#include <al.h>
#include <alc.h>
#include <string>
#include <map>
#include <vector>namespace MyNamespace
{class AudioCapture{public://FIXME: 添加函数注释/**\brief 构造函数 \param sDevName 设备名称\param nSmplRate 采样率,默认为44100\param eAudFmt 音频格式,默认为AL_FORMAT_STEREO16(16位立体声)\note 本函数可能抛出异常*/AudioCapture(const std::string & sDevName,const ALCuint nSmplRate = 44100,const ALCenum eAudFmt = AL_FORMAT_STEREO16);/**\brief 析构函数 \note 本函数不会抛出异常*/~AudioCapture();/**\brief 启动设备 \note 本函数可能抛出异常*/void start();/**\brief 停止设备 \note 本函数可能抛出异常*/void stop();/**\brief 获取设备采集的数据\param nSmplNum (输出)设备采集到的样本数\param pDataBuf (输出)数据保存缓冲区指针\param nDataSize(输出)采集到的数据大小,单位是Byte\param nBufSize  数据保存缓冲区大小,单位是Byte\return 设备是否采集到数据\note 本函数可能抛出异常*/bool getData(ALint & nSmplNum, ALbyte * pDataBuf, ALsizei & nDataSize, const size_t nBufSize);/**\brief 查询设备是否已经启动\return 设备是否已经启动\note 本函数不会抛出异常*/bool isStart();/**\brief 查询设备是否已经连接在操作系统中\return 设备是否已经连接在操作系统中\note 本函数可能抛出异常*/bool isConnect();/**\brief 查询系统中可以进行音频采集的设备\return 系统中可以进行音频采集的设备列表\note 本函数可能抛出异常*/static size_t listCaptureDevices(std::vector<std::string> & devLst);private:static const std::string getErrStr(const ALenum nErrCode) noexcept;private:ALCdevice *m_pCapDev = nullptr;bool m_bStart = false;const std::string m_sDevName; /// 设备名称const ALCuint m_nSmplRate;    /// 音频采样率const ALCenum m_eAudFmt;    /// 音频格式const int C_SAMPLE_BUFFER_SIZE = 1024;//音频格式列表const std::map<ALCenum, ALuint> C_AUDIO_FORMAT_DIC {{AL_FORMAT_STEREO16, 4},{AL_FORMAT_STEREO8, 2},{AL_FORMAT_MONO16, 2},{AL_FORMAT_MONO8, 1},};};
}

接口实现文件

#include "AudioCapture.h"
#include "Log.h"
#include "DeviceUtil.h"#include <algorithm>using namespace std;#define ADD_LOC(sMsg) \(string(__FILE__).append("(Ln ").append(to_string(__LINE__)) \.append(")::").append(__FUNCTION__).append("()::").append(sMsg)).c_str() #define CALL_AL_FUN(fun, errMsg) { \alGetError(); \fun; \ALenum eErrCode = AL_NO_ERROR; \if ((eErrCode = alGetError()) != AL_NO_ERROR) { \string sMsg = string(errMsg) + ", error code is " + getErrStr(eErrCode); \throw runtime_error(ADD_LOC(sMsg)); \} else { /* 不进行任何操作 */ } \
}namespace MyNamespace
{AudioCapture::AudioCapture(const string & sDevName, const ALCuint nSmplRate, const ALCenum eAudFmt) : m_sDevName(sDevName), m_nSmplRate(nSmplRate), m_eAudFmt(eAudFmt){FU_LOG_TRACE_SCOPE();if (sDevName.empty()) {throw invalid_argument("Microphone name is empty");} else if (0 == m_nSmplRate) {throw invalid_argument("Sample rate is zero");} else if (C_AUDIO_FORMAT_DIC.find(m_eAudFmt) == C_AUDIO_FORMAT_DIC.end()) {throw invalid_argument("Audio format is invald");} else { /* do nothing */ }FU_LOG_DEBUG("Create audio capture \"" + m_sDevName + "\"");//FU_LOG_DEBUG("m_sDevName = \"" + m_sDevName + "\"");//FU_LOG_DEBUG("m_nSmplRate = " + to_string(m_nSmplRate));//FU_LOG_DEBUG("m_eAudFmt = " + to_string(m_eAudFmt));//FU_LOG_DEBUG("C_SAMPLE_BUFFER_SIZE = " + to_string(C_SAMPLE_BUFFER_SIZE));CALL_AL_FUN(m_pCapDev = alcCaptureOpenDevice(m_sDevName.c_str(), m_nSmplRate, m_eAudFmt, C_SAMPLE_BUFFER_SIZE), "Fail to open capture device");if (!m_pCapDev) {throw runtime_error(("Fail to open microphone whose name is " + sDevName).c_str());} else { /* do nothing */ }}AudioCapture::~AudioCapture(){FU_LOG_TRACE_SCOPE();FU_LOG_DEBUG("Destroy audio capture \"" + m_sDevName + "\"");if (m_bStart) {FU_LOG_TRACE("Stop audio capture \"" + m_sDevName + "\"");stop();} else { /* do nothing */ }FU_LOG_TRACE("Close audio capture \"" + m_sDevName + "\"");alcCaptureCloseDevice(m_pCapDev);}const string AudioCapture::getErrStr(const ALenum nErrCode) noexcept{FU_LOG_TRACE_SCOPE();switch (nErrCode) {case AL_NO_ERROR:return "No Error";break;case AL_INVALID_NAME:return "Invalid Name paramater passed to AL call";break;case AL_ILLEGAL_ENUM:return "Invalid parameter passed to AL call";break;case AL_INVALID_VALUE:return "Invalid enum parameter value";break;case AL_ILLEGAL_COMMAND:return "Illegal call";break;case AL_OUT_OF_MEMORY:return "Out of memory ";break;default:return "No known error code";}}void AudioCapture::start(){FU_LOG_TRACE_SCOPE();FU_LOG_DEBUG("Start capture device \"" + m_sDevName + "\"");CALL_AL_FUN(alcCaptureStart(m_pCapDev), "Fail to start capturing audio");m_bStart = true;}void AudioCapture::stop(){FU_LOG_TRACE_SCOPE();FU_LOG_DEBUG("Stop capture device \"" + m_sDevName + "\"");if (!m_bStart) {return;} else { /* do nothing */ }CALL_AL_FUN(alcCaptureStop(m_pCapDev), "Fail to stop capturing audio");m_bStart = false;}bool AudioCapture::getData(ALint & nSmplNum, ALbyte * pDataBuf, ALsizei & nDataSize, const size_t nBufSize){FU_LOG_TRACE_SCOPE();if (0 == nBufSize) {throw invalid_argument("Buffer size is zero");} else if (nullptr == pDataBuf) {throw invalid_argument("Buffer pointer is null");} else { /* do nothing */ }if (!m_bStart) {throw runtime_error("It has not start capturing audio");} else { /* do nothing */ }CALL_AL_FUN(alcGetIntegerv(m_pCapDev, ALC_CAPTURE_SAMPLES, static_cast<ALCsizei>(sizeof(ALint)), &nSmplNum), "Fail to get number of captured samples");if (nSmplNum <= 0) {return false;} else { /* do nothing */ }nDataSize = nSmplNum * C_AUDIO_FORMAT_DIC.at(m_eAudFmt);if (static_cast<decltype(nBufSize)>(nDataSize) > nBufSize) {throw runtime_error("Buffer capacity is not enough");} else { /* do nothing */ }CALL_AL_FUN(alcCaptureSamples(m_pCapDev, (ALCvoid *)pDataBuf, nSmplNum),"Fail to get data of captured samples");return true;}bool AudioCapture::isStart(){return m_bStart;}bool AudioCapture::isConnect(){vector<string> micLst;listCaptureDevices(micLst);return micLst.end() != find(micLst.begin(), micLst.end(), m_sDevName);}size_t AudioCapture::listCaptureDevices(std::vector<std::string> & devLst){DeviceUtil::getMicsName(devLst);return devLst.size();}
}

依赖模块头文件

#pragma once#include <string>
#include <map>
#include <vector>namespace MyNamespace
{class DeviceUtil{public:/**\brief 查询系统中所有麦克风的名称\param micsName(输出)系统中所有的麦克风名称\note 本函数可能抛出异常*/static void getMicsName(std::vector<std::string>& micsName);private:/**\brief 查询系统中所有麦克风\param micsName(输出)系统中所有的麦克风\note 本函数可能抛出异常*/static void getMicDevices(std::map<std::wstring, std::wstring>& deviceMap);};
}

依赖模块实现

#include "DeviceUtil.h"
#include "utility.hpp"
#include "UtilTools.h"#include <mmdeviceapi.h>
#include <functiondiscoverykeys_devpkey.h>#include <functional>using namespace std;namespace MyNamespace
{void checkCallRes(const HRESULT res, const string & sFunNm) {if (FAILED(res)) {throw runtime_error(("Fail to call " + sFunNm + ", error code is "+ to_string(res)).c_str());} else { /* do nothing */ }}void GetDevices(IMMDeviceCollection * pMMDeviceCollection, map<wstring, wstring>& deviceMap){bool ret = true;char wszErrMsg[MAX_PATH] = { 0 };UINT count;HRESULT hr = pMMDeviceCollection->GetCount(&count);checkCallRes(hr, "IMMDeviceCollection::GetCount");for (UINT i = 0; i < count; i++) {IMMDevice *pMMDevice = nullptr;IPropertyStore *pPropertyStore = nullptr;PROPVARIANT pv;Utility::ScopeGuard scpGrd([]() {}, [&]() {if (pMMDevice != nullptr) {pMMDevice->Release();}if (pPropertyStore != nullptr) {pPropertyStore->Release();}if (pv.vt != VT_EMPTY) {hr = PropVariantClear(&pv);checkCallRes(hr, "PropVariantClear");}});PropVariantInit(&pv);// get the "n"th devicehr = pMMDeviceCollection->Item(i, &pMMDevice);checkCallRes(hr, "IMMDeviceCollection::Item");LPWSTR pwszDeviceId = nullptr;hr = pMMDevice->GetId(&pwszDeviceId);checkCallRes(hr, "IMMDeviceCollection::GetId");// open the property store on that devicehr = pMMDevice->OpenPropertyStore(STGM_READ, &pPropertyStore);checkCallRes(hr, "IMMDevice::OpenPropertyStore");// get the long name propertyhr = pPropertyStore->GetValue(PKEY_Device_FriendlyName, &pv);checkCallRes(hr, "IPropertyStore::GetValue");if (VT_LPWSTR != pv.vt) {throw runtime_error(("PKEY_Device_FriendlyName variant type is "+ to_string(pv.vt) + " - expected VT_LPWSTR").c_str());} else { /* do nothing */ }deviceMap[pwszDeviceId] = pv.pwszVal;}}void DeviceUtil::getMicsName(std::vector<std::string>& micsName){micsName.clear();map<wstring, wstring> micsMap;getMicDevices(micsMap);for (const auto & pair : micsMap) {micsName.emplace_back(UtilTools::wstr2str(pair.second));}}void DeviceUtil::getMicDevices(map<wstring, wstring>& deviceMap){deviceMap.clear();bool ret = false;char wszErrMsg[MAX_PATH] = { 0 };IMMDeviceEnumerator *pMMDeviceEnumerator = nullptr;IMMDeviceCollection *pMMDeviceCollection = nullptr;Utility::ScopeGuard scpGrd([]() {}, [&]() {//释放资源if (pMMDeviceEnumerator != nullptr) {pMMDeviceEnumerator->Release();}if (pMMDeviceCollection != nullptr) {pMMDeviceCollection->Release();}});CoInitialize(nullptr);HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_ALL,__uuidof(IMMDeviceEnumerator),reinterpret_cast<void**>(&pMMDeviceEnumerator));checkCallRes(hr, "CoCreateInstance");// get all the active capture endpointshr = pMMDeviceEnumerator->EnumAudioEndpoints(eCapture, DEVICE_STATE_ACTIVE, &pMMDeviceCollection);checkCallRes(hr, "EnumAudioEndpoints");GetDevices(pMMDeviceCollection, deviceMap);}
}

基于OpenAL的音频采集模块相关推荐

  1. 基于傅里叶变换的音频重采样算法 (附完整c代码)

    前面有提到音频采样算法: WebRTC 音频采样算法 附完整C++示例代码 简洁明了的插值音频重采样算法例子 (附完整C代码) 近段时间有不少朋友给我写过邮件,说了一些他们使用的情况和问题. 坦白讲, ...

  2. 基于 CoreAudio 的音频编解码(二):音频编码

    系列文章目录 基于 CoreAudio 的音频编解码(一):音频解码 基于 CoreAudio 的音频编解码(二):音频编码 前言 在 基于 CoreAudio 的音频编解码(一):音频解码 中,我们 ...

  3. 基于 CoreAudio 的音频编解码(一):音频解码

    系列文章目录 基于 CoreAudio 的音频编解码(一):音频解码 基于 CoreAudio 的音频编解码(二):音频编码 前言 Core Audio 是iOS和MAC系统中的关于数字音频处理的基础 ...

  4. 基于 FFMPEG 的音频编解码(三):音频编码

    音频编码 基于 FFMPEG 的音频编解码(一):Hello FFMPEG,安装与编译 基于 FFMPEG 的音频编解码(二):音频解码 基于 FFMPEG 的音频编解码(三):音频编码 在前面文章中 ...

  5. 基于 FFMPEG 的音频编解码(二):音频解码

    音频解码 基于 FFMPEG 的音频编解码(一):Hello FFMPEG,安装与编译 基于 FFMPEG 的音频编解码(二):音频解码 基于 FFMPEG 的音频编解码(三):音频编码 在 Hell ...

  6. 基于 FFMPEG 的音频编解码(一):Hello FFMPEG,安装与编译

    Hello FFMPEG 基于 FFMPEG 的音频编解码(一):Hello FFMPEG,安装与编译 基于 FFMPEG 的音频编解码(二):音频解码 基于 FFMPEG 的音频编解码(三):音频编 ...

  7. 最简单的基于FFMPEG的音频编码器(PCM编码为AAC)

    本文介绍一个最简单的基于FFMPEG的音频编码器.该编码器实现了PCM音频采样数据编码为AAC的压缩编码数据.编码器代码十分简单,但是每一行代码都很重要.通过看本编码器的源代码,可以了解FFMPEG音 ...

  8. python音频实时频谱分析_基于python的音频设计及频谱分析

    74 Internet Technology 互联网 + 技术 一.引言 WAV 是 Microsoft 开发的一种声音文件格式,虽然它支持多种压缩格式,但是它通常被用来保存未压缩的声音数据(PCM ...

  9. 基于sinc的音频重采样(二):实现

    上篇(基于sinc的音频重采样(一):原理)讲了基于sinc方法的重采样原理,并给出了数学表达式,如下:                  (1) 本文讲如何基于这个数学表达式来做软件实现.软件实现的 ...

最新文章

  1. 【译Py】数据科学面试终极指南(二)
  2. nginx 网站目录重写
  3. Oracle入门(十四.18)之使用动态SQL
  4. 可编程ic卡 通用吗_8255可编程IC
  5. 【script】python多线程模块threading的使用
  6. 深圳职业技术学院计算机专业组代码,深圳职业技术学院代码是多少 有哪些优势专业...
  7. java 工具箱安装方法,Java工具包的安装配置和使用-JSP教程,Java技巧及代码
  8. Sublime Text关闭自动更新
  9. 松本行弘为什么开发Ruby
  10. NUVOTON新唐单片机使用总结
  11. arduino tft 方向_Arduino2.4英寸TFT显示屏的演示
  12. ClickHouse快速安装-可视化工具连接-创建第一个ck库表(一)
  13. STM32基于 FatFs R0.14bSD Card 的MP3音乐播放器(也算是FatFs的简单应用了吧)
  14. java.io.FileNotFoundException: file:/xxx/xxx.jar!/BOOT-INF/classes!/xxx.xlsx (没有那个文件或目录)
  15. 移动端:js判断苹果ios各类机型
  16. Linux 下安装 Tomcat 6(zhuang)
  17. 基于SSM的二手书推荐系统(商城)
  18. 太阳能发电系统的构成及简单工作原理
  19. ONE一言每天一句名言API
  20. 让人和猪都很感动的广告

热门文章

  1. vs2010编译64位qt5.5
  2. SSP广告引擎(1)对接流程
  3. Access 2003中文版即学即会视频教程(下)
  4. 把仙剑奇侠传5的音乐从pkg里请出来变成mp3吧
  5. 基于javaweb的养老院管理系统(java+ssm+bootstrap+jsp+mysql)
  6. 头戴式蓝牙耳机哪款音质比较好?2020最受欢迎五款高清音质蓝牙耳机分享
  7. 银行自助设备详细介绍(四)——存款机
  8. java jsch example_JSch基本使用
  9. Python提取通达信日数据转存为.csv表格数据
  10. s7200cpu224xp手册_S7-200 CPU224XP 通讯连接不上