生成特定分貝的音頻波形

在處理音頻的時候的有時候須要特定分貝(如-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

  • 數字頻率,相鄰兩個採樣點之間變化的弧度數。 該值能夠由模擬頻率和採樣率獲得 \(2f\pi / fs\)
  • 幅度值,幅度值可經過分貝dB換算獲得。 \(dB = 20 * \log(A) \to A = 10 ^ {db/20}\),這裏幅值A歸一化到[-1,1]。關於分貝和幅值之間的關係能夠參考聲音分貝的概念,dBSPL.dBm,dBu,dBV,dBFS

現假設要生成-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文件了。對於wav文件讀寫,在前面有個介紹C++標準庫實現WAV文件讀寫,可是後來在使用SoundTouch這個變調變速的庫的時候,發現其帶的WavOutFileWavInFile用着挺方便的,這是就是用其來保存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各類波形的結果

總結

本文主要介紹瞭如何生成指定分貝的標準信號,正弦波、方形波、三角波、鋸齒波等。對於波形的生成,首先要弄清楚模擬頻率和數字頻率之間的關係。

  • 模擬頻率f,單位時間內信號變化的週期數
  • 模擬角頻率,單位時間內信號變化的弧度,單位 rad/s
  • 採樣率fs,單位時間內採樣獲得的樣本數
  • 數字頻率 \(2f/fs \cdot \pi\),相鄰兩個樣本間變化的弧度數。

本文使用的源代碼 http://download.csdn.net/detail/brookicv/9715390

相關文章
相關標籤/搜索