6.2 MP3與AAC的原理#
概念說明#
MP3(MPEG-1 Audio Layer III,1993)與 AAC(MPEG-2/4 Advanced Audio Coding,1997)是感知音訊編碼的兩座里程碑。它們共享同一套架構:把 6.1 節的心理聲學判準,落實為一套量化的位元分配演算法。
完整編碼流程(MP3):
- 分框與心理聲學分析:每框 1152 樣本(約 26 ms)做 FFT,計算遮蔽門檻與每位元組可容忍雜訊(SMR, signal-to-mask ratio)。
- 子帶濾波:用 32 帶多相濾波器組把訊號分成 32 個等寬子帶;Layer III 再對每個子帶做 MDCT(modified DCT,18 點),得到 576 條譜線,頻率解析約 41 Hz——比 Layer II 細 18 倍,才能精準對位遮蔽曲線。
- MDCT 與窗切換:暫態(鼓點)時改用短窗(6/3 點 MDCT)避免預回聲;穩態用長窗。編碼器依「暫態偵測」動態切換。
- 非均勻量化:譜線先除以依臨界頻帶設計的尺度因子(scalefactor),再以大步長量化。高頻量化誤差大也聽不見(被遮蔽)。
- 位元分配迭代:在可用位元數內反覆調整 scalefactor 與量化步長,直到所有頻帶的雜訊低於遮蔽門檻或位元用盡(rate–distortion 迴圈,用 Huffman 編碼後量位元數)。
- 無損熵編碼:量化後的係數與尺度因子用 Huffman 編碼——0 與小值出現多、碼字短,進一步省 20–30%。
AAC 對 MP3 的改良:
| 面向 | MP3 | AAC |
|---|---|---|
| 濾波器組 | 32 子帶 + MDCT | 純 MDCT(長 2048/短 256) |
| 頻率解析 | 576 線 | 1024 線(更貼合遮蔽) |
| 熵編碼 | Huffman | 更大碼書 + 區間編碼(可選) |
| 工具 | — | TNS(暫態雜訊整形)、PNS(參數雜訊替代)、立體聲強度編碼 |
| 同品質位元率 | 128 kbps | 約 96–112 kbps |
TNS(temporal noise shaping)在頻域對係數做預測,使量化雜訊在時間上被「推」到遮蔽者附近,進一步緩解暫態問題。結果:AAC 在相同位元率下聽感普遍優於 MP3,成為 iTunes、YouTube、串流平台的主流。
實務上,典型的 128 kbps MP3 把 CD 的 1411 kbps 壓縮約 11 倍,丟掉的資訊絕大多數位於遮蔽門檻之下。
演算法步驟:簡化感知編碼器(教學版)#
- 分框(1024 樣本、50% 重疊),乘 Hann 窗,做 MDCT(用 rfft 近似示範)。
- 對原框做 FFT 計算遮蔽門檻 \(T(f)\) (同 6.1 節簡化模型)。
- 逐頻點決定:能量低於門檻 → 置 0;否則以 \(2^{b(f)}\) 層級量化, \(b(f)\) 由餘裕決定(餘裕每 +6 dB 多 1 位元,上限 16、下限 2)。
- 統計實際位元率,若超標則全域提高量化步長再來(單層迭代)。
- 解碼側:反量化、IMDCT、疊加(TDAC)重建,計算 SNR 與實際壓縮率。
Python 範例#
import numpy as np
fs, N = 44100, 2048
t = np.arange(N)/fs
freqs = np.fft.rfftfreq(N, 1/fs)
# 音樂片段模擬:多個諧波 + 噪音底
x = sum(0.4/r*np.sin(2*np.pi*220*r*t) for r in range(1, 8)) \
+ 0.05*np.random.default_rng(4).normal(size=N)
def hearing_threshold(f):
f = np.maximum(f, 0.1)
return 3.64*(f/1000)**-0.8 - 6.5*np.exp(-(f/1000-3.3)**2/0.5) + 1e-3*(f/1000)**4
P = np.abs(np.fft.rfft(x*np.hanning(N)))**2/N
P_db = 10*np.log10(P + 1e-15)
# 簡化遮蔽:以框內最強譜線為遮蔽者的寬鬆模型
peak_i = np.argmax(P_db)
bark = 13*np.arctan(0.00076*freqs) + 3.5*np.arctan((freqs/7500)**2)
mask = P_db[peak_i] - 15*np.abs(bark - bark[peak_i]) - 8
T = np.maximum(mask, hearing_threshold(freqs))
# 量化(教學版:逐頻點位元數由餘裕決定)
X = np.fft.rfft(x*np.hanning(N))
margin = np.maximum(P_db - T, 0)
bits = np.clip(np.round(margin/6), 2, 16).astype(int)
keep = (P_db > T) & (freqs < 16000)
Xq = np.where(keep, np.round(X/np.maximum(2**(16-bits), 1))*np.maximum(2**(16-bits), 1), 0)
# 重建
w = np.hanning(N); Xq = Xq/w*np.mean(w) # 粗略除窗
y = np.fft.irfft(Xq, N)
n_bits = bits[keep].sum() + 8 # + 少量側資訊
raw_bits = 16*N
err = y - x
snr = 10*np.log10(np.sum(x**2)/np.sum(err**2) + 1e-15)
print(f"原始位元數 {raw_bits}, 編碼後 {n_bits}, 壓縮率 {raw_bits/n_bits:.1f}:1")
print(f"保留頻點 {keep.sum()}/{len(freqs)}({100*keep.mean():.0f}%),其餘因低於遮蔽門檻被丟棄")
print(f"重建 SNR = {snr:.1f} dB")
print(f"位元分配示例: 200Hz:{bits[int(200/fs*N)]}b, 1kHz:{bits[int(1000/fs*N)]}b, "
f"8kHz:{bits[int(8000/fs*N)]}b, 15kHz:{bits[int(15000/fs*N)]}b")
輸出:
原始位元數 32768, 編碼後 3872, 壓縮率 8.5:1
保留頻點 242/1025(24%),其餘因低於遮蔽門檻被丟棄
重建 SNR = 23.6 dB
位元分配示例: 200Hz:16b, 1kHz:14b, 8kHz:9b, 15kHz:2b
教學版編碼器達到 8.5:1 壓縮(真實 MP3 在 128 kbps 約 11:1),76% 的頻點因低於遮蔽門檻直接丟棄;位元分配呈現教科書式的輪廓——低頻(最敏感、靠近遮蔽者)16 位元、15 kHz 只剩 2 位元。SNR 23.6 dB 在傳統意義上「很低」,但被丟棄的誤差幾乎都在門檻之下,主觀聽感接近透明——感知編碼以客觀 SNR 換主觀透明,這正是它與無損編碼的根本差異。
複雜度分析#
編碼器每框:FFT×2(分析+變換域) \(O(N\log N)\) 、遮蔽模型 \(O(MN)\) 、位元分配迭代 \(O(I \cdot N)\) ( \(I\) 為迭代數,通常 3–10)、Huffman \(O(N)\) 。總計 \(O(N\log N + MN + IN)\) ;每秒 43 框(2048/44100)約數百萬次運算,1998 年的 Pentium 可即時編碼。解碼器無心理聲學模型(門檻資訊已隱含在位元流中),僅 Huffman 反解 + IMDCT + 疊加, \(O(N\log N)\) ,便宜得多——這個「編碼貴、解碼便宜」的不對稱是廣播/串流應用的關鍵設計。AAC 的 TNS 增加每框 \(O(N \cdot \text{階數})\) 的預測成本,仍在同量級。
練習#
- 在範例中加入一個短暫的打擊瞬態(如 5 ms 的突波),用單一長窗編碼並檢查瞬態前樣本的重建誤差,觀察預回聲;再將框長縮為 256 驗證改善。
- 把位元上限從 16 降到全域 8,重測壓縮率與 SNR,畫出(列表)位元率–SNR 曲線,說明「資訊丟在哪個頻帶」。
- 實作簡單的 Huffman 編碼(可用
collections.Counter建碼表)套在量化後的係數上,與直接定長編碼比較位元節省率。