17、算法作曲的早期实验

算法作曲的早期实验

1. 引言

1967 年,我在数学中心担任程序员,这让我能自由探索可编程电子计算机的潜力。1962 年,我的第一篇出版物是关于人工智能的实验,是一个用于解决迷宫寻路、狼羊白菜过河等谜题的程序。此后,我进行了许多实验,主要集中在算法艺术领域,也涉及其他多个领域。

2. 词缀语法

1962 年,我和基斯·科斯特(Kees Koster)参加了埃弗特·贝斯(Evert Beth)组织的一个关于机器翻译的欧共体项目研讨会。在会上,我们阅读了诺姆·乔姆斯基(Noam Chomsky)的《句法结构》(Syntactic Structures),他在书中引入了“短语结构语法”来描述自然语言句子的结构。虽然大多数参与者对这些语法很陌生,但我和基斯熟悉它们,因为它们(至少在最简单的上下文无关语法形式下)与用于描述 Algol 60 人工语言“句子”结构的 BNF 语法相同。而且,使用这些语法来描述自然语言的想法对我们来说也是全新的。在乔姆斯基的观点中,这些语法的功能不仅是描述性的,更主要是生成性的。

研讨会要求参与者进行展示。我和基斯大胆地承担起用短语结构语法生成语法正确的英语句子的演示任务。基斯负责用 Electrologica X1 的机器代码编写程序,而我则提供语法。我不想让语法过于简单,为了让演示有说服力,它需要展示递归的可能性,比如在从句中看到的那样。显然,它还应该生成由名词短语(主语)、动词形式和可选的第二个名词短语(宾语)组成的基本句子,例如:
- the boy falls
- mice like grains

能生成这些句子的短语结构语法如下:

sent → np verb
sent → np verb np
np → the boy
np → mice
np → grains
verb → falls
verb → like

然而,这个语法产生了一些不符合语法规则的句子,如 “∗the boy like” 和 “∗the mice falls”,因为它们没有遵循主谓一致原则。为了解决这个问题,我们需要为每个语法数的组合复制句子规则:

sentsg → npsg verbsg
sentpl → nppl verbpl
sentsg → npsg verbsg npsg
sentsg → npsg verbsg nppl
sentpl → nppl verbpl npsg
sentpl → nppl verbpl nppl
npsg → the boy
nppl → mice
nppl → grains
verbsg → falls
verbpl → like

扩展后的语法可以生成我们最初的两个例句,但不会生成带星号的不符合语法的句子。不过,它仍然会生成一些奇怪的句子,如 “grains like the boy”,但只要句子语法正确就可以。但这些新增规则并不能提供完整的解决方案,我们仍然有一些不符合语法的生成结果,如 “∗the boy falls mice” 和 “∗grains like”。

除了数的一致,英语语法还需要考虑及物性、人称一致、代词格、时态等。考虑这些因素需要对规则进行多次复制,这会导致组合爆炸。我很快意识到,创建一个语法并不像我天真地认为的那么简单。如果每个基本规则都会衍生出几十个遵循一致性的规则,我预见到了三个问题:一是无法按时完成预定的演示;二是即使按时完成,也没有时间调试臃肿的语法;三是生成扩展语法会非常枯燥。

我曾考虑编写一个程序来扩展语法,它需要一组产生式规则模式作为输入,例如:

sentN → npN verbN
sentN → npN verbN npN′
N, N′ → sg
N, N′ → pl

后来我想到,可以将这个扩展步骤与生成过程融合,这样只需要一个(更复杂的)程序。我用了一个晚上编写了整个语法,使用 “词缀” 来表示上述作为下标出现的实体,它们用于协调解析树的不同分支。第二天,我把它整理得更整洁后交给了基斯,我原以为他会对这个额外的复杂性不满意,但实际上他很热情。演示进行得很顺利,但没有人对这种新颖的短语结构语法变体感兴趣。而基斯则继续使用词缀语法,将其用于解析自然语言句子,也是编程语言 CDL(编译器描述语言)的基础。

数学中心计算部门的负责人是阿德里安·范·维恩加登(Adriaan van Wijngaarden),我们都称他为范·维恩加登教授。他在报告印刷前会阅读数学中心的报告,也会看我们部门会议的报告,基斯会在会议上展示他的工作。我想知道这是否为他在描述 Algol 68 时使用的两级语法提供了灵感,这种语法在 1962 年的词缀语法中起到了类似的协调作用。据我所知,这是最早的两级语法,早于第一个范·维恩加登语法,而后者又早于唐纳德·克努斯(Donald E. Knuth)的属性语法。

虽然我没有继续研究词缀语法形式,但它们在协调解析树不同分支方面的实用性一直留在我的脑海中。我意识到,如果允许对词缀进行适度的计算,它们会变得更强大。这样,它们就可以像林登迈尔系统一样,用于描述有限近似的连续世代,例如图厄 - 摩尔斯序列(Thue–Morse sequence)0110100110010110… 或分形图案。最初生成英语句子的程序是用机器代码编写的,但后来我用 Algol 60 进行此类编程,充分利用递归,使用过程来模拟非终结符,参数充当词缀。我尝试用词缀语法来概括常见歌曲形式的模式,如 ABAC 和 AABA 形式。后来我意识到,如果在对词缀的计算中引入随机元素,我实际上可以同时生成旋律线。最初生成的旋律由一系列相同时长的音符组成,后来通过计算增强的词缀语法,旋律很快就有了节奏。

我看乐谱就像大多数荷兰人看巴斯克语写的书一样,能认出所有字母,但不知道它们组合起来听起来是什么样。有人(我不记得是谁了,可能是莱顿大学中央计算研究所的人)编写了一个程序,让 X1 进行计算,产生了一段可识别的旋律(只有一个声部)。当 X1 计算机执行程序时,在磁芯存储器中读取和存储数据会产生电磁脉冲,将晶体管收音机靠近存储模块(俗称 “垫子”)的机柜,就能听到这些脉冲声,这让我能听到自己实验生成的旋律。这个程序没有考虑潜在的和声,但旋律很容易演唱。

下面用 mermaid 流程图展示词缀语法生成句子的过程:

graph TD;
    A[输入规则模式] --> B[扩展规则];
    B --> C[生成句子];
    C --> D{是否符合语法};
    D -- 是 --> E[输出句子];
    D -- 否 --> B;
3. IFIP 竞赛

1967 年圣诞节假期,我在数学中心的图书馆翻阅计算机科学期刊时,看到了国际信息处理联合会(IFIP)举办的计算机作曲比赛的公告。该比赛与每四年举办一次的 IFIP 大会相关,参赛作品需以录音或弦乐四重奏乐谱的形式提交。我不会制作录音,但觉得创作一份乐谱是有可能的。然而,公告所在的期刊已经是几个月前的了,提交参赛作品的截止日期只剩下一个月,所以我当时觉得无法按时完成。

我一直对编写作曲程序的可能性感兴趣,不是那种现代风格的,如计算机创作的《伊利阿克组曲》(Illiac Suite),而是特定风格的,如格里高利圣咏或海顿(Haydn)的古典风格。由于缺乏音乐理论背景,这对我来说一直是一项无望的任务,但现在我因无法接受这个挑战而深感遗憾。第二天,遗憾感更加强烈,我想为什么不试试呢?我觉得不尝试会是更糟糕的失败,所以决定把这当作一个有趣的机会,无论结果如何。

于是,我开始编写一个包含音乐相关函数的小库,用于表示音调和音阶。圣诞节和新年假期结束后,阿姆斯特丹音乐图书馆一重新开放,我就一头扎进去,连续一周每天都借出尽可能多的音乐理论书籍。从一本关于海顿弦乐四重奏的书中,我了解到了一些关于其结构的有用信息,但总体来说,这让我非常失望。关于旋律,我没有找到任何有用的东西,只找到了一些关于旋律中连续音符之间音程出现频率的统计数据。有一些关于和声的书,我完全看不懂,更糟糕的是,这些书让我觉得其中隐藏着一些算法内容,但却难以捉摸。就在我开始绝望的时候,我找到了保罗·欣德米特(Paul Hindemith)为传统和声学生编写的一本练习册,它给出了关于构建和弦及和弦进行的非常具体的指导。我立刻开始将这些指导转化为代码。不幸的是,在读完两个简短章节中的第二章后,我不得不放弃,因为我再也看不懂那些说明。不过,至少我在和声部分有了一些成果(对于了解和弦进行的读者来说,这就解释了为什么和声方面除了无休止地重复 I–IV–V–I 和弦外,没有太多其他内容)。

接下来,我借出了一套海顿四重奏的乐谱并仔细研究。我从视觉上识别出了许多我在书中没有读到过的东西,比如两个中间声部(第二小提琴和中提琴)通常以相同的节奏演奏,与第一小提琴的旋律和大提琴的低音线不同。我已经为主要程序搭建了一个框架,我将其命名为 “compose”,它基于我之前对音乐词缀语法的实验。每次我看到一个可识别的模式,就会在 “compose” 中添加一段代码,这通常需要以参数的形式添加另一个词缀。这些参数在递归过程中传递,但会以修改后的形式出现,修改由随机因素和其他参数的值控制。这样,我实际上是在临时拼凑一种(可能是海顿特定风格的)音乐理论,而这些理论在图书馆的书中找不到。整个程序变得复杂而臃肿,没有明显的方法使其更模块化。

以下是当时发现的海顿弦乐四重奏声部特点表格:
|声部|特点|
| ---- | ---- |
|第一小提琴|承载旋律|
|第二小提琴|与中提琴节奏相同|
|中提琴|与第二小提琴节奏相同|
|大提琴|提供低音线|

到了开始测试程序的时候,由于程序太大,X1 无法运行,我在 Electrologica X8 上进行测试。离截止日期已经没剩多少时间了。很快我就发现,测试一个由随机数生成器控制、行为本质上是非确定性的程序是一场噩梦。没有调试工具可用,所有操作都是批量处理的,连续一周每次运行程序都会终止并给出通常不太有用的错误信息。找到并修复一个 bug 后,一个看似无害且无关的小改动可能会让问题再次出现。我自己也不知道所有这些代码组合起来应该产生什么样的效果,所以即使程序产生了一些东西,我也无法判断其中有多少符合我逐个添加代码片段时的意图,有多少是由错误代码导致的。

还有一个我之前没有过多考虑的问题。我的参赛作品必须以乐谱的形式提交。但除了穿孔纸带外,唯一的输出设备是行式打印机,它甚至不能打印小写字母,更不用说打印乐谱了。X1 连接了一台 Calcomp 绘图仪,并且可以通过纸带在 X8 和 X1 之间交换数据,但编写一个绘制乐谱的程序需要花费太多时间。我想出了一个简陋的解决方案,就是把行式打印机当作绘图仪使用,将音乐旋转 90 度打印,使五线谱垂直排列,每行为四个声部分别打印 “| | | | |”,或者通过字符叠印的方式,用 “| |O | | |” 表示中央 C 上方的 G 音(用于高音谱号的五线谱)。这些图案堆叠起来,模拟了五线谱的不间断线条。如果你把打印输出拿在离脸一两米远的地方,眯着眼睛看,只要你有丰富的想象力,就可以想象这代表了一份乐谱。

终于,弦乐四重奏的四个部分(快板 - 行板 - 小步舞曲 - 终曲)都完成了运行。不能再晚了,我抓起打印输出,第二天一大早就去了邮局,用挂号特快专递把所有东西寄到了伦敦的 IFIP 大会办公室。包裹重超过 5 公斤(约 12 磅),花了我一大笔钱。这确实是一个简陋的解决方案。(我当时没想到让数学中心报销费用。)我完全不知道我寄出去的东西演奏起来会是什么样子。它听起来会像音乐吗?我会知道答案吗?

不久后,我收到了评审团的一封信,礼貌地要求我尽快给他们一份人类可读的乐谱,最好是马上。我弄到了大量空白的乐谱纸(除了预先印好的五线谱线外是空白的),然后用细笔费力地将难以辨认的打印输出上的音乐手动抄写到纸上。我每天工作 16 个小时,花了一周时间才完成。我必须非常小心,不能出错,而且我的手不习惯于画音符。现在我知道,作曲家在画四分音符的实心符头时,满足于画成粗线条而不是椭圆形,那样可以节省很多时间。

算法作曲的早期实验

4. 后续处理与反思

在完成手动抄写乐谱后,我将这份人类可读的乐谱再次寄给了评审团。等待结果的过程既漫长又煎熬,我对自己的作品没有十足的信心,毕竟从创作过程来看,它存在诸多不完善的地方。

最终,我收到了评审团的反馈,我的弦乐四重奏获得了特别提及。这一结果让我既惊喜又意外,惊喜于自己的努力得到了一定的认可,意外于在如此多的不足下还能获得这样的评价。

回顾整个创作过程,我总结了以下几个方面的经验和教训:

4.1 语法构建的复杂性
  • 规则扩展的困境 :在构建词缀语法时,为了满足各种语法规则的一致性,如主谓一致、及物性、人称一致等,规则的扩展变得异常复杂,导致组合爆炸。这提醒我在设计算法时,要提前考虑规则的扩展性和复杂性,避免陷入过度扩展的困境。
  • 自动化处理的重要性 :手动扩展语法规则不仅耗时耗力,还容易出错。后来想到将扩展步骤与生成过程融合,使用一个更复杂的程序来处理,这是一个有效的解决思路。这表明在面对复杂的任务时,应充分利用计算机的自动化处理能力。

以下是创作过程中遇到的问题及解决思路列表:
|问题|解决思路|
| ---- | ---- |
|语法规则扩展复杂|将扩展步骤与生成过程融合,编写复杂程序处理|
|程序调试困难|虽无完美解决办法,但可加强对参数和随机因素的控制|
|输出乐谱困难|采用临时简陋方法,后续可考虑专业工具和算法|

4.2 程序调试的挑战
  • 非确定性程序的调试 :由于程序中引入了随机数生成器,导致程序行为具有非确定性,调试变得异常困难。在未来的编程中,需要更加谨慎地使用随机因素,或者在调试过程中对随机因素进行更好的控制和监测。
  • 缺乏调试工具 :当时没有合适的调试工具,所有操作都是批量处理,这大大增加了调试的难度。这让我认识到调试工具在软件开发中的重要性,在后续的项目中应优先考虑使用合适的调试工具。

下面用 mermaid 流程图展示程序调试的困境与应对思路:

graph TD;
    A[编写含随机因素程序] --> B[调试困难];
    B --> C{是否有调试工具};
    C -- 否 --> D[加强对随机因素控制];
    C -- 是 --> E[使用工具调试];
    D --> F[继续调试];
    E --> F;
    F --> G{是否解决问题};
    G -- 是 --> H[完成调试];
    G -- 否 --> D;
4.3 音乐理论与实践的结合
  • 理论知识的获取 :在创作过程中,我发现获取有用的音乐理论知识并不容易。很多关于音乐理论的书籍要么内容过于晦涩难懂,要么无法提供具体的创作指导。这让我明白,在跨领域的项目中,需要有针对性地获取相关知识,并且要学会将理论知识转化为实际的代码实现。
  • 实践中的创新 :由于在图书馆的书籍中找不到足够的音乐理论支持,我不得不通过观察海顿四重奏的乐谱,临时拼凑一种音乐理论。这种实践中的创新虽然让程序变得复杂臃肿,但也为创作带来了新的思路。这启示我在面对知识匮乏的情况时,要勇于从实践中探索和创新。
4.4 输出形式的难题
  • 输出设备的限制 :行式打印机无法满足打印乐谱的需求,这是一个很大的问题。在未来的项目中,应提前考虑输出设备的兼容性,选择合适的输出方式。
  • 临时解决方案的局限性 :采用将行式打印机当作绘图仪的简陋方法虽然解决了燃眉之急,但效果并不理想。这提醒我在遇到问题时,可以采用临时解决方案,但也要考虑其局限性,后续应寻找更完善的解决办法。

通过这次 IFIP 竞赛的经历,我深刻体会到了算法作曲的复杂性和挑战性。虽然在创作过程中遇到了诸多困难,但也积累了宝贵的经验。未来,我希望能够进一步改进算法,提高创作的质量和效率,探索更多算法作曲的可能性。例如,可以引入更先进的音乐理论和算法,优化词缀语法的设计,加强对程序的调试和控制,以及使用更专业的工具来生成高质量的乐谱和音频。

总之,这次实验是一次宝贵的学习机会,它让我在算法作曲的道路上迈出了重要的一步,也为未来的研究和创作奠定了基础。我相信,随着技术的不断发展和音乐理论的不断完善,算法作曲将会有更广阔的发展前景。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值