检查类型系统以检查音乐的正确性



如今,关于使用编程语言呈现音乐的讨论很多,因为一方面,这对于工程师来说是一项有趣的任务,另一方面,它是对音乐进行通用描述的一部分。

它是什么样子的?对于许多语言,已经创建了音乐编程环境。最受欢迎的是Raspberry Pi上的Haskell的TidalCyclesRuby的Sonic Pi。还有一个使用莱比锡作曲家库的工具。由于它是用Clojure编写的,因此缺少类型检查。

(def row-row-row-your-boat
  (phrase [3/3 3/3 2/3 1/3 3/3]
          [  0   0   0   1   2]))

->> row-row-row-your-boat
  (canon (simple 4))
  (where :pitch (comp C major))
  (where :time (bpm 90))
  play)

声音的持续时间和音高用整数和文字系数表示,这不是很方便。当涉及到音乐转换时,编程可以提供很大的帮助。说,在上面的示例中,该键设置为C大调的键,速度为每分钟90次。

当程序员看到音乐代码时,他们通常会问是否可以防止使用类型系统执行虚假执行。公平的问题。如果音乐可以表示为代码,那么书写音乐的错误是否可以视为编程错误?如果是这样,为什么不使用我们用来编写程序的技术来改善音乐编写的过程呢?

特别是,类型系统可以防止的错误与音乐不准确的常见情况之间存在明显的类比。如果您的编程语言能够检查您是否将字符串传递给希望获取数字的函数,那么它可以检查您是否在用C专业编写的段落中不使用f-sharp,关键是没有变化的迹象。

在本文中,我们将找出使音乐正确的原因以及如何使用类型系统来表现音乐。

规范主义


几个世纪以来,理论家一直在争论什么使音乐正确。通常,它们在音乐规范主义的框架内运作:评估音乐是否符合某些规则。

您可以描述规则形成的主要方法:

  • 学习经典
  • 模式的定义
  • 将它们制定为一组规则

然后可以说:

不符合这些规则的音乐是不正确的。

例如,合成规则指出,不允许在旋律中从大跳变到大音阶。这种方法导致了许多有价值的发现。古典音乐具有非常有趣的模式,将我们的观察结果概括为一组规则有助于讨论音乐现象。

另外,在掌握乐器时,遵循规则很有用。如果老师纠正那些在C大专里练习拉小提琴但误入歧途的孩子,那是很好的。在某些情况下,遵循正确和错误音乐的定义很有用。

但是,这种方法有两个主要缺点。

首先,此过程产生的规则高度依赖于理论家认为的经典。数据集的偏差导致结论的偏差,而西方古典音乐不能反映人类的整个音乐体验。根据巴赫(Bach)和莫扎特(Mozart)的音乐创建规则可能会很有趣,但是它们通常不会告诉您太多关于音乐的知识。

当您意识到音乐流派通常与某些文化和种族紧密相关,并且将某些类型的音乐放在优先位置,从而无意中将某些类型的人放在优先位置时,情况就变得很复杂。例如,在音乐的学术理论中,人们非常关注古典欧洲传统的研究,而很少关注源于非洲移民的音乐。:布鲁斯,爵士,摇滚和嘻哈。如果我们通过传统理论评估音乐的正确性,则这可能会导致问题的文化版本,因为高加索人的面部识别算法比黑鬼更有效。

通过识别和概括模式来创建规则的第二大缺点是,我们将始终专注于过去。违反规则时,音乐最受刺激。在吉米·佩奇(Jimmy Page)使用失真效应之前,工程师考虑将放大期间的信号削波视为缺陷。并且尽管揭示的模式有助于理解现代音乐,但是它们完全不适合评估未来的音乐。

一个好的音乐类型系统应该避免描述的两种描述性陷阱。它不应基于不正确或不正确的音乐标准。同时,尝试使错误状态无法表示,类型系统不应干扰新状态的出现。

描述主义


规定主义的另一种选择是描述主义。如果在第一种情况下将音乐模式视为约束规则,则在第二种情况下将其视为在实践过程中形成的模式。研究音乐规则的描述性方法如下所示:

  • 探索音乐的身体
  • 识别趋势
  • 我们以模型的形式制定它们

可以说:

音乐偏离模型是不寻常的。

为此,我们需要一个良好的音乐结构模型,该模型可以描述普通或不寻常,而不是对或错。大卫·休伦(David Huron)在他的《甜蜜的期待》书中提供了一种描述性理论,可以将其归结为三个关键方面:

  • 音乐通过统计培训进行分级
  • 您对正确的预测感到满意
  • 新颖性不会让您感到无聊

在我们之前的说明性示例中,从音符跳转到C被认为是错误的。而且根据休伦理论,这将是非常不寻常的。那些听过很多西方音乐的人会发现,当他们听到一个音符时,下一个音符通常是另一个音符,或者音高或低一个音阶。跳到B点会让观众感到惊讶,他们可能对此表示消极。

休伦理论的奇怪结果之一是,写作音乐并不是使小说最大化或最小化的一种练习。音乐在秩序与混乱之间的边界上保持平衡,听众的位置取决于音乐家平衡新颖感与满足预期期望的能力。如果您确实要检查组成的正确性,则必须确保它与公认的惯例相距不远,而且也不要过于严格地遵守它们。

以下是旋律趋势的统计分析数据。深色表示可能的结果。选择一条带注释的行,然后在其上找到一个包含注释的单元格,该注释更可能跟随您选择的注释-深色突出显示与另一注释列的交点。例如,在该数据集中,注释后的案例占33.53%。


这些数据取自于休伦书,它们是对来自日耳曼语民谣的25万多个音调进行分析的结果。另一组数据,例如嘻哈音乐或摇滚音乐,将展示出不同的概率。

所有这些概率都可以等效地表示为熵位:50%的概率对应一位,25%的概率对应两位,等等。为了避免被零除,我们假设对于数据集中从未发生的过渡,概率为1/100000。现在,较深的颜色显示的过渡在熵方面更昂贵-可能性较小。例如,对于等于之后的6位熵,re的概率为1/64。


熵形式的表达帮助我们将一系列音符的惊喜结合在一起,其中从一个声音到另一个声音的每个过渡都是一个熵计数器,而整个旋律的惊奇则是过渡的总和。

 ->  ->  ->  = 2,20 + 2,71 + 5,94 = 10,85 
 ->  ->  ->  = 2,48 + 7,43 + 5,94 = 15,85 

这与隐马尔可夫模型非常相似,但是我们没有生成音符字符串,而是使用加权概率来估计旋律的可能性。

您可能已经注意到,从音符到C的过渡只有2.48位,尽管乍一看这与上面描述的从C到C的过渡非常不寻常这一事实相矛盾。最常见的是,异常过渡大约是10位熵。出现差异的原因是,休伦数据无法区分从6音符到si的跳变(非常不寻常)和从1音符到si的下移(相当常见的情况)之间的区别。如果数据允许我们将这两种情况分开,该模型将显示do-si-mi-fa链听起来比re-mi-fa之前更加不合常规。

打字笔记


理想情况下,类型系统可以帮助程序员做出正确的决定并消除错误的可能性。一个很好的音乐比喻是视唱练耳。如果您没有音乐教育,那么可以从音乐剧The Sound of Music中的Do-Re-Mi歌曲中了解视唱练耳一个八度音阶的每个音符都有其自己的名称,并且该音调之外的任何音符都没有:

  1. 到(此组中的最低音符)
  2. 回覆
  3. F
  4. 到(现在高八度)

该系统允许音乐家遵循正确的音符,它设置了一种迷你语言,禁止在所选琴键之外演奏音符。d和re之间有无数个频率,但它们在视视学范围内没有复制。

这可以很容易地在代码中表示为代数类型数据:

data Solfege = Do | Re | Mi | Fa | So | La | Ti

现在,您可以定义不允许在琴键外没有音符的音乐功能。例如,此功能重复一个音符n次:

repeat : Nat -> Solfege -> List Solfege
repeat 0 s = [s]
repeat n s = s :: repeat (n - 1) s

通过该定义,音符的三次重复是一种在音乐领域将容易理解的表达。锐利尖锐笔的三倍重复是类型检查错误,因为锐利尖锐笔尖不是类型值Solfege

这给了我们某种安全性。我们的系统可以避免出现错误的音符,但是仍然不能防止音符错误组合如果我们在C大调中演奏,我们不会演奏F尖锐,但我们可以演奏C,然后跳到B音符上的大音阶,这是(传统)构图规则所禁止的。这样的上下文类型检查是可能的,但是需要更复杂的方法。

类型化过渡


Mezzo Haskell库使用相关类型来验证音乐的正确性。描述说这是“对音乐的非常严格的测试”,库可以检查是否符合各种作曲规则,而不仅仅是音调。在Mezzo中,由于pre-re-mi-fa旋律由允许的间隔组成,因此会编译以下代码:

comp = defScore $ start $ melody :| c :| d :| e :| f :>> g

但是,如果我们违反了禁止从跳转到si的规则,那么Mezzo将不会编译这样的代码:

comp = defScore $ start $ melody :| c :| b :| e :| f :>> g

Mezzo甚至会指出一个问题,这是很好的:

Major sevenths are not permitted in melody: C and B
In the first argument of ‘(:|)’, namely ‘melody :| c :| b’
In the first argument of ‘(:|)’, namely ‘melody :| c :| b :| e’
In the first argument of ‘(:>>)’, namely
    ‘melody :| c :| b :| e :| f_’

似乎出现了将类型错误作为错误音乐呈现的梦想。但是有时会出现尴尬的情况。当库的作者对肖邦的前奏进行编码时,事实证明作曲家在某些情况下没有遵循传统规则:

作品几乎全部被转录,但是有时有必要跳过一些音符,因为它们造成了Mezzo指示的禁止间隔。

您可能会问,是谁禁止的?如果不允许肖邦谈论西方音乐中允许的内容和不允许的内容,那又是谁呢?

问题是,尽管Mezzo能够考虑上下文,但是类型估计仍然是一个二元选择任务。所有注释是否正确,并且是否编译工作,是否剔除其中一个注释并且未编译工作。尽管Mezzo允许您选择合成规则,但是否应用该规则的决定是全局的。如果由于某些音乐决策违反通常的约定而禁用规则,则将取消对作品其他部分的检查。

类型熵


打破或规避音乐打字规则的一种方法是根据休伦建议的相似性对音符之间的过渡进行建模。您可以计算熵的数量来反映听众对音符配对组合的惊讶程度,而不用声明特定的转换是非。

以下代码使用从属类型功能语言Idris编写在代码中,将检查与规则相对应的旋律,以to开头并以salt结尾,并且检查这些音符之间的路径是否会产生8-16位的熵。首先,您需要确定Melody某种类型,以反映我们对旋律的表示,并具有一定的相似性或熵值。此类型具有三个构造函数:

  • Pure:反映单个音符的旋律的创建,其上下熵级别等于零。
  • (>>=):包含两个旋律,并通过从第一个旋律结尾的音符切换到第二个结尾的音符的代价来加总它们的熵边界。
  • Relax:进行旋律并扩展其熵的边界。

data Melody : (Solfege, Solfege) -> (Nat, Nat) -> Type where

  Pure  : x -> Melody (x, x) (0, 0)

  (>>=) : Melody (w, x) (low, high) ->
          (() -> Melody (y, z) (low2, high2)) ->
          Melody (w, z) (cost x y + low + low2, cost x y + high + high2)

  Relax : Melody (x, y) (low + dl, high) ->
          Melody (x, y) (low, high + dh)

这个类型定义更加复杂,但是由于它,Idris可以使用符号来do编写旋律,并且编译器将为我们跟踪熵的边界。以下是所有转换的低成本相关的调:

conventional : Melody (Do, So) (8, 16)
conventional = Relax $ do
  Pure Do
  Pure Re
  Pure Mi
  Pure Fa
  Pure So

接下来的曲调不符合规则,因为它跳到较大的音阶。在Mezzo中,将禁止该规则,或者您必须完全禁用该规则。在这里,我们只是扩展了旋律复杂度的界限,以使其适合8-24位熵的范围:

unconventional : Melody (Do, So) (8, 24)
unconventional = Relax $ do
  Pure Do
  Pure Ti
  Pure Mi
  Pure Fa
  Pure So

相反,如果Melody (Do, So) (8, 24)我们分配一个不符合旋律规则的类型Melody (Do, So) (8, 16),那么它将无法编译!

这种方法的新颖之处在于,它可以让您了解音乐何时变得太无聊。如果旋律的熵未达到下限,则产生类型错误。因此,如果您为符合规则的旋律分配类型,Melody (Do, So) (16, 24)则它也不会被编译,因为其熵未达到下限。这激发了我们对休伦理论的信念,根据该理论,人们很难听过太不寻常或无法预测的音乐。

在退化的情况下,键内音符之间的过渡不会增加熵,而键外音符之间的过渡会任意增加熵。然后建议恢复为二进制评估,其中每个便笺将被视为允许或禁止,而没有任何中间状态。

重要但难以验证


检查音乐类型很困难,因为很难确定音乐的正确性。如果我们无法在数学上准确地对音乐正确性的定义建模,那么我们就无法将其转移到类型系统中。即使我们可以正式地使我们的理解正式化,我们也需要付出努力并应用功能强大的类型系统,以便编译器可以充分评估旋律。但是,通过一些定义和一些简化的假设,这是可能的。

这只是好奇吗?也许。音乐行业不太可能很快采用类型系统来防止作曲错误。但是,如果对音乐领域的计算研究能够帮助我们以不同的方式评估在我们所有人的生活中起着如此重要作用的因素,那么这本身就证明了所有努力都是合理的。

音乐只是正确性很重要但很难验证的领域之一,这一点也许同样重要。在编程的帮助下,社会和文化领域越积极地实现自动化,就越常出现难以评估答案的情况,这对人们而言至关重要。就拿使用该决定多长时间禁锢的计算机程序判刑的正确性取决于成本与概率之间的复杂权衡,如果我们无法跟踪系统决策的逻辑,那么我们如何评估结果的公平性?

每次我们使不透明的决策流程自动化时,我们都需要问自己:“我们如何知道系统正常运行?”

All Articles