根据MATLAB的音乐电子琴制作.docx
《根据MATLAB的音乐电子琴制作.docx》由会员分享,可在线阅读,更多相关《根据MATLAB的音乐电子琴制作.docx(16页珍藏版)》请在冰豆网上搜索。
根据MATLAB的音乐电子琴制作
基于MATLAB的音乐电子琴制作
简述:
电子琴的每个音阶均对应一个特定频率的信号,通过调用数字信号发生器产生一系列指定的频率的声音,从而达到虚拟的电子琴的功能。
本次设计是基于MATLABGUI程序实现的一个音乐键盘仿真系统。
1功能介绍
总体设计框图如下图所示,其包括单音键盘发音模块,音效长短的选择模块,包络的选择模块,实现键盘代替鼠标输入模块,双音多频模块,演奏音乐模块,播放歌曲,视频模块包括对文件播放的暂停,停止和复位,多键盘输入对输入后统一播放模块和画图模块。
Matlab的数据采集工具箱(DAT)提供了一系列的函数和命令来实现实时模拟信号的输出,通过调用这些函数和命令可以直接控制声卡输出虚拟信号。
只需要一台带有普通多媒体声卡并安装了Matlab软件的计算机就可以满足要求实现虚拟信号的输出,系统结构简单方便。
交互界面如图1所示:
图1程序的交互界面
1.1功能模块
1)单音键盘发音模块
设计一个带参子函数实现键盘的发音功能,当实现需要实现音阶的播放时,只要调用这个子函数,并根据不同音阶、不同音调的频率改变子函数的参数即可。
2)音效长短的选择模块
制作一个buttongroup的组控件分别选择不同的控件实现不同的音效长短,音效的长短是通过改变播放一个音阶的时间长短来实现的。
3)包络的选择模块
制作一个buttongroup的组控件分别选择不同的控件实现不同的包络,在模块一的基础上,设置选中不同的控件分别对应不同的包络,x为不同包络(如正弦波、三角波、指数等)的表达形式,将x与模块一中实现单音键盘发音的函数相乘时便可实现不同形式的衰减,实现音型的改变。
4)实现键盘代替鼠标输入模块
根据计算机键盘上的不同按键对应不同的ASCII码的值,利用函数get()获取当前所按下的数字键对应的ASCII码的值,根据ASCII码的值判断对应是按下键盘的值。
并执行相应音阶的功能键。
5)双音多频模块
通过设置一个radiobutton来实现双音多频的功能,设置一个全局变量,当选中该控件时,全局变量的值改变,即在带参的子函数中增加它的频率分量。
就可以实现双音多频功能。
6)演奏音乐模块
通过设置一个pushbutton键来实现,按下该键时,可以选择事先自己编好的txt的文档,通过这个文档就可以播放音乐。
7)播放歌曲,视频模块
根据matlab提供的函数,视频时首先对文件的名字和路径进行提起,直接对文件的播放。
音乐的则是首先对文件的名字和路径进行提起,得到名字和路径后就对该文件进行采样,使其离散化。
最后实现对文件的播放。
8)多键盘输入对输入后统一播放模块
该功能实现先对键盘输入内容进行存储,当输入完成后就可以按播放键对刚才的存储内容进行播放,本功能通过radiobutton键实现的键盘输入的存储,当该建被选中时,则会不断的扫描键盘是否有键盘按下,并对按下键进行存储,直到该键没有被选中为止,同时设置一个pushbutton键对存储的信息进行播放,播放完成后自动清除存储的内容,以便下一次存储。
9)画图模块
该模块的功能是根据播放每一个音符的数组画出每一个音符的波形,使我们对播放的音型可以一目了然,便于观察与分析。
2功能实现
程序由两个部分组成:
MATLAB代码(.m文件)和GUI图形(.fig)。
备注:
软件版本:
MATLABR2011b
2.1单音键盘发音模块
根据要求,首先利用pushbutton键作为单音键盘的发音键。
1~7七个音阶对应高中低三种不同的音调共21个键,还有15个辅音,共36个按键。
如图2所示,白色的按键代表音调键,前7个是低音的七个音阶,中间7个是中音的七个音阶,后面7个是高音的七个音阶,黑色的代表辅音。
查阅相关资料可知,发音频率对应的表达式为f=440*2^((s-49)/12),当所发音为低音时s的取值为31~37,发中音时s的取值为40~46,发高音时s的取值为49~55。
为了程序设计简化目的,设计一个名为gangqin(s)的子函数。
有以上带参的子函数后则每个键盘下面的程序非常简单,然后在每个pushbutton键的callback函数中调用该子函数即可,如gangqin(45);
图2单音键盘发音模块
具体代码参见:
functiongangqin(s)
2.1.1音效长短的选择模块
制作一个buttongroup的组控件分别选择不同的控件实现不同的音效长短,该组控件包括三个radiobutton分别对应不同的音效长短(长,中,短)。
音效长短的改变实质上是改变其音阶播放时间的长短,定义一个全局变量T通过改变T的值来改变音效的长度,gangqin的子函数如上面所示。
界面如图3所示:
图3音效长短的选择模块
具体代码参见:
Functionyinxiao_Callback(hObject,eventdata,handles)
2.2包络的选择模块
制作一个buttongroup的组控件分别选择不同的控件实现不同的包络,该组控件包括三个radiobutton分别对应不同的包络。
如图4所示,定义了方波、三角波和正弦波三种不同形式的包络可供选择。
图4包络的选择模块
将组合键中的buttongroup键的Tag的值设置为“baol”,三个radiobutton键分别对应指数波、三角波、正弦波,相应的Tag设“zhishu”、”sanjiao”、”zhengxian”,def=get(de,'tag')获取三个RadioButton键中Tag的值,设置一个全局变量p,用switch函数实现,当case为'zhishu'时,p的值1;case为'sanjiao'时,p的值2;case为'zhengxian'时p的值为3。
把p的值反应到gangqin(s)的子函数中,在子函数中根据不同的p的值选用不同的包络,即可实现不同的包络,所发出的音型便不一样。
由于电子琴的实验结果是声音,难以用文字表达,在下面仅用几张图片展示一下结果。
图5正弦波包络
图6方波包络
图7三角波
具体代码参见:
Functionbaoluo_Callback(hObject,eventdata,handles)
2.3实现键盘代替鼠标输入模块
用一个radiobutton键便可实现该功能,可以根据不同的键盘按键发出不同的音乐。
同时将该键的string改为“键盘”,表示这个键是实现键盘的功能。
如图8所示:
图8键盘代替鼠标输入
根据计算机键盘上不同键对应的ASCII码的值不同,利用函数get()获取当前所按下的键对应的ASCII码的值,并执行相应播放音阶。
具体代码参见:
functionjianpan_KeyPressFcn(hObject,eventdata,handles)
2.4双音多频模块
通过设置一个radiobutton来实现双音多频的功能,当选中该按钮时,则增加它的频率分量。
使其含有丰富频率分量。
如图9所示:
图9双音多频模块
具体代码参见:
functionduopin_Callback(hObject,eventdata,handles)
2.5演奏音乐模块
通过设置一个pushbutton键来实现,如图10所示:
图10播放谱曲
按下该键时,界面会要求你选择文档,可以选择事先自己编好的txt的文档,选中该文档,并按打开键就可以播放音乐。
txt文件的内容如图11所示:
图11乐谱内容
Txt文件代表的意思是如‘441’,是表示频率为44,延长的长度为1。
播放音乐不仅需要确定每个音符,还要确定每个音符延长的时间,音符延长的时间是有n的取值大小所确定的。
因此这里就可以调用子函数的形式方便的实现。
具体代码参见:
functionqinpu(a,b)
functiondakai_Callback(hObject,eventdata,handles)
2.6播放歌曲,视频模块
本功能的实现是通过pushbutton键来实现的。
界面如图12所示:
图12播放歌曲视频模块
通过按下播放歌曲键或者播放视频键就可以选择播放的文件,界面如图13所示:
按打开按钮就可以播放音乐或视频。
图13播放视频
视频时首先对文件的名字和路径进行提起,直接对文件的播放,用的是matlab自带的函数implay()。
音乐的则是首先对文件的名字和路径进行提起,得到名字和路径后就对该文件进行采样,使其离散化。
最后实现对文件的播放。
这里不仅可以对音乐的播放,还可以对音乐的暂停,复位和停止。
分别用到matlab自带的函数:
播放play(),暂停pause(),复位resume()和停止stop()。
具体代码参见:
functionshipin_Callback(hObject,eventdata,handles)
functionchangge_Callback(hObject,eventdata,handles)
2.7多键盘输入后统一播放模块
本功能通过radiobutton键实现的键盘输入的存储,当该建被选中时,则会不断的扫描键盘是否有键盘按下,并对按下键进行存储,直到该键没有被选中为止,同时设置一个pushbutton键对存储的信息进行播放,播放完成后自动清除存储的内容,以便下一次存储。
界面如图14所示:
图14多键盘输入后播放
该功能实现先对键盘输入内容进行存储,当输入完成后就可以按播放键对刚才的存储内容进行播放,播放完成后对存储的内容进行清空。
实现对键盘的输入内容进行存储,并把存储的值整合到数组y内,pushbutton键是对上述存储的信息进行读取,识别并播放。
具体代码参见:
functionjianc_KeyPressFcn(hObject,eventdata,handles)
functionbf_Callback(hObject,eventdata,handles)
2.8画图模块
通过axse控件实现绘图,绘出播放每个音阶的波形,并把画出的波形显示到axse上,界面如图15所示:
图15音阶的波形
该模块的功能是根据播放每一个音符的数组画出每一个音符的波形,并把其显示在界面上。
具体代码参见:
functionaxes1_CreateFcn(hObject,eventdata,handles)
3程序总结
本程序实现简单的音频处理功能及便捷的图形化交互界面。
具有以下特点与缺陷:
1、图形化用户交互界面简洁明了。
右侧放置音频视频播放的各选项控件,且从上到下的排布体现操作步骤;上侧显示音频文件的数据波形。
提示信息丰富,方便操作。
2、程序可扩展性好,方便功能扩展。
未尽功能:
(1)没有调节音量大小的按钮,可以优化。
(2)声音播放时,实时的显示播放进度。
(3)更为实际的音频处理功能待加强。
4课程总结
整个制作和调试过程都是按模块进行的,对每一个模块功能的实现的情况下才对下一个功能进行制作。
刚开始做模块一时,由于没有想到后面的一些功能所以就没有写子函数,是在每一个按键下都写了一个声音播放的程序,这样做不仅量大也很不方便。
因此后面改成了子函数的形式,这样的话程序不仅简练也很好调用,非常方便。
在调试过程中先将函数gangqin(s)中的全局变量p的不同数字代表不同的波形表达式,鼠标点击包络组控件的不同按钮,选择不同的包络,听起来的效果也相差较大,当包络为指数形式时较符合平时听音乐的习惯。
还有一个是gangqin(s)中的全局变量T,不同的值表示不同的音效长短。
播放的时间是不一样的,因此听起来会有不同的感觉。
调试过程中应根据相关资料和自己的感觉不断改变这两个值的数字,最终选择一个听起来较为合适的取值。
上述完成后就需要做键盘代替鼠标按键的功能,首先需要了解按下键盘电脑获得的是按键的ASCII值。
所以我们必须把按键的ASCII的值对应到播放不同音符的频率上去,这样才能灵活的控制每一个音符,每个音符要与键盘的按键形成一一对应关系,不然的话程序很容易出错。
程序完成后,需要对程序进行调试,使得每一个键盘的按钮按下对应的音符会播放出来。
接下来制作的是键盘的存储功能,即实现对键盘的多输入,输入过程中音符是不会响的,当输入结束后可以控制对刚才输入的按键对应的音符进行播放声音。
刚开始是一直对数字的存储个数会多很多,后面才知道但你按下键时程序已经运行了好多遍了,每次按键是当然就会存储多个相同的值。
改进后就一直只能对最后一次按键进行存储,前面的被当前的值覆盖,后面设置了一个存储数据的数组进行存储,使得功能才能实现。
本次课程设计,我花了大量的时间来做这些功能,但是做完后感觉功能又特别简单,而且做的大部分时间都是做一些无用的工作,程序一直不够简单,经常用一条很长的程序来实现一个简单的功能,做完过后发现可以用简单的程序就可以实现,整个制作过程中大部分的时间都花在这样的工作上,工作效率很低。
同时感觉到自己对matlab的编程了解太少,很多基本功能多不了解。
完成实践后我深深的体会到了MATLAB功能的强大,它不但可以实现对声音信号的处理,对图像的处理,同时还可以对视频,wav格式歌曲的播放以及还有很多我现在还不知道的很多强大的功能。
在实验过程中,遇到了很多问题。
首先是资料的缺乏,没能找到老师推荐的《信号与系统—MATLAB综合实验》,所以大部分的参考资料只能上网查找没有什么权威性。
还有自己对matlab的了解本来就很浅,很多知识都不懂,经常需要问老师,同学或者上网查资料。
在设计刚开始没有什么明确的思路导致经常改GUI界面,最后实在无法改了就重新设计,思路很不明确,希望在以后的学习和工作中能好好利用这次实践的经验,要先想好总体思路,不能到临时才改,这样的话,不仅工作量大,还耗时多。
本次实践使我受益匪浅。
附录(部分程序)
functiongangqin(s)%演奏音符
globalr;
globaln;
globalff;
globalT;
globalp;
f=440*2^((s-49)/12);%各音阶的频率
n=0:
1/8000:
T;%各音阶的长短
ifp==1%选用包络
x=exp(-3*n);
elseifp==2
x=exp(-3*n).*sawtooth(2*pi*50*n);
elseifp==3
x=exp(-3*n).*sin(2*pi*n);
end
ifr==1%选用单频还是多频
ff=x.*sin(f*2*pi*n)+0.2*x.*sin(f*4*pi*n)+0.05*x.*sin(f*8*pi*n);
elseff=x.*sin(f*2*pi*n);
end
axes1_CreateFcn();%画图
soundsc(ff);%播放音符
Functionyinxiao_Callback(hObject,eventdata,handles)
globalT;
g=get(handles.yinxiao,'selectedobject');%获得音效的选中对象
gt=get(g,'tag');%把对象赋给gt
switchgt%确认选中的对象
case'duanyin'
T=0.3;
case'zhongyin'
T=1;
case'changyin'
T=2;
End
Functionbaoluo_Callback(hObject,eventdata,handles)
globalT;
globaln;
globalp;
de=get(handles.baol,'selectedobject');%获得包络选中的对象
def=get(de,'tag');%把选中对象的tag赋给def
switchdef
case'正弦波'
x=0:
0.001:
1;
y1=A*sin(2*f*pi*x+c);
plot(x,y1);
case'三角波’
x=0:
0.001:
1;
y2=A*sawtooth(2*pi*f*x+c);
plot(x,y2);
case'方波'
x=0:
0.001:
1;
y3=A*square(2*f*pi*x+c);
plot(x,y3);
end
functionjianpan_KeyPressFcn(hObject,eventdata,handles)
s=get(handles.jianpan,'value');%获得按下键的值
ifs==0%看radiobutton是否按下
elseifget(gcf,'CurrentCharacter')==48
gangqin(28);
elseifget(gcf,'CurrentCharacter')==49
gangqin(29);
elseifget(gcf,'CurrentCharacter')==50
gangqin(30);
elseifget(gcf,'CurrentCharacter')==51
gangqin(31);
elseifget(gcf,'CurrentCharacter')==52
gangqin(32);
elseifget(gcf,'CurrentCharacter')==53
gangqin(33);
elseifget(gcf,'CurrentCharacter')==54
gangqin(34);
elseifget(gcf,'CurrentCharacter')==55
gangqin(35);
elseifget(gcf,'CurrentCharacter')==56
gangqin(36);
elseifget(gcf,'CurrentCharacter')==57
gangqin(37);
elseifget(gcf,'CurrentCharacter')==65
gangqin(38);
elseifget(gcf,'CurrentCharacter')==66
gangqin(39);
elseifget(gcf,'CurrentCharacter')==67
gangqin(40);
elseifget(gcf,'CurrentCharacter')==68
gangqin(41);
elseifget(gcf,'CurrentCharacter')==69
gangqin(42);
elseifget(gcf,'CurrentCharacter')==70
gangqin(43);
elseifget(gcf,'CurrentCharacter')==71
gangqin(44);
elseifget(gcf,'CurrentCharacter')==72
gangqin(45);
elseifget(gcf,'CurrentCharacter')==73
gangqin(46);
elseifget(gcf,'CurrentCharacter')==74
gangqin(47);
elseifget(gcf,'CurrentCharacter')==75
gangqin(48);
elseifget(gcf,'CurrentCharacter')==76
gangqin(49);
elseifget(gcf,'CurrentCharacter')==77
gangqin(50);
elseifget(gcf,'CurrentCharacter')==78
gangqin(51);
elseifget(gcf,'CurrentCharacter')==79
gangqin(52);
elseifget(gcf,'CurrentCharacter')==80
gangqin(53);
elseifget(gcf,'CurrentCharacter')==81
gangqin(54);
elseifget(gcf,'CurrentCharacter')==82
gangqin(55);
elseifget(gcf,'CurrentCharacter')==83
gangqin(56);
elseifget(gcf,'CurrentCharacter')==84
gangqin(57);
elseifget(gcf,'CurrentCharacter')==85
gangqin(58);
elseifget(gcf,'CurrentCharacter')==86
gangqin(59);
elseifget(gcf,'CurrentCharacter')==87
gangqin(60);
elseifget(gcf,'CurrentCharacter')==88
gangqin(61);
elseifget(gcf,'CurrentCharacter')==89
gangqin(62);
elseifget(gcf,'CurrentCharacter')==90
gangqin(63);
end
functionduopin_Callback(hObject,eventdata,handles)
globalr;
r=get(handles.duopin,'value');%多频的按钮是否选中
ifr==1%选单频还是多频
ff=x.*sin(f*2*pi*n)+0.2*x.*sin(f*4*pi*n)+0.05*x.*sin(f*8*pi*n);
elseff=x.*sin(f*2*pi*n);
End
functionqinpu(a,b)%播放不同延长音的音符a表示音阶的频率b表示延长音的长度
globalr;
globaln;
globalff;
f=440*2^((a-49)/12);
n=0:
1/8000:
b*0.5;%延长不同的长度
x=exp(-2*n);
ifr==1%选用单频还是多频播放
ff=x.*sin(f*2*pi*n)+0.2*x.*sin(f*4*pi*n)+0.05*x.*sin(f*8*pi*n);
elseff=x.*sin(f*2*pi*n);
end
axes1_CreateFcn();%画图
soundsc(ff);%播放音符
functiondakai_Callback(hObject,eventdata,handles)
[name,path]=uigetfile('*.*','');%读取文件
file=sprintf('%s%s',path,name);
de=importdata(file);%读取文件的数据
fork=1:
length(de)
qinpu(de(k),de(k+length(de)));%按文件数据播放音符
end
functionshipin_Callback(hObject,eventdata,handles)%播放视频程序
[name,path]=uigetfile('*.*','');
file=[path,name];
implay(file);
functionchangge_Callbac