用Matlab来放音乐,和用单片机加蜂鸣器放音乐的原理都差不多,就是把连续的声音信号事先转换成用数字信号,然后用扬声器按照一定的节奏放出来。换句话说,演唱者是把声音经过麦克风转换成电信号,录音设备对这个电信号按照一定的时间间隔(采样频率)进行采样,得到一长串数字。如果采样的频率高,即单位时间采样的点数多,同样长度的一首歌,得到的这串数字也越长。数字的大小表示电压的高低,也就是录制时声音的大小。这串数字就是原始的音频信号。
链接里给出的那段Matlab代码的功能,就是模拟产生那串代表音频信号的数字,把这串数字交给数字模拟转换器,生成对应的电压信号,再由扬声器把这个电信号(电能)转变成声音信号(机械能)。
我不懂音乐,就从信号的角度来解释吧。
%%----------------------------------------------------------------------
fs = 44100; % sample rate
dt = 1/fs;
这一行定义了采样频率为44100赫兹,也就是说每秒钟需要播放44100个数字。为什么是44100Hz,或者44.1KHz,而不是其他更高或者更低的频率?因为我们的耳朵能够感受到的声音频率大约是20Hz ~ 20KHz(我最多只能听到17KHz左右),对于更高频率的声音我们的耳朵只能“呵呵”了。然后根据采样定理,“当采样频率大于信号中最高频率的2倍时,采样之后的数字信号可以完整地保留了原始信号中的信息”,所以理论上20KHz的信号至少用40KHz去采样。44.1KHz这个数字按照维基百科的说法是Sony公司在1979年先开始使用,然后被公司慢慢接受了。dt就是fs的倒数,即每两个数据点之间的时间间隔。
%%----------------------------------------------------------------------
T16 = 0.125;
t16 = [0:dt:T16];
t4 = linspace(0,4*T16,4*k);
t8 = linspace(0,2*T16,2*k);
t16, t4, t8都是数组,他们的长度不一样。k表示t16数组的长度,是5513个点,也即44100的八分之一。i和j表示t4和t8数组的长度,分别是44100的二分之一和四分之一,这三个数组定义了每个音符播放的时间。以44100点为一秒钟,5513个点就表示持续八分之一秒的长度。
%%----------------------------------------------------------------------
mod4=(t4.^4).*exp(-30*(t4.^0.5));
mod4=mod4*(1/max(mod4));
mod8=(t8.^4).*exp(-50*(t8.^0.5));
mod8=mod8*(1/max(mod8));
mod16=(t16.^4).*exp(-90*(t16.^0.5));
mod16=mod16*(1/max(mod16));
f0 = 2*146.8; % reference frequency
ScaleTable = [2/3 3/4 5/6 15/16 ...
1 9/8 5/4 4/3 3/2 5/3 9/5 15/8 ...
2 9/4 5/2 8/3 3 10/3 15/4 4 ...
1/2 9/16 5/8];
% 1/4 notes
do0f = mod4.*cos(2*pi*ScaleTable(21)*f0*t4);
这一步相当于是生成一个调幅的包络函数,我们可以单独把mod4画出来看一看。
最上面的图形就是mod4的函数图形,中间是cos(2*pi*ScaleTable(21)*f0*t4)的图形,下面是前两个的乘积,也就是do0f的图形。f0的频率是2*146.8 = 293.6Hz,查表是知道是D4,把他当做一个基准频率(我严重怀疑这个频率偏低了,听上去很不舒服,把f0改成880Hz就好听很多)。那么中间的图形是频率为ScaleTable(21)*f0 = 0.5*293.6 = 146.8Hz的正弦波。两个相乘得到了do0f的函数图形。do0f是1/4拍,它是一个22052个元素的数组,表示这个音符的频率是146.8Hz(this is weird!),播放0.5秒长度。
但是,这是一个频率单一的信号,且信号的幅度都一样,音符间的切换都很生硬。你可以动手试一试把每个音符前面相乘的mod4, mod8, mod16注释掉,然后再播放,你会发现真的很难听。如果大伯大妈每天晚上在广场上这个音乐跳舞,环保局的投诉电话肯定打爆了。所以,就用最上面的包络函数和正弦信号相乘,让每个音符柔和的飘过来,再缓缓的离开,这样的音乐就好听很多。
包络函数一般使用一个叫做ADSR的方法,就是把整个包络线分成四段,attack time, decay time, sustain level and relearse time. 每一段都根据需要有各自的函数和参数。
Synthesizer
%%----------------------------------------------------------------------
% 1/4 notes
do0f = mod4.*cos(2*pi*ScaleTable(21)*f0*t4);
re0f = mod4.*cos(2*pi*ScaleTable(22)*f0*t4);
mi0f = mod4.*cos(2*pi*ScaleTable(23)*f0*t4);
......
......
接下去的就是给每个音符定义一个数组,主要的参数就是节拍和频率。懂音乐的同学可以帮忙解释一下频率之间的关系,像我这样的乐盲就只能直接查表。
%%----------------------------------------------------------------------
% Melody by Schau_mal
part0 = [mi1f la0e la0e do1f mi1f ...
re1e re1s mi1s re1e do1e re1e do1e la0f ...
......
......
在接下去的一步是根据乐谱来生成一个大数组。对照着《最炫民族风》的乐谱,一一对应的写到数组里保存。这一步就相当于演唱者把他的声音录在了磁带里存放。下面的图是V1数组最开始的一段,就好比是磁带的一小段,或者说光盘的几圈。如果继续放大图像,看到的就是一个个点连成的折现,每44100个点需要一秒钟的时间播放。
%%----------------------------------------------------------------------
s = v1;
s = s/max(s);
sound(s,fs);
最后把数值归一化到[-1,1]区间。用Matlab自带的sound()函数播放。sound()函数需要两个参数,一个是音频信号的一个数组,另一个采样频率千万不要忘记。同一段信号用44100Hz播放是正常效果,用88200Hz就成了快进播放。
http://www.zhihu.com/question/20248007/answer/21230323