一、原理

我们都知道声音其实是一种波,所以要使用Matlab生成一段音乐其重点就是生成该声音的。首先我们来看最基本的波是怎么使用Matlab画出来的。使用Matlab画出最简单的正弦波。

1
2
3
4
5
% 最基本的正弦波
x = linspace(0, 2*pi, 100); % 在(0,200)之间取100个点
y = sin(x);

plot(x, y)

可以看到,该波的波形十分的平滑,这是因为我们在x = linspace(0, 2*pi, 100)中选择取100个点。其中点的个数100在一段完整的声音中,叫做采样率(在一秒钟的声音中,一共取了多少个点)。在Matlab中默认的采样率是8192,采样范围是$1000Hz \leq FS \leq 384000Hz$,也就是说采样率必须在此范围内,Matlab才能播放声音。

1
2
3
4
5
6
% 默认采样率
Fs = 8192;
x = linspace(0, 2*pi, Fs);
y = sin(x);
plot(x, y);
title('采样率为8192Hz')

该代码表示播放一个周期的声音,之后我们使用sound()函数,播放其声音。戴上耳机,仔细听会有两声响。(据说这应该听不到,是次声波来着,但我确实听到了/(ㄒoㄒ)/~~)

1
2
3
4
5
6
7
% 默认采样率
Fs = 8192;
x = linspace(0, 2*pi, Fs);
y = sin(x);
plot(x, y);
title('采样率为8192Hz')
sound(y, Fs);

我们根据乐理知识,有一个标准音高A=440,所以我们改变代码使其发出标准音高声。可以看到这时候的周期特别的多。

1
2
3
4
5
6
7
% 默认采样率
Fs = 8192;
x = linspace(0, 2*pi, Fs);
y = sin(440*x);
plot(x, y);
title('采样率为8192Hz')
sound(y, Fs);

二、探究音高的影响因素

上述,我们已经成功使用Matlab发声,那么影响音高的因素有哪些呢?我们从两个因素考虑:一个是声音的采样率,另一个是声音的频率。使用控制变量法研究以上两个因素。

1
2
3
4
5
6
7
8
9
10
11
12
13
% 影响音高的因素
% 控制声音频率不变,只改变采样率
Fs1 = 8192;
Fs2 = 20000;

x1 = linspace(0, 2*pi, Fs1);
x2 = linspace(0, 2*pi, Fs2);
y1 = sin(440 * x1);
y2 = sin(440 * x2);

sound(y1, Fs1);
%请先听完第一个声音再打开y2的注释,并注释y1。
%sound(y2, Fs2);

经过测试,两个声音没有任何区别,如此可以说明,声音的采样率不会对音高产生影响。

1
2
3
4
5
6
7
8
9
10
% 影响音高的因素
% 控制采样率不变,只改变声音的频率
Fz = 8192;
x = linspace(0, 2 * pi, Fz);
y1 = sin(440 * x);
y2 = sin(880 * x);

sound(y1, Fz);
%请先听完第一个声音再打开y2的注释,并注释y1。
% sound(y2, Fz);

经过测试,y2的音高较y1有明显提高,综上所述,影响声音音高的只有频率因素。

三、十二平均律

已知声音的音高只与其频率有关,那么如何才能通过调节频率产生一段优美的音乐呢?下面是来自wiki上的十二平均律表,该表计算的是一个八度的频率表,对于一个八度里的声音我们将其分成12份,这里采用以440Hz为主音乘以$2 ^ {\frac{x}{12}}$,依次得到下个一个音。会发现从440-880正好跨越一个八度。

下面的十二平均律表最左侧音程名称中带有$# ^1$的表示升,带有$b^1$的表示降。不带升号和降号的可以看作是钢琴的白键,带升号和降号的可以看作是钢琴的黑键。这张表我们不需要全部记下来,我们只需要从小三度(C)开始记即可(跳过黑键#),因为C就是八度中的第一个dou音。

1
2
3
4
5
6
7
8
常用频率
C: 523.2511
D: 587.3295
E: 659.2551
F: 698.4564
G: 783.9908
A: 880
B: 987.7666

如此我们就可以得到一个八度了

1
2
3
4
5
6
7
8
9
10
11
12
13
% 八度
Fz = 8192;
x = linspace(0, 2 * pi, Fz);
freqs = [523.2511, 587.3295, 659.2551, 698.4564, 783.9908, 880, 987.7666];
y1 = sin(freqs(1) * x);
y2 = sin(freqs(2) * x);
y3 = sin(freqs(3) * x);
y4 = sin(freqs(4) * x);
y5 = sin(freqs(5) * x);
y6 = sin(freqs(6) * x);
y7 = sin(freqs(7) * x);
y = [y1, y2, y3, y4, y5, y6, y7]
sound(y, Fz);

四、小星星

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Fs = 8192;
x = linspace(0, 2 * pi, Fs);

freqs = [523.2511, 587.3295, 659.2551, 698.4564, 783.9908, 880, 987.7666];
y1 = sin(freqs(1) * x);
y2 = sin(freqs(1) * x);
y3 = sin(freqs(5) * x);
y4 = sin(freqs(5) * x);
y5 = sin(freqs(6) * x);
y6 = sin(freqs(6) * x);
y7 = sin(freqs(5) * x);
y8 = sin(freqs(5) * x);

y = [y1, y2, y3, y4, y5, y6, y7, y8];
sound(y, Fs);

播放完成后会发现,声音的间隔不是很清楚,我们先看一下y的波形。

如何解决这一问题呢?我们可以通过压缩正弦波解决

1
2
3
4
5
6
7
8
9
10
11
12
13
14
% 解决声音连在一起的现象
Fz = 8192;
x1 = linspace(0, 2*pi, 8192);
y1 = sin(x1);

x2 = [0 2 * pi];
y2 = [1, 0];


subplot(1, 2, 1)
plot(x1, y1);

subplot(1, 2, 2)
plot(x2, y2); % y = 1- x / (2*pi)

我们对以上小星星的程序进行改写所有正弦波都乘以$1 - \frac{x}{2\pi}$

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Fs = 8192;
x = linspace(0, 2 * pi, Fs);
x2 =linspace(0, 2 * pi * 2, Fs * 2);
freqs = [523.2511, 587.3295, 659.2551, 698.4564, 783.9908, 880, 987.7666];
y1 = sin(freqs(1) * x) .* (1 - x / ( 2 * pi));
y2 = sin(freqs(1) * x) .* (1 - x / ( 2 * pi));
y3 = sin(freqs(5) * x) .* (1 - x / ( 2 * pi));
y4 = sin(freqs(5) * x) .* (1 - x / ( 2 * pi));
y5 = sin(freqs(6) * x) .* (1 - x / ( 2 * pi));
y6 = sin(freqs(6) * x) .* (1 - x / ( 2 * pi));
y7 = sin(freqs(5) * x2);

y = [y1, y2, y3, y4, y5, y6, y7];
plot(y);
sound(y, Fs);

最后一个音听着有些不合群

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Fs = 8192;
x = linspace(0, 2 * pi, Fs);
x2 =linspace(0, 2 * pi * 2, Fs * 2);
freqs = [523.2511, 587.3295, 659.2551, 698.4564, 783.9908, 880, 987.7666];
y1 = sin(freqs(1) * x) .* (1 - x / ( 2 * pi));
y2 = sin(freqs(1) * x) .* (1 - x / ( 2 * pi));
y3 = sin(freqs(5) * x) .* (1 - x / ( 2 * pi));
y4 = sin(freqs(5) * x) .* (1 - x / ( 2 * pi));
y5 = sin(freqs(6) * x) .* (1 - x / ( 2 * pi));
y6 = sin(freqs(6) * x) .* (1 - x / ( 2 * pi));
y7 = sin(freqs(5) * x2) .* (1 - x2 / ( 4 * pi));

y = [y1, y2, y3, y4, y5, y6, y7];
plot(y);
% sound(y, Fs);

五、封装函数

为了简便我们以后完成更长的音乐,可以选择将主要部分封装成函数以便调用。

1
2
3
4
5
6
7
8
function y = music(tone, rythm)
%UNTITLED5 此处提供此函数的摘要
% 此处提供详细说明
Fs = 8192;
freqs = [523.2511, 587.3295, 659.2551, 698.4564, 783.9908, 880, 987.7666];
x = linspace(0, 2 * pi *rythm, floor(Fs * rythm));
y = sin(freqs(tone) * x) .* (1 - x / (rythm * 2 * pi));
end
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Fs = 8192;

y1 = music(1, 0.5);
y2 = music(1, 0.5);
y3 = music(5, 0.5);
y4 = music(5, 0.5);
y5 = music(6, 0.5);
y6 = music(6, 0.5);
y7 = music(5, 1);
y = [y1, y2, y3, y4, y5, y6, y7];

y1 = music(4, 0.5);
y2 = music(4, 0.5);
y3 = music(3, 0.5);
y4 = music(3, 0.5);
y5 = music(2, 0.5);
y6 = music(2, 0.5);
y7 = music(1, 1);
y = [y, y1, y2, y3, y4, y5, y6, y7];
sound(y, Fs)