以前在学习WAV文件格式的时候发现了一个叫SoundTouch的开源小众库,提供WAV音频的变调和变速功能。这几天忽然想起来这个库,无聊的我就写了一个简单的歌声合成程序。功能和质量都比不上专业的音频合成软件,而且我也不想再做下去了......
这个简单的歌声合成程序,我称它为FA♂乐器好了,因为音源是FA(滑稽
http://files.cnblogs.com/files/CodeMIRACLE/fa.zip
我的开发环境是win10 CodeBlock+Mingw32,我没有预先编译这个库,直接把源代码添加到工程里。sourceSouthStretch文件夹里面有个WavFile.cpp提供了一系列对WAV文件操作的函数,很方便,我直接加进工程里使用。
程序的设计参考了官方SouthStretch的实现。编译的时候这个库的SSE和MMX优化会报编译错误,我就把那些优化的代码都注释掉了。
SoundTouch的使用十分简单
1 #include <stdexcept>
2 #include <stdio.h>
3 #include <string.h>
4 #include <time.h>
5 #include<cstdlib>
6 #include "WavFile.h"
7 #include "SoundTouch.h"
8 #include "BPMDetect.h"
9 #include<iostream>
10 #include<algorithm>
11 #include "STTypes.h"
12 #include <vector>
13 #include <sstream>
14 #include <map>
15 #include <windows.h>
16 using namespace soundtouch;
17 using namespace std;
18 #define BUFF_SIZE 6720
19 map<string,int> tonemap;
20 template<class T>
21 string tostring(T& x)
22 {
23 stringstream ss;
24 ss<<x;
25 return ss.str();
26 }
27 void InitToneMap()
28 {
29 string keys[]={"C","C#","D","D#","E","F","F#","G","G#","A","A#","B"};
30 for(int i=0;i<=10;i++)
31 {
32 for(int j=0;j<12;j++)
33 tonemap[keys[j]+tostring(i)]=i*12+j-82;
34 }
35 }
36 void WriteVoidSample(WavOutFile* wavout,SoundTouch* pSoundTouch,int num,int channels,double tempo)
37 {
38 short *sampleBuffer=new short[num];
39 memset(sampleBuffer,0,sizeof(short)*num);
40 pSoundTouch->setTempo(tempo);
41 pSoundTouch->putSamples(sampleBuffer,num/channels);
42 pSoundTouch->receiveSamples(sampleBuffer,num);
43 wavout->write(sampleBuffer,num);
44 delete []sampleBuffer;
45 }
46 void WriteProcessSample(WavInFile* wavin,WavOutFile* wavout,SoundTouch* pSoundTouch,int tone,double tempo)
47 {
48 int nSamples;
49 int nChannels;
50 int buffSizeSamples;
51 short *sampleBuffer=new short[BUFF_SIZE];
52 pSoundTouch->setPitchSemiTones(tone);
53 pSoundTouch->setTempo(tempo);
54 while(!wavin->eof())
55 {
56 int num;
57 num = wavin->read(sampleBuffer, BUFF_SIZE);
58 nSamples = num / (int)wavin->getNumChannels();
59 nChannels = (int)wavin->getNumChannels();
60 buffSizeSamples = BUFF_SIZE / nChannels;
61 pSoundTouch->putSamples(sampleBuffer, nSamples);
62 do
63 {
64 nSamples = pSoundTouch->receiveSamples(sampleBuffer,buffSizeSamples);
65 wavout->write(sampleBuffer, nSamples * nChannels);
66 } while (nSamples != 0);
67 }
68 wavin->rewind();
69 pSoundTouch->clear();
70 delete []sampleBuffer;
71 }
72 int main()
73 {
74 FILE* fp=fopen("tone.txt","r");
75 if(fp)
76 {
77 WavInFile *wavin;
78 WavOutFile *wavout;
79 SoundTouch *pSoundTouch;
80 wavin = new WavInFile("fa.wav");
81 pSoundTouch = new SoundTouch;
82 pSoundTouch->setSampleRate(wavin->getSampleRate());
83 pSoundTouch->setChannels(wavin->getNumChannels());
84 wavout = new WavOutFile("out.wav",wavin->getSampleRate(),wavin->getNumBits(),wavin->getNumChannels());
85 InitToneMap();
86 char tone[4];
87 double meter;
88 while(~fscanf(fp,"%s%lf",tone,&meter))
89 {
90 if(tone[0]!='0')
91 WriteProcessSample(wavin,wavout,pSoundTouch,tonemap[tone],1/meter);
92 else
93 WriteVoidSample(wavout,pSoundTouch,wavin->getDataSizeInBytes(),wavin->getNumChannels(),1/meter);
94 }
95 delete wavin;
96 delete wavout;
97 delete pSoundTouch;
98 PlaySound("out.wav",NULL,SND_SYNC);
99 }
100 }