前面提及到《大話音訊變聲原理 附簡單示例程式碼》與《聲音變調演算法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 "); printf("blog:http://cpuimage.cnblogs.com/ "); printf("Pitch Shifting Using The Fourier Transform "); 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 ", (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. "); getchar(); return 0; }
不做多解釋,大家可以參閱pitchshift函式的實現,
主要實現位於檔案PitchShift.h。
整個演算法不到200行,邏輯非常清晰,
已經做了一定程度上的工程化優化。
當然還有很大的改進空間,
不過這份程式碼,更多的意義在於學習。
授人以魚不如授人以漁。
若有其他相關問題或者需求也可以郵件聯絡俺探討。
郵箱地址是:
gaozhihan@vip.qq.com