实时生成音乐符号

作者:Kevin C. Baird

来自各种背景的作曲家一直对在音乐表演中拥有更大的自由度感兴趣。本文描述了该传统中的另一次尝试。

卡尔海因茨·施托克豪森在他的钢琴曲中,允许表演者随意排列预先写好的材料。厄尔·布朗允许指挥在他的可用形式作品中混合搭配材料,在他的图形乐谱中,他基本上允许表演者即兴创作乐谱中的图像启发他们演奏的任何内容。即兴创作对于爵士乐来说显然也至关重要。

在所有这些例子中,作曲家都只将这种增加的自由度给予了表演者或指挥,而不是观众。我的博士论文作品无神职人员及其相关程序构成了一次尝试,旨在将类似的自由度赋予观众。

无神职人员中,表演者演奏通过 Web 浏览器呈现的屏幕乐谱。观众打开 Web 浏览器访问带有标准 CGI 表单的页面,允许他们通过输入数据来对他们听到的内容做出反应。然后,该作品的脚本处理数据,影响呈现给表演者的后续乐谱页面。

无神职人员具有特定的技术、后勤和审美要求。处理必须实时进行。观众和表演者的作品界面必须熟悉且舒适。作品必须是可移植的——能够在任何地点演出,且设置要求极低。

用户,即充当“指挥”的人,可以是本人或在没有我的演出中担任类似角色的人,通过运行 bash 脚本 setup.sh 来启动作品。该脚本使用 Python 为 GNU Lilypond 生成一个标记文件,GNU Lilypond 是一种音乐排版程序,将在本文后面更详细地介绍。然后,它将 Lilypond 文件处理成 PNG 图像,如清单 1 所示。未显示的脚本部分还执行清理操作,以删除旧数据和结束操作,将图像放置在适当的 Web 目录中。

清单 1. setup.sh 的摘录

python -O NoClergy/Python/make_ly.py \
clar > lilypond/ly/clar.ly
lilypond --png -o lilypond/out/ lilypond/ly/clar.ly

Generating Music Notation in Real Time

图 1. 单簧管的初始输出示例

稍后,当用户数据可用时,类似的 bash 脚本 noclergy.sh(清单 2)读取先前的数据并生成后续的乐谱页面。脚本 mutate_config.py 读取用户数据并更新配置文件,而 mutate.py 将这些更改应用于音乐材料,执行类似于上面描述的 setup.sh 脚本的操作,但这次是从先前输出的示例中进行工作。

清单 2. noclergy.sh 的摘录

python -O NoClergy/Python/mutate_config.py
python -O NoClergy/Python/mutate.py 'lilypond/ly/'

我选择面向对象的范例是因为音乐材料包含多种相似类型的数据实例。最初,顶层类是 Score,定义为一件乐器的一页乐谱。每个 Score 都有适合其乐器的移调级别,并且可以包含任意数量的小节 (Measures)。我选择 20 作为适合单页且具有合理易读性的小节数量,并且作为每个更新的乐谱页面之间良好的音乐时间长度。

面向非音乐家

移调

移调是一个音乐术语,指的是改变乐曲的音高。有些乐器的声音比正常声音低或高,因此,需要为它们编写的音乐进行移调。这种做法允许表演者学习一个乐器系列的指法,而不仅仅是一种特定的乐器。

音高类别

音符的音高类别是其音高模 12。中央 C 是一个音高,而 C 是一个音高类别。当音乐家谈论 C 或降 B 时,他们指的是音高类别。频率加倍是一个八度的升高,八度的差异是区分具有不同音高但相同音高类别的音符的原因。

每个小节都有一个节拍,它决定了小节的长度及其内部的节奏组织。它包含填充该节拍的音符数量。音符的总数也根据每个音符的时长而变化。

音符被定义为小节内的单个声音或静音事件。它有一个名为音高的字符串,该字符串可以是 r(表示休止符),也可以指示其音高类别,例如 c、cs(升 C)、d 或 ef(降 E)。它还有一个名为八度的整数,该整数仅对非休止符有意义。

音符还具有时长、力度(幅度或音量的变化)和发音,发音决定音符是重音、断奏还是持续到下一个音符,等等。音符也可以是连音,这是一种特殊的节奏组织类型。

连音是以不同于其音符类型指示的速率发生的音符或休止符。到目前为止,最常见的连音类型是三连音。一组三连音占据与两个音符相同的时长,因此三个八分音符三连音发生在两个普通八分音符的跨度内,或者在大多数情况下是一个节拍。五连音是在四个跨度内的五个音符,七连音是在四个跨度内的七个音符,依此类推。除非另有说明,否则一组连音会在 y 的跨度内挤压 x 个音符,其中 y 是低于 x 的最高 2 的幂。作曲家通常使用 x:y 符号在连音组上指示与此实践的偏差,因此 7:8 连音组将七个音符扩展到八个音符的跨度上。

无神职人员使用非唯一的连音类型列表——由前面段落中的 x 指示的数字。这允许我偏向于三连音和五连音,这很常见。任何想使用我的程序来听起来像弗兰克·扎帕或布莱恩·费尼霍夫的人都可以随意更改连音列表,尽管音乐家应该知道该程序的初始版本尚不支持嵌套连音。非音乐家程序员可能可以从名称中辨别出嵌套连音是连音的集合,这些连音本身包含一个或多个连音集合。该程序也尚不支持附点音符,附点音符使用一种记谱约定来表示音符应持续的时间再长一半。

当脚本生成乐谱时,它们会读取一个配置文件,该配置文件由上面提到的 mutate_config.py 脚本更新。清单 3 中显示的各种 pc 变量表示给定音符具有所讨论特征的百分比机会,例如成为连音、成为休止符而不是发出声音的音符、具有显式力度标记以及具有显式发音标记。

清单 3. 配置变量

# No Clergy config.txt, written by script
tupletpc = 50
restpc = 25
dynpc = 25
artpc = 25
number_of_measures = 20

然后,脚本生成完整的 Score 值的音乐材料,受上述变量的约束。此时,所有这些数据仍然仅作为 Python 脚本中的对象列表存在。我选择 MusicXML 作为外部存储的格式。

MusicXML 是 XML 的一个子集,专门用于音乐数据。它由 Recordare LLC 开发,主要用于作为音乐符号程序的交换格式。然而,它也可用作音乐数据的通用存储格式,这符合 XML 方言的特点。

清单 4. 一个音符的 MusicXML 片段示例

<note>
 <pitch>
  <step>b</step>
  <alter>-1</alter>
  <octave>3</octave>
 </pitch>
 <duration>16</duration>
 <type>sixteenth</type>
</note>

音乐家可能可以弄清楚这个 XML 片段表示第三个八度音程中的 16 分音符降 B,没有力度、发音或其他特殊变化。

脚本使用路径约定 inst/yyyy_mo_dd-hh_mi_ss.xml 存储每种乐器最新的 MusicXML 文件,其中 inst 是乐器名称,其他字母代码是时间单位。读取 XML 文件后,脚本会将其移动到备份目录并使用 bzip2 压缩。旧数据的存储对于记录特定演出、了解观众对其在作品中的角色的反应以及调试非常有用。一旦脚本读取了 XML 数据,它就可以输出以供 Lilypond 处理。

GNU Lilypond 是一个基于 Scheme 的音乐排版程序,它使用类似 TeX 的反斜杠符号进行格式化命令,并且特别关注受最佳传统手工雕刻启发的高质量音乐雕刻。它输出多种高分辨率格式,包括 PostScript、DVI 和 PNG。

清单 5. Lilypond 标记示例

| % MEASURE 2
\time 7/8 ef''8-\pp a'4 d'2-\marcato

在清单 5 中,我们看到了一个音乐小节的 Lilypond 标记。竖线表示小节线,% MEASURE 2 是注释,\time 指示 7/8 节拍,ef 使第一个音符为降 E,'' 将音符比程序使用的基线提高两个八度,8 给音符一个八分音符的节奏值,-\pp 将极弱力度指示器附加到音符。后面还有两个音符。该小节的渲染效果如图 2 所示。

Generating Music Notation in Real Time

图 2. Lilypond 标记的渲染

将语义上有意义的音乐数据存储在 MusicXML 中,并将演示数据存储在 Lilypond 标记中,这反映了 HTML 或 DocBook 世界的趋势,在这些世界中,结构信息与最终渲染保持独立。作为附带好处,这些脚本在紧急情况下也可用作 MusicXML 到 Lilypond 到 dvi/ps/pdf 转换器。

在演出期间,观众可以并且应该输入数据,这些数据将指导音乐的未来走向。我想为观众使用 GUI 界面。该作品使用 bash 脚本作为胶水语言,但所有真正的工作都由 Python 完成,这使得各种 Python GUI 选项成为明显的候选者。由于多种原因,我选择使用带有 CGI 表单的 Web 浏览器界面进行数据输入。与基于 Python 的 GUI 相比,Web 界面的优势是什么?

Web 浏览器无处不在,是世界上最常用的用户应用程序之一。我只需要求演出场地配备装有 Web 浏览器的机器即可,这是一个微不足道的要求,而无需安装 X 客户端和带有 GUI 库的 Python,然后需要解决特定于操作系统的问题。

很大程度上由于浏览器的普及,它们也很熟悉。自称为新手计算机用户的用户通常比他们以前从未见过的新 GUI 界面更习惯于操作浏览器。事实上,即使 HTML 的页面布局渲染在不同实例之间差异很大,用户也会感到熟悉。这种舒适度对于像我这样的演出情况尤其关键,在这种情况下,观众需要克服对积极参与传统上为表演者保留的过程的自然抵触情绪。

此外,HTML 易于编码。罗布·派克的规则 4 指出“花哨的算法比简单的算法更容易出错”,而直接的标记通常甚至更简单。有关这种瘦客户端设计的优势的更多信息,请参阅休·威廉姆斯和大卫·莱恩的Web 数据库应用程序,由 O'Reilly and Associates 出版。

使用浏览器界面有明显的缺点,但幸运的是,没有一个与我的项目特别相关。HTTP 速度慢,但我只需要每页音乐更新一次,速度不超过每分钟一次左右。HTML/CSS 样式相对不灵活,但我的设计需求很简单。对于没有通常是专有的插件的纯 HTML 演示文稿,视频是有问题的,但我只需要静态图像。

Generating Music Notation in Real Time

图 3. 用户界面示例

鉴于我们有一个浏览器界面,并且希望有参与的观众,我们需要处理他们的数据。我使用 Python CGI 脚本来捕获变量。它在 <pre> 标记中写入值,稍后的处理脚本会读取这些值,以便对音乐数据进行实际修改。这种通用的、可扩展的设置意味着,随着我通过添加变量或更改它们在作品其他阶段的解释方式来改进作品的其他脚本,捕获脚本几乎不需要或完全不需要更改。

清单 6. 用于捕获用户数据的脚本片段

import cgi, re
form = cgi.FieldStorage()
formS = '<pre>\n'
for field in form:
  formS += field + ' = '
  formS += form[field].value + '\n'
formS += '</pre>\n'

然后,脚本将 formS 字符串插入到反馈文件中的适当位置。变量是整数,表示给定音符具有给定特征的百分比机会。我选择在 <pre> 标记中列出这些值,以便在使用 Web 浏览器时可以轻松观察演出期间变量的变化。脚本始终将最新数据紧接在 <!-- begin --> 注释之后写入。

除了观众数据变量之外,处理脚本还结合了二阶马尔可夫链(请参阅“马尔可夫链”侧边栏)。这允许第一页音乐之后的所有页面共享第一页的特征,同时仍然受到观众反馈的影响。

马尔可夫链

马尔可夫链,以俄罗斯数学家 A.A. 马尔可夫的名字命名,是基于样本的有序数据集合。对于每个集合元素 x,我们找到从它到任何元素的​​所有转换,包括它自身的重复。从这些数据中,我们找到 x 导致其后每个元素的概率。然后,我们可以使用这些概率来重建一个随机生成的集合,该集合类似于源集合,但不一定与源集合相同。

然后,用户继续运行 noclergy.py 脚本,直到达到期望的时长。另一个包装 shell 脚本可以使作品一直运行,直到满足特定条件,例如 pc 变量之一达到给定的最小值或最大值。它也可以具有设定的迭代次数。它甚至可以作为长期装置运行。没有表演者喜欢坐上八个小时或更长时间,演奏屏幕上出现在他们面前的任何东西的想法。因此,对于非常长的运行,我应该更改程序,使其不需要表演者。一种选择是输出到合成器,例如 Csound。另一种选择是仅显示乐谱以进行视觉演示。

该项目的未来计划包括移植到 Ruby 语言。这主要是为了自我教育,但还有其他原因。Ruby 代码往往更紧凑——像 each 方法这样的功能的优势会随着足够的代码而累积。在非正式基准测试中,我还发现 Ruby 在某些任务上比 Python 更快。我想更科学地测试这一点,无论是否使用 Python 字节码优化。最后,通过移植,我将更好地学习 Python 和 Ruby——这总是一件好事。

如前所述,脚本以 MusicXML 格式存储作品的先前运行。没有理由不能通过脚本运行相同格式的其他音乐。通过使用其他源音乐,mutate.py 脚本可以创建查理·帕克和巴托克的马尔可夫混合,或任何适合用户口味的音乐。

最后,这里有一些可能让读者感兴趣的技术琐事。当我使用我自己的 \n 字符放置约定进行“哑”文件/字符串 readline 操作时,我获得了更快的 XML 访问速度。转换为标准的 DOM 感知 XML 库会显着降低程序速度,但使其更健壮且更易于他人使用。有一段时间,我甚至保留了 XML 读取的两个版本。最终,我决定仅使用基于 DOM 的读取,即使它已成为迄今为止脚本中最慢的部分。

最后,我认为使用基于 HTTP 的观众界面的论点对于 XML 访问问题具有价值。使用标准 DOM 库使脚本可供更广泛的用户群使用。XML 读取速度对于今天的可用性来说足够快,并且在未来随着硬件速度的提高会更快。因此,我决定为了稳健性而遵守现有标准。

我计划在 2004 年底在布法罗大学进行一次演出。显然,该程序与现实世界测试的碰撞应该提供大量关于如何改进它的有用信息。演出的具体细节仍在讨论中,但我可能会尝试为观众使用无线浏览器,我希望这将为他们提供一个不那么令人生畏的界面,并将改善表演者和观众成员的整体体验。

本文资源: www.linuxjournal.com/article/7815

Kevin Baird 正在纽约州布法罗市布法罗大学攻读音乐作曲博士学位。他的在线网址是 kevinbaird.net

加载 Disqus 评论