大话音频变声原理 附简单示例代码

关于音频变声算法,这个是一个不少人特别感兴趣的话题。html

固然也有很多开源算法能够参阅学习,有基于时域,也有基于频域的算法。git

最终算法想要达到的目的是一致。github

最近也有很多网友问过关于变声算法的一些细节问题,邮件询问我。算法

要给出一个比较合理或者说通俗易懂的解释,看似简单,其实还蛮难的。函数

按照大概的一个逻辑思路,稍微理一理,因此这个主题必须加上“大话”这个前缀。学习

也不打算讲特别高深的,固然也是由于讲不来。ui

之于图像算法领域,很是重要的算法是高斯模糊,spa

固然也能够认为是卷积,高斯模糊是卷积的一种特例,这里就不展开了。code

而之于音频,也许你也猜到了,基于时间的,毫无疑问,就是重采样算法。htm

音频采样率是指录音设备在一秒钟内对声音信号的采样次数,

采样频率越高声音的还原就越真实越天然。

在当今的主流采集卡上,采样频率通常共分为22.05KHz、44.1KHz、48KHz三个等级,

22.05KHz只能达到FM广播的声音品质,

44.1KHz则是理论上的CD音质界限,48KHz则更加精确一些。

看到这里,也许大多数人仍是无法理解采样频率大概是什么意思。

换个角度来讲的话,就是假设一我的说“你好”,花了20毫秒,而机器在这20毫秒内,

采集的数据多少就能够理解为采样率高低。

也就是说,20毫秒内,采集到的数据量就是能够大概认为目前的采样率,数据量越大,精度越高,采样率越高。

那么,咱们再换一个思路,想一个问题。

若是在一样的速率的状况下,

一我的的语速快,一我的的语速慢,那也可能形成采样数据分布不一致。

这里就能够展开一个音频算法,就是变速。

嗯,是的,就是变速。

从原理上来说的话,其实变速就是在一样的采样率环境下,对采样数据进行拉伸或压缩。

从算法的角度上来讲的话,能够认为是插值或抽值。

若是你让一我的讲话的速度变得更快怎么作,

很明显,就是在一样的采样率下,抽掉一些样本。

反之,降速则是插入一些样本。

最终决定变速效果的就是插入样本和抽离样本的权重计算。

例如原来采样到的数据是

1234

加速的时候,抽离样本 1 和 4

23

降速的时候,增长样本 

11223344

固然只是举个例子,便于你们理解这个概念逻辑。

看到这里,确定有人会问,

那声音的大小呢?或者说信号的强弱呢?

其实也就是提高音量和下降音量,我想这个应该不用解释。

变速是时域变,空间不变。

而音量则反之,时域不变,空间变。

能够简单粗暴地理解,就是线性拉伸。

例如原来采样到的数据是

1234

每一个样本+4,直接拉伸为

5678

也有采用乘法进行拉伸的,

例如 乘以2

2468

上面是增大音量,下降音量反之就是减和除。

而最终无论变速仍是音量调节,

最终算法要作的事情就是肯定对应位置的对应权重。

固然也要看最终想要达到什么样的效果,适配权重。

饶了这么一大圈,仍是没有说到变声的问题。

其实,变声就是变速+音量调节。

以上变速也好,音量调节也好,相对而言都是线性拉伸,

直接的加减乘除而后插值抽值就能达到的。

而变声的概念其实也是相似的,

就是在在同一时域内同时调节对应时域的音量权重。

换言之就是在同一个采样率内,同时控制语速和音量在一个特定的权重内。

其实就是一个时域和空间的二维拉伸。

理解这个逻辑确实有点绕。

用采样算法来作一个简单的示例。

参阅前面的文章《简洁明了的插值音频重采样算法例子 (附完整C代码)》

这个示例中的采样函数是:

void resampler(char *in_file, char *out_file) {
    //音频采样率
    uint32_t in_sampleRate = 0;
    //总音频采样数
    uint64_t totalSampleCount = 0;
    int16_t *data_in = wavRead_int16(in_file, &in_sampleRate, &totalSampleCount);
    uint32_t out_sampleRate = in_sampleRate * 2;
    uint32_t out_size = (uint32_t) (totalSampleCount * ((float) out_sampleRate / in_sampleRate));
    int16_t *data_out = (int16_t *) malloc(out_size * sizeof(int16_t));
    //若是加载成功
    if (data_in != NULL && data_out != NULL) {
        resampleData(data_in, in_sampleRate, (uint32_t) totalSampleCount, data_out, out_sampleRate);
        wavWrite_int16(out_file, data_out, out_sampleRate, (uint32_t) out_size);
        free(data_in);
        free(data_out);
    } else {
        if (data_in) free(data_in);
        if (data_out) free(data_out);
    }
}

让咱们稍微变通一下,设一个采样速率,用来调节声音的速度,同时保证采样率不变。

void resampler(char *in_file, char *out_file) {
    //音频采样率
    uint32_t in_sampleRate = 0;
    //总音频采样数
    uint64_t totalSampleCount = 0;
    int16_t *data_in = wavRead_int16(in_file, &in_sampleRate, &totalSampleCount);
    float speed = 0.88;//增长一个速度权重
    uint32_t out_sampleRate = in_sampleRate * speed;
    uint32_t out_size = (uint32_t) (totalSampleCount * ((float) out_sampleRate / in_sampleRate));
    int16_t *data_out = (int16_t *) malloc(out_size * sizeof(int16_t));
    //若是加载成功
    if (data_in != NULL && data_out != NULL) {
        resampleData(data_in, in_sampleRate, (uint32_t) totalSampleCount, data_out, out_sampleRate);
        //out_sampleRate改成输出同样的采样率in_sampleRate
        wavWrite_int16(out_file, data_out, in_sampleRate, (uint32_t) out_size);
        free(data_in);
        free(data_out);
    } else {
        if (data_in) free(data_in);
        if (data_out) free(data_out);
    }
}

修改后是这个样子的。

有心的朋友发现了。out_size数值有可能增大或缩小了。

以上示例代码,就是一个简单的变速算法。

变速就是这么一个原理,音量增大下降就不作示例了。

而变声是一个什么算法呢?

说白了,就是变速的同时保证out_size仍是原来的totalSampleCount。

那要怎么保证呢?

答案就是插值,若是简单粗暴一点,补0或者删0便可。

固然这样作的话,可能会致使音量不一致,最终发声不对的状况。

这确定是不科学的,最终的插值时候的权重和对应的内容,产生的效果就看各家本领了。

以上原理,也说得差很少了,具体怎么实现的话,

你们自行参阅相关的开源代码,再去理解一下。

另外说一下前面《声音变调算法PitchShift(模拟汤姆猫) 附完整C++算法实现代码》

这篇文章中的sin和cos 没有在有效区间内,因此fastsin fastcos计算的结果是有问题的。

详情你们仍是参阅做者原算法吧。

固然,后面有时间我会放出,

简单清晰的变声算法的完整c代码和对应的示例代码。

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

在对应的github 项目fftResample上,我也作了算法逻辑上的修正。

发表过的文章通常不多进行二次编辑了,

关于后期的一些修正和变动,你们仍是关注一下github项目的更新比较直接一点。

具体变声的实现原理,

如上所述,但愿经过这篇文章,

你们对音频变声算法能有比较直观的理解和认识。

以上,权当抛砖引玉。

独乐乐不如一块儿玩乐。

如有其余相关问题或者需求也能够邮件联系俺探讨。

邮箱地址是: gaozhihan@vip.qq.com

相关文章
相关标签/搜索