1. 程式人生 > 實用技巧 >多媒體開發(5)&音訊特徵:聲音可以調大一點嗎?

多媒體開發(5)&音訊特徵:聲音可以調大一點嗎?

基本上,現在常用的聲音取樣辦法是pcm,而對於壓縮音訊的解碼,得到的也pcm資料。這個pcm資料,只是一堆數值,有正有負,看這個值看不出什麼花樣。

聲音採集,採的是什麼呢?

採的是聲音的強度變化,也是聲音這種能量的強弱變化,這種強弱用分貝來表示,即dB。所以,pcm資料跟這個dB就一定有關係,這個關係是這樣的:
dB=20∗log10(pcm)
pcm=pow(10,(dB/20.0))

模數轉換ADC時常用的位深是16bit,也就是用16位來表示一個sample,這裡不考慮偷懶而使用不足16位的情況。16位能表示65536個值,也就意味著有65536個dB可以表示出來,哪又怎麼樣?很厲害了嗎?

的確是比較厲害的了。

16位的pcm數值,分正負,那正數的範圍是0至32767,負數的範圍是-1至-32768,隨便挑幾個來看看,對應的dB是多少,如下圖:

由上面的運算可知,16位的pcm值,如果不分正負,最大可以表示96dB,如果分正負,也能表示到90dB。90dB是什麼概念?有資料表明(我也不清楚什麼資料),85dB就會傷害了你,90dB相當於摩托車啟動的聲音--你有開過嗎?

所以,你的pcm資料需要去到90dB以上嗎?想禍害誰?一般情況下,能表示到90dB就很夠用了。

既然知道了pcm數值與dB的關係,就可以搞點事情了,比如把pcm轉成dB後再放大一點,再儲存成新的檔案,是不是播放就可以大聲一點了呢?

來做個實驗。

import math
import math
import wave
import audioread
import contextlib
import sys
import math
import struct

def gainpcm(filepath):
    try:
        with audioread.audio_open(filepath) as f:
            with contextlib.closing(wave.open(filepath+'.wav', 'w')) as of:
                of.setnchannels(f.channels)
                of.setframerate(f.samplerate)
                of.setsampwidth(2)
                for buf in f:
                    for i in range(0, len(buf)-2, 2):
                        s = buf[i] + buf[i+1]
                        pcm = struct.unpack('<h', s)[0]
                        apcm = abs(pcm)
                        if apcm==0:
                            apcm=1
                        db = 20*math.log10(apcm)
                        db = db * 1.2
                        apcm=int(math.pow(10, float(db)/20.0))
                        if apcm>32767:
                            apcm=32767
                        if pcm < 0:
                            pcm=-apcm
                        else:
                            pcm = apcm
                        tbuf = struct.pack('<h', pcm)
                        of.writeframes(tbuf)

    except audioread.DecodeError:
        print("File could not be decoded.")
        sys.exit(1)

if __name__ == '__main__':
    gainpcm('test.mp3')

這裡是對程式碼的簡單解釋:

把一個mp3放過去試驗,出來一個wav,發現wav檔案的聲音真的大了好多(好多是因為這裡設定了db*1.2)。但是,另一個問題也暴露出來了,就是聽起來聲音失真很嚴重了,這是因為放大到這個程度,很多apcm都超過了32767,也就是人們說的截頂失真了,看一下原檔案與放大後的檔案波形圖就更清楚了:

由於不能上傳音訊,這裡就不提供直接的音訊對比了。

由此可見,要想不失真,那db就不要乘那麼大的數啊--另一個辦法:給它限幅(壓幅),或者直接使用更合適的整體技術(靈活變音量跟限幅都考慮進去了)比如agc或drc等,明顯這個不是這裡的內容。

好了,總結一下,本文演示了改變音量的一種最原始的辦法,就是直接改pcm的值(轉dB再改變,其實也可以直接改變pcm值),但這不是最好的辦法,因為它會引入失真的副作用。而從pcm資料中提取出能量(dB),這個也更像是音訊特徵的技能。有緣再見,see you。