用Matlab演奏《错位时空》
这里是开头介绍
前两天在b站看到有个up用matlab演奏了一首《这世界有那么多人》,真的很想自己也做一个,所以就参考那个UP的代码,然后做出来了一个演奏《错位时空》版本的。在这里由衷的感谢那位UP。up的原视频链接在这里 我点我我 :) 然后我的版本我会在后面把代码贴出来,这里我讲解一下具体的原理以及怎么实现。
2024年11月6日修订
神奇的音乐
在初中的时候,我们就在物理中学过声音的三种要素,分别是响度,音高,和音色。在最近的之前我知道声音可以用波来描述,然后也知道响度可以用波的振幅来描述,音高可以用波的频率来描述,但是音色好像没有可以和波的参数对应的部分,是相位嘛,我只能说,很小的一部分是。
然后就在最近,了解一些音乐的知识加上这个小项目在做的过程中的理解,我终于明白了,其实音色对应的是波的包络,以及波分解到频域之后各个谐波的分量的大小和相位。下面来分别介绍下这三部分。
响度
响度自不用说,对应的就是音量的大小,或者说能量的大小,与波的振幅有关系,这个我相信大家在日常生活中都有体会。
音高
音高对应是波的频率,这个你去看看音乐中不同的调与频率的对应你就能知道。例如钢琴上的每个按键,就是不同的音高,为啥能和频率扯上关系,其实你去看看钢琴是怎么发出声音的就能很好的理解了。
钢琴的每个琴键按下的时候会敲击不同长度的琴弦震动(Vibrate),而不同长度的琴弦会以不同的频率震动(至于为什么不同长度的琴弦会以不同频率震动,去问你的大物老师吧 :)),也就是震动的速度不一样,所以会发出不同音高的声音。下面有一个视频,看完会有更直观的理解。
音色
这一部分你不理解为啥和各个谐波的分量大小及相位和时域的包络有关系,也没有什么关系,我们下面就先用单一频率的正弦波来演奏,照样是可以演奏的。然后通过改变时域包络和各个谐波的分量大小及相位,你就能听到不同的声音。
音符
音乐的最小单位是什么,当然是音符(note),然后一首音乐就是由不同的音符按照一定的时间顺序组合起来,然后演奏形成音乐。
那要演奏音乐,我们肯定得搞懂音乐的最小单位,音符是由什么组成的。想想你再音乐课上学的4分音符,8分音符,这两个有什么不同,时间长度不同,所以音符有时间的长短这一部分。再想想do、re、mi、fa、sol、la、si,这是什么,是不同的音高,上面说音高对应频率,所以不同的频率也是音符的组成部分之一。然后再想想还有什么,不同的乐器的音符声音是不一样的,具体就是音色不同,所以不同的音色也是它的组成部分。还有就是响度不同。
所以,我们总结出了音符的四部分,四部分要素。然后我们要演奏一首歌曲,这里我们仅使用一种乐器,就是能发出正弦波声音的matlab,然后音乐的响度一般都是整体调节,所以每个音符的响度都一样,音色也一样。所以需要我们控制就只有频率和长度。
简谱
我们可以看这个简谱,上面的67代表一分钟67拍(BPM, beat per minute),所以每拍就是60/67秒。然后上面的4/4(meter)代表,1/4音符(Quarter note或者叫四分音符,可以通过BPM算出每拍的时间)为一拍,也就是一个1/4音符是60/67秒,然后上面的4代表每一小节有四拍,也就是四个1/4音符。
所以总结一下,一拍(beat)表示时间长短(下面的"-“符号),可以用音符填到下面的“骨架”中。
4/4
67
| - - - - | - - - - | - - - - | - - - - |
| - - - - | - - - - |
一节(measure/bar)在简谱里面就是| 1 2 3 1 |这样,所以只要我们写出了产生音符的函数,然后就可以用这些音符来奏乐了。然后简谱中的每个数字代表一个音符,数字有一个下划线,代表音符持续时间缩小一半,两个下划线代表一半的一半,也就是一个下划线会将一个1/4音符,变成一个1/8音符,两个会变成1/16音符。
然后点在上面代表升一个八度,点在下面代表降一个八度,至于八度是什么,就是一组八度就是一组频率,升八度就是在原来的基础上频率乘2,降八度就是在原来的基础上除以2。
音乐中英文术语对照
中文 | 英文 |
---|---|
音符 | note |
拍 | beat |
节 | measure/bar |
节拍 | meter |
产生音符
代码就放到下面,希望你理解上面的知识后能够写出自己的代码,我的就直接放到下面吧。不要期待看懂,希望你能自己写出来。
gen_wave函数
可以生成音符,需要调用am函数,am函数可以调整时域包络,跟音色有关系。同时你也可以将 y = sin(tone * x) 中的sin换成square来解锁方波音色。
function y = gen_wave( tone, dura_scale_factor )
% parameter
fs = 44100;
% | one measure(or bar) |
beat = 60/67; % time of one beat,get it via BPM(1min/BPM)
duration = beat * dura_scale_factor; % calculate duration of a note
% genrate time date array from 0 to 2*pi*duration (in seconds)
% we can calculate how many date need from sample rate
x = linspace(0, 2 * pi * duration, floor(fs * duration));
y = sin(tone * x); % sin wave
y = am(length(y)) .* y; % apply envelope
% stop note
if tone < 1
y = y .* 0;
end
end
am函数
调整包络
function [yy] = am(xx_num)
xx = linspace(0, 1, xx_num);
y = [1 1 0.8 0.65 0.5 0.5 0.5 0.5 0.4 0.3 0.3 0.2 0.2 0.2 0.2 0.1 0.1 0.1 0.1 0.05 0];
x = linspace(0, 1, length(y));
yy = spline(x, y, xx);
end
主程序
演奏音乐《错位时空》。
% gen_wave(freqency,duration_factor)
clear
fs = 44100;
F = [261.63 293.66 329.63 349.23 392.00 440.00 493.88];
FL = F./2;
FH = F.*2;
y1 = gen_wave(F(6), 0.5);
y2 = gen_wave(FH(3), 0.5);
y3 = gen_wave(FH(3), 0.25);
y4 = gen_wave(FH(2), 0.25);
y5 = gen_wave(FH(3), 0.25);
y6 = gen_wave(FH(6), 0.25);
y7 = gen_wave(FH(5), 0.5);
y8 = gen_wave(FH(3), 0.5);
y9 = gen_wave(FH(5), 1);
y = [y1 y2 y3 y4 y5 y6 y7 y8 y9];
y1 = gen_wave(FH(6), 0.5);
y2 = gen_wave(FH(1), 0.25);
y3 = gen_wave(FH(6), 0.25);
y4 = gen_wave(FH(1), 0.5);
y5 = gen_wave(FH(2), 0.5);
y6 = gen_wave(FH(3), 0.5);
y7 = gen_wave(FH(2), 0.25);
y8 = gen_wave(FH(1), 0.25);
y9 = gen_wave(F(7), 1);
y = [y y1 y2 y3 y4 y5 y6 y7 y8 y9];
y1 = gen_wave(F(6), 0.5);
y2 = gen_wave(FH(3), 0.5);
y3 = gen_wave(FH(3), 0.25);
y4 = gen_wave(FH(2), 0.25);
y5 = gen_wave(FH(3), 0.25);
y6 = gen_wave(FH(6), 0.25);
y7 = gen_wave(FH(5), 0.5);
y8 = gen_wave(FH(3), 0.5);
y9 = gen_wave(FH(5), 1);
y = [y y1 y2 y3 y4 y5 y6 y7 y8 y9];
y1 = gen_wave(F(6), 1);
y2 = gen_wave(F(6), 0.25);
y3 = gen_wave(F(5), 0.25);
y4 = gen_wave(F(6), 0.25);
y5 = gen_wave(FH(1), 0.25);
y6 = gen_wave(FH(5), 1);
dot = gen_wave(FH(5), 0.5);
y7 = gen_wave(F(6), 0.25);
y8 = gen_wave(F(7), 0.25);
y = [y y1 y2 y3 y4 y5 y6 dot y7 y8];
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% 填不满
y1 = gen_wave(FH(1), 1);
y2 = gen_wave(FH(1), 0.25);
y3 = gen_wave(F(7), 0.25);
y4 = gen_wave(F(6), 0.25);
y5 = gen_wave(F(3), 0.25);
y6 = gen_wave(F(5), 0.5);
y7 = gen_wave(F(5), 0.25);
y8 = gen_wave(F(5), 0.25);
y9 = gen_wave(F(5), 0.5);
y10 = gen_wave(F(5), 0.25);
y11 = gen_wave(F(1), 0.25);
y = [y y1 y2 y3 y4 y5 y6 y7 y8 y9 y10 y11];
y1 = gen_wave(F(6), 1);
y2 = gen_wave(F(6), 0.25);
y3 = gen_wave(F(5), 0.25);
y4 = gen_wave(F(4), 0.25);
y5 = gen_wave(F(3), 0.25);
y6 = gen_wave(F(3), 0.5);
y7 = gen_wave(F(3), 0.25);
y8 = gen_wave(F(3), 0.25);
y9 = gen_wave(F(3), 0.5);
dot = gen_wave(F(3), 0.25);
y10 = gen_wave(F(3), 0.25);
y = [y y1 y2 y3 y4 y5 y6 y7 y8 y9 dot y10];
y1 = gen_wave(F(4), 0.25);
y2 = gen_wave(F(3), 0.25);
y3 = gen_wave(F(2), 0.25);
y4 = gen_wave(F(3), 0.25);
y5 = gen_wave(F(4), 0.25);
y6 = gen_wave(F(3), 0.25);
y7 = gen_wave(F(4), 0.25);
y8 = gen_wave(FH(2), 0.25);
y9 = gen_wave(F(7), 0.5);
y10 = gen_wave(FH(1), 0.25);
y11 = gen_wave(F(7), 0.25);
y12 = gen_wave(F(7), 0.5);
y13 = gen_wave(F(6), 0.25);
y14 = gen_wave(F(7), 0.25);
y = [y y1 y2 y3 y4 y5 y6 y7 y8 y9 y10 y11 y12 y13 y14];
y1 = gen_wave(FH(1), 0.5);
y2 = gen_wave(F(6), 0.25);
y3 = gen_wave(F(7), 0.25);
y4 = gen_wave(FH(1), 0.25);
y5 = gen_wave(F(7), 0.25);
y6 = gen_wave(F(6), 0.5);
y7 = gen_wave(F(7), 1);
y8 = gen_wave(F(7), 0.5);
y9 = gen_wave(F(6), 0.25);
y10 = gen_wave(F(7), 0.25);
y = [y y1 y2 y3 y4 y5 y6 y7 y8 y9 y10];
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% 像个自不量力
y1 = gen_wave(FH(1), 1);
y2 = gen_wave(FH(1), 0.25);
y3 = gen_wave(F(7), 0.25);
y4 = gen_wave(F(6), 0.25);
y5 = gen_wave(F(3), 0.25);
y6 = gen_wave(F(5), 0.5);
y7 = gen_wave(F(5), 0.25);
y8 = gen_wave(F(5), 0.25);
y9 = gen_wave(F(5), 0.5);
y10 = gen_wave(F(5), 0.25);
y11 = gen_wave(F(1), 0.25);
y = [y y1 y2 y3 y4 y5 y6 y7 y8 y9 y10 y11];
y1 = gen_wave(F(6), 1);
y2 = gen_wave(F(6), 0.25);
y3 = gen_wave(F(5), 0.25);
y4 = gen_wave(F(4), 0.25);
y5 = gen_wave(F(3), 0.25);
y6 = gen_wave(F(3), 0.5);
y7 = gen_wave(F(3), 0.25);
y8 = gen_wave(F(3), 0.25);
y9 = gen_wave(F(3), 0.5);
dot = gen_wave(F(3), 0.25);
y10 = gen_wave(F(3), 0.25);
y = [y y1 y2 y3 y4 y5 y6 y7 y8 y9 dot y10];
y1 = gen_wave(F(4), 0.25);
y2 = gen_wave(F(3), 0.25);
y3 = gen_wave(F(2), 0.25);
y4 = gen_wave(F(3), 0.25);
y5 = gen_wave(F(4), 0.25);
y6 = gen_wave(F(3), 0.25);
y7 = gen_wave(F(4), 0.25);
y8 = gen_wave(FH(2), 0.25);
y9 = gen_wave(F(7), 0.5);
y10 = gen_wave(FH(1), 0.25);
y11 = gen_wave(F(7), 0.25);
y12 = gen_wave(F(7), 1);
y = [y y1 y2 y3 y4 y5 y6 y7 y8 y9 y10 y11 y12];
y1 = gen_wave(F(6), 1);
y2 = gen_wave(FH(2), 0.5);
y3 = gen_wave(F(6), 0.5);
y4 = gen_wave(F(7), 2);
y = [y y1 y2 y3 y4];
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%% 我吹过
sound(y, fs);