美文网首页
又用Mathematica写了一段卡农

又用Mathematica写了一段卡农

作者: 特斯拉小怪兽 | 来源:发表于2017-12-15 21:46 被阅读0次

    你能在这里听到它

    用数学软件演奏音乐早已不是什么新鲜事,很早就有人用Matlab弹奏卡农或是最炫民族风,最近我知道还有人用无理数生成一段音乐,很有趣。而我选择用Mathematica弹奏卡农是因为卡农这种谱曲方式很奇妙,体现在程序上也会是简洁而优美的。可能有人还不了解卡农是什么,卡农不是指某一首曲子,而是一种谱曲方式,它把几段相同的旋律在不同的时刻依次展开,交错的旋律又能相互配合,形成一首完整的卡农。这用Mathematica演奏的卡农就是大家最耳熟的“帕赫贝尔的卡农”,你可以在它的乐谱中一窥其中的奇妙。

    我们关注的重点是,这首卡农有三个声部(在乐谱上对应着前三排)和一个背景旋律,但实际上三个声部演奏的旋律是相同的,所以我们只需要输入一个声部的谱子,然后让三个声部在不同的时间进入就可以了。

    手动输入乐谱——失败

    起初我在网上找到的是巴赫的手稿,当我把一个声部的旋律都输入进去后,最终的效果并不是很理想,我想这个谱子更适合音乐会弹奏,需要乐手控制不同声部的音量与节奏,但这在Mathematica上很难实现。

    从Matlab到Mathematica

    后来我在网上发现了一个用Matlab弹卡农的程序,听过之后,感觉他的谱子更适合用软件演奏。就决定把Matlab代码翻译到Mathematica上。首先我们分析一下Matlab的代码

    Matlab代码

    Matlab中的代码一共分三部分

    1. 告诉电脑如何弹奏音符

    2. 输入一个声部的旋律

    3. 组合三个声部

    1.告诉电脑如何弹奏音符

    我们需要告诉电脑

    i.音符名称(do,re,mi,fa...)

    ii.音符持续的时间(音符时值t)

    iii.音调(频率f)

    所以我们输入的应该是如下格式的信息:

                                                音符名称=sin(2πft)

    看看这在Matlab中是如何做到的

    % 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);

    % 1/8 notes

    fa0e = mod8.*cos(2*pi*ScaleTable(1)*f0*t8);

    so0e = mod8.*cos(2*pi*ScaleTable(2)*f0*t8);

    la0e = mod8.*cos(2*pi*ScaleTable(3)*f0*t8);

    % 1/16 notes

    fa0s = mod16.*cos(2*pi*ScaleTable(1)*f0*t16);

    so0s = mod16.*cos(2*pi*ScaleTable(2)*f0*t16);

    la0s = mod16.*cos(2*pi*ScaleTable(3)*f0*t16);

    初看感觉很复杂,但其实它的形式和上面的公式是相同的

    i.do0f,re0f,mi0f...代表音符名称

    ii.ScaleTable(\space)*f0代表频率f,不同音符的ScaleTable()不同

    iii.t4,t8,t16代表持续时间$t$,更具体的,t4=0.5s,t8=0.25s,t16=0.125s

    但我们还发现他多出一个mod函数,这其实是一个修正函数(modify),乘上他之后可以让波形变得更柔和,有渐入渐出的效果。

    未经mod修正过的波形 经mod修正过的波形

    mod后面的数字代表对应音符的持续时间。比如mod4要和t4相乘。

    2. 输入一个声部的音符

    这一步就很好理解了,这首卡农背景旋律(Base Melody)由大提琴演奏,主旋律(Long Melody )由三把小提琴演奏,我们分别命名cello和violin

    % Base Melody

    cello = [do1f do1f so0f so0f la0f la0f mi0f mi0f... fa0f fa0f do0f do0f fa0f fa0f so0f so0f];......

    % Long Melody

    violin = [mi2f mi2f re2f re2f do2f do2f ti1f ti1f... la1f la1f so1f so1f la1f la1f ti1f ti1f ......

    3.组合三个声部

    正如我前面说的,我们只需要在不同的时间点加入相应的旋律,就像下面这样(blkblock代表空白音符)

    % violin1

    v1 = [blkblock violin blkblock blkblock];

    % violin2

    v2 = [blkblock blkblock violin blkblock];

    % violin3

    v3 = [blkblock blkblock blkblock violin];

    % Combination

    s = c1+v1+v2+v3;

    sound(s,fs);

    这样就得到了一段能在Matlab上演奏卡农的代码,接下来就要把它翻译到Mathematica上。

    翻译到Mathematica

    我们当然要借助Mathematica来翻译,思路跟Matlab上的思路一样

    1.建立音符名称和声音的关联(Association)

    最终的关联应该像下面这样。我给这个关联起名叫“asswecan”,意思是用这个关联我们能做到「音符名称到声音的转换」,记住这个名字,我们之后会用到。

    那么要如何生成这个关联呢?首先我们需要生成声音,这里用到Play函数:

    一个频率为440Hz的“中音A”的波形

    根据Matlab代码中的音符信息「ScaleTable(),f0,t4,t8,t16」改变Play函数中的参数来发出不同的音调,替换的关系是这样的:

    有了这个思路,就可以用StringReplace函数进行替换了。

    2.输入一个声部的旋律

    Matlab中的旋律代码有许多多余的字符,需要我们处理一下

    i.输入Matlab代码中的旋律

    cellonoteinfo =

    "do1f do1f so0f so0f la0f%% la0f mi0f mi0f...fa0f%% fa0f do0f do0f fa0f \n

    fa0f so0f so0f";

    这里面有很多我们不希望得到的 ... %% 空格回车这样的字符,需要把他们去掉。

    ii.定义去掉无用字符的函数

    infotonote[info_] :=

    StringPartition[StringJoin@StringCases[info, _?LetterQ | _?DigitQ],4];

    iii.得到适用于Mathematica的旋律

    In[1]:= cellonote = infotonote[cellonoteinfo]

    Out[1]:= {"do1f", "do1f", "so0f", "so0f", "la0f", "la0f", "mi0f", "mi0f", \

    "fa0f", "fa0f", "do0f", "do0f", "fa0f", "fa0f", "so0f", "so0f"}

    这样我们就得到了旋律中的音符名称列表

    3.用声音替换旋律中的音符名称

    还记得那个"asswecan"吗?它可以把音符名称替换成声音。用“asswecan”关联完成对几段旋律的转换,并规定好他们开始弹奏的时间,比如vio1末尾的8就代表第一把小提琴在第8秒才开始弹奏。

    cello = Sound[Flatten@Table[Replace[cellonote, notetosoundass, 1], 23], {0}];

    vio1 = Sound[Replace[violinnote, notetosoundass, 1], {8}];

    vio2 = Sound[Replace[violinnote, notetosoundass, 1], {16}];

    vio3 = Sound[Replace[violinnote, notetosoundass, 1], {24}];

    4.最后把他们组合起来

    As we can see, 我们完成了!

    你可以在B站听到这段旋律

    你也可以前往Wolfram社区下载Matlab及Mathematica代码文件

    相关文章

      网友评论

          本文标题:又用Mathematica写了一段卡农

          本文链接:https://www.haomeiwen.com/subject/znxjwxtx.html