前面提及到《大话音频变声原理 附简单示例代码》与《声音变调算法 PitchShift(模拟汤姆猫) 附完整 C++ 算法实现代码》
都稍微讲过变声的原理和具体实现.
大家都知道, 算法从实现到最后工程应用, 中间的环节和问题特别多.
尤其是编码的架构设计, 好的数据结构和代码逻辑封装肯定是可复用, 组件化的.
前几天写完《音频识别算法思考与阶段性小结》的时候,
我也提及到了.
会做一些算法编码优化相关的分享.
而有时候我总觉得文字表达很苍白,
所以我尽可能地把代码写得简洁易懂,
一方面是便于基础差的朋友学习.
另一方面也是为了自己在编码以及思考的时候, 能更加清晰.
当然, 变声算法绝大多数朋友都会选择一些开源的或者商业 sdk 去做二次开发.
例如:
- https://www.fmod.com/
- 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 < 2)
- return -1;
- char *in_file = argv[1];
- uint32_t sampleRate = 0;
- uint64_t totalSampleCount = 0;
- uint32_t channels = 0;
- short *data_in = wavRead_s16(in_file, &sampleRate, &totalSampleCount, &channels);
- if (data_in != NULL) {
- float pitchShift = 0.9f;
- size_t ms = 50;
- size_t overSampling = 4;
- size_t frameSize = sampleRate * ms / 1000;
- frameSize += frameSize % 2;
- planData pitchPlanData = {0};
- double startTime = now();
- makePlanData(frameSize, overSampling, sampleRate, &pitchPlanData);
- pitchshift(pitchShift, data_in, data_in, totalSampleCount, &pitchPlanData);
- // turn to minion pitch
- {
- totalSampleCount /= 2;
- short *samples = data_in;
- for (int i = 0; i < totalSampleCount; i++) {
- data_in[i] = samples[0];
- samples += 2;
- }
- }
- double time_interval = calcElapsed(startTime, now());
- freePlanData(&pitchPlanData);
- printf("time interval: %f ms\n", (time_interval * 1000));
- }
- 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);
- wavWrite_s16(out_file, data_in, sampleRate, totalSampleCount);
- if (data_in) {
- free(data_in);
- }
- printf("press any key to exit.\n");
- getchar();
- return 0;
- }
不做多解释, 大家可以参阅 pitchshift 函数的实现,
主要实现位于文件 PitchShift.h.
整个算法不到 200 行, 逻辑非常清晰,
已经做了一定程度上的工程化优化.
当然还有很大的改进空间,
不过这份代码, 更多的意义在于学习.
授人以鱼不如授人以渔.
来源: https://www.cnblogs.com/cpuimage/p/9690112.html