前面提及到《大话音频变声原理 附简单示例代码》与《声音变调算法PitchShift(模拟汤姆猫) 附完整C++算法实现代码》
都稍微讲过变声的原理和具体实现。
大家都知道,算法从实现到最后工程应用,中间的环节和问题特别多。
尤其是编码的架构设计,好的数据结构和代码逻辑封装肯定是可复用,组件化的。
前几天写完《音频识别算法思考与阶段性小结》的时候,
我也提及到了。
会做一些算法编码优化相关的分享。
而有时候我总觉得文字表达很苍白,
所以我尽可能地把代码写得简洁易懂,
一方面是便于基础差的朋友学习。
另一方面也是为了自己在编码以及思考的时候,能更加清晰。
当然,变声算法绝大多数朋友都会选择一些开源的或者商业sdk去做二次开发。
例如:
https://www.surina.net/soundtouch/
但如果仅仅停留在使用的阶段,它就是一个黑盒子。
知其然,却不知其所以然。
是远远不够的。
有时候我们是要站在巨人的肩膀上去看到更美丽的风景。
但是,我希望是一群人,而不是一个人。
也许大家也发现了,我写的大多数算法,是纯c无第三方依赖的。
是不是就会怀疑,我就只会写c语言?
不是的,我所掌握的编程语言:
主要: c,c++,python,汇编
其次:pascal,c#,js,lua,go等
编程语言只是一个工具,关键还是算法思路。
用纯c写的主要目的,是为了破除一些第三方依赖,
不要一知半解地使用黑盒子。
当然,其次的好处就是跨平台,便携,可复用。
这样,一切了然于心。
为什么不可以造*呢?
只要你造的*是有用的,
不管是用于观赏用于学习还是其他用途。
在我了解到一些音频算法的思路之后,
变声算法的思路,
我觉得它的思路非常适用于扩展到大多数音频算法实现,
而且可复用度比较高。
所以,将它梳理开源,就显得特别有意义。
而大家可以基于这个实现,进一步去改进或者学习 音频算法,
例如降噪,增益等等。
因为这个编码实现的设计是完全可以适用到音频算法应用场景的。
逻辑也非常清晰。
项目地址:
https://github.com/cpuimage/pitchshift
当然为了便于一些朋友的学习使用,
示例代码提供一个简易的实现,
模拟变声为小黄人。
int main(int argc, char *argv[]) {
printf("Audio Processing \n");
printf("blog:http://cpuimage.cnblogs.com/ \n");
printf("Pitch Shifting Using The Fourier Transform\n"); if (argc < )
return -; char *in_file = argv[];
uint32_t sampleRate = ;
uint64_t totalSampleCount = ;
uint32_t channels = ;
short *data_in = wavRead_s16(in_file, &sampleRate, &totalSampleCount, &channels);
if (data_in != NULL) {
float pitchShift = 0.9f;
size_t ms = ;
size_t overSampling = ;
size_t frameSize = sampleRate * ms / ;
frameSize += frameSize % ;
planData pitchPlanData = {};
double startTime = now();
makePlanData(frameSize, overSampling, sampleRate, &pitchPlanData);
pitchshift(pitchShift, data_in, data_in, totalSampleCount, &pitchPlanData);
// turn to minion pitch
{
totalSampleCount /= ;
short *samples = data_in;
for (int i = ; i < totalSampleCount; i++) {
data_in[i] = samples[];
samples += ;
}
}
double time_interval = calcElapsed(startTime, now());
freePlanData(&pitchPlanData);
printf("time interval: %f ms\n ", (time_interval * ));
}
char drive[];
char dir[];
char fname[];
char ext[];
char out_file[];
splitpath(in_file, drive, dir, fname, ext);
sprintf(out_file, "%s%s%s_out%s", drive, dir, fname, ext);
wavWrite_s16(out_file, data_in, sampleRate, totalSampleCount);
if (data_in) {
free(data_in);
}
printf("press any key to exit.\n");
getchar();
return ;
}
不做多解释,大家可以参阅pitchshift函数的实现,
主要实现位于文件PitchShift.h。
整个算法不到200行,逻辑非常清晰,
已经做了一定程度上的工程化优化。
当然还有很大的改进空间,
不过这份代码,更多的意义在于学习。
授人以鱼不如授人以渔。
若有其他相关问题或者需求也可以邮件联系俺探讨。
邮箱地址是:
gaozhihan@vip.qq.com