TuttiDAW 代码编曲TuttiDAW Code 指南

代码中的 JavaScript

变量、函数、循环和 register,以及让共享代码保持安全的沙箱、确定性和限制。

程序就是 JavaScript。大多数程序只是串联模式方法,但你也可以用变量、函数、数组、循环和条件来构建它们。

变量与函数

const 给模式起个名字,方便重复使用。函数可以根据参数构建模式。

const groove = s("bd [~ bd] sd ~, hh*8")
const riff = (root) => n("0 2 4 2").scale(`${root}:minor`)
drums: groove.bank("tr909")
lead: cat(riff("C"), riff("F"), riff("G"), riff("C"))
在 /code 中打开

双引号与单引号

双引号(以及反引号)是迷你记谱法:"c3 e3" 是一个两个音符的模式。单引号是普通字符串, 不会被当作迷你记谱法读取,适合包含空格的名字。

n("0 1 2 3").scale('C minor')
在 /code 中打开

循环与数组

普通的 JavaScript 就能生成模式。这里用循环构建一个级数数组,再拼接成迷你记谱法:

const steps = []
for (let i = 0; i < 8; i++) steps.push(i % 3 === 0 ? i : "~")
n(steps.join(" ")).scale("A:minor").s("triangle")
在 /code 中打开

你自己的方法:register

register(name, fn) 添加一个可以像内置方法一样串联的方法。在函数的参数里,模式排在最后。

register('dub', (amount, pat) => pat.delay(amount).delayfeedback(0.6).room(0.3))
note("c3 ~ ~ eb3 ~ ~ g3 ~").s("square").dub(0.5)
在 /code 中打开

沙箱

代码会出现在共享项目和链接里,所以 TuttiDAW 从不把它当作普通的页面脚本运行。简单的程序由 TuttiDAW 自己的解释器读取;其余的在一个封闭的 JavaScript 沙箱(WebAssembly 中的 QuickJS) 里运行,它唯一的能力就是构建模式:

  • 没有网络、没有页面、没有存储、没有定时器,也没有 eval;
  • 不能用 import(...) 和 fetch;外部音频用 samples(...) 声明,只有在你按下 Play 时 才由播放器加载;
  • 每个程序都有工作量预算和时间限制,所以死循环会以报错结束,而不会卡死页面。

确定性

同样的代码总是演奏同样的音符:在你的电脑上、合作者的电脑上、导出时都一样:

  • Math.random() 以及模式函数 rand、?、|、sometimes 等都有种子;
  • 时钟是停住的:Date.now() 总是返回同一个时刻;
  • 每次渲染都从头开始,所以存在变量里的状态不会从这一次播放带到下一次。
const pick = Math.floor(Math.random() * 4)
n(`<${pick} 2 4 6>`).scale("E:minor")
在 /code 中打开

这个程序每次运行,挑出的“随机”音都一样。

限制

  • 一个程序最长 100,000 个字符(/code 链接最长 20,000);
  • 一个声部的一个周期最多 2,048 个事件。

超出限制的程序会在引起问题的代码处报错;其他声部继续演奏。