linux 虚拟钢琴程序,用Python制作简单的钢琴程序的教程

录一段音频,把它的音高改变50次并把每一个新的音频匹配到键盘的一个键位,你就能把电脑变成一架钢琴!

一段音频可以被编码为一组数值的数组(或者列表),像这样:

7f42fa4905168926fc2bdbdcc5a90272.png

我们可以在数组中每隔一秒拿掉一秒的值来将这段音频的速度变成两倍。

6520f2208177700aaad53b391455bbad.png

如此我们不仅将音频的长度减半了,而且我们还将它的频率翻倍了,这样使得它拥有比原来更高的音高(pitch)。

相反地,假如我们将数组中每个值重复一次,我们将得到一段更慢,周期更长,即音高更低的音频:

e26da0333eb0a1b9b0e10ea1ff2ecffa.png

这里提供一个可以按任意系数改变音频速度的任意简单的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,感兴趣的朋友可以读一下维基百科的页面)来实现。首先将音频分解成重叠的比特,然后将这些比特重新排列使得他们重叠得更多(将缩短声音的长度)或者更少(将拉伸音频的长度),如下图所示:

687980ed0b77135c7fb043df0d6360bb.png

困难之处在于重新排列的比特可能很严重的互相影响,那么这里就需要用到相位变换来确保它们之间没有影响。这里有一段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):

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值