nwang1986的个人博客分享 http://blog.sciencenet.cn/u/nwang1986

博文

[原][Matlab][04] Midi音乐键盘

已有 6021 次阅读 2015-11-20 18:55 |个人分类:[原创]|系统分类:科研笔记|关键词:学者| MATLAB, Music, video, midi, audio

以前在网上读到一个用matlab播canon音乐的源码感觉很有意思,但声音还不够好。

http://emuch.net/html/201205/4521134.html

http://wenku.baidu.com/view/31fb6dd476a20029bd642d85

于是想做个自己的音乐播放器,有键盘,有对应的钢琴声音,读取乐谱就能播。如下。

(1)按键的音高与频率

从1到下一个音高的1之间有12个音,分别是:1,1#,2,2#,3,4,4#,5,5#,6,6#,7,1(高)。

假设1的音波振动频率是f,而1(高)的频率是2f,那么这十二个半音之间的频率间隔是2对12开方,即2^(1/12)。

于是各个音的相对频率就确定了。

https://en.wikipedia.org/wiki/Piano_key_frequencies

http://blog.sciencenet.cn/blog-81613-673825.html

(2)按键的绝对中心频率

钢琴C调1的中心频率是261.626Hz,对应88键钢琴中的第24个白键。这样根据中心频率和相对频率,所有琴键的振动频率就都知道了。

http://www.360doc.com/content/11/0815/08/3416571_140455810.shtml

http://my.popiano.org/?508271/action_viewspace_itemid_61861.html

(3)采样频率

多种音波采样频率,在此选标准的44100Hz,即每秒钟从声音中采集44100个点。

http://baike.baidu.com/view/23380.htm

(4)音速与采样时间

通常的节拍每分钟对应120个四分之一音符,即每个1/4音符的时长是0.5秒,由此可得1/2音符是1秒,1/8音符是0.25秒,1/16音符是0.125秒。

https://zh.wikipedia.org/wiki/%E9%80%9F%E5%BA%A6_(%E9%9F%B3%E6%A8%82)

(5)乐器音色

不同的乐器演奏出的音色不同,主要是因为发声时不但有中心频率的振动,还有谐波甚至和频波。在网上下载了标准C键钢琴音进行分析。

http://www.vibrationdata.com/piano.htm

频率:将声音波形FFT变换到频域,发现除中心频率,还有2阶到6阶的谐波,各阶的谐波也有对应的强度。

时域:是一个近似的指数衰减,可以通过拟合曲线得到包络参数。

最终可以将基频与谐波的采样曲线线性叠加并乘以时域的包络曲线得到单个钢琴按键的声音。

(6)变音与升降调

有的时候音符需要升降调,感觉很复杂,看不懂,目前我主要采用升降半音或升降一个全音的方式来实现调的转换。

https://zh.wikipedia.org/wiki/%E5%8D%87%E9%9F%B3%E7%AC%A6

https://zh.wikipedia.org/wiki/%E8%87%AA%E7%84%B6%E5%A4%A7%E8%B0%83

这样通过(1)-(6),钢琴的发声基本完成。

(7)键盘布局

标准钢琴共有88键,其中白键52个,黑键26个。白键长144mm*24mm,黑键长86mm*9mm,以以上参数在matlab中使用rectangle函数分别采用edgecolor和facecolor参数设置边框颜色和填充颜色没有问题。

(8)按键的动态显示

当按下键时使用set函数改变对应按键handleID的facecolor可以实现变色。由于会出现图形图层的变化,可以使用uistack函数将白键设置为底层,黑键设置为顶层。

这样通过(7)-(8),钢琴的按键动画基本完成。

(9)按键声音与按键动画的配合

在程序中首先初始化所有声音,钢琴键盘和为每个按键分配一个handleID,将每个ID与该ID进行声音和图像关联。当需要对一个音符响应时,首先将音符翻译到对应的handleID,然后设置该ID的键变色,然后开始播放声音,根据音符的长度使用pause函数停顿对应的时长,然后将ID的按键颜色复原。

(10)乐谱读取

最方便的播放方法是将乐谱写在一个文本文件中,程序读取文本文件,然后翻译成音符,然后再翻译成handleID,然后播放。我用一个6位字符表示一个音符,比如1#+104表示音高为1升半音再高一个八度音符是1/4音符;比如3-216表示音高为3再减2个八度音符是1/16音符;1/4-108表示音高为1的键和音高为4减一个八度的键同时按下持续时长对应1/8音符。

在程序中使用fgets读取文件,并将所有行连接成一个单行字符串,然后使用regexp的split方法将字符串用空格分开,再用split方法用/符号将各元胞分开得到单个音符,然后判断音符的音高,是否升降半音,是否升降八度,音符长度。之后根据音调得到这个按键对应的handleID,是否是黑键,和音符长度。

通过(9)-(10),乐谱读取和播放的功能就实现了。

(11)音乐保存

想把生成的音乐保存下来,首先要把所有的音符根据其时长间隔加到同一个一维数组上,然后使用audiowrite函数保存为文件。

(12)视频保存

想把视频保存下来,首要要把所有的按键响应动画使用getframe获取,然后使用VideoWriter,open,writeVideo,close函数实现。中间需要根据帧速和音符长度设置每帧写入的数量。

(13)合成多媒体

将视频和音乐合成在一起就完美了,但我还没有实现,有个toolbox可以做,但目前由于采样率的不同遇到一些问题。可以在matlab的命令行窗口中输入ex_combine_video_and_andio_streams打开示例查看。最终将matlab生成的视频和音乐用moviemaker合成,可以得到初级的视频效果。

附件:CanonC.mp4(CanonC调),CanonD.mp4(CanonD调)



https://m.sciencenet.cn/blog-2857675-936944.html

上一篇:[原][Python][01] 下载器+破防盗链
下一篇:[随记][算法]进行三维图像处理的入门算法

0

该博文允许注册用户评论 请点击登录 评论 (0 个评论)

数据加载中...
扫一扫,分享此博文

Archiver|手机版|科学网 ( 京ICP备07017567号-12 )

GMT+8, 2024-5-13 06:32

Powered by ScienceNet.cn

Copyright © 2007- 中国科学报社

返回顶部