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

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

当然也有不少开源算法可以参阅学习,有基于时域,也有基于频域的算法。

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

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

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

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

也不打算讲特别高深的,当然也是因为讲不来。

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

当然也可以认为是卷积,高斯模糊是卷积的一种特例,这里就不展开了。

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

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

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

在当今的主流采集卡上,采样频率一般共分为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); } }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wppzsd.html