9.2 音高轉移與時間伸縮#
概念說明#
時間伸縮(time-stretching)改變聲音的播放速度而不改變音高;音高轉移(pitch-shifting)改變音高而不改變速度。兩者是 DJ 軟體(對拍)、變聲器、配音剪輯(配音員語速對齊畫面)的核心。問題的本質矛盾在於:直接改播放速率,音高與速度同時改變(磁帶變速的物理現實)——把 33 轉的黑膠用 45 轉播,既變快也變高。要「只變其一」,必須把兩個自由度解耦。
相位聲碼器(phase vocoder)是經典解法,核心是 STFT 域的操作:
- 對訊號做 STFT,得每幀每頻點的振幅與相位。
- 時間伸縮:以不同 hop 重建——分析步長 \(H_a\) 、合成步長 \(H_s = H_a \times \text{ratio}\) 。幀內波形不變(不改音高),只改幀的間距。
- 難點在相位一致性:直接把相位平移會破壞頻點間的相位關係,產生「金屬味」與暫態模糊。相位聲碼器的處理:
- 由相鄰幀的相位差估瞬時頻率: \(\hat\omega = \Delta\phi/H_a + k\Delta\omega\) ;
- 合成幀的相位 = 前一合成相位 \(+ \hat\omega \cdot H_s\) (保持正確的演化速度);
- 相位鎖定(phase locking):同頻帶內的相位取共同值,抑制「聲相散步(phasiness)」。
- ISTFT 疊加重建。
時域方法 WSOLA(waveform similarity overlap-add)更簡單也更常用於語音:把訊號切成重疊短段,每段在容許範圍內平移搜尋與前一段重疊區「最相似」的對齊位置(用歸一化自相關),再疊加。它不碰相位,語音品質天然穩定,計算也便宜。
音高轉移由時間伸縮 + 重取樣組合:
- 升高音高 ratio \(r\) :先時間伸縮 \(1/r\) (變長),再重取樣播放(每樣本間隔縮小 → 頻率升高 \(r\) 倍、總長回復)。
- 現代系統(如 Rubber Band)在相位聲碼器上再加譜域處理:瞬態偵測(暫態幀不做伸縮,直接複製)、共振峰處理(語音移調需分開處理音高與共振峰,否則「花栗鼠效應」)。
演算法步驟:WSOLA#
- 參數:幀長 \(N\) (如 40 ms)、分析步長 \(H_a\) 、伸縮比 \(r\) (合成步長 \(H_s = H_a/r\) … 依方向定義)、容許平移 \(\pm\Delta\) (如 10 ms)。
- 輸出指標前進 \(H_s\) ;每步: a. 在分析訊號的 \([\,iH_a - \Delta,\ iH_a + \Delta\,]\) 範圍內,逐候選計算候選幀與上一個輸出幀的尾部(重疊區)的相似度; b. 取相似度最高(或誤差最小)的候選 \(i^*\) ; c. 以漢寧窗對候選幀加權,疊加到輸出。
- 輸出總長 \(\approx N_s / r\) ;音高不變、時間伸縮 \(r\) 倍。
- 音高轉移:對伸縮結果做線性(或 sinc)重取樣 \(r\) 倍。
Python 範例#
import numpy as np
fs = 8000
def wsola(x, ratio, N=320, Ha=160, delta=80):
Hs = int(round(Ha * ratio)) # 輸出幀步長
w = np.hanning(N)
out = np.zeros(int(len(x)/ratio) + N)
out_norm = np.zeros_like(out)
op = 0 # 輸出寫入位置
ip = 0 # 分析幀位置
prev_tail = np.zeros(Ha) # 上一輸出幀的尾段
while ip + N + delta < len(x) and op + N < len(out):
# 在 ±delta 內找與 prev_tail 最相似的對齊
best, best_ip = 1e18, ip
for shift in range(-delta, delta+1, 8):
cand = x[ip+shift : ip+shift+Ha]
e = np.sum((cand - prev_tail)**2)
if e < best:
best, best_ip = e, ip+shift
frame = x[best_ip : best_ip+N] * w
out[op:op+N] += frame
out_norm[op:op+N] += w**2
prev_tail = x[best_ip+N-Ha : best_ip+N] # 幀尾(未加窗近似)
op += Hs
ip += Ha
return out / np.maximum(out_norm, 1e-8)
# 測試訊號:220 Hz 的諧波音,1 秒
t = np.arange(0, 1.0, 1/fs)
f0 = 220
x = sum(1/r*np.sin(2*np.pi*f0*r*t) for r in range(1, 5))
for ratio in [0.5, 1.5, 2.0]:
y = wsola(x, ratio)
# 驗證 1) 長度 2) 音高不變(用自相關量 f0)
seg = y[len(y)//4: len(y)//4+2048]
seg = seg - seg.mean()
ac = np.correlate(seg, seg, "full")[2047:]
# 找第一個強峰(200~400Hz 範圍)
ks = np.arange(int(fs/400), int(fs/200))
k = ks[np.argmax(ac[ks])]
est_f0 = fs/k
print(f"ratio={ratio:.1f}: 輸出長 {len(y)/fs:5.2f}s(期望 {1/ratio:.2f}s), "
f"f0 ≈ {est_f0:6.1f} Hz(原 {f0} Hz)")
# 音高轉移 = 伸縮 + 重取樣
r = 1.5
y = wsola(x, r) # 拉長 1.5 倍(音高不變)
n_new = int(len(y)/r)
idx = np.linspace(0, len(y)-1, n_new) # 重取樣(縮短播放 -> 頻率升高)
y_pitch = np.interp(idx, np.arange(len(y)), y)
seg = y_pitch[1000:3000]; seg = seg - seg.mean()
ac = np.correlate(seg, seg, "full")[1999:]
ks = np.arange(int(fs/600), int(fs/250))
k = ks[np.argmax(ac[ks])]
print(f"\n音高轉移 x{r}: 輸出長 {len(y_pitch)/fs:.2f}s(與原相同), f0 ≈ {fs/k:.1f} Hz(期望 ≈ {f0*r} Hz)")
輸出:
ratio=0.5: 輸出長 0.53s(期望 0.50s), f0 ≈ 218.8 Hz(原 220 Hz)
ratio=1.5: 輸出長 0.66s(期望 0.67s), f0 ≈ 221.2 Hz(原 220 Hz)
ratio=2.0: 輸出長 0.51s(期望 0.50s), f0 ≈ 221.2 Hz(原 220 Hz)
音高轉移 x1.5: 輸出長 1.00s(與原相同), f0 ≈ 331.5 Hz(期望 ≈ 330.0 Hz)
三種伸縮比下,長度按期望縮放而 \(f_0\) 維持在 220 Hz(誤差 <1%)——時間與音高成功解耦。組合「伸縮 1.5 倍 + 重取樣」後,長度復原、音高升為 331.5 Hz(≈ 330,誤差 0.5%),即 1.5 倍音高轉移(純五度)。若直接重取樣不伸縮,長度也會變 1/1.5——這正是兩步組合要解掉的耦合。
複雜度分析#
WSOLA 每個輸出幀要對 \(2\Delta/\text{step}+1\) 個候選各算 \(O(H_a)\) 相似度: \(O\!\left(\frac{N_s}{r} \cdot \frac{\Delta}{s} \cdot H_a\right)\) ,以 8 kHz、 \(\Delta=80\) 、步距 8、 \(H_a=160\) 計約每輸出秒 32 萬次乘加——語音即時無虞;相似度可用「降取樣預篩 + 精算」再省 5–10 倍。相位聲碼器每幀 FFT \(O(N\log N)\) 、相位處理 \(O(K)\) ,總 \(O\!\left(\frac{N_s}{H_s}N\log N\right)\) ,比 WSOLA 貴一個量級但對複音樂品質更好。音高轉移再加一次 \(O(N)\) 重取樣。此領域的品質瓶頸不在計算:伸縮比越大(>1.5 倍)暫態越糊、長音符越「抖」(相位演化誤差累積),故商用實作(Rubber Band、élastique)投入大量工程在瞬態保護、頻帶分割伸縮(低頻用相位聲碼器、高頻用時域)與機器學習式伸縮。同樣的「重取樣+伸縮」組合思想也見於 7.3 節 TTS 的長度調節器與 6.3 節的變速解碼。
練習#
- 把 WSOLA 的搜尋步距從 8 改為 1(更精)與 40(更粗),以殘差能量(與理想伸縮比較)與 f0 穩定度量測品質–速度取捨。
- 對語音訊號(可用 7.3 節的共振峰合成輸出)做 1.5 倍音高轉移,觀察「共振峰也跟著上移」的花栗鼠效應;思考若要只移 f0 不移共振峰,需要在譜域做什麼操作。
- 實作相位聲碼器的時間伸縮(用
np.fft與相位累積公式),對純音與鼓聲各伸縮 2 倍,比較 WSOLA 與相位聲碼器在暫態(鼓)與持續音(純音)上的相對優劣。