用Python制作简单的钢琴程序的教程


Posted in Python onApril 01, 2015

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

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

用Python制作简单的钢琴程序的教程

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

用Python制作简单的钢琴程序的教程

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

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

用Python制作简单的钢琴程序的教程

这里提供一个可以按任意系数改变音频速度的任意简单的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制作简单的钢琴程序的教程

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

def pitchshift(snd_array, n, window_size=2**13, h=2**11):
  """ 将一段音频的音高提高``n``个半音 """
  factor = 2**(1.0 * n / 12.0)
  stretched = stretch(snd_array, 1.0/factor, window_size, h)
  return speedx(stretched[window_size:], factor)

 
小程序:电脑钢琴

让我们来玩一下我们的变调器。我们先敲碗来确定一个“标准音高”:

[youku id="XNzM1NDM2NTky"]

接下来我们基于之前的音频创造50个变调的音高,从很低到很高:
 

from scipy.io import wavfile
 
fps, bowl_sound = wavfile.read("bowl.wav")
tones = range(-25,25)
transposed = [pitchshift(bowl_sound, n) for n in tones]

接下来根据这个文件中的顺序,我们把每一个音频匹配到键盘的一个键位,如下图所示:

用Python制作简单的钢琴程序的教程

我们只需要在代码中告诉计算机当一个键按下来的时候播放其对应的声音,然后当按键松开后停止播放就可以了:

import pygame
 
pygame.mixer.init(fps, -16, 1, 512) # 太灵活了 <img src="http://python.jobbole.com/wp-includes/images/smilies/icon_wink.gif" alt=";)" class="wp-smiley">
screen = pygame.display.set_mode((640,480)) # 设置焦点
 
# 得到键盘的键位的正确顺序的列表
# ``keys`` 如 ['Q','W','E','R' ...] 一样排列
keys = open('typewriter.kb').read().split('\n')
 
sounds = map(pygame.sndarray.make_sound, transposed)
key_sound = dict( zip(keys, sounds) )
is_playing = {k: False for k in keys}
 
while True:
 
  event = pygame.event.wait()
 
  if event.type in (pygame.KEYDOWN, pygame.KEYUP):
    key = pygame.key.name(event.key)
 
  if event.type == pygame.KEYDOWN:
 
    if (key in key_sound.keys()) and (not is_playing[key]):
      key_sound[key].play(fade_ms=50)
      is_playing[key] = True
 
    elif event.key == pygame.K_ESCAPE:
      pygame.quit()
      raise KeyboardInterrupt
 
  elif event.type == pygame.KEYUP and key in key_sound.keys():
 
    key_sound[key].fadeout(50) # 停止播放并50ms淡出
    is_playing[key] = False

就这样我们把计算机变成了一台钢琴!至此,让我为您表演一段土耳其进行曲来表达对您耐心阅读此文的谢意吧:

[youku id="XNzM1NDQ1MDA4"]

如果想自己试试的话,在这里可以下载你需要的所有文件。因为不是所有的人都用Python,我也用Javascript/HTML5(在这儿)实现了一台电脑钢琴,但是不是特别理想。如果有经验丰富的HTML5/JS/elm程序员来改进改进,或者从头重写就太好了。
接下来做什么?

更通常的情况下,我发现计算机很少被用来进行表演性质的演奏。我明白使用钢琴键盘或者直接从乐器录音会容易很多,但是请看看仅仅用一个碗和60行的Python代码就能做到什么!

即便是很便宜的计算机也有如此多的控制来实现一个马马虎虎的音乐台:你可以对着麦克风唱歌,对着摄像头做手势,用鼠标来调制,然后用键盘来完成剩下来的玩意儿。有如此多方式来表现自我,而每种方式又有那么一个Python包……有没有具有艺术天赋的大神加入呀?

Python 相关文章推荐
python使用BeautifulSoup分析网页信息的方法
Apr 04 Python
Python实现网络端口转发和重定向的方法
Sep 19 Python
Python利用multiprocessing实现最简单的分布式作业调度系统实例
Nov 14 Python
python导入csv文件出现SyntaxError问题分析
Dec 15 Python
Python实现时钟显示效果思路详解
Apr 11 Python
Django使用详解:ORM 的反向查找(related_name)
May 30 Python
Python检查ping终端的方法
Jan 26 Python
Appium+python自动化怎么查看程序所占端口号和IP
Jun 14 Python
pybind11和numpy进行交互的方法
Jul 04 Python
django框架cookie和session用法实例详解
Dec 10 Python
No module named ‘win32gui‘ 的解决方法(踩坑之旅)
Feb 18 Python
opencv实现图像平移效果
Mar 24 Python
仅利用30行Python代码来展示X算法
Apr 01 #Python
探究数组排序提升Python程序的循环的运行效率的原因
Apr 01 #Python
用Python编写分析Python程序性能的工具的教程
Apr 01 #Python
对Python新手编程过程中如何规避一些常见问题的建议
Apr 01 #Python
利用Django框架中select_related和prefetch_related函数对数据库查询优化
Apr 01 #Python
用实例详解Python中的Django框架中prefetch_related()函数对数据库查询的优化
Apr 01 #Python
Python的Django框架中的select_related函数对QuerySet 查询的优化
Apr 01 #Python
You might like
新版PHP极大的增强功能和性能
2006/10/09 PHP
发布一个用PHP fsockopen写的HTTP下载的类
2007/02/22 PHP
PHP编码规范-php coding standard
2007/03/16 PHP
ThinkPHP3.1新特性之命名范围的使用
2014/06/19 PHP
input按钮的事件处理大全
2010/12/10 Javascript
基于jquery的可多选的下拉列表框
2012/07/20 Javascript
枚举的实现求得1-1000所有出现1的数字并计算出现1的个数
2013/09/10 Javascript
html+js实现动态显示本地时间
2013/09/21 Javascript
javaScript 计算两个日期的天数相差(示例代码)
2013/12/27 Javascript
比较不错的JS/JQuery显示或隐藏文本的方法
2014/02/13 Javascript
一个非常全面的javascript URL解析函数和分段URL解析方法
2014/04/12 Javascript
Jquery跳到页面指定位置的方法
2014/05/12 Javascript
使用JavaScript获取Request中参数的值方法
2016/09/27 Javascript
jQuery实现背景滑动菜单
2016/12/02 Javascript
详解Node.js实现301、302重定向服务
2017/04/07 Javascript
微信小程序 动态修改页面数据及参数传递过程详解
2019/09/27 Javascript
详解为什么Vue中不要用index作为key(diff算法)
2020/04/04 Javascript
vue 如何使用递归组件
2020/10/23 Javascript
[22:20]初生之犊-TI4第5名LGD战队纪录片
2014/08/13 DOTA
TensorFlow模型保存/载入的两种方法
2018/03/08 Python
tensorflow实现逻辑回归模型
2018/09/08 Python
Flask中endpoint的理解(小结)
2019/12/11 Python
python爬虫开发之PyQuery模块详细使用方法与实例全解
2020/03/09 Python
学习python需要有编程基础吗
2020/06/02 Python
Django实现简单的分页功能
2021/02/22 Python
CSS3改变浏览器滚动条样式
2019/01/04 HTML / CSS
美国电视购物HSN官网:HSN
2016/09/07 全球购物
意大利奢侈品多品牌集合店:TheDoubleF
2019/08/24 全球购物
美国和加拿大计算机和电子产品购物网站:TigerDirect.com
2019/09/13 全球购物
Engel & Bengel官网:婴儿推车、儿童房家具和婴儿设备
2019/12/28 全球购物
英语专业毕业个人求职自荐信
2013/09/21 职场文书
大课间体育活动方案
2014/03/12 职场文书
学生个人自我鉴定
2014/03/26 职场文书
公司周年庆典标语
2014/10/07 职场文书
房贷收入证明范本
2015/06/12 职场文书
2015年初中教师个人工作总结
2015/07/21 职场文书