錄一段音頻,把它的音高改變50次并把每一個新的音頻匹配到鍵盤的一個鍵位,你就能把電腦變成一架鋼琴!
一段音頻可以被編碼為一組數值的數組(或者列表),像這樣:
我們可以在數組中每隔一秒拿掉一秒的值來將這段音頻的速度變成兩倍。
如此我們不僅將音頻的長度減半了,而且我們還將它的頻率翻倍了,這樣使得它擁有比原來更高的音高(pitch)。
相反地,假如我們將數組中每個值重復一次,我們將得到一段更慢,周期更長,即音高更低的音頻:
這里提供一個可以按任意系數改變音頻速度的任意簡單的Python函數:
import numpy as np def speedx(sound_array, factor): """ 將音頻速度乘以任意系數`factor` """ indices = np.round( np.arange(0, len(snd_array), factor) ) indices = indices[indices < len(snd_array)].astype(int) return sound_array[ indices.astype(int) ]
這個問題更困難的地方在于改變音頻長度的同時保持它的音高(變速,音頻拉伸(sound stretching)),或者在改變音頻的音高的同時保持它的長度(變調(pitch shifting))。
變速
變速可以通過傳統的相位聲碼器(phase vocoder,感興趣的朋友可以讀一下維基百科的頁面)來實現。首先將音頻分解成重疊的比特,然后將這些比特重新排列使得他們重疊得更多(將縮短聲音的長度)或者更少(將拉伸音頻的長度),如下圖所示:
困難之處在于重新排列的比特可能很嚴重的互相影響,那么這里就需要用到相位變換來確保它們之間沒有影響。這里有一段Python代碼,取自這個網頁(打不開的話,您懂的。――譯者注):
def stretch(sound_array, f, window_size, h): """ 將音頻按系數`f`拉伸 """ phase = np.zeros(window_size) hanning_window = np.hanning(window_size) result = np.zeros( len(sound_array) /f + window_size) for i in np.arange(0, len(sound_array)-(window_size+h), h*f): # 兩個可能互相重疊的子數列 a1 = sound_array[i: i + window_size] a2 = sound_array[i + h: i + window_size + h] # 按第一個數列重新同步第二個數列 s1 = np.fft.fft(hanning_window * a1) s2 = np.fft.fft(hanning_window * a2) phase = (phase + np.angle(s2/s1)) % 2*np.pi a2_rephased = np.fft.ifft(np.abs(s2)*np.exp(1j*phase)) # 加入到結果中 i2 = int(i/f) result[i2 : i2 + window_size] += hanning_window*a2_rephased result = ((2**(16-4)) * result/result.max()) # 歸一化 (16bit) return result.astype('int16')
變調
一旦你實現了變速以后,變調就不難了。如果需要一個更高的音高,可以先將這段音頻拉伸并保持音高不變,然后再加快它的速度,如此最后得到的音頻將具有原始音頻同樣的長度,更高的頻率,即更高的音高。
把一段音頻的頻率翻倍將把音高提高一個八度,也就是12個半音。因此,要將音高提高n個半音的話,我們需要將頻率乘上系數2^(n/12):
新聞熱點
疑難解答