信号与系统设计性实验之
MATLAB 音乐制作
实验报告
吕祺
电信提高 0901 班
U200913911
摘要:
本文共有四大部分:第一部分,简单的音乐合成;第二部分,用傅里叶变换
分析音乐;第三部分,基于傅里叶级数的音乐合成;第四部分,加包络除噪声。
由潜入深,一步一步分析了用 MATLAB 进行音乐合成的过程。通过本实验达到了
加深对傅里叶级数和傅里叶分析的理解,熟悉对 MATLAB 基本使用的目标,提高了
对一维信号与线性系统的直观认识。
一、合成简单音乐
A、 频率选择
音符对应的频率表如下:
音符
频率(HZ) 简谱码(T 值)
音符
频率(HZ) 简谱码(T 值)
低 1
DO
#1
DO#
RE
低 2
#2 RE#
低 3 M
低 4 FA
# 4 FA#
低 5 SO
# 5 SO#
低 6 LA
# 6
低 7 SI
中 1 DO
# 1 DO#
中 2 RE
# 2 RE#
中 3 M
中 4 FA
262
277
294
311
330
349
370
392
415
440
466
494
523
554
587
622
659
698
63628
63731
63835
63928
64021
64103
64185
64260
64331
64400
64463
64524
64580
64633
64684
64732
64777
64820
# 4 FA#
中 5 SO
# 5 SO#
中 6 LA
# 6
中 7 SI
高 1 DO
# 1 DO#
高 2 RE
# 2 RE#
高 3 M
高 4 FA
# 4 FA#
高 5 SO
# 5 SO#
高 6 LA
# 6
高 7 SI
740
784
831
880
932
988
1046
1109
1175
1245
1318
1397
1480
1568
1661
1760
1865
1967
64860
64898
64934
64968
64994
65030
65058
65085
65110
65134
65157
65178
65198
65217
65235
65252
65268
65283
经过观察,发现相邻音符之间频率成线性倍数关系,相邻两频率之商为 2^(1/12),
少数为 2^(2/12)。
通过计算,写出分段函数如下:
if real(a)<0
a1=-2*real(a)-14;
elseif
real(a)<=3&&real(a)~=0
a1=2*real(a);
elseif real(a)<7&&real(a)~=0
a1=2*real(a)-1;
elseif real(a)>=7
a1=2*real(a)-1;
end
通过 cos(2*pi*494*2^(a1/12)*[1:10]'*(1/44100))即可对应正确的频率,这样
省去了加谐波时的不便,可以直接在幅度上修改成求和的形式,如下所示:
fn=horzcat(fn,[1 0.3094 0.0762 0.0912 0.0164 0.0232 0.0312 0.0223 0.0131
0.0112]*cos(2*pi*494*2^(a1/12)*[1:10]'*(1/44100)
*([0:lengt*(1+imag(a))].*exp(-0.2:-0.01/(lengt*(1+imag(a))):-0.21))));
%horzcat 做连接运算,将前后音乐数据放在一个数组中
B、 播放长度控制
播放长度与数据长度成正比,通过 lengt 设置每一节拍长度如下:
if nargin<=1
lengt=5000;
else
lengt=lengt;
end%可以通过输入参数控制播放长度,不自行设置时也可使用默认值
节拍长度由音乐文件的虚部控制,可以减少数据量,若有多个节拍或半拍等,通
过虚部进行加减即可。如下为《我和你》的音乐数据:
[3 5 1+i 0 2
3
-6+i 0 1 2 3 5 2+3*i 0 0 0
3 5 1+i 0 2 3 -6+i 0 2 -5 2 3 1+3*i 0 0 0];
6+i 0 5+i 0 6+i 0 1+i 0 2 6 3 5 2+3*i 0 0 0
3 5 1+i 0 2 3 -6+i 0 2 -5 2 3 1+3i 0 0 0 ];
当节拍为 1/4,1/8,3/2 等非整数倍时,可以将虚部设置成相应的值,如下为《同
一首歌》的音乐数据,可以看到使用虚部控制可以很方便地改变持续时长:
2
2 3+0.5i 4-0.5i 3 1 2+i 1
[-5+i 1
-6 1+3i -5+i 1
3
3-0.5i 4-0.5i 5 1
4+0.5i 3-0.5i 5 2-0.5i 3-0.5i 3-0.5i 2+2.5i 3+i 5
7
7+0.5i 6-0.5i 6+i
5 5-0.5i 6-0.5i 7 6-0.5i 3+3i];
二、用傅里叶变换分析音乐
通过查阅 matlab 关于 fft 函数的相关帮助,写出相应函数代码,这一步比较简
单。需要注意的是:
1、从公式上看,matlab 的 fft 序号是从 1 到 N,但是绝大多数教材上是从 0 到
N-1。
2、Y=fft(x)之后,这个 Y 是一个复数,它的模值应该除以(length(x)/2),
才能得到各个频率信号实际幅值。作 FFT 分析时,幅值大小与 FFT 选择点数有关,
但不影响分析结果。
3、根据需要选点作图,fft 变换的结果是关于 y 轴对称的,且在频域可能扩展
到很大的一个值,这样不利于较小频率的直观分析,此时应选择变换结果前面一
半的相应项。
y=fft(fn);y1=abs(y);
dnum=length(y1);
f=(0:dnum-1).*freq/dnum;
figure,plot(f(1:dnum/20),y1(1:dnum/20)/length(fn));
三、基于傅里叶级数的音乐合成
通过读取音乐文件,分析频谱后将文件分成 8 份,用 wavwrite 保存,然后选择
其中一个分析谐波。
用 fft 变换分析频域特性,然后对谐波的幅值进行记录,将基频设为大小“1”,
求出其他谐频与基频的比例,得到幅值数组[1 0.3094 0.0762 0.0912 0.0164
0.0232 0.0312 0.0223 0.0131 0.0112]。取幅值代码如下:
[fn,freq]=wavread('0.wav')
fn=repmat(fn,20,1);
y=fft(fn);y1=abs(y);
dnum=length(y1);
f=(0:dnum-1).*freq/dnum;
figure,plot(f(1:dnum/20),y1(1:dnum/20));
[x,y]=ginput(12);
四、加包络,除噪声
谐频加上后,音乐基本制作成功,声音更加动听,与吉他的音色有几分接近。可
是由于是函数模拟的原因,没有实际乐器演奏的淡入淡出的效果。通过查阅资料
发现可以使用包络来调节。于是我经过多次尝试,在原来函数的基础上加上了自
然对数 exp(0:-1/(lengt*(1+imag(a))):-1)),音色变得非常好,没有了换音时
的杂音。
附完整程序如下:
function [t]=play(m,lengt)
freq=44100;
if nargin<=1
lengt=5000;
else
lengt=lengt;
end
t=0;fn=0;
%m=textread('youandme.txt');
m=[3 5 1+i 0 2
3
-6+i 0 1 2 3 5 2+3*i 0 0 0
3 5 1+i 0 2 3 -6+i 0 2 -5 2 3 1+3*i 0 0 0
6+i 0 5+i 0 6+i 0 1+i 0 2 6 3 5 2+3*i 0 0 0
3 5 1+i 0 2 3 -6+i 0 2 -5 2 3 1+3i 0 0 0 ];
% m=[-5+i 1
2 3+0.5i 4-0.5i 3 1 2+i 1
-6 1+3i -5+i 1
2
3 3-0.5i 4-0.5i
5 1
%
4+0.5i 3-0.5i 5 2-0.5i 3-0.5i 3-0.5i 2+2.5i 3+i 5
7
7+0.5i 6-0.5i 6+i 5 5-0.5i
6-0.5i 7 6-0.5i 3+3i]; %5-0.5i];% 3+3i]
s=size(m);
if s(1)==1
error('not a song');
end
for i1=1:(s(1))
for i2=1:(s(2))
a=m(i1,i2);
while a~=0
if real(a)<0
a1=-2*real(a)-14;
elseif
real(a)<=3&&real(a)~=0
a1=2*real(a);
elseif real(a)<7&&real(a)~=0
a1=2*real(a)-1;
elseif real(a)>=7
a1=2*real(a)-1;
end
fn=horzcat(fn,[1 0.3094 0.0762 0.0912 0.0164 0.0232 0.0312 0.0223
0.0131 0.0112]...
*cos(2*pi*494*2^(a1/12)*[1:10]'*(1/44100)*[0:lengt*(1+imag(a))])...
.*exp(0:-1/(lengt*(1+imag(a))):-1));
drawnow
plot(1:length(fn),fn);
break
end
end
end
wavplay(fn,freq);
y=fft(fn);y1=abs(y);
dnum=length(y1);
f=(0:dnum-1).*freq/dnum;
figure,plot(f(1:dnum/20),y1(1:dnum/20));