录一段音频,把它的音高改变50次并把每一个新的音频匹配到键盘的一个键位,你就能把电脑变成一架钢琴!
一段音频可以被编码为一组数值的数组(或者列表),像这样: 
我们可以在数组中每隔一秒拿掉一秒的值来将这段音频的速度变成两倍: 
如此我们不仅将音频的长度减半了,而且我们还将它的频率翻倍了,这样使得它拥有比原来更高的音高(pitch)。
相反地,假如我们将数组中每个值重复一次,我们将得到一段更慢,周期更长,即音高更低的音频: 
这里提供一个可以按任意系数改变音频速度的任意简单的Python函数: import numpy as np def speedx(sound_array, 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): 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 ()) return result.astype( 'int16' ) |
|