WebRTC 音频采样算法 附完整C++示例代码
之前有大概介绍了音频采样相关的思路,详情见《简洁明了的插值音频重采样算法例子 (附完整C代码)》。
音频方面的开源项目很多很多。
最知名的莫过于谷歌开源的WebRTC,
其中的音频模块就包含有
AGC自动增益补偿(Automatic Gain Control)
自动调麦克风的收音量,使与会者收到一定的音量水平,不会因发言者与麦克风的距离改变时,声音有忽大忽小声的缺点。
ANS背景噪音抑制(Automatic Noise Suppression)
探测出背景固定频率的杂音并消除背景噪音。
AEC是回声消除器(Acoustic Echo Canceller)
对扬声器信号与由它产生的多路径回声的相关性为基础,建立远端信号的语音模型,利用它对回声进行估计,并不断地修改滤波器的系数,使得估计值更加逼近真实的回声。然后,将回声估计值从话筒的输入信号中减去,从而达到消除回声的目的,AEC还将话筒的输入与扬声器过去的值相比较,从而消除延长延迟的多次反射的声学回声。根椐存储器存放的过去的扬声器的输出值的多少,AEC可以消除各种延迟的回声。
在《音频增益响度分析 ReplayGain 附完整C代码示例》也提及到了。
不过本文还不是着重于这三个算法,还是先从采样算法来。
当然有兴趣的小伙伴,建议去看下 WebRTC中与signal_processing_library相关的操作算法。
有不少优化的思路可以学习之。
这里也不展开了。
之前说过采样可以采用简单的插值的方式进行模拟处理,在精度要求不高的情况下。
但是若是对精度有所要求,那就另论了。
好在前人踩坑,后人走路。
WebRTC中有一个音频采样器的类,虽然有一定的使用限制,但是在大多数应用场景下,也够用了。
WebRTC的代码是很干净,奈何,各个头文件之间的依赖,实在混乱。
不过稍微耐心,还是能把代码理出个七七八八。
稍微花了时间,造福下大家。
将WebRTC中的采样器代码单独抽离出来,
并编写了C++示例代码。
完整示例代码:
#include <cstdio> #include <cstdlib> #include <cstdint> //采用https://github.com/mackron/dr_libs/blob/master/dr_wav.h 解码 #define DR_WAV_IMPLEMENTATION #include "dr_wav.h" #include "resampler.h" //写wav文件 void wavWrite_int16(char *filename, int16_t *buffer, size_t sampleRate, size_t totalSampleCount) { drwav_data_format format = {}; format.container = drwav_container_riff; // <-- drwav_container_riff = normal WAV files, drwav_container_w64 = Sony Wave64. format.format = DR_WAVE_FORMAT_PCM; // <-- Any of the DR_WAVE_FORMAT_* codes. format.channels = ; format.sampleRate = (drwav_uint32) sampleRate; format.bitsPerSample = ; drwav *pWav = drwav_open_file_write(filename, &format); if (pWav) { drwav_uint64 samplesWritten = drwav_write(pWav, totalSampleCount, buffer); drwav_uninit(pWav); if (samplesWritten != totalSampleCount) { fprintf(stderr, "ERROR\n"); exit(); } } } //读取wav文件 int16_t *wavRead_int16(char *filename, uint32_t *sampleRate, uint64_t *totalSampleCount) { unsigned int channels; int16_t *buffer = drwav_open_and_read_file_s16(filename, &channels, sampleRate, totalSampleCount); if (buffer == nullptr) { printf("读取wav文件失败."); } //仅仅处理单通道音频 ) { drwav_free(buffer); buffer = nullptr; *sampleRate = ; *totalSampleCount = ; } return buffer; } //分割路径函数 void splitpath(const char *path, char *drv, char *dir, char *name, char *ext) { const char *end; const char *p; const char *s; ] && path[] == ':') { if (drv) { *drv++ = *path++; *drv++ = *path++; *drv = '\0'; } } else if (drv) *drv = '\0'; for (end = path; *end && *end != ':';) end++; for (p = end; p > path && *--p != '\\' && *p != '/';) if (*p == '.') { end = p; break; } if (ext) for (s = end; (*ext = *s++);) ext++; for (p = end; p > path;) if (*--p == '\\' || *p == '/') { p++; break; } if (name) { for (s = p; s < end;) *name++ = *s++; *name = '\0'; } if (dir) { for (s = path; s < p;) *dir++ = *s++; *dir = '\0'; } } int16_t *resampler(int16_t *data_in, size_t totalSampleCount, size_t in_sample_rate, size_t out_sample_rate) { if (data_in == nullptr) return nullptr; ) return nullptr; size_t lengthIn = in_sample_rate / ; size_t maxLen = out_sample_rate / ; ; Resampler rs; size_t outLen = (size_t) (totalSampleCount * out_sample_rate / in_sample_rate); int16_t *data_out = (int16_t *) malloc(outLen * sizeof(int16_t)); if (data_out == nullptr) return nullptr; size_t nCount = (totalSampleCount / lengthIn); size_t nLast = totalSampleCount - (lengthIn * nCount); int16_t *samplesIn = data_in; int16_t *samplesOut = data_out; rs.Reset(in_sample_rate, out_sample_rate, channels); outLen = ; ; i < nCount; i++) { rs.Push(samplesIn, lengthIn, samplesOut, maxLen, outLen); samplesIn += lengthIn; samplesOut += outLen; } ) { ; int16_t samplePatchIn[max_samples] = {}; int16_t samplePatchOut[max_samples] = {}; memcpy(samplePatchIn, samplesIn, nLast * sizeof(int16_t)); rs.Push(samplesIn, nLast, samplePatchOut, maxLen, outLen); memcpy(samplesOut, samplePatchOut, (nLast * out_sample_rate / in_sample_rate) * sizeof(int16_t)); } return data_out; } ) { //音频采样率 uint32_t sampleRate = ; //总音频采样数 uint64_t inSampleCount = ; int16_t *inBuffer = wavRead_int16(in_file, &sampleRate, &inSampleCount); //如果加载成功 if (inBuffer != nullptr) { int16_t *outBuffer = resampler(inBuffer, (size_t) inSampleCount, sampleRate, out_sample_rate); if (outBuffer != nullptr) { size_t outSampleCount = (size_t) (inSampleCount * (out_sample_rate * 1.0f / sampleRate)); wavWrite_int16(out_file, outBuffer, out_sample_rate, outSampleCount); free(outBuffer); } free(inBuffer); } } int main(int argc, char *argv[]) { printf("WebRtc Resampler\n"); printf("博客:http://tntmonks.cnblogs.com/\n"); printf("音频插值重采样\n"); printf("支持采样率: 8k、16k、32k、48k、96k\n"); ) ; ]; ]; ]; ]; ]; ]; splitpath(in_file, drive, dir, fname, ext); sprintf(out_file, "%s%s%s_out%s", drive, dir, fname, ext); ResampleTo(in_file, out_file, ); getchar(); printf("按任意键退出程序 \n"); ; }
项目地址:https://github.com/cpuimage/WebRTC_Resampler
采样器的代码很简单,详情见resampler.cpp
示例具体流程为:
加载wav(拖放wav文件到可执行文件上)->重采样->保存为_out.wav文件
示例比较简单,用cmake即可进行编译示例代码,详情见CMakeLists.txt。
若有其他相关问题或者需求也可以邮件联系俺探讨。
邮箱地址是:
gaozhihan@vip.qq.com
WebRTC 音频采样算法 附完整C++示例代码的更多相关文章
- 人脸姿态校正算法 附完整C++示例代码
在一些特殊情况下,经常需要依据图像中的人脸,对图片进行倾斜矫正. 例如拍照角度幅度过大之类的情况,而进行人工矫正确实很叫人头大. 那是不是可以有一种算法,可以根据人脸的信息对图片进行角度的修复呢? 答 ...
- 基于傅里叶变换的音频重采样算法 (附完整c代码)
前面有提到音频采样算法: WebRTC 音频采样算法 附完整C++示例代码 简洁明了的插值音频重采样算法例子 (附完整C代码) 近段时间有不少朋友给我写过邮件,说了一些他们使用的情况和问题. 坦白讲, ...
- 音频降噪算法 附完整C代码
降噪是音频图像算法中的必不可少的. 目的肯定是让图片或语音 更加自然平滑,简而言之,美化. 图像算法和音频算法 都有其共通点. 图像是偏向 空间 处理,例如图片中的某个区域. 图像很多时候是以二维数据 ...
- 单独编译和使用webrtc音频降噪模块(附完整源码+测试音频文件)
单独编译和使用webrtc音频增益模块(附完整源码+测试音频文件) 单独编译和使用webrtc音频回声消除模块(附完整源码+测试音频文件) webrtc的音频处理模块分为降噪ns,回音消除aec,回声 ...
- 单独编译和使用webrtc音频增益模块(附完整源码+测试音频文件)
webrtc的音频处理模块分为降噪ns和nsx,回音消除aec,回声控制acem,音频增益agc,静音检测部分.另外webrtc已经封装好了一套音频处理模块APM,如果不是有特殊必要,使用者如果要用到 ...
- 基于RNN的音频降噪算法 (附完整C代码)
前几天无意间看到一个项目rnnoise. 项目地址: https://github.com/xiph/rnnoise 基于RNN的音频降噪算法. 采用的是 GRU/LSTM 模型. 阅读下训练代码,可 ...
- mser 最大稳定极值区域(文字区域定位)算法 附完整C代码
mser 的全称:Maximally Stable Extremal Regions 第一次听说这个算法时,是来自当时部门的一个同事, 提及到他的项目用它来做文字区域的定位,对这个算法做了一些优化. ...
- WebRTC 音频算法 附完整C代码
WebRTC提供一套音频处理引擎, 包含以下算法: AGC自动增益控制(Automatic Gain Control) ANS噪音抑制(Automatic Noise Suppression) AEC ...
- 音频自动增益 与 静音检测 算法 附完整C代码
前面分享过一个算法<音频增益响度分析 ReplayGain 附完整C代码示例> 主要用于评估一定长度音频的音量强度, 而分析之后,很多类似的需求,肯定是做音频增益,提高音量诸如此类做法. ...
随机推荐
- WCF跨域解决方法及一些零碎的东西。
之前发过一篇随笔,说的WCF配置文件配置问题.里面也配了跨域支持,但是jsoncollback只支持Get请求,Post请求是解决不了,所以这里把真正的WCF跨域问题贴出来. 话不多说,直接帖配置文件 ...
- JavaScript(第八天)【时间与日期】
ECMAScript提供了Date类型来处理时间和日期.Date类型内置一系列获取和设置日期时间信息的方法. 一.Date类型 ECMAScript中的Date类型是在早期Java中java.util ...
- HIVE的常用操作(HQL)语句
HIVE基本操作命令 创建数据库 >create database db_name; >create database if not exists db_name;//创建一个不存在的数据 ...
- 解决fiddler无法抓取本地部署项目的请求问题
在本地部署了几个应用,然后想用fiddler抓取一些请求看看调用了哪些接口,然鹅,一直抓不到... 比如访问地址是这样的: 在网上搜罗半天,找到一个解决方法 在localhost或127.0.0.1后 ...
- 201621123060《JAVA程序设计》第十一周学习总结
1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结多线程相关内容. 2. 书面作业 本次PTA作业题集多线程 1. 源代码阅读:多线程程序BounceThread 1.1 BallR ...
- Bate敏捷冲刺每日报告--day3
1 团队介绍 团队组成: PM:齐爽爽(258) 小组成员:马帅(248),何健(267),蔡凯峰(285) Git链接:https://github.com/WHUSE2017/C-team 2 ...
- 2017-2018-1 我爱学Java 第三周 作业
Team Presentation 团队展示 队员学号 队名 团队项目描述 队员风采 团队首次合照 团队的特色描述 团队初步合作 前两周合作过程中的优缺点 如何改进 团队选题 确立,建立和初步熟悉团队 ...
- LR录制脚本的时候打不开浏览器问题
使用Chrome时,显示开始录制但是Action中无任何脚本,即脚本没成功生成. 使用Firefox(最新版),一直关闭程序,详细信息有StackHash_0a9e. 使用IE11时,也是显示开始录制 ...
- leetcode题解 6.ZigZag Conversion
6.ZigZag Conversion 题目: The string "PAYPALISHIRING" is written in a zigzag pattern on a gi ...
- js 防止重复点击
1.添加flag 适用于ajax 表单提交,提交之前flag = false , 提及中,true ,提交后false 2.事件重复点击: <script> var throttle = ...