音频自动增益 与 静音检测 算法 附完整C代码
前面分享过一个算法《音频增益响度分析 ReplayGain 附完整C代码示例》
主要用于评估一定长度音频的音量强度,
而分析之后,很多类似的需求,肯定是做音频增益,提高音量诸如此类做法。
不过在项目实测的时候,其实真的很难定标准,
到底在什么样的环境下,要增大音量,还是降低。
在通讯行业一般的做法就是采用静音检测,
一旦检测为静音或者噪音,则不做处理,反之通过一定的策略进行处理。
这里就涉及到两个算法,一个是静音检测,一个是音频增益。
增益其实没什么好说的,类似于数据归一化拉伸的做法。
静音检测 在WebRTC中 是采用计算GMM (Gaussian Mixture Model,高斯混合模型)进行特征提取的。
在很长一段时间里面,音频特征 有3个主要的方法,
GMM ,Spectrogram (声谱图), MFCC 即 Mel-Frequency Cepstrum(Mel频率倒谱)
恕我直言,GMM 提取的特征,其鲁棒性 不如后两者。
也不多做介绍,感兴趣的同学,翻翻 维基百科 ,补补课。
当然在实际使用算法时,会由此延伸出来一些小技巧。
例如,用静音检测 来做音频裁剪,或者搭配音频增益做一些音频增强之类的操作。
自动增益在WebRTC 源代码文件是:analog_agc.c 和 digital_agc.c
静音检测 源代码文件是: webrtc_vad.c
这个命名,有一定的历史原因了。
经过梳理后,
增益算法为 agc.c agc.h
静音检测为 vad.c vad.h
增益算法的完整示例代码:
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
//采用https://github.com/mackron/dr_libs/blob/master/dr_wav.h 解码
#define DR_WAV_IMPLEMENTATION
#include "dr_wav.h"
#include "agc.h" #ifndef nullptr
#define nullptr 0
#endif #ifndef MIN
#define MIN(A, B) ((A) < (B) ? (A) : (B))
#endif //写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文件失败.");
}
//仅仅处理单通道音频
if (channels != ) {
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;
if (path[] && 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';
}
} int agcProcess(int16_t *buffer, uint32_t sampleRate, size_t samplesCount, int16_t agcMode) {
if (buffer == nullptr) return -;
if (samplesCount == ) return -;
WebRtcAgcConfig agcConfig;
agcConfig.compressionGaindB = ; // default 9 dB
agcConfig.limiterEnable = ; // default kAgcTrue (on)
agcConfig.targetLevelDbfs = ; // default 3 (-3 dBOv)
int minLevel = ;
int maxLevel = ;
size_t samples = MIN(, sampleRate / );
if (samples == ) return -;
const int maxSamples = ;
int16_t *input = buffer;
size_t nTotal = (samplesCount / samples);
void *agcInst = WebRtcAgc_Create();
if (agcInst == NULL) return -;
int status = WebRtcAgc_Init(agcInst, minLevel, maxLevel, agcMode, sampleRate);
if (status != ) {
printf("WebRtcAgc_Init fail\n");
WebRtcAgc_Free(agcInst);
return -;
}
status = WebRtcAgc_set_config(agcInst, agcConfig);
if (status != ) {
printf("WebRtcAgc_set_config fail\n");
WebRtcAgc_Free(agcInst);
return -;
}
size_t num_bands = ;
int inMicLevel, outMicLevel = -;
int16_t out_buffer[maxSamples];
int16_t *out16 = out_buffer;
uint8_t saturationWarning = ; //是否有溢出发生,增益放大以后的最大值超过了65536
int16_t echo = ; //增益放大是否考虑回声影响
for (int i = ; i < nTotal; i++) {
inMicLevel = ;
int nAgcRet = WebRtcAgc_Process(agcInst, (const int16_t *const *) &input, num_bands, samples,
(int16_t *const *) &out16, inMicLevel, &outMicLevel, echo,
&saturationWarning); if (nAgcRet != ) {
printf("failed in WebRtcAgc_Process\n");
WebRtcAgc_Free(agcInst);
return -;
}
memcpy(input, out_buffer, samples * sizeof(int16_t));
input += samples;
}
WebRtcAgc_Free(agcInst);
return ;
} void auto_gain(char *in_file, char *out_file) {
//音频采样率
uint32_t sampleRate = ;
//总音频采样数
uint64_t inSampleCount = ;
int16_t *inBuffer = wavRead_int16(in_file, &sampleRate, &inSampleCount);
//如果加载成功
if (inBuffer != nullptr) {
// kAgcModeAdaptiveAnalog 模拟音量调节
// kAgcModeAdaptiveDigital 自适应增益
// kAgcModeFixedDigital 固定增益
agcProcess(inBuffer, sampleRate, inSampleCount, kAgcModeAdaptiveDigital);
wavWrite_int16(out_file, inBuffer, sampleRate, inSampleCount);
free(inBuffer);
}
} int main(int argc, char *argv[]) {
printf("WebRTC Automatic Gain Control\n");
printf("博客:http://cpuimage.cnblogs.com/\n");
printf("音频自动增益\n");
if (argc < )
return -;
char *in_file = argv[];
char drive[];
char dir[];
char fname[];
char ext[];
char out_file[];
splitpath(in_file, drive, dir, fname, ext);
sprintf(out_file, "%s%s%s_out%s", drive, dir, fname, ext);
auto_gain(in_file, out_file); printf("按任意键退出程序 \n");
getchar();
return ;
}
静音检测完整示例代码:
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
//采用https://github.com/mackron/dr_libs/blob/master/dr_wav.h 解码
#define DR_WAV_IMPLEMENTATION #include "dr_wav.h"
#include "vad.h" #ifndef nullptr
#define nullptr 0
#endif #ifndef MIN
#define MIN(A, B) ((A) < (B) ? (A) : (B))
#endif #ifndef MAX
#define MAX(A, B) ((A) > (B) ? (A) : (B))
#endif //读取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文件失败.");
}
//仅仅处理单通道音频
if (channels != ) {
drwav_free(buffer);
buffer = nullptr;
*sampleRate = ;
*totalSampleCount = ;
}
return buffer;
} int vadProcess(int16_t *buffer, uint32_t sampleRate, size_t samplesCount, int16_t vad_mode, int per_ms_frames) {
if (buffer == nullptr) return -;
if (samplesCount == ) return -;
// kValidRates : 8000, 16000, 32000, 48000
// 10, 20 or 30 ms frames
per_ms_frames = MAX(MIN(, per_ms_frames), );
size_t samples = sampleRate * per_ms_frames / ;
if (samples == ) return -;
int16_t *input = buffer;
size_t nTotal = (samplesCount / samples); void *vadInst = WebRtcVad_Create();
if (vadInst == NULL) return -;
int status = WebRtcVad_Init(vadInst);
if (status != ) {
printf("WebRtcVad_Init fail\n");
WebRtcVad_Free(vadInst);
return -;
}
status = WebRtcVad_set_mode(vadInst, vad_mode);
if (status != ) {
printf("WebRtcVad_set_mode fail\n");
WebRtcVad_Free(vadInst);
return -;
}
printf("Activity : \n");
for (int i = ; i < nTotal; i++) {
int nVadRet = WebRtcVad_Process(vadInst, sampleRate, input, samples);
if (nVadRet == -) {
printf("failed in WebRtcVad_Process\n");
WebRtcVad_Free(vadInst);
return -;
} else {
// output result
printf(" %d \t", nVadRet);
}
input += samples;
}
printf("\n");
WebRtcVad_Free(vadInst);
return ;
} void vad(char *in_file) {
//音频采样率
uint32_t sampleRate = ;
//总音频采样数
uint64_t inSampleCount = ;
int16_t *inBuffer = wavRead_int16(in_file, &sampleRate, &inSampleCount);
//如果加载成功
if (inBuffer != nullptr) {
// Aggressiveness mode (0, 1, 2, or 3)
int16_t mode = ;
int per_ms = ;
vadProcess(inBuffer, sampleRate, inSampleCount, mode, per_ms);
free(inBuffer);
}
} int main(int argc, char *argv[]) {
printf("WebRTC Voice Activity Detector\n");
printf("博客:http://cpuimage.cnblogs.com/\n");
printf("静音检测\n");
if (argc < )
return -;
char *in_file = argv[];
vad(in_file);
printf("按任意键退出程序 \n");
getchar();
return ;
}
自动增益项目地址:https://github.com/cpuimage/WebRTC_AGC
具体流程为:
加载wav(拖放wav文件到可执行文件上)->增益处理->保存为_out.wav文件
静音检测项目地址:https://github.com/cpuimage/WebRTC_VAD
具体流程为:
加载wav(拖放wav文件到可执行文件上)->输出静音检测结果
备注 :1 为非静音,0 为静音
该注意的地方和参数,见代码注释。
用cmake即可进行编译示例代码,详情见CMakeLists.txt。
若有其他相关问题或者需求也可以邮件联系俺探讨。
邮箱地址是:
gaozhihan@vip.qq.com
音频自动增益 与 静音检测 算法 附完整C代码的更多相关文章
- 音频自动增益 与 静音检测 算法 附完整C代码【转】
转自:https://www.cnblogs.com/cpuimage/p/8908551.html 前面分享过一个算法<音频增益响度分析 ReplayGain 附完整C代码示例> 主要用 ...
- 音频降噪算法 附完整C代码
降噪是音频图像算法中的必不可少的. 目的肯定是让图片或语音 更加自然平滑,简而言之,美化. 图像算法和音频算法 都有其共通点. 图像是偏向 空间 处理,例如图片中的某个区域. 图像很多时候是以二维数据 ...
- mser 最大稳定极值区域(文字区域定位)算法 附完整C代码
mser 的全称:Maximally Stable Extremal Regions 第一次听说这个算法时,是来自当时部门的一个同事, 提及到他的项目用它来做文字区域的定位,对这个算法做了一些优化. ...
- 基于RNN的音频降噪算法 (附完整C代码)
前几天无意间看到一个项目rnnoise. 项目地址: https://github.com/xiph/rnnoise 基于RNN的音频降噪算法. 采用的是 GRU/LSTM 模型. 阅读下训练代码,可 ...
- 基于傅里叶变换的音频重采样算法 (附完整c代码)
前面有提到音频采样算法: WebRTC 音频采样算法 附完整C++示例代码 简洁明了的插值音频重采样算法例子 (附完整C代码) 近段时间有不少朋友给我写过邮件,说了一些他们使用的情况和问题. 坦白讲, ...
- 自动曝光修复算法 附完整C代码
众所周知, 图像方面的3A算法有: AF自动对焦(Automatic Focus)自动对焦即调节摄像头焦距自动得到清晰的图像的过程 AE自动曝光(Automatic Exposure)自动曝光的是为了 ...
- WebRTC 音频算法 附完整C代码
WebRTC提供一套音频处理引擎, 包含以下算法: AGC自动增益控制(Automatic Gain Control) ANS噪音抑制(Automatic Noise Suppression) AEC ...
- 磨皮美颜算法 附完整C代码
前言 2017年底时候写了这篇<集 降噪 美颜 虚化 增强 为一体的极速图像润色算法 附Demo程序> 这也算是学习过程中比较有成就感的一个算法. 自2015年做算法开始到今天,还有个把月 ...
- 图片文档倾斜矫正算法 附完整c代码
2年前在学习图像算法的时候看到一个文档倾斜矫正的算法. 也就是说能将一些文档图像进行旋转矫正, 当然这个算法一般用于一些文档扫描软件做后处理 或者用于ocr 文字识别做前处理. 相关的关键词: 抗倾斜 ...
随机推荐
- VS2013 重装 无法打开项目
今天遇到的奇葩BUG,耗时我一下午,现在跟大家说道说道. 今天重装系统,让各种开发环境开发工具自然要重装一次,最后装完VS2013,然后刚好客户打电话要改点东西,然后我就双击项目准备打开改,然后奇葩来 ...
- Node入门教程(1)目录
aicoder.com 全栈实习之简明 Node 入门文档 aicoder.com 线下实习: 不 8000 就业,不还实习费. 如果需要转载本文档,请联系老马,Q: 515154084 JS基础教程 ...
- c# 字符串的内存分配和驻留池( 转 )
刚开始学习C#的时候,就听说CLR对于String类有一种特别的内存管理机制:有时候,明明声明了两个String类的对象,但是他们偏偏却指向同一个实例.如下: string s1 = "he ...
- MySQL 主从复制那些事(一)
本部分主要以理论为主,具体的主从搭建环境,大家可以参考博客其他部分.下面我就给大家数说主从复制那些理论的东西.说的不一定都是正确的,有不同出入的地方,欢迎大家一起交流沟通,以下我把我自己整理出来的主从 ...
- gradle入门(1-1)gradle的概念和使用
一.Gradle是什么 Gradle是一种Java应用构建工具,它采用领域特定语言 Groovy 语法实现配置. 1.Gradle的基本概念 项目:项目的配置 即 build.gradle. 任务:任 ...
- django中图片的上传和显示
上传图片实际上是 把图片存在服务器的硬盘中,将图片存储的路径存在数据库中. 1 首先要配置文件上传的路径: 1.1 建立静态文件目录 在项目根目录下 新建一个 static文件夹,下面再建立一个med ...
- Python之格式化输出,初始编码以及运算符
一.题型 1.使用while循环输入 1 2 3 4 5 6 8 9 10 count = 0 while count < 10: count += 1 #count = count + ...
- Hibernate(十三):HQL查询(二)
背景 基于上一章节<Hibernate(十二):HQL查询(一)>,已经学习了一部分关于HQL的用法: HQL带参数查询 HQL Order By排序查询 HQL 设置实体参数查询 本章节 ...
- ZOJ-1649 Rescue---BFS+优先队列
题目链接: https://vjudge.net/problem/ZOJ-1649 题目大意: 天使的朋友要去救天使,a是天使,r 是朋友,x是卫兵.每走一步需要时间1,打倒卫兵需要另外的时间1,问救 ...
- 定义一个方法get_page(url),url参数是需要获取网页内容的网址,返回网页的内容。提示(可以了解python的urllib模块)
定义一个方法get_page(url),url参数是需要获取网页内容的网址,返回网页的内容.提示(可以了解python的urllib模块) import urllib.request def get_ ...