(轉)音頻降噪算法 附完整C代碼

轉:https://www.cnblogs.com/cpuimage/p/8905965.html

降噪是音頻圖像算法中的必不可少的。

目的肯定是讓圖片或語音 更加自然平滑,簡而言之,美化。

圖像算法和音頻算法 都有其共通點。

圖像是偏向 空間 處理,例如圖片中的某個區域。

圖像很多時候是以二維數據爲主,矩形數據分佈。

音頻更偏向 時間 處理,例如語音中的某段時長。

音頻一般是一維數據爲主,單聲道波長。

處理方式也是差不多,要不單通道處理,然後合併,或者直接多通道處理。

只是處理時候數據參考系維度不一而已。

一般而言,圖像偏向於多通道處理,音頻偏向於單通道處理。

而從數字信號的角度來看,也可以理解爲聚類,頻率歸一化之類的。

總之就是對一些有一定規律的數字數據進行計算處理。

圖像降噪被磨皮美顏這個大主題給帶遠了。

音頻降噪目前感覺大有所爲,像前面分享的《基於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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章