音频降噪算法 附完整C代码

降噪是音频图像算法中的必不可少的。

目的肯定是让图片或语音 更加自然平滑,简而言之,美化。

图像算法和音频算法 都有其共通点。

图像是偏向 空间 处理,例如图片中的某个区域。

图像很多时候是以二维数据为主,矩形数据分布。

音频更偏向 时间 处理,例如语音中的某短时长。

音频一般是一维数据为主,单声道波长。

处理方式也是差不多,要不单通道处理,然后合并,或者直接多通道处理。

只是处理时候数据参考系维度不一而已。

一般而言,

图像偏向于多通道处理,音频偏向于单通道处理。

而从数字信号的角度来看,也可以理解为聚类,频率归一化之类的。

总之就是对一些有一定规律的数字数据进行计算处理。

图像降噪被磨皮美颜这个大主题给带远了。

音频降噪目前感觉大有所为,像前面分享的《基于RNN的音频降噪算法 (附完整C代码)

能达到这样的降噪效果,深度学习 确实有它独到的一面。

但是无可厚非,做机器学习之前还是要基于前人很多 基础算法进行数据的预处理等操作。

才能达到至善至美。

各有忧虑,所谓算法肯定是互相配合为主,没有说谁能替换掉谁。

做算法最核心的思路就是使用各个算法的核心思想,放大它的优点,弱化它的缺点。

当然,做人也是如此。

音频降噪算法,网上公开的算法不多,资源也比较有限。

还是谷歌做了好事,把WebRTC开源,确实是一个基础。

前人种树,后人乘凉。

花了点时间,把WebRTC的噪声抑制模块提取出来,方便他人。

噪声抑制在WebRTC中有两个版本,一个是浮点,一个是定点。

一般定点做法是为了在一些特定环境下牺牲极少的精度,提升计算性能。

这个就不展开了,涉及到算法性能优化方面的一些知识点。

至于算法的实现,见源代码:

浮点版本:

noise_suppression.c

定点版本:

noise_suppression_x.c

算法提供4个降噪级别,分别是:

enum nsLevel {    kLow,    kModerate,    kHigh,    kVeryHigh};

实测效果还是很不错的,不过在一些特定的应用场景下,其实这个算法还可以进一步调优。改进思路,很多时候是基于需求来的,打住打住,不细说了。

完整示例代码:
#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 "noise_suppression.h"

#ifndef nullptr
#define nullptr 0
#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 = 1;
    format.sampleRate = (drwav_uint32) sampleRate;
    format.bitsPerSample = 16;
    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(1);
        }
    }
}

//读取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 != 1) {
        drwav_free(buffer);
        buffer = nullptr;
        *sampleRate = 0;
        *totalSampleCount = 0;
    }
    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[0] && path[1] == ‘:‘) {
        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‘;
    }
}

enum nsLevel {
    kLow,
    kModerate,
    kHigh,
    kVeryHigh
};

static float S16ToFloat_C(int16_t v) {
    if (v > 0) {
        return ((float) v) / (float) INT16_MAX;
    }

    return (((float) v) / ((float) -INT16_MIN));
}

void S16ToFloat(const int16_t *src, size_t size, float *dest) {
    size_t i;
    for (i = 0; i < size; ++i)
        dest[i] = S16ToFloat_C(src[i]);
}

static int16_t FloatToS16_C(float v) {
    static const float kMaxRound = (float) INT16_MAX - 0.5f;
    static const float kMinRound = (float) INT16_MIN + 0.5f;
    if (v > 0) {
        v *= kMaxRound;
        return v >= kMaxRound ? INT16_MAX : (int16_t) (v + 0.5f);
    }

    v *= -kMinRound;
    return v <= kMinRound ? INT16_MIN : (int16_t) (v - 0.5f);
}

void FloatToS16(const float *src, size_t size, int16_t *dest) {
    size_t i;
    for (i = 0; i < size; ++i)
        dest[i] = FloatToS16_C(src[i]);
}

int nsProcess(int16_t *buffer, size_t sampleRate, int samplesCount, enum nsLevel level) {
    if (buffer == nullptr) return -1;
    if (samplesCount == 0) return -1;
    size_t samples = WEBRTC_SPL_MIN(160, sampleRate / 100);
    if (samples == 0) return -1;
    const int maxSamples = 320;
    int num_bands = 1;
    int16_t *input = buffer;
    size_t nTotal = (samplesCount / samples);

    NsHandle *nsHandle = WebRtcNs_Create();

    int status = WebRtcNs_Init(nsHandle, sampleRate);
    if (status != 0) {
        printf("WebRtcNs_Init fail\n");
        return -1;
    }
    status = WebRtcNs_set_policy(nsHandle, level);
    if (status != 0) {
        printf("WebRtcNs_set_policy fail\n");
        return -1;
    }
    for (int i = 0; i < nTotal; i++) {
        float inf_buffer[maxSamples];
        float outf_buffer[maxSamples];
        S16ToFloat(input, samples, inf_buffer);
        float *nsIn[1] = {inf_buffer};   //ns input[band][data]
        float *nsOut[1] = {outf_buffer};  //ns output[band][data]
        WebRtcNs_Analyze(nsHandle, nsIn[0]);
        WebRtcNs_Process(nsHandle, (const float *const *) nsIn, num_bands, nsOut);
        FloatToS16(outf_buffer, samples, input);
        input += samples;
    }
    WebRtcNs_Free(nsHandle);

    return 1;
}

void noise_suppression(char *in_file, char *out_file) {
    //音频采样率
    uint32_t sampleRate = 0;
    //总音频采样数
    uint64_t inSampleCount = 0;
    int16_t *inBuffer = wavRead_int16(in_file, &sampleRate, &inSampleCount);

    //如果加载成功
    if (inBuffer != nullptr) {
        nsProcess(inBuffer, sampleRate, inSampleCount, kVeryHigh);
        wavWrite_int16(out_file, inBuffer, sampleRate, inSampleCount);

        free(inBuffer);
    }
}

int main(int argc, char *argv[]) {
    printf("WebRtc Noise Suppression\n");
    printf("博客:http://cpuimage.cnblogs.com/\n");
    printf("音频噪声抑制\n");
    if (argc < 2)
        return -1;
    char *in_file = argv[1];
    char drive[3];
    char dir[256];
    char fname[256];
    char ext[256];
    char out_file[1024];
    splitpath(in_file, drive, dir, fname, ext);
    sprintf(out_file, "%s%s%s_out%s", drive, dir, fname, ext);
    noise_suppression(in_file, out_file);

    printf("按任意键退出程序 \n");
    getchar();
    return 0;
}

项目地址:https://github.com/cpuimage/WebRTC_NS

示例具体流程为:

加载wav(拖放wav文件到可执行文件上)->降噪->保存wav

若有其他相关问题或者需求也可以邮件联系俺探讨。

邮箱地址是: 
[email protected]

原文地址:https://www.cnblogs.com/cpuimage/p/8905965.html

时间: 2024-10-26 12:29:55

音频降噪算法 附完整C代码的相关文章

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

前面有提到音频采样算法: WebRTC 音频采样算法 附完整C++示例代码 简洁明了的插值音频重采样算法例子 (附完整C代码) 近段时间有不少朋友给我写过邮件,说了一些他们使用的情况和问题. 坦白讲,我精力有限,但一般都会抽空回复一下. 大多数情况,阅读一下代码就能解决的问题, 也是要尝试一下的. 没准,你就解决了呢? WebRtc的采样算法本身就考虑到它的自身应用场景, 所以它会有一些局限性,例如不支持任意采样率等等. 而简洁插值的这个算法, 我个人也一直在使用,因为简洁明了,简单粗暴. 我自

mser 最大稳定极值区域(文字区域定位)算法 附完整C代码

mser 的全称:Maximally Stable Extremal Regions 第一次听说这个算法时,是来自当时部门的一个同事, 提及到他的项目用它来做文字区域的定位,对这个算法做了一些优化. 也就是中文车牌识别开源项目EasyPR的作者liuruoze,刘兄. 自那时起就有一块石头没放下,想要找个时间好好理理这个算法. 学习一些它的一些思路. 因为一般我学习算法的思路:3个做法, 第一步,编写demo示例. 第二步,进行算法移植或效果改进. 第三步,进行算法性能优化. 然后在这三个过程中

音频自动增益 与 静音检测 算法 附完整C代码

前面分享过一个算法<音频增益响度分析 ReplayGain 附完整C代码示例> 主要用于评估一定长度音频的音量强度, 而分析之后,很多类似的需求,肯定是做音频增益,提高音量诸如此类做法. 不过在项目实测的时候,其实真的很难定标准, 到底在什么样的环境下,要增大音量,还是降低. 在通讯行业一般的做法就是采用静音检测, 一旦检测为静音或者噪音,则不做处理,反之通过一定的策略进行处理. 这里就涉及到两个算法,一个是静音检测,一个是音频增益. 增益其实没什么好说的,类似于数据归一化拉伸的做法. 静音检

音频自动增益 与 静音检测 算法 附完整C代码【转】

转自:https://www.cnblogs.com/cpuimage/p/8908551.html 前面分享过一个算法<音频增益响度分析 ReplayGain 附完整C代码示例> 主要用于评估一定长度音频的音量强度, 而分析之后,很多类似的需求,肯定是做音频增益,提高音量诸如此类做法. 不过在项目实测的时候,其实真的很难定标准, 到底在什么样的环境下,要增大音量,还是降低. 在通讯行业一般的做法就是采用静音检测, 一旦检测为静音或者噪音,则不做处理,反之通过一定的策略进行处理. 这里就涉及到

自动曝光修复算法 附完整C代码

众所周知, 图像方面的3A算法有: AF自动对焦(Automatic Focus)自动对焦即调节摄像头焦距自动得到清晰的图像的过程 AE自动曝光(Automatic Exposure)自动曝光的是为了使感光器件获得合适的曝光量 AW自动白平衡(Automatic White Balance)白平衡的本质是使白色物体在任何光源下都显示白色 前面的文章也有提及过,在刚开始做图像算法的时候,我是先攻克的自动白平衡算法. 后来攻克自动曝光的时候,傻啦吧唧的,踩了不少坑. 我相信一定不止我一个,一开始的时

图片文档倾斜矫正算法 附完整c代码

2年前在学习图像算法的时候看到一个文档倾斜矫正的算法. 也就是说能将一些文档图像进行旋转矫正, 当然这个算法一般用于一些文档扫描软件做后处理 或者用于ocr 文字识别做前处理. 相关的关键词: 抗倾斜 反倾斜  Deskew 等等. 最简单算法实现思路,采用 霍夫变换(Hough Transform)进行直线检测, 当然也可以用霍夫变换检测圆. 在倾斜矫正算法中,自然就是检测直线. 通过对检测出来的直线进行角度判断, 一般取 认可度最高的几条直线进行计算, 最后求取均衡后的角度值. 进行图像角度

传统高斯模糊与优化算法(附完整C++代码)

高斯模糊(英语:Gaussian Blur),也叫高斯平滑,是在Adobe Photoshop.GIMP以及Paint.NET等图像处理软件中广泛使用的处理效果,通常用它来减少图像噪声以及降低细节层次.这种模糊技术生成的图像,其视觉效果就像是经过一个半透明屏幕在观察图像,这与镜头焦外成像效果散景以及普通照明阴影中的效果都明显不同.高斯平滑也用于计算机视觉算法中的预先处理阶段,以增强图像在不同比例大小下的图像效果(参见尺度空间表示以及尺度空间实现). 从数学的角度来看,图像的高斯模糊过程就是图像与

一种简单高效的音频降噪算法示例(附完整C代码)

近期比较忙, 抽空出来5.1开源献礼. 但凡学习音频降噪算法的朋友,肯定看过一个算法. <<语音增强-理论与实践>> 中提及到基于对数的最小均方误差的降噪算法,也就是LogMMSE. 资料见: <<Speech enhancement using a minimum  mean-square error log-spectral amplitude estimator.>> -----Ephraim, Y. and Malah, D. (1985) 之前也是

音频算法之小黄人变声 附完整C代码

前面提及到<大话音频变声原理 附简单示例代码>与<声音变调算法PitchShift(模拟汤姆猫) 附完整C++算法实现代码> 都稍微讲过变声的原理和具体实现. 大家都知道,算法从实现到最后工程应用,中间的环节和问题特别多. 尤其是编码的架构设计,好的数据结构和代码逻辑封装肯定是可复用,组件化的. 前几天写完<音频识别算法思考与阶段性小结>的时候, 我也提及到了. 会做一些算法编码优化相关的分享. 而有时候我总觉得文字表达很苍白, 所以我尽可能地把代码写得简洁易懂, 一方