From 4cb93ddcded1d14794787de057c1a59d1efbb1c1 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Wed, 13 Sep 2023 12:59:51 +0200 Subject: [PATCH 001/158] fix: eval scope --- website/src/repl/Repl.jsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/website/src/repl/Repl.jsx b/website/src/repl/Repl.jsx index 173bb455..eb61a216 100644 --- a/website/src/repl/Repl.jsx +++ b/website/src/repl/Repl.jsx @@ -33,7 +33,7 @@ const supabase = createClient( 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InBpZHhkc3hwaGxoempuem1pZnRoIiwicm9sZSI6ImFub24iLCJpYXQiOjE2NTYyMzA1NTYsImV4cCI6MTk3MTgwNjU1Nn0.bqlw7802fsWRnqU5BLYtmXk_k-D1VFmbkHMywWc15NM', ); -const modules = [ +let modules = [ import('@strudel.cycles/core'), import('@strudel.cycles/tonal'), import('@strudel.cycles/mini'), @@ -45,13 +45,13 @@ const modules = [ import('@strudel.cycles/csound'), ]; if (isTauri()) { - modules.concat([ + modules = modules.concat([ import('@strudel/desktopbridge/loggerbridge.mjs'), import('@strudel/desktopbridge/midibridge.mjs'), import('@strudel/desktopbridge/oscbridge.mjs'), ]); } else { - modules.concat([import('@strudel.cycles/midi'), import('@strudel.cycles/osc')]); + modules = modules.concat([import('@strudel.cycles/midi'), import('@strudel.cycles/osc')]); } const modulesLoading = evalScope( From 0a8874180cf5b22c85d8a844c7035623e7230573 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Wed, 13 Sep 2023 13:48:33 +0200 Subject: [PATCH 002/158] generalize getDevice + begin midiIn implementation --- packages/midi/midi.mjs | 49 ++++++++++++++++++++++++++++++------------ 1 file changed, 35 insertions(+), 14 deletions(-) diff --git a/packages/midi/midi.mjs b/packages/midi/midi.mjs index 98509b46..831c3afd 100644 --- a/packages/midi/midi.mjs +++ b/packages/midi/midi.mjs @@ -8,6 +8,7 @@ import * as _WebMidi from 'webmidi'; import { Pattern, isPattern, logger } from '@strudel.cycles/core'; import { noteToMidi } from '@strudel.cycles/core'; import { Note } from 'webmidi'; +import EventEmitter from 'events'; // if you use WebMidi from outside of this package, make sure to import that instance: export const { WebMidi } = _WebMidi; @@ -15,8 +16,8 @@ function supportsMidi() { return typeof navigator.requestMIDIAccess === 'function'; } -function getMidiDeviceNamesString(outputs) { - return outputs.map((o) => `'${o.name}'`).join(' | '); +function getMidiDeviceNamesString(devices) { + return devices.map((o) => `'${o.name}'`).join(' | '); } export function enableWebMidi(options = {}) { @@ -52,26 +53,24 @@ export function enableWebMidi(options = {}) { }); }); } -// const outputByName = (name: string) => WebMidi.getOutputByName(name); -const outputByName = (name) => WebMidi.getOutputByName(name); -// output?: string | number, outputs: typeof WebMidi.outputs -function getDevice(output, outputs) { - if (!outputs.length) { +function getDevice(indexOrName, devices) { + if (!devices.length) { throw new Error(`🔌 No MIDI devices found. Connect a device or enable IAC Driver.`); } - if (typeof output === 'number') { - return outputs[output]; + if (typeof indexOrName === 'number') { + return devices[indexOrName]; } - if (typeof output === 'string') { - return outputByName(output); + const byName = (name) => devices.find((output) => output.name.includes(name)); + if (typeof indexOrName === 'string') { + return byName(indexOrName); } // attempt to default to first IAC device if none is specified - const IACOutput = outputs.find((output) => output.name.includes('IAC')); - const device = IACOutput ?? outputs[0]; + const IACOutput = byName('IAC'); + const device = IACOutput ?? devices[0]; if (!device) { throw new Error( - `🔌 MIDI device '${output ? output : ''}' not found. Use one of ${getMidiDeviceNamesString(WebMidi.outputs)}`, + `🔌 MIDI device '${device ? device : ''}' not found. Use one of ${getMidiDeviceNamesString(devices)}`, ); } @@ -137,3 +136,25 @@ Pattern.prototype.midi = function (output) { } }); }; + +export async function midiIn(input) { + console.log('midi in...'); + const initial = await enableWebMidi(); // only returns on first init + const device = getDevice(input, WebMidi.inputs); + + if (initial) { + const otherInputs = WebMidi.inputs.filter((o) => o.name !== device.name); + logger( + `Midi enabled! Using "${device.name}". ${ + otherInputs?.length ? `Also available: ${getMidiDeviceNamesString(otherInputs)}` : '' + }`, + ); + } + return (fn) => { + device.addListener(EventEmitter.ANY_EVENT, (...args) => { + console.log('event!', args); + fn(args); + }); + return; + }; +} From 0f72729f0de9fedd7e4bdccf8c717746351942c8 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Thu, 14 Sep 2023 09:36:06 +0200 Subject: [PATCH 003/158] midi cc input poc --- packages/core/pattern.mjs | 6 ++++++ packages/midi/midi.mjs | 24 ++++++++++++++++-------- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/packages/core/pattern.mjs b/packages/core/pattern.mjs index 7e694f49..d29fde3f 100644 --- a/packages/core/pattern.mjs +++ b/packages/core/pattern.mjs @@ -2336,3 +2336,9 @@ export const fit = register('fit', (pat) => export const { loopAtCps, loopatcps } = register(['loopAtCps', 'loopatcps'], function (factor, cps, pat) { return _loopAt(factor, pat, cps); }); + +/** exposes a custom value at query time. basically allows mutating state without evaluation */ +export const ref = (accessor) => + pure(1) + .withValue(() => reify(accessor())) + .innerJoin(); diff --git a/packages/midi/midi.mjs b/packages/midi/midi.mjs index 831c3afd..95af837e 100644 --- a/packages/midi/midi.mjs +++ b/packages/midi/midi.mjs @@ -8,7 +8,6 @@ import * as _WebMidi from 'webmidi'; import { Pattern, isPattern, logger } from '@strudel.cycles/core'; import { noteToMidi } from '@strudel.cycles/core'; import { Note } from 'webmidi'; -import EventEmitter from 'events'; // if you use WebMidi from outside of this package, make sure to import that instance: export const { WebMidi } = _WebMidi; @@ -137,7 +136,10 @@ Pattern.prototype.midi = function (output) { }); }; -export async function midiIn(input) { +let listeners = {}; +const refs = {}; + +export async function midin(input) { console.log('midi in...'); const initial = await enableWebMidi(); // only returns on first init const device = getDevice(input, WebMidi.inputs); @@ -149,12 +151,18 @@ export async function midiIn(input) { otherInputs?.length ? `Also available: ${getMidiDeviceNamesString(otherInputs)}` : '' }`, ); + refs[input] = {}; } - return (fn) => { - device.addListener(EventEmitter.ANY_EVENT, (...args) => { - console.log('event!', args); - fn(args); - }); - return; + const cc = (cc) => ref(() => refs[input][cc] || 0); + + listeners[input] && device.removeListener('midimessage', listeners[input]); + listeners[input] = (e) => { + const cc = e.dataBytes[0]; + const v = e.dataBytes[1]; + console.log(cc, v); + refs[input][cc] = v / 127; }; + device.addListener('midimessage', listeners[input]); + //return { cc }; + return cc; } From 3a69fd50bb7d0c314dae7f961e7beae1f6c674a1 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Thu, 14 Sep 2023 09:39:31 +0200 Subject: [PATCH 004/158] fix: linting errors --- packages/midi/midi.mjs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/midi/midi.mjs b/packages/midi/midi.mjs index 95af837e..bc65497a 100644 --- a/packages/midi/midi.mjs +++ b/packages/midi/midi.mjs @@ -5,7 +5,7 @@ This program is free software: you can redistribute it and/or modify it under th */ import * as _WebMidi from 'webmidi'; -import { Pattern, isPattern, logger } from '@strudel.cycles/core'; +import { Pattern, isPattern, logger, ref } from '@strudel.cycles/core'; import { noteToMidi } from '@strudel.cycles/core'; import { Note } from 'webmidi'; // if you use WebMidi from outside of this package, make sure to import that instance: @@ -73,7 +73,7 @@ function getDevice(indexOrName, devices) { ); } - return IACOutput ?? outputs[0]; + return IACOutput ?? devices[0]; } Pattern.prototype.midi = function (output) { From 30d96dcb65fd176ea6c515e9145d4181358de04a Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Thu, 14 Sep 2023 16:28:47 +0200 Subject: [PATCH 005/158] remove log --- packages/midi/midi.mjs | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/midi/midi.mjs b/packages/midi/midi.mjs index bc65497a..688b7a9b 100644 --- a/packages/midi/midi.mjs +++ b/packages/midi/midi.mjs @@ -159,7 +159,6 @@ export async function midin(input) { listeners[input] = (e) => { const cc = e.dataBytes[0]; const v = e.dataBytes[1]; - console.log(cc, v); refs[input][cc] = v / 127; }; device.addListener('midimessage', listeners[input]); From 6e26f3975165ff4430b2079242937be351c40317 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Mon, 25 Sep 2023 22:34:31 +0200 Subject: [PATCH 006/158] add dough function for raw dsp --- packages/superdough/dspworklet.mjs | 80 ++++++++++++++++++++++++++++++ packages/superdough/index.mjs | 1 + website/src/repl/Repl.jsx | 7 ++- 3 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 packages/superdough/dspworklet.mjs diff --git a/packages/superdough/dspworklet.mjs b/packages/superdough/dspworklet.mjs new file mode 100644 index 00000000..ac51c8fc --- /dev/null +++ b/packages/superdough/dspworklet.mjs @@ -0,0 +1,80 @@ +import { Pattern } from '@strudel.cycles/core'; +import { getAudioContext } from './superdough.mjs'; + +let worklet; +export async function dspWorklet(ac, code) { + const name = `dsp-worklet-${Date.now()}`; + const workletCode = `${code} +let __q = []; // trigger queue +class MyProcessor extends AudioWorkletProcessor { + constructor() { + super(); + this.t = 0; + this.stopped = false; + this.port.onmessage = (e) => { + if(e.data==='stop') { + this.stopped = true; + } else if(e.data?.dough) { + const deadline = e.data.time-currentTime; + __q.push(e.data) + } else { + msg?.(e.data) + } + }; + } + process(inputs, outputs, parameters) { + const output = outputs[0]; + if(__q.length) { + __q = __q.filter((el) => { + const deadline = el.time-currentTime; + return deadline>0 ? true : trigger(el.dough) + }) + } + for (let i = 0; i < output[0].length; i++) { + const out = dsp(this.t / sampleRate); + output.forEach((channel) => { + channel[i] = out; + }); + this.t++; + } + return !this.stopped; + } +} +registerProcessor('${name}', MyProcessor); +`; + const base64String = btoa(workletCode); + const dataURL = `data:text/javascript;base64,${base64String}`; + await ac.audioWorklet.addModule(dataURL); + const node = new AudioWorkletNode(ac, name); + const stop = () => node.port.postMessage('stop'); + return { node, stop }; +} +const stop = () => { + if (worklet) { + worklet?.stop(); + worklet?.node?.disconnect(); + } +}; + +if (typeof window !== 'undefined') { + window.addEventListener('message', (e) => { + if (e.data === 'strudel-stop') { + stop(); + } else if (e.data?.dough) { + worklet?.node.port.postMessage(e.data); + } + }); +} + +export const dough = async (code) => { + const ac = getAudioContext(); + stop(); + worklet = await dspWorklet(ac, code); + worklet.node.connect(ac.destination); +}; + +Pattern.prototype.dough = function () { + return this.onTrigger((t, hap) => { + window.postMessage({ time: t, dough: hap.value }); + }, 1); +}; diff --git a/packages/superdough/index.mjs b/packages/superdough/index.mjs index e5d4498b..3247c5b4 100644 --- a/packages/superdough/index.mjs +++ b/packages/superdough/index.mjs @@ -10,3 +10,4 @@ export * from './helpers.mjs'; export * from './synth.mjs'; export * from './zzfx.mjs'; export * from './logger.mjs'; +export * from './dspworklet.mjs'; diff --git a/website/src/repl/Repl.jsx b/website/src/repl/Repl.jsx index 173bb455..fe32e741 100644 --- a/website/src/repl/Repl.jsx +++ b/website/src/repl/Repl.jsx @@ -149,7 +149,12 @@ export function Repl({ embedded = false }) { onEvalError: (err) => { setPending(false); }, - onToggle: (play) => !play && cleanupDraw(false), + onToggle: (play) => { + if (!play) { + cleanupDraw(false); + window.postMessage('strudel-stop'); + } + }, drawContext, // drawTime: [0, 6], paintOptions, From 52c01abbe9e62abc18820143bfea0569e2c7d31a Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Mon, 25 Sep 2023 22:39:03 +0200 Subject: [PATCH 007/158] encapsulate .dough --- packages/superdough/dspworklet.mjs | 9 +++------ packages/webaudio/webaudio.mjs | 6 +++++- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/packages/superdough/dspworklet.mjs b/packages/superdough/dspworklet.mjs index ac51c8fc..2a77de53 100644 --- a/packages/superdough/dspworklet.mjs +++ b/packages/superdough/dspworklet.mjs @@ -1,4 +1,3 @@ -import { Pattern } from '@strudel.cycles/core'; import { getAudioContext } from './superdough.mjs'; let worklet; @@ -73,8 +72,6 @@ export const dough = async (code) => { worklet.node.connect(ac.destination); }; -Pattern.prototype.dough = function () { - return this.onTrigger((t, hap) => { - window.postMessage({ time: t, dough: hap.value }); - }, 1); -}; +export function doughTrigger(t, hap, currentTime, duration, cps) { + window.postMessage({ time: t, dough: hap.value, currentTime, duration, cps }); +} diff --git a/packages/webaudio/webaudio.mjs b/packages/webaudio/webaudio.mjs index 8b32a90c..fb4a3d7d 100644 --- a/packages/webaudio/webaudio.mjs +++ b/packages/webaudio/webaudio.mjs @@ -5,7 +5,7 @@ This program is free software: you can redistribute it and/or modify it under th */ import * as strudel from '@strudel.cycles/core'; -import { superdough, getAudioContext, setLogger } from 'superdough'; +import { superdough, getAudioContext, setLogger, doughTrigger } from 'superdough'; const { Pattern, logger } = strudel; setLogger(logger); @@ -35,3 +35,7 @@ export function webaudioScheduler(options = {}) { onTrigger: strudel.getTrigger({ defaultOutput, getTime }), }); } + +Pattern.prototype.dough = function () { + return this.onTrigger(doughTrigger, 1); +}; From 7078e20200bcbcc98e61de795604fc174d9495e3 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Mon, 25 Sep 2023 22:56:44 +0200 Subject: [PATCH 008/158] less garbage --- packages/superdough/dspworklet.mjs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/packages/superdough/dspworklet.mjs b/packages/superdough/dspworklet.mjs index 2a77de53..1c109452 100644 --- a/packages/superdough/dspworklet.mjs +++ b/packages/superdough/dspworklet.mjs @@ -24,10 +24,13 @@ class MyProcessor extends AudioWorkletProcessor { process(inputs, outputs, parameters) { const output = outputs[0]; if(__q.length) { - __q = __q.filter((el) => { - const deadline = el.time-currentTime; - return deadline>0 ? true : trigger(el.dough) - }) + for(let i=0;i<__q.length;++i) { + const deadline = __q[i].time-currentTime; + if(deadline<=0) { + trigger(__q[i].dough) + __q.splice(i,1) + } + } } for (let i = 0; i < output[0].length; i++) { const out = dsp(this.t / sampleRate); From eec3752b5a9b2ba0318029fbe4c51e03e9bf4dc7 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Mon, 25 Sep 2023 23:57:09 +0200 Subject: [PATCH 009/158] cleanup --- packages/superdough/dspworklet.mjs | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/superdough/dspworklet.mjs b/packages/superdough/dspworklet.mjs index 1c109452..deff485a 100644 --- a/packages/superdough/dspworklet.mjs +++ b/packages/superdough/dspworklet.mjs @@ -14,7 +14,6 @@ class MyProcessor extends AudioWorkletProcessor { if(e.data==='stop') { this.stopped = true; } else if(e.data?.dough) { - const deadline = e.data.time-currentTime; __q.push(e.data) } else { msg?.(e.data) From 62743edf4540356e7e7aae474ff4adbf4a3bfe53 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Mon, 25 Sep 2023 23:57:19 +0200 Subject: [PATCH 010/158] bump superdough to 0.9.9 --- packages/superdough/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/superdough/package.json b/packages/superdough/package.json index 69ba6f8c..387749c8 100644 --- a/packages/superdough/package.json +++ b/packages/superdough/package.json @@ -1,6 +1,6 @@ { "name": "superdough", - "version": "0.9.8", + "version": "0.9.9", "description": "simple web audio synth and sampler intended for live coding. inspired by superdirt and webdirt.", "main": "index.mjs", "type": "module", From cf72e3bba5f22a6109c381af47634ebb34c2141a Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Wed, 27 Sep 2023 21:25:30 +0200 Subject: [PATCH 011/158] fix: add conditional imports to eval scope + fire postMessage on start --- website/src/repl/Repl.jsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/website/src/repl/Repl.jsx b/website/src/repl/Repl.jsx index fe32e741..4002eba8 100644 --- a/website/src/repl/Repl.jsx +++ b/website/src/repl/Repl.jsx @@ -33,7 +33,7 @@ const supabase = createClient( 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InBpZHhkc3hwaGxoempuem1pZnRoIiwicm9sZSI6ImFub24iLCJpYXQiOjE2NTYyMzA1NTYsImV4cCI6MTk3MTgwNjU1Nn0.bqlw7802fsWRnqU5BLYtmXk_k-D1VFmbkHMywWc15NM', ); -const modules = [ +let modules = [ import('@strudel.cycles/core'), import('@strudel.cycles/tonal'), import('@strudel.cycles/mini'), @@ -45,13 +45,13 @@ const modules = [ import('@strudel.cycles/csound'), ]; if (isTauri()) { - modules.concat([ + modules = modules.concat([ import('@strudel/desktopbridge/loggerbridge.mjs'), import('@strudel/desktopbridge/midibridge.mjs'), import('@strudel/desktopbridge/oscbridge.mjs'), ]); } else { - modules.concat([import('@strudel.cycles/midi'), import('@strudel.cycles/osc')]); + modules = modules.concat([import('@strudel.cycles/midi'), import('@strudel.cycles/osc')]); } const modulesLoading = evalScope( @@ -153,6 +153,8 @@ export function Repl({ embedded = false }) { if (!play) { cleanupDraw(false); window.postMessage('strudel-stop'); + } else { + window.postMessage('strudel-start'); } }, drawContext, From 68ab43b3ab2c79691e55e3cf2ca288573bc00e72 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Wed, 27 Sep 2023 21:26:24 +0200 Subject: [PATCH 012/158] support midi clock via "clock" control (not on desktop yet) --- packages/core/controls.mjs | 1 + packages/midi/midi.mjs | 19 +++++++++++++++++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/packages/core/controls.mjs b/packages/core/controls.mjs index 6cac6e54..62cfdd46 100644 --- a/packages/core/controls.mjs +++ b/packages/core/controls.mjs @@ -1066,6 +1066,7 @@ const generic_params = [ */ ['waveloss'], // TODO: midi effects? + ['clock'], ['dur'], // ['modwheel'], ['expression'], diff --git a/packages/midi/midi.mjs b/packages/midi/midi.mjs index 98509b46..32583d75 100644 --- a/packages/midi/midi.mjs +++ b/packages/midi/midi.mjs @@ -90,6 +90,13 @@ Pattern.prototype.midi = function (output) { enableWebMidi({ onEnabled: ({ outputs }) => { const device = getDevice(output, outputs); + if (typeof window !== 'undefined') { + window.addEventListener('message', (e) => { + if (e.data === 'strudel-stop') { + device.sendStop(); + } + }); + } const otherOutputs = outputs.filter((o) => o.name !== device.name); logger( `Midi enabled! Using "${device.name}". ${ @@ -103,6 +110,7 @@ Pattern.prototype.midi = function (output) { return this.onTrigger((time, hap, currentTime, cps) => { if (!WebMidi.enabled) { + console.log('not enabled'); return; } const device = getDevice(output, WebMidi.outputs); @@ -113,7 +121,7 @@ Pattern.prototype.midi = function (output) { const timeOffsetString = `+${offset}`; // destructure value - const { note, nrpnn, nrpv, ccn, ccv, midichan = 1 } = hap.value; + const { note, nrpnn, nrpv, ccn, ccv, midichan = 1, clock } = hap.value; const velocity = hap.context?.velocity ?? 0.9; // TODO: refactor velocity // note off messages will often a few ms arrive late, try to prevent glitching by subtracting from the duration length @@ -125,7 +133,7 @@ Pattern.prototype.midi = function (output) { time: timeOffsetString, }); } - if (ccv && ccn) { + if (ccv !== undefined && ccn !== undefined) { if (typeof ccv !== 'number' || ccv < 0 || ccv > 1) { throw new Error('expected ccv to be a number between 0 and 1'); } @@ -135,5 +143,12 @@ Pattern.prototype.midi = function (output) { const scaled = Math.round(ccv * 127); device.sendControlChange(ccn, scaled, midichan, { time: timeOffsetString }); } + const begin = hap.whole.begin + 0; + if (begin === 0) { + device.sendStart({ time: timeOffsetString }); + } + if (clock) { + device.sendClock({ time: timeOffsetString }); + } }); }; From dea1c31701a9ad8b0bbb081c85401b088f480f88 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Wed, 27 Sep 2023 22:10:21 +0200 Subject: [PATCH 013/158] use midicmd instead of clock --- packages/core/controls.mjs | 2 +- packages/midi/midi.mjs | 21 ++++++++------------- 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/packages/core/controls.mjs b/packages/core/controls.mjs index 62cfdd46..d1a9ce7f 100644 --- a/packages/core/controls.mjs +++ b/packages/core/controls.mjs @@ -1066,7 +1066,7 @@ const generic_params = [ */ ['waveloss'], // TODO: midi effects? - ['clock'], + ['midicmd'], ['dur'], // ['modwheel'], ['expression'], diff --git a/packages/midi/midi.mjs b/packages/midi/midi.mjs index 32583d75..ee111d7e 100644 --- a/packages/midi/midi.mjs +++ b/packages/midi/midi.mjs @@ -90,13 +90,6 @@ Pattern.prototype.midi = function (output) { enableWebMidi({ onEnabled: ({ outputs }) => { const device = getDevice(output, outputs); - if (typeof window !== 'undefined') { - window.addEventListener('message', (e) => { - if (e.data === 'strudel-stop') { - device.sendStop(); - } - }); - } const otherOutputs = outputs.filter((o) => o.name !== device.name); logger( `Midi enabled! Using "${device.name}". ${ @@ -121,7 +114,7 @@ Pattern.prototype.midi = function (output) { const timeOffsetString = `+${offset}`; // destructure value - const { note, nrpnn, nrpv, ccn, ccv, midichan = 1, clock } = hap.value; + const { note, nrpnn, nrpv, ccn, ccv, midichan = 1, midicmd } = hap.value; const velocity = hap.context?.velocity ?? 0.9; // TODO: refactor velocity // note off messages will often a few ms arrive late, try to prevent glitching by subtracting from the duration length @@ -143,12 +136,14 @@ Pattern.prototype.midi = function (output) { const scaled = Math.round(ccv * 127); device.sendControlChange(ccn, scaled, midichan, { time: timeOffsetString }); } - const begin = hap.whole.begin + 0; - if (begin === 0) { - device.sendStart({ time: timeOffsetString }); - } - if (clock) { + if (['clock', 'midiClock'].includes(midicmd)) { device.sendClock({ time: timeOffsetString }); + } else if (['start'].includes(midicmd)) { + device.sendStart({ time: timeOffsetString }); + } else if (['stop'].includes(midicmd)) { + device.sendStop({ time: timeOffsetString }); + } else if (['continue'].includes(midicmd)) { + device.sendContinue({ time: timeOffsetString }); } }); }; From f11462bf41ee9898cac562970e6252035cdd77a4 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Wed, 27 Sep 2023 22:28:52 +0200 Subject: [PATCH 014/158] sync start / stop automatically too --- packages/midi/midi.mjs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/packages/midi/midi.mjs b/packages/midi/midi.mjs index ee111d7e..e3ce0b3e 100644 --- a/packages/midi/midi.mjs +++ b/packages/midi/midi.mjs @@ -78,6 +78,20 @@ function getDevice(output, outputs) { return IACOutput ?? outputs[0]; } +// send start/stop messages to outputs when repl starts/stops +if (typeof window !== 'undefined') { + window.addEventListener('message', (e) => { + if (!WebMidi?.enabled) { + return; + } + if (e.data === 'strudel-stop') { + WebMidi.outputs.forEach((output) => output.sendStop()); + } else if (e.data === 'strudel-start') { + WebMidi.outputs.forEach((output) => output.sendStart()); + } + }); +} + Pattern.prototype.midi = function (output) { if (isPattern(output)) { throw new Error( From 4eb0a7b7c0606acd5469b93f5275481dff86132a Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Wed, 27 Sep 2023 22:42:35 +0200 Subject: [PATCH 015/158] send start with accurate timing --- packages/midi/midi.mjs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/midi/midi.mjs b/packages/midi/midi.mjs index e3ce0b3e..07e6e65c 100644 --- a/packages/midi/midi.mjs +++ b/packages/midi/midi.mjs @@ -86,9 +86,8 @@ if (typeof window !== 'undefined') { } if (e.data === 'strudel-stop') { WebMidi.outputs.forEach((output) => output.sendStop()); - } else if (e.data === 'strudel-start') { - WebMidi.outputs.forEach((output) => output.sendStart()); } + // cannot start here, since we have no timing info, see sendStart below }); } @@ -150,6 +149,10 @@ Pattern.prototype.midi = function (output) { const scaled = Math.round(ccv * 127); device.sendControlChange(ccn, scaled, midichan, { time: timeOffsetString }); } + if (hap.whole.begin + 0 === 0) { + // we need to start here because we have the timing info + device.sendStart({ time: timeOffsetString }); + } if (['clock', 'midiClock'].includes(midicmd)) { device.sendClock({ time: timeOffsetString }); } else if (['start'].includes(midicmd)) { From 6c3a3b9e29123286f686c00851c4ff44a28e142a Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Thu, 28 Sep 2023 10:40:44 +0200 Subject: [PATCH 016/158] dedupe --- packages/core/controls.mjs | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/core/controls.mjs b/packages/core/controls.mjs index d1a9ce7f..6cac6e54 100644 --- a/packages/core/controls.mjs +++ b/packages/core/controls.mjs @@ -1066,7 +1066,6 @@ const generic_params = [ */ ['waveloss'], // TODO: midi effects? - ['midicmd'], ['dur'], // ['modwheel'], ['expression'], From 0dcc55ee167a6dd26cf4a671ae73e326e62ae997 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Thu, 28 Sep 2023 10:48:31 +0200 Subject: [PATCH 017/158] prevent error --- packages/midi/midi.mjs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/midi/midi.mjs b/packages/midi/midi.mjs index 1d89b6dc..ab59c2e4 100644 --- a/packages/midi/midi.mjs +++ b/packages/midi/midi.mjs @@ -167,7 +167,6 @@ let listeners = {}; const refs = {}; export async function midin(input) { - console.log('midi in...'); const initial = await enableWebMidi(); // only returns on first init const device = getDevice(input, WebMidi.inputs); @@ -186,9 +185,8 @@ export async function midin(input) { listeners[input] = (e) => { const cc = e.dataBytes[0]; const v = e.dataBytes[1]; - refs[input][cc] = v / 127; + refs[input] && (refs[input][cc] = v / 127); }; device.addListener('midimessage', listeners[input]); - //return { cc }; return cc; } From aa094bf9309ccef568b1d34726aaf878437df802 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Fri, 29 Sep 2023 10:40:00 +0200 Subject: [PATCH 018/158] checkbox + number slider --- packages/codemirror/checkbox.mjs | 87 ++++++++++++ packages/codemirror/slider.mjs | 129 ++++++++++++++++++ packages/react/src/components/CodeMirror6.jsx | 4 +- 3 files changed, 219 insertions(+), 1 deletion(-) create mode 100644 packages/codemirror/checkbox.mjs create mode 100644 packages/codemirror/slider.mjs diff --git a/packages/codemirror/checkbox.mjs b/packages/codemirror/checkbox.mjs new file mode 100644 index 00000000..279e2eb8 --- /dev/null +++ b/packages/codemirror/checkbox.mjs @@ -0,0 +1,87 @@ +import { WidgetType } from '@codemirror/view'; +import { ViewPlugin, Decoration } from '@codemirror/view'; +import { syntaxTree } from '@codemirror/language'; + +export class CheckboxWidget extends WidgetType { + constructor(checked) { + super(); + this.checked = checked; + } + + eq(other) { + return other.checked == this.checked; + } + + toDOM() { + let wrap = document.createElement('span'); + wrap.setAttribute('aria-hidden', 'true'); + wrap.className = 'cm-boolean-toggle'; + let box = wrap.appendChild(document.createElement('input')); + box.type = 'checkbox'; + box.checked = this.checked; + return wrap; + } + + ignoreEvent() { + return false; + } +} + +// EditorView +export function checkboxes(view) { + let widgets = []; + for (let { from, to } of view.visibleRanges) { + syntaxTree(view.state).iterate({ + from, + to, + enter: (node) => { + if (node.name == 'BooleanLiteral') { + let isTrue = view.state.doc.sliceString(node.from, node.to) == 'true'; + let deco = Decoration.widget({ + widget: new CheckboxWidget(isTrue), + side: 1, + }); + widgets.push(deco.range(node.from)); + } + }, + }); + } + return Decoration.set(widgets); +} + +export const checkboxPlugin = ViewPlugin.fromClass( + class { + decorations; //: DecorationSet + + constructor(view /* : EditorView */) { + this.decorations = checkboxes(view); + } + + update(update /* : ViewUpdate */) { + if (update.docChanged || update.viewportChanged) this.decorations = checkboxes(update.view); + } + }, + { + decorations: (v) => v.decorations, + + eventHandlers: { + mousedown: (e, view) => { + let target = e.target; /* as HTMLElement */ + if (target.nodeName == 'INPUT' && target.parentElement.classList.contains('cm-boolean-toggle')) + return toggleBoolean(view, view.posAtDOM(target)); + }, + }, + }, +); + +function toggleBoolean(view /* : EditorView */, pos /* : number */) { + let before = view.state.doc.sliceString(Math.max(0, pos), pos + 5).trim(); + let change; + if (!['true', 'false'].includes(before)) { + return false; + } + let insert = before === 'true' ? 'false' : 'true'; + change = { from: pos, to: pos + before.length, insert }; + view.dispatch({ changes: change }); + return true; +} diff --git a/packages/codemirror/slider.mjs b/packages/codemirror/slider.mjs new file mode 100644 index 00000000..f03dba18 --- /dev/null +++ b/packages/codemirror/slider.mjs @@ -0,0 +1,129 @@ +import { WidgetType } from '@codemirror/view'; +import { ViewPlugin, Decoration } from '@codemirror/view'; +import { syntaxTree } from '@codemirror/language'; + +export class SliderWidget extends WidgetType { + constructor(value, min, max, from, to) { + super(); + this.value = value; + this.min = min; + this.max = max; + this.from = from; + this.to = to; + } + + eq(other) { + const isSame = other.value.toFixed(4) == this.value.toFixed(4); + return isSame; + } + + toDOM() { + let wrap = document.createElement('span'); + wrap.setAttribute('aria-hidden', 'true'); + wrap.className = 'cm-slider'; // inline-flex items-center + let slider = wrap.appendChild(document.createElement('input')); + slider.type = 'range'; + slider.min = this.min; + slider.max = this.max; + slider.step = (this.max - this.min) / 1000; + slider.value = this.value; + slider.from = this.from; + slider.to = this.to; + slider.className = 'w-16'; + return wrap; + } + + ignoreEvent() { + return false; + } +} + +// EditorView +export function sliders(view) { + let widgets = []; + for (let { from, to } of view.visibleRanges) { + syntaxTree(view.state).iterate({ + from, + to, + enter: (node) => { + if (node.name == 'Number') { + let value = view.state.doc.sliceString(node.from, node.to); + value = Number(value); + /* let min = Math.min(0, value); + let max = Math.max(value, 1); */ + let min = 0; + let max = 10; + //console.log('from', node.from, 'to', node.to); + let deco = Decoration.widget({ + widget: new SliderWidget(value, min, max, node.from, node.to), + side: 1, + }); + widgets.push(deco.range(node.from)); + } + }, + }); + } + return Decoration.set(widgets); +} + +let draggedSlider, init; +export const sliderPlugin = ViewPlugin.fromClass( + class { + decorations; //: DecorationSet + + constructor(view /* : EditorView */) { + this.decorations = sliders(view); + } + + update(update /* : ViewUpdate */) { + if (update.docChanged || update.viewportChanged) { + !init && (this.decorations = sliders(update.view)); + //init = true; + } + } + }, + { + decorations: (v) => v.decorations, + + eventHandlers: { + mousedown: (e, view) => { + let target = e.target; /* as HTMLElement */ + if (target.nodeName == 'INPUT' && target.parentElement.classList.contains('cm-slider')) { + draggedSlider = target; + // remember offsetLeft / clientWidth, as they will vanish inside mousemove events for some reason + draggedSlider._offsetLeft = draggedSlider.offsetLeft; + draggedSlider._clientWidth = draggedSlider.clientWidth; + return updateSliderValue(view, e); + } + }, + mouseup: () => { + draggedSlider = undefined; + }, + mousemove: (e, view) => { + draggedSlider && updateSliderValue(view, e); + }, + }, + }, +); + +function updateSliderValue(view, e) { + const mouseX = e.clientX; + let progress = (mouseX - draggedSlider._offsetLeft) / draggedSlider._clientWidth; + progress = Math.max(Math.min(1, progress), 0); + let min = Number(draggedSlider.min); + let max = Number(draggedSlider.max); + const next = Number(progress * (max - min) + min); + let insert = next.toFixed(2); + let before = view.state.doc.sliceString(draggedSlider.from, draggedSlider.to).trim(); + before = Number(before).toFixed(4); + if (before === next) { + return false; + } + //console.log('before', before, '->', insert); + let change = { from: draggedSlider.from, to: draggedSlider.to, insert }; + draggedSlider.to = draggedSlider.from + insert.length; + //console.log('change', change); + view.dispatch({ changes: change }); + + return true; +} diff --git a/packages/react/src/components/CodeMirror6.jsx b/packages/react/src/components/CodeMirror6.jsx index 0f1b2274..f3c764e0 100644 --- a/packages/react/src/components/CodeMirror6.jsx +++ b/packages/react/src/components/CodeMirror6.jsx @@ -15,10 +15,12 @@ import { updateMiniLocations, } from '@strudel/codemirror'; import './style.css'; +import { checkboxPlugin } from '@strudel/codemirror/checkbox.mjs'; +import { sliderPlugin } from '@strudel/codemirror/slider.mjs'; export { flash, highlightMiniLocations, updateMiniLocations }; -const staticExtensions = [javascript(), flashField, highlightExtension]; +const staticExtensions = [javascript(), flashField, highlightExtension, checkboxPlugin, sliderPlugin]; export default function CodeMirror({ value, From c93e4a951a003a21431e8cc9388968bc55dc68e7 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Fri, 29 Sep 2023 12:58:16 +0200 Subject: [PATCH 019/158] match number.slider --- packages/codemirror/slider.mjs | 57 ++++++++++++++++++++++++++-------- 1 file changed, 44 insertions(+), 13 deletions(-) diff --git a/packages/codemirror/slider.mjs b/packages/codemirror/slider.mjs index f03dba18..423d3651 100644 --- a/packages/codemirror/slider.mjs +++ b/packages/codemirror/slider.mjs @@ -38,6 +38,43 @@ export class SliderWidget extends WidgetType { } } +let nodeValue = (node, view) => view.state.doc.sliceString(node.from, node.to); + +// matches a number and returns slider widget +/* let matchNumber = (node, view) => { + if (node.name == 'Number') { + const value = view.state.doc.sliceString(node.from, node.to); + let min = 0; + let max = 10; + return Decoration.widget({ + widget: new SliderWidget(Number(value), min, max, node.from, node.to), + side: 0, + }); + } +}; */ +// matches something like 123.xxx and returns slider widget +let matchNumberSlider = (node, view) => { + if ( + node.name === 'MemberExpression' && + node.node.firstChild.name === 'Number' && + node.node.lastChild.name === 'PropertyName' + ) { + // node is sth like 123.xxx + let prop = nodeValue(node.node.lastChild, view); // get prop name (e.g. xxx) + if (prop === 'slider') { + let value = nodeValue(node.node.firstChild, view); // get number (e.g. 123) + // console.log('slider value', value); + let { from, to } = node.node.firstChild; + let min = 0; + let max = 10; + return Decoration.widget({ + widget: new SliderWidget(Number(value), min, max, from, to), + side: 0, + }); + } + } +}; + // EditorView export function sliders(view) { let widgets = []; @@ -46,20 +83,14 @@ export function sliders(view) { from, to, enter: (node) => { - if (node.name == 'Number') { - let value = view.state.doc.sliceString(node.from, node.to); - value = Number(value); - /* let min = Math.min(0, value); - let max = Math.max(value, 1); */ - let min = 0; - let max = 10; - //console.log('from', node.from, 'to', node.to); - let deco = Decoration.widget({ - widget: new SliderWidget(value, min, max, node.from, node.to), - side: 1, - }); - widgets.push(deco.range(node.from)); + let numberSlider = matchNumberSlider(node, view); + if (numberSlider) { + widgets.push(numberSlider.range(node.from)); } + /* let number = matchNumber(node, view); + if (number) { + widgets.push(number.range(node.from)); + } */ }, }); } From c2481e460b5c455662999fd61a8d2b9ec1ffb22c Mon Sep 17 00:00:00 2001 From: Raphael Forment Date: Sat, 30 Sep 2023 14:07:33 +0200 Subject: [PATCH 020/158] Add pink, white and brown oscillators --- packages/superdough/synth.mjs | 282 ++++++++++++++++++++-------------- 1 file changed, 167 insertions(+), 115 deletions(-) diff --git a/packages/superdough/synth.mjs b/packages/superdough/synth.mjs index 633d0113..3ab3720f 100644 --- a/packages/superdough/synth.mjs +++ b/packages/superdough/synth.mjs @@ -20,100 +20,102 @@ const fm = (osc, harmonicityRatio, modulationIndex, wave = 'sine') => { return mod(modfreq, modgain, wave); }; + export function registerSynthSounds() { - ['sine', 'square', 'triangle', 'sawtooth'].forEach((wave) => { - registerSound( - wave, - (t, value, onended) => { - // destructure adsr here, because the default should be different for synths and samples - let { - attack = 0.001, - decay = 0.05, - sustain = 0.6, - release = 0.01, - fmh: fmHarmonicity = 1, - fmi: fmModulationIndex, - fmenv: fmEnvelopeType = 'lin', - fmattack: fmAttack, - fmdecay: fmDecay, - fmsustain: fmSustain, - fmrelease: fmRelease, - fmvelocity: fmVelocity, - fmwave: fmWaveform = 'sine', - vib = 0, - vibmod = 0.5, - } = value; - let { n, note, freq } = value; - // with synths, n and note are the same thing - note = note || 36; - if (typeof note === 'string') { - note = noteToMidi(note); // e.g. c3 => 48 - } - // get frequency - if (!freq && typeof note === 'number') { - freq = midiToFreq(note); // + 48); - } - // maybe pull out the above frequency resolution?? (there is also getFrequency but it has no default) - // make oscillator - const { node: o, stop } = getOscillator({ - t, - s: wave, - freq, - vib, - vibmod, - partials: n, - }); - - // FM + FM envelope - let stopFm, fmEnvelope; - if (fmModulationIndex) { - const { node: modulator, stop } = fm(o, fmHarmonicity, fmModulationIndex, fmWaveform); - if (![fmAttack, fmDecay, fmSustain, fmRelease, fmVelocity].find((v) => v !== undefined)) { - // no envelope by default - modulator.connect(o.frequency); - } else { - fmAttack = fmAttack ?? 0.001; - fmDecay = fmDecay ?? 0.001; - fmSustain = fmSustain ?? 1; - fmRelease = fmRelease ?? 0.001; - fmVelocity = fmVelocity ?? 1; - fmEnvelope = getEnvelope(fmAttack, fmDecay, fmSustain, fmRelease, fmVelocity, t); - if (fmEnvelopeType === 'exp') { - fmEnvelope = getExpEnvelope(fmAttack, fmDecay, fmSustain, fmRelease, fmVelocity, t); - fmEnvelope.node.maxValue = fmModulationIndex * 2; - fmEnvelope.node.minValue = 0.00001; - } - modulator.connect(fmEnvelope.node); - fmEnvelope.node.connect(o.frequency); + ['sine', 'square', 'triangle', + 'sawtooth', 'pink', 'white', + 'brown'].forEach((wave) => { + registerSound( + wave, + (t, value, onended) => { + // destructure adsr here, because the default should be different for synths and samples + let { + attack = 0.001, + decay = 0.05, + sustain = 0.6, + release = 0.01, + fmh: fmHarmonicity = 1, + fmi: fmModulationIndex, + fmenv: fmEnvelopeType = 'lin', + fmattack: fmAttack, + fmdecay: fmDecay, + fmsustain: fmSustain, + fmrelease: fmRelease, + fmvelocity: fmVelocity, + fmwave: fmWaveform = 'sine', + vib = 0, + vibmod = 0.5, + } = value; + let { n, note, freq } = value; + // with synths, n and note are the same thing + note = note || 36; + if (typeof note === 'string') { + note = noteToMidi(note); // e.g. c3 => 48 + } + // get frequency + if (!freq && typeof note === 'number') { + freq = midiToFreq(note); // + 48); + } + // maybe pull out the above frequency resolution?? (there is also getFrequency but it has no default) + // make oscillator + const { node: o, stop } = getOscillator({ + t, + s: wave, + freq, + vib, + vibmod, + partials: n, + }); + // FM + FM envelope + let stopFm, fmEnvelope; + if (fmModulationIndex) { + const { node: modulator, stop } = fm(o, fmHarmonicity, fmModulationIndex, fmWaveform); + if (![fmAttack, fmDecay, fmSustain, fmRelease, fmVelocity].find((v) => v !== undefined)) { + // no envelope by default + modulator.connect(o.frequency); + } else { + fmAttack = fmAttack ?? 0.001; + fmDecay = fmDecay ?? 0.001; + fmSustain = fmSustain ?? 1; + fmRelease = fmRelease ?? 0.001; + fmVelocity = fmVelocity ?? 1; + fmEnvelope = getEnvelope(fmAttack, fmDecay, fmSustain, fmRelease, fmVelocity, t); + if (fmEnvelopeType === 'exp') { + fmEnvelope = getExpEnvelope(fmAttack, fmDecay, fmSustain, fmRelease, fmVelocity, t); + fmEnvelope.node.maxValue = fmModulationIndex * 2; + fmEnvelope.node.minValue = 0.00001; + } + modulator.connect(fmEnvelope.node); + fmEnvelope.node.connect(o.frequency); + } + stopFm = stop; } - stopFm = stop; - } - // turn down - const g = gainNode(0.3); + // turn down + const g = gainNode(0.3); - // gain envelope - const { node: envelope, stop: releaseEnvelope } = getEnvelope(attack, decay, sustain, release, 1, t); + // gain envelope + const { node: envelope, stop: releaseEnvelope } = getEnvelope(attack, decay, sustain, release, 1, t); - o.onended = () => { - o.disconnect(); - g.disconnect(); - onended(); - }; - return { - node: o.connect(g).connect(envelope), - stop: (releaseTime) => { - releaseEnvelope(releaseTime); - fmEnvelope?.stop(releaseTime); - let end = releaseTime + release; - stop(end); - stopFm?.(end); - }, - }; - }, - { type: 'synth', prebake: true }, - ); - }); + o.onended = () => { + o.disconnect(); + g.disconnect(); + onended(); + }; + return { + node: o.connect(g).connect(envelope), + stop: (releaseTime) => { + releaseEnvelope(releaseTime); + fmEnvelope?.stop(releaseTime); + let end = releaseTime + release; + stop(end); + stopFm?.(end); + }, + }; + }, + { type: 'synth', prebake: true }, + ); + }); } export function waveformN(partials, type) { @@ -146,36 +148,86 @@ export function waveformN(partials, type) { return osc; } -export function getOscillator({ s, freq, t, vib, vibmod, partials }) { - // Make oscillator with partial count - let o; - if (!partials || s === 'sine') { - o = getAudioContext().createOscillator(); - o.type = s || 'triangle'; - } else { - o = waveformN(partials, s); - } - o.frequency.value = Number(freq); - o.start(t); +export function getNoiseOscillator({ t, ac, type = 'white' }) { + const bufferSize = 2 * ac.sampleRate; + const noiseBuffer = ac.createBuffer(1, bufferSize, ac.sampleRate); + const output = noiseBuffer.getChannelData(0); + let lastOut = 0; + let b0, b1, b2, b3, b4, b5, b6; + b0 = b1 = b2 = b3 = b4 = b5 = b6 = 0.0; - // Additional oscillator for vibrato effect - let vibrato_oscillator; - if (vib > 0) { - vibrato_oscillator = getAudioContext().createOscillator(); - vibrato_oscillator.frequency.value = vib; - const gain = getAudioContext().createGain(); - // Vibmod is the amount of vibrato, in semitones - gain.gain.value = vibmod * 100; - vibrato_oscillator.connect(gain); - gain.connect(o.detune); - vibrato_oscillator.start(t); + for (let i = 0; i < bufferSize; i++) { + if (type === 'white') { + output[i] = Math.random() * 2 - 1; + } else if (type === 'brown') { + let white = Math.random() * 2 - 1; + output[i] = (lastOut + (0.02 * white)) / 1.02; + lastOut = output[i]; + } else if (type === 'pink') { + let white = Math.random() * 2 - 1; + b0 = 0.99886 * b0 + white * 0.0555179; + b1 = 0.99332 * b1 + white * 0.0750759; + b2 = 0.96900 * b2 + white * 0.1538520; + b3 = 0.86650 * b3 + white * 0.3104856; + b4 = 0.55000 * b4 + white * 0.5329522; + b5 = -0.7616 * b5 - white * 0.0168980; + output[i] = b0 + b1 + b2 + b3 + b4 + b5 + b6 + white * 0.5362; + output[i] *= 0.11; + b6 = white * 0.115926; + } } + const o = ac.createBufferSource(); + o.buffer = noiseBuffer; + o.loop = true; + o.start(t); + return { node: o, - stop: (time) => { - vibrato_oscillator?.stop(time); - o.stop(time); - }, + stop: (time) => o.stop(time) }; } + +export function getOscillator({ s, freq, t, vib, vibmod, partials }) { + // Make oscillator with partial count + let o; + + if (['pink', 'white', 'brown'].includes(s)) { + let noiseOscillator = getNoiseOscillator({ t: t, ac: getAudioContext(), type: s }) + return { + node: noiseOscillator.node, + stop: noiseOscillator.stop + } + } else { + if (!partials || s === 'sine') { + o = getAudioContext().createOscillator(); + o.type = s || 'triangle'; + } else { + o = waveformN(partials, s); + } + o.frequency.value = Number(freq); + o.start(t); + + // Additional oscillator for vibrato effect + let vibrato_oscillator; + if (vib > 0) { + vibrato_oscillator = getAudioContext().createOscillator(); + vibrato_oscillator.frequency.value = vib; + const gain = getAudioContext().createGain(); + // Vibmod is the amount of vibrato, in semitones + gain.gain.value = vibmod * 100; + vibrato_oscillator.connect(gain); + gain.connect(o.detune); + vibrato_oscillator.start(t); + } + + return { + node: o, + stop: (time) => { + vibrato_oscillator?.stop(time); + o.stop(time); + }, + }; + } +} + From 389c7be264c22fd7ce7fb2374dad9936ad4ffc15 Mon Sep 17 00:00:00 2001 From: Raphael Forment Date: Sat, 30 Sep 2023 14:39:44 +0200 Subject: [PATCH 021/158] Add noise parameter for base oscillators --- packages/superdough/synth.mjs | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/packages/superdough/synth.mjs b/packages/superdough/synth.mjs index 3ab3720f..aa7b63ee 100644 --- a/packages/superdough/synth.mjs +++ b/packages/superdough/synth.mjs @@ -45,6 +45,7 @@ export function registerSynthSounds() { fmwave: fmWaveform = 'sine', vib = 0, vibmod = 0.5, + noise = 0, } = value; let { n, note, freq } = value; // with synths, n and note are the same thing @@ -65,6 +66,7 @@ export function registerSynthSounds() { vib, vibmod, partials: n, + noise: noise, }); // FM + FM envelope let stopFm, fmEnvelope; @@ -188,8 +190,9 @@ export function getNoiseOscillator({ t, ac, type = 'white' }) { }; } -export function getOscillator({ s, freq, t, vib, vibmod, partials }) { +export function getOscillator({ s, freq, t, vib, vibmod, partials, noise }) { // Make oscillator with partial count + let ac = getAudioContext(); let o; if (['pink', 'white', 'brown'].includes(s)) { @@ -221,6 +224,33 @@ export function getOscillator({ s, freq, t, vib, vibmod, partials }) { vibrato_oscillator.start(t); } + if (noise > 0) { + // Two gain nodes to set the oscillators to their respective levels + let o_gain = ac.createGain(); + let n_gain = ac.createGain(); + o_gain.gain.setValueAtTime(1 - noise, ac.currentTime); + n_gain.gain.setValueAtTime(noise, ac.currentTime); + + // Instanciating a mixer to blend sources together + let mix_gain = ac.createGain(); + + // Connecting the main oscillator to the gain node + o.connect(o_gain).connect(mix_gain); + + // Instanciating a noise oscillator and connecting + const noiseOscillator = getNoiseOscillator({ t: t, ac: ac, type: 'pink' }); + noiseOscillator.node.connect(n_gain).connect(mix_gain); + + return { + node: mix_gain, + stop: (time) => { + vibrato_oscillator?.stop(time); + o.stop(time); + noiseOscillator.stop(time); + } + } + } + return { node: o, stop: (time) => { From bb7b8c2fabe72b520e9e312e2d2d02c8cb2e17c9 Mon Sep 17 00:00:00 2001 From: Raphael Forment Date: Sat, 30 Sep 2023 14:59:43 +0200 Subject: [PATCH 022/158] Fix noise parameter and FM parameters compatibility --- packages/superdough/synth.mjs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/superdough/synth.mjs b/packages/superdough/synth.mjs index aa7b63ee..cd523de8 100644 --- a/packages/superdough/synth.mjs +++ b/packages/superdough/synth.mjs @@ -59,7 +59,7 @@ export function registerSynthSounds() { } // maybe pull out the above frequency resolution?? (there is also getFrequency but it has no default) // make oscillator - const { node: o, stop } = getOscillator({ + const { node: o, stop, dry_node = null } = getOscillator({ t, s: wave, freq, @@ -71,10 +71,10 @@ export function registerSynthSounds() { // FM + FM envelope let stopFm, fmEnvelope; if (fmModulationIndex) { - const { node: modulator, stop } = fm(o, fmHarmonicity, fmModulationIndex, fmWaveform); + const { node: modulator, stop } = fm(dry_node !== null ? dry_node : o, fmHarmonicity, fmModulationIndex, fmWaveform); if (![fmAttack, fmDecay, fmSustain, fmRelease, fmVelocity].find((v) => v !== undefined)) { // no envelope by default - modulator.connect(o.frequency); + modulator.connect(dry_node !== null ? dry_node.frequency : o.frequency); } else { fmAttack = fmAttack ?? 0.001; fmDecay = fmDecay ?? 0.001; @@ -88,7 +88,7 @@ export function registerSynthSounds() { fmEnvelope.node.minValue = 0.00001; } modulator.connect(fmEnvelope.node); - fmEnvelope.node.connect(o.frequency); + fmEnvelope.node.connect(dry_node !== null ? dry_node.frequency : o.frequency); } stopFm = stop; } @@ -243,6 +243,7 @@ export function getOscillator({ s, freq, t, vib, vibmod, partials, noise }) { return { node: mix_gain, + dry_node: o, stop: (time) => { vibrato_oscillator?.stop(time); o.stop(time); From e3333e716fc6b30da5a792eced45a5487dd59a35 Mon Sep 17 00:00:00 2001 From: Raphael Forment Date: Sat, 30 Sep 2023 15:08:09 +0200 Subject: [PATCH 023/158] Cap noise amount to 1 --- packages/superdough/synth.mjs | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/superdough/synth.mjs b/packages/superdough/synth.mjs index cd523de8..e5f84bcf 100644 --- a/packages/superdough/synth.mjs +++ b/packages/superdough/synth.mjs @@ -226,6 +226,7 @@ export function getOscillator({ s, freq, t, vib, vibmod, partials, noise }) { if (noise > 0) { // Two gain nodes to set the oscillators to their respective levels + noise = noise > 1 ? 1 : noise; let o_gain = ac.createGain(); let n_gain = ac.createGain(); o_gain.gain.setValueAtTime(1 - noise, ac.currentTime); From b2acff40c4354b5390334fcd9ec83b5aeacd2502 Mon Sep 17 00:00:00 2001 From: Raphael Forment Date: Sat, 30 Sep 2023 15:17:22 +0200 Subject: [PATCH 024/158] Add documentation about noise sources --- website/src/pages/learn/synths.mdx | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/website/src/pages/learn/synths.mdx b/website/src/pages/learn/synths.mdx index 9f21204f..661ec860 100644 --- a/website/src/pages/learn/synths.mdx +++ b/website/src/pages/learn/synths.mdx @@ -23,6 +23,23 @@ The basic waveforms are `sine`, `sawtooth`, `square` and `triangle`, which can b If you don't set a `sound` but a `note` the default value for `sound` is `triangle`! +You can also use noise as a source by setting the waveform to: `white`, `pink` or `brown`. These are different +flavours of noise, here written from hard to soft. + +>") +.sound("/2") +.scope()`} +/> + +Some amount of pink noise can also be added to any oscillator by using the `noise` paremeter: + +").scope()`} +/> + ### Additive Synthesis To tame the harsh sound of the basic waveforms, we can set the `n` control to limit the overtones of the waveform: From 276cf858fcf851bbe92a7b3f4a31dac405714c64 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Sat, 30 Sep 2023 20:39:03 +0200 Subject: [PATCH 025/158] match slider as function --- packages/codemirror/slider.mjs | 53 ++++++++++++++++------------------ website/tailwind.config.cjs | 1 + 2 files changed, 26 insertions(+), 28 deletions(-) diff --git a/packages/codemirror/slider.mjs b/packages/codemirror/slider.mjs index 423d3651..63015051 100644 --- a/packages/codemirror/slider.mjs +++ b/packages/codemirror/slider.mjs @@ -13,7 +13,7 @@ export class SliderWidget extends WidgetType { } eq(other) { - const isSame = other.value.toFixed(4) == this.value.toFixed(4); + const isSame = other.value.toFixed(4) == this.value.toFixed(4) && other.min == this.min && other.max == this.max; return isSame; } @@ -29,7 +29,7 @@ export class SliderWidget extends WidgetType { slider.value = this.value; slider.from = this.from; slider.to = this.to; - slider.className = 'w-16'; + slider.className = 'w-16 translate-y-1.5'; return wrap; } @@ -52,26 +52,28 @@ let nodeValue = (node, view) => view.state.doc.sliceString(node.from, node.to); }); } }; */ -// matches something like 123.xxx and returns slider widget -let matchNumberSlider = (node, view) => { - if ( - node.name === 'MemberExpression' && - node.node.firstChild.name === 'Number' && - node.node.lastChild.name === 'PropertyName' - ) { - // node is sth like 123.xxx - let prop = nodeValue(node.node.lastChild, view); // get prop name (e.g. xxx) - if (prop === 'slider') { - let value = nodeValue(node.node.firstChild, view); // get number (e.g. 123) - // console.log('slider value', value); - let { from, to } = node.node.firstChild; - let min = 0; - let max = 10; - return Decoration.widget({ + +// matches something like slider(123) and returns slider widget +let matchSliderFunction = (node, view) => { + if (node.name === 'CallExpression' /* && node.node.firstChild.name === 'ArgList' */) { + let name = nodeValue(node.node.firstChild, view); // slider ? + if (name === 'slider') { + const args = node.node.lastChild.getChildren('Number'); + if (!args.length) { + return; + } + const [value, min = 0, max = 1] = args.map((node) => nodeValue(node, view)); + //console.log('slider value', value, min, max); + let { from, to } = args[0]; + let widget = Decoration.widget({ widget: new SliderWidget(Number(value), min, max, from, to), side: 0, }); + //widget._range = widget.range(from); + widget._range = widget.range(node.from); + return widget; } + // node is sth like 123.xxx } }; @@ -83,14 +85,11 @@ export function sliders(view) { from, to, enter: (node) => { - let numberSlider = matchNumberSlider(node, view); - if (numberSlider) { - widgets.push(numberSlider.range(node.from)); + let widget = matchSliderFunction(node, view); + // let widget = matchNumber(node, view); + if (widget) { + widgets.push(widget._range || widget.range(node.from)); } - /* let number = matchNumber(node, view); - if (number) { - widgets.push(number.range(node.from)); - } */ }, }); } @@ -150,11 +149,9 @@ function updateSliderValue(view, e) { if (before === next) { return false; } - //console.log('before', before, '->', insert); let change = { from: draggedSlider.from, to: draggedSlider.to, insert }; draggedSlider.to = draggedSlider.from + insert.length; - //console.log('change', change); view.dispatch({ changes: change }); - + window.postMessage({ type: 'slider-change', value: next }); return true; } diff --git a/website/tailwind.config.cjs b/website/tailwind.config.cjs index d92d5949..2c682d7d 100644 --- a/website/tailwind.config.cjs +++ b/website/tailwind.config.cjs @@ -7,6 +7,7 @@ module.exports = { content: [ './src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}', '../packages/react/src/**/*.{html,js,jsx,md,mdx,ts,tsx}', + '../packages/codemirror/slider.mjs', ], theme: { extend: { From 2731e70fb7c41b654d01cb2cd21a65afcfcbf758 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Sat, 30 Sep 2023 21:03:28 +0200 Subject: [PATCH 026/158] use loc as slider id --- packages/codemirror/slider.mjs | 2 +- packages/transpiler/transpiler.mjs | 25 ++++++++++++++++++++++--- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/packages/codemirror/slider.mjs b/packages/codemirror/slider.mjs index 63015051..2e75959b 100644 --- a/packages/codemirror/slider.mjs +++ b/packages/codemirror/slider.mjs @@ -152,6 +152,6 @@ function updateSliderValue(view, e) { let change = { from: draggedSlider.from, to: draggedSlider.to, insert }; draggedSlider.to = draggedSlider.from + insert.length; view.dispatch({ changes: change }); - window.postMessage({ type: 'slider-change', value: next }); + window.postMessage({ type: 'cm-slider', value: next, loc: draggedSlider.from }); return true; } diff --git a/packages/transpiler/transpiler.mjs b/packages/transpiler/transpiler.mjs index 78aae9f7..9ff558cb 100644 --- a/packages/transpiler/transpiler.mjs +++ b/packages/transpiler/transpiler.mjs @@ -35,6 +35,10 @@ export function transpiler(input, options = {}) { emitMiniLocations && collectMiniLocations(value, node); return this.replace(miniWithLocation(value, node)); } + if (isWidgetFunction(node)) { + // collectSliderLocations? + return this.replace(widgetWithLocation(node)); + } // TODO: remove pseudo note variables? if (node.type === 'Identifier' && isNoteWithOctave(node.name)) { this.skip(); @@ -68,11 +72,10 @@ export function transpiler(input, options = {}) { } function isStringWithDoubleQuotes(node, locations, code) { - const { raw, type } = node; - if (type !== 'Literal') { + if (node.type !== 'Literal') { return false; } - return raw[0] === '"'; + return node.raw[0] === '"'; } function isBackTickString(node, parent) { @@ -94,3 +97,19 @@ function miniWithLocation(value, node) { optional: false, }; } + +function isWidgetFunction(node) { + return node.type === 'CallExpression' && node.callee.name === 'slider'; +} + +function widgetWithLocation(node) { + const loc = node.arguments[0].start; + // add loc as identifier to first argument + // the slider function is assumed to be slider(loc, value, min?, max?) + node.arguments.unshift({ + type: 'Literal', + value: loc, + raw: loc + '', + }); + return node; +} From b36cee93f7e86b003e65b438caee14bdd24b976c Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Sat, 30 Sep 2023 21:22:49 +0200 Subject: [PATCH 027/158] add slider function to scope --- packages/codemirror/index.mjs | 1 + packages/codemirror/slider.mjs | 25 +++++++++++++++++++++++-- packages/transpiler/transpiler.mjs | 10 ++++++---- website/src/repl/Repl.jsx | 1 + 4 files changed, 31 insertions(+), 6 deletions(-) diff --git a/packages/codemirror/index.mjs b/packages/codemirror/index.mjs index bf7ce971..c847c32c 100644 --- a/packages/codemirror/index.mjs +++ b/packages/codemirror/index.mjs @@ -1,3 +1,4 @@ export * from './codemirror.mjs'; export * from './highlight.mjs'; export * from './flash.mjs'; +export * from './slider.mjs'; diff --git a/packages/codemirror/slider.mjs b/packages/codemirror/slider.mjs index 2e75959b..7695cee6 100644 --- a/packages/codemirror/slider.mjs +++ b/packages/codemirror/slider.mjs @@ -13,7 +13,12 @@ export class SliderWidget extends WidgetType { } eq(other) { - const isSame = other.value.toFixed(4) == this.value.toFixed(4) && other.min == this.min && other.max == this.max; + const isSame = + other.value.toFixed(4) == this.value.toFixed(4) && + other.min == this.min && + other.max == this.max && + other.from === this.from && + other.to === this.to; return isSame; } @@ -152,6 +157,22 @@ function updateSliderValue(view, e) { let change = { from: draggedSlider.from, to: draggedSlider.to, insert }; draggedSlider.to = draggedSlider.from + insert.length; view.dispatch({ changes: change }); - window.postMessage({ type: 'cm-slider', value: next, loc: draggedSlider.from }); + const id = 'slider_' + draggedSlider.from; // matches id generated in transpiler + window.postMessage({ type: 'cm-slider', value: next, id }); return true; } + +export let sliderValues = {}; + +export let slider = (id, value, min, max) => { + sliderValues[id] = value; // sync state at eval time (code -> state) + return ref(() => sliderValues[id]); // use state at query time +}; +if (typeof window !== 'undefined') { + window.addEventListener('message', (e) => { + if (e.data.type === 'cm-slider') { + // update state when slider is moved + sliderValues[e.data.id] = e.data.value; + } + }); +} diff --git a/packages/transpiler/transpiler.mjs b/packages/transpiler/transpiler.mjs index 9ff558cb..28f7fdfa 100644 --- a/packages/transpiler/transpiler.mjs +++ b/packages/transpiler/transpiler.mjs @@ -98,18 +98,20 @@ function miniWithLocation(value, node) { }; } +// these functions are connected to @strudel/codemirror -> slider.mjs +// maybe someday there will be pluggable transpiler functions, then move this there function isWidgetFunction(node) { return node.type === 'CallExpression' && node.callee.name === 'slider'; } function widgetWithLocation(node) { - const loc = node.arguments[0].start; + const id = 'slider_' + node.arguments[0].start; // use loc of first arg for id // add loc as identifier to first argument - // the slider function is assumed to be slider(loc, value, min?, max?) + // the slider function is assumed to be slider(id, value, min?, max?) node.arguments.unshift({ type: 'Literal', - value: loc, - raw: loc + '', + value: id, + raw: id, }); return node; } diff --git a/website/src/repl/Repl.jsx b/website/src/repl/Repl.jsx index 4002eba8..9d80cc53 100644 --- a/website/src/repl/Repl.jsx +++ b/website/src/repl/Repl.jsx @@ -39,6 +39,7 @@ let modules = [ import('@strudel.cycles/mini'), import('@strudel.cycles/xen'), import('@strudel.cycles/webaudio'), + import('@strudel/codemirror'), import('@strudel.cycles/serial'), import('@strudel.cycles/soundfonts'), From 0ad0046a769db952c8e68f097a730374e2b94a1e Mon Sep 17 00:00:00 2001 From: Raphael Forment Date: Sat, 30 Sep 2023 22:04:22 +0200 Subject: [PATCH 028/158] Replacing old reverb by better convolution --- packages/superdough/reverb.mjs | 34 ++--- packages/superdough/reverbGen.mjs | 209 +++++++++++++++++++++++++++++ packages/superdough/superdough.mjs | 9 +- 3 files changed, 236 insertions(+), 16 deletions(-) create mode 100644 packages/superdough/reverbGen.mjs diff --git a/packages/superdough/reverb.mjs b/packages/superdough/reverb.mjs index e6d31f6a..4d5f655f 100644 --- a/packages/superdough/reverb.mjs +++ b/packages/superdough/reverb.mjs @@ -1,23 +1,27 @@ -if (typeof AudioContext !== 'undefined') { - AudioContext.prototype.impulseResponse = function (duration, channels = 1) { - const length = this.sampleRate * duration; - const impulse = this.createBuffer(channels, length, this.sampleRate); - const IR = impulse.getChannelData(0); - for (let i = 0; i < length; i++) IR[i] = (2 * Math.random() - 1) * Math.pow(1 - i / length, duration); - return impulse; - }; +import reverbGen from './reverbGen.mjs'; - AudioContext.prototype.createReverb = function (duration) { +if (typeof AudioContext !== 'undefined') { + AudioContext.prototype.generateReverb = reverbGen.generateReverb; + AudioContext.prototype.createReverb = function(duration, audioContext) { const convolver = this.createConvolver(); convolver.setDuration = (d) => { - convolver.buffer = this.impulseResponse(d); - convolver.duration = duration; - return convolver; + this.generateReverb( + { + audioContext, + sampleRate: 44100, + numChannels: 2, + decayTime: d, + fadeInTime: d, + lpFreqStart: 2000, + lpFreqEnd: 15000, + }, + (buffer) => { + convolver.buffer = buffer; + } + ); + convolver.duration = d; }; convolver.setDuration(duration); return convolver; }; } - -// TODO: make the reverb more exciting -// check out https://blog.gskinner.com/archives/2019/02/reverb-web-audio-api.html diff --git a/packages/superdough/reverbGen.mjs b/packages/superdough/reverbGen.mjs new file mode 100644 index 00000000..1d05ee82 --- /dev/null +++ b/packages/superdough/reverbGen.mjs @@ -0,0 +1,209 @@ +// Copyright 2014 Alan deLespinasse +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +"use strict"; + + +var reverbGen = {}; + +/** Generates a reverb impulse response. + + @param {!Object} params TODO: Document the properties. + @param {!function(!AudioBuffer)} callback Function to call when + the impulse response has been generated. The impulse response + is passed to this function as its parameter. May be called + immediately within the current execution context, or later. */ +reverbGen.generateReverb = function(params, callback) { + var audioContext = params.audioContext || new AudioContext(); + var sampleRate = params.sampleRate || 44100; + var numChannels = params.numChannels || 2; + // params.decayTime is the -60dB fade time. We let it go 50% longer to get to -90dB. + var totalTime = params.decayTime * 1.5; + var decaySampleFrames = Math.round(params.decayTime * sampleRate); + var numSampleFrames = Math.round(totalTime * sampleRate); + var fadeInSampleFrames = Math.round((params.fadeInTime || 0) * sampleRate); + // 60dB is a factor of 1 million in power, or 1000 in amplitude. + var decayBase = Math.pow(1 / 1000, 1 / decaySampleFrames); + var reverbIR = audioContext.createBuffer(numChannels, numSampleFrames, sampleRate); + for (var i = 0; i < numChannels; i++) { + var chan = reverbIR.getChannelData(i); + for (var j = 0; j < numSampleFrames; j++) { + chan[j] = randomSample() * Math.pow(decayBase, j); + } + for (var j = 0; j < fadeInSampleFrames; j++) { + chan[j] *= (j / fadeInSampleFrames); + } + } + + applyGradualLowpass(reverbIR, params.lpFreqStart || 0, params.lpFreqEnd || 0, params.decayTime, callback); +}; + +/** Creates a canvas element showing a graph of the given data. + + @param {!Float32Array} data An array of numbers, or a Float32Array. + @param {number} width Width in pixels of the canvas. + @param {number} height Height in pixels of the canvas. + @param {number} min Minimum value of data for the graph (lower edge). + @param {number} max Maximum value of data in the graph (upper edge). + @return {!CanvasElement} The generated canvas element. */ +reverbGen.generateGraph = function(data, width, height, min, max) { + var canvas = document.createElement('canvas'); + canvas.width = width; + canvas.height = height; + var gc = canvas.getContext('2d'); + gc.fillStyle = '#000'; + gc.fillRect(0, 0, canvas.width, canvas.height); + gc.fillStyle = '#fff'; + var xscale = width / data.length; + var yscale = height / (max - min); + for (var i = 0; i < data.length; i++) { + gc.fillRect(i * xscale, height - (data[i] - min) * yscale, 1, 1); + } + return canvas; +} + +/** Saves an AudioBuffer as a 16-bit WAV file on the client's host + file system. Normalizes it to peak at +-32767, and optionally + truncates it if there's a lot of "silence" at the end. + + @param {!AudioBuffer} buffer The buffer to save. + @param {string} name Name of file to create. + @param {number?} opt_minTail Defines what counts as "silence" for + auto-truncating the buffer. If there is a point past which every + value of every channel is less than opt_minTail, then the buffer + is truncated at that point. This is expressed as an integer, + applying to the post-normalized and integer-converted + buffer. The default is 0, meaning don't truncate. */ +reverbGen.saveWavFile = function(buffer, name, opt_minTail) { + var bitsPerSample = 16; + var bytesPerSample = 2; + var sampleRate = buffer.sampleRate; + var numChannels = buffer.numberOfChannels; + var channels = getAllChannelData(buffer); + var numSampleFrames = channels[0].length; + var scale = 32767; + // Find normalization constant. + var max = 0; + for (var i = 0; i < numChannels; i++) { + for (var j = 0; j < numSampleFrames; j++) { + max = Math.max(max, Math.abs(channels[i][j])); + } + } + if (max) { + scale = 32767 / max; + } + // Find truncation point. + if (opt_minTail) { + var truncateAt = 0; + for (var i = 0; i < numChannels; i++) { + for (var j = 0; j < numSampleFrames; j++) { + var absSample = Math.abs(Math.round(scale * channels[i][j])); + if (absSample > opt_minTail) { + truncateAt = j; + } + } + } + numSampleFrames = truncateAt + 1; + } + var sampleDataBytes = bytesPerSample * numChannels * numSampleFrames; + var fileBytes = sampleDataBytes + 44; + var arrayBuffer = new ArrayBuffer(fileBytes); + var dataView = new DataView(arrayBuffer); + dataView.setUint32(0, 1179011410, true); // "RIFF" + dataView.setUint32(4, fileBytes - 8, true); // file length + dataView.setUint32(8, 1163280727, true); // "WAVE" + dataView.setUint32(12, 544501094, true); // "fmt " + dataView.setUint32(16, 16, true) // fmt chunk length + dataView.setUint16(20, 1, true); // PCM format + dataView.setUint16(22, numChannels, true); // NumChannels + dataView.setUint32(24, sampleRate, true); // SampleRate + var bytesPerSampleFrame = numChannels * bytesPerSample; + dataView.setUint32(28, sampleRate * bytesPerSampleFrame, true); // ByteRate + dataView.setUint16(32, bytesPerSampleFrame, true); // BlockAlign + dataView.setUint16(34, bitsPerSample, true); // BitsPerSample + dataView.setUint32(36, 1635017060, true); // "data" + dataView.setUint32(40, sampleDataBytes, true); + for (var j = 0; j < numSampleFrames; j++) { + for (var i = 0; i < numChannels; i++) { + dataView.setInt16(44 + j * bytesPerSampleFrame + i * bytesPerSample, + Math.round(scale * channels[i][j]), true); + } + } + var blob = new Blob([arrayBuffer], { 'type': 'audio/wav' }); + var url = window.URL.createObjectURL(blob); + var linkEl = document.createElement('a'); + linkEl.href = url; + linkEl.download = name; + linkEl.style.display = 'none'; + document.body.appendChild(linkEl); + linkEl.click(); +}; + +/** Applies a constantly changing lowpass filter to the given sound. + + @private + @param {!AudioBuffer} input + @param {number} lpFreqStart + @param {number} lpFreqEnd + @param {number} lpFreqEndAt + @param {!function(!AudioBuffer)} callback May be called + immediately within the current execution context, or later.*/ +var applyGradualLowpass = function(input, lpFreqStart, lpFreqEnd, lpFreqEndAt, callback) { + if (lpFreqStart == 0) { + callback(input); + return; + } + var channelData = getAllChannelData(input); + var context = new OfflineAudioContext(input.numberOfChannels, channelData[0].length, input.sampleRate); + var player = context.createBufferSource(); + player.buffer = input; + var filter = context.createBiquadFilter(); + + lpFreqStart = Math.min(lpFreqStart, input.sampleRate / 2); + lpFreqEnd = Math.min(lpFreqEnd, input.sampleRate / 2); + + filter.type = "lowpass"; + filter.Q.value = 0.0001; + filter.frequency.setValueAtTime(lpFreqStart, 0); + filter.frequency.linearRampToValueAtTime(lpFreqEnd, lpFreqEndAt); + + player.connect(filter); + filter.connect(context.destination); + player.start(); + context.oncomplete = function(event) { + callback(event.renderedBuffer); + }; + context.startRendering(); + + window.filterNode = filter; +}; + +/** @private + @param {!AudioBuffer} buffer + @return {!Array.} An array containing the Float32Array of each channel's samples. */ +var getAllChannelData = function(buffer) { + var channels = []; + for (var i = 0; i < buffer.numberOfChannels; i++) { + channels[i] = buffer.getChannelData(i); + } + return channels; +}; + +/** @private + @return {number} A random number from -1 to 1. */ +var randomSample = function() { + return Math.random() * 2 - 1; +}; + +export default reverbGen; diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index 289e8d97..9010c85d 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -107,17 +107,24 @@ function getDelay(orbit, delaytime, delayfeedback, t) { } let reverbs = {}; + function getReverb(orbit, duration = 2) { + + // If no reverb has been created for a given orbit, create one if (!reverbs[orbit]) { const ac = getAudioContext(); - const reverb = ac.createReverb(duration); + const reverb = ac.createReverb(duration, getAudioContext()); reverb.connect(getDestination()); + console.log(reverb) reverbs[orbit] = reverb; } + + // Update the reverb duration if needed after instanciation if (reverbs[orbit].duration !== duration) { reverbs[orbit] = reverbs[orbit].setDuration(duration); reverbs[orbit].duration = duration; } + return reverbs[orbit]; } From 062d92690036394418717821499ebc71225e1114 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Sun, 1 Oct 2023 00:24:31 +0200 Subject: [PATCH 029/158] make sliders work! --- packages/codemirror/slider.mjs | 149 +++++++++++------------- packages/react/src/hooks/useWidgets.mjs | 13 +++ packages/transpiler/transpiler.mjs | 14 ++- website/src/repl/Repl.jsx | 6 +- 4 files changed, 95 insertions(+), 87 deletions(-) create mode 100644 packages/react/src/hooks/useWidgets.mjs diff --git a/packages/codemirror/slider.mjs b/packages/codemirror/slider.mjs index 7695cee6..6510f511 100644 --- a/packages/codemirror/slider.mjs +++ b/packages/codemirror/slider.mjs @@ -1,6 +1,8 @@ -import { WidgetType } from '@codemirror/view'; -import { ViewPlugin, Decoration } from '@codemirror/view'; -import { syntaxTree } from '@codemirror/language'; +import { WidgetType, ViewPlugin, Decoration } from '@codemirror/view'; +import { StateEffect, StateField } from '@codemirror/state'; + +export let sliderValues = {}; +const getSliderID = (from) => `slider_${from}`; export class SliderWidget extends WidgetType { constructor(value, min, max, from, to) { @@ -9,17 +11,12 @@ export class SliderWidget extends WidgetType { this.min = min; this.max = max; this.from = from; + this.originalFrom = from; this.to = to; } - eq(other) { - const isSame = - other.value.toFixed(4) == this.value.toFixed(4) && - other.min == this.min && - other.max == this.max && - other.from === this.from && - other.to === this.to; - return isSame; + eq() { + return false; } toDOM() { @@ -31,10 +28,15 @@ export class SliderWidget extends WidgetType { slider.min = this.min; slider.max = this.max; slider.step = (this.max - this.min) / 1000; - slider.value = this.value; + slider.originalValue = this.value.toFixed(2); + // to make sure the code stays in sync, let's save the original value + // becuase .value automatically clamps values so it'll desync with the code + slider.value = slider.originalValue; slider.from = this.from; + slider.originalFrom = this.originalFrom; slider.to = this.to; - slider.className = 'w-16 translate-y-1.5'; + slider.className = 'w-16 translate-y-1.5 mx-2'; + this.slider = slider; return wrap; } @@ -43,78 +45,50 @@ export class SliderWidget extends WidgetType { } } -let nodeValue = (node, view) => view.state.doc.sliceString(node.from, node.to); +export const setWidgets = StateEffect.define(); -// matches a number and returns slider widget -/* let matchNumber = (node, view) => { - if (node.name == 'Number') { - const value = view.state.doc.sliceString(node.from, node.to); - let min = 0; - let max = 10; - return Decoration.widget({ - widget: new SliderWidget(Number(value), min, max, node.from, node.to), - side: 0, - }); - } -}; */ - -// matches something like slider(123) and returns slider widget -let matchSliderFunction = (node, view) => { - if (node.name === 'CallExpression' /* && node.node.firstChild.name === 'ArgList' */) { - let name = nodeValue(node.node.firstChild, view); // slider ? - if (name === 'slider') { - const args = node.node.lastChild.getChildren('Number'); - if (!args.length) { - return; - } - const [value, min = 0, max = 1] = args.map((node) => nodeValue(node, view)); - //console.log('slider value', value, min, max); - let { from, to } = args[0]; - let widget = Decoration.widget({ - widget: new SliderWidget(Number(value), min, max, from, to), - side: 0, - }); - //widget._range = widget.range(from); - widget._range = widget.range(node.from); - return widget; - } - // node is sth like 123.xxx - } +export const updateWidgets = (view, widgets) => { + view.dispatch({ effects: setWidgets.of(widgets) }); }; -// EditorView -export function sliders(view) { - let widgets = []; - for (let { from, to } of view.visibleRanges) { - syntaxTree(view.state).iterate({ - from, - to, - enter: (node) => { - let widget = matchSliderFunction(node, view); - // let widget = matchNumber(node, view); - if (widget) { - widgets.push(widget._range || widget.range(node.from)); - } - }, - }); - } - return Decoration.set(widgets); +let draggedSlider; + +function getWidgets(widgetConfigs) { + return widgetConfigs.map(({ from, to, value, min, max }) => { + return Decoration.widget({ + widget: new SliderWidget(Number(value), min, max, from, to), + side: 0, + }).range(from /* , to */); + }); } -let draggedSlider, init; export const sliderPlugin = ViewPlugin.fromClass( class { decorations; //: DecorationSet constructor(view /* : EditorView */) { - this.decorations = sliders(view); + this.decorations = Decoration.set([]); } update(update /* : ViewUpdate */) { - if (update.docChanged || update.viewportChanged) { - !init && (this.decorations = sliders(update.view)); - //init = true; - } + update.transactions.forEach((tr) => { + if (tr.docChanged) { + this.decorations = this.decorations.map(tr.changes); + const iterator = this.decorations.iter(); + while (iterator.value) { + // when the widgets are moved, we need to tell the dom node the current position + // this is important because the updateSliderValue function has to work with the dom node + iterator.value.widget.slider.from = iterator.from; + iterator.value.widget.slider.to = iterator.to; + iterator.next(); + } + } + for (let e of tr.effects) { + if (e.is(setWidgets)) { + this.decorations = Decoration.set(getWidgets(e.value)); + } + } + }); } }, { @@ -124,6 +98,8 @@ export const sliderPlugin = ViewPlugin.fromClass( mousedown: (e, view) => { let target = e.target; /* as HTMLElement */ if (target.nodeName == 'INPUT' && target.parentElement.classList.contains('cm-slider')) { + e.preventDefault(); + e.stopPropagation(); draggedSlider = target; // remember offsetLeft / clientWidth, as they will vanish inside mousemove events for some reason draggedSlider._offsetLeft = draggedSlider.offsetLeft; @@ -141,6 +117,7 @@ export const sliderPlugin = ViewPlugin.fromClass( }, ); +// moves slider on mouse event function updateSliderValue(view, e) { const mouseX = e.clientX; let progress = (mouseX - draggedSlider._offsetLeft) / draggedSlider._clientWidth; @@ -149,30 +126,38 @@ function updateSliderValue(view, e) { let max = Number(draggedSlider.max); const next = Number(progress * (max - min) + min); let insert = next.toFixed(2); - let before = view.state.doc.sliceString(draggedSlider.from, draggedSlider.to).trim(); - before = Number(before).toFixed(4); - if (before === next) { + //let before = view.state.doc.sliceString(draggedSlider.from, draggedSlider.to).trim(); + let before = draggedSlider.originalValue; + before = Number(before).toFixed(2); + // console.log('before', before, 'insert', insert, 'v'); + if (before === insert) { return false; } - let change = { from: draggedSlider.from, to: draggedSlider.to, insert }; - draggedSlider.to = draggedSlider.from + insert.length; + const to = draggedSlider.from + before.length; + let change = { from: draggedSlider.from, to, insert }; + draggedSlider.originalValue = insert; + draggedSlider.value = insert; view.dispatch({ changes: change }); - const id = 'slider_' + draggedSlider.from; // matches id generated in transpiler + const id = getSliderID(draggedSlider.originalFrom); // matches id generated in transpiler window.postMessage({ type: 'cm-slider', value: next, id }); return true; } -export let sliderValues = {}; - +// user api export let slider = (id, value, min, max) => { sliderValues[id] = value; // sync state at eval time (code -> state) return ref(() => sliderValues[id]); // use state at query time }; +// update state when sliders are moved if (typeof window !== 'undefined') { window.addEventListener('message', (e) => { if (e.data.type === 'cm-slider') { - // update state when slider is moved - sliderValues[e.data.id] = e.data.value; + if (sliderValues[e.data.id] !== undefined) { + // update state when slider is moved + sliderValues[e.data.id] = e.data.value; + } else { + console.warn(`slider with id "${e.data.id}" is not registered. Only ${Object.keys(sliderValues)}`); + } } }); } diff --git a/packages/react/src/hooks/useWidgets.mjs b/packages/react/src/hooks/useWidgets.mjs new file mode 100644 index 00000000..e7ca136a --- /dev/null +++ b/packages/react/src/hooks/useWidgets.mjs @@ -0,0 +1,13 @@ +import { useEffect, useState } from 'react'; +import { updateWidgets } from '@strudel/codemirror'; + +// i know this is ugly.. in the future, repl needs to run without react +export function useWidgets(view) { + const [widgets, setWidgets] = useState([]); + useEffect(() => { + if (view) { + updateWidgets(view, widgets); + } + }, [view, widgets]); + return { widgets, setWidgets }; +} diff --git a/packages/transpiler/transpiler.mjs b/packages/transpiler/transpiler.mjs index 28f7fdfa..48b223d4 100644 --- a/packages/transpiler/transpiler.mjs +++ b/packages/transpiler/transpiler.mjs @@ -5,7 +5,7 @@ import { isNoteWithOctave } from '@strudel.cycles/core'; import { getLeafLocations } from '@strudel.cycles/mini'; export function transpiler(input, options = {}) { - const { wrapAsync = false, addReturn = true, emitMiniLocations = true } = options; + const { wrapAsync = false, addReturn = true, emitMiniLocations = true, emitWidgets = true } = options; let ast = parse(input, { ecmaVersion: 2022, @@ -16,9 +16,9 @@ export function transpiler(input, options = {}) { let miniLocations = []; const collectMiniLocations = (value, node) => { const leafLocs = getLeafLocations(`"${value}"`, node.start); // stimmt! - //const withOffset = leafLocs.map((offsets) => offsets.map((o) => o + node.start)); miniLocations = miniLocations.concat(leafLocs); }; + let widgets = []; walk(ast, { enter(node, parent /* , prop, index */) { @@ -37,6 +37,14 @@ export function transpiler(input, options = {}) { } if (isWidgetFunction(node)) { // collectSliderLocations? + emitWidgets && + widgets.push({ + from: node.arguments[0].start, + to: node.arguments[0].end, + value: node.arguments[0].value, + min: node.arguments[1]?.value ?? 0, + max: node.arguments[2]?.value ?? 1, + }); return this.replace(widgetWithLocation(node)); } // TODO: remove pseudo note variables? @@ -68,7 +76,7 @@ export function transpiler(input, options = {}) { if (!emitMiniLocations) { return { output }; } - return { output, miniLocations }; + return { output, miniLocations, widgets }; } function isStringWithDoubleQuotes(node, locations, code) { diff --git a/website/src/repl/Repl.jsx b/website/src/repl/Repl.jsx index 9d80cc53..8fe43c6d 100644 --- a/website/src/repl/Repl.jsx +++ b/website/src/repl/Repl.jsx @@ -22,6 +22,7 @@ import Loader from './Loader'; import { settingPatterns } from '../settings.mjs'; import { code2hash, hash2code } from './helpers.mjs'; import { isTauri } from '../tauri.mjs'; +import { useWidgets } from '@strudel.cycles/react/src/hooks/useWidgets.mjs'; const { latestCode } = settingsMap.get(); @@ -129,7 +130,7 @@ export function Repl({ embedded = false }) { } = useSettings(); const paintOptions = useMemo(() => ({ fontFamily }), [fontFamily]); - + const { setWidgets } = useWidgets(view); const { code, setCode, scheduler, evaluate, activateCode, isDirty, activeCode, pattern, started, stop, error } = useStrudel({ initialCode: '// LOADING...', @@ -143,6 +144,7 @@ export function Repl({ embedded = false }) { }, afterEval: ({ code, meta }) => { setMiniLocations(meta.miniLocations); + setWidgets(meta.widgets); setPending(false); setLatestCode(code); window.location.hash = '#' + code2hash(code); @@ -220,7 +222,7 @@ export function Repl({ embedded = false }) { const handleChangeCode = useCallback( (c) => { setCode(c); - started && logger('[edit] code changed. hit ctrl+enter to update'); + //started && logger('[edit] code changed. hit ctrl+enter to update'); }, [started], ); From 33c40e5ef84af31a1f395d1cf30416c9602aa9e8 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Sun, 1 Oct 2023 00:33:10 +0200 Subject: [PATCH 030/158] fix some odd number / string problems --- packages/codemirror/slider.mjs | 6 +++--- packages/transpiler/transpiler.mjs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/codemirror/slider.mjs b/packages/codemirror/slider.mjs index 6510f511..5c2dcdab 100644 --- a/packages/codemirror/slider.mjs +++ b/packages/codemirror/slider.mjs @@ -28,7 +28,7 @@ export class SliderWidget extends WidgetType { slider.min = this.min; slider.max = this.max; slider.step = (this.max - this.min) / 1000; - slider.originalValue = this.value.toFixed(2); + slider.originalValue = this.value; // to make sure the code stays in sync, let's save the original value // becuase .value automatically clamps values so it'll desync with the code slider.value = slider.originalValue; @@ -56,7 +56,7 @@ let draggedSlider; function getWidgets(widgetConfigs) { return widgetConfigs.map(({ from, to, value, min, max }) => { return Decoration.widget({ - widget: new SliderWidget(Number(value), min, max, from, to), + widget: new SliderWidget(value, min, max, from, to), side: 0, }).range(from /* , to */); }); @@ -133,7 +133,7 @@ function updateSliderValue(view, e) { if (before === insert) { return false; } - const to = draggedSlider.from + before.length; + const to = draggedSlider.from + draggedSlider.originalValue.length; let change = { from: draggedSlider.from, to, insert }; draggedSlider.originalValue = insert; draggedSlider.value = insert; diff --git a/packages/transpiler/transpiler.mjs b/packages/transpiler/transpiler.mjs index 48b223d4..5fa9dc49 100644 --- a/packages/transpiler/transpiler.mjs +++ b/packages/transpiler/transpiler.mjs @@ -41,7 +41,7 @@ export function transpiler(input, options = {}) { widgets.push({ from: node.arguments[0].start, to: node.arguments[0].end, - value: node.arguments[0].value, + value: node.arguments[0].raw, // don't use value! min: node.arguments[1]?.value ?? 0, max: node.arguments[2]?.value ?? 1, }); From d7bc309eeb06bbe5f2f0e8aa12f9c9af2015452f Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Sun, 1 Oct 2023 00:36:34 +0200 Subject: [PATCH 031/158] fix: import --- packages/codemirror/slider.mjs | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/codemirror/slider.mjs b/packages/codemirror/slider.mjs index 5c2dcdab..44875e25 100644 --- a/packages/codemirror/slider.mjs +++ b/packages/codemirror/slider.mjs @@ -1,3 +1,4 @@ +import { ref } from '@strudel.cycles/core'; import { WidgetType, ViewPlugin, Decoration } from '@codemirror/view'; import { StateEffect, StateField } from '@codemirror/state'; From fd18508266086e756220dc4c2657455910930502 Mon Sep 17 00:00:00 2001 From: Vasilii Milovidov Date: Sun, 1 Oct 2023 05:15:47 +0400 Subject: [PATCH 032/158] wip: use sample as an impulse response for the reverb --- packages/core/controls.mjs | 2375 ++++++++++++++-------------- packages/superdough/reverb.mjs | 18 +- packages/superdough/superdough.mjs | 31 +- 3 files changed, 1232 insertions(+), 1192 deletions(-) diff --git a/packages/core/controls.mjs b/packages/core/controls.mjs index 6cac6e54..76a278a5 100644 --- a/packages/core/controls.mjs +++ b/packages/core/controls.mjs @@ -4,1220 +4,1231 @@ Copyright (C) 2022 Strudel contributors - see . */ -import { Pattern, register, sequence } from './pattern.mjs'; -import { zipWith } from './util.mjs'; +import {Pattern, register, sequence} from './pattern.mjs'; +import {zipWith} from './util.mjs'; const controls = {}; const generic_params = [ - /** - * Select a sound / sample by name. When using mininotation, you can also optionally supply 'n' and 'gain' parameters - * separated by ':'. - * - * @name s - * @param {string | Pattern} sound The sound / pattern of sounds to pick - * @synonyms sound - * @example - * s("bd hh") - * @example - * s("bd:0 bd:1 bd:0:0.3 bd:1:1.4") - * - */ - [['s', 'n', 'gain'], 'sound'], - /** - * Define a custom webaudio node to use as a sound source. - * - * @name source - * @param {function} getSource - * @synonyms src - * - */ - ['source', 'src'], - /** - * Selects the given index from the sample map. - * Numbers too high will wrap around. - * `n` can also be used to play midi numbers, but it is recommended to use `note` instead. - * - * @name n - * @param {number | Pattern} value sample index starting from 0 - * @example - * s("bd sd,hh*3").n("<0 1>") - */ - // also see https://github.com/tidalcycles/strudel/pull/63 - ['n'], - /** - * Plays the given note name or midi number. A note name consists of - * - * - a letter (a-g or A-G) - * - optional accidentals (b or #) - * - optional octave number (0-9). Defaults to 3 - * - * Examples of valid note names: `c`, `bb`, `Bb`, `f#`, `c3`, `A4`, `Eb2`, `c#5` - * - * You can also use midi numbers instead of note names, where 69 is mapped to A4 440Hz in 12EDO. - * - * @name note - * @example - * note("c a f e") - * @example - * note("c4 a4 f4 e4") - * @example - * note("60 69 65 64") - */ - [['note', 'n']], + /** + * Select a sound / sample by name. When using mininotation, you can also optionally supply 'n' and 'gain' parameters + * separated by ':'. + * + * @name s + * @param {string | Pattern} sound The sound / pattern of sounds to pick + * @synonyms sound + * @example + * s("bd hh") + * @example + * s("bd:0 bd:1 bd:0:0.3 bd:1:1.4") + * + */ + [['s', 'n', 'gain'], 'sound'], + /** + * Define a custom webaudio node to use as a sound source. + * + * @name source + * @param {function} getSource + * @synonyms src + * + */ + ['source', 'src'], + /** + * Selects the given index from the sample map. + * Numbers too high will wrap around. + * `n` can also be used to play midi numbers, but it is recommended to use `note` instead. + * + * @name n + * @param {number | Pattern} value sample index starting from 0 + * @example + * s("bd sd,hh*3").n("<0 1>") + */ + // also see https://github.com/tidalcycles/strudel/pull/63 + ['n'], + /** + * Plays the given note name or midi number. A note name consists of + * + * - a letter (a-g or A-G) + * - optional accidentals (b or #) + * - optional octave number (0-9). Defaults to 3 + * + * Examples of valid note names: `c`, `bb`, `Bb`, `f#`, `c3`, `A4`, `Eb2`, `c#5` + * + * You can also use midi numbers instead of note names, where 69 is mapped to A4 440Hz in 12EDO. + * + * @name note + * @example + * note("c a f e") + * @example + * note("c4 a4 f4 e4") + * @example + * note("60 69 65 64") + */ + [['note', 'n']], - /** - * A pattern of numbers that speed up (or slow down) samples while they play. Currently only supported by osc / superdirt. - * - * @name accelerate - * @param {number | Pattern} amount acceleration. - * @superdirtOnly - * @example - * s("sax").accelerate("<0 1 2 4 8 16>").slow(2).osc() - * - */ - ['accelerate'], - /** - * Controls the gain by an exponential amount. - * - * @name gain - * @param {number | Pattern} amount gain. - * @example - * s("hh*8").gain(".4!2 1 .4!2 1 .4 1") - * - */ - ['gain'], - /** - * Like {@link gain}, but linear. - * - * @name amp - * @param {number | Pattern} amount gain. - * @superdirtOnly - * @example - * s("bd*8").amp(".1*2 .5 .1*2 .5 .1 .5").osc() - * - */ - ['amp'], - /** - * Amplitude envelope attack time: Specifies how long it takes for the sound to reach its peak value, relative to the onset. - * - * @name attack - * @param {number | Pattern} attack time in seconds. - * @synonyms att - * @example - * note("c3 e3").attack("<0 .1 .5>") - * - */ - ['attack', 'att'], + /** + * A pattern of numbers that speed up (or slow down) samples while they play. Currently only supported by osc / superdirt. + * + * @name accelerate + * @param {number | Pattern} amount acceleration. + * @superdirtOnly + * @example + * s("sax").accelerate("<0 1 2 4 8 16>").slow(2).osc() + * + */ + ['accelerate'], + /** + * Controls the gain by an exponential amount. + * + * @name gain + * @param {number | Pattern} amount gain. + * @example + * s("hh*8").gain(".4!2 1 .4!2 1 .4 1") + * + */ + ['gain'], + /** + * Like {@link gain}, but linear. + * + * @name amp + * @param {number | Pattern} amount gain. + * @superdirtOnly + * @example + * s("bd*8").amp(".1*2 .5 .1*2 .5 .1 .5").osc() + * + */ + ['amp'], + /** + * Amplitude envelope attack time: Specifies how long it takes for the sound to reach its peak value, relative to the onset. + * + * @name attack + * @param {number | Pattern} attack time in seconds. + * @synonyms att + * @example + * note("c3 e3").attack("<0 .1 .5>") + * + */ + ['attack', 'att'], - /** - * Sets the Frequency Modulation Harmonicity Ratio. - * Controls the timbre of the sound. - * Whole numbers and simple ratios sound more natural, - * while decimal numbers and complex ratios sound metallic. - * - * @name fmh - * @param {number | Pattern} harmonicity - * @example - * note("c e g b") - * .fm(4) - * .fmh("<1 2 1.5 1.61>") - * .scope() - * - */ - [['fmh', 'fmi'], 'fmh'], - /** - * Sets the Frequency Modulation of the synth. - * Controls the modulation index, which defines the brightness of the sound. - * - * @name fm - * @param {number | Pattern} brightness modulation index - * @synonyms fmi - * @example - * note("c e g b") - * .fm("<0 1 2 8 32>") - * .scope() - * - */ - [['fmi', 'fmh'], 'fm'], - // fm envelope - /** - * Ramp type of fm envelope. Exp might be a bit broken.. - * - * @name fmenv - * @param {number | Pattern} type lin | exp - * @example - * note("c e g b") - * .fm(4) - * .fmdecay(.2) - * .fmsustain(0) - * .fmenv("") - * .scope() - * - */ - ['fmenv'], - /** - * Attack time for the FM envelope: time it takes to reach maximum modulation - * - * @name fmattack - * @param {number | Pattern} time attack time - * @example - * note("c e g b") - * .fm(4) - * .fmattack("<0 .05 .1 .2>") - * .scope() - * - */ - ['fmattack'], - /** - * Decay time for the FM envelope: seconds until the sustain level is reached after the attack phase. - * - * @name fmdecay - * @param {number | Pattern} time decay time - * @example - * note("c e g b") - * .fm(4) - * .fmdecay("<.01 .05 .1 .2>") - * .fmsustain(.4) - * .scope() - * - */ - ['fmdecay'], - /** - * Sustain level for the FM envelope: how much modulation is applied after the decay phase - * - * @name fmsustain - * @param {number | Pattern} level sustain level - * @example - * note("c e g b") - * .fm(4) - * .fmdecay(.1) - * .fmsustain("<1 .75 .5 0>") - * .scope() - * - */ - ['fmsustain'], - // these are not really useful... skipping for now - ['fmrelease'], - ['fmvelocity'], + /** + * Sets the Frequency Modulation Harmonicity Ratio. + * Controls the timbre of the sound. + * Whole numbers and simple ratios sound more natural, + * while decimal numbers and complex ratios sound metallic. + * + * @name fmh + * @param {number | Pattern} harmonicity + * @example + * note("c e g b") + * .fm(4) + * .fmh("<1 2 1.5 1.61>") + * .scope() + * + */ + [['fmh', 'fmi'], 'fmh'], + /** + * Sets the Frequency Modulation of the synth. + * Controls the modulation index, which defines the brightness of the sound. + * + * @name fm + * @param {number | Pattern} brightness modulation index + * @synonyms fmi + * @example + * note("c e g b") + * .fm("<0 1 2 8 32>") + * .scope() + * + */ + [['fmi', 'fmh'], 'fm'], + // fm envelope + /** + * Ramp type of fm envelope. Exp might be a bit broken.. + * + * @name fmenv + * @param {number | Pattern} type lin | exp + * @example + * note("c e g b") + * .fm(4) + * .fmdecay(.2) + * .fmsustain(0) + * .fmenv("") + * .scope() + * + */ + ['fmenv'], + /** + * Attack time for the FM envelope: time it takes to reach maximum modulation + * + * @name fmattack + * @param {number | Pattern} time attack time + * @example + * note("c e g b") + * .fm(4) + * .fmattack("<0 .05 .1 .2>") + * .scope() + * + */ + ['fmattack'], + /** + * Decay time for the FM envelope: seconds until the sustain level is reached after the attack phase. + * + * @name fmdecay + * @param {number | Pattern} time decay time + * @example + * note("c e g b") + * .fm(4) + * .fmdecay("<.01 .05 .1 .2>") + * .fmsustain(.4) + * .scope() + * + */ + ['fmdecay'], + /** + * Sustain level for the FM envelope: how much modulation is applied after the decay phase + * + * @name fmsustain + * @param {number | Pattern} level sustain level + * @example + * note("c e g b") + * .fm(4) + * .fmdecay(.1) + * .fmsustain("<1 .75 .5 0>") + * .scope() + * + */ + ['fmsustain'], + // these are not really useful... skipping for now + ['fmrelease'], + ['fmvelocity'], - /** - * Select the sound bank to use. To be used together with `s`. The bank name (+ "_") will be prepended to the value of `s`. - * - * @name bank - * @param {string | Pattern} bank the name of the bank - * @example - * s("bd sd").bank('RolandTR909') // = s("RolandTR909_bd RolandTR909_sd") - * - */ - ['bank'], + /** + * Select the sound bank to use. To be used together with `s`. The bank name (+ "_") will be prepended to the value of `s`. + * + * @name bank + * @param {string | Pattern} bank the name of the bank + * @example + * s("bd sd").bank('RolandTR909') // = s("RolandTR909_bd RolandTR909_sd") + * + */ + ['bank'], - ['analyze'], // analyser node send amount 0 - 1 (used by scope) - ['fft'], // fftSize of analyser + ['analyze'], // analyser node send amount 0 - 1 (used by scope) + ['fft'], // fftSize of analyser - /** - * Amplitude envelope decay time: the time it takes after the attack time to reach the sustain level. - * Note that the decay is only audible if the sustain value is lower than 1. - * - * @name decay - * @param {number | Pattern} time decay time in seconds - * @example - * note("c3 e3").decay("<.1 .2 .3 .4>").sustain(0) - * - */ - ['decay'], - /** - * Amplitude envelope sustain level: The level which is reached after attack / decay, being sustained until the offset. - * - * @name sustain - * @param {number | Pattern} gain sustain level between 0 and 1 - * @synonyms sus - * @example - * note("c3 e3").decay(.2).sustain("<0 .1 .4 .6 1>") - * - */ - ['sustain', 'sus'], - /** - * Amplitude envelope release time: The time it takes after the offset to go from sustain level to zero. - * - * @name release - * @param {number | Pattern} time release time in seconds - * @synonyms rel - * @example - * note("c3 e3 g3 c4").release("<0 .1 .4 .6 1>/2") - * - */ - ['release', 'rel'], - ['hold'], - // TODO: in tidal, it seems to be normalized - /** - * Sets the center frequency of the **b**and-**p**ass **f**ilter. When using mininotation, you - * can also optionally supply the 'bpq' parameter separated by ':'. - * - * @name bpf - * @param {number | Pattern} frequency center frequency - * @synonyms bandf, bp - * @example - * s("bd sd,hh*3").bpf("<1000 2000 4000 8000>") - * - */ - [['bandf', 'bandq'], 'bpf', 'bp'], - // TODO: in tidal, it seems to be normalized - /** - * Sets the **b**and-**p**ass **q**-factor (resonance). - * - * @name bpq - * @param {number | Pattern} q q factor - * @synonyms bandq - * @example - * s("bd sd").bpf(500).bpq("<0 1 2 3>") - * - */ - // currently an alias of 'bandq' https://github.com/tidalcycles/strudel/issues/496 - // ['bpq'], - ['bandq', 'bpq'], - /** - * a pattern of numbers from 0 to 1. Skips the beginning of each sample, e.g. `0.25` to cut off the first quarter from each sample. - * - * @memberof Pattern - * @name begin - * @param {number | Pattern} amount between 0 and 1, where 1 is the length of the sample - * @example - * samples({ rave: 'rave/AREUREADY.wav' }, 'github:tidalcycles/Dirt-Samples/master/') - * s("rave").begin("<0 .25 .5 .75>") - * - */ - ['begin'], - /** - * The same as .begin, but cuts off the end off each sample. - * - * @memberof Pattern - * @name end - * @param {number | Pattern} length 1 = whole sample, .5 = half sample, .25 = quarter sample etc.. - * @example - * s("bd*2,oh*4").end("<.1 .2 .5 1>") - * - */ - ['end'], - /** - * Loops the sample. - * Note that the tempo of the loop is not synced with the cycle tempo. - * To change the loop region, use loopBegin / loopEnd. - * - * @name loop - * @param {number | Pattern} on If 1, the sample is looped - * @example - * s("casio").loop(1) - * - */ - ['loop'], - /** - * Begin to loop at a specific point in the sample (inbetween `begin` and `end`). - * Note that the loop point must be inbetween `begin` and `end`, and before `loopEnd`! - * Note: Samples starting with wt_ will automatically loop! (wt = wavetable) - * - * @name loopBegin - * @param {number | Pattern} time between 0 and 1, where 1 is the length of the sample - * @synonyms loopb - * @example - * s("space").loop(1) - * .loopBegin("<0 .125 .25>").scope() - */ - ['loopBegin', 'loopb'], - /** - * - * End the looping section at a specific point in the sample (inbetween `begin` and `end`). - * Note that the loop point must be inbetween `begin` and `end`, and after `loopBegin`! - * - * @name loopEnd - * @param {number | Pattern} time between 0 and 1, where 1 is the length of the sample - * @synonyms loope - * @example - * s("space").loop(1) - * .loopEnd("<1 .75 .5 .25>").scope() - */ - ['loopEnd', 'loope'], - /** - * bit crusher effect. - * - * @name crush - * @param {number | Pattern} depth between 1 (for drastic reduction in bit-depth) to 16 (for barely no reduction). - * @example - * s(",hh*3").fast(2).crush("<16 8 7 6 5 4 3 2>") - * - */ - // TODO: currently duplicated with "native" legato - // TODO: superdirt legato will do more: https://youtu.be/dQPmE1WaD1k?t=419 - /** - * a pattern of numbers from 0 to 1. Skips the beginning of each sample, e.g. `0.25` to cut off the first quarter from each sample. - * - * @name legato - * @param {number | Pattern} duration between 0 and 1, where 1 is the length of the whole hap time - * @noAutocomplete - * @example - * "c4 eb4 g4 bb4".legato("<0.125 .25 .5 .75 1 2 4>") - * - */ - // ['legato'], - // ['clhatdecay'], - ['crush'], - /** - * fake-resampling for lowering the sample rate. Caution: This effect seems to only work in chromium based browsers - * - * @name coarse - * @param {number | Pattern} factor 1 for original 2 for half, 3 for a third and so on. - * @example - * s("bd sd,hh*4").coarse("<1 4 8 16 32>") - * - */ - ['coarse'], - /** - * choose the channel the pattern is sent to in superdirt - * - * @name channel - * @param {number | Pattern} channel channel number - * - */ - ['channel'], - /** - * In the style of classic drum-machines, `cut` will stop a playing sample as soon as another samples with in same cutgroup is to be played. An example would be an open hi-hat followed by a closed one, essentially muting the open. - * - * @name cut - * @param {number | Pattern} group cut group number - * @example - * s("rd*4").cut(1) - * - */ - ['cut'], - /** - * Applies the cutoff frequency of the **l**ow-**p**ass **f**ilter. - * - * When using mininotation, you can also optionally add the 'lpq' parameter, separated by ':'. - * - * @name lpf - * @param {number | Pattern} frequency audible between 0 and 20000 - * @synonyms cutoff, ctf, lp - * @example - * s("bd sd,hh*3").lpf("<4000 2000 1000 500 200 100>") - * @example - * s("bd*8").lpf("1000:0 1000:10 1000:20 1000:30") - * - */ - [['cutoff', 'resonance'], 'ctf', 'lpf', 'lp'], + /** + * Amplitude envelope decay time: the time it takes after the attack time to reach the sustain level. + * Note that the decay is only audible if the sustain value is lower than 1. + * + * @name decay + * @param {number | Pattern} time decay time in seconds + * @example + * note("c3 e3").decay("<.1 .2 .3 .4>").sustain(0) + * + */ + ['decay'], + /** + * Amplitude envelope sustain level: The level which is reached after attack / decay, being sustained until the offset. + * + * @name sustain + * @param {number | Pattern} gain sustain level between 0 and 1 + * @synonyms sus + * @example + * note("c3 e3").decay(.2).sustain("<0 .1 .4 .6 1>") + * + */ + ['sustain', 'sus'], + /** + * Amplitude envelope release time: The time it takes after the offset to go from sustain level to zero. + * + * @name release + * @param {number | Pattern} time release time in seconds + * @synonyms rel + * @example + * note("c3 e3 g3 c4").release("<0 .1 .4 .6 1>/2") + * + */ + ['release', 'rel'], + ['hold'], + // TODO: in tidal, it seems to be normalized + /** + * Sets the center frequency of the **b**and-**p**ass **f**ilter. When using mininotation, you + * can also optionally supply the 'bpq' parameter separated by ':'. + * + * @name bpf + * @param {number | Pattern} frequency center frequency + * @synonyms bandf, bp + * @example + * s("bd sd,hh*3").bpf("<1000 2000 4000 8000>") + * + */ + [['bandf', 'bandq'], 'bpf', 'bp'], + // TODO: in tidal, it seems to be normalized + /** + * Sets the **b**and-**p**ass **q**-factor (resonance). + * + * @name bpq + * @param {number | Pattern} q q factor + * @synonyms bandq + * @example + * s("bd sd").bpf(500).bpq("<0 1 2 3>") + * + */ + // currently an alias of 'bandq' https://github.com/tidalcycles/strudel/issues/496 + // ['bpq'], + ['bandq', 'bpq'], + /** + * a pattern of numbers from 0 to 1. Skips the beginning of each sample, e.g. `0.25` to cut off the first quarter from each sample. + * + * @memberof Pattern + * @name begin + * @param {number | Pattern} amount between 0 and 1, where 1 is the length of the sample + * @example + * samples({ rave: 'rave/AREUREADY.wav' }, 'github:tidalcycles/Dirt-Samples/master/') + * s("rave").begin("<0 .25 .5 .75>") + * + */ + ['begin'], + /** + * The same as .begin, but cuts off the end off each sample. + * + * @memberof Pattern + * @name end + * @param {number | Pattern} length 1 = whole sample, .5 = half sample, .25 = quarter sample etc.. + * @example + * s("bd*2,oh*4").end("<.1 .2 .5 1>") + * + */ + ['end'], + /** + * Loops the sample. + * Note that the tempo of the loop is not synced with the cycle tempo. + * To change the loop region, use loopBegin / loopEnd. + * + * @name loop + * @param {number | Pattern} on If 1, the sample is looped + * @example + * s("casio").loop(1) + * + */ + ['loop'], + /** + * Begin to loop at a specific point in the sample (inbetween `begin` and `end`). + * Note that the loop point must be inbetween `begin` and `end`, and before `loopEnd`! + * Note: Samples starting with wt_ will automatically loop! (wt = wavetable) + * + * @name loopBegin + * @param {number | Pattern} time between 0 and 1, where 1 is the length of the sample + * @synonyms loopb + * @example + * s("space").loop(1) + * .loopBegin("<0 .125 .25>").scope() + */ + ['loopBegin', 'loopb'], + /** + * + * End the looping section at a specific point in the sample (inbetween `begin` and `end`). + * Note that the loop point must be inbetween `begin` and `end`, and after `loopBegin`! + * + * @name loopEnd + * @param {number | Pattern} time between 0 and 1, where 1 is the length of the sample + * @synonyms loope + * @example + * s("space").loop(1) + * .loopEnd("<1 .75 .5 .25>").scope() + */ + ['loopEnd', 'loope'], + /** + * bit crusher effect. + * + * @name crush + * @param {number | Pattern} depth between 1 (for drastic reduction in bit-depth) to 16 (for barely no reduction). + * @example + * s(",hh*3").fast(2).crush("<16 8 7 6 5 4 3 2>") + * + */ + // TODO: currently duplicated with "native" legato + // TODO: superdirt legato will do more: https://youtu.be/dQPmE1WaD1k?t=419 + /** + * a pattern of numbers from 0 to 1. Skips the beginning of each sample, e.g. `0.25` to cut off the first quarter from each sample. + * + * @name legato + * @param {number | Pattern} duration between 0 and 1, where 1 is the length of the whole hap time + * @noAutocomplete + * @example + * "c4 eb4 g4 bb4".legato("<0.125 .25 .5 .75 1 2 4>") + * + */ + // ['legato'], + // ['clhatdecay'], + ['crush'], + /** + * fake-resampling for lowering the sample rate. Caution: This effect seems to only work in chromium based browsers + * + * @name coarse + * @param {number | Pattern} factor 1 for original 2 for half, 3 for a third and so on. + * @example + * s("bd sd,hh*4").coarse("<1 4 8 16 32>") + * + */ + ['coarse'], + /** + * choose the channel the pattern is sent to in superdirt + * + * @name channel + * @param {number | Pattern} channel channel number + * + */ + ['channel'], + /** + * In the style of classic drum-machines, `cut` will stop a playing sample as soon as another samples with in same cutgroup is to be played. An example would be an open hi-hat followed by a closed one, essentially muting the open. + * + * @name cut + * @param {number | Pattern} group cut group number + * @example + * s("rd*4").cut(1) + * + */ + ['cut'], + /** + * Applies the cutoff frequency of the **l**ow-**p**ass **f**ilter. + * + * When using mininotation, you can also optionally add the 'lpq' parameter, separated by ':'. + * + * @name lpf + * @param {number | Pattern} frequency audible between 0 and 20000 + * @synonyms cutoff, ctf, lp + * @example + * s("bd sd,hh*3").lpf("<4000 2000 1000 500 200 100>") + * @example + * s("bd*8").lpf("1000:0 1000:10 1000:20 1000:30") + * + */ + [['cutoff', 'resonance'], 'ctf', 'lpf', 'lp'], - /** - * Sets the lowpass filter envelope modulation depth. - * @name lpenv - * @param {number | Pattern} modulation depth of the lowpass filter envelope between 0 and _n_ - * @synonyms lpe - * @example - * note("") - * .sound('sawtooth') - * .lpf(500) - * .lpa(.5) - * .lpenv("<4 2 1 0 -1 -2 -4>/4") - */ - ['lpenv', 'lpe'], - /** - * Sets the highpass filter envelope modulation depth. - * @name hpenv - * @param {number | Pattern} modulation depth of the highpass filter envelope between 0 and _n_ - * @synonyms hpe - * @example - * note("") - * .sound('sawtooth') - * .hpf(500) - * .hpa(.5) - * .hpenv("<4 2 1 0 -1 -2 -4>/4") - */ - ['hpenv', 'hpe'], - /** - * Sets the bandpass filter envelope modulation depth. - * @name bpenv - * @param {number | Pattern} modulation depth of the bandpass filter envelope between 0 and _n_ - * @synonyms bpe - * @example - * note("") - * .sound('sawtooth') - * .bpf(500) - * .bpa(.5) - * .bpenv("<4 2 1 0 -1 -2 -4>/4") - */ - ['bpenv', 'bpe'], - /** - * Sets the attack duration for the lowpass filter envelope. - * @name lpattack - * @param {number | Pattern} attack time of the filter envelope - * @synonyms lpa - * @example - * note("") - * .sound('sawtooth') - * .lpf(500) - * .lpa("<.5 .25 .1 .01>/4") - * .lpenv(4) - */ - ['lpattack', 'lpa'], - /** - * Sets the attack duration for the highpass filter envelope. - * @name hpattack - * @param {number | Pattern} attack time of the highpass filter envelope - * @synonyms hpa - * @example - * note("") - * .sound('sawtooth') - * .hpf(500) - * .hpa("<.5 .25 .1 .01>/4") - * .hpenv(4) - */ - ['hpattack', 'hpa'], - /** - * Sets the attack duration for the bandpass filter envelope. - * @name bpattack - * @param {number | Pattern} attack time of the bandpass filter envelope - * @synonyms bpa - * @example - * note("") - * .sound('sawtooth') - * .bpf(500) - * .bpa("<.5 .25 .1 .01>/4") - * .bpenv(4) - */ - ['bpattack', 'bpa'], - /** - * Sets the decay duration for the lowpass filter envelope. - * @name lpdecay - * @param {number | Pattern} decay time of the filter envelope - * @synonyms lpd - * @example - * note("") - * .sound('sawtooth') - * .lpf(500) - * .lpd("<.5 .25 .1 0>/4") - * .lps(0.2) - * .lpenv(4) - */ - ['lpdecay', 'lpd'], - /** - * Sets the decay duration for the highpass filter envelope. - * @name hpdecay - * @param {number | Pattern} decay time of the highpass filter envelope - * @synonyms hpd - * @example - * note("") - * .sound('sawtooth') - * .hpf(500) - * .hpd("<.5 .25 .1 0>/4") - * .hps(0.2) - * .hpenv(4) - */ - ['hpdecay', 'hpd'], - /** - * Sets the decay duration for the bandpass filter envelope. - * @name bpdecay - * @param {number | Pattern} decay time of the bandpass filter envelope - * @synonyms bpd - * @example - * note("") - * .sound('sawtooth') - * .bpf(500) - * .bpd("<.5 .25 .1 0>/4") - * .bps(0.2) - * .bpenv(4) - */ - ['bpdecay', 'bpd'], - /** - * Sets the sustain amplitude for the lowpass filter envelope. - * @name lpsustain - * @param {number | Pattern} sustain amplitude of the lowpass filter envelope - * @synonyms lps - * @example - * note("") - * .sound('sawtooth') - * .lpf(500) - * .lpd(.5) - * .lps("<0 .25 .5 1>/4") - * .lpenv(4) - */ - ['lpsustain', 'lps'], - /** - * Sets the sustain amplitude for the highpass filter envelope. - * @name hpsustain - * @param {number | Pattern} sustain amplitude of the highpass filter envelope - * @synonyms hps - * @example - * note("") - * .sound('sawtooth') - * .hpf(500) - * .hpd(.5) - * .hps("<0 .25 .5 1>/4") - * .hpenv(4) - */ - ['hpsustain', 'hps'], - /** - * Sets the sustain amplitude for the bandpass filter envelope. - * @name bpsustain - * @param {number | Pattern} sustain amplitude of the bandpass filter envelope - * @synonyms bps - * @example - * note("") - * .sound('sawtooth') - * .bpf(500) - * .bpd(.5) - * .bps("<0 .25 .5 1>/4") - * .bpenv(4) - */ - ['bpsustain', 'bps'], - /** - * Sets the release time for the lowpass filter envelope. - * @name lprelease - * @param {number | Pattern} release time of the filter envelope - * @synonyms lpr - * @example - * note("") - * .sound('sawtooth') - * .clip(.5) - * .lpf(500) - * .lpenv(4) - * .lpr("<.5 .25 .1 0>/4") - * .release(.5) - */ - ['lprelease', 'lpr'], - /** - * Sets the release time for the highpass filter envelope. - * @name hprelease - * @param {number | Pattern} release time of the highpass filter envelope - * @synonyms hpr - * @example - * note("") - * .sound('sawtooth') - * .clip(.5) - * .hpf(500) - * .hpenv(4) - * .hpr("<.5 .25 .1 0>/4") - * .release(.5) - */ - ['hprelease', 'hpr'], - /** - * Sets the release time for the bandpass filter envelope. - * @name bprelease - * @param {number | Pattern} release time of the bandpass filter envelope - * @synonyms bpr - * @example - * note("") - * .sound('sawtooth') - * .clip(.5) - * .bpf(500) - * .bpenv(4) - * .bpr("<.5 .25 .1 0>/4") - * .release(.5) - */ - ['bprelease', 'bpr'], - /** - * Sets the filter type. The 24db filter is more aggressive. More types might be added in the future. - * @name ftype - * @param {number | Pattern} type 12db (default) or 24db - * @example - * note("") - * .sound('sawtooth') - * .lpf(500) - * .bpenv(4) - * .ftype("<12db 24db>") - */ - ['ftype'], - ['fanchor'], - /** - * Applies the cutoff frequency of the **h**igh-**p**ass **f**ilter. - * - * When using mininotation, you can also optionally add the 'hpq' parameter, separated by ':'. - * - * @name hpf - * @param {number | Pattern} frequency audible between 0 and 20000 - * @synonyms hp, hcutoff - * @example - * s("bd sd,hh*4").hpf("<4000 2000 1000 500 200 100>") - * @example - * s("bd sd,hh*4").hpf("<2000 2000:25>") - * - */ - // currently an alias of 'hcutoff' https://github.com/tidalcycles/strudel/issues/496 - // ['hpf'], - /** - * Applies a vibrato to the frequency of the oscillator. - * - * @name vib - * @synonyms vibrato, v - * @param {number | Pattern} frequency of the vibrato in hertz - * @example - * note("a") - * .vib("<.5 1 2 4 8 16>") - * @example - * // change the modulation depth with ":" - * note("a") - * .vib("<.5 1 2 4 8 16>:12") - */ - [['vib', 'vibmod'], 'vibrato', 'v'], - /** - * Sets the vibrato depth in semitones. Only has an effect if `vibrato` | `vib` | `v` is is also set - * - * @name vibmod - * @synonyms vmod - * @param {number | Pattern} depth of vibrato (in semitones) - * @example - * note("a").vib(4) - * .vibmod("<.25 .5 1 2 12>") - * @example - * // change the vibrato frequency with ":" - * note("a") - * .vibmod("<.25 .5 1 2 12>:8") - */ - [['vibmod', 'vib'], 'vmod'], - [['hcutoff', 'hresonance'], 'hpf', 'hp'], - /** - * Controls the **h**igh-**p**ass **q**-value. - * - * @name hpq - * @param {number | Pattern} q resonance factor between 0 and 50 - * @synonyms hresonance - * @example - * s("bd sd,hh*4").hpf(2000).hpq("<0 10 20 30>") - * - */ - ['hresonance', 'hpq'], - /** - * Controls the **l**ow-**p**ass **q**-value. - * - * @name lpq - * @param {number | Pattern} q resonance factor between 0 and 50 - * @synonyms resonance - * @example - * s("bd sd,hh*4").lpf(2000).lpq("<0 10 20 30>") - * - */ - // currently an alias of 'resonance' https://github.com/tidalcycles/strudel/issues/496 - ['resonance', 'lpq'], - /** - * DJ filter, below 0.5 is low pass filter, above is high pass filter. - * - * @name djf - * @param {number | Pattern} cutoff below 0.5 is low pass filter, above is high pass filter - * @example - * n("0 3 7 [10,24]").s('superzow').octave(3).djf("<.5 .25 .5 .75>").osc() - * - */ - ['djf'], - // ['cutoffegint'], - // TODO: does not seem to work - /** - * Sets the level of the delay signal. - * - * When using mininotation, you can also optionally add the 'delaytime' and 'delayfeedback' parameter, - * separated by ':'. - * - * - * @name delay - * @param {number | Pattern} level between 0 and 1 - * @example - * s("bd").delay("<0 .25 .5 1>") - * @example - * s("bd bd").delay("0.65:0.25:0.9 0.65:0.125:0.7") - * - */ - [['delay', 'delaytime', 'delayfeedback']], - /** - * Sets the level of the signal that is fed back into the delay. - * Caution: Values >= 1 will result in a signal that gets louder and louder! Don't do it - * - * @name delayfeedback - * @param {number | Pattern} feedback between 0 and 1 - * @synonyms delayfb, dfb - * @example - * s("bd").delay(.25).delayfeedback("<.25 .5 .75 1>").slow(2) - * - */ - ['delayfeedback', 'delayfb', 'dfb'], - /** - * Sets the time of the delay effect. - * - * @name delaytime - * @param {number | Pattern} seconds between 0 and Infinity - * @synonyms delayt, dt - * @example - * s("bd").delay(.25).delaytime("<.125 .25 .5 1>").slow(2) - * - */ - ['delaytime', 'delayt', 'dt'], - /* // TODO: test - * Specifies whether delaytime is calculated relative to cps. - * - * @name lock - * @param {number | Pattern} enable When set to 1, delaytime is a direct multiple of a cycle. - * @example - * s("sd").delay().lock(1).osc() - * - */ - ['lock'], - /** - * Set detune of oscillators. Works only with some synths, see tidal doc - * - * @name detune - * @param {number | Pattern} amount between 0 and 1 - * @synonyms det - * @superdirtOnly - * @example - * n("0 3 7").s('superzow').octave(3).detune("<0 .25 .5 1 2>").osc() - * - */ - ['detune', 'det'], - /** - * Set dryness of reverb. See {@link room} and {@link size} for more information about reverb. - * - * @name dry - * @param {number | Pattern} dry 0 = wet, 1 = dry - * @example - * n("[0,3,7](3,8)").s("superpiano").room(.7).dry("<0 .5 .75 1>").osc() - * @superdirtOnly - * - */ - ['dry'], - // TODO: does not seem to do anything - /* - * Used when using {@link begin}/{@link end} or {@link chop}/{@link striate} and friends, to change the fade out time of the 'grain' envelope. - * - * @name fadeTime - * @param {number | Pattern} time between 0 and 1 - * @example - * s("oh*4").end(.1).fadeTime("<0 .2 .4 .8>").osc() - * - */ - ['fadeTime', 'fadeOutTime'], - // TODO: see above - ['fadeInTime'], - /** - * Set frequency of sound. - * - * @name freq - * @param {number | Pattern} frequency in Hz. the audible range is between 20 and 20000 Hz - * @example - * freq("220 110 440 110").s("superzow").osc() - * @example - * freq("110".mul.out(".5 1.5 .6 [2 3]")).s("superzow").osc() - * - */ - ['freq'], - // TODO: https://tidalcycles.org/docs/configuration/MIDIOSC/control-voltage/#gate - ['gate', 'gat'], - // ['hatgrain'], - // ['lagogo'], - // ['lclap'], - // ['lclaves'], - // ['lclhat'], - // ['lcrash'], - // TODO: - // https://tidalcycles.org/docs/reference/audio_effects/#leslie-1 - // https://tidalcycles.org/docs/reference/audio_effects/#leslie - /** - * Emulation of a Leslie speaker: speakers rotating in a wooden amplified cabinet. - * - * @name leslie - * @param {number | Pattern} wet between 0 and 1 - * @example - * n("0,4,7").s("supersquare").leslie("<0 .4 .6 1>").osc() - * @superdirtOnly - * - */ - ['leslie'], - /** - * Rate of modulation / rotation for leslie effect - * - * @name lrate - * @param {number | Pattern} rate 6.7 for fast, 0.7 for slow - * @example - * n("0,4,7").s("supersquare").leslie(1).lrate("<1 2 4 8>").osc() - * @superdirtOnly - * - */ - // TODO: the rate seems to "lag" (in the example, 1 will be fast) - ['lrate'], - /** - * Physical size of the cabinet in meters. Be careful, it might be slightly larger than your computer. Affects the Doppler amount (pitch warble) - * - * @name lsize - * @param {number | Pattern} meters somewhere between 0 and 1 - * @example - * n("0,4,7").s("supersquare").leslie(1).lrate(2).lsize("<.1 .5 1>").osc() - * @superdirtOnly - * - */ - ['lsize'], - // label for pianoroll - ['activeLabel'], - [['label', 'activeLabel']], - // ['lfo'], - // ['lfocutoffint'], - // ['lfodelay'], - // ['lfoint'], - // ['lfopitchint'], - // ['lfoshape'], - // ['lfosync'], - // ['lhitom'], - // ['lkick'], - // ['llotom'], - // ['lophat'], - // ['lsnare'], - ['degree'], // TODO: what is this? not found in tidal doc - ['mtranspose'], // TODO: what is this? not found in tidal doc - ['ctranspose'], // TODO: what is this? not found in tidal doc - ['harmonic'], // TODO: what is this? not found in tidal doc - ['stepsPerOctave'], // TODO: what is this? not found in tidal doc - ['octaveR'], // TODO: what is this? not found in tidal doc - // TODO: why is this needed? what's the difference to late / early? Answer: it's in seconds, and delays the message at - // OSC time (so can't be negative, at least not beyond the latency value) - ['nudge'], - // TODO: the following doc is just a guess, it's not documented in tidal doc. - /** - * Sets the default octave of a synth. - * - * @name octave - * @param {number | Pattern} octave octave number - * @example - * n("0,4,7").s('supersquare').octave("<3 4 5 6>").osc() - * @superDirtOnly - */ - ['octave'], + /** + * Sets the lowpass filter envelope modulation depth. + * @name lpenv + * @param {number | Pattern} modulation depth of the lowpass filter envelope between 0 and _n_ + * @synonyms lpe + * @example + * note("") + * .sound('sawtooth') + * .lpf(500) + * .lpa(.5) + * .lpenv("<4 2 1 0 -1 -2 -4>/4") + */ + ['lpenv', 'lpe'], + /** + * Sets the highpass filter envelope modulation depth. + * @name hpenv + * @param {number | Pattern} modulation depth of the highpass filter envelope between 0 and _n_ + * @synonyms hpe + * @example + * note("") + * .sound('sawtooth') + * .hpf(500) + * .hpa(.5) + * .hpenv("<4 2 1 0 -1 -2 -4>/4") + */ + ['hpenv', 'hpe'], + /** + * Sets the bandpass filter envelope modulation depth. + * @name bpenv + * @param {number | Pattern} modulation depth of the bandpass filter envelope between 0 and _n_ + * @synonyms bpe + * @example + * note("") + * .sound('sawtooth') + * .bpf(500) + * .bpa(.5) + * .bpenv("<4 2 1 0 -1 -2 -4>/4") + */ + ['bpenv', 'bpe'], + /** + * Sets the attack duration for the lowpass filter envelope. + * @name lpattack + * @param {number | Pattern} attack time of the filter envelope + * @synonyms lpa + * @example + * note("") + * .sound('sawtooth') + * .lpf(500) + * .lpa("<.5 .25 .1 .01>/4") + * .lpenv(4) + */ + ['lpattack', 'lpa'], + /** + * Sets the attack duration for the highpass filter envelope. + * @name hpattack + * @param {number | Pattern} attack time of the highpass filter envelope + * @synonyms hpa + * @example + * note("") + * .sound('sawtooth') + * .hpf(500) + * .hpa("<.5 .25 .1 .01>/4") + * .hpenv(4) + */ + ['hpattack', 'hpa'], + /** + * Sets the attack duration for the bandpass filter envelope. + * @name bpattack + * @param {number | Pattern} attack time of the bandpass filter envelope + * @synonyms bpa + * @example + * note("") + * .sound('sawtooth') + * .bpf(500) + * .bpa("<.5 .25 .1 .01>/4") + * .bpenv(4) + */ + ['bpattack', 'bpa'], + /** + * Sets the decay duration for the lowpass filter envelope. + * @name lpdecay + * @param {number | Pattern} decay time of the filter envelope + * @synonyms lpd + * @example + * note("") + * .sound('sawtooth') + * .lpf(500) + * .lpd("<.5 .25 .1 0>/4") + * .lps(0.2) + * .lpenv(4) + */ + ['lpdecay', 'lpd'], + /** + * Sets the decay duration for the highpass filter envelope. + * @name hpdecay + * @param {number | Pattern} decay time of the highpass filter envelope + * @synonyms hpd + * @example + * note("") + * .sound('sawtooth') + * .hpf(500) + * .hpd("<.5 .25 .1 0>/4") + * .hps(0.2) + * .hpenv(4) + */ + ['hpdecay', 'hpd'], + /** + * Sets the decay duration for the bandpass filter envelope. + * @name bpdecay + * @param {number | Pattern} decay time of the bandpass filter envelope + * @synonyms bpd + * @example + * note("") + * .sound('sawtooth') + * .bpf(500) + * .bpd("<.5 .25 .1 0>/4") + * .bps(0.2) + * .bpenv(4) + */ + ['bpdecay', 'bpd'], + /** + * Sets the sustain amplitude for the lowpass filter envelope. + * @name lpsustain + * @param {number | Pattern} sustain amplitude of the lowpass filter envelope + * @synonyms lps + * @example + * note("") + * .sound('sawtooth') + * .lpf(500) + * .lpd(.5) + * .lps("<0 .25 .5 1>/4") + * .lpenv(4) + */ + ['lpsustain', 'lps'], + /** + * Sets the sustain amplitude for the highpass filter envelope. + * @name hpsustain + * @param {number | Pattern} sustain amplitude of the highpass filter envelope + * @synonyms hps + * @example + * note("") + * .sound('sawtooth') + * .hpf(500) + * .hpd(.5) + * .hps("<0 .25 .5 1>/4") + * .hpenv(4) + */ + ['hpsustain', 'hps'], + /** + * Sets the sustain amplitude for the bandpass filter envelope. + * @name bpsustain + * @param {number | Pattern} sustain amplitude of the bandpass filter envelope + * @synonyms bps + * @example + * note("") + * .sound('sawtooth') + * .bpf(500) + * .bpd(.5) + * .bps("<0 .25 .5 1>/4") + * .bpenv(4) + */ + ['bpsustain', 'bps'], + /** + * Sets the release time for the lowpass filter envelope. + * @name lprelease + * @param {number | Pattern} release time of the filter envelope + * @synonyms lpr + * @example + * note("") + * .sound('sawtooth') + * .clip(.5) + * .lpf(500) + * .lpenv(4) + * .lpr("<.5 .25 .1 0>/4") + * .release(.5) + */ + ['lprelease', 'lpr'], + /** + * Sets the release time for the highpass filter envelope. + * @name hprelease + * @param {number | Pattern} release time of the highpass filter envelope + * @synonyms hpr + * @example + * note("") + * .sound('sawtooth') + * .clip(.5) + * .hpf(500) + * .hpenv(4) + * .hpr("<.5 .25 .1 0>/4") + * .release(.5) + */ + ['hprelease', 'hpr'], + /** + * Sets the release time for the bandpass filter envelope. + * @name bprelease + * @param {number | Pattern} release time of the bandpass filter envelope + * @synonyms bpr + * @example + * note("") + * .sound('sawtooth') + * .clip(.5) + * .bpf(500) + * .bpenv(4) + * .bpr("<.5 .25 .1 0>/4") + * .release(.5) + */ + ['bprelease', 'bpr'], + /** + * Sets the filter type. The 24db filter is more aggressive. More types might be added in the future. + * @name ftype + * @param {number | Pattern} type 12db (default) or 24db + * @example + * note("") + * .sound('sawtooth') + * .lpf(500) + * .bpenv(4) + * .ftype("<12db 24db>") + */ + ['ftype'], + ['fanchor'], + /** + * Applies the cutoff frequency of the **h**igh-**p**ass **f**ilter. + * + * When using mininotation, you can also optionally add the 'hpq' parameter, separated by ':'. + * + * @name hpf + * @param {number | Pattern} frequency audible between 0 and 20000 + * @synonyms hp, hcutoff + * @example + * s("bd sd,hh*4").hpf("<4000 2000 1000 500 200 100>") + * @example + * s("bd sd,hh*4").hpf("<2000 2000:25>") + * + */ + // currently an alias of 'hcutoff' https://github.com/tidalcycles/strudel/issues/496 + // ['hpf'], + /** + * Applies a vibrato to the frequency of the oscillator. + * + * @name vib + * @synonyms vibrato, v + * @param {number | Pattern} frequency of the vibrato in hertz + * @example + * note("a") + * .vib("<.5 1 2 4 8 16>") + * @example + * // change the modulation depth with ":" + * note("a") + * .vib("<.5 1 2 4 8 16>:12") + */ + [['vib', 'vibmod'], 'vibrato', 'v'], + /** + * Sets the vibrato depth in semitones. Only has an effect if `vibrato` | `vib` | `v` is is also set + * + * @name vibmod + * @synonyms vmod + * @param {number | Pattern} depth of vibrato (in semitones) + * @example + * note("a").vib(4) + * .vibmod("<.25 .5 1 2 12>") + * @example + * // change the vibrato frequency with ":" + * note("a") + * .vibmod("<.25 .5 1 2 12>:8") + */ + [['vibmod', 'vib'], 'vmod'], + [['hcutoff', 'hresonance'], 'hpf', 'hp'], + /** + * Controls the **h**igh-**p**ass **q**-value. + * + * @name hpq + * @param {number | Pattern} q resonance factor between 0 and 50 + * @synonyms hresonance + * @example + * s("bd sd,hh*4").hpf(2000).hpq("<0 10 20 30>") + * + */ + ['hresonance', 'hpq'], + /** + * Controls the **l**ow-**p**ass **q**-value. + * + * @name lpq + * @param {number | Pattern} q resonance factor between 0 and 50 + * @synonyms resonance + * @example + * s("bd sd,hh*4").lpf(2000).lpq("<0 10 20 30>") + * + */ + // currently an alias of 'resonance' https://github.com/tidalcycles/strudel/issues/496 + ['resonance', 'lpq'], + /** + * DJ filter, below 0.5 is low pass filter, above is high pass filter. + * + * @name djf + * @param {number | Pattern} cutoff below 0.5 is low pass filter, above is high pass filter + * @example + * n("0 3 7 [10,24]").s('superzow').octave(3).djf("<.5 .25 .5 .75>").osc() + * + */ + ['djf'], + // ['cutoffegint'], + // TODO: does not seem to work + /** + * Sets the level of the delay signal. + * + * When using mininotation, you can also optionally add the 'delaytime' and 'delayfeedback' parameter, + * separated by ':'. + * + * + * @name delay + * @param {number | Pattern} level between 0 and 1 + * @example + * s("bd").delay("<0 .25 .5 1>") + * @example + * s("bd bd").delay("0.65:0.25:0.9 0.65:0.125:0.7") + * + */ + [['delay', 'delaytime', 'delayfeedback']], + /** + * Sets the level of the signal that is fed back into the delay. + * Caution: Values >= 1 will result in a signal that gets louder and louder! Don't do it + * + * @name delayfeedback + * @param {number | Pattern} feedback between 0 and 1 + * @synonyms delayfb, dfb + * @example + * s("bd").delay(.25).delayfeedback("<.25 .5 .75 1>").slow(2) + * + */ + ['delayfeedback', 'delayfb', 'dfb'], + /** + * Sets the time of the delay effect. + * + * @name delaytime + * @param {number | Pattern} seconds between 0 and Infinity + * @synonyms delayt, dt + * @example + * s("bd").delay(.25).delaytime("<.125 .25 .5 1>").slow(2) + * + */ + ['delaytime', 'delayt', 'dt'], + /* // TODO: test + * Specifies whether delaytime is calculated relative to cps. + * + * @name lock + * @param {number | Pattern} enable When set to 1, delaytime is a direct multiple of a cycle. + * @example + * s("sd").delay().lock(1).osc() + * + */ + ['lock'], + /** + * Set detune of oscillators. Works only with some synths, see tidal doc + * + * @name detune + * @param {number | Pattern} amount between 0 and 1 + * @synonyms det + * @superdirtOnly + * @example + * n("0 3 7").s('superzow').octave(3).detune("<0 .25 .5 1 2>").osc() + * + */ + ['detune', 'det'], + /** + * Set dryness of reverb. See {@link room} and {@link size} for more information about reverb. + * + * @name dry + * @param {number | Pattern} dry 0 = wet, 1 = dry + * @example + * n("[0,3,7](3,8)").s("superpiano").room(.7).dry("<0 .5 .75 1>").osc() + * @superdirtOnly + * + */ + ['dry'], + // TODO: does not seem to do anything + /* + * Used when using {@link begin}/{@link end} or {@link chop}/{@link striate} and friends, to change the fade out time of the 'grain' envelope. + * + * @name fadeTime + * @param {number | Pattern} time between 0 and 1 + * @example + * s("oh*4").end(.1).fadeTime("<0 .2 .4 .8>").osc() + * + */ + ['fadeTime', 'fadeOutTime'], + // TODO: see above + ['fadeInTime'], + /** + * Set frequency of sound. + * + * @name freq + * @param {number | Pattern} frequency in Hz. the audible range is between 20 and 20000 Hz + * @example + * freq("220 110 440 110").s("superzow").osc() + * @example + * freq("110".mul.out(".5 1.5 .6 [2 3]")).s("superzow").osc() + * + */ + ['freq'], + // TODO: https://tidalcycles.org/docs/configuration/MIDIOSC/control-voltage/#gate + ['gate', 'gat'], + // ['hatgrain'], + // ['lagogo'], + // ['lclap'], + // ['lclaves'], + // ['lclhat'], + // ['lcrash'], + // TODO: + // https://tidalcycles.org/docs/reference/audio_effects/#leslie-1 + // https://tidalcycles.org/docs/reference/audio_effects/#leslie + /** + * Emulation of a Leslie speaker: speakers rotating in a wooden amplified cabinet. + * + * @name leslie + * @param {number | Pattern} wet between 0 and 1 + * @example + * n("0,4,7").s("supersquare").leslie("<0 .4 .6 1>").osc() + * @superdirtOnly + * + */ + ['leslie'], + /** + * Rate of modulation / rotation for leslie effect + * + * @name lrate + * @param {number | Pattern} rate 6.7 for fast, 0.7 for slow + * @example + * n("0,4,7").s("supersquare").leslie(1).lrate("<1 2 4 8>").osc() + * @superdirtOnly + * + */ + // TODO: the rate seems to "lag" (in the example, 1 will be fast) + ['lrate'], + /** + * Physical size of the cabinet in meters. Be careful, it might be slightly larger than your computer. Affects the Doppler amount (pitch warble) + * + * @name lsize + * @param {number | Pattern} meters somewhere between 0 and 1 + * @example + * n("0,4,7").s("supersquare").leslie(1).lrate(2).lsize("<.1 .5 1>").osc() + * @superdirtOnly + * + */ + ['lsize'], + // label for pianoroll + ['activeLabel'], + [['label', 'activeLabel']], + // ['lfo'], + // ['lfocutoffint'], + // ['lfodelay'], + // ['lfoint'], + // ['lfopitchint'], + // ['lfoshape'], + // ['lfosync'], + // ['lhitom'], + // ['lkick'], + // ['llotom'], + // ['lophat'], + // ['lsnare'], + ['degree'], // TODO: what is this? not found in tidal doc + ['mtranspose'], // TODO: what is this? not found in tidal doc + ['ctranspose'], // TODO: what is this? not found in tidal doc + ['harmonic'], // TODO: what is this? not found in tidal doc + ['stepsPerOctave'], // TODO: what is this? not found in tidal doc + ['octaveR'], // TODO: what is this? not found in tidal doc + // TODO: why is this needed? what's the difference to late / early? Answer: it's in seconds, and delays the message at + // OSC time (so can't be negative, at least not beyond the latency value) + ['nudge'], + // TODO: the following doc is just a guess, it's not documented in tidal doc. + /** + * Sets the default octave of a synth. + * + * @name octave + * @param {number | Pattern} octave octave number + * @example + * n("0,4,7").s('supersquare').octave("<3 4 5 6>").osc() + * @superDirtOnly + */ + ['octave'], - // ['ophatdecay'], - // TODO: example - /** - * An `orbit` is a global parameter context for patterns. Patterns with the same orbit will share the same global effects. - * - * @name orbit - * @param {number | Pattern} number - * @example - * stack( - * s("hh*3").delay(.5).delaytime(.25).orbit(1), - * s("~ sd").delay(.5).delaytime(.125).orbit(2) - * ) - */ - ['orbit'], - ['overgain'], // TODO: what is this? not found in tidal doc Answer: gain is limited to maximum of 2. This allows you to go over that - ['overshape'], // TODO: what is this? not found in tidal doc. Similar to above, but limited to 1 - /** - * Sets position in stereo. - * - * @name pan - * @param {number | Pattern} pan between 0 and 1, from left to right (assuming stereo), once round a circle (assuming multichannel) - * @example - * s("[bd hh]*2").pan("<.5 1 .5 0>") - * - */ - ['pan'], - // TODO: this has no effect (see example) - /* - * Controls how much multichannel output is fanned out - * - * @name panspan - * @param {number | Pattern} span between -inf and inf, negative is backwards ordering - * @example - * s("[bd hh]*2").pan("<.5 1 .5 0>").panspan("<0 .5 1>").osc() - * - */ - ['panspan'], - // TODO: this has no effect (see example) - /* - * Controls how much multichannel output is spread - * - * @name pansplay - * @param {number | Pattern} spread between 0 and 1 - * @example - * s("[bd hh]*2").pan("<.5 1 .5 0>").pansplay("<0 .5 1>").osc() - * - */ - ['pansplay'], - ['panwidth'], - ['panorient'], - // ['pitch1'], - // ['pitch2'], - // ['pitch3'], - // ['portamento'], - // TODO: LFO rate see https://tidalcycles.org/docs/patternlib/tutorials/synthesizers/#supersquare - ['rate'], - // TODO: slide param for certain synths - ['slide'], - // TODO: detune? https://tidalcycles.org/docs/patternlib/tutorials/synthesizers/#supersquare - ['semitone'], - // TODO: dedup with synth param, see https://tidalcycles.org/docs/reference/synthesizers/#superpiano - // ['velocity'], - ['voice'], // TODO: synth param + // ['ophatdecay'], + // TODO: example + /** + * An `orbit` is a global parameter context for patterns. Patterns with the same orbit will share the same global effects. + * + * @name orbit + * @param {number | Pattern} number + * @example + * stack( + * s("hh*3").delay(.5).delaytime(.25).orbit(1), + * s("~ sd").delay(.5).delaytime(.125).orbit(2) + * ) + */ + ['orbit'], + ['overgain'], // TODO: what is this? not found in tidal doc Answer: gain is limited to maximum of 2. This allows you to go over that + ['overshape'], // TODO: what is this? not found in tidal doc. Similar to above, but limited to 1 + /** + * Sets position in stereo. + * + * @name pan + * @param {number | Pattern} pan between 0 and 1, from left to right (assuming stereo), once round a circle (assuming multichannel) + * @example + * s("[bd hh]*2").pan("<.5 1 .5 0>") + * + */ + ['pan'], + // TODO: this has no effect (see example) + /* + * Controls how much multichannel output is fanned out + * + * @name panspan + * @param {number | Pattern} span between -inf and inf, negative is backwards ordering + * @example + * s("[bd hh]*2").pan("<.5 1 .5 0>").panspan("<0 .5 1>").osc() + * + */ + ['panspan'], + // TODO: this has no effect (see example) + /* + * Controls how much multichannel output is spread + * + * @name pansplay + * @param {number | Pattern} spread between 0 and 1 + * @example + * s("[bd hh]*2").pan("<.5 1 .5 0>").pansplay("<0 .5 1>").osc() + * + */ + ['pansplay'], + ['panwidth'], + ['panorient'], + // ['pitch1'], + // ['pitch2'], + // ['pitch3'], + // ['portamento'], + // TODO: LFO rate see https://tidalcycles.org/docs/patternlib/tutorials/synthesizers/#supersquare + ['rate'], + // TODO: slide param for certain synths + ['slide'], + // TODO: detune? https://tidalcycles.org/docs/patternlib/tutorials/synthesizers/#supersquare + ['semitone'], + // TODO: dedup with synth param, see https://tidalcycles.org/docs/reference/synthesizers/#superpiano + // ['velocity'], + ['voice'], // TODO: synth param - // voicings // https://github.com/tidalcycles/strudel/issues/506 - ['chord'], // chord to voice, like C Eb Fm7 G7. the symbols can be defined via addVoicings - ['dictionary', 'dict'], // which dictionary to use for the voicings - ['anchor'], // the top note to align the voicing to, defaults to c5 - ['offset'], // how the voicing is offset from the anchored position - ['octaves'], // how many octaves are voicing steps spread apart, defaults to 1 - [['mode', 'anchor']], // below = anchor note will be removed from the voicing, useful for melody harmonization + // voicings // https://github.com/tidalcycles/strudel/issues/506 + ['chord'], // chord to voice, like C Eb Fm7 G7. the symbols can be defined via addVoicings + ['dictionary', 'dict'], // which dictionary to use for the voicings + ['anchor'], // the top note to align the voicing to, defaults to c5 + ['offset'], // how the voicing is offset from the anchored position + ['octaves'], // how many octaves are voicing steps spread apart, defaults to 1 + [['mode', 'anchor']], // below = anchor note will be removed from the voicing, useful for melody harmonization - /** - * Sets the level of reverb. - * - * When using mininotation, you can also optionally add the 'size' parameter, separated by ':'. - * - * @name room - * @param {number | Pattern} level between 0 and 1 - * @example - * s("bd sd").room("<0 .2 .4 .6 .8 1>") - * @example - * s("bd sd").room("<0.9:1 0.9:4>") - * - */ - [['room', 'size']], - /** - * Sets the room size of the reverb, see {@link room}. - * - * @name roomsize - * @param {number | Pattern} size between 0 and 10 - * @synonyms size, sz - * @example - * s("bd sd").room(.8).roomsize("<0 1 2 4 8>") - * - */ - // TODO: find out why : - // s("bd sd").room(.8).roomsize("<0 .2 .4 .6 .8 [1,0]>").osc() - // .. does not work. Is it because room is only one effect? - ['size', 'sz', 'roomsize'], - // ['sagogo'], - // ['sclap'], - // ['sclaves'], - // ['scrash'], - /** - * Wave shaping distortion. CAUTION: it might get loud - * - * @name shape - * @param {number | Pattern} distortion between 0 and 1 - * @example - * s("bd sd,hh*4").shape("<0 .2 .4 .6 .8>") - * - */ - ['shape'], - /** - * Changes the speed of sample playback, i.e. a cheap way of changing pitch. - * - * @name speed - * @param {number | Pattern} speed -inf to inf, negative numbers play the sample backwards. - * @example - * s("bd").speed("<1 2 4 1 -2 -4>") - * @example - * speed("1 1.5*2 [2 1.1]").s("piano").clip(1) - * - */ - ['speed'], - /** - * Used in conjunction with {@link speed}, accepts values of "r" (rate, default behavior), "c" (cycles), or "s" (seconds). Using `unit "c"` means `speed` will be interpreted in units of cycles, e.g. `speed "1"` means samples will be stretched to fill a cycle. Using `unit "s"` means the playback speed will be adjusted so that the duration is the number of seconds specified by `speed`. - * - * @name unit - * @param {number | string | Pattern} unit see description above - * @example - * speed("1 2 .5 3").s("bd").unit("c").osc() - * @superdirtOnly - * - */ - ['unit'], - /** - * Made by Calum Gunn. Reminiscent of some weird mixture of filter, ring-modulator and pitch-shifter. The SuperCollider manual defines Squiz as: - * - * "A simplistic pitch-raising algorithm. It's not meant to sound natural; its sound is reminiscent of some weird mixture of filter, ring-modulator and pitch-shifter, depending on the input. The algorithm works by cutting the signal into fragments (delimited by upwards-going zero-crossings) and squeezing those fragments in the time domain (i.e. simply playing them back faster than they came in), leaving silences inbetween. All the parameters apart from memlen can be modulated." - * - * @name squiz - * @param {number | Pattern} squiz Try passing multiples of 2 to it - 2, 4, 8 etc. - * @example - * squiz("2 4/2 6 [8 16]").s("bd").osc() - * @superdirtOnly - * - */ - ['squiz'], - // ['stutterdepth'], // TODO: what is this? not found in tidal doc - // ['stuttertime'], // TODO: what is this? not found in tidal doc - // ['timescale'], // TODO: what is this? not found in tidal doc - // ['timescalewin'], // TODO: what is this? not found in tidal doc - // ['tomdecay'], - // ['vcfegint'], - // ['vcoegint'], - // TODO: Use a rest (~) to override the effect <- vowel - /** - * - * Formant filter to make things sound like vowels. - * - * @name vowel - * @param {string | Pattern} vowel You can use a e i o u. - * @example - * note("c2 >").s('sawtooth') - * .vowel(">") - * - */ - ['vowel'], - /* // TODO: find out how it works - * Made by Calum Gunn. Divides an audio stream into tiny segments, using the signal's zero-crossings as segment boundaries, and discards a fraction of them. Takes a number between 1 and 100, denoted the percentage of segments to drop. The SuperCollider manual describes the Waveloss effect this way: - * - * Divide an audio stream into tiny segments, using the signal's zero-crossings as segment boundaries, and discard a fraction of them (i.e. replace them with silence of the same length). The technique was described by Trevor Wishart in a lecture. Parameters: the filter drops drop out of out of chunks. mode can be 1 to drop chunks in a simple deterministic fashion (e.g. always dropping the first 30 out of a set of 40 segments), or 2 to drop chunks randomly but in an appropriate proportion.) - * - * mode: ? - * waveloss: ? - * - * @name waveloss - */ - ['waveloss'], - // TODO: midi effects? - ['dur'], - // ['modwheel'], - ['expression'], - ['sustainpedal'], - /* // TODO: doesn't seem to do anything - * - * Tremolo Audio DSP effect - * - * @name tremolodepth - * @param {number | Pattern} depth between 0 and 1 - * @example - * n("0,4,7").tremolodepth("<0 .3 .6 .9>").osc() - * - */ - ['tremolodepth', 'tremdp'], - ['tremolorate', 'tremr'], - // TODO: doesn't seem to do anything - ['phaserdepth', 'phasdp'], - ['phaserrate', 'phasr'], + /** + * Sets the level of reverb. + * + * When using mininotation, you can also optionally add the 'size' parameter, separated by ':'. + * + * @name room + * @param {number | Pattern} level between 0 and 1 + * @example + * s("bd sd").room("<0 .2 .4 .6 .8 1>") + * @example + * s("bd sd").room("<0.9:1 0.9:4>") + * + */ + [['room', 'size']], + /** + * Sets the room size of the reverb, see {@link room}. + * + * @name roomsize + * @param {number | Pattern} size between 0 and 10 + * @synonyms size, sz + * @example + * s("bd sd").room(.8).roomsize("<0 1 2 4 8>") + * + */ + // TODO: find out why : + // s("bd sd").room(.8).roomsize("<0 .2 .4 .6 .8 [1,0]>").osc() + // .. does not work. Is it because room is only one effect? + ['size', 'sz', 'roomsize'], + // ['sagogo'], + // ['sclap'], + // ['sclaves'], + // ['scrash'], - ['fshift'], - ['fshiftnote'], - ['fshiftphase'], + /** + * Sets the sample to use as an impulse response for the reverb. + * + * @name iresponse + * @param {string | Pattern} Sets the impulse response + * @example + * s("bd sd").room(.8).ir("") + * + */ + [['ir', 'i'], 'iresponse'], + /** + * Wave shaping distortion. CAUTION: it might get loud + * + * @name shape + * @param {number | Pattern} distortion between 0 and 1 + * @example + * s("bd sd,hh*4").shape("<0 .2 .4 .6 .8>") + * + */ + ['shape'], + /** + * Changes the speed of sample playback, i.e. a cheap way of changing pitch. + * + * @name speed + * @param {number | Pattern} speed -inf to inf, negative numbers play the sample backwards. + * @example + * s("bd").speed("<1 2 4 1 -2 -4>") + * @example + * speed("1 1.5*2 [2 1.1]").s("piano").clip(1) + * + */ + ['speed'], + /** + * Used in conjunction with {@link speed}, accepts values of "r" (rate, default behavior), "c" (cycles), or "s" (seconds). Using `unit "c"` means `speed` will be interpreted in units of cycles, e.g. `speed "1"` means samples will be stretched to fill a cycle. Using `unit "s"` means the playback speed will be adjusted so that the duration is the number of seconds specified by `speed`. + * + * @name unit + * @param {number | string | Pattern} unit see description above + * @example + * speed("1 2 .5 3").s("bd").unit("c").osc() + * @superdirtOnly + * + */ + ['unit'], + /** + * Made by Calum Gunn. Reminiscent of some weird mixture of filter, ring-modulator and pitch-shifter. The SuperCollider manual defines Squiz as: + * + * "A simplistic pitch-raising algorithm. It's not meant to sound natural; its sound is reminiscent of some weird mixture of filter, ring-modulator and pitch-shifter, depending on the input. The algorithm works by cutting the signal into fragments (delimited by upwards-going zero-crossings) and squeezing those fragments in the time domain (i.e. simply playing them back faster than they came in), leaving silences inbetween. All the parameters apart from memlen can be modulated." + * + * @name squiz + * @param {number | Pattern} squiz Try passing multiples of 2 to it - 2, 4, 8 etc. + * @example + * squiz("2 4/2 6 [8 16]").s("bd").osc() + * @superdirtOnly + * + */ + ['squiz'], + // ['stutterdepth'], // TODO: what is this? not found in tidal doc + // ['stuttertime'], // TODO: what is this? not found in tidal doc + // ['timescale'], // TODO: what is this? not found in tidal doc + // ['timescalewin'], // TODO: what is this? not found in tidal doc + // ['tomdecay'], + // ['vcfegint'], + // ['vcoegint'], + // TODO: Use a rest (~) to override the effect <- vowel + /** + * + * Formant filter to make things sound like vowels. + * + * @name vowel + * @param {string | Pattern} vowel You can use a e i o u. + * @example + * note("c2 >").s('sawtooth') + * .vowel(">") + * + */ + ['vowel'], + /* // TODO: find out how it works + * Made by Calum Gunn. Divides an audio stream into tiny segments, using the signal's zero-crossings as segment boundaries, and discards a fraction of them. Takes a number between 1 and 100, denoted the percentage of segments to drop. The SuperCollider manual describes the Waveloss effect this way: + * + * Divide an audio stream into tiny segments, using the signal's zero-crossings as segment boundaries, and discard a fraction of them (i.e. replace them with silence of the same length). The technique was described by Trevor Wishart in a lecture. Parameters: the filter drops drop out of out of chunks. mode can be 1 to drop chunks in a simple deterministic fashion (e.g. always dropping the first 30 out of a set of 40 segments), or 2 to drop chunks randomly but in an appropriate proportion.) + * + * mode: ? + * waveloss: ? + * + * @name waveloss + */ + ['waveloss'], + // TODO: midi effects? + ['dur'], + // ['modwheel'], + ['expression'], + ['sustainpedal'], + /* // TODO: doesn't seem to do anything + * + * Tremolo Audio DSP effect + * + * @name tremolodepth + * @param {number | Pattern} depth between 0 and 1 + * @example + * n("0,4,7").tremolodepth("<0 .3 .6 .9>").osc() + * + */ + ['tremolodepth', 'tremdp'], + ['tremolorate', 'tremr'], + // TODO: doesn't seem to do anything + ['phaserdepth', 'phasdp'], + ['phaserrate', 'phasr'], - ['triode'], - ['krush'], - ['kcutoff'], - ['octer'], - ['octersub'], - ['octersubsub'], - ['ring'], - ['ringf'], - ['ringdf'], - ['distort'], - ['freeze'], - ['xsdelay'], - ['tsdelay'], - ['real'], - ['imag'], - ['enhance'], - ['partials'], - ['comb'], - ['smear'], - ['scram'], - ['binshift'], - ['hbrick'], - ['lbrick'], - ['midichan'], - ['control'], - ['ccn'], - ['ccv'], - ['polyTouch'], - ['midibend'], - ['miditouch'], - ['ctlNum'], - ['frameRate'], - ['frames'], - ['hours'], - ['midicmd'], - ['minutes'], - ['progNum'], - ['seconds'], - ['songPtr'], - ['uid'], - ['val'], - ['cps'], - /** - * Multiplies the duration with the given number. Also cuts samples off at the end if they exceed the duration. - * In tidal, this would be done with legato, [which has a complicated history in strudel](https://github.com/tidalcycles/strudel/issues/111). - * For now, if you're coming from tidal, just think clip = legato. - * - * @name clip - * @param {number | Pattern} factor >= 0 - * @example - * note("c a f e").s("piano").clip("<.5 1 2>") - * - */ - ['clip'], + ['fshift'], + ['fshiftnote'], + ['fshiftphase'], - // ZZFX - ['zrand'], - ['curve'], - ['slide'], // superdirt duplicate - ['deltaSlide'], - ['pitchJump'], - ['pitchJumpTime'], - ['lfo', 'repeatTime'], - ['noise'], - ['zmod'], - ['zcrush'], // like crush but scaled differently - ['zdelay'], - ['tremolo'], - ['zzfx'], + ['triode'], + ['krush'], + ['kcutoff'], + ['octer'], + ['octersub'], + ['octersubsub'], + ['ring'], + ['ringf'], + ['ringdf'], + ['distort'], + ['freeze'], + ['xsdelay'], + ['tsdelay'], + ['real'], + ['imag'], + ['enhance'], + ['partials'], + ['comb'], + ['smear'], + ['scram'], + ['binshift'], + ['hbrick'], + ['lbrick'], + ['midichan'], + ['control'], + ['ccn'], + ['ccv'], + ['polyTouch'], + ['midibend'], + ['miditouch'], + ['ctlNum'], + ['frameRate'], + ['frames'], + ['hours'], + ['midicmd'], + ['minutes'], + ['progNum'], + ['seconds'], + ['songPtr'], + ['uid'], + ['val'], + ['cps'], + /** + * Multiplies the duration with the given number. Also cuts samples off at the end if they exceed the duration. + * In tidal, this would be done with legato, [which has a complicated history in strudel](https://github.com/tidalcycles/strudel/issues/111). + * For now, if you're coming from tidal, just think clip = legato. + * + * @name clip + * @param {number | Pattern} factor >= 0 + * @example + * note("c a f e").s("piano").clip("<.5 1 2>") + * + */ + ['clip'], + + // ZZFX + ['zrand'], + ['curve'], + ['slide'], // superdirt duplicate + ['deltaSlide'], + ['pitchJump'], + ['pitchJumpTime'], + ['lfo', 'repeatTime'], + ['noise'], + ['zmod'], + ['zcrush'], // like crush but scaled differently + ['zdelay'], + ['tremolo'], + ['zzfx'], ]; // TODO: slice / splice https://www.youtube.com/watch?v=hKhPdO0RKDQ&list=PL2lW1zNIIwj3bDkh-Y3LUGDuRcoUigoDs&index=13 controls.createParam = function (names) { - const name = Array.isArray(names) ? names[0] : names; + const name = Array.isArray(names) ? names[0] : names; - var withVal; - if (Array.isArray(names)) { - withVal = (xs) => { - if (Array.isArray(xs)) { - const result = {}; - xs.forEach((x, i) => { - if (i < names.length) { - result[names[i]] = x; - } - }); - return result; - } else { - return { [name]: xs }; - } - }; - } else { - withVal = (x) => ({ [name]: x }); - } - - const func = (...pats) => sequence(...pats).withValue(withVal); - - const setter = function (...pats) { - if (!pats.length) { - return this.fmap(withVal); + var withVal; + if (Array.isArray(names)) { + withVal = (xs) => { + if (Array.isArray(xs)) { + const result = {}; + xs.forEach((x, i) => { + if (i < names.length) { + result[names[i]] = x; + } + }); + return result; + } else { + return {[name]: xs}; + } + }; + } else { + withVal = (x) => ({[name]: x}); } - return this.set(func(...pats)); - }; - Pattern.prototype[name] = setter; - return func; + + const func = (...pats) => sequence(...pats).withValue(withVal); + + const setter = function (...pats) { + if (!pats.length) { + return this.fmap(withVal); + } + return this.set(func(...pats)); + }; + Pattern.prototype[name] = setter; + return func; }; generic_params.forEach(([names, ...aliases]) => { - const name = Array.isArray(names) ? names[0] : names; - controls[name] = controls.createParam(names); + const name = Array.isArray(names) ? names[0] : names; + controls[name] = controls.createParam(names); - aliases.forEach((alias) => { - controls[alias] = controls[name]; - Pattern.prototype[alias] = Pattern.prototype[name]; - }); + aliases.forEach((alias) => { + controls[alias] = controls[name]; + Pattern.prototype[alias] = Pattern.prototype[name]; + }); }); controls.createParams = (...names) => - names.reduce((acc, name) => Object.assign(acc, { [name]: controls.createParam(name) }), {}); + names.reduce((acc, name) => Object.assign(acc, {[name]: controls.createParam(name)}), {}); controls.adsr = register('adsr', (adsr, pat) => { - adsr = !Array.isArray(adsr) ? [adsr] : adsr; - const [attack, decay, sustain, release] = adsr; - return pat.set({ attack, decay, sustain, release }); + adsr = !Array.isArray(adsr) ? [adsr] : adsr; + const [attack, decay, sustain, release] = adsr; + return pat.set({attack, decay, sustain, release}); }); controls.ds = register('ds', (ds, pat) => { - ds = !Array.isArray(ds) ? [ds] : ds; - const [decay, sustain] = ds; - return pat.set({ decay, sustain }); + ds = !Array.isArray(ds) ? [ds] : ds; + const [decay, sustain] = ds; + return pat.set({decay, sustain}); }); export default controls; diff --git a/packages/superdough/reverb.mjs b/packages/superdough/reverb.mjs index e6d31f6a..72709085 100644 --- a/packages/superdough/reverb.mjs +++ b/packages/superdough/reverb.mjs @@ -7,14 +7,22 @@ if (typeof AudioContext !== 'undefined') { return impulse; }; - AudioContext.prototype.createReverb = function (duration) { + AudioContext.prototype.createReverb = function (duration, buffer) { const convolver = this.createConvolver(); - convolver.setDuration = (d) => { - convolver.buffer = this.impulseResponse(d); - convolver.duration = duration; + convolver.setDuration = (d, i) => { + convolver.buffer = i !== undefined ? buffer : this.impulseResponse(d); + convolver.duration = d; return convolver; }; - convolver.setDuration(duration); + convolver.setIR = (i) => { + convolver.buffer = i; + return convolver; + }; + if (buffer !== undefined) { + convolver.setIR(buffer); + } else { + convolver.setDuration(duration); + } return convolver; }; } diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index 289e8d97..2b46ae3c 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -12,14 +12,18 @@ import workletsUrl from './worklets.mjs?url'; import { createFilter, gainNode } from './helpers.mjs'; import { map } from 'nanostores'; import { logger } from './logger.mjs'; +import { loadBuffer } from './sampler.mjs'; export const soundMap = map(); + export function registerSound(key, onTrigger, data = {}) { soundMap.setKey(key, { onTrigger, data }); } + export function getSound(s) { return soundMap.get()[s]; } + export const resetLoadedSounds = () => soundMap.set({}); let audioContext; @@ -46,6 +50,7 @@ export const panic = () => { }; let workletsLoading; + function loadWorklets() { if (workletsLoading) { return workletsLoading; @@ -89,6 +94,7 @@ export async function initAudioOnFirstClick(options) { let delays = {}; const maxfeedback = 0.98; + function getDelay(orbit, delaytime, delayfeedback, t) { if (delayfeedback > maxfeedback) { //logger(`delayfeedback was clamped to ${maxfeedback} to save your ears`); @@ -107,10 +113,11 @@ function getDelay(orbit, delaytime, delayfeedback, t) { } let reverbs = {}; -function getReverb(orbit, duration = 2) { + +function getReverb(orbit, duration = 2, ir) { if (!reverbs[orbit]) { const ac = getAudioContext(); - const reverb = ac.createReverb(duration); + const reverb = ac.createReverb(duration, ir); reverb.connect(getDestination()); reverbs[orbit] = reverb; } @@ -118,10 +125,15 @@ function getReverb(orbit, duration = 2) { reverbs[orbit] = reverbs[orbit].setDuration(duration); reverbs[orbit].duration = duration; } + if (reverbs[orbit].ir !== ir) { + reverbs[orbit] = reverbs[orbit].setIR(ir); + reverbs[orbit].ir = ir; + } return reverbs[orbit]; } export let analyser, analyserData /* s = {} */; + export function getAnalyser(/* orbit, */ fftSize = 2048) { if (!analyser /*s [orbit] */) { const analyserNode = getAudioContext().createAnalyser(); @@ -151,7 +163,7 @@ export function getAnalyzerData(type = 'time') { return analyserData; } -function effectSend(input, effect, wet) { +function effectSend(input, effect, wet, size) { const send = gainNode(wet); input.connect(send); send.connect(effect); @@ -219,6 +231,8 @@ export const superdough = async (value, deadline, hapDuration) => { velocity = 1, analyze, // analyser wet fft = 8, // fftSize 0 - 10 + ir, + i = 0, } = value; gain *= velocity; // legacy fix for velocity let toDisconnect = []; // audio nodes that will be disconnected when the source has ended @@ -247,6 +261,7 @@ export const superdough = async (value, deadline, hapDuration) => { // this can be used for things like speed(0) in the sampler return; } + if (ac.currentTime > t) { logger('[webaudio] skip hap: still loading', ac.currentTime - t); return; @@ -352,10 +367,16 @@ export const superdough = async (value, deadline, hapDuration) => { delaySend = effectSend(post, delyNode, delay); } // reverb + let buffer; + if (ir !== undefined) { + let sample = getSound(ir); + let url = sample.data.samples[i % sample.data.samples.length]; + buffer = await loadBuffer(url, ac, ir, 0); + } let reverbSend; if (room > 0 && size > 0) { - const reverbNode = getReverb(orbit, size); - reverbSend = effectSend(post, reverbNode, room); + const reverbNode = getReverb(orbit, size, buffer); + reverbSend = effectSend(post, reverbNode, room, size); } // analyser From abff27970746fecb4620d54a272abc5b67972d0c Mon Sep 17 00:00:00 2001 From: Raphael Forment Date: Sun, 1 Oct 2023 11:52:24 +0200 Subject: [PATCH 033/158] Document reverb controls --- packages/core/controls.mjs | 39 ++++++++++++++++++++++++++++-- packages/superdough/reverb.mjs | 14 ++++++++--- packages/superdough/superdough.mjs | 11 ++++++++- 3 files changed, 57 insertions(+), 7 deletions(-) diff --git a/packages/core/controls.mjs b/packages/core/controls.mjs index 6cac6e54..216b7001 100644 --- a/packages/core/controls.mjs +++ b/packages/core/controls.mjs @@ -970,6 +970,41 @@ const generic_params = [ * */ [['room', 'size']], + /** + * Reverb lowpass starting frequency (in hertz). + * + * @name revlp + * @param {number} level between 0 and 20000hz + * @example + * s("bd sd").room(0.5).revlp(10000) + * @example + * s("bd sd").room(0.5).revlp(5000) + */ + ['revlp'], + /** + * Reverb lowpass frequency at -60dB (in hertz). + * + * @name revdim + * @param {number} level between 0 and 20000hz + * @example + * s("bd sd").room(0.5).revlp(10000).revdim(8000) + * @example + * s("bd sd").room(0.5).revlp(5000).revdim(400) + * + */ + ['revdim'], + /** + * Reverb fade time (in seconds). + * + * @name fade + * @param {number} seconds for the reverb to fade + * @example + * s("bd sd").room(0.5).revlp(10000).fade(0.5) + * @example + * s("bd sd").room(0.5).revlp(5000).fade(4) + * + */ + ['fade'], /** * Sets the room size of the reverb, see {@link room}. * @@ -1162,7 +1197,7 @@ const generic_params = [ ]; // TODO: slice / splice https://www.youtube.com/watch?v=hKhPdO0RKDQ&list=PL2lW1zNIIwj3bDkh-Y3LUGDuRcoUigoDs&index=13 -controls.createParam = function (names) { +controls.createParam = function(names) { const name = Array.isArray(names) ? names[0] : names; var withVal; @@ -1186,7 +1221,7 @@ controls.createParam = function (names) { const func = (...pats) => sequence(...pats).withValue(withVal); - const setter = function (...pats) { + const setter = function(...pats) { if (!pats.length) { return this.fmap(withVal); } diff --git a/packages/superdough/reverb.mjs b/packages/superdough/reverb.mjs index 4d5f655f..505d0ac2 100644 --- a/packages/superdough/reverb.mjs +++ b/packages/superdough/reverb.mjs @@ -2,7 +2,13 @@ import reverbGen from './reverbGen.mjs'; if (typeof AudioContext !== 'undefined') { AudioContext.prototype.generateReverb = reverbGen.generateReverb; - AudioContext.prototype.createReverb = function(duration, audioContext) { + AudioContext.prototype.createReverb = function( + duration, + audioContext, + fade, + revlp, + revdim + ) { const convolver = this.createConvolver(); convolver.setDuration = (d) => { this.generateReverb( @@ -11,9 +17,9 @@ if (typeof AudioContext !== 'undefined') { sampleRate: 44100, numChannels: 2, decayTime: d, - fadeInTime: d, - lpFreqStart: 2000, - lpFreqEnd: 15000, + fadeInTime: fade, + lpFreqStart: revlp, + lpFreqEnd: revdim, }, (buffer) => { convolver.buffer = buffer; diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index 9010c85d..33c108f2 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -113,7 +113,13 @@ function getReverb(orbit, duration = 2) { // If no reverb has been created for a given orbit, create one if (!reverbs[orbit]) { const ac = getAudioContext(); - const reverb = ac.createReverb(duration, getAudioContext()); + const reverb = ac.createReverb( + duration, + getAudioContext(), + fade, + revlp, + revdim, + ); reverb.connect(getDestination()); console.log(reverb) reverbs[orbit] = reverb; @@ -222,6 +228,9 @@ export const superdough = async (value, deadline, hapDuration) => { delaytime = 0.25, orbit = 1, room, + fade = 0.1, + revlp = 15000, + revdim = 1000, size = 2, velocity = 1, analyze, // analyser wet From e600b91a8569685444bb1f719f9c6ed4b8910b9b Mon Sep 17 00:00:00 2001 From: Raphael Forment Date: Sun, 1 Oct 2023 12:02:17 +0200 Subject: [PATCH 034/158] bugfixes for parameter passing --- packages/superdough/reverb.mjs | 4 ++-- packages/superdough/superdough.mjs | 12 +++++------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/packages/superdough/reverb.mjs b/packages/superdough/reverb.mjs index 505d0ac2..4c5bc1d1 100644 --- a/packages/superdough/reverb.mjs +++ b/packages/superdough/reverb.mjs @@ -3,14 +3,14 @@ import reverbGen from './reverbGen.mjs'; if (typeof AudioContext !== 'undefined') { AudioContext.prototype.generateReverb = reverbGen.generateReverb; AudioContext.prototype.createReverb = function( - duration, audioContext, + duration, fade, revlp, revdim ) { const convolver = this.createConvolver(); - convolver.setDuration = (d) => { + convolver.setDuration = (d, fade, revlp, revdim) => { this.generateReverb( { audioContext, diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index 33c108f2..607c649d 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -108,29 +108,27 @@ function getDelay(orbit, delaytime, delayfeedback, t) { let reverbs = {}; -function getReverb(orbit, duration = 2) { +function getReverb(orbit, duration = 2, fade, revlp, revdim) { // If no reverb has been created for a given orbit, create one if (!reverbs[orbit]) { const ac = getAudioContext(); const reverb = ac.createReverb( - duration, getAudioContext(), + duration, fade, revlp, - revdim, ); reverb.connect(getDestination()); console.log(reverb) reverbs[orbit] = reverb; } - // Update the reverb duration if needed after instanciation if (reverbs[orbit].duration !== duration) { - reverbs[orbit] = reverbs[orbit].setDuration(duration); + reverbs[orbit] = reverbs[orbit].setDuration( + duration, fade, revlp, revdim); reverbs[orbit].duration = duration; } - return reverbs[orbit]; } @@ -370,7 +368,7 @@ export const superdough = async (value, deadline, hapDuration) => { // reverb let reverbSend; if (room > 0 && size > 0) { - const reverbNode = getReverb(orbit, size); + const reverbNode = getReverb(orbit, size, fade, revlp, revdim); reverbSend = effectSend(post, reverbNode, room); } From c6ecd31ea11baef947f4f261f7edcea31290ebec Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Sun, 1 Oct 2023 13:27:44 +0200 Subject: [PATCH 035/158] improve mouse tracking --- packages/codemirror/slider.mjs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/codemirror/slider.mjs b/packages/codemirror/slider.mjs index 44875e25..d4fbcefa 100644 --- a/packages/codemirror/slider.mjs +++ b/packages/codemirror/slider.mjs @@ -36,7 +36,7 @@ export class SliderWidget extends WidgetType { slider.from = this.from; slider.originalFrom = this.originalFrom; slider.to = this.to; - slider.className = 'w-16 translate-y-1.5 mx-2'; + slider.className = 'w-16 translate-y-1'; this.slider = slider; return wrap; } @@ -97,7 +97,7 @@ export const sliderPlugin = ViewPlugin.fromClass( eventHandlers: { mousedown: (e, view) => { - let target = e.target; /* as HTMLElement */ + let target = e.target; if (target.nodeName == 'INPUT' && target.parentElement.classList.contains('cm-slider')) { e.preventDefault(); e.stopPropagation(); @@ -121,7 +121,8 @@ export const sliderPlugin = ViewPlugin.fromClass( // moves slider on mouse event function updateSliderValue(view, e) { const mouseX = e.clientX; - let progress = (mouseX - draggedSlider._offsetLeft) / draggedSlider._clientWidth; + let mx = 10; + let progress = (mouseX - draggedSlider._offsetLeft - mx) / (draggedSlider._clientWidth - mx * 2); progress = Math.max(Math.min(1, progress), 0); let min = Number(draggedSlider.min); let max = Number(draggedSlider.max); From 755e24315e04223f75d22ac7680d05576b034c2f Mon Sep 17 00:00:00 2001 From: Vasilii Milovidov Date: Sun, 1 Oct 2023 15:31:10 +0400 Subject: [PATCH 036/158] use sample as an impulse response for the reverb --- packages/core/controls.mjs | 2386 ++++++++++++++-------------- packages/superdough/reverb.mjs | 29 +- packages/superdough/superdough.mjs | 11 +- 3 files changed, 1221 insertions(+), 1205 deletions(-) diff --git a/packages/core/controls.mjs b/packages/core/controls.mjs index 76a278a5..bc06ecc2 100644 --- a/packages/core/controls.mjs +++ b/packages/core/controls.mjs @@ -4,1231 +4,1233 @@ Copyright (C) 2022 Strudel contributors - see . */ -import {Pattern, register, sequence} from './pattern.mjs'; -import {zipWith} from './util.mjs'; +import { Pattern, register, sequence } from './pattern.mjs'; +import { zipWith } from './util.mjs'; const controls = {}; const generic_params = [ - /** - * Select a sound / sample by name. When using mininotation, you can also optionally supply 'n' and 'gain' parameters - * separated by ':'. - * - * @name s - * @param {string | Pattern} sound The sound / pattern of sounds to pick - * @synonyms sound - * @example - * s("bd hh") - * @example - * s("bd:0 bd:1 bd:0:0.3 bd:1:1.4") - * - */ - [['s', 'n', 'gain'], 'sound'], - /** - * Define a custom webaudio node to use as a sound source. - * - * @name source - * @param {function} getSource - * @synonyms src - * - */ - ['source', 'src'], - /** - * Selects the given index from the sample map. - * Numbers too high will wrap around. - * `n` can also be used to play midi numbers, but it is recommended to use `note` instead. - * - * @name n - * @param {number | Pattern} value sample index starting from 0 - * @example - * s("bd sd,hh*3").n("<0 1>") - */ - // also see https://github.com/tidalcycles/strudel/pull/63 - ['n'], - /** - * Plays the given note name or midi number. A note name consists of - * - * - a letter (a-g or A-G) - * - optional accidentals (b or #) - * - optional octave number (0-9). Defaults to 3 - * - * Examples of valid note names: `c`, `bb`, `Bb`, `f#`, `c3`, `A4`, `Eb2`, `c#5` - * - * You can also use midi numbers instead of note names, where 69 is mapped to A4 440Hz in 12EDO. - * - * @name note - * @example - * note("c a f e") - * @example - * note("c4 a4 f4 e4") - * @example - * note("60 69 65 64") - */ - [['note', 'n']], + /** + * Select a sound / sample by name. When using mininotation, you can also optionally supply 'n' and 'gain' parameters + * separated by ':'. + * + * @name s + * @param {string | Pattern} sound The sound / pattern of sounds to pick + * @synonyms sound + * @example + * s("bd hh") + * @example + * s("bd:0 bd:1 bd:0:0.3 bd:1:1.4") + * + */ + [['s', 'n', 'gain'], 'sound'], + /** + * Define a custom webaudio node to use as a sound source. + * + * @name source + * @param {function} getSource + * @synonyms src + * + */ + ['source', 'src'], + /** + * Selects the given index from the sample map. + * Numbers too high will wrap around. + * `n` can also be used to play midi numbers, but it is recommended to use `note` instead. + * + * @name n + * @param {number | Pattern} value sample index starting from 0 + * @example + * s("bd sd,hh*3").n("<0 1>") + */ + // also see https://github.com/tidalcycles/strudel/pull/63 + ['n'], + /** + * Plays the given note name or midi number. A note name consists of + * + * - a letter (a-g or A-G) + * - optional accidentals (b or #) + * - optional octave number (0-9). Defaults to 3 + * + * Examples of valid note names: `c`, `bb`, `Bb`, `f#`, `c3`, `A4`, `Eb2`, `c#5` + * + * You can also use midi numbers instead of note names, where 69 is mapped to A4 440Hz in 12EDO. + * + * @name note + * @example + * note("c a f e") + * @example + * note("c4 a4 f4 e4") + * @example + * note("60 69 65 64") + */ + [['note', 'n']], - /** - * A pattern of numbers that speed up (or slow down) samples while they play. Currently only supported by osc / superdirt. - * - * @name accelerate - * @param {number | Pattern} amount acceleration. - * @superdirtOnly - * @example - * s("sax").accelerate("<0 1 2 4 8 16>").slow(2).osc() - * - */ - ['accelerate'], - /** - * Controls the gain by an exponential amount. - * - * @name gain - * @param {number | Pattern} amount gain. - * @example - * s("hh*8").gain(".4!2 1 .4!2 1 .4 1") - * - */ - ['gain'], - /** - * Like {@link gain}, but linear. - * - * @name amp - * @param {number | Pattern} amount gain. - * @superdirtOnly - * @example - * s("bd*8").amp(".1*2 .5 .1*2 .5 .1 .5").osc() - * - */ - ['amp'], - /** - * Amplitude envelope attack time: Specifies how long it takes for the sound to reach its peak value, relative to the onset. - * - * @name attack - * @param {number | Pattern} attack time in seconds. - * @synonyms att - * @example - * note("c3 e3").attack("<0 .1 .5>") - * - */ - ['attack', 'att'], + /** + * A pattern of numbers that speed up (or slow down) samples while they play. Currently only supported by osc / superdirt. + * + * @name accelerate + * @param {number | Pattern} amount acceleration. + * @superdirtOnly + * @example + * s("sax").accelerate("<0 1 2 4 8 16>").slow(2).osc() + * + */ + ['accelerate'], + /** + * Controls the gain by an exponential amount. + * + * @name gain + * @param {number | Pattern} amount gain. + * @example + * s("hh*8").gain(".4!2 1 .4!2 1 .4 1") + * + */ + ['gain'], + /** + * Like {@link gain}, but linear. + * + * @name amp + * @param {number | Pattern} amount gain. + * @superdirtOnly + * @example + * s("bd*8").amp(".1*2 .5 .1*2 .5 .1 .5").osc() + * + */ + ['amp'], + /** + * Amplitude envelope attack time: Specifies how long it takes for the sound to reach its peak value, relative to the onset. + * + * @name attack + * @param {number | Pattern} attack time in seconds. + * @synonyms att + * @example + * note("c3 e3").attack("<0 .1 .5>") + * + */ + ['attack', 'att'], - /** - * Sets the Frequency Modulation Harmonicity Ratio. - * Controls the timbre of the sound. - * Whole numbers and simple ratios sound more natural, - * while decimal numbers and complex ratios sound metallic. - * - * @name fmh - * @param {number | Pattern} harmonicity - * @example - * note("c e g b") - * .fm(4) - * .fmh("<1 2 1.5 1.61>") - * .scope() - * - */ - [['fmh', 'fmi'], 'fmh'], - /** - * Sets the Frequency Modulation of the synth. - * Controls the modulation index, which defines the brightness of the sound. - * - * @name fm - * @param {number | Pattern} brightness modulation index - * @synonyms fmi - * @example - * note("c e g b") - * .fm("<0 1 2 8 32>") - * .scope() - * - */ - [['fmi', 'fmh'], 'fm'], - // fm envelope - /** - * Ramp type of fm envelope. Exp might be a bit broken.. - * - * @name fmenv - * @param {number | Pattern} type lin | exp - * @example - * note("c e g b") - * .fm(4) - * .fmdecay(.2) - * .fmsustain(0) - * .fmenv("") - * .scope() - * - */ - ['fmenv'], - /** - * Attack time for the FM envelope: time it takes to reach maximum modulation - * - * @name fmattack - * @param {number | Pattern} time attack time - * @example - * note("c e g b") - * .fm(4) - * .fmattack("<0 .05 .1 .2>") - * .scope() - * - */ - ['fmattack'], - /** - * Decay time for the FM envelope: seconds until the sustain level is reached after the attack phase. - * - * @name fmdecay - * @param {number | Pattern} time decay time - * @example - * note("c e g b") - * .fm(4) - * .fmdecay("<.01 .05 .1 .2>") - * .fmsustain(.4) - * .scope() - * - */ - ['fmdecay'], - /** - * Sustain level for the FM envelope: how much modulation is applied after the decay phase - * - * @name fmsustain - * @param {number | Pattern} level sustain level - * @example - * note("c e g b") - * .fm(4) - * .fmdecay(.1) - * .fmsustain("<1 .75 .5 0>") - * .scope() - * - */ - ['fmsustain'], - // these are not really useful... skipping for now - ['fmrelease'], - ['fmvelocity'], + /** + * Sets the Frequency Modulation Harmonicity Ratio. + * Controls the timbre of the sound. + * Whole numbers and simple ratios sound more natural, + * while decimal numbers and complex ratios sound metallic. + * + * @name fmh + * @param {number | Pattern} harmonicity + * @example + * note("c e g b") + * .fm(4) + * .fmh("<1 2 1.5 1.61>") + * .scope() + * + */ + [['fmh', 'fmi'], 'fmh'], + /** + * Sets the Frequency Modulation of the synth. + * Controls the modulation index, which defines the brightness of the sound. + * + * @name fm + * @param {number | Pattern} brightness modulation index + * @synonyms fmi + * @example + * note("c e g b") + * .fm("<0 1 2 8 32>") + * .scope() + * + */ + [['fmi', 'fmh'], 'fm'], + // fm envelope + /** + * Ramp type of fm envelope. Exp might be a bit broken.. + * + * @name fmenv + * @param {number | Pattern} type lin | exp + * @example + * note("c e g b") + * .fm(4) + * .fmdecay(.2) + * .fmsustain(0) + * .fmenv("") + * .scope() + * + */ + ['fmenv'], + /** + * Attack time for the FM envelope: time it takes to reach maximum modulation + * + * @name fmattack + * @param {number | Pattern} time attack time + * @example + * note("c e g b") + * .fm(4) + * .fmattack("<0 .05 .1 .2>") + * .scope() + * + */ + ['fmattack'], + /** + * Decay time for the FM envelope: seconds until the sustain level is reached after the attack phase. + * + * @name fmdecay + * @param {number | Pattern} time decay time + * @example + * note("c e g b") + * .fm(4) + * .fmdecay("<.01 .05 .1 .2>") + * .fmsustain(.4) + * .scope() + * + */ + ['fmdecay'], + /** + * Sustain level for the FM envelope: how much modulation is applied after the decay phase + * + * @name fmsustain + * @param {number | Pattern} level sustain level + * @example + * note("c e g b") + * .fm(4) + * .fmdecay(.1) + * .fmsustain("<1 .75 .5 0>") + * .scope() + * + */ + ['fmsustain'], + // these are not really useful... skipping for now + ['fmrelease'], + ['fmvelocity'], - /** - * Select the sound bank to use. To be used together with `s`. The bank name (+ "_") will be prepended to the value of `s`. - * - * @name bank - * @param {string | Pattern} bank the name of the bank - * @example - * s("bd sd").bank('RolandTR909') // = s("RolandTR909_bd RolandTR909_sd") - * - */ - ['bank'], + /** + * Select the sound bank to use. To be used together with `s`. The bank name (+ "_") will be prepended to the value of `s`. + * + * @name bank + * @param {string | Pattern} bank the name of the bank + * @example + * s("bd sd").bank('RolandTR909') // = s("RolandTR909_bd RolandTR909_sd") + * + */ + ['bank'], - ['analyze'], // analyser node send amount 0 - 1 (used by scope) - ['fft'], // fftSize of analyser + ['analyze'], // analyser node send amount 0 - 1 (used by scope) + ['fft'], // fftSize of analyser - /** - * Amplitude envelope decay time: the time it takes after the attack time to reach the sustain level. - * Note that the decay is only audible if the sustain value is lower than 1. - * - * @name decay - * @param {number | Pattern} time decay time in seconds - * @example - * note("c3 e3").decay("<.1 .2 .3 .4>").sustain(0) - * - */ - ['decay'], - /** - * Amplitude envelope sustain level: The level which is reached after attack / decay, being sustained until the offset. - * - * @name sustain - * @param {number | Pattern} gain sustain level between 0 and 1 - * @synonyms sus - * @example - * note("c3 e3").decay(.2).sustain("<0 .1 .4 .6 1>") - * - */ - ['sustain', 'sus'], - /** - * Amplitude envelope release time: The time it takes after the offset to go from sustain level to zero. - * - * @name release - * @param {number | Pattern} time release time in seconds - * @synonyms rel - * @example - * note("c3 e3 g3 c4").release("<0 .1 .4 .6 1>/2") - * - */ - ['release', 'rel'], - ['hold'], - // TODO: in tidal, it seems to be normalized - /** - * Sets the center frequency of the **b**and-**p**ass **f**ilter. When using mininotation, you - * can also optionally supply the 'bpq' parameter separated by ':'. - * - * @name bpf - * @param {number | Pattern} frequency center frequency - * @synonyms bandf, bp - * @example - * s("bd sd,hh*3").bpf("<1000 2000 4000 8000>") - * - */ - [['bandf', 'bandq'], 'bpf', 'bp'], - // TODO: in tidal, it seems to be normalized - /** - * Sets the **b**and-**p**ass **q**-factor (resonance). - * - * @name bpq - * @param {number | Pattern} q q factor - * @synonyms bandq - * @example - * s("bd sd").bpf(500).bpq("<0 1 2 3>") - * - */ - // currently an alias of 'bandq' https://github.com/tidalcycles/strudel/issues/496 - // ['bpq'], - ['bandq', 'bpq'], - /** - * a pattern of numbers from 0 to 1. Skips the beginning of each sample, e.g. `0.25` to cut off the first quarter from each sample. - * - * @memberof Pattern - * @name begin - * @param {number | Pattern} amount between 0 and 1, where 1 is the length of the sample - * @example - * samples({ rave: 'rave/AREUREADY.wav' }, 'github:tidalcycles/Dirt-Samples/master/') - * s("rave").begin("<0 .25 .5 .75>") - * - */ - ['begin'], - /** - * The same as .begin, but cuts off the end off each sample. - * - * @memberof Pattern - * @name end - * @param {number | Pattern} length 1 = whole sample, .5 = half sample, .25 = quarter sample etc.. - * @example - * s("bd*2,oh*4").end("<.1 .2 .5 1>") - * - */ - ['end'], - /** - * Loops the sample. - * Note that the tempo of the loop is not synced with the cycle tempo. - * To change the loop region, use loopBegin / loopEnd. - * - * @name loop - * @param {number | Pattern} on If 1, the sample is looped - * @example - * s("casio").loop(1) - * - */ - ['loop'], - /** - * Begin to loop at a specific point in the sample (inbetween `begin` and `end`). - * Note that the loop point must be inbetween `begin` and `end`, and before `loopEnd`! - * Note: Samples starting with wt_ will automatically loop! (wt = wavetable) - * - * @name loopBegin - * @param {number | Pattern} time between 0 and 1, where 1 is the length of the sample - * @synonyms loopb - * @example - * s("space").loop(1) - * .loopBegin("<0 .125 .25>").scope() - */ - ['loopBegin', 'loopb'], - /** - * - * End the looping section at a specific point in the sample (inbetween `begin` and `end`). - * Note that the loop point must be inbetween `begin` and `end`, and after `loopBegin`! - * - * @name loopEnd - * @param {number | Pattern} time between 0 and 1, where 1 is the length of the sample - * @synonyms loope - * @example - * s("space").loop(1) - * .loopEnd("<1 .75 .5 .25>").scope() - */ - ['loopEnd', 'loope'], - /** - * bit crusher effect. - * - * @name crush - * @param {number | Pattern} depth between 1 (for drastic reduction in bit-depth) to 16 (for barely no reduction). - * @example - * s(",hh*3").fast(2).crush("<16 8 7 6 5 4 3 2>") - * - */ - // TODO: currently duplicated with "native" legato - // TODO: superdirt legato will do more: https://youtu.be/dQPmE1WaD1k?t=419 - /** - * a pattern of numbers from 0 to 1. Skips the beginning of each sample, e.g. `0.25` to cut off the first quarter from each sample. - * - * @name legato - * @param {number | Pattern} duration between 0 and 1, where 1 is the length of the whole hap time - * @noAutocomplete - * @example - * "c4 eb4 g4 bb4".legato("<0.125 .25 .5 .75 1 2 4>") - * - */ - // ['legato'], - // ['clhatdecay'], - ['crush'], - /** - * fake-resampling for lowering the sample rate. Caution: This effect seems to only work in chromium based browsers - * - * @name coarse - * @param {number | Pattern} factor 1 for original 2 for half, 3 for a third and so on. - * @example - * s("bd sd,hh*4").coarse("<1 4 8 16 32>") - * - */ - ['coarse'], - /** - * choose the channel the pattern is sent to in superdirt - * - * @name channel - * @param {number | Pattern} channel channel number - * - */ - ['channel'], - /** - * In the style of classic drum-machines, `cut` will stop a playing sample as soon as another samples with in same cutgroup is to be played. An example would be an open hi-hat followed by a closed one, essentially muting the open. - * - * @name cut - * @param {number | Pattern} group cut group number - * @example - * s("rd*4").cut(1) - * - */ - ['cut'], - /** - * Applies the cutoff frequency of the **l**ow-**p**ass **f**ilter. - * - * When using mininotation, you can also optionally add the 'lpq' parameter, separated by ':'. - * - * @name lpf - * @param {number | Pattern} frequency audible between 0 and 20000 - * @synonyms cutoff, ctf, lp - * @example - * s("bd sd,hh*3").lpf("<4000 2000 1000 500 200 100>") - * @example - * s("bd*8").lpf("1000:0 1000:10 1000:20 1000:30") - * - */ - [['cutoff', 'resonance'], 'ctf', 'lpf', 'lp'], + /** + * Amplitude envelope decay time: the time it takes after the attack time to reach the sustain level. + * Note that the decay is only audible if the sustain value is lower than 1. + * + * @name decay + * @param {number | Pattern} time decay time in seconds + * @example + * note("c3 e3").decay("<.1 .2 .3 .4>").sustain(0) + * + */ + ['decay'], + /** + * Amplitude envelope sustain level: The level which is reached after attack / decay, being sustained until the offset. + * + * @name sustain + * @param {number | Pattern} gain sustain level between 0 and 1 + * @synonyms sus + * @example + * note("c3 e3").decay(.2).sustain("<0 .1 .4 .6 1>") + * + */ + ['sustain', 'sus'], + /** + * Amplitude envelope release time: The time it takes after the offset to go from sustain level to zero. + * + * @name release + * @param {number | Pattern} time release time in seconds + * @synonyms rel + * @example + * note("c3 e3 g3 c4").release("<0 .1 .4 .6 1>/2") + * + */ + ['release', 'rel'], + ['hold'], + // TODO: in tidal, it seems to be normalized + /** + * Sets the center frequency of the **b**and-**p**ass **f**ilter. When using mininotation, you + * can also optionally supply the 'bpq' parameter separated by ':'. + * + * @name bpf + * @param {number | Pattern} frequency center frequency + * @synonyms bandf, bp + * @example + * s("bd sd,hh*3").bpf("<1000 2000 4000 8000>") + * + */ + [['bandf', 'bandq'], 'bpf', 'bp'], + // TODO: in tidal, it seems to be normalized + /** + * Sets the **b**and-**p**ass **q**-factor (resonance). + * + * @name bpq + * @param {number | Pattern} q q factor + * @synonyms bandq + * @example + * s("bd sd").bpf(500).bpq("<0 1 2 3>") + * + */ + // currently an alias of 'bandq' https://github.com/tidalcycles/strudel/issues/496 + // ['bpq'], + ['bandq', 'bpq'], + /** + * a pattern of numbers from 0 to 1. Skips the beginning of each sample, e.g. `0.25` to cut off the first quarter from each sample. + * + * @memberof Pattern + * @name begin + * @param {number | Pattern} amount between 0 and 1, where 1 is the length of the sample + * @example + * samples({ rave: 'rave/AREUREADY.wav' }, 'github:tidalcycles/Dirt-Samples/master/') + * s("rave").begin("<0 .25 .5 .75>") + * + */ + ['begin'], + /** + * The same as .begin, but cuts off the end off each sample. + * + * @memberof Pattern + * @name end + * @param {number | Pattern} length 1 = whole sample, .5 = half sample, .25 = quarter sample etc.. + * @example + * s("bd*2,oh*4").end("<.1 .2 .5 1>") + * + */ + ['end'], + /** + * Loops the sample. + * Note that the tempo of the loop is not synced with the cycle tempo. + * To change the loop region, use loopBegin / loopEnd. + * + * @name loop + * @param {number | Pattern} on If 1, the sample is looped + * @example + * s("casio").loop(1) + * + */ + ['loop'], + /** + * Begin to loop at a specific point in the sample (inbetween `begin` and `end`). + * Note that the loop point must be inbetween `begin` and `end`, and before `loopEnd`! + * Note: Samples starting with wt_ will automatically loop! (wt = wavetable) + * + * @name loopBegin + * @param {number | Pattern} time between 0 and 1, where 1 is the length of the sample + * @synonyms loopb + * @example + * s("space").loop(1) + * .loopBegin("<0 .125 .25>").scope() + */ + ['loopBegin', 'loopb'], + /** + * + * End the looping section at a specific point in the sample (inbetween `begin` and `end`). + * Note that the loop point must be inbetween `begin` and `end`, and after `loopBegin`! + * + * @name loopEnd + * @param {number | Pattern} time between 0 and 1, where 1 is the length of the sample + * @synonyms loope + * @example + * s("space").loop(1) + * .loopEnd("<1 .75 .5 .25>").scope() + */ + ['loopEnd', 'loope'], + /** + * bit crusher effect. + * + * @name crush + * @param {number | Pattern} depth between 1 (for drastic reduction in bit-depth) to 16 (for barely no reduction). + * @example + * s(",hh*3").fast(2).crush("<16 8 7 6 5 4 3 2>") + * + */ + // TODO: currently duplicated with "native" legato + // TODO: superdirt legato will do more: https://youtu.be/dQPmE1WaD1k?t=419 + /** + * a pattern of numbers from 0 to 1. Skips the beginning of each sample, e.g. `0.25` to cut off the first quarter from each sample. + * + * @name legato + * @param {number | Pattern} duration between 0 and 1, where 1 is the length of the whole hap time + * @noAutocomplete + * @example + * "c4 eb4 g4 bb4".legato("<0.125 .25 .5 .75 1 2 4>") + * + */ + // ['legato'], + // ['clhatdecay'], + ['crush'], + /** + * fake-resampling for lowering the sample rate. Caution: This effect seems to only work in chromium based browsers + * + * @name coarse + * @param {number | Pattern} factor 1 for original 2 for half, 3 for a third and so on. + * @example + * s("bd sd,hh*4").coarse("<1 4 8 16 32>") + * + */ + ['coarse'], + /** + * choose the channel the pattern is sent to in superdirt + * + * @name channel + * @param {number | Pattern} channel channel number + * + */ + ['channel'], + /** + * In the style of classic drum-machines, `cut` will stop a playing sample as soon as another samples with in same cutgroup is to be played. An example would be an open hi-hat followed by a closed one, essentially muting the open. + * + * @name cut + * @param {number | Pattern} group cut group number + * @example + * s("rd*4").cut(1) + * + */ + ['cut'], + /** + * Applies the cutoff frequency of the **l**ow-**p**ass **f**ilter. + * + * When using mininotation, you can also optionally add the 'lpq' parameter, separated by ':'. + * + * @name lpf + * @param {number | Pattern} frequency audible between 0 and 20000 + * @synonyms cutoff, ctf, lp + * @example + * s("bd sd,hh*3").lpf("<4000 2000 1000 500 200 100>") + * @example + * s("bd*8").lpf("1000:0 1000:10 1000:20 1000:30") + * + */ + [['cutoff', 'resonance'], 'ctf', 'lpf', 'lp'], - /** - * Sets the lowpass filter envelope modulation depth. - * @name lpenv - * @param {number | Pattern} modulation depth of the lowpass filter envelope between 0 and _n_ - * @synonyms lpe - * @example - * note("") - * .sound('sawtooth') - * .lpf(500) - * .lpa(.5) - * .lpenv("<4 2 1 0 -1 -2 -4>/4") - */ - ['lpenv', 'lpe'], - /** - * Sets the highpass filter envelope modulation depth. - * @name hpenv - * @param {number | Pattern} modulation depth of the highpass filter envelope between 0 and _n_ - * @synonyms hpe - * @example - * note("") - * .sound('sawtooth') - * .hpf(500) - * .hpa(.5) - * .hpenv("<4 2 1 0 -1 -2 -4>/4") - */ - ['hpenv', 'hpe'], - /** - * Sets the bandpass filter envelope modulation depth. - * @name bpenv - * @param {number | Pattern} modulation depth of the bandpass filter envelope between 0 and _n_ - * @synonyms bpe - * @example - * note("") - * .sound('sawtooth') - * .bpf(500) - * .bpa(.5) - * .bpenv("<4 2 1 0 -1 -2 -4>/4") - */ - ['bpenv', 'bpe'], - /** - * Sets the attack duration for the lowpass filter envelope. - * @name lpattack - * @param {number | Pattern} attack time of the filter envelope - * @synonyms lpa - * @example - * note("") - * .sound('sawtooth') - * .lpf(500) - * .lpa("<.5 .25 .1 .01>/4") - * .lpenv(4) - */ - ['lpattack', 'lpa'], - /** - * Sets the attack duration for the highpass filter envelope. - * @name hpattack - * @param {number | Pattern} attack time of the highpass filter envelope - * @synonyms hpa - * @example - * note("") - * .sound('sawtooth') - * .hpf(500) - * .hpa("<.5 .25 .1 .01>/4") - * .hpenv(4) - */ - ['hpattack', 'hpa'], - /** - * Sets the attack duration for the bandpass filter envelope. - * @name bpattack - * @param {number | Pattern} attack time of the bandpass filter envelope - * @synonyms bpa - * @example - * note("") - * .sound('sawtooth') - * .bpf(500) - * .bpa("<.5 .25 .1 .01>/4") - * .bpenv(4) - */ - ['bpattack', 'bpa'], - /** - * Sets the decay duration for the lowpass filter envelope. - * @name lpdecay - * @param {number | Pattern} decay time of the filter envelope - * @synonyms lpd - * @example - * note("") - * .sound('sawtooth') - * .lpf(500) - * .lpd("<.5 .25 .1 0>/4") - * .lps(0.2) - * .lpenv(4) - */ - ['lpdecay', 'lpd'], - /** - * Sets the decay duration for the highpass filter envelope. - * @name hpdecay - * @param {number | Pattern} decay time of the highpass filter envelope - * @synonyms hpd - * @example - * note("") - * .sound('sawtooth') - * .hpf(500) - * .hpd("<.5 .25 .1 0>/4") - * .hps(0.2) - * .hpenv(4) - */ - ['hpdecay', 'hpd'], - /** - * Sets the decay duration for the bandpass filter envelope. - * @name bpdecay - * @param {number | Pattern} decay time of the bandpass filter envelope - * @synonyms bpd - * @example - * note("") - * .sound('sawtooth') - * .bpf(500) - * .bpd("<.5 .25 .1 0>/4") - * .bps(0.2) - * .bpenv(4) - */ - ['bpdecay', 'bpd'], - /** - * Sets the sustain amplitude for the lowpass filter envelope. - * @name lpsustain - * @param {number | Pattern} sustain amplitude of the lowpass filter envelope - * @synonyms lps - * @example - * note("") - * .sound('sawtooth') - * .lpf(500) - * .lpd(.5) - * .lps("<0 .25 .5 1>/4") - * .lpenv(4) - */ - ['lpsustain', 'lps'], - /** - * Sets the sustain amplitude for the highpass filter envelope. - * @name hpsustain - * @param {number | Pattern} sustain amplitude of the highpass filter envelope - * @synonyms hps - * @example - * note("") - * .sound('sawtooth') - * .hpf(500) - * .hpd(.5) - * .hps("<0 .25 .5 1>/4") - * .hpenv(4) - */ - ['hpsustain', 'hps'], - /** - * Sets the sustain amplitude for the bandpass filter envelope. - * @name bpsustain - * @param {number | Pattern} sustain amplitude of the bandpass filter envelope - * @synonyms bps - * @example - * note("") - * .sound('sawtooth') - * .bpf(500) - * .bpd(.5) - * .bps("<0 .25 .5 1>/4") - * .bpenv(4) - */ - ['bpsustain', 'bps'], - /** - * Sets the release time for the lowpass filter envelope. - * @name lprelease - * @param {number | Pattern} release time of the filter envelope - * @synonyms lpr - * @example - * note("") - * .sound('sawtooth') - * .clip(.5) - * .lpf(500) - * .lpenv(4) - * .lpr("<.5 .25 .1 0>/4") - * .release(.5) - */ - ['lprelease', 'lpr'], - /** - * Sets the release time for the highpass filter envelope. - * @name hprelease - * @param {number | Pattern} release time of the highpass filter envelope - * @synonyms hpr - * @example - * note("") - * .sound('sawtooth') - * .clip(.5) - * .hpf(500) - * .hpenv(4) - * .hpr("<.5 .25 .1 0>/4") - * .release(.5) - */ - ['hprelease', 'hpr'], - /** - * Sets the release time for the bandpass filter envelope. - * @name bprelease - * @param {number | Pattern} release time of the bandpass filter envelope - * @synonyms bpr - * @example - * note("") - * .sound('sawtooth') - * .clip(.5) - * .bpf(500) - * .bpenv(4) - * .bpr("<.5 .25 .1 0>/4") - * .release(.5) - */ - ['bprelease', 'bpr'], - /** - * Sets the filter type. The 24db filter is more aggressive. More types might be added in the future. - * @name ftype - * @param {number | Pattern} type 12db (default) or 24db - * @example - * note("") - * .sound('sawtooth') - * .lpf(500) - * .bpenv(4) - * .ftype("<12db 24db>") - */ - ['ftype'], - ['fanchor'], - /** - * Applies the cutoff frequency of the **h**igh-**p**ass **f**ilter. - * - * When using mininotation, you can also optionally add the 'hpq' parameter, separated by ':'. - * - * @name hpf - * @param {number | Pattern} frequency audible between 0 and 20000 - * @synonyms hp, hcutoff - * @example - * s("bd sd,hh*4").hpf("<4000 2000 1000 500 200 100>") - * @example - * s("bd sd,hh*4").hpf("<2000 2000:25>") - * - */ - // currently an alias of 'hcutoff' https://github.com/tidalcycles/strudel/issues/496 - // ['hpf'], - /** - * Applies a vibrato to the frequency of the oscillator. - * - * @name vib - * @synonyms vibrato, v - * @param {number | Pattern} frequency of the vibrato in hertz - * @example - * note("a") - * .vib("<.5 1 2 4 8 16>") - * @example - * // change the modulation depth with ":" - * note("a") - * .vib("<.5 1 2 4 8 16>:12") - */ - [['vib', 'vibmod'], 'vibrato', 'v'], - /** - * Sets the vibrato depth in semitones. Only has an effect if `vibrato` | `vib` | `v` is is also set - * - * @name vibmod - * @synonyms vmod - * @param {number | Pattern} depth of vibrato (in semitones) - * @example - * note("a").vib(4) - * .vibmod("<.25 .5 1 2 12>") - * @example - * // change the vibrato frequency with ":" - * note("a") - * .vibmod("<.25 .5 1 2 12>:8") - */ - [['vibmod', 'vib'], 'vmod'], - [['hcutoff', 'hresonance'], 'hpf', 'hp'], - /** - * Controls the **h**igh-**p**ass **q**-value. - * - * @name hpq - * @param {number | Pattern} q resonance factor between 0 and 50 - * @synonyms hresonance - * @example - * s("bd sd,hh*4").hpf(2000).hpq("<0 10 20 30>") - * - */ - ['hresonance', 'hpq'], - /** - * Controls the **l**ow-**p**ass **q**-value. - * - * @name lpq - * @param {number | Pattern} q resonance factor between 0 and 50 - * @synonyms resonance - * @example - * s("bd sd,hh*4").lpf(2000).lpq("<0 10 20 30>") - * - */ - // currently an alias of 'resonance' https://github.com/tidalcycles/strudel/issues/496 - ['resonance', 'lpq'], - /** - * DJ filter, below 0.5 is low pass filter, above is high pass filter. - * - * @name djf - * @param {number | Pattern} cutoff below 0.5 is low pass filter, above is high pass filter - * @example - * n("0 3 7 [10,24]").s('superzow').octave(3).djf("<.5 .25 .5 .75>").osc() - * - */ - ['djf'], - // ['cutoffegint'], - // TODO: does not seem to work - /** - * Sets the level of the delay signal. - * - * When using mininotation, you can also optionally add the 'delaytime' and 'delayfeedback' parameter, - * separated by ':'. - * - * - * @name delay - * @param {number | Pattern} level between 0 and 1 - * @example - * s("bd").delay("<0 .25 .5 1>") - * @example - * s("bd bd").delay("0.65:0.25:0.9 0.65:0.125:0.7") - * - */ - [['delay', 'delaytime', 'delayfeedback']], - /** - * Sets the level of the signal that is fed back into the delay. - * Caution: Values >= 1 will result in a signal that gets louder and louder! Don't do it - * - * @name delayfeedback - * @param {number | Pattern} feedback between 0 and 1 - * @synonyms delayfb, dfb - * @example - * s("bd").delay(.25).delayfeedback("<.25 .5 .75 1>").slow(2) - * - */ - ['delayfeedback', 'delayfb', 'dfb'], - /** - * Sets the time of the delay effect. - * - * @name delaytime - * @param {number | Pattern} seconds between 0 and Infinity - * @synonyms delayt, dt - * @example - * s("bd").delay(.25).delaytime("<.125 .25 .5 1>").slow(2) - * - */ - ['delaytime', 'delayt', 'dt'], - /* // TODO: test - * Specifies whether delaytime is calculated relative to cps. - * - * @name lock - * @param {number | Pattern} enable When set to 1, delaytime is a direct multiple of a cycle. - * @example - * s("sd").delay().lock(1).osc() - * - */ - ['lock'], - /** - * Set detune of oscillators. Works only with some synths, see tidal doc - * - * @name detune - * @param {number | Pattern} amount between 0 and 1 - * @synonyms det - * @superdirtOnly - * @example - * n("0 3 7").s('superzow').octave(3).detune("<0 .25 .5 1 2>").osc() - * - */ - ['detune', 'det'], - /** - * Set dryness of reverb. See {@link room} and {@link size} for more information about reverb. - * - * @name dry - * @param {number | Pattern} dry 0 = wet, 1 = dry - * @example - * n("[0,3,7](3,8)").s("superpiano").room(.7).dry("<0 .5 .75 1>").osc() - * @superdirtOnly - * - */ - ['dry'], - // TODO: does not seem to do anything - /* - * Used when using {@link begin}/{@link end} or {@link chop}/{@link striate} and friends, to change the fade out time of the 'grain' envelope. - * - * @name fadeTime - * @param {number | Pattern} time between 0 and 1 - * @example - * s("oh*4").end(.1).fadeTime("<0 .2 .4 .8>").osc() - * - */ - ['fadeTime', 'fadeOutTime'], - // TODO: see above - ['fadeInTime'], - /** - * Set frequency of sound. - * - * @name freq - * @param {number | Pattern} frequency in Hz. the audible range is between 20 and 20000 Hz - * @example - * freq("220 110 440 110").s("superzow").osc() - * @example - * freq("110".mul.out(".5 1.5 .6 [2 3]")).s("superzow").osc() - * - */ - ['freq'], - // TODO: https://tidalcycles.org/docs/configuration/MIDIOSC/control-voltage/#gate - ['gate', 'gat'], - // ['hatgrain'], - // ['lagogo'], - // ['lclap'], - // ['lclaves'], - // ['lclhat'], - // ['lcrash'], - // TODO: - // https://tidalcycles.org/docs/reference/audio_effects/#leslie-1 - // https://tidalcycles.org/docs/reference/audio_effects/#leslie - /** - * Emulation of a Leslie speaker: speakers rotating in a wooden amplified cabinet. - * - * @name leslie - * @param {number | Pattern} wet between 0 and 1 - * @example - * n("0,4,7").s("supersquare").leslie("<0 .4 .6 1>").osc() - * @superdirtOnly - * - */ - ['leslie'], - /** - * Rate of modulation / rotation for leslie effect - * - * @name lrate - * @param {number | Pattern} rate 6.7 for fast, 0.7 for slow - * @example - * n("0,4,7").s("supersquare").leslie(1).lrate("<1 2 4 8>").osc() - * @superdirtOnly - * - */ - // TODO: the rate seems to "lag" (in the example, 1 will be fast) - ['lrate'], - /** - * Physical size of the cabinet in meters. Be careful, it might be slightly larger than your computer. Affects the Doppler amount (pitch warble) - * - * @name lsize - * @param {number | Pattern} meters somewhere between 0 and 1 - * @example - * n("0,4,7").s("supersquare").leslie(1).lrate(2).lsize("<.1 .5 1>").osc() - * @superdirtOnly - * - */ - ['lsize'], - // label for pianoroll - ['activeLabel'], - [['label', 'activeLabel']], - // ['lfo'], - // ['lfocutoffint'], - // ['lfodelay'], - // ['lfoint'], - // ['lfopitchint'], - // ['lfoshape'], - // ['lfosync'], - // ['lhitom'], - // ['lkick'], - // ['llotom'], - // ['lophat'], - // ['lsnare'], - ['degree'], // TODO: what is this? not found in tidal doc - ['mtranspose'], // TODO: what is this? not found in tidal doc - ['ctranspose'], // TODO: what is this? not found in tidal doc - ['harmonic'], // TODO: what is this? not found in tidal doc - ['stepsPerOctave'], // TODO: what is this? not found in tidal doc - ['octaveR'], // TODO: what is this? not found in tidal doc - // TODO: why is this needed? what's the difference to late / early? Answer: it's in seconds, and delays the message at - // OSC time (so can't be negative, at least not beyond the latency value) - ['nudge'], - // TODO: the following doc is just a guess, it's not documented in tidal doc. - /** - * Sets the default octave of a synth. - * - * @name octave - * @param {number | Pattern} octave octave number - * @example - * n("0,4,7").s('supersquare').octave("<3 4 5 6>").osc() - * @superDirtOnly - */ - ['octave'], + /** + * Sets the lowpass filter envelope modulation depth. + * @name lpenv + * @param {number | Pattern} modulation depth of the lowpass filter envelope between 0 and _n_ + * @synonyms lpe + * @example + * note("") + * .sound('sawtooth') + * .lpf(500) + * .lpa(.5) + * .lpenv("<4 2 1 0 -1 -2 -4>/4") + */ + ['lpenv', 'lpe'], + /** + * Sets the highpass filter envelope modulation depth. + * @name hpenv + * @param {number | Pattern} modulation depth of the highpass filter envelope between 0 and _n_ + * @synonyms hpe + * @example + * note("") + * .sound('sawtooth') + * .hpf(500) + * .hpa(.5) + * .hpenv("<4 2 1 0 -1 -2 -4>/4") + */ + ['hpenv', 'hpe'], + /** + * Sets the bandpass filter envelope modulation depth. + * @name bpenv + * @param {number | Pattern} modulation depth of the bandpass filter envelope between 0 and _n_ + * @synonyms bpe + * @example + * note("") + * .sound('sawtooth') + * .bpf(500) + * .bpa(.5) + * .bpenv("<4 2 1 0 -1 -2 -4>/4") + */ + ['bpenv', 'bpe'], + /** + * Sets the attack duration for the lowpass filter envelope. + * @name lpattack + * @param {number | Pattern} attack time of the filter envelope + * @synonyms lpa + * @example + * note("") + * .sound('sawtooth') + * .lpf(500) + * .lpa("<.5 .25 .1 .01>/4") + * .lpenv(4) + */ + ['lpattack', 'lpa'], + /** + * Sets the attack duration for the highpass filter envelope. + * @name hpattack + * @param {number | Pattern} attack time of the highpass filter envelope + * @synonyms hpa + * @example + * note("") + * .sound('sawtooth') + * .hpf(500) + * .hpa("<.5 .25 .1 .01>/4") + * .hpenv(4) + */ + ['hpattack', 'hpa'], + /** + * Sets the attack duration for the bandpass filter envelope. + * @name bpattack + * @param {number | Pattern} attack time of the bandpass filter envelope + * @synonyms bpa + * @example + * note("") + * .sound('sawtooth') + * .bpf(500) + * .bpa("<.5 .25 .1 .01>/4") + * .bpenv(4) + */ + ['bpattack', 'bpa'], + /** + * Sets the decay duration for the lowpass filter envelope. + * @name lpdecay + * @param {number | Pattern} decay time of the filter envelope + * @synonyms lpd + * @example + * note("") + * .sound('sawtooth') + * .lpf(500) + * .lpd("<.5 .25 .1 0>/4") + * .lps(0.2) + * .lpenv(4) + */ + ['lpdecay', 'lpd'], + /** + * Sets the decay duration for the highpass filter envelope. + * @name hpdecay + * @param {number | Pattern} decay time of the highpass filter envelope + * @synonyms hpd + * @example + * note("") + * .sound('sawtooth') + * .hpf(500) + * .hpd("<.5 .25 .1 0>/4") + * .hps(0.2) + * .hpenv(4) + */ + ['hpdecay', 'hpd'], + /** + * Sets the decay duration for the bandpass filter envelope. + * @name bpdecay + * @param {number | Pattern} decay time of the bandpass filter envelope + * @synonyms bpd + * @example + * note("") + * .sound('sawtooth') + * .bpf(500) + * .bpd("<.5 .25 .1 0>/4") + * .bps(0.2) + * .bpenv(4) + */ + ['bpdecay', 'bpd'], + /** + * Sets the sustain amplitude for the lowpass filter envelope. + * @name lpsustain + * @param {number | Pattern} sustain amplitude of the lowpass filter envelope + * @synonyms lps + * @example + * note("") + * .sound('sawtooth') + * .lpf(500) + * .lpd(.5) + * .lps("<0 .25 .5 1>/4") + * .lpenv(4) + */ + ['lpsustain', 'lps'], + /** + * Sets the sustain amplitude for the highpass filter envelope. + * @name hpsustain + * @param {number | Pattern} sustain amplitude of the highpass filter envelope + * @synonyms hps + * @example + * note("") + * .sound('sawtooth') + * .hpf(500) + * .hpd(.5) + * .hps("<0 .25 .5 1>/4") + * .hpenv(4) + */ + ['hpsustain', 'hps'], + /** + * Sets the sustain amplitude for the bandpass filter envelope. + * @name bpsustain + * @param {number | Pattern} sustain amplitude of the bandpass filter envelope + * @synonyms bps + * @example + * note("") + * .sound('sawtooth') + * .bpf(500) + * .bpd(.5) + * .bps("<0 .25 .5 1>/4") + * .bpenv(4) + */ + ['bpsustain', 'bps'], + /** + * Sets the release time for the lowpass filter envelope. + * @name lprelease + * @param {number | Pattern} release time of the filter envelope + * @synonyms lpr + * @example + * note("") + * .sound('sawtooth') + * .clip(.5) + * .lpf(500) + * .lpenv(4) + * .lpr("<.5 .25 .1 0>/4") + * .release(.5) + */ + ['lprelease', 'lpr'], + /** + * Sets the release time for the highpass filter envelope. + * @name hprelease + * @param {number | Pattern} release time of the highpass filter envelope + * @synonyms hpr + * @example + * note("") + * .sound('sawtooth') + * .clip(.5) + * .hpf(500) + * .hpenv(4) + * .hpr("<.5 .25 .1 0>/4") + * .release(.5) + */ + ['hprelease', 'hpr'], + /** + * Sets the release time for the bandpass filter envelope. + * @name bprelease + * @param {number | Pattern} release time of the bandpass filter envelope + * @synonyms bpr + * @example + * note("") + * .sound('sawtooth') + * .clip(.5) + * .bpf(500) + * .bpenv(4) + * .bpr("<.5 .25 .1 0>/4") + * .release(.5) + */ + ['bprelease', 'bpr'], + /** + * Sets the filter type. The 24db filter is more aggressive. More types might be added in the future. + * @name ftype + * @param {number | Pattern} type 12db (default) or 24db + * @example + * note("") + * .sound('sawtooth') + * .lpf(500) + * .bpenv(4) + * .ftype("<12db 24db>") + */ + ['ftype'], + ['fanchor'], + /** + * Applies the cutoff frequency of the **h**igh-**p**ass **f**ilter. + * + * When using mininotation, you can also optionally add the 'hpq' parameter, separated by ':'. + * + * @name hpf + * @param {number | Pattern} frequency audible between 0 and 20000 + * @synonyms hp, hcutoff + * @example + * s("bd sd,hh*4").hpf("<4000 2000 1000 500 200 100>") + * @example + * s("bd sd,hh*4").hpf("<2000 2000:25>") + * + */ + // currently an alias of 'hcutoff' https://github.com/tidalcycles/strudel/issues/496 + // ['hpf'], + /** + * Applies a vibrato to the frequency of the oscillator. + * + * @name vib + * @synonyms vibrato, v + * @param {number | Pattern} frequency of the vibrato in hertz + * @example + * note("a") + * .vib("<.5 1 2 4 8 16>") + * @example + * // change the modulation depth with ":" + * note("a") + * .vib("<.5 1 2 4 8 16>:12") + */ + [['vib', 'vibmod'], 'vibrato', 'v'], + /** + * Sets the vibrato depth in semitones. Only has an effect if `vibrato` | `vib` | `v` is is also set + * + * @name vibmod + * @synonyms vmod + * @param {number | Pattern} depth of vibrato (in semitones) + * @example + * note("a").vib(4) + * .vibmod("<.25 .5 1 2 12>") + * @example + * // change the vibrato frequency with ":" + * note("a") + * .vibmod("<.25 .5 1 2 12>:8") + */ + [['vibmod', 'vib'], 'vmod'], + [['hcutoff', 'hresonance'], 'hpf', 'hp'], + /** + * Controls the **h**igh-**p**ass **q**-value. + * + * @name hpq + * @param {number | Pattern} q resonance factor between 0 and 50 + * @synonyms hresonance + * @example + * s("bd sd,hh*4").hpf(2000).hpq("<0 10 20 30>") + * + */ + ['hresonance', 'hpq'], + /** + * Controls the **l**ow-**p**ass **q**-value. + * + * @name lpq + * @param {number | Pattern} q resonance factor between 0 and 50 + * @synonyms resonance + * @example + * s("bd sd,hh*4").lpf(2000).lpq("<0 10 20 30>") + * + */ + // currently an alias of 'resonance' https://github.com/tidalcycles/strudel/issues/496 + ['resonance', 'lpq'], + /** + * DJ filter, below 0.5 is low pass filter, above is high pass filter. + * + * @name djf + * @param {number | Pattern} cutoff below 0.5 is low pass filter, above is high pass filter + * @example + * n("0 3 7 [10,24]").s('superzow').octave(3).djf("<.5 .25 .5 .75>").osc() + * + */ + ['djf'], + // ['cutoffegint'], + // TODO: does not seem to work + /** + * Sets the level of the delay signal. + * + * When using mininotation, you can also optionally add the 'delaytime' and 'delayfeedback' parameter, + * separated by ':'. + * + * + * @name delay + * @param {number | Pattern} level between 0 and 1 + * @example + * s("bd").delay("<0 .25 .5 1>") + * @example + * s("bd bd").delay("0.65:0.25:0.9 0.65:0.125:0.7") + * + */ + [['delay', 'delaytime', 'delayfeedback']], + /** + * Sets the level of the signal that is fed back into the delay. + * Caution: Values >= 1 will result in a signal that gets louder and louder! Don't do it + * + * @name delayfeedback + * @param {number | Pattern} feedback between 0 and 1 + * @synonyms delayfb, dfb + * @example + * s("bd").delay(.25).delayfeedback("<.25 .5 .75 1>").slow(2) + * + */ + ['delayfeedback', 'delayfb', 'dfb'], + /** + * Sets the time of the delay effect. + * + * @name delaytime + * @param {number | Pattern} seconds between 0 and Infinity + * @synonyms delayt, dt + * @example + * s("bd").delay(.25).delaytime("<.125 .25 .5 1>").slow(2) + * + */ + ['delaytime', 'delayt', 'dt'], + /* // TODO: test + * Specifies whether delaytime is calculated relative to cps. + * + * @name lock + * @param {number | Pattern} enable When set to 1, delaytime is a direct multiple of a cycle. + * @example + * s("sd").delay().lock(1).osc() + * + */ + ['lock'], + /** + * Set detune of oscillators. Works only with some synths, see tidal doc + * + * @name detune + * @param {number | Pattern} amount between 0 and 1 + * @synonyms det + * @superdirtOnly + * @example + * n("0 3 7").s('superzow').octave(3).detune("<0 .25 .5 1 2>").osc() + * + */ + ['detune', 'det'], + /** + * Set dryness of reverb. See {@link room} and {@link size} for more information about reverb. + * + * @name dry + * @param {number | Pattern} dry 0 = wet, 1 = dry + * @example + * n("[0,3,7](3,8)").s("superpiano").room(.7).dry("<0 .5 .75 1>").osc() + * @superdirtOnly + * + */ + ['dry'], + // TODO: does not seem to do anything + /* + * Used when using {@link begin}/{@link end} or {@link chop}/{@link striate} and friends, to change the fade out time of the 'grain' envelope. + * + * @name fadeTime + * @param {number | Pattern} time between 0 and 1 + * @example + * s("oh*4").end(.1).fadeTime("<0 .2 .4 .8>").osc() + * + */ + ['fadeTime', 'fadeOutTime'], + // TODO: see above + ['fadeInTime'], + /** + * Set frequency of sound. + * + * @name freq + * @param {number | Pattern} frequency in Hz. the audible range is between 20 and 20000 Hz + * @example + * freq("220 110 440 110").s("superzow").osc() + * @example + * freq("110".mul.out(".5 1.5 .6 [2 3]")).s("superzow").osc() + * + */ + ['freq'], + // TODO: https://tidalcycles.org/docs/configuration/MIDIOSC/control-voltage/#gate + ['gate', 'gat'], + // ['hatgrain'], + // ['lagogo'], + // ['lclap'], + // ['lclaves'], + // ['lclhat'], + // ['lcrash'], + // TODO: + // https://tidalcycles.org/docs/reference/audio_effects/#leslie-1 + // https://tidalcycles.org/docs/reference/audio_effects/#leslie + /** + * Emulation of a Leslie speaker: speakers rotating in a wooden amplified cabinet. + * + * @name leslie + * @param {number | Pattern} wet between 0 and 1 + * @example + * n("0,4,7").s("supersquare").leslie("<0 .4 .6 1>").osc() + * @superdirtOnly + * + */ + ['leslie'], + /** + * Rate of modulation / rotation for leslie effect + * + * @name lrate + * @param {number | Pattern} rate 6.7 for fast, 0.7 for slow + * @example + * n("0,4,7").s("supersquare").leslie(1).lrate("<1 2 4 8>").osc() + * @superdirtOnly + * + */ + // TODO: the rate seems to "lag" (in the example, 1 will be fast) + ['lrate'], + /** + * Physical size of the cabinet in meters. Be careful, it might be slightly larger than your computer. Affects the Doppler amount (pitch warble) + * + * @name lsize + * @param {number | Pattern} meters somewhere between 0 and 1 + * @example + * n("0,4,7").s("supersquare").leslie(1).lrate(2).lsize("<.1 .5 1>").osc() + * @superdirtOnly + * + */ + ['lsize'], + // label for pianoroll + ['activeLabel'], + [['label', 'activeLabel']], + // ['lfo'], + // ['lfocutoffint'], + // ['lfodelay'], + // ['lfoint'], + // ['lfopitchint'], + // ['lfoshape'], + // ['lfosync'], + // ['lhitom'], + // ['lkick'], + // ['llotom'], + // ['lophat'], + // ['lsnare'], + ['degree'], // TODO: what is this? not found in tidal doc + ['mtranspose'], // TODO: what is this? not found in tidal doc + ['ctranspose'], // TODO: what is this? not found in tidal doc + ['harmonic'], // TODO: what is this? not found in tidal doc + ['stepsPerOctave'], // TODO: what is this? not found in tidal doc + ['octaveR'], // TODO: what is this? not found in tidal doc + // TODO: why is this needed? what's the difference to late / early? Answer: it's in seconds, and delays the message at + // OSC time (so can't be negative, at least not beyond the latency value) + ['nudge'], + // TODO: the following doc is just a guess, it's not documented in tidal doc. + /** + * Sets the default octave of a synth. + * + * @name octave + * @param {number | Pattern} octave octave number + * @example + * n("0,4,7").s('supersquare').octave("<3 4 5 6>").osc() + * @superDirtOnly + */ + ['octave'], - // ['ophatdecay'], - // TODO: example - /** - * An `orbit` is a global parameter context for patterns. Patterns with the same orbit will share the same global effects. - * - * @name orbit - * @param {number | Pattern} number - * @example - * stack( - * s("hh*3").delay(.5).delaytime(.25).orbit(1), - * s("~ sd").delay(.5).delaytime(.125).orbit(2) - * ) - */ - ['orbit'], - ['overgain'], // TODO: what is this? not found in tidal doc Answer: gain is limited to maximum of 2. This allows you to go over that - ['overshape'], // TODO: what is this? not found in tidal doc. Similar to above, but limited to 1 - /** - * Sets position in stereo. - * - * @name pan - * @param {number | Pattern} pan between 0 and 1, from left to right (assuming stereo), once round a circle (assuming multichannel) - * @example - * s("[bd hh]*2").pan("<.5 1 .5 0>") - * - */ - ['pan'], - // TODO: this has no effect (see example) - /* - * Controls how much multichannel output is fanned out - * - * @name panspan - * @param {number | Pattern} span between -inf and inf, negative is backwards ordering - * @example - * s("[bd hh]*2").pan("<.5 1 .5 0>").panspan("<0 .5 1>").osc() - * - */ - ['panspan'], - // TODO: this has no effect (see example) - /* - * Controls how much multichannel output is spread - * - * @name pansplay - * @param {number | Pattern} spread between 0 and 1 - * @example - * s("[bd hh]*2").pan("<.5 1 .5 0>").pansplay("<0 .5 1>").osc() - * - */ - ['pansplay'], - ['panwidth'], - ['panorient'], - // ['pitch1'], - // ['pitch2'], - // ['pitch3'], - // ['portamento'], - // TODO: LFO rate see https://tidalcycles.org/docs/patternlib/tutorials/synthesizers/#supersquare - ['rate'], - // TODO: slide param for certain synths - ['slide'], - // TODO: detune? https://tidalcycles.org/docs/patternlib/tutorials/synthesizers/#supersquare - ['semitone'], - // TODO: dedup with synth param, see https://tidalcycles.org/docs/reference/synthesizers/#superpiano - // ['velocity'], - ['voice'], // TODO: synth param + // ['ophatdecay'], + // TODO: example + /** + * An `orbit` is a global parameter context for patterns. Patterns with the same orbit will share the same global effects. + * + * @name orbit + * @param {number | Pattern} number + * @example + * stack( + * s("hh*3").delay(.5).delaytime(.25).orbit(1), + * s("~ sd").delay(.5).delaytime(.125).orbit(2) + * ) + */ + ['orbit'], + ['overgain'], // TODO: what is this? not found in tidal doc Answer: gain is limited to maximum of 2. This allows you to go over that + ['overshape'], // TODO: what is this? not found in tidal doc. Similar to above, but limited to 1 + /** + * Sets position in stereo. + * + * @name pan + * @param {number | Pattern} pan between 0 and 1, from left to right (assuming stereo), once round a circle (assuming multichannel) + * @example + * s("[bd hh]*2").pan("<.5 1 .5 0>") + * + */ + ['pan'], + // TODO: this has no effect (see example) + /* + * Controls how much multichannel output is fanned out + * + * @name panspan + * @param {number | Pattern} span between -inf and inf, negative is backwards ordering + * @example + * s("[bd hh]*2").pan("<.5 1 .5 0>").panspan("<0 .5 1>").osc() + * + */ + ['panspan'], + // TODO: this has no effect (see example) + /* + * Controls how much multichannel output is spread + * + * @name pansplay + * @param {number | Pattern} spread between 0 and 1 + * @example + * s("[bd hh]*2").pan("<.5 1 .5 0>").pansplay("<0 .5 1>").osc() + * + */ + ['pansplay'], + ['panwidth'], + ['panorient'], + // ['pitch1'], + // ['pitch2'], + // ['pitch3'], + // ['portamento'], + // TODO: LFO rate see https://tidalcycles.org/docs/patternlib/tutorials/synthesizers/#supersquare + ['rate'], + // TODO: slide param for certain synths + ['slide'], + // TODO: detune? https://tidalcycles.org/docs/patternlib/tutorials/synthesizers/#supersquare + ['semitone'], + // TODO: dedup with synth param, see https://tidalcycles.org/docs/reference/synthesizers/#superpiano + // ['velocity'], + ['voice'], // TODO: synth param - // voicings // https://github.com/tidalcycles/strudel/issues/506 - ['chord'], // chord to voice, like C Eb Fm7 G7. the symbols can be defined via addVoicings - ['dictionary', 'dict'], // which dictionary to use for the voicings - ['anchor'], // the top note to align the voicing to, defaults to c5 - ['offset'], // how the voicing is offset from the anchored position - ['octaves'], // how many octaves are voicing steps spread apart, defaults to 1 - [['mode', 'anchor']], // below = anchor note will be removed from the voicing, useful for melody harmonization + // voicings // https://github.com/tidalcycles/strudel/issues/506 + ['chord'], // chord to voice, like C Eb Fm7 G7. the symbols can be defined via addVoicings + ['dictionary', 'dict'], // which dictionary to use for the voicings + ['anchor'], // the top note to align the voicing to, defaults to c5 + ['offset'], // how the voicing is offset from the anchored position + ['octaves'], // how many octaves are voicing steps spread apart, defaults to 1 + [['mode', 'anchor']], // below = anchor note will be removed from the voicing, useful for melody harmonization - /** - * Sets the level of reverb. - * - * When using mininotation, you can also optionally add the 'size' parameter, separated by ':'. - * - * @name room - * @param {number | Pattern} level between 0 and 1 - * @example - * s("bd sd").room("<0 .2 .4 .6 .8 1>") - * @example - * s("bd sd").room("<0.9:1 0.9:4>") - * - */ - [['room', 'size']], - /** - * Sets the room size of the reverb, see {@link room}. - * - * @name roomsize - * @param {number | Pattern} size between 0 and 10 - * @synonyms size, sz - * @example - * s("bd sd").room(.8).roomsize("<0 1 2 4 8>") - * - */ - // TODO: find out why : - // s("bd sd").room(.8).roomsize("<0 .2 .4 .6 .8 [1,0]>").osc() - // .. does not work. Is it because room is only one effect? - ['size', 'sz', 'roomsize'], - // ['sagogo'], - // ['sclap'], - // ['sclaves'], - // ['scrash'], + /** + * Sets the level of reverb. + * + * When using mininotation, you can also optionally add the 'size' parameter, separated by ':'. + * + * @name room + * @param {number | Pattern} level between 0 and 1 + * @example + * s("bd sd").room("<0 .2 .4 .6 .8 1>") + * @example + * s("bd sd").room("<0.9:1 0.9:4>") + * + */ + [['room', 'size']], + /** + * Sets the room size of the reverb, see {@link room}. + * + * @name roomsize + * @param {number | Pattern} size between 0 and 10 + * @synonyms size, sz + * @example + * s("bd sd").room(.8).roomsize("<0 1 2 4 8>") + * + */ - /** - * Sets the sample to use as an impulse response for the reverb. - * - * @name iresponse - * @param {string | Pattern} Sets the impulse response - * @example - * s("bd sd").room(.8).ir("") - * - */ - [['ir', 'i'], 'iresponse'], - /** - * Wave shaping distortion. CAUTION: it might get loud - * - * @name shape - * @param {number | Pattern} distortion between 0 and 1 - * @example - * s("bd sd,hh*4").shape("<0 .2 .4 .6 .8>") - * - */ - ['shape'], - /** - * Changes the speed of sample playback, i.e. a cheap way of changing pitch. - * - * @name speed - * @param {number | Pattern} speed -inf to inf, negative numbers play the sample backwards. - * @example - * s("bd").speed("<1 2 4 1 -2 -4>") - * @example - * speed("1 1.5*2 [2 1.1]").s("piano").clip(1) - * - */ - ['speed'], - /** - * Used in conjunction with {@link speed}, accepts values of "r" (rate, default behavior), "c" (cycles), or "s" (seconds). Using `unit "c"` means `speed` will be interpreted in units of cycles, e.g. `speed "1"` means samples will be stretched to fill a cycle. Using `unit "s"` means the playback speed will be adjusted so that the duration is the number of seconds specified by `speed`. - * - * @name unit - * @param {number | string | Pattern} unit see description above - * @example - * speed("1 2 .5 3").s("bd").unit("c").osc() - * @superdirtOnly - * - */ - ['unit'], - /** - * Made by Calum Gunn. Reminiscent of some weird mixture of filter, ring-modulator and pitch-shifter. The SuperCollider manual defines Squiz as: - * - * "A simplistic pitch-raising algorithm. It's not meant to sound natural; its sound is reminiscent of some weird mixture of filter, ring-modulator and pitch-shifter, depending on the input. The algorithm works by cutting the signal into fragments (delimited by upwards-going zero-crossings) and squeezing those fragments in the time domain (i.e. simply playing them back faster than they came in), leaving silences inbetween. All the parameters apart from memlen can be modulated." - * - * @name squiz - * @param {number | Pattern} squiz Try passing multiples of 2 to it - 2, 4, 8 etc. - * @example - * squiz("2 4/2 6 [8 16]").s("bd").osc() - * @superdirtOnly - * - */ - ['squiz'], - // ['stutterdepth'], // TODO: what is this? not found in tidal doc - // ['stuttertime'], // TODO: what is this? not found in tidal doc - // ['timescale'], // TODO: what is this? not found in tidal doc - // ['timescalewin'], // TODO: what is this? not found in tidal doc - // ['tomdecay'], - // ['vcfegint'], - // ['vcoegint'], - // TODO: Use a rest (~) to override the effect <- vowel - /** - * - * Formant filter to make things sound like vowels. - * - * @name vowel - * @param {string | Pattern} vowel You can use a e i o u. - * @example - * note("c2 >").s('sawtooth') - * .vowel(">") - * - */ - ['vowel'], - /* // TODO: find out how it works - * Made by Calum Gunn. Divides an audio stream into tiny segments, using the signal's zero-crossings as segment boundaries, and discards a fraction of them. Takes a number between 1 and 100, denoted the percentage of segments to drop. The SuperCollider manual describes the Waveloss effect this way: - * - * Divide an audio stream into tiny segments, using the signal's zero-crossings as segment boundaries, and discard a fraction of them (i.e. replace them with silence of the same length). The technique was described by Trevor Wishart in a lecture. Parameters: the filter drops drop out of out of chunks. mode can be 1 to drop chunks in a simple deterministic fashion (e.g. always dropping the first 30 out of a set of 40 segments), or 2 to drop chunks randomly but in an appropriate proportion.) - * - * mode: ? - * waveloss: ? - * - * @name waveloss - */ - ['waveloss'], - // TODO: midi effects? - ['dur'], - // ['modwheel'], - ['expression'], - ['sustainpedal'], - /* // TODO: doesn't seem to do anything - * - * Tremolo Audio DSP effect - * - * @name tremolodepth - * @param {number | Pattern} depth between 0 and 1 - * @example - * n("0,4,7").tremolodepth("<0 .3 .6 .9>").osc() - * - */ - ['tremolodepth', 'tremdp'], - ['tremolorate', 'tremr'], - // TODO: doesn't seem to do anything - ['phaserdepth', 'phasdp'], - ['phaserrate', 'phasr'], + // TODO: find out why : + // s("bd sd").room(.8).roomsize("<0 .2 .4 .6 .8 [1,0]>").osc() + // .. does not work. Is it because room is only one effect? + ['size', 'sz', 'roomsize'], + // ['sagogo'], + // ['sclap'], + // ['sclaves'], + // ['scrash'], - ['fshift'], - ['fshiftnote'], - ['fshiftphase'], + /** + * Sets the sample to use as an impulse response for the reverb. + * + * @name iresponse + * @param {string | Pattern} Sets the impulse response + * @example + * s("bd sd").room(.8).ir("") + * + */ + [['ir', 'i'], 'iresponse'], - ['triode'], - ['krush'], - ['kcutoff'], - ['octer'], - ['octersub'], - ['octersubsub'], - ['ring'], - ['ringf'], - ['ringdf'], - ['distort'], - ['freeze'], - ['xsdelay'], - ['tsdelay'], - ['real'], - ['imag'], - ['enhance'], - ['partials'], - ['comb'], - ['smear'], - ['scram'], - ['binshift'], - ['hbrick'], - ['lbrick'], - ['midichan'], - ['control'], - ['ccn'], - ['ccv'], - ['polyTouch'], - ['midibend'], - ['miditouch'], - ['ctlNum'], - ['frameRate'], - ['frames'], - ['hours'], - ['midicmd'], - ['minutes'], - ['progNum'], - ['seconds'], - ['songPtr'], - ['uid'], - ['val'], - ['cps'], - /** - * Multiplies the duration with the given number. Also cuts samples off at the end if they exceed the duration. - * In tidal, this would be done with legato, [which has a complicated history in strudel](https://github.com/tidalcycles/strudel/issues/111). - * For now, if you're coming from tidal, just think clip = legato. - * - * @name clip - * @param {number | Pattern} factor >= 0 - * @example - * note("c a f e").s("piano").clip("<.5 1 2>") - * - */ - ['clip'], + /** + * Wave shaping distortion. CAUTION: it might get loud + * + * @name shape + * @param {number | Pattern} distortion between 0 and 1 + * @example + * s("bd sd,hh*4").shape("<0 .2 .4 .6 .8>") + * + */ + ['shape'], + /** + * Changes the speed of sample playback, i.e. a cheap way of changing pitch. + * + * @name speed + * @param {number | Pattern} speed -inf to inf, negative numbers play the sample backwards. + * @example + * s("bd").speed("<1 2 4 1 -2 -4>") + * @example + * speed("1 1.5*2 [2 1.1]").s("piano").clip(1) + * + */ + ['speed'], + /** + * Used in conjunction with {@link speed}, accepts values of "r" (rate, default behavior), "c" (cycles), or "s" (seconds). Using `unit "c"` means `speed` will be interpreted in units of cycles, e.g. `speed "1"` means samples will be stretched to fill a cycle. Using `unit "s"` means the playback speed will be adjusted so that the duration is the number of seconds specified by `speed`. + * + * @name unit + * @param {number | string | Pattern} unit see description above + * @example + * speed("1 2 .5 3").s("bd").unit("c").osc() + * @superdirtOnly + * + */ + ['unit'], + /** + * Made by Calum Gunn. Reminiscent of some weird mixture of filter, ring-modulator and pitch-shifter. The SuperCollider manual defines Squiz as: + * + * "A simplistic pitch-raising algorithm. It's not meant to sound natural; its sound is reminiscent of some weird mixture of filter, ring-modulator and pitch-shifter, depending on the input. The algorithm works by cutting the signal into fragments (delimited by upwards-going zero-crossings) and squeezing those fragments in the time domain (i.e. simply playing them back faster than they came in), leaving silences inbetween. All the parameters apart from memlen can be modulated." + * + * @name squiz + * @param {number | Pattern} squiz Try passing multiples of 2 to it - 2, 4, 8 etc. + * @example + * squiz("2 4/2 6 [8 16]").s("bd").osc() + * @superdirtOnly + * + */ + ['squiz'], + // ['stutterdepth'], // TODO: what is this? not found in tidal doc + // ['stuttertime'], // TODO: what is this? not found in tidal doc + // ['timescale'], // TODO: what is this? not found in tidal doc + // ['timescalewin'], // TODO: what is this? not found in tidal doc + // ['tomdecay'], + // ['vcfegint'], + // ['vcoegint'], + // TODO: Use a rest (~) to override the effect <- vowel + /** + * + * Formant filter to make things sound like vowels. + * + * @name vowel + * @param {string | Pattern} vowel You can use a e i o u. + * @example + * note("c2 >").s('sawtooth') + * .vowel(">") + * + */ + ['vowel'], + /* // TODO: find out how it works + * Made by Calum Gunn. Divides an audio stream into tiny segments, using the signal's zero-crossings as segment boundaries, and discards a fraction of them. Takes a number between 1 and 100, denoted the percentage of segments to drop. The SuperCollider manual describes the Waveloss effect this way: + * + * Divide an audio stream into tiny segments, using the signal's zero-crossings as segment boundaries, and discard a fraction of them (i.e. replace them with silence of the same length). The technique was described by Trevor Wishart in a lecture. Parameters: the filter drops drop out of out of chunks. mode can be 1 to drop chunks in a simple deterministic fashion (e.g. always dropping the first 30 out of a set of 40 segments), or 2 to drop chunks randomly but in an appropriate proportion.) + * + * mode: ? + * waveloss: ? + * + * @name waveloss + */ + ['waveloss'], + // TODO: midi effects? + ['dur'], + // ['modwheel'], + ['expression'], + ['sustainpedal'], + /* // TODO: doesn't seem to do anything + * + * Tremolo Audio DSP effect + * + * @name tremolodepth + * @param {number | Pattern} depth between 0 and 1 + * @example + * n("0,4,7").tremolodepth("<0 .3 .6 .9>").osc() + * + */ + ['tremolodepth', 'tremdp'], + ['tremolorate', 'tremr'], + // TODO: doesn't seem to do anything + ['phaserdepth', 'phasdp'], + ['phaserrate', 'phasr'], - // ZZFX - ['zrand'], - ['curve'], - ['slide'], // superdirt duplicate - ['deltaSlide'], - ['pitchJump'], - ['pitchJumpTime'], - ['lfo', 'repeatTime'], - ['noise'], - ['zmod'], - ['zcrush'], // like crush but scaled differently - ['zdelay'], - ['tremolo'], - ['zzfx'], + ['fshift'], + ['fshiftnote'], + ['fshiftphase'], + + ['triode'], + ['krush'], + ['kcutoff'], + ['octer'], + ['octersub'], + ['octersubsub'], + ['ring'], + ['ringf'], + ['ringdf'], + ['distort'], + ['freeze'], + ['xsdelay'], + ['tsdelay'], + ['real'], + ['imag'], + ['enhance'], + ['partials'], + ['comb'], + ['smear'], + ['scram'], + ['binshift'], + ['hbrick'], + ['lbrick'], + ['midichan'], + ['control'], + ['ccn'], + ['ccv'], + ['polyTouch'], + ['midibend'], + ['miditouch'], + ['ctlNum'], + ['frameRate'], + ['frames'], + ['hours'], + ['midicmd'], + ['minutes'], + ['progNum'], + ['seconds'], + ['songPtr'], + ['uid'], + ['val'], + ['cps'], + /** + * Multiplies the duration with the given number. Also cuts samples off at the end if they exceed the duration. + * In tidal, this would be done with legato, [which has a complicated history in strudel](https://github.com/tidalcycles/strudel/issues/111). + * For now, if you're coming from tidal, just think clip = legato. + * + * @name clip + * @param {number | Pattern} factor >= 0 + * @example + * note("c a f e").s("piano").clip("<.5 1 2>") + * + */ + ['clip'], + + // ZZFX + ['zrand'], + ['curve'], + ['slide'], // superdirt duplicate + ['deltaSlide'], + ['pitchJump'], + ['pitchJumpTime'], + ['lfo', 'repeatTime'], + ['noise'], + ['zmod'], + ['zcrush'], // like crush but scaled differently + ['zdelay'], + ['tremolo'], + ['zzfx'], ]; // TODO: slice / splice https://www.youtube.com/watch?v=hKhPdO0RKDQ&list=PL2lW1zNIIwj3bDkh-Y3LUGDuRcoUigoDs&index=13 controls.createParam = function (names) { - const name = Array.isArray(names) ? names[0] : names; + const name = Array.isArray(names) ? names[0] : names; - var withVal; - if (Array.isArray(names)) { - withVal = (xs) => { - if (Array.isArray(xs)) { - const result = {}; - xs.forEach((x, i) => { - if (i < names.length) { - result[names[i]] = x; - } - }); - return result; - } else { - return {[name]: xs}; - } - }; - } else { - withVal = (x) => ({[name]: x}); - } - - const func = (...pats) => sequence(...pats).withValue(withVal); - - const setter = function (...pats) { - if (!pats.length) { - return this.fmap(withVal); - } - return this.set(func(...pats)); + var withVal; + if (Array.isArray(names)) { + withVal = (xs) => { + if (Array.isArray(xs)) { + const result = {}; + xs.forEach((x, i) => { + if (i < names.length) { + result[names[i]] = x; + } + }); + return result; + } else { + return { [name]: xs }; + } }; - Pattern.prototype[name] = setter; - return func; + } else { + withVal = (x) => ({ [name]: x }); + } + + const func = (...pats) => sequence(...pats).withValue(withVal); + + const setter = function (...pats) { + if (!pats.length) { + return this.fmap(withVal); + } + return this.set(func(...pats)); + }; + Pattern.prototype[name] = setter; + return func; }; generic_params.forEach(([names, ...aliases]) => { - const name = Array.isArray(names) ? names[0] : names; - controls[name] = controls.createParam(names); + const name = Array.isArray(names) ? names[0] : names; + controls[name] = controls.createParam(names); - aliases.forEach((alias) => { - controls[alias] = controls[name]; - Pattern.prototype[alias] = Pattern.prototype[name]; - }); + aliases.forEach((alias) => { + controls[alias] = controls[name]; + Pattern.prototype[alias] = Pattern.prototype[name]; + }); }); controls.createParams = (...names) => - names.reduce((acc, name) => Object.assign(acc, {[name]: controls.createParam(name)}), {}); + names.reduce((acc, name) => Object.assign(acc, { [name]: controls.createParam(name) }), {}); controls.adsr = register('adsr', (adsr, pat) => { - adsr = !Array.isArray(adsr) ? [adsr] : adsr; - const [attack, decay, sustain, release] = adsr; - return pat.set({attack, decay, sustain, release}); + adsr = !Array.isArray(adsr) ? [adsr] : adsr; + const [attack, decay, sustain, release] = adsr; + return pat.set({ attack, decay, sustain, release }); }); controls.ds = register('ds', (ds, pat) => { - ds = !Array.isArray(ds) ? [ds] : ds; - const [decay, sustain] = ds; - return pat.set({decay, sustain}); + ds = !Array.isArray(ds) ? [ds] : ds; + const [decay, sustain] = ds; + return pat.set({ decay, sustain }); }); export default controls; diff --git a/packages/superdough/reverb.mjs b/packages/superdough/reverb.mjs index 72709085..e72af033 100644 --- a/packages/superdough/reverb.mjs +++ b/packages/superdough/reverb.mjs @@ -7,22 +7,31 @@ if (typeof AudioContext !== 'undefined') { return impulse; }; + AudioContext.prototype.adjustLength = function (duration, buffer) { + const newLength = buffer.sampleRate * duration; + const newBuffer = this.createBuffer(buffer.numberOfChannels, buffer.length, buffer.sampleRate); + for (let channel = 0; channel < buffer.numberOfChannels; channel++) { + let oldData = buffer.getChannelData(channel); + let newData = newBuffer.getChannelData(channel); + + for (let i = 0; i < newLength; i++) { + newData[i] = oldData[i] || 0; + } + } + return newBuffer; + }; + AudioContext.prototype.createReverb = function (duration, buffer) { const convolver = this.createConvolver(); - convolver.setDuration = (d, i) => { - convolver.buffer = i !== undefined ? buffer : this.impulseResponse(d); - convolver.duration = d; + convolver.setDuration = (dur, imp) => { + convolver.buffer = imp ? this.adjustLength(dur, imp) : this.impulseResponse(dur); return convolver; }; - convolver.setIR = (i) => { - convolver.buffer = i; + convolver.setIR = (dur, imp) => { + convolver.buffer = imp ? this.adjustLength(dur, imp) : this.impulseResponse(dur); return convolver; }; - if (buffer !== undefined) { - convolver.setIR(buffer); - } else { - convolver.setDuration(duration); - } + convolver.setDuration(duration, buffer); return convolver; }; } diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index 2b46ae3c..b61c9568 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -122,11 +122,11 @@ function getReverb(orbit, duration = 2, ir) { reverbs[orbit] = reverb; } if (reverbs[orbit].duration !== duration) { - reverbs[orbit] = reverbs[orbit].setDuration(duration); + reverbs[orbit] = reverbs[orbit].setDuration(duration, ir); reverbs[orbit].duration = duration; } if (reverbs[orbit].ir !== ir) { - reverbs[orbit] = reverbs[orbit].setIR(ir); + reverbs[orbit] = reverbs[orbit].setIR(duration, ir); reverbs[orbit].ir = ir; } return reverbs[orbit]; @@ -368,9 +368,14 @@ export const superdough = async (value, deadline, hapDuration) => { } // reverb let buffer; + let url; if (ir !== undefined) { let sample = getSound(ir); - let url = sample.data.samples[i % sample.data.samples.length]; + if (Array.isArray(sample)) { + url = sample.data.samples[i % sample.data.samples.length]; + } else if (typeof sample === 'object') { + url = Object.values(sample.data.samples)[i & Object.values(sample.data.samples).length]; + } buffer = await loadBuffer(url, ac, ir, 0); } let reverbSend; From ac9c629c0b271790b5b39d0efd5e4ac63480aa2a Mon Sep 17 00:00:00 2001 From: Vasilii Milovidov Date: Sun, 1 Oct 2023 15:36:52 +0400 Subject: [PATCH 037/158] cleanup --- packages/superdough/superdough.mjs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index b61c9568..fdf42c02 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -163,7 +163,7 @@ export function getAnalyzerData(type = 'time') { return analyserData; } -function effectSend(input, effect, wet, size) { +function effectSend(input, effect, wet) { const send = gainNode(wet); input.connect(send); send.connect(effect); @@ -381,7 +381,7 @@ export const superdough = async (value, deadline, hapDuration) => { let reverbSend; if (room > 0 && size > 0) { const reverbNode = getReverb(orbit, size, buffer); - reverbSend = effectSend(post, reverbNode, room, size); + reverbSend = effectSend(post, reverbNode, room); } // analyser From b550ff038c33b1ecabff5f52e8916f0092740e31 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Sun, 1 Oct 2023 13:42:29 +0200 Subject: [PATCH 038/158] simplify: use native events --- packages/codemirror/slider.mjs | 75 +++++++++------------------------- 1 file changed, 19 insertions(+), 56 deletions(-) diff --git a/packages/codemirror/slider.mjs b/packages/codemirror/slider.mjs index d4fbcefa..a4ee6a93 100644 --- a/packages/codemirror/slider.mjs +++ b/packages/codemirror/slider.mjs @@ -6,7 +6,7 @@ export let sliderValues = {}; const getSliderID = (from) => `slider_${from}`; export class SliderWidget extends WidgetType { - constructor(value, min, max, from, to) { + constructor(value, min, max, from, to, view) { super(); this.value = value; this.min = min; @@ -14,6 +14,7 @@ export class SliderWidget extends WidgetType { this.from = from; this.originalFrom = from; this.to = to; + this.view = view; } eq() { @@ -38,11 +39,23 @@ export class SliderWidget extends WidgetType { slider.to = this.to; slider.className = 'w-16 translate-y-1'; this.slider = slider; + slider.addEventListener('input', (e) => { + const next = e.target.value; + let insert = next; + //let insert = next.toFixed(2); + const to = slider.from + slider.originalValue.length; + let change = { from: slider.from, to, insert }; + slider.originalValue = insert; + slider.value = insert; + this.view.dispatch({ changes: change }); + const id = getSliderID(slider.originalFrom); // matches id generated in transpiler + window.postMessage({ type: 'cm-slider', value: Number(next), id }); + }); return wrap; } - ignoreEvent() { - return false; + ignoreEvent(e) { + return true; } } @@ -52,12 +65,10 @@ export const updateWidgets = (view, widgets) => { view.dispatch({ effects: setWidgets.of(widgets) }); }; -let draggedSlider; - -function getWidgets(widgetConfigs) { +function getWidgets(widgetConfigs, view) { return widgetConfigs.map(({ from, to, value, min, max }) => { return Decoration.widget({ - widget: new SliderWidget(value, min, max, from, to), + widget: new SliderWidget(value, min, max, from, to, view), side: 0, }).range(from /* , to */); }); @@ -86,7 +97,7 @@ export const sliderPlugin = ViewPlugin.fromClass( } for (let e of tr.effects) { if (e.is(setWidgets)) { - this.decorations = Decoration.set(getWidgets(e.value)); + this.decorations = Decoration.set(getWidgets(e.value, update.view)); } } }); @@ -94,57 +105,9 @@ export const sliderPlugin = ViewPlugin.fromClass( }, { decorations: (v) => v.decorations, - - eventHandlers: { - mousedown: (e, view) => { - let target = e.target; - if (target.nodeName == 'INPUT' && target.parentElement.classList.contains('cm-slider')) { - e.preventDefault(); - e.stopPropagation(); - draggedSlider = target; - // remember offsetLeft / clientWidth, as they will vanish inside mousemove events for some reason - draggedSlider._offsetLeft = draggedSlider.offsetLeft; - draggedSlider._clientWidth = draggedSlider.clientWidth; - return updateSliderValue(view, e); - } - }, - mouseup: () => { - draggedSlider = undefined; - }, - mousemove: (e, view) => { - draggedSlider && updateSliderValue(view, e); - }, - }, }, ); -// moves slider on mouse event -function updateSliderValue(view, e) { - const mouseX = e.clientX; - let mx = 10; - let progress = (mouseX - draggedSlider._offsetLeft - mx) / (draggedSlider._clientWidth - mx * 2); - progress = Math.max(Math.min(1, progress), 0); - let min = Number(draggedSlider.min); - let max = Number(draggedSlider.max); - const next = Number(progress * (max - min) + min); - let insert = next.toFixed(2); - //let before = view.state.doc.sliceString(draggedSlider.from, draggedSlider.to).trim(); - let before = draggedSlider.originalValue; - before = Number(before).toFixed(2); - // console.log('before', before, 'insert', insert, 'v'); - if (before === insert) { - return false; - } - const to = draggedSlider.from + draggedSlider.originalValue.length; - let change = { from: draggedSlider.from, to, insert }; - draggedSlider.originalValue = insert; - draggedSlider.value = insert; - view.dispatch({ changes: change }); - const id = getSliderID(draggedSlider.originalFrom); // matches id generated in transpiler - window.postMessage({ type: 'cm-slider', value: next, id }); - return true; -} - // user api export let slider = (id, value, min, max) => { sliderValues[id] = value; // sync state at eval time (code -> state) From f84d5ba3a02d7d498cef44df7baaee654ab378b0 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Sun, 1 Oct 2023 13:44:41 +0200 Subject: [PATCH 039/158] add back some margin --- packages/codemirror/slider.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/codemirror/slider.mjs b/packages/codemirror/slider.mjs index a4ee6a93..47bb7509 100644 --- a/packages/codemirror/slider.mjs +++ b/packages/codemirror/slider.mjs @@ -37,7 +37,7 @@ export class SliderWidget extends WidgetType { slider.from = this.from; slider.originalFrom = this.originalFrom; slider.to = this.to; - slider.className = 'w-16 translate-y-1'; + slider.className = 'w-16 translate-y-1 mr-1'; this.slider = slider; slider.addEventListener('input', (e) => { const next = e.target.value; From d4bf358eae42ccbd78e1c6b9df81f9f1bba44d8c Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Sun, 1 Oct 2023 13:56:57 +0200 Subject: [PATCH 040/158] use raw css instead of tailwind --- packages/codemirror/slider.mjs | 2 +- website/tailwind.config.cjs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/codemirror/slider.mjs b/packages/codemirror/slider.mjs index 47bb7509..e7be78f7 100644 --- a/packages/codemirror/slider.mjs +++ b/packages/codemirror/slider.mjs @@ -37,7 +37,7 @@ export class SliderWidget extends WidgetType { slider.from = this.from; slider.originalFrom = this.originalFrom; slider.to = this.to; - slider.className = 'w-16 translate-y-1 mr-1'; + slider.style = 'width:64px;margin-right:4px;transform:translateY(4px)'; this.slider = slider; slider.addEventListener('input', (e) => { const next = e.target.value; diff --git a/website/tailwind.config.cjs b/website/tailwind.config.cjs index 2c682d7d..d92d5949 100644 --- a/website/tailwind.config.cjs +++ b/website/tailwind.config.cjs @@ -7,7 +7,6 @@ module.exports = { content: [ './src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}', '../packages/react/src/**/*.{html,js,jsx,md,mdx,ts,tsx}', - '../packages/codemirror/slider.mjs', ], theme: { extend: { From 564697e175e99ffea7430db1bb6d48d1757bf284 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Sun, 1 Oct 2023 14:05:16 +0200 Subject: [PATCH 041/158] add extra sliderWithID function + add warning to slider function --- packages/codemirror/slider.mjs | 8 ++++++-- packages/transpiler/transpiler.mjs | 1 + 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/codemirror/slider.mjs b/packages/codemirror/slider.mjs index e7be78f7..159e4fd1 100644 --- a/packages/codemirror/slider.mjs +++ b/packages/codemirror/slider.mjs @@ -108,8 +108,12 @@ export const sliderPlugin = ViewPlugin.fromClass( }, ); -// user api -export let slider = (id, value, min, max) => { +export let slider = (value) => { + console.warn('slider will only work when the transpiler is used... passing value as is'); + return pure(value); +}; +// function transpiled from slider = (value, min, max) +export let sliderWithID = (id, value, min, max) => { sliderValues[id] = value; // sync state at eval time (code -> state) return ref(() => sliderValues[id]); // use state at query time }; diff --git a/packages/transpiler/transpiler.mjs b/packages/transpiler/transpiler.mjs index 5fa9dc49..37a937bc 100644 --- a/packages/transpiler/transpiler.mjs +++ b/packages/transpiler/transpiler.mjs @@ -121,5 +121,6 @@ function widgetWithLocation(node) { value: id, raw: id, }); + node.callee.name = 'sliderWithID'; return node; } From c051a1249d7a6adede3817f85cb3fa0992e33ba5 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Sun, 1 Oct 2023 14:06:32 +0200 Subject: [PATCH 042/158] remove checkbox --- packages/codemirror/checkbox.mjs | 87 ------------------- packages/react/src/components/CodeMirror6.jsx | 3 +- 2 files changed, 1 insertion(+), 89 deletions(-) delete mode 100644 packages/codemirror/checkbox.mjs diff --git a/packages/codemirror/checkbox.mjs b/packages/codemirror/checkbox.mjs deleted file mode 100644 index 279e2eb8..00000000 --- a/packages/codemirror/checkbox.mjs +++ /dev/null @@ -1,87 +0,0 @@ -import { WidgetType } from '@codemirror/view'; -import { ViewPlugin, Decoration } from '@codemirror/view'; -import { syntaxTree } from '@codemirror/language'; - -export class CheckboxWidget extends WidgetType { - constructor(checked) { - super(); - this.checked = checked; - } - - eq(other) { - return other.checked == this.checked; - } - - toDOM() { - let wrap = document.createElement('span'); - wrap.setAttribute('aria-hidden', 'true'); - wrap.className = 'cm-boolean-toggle'; - let box = wrap.appendChild(document.createElement('input')); - box.type = 'checkbox'; - box.checked = this.checked; - return wrap; - } - - ignoreEvent() { - return false; - } -} - -// EditorView -export function checkboxes(view) { - let widgets = []; - for (let { from, to } of view.visibleRanges) { - syntaxTree(view.state).iterate({ - from, - to, - enter: (node) => { - if (node.name == 'BooleanLiteral') { - let isTrue = view.state.doc.sliceString(node.from, node.to) == 'true'; - let deco = Decoration.widget({ - widget: new CheckboxWidget(isTrue), - side: 1, - }); - widgets.push(deco.range(node.from)); - } - }, - }); - } - return Decoration.set(widgets); -} - -export const checkboxPlugin = ViewPlugin.fromClass( - class { - decorations; //: DecorationSet - - constructor(view /* : EditorView */) { - this.decorations = checkboxes(view); - } - - update(update /* : ViewUpdate */) { - if (update.docChanged || update.viewportChanged) this.decorations = checkboxes(update.view); - } - }, - { - decorations: (v) => v.decorations, - - eventHandlers: { - mousedown: (e, view) => { - let target = e.target; /* as HTMLElement */ - if (target.nodeName == 'INPUT' && target.parentElement.classList.contains('cm-boolean-toggle')) - return toggleBoolean(view, view.posAtDOM(target)); - }, - }, - }, -); - -function toggleBoolean(view /* : EditorView */, pos /* : number */) { - let before = view.state.doc.sliceString(Math.max(0, pos), pos + 5).trim(); - let change; - if (!['true', 'false'].includes(before)) { - return false; - } - let insert = before === 'true' ? 'false' : 'true'; - change = { from: pos, to: pos + before.length, insert }; - view.dispatch({ changes: change }); - return true; -} diff --git a/packages/react/src/components/CodeMirror6.jsx b/packages/react/src/components/CodeMirror6.jsx index f3c764e0..a5af5312 100644 --- a/packages/react/src/components/CodeMirror6.jsx +++ b/packages/react/src/components/CodeMirror6.jsx @@ -15,12 +15,11 @@ import { updateMiniLocations, } from '@strudel/codemirror'; import './style.css'; -import { checkboxPlugin } from '@strudel/codemirror/checkbox.mjs'; import { sliderPlugin } from '@strudel/codemirror/slider.mjs'; export { flash, highlightMiniLocations, updateMiniLocations }; -const staticExtensions = [javascript(), flashField, highlightExtension, checkboxPlugin, sliderPlugin]; +const staticExtensions = [javascript(), flashField, highlightExtension, sliderPlugin]; export default function CodeMirror({ value, From 21b99b3810eeb9b114d8a39f83faab8c1bd97f40 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Sun, 1 Oct 2023 14:07:48 +0200 Subject: [PATCH 043/158] remove comment --- packages/transpiler/transpiler.mjs | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/transpiler/transpiler.mjs b/packages/transpiler/transpiler.mjs index 37a937bc..dced458f 100644 --- a/packages/transpiler/transpiler.mjs +++ b/packages/transpiler/transpiler.mjs @@ -36,7 +36,6 @@ export function transpiler(input, options = {}) { return this.replace(miniWithLocation(value, node)); } if (isWidgetFunction(node)) { - // collectSliderLocations? emitWidgets && widgets.push({ from: node.arguments[0].start, From 935d8e8aea53362c247d9e3541ece5105769fa0d Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Sun, 1 Oct 2023 14:08:41 +0200 Subject: [PATCH 044/158] fix: comment --- packages/transpiler/transpiler.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/transpiler/transpiler.mjs b/packages/transpiler/transpiler.mjs index dced458f..6eac171f 100644 --- a/packages/transpiler/transpiler.mjs +++ b/packages/transpiler/transpiler.mjs @@ -114,7 +114,7 @@ function isWidgetFunction(node) { function widgetWithLocation(node) { const id = 'slider_' + node.arguments[0].start; // use loc of first arg for id // add loc as identifier to first argument - // the slider function is assumed to be slider(id, value, min?, max?) + // the sliderWithID function is assumed to be sliderWithID(id, value, min?, max?) node.arguments.unshift({ type: 'Literal', value: id, From 96f06d802644c4ec1175328ec893d0df10f345c4 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Sun, 1 Oct 2023 14:09:07 +0200 Subject: [PATCH 045/158] fix: import --- packages/codemirror/slider.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/codemirror/slider.mjs b/packages/codemirror/slider.mjs index 159e4fd1..47b686c8 100644 --- a/packages/codemirror/slider.mjs +++ b/packages/codemirror/slider.mjs @@ -1,4 +1,4 @@ -import { ref } from '@strudel.cycles/core'; +import { ref, pure } from '@strudel.cycles/core'; import { WidgetType, ViewPlugin, Decoration } from '@codemirror/view'; import { StateEffect, StateField } from '@codemirror/state'; From edbd437d7b8076a9ce3b020a6900b9e6a043d0ed Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Sun, 1 Oct 2023 14:20:30 +0200 Subject: [PATCH 046/158] hotfix: add missing dependency --- pnpm-lock.yaml | 18 ++++++++++++++++++ website/package.json | 1 + 2 files changed, 19 insertions(+) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8b48a93f..9f222a20 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -588,6 +588,9 @@ importers: '@strudel.cycles/xen': specifier: workspace:* version: link:../packages/xen + '@strudel/codemirror': + specifier: workspace:* + version: link:../packages/codemirror '@strudel/desktopbridge': specifier: workspace:* version: link:../packages/desktopbridge @@ -1426,6 +1429,7 @@ packages: /@babel/plugin-proposal-async-generator-functions@7.20.7(@babel/core@7.21.5): resolution: {integrity: sha512-xMbiLsn/8RK7Wq7VeVytytS2L6qE69bXPB10YCmMdDZbKF4okCqY74pI/jJQ/8U0b/F6NrT2+14b8/P9/3AMGA==} engines: {node: '>=6.9.0'} + deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-async-generator-functions instead. peerDependencies: '@babel/core': ^7.0.0-0 dependencies: @@ -1441,6 +1445,7 @@ packages: /@babel/plugin-proposal-class-properties@7.18.6(@babel/core@7.21.5): resolution: {integrity: sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==} engines: {node: '>=6.9.0'} + deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-class-properties instead. peerDependencies: '@babel/core': ^7.0.0-0 dependencies: @@ -1454,6 +1459,7 @@ packages: /@babel/plugin-proposal-class-static-block@7.20.7(@babel/core@7.21.5): resolution: {integrity: sha512-AveGOoi9DAjUYYuUAG//Ig69GlazLnoyzMw68VCDux+c1tsnnH/OkYcpz/5xzMkEFC6UxjR5Gw1c+iY2wOGVeQ==} engines: {node: '>=6.9.0'} + deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-class-static-block instead. peerDependencies: '@babel/core': ^7.12.0 dependencies: @@ -1468,6 +1474,7 @@ packages: /@babel/plugin-proposal-dynamic-import@7.18.6(@babel/core@7.21.5): resolution: {integrity: sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw==} engines: {node: '>=6.9.0'} + deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-dynamic-import instead. peerDependencies: '@babel/core': ^7.0.0-0 dependencies: @@ -1479,6 +1486,7 @@ packages: /@babel/plugin-proposal-export-namespace-from@7.18.9(@babel/core@7.21.5): resolution: {integrity: sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA==} engines: {node: '>=6.9.0'} + deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-export-namespace-from instead. peerDependencies: '@babel/core': ^7.0.0-0 dependencies: @@ -1490,6 +1498,7 @@ packages: /@babel/plugin-proposal-json-strings@7.18.6(@babel/core@7.21.5): resolution: {integrity: sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ==} engines: {node: '>=6.9.0'} + deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-json-strings instead. peerDependencies: '@babel/core': ^7.0.0-0 dependencies: @@ -1501,6 +1510,7 @@ packages: /@babel/plugin-proposal-logical-assignment-operators@7.20.7(@babel/core@7.21.5): resolution: {integrity: sha512-y7C7cZgpMIjWlKE5T7eJwp+tnRYM89HmRvWM5EQuB5BoHEONjmQ8lSNmBUwOyy/GFRsohJED51YBF79hE1djug==} engines: {node: '>=6.9.0'} + deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-logical-assignment-operators instead. peerDependencies: '@babel/core': ^7.0.0-0 dependencies: @@ -1512,6 +1522,7 @@ packages: /@babel/plugin-proposal-nullish-coalescing-operator@7.18.6(@babel/core@7.21.5): resolution: {integrity: sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==} engines: {node: '>=6.9.0'} + deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-nullish-coalescing-operator instead. peerDependencies: '@babel/core': ^7.0.0-0 dependencies: @@ -1523,6 +1534,7 @@ packages: /@babel/plugin-proposal-numeric-separator@7.18.6(@babel/core@7.21.5): resolution: {integrity: sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q==} engines: {node: '>=6.9.0'} + deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-numeric-separator instead. peerDependencies: '@babel/core': ^7.0.0-0 dependencies: @@ -1534,6 +1546,7 @@ packages: /@babel/plugin-proposal-object-rest-spread@7.20.7(@babel/core@7.21.5): resolution: {integrity: sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg==} engines: {node: '>=6.9.0'} + deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-object-rest-spread instead. peerDependencies: '@babel/core': ^7.0.0-0 dependencies: @@ -1548,6 +1561,7 @@ packages: /@babel/plugin-proposal-optional-catch-binding@7.18.6(@babel/core@7.21.5): resolution: {integrity: sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw==} engines: {node: '>=6.9.0'} + deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-optional-catch-binding instead. peerDependencies: '@babel/core': ^7.0.0-0 dependencies: @@ -1559,6 +1573,7 @@ packages: /@babel/plugin-proposal-optional-chaining@7.20.7(@babel/core@7.21.5): resolution: {integrity: sha512-T+A7b1kfjtRM51ssoOfS1+wbyCVqorfyZhT99TvxxLMirPShD8CzKMRepMlCBGM5RpHMbn8s+5MMHnPstJH6mQ==} engines: {node: '>=6.9.0'} + deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-optional-chaining instead. peerDependencies: '@babel/core': ^7.0.0-0 dependencies: @@ -1571,6 +1586,7 @@ packages: /@babel/plugin-proposal-private-methods@7.18.6(@babel/core@7.21.5): resolution: {integrity: sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==} engines: {node: '>=6.9.0'} + deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-private-methods instead. peerDependencies: '@babel/core': ^7.0.0-0 dependencies: @@ -1584,6 +1600,7 @@ packages: /@babel/plugin-proposal-private-property-in-object@7.20.5(@babel/core@7.21.5): resolution: {integrity: sha512-Vq7b9dUA12ByzB4EjQTPo25sFhY+08pQDBSZRtUAkj7lb7jahaHR5igera16QZ+3my1nYR4dKsNdYj5IjPHilQ==} engines: {node: '>=6.9.0'} + deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-private-property-in-object instead. peerDependencies: '@babel/core': ^7.0.0-0 dependencies: @@ -1599,6 +1616,7 @@ packages: /@babel/plugin-proposal-unicode-property-regex@7.18.6(@babel/core@7.21.5): resolution: {integrity: sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w==} engines: {node: '>=4'} + deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-unicode-property-regex instead. peerDependencies: '@babel/core': ^7.0.0-0 dependencies: diff --git a/website/package.json b/website/package.json index 6ac799d3..c8fec20c 100644 --- a/website/package.json +++ b/website/package.json @@ -28,6 +28,7 @@ "@strudel.cycles/mini": "workspace:*", "@strudel.cycles/osc": "workspace:*", "@strudel.cycles/react": "workspace:*", + "@strudel/codemirror": "workspace:*", "@strudel.cycles/serial": "workspace:*", "@strudel.cycles/soundfonts": "workspace:*", "@strudel.cycles/tonal": "workspace:*", From fd316c81c0b9529a25c8d08ede3f4f2c3ccce6c2 Mon Sep 17 00:00:00 2001 From: Alex McLean Date: Sun, 1 Oct 2023 13:20:49 +0100 Subject: [PATCH 047/158] support mininotation '..' range operator, fixes #715 (#716) * support mininotation .. range operator, fixes #715 * remove logs --- packages/mini/krill-parser.js | 437 +++++++++++++++++-------------- packages/mini/krill.pegjs | 8 +- packages/mini/mini.mjs | 11 + packages/mini/test/mini.test.mjs | 6 + 4 files changed, 270 insertions(+), 192 deletions(-) diff --git a/packages/mini/krill-parser.js b/packages/mini/krill-parser.js index e85e3363..7831087b 100644 --- a/packages/mini/krill-parser.js +++ b/packages/mini/krill-parser.js @@ -200,20 +200,21 @@ function peg$parse(input, options) { var peg$c23 = "*"; var peg$c24 = "?"; var peg$c25 = ":"; - var peg$c26 = "struct"; - var peg$c27 = "target"; - var peg$c28 = "euclid"; - var peg$c29 = "slow"; - var peg$c30 = "rotL"; - var peg$c31 = "rotR"; - var peg$c32 = "fast"; - var peg$c33 = "scale"; - var peg$c34 = "//"; - var peg$c35 = "cat"; - var peg$c36 = "$"; - var peg$c37 = "setcps"; - var peg$c38 = "setbpm"; - var peg$c39 = "hush"; + var peg$c26 = ".."; + var peg$c27 = "struct"; + var peg$c28 = "target"; + var peg$c29 = "euclid"; + var peg$c30 = "slow"; + var peg$c31 = "rotL"; + var peg$c32 = "rotR"; + var peg$c33 = "fast"; + var peg$c34 = "scale"; + var peg$c35 = "//"; + var peg$c36 = "cat"; + var peg$c37 = "$"; + var peg$c38 = "setcps"; + var peg$c39 = "setbpm"; + var peg$c40 = "hush"; var peg$r0 = /^[1-9]/; var peg$r1 = /^[eE]/; @@ -255,64 +256,67 @@ function peg$parse(input, options) { var peg$e30 = peg$literalExpectation("*", false); var peg$e31 = peg$literalExpectation("?", false); var peg$e32 = peg$literalExpectation(":", false); - var peg$e33 = peg$literalExpectation("struct", false); - var peg$e34 = peg$literalExpectation("target", false); - var peg$e35 = peg$literalExpectation("euclid", false); - var peg$e36 = peg$literalExpectation("slow", false); - var peg$e37 = peg$literalExpectation("rotL", false); - var peg$e38 = peg$literalExpectation("rotR", false); - var peg$e39 = peg$literalExpectation("fast", false); - var peg$e40 = peg$literalExpectation("scale", false); - var peg$e41 = peg$literalExpectation("//", false); - var peg$e42 = peg$classExpectation(["\n"], true, false); - var peg$e43 = peg$literalExpectation("cat", false); - var peg$e44 = peg$literalExpectation("$", false); - var peg$e45 = peg$literalExpectation("setcps", false); - var peg$e46 = peg$literalExpectation("setbpm", false); - var peg$e47 = peg$literalExpectation("hush", false); + var peg$e33 = peg$literalExpectation("..", false); + var peg$e34 = peg$literalExpectation("struct", false); + var peg$e35 = peg$literalExpectation("target", false); + var peg$e36 = peg$literalExpectation("euclid", false); + var peg$e37 = peg$literalExpectation("slow", false); + var peg$e38 = peg$literalExpectation("rotL", false); + var peg$e39 = peg$literalExpectation("rotR", false); + var peg$e40 = peg$literalExpectation("fast", false); + var peg$e41 = peg$literalExpectation("scale", false); + var peg$e42 = peg$literalExpectation("//", false); + var peg$e43 = peg$classExpectation(["\n"], true, false); + var peg$e44 = peg$literalExpectation("cat", false); + var peg$e45 = peg$literalExpectation("$", false); + var peg$e46 = peg$literalExpectation("setcps", false); + var peg$e47 = peg$literalExpectation("setbpm", false); + var peg$e48 = peg$literalExpectation("hush", false); var peg$f0 = function() { return parseFloat(text()); }; - var peg$f1 = function(chars) { return new AtomStub(chars.join("")) }; - var peg$f2 = function(s) { return s }; - var peg$f3 = function(s, stepsPerCycle) { s.arguments_.stepsPerCycle = stepsPerCycle ; return s; }; - var peg$f4 = function(a) { return a }; - var peg$f5 = function(s) { s.arguments_.alignment = 'slowcat'; return s; }; - var peg$f6 = function(a) { return x => x.options_['weight'] = a }; - var peg$f7 = function(a) { return x => x.options_['reps'] = a }; - var peg$f8 = function(p, s, r) { return x => x.options_['ops'].push({ type_: "bjorklund", arguments_ :{ pulse: p, step:s, rotation:r }}) }; - var peg$f9 = function(a) { return x => x.options_['ops'].push({ type_: "stretch", arguments_ :{ amount:a, type: 'slow' }}) }; - var peg$f10 = function(a) { return x => x.options_['ops'].push({ type_: "stretch", arguments_ :{ amount:a, type: 'fast' }}) }; - var peg$f11 = function(a) { return x => x.options_['ops'].push({ type_: "degradeBy", arguments_ :{ amount:a, seed: seed++ } }) }; - var peg$f12 = function(s) { return x => x.options_['ops'].push({ type_: "tail", arguments_ :{ element:s } }) }; - var peg$f13 = function(s, ops) { const result = new ElementStub(s, {ops: [], weight: 1, reps: 1}); + var peg$f1 = function() { return parseInt(text()); }; + var peg$f2 = function(chars) { return new AtomStub(chars.join("")) }; + var peg$f3 = function(s) { return s }; + var peg$f4 = function(s, stepsPerCycle) { s.arguments_.stepsPerCycle = stepsPerCycle ; return s; }; + var peg$f5 = function(a) { return a }; + var peg$f6 = function(s) { s.arguments_.alignment = 'slowcat'; return s; }; + var peg$f7 = function(a) { return x => x.options_['weight'] = a }; + var peg$f8 = function(a) { return x => x.options_['reps'] = a }; + var peg$f9 = function(p, s, r) { return x => x.options_['ops'].push({ type_: "bjorklund", arguments_ :{ pulse: p, step:s, rotation:r }}) }; + var peg$f10 = function(a) { return x => x.options_['ops'].push({ type_: "stretch", arguments_ :{ amount:a, type: 'slow' }}) }; + var peg$f11 = function(a) { return x => x.options_['ops'].push({ type_: "stretch", arguments_ :{ amount:a, type: 'fast' }}) }; + var peg$f12 = function(a) { return x => x.options_['ops'].push({ type_: "degradeBy", arguments_ :{ amount:a, seed: seed++ } }) }; + var peg$f13 = function(s) { return x => x.options_['ops'].push({ type_: "tail", arguments_ :{ element:s } }) }; + var peg$f14 = function(s) { return x => x.options_['ops'].push({ type_: "range", arguments_ :{ element:s } }) }; + var peg$f15 = function(s, ops) { const result = new ElementStub(s, {ops: [], weight: 1, reps: 1}); for (const op of ops) { op(result); } return result; }; - var peg$f14 = function(s) { return new PatternStub(s, 'fastcat'); }; - var peg$f15 = function(tail) { return { alignment: 'stack', list: tail }; }; - var peg$f16 = function(tail) { return { alignment: 'rand', list: tail, seed: seed++ }; }; - var peg$f17 = function(head, tail) { if (tail && tail.list.length > 0) { return new PatternStub([head, ...tail.list], tail.alignment, tail.seed); } else { return head; } }; - var peg$f18 = function(head, tail) { return new PatternStub(tail ? [head, ...tail.list] : [head], 'polymeter'); }; - var peg$f19 = function(sc) { return sc; }; - var peg$f20 = function(s) { return { name: "struct", args: { mini:s }}}; - var peg$f21 = function(s) { return { name: "target", args : { name:s}}}; - var peg$f22 = function(p, s, r) { return { name: "bjorklund", args :{ pulse: p, step:parseInt(s) }}}; - var peg$f23 = function(a) { return { name: "stretch", args :{ amount: a}}}; - var peg$f24 = function(a) { return { name: "shift", args :{ amount: "-"+a}}}; - var peg$f25 = function(a) { return { name: "shift", args :{ amount: a}}}; - var peg$f26 = function(a) { return { name: "stretch", args :{ amount: "1/"+a}}}; - var peg$f27 = function(s) { return { name: "scale", args :{ scale: s.join("")}}}; - var peg$f28 = function(s, v) { return v}; - var peg$f29 = function(s, ss) { ss.unshift(s); return new PatternStub(ss, 'slowcat'); }; - var peg$f30 = function(sg) {return sg}; - var peg$f31 = function(o, soc) { return new OperatorStub(o.name,o.args,soc)}; - var peg$f32 = function(sc) { return sc }; - var peg$f33 = function(c) { return c }; - var peg$f34 = function(v) { return new CommandStub("setcps", { value: v})}; - var peg$f35 = function(v) { return new CommandStub("setcps", { value: (v/120/2)})}; - var peg$f36 = function() { return new CommandStub("hush")}; + var peg$f16 = function(s) { return new PatternStub(s, 'fastcat'); }; + var peg$f17 = function(tail) { return { alignment: 'stack', list: tail }; }; + var peg$f18 = function(tail) { return { alignment: 'rand', list: tail, seed: seed++ }; }; + var peg$f19 = function(head, tail) { if (tail && tail.list.length > 0) { return new PatternStub([head, ...tail.list], tail.alignment, tail.seed); } else { return head; } }; + var peg$f20 = function(head, tail) { return new PatternStub(tail ? [head, ...tail.list] : [head], 'polymeter'); }; + var peg$f21 = function(sc) { return sc; }; + var peg$f22 = function(s) { return { name: "struct", args: { mini:s }}}; + var peg$f23 = function(s) { return { name: "target", args : { name:s}}}; + var peg$f24 = function(p, s, r) { return { name: "bjorklund", args :{ pulse: p, step:parseInt(s) }}}; + var peg$f25 = function(a) { return { name: "stretch", args :{ amount: a}}}; + var peg$f26 = function(a) { return { name: "shift", args :{ amount: "-"+a}}}; + var peg$f27 = function(a) { return { name: "shift", args :{ amount: a}}}; + var peg$f28 = function(a) { return { name: "stretch", args :{ amount: "1/"+a}}}; + var peg$f29 = function(s) { return { name: "scale", args :{ scale: s.join("")}}}; + var peg$f30 = function(s, v) { return v}; + var peg$f31 = function(s, ss) { ss.unshift(s); return new PatternStub(ss, 'slowcat'); }; + var peg$f32 = function(sg) {return sg}; + var peg$f33 = function(o, soc) { return new OperatorStub(o.name,o.args,soc)}; + var peg$f34 = function(sc) { return sc }; + var peg$f35 = function(c) { return c }; + var peg$f36 = function(v) { return new CommandStub("setcps", { value: v})}; + var peg$f37 = function(v) { return new CommandStub("setcps", { value: (v/120/2)})}; + var peg$f38 = function() { return new CommandStub("hush")}; var peg$currPos = 0; var peg$savedPos = 0; var peg$posDetailsCache = [{ line: 1, column: 1 }]; @@ -651,6 +655,26 @@ function peg$parse(input, options) { return s0; } + function peg$parseintneg() { + var s0, s1, s2; + + s0 = peg$currPos; + s1 = peg$parseminus(); + if (s1 === peg$FAILED) { + s1 = null; + } + s2 = peg$parseint(); + if (s2 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f1(); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + function peg$parseminus() { var s0; @@ -884,7 +908,7 @@ function peg$parse(input, options) { if (s2 !== peg$FAILED) { s3 = peg$parsews(); peg$savedPos = s0; - s0 = peg$f1(s2); + s0 = peg$f2(s2); } else { peg$currPos = s0; s0 = peg$FAILED; @@ -920,7 +944,7 @@ function peg$parse(input, options) { if (s6 !== peg$FAILED) { s7 = peg$parsews(); peg$savedPos = s0; - s0 = peg$f2(s4); + s0 = peg$f3(s4); } else { peg$currPos = s0; s0 = peg$FAILED; @@ -968,7 +992,7 @@ function peg$parse(input, options) { } s8 = peg$parsews(); peg$savedPos = s0; - s0 = peg$f3(s4, s7); + s0 = peg$f4(s4, s7); } else { peg$currPos = s0; s0 = peg$FAILED; @@ -1000,7 +1024,7 @@ function peg$parse(input, options) { s2 = peg$parseslice(); if (s2 !== peg$FAILED) { peg$savedPos = s0; - s0 = peg$f4(s2); + s0 = peg$f5(s2); } else { peg$currPos = s0; s0 = peg$FAILED; @@ -1040,7 +1064,7 @@ function peg$parse(input, options) { if (s6 !== peg$FAILED) { s7 = peg$parsews(); peg$savedPos = s0; - s0 = peg$f5(s4); + s0 = peg$f6(s4); } else { peg$currPos = s0; s0 = peg$FAILED; @@ -1090,6 +1114,9 @@ function peg$parse(input, options) { s0 = peg$parseop_degrade(); if (s0 === peg$FAILED) { s0 = peg$parseop_tail(); + if (s0 === peg$FAILED) { + s0 = peg$parseop_range(); + } } } } @@ -1115,7 +1142,7 @@ function peg$parse(input, options) { s2 = peg$parsenumber(); if (s2 !== peg$FAILED) { peg$savedPos = s0; - s0 = peg$f6(s2); + s0 = peg$f7(s2); } else { peg$currPos = s0; s0 = peg$FAILED; @@ -1143,7 +1170,7 @@ function peg$parse(input, options) { s2 = peg$parsenumber(); if (s2 !== peg$FAILED) { peg$savedPos = s0; - s0 = peg$f7(s2); + s0 = peg$f8(s2); } else { peg$currPos = s0; s0 = peg$FAILED; @@ -1197,7 +1224,7 @@ function peg$parse(input, options) { } if (s13 !== peg$FAILED) { peg$savedPos = s0; - s0 = peg$f8(s3, s7, s11); + s0 = peg$f9(s3, s7, s11); } else { peg$currPos = s0; s0 = peg$FAILED; @@ -1237,7 +1264,7 @@ function peg$parse(input, options) { s2 = peg$parseslice(); if (s2 !== peg$FAILED) { peg$savedPos = s0; - s0 = peg$f9(s2); + s0 = peg$f10(s2); } else { peg$currPos = s0; s0 = peg$FAILED; @@ -1265,7 +1292,7 @@ function peg$parse(input, options) { s2 = peg$parseslice(); if (s2 !== peg$FAILED) { peg$savedPos = s0; - s0 = peg$f10(s2); + s0 = peg$f11(s2); } else { peg$currPos = s0; s0 = peg$FAILED; @@ -1295,7 +1322,7 @@ function peg$parse(input, options) { s2 = null; } peg$savedPos = s0; - s0 = peg$f11(s2); + s0 = peg$f12(s2); } else { peg$currPos = s0; s0 = peg$FAILED; @@ -1319,7 +1346,35 @@ function peg$parse(input, options) { s2 = peg$parseslice(); if (s2 !== peg$FAILED) { peg$savedPos = s0; - s0 = peg$f12(s2); + s0 = peg$f13(s2); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseop_range() { + var s0, s1, s2; + + s0 = peg$currPos; + if (input.substr(peg$currPos, 2) === peg$c26) { + s1 = peg$c26; + peg$currPos += 2; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e33); } + } + if (s1 !== peg$FAILED) { + s2 = peg$parseslice(); + if (s2 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f14(s2); } else { peg$currPos = s0; s0 = peg$FAILED; @@ -1345,7 +1400,7 @@ function peg$parse(input, options) { s3 = peg$parseslice_op(); } peg$savedPos = s0; - s0 = peg$f13(s1, s2); + s0 = peg$f15(s1, s2); } else { peg$currPos = s0; s0 = peg$FAILED; @@ -1370,7 +1425,7 @@ function peg$parse(input, options) { } if (s1 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$f14(s1); + s1 = peg$f16(s1); } s0 = s1; @@ -1419,7 +1474,7 @@ function peg$parse(input, options) { } if (s1 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$f15(s1); + s1 = peg$f17(s1); } s0 = s1; @@ -1468,7 +1523,7 @@ function peg$parse(input, options) { } if (s1 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$f16(s1); + s1 = peg$f18(s1); } s0 = s1; @@ -1489,7 +1544,7 @@ function peg$parse(input, options) { s2 = null; } peg$savedPos = s0; - s0 = peg$f17(s1, s2); + s0 = peg$f19(s1, s2); } else { peg$currPos = s0; s0 = peg$FAILED; @@ -1509,7 +1564,7 @@ function peg$parse(input, options) { s2 = null; } peg$savedPos = s0; - s0 = peg$f18(s1, s2); + s0 = peg$f20(s1, s2); } else { peg$currPos = s0; s0 = peg$FAILED; @@ -1532,7 +1587,7 @@ function peg$parse(input, options) { s6 = peg$parsequote(); if (s6 !== peg$FAILED) { peg$savedPos = s0; - s0 = peg$f19(s4); + s0 = peg$f21(s4); } else { peg$currPos = s0; s0 = peg$FAILED; @@ -1582,19 +1637,19 @@ function peg$parse(input, options) { var s0, s1, s2, s3; s0 = peg$currPos; - if (input.substr(peg$currPos, 6) === peg$c26) { - s1 = peg$c26; + if (input.substr(peg$currPos, 6) === peg$c27) { + s1 = peg$c27; peg$currPos += 6; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e33); } + if (peg$silentFails === 0) { peg$fail(peg$e34); } } if (s1 !== peg$FAILED) { s2 = peg$parsews(); s3 = peg$parsemini_or_operator(); if (s3 !== peg$FAILED) { peg$savedPos = s0; - s0 = peg$f20(s3); + s0 = peg$f22(s3); } else { peg$currPos = s0; s0 = peg$FAILED; @@ -1611,12 +1666,12 @@ function peg$parse(input, options) { var s0, s1, s2, s3, s4, s5; s0 = peg$currPos; - if (input.substr(peg$currPos, 6) === peg$c27) { - s1 = peg$c27; + if (input.substr(peg$currPos, 6) === peg$c28) { + s1 = peg$c28; peg$currPos += 6; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e34); } + if (peg$silentFails === 0) { peg$fail(peg$e35); } } if (s1 !== peg$FAILED) { s2 = peg$parsews(); @@ -1627,7 +1682,7 @@ function peg$parse(input, options) { s5 = peg$parsequote(); if (s5 !== peg$FAILED) { peg$savedPos = s0; - s0 = peg$f21(s4); + s0 = peg$f23(s4); } else { peg$currPos = s0; s0 = peg$FAILED; @@ -1652,12 +1707,12 @@ function peg$parse(input, options) { var s0, s1, s2, s3, s4, s5, s6, s7; s0 = peg$currPos; - if (input.substr(peg$currPos, 6) === peg$c28) { - s1 = peg$c28; + if (input.substr(peg$currPos, 6) === peg$c29) { + s1 = peg$c29; peg$currPos += 6; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e35); } + if (peg$silentFails === 0) { peg$fail(peg$e36); } } if (s1 !== peg$FAILED) { s2 = peg$parsews(); @@ -1672,7 +1727,7 @@ function peg$parse(input, options) { s7 = null; } peg$savedPos = s0; - s0 = peg$f22(s3, s5, s7); + s0 = peg$f24(s3, s5, s7); } else { peg$currPos = s0; s0 = peg$FAILED; @@ -1692,35 +1747,6 @@ function peg$parse(input, options) { function peg$parseslow() { var s0, s1, s2, s3; - s0 = peg$currPos; - if (input.substr(peg$currPos, 4) === peg$c29) { - s1 = peg$c29; - peg$currPos += 4; - } else { - s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e36); } - } - if (s1 !== peg$FAILED) { - s2 = peg$parsews(); - s3 = peg$parsenumber(); - if (s3 !== peg$FAILED) { - peg$savedPos = s0; - s0 = peg$f23(s3); - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - - return s0; - } - - function peg$parserotL() { - var s0, s1, s2, s3; - s0 = peg$currPos; if (input.substr(peg$currPos, 4) === peg$c30) { s1 = peg$c30; @@ -1729,35 +1755,6 @@ function peg$parse(input, options) { s1 = peg$FAILED; if (peg$silentFails === 0) { peg$fail(peg$e37); } } - if (s1 !== peg$FAILED) { - s2 = peg$parsews(); - s3 = peg$parsenumber(); - if (s3 !== peg$FAILED) { - peg$savedPos = s0; - s0 = peg$f24(s3); - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - - return s0; - } - - function peg$parserotR() { - var s0, s1, s2, s3; - - s0 = peg$currPos; - if (input.substr(peg$currPos, 4) === peg$c31) { - s1 = peg$c31; - peg$currPos += 4; - } else { - s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e38); } - } if (s1 !== peg$FAILED) { s2 = peg$parsews(); s3 = peg$parsenumber(); @@ -1776,16 +1773,16 @@ function peg$parse(input, options) { return s0; } - function peg$parsefast() { + function peg$parserotL() { var s0, s1, s2, s3; s0 = peg$currPos; - if (input.substr(peg$currPos, 4) === peg$c32) { - s1 = peg$c32; + if (input.substr(peg$currPos, 4) === peg$c31) { + s1 = peg$c31; peg$currPos += 4; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e39); } + if (peg$silentFails === 0) { peg$fail(peg$e38); } } if (s1 !== peg$FAILED) { s2 = peg$parsews(); @@ -1805,16 +1802,74 @@ function peg$parse(input, options) { return s0; } + function peg$parserotR() { + var s0, s1, s2, s3; + + s0 = peg$currPos; + if (input.substr(peg$currPos, 4) === peg$c32) { + s1 = peg$c32; + peg$currPos += 4; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e39); } + } + if (s1 !== peg$FAILED) { + s2 = peg$parsews(); + s3 = peg$parsenumber(); + if (s3 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f27(s3); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parsefast() { + var s0, s1, s2, s3; + + s0 = peg$currPos; + if (input.substr(peg$currPos, 4) === peg$c33) { + s1 = peg$c33; + peg$currPos += 4; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e40); } + } + if (s1 !== peg$FAILED) { + s2 = peg$parsews(); + s3 = peg$parsenumber(); + if (s3 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f28(s3); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + function peg$parsescale() { var s0, s1, s2, s3, s4, s5; s0 = peg$currPos; - if (input.substr(peg$currPos, 5) === peg$c33) { - s1 = peg$c33; + if (input.substr(peg$currPos, 5) === peg$c34) { + s1 = peg$c34; peg$currPos += 5; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e40); } + if (peg$silentFails === 0) { peg$fail(peg$e41); } } if (s1 !== peg$FAILED) { s2 = peg$parsews(); @@ -1834,7 +1889,7 @@ function peg$parse(input, options) { s5 = peg$parsequote(); if (s5 !== peg$FAILED) { peg$savedPos = s0; - s0 = peg$f27(s4); + s0 = peg$f29(s4); } else { peg$currPos = s0; s0 = peg$FAILED; @@ -1859,12 +1914,12 @@ function peg$parse(input, options) { var s0, s1, s2, s3; s0 = peg$currPos; - if (input.substr(peg$currPos, 2) === peg$c34) { - s1 = peg$c34; + if (input.substr(peg$currPos, 2) === peg$c35) { + s1 = peg$c35; peg$currPos += 2; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e41); } + if (peg$silentFails === 0) { peg$fail(peg$e42); } } if (s1 !== peg$FAILED) { s2 = []; @@ -1873,7 +1928,7 @@ function peg$parse(input, options) { peg$currPos++; } else { s3 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e42); } + if (peg$silentFails === 0) { peg$fail(peg$e43); } } while (s3 !== peg$FAILED) { s2.push(s3); @@ -1882,7 +1937,7 @@ function peg$parse(input, options) { peg$currPos++; } else { s3 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e42); } + if (peg$silentFails === 0) { peg$fail(peg$e43); } } } s1 = [s1, s2]; @@ -1899,12 +1954,12 @@ function peg$parse(input, options) { var s0, s1, s2, s3, s4, s5, s6, s7, s8, s9; s0 = peg$currPos; - if (input.substr(peg$currPos, 3) === peg$c35) { - s1 = peg$c35; + if (input.substr(peg$currPos, 3) === peg$c36) { + s1 = peg$c36; peg$currPos += 3; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e43); } + if (peg$silentFails === 0) { peg$fail(peg$e44); } } if (s1 !== peg$FAILED) { s2 = peg$parsews(); @@ -1926,7 +1981,7 @@ function peg$parse(input, options) { s9 = peg$parsemini_or_operator(); if (s9 !== peg$FAILED) { peg$savedPos = s7; - s7 = peg$f28(s5, s9); + s7 = peg$f30(s5, s9); } else { peg$currPos = s7; s7 = peg$FAILED; @@ -1943,7 +1998,7 @@ function peg$parse(input, options) { s9 = peg$parsemini_or_operator(); if (s9 !== peg$FAILED) { peg$savedPos = s7; - s7 = peg$f28(s5, s9); + s7 = peg$f30(s5, s9); } else { peg$currPos = s7; s7 = peg$FAILED; @@ -1963,7 +2018,7 @@ function peg$parse(input, options) { } if (s8 !== peg$FAILED) { peg$savedPos = s0; - s0 = peg$f29(s5, s6); + s0 = peg$f31(s5, s6); } else { peg$currPos = s0; s0 = peg$FAILED; @@ -2009,7 +2064,7 @@ function peg$parse(input, options) { s4 = peg$parsecomment(); } peg$savedPos = s0; - s0 = peg$f30(s1); + s0 = peg$f32(s1); } else { peg$currPos = s0; s0 = peg$FAILED; @@ -2020,18 +2075,18 @@ function peg$parse(input, options) { if (s1 !== peg$FAILED) { s2 = peg$parsews(); if (input.charCodeAt(peg$currPos) === 36) { - s3 = peg$c36; + s3 = peg$c37; peg$currPos++; } else { s3 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e44); } + if (peg$silentFails === 0) { peg$fail(peg$e45); } } if (s3 !== peg$FAILED) { s4 = peg$parsews(); s5 = peg$parsemini_or_operator(); if (s5 !== peg$FAILED) { peg$savedPos = s0; - s0 = peg$f31(s1, s5); + s0 = peg$f33(s1, s5); } else { peg$currPos = s0; s0 = peg$FAILED; @@ -2056,7 +2111,7 @@ function peg$parse(input, options) { s1 = peg$parsemini_or_operator(); if (s1 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$f32(s1); + s1 = peg$f34(s1); } s0 = s1; if (s0 === peg$FAILED) { @@ -2089,7 +2144,7 @@ function peg$parse(input, options) { if (s2 !== peg$FAILED) { s3 = peg$parsews(); peg$savedPos = s0; - s0 = peg$f33(s2); + s0 = peg$f35(s2); } else { peg$currPos = s0; s0 = peg$FAILED; @@ -2102,19 +2157,19 @@ function peg$parse(input, options) { var s0, s1, s2, s3; s0 = peg$currPos; - if (input.substr(peg$currPos, 6) === peg$c37) { - s1 = peg$c37; + if (input.substr(peg$currPos, 6) === peg$c38) { + s1 = peg$c38; peg$currPos += 6; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e45); } + if (peg$silentFails === 0) { peg$fail(peg$e46); } } if (s1 !== peg$FAILED) { s2 = peg$parsews(); s3 = peg$parsenumber(); if (s3 !== peg$FAILED) { peg$savedPos = s0; - s0 = peg$f34(s3); + s0 = peg$f36(s3); } else { peg$currPos = s0; s0 = peg$FAILED; @@ -2131,19 +2186,19 @@ function peg$parse(input, options) { var s0, s1, s2, s3; s0 = peg$currPos; - if (input.substr(peg$currPos, 6) === peg$c38) { - s1 = peg$c38; + if (input.substr(peg$currPos, 6) === peg$c39) { + s1 = peg$c39; peg$currPos += 6; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e46); } + if (peg$silentFails === 0) { peg$fail(peg$e47); } } if (s1 !== peg$FAILED) { s2 = peg$parsews(); s3 = peg$parsenumber(); if (s3 !== peg$FAILED) { peg$savedPos = s0; - s0 = peg$f35(s3); + s0 = peg$f37(s3); } else { peg$currPos = s0; s0 = peg$FAILED; @@ -2160,16 +2215,16 @@ function peg$parse(input, options) { var s0, s1; s0 = peg$currPos; - if (input.substr(peg$currPos, 4) === peg$c39) { - s1 = peg$c39; + if (input.substr(peg$currPos, 4) === peg$c40) { + s1 = peg$c40; peg$currPos += 4; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e47); } + if (peg$silentFails === 0) { peg$fail(peg$e48); } } if (s1 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$f36(); + s1 = peg$f38(); } s0 = s1; diff --git a/packages/mini/krill.pegjs b/packages/mini/krill.pegjs index 72b453be..f52743bf 100644 --- a/packages/mini/krill.pegjs +++ b/packages/mini/krill.pegjs @@ -79,6 +79,9 @@ frac int = zero / (digit1_9 DIGIT*) +intneg + = minus? int { return parseInt(text()); } + minus = "-" @@ -123,7 +126,7 @@ slice = step / sub_cycle / polymeter / slow_sequence // slice modifier affects the timing/size of a slice (e.g. [a b c]@3) // at this point, we assume we can represent them as regular sequence operators -slice_op = op_weight / op_bjorklund / op_slow / op_fast / op_replicate / op_degrade / op_tail +slice_op = op_weight / op_bjorklund / op_slow / op_fast / op_replicate / op_degrade / op_tail / op_range op_weight = "@" a:number { return x => x.options_['weight'] = a } @@ -146,6 +149,9 @@ op_degrade = "?"a:number? op_tail = ":" s:slice { return x => x.options_['ops'].push({ type_: "tail", arguments_ :{ element:s } }) } +op_range = ".." s:slice + { return x => x.options_['ops'].push({ type_: "range", arguments_ :{ element:s } }) } + // a slice with an modifier applied i.e [bd@4 sd@3]@2 hh] slice_with_ops = s:slice ops:slice_op* { const result = new ElementStub(s, {ops: [], weight: 1, reps: 1}); diff --git a/packages/mini/mini.mjs b/packages/mini/mini.mjs index 3321e7bc..8e6b844f 100644 --- a/packages/mini/mini.mjs +++ b/packages/mini/mini.mjs @@ -45,6 +45,17 @@ const applyOptions = (parent, enter) => (pat, i) => { pat = pat.fmap((a) => (b) => Array.isArray(a) ? [...a, b] : [a, b]).appLeft(friend); break; } + case 'range': { + const friend = enter(op.arguments_.element); + pat = strudel.reify(pat); + const arrayRange = (start, stop, step = 1) => + Array.from({ length: Math.abs(stop - start) / step + 1 }, (value, index) => + start < stop ? start + index * step : start - index * step, + ); + let range = (apat, bpat) => apat.squeezeBind((a) => bpat.bind((b) => strudel.fastcat(...arrayRange(a, b)))); + pat = range(pat, friend); + break; + } default: { console.warn(`operator "${op.type_}" not implemented`); } diff --git a/packages/mini/test/mini.test.mjs b/packages/mini/test/mini.test.mjs index 0c7f381e..6d9ac367 100644 --- a/packages/mini/test/mini.test.mjs +++ b/packages/mini/test/mini.test.mjs @@ -184,6 +184,12 @@ describe('mini', () => { it('supports lists', () => { expect(minV('a:b c:d:[e:f] g')).toEqual([['a', 'b'], ['c', 'd', ['e', 'f']], 'g']); }); + it('supports ranges', () => { + expect(minV('0 .. 4')).toEqual([0, 1, 2, 3, 4]); + }); + it('supports patterned ranges', () => { + expect(minS('[<0 1> .. <2 4>]*2')).toEqual(minS('[0 1 2] [1 2 3 4]')); + }); }); describe('getLeafLocation', () => { From 2d07eeb518e9b200bae4e38b818808ef2a388849 Mon Sep 17 00:00:00 2001 From: Raphael Forment Date: Sun, 1 Oct 2023 14:44:34 +0200 Subject: [PATCH 048/158] Connecting all parameters to convolution generator --- packages/superdough/reverb.mjs | 5 ++++- packages/superdough/superdough.mjs | 20 ++++++++++++++++---- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/packages/superdough/reverb.mjs b/packages/superdough/reverb.mjs index 4c5bc1d1..54019fc2 100644 --- a/packages/superdough/reverb.mjs +++ b/packages/superdough/reverb.mjs @@ -26,8 +26,11 @@ if (typeof AudioContext !== 'undefined') { } ); convolver.duration = d; + convolver.fade = fade; + convolver.revlp = revlp; + convolver.revdim = revdim; }; - convolver.setDuration(duration); + convolver.setDuration(duration, fade, revlp, revdim); return convolver; }; } diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index 607c649d..0d08c8ad 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -118,17 +118,29 @@ function getReverb(orbit, duration = 2, fade, revlp, revdim) { duration, fade, revlp, + revdim, ); reverb.connect(getDestination()); - console.log(reverb) reverbs[orbit] = reverb; + console.log(reverbs[orbit]); } - // Update the reverb duration if needed after instanciation - if (reverbs[orbit].duration !== duration) { + + if ( + reverbs[orbit].duration !== duration || + reverbs[orbit].fade !== fade || + reverbs[orbit].revlp !== revlp || + reverbs[orbit].revdim !== revdim + ) { reverbs[orbit] = reverbs[orbit].setDuration( - duration, fade, revlp, revdim); + duration, fade, revlp, revdim + ); reverbs[orbit].duration = duration; + reverbs[orbit].fade = fade; + reverbs[orbit].revlp = revlp; + reverbs[orbit].revdim = revdim; + } + return reverbs[orbit]; } From 1909caf769d83e89fbc6ace74b464f25179fe20d Mon Sep 17 00:00:00 2001 From: Raphael Forment Date: Sun, 1 Oct 2023 14:50:29 +0200 Subject: [PATCH 049/158] Connecting new reverb documentation --- website/src/pages/learn/effects.mdx | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/website/src/pages/learn/effects.mdx b/website/src/pages/learn/effects.mdx index f77ab4c4..062a1947 100644 --- a/website/src/pages/learn/effects.mdx +++ b/website/src/pages/learn/effects.mdx @@ -203,4 +203,16 @@ global effects use the same chain for all events of the same orbit: +## fade + + + +## revlp + + + +## revdim + + + Next, we'll look at strudel's support for [Csound](/learn/csound). From 6ca99e33aba268442e519760e1488d9e87460ab5 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Sun, 1 Oct 2023 23:20:05 +0200 Subject: [PATCH 050/158] codeformat --- packages/core/controls.mjs | 4 +-- packages/superdough/reverb.mjs | 10 ++------ packages/superdough/reverbGen.mjs | 40 ++++++++++++++---------------- packages/superdough/superdough.mjs | 15 ++--------- 4 files changed, 24 insertions(+), 45 deletions(-) diff --git a/packages/core/controls.mjs b/packages/core/controls.mjs index 216b7001..e72f006d 100644 --- a/packages/core/controls.mjs +++ b/packages/core/controls.mjs @@ -1197,7 +1197,7 @@ const generic_params = [ ]; // TODO: slice / splice https://www.youtube.com/watch?v=hKhPdO0RKDQ&list=PL2lW1zNIIwj3bDkh-Y3LUGDuRcoUigoDs&index=13 -controls.createParam = function(names) { +controls.createParam = function (names) { const name = Array.isArray(names) ? names[0] : names; var withVal; @@ -1221,7 +1221,7 @@ controls.createParam = function(names) { const func = (...pats) => sequence(...pats).withValue(withVal); - const setter = function(...pats) { + const setter = function (...pats) { if (!pats.length) { return this.fmap(withVal); } diff --git a/packages/superdough/reverb.mjs b/packages/superdough/reverb.mjs index 54019fc2..7151e7fc 100644 --- a/packages/superdough/reverb.mjs +++ b/packages/superdough/reverb.mjs @@ -2,13 +2,7 @@ import reverbGen from './reverbGen.mjs'; if (typeof AudioContext !== 'undefined') { AudioContext.prototype.generateReverb = reverbGen.generateReverb; - AudioContext.prototype.createReverb = function( - audioContext, - duration, - fade, - revlp, - revdim - ) { + AudioContext.prototype.createReverb = function (audioContext, duration, fade, revlp, revdim) { const convolver = this.createConvolver(); convolver.setDuration = (d, fade, revlp, revdim) => { this.generateReverb( @@ -23,7 +17,7 @@ if (typeof AudioContext !== 'undefined') { }, (buffer) => { convolver.buffer = buffer; - } + }, ); convolver.duration = d; convolver.fade = fade; diff --git a/packages/superdough/reverbGen.mjs b/packages/superdough/reverbGen.mjs index 1d05ee82..cac5d24a 100644 --- a/packages/superdough/reverbGen.mjs +++ b/packages/superdough/reverbGen.mjs @@ -12,9 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -"use strict"; - - var reverbGen = {}; /** Generates a reverb impulse response. @@ -24,7 +21,7 @@ var reverbGen = {}; the impulse response has been generated. The impulse response is passed to this function as its parameter. May be called immediately within the current execution context, or later. */ -reverbGen.generateReverb = function(params, callback) { +reverbGen.generateReverb = function (params, callback) { var audioContext = params.audioContext || new AudioContext(); var sampleRate = params.sampleRate || 44100; var numChannels = params.numChannels || 2; @@ -42,7 +39,7 @@ reverbGen.generateReverb = function(params, callback) { chan[j] = randomSample() * Math.pow(decayBase, j); } for (var j = 0; j < fadeInSampleFrames; j++) { - chan[j] *= (j / fadeInSampleFrames); + chan[j] *= j / fadeInSampleFrames; } } @@ -57,7 +54,7 @@ reverbGen.generateReverb = function(params, callback) { @param {number} min Minimum value of data for the graph (lower edge). @param {number} max Maximum value of data in the graph (upper edge). @return {!CanvasElement} The generated canvas element. */ -reverbGen.generateGraph = function(data, width, height, min, max) { +reverbGen.generateGraph = function (data, width, height, min, max) { var canvas = document.createElement('canvas'); canvas.width = width; canvas.height = height; @@ -71,7 +68,7 @@ reverbGen.generateGraph = function(data, width, height, min, max) { gc.fillRect(i * xscale, height - (data[i] - min) * yscale, 1, 1); } return canvas; -} +}; /** Saves an AudioBuffer as a 16-bit WAV file on the client's host file system. Normalizes it to peak at +-32767, and optionally @@ -85,7 +82,7 @@ reverbGen.generateGraph = function(data, width, height, min, max) { is truncated at that point. This is expressed as an integer, applying to the post-normalized and integer-converted buffer. The default is 0, meaning don't truncate. */ -reverbGen.saveWavFile = function(buffer, name, opt_minTail) { +reverbGen.saveWavFile = function (buffer, name, opt_minTail) { var bitsPerSample = 16; var bytesPerSample = 2; var sampleRate = buffer.sampleRate; @@ -124,23 +121,22 @@ reverbGen.saveWavFile = function(buffer, name, opt_minTail) { dataView.setUint32(4, fileBytes - 8, true); // file length dataView.setUint32(8, 1163280727, true); // "WAVE" dataView.setUint32(12, 544501094, true); // "fmt " - dataView.setUint32(16, 16, true) // fmt chunk length - dataView.setUint16(20, 1, true); // PCM format + dataView.setUint32(16, 16, true); // fmt chunk length + dataView.setUint16(20, 1, true); // PCM format dataView.setUint16(22, numChannels, true); // NumChannels - dataView.setUint32(24, sampleRate, true); // SampleRate + dataView.setUint32(24, sampleRate, true); // SampleRate var bytesPerSampleFrame = numChannels * bytesPerSample; dataView.setUint32(28, sampleRate * bytesPerSampleFrame, true); // ByteRate - dataView.setUint16(32, bytesPerSampleFrame, true); // BlockAlign - dataView.setUint16(34, bitsPerSample, true); // BitsPerSample - dataView.setUint32(36, 1635017060, true); // "data" + dataView.setUint16(32, bytesPerSampleFrame, true); // BlockAlign + dataView.setUint16(34, bitsPerSample, true); // BitsPerSample + dataView.setUint32(36, 1635017060, true); // "data" dataView.setUint32(40, sampleDataBytes, true); for (var j = 0; j < numSampleFrames; j++) { for (var i = 0; i < numChannels; i++) { - dataView.setInt16(44 + j * bytesPerSampleFrame + i * bytesPerSample, - Math.round(scale * channels[i][j]), true); + dataView.setInt16(44 + j * bytesPerSampleFrame + i * bytesPerSample, Math.round(scale * channels[i][j]), true); } } - var blob = new Blob([arrayBuffer], { 'type': 'audio/wav' }); + var blob = new Blob([arrayBuffer], { type: 'audio/wav' }); var url = window.URL.createObjectURL(blob); var linkEl = document.createElement('a'); linkEl.href = url; @@ -159,7 +155,7 @@ reverbGen.saveWavFile = function(buffer, name, opt_minTail) { @param {number} lpFreqEndAt @param {!function(!AudioBuffer)} callback May be called immediately within the current execution context, or later.*/ -var applyGradualLowpass = function(input, lpFreqStart, lpFreqEnd, lpFreqEndAt, callback) { +var applyGradualLowpass = function (input, lpFreqStart, lpFreqEnd, lpFreqEndAt, callback) { if (lpFreqStart == 0) { callback(input); return; @@ -173,7 +169,7 @@ var applyGradualLowpass = function(input, lpFreqStart, lpFreqEnd, lpFreqEndAt, c lpFreqStart = Math.min(lpFreqStart, input.sampleRate / 2); lpFreqEnd = Math.min(lpFreqEnd, input.sampleRate / 2); - filter.type = "lowpass"; + filter.type = 'lowpass'; filter.Q.value = 0.0001; filter.frequency.setValueAtTime(lpFreqStart, 0); filter.frequency.linearRampToValueAtTime(lpFreqEnd, lpFreqEndAt); @@ -181,7 +177,7 @@ var applyGradualLowpass = function(input, lpFreqStart, lpFreqEnd, lpFreqEndAt, c player.connect(filter); filter.connect(context.destination); player.start(); - context.oncomplete = function(event) { + context.oncomplete = function (event) { callback(event.renderedBuffer); }; context.startRendering(); @@ -192,7 +188,7 @@ var applyGradualLowpass = function(input, lpFreqStart, lpFreqEnd, lpFreqEndAt, c /** @private @param {!AudioBuffer} buffer @return {!Array.} An array containing the Float32Array of each channel's samples. */ -var getAllChannelData = function(buffer) { +var getAllChannelData = function (buffer) { var channels = []; for (var i = 0; i < buffer.numberOfChannels; i++) { channels[i] = buffer.getChannelData(i); @@ -202,7 +198,7 @@ var getAllChannelData = function(buffer) { /** @private @return {number} A random number from -1 to 1. */ -var randomSample = function() { +var randomSample = function () { return Math.random() * 2 - 1; }; diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index 0d08c8ad..39e05e79 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -109,20 +109,12 @@ function getDelay(orbit, delaytime, delayfeedback, t) { let reverbs = {}; function getReverb(orbit, duration = 2, fade, revlp, revdim) { - // If no reverb has been created for a given orbit, create one if (!reverbs[orbit]) { const ac = getAudioContext(); - const reverb = ac.createReverb( - getAudioContext(), - duration, - fade, - revlp, - revdim, - ); + const reverb = ac.createReverb(getAudioContext(), duration, fade, revlp, revdim); reverb.connect(getDestination()); reverbs[orbit] = reverb; - console.log(reverbs[orbit]); } if ( @@ -131,14 +123,11 @@ function getReverb(orbit, duration = 2, fade, revlp, revdim) { reverbs[orbit].revlp !== revlp || reverbs[orbit].revdim !== revdim ) { - reverbs[orbit] = reverbs[orbit].setDuration( - duration, fade, revlp, revdim - ); + reverbs[orbit] = reverbs[orbit].setDuration(duration, fade, revlp, revdim); reverbs[orbit].duration = duration; reverbs[orbit].fade = fade; reverbs[orbit].revlp = revlp; reverbs[orbit].revdim = revdim; - } return reverbs[orbit]; From 275796c241f3740e71bc1366e501a4855d7cbc8d Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Sun, 1 Oct 2023 23:21:02 +0200 Subject: [PATCH 051/158] fix: reverbs[orbit] is undefined --- packages/superdough/superdough.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index 39e05e79..28f4c261 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -123,7 +123,7 @@ function getReverb(orbit, duration = 2, fade, revlp, revdim) { reverbs[orbit].revlp !== revlp || reverbs[orbit].revdim !== revdim ) { - reverbs[orbit] = reverbs[orbit].setDuration(duration, fade, revlp, revdim); + reverbs[orbit].setDuration(duration, fade, revlp, revdim); reverbs[orbit].duration = duration; reverbs[orbit].fade = fade; reverbs[orbit].revlp = revlp; From dfdd9e02ca43f7117fe995cd9baba57ed7bed561 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Sun, 1 Oct 2023 23:22:32 +0200 Subject: [PATCH 052/158] eslint ignore reverbGen + snapshot --- .eslintignore | 3 +- test/__snapshots__/examples.test.mjs.snap | 78 +++++++++++++++++++++++ 2 files changed, 80 insertions(+), 1 deletion(-) diff --git a/.eslintignore b/.eslintignore index 13e635f3..58d3643d 100644 --- a/.eslintignore +++ b/.eslintignore @@ -18,4 +18,5 @@ vite.config.js **/*.json **/dev-dist **/dist -/src-tauri/target/**/* \ No newline at end of file +/src-tauri/target/**/* +reverbGen.mjs \ No newline at end of file diff --git a/test/__snapshots__/examples.test.mjs.snap b/test/__snapshots__/examples.test.mjs.snap index e026f9c4..616fff12 100644 --- a/test/__snapshots__/examples.test.mjs.snap +++ b/test/__snapshots__/examples.test.mjs.snap @@ -1773,6 +1773,32 @@ exports[`runs examples > example "every" example index 0 1`] = ` ] `; +exports[`runs examples > example "fade" example index 0 1`] = ` +[ + "[ 0/1 → 1/2 | s:bd room:0.5 revlp:10000 fade:0.5 ]", + "[ 1/2 → 1/1 | s:sd room:0.5 revlp:10000 fade:0.5 ]", + "[ 1/1 → 3/2 | s:bd room:0.5 revlp:10000 fade:0.5 ]", + "[ 3/2 → 2/1 | s:sd room:0.5 revlp:10000 fade:0.5 ]", + "[ 2/1 → 5/2 | s:bd room:0.5 revlp:10000 fade:0.5 ]", + "[ 5/2 → 3/1 | s:sd room:0.5 revlp:10000 fade:0.5 ]", + "[ 3/1 → 7/2 | s:bd room:0.5 revlp:10000 fade:0.5 ]", + "[ 7/2 → 4/1 | s:sd room:0.5 revlp:10000 fade:0.5 ]", +] +`; + +exports[`runs examples > example "fade" example index 1 1`] = ` +[ + "[ 0/1 → 1/2 | s:bd room:0.5 revlp:5000 fade:4 ]", + "[ 1/2 → 1/1 | s:sd room:0.5 revlp:5000 fade:4 ]", + "[ 1/1 → 3/2 | s:bd room:0.5 revlp:5000 fade:4 ]", + "[ 3/2 → 2/1 | s:sd room:0.5 revlp:5000 fade:4 ]", + "[ 2/1 → 5/2 | s:bd room:0.5 revlp:5000 fade:4 ]", + "[ 5/2 → 3/1 | s:sd room:0.5 revlp:5000 fade:4 ]", + "[ 3/1 → 7/2 | s:bd room:0.5 revlp:5000 fade:4 ]", + "[ 7/2 → 4/1 | s:sd room:0.5 revlp:5000 fade:4 ]", +] +`; + exports[`runs examples > example "fast" example index 0 1`] = ` [ "[ 0/1 → 1/4 | s:bd ]", @@ -3607,6 +3633,58 @@ exports[`runs examples > example "rev" example index 0 1`] = ` ] `; +exports[`runs examples > example "revdim" example index 0 1`] = ` +[ + "[ 0/1 → 1/2 | s:bd room:0.5 revlp:10000 revdim:8000 ]", + "[ 1/2 → 1/1 | s:sd room:0.5 revlp:10000 revdim:8000 ]", + "[ 1/1 → 3/2 | s:bd room:0.5 revlp:10000 revdim:8000 ]", + "[ 3/2 → 2/1 | s:sd room:0.5 revlp:10000 revdim:8000 ]", + "[ 2/1 → 5/2 | s:bd room:0.5 revlp:10000 revdim:8000 ]", + "[ 5/2 → 3/1 | s:sd room:0.5 revlp:10000 revdim:8000 ]", + "[ 3/1 → 7/2 | s:bd room:0.5 revlp:10000 revdim:8000 ]", + "[ 7/2 → 4/1 | s:sd room:0.5 revlp:10000 revdim:8000 ]", +] +`; + +exports[`runs examples > example "revdim" example index 1 1`] = ` +[ + "[ 0/1 → 1/2 | s:bd room:0.5 revlp:5000 revdim:400 ]", + "[ 1/2 → 1/1 | s:sd room:0.5 revlp:5000 revdim:400 ]", + "[ 1/1 → 3/2 | s:bd room:0.5 revlp:5000 revdim:400 ]", + "[ 3/2 → 2/1 | s:sd room:0.5 revlp:5000 revdim:400 ]", + "[ 2/1 → 5/2 | s:bd room:0.5 revlp:5000 revdim:400 ]", + "[ 5/2 → 3/1 | s:sd room:0.5 revlp:5000 revdim:400 ]", + "[ 3/1 → 7/2 | s:bd room:0.5 revlp:5000 revdim:400 ]", + "[ 7/2 → 4/1 | s:sd room:0.5 revlp:5000 revdim:400 ]", +] +`; + +exports[`runs examples > example "revlp" example index 0 1`] = ` +[ + "[ 0/1 → 1/2 | s:bd room:0.5 revlp:10000 ]", + "[ 1/2 → 1/1 | s:sd room:0.5 revlp:10000 ]", + "[ 1/1 → 3/2 | s:bd room:0.5 revlp:10000 ]", + "[ 3/2 → 2/1 | s:sd room:0.5 revlp:10000 ]", + "[ 2/1 → 5/2 | s:bd room:0.5 revlp:10000 ]", + "[ 5/2 → 3/1 | s:sd room:0.5 revlp:10000 ]", + "[ 3/1 → 7/2 | s:bd room:0.5 revlp:10000 ]", + "[ 7/2 → 4/1 | s:sd room:0.5 revlp:10000 ]", +] +`; + +exports[`runs examples > example "revlp" example index 1 1`] = ` +[ + "[ 0/1 → 1/2 | s:bd room:0.5 revlp:5000 ]", + "[ 1/2 → 1/1 | s:sd room:0.5 revlp:5000 ]", + "[ 1/1 → 3/2 | s:bd room:0.5 revlp:5000 ]", + "[ 3/2 → 2/1 | s:sd room:0.5 revlp:5000 ]", + "[ 2/1 → 5/2 | s:bd room:0.5 revlp:5000 ]", + "[ 5/2 → 3/1 | s:sd room:0.5 revlp:5000 ]", + "[ 3/1 → 7/2 | s:bd room:0.5 revlp:5000 ]", + "[ 7/2 → 4/1 | s:sd room:0.5 revlp:5000 ]", +] +`; + exports[`runs examples > example "ribbon" example index 0 1`] = ` [ "[ 0/1 → 1/4 | note:C3 ]", From 13cb32903a8e44f2fb11a74a62a6776caf3d6624 Mon Sep 17 00:00:00 2001 From: Vasilii Milovidov Date: Mon, 2 Oct 2023 12:13:44 +0400 Subject: [PATCH 053/158] Prepare to merge with PR #718 --- packages/core/controls.mjs | 68 +++++++-- packages/superdough/reverb.mjs | 53 +++++-- packages/superdough/reverbGen.mjs | 207 ++++++++++++++++++++++++++++ packages/superdough/superdough.mjs | 34 +++-- website/src/pages/learn/effects.mdx | 16 +++ 5 files changed, 341 insertions(+), 37 deletions(-) create mode 100644 packages/superdough/reverbGen.mjs diff --git a/packages/core/controls.mjs b/packages/core/controls.mjs index bc06ecc2..d909423f 100644 --- a/packages/core/controls.mjs +++ b/packages/core/controls.mjs @@ -980,7 +980,62 @@ const generic_params = [ * s("bd sd").room(.8).roomsize("<0 1 2 4 8>") * */ - + /** + * Reverb lowpass starting frequency (in hertz). + * + * @name revlp + * @param {number} level between 0 and 20000hz + * @example + * s("bd sd").room(0.5).revlp(10000) + * @example + * s("bd sd").room(0.5).revlp(5000) + */ + ['revlp'], + /** + * Reverb lowpass frequency at -60dB (in hertz). + * + * @name revdim + * @param {number} level between 0 and 20000hz + * @example + * s("bd sd").room(0.5).revlp(10000).revdim(8000) + * @example + * s("bd sd").room(0.5).revlp(5000).revdim(400) + * + */ + ['revdim'], + /** + * Reverb fade time (in seconds). + * + * @name fade + * @param {number} seconds for the reverb to fade + * @example + * s("bd sd").room(0.5).revlp(10000).fade(0.5) + * @example + * s("bd sd").room(0.5).revlp(5000).fade(4) + * + */ + ['fade'], + /** + * Sets the sample to use as an impulse response for the reverb. + * + * @name iresponse + * @param {string | Pattern} sample sample to pick as an impulse response + * @synonyms ir + * @example + * s("bd sd").room(.8).ir("") + * + */ + [['ir', 'i'], 'iresponse'], + /** + * Sets the room size of the reverb, see {@link room}. + * + * @name roomsize + * @param {number | Pattern} size between 0 and 10 + * @synonyms size, sz + * @example + * s("bd sd").room(.8).roomsize("<0 1 2 4 8>") + * + */ // TODO: find out why : // s("bd sd").room(.8).roomsize("<0 .2 .4 .6 .8 [1,0]>").osc() // .. does not work. Is it because room is only one effect? @@ -990,17 +1045,6 @@ const generic_params = [ // ['sclaves'], // ['scrash'], - /** - * Sets the sample to use as an impulse response for the reverb. - * - * @name iresponse - * @param {string | Pattern} Sets the impulse response - * @example - * s("bd sd").room(.8).ir("") - * - */ - [['ir', 'i'], 'iresponse'], - /** * Wave shaping distortion. CAUTION: it might get loud * diff --git a/packages/superdough/reverb.mjs b/packages/superdough/reverb.mjs index e72af033..5e4ce8f1 100644 --- a/packages/superdough/reverb.mjs +++ b/packages/superdough/reverb.mjs @@ -1,11 +1,7 @@ +import reverbGen from './reverbGen.mjs'; + if (typeof AudioContext !== 'undefined') { - AudioContext.prototype.impulseResponse = function (duration, channels = 1) { - const length = this.sampleRate * duration; - const impulse = this.createBuffer(channels, length, this.sampleRate); - const IR = impulse.getChannelData(0); - for (let i = 0; i < length; i++) IR[i] = (2 * Math.random() - 1) * Math.pow(1 - i / length, duration); - return impulse; - }; + AudioContext.prototype.generateReverb = reverbGen.generateReverb; AudioContext.prototype.adjustLength = function (duration, buffer) { const newLength = buffer.sampleRate * duration; @@ -21,17 +17,44 @@ if (typeof AudioContext !== 'undefined') { return newBuffer; }; - AudioContext.prototype.createReverb = function (duration, buffer) { + AudioContext.prototype.createReverb = function (audioContext, duration, fade, revlp, revdim, imp) { const convolver = this.createConvolver(); - convolver.setDuration = (dur, imp) => { - convolver.buffer = imp ? this.adjustLength(dur, imp) : this.impulseResponse(dur); + + convolver.setDuration = (d, fade, revlp, revdim, imp) => { + if (imp) { + convolver.buffer = this.adjustLength(d, imp); + return convolver; + } else { + this.generateReverb( + { + audioContext, + sampleRate: 44100, + numChannels: 2, + decayTime: d, + fadeInTime: fade, + lpFreqStart: revlp, + lpFreqEnd: revdim, + }, + (buffer) => { + convolver.buffer = buffer; + }, + ); + convolver.duration = duration; + convolver.fade = fade; + convolver.revlp = revlp; + convolver.revdim = revdim; + return convolver; + } + }; + convolver.setIR = (d, fade, revlp, revdim, imp) => { + if (imp) { + convolver.buffer = this.adjustLength(d, imp); + } else { + convolver.setDuration(d, fade, revlp, revdim, imp); + } return convolver; }; - convolver.setIR = (dur, imp) => { - convolver.buffer = imp ? this.adjustLength(dur, imp) : this.impulseResponse(dur); - return convolver; - }; - convolver.setDuration(duration, buffer); + convolver.setDuration(duration, fade, revlp, revdim, imp); return convolver; }; } diff --git a/packages/superdough/reverbGen.mjs b/packages/superdough/reverbGen.mjs new file mode 100644 index 00000000..9fb46173 --- /dev/null +++ b/packages/superdough/reverbGen.mjs @@ -0,0 +1,207 @@ +// Copyright 2014 Alan deLespinasse +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +var reverbGen = {}; + +/** Generates a reverb impulse response. + + @param {!Object} params TODO: Document the properties. + @param {!function(!AudioBuffer)} callback Function to call when + the impulse response has been generated. The impulse response + is passed to this function as its parameter. May be called + immediately within the current execution context, or later. */ +reverbGen.generateReverb = function (params, callback) { + var audioContext = params.audioContext || new AudioContext(); + var sampleRate = params.sampleRate || 44100; + var numChannels = params.numChannels || 2; + // params.decayTime is the -60dB fade time. We let it go 50% longer to get to -90dB. + var totalTime = params.decayTime * 1.5; + var decaySampleFrames = Math.round(params.decayTime * sampleRate); + var numSampleFrames = Math.round(totalTime * sampleRate); + var fadeInSampleFrames = Math.round((params.fadeInTime || 0) * sampleRate); + // 60dB is a factor of 1 million in power, or 1000 in amplitude. + var decayBase = Math.pow(1 / 1000, 1 / decaySampleFrames); + var reverbIR = audioContext.createBuffer(numChannels, numSampleFrames, sampleRate); + for (var i = 0; i < numChannels; i++) { + var chan = reverbIR.getChannelData(i); + for (var j = 0; j < numSampleFrames; j++) { + chan[j] = randomSample() * Math.pow(decayBase, j); + } + for (var j = 0; j < fadeInSampleFrames; j++) { + chan[j] *= j / fadeInSampleFrames; + } + } + + applyGradualLowpass(reverbIR, params.lpFreqStart || 0, params.lpFreqEnd || 0, params.decayTime, callback); +}; + +/** Creates a canvas element showing a graph of the given data. + + @param {!Float32Array} data An array of numbers, or a Float32Array. + @param {number} width Width in pixels of the canvas. + @param {number} height Height in pixels of the canvas. + @param {number} min Minimum value of data for the graph (lower edge). + @param {number} max Maximum value of data in the graph (upper edge). + @return {!CanvasElement} The generated canvas element. */ +reverbGen.generateGraph = function (data, width, height, min, max) { + var canvas = document.createElement('canvas'); + canvas.width = width; + canvas.height = height; + var gc = canvas.getContext('2d'); + gc.fillStyle = '#000'; + gc.fillRect(0, 0, canvas.width, canvas.height); + gc.fillStyle = '#fff'; + var xscale = width / data.length; + var yscale = height / (max - min); + for (var i = 0; i < data.length; i++) { + gc.fillRect(i * xscale, height - (data[i] - min) * yscale, 1, 1); + } + return canvas; +}; + +/** Saves an AudioBuffer as a 16-bit WAV file on the client's host + file system. Normalizes it to peak at +-32767, and optionally + truncates it if there's a lot of "silence" at the end. + + @param {!AudioBuffer} buffer The buffer to save. + @param {string} name Name of file to create. + @param {number?} opt_minTail Defines what counts as "silence" for + auto-truncating the buffer. If there is a point past which every + value of every channel is less than opt_minTail, then the buffer + is truncated at that point. This is expressed as an integer, + applying to the post-normalized and integer-converted + buffer. The default is 0, meaning don't truncate. */ +reverbGen.saveWavFile = function (buffer, name, opt_minTail) { + var bitsPerSample = 16; + var bytesPerSample = 2; + var sampleRate = buffer.sampleRate; + var numChannels = buffer.numberOfChannels; + var channels = getAllChannelData(buffer); + var numSampleFrames = channels[0].length; + var scale = 32767; + // Find normalization constant. + var max = 0; + for (var i = 0; i < numChannels; i++) { + for (var j = 0; j < numSampleFrames; j++) { + max = Math.max(max, Math.abs(channels[i][j])); + } + } + if (max) { + scale = 32767 / max; + } + // Find truncation point. + if (opt_minTail) { + var truncateAt = 0; + for (var i = 0; i < numChannels; i++) { + for (var j = 0; j < numSampleFrames; j++) { + var absSample = Math.abs(Math.round(scale * channels[i][j])); + if (absSample > opt_minTail) { + truncateAt = j; + } + } + } + numSampleFrames = truncateAt + 1; + } + var sampleDataBytes = bytesPerSample * numChannels * numSampleFrames; + var fileBytes = sampleDataBytes + 44; + var arrayBuffer = new ArrayBuffer(fileBytes); + var dataView = new DataView(arrayBuffer); + dataView.setUint32(0, 1179011410, true); // "RIFF" + dataView.setUint32(4, fileBytes - 8, true); // file length + dataView.setUint32(8, 1163280727, true); // "WAVE" + dataView.setUint32(12, 544501094, true); // "fmt " + dataView.setUint32(16, 16, true); // fmt chunk length + dataView.setUint16(20, 1, true); // PCM format + dataView.setUint16(22, numChannels, true); // NumChannels + dataView.setUint32(24, sampleRate, true); // SampleRate + var bytesPerSampleFrame = numChannels * bytesPerSample; + dataView.setUint32(28, sampleRate * bytesPerSampleFrame, true); // ByteRate + dataView.setUint16(32, bytesPerSampleFrame, true); // BlockAlign + dataView.setUint16(34, bitsPerSample, true); // BitsPerSample + dataView.setUint32(36, 1635017060, true); // "data" + dataView.setUint32(40, sampleDataBytes, true); + for (var j = 0; j < numSampleFrames; j++) { + for (var i = 0; i < numChannels; i++) { + dataView.setInt16(44 + j * bytesPerSampleFrame + i * bytesPerSample, Math.round(scale * channels[i][j]), true); + } + } + var blob = new Blob([arrayBuffer], { type: 'audio/wav' }); + var url = window.URL.createObjectURL(blob); + var linkEl = document.createElement('a'); + linkEl.href = url; + linkEl.download = name; + linkEl.style.display = 'none'; + document.body.appendChild(linkEl); + linkEl.click(); +}; + +/** Applies a constantly changing lowpass filter to the given sound. + + @private + @param {!AudioBuffer} input + @param {number} lpFreqStart + @param {number} lpFreqEnd + @param {number} lpFreqEndAt + @param {!function(!AudioBuffer)} callback May be called + immediately within the current execution context, or later.*/ +var applyGradualLowpass = function (input, lpFreqStart, lpFreqEnd, lpFreqEndAt, callback) { + if (lpFreqStart == 0) { + callback(input); + return; + } + var channelData = getAllChannelData(input); + var context = new OfflineAudioContext(input.numberOfChannels, channelData[0].length, input.sampleRate); + var player = context.createBufferSource(); + player.buffer = input; + var filter = context.createBiquadFilter(); + + lpFreqStart = Math.min(lpFreqStart, input.sampleRate / 2); + lpFreqEnd = Math.min(lpFreqEnd, input.sampleRate / 2); + + filter.type = 'lowpass'; + filter.Q.value = 0.0001; + filter.frequency.setValueAtTime(lpFreqStart, 0); + filter.frequency.linearRampToValueAtTime(lpFreqEnd, lpFreqEndAt); + + player.connect(filter); + filter.connect(context.destination); + player.start(); + context.oncomplete = function (event) { + callback(event.renderedBuffer); + }; + context.startRendering(); + + window.filterNode = filter; +}; + +/** @private + @param {!AudioBuffer} buffer + @return {!Array.} An array containing the Float32Array of each channel's samples. */ +var getAllChannelData = function (buffer) { + var channels = []; + for (var i = 0; i < buffer.numberOfChannels; i++) { + channels[i] = buffer.getChannelData(i); + } + return channels; +}; + +/** @private + @return {number} A random number from -1 to 1. */ +var randomSample = function () { + return Math.random() * 2 - 1; +}; + +export default reverbGen; diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index fdf42c02..ec3b981e 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -114,20 +114,31 @@ function getDelay(orbit, delaytime, delayfeedback, t) { let reverbs = {}; -function getReverb(orbit, duration = 2, ir) { +function getReverb(orbit, duration = 2, fade, revlp, revdim, imp) { if (!reverbs[orbit]) { const ac = getAudioContext(); - const reverb = ac.createReverb(duration, ir); + const reverb = ac.createReverb(getAudioContext(), duration, fade, revlp, revdim, imp); reverb.connect(getDestination()); reverbs[orbit] = reverb; } - if (reverbs[orbit].duration !== duration) { - reverbs[orbit] = reverbs[orbit].setDuration(duration, ir); + + const reverbOrbit = reverbs[orbit]; + + if ( + reverbs[orbit].duration !== duration || + reverbs[orbit].fade !== fade || + reverbs[orbit].revlp !== revlp || + reverbs[orbit].revdim !== revdim + ) { + reverbs[orbit].setDuration(duration, fade, revlp, revdim); reverbs[orbit].duration = duration; + reverbs[orbit].fade = fade; + reverbs[orbit].revlp = revlp; + reverbs[orbit].revdim = revdim; } - if (reverbs[orbit].ir !== ir) { - reverbs[orbit] = reverbs[orbit].setIR(duration, ir); - reverbs[orbit].ir = ir; + if (reverbs[orbit].ir !== imp) { + reverbs[orbit] = reverbs[orbit].setIR(duration, fade, revlp, revdim, imp); + reverbs[orbit].ir = imp; } return reverbs[orbit]; } @@ -227,12 +238,15 @@ export const superdough = async (value, deadline, hapDuration) => { delaytime = 0.25, orbit = 1, room, + fade = 0.1, + revlp = 15000, + revdim = 1000, size = 2, + ir, + i = 0, velocity = 1, analyze, // analyser wet fft = 8, // fftSize 0 - 10 - ir, - i = 0, } = value; gain *= velocity; // legacy fix for velocity let toDisconnect = []; // audio nodes that will be disconnected when the source has ended @@ -380,7 +394,7 @@ export const superdough = async (value, deadline, hapDuration) => { } let reverbSend; if (room > 0 && size > 0) { - const reverbNode = getReverb(orbit, size, buffer); + const reverbNode = getReverb(orbit, size, fade, revlp, revdim, buffer); reverbSend = effectSend(post, reverbNode, room); } diff --git a/website/src/pages/learn/effects.mdx b/website/src/pages/learn/effects.mdx index f77ab4c4..3ca43f69 100644 --- a/website/src/pages/learn/effects.mdx +++ b/website/src/pages/learn/effects.mdx @@ -203,4 +203,20 @@ global effects use the same chain for all events of the same orbit: +## fade + + + +## revlp + + + +## revdim + + + +## iresponse + + + Next, we'll look at strudel's support for [Csound](/learn/csound). From 28966739f61222d83087355baf99413168ef5607 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Mon, 2 Oct 2023 20:44:51 +0200 Subject: [PATCH 054/158] feat: add step as slider param --- packages/codemirror/slider.mjs | 9 +++++---- packages/transpiler/transpiler.mjs | 1 + 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/codemirror/slider.mjs b/packages/codemirror/slider.mjs index 47b686c8..62ec33c2 100644 --- a/packages/codemirror/slider.mjs +++ b/packages/codemirror/slider.mjs @@ -6,7 +6,7 @@ export let sliderValues = {}; const getSliderID = (from) => `slider_${from}`; export class SliderWidget extends WidgetType { - constructor(value, min, max, from, to, view) { + constructor(value, min, max, from, to, step, view) { super(); this.value = value; this.min = min; @@ -14,6 +14,7 @@ export class SliderWidget extends WidgetType { this.from = from; this.originalFrom = from; this.to = to; + this.step = step; this.view = view; } @@ -29,7 +30,7 @@ export class SliderWidget extends WidgetType { slider.type = 'range'; slider.min = this.min; slider.max = this.max; - slider.step = (this.max - this.min) / 1000; + slider.step = this.step ?? (this.max - this.min) / 1000; slider.originalValue = this.value; // to make sure the code stays in sync, let's save the original value // becuase .value automatically clamps values so it'll desync with the code @@ -66,9 +67,9 @@ export const updateWidgets = (view, widgets) => { }; function getWidgets(widgetConfigs, view) { - return widgetConfigs.map(({ from, to, value, min, max }) => { + return widgetConfigs.map(({ from, to, value, min, max, step }) => { return Decoration.widget({ - widget: new SliderWidget(value, min, max, from, to, view), + widget: new SliderWidget(value, min, max, from, to, step, view), side: 0, }).range(from /* , to */); }); diff --git a/packages/transpiler/transpiler.mjs b/packages/transpiler/transpiler.mjs index 6eac171f..256be1d2 100644 --- a/packages/transpiler/transpiler.mjs +++ b/packages/transpiler/transpiler.mjs @@ -43,6 +43,7 @@ export function transpiler(input, options = {}) { value: node.arguments[0].raw, // don't use value! min: node.arguments[1]?.value ?? 0, max: node.arguments[2]?.value ?? 1, + step: node.arguments[3]?.value, }); return this.replace(widgetWithLocation(node)); } From a3649148c1ab0975a1a3486a3bd68313c762dbb5 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Tue, 3 Oct 2023 08:35:15 +0200 Subject: [PATCH 055/158] more logical naming + update docs --- packages/core/controls.mjs | 43 +++++++++++++++++------------ packages/superdough/superdough.mjs | 12 ++++---- website/src/pages/learn/effects.mdx | 26 +++++++++-------- 3 files changed, 47 insertions(+), 34 deletions(-) diff --git a/packages/core/controls.mjs b/packages/core/controls.mjs index e72f006d..63ffdaf4 100644 --- a/packages/core/controls.mjs +++ b/packages/core/controls.mjs @@ -972,53 +972,62 @@ const generic_params = [ [['room', 'size']], /** * Reverb lowpass starting frequency (in hertz). + * When this property is changed, the reverb will be recaculated, so only change this sparsely.. * - * @name revlp - * @param {number} level between 0 and 20000hz + * @name roomlp + * @synonyms rlp + * @param {number} frequency between 0 and 20000hz * @example - * s("bd sd").room(0.5).revlp(10000) + * s("bd sd").room(0.5).rlp(10000) * @example - * s("bd sd").room(0.5).revlp(5000) + * s("bd sd").room(0.5).rlp(5000) */ - ['revlp'], + ['roomlp', 'rlp'], /** * Reverb lowpass frequency at -60dB (in hertz). + * When this property is changed, the reverb will be recaculated, so only change this sparsely.. * - * @name revdim - * @param {number} level between 0 and 20000hz + * @name roomdim + * @synonyms rdim + * @param {number} frequency between 0 and 20000hz * @example - * s("bd sd").room(0.5).revlp(10000).revdim(8000) + * s("bd sd").room(0.5).rlp(10000).rdim(8000) * @example - * s("bd sd").room(0.5).revlp(5000).revdim(400) + * s("bd sd").room(0.5).rlp(5000).rdim(400) * */ - ['revdim'], + ['roomdim', 'rdim'], /** * Reverb fade time (in seconds). + * When this property is changed, the reverb will be recaculated, so only change this sparsely.. * - * @name fade + * @name roomfade + * @synonyms rfade * @param {number} seconds for the reverb to fade * @example - * s("bd sd").room(0.5).revlp(10000).fade(0.5) + * s("bd sd").room(0.5).rlp(10000).rfade(0.5) * @example - * s("bd sd").room(0.5).revlp(5000).fade(4) + * s("bd sd").room(0.5).rlp(5000).rfade(4) * */ - ['fade'], + ['roomfade', 'rfade'], /** * Sets the room size of the reverb, see {@link room}. + * When this property is changed, the reverb will be recaculated, so only change this sparsely.. * * @name roomsize * @param {number | Pattern} size between 0 and 10 - * @synonyms size, sz + * @synonyms rsize, sz, size * @example - * s("bd sd").room(.8).roomsize("<0 1 2 4 8>") + * s("bd sd").room(.8).rsize(1) + * @example + * s("bd sd").room(.8).rsize(4) * */ // TODO: find out why : // s("bd sd").room(.8).roomsize("<0 .2 .4 .6 .8 [1,0]>").osc() // .. does not work. Is it because room is only one effect? - ['size', 'sz', 'roomsize'], + ['roomsize', 'size', 'sz', 'rsize'], // ['sagogo'], // ['sclap'], // ['sclaves'], diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index 28f4c261..e387ff6c 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -227,10 +227,10 @@ export const superdough = async (value, deadline, hapDuration) => { delaytime = 0.25, orbit = 1, room, - fade = 0.1, - revlp = 15000, - revdim = 1000, - size = 2, + roomfade = 0.1, + roomlp = 15000, + roomdim = 1000, + roomsize = 2, velocity = 1, analyze, // analyser wet fft = 8, // fftSize 0 - 10 @@ -368,8 +368,8 @@ export const superdough = async (value, deadline, hapDuration) => { } // reverb let reverbSend; - if (room > 0 && size > 0) { - const reverbNode = getReverb(orbit, size, fade, revlp, revdim); + if (room > 0 && roomsize > 0) { + const reverbNode = getReverb(orbit, roomsize, roomfade, roomlp, roomdim); reverbSend = effectSend(post, reverbNode, room); } diff --git a/website/src/pages/learn/effects.mdx b/website/src/pages/learn/effects.mdx index 062a1947..2ee6c44a 100644 --- a/website/src/pages/learn/effects.mdx +++ b/website/src/pages/learn/effects.mdx @@ -183,36 +183,40 @@ global effects use the same chain for all events of the same orbit: -## delay +## Delay + +### delay -## delaytime +### delaytime -## delayfeedback +### delayfeedback -## room +## Reverb + +### room -## roomsize +### roomsize -## fade +### roomfade - + -## revlp +### roomlp - + -## revdim +### roomdim - + Next, we'll look at strudel's support for [Csound](/learn/csound). From 904306454349862a7356f355c294e3e89f457842 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Tue, 3 Oct 2023 08:36:52 +0200 Subject: [PATCH 056/158] snapshot --- test/__snapshots__/examples.test.mjs.snap | 185 ++++++++++++---------- 1 file changed, 99 insertions(+), 86 deletions(-) diff --git a/test/__snapshots__/examples.test.mjs.snap b/test/__snapshots__/examples.test.mjs.snap index 616fff12..99d078ea 100644 --- a/test/__snapshots__/examples.test.mjs.snap +++ b/test/__snapshots__/examples.test.mjs.snap @@ -1773,32 +1773,6 @@ exports[`runs examples > example "every" example index 0 1`] = ` ] `; -exports[`runs examples > example "fade" example index 0 1`] = ` -[ - "[ 0/1 → 1/2 | s:bd room:0.5 revlp:10000 fade:0.5 ]", - "[ 1/2 → 1/1 | s:sd room:0.5 revlp:10000 fade:0.5 ]", - "[ 1/1 → 3/2 | s:bd room:0.5 revlp:10000 fade:0.5 ]", - "[ 3/2 → 2/1 | s:sd room:0.5 revlp:10000 fade:0.5 ]", - "[ 2/1 → 5/2 | s:bd room:0.5 revlp:10000 fade:0.5 ]", - "[ 5/2 → 3/1 | s:sd room:0.5 revlp:10000 fade:0.5 ]", - "[ 3/1 → 7/2 | s:bd room:0.5 revlp:10000 fade:0.5 ]", - "[ 7/2 → 4/1 | s:sd room:0.5 revlp:10000 fade:0.5 ]", -] -`; - -exports[`runs examples > example "fade" example index 1 1`] = ` -[ - "[ 0/1 → 1/2 | s:bd room:0.5 revlp:5000 fade:4 ]", - "[ 1/2 → 1/1 | s:sd room:0.5 revlp:5000 fade:4 ]", - "[ 1/1 → 3/2 | s:bd room:0.5 revlp:5000 fade:4 ]", - "[ 3/2 → 2/1 | s:sd room:0.5 revlp:5000 fade:4 ]", - "[ 2/1 → 5/2 | s:bd room:0.5 revlp:5000 fade:4 ]", - "[ 5/2 → 3/1 | s:sd room:0.5 revlp:5000 fade:4 ]", - "[ 3/1 → 7/2 | s:bd room:0.5 revlp:5000 fade:4 ]", - "[ 7/2 → 4/1 | s:sd room:0.5 revlp:5000 fade:4 ]", -] -`; - exports[`runs examples > example "fast" example index 0 1`] = ` [ "[ 0/1 → 1/4 | s:bd ]", @@ -3633,58 +3607,6 @@ exports[`runs examples > example "rev" example index 0 1`] = ` ] `; -exports[`runs examples > example "revdim" example index 0 1`] = ` -[ - "[ 0/1 → 1/2 | s:bd room:0.5 revlp:10000 revdim:8000 ]", - "[ 1/2 → 1/1 | s:sd room:0.5 revlp:10000 revdim:8000 ]", - "[ 1/1 → 3/2 | s:bd room:0.5 revlp:10000 revdim:8000 ]", - "[ 3/2 → 2/1 | s:sd room:0.5 revlp:10000 revdim:8000 ]", - "[ 2/1 → 5/2 | s:bd room:0.5 revlp:10000 revdim:8000 ]", - "[ 5/2 → 3/1 | s:sd room:0.5 revlp:10000 revdim:8000 ]", - "[ 3/1 → 7/2 | s:bd room:0.5 revlp:10000 revdim:8000 ]", - "[ 7/2 → 4/1 | s:sd room:0.5 revlp:10000 revdim:8000 ]", -] -`; - -exports[`runs examples > example "revdim" example index 1 1`] = ` -[ - "[ 0/1 → 1/2 | s:bd room:0.5 revlp:5000 revdim:400 ]", - "[ 1/2 → 1/1 | s:sd room:0.5 revlp:5000 revdim:400 ]", - "[ 1/1 → 3/2 | s:bd room:0.5 revlp:5000 revdim:400 ]", - "[ 3/2 → 2/1 | s:sd room:0.5 revlp:5000 revdim:400 ]", - "[ 2/1 → 5/2 | s:bd room:0.5 revlp:5000 revdim:400 ]", - "[ 5/2 → 3/1 | s:sd room:0.5 revlp:5000 revdim:400 ]", - "[ 3/1 → 7/2 | s:bd room:0.5 revlp:5000 revdim:400 ]", - "[ 7/2 → 4/1 | s:sd room:0.5 revlp:5000 revdim:400 ]", -] -`; - -exports[`runs examples > example "revlp" example index 0 1`] = ` -[ - "[ 0/1 → 1/2 | s:bd room:0.5 revlp:10000 ]", - "[ 1/2 → 1/1 | s:sd room:0.5 revlp:10000 ]", - "[ 1/1 → 3/2 | s:bd room:0.5 revlp:10000 ]", - "[ 3/2 → 2/1 | s:sd room:0.5 revlp:10000 ]", - "[ 2/1 → 5/2 | s:bd room:0.5 revlp:10000 ]", - "[ 5/2 → 3/1 | s:sd room:0.5 revlp:10000 ]", - "[ 3/1 → 7/2 | s:bd room:0.5 revlp:10000 ]", - "[ 7/2 → 4/1 | s:sd room:0.5 revlp:10000 ]", -] -`; - -exports[`runs examples > example "revlp" example index 1 1`] = ` -[ - "[ 0/1 → 1/2 | s:bd room:0.5 revlp:5000 ]", - "[ 1/2 → 1/1 | s:sd room:0.5 revlp:5000 ]", - "[ 1/1 → 3/2 | s:bd room:0.5 revlp:5000 ]", - "[ 3/2 → 2/1 | s:sd room:0.5 revlp:5000 ]", - "[ 2/1 → 5/2 | s:bd room:0.5 revlp:5000 ]", - "[ 5/2 → 3/1 | s:sd room:0.5 revlp:5000 ]", - "[ 3/1 → 7/2 | s:bd room:0.5 revlp:5000 ]", - "[ 7/2 → 4/1 | s:sd room:0.5 revlp:5000 ]", -] -`; - exports[`runs examples > example "ribbon" example index 0 1`] = ` [ "[ 0/1 → 1/4 | note:C3 ]", @@ -3732,16 +3654,107 @@ exports[`runs examples > example "room" example index 1 1`] = ` ] `; +exports[`runs examples > example "roomdim" example index 0 1`] = ` +[ + "[ 0/1 → 1/2 | s:bd room:0.5 roomlp:10000 roomdim:8000 ]", + "[ 1/2 → 1/1 | s:sd room:0.5 roomlp:10000 roomdim:8000 ]", + "[ 1/1 → 3/2 | s:bd room:0.5 roomlp:10000 roomdim:8000 ]", + "[ 3/2 → 2/1 | s:sd room:0.5 roomlp:10000 roomdim:8000 ]", + "[ 2/1 → 5/2 | s:bd room:0.5 roomlp:10000 roomdim:8000 ]", + "[ 5/2 → 3/1 | s:sd room:0.5 roomlp:10000 roomdim:8000 ]", + "[ 3/1 → 7/2 | s:bd room:0.5 roomlp:10000 roomdim:8000 ]", + "[ 7/2 → 4/1 | s:sd room:0.5 roomlp:10000 roomdim:8000 ]", +] +`; + +exports[`runs examples > example "roomdim" example index 1 1`] = ` +[ + "[ 0/1 → 1/2 | s:bd room:0.5 roomlp:5000 roomdim:400 ]", + "[ 1/2 → 1/1 | s:sd room:0.5 roomlp:5000 roomdim:400 ]", + "[ 1/1 → 3/2 | s:bd room:0.5 roomlp:5000 roomdim:400 ]", + "[ 3/2 → 2/1 | s:sd room:0.5 roomlp:5000 roomdim:400 ]", + "[ 2/1 → 5/2 | s:bd room:0.5 roomlp:5000 roomdim:400 ]", + "[ 5/2 → 3/1 | s:sd room:0.5 roomlp:5000 roomdim:400 ]", + "[ 3/1 → 7/2 | s:bd room:0.5 roomlp:5000 roomdim:400 ]", + "[ 7/2 → 4/1 | s:sd room:0.5 roomlp:5000 roomdim:400 ]", +] +`; + +exports[`runs examples > example "roomfade" example index 0 1`] = ` +[ + "[ 0/1 → 1/2 | s:bd room:0.5 roomlp:10000 roomfade:0.5 ]", + "[ 1/2 → 1/1 | s:sd room:0.5 roomlp:10000 roomfade:0.5 ]", + "[ 1/1 → 3/2 | s:bd room:0.5 roomlp:10000 roomfade:0.5 ]", + "[ 3/2 → 2/1 | s:sd room:0.5 roomlp:10000 roomfade:0.5 ]", + "[ 2/1 → 5/2 | s:bd room:0.5 roomlp:10000 roomfade:0.5 ]", + "[ 5/2 → 3/1 | s:sd room:0.5 roomlp:10000 roomfade:0.5 ]", + "[ 3/1 → 7/2 | s:bd room:0.5 roomlp:10000 roomfade:0.5 ]", + "[ 7/2 → 4/1 | s:sd room:0.5 roomlp:10000 roomfade:0.5 ]", +] +`; + +exports[`runs examples > example "roomfade" example index 1 1`] = ` +[ + "[ 0/1 → 1/2 | s:bd room:0.5 roomlp:5000 roomfade:4 ]", + "[ 1/2 → 1/1 | s:sd room:0.5 roomlp:5000 roomfade:4 ]", + "[ 1/1 → 3/2 | s:bd room:0.5 roomlp:5000 roomfade:4 ]", + "[ 3/2 → 2/1 | s:sd room:0.5 roomlp:5000 roomfade:4 ]", + "[ 2/1 → 5/2 | s:bd room:0.5 roomlp:5000 roomfade:4 ]", + "[ 5/2 → 3/1 | s:sd room:0.5 roomlp:5000 roomfade:4 ]", + "[ 3/1 → 7/2 | s:bd room:0.5 roomlp:5000 roomfade:4 ]", + "[ 7/2 → 4/1 | s:sd room:0.5 roomlp:5000 roomfade:4 ]", +] +`; + +exports[`runs examples > example "roomlp" example index 0 1`] = ` +[ + "[ 0/1 → 1/2 | s:bd room:0.5 roomlp:10000 ]", + "[ 1/2 → 1/1 | s:sd room:0.5 roomlp:10000 ]", + "[ 1/1 → 3/2 | s:bd room:0.5 roomlp:10000 ]", + "[ 3/2 → 2/1 | s:sd room:0.5 roomlp:10000 ]", + "[ 2/1 → 5/2 | s:bd room:0.5 roomlp:10000 ]", + "[ 5/2 → 3/1 | s:sd room:0.5 roomlp:10000 ]", + "[ 3/1 → 7/2 | s:bd room:0.5 roomlp:10000 ]", + "[ 7/2 → 4/1 | s:sd room:0.5 roomlp:10000 ]", +] +`; + +exports[`runs examples > example "roomlp" example index 1 1`] = ` +[ + "[ 0/1 → 1/2 | s:bd room:0.5 roomlp:5000 ]", + "[ 1/2 → 1/1 | s:sd room:0.5 roomlp:5000 ]", + "[ 1/1 → 3/2 | s:bd room:0.5 roomlp:5000 ]", + "[ 3/2 → 2/1 | s:sd room:0.5 roomlp:5000 ]", + "[ 2/1 → 5/2 | s:bd room:0.5 roomlp:5000 ]", + "[ 5/2 → 3/1 | s:sd room:0.5 roomlp:5000 ]", + "[ 3/1 → 7/2 | s:bd room:0.5 roomlp:5000 ]", + "[ 7/2 → 4/1 | s:sd room:0.5 roomlp:5000 ]", +] +`; + exports[`runs examples > example "roomsize" example index 0 1`] = ` [ - "[ 0/1 → 1/2 | s:bd room:0.8 size:0 ]", - "[ 1/2 → 1/1 | s:sd room:0.8 size:0 ]", - "[ 1/1 → 3/2 | s:bd room:0.8 size:1 ]", - "[ 3/2 → 2/1 | s:sd room:0.8 size:1 ]", - "[ 2/1 → 5/2 | s:bd room:0.8 size:2 ]", - "[ 5/2 → 3/1 | s:sd room:0.8 size:2 ]", - "[ 3/1 → 7/2 | s:bd room:0.8 size:4 ]", - "[ 7/2 → 4/1 | s:sd room:0.8 size:4 ]", + "[ 0/1 → 1/2 | s:bd room:0.8 roomsize:1 ]", + "[ 1/2 → 1/1 | s:sd room:0.8 roomsize:1 ]", + "[ 1/1 → 3/2 | s:bd room:0.8 roomsize:1 ]", + "[ 3/2 → 2/1 | s:sd room:0.8 roomsize:1 ]", + "[ 2/1 → 5/2 | s:bd room:0.8 roomsize:1 ]", + "[ 5/2 → 3/1 | s:sd room:0.8 roomsize:1 ]", + "[ 3/1 → 7/2 | s:bd room:0.8 roomsize:1 ]", + "[ 7/2 → 4/1 | s:sd room:0.8 roomsize:1 ]", +] +`; + +exports[`runs examples > example "roomsize" example index 1 1`] = ` +[ + "[ 0/1 → 1/2 | s:bd room:0.8 roomsize:4 ]", + "[ 1/2 → 1/1 | s:sd room:0.8 roomsize:4 ]", + "[ 1/1 → 3/2 | s:bd room:0.8 roomsize:4 ]", + "[ 3/2 → 2/1 | s:sd room:0.8 roomsize:4 ]", + "[ 2/1 → 5/2 | s:bd room:0.8 roomsize:4 ]", + "[ 5/2 → 3/1 | s:sd room:0.8 roomsize:4 ]", + "[ 3/1 → 7/2 | s:bd room:0.8 roomsize:4 ]", + "[ 7/2 → 4/1 | s:sd room:0.8 roomsize:4 ]", ] `; From 1e352fdf8001696b243cdb7f7fc777e34b151eb5 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Tue, 3 Oct 2023 08:51:35 +0200 Subject: [PATCH 057/158] codeformat --- packages/superdough/synth.mjs | 215 +++++++++++++++-------------- website/src/pages/learn/synths.mdx | 5 +- 2 files changed, 111 insertions(+), 109 deletions(-) diff --git a/packages/superdough/synth.mjs b/packages/superdough/synth.mjs index e5f84bcf..51d56993 100644 --- a/packages/superdough/synth.mjs +++ b/packages/superdough/synth.mjs @@ -20,104 +20,110 @@ const fm = (osc, harmonicityRatio, modulationIndex, wave = 'sine') => { return mod(modfreq, modgain, wave); }; - export function registerSynthSounds() { - ['sine', 'square', 'triangle', - 'sawtooth', 'pink', 'white', - 'brown'].forEach((wave) => { - registerSound( - wave, - (t, value, onended) => { - // destructure adsr here, because the default should be different for synths and samples - let { - attack = 0.001, - decay = 0.05, - sustain = 0.6, - release = 0.01, - fmh: fmHarmonicity = 1, - fmi: fmModulationIndex, - fmenv: fmEnvelopeType = 'lin', - fmattack: fmAttack, - fmdecay: fmDecay, - fmsustain: fmSustain, - fmrelease: fmRelease, - fmvelocity: fmVelocity, - fmwave: fmWaveform = 'sine', - vib = 0, - vibmod = 0.5, - noise = 0, - } = value; - let { n, note, freq } = value; - // with synths, n and note are the same thing - note = note || 36; - if (typeof note === 'string') { - note = noteToMidi(note); // e.g. c3 => 48 - } - // get frequency - if (!freq && typeof note === 'number') { - freq = midiToFreq(note); // + 48); - } - // maybe pull out the above frequency resolution?? (there is also getFrequency but it has no default) - // make oscillator - const { node: o, stop, dry_node = null } = getOscillator({ - t, - s: wave, - freq, - vib, - vibmod, - partials: n, - noise: noise, - }); - // FM + FM envelope - let stopFm, fmEnvelope; - if (fmModulationIndex) { - const { node: modulator, stop } = fm(dry_node !== null ? dry_node : o, fmHarmonicity, fmModulationIndex, fmWaveform); - if (![fmAttack, fmDecay, fmSustain, fmRelease, fmVelocity].find((v) => v !== undefined)) { - // no envelope by default - modulator.connect(dry_node !== null ? dry_node.frequency : o.frequency); - } else { - fmAttack = fmAttack ?? 0.001; - fmDecay = fmDecay ?? 0.001; - fmSustain = fmSustain ?? 1; - fmRelease = fmRelease ?? 0.001; - fmVelocity = fmVelocity ?? 1; - fmEnvelope = getEnvelope(fmAttack, fmDecay, fmSustain, fmRelease, fmVelocity, t); - if (fmEnvelopeType === 'exp') { - fmEnvelope = getExpEnvelope(fmAttack, fmDecay, fmSustain, fmRelease, fmVelocity, t); - fmEnvelope.node.maxValue = fmModulationIndex * 2; - fmEnvelope.node.minValue = 0.00001; - } - modulator.connect(fmEnvelope.node); - fmEnvelope.node.connect(dry_node !== null ? dry_node.frequency : o.frequency); + ['sine', 'square', 'triangle', 'sawtooth', 'pink', 'white', 'brown'].forEach((wave) => { + registerSound( + wave, + (t, value, onended) => { + // destructure adsr here, because the default should be different for synths and samples + let { + attack = 0.001, + decay = 0.05, + sustain = 0.6, + release = 0.01, + fmh: fmHarmonicity = 1, + fmi: fmModulationIndex, + fmenv: fmEnvelopeType = 'lin', + fmattack: fmAttack, + fmdecay: fmDecay, + fmsustain: fmSustain, + fmrelease: fmRelease, + fmvelocity: fmVelocity, + fmwave: fmWaveform = 'sine', + vib = 0, + vibmod = 0.5, + noise = 0, + } = value; + let { n, note, freq } = value; + // with synths, n and note are the same thing + note = note || 36; + if (typeof note === 'string') { + note = noteToMidi(note); // e.g. c3 => 48 + } + // get frequency + if (!freq && typeof note === 'number') { + freq = midiToFreq(note); // + 48); + } + // maybe pull out the above frequency resolution?? (there is also getFrequency but it has no default) + // make oscillator + const { + node: o, + stop, + dry_node = null, + } = getOscillator({ + t, + s: wave, + freq, + vib, + vibmod, + partials: n, + noise: noise, + }); + // FM + FM envelope + let stopFm, fmEnvelope; + if (fmModulationIndex) { + const { node: modulator, stop } = fm( + dry_node !== null ? dry_node : o, + fmHarmonicity, + fmModulationIndex, + fmWaveform, + ); + if (![fmAttack, fmDecay, fmSustain, fmRelease, fmVelocity].find((v) => v !== undefined)) { + // no envelope by default + modulator.connect(dry_node !== null ? dry_node.frequency : o.frequency); + } else { + fmAttack = fmAttack ?? 0.001; + fmDecay = fmDecay ?? 0.001; + fmSustain = fmSustain ?? 1; + fmRelease = fmRelease ?? 0.001; + fmVelocity = fmVelocity ?? 1; + fmEnvelope = getEnvelope(fmAttack, fmDecay, fmSustain, fmRelease, fmVelocity, t); + if (fmEnvelopeType === 'exp') { + fmEnvelope = getExpEnvelope(fmAttack, fmDecay, fmSustain, fmRelease, fmVelocity, t); + fmEnvelope.node.maxValue = fmModulationIndex * 2; + fmEnvelope.node.minValue = 0.00001; } - stopFm = stop; + modulator.connect(fmEnvelope.node); + fmEnvelope.node.connect(dry_node !== null ? dry_node.frequency : o.frequency); } + stopFm = stop; + } - // turn down - const g = gainNode(0.3); + // turn down + const g = gainNode(0.3); - // gain envelope - const { node: envelope, stop: releaseEnvelope } = getEnvelope(attack, decay, sustain, release, 1, t); + // gain envelope + const { node: envelope, stop: releaseEnvelope } = getEnvelope(attack, decay, sustain, release, 1, t); - o.onended = () => { - o.disconnect(); - g.disconnect(); - onended(); - }; - return { - node: o.connect(g).connect(envelope), - stop: (releaseTime) => { - releaseEnvelope(releaseTime); - fmEnvelope?.stop(releaseTime); - let end = releaseTime + release; - stop(end); - stopFm?.(end); - }, - }; - }, - { type: 'synth', prebake: true }, - ); - }); + o.onended = () => { + o.disconnect(); + g.disconnect(); + onended(); + }; + return { + node: o.connect(g).connect(envelope), + stop: (releaseTime) => { + releaseEnvelope(releaseTime); + fmEnvelope?.stop(releaseTime); + let end = releaseTime + release; + stop(end); + stopFm?.(end); + }, + }; + }, + { type: 'synth', prebake: true }, + ); + }); } export function waveformN(partials, type) { @@ -163,16 +169,16 @@ export function getNoiseOscillator({ t, ac, type = 'white' }) { output[i] = Math.random() * 2 - 1; } else if (type === 'brown') { let white = Math.random() * 2 - 1; - output[i] = (lastOut + (0.02 * white)) / 1.02; + output[i] = (lastOut + 0.02 * white) / 1.02; lastOut = output[i]; } else if (type === 'pink') { let white = Math.random() * 2 - 1; b0 = 0.99886 * b0 + white * 0.0555179; b1 = 0.99332 * b1 + white * 0.0750759; - b2 = 0.96900 * b2 + white * 0.1538520; - b3 = 0.86650 * b3 + white * 0.3104856; - b4 = 0.55000 * b4 + white * 0.5329522; - b5 = -0.7616 * b5 - white * 0.0168980; + b2 = 0.969 * b2 + white * 0.153852; + b3 = 0.8665 * b3 + white * 0.3104856; + b4 = 0.55 * b4 + white * 0.5329522; + b5 = -0.7616 * b5 - white * 0.016898; output[i] = b0 + b1 + b2 + b3 + b4 + b5 + b6 + white * 0.5362; output[i] *= 0.11; b6 = white * 0.115926; @@ -186,7 +192,7 @@ export function getNoiseOscillator({ t, ac, type = 'white' }) { return { node: o, - stop: (time) => o.stop(time) + stop: (time) => o.stop(time), }; } @@ -196,11 +202,11 @@ export function getOscillator({ s, freq, t, vib, vibmod, partials, noise }) { let o; if (['pink', 'white', 'brown'].includes(s)) { - let noiseOscillator = getNoiseOscillator({ t: t, ac: getAudioContext(), type: s }) + let noiseOscillator = getNoiseOscillator({ t: t, ac: getAudioContext(), type: s }); return { node: noiseOscillator.node, - stop: noiseOscillator.stop - } + stop: noiseOscillator.stop, + }; } else { if (!partials || s === 'sine') { o = getAudioContext().createOscillator(); @@ -238,7 +244,7 @@ export function getOscillator({ s, freq, t, vib, vibmod, partials, noise }) { // Connecting the main oscillator to the gain node o.connect(o_gain).connect(mix_gain); - // Instanciating a noise oscillator and connecting + // Instanciating a noise oscillator and connecting const noiseOscillator = getNoiseOscillator({ t: t, ac: ac, type: 'pink' }); noiseOscillator.node.connect(n_gain).connect(mix_gain); @@ -249,8 +255,8 @@ export function getOscillator({ s, freq, t, vib, vibmod, partials, noise }) { vibrato_oscillator?.stop(time); o.stop(time); noiseOscillator.stop(time); - } - } + }, + }; } return { @@ -262,4 +268,3 @@ export function getOscillator({ s, freq, t, vib, vibmod, partials, noise }) { }; } } - diff --git a/website/src/pages/learn/synths.mdx b/website/src/pages/learn/synths.mdx index 661ec860..83de3ca5 100644 --- a/website/src/pages/learn/synths.mdx +++ b/website/src/pages/learn/synths.mdx @@ -35,10 +35,7 @@ flavours of noise, here written from hard to soft. Some amount of pink noise can also be added to any oscillator by using the `noise` paremeter: -").scope()`} -/> +").scope()`} /> ### Additive Synthesis From a0884e2a038c654a722647237f5e760810ddcf63 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Tue, 3 Oct 2023 09:09:49 +0200 Subject: [PATCH 058/158] add noise heading + hihat example --- website/src/pages/learn/synths.mdx | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/website/src/pages/learn/synths.mdx b/website/src/pages/learn/synths.mdx index 83de3ca5..432276ef 100644 --- a/website/src/pages/learn/synths.mdx +++ b/website/src/pages/learn/synths.mdx @@ -23,14 +23,19 @@ The basic waveforms are `sine`, `sawtooth`, `square` and `triangle`, which can b If you don't set a `sound` but a `note` the default value for `sound` is `triangle`! +## Noise + You can also use noise as a source by setting the waveform to: `white`, `pink` or `brown`. These are different flavours of noise, here written from hard to soft. +/2").scope()`} /> + +Here's a more musical example of how to use noise for hihats: + >") -.sound("/2") -.scope()`} + tune={`sound("bd*2,*8") +.decay(.04).sustain(0).scope()`} /> Some amount of pink noise can also be added to any oscillator by using the `noise` paremeter: From 484bb6b11f7c79220cc9bd0c8f5056a88232d90a Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Tue, 3 Oct 2023 10:03:09 +0200 Subject: [PATCH 059/158] refactor synth - separate waveform / noise oscillators - pull noise out of getOscillator - put fm into getOscillator - simplify overall value plumbing --- packages/superdough/synth.mjs | 280 ++++++++++++++++------------------ 1 file changed, 135 insertions(+), 145 deletions(-) diff --git a/packages/superdough/synth.mjs b/packages/superdough/synth.mjs index 51d56993..f12be9c8 100644 --- a/packages/superdough/synth.mjs +++ b/packages/superdough/synth.mjs @@ -20,85 +20,26 @@ const fm = (osc, harmonicityRatio, modulationIndex, wave = 'sine') => { return mod(modfreq, modgain, wave); }; +const waveforms = ['sine', 'square', 'triangle', 'sawtooth']; +const noises = ['pink', 'white', 'brown']; + export function registerSynthSounds() { - ['sine', 'square', 'triangle', 'sawtooth', 'pink', 'white', 'brown'].forEach((wave) => { + [...waveforms, ...noises].forEach((s) => { registerSound( - wave, + s, (t, value, onended) => { // destructure adsr here, because the default should be different for synths and samples - let { - attack = 0.001, - decay = 0.05, - sustain = 0.6, - release = 0.01, - fmh: fmHarmonicity = 1, - fmi: fmModulationIndex, - fmenv: fmEnvelopeType = 'lin', - fmattack: fmAttack, - fmdecay: fmDecay, - fmsustain: fmSustain, - fmrelease: fmRelease, - fmvelocity: fmVelocity, - fmwave: fmWaveform = 'sine', - vib = 0, - vibmod = 0.5, - noise = 0, - } = value; - let { n, note, freq } = value; - // with synths, n and note are the same thing - note = note || 36; - if (typeof note === 'string') { - note = noteToMidi(note); // e.g. c3 => 48 - } - // get frequency - if (!freq && typeof note === 'number') { - freq = midiToFreq(note); // + 48); - } - // maybe pull out the above frequency resolution?? (there is also getFrequency but it has no default) - // make oscillator - const { - node: o, - stop, - dry_node = null, - } = getOscillator({ - t, - s: wave, - freq, - vib, - vibmod, - partials: n, - noise: noise, - }); - // FM + FM envelope - let stopFm, fmEnvelope; - if (fmModulationIndex) { - const { node: modulator, stop } = fm( - dry_node !== null ? dry_node : o, - fmHarmonicity, - fmModulationIndex, - fmWaveform, - ); - if (![fmAttack, fmDecay, fmSustain, fmRelease, fmVelocity].find((v) => v !== undefined)) { - // no envelope by default - modulator.connect(dry_node !== null ? dry_node.frequency : o.frequency); - } else { - fmAttack = fmAttack ?? 0.001; - fmDecay = fmDecay ?? 0.001; - fmSustain = fmSustain ?? 1; - fmRelease = fmRelease ?? 0.001; - fmVelocity = fmVelocity ?? 1; - fmEnvelope = getEnvelope(fmAttack, fmDecay, fmSustain, fmRelease, fmVelocity, t); - if (fmEnvelopeType === 'exp') { - fmEnvelope = getExpEnvelope(fmAttack, fmDecay, fmSustain, fmRelease, fmVelocity, t); - fmEnvelope.node.maxValue = fmModulationIndex * 2; - fmEnvelope.node.minValue = 0.00001; - } - modulator.connect(fmEnvelope.node); - fmEnvelope.node.connect(dry_node !== null ? dry_node.frequency : o.frequency); - } - stopFm = stop; + let { attack = 0.001, decay = 0.05, sustain = 0.6, release = 0.01 } = value; + + let sound; + if (waveforms.includes(s)) { + sound = getOscillator(s, t, value); + } else { + sound = getNoiseOscillator(t, s); } + let { node: o, stop, triggerRelease } = sound; + // turn down const g = gainNode(0.3); @@ -114,10 +55,9 @@ export function registerSynthSounds() { node: o.connect(g).connect(envelope), stop: (releaseTime) => { releaseEnvelope(releaseTime); - fmEnvelope?.stop(releaseTime); + triggerRelease?.(releaseTime); let end = releaseTime + release; stop(end); - stopFm?.(end); }, }; }, @@ -156,7 +96,9 @@ export function waveformN(partials, type) { return osc; } -export function getNoiseOscillator({ t, ac, type = 'white' }) { +// expects one of noises as type +export function getNoiseOscillator(t, type = 'white') { + const ac = getAudioContext(); const bufferSize = 2 * ac.sampleRate; const noiseBuffer = ac.createBuffer(1, bufferSize, ac.sampleRate); const output = noiseBuffer.getChannelData(0); @@ -189,82 +131,130 @@ export function getNoiseOscillator({ t, ac, type = 'white' }) { o.buffer = noiseBuffer; o.loop = true; o.start(t); - return { node: o, stop: (time) => o.stop(time), }; } -export function getOscillator({ s, freq, t, vib, vibmod, partials, noise }) { - // Make oscillator with partial count +// expects one of waveforms as s +export function getOscillator( + s, + t, + { + n: partials, + note, + freq, + vib = 0, + vibmod = 0.5, + noise = 0, + // fm + fmh: fmHarmonicity = 1, + fmi: fmModulationIndex, + fmenv: fmEnvelopeType = 'lin', + fmattack: fmAttack, + fmdecay: fmDecay, + fmsustain: fmSustain, + fmrelease: fmRelease, + fmvelocity: fmVelocity, + fmwave: fmWaveform = 'sine', + }, +) { let ac = getAudioContext(); let o; - - if (['pink', 'white', 'brown'].includes(s)) { - let noiseOscillator = getNoiseOscillator({ t: t, ac: getAudioContext(), type: s }); - return { - node: noiseOscillator.node, - stop: noiseOscillator.stop, - }; - } else { - if (!partials || s === 'sine') { - o = getAudioContext().createOscillator(); - o.type = s || 'triangle'; - } else { - o = waveformN(partials, s); - } - o.frequency.value = Number(freq); - o.start(t); - - // Additional oscillator for vibrato effect - let vibrato_oscillator; - if (vib > 0) { - vibrato_oscillator = getAudioContext().createOscillator(); - vibrato_oscillator.frequency.value = vib; - const gain = getAudioContext().createGain(); - // Vibmod is the amount of vibrato, in semitones - gain.gain.value = vibmod * 100; - vibrato_oscillator.connect(gain); - gain.connect(o.detune); - vibrato_oscillator.start(t); - } - - if (noise > 0) { - // Two gain nodes to set the oscillators to their respective levels - noise = noise > 1 ? 1 : noise; - let o_gain = ac.createGain(); - let n_gain = ac.createGain(); - o_gain.gain.setValueAtTime(1 - noise, ac.currentTime); - n_gain.gain.setValueAtTime(noise, ac.currentTime); - - // Instanciating a mixer to blend sources together - let mix_gain = ac.createGain(); - - // Connecting the main oscillator to the gain node - o.connect(o_gain).connect(mix_gain); - - // Instanciating a noise oscillator and connecting - const noiseOscillator = getNoiseOscillator({ t: t, ac: ac, type: 'pink' }); - noiseOscillator.node.connect(n_gain).connect(mix_gain); - - return { - node: mix_gain, - dry_node: o, - stop: (time) => { - vibrato_oscillator?.stop(time); - o.stop(time); - noiseOscillator.stop(time); - }, - }; - } - - return { - node: o, - stop: (time) => { - vibrato_oscillator?.stop(time); - o.stop(time); - }, - }; + // If no partials are given, use stock waveforms + if (!partials || s === 'sine') { + o = getAudioContext().createOscillator(); + o.type = s || 'triangle'; } + // generate custom waveform if partials are given + else { + o = waveformN(partials, s); + } + + // get frequency from note... + note = note || 36; + if (typeof note === 'string') { + note = noteToMidi(note); // e.g. c3 => 48 + } + // get frequency + if (!freq && typeof note === 'number') { + freq = midiToFreq(note); // + 48); + } + + // set frequency + o.frequency.value = Number(freq); + o.start(t); + + // FM + let stopFm, fmEnvelope; + if (fmModulationIndex) { + const { node: modulator, stop } = fm(o, fmHarmonicity, fmModulationIndex, fmWaveform); + if (![fmAttack, fmDecay, fmSustain, fmRelease, fmVelocity].find((v) => v !== undefined)) { + // no envelope by default + modulator.connect(o.frequency); + } else { + fmAttack = fmAttack ?? 0.001; + fmDecay = fmDecay ?? 0.001; + fmSustain = fmSustain ?? 1; + fmRelease = fmRelease ?? 0.001; + fmVelocity = fmVelocity ?? 1; + fmEnvelope = getEnvelope(fmAttack, fmDecay, fmSustain, fmRelease, fmVelocity, t); + if (fmEnvelopeType === 'exp') { + fmEnvelope = getExpEnvelope(fmAttack, fmDecay, fmSustain, fmRelease, fmVelocity, t); + fmEnvelope.node.maxValue = fmModulationIndex * 2; + fmEnvelope.node.minValue = 0.00001; + } + modulator.connect(fmEnvelope.node); + fmEnvelope.node.connect(o.frequency); + } + stopFm = stop; + } + + // Additional oscillator for vibrato effect + let vibratoOscillator; + if (vib > 0) { + vibratoOscillator = getAudioContext().createOscillator(); + vibratoOscillator.frequency.value = vib; + const gain = getAudioContext().createGain(); + // Vibmod is the amount of vibrato, in semitones + gain.gain.value = vibmod * 100; + vibratoOscillator.connect(gain); + gain.connect(o.detune); + vibratoOscillator.start(t); + } + + let noiseOscillator, noiseMix; + // noise mix + if (noise > 0) { + // Two gain nodes to set the oscillators to their respective levels + noise = noise > 1 ? 1 : noise; + let o_gain = ac.createGain(); + let n_gain = ac.createGain(); + o_gain.gain.setValueAtTime(1 - noise, ac.currentTime); + n_gain.gain.setValueAtTime(noise, ac.currentTime); + + // Instanciating a mixer to blend sources together + noiseMix = ac.createGain(); + + // Connecting the main oscillator to the gain node + o.connect(o_gain).connect(noiseMix); + + // Instanciating a noise oscillator and connecting + noiseOscillator = getNoiseOscillator(t, 'pink'); + noiseOscillator.node.connect(n_gain).connect(noiseMix); + } + + return { + node: noiseMix || o, + stop: (time) => { + vibratoOscillator?.stop(time); + noiseOscillator?.stop(time); + stopFm?.(time); + o.stop(time); + }, + triggerRelease: (time) => { + fmEnvelope?.stop(time); + }, + }; } From 2bc6d0841049aaac6943cec2cdf217905aa1ac13 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Tue, 3 Oct 2023 12:19:30 +0200 Subject: [PATCH 060/158] proper dry wet + pull out noise to extra file --- packages/superdough/helpers.mjs | 22 +++++++++++ packages/superdough/noise.mjs | 51 ++++++++++++++++++++++++ packages/superdough/synth.mjs | 70 ++++----------------------------- 3 files changed, 80 insertions(+), 63 deletions(-) create mode 100644 packages/superdough/noise.mjs diff --git a/packages/superdough/helpers.mjs b/packages/superdough/helpers.mjs index 32ca0bb2..576ec3f1 100644 --- a/packages/superdough/helpers.mjs +++ b/packages/superdough/helpers.mjs @@ -112,3 +112,25 @@ export function createFilter( return filter; } + +// stays 1 until .5, then fades out +let wetfade = (d) => (d < 0.5 ? 1 : 1 - (d - 0.5) / 0.5); + +// mix together dry and wet nodes. 0 = only dry 1 = only wet +// still not too sure about how this could be used more generally... +export function drywet(dry, wet, wetAmount = 0) { + const ac = getAudioContext(); + if (!wetAmount) { + return dry; + } + let dry_gain = ac.createGain(); + let wet_gain = ac.createGain(); + dry.connect(dry_gain); + wet.connect(wet_gain); + dry_gain.gain.value = wetfade(wetAmount); + wet_gain.gain.value = wetfade(1 - wetAmount); + let mix = ac.createGain(); + dry_gain.connect(mix); + wet_gain.connect(mix); + return mix; +} diff --git a/packages/superdough/noise.mjs b/packages/superdough/noise.mjs new file mode 100644 index 00000000..eeb2a9d0 --- /dev/null +++ b/packages/superdough/noise.mjs @@ -0,0 +1,51 @@ +import { drywet } from './helpers.mjs'; + +// expects one of noises as type +export function getNoiseOscillator(type = 'white', t) { + const ac = getAudioContext(); + const bufferSize = 2 * ac.sampleRate; + const noiseBuffer = ac.createBuffer(1, bufferSize, ac.sampleRate); + const output = noiseBuffer.getChannelData(0); + let lastOut = 0; + let b0, b1, b2, b3, b4, b5, b6; + b0 = b1 = b2 = b3 = b4 = b5 = b6 = 0.0; + + for (let i = 0; i < bufferSize; i++) { + if (type === 'white') { + output[i] = Math.random() * 2 - 1; + } else if (type === 'brown') { + let white = Math.random() * 2 - 1; + output[i] = (lastOut + 0.02 * white) / 1.02; + lastOut = output[i]; + } else if (type === 'pink') { + let white = Math.random() * 2 - 1; + b0 = 0.99886 * b0 + white * 0.0555179; + b1 = 0.99332 * b1 + white * 0.0750759; + b2 = 0.969 * b2 + white * 0.153852; + b3 = 0.8665 * b3 + white * 0.3104856; + b4 = 0.55 * b4 + white * 0.5329522; + b5 = -0.7616 * b5 - white * 0.016898; + output[i] = b0 + b1 + b2 + b3 + b4 + b5 + b6 + white * 0.5362; + output[i] *= 0.11; + b6 = white * 0.115926; + } + } + + const o = ac.createBufferSource(); + o.buffer = noiseBuffer; + o.loop = true; + o.start(t); + return { + node: o, + stop: (time) => o.stop(time), + }; +} + +export function getNoiseMix(inputNode, wet, t) { + const noiseOscillator = getNoiseOscillator('pink', t); + const noiseMix = drywet(inputNode, noiseOscillator.node, wet); + return { + node: noiseMix, + stop: (time) => noiseOscillator?.stop(time), + }; +} diff --git a/packages/superdough/synth.mjs b/packages/superdough/synth.mjs index f12be9c8..8c07f34e 100644 --- a/packages/superdough/synth.mjs +++ b/packages/superdough/synth.mjs @@ -1,6 +1,7 @@ import { midiToFreq, noteToMidi } from './util.mjs'; import { registerSound, getAudioContext } from './superdough.mjs'; import { gainNode, getEnvelope, getExpEnvelope } from './helpers.mjs'; +import { getNoiseMix } from './noise.mjs'; const mod = (freq, range = 1, type = 'sine') => { const ctx = getAudioContext(); @@ -35,7 +36,7 @@ export function registerSynthSounds() { if (waveforms.includes(s)) { sound = getOscillator(s, t, value); } else { - sound = getNoiseOscillator(t, s); + sound = getNoiseOscillator(s, t); } let { node: o, stop, triggerRelease } = sound; @@ -96,47 +97,6 @@ export function waveformN(partials, type) { return osc; } -// expects one of noises as type -export function getNoiseOscillator(t, type = 'white') { - const ac = getAudioContext(); - const bufferSize = 2 * ac.sampleRate; - const noiseBuffer = ac.createBuffer(1, bufferSize, ac.sampleRate); - const output = noiseBuffer.getChannelData(0); - let lastOut = 0; - let b0, b1, b2, b3, b4, b5, b6; - b0 = b1 = b2 = b3 = b4 = b5 = b6 = 0.0; - - for (let i = 0; i < bufferSize; i++) { - if (type === 'white') { - output[i] = Math.random() * 2 - 1; - } else if (type === 'brown') { - let white = Math.random() * 2 - 1; - output[i] = (lastOut + 0.02 * white) / 1.02; - lastOut = output[i]; - } else if (type === 'pink') { - let white = Math.random() * 2 - 1; - b0 = 0.99886 * b0 + white * 0.0555179; - b1 = 0.99332 * b1 + white * 0.0750759; - b2 = 0.969 * b2 + white * 0.153852; - b3 = 0.8665 * b3 + white * 0.3104856; - b4 = 0.55 * b4 + white * 0.5329522; - b5 = -0.7616 * b5 - white * 0.016898; - output[i] = b0 + b1 + b2 + b3 + b4 + b5 + b6 + white * 0.5362; - output[i] *= 0.11; - b6 = white * 0.115926; - } - } - - const o = ac.createBufferSource(); - o.buffer = noiseBuffer; - o.loop = true; - o.start(t); - return { - node: o, - stop: (time) => o.stop(time), - }; -} - // expects one of waveforms as s export function getOscillator( s, @@ -224,32 +184,16 @@ export function getOscillator( vibratoOscillator.start(t); } - let noiseOscillator, noiseMix; - // noise mix - if (noise > 0) { - // Two gain nodes to set the oscillators to their respective levels - noise = noise > 1 ? 1 : noise; - let o_gain = ac.createGain(); - let n_gain = ac.createGain(); - o_gain.gain.setValueAtTime(1 - noise, ac.currentTime); - n_gain.gain.setValueAtTime(noise, ac.currentTime); - - // Instanciating a mixer to blend sources together - noiseMix = ac.createGain(); - - // Connecting the main oscillator to the gain node - o.connect(o_gain).connect(noiseMix); - - // Instanciating a noise oscillator and connecting - noiseOscillator = getNoiseOscillator(t, 'pink'); - noiseOscillator.node.connect(n_gain).connect(noiseMix); + let noiseMix; + if (noise) { + noiseMix = getNoiseMix(o, noise, t); } return { - node: noiseMix || o, + node: noiseMix?.node || o, stop: (time) => { vibratoOscillator?.stop(time); - noiseOscillator?.stop(time); + noiseMix?.stop(time); stopFm?.(time); o.stop(time); }, From 4b64168faa0c95b70edac6c6ec17ca178c72278a Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Tue, 3 Oct 2023 12:20:28 +0200 Subject: [PATCH 061/158] fix: imports --- packages/superdough/noise.mjs | 1 + packages/superdough/synth.mjs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/superdough/noise.mjs b/packages/superdough/noise.mjs index eeb2a9d0..0e6c436e 100644 --- a/packages/superdough/noise.mjs +++ b/packages/superdough/noise.mjs @@ -1,4 +1,5 @@ import { drywet } from './helpers.mjs'; +import { getAudioContext } from './superdough.mjs'; // expects one of noises as type export function getNoiseOscillator(type = 'white', t) { diff --git a/packages/superdough/synth.mjs b/packages/superdough/synth.mjs index 8c07f34e..24d1d5ef 100644 --- a/packages/superdough/synth.mjs +++ b/packages/superdough/synth.mjs @@ -1,7 +1,7 @@ import { midiToFreq, noteToMidi } from './util.mjs'; import { registerSound, getAudioContext } from './superdough.mjs'; import { gainNode, getEnvelope, getExpEnvelope } from './helpers.mjs'; -import { getNoiseMix } from './noise.mjs'; +import { getNoiseMix, getNoiseOscillator } from './noise.mjs'; const mod = (freq, range = 1, type = 'sine') => { const ctx = getAudioContext(); From 047129223e62a0d161083853bcfe84a599e4ccc0 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Tue, 3 Oct 2023 12:25:47 +0200 Subject: [PATCH 062/158] cache noise --- packages/superdough/noise.mjs | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/packages/superdough/noise.mjs b/packages/superdough/noise.mjs index 0e6c436e..2c8c1d4a 100644 --- a/packages/superdough/noise.mjs +++ b/packages/superdough/noise.mjs @@ -1,9 +1,14 @@ import { drywet } from './helpers.mjs'; import { getAudioContext } from './superdough.mjs'; -// expects one of noises as type -export function getNoiseOscillator(type = 'white', t) { +let noiseCache = {}; + +// lazy generates noise buffers and keeps them forever +function getNoiseBuffer(type) { const ac = getAudioContext(); + if (noiseCache[type]) { + return noiseCache[type]; + } const bufferSize = 2 * ac.sampleRate; const noiseBuffer = ac.createBuffer(1, bufferSize, ac.sampleRate); const output = noiseBuffer.getChannelData(0); @@ -31,9 +36,15 @@ export function getNoiseOscillator(type = 'white', t) { b6 = white * 0.115926; } } + noiseCache[type] = noiseBuffer; + return noiseBuffer; +} +// expects one of noises as type +export function getNoiseOscillator(type = 'white', t) { + const ac = getAudioContext(); const o = ac.createBufferSource(); - o.buffer = noiseBuffer; + o.buffer = getNoiseBuffer(type); o.loop = true; o.start(t); return { From 376cf09565bb9c63a08c4eef43a6909186f00b29 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Tue, 3 Oct 2023 12:41:57 +0200 Subject: [PATCH 063/158] rename zzfx noise to znoise --- packages/core/controls.mjs | 11 ++++++++++- packages/superdough/zzfx.mjs | 4 ++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/packages/core/controls.mjs b/packages/core/controls.mjs index 6cac6e54..39c2df2f 100644 --- a/packages/core/controls.mjs +++ b/packages/core/controls.mjs @@ -655,6 +655,15 @@ const generic_params = [ * .vib("<.5 1 2 4 8 16>:12") */ [['vib', 'vibmod'], 'vibrato', 'v'], + /** + * Adds pink noise to the mix + * + * @name noise + * @param {number | Pattern} wet wet amount + * @example + * sound("/2") + */ + ['noise'], /** * Sets the vibrato depth in semitones. Only has an effect if `vibrato` | `vib` | `v` is is also set * @@ -1153,7 +1162,7 @@ const generic_params = [ ['pitchJump'], ['pitchJumpTime'], ['lfo', 'repeatTime'], - ['noise'], + ['znoise'], // noise on the frequency or as bubo calls it "frequency fog" :) ['zmod'], ['zcrush'], // like crush but scaled differently ['zdelay'], diff --git a/packages/superdough/zzfx.mjs b/packages/superdough/zzfx.mjs index da505d74..a6af8260 100644 --- a/packages/superdough/zzfx.mjs +++ b/packages/superdough/zzfx.mjs @@ -20,7 +20,7 @@ export const getZZFX = (value, t) => { pitchJump = 0, pitchJumpTime = 0, lfo = 0, - noise = 0, + znoise = 0, zmod = 0, zcrush = 0, zdelay = 0, @@ -54,7 +54,7 @@ export const getZZFX = (value, t) => { pitchJump, pitchJumpTime, lfo, - noise, + znoise, zmod, zcrush, zdelay, From 9c9323e04099db415645d95a24e6969d3c21d113 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Tue, 3 Oct 2023 12:42:56 +0200 Subject: [PATCH 064/158] snapshot --- test/__snapshots__/examples.test.mjs.snap | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/__snapshots__/examples.test.mjs.snap b/test/__snapshots__/examples.test.mjs.snap index e026f9c4..4a4c966d 100644 --- a/test/__snapshots__/examples.test.mjs.snap +++ b/test/__snapshots__/examples.test.mjs.snap @@ -2959,6 +2959,15 @@ exports[`runs examples > example "never" example index 0 1`] = ` ] `; +exports[`runs examples > example "noise" example index 0 1`] = ` +[ + "[ (0/1 → 1/1) ⇝ 2/1 | s:white ]", + "[ 0/1 ⇜ (1/1 → 2/1) | s:white ]", + "[ (2/1 → 3/1) ⇝ 4/1 | s:pink ]", + "[ 2/1 ⇜ (3/1 → 4/1) | s:pink ]", +] +`; + exports[`runs examples > example "note" example index 0 1`] = ` [ "[ 0/1 → 1/4 | note:c ]", From 020e85906d9ca9bb304eaedd2ae7814319eaf94b Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Wed, 4 Oct 2023 09:42:10 +0200 Subject: [PATCH 065/158] fix: slider crash on some platforms --- packages/codemirror/slider.mjs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/codemirror/slider.mjs b/packages/codemirror/slider.mjs index 62ec33c2..519e5610 100644 --- a/packages/codemirror/slider.mjs +++ b/packages/codemirror/slider.mjs @@ -91,8 +91,10 @@ export const sliderPlugin = ViewPlugin.fromClass( while (iterator.value) { // when the widgets are moved, we need to tell the dom node the current position // this is important because the updateSliderValue function has to work with the dom node - iterator.value.widget.slider.from = iterator.from; - iterator.value.widget.slider.to = iterator.to; + if (iterator.value?.widget?.slider) { + iterator.value.widget.slider.from = iterator.from; + iterator.value.widget.slider.to = iterator.to; + } iterator.next(); } } From 508af7eb72dfd2a56f3bad50355df987e0b75094 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Wed, 4 Oct 2023 23:48:53 +0200 Subject: [PATCH 066/158] update internal reverb param names --- packages/superdough/superdough.mjs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index e387ff6c..1e26cc9d 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -108,11 +108,11 @@ function getDelay(orbit, delaytime, delayfeedback, t) { let reverbs = {}; -function getReverb(orbit, duration = 2, fade, revlp, revdim) { +function getReverb(orbit, duration = 2, fade, lp, dim) { // If no reverb has been created for a given orbit, create one if (!reverbs[orbit]) { const ac = getAudioContext(); - const reverb = ac.createReverb(getAudioContext(), duration, fade, revlp, revdim); + const reverb = ac.createReverb(getAudioContext(), duration, fade, lp, dim); reverb.connect(getDestination()); reverbs[orbit] = reverb; } @@ -120,14 +120,14 @@ function getReverb(orbit, duration = 2, fade, revlp, revdim) { if ( reverbs[orbit].duration !== duration || reverbs[orbit].fade !== fade || - reverbs[orbit].revlp !== revlp || - reverbs[orbit].revdim !== revdim + reverbs[orbit].lp !== lp || + reverbs[orbit].dim !== dim ) { - reverbs[orbit].setDuration(duration, fade, revlp, revdim); + reverbs[orbit].setDuration(duration, fade, lp, dim); reverbs[orbit].duration = duration; reverbs[orbit].fade = fade; - reverbs[orbit].revlp = revlp; - reverbs[orbit].revdim = revdim; + reverbs[orbit].lp = lp; + reverbs[orbit].dim = dim; } return reverbs[orbit]; From ce842f75612202b916c838a26e1fa7dd58ab3c35 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Wed, 4 Oct 2023 23:56:00 +0200 Subject: [PATCH 067/158] simplify createReverb --- packages/superdough/reverb.mjs | 4 ++-- packages/superdough/superdough.mjs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/superdough/reverb.mjs b/packages/superdough/reverb.mjs index 7151e7fc..52d6983c 100644 --- a/packages/superdough/reverb.mjs +++ b/packages/superdough/reverb.mjs @@ -2,12 +2,12 @@ import reverbGen from './reverbGen.mjs'; if (typeof AudioContext !== 'undefined') { AudioContext.prototype.generateReverb = reverbGen.generateReverb; - AudioContext.prototype.createReverb = function (audioContext, duration, fade, revlp, revdim) { + AudioContext.prototype.createReverb = function (duration, fade, revlp, revdim) { const convolver = this.createConvolver(); convolver.setDuration = (d, fade, revlp, revdim) => { this.generateReverb( { - audioContext, + audioContext: this, sampleRate: 44100, numChannels: 2, decayTime: d, diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index 1e26cc9d..0f2b3dd0 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -112,7 +112,7 @@ function getReverb(orbit, duration = 2, fade, lp, dim) { // If no reverb has been created for a given orbit, create one if (!reverbs[orbit]) { const ac = getAudioContext(); - const reverb = ac.createReverb(getAudioContext(), duration, fade, lp, dim); + const reverb = ac.createReverb(duration, fade, lp, dim); reverb.connect(getDestination()); reverbs[orbit] = reverb; } From 3c4f835d8b6fb8f520c7302bff4b83d5bd2dfe5d Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Thu, 5 Oct 2023 00:00:47 +0200 Subject: [PATCH 068/158] consistent naming + simplify --- packages/superdough/reverb.mjs | 14 +++++++------- packages/superdough/superdough.mjs | 6 +----- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/packages/superdough/reverb.mjs b/packages/superdough/reverb.mjs index 52d6983c..de6c903e 100644 --- a/packages/superdough/reverb.mjs +++ b/packages/superdough/reverb.mjs @@ -2,9 +2,9 @@ import reverbGen from './reverbGen.mjs'; if (typeof AudioContext !== 'undefined') { AudioContext.prototype.generateReverb = reverbGen.generateReverb; - AudioContext.prototype.createReverb = function (duration, fade, revlp, revdim) { + AudioContext.prototype.createReverb = function (duration, fade, lp, dim) { const convolver = this.createConvolver(); - convolver.setDuration = (d, fade, revlp, revdim) => { + convolver.generate = (d, fade, lp, dim) => { this.generateReverb( { audioContext: this, @@ -12,8 +12,8 @@ if (typeof AudioContext !== 'undefined') { numChannels: 2, decayTime: d, fadeInTime: fade, - lpFreqStart: revlp, - lpFreqEnd: revdim, + lpFreqStart: lp, + lpFreqEnd: dim, }, (buffer) => { convolver.buffer = buffer; @@ -21,10 +21,10 @@ if (typeof AudioContext !== 'undefined') { ); convolver.duration = d; convolver.fade = fade; - convolver.revlp = revlp; - convolver.revdim = revdim; + convolver.lp = lp; + convolver.dim = dim; }; - convolver.setDuration(duration, fade, revlp, revdim); + convolver.generate(duration, fade, lp, dim); return convolver; }; } diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index 0f2b3dd0..ea8afc58 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -123,11 +123,7 @@ function getReverb(orbit, duration = 2, fade, lp, dim) { reverbs[orbit].lp !== lp || reverbs[orbit].dim !== dim ) { - reverbs[orbit].setDuration(duration, fade, lp, dim); - reverbs[orbit].duration = duration; - reverbs[orbit].fade = fade; - reverbs[orbit].lp = lp; - reverbs[orbit].dim = dim; + reverbs[orbit].generate(duration, fade, lp, dim); } return reverbs[orbit]; From 75099abbc125908df97ee80a12ede3abda5e1ee3 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Thu, 5 Oct 2023 00:03:02 +0200 Subject: [PATCH 069/158] remove unused reverb method --- packages/superdough/reverbGen.mjs | 76 ------------------------------- 1 file changed, 76 deletions(-) diff --git a/packages/superdough/reverbGen.mjs b/packages/superdough/reverbGen.mjs index cac5d24a..49429937 100644 --- a/packages/superdough/reverbGen.mjs +++ b/packages/superdough/reverbGen.mjs @@ -70,82 +70,6 @@ reverbGen.generateGraph = function (data, width, height, min, max) { return canvas; }; -/** Saves an AudioBuffer as a 16-bit WAV file on the client's host - file system. Normalizes it to peak at +-32767, and optionally - truncates it if there's a lot of "silence" at the end. - - @param {!AudioBuffer} buffer The buffer to save. - @param {string} name Name of file to create. - @param {number?} opt_minTail Defines what counts as "silence" for - auto-truncating the buffer. If there is a point past which every - value of every channel is less than opt_minTail, then the buffer - is truncated at that point. This is expressed as an integer, - applying to the post-normalized and integer-converted - buffer. The default is 0, meaning don't truncate. */ -reverbGen.saveWavFile = function (buffer, name, opt_minTail) { - var bitsPerSample = 16; - var bytesPerSample = 2; - var sampleRate = buffer.sampleRate; - var numChannels = buffer.numberOfChannels; - var channels = getAllChannelData(buffer); - var numSampleFrames = channels[0].length; - var scale = 32767; - // Find normalization constant. - var max = 0; - for (var i = 0; i < numChannels; i++) { - for (var j = 0; j < numSampleFrames; j++) { - max = Math.max(max, Math.abs(channels[i][j])); - } - } - if (max) { - scale = 32767 / max; - } - // Find truncation point. - if (opt_minTail) { - var truncateAt = 0; - for (var i = 0; i < numChannels; i++) { - for (var j = 0; j < numSampleFrames; j++) { - var absSample = Math.abs(Math.round(scale * channels[i][j])); - if (absSample > opt_minTail) { - truncateAt = j; - } - } - } - numSampleFrames = truncateAt + 1; - } - var sampleDataBytes = bytesPerSample * numChannels * numSampleFrames; - var fileBytes = sampleDataBytes + 44; - var arrayBuffer = new ArrayBuffer(fileBytes); - var dataView = new DataView(arrayBuffer); - dataView.setUint32(0, 1179011410, true); // "RIFF" - dataView.setUint32(4, fileBytes - 8, true); // file length - dataView.setUint32(8, 1163280727, true); // "WAVE" - dataView.setUint32(12, 544501094, true); // "fmt " - dataView.setUint32(16, 16, true); // fmt chunk length - dataView.setUint16(20, 1, true); // PCM format - dataView.setUint16(22, numChannels, true); // NumChannels - dataView.setUint32(24, sampleRate, true); // SampleRate - var bytesPerSampleFrame = numChannels * bytesPerSample; - dataView.setUint32(28, sampleRate * bytesPerSampleFrame, true); // ByteRate - dataView.setUint16(32, bytesPerSampleFrame, true); // BlockAlign - dataView.setUint16(34, bitsPerSample, true); // BitsPerSample - dataView.setUint32(36, 1635017060, true); // "data" - dataView.setUint32(40, sampleDataBytes, true); - for (var j = 0; j < numSampleFrames; j++) { - for (var i = 0; i < numChannels; i++) { - dataView.setInt16(44 + j * bytesPerSampleFrame + i * bytesPerSample, Math.round(scale * channels[i][j]), true); - } - } - var blob = new Blob([arrayBuffer], { type: 'audio/wav' }); - var url = window.URL.createObjectURL(blob); - var linkEl = document.createElement('a'); - linkEl.href = url; - linkEl.download = name; - linkEl.style.display = 'none'; - document.body.appendChild(linkEl); - linkEl.click(); -}; - /** Applies a constantly changing lowpass filter to the given sound. @private From d6c8838fe26db2bf3b5db75c59145952e544f1ad Mon Sep 17 00:00:00 2001 From: Vasilii Milovidov Date: Thu, 5 Oct 2023 03:49:50 +0400 Subject: [PATCH 070/158] Integrate with the new impulse generation functionality --- packages/core/controls.mjs | 71 +++++++++++++++-------------- packages/superdough/reverb.mjs | 34 ++++++-------- packages/superdough/superdough.mjs | 37 +++++++-------- website/src/pages/learn/effects.mdx | 28 +++++++----- 4 files changed, 86 insertions(+), 84 deletions(-) diff --git a/packages/core/controls.mjs b/packages/core/controls.mjs index d909423f..e23114ca 100644 --- a/packages/core/controls.mjs +++ b/packages/core/controls.mjs @@ -655,6 +655,15 @@ const generic_params = [ * .vib("<.5 1 2 4 8 16>:12") */ [['vib', 'vibmod'], 'vibrato', 'v'], + /** + * Adds pink noise to the mix + * + * @name noise + * @param {number | Pattern} wet wet amount + * @example + * sound("/2") + */ + ['noise'], /** * Sets the vibrato depth in semitones. Only has an effect if `vibrato` | `vib` | `v` is is also set * @@ -970,56 +979,50 @@ const generic_params = [ * */ [['room', 'size']], - /** - * Sets the room size of the reverb, see {@link room}. - * - * @name roomsize - * @param {number | Pattern} size between 0 and 10 - * @synonyms size, sz - * @example - * s("bd sd").room(.8).roomsize("<0 1 2 4 8>") - * - */ /** * Reverb lowpass starting frequency (in hertz). + * When this property is changed, the reverb will be recaculated, so only change this sparsely.. * - * @name revlp - * @param {number} level between 0 and 20000hz + * @name roomlp + * @synonyms rlp + * @param {number} frequency between 0 and 20000hz * @example - * s("bd sd").room(0.5).revlp(10000) + * s("bd sd").room(0.5).rlp(10000) * @example - * s("bd sd").room(0.5).revlp(5000) + * s("bd sd").room(0.5).rlp(5000) */ - ['revlp'], + ['roomlp', 'rlp'], /** * Reverb lowpass frequency at -60dB (in hertz). + * When this property is changed, the reverb will be recaculated, so only change this sparsely.. * - * @name revdim - * @param {number} level between 0 and 20000hz + * @name roomdim + * @synonyms rdim + * @param {number} frequency between 0 and 20000hz * @example - * s("bd sd").room(0.5).revlp(10000).revdim(8000) + * s("bd sd").room(0.5).rlp(10000).rdim(8000) * @example - * s("bd sd").room(0.5).revlp(5000).revdim(400) + * s("bd sd").room(0.5).rlp(5000).rdim(400) * */ - ['revdim'], + ['roomdim', 'rdim'], /** * Reverb fade time (in seconds). + * When this property is changed, the reverb will be recaculated, so only change this sparsely.. * - * @name fade + * @name roomfade + * @synonyms rfade * @param {number} seconds for the reverb to fade * @example - * s("bd sd").room(0.5).revlp(10000).fade(0.5) + * s("bd sd").room(0.5).rlp(10000).rfade(0.5) * @example - * s("bd sd").room(0.5).revlp(5000).fade(4) + * s("bd sd").room(0.5).rlp(5000).rfade(4) * */ - ['fade'], + ['roomfade', 'rfade'], /** - * Sets the sample to use as an impulse response for the reverb. - * - * @name iresponse - * @param {string | Pattern} sample sample to pick as an impulse response + * Sets the sample to use as an impulse response for the reverb. * * @name iresponse + * @param {string | Pattern} sample to use as an impulse response * @synonyms ir * @example * s("bd sd").room(.8).ir("") @@ -1028,23 +1031,25 @@ const generic_params = [ [['ir', 'i'], 'iresponse'], /** * Sets the room size of the reverb, see {@link room}. + * When this property is changed, the reverb will be recaculated, so only change this sparsely.. * * @name roomsize * @param {number | Pattern} size between 0 and 10 - * @synonyms size, sz + * @synonyms rsize, sz, size * @example - * s("bd sd").room(.8).roomsize("<0 1 2 4 8>") + * s("bd sd").room(.8).rsize(1) + * @example + * s("bd sd").room(.8).rsize(4) * */ // TODO: find out why : // s("bd sd").room(.8).roomsize("<0 .2 .4 .6 .8 [1,0]>").osc() // .. does not work. Is it because room is only one effect? - ['size', 'sz', 'roomsize'], + ['roomsize', 'size', 'sz', 'rsize'], // ['sagogo'], // ['sclap'], // ['sclaves'], // ['scrash'], - /** * Wave shaping distortion. CAUTION: it might get loud * @@ -1210,7 +1215,7 @@ const generic_params = [ ['pitchJump'], ['pitchJumpTime'], ['lfo', 'repeatTime'], - ['noise'], + ['znoise'], // noise on the frequency or as bubo calls it "frequency fog" :) ['zmod'], ['zcrush'], // like crush but scaled differently ['zdelay'], diff --git a/packages/superdough/reverb.mjs b/packages/superdough/reverb.mjs index 5e4ce8f1..85be19ae 100644 --- a/packages/superdough/reverb.mjs +++ b/packages/superdough/reverb.mjs @@ -17,47 +17,43 @@ if (typeof AudioContext !== 'undefined') { return newBuffer; }; - AudioContext.prototype.createReverb = function (audioContext, duration, fade, revlp, revdim, imp) { + AudioContext.prototype.createReverb = function (duration, fade, lp, dim, ir) { const convolver = this.createConvolver(); - - convolver.setDuration = (d, fade, revlp, revdim, imp) => { - if (imp) { - convolver.buffer = this.adjustLength(d, imp); + convolver.generate = (d, fade, lp, dim, buf) => { + if (buf) { + convolver.buffer = this.adjustLength(d, buf); return convolver; } else { this.generateReverb( { - audioContext, + audioContext: this, sampleRate: 44100, numChannels: 2, decayTime: d, fadeInTime: fade, - lpFreqStart: revlp, - lpFreqEnd: revdim, + lpFreqStart: lp, + lpFreqEnd: dim, }, (buffer) => { convolver.buffer = buffer; }, ); - convolver.duration = duration; + convolver.duration = d; convolver.fade = fade; - convolver.revlp = revlp; - convolver.revdim = revdim; + convolver.lp = lp; + convolver.dim = dim; return convolver; } }; - convolver.setIR = (d, fade, revlp, revdim, imp) => { - if (imp) { - convolver.buffer = this.adjustLength(d, imp); + convolver.setIR = (d, fade, lp, dim, buf) => { + if (buf) { + convolver.buffer = this.adjustLength(d, buf); } else { - convolver.setDuration(d, fade, revlp, revdim, imp); + convolver.generate(d, fade, lp, dim, buf); } return convolver; }; - convolver.setDuration(duration, fade, revlp, revdim, imp); + convolver.generate(duration, fade, lp, dim, ir); return convolver; }; } - -// TODO: make the reverb more exciting -// check out https://blog.gskinner.com/archives/2019/02/reverb-web-audio-api.html diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index ec3b981e..d7f0b14b 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -114,32 +114,29 @@ function getDelay(orbit, delaytime, delayfeedback, t) { let reverbs = {}; -function getReverb(orbit, duration = 2, fade, revlp, revdim, imp) { +function getReverb(orbit, duration = 2, fade, lp, dim, ir) { if (!reverbs[orbit]) { const ac = getAudioContext(); - const reverb = ac.createReverb(getAudioContext(), duration, fade, revlp, revdim, imp); + const reverb = ac.createReverb(duration, fade, lp, dim, ir); reverb.connect(getDestination()); reverbs[orbit] = reverb; } - const reverbOrbit = reverbs[orbit]; - if ( reverbs[orbit].duration !== duration || reverbs[orbit].fade !== fade || - reverbs[orbit].revlp !== revlp || - reverbs[orbit].revdim !== revdim + reverbs[orbit].ir !== lp || + reverbs[orbit].dim !== dim || + reverbs[orbit].ir !== ir ) { - reverbs[orbit].setDuration(duration, fade, revlp, revdim); - reverbs[orbit].duration = duration; - reverbs[orbit].fade = fade; - reverbs[orbit].revlp = revlp; - reverbs[orbit].revdim = revdim; + reverbs[orbit].generate(duration, fade, lp, dim, ir); } - if (reverbs[orbit].ir !== imp) { - reverbs[orbit] = reverbs[orbit].setIR(duration, fade, revlp, revdim, imp); - reverbs[orbit].ir = imp; + + if (reverbs[orbit].ir !== ir) { + reverbs[orbit] = reverbs[orbit].setIR(duration, fade, lp, dim, ir); + reverbs[orbit].ir = ir; } + return reverbs[orbit]; } @@ -238,10 +235,10 @@ export const superdough = async (value, deadline, hapDuration) => { delaytime = 0.25, orbit = 1, room, - fade = 0.1, - revlp = 15000, - revdim = 1000, - size = 2, + roomfade = 0.1, + roomlp = 15000, + roomdim = 1000, + roomsize = 2, ir, i = 0, velocity = 1, @@ -393,8 +390,8 @@ export const superdough = async (value, deadline, hapDuration) => { buffer = await loadBuffer(url, ac, ir, 0); } let reverbSend; - if (room > 0 && size > 0) { - const reverbNode = getReverb(orbit, size, fade, revlp, revdim, buffer); + if (room > 0 && roomsize > 0) { + const reverbNode = getReverb(orbit, roomsize, roomfade, roomlp, roomdim, buffer); reverbSend = effectSend(post, reverbNode, room); } diff --git a/website/src/pages/learn/effects.mdx b/website/src/pages/learn/effects.mdx index 3ca43f69..35052788 100644 --- a/website/src/pages/learn/effects.mdx +++ b/website/src/pages/learn/effects.mdx @@ -183,39 +183,43 @@ global effects use the same chain for all events of the same orbit: -## delay +## Delay + +### delay -## delaytime +### delaytime -## delayfeedback +### delayfeedback -## room +## Reverb + +### room -## roomsize +### roomsize -## fade +### roomfade - + -## revlp +### roomlp - + -## revdim +### roomdim - + -## iresponse +### iresponse From 1d4eb59e942687313c5f14c03ac771618379c5ac Mon Sep 17 00:00:00 2001 From: Vasilii Milovidov Date: Thu, 5 Oct 2023 04:10:35 +0400 Subject: [PATCH 071/158] fix: reverbGen.mjs --- packages/superdough/reverbGen.mjs | 78 ------------------------------- 1 file changed, 78 deletions(-) diff --git a/packages/superdough/reverbGen.mjs b/packages/superdough/reverbGen.mjs index 9fb46173..d8024ccb 100644 --- a/packages/superdough/reverbGen.mjs +++ b/packages/superdough/reverbGen.mjs @@ -12,8 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -'use strict'; - var reverbGen = {}; /** Generates a reverb impulse response. @@ -72,82 +70,6 @@ reverbGen.generateGraph = function (data, width, height, min, max) { return canvas; }; -/** Saves an AudioBuffer as a 16-bit WAV file on the client's host - file system. Normalizes it to peak at +-32767, and optionally - truncates it if there's a lot of "silence" at the end. - - @param {!AudioBuffer} buffer The buffer to save. - @param {string} name Name of file to create. - @param {number?} opt_minTail Defines what counts as "silence" for - auto-truncating the buffer. If there is a point past which every - value of every channel is less than opt_minTail, then the buffer - is truncated at that point. This is expressed as an integer, - applying to the post-normalized and integer-converted - buffer. The default is 0, meaning don't truncate. */ -reverbGen.saveWavFile = function (buffer, name, opt_minTail) { - var bitsPerSample = 16; - var bytesPerSample = 2; - var sampleRate = buffer.sampleRate; - var numChannels = buffer.numberOfChannels; - var channels = getAllChannelData(buffer); - var numSampleFrames = channels[0].length; - var scale = 32767; - // Find normalization constant. - var max = 0; - for (var i = 0; i < numChannels; i++) { - for (var j = 0; j < numSampleFrames; j++) { - max = Math.max(max, Math.abs(channels[i][j])); - } - } - if (max) { - scale = 32767 / max; - } - // Find truncation point. - if (opt_minTail) { - var truncateAt = 0; - for (var i = 0; i < numChannels; i++) { - for (var j = 0; j < numSampleFrames; j++) { - var absSample = Math.abs(Math.round(scale * channels[i][j])); - if (absSample > opt_minTail) { - truncateAt = j; - } - } - } - numSampleFrames = truncateAt + 1; - } - var sampleDataBytes = bytesPerSample * numChannels * numSampleFrames; - var fileBytes = sampleDataBytes + 44; - var arrayBuffer = new ArrayBuffer(fileBytes); - var dataView = new DataView(arrayBuffer); - dataView.setUint32(0, 1179011410, true); // "RIFF" - dataView.setUint32(4, fileBytes - 8, true); // file length - dataView.setUint32(8, 1163280727, true); // "WAVE" - dataView.setUint32(12, 544501094, true); // "fmt " - dataView.setUint32(16, 16, true); // fmt chunk length - dataView.setUint16(20, 1, true); // PCM format - dataView.setUint16(22, numChannels, true); // NumChannels - dataView.setUint32(24, sampleRate, true); // SampleRate - var bytesPerSampleFrame = numChannels * bytesPerSample; - dataView.setUint32(28, sampleRate * bytesPerSampleFrame, true); // ByteRate - dataView.setUint16(32, bytesPerSampleFrame, true); // BlockAlign - dataView.setUint16(34, bitsPerSample, true); // BitsPerSample - dataView.setUint32(36, 1635017060, true); // "data" - dataView.setUint32(40, sampleDataBytes, true); - for (var j = 0; j < numSampleFrames; j++) { - for (var i = 0; i < numChannels; i++) { - dataView.setInt16(44 + j * bytesPerSampleFrame + i * bytesPerSample, Math.round(scale * channels[i][j]), true); - } - } - var blob = new Blob([arrayBuffer], { type: 'audio/wav' }); - var url = window.URL.createObjectURL(blob); - var linkEl = document.createElement('a'); - linkEl.href = url; - linkEl.download = name; - linkEl.style.display = 'none'; - document.body.appendChild(linkEl); - linkEl.click(); -}; - /** Applies a constantly changing lowpass filter to the given sound. @private From 2f0798de3267556f275598d84c38a86d35d6cf7d Mon Sep 17 00:00:00 2001 From: Vasilii Milovidov Date: Thu, 5 Oct 2023 13:17:17 +0400 Subject: [PATCH 072/158] fix: formatting --- packages/superdough/superdough.mjs | 1 - website/src/pages/learn/effects.mdx | 1 - 2 files changed, 2 deletions(-) diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index d74e3182..d7f0b14b 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -114,7 +114,6 @@ function getDelay(orbit, delaytime, delayfeedback, t) { let reverbs = {}; - function getReverb(orbit, duration = 2, fade, lp, dim, ir) { if (!reverbs[orbit]) { const ac = getAudioContext(); diff --git a/website/src/pages/learn/effects.mdx b/website/src/pages/learn/effects.mdx index e2a0de8b..35052788 100644 --- a/website/src/pages/learn/effects.mdx +++ b/website/src/pages/learn/effects.mdx @@ -219,7 +219,6 @@ global effects use the same chain for all events of the same orbit: - ### iresponse From 4718bfac25e323997eaa5bf8e5019563764520bb Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Sat, 7 Oct 2023 00:22:55 +0200 Subject: [PATCH 073/158] fix: reverb regenerate loophole --- packages/superdough/reverb.mjs | 2 +- packages/superdough/superdough.mjs | 25 ++++++++++++++++--------- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/packages/superdough/reverb.mjs b/packages/superdough/reverb.mjs index de6c903e..3d54203c 100644 --- a/packages/superdough/reverb.mjs +++ b/packages/superdough/reverb.mjs @@ -4,7 +4,7 @@ if (typeof AudioContext !== 'undefined') { AudioContext.prototype.generateReverb = reverbGen.generateReverb; AudioContext.prototype.createReverb = function (duration, fade, lp, dim) { const convolver = this.createConvolver(); - convolver.generate = (d, fade, lp, dim) => { + convolver.generate = (d = 2, fade = 0.1, lp = 15000, dim = 1000) => { this.generateReverb( { audioContext: this, diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index ea8afc58..730e495d 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -108,7 +108,9 @@ function getDelay(orbit, delaytime, delayfeedback, t) { let reverbs = {}; -function getReverb(orbit, duration = 2, fade, lp, dim) { +let hasChanged = (now, before) => now !== undefined && now !== before; + +function getReverb(orbit, duration, fade, lp, dim) { // If no reverb has been created for a given orbit, create one if (!reverbs[orbit]) { const ac = getAudioContext(); @@ -118,11 +120,16 @@ function getReverb(orbit, duration = 2, fade, lp, dim) { } if ( - reverbs[orbit].duration !== duration || - reverbs[orbit].fade !== fade || - reverbs[orbit].lp !== lp || - reverbs[orbit].dim !== dim + hasChanged(duration, reverbs[orbit].duration) || + hasChanged(fade, reverbs[orbit].fade) || + hasChanged(lp, reverbs[orbit].lp) || + hasChanged(dim, reverbs[orbit].dim) ) { + // only regenerate when something has changed + // avoids endless regeneration on things like + // stack(s("a"), s("b").rsize(8)).room(.5) + // this only works when args may stay undefined until here + // setting default values breaks this reverbs[orbit].generate(duration, fade, lp, dim); } @@ -223,10 +230,10 @@ export const superdough = async (value, deadline, hapDuration) => { delaytime = 0.25, orbit = 1, room, - roomfade = 0.1, - roomlp = 15000, - roomdim = 1000, - roomsize = 2, + roomfade, + roomlp, + roomdim, + roomsize, velocity = 1, analyze, // analyser wet fft = 8, // fftSize 0 - 10 From b297888eb414c0be157191713cca7c47f443ecee Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Sat, 7 Oct 2023 15:40:08 +0200 Subject: [PATCH 074/158] consume n with scale --- packages/tonal/tonal.mjs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/tonal/tonal.mjs b/packages/tonal/tonal.mjs index 3fd8a5da..99a11c53 100644 --- a/packages/tonal/tonal.mjs +++ b/packages/tonal/tonal.mjs @@ -150,6 +150,9 @@ export const scale = register('scale', function (scale, pat) { return pat.withHap((hap) => { const isObject = typeof hap.value === 'object'; let note = isObject ? hap.value.n : hap.value; + if (isObject) { + delete hap.value.n; // remove n so it won't cause trouble + } const asNumber = Number(note); if (!isNaN(asNumber)) { // TODO: worth keeping for supporting ':' in (non-mininotation) strings? From 296cd83a556635181e021260c5160447901c992c Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Sat, 7 Oct 2023 15:45:44 +0200 Subject: [PATCH 075/158] snapshot --- test/__snapshots__/examples.test.mjs.snap | 160 +- test/__snapshots__/tunes.test.mjs.snap | 3110 ++++++++++----------- 2 files changed, 1635 insertions(+), 1635 deletions(-) diff --git a/test/__snapshots__/examples.test.mjs.snap b/test/__snapshots__/examples.test.mjs.snap index f2bc7039..18370903 100644 --- a/test/__snapshots__/examples.test.mjs.snap +++ b/test/__snapshots__/examples.test.mjs.snap @@ -3942,96 +3942,96 @@ exports[`runs examples > example "saw" example index 1 1`] = ` exports[`runs examples > example "scale" example index 0 1`] = ` [ - "[ 0/1 → 1/6 | n:0 note:C3 ]", - "[ 1/6 → 1/3 | n:2 note:E3 ]", - "[ 1/3 → 1/2 | n:4 note:G3 ]", - "[ 1/2 → 2/3 | n:6 note:B3 ]", - "[ 2/3 → 5/6 | n:4 note:G3 ]", - "[ 5/6 → 1/1 | n:2 note:E3 ]", - "[ 1/1 → 7/6 | n:0 note:C3 ]", - "[ 7/6 → 4/3 | n:2 note:E3 ]", - "[ 4/3 → 3/2 | n:4 note:G3 ]", - "[ 3/2 → 5/3 | n:6 note:B3 ]", - "[ 5/3 → 11/6 | n:4 note:G3 ]", - "[ 11/6 → 2/1 | n:2 note:E3 ]", - "[ 2/1 → 13/6 | n:0 note:C3 ]", - "[ 13/6 → 7/3 | n:2 note:E3 ]", - "[ 7/3 → 5/2 | n:4 note:G3 ]", - "[ 5/2 → 8/3 | n:6 note:B3 ]", - "[ 8/3 → 17/6 | n:4 note:G3 ]", - "[ 17/6 → 3/1 | n:2 note:E3 ]", - "[ 3/1 → 19/6 | n:0 note:C3 ]", - "[ 19/6 → 10/3 | n:2 note:E3 ]", - "[ 10/3 → 7/2 | n:4 note:G3 ]", - "[ 7/2 → 11/3 | n:6 note:B3 ]", - "[ 11/3 → 23/6 | n:4 note:G3 ]", - "[ 23/6 → 4/1 | n:2 note:E3 ]", + "[ 0/1 → 1/6 | note:C3 ]", + "[ 1/6 → 1/3 | note:E3 ]", + "[ 1/3 → 1/2 | note:G3 ]", + "[ 1/2 → 2/3 | note:B3 ]", + "[ 2/3 → 5/6 | note:G3 ]", + "[ 5/6 → 1/1 | note:E3 ]", + "[ 1/1 → 7/6 | note:C3 ]", + "[ 7/6 → 4/3 | note:E3 ]", + "[ 4/3 → 3/2 | note:G3 ]", + "[ 3/2 → 5/3 | note:B3 ]", + "[ 5/3 → 11/6 | note:G3 ]", + "[ 11/6 → 2/1 | note:E3 ]", + "[ 2/1 → 13/6 | note:C3 ]", + "[ 13/6 → 7/3 | note:E3 ]", + "[ 7/3 → 5/2 | note:G3 ]", + "[ 5/2 → 8/3 | note:B3 ]", + "[ 8/3 → 17/6 | note:G3 ]", + "[ 17/6 → 3/1 | note:E3 ]", + "[ 3/1 → 19/6 | note:C3 ]", + "[ 19/6 → 10/3 | note:E3 ]", + "[ 10/3 → 7/2 | note:G3 ]", + "[ 7/2 → 11/3 | note:B3 ]", + "[ 11/3 → 23/6 | note:G3 ]", + "[ 23/6 → 4/1 | note:E3 ]", ] `; exports[`runs examples > example "scale" example index 1 1`] = ` [ - "[ 0/1 → 1/4 | n:0 note:C3 s:piano ]", - "[ 0/1 → 1/4 | n:7 note:C4 s:piano ]", - "[ 1/4 → 1/2 | n:4 note:G3 s:piano ]", - "[ 1/2 → 3/4 | n:2 note:E3 s:piano ]", - "[ 1/2 → 3/4 | n:7 note:C4 s:piano ]", - "[ 3/4 → 1/1 | n:4 note:G3 s:piano ]", - "[ 1/1 → 5/4 | n:0 note:C3 s:piano ]", - "[ 1/1 → 5/4 | n:7 note:C4 s:piano ]", - "[ 5/4 → 3/2 | n:4 note:G3 s:piano ]", - "[ 3/2 → 7/4 | n:2 note:E3 s:piano ]", - "[ 3/2 → 7/4 | n:7 note:C4 s:piano ]", - "[ 7/4 → 2/1 | n:4 note:G3 s:piano ]", - "[ 2/1 → 9/4 | n:0 note:C3 s:piano ]", - "[ 2/1 → 9/4 | n:7 note:C4 s:piano ]", - "[ 9/4 → 5/2 | n:4 note:G3 s:piano ]", - "[ 5/2 → 11/4 | n:2 note:Eb3 s:piano ]", - "[ 5/2 → 11/4 | n:7 note:C4 s:piano ]", - "[ 11/4 → 3/1 | n:4 note:G3 s:piano ]", - "[ 3/1 → 13/4 | n:0 note:C3 s:piano ]", - "[ 3/1 → 13/4 | n:7 note:C4 s:piano ]", - "[ 13/4 → 7/2 | n:4 note:G3 s:piano ]", - "[ 7/2 → 15/4 | n:2 note:Eb3 s:piano ]", - "[ 7/2 → 15/4 | n:7 note:C4 s:piano ]", - "[ 15/4 → 4/1 | n:4 note:G3 s:piano ]", + "[ 0/1 → 1/4 | note:C3 s:piano ]", + "[ 0/1 → 1/4 | note:C4 s:piano ]", + "[ 1/4 → 1/2 | note:G3 s:piano ]", + "[ 1/2 → 3/4 | note:E3 s:piano ]", + "[ 1/2 → 3/4 | note:C4 s:piano ]", + "[ 3/4 → 1/1 | note:G3 s:piano ]", + "[ 1/1 → 5/4 | note:C3 s:piano ]", + "[ 1/1 → 5/4 | note:C4 s:piano ]", + "[ 5/4 → 3/2 | note:G3 s:piano ]", + "[ 3/2 → 7/4 | note:E3 s:piano ]", + "[ 3/2 → 7/4 | note:C4 s:piano ]", + "[ 7/4 → 2/1 | note:G3 s:piano ]", + "[ 2/1 → 9/4 | note:C3 s:piano ]", + "[ 2/1 → 9/4 | note:C4 s:piano ]", + "[ 9/4 → 5/2 | note:G3 s:piano ]", + "[ 5/2 → 11/4 | note:Eb3 s:piano ]", + "[ 5/2 → 11/4 | note:C4 s:piano ]", + "[ 11/4 → 3/1 | note:G3 s:piano ]", + "[ 3/1 → 13/4 | note:C3 s:piano ]", + "[ 3/1 → 13/4 | note:C4 s:piano ]", + "[ 13/4 → 7/2 | note:G3 s:piano ]", + "[ 7/2 → 15/4 | note:Eb3 s:piano ]", + "[ 7/2 → 15/4 | note:C4 s:piano ]", + "[ 15/4 → 4/1 | note:G3 s:piano ]", ] `; exports[`runs examples > example "scale" example index 2 1`] = ` [ - "[ 0/1 → 1/8 | n:10 note:C5 s:folkharp ]", - "[ 1/8 → 1/4 | n:2 note:F3 s:folkharp ]", - "[ 1/4 → 3/8 | n:7 note:F4 s:folkharp ]", - "[ 3/8 → 1/2 | n:4 note:A3 s:folkharp ]", - "[ 1/2 → 5/8 | n:2 note:F3 s:folkharp ]", - "[ 5/8 → 3/4 | n:5 note:C4 s:folkharp ]", - "[ 3/4 → 7/8 | n:9 note:A4 s:folkharp ]", - "[ 7/8 → 1/1 | n:8 note:G4 s:folkharp ]", - "[ 1/1 → 9/8 | n:7 note:F4 s:folkharp ]", - "[ 9/8 → 5/4 | n:1 note:D3 s:folkharp ]", - "[ 5/4 → 11/8 | n:1 note:D3 s:folkharp ]", - "[ 11/8 → 3/2 | n:6 note:D4 s:folkharp ]", - "[ 3/2 → 13/8 | n:2 note:F3 s:folkharp ]", - "[ 13/8 → 7/4 | n:4 note:A3 s:folkharp ]", - "[ 7/4 → 15/8 | n:6 note:D4 s:folkharp ]", - "[ 15/8 → 2/1 | n:10 note:C5 s:folkharp ]", - "[ 2/1 → 17/8 | n:4 note:A3 s:folkharp ]", - "[ 17/8 → 9/4 | n:0 note:C3 s:folkharp ]", - "[ 9/4 → 19/8 | n:8 note:G4 s:folkharp ]", - "[ 19/8 → 5/2 | n:2 note:F3 s:folkharp ]", - "[ 5/2 → 21/8 | n:7 note:F4 s:folkharp ]", - "[ 21/8 → 11/4 | n:6 note:D4 s:folkharp ]", - "[ 11/4 → 23/8 | n:11 note:D5 s:folkharp ]", - "[ 23/8 → 3/1 | n:3 note:G3 s:folkharp ]", - "[ 3/1 → 25/8 | n:0 note:C3 s:folkharp ]", - "[ 25/8 → 13/4 | n:11 note:D5 s:folkharp ]", - "[ 13/4 → 27/8 | n:4 note:A3 s:folkharp ]", - "[ 27/8 → 7/2 | n:9 note:A4 s:folkharp ]", - "[ 7/2 → 29/8 | n:10 note:C5 s:folkharp ]", - "[ 29/8 → 15/4 | n:12 note:F5 s:folkharp ]", - "[ 15/4 → 31/8 | n:1 note:D3 s:folkharp ]", - "[ 31/8 → 4/1 | n:4 note:A3 s:folkharp ]", + "[ 0/1 → 1/8 | note:C5 s:folkharp ]", + "[ 1/8 → 1/4 | note:F3 s:folkharp ]", + "[ 1/4 → 3/8 | note:F4 s:folkharp ]", + "[ 3/8 → 1/2 | note:A3 s:folkharp ]", + "[ 1/2 → 5/8 | note:F3 s:folkharp ]", + "[ 5/8 → 3/4 | note:C4 s:folkharp ]", + "[ 3/4 → 7/8 | note:A4 s:folkharp ]", + "[ 7/8 → 1/1 | note:G4 s:folkharp ]", + "[ 1/1 → 9/8 | note:F4 s:folkharp ]", + "[ 9/8 → 5/4 | note:D3 s:folkharp ]", + "[ 5/4 → 11/8 | note:D3 s:folkharp ]", + "[ 11/8 → 3/2 | note:D4 s:folkharp ]", + "[ 3/2 → 13/8 | note:F3 s:folkharp ]", + "[ 13/8 → 7/4 | note:A3 s:folkharp ]", + "[ 7/4 → 15/8 | note:D4 s:folkharp ]", + "[ 15/8 → 2/1 | note:C5 s:folkharp ]", + "[ 2/1 → 17/8 | note:A3 s:folkharp ]", + "[ 17/8 → 9/4 | note:C3 s:folkharp ]", + "[ 9/4 → 19/8 | note:G4 s:folkharp ]", + "[ 19/8 → 5/2 | note:F3 s:folkharp ]", + "[ 5/2 → 21/8 | note:F4 s:folkharp ]", + "[ 21/8 → 11/4 | note:D4 s:folkharp ]", + "[ 11/4 → 23/8 | note:D5 s:folkharp ]", + "[ 23/8 → 3/1 | note:G3 s:folkharp ]", + "[ 3/1 → 25/8 | note:C3 s:folkharp ]", + "[ 25/8 → 13/4 | note:D5 s:folkharp ]", + "[ 13/4 → 27/8 | note:A3 s:folkharp ]", + "[ 27/8 → 7/2 | note:A4 s:folkharp ]", + "[ 7/2 → 29/8 | note:C5 s:folkharp ]", + "[ 29/8 → 15/4 | note:F5 s:folkharp ]", + "[ 15/4 → 31/8 | note:D3 s:folkharp ]", + "[ 31/8 → 4/1 | note:A3 s:folkharp ]", ] `; diff --git a/test/__snapshots__/tunes.test.mjs.snap b/test/__snapshots__/tunes.test.mjs.snap index cd89ee3f..8d696d75 100644 --- a/test/__snapshots__/tunes.test.mjs.snap +++ b/test/__snapshots__/tunes.test.mjs.snap @@ -341,396 +341,396 @@ exports[`renders tunes > tune: blippyRhodes 1`] = ` exports[`renders tunes > tune: bridgeIsOver 1`] = ` [ - "[ -155/52 ⇜ (0/1 → 5/52) | n:2 clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ -75/26 ⇜ (0/1 → 5/26) | n:3 clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]", - "[ -145/52 ⇜ (0/1 → 15/52) | n:4 clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]", - "[ -35/13 ⇜ (0/1 → 5/13) | n:5 clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]", - "[ -35/13 ⇜ (0/1 → 5/13) | n:1 clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", - "[ -135/52 ⇜ (0/1 → 5/13) ⇝ 25/52 | n:2 clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ -5/2 ⇜ (0/1 → 5/13) ⇝ 15/26 | n:3 clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]", - "[ -125/52 ⇜ (0/1 → 5/13) ⇝ 35/52 | n:4 clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]", - "[ -30/13 ⇜ (0/1 → 5/13) ⇝ 10/13 | n:5 clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]", - "[ -115/52 ⇜ (0/1 → 5/13) ⇝ 45/52 | n:6 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", - "[ -55/26 ⇜ (0/1 → 5/13) ⇝ 25/26 | n:7 clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", - "[ -105/52 ⇜ (0/1 → 5/13) ⇝ 55/52 | n:8 clip:1 note:E5 s:piano release:0.1 pan:0.6018518518518519 ]", + "[ -155/52 ⇜ (0/1 → 5/52) | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ -75/26 ⇜ (0/1 → 5/26) | clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]", + "[ -145/52 ⇜ (0/1 → 15/52) | clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]", + "[ -35/13 ⇜ (0/1 → 5/13) | clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]", + "[ -35/13 ⇜ (0/1 → 5/13) | clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", + "[ -135/52 ⇜ (0/1 → 5/13) ⇝ 25/52 | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ -5/2 ⇜ (0/1 → 5/13) ⇝ 15/26 | clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]", + "[ -125/52 ⇜ (0/1 → 5/13) ⇝ 35/52 | clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]", + "[ -30/13 ⇜ (0/1 → 5/13) ⇝ 10/13 | clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]", + "[ -115/52 ⇜ (0/1 → 5/13) ⇝ 45/52 | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", + "[ -55/26 ⇜ (0/1 → 5/13) ⇝ 25/26 | clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", + "[ -105/52 ⇜ (0/1 → 5/13) ⇝ 55/52 | clip:1 note:E5 s:piano release:0.1 pan:0.6018518518518519 ]", "[ 0/1 → 5/13 | note:c3 gain:0.8 clip:1 s:piano release:0.1 pan:0.4722222222222222 ]", - "[ -135/52 ⇜ (0/1 → 25/52) | n:6 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", - "[ -5/2 ⇜ (0/1 → 15/26) | n:7 clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", - "[ -125/52 ⇜ (0/1 → 35/52) | n:8 clip:1 note:E5 s:piano release:0.1 pan:0.6018518518518519 ]", - "[ -30/13 ⇜ (0/1 → 10/13) | n:1 clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", - "[ -115/52 ⇜ (0/1 → 10/13) ⇝ 45/52 | n:2 clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ -55/26 ⇜ (0/1 → 10/13) ⇝ 25/26 | n:3 clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]", - "[ -105/52 ⇜ (0/1 → 10/13) ⇝ 55/52 | n:4 clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]", - "[ -25/13 ⇜ (0/1 → 10/13) ⇝ 15/13 | n:5 clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]", - "[ -95/52 ⇜ (0/1 → 10/13) ⇝ 5/4 | n:6 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", - "[ -45/26 ⇜ (0/1 → 10/13) ⇝ 35/26 | n:7 clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", - "[ -85/52 ⇜ (0/1 → 10/13) ⇝ 75/52 | n:8 clip:1 note:E5 s:piano release:0.1 pan:0.6018518518518519 ]", - "[ (0/1 → 1/1) ⇝ 40/13 | n:0 clip:1 note:C4 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ -135/52 ⇜ (0/1 → 25/52) | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", + "[ -5/2 ⇜ (0/1 → 15/26) | clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", + "[ -125/52 ⇜ (0/1 → 35/52) | clip:1 note:E5 s:piano release:0.1 pan:0.6018518518518519 ]", + "[ -30/13 ⇜ (0/1 → 10/13) | clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", + "[ -115/52 ⇜ (0/1 → 10/13) ⇝ 45/52 | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ -55/26 ⇜ (0/1 → 10/13) ⇝ 25/26 | clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]", + "[ -105/52 ⇜ (0/1 → 10/13) ⇝ 55/52 | clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]", + "[ -25/13 ⇜ (0/1 → 10/13) ⇝ 15/13 | clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]", + "[ -95/52 ⇜ (0/1 → 10/13) ⇝ 5/4 | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", + "[ -45/26 ⇜ (0/1 → 10/13) ⇝ 35/26 | clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", + "[ -85/52 ⇜ (0/1 → 10/13) ⇝ 75/52 | clip:1 note:E5 s:piano release:0.1 pan:0.6018518518518519 ]", + "[ (0/1 → 1/1) ⇝ 40/13 | clip:1 note:C4 s:piano release:0.1 pan:0.5277777777777778 ]", "[ (0/1 → 1/1) ⇝ 80/13 | s:mad ]", - "[ (5/52 → 1/1) ⇝ 165/52 | n:1 clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", - "[ (5/26 → 1/1) ⇝ 85/26 | n:2 clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ (15/52 → 1/1) ⇝ 175/52 | n:3 clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]", - "[ -135/52 ⇜ (5/13 → 25/52) | n:2 clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ -5/2 ⇜ (5/13 → 15/26) | n:3 clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]", - "[ -125/52 ⇜ (5/13 → 35/52) | n:4 clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]", - "[ -30/13 ⇜ (5/13 → 10/13) | n:5 clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]", + "[ (5/52 → 1/1) ⇝ 165/52 | clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", + "[ (5/26 → 1/1) ⇝ 85/26 | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ (15/52 → 1/1) ⇝ 175/52 | clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]", + "[ -135/52 ⇜ (5/13 → 25/52) | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ -5/2 ⇜ (5/13 → 15/26) | clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]", + "[ -125/52 ⇜ (5/13 → 35/52) | clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]", + "[ -30/13 ⇜ (5/13 → 10/13) | clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]", "[ 5/13 → 10/13 | note:c3 gain:0.8 clip:1 s:piano release:0.1 pan:0.4722222222222222 ]", - "[ -115/52 ⇜ (5/13 → 45/52) | n:6 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", - "[ -55/26 ⇜ (5/13 → 25/26) | n:7 clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", - "[ -105/52 ⇜ (5/13 → 1/1) ⇝ 55/52 | n:8 clip:1 note:E5 s:piano release:0.1 pan:0.6018518518518519 ]", - "[ (5/13 → 1/1) ⇝ 45/13 | n:4 clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]", - "[ (5/13 → 1/1) ⇝ 45/13 | n:0 clip:1 note:C4 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ (25/52 → 1/1) ⇝ 185/52 | n:5 clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]", - "[ (25/52 → 1/1) ⇝ 185/52 | n:1 clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", - "[ (15/26 → 1/1) ⇝ 95/26 | n:6 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", - "[ (15/26 → 1/1) ⇝ 95/26 | n:2 clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ (35/52 → 1/1) ⇝ 15/4 | n:7 clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", - "[ (35/52 → 1/1) ⇝ 15/4 | n:3 clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]", - "[ -115/52 ⇜ (10/13 → 45/52) | n:2 clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ -55/26 ⇜ (10/13 → 25/26) | n:3 clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]", + "[ -115/52 ⇜ (5/13 → 45/52) | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", + "[ -55/26 ⇜ (5/13 → 25/26) | clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", + "[ -105/52 ⇜ (5/13 → 1/1) ⇝ 55/52 | clip:1 note:E5 s:piano release:0.1 pan:0.6018518518518519 ]", + "[ (5/13 → 1/1) ⇝ 45/13 | clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]", + "[ (5/13 → 1/1) ⇝ 45/13 | clip:1 note:C4 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ (25/52 → 1/1) ⇝ 185/52 | clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]", + "[ (25/52 → 1/1) ⇝ 185/52 | clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", + "[ (15/26 → 1/1) ⇝ 95/26 | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", + "[ (15/26 → 1/1) ⇝ 95/26 | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ (35/52 → 1/1) ⇝ 15/4 | clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", + "[ (35/52 → 1/1) ⇝ 15/4 | clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]", + "[ -115/52 ⇜ (10/13 → 45/52) | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ -55/26 ⇜ (10/13 → 25/26) | clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]", "[ 10/13 → 155/156 | note:c3 gain:0.8 clip:1 s:piano release:0.1 pan:0.4722222222222222 ]", - "[ -105/52 ⇜ (10/13 → 1/1) ⇝ 55/52 | n:4 clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]", - "[ -25/13 ⇜ (10/13 → 1/1) ⇝ 15/13 | n:5 clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]", - "[ -95/52 ⇜ (10/13 → 1/1) ⇝ 5/4 | n:6 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", - "[ -45/26 ⇜ (10/13 → 1/1) ⇝ 35/26 | n:7 clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", - "[ -85/52 ⇜ (10/13 → 1/1) ⇝ 75/52 | n:8 clip:1 note:E5 s:piano release:0.1 pan:0.6018518518518519 ]", - "[ (10/13 → 1/1) ⇝ 50/13 | n:4 clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]", - "[ (10/13 → 1/1) ⇝ 50/13 | n:0 clip:1 note:C4 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ (45/52 → 1/1) ⇝ 205/52 | n:5 clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]", - "[ (45/52 → 1/1) ⇝ 205/52 | n:1 clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", - "[ (25/26 → 1/1) ⇝ 105/26 | n:6 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", - "[ (25/26 → 1/1) ⇝ 105/26 | n:2 clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ -105/52 ⇜ (10/13 → 1/1) ⇝ 55/52 | clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]", + "[ -25/13 ⇜ (10/13 → 1/1) ⇝ 15/13 | clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]", + "[ -95/52 ⇜ (10/13 → 1/1) ⇝ 5/4 | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", + "[ -45/26 ⇜ (10/13 → 1/1) ⇝ 35/26 | clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", + "[ -85/52 ⇜ (10/13 → 1/1) ⇝ 75/52 | clip:1 note:E5 s:piano release:0.1 pan:0.6018518518518519 ]", + "[ (10/13 → 1/1) ⇝ 50/13 | clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]", + "[ (10/13 → 1/1) ⇝ 50/13 | clip:1 note:C4 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ (45/52 → 1/1) ⇝ 205/52 | clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]", + "[ (45/52 → 1/1) ⇝ 205/52 | clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", + "[ (25/26 → 1/1) ⇝ 105/26 | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", + "[ (25/26 → 1/1) ⇝ 105/26 | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", "[ (155/156 → 1/1) ⇝ 15/13 | note:bb2 gain:0.8 clip:1 s:piano release:0.1 pan:0.46296296296296297 ]", - "[ -105/52 ⇜ (1/1 → 55/52) | n:8 clip:1 note:E5 s:piano release:0.1 pan:0.6018518518518519 ]", - "[ -105/52 ⇜ (1/1 → 55/52) | n:4 clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]", - "[ -25/13 ⇜ (1/1 → 15/13) | n:5 clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]", + "[ -105/52 ⇜ (1/1 → 55/52) | clip:1 note:E5 s:piano release:0.1 pan:0.6018518518518519 ]", + "[ -105/52 ⇜ (1/1 → 55/52) | clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]", + "[ -25/13 ⇜ (1/1 → 15/13) | clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]", "[ 155/156 ⇜ (1/1 → 15/13) | note:bb2 gain:0.8 clip:1 s:piano release:0.1 pan:0.46296296296296297 ]", - "[ -95/52 ⇜ (1/1 → 5/4) | n:6 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", - "[ -45/26 ⇜ (1/1 → 35/26) | n:7 clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", - "[ -85/52 ⇜ (1/1 → 75/52) | n:8 clip:1 note:E5 s:piano release:0.1 pan:0.6018518518518519 ]", - "[ 0/1 ⇜ (1/1 → 2/1) ⇝ 40/13 | n:0 clip:1 note:C4 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ -95/52 ⇜ (1/1 → 5/4) | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", + "[ -45/26 ⇜ (1/1 → 35/26) | clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", + "[ -85/52 ⇜ (1/1 → 75/52) | clip:1 note:E5 s:piano release:0.1 pan:0.6018518518518519 ]", + "[ 0/1 ⇜ (1/1 → 2/1) ⇝ 40/13 | clip:1 note:C4 s:piano release:0.1 pan:0.5277777777777778 ]", "[ 0/1 ⇜ (1/1 → 2/1) ⇝ 80/13 | s:mad ]", - "[ 5/52 ⇜ (1/1 → 2/1) ⇝ 165/52 | n:1 clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", - "[ 5/26 ⇜ (1/1 → 2/1) ⇝ 85/26 | n:2 clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 15/52 ⇜ (1/1 → 2/1) ⇝ 175/52 | n:3 clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]", - "[ 5/13 ⇜ (1/1 → 2/1) ⇝ 45/13 | n:4 clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]", - "[ 5/13 ⇜ (1/1 → 2/1) ⇝ 45/13 | n:0 clip:1 note:C4 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ 25/52 ⇜ (1/1 → 2/1) ⇝ 185/52 | n:5 clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]", - "[ 25/52 ⇜ (1/1 → 2/1) ⇝ 185/52 | n:1 clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", - "[ 15/26 ⇜ (1/1 → 2/1) ⇝ 95/26 | n:6 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", - "[ 15/26 ⇜ (1/1 → 2/1) ⇝ 95/26 | n:2 clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 35/52 ⇜ (1/1 → 2/1) ⇝ 15/4 | n:7 clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", - "[ 35/52 ⇜ (1/1 → 2/1) ⇝ 15/4 | n:3 clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]", - "[ 10/13 ⇜ (1/1 → 2/1) ⇝ 50/13 | n:4 clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]", - "[ 10/13 ⇜ (1/1 → 2/1) ⇝ 50/13 | n:0 clip:1 note:C4 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ 45/52 ⇜ (1/1 → 2/1) ⇝ 205/52 | n:5 clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]", - "[ 45/52 ⇜ (1/1 → 2/1) ⇝ 205/52 | n:1 clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", - "[ 25/26 ⇜ (1/1 → 2/1) ⇝ 105/26 | n:6 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", - "[ 25/26 ⇜ (1/1 → 2/1) ⇝ 105/26 | n:2 clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ (55/52 → 2/1) ⇝ 215/52 | n:7 clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", - "[ (55/52 → 2/1) ⇝ 215/52 | n:3 clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]", + "[ 5/52 ⇜ (1/1 → 2/1) ⇝ 165/52 | clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", + "[ 5/26 ⇜ (1/1 → 2/1) ⇝ 85/26 | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 15/52 ⇜ (1/1 → 2/1) ⇝ 175/52 | clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]", + "[ 5/13 ⇜ (1/1 → 2/1) ⇝ 45/13 | clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]", + "[ 5/13 ⇜ (1/1 → 2/1) ⇝ 45/13 | clip:1 note:C4 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ 25/52 ⇜ (1/1 → 2/1) ⇝ 185/52 | clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]", + "[ 25/52 ⇜ (1/1 → 2/1) ⇝ 185/52 | clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", + "[ 15/26 ⇜ (1/1 → 2/1) ⇝ 95/26 | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", + "[ 15/26 ⇜ (1/1 → 2/1) ⇝ 95/26 | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 35/52 ⇜ (1/1 → 2/1) ⇝ 15/4 | clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", + "[ 35/52 ⇜ (1/1 → 2/1) ⇝ 15/4 | clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]", + "[ 10/13 ⇜ (1/1 → 2/1) ⇝ 50/13 | clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]", + "[ 10/13 ⇜ (1/1 → 2/1) ⇝ 50/13 | clip:1 note:C4 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ 45/52 ⇜ (1/1 → 2/1) ⇝ 205/52 | clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]", + "[ 45/52 ⇜ (1/1 → 2/1) ⇝ 205/52 | clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", + "[ 25/26 ⇜ (1/1 → 2/1) ⇝ 105/26 | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", + "[ 25/26 ⇜ (1/1 → 2/1) ⇝ 105/26 | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ (55/52 → 2/1) ⇝ 215/52 | clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", + "[ (55/52 → 2/1) ⇝ 215/52 | clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]", "[ 15/13 → 20/13 | note:ab2 gain:0.8 clip:1 s:piano release:0.1 pan:0.4537037037037037 ]", - "[ (15/13 → 2/1) ⇝ 55/13 | n:4 clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]", - "[ (5/4 → 2/1) ⇝ 225/52 | n:5 clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]", - "[ (35/26 → 2/1) ⇝ 115/26 | n:6 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", - "[ (75/52 → 2/1) ⇝ 235/52 | n:7 clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", + "[ (15/13 → 2/1) ⇝ 55/13 | clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]", + "[ (5/4 → 2/1) ⇝ 225/52 | clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]", + "[ (35/26 → 2/1) ⇝ 115/26 | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", + "[ (75/52 → 2/1) ⇝ 235/52 | clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", "[ 20/13 → 25/13 | note:gb2 gain:0.8 clip:1 s:piano release:0.1 pan:0.4444444444444444 ]", "[ (25/13 → 2/1) ⇝ 30/13 | note:gb2 gain:0.8 clip:1 s:piano release:0.1 pan:0.4444444444444444 ]", "[ 25/13 ⇜ (2/1 → 30/13) | note:gb2 gain:0.8 clip:1 s:piano release:0.1 pan:0.4444444444444444 ]", - "[ 0/1 ⇜ (2/1 → 3/1) ⇝ 40/13 | n:0 clip:1 note:C4 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ 0/1 ⇜ (2/1 → 3/1) ⇝ 40/13 | clip:1 note:C4 s:piano release:0.1 pan:0.5277777777777778 ]", "[ 0/1 ⇜ (2/1 → 3/1) ⇝ 80/13 | s:mad ]", - "[ 5/52 ⇜ (2/1 → 3/1) ⇝ 165/52 | n:1 clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", - "[ 5/26 ⇜ (2/1 → 3/1) ⇝ 85/26 | n:2 clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 15/52 ⇜ (2/1 → 3/1) ⇝ 175/52 | n:3 clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]", - "[ 5/13 ⇜ (2/1 → 3/1) ⇝ 45/13 | n:4 clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]", - "[ 5/13 ⇜ (2/1 → 3/1) ⇝ 45/13 | n:0 clip:1 note:C4 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ 25/52 ⇜ (2/1 → 3/1) ⇝ 185/52 | n:5 clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]", - "[ 25/52 ⇜ (2/1 → 3/1) ⇝ 185/52 | n:1 clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", - "[ 15/26 ⇜ (2/1 → 3/1) ⇝ 95/26 | n:6 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", - "[ 15/26 ⇜ (2/1 → 3/1) ⇝ 95/26 | n:2 clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 35/52 ⇜ (2/1 → 3/1) ⇝ 15/4 | n:7 clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", - "[ 35/52 ⇜ (2/1 → 3/1) ⇝ 15/4 | n:3 clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]", - "[ 10/13 ⇜ (2/1 → 3/1) ⇝ 50/13 | n:4 clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]", - "[ 10/13 ⇜ (2/1 → 3/1) ⇝ 50/13 | n:0 clip:1 note:C4 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ 45/52 ⇜ (2/1 → 3/1) ⇝ 205/52 | n:5 clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]", - "[ 45/52 ⇜ (2/1 → 3/1) ⇝ 205/52 | n:1 clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", - "[ 25/26 ⇜ (2/1 → 3/1) ⇝ 105/26 | n:6 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", - "[ 25/26 ⇜ (2/1 → 3/1) ⇝ 105/26 | n:2 clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 55/52 ⇜ (2/1 → 3/1) ⇝ 215/52 | n:7 clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", - "[ 55/52 ⇜ (2/1 → 3/1) ⇝ 215/52 | n:3 clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]", - "[ 15/13 ⇜ (2/1 → 3/1) ⇝ 55/13 | n:4 clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]", - "[ 5/4 ⇜ (2/1 → 3/1) ⇝ 225/52 | n:5 clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]", - "[ 35/26 ⇜ (2/1 → 3/1) ⇝ 115/26 | n:6 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", - "[ 75/52 ⇜ (2/1 → 3/1) ⇝ 235/52 | n:7 clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", + "[ 5/52 ⇜ (2/1 → 3/1) ⇝ 165/52 | clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", + "[ 5/26 ⇜ (2/1 → 3/1) ⇝ 85/26 | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 15/52 ⇜ (2/1 → 3/1) ⇝ 175/52 | clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]", + "[ 5/13 ⇜ (2/1 → 3/1) ⇝ 45/13 | clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]", + "[ 5/13 ⇜ (2/1 → 3/1) ⇝ 45/13 | clip:1 note:C4 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ 25/52 ⇜ (2/1 → 3/1) ⇝ 185/52 | clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]", + "[ 25/52 ⇜ (2/1 → 3/1) ⇝ 185/52 | clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", + "[ 15/26 ⇜ (2/1 → 3/1) ⇝ 95/26 | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", + "[ 15/26 ⇜ (2/1 → 3/1) ⇝ 95/26 | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 35/52 ⇜ (2/1 → 3/1) ⇝ 15/4 | clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", + "[ 35/52 ⇜ (2/1 → 3/1) ⇝ 15/4 | clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]", + "[ 10/13 ⇜ (2/1 → 3/1) ⇝ 50/13 | clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]", + "[ 10/13 ⇜ (2/1 → 3/1) ⇝ 50/13 | clip:1 note:C4 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ 45/52 ⇜ (2/1 → 3/1) ⇝ 205/52 | clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]", + "[ 45/52 ⇜ (2/1 → 3/1) ⇝ 205/52 | clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", + "[ 25/26 ⇜ (2/1 → 3/1) ⇝ 105/26 | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", + "[ 25/26 ⇜ (2/1 → 3/1) ⇝ 105/26 | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 55/52 ⇜ (2/1 → 3/1) ⇝ 215/52 | clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", + "[ 55/52 ⇜ (2/1 → 3/1) ⇝ 215/52 | clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]", + "[ 15/13 ⇜ (2/1 → 3/1) ⇝ 55/13 | clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]", + "[ 5/4 ⇜ (2/1 → 3/1) ⇝ 225/52 | clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]", + "[ 35/26 ⇜ (2/1 → 3/1) ⇝ 115/26 | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", + "[ 75/52 ⇜ (2/1 → 3/1) ⇝ 235/52 | clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", "[ 30/13 → 395/156 | note:gb2 gain:0.8 clip:1 s:piano release:0.1 pan:0.4444444444444444 ]", "[ 395/156 → 35/13 | note:ab2 gain:0.8 clip:1 s:piano release:0.1 pan:0.4537037037037037 ]", "[ (35/13 → 3/1) ⇝ 40/13 | note:bb2 gain:0.8 clip:1 s:piano release:0.1 pan:0.46296296296296297 ]", - "[ 0/1 ⇜ (3/1 → 40/13) | n:0 clip:1 note:C4 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ 0/1 ⇜ (3/1 → 40/13) | clip:1 note:C4 s:piano release:0.1 pan:0.5277777777777778 ]", "[ 0/1 ⇜ (3/1 → 40/13) ⇝ 80/13 | s:mad ]", - "[ 5/52 ⇜ (3/1 → 40/13) ⇝ 165/52 | n:1 clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", - "[ 5/26 ⇜ (3/1 → 40/13) ⇝ 85/26 | n:2 clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 15/52 ⇜ (3/1 → 40/13) ⇝ 175/52 | n:3 clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]", - "[ 5/13 ⇜ (3/1 → 40/13) ⇝ 45/13 | n:4 clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]", - "[ 5/13 ⇜ (3/1 → 40/13) ⇝ 45/13 | n:0 clip:1 note:C4 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ 25/52 ⇜ (3/1 → 40/13) ⇝ 185/52 | n:5 clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]", - "[ 25/52 ⇜ (3/1 → 40/13) ⇝ 185/52 | n:1 clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", - "[ 15/26 ⇜ (3/1 → 40/13) ⇝ 95/26 | n:6 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", - "[ 15/26 ⇜ (3/1 → 40/13) ⇝ 95/26 | n:2 clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 35/52 ⇜ (3/1 → 40/13) ⇝ 15/4 | n:7 clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", - "[ 35/52 ⇜ (3/1 → 40/13) ⇝ 15/4 | n:3 clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]", - "[ 10/13 ⇜ (3/1 → 40/13) ⇝ 50/13 | n:4 clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]", - "[ 10/13 ⇜ (3/1 → 40/13) ⇝ 50/13 | n:0 clip:1 note:C4 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ 45/52 ⇜ (3/1 → 40/13) ⇝ 205/52 | n:5 clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]", - "[ 45/52 ⇜ (3/1 → 40/13) ⇝ 205/52 | n:1 clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", - "[ 25/26 ⇜ (3/1 → 40/13) ⇝ 105/26 | n:6 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", - "[ 25/26 ⇜ (3/1 → 40/13) ⇝ 105/26 | n:2 clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 55/52 ⇜ (3/1 → 40/13) ⇝ 215/52 | n:7 clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", - "[ 55/52 ⇜ (3/1 → 40/13) ⇝ 215/52 | n:3 clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]", - "[ 15/13 ⇜ (3/1 → 40/13) ⇝ 55/13 | n:4 clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]", - "[ 5/4 ⇜ (3/1 → 40/13) ⇝ 225/52 | n:5 clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]", - "[ 35/26 ⇜ (3/1 → 40/13) ⇝ 115/26 | n:6 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", - "[ 75/52 ⇜ (3/1 → 40/13) ⇝ 235/52 | n:7 clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", + "[ 5/52 ⇜ (3/1 → 40/13) ⇝ 165/52 | clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", + "[ 5/26 ⇜ (3/1 → 40/13) ⇝ 85/26 | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 15/52 ⇜ (3/1 → 40/13) ⇝ 175/52 | clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]", + "[ 5/13 ⇜ (3/1 → 40/13) ⇝ 45/13 | clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]", + "[ 5/13 ⇜ (3/1 → 40/13) ⇝ 45/13 | clip:1 note:C4 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ 25/52 ⇜ (3/1 → 40/13) ⇝ 185/52 | clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]", + "[ 25/52 ⇜ (3/1 → 40/13) ⇝ 185/52 | clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", + "[ 15/26 ⇜ (3/1 → 40/13) ⇝ 95/26 | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", + "[ 15/26 ⇜ (3/1 → 40/13) ⇝ 95/26 | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 35/52 ⇜ (3/1 → 40/13) ⇝ 15/4 | clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", + "[ 35/52 ⇜ (3/1 → 40/13) ⇝ 15/4 | clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]", + "[ 10/13 ⇜ (3/1 → 40/13) ⇝ 50/13 | clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]", + "[ 10/13 ⇜ (3/1 → 40/13) ⇝ 50/13 | clip:1 note:C4 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ 45/52 ⇜ (3/1 → 40/13) ⇝ 205/52 | clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]", + "[ 45/52 ⇜ (3/1 → 40/13) ⇝ 205/52 | clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", + "[ 25/26 ⇜ (3/1 → 40/13) ⇝ 105/26 | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", + "[ 25/26 ⇜ (3/1 → 40/13) ⇝ 105/26 | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 55/52 ⇜ (3/1 → 40/13) ⇝ 215/52 | clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", + "[ 55/52 ⇜ (3/1 → 40/13) ⇝ 215/52 | clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]", + "[ 15/13 ⇜ (3/1 → 40/13) ⇝ 55/13 | clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]", + "[ 5/4 ⇜ (3/1 → 40/13) ⇝ 225/52 | clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]", + "[ 35/26 ⇜ (3/1 → 40/13) ⇝ 115/26 | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", + "[ 75/52 ⇜ (3/1 → 40/13) ⇝ 235/52 | clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", "[ 35/13 ⇜ (3/1 → 40/13) | note:bb2 gain:0.8 clip:1 s:piano release:0.1 pan:0.46296296296296297 ]", - "[ 5/52 ⇜ (40/13 → 165/52) | n:1 clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", - "[ 5/26 ⇜ (40/13 → 85/26) | n:2 clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 15/52 ⇜ (40/13 → 175/52) | n:3 clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]", - "[ 5/13 ⇜ (40/13 → 45/13) | n:4 clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]", - "[ 5/13 ⇜ (40/13 → 45/13) | n:0 clip:1 note:C4 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ 25/52 ⇜ (40/13 → 45/13) ⇝ 185/52 | n:1 clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", - "[ 15/26 ⇜ (40/13 → 45/13) ⇝ 95/26 | n:2 clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 35/52 ⇜ (40/13 → 45/13) ⇝ 15/4 | n:3 clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]", - "[ 10/13 ⇜ (40/13 → 45/13) ⇝ 50/13 | n:4 clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]", - "[ 45/52 ⇜ (40/13 → 45/13) ⇝ 205/52 | n:5 clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]", - "[ 25/26 ⇜ (40/13 → 45/13) ⇝ 105/26 | n:6 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", - "[ 55/52 ⇜ (40/13 → 45/13) ⇝ 215/52 | n:7 clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", + "[ 5/52 ⇜ (40/13 → 165/52) | clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", + "[ 5/26 ⇜ (40/13 → 85/26) | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 15/52 ⇜ (40/13 → 175/52) | clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]", + "[ 5/13 ⇜ (40/13 → 45/13) | clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]", + "[ 5/13 ⇜ (40/13 → 45/13) | clip:1 note:C4 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ 25/52 ⇜ (40/13 → 45/13) ⇝ 185/52 | clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", + "[ 15/26 ⇜ (40/13 → 45/13) ⇝ 95/26 | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 35/52 ⇜ (40/13 → 45/13) ⇝ 15/4 | clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]", + "[ 10/13 ⇜ (40/13 → 45/13) ⇝ 50/13 | clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]", + "[ 45/52 ⇜ (40/13 → 45/13) ⇝ 205/52 | clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]", + "[ 25/26 ⇜ (40/13 → 45/13) ⇝ 105/26 | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", + "[ 55/52 ⇜ (40/13 → 45/13) ⇝ 215/52 | clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", "[ 40/13 → 45/13 | note:c3 gain:0.8 clip:1 s:piano release:0.1 pan:0.4722222222222222 ]", - "[ 25/52 ⇜ (40/13 → 185/52) | n:5 clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]", - "[ 15/26 ⇜ (40/13 → 95/26) | n:6 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", - "[ 35/52 ⇜ (40/13 → 15/4) | n:7 clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", - "[ 10/13 ⇜ (40/13 → 50/13) | n:0 clip:1 note:C4 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ 45/52 ⇜ (40/13 → 50/13) ⇝ 205/52 | n:1 clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", - "[ 25/26 ⇜ (40/13 → 50/13) ⇝ 105/26 | n:2 clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 55/52 ⇜ (40/13 → 50/13) ⇝ 215/52 | n:3 clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]", - "[ 15/13 ⇜ (40/13 → 50/13) ⇝ 55/13 | n:4 clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]", - "[ 5/4 ⇜ (40/13 → 50/13) ⇝ 225/52 | n:5 clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]", - "[ 35/26 ⇜ (40/13 → 50/13) ⇝ 115/26 | n:6 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", - "[ 75/52 ⇜ (40/13 → 50/13) ⇝ 235/52 | n:7 clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", + "[ 25/52 ⇜ (40/13 → 185/52) | clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]", + "[ 15/26 ⇜ (40/13 → 95/26) | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", + "[ 35/52 ⇜ (40/13 → 15/4) | clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", + "[ 10/13 ⇜ (40/13 → 50/13) | clip:1 note:C4 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ 45/52 ⇜ (40/13 → 50/13) ⇝ 205/52 | clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", + "[ 25/26 ⇜ (40/13 → 50/13) ⇝ 105/26 | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 55/52 ⇜ (40/13 → 50/13) ⇝ 215/52 | clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]", + "[ 15/13 ⇜ (40/13 → 50/13) ⇝ 55/13 | clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]", + "[ 5/4 ⇜ (40/13 → 50/13) ⇝ 225/52 | clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]", + "[ 35/26 ⇜ (40/13 → 50/13) ⇝ 115/26 | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", + "[ 75/52 ⇜ (40/13 → 50/13) ⇝ 235/52 | clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", "[ 0/1 ⇜ (40/13 → 4/1) ⇝ 80/13 | s:mad ]", - "[ (40/13 → 4/1) ⇝ 80/13 | n:1 clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", - "[ (165/52 → 4/1) ⇝ 25/4 | n:2 clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ (85/26 → 4/1) ⇝ 165/26 | n:3 clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]", - "[ (175/52 → 4/1) ⇝ 335/52 | n:4 clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]", - "[ 25/52 ⇜ (45/13 → 185/52) | n:1 clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", - "[ 15/26 ⇜ (45/13 → 95/26) | n:2 clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 35/52 ⇜ (45/13 → 15/4) | n:3 clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]", - "[ 10/13 ⇜ (45/13 → 50/13) | n:4 clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]", + "[ (40/13 → 4/1) ⇝ 80/13 | clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", + "[ (165/52 → 4/1) ⇝ 25/4 | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ (85/26 → 4/1) ⇝ 165/26 | clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]", + "[ (175/52 → 4/1) ⇝ 335/52 | clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]", + "[ 25/52 ⇜ (45/13 → 185/52) | clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", + "[ 15/26 ⇜ (45/13 → 95/26) | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 35/52 ⇜ (45/13 → 15/4) | clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]", + "[ 10/13 ⇜ (45/13 → 50/13) | clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]", "[ 45/13 → 50/13 | note:c3 gain:0.8 clip:1 s:piano release:0.1 pan:0.4722222222222222 ]", - "[ 45/52 ⇜ (45/13 → 205/52) | n:5 clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]", - "[ 25/26 ⇜ (45/13 → 4/1) ⇝ 105/26 | n:6 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", - "[ 55/52 ⇜ (45/13 → 4/1) ⇝ 215/52 | n:7 clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", - "[ (45/13 → 4/1) ⇝ 85/13 | n:5 clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]", - "[ (45/13 → 4/1) ⇝ 85/13 | n:1 clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", - "[ (185/52 → 4/1) ⇝ 345/52 | n:6 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", - "[ (185/52 → 4/1) ⇝ 345/52 | n:2 clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ (95/26 → 4/1) ⇝ 175/26 | n:7 clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", - "[ (95/26 → 4/1) ⇝ 175/26 | n:3 clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]", - "[ (15/4 → 4/1) ⇝ 355/52 | n:8 clip:1 note:E5 s:piano release:0.1 pan:0.6018518518518519 ]", - "[ (15/4 → 4/1) ⇝ 355/52 | n:4 clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]", - "[ 45/52 ⇜ (50/13 → 205/52) | n:1 clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", - "[ 25/26 ⇜ (50/13 → 4/1) ⇝ 105/26 | n:2 clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 55/52 ⇜ (50/13 → 4/1) ⇝ 215/52 | n:3 clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]", - "[ 15/13 ⇜ (50/13 → 4/1) ⇝ 55/13 | n:4 clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]", - "[ 5/4 ⇜ (50/13 → 4/1) ⇝ 225/52 | n:5 clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]", - "[ 35/26 ⇜ (50/13 → 4/1) ⇝ 115/26 | n:6 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", - "[ 75/52 ⇜ (50/13 → 4/1) ⇝ 235/52 | n:7 clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", + "[ 45/52 ⇜ (45/13 → 205/52) | clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]", + "[ 25/26 ⇜ (45/13 → 4/1) ⇝ 105/26 | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", + "[ 55/52 ⇜ (45/13 → 4/1) ⇝ 215/52 | clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", + "[ (45/13 → 4/1) ⇝ 85/13 | clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]", + "[ (45/13 → 4/1) ⇝ 85/13 | clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", + "[ (185/52 → 4/1) ⇝ 345/52 | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", + "[ (185/52 → 4/1) ⇝ 345/52 | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ (95/26 → 4/1) ⇝ 175/26 | clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", + "[ (95/26 → 4/1) ⇝ 175/26 | clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]", + "[ (15/4 → 4/1) ⇝ 355/52 | clip:1 note:E5 s:piano release:0.1 pan:0.6018518518518519 ]", + "[ (15/4 → 4/1) ⇝ 355/52 | clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]", + "[ 45/52 ⇜ (50/13 → 205/52) | clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", + "[ 25/26 ⇜ (50/13 → 4/1) ⇝ 105/26 | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 55/52 ⇜ (50/13 → 4/1) ⇝ 215/52 | clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]", + "[ 15/13 ⇜ (50/13 → 4/1) ⇝ 55/13 | clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]", + "[ 5/4 ⇜ (50/13 → 4/1) ⇝ 225/52 | clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]", + "[ 35/26 ⇜ (50/13 → 4/1) ⇝ 115/26 | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", + "[ 75/52 ⇜ (50/13 → 4/1) ⇝ 235/52 | clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", "[ (50/13 → 4/1) ⇝ 635/156 | note:c3 gain:0.8 clip:1 s:piano release:0.1 pan:0.4722222222222222 ]", - "[ (50/13 → 4/1) ⇝ 90/13 | n:5 clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]", - "[ (50/13 → 4/1) ⇝ 90/13 | n:1 clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", - "[ (205/52 → 4/1) ⇝ 365/52 | n:6 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", - "[ (205/52 → 4/1) ⇝ 365/52 | n:2 clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 25/26 ⇜ (4/1 → 105/26) | n:6 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", - "[ 25/26 ⇜ (4/1 → 105/26) | n:2 clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ (50/13 → 4/1) ⇝ 90/13 | clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]", + "[ (50/13 → 4/1) ⇝ 90/13 | clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", + "[ (205/52 → 4/1) ⇝ 365/52 | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", + "[ (205/52 → 4/1) ⇝ 365/52 | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 25/26 ⇜ (4/1 → 105/26) | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", + "[ 25/26 ⇜ (4/1 → 105/26) | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", "[ 50/13 ⇜ (4/1 → 635/156) | note:c3 gain:0.8 clip:1 s:piano release:0.1 pan:0.4722222222222222 ]", - "[ 55/52 ⇜ (4/1 → 215/52) | n:7 clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", - "[ 55/52 ⇜ (4/1 → 215/52) | n:3 clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]", - "[ 15/13 ⇜ (4/1 → 55/13) | n:4 clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]", - "[ 5/4 ⇜ (4/1 → 225/52) | n:5 clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]", - "[ 35/26 ⇜ (4/1 → 115/26) | n:6 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", - "[ 75/52 ⇜ (4/1 → 235/52) | n:7 clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", + "[ 55/52 ⇜ (4/1 → 215/52) | clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", + "[ 55/52 ⇜ (4/1 → 215/52) | clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]", + "[ 15/13 ⇜ (4/1 → 55/13) | clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]", + "[ 5/4 ⇜ (4/1 → 225/52) | clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]", + "[ 35/26 ⇜ (4/1 → 115/26) | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", + "[ 75/52 ⇜ (4/1 → 235/52) | clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", "[ 0/1 ⇜ (4/1 → 5/1) ⇝ 80/13 | s:mad ]", - "[ 40/13 ⇜ (4/1 → 5/1) ⇝ 80/13 | n:1 clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", - "[ 165/52 ⇜ (4/1 → 5/1) ⇝ 25/4 | n:2 clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 85/26 ⇜ (4/1 → 5/1) ⇝ 165/26 | n:3 clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]", - "[ 175/52 ⇜ (4/1 → 5/1) ⇝ 335/52 | n:4 clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]", - "[ 45/13 ⇜ (4/1 → 5/1) ⇝ 85/13 | n:5 clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]", - "[ 45/13 ⇜ (4/1 → 5/1) ⇝ 85/13 | n:1 clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", - "[ 185/52 ⇜ (4/1 → 5/1) ⇝ 345/52 | n:6 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", - "[ 185/52 ⇜ (4/1 → 5/1) ⇝ 345/52 | n:2 clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 95/26 ⇜ (4/1 → 5/1) ⇝ 175/26 | n:7 clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", - "[ 95/26 ⇜ (4/1 → 5/1) ⇝ 175/26 | n:3 clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]", - "[ 15/4 ⇜ (4/1 → 5/1) ⇝ 355/52 | n:8 clip:1 note:E5 s:piano release:0.1 pan:0.6018518518518519 ]", - "[ 15/4 ⇜ (4/1 → 5/1) ⇝ 355/52 | n:4 clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]", - "[ 50/13 ⇜ (4/1 → 5/1) ⇝ 90/13 | n:5 clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]", - "[ 50/13 ⇜ (4/1 → 5/1) ⇝ 90/13 | n:1 clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", - "[ 205/52 ⇜ (4/1 → 5/1) ⇝ 365/52 | n:6 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", - "[ 205/52 ⇜ (4/1 → 5/1) ⇝ 365/52 | n:2 clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ (105/26 → 5/1) ⇝ 185/26 | n:7 clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", - "[ (105/26 → 5/1) ⇝ 185/26 | n:3 clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]", + "[ 40/13 ⇜ (4/1 → 5/1) ⇝ 80/13 | clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", + "[ 165/52 ⇜ (4/1 → 5/1) ⇝ 25/4 | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 85/26 ⇜ (4/1 → 5/1) ⇝ 165/26 | clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]", + "[ 175/52 ⇜ (4/1 → 5/1) ⇝ 335/52 | clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]", + "[ 45/13 ⇜ (4/1 → 5/1) ⇝ 85/13 | clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]", + "[ 45/13 ⇜ (4/1 → 5/1) ⇝ 85/13 | clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", + "[ 185/52 ⇜ (4/1 → 5/1) ⇝ 345/52 | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", + "[ 185/52 ⇜ (4/1 → 5/1) ⇝ 345/52 | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 95/26 ⇜ (4/1 → 5/1) ⇝ 175/26 | clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", + "[ 95/26 ⇜ (4/1 → 5/1) ⇝ 175/26 | clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]", + "[ 15/4 ⇜ (4/1 → 5/1) ⇝ 355/52 | clip:1 note:E5 s:piano release:0.1 pan:0.6018518518518519 ]", + "[ 15/4 ⇜ (4/1 → 5/1) ⇝ 355/52 | clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]", + "[ 50/13 ⇜ (4/1 → 5/1) ⇝ 90/13 | clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]", + "[ 50/13 ⇜ (4/1 → 5/1) ⇝ 90/13 | clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", + "[ 205/52 ⇜ (4/1 → 5/1) ⇝ 365/52 | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", + "[ 205/52 ⇜ (4/1 → 5/1) ⇝ 365/52 | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ (105/26 → 5/1) ⇝ 185/26 | clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", + "[ (105/26 → 5/1) ⇝ 185/26 | clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]", "[ 635/156 → 55/13 | note:bb2 gain:0.8 clip:1 s:piano release:0.1 pan:0.46296296296296297 ]", - "[ (215/52 → 5/1) ⇝ 375/52 | n:8 clip:1 note:E5 s:piano release:0.1 pan:0.6018518518518519 ]", - "[ (215/52 → 5/1) ⇝ 375/52 | n:4 clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]", + "[ (215/52 → 5/1) ⇝ 375/52 | clip:1 note:E5 s:piano release:0.1 pan:0.6018518518518519 ]", + "[ (215/52 → 5/1) ⇝ 375/52 | clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]", "[ 55/13 → 60/13 | note:ab2 gain:0.8 clip:1 s:piano release:0.1 pan:0.4537037037037037 ]", - "[ (55/13 → 5/1) ⇝ 95/13 | n:5 clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]", - "[ (225/52 → 5/1) ⇝ 385/52 | n:6 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", - "[ (115/26 → 5/1) ⇝ 15/2 | n:7 clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", - "[ (235/52 → 5/1) ⇝ 395/52 | n:8 clip:1 note:E5 s:piano release:0.1 pan:0.6018518518518519 ]", + "[ (55/13 → 5/1) ⇝ 95/13 | clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]", + "[ (225/52 → 5/1) ⇝ 385/52 | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", + "[ (115/26 → 5/1) ⇝ 15/2 | clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", + "[ (235/52 → 5/1) ⇝ 395/52 | clip:1 note:E5 s:piano release:0.1 pan:0.6018518518518519 ]", "[ 60/13 → 5/1 | note:gb2 gain:0.8 clip:1 s:piano release:0.1 pan:0.4444444444444444 ]", "[ 5/1 → 70/13 | note:gb2 gain:0.8 clip:1 s:piano release:0.1 pan:0.4444444444444444 ]", "[ 0/1 ⇜ (5/1 → 6/1) ⇝ 80/13 | s:mad ]", - "[ 40/13 ⇜ (5/1 → 6/1) ⇝ 80/13 | n:1 clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", - "[ 165/52 ⇜ (5/1 → 6/1) ⇝ 25/4 | n:2 clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 85/26 ⇜ (5/1 → 6/1) ⇝ 165/26 | n:3 clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]", - "[ 175/52 ⇜ (5/1 → 6/1) ⇝ 335/52 | n:4 clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]", - "[ 45/13 ⇜ (5/1 → 6/1) ⇝ 85/13 | n:5 clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]", - "[ 45/13 ⇜ (5/1 → 6/1) ⇝ 85/13 | n:1 clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", - "[ 185/52 ⇜ (5/1 → 6/1) ⇝ 345/52 | n:6 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", - "[ 185/52 ⇜ (5/1 → 6/1) ⇝ 345/52 | n:2 clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 95/26 ⇜ (5/1 → 6/1) ⇝ 175/26 | n:7 clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", - "[ 95/26 ⇜ (5/1 → 6/1) ⇝ 175/26 | n:3 clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]", - "[ 15/4 ⇜ (5/1 → 6/1) ⇝ 355/52 | n:8 clip:1 note:E5 s:piano release:0.1 pan:0.6018518518518519 ]", - "[ 15/4 ⇜ (5/1 → 6/1) ⇝ 355/52 | n:4 clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]", - "[ 50/13 ⇜ (5/1 → 6/1) ⇝ 90/13 | n:5 clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]", - "[ 50/13 ⇜ (5/1 → 6/1) ⇝ 90/13 | n:1 clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", - "[ 205/52 ⇜ (5/1 → 6/1) ⇝ 365/52 | n:6 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", - "[ 205/52 ⇜ (5/1 → 6/1) ⇝ 365/52 | n:2 clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 105/26 ⇜ (5/1 → 6/1) ⇝ 185/26 | n:7 clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", - "[ 105/26 ⇜ (5/1 → 6/1) ⇝ 185/26 | n:3 clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]", - "[ 215/52 ⇜ (5/1 → 6/1) ⇝ 375/52 | n:8 clip:1 note:E5 s:piano release:0.1 pan:0.6018518518518519 ]", - "[ 215/52 ⇜ (5/1 → 6/1) ⇝ 375/52 | n:4 clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]", - "[ 55/13 ⇜ (5/1 → 6/1) ⇝ 95/13 | n:5 clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]", - "[ 225/52 ⇜ (5/1 → 6/1) ⇝ 385/52 | n:6 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", - "[ 115/26 ⇜ (5/1 → 6/1) ⇝ 15/2 | n:7 clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", - "[ 235/52 ⇜ (5/1 → 6/1) ⇝ 395/52 | n:8 clip:1 note:E5 s:piano release:0.1 pan:0.6018518518518519 ]", + "[ 40/13 ⇜ (5/1 → 6/1) ⇝ 80/13 | clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", + "[ 165/52 ⇜ (5/1 → 6/1) ⇝ 25/4 | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 85/26 ⇜ (5/1 → 6/1) ⇝ 165/26 | clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]", + "[ 175/52 ⇜ (5/1 → 6/1) ⇝ 335/52 | clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]", + "[ 45/13 ⇜ (5/1 → 6/1) ⇝ 85/13 | clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]", + "[ 45/13 ⇜ (5/1 → 6/1) ⇝ 85/13 | clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", + "[ 185/52 ⇜ (5/1 → 6/1) ⇝ 345/52 | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", + "[ 185/52 ⇜ (5/1 → 6/1) ⇝ 345/52 | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 95/26 ⇜ (5/1 → 6/1) ⇝ 175/26 | clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", + "[ 95/26 ⇜ (5/1 → 6/1) ⇝ 175/26 | clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]", + "[ 15/4 ⇜ (5/1 → 6/1) ⇝ 355/52 | clip:1 note:E5 s:piano release:0.1 pan:0.6018518518518519 ]", + "[ 15/4 ⇜ (5/1 → 6/1) ⇝ 355/52 | clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]", + "[ 50/13 ⇜ (5/1 → 6/1) ⇝ 90/13 | clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]", + "[ 50/13 ⇜ (5/1 → 6/1) ⇝ 90/13 | clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", + "[ 205/52 ⇜ (5/1 → 6/1) ⇝ 365/52 | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", + "[ 205/52 ⇜ (5/1 → 6/1) ⇝ 365/52 | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 105/26 ⇜ (5/1 → 6/1) ⇝ 185/26 | clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", + "[ 105/26 ⇜ (5/1 → 6/1) ⇝ 185/26 | clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]", + "[ 215/52 ⇜ (5/1 → 6/1) ⇝ 375/52 | clip:1 note:E5 s:piano release:0.1 pan:0.6018518518518519 ]", + "[ 215/52 ⇜ (5/1 → 6/1) ⇝ 375/52 | clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]", + "[ 55/13 ⇜ (5/1 → 6/1) ⇝ 95/13 | clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]", + "[ 225/52 ⇜ (5/1 → 6/1) ⇝ 385/52 | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", + "[ 115/26 ⇜ (5/1 → 6/1) ⇝ 15/2 | clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", + "[ 235/52 ⇜ (5/1 → 6/1) ⇝ 395/52 | clip:1 note:E5 s:piano release:0.1 pan:0.6018518518518519 ]", "[ (70/13 → 6/1) ⇝ 80/13 | note:gb2 gain:0.8 clip:1 s:piano release:0.1 pan:0.4444444444444444 ]", "[ 0/1 ⇜ (6/1 → 80/13) | s:mad ]", - "[ 40/13 ⇜ (6/1 → 80/13) | n:1 clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", - "[ 165/52 ⇜ (6/1 → 80/13) ⇝ 25/4 | n:2 clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 85/26 ⇜ (6/1 → 80/13) ⇝ 165/26 | n:3 clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]", - "[ 175/52 ⇜ (6/1 → 80/13) ⇝ 335/52 | n:4 clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]", - "[ 45/13 ⇜ (6/1 → 80/13) ⇝ 85/13 | n:5 clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]", - "[ 45/13 ⇜ (6/1 → 80/13) ⇝ 85/13 | n:1 clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", - "[ 185/52 ⇜ (6/1 → 80/13) ⇝ 345/52 | n:6 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", - "[ 185/52 ⇜ (6/1 → 80/13) ⇝ 345/52 | n:2 clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 95/26 ⇜ (6/1 → 80/13) ⇝ 175/26 | n:7 clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", - "[ 95/26 ⇜ (6/1 → 80/13) ⇝ 175/26 | n:3 clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]", - "[ 15/4 ⇜ (6/1 → 80/13) ⇝ 355/52 | n:8 clip:1 note:E5 s:piano release:0.1 pan:0.6018518518518519 ]", - "[ 15/4 ⇜ (6/1 → 80/13) ⇝ 355/52 | n:4 clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]", - "[ 50/13 ⇜ (6/1 → 80/13) ⇝ 90/13 | n:5 clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]", - "[ 50/13 ⇜ (6/1 → 80/13) ⇝ 90/13 | n:1 clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", - "[ 205/52 ⇜ (6/1 → 80/13) ⇝ 365/52 | n:6 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", - "[ 205/52 ⇜ (6/1 → 80/13) ⇝ 365/52 | n:2 clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 105/26 ⇜ (6/1 → 80/13) ⇝ 185/26 | n:7 clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", - "[ 105/26 ⇜ (6/1 → 80/13) ⇝ 185/26 | n:3 clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]", - "[ 215/52 ⇜ (6/1 → 80/13) ⇝ 375/52 | n:8 clip:1 note:E5 s:piano release:0.1 pan:0.6018518518518519 ]", - "[ 215/52 ⇜ (6/1 → 80/13) ⇝ 375/52 | n:4 clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]", - "[ 55/13 ⇜ (6/1 → 80/13) ⇝ 95/13 | n:5 clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]", - "[ 225/52 ⇜ (6/1 → 80/13) ⇝ 385/52 | n:6 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", - "[ 115/26 ⇜ (6/1 → 80/13) ⇝ 15/2 | n:7 clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", - "[ 235/52 ⇜ (6/1 → 80/13) ⇝ 395/52 | n:8 clip:1 note:E5 s:piano release:0.1 pan:0.6018518518518519 ]", + "[ 40/13 ⇜ (6/1 → 80/13) | clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", + "[ 165/52 ⇜ (6/1 → 80/13) ⇝ 25/4 | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 85/26 ⇜ (6/1 → 80/13) ⇝ 165/26 | clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]", + "[ 175/52 ⇜ (6/1 → 80/13) ⇝ 335/52 | clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]", + "[ 45/13 ⇜ (6/1 → 80/13) ⇝ 85/13 | clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]", + "[ 45/13 ⇜ (6/1 → 80/13) ⇝ 85/13 | clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", + "[ 185/52 ⇜ (6/1 → 80/13) ⇝ 345/52 | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", + "[ 185/52 ⇜ (6/1 → 80/13) ⇝ 345/52 | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 95/26 ⇜ (6/1 → 80/13) ⇝ 175/26 | clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", + "[ 95/26 ⇜ (6/1 → 80/13) ⇝ 175/26 | clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]", + "[ 15/4 ⇜ (6/1 → 80/13) ⇝ 355/52 | clip:1 note:E5 s:piano release:0.1 pan:0.6018518518518519 ]", + "[ 15/4 ⇜ (6/1 → 80/13) ⇝ 355/52 | clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]", + "[ 50/13 ⇜ (6/1 → 80/13) ⇝ 90/13 | clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]", + "[ 50/13 ⇜ (6/1 → 80/13) ⇝ 90/13 | clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", + "[ 205/52 ⇜ (6/1 → 80/13) ⇝ 365/52 | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", + "[ 205/52 ⇜ (6/1 → 80/13) ⇝ 365/52 | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 105/26 ⇜ (6/1 → 80/13) ⇝ 185/26 | clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", + "[ 105/26 ⇜ (6/1 → 80/13) ⇝ 185/26 | clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]", + "[ 215/52 ⇜ (6/1 → 80/13) ⇝ 375/52 | clip:1 note:E5 s:piano release:0.1 pan:0.6018518518518519 ]", + "[ 215/52 ⇜ (6/1 → 80/13) ⇝ 375/52 | clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]", + "[ 55/13 ⇜ (6/1 → 80/13) ⇝ 95/13 | clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]", + "[ 225/52 ⇜ (6/1 → 80/13) ⇝ 385/52 | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", + "[ 115/26 ⇜ (6/1 → 80/13) ⇝ 15/2 | clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", + "[ 235/52 ⇜ (6/1 → 80/13) ⇝ 395/52 | clip:1 note:E5 s:piano release:0.1 pan:0.6018518518518519 ]", "[ 70/13 ⇜ (6/1 → 80/13) | note:gb2 gain:0.8 clip:1 s:piano release:0.1 pan:0.4444444444444444 ]", - "[ 165/52 ⇜ (80/13 → 25/4) | n:2 clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 85/26 ⇜ (80/13 → 165/26) | n:3 clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]", - "[ 175/52 ⇜ (80/13 → 335/52) | n:4 clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]", - "[ 45/13 ⇜ (80/13 → 85/13) | n:5 clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]", - "[ 45/13 ⇜ (80/13 → 85/13) | n:1 clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", - "[ 185/52 ⇜ (80/13 → 85/13) ⇝ 345/52 | n:2 clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 95/26 ⇜ (80/13 → 85/13) ⇝ 175/26 | n:3 clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]", - "[ 15/4 ⇜ (80/13 → 85/13) ⇝ 355/52 | n:4 clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]", - "[ 50/13 ⇜ (80/13 → 85/13) ⇝ 90/13 | n:5 clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]", - "[ 205/52 ⇜ (80/13 → 85/13) ⇝ 365/52 | n:6 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", - "[ 105/26 ⇜ (80/13 → 85/13) ⇝ 185/26 | n:7 clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", - "[ 215/52 ⇜ (80/13 → 85/13) ⇝ 375/52 | n:8 clip:1 note:E5 s:piano release:0.1 pan:0.6018518518518519 ]", + "[ 165/52 ⇜ (80/13 → 25/4) | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 85/26 ⇜ (80/13 → 165/26) | clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]", + "[ 175/52 ⇜ (80/13 → 335/52) | clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]", + "[ 45/13 ⇜ (80/13 → 85/13) | clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]", + "[ 45/13 ⇜ (80/13 → 85/13) | clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", + "[ 185/52 ⇜ (80/13 → 85/13) ⇝ 345/52 | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 95/26 ⇜ (80/13 → 85/13) ⇝ 175/26 | clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]", + "[ 15/4 ⇜ (80/13 → 85/13) ⇝ 355/52 | clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]", + "[ 50/13 ⇜ (80/13 → 85/13) ⇝ 90/13 | clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]", + "[ 205/52 ⇜ (80/13 → 85/13) ⇝ 365/52 | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", + "[ 105/26 ⇜ (80/13 → 85/13) ⇝ 185/26 | clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", + "[ 215/52 ⇜ (80/13 → 85/13) ⇝ 375/52 | clip:1 note:E5 s:piano release:0.1 pan:0.6018518518518519 ]", "[ 80/13 → 85/13 | note:c3 gain:0.8 clip:1 s:piano release:0.1 pan:0.4722222222222222 ]", - "[ 185/52 ⇜ (80/13 → 345/52) | n:6 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", - "[ 95/26 ⇜ (80/13 → 175/26) | n:7 clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", - "[ 15/4 ⇜ (80/13 → 355/52) | n:8 clip:1 note:E5 s:piano release:0.1 pan:0.6018518518518519 ]", - "[ 50/13 ⇜ (80/13 → 90/13) | n:1 clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", - "[ 205/52 ⇜ (80/13 → 90/13) ⇝ 365/52 | n:2 clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 105/26 ⇜ (80/13 → 90/13) ⇝ 185/26 | n:3 clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]", - "[ 215/52 ⇜ (80/13 → 90/13) ⇝ 375/52 | n:4 clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]", - "[ 55/13 ⇜ (80/13 → 90/13) ⇝ 95/13 | n:5 clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]", - "[ 225/52 ⇜ (80/13 → 90/13) ⇝ 385/52 | n:6 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", - "[ 115/26 ⇜ (80/13 → 90/13) ⇝ 15/2 | n:7 clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", - "[ 235/52 ⇜ (80/13 → 90/13) ⇝ 395/52 | n:8 clip:1 note:E5 s:piano release:0.1 pan:0.6018518518518519 ]", - "[ (80/13 → 7/1) ⇝ 120/13 | n:2 clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 185/52 ⇜ (80/13 → 345/52) | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", + "[ 95/26 ⇜ (80/13 → 175/26) | clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", + "[ 15/4 ⇜ (80/13 → 355/52) | clip:1 note:E5 s:piano release:0.1 pan:0.6018518518518519 ]", + "[ 50/13 ⇜ (80/13 → 90/13) | clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", + "[ 205/52 ⇜ (80/13 → 90/13) ⇝ 365/52 | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 105/26 ⇜ (80/13 → 90/13) ⇝ 185/26 | clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]", + "[ 215/52 ⇜ (80/13 → 90/13) ⇝ 375/52 | clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]", + "[ 55/13 ⇜ (80/13 → 90/13) ⇝ 95/13 | clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]", + "[ 225/52 ⇜ (80/13 → 90/13) ⇝ 385/52 | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", + "[ 115/26 ⇜ (80/13 → 90/13) ⇝ 15/2 | clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", + "[ 235/52 ⇜ (80/13 → 90/13) ⇝ 395/52 | clip:1 note:E5 s:piano release:0.1 pan:0.6018518518518519 ]", + "[ (80/13 → 7/1) ⇝ 120/13 | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", "[ (80/13 → 7/1) ⇝ 160/13 | s:mad ]", - "[ (25/4 → 7/1) ⇝ 485/52 | n:3 clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]", - "[ (165/26 → 7/1) ⇝ 245/26 | n:4 clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]", - "[ (335/52 → 7/1) ⇝ 495/52 | n:5 clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]", - "[ 185/52 ⇜ (85/13 → 345/52) | n:2 clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 95/26 ⇜ (85/13 → 175/26) | n:3 clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]", - "[ 15/4 ⇜ (85/13 → 355/52) | n:4 clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]", - "[ 50/13 ⇜ (85/13 → 90/13) | n:5 clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]", + "[ (25/4 → 7/1) ⇝ 485/52 | clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]", + "[ (165/26 → 7/1) ⇝ 245/26 | clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]", + "[ (335/52 → 7/1) ⇝ 495/52 | clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]", + "[ 185/52 ⇜ (85/13 → 345/52) | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 95/26 ⇜ (85/13 → 175/26) | clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]", + "[ 15/4 ⇜ (85/13 → 355/52) | clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]", + "[ 50/13 ⇜ (85/13 → 90/13) | clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]", "[ 85/13 → 90/13 | note:c3 gain:0.8 clip:1 s:piano release:0.1 pan:0.4722222222222222 ]", - "[ 205/52 ⇜ (85/13 → 7/1) ⇝ 365/52 | n:6 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", - "[ 105/26 ⇜ (85/13 → 7/1) ⇝ 185/26 | n:7 clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", - "[ 215/52 ⇜ (85/13 → 7/1) ⇝ 375/52 | n:8 clip:1 note:E5 s:piano release:0.1 pan:0.6018518518518519 ]", - "[ (85/13 → 7/1) ⇝ 125/13 | n:6 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", - "[ (85/13 → 7/1) ⇝ 125/13 | n:2 clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ (345/52 → 7/1) ⇝ 505/52 | n:7 clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", - "[ (345/52 → 7/1) ⇝ 505/52 | n:3 clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]", - "[ (175/26 → 7/1) ⇝ 255/26 | n:8 clip:1 note:E5 s:piano release:0.1 pan:0.6018518518518519 ]", - "[ (175/26 → 7/1) ⇝ 255/26 | n:4 clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]", - "[ (355/52 → 7/1) ⇝ 515/52 | n:9 clip:1 note:F#5 s:piano release:0.1 pan:0.6111111111111112 ]", - "[ (355/52 → 7/1) ⇝ 515/52 | n:5 clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]", - "[ 205/52 ⇜ (90/13 → 7/1) ⇝ 365/52 | n:2 clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 105/26 ⇜ (90/13 → 7/1) ⇝ 185/26 | n:3 clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]", - "[ 215/52 ⇜ (90/13 → 7/1) ⇝ 375/52 | n:4 clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]", - "[ 55/13 ⇜ (90/13 → 7/1) ⇝ 95/13 | n:5 clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]", - "[ 225/52 ⇜ (90/13 → 7/1) ⇝ 385/52 | n:6 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", - "[ 115/26 ⇜ (90/13 → 7/1) ⇝ 15/2 | n:7 clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", - "[ 235/52 ⇜ (90/13 → 7/1) ⇝ 395/52 | n:8 clip:1 note:E5 s:piano release:0.1 pan:0.6018518518518519 ]", + "[ 205/52 ⇜ (85/13 → 7/1) ⇝ 365/52 | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", + "[ 105/26 ⇜ (85/13 → 7/1) ⇝ 185/26 | clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", + "[ 215/52 ⇜ (85/13 → 7/1) ⇝ 375/52 | clip:1 note:E5 s:piano release:0.1 pan:0.6018518518518519 ]", + "[ (85/13 → 7/1) ⇝ 125/13 | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", + "[ (85/13 → 7/1) ⇝ 125/13 | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ (345/52 → 7/1) ⇝ 505/52 | clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", + "[ (345/52 → 7/1) ⇝ 505/52 | clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]", + "[ (175/26 → 7/1) ⇝ 255/26 | clip:1 note:E5 s:piano release:0.1 pan:0.6018518518518519 ]", + "[ (175/26 → 7/1) ⇝ 255/26 | clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]", + "[ (355/52 → 7/1) ⇝ 515/52 | clip:1 note:F#5 s:piano release:0.1 pan:0.6111111111111112 ]", + "[ (355/52 → 7/1) ⇝ 515/52 | clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]", + "[ 205/52 ⇜ (90/13 → 7/1) ⇝ 365/52 | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 105/26 ⇜ (90/13 → 7/1) ⇝ 185/26 | clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]", + "[ 215/52 ⇜ (90/13 → 7/1) ⇝ 375/52 | clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]", + "[ 55/13 ⇜ (90/13 → 7/1) ⇝ 95/13 | clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]", + "[ 225/52 ⇜ (90/13 → 7/1) ⇝ 385/52 | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", + "[ 115/26 ⇜ (90/13 → 7/1) ⇝ 15/2 | clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", + "[ 235/52 ⇜ (90/13 → 7/1) ⇝ 395/52 | clip:1 note:E5 s:piano release:0.1 pan:0.6018518518518519 ]", "[ (90/13 → 7/1) ⇝ 1115/156 | note:c3 gain:0.8 clip:1 s:piano release:0.1 pan:0.4722222222222222 ]", - "[ (90/13 → 7/1) ⇝ 10/1 | n:6 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", - "[ (90/13 → 7/1) ⇝ 10/1 | n:2 clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 205/52 ⇜ (7/1 → 365/52) | n:6 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", - "[ 205/52 ⇜ (7/1 → 365/52) | n:2 clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 105/26 ⇜ (7/1 → 185/26) | n:7 clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", - "[ 105/26 ⇜ (7/1 → 185/26) | n:3 clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]", + "[ (90/13 → 7/1) ⇝ 10/1 | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", + "[ (90/13 → 7/1) ⇝ 10/1 | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 205/52 ⇜ (7/1 → 365/52) | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", + "[ 205/52 ⇜ (7/1 → 365/52) | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 105/26 ⇜ (7/1 → 185/26) | clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", + "[ 105/26 ⇜ (7/1 → 185/26) | clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]", "[ 90/13 ⇜ (7/1 → 1115/156) | note:c3 gain:0.8 clip:1 s:piano release:0.1 pan:0.4722222222222222 ]", - "[ 215/52 ⇜ (7/1 → 375/52) | n:8 clip:1 note:E5 s:piano release:0.1 pan:0.6018518518518519 ]", - "[ 215/52 ⇜ (7/1 → 375/52) | n:4 clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]", - "[ 55/13 ⇜ (7/1 → 95/13) | n:5 clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]", - "[ 225/52 ⇜ (7/1 → 385/52) | n:6 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", - "[ 115/26 ⇜ (7/1 → 15/2) | n:7 clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", - "[ 235/52 ⇜ (7/1 → 395/52) | n:8 clip:1 note:E5 s:piano release:0.1 pan:0.6018518518518519 ]", - "[ 80/13 ⇜ (7/1 → 8/1) ⇝ 120/13 | n:2 clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 215/52 ⇜ (7/1 → 375/52) | clip:1 note:E5 s:piano release:0.1 pan:0.6018518518518519 ]", + "[ 215/52 ⇜ (7/1 → 375/52) | clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]", + "[ 55/13 ⇜ (7/1 → 95/13) | clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]", + "[ 225/52 ⇜ (7/1 → 385/52) | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", + "[ 115/26 ⇜ (7/1 → 15/2) | clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", + "[ 235/52 ⇜ (7/1 → 395/52) | clip:1 note:E5 s:piano release:0.1 pan:0.6018518518518519 ]", + "[ 80/13 ⇜ (7/1 → 8/1) ⇝ 120/13 | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", "[ 80/13 ⇜ (7/1 → 8/1) ⇝ 160/13 | s:mad ]", - "[ 25/4 ⇜ (7/1 → 8/1) ⇝ 485/52 | n:3 clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]", - "[ 165/26 ⇜ (7/1 → 8/1) ⇝ 245/26 | n:4 clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]", - "[ 335/52 ⇜ (7/1 → 8/1) ⇝ 495/52 | n:5 clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]", - "[ 85/13 ⇜ (7/1 → 8/1) ⇝ 125/13 | n:6 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", - "[ 85/13 ⇜ (7/1 → 8/1) ⇝ 125/13 | n:2 clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 345/52 ⇜ (7/1 → 8/1) ⇝ 505/52 | n:7 clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", - "[ 345/52 ⇜ (7/1 → 8/1) ⇝ 505/52 | n:3 clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]", - "[ 175/26 ⇜ (7/1 → 8/1) ⇝ 255/26 | n:8 clip:1 note:E5 s:piano release:0.1 pan:0.6018518518518519 ]", - "[ 175/26 ⇜ (7/1 → 8/1) ⇝ 255/26 | n:4 clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]", - "[ 355/52 ⇜ (7/1 → 8/1) ⇝ 515/52 | n:9 clip:1 note:F#5 s:piano release:0.1 pan:0.6111111111111112 ]", - "[ 355/52 ⇜ (7/1 → 8/1) ⇝ 515/52 | n:5 clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]", - "[ 90/13 ⇜ (7/1 → 8/1) ⇝ 10/1 | n:6 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", - "[ 90/13 ⇜ (7/1 → 8/1) ⇝ 10/1 | n:2 clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ (365/52 → 8/1) ⇝ 525/52 | n:7 clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", - "[ (365/52 → 8/1) ⇝ 525/52 | n:3 clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]", - "[ (185/26 → 8/1) ⇝ 265/26 | n:8 clip:1 note:E5 s:piano release:0.1 pan:0.6018518518518519 ]", - "[ (185/26 → 8/1) ⇝ 265/26 | n:4 clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]", + "[ 25/4 ⇜ (7/1 → 8/1) ⇝ 485/52 | clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]", + "[ 165/26 ⇜ (7/1 → 8/1) ⇝ 245/26 | clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]", + "[ 335/52 ⇜ (7/1 → 8/1) ⇝ 495/52 | clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]", + "[ 85/13 ⇜ (7/1 → 8/1) ⇝ 125/13 | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", + "[ 85/13 ⇜ (7/1 → 8/1) ⇝ 125/13 | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 345/52 ⇜ (7/1 → 8/1) ⇝ 505/52 | clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", + "[ 345/52 ⇜ (7/1 → 8/1) ⇝ 505/52 | clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]", + "[ 175/26 ⇜ (7/1 → 8/1) ⇝ 255/26 | clip:1 note:E5 s:piano release:0.1 pan:0.6018518518518519 ]", + "[ 175/26 ⇜ (7/1 → 8/1) ⇝ 255/26 | clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]", + "[ 355/52 ⇜ (7/1 → 8/1) ⇝ 515/52 | clip:1 note:F#5 s:piano release:0.1 pan:0.6111111111111112 ]", + "[ 355/52 ⇜ (7/1 → 8/1) ⇝ 515/52 | clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]", + "[ 90/13 ⇜ (7/1 → 8/1) ⇝ 10/1 | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", + "[ 90/13 ⇜ (7/1 → 8/1) ⇝ 10/1 | clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ (365/52 → 8/1) ⇝ 525/52 | clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", + "[ (365/52 → 8/1) ⇝ 525/52 | clip:1 note:F#4 s:piano release:0.1 pan:0.5555555555555556 ]", + "[ (185/26 → 8/1) ⇝ 265/26 | clip:1 note:E5 s:piano release:0.1 pan:0.6018518518518519 ]", + "[ (185/26 → 8/1) ⇝ 265/26 | clip:1 note:G#4 s:piano release:0.1 pan:0.5648148148148149 ]", "[ 1115/156 → 95/13 | note:bb2 gain:0.8 clip:1 s:piano release:0.1 pan:0.46296296296296297 ]", - "[ (375/52 → 8/1) ⇝ 535/52 | n:9 clip:1 note:F#5 s:piano release:0.1 pan:0.6111111111111112 ]", - "[ (375/52 → 8/1) ⇝ 535/52 | n:5 clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]", + "[ (375/52 → 8/1) ⇝ 535/52 | clip:1 note:F#5 s:piano release:0.1 pan:0.6111111111111112 ]", + "[ (375/52 → 8/1) ⇝ 535/52 | clip:1 note:A#4 s:piano release:0.1 pan:0.5740740740740741 ]", "[ 95/13 → 100/13 | note:ab2 gain:0.8 clip:1 s:piano release:0.1 pan:0.4537037037037037 ]", - "[ (95/13 → 8/1) ⇝ 135/13 | n:6 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", - "[ (385/52 → 8/1) ⇝ 545/52 | n:7 clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", - "[ (15/2 → 8/1) ⇝ 275/26 | n:8 clip:1 note:E5 s:piano release:0.1 pan:0.6018518518518519 ]", - "[ (395/52 → 8/1) ⇝ 555/52 | n:9 clip:1 note:F#5 s:piano release:0.1 pan:0.6111111111111112 ]", + "[ (95/13 → 8/1) ⇝ 135/13 | clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", + "[ (385/52 → 8/1) ⇝ 545/52 | clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", + "[ (15/2 → 8/1) ⇝ 275/26 | clip:1 note:E5 s:piano release:0.1 pan:0.6018518518518519 ]", + "[ (395/52 → 8/1) ⇝ 555/52 | clip:1 note:F#5 s:piano release:0.1 pan:0.6111111111111112 ]", "[ (100/13 → 8/1) ⇝ 105/13 | note:gb2 gain:0.8 clip:1 s:piano release:0.1 pan:0.4444444444444444 ]", ] `; @@ -1563,394 +1563,394 @@ exports[`renders tunes > tune: dinofunk 1`] = ` exports[`renders tunes > tune: echoPiano 1`] = ` [ - "[ -3/8 ⇜ (0/1 → 1/8) | n:3 note:G3 clip:1 s:piano release:0.1 pan:0.5046296296296297 ]", - "[ -3/8 ⇜ (0/1 → 1/8) | n:9 note:F4 clip:1 s:piano release:0.1 pan:0.5509259259259259 ]", - "[ -3/8 ⇜ (0/1 → 1/8) | n:5 note:Bb3 clip:1 s:piano release:0.1 pan:0.5185185185185186 ]", - "[ -1/8 ⇜ (0/1 → 1/8) | n:14 note:D5 clip:1 s:piano release:0.1 pan:0.5925925925925926 ]", - "[ -1/8 ⇜ (0/1 → 1/8) ⇝ 3/8 | n:5 note:Bb3 clip:1 s:piano release:0.1 pan:0.5185185185185186 ]", - "[ -1/8 ⇜ (0/1 → 1/8) ⇝ 3/8 | n:11 note:A4 clip:1 s:piano release:0.1 pan:0.5694444444444444 ]", - "[ -1/4 ⇜ (0/1 → 1/4) | n:5 note:Bb3 clip:1 s:piano release:0.1 pan:0.5185185185185186 ]", - "[ -1/4 ⇜ (0/1 → 1/4) | n:11 note:A4 clip:1 s:piano release:0.1 pan:0.5694444444444444 ]", - "[ -1/4 ⇜ (0/1 → 1/4) | n:3 note:G3 clip:1 s:piano release:0.1 pan:0.5046296296296297 ]", - "[ -1/4 ⇜ (0/1 → 1/4) | n:9 note:F4 clip:1 s:piano release:0.1 pan:0.5509259259259259 ]", - "[ (0/1 → 1/4) ⇝ 1/2 | n:5 note:Bb3 clip:1 s:piano release:0.1 pan:0.5185185185185186 ]", - "[ (0/1 → 1/4) ⇝ 1/2 | n:11 note:A4 clip:1 s:piano release:0.1 pan:0.5694444444444444 ]", - "[ -1/8 ⇜ (0/1 → 3/8) | n:3 note:G3 clip:1 s:piano release:0.1 pan:0.5046296296296297 ]", - "[ -1/8 ⇜ (0/1 → 3/8) | n:9 note:F4 clip:1 s:piano release:0.1 pan:0.5509259259259259 ]", - "[ 0/1 → 1/2 | n:9 note:F4 clip:1 s:piano release:0.1 pan:0.5509259259259259 ]", - "[ 0/1 → 1/1 | n:0 note:D3 clip:1 s:piano release:0.1 pan:0.4814814814814815 ]", - "[ -1/8 ⇜ (1/8 → 3/8) | n:5 note:Bb3 clip:1 s:piano release:0.1 pan:0.5185185185185186 ]", - "[ -1/8 ⇜ (1/8 → 3/8) | n:11 note:A4 clip:1 s:piano release:0.1 pan:0.5694444444444444 ]", - "[ (1/8 → 3/8) ⇝ 5/8 | n:5 note:Bb3 clip:1 s:piano release:0.1 pan:0.5185185185185186 ]", - "[ (1/8 → 3/8) ⇝ 5/8 | n:11 note:A4 clip:1 s:piano release:0.1 pan:0.5694444444444444 ]", - "[ 1/8 → 5/8 | n:9 note:F4 clip:1 s:piano release:0.1 pan:0.5509259259259259 ]", - "[ (1/8 → 1/1) ⇝ 9/8 | n:0 note:D3 clip:1 s:piano release:0.1 pan:0.4814814814814815 ]", - "[ 0/1 ⇜ (1/4 → 1/2) | n:5 note:Bb3 clip:1 s:piano release:0.1 pan:0.5185185185185186 ]", - "[ 0/1 ⇜ (1/4 → 1/2) | n:11 note:A4 clip:1 s:piano release:0.1 pan:0.5694444444444444 ]", - "[ (1/4 → 1/2) ⇝ 3/4 | n:11 note:A4 clip:1 s:piano release:0.1 pan:0.5694444444444444 ]", - "[ 1/4 → 3/4 | n:9 note:F4 clip:1 s:piano release:0.1 pan:0.5509259259259259 ]", - "[ (1/4 → 1/1) ⇝ 5/4 | n:2 note:F3 clip:1 s:piano release:0.1 pan:0.49537037037037035 ]", - "[ (1/4 → 1/1) ⇝ 5/4 | n:0 note:D3 clip:1 s:piano release:0.1 pan:0.4814814814814815 ]", - "[ 1/8 ⇜ (3/8 → 5/8) | n:5 note:Bb3 clip:1 s:piano release:0.1 pan:0.5185185185185186 ]", - "[ 1/8 ⇜ (3/8 → 5/8) | n:11 note:A4 clip:1 s:piano release:0.1 pan:0.5694444444444444 ]", - "[ (3/8 → 5/8) ⇝ 7/8 | n:11 note:A4 clip:1 s:piano release:0.1 pan:0.5694444444444444 ]", - "[ 3/8 → 7/8 | n:9 note:F4 clip:1 s:piano release:0.1 pan:0.5509259259259259 ]", - "[ (3/8 → 1/1) ⇝ 11/8 | n:2 note:F3 clip:1 s:piano release:0.1 pan:0.49537037037037035 ]", - "[ (3/8 → 1/1) ⇝ 11/8 | n:0 note:D3 clip:1 s:piano release:0.1 pan:0.4814814814814815 ]", - "[ 1/4 ⇜ (1/2 → 3/4) | n:11 note:A4 clip:1 s:piano release:0.1 pan:0.5694444444444444 ]", - "[ (1/2 → 3/4) ⇝ 1/1 | n:11 note:A4 clip:1 s:piano release:0.1 pan:0.5694444444444444 ]", - "[ (1/2 → 1/1) ⇝ 3/2 | n:6 note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ (1/2 → 1/1) ⇝ 3/2 | n:2 note:F3 clip:1 s:piano release:0.1 pan:0.49537037037037035 ]", - "[ 3/8 ⇜ (5/8 → 7/8) | n:11 note:A4 clip:1 s:piano release:0.1 pan:0.5694444444444444 ]", - "[ (5/8 → 7/8) ⇝ 9/8 | n:11 note:A4 clip:1 s:piano release:0.1 pan:0.5694444444444444 ]", - "[ (5/8 → 1/1) ⇝ 13/8 | n:6 note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ (5/8 → 1/1) ⇝ 13/8 | n:2 note:F3 clip:1 s:piano release:0.1 pan:0.49537037037037035 ]", - "[ 1/2 ⇜ (3/4 → 1/1) | n:11 note:A4 clip:1 s:piano release:0.1 pan:0.5694444444444444 ]", - "[ (3/4 → 1/1) ⇝ 7/4 | n:8 note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ (3/4 → 1/1) ⇝ 7/4 | n:6 note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ 5/8 ⇜ (7/8 → 1/1) ⇝ 9/8 | n:11 note:A4 clip:1 s:piano release:0.1 pan:0.5694444444444444 ]", - "[ (7/8 → 1/1) ⇝ 15/8 | n:8 note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ (7/8 → 1/1) ⇝ 15/8 | n:6 note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ 1/8 ⇜ (1/1 → 9/8) | n:0 note:D3 clip:1 s:piano release:0.1 pan:0.4814814814814815 ]", - "[ 3/8 ⇜ (1/1 → 9/8) ⇝ 11/8 | n:2 note:F3 clip:1 s:piano release:0.1 pan:0.49537037037037035 ]", - "[ 5/8 ⇜ (1/1 → 9/8) | n:11 note:A4 clip:1 s:piano release:0.1 pan:0.5694444444444444 ]", - "[ 5/8 ⇜ (1/1 → 9/8) ⇝ 13/8 | n:6 note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ 7/8 ⇜ (1/1 → 9/8) ⇝ 15/8 | n:8 note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 1/4 ⇜ (1/1 → 5/4) | n:2 note:F3 clip:1 s:piano release:0.1 pan:0.49537037037037035 ]", - "[ 1/4 ⇜ (1/1 → 5/4) | n:0 note:D3 clip:1 s:piano release:0.1 pan:0.4814814814814815 ]", - "[ 1/2 ⇜ (1/1 → 5/4) ⇝ 3/2 | n:2 note:F3 clip:1 s:piano release:0.1 pan:0.49537037037037035 ]", - "[ 3/4 ⇜ (1/1 → 5/4) ⇝ 7/4 | n:6 note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ (1/1 → 5/4) ⇝ 2/1 | n:8 note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 3/8 ⇜ (1/1 → 11/8) | n:0 note:D3 clip:1 s:piano release:0.1 pan:0.4814814814814815 ]", - "[ 5/8 ⇜ (1/1 → 11/8) ⇝ 13/8 | n:2 note:F3 clip:1 s:piano release:0.1 pan:0.49537037037037035 ]", - "[ 7/8 ⇜ (1/1 → 11/8) ⇝ 15/8 | n:6 note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ 1/2 ⇜ (1/1 → 3/2) | n:6 note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ 3/4 ⇜ (1/1 → 3/2) ⇝ 7/4 | n:8 note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 1/1 → 2/1 | n:2 note:F3 clip:1 s:piano release:0.1 pan:0.49537037037037035 ]", - "[ 3/8 ⇜ (9/8 → 11/8) | n:2 note:F3 clip:1 s:piano release:0.1 pan:0.49537037037037035 ]", - "[ (9/8 → 11/8) ⇝ 17/8 | n:8 note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 5/8 ⇜ (9/8 → 13/8) | n:6 note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ 7/8 ⇜ (9/8 → 13/8) ⇝ 15/8 | n:8 note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ (9/8 → 2/1) ⇝ 17/8 | n:2 note:F3 clip:1 s:piano release:0.1 pan:0.49537037037037035 ]", - "[ 1/2 ⇜ (5/4 → 3/2) | n:2 note:F3 clip:1 s:piano release:0.1 pan:0.49537037037037035 ]", - "[ 3/4 ⇜ (5/4 → 7/4) | n:6 note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ 1/1 ⇜ (5/4 → 7/4) ⇝ 2/1 | n:8 note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ (5/4 → 2/1) ⇝ 9/4 | n:4 note:A3 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", - "[ (5/4 → 2/1) ⇝ 9/4 | n:2 note:F3 clip:1 s:piano release:0.1 pan:0.49537037037037035 ]", - "[ 5/8 ⇜ (11/8 → 13/8) | n:2 note:F3 clip:1 s:piano release:0.1 pan:0.49537037037037035 ]", - "[ 7/8 ⇜ (11/8 → 15/8) | n:6 note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ 9/8 ⇜ (11/8 → 15/8) ⇝ 17/8 | n:8 note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ (11/8 → 2/1) ⇝ 19/8 | n:4 note:A3 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", - "[ (11/8 → 2/1) ⇝ 19/8 | n:2 note:F3 clip:1 s:piano release:0.1 pan:0.49537037037037035 ]", - "[ 3/4 ⇜ (3/2 → 7/4) | n:8 note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ (3/2 → 2/1) ⇝ 5/2 | n:8 note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ (3/2 → 2/1) ⇝ 5/2 | n:4 note:A3 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", - "[ 7/8 ⇜ (13/8 → 15/8) | n:8 note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ (13/8 → 2/1) ⇝ 21/8 | n:8 note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ (13/8 → 2/1) ⇝ 21/8 | n:4 note:A3 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", - "[ 1/1 ⇜ (7/4 → 2/1) | n:8 note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ (7/4 → 2/1) ⇝ 11/4 | n:10 note:G4 clip:1 s:piano release:0.1 pan:0.5601851851851851 ]", - "[ (7/4 → 2/1) ⇝ 11/4 | n:8 note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 9/8 ⇜ (15/8 → 2/1) ⇝ 17/8 | n:8 note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ (15/8 → 2/1) ⇝ 23/8 | n:10 note:G4 clip:1 s:piano release:0.1 pan:0.5601851851851851 ]", - "[ (15/8 → 2/1) ⇝ 23/8 | n:8 note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 9/8 ⇜ (2/1 → 17/8) | n:2 note:F3 clip:1 s:piano release:0.1 pan:0.49537037037037035 ]", - "[ 9/8 ⇜ (2/1 → 17/8) | n:8 note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 11/8 ⇜ (2/1 → 17/8) ⇝ 19/8 | n:4 note:A3 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", - "[ 13/8 ⇜ (2/1 → 17/8) ⇝ 21/8 | n:8 note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 15/8 ⇜ (2/1 → 17/8) ⇝ 23/8 | n:10 note:G4 clip:1 s:piano release:0.1 pan:0.5601851851851851 ]", - "[ 5/4 ⇜ (2/1 → 9/4) | n:4 note:A3 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", - "[ 5/4 ⇜ (2/1 → 9/4) | n:2 note:F3 clip:1 s:piano release:0.1 pan:0.49537037037037035 ]", - "[ 3/2 ⇜ (2/1 → 9/4) ⇝ 5/2 | n:4 note:A3 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", - "[ 7/4 ⇜ (2/1 → 9/4) ⇝ 11/4 | n:8 note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 2/1 → 9/4 | n:4 note:A3 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", - "[ (2/1 → 9/4) ⇝ 3/1 | n:10 note:G4 clip:1 s:piano release:0.1 pan:0.5601851851851851 ]", - "[ 11/8 ⇜ (2/1 → 19/8) | n:2 note:F3 clip:1 s:piano release:0.1 pan:0.49537037037037035 ]", - "[ 13/8 ⇜ (2/1 → 19/8) ⇝ 21/8 | n:4 note:A3 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", - "[ 15/8 ⇜ (2/1 → 19/8) ⇝ 23/8 | n:8 note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 3/2 ⇜ (2/1 → 5/2) | n:8 note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 7/4 ⇜ (2/1 → 5/2) ⇝ 11/4 | n:10 note:G4 clip:1 s:piano release:0.1 pan:0.5601851851851851 ]", - "[ 11/8 ⇜ (17/8 → 19/8) | n:4 note:A3 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", - "[ 17/8 → 19/8 | n:4 note:A3 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", - "[ (17/8 → 19/8) ⇝ 25/8 | n:10 note:G4 clip:1 s:piano release:0.1 pan:0.5601851851851851 ]", - "[ 13/8 ⇜ (17/8 → 21/8) | n:8 note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 15/8 ⇜ (17/8 → 21/8) ⇝ 23/8 | n:10 note:G4 clip:1 s:piano release:0.1 pan:0.5601851851851851 ]", - "[ 3/2 ⇜ (9/4 → 5/2) | n:4 note:A3 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", - "[ 9/4 → 5/2 | n:6 note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ 9/4 → 5/2 | n:4 note:A3 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", - "[ 7/4 ⇜ (9/4 → 11/4) | n:8 note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 2/1 ⇜ (9/4 → 11/4) ⇝ 3/1 | n:10 note:G4 clip:1 s:piano release:0.1 pan:0.5601851851851851 ]", - "[ 13/8 ⇜ (19/8 → 21/8) | n:4 note:A3 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", - "[ 19/8 → 21/8 | n:6 note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ 19/8 → 21/8 | n:4 note:A3 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", - "[ 15/8 ⇜ (19/8 → 23/8) | n:8 note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 17/8 ⇜ (19/8 → 23/8) ⇝ 25/8 | n:10 note:G4 clip:1 s:piano release:0.1 pan:0.5601851851851851 ]", - "[ 7/4 ⇜ (5/2 → 11/4) | n:10 note:G4 clip:1 s:piano release:0.1 pan:0.5601851851851851 ]", - "[ 5/2 → 11/4 | n:6 note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ 5/2 → 11/4 | n:10 note:G4 clip:1 s:piano release:0.1 pan:0.5601851851851851 ]", - "[ 5/2 → 11/4 | n:6 note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ 15/8 ⇜ (21/8 → 23/8) | n:10 note:G4 clip:1 s:piano release:0.1 pan:0.5601851851851851 ]", - "[ 21/8 → 23/8 | n:6 note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ 21/8 → 23/8 | n:10 note:G4 clip:1 s:piano release:0.1 pan:0.5601851851851851 ]", - "[ 21/8 → 23/8 | n:6 note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ 2/1 ⇜ (11/4 → 3/1) | n:10 note:G4 clip:1 s:piano release:0.1 pan:0.5601851851851851 ]", - "[ 11/4 → 3/1 | n:6 note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ 11/4 → 3/1 | n:8 note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 11/4 → 3/1 | n:12 note:Bb4 clip:1 s:piano release:0.1 pan:0.5740740740740741 ]", - "[ 11/4 → 3/1 | n:6 note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ 11/4 → 3/1 | n:10 note:G4 clip:1 s:piano release:0.1 pan:0.5601851851851851 ]", - "[ 17/8 ⇜ (23/8 → 3/1) ⇝ 25/8 | n:10 note:G4 clip:1 s:piano release:0.1 pan:0.5601851851851851 ]", - "[ (23/8 → 3/1) ⇝ 25/8 | n:6 note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ (23/8 → 3/1) ⇝ 25/8 | n:8 note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ (23/8 → 3/1) ⇝ 25/8 | n:12 note:Bb4 clip:1 s:piano release:0.1 pan:0.5740740740740741 ]", - "[ (23/8 → 3/1) ⇝ 25/8 | n:6 note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ (23/8 → 3/1) ⇝ 25/8 | n:10 note:G4 clip:1 s:piano release:0.1 pan:0.5601851851851851 ]", - "[ 17/8 ⇜ (3/1 → 25/8) | n:10 note:G4 clip:1 s:piano release:0.1 pan:0.5601851851851851 ]", - "[ 23/8 ⇜ (3/1 → 25/8) | n:6 note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ 23/8 ⇜ (3/1 → 25/8) | n:8 note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 23/8 ⇜ (3/1 → 25/8) | n:12 note:Bb4 clip:1 s:piano release:0.1 pan:0.5740740740740741 ]", - "[ 23/8 ⇜ (3/1 → 25/8) | n:6 note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ 23/8 ⇜ (3/1 → 25/8) | n:10 note:G4 clip:1 s:piano release:0.1 pan:0.5601851851851851 ]", - "[ 3/1 → 13/4 | n:8 note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 3/1 → 13/4 | n:12 note:Bb4 clip:1 s:piano release:0.1 pan:0.5740740740740741 ]", - "[ 3/1 → 13/4 | n:6 note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ 3/1 → 13/4 | n:8 note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 3/1 → 13/4 | n:12 note:Bb4 clip:1 s:piano release:0.1 pan:0.5740740740740741 ]", - "[ 3/1 → 7/2 | n:3 note:G3 clip:1 s:piano release:0.1 pan:0.5046296296296297 ]", - "[ 25/8 → 27/8 | n:8 note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 25/8 → 27/8 | n:12 note:Bb4 clip:1 s:piano release:0.1 pan:0.5740740740740741 ]", - "[ 25/8 → 27/8 | n:6 note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ 25/8 → 27/8 | n:8 note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 25/8 → 27/8 | n:12 note:Bb4 clip:1 s:piano release:0.1 pan:0.5740740740740741 ]", - "[ 25/8 → 29/8 | n:3 note:G3 clip:1 s:piano release:0.1 pan:0.5046296296296297 ]", - "[ 13/4 → 7/2 | n:12 note:Bb4 clip:1 s:piano release:0.1 pan:0.5740740740740741 ]", - "[ 13/4 → 7/2 | n:14 note:D5 clip:1 s:piano release:0.1 pan:0.5925925925925926 ]", - "[ 13/4 → 7/2 | n:8 note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 13/4 → 7/2 | n:12 note:Bb4 clip:1 s:piano release:0.1 pan:0.5740740740740741 ]", - "[ 13/4 → 15/4 | n:5 note:Bb3 clip:1 s:piano release:0.1 pan:0.5185185185185186 ]", - "[ 13/4 → 15/4 | n:3 note:G3 clip:1 s:piano release:0.1 pan:0.5046296296296297 ]", - "[ 27/8 → 29/8 | n:12 note:Bb4 clip:1 s:piano release:0.1 pan:0.5740740740740741 ]", - "[ 27/8 → 29/8 | n:14 note:D5 clip:1 s:piano release:0.1 pan:0.5925925925925926 ]", - "[ 27/8 → 29/8 | n:8 note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 27/8 → 29/8 | n:12 note:Bb4 clip:1 s:piano release:0.1 pan:0.5740740740740741 ]", - "[ 27/8 → 31/8 | n:5 note:Bb3 clip:1 s:piano release:0.1 pan:0.5185185185185186 ]", - "[ 27/8 → 31/8 | n:3 note:G3 clip:1 s:piano release:0.1 pan:0.5046296296296297 ]", - "[ 7/2 → 15/4 | n:14 note:D5 clip:1 s:piano release:0.1 pan:0.5925925925925926 ]", - "[ 7/2 → 15/4 | n:12 note:Bb4 clip:1 s:piano release:0.1 pan:0.5740740740740741 ]", - "[ 7/2 → 15/4 | n:14 note:D5 clip:1 s:piano release:0.1 pan:0.5925925925925926 ]", - "[ 7/2 → 4/1 | n:3 note:G3 clip:1 s:piano release:0.1 pan:0.5046296296296297 ]", - "[ 7/2 → 4/1 | n:9 note:F4 clip:1 s:piano release:0.1 pan:0.5509259259259259 ]", - "[ 7/2 → 4/1 | n:5 note:Bb3 clip:1 s:piano release:0.1 pan:0.5185185185185186 ]", - "[ 29/8 → 31/8 | n:14 note:D5 clip:1 s:piano release:0.1 pan:0.5925925925925926 ]", - "[ 29/8 → 31/8 | n:12 note:Bb4 clip:1 s:piano release:0.1 pan:0.5740740740740741 ]", - "[ 29/8 → 31/8 | n:14 note:D5 clip:1 s:piano release:0.1 pan:0.5925925925925926 ]", - "[ (29/8 → 4/1) ⇝ 33/8 | n:3 note:G3 clip:1 s:piano release:0.1 pan:0.5046296296296297 ]", - "[ (29/8 → 4/1) ⇝ 33/8 | n:9 note:F4 clip:1 s:piano release:0.1 pan:0.5509259259259259 ]", - "[ (29/8 → 4/1) ⇝ 33/8 | n:5 note:Bb3 clip:1 s:piano release:0.1 pan:0.5185185185185186 ]", - "[ 15/4 → 4/1 | n:14 note:D5 clip:1 s:piano release:0.1 pan:0.5925925925925926 ]", - "[ (15/4 → 4/1) ⇝ 17/4 | n:5 note:Bb3 clip:1 s:piano release:0.1 pan:0.5185185185185186 ]", - "[ (15/4 → 4/1) ⇝ 17/4 | n:11 note:A4 clip:1 s:piano release:0.1 pan:0.5694444444444444 ]", - "[ (15/4 → 4/1) ⇝ 17/4 | n:3 note:G3 clip:1 s:piano release:0.1 pan:0.5046296296296297 ]", - "[ (15/4 → 4/1) ⇝ 17/4 | n:9 note:F4 clip:1 s:piano release:0.1 pan:0.5509259259259259 ]", - "[ (31/8 → 4/1) ⇝ 33/8 | n:14 note:D5 clip:1 s:piano release:0.1 pan:0.5925925925925926 ]", - "[ (31/8 → 4/1) ⇝ 35/8 | n:5 note:Bb3 clip:1 s:piano release:0.1 pan:0.5185185185185186 ]", - "[ (31/8 → 4/1) ⇝ 35/8 | n:11 note:A4 clip:1 s:piano release:0.1 pan:0.5694444444444444 ]", - "[ (31/8 → 4/1) ⇝ 35/8 | n:3 note:G3 clip:1 s:piano release:0.1 pan:0.5046296296296297 ]", - "[ (31/8 → 4/1) ⇝ 35/8 | n:9 note:F4 clip:1 s:piano release:0.1 pan:0.5509259259259259 ]", - "[ 29/8 ⇜ (4/1 → 33/8) | n:3 note:G3 clip:1 s:piano release:0.1 pan:0.5046296296296297 ]", - "[ 29/8 ⇜ (4/1 → 33/8) | n:9 note:F4 clip:1 s:piano release:0.1 pan:0.5509259259259259 ]", - "[ 29/8 ⇜ (4/1 → 33/8) | n:5 note:Bb3 clip:1 s:piano release:0.1 pan:0.5185185185185186 ]", - "[ 31/8 ⇜ (4/1 → 33/8) | n:14 note:D5 clip:1 s:piano release:0.1 pan:0.5925925925925926 ]", - "[ 31/8 ⇜ (4/1 → 33/8) ⇝ 35/8 | n:5 note:Bb3 clip:1 s:piano release:0.1 pan:0.5185185185185186 ]", - "[ 31/8 ⇜ (4/1 → 33/8) ⇝ 35/8 | n:11 note:A4 clip:1 s:piano release:0.1 pan:0.5694444444444444 ]", - "[ 15/4 ⇜ (4/1 → 17/4) | n:5 note:Bb3 clip:1 s:piano release:0.1 pan:0.5185185185185186 ]", - "[ 15/4 ⇜ (4/1 → 17/4) | n:11 note:A4 clip:1 s:piano release:0.1 pan:0.5694444444444444 ]", - "[ 15/4 ⇜ (4/1 → 17/4) | n:3 note:G3 clip:1 s:piano release:0.1 pan:0.5046296296296297 ]", - "[ 15/4 ⇜ (4/1 → 17/4) | n:9 note:F4 clip:1 s:piano release:0.1 pan:0.5509259259259259 ]", - "[ (4/1 → 17/4) ⇝ 9/2 | n:5 note:Bb3 clip:1 s:piano release:0.1 pan:0.5185185185185186 ]", - "[ (4/1 → 17/4) ⇝ 9/2 | n:11 note:A4 clip:1 s:piano release:0.1 pan:0.5694444444444444 ]", - "[ 31/8 ⇜ (4/1 → 35/8) | n:3 note:G3 clip:1 s:piano release:0.1 pan:0.5046296296296297 ]", - "[ 31/8 ⇜ (4/1 → 35/8) | n:9 note:F4 clip:1 s:piano release:0.1 pan:0.5509259259259259 ]", - "[ 4/1 → 9/2 | n:9 note:F4 clip:1 s:piano release:0.1 pan:0.5509259259259259 ]", - "[ 4/1 → 5/1 | n:0 note:D3 clip:1 s:piano release:0.1 pan:0.4814814814814815 ]", - "[ 31/8 ⇜ (33/8 → 35/8) | n:5 note:Bb3 clip:1 s:piano release:0.1 pan:0.5185185185185186 ]", - "[ 31/8 ⇜ (33/8 → 35/8) | n:11 note:A4 clip:1 s:piano release:0.1 pan:0.5694444444444444 ]", - "[ (33/8 → 35/8) ⇝ 37/8 | n:5 note:Bb3 clip:1 s:piano release:0.1 pan:0.5185185185185186 ]", - "[ (33/8 → 35/8) ⇝ 37/8 | n:11 note:A4 clip:1 s:piano release:0.1 pan:0.5694444444444444 ]", - "[ 33/8 → 37/8 | n:9 note:F4 clip:1 s:piano release:0.1 pan:0.5509259259259259 ]", - "[ (33/8 → 5/1) ⇝ 41/8 | n:0 note:D3 clip:1 s:piano release:0.1 pan:0.4814814814814815 ]", - "[ 4/1 ⇜ (17/4 → 9/2) | n:5 note:Bb3 clip:1 s:piano release:0.1 pan:0.5185185185185186 ]", - "[ 4/1 ⇜ (17/4 → 9/2) | n:11 note:A4 clip:1 s:piano release:0.1 pan:0.5694444444444444 ]", - "[ (17/4 → 9/2) ⇝ 19/4 | n:11 note:A4 clip:1 s:piano release:0.1 pan:0.5694444444444444 ]", - "[ 17/4 → 19/4 | n:9 note:F4 clip:1 s:piano release:0.1 pan:0.5509259259259259 ]", - "[ (17/4 → 5/1) ⇝ 21/4 | n:2 note:F3 clip:1 s:piano release:0.1 pan:0.49537037037037035 ]", - "[ (17/4 → 5/1) ⇝ 21/4 | n:0 note:D3 clip:1 s:piano release:0.1 pan:0.4814814814814815 ]", - "[ 33/8 ⇜ (35/8 → 37/8) | n:5 note:Bb3 clip:1 s:piano release:0.1 pan:0.5185185185185186 ]", - "[ 33/8 ⇜ (35/8 → 37/8) | n:11 note:A4 clip:1 s:piano release:0.1 pan:0.5694444444444444 ]", - "[ (35/8 → 37/8) ⇝ 39/8 | n:11 note:A4 clip:1 s:piano release:0.1 pan:0.5694444444444444 ]", - "[ 35/8 → 39/8 | n:9 note:F4 clip:1 s:piano release:0.1 pan:0.5509259259259259 ]", - "[ (35/8 → 5/1) ⇝ 43/8 | n:2 note:F3 clip:1 s:piano release:0.1 pan:0.49537037037037035 ]", - "[ (35/8 → 5/1) ⇝ 43/8 | n:0 note:D3 clip:1 s:piano release:0.1 pan:0.4814814814814815 ]", - "[ 17/4 ⇜ (9/2 → 19/4) | n:11 note:A4 clip:1 s:piano release:0.1 pan:0.5694444444444444 ]", - "[ (9/2 → 19/4) ⇝ 5/1 | n:11 note:A4 clip:1 s:piano release:0.1 pan:0.5694444444444444 ]", - "[ (9/2 → 5/1) ⇝ 11/2 | n:6 note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ (9/2 → 5/1) ⇝ 11/2 | n:2 note:F3 clip:1 s:piano release:0.1 pan:0.49537037037037035 ]", - "[ 35/8 ⇜ (37/8 → 39/8) | n:11 note:A4 clip:1 s:piano release:0.1 pan:0.5694444444444444 ]", - "[ (37/8 → 39/8) ⇝ 41/8 | n:11 note:A4 clip:1 s:piano release:0.1 pan:0.5694444444444444 ]", - "[ (37/8 → 5/1) ⇝ 45/8 | n:6 note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ (37/8 → 5/1) ⇝ 45/8 | n:2 note:F3 clip:1 s:piano release:0.1 pan:0.49537037037037035 ]", - "[ 9/2 ⇜ (19/4 → 5/1) | n:11 note:A4 clip:1 s:piano release:0.1 pan:0.5694444444444444 ]", - "[ (19/4 → 5/1) ⇝ 23/4 | n:8 note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ (19/4 → 5/1) ⇝ 23/4 | n:6 note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ 37/8 ⇜ (39/8 → 5/1) ⇝ 41/8 | n:11 note:A4 clip:1 s:piano release:0.1 pan:0.5694444444444444 ]", - "[ (39/8 → 5/1) ⇝ 47/8 | n:8 note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ (39/8 → 5/1) ⇝ 47/8 | n:6 note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ 33/8 ⇜ (5/1 → 41/8) | n:0 note:D3 clip:1 s:piano release:0.1 pan:0.4814814814814815 ]", - "[ 35/8 ⇜ (5/1 → 41/8) ⇝ 43/8 | n:2 note:F3 clip:1 s:piano release:0.1 pan:0.49537037037037035 ]", - "[ 37/8 ⇜ (5/1 → 41/8) | n:11 note:A4 clip:1 s:piano release:0.1 pan:0.5694444444444444 ]", - "[ 37/8 ⇜ (5/1 → 41/8) ⇝ 45/8 | n:6 note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ 39/8 ⇜ (5/1 → 41/8) ⇝ 47/8 | n:8 note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 17/4 ⇜ (5/1 → 21/4) | n:2 note:F3 clip:1 s:piano release:0.1 pan:0.49537037037037035 ]", - "[ 17/4 ⇜ (5/1 → 21/4) | n:0 note:D3 clip:1 s:piano release:0.1 pan:0.4814814814814815 ]", - "[ 9/2 ⇜ (5/1 → 21/4) ⇝ 11/2 | n:2 note:F3 clip:1 s:piano release:0.1 pan:0.49537037037037035 ]", - "[ 19/4 ⇜ (5/1 → 21/4) ⇝ 23/4 | n:6 note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ (5/1 → 21/4) ⇝ 6/1 | n:8 note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 35/8 ⇜ (5/1 → 43/8) | n:0 note:D3 clip:1 s:piano release:0.1 pan:0.4814814814814815 ]", - "[ 37/8 ⇜ (5/1 → 43/8) ⇝ 45/8 | n:2 note:F3 clip:1 s:piano release:0.1 pan:0.49537037037037035 ]", - "[ 39/8 ⇜ (5/1 → 43/8) ⇝ 47/8 | n:6 note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ 9/2 ⇜ (5/1 → 11/2) | n:6 note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ 19/4 ⇜ (5/1 → 11/2) ⇝ 23/4 | n:8 note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 5/1 → 6/1 | n:2 note:F3 clip:1 s:piano release:0.1 pan:0.49537037037037035 ]", - "[ 35/8 ⇜ (41/8 → 43/8) | n:2 note:F3 clip:1 s:piano release:0.1 pan:0.49537037037037035 ]", - "[ (41/8 → 43/8) ⇝ 49/8 | n:8 note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 37/8 ⇜ (41/8 → 45/8) | n:6 note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ 39/8 ⇜ (41/8 → 45/8) ⇝ 47/8 | n:8 note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ (41/8 → 6/1) ⇝ 49/8 | n:2 note:F3 clip:1 s:piano release:0.1 pan:0.49537037037037035 ]", - "[ 9/2 ⇜ (21/4 → 11/2) | n:2 note:F3 clip:1 s:piano release:0.1 pan:0.49537037037037035 ]", - "[ 19/4 ⇜ (21/4 → 23/4) | n:6 note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ 5/1 ⇜ (21/4 → 23/4) ⇝ 6/1 | n:8 note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ (21/4 → 6/1) ⇝ 25/4 | n:4 note:A3 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", - "[ (21/4 → 6/1) ⇝ 25/4 | n:2 note:F3 clip:1 s:piano release:0.1 pan:0.49537037037037035 ]", - "[ 37/8 ⇜ (43/8 → 45/8) | n:2 note:F3 clip:1 s:piano release:0.1 pan:0.49537037037037035 ]", - "[ 39/8 ⇜ (43/8 → 47/8) | n:6 note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ 41/8 ⇜ (43/8 → 47/8) ⇝ 49/8 | n:8 note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ (43/8 → 6/1) ⇝ 51/8 | n:4 note:A3 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", - "[ (43/8 → 6/1) ⇝ 51/8 | n:2 note:F3 clip:1 s:piano release:0.1 pan:0.49537037037037035 ]", - "[ 19/4 ⇜ (11/2 → 23/4) | n:8 note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ (11/2 → 6/1) ⇝ 13/2 | n:8 note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ (11/2 → 6/1) ⇝ 13/2 | n:4 note:A3 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", - "[ 39/8 ⇜ (45/8 → 47/8) | n:8 note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ (45/8 → 6/1) ⇝ 53/8 | n:8 note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ (45/8 → 6/1) ⇝ 53/8 | n:4 note:A3 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", - "[ 5/1 ⇜ (23/4 → 6/1) | n:8 note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ (23/4 → 6/1) ⇝ 27/4 | n:10 note:G4 clip:1 s:piano release:0.1 pan:0.5601851851851851 ]", - "[ (23/4 → 6/1) ⇝ 27/4 | n:8 note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 41/8 ⇜ (47/8 → 6/1) ⇝ 49/8 | n:8 note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ (47/8 → 6/1) ⇝ 55/8 | n:10 note:G4 clip:1 s:piano release:0.1 pan:0.5601851851851851 ]", - "[ (47/8 → 6/1) ⇝ 55/8 | n:8 note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 41/8 ⇜ (6/1 → 49/8) | n:2 note:F3 clip:1 s:piano release:0.1 pan:0.49537037037037035 ]", - "[ 41/8 ⇜ (6/1 → 49/8) | n:8 note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 43/8 ⇜ (6/1 → 49/8) ⇝ 51/8 | n:4 note:A3 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", - "[ 45/8 ⇜ (6/1 → 49/8) ⇝ 53/8 | n:8 note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 47/8 ⇜ (6/1 → 49/8) ⇝ 55/8 | n:10 note:G4 clip:1 s:piano release:0.1 pan:0.5601851851851851 ]", - "[ 21/4 ⇜ (6/1 → 25/4) | n:4 note:A3 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", - "[ 21/4 ⇜ (6/1 → 25/4) | n:2 note:F3 clip:1 s:piano release:0.1 pan:0.49537037037037035 ]", - "[ 11/2 ⇜ (6/1 → 25/4) ⇝ 13/2 | n:4 note:A3 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", - "[ 23/4 ⇜ (6/1 → 25/4) ⇝ 27/4 | n:8 note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 6/1 → 25/4 | n:4 note:A3 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", - "[ (6/1 → 25/4) ⇝ 7/1 | n:10 note:G4 clip:1 s:piano release:0.1 pan:0.5601851851851851 ]", - "[ 43/8 ⇜ (6/1 → 51/8) | n:2 note:F3 clip:1 s:piano release:0.1 pan:0.49537037037037035 ]", - "[ 45/8 ⇜ (6/1 → 51/8) ⇝ 53/8 | n:4 note:A3 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", - "[ 47/8 ⇜ (6/1 → 51/8) ⇝ 55/8 | n:8 note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 11/2 ⇜ (6/1 → 13/2) | n:8 note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 23/4 ⇜ (6/1 → 13/2) ⇝ 27/4 | n:10 note:G4 clip:1 s:piano release:0.1 pan:0.5601851851851851 ]", - "[ 43/8 ⇜ (49/8 → 51/8) | n:4 note:A3 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", - "[ 49/8 → 51/8 | n:4 note:A3 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", - "[ (49/8 → 51/8) ⇝ 57/8 | n:10 note:G4 clip:1 s:piano release:0.1 pan:0.5601851851851851 ]", - "[ 45/8 ⇜ (49/8 → 53/8) | n:8 note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 47/8 ⇜ (49/8 → 53/8) ⇝ 55/8 | n:10 note:G4 clip:1 s:piano release:0.1 pan:0.5601851851851851 ]", - "[ 11/2 ⇜ (25/4 → 13/2) | n:4 note:A3 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", - "[ 25/4 → 13/2 | n:6 note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ 25/4 → 13/2 | n:4 note:A3 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", - "[ 23/4 ⇜ (25/4 → 27/4) | n:8 note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 6/1 ⇜ (25/4 → 27/4) ⇝ 7/1 | n:10 note:G4 clip:1 s:piano release:0.1 pan:0.5601851851851851 ]", - "[ 45/8 ⇜ (51/8 → 53/8) | n:4 note:A3 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", - "[ 51/8 → 53/8 | n:6 note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ 51/8 → 53/8 | n:4 note:A3 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", - "[ 47/8 ⇜ (51/8 → 55/8) | n:8 note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 49/8 ⇜ (51/8 → 55/8) ⇝ 57/8 | n:10 note:G4 clip:1 s:piano release:0.1 pan:0.5601851851851851 ]", - "[ 23/4 ⇜ (13/2 → 27/4) | n:10 note:G4 clip:1 s:piano release:0.1 pan:0.5601851851851851 ]", - "[ 13/2 → 27/4 | n:6 note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ 13/2 → 27/4 | n:10 note:G4 clip:1 s:piano release:0.1 pan:0.5601851851851851 ]", - "[ 13/2 → 27/4 | n:6 note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ 47/8 ⇜ (53/8 → 55/8) | n:10 note:G4 clip:1 s:piano release:0.1 pan:0.5601851851851851 ]", - "[ 53/8 → 55/8 | n:6 note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ 53/8 → 55/8 | n:10 note:G4 clip:1 s:piano release:0.1 pan:0.5601851851851851 ]", - "[ 53/8 → 55/8 | n:6 note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ 6/1 ⇜ (27/4 → 7/1) | n:10 note:G4 clip:1 s:piano release:0.1 pan:0.5601851851851851 ]", - "[ 27/4 → 7/1 | n:6 note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ 27/4 → 7/1 | n:8 note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 27/4 → 7/1 | n:12 note:Bb4 clip:1 s:piano release:0.1 pan:0.5740740740740741 ]", - "[ 27/4 → 7/1 | n:6 note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ 27/4 → 7/1 | n:10 note:G4 clip:1 s:piano release:0.1 pan:0.5601851851851851 ]", - "[ 49/8 ⇜ (55/8 → 7/1) ⇝ 57/8 | n:10 note:G4 clip:1 s:piano release:0.1 pan:0.5601851851851851 ]", - "[ (55/8 → 7/1) ⇝ 57/8 | n:6 note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ (55/8 → 7/1) ⇝ 57/8 | n:8 note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ (55/8 → 7/1) ⇝ 57/8 | n:12 note:Bb4 clip:1 s:piano release:0.1 pan:0.5740740740740741 ]", - "[ (55/8 → 7/1) ⇝ 57/8 | n:6 note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ (55/8 → 7/1) ⇝ 57/8 | n:10 note:G4 clip:1 s:piano release:0.1 pan:0.5601851851851851 ]", - "[ 49/8 ⇜ (7/1 → 57/8) | n:10 note:G4 clip:1 s:piano release:0.1 pan:0.5601851851851851 ]", - "[ 55/8 ⇜ (7/1 → 57/8) | n:6 note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ 55/8 ⇜ (7/1 → 57/8) | n:8 note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 55/8 ⇜ (7/1 → 57/8) | n:12 note:Bb4 clip:1 s:piano release:0.1 pan:0.5740740740740741 ]", - "[ 55/8 ⇜ (7/1 → 57/8) | n:6 note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ 55/8 ⇜ (7/1 → 57/8) | n:10 note:G4 clip:1 s:piano release:0.1 pan:0.5601851851851851 ]", - "[ 7/1 → 29/4 | n:8 note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 7/1 → 29/4 | n:12 note:Bb4 clip:1 s:piano release:0.1 pan:0.5740740740740741 ]", - "[ 7/1 → 29/4 | n:6 note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ 7/1 → 29/4 | n:8 note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 7/1 → 29/4 | n:12 note:Bb4 clip:1 s:piano release:0.1 pan:0.5740740740740741 ]", - "[ 7/1 → 15/2 | n:3 note:G3 clip:1 s:piano release:0.1 pan:0.5046296296296297 ]", - "[ 57/8 → 59/8 | n:8 note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 57/8 → 59/8 | n:12 note:Bb4 clip:1 s:piano release:0.1 pan:0.5740740740740741 ]", - "[ 57/8 → 59/8 | n:6 note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ 57/8 → 59/8 | n:8 note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 57/8 → 59/8 | n:12 note:Bb4 clip:1 s:piano release:0.1 pan:0.5740740740740741 ]", - "[ 57/8 → 61/8 | n:3 note:G3 clip:1 s:piano release:0.1 pan:0.5046296296296297 ]", - "[ 29/4 → 15/2 | n:12 note:Bb4 clip:1 s:piano release:0.1 pan:0.5740740740740741 ]", - "[ 29/4 → 15/2 | n:14 note:D5 clip:1 s:piano release:0.1 pan:0.5925925925925926 ]", - "[ 29/4 → 15/2 | n:8 note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 29/4 → 15/2 | n:12 note:Bb4 clip:1 s:piano release:0.1 pan:0.5740740740740741 ]", - "[ 29/4 → 31/4 | n:5 note:Bb3 clip:1 s:piano release:0.1 pan:0.5185185185185186 ]", - "[ 29/4 → 31/4 | n:3 note:G3 clip:1 s:piano release:0.1 pan:0.5046296296296297 ]", - "[ 59/8 → 61/8 | n:12 note:Bb4 clip:1 s:piano release:0.1 pan:0.5740740740740741 ]", - "[ 59/8 → 61/8 | n:14 note:D5 clip:1 s:piano release:0.1 pan:0.5925925925925926 ]", - "[ 59/8 → 61/8 | n:8 note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 59/8 → 61/8 | n:12 note:Bb4 clip:1 s:piano release:0.1 pan:0.5740740740740741 ]", - "[ 59/8 → 63/8 | n:5 note:Bb3 clip:1 s:piano release:0.1 pan:0.5185185185185186 ]", - "[ 59/8 → 63/8 | n:3 note:G3 clip:1 s:piano release:0.1 pan:0.5046296296296297 ]", - "[ 15/2 → 31/4 | n:14 note:D5 clip:1 s:piano release:0.1 pan:0.5925925925925926 ]", - "[ 15/2 → 31/4 | n:12 note:Bb4 clip:1 s:piano release:0.1 pan:0.5740740740740741 ]", - "[ 15/2 → 31/4 | n:14 note:D5 clip:1 s:piano release:0.1 pan:0.5925925925925926 ]", - "[ 15/2 → 8/1 | n:3 note:G3 clip:1 s:piano release:0.1 pan:0.5046296296296297 ]", - "[ 15/2 → 8/1 | n:9 note:F4 clip:1 s:piano release:0.1 pan:0.5509259259259259 ]", - "[ 15/2 → 8/1 | n:5 note:Bb3 clip:1 s:piano release:0.1 pan:0.5185185185185186 ]", - "[ 61/8 → 63/8 | n:14 note:D5 clip:1 s:piano release:0.1 pan:0.5925925925925926 ]", - "[ 61/8 → 63/8 | n:12 note:Bb4 clip:1 s:piano release:0.1 pan:0.5740740740740741 ]", - "[ 61/8 → 63/8 | n:14 note:D5 clip:1 s:piano release:0.1 pan:0.5925925925925926 ]", - "[ (61/8 → 8/1) ⇝ 65/8 | n:3 note:G3 clip:1 s:piano release:0.1 pan:0.5046296296296297 ]", - "[ (61/8 → 8/1) ⇝ 65/8 | n:9 note:F4 clip:1 s:piano release:0.1 pan:0.5509259259259259 ]", - "[ (61/8 → 8/1) ⇝ 65/8 | n:5 note:Bb3 clip:1 s:piano release:0.1 pan:0.5185185185185186 ]", - "[ 31/4 → 8/1 | n:14 note:D5 clip:1 s:piano release:0.1 pan:0.5925925925925926 ]", - "[ (31/4 → 8/1) ⇝ 33/4 | n:5 note:Bb3 clip:1 s:piano release:0.1 pan:0.5185185185185186 ]", - "[ (31/4 → 8/1) ⇝ 33/4 | n:11 note:A4 clip:1 s:piano release:0.1 pan:0.5694444444444444 ]", - "[ (31/4 → 8/1) ⇝ 33/4 | n:3 note:G3 clip:1 s:piano release:0.1 pan:0.5046296296296297 ]", - "[ (31/4 → 8/1) ⇝ 33/4 | n:9 note:F4 clip:1 s:piano release:0.1 pan:0.5509259259259259 ]", - "[ (63/8 → 8/1) ⇝ 65/8 | n:14 note:D5 clip:1 s:piano release:0.1 pan:0.5925925925925926 ]", - "[ (63/8 → 8/1) ⇝ 67/8 | n:5 note:Bb3 clip:1 s:piano release:0.1 pan:0.5185185185185186 ]", - "[ (63/8 → 8/1) ⇝ 67/8 | n:11 note:A4 clip:1 s:piano release:0.1 pan:0.5694444444444444 ]", - "[ (63/8 → 8/1) ⇝ 67/8 | n:3 note:G3 clip:1 s:piano release:0.1 pan:0.5046296296296297 ]", - "[ (63/8 → 8/1) ⇝ 67/8 | n:9 note:F4 clip:1 s:piano release:0.1 pan:0.5509259259259259 ]", + "[ -3/8 ⇜ (0/1 → 1/8) | note:G3 clip:1 s:piano release:0.1 pan:0.5046296296296297 ]", + "[ -3/8 ⇜ (0/1 → 1/8) | note:F4 clip:1 s:piano release:0.1 pan:0.5509259259259259 ]", + "[ -3/8 ⇜ (0/1 → 1/8) | note:Bb3 clip:1 s:piano release:0.1 pan:0.5185185185185186 ]", + "[ -1/8 ⇜ (0/1 → 1/8) | note:D5 clip:1 s:piano release:0.1 pan:0.5925925925925926 ]", + "[ -1/8 ⇜ (0/1 → 1/8) ⇝ 3/8 | note:Bb3 clip:1 s:piano release:0.1 pan:0.5185185185185186 ]", + "[ -1/8 ⇜ (0/1 → 1/8) ⇝ 3/8 | note:A4 clip:1 s:piano release:0.1 pan:0.5694444444444444 ]", + "[ -1/4 ⇜ (0/1 → 1/4) | note:Bb3 clip:1 s:piano release:0.1 pan:0.5185185185185186 ]", + "[ -1/4 ⇜ (0/1 → 1/4) | note:A4 clip:1 s:piano release:0.1 pan:0.5694444444444444 ]", + "[ -1/4 ⇜ (0/1 → 1/4) | note:G3 clip:1 s:piano release:0.1 pan:0.5046296296296297 ]", + "[ -1/4 ⇜ (0/1 → 1/4) | note:F4 clip:1 s:piano release:0.1 pan:0.5509259259259259 ]", + "[ (0/1 → 1/4) ⇝ 1/2 | note:Bb3 clip:1 s:piano release:0.1 pan:0.5185185185185186 ]", + "[ (0/1 → 1/4) ⇝ 1/2 | note:A4 clip:1 s:piano release:0.1 pan:0.5694444444444444 ]", + "[ -1/8 ⇜ (0/1 → 3/8) | note:G3 clip:1 s:piano release:0.1 pan:0.5046296296296297 ]", + "[ -1/8 ⇜ (0/1 → 3/8) | note:F4 clip:1 s:piano release:0.1 pan:0.5509259259259259 ]", + "[ 0/1 → 1/2 | note:F4 clip:1 s:piano release:0.1 pan:0.5509259259259259 ]", + "[ 0/1 → 1/1 | note:D3 clip:1 s:piano release:0.1 pan:0.4814814814814815 ]", + "[ -1/8 ⇜ (1/8 → 3/8) | note:Bb3 clip:1 s:piano release:0.1 pan:0.5185185185185186 ]", + "[ -1/8 ⇜ (1/8 → 3/8) | note:A4 clip:1 s:piano release:0.1 pan:0.5694444444444444 ]", + "[ (1/8 → 3/8) ⇝ 5/8 | note:Bb3 clip:1 s:piano release:0.1 pan:0.5185185185185186 ]", + "[ (1/8 → 3/8) ⇝ 5/8 | note:A4 clip:1 s:piano release:0.1 pan:0.5694444444444444 ]", + "[ 1/8 → 5/8 | note:F4 clip:1 s:piano release:0.1 pan:0.5509259259259259 ]", + "[ (1/8 → 1/1) ⇝ 9/8 | note:D3 clip:1 s:piano release:0.1 pan:0.4814814814814815 ]", + "[ 0/1 ⇜ (1/4 → 1/2) | note:Bb3 clip:1 s:piano release:0.1 pan:0.5185185185185186 ]", + "[ 0/1 ⇜ (1/4 → 1/2) | note:A4 clip:1 s:piano release:0.1 pan:0.5694444444444444 ]", + "[ (1/4 → 1/2) ⇝ 3/4 | note:A4 clip:1 s:piano release:0.1 pan:0.5694444444444444 ]", + "[ 1/4 → 3/4 | note:F4 clip:1 s:piano release:0.1 pan:0.5509259259259259 ]", + "[ (1/4 → 1/1) ⇝ 5/4 | note:F3 clip:1 s:piano release:0.1 pan:0.49537037037037035 ]", + "[ (1/4 → 1/1) ⇝ 5/4 | note:D3 clip:1 s:piano release:0.1 pan:0.4814814814814815 ]", + "[ 1/8 ⇜ (3/8 → 5/8) | note:Bb3 clip:1 s:piano release:0.1 pan:0.5185185185185186 ]", + "[ 1/8 ⇜ (3/8 → 5/8) | note:A4 clip:1 s:piano release:0.1 pan:0.5694444444444444 ]", + "[ (3/8 → 5/8) ⇝ 7/8 | note:A4 clip:1 s:piano release:0.1 pan:0.5694444444444444 ]", + "[ 3/8 → 7/8 | note:F4 clip:1 s:piano release:0.1 pan:0.5509259259259259 ]", + "[ (3/8 → 1/1) ⇝ 11/8 | note:F3 clip:1 s:piano release:0.1 pan:0.49537037037037035 ]", + "[ (3/8 → 1/1) ⇝ 11/8 | note:D3 clip:1 s:piano release:0.1 pan:0.4814814814814815 ]", + "[ 1/4 ⇜ (1/2 → 3/4) | note:A4 clip:1 s:piano release:0.1 pan:0.5694444444444444 ]", + "[ (1/2 → 3/4) ⇝ 1/1 | note:A4 clip:1 s:piano release:0.1 pan:0.5694444444444444 ]", + "[ (1/2 → 1/1) ⇝ 3/2 | note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ (1/2 → 1/1) ⇝ 3/2 | note:F3 clip:1 s:piano release:0.1 pan:0.49537037037037035 ]", + "[ 3/8 ⇜ (5/8 → 7/8) | note:A4 clip:1 s:piano release:0.1 pan:0.5694444444444444 ]", + "[ (5/8 → 7/8) ⇝ 9/8 | note:A4 clip:1 s:piano release:0.1 pan:0.5694444444444444 ]", + "[ (5/8 → 1/1) ⇝ 13/8 | note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ (5/8 → 1/1) ⇝ 13/8 | note:F3 clip:1 s:piano release:0.1 pan:0.49537037037037035 ]", + "[ 1/2 ⇜ (3/4 → 1/1) | note:A4 clip:1 s:piano release:0.1 pan:0.5694444444444444 ]", + "[ (3/4 → 1/1) ⇝ 7/4 | note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ (3/4 → 1/1) ⇝ 7/4 | note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ 5/8 ⇜ (7/8 → 1/1) ⇝ 9/8 | note:A4 clip:1 s:piano release:0.1 pan:0.5694444444444444 ]", + "[ (7/8 → 1/1) ⇝ 15/8 | note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ (7/8 → 1/1) ⇝ 15/8 | note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ 1/8 ⇜ (1/1 → 9/8) | note:D3 clip:1 s:piano release:0.1 pan:0.4814814814814815 ]", + "[ 3/8 ⇜ (1/1 → 9/8) ⇝ 11/8 | note:F3 clip:1 s:piano release:0.1 pan:0.49537037037037035 ]", + "[ 5/8 ⇜ (1/1 → 9/8) | note:A4 clip:1 s:piano release:0.1 pan:0.5694444444444444 ]", + "[ 5/8 ⇜ (1/1 → 9/8) ⇝ 13/8 | note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ 7/8 ⇜ (1/1 → 9/8) ⇝ 15/8 | note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 1/4 ⇜ (1/1 → 5/4) | note:F3 clip:1 s:piano release:0.1 pan:0.49537037037037035 ]", + "[ 1/4 ⇜ (1/1 → 5/4) | note:D3 clip:1 s:piano release:0.1 pan:0.4814814814814815 ]", + "[ 1/2 ⇜ (1/1 → 5/4) ⇝ 3/2 | note:F3 clip:1 s:piano release:0.1 pan:0.49537037037037035 ]", + "[ 3/4 ⇜ (1/1 → 5/4) ⇝ 7/4 | note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ (1/1 → 5/4) ⇝ 2/1 | note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 3/8 ⇜ (1/1 → 11/8) | note:D3 clip:1 s:piano release:0.1 pan:0.4814814814814815 ]", + "[ 5/8 ⇜ (1/1 → 11/8) ⇝ 13/8 | note:F3 clip:1 s:piano release:0.1 pan:0.49537037037037035 ]", + "[ 7/8 ⇜ (1/1 → 11/8) ⇝ 15/8 | note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ 1/2 ⇜ (1/1 → 3/2) | note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ 3/4 ⇜ (1/1 → 3/2) ⇝ 7/4 | note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 1/1 → 2/1 | note:F3 clip:1 s:piano release:0.1 pan:0.49537037037037035 ]", + "[ 3/8 ⇜ (9/8 → 11/8) | note:F3 clip:1 s:piano release:0.1 pan:0.49537037037037035 ]", + "[ (9/8 → 11/8) ⇝ 17/8 | note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 5/8 ⇜ (9/8 → 13/8) | note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ 7/8 ⇜ (9/8 → 13/8) ⇝ 15/8 | note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ (9/8 → 2/1) ⇝ 17/8 | note:F3 clip:1 s:piano release:0.1 pan:0.49537037037037035 ]", + "[ 1/2 ⇜ (5/4 → 3/2) | note:F3 clip:1 s:piano release:0.1 pan:0.49537037037037035 ]", + "[ 3/4 ⇜ (5/4 → 7/4) | note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ 1/1 ⇜ (5/4 → 7/4) ⇝ 2/1 | note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ (5/4 → 2/1) ⇝ 9/4 | note:A3 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", + "[ (5/4 → 2/1) ⇝ 9/4 | note:F3 clip:1 s:piano release:0.1 pan:0.49537037037037035 ]", + "[ 5/8 ⇜ (11/8 → 13/8) | note:F3 clip:1 s:piano release:0.1 pan:0.49537037037037035 ]", + "[ 7/8 ⇜ (11/8 → 15/8) | note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ 9/8 ⇜ (11/8 → 15/8) ⇝ 17/8 | note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ (11/8 → 2/1) ⇝ 19/8 | note:A3 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", + "[ (11/8 → 2/1) ⇝ 19/8 | note:F3 clip:1 s:piano release:0.1 pan:0.49537037037037035 ]", + "[ 3/4 ⇜ (3/2 → 7/4) | note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ (3/2 → 2/1) ⇝ 5/2 | note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ (3/2 → 2/1) ⇝ 5/2 | note:A3 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", + "[ 7/8 ⇜ (13/8 → 15/8) | note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ (13/8 → 2/1) ⇝ 21/8 | note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ (13/8 → 2/1) ⇝ 21/8 | note:A3 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", + "[ 1/1 ⇜ (7/4 → 2/1) | note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ (7/4 → 2/1) ⇝ 11/4 | note:G4 clip:1 s:piano release:0.1 pan:0.5601851851851851 ]", + "[ (7/4 → 2/1) ⇝ 11/4 | note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 9/8 ⇜ (15/8 → 2/1) ⇝ 17/8 | note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ (15/8 → 2/1) ⇝ 23/8 | note:G4 clip:1 s:piano release:0.1 pan:0.5601851851851851 ]", + "[ (15/8 → 2/1) ⇝ 23/8 | note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 9/8 ⇜ (2/1 → 17/8) | note:F3 clip:1 s:piano release:0.1 pan:0.49537037037037035 ]", + "[ 9/8 ⇜ (2/1 → 17/8) | note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 11/8 ⇜ (2/1 → 17/8) ⇝ 19/8 | note:A3 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", + "[ 13/8 ⇜ (2/1 → 17/8) ⇝ 21/8 | note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 15/8 ⇜ (2/1 → 17/8) ⇝ 23/8 | note:G4 clip:1 s:piano release:0.1 pan:0.5601851851851851 ]", + "[ 5/4 ⇜ (2/1 → 9/4) | note:A3 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", + "[ 5/4 ⇜ (2/1 → 9/4) | note:F3 clip:1 s:piano release:0.1 pan:0.49537037037037035 ]", + "[ 3/2 ⇜ (2/1 → 9/4) ⇝ 5/2 | note:A3 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", + "[ 7/4 ⇜ (2/1 → 9/4) ⇝ 11/4 | note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 2/1 → 9/4 | note:A3 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", + "[ (2/1 → 9/4) ⇝ 3/1 | note:G4 clip:1 s:piano release:0.1 pan:0.5601851851851851 ]", + "[ 11/8 ⇜ (2/1 → 19/8) | note:F3 clip:1 s:piano release:0.1 pan:0.49537037037037035 ]", + "[ 13/8 ⇜ (2/1 → 19/8) ⇝ 21/8 | note:A3 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", + "[ 15/8 ⇜ (2/1 → 19/8) ⇝ 23/8 | note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 3/2 ⇜ (2/1 → 5/2) | note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 7/4 ⇜ (2/1 → 5/2) ⇝ 11/4 | note:G4 clip:1 s:piano release:0.1 pan:0.5601851851851851 ]", + "[ 11/8 ⇜ (17/8 → 19/8) | note:A3 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", + "[ 17/8 → 19/8 | note:A3 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", + "[ (17/8 → 19/8) ⇝ 25/8 | note:G4 clip:1 s:piano release:0.1 pan:0.5601851851851851 ]", + "[ 13/8 ⇜ (17/8 → 21/8) | note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 15/8 ⇜ (17/8 → 21/8) ⇝ 23/8 | note:G4 clip:1 s:piano release:0.1 pan:0.5601851851851851 ]", + "[ 3/2 ⇜ (9/4 → 5/2) | note:A3 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", + "[ 9/4 → 5/2 | note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ 9/4 → 5/2 | note:A3 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", + "[ 7/4 ⇜ (9/4 → 11/4) | note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 2/1 ⇜ (9/4 → 11/4) ⇝ 3/1 | note:G4 clip:1 s:piano release:0.1 pan:0.5601851851851851 ]", + "[ 13/8 ⇜ (19/8 → 21/8) | note:A3 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", + "[ 19/8 → 21/8 | note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ 19/8 → 21/8 | note:A3 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", + "[ 15/8 ⇜ (19/8 → 23/8) | note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 17/8 ⇜ (19/8 → 23/8) ⇝ 25/8 | note:G4 clip:1 s:piano release:0.1 pan:0.5601851851851851 ]", + "[ 7/4 ⇜ (5/2 → 11/4) | note:G4 clip:1 s:piano release:0.1 pan:0.5601851851851851 ]", + "[ 5/2 → 11/4 | note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ 5/2 → 11/4 | note:G4 clip:1 s:piano release:0.1 pan:0.5601851851851851 ]", + "[ 5/2 → 11/4 | note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ 15/8 ⇜ (21/8 → 23/8) | note:G4 clip:1 s:piano release:0.1 pan:0.5601851851851851 ]", + "[ 21/8 → 23/8 | note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ 21/8 → 23/8 | note:G4 clip:1 s:piano release:0.1 pan:0.5601851851851851 ]", + "[ 21/8 → 23/8 | note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ 2/1 ⇜ (11/4 → 3/1) | note:G4 clip:1 s:piano release:0.1 pan:0.5601851851851851 ]", + "[ 11/4 → 3/1 | note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ 11/4 → 3/1 | note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 11/4 → 3/1 | note:Bb4 clip:1 s:piano release:0.1 pan:0.5740740740740741 ]", + "[ 11/4 → 3/1 | note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ 11/4 → 3/1 | note:G4 clip:1 s:piano release:0.1 pan:0.5601851851851851 ]", + "[ 17/8 ⇜ (23/8 → 3/1) ⇝ 25/8 | note:G4 clip:1 s:piano release:0.1 pan:0.5601851851851851 ]", + "[ (23/8 → 3/1) ⇝ 25/8 | note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ (23/8 → 3/1) ⇝ 25/8 | note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ (23/8 → 3/1) ⇝ 25/8 | note:Bb4 clip:1 s:piano release:0.1 pan:0.5740740740740741 ]", + "[ (23/8 → 3/1) ⇝ 25/8 | note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ (23/8 → 3/1) ⇝ 25/8 | note:G4 clip:1 s:piano release:0.1 pan:0.5601851851851851 ]", + "[ 17/8 ⇜ (3/1 → 25/8) | note:G4 clip:1 s:piano release:0.1 pan:0.5601851851851851 ]", + "[ 23/8 ⇜ (3/1 → 25/8) | note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ 23/8 ⇜ (3/1 → 25/8) | note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 23/8 ⇜ (3/1 → 25/8) | note:Bb4 clip:1 s:piano release:0.1 pan:0.5740740740740741 ]", + "[ 23/8 ⇜ (3/1 → 25/8) | note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ 23/8 ⇜ (3/1 → 25/8) | note:G4 clip:1 s:piano release:0.1 pan:0.5601851851851851 ]", + "[ 3/1 → 13/4 | note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 3/1 → 13/4 | note:Bb4 clip:1 s:piano release:0.1 pan:0.5740740740740741 ]", + "[ 3/1 → 13/4 | note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ 3/1 → 13/4 | note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 3/1 → 13/4 | note:Bb4 clip:1 s:piano release:0.1 pan:0.5740740740740741 ]", + "[ 3/1 → 7/2 | note:G3 clip:1 s:piano release:0.1 pan:0.5046296296296297 ]", + "[ 25/8 → 27/8 | note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 25/8 → 27/8 | note:Bb4 clip:1 s:piano release:0.1 pan:0.5740740740740741 ]", + "[ 25/8 → 27/8 | note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ 25/8 → 27/8 | note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 25/8 → 27/8 | note:Bb4 clip:1 s:piano release:0.1 pan:0.5740740740740741 ]", + "[ 25/8 → 29/8 | note:G3 clip:1 s:piano release:0.1 pan:0.5046296296296297 ]", + "[ 13/4 → 7/2 | note:Bb4 clip:1 s:piano release:0.1 pan:0.5740740740740741 ]", + "[ 13/4 → 7/2 | note:D5 clip:1 s:piano release:0.1 pan:0.5925925925925926 ]", + "[ 13/4 → 7/2 | note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 13/4 → 7/2 | note:Bb4 clip:1 s:piano release:0.1 pan:0.5740740740740741 ]", + "[ 13/4 → 15/4 | note:Bb3 clip:1 s:piano release:0.1 pan:0.5185185185185186 ]", + "[ 13/4 → 15/4 | note:G3 clip:1 s:piano release:0.1 pan:0.5046296296296297 ]", + "[ 27/8 → 29/8 | note:Bb4 clip:1 s:piano release:0.1 pan:0.5740740740740741 ]", + "[ 27/8 → 29/8 | note:D5 clip:1 s:piano release:0.1 pan:0.5925925925925926 ]", + "[ 27/8 → 29/8 | note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 27/8 → 29/8 | note:Bb4 clip:1 s:piano release:0.1 pan:0.5740740740740741 ]", + "[ 27/8 → 31/8 | note:Bb3 clip:1 s:piano release:0.1 pan:0.5185185185185186 ]", + "[ 27/8 → 31/8 | note:G3 clip:1 s:piano release:0.1 pan:0.5046296296296297 ]", + "[ 7/2 → 15/4 | note:D5 clip:1 s:piano release:0.1 pan:0.5925925925925926 ]", + "[ 7/2 → 15/4 | note:Bb4 clip:1 s:piano release:0.1 pan:0.5740740740740741 ]", + "[ 7/2 → 15/4 | note:D5 clip:1 s:piano release:0.1 pan:0.5925925925925926 ]", + "[ 7/2 → 4/1 | note:G3 clip:1 s:piano release:0.1 pan:0.5046296296296297 ]", + "[ 7/2 → 4/1 | note:F4 clip:1 s:piano release:0.1 pan:0.5509259259259259 ]", + "[ 7/2 → 4/1 | note:Bb3 clip:1 s:piano release:0.1 pan:0.5185185185185186 ]", + "[ 29/8 → 31/8 | note:D5 clip:1 s:piano release:0.1 pan:0.5925925925925926 ]", + "[ 29/8 → 31/8 | note:Bb4 clip:1 s:piano release:0.1 pan:0.5740740740740741 ]", + "[ 29/8 → 31/8 | note:D5 clip:1 s:piano release:0.1 pan:0.5925925925925926 ]", + "[ (29/8 → 4/1) ⇝ 33/8 | note:G3 clip:1 s:piano release:0.1 pan:0.5046296296296297 ]", + "[ (29/8 → 4/1) ⇝ 33/8 | note:F4 clip:1 s:piano release:0.1 pan:0.5509259259259259 ]", + "[ (29/8 → 4/1) ⇝ 33/8 | note:Bb3 clip:1 s:piano release:0.1 pan:0.5185185185185186 ]", + "[ 15/4 → 4/1 | note:D5 clip:1 s:piano release:0.1 pan:0.5925925925925926 ]", + "[ (15/4 → 4/1) ⇝ 17/4 | note:Bb3 clip:1 s:piano release:0.1 pan:0.5185185185185186 ]", + "[ (15/4 → 4/1) ⇝ 17/4 | note:A4 clip:1 s:piano release:0.1 pan:0.5694444444444444 ]", + "[ (15/4 → 4/1) ⇝ 17/4 | note:G3 clip:1 s:piano release:0.1 pan:0.5046296296296297 ]", + "[ (15/4 → 4/1) ⇝ 17/4 | note:F4 clip:1 s:piano release:0.1 pan:0.5509259259259259 ]", + "[ (31/8 → 4/1) ⇝ 33/8 | note:D5 clip:1 s:piano release:0.1 pan:0.5925925925925926 ]", + "[ (31/8 → 4/1) ⇝ 35/8 | note:Bb3 clip:1 s:piano release:0.1 pan:0.5185185185185186 ]", + "[ (31/8 → 4/1) ⇝ 35/8 | note:A4 clip:1 s:piano release:0.1 pan:0.5694444444444444 ]", + "[ (31/8 → 4/1) ⇝ 35/8 | note:G3 clip:1 s:piano release:0.1 pan:0.5046296296296297 ]", + "[ (31/8 → 4/1) ⇝ 35/8 | note:F4 clip:1 s:piano release:0.1 pan:0.5509259259259259 ]", + "[ 29/8 ⇜ (4/1 → 33/8) | note:G3 clip:1 s:piano release:0.1 pan:0.5046296296296297 ]", + "[ 29/8 ⇜ (4/1 → 33/8) | note:F4 clip:1 s:piano release:0.1 pan:0.5509259259259259 ]", + "[ 29/8 ⇜ (4/1 → 33/8) | note:Bb3 clip:1 s:piano release:0.1 pan:0.5185185185185186 ]", + "[ 31/8 ⇜ (4/1 → 33/8) | note:D5 clip:1 s:piano release:0.1 pan:0.5925925925925926 ]", + "[ 31/8 ⇜ (4/1 → 33/8) ⇝ 35/8 | note:Bb3 clip:1 s:piano release:0.1 pan:0.5185185185185186 ]", + "[ 31/8 ⇜ (4/1 → 33/8) ⇝ 35/8 | note:A4 clip:1 s:piano release:0.1 pan:0.5694444444444444 ]", + "[ 15/4 ⇜ (4/1 → 17/4) | note:Bb3 clip:1 s:piano release:0.1 pan:0.5185185185185186 ]", + "[ 15/4 ⇜ (4/1 → 17/4) | note:A4 clip:1 s:piano release:0.1 pan:0.5694444444444444 ]", + "[ 15/4 ⇜ (4/1 → 17/4) | note:G3 clip:1 s:piano release:0.1 pan:0.5046296296296297 ]", + "[ 15/4 ⇜ (4/1 → 17/4) | note:F4 clip:1 s:piano release:0.1 pan:0.5509259259259259 ]", + "[ (4/1 → 17/4) ⇝ 9/2 | note:Bb3 clip:1 s:piano release:0.1 pan:0.5185185185185186 ]", + "[ (4/1 → 17/4) ⇝ 9/2 | note:A4 clip:1 s:piano release:0.1 pan:0.5694444444444444 ]", + "[ 31/8 ⇜ (4/1 → 35/8) | note:G3 clip:1 s:piano release:0.1 pan:0.5046296296296297 ]", + "[ 31/8 ⇜ (4/1 → 35/8) | note:F4 clip:1 s:piano release:0.1 pan:0.5509259259259259 ]", + "[ 4/1 → 9/2 | note:F4 clip:1 s:piano release:0.1 pan:0.5509259259259259 ]", + "[ 4/1 → 5/1 | note:D3 clip:1 s:piano release:0.1 pan:0.4814814814814815 ]", + "[ 31/8 ⇜ (33/8 → 35/8) | note:Bb3 clip:1 s:piano release:0.1 pan:0.5185185185185186 ]", + "[ 31/8 ⇜ (33/8 → 35/8) | note:A4 clip:1 s:piano release:0.1 pan:0.5694444444444444 ]", + "[ (33/8 → 35/8) ⇝ 37/8 | note:Bb3 clip:1 s:piano release:0.1 pan:0.5185185185185186 ]", + "[ (33/8 → 35/8) ⇝ 37/8 | note:A4 clip:1 s:piano release:0.1 pan:0.5694444444444444 ]", + "[ 33/8 → 37/8 | note:F4 clip:1 s:piano release:0.1 pan:0.5509259259259259 ]", + "[ (33/8 → 5/1) ⇝ 41/8 | note:D3 clip:1 s:piano release:0.1 pan:0.4814814814814815 ]", + "[ 4/1 ⇜ (17/4 → 9/2) | note:Bb3 clip:1 s:piano release:0.1 pan:0.5185185185185186 ]", + "[ 4/1 ⇜ (17/4 → 9/2) | note:A4 clip:1 s:piano release:0.1 pan:0.5694444444444444 ]", + "[ (17/4 → 9/2) ⇝ 19/4 | note:A4 clip:1 s:piano release:0.1 pan:0.5694444444444444 ]", + "[ 17/4 → 19/4 | note:F4 clip:1 s:piano release:0.1 pan:0.5509259259259259 ]", + "[ (17/4 → 5/1) ⇝ 21/4 | note:F3 clip:1 s:piano release:0.1 pan:0.49537037037037035 ]", + "[ (17/4 → 5/1) ⇝ 21/4 | note:D3 clip:1 s:piano release:0.1 pan:0.4814814814814815 ]", + "[ 33/8 ⇜ (35/8 → 37/8) | note:Bb3 clip:1 s:piano release:0.1 pan:0.5185185185185186 ]", + "[ 33/8 ⇜ (35/8 → 37/8) | note:A4 clip:1 s:piano release:0.1 pan:0.5694444444444444 ]", + "[ (35/8 → 37/8) ⇝ 39/8 | note:A4 clip:1 s:piano release:0.1 pan:0.5694444444444444 ]", + "[ 35/8 → 39/8 | note:F4 clip:1 s:piano release:0.1 pan:0.5509259259259259 ]", + "[ (35/8 → 5/1) ⇝ 43/8 | note:F3 clip:1 s:piano release:0.1 pan:0.49537037037037035 ]", + "[ (35/8 → 5/1) ⇝ 43/8 | note:D3 clip:1 s:piano release:0.1 pan:0.4814814814814815 ]", + "[ 17/4 ⇜ (9/2 → 19/4) | note:A4 clip:1 s:piano release:0.1 pan:0.5694444444444444 ]", + "[ (9/2 → 19/4) ⇝ 5/1 | note:A4 clip:1 s:piano release:0.1 pan:0.5694444444444444 ]", + "[ (9/2 → 5/1) ⇝ 11/2 | note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ (9/2 → 5/1) ⇝ 11/2 | note:F3 clip:1 s:piano release:0.1 pan:0.49537037037037035 ]", + "[ 35/8 ⇜ (37/8 → 39/8) | note:A4 clip:1 s:piano release:0.1 pan:0.5694444444444444 ]", + "[ (37/8 → 39/8) ⇝ 41/8 | note:A4 clip:1 s:piano release:0.1 pan:0.5694444444444444 ]", + "[ (37/8 → 5/1) ⇝ 45/8 | note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ (37/8 → 5/1) ⇝ 45/8 | note:F3 clip:1 s:piano release:0.1 pan:0.49537037037037035 ]", + "[ 9/2 ⇜ (19/4 → 5/1) | note:A4 clip:1 s:piano release:0.1 pan:0.5694444444444444 ]", + "[ (19/4 → 5/1) ⇝ 23/4 | note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ (19/4 → 5/1) ⇝ 23/4 | note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ 37/8 ⇜ (39/8 → 5/1) ⇝ 41/8 | note:A4 clip:1 s:piano release:0.1 pan:0.5694444444444444 ]", + "[ (39/8 → 5/1) ⇝ 47/8 | note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ (39/8 → 5/1) ⇝ 47/8 | note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ 33/8 ⇜ (5/1 → 41/8) | note:D3 clip:1 s:piano release:0.1 pan:0.4814814814814815 ]", + "[ 35/8 ⇜ (5/1 → 41/8) ⇝ 43/8 | note:F3 clip:1 s:piano release:0.1 pan:0.49537037037037035 ]", + "[ 37/8 ⇜ (5/1 → 41/8) | note:A4 clip:1 s:piano release:0.1 pan:0.5694444444444444 ]", + "[ 37/8 ⇜ (5/1 → 41/8) ⇝ 45/8 | note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ 39/8 ⇜ (5/1 → 41/8) ⇝ 47/8 | note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 17/4 ⇜ (5/1 → 21/4) | note:F3 clip:1 s:piano release:0.1 pan:0.49537037037037035 ]", + "[ 17/4 ⇜ (5/1 → 21/4) | note:D3 clip:1 s:piano release:0.1 pan:0.4814814814814815 ]", + "[ 9/2 ⇜ (5/1 → 21/4) ⇝ 11/2 | note:F3 clip:1 s:piano release:0.1 pan:0.49537037037037035 ]", + "[ 19/4 ⇜ (5/1 → 21/4) ⇝ 23/4 | note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ (5/1 → 21/4) ⇝ 6/1 | note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 35/8 ⇜ (5/1 → 43/8) | note:D3 clip:1 s:piano release:0.1 pan:0.4814814814814815 ]", + "[ 37/8 ⇜ (5/1 → 43/8) ⇝ 45/8 | note:F3 clip:1 s:piano release:0.1 pan:0.49537037037037035 ]", + "[ 39/8 ⇜ (5/1 → 43/8) ⇝ 47/8 | note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ 9/2 ⇜ (5/1 → 11/2) | note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ 19/4 ⇜ (5/1 → 11/2) ⇝ 23/4 | note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 5/1 → 6/1 | note:F3 clip:1 s:piano release:0.1 pan:0.49537037037037035 ]", + "[ 35/8 ⇜ (41/8 → 43/8) | note:F3 clip:1 s:piano release:0.1 pan:0.49537037037037035 ]", + "[ (41/8 → 43/8) ⇝ 49/8 | note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 37/8 ⇜ (41/8 → 45/8) | note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ 39/8 ⇜ (41/8 → 45/8) ⇝ 47/8 | note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ (41/8 → 6/1) ⇝ 49/8 | note:F3 clip:1 s:piano release:0.1 pan:0.49537037037037035 ]", + "[ 9/2 ⇜ (21/4 → 11/2) | note:F3 clip:1 s:piano release:0.1 pan:0.49537037037037035 ]", + "[ 19/4 ⇜ (21/4 → 23/4) | note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ 5/1 ⇜ (21/4 → 23/4) ⇝ 6/1 | note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ (21/4 → 6/1) ⇝ 25/4 | note:A3 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", + "[ (21/4 → 6/1) ⇝ 25/4 | note:F3 clip:1 s:piano release:0.1 pan:0.49537037037037035 ]", + "[ 37/8 ⇜ (43/8 → 45/8) | note:F3 clip:1 s:piano release:0.1 pan:0.49537037037037035 ]", + "[ 39/8 ⇜ (43/8 → 47/8) | note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ 41/8 ⇜ (43/8 → 47/8) ⇝ 49/8 | note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ (43/8 → 6/1) ⇝ 51/8 | note:A3 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", + "[ (43/8 → 6/1) ⇝ 51/8 | note:F3 clip:1 s:piano release:0.1 pan:0.49537037037037035 ]", + "[ 19/4 ⇜ (11/2 → 23/4) | note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ (11/2 → 6/1) ⇝ 13/2 | note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ (11/2 → 6/1) ⇝ 13/2 | note:A3 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", + "[ 39/8 ⇜ (45/8 → 47/8) | note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ (45/8 → 6/1) ⇝ 53/8 | note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ (45/8 → 6/1) ⇝ 53/8 | note:A3 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", + "[ 5/1 ⇜ (23/4 → 6/1) | note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ (23/4 → 6/1) ⇝ 27/4 | note:G4 clip:1 s:piano release:0.1 pan:0.5601851851851851 ]", + "[ (23/4 → 6/1) ⇝ 27/4 | note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 41/8 ⇜ (47/8 → 6/1) ⇝ 49/8 | note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ (47/8 → 6/1) ⇝ 55/8 | note:G4 clip:1 s:piano release:0.1 pan:0.5601851851851851 ]", + "[ (47/8 → 6/1) ⇝ 55/8 | note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 41/8 ⇜ (6/1 → 49/8) | note:F3 clip:1 s:piano release:0.1 pan:0.49537037037037035 ]", + "[ 41/8 ⇜ (6/1 → 49/8) | note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 43/8 ⇜ (6/1 → 49/8) ⇝ 51/8 | note:A3 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", + "[ 45/8 ⇜ (6/1 → 49/8) ⇝ 53/8 | note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 47/8 ⇜ (6/1 → 49/8) ⇝ 55/8 | note:G4 clip:1 s:piano release:0.1 pan:0.5601851851851851 ]", + "[ 21/4 ⇜ (6/1 → 25/4) | note:A3 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", + "[ 21/4 ⇜ (6/1 → 25/4) | note:F3 clip:1 s:piano release:0.1 pan:0.49537037037037035 ]", + "[ 11/2 ⇜ (6/1 → 25/4) ⇝ 13/2 | note:A3 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", + "[ 23/4 ⇜ (6/1 → 25/4) ⇝ 27/4 | note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 6/1 → 25/4 | note:A3 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", + "[ (6/1 → 25/4) ⇝ 7/1 | note:G4 clip:1 s:piano release:0.1 pan:0.5601851851851851 ]", + "[ 43/8 ⇜ (6/1 → 51/8) | note:F3 clip:1 s:piano release:0.1 pan:0.49537037037037035 ]", + "[ 45/8 ⇜ (6/1 → 51/8) ⇝ 53/8 | note:A3 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", + "[ 47/8 ⇜ (6/1 → 51/8) ⇝ 55/8 | note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 11/2 ⇜ (6/1 → 13/2) | note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 23/4 ⇜ (6/1 → 13/2) ⇝ 27/4 | note:G4 clip:1 s:piano release:0.1 pan:0.5601851851851851 ]", + "[ 43/8 ⇜ (49/8 → 51/8) | note:A3 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", + "[ 49/8 → 51/8 | note:A3 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", + "[ (49/8 → 51/8) ⇝ 57/8 | note:G4 clip:1 s:piano release:0.1 pan:0.5601851851851851 ]", + "[ 45/8 ⇜ (49/8 → 53/8) | note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 47/8 ⇜ (49/8 → 53/8) ⇝ 55/8 | note:G4 clip:1 s:piano release:0.1 pan:0.5601851851851851 ]", + "[ 11/2 ⇜ (25/4 → 13/2) | note:A3 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", + "[ 25/4 → 13/2 | note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ 25/4 → 13/2 | note:A3 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", + "[ 23/4 ⇜ (25/4 → 27/4) | note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 6/1 ⇜ (25/4 → 27/4) ⇝ 7/1 | note:G4 clip:1 s:piano release:0.1 pan:0.5601851851851851 ]", + "[ 45/8 ⇜ (51/8 → 53/8) | note:A3 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", + "[ 51/8 → 53/8 | note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ 51/8 → 53/8 | note:A3 clip:1 s:piano release:0.1 pan:0.5138888888888888 ]", + "[ 47/8 ⇜ (51/8 → 55/8) | note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 49/8 ⇜ (51/8 → 55/8) ⇝ 57/8 | note:G4 clip:1 s:piano release:0.1 pan:0.5601851851851851 ]", + "[ 23/4 ⇜ (13/2 → 27/4) | note:G4 clip:1 s:piano release:0.1 pan:0.5601851851851851 ]", + "[ 13/2 → 27/4 | note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ 13/2 → 27/4 | note:G4 clip:1 s:piano release:0.1 pan:0.5601851851851851 ]", + "[ 13/2 → 27/4 | note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ 47/8 ⇜ (53/8 → 55/8) | note:G4 clip:1 s:piano release:0.1 pan:0.5601851851851851 ]", + "[ 53/8 → 55/8 | note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ 53/8 → 55/8 | note:G4 clip:1 s:piano release:0.1 pan:0.5601851851851851 ]", + "[ 53/8 → 55/8 | note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ 6/1 ⇜ (27/4 → 7/1) | note:G4 clip:1 s:piano release:0.1 pan:0.5601851851851851 ]", + "[ 27/4 → 7/1 | note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ 27/4 → 7/1 | note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 27/4 → 7/1 | note:Bb4 clip:1 s:piano release:0.1 pan:0.5740740740740741 ]", + "[ 27/4 → 7/1 | note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ 27/4 → 7/1 | note:G4 clip:1 s:piano release:0.1 pan:0.5601851851851851 ]", + "[ 49/8 ⇜ (55/8 → 7/1) ⇝ 57/8 | note:G4 clip:1 s:piano release:0.1 pan:0.5601851851851851 ]", + "[ (55/8 → 7/1) ⇝ 57/8 | note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ (55/8 → 7/1) ⇝ 57/8 | note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ (55/8 → 7/1) ⇝ 57/8 | note:Bb4 clip:1 s:piano release:0.1 pan:0.5740740740740741 ]", + "[ (55/8 → 7/1) ⇝ 57/8 | note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ (55/8 → 7/1) ⇝ 57/8 | note:G4 clip:1 s:piano release:0.1 pan:0.5601851851851851 ]", + "[ 49/8 ⇜ (7/1 → 57/8) | note:G4 clip:1 s:piano release:0.1 pan:0.5601851851851851 ]", + "[ 55/8 ⇜ (7/1 → 57/8) | note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ 55/8 ⇜ (7/1 → 57/8) | note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 55/8 ⇜ (7/1 → 57/8) | note:Bb4 clip:1 s:piano release:0.1 pan:0.5740740740740741 ]", + "[ 55/8 ⇜ (7/1 → 57/8) | note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ 55/8 ⇜ (7/1 → 57/8) | note:G4 clip:1 s:piano release:0.1 pan:0.5601851851851851 ]", + "[ 7/1 → 29/4 | note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 7/1 → 29/4 | note:Bb4 clip:1 s:piano release:0.1 pan:0.5740740740740741 ]", + "[ 7/1 → 29/4 | note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ 7/1 → 29/4 | note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 7/1 → 29/4 | note:Bb4 clip:1 s:piano release:0.1 pan:0.5740740740740741 ]", + "[ 7/1 → 15/2 | note:G3 clip:1 s:piano release:0.1 pan:0.5046296296296297 ]", + "[ 57/8 → 59/8 | note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 57/8 → 59/8 | note:Bb4 clip:1 s:piano release:0.1 pan:0.5740740740740741 ]", + "[ 57/8 → 59/8 | note:C4 clip:1 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ 57/8 → 59/8 | note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 57/8 → 59/8 | note:Bb4 clip:1 s:piano release:0.1 pan:0.5740740740740741 ]", + "[ 57/8 → 61/8 | note:G3 clip:1 s:piano release:0.1 pan:0.5046296296296297 ]", + "[ 29/4 → 15/2 | note:Bb4 clip:1 s:piano release:0.1 pan:0.5740740740740741 ]", + "[ 29/4 → 15/2 | note:D5 clip:1 s:piano release:0.1 pan:0.5925925925925926 ]", + "[ 29/4 → 15/2 | note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 29/4 → 15/2 | note:Bb4 clip:1 s:piano release:0.1 pan:0.5740740740740741 ]", + "[ 29/4 → 31/4 | note:Bb3 clip:1 s:piano release:0.1 pan:0.5185185185185186 ]", + "[ 29/4 → 31/4 | note:G3 clip:1 s:piano release:0.1 pan:0.5046296296296297 ]", + "[ 59/8 → 61/8 | note:Bb4 clip:1 s:piano release:0.1 pan:0.5740740740740741 ]", + "[ 59/8 → 61/8 | note:D5 clip:1 s:piano release:0.1 pan:0.5925925925925926 ]", + "[ 59/8 → 61/8 | note:E4 clip:1 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 59/8 → 61/8 | note:Bb4 clip:1 s:piano release:0.1 pan:0.5740740740740741 ]", + "[ 59/8 → 63/8 | note:Bb3 clip:1 s:piano release:0.1 pan:0.5185185185185186 ]", + "[ 59/8 → 63/8 | note:G3 clip:1 s:piano release:0.1 pan:0.5046296296296297 ]", + "[ 15/2 → 31/4 | note:D5 clip:1 s:piano release:0.1 pan:0.5925925925925926 ]", + "[ 15/2 → 31/4 | note:Bb4 clip:1 s:piano release:0.1 pan:0.5740740740740741 ]", + "[ 15/2 → 31/4 | note:D5 clip:1 s:piano release:0.1 pan:0.5925925925925926 ]", + "[ 15/2 → 8/1 | note:G3 clip:1 s:piano release:0.1 pan:0.5046296296296297 ]", + "[ 15/2 → 8/1 | note:F4 clip:1 s:piano release:0.1 pan:0.5509259259259259 ]", + "[ 15/2 → 8/1 | note:Bb3 clip:1 s:piano release:0.1 pan:0.5185185185185186 ]", + "[ 61/8 → 63/8 | note:D5 clip:1 s:piano release:0.1 pan:0.5925925925925926 ]", + "[ 61/8 → 63/8 | note:Bb4 clip:1 s:piano release:0.1 pan:0.5740740740740741 ]", + "[ 61/8 → 63/8 | note:D5 clip:1 s:piano release:0.1 pan:0.5925925925925926 ]", + "[ (61/8 → 8/1) ⇝ 65/8 | note:G3 clip:1 s:piano release:0.1 pan:0.5046296296296297 ]", + "[ (61/8 → 8/1) ⇝ 65/8 | note:F4 clip:1 s:piano release:0.1 pan:0.5509259259259259 ]", + "[ (61/8 → 8/1) ⇝ 65/8 | note:Bb3 clip:1 s:piano release:0.1 pan:0.5185185185185186 ]", + "[ 31/4 → 8/1 | note:D5 clip:1 s:piano release:0.1 pan:0.5925925925925926 ]", + "[ (31/4 → 8/1) ⇝ 33/4 | note:Bb3 clip:1 s:piano release:0.1 pan:0.5185185185185186 ]", + "[ (31/4 → 8/1) ⇝ 33/4 | note:A4 clip:1 s:piano release:0.1 pan:0.5694444444444444 ]", + "[ (31/4 → 8/1) ⇝ 33/4 | note:G3 clip:1 s:piano release:0.1 pan:0.5046296296296297 ]", + "[ (31/4 → 8/1) ⇝ 33/4 | note:F4 clip:1 s:piano release:0.1 pan:0.5509259259259259 ]", + "[ (63/8 → 8/1) ⇝ 65/8 | note:D5 clip:1 s:piano release:0.1 pan:0.5925925925925926 ]", + "[ (63/8 → 8/1) ⇝ 67/8 | note:Bb3 clip:1 s:piano release:0.1 pan:0.5185185185185186 ]", + "[ (63/8 → 8/1) ⇝ 67/8 | note:A4 clip:1 s:piano release:0.1 pan:0.5694444444444444 ]", + "[ (63/8 → 8/1) ⇝ 67/8 | note:G3 clip:1 s:piano release:0.1 pan:0.5046296296296297 ]", + "[ (63/8 → 8/1) ⇝ 67/8 | note:F4 clip:1 s:piano release:0.1 pan:0.5509259259259259 ]", ] `; @@ -6601,710 +6601,710 @@ exports[`renders tunes > tune: festivalOfFingers 1`] = ` exports[`renders tunes > tune: festivalOfFingers3 1`] = ` [ - "[ -1/2 ⇜ (0/1 → 1/6) | n:14 gain:0.25 clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", - "[ -1/6 ⇜ (0/1 → 1/6) | n:25 clip:1 gain:0.5790943073464694 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", - "[ -1/6 ⇜ (0/1 → 1/6) | n:22 clip:1 gain:0.5381966011250106 note:E6 s:piano release:0.1 pan:0.6574074074074074 ]", - "[ -1/3 ⇜ (0/1 → 1/3) | n:7 gain:0.3333333333333333 clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", - "[ 0/1 → 1/3 | n:22 clip:1 gain:0.6209056926535308 note:E6 s:piano release:0.1 pan:0.6574074074074074 ]", - "[ 0/1 → 1/3 | n:25 clip:1 gain:0.5790943073464694 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", - "[ -3/2 ⇜ (0/1 → 1/2) | n:7 gain:0.5 clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", - "[ -3/2 ⇜ (0/1 → 1/2) | n:9 gain:0.5 clip:1 note:F4 s:piano release:0.1 pan:0.5509259259259259 ]", - "[ -3/2 ⇜ (0/1 → 1/2) | n:13 gain:0.5 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", - "[ -1/2 ⇜ (0/1 → 1/2) | n:14 gain:0.5 clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", - "[ -1/2 ⇜ (0/1 → 1/2) | n:29 gain:0.25 clip:1 note:E7 s:piano release:0.1 pan:0.712962962962963 ]", - "[ -1/6 ⇜ (0/1 → 1/2) | n:0 gain:0.5 clip:1 note:D3 s:piano release:0.1 pan:0.4814814814814815 ]", - "[ 0/1 → 2/3 | n:-7 gain:1 clip:1 note:D2 s:piano release:0.1 pan:0.42592592592592593 ]", - "[ -1/1 ⇜ (0/1 → 1/1) | n:14 gain:0.3333333333333333 clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", - "[ -1/1 ⇜ (0/1 → 1/1) | n:16 gain:0.3333333333333333 clip:1 note:F5 s:piano release:0.1 pan:0.6064814814814814 ]", - "[ -1/1 ⇜ (0/1 → 1/1) | n:20 gain:0.3333333333333333 clip:1 note:C6 s:piano release:0.1 pan:0.6388888888888888 ]", - "[ -1/2 ⇜ (0/1 → 1/1) ⇝ 3/2 | n:21 gain:0.25 clip:1 note:D6 s:piano release:0.1 pan:0.6481481481481481 ]", - "[ -1/2 ⇜ (0/1 → 1/1) ⇝ 3/2 | n:23 gain:0.25 clip:1 note:F6 s:piano release:0.1 pan:0.662037037037037 ]", - "[ -1/2 ⇜ (0/1 → 1/1) ⇝ 3/2 | n:27 gain:0.25 clip:1 note:C7 s:piano release:0.1 pan:0.6944444444444444 ]", - "[ 0/1 → 1/1 | n:8 gain:1 clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 0/1 → 1/1 | n:21 gain:0.3333333333333333 clip:1 note:D6 s:piano release:0.1 pan:0.6481481481481481 ]", - "[ (0/1 → 1/1) ⇝ 2/1 | n:0 gain:1 clip:1 note:D3 s:piano release:0.1 pan:0.4814814814814815 ]", - "[ (0/1 → 1/1) ⇝ 2/1 | n:2 gain:1 clip:1 note:F3 s:piano release:0.1 pan:0.49537037037037035 ]", - "[ (0/1 → 1/1) ⇝ 2/1 | n:6 gain:1 clip:1 note:C4 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ 1/6 → 1/2 | n:22 clip:1 gain:0.6209056926535308 note:E6 s:piano release:0.1 pan:0.6574074074074074 ]", - "[ 1/6 → 1/2 | n:25 clip:1 gain:0.5790943073464694 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", - "[ 1/6 → 5/6 | n:14 gain:0.25 clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", - "[ 1/3 → 2/3 | n:25 clip:1 gain:0.6618033988749895 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", - "[ 1/3 → 2/3 | n:22 clip:1 gain:0.6209056926535308 note:E6 s:piano release:0.1 pan:0.6574074074074074 ]", - "[ 1/3 → 1/1 | n:7 gain:0.3333333333333333 clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", - "[ 1/2 → 5/6 | n:25 clip:1 gain:0.6618033988749895 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", - "[ 1/2 → 5/6 | n:22 clip:1 gain:0.6209056926535308 note:E6 s:piano release:0.1 pan:0.6574074074074074 ]", - "[ (1/2 → 1/1) ⇝ 7/6 | n:0 gain:0.5 clip:1 note:D3 s:piano release:0.1 pan:0.4814814814814815 ]", - "[ (1/2 → 1/1) ⇝ 3/2 | n:15 gain:0.5 clip:1 note:E5 s:piano release:0.1 pan:0.6018518518518519 ]", - "[ (1/2 → 1/1) ⇝ 3/2 | n:28 gain:0.25 clip:1 note:D7 s:piano release:0.1 pan:0.7037037037037037 ]", - "[ (1/2 → 1/1) ⇝ 5/2 | n:7 gain:0.5 clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", - "[ (1/2 → 1/1) ⇝ 5/2 | n:9 gain:0.5 clip:1 note:F4 s:piano release:0.1 pan:0.5509259259259259 ]", - "[ (1/2 → 1/1) ⇝ 5/2 | n:13 gain:0.5 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", - "[ 2/3 → 1/1 | n:22 clip:1 gain:0.7000000000000001 note:E6 s:piano release:0.1 pan:0.6574074074074074 ]", - "[ 2/3 → 1/1 | n:25 clip:1 gain:0.6618033988749895 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", - "[ (2/3 → 1/1) ⇝ 4/3 | n:-7 gain:1 clip:1 note:D2 s:piano release:0.1 pan:0.42592592592592593 ]", - "[ (5/6 → 1/1) ⇝ 7/6 | n:22 clip:1 gain:0.7000000000000001 note:E6 s:piano release:0.1 pan:0.6574074074074074 ]", - "[ (5/6 → 1/1) ⇝ 7/6 | n:25 clip:1 gain:0.6618033988749895 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", - "[ (5/6 → 1/1) ⇝ 3/2 | n:14 gain:0.25 clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", - "[ 1/2 ⇜ (1/1 → 7/6) | n:0 gain:0.5 clip:1 note:D3 s:piano release:0.1 pan:0.4814814814814815 ]", - "[ 5/6 ⇜ (1/1 → 7/6) | n:22 clip:1 gain:0.7000000000000001 note:E6 s:piano release:0.1 pan:0.6574074074074074 ]", - "[ 5/6 ⇜ (1/1 → 7/6) | n:25 clip:1 gain:0.6618033988749895 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", - "[ 2/3 ⇜ (1/1 → 4/3) | n:-7 gain:1 clip:1 note:D2 s:piano release:0.1 pan:0.42592592592592593 ]", - "[ 1/1 → 4/3 | n:25 clip:1 gain:0.7338261212717717 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", - "[ 1/1 → 4/3 | n:22 clip:1 gain:0.7000000000000001 note:E6 s:piano release:0.1 pan:0.6574074074074074 ]", - "[ -1/2 ⇜ (1/1 → 3/2) | n:21 gain:0.25 clip:1 note:D6 s:piano release:0.1 pan:0.6481481481481481 ]", - "[ -1/2 ⇜ (1/1 → 3/2) | n:23 gain:0.25 clip:1 note:F6 s:piano release:0.1 pan:0.662037037037037 ]", - "[ -1/2 ⇜ (1/1 → 3/2) | n:27 gain:0.25 clip:1 note:C7 s:piano release:0.1 pan:0.6944444444444444 ]", - "[ 1/2 ⇜ (1/1 → 3/2) | n:15 gain:0.5 clip:1 note:E5 s:piano release:0.1 pan:0.6018518518518519 ]", - "[ 1/2 ⇜ (1/1 → 3/2) | n:28 gain:0.25 clip:1 note:D7 s:piano release:0.1 pan:0.7037037037037037 ]", - "[ 5/6 ⇜ (1/1 → 3/2) | n:14 gain:0.25 clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", - "[ 1/1 → 5/3 | n:7 gain:0.3333333333333333 clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", - "[ 0/1 ⇜ (1/1 → 2/1) | n:0 gain:1 clip:1 note:D3 s:piano release:0.1 pan:0.4814814814814815 ]", - "[ 0/1 ⇜ (1/1 → 2/1) | n:2 gain:1 clip:1 note:F3 s:piano release:0.1 pan:0.49537037037037035 ]", - "[ 0/1 ⇜ (1/1 → 2/1) | n:6 gain:1 clip:1 note:C4 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ 1/2 ⇜ (1/1 → 2/1) ⇝ 5/2 | n:7 gain:0.5 clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", - "[ 1/2 ⇜ (1/1 → 2/1) ⇝ 5/2 | n:9 gain:0.5 clip:1 note:F4 s:piano release:0.1 pan:0.5509259259259259 ]", - "[ 1/2 ⇜ (1/1 → 2/1) ⇝ 5/2 | n:13 gain:0.5 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", - "[ 1/1 → 2/1 | n:7 gain:1 clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", - "[ 1/1 → 2/1 | n:22 gain:0.3333333333333333 clip:1 note:E6 s:piano release:0.1 pan:0.6574074074074074 ]", - "[ (1/1 → 2/1) ⇝ 3/1 | n:14 gain:0.3333333333333333 clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", - "[ (1/1 → 2/1) ⇝ 3/1 | n:16 gain:0.3333333333333333 clip:1 note:F5 s:piano release:0.1 pan:0.6064814814814814 ]", - "[ (1/1 → 2/1) ⇝ 3/1 | n:20 gain:0.3333333333333333 clip:1 note:C6 s:piano release:0.1 pan:0.6388888888888888 ]", - "[ 7/6 → 3/2 | n:25 clip:1 gain:0.7338261212717717 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", - "[ 7/6 → 3/2 | n:22 clip:1 gain:0.7000000000000001 note:E6 s:piano release:0.1 pan:0.6574074074074074 ]", - "[ 7/6 → 11/6 | n:0 gain:0.5 clip:1 note:D3 s:piano release:0.1 pan:0.4814814814814815 ]", - "[ 4/3 → 5/3 | n:22 clip:1 gain:0.7618033988749895 note:E6 s:piano release:0.1 pan:0.6574074074074074 ]", - "[ 4/3 → 5/3 | n:25 clip:1 gain:0.7338261212717717 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", - "[ 4/3 → 2/1 | n:-7 gain:1 clip:1 note:D2 s:piano release:0.1 pan:0.42592592592592593 ]", - "[ 3/2 → 11/6 | n:22 clip:1 gain:0.7618033988749895 note:E6 s:piano release:0.1 pan:0.6574074074074074 ]", - "[ 3/2 → 11/6 | n:25 clip:1 gain:0.7338261212717717 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", - "[ (3/2 → 2/1) ⇝ 13/6 | n:14 gain:0.25 clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", - "[ (3/2 → 2/1) ⇝ 5/2 | n:14 gain:0.5 clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", - "[ (3/2 → 2/1) ⇝ 5/2 | n:29 gain:0.25 clip:1 note:E7 s:piano release:0.1 pan:0.712962962962963 ]", - "[ (3/2 → 2/1) ⇝ 7/2 | n:21 gain:0.25 clip:1 note:D6 s:piano release:0.1 pan:0.6481481481481481 ]", - "[ (3/2 → 2/1) ⇝ 7/2 | n:23 gain:0.25 clip:1 note:F6 s:piano release:0.1 pan:0.662037037037037 ]", - "[ (3/2 → 2/1) ⇝ 7/2 | n:27 gain:0.25 clip:1 note:C7 s:piano release:0.1 pan:0.6944444444444444 ]", - "[ 5/3 → 2/1 | n:25 clip:1 gain:0.7827090915285202 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", - "[ 5/3 → 2/1 | n:22 clip:1 gain:0.7618033988749895 note:E6 s:piano release:0.1 pan:0.6574074074074074 ]", - "[ (5/3 → 2/1) ⇝ 7/3 | n:7 gain:0.3333333333333333 clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", - "[ (11/6 → 2/1) ⇝ 13/6 | n:25 clip:1 gain:0.7827090915285202 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", - "[ (11/6 → 2/1) ⇝ 13/6 | n:22 clip:1 gain:0.7618033988749895 note:E6 s:piano release:0.1 pan:0.6574074074074074 ]", - "[ (11/6 → 2/1) ⇝ 5/2 | n:0 gain:0.5 clip:1 note:D3 s:piano release:0.1 pan:0.4814814814814815 ]", - "[ 3/2 ⇜ (2/1 → 13/6) | n:14 gain:0.25 clip:1 note:G5 s:piano release:0.1 pan:0.6157407407407407 ]", - "[ 11/6 ⇜ (2/1 → 13/6) | n:25 clip:1 gain:0.7827090915285202 note:D7 s:piano release:0.1 pan:0.7037037037037037 ]", - "[ 11/6 ⇜ (2/1 → 13/6) | n:22 clip:1 gain:0.7618033988749895 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", - "[ 5/3 ⇜ (2/1 → 7/3) | n:7 gain:0.3333333333333333 clip:1 note:G4 s:piano release:0.1 pan:0.5601851851851851 ]", - "[ 2/1 → 7/3 | n:22 clip:1 gain:0.7956295201467611 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", - "[ 2/1 → 7/3 | n:25 clip:1 gain:0.7827090915285202 note:D7 s:piano release:0.1 pan:0.7037037037037037 ]", - "[ 1/2 ⇜ (2/1 → 5/2) | n:7 gain:0.5 clip:1 note:G4 s:piano release:0.1 pan:0.5601851851851851 ]", - "[ 1/2 ⇜ (2/1 → 5/2) | n:9 gain:0.5 clip:1 note:B4 s:piano release:0.1 pan:0.5787037037037037 ]", - "[ 1/2 ⇜ (2/1 → 5/2) | n:13 gain:0.5 clip:1 note:F5 s:piano release:0.1 pan:0.6064814814814814 ]", - "[ 3/2 ⇜ (2/1 → 5/2) | n:14 gain:0.5 clip:1 note:G5 s:piano release:0.1 pan:0.6157407407407407 ]", - "[ 3/2 ⇜ (2/1 → 5/2) | n:29 gain:0.25 clip:1 note:A7 s:piano release:0.1 pan:0.7361111111111112 ]", - "[ 11/6 ⇜ (2/1 → 5/2) | n:0 gain:0.5 clip:1 note:G3 s:piano release:0.1 pan:0.5046296296296297 ]", - "[ 2/1 → 8/3 | n:-7 gain:1 clip:1 note:G2 s:piano release:0.1 pan:0.44907407407407407 ]", - "[ 1/1 ⇜ (2/1 → 3/1) | n:14 gain:0.3333333333333333 clip:1 note:G5 s:piano release:0.1 pan:0.6157407407407407 ]", - "[ 1/1 ⇜ (2/1 → 3/1) | n:16 gain:0.3333333333333333 clip:1 note:B5 s:piano release:0.1 pan:0.6342592592592593 ]", - "[ 1/1 ⇜ (2/1 → 3/1) | n:20 gain:0.3333333333333333 clip:1 note:F6 s:piano release:0.1 pan:0.662037037037037 ]", - "[ 3/2 ⇜ (2/1 → 3/1) ⇝ 7/2 | n:21 gain:0.25 clip:1 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", - "[ 3/2 ⇜ (2/1 → 3/1) ⇝ 7/2 | n:23 gain:0.25 clip:1 note:B6 s:piano release:0.1 pan:0.6898148148148149 ]", - "[ 3/2 ⇜ (2/1 → 3/1) ⇝ 7/2 | n:27 gain:0.25 clip:1 note:F7 s:piano release:0.1 pan:0.7175925925925926 ]", - "[ 2/1 → 3/1 | n:8 gain:1 clip:1 note:A4 s:piano release:0.1 pan:0.5694444444444444 ]", - "[ 2/1 → 3/1 | n:21 gain:0.3333333333333333 clip:1 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", - "[ (2/1 → 3/1) ⇝ 4/1 | n:0 gain:1 clip:1 note:G3 s:piano release:0.1 pan:0.5046296296296297 ]", - "[ (2/1 → 3/1) ⇝ 4/1 | n:2 gain:1 clip:1 note:B3 s:piano release:0.1 pan:0.5231481481481481 ]", - "[ (2/1 → 3/1) ⇝ 4/1 | n:6 gain:1 clip:1 note:F4 s:piano release:0.1 pan:0.5509259259259259 ]", - "[ 13/6 → 5/2 | n:22 clip:1 gain:0.7956295201467611 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", - "[ 13/6 → 5/2 | n:25 clip:1 gain:0.7827090915285202 note:D7 s:piano release:0.1 pan:0.7037037037037037 ]", - "[ 13/6 → 17/6 | n:14 gain:0.25 clip:1 note:G5 s:piano release:0.1 pan:0.6157407407407407 ]", - "[ 7/3 → 8/3 | n:25 clip:1 gain:0.8 note:D7 s:piano release:0.1 pan:0.7037037037037037 ]", - "[ 7/3 → 8/3 | n:22 clip:1 gain:0.7956295201467611 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", - "[ 7/3 → 3/1 | n:7 gain:0.3333333333333333 clip:1 note:G4 s:piano release:0.1 pan:0.5601851851851851 ]", - "[ 5/2 → 17/6 | n:25 clip:1 gain:0.8 note:D7 s:piano release:0.1 pan:0.7037037037037037 ]", - "[ 5/2 → 17/6 | n:22 clip:1 gain:0.7956295201467611 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", - "[ (5/2 → 3/1) ⇝ 19/6 | n:0 gain:0.5 clip:1 note:G3 s:piano release:0.1 pan:0.5046296296296297 ]", - "[ (5/2 → 3/1) ⇝ 7/2 | n:15 gain:0.5 clip:1 note:A5 s:piano release:0.1 pan:0.625 ]", - "[ (5/2 → 3/1) ⇝ 7/2 | n:28 gain:0.25 clip:1 note:G7 s:piano release:0.1 pan:0.7268518518518519 ]", - "[ (5/2 → 3/1) ⇝ 9/2 | n:7 gain:0.5 clip:1 note:G4 s:piano release:0.1 pan:0.5601851851851851 ]", - "[ (5/2 → 3/1) ⇝ 9/2 | n:9 gain:0.5 clip:1 note:B4 s:piano release:0.1 pan:0.5787037037037037 ]", - "[ (5/2 → 3/1) ⇝ 9/2 | n:13 gain:0.5 clip:1 note:F5 s:piano release:0.1 pan:0.6064814814814814 ]", - "[ 8/3 → 3/1 | n:22 clip:1 gain:0.7956295201467611 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", - "[ 8/3 → 3/1 | n:25 clip:1 gain:0.8 note:D7 s:piano release:0.1 pan:0.7037037037037037 ]", - "[ (8/3 → 3/1) ⇝ 10/3 | n:-7 gain:1 clip:1 note:G2 s:piano release:0.1 pan:0.44907407407407407 ]", - "[ (17/6 → 3/1) ⇝ 19/6 | n:22 clip:1 gain:0.7956295201467611 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", - "[ (17/6 → 3/1) ⇝ 19/6 | n:25 clip:1 gain:0.8 note:D7 s:piano release:0.1 pan:0.7037037037037037 ]", - "[ (17/6 → 3/1) ⇝ 7/2 | n:14 gain:0.25 clip:1 note:G5 s:piano release:0.1 pan:0.6157407407407407 ]", - "[ 5/2 ⇜ (3/1 → 19/6) | n:0 gain:0.5 clip:1 note:G3 s:piano release:0.1 pan:0.5046296296296297 ]", - "[ 17/6 ⇜ (3/1 → 19/6) | n:22 clip:1 gain:0.7956295201467611 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", - "[ 17/6 ⇜ (3/1 → 19/6) | n:25 clip:1 gain:0.8 note:D7 s:piano release:0.1 pan:0.7037037037037037 ]", - "[ 8/3 ⇜ (3/1 → 10/3) | n:-7 gain:1 clip:1 note:G2 s:piano release:0.1 pan:0.44907407407407407 ]", - "[ 3/1 → 10/3 | n:25 clip:1 gain:0.7827090915285202 note:D7 s:piano release:0.1 pan:0.7037037037037037 ]", - "[ 3/1 → 10/3 | n:22 clip:1 gain:0.7956295201467611 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", - "[ 3/2 ⇜ (3/1 → 7/2) | n:21 gain:0.25 clip:1 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", - "[ 3/2 ⇜ (3/1 → 7/2) | n:23 gain:0.25 clip:1 note:B6 s:piano release:0.1 pan:0.6898148148148149 ]", - "[ 3/2 ⇜ (3/1 → 7/2) | n:27 gain:0.25 clip:1 note:F7 s:piano release:0.1 pan:0.7175925925925926 ]", - "[ 5/2 ⇜ (3/1 → 7/2) | n:15 gain:0.5 clip:1 note:A5 s:piano release:0.1 pan:0.625 ]", - "[ 5/2 ⇜ (3/1 → 7/2) | n:28 gain:0.25 clip:1 note:G7 s:piano release:0.1 pan:0.7268518518518519 ]", - "[ 17/6 ⇜ (3/1 → 7/2) | n:14 gain:0.25 clip:1 note:G5 s:piano release:0.1 pan:0.6157407407407407 ]", - "[ 3/1 → 11/3 | n:7 gain:0.3333333333333333 clip:1 note:G4 s:piano release:0.1 pan:0.5601851851851851 ]", - "[ 2/1 ⇜ (3/1 → 4/1) | n:0 gain:1 clip:1 note:G3 s:piano release:0.1 pan:0.5046296296296297 ]", - "[ 2/1 ⇜ (3/1 → 4/1) | n:2 gain:1 clip:1 note:B3 s:piano release:0.1 pan:0.5231481481481481 ]", - "[ 2/1 ⇜ (3/1 → 4/1) | n:6 gain:1 clip:1 note:F4 s:piano release:0.1 pan:0.5509259259259259 ]", - "[ 5/2 ⇜ (3/1 → 4/1) ⇝ 9/2 | n:7 gain:0.5 clip:1 note:G4 s:piano release:0.1 pan:0.5601851851851851 ]", - "[ 5/2 ⇜ (3/1 → 4/1) ⇝ 9/2 | n:9 gain:0.5 clip:1 note:B4 s:piano release:0.1 pan:0.5787037037037037 ]", - "[ 5/2 ⇜ (3/1 → 4/1) ⇝ 9/2 | n:13 gain:0.5 clip:1 note:F5 s:piano release:0.1 pan:0.6064814814814814 ]", - "[ 3/1 → 4/1 | n:7 gain:1 clip:1 note:G4 s:piano release:0.1 pan:0.5601851851851851 ]", - "[ 3/1 → 4/1 | n:22 gain:0.3333333333333333 clip:1 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", - "[ (3/1 → 4/1) ⇝ 5/1 | n:14 gain:0.3333333333333333 clip:1 note:G5 s:piano release:0.1 pan:0.6157407407407407 ]", - "[ (3/1 → 4/1) ⇝ 5/1 | n:16 gain:0.3333333333333333 clip:1 note:B5 s:piano release:0.1 pan:0.6342592592592593 ]", - "[ (3/1 → 4/1) ⇝ 5/1 | n:20 gain:0.3333333333333333 clip:1 note:F6 s:piano release:0.1 pan:0.662037037037037 ]", - "[ 19/6 → 7/2 | n:25 clip:1 gain:0.7827090915285202 note:D7 s:piano release:0.1 pan:0.7037037037037037 ]", - "[ 19/6 → 7/2 | n:22 clip:1 gain:0.7956295201467611 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", - "[ 19/6 → 23/6 | n:0 gain:0.5 clip:1 note:G3 s:piano release:0.1 pan:0.5046296296296297 ]", - "[ 10/3 → 11/3 | n:22 clip:1 gain:0.7618033988749895 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", - "[ 10/3 → 11/3 | n:25 clip:1 gain:0.7827090915285202 note:D7 s:piano release:0.1 pan:0.7037037037037037 ]", - "[ 10/3 → 4/1 | n:-7 gain:1 clip:1 note:G2 s:piano release:0.1 pan:0.44907407407407407 ]", - "[ 7/2 → 23/6 | n:22 clip:1 gain:0.7618033988749895 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", - "[ 7/2 → 23/6 | n:25 clip:1 gain:0.7827090915285202 note:D7 s:piano release:0.1 pan:0.7037037037037037 ]", - "[ (7/2 → 4/1) ⇝ 25/6 | n:14 gain:0.25 clip:1 note:G5 s:piano release:0.1 pan:0.6157407407407407 ]", - "[ (7/2 → 4/1) ⇝ 9/2 | n:14 gain:0.5 clip:1 note:G5 s:piano release:0.1 pan:0.6157407407407407 ]", - "[ (7/2 → 4/1) ⇝ 9/2 | n:29 gain:0.25 clip:1 note:A7 s:piano release:0.1 pan:0.7361111111111112 ]", - "[ (7/2 → 4/1) ⇝ 11/2 | n:21 gain:0.25 clip:1 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", - "[ (7/2 → 4/1) ⇝ 11/2 | n:23 gain:0.25 clip:1 note:B6 s:piano release:0.1 pan:0.6898148148148149 ]", - "[ (7/2 → 4/1) ⇝ 11/2 | n:27 gain:0.25 clip:1 note:F7 s:piano release:0.1 pan:0.7175925925925926 ]", - "[ 11/3 → 4/1 | n:25 clip:1 gain:0.7338261212717716 note:D7 s:piano release:0.1 pan:0.7037037037037037 ]", - "[ 11/3 → 4/1 | n:22 clip:1 gain:0.7618033988749895 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", - "[ (11/3 → 4/1) ⇝ 13/3 | n:7 gain:0.3333333333333333 clip:1 note:G4 s:piano release:0.1 pan:0.5601851851851851 ]", - "[ (23/6 → 4/1) ⇝ 25/6 | n:25 clip:1 gain:0.7338261212717716 note:D7 s:piano release:0.1 pan:0.7037037037037037 ]", - "[ (23/6 → 4/1) ⇝ 25/6 | n:22 clip:1 gain:0.7618033988749895 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", - "[ (23/6 → 4/1) ⇝ 9/2 | n:0 gain:0.5 clip:1 note:G3 s:piano release:0.1 pan:0.5046296296296297 ]", - "[ 7/2 ⇜ (4/1 → 25/6) | n:14 gain:0.25 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", - "[ 23/6 ⇜ (4/1 → 25/6) | n:25 clip:1 gain:0.7338261212717716 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", - "[ 23/6 ⇜ (4/1 → 25/6) | n:22 clip:1 gain:0.7618033988749895 note:D6 s:piano release:0.1 pan:0.6481481481481481 ]", - "[ 11/3 ⇜ (4/1 → 13/3) | n:7 gain:0.3333333333333333 clip:1 note:C4 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ 4/1 → 13/3 | n:22 clip:1 gain:0.7000000000000001 note:D6 s:piano release:0.1 pan:0.6481481481481481 ]", - "[ 4/1 → 13/3 | n:25 clip:1 gain:0.7338261212717716 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", - "[ 5/2 ⇜ (4/1 → 9/2) | n:7 gain:0.5 clip:1 note:C4 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ 5/2 ⇜ (4/1 → 9/2) | n:9 gain:0.5 clip:1 note:Eb4 s:piano release:0.1 pan:0.5416666666666667 ]", - "[ 5/2 ⇜ (4/1 → 9/2) | n:13 gain:0.5 clip:1 note:Bb4 s:piano release:0.1 pan:0.5740740740740741 ]", - "[ 7/2 ⇜ (4/1 → 9/2) | n:14 gain:0.5 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", - "[ 7/2 ⇜ (4/1 → 9/2) | n:29 gain:0.25 clip:1 note:D7 s:piano release:0.1 pan:0.7037037037037037 ]", - "[ 23/6 ⇜ (4/1 → 9/2) | n:0 gain:0.5 clip:1 note:C3 s:piano release:0.1 pan:0.4722222222222222 ]", - "[ 4/1 → 14/3 | n:-7 gain:1 clip:1 note:C2 s:piano release:0.1 pan:0.41666666666666663 ]", - "[ 3/1 ⇜ (4/1 → 5/1) | n:14 gain:0.3333333333333333 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", - "[ 3/1 ⇜ (4/1 → 5/1) | n:16 gain:0.3333333333333333 clip:1 note:Eb5 s:piano release:0.1 pan:0.5972222222222222 ]", - "[ 3/1 ⇜ (4/1 → 5/1) | n:20 gain:0.3333333333333333 clip:1 note:Bb5 s:piano release:0.1 pan:0.6296296296296297 ]", - "[ 7/2 ⇜ (4/1 → 5/1) ⇝ 11/2 | n:21 gain:0.25 clip:1 note:C6 s:piano release:0.1 pan:0.6388888888888888 ]", - "[ 7/2 ⇜ (4/1 → 5/1) ⇝ 11/2 | n:23 gain:0.25 clip:1 note:Eb6 s:piano release:0.1 pan:0.6527777777777778 ]", - "[ 7/2 ⇜ (4/1 → 5/1) ⇝ 11/2 | n:27 gain:0.25 clip:1 note:Bb6 s:piano release:0.1 pan:0.6851851851851851 ]", - "[ 4/1 → 5/1 | n:8 gain:1 clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", - "[ 4/1 → 5/1 | n:21 gain:0.3333333333333333 clip:1 note:C6 s:piano release:0.1 pan:0.6388888888888888 ]", - "[ (4/1 → 5/1) ⇝ 6/1 | n:0 gain:1 clip:1 note:C3 s:piano release:0.1 pan:0.4722222222222222 ]", - "[ (4/1 → 5/1) ⇝ 6/1 | n:2 gain:1 clip:1 note:Eb3 s:piano release:0.1 pan:0.4861111111111111 ]", - "[ (4/1 → 5/1) ⇝ 6/1 | n:6 gain:1 clip:1 note:Bb3 s:piano release:0.1 pan:0.5185185185185186 ]", - "[ 25/6 → 9/2 | n:22 clip:1 gain:0.7000000000000001 note:D6 s:piano release:0.1 pan:0.6481481481481481 ]", - "[ 25/6 → 9/2 | n:25 clip:1 gain:0.7338261212717716 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", - "[ 25/6 → 29/6 | n:14 gain:0.25 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", - "[ 13/3 → 14/3 | n:25 clip:1 gain:0.6618033988749895 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", - "[ 13/3 → 14/3 | n:22 clip:1 gain:0.7000000000000001 note:D6 s:piano release:0.1 pan:0.6481481481481481 ]", - "[ 13/3 → 5/1 | n:7 gain:0.3333333333333333 clip:1 note:C4 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ 9/2 → 29/6 | n:25 clip:1 gain:0.6618033988749895 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", - "[ 9/2 → 29/6 | n:22 clip:1 gain:0.7000000000000001 note:D6 s:piano release:0.1 pan:0.6481481481481481 ]", - "[ (9/2 → 5/1) ⇝ 31/6 | n:0 gain:0.5 clip:1 note:C3 s:piano release:0.1 pan:0.4722222222222222 ]", - "[ (9/2 → 5/1) ⇝ 11/2 | n:15 gain:0.5 clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", - "[ (9/2 → 5/1) ⇝ 11/2 | n:28 gain:0.25 clip:1 note:C7 s:piano release:0.1 pan:0.6944444444444444 ]", - "[ (9/2 → 5/1) ⇝ 13/2 | n:7 gain:0.5 clip:1 note:C4 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ (9/2 → 5/1) ⇝ 13/2 | n:9 gain:0.5 clip:1 note:Eb4 s:piano release:0.1 pan:0.5416666666666667 ]", - "[ (9/2 → 5/1) ⇝ 13/2 | n:13 gain:0.5 clip:1 note:Bb4 s:piano release:0.1 pan:0.5740740740740741 ]", - "[ 14/3 → 5/1 | n:22 clip:1 gain:0.6209056926535308 note:D6 s:piano release:0.1 pan:0.6481481481481481 ]", - "[ 14/3 → 5/1 | n:25 clip:1 gain:0.6618033988749895 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", - "[ (14/3 → 5/1) ⇝ 16/3 | n:-7 gain:1 clip:1 note:C2 s:piano release:0.1 pan:0.41666666666666663 ]", - "[ (29/6 → 5/1) ⇝ 31/6 | n:22 clip:1 gain:0.6209056926535308 note:D6 s:piano release:0.1 pan:0.6481481481481481 ]", - "[ (29/6 → 5/1) ⇝ 31/6 | n:25 clip:1 gain:0.6618033988749895 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", - "[ (29/6 → 5/1) ⇝ 11/2 | n:14 gain:0.25 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", - "[ 9/2 ⇜ (5/1 → 31/6) | n:0 gain:0.5 clip:1 note:C3 s:piano release:0.1 pan:0.4722222222222222 ]", - "[ 29/6 ⇜ (5/1 → 31/6) | n:22 clip:1 gain:0.6209056926535308 note:D6 s:piano release:0.1 pan:0.6481481481481481 ]", - "[ 29/6 ⇜ (5/1 → 31/6) | n:25 clip:1 gain:0.6618033988749895 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", - "[ 14/3 ⇜ (5/1 → 16/3) | n:-7 gain:1 clip:1 note:C2 s:piano release:0.1 pan:0.41666666666666663 ]", - "[ 5/1 → 16/3 | n:25 clip:1 gain:0.5790943073464694 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", - "[ 5/1 → 16/3 | n:22 clip:1 gain:0.6209056926535308 note:D6 s:piano release:0.1 pan:0.6481481481481481 ]", - "[ 7/2 ⇜ (5/1 → 11/2) | n:21 gain:0.25 clip:1 note:C6 s:piano release:0.1 pan:0.6388888888888888 ]", - "[ 7/2 ⇜ (5/1 → 11/2) | n:23 gain:0.25 clip:1 note:Eb6 s:piano release:0.1 pan:0.6527777777777778 ]", - "[ 7/2 ⇜ (5/1 → 11/2) | n:27 gain:0.25 clip:1 note:Bb6 s:piano release:0.1 pan:0.6851851851851851 ]", - "[ 9/2 ⇜ (5/1 → 11/2) | n:15 gain:0.5 clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", - "[ 9/2 ⇜ (5/1 → 11/2) | n:28 gain:0.25 clip:1 note:C7 s:piano release:0.1 pan:0.6944444444444444 ]", - "[ 29/6 ⇜ (5/1 → 11/2) | n:14 gain:0.25 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", - "[ 5/1 → 17/3 | n:7 gain:0.3333333333333333 clip:1 note:C4 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ 4/1 ⇜ (5/1 → 6/1) | n:0 gain:1 clip:1 note:C3 s:piano release:0.1 pan:0.4722222222222222 ]", - "[ 4/1 ⇜ (5/1 → 6/1) | n:2 gain:1 clip:1 note:Eb3 s:piano release:0.1 pan:0.4861111111111111 ]", - "[ 4/1 ⇜ (5/1 → 6/1) | n:6 gain:1 clip:1 note:Bb3 s:piano release:0.1 pan:0.5185185185185186 ]", - "[ 9/2 ⇜ (5/1 → 6/1) ⇝ 13/2 | n:7 gain:0.5 clip:1 note:C4 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ 9/2 ⇜ (5/1 → 6/1) ⇝ 13/2 | n:9 gain:0.5 clip:1 note:Eb4 s:piano release:0.1 pan:0.5416666666666667 ]", - "[ 9/2 ⇜ (5/1 → 6/1) ⇝ 13/2 | n:13 gain:0.5 clip:1 note:Bb4 s:piano release:0.1 pan:0.5740740740740741 ]", - "[ 5/1 → 6/1 | n:7 gain:1 clip:1 note:C4 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ 5/1 → 6/1 | n:22 gain:0.3333333333333333 clip:1 note:D6 s:piano release:0.1 pan:0.6481481481481481 ]", - "[ (5/1 → 6/1) ⇝ 7/1 | n:14 gain:0.3333333333333333 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", - "[ (5/1 → 6/1) ⇝ 7/1 | n:16 gain:0.3333333333333333 clip:1 note:Eb5 s:piano release:0.1 pan:0.5972222222222222 ]", - "[ (5/1 → 6/1) ⇝ 7/1 | n:20 gain:0.3333333333333333 clip:1 note:Bb5 s:piano release:0.1 pan:0.6296296296296297 ]", - "[ 31/6 → 11/2 | n:25 clip:1 gain:0.5790943073464694 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", - "[ 31/6 → 11/2 | n:22 clip:1 gain:0.6209056926535308 note:D6 s:piano release:0.1 pan:0.6481481481481481 ]", - "[ 31/6 → 35/6 | n:0 gain:0.5 clip:1 note:C3 s:piano release:0.1 pan:0.4722222222222222 ]", - "[ 16/3 → 17/3 | n:22 clip:1 gain:0.5381966011250106 note:D6 s:piano release:0.1 pan:0.6481481481481481 ]", - "[ 16/3 → 17/3 | n:25 clip:1 gain:0.5790943073464694 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", - "[ 16/3 → 6/1 | n:-7 gain:1 clip:1 note:C2 s:piano release:0.1 pan:0.41666666666666663 ]", - "[ 11/2 → 35/6 | n:22 clip:1 gain:0.5381966011250106 note:D6 s:piano release:0.1 pan:0.6481481481481481 ]", - "[ 11/2 → 35/6 | n:25 clip:1 gain:0.5790943073464694 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", - "[ (11/2 → 6/1) ⇝ 37/6 | n:14 gain:0.25 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", - "[ (11/2 → 6/1) ⇝ 13/2 | n:14 gain:0.5 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", - "[ (11/2 → 6/1) ⇝ 13/2 | n:29 gain:0.25 clip:1 note:D7 s:piano release:0.1 pan:0.7037037037037037 ]", - "[ (11/2 → 6/1) ⇝ 15/2 | n:21 gain:0.25 clip:1 note:C6 s:piano release:0.1 pan:0.6388888888888888 ]", - "[ (11/2 → 6/1) ⇝ 15/2 | n:23 gain:0.25 clip:1 note:Eb6 s:piano release:0.1 pan:0.6527777777777778 ]", - "[ (11/2 → 6/1) ⇝ 15/2 | n:27 gain:0.25 clip:1 note:Bb6 s:piano release:0.1 pan:0.6851851851851851 ]", - "[ 17/3 → 6/1 | n:25 clip:1 gain:0.5 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", - "[ 17/3 → 6/1 | n:22 clip:1 gain:0.5381966011250106 note:D6 s:piano release:0.1 pan:0.6481481481481481 ]", - "[ (17/3 → 6/1) ⇝ 19/3 | n:7 gain:0.3333333333333333 clip:1 note:C4 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ (35/6 → 6/1) ⇝ 37/6 | n:25 clip:1 gain:0.5 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", - "[ (35/6 → 6/1) ⇝ 37/6 | n:22 clip:1 gain:0.5381966011250106 note:D6 s:piano release:0.1 pan:0.6481481481481481 ]", - "[ (35/6 → 6/1) ⇝ 13/2 | n:0 gain:0.5 clip:1 note:C3 s:piano release:0.1 pan:0.4722222222222222 ]", - "[ 11/2 ⇜ (6/1 → 37/6) | n:14 gain:0.25 clip:1 note:F5 s:piano release:0.1 pan:0.6064814814814814 ]", - "[ 35/6 ⇜ (6/1 → 37/6) | n:25 clip:1 gain:0.5 note:C7 s:piano release:0.1 pan:0.6944444444444444 ]", - "[ 35/6 ⇜ (6/1 → 37/6) | n:22 clip:1 gain:0.5381966011250106 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", - "[ 17/3 ⇜ (6/1 → 19/3) | n:7 gain:0.3333333333333333 clip:1 note:F4 s:piano release:0.1 pan:0.5509259259259259 ]", - "[ 6/1 → 19/3 | n:22 clip:1 gain:0.46617387872822835 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", - "[ 6/1 → 19/3 | n:25 clip:1 gain:0.5 note:C7 s:piano release:0.1 pan:0.6944444444444444 ]", - "[ 9/2 ⇜ (6/1 → 13/2) | n:7 gain:0.5 clip:1 note:F4 s:piano release:0.1 pan:0.5509259259259259 ]", - "[ 9/2 ⇜ (6/1 → 13/2) | n:9 gain:0.5 clip:1 note:A4 s:piano release:0.1 pan:0.5694444444444444 ]", - "[ 9/2 ⇜ (6/1 → 13/2) | n:13 gain:0.5 clip:1 note:Eb5 s:piano release:0.1 pan:0.5972222222222222 ]", - "[ 11/2 ⇜ (6/1 → 13/2) | n:14 gain:0.5 clip:1 note:F5 s:piano release:0.1 pan:0.6064814814814814 ]", - "[ 11/2 ⇜ (6/1 → 13/2) | n:29 gain:0.25 clip:1 note:G7 s:piano release:0.1 pan:0.7268518518518519 ]", - "[ 35/6 ⇜ (6/1 → 13/2) | n:0 gain:0.5 clip:1 note:F3 s:piano release:0.1 pan:0.49537037037037035 ]", - "[ 6/1 → 20/3 | n:-7 gain:1 clip:1 note:F2 s:piano release:0.1 pan:0.4398148148148148 ]", - "[ 5/1 ⇜ (6/1 → 7/1) | n:14 gain:0.3333333333333333 clip:1 note:F5 s:piano release:0.1 pan:0.6064814814814814 ]", - "[ 5/1 ⇜ (6/1 → 7/1) | n:16 gain:0.3333333333333333 clip:1 note:A5 s:piano release:0.1 pan:0.625 ]", - "[ 5/1 ⇜ (6/1 → 7/1) | n:20 gain:0.3333333333333333 clip:1 note:Eb6 s:piano release:0.1 pan:0.6527777777777778 ]", - "[ 11/2 ⇜ (6/1 → 7/1) ⇝ 15/2 | n:21 gain:0.25 clip:1 note:F6 s:piano release:0.1 pan:0.662037037037037 ]", - "[ 11/2 ⇜ (6/1 → 7/1) ⇝ 15/2 | n:23 gain:0.25 clip:1 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", - "[ 11/2 ⇜ (6/1 → 7/1) ⇝ 15/2 | n:27 gain:0.25 clip:1 note:Eb7 s:piano release:0.1 pan:0.7083333333333333 ]", - "[ 6/1 → 7/1 | n:8 gain:1 clip:1 note:G4 s:piano release:0.1 pan:0.5601851851851851 ]", - "[ 6/1 → 7/1 | n:21 gain:0.3333333333333333 clip:1 note:F6 s:piano release:0.1 pan:0.662037037037037 ]", - "[ (6/1 → 7/1) ⇝ 8/1 | n:0 gain:1 clip:1 note:F3 s:piano release:0.1 pan:0.49537037037037035 ]", - "[ (6/1 → 7/1) ⇝ 8/1 | n:2 gain:1 clip:1 note:A3 s:piano release:0.1 pan:0.5138888888888888 ]", - "[ (6/1 → 7/1) ⇝ 8/1 | n:6 gain:1 clip:1 note:Eb4 s:piano release:0.1 pan:0.5416666666666667 ]", - "[ 37/6 → 13/2 | n:22 clip:1 gain:0.46617387872822835 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", - "[ 37/6 → 13/2 | n:25 clip:1 gain:0.5 note:C7 s:piano release:0.1 pan:0.6944444444444444 ]", - "[ 37/6 → 41/6 | n:14 gain:0.25 clip:1 note:F5 s:piano release:0.1 pan:0.6064814814814814 ]", - "[ 19/3 → 20/3 | n:25 clip:1 gain:0.4381966011250106 note:C7 s:piano release:0.1 pan:0.6944444444444444 ]", - "[ 19/3 → 20/3 | n:22 clip:1 gain:0.46617387872822835 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", - "[ 19/3 → 7/1 | n:7 gain:0.3333333333333333 clip:1 note:F4 s:piano release:0.1 pan:0.5509259259259259 ]", - "[ 13/2 → 41/6 | n:25 clip:1 gain:0.4381966011250106 note:C7 s:piano release:0.1 pan:0.6944444444444444 ]", - "[ 13/2 → 41/6 | n:22 clip:1 gain:0.46617387872822835 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", - "[ (13/2 → 7/1) ⇝ 43/6 | n:0 gain:0.5 clip:1 note:F3 s:piano release:0.1 pan:0.49537037037037035 ]", - "[ (13/2 → 7/1) ⇝ 15/2 | n:15 gain:0.5 clip:1 note:G5 s:piano release:0.1 pan:0.6157407407407407 ]", - "[ (13/2 → 7/1) ⇝ 15/2 | n:28 gain:0.25 clip:1 note:F7 s:piano release:0.1 pan:0.7175925925925926 ]", - "[ (13/2 → 7/1) ⇝ 17/2 | n:7 gain:0.5 clip:1 note:F4 s:piano release:0.1 pan:0.5509259259259259 ]", - "[ (13/2 → 7/1) ⇝ 17/2 | n:9 gain:0.5 clip:1 note:A4 s:piano release:0.1 pan:0.5694444444444444 ]", - "[ (13/2 → 7/1) ⇝ 17/2 | n:13 gain:0.5 clip:1 note:Eb5 s:piano release:0.1 pan:0.5972222222222222 ]", - "[ 20/3 → 7/1 | n:22 clip:1 gain:0.4172909084714798 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", - "[ 20/3 → 7/1 | n:25 clip:1 gain:0.4381966011250106 note:C7 s:piano release:0.1 pan:0.6944444444444444 ]", - "[ (20/3 → 7/1) ⇝ 22/3 | n:-7 gain:1 clip:1 note:F2 s:piano release:0.1 pan:0.4398148148148148 ]", - "[ (41/6 → 7/1) ⇝ 43/6 | n:22 clip:1 gain:0.4172909084714798 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", - "[ (41/6 → 7/1) ⇝ 43/6 | n:25 clip:1 gain:0.4381966011250106 note:C7 s:piano release:0.1 pan:0.6944444444444444 ]", - "[ (41/6 → 7/1) ⇝ 15/2 | n:14 gain:0.25 clip:1 note:F5 s:piano release:0.1 pan:0.6064814814814814 ]", - "[ 13/2 ⇜ (7/1 → 43/6) | n:0 gain:0.5 clip:1 note:F3 s:piano release:0.1 pan:0.49537037037037035 ]", - "[ 41/6 ⇜ (7/1 → 43/6) | n:22 clip:1 gain:0.4172909084714798 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", - "[ 41/6 ⇜ (7/1 → 43/6) | n:25 clip:1 gain:0.4381966011250106 note:C7 s:piano release:0.1 pan:0.6944444444444444 ]", - "[ 20/3 ⇜ (7/1 → 22/3) | n:-7 gain:1 clip:1 note:F2 s:piano release:0.1 pan:0.4398148148148148 ]", - "[ 7/1 → 22/3 | n:25 clip:1 gain:0.40437047985323893 note:C7 s:piano release:0.1 pan:0.6944444444444444 ]", - "[ 7/1 → 22/3 | n:22 clip:1 gain:0.4172909084714798 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", - "[ 11/2 ⇜ (7/1 → 15/2) | n:21 gain:0.25 clip:1 note:F6 s:piano release:0.1 pan:0.662037037037037 ]", - "[ 11/2 ⇜ (7/1 → 15/2) | n:23 gain:0.25 clip:1 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", - "[ 11/2 ⇜ (7/1 → 15/2) | n:27 gain:0.25 clip:1 note:Eb7 s:piano release:0.1 pan:0.7083333333333333 ]", - "[ 13/2 ⇜ (7/1 → 15/2) | n:15 gain:0.5 clip:1 note:G5 s:piano release:0.1 pan:0.6157407407407407 ]", - "[ 13/2 ⇜ (7/1 → 15/2) | n:28 gain:0.25 clip:1 note:F7 s:piano release:0.1 pan:0.7175925925925926 ]", - "[ 41/6 ⇜ (7/1 → 15/2) | n:14 gain:0.25 clip:1 note:F5 s:piano release:0.1 pan:0.6064814814814814 ]", - "[ 7/1 → 23/3 | n:7 gain:0.3333333333333333 clip:1 note:F4 s:piano release:0.1 pan:0.5509259259259259 ]", - "[ 6/1 ⇜ (7/1 → 8/1) | n:0 gain:1 clip:1 note:F3 s:piano release:0.1 pan:0.49537037037037035 ]", - "[ 6/1 ⇜ (7/1 → 8/1) | n:2 gain:1 clip:1 note:A3 s:piano release:0.1 pan:0.5138888888888888 ]", - "[ 6/1 ⇜ (7/1 → 8/1) | n:6 gain:1 clip:1 note:Eb4 s:piano release:0.1 pan:0.5416666666666667 ]", - "[ 13/2 ⇜ (7/1 → 8/1) ⇝ 17/2 | n:7 gain:0.5 clip:1 note:F4 s:piano release:0.1 pan:0.5509259259259259 ]", - "[ 13/2 ⇜ (7/1 → 8/1) ⇝ 17/2 | n:9 gain:0.5 clip:1 note:A4 s:piano release:0.1 pan:0.5694444444444444 ]", - "[ 13/2 ⇜ (7/1 → 8/1) ⇝ 17/2 | n:13 gain:0.5 clip:1 note:Eb5 s:piano release:0.1 pan:0.5972222222222222 ]", - "[ 7/1 → 8/1 | n:7 gain:1 clip:1 note:F4 s:piano release:0.1 pan:0.5509259259259259 ]", - "[ 7/1 → 8/1 | n:22 gain:0.3333333333333333 clip:1 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", - "[ (7/1 → 8/1) ⇝ 9/1 | n:14 gain:0.3333333333333333 clip:1 note:F5 s:piano release:0.1 pan:0.6064814814814814 ]", - "[ (7/1 → 8/1) ⇝ 9/1 | n:16 gain:0.3333333333333333 clip:1 note:A5 s:piano release:0.1 pan:0.625 ]", - "[ (7/1 → 8/1) ⇝ 9/1 | n:20 gain:0.3333333333333333 clip:1 note:Eb6 s:piano release:0.1 pan:0.6527777777777778 ]", - "[ 43/6 → 15/2 | n:25 clip:1 gain:0.40437047985323893 note:C7 s:piano release:0.1 pan:0.6944444444444444 ]", - "[ 43/6 → 15/2 | n:22 clip:1 gain:0.4172909084714798 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", - "[ 43/6 → 47/6 | n:0 gain:0.5 clip:1 note:F3 s:piano release:0.1 pan:0.49537037037037035 ]", - "[ 22/3 → 23/3 | n:22 clip:1 gain:0.4 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", - "[ 22/3 → 23/3 | n:25 clip:1 gain:0.40437047985323893 note:C7 s:piano release:0.1 pan:0.6944444444444444 ]", - "[ 22/3 → 8/1 | n:-7 gain:1 clip:1 note:F2 s:piano release:0.1 pan:0.4398148148148148 ]", - "[ 15/2 → 47/6 | n:22 clip:1 gain:0.4 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", - "[ 15/2 → 47/6 | n:25 clip:1 gain:0.40437047985323893 note:C7 s:piano release:0.1 pan:0.6944444444444444 ]", - "[ (15/2 → 8/1) ⇝ 49/6 | n:14 gain:0.25 clip:1 note:F5 s:piano release:0.1 pan:0.6064814814814814 ]", - "[ (15/2 → 8/1) ⇝ 17/2 | n:14 gain:0.5 clip:1 note:F5 s:piano release:0.1 pan:0.6064814814814814 ]", - "[ (15/2 → 8/1) ⇝ 17/2 | n:29 gain:0.25 clip:1 note:G7 s:piano release:0.1 pan:0.7268518518518519 ]", - "[ (15/2 → 8/1) ⇝ 19/2 | n:21 gain:0.25 clip:1 note:F6 s:piano release:0.1 pan:0.662037037037037 ]", - "[ (15/2 → 8/1) ⇝ 19/2 | n:23 gain:0.25 clip:1 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", - "[ (15/2 → 8/1) ⇝ 19/2 | n:27 gain:0.25 clip:1 note:Eb7 s:piano release:0.1 pan:0.7083333333333333 ]", - "[ 23/3 → 8/1 | n:25 clip:1 gain:0.40437047985323893 note:C7 s:piano release:0.1 pan:0.6944444444444444 ]", - "[ 23/3 → 8/1 | n:22 clip:1 gain:0.4 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", - "[ (23/3 → 8/1) ⇝ 25/3 | n:7 gain:0.3333333333333333 clip:1 note:F4 s:piano release:0.1 pan:0.5509259259259259 ]", - "[ (47/6 → 8/1) ⇝ 49/6 | n:25 clip:1 gain:0.40437047985323893 note:C7 s:piano release:0.1 pan:0.6944444444444444 ]", - "[ (47/6 → 8/1) ⇝ 49/6 | n:22 clip:1 gain:0.4 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", - "[ (47/6 → 8/1) ⇝ 17/2 | n:0 gain:0.5 clip:1 note:F3 s:piano release:0.1 pan:0.49537037037037035 ]", - "[ 15/2 ⇜ (8/1 → 49/6) | n:14 gain:0.25 clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", - "[ 47/6 ⇜ (8/1 → 49/6) | n:25 clip:1 gain:0.40437047985323893 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", - "[ 47/6 ⇜ (8/1 → 49/6) | n:22 clip:1 gain:0.4 note:E6 s:piano release:0.1 pan:0.6574074074074074 ]", - "[ 23/3 ⇜ (8/1 → 25/3) | n:7 gain:0.3333333333333333 clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", - "[ 8/1 → 25/3 | n:22 clip:1 gain:0.4172909084714798 note:E6 s:piano release:0.1 pan:0.6574074074074074 ]", - "[ 8/1 → 25/3 | n:25 clip:1 gain:0.40437047985323893 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", - "[ 13/2 ⇜ (8/1 → 17/2) | n:7 gain:0.5 clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", - "[ 13/2 ⇜ (8/1 → 17/2) | n:9 gain:0.5 clip:1 note:F4 s:piano release:0.1 pan:0.5509259259259259 ]", - "[ 13/2 ⇜ (8/1 → 17/2) | n:13 gain:0.5 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", - "[ 15/2 ⇜ (8/1 → 17/2) | n:14 gain:0.5 clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", - "[ 15/2 ⇜ (8/1 → 17/2) | n:29 gain:0.25 clip:1 note:E7 s:piano release:0.1 pan:0.712962962962963 ]", - "[ 47/6 ⇜ (8/1 → 17/2) | n:0 gain:0.5 clip:1 note:D3 s:piano release:0.1 pan:0.4814814814814815 ]", - "[ 8/1 → 26/3 | n:-7 gain:1 clip:1 note:D2 s:piano release:0.1 pan:0.42592592592592593 ]", - "[ 7/1 ⇜ (8/1 → 9/1) | n:14 gain:0.3333333333333333 clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", - "[ 7/1 ⇜ (8/1 → 9/1) | n:16 gain:0.3333333333333333 clip:1 note:F5 s:piano release:0.1 pan:0.6064814814814814 ]", - "[ 7/1 ⇜ (8/1 → 9/1) | n:20 gain:0.3333333333333333 clip:1 note:C6 s:piano release:0.1 pan:0.6388888888888888 ]", - "[ 15/2 ⇜ (8/1 → 9/1) ⇝ 19/2 | n:21 gain:0.25 clip:1 note:D6 s:piano release:0.1 pan:0.6481481481481481 ]", - "[ 15/2 ⇜ (8/1 → 9/1) ⇝ 19/2 | n:23 gain:0.25 clip:1 note:F6 s:piano release:0.1 pan:0.662037037037037 ]", - "[ 15/2 ⇜ (8/1 → 9/1) ⇝ 19/2 | n:27 gain:0.25 clip:1 note:C7 s:piano release:0.1 pan:0.6944444444444444 ]", - "[ 8/1 → 9/1 | n:8 gain:1 clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 8/1 → 9/1 | n:21 gain:0.3333333333333333 clip:1 note:D6 s:piano release:0.1 pan:0.6481481481481481 ]", - "[ (8/1 → 9/1) ⇝ 10/1 | n:0 gain:1 clip:1 note:D3 s:piano release:0.1 pan:0.4814814814814815 ]", - "[ (8/1 → 9/1) ⇝ 10/1 | n:2 gain:1 clip:1 note:F3 s:piano release:0.1 pan:0.49537037037037035 ]", - "[ (8/1 → 9/1) ⇝ 10/1 | n:6 gain:1 clip:1 note:C4 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ 49/6 → 17/2 | n:22 clip:1 gain:0.4172909084714798 note:E6 s:piano release:0.1 pan:0.6574074074074074 ]", - "[ 49/6 → 17/2 | n:25 clip:1 gain:0.40437047985323893 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", - "[ 49/6 → 53/6 | n:14 gain:0.25 clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", - "[ 25/3 → 26/3 | n:25 clip:1 gain:0.4381966011250105 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", - "[ 25/3 → 26/3 | n:22 clip:1 gain:0.4172909084714798 note:E6 s:piano release:0.1 pan:0.6574074074074074 ]", - "[ 25/3 → 9/1 | n:7 gain:0.3333333333333333 clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", - "[ 17/2 → 53/6 | n:25 clip:1 gain:0.4381966011250105 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", - "[ 17/2 → 53/6 | n:22 clip:1 gain:0.4172909084714798 note:E6 s:piano release:0.1 pan:0.6574074074074074 ]", - "[ (17/2 → 9/1) ⇝ 55/6 | n:0 gain:0.5 clip:1 note:D3 s:piano release:0.1 pan:0.4814814814814815 ]", - "[ (17/2 → 9/1) ⇝ 19/2 | n:15 gain:0.5 clip:1 note:E5 s:piano release:0.1 pan:0.6018518518518519 ]", - "[ (17/2 → 9/1) ⇝ 19/2 | n:28 gain:0.25 clip:1 note:D7 s:piano release:0.1 pan:0.7037037037037037 ]", - "[ (17/2 → 9/1) ⇝ 21/2 | n:7 gain:0.5 clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", - "[ (17/2 → 9/1) ⇝ 21/2 | n:9 gain:0.5 clip:1 note:F4 s:piano release:0.1 pan:0.5509259259259259 ]", - "[ (17/2 → 9/1) ⇝ 21/2 | n:13 gain:0.5 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", - "[ 26/3 → 9/1 | n:22 clip:1 gain:0.46617387872822824 note:E6 s:piano release:0.1 pan:0.6574074074074074 ]", - "[ 26/3 → 9/1 | n:25 clip:1 gain:0.4381966011250105 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", - "[ (26/3 → 9/1) ⇝ 28/3 | n:-7 gain:1 clip:1 note:D2 s:piano release:0.1 pan:0.42592592592592593 ]", - "[ (53/6 → 9/1) ⇝ 55/6 | n:22 clip:1 gain:0.46617387872822824 note:E6 s:piano release:0.1 pan:0.6574074074074074 ]", - "[ (53/6 → 9/1) ⇝ 55/6 | n:25 clip:1 gain:0.4381966011250105 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", - "[ (53/6 → 9/1) ⇝ 19/2 | n:14 gain:0.25 clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", - "[ 17/2 ⇜ (9/1 → 55/6) | n:0 gain:0.5 clip:1 note:D3 s:piano release:0.1 pan:0.4814814814814815 ]", - "[ 53/6 ⇜ (9/1 → 55/6) | n:22 clip:1 gain:0.46617387872822824 note:E6 s:piano release:0.1 pan:0.6574074074074074 ]", - "[ 53/6 ⇜ (9/1 → 55/6) | n:25 clip:1 gain:0.4381966011250105 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", - "[ 26/3 ⇜ (9/1 → 28/3) | n:-7 gain:1 clip:1 note:D2 s:piano release:0.1 pan:0.42592592592592593 ]", - "[ 9/1 → 28/3 | n:25 clip:1 gain:0.49999999999999994 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", - "[ 9/1 → 28/3 | n:22 clip:1 gain:0.46617387872822824 note:E6 s:piano release:0.1 pan:0.6574074074074074 ]", - "[ 15/2 ⇜ (9/1 → 19/2) | n:21 gain:0.25 clip:1 note:D6 s:piano release:0.1 pan:0.6481481481481481 ]", - "[ 15/2 ⇜ (9/1 → 19/2) | n:23 gain:0.25 clip:1 note:F6 s:piano release:0.1 pan:0.662037037037037 ]", - "[ 15/2 ⇜ (9/1 → 19/2) | n:27 gain:0.25 clip:1 note:C7 s:piano release:0.1 pan:0.6944444444444444 ]", - "[ 17/2 ⇜ (9/1 → 19/2) | n:15 gain:0.5 clip:1 note:E5 s:piano release:0.1 pan:0.6018518518518519 ]", - "[ 17/2 ⇜ (9/1 → 19/2) | n:28 gain:0.25 clip:1 note:D7 s:piano release:0.1 pan:0.7037037037037037 ]", - "[ 53/6 ⇜ (9/1 → 19/2) | n:14 gain:0.25 clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", - "[ 9/1 → 29/3 | n:7 gain:0.3333333333333333 clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", - "[ 8/1 ⇜ (9/1 → 10/1) | n:0 gain:1 clip:1 note:D3 s:piano release:0.1 pan:0.4814814814814815 ]", - "[ 8/1 ⇜ (9/1 → 10/1) | n:2 gain:1 clip:1 note:F3 s:piano release:0.1 pan:0.49537037037037035 ]", - "[ 8/1 ⇜ (9/1 → 10/1) | n:6 gain:1 clip:1 note:C4 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ 17/2 ⇜ (9/1 → 10/1) ⇝ 21/2 | n:7 gain:0.5 clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", - "[ 17/2 ⇜ (9/1 → 10/1) ⇝ 21/2 | n:9 gain:0.5 clip:1 note:F4 s:piano release:0.1 pan:0.5509259259259259 ]", - "[ 17/2 ⇜ (9/1 → 10/1) ⇝ 21/2 | n:13 gain:0.5 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", - "[ 9/1 → 10/1 | n:7 gain:1 clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", - "[ 9/1 → 10/1 | n:22 gain:0.3333333333333333 clip:1 note:E6 s:piano release:0.1 pan:0.6574074074074074 ]", - "[ (9/1 → 10/1) ⇝ 11/1 | n:14 gain:0.3333333333333333 clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", - "[ (9/1 → 10/1) ⇝ 11/1 | n:16 gain:0.3333333333333333 clip:1 note:F5 s:piano release:0.1 pan:0.6064814814814814 ]", - "[ (9/1 → 10/1) ⇝ 11/1 | n:20 gain:0.3333333333333333 clip:1 note:C6 s:piano release:0.1 pan:0.6388888888888888 ]", - "[ 55/6 → 19/2 | n:25 clip:1 gain:0.49999999999999994 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", - "[ 55/6 → 19/2 | n:22 clip:1 gain:0.46617387872822824 note:E6 s:piano release:0.1 pan:0.6574074074074074 ]", - "[ 55/6 → 59/6 | n:0 gain:0.5 clip:1 note:D3 s:piano release:0.1 pan:0.4814814814814815 ]", - "[ 28/3 → 29/3 | n:22 clip:1 gain:0.5381966011250106 note:E6 s:piano release:0.1 pan:0.6574074074074074 ]", - "[ 28/3 → 29/3 | n:25 clip:1 gain:0.49999999999999994 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", - "[ 28/3 → 10/1 | n:-7 gain:1 clip:1 note:D2 s:piano release:0.1 pan:0.42592592592592593 ]", - "[ 19/2 → 59/6 | n:22 clip:1 gain:0.5381966011250106 note:E6 s:piano release:0.1 pan:0.6574074074074074 ]", - "[ 19/2 → 59/6 | n:25 clip:1 gain:0.49999999999999994 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", - "[ (19/2 → 10/1) ⇝ 61/6 | n:14 gain:0.25 clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", - "[ (19/2 → 10/1) ⇝ 21/2 | n:14 gain:0.5 clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", - "[ (19/2 → 10/1) ⇝ 21/2 | n:29 gain:0.25 clip:1 note:E7 s:piano release:0.1 pan:0.712962962962963 ]", - "[ (19/2 → 10/1) ⇝ 23/2 | n:21 gain:0.25 clip:1 note:D6 s:piano release:0.1 pan:0.6481481481481481 ]", - "[ (19/2 → 10/1) ⇝ 23/2 | n:23 gain:0.25 clip:1 note:F6 s:piano release:0.1 pan:0.662037037037037 ]", - "[ (19/2 → 10/1) ⇝ 23/2 | n:27 gain:0.25 clip:1 note:C7 s:piano release:0.1 pan:0.6944444444444444 ]", - "[ 29/3 → 10/1 | n:25 clip:1 gain:0.5790943073464692 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", - "[ 29/3 → 10/1 | n:22 clip:1 gain:0.5381966011250106 note:E6 s:piano release:0.1 pan:0.6574074074074074 ]", - "[ (29/3 → 10/1) ⇝ 31/3 | n:7 gain:0.3333333333333333 clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", - "[ (59/6 → 10/1) ⇝ 61/6 | n:25 clip:1 gain:0.5790943073464692 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", - "[ (59/6 → 10/1) ⇝ 61/6 | n:22 clip:1 gain:0.5381966011250106 note:E6 s:piano release:0.1 pan:0.6574074074074074 ]", - "[ (59/6 → 10/1) ⇝ 21/2 | n:0 gain:0.5 clip:1 note:D3 s:piano release:0.1 pan:0.4814814814814815 ]", - "[ 19/2 ⇜ (10/1 → 61/6) | n:14 gain:0.25 clip:1 note:G5 s:piano release:0.1 pan:0.6157407407407407 ]", - "[ 59/6 ⇜ (10/1 → 61/6) | n:25 clip:1 gain:0.5790943073464692 note:D7 s:piano release:0.1 pan:0.7037037037037037 ]", - "[ 59/6 ⇜ (10/1 → 61/6) | n:22 clip:1 gain:0.5381966011250106 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", - "[ 29/3 ⇜ (10/1 → 31/3) | n:7 gain:0.3333333333333333 clip:1 note:G4 s:piano release:0.1 pan:0.5601851851851851 ]", - "[ 10/1 → 31/3 | n:22 clip:1 gain:0.6209056926535306 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", - "[ 10/1 → 31/3 | n:25 clip:1 gain:0.5790943073464692 note:D7 s:piano release:0.1 pan:0.7037037037037037 ]", - "[ 17/2 ⇜ (10/1 → 21/2) | n:7 gain:0.5 clip:1 note:G4 s:piano release:0.1 pan:0.5601851851851851 ]", - "[ 17/2 ⇜ (10/1 → 21/2) | n:9 gain:0.5 clip:1 note:B4 s:piano release:0.1 pan:0.5787037037037037 ]", - "[ 17/2 ⇜ (10/1 → 21/2) | n:13 gain:0.5 clip:1 note:F5 s:piano release:0.1 pan:0.6064814814814814 ]", - "[ 19/2 ⇜ (10/1 → 21/2) | n:14 gain:0.5 clip:1 note:G5 s:piano release:0.1 pan:0.6157407407407407 ]", - "[ 19/2 ⇜ (10/1 → 21/2) | n:29 gain:0.25 clip:1 note:A7 s:piano release:0.1 pan:0.7361111111111112 ]", - "[ 59/6 ⇜ (10/1 → 21/2) | n:0 gain:0.5 clip:1 note:G3 s:piano release:0.1 pan:0.5046296296296297 ]", - "[ 10/1 → 32/3 | n:-7 gain:1 clip:1 note:G2 s:piano release:0.1 pan:0.44907407407407407 ]", - "[ 9/1 ⇜ (10/1 → 11/1) | n:14 gain:0.3333333333333333 clip:1 note:G5 s:piano release:0.1 pan:0.6157407407407407 ]", - "[ 9/1 ⇜ (10/1 → 11/1) | n:16 gain:0.3333333333333333 clip:1 note:B5 s:piano release:0.1 pan:0.6342592592592593 ]", - "[ 9/1 ⇜ (10/1 → 11/1) | n:20 gain:0.3333333333333333 clip:1 note:F6 s:piano release:0.1 pan:0.662037037037037 ]", - "[ 19/2 ⇜ (10/1 → 11/1) ⇝ 23/2 | n:21 gain:0.25 clip:1 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", - "[ 19/2 ⇜ (10/1 → 11/1) ⇝ 23/2 | n:23 gain:0.25 clip:1 note:B6 s:piano release:0.1 pan:0.6898148148148149 ]", - "[ 19/2 ⇜ (10/1 → 11/1) ⇝ 23/2 | n:27 gain:0.25 clip:1 note:F7 s:piano release:0.1 pan:0.7175925925925926 ]", - "[ 10/1 → 11/1 | n:8 gain:1 clip:1 note:A4 s:piano release:0.1 pan:0.5694444444444444 ]", - "[ 10/1 → 11/1 | n:21 gain:0.3333333333333333 clip:1 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", - "[ (10/1 → 11/1) ⇝ 12/1 | n:0 gain:1 clip:1 note:G3 s:piano release:0.1 pan:0.5046296296296297 ]", - "[ (10/1 → 11/1) ⇝ 12/1 | n:2 gain:1 clip:1 note:B3 s:piano release:0.1 pan:0.5231481481481481 ]", - "[ (10/1 → 11/1) ⇝ 12/1 | n:6 gain:1 clip:1 note:F4 s:piano release:0.1 pan:0.5509259259259259 ]", - "[ 61/6 → 21/2 | n:22 clip:1 gain:0.6209056926535306 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", - "[ 61/6 → 21/2 | n:25 clip:1 gain:0.5790943073464692 note:D7 s:piano release:0.1 pan:0.7037037037037037 ]", - "[ 61/6 → 65/6 | n:14 gain:0.25 clip:1 note:G5 s:piano release:0.1 pan:0.6157407407407407 ]", - "[ 31/3 → 32/3 | n:25 clip:1 gain:0.6618033988749894 note:D7 s:piano release:0.1 pan:0.7037037037037037 ]", - "[ 31/3 → 32/3 | n:22 clip:1 gain:0.6209056926535306 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", - "[ 31/3 → 11/1 | n:7 gain:0.3333333333333333 clip:1 note:G4 s:piano release:0.1 pan:0.5601851851851851 ]", - "[ 21/2 → 65/6 | n:25 clip:1 gain:0.6618033988749894 note:D7 s:piano release:0.1 pan:0.7037037037037037 ]", - "[ 21/2 → 65/6 | n:22 clip:1 gain:0.6209056926535306 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", - "[ (21/2 → 11/1) ⇝ 67/6 | n:0 gain:0.5 clip:1 note:G3 s:piano release:0.1 pan:0.5046296296296297 ]", - "[ (21/2 → 11/1) ⇝ 23/2 | n:15 gain:0.5 clip:1 note:A5 s:piano release:0.1 pan:0.625 ]", - "[ (21/2 → 11/1) ⇝ 23/2 | n:28 gain:0.25 clip:1 note:G7 s:piano release:0.1 pan:0.7268518518518519 ]", - "[ (21/2 → 11/1) ⇝ 25/2 | n:7 gain:0.5 clip:1 note:G4 s:piano release:0.1 pan:0.5601851851851851 ]", - "[ (21/2 → 11/1) ⇝ 25/2 | n:9 gain:0.5 clip:1 note:B4 s:piano release:0.1 pan:0.5787037037037037 ]", - "[ (21/2 → 11/1) ⇝ 25/2 | n:13 gain:0.5 clip:1 note:F5 s:piano release:0.1 pan:0.6064814814814814 ]", - "[ 32/3 → 11/1 | n:22 clip:1 gain:0.7 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", - "[ 32/3 → 11/1 | n:25 clip:1 gain:0.6618033988749894 note:D7 s:piano release:0.1 pan:0.7037037037037037 ]", - "[ (32/3 → 11/1) ⇝ 34/3 | n:-7 gain:1 clip:1 note:G2 s:piano release:0.1 pan:0.44907407407407407 ]", - "[ (65/6 → 11/1) ⇝ 67/6 | n:22 clip:1 gain:0.7 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", - "[ (65/6 → 11/1) ⇝ 67/6 | n:25 clip:1 gain:0.6618033988749894 note:D7 s:piano release:0.1 pan:0.7037037037037037 ]", - "[ (65/6 → 11/1) ⇝ 23/2 | n:14 gain:0.25 clip:1 note:G5 s:piano release:0.1 pan:0.6157407407407407 ]", - "[ 21/2 ⇜ (11/1 → 67/6) | n:0 gain:0.5 clip:1 note:G3 s:piano release:0.1 pan:0.5046296296296297 ]", - "[ 65/6 ⇜ (11/1 → 67/6) | n:22 clip:1 gain:0.7 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", - "[ 65/6 ⇜ (11/1 → 67/6) | n:25 clip:1 gain:0.6618033988749894 note:D7 s:piano release:0.1 pan:0.7037037037037037 ]", - "[ 32/3 ⇜ (11/1 → 34/3) | n:-7 gain:1 clip:1 note:G2 s:piano release:0.1 pan:0.44907407407407407 ]", - "[ 11/1 → 34/3 | n:25 clip:1 gain:0.7338261212717717 note:D7 s:piano release:0.1 pan:0.7037037037037037 ]", - "[ 11/1 → 34/3 | n:22 clip:1 gain:0.7 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", - "[ 19/2 ⇜ (11/1 → 23/2) | n:21 gain:0.25 clip:1 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", - "[ 19/2 ⇜ (11/1 → 23/2) | n:23 gain:0.25 clip:1 note:B6 s:piano release:0.1 pan:0.6898148148148149 ]", - "[ 19/2 ⇜ (11/1 → 23/2) | n:27 gain:0.25 clip:1 note:F7 s:piano release:0.1 pan:0.7175925925925926 ]", - "[ 21/2 ⇜ (11/1 → 23/2) | n:15 gain:0.5 clip:1 note:A5 s:piano release:0.1 pan:0.625 ]", - "[ 21/2 ⇜ (11/1 → 23/2) | n:28 gain:0.25 clip:1 note:G7 s:piano release:0.1 pan:0.7268518518518519 ]", - "[ 65/6 ⇜ (11/1 → 23/2) | n:14 gain:0.25 clip:1 note:G5 s:piano release:0.1 pan:0.6157407407407407 ]", - "[ 11/1 → 35/3 | n:7 gain:0.3333333333333333 clip:1 note:G4 s:piano release:0.1 pan:0.5601851851851851 ]", - "[ 10/1 ⇜ (11/1 → 12/1) | n:0 gain:1 clip:1 note:G3 s:piano release:0.1 pan:0.5046296296296297 ]", - "[ 10/1 ⇜ (11/1 → 12/1) | n:2 gain:1 clip:1 note:B3 s:piano release:0.1 pan:0.5231481481481481 ]", - "[ 10/1 ⇜ (11/1 → 12/1) | n:6 gain:1 clip:1 note:F4 s:piano release:0.1 pan:0.5509259259259259 ]", - "[ 21/2 ⇜ (11/1 → 12/1) ⇝ 25/2 | n:7 gain:0.5 clip:1 note:G4 s:piano release:0.1 pan:0.5601851851851851 ]", - "[ 21/2 ⇜ (11/1 → 12/1) ⇝ 25/2 | n:9 gain:0.5 clip:1 note:B4 s:piano release:0.1 pan:0.5787037037037037 ]", - "[ 21/2 ⇜ (11/1 → 12/1) ⇝ 25/2 | n:13 gain:0.5 clip:1 note:F5 s:piano release:0.1 pan:0.6064814814814814 ]", - "[ 11/1 → 12/1 | n:7 gain:1 clip:1 note:G4 s:piano release:0.1 pan:0.5601851851851851 ]", - "[ 11/1 → 12/1 | n:22 gain:0.3333333333333333 clip:1 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", - "[ (11/1 → 12/1) ⇝ 13/1 | n:14 gain:0.3333333333333333 clip:1 note:G5 s:piano release:0.1 pan:0.6157407407407407 ]", - "[ (11/1 → 12/1) ⇝ 13/1 | n:16 gain:0.3333333333333333 clip:1 note:B5 s:piano release:0.1 pan:0.6342592592592593 ]", - "[ (11/1 → 12/1) ⇝ 13/1 | n:20 gain:0.3333333333333333 clip:1 note:F6 s:piano release:0.1 pan:0.662037037037037 ]", - "[ 67/6 → 23/2 | n:25 clip:1 gain:0.7338261212717717 note:D7 s:piano release:0.1 pan:0.7037037037037037 ]", - "[ 67/6 → 23/2 | n:22 clip:1 gain:0.7 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", - "[ 67/6 → 71/6 | n:0 gain:0.5 clip:1 note:G3 s:piano release:0.1 pan:0.5046296296296297 ]", - "[ 34/3 → 35/3 | n:22 clip:1 gain:0.7618033988749894 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", - "[ 34/3 → 35/3 | n:25 clip:1 gain:0.7338261212717717 note:D7 s:piano release:0.1 pan:0.7037037037037037 ]", - "[ 34/3 → 12/1 | n:-7 gain:1 clip:1 note:G2 s:piano release:0.1 pan:0.44907407407407407 ]", - "[ 23/2 → 71/6 | n:22 clip:1 gain:0.7618033988749894 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", - "[ 23/2 → 71/6 | n:25 clip:1 gain:0.7338261212717717 note:D7 s:piano release:0.1 pan:0.7037037037037037 ]", - "[ (23/2 → 12/1) ⇝ 73/6 | n:14 gain:0.25 clip:1 note:G5 s:piano release:0.1 pan:0.6157407407407407 ]", - "[ (23/2 → 12/1) ⇝ 25/2 | n:14 gain:0.5 clip:1 note:G5 s:piano release:0.1 pan:0.6157407407407407 ]", - "[ (23/2 → 12/1) ⇝ 25/2 | n:29 gain:0.25 clip:1 note:A7 s:piano release:0.1 pan:0.7361111111111112 ]", - "[ (23/2 → 12/1) ⇝ 27/2 | n:21 gain:0.25 clip:1 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", - "[ (23/2 → 12/1) ⇝ 27/2 | n:23 gain:0.25 clip:1 note:B6 s:piano release:0.1 pan:0.6898148148148149 ]", - "[ (23/2 → 12/1) ⇝ 27/2 | n:27 gain:0.25 clip:1 note:F7 s:piano release:0.1 pan:0.7175925925925926 ]", - "[ 35/3 → 12/1 | n:25 clip:1 gain:0.7827090915285202 note:D7 s:piano release:0.1 pan:0.7037037037037037 ]", - "[ 35/3 → 12/1 | n:22 clip:1 gain:0.7618033988749894 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", - "[ (35/3 → 12/1) ⇝ 37/3 | n:7 gain:0.3333333333333333 clip:1 note:G4 s:piano release:0.1 pan:0.5601851851851851 ]", - "[ (71/6 → 12/1) ⇝ 73/6 | n:25 clip:1 gain:0.7827090915285202 note:D7 s:piano release:0.1 pan:0.7037037037037037 ]", - "[ (71/6 → 12/1) ⇝ 73/6 | n:22 clip:1 gain:0.7618033988749894 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", - "[ (71/6 → 12/1) ⇝ 25/2 | n:0 gain:0.5 clip:1 note:G3 s:piano release:0.1 pan:0.5046296296296297 ]", - "[ 23/2 ⇜ (12/1 → 73/6) | n:14 gain:0.25 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", - "[ 71/6 ⇜ (12/1 → 73/6) | n:25 clip:1 gain:0.7827090915285202 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", - "[ 71/6 ⇜ (12/1 → 73/6) | n:22 clip:1 gain:0.7618033988749894 note:D6 s:piano release:0.1 pan:0.6481481481481481 ]", - "[ 35/3 ⇜ (12/1 → 37/3) | n:7 gain:0.3333333333333333 clip:1 note:C4 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ 12/1 → 37/3 | n:22 clip:1 gain:0.7956295201467611 note:D6 s:piano release:0.1 pan:0.6481481481481481 ]", - "[ 12/1 → 37/3 | n:25 clip:1 gain:0.7827090915285202 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", - "[ 21/2 ⇜ (12/1 → 25/2) | n:7 gain:0.5 clip:1 note:C4 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ 21/2 ⇜ (12/1 → 25/2) | n:9 gain:0.5 clip:1 note:Eb4 s:piano release:0.1 pan:0.5416666666666667 ]", - "[ 21/2 ⇜ (12/1 → 25/2) | n:13 gain:0.5 clip:1 note:Bb4 s:piano release:0.1 pan:0.5740740740740741 ]", - "[ 23/2 ⇜ (12/1 → 25/2) | n:14 gain:0.5 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", - "[ 23/2 ⇜ (12/1 → 25/2) | n:29 gain:0.25 clip:1 note:D7 s:piano release:0.1 pan:0.7037037037037037 ]", - "[ 71/6 ⇜ (12/1 → 25/2) | n:0 gain:0.5 clip:1 note:C3 s:piano release:0.1 pan:0.4722222222222222 ]", - "[ 12/1 → 38/3 | n:-7 gain:1 clip:1 note:C2 s:piano release:0.1 pan:0.41666666666666663 ]", - "[ 11/1 ⇜ (12/1 → 13/1) | n:14 gain:0.3333333333333333 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", - "[ 11/1 ⇜ (12/1 → 13/1) | n:16 gain:0.3333333333333333 clip:1 note:Eb5 s:piano release:0.1 pan:0.5972222222222222 ]", - "[ 11/1 ⇜ (12/1 → 13/1) | n:20 gain:0.3333333333333333 clip:1 note:Bb5 s:piano release:0.1 pan:0.6296296296296297 ]", - "[ 23/2 ⇜ (12/1 → 13/1) ⇝ 27/2 | n:21 gain:0.25 clip:1 note:C6 s:piano release:0.1 pan:0.6388888888888888 ]", - "[ 23/2 ⇜ (12/1 → 13/1) ⇝ 27/2 | n:23 gain:0.25 clip:1 note:Eb6 s:piano release:0.1 pan:0.6527777777777778 ]", - "[ 23/2 ⇜ (12/1 → 13/1) ⇝ 27/2 | n:27 gain:0.25 clip:1 note:Bb6 s:piano release:0.1 pan:0.6851851851851851 ]", - "[ 12/1 → 13/1 | n:8 gain:1 clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", - "[ 12/1 → 13/1 | n:21 gain:0.3333333333333333 clip:1 note:C6 s:piano release:0.1 pan:0.6388888888888888 ]", - "[ (12/1 → 13/1) ⇝ 14/1 | n:0 gain:1 clip:1 note:C3 s:piano release:0.1 pan:0.4722222222222222 ]", - "[ (12/1 → 13/1) ⇝ 14/1 | n:2 gain:1 clip:1 note:Eb3 s:piano release:0.1 pan:0.4861111111111111 ]", - "[ (12/1 → 13/1) ⇝ 14/1 | n:6 gain:1 clip:1 note:Bb3 s:piano release:0.1 pan:0.5185185185185186 ]", - "[ 73/6 → 25/2 | n:22 clip:1 gain:0.7956295201467611 note:D6 s:piano release:0.1 pan:0.6481481481481481 ]", - "[ 73/6 → 25/2 | n:25 clip:1 gain:0.7827090915285202 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", - "[ 73/6 → 77/6 | n:14 gain:0.25 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", - "[ 37/3 → 38/3 | n:25 clip:1 gain:0.8 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", - "[ 37/3 → 38/3 | n:22 clip:1 gain:0.7956295201467611 note:D6 s:piano release:0.1 pan:0.6481481481481481 ]", - "[ 37/3 → 13/1 | n:7 gain:0.3333333333333333 clip:1 note:C4 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ 25/2 → 77/6 | n:25 clip:1 gain:0.8 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", - "[ 25/2 → 77/6 | n:22 clip:1 gain:0.7956295201467611 note:D6 s:piano release:0.1 pan:0.6481481481481481 ]", - "[ (25/2 → 13/1) ⇝ 79/6 | n:0 gain:0.5 clip:1 note:C3 s:piano release:0.1 pan:0.4722222222222222 ]", - "[ (25/2 → 13/1) ⇝ 27/2 | n:15 gain:0.5 clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", - "[ (25/2 → 13/1) ⇝ 27/2 | n:28 gain:0.25 clip:1 note:C7 s:piano release:0.1 pan:0.6944444444444444 ]", - "[ (25/2 → 13/1) ⇝ 29/2 | n:7 gain:0.5 clip:1 note:C4 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ (25/2 → 13/1) ⇝ 29/2 | n:9 gain:0.5 clip:1 note:Eb4 s:piano release:0.1 pan:0.5416666666666667 ]", - "[ (25/2 → 13/1) ⇝ 29/2 | n:13 gain:0.5 clip:1 note:Bb4 s:piano release:0.1 pan:0.5740740740740741 ]", - "[ 38/3 → 13/1 | n:22 clip:1 gain:0.7956295201467611 note:D6 s:piano release:0.1 pan:0.6481481481481481 ]", - "[ 38/3 → 13/1 | n:25 clip:1 gain:0.8 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", - "[ (38/3 → 13/1) ⇝ 40/3 | n:-7 gain:1 clip:1 note:C2 s:piano release:0.1 pan:0.41666666666666663 ]", - "[ (77/6 → 13/1) ⇝ 79/6 | n:22 clip:1 gain:0.7956295201467611 note:D6 s:piano release:0.1 pan:0.6481481481481481 ]", - "[ (77/6 → 13/1) ⇝ 79/6 | n:25 clip:1 gain:0.8 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", - "[ (77/6 → 13/1) ⇝ 27/2 | n:14 gain:0.25 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", - "[ 25/2 ⇜ (13/1 → 79/6) | n:0 gain:0.5 clip:1 note:C3 s:piano release:0.1 pan:0.4722222222222222 ]", - "[ 77/6 ⇜ (13/1 → 79/6) | n:22 clip:1 gain:0.7956295201467611 note:D6 s:piano release:0.1 pan:0.6481481481481481 ]", - "[ 77/6 ⇜ (13/1 → 79/6) | n:25 clip:1 gain:0.8 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", - "[ 38/3 ⇜ (13/1 → 40/3) | n:-7 gain:1 clip:1 note:C2 s:piano release:0.1 pan:0.41666666666666663 ]", - "[ 13/1 → 40/3 | n:25 clip:1 gain:0.7827090915285202 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", - "[ 13/1 → 40/3 | n:22 clip:1 gain:0.7956295201467611 note:D6 s:piano release:0.1 pan:0.6481481481481481 ]", - "[ 23/2 ⇜ (13/1 → 27/2) | n:21 gain:0.25 clip:1 note:C6 s:piano release:0.1 pan:0.6388888888888888 ]", - "[ 23/2 ⇜ (13/1 → 27/2) | n:23 gain:0.25 clip:1 note:Eb6 s:piano release:0.1 pan:0.6527777777777778 ]", - "[ 23/2 ⇜ (13/1 → 27/2) | n:27 gain:0.25 clip:1 note:Bb6 s:piano release:0.1 pan:0.6851851851851851 ]", - "[ 25/2 ⇜ (13/1 → 27/2) | n:15 gain:0.5 clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", - "[ 25/2 ⇜ (13/1 → 27/2) | n:28 gain:0.25 clip:1 note:C7 s:piano release:0.1 pan:0.6944444444444444 ]", - "[ 77/6 ⇜ (13/1 → 27/2) | n:14 gain:0.25 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", - "[ 13/1 → 41/3 | n:7 gain:0.3333333333333333 clip:1 note:C4 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ 12/1 ⇜ (13/1 → 14/1) | n:0 gain:1 clip:1 note:C3 s:piano release:0.1 pan:0.4722222222222222 ]", - "[ 12/1 ⇜ (13/1 → 14/1) | n:2 gain:1 clip:1 note:Eb3 s:piano release:0.1 pan:0.4861111111111111 ]", - "[ 12/1 ⇜ (13/1 → 14/1) | n:6 gain:1 clip:1 note:Bb3 s:piano release:0.1 pan:0.5185185185185186 ]", - "[ 25/2 ⇜ (13/1 → 14/1) ⇝ 29/2 | n:7 gain:0.5 clip:1 note:C4 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ 25/2 ⇜ (13/1 → 14/1) ⇝ 29/2 | n:9 gain:0.5 clip:1 note:Eb4 s:piano release:0.1 pan:0.5416666666666667 ]", - "[ 25/2 ⇜ (13/1 → 14/1) ⇝ 29/2 | n:13 gain:0.5 clip:1 note:Bb4 s:piano release:0.1 pan:0.5740740740740741 ]", - "[ 13/1 → 14/1 | n:7 gain:1 clip:1 note:C4 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ 13/1 → 14/1 | n:22 gain:0.3333333333333333 clip:1 note:D6 s:piano release:0.1 pan:0.6481481481481481 ]", - "[ (13/1 → 14/1) ⇝ 15/1 | n:14 gain:0.3333333333333333 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", - "[ (13/1 → 14/1) ⇝ 15/1 | n:16 gain:0.3333333333333333 clip:1 note:Eb5 s:piano release:0.1 pan:0.5972222222222222 ]", - "[ (13/1 → 14/1) ⇝ 15/1 | n:20 gain:0.3333333333333333 clip:1 note:Bb5 s:piano release:0.1 pan:0.6296296296296297 ]", - "[ 79/6 → 27/2 | n:25 clip:1 gain:0.7827090915285202 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", - "[ 79/6 → 27/2 | n:22 clip:1 gain:0.7956295201467611 note:D6 s:piano release:0.1 pan:0.6481481481481481 ]", - "[ 79/6 → 83/6 | n:0 gain:0.5 clip:1 note:C3 s:piano release:0.1 pan:0.4722222222222222 ]", - "[ 40/3 → 41/3 | n:22 clip:1 gain:0.7618033988749895 note:D6 s:piano release:0.1 pan:0.6481481481481481 ]", - "[ 40/3 → 41/3 | n:25 clip:1 gain:0.7827090915285202 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", - "[ 40/3 → 14/1 | n:-7 gain:1 clip:1 note:C2 s:piano release:0.1 pan:0.41666666666666663 ]", - "[ 27/2 → 83/6 | n:22 clip:1 gain:0.7618033988749895 note:D6 s:piano release:0.1 pan:0.6481481481481481 ]", - "[ 27/2 → 83/6 | n:25 clip:1 gain:0.7827090915285202 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", - "[ (27/2 → 14/1) ⇝ 85/6 | n:14 gain:0.25 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", - "[ (27/2 → 14/1) ⇝ 29/2 | n:14 gain:0.5 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", - "[ (27/2 → 14/1) ⇝ 29/2 | n:29 gain:0.25 clip:1 note:D7 s:piano release:0.1 pan:0.7037037037037037 ]", - "[ (27/2 → 14/1) ⇝ 31/2 | n:21 gain:0.25 clip:1 note:C6 s:piano release:0.1 pan:0.6388888888888888 ]", - "[ (27/2 → 14/1) ⇝ 31/2 | n:23 gain:0.25 clip:1 note:Eb6 s:piano release:0.1 pan:0.6527777777777778 ]", - "[ (27/2 → 14/1) ⇝ 31/2 | n:27 gain:0.25 clip:1 note:Bb6 s:piano release:0.1 pan:0.6851851851851851 ]", - "[ 41/3 → 14/1 | n:25 clip:1 gain:0.7338261212717718 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", - "[ 41/3 → 14/1 | n:22 clip:1 gain:0.7618033988749895 note:D6 s:piano release:0.1 pan:0.6481481481481481 ]", - "[ (41/3 → 14/1) ⇝ 43/3 | n:7 gain:0.3333333333333333 clip:1 note:C4 s:piano release:0.1 pan:0.5277777777777778 ]", - "[ (83/6 → 14/1) ⇝ 85/6 | n:25 clip:1 gain:0.7338261212717718 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", - "[ (83/6 → 14/1) ⇝ 85/6 | n:22 clip:1 gain:0.7618033988749895 note:D6 s:piano release:0.1 pan:0.6481481481481481 ]", - "[ (83/6 → 14/1) ⇝ 29/2 | n:0 gain:0.5 clip:1 note:C3 s:piano release:0.1 pan:0.4722222222222222 ]", - "[ 27/2 ⇜ (14/1 → 85/6) | n:14 gain:0.25 clip:1 note:F5 s:piano release:0.1 pan:0.6064814814814814 ]", - "[ 83/6 ⇜ (14/1 → 85/6) | n:25 clip:1 gain:0.7338261212717718 note:C7 s:piano release:0.1 pan:0.6944444444444444 ]", - "[ 83/6 ⇜ (14/1 → 85/6) | n:22 clip:1 gain:0.7618033988749895 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", - "[ 41/3 ⇜ (14/1 → 43/3) | n:7 gain:0.3333333333333333 clip:1 note:F4 s:piano release:0.1 pan:0.5509259259259259 ]", - "[ 14/1 → 43/3 | n:22 clip:1 gain:0.7 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", - "[ 14/1 → 43/3 | n:25 clip:1 gain:0.7338261212717718 note:C7 s:piano release:0.1 pan:0.6944444444444444 ]", - "[ 25/2 ⇜ (14/1 → 29/2) | n:7 gain:0.5 clip:1 note:F4 s:piano release:0.1 pan:0.5509259259259259 ]", - "[ 25/2 ⇜ (14/1 → 29/2) | n:9 gain:0.5 clip:1 note:A4 s:piano release:0.1 pan:0.5694444444444444 ]", - "[ 25/2 ⇜ (14/1 → 29/2) | n:13 gain:0.5 clip:1 note:Eb5 s:piano release:0.1 pan:0.5972222222222222 ]", - "[ 27/2 ⇜ (14/1 → 29/2) | n:14 gain:0.5 clip:1 note:F5 s:piano release:0.1 pan:0.6064814814814814 ]", - "[ 27/2 ⇜ (14/1 → 29/2) | n:29 gain:0.25 clip:1 note:G7 s:piano release:0.1 pan:0.7268518518518519 ]", - "[ 83/6 ⇜ (14/1 → 29/2) | n:0 gain:0.5 clip:1 note:F3 s:piano release:0.1 pan:0.49537037037037035 ]", - "[ 14/1 → 44/3 | n:-7 gain:1 clip:1 note:F2 s:piano release:0.1 pan:0.4398148148148148 ]", - "[ 13/1 ⇜ (14/1 → 15/1) | n:14 gain:0.3333333333333333 clip:1 note:F5 s:piano release:0.1 pan:0.6064814814814814 ]", - "[ 13/1 ⇜ (14/1 → 15/1) | n:16 gain:0.3333333333333333 clip:1 note:A5 s:piano release:0.1 pan:0.625 ]", - "[ 13/1 ⇜ (14/1 → 15/1) | n:20 gain:0.3333333333333333 clip:1 note:Eb6 s:piano release:0.1 pan:0.6527777777777778 ]", - "[ 27/2 ⇜ (14/1 → 15/1) ⇝ 31/2 | n:21 gain:0.25 clip:1 note:F6 s:piano release:0.1 pan:0.662037037037037 ]", - "[ 27/2 ⇜ (14/1 → 15/1) ⇝ 31/2 | n:23 gain:0.25 clip:1 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", - "[ 27/2 ⇜ (14/1 → 15/1) ⇝ 31/2 | n:27 gain:0.25 clip:1 note:Eb7 s:piano release:0.1 pan:0.7083333333333333 ]", - "[ 14/1 → 15/1 | n:8 gain:1 clip:1 note:G4 s:piano release:0.1 pan:0.5601851851851851 ]", - "[ 14/1 → 15/1 | n:21 gain:0.3333333333333333 clip:1 note:F6 s:piano release:0.1 pan:0.662037037037037 ]", - "[ (14/1 → 15/1) ⇝ 16/1 | n:0 gain:1 clip:1 note:F3 s:piano release:0.1 pan:0.49537037037037035 ]", - "[ (14/1 → 15/1) ⇝ 16/1 | n:2 gain:1 clip:1 note:A3 s:piano release:0.1 pan:0.5138888888888888 ]", - "[ (14/1 → 15/1) ⇝ 16/1 | n:6 gain:1 clip:1 note:Eb4 s:piano release:0.1 pan:0.5416666666666667 ]", - "[ 85/6 → 29/2 | n:22 clip:1 gain:0.7 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", - "[ 85/6 → 29/2 | n:25 clip:1 gain:0.7338261212717718 note:C7 s:piano release:0.1 pan:0.6944444444444444 ]", - "[ 85/6 → 89/6 | n:14 gain:0.25 clip:1 note:F5 s:piano release:0.1 pan:0.6064814814814814 ]", - "[ 43/3 → 44/3 | n:25 clip:1 gain:0.6618033988749896 note:C7 s:piano release:0.1 pan:0.6944444444444444 ]", - "[ 43/3 → 44/3 | n:22 clip:1 gain:0.7 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", - "[ 43/3 → 15/1 | n:7 gain:0.3333333333333333 clip:1 note:F4 s:piano release:0.1 pan:0.5509259259259259 ]", - "[ 29/2 → 89/6 | n:25 clip:1 gain:0.6618033988749896 note:C7 s:piano release:0.1 pan:0.6944444444444444 ]", - "[ 29/2 → 89/6 | n:22 clip:1 gain:0.7 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", - "[ (29/2 → 15/1) ⇝ 91/6 | n:0 gain:0.5 clip:1 note:F3 s:piano release:0.1 pan:0.49537037037037035 ]", - "[ (29/2 → 15/1) ⇝ 31/2 | n:15 gain:0.5 clip:1 note:G5 s:piano release:0.1 pan:0.6157407407407407 ]", - "[ (29/2 → 15/1) ⇝ 31/2 | n:28 gain:0.25 clip:1 note:F7 s:piano release:0.1 pan:0.7175925925925926 ]", - "[ (29/2 → 15/1) ⇝ 33/2 | n:7 gain:0.5 clip:1 note:F4 s:piano release:0.1 pan:0.5509259259259259 ]", - "[ (29/2 → 15/1) ⇝ 33/2 | n:9 gain:0.5 clip:1 note:A4 s:piano release:0.1 pan:0.5694444444444444 ]", - "[ (29/2 → 15/1) ⇝ 33/2 | n:13 gain:0.5 clip:1 note:Eb5 s:piano release:0.1 pan:0.5972222222222222 ]", - "[ 44/3 → 15/1 | n:22 clip:1 gain:0.6209056926535306 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", - "[ 44/3 → 15/1 | n:25 clip:1 gain:0.6618033988749896 note:C7 s:piano release:0.1 pan:0.6944444444444444 ]", - "[ (44/3 → 15/1) ⇝ 46/3 | n:-7 gain:1 clip:1 note:F2 s:piano release:0.1 pan:0.4398148148148148 ]", - "[ (89/6 → 15/1) ⇝ 91/6 | n:22 clip:1 gain:0.6209056926535306 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", - "[ (89/6 → 15/1) ⇝ 91/6 | n:25 clip:1 gain:0.6618033988749896 note:C7 s:piano release:0.1 pan:0.6944444444444444 ]", - "[ (89/6 → 15/1) ⇝ 31/2 | n:14 gain:0.25 clip:1 note:F5 s:piano release:0.1 pan:0.6064814814814814 ]", - "[ 29/2 ⇜ (15/1 → 91/6) | n:0 gain:0.5 clip:1 note:F3 s:piano release:0.1 pan:0.49537037037037035 ]", - "[ 89/6 ⇜ (15/1 → 91/6) | n:22 clip:1 gain:0.6209056926535306 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", - "[ 89/6 ⇜ (15/1 → 91/6) | n:25 clip:1 gain:0.6618033988749896 note:C7 s:piano release:0.1 pan:0.6944444444444444 ]", - "[ 44/3 ⇜ (15/1 → 46/3) | n:-7 gain:1 clip:1 note:F2 s:piano release:0.1 pan:0.4398148148148148 ]", - "[ 15/1 → 46/3 | n:25 clip:1 gain:0.5790943073464696 note:C7 s:piano release:0.1 pan:0.6944444444444444 ]", - "[ 15/1 → 46/3 | n:22 clip:1 gain:0.6209056926535306 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", - "[ 27/2 ⇜ (15/1 → 31/2) | n:21 gain:0.25 clip:1 note:F6 s:piano release:0.1 pan:0.662037037037037 ]", - "[ 27/2 ⇜ (15/1 → 31/2) | n:23 gain:0.25 clip:1 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", - "[ 27/2 ⇜ (15/1 → 31/2) | n:27 gain:0.25 clip:1 note:Eb7 s:piano release:0.1 pan:0.7083333333333333 ]", - "[ 29/2 ⇜ (15/1 → 31/2) | n:15 gain:0.5 clip:1 note:G5 s:piano release:0.1 pan:0.6157407407407407 ]", - "[ 29/2 ⇜ (15/1 → 31/2) | n:28 gain:0.25 clip:1 note:F7 s:piano release:0.1 pan:0.7175925925925926 ]", - "[ 89/6 ⇜ (15/1 → 31/2) | n:14 gain:0.25 clip:1 note:F5 s:piano release:0.1 pan:0.6064814814814814 ]", - "[ 15/1 → 47/3 | n:7 gain:0.3333333333333333 clip:1 note:F4 s:piano release:0.1 pan:0.5509259259259259 ]", - "[ 14/1 ⇜ (15/1 → 16/1) | n:0 gain:1 clip:1 note:F3 s:piano release:0.1 pan:0.49537037037037035 ]", - "[ 14/1 ⇜ (15/1 → 16/1) | n:2 gain:1 clip:1 note:A3 s:piano release:0.1 pan:0.5138888888888888 ]", - "[ 14/1 ⇜ (15/1 → 16/1) | n:6 gain:1 clip:1 note:Eb4 s:piano release:0.1 pan:0.5416666666666667 ]", - "[ 29/2 ⇜ (15/1 → 16/1) ⇝ 33/2 | n:7 gain:0.5 clip:1 note:F4 s:piano release:0.1 pan:0.5509259259259259 ]", - "[ 29/2 ⇜ (15/1 → 16/1) ⇝ 33/2 | n:9 gain:0.5 clip:1 note:A4 s:piano release:0.1 pan:0.5694444444444444 ]", - "[ 29/2 ⇜ (15/1 → 16/1) ⇝ 33/2 | n:13 gain:0.5 clip:1 note:Eb5 s:piano release:0.1 pan:0.5972222222222222 ]", - "[ 15/1 → 16/1 | n:7 gain:1 clip:1 note:F4 s:piano release:0.1 pan:0.5509259259259259 ]", - "[ 15/1 → 16/1 | n:22 gain:0.3333333333333333 clip:1 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", - "[ (15/1 → 16/1) ⇝ 17/1 | n:14 gain:0.3333333333333333 clip:1 note:F5 s:piano release:0.1 pan:0.6064814814814814 ]", - "[ (15/1 → 16/1) ⇝ 17/1 | n:16 gain:0.3333333333333333 clip:1 note:A5 s:piano release:0.1 pan:0.625 ]", - "[ (15/1 → 16/1) ⇝ 17/1 | n:20 gain:0.3333333333333333 clip:1 note:Eb6 s:piano release:0.1 pan:0.6527777777777778 ]", - "[ 91/6 → 31/2 | n:25 clip:1 gain:0.5790943073464696 note:C7 s:piano release:0.1 pan:0.6944444444444444 ]", - "[ 91/6 → 31/2 | n:22 clip:1 gain:0.6209056926535306 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", - "[ 91/6 → 95/6 | n:0 gain:0.5 clip:1 note:F3 s:piano release:0.1 pan:0.49537037037037035 ]", - "[ 46/3 → 47/3 | n:22 clip:1 gain:0.5381966011250107 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", - "[ 46/3 → 47/3 | n:25 clip:1 gain:0.5790943073464696 note:C7 s:piano release:0.1 pan:0.6944444444444444 ]", - "[ 46/3 → 16/1 | n:-7 gain:1 clip:1 note:F2 s:piano release:0.1 pan:0.4398148148148148 ]", - "[ 31/2 → 95/6 | n:22 clip:1 gain:0.5381966011250107 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", - "[ 31/2 → 95/6 | n:25 clip:1 gain:0.5790943073464696 note:C7 s:piano release:0.1 pan:0.6944444444444444 ]", - "[ (31/2 → 16/1) ⇝ 97/6 | n:14 gain:0.25 clip:1 note:F5 s:piano release:0.1 pan:0.6064814814814814 ]", - "[ (31/2 → 16/1) ⇝ 33/2 | n:14 gain:0.5 clip:1 note:F5 s:piano release:0.1 pan:0.6064814814814814 ]", - "[ (31/2 → 16/1) ⇝ 33/2 | n:29 gain:0.25 clip:1 note:G7 s:piano release:0.1 pan:0.7268518518518519 ]", - "[ (31/2 → 16/1) ⇝ 35/2 | n:21 gain:0.25 clip:1 note:F6 s:piano release:0.1 pan:0.662037037037037 ]", - "[ (31/2 → 16/1) ⇝ 35/2 | n:23 gain:0.25 clip:1 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", - "[ (31/2 → 16/1) ⇝ 35/2 | n:27 gain:0.25 clip:1 note:Eb7 s:piano release:0.1 pan:0.7083333333333333 ]", - "[ 47/3 → 16/1 | n:25 clip:1 gain:0.5000000000000002 note:C7 s:piano release:0.1 pan:0.6944444444444444 ]", - "[ 47/3 → 16/1 | n:22 clip:1 gain:0.5381966011250107 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", - "[ (47/3 → 16/1) ⇝ 49/3 | n:7 gain:0.3333333333333333 clip:1 note:F4 s:piano release:0.1 pan:0.5509259259259259 ]", - "[ (95/6 → 16/1) ⇝ 97/6 | n:25 clip:1 gain:0.5000000000000002 note:C7 s:piano release:0.1 pan:0.6944444444444444 ]", - "[ (95/6 → 16/1) ⇝ 97/6 | n:22 clip:1 gain:0.5381966011250107 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", - "[ (95/6 → 16/1) ⇝ 33/2 | n:0 gain:0.5 clip:1 note:F3 s:piano release:0.1 pan:0.49537037037037035 ]", + "[ -1/2 ⇜ (0/1 → 1/6) | gain:0.25 clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", + "[ -1/6 ⇜ (0/1 → 1/6) | clip:1 gain:0.5790943073464694 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", + "[ -1/6 ⇜ (0/1 → 1/6) | clip:1 gain:0.5381966011250106 note:E6 s:piano release:0.1 pan:0.6574074074074074 ]", + "[ -1/3 ⇜ (0/1 → 1/3) | gain:0.3333333333333333 clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", + "[ 0/1 → 1/3 | clip:1 gain:0.6209056926535308 note:E6 s:piano release:0.1 pan:0.6574074074074074 ]", + "[ 0/1 → 1/3 | clip:1 gain:0.5790943073464694 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", + "[ -3/2 ⇜ (0/1 → 1/2) | gain:0.5 clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", + "[ -3/2 ⇜ (0/1 → 1/2) | gain:0.5 clip:1 note:F4 s:piano release:0.1 pan:0.5509259259259259 ]", + "[ -3/2 ⇜ (0/1 → 1/2) | gain:0.5 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", + "[ -1/2 ⇜ (0/1 → 1/2) | gain:0.5 clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", + "[ -1/2 ⇜ (0/1 → 1/2) | gain:0.25 clip:1 note:E7 s:piano release:0.1 pan:0.712962962962963 ]", + "[ -1/6 ⇜ (0/1 → 1/2) | gain:0.5 clip:1 note:D3 s:piano release:0.1 pan:0.4814814814814815 ]", + "[ 0/1 → 2/3 | gain:1 clip:1 note:D2 s:piano release:0.1 pan:0.42592592592592593 ]", + "[ -1/1 ⇜ (0/1 → 1/1) | gain:0.3333333333333333 clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", + "[ -1/1 ⇜ (0/1 → 1/1) | gain:0.3333333333333333 clip:1 note:F5 s:piano release:0.1 pan:0.6064814814814814 ]", + "[ -1/1 ⇜ (0/1 → 1/1) | gain:0.3333333333333333 clip:1 note:C6 s:piano release:0.1 pan:0.6388888888888888 ]", + "[ -1/2 ⇜ (0/1 → 1/1) ⇝ 3/2 | gain:0.25 clip:1 note:D6 s:piano release:0.1 pan:0.6481481481481481 ]", + "[ -1/2 ⇜ (0/1 → 1/1) ⇝ 3/2 | gain:0.25 clip:1 note:F6 s:piano release:0.1 pan:0.662037037037037 ]", + "[ -1/2 ⇜ (0/1 → 1/1) ⇝ 3/2 | gain:0.25 clip:1 note:C7 s:piano release:0.1 pan:0.6944444444444444 ]", + "[ 0/1 → 1/1 | gain:1 clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 0/1 → 1/1 | gain:0.3333333333333333 clip:1 note:D6 s:piano release:0.1 pan:0.6481481481481481 ]", + "[ (0/1 → 1/1) ⇝ 2/1 | gain:1 clip:1 note:D3 s:piano release:0.1 pan:0.4814814814814815 ]", + "[ (0/1 → 1/1) ⇝ 2/1 | gain:1 clip:1 note:F3 s:piano release:0.1 pan:0.49537037037037035 ]", + "[ (0/1 → 1/1) ⇝ 2/1 | gain:1 clip:1 note:C4 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ 1/6 → 1/2 | clip:1 gain:0.6209056926535308 note:E6 s:piano release:0.1 pan:0.6574074074074074 ]", + "[ 1/6 → 1/2 | clip:1 gain:0.5790943073464694 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", + "[ 1/6 → 5/6 | gain:0.25 clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", + "[ 1/3 → 2/3 | clip:1 gain:0.6618033988749895 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", + "[ 1/3 → 2/3 | clip:1 gain:0.6209056926535308 note:E6 s:piano release:0.1 pan:0.6574074074074074 ]", + "[ 1/3 → 1/1 | gain:0.3333333333333333 clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", + "[ 1/2 → 5/6 | clip:1 gain:0.6618033988749895 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", + "[ 1/2 → 5/6 | clip:1 gain:0.6209056926535308 note:E6 s:piano release:0.1 pan:0.6574074074074074 ]", + "[ (1/2 → 1/1) ⇝ 7/6 | gain:0.5 clip:1 note:D3 s:piano release:0.1 pan:0.4814814814814815 ]", + "[ (1/2 → 1/1) ⇝ 3/2 | gain:0.5 clip:1 note:E5 s:piano release:0.1 pan:0.6018518518518519 ]", + "[ (1/2 → 1/1) ⇝ 3/2 | gain:0.25 clip:1 note:D7 s:piano release:0.1 pan:0.7037037037037037 ]", + "[ (1/2 → 1/1) ⇝ 5/2 | gain:0.5 clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", + "[ (1/2 → 1/1) ⇝ 5/2 | gain:0.5 clip:1 note:F4 s:piano release:0.1 pan:0.5509259259259259 ]", + "[ (1/2 → 1/1) ⇝ 5/2 | gain:0.5 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", + "[ 2/3 → 1/1 | clip:1 gain:0.7000000000000001 note:E6 s:piano release:0.1 pan:0.6574074074074074 ]", + "[ 2/3 → 1/1 | clip:1 gain:0.6618033988749895 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", + "[ (2/3 → 1/1) ⇝ 4/3 | gain:1 clip:1 note:D2 s:piano release:0.1 pan:0.42592592592592593 ]", + "[ (5/6 → 1/1) ⇝ 7/6 | clip:1 gain:0.7000000000000001 note:E6 s:piano release:0.1 pan:0.6574074074074074 ]", + "[ (5/6 → 1/1) ⇝ 7/6 | clip:1 gain:0.6618033988749895 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", + "[ (5/6 → 1/1) ⇝ 3/2 | gain:0.25 clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", + "[ 1/2 ⇜ (1/1 → 7/6) | gain:0.5 clip:1 note:D3 s:piano release:0.1 pan:0.4814814814814815 ]", + "[ 5/6 ⇜ (1/1 → 7/6) | clip:1 gain:0.7000000000000001 note:E6 s:piano release:0.1 pan:0.6574074074074074 ]", + "[ 5/6 ⇜ (1/1 → 7/6) | clip:1 gain:0.6618033988749895 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", + "[ 2/3 ⇜ (1/1 → 4/3) | gain:1 clip:1 note:D2 s:piano release:0.1 pan:0.42592592592592593 ]", + "[ 1/1 → 4/3 | clip:1 gain:0.7338261212717717 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", + "[ 1/1 → 4/3 | clip:1 gain:0.7000000000000001 note:E6 s:piano release:0.1 pan:0.6574074074074074 ]", + "[ -1/2 ⇜ (1/1 → 3/2) | gain:0.25 clip:1 note:D6 s:piano release:0.1 pan:0.6481481481481481 ]", + "[ -1/2 ⇜ (1/1 → 3/2) | gain:0.25 clip:1 note:F6 s:piano release:0.1 pan:0.662037037037037 ]", + "[ -1/2 ⇜ (1/1 → 3/2) | gain:0.25 clip:1 note:C7 s:piano release:0.1 pan:0.6944444444444444 ]", + "[ 1/2 ⇜ (1/1 → 3/2) | gain:0.5 clip:1 note:E5 s:piano release:0.1 pan:0.6018518518518519 ]", + "[ 1/2 ⇜ (1/1 → 3/2) | gain:0.25 clip:1 note:D7 s:piano release:0.1 pan:0.7037037037037037 ]", + "[ 5/6 ⇜ (1/1 → 3/2) | gain:0.25 clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", + "[ 1/1 → 5/3 | gain:0.3333333333333333 clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", + "[ 0/1 ⇜ (1/1 → 2/1) | gain:1 clip:1 note:D3 s:piano release:0.1 pan:0.4814814814814815 ]", + "[ 0/1 ⇜ (1/1 → 2/1) | gain:1 clip:1 note:F3 s:piano release:0.1 pan:0.49537037037037035 ]", + "[ 0/1 ⇜ (1/1 → 2/1) | gain:1 clip:1 note:C4 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ 1/2 ⇜ (1/1 → 2/1) ⇝ 5/2 | gain:0.5 clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", + "[ 1/2 ⇜ (1/1 → 2/1) ⇝ 5/2 | gain:0.5 clip:1 note:F4 s:piano release:0.1 pan:0.5509259259259259 ]", + "[ 1/2 ⇜ (1/1 → 2/1) ⇝ 5/2 | gain:0.5 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", + "[ 1/1 → 2/1 | gain:1 clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", + "[ 1/1 → 2/1 | gain:0.3333333333333333 clip:1 note:E6 s:piano release:0.1 pan:0.6574074074074074 ]", + "[ (1/1 → 2/1) ⇝ 3/1 | gain:0.3333333333333333 clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", + "[ (1/1 → 2/1) ⇝ 3/1 | gain:0.3333333333333333 clip:1 note:F5 s:piano release:0.1 pan:0.6064814814814814 ]", + "[ (1/1 → 2/1) ⇝ 3/1 | gain:0.3333333333333333 clip:1 note:C6 s:piano release:0.1 pan:0.6388888888888888 ]", + "[ 7/6 → 3/2 | clip:1 gain:0.7338261212717717 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", + "[ 7/6 → 3/2 | clip:1 gain:0.7000000000000001 note:E6 s:piano release:0.1 pan:0.6574074074074074 ]", + "[ 7/6 → 11/6 | gain:0.5 clip:1 note:D3 s:piano release:0.1 pan:0.4814814814814815 ]", + "[ 4/3 → 5/3 | clip:1 gain:0.7618033988749895 note:E6 s:piano release:0.1 pan:0.6574074074074074 ]", + "[ 4/3 → 5/3 | clip:1 gain:0.7338261212717717 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", + "[ 4/3 → 2/1 | gain:1 clip:1 note:D2 s:piano release:0.1 pan:0.42592592592592593 ]", + "[ 3/2 → 11/6 | clip:1 gain:0.7618033988749895 note:E6 s:piano release:0.1 pan:0.6574074074074074 ]", + "[ 3/2 → 11/6 | clip:1 gain:0.7338261212717717 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", + "[ (3/2 → 2/1) ⇝ 13/6 | gain:0.25 clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", + "[ (3/2 → 2/1) ⇝ 5/2 | gain:0.5 clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", + "[ (3/2 → 2/1) ⇝ 5/2 | gain:0.25 clip:1 note:E7 s:piano release:0.1 pan:0.712962962962963 ]", + "[ (3/2 → 2/1) ⇝ 7/2 | gain:0.25 clip:1 note:D6 s:piano release:0.1 pan:0.6481481481481481 ]", + "[ (3/2 → 2/1) ⇝ 7/2 | gain:0.25 clip:1 note:F6 s:piano release:0.1 pan:0.662037037037037 ]", + "[ (3/2 → 2/1) ⇝ 7/2 | gain:0.25 clip:1 note:C7 s:piano release:0.1 pan:0.6944444444444444 ]", + "[ 5/3 → 2/1 | clip:1 gain:0.7827090915285202 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", + "[ 5/3 → 2/1 | clip:1 gain:0.7618033988749895 note:E6 s:piano release:0.1 pan:0.6574074074074074 ]", + "[ (5/3 → 2/1) ⇝ 7/3 | gain:0.3333333333333333 clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", + "[ (11/6 → 2/1) ⇝ 13/6 | clip:1 gain:0.7827090915285202 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", + "[ (11/6 → 2/1) ⇝ 13/6 | clip:1 gain:0.7618033988749895 note:E6 s:piano release:0.1 pan:0.6574074074074074 ]", + "[ (11/6 → 2/1) ⇝ 5/2 | gain:0.5 clip:1 note:D3 s:piano release:0.1 pan:0.4814814814814815 ]", + "[ 3/2 ⇜ (2/1 → 13/6) | gain:0.25 clip:1 note:G5 s:piano release:0.1 pan:0.6157407407407407 ]", + "[ 11/6 ⇜ (2/1 → 13/6) | clip:1 gain:0.7827090915285202 note:D7 s:piano release:0.1 pan:0.7037037037037037 ]", + "[ 11/6 ⇜ (2/1 → 13/6) | clip:1 gain:0.7618033988749895 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", + "[ 5/3 ⇜ (2/1 → 7/3) | gain:0.3333333333333333 clip:1 note:G4 s:piano release:0.1 pan:0.5601851851851851 ]", + "[ 2/1 → 7/3 | clip:1 gain:0.7956295201467611 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", + "[ 2/1 → 7/3 | clip:1 gain:0.7827090915285202 note:D7 s:piano release:0.1 pan:0.7037037037037037 ]", + "[ 1/2 ⇜ (2/1 → 5/2) | gain:0.5 clip:1 note:G4 s:piano release:0.1 pan:0.5601851851851851 ]", + "[ 1/2 ⇜ (2/1 → 5/2) | gain:0.5 clip:1 note:B4 s:piano release:0.1 pan:0.5787037037037037 ]", + "[ 1/2 ⇜ (2/1 → 5/2) | gain:0.5 clip:1 note:F5 s:piano release:0.1 pan:0.6064814814814814 ]", + "[ 3/2 ⇜ (2/1 → 5/2) | gain:0.5 clip:1 note:G5 s:piano release:0.1 pan:0.6157407407407407 ]", + "[ 3/2 ⇜ (2/1 → 5/2) | gain:0.25 clip:1 note:A7 s:piano release:0.1 pan:0.7361111111111112 ]", + "[ 11/6 ⇜ (2/1 → 5/2) | gain:0.5 clip:1 note:G3 s:piano release:0.1 pan:0.5046296296296297 ]", + "[ 2/1 → 8/3 | gain:1 clip:1 note:G2 s:piano release:0.1 pan:0.44907407407407407 ]", + "[ 1/1 ⇜ (2/1 → 3/1) | gain:0.3333333333333333 clip:1 note:G5 s:piano release:0.1 pan:0.6157407407407407 ]", + "[ 1/1 ⇜ (2/1 → 3/1) | gain:0.3333333333333333 clip:1 note:B5 s:piano release:0.1 pan:0.6342592592592593 ]", + "[ 1/1 ⇜ (2/1 → 3/1) | gain:0.3333333333333333 clip:1 note:F6 s:piano release:0.1 pan:0.662037037037037 ]", + "[ 3/2 ⇜ (2/1 → 3/1) ⇝ 7/2 | gain:0.25 clip:1 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", + "[ 3/2 ⇜ (2/1 → 3/1) ⇝ 7/2 | gain:0.25 clip:1 note:B6 s:piano release:0.1 pan:0.6898148148148149 ]", + "[ 3/2 ⇜ (2/1 → 3/1) ⇝ 7/2 | gain:0.25 clip:1 note:F7 s:piano release:0.1 pan:0.7175925925925926 ]", + "[ 2/1 → 3/1 | gain:1 clip:1 note:A4 s:piano release:0.1 pan:0.5694444444444444 ]", + "[ 2/1 → 3/1 | gain:0.3333333333333333 clip:1 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", + "[ (2/1 → 3/1) ⇝ 4/1 | gain:1 clip:1 note:G3 s:piano release:0.1 pan:0.5046296296296297 ]", + "[ (2/1 → 3/1) ⇝ 4/1 | gain:1 clip:1 note:B3 s:piano release:0.1 pan:0.5231481481481481 ]", + "[ (2/1 → 3/1) ⇝ 4/1 | gain:1 clip:1 note:F4 s:piano release:0.1 pan:0.5509259259259259 ]", + "[ 13/6 → 5/2 | clip:1 gain:0.7956295201467611 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", + "[ 13/6 → 5/2 | clip:1 gain:0.7827090915285202 note:D7 s:piano release:0.1 pan:0.7037037037037037 ]", + "[ 13/6 → 17/6 | gain:0.25 clip:1 note:G5 s:piano release:0.1 pan:0.6157407407407407 ]", + "[ 7/3 → 8/3 | clip:1 gain:0.8 note:D7 s:piano release:0.1 pan:0.7037037037037037 ]", + "[ 7/3 → 8/3 | clip:1 gain:0.7956295201467611 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", + "[ 7/3 → 3/1 | gain:0.3333333333333333 clip:1 note:G4 s:piano release:0.1 pan:0.5601851851851851 ]", + "[ 5/2 → 17/6 | clip:1 gain:0.8 note:D7 s:piano release:0.1 pan:0.7037037037037037 ]", + "[ 5/2 → 17/6 | clip:1 gain:0.7956295201467611 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", + "[ (5/2 → 3/1) ⇝ 19/6 | gain:0.5 clip:1 note:G3 s:piano release:0.1 pan:0.5046296296296297 ]", + "[ (5/2 → 3/1) ⇝ 7/2 | gain:0.5 clip:1 note:A5 s:piano release:0.1 pan:0.625 ]", + "[ (5/2 → 3/1) ⇝ 7/2 | gain:0.25 clip:1 note:G7 s:piano release:0.1 pan:0.7268518518518519 ]", + "[ (5/2 → 3/1) ⇝ 9/2 | gain:0.5 clip:1 note:G4 s:piano release:0.1 pan:0.5601851851851851 ]", + "[ (5/2 → 3/1) ⇝ 9/2 | gain:0.5 clip:1 note:B4 s:piano release:0.1 pan:0.5787037037037037 ]", + "[ (5/2 → 3/1) ⇝ 9/2 | gain:0.5 clip:1 note:F5 s:piano release:0.1 pan:0.6064814814814814 ]", + "[ 8/3 → 3/1 | clip:1 gain:0.7956295201467611 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", + "[ 8/3 → 3/1 | clip:1 gain:0.8 note:D7 s:piano release:0.1 pan:0.7037037037037037 ]", + "[ (8/3 → 3/1) ⇝ 10/3 | gain:1 clip:1 note:G2 s:piano release:0.1 pan:0.44907407407407407 ]", + "[ (17/6 → 3/1) ⇝ 19/6 | clip:1 gain:0.7956295201467611 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", + "[ (17/6 → 3/1) ⇝ 19/6 | clip:1 gain:0.8 note:D7 s:piano release:0.1 pan:0.7037037037037037 ]", + "[ (17/6 → 3/1) ⇝ 7/2 | gain:0.25 clip:1 note:G5 s:piano release:0.1 pan:0.6157407407407407 ]", + "[ 5/2 ⇜ (3/1 → 19/6) | gain:0.5 clip:1 note:G3 s:piano release:0.1 pan:0.5046296296296297 ]", + "[ 17/6 ⇜ (3/1 → 19/6) | clip:1 gain:0.7956295201467611 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", + "[ 17/6 ⇜ (3/1 → 19/6) | clip:1 gain:0.8 note:D7 s:piano release:0.1 pan:0.7037037037037037 ]", + "[ 8/3 ⇜ (3/1 → 10/3) | gain:1 clip:1 note:G2 s:piano release:0.1 pan:0.44907407407407407 ]", + "[ 3/1 → 10/3 | clip:1 gain:0.7827090915285202 note:D7 s:piano release:0.1 pan:0.7037037037037037 ]", + "[ 3/1 → 10/3 | clip:1 gain:0.7956295201467611 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", + "[ 3/2 ⇜ (3/1 → 7/2) | gain:0.25 clip:1 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", + "[ 3/2 ⇜ (3/1 → 7/2) | gain:0.25 clip:1 note:B6 s:piano release:0.1 pan:0.6898148148148149 ]", + "[ 3/2 ⇜ (3/1 → 7/2) | gain:0.25 clip:1 note:F7 s:piano release:0.1 pan:0.7175925925925926 ]", + "[ 5/2 ⇜ (3/1 → 7/2) | gain:0.5 clip:1 note:A5 s:piano release:0.1 pan:0.625 ]", + "[ 5/2 ⇜ (3/1 → 7/2) | gain:0.25 clip:1 note:G7 s:piano release:0.1 pan:0.7268518518518519 ]", + "[ 17/6 ⇜ (3/1 → 7/2) | gain:0.25 clip:1 note:G5 s:piano release:0.1 pan:0.6157407407407407 ]", + "[ 3/1 → 11/3 | gain:0.3333333333333333 clip:1 note:G4 s:piano release:0.1 pan:0.5601851851851851 ]", + "[ 2/1 ⇜ (3/1 → 4/1) | gain:1 clip:1 note:G3 s:piano release:0.1 pan:0.5046296296296297 ]", + "[ 2/1 ⇜ (3/1 → 4/1) | gain:1 clip:1 note:B3 s:piano release:0.1 pan:0.5231481481481481 ]", + "[ 2/1 ⇜ (3/1 → 4/1) | gain:1 clip:1 note:F4 s:piano release:0.1 pan:0.5509259259259259 ]", + "[ 5/2 ⇜ (3/1 → 4/1) ⇝ 9/2 | gain:0.5 clip:1 note:G4 s:piano release:0.1 pan:0.5601851851851851 ]", + "[ 5/2 ⇜ (3/1 → 4/1) ⇝ 9/2 | gain:0.5 clip:1 note:B4 s:piano release:0.1 pan:0.5787037037037037 ]", + "[ 5/2 ⇜ (3/1 → 4/1) ⇝ 9/2 | gain:0.5 clip:1 note:F5 s:piano release:0.1 pan:0.6064814814814814 ]", + "[ 3/1 → 4/1 | gain:1 clip:1 note:G4 s:piano release:0.1 pan:0.5601851851851851 ]", + "[ 3/1 → 4/1 | gain:0.3333333333333333 clip:1 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", + "[ (3/1 → 4/1) ⇝ 5/1 | gain:0.3333333333333333 clip:1 note:G5 s:piano release:0.1 pan:0.6157407407407407 ]", + "[ (3/1 → 4/1) ⇝ 5/1 | gain:0.3333333333333333 clip:1 note:B5 s:piano release:0.1 pan:0.6342592592592593 ]", + "[ (3/1 → 4/1) ⇝ 5/1 | gain:0.3333333333333333 clip:1 note:F6 s:piano release:0.1 pan:0.662037037037037 ]", + "[ 19/6 → 7/2 | clip:1 gain:0.7827090915285202 note:D7 s:piano release:0.1 pan:0.7037037037037037 ]", + "[ 19/6 → 7/2 | clip:1 gain:0.7956295201467611 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", + "[ 19/6 → 23/6 | gain:0.5 clip:1 note:G3 s:piano release:0.1 pan:0.5046296296296297 ]", + "[ 10/3 → 11/3 | clip:1 gain:0.7618033988749895 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", + "[ 10/3 → 11/3 | clip:1 gain:0.7827090915285202 note:D7 s:piano release:0.1 pan:0.7037037037037037 ]", + "[ 10/3 → 4/1 | gain:1 clip:1 note:G2 s:piano release:0.1 pan:0.44907407407407407 ]", + "[ 7/2 → 23/6 | clip:1 gain:0.7618033988749895 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", + "[ 7/2 → 23/6 | clip:1 gain:0.7827090915285202 note:D7 s:piano release:0.1 pan:0.7037037037037037 ]", + "[ (7/2 → 4/1) ⇝ 25/6 | gain:0.25 clip:1 note:G5 s:piano release:0.1 pan:0.6157407407407407 ]", + "[ (7/2 → 4/1) ⇝ 9/2 | gain:0.5 clip:1 note:G5 s:piano release:0.1 pan:0.6157407407407407 ]", + "[ (7/2 → 4/1) ⇝ 9/2 | gain:0.25 clip:1 note:A7 s:piano release:0.1 pan:0.7361111111111112 ]", + "[ (7/2 → 4/1) ⇝ 11/2 | gain:0.25 clip:1 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", + "[ (7/2 → 4/1) ⇝ 11/2 | gain:0.25 clip:1 note:B6 s:piano release:0.1 pan:0.6898148148148149 ]", + "[ (7/2 → 4/1) ⇝ 11/2 | gain:0.25 clip:1 note:F7 s:piano release:0.1 pan:0.7175925925925926 ]", + "[ 11/3 → 4/1 | clip:1 gain:0.7338261212717716 note:D7 s:piano release:0.1 pan:0.7037037037037037 ]", + "[ 11/3 → 4/1 | clip:1 gain:0.7618033988749895 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", + "[ (11/3 → 4/1) ⇝ 13/3 | gain:0.3333333333333333 clip:1 note:G4 s:piano release:0.1 pan:0.5601851851851851 ]", + "[ (23/6 → 4/1) ⇝ 25/6 | clip:1 gain:0.7338261212717716 note:D7 s:piano release:0.1 pan:0.7037037037037037 ]", + "[ (23/6 → 4/1) ⇝ 25/6 | clip:1 gain:0.7618033988749895 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", + "[ (23/6 → 4/1) ⇝ 9/2 | gain:0.5 clip:1 note:G3 s:piano release:0.1 pan:0.5046296296296297 ]", + "[ 7/2 ⇜ (4/1 → 25/6) | gain:0.25 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", + "[ 23/6 ⇜ (4/1 → 25/6) | clip:1 gain:0.7338261212717716 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", + "[ 23/6 ⇜ (4/1 → 25/6) | clip:1 gain:0.7618033988749895 note:D6 s:piano release:0.1 pan:0.6481481481481481 ]", + "[ 11/3 ⇜ (4/1 → 13/3) | gain:0.3333333333333333 clip:1 note:C4 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ 4/1 → 13/3 | clip:1 gain:0.7000000000000001 note:D6 s:piano release:0.1 pan:0.6481481481481481 ]", + "[ 4/1 → 13/3 | clip:1 gain:0.7338261212717716 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", + "[ 5/2 ⇜ (4/1 → 9/2) | gain:0.5 clip:1 note:C4 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ 5/2 ⇜ (4/1 → 9/2) | gain:0.5 clip:1 note:Eb4 s:piano release:0.1 pan:0.5416666666666667 ]", + "[ 5/2 ⇜ (4/1 → 9/2) | gain:0.5 clip:1 note:Bb4 s:piano release:0.1 pan:0.5740740740740741 ]", + "[ 7/2 ⇜ (4/1 → 9/2) | gain:0.5 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", + "[ 7/2 ⇜ (4/1 → 9/2) | gain:0.25 clip:1 note:D7 s:piano release:0.1 pan:0.7037037037037037 ]", + "[ 23/6 ⇜ (4/1 → 9/2) | gain:0.5 clip:1 note:C3 s:piano release:0.1 pan:0.4722222222222222 ]", + "[ 4/1 → 14/3 | gain:1 clip:1 note:C2 s:piano release:0.1 pan:0.41666666666666663 ]", + "[ 3/1 ⇜ (4/1 → 5/1) | gain:0.3333333333333333 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", + "[ 3/1 ⇜ (4/1 → 5/1) | gain:0.3333333333333333 clip:1 note:Eb5 s:piano release:0.1 pan:0.5972222222222222 ]", + "[ 3/1 ⇜ (4/1 → 5/1) | gain:0.3333333333333333 clip:1 note:Bb5 s:piano release:0.1 pan:0.6296296296296297 ]", + "[ 7/2 ⇜ (4/1 → 5/1) ⇝ 11/2 | gain:0.25 clip:1 note:C6 s:piano release:0.1 pan:0.6388888888888888 ]", + "[ 7/2 ⇜ (4/1 → 5/1) ⇝ 11/2 | gain:0.25 clip:1 note:Eb6 s:piano release:0.1 pan:0.6527777777777778 ]", + "[ 7/2 ⇜ (4/1 → 5/1) ⇝ 11/2 | gain:0.25 clip:1 note:Bb6 s:piano release:0.1 pan:0.6851851851851851 ]", + "[ 4/1 → 5/1 | gain:1 clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", + "[ 4/1 → 5/1 | gain:0.3333333333333333 clip:1 note:C6 s:piano release:0.1 pan:0.6388888888888888 ]", + "[ (4/1 → 5/1) ⇝ 6/1 | gain:1 clip:1 note:C3 s:piano release:0.1 pan:0.4722222222222222 ]", + "[ (4/1 → 5/1) ⇝ 6/1 | gain:1 clip:1 note:Eb3 s:piano release:0.1 pan:0.4861111111111111 ]", + "[ (4/1 → 5/1) ⇝ 6/1 | gain:1 clip:1 note:Bb3 s:piano release:0.1 pan:0.5185185185185186 ]", + "[ 25/6 → 9/2 | clip:1 gain:0.7000000000000001 note:D6 s:piano release:0.1 pan:0.6481481481481481 ]", + "[ 25/6 → 9/2 | clip:1 gain:0.7338261212717716 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", + "[ 25/6 → 29/6 | gain:0.25 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", + "[ 13/3 → 14/3 | clip:1 gain:0.6618033988749895 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", + "[ 13/3 → 14/3 | clip:1 gain:0.7000000000000001 note:D6 s:piano release:0.1 pan:0.6481481481481481 ]", + "[ 13/3 → 5/1 | gain:0.3333333333333333 clip:1 note:C4 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ 9/2 → 29/6 | clip:1 gain:0.6618033988749895 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", + "[ 9/2 → 29/6 | clip:1 gain:0.7000000000000001 note:D6 s:piano release:0.1 pan:0.6481481481481481 ]", + "[ (9/2 → 5/1) ⇝ 31/6 | gain:0.5 clip:1 note:C3 s:piano release:0.1 pan:0.4722222222222222 ]", + "[ (9/2 → 5/1) ⇝ 11/2 | gain:0.5 clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", + "[ (9/2 → 5/1) ⇝ 11/2 | gain:0.25 clip:1 note:C7 s:piano release:0.1 pan:0.6944444444444444 ]", + "[ (9/2 → 5/1) ⇝ 13/2 | gain:0.5 clip:1 note:C4 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ (9/2 → 5/1) ⇝ 13/2 | gain:0.5 clip:1 note:Eb4 s:piano release:0.1 pan:0.5416666666666667 ]", + "[ (9/2 → 5/1) ⇝ 13/2 | gain:0.5 clip:1 note:Bb4 s:piano release:0.1 pan:0.5740740740740741 ]", + "[ 14/3 → 5/1 | clip:1 gain:0.6209056926535308 note:D6 s:piano release:0.1 pan:0.6481481481481481 ]", + "[ 14/3 → 5/1 | clip:1 gain:0.6618033988749895 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", + "[ (14/3 → 5/1) ⇝ 16/3 | gain:1 clip:1 note:C2 s:piano release:0.1 pan:0.41666666666666663 ]", + "[ (29/6 → 5/1) ⇝ 31/6 | clip:1 gain:0.6209056926535308 note:D6 s:piano release:0.1 pan:0.6481481481481481 ]", + "[ (29/6 → 5/1) ⇝ 31/6 | clip:1 gain:0.6618033988749895 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", + "[ (29/6 → 5/1) ⇝ 11/2 | gain:0.25 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", + "[ 9/2 ⇜ (5/1 → 31/6) | gain:0.5 clip:1 note:C3 s:piano release:0.1 pan:0.4722222222222222 ]", + "[ 29/6 ⇜ (5/1 → 31/6) | clip:1 gain:0.6209056926535308 note:D6 s:piano release:0.1 pan:0.6481481481481481 ]", + "[ 29/6 ⇜ (5/1 → 31/6) | clip:1 gain:0.6618033988749895 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", + "[ 14/3 ⇜ (5/1 → 16/3) | gain:1 clip:1 note:C2 s:piano release:0.1 pan:0.41666666666666663 ]", + "[ 5/1 → 16/3 | clip:1 gain:0.5790943073464694 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", + "[ 5/1 → 16/3 | clip:1 gain:0.6209056926535308 note:D6 s:piano release:0.1 pan:0.6481481481481481 ]", + "[ 7/2 ⇜ (5/1 → 11/2) | gain:0.25 clip:1 note:C6 s:piano release:0.1 pan:0.6388888888888888 ]", + "[ 7/2 ⇜ (5/1 → 11/2) | gain:0.25 clip:1 note:Eb6 s:piano release:0.1 pan:0.6527777777777778 ]", + "[ 7/2 ⇜ (5/1 → 11/2) | gain:0.25 clip:1 note:Bb6 s:piano release:0.1 pan:0.6851851851851851 ]", + "[ 9/2 ⇜ (5/1 → 11/2) | gain:0.5 clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", + "[ 9/2 ⇜ (5/1 → 11/2) | gain:0.25 clip:1 note:C7 s:piano release:0.1 pan:0.6944444444444444 ]", + "[ 29/6 ⇜ (5/1 → 11/2) | gain:0.25 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", + "[ 5/1 → 17/3 | gain:0.3333333333333333 clip:1 note:C4 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ 4/1 ⇜ (5/1 → 6/1) | gain:1 clip:1 note:C3 s:piano release:0.1 pan:0.4722222222222222 ]", + "[ 4/1 ⇜ (5/1 → 6/1) | gain:1 clip:1 note:Eb3 s:piano release:0.1 pan:0.4861111111111111 ]", + "[ 4/1 ⇜ (5/1 → 6/1) | gain:1 clip:1 note:Bb3 s:piano release:0.1 pan:0.5185185185185186 ]", + "[ 9/2 ⇜ (5/1 → 6/1) ⇝ 13/2 | gain:0.5 clip:1 note:C4 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ 9/2 ⇜ (5/1 → 6/1) ⇝ 13/2 | gain:0.5 clip:1 note:Eb4 s:piano release:0.1 pan:0.5416666666666667 ]", + "[ 9/2 ⇜ (5/1 → 6/1) ⇝ 13/2 | gain:0.5 clip:1 note:Bb4 s:piano release:0.1 pan:0.5740740740740741 ]", + "[ 5/1 → 6/1 | gain:1 clip:1 note:C4 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ 5/1 → 6/1 | gain:0.3333333333333333 clip:1 note:D6 s:piano release:0.1 pan:0.6481481481481481 ]", + "[ (5/1 → 6/1) ⇝ 7/1 | gain:0.3333333333333333 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", + "[ (5/1 → 6/1) ⇝ 7/1 | gain:0.3333333333333333 clip:1 note:Eb5 s:piano release:0.1 pan:0.5972222222222222 ]", + "[ (5/1 → 6/1) ⇝ 7/1 | gain:0.3333333333333333 clip:1 note:Bb5 s:piano release:0.1 pan:0.6296296296296297 ]", + "[ 31/6 → 11/2 | clip:1 gain:0.5790943073464694 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", + "[ 31/6 → 11/2 | clip:1 gain:0.6209056926535308 note:D6 s:piano release:0.1 pan:0.6481481481481481 ]", + "[ 31/6 → 35/6 | gain:0.5 clip:1 note:C3 s:piano release:0.1 pan:0.4722222222222222 ]", + "[ 16/3 → 17/3 | clip:1 gain:0.5381966011250106 note:D6 s:piano release:0.1 pan:0.6481481481481481 ]", + "[ 16/3 → 17/3 | clip:1 gain:0.5790943073464694 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", + "[ 16/3 → 6/1 | gain:1 clip:1 note:C2 s:piano release:0.1 pan:0.41666666666666663 ]", + "[ 11/2 → 35/6 | clip:1 gain:0.5381966011250106 note:D6 s:piano release:0.1 pan:0.6481481481481481 ]", + "[ 11/2 → 35/6 | clip:1 gain:0.5790943073464694 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", + "[ (11/2 → 6/1) ⇝ 37/6 | gain:0.25 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", + "[ (11/2 → 6/1) ⇝ 13/2 | gain:0.5 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", + "[ (11/2 → 6/1) ⇝ 13/2 | gain:0.25 clip:1 note:D7 s:piano release:0.1 pan:0.7037037037037037 ]", + "[ (11/2 → 6/1) ⇝ 15/2 | gain:0.25 clip:1 note:C6 s:piano release:0.1 pan:0.6388888888888888 ]", + "[ (11/2 → 6/1) ⇝ 15/2 | gain:0.25 clip:1 note:Eb6 s:piano release:0.1 pan:0.6527777777777778 ]", + "[ (11/2 → 6/1) ⇝ 15/2 | gain:0.25 clip:1 note:Bb6 s:piano release:0.1 pan:0.6851851851851851 ]", + "[ 17/3 → 6/1 | clip:1 gain:0.5 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", + "[ 17/3 → 6/1 | clip:1 gain:0.5381966011250106 note:D6 s:piano release:0.1 pan:0.6481481481481481 ]", + "[ (17/3 → 6/1) ⇝ 19/3 | gain:0.3333333333333333 clip:1 note:C4 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ (35/6 → 6/1) ⇝ 37/6 | clip:1 gain:0.5 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", + "[ (35/6 → 6/1) ⇝ 37/6 | clip:1 gain:0.5381966011250106 note:D6 s:piano release:0.1 pan:0.6481481481481481 ]", + "[ (35/6 → 6/1) ⇝ 13/2 | gain:0.5 clip:1 note:C3 s:piano release:0.1 pan:0.4722222222222222 ]", + "[ 11/2 ⇜ (6/1 → 37/6) | gain:0.25 clip:1 note:F5 s:piano release:0.1 pan:0.6064814814814814 ]", + "[ 35/6 ⇜ (6/1 → 37/6) | clip:1 gain:0.5 note:C7 s:piano release:0.1 pan:0.6944444444444444 ]", + "[ 35/6 ⇜ (6/1 → 37/6) | clip:1 gain:0.5381966011250106 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", + "[ 17/3 ⇜ (6/1 → 19/3) | gain:0.3333333333333333 clip:1 note:F4 s:piano release:0.1 pan:0.5509259259259259 ]", + "[ 6/1 → 19/3 | clip:1 gain:0.46617387872822835 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", + "[ 6/1 → 19/3 | clip:1 gain:0.5 note:C7 s:piano release:0.1 pan:0.6944444444444444 ]", + "[ 9/2 ⇜ (6/1 → 13/2) | gain:0.5 clip:1 note:F4 s:piano release:0.1 pan:0.5509259259259259 ]", + "[ 9/2 ⇜ (6/1 → 13/2) | gain:0.5 clip:1 note:A4 s:piano release:0.1 pan:0.5694444444444444 ]", + "[ 9/2 ⇜ (6/1 → 13/2) | gain:0.5 clip:1 note:Eb5 s:piano release:0.1 pan:0.5972222222222222 ]", + "[ 11/2 ⇜ (6/1 → 13/2) | gain:0.5 clip:1 note:F5 s:piano release:0.1 pan:0.6064814814814814 ]", + "[ 11/2 ⇜ (6/1 → 13/2) | gain:0.25 clip:1 note:G7 s:piano release:0.1 pan:0.7268518518518519 ]", + "[ 35/6 ⇜ (6/1 → 13/2) | gain:0.5 clip:1 note:F3 s:piano release:0.1 pan:0.49537037037037035 ]", + "[ 6/1 → 20/3 | gain:1 clip:1 note:F2 s:piano release:0.1 pan:0.4398148148148148 ]", + "[ 5/1 ⇜ (6/1 → 7/1) | gain:0.3333333333333333 clip:1 note:F5 s:piano release:0.1 pan:0.6064814814814814 ]", + "[ 5/1 ⇜ (6/1 → 7/1) | gain:0.3333333333333333 clip:1 note:A5 s:piano release:0.1 pan:0.625 ]", + "[ 5/1 ⇜ (6/1 → 7/1) | gain:0.3333333333333333 clip:1 note:Eb6 s:piano release:0.1 pan:0.6527777777777778 ]", + "[ 11/2 ⇜ (6/1 → 7/1) ⇝ 15/2 | gain:0.25 clip:1 note:F6 s:piano release:0.1 pan:0.662037037037037 ]", + "[ 11/2 ⇜ (6/1 → 7/1) ⇝ 15/2 | gain:0.25 clip:1 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", + "[ 11/2 ⇜ (6/1 → 7/1) ⇝ 15/2 | gain:0.25 clip:1 note:Eb7 s:piano release:0.1 pan:0.7083333333333333 ]", + "[ 6/1 → 7/1 | gain:1 clip:1 note:G4 s:piano release:0.1 pan:0.5601851851851851 ]", + "[ 6/1 → 7/1 | gain:0.3333333333333333 clip:1 note:F6 s:piano release:0.1 pan:0.662037037037037 ]", + "[ (6/1 → 7/1) ⇝ 8/1 | gain:1 clip:1 note:F3 s:piano release:0.1 pan:0.49537037037037035 ]", + "[ (6/1 → 7/1) ⇝ 8/1 | gain:1 clip:1 note:A3 s:piano release:0.1 pan:0.5138888888888888 ]", + "[ (6/1 → 7/1) ⇝ 8/1 | gain:1 clip:1 note:Eb4 s:piano release:0.1 pan:0.5416666666666667 ]", + "[ 37/6 → 13/2 | clip:1 gain:0.46617387872822835 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", + "[ 37/6 → 13/2 | clip:1 gain:0.5 note:C7 s:piano release:0.1 pan:0.6944444444444444 ]", + "[ 37/6 → 41/6 | gain:0.25 clip:1 note:F5 s:piano release:0.1 pan:0.6064814814814814 ]", + "[ 19/3 → 20/3 | clip:1 gain:0.4381966011250106 note:C7 s:piano release:0.1 pan:0.6944444444444444 ]", + "[ 19/3 → 20/3 | clip:1 gain:0.46617387872822835 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", + "[ 19/3 → 7/1 | gain:0.3333333333333333 clip:1 note:F4 s:piano release:0.1 pan:0.5509259259259259 ]", + "[ 13/2 → 41/6 | clip:1 gain:0.4381966011250106 note:C7 s:piano release:0.1 pan:0.6944444444444444 ]", + "[ 13/2 → 41/6 | clip:1 gain:0.46617387872822835 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", + "[ (13/2 → 7/1) ⇝ 43/6 | gain:0.5 clip:1 note:F3 s:piano release:0.1 pan:0.49537037037037035 ]", + "[ (13/2 → 7/1) ⇝ 15/2 | gain:0.5 clip:1 note:G5 s:piano release:0.1 pan:0.6157407407407407 ]", + "[ (13/2 → 7/1) ⇝ 15/2 | gain:0.25 clip:1 note:F7 s:piano release:0.1 pan:0.7175925925925926 ]", + "[ (13/2 → 7/1) ⇝ 17/2 | gain:0.5 clip:1 note:F4 s:piano release:0.1 pan:0.5509259259259259 ]", + "[ (13/2 → 7/1) ⇝ 17/2 | gain:0.5 clip:1 note:A4 s:piano release:0.1 pan:0.5694444444444444 ]", + "[ (13/2 → 7/1) ⇝ 17/2 | gain:0.5 clip:1 note:Eb5 s:piano release:0.1 pan:0.5972222222222222 ]", + "[ 20/3 → 7/1 | clip:1 gain:0.4172909084714798 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", + "[ 20/3 → 7/1 | clip:1 gain:0.4381966011250106 note:C7 s:piano release:0.1 pan:0.6944444444444444 ]", + "[ (20/3 → 7/1) ⇝ 22/3 | gain:1 clip:1 note:F2 s:piano release:0.1 pan:0.4398148148148148 ]", + "[ (41/6 → 7/1) ⇝ 43/6 | clip:1 gain:0.4172909084714798 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", + "[ (41/6 → 7/1) ⇝ 43/6 | clip:1 gain:0.4381966011250106 note:C7 s:piano release:0.1 pan:0.6944444444444444 ]", + "[ (41/6 → 7/1) ⇝ 15/2 | gain:0.25 clip:1 note:F5 s:piano release:0.1 pan:0.6064814814814814 ]", + "[ 13/2 ⇜ (7/1 → 43/6) | gain:0.5 clip:1 note:F3 s:piano release:0.1 pan:0.49537037037037035 ]", + "[ 41/6 ⇜ (7/1 → 43/6) | clip:1 gain:0.4172909084714798 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", + "[ 41/6 ⇜ (7/1 → 43/6) | clip:1 gain:0.4381966011250106 note:C7 s:piano release:0.1 pan:0.6944444444444444 ]", + "[ 20/3 ⇜ (7/1 → 22/3) | gain:1 clip:1 note:F2 s:piano release:0.1 pan:0.4398148148148148 ]", + "[ 7/1 → 22/3 | clip:1 gain:0.40437047985323893 note:C7 s:piano release:0.1 pan:0.6944444444444444 ]", + "[ 7/1 → 22/3 | clip:1 gain:0.4172909084714798 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", + "[ 11/2 ⇜ (7/1 → 15/2) | gain:0.25 clip:1 note:F6 s:piano release:0.1 pan:0.662037037037037 ]", + "[ 11/2 ⇜ (7/1 → 15/2) | gain:0.25 clip:1 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", + "[ 11/2 ⇜ (7/1 → 15/2) | gain:0.25 clip:1 note:Eb7 s:piano release:0.1 pan:0.7083333333333333 ]", + "[ 13/2 ⇜ (7/1 → 15/2) | gain:0.5 clip:1 note:G5 s:piano release:0.1 pan:0.6157407407407407 ]", + "[ 13/2 ⇜ (7/1 → 15/2) | gain:0.25 clip:1 note:F7 s:piano release:0.1 pan:0.7175925925925926 ]", + "[ 41/6 ⇜ (7/1 → 15/2) | gain:0.25 clip:1 note:F5 s:piano release:0.1 pan:0.6064814814814814 ]", + "[ 7/1 → 23/3 | gain:0.3333333333333333 clip:1 note:F4 s:piano release:0.1 pan:0.5509259259259259 ]", + "[ 6/1 ⇜ (7/1 → 8/1) | gain:1 clip:1 note:F3 s:piano release:0.1 pan:0.49537037037037035 ]", + "[ 6/1 ⇜ (7/1 → 8/1) | gain:1 clip:1 note:A3 s:piano release:0.1 pan:0.5138888888888888 ]", + "[ 6/1 ⇜ (7/1 → 8/1) | gain:1 clip:1 note:Eb4 s:piano release:0.1 pan:0.5416666666666667 ]", + "[ 13/2 ⇜ (7/1 → 8/1) ⇝ 17/2 | gain:0.5 clip:1 note:F4 s:piano release:0.1 pan:0.5509259259259259 ]", + "[ 13/2 ⇜ (7/1 → 8/1) ⇝ 17/2 | gain:0.5 clip:1 note:A4 s:piano release:0.1 pan:0.5694444444444444 ]", + "[ 13/2 ⇜ (7/1 → 8/1) ⇝ 17/2 | gain:0.5 clip:1 note:Eb5 s:piano release:0.1 pan:0.5972222222222222 ]", + "[ 7/1 → 8/1 | gain:1 clip:1 note:F4 s:piano release:0.1 pan:0.5509259259259259 ]", + "[ 7/1 → 8/1 | gain:0.3333333333333333 clip:1 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", + "[ (7/1 → 8/1) ⇝ 9/1 | gain:0.3333333333333333 clip:1 note:F5 s:piano release:0.1 pan:0.6064814814814814 ]", + "[ (7/1 → 8/1) ⇝ 9/1 | gain:0.3333333333333333 clip:1 note:A5 s:piano release:0.1 pan:0.625 ]", + "[ (7/1 → 8/1) ⇝ 9/1 | gain:0.3333333333333333 clip:1 note:Eb6 s:piano release:0.1 pan:0.6527777777777778 ]", + "[ 43/6 → 15/2 | clip:1 gain:0.40437047985323893 note:C7 s:piano release:0.1 pan:0.6944444444444444 ]", + "[ 43/6 → 15/2 | clip:1 gain:0.4172909084714798 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", + "[ 43/6 → 47/6 | gain:0.5 clip:1 note:F3 s:piano release:0.1 pan:0.49537037037037035 ]", + "[ 22/3 → 23/3 | clip:1 gain:0.4 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", + "[ 22/3 → 23/3 | clip:1 gain:0.40437047985323893 note:C7 s:piano release:0.1 pan:0.6944444444444444 ]", + "[ 22/3 → 8/1 | gain:1 clip:1 note:F2 s:piano release:0.1 pan:0.4398148148148148 ]", + "[ 15/2 → 47/6 | clip:1 gain:0.4 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", + "[ 15/2 → 47/6 | clip:1 gain:0.40437047985323893 note:C7 s:piano release:0.1 pan:0.6944444444444444 ]", + "[ (15/2 → 8/1) ⇝ 49/6 | gain:0.25 clip:1 note:F5 s:piano release:0.1 pan:0.6064814814814814 ]", + "[ (15/2 → 8/1) ⇝ 17/2 | gain:0.5 clip:1 note:F5 s:piano release:0.1 pan:0.6064814814814814 ]", + "[ (15/2 → 8/1) ⇝ 17/2 | gain:0.25 clip:1 note:G7 s:piano release:0.1 pan:0.7268518518518519 ]", + "[ (15/2 → 8/1) ⇝ 19/2 | gain:0.25 clip:1 note:F6 s:piano release:0.1 pan:0.662037037037037 ]", + "[ (15/2 → 8/1) ⇝ 19/2 | gain:0.25 clip:1 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", + "[ (15/2 → 8/1) ⇝ 19/2 | gain:0.25 clip:1 note:Eb7 s:piano release:0.1 pan:0.7083333333333333 ]", + "[ 23/3 → 8/1 | clip:1 gain:0.40437047985323893 note:C7 s:piano release:0.1 pan:0.6944444444444444 ]", + "[ 23/3 → 8/1 | clip:1 gain:0.4 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", + "[ (23/3 → 8/1) ⇝ 25/3 | gain:0.3333333333333333 clip:1 note:F4 s:piano release:0.1 pan:0.5509259259259259 ]", + "[ (47/6 → 8/1) ⇝ 49/6 | clip:1 gain:0.40437047985323893 note:C7 s:piano release:0.1 pan:0.6944444444444444 ]", + "[ (47/6 → 8/1) ⇝ 49/6 | clip:1 gain:0.4 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", + "[ (47/6 → 8/1) ⇝ 17/2 | gain:0.5 clip:1 note:F3 s:piano release:0.1 pan:0.49537037037037035 ]", + "[ 15/2 ⇜ (8/1 → 49/6) | gain:0.25 clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", + "[ 47/6 ⇜ (8/1 → 49/6) | clip:1 gain:0.40437047985323893 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", + "[ 47/6 ⇜ (8/1 → 49/6) | clip:1 gain:0.4 note:E6 s:piano release:0.1 pan:0.6574074074074074 ]", + "[ 23/3 ⇜ (8/1 → 25/3) | gain:0.3333333333333333 clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", + "[ 8/1 → 25/3 | clip:1 gain:0.4172909084714798 note:E6 s:piano release:0.1 pan:0.6574074074074074 ]", + "[ 8/1 → 25/3 | clip:1 gain:0.40437047985323893 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", + "[ 13/2 ⇜ (8/1 → 17/2) | gain:0.5 clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", + "[ 13/2 ⇜ (8/1 → 17/2) | gain:0.5 clip:1 note:F4 s:piano release:0.1 pan:0.5509259259259259 ]", + "[ 13/2 ⇜ (8/1 → 17/2) | gain:0.5 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", + "[ 15/2 ⇜ (8/1 → 17/2) | gain:0.5 clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", + "[ 15/2 ⇜ (8/1 → 17/2) | gain:0.25 clip:1 note:E7 s:piano release:0.1 pan:0.712962962962963 ]", + "[ 47/6 ⇜ (8/1 → 17/2) | gain:0.5 clip:1 note:D3 s:piano release:0.1 pan:0.4814814814814815 ]", + "[ 8/1 → 26/3 | gain:1 clip:1 note:D2 s:piano release:0.1 pan:0.42592592592592593 ]", + "[ 7/1 ⇜ (8/1 → 9/1) | gain:0.3333333333333333 clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", + "[ 7/1 ⇜ (8/1 → 9/1) | gain:0.3333333333333333 clip:1 note:F5 s:piano release:0.1 pan:0.6064814814814814 ]", + "[ 7/1 ⇜ (8/1 → 9/1) | gain:0.3333333333333333 clip:1 note:C6 s:piano release:0.1 pan:0.6388888888888888 ]", + "[ 15/2 ⇜ (8/1 → 9/1) ⇝ 19/2 | gain:0.25 clip:1 note:D6 s:piano release:0.1 pan:0.6481481481481481 ]", + "[ 15/2 ⇜ (8/1 → 9/1) ⇝ 19/2 | gain:0.25 clip:1 note:F6 s:piano release:0.1 pan:0.662037037037037 ]", + "[ 15/2 ⇜ (8/1 → 9/1) ⇝ 19/2 | gain:0.25 clip:1 note:C7 s:piano release:0.1 pan:0.6944444444444444 ]", + "[ 8/1 → 9/1 | gain:1 clip:1 note:E4 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 8/1 → 9/1 | gain:0.3333333333333333 clip:1 note:D6 s:piano release:0.1 pan:0.6481481481481481 ]", + "[ (8/1 → 9/1) ⇝ 10/1 | gain:1 clip:1 note:D3 s:piano release:0.1 pan:0.4814814814814815 ]", + "[ (8/1 → 9/1) ⇝ 10/1 | gain:1 clip:1 note:F3 s:piano release:0.1 pan:0.49537037037037035 ]", + "[ (8/1 → 9/1) ⇝ 10/1 | gain:1 clip:1 note:C4 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ 49/6 → 17/2 | clip:1 gain:0.4172909084714798 note:E6 s:piano release:0.1 pan:0.6574074074074074 ]", + "[ 49/6 → 17/2 | clip:1 gain:0.40437047985323893 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", + "[ 49/6 → 53/6 | gain:0.25 clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", + "[ 25/3 → 26/3 | clip:1 gain:0.4381966011250105 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", + "[ 25/3 → 26/3 | clip:1 gain:0.4172909084714798 note:E6 s:piano release:0.1 pan:0.6574074074074074 ]", + "[ 25/3 → 9/1 | gain:0.3333333333333333 clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", + "[ 17/2 → 53/6 | clip:1 gain:0.4381966011250105 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", + "[ 17/2 → 53/6 | clip:1 gain:0.4172909084714798 note:E6 s:piano release:0.1 pan:0.6574074074074074 ]", + "[ (17/2 → 9/1) ⇝ 55/6 | gain:0.5 clip:1 note:D3 s:piano release:0.1 pan:0.4814814814814815 ]", + "[ (17/2 → 9/1) ⇝ 19/2 | gain:0.5 clip:1 note:E5 s:piano release:0.1 pan:0.6018518518518519 ]", + "[ (17/2 → 9/1) ⇝ 19/2 | gain:0.25 clip:1 note:D7 s:piano release:0.1 pan:0.7037037037037037 ]", + "[ (17/2 → 9/1) ⇝ 21/2 | gain:0.5 clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", + "[ (17/2 → 9/1) ⇝ 21/2 | gain:0.5 clip:1 note:F4 s:piano release:0.1 pan:0.5509259259259259 ]", + "[ (17/2 → 9/1) ⇝ 21/2 | gain:0.5 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", + "[ 26/3 → 9/1 | clip:1 gain:0.46617387872822824 note:E6 s:piano release:0.1 pan:0.6574074074074074 ]", + "[ 26/3 → 9/1 | clip:1 gain:0.4381966011250105 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", + "[ (26/3 → 9/1) ⇝ 28/3 | gain:1 clip:1 note:D2 s:piano release:0.1 pan:0.42592592592592593 ]", + "[ (53/6 → 9/1) ⇝ 55/6 | clip:1 gain:0.46617387872822824 note:E6 s:piano release:0.1 pan:0.6574074074074074 ]", + "[ (53/6 → 9/1) ⇝ 55/6 | clip:1 gain:0.4381966011250105 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", + "[ (53/6 → 9/1) ⇝ 19/2 | gain:0.25 clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", + "[ 17/2 ⇜ (9/1 → 55/6) | gain:0.5 clip:1 note:D3 s:piano release:0.1 pan:0.4814814814814815 ]", + "[ 53/6 ⇜ (9/1 → 55/6) | clip:1 gain:0.46617387872822824 note:E6 s:piano release:0.1 pan:0.6574074074074074 ]", + "[ 53/6 ⇜ (9/1 → 55/6) | clip:1 gain:0.4381966011250105 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", + "[ 26/3 ⇜ (9/1 → 28/3) | gain:1 clip:1 note:D2 s:piano release:0.1 pan:0.42592592592592593 ]", + "[ 9/1 → 28/3 | clip:1 gain:0.49999999999999994 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", + "[ 9/1 → 28/3 | clip:1 gain:0.46617387872822824 note:E6 s:piano release:0.1 pan:0.6574074074074074 ]", + "[ 15/2 ⇜ (9/1 → 19/2) | gain:0.25 clip:1 note:D6 s:piano release:0.1 pan:0.6481481481481481 ]", + "[ 15/2 ⇜ (9/1 → 19/2) | gain:0.25 clip:1 note:F6 s:piano release:0.1 pan:0.662037037037037 ]", + "[ 15/2 ⇜ (9/1 → 19/2) | gain:0.25 clip:1 note:C7 s:piano release:0.1 pan:0.6944444444444444 ]", + "[ 17/2 ⇜ (9/1 → 19/2) | gain:0.5 clip:1 note:E5 s:piano release:0.1 pan:0.6018518518518519 ]", + "[ 17/2 ⇜ (9/1 → 19/2) | gain:0.25 clip:1 note:D7 s:piano release:0.1 pan:0.7037037037037037 ]", + "[ 53/6 ⇜ (9/1 → 19/2) | gain:0.25 clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", + "[ 9/1 → 29/3 | gain:0.3333333333333333 clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", + "[ 8/1 ⇜ (9/1 → 10/1) | gain:1 clip:1 note:D3 s:piano release:0.1 pan:0.4814814814814815 ]", + "[ 8/1 ⇜ (9/1 → 10/1) | gain:1 clip:1 note:F3 s:piano release:0.1 pan:0.49537037037037035 ]", + "[ 8/1 ⇜ (9/1 → 10/1) | gain:1 clip:1 note:C4 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ 17/2 ⇜ (9/1 → 10/1) ⇝ 21/2 | gain:0.5 clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", + "[ 17/2 ⇜ (9/1 → 10/1) ⇝ 21/2 | gain:0.5 clip:1 note:F4 s:piano release:0.1 pan:0.5509259259259259 ]", + "[ 17/2 ⇜ (9/1 → 10/1) ⇝ 21/2 | gain:0.5 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", + "[ 9/1 → 10/1 | gain:1 clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", + "[ 9/1 → 10/1 | gain:0.3333333333333333 clip:1 note:E6 s:piano release:0.1 pan:0.6574074074074074 ]", + "[ (9/1 → 10/1) ⇝ 11/1 | gain:0.3333333333333333 clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", + "[ (9/1 → 10/1) ⇝ 11/1 | gain:0.3333333333333333 clip:1 note:F5 s:piano release:0.1 pan:0.6064814814814814 ]", + "[ (9/1 → 10/1) ⇝ 11/1 | gain:0.3333333333333333 clip:1 note:C6 s:piano release:0.1 pan:0.6388888888888888 ]", + "[ 55/6 → 19/2 | clip:1 gain:0.49999999999999994 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", + "[ 55/6 → 19/2 | clip:1 gain:0.46617387872822824 note:E6 s:piano release:0.1 pan:0.6574074074074074 ]", + "[ 55/6 → 59/6 | gain:0.5 clip:1 note:D3 s:piano release:0.1 pan:0.4814814814814815 ]", + "[ 28/3 → 29/3 | clip:1 gain:0.5381966011250106 note:E6 s:piano release:0.1 pan:0.6574074074074074 ]", + "[ 28/3 → 29/3 | clip:1 gain:0.49999999999999994 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", + "[ 28/3 → 10/1 | gain:1 clip:1 note:D2 s:piano release:0.1 pan:0.42592592592592593 ]", + "[ 19/2 → 59/6 | clip:1 gain:0.5381966011250106 note:E6 s:piano release:0.1 pan:0.6574074074074074 ]", + "[ 19/2 → 59/6 | clip:1 gain:0.49999999999999994 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", + "[ (19/2 → 10/1) ⇝ 61/6 | gain:0.25 clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", + "[ (19/2 → 10/1) ⇝ 21/2 | gain:0.5 clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", + "[ (19/2 → 10/1) ⇝ 21/2 | gain:0.25 clip:1 note:E7 s:piano release:0.1 pan:0.712962962962963 ]", + "[ (19/2 → 10/1) ⇝ 23/2 | gain:0.25 clip:1 note:D6 s:piano release:0.1 pan:0.6481481481481481 ]", + "[ (19/2 → 10/1) ⇝ 23/2 | gain:0.25 clip:1 note:F6 s:piano release:0.1 pan:0.662037037037037 ]", + "[ (19/2 → 10/1) ⇝ 23/2 | gain:0.25 clip:1 note:C7 s:piano release:0.1 pan:0.6944444444444444 ]", + "[ 29/3 → 10/1 | clip:1 gain:0.5790943073464692 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", + "[ 29/3 → 10/1 | clip:1 gain:0.5381966011250106 note:E6 s:piano release:0.1 pan:0.6574074074074074 ]", + "[ (29/3 → 10/1) ⇝ 31/3 | gain:0.3333333333333333 clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", + "[ (59/6 → 10/1) ⇝ 61/6 | clip:1 gain:0.5790943073464692 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", + "[ (59/6 → 10/1) ⇝ 61/6 | clip:1 gain:0.5381966011250106 note:E6 s:piano release:0.1 pan:0.6574074074074074 ]", + "[ (59/6 → 10/1) ⇝ 21/2 | gain:0.5 clip:1 note:D3 s:piano release:0.1 pan:0.4814814814814815 ]", + "[ 19/2 ⇜ (10/1 → 61/6) | gain:0.25 clip:1 note:G5 s:piano release:0.1 pan:0.6157407407407407 ]", + "[ 59/6 ⇜ (10/1 → 61/6) | clip:1 gain:0.5790943073464692 note:D7 s:piano release:0.1 pan:0.7037037037037037 ]", + "[ 59/6 ⇜ (10/1 → 61/6) | clip:1 gain:0.5381966011250106 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", + "[ 29/3 ⇜ (10/1 → 31/3) | gain:0.3333333333333333 clip:1 note:G4 s:piano release:0.1 pan:0.5601851851851851 ]", + "[ 10/1 → 31/3 | clip:1 gain:0.6209056926535306 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", + "[ 10/1 → 31/3 | clip:1 gain:0.5790943073464692 note:D7 s:piano release:0.1 pan:0.7037037037037037 ]", + "[ 17/2 ⇜ (10/1 → 21/2) | gain:0.5 clip:1 note:G4 s:piano release:0.1 pan:0.5601851851851851 ]", + "[ 17/2 ⇜ (10/1 → 21/2) | gain:0.5 clip:1 note:B4 s:piano release:0.1 pan:0.5787037037037037 ]", + "[ 17/2 ⇜ (10/1 → 21/2) | gain:0.5 clip:1 note:F5 s:piano release:0.1 pan:0.6064814814814814 ]", + "[ 19/2 ⇜ (10/1 → 21/2) | gain:0.5 clip:1 note:G5 s:piano release:0.1 pan:0.6157407407407407 ]", + "[ 19/2 ⇜ (10/1 → 21/2) | gain:0.25 clip:1 note:A7 s:piano release:0.1 pan:0.7361111111111112 ]", + "[ 59/6 ⇜ (10/1 → 21/2) | gain:0.5 clip:1 note:G3 s:piano release:0.1 pan:0.5046296296296297 ]", + "[ 10/1 → 32/3 | gain:1 clip:1 note:G2 s:piano release:0.1 pan:0.44907407407407407 ]", + "[ 9/1 ⇜ (10/1 → 11/1) | gain:0.3333333333333333 clip:1 note:G5 s:piano release:0.1 pan:0.6157407407407407 ]", + "[ 9/1 ⇜ (10/1 → 11/1) | gain:0.3333333333333333 clip:1 note:B5 s:piano release:0.1 pan:0.6342592592592593 ]", + "[ 9/1 ⇜ (10/1 → 11/1) | gain:0.3333333333333333 clip:1 note:F6 s:piano release:0.1 pan:0.662037037037037 ]", + "[ 19/2 ⇜ (10/1 → 11/1) ⇝ 23/2 | gain:0.25 clip:1 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", + "[ 19/2 ⇜ (10/1 → 11/1) ⇝ 23/2 | gain:0.25 clip:1 note:B6 s:piano release:0.1 pan:0.6898148148148149 ]", + "[ 19/2 ⇜ (10/1 → 11/1) ⇝ 23/2 | gain:0.25 clip:1 note:F7 s:piano release:0.1 pan:0.7175925925925926 ]", + "[ 10/1 → 11/1 | gain:1 clip:1 note:A4 s:piano release:0.1 pan:0.5694444444444444 ]", + "[ 10/1 → 11/1 | gain:0.3333333333333333 clip:1 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", + "[ (10/1 → 11/1) ⇝ 12/1 | gain:1 clip:1 note:G3 s:piano release:0.1 pan:0.5046296296296297 ]", + "[ (10/1 → 11/1) ⇝ 12/1 | gain:1 clip:1 note:B3 s:piano release:0.1 pan:0.5231481481481481 ]", + "[ (10/1 → 11/1) ⇝ 12/1 | gain:1 clip:1 note:F4 s:piano release:0.1 pan:0.5509259259259259 ]", + "[ 61/6 → 21/2 | clip:1 gain:0.6209056926535306 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", + "[ 61/6 → 21/2 | clip:1 gain:0.5790943073464692 note:D7 s:piano release:0.1 pan:0.7037037037037037 ]", + "[ 61/6 → 65/6 | gain:0.25 clip:1 note:G5 s:piano release:0.1 pan:0.6157407407407407 ]", + "[ 31/3 → 32/3 | clip:1 gain:0.6618033988749894 note:D7 s:piano release:0.1 pan:0.7037037037037037 ]", + "[ 31/3 → 32/3 | clip:1 gain:0.6209056926535306 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", + "[ 31/3 → 11/1 | gain:0.3333333333333333 clip:1 note:G4 s:piano release:0.1 pan:0.5601851851851851 ]", + "[ 21/2 → 65/6 | clip:1 gain:0.6618033988749894 note:D7 s:piano release:0.1 pan:0.7037037037037037 ]", + "[ 21/2 → 65/6 | clip:1 gain:0.6209056926535306 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", + "[ (21/2 → 11/1) ⇝ 67/6 | gain:0.5 clip:1 note:G3 s:piano release:0.1 pan:0.5046296296296297 ]", + "[ (21/2 → 11/1) ⇝ 23/2 | gain:0.5 clip:1 note:A5 s:piano release:0.1 pan:0.625 ]", + "[ (21/2 → 11/1) ⇝ 23/2 | gain:0.25 clip:1 note:G7 s:piano release:0.1 pan:0.7268518518518519 ]", + "[ (21/2 → 11/1) ⇝ 25/2 | gain:0.5 clip:1 note:G4 s:piano release:0.1 pan:0.5601851851851851 ]", + "[ (21/2 → 11/1) ⇝ 25/2 | gain:0.5 clip:1 note:B4 s:piano release:0.1 pan:0.5787037037037037 ]", + "[ (21/2 → 11/1) ⇝ 25/2 | gain:0.5 clip:1 note:F5 s:piano release:0.1 pan:0.6064814814814814 ]", + "[ 32/3 → 11/1 | clip:1 gain:0.7 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", + "[ 32/3 → 11/1 | clip:1 gain:0.6618033988749894 note:D7 s:piano release:0.1 pan:0.7037037037037037 ]", + "[ (32/3 → 11/1) ⇝ 34/3 | gain:1 clip:1 note:G2 s:piano release:0.1 pan:0.44907407407407407 ]", + "[ (65/6 → 11/1) ⇝ 67/6 | clip:1 gain:0.7 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", + "[ (65/6 → 11/1) ⇝ 67/6 | clip:1 gain:0.6618033988749894 note:D7 s:piano release:0.1 pan:0.7037037037037037 ]", + "[ (65/6 → 11/1) ⇝ 23/2 | gain:0.25 clip:1 note:G5 s:piano release:0.1 pan:0.6157407407407407 ]", + "[ 21/2 ⇜ (11/1 → 67/6) | gain:0.5 clip:1 note:G3 s:piano release:0.1 pan:0.5046296296296297 ]", + "[ 65/6 ⇜ (11/1 → 67/6) | clip:1 gain:0.7 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", + "[ 65/6 ⇜ (11/1 → 67/6) | clip:1 gain:0.6618033988749894 note:D7 s:piano release:0.1 pan:0.7037037037037037 ]", + "[ 32/3 ⇜ (11/1 → 34/3) | gain:1 clip:1 note:G2 s:piano release:0.1 pan:0.44907407407407407 ]", + "[ 11/1 → 34/3 | clip:1 gain:0.7338261212717717 note:D7 s:piano release:0.1 pan:0.7037037037037037 ]", + "[ 11/1 → 34/3 | clip:1 gain:0.7 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", + "[ 19/2 ⇜ (11/1 → 23/2) | gain:0.25 clip:1 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", + "[ 19/2 ⇜ (11/1 → 23/2) | gain:0.25 clip:1 note:B6 s:piano release:0.1 pan:0.6898148148148149 ]", + "[ 19/2 ⇜ (11/1 → 23/2) | gain:0.25 clip:1 note:F7 s:piano release:0.1 pan:0.7175925925925926 ]", + "[ 21/2 ⇜ (11/1 → 23/2) | gain:0.5 clip:1 note:A5 s:piano release:0.1 pan:0.625 ]", + "[ 21/2 ⇜ (11/1 → 23/2) | gain:0.25 clip:1 note:G7 s:piano release:0.1 pan:0.7268518518518519 ]", + "[ 65/6 ⇜ (11/1 → 23/2) | gain:0.25 clip:1 note:G5 s:piano release:0.1 pan:0.6157407407407407 ]", + "[ 11/1 → 35/3 | gain:0.3333333333333333 clip:1 note:G4 s:piano release:0.1 pan:0.5601851851851851 ]", + "[ 10/1 ⇜ (11/1 → 12/1) | gain:1 clip:1 note:G3 s:piano release:0.1 pan:0.5046296296296297 ]", + "[ 10/1 ⇜ (11/1 → 12/1) | gain:1 clip:1 note:B3 s:piano release:0.1 pan:0.5231481481481481 ]", + "[ 10/1 ⇜ (11/1 → 12/1) | gain:1 clip:1 note:F4 s:piano release:0.1 pan:0.5509259259259259 ]", + "[ 21/2 ⇜ (11/1 → 12/1) ⇝ 25/2 | gain:0.5 clip:1 note:G4 s:piano release:0.1 pan:0.5601851851851851 ]", + "[ 21/2 ⇜ (11/1 → 12/1) ⇝ 25/2 | gain:0.5 clip:1 note:B4 s:piano release:0.1 pan:0.5787037037037037 ]", + "[ 21/2 ⇜ (11/1 → 12/1) ⇝ 25/2 | gain:0.5 clip:1 note:F5 s:piano release:0.1 pan:0.6064814814814814 ]", + "[ 11/1 → 12/1 | gain:1 clip:1 note:G4 s:piano release:0.1 pan:0.5601851851851851 ]", + "[ 11/1 → 12/1 | gain:0.3333333333333333 clip:1 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", + "[ (11/1 → 12/1) ⇝ 13/1 | gain:0.3333333333333333 clip:1 note:G5 s:piano release:0.1 pan:0.6157407407407407 ]", + "[ (11/1 → 12/1) ⇝ 13/1 | gain:0.3333333333333333 clip:1 note:B5 s:piano release:0.1 pan:0.6342592592592593 ]", + "[ (11/1 → 12/1) ⇝ 13/1 | gain:0.3333333333333333 clip:1 note:F6 s:piano release:0.1 pan:0.662037037037037 ]", + "[ 67/6 → 23/2 | clip:1 gain:0.7338261212717717 note:D7 s:piano release:0.1 pan:0.7037037037037037 ]", + "[ 67/6 → 23/2 | clip:1 gain:0.7 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", + "[ 67/6 → 71/6 | gain:0.5 clip:1 note:G3 s:piano release:0.1 pan:0.5046296296296297 ]", + "[ 34/3 → 35/3 | clip:1 gain:0.7618033988749894 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", + "[ 34/3 → 35/3 | clip:1 gain:0.7338261212717717 note:D7 s:piano release:0.1 pan:0.7037037037037037 ]", + "[ 34/3 → 12/1 | gain:1 clip:1 note:G2 s:piano release:0.1 pan:0.44907407407407407 ]", + "[ 23/2 → 71/6 | clip:1 gain:0.7618033988749894 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", + "[ 23/2 → 71/6 | clip:1 gain:0.7338261212717717 note:D7 s:piano release:0.1 pan:0.7037037037037037 ]", + "[ (23/2 → 12/1) ⇝ 73/6 | gain:0.25 clip:1 note:G5 s:piano release:0.1 pan:0.6157407407407407 ]", + "[ (23/2 → 12/1) ⇝ 25/2 | gain:0.5 clip:1 note:G5 s:piano release:0.1 pan:0.6157407407407407 ]", + "[ (23/2 → 12/1) ⇝ 25/2 | gain:0.25 clip:1 note:A7 s:piano release:0.1 pan:0.7361111111111112 ]", + "[ (23/2 → 12/1) ⇝ 27/2 | gain:0.25 clip:1 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", + "[ (23/2 → 12/1) ⇝ 27/2 | gain:0.25 clip:1 note:B6 s:piano release:0.1 pan:0.6898148148148149 ]", + "[ (23/2 → 12/1) ⇝ 27/2 | gain:0.25 clip:1 note:F7 s:piano release:0.1 pan:0.7175925925925926 ]", + "[ 35/3 → 12/1 | clip:1 gain:0.7827090915285202 note:D7 s:piano release:0.1 pan:0.7037037037037037 ]", + "[ 35/3 → 12/1 | clip:1 gain:0.7618033988749894 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", + "[ (35/3 → 12/1) ⇝ 37/3 | gain:0.3333333333333333 clip:1 note:G4 s:piano release:0.1 pan:0.5601851851851851 ]", + "[ (71/6 → 12/1) ⇝ 73/6 | clip:1 gain:0.7827090915285202 note:D7 s:piano release:0.1 pan:0.7037037037037037 ]", + "[ (71/6 → 12/1) ⇝ 73/6 | clip:1 gain:0.7618033988749894 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", + "[ (71/6 → 12/1) ⇝ 25/2 | gain:0.5 clip:1 note:G3 s:piano release:0.1 pan:0.5046296296296297 ]", + "[ 23/2 ⇜ (12/1 → 73/6) | gain:0.25 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", + "[ 71/6 ⇜ (12/1 → 73/6) | clip:1 gain:0.7827090915285202 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", + "[ 71/6 ⇜ (12/1 → 73/6) | clip:1 gain:0.7618033988749894 note:D6 s:piano release:0.1 pan:0.6481481481481481 ]", + "[ 35/3 ⇜ (12/1 → 37/3) | gain:0.3333333333333333 clip:1 note:C4 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ 12/1 → 37/3 | clip:1 gain:0.7956295201467611 note:D6 s:piano release:0.1 pan:0.6481481481481481 ]", + "[ 12/1 → 37/3 | clip:1 gain:0.7827090915285202 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", + "[ 21/2 ⇜ (12/1 → 25/2) | gain:0.5 clip:1 note:C4 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ 21/2 ⇜ (12/1 → 25/2) | gain:0.5 clip:1 note:Eb4 s:piano release:0.1 pan:0.5416666666666667 ]", + "[ 21/2 ⇜ (12/1 → 25/2) | gain:0.5 clip:1 note:Bb4 s:piano release:0.1 pan:0.5740740740740741 ]", + "[ 23/2 ⇜ (12/1 → 25/2) | gain:0.5 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", + "[ 23/2 ⇜ (12/1 → 25/2) | gain:0.25 clip:1 note:D7 s:piano release:0.1 pan:0.7037037037037037 ]", + "[ 71/6 ⇜ (12/1 → 25/2) | gain:0.5 clip:1 note:C3 s:piano release:0.1 pan:0.4722222222222222 ]", + "[ 12/1 → 38/3 | gain:1 clip:1 note:C2 s:piano release:0.1 pan:0.41666666666666663 ]", + "[ 11/1 ⇜ (12/1 → 13/1) | gain:0.3333333333333333 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", + "[ 11/1 ⇜ (12/1 → 13/1) | gain:0.3333333333333333 clip:1 note:Eb5 s:piano release:0.1 pan:0.5972222222222222 ]", + "[ 11/1 ⇜ (12/1 → 13/1) | gain:0.3333333333333333 clip:1 note:Bb5 s:piano release:0.1 pan:0.6296296296296297 ]", + "[ 23/2 ⇜ (12/1 → 13/1) ⇝ 27/2 | gain:0.25 clip:1 note:C6 s:piano release:0.1 pan:0.6388888888888888 ]", + "[ 23/2 ⇜ (12/1 → 13/1) ⇝ 27/2 | gain:0.25 clip:1 note:Eb6 s:piano release:0.1 pan:0.6527777777777778 ]", + "[ 23/2 ⇜ (12/1 → 13/1) ⇝ 27/2 | gain:0.25 clip:1 note:Bb6 s:piano release:0.1 pan:0.6851851851851851 ]", + "[ 12/1 → 13/1 | gain:1 clip:1 note:D4 s:piano release:0.1 pan:0.537037037037037 ]", + "[ 12/1 → 13/1 | gain:0.3333333333333333 clip:1 note:C6 s:piano release:0.1 pan:0.6388888888888888 ]", + "[ (12/1 → 13/1) ⇝ 14/1 | gain:1 clip:1 note:C3 s:piano release:0.1 pan:0.4722222222222222 ]", + "[ (12/1 → 13/1) ⇝ 14/1 | gain:1 clip:1 note:Eb3 s:piano release:0.1 pan:0.4861111111111111 ]", + "[ (12/1 → 13/1) ⇝ 14/1 | gain:1 clip:1 note:Bb3 s:piano release:0.1 pan:0.5185185185185186 ]", + "[ 73/6 → 25/2 | clip:1 gain:0.7956295201467611 note:D6 s:piano release:0.1 pan:0.6481481481481481 ]", + "[ 73/6 → 25/2 | clip:1 gain:0.7827090915285202 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", + "[ 73/6 → 77/6 | gain:0.25 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", + "[ 37/3 → 38/3 | clip:1 gain:0.8 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", + "[ 37/3 → 38/3 | clip:1 gain:0.7956295201467611 note:D6 s:piano release:0.1 pan:0.6481481481481481 ]", + "[ 37/3 → 13/1 | gain:0.3333333333333333 clip:1 note:C4 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ 25/2 → 77/6 | clip:1 gain:0.8 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", + "[ 25/2 → 77/6 | clip:1 gain:0.7956295201467611 note:D6 s:piano release:0.1 pan:0.6481481481481481 ]", + "[ (25/2 → 13/1) ⇝ 79/6 | gain:0.5 clip:1 note:C3 s:piano release:0.1 pan:0.4722222222222222 ]", + "[ (25/2 → 13/1) ⇝ 27/2 | gain:0.5 clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", + "[ (25/2 → 13/1) ⇝ 27/2 | gain:0.25 clip:1 note:C7 s:piano release:0.1 pan:0.6944444444444444 ]", + "[ (25/2 → 13/1) ⇝ 29/2 | gain:0.5 clip:1 note:C4 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ (25/2 → 13/1) ⇝ 29/2 | gain:0.5 clip:1 note:Eb4 s:piano release:0.1 pan:0.5416666666666667 ]", + "[ (25/2 → 13/1) ⇝ 29/2 | gain:0.5 clip:1 note:Bb4 s:piano release:0.1 pan:0.5740740740740741 ]", + "[ 38/3 → 13/1 | clip:1 gain:0.7956295201467611 note:D6 s:piano release:0.1 pan:0.6481481481481481 ]", + "[ 38/3 → 13/1 | clip:1 gain:0.8 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", + "[ (38/3 → 13/1) ⇝ 40/3 | gain:1 clip:1 note:C2 s:piano release:0.1 pan:0.41666666666666663 ]", + "[ (77/6 → 13/1) ⇝ 79/6 | clip:1 gain:0.7956295201467611 note:D6 s:piano release:0.1 pan:0.6481481481481481 ]", + "[ (77/6 → 13/1) ⇝ 79/6 | clip:1 gain:0.8 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", + "[ (77/6 → 13/1) ⇝ 27/2 | gain:0.25 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", + "[ 25/2 ⇜ (13/1 → 79/6) | gain:0.5 clip:1 note:C3 s:piano release:0.1 pan:0.4722222222222222 ]", + "[ 77/6 ⇜ (13/1 → 79/6) | clip:1 gain:0.7956295201467611 note:D6 s:piano release:0.1 pan:0.6481481481481481 ]", + "[ 77/6 ⇜ (13/1 → 79/6) | clip:1 gain:0.8 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", + "[ 38/3 ⇜ (13/1 → 40/3) | gain:1 clip:1 note:C2 s:piano release:0.1 pan:0.41666666666666663 ]", + "[ 13/1 → 40/3 | clip:1 gain:0.7827090915285202 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", + "[ 13/1 → 40/3 | clip:1 gain:0.7956295201467611 note:D6 s:piano release:0.1 pan:0.6481481481481481 ]", + "[ 23/2 ⇜ (13/1 → 27/2) | gain:0.25 clip:1 note:C6 s:piano release:0.1 pan:0.6388888888888888 ]", + "[ 23/2 ⇜ (13/1 → 27/2) | gain:0.25 clip:1 note:Eb6 s:piano release:0.1 pan:0.6527777777777778 ]", + "[ 23/2 ⇜ (13/1 → 27/2) | gain:0.25 clip:1 note:Bb6 s:piano release:0.1 pan:0.6851851851851851 ]", + "[ 25/2 ⇜ (13/1 → 27/2) | gain:0.5 clip:1 note:D5 s:piano release:0.1 pan:0.5925925925925926 ]", + "[ 25/2 ⇜ (13/1 → 27/2) | gain:0.25 clip:1 note:C7 s:piano release:0.1 pan:0.6944444444444444 ]", + "[ 77/6 ⇜ (13/1 → 27/2) | gain:0.25 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", + "[ 13/1 → 41/3 | gain:0.3333333333333333 clip:1 note:C4 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ 12/1 ⇜ (13/1 → 14/1) | gain:1 clip:1 note:C3 s:piano release:0.1 pan:0.4722222222222222 ]", + "[ 12/1 ⇜ (13/1 → 14/1) | gain:1 clip:1 note:Eb3 s:piano release:0.1 pan:0.4861111111111111 ]", + "[ 12/1 ⇜ (13/1 → 14/1) | gain:1 clip:1 note:Bb3 s:piano release:0.1 pan:0.5185185185185186 ]", + "[ 25/2 ⇜ (13/1 → 14/1) ⇝ 29/2 | gain:0.5 clip:1 note:C4 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ 25/2 ⇜ (13/1 → 14/1) ⇝ 29/2 | gain:0.5 clip:1 note:Eb4 s:piano release:0.1 pan:0.5416666666666667 ]", + "[ 25/2 ⇜ (13/1 → 14/1) ⇝ 29/2 | gain:0.5 clip:1 note:Bb4 s:piano release:0.1 pan:0.5740740740740741 ]", + "[ 13/1 → 14/1 | gain:1 clip:1 note:C4 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ 13/1 → 14/1 | gain:0.3333333333333333 clip:1 note:D6 s:piano release:0.1 pan:0.6481481481481481 ]", + "[ (13/1 → 14/1) ⇝ 15/1 | gain:0.3333333333333333 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", + "[ (13/1 → 14/1) ⇝ 15/1 | gain:0.3333333333333333 clip:1 note:Eb5 s:piano release:0.1 pan:0.5972222222222222 ]", + "[ (13/1 → 14/1) ⇝ 15/1 | gain:0.3333333333333333 clip:1 note:Bb5 s:piano release:0.1 pan:0.6296296296296297 ]", + "[ 79/6 → 27/2 | clip:1 gain:0.7827090915285202 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", + "[ 79/6 → 27/2 | clip:1 gain:0.7956295201467611 note:D6 s:piano release:0.1 pan:0.6481481481481481 ]", + "[ 79/6 → 83/6 | gain:0.5 clip:1 note:C3 s:piano release:0.1 pan:0.4722222222222222 ]", + "[ 40/3 → 41/3 | clip:1 gain:0.7618033988749895 note:D6 s:piano release:0.1 pan:0.6481481481481481 ]", + "[ 40/3 → 41/3 | clip:1 gain:0.7827090915285202 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", + "[ 40/3 → 14/1 | gain:1 clip:1 note:C2 s:piano release:0.1 pan:0.41666666666666663 ]", + "[ 27/2 → 83/6 | clip:1 gain:0.7618033988749895 note:D6 s:piano release:0.1 pan:0.6481481481481481 ]", + "[ 27/2 → 83/6 | clip:1 gain:0.7827090915285202 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", + "[ (27/2 → 14/1) ⇝ 85/6 | gain:0.25 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", + "[ (27/2 → 14/1) ⇝ 29/2 | gain:0.5 clip:1 note:C5 s:piano release:0.1 pan:0.5833333333333333 ]", + "[ (27/2 → 14/1) ⇝ 29/2 | gain:0.25 clip:1 note:D7 s:piano release:0.1 pan:0.7037037037037037 ]", + "[ (27/2 → 14/1) ⇝ 31/2 | gain:0.25 clip:1 note:C6 s:piano release:0.1 pan:0.6388888888888888 ]", + "[ (27/2 → 14/1) ⇝ 31/2 | gain:0.25 clip:1 note:Eb6 s:piano release:0.1 pan:0.6527777777777778 ]", + "[ (27/2 → 14/1) ⇝ 31/2 | gain:0.25 clip:1 note:Bb6 s:piano release:0.1 pan:0.6851851851851851 ]", + "[ 41/3 → 14/1 | clip:1 gain:0.7338261212717718 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", + "[ 41/3 → 14/1 | clip:1 gain:0.7618033988749895 note:D6 s:piano release:0.1 pan:0.6481481481481481 ]", + "[ (41/3 → 14/1) ⇝ 43/3 | gain:0.3333333333333333 clip:1 note:C4 s:piano release:0.1 pan:0.5277777777777778 ]", + "[ (83/6 → 14/1) ⇝ 85/6 | clip:1 gain:0.7338261212717718 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", + "[ (83/6 → 14/1) ⇝ 85/6 | clip:1 gain:0.7618033988749895 note:D6 s:piano release:0.1 pan:0.6481481481481481 ]", + "[ (83/6 → 14/1) ⇝ 29/2 | gain:0.5 clip:1 note:C3 s:piano release:0.1 pan:0.4722222222222222 ]", + "[ 27/2 ⇜ (14/1 → 85/6) | gain:0.25 clip:1 note:F5 s:piano release:0.1 pan:0.6064814814814814 ]", + "[ 83/6 ⇜ (14/1 → 85/6) | clip:1 gain:0.7338261212717718 note:C7 s:piano release:0.1 pan:0.6944444444444444 ]", + "[ 83/6 ⇜ (14/1 → 85/6) | clip:1 gain:0.7618033988749895 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", + "[ 41/3 ⇜ (14/1 → 43/3) | gain:0.3333333333333333 clip:1 note:F4 s:piano release:0.1 pan:0.5509259259259259 ]", + "[ 14/1 → 43/3 | clip:1 gain:0.7 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", + "[ 14/1 → 43/3 | clip:1 gain:0.7338261212717718 note:C7 s:piano release:0.1 pan:0.6944444444444444 ]", + "[ 25/2 ⇜ (14/1 → 29/2) | gain:0.5 clip:1 note:F4 s:piano release:0.1 pan:0.5509259259259259 ]", + "[ 25/2 ⇜ (14/1 → 29/2) | gain:0.5 clip:1 note:A4 s:piano release:0.1 pan:0.5694444444444444 ]", + "[ 25/2 ⇜ (14/1 → 29/2) | gain:0.5 clip:1 note:Eb5 s:piano release:0.1 pan:0.5972222222222222 ]", + "[ 27/2 ⇜ (14/1 → 29/2) | gain:0.5 clip:1 note:F5 s:piano release:0.1 pan:0.6064814814814814 ]", + "[ 27/2 ⇜ (14/1 → 29/2) | gain:0.25 clip:1 note:G7 s:piano release:0.1 pan:0.7268518518518519 ]", + "[ 83/6 ⇜ (14/1 → 29/2) | gain:0.5 clip:1 note:F3 s:piano release:0.1 pan:0.49537037037037035 ]", + "[ 14/1 → 44/3 | gain:1 clip:1 note:F2 s:piano release:0.1 pan:0.4398148148148148 ]", + "[ 13/1 ⇜ (14/1 → 15/1) | gain:0.3333333333333333 clip:1 note:F5 s:piano release:0.1 pan:0.6064814814814814 ]", + "[ 13/1 ⇜ (14/1 → 15/1) | gain:0.3333333333333333 clip:1 note:A5 s:piano release:0.1 pan:0.625 ]", + "[ 13/1 ⇜ (14/1 → 15/1) | gain:0.3333333333333333 clip:1 note:Eb6 s:piano release:0.1 pan:0.6527777777777778 ]", + "[ 27/2 ⇜ (14/1 → 15/1) ⇝ 31/2 | gain:0.25 clip:1 note:F6 s:piano release:0.1 pan:0.662037037037037 ]", + "[ 27/2 ⇜ (14/1 → 15/1) ⇝ 31/2 | gain:0.25 clip:1 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", + "[ 27/2 ⇜ (14/1 → 15/1) ⇝ 31/2 | gain:0.25 clip:1 note:Eb7 s:piano release:0.1 pan:0.7083333333333333 ]", + "[ 14/1 → 15/1 | gain:1 clip:1 note:G4 s:piano release:0.1 pan:0.5601851851851851 ]", + "[ 14/1 → 15/1 | gain:0.3333333333333333 clip:1 note:F6 s:piano release:0.1 pan:0.662037037037037 ]", + "[ (14/1 → 15/1) ⇝ 16/1 | gain:1 clip:1 note:F3 s:piano release:0.1 pan:0.49537037037037035 ]", + "[ (14/1 → 15/1) ⇝ 16/1 | gain:1 clip:1 note:A3 s:piano release:0.1 pan:0.5138888888888888 ]", + "[ (14/1 → 15/1) ⇝ 16/1 | gain:1 clip:1 note:Eb4 s:piano release:0.1 pan:0.5416666666666667 ]", + "[ 85/6 → 29/2 | clip:1 gain:0.7 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", + "[ 85/6 → 29/2 | clip:1 gain:0.7338261212717718 note:C7 s:piano release:0.1 pan:0.6944444444444444 ]", + "[ 85/6 → 89/6 | gain:0.25 clip:1 note:F5 s:piano release:0.1 pan:0.6064814814814814 ]", + "[ 43/3 → 44/3 | clip:1 gain:0.6618033988749896 note:C7 s:piano release:0.1 pan:0.6944444444444444 ]", + "[ 43/3 → 44/3 | clip:1 gain:0.7 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", + "[ 43/3 → 15/1 | gain:0.3333333333333333 clip:1 note:F4 s:piano release:0.1 pan:0.5509259259259259 ]", + "[ 29/2 → 89/6 | clip:1 gain:0.6618033988749896 note:C7 s:piano release:0.1 pan:0.6944444444444444 ]", + "[ 29/2 → 89/6 | clip:1 gain:0.7 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", + "[ (29/2 → 15/1) ⇝ 91/6 | gain:0.5 clip:1 note:F3 s:piano release:0.1 pan:0.49537037037037035 ]", + "[ (29/2 → 15/1) ⇝ 31/2 | gain:0.5 clip:1 note:G5 s:piano release:0.1 pan:0.6157407407407407 ]", + "[ (29/2 → 15/1) ⇝ 31/2 | gain:0.25 clip:1 note:F7 s:piano release:0.1 pan:0.7175925925925926 ]", + "[ (29/2 → 15/1) ⇝ 33/2 | gain:0.5 clip:1 note:F4 s:piano release:0.1 pan:0.5509259259259259 ]", + "[ (29/2 → 15/1) ⇝ 33/2 | gain:0.5 clip:1 note:A4 s:piano release:0.1 pan:0.5694444444444444 ]", + "[ (29/2 → 15/1) ⇝ 33/2 | gain:0.5 clip:1 note:Eb5 s:piano release:0.1 pan:0.5972222222222222 ]", + "[ 44/3 → 15/1 | clip:1 gain:0.6209056926535306 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", + "[ 44/3 → 15/1 | clip:1 gain:0.6618033988749896 note:C7 s:piano release:0.1 pan:0.6944444444444444 ]", + "[ (44/3 → 15/1) ⇝ 46/3 | gain:1 clip:1 note:F2 s:piano release:0.1 pan:0.4398148148148148 ]", + "[ (89/6 → 15/1) ⇝ 91/6 | clip:1 gain:0.6209056926535306 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", + "[ (89/6 → 15/1) ⇝ 91/6 | clip:1 gain:0.6618033988749896 note:C7 s:piano release:0.1 pan:0.6944444444444444 ]", + "[ (89/6 → 15/1) ⇝ 31/2 | gain:0.25 clip:1 note:F5 s:piano release:0.1 pan:0.6064814814814814 ]", + "[ 29/2 ⇜ (15/1 → 91/6) | gain:0.5 clip:1 note:F3 s:piano release:0.1 pan:0.49537037037037035 ]", + "[ 89/6 ⇜ (15/1 → 91/6) | clip:1 gain:0.6209056926535306 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", + "[ 89/6 ⇜ (15/1 → 91/6) | clip:1 gain:0.6618033988749896 note:C7 s:piano release:0.1 pan:0.6944444444444444 ]", + "[ 44/3 ⇜ (15/1 → 46/3) | gain:1 clip:1 note:F2 s:piano release:0.1 pan:0.4398148148148148 ]", + "[ 15/1 → 46/3 | clip:1 gain:0.5790943073464696 note:C7 s:piano release:0.1 pan:0.6944444444444444 ]", + "[ 15/1 → 46/3 | clip:1 gain:0.6209056926535306 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", + "[ 27/2 ⇜ (15/1 → 31/2) | gain:0.25 clip:1 note:F6 s:piano release:0.1 pan:0.662037037037037 ]", + "[ 27/2 ⇜ (15/1 → 31/2) | gain:0.25 clip:1 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", + "[ 27/2 ⇜ (15/1 → 31/2) | gain:0.25 clip:1 note:Eb7 s:piano release:0.1 pan:0.7083333333333333 ]", + "[ 29/2 ⇜ (15/1 → 31/2) | gain:0.5 clip:1 note:G5 s:piano release:0.1 pan:0.6157407407407407 ]", + "[ 29/2 ⇜ (15/1 → 31/2) | gain:0.25 clip:1 note:F7 s:piano release:0.1 pan:0.7175925925925926 ]", + "[ 89/6 ⇜ (15/1 → 31/2) | gain:0.25 clip:1 note:F5 s:piano release:0.1 pan:0.6064814814814814 ]", + "[ 15/1 → 47/3 | gain:0.3333333333333333 clip:1 note:F4 s:piano release:0.1 pan:0.5509259259259259 ]", + "[ 14/1 ⇜ (15/1 → 16/1) | gain:1 clip:1 note:F3 s:piano release:0.1 pan:0.49537037037037035 ]", + "[ 14/1 ⇜ (15/1 → 16/1) | gain:1 clip:1 note:A3 s:piano release:0.1 pan:0.5138888888888888 ]", + "[ 14/1 ⇜ (15/1 → 16/1) | gain:1 clip:1 note:Eb4 s:piano release:0.1 pan:0.5416666666666667 ]", + "[ 29/2 ⇜ (15/1 → 16/1) ⇝ 33/2 | gain:0.5 clip:1 note:F4 s:piano release:0.1 pan:0.5509259259259259 ]", + "[ 29/2 ⇜ (15/1 → 16/1) ⇝ 33/2 | gain:0.5 clip:1 note:A4 s:piano release:0.1 pan:0.5694444444444444 ]", + "[ 29/2 ⇜ (15/1 → 16/1) ⇝ 33/2 | gain:0.5 clip:1 note:Eb5 s:piano release:0.1 pan:0.5972222222222222 ]", + "[ 15/1 → 16/1 | gain:1 clip:1 note:F4 s:piano release:0.1 pan:0.5509259259259259 ]", + "[ 15/1 → 16/1 | gain:0.3333333333333333 clip:1 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", + "[ (15/1 → 16/1) ⇝ 17/1 | gain:0.3333333333333333 clip:1 note:F5 s:piano release:0.1 pan:0.6064814814814814 ]", + "[ (15/1 → 16/1) ⇝ 17/1 | gain:0.3333333333333333 clip:1 note:A5 s:piano release:0.1 pan:0.625 ]", + "[ (15/1 → 16/1) ⇝ 17/1 | gain:0.3333333333333333 clip:1 note:Eb6 s:piano release:0.1 pan:0.6527777777777778 ]", + "[ 91/6 → 31/2 | clip:1 gain:0.5790943073464696 note:C7 s:piano release:0.1 pan:0.6944444444444444 ]", + "[ 91/6 → 31/2 | clip:1 gain:0.6209056926535306 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", + "[ 91/6 → 95/6 | gain:0.5 clip:1 note:F3 s:piano release:0.1 pan:0.49537037037037035 ]", + "[ 46/3 → 47/3 | clip:1 gain:0.5381966011250107 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", + "[ 46/3 → 47/3 | clip:1 gain:0.5790943073464696 note:C7 s:piano release:0.1 pan:0.6944444444444444 ]", + "[ 46/3 → 16/1 | gain:1 clip:1 note:F2 s:piano release:0.1 pan:0.4398148148148148 ]", + "[ 31/2 → 95/6 | clip:1 gain:0.5381966011250107 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", + "[ 31/2 → 95/6 | clip:1 gain:0.5790943073464696 note:C7 s:piano release:0.1 pan:0.6944444444444444 ]", + "[ (31/2 → 16/1) ⇝ 97/6 | gain:0.25 clip:1 note:F5 s:piano release:0.1 pan:0.6064814814814814 ]", + "[ (31/2 → 16/1) ⇝ 33/2 | gain:0.5 clip:1 note:F5 s:piano release:0.1 pan:0.6064814814814814 ]", + "[ (31/2 → 16/1) ⇝ 33/2 | gain:0.25 clip:1 note:G7 s:piano release:0.1 pan:0.7268518518518519 ]", + "[ (31/2 → 16/1) ⇝ 35/2 | gain:0.25 clip:1 note:F6 s:piano release:0.1 pan:0.662037037037037 ]", + "[ (31/2 → 16/1) ⇝ 35/2 | gain:0.25 clip:1 note:A6 s:piano release:0.1 pan:0.6805555555555556 ]", + "[ (31/2 → 16/1) ⇝ 35/2 | gain:0.25 clip:1 note:Eb7 s:piano release:0.1 pan:0.7083333333333333 ]", + "[ 47/3 → 16/1 | clip:1 gain:0.5000000000000002 note:C7 s:piano release:0.1 pan:0.6944444444444444 ]", + "[ 47/3 → 16/1 | clip:1 gain:0.5381966011250107 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", + "[ (47/3 → 16/1) ⇝ 49/3 | gain:0.3333333333333333 clip:1 note:F4 s:piano release:0.1 pan:0.5509259259259259 ]", + "[ (95/6 → 16/1) ⇝ 97/6 | clip:1 gain:0.5000000000000002 note:C7 s:piano release:0.1 pan:0.6944444444444444 ]", + "[ (95/6 → 16/1) ⇝ 97/6 | clip:1 gain:0.5381966011250107 note:G6 s:piano release:0.1 pan:0.6712962962962963 ]", + "[ (95/6 → 16/1) ⇝ 33/2 | gain:0.5 clip:1 note:F3 s:piano release:0.1 pan:0.49537037037037035 ]", ] `; @@ -7571,118 +7571,118 @@ exports[`renders tunes > tune: giantSteps 1`] = ` exports[`renders tunes > tune: goodTimes 1`] = ` [ - "[ 0/1 → 1/4 | n:0 note:52 clip:2 s:piano release:0.1 pan:0.4907407407407407 ]", - "[ 1/4 → 1/2 | n:0 note:40 clip:2 s:piano release:0.1 pan:0.4351851851851852 ]", - "[ 1/2 → 3/4 | n:0 note:52 clip:2 s:piano release:0.1 pan:0.4907407407407407 ]", - "[ 1/2 → 3/4 | n:2 note:55 clip:2 s:piano release:0.1 pan:0.5046296296296297 ]", - "[ 1/2 → 3/4 | n:4 note:59 clip:2 s:piano release:0.1 pan:0.5231481481481481 ]", - "[ 1/2 → 3/4 | n:0 note:40 clip:2 s:piano release:0.1 pan:0.4351851851851852 ]", - "[ 1/1 → 5/4 | n:2 note:55 clip:2 s:piano release:0.1 pan:0.5046296296296297 ]", - "[ 1/1 → 5/4 | n:4 note:59 clip:2 s:piano release:0.1 pan:0.5231481481481481 ]", - "[ 1/1 → 5/4 | n:0 note:40 clip:2 s:piano release:0.1 pan:0.4351851851851852 ]", - "[ 5/4 → 3/2 | n:0 note:52 clip:2 s:piano release:0.1 pan:0.4907407407407407 ]", - "[ 5/4 → 3/2 | n:0 note:40 clip:2 s:piano release:0.1 pan:0.4351851851851852 ]", - "[ 7/4 → 2/1 | n:2 note:55 clip:2 s:piano release:0.1 pan:0.5046296296296297 ]", - "[ 7/4 → 2/1 | n:4 note:59 clip:2 s:piano release:0.1 pan:0.5231481481481481 ]", - "[ 7/4 → 2/1 | n:0 note:40 clip:2 s:piano release:0.1 pan:0.4351851851851852 ]", - "[ 2/1 → 9/4 | n:1 note:54 clip:2 s:piano release:0.1 pan:0.5 ]", - "[ 9/4 → 5/2 | n:4 note:47 clip:2 s:piano release:0.1 pan:0.46759259259259256 ]", - "[ 5/2 → 11/4 | n:1 note:54 clip:2 s:piano release:0.1 pan:0.5 ]", - "[ 5/2 → 11/4 | n:3 note:57 clip:2 s:piano release:0.1 pan:0.5138888888888888 ]", - "[ 5/2 → 11/4 | n:5 note:61 clip:2 s:piano release:0.1 pan:0.5324074074074074 ]", - "[ 5/2 → 11/4 | n:4 note:47 clip:2 s:piano release:0.1 pan:0.46759259259259256 ]", - "[ 3/1 → 13/4 | n:3 note:57 clip:2 s:piano release:0.1 pan:0.5138888888888888 ]", - "[ 3/1 → 13/4 | n:5 note:61 clip:2 s:piano release:0.1 pan:0.5324074074074074 ]", - "[ 3/1 → 13/4 | n:4 note:47 clip:2 s:piano release:0.1 pan:0.46759259259259256 ]", - "[ 13/4 → 7/2 | n:1 note:54 clip:2 s:piano release:0.1 pan:0.5 ]", - "[ 13/4 → 7/2 | n:4 note:47 clip:2 s:piano release:0.1 pan:0.46759259259259256 ]", - "[ 15/4 → 4/1 | n:3 note:57 clip:2 s:piano release:0.1 pan:0.5138888888888888 ]", - "[ 15/4 → 4/1 | n:5 note:61 clip:2 s:piano release:0.1 pan:0.5324074074074074 ]", - "[ 15/4 → 4/1 | n:4 note:47 clip:2 s:piano release:0.1 pan:0.46759259259259256 ]", - "[ 4/1 → 17/4 | n:2 note:55 clip:2 s:piano release:0.1 pan:0.5046296296296297 ]", - "[ 17/4 → 9/2 | n:0 note:40 clip:2 s:piano release:0.1 pan:0.4351851851851852 ]", - "[ 9/2 → 19/4 | n:2 note:55 clip:2 s:piano release:0.1 pan:0.5046296296296297 ]", - "[ 9/2 → 19/4 | n:4 note:59 clip:2 s:piano release:0.1 pan:0.5231481481481481 ]", - "[ 9/2 → 19/4 | n:6 note:62 clip:2 s:piano release:0.1 pan:0.537037037037037 ]", - "[ 9/2 → 19/4 | n:0 note:40 clip:2 s:piano release:0.1 pan:0.4351851851851852 ]", - "[ 5/1 → 21/4 | n:4 note:59 clip:2 s:piano release:0.1 pan:0.5231481481481481 ]", - "[ 5/1 → 21/4 | n:6 note:62 clip:2 s:piano release:0.1 pan:0.537037037037037 ]", - "[ 5/1 → 21/4 | n:0 note:40 clip:2 s:piano release:0.1 pan:0.4351851851851852 ]", - "[ 21/4 → 11/2 | n:2 note:55 clip:2 s:piano release:0.1 pan:0.5046296296296297 ]", - "[ 21/4 → 11/2 | n:0 note:40 clip:2 s:piano release:0.1 pan:0.4351851851851852 ]", - "[ 23/4 → 6/1 | n:4 note:59 clip:2 s:piano release:0.1 pan:0.5231481481481481 ]", - "[ 23/4 → 6/1 | n:6 note:62 clip:2 s:piano release:0.1 pan:0.537037037037037 ]", - "[ 23/4 → 6/1 | n:0 note:40 clip:2 s:piano release:0.1 pan:0.4351851851851852 ]", - "[ 6/1 → 25/4 | n:3 note:57 clip:2 s:piano release:0.1 pan:0.5138888888888888 ]", - "[ 25/4 → 13/2 | n:4 note:47 clip:2 s:piano release:0.1 pan:0.46759259259259256 ]", - "[ 13/2 → 27/4 | n:3 note:57 clip:2 s:piano release:0.1 pan:0.5138888888888888 ]", - "[ 13/2 → 27/4 | n:5 note:61 clip:2 s:piano release:0.1 pan:0.5324074074074074 ]", - "[ 13/2 → 27/4 | n:7 note:64 clip:2 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 13/2 → 27/4 | n:4 note:47 clip:2 s:piano release:0.1 pan:0.46759259259259256 ]", - "[ 7/1 → 29/4 | n:5 note:61 clip:2 s:piano release:0.1 pan:0.5324074074074074 ]", - "[ 7/1 → 29/4 | n:7 note:64 clip:2 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 7/1 → 29/4 | n:4 note:47 clip:2 s:piano release:0.1 pan:0.46759259259259256 ]", - "[ 29/4 → 15/2 | n:3 note:57 clip:2 s:piano release:0.1 pan:0.5138888888888888 ]", - "[ 29/4 → 15/2 | n:4 note:47 clip:2 s:piano release:0.1 pan:0.46759259259259256 ]", - "[ 31/4 → 8/1 | n:5 note:61 clip:2 s:piano release:0.1 pan:0.5324074074074074 ]", - "[ 31/4 → 8/1 | n:7 note:64 clip:2 s:piano release:0.1 pan:0.5462962962962963 ]", - "[ 31/4 → 8/1 | n:4 note:47 clip:2 s:piano release:0.1 pan:0.46759259259259256 ]", - "[ 8/1 → 33/4 | n:0 note:50 clip:2 s:piano release:0.1 pan:0.4814814814814815 ]", - "[ 33/4 → 17/2 | n:0 note:38 clip:2 s:piano release:0.1 pan:0.42592592592592593 ]", - "[ 17/2 → 35/4 | n:0 note:50 clip:2 s:piano release:0.1 pan:0.4814814814814815 ]", - "[ 17/2 → 35/4 | n:2 note:54 clip:2 s:piano release:0.1 pan:0.5 ]", - "[ 17/2 → 35/4 | n:4 note:57 clip:2 s:piano release:0.1 pan:0.5138888888888888 ]", - "[ 17/2 → 35/4 | n:0 note:38 clip:2 s:piano release:0.1 pan:0.42592592592592593 ]", - "[ 9/1 → 37/4 | n:2 note:54 clip:2 s:piano release:0.1 pan:0.5 ]", - "[ 9/1 → 37/4 | n:4 note:57 clip:2 s:piano release:0.1 pan:0.5138888888888888 ]", - "[ 9/1 → 37/4 | n:0 note:38 clip:2 s:piano release:0.1 pan:0.42592592592592593 ]", - "[ 37/4 → 19/2 | n:0 note:50 clip:2 s:piano release:0.1 pan:0.4814814814814815 ]", - "[ 37/4 → 19/2 | n:0 note:38 clip:2 s:piano release:0.1 pan:0.42592592592592593 ]", - "[ 39/4 → 10/1 | n:2 note:54 clip:2 s:piano release:0.1 pan:0.5 ]", - "[ 39/4 → 10/1 | n:4 note:57 clip:2 s:piano release:0.1 pan:0.5138888888888888 ]", - "[ 39/4 → 10/1 | n:0 note:38 clip:2 s:piano release:0.1 pan:0.42592592592592593 ]", - "[ 10/1 → 41/4 | n:1 note:52 clip:2 s:piano release:0.1 pan:0.4907407407407407 ]", - "[ 41/4 → 21/2 | n:4 note:45 clip:2 s:piano release:0.1 pan:0.45833333333333337 ]", - "[ 21/2 → 43/4 | n:1 note:52 clip:2 s:piano release:0.1 pan:0.4907407407407407 ]", - "[ 21/2 → 43/4 | n:3 note:55 clip:2 s:piano release:0.1 pan:0.5046296296296297 ]", - "[ 21/2 → 43/4 | n:5 note:59 clip:2 s:piano release:0.1 pan:0.5231481481481481 ]", - "[ 21/2 → 43/4 | n:4 note:45 clip:2 s:piano release:0.1 pan:0.45833333333333337 ]", - "[ 11/1 → 45/4 | n:3 note:55 clip:2 s:piano release:0.1 pan:0.5046296296296297 ]", - "[ 11/1 → 45/4 | n:5 note:59 clip:2 s:piano release:0.1 pan:0.5231481481481481 ]", - "[ 11/1 → 45/4 | n:4 note:45 clip:2 s:piano release:0.1 pan:0.45833333333333337 ]", - "[ 45/4 → 23/2 | n:1 note:52 clip:2 s:piano release:0.1 pan:0.4907407407407407 ]", - "[ 45/4 → 23/2 | n:4 note:45 clip:2 s:piano release:0.1 pan:0.45833333333333337 ]", - "[ 47/4 → 12/1 | n:3 note:55 clip:2 s:piano release:0.1 pan:0.5046296296296297 ]", - "[ 47/4 → 12/1 | n:5 note:59 clip:2 s:piano release:0.1 pan:0.5231481481481481 ]", - "[ 47/4 → 12/1 | n:4 note:45 clip:2 s:piano release:0.1 pan:0.45833333333333337 ]", - "[ 12/1 → 49/4 | n:2 note:54 clip:2 s:piano release:0.1 pan:0.5 ]", - "[ 49/4 → 25/2 | n:0 note:38 clip:2 s:piano release:0.1 pan:0.42592592592592593 ]", - "[ 25/2 → 51/4 | n:2 note:54 clip:2 s:piano release:0.1 pan:0.5 ]", - "[ 25/2 → 51/4 | n:4 note:57 clip:2 s:piano release:0.1 pan:0.5138888888888888 ]", - "[ 25/2 → 51/4 | n:6 note:61 clip:2 s:piano release:0.1 pan:0.5324074074074074 ]", - "[ 25/2 → 51/4 | n:0 note:38 clip:2 s:piano release:0.1 pan:0.42592592592592593 ]", - "[ 13/1 → 53/4 | n:4 note:57 clip:2 s:piano release:0.1 pan:0.5138888888888888 ]", - "[ 13/1 → 53/4 | n:6 note:61 clip:2 s:piano release:0.1 pan:0.5324074074074074 ]", - "[ 13/1 → 53/4 | n:0 note:38 clip:2 s:piano release:0.1 pan:0.42592592592592593 ]", - "[ 53/4 → 27/2 | n:2 note:54 clip:2 s:piano release:0.1 pan:0.5 ]", - "[ 53/4 → 27/2 | n:0 note:38 clip:2 s:piano release:0.1 pan:0.42592592592592593 ]", - "[ 55/4 → 14/1 | n:4 note:57 clip:2 s:piano release:0.1 pan:0.5138888888888888 ]", - "[ 55/4 → 14/1 | n:6 note:61 clip:2 s:piano release:0.1 pan:0.5324074074074074 ]", - "[ 55/4 → 14/1 | n:0 note:38 clip:2 s:piano release:0.1 pan:0.42592592592592593 ]", - "[ 14/1 → 57/4 | n:3 note:55 clip:2 s:piano release:0.1 pan:0.5046296296296297 ]", - "[ 57/4 → 29/2 | n:4 note:45 clip:2 s:piano release:0.1 pan:0.45833333333333337 ]", - "[ 29/2 → 59/4 | n:3 note:55 clip:2 s:piano release:0.1 pan:0.5046296296296297 ]", - "[ 29/2 → 59/4 | n:5 note:59 clip:2 s:piano release:0.1 pan:0.5231481481481481 ]", - "[ 29/2 → 59/4 | n:7 note:62 clip:2 s:piano release:0.1 pan:0.537037037037037 ]", - "[ 29/2 → 59/4 | n:4 note:45 clip:2 s:piano release:0.1 pan:0.45833333333333337 ]", - "[ 15/1 → 61/4 | n:5 note:59 clip:2 s:piano release:0.1 pan:0.5231481481481481 ]", - "[ 15/1 → 61/4 | n:7 note:62 clip:2 s:piano release:0.1 pan:0.537037037037037 ]", - "[ 15/1 → 61/4 | n:4 note:45 clip:2 s:piano release:0.1 pan:0.45833333333333337 ]", - "[ 61/4 → 31/2 | n:3 note:55 clip:2 s:piano release:0.1 pan:0.5046296296296297 ]", - "[ 61/4 → 31/2 | n:4 note:45 clip:2 s:piano release:0.1 pan:0.45833333333333337 ]", - "[ 63/4 → 16/1 | n:5 note:59 clip:2 s:piano release:0.1 pan:0.5231481481481481 ]", - "[ 63/4 → 16/1 | n:7 note:62 clip:2 s:piano release:0.1 pan:0.537037037037037 ]", - "[ 63/4 → 16/1 | n:4 note:45 clip:2 s:piano release:0.1 pan:0.45833333333333337 ]", + "[ 0/1 → 1/4 | note:52 clip:2 s:piano release:0.1 pan:0.4907407407407407 ]", + "[ 1/4 → 1/2 | note:40 clip:2 s:piano release:0.1 pan:0.4351851851851852 ]", + "[ 1/2 → 3/4 | note:52 clip:2 s:piano release:0.1 pan:0.4907407407407407 ]", + "[ 1/2 → 3/4 | note:55 clip:2 s:piano release:0.1 pan:0.5046296296296297 ]", + "[ 1/2 → 3/4 | note:59 clip:2 s:piano release:0.1 pan:0.5231481481481481 ]", + "[ 1/2 → 3/4 | note:40 clip:2 s:piano release:0.1 pan:0.4351851851851852 ]", + "[ 1/1 → 5/4 | note:55 clip:2 s:piano release:0.1 pan:0.5046296296296297 ]", + "[ 1/1 → 5/4 | note:59 clip:2 s:piano release:0.1 pan:0.5231481481481481 ]", + "[ 1/1 → 5/4 | note:40 clip:2 s:piano release:0.1 pan:0.4351851851851852 ]", + "[ 5/4 → 3/2 | note:52 clip:2 s:piano release:0.1 pan:0.4907407407407407 ]", + "[ 5/4 → 3/2 | note:40 clip:2 s:piano release:0.1 pan:0.4351851851851852 ]", + "[ 7/4 → 2/1 | note:55 clip:2 s:piano release:0.1 pan:0.5046296296296297 ]", + "[ 7/4 → 2/1 | note:59 clip:2 s:piano release:0.1 pan:0.5231481481481481 ]", + "[ 7/4 → 2/1 | note:40 clip:2 s:piano release:0.1 pan:0.4351851851851852 ]", + "[ 2/1 → 9/4 | note:54 clip:2 s:piano release:0.1 pan:0.5 ]", + "[ 9/4 → 5/2 | note:47 clip:2 s:piano release:0.1 pan:0.46759259259259256 ]", + "[ 5/2 → 11/4 | note:54 clip:2 s:piano release:0.1 pan:0.5 ]", + "[ 5/2 → 11/4 | note:57 clip:2 s:piano release:0.1 pan:0.5138888888888888 ]", + "[ 5/2 → 11/4 | note:61 clip:2 s:piano release:0.1 pan:0.5324074074074074 ]", + "[ 5/2 → 11/4 | note:47 clip:2 s:piano release:0.1 pan:0.46759259259259256 ]", + "[ 3/1 → 13/4 | note:57 clip:2 s:piano release:0.1 pan:0.5138888888888888 ]", + "[ 3/1 → 13/4 | note:61 clip:2 s:piano release:0.1 pan:0.5324074074074074 ]", + "[ 3/1 → 13/4 | note:47 clip:2 s:piano release:0.1 pan:0.46759259259259256 ]", + "[ 13/4 → 7/2 | note:54 clip:2 s:piano release:0.1 pan:0.5 ]", + "[ 13/4 → 7/2 | note:47 clip:2 s:piano release:0.1 pan:0.46759259259259256 ]", + "[ 15/4 → 4/1 | note:57 clip:2 s:piano release:0.1 pan:0.5138888888888888 ]", + "[ 15/4 → 4/1 | note:61 clip:2 s:piano release:0.1 pan:0.5324074074074074 ]", + "[ 15/4 → 4/1 | note:47 clip:2 s:piano release:0.1 pan:0.46759259259259256 ]", + "[ 4/1 → 17/4 | note:55 clip:2 s:piano release:0.1 pan:0.5046296296296297 ]", + "[ 17/4 → 9/2 | note:40 clip:2 s:piano release:0.1 pan:0.4351851851851852 ]", + "[ 9/2 → 19/4 | note:55 clip:2 s:piano release:0.1 pan:0.5046296296296297 ]", + "[ 9/2 → 19/4 | note:59 clip:2 s:piano release:0.1 pan:0.5231481481481481 ]", + "[ 9/2 → 19/4 | note:62 clip:2 s:piano release:0.1 pan:0.537037037037037 ]", + "[ 9/2 → 19/4 | note:40 clip:2 s:piano release:0.1 pan:0.4351851851851852 ]", + "[ 5/1 → 21/4 | note:59 clip:2 s:piano release:0.1 pan:0.5231481481481481 ]", + "[ 5/1 → 21/4 | note:62 clip:2 s:piano release:0.1 pan:0.537037037037037 ]", + "[ 5/1 → 21/4 | note:40 clip:2 s:piano release:0.1 pan:0.4351851851851852 ]", + "[ 21/4 → 11/2 | note:55 clip:2 s:piano release:0.1 pan:0.5046296296296297 ]", + "[ 21/4 → 11/2 | note:40 clip:2 s:piano release:0.1 pan:0.4351851851851852 ]", + "[ 23/4 → 6/1 | note:59 clip:2 s:piano release:0.1 pan:0.5231481481481481 ]", + "[ 23/4 → 6/1 | note:62 clip:2 s:piano release:0.1 pan:0.537037037037037 ]", + "[ 23/4 → 6/1 | note:40 clip:2 s:piano release:0.1 pan:0.4351851851851852 ]", + "[ 6/1 → 25/4 | note:57 clip:2 s:piano release:0.1 pan:0.5138888888888888 ]", + "[ 25/4 → 13/2 | note:47 clip:2 s:piano release:0.1 pan:0.46759259259259256 ]", + "[ 13/2 → 27/4 | note:57 clip:2 s:piano release:0.1 pan:0.5138888888888888 ]", + "[ 13/2 → 27/4 | note:61 clip:2 s:piano release:0.1 pan:0.5324074074074074 ]", + "[ 13/2 → 27/4 | note:64 clip:2 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 13/2 → 27/4 | note:47 clip:2 s:piano release:0.1 pan:0.46759259259259256 ]", + "[ 7/1 → 29/4 | note:61 clip:2 s:piano release:0.1 pan:0.5324074074074074 ]", + "[ 7/1 → 29/4 | note:64 clip:2 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 7/1 → 29/4 | note:47 clip:2 s:piano release:0.1 pan:0.46759259259259256 ]", + "[ 29/4 → 15/2 | note:57 clip:2 s:piano release:0.1 pan:0.5138888888888888 ]", + "[ 29/4 → 15/2 | note:47 clip:2 s:piano release:0.1 pan:0.46759259259259256 ]", + "[ 31/4 → 8/1 | note:61 clip:2 s:piano release:0.1 pan:0.5324074074074074 ]", + "[ 31/4 → 8/1 | note:64 clip:2 s:piano release:0.1 pan:0.5462962962962963 ]", + "[ 31/4 → 8/1 | note:47 clip:2 s:piano release:0.1 pan:0.46759259259259256 ]", + "[ 8/1 → 33/4 | note:50 clip:2 s:piano release:0.1 pan:0.4814814814814815 ]", + "[ 33/4 → 17/2 | note:38 clip:2 s:piano release:0.1 pan:0.42592592592592593 ]", + "[ 17/2 → 35/4 | note:50 clip:2 s:piano release:0.1 pan:0.4814814814814815 ]", + "[ 17/2 → 35/4 | note:54 clip:2 s:piano release:0.1 pan:0.5 ]", + "[ 17/2 → 35/4 | note:57 clip:2 s:piano release:0.1 pan:0.5138888888888888 ]", + "[ 17/2 → 35/4 | note:38 clip:2 s:piano release:0.1 pan:0.42592592592592593 ]", + "[ 9/1 → 37/4 | note:54 clip:2 s:piano release:0.1 pan:0.5 ]", + "[ 9/1 → 37/4 | note:57 clip:2 s:piano release:0.1 pan:0.5138888888888888 ]", + "[ 9/1 → 37/4 | note:38 clip:2 s:piano release:0.1 pan:0.42592592592592593 ]", + "[ 37/4 → 19/2 | note:50 clip:2 s:piano release:0.1 pan:0.4814814814814815 ]", + "[ 37/4 → 19/2 | note:38 clip:2 s:piano release:0.1 pan:0.42592592592592593 ]", + "[ 39/4 → 10/1 | note:54 clip:2 s:piano release:0.1 pan:0.5 ]", + "[ 39/4 → 10/1 | note:57 clip:2 s:piano release:0.1 pan:0.5138888888888888 ]", + "[ 39/4 → 10/1 | note:38 clip:2 s:piano release:0.1 pan:0.42592592592592593 ]", + "[ 10/1 → 41/4 | note:52 clip:2 s:piano release:0.1 pan:0.4907407407407407 ]", + "[ 41/4 → 21/2 | note:45 clip:2 s:piano release:0.1 pan:0.45833333333333337 ]", + "[ 21/2 → 43/4 | note:52 clip:2 s:piano release:0.1 pan:0.4907407407407407 ]", + "[ 21/2 → 43/4 | note:55 clip:2 s:piano release:0.1 pan:0.5046296296296297 ]", + "[ 21/2 → 43/4 | note:59 clip:2 s:piano release:0.1 pan:0.5231481481481481 ]", + "[ 21/2 → 43/4 | note:45 clip:2 s:piano release:0.1 pan:0.45833333333333337 ]", + "[ 11/1 → 45/4 | note:55 clip:2 s:piano release:0.1 pan:0.5046296296296297 ]", + "[ 11/1 → 45/4 | note:59 clip:2 s:piano release:0.1 pan:0.5231481481481481 ]", + "[ 11/1 → 45/4 | note:45 clip:2 s:piano release:0.1 pan:0.45833333333333337 ]", + "[ 45/4 → 23/2 | note:52 clip:2 s:piano release:0.1 pan:0.4907407407407407 ]", + "[ 45/4 → 23/2 | note:45 clip:2 s:piano release:0.1 pan:0.45833333333333337 ]", + "[ 47/4 → 12/1 | note:55 clip:2 s:piano release:0.1 pan:0.5046296296296297 ]", + "[ 47/4 → 12/1 | note:59 clip:2 s:piano release:0.1 pan:0.5231481481481481 ]", + "[ 47/4 → 12/1 | note:45 clip:2 s:piano release:0.1 pan:0.45833333333333337 ]", + "[ 12/1 → 49/4 | note:54 clip:2 s:piano release:0.1 pan:0.5 ]", + "[ 49/4 → 25/2 | note:38 clip:2 s:piano release:0.1 pan:0.42592592592592593 ]", + "[ 25/2 → 51/4 | note:54 clip:2 s:piano release:0.1 pan:0.5 ]", + "[ 25/2 → 51/4 | note:57 clip:2 s:piano release:0.1 pan:0.5138888888888888 ]", + "[ 25/2 → 51/4 | note:61 clip:2 s:piano release:0.1 pan:0.5324074074074074 ]", + "[ 25/2 → 51/4 | note:38 clip:2 s:piano release:0.1 pan:0.42592592592592593 ]", + "[ 13/1 → 53/4 | note:57 clip:2 s:piano release:0.1 pan:0.5138888888888888 ]", + "[ 13/1 → 53/4 | note:61 clip:2 s:piano release:0.1 pan:0.5324074074074074 ]", + "[ 13/1 → 53/4 | note:38 clip:2 s:piano release:0.1 pan:0.42592592592592593 ]", + "[ 53/4 → 27/2 | note:54 clip:2 s:piano release:0.1 pan:0.5 ]", + "[ 53/4 → 27/2 | note:38 clip:2 s:piano release:0.1 pan:0.42592592592592593 ]", + "[ 55/4 → 14/1 | note:57 clip:2 s:piano release:0.1 pan:0.5138888888888888 ]", + "[ 55/4 → 14/1 | note:61 clip:2 s:piano release:0.1 pan:0.5324074074074074 ]", + "[ 55/4 → 14/1 | note:38 clip:2 s:piano release:0.1 pan:0.42592592592592593 ]", + "[ 14/1 → 57/4 | note:55 clip:2 s:piano release:0.1 pan:0.5046296296296297 ]", + "[ 57/4 → 29/2 | note:45 clip:2 s:piano release:0.1 pan:0.45833333333333337 ]", + "[ 29/2 → 59/4 | note:55 clip:2 s:piano release:0.1 pan:0.5046296296296297 ]", + "[ 29/2 → 59/4 | note:59 clip:2 s:piano release:0.1 pan:0.5231481481481481 ]", + "[ 29/2 → 59/4 | note:62 clip:2 s:piano release:0.1 pan:0.537037037037037 ]", + "[ 29/2 → 59/4 | note:45 clip:2 s:piano release:0.1 pan:0.45833333333333337 ]", + "[ 15/1 → 61/4 | note:59 clip:2 s:piano release:0.1 pan:0.5231481481481481 ]", + "[ 15/1 → 61/4 | note:62 clip:2 s:piano release:0.1 pan:0.537037037037037 ]", + "[ 15/1 → 61/4 | note:45 clip:2 s:piano release:0.1 pan:0.45833333333333337 ]", + "[ 61/4 → 31/2 | note:55 clip:2 s:piano release:0.1 pan:0.5046296296296297 ]", + "[ 61/4 → 31/2 | note:45 clip:2 s:piano release:0.1 pan:0.45833333333333337 ]", + "[ 63/4 → 16/1 | note:59 clip:2 s:piano release:0.1 pan:0.5231481481481481 ]", + "[ 63/4 → 16/1 | note:62 clip:2 s:piano release:0.1 pan:0.537037037037037 ]", + "[ 63/4 → 16/1 | note:45 clip:2 s:piano release:0.1 pan:0.45833333333333337 ]", ] `; From b25b9d815b87461a107da22f0b662a4c12fc6af2 Mon Sep 17 00:00:00 2001 From: Vasilii Milovidov Date: Sat, 7 Oct 2023 19:30:07 +0400 Subject: [PATCH 076/158] fix: conflicts --- packages/superdough/reverb.mjs | 4 +--- packages/superdough/superdough.mjs | 26 ++++++++++++++++---------- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/packages/superdough/reverb.mjs b/packages/superdough/reverb.mjs index 85be19ae..f853d3fb 100644 --- a/packages/superdough/reverb.mjs +++ b/packages/superdough/reverb.mjs @@ -19,10 +19,9 @@ if (typeof AudioContext !== 'undefined') { AudioContext.prototype.createReverb = function (duration, fade, lp, dim, ir) { const convolver = this.createConvolver(); - convolver.generate = (d, fade, lp, dim, buf) => { + convolver.generate = (d = 2, fade = 0.1, lp = 15000, dim = 1000, buf) => { if (buf) { convolver.buffer = this.adjustLength(d, buf); - return convolver; } else { this.generateReverb( { @@ -42,7 +41,6 @@ if (typeof AudioContext !== 'undefined') { convolver.fade = fade; convolver.lp = lp; convolver.dim = dim; - return convolver; } }; convolver.setIR = (d, fade, lp, dim, buf) => { diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index d7f0b14b..20edbe33 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -114,6 +114,8 @@ function getDelay(orbit, delaytime, delayfeedback, t) { let reverbs = {}; +let hasChanged = (now, before) => now !== undefined && now !== before; + function getReverb(orbit, duration = 2, fade, lp, dim, ir) { if (!reverbs[orbit]) { const ac = getAudioContext(); @@ -123,13 +125,17 @@ function getReverb(orbit, duration = 2, fade, lp, dim, ir) { } if ( - reverbs[orbit].duration !== duration || - reverbs[orbit].fade !== fade || - reverbs[orbit].ir !== lp || - reverbs[orbit].dim !== dim || - reverbs[orbit].ir !== ir + hasChanged(duration, reverbs[orbit].duration) || + hasChanged(fade, reverbs[orbit].fade) || + hasChanged(lp, reverbs[orbit].lp) || + hasChanged(dim, reverbs[orbit].dim) ) { - reverbs[orbit].generate(duration, fade, lp, dim, ir); + // only regenerate when something has changed + // avoids endless regeneration on things like + // stack(s("a"), s("b").rsize(8)).room(.5) + // this only works when args may stay undefined until here + // setting default values breaks this + reverbs[orbit].generate(duration, fade, lp, dim); } if (reverbs[orbit].ir !== ir) { @@ -235,10 +241,10 @@ export const superdough = async (value, deadline, hapDuration) => { delaytime = 0.25, orbit = 1, room, - roomfade = 0.1, - roomlp = 15000, - roomdim = 1000, - roomsize = 2, + roomfade, + roomlp, + roomdim, + roomsize, ir, i = 0, velocity = 1, From 2d750a14b9bd5da47a86aada47b0f5d5836aca8c Mon Sep 17 00:00:00 2001 From: Vasilii Milovidov Date: Sat, 7 Oct 2023 19:48:07 +0400 Subject: [PATCH 077/158] fix: conflicts --- packages/superdough/superdough.mjs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index 20edbe33..d2507672 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -116,7 +116,8 @@ let reverbs = {}; let hasChanged = (now, before) => now !== undefined && now !== before; -function getReverb(orbit, duration = 2, fade, lp, dim, ir) { +function getReverb(orbit, duration, fade, lp, dim, ir) { + // If no reverb has been created for a given orbit, create one if (!reverbs[orbit]) { const ac = getAudioContext(); const reverb = ac.createReverb(duration, fade, lp, dim, ir); From 130ad4451b7af054371992220342a0658b9543d7 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Sun, 8 Oct 2023 00:30:12 +0200 Subject: [PATCH 078/158] add compressor + postgain --- packages/core/controls.mjs | 6 ++++++ packages/superdough/helpers.mjs | 11 +++++++++++ packages/superdough/superdough.mjs | 15 +++++++++++++-- 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/packages/core/controls.mjs b/packages/core/controls.mjs index 88c2c072..832b09dc 100644 --- a/packages/core/controls.mjs +++ b/packages/core/controls.mjs @@ -86,6 +86,7 @@ const generic_params = [ * */ ['gain'], + ['postgain'], /** * Like {@link gain}, but linear. * @@ -1051,6 +1052,11 @@ const generic_params = [ * */ ['shape'], + [['compressor', 'compressorRatio', 'compressorKnee', 'compressorAttack', 'compressorRelease']], + ['compressorKnee'], + ['compressorRatio'], + ['compressorAttack'], + ['compressorRelease'], /** * Changes the speed of sample playback, i.e. a cheap way of changing pitch. * diff --git a/packages/superdough/helpers.mjs b/packages/superdough/helpers.mjs index 576ec3f1..d87ea94d 100644 --- a/packages/superdough/helpers.mjs +++ b/packages/superdough/helpers.mjs @@ -78,6 +78,17 @@ export const getParamADSR = (param, attack, decay, sustain, release, min, max, b param.linearRampToValueAtTime(min, end + Math.max(release, 0.1)); }; +export function getCompressor(ac, threshold, ratio, knee, attack, release) { + const options = { + threshold: threshold ?? -3, + ratio: ratio ?? 10, + knee: knee ?? 10, + attack: attack ?? 0.005, + release: release ?? 0.05, + }; + return new DynamicsCompressorNode(ac, options); +} + export function createFilter( context, type, diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index 730e495d..410b7b96 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -9,7 +9,7 @@ import './reverb.mjs'; import './vowel.mjs'; import { clamp } from './util.mjs'; import workletsUrl from './worklets.mjs?url'; -import { createFilter, gainNode } from './helpers.mjs'; +import { createFilter, gainNode, getCompressor } from './helpers.mjs'; import { map } from 'nanostores'; import { logger } from './logger.mjs'; @@ -192,6 +192,7 @@ export const superdough = async (value, deadline, hapDuration) => { bank, source, gain = 0.8, + postgain = 1, // filters ftype = '12db', fanchor = 0.5, @@ -237,6 +238,11 @@ export const superdough = async (value, deadline, hapDuration) => { velocity = 1, analyze, // analyser wet fft = 8, // fftSize 0 - 10 + compressor: compressorThreshold, + compressorRatio, + compressorKnee, + compressorAttack, + compressorRelease, } = value; gain *= velocity; // legacy fix for velocity let toDisconnect = []; // audio nodes that will be disconnected when the source has ended @@ -351,6 +357,11 @@ export const superdough = async (value, deadline, hapDuration) => { crush !== undefined && chain.push(getWorklet(ac, 'crush-processor', { crush })); shape !== undefined && chain.push(getWorklet(ac, 'shape-processor', { shape })); + compressorThreshold !== undefined && + chain.push( + getCompressor(ac, compressorThreshold, compressorRatio, compressorKnee, compressorAttack, compressorRelease), + ); + // panning if (pan !== undefined) { const panner = ac.createStereoPanner(); @@ -359,7 +370,7 @@ export const superdough = async (value, deadline, hapDuration) => { } // last gain - const post = gainNode(1); + const post = gainNode(postgain); chain.push(post); post.connect(getDestination()); From 444d583a49065648af5d4c2cc6b1c2bf82532d26 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Sun, 8 Oct 2023 13:23:13 +0200 Subject: [PATCH 079/158] fix: hashes in urls --- packages/superdough/sampler.mjs | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/superdough/sampler.mjs b/packages/superdough/sampler.mjs index 76b6a542..a7e9c3a3 100644 --- a/packages/superdough/sampler.mjs +++ b/packages/superdough/sampler.mjs @@ -64,6 +64,7 @@ export const getSampleBufferSource = async (s, n, note, speed, freq, bank, resol export const loadBuffer = (url, ac, s, n = 0) => { const label = s ? `sound "${s}:${n}"` : 'sample'; + url = url.replace('#', '%23'); if (!loadCache[url]) { logger(`[sampler] load ${label}..`, 'load-sample', { url }); const timestamp = Date.now(); From 0a3692e459ca2eb807b71f55ee6698d36dfd2aa3 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Sun, 8 Oct 2023 13:37:39 +0200 Subject: [PATCH 080/158] compressor docs --- packages/core/controls.mjs | 19 +++++++++++++++++++ website/src/pages/learn/effects.mdx | 9 +++++++++ 2 files changed, 28 insertions(+) diff --git a/packages/core/controls.mjs b/packages/core/controls.mjs index 832b09dc..3abeb504 100644 --- a/packages/core/controls.mjs +++ b/packages/core/controls.mjs @@ -86,6 +86,15 @@ const generic_params = [ * */ ['gain'], + /** + * Gain applied after all effects have been processed. + * + * @name postgain + * @example + * s("bd sd,hh*4") + * .compressor("-20:20:10:.002:.02").postgain(1.5) + * + */ ['postgain'], /** * Like {@link gain}, but linear. @@ -1052,6 +1061,16 @@ const generic_params = [ * */ ['shape'], + /** + * Dynamics Compressor. The params are `compressor("threshold:ratio:knee:attack:release")` + * More info [here](https://developer.mozilla.org/en-US/docs/Web/API/DynamicsCompressorNode?retiredLocale=de#instance_properties) + * + * @name compressor + * @example + * s("bd sd,hh*4") + * .compressor("-20:20:10:.002:.02") + * + */ [['compressor', 'compressorRatio', 'compressorKnee', 'compressorAttack', 'compressorRelease']], ['compressorKnee'], ['compressorRatio'], diff --git a/website/src/pages/learn/effects.mdx b/website/src/pages/learn/effects.mdx index 2ee6c44a..11b74646 100644 --- a/website/src/pages/learn/effects.mdx +++ b/website/src/pages/learn/effects.mdx @@ -144,6 +144,15 @@ There is one filter envelope for each filter type and thus one set of envelope f +## compressor + + + +## postgain + + + + # Panning ## jux From 08ce8213fcb09eb2dd709650b1f5339138a774bc Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Sun, 8 Oct 2023 13:37:56 +0200 Subject: [PATCH 081/158] format --- website/src/pages/learn/effects.mdx | 1 - 1 file changed, 1 deletion(-) diff --git a/website/src/pages/learn/effects.mdx b/website/src/pages/learn/effects.mdx index 11b74646..0bb9da9e 100644 --- a/website/src/pages/learn/effects.mdx +++ b/website/src/pages/learn/effects.mdx @@ -152,7 +152,6 @@ There is one filter envelope for each filter type and thus one set of envelope f - # Panning ## jux From 56c14414946796436db34f4f565a9c9ec437674d Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Sun, 8 Oct 2023 13:38:18 +0200 Subject: [PATCH 082/158] snapshot --- test/__snapshots__/examples.test.mjs.snap | 58 +++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/test/__snapshots__/examples.test.mjs.snap b/test/__snapshots__/examples.test.mjs.snap index 18370903..f351a5a0 100644 --- a/test/__snapshots__/examples.test.mjs.snap +++ b/test/__snapshots__/examples.test.mjs.snap @@ -1185,6 +1185,35 @@ exports[`runs examples > example "compress" example index 0 1`] = ` ] `; +exports[`runs examples > example "compressor" example index 0 1`] = ` +[ + "[ 0/1 → 1/4 | s:hh compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 ]", + "[ 0/1 → 1/2 | s:bd compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 ]", + "[ 1/4 → 1/2 | s:hh compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 ]", + "[ 1/2 → 3/4 | s:hh compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 ]", + "[ 1/2 → 1/1 | s:sd compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 ]", + "[ 3/4 → 1/1 | s:hh compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 ]", + "[ 1/1 → 5/4 | s:hh compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 ]", + "[ 1/1 → 3/2 | s:bd compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 ]", + "[ 5/4 → 3/2 | s:hh compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 ]", + "[ 3/2 → 7/4 | s:hh compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 ]", + "[ 3/2 → 2/1 | s:sd compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 ]", + "[ 7/4 → 2/1 | s:hh compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 ]", + "[ 2/1 → 9/4 | s:hh compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 ]", + "[ 2/1 → 5/2 | s:bd compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 ]", + "[ 9/4 → 5/2 | s:hh compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 ]", + "[ 5/2 → 11/4 | s:hh compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 ]", + "[ 5/2 → 3/1 | s:sd compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 ]", + "[ 11/4 → 3/1 | s:hh compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 ]", + "[ 3/1 → 13/4 | s:hh compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 ]", + "[ 3/1 → 7/2 | s:bd compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 ]", + "[ 13/4 → 7/2 | s:hh compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 ]", + "[ 7/2 → 15/4 | s:hh compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 ]", + "[ 7/2 → 4/1 | s:sd compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 ]", + "[ 15/4 → 4/1 | s:hh compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 ]", +] +`; + exports[`runs examples > example "cosine" example index 0 1`] = ` [ "[ 0/1 → 1/8 | note:Eb4 ]", @@ -3293,6 +3322,35 @@ exports[`runs examples > example "polymeterSteps" example index 0 1`] = ` ] `; +exports[`runs examples > example "postgain" example index 0 1`] = ` +[ + "[ 0/1 → 1/4 | s:hh compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 postgain:1.5 ]", + "[ 0/1 → 1/2 | s:bd compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 postgain:1.5 ]", + "[ 1/4 → 1/2 | s:hh compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 postgain:1.5 ]", + "[ 1/2 → 3/4 | s:hh compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 postgain:1.5 ]", + "[ 1/2 → 1/1 | s:sd compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 postgain:1.5 ]", + "[ 3/4 → 1/1 | s:hh compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 postgain:1.5 ]", + "[ 1/1 → 5/4 | s:hh compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 postgain:1.5 ]", + "[ 1/1 → 3/2 | s:bd compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 postgain:1.5 ]", + "[ 5/4 → 3/2 | s:hh compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 postgain:1.5 ]", + "[ 3/2 → 7/4 | s:hh compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 postgain:1.5 ]", + "[ 3/2 → 2/1 | s:sd compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 postgain:1.5 ]", + "[ 7/4 → 2/1 | s:hh compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 postgain:1.5 ]", + "[ 2/1 → 9/4 | s:hh compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 postgain:1.5 ]", + "[ 2/1 → 5/2 | s:bd compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 postgain:1.5 ]", + "[ 9/4 → 5/2 | s:hh compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 postgain:1.5 ]", + "[ 5/2 → 11/4 | s:hh compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 postgain:1.5 ]", + "[ 5/2 → 3/1 | s:sd compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 postgain:1.5 ]", + "[ 11/4 → 3/1 | s:hh compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 postgain:1.5 ]", + "[ 3/1 → 13/4 | s:hh compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 postgain:1.5 ]", + "[ 3/1 → 7/2 | s:bd compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 postgain:1.5 ]", + "[ 13/4 → 7/2 | s:hh compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 postgain:1.5 ]", + "[ 7/2 → 15/4 | s:hh compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 postgain:1.5 ]", + "[ 7/2 → 4/1 | s:sd compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 postgain:1.5 ]", + "[ 15/4 → 4/1 | s:hh compressor:-20 compressorRatio:20 compressorKnee:10 compressorAttack:0.002 compressorRelease:0.02 postgain:1.5 ]", +] +`; + exports[`runs examples > example "press" example index 0 1`] = ` [ "[ 0/1 → 1/2 | s:hh ]", From f34937d4aef4a89497f5cffdb5ba1bcaaa288130 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Mon, 9 Oct 2023 21:31:49 +0200 Subject: [PATCH 083/158] fix: roomsize not required --- packages/superdough/superdough.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index 410b7b96..9f11b983 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -382,7 +382,7 @@ export const superdough = async (value, deadline, hapDuration) => { } // reverb let reverbSend; - if (room > 0 && roomsize > 0) { + if (room > 0) { const reverbNode = getReverb(orbit, roomsize, roomfade, roomlp, roomdim); reverbSend = effectSend(post, reverbNode, room); } From 6544e8135cf8b40eaa751cd13fdabeb5baa489a1 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Mon, 9 Oct 2023 21:40:38 +0200 Subject: [PATCH 084/158] fix: reverb sampleRate --- packages/superdough/reverb.mjs | 4 +--- packages/superdough/reverbGen.mjs | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/superdough/reverb.mjs b/packages/superdough/reverb.mjs index 3d54203c..ff4cb78a 100644 --- a/packages/superdough/reverb.mjs +++ b/packages/superdough/reverb.mjs @@ -1,14 +1,12 @@ import reverbGen from './reverbGen.mjs'; if (typeof AudioContext !== 'undefined') { - AudioContext.prototype.generateReverb = reverbGen.generateReverb; AudioContext.prototype.createReverb = function (duration, fade, lp, dim) { const convolver = this.createConvolver(); convolver.generate = (d = 2, fade = 0.1, lp = 15000, dim = 1000) => { - this.generateReverb( + reverbGen.generateReverb( { audioContext: this, - sampleRate: 44100, numChannels: 2, decayTime: d, fadeInTime: fade, diff --git a/packages/superdough/reverbGen.mjs b/packages/superdough/reverbGen.mjs index 49429937..eed63792 100644 --- a/packages/superdough/reverbGen.mjs +++ b/packages/superdough/reverbGen.mjs @@ -23,7 +23,7 @@ var reverbGen = {}; immediately within the current execution context, or later. */ reverbGen.generateReverb = function (params, callback) { var audioContext = params.audioContext || new AudioContext(); - var sampleRate = params.sampleRate || 44100; + var sampleRate = audioContext.sampleRate; var numChannels = params.numChannels || 2; // params.decayTime is the -60dB fade time. We let it go 50% longer to get to -90dB. var totalTime = params.decayTime * 1.5; From 5c76f2b9a28a7456e8fd02a0ea7b7f9f09fde34a Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Mon, 9 Oct 2023 22:30:34 +0200 Subject: [PATCH 085/158] bump superdough to 0.9.10 --- packages/superdough/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/superdough/package.json b/packages/superdough/package.json index 387749c8..0f862c03 100644 --- a/packages/superdough/package.json +++ b/packages/superdough/package.json @@ -1,6 +1,6 @@ { "name": "superdough", - "version": "0.9.9", + "version": "0.9.10", "description": "simple web audio synth and sampler intended for live coding. inspired by superdirt and webdirt.", "main": "index.mjs", "type": "module", From 57b166d13a9523350d42c5e682b0233678365816 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Mon, 9 Oct 2023 22:49:38 +0200 Subject: [PATCH 086/158] fix: pitched sample as ir --- packages/superdough/superdough.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index 1d615548..e3033afe 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -399,7 +399,7 @@ export const superdough = async (value, deadline, hapDuration) => { if (Array.isArray(sample)) { url = sample.data.samples[i % sample.data.samples.length]; } else if (typeof sample === 'object') { - url = Object.values(sample.data.samples)[i & Object.values(sample.data.samples).length]; + url = Object.values(sample.data.samples).flat()[i % Object.values(sample.data.samples).length]; } roomIR = await loadBuffer(url, ac, ir, 0); } From d7028252c2210939b1435d1aee5c9df84f4046ae Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Fri, 13 Oct 2023 12:55:31 +0200 Subject: [PATCH 087/158] vite-vanilla-repl readme fix --- packages/core/examples/vite-vanilla-repl-cm6/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/core/examples/vite-vanilla-repl-cm6/README.md b/packages/core/examples/vite-vanilla-repl-cm6/README.md index 70d18e09..4d99d4e6 100644 --- a/packages/core/examples/vite-vanilla-repl-cm6/README.md +++ b/packages/core/examples/vite-vanilla-repl-cm6/README.md @@ -3,6 +3,6 @@ This folder demonstrates how to set up a strudel repl using vite and vanilla JS + codemirror. Run it using: ```sh -npm i -npm run dev +pnpm i +pnpm dev ``` From 289820188ea00224e1ef770ac79c40bd815afdaa Mon Sep 17 00:00:00 2001 From: "Alexandre G.-Raymond" Date: Sat, 14 Oct 2023 16:20:17 +0200 Subject: [PATCH 088/158] Add shabda shortcut (#406) --- packages/superdough/sampler.mjs | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/packages/superdough/sampler.mjs b/packages/superdough/sampler.mjs index a7e9c3a3..d0733b14 100644 --- a/packages/superdough/sampler.mjs +++ b/packages/superdough/sampler.mjs @@ -147,7 +147,12 @@ function getSamplesPrefixHandler(url) { * sd: '808sd/SD0010.WAV' * }, 'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/'); * s("[bd ~]*2, [~ hh]*2, ~ sd") - * + * @example + * samples('shabda:noise,chimp:2') + * s("noise ") + * @example + * samples('shabda/speech/fr-FR/f:chocolat') + * s("chocolat*4") */ export const samples = async (sampleMap, baseUrl = sampleMap._base || '', options = {}) => { @@ -162,6 +167,21 @@ export const samples = async (sampleMap, baseUrl = sampleMap._base || '', option path = path.endsWith('/') ? path.slice(0, -1) : path; sampleMap = `https://raw.githubusercontent.com/${path}/strudel.json`; } + if (sampleMap.startsWith('shabda:')) { + let [_, path] = sampleMap.split('shabda:'); + sampleMap = `https://shabda.ndre.gr/${path}.json?strudel=1`; + } + if (sampleMap.startsWith('shabda/speech')) { + let [_, path] = sampleMap.split('shabda/speech'); + path = path.startsWith('/') ? path.substring(1) : path; + let [params, words] = path.split(':'); + let gender = 'f'; + let language = 'en-GB'; + if (params) { + [language, gender] = params.split('/'); + } + sampleMap = `https://shabda.ndre.gr/speech/${words}.json?gender=${gender}&language=${language}&strudel=1'`; + } if (typeof fetch !== 'function') { // not a browser return; From 1721209f85a9362753f2ad3490d18ac3f58ddd25 Mon Sep 17 00:00:00 2001 From: "Alexandre G.-Raymond" Date: Sat, 14 Oct 2023 16:20:53 +0200 Subject: [PATCH 089/158] Update samples/shabda documentation --- website/src/pages/learn/samples.mdx | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/website/src/pages/learn/samples.mdx b/website/src/pages/learn/samples.mdx index 10d8c730..ca857a1f 100644 --- a/website/src/pages/learn/samples.mdx +++ b/website/src/pages/learn/samples.mdx @@ -283,7 +283,7 @@ With it, you can enter any sample name(s) to query from [freesound.org](https:// +You can also generate artificial voice samples with any text, in multiple languages. +Note that the language code and the gender parameters are optional and default to `en-GB` and `f` + + + # Sampler Effects Sampler effects are functions that can be used to change the behaviour of sample playback. From fed4c51aa41313e57204208780676d82b51424c1 Mon Sep 17 00:00:00 2001 From: "Alexandre G.-Raymond" Date: Sat, 14 Oct 2023 16:30:29 +0200 Subject: [PATCH 090/158] Prettier doc file --- website/src/pages/learn/samples.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/src/pages/learn/samples.mdx b/website/src/pages/learn/samples.mdx index ca857a1f..658caec7 100644 --- a/website/src/pages/learn/samples.mdx +++ b/website/src/pages/learn/samples.mdx @@ -292,7 +292,7 @@ stack( /> You can also generate artificial voice samples with any text, in multiple languages. -Note that the language code and the gender parameters are optional and default to `en-GB` and `f` +Note that the language code and the gender parameters are optional and default to `en-GB` and `f` Date: Tue, 17 Oct 2023 01:33:51 -0400 Subject: [PATCH 091/158] fixed --- packages/core/signal.mjs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/core/signal.mjs b/packages/core/signal.mjs index d0db0be9..f404f000 100644 --- a/packages/core/signal.mjs +++ b/packages/core/signal.mjs @@ -160,7 +160,12 @@ export const __chooseWith = (pat, xs) => { if (xs.length == 0) { return silence; } - return pat.range(0, xs.length).fmap((i) => xs[Math.floor(i)]); + + return pat.range(0, xs.length).fmap((i) => { + const key = Math.min(Math.max(Math.floor(i), 0), xs.length - 1); + console.log({ key }); + return xs[key]; + }); }; /** * Choose from the list of values (or patterns of values) using the given @@ -179,6 +184,8 @@ export const chooseWith = (pat, xs) => { * @param {Pattern} pat * @param {*} xs * @returns {Pattern} + * @example + * note("c g g d f").s(chooseInWith(slider(1, 0, 1), ["sawtooth", "triangle", "bd hh sd hh"])) */ export const chooseInWith = (pat, xs) => { return __chooseWith(pat, xs).innerJoin(); From d3edc62fc7178eef9f22a116d05244ef368c3d88 Mon Sep 17 00:00:00 2001 From: Jade Rowland Date: Tue, 17 Oct 2023 21:31:11 -0400 Subject: [PATCH 092/158] document function --- packages/core/signal.mjs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/core/signal.mjs b/packages/core/signal.mjs index f404f000..8bf3c911 100644 --- a/packages/core/signal.mjs +++ b/packages/core/signal.mjs @@ -163,7 +163,6 @@ export const __chooseWith = (pat, xs) => { return pat.range(0, xs.length).fmap((i) => { const key = Math.min(Math.max(Math.floor(i), 0), xs.length - 1); - console.log({ key }); return xs[key]; }); }; @@ -173,6 +172,8 @@ export const __chooseWith = (pat, xs) => { * @param {Pattern} pat * @param {*} xs * @returns {Pattern} + * @example + * note("c g g d f").s(chooseWith(slider(0, 0, 1), ["sawtooth", "triangle", "bd hh sd hh"])) */ export const chooseWith = (pat, xs) => { return __chooseWith(pat, xs).outerJoin(); @@ -184,8 +185,6 @@ export const chooseWith = (pat, xs) => { * @param {Pattern} pat * @param {*} xs * @returns {Pattern} - * @example - * note("c g g d f").s(chooseInWith(slider(1, 0, 1), ["sawtooth", "triangle", "bd hh sd hh"])) */ export const chooseInWith = (pat, xs) => { return __chooseWith(pat, xs).innerJoin(); From 912a661c042bf620995adea21ab6e9cf882e4f3c Mon Sep 17 00:00:00 2001 From: Jade Rowland Date: Tue, 17 Oct 2023 23:03:14 -0400 Subject: [PATCH 093/158] fixed example test failure --- packages/core/signal.mjs | 2 +- test/__snapshots__/examples.test.mjs.snap | 25 +++++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/packages/core/signal.mjs b/packages/core/signal.mjs index 8bf3c911..d1408809 100644 --- a/packages/core/signal.mjs +++ b/packages/core/signal.mjs @@ -173,7 +173,7 @@ export const __chooseWith = (pat, xs) => { * @param {*} xs * @returns {Pattern} * @example - * note("c g g d f").s(chooseWith(slider(0, 0, 1), ["sawtooth", "triangle", "bd hh sd hh"])) + * note("c2 g2!2 d2 f1").s(chooseWith(sine.fast(2), ["sawtooth", "triangle", "bd:6"])) */ export const chooseWith = (pat, xs) => { return __chooseWith(pat, xs).outerJoin(); diff --git a/test/__snapshots__/examples.test.mjs.snap b/test/__snapshots__/examples.test.mjs.snap index f351a5a0..ed0249f3 100644 --- a/test/__snapshots__/examples.test.mjs.snap +++ b/test/__snapshots__/examples.test.mjs.snap @@ -1071,6 +1071,31 @@ exports[`runs examples > example "chooseCycles" example index 1 1`] = ` ] `; +exports[`runs examples > example "chooseWith" example index 0 1`] = ` +[ + "[ 0/1 → 1/5 | note:c2 s:bd n:6 ]", + "[ 1/5 → 2/5 | note:g2 s:sawtooth ]", + "[ 2/5 → 3/5 | note:g2 s:triangle ]", + "[ 3/5 → 4/5 | note:d2 s:bd n:6 ]", + "[ 4/5 → 1/1 | note:f1 s:sawtooth ]", + "[ 1/1 → 6/5 | note:c2 s:bd n:6 ]", + "[ 6/5 → 7/5 | note:g2 s:sawtooth ]", + "[ 7/5 → 8/5 | note:g2 s:triangle ]", + "[ 8/5 → 9/5 | note:d2 s:bd n:6 ]", + "[ 9/5 → 2/1 | note:f1 s:sawtooth ]", + "[ 2/1 → 11/5 | note:c2 s:bd n:6 ]", + "[ 11/5 → 12/5 | note:g2 s:sawtooth ]", + "[ 12/5 → 13/5 | note:g2 s:triangle ]", + "[ 13/5 → 14/5 | note:d2 s:bd n:6 ]", + "[ 14/5 → 3/1 | note:f1 s:sawtooth ]", + "[ 3/1 → 16/5 | note:c2 s:bd n:6 ]", + "[ 16/5 → 17/5 | note:g2 s:sawtooth ]", + "[ 17/5 → 18/5 | note:g2 s:triangle ]", + "[ 18/5 → 19/5 | note:d2 s:bd n:6 ]", + "[ 19/5 → 4/1 | note:f1 s:sawtooth ]", +] +`; + exports[`runs examples > example "chop" example index 0 1`] = ` [ "[ 0/1 → 1/1 | s:rhodes begin:0.75 end:1 speed:0.25 unit:c ]", From d9db5263e8c665ed62da4b0ef544cd2c46d517f6 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Fri, 20 Oct 2023 11:18:40 +0200 Subject: [PATCH 094/158] add recipes --- website/src/pages/recipes/recipes.mdx | 313 ++++++++++++++++++++++++++ 1 file changed, 313 insertions(+) create mode 100644 website/src/pages/recipes/recipes.mdx diff --git a/website/src/pages/recipes/recipes.mdx b/website/src/pages/recipes/recipes.mdx new file mode 100644 index 00000000..5e5ae9bf --- /dev/null +++ b/website/src/pages/recipes/recipes.mdx @@ -0,0 +1,313 @@ +--- +title: Recipes +layout: ../../layouts/MainLayout.astro +--- + +import { MiniRepl } from '../../docs/MiniRepl'; + +# Recipes + +This page shows possible ways to achieve common (or not so common) musical goals. +There are often many ways to do a thing and there is no right or wrong. +The fun part is that each representation will give you different impulses when improvising. + +## Arpeggios + +An arpeggio is when the notes of a chord are played in sequence. +We can either write the notes by hand: + + + +...or use scales: + + + +...or chord symbols: + + + +...using off: + + + +## Chopping Breaks + +A sample can be looped and chopped like this: + + + +This fits the break into 8 cycles + chops it in 16 pieces. +The chops are not audible yet, because we're not doing any manipulation. +Let's add randmized doubling + reversing: + + + +If we want to specify the order of samples, we can replace `chop` with `slice`: + +") + .cut(1).rarely(ply(2))`} + punchcard +/> + +If we use `splice` instead of `slice`, the speed adjusts to the duration of the event: + +") + .cut(1).rarely(ply(2))`} + punchcard +/> + +Note that we don't need `fit`, because `splice` will do that by itself. + +## Filter Envelopes + +A minimal filter envelope looks like this: + + d2") + .s("sawtooth") + .lpf(400).lpa(.2).lpenv(4) + .scope()`} +/> + +We can flip the envelope by setting `lpenv` negative + add some resonance `lpq`: + + d2") + .s("sawtooth").lpq(8) + .lpf(400).lpa(.2).lpenv(-4) + .scope()`} +/> + +## Layering Sounds + +We can layer sounds by separating them with ",": + +") +.s("sawtooth, square") // <------ +.scope()`} +/> + +We can control the gain of individual sounds like this: + +") +.s("sawtooth, square:0:.5") // <--- "name:number:gain" +.scope()`} +/> + +For more control over each voice, we can use `layer`: + +").layer( + x=>x.s("sawtooth").vib(4), + x=>x.s("square").add(note(12)) +).scope()`} +/> + +Here, we give the sawtooth a vibrato and the square is moved an octave up. +With `layer`, you can use any pattern method available on each voice, so sky is the limit.. + +## Oscillator Detune + +We can fatten a sound by adding a detuned version to itself: + +") +.add(note("0,.1")) // <------ chorus +.s("sawtooth").scope()`} + punchcard +/> + +Try out different values, or add another voice! + +## Polyrhythms + +Here is a simple example of a polyrhythm: + + + +A polyrhythm is when 2 different tempos happen at the same time. + +## Polymeter + +This is a polymeter: + +,").fast(2)`} punchcard /> + +A polymeter is when 2 different bar lengths play at the same tempo. + +## Phasing + +This is a phasing: + +*[6,6.1]").piano()`} punchcard /> + +Phasing happens when the same sequence plays at slightly different tempos. + +## Running through samples + +Using `run` with `n`, we can rush through a sample bank: + + + +This works great with sample banks that contain similar sounds, like in this case different recordings of a tabla. +Often times, you'll hear the beginning of the phrase not where the pattern begins. +In this case, I hear the beginning at the third sample, which can be accounted for with `early`. + + + +Let's add some randomness: + + + +## Tape Warble + +We can emulate a pitch warbling effect like this: + + + +## Sound Duration + +There are a number of ways to change the sound duration. Using clip: + +/2")`} +/> + +The value of clip is relative to the duration of each event. +We can also create overlaps using release: + +/2")`} +/> + +This will smoothly fade out each sound for the given number of seconds. +We could also make the notes shorter with decay / sustain: + +/2").sustain(0)`} +/> + +For now, there is a limitation where decay values that exceed the event duration may cause little cracks, so use higher numbers with caution.. + +When using samples, we also have `.end` to cut relative to the sample length: + +")`} /> + +Compare that to clip: + +")`} /> + +or decay / sustain + +").sustain(0)`} /> + +## Wavetable Synthesis + +You can loop a sample with `loop` / `loopEnd`: + +").s("bd").loop(1).loopEnd(.05)`} /> + +This allows us to play the first 5% of the bass drum as a synth! +To simplify loading wavetables, any sample that starts with `wt_` will be looped automatically: + + + +Running through different wavetables can also give interesting variations: + + + +...adding a filter envelope + reverb: + + + \ No newline at end of file From 7b8a35a311e490170ac2912a943f50646bfdf808 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Fri, 20 Oct 2023 11:18:47 +0200 Subject: [PATCH 095/158] add understand cycles --- website/src/pages/understand/cycles.mdx | 130 ++++++++++++++++++++++++ 1 file changed, 130 insertions(+) create mode 100644 website/src/pages/understand/cycles.mdx diff --git a/website/src/pages/understand/cycles.mdx b/website/src/pages/understand/cycles.mdx new file mode 100644 index 00000000..a66794be --- /dev/null +++ b/website/src/pages/understand/cycles.mdx @@ -0,0 +1,130 @@ +--- +title: Understanding Cycles +layout: ../../layouts/MainLayout.astro +--- + +import { MiniRepl } from '../../docs/MiniRepl'; +import { PitchSlider } from '../../components/PitchSlider'; +import Box from '@components/Box.astro'; + +# Understanding Cycles + +The concept of cycles is very central to be able to understand how Strudel works. +Strudel's mother language, TidalCycles, even has it in its name. + +## Cycles and BPM + +In most music software, the unit BPM (beats per minute) is used to set the tempo. +Strudel expresses tempo as CPS (cycles per second), with a default of 1CPS: + + + +Here we can hear the 1CPS in action: The kick repeats once per second like a clock. +We could say 1CPS = 1BPS (beats per second) = 60BPM. Let's add another kick: + + + +Now we have 2 kicks per second, but the whole pattern still plays at 1CPS. +In terms of BPM, most musicians would tell you this is playing at 120bpm. +What about this one: + + + +Because the second sound is now a hihat, the tempo feels slower again. +This brings us to an important realization: + + + +Tempo is based on perception. +The choice of sounds also has an impact on the tempo feel. +This is why the same CPS can produce different perceived tempos. + + + +## Setting CPM + +If you're familiar with BPM, you can use the `cpm` method to set the tempo in cycles per minute: + + + +If you want to add more beats per cycle, you might want to divide the cpm: + + + +Or using 2 beats per cycle: + + + + + +To set a specific bpm, use `.cpm(bpm/bpc)` + +- bpm: the target beats per minute +- bpc: the number of perceived beats per cycle + + + +## Cycles and Bars + +Also in most music software, multiple beats form a bar (or measure). +The so called time signature specifies how many beats are in each bar. +In many types of music, it is common to use 4 beats per bar, also known as 4/4 time. +Many music programs use it as a default. + +Strudel does not a have concept of bars or measures, there are only cycles. +How you use them is up to you. Above, we've had this example: + + + +This could be interpreted as 4/4 time with a tempo of 110bpm. +We could write out multiple bars like this: + +\`).cpm(110/4)`} +/> + +Instead of writing out each bar separately, we could express this much shorter: + +>,hh*4").cpm(110/2)`} /> + +Here we can see that thinking in cycles rather than bars simplifies things a lot! +These types of simplifications work because of the repetitive nature of rhythm. +In computational terms, you could say the former notation has a lot of redundancy. + +## Time Signatures + +To get a time signature, just change the number of elements per bar. Here is a rhythm with 7 beats: + + + +or with 5: + +*5")`} /> + +We could also write multiple bars with different time signatures: + +\`).cpm(110*2)`} +/> + +Here we switch between 3/4 and 4/4, keeping the same tempo. + +If we don't specify the length, we get what's called a metric modulation: + +\`).cpm(110/2)`} +/> + +Now the 3 elements get the same time as the 4 elements, which is why the tempo changes. From f51c01dc88fed3d419958b2cbd2349b37f6b8598 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Fri, 20 Oct 2023 11:18:54 +0200 Subject: [PATCH 096/158] move stuff around --- website/src/config.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/website/src/config.ts b/website/src/config.ts index ba9b0666..0d774a5a 100644 --- a/website/src/config.ts +++ b/website/src/config.ts @@ -70,12 +70,10 @@ export const SIDEBAR: Sidebar = { { text: 'MIDI & OSC', link: 'learn/input-output' }, ], More: [ + { text: 'Recipes', link: 'recipes/recipes' }, { text: 'Mini-Notation', link: 'learn/mini-notation' }, - { text: 'Coding syntax', link: 'learn/code' }, { text: 'Offline', link: 'learn/pwa' }, { text: 'Patterns', link: 'technical-manual/patterns' }, - { text: 'Pattern Alignment', link: 'technical-manual/alignment' }, - { text: 'Strudel vs Tidal', link: 'learn/strudel-vs-tidal' }, { text: 'Music metadata', link: 'learn/metadata' }, { text: 'CSound', link: 'learn/csound' }, ], @@ -89,7 +87,13 @@ export const SIDEBAR: Sidebar = { { text: 'Accumulation', link: 'learn/accumulation' }, { text: 'Tonal Functions', link: 'learn/tonal' }, ], - Understand: [{ text: 'Pitch', link: 'understand/pitch' }], + Understand: [ + { text: 'Coding syntax', link: 'learn/code' }, + { text: 'Pitch', link: 'understand/pitch' }, + { text: 'Cycles', link: 'understand/cycles' }, + { text: 'Pattern Alignment', link: 'technical-manual/alignment' }, + { text: 'Strudel vs Tidal', link: 'learn/strudel-vs-tidal' }, + ], Development: [ { text: 'REPL', link: 'technical-manual/repl' }, { text: 'Sounds', link: 'technical-manual/sounds' }, From 80c98b6a35f94a4041ade36cbe345d35612109e5 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Fri, 20 Oct 2023 11:23:38 +0200 Subject: [PATCH 097/158] fix: loud example --- website/src/pages/recipes/recipes.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/src/pages/recipes/recipes.mdx b/website/src/pages/recipes/recipes.mdx index 5e5ae9bf..2e4765a8 100644 --- a/website/src/pages/recipes/recipes.mdx +++ b/website/src/pages/recipes/recipes.mdx @@ -282,7 +282,7 @@ or decay / sustain You can loop a sample with `loop` / `loopEnd`: -").s("bd").loop(1).loopEnd(.05)`} /> +").s("bd").loop(1).loopEnd(.05).gain(.2)`} /> This allows us to play the first 5% of the bass drum as a synth! To simplify loading wavetables, any sample that starts with `wt_` will be looped automatically: From 2cc60c6ee2b97e66444aa430ac5cc33b9ea58b11 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Fri, 20 Oct 2023 11:28:35 +0200 Subject: [PATCH 098/158] format --- website/src/pages/recipes/recipes.mdx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/website/src/pages/recipes/recipes.mdx b/website/src/pages/recipes/recipes.mdx index 2e4765a8..22617e4b 100644 --- a/website/src/pages/recipes/recipes.mdx +++ b/website/src/pages/recipes/recipes.mdx @@ -303,11 +303,10 @@ note("c2*8").s("wt_dbass").n(run(8))`} ...adding a filter envelope + reverb: - \ No newline at end of file +/> From 1b072448c4a47b2f7b090b57725554f9d409baff Mon Sep 17 00:00:00 2001 From: "Alexandre G.-Raymond" Date: Fri, 20 Oct 2023 21:59:54 +0200 Subject: [PATCH 099/158] Add test snapshots for shabda shortcut --- test/__snapshots__/examples.test.mjs.snap | 36 +++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/test/__snapshots__/examples.test.mjs.snap b/test/__snapshots__/examples.test.mjs.snap index f351a5a0..c6410733 100644 --- a/test/__snapshots__/examples.test.mjs.snap +++ b/test/__snapshots__/examples.test.mjs.snap @@ -3956,6 +3956,42 @@ exports[`runs examples > example "samples" example index 1 1`] = ` ] `; +exports[`runs examples > example "samples" example index 2 1`] = ` +[ + "[ 0/1 → 1/2 | s:noise ]", + "[ 1/2 → 3/4 | s:chimp n:0 ]", + "[ 3/4 → 1/1 | s:chimp n:0 ]", + "[ 1/1 → 3/2 | s:noise ]", + "[ 3/2 → 2/1 | s:chimp n:1 ]", + "[ 2/1 → 5/2 | s:noise ]", + "[ 5/2 → 11/4 | s:chimp n:0 ]", + "[ 11/4 → 3/1 | s:chimp n:0 ]", + "[ 3/1 → 7/2 | s:noise ]", + "[ 7/2 → 4/1 | s:chimp n:1 ]", +] +`; + +exports[`runs examples > example "samples" example index 3 1`] = ` +[ + "[ 0/1 → 1/4 | s:chocolat ]", + "[ 1/4 → 1/2 | s:chocolat ]", + "[ 1/2 → 3/4 | s:chocolat ]", + "[ 3/4 → 1/1 | s:chocolat ]", + "[ 1/1 → 5/4 | s:chocolat ]", + "[ 5/4 → 3/2 | s:chocolat ]", + "[ 3/2 → 7/4 | s:chocolat ]", + "[ 7/4 → 2/1 | s:chocolat ]", + "[ 2/1 → 9/4 | s:chocolat ]", + "[ 9/4 → 5/2 | s:chocolat ]", + "[ 5/2 → 11/4 | s:chocolat ]", + "[ 11/4 → 3/1 | s:chocolat ]", + "[ 3/1 → 13/4 | s:chocolat ]", + "[ 13/4 → 7/2 | s:chocolat ]", + "[ 7/2 → 15/4 | s:chocolat ]", + "[ 15/4 → 4/1 | s:chocolat ]", +] +`; + exports[`runs examples > example "saw" example index 0 1`] = ` [ "[ 0/1 → 1/4 | note:c3 clip:0.03125 ]", From a23765a1cb47a1f122821613d14bc74ac31475d2 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Fri, 20 Oct 2023 22:30:54 +0200 Subject: [PATCH 100/158] fix: trailing slash confusion --- website/astro.config.mjs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/website/astro.config.mjs b/website/astro.config.mjs index bd018ddd..7a340516 100644 --- a/website/astro.config.mjs +++ b/website/astro.config.mjs @@ -45,6 +45,10 @@ const options = { // https://astro.build/config export default defineConfig({ + trailingSlash: 'always', + build: { + format: 'directory', + }, integrations: [ react(), mdx(options), From 624e68521277df2bde4fa7b259cda1e3719ec3d1 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Fri, 20 Oct 2023 22:32:12 +0200 Subject: [PATCH 101/158] fix: trailing slash confusion --- website/astro.config.mjs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/website/astro.config.mjs b/website/astro.config.mjs index bd018ddd..7a340516 100644 --- a/website/astro.config.mjs +++ b/website/astro.config.mjs @@ -45,6 +45,10 @@ const options = { // https://astro.build/config export default defineConfig({ + trailingSlash: 'always', + build: { + format: 'directory', + }, integrations: [ react(), mdx(options), From 8f4344f4130b7e5681a71da829246afa08c34062 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Fri, 20 Oct 2023 22:38:46 +0200 Subject: [PATCH 102/158] fix: try different trailing slash behavior --- website/astro.config.mjs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/website/astro.config.mjs b/website/astro.config.mjs index 7a340516..c9585ebe 100644 --- a/website/astro.config.mjs +++ b/website/astro.config.mjs @@ -45,9 +45,9 @@ const options = { // https://astro.build/config export default defineConfig({ - trailingSlash: 'always', + trailingSlash: 'never', build: { - format: 'directory', + format: 'file', }, integrations: [ react(), From 46cdbd514fa2fcf57656ddedff9271cb2558bde1 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Sat, 21 Oct 2023 00:18:41 +0200 Subject: [PATCH 103/158] completely revert config mess --- website/astro.config.mjs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/website/astro.config.mjs b/website/astro.config.mjs index c9585ebe..bd018ddd 100644 --- a/website/astro.config.mjs +++ b/website/astro.config.mjs @@ -45,10 +45,6 @@ const options = { // https://astro.build/config export default defineConfig({ - trailingSlash: 'never', - build: { - format: 'file', - }, integrations: [ react(), mdx(options), From 072ae7280eff1aa2124a6b7cd37d68dfd5d513a4 Mon Sep 17 00:00:00 2001 From: "Alexandre G.-Raymond" Date: Sat, 21 Oct 2023 22:26:12 +0200 Subject: [PATCH 104/158] Fix krill build command in README --- packages/mini/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/mini/README.md b/packages/mini/README.md index 3135c286..2527352f 100644 --- a/packages/mini/README.md +++ b/packages/mini/README.md @@ -40,5 +40,5 @@ The parser [krill-parser.js] is generated from [krill.pegjs](./krill.pegjs) usin To generate the parser, run ```js -npm build:parser +npm run build:parser ``` From cb8edd9b8f1f4b2136d6321cb09fcf8d80c1c5dc Mon Sep 17 00:00:00 2001 From: "Alexandre G.-Raymond" Date: Sat, 21 Oct 2023 22:51:20 +0200 Subject: [PATCH 105/158] Support international alphabets in mininotation --- packages/mini/krill-parser.js | 170 +++++++++++++++++++++++++++++----- packages/mini/krill.pegjs | 24 ++++- website/src/repl/Repl.jsx | 10 +- 3 files changed, 176 insertions(+), 28 deletions(-) diff --git a/packages/mini/krill-parser.js b/packages/mini/krill-parser.js index 7831087b..4631175a 100644 --- a/packages/mini/krill-parser.js +++ b/packages/mini/krill-parser.js @@ -220,8 +220,14 @@ function peg$parse(input, options) { var peg$r1 = /^[eE]/; var peg$r2 = /^[0-9]/; var peg$r3 = /^[ \n\r\t]/; - var peg$r4 = /^[0-9a-zA-Z~]/; + var peg$r4 = /^[0-9~]/; var peg$r5 = /^[^\n]/; + var peg$r6 = /^[a-z\xB5\xDF-\xF6\xF8-\xFF\u0101\u0103\u0105\u0107\u0109\u010B\u010D\u010F\u0111\u0113\u0115\u0117\u0119\u011B\u011D\u011F\u0121\u0123\u0125\u0127\u0129\u012B\u012D\u012F\u0131\u0133\u0135\u0137-\u0138\u013A\u013C\u013E\u0140\u0142\u0144\u0146\u0148-\u0149\u014B\u014D\u014F\u0151\u0153\u0155\u0157\u0159\u015B\u015D\u015F\u0161\u0163\u0165\u0167\u0169\u016B\u016D\u016F\u0171\u0173\u0175\u0177\u017A\u017C\u017E-\u0180\u0183\u0185\u0188\u018C-\u018D\u0192\u0195\u0199-\u019B\u019E\u01A1\u01A3\u01A5\u01A8\u01AA-\u01AB\u01AD\u01B0\u01B4\u01B6\u01B9-\u01BA\u01BD-\u01BF\u01C6\u01C9\u01CC\u01CE\u01D0\u01D2\u01D4\u01D6\u01D8\u01DA\u01DC-\u01DD\u01DF\u01E1\u01E3\u01E5\u01E7\u01E9\u01EB\u01ED\u01EF-\u01F0\u01F3\u01F5\u01F9\u01FB\u01FD\u01FF\u0201\u0203\u0205\u0207\u0209\u020B\u020D\u020F\u0211\u0213\u0215\u0217\u0219\u021B\u021D\u021F\u0221\u0223\u0225\u0227\u0229\u022B\u022D\u022F\u0231\u0233-\u0239\u023C\u023F-\u0240\u0242\u0247\u0249\u024B\u024D\u024F-\u0293\u0295-\u02AF\u0371\u0373\u0377\u037B-\u037D\u0390\u03AC-\u03CE\u03D0-\u03D1\u03D5-\u03D7\u03D9\u03DB\u03DD\u03DF\u03E1\u03E3\u03E5\u03E7\u03E9\u03EB\u03ED\u03EF-\u03F3\u03F5\u03F8\u03FB-\u03FC\u0430-\u045F\u0461\u0463\u0465\u0467\u0469\u046B\u046D\u046F\u0471\u0473\u0475\u0477\u0479\u047B\u047D\u047F\u0481\u048B\u048D\u048F\u0491\u0493\u0495\u0497\u0499\u049B\u049D\u049F\u04A1\u04A3\u04A5\u04A7\u04A9\u04AB\u04AD\u04AF\u04B1\u04B3\u04B5\u04B7\u04B9\u04BB\u04BD\u04BF\u04C2\u04C4\u04C6\u04C8\u04CA\u04CC\u04CE-\u04CF\u04D1\u04D3\u04D5\u04D7\u04D9\u04DB\u04DD\u04DF\u04E1\u04E3\u04E5\u04E7\u04E9\u04EB\u04ED\u04EF\u04F1\u04F3\u04F5\u04F7\u04F9\u04FB\u04FD\u04FF\u0501\u0503\u0505\u0507\u0509\u050B\u050D\u050F\u0511\u0513\u0515\u0517\u0519\u051B\u051D\u051F\u0521\u0523\u0525\u0527\u0529\u052B\u052D\u052F\u0560-\u0588\u10D0-\u10FA\u10FD-\u10FF\u13F8-\u13FD\u1C80-\u1C88\u1D00-\u1D2B\u1D6B-\u1D77\u1D79-\u1D9A\u1E01\u1E03\u1E05\u1E07\u1E09\u1E0B\u1E0D\u1E0F\u1E11\u1E13\u1E15\u1E17\u1E19\u1E1B\u1E1D\u1E1F\u1E21\u1E23\u1E25\u1E27\u1E29\u1E2B\u1E2D\u1E2F\u1E31\u1E33\u1E35\u1E37\u1E39\u1E3B\u1E3D\u1E3F\u1E41\u1E43\u1E45\u1E47\u1E49\u1E4B\u1E4D\u1E4F\u1E51\u1E53\u1E55\u1E57\u1E59\u1E5B\u1E5D\u1E5F\u1E61\u1E63\u1E65\u1E67\u1E69\u1E6B\u1E6D\u1E6F\u1E71\u1E73\u1E75\u1E77\u1E79\u1E7B\u1E7D\u1E7F\u1E81\u1E83\u1E85\u1E87\u1E89\u1E8B\u1E8D\u1E8F\u1E91\u1E93\u1E95-\u1E9D\u1E9F\u1EA1\u1EA3\u1EA5\u1EA7\u1EA9\u1EAB\u1EAD\u1EAF\u1EB1\u1EB3\u1EB5\u1EB7\u1EB9\u1EBB\u1EBD\u1EBF\u1EC1\u1EC3\u1EC5\u1EC7\u1EC9\u1ECB\u1ECD\u1ECF\u1ED1\u1ED3\u1ED5\u1ED7\u1ED9\u1EDB\u1EDD\u1EDF\u1EE1\u1EE3\u1EE5\u1EE7\u1EE9\u1EEB\u1EED\u1EEF\u1EF1\u1EF3\u1EF5\u1EF7\u1EF9\u1EFB\u1EFD\u1EFF-\u1F07\u1F10-\u1F15\u1F20-\u1F27\u1F30-\u1F37\u1F40-\u1F45\u1F50-\u1F57\u1F60-\u1F67\u1F70-\u1F7D\u1F80-\u1F87\u1F90-\u1F97\u1FA0-\u1FA7\u1FB0-\u1FB4\u1FB6-\u1FB7\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FC7\u1FD0-\u1FD3\u1FD6-\u1FD7\u1FE0-\u1FE7\u1FF2-\u1FF4\u1FF6-\u1FF7\u210A\u210E-\u210F\u2113\u212F\u2134\u2139\u213C-\u213D\u2146-\u2149\u214E\u2184\u2C30-\u2C5E\u2C61\u2C65-\u2C66\u2C68\u2C6A\u2C6C\u2C71\u2C73-\u2C74\u2C76-\u2C7B\u2C81\u2C83\u2C85\u2C87\u2C89\u2C8B\u2C8D\u2C8F\u2C91\u2C93\u2C95\u2C97\u2C99\u2C9B\u2C9D\u2C9F\u2CA1\u2CA3\u2CA5\u2CA7\u2CA9\u2CAB\u2CAD\u2CAF\u2CB1\u2CB3\u2CB5\u2CB7\u2CB9\u2CBB\u2CBD\u2CBF\u2CC1\u2CC3\u2CC5\u2CC7\u2CC9\u2CCB\u2CCD\u2CCF\u2CD1\u2CD3\u2CD5\u2CD7\u2CD9\u2CDB\u2CDD\u2CDF\u2CE1\u2CE3-\u2CE4\u2CEC\u2CEE\u2CF3\u2D00-\u2D25\u2D27\u2D2D\uA641\uA643\uA645\uA647\uA649\uA64B\uA64D\uA64F\uA651\uA653\uA655\uA657\uA659\uA65B\uA65D\uA65F\uA661\uA663\uA665\uA667\uA669\uA66B\uA66D\uA681\uA683\uA685\uA687\uA689\uA68B\uA68D\uA68F\uA691\uA693\uA695\uA697\uA699\uA69B\uA723\uA725\uA727\uA729\uA72B\uA72D\uA72F-\uA731\uA733\uA735\uA737\uA739\uA73B\uA73D\uA73F\uA741\uA743\uA745\uA747\uA749\uA74B\uA74D\uA74F\uA751\uA753\uA755\uA757\uA759\uA75B\uA75D\uA75F\uA761\uA763\uA765\uA767\uA769\uA76B\uA76D\uA76F\uA771-\uA778\uA77A\uA77C\uA77F\uA781\uA783\uA785\uA787\uA78C\uA78E\uA791\uA793-\uA795\uA797\uA799\uA79B\uA79D\uA79F\uA7A1\uA7A3\uA7A5\uA7A7\uA7A9\uA7AF\uA7B5\uA7B7\uA7B9\uA7FA\uAB30-\uAB5A\uAB60-\uAB65\uAB70-\uABBF\uFB00-\uFB06\uFB13-\uFB17\uFF41-\uFF5A]/; + var peg$r7 = /^[\u02B0-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0374\u037A\u0559\u0640\u06E5-\u06E6\u07F4-\u07F5\u07FA\u081A\u0824\u0828\u0971\u0E46\u0EC6\u10FC\u17D7\u1843\u1AA7\u1C78-\u1C7D\u1D2C-\u1D6A\u1D78\u1D9B-\u1DBF\u2071\u207F\u2090-\u209C\u2C7C-\u2C7D\u2D6F\u2E2F\u3005\u3031-\u3035\u303B\u309D-\u309E\u30FC-\u30FE\uA015\uA4F8-\uA4FD\uA60C\uA67F\uA69C-\uA69D\uA717-\uA71F\uA770\uA788\uA7F8-\uA7F9\uA9CF\uA9E6\uAA70\uAADD\uAAF3-\uAAF4\uAB5C-\uAB5F\uFF70\uFF9E-\uFF9F]/; + var peg$r8 = /^[\xAA\xBA\u01BB\u01C0-\u01C3\u0294\u05D0-\u05EA\u05EF-\u05F2\u0620-\u063F\u0641-\u064A\u066E-\u066F\u0671-\u06D3\u06D5\u06EE-\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u0800-\u0815\u0840-\u0858\u0860-\u086A\u08A0-\u08B4\u08B6-\u08BD\u0904-\u0939\u093D\u0950\u0958-\u0961\u0972-\u0980\u0985-\u098C\u098F-\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC-\u09DD\u09DF-\u09E1\u09F0-\u09F1\u09FC\u0A05-\u0A0A\u0A0F-\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32-\u0A33\u0A35-\u0A36\u0A38-\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2-\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0-\u0AE1\u0AF9\u0B05-\u0B0C\u0B0F-\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32-\u0B33\u0B35-\u0B39\u0B3D\u0B5C-\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99-\u0B9A\u0B9C\u0B9E-\u0B9F\u0BA3-\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D\u0C58-\u0C5A\u0C60-\u0C61\u0C80\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0-\u0CE1\u0CF1-\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D54-\u0D56\u0D5F-\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32-\u0E33\u0E40-\u0E45\u0E81-\u0E82\u0E84\u0E87-\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA-\u0EAB\u0EAD-\u0EB0\u0EB2-\u0EB3\u0EBD\u0EC0-\u0EC4\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065-\u1066\u106E-\u1070\u1075-\u1081\u108E\u1100-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16F1-\u16F8\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17DC\u1820-\u1842\u1844-\u1878\u1880-\u1884\u1887-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191E\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u1A00-\u1A16\u1A20-\u1A54\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE-\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C77\u1CE9-\u1CEC\u1CEE-\u1CF1\u1CF5-\u1CF6\u2135-\u2138\u2D30-\u2D67\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u3006\u303C\u3041-\u3096\u309F\u30A1-\u30FA\u30FF\u3105-\u312F\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FEF\uA000-\uA014\uA016-\uA48C\uA4D0-\uA4F7\uA500-\uA60B\uA610-\uA61F\uA62A-\uA62B\uA66E\uA6A0-\uA6E5\uA78F\uA7F7\uA7FB-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA8FD-\uA8FE\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9E0-\uA9E4\uA9E7-\uA9EF\uA9FA-\uA9FE\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA6F\uAA71-\uAA76\uAA7A\uAA7E-\uAAAF\uAAB1\uAAB5-\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADC\uAAE0-\uAAEA\uAAF2\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uABC0-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40-\uFB41\uFB43-\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF66-\uFF6F\uFF71-\uFF9D\uFFA0-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]/; + var peg$r9 = /^[\u01C5\u01C8\u01CB\u01F2\u1F88-\u1F8F\u1F98-\u1F9F\u1FA8-\u1FAF\u1FBC\u1FCC\u1FFC]/; + var peg$r10 = /^[A-Z\xC0-\xD6\xD8-\xDE\u0100\u0102\u0104\u0106\u0108\u010A\u010C\u010E\u0110\u0112\u0114\u0116\u0118\u011A\u011C\u011E\u0120\u0122\u0124\u0126\u0128\u012A\u012C\u012E\u0130\u0132\u0134\u0136\u0139\u013B\u013D\u013F\u0141\u0143\u0145\u0147\u014A\u014C\u014E\u0150\u0152\u0154\u0156\u0158\u015A\u015C\u015E\u0160\u0162\u0164\u0166\u0168\u016A\u016C\u016E\u0170\u0172\u0174\u0176\u0178-\u0179\u017B\u017D\u0181-\u0182\u0184\u0186-\u0187\u0189-\u018B\u018E-\u0191\u0193-\u0194\u0196-\u0198\u019C-\u019D\u019F-\u01A0\u01A2\u01A4\u01A6-\u01A7\u01A9\u01AC\u01AE-\u01AF\u01B1-\u01B3\u01B5\u01B7-\u01B8\u01BC\u01C4\u01C7\u01CA\u01CD\u01CF\u01D1\u01D3\u01D5\u01D7\u01D9\u01DB\u01DE\u01E0\u01E2\u01E4\u01E6\u01E8\u01EA\u01EC\u01EE\u01F1\u01F4\u01F6-\u01F8\u01FA\u01FC\u01FE\u0200\u0202\u0204\u0206\u0208\u020A\u020C\u020E\u0210\u0212\u0214\u0216\u0218\u021A\u021C\u021E\u0220\u0222\u0224\u0226\u0228\u022A\u022C\u022E\u0230\u0232\u023A-\u023B\u023D-\u023E\u0241\u0243-\u0246\u0248\u024A\u024C\u024E\u0370\u0372\u0376\u037F\u0386\u0388-\u038A\u038C\u038E-\u038F\u0391-\u03A1\u03A3-\u03AB\u03CF\u03D2-\u03D4\u03D8\u03DA\u03DC\u03DE\u03E0\u03E2\u03E4\u03E6\u03E8\u03EA\u03EC\u03EE\u03F4\u03F7\u03F9-\u03FA\u03FD-\u042F\u0460\u0462\u0464\u0466\u0468\u046A\u046C\u046E\u0470\u0472\u0474\u0476\u0478\u047A\u047C\u047E\u0480\u048A\u048C\u048E\u0490\u0492\u0494\u0496\u0498\u049A\u049C\u049E\u04A0\u04A2\u04A4\u04A6\u04A8\u04AA\u04AC\u04AE\u04B0\u04B2\u04B4\u04B6\u04B8\u04BA\u04BC\u04BE\u04C0-\u04C1\u04C3\u04C5\u04C7\u04C9\u04CB\u04CD\u04D0\u04D2\u04D4\u04D6\u04D8\u04DA\u04DC\u04DE\u04E0\u04E2\u04E4\u04E6\u04E8\u04EA\u04EC\u04EE\u04F0\u04F2\u04F4\u04F6\u04F8\u04FA\u04FC\u04FE\u0500\u0502\u0504\u0506\u0508\u050A\u050C\u050E\u0510\u0512\u0514\u0516\u0518\u051A\u051C\u051E\u0520\u0522\u0524\u0526\u0528\u052A\u052C\u052E\u0531-\u0556\u10A0-\u10C5\u10C7\u10CD\u13A0-\u13F5\u1C90-\u1CBA\u1CBD-\u1CBF\u1E00\u1E02\u1E04\u1E06\u1E08\u1E0A\u1E0C\u1E0E\u1E10\u1E12\u1E14\u1E16\u1E18\u1E1A\u1E1C\u1E1E\u1E20\u1E22\u1E24\u1E26\u1E28\u1E2A\u1E2C\u1E2E\u1E30\u1E32\u1E34\u1E36\u1E38\u1E3A\u1E3C\u1E3E\u1E40\u1E42\u1E44\u1E46\u1E48\u1E4A\u1E4C\u1E4E\u1E50\u1E52\u1E54\u1E56\u1E58\u1E5A\u1E5C\u1E5E\u1E60\u1E62\u1E64\u1E66\u1E68\u1E6A\u1E6C\u1E6E\u1E70\u1E72\u1E74\u1E76\u1E78\u1E7A\u1E7C\u1E7E\u1E80\u1E82\u1E84\u1E86\u1E88\u1E8A\u1E8C\u1E8E\u1E90\u1E92\u1E94\u1E9E\u1EA0\u1EA2\u1EA4\u1EA6\u1EA8\u1EAA\u1EAC\u1EAE\u1EB0\u1EB2\u1EB4\u1EB6\u1EB8\u1EBA\u1EBC\u1EBE\u1EC0\u1EC2\u1EC4\u1EC6\u1EC8\u1ECA\u1ECC\u1ECE\u1ED0\u1ED2\u1ED4\u1ED6\u1ED8\u1EDA\u1EDC\u1EDE\u1EE0\u1EE2\u1EE4\u1EE6\u1EE8\u1EEA\u1EEC\u1EEE\u1EF0\u1EF2\u1EF4\u1EF6\u1EF8\u1EFA\u1EFC\u1EFE\u1F08-\u1F0F\u1F18-\u1F1D\u1F28-\u1F2F\u1F38-\u1F3F\u1F48-\u1F4D\u1F59\u1F5B\u1F5D\u1F5F\u1F68-\u1F6F\u1FB8-\u1FBB\u1FC8-\u1FCB\u1FD8-\u1FDB\u1FE8-\u1FEC\u1FF8-\u1FFB\u2102\u2107\u210B-\u210D\u2110-\u2112\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u2130-\u2133\u213E-\u213F\u2145\u2183\u2C00-\u2C2E\u2C60\u2C62-\u2C64\u2C67\u2C69\u2C6B\u2C6D-\u2C70\u2C72\u2C75\u2C7E-\u2C80\u2C82\u2C84\u2C86\u2C88\u2C8A\u2C8C\u2C8E\u2C90\u2C92\u2C94\u2C96\u2C98\u2C9A\u2C9C\u2C9E\u2CA0\u2CA2\u2CA4\u2CA6\u2CA8\u2CAA\u2CAC\u2CAE\u2CB0\u2CB2\u2CB4\u2CB6\u2CB8\u2CBA\u2CBC\u2CBE\u2CC0\u2CC2\u2CC4\u2CC6\u2CC8\u2CCA\u2CCC\u2CCE\u2CD0\u2CD2\u2CD4\u2CD6\u2CD8\u2CDA\u2CDC\u2CDE\u2CE0\u2CE2\u2CEB\u2CED\u2CF2\uA640\uA642\uA644\uA646\uA648\uA64A\uA64C\uA64E\uA650\uA652\uA654\uA656\uA658\uA65A\uA65C\uA65E\uA660\uA662\uA664\uA666\uA668\uA66A\uA66C\uA680\uA682\uA684\uA686\uA688\uA68A\uA68C\uA68E\uA690\uA692\uA694\uA696\uA698\uA69A\uA722\uA724\uA726\uA728\uA72A\uA72C\uA72E\uA732\uA734\uA736\uA738\uA73A\uA73C\uA73E\uA740\uA742\uA744\uA746\uA748\uA74A\uA74C\uA74E\uA750\uA752\uA754\uA756\uA758\uA75A\uA75C\uA75E\uA760\uA762\uA764\uA766\uA768\uA76A\uA76C\uA76E\uA779\uA77B\uA77D-\uA77E\uA780\uA782\uA784\uA786\uA78B\uA78D\uA790\uA792\uA796\uA798\uA79A\uA79C\uA79E\uA7A0\uA7A2\uA7A4\uA7A6\uA7A8\uA7AA-\uA7AE\uA7B0-\uA7B4\uA7B6\uA7B8\uFF21-\uFF3A]/; + var peg$r11 = /^[\u16EE-\u16F0\u2160-\u2182\u2185-\u2188\u3007\u3021-\u3029\u3038-\u303A\uA6E6-\uA6EF]/; var peg$e0 = peg$otherExpectation("number"); var peg$e1 = peg$literalExpectation(".", false); @@ -237,7 +243,7 @@ function peg$parse(input, options) { var peg$e11 = peg$literalExpectation("|", false); var peg$e12 = peg$literalExpectation("\"", false); var peg$e13 = peg$literalExpectation("'", false); - var peg$e14 = peg$classExpectation([["0", "9"], ["a", "z"], ["A", "Z"], "~"], false, false); + var peg$e14 = peg$classExpectation([["0", "9"], "~"], false, false); var peg$e15 = peg$literalExpectation("#", false); var peg$e16 = peg$literalExpectation("^", false); var peg$e17 = peg$literalExpectation("_", false); @@ -272,6 +278,12 @@ function peg$parse(input, options) { var peg$e46 = peg$literalExpectation("setcps", false); var peg$e47 = peg$literalExpectation("setbpm", false); var peg$e48 = peg$literalExpectation("hush", false); + var peg$e49 = peg$classExpectation([["a", "z"], "\xB5", ["\xDF", "\xF6"], ["\xF8", "\xFF"], "\u0101", "\u0103", "\u0105", "\u0107", "\u0109", "\u010B", "\u010D", "\u010F", "\u0111", "\u0113", "\u0115", "\u0117", "\u0119", "\u011B", "\u011D", "\u011F", "\u0121", "\u0123", "\u0125", "\u0127", "\u0129", "\u012B", "\u012D", "\u012F", "\u0131", "\u0133", "\u0135", ["\u0137", "\u0138"], "\u013A", "\u013C", "\u013E", "\u0140", "\u0142", "\u0144", "\u0146", ["\u0148", "\u0149"], "\u014B", "\u014D", "\u014F", "\u0151", "\u0153", "\u0155", "\u0157", "\u0159", "\u015B", "\u015D", "\u015F", "\u0161", "\u0163", "\u0165", "\u0167", "\u0169", "\u016B", "\u016D", "\u016F", "\u0171", "\u0173", "\u0175", "\u0177", "\u017A", "\u017C", ["\u017E", "\u0180"], "\u0183", "\u0185", "\u0188", ["\u018C", "\u018D"], "\u0192", "\u0195", ["\u0199", "\u019B"], "\u019E", "\u01A1", "\u01A3", "\u01A5", "\u01A8", ["\u01AA", "\u01AB"], "\u01AD", "\u01B0", "\u01B4", "\u01B6", ["\u01B9", "\u01BA"], ["\u01BD", "\u01BF"], "\u01C6", "\u01C9", "\u01CC", "\u01CE", "\u01D0", "\u01D2", "\u01D4", "\u01D6", "\u01D8", "\u01DA", ["\u01DC", "\u01DD"], "\u01DF", "\u01E1", "\u01E3", "\u01E5", "\u01E7", "\u01E9", "\u01EB", "\u01ED", ["\u01EF", "\u01F0"], "\u01F3", "\u01F5", "\u01F9", "\u01FB", "\u01FD", "\u01FF", "\u0201", "\u0203", "\u0205", "\u0207", "\u0209", "\u020B", "\u020D", "\u020F", "\u0211", "\u0213", "\u0215", "\u0217", "\u0219", "\u021B", "\u021D", "\u021F", "\u0221", "\u0223", "\u0225", "\u0227", "\u0229", "\u022B", "\u022D", "\u022F", "\u0231", ["\u0233", "\u0239"], "\u023C", ["\u023F", "\u0240"], "\u0242", "\u0247", "\u0249", "\u024B", "\u024D", ["\u024F", "\u0293"], ["\u0295", "\u02AF"], "\u0371", "\u0373", "\u0377", ["\u037B", "\u037D"], "\u0390", ["\u03AC", "\u03CE"], ["\u03D0", "\u03D1"], ["\u03D5", "\u03D7"], "\u03D9", "\u03DB", "\u03DD", "\u03DF", "\u03E1", "\u03E3", "\u03E5", "\u03E7", "\u03E9", "\u03EB", "\u03ED", ["\u03EF", "\u03F3"], "\u03F5", "\u03F8", ["\u03FB", "\u03FC"], ["\u0430", "\u045F"], "\u0461", "\u0463", "\u0465", "\u0467", "\u0469", "\u046B", "\u046D", "\u046F", "\u0471", "\u0473", "\u0475", "\u0477", "\u0479", "\u047B", "\u047D", "\u047F", "\u0481", "\u048B", "\u048D", "\u048F", "\u0491", "\u0493", "\u0495", "\u0497", "\u0499", "\u049B", "\u049D", "\u049F", "\u04A1", "\u04A3", "\u04A5", "\u04A7", "\u04A9", "\u04AB", "\u04AD", "\u04AF", "\u04B1", "\u04B3", "\u04B5", "\u04B7", "\u04B9", "\u04BB", "\u04BD", "\u04BF", "\u04C2", "\u04C4", "\u04C6", "\u04C8", "\u04CA", "\u04CC", ["\u04CE", "\u04CF"], "\u04D1", "\u04D3", "\u04D5", "\u04D7", "\u04D9", "\u04DB", "\u04DD", "\u04DF", "\u04E1", "\u04E3", "\u04E5", "\u04E7", "\u04E9", "\u04EB", "\u04ED", "\u04EF", "\u04F1", "\u04F3", "\u04F5", "\u04F7", "\u04F9", "\u04FB", "\u04FD", "\u04FF", "\u0501", "\u0503", "\u0505", "\u0507", "\u0509", "\u050B", "\u050D", "\u050F", "\u0511", "\u0513", "\u0515", "\u0517", "\u0519", "\u051B", "\u051D", "\u051F", "\u0521", "\u0523", "\u0525", "\u0527", "\u0529", "\u052B", "\u052D", "\u052F", ["\u0560", "\u0588"], ["\u10D0", "\u10FA"], ["\u10FD", "\u10FF"], ["\u13F8", "\u13FD"], ["\u1C80", "\u1C88"], ["\u1D00", "\u1D2B"], ["\u1D6B", "\u1D77"], ["\u1D79", "\u1D9A"], "\u1E01", "\u1E03", "\u1E05", "\u1E07", "\u1E09", "\u1E0B", "\u1E0D", "\u1E0F", "\u1E11", "\u1E13", "\u1E15", "\u1E17", "\u1E19", "\u1E1B", "\u1E1D", "\u1E1F", "\u1E21", "\u1E23", "\u1E25", "\u1E27", "\u1E29", "\u1E2B", "\u1E2D", "\u1E2F", "\u1E31", "\u1E33", "\u1E35", "\u1E37", "\u1E39", "\u1E3B", "\u1E3D", "\u1E3F", "\u1E41", "\u1E43", "\u1E45", "\u1E47", "\u1E49", "\u1E4B", "\u1E4D", "\u1E4F", "\u1E51", "\u1E53", "\u1E55", "\u1E57", "\u1E59", "\u1E5B", "\u1E5D", "\u1E5F", "\u1E61", "\u1E63", "\u1E65", "\u1E67", "\u1E69", "\u1E6B", "\u1E6D", "\u1E6F", "\u1E71", "\u1E73", "\u1E75", "\u1E77", "\u1E79", "\u1E7B", "\u1E7D", "\u1E7F", "\u1E81", "\u1E83", "\u1E85", "\u1E87", "\u1E89", "\u1E8B", "\u1E8D", "\u1E8F", "\u1E91", "\u1E93", ["\u1E95", "\u1E9D"], "\u1E9F", "\u1EA1", "\u1EA3", "\u1EA5", "\u1EA7", "\u1EA9", "\u1EAB", "\u1EAD", "\u1EAF", "\u1EB1", "\u1EB3", "\u1EB5", "\u1EB7", "\u1EB9", "\u1EBB", "\u1EBD", "\u1EBF", "\u1EC1", "\u1EC3", "\u1EC5", "\u1EC7", "\u1EC9", "\u1ECB", "\u1ECD", "\u1ECF", "\u1ED1", "\u1ED3", "\u1ED5", "\u1ED7", "\u1ED9", "\u1EDB", "\u1EDD", "\u1EDF", "\u1EE1", "\u1EE3", "\u1EE5", "\u1EE7", "\u1EE9", "\u1EEB", "\u1EED", "\u1EEF", "\u1EF1", "\u1EF3", "\u1EF5", "\u1EF7", "\u1EF9", "\u1EFB", "\u1EFD", ["\u1EFF", "\u1F07"], ["\u1F10", "\u1F15"], ["\u1F20", "\u1F27"], ["\u1F30", "\u1F37"], ["\u1F40", "\u1F45"], ["\u1F50", "\u1F57"], ["\u1F60", "\u1F67"], ["\u1F70", "\u1F7D"], ["\u1F80", "\u1F87"], ["\u1F90", "\u1F97"], ["\u1FA0", "\u1FA7"], ["\u1FB0", "\u1FB4"], ["\u1FB6", "\u1FB7"], "\u1FBE", ["\u1FC2", "\u1FC4"], ["\u1FC6", "\u1FC7"], ["\u1FD0", "\u1FD3"], ["\u1FD6", "\u1FD7"], ["\u1FE0", "\u1FE7"], ["\u1FF2", "\u1FF4"], ["\u1FF6", "\u1FF7"], "\u210A", ["\u210E", "\u210F"], "\u2113", "\u212F", "\u2134", "\u2139", ["\u213C", "\u213D"], ["\u2146", "\u2149"], "\u214E", "\u2184", ["\u2C30", "\u2C5E"], "\u2C61", ["\u2C65", "\u2C66"], "\u2C68", "\u2C6A", "\u2C6C", "\u2C71", ["\u2C73", "\u2C74"], ["\u2C76", "\u2C7B"], "\u2C81", "\u2C83", "\u2C85", "\u2C87", "\u2C89", "\u2C8B", "\u2C8D", "\u2C8F", "\u2C91", "\u2C93", "\u2C95", "\u2C97", "\u2C99", "\u2C9B", "\u2C9D", "\u2C9F", "\u2CA1", "\u2CA3", "\u2CA5", "\u2CA7", "\u2CA9", "\u2CAB", "\u2CAD", "\u2CAF", "\u2CB1", "\u2CB3", "\u2CB5", "\u2CB7", "\u2CB9", "\u2CBB", "\u2CBD", "\u2CBF", "\u2CC1", "\u2CC3", "\u2CC5", "\u2CC7", "\u2CC9", "\u2CCB", "\u2CCD", "\u2CCF", "\u2CD1", "\u2CD3", "\u2CD5", "\u2CD7", "\u2CD9", "\u2CDB", "\u2CDD", "\u2CDF", "\u2CE1", ["\u2CE3", "\u2CE4"], "\u2CEC", "\u2CEE", "\u2CF3", ["\u2D00", "\u2D25"], "\u2D27", "\u2D2D", "\uA641", "\uA643", "\uA645", "\uA647", "\uA649", "\uA64B", "\uA64D", "\uA64F", "\uA651", "\uA653", "\uA655", "\uA657", "\uA659", "\uA65B", "\uA65D", "\uA65F", "\uA661", "\uA663", "\uA665", "\uA667", "\uA669", "\uA66B", "\uA66D", "\uA681", "\uA683", "\uA685", "\uA687", "\uA689", "\uA68B", "\uA68D", "\uA68F", "\uA691", "\uA693", "\uA695", "\uA697", "\uA699", "\uA69B", "\uA723", "\uA725", "\uA727", "\uA729", "\uA72B", "\uA72D", ["\uA72F", "\uA731"], "\uA733", "\uA735", "\uA737", "\uA739", "\uA73B", "\uA73D", "\uA73F", "\uA741", "\uA743", "\uA745", "\uA747", "\uA749", "\uA74B", "\uA74D", "\uA74F", "\uA751", "\uA753", "\uA755", "\uA757", "\uA759", "\uA75B", "\uA75D", "\uA75F", "\uA761", "\uA763", "\uA765", "\uA767", "\uA769", "\uA76B", "\uA76D", "\uA76F", ["\uA771", "\uA778"], "\uA77A", "\uA77C", "\uA77F", "\uA781", "\uA783", "\uA785", "\uA787", "\uA78C", "\uA78E", "\uA791", ["\uA793", "\uA795"], "\uA797", "\uA799", "\uA79B", "\uA79D", "\uA79F", "\uA7A1", "\uA7A3", "\uA7A5", "\uA7A7", "\uA7A9", "\uA7AF", "\uA7B5", "\uA7B7", "\uA7B9", "\uA7FA", ["\uAB30", "\uAB5A"], ["\uAB60", "\uAB65"], ["\uAB70", "\uABBF"], ["\uFB00", "\uFB06"], ["\uFB13", "\uFB17"], ["\uFF41", "\uFF5A"]], false, false); + var peg$e50 = peg$classExpectation([["\u02B0", "\u02C1"], ["\u02C6", "\u02D1"], ["\u02E0", "\u02E4"], "\u02EC", "\u02EE", "\u0374", "\u037A", "\u0559", "\u0640", ["\u06E5", "\u06E6"], ["\u07F4", "\u07F5"], "\u07FA", "\u081A", "\u0824", "\u0828", "\u0971", "\u0E46", "\u0EC6", "\u10FC", "\u17D7", "\u1843", "\u1AA7", ["\u1C78", "\u1C7D"], ["\u1D2C", "\u1D6A"], "\u1D78", ["\u1D9B", "\u1DBF"], "\u2071", "\u207F", ["\u2090", "\u209C"], ["\u2C7C", "\u2C7D"], "\u2D6F", "\u2E2F", "\u3005", ["\u3031", "\u3035"], "\u303B", ["\u309D", "\u309E"], ["\u30FC", "\u30FE"], "\uA015", ["\uA4F8", "\uA4FD"], "\uA60C", "\uA67F", ["\uA69C", "\uA69D"], ["\uA717", "\uA71F"], "\uA770", "\uA788", ["\uA7F8", "\uA7F9"], "\uA9CF", "\uA9E6", "\uAA70", "\uAADD", ["\uAAF3", "\uAAF4"], ["\uAB5C", "\uAB5F"], "\uFF70", ["\uFF9E", "\uFF9F"]], false, false); + var peg$e51 = peg$classExpectation(["\xAA", "\xBA", "\u01BB", ["\u01C0", "\u01C3"], "\u0294", ["\u05D0", "\u05EA"], ["\u05EF", "\u05F2"], ["\u0620", "\u063F"], ["\u0641", "\u064A"], ["\u066E", "\u066F"], ["\u0671", "\u06D3"], "\u06D5", ["\u06EE", "\u06EF"], ["\u06FA", "\u06FC"], "\u06FF", "\u0710", ["\u0712", "\u072F"], ["\u074D", "\u07A5"], "\u07B1", ["\u07CA", "\u07EA"], ["\u0800", "\u0815"], ["\u0840", "\u0858"], ["\u0860", "\u086A"], ["\u08A0", "\u08B4"], ["\u08B6", "\u08BD"], ["\u0904", "\u0939"], "\u093D", "\u0950", ["\u0958", "\u0961"], ["\u0972", "\u0980"], ["\u0985", "\u098C"], ["\u098F", "\u0990"], ["\u0993", "\u09A8"], ["\u09AA", "\u09B0"], "\u09B2", ["\u09B6", "\u09B9"], "\u09BD", "\u09CE", ["\u09DC", "\u09DD"], ["\u09DF", "\u09E1"], ["\u09F0", "\u09F1"], "\u09FC", ["\u0A05", "\u0A0A"], ["\u0A0F", "\u0A10"], ["\u0A13", "\u0A28"], ["\u0A2A", "\u0A30"], ["\u0A32", "\u0A33"], ["\u0A35", "\u0A36"], ["\u0A38", "\u0A39"], ["\u0A59", "\u0A5C"], "\u0A5E", ["\u0A72", "\u0A74"], ["\u0A85", "\u0A8D"], ["\u0A8F", "\u0A91"], ["\u0A93", "\u0AA8"], ["\u0AAA", "\u0AB0"], ["\u0AB2", "\u0AB3"], ["\u0AB5", "\u0AB9"], "\u0ABD", "\u0AD0", ["\u0AE0", "\u0AE1"], "\u0AF9", ["\u0B05", "\u0B0C"], ["\u0B0F", "\u0B10"], ["\u0B13", "\u0B28"], ["\u0B2A", "\u0B30"], ["\u0B32", "\u0B33"], ["\u0B35", "\u0B39"], "\u0B3D", ["\u0B5C", "\u0B5D"], ["\u0B5F", "\u0B61"], "\u0B71", "\u0B83", ["\u0B85", "\u0B8A"], ["\u0B8E", "\u0B90"], ["\u0B92", "\u0B95"], ["\u0B99", "\u0B9A"], "\u0B9C", ["\u0B9E", "\u0B9F"], ["\u0BA3", "\u0BA4"], ["\u0BA8", "\u0BAA"], ["\u0BAE", "\u0BB9"], "\u0BD0", ["\u0C05", "\u0C0C"], ["\u0C0E", "\u0C10"], ["\u0C12", "\u0C28"], ["\u0C2A", "\u0C39"], "\u0C3D", ["\u0C58", "\u0C5A"], ["\u0C60", "\u0C61"], "\u0C80", ["\u0C85", "\u0C8C"], ["\u0C8E", "\u0C90"], ["\u0C92", "\u0CA8"], ["\u0CAA", "\u0CB3"], ["\u0CB5", "\u0CB9"], "\u0CBD", "\u0CDE", ["\u0CE0", "\u0CE1"], ["\u0CF1", "\u0CF2"], ["\u0D05", "\u0D0C"], ["\u0D0E", "\u0D10"], ["\u0D12", "\u0D3A"], "\u0D3D", "\u0D4E", ["\u0D54", "\u0D56"], ["\u0D5F", "\u0D61"], ["\u0D7A", "\u0D7F"], ["\u0D85", "\u0D96"], ["\u0D9A", "\u0DB1"], ["\u0DB3", "\u0DBB"], "\u0DBD", ["\u0DC0", "\u0DC6"], ["\u0E01", "\u0E30"], ["\u0E32", "\u0E33"], ["\u0E40", "\u0E45"], ["\u0E81", "\u0E82"], "\u0E84", ["\u0E87", "\u0E88"], "\u0E8A", "\u0E8D", ["\u0E94", "\u0E97"], ["\u0E99", "\u0E9F"], ["\u0EA1", "\u0EA3"], "\u0EA5", "\u0EA7", ["\u0EAA", "\u0EAB"], ["\u0EAD", "\u0EB0"], ["\u0EB2", "\u0EB3"], "\u0EBD", ["\u0EC0", "\u0EC4"], ["\u0EDC", "\u0EDF"], "\u0F00", ["\u0F40", "\u0F47"], ["\u0F49", "\u0F6C"], ["\u0F88", "\u0F8C"], ["\u1000", "\u102A"], "\u103F", ["\u1050", "\u1055"], ["\u105A", "\u105D"], "\u1061", ["\u1065", "\u1066"], ["\u106E", "\u1070"], ["\u1075", "\u1081"], "\u108E", ["\u1100", "\u1248"], ["\u124A", "\u124D"], ["\u1250", "\u1256"], "\u1258", ["\u125A", "\u125D"], ["\u1260", "\u1288"], ["\u128A", "\u128D"], ["\u1290", "\u12B0"], ["\u12B2", "\u12B5"], ["\u12B8", "\u12BE"], "\u12C0", ["\u12C2", "\u12C5"], ["\u12C8", "\u12D6"], ["\u12D8", "\u1310"], ["\u1312", "\u1315"], ["\u1318", "\u135A"], ["\u1380", "\u138F"], ["\u1401", "\u166C"], ["\u166F", "\u167F"], ["\u1681", "\u169A"], ["\u16A0", "\u16EA"], ["\u16F1", "\u16F8"], ["\u1700", "\u170C"], ["\u170E", "\u1711"], ["\u1720", "\u1731"], ["\u1740", "\u1751"], ["\u1760", "\u176C"], ["\u176E", "\u1770"], ["\u1780", "\u17B3"], "\u17DC", ["\u1820", "\u1842"], ["\u1844", "\u1878"], ["\u1880", "\u1884"], ["\u1887", "\u18A8"], "\u18AA", ["\u18B0", "\u18F5"], ["\u1900", "\u191E"], ["\u1950", "\u196D"], ["\u1970", "\u1974"], ["\u1980", "\u19AB"], ["\u19B0", "\u19C9"], ["\u1A00", "\u1A16"], ["\u1A20", "\u1A54"], ["\u1B05", "\u1B33"], ["\u1B45", "\u1B4B"], ["\u1B83", "\u1BA0"], ["\u1BAE", "\u1BAF"], ["\u1BBA", "\u1BE5"], ["\u1C00", "\u1C23"], ["\u1C4D", "\u1C4F"], ["\u1C5A", "\u1C77"], ["\u1CE9", "\u1CEC"], ["\u1CEE", "\u1CF1"], ["\u1CF5", "\u1CF6"], ["\u2135", "\u2138"], ["\u2D30", "\u2D67"], ["\u2D80", "\u2D96"], ["\u2DA0", "\u2DA6"], ["\u2DA8", "\u2DAE"], ["\u2DB0", "\u2DB6"], ["\u2DB8", "\u2DBE"], ["\u2DC0", "\u2DC6"], ["\u2DC8", "\u2DCE"], ["\u2DD0", "\u2DD6"], ["\u2DD8", "\u2DDE"], "\u3006", "\u303C", ["\u3041", "\u3096"], "\u309F", ["\u30A1", "\u30FA"], "\u30FF", ["\u3105", "\u312F"], ["\u3131", "\u318E"], ["\u31A0", "\u31BA"], ["\u31F0", "\u31FF"], ["\u3400", "\u4DB5"], ["\u4E00", "\u9FEF"], ["\uA000", "\uA014"], ["\uA016", "\uA48C"], ["\uA4D0", "\uA4F7"], ["\uA500", "\uA60B"], ["\uA610", "\uA61F"], ["\uA62A", "\uA62B"], "\uA66E", ["\uA6A0", "\uA6E5"], "\uA78F", "\uA7F7", ["\uA7FB", "\uA801"], ["\uA803", "\uA805"], ["\uA807", "\uA80A"], ["\uA80C", "\uA822"], ["\uA840", "\uA873"], ["\uA882", "\uA8B3"], ["\uA8F2", "\uA8F7"], "\uA8FB", ["\uA8FD", "\uA8FE"], ["\uA90A", "\uA925"], ["\uA930", "\uA946"], ["\uA960", "\uA97C"], ["\uA984", "\uA9B2"], ["\uA9E0", "\uA9E4"], ["\uA9E7", "\uA9EF"], ["\uA9FA", "\uA9FE"], ["\uAA00", "\uAA28"], ["\uAA40", "\uAA42"], ["\uAA44", "\uAA4B"], ["\uAA60", "\uAA6F"], ["\uAA71", "\uAA76"], "\uAA7A", ["\uAA7E", "\uAAAF"], "\uAAB1", ["\uAAB5", "\uAAB6"], ["\uAAB9", "\uAABD"], "\uAAC0", "\uAAC2", ["\uAADB", "\uAADC"], ["\uAAE0", "\uAAEA"], "\uAAF2", ["\uAB01", "\uAB06"], ["\uAB09", "\uAB0E"], ["\uAB11", "\uAB16"], ["\uAB20", "\uAB26"], ["\uAB28", "\uAB2E"], ["\uABC0", "\uABE2"], ["\uAC00", "\uD7A3"], ["\uD7B0", "\uD7C6"], ["\uD7CB", "\uD7FB"], ["\uF900", "\uFA6D"], ["\uFA70", "\uFAD9"], "\uFB1D", ["\uFB1F", "\uFB28"], ["\uFB2A", "\uFB36"], ["\uFB38", "\uFB3C"], "\uFB3E", ["\uFB40", "\uFB41"], ["\uFB43", "\uFB44"], ["\uFB46", "\uFBB1"], ["\uFBD3", "\uFD3D"], ["\uFD50", "\uFD8F"], ["\uFD92", "\uFDC7"], ["\uFDF0", "\uFDFB"], ["\uFE70", "\uFE74"], ["\uFE76", "\uFEFC"], ["\uFF66", "\uFF6F"], ["\uFF71", "\uFF9D"], ["\uFFA0", "\uFFBE"], ["\uFFC2", "\uFFC7"], ["\uFFCA", "\uFFCF"], ["\uFFD2", "\uFFD7"], ["\uFFDA", "\uFFDC"]], false, false); + var peg$e52 = peg$classExpectation(["\u01C5", "\u01C8", "\u01CB", "\u01F2", ["\u1F88", "\u1F8F"], ["\u1F98", "\u1F9F"], ["\u1FA8", "\u1FAF"], "\u1FBC", "\u1FCC", "\u1FFC"], false, false); + var peg$e53 = peg$classExpectation([["A", "Z"], ["\xC0", "\xD6"], ["\xD8", "\xDE"], "\u0100", "\u0102", "\u0104", "\u0106", "\u0108", "\u010A", "\u010C", "\u010E", "\u0110", "\u0112", "\u0114", "\u0116", "\u0118", "\u011A", "\u011C", "\u011E", "\u0120", "\u0122", "\u0124", "\u0126", "\u0128", "\u012A", "\u012C", "\u012E", "\u0130", "\u0132", "\u0134", "\u0136", "\u0139", "\u013B", "\u013D", "\u013F", "\u0141", "\u0143", "\u0145", "\u0147", "\u014A", "\u014C", "\u014E", "\u0150", "\u0152", "\u0154", "\u0156", "\u0158", "\u015A", "\u015C", "\u015E", "\u0160", "\u0162", "\u0164", "\u0166", "\u0168", "\u016A", "\u016C", "\u016E", "\u0170", "\u0172", "\u0174", "\u0176", ["\u0178", "\u0179"], "\u017B", "\u017D", ["\u0181", "\u0182"], "\u0184", ["\u0186", "\u0187"], ["\u0189", "\u018B"], ["\u018E", "\u0191"], ["\u0193", "\u0194"], ["\u0196", "\u0198"], ["\u019C", "\u019D"], ["\u019F", "\u01A0"], "\u01A2", "\u01A4", ["\u01A6", "\u01A7"], "\u01A9", "\u01AC", ["\u01AE", "\u01AF"], ["\u01B1", "\u01B3"], "\u01B5", ["\u01B7", "\u01B8"], "\u01BC", "\u01C4", "\u01C7", "\u01CA", "\u01CD", "\u01CF", "\u01D1", "\u01D3", "\u01D5", "\u01D7", "\u01D9", "\u01DB", "\u01DE", "\u01E0", "\u01E2", "\u01E4", "\u01E6", "\u01E8", "\u01EA", "\u01EC", "\u01EE", "\u01F1", "\u01F4", ["\u01F6", "\u01F8"], "\u01FA", "\u01FC", "\u01FE", "\u0200", "\u0202", "\u0204", "\u0206", "\u0208", "\u020A", "\u020C", "\u020E", "\u0210", "\u0212", "\u0214", "\u0216", "\u0218", "\u021A", "\u021C", "\u021E", "\u0220", "\u0222", "\u0224", "\u0226", "\u0228", "\u022A", "\u022C", "\u022E", "\u0230", "\u0232", ["\u023A", "\u023B"], ["\u023D", "\u023E"], "\u0241", ["\u0243", "\u0246"], "\u0248", "\u024A", "\u024C", "\u024E", "\u0370", "\u0372", "\u0376", "\u037F", "\u0386", ["\u0388", "\u038A"], "\u038C", ["\u038E", "\u038F"], ["\u0391", "\u03A1"], ["\u03A3", "\u03AB"], "\u03CF", ["\u03D2", "\u03D4"], "\u03D8", "\u03DA", "\u03DC", "\u03DE", "\u03E0", "\u03E2", "\u03E4", "\u03E6", "\u03E8", "\u03EA", "\u03EC", "\u03EE", "\u03F4", "\u03F7", ["\u03F9", "\u03FA"], ["\u03FD", "\u042F"], "\u0460", "\u0462", "\u0464", "\u0466", "\u0468", "\u046A", "\u046C", "\u046E", "\u0470", "\u0472", "\u0474", "\u0476", "\u0478", "\u047A", "\u047C", "\u047E", "\u0480", "\u048A", "\u048C", "\u048E", "\u0490", "\u0492", "\u0494", "\u0496", "\u0498", "\u049A", "\u049C", "\u049E", "\u04A0", "\u04A2", "\u04A4", "\u04A6", "\u04A8", "\u04AA", "\u04AC", "\u04AE", "\u04B0", "\u04B2", "\u04B4", "\u04B6", "\u04B8", "\u04BA", "\u04BC", "\u04BE", ["\u04C0", "\u04C1"], "\u04C3", "\u04C5", "\u04C7", "\u04C9", "\u04CB", "\u04CD", "\u04D0", "\u04D2", "\u04D4", "\u04D6", "\u04D8", "\u04DA", "\u04DC", "\u04DE", "\u04E0", "\u04E2", "\u04E4", "\u04E6", "\u04E8", "\u04EA", "\u04EC", "\u04EE", "\u04F0", "\u04F2", "\u04F4", "\u04F6", "\u04F8", "\u04FA", "\u04FC", "\u04FE", "\u0500", "\u0502", "\u0504", "\u0506", "\u0508", "\u050A", "\u050C", "\u050E", "\u0510", "\u0512", "\u0514", "\u0516", "\u0518", "\u051A", "\u051C", "\u051E", "\u0520", "\u0522", "\u0524", "\u0526", "\u0528", "\u052A", "\u052C", "\u052E", ["\u0531", "\u0556"], ["\u10A0", "\u10C5"], "\u10C7", "\u10CD", ["\u13A0", "\u13F5"], ["\u1C90", "\u1CBA"], ["\u1CBD", "\u1CBF"], "\u1E00", "\u1E02", "\u1E04", "\u1E06", "\u1E08", "\u1E0A", "\u1E0C", "\u1E0E", "\u1E10", "\u1E12", "\u1E14", "\u1E16", "\u1E18", "\u1E1A", "\u1E1C", "\u1E1E", "\u1E20", "\u1E22", "\u1E24", "\u1E26", "\u1E28", "\u1E2A", "\u1E2C", "\u1E2E", "\u1E30", "\u1E32", "\u1E34", "\u1E36", "\u1E38", "\u1E3A", "\u1E3C", "\u1E3E", "\u1E40", "\u1E42", "\u1E44", "\u1E46", "\u1E48", "\u1E4A", "\u1E4C", "\u1E4E", "\u1E50", "\u1E52", "\u1E54", "\u1E56", "\u1E58", "\u1E5A", "\u1E5C", "\u1E5E", "\u1E60", "\u1E62", "\u1E64", "\u1E66", "\u1E68", "\u1E6A", "\u1E6C", "\u1E6E", "\u1E70", "\u1E72", "\u1E74", "\u1E76", "\u1E78", "\u1E7A", "\u1E7C", "\u1E7E", "\u1E80", "\u1E82", "\u1E84", "\u1E86", "\u1E88", "\u1E8A", "\u1E8C", "\u1E8E", "\u1E90", "\u1E92", "\u1E94", "\u1E9E", "\u1EA0", "\u1EA2", "\u1EA4", "\u1EA6", "\u1EA8", "\u1EAA", "\u1EAC", "\u1EAE", "\u1EB0", "\u1EB2", "\u1EB4", "\u1EB6", "\u1EB8", "\u1EBA", "\u1EBC", "\u1EBE", "\u1EC0", "\u1EC2", "\u1EC4", "\u1EC6", "\u1EC8", "\u1ECA", "\u1ECC", "\u1ECE", "\u1ED0", "\u1ED2", "\u1ED4", "\u1ED6", "\u1ED8", "\u1EDA", "\u1EDC", "\u1EDE", "\u1EE0", "\u1EE2", "\u1EE4", "\u1EE6", "\u1EE8", "\u1EEA", "\u1EEC", "\u1EEE", "\u1EF0", "\u1EF2", "\u1EF4", "\u1EF6", "\u1EF8", "\u1EFA", "\u1EFC", "\u1EFE", ["\u1F08", "\u1F0F"], ["\u1F18", "\u1F1D"], ["\u1F28", "\u1F2F"], ["\u1F38", "\u1F3F"], ["\u1F48", "\u1F4D"], "\u1F59", "\u1F5B", "\u1F5D", "\u1F5F", ["\u1F68", "\u1F6F"], ["\u1FB8", "\u1FBB"], ["\u1FC8", "\u1FCB"], ["\u1FD8", "\u1FDB"], ["\u1FE8", "\u1FEC"], ["\u1FF8", "\u1FFB"], "\u2102", "\u2107", ["\u210B", "\u210D"], ["\u2110", "\u2112"], "\u2115", ["\u2119", "\u211D"], "\u2124", "\u2126", "\u2128", ["\u212A", "\u212D"], ["\u2130", "\u2133"], ["\u213E", "\u213F"], "\u2145", "\u2183", ["\u2C00", "\u2C2E"], "\u2C60", ["\u2C62", "\u2C64"], "\u2C67", "\u2C69", "\u2C6B", ["\u2C6D", "\u2C70"], "\u2C72", "\u2C75", ["\u2C7E", "\u2C80"], "\u2C82", "\u2C84", "\u2C86", "\u2C88", "\u2C8A", "\u2C8C", "\u2C8E", "\u2C90", "\u2C92", "\u2C94", "\u2C96", "\u2C98", "\u2C9A", "\u2C9C", "\u2C9E", "\u2CA0", "\u2CA2", "\u2CA4", "\u2CA6", "\u2CA8", "\u2CAA", "\u2CAC", "\u2CAE", "\u2CB0", "\u2CB2", "\u2CB4", "\u2CB6", "\u2CB8", "\u2CBA", "\u2CBC", "\u2CBE", "\u2CC0", "\u2CC2", "\u2CC4", "\u2CC6", "\u2CC8", "\u2CCA", "\u2CCC", "\u2CCE", "\u2CD0", "\u2CD2", "\u2CD4", "\u2CD6", "\u2CD8", "\u2CDA", "\u2CDC", "\u2CDE", "\u2CE0", "\u2CE2", "\u2CEB", "\u2CED", "\u2CF2", "\uA640", "\uA642", "\uA644", "\uA646", "\uA648", "\uA64A", "\uA64C", "\uA64E", "\uA650", "\uA652", "\uA654", "\uA656", "\uA658", "\uA65A", "\uA65C", "\uA65E", "\uA660", "\uA662", "\uA664", "\uA666", "\uA668", "\uA66A", "\uA66C", "\uA680", "\uA682", "\uA684", "\uA686", "\uA688", "\uA68A", "\uA68C", "\uA68E", "\uA690", "\uA692", "\uA694", "\uA696", "\uA698", "\uA69A", "\uA722", "\uA724", "\uA726", "\uA728", "\uA72A", "\uA72C", "\uA72E", "\uA732", "\uA734", "\uA736", "\uA738", "\uA73A", "\uA73C", "\uA73E", "\uA740", "\uA742", "\uA744", "\uA746", "\uA748", "\uA74A", "\uA74C", "\uA74E", "\uA750", "\uA752", "\uA754", "\uA756", "\uA758", "\uA75A", "\uA75C", "\uA75E", "\uA760", "\uA762", "\uA764", "\uA766", "\uA768", "\uA76A", "\uA76C", "\uA76E", "\uA779", "\uA77B", ["\uA77D", "\uA77E"], "\uA780", "\uA782", "\uA784", "\uA786", "\uA78B", "\uA78D", "\uA790", "\uA792", "\uA796", "\uA798", "\uA79A", "\uA79C", "\uA79E", "\uA7A0", "\uA7A2", "\uA7A4", "\uA7A6", "\uA7A8", ["\uA7AA", "\uA7AE"], ["\uA7B0", "\uA7B4"], "\uA7B6", "\uA7B8", ["\uFF21", "\uFF3A"]], false, false); + var peg$e54 = peg$classExpectation([["\u16EE", "\u16F0"], ["\u2160", "\u2182"], ["\u2185", "\u2188"], "\u3007", ["\u3021", "\u3029"], ["\u3038", "\u303A"], ["\uA6E6", "\uA6EF"]], false, false); var peg$f0 = function() { return parseFloat(text()); }; var peg$f1 = function() { return parseInt(text()); }; @@ -834,52 +846,55 @@ function peg$parse(input, options) { function peg$parsestep_char() { var s0; - if (peg$r4.test(input.charAt(peg$currPos))) { - s0 = input.charAt(peg$currPos); - peg$currPos++; - } else { - s0 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e14); } - } + s0 = peg$parseunicode_letter(); if (s0 === peg$FAILED) { - if (input.charCodeAt(peg$currPos) === 45) { - s0 = peg$c1; + if (peg$r4.test(input.charAt(peg$currPos))) { + s0 = input.charAt(peg$currPos); peg$currPos++; } else { s0 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e4); } + if (peg$silentFails === 0) { peg$fail(peg$e14); } } if (s0 === peg$FAILED) { - if (input.charCodeAt(peg$currPos) === 35) { - s0 = peg$c8; + if (input.charCodeAt(peg$currPos) === 45) { + s0 = peg$c1; peg$currPos++; } else { s0 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e15); } + if (peg$silentFails === 0) { peg$fail(peg$e4); } } if (s0 === peg$FAILED) { - if (input.charCodeAt(peg$currPos) === 46) { - s0 = peg$c0; + if (input.charCodeAt(peg$currPos) === 35) { + s0 = peg$c8; peg$currPos++; } else { s0 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e1); } + if (peg$silentFails === 0) { peg$fail(peg$e15); } } if (s0 === peg$FAILED) { - if (input.charCodeAt(peg$currPos) === 94) { - s0 = peg$c9; + if (input.charCodeAt(peg$currPos) === 46) { + s0 = peg$c0; peg$currPos++; } else { s0 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e16); } + if (peg$silentFails === 0) { peg$fail(peg$e1); } } if (s0 === peg$FAILED) { - if (input.charCodeAt(peg$currPos) === 95) { - s0 = peg$c10; + if (input.charCodeAt(peg$currPos) === 94) { + s0 = peg$c9; peg$currPos++; } else { s0 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e17); } + if (peg$silentFails === 0) { peg$fail(peg$e16); } + } + if (s0 === peg$FAILED) { + if (input.charCodeAt(peg$currPos) === 95) { + s0 = peg$c10; + peg$currPos++; + } else { + s0 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e17); } + } } } } @@ -2242,6 +2257,113 @@ function peg$parse(input, options) { return s0; } + function peg$parseunicode_letter() { + var s0; + + s0 = peg$parseLu(); + if (s0 === peg$FAILED) { + s0 = peg$parseLl(); + if (s0 === peg$FAILED) { + s0 = peg$parseLt(); + if (s0 === peg$FAILED) { + s0 = peg$parseLm(); + if (s0 === peg$FAILED) { + s0 = peg$parseLo(); + if (s0 === peg$FAILED) { + s0 = peg$parseNl(); + } + } + } + } + } + + return s0; + } + + function peg$parseLl() { + var s0; + + if (peg$r6.test(input.charAt(peg$currPos))) { + s0 = input.charAt(peg$currPos); + peg$currPos++; + } else { + s0 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e49); } + } + + return s0; + } + + function peg$parseLm() { + var s0; + + if (peg$r7.test(input.charAt(peg$currPos))) { + s0 = input.charAt(peg$currPos); + peg$currPos++; + } else { + s0 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e50); } + } + + return s0; + } + + function peg$parseLo() { + var s0; + + if (peg$r8.test(input.charAt(peg$currPos))) { + s0 = input.charAt(peg$currPos); + peg$currPos++; + } else { + s0 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e51); } + } + + return s0; + } + + function peg$parseLt() { + var s0; + + if (peg$r9.test(input.charAt(peg$currPos))) { + s0 = input.charAt(peg$currPos); + peg$currPos++; + } else { + s0 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e52); } + } + + return s0; + } + + function peg$parseLu() { + var s0; + + if (peg$r10.test(input.charAt(peg$currPos))) { + s0 = input.charAt(peg$currPos); + peg$currPos++; + } else { + s0 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e53); } + } + + return s0; + } + + function peg$parseNl() { + var s0; + + if (peg$r11.test(input.charAt(peg$currPos))) { + s0 = input.charAt(peg$currPos); + peg$currPos++; + } else { + s0 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e54); } + } + + return s0; + } + var AtomStub = function(source) { diff --git a/packages/mini/krill.pegjs b/packages/mini/krill.pegjs index f52743bf..1d8ecc9b 100644 --- a/packages/mini/krill.pegjs +++ b/packages/mini/krill.pegjs @@ -103,7 +103,7 @@ quote = '"' / "'" // ------------------ steps and cycles --------------------------- // single step definition (e.g bd) -step_char = [0-9a-zA-Z~] / "-" / "#" / "." / "^" / "_" +step_char = unicode_letter / [0-9~] / "-" / "#" / "." / "^" / "_" step = ws chars:step_char+ ws { return new AtomStub(chars.join("")) } // define a sub cycle e.g. [1 2, 3 [4]] @@ -265,3 +265,25 @@ hush = "hush" // ---------------------- statements ---------------------------- statement = mini_definition / command + +// ---------------------- unicode ---------------------------- + +unicode_letter = Lu / Ll / Lt / Lm / Lo / Nl + +// Letter, Lowercase +Ll = [\u0061-\u007A\u00B5\u00DF-\u00F6\u00F8-\u00FF\u0101\u0103\u0105\u0107\u0109\u010B\u010D\u010F\u0111\u0113\u0115\u0117\u0119\u011B\u011D\u011F\u0121\u0123\u0125\u0127\u0129\u012B\u012D\u012F\u0131\u0133\u0135\u0137-\u0138\u013A\u013C\u013E\u0140\u0142\u0144\u0146\u0148-\u0149\u014B\u014D\u014F\u0151\u0153\u0155\u0157\u0159\u015B\u015D\u015F\u0161\u0163\u0165\u0167\u0169\u016B\u016D\u016F\u0171\u0173\u0175\u0177\u017A\u017C\u017E-\u0180\u0183\u0185\u0188\u018C-\u018D\u0192\u0195\u0199-\u019B\u019E\u01A1\u01A3\u01A5\u01A8\u01AA-\u01AB\u01AD\u01B0\u01B4\u01B6\u01B9-\u01BA\u01BD-\u01BF\u01C6\u01C9\u01CC\u01CE\u01D0\u01D2\u01D4\u01D6\u01D8\u01DA\u01DC-\u01DD\u01DF\u01E1\u01E3\u01E5\u01E7\u01E9\u01EB\u01ED\u01EF-\u01F0\u01F3\u01F5\u01F9\u01FB\u01FD\u01FF\u0201\u0203\u0205\u0207\u0209\u020B\u020D\u020F\u0211\u0213\u0215\u0217\u0219\u021B\u021D\u021F\u0221\u0223\u0225\u0227\u0229\u022B\u022D\u022F\u0231\u0233-\u0239\u023C\u023F-\u0240\u0242\u0247\u0249\u024B\u024D\u024F-\u0293\u0295-\u02AF\u0371\u0373\u0377\u037B-\u037D\u0390\u03AC-\u03CE\u03D0-\u03D1\u03D5-\u03D7\u03D9\u03DB\u03DD\u03DF\u03E1\u03E3\u03E5\u03E7\u03E9\u03EB\u03ED\u03EF-\u03F3\u03F5\u03F8\u03FB-\u03FC\u0430-\u045F\u0461\u0463\u0465\u0467\u0469\u046B\u046D\u046F\u0471\u0473\u0475\u0477\u0479\u047B\u047D\u047F\u0481\u048B\u048D\u048F\u0491\u0493\u0495\u0497\u0499\u049B\u049D\u049F\u04A1\u04A3\u04A5\u04A7\u04A9\u04AB\u04AD\u04AF\u04B1\u04B3\u04B5\u04B7\u04B9\u04BB\u04BD\u04BF\u04C2\u04C4\u04C6\u04C8\u04CA\u04CC\u04CE-\u04CF\u04D1\u04D3\u04D5\u04D7\u04D9\u04DB\u04DD\u04DF\u04E1\u04E3\u04E5\u04E7\u04E9\u04EB\u04ED\u04EF\u04F1\u04F3\u04F5\u04F7\u04F9\u04FB\u04FD\u04FF\u0501\u0503\u0505\u0507\u0509\u050B\u050D\u050F\u0511\u0513\u0515\u0517\u0519\u051B\u051D\u051F\u0521\u0523\u0525\u0527\u0529\u052B\u052D\u052F\u0560-\u0588\u10D0-\u10FA\u10FD-\u10FF\u13F8-\u13FD\u1C80-\u1C88\u1D00-\u1D2B\u1D6B-\u1D77\u1D79-\u1D9A\u1E01\u1E03\u1E05\u1E07\u1E09\u1E0B\u1E0D\u1E0F\u1E11\u1E13\u1E15\u1E17\u1E19\u1E1B\u1E1D\u1E1F\u1E21\u1E23\u1E25\u1E27\u1E29\u1E2B\u1E2D\u1E2F\u1E31\u1E33\u1E35\u1E37\u1E39\u1E3B\u1E3D\u1E3F\u1E41\u1E43\u1E45\u1E47\u1E49\u1E4B\u1E4D\u1E4F\u1E51\u1E53\u1E55\u1E57\u1E59\u1E5B\u1E5D\u1E5F\u1E61\u1E63\u1E65\u1E67\u1E69\u1E6B\u1E6D\u1E6F\u1E71\u1E73\u1E75\u1E77\u1E79\u1E7B\u1E7D\u1E7F\u1E81\u1E83\u1E85\u1E87\u1E89\u1E8B\u1E8D\u1E8F\u1E91\u1E93\u1E95-\u1E9D\u1E9F\u1EA1\u1EA3\u1EA5\u1EA7\u1EA9\u1EAB\u1EAD\u1EAF\u1EB1\u1EB3\u1EB5\u1EB7\u1EB9\u1EBB\u1EBD\u1EBF\u1EC1\u1EC3\u1EC5\u1EC7\u1EC9\u1ECB\u1ECD\u1ECF\u1ED1\u1ED3\u1ED5\u1ED7\u1ED9\u1EDB\u1EDD\u1EDF\u1EE1\u1EE3\u1EE5\u1EE7\u1EE9\u1EEB\u1EED\u1EEF\u1EF1\u1EF3\u1EF5\u1EF7\u1EF9\u1EFB\u1EFD\u1EFF-\u1F07\u1F10-\u1F15\u1F20-\u1F27\u1F30-\u1F37\u1F40-\u1F45\u1F50-\u1F57\u1F60-\u1F67\u1F70-\u1F7D\u1F80-\u1F87\u1F90-\u1F97\u1FA0-\u1FA7\u1FB0-\u1FB4\u1FB6-\u1FB7\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FC7\u1FD0-\u1FD3\u1FD6-\u1FD7\u1FE0-\u1FE7\u1FF2-\u1FF4\u1FF6-\u1FF7\u210A\u210E-\u210F\u2113\u212F\u2134\u2139\u213C-\u213D\u2146-\u2149\u214E\u2184\u2C30-\u2C5E\u2C61\u2C65-\u2C66\u2C68\u2C6A\u2C6C\u2C71\u2C73-\u2C74\u2C76-\u2C7B\u2C81\u2C83\u2C85\u2C87\u2C89\u2C8B\u2C8D\u2C8F\u2C91\u2C93\u2C95\u2C97\u2C99\u2C9B\u2C9D\u2C9F\u2CA1\u2CA3\u2CA5\u2CA7\u2CA9\u2CAB\u2CAD\u2CAF\u2CB1\u2CB3\u2CB5\u2CB7\u2CB9\u2CBB\u2CBD\u2CBF\u2CC1\u2CC3\u2CC5\u2CC7\u2CC9\u2CCB\u2CCD\u2CCF\u2CD1\u2CD3\u2CD5\u2CD7\u2CD9\u2CDB\u2CDD\u2CDF\u2CE1\u2CE3-\u2CE4\u2CEC\u2CEE\u2CF3\u2D00-\u2D25\u2D27\u2D2D\uA641\uA643\uA645\uA647\uA649\uA64B\uA64D\uA64F\uA651\uA653\uA655\uA657\uA659\uA65B\uA65D\uA65F\uA661\uA663\uA665\uA667\uA669\uA66B\uA66D\uA681\uA683\uA685\uA687\uA689\uA68B\uA68D\uA68F\uA691\uA693\uA695\uA697\uA699\uA69B\uA723\uA725\uA727\uA729\uA72B\uA72D\uA72F-\uA731\uA733\uA735\uA737\uA739\uA73B\uA73D\uA73F\uA741\uA743\uA745\uA747\uA749\uA74B\uA74D\uA74F\uA751\uA753\uA755\uA757\uA759\uA75B\uA75D\uA75F\uA761\uA763\uA765\uA767\uA769\uA76B\uA76D\uA76F\uA771-\uA778\uA77A\uA77C\uA77F\uA781\uA783\uA785\uA787\uA78C\uA78E\uA791\uA793-\uA795\uA797\uA799\uA79B\uA79D\uA79F\uA7A1\uA7A3\uA7A5\uA7A7\uA7A9\uA7AF\uA7B5\uA7B7\uA7B9\uA7FA\uAB30-\uAB5A\uAB60-\uAB65\uAB70-\uABBF\uFB00-\uFB06\uFB13-\uFB17\uFF41-\uFF5A] + +// Letter, Modifier +Lm = [\u02B0-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0374\u037A\u0559\u0640\u06E5-\u06E6\u07F4-\u07F5\u07FA\u081A\u0824\u0828\u0971\u0E46\u0EC6\u10FC\u17D7\u1843\u1AA7\u1C78-\u1C7D\u1D2C-\u1D6A\u1D78\u1D9B-\u1DBF\u2071\u207F\u2090-\u209C\u2C7C-\u2C7D\u2D6F\u2E2F\u3005\u3031-\u3035\u303B\u309D-\u309E\u30FC-\u30FE\uA015\uA4F8-\uA4FD\uA60C\uA67F\uA69C-\uA69D\uA717-\uA71F\uA770\uA788\uA7F8-\uA7F9\uA9CF\uA9E6\uAA70\uAADD\uAAF3-\uAAF4\uAB5C-\uAB5F\uFF70\uFF9E-\uFF9F] + +// Letter, Other +Lo = [\u00AA\u00BA\u01BB\u01C0-\u01C3\u0294\u05D0-\u05EA\u05EF-\u05F2\u0620-\u063F\u0641-\u064A\u066E-\u066F\u0671-\u06D3\u06D5\u06EE-\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u0800-\u0815\u0840-\u0858\u0860-\u086A\u08A0-\u08B4\u08B6-\u08BD\u0904-\u0939\u093D\u0950\u0958-\u0961\u0972-\u0980\u0985-\u098C\u098F-\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC-\u09DD\u09DF-\u09E1\u09F0-\u09F1\u09FC\u0A05-\u0A0A\u0A0F-\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32-\u0A33\u0A35-\u0A36\u0A38-\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2-\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0-\u0AE1\u0AF9\u0B05-\u0B0C\u0B0F-\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32-\u0B33\u0B35-\u0B39\u0B3D\u0B5C-\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99-\u0B9A\u0B9C\u0B9E-\u0B9F\u0BA3-\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D\u0C58-\u0C5A\u0C60-\u0C61\u0C80\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0-\u0CE1\u0CF1-\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D54-\u0D56\u0D5F-\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32-\u0E33\u0E40-\u0E45\u0E81-\u0E82\u0E84\u0E87-\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA-\u0EAB\u0EAD-\u0EB0\u0EB2-\u0EB3\u0EBD\u0EC0-\u0EC4\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065-\u1066\u106E-\u1070\u1075-\u1081\u108E\u1100-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16F1-\u16F8\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17DC\u1820-\u1842\u1844-\u1878\u1880-\u1884\u1887-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191E\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u1A00-\u1A16\u1A20-\u1A54\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE-\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C77\u1CE9-\u1CEC\u1CEE-\u1CF1\u1CF5-\u1CF6\u2135-\u2138\u2D30-\u2D67\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u3006\u303C\u3041-\u3096\u309F\u30A1-\u30FA\u30FF\u3105-\u312F\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FEF\uA000-\uA014\uA016-\uA48C\uA4D0-\uA4F7\uA500-\uA60B\uA610-\uA61F\uA62A-\uA62B\uA66E\uA6A0-\uA6E5\uA78F\uA7F7\uA7FB-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA8FD-\uA8FE\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9E0-\uA9E4\uA9E7-\uA9EF\uA9FA-\uA9FE\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA6F\uAA71-\uAA76\uAA7A\uAA7E-\uAAAF\uAAB1\uAAB5-\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADC\uAAE0-\uAAEA\uAAF2\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uABC0-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40-\uFB41\uFB43-\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF66-\uFF6F\uFF71-\uFF9D\uFFA0-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC] + +// Letter, Titlecase +Lt = [\u01C5\u01C8\u01CB\u01F2\u1F88-\u1F8F\u1F98-\u1F9F\u1FA8-\u1FAF\u1FBC\u1FCC\u1FFC] + +// Letter, Uppercase +Lu = [\u0041-\u005A\u00C0-\u00D6\u00D8-\u00DE\u0100\u0102\u0104\u0106\u0108\u010A\u010C\u010E\u0110\u0112\u0114\u0116\u0118\u011A\u011C\u011E\u0120\u0122\u0124\u0126\u0128\u012A\u012C\u012E\u0130\u0132\u0134\u0136\u0139\u013B\u013D\u013F\u0141\u0143\u0145\u0147\u014A\u014C\u014E\u0150\u0152\u0154\u0156\u0158\u015A\u015C\u015E\u0160\u0162\u0164\u0166\u0168\u016A\u016C\u016E\u0170\u0172\u0174\u0176\u0178-\u0179\u017B\u017D\u0181-\u0182\u0184\u0186-\u0187\u0189-\u018B\u018E-\u0191\u0193-\u0194\u0196-\u0198\u019C-\u019D\u019F-\u01A0\u01A2\u01A4\u01A6-\u01A7\u01A9\u01AC\u01AE-\u01AF\u01B1-\u01B3\u01B5\u01B7-\u01B8\u01BC\u01C4\u01C7\u01CA\u01CD\u01CF\u01D1\u01D3\u01D5\u01D7\u01D9\u01DB\u01DE\u01E0\u01E2\u01E4\u01E6\u01E8\u01EA\u01EC\u01EE\u01F1\u01F4\u01F6-\u01F8\u01FA\u01FC\u01FE\u0200\u0202\u0204\u0206\u0208\u020A\u020C\u020E\u0210\u0212\u0214\u0216\u0218\u021A\u021C\u021E\u0220\u0222\u0224\u0226\u0228\u022A\u022C\u022E\u0230\u0232\u023A-\u023B\u023D-\u023E\u0241\u0243-\u0246\u0248\u024A\u024C\u024E\u0370\u0372\u0376\u037F\u0386\u0388-\u038A\u038C\u038E-\u038F\u0391-\u03A1\u03A3-\u03AB\u03CF\u03D2-\u03D4\u03D8\u03DA\u03DC\u03DE\u03E0\u03E2\u03E4\u03E6\u03E8\u03EA\u03EC\u03EE\u03F4\u03F7\u03F9-\u03FA\u03FD-\u042F\u0460\u0462\u0464\u0466\u0468\u046A\u046C\u046E\u0470\u0472\u0474\u0476\u0478\u047A\u047C\u047E\u0480\u048A\u048C\u048E\u0490\u0492\u0494\u0496\u0498\u049A\u049C\u049E\u04A0\u04A2\u04A4\u04A6\u04A8\u04AA\u04AC\u04AE\u04B0\u04B2\u04B4\u04B6\u04B8\u04BA\u04BC\u04BE\u04C0-\u04C1\u04C3\u04C5\u04C7\u04C9\u04CB\u04CD\u04D0\u04D2\u04D4\u04D6\u04D8\u04DA\u04DC\u04DE\u04E0\u04E2\u04E4\u04E6\u04E8\u04EA\u04EC\u04EE\u04F0\u04F2\u04F4\u04F6\u04F8\u04FA\u04FC\u04FE\u0500\u0502\u0504\u0506\u0508\u050A\u050C\u050E\u0510\u0512\u0514\u0516\u0518\u051A\u051C\u051E\u0520\u0522\u0524\u0526\u0528\u052A\u052C\u052E\u0531-\u0556\u10A0-\u10C5\u10C7\u10CD\u13A0-\u13F5\u1C90-\u1CBA\u1CBD-\u1CBF\u1E00\u1E02\u1E04\u1E06\u1E08\u1E0A\u1E0C\u1E0E\u1E10\u1E12\u1E14\u1E16\u1E18\u1E1A\u1E1C\u1E1E\u1E20\u1E22\u1E24\u1E26\u1E28\u1E2A\u1E2C\u1E2E\u1E30\u1E32\u1E34\u1E36\u1E38\u1E3A\u1E3C\u1E3E\u1E40\u1E42\u1E44\u1E46\u1E48\u1E4A\u1E4C\u1E4E\u1E50\u1E52\u1E54\u1E56\u1E58\u1E5A\u1E5C\u1E5E\u1E60\u1E62\u1E64\u1E66\u1E68\u1E6A\u1E6C\u1E6E\u1E70\u1E72\u1E74\u1E76\u1E78\u1E7A\u1E7C\u1E7E\u1E80\u1E82\u1E84\u1E86\u1E88\u1E8A\u1E8C\u1E8E\u1E90\u1E92\u1E94\u1E9E\u1EA0\u1EA2\u1EA4\u1EA6\u1EA8\u1EAA\u1EAC\u1EAE\u1EB0\u1EB2\u1EB4\u1EB6\u1EB8\u1EBA\u1EBC\u1EBE\u1EC0\u1EC2\u1EC4\u1EC6\u1EC8\u1ECA\u1ECC\u1ECE\u1ED0\u1ED2\u1ED4\u1ED6\u1ED8\u1EDA\u1EDC\u1EDE\u1EE0\u1EE2\u1EE4\u1EE6\u1EE8\u1EEA\u1EEC\u1EEE\u1EF0\u1EF2\u1EF4\u1EF6\u1EF8\u1EFA\u1EFC\u1EFE\u1F08-\u1F0F\u1F18-\u1F1D\u1F28-\u1F2F\u1F38-\u1F3F\u1F48-\u1F4D\u1F59\u1F5B\u1F5D\u1F5F\u1F68-\u1F6F\u1FB8-\u1FBB\u1FC8-\u1FCB\u1FD8-\u1FDB\u1FE8-\u1FEC\u1FF8-\u1FFB\u2102\u2107\u210B-\u210D\u2110-\u2112\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u2130-\u2133\u213E-\u213F\u2145\u2183\u2C00-\u2C2E\u2C60\u2C62-\u2C64\u2C67\u2C69\u2C6B\u2C6D-\u2C70\u2C72\u2C75\u2C7E-\u2C80\u2C82\u2C84\u2C86\u2C88\u2C8A\u2C8C\u2C8E\u2C90\u2C92\u2C94\u2C96\u2C98\u2C9A\u2C9C\u2C9E\u2CA0\u2CA2\u2CA4\u2CA6\u2CA8\u2CAA\u2CAC\u2CAE\u2CB0\u2CB2\u2CB4\u2CB6\u2CB8\u2CBA\u2CBC\u2CBE\u2CC0\u2CC2\u2CC4\u2CC6\u2CC8\u2CCA\u2CCC\u2CCE\u2CD0\u2CD2\u2CD4\u2CD6\u2CD8\u2CDA\u2CDC\u2CDE\u2CE0\u2CE2\u2CEB\u2CED\u2CF2\uA640\uA642\uA644\uA646\uA648\uA64A\uA64C\uA64E\uA650\uA652\uA654\uA656\uA658\uA65A\uA65C\uA65E\uA660\uA662\uA664\uA666\uA668\uA66A\uA66C\uA680\uA682\uA684\uA686\uA688\uA68A\uA68C\uA68E\uA690\uA692\uA694\uA696\uA698\uA69A\uA722\uA724\uA726\uA728\uA72A\uA72C\uA72E\uA732\uA734\uA736\uA738\uA73A\uA73C\uA73E\uA740\uA742\uA744\uA746\uA748\uA74A\uA74C\uA74E\uA750\uA752\uA754\uA756\uA758\uA75A\uA75C\uA75E\uA760\uA762\uA764\uA766\uA768\uA76A\uA76C\uA76E\uA779\uA77B\uA77D-\uA77E\uA780\uA782\uA784\uA786\uA78B\uA78D\uA790\uA792\uA796\uA798\uA79A\uA79C\uA79E\uA7A0\uA7A2\uA7A4\uA7A6\uA7A8\uA7AA-\uA7AE\uA7B0-\uA7B4\uA7B6\uA7B8\uFF21-\uFF3A] + +// Number, Letter +Nl = [\u16EE-\u16F0\u2160-\u2182\u2185-\u2188\u3007\u3021-\u3029\u3038-\u303A\uA6E6-\uA6EF] \ No newline at end of file diff --git a/website/src/repl/Repl.jsx b/website/src/repl/Repl.jsx index 8fe43c6d..f12c64fc 100644 --- a/website/src/repl/Repl.jsx +++ b/website/src/repl/Repl.jsx @@ -339,9 +339,13 @@ export function Repl({ embedded = false }) { {panelPosition === 'right' && !isEmbedded &&