基于WebRTC实现iOS端音频降噪功能

AnnaFrancis 发布于9月前 阅读641次
0 条评论

WebRTC下载要很麻烦,并且学会使用一个库也要花费不少时间,另一方面导入一个第三方库app的体积会加大,因此用了一位大神从WebRTC提出来的模块,但因为是c的所以还要转一次。

WebRTC降噪有两部分代码,一套是定点算法(noise_suppression_x.h),一套是浮点算法(noise_suppression.h)。相对来说浮点算法精度更高,但是耗系统资源更多,特别是浮点计算能力较弱的低端ARM CPU上。

音频处理的时候webrtc一次仅能处理10ms数据,小于10ms的数据不要传入,如果是传入小于10ms的数据最后传入也是按照10ms的数据传出,此时会出现问题。另外支持采样率也只有8K,16K,32K三种,不论是降噪模块,或者是回声消除增益等等均是如此。对于8000采样率,16bit的音频数据,10ms的时间采样点就是80个,一个采样点16bit也就是两个字节,那么需要传入WebRtcNsx_Process的数据就是160字节。对于8000和16000采样率的音频数据在使用时可以不管高频部分,只需要传入低频数据即可,但是对于32K采样率的数据就必须通过滤波接口将数据分为高频和低频传入,传入降噪后再组合成音频数据。大于32K的音频文件就必须要通过重采样接口降频到对应的采样率再处理。

部分代码

- (void)viewDidLoad {

    [super viewDidLoad];

    NSString *inpath  = @"/Users/apple/Desktop/a.wav";
    NSString *outpath = @"/Users/apple/Desktop/b.wav";

    const char *in_file  = [inpath  UTF8String];
    const char *out_file = [outpath UTF8String];

    char in_f[1024];
    //把从src地址开始且含有'\0'结束符的字符串复制到以dest开始的地址空间,返回值的类型为char*
    strcpy(in_f,in_file);

    char out_f[1024];
    strcpy(out_f,out_file);

    [self noise_suppression:in_f and:out_f];
}


- (void)noise_suppression:(char *)in_file and:(char *)out_file {
    //音频采样率
    uint32_t sampleRate = 0;
    //总音频采样数
    uint64_t inSampleCount = 0;

    int16_t *inBuffer = [self wavRead_int16:in_file :&sampleRate :&inSampleCount];

    //如果加载成功
    if (inBuffer != nullptr) {
        double startTime = now();
        [self nsProcess:inBuffer :sampleRate :(int)inSampleCount :kModerate];
        double time_interval = calcElapsed(startTime, now());
        printf("time interval: %d ms\n ", (int) (time_interval * 1000));
        [self wavWrite_int16:out_file :inBuffer :sampleRate :inSampleCount];
        free(inBuffer);
    }
}

//写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("ERROR.");
    }
    return buffer;
}

-(int)nsProcess:(int16_t *)buffer :(uint32_t)sampleRate :(int)samplesCount :(enum nsLevel)level {
    if (buffer == nullptr) return -1;
    if (samplesCount == 0) return -1;
    size_t samples = MIN(160, sampleRate / 100);
    if (samples == 0) return -1;
    uint32_t 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++) {
        int16_t *nsIn[1] = {input};   //ns input[band][data]
        int16_t *nsOut[1] = {input};  //ns output[band][data]
        WebRtcNs_Analyze(nsHandle, nsIn[0]);
        WebRtcNs_Process(nsHandle, (const int16_t *const *) nsIn, num_bands, nsOut);
        input += samples;
    }
    WebRtcNs_Free(nsHandle);

    return 1;
}

oc代码

参考:单独编译和使用webrtc音频降噪模块

参考:音频降噪算法

作者:李境沛
链接:https://www.jianshu.com/p/c8d79056c6fc

查看原文: 基于WebRTC实现iOS端音频降噪功能

  • purplefish
  • goldenelephant
  • blackpanda
  • Ray
需要 登录 后回复方可回复, 如果你还没有账号你可以 注册 一个帐号。