分享个国外程序员做的项目:让一个5分钟的曲子,不重样播放500多天......
程序员要是喜欢上一个东西,指不定会搞出什么“疯狂”事情来。
今天要说的,是特别喜欢一首曲子的程序员贝特(Alex Bainter)。
这首曲子,名为aisatsana,来自电子乐大师Aphex Twin,只有5分多钟。
让他很苦恼的是,单曲循环听着容易腻歪,他想让这个曲子能一直不重样地播放下去。
然后,他选择了马尔可夫链……
现在这首曲子已经能够不间断播放451天不重样……(听歌地址在文末)
贝特把完成这一“壮举”的过程写成了文章,分享了出来,而且,代码已经开源。
在Hacker News很快就获得了400多热度,200多条评论。
不少人评论称,这样听歌真的很爽, 实现起来很简单,但效果很好!
它到底是怎么做的呢?我们一起来看看。
想要曲子不重样地播放下去,首要的任务就是要理解曲子的结构。
一般情况下,播放曲子的速度可以BPM(beats per minute)来衡量。
aisatsana这首曲子很简单,共有102BPM。如果从第一个音符开始计数的,每16个节拍包含一系列音符,他称之为乐句(phrase),整首曲子中,一共有32个乐句。
想要创建一个能够不间断、不重复播放aisatsana的系统,算法需要满足以下两个要求:
第一个要求,相对来说非常简单,只是计时。第二个要求,是最棘手的部分。
一种简单的策略是,编写一个程序,粗暴地把32个乐句拆分开,一个乐句之后,随机播放另一个。
这种方式,虽然相互衔接上没有问题,听起来也比单曲循环更加灵活有趣,但本质上也是重复的。
只要熟悉了这32个乐句,曲子依旧会无聊。
怎么办?答案只有一个:
创造一个系统,来生成听起很与原来乐句很相似的新乐句。
最直接的方法,就是深度学习技术了。但只有32个乐句的样本……很显然不太现实。
最后,贝特就把目光转向了马尔可夫链这个“老方法”。
马尔可夫链,能够记录一组状态中,从一个状态转移到另一个状态的概率。
举例来说,你生活中只去3个地方:家、公司和商店,也就是说,你要么在家里,要么在公司,要么在商店。
想要的知道你接下来要去哪?首先就是记录你一段时间内去这三个地方情况,根据记录数据来分析相关的可能性。
比如说,当你在家的时候,你有80%的可能性去公司,你在公司的时候,回家和去商店概率都是50%,如果你在商店,接下来回家的可能性就有95%。
以上,就是创建一个马尔可夫链的所需要的一切:状态,以及从一个状态到另一个状态的概率。
如果把它应用到音乐上,该怎么做?
先来看一张图:
图中展示的是两个乐句,每个有4个节拍,每个音符旁边,是音符的名字,最上面是节拍。播放这些乐句的顺序,是从左到右依次进行。
然后,把两个乐句放到马尔可夫链中,状态命名为节拍与音符的组合,比如1A就是在第一个节拍的时候演奏A音符。
除了这些,还需要有开始和结束的状态,来表示乐句的开始和结束。用箭头来表示一个状态到另一个状态的转变。
在上图中,第一个乐句就是开始→1A→2F→3A→4F→结束。第二个乐句是开始→1E→2C→3A→4C→结束。
这里面,一共有9个状态,分别是开始、1A、1E、2F、2C、3A、4F、4C和结束。从一个状态到另一个状态的概率是:
开始→1A:50%
开始→1E:50%
1A→2F:100%
1E→2C:100%
2F→3A:100%
2C→3A:100%
3A→4F:50%
3A→4C:50%
4F→结束:100%
4C→结束:100%
这样,就建立了一个马尔可夫链。从开始到结束,所有可能的路径,可以生成4个乐句:
开始→1A→2F→3A→4F→结束(原始乐句1)
开始→1A→2F→3A→4C→结束(新乐句)
开始→1E→2C→3A→4C→结束(原始乐句2)
开始→1E→2C→3A→4F→结束(新乐句)
就这样,马尔科夫链就生成了两个新的乐句!因为是通过同一个链构建的,听起来也会非常相似。
在这个例子中,只是用了2个乐句,每个乐句中4个节拍。而aisatsana中有32个乐句,每个乐句16个节拍。
按照这个逻辑,贝特最后生成了400多万个独特的乐句,可以播放超过451天,而且所有的乐句都不会重复……
整个实现代码,贝特已经开源了,项目地址:
https://github.com/generative-music/pieces-alex-bainter/blob/master/packages/piece-aisatsana/src/piece.js
如果你有兴趣,可以自己上手试试~
最后,附上生成的aisatsana地址: