在處理音頻的時候的有時候須要特定分貝(如-10dB)的音頻波形,本文主要介紹如何生成特定分貝數的音頻文件。有如下幾個方面:html
波形能夠經過一個週期內幅度值的變化來描述,因此要生成指定的波形就要知道兩個量:週期(頻率)和幅度的變化值。數字信號一般由模擬信號採樣獲得,而一般所說的頻率也是模擬頻率,因此首先要搞清楚模擬頻率、數字頻率,採樣率這些量之間的關係。函數
一般所說的頻率爲模擬頻率,其單位爲赫茲Hz,表示每秒信號變化的週期數。以單位圓爲例,旋轉一圈表示信號變化一個週期(產生一個正弦或者餘弦波形),則模擬頻率指的是每秒鐘圓旋轉的圈數。1000Hz,就是1秒鐘圓旋轉了1000圈(1秒鐘有1000個正弦或者餘弦曲線)。以下圖:spa
單位圓旋轉一週,在水平方向產生一個正弦波;豎直方向產生一個餘弦波。.net
模擬角頻率,仍然以單位圓爲例,頻率是單位時間內單位圓旋轉的圈數,每旋轉一圈單位圓旋轉的角度是 \(2 * \pi\)。 頻率爲f的波,表示一秒鐘旋轉f圈,角度變化就是 \(2 * f * \pi\),故模擬角頻率就是 \(2 * f * \pi rad /s\)。3d
數字信號一般有模擬信號採樣而來,採樣頻率 指的是單位時間內提取到的樣本的個數,由奈奎斯特採樣定理知道,要徹底的保留模擬信號的信息,就須要採樣頻率大於等於模擬信號中最高頻率的2倍。code
數字頻率,更準確叫法應該是歸一化角頻率,其單位爲弧度(rad),表達式爲:\(2 * \pi * f / fs\),其中f爲頻率,fs爲採樣率。物理意義爲相鄰兩個採樣點之間變化的弧度數。orm
如今假設有個模擬的正弦信號x[t],其模擬頻率爲f=1000Hz,幅度爲A,初始相位爲0,則該信號的表達式爲:\(x[t] = A * sin(2\pi * f * t) = Asin(2000*\pi*t)\)
以採樣率fs = 5000對其進行採樣,獲得數字信號x[n],則採樣獲得的數字信號的表達式i爲:\(x[n] = A * sin(2\pi * f / fs * t) = Asin(0.4pi*n)\)。能夠看出數字頻率爲0.4pi,也就是每隔0.4pi弧度取得一個sample。初始相位爲0,則該數字信號的幅度序列爲:\(Asin(0),Asin(0.4\pi),Asin(0.4 * 2 * \pi),Asin(0.4 * 3 * \pi),...,Asin(0.4*n*\pi)\)。這一系列離散的點組成的數字信號其對應的模擬的信號就是\(x[t] = Asin(2000*\pi*t)\)。也就說,要想生成特定頻率,特定幅度(幅度和分貝有轉換關係)的波形,只須要知道其數字頻率就能夠了。htm
總結:
模擬頻率f表示單位時間內信號變化的週期數,單位是赫茲Hz;模擬角頻率$\Omega = 2f \pi \(,表示單位時間內信號變化的角度,單位是rad/s; 採樣率fs表示單位時間內採樣獲得的樣本數;數字頻率,歸一化角頻率\)\omega=2f\pi/fs$,表示採樣時相鄰兩個樣本間變化的弧度數。
由以上可知,即便兩個數字頻率徹底相同的數字信號,其對應的模擬信號缺不必定相同,還須要考慮到採樣率。並且採樣率是模擬信號和數字信號之間進行轉換的橋樑。blog
從上面能夠知道,要生成指定頻率和分貝的波形,須要兩個量:ci
現假設要生成-10dB,頻率爲1000Hz的正弦波形,其採樣率爲48000,有下面代碼:
double f = 1000; double fs = 48000 double db = -10.0f; double duration = 10; double incr = 2 * pi * f / fs ;// 數字頻率,也是相鄰兩個採樣點的變化的弧度 double A = powf(10,db / 20); // 波形的最大幅度值 float* frame = new float[static_cast<int>(duration * fs)]; for(int i=0; i < static_cast<int>(duration * fs); i ++) frame[i] = A * sin(i * incr);
有了上面模擬頻率和數字頻率之間的轉換關係後,上面代碼仍是比較簡單明瞭的。首先經過模擬頻率和採樣率計算出數字頻率,也就是相鄰兩個採樣點之間的變化的弧度;而後,根據分貝數和幅度之間關係計算出波形的最大幅度值(這裏說明下,音頻的分貝計算一般取一段時間內(例如50ms)樣本值的最大值(Peak值)。關於音頻音量的度量,有機會會單獨介紹)。最後,for循環計算各個sample的值,生成波形。以下圖獲得一個週期內的樣本值:
使用上面不到10行的代碼就能夠生成一個指定頻率,指定分貝的正弦波形了。可是,上述代碼實在太簡單,下面就使用C++的類,將上面不到10行的代碼編變成200行。
標準的波形除了正弦波外,還有方形波、三角波、鋸齒波等。以下圖:
首先,聲明一個classOscillator
,其功能就是根據頻率和採樣率以及選擇的波形形狀,連續的產生波形的sample值。有如下的字段:
double sampleRate; // 採樣率 double twoPIdivSamplerate; // 2 * pi / sampleRate double curFreq; // 當前頻率 double curPhase; // 當前相位 double incrSample; // 每一個sample增加的值 // 正弦波 double sinetick(double freq) { auto val = sin(curPhase); updateFreq(freq); updatePhase(); return val; }
sinetick
生成正弦波的sample。該函數須要波形的頻率做爲參數,在生成返回當前的sample後。根據傳入的頻率不一樣,更新相鄰sample的變化值,爲生成下一個sample作準備。
void updateFreq(double freq) { if (curFreq != freq) { curFreq = freq; incrSample = twoPIdivSamplerate * freq; } } void updatePhase() { curPhase += incrSample; if (curPhase >= 2 * pi) curPhase -= 2 * pi; else if (curPhase < 0.0) curPhase += 2 * pi; }
除了sinetick
外,還有squaretick
生成方形波的sample;triangletick
,生成三角波的sample;sawtoothDownTick
,生成向下的鋸齒波的sample;sawtoothUpTick
生成向上的鋸齒波的sample。
使用class Oscillator
能夠生成諸如\(\sin(2f/fs \cdot \pi)\)的波形,可是還缺乏一個對波形幅值的縮放係數,來生成特定分貝的波形。下面再定一個class AudioGenerator
,該類的主要功能是可以 生成不一樣形狀的指定分貝的波形,對於sample的類型也有三種選擇:16位的有符號整型、32位有符號整型以及單精度浮點數。
float GenerateFloat_32(double decibel, double freq, WavformType wavType = WavformType::SIN) { auto amplitude = powf(10.0, decibel / 20); // 幅度 double val; val = value(freq, wavType); amplitude *= val; if (amplitude > 1.0f) amplitude = 1.0f; else if (amplitude < -1.0f) amplitude = -1.0f; return static_cast<float>(amplitude); }
上面方法是生成單精度浮點數的sample。首先根據分貝數,計算獲得波形的最大幅度值;value
函數根據選擇波形形狀的不一樣,調用Oscialltor
中的不一樣波形的生成方法,對獲得的sample使用前面最大幅值進行縮放。
代碼基本已經完成了,接下來就是將生成的波形保存爲wav文件了。對於wav文件讀寫,在前面有個介紹C++標準庫實現WAV文件讀寫,可是後來在使用SoundTouch
這個變調變速的庫的時候,發現其帶的WavOutFile
和WavInFile
用着挺方便的,這是就是用其來保存wav文件 。
int db = -10; float amplitude = powf(10.0, static_cast<float>(db) / 20); int f = 1000; // 信號的模擬頻率爲1000Hz int fs = 48000; // 採樣頻率爲48000Hz int duration = 10; //生成10s的信號 AudioGenerator gen(fs); WavOutFile *outFile; float *outFrame = new float[duration * fs]; outFile = new WavOutFile("-10db_sin.wav", fs, 32, 1); for (int i = 0; i < fs * duration; i++) outFrame[i] = gen.GenerateFloat_32(-10, f); outFile->write(outFrame, fs * duration); delete outFile;
代碼很簡單,就不作過多的解釋了。生成-10dB各類波形的結果
本文主要介紹瞭如何生成指定分貝的標準信號,正弦波、方形波、三角波、鋸齒波等。對於波形的生成,首先要弄清楚模擬頻率和數字頻率之間的關係。