Symi 语法

宏定义和调用

宏是Symi中一种强大的抽象机制,允许你定义可重用的代码块,并在需要的地方调用它们,这在音乐的重复结构中尤其有用。

定义宏

宏定义的基本形式都是:

宏名 = ...

其中宏名使用标识符(字母、数字、下划线组合,且不能以数字开头)。

当前版本中,宏分为三类:

  • alias macro:单行、无 :,表示一个音高链别名
  • simple macro:单行、含 :,表示多个音高项
  • complex macro:多行片段(或以非音高标记开头的单行片段)

别名宏

别名(alias)宏的 body 是一个音高链:

a = 3/2@5/4
b = C4+

别名宏可用于:

  • 在音高链中作为标识符被引用(例如 C4@a
  • 直接作为宏调用(例如 a,a@D4,
  • 在基准音定义右侧被引用(例如 <C4=a>

简单宏

简单(simple)宏用于定义一组音高项,内容由 : 分隔,例如:

arp = 1/1:3/2:2/1

上例定义了一个名为 arp 的简单宏,内部是一个三音组。

简单宏只负责音高内容复用,不在定义处固定时间戳推进;实际时值与节奏由调用位置决定。

复杂宏

复杂(complex)宏用于定义完整的行级片段(可包含时间控制、和弦、分号细分、控制信息等)。

写法是将 = 后换行,然后在下一行(或多行)书写宏体,最后用一个额外的空白行结束

riff =
{4}C,E,G,E,
D,F,A,F,

如果宏体只有一行,也可以直接写在 = 后,但是宏体必须以非音高标记开头:

riff = {4}C,E,G,E,   // 这是合法的单行复杂宏定义
riff = C,E,G,E,     // 这样写会被解析为 alias/simple 路径,而不是 complex 宏

复杂宏的宏体在逻辑上是一段独立片段:

  • 宏体内部按自身内容推进时间;
  • 调用时会将宏体事件整体平移到调用位置;
  • 宏体可以包含多个小节(多行)。
小节分隔记号看起来就像是没有名字的单行复杂宏,其实它就是对复杂宏的一种特殊语法糖。

调用宏

宏调用的基础形式是直接写宏名:

arp,
riff,
a,

调用时可以在宏名后追加音高链尾部(@...+-):

arp@D4,
arp@3/2@10c,
riff@A3,
a+,

其语义与音高链一致:调用尾部会拼接到宏内每个音符(或 alias 链)的音高链末尾,再按 @ 右结合计算最终频率。

这对 alias/simple/complex 三类宏都生效。

示例

<C4=261.63>

a = 3/2@5/4
arp = 1/1:3/2:2/1
riff =
{4}C,E,G,E,
D,F,A,F,

{1}a,
arp@D4,
riff,
,
riff@A3,

上例中:

  • a 作为 alias 宏可直接调用;
  • arp@D4riff@A3 会把 @D4 追加到展开后的音高链末尾;
  • 该调用尾部只影响当前调用,不影响后续内容。