diff --git a/my-patterns/README.md b/my-patterns/README.md index f504d328..c2ad8d40 100644 --- a/my-patterns/README.md +++ b/my-patterns/README.md @@ -1,16 +1,20 @@ # my-patterns -This directory can be used to save your own patterns. +This directory can be used to save your own patterns, which then get +made into a pattern swatch. -0. fork the strudel repo -1. Save one or more .txt files here. -2. and run `npm run repl` -3. open `http://localhost:3000/my-patterns` ! +0. fork and clone the strudel repository +1. run `npm run setup` in the strudel folder +1. Save one or more .txt files in this folder +2. run `npm run repl` in the top-level strudel folder +3. open `http://localhost:3000/swatch/` ! ## deploy 1. in your fork, go to settings -> pages and select "Github Actions" as source 2. edit `website/public/CNAME` to contain `.github.io/strudel` 3. edit `website/astro.config.mjs` to use site: `.github.io/strudel` and base `/strudel` -4. go to Actions -> "Build and Deploy" and click "Run workflow" -5. view your patterns at `.github.io/strudel/my-patterns` +4. go to Actions -> `Build and Deploy` and click `Run workflow` +5. view your patterns at `.github.io/strudel/swatch/` + +Alternatively, github pages allows you to use a custom domain, like https://mycooldomain.org/swatch/. [See their documentation for details](https://docs.github.com/en/pages/configuring-a-custom-domain-for-your-github-pages-site). diff --git a/packages/core/animate.mjs b/packages/core/animate.mjs new file mode 100644 index 00000000..4ebb71ff --- /dev/null +++ b/packages/core/animate.mjs @@ -0,0 +1,67 @@ +import { controls, Pattern, getDrawContext, silence, register, pure } from './index.mjs'; +const { createParams } = controls; + +let clearColor = '#22222210'; + +Pattern.prototype.animate = function ({ callback, sync = false, smear = 0.5 } = {}) { + window.frame && cancelAnimationFrame(window.frame); + const ctx = getDrawContext(); + const { clientWidth: ww, clientHeight: wh } = ctx.canvas; + let smearPart = smear === 0 ? '99' : Number((1 - smear) * 100).toFixed(0); + smearPart = smearPart.length === 1 ? `0${smearPart}` : smearPart; + clearColor = `#200010${smearPart}`; + const render = (t) => { + let frame; + /* if (sync) { + t = scheduler.now(); + frame = this.queryArc(t, t); + } else { */ + t = Math.round(t); + frame = this.slow(1000).queryArc(t, t); + // } + ctx.fillStyle = clearColor; + ctx.fillRect(0, 0, ww, wh); + frame.forEach((f) => { + let { x, y, w, h, s, r, a = 0, fill = 'darkseagreen' } = f.value; + w *= ww; + h *= wh; + if (r !== undefined && a !== undefined) { + const radians = a * 2 * Math.PI; + const [cx, cy] = [(ww - w) / 2, (wh - h) / 2]; + x = cx + Math.cos(radians) * r * cx; + y = cy + Math.sin(radians) * r * cy; + } else { + x *= ww - w; + y *= wh - h; + } + const val = { ...f.value, x, y, w, h }; + ctx.fillStyle = fill; + if (s === 'rect') { + ctx.fillRect(x, y, w, h); + } else if (s === 'ellipse') { + ctx.beginPath(); + ctx.ellipse(x + w / 2, y + h / 2, w / 2, h / 2, 0, 0, 2 * Math.PI); + ctx.fill(); + } + callback && callback(ctx, val, f); + }); + window.frame = requestAnimationFrame(render); + }; + window.frame = requestAnimationFrame(render); + return silence; +}; + +export const { x, y, w, h, a, r, fill, smear } = createParams('x', 'y', 'w', 'h', 'a', 'r', 'fill', 'smear'); + +export const rescale = register('rescale', function (f, pat) { + return pat.mul(x(f).w(f).y(f).h(f)); +}); + +export const moveXY = register('moveXY', function (dx, dy, pat) { + return pat.add(x(dx).y(dy)); +}); + +export const zoomIn = register('zoomIn', function (f, pat) { + const d = pure(1).sub(f).div(2); + return pat.rescale(f).move(d, d); +}); diff --git a/packages/core/euclid.mjs b/packages/core/euclid.mjs index 45a59783..30871bf9 100644 --- a/packages/core/euclid.mjs +++ b/packages/core/euclid.mjs @@ -4,44 +4,49 @@ Copyright (C) 2022 Strudel contributors - see . */ -import { Pattern, timeCat } from './pattern.mjs'; +import { Pattern, timeCat, register } from './pattern.mjs'; import bjork from 'bjork'; import { rotate } from './util.mjs'; import Fraction from './fraction.mjs'; -const euclid = (pulses, steps, rotation = 0) => { - const b = bjork(steps, pulses); - if (rotation) { - return rotate(b, -rotation); - } - return b; -}; - /** * Changes the structure of the pattern to form an euclidean rhythm. - * Euclidian rhythms are rhythms obtained using the greatest common divisor of two numbers. - * They were described in 2004 by Godfried Toussaint, a canadian computer scientist. - * Euclidian rhythms are really useful for computer/algorithmic music because they can accurately - * describe a large number of rhythms used in the most important music world traditions. + * Euclidian rhythms are rhythms obtained using the greatest common + * divisor of two numbers. They were described in 2004 by Godfried + * Toussaint, a canadian computer scientist. Euclidian rhythms are + * really useful for computer/algorithmic music because they can + * describe a large number of rhythms with a couple of numbers. * * @memberof Pattern * @name euclid * @param {number} pulses the number of onsets / beats * @param {number} steps the number of steps to fill - * @param {number} rotation (optional) offset in steps * @returns Pattern * @example * // The Cuban tresillo pattern. * note("c3").euclid(3,8) */ +/** + * Like `iter`, but has an additional parameter for 'rotating' the resulting sequence. + * @memberof Pattern + * @name euclidRot + * @param {number} pulses the number of onsets / beats + * @param {number} steps the number of steps to fill + * @param {number} rotation offset in steps + * @returns Pattern + * @example + * // A Samba rhythm necklace from Brazil + * note("c3").euclidRot(3,16,14) + */ + /** * @example // A thirteenth century Persian rhythm called Khafif-e-ramal. * note("c3").euclid(2,5) * @example // The archetypal pattern of the Cumbia from Colombia, as well as a Calypso rhythm from Trinidad. * note("c3").euclid(3,4) * @example // Another thirteenth century Persian rhythm by the name of Khafif-e-ramal, as well as a Rumanian folk-dance rhythm. - * note("c3").euclid(3,5,2) + * note("c3").euclidRot(3,5,2) * @example // A Ruchenitza rhythm used in a Bulgarian folk-dance. * note("c3").euclid(3,7) * @example // The Cuban tresillo pattern. @@ -71,34 +76,55 @@ const euclid = (pulses, steps, rotation = 0) => { * @example // A common West African bell pattern. * note("c3").euclid(7,12) * @example // A Samba rhythm necklace from Brazil. - * note("c3").euclid(7,16,14) + * note("c3").euclidRot(7,16,14) * @example // A rhythm necklace used in the Central African Republic. * note("c3").euclid(9,16) * @example // A rhythm necklace of the Aka Pygmies of Central Africa. - * note("c3").euclid(11,24,14) + * note("c3").euclidRot(11,24,14) * @example // Another rhythm necklace of the Aka Pygmies of the upper Sangha. - * note("c3").euclid(13,24,5) + * note("c3").euclidRot(13,24,5) */ -Pattern.prototype.euclid = function (pulses, steps, rotation = 0) { - return this.struct(euclid(pulses, steps, rotation)); + +const _euclidRot = function (pulses, steps, rotation) { + const b = bjork(steps, pulses); + if (rotation) { + return rotate(b, -rotation); + } + return b; }; +export const euclid = register('euclid', function (pulses, steps, pat) { + return pat.struct(_euclidRot(steps, pulses, 0)); +}); + +export const { euclidrot, euclidRot } = register(['euclidrot', 'euclidRot'], function (pulses, steps, rotation, pat) { + return pat.struct(_euclidRot(steps, pulses, rotation)); +}); + /** - * Similar to `.euclid`, but each pulse is held until the next pulse, so there will be no gaps. + * Similar to `euclid`, but each pulse is held until the next pulse, + * so there will be no gaps. * @name euclidLegato * @memberof Pattern * @example * n("g2").decay(.1).sustain(.3).euclidLegato(3,8) */ -Pattern.prototype.euclidLegato = function (pulses, steps, rotation = 0) { - const bin_pat = euclid(pulses, steps, rotation); + +const _euclidLegato = function (pulses, steps, rotation, pat) { + const bin_pat = _euclidRot(pulses, steps, rotation); const firstOne = bin_pat.indexOf(1); const gapless = rotate(bin_pat, firstOne) .join('') .split('1') .slice(1) .map((s) => [s.length + 1, true]); - return this.struct(timeCat(...gapless)).late(Fraction(firstOne).div(steps)); + return pat.struct(timeCat(...gapless)).late(Fraction(firstOne).div(steps)); }; -export default euclid; +export const euclidLegato = register(['euclidLegato'], function (pulses, steps, pat) { + return _euclidLegato(pulses, steps, 0, pat); +}); + +export const euclidLegatoRot = register(['euclidLegatoRot'], function (pulses, steps, rotation, pat) { + return _euclidLegato(pulses, steps, rotation, pat); +}); diff --git a/packages/core/index.mjs b/packages/core/index.mjs index b9fd7061..b6c74848 100644 --- a/packages/core/index.mjs +++ b/packages/core/index.mjs @@ -21,6 +21,7 @@ export * from './repl.mjs'; export * from './logger.mjs'; export * from './time.mjs'; export * from './draw.mjs'; +export * from './animate.mjs'; export * from './pianoroll.mjs'; export * from './ui.mjs'; export { default as drawLine } from './drawLine.mjs'; diff --git a/packages/midi/midi.mjs b/packages/midi/midi.mjs index c59757fb..de04f355 100644 --- a/packages/midi/midi.mjs +++ b/packages/midi/midi.mjs @@ -11,10 +11,14 @@ import { getAudioContext } from '@strudel.cycles/webaudio'; // if you use WebMidi from outside of this package, make sure to import that instance: export const { WebMidi } = _WebMidi; +function supportsMidi() { + return typeof navigator.requestMIDIAccess === 'function'; +} + export function enableWebMidi(options = {}) { const { onReady, onConnected, onDisconnected } = options; - if (typeof navigator.requestMIDIAccess !== 'function') { + if (!supportsMidi()) { throw new Error('Your Browser does not support WebMIDI.'); } return new Promise((resolve, reject) => { @@ -42,23 +46,43 @@ export function enableWebMidi(options = {}) { // const outputByName = (name: string) => WebMidi.getOutputByName(name); const outputByName = (name) => WebMidi.getOutputByName(name); +let midiReady; + +// output?: string | number, outputs: typeof WebMidi.outputs +function getDevice(output, outputs) { + if (!outputs.length) { + throw new Error(`🔌 No MIDI devices found. Connect a device or enable IAC Driver.`); + } + if (typeof output === 'number') { + return outputs[output]; + } + if (typeof output === 'string') { + return outputByName(output); + } + return outputs[0]; +} + // Pattern.prototype.midi = function (output: string | number, channel = 1) { -Pattern.prototype.midi = async function (output, channel = 1) { - await enableWebMidi({ +Pattern.prototype.midi = function (output, channel = 1) { + if (!supportsMidi()) { + throw new Error(`🎹 WebMidi is not enabled. Supported Browsers: https://caniuse.com/?search=webmidi`); + } + /* await */ enableWebMidi({ onConnected: ({ outputs }) => logger(`Midi device connected! Available: ${outputs.map((o) => `'${o.name}'`).join(', ')}`), onDisconnected: ({ outputs }) => logger(`Midi device disconnected! Available: ${outputs.map((o) => `'${o.name}'`).join(', ')}`), onReady: ({ outputs }) => { - const chosenOutput = output ?? outputs[0]; + const device = getDevice(output, outputs); const otherOutputs = outputs - .filter((o) => o.name !== chosenOutput.name) + .filter((o) => o.name !== device.name) .map((o) => `'${o.name}'`) .join(' | '); - logger(`Midi connected! Using "${chosenOutput.name}". ${otherOutputs ? `Also available: ${otherOutputs}` : ''}`); + midiReady = true; + logger(`Midi connected! Using "${device.name}". ${otherOutputs ? `Also available: ${otherOutputs}` : ''}`); }, }); - if (isPattern(output?.constructor?.name)) { + if (isPattern(output)) { throw new Error( `.midi does not accept Pattern input. Make sure to pass device name with single quotes. Example: .midi('${ WebMidi.outputs?.[0]?.name || 'IAC Driver Bus 1' @@ -71,20 +95,10 @@ Pattern.prototype.midi = async function (output, channel = 1) { if (!isNote(note)) { throw new Error('not a note: ' + note); } - if (!WebMidi.enabled) { - throw new Error(`🎹 WebMidi is not enabled. Supported Browsers: https://caniuse.com/?search=webmidi`); - } - if (!WebMidi.outputs.length) { - throw new Error(`🔌 No MIDI devices found. Connect a device or enable IAC Driver.`); - } - let device; - if (typeof output === 'number') { - device = WebMidi.outputs[output]; - } else if (typeof output === 'string') { - device = outputByName(output); - } else { - device = WebMidi.outputs[0]; + if (!midiReady) { + return; } + const device = getDevice(output, WebMidi.outputs); if (!device) { throw new Error( `🔌 MIDI device '${output ? output : ''}' not found. Use one of ${WebMidi.outputs diff --git a/packages/mini/krill-parser.js b/packages/mini/krill-parser.js index 2bc565f7..7aa297ad 100644 --- a/packages/mini/krill-parser.js +++ b/packages/mini/krill-parser.js @@ -185,30 +185,32 @@ function peg$parse(input, options) { var peg$c11 = ":"; var peg$c12 = "["; var peg$c13 = "]"; - var peg$c14 = "<"; - var peg$c15 = ">"; - var peg$c16 = "@"; - var peg$c17 = "!"; - var peg$c18 = "("; - var peg$c19 = ")"; - var peg$c20 = "/"; - var peg$c21 = "*"; - var peg$c22 = "%"; - var peg$c23 = "?"; - var peg$c24 = "struct"; - var peg$c25 = "target"; - var peg$c26 = "euclid"; - var peg$c27 = "slow"; - var peg$c28 = "rotL"; - var peg$c29 = "rotR"; - var peg$c30 = "fast"; - var peg$c31 = "scale"; - var peg$c32 = "//"; - var peg$c33 = "cat"; - var peg$c34 = "$"; - var peg$c35 = "setcps"; - var peg$c36 = "setbpm"; - var peg$c37 = "hush"; + var peg$c14 = "{"; + var peg$c15 = "}"; + var peg$c16 = "%"; + var peg$c17 = "<"; + var peg$c18 = ">"; + var peg$c19 = "@"; + var peg$c20 = "!"; + var peg$c21 = "("; + var peg$c22 = ")"; + 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$r0 = /^[1-9]/; var peg$r1 = /^[eE]/; @@ -238,66 +240,70 @@ function peg$parse(input, options) { var peg$e18 = peg$literalExpectation(":", false); var peg$e19 = peg$literalExpectation("[", false); var peg$e20 = peg$literalExpectation("]", false); - var peg$e21 = peg$literalExpectation("<", false); - var peg$e22 = peg$literalExpectation(">", false); - var peg$e23 = peg$literalExpectation("@", false); - var peg$e24 = peg$literalExpectation("!", false); - var peg$e25 = peg$literalExpectation("(", false); - var peg$e26 = peg$literalExpectation(")", false); - var peg$e27 = peg$literalExpectation("/", false); - var peg$e28 = peg$literalExpectation("*", false); - var peg$e29 = peg$literalExpectation("%", false); - var peg$e30 = peg$literalExpectation("?", false); - var peg$e31 = peg$literalExpectation("struct", false); - var peg$e32 = peg$literalExpectation("target", false); - var peg$e33 = peg$literalExpectation("euclid", false); - var peg$e34 = peg$literalExpectation("slow", false); - var peg$e35 = peg$literalExpectation("rotL", false); - var peg$e36 = peg$literalExpectation("rotR", false); - var peg$e37 = peg$literalExpectation("fast", false); - var peg$e38 = peg$literalExpectation("scale", false); - var peg$e39 = peg$literalExpectation("//", false); - var peg$e40 = peg$classExpectation(["\n"], true, false); - var peg$e41 = peg$literalExpectation("cat", false); - var peg$e42 = peg$literalExpectation("$", false); - var peg$e43 = peg$literalExpectation("setcps", false); - var peg$e44 = peg$literalExpectation("setbpm", false); - var peg$e45 = peg$literalExpectation("hush", false); + var peg$e21 = peg$literalExpectation("{", false); + var peg$e22 = peg$literalExpectation("}", false); + var peg$e23 = peg$literalExpectation("%", false); + var peg$e24 = peg$literalExpectation("<", false); + var peg$e25 = peg$literalExpectation(">", false); + var peg$e26 = peg$literalExpectation("@", false); + var peg$e27 = peg$literalExpectation("!", false); + var peg$e28 = peg$literalExpectation("(", false); + var peg$e29 = peg$literalExpectation(")", false); + 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$f0 = function() { return parseFloat(text()); }; - var peg$f1 = function(chars) { return chars.join("") }; - var peg$f2 = function(s) { return s}; - var peg$f3 = function(sc) { sc.arguments_.alignment = "t"; return sc;}; - var peg$f4 = function(a) { return { weight: a} }; - var peg$f5 = function(a) { return { replicate: a } }; - var peg$f6 = function(p, s, r) { return { operator : { type_: "bjorklund", arguments_ :{ pulse: p, step:s, rotation:r || 0 } } } }; - var peg$f7 = function(a) { return { operator : { type_: "stretch", arguments_ :{ amount:a, type: 'slow' } } } }; - var peg$f8 = function(a) { return { operator : { type_: "stretch", arguments_ :{ amount:a, type: 'fast' } } } }; - var peg$f9 = function(a) { return { operator : { type_: "fixed-step", arguments_ :{ amount:a } } } }; - var peg$f10 = function(a) { return { operator : { type_: "degradeBy", arguments_ :{ amount:(a? a : 0.5) } } } }; - var peg$f11 = function(s, o) { return new ElementStub(s, o);}; - var peg$f12 = function(s) { return new PatternStub(s,"h"); }; - var peg$f13 = function(tail) { return { alignment: 'v', list: tail }; }; - var peg$f14 = function(tail) { return { alignment: 'r', list: tail }; }; - var peg$f15 = function(head, tail) { if (tail && tail.list.length > 0) { return new PatternStub([head, ...tail.list], tail.alignment); } else { return head; } }; - var peg$f16 = function(sc) { return sc; }; - var peg$f17 = function(s) { return { name: "struct", args: { sequence:s }}}; - var peg$f18 = function(s) { return { name: "target", args : { name:s}}}; - var peg$f19 = function(p, s, r) { return { name: "bjorklund", args :{ pulse: parseInt(p), step:parseInt(s) }}}; - var peg$f20 = function(a) { return { name: "stretch", args :{ amount: a}}}; - var peg$f21 = function(a) { return { name: "shift", args :{ amount: "-"+a}}}; - var peg$f22 = function(a) { return { name: "shift", args :{ amount: a}}}; - var peg$f23 = function(a) { return { name: "stretch", args :{ amount: "1/"+a}}}; - var peg$f24 = function(s) { return { name: "scale", args :{ scale: s.join("")}}}; - var peg$f25 = function(s, v) { return v}; - var peg$f26 = function(s, ss) { ss.unshift(s); return new PatternStub(ss,"t"); }; - var peg$f27 = function(sg) {return sg}; - var peg$f28 = function(o, soc) { return new OperatorStub(o.name,o.args,soc)}; - var peg$f29 = function(sc) { return sc }; - var peg$f30 = function(c) { return c }; - var peg$f31 = function(v) { return new CommandStub("setcps", { value: v})}; - var peg$f32 = function(v) { return new CommandStub("setcps", { value: (v/120/2)})}; - var peg$f33 = function() { return new CommandStub("hush")}; + 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 { weight: a} }; + var peg$f7 = function(a) { return { replicate: a } }; + var peg$f8 = function(p, s, r) { return { operator : { type_: "bjorklund", arguments_ :{ pulse: p, step:s, rotation:r } } } }; + var peg$f9 = function(a) { return { operator : { type_: "stretch", arguments_ :{ amount:a, type: 'slow' } } } }; + var peg$f10 = function(a) { return { operator : { type_: "stretch", arguments_ :{ amount:a, type: 'fast' } } } }; + var peg$f11 = function(a) { return { operator : { type_: "degradeBy", arguments_ :{ amount:a } } } }; + var peg$f12 = function(s, o) { return new ElementStub(s, o);}; + var peg$f13 = function(s) { return new PatternStub(s, 'fastcat'); }; + var peg$f14 = function(tail) { return { alignment: 'stack', list: tail }; }; + var peg$f15 = function(tail) { return { alignment: 'rand', list: tail }; }; + var peg$f16 = function(head, tail) { if (tail && tail.list.length > 0) { return new PatternStub([head, ...tail.list], tail.alignment); } else { return head; } }; + var peg$f17 = function(head, tail) { return new PatternStub(tail ? [head, ...tail.list] : [head], 'polymeter'); }; + var peg$f18 = function(sc) { return sc; }; + var peg$f19 = function(s) { return { name: "struct", args: { mini:s }}}; + var peg$f20 = function(s) { return { name: "target", args : { name:s}}}; + var peg$f21 = function(p, s, r) { return { name: "bjorklund", args :{ pulse: p, step:parseInt(s) }}}; + var peg$f22 = function(a) { return { name: "stretch", args :{ amount: a}}}; + var peg$f23 = function(a) { return { name: "shift", args :{ amount: "-"+a}}}; + var peg$f24 = function(a) { return { name: "shift", args :{ amount: a}}}; + var peg$f25 = function(a) { return { name: "stretch", args :{ amount: "1/"+a}}}; + var peg$f26 = function(s) { return { name: "scale", args :{ scale: s.join("")}}}; + var peg$f27 = function(s, v) { return v}; + var peg$f28 = function(s, ss) { ss.unshift(s); return new PatternStub(ss, 'slowcat'); }; + var peg$f29 = function(sg) {return sg}; + var peg$f30 = function(o, soc) { return new OperatorStub(o.name,o.args,soc)}; + var peg$f31 = function(sc) { return sc }; + var peg$f32 = function(c) { return c }; + var peg$f33 = function(v) { return new CommandStub("setcps", { value: v})}; + var peg$f34 = function(v) { return new CommandStub("setcps", { value: (v/120/2)})}; + var peg$f35 = function() { return new CommandStub("hush")}; var peg$currPos = 0; var peg$savedPos = 0; var peg$posDetailsCache = [{ line: 1, column: 1 }]; @@ -926,12 +932,12 @@ function peg$parse(input, options) { return s0; } - function peg$parsetimeline() { - var s0, s1, s2, s3, s4, s5, s6, s7; + function peg$parsepolymeter() { + var s0, s1, s2, s3, s4, s5, s6, s7, s8; s0 = peg$currPos; s1 = peg$parsews(); - if (input.charCodeAt(peg$currPos) === 60) { + if (input.charCodeAt(peg$currPos) === 123) { s2 = peg$c14; peg$currPos++; } else { @@ -940,20 +946,96 @@ function peg$parse(input, options) { } if (s2 !== peg$FAILED) { s3 = peg$parsews(); - s4 = peg$parsesingle_cycle(); + s4 = peg$parsepolymeter_stack(); if (s4 !== peg$FAILED) { s5 = peg$parsews(); - if (input.charCodeAt(peg$currPos) === 62) { + if (input.charCodeAt(peg$currPos) === 125) { s6 = peg$c15; peg$currPos++; } else { s6 = peg$FAILED; if (peg$silentFails === 0) { peg$fail(peg$e22); } } + if (s6 !== peg$FAILED) { + s7 = peg$parsepolymeter_steps(); + if (s7 === peg$FAILED) { + s7 = null; + } + s8 = peg$parsews(); + peg$savedPos = s0; + s0 = peg$f3(s4, s7); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parsepolymeter_steps() { + var s0, s1, s2; + + s0 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 37) { + s1 = peg$c16; + peg$currPos++; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e23); } + } + if (s1 !== peg$FAILED) { + s2 = peg$parseslice(); + if (s2 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f4(s2); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseslow_sequence() { + var s0, s1, s2, s3, s4, s5, s6, s7; + + s0 = peg$currPos; + s1 = peg$parsews(); + if (input.charCodeAt(peg$currPos) === 60) { + s2 = peg$c17; + peg$currPos++; + } else { + s2 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e24); } + } + if (s2 !== peg$FAILED) { + s3 = peg$parsews(); + s4 = peg$parsesequence(); + if (s4 !== peg$FAILED) { + s5 = peg$parsews(); + if (input.charCodeAt(peg$currPos) === 62) { + s6 = peg$c18; + peg$currPos++; + } else { + s6 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e25); } + } if (s6 !== peg$FAILED) { s7 = peg$parsews(); peg$savedPos = s0; - s0 = peg$f3(s4); + s0 = peg$f5(s4); } else { peg$currPos = s0; s0 = peg$FAILED; @@ -977,7 +1059,10 @@ function peg$parse(input, options) { if (s0 === peg$FAILED) { s0 = peg$parsesub_cycle(); if (s0 === peg$FAILED) { - s0 = peg$parsetimeline(); + s0 = peg$parsepolymeter(); + if (s0 === peg$FAILED) { + s0 = peg$parseslow_sequence(); + } } } @@ -995,12 +1080,9 @@ function peg$parse(input, options) { if (s0 === peg$FAILED) { s0 = peg$parseslice_fast(); if (s0 === peg$FAILED) { - s0 = peg$parseslice_fixed_step(); + s0 = peg$parseslice_replicate(); if (s0 === peg$FAILED) { - s0 = peg$parseslice_replicate(); - if (s0 === peg$FAILED) { - s0 = peg$parseslice_degrade(); - } + s0 = peg$parseslice_degrade(); } } } @@ -1015,17 +1097,17 @@ function peg$parse(input, options) { s0 = peg$currPos; if (input.charCodeAt(peg$currPos) === 64) { - s1 = peg$c16; + s1 = peg$c19; peg$currPos++; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e23); } + if (peg$silentFails === 0) { peg$fail(peg$e26); } } if (s1 !== peg$FAILED) { s2 = peg$parsenumber(); if (s2 !== peg$FAILED) { peg$savedPos = s0; - s0 = peg$f4(s2); + s0 = peg$f6(s2); } else { peg$currPos = s0; s0 = peg$FAILED; @@ -1043,17 +1125,17 @@ function peg$parse(input, options) { s0 = peg$currPos; if (input.charCodeAt(peg$currPos) === 33) { - s1 = peg$c17; + s1 = peg$c20; peg$currPos++; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e24); } + if (peg$silentFails === 0) { peg$fail(peg$e27); } } if (s1 !== peg$FAILED) { s2 = peg$parsenumber(); if (s2 !== peg$FAILED) { peg$savedPos = s0; - s0 = peg$f5(s2); + s0 = peg$f7(s2); } else { peg$currPos = s0; s0 = peg$FAILED; @@ -1071,21 +1153,21 @@ function peg$parse(input, options) { s0 = peg$currPos; if (input.charCodeAt(peg$currPos) === 40) { - s1 = peg$c18; + s1 = peg$c21; peg$currPos++; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e25); } + if (peg$silentFails === 0) { peg$fail(peg$e28); } } if (s1 !== peg$FAILED) { s2 = peg$parsews(); - s3 = peg$parsenumber(); + s3 = peg$parseslice_with_modifier(); if (s3 !== peg$FAILED) { s4 = peg$parsews(); s5 = peg$parsecomma(); if (s5 !== peg$FAILED) { s6 = peg$parsews(); - s7 = peg$parsenumber(); + s7 = peg$parseslice_with_modifier(); if (s7 !== peg$FAILED) { s8 = peg$parsews(); s9 = peg$parsecomma(); @@ -1093,21 +1175,21 @@ function peg$parse(input, options) { s9 = null; } s10 = peg$parsews(); - s11 = peg$parsenumber(); + s11 = peg$parseslice_with_modifier(); if (s11 === peg$FAILED) { s11 = null; } s12 = peg$parsews(); if (input.charCodeAt(peg$currPos) === 41) { - s13 = peg$c19; + s13 = peg$c22; peg$currPos++; } else { s13 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e26); } + if (peg$silentFails === 0) { peg$fail(peg$e29); } } if (s13 !== peg$FAILED) { peg$savedPos = s0; - s0 = peg$f6(s3, s7, s11); + s0 = peg$f8(s3, s7, s11); } else { peg$currPos = s0; s0 = peg$FAILED; @@ -1137,17 +1219,17 @@ function peg$parse(input, options) { s0 = peg$currPos; if (input.charCodeAt(peg$currPos) === 47) { - s1 = peg$c20; + s1 = peg$c23; peg$currPos++; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e27); } + if (peg$silentFails === 0) { peg$fail(peg$e30); } } if (s1 !== peg$FAILED) { - s2 = peg$parsenumber(); + s2 = peg$parseslice(); if (s2 !== peg$FAILED) { peg$savedPos = s0; - s0 = peg$f7(s2); + s0 = peg$f9(s2); } else { peg$currPos = s0; s0 = peg$FAILED; @@ -1165,45 +1247,17 @@ function peg$parse(input, options) { s0 = peg$currPos; if (input.charCodeAt(peg$currPos) === 42) { - s1 = peg$c21; + s1 = peg$c24; peg$currPos++; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e28); } + if (peg$silentFails === 0) { peg$fail(peg$e31); } } if (s1 !== peg$FAILED) { - s2 = peg$parsenumber(); + s2 = peg$parseslice(); if (s2 !== peg$FAILED) { peg$savedPos = s0; - s0 = peg$f8(s2); - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - - return s0; - } - - function peg$parseslice_fixed_step() { - var s0, s1, s2; - - s0 = peg$currPos; - if (input.charCodeAt(peg$currPos) === 37) { - s1 = peg$c22; - peg$currPos++; - } else { - s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e29); } - } - if (s1 !== peg$FAILED) { - s2 = peg$parsenumber(); - if (s2 !== peg$FAILED) { - peg$savedPos = s0; - s0 = peg$f9(s2); + s0 = peg$f10(s2); } else { peg$currPos = s0; s0 = peg$FAILED; @@ -1221,11 +1275,11 @@ function peg$parse(input, options) { s0 = peg$currPos; if (input.charCodeAt(peg$currPos) === 63) { - s1 = peg$c23; + s1 = peg$c25; peg$currPos++; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e30); } + if (peg$silentFails === 0) { peg$fail(peg$e32); } } if (s1 !== peg$FAILED) { s2 = peg$parsenumber(); @@ -1233,7 +1287,7 @@ function peg$parse(input, options) { s2 = null; } peg$savedPos = s0; - s0 = peg$f10(s2); + s0 = peg$f11(s2); } else { peg$currPos = s0; s0 = peg$FAILED; @@ -1253,7 +1307,7 @@ function peg$parse(input, options) { s2 = null; } peg$savedPos = s0; - s0 = peg$f11(s1, s2); + s0 = peg$f12(s1, s2); } else { peg$currPos = s0; s0 = peg$FAILED; @@ -1262,7 +1316,7 @@ function peg$parse(input, options) { return s0; } - function peg$parsesingle_cycle() { + function peg$parsesequence() { var s0, s1, s2; s0 = peg$currPos; @@ -1278,7 +1332,7 @@ function peg$parse(input, options) { } if (s1 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$f12(s1); + s1 = peg$f13(s1); } s0 = s1; @@ -1293,7 +1347,7 @@ function peg$parse(input, options) { s2 = peg$currPos; s3 = peg$parsecomma(); if (s3 !== peg$FAILED) { - s4 = peg$parsesingle_cycle(); + s4 = peg$parsesequence(); if (s4 !== peg$FAILED) { s2 = s4; } else { @@ -1310,56 +1364,7 @@ function peg$parse(input, options) { s2 = peg$currPos; s3 = peg$parsecomma(); if (s3 !== peg$FAILED) { - s4 = peg$parsesingle_cycle(); - if (s4 !== peg$FAILED) { - s2 = s4; - } else { - peg$currPos = s2; - s2 = peg$FAILED; - } - } else { - peg$currPos = s2; - s2 = peg$FAILED; - } - } - } else { - s1 = peg$FAILED; - } - if (s1 !== peg$FAILED) { - peg$savedPos = s0; - s1 = peg$f13(s1); - } - s0 = s1; - - return s0; - } - - function peg$parsechoose_tail() { - var s0, s1, s2, s3, s4; - - s0 = peg$currPos; - s1 = []; - s2 = peg$currPos; - s3 = peg$parsepipe(); - if (s3 !== peg$FAILED) { - s4 = peg$parsesingle_cycle(); - if (s4 !== peg$FAILED) { - s2 = s4; - } else { - peg$currPos = s2; - s2 = peg$FAILED; - } - } else { - peg$currPos = s2; - s2 = peg$FAILED; - } - if (s2 !== peg$FAILED) { - while (s2 !== peg$FAILED) { - s1.push(s2); - s2 = peg$currPos; - s3 = peg$parsepipe(); - if (s3 !== peg$FAILED) { - s4 = peg$parsesingle_cycle(); + s4 = peg$parsesequence(); if (s4 !== peg$FAILED) { s2 = s4; } else { @@ -1383,11 +1388,60 @@ function peg$parse(input, options) { return s0; } + function peg$parsechoose_tail() { + var s0, s1, s2, s3, s4; + + s0 = peg$currPos; + s1 = []; + s2 = peg$currPos; + s3 = peg$parsepipe(); + if (s3 !== peg$FAILED) { + s4 = peg$parsesequence(); + if (s4 !== peg$FAILED) { + s2 = s4; + } else { + peg$currPos = s2; + s2 = peg$FAILED; + } + } else { + peg$currPos = s2; + s2 = peg$FAILED; + } + if (s2 !== peg$FAILED) { + while (s2 !== peg$FAILED) { + s1.push(s2); + s2 = peg$currPos; + s3 = peg$parsepipe(); + if (s3 !== peg$FAILED) { + s4 = peg$parsesequence(); + if (s4 !== peg$FAILED) { + s2 = s4; + } else { + peg$currPos = s2; + s2 = peg$FAILED; + } + } else { + peg$currPos = s2; + s2 = peg$FAILED; + } + } + } else { + s1 = peg$FAILED; + } + if (s1 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$f15(s1); + } + s0 = s1; + + return s0; + } + function peg$parsestack_or_choose() { var s0, s1, s2; s0 = peg$currPos; - s1 = peg$parsesingle_cycle(); + s1 = peg$parsesequence(); if (s1 !== peg$FAILED) { s2 = peg$parsestack_tail(); if (s2 === peg$FAILED) { @@ -1397,7 +1451,7 @@ function peg$parse(input, options) { s2 = null; } peg$savedPos = s0; - s0 = peg$f15(s1, s2); + s0 = peg$f16(s1, s2); } else { peg$currPos = s0; s0 = peg$FAILED; @@ -1406,7 +1460,27 @@ function peg$parse(input, options) { return s0; } - function peg$parsesequence() { + function peg$parsepolymeter_stack() { + var s0, s1, s2; + + s0 = peg$currPos; + s1 = peg$parsesequence(); + if (s1 !== peg$FAILED) { + s2 = peg$parsestack_tail(); + if (s2 === peg$FAILED) { + s2 = null; + } + peg$savedPos = s0; + s0 = peg$f17(s1, s2); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parsemini() { var s0, s1, s2, s3, s4; s0 = peg$currPos; @@ -1418,7 +1492,7 @@ function peg$parse(input, options) { s4 = peg$parsequote(); if (s4 !== peg$FAILED) { peg$savedPos = s0; - s0 = peg$f16(s3); + s0 = peg$f18(s3); } else { peg$currPos = s0; s0 = peg$FAILED; @@ -1468,19 +1542,19 @@ function peg$parse(input, options) { var s0, s1, s2, s3; s0 = peg$currPos; - if (input.substr(peg$currPos, 6) === peg$c24) { - s1 = peg$c24; + if (input.substr(peg$currPos, 6) === peg$c26) { + s1 = peg$c26; peg$currPos += 6; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e31); } + if (peg$silentFails === 0) { peg$fail(peg$e33); } } if (s1 !== peg$FAILED) { s2 = peg$parsews(); - s3 = peg$parsesequence_or_operator(); + s3 = peg$parsemini_or_operator(); if (s3 !== peg$FAILED) { peg$savedPos = s0; - s0 = peg$f17(s3); + s0 = peg$f19(s3); } else { peg$currPos = s0; s0 = peg$FAILED; @@ -1497,12 +1571,12 @@ function peg$parse(input, options) { var s0, s1, s2, s3, s4, s5; s0 = peg$currPos; - if (input.substr(peg$currPos, 6) === peg$c25) { - s1 = peg$c25; + 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$e32); } + if (peg$silentFails === 0) { peg$fail(peg$e34); } } if (s1 !== peg$FAILED) { s2 = peg$parsews(); @@ -1513,7 +1587,7 @@ function peg$parse(input, options) { s5 = peg$parsequote(); if (s5 !== peg$FAILED) { peg$savedPos = s0; - s0 = peg$f18(s4); + s0 = peg$f20(s4); } else { peg$currPos = s0; s0 = peg$FAILED; @@ -1538,12 +1612,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$c26) { - s1 = peg$c26; + 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$e33); } + if (peg$silentFails === 0) { peg$fail(peg$e35); } } if (s1 !== peg$FAILED) { s2 = peg$parsews(); @@ -1558,7 +1632,7 @@ function peg$parse(input, options) { s7 = null; } peg$savedPos = s0; - s0 = peg$f19(s3, s5, s7); + s0 = peg$f21(s3, s5, s7); } else { peg$currPos = s0; s0 = peg$FAILED; @@ -1578,64 +1652,6 @@ function peg$parse(input, options) { function peg$parseslow() { var s0, s1, s2, s3; - s0 = peg$currPos; - if (input.substr(peg$currPos, 4) === peg$c27) { - s1 = peg$c27; - peg$currPos += 4; - } else { - s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e34); } - } - if (s1 !== peg$FAILED) { - s2 = peg$parsews(); - s3 = peg$parsenumber(); - if (s3 !== peg$FAILED) { - peg$savedPos = s0; - s0 = peg$f20(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$c28) { - s1 = peg$c28; - peg$currPos += 4; - } else { - s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e35); } - } - if (s1 !== peg$FAILED) { - s2 = peg$parsews(); - s3 = peg$parsenumber(); - if (s3 !== peg$FAILED) { - peg$savedPos = s0; - s0 = peg$f21(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$c29) { s1 = peg$c29; @@ -1662,7 +1678,7 @@ function peg$parse(input, options) { return s0; } - function peg$parsefast() { + function peg$parserotL() { var s0, s1, s2, s3; s0 = peg$currPos; @@ -1691,16 +1707,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$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(); + 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$parsefast() { + 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$f25(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$c31) { - s1 = peg$c31; + if (input.substr(peg$currPos, 5) === peg$c33) { + s1 = peg$c33; peg$currPos += 5; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e38); } + if (peg$silentFails === 0) { peg$fail(peg$e40); } } if (s1 !== peg$FAILED) { s2 = peg$parsews(); @@ -1720,7 +1794,7 @@ function peg$parse(input, options) { s5 = peg$parsequote(); if (s5 !== peg$FAILED) { peg$savedPos = s0; - s0 = peg$f24(s4); + s0 = peg$f26(s4); } else { peg$currPos = s0; s0 = peg$FAILED; @@ -1745,12 +1819,12 @@ function peg$parse(input, options) { var s0, s1, s2, s3; s0 = peg$currPos; - if (input.substr(peg$currPos, 2) === peg$c32) { - s1 = peg$c32; + if (input.substr(peg$currPos, 2) === peg$c34) { + s1 = peg$c34; peg$currPos += 2; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e39); } + if (peg$silentFails === 0) { peg$fail(peg$e41); } } if (s1 !== peg$FAILED) { s2 = []; @@ -1759,7 +1833,7 @@ function peg$parse(input, options) { peg$currPos++; } else { s3 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e40); } + if (peg$silentFails === 0) { peg$fail(peg$e42); } } while (s3 !== peg$FAILED) { s2.push(s3); @@ -1768,7 +1842,7 @@ function peg$parse(input, options) { peg$currPos++; } else { s3 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e40); } + if (peg$silentFails === 0) { peg$fail(peg$e42); } } } s1 = [s1, s2]; @@ -1785,12 +1859,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$c33) { - s1 = peg$c33; + if (input.substr(peg$currPos, 3) === peg$c35) { + s1 = peg$c35; peg$currPos += 3; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e41); } + if (peg$silentFails === 0) { peg$fail(peg$e43); } } if (s1 !== peg$FAILED) { s2 = peg$parsews(); @@ -1803,16 +1877,16 @@ function peg$parse(input, options) { } if (s3 !== peg$FAILED) { s4 = peg$parsews(); - s5 = peg$parsesequence_or_operator(); + s5 = peg$parsemini_or_operator(); if (s5 !== peg$FAILED) { s6 = []; s7 = peg$currPos; s8 = peg$parsecomma(); if (s8 !== peg$FAILED) { - s9 = peg$parsesequence_or_operator(); + s9 = peg$parsemini_or_operator(); if (s9 !== peg$FAILED) { peg$savedPos = s7; - s7 = peg$f25(s5, s9); + s7 = peg$f27(s5, s9); } else { peg$currPos = s7; s7 = peg$FAILED; @@ -1826,10 +1900,10 @@ function peg$parse(input, options) { s7 = peg$currPos; s8 = peg$parsecomma(); if (s8 !== peg$FAILED) { - s9 = peg$parsesequence_or_operator(); + s9 = peg$parsemini_or_operator(); if (s9 !== peg$FAILED) { peg$savedPos = s7; - s7 = peg$f25(s5, s9); + s7 = peg$f27(s5, s9); } else { peg$currPos = s7; s7 = peg$FAILED; @@ -1849,7 +1923,7 @@ function peg$parse(input, options) { } if (s8 !== peg$FAILED) { peg$savedPos = s0; - s0 = peg$f26(s5, s6); + s0 = peg$f28(s5, s6); } else { peg$currPos = s0; s0 = peg$FAILED; @@ -1870,22 +1944,22 @@ function peg$parse(input, options) { return s0; } - function peg$parsesequence_or_group() { + function peg$parsemini_or_group() { var s0; s0 = peg$parsecat(); if (s0 === peg$FAILED) { - s0 = peg$parsesequence(); + s0 = peg$parsemini(); } return s0; } - function peg$parsesequence_or_operator() { + function peg$parsemini_or_operator() { var s0, s1, s2, s3, s4, s5; s0 = peg$currPos; - s1 = peg$parsesequence_or_group(); + s1 = peg$parsemini_or_group(); if (s1 !== peg$FAILED) { s2 = peg$parsews(); s3 = []; @@ -1895,7 +1969,7 @@ function peg$parse(input, options) { s4 = peg$parsecomment(); } peg$savedPos = s0; - s0 = peg$f27(s1); + s0 = peg$f29(s1); } else { peg$currPos = s0; s0 = peg$FAILED; @@ -1906,18 +1980,18 @@ function peg$parse(input, options) { if (s1 !== peg$FAILED) { s2 = peg$parsews(); if (input.charCodeAt(peg$currPos) === 36) { - s3 = peg$c34; + s3 = peg$c36; peg$currPos++; } else { s3 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e42); } + if (peg$silentFails === 0) { peg$fail(peg$e44); } } if (s3 !== peg$FAILED) { s4 = peg$parsews(); - s5 = peg$parsesequence_or_operator(); + s5 = peg$parsemini_or_operator(); if (s5 !== peg$FAILED) { peg$savedPos = s0; - s0 = peg$f28(s1, s5); + s0 = peg$f30(s1, s5); } else { peg$currPos = s0; s0 = peg$FAILED; @@ -1939,10 +2013,10 @@ function peg$parse(input, options) { var s0, s1; s0 = peg$currPos; - s1 = peg$parsesequence_or_operator(); + s1 = peg$parsemini_or_operator(); if (s1 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$f29(s1); + s1 = peg$f31(s1); } s0 = s1; if (s0 === peg$FAILED) { @@ -1952,7 +2026,7 @@ function peg$parse(input, options) { return s0; } - function peg$parsesequence_definition() { + function peg$parsemini_definition() { var s0; s0 = peg$parsesequ_or_operator_or_comment(); @@ -1975,7 +2049,7 @@ function peg$parse(input, options) { if (s2 !== peg$FAILED) { s3 = peg$parsews(); peg$savedPos = s0; - s0 = peg$f30(s2); + s0 = peg$f32(s2); } else { peg$currPos = s0; s0 = peg$FAILED; @@ -1988,19 +2062,19 @@ function peg$parse(input, options) { var s0, s1, s2, s3; s0 = peg$currPos; - if (input.substr(peg$currPos, 6) === peg$c35) { - s1 = peg$c35; + if (input.substr(peg$currPos, 6) === peg$c37) { + s1 = peg$c37; peg$currPos += 6; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e43); } + if (peg$silentFails === 0) { peg$fail(peg$e45); } } if (s1 !== peg$FAILED) { s2 = peg$parsews(); s3 = peg$parsenumber(); if (s3 !== peg$FAILED) { peg$savedPos = s0; - s0 = peg$f31(s3); + s0 = peg$f33(s3); } else { peg$currPos = s0; s0 = peg$FAILED; @@ -2017,19 +2091,19 @@ function peg$parse(input, options) { var s0, s1, s2, s3; s0 = peg$currPos; - if (input.substr(peg$currPos, 6) === peg$c36) { - s1 = peg$c36; + 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$e44); } + 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$f32(s3); + s0 = peg$f34(s3); } else { peg$currPos = s0; s0 = peg$FAILED; @@ -2046,16 +2120,16 @@ function peg$parse(input, options) { var s0, s1; s0 = peg$currPos; - if (input.substr(peg$currPos, 4) === peg$c37) { - s1 = peg$c37; + if (input.substr(peg$currPos, 4) === peg$c39) { + s1 = peg$c39; peg$currPos += 4; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e45); } + if (peg$silentFails === 0) { peg$fail(peg$e47); } } if (s1 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$f33(); + s1 = peg$f35(); } s0 = s1; @@ -2065,7 +2139,7 @@ function peg$parse(input, options) { function peg$parsestatement() { var s0; - s0 = peg$parsesequence_definition(); + s0 = peg$parsemini_definition(); if (s0 === peg$FAILED) { s0 = peg$parsecommand(); } @@ -2074,6 +2148,13 @@ function peg$parse(input, options) { } + var AtomStub = function(source) + { + this.type_ = "atom"; + this.source_ = source; + this.location_ = location(); + } + var PatternStub = function(source, alignment) { this.type_ = "pattern"; diff --git a/packages/mini/krill.pegjs b/packages/mini/krill.pegjs index 87e9df3f..b79d88d1 100644 --- a/packages/mini/krill.pegjs +++ b/packages/mini/krill.pegjs @@ -5,13 +5,20 @@ This program is free software: you can redistribute it and/or modify it under th */ // Some terminology: -// a sequence = a serie of elements placed between quotes -// a stack = a serie of vertically aligned slices sharing the same overall length -// a slice = a serie of horizontally aligned elements -// a choose = a serie of elements, one of which is chosen at random +// mini(notation) = a series of elements placed between quotes +// a stack = a series of vertically aligned slices sharing the same overall length +// a sequence = a series of horizontally aligned elements +// a choose = a series of elements, one of which is chosen at random { + var AtomStub = function(source) + { + this.type_ = "atom"; + this.source_ = source; + this.location_ = location(); + } + var PatternStub = function(source, alignment) { this.type_ = "pattern"; @@ -90,82 +97,94 @@ quote = '"' / "'" // single step definition (e.g bd) step_char = [0-9a-zA-Z~] / "-" / "#" / "." / "^" / "_" / ":" -step = ws chars:step_char+ ws { return chars.join("") } +step = ws chars:step_char+ ws { return new AtomStub(chars.join("")) } // define a sub cycle e.g. [1 2, 3 [4]] -sub_cycle = ws "[" ws s:stack_or_choose ws "]" ws { return s} +sub_cycle = ws "[" ws s:stack_or_choose ws "]" ws { return s } -// define a timeline e.g <1 3 [3 5]>. We simply defer to a stack and change the alignement -timeline = ws "<" ws sc:single_cycle ws ">" ws - { sc.arguments_.alignment = "t"; return sc;} +// define a polymeter e.g. {1 2, 3 4 5} +polymeter = ws "{" ws s:polymeter_stack ws "}" stepsPerCycle:polymeter_steps? ws + { s.arguments_.stepsPerCycle = stepsPerCycle ; return s; } + +polymeter_steps = "%"a:slice + { return a } + +// define a step-per-cycle timeline e.g <1 3 [3 5]>. We simply defer to a sequence and +// change the alignment to slowcat +slow_sequence = ws "<" ws s:sequence ws ">" ws + { s.arguments_.alignment = 'slowcat'; return s; } // a slice is either a single step or a sub cycle -slice = step / sub_cycle / timeline +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_modifier = slice_weight / slice_bjorklund / slice_slow / slice_fast / slice_fixed_step / slice_replicate / slice_degrade +slice_modifier = slice_weight / slice_bjorklund / slice_slow / slice_fast / slice_replicate / slice_degrade slice_weight = "@" a:number { return { weight: a} } slice_replicate = "!"a:number - { return { replicate: a } } + { return { replicate: a } } -slice_bjorklund = "(" ws p:number ws comma ws s:number ws comma? ws r:number? ws ")" - { return { operator : { type_: "bjorklund", arguments_ :{ pulse: p, step:s, rotation:r || 0 } } } } +slice_bjorklund = "(" ws p:slice_with_modifier ws comma ws s:slice_with_modifier ws comma? ws r:slice_with_modifier? ws ")" + { return { operator : { type_: "bjorklund", arguments_ :{ pulse: p, step:s, rotation:r } } } } -slice_slow = "/"a:number +slice_slow = "/"a:slice { return { operator : { type_: "stretch", arguments_ :{ amount:a, type: 'slow' } } } } -slice_fast = "*"a:number +slice_fast = "*"a:slice { return { operator : { type_: "stretch", arguments_ :{ amount:a, type: 'fast' } } } } -slice_fixed_step = "%"a:number - { return { operator : { type_: "fixed-step", arguments_ :{ amount:a } } } } - slice_degrade = "?"a:number? - { return { operator : { type_: "degradeBy", arguments_ :{ amount:(a? a : 0.5) } } } } + { return { operator : { type_: "degradeBy", arguments_ :{ amount:a } } } } // a slice with an modifier applied i.e [bd@4 sd@3]@2 hh] slice_with_modifier = s:slice o:slice_modifier? { return new ElementStub(s, o);} -// a single cycle is a combination of one or more successive slices (as an array). If we -// have only one element, we skip the array and return the element itself -single_cycle = s:(slice_with_modifier)+ - { return new PatternStub(s,"h"); } +// a sequence is a combination of one or more successive slices (as an array) +sequence = s:(slice_with_modifier)+ + { return new PatternStub(s, 'fastcat'); } -// a stack is a serie of vertically aligned single cycles, separated by a comma -stack_tail = tail:(comma @single_cycle)+ - { return { alignment: 'v', list: tail }; } +// a stack is a series of vertically aligned sequence, separated by a comma +stack_tail = tail:(comma @sequence)+ + { return { alignment: 'stack', list: tail }; } -// a choose is a serie of pipe-separated single cycles, one of which is chosen -// at random each time through the pattern -choose_tail = tail:(pipe @single_cycle)+ - { return { alignment: 'r', list: tail }; } +// a choose is a series of pipe-separated sequence, one of which is +// chosen at random, each cycle +choose_tail = tail:(pipe @sequence)+ + { return { alignment: 'rand', list: tail }; } // if the stack contains only one element, we don't create a stack but return the // underlying element -stack_or_choose = head:single_cycle tail:(stack_tail / choose_tail)? +stack_or_choose = head:sequence tail:(stack_tail / choose_tail)? { if (tail && tail.list.length > 0) { return new PatternStub([head, ...tail.list], tail.alignment); } else { return head; } } -// a sequence is a quoted stack -sequence = ws quote sc:stack_or_choose quote +polymeter_stack = head:sequence tail:stack_tail? + { return new PatternStub(tail ? [head, ...tail.list] : [head], 'polymeter'); } + + +// Mini-notation innards ends +// ---------->8---------->8---------->8---------->8---------->8---------- +// Experimental haskellish parser begins + +// mini-notation = a quoted stack +mini = ws quote sc:stack_or_choose quote { return sc; } // ------------------ operators --------------------------- operator = scale / slow / fast / target / bjorklund / struct / rotR / rotL -struct = "struct" ws s:sequence_or_operator - { return { name: "struct", args: { sequence:s }}} +struct = "struct" ws s:mini_or_operator + { return { name: "struct", args: { mini:s }}} target = "target" ws quote s:step quote { return { name: "target", args : { name:s}}} bjorklund = "euclid" ws p:int ws s:int ws r:int? - { return { name: "bjorklund", args :{ pulse: parseInt(p), step:parseInt(s) }}} + { return { name: "bjorklund", args :{ pulse: p, step:parseInt(s) }}} slow = "slow" ws a:number { return { name: "stretch", args :{ amount: a}}} @@ -189,27 +208,27 @@ comment = '//' p:([^\n]*) group_operator = cat // cat is another form of timeline -cat = "cat" ws "[" ws s:sequence_or_operator ss:(comma v:sequence_or_operator { return v})* ws "]" - { ss.unshift(s); return new PatternStub(ss,"t"); } +cat = "cat" ws "[" ws s:mini_or_operator ss:(comma v:mini_or_operator { return v})* ws "]" + { ss.unshift(s); return new PatternStub(ss, 'slowcat'); } -// ------------------ high level sequence --------------------------- +// ------------------ high level mini --------------------------- -sequence_or_group = +mini_or_group = group_operator / - sequence + mini -sequence_or_operator = - sg:sequence_or_group ws (comment)* +mini_or_operator = + sg:mini_or_group ws (comment)* {return sg} - / o:operator ws "$" ws soc:sequence_or_operator + / o:operator ws "$" ws soc:mini_or_operator { return new OperatorStub(o.name,o.args,soc)} sequ_or_operator_or_comment = - sc: sequence_or_operator + sc: mini_or_operator { return sc } / comment -sequence_definition = s:sequ_or_operator_or_comment +mini_definition = s:sequ_or_operator_or_comment // ---------------------- statements ---------------------------- @@ -227,4 +246,4 @@ hush = "hush" // ---------------------- statements ---------------------------- -statement = sequence_definition / command +statement = mini_definition / command diff --git a/packages/mini/mini.mjs b/packages/mini/mini.mjs index 745f0f85..21f7314a 100644 --- a/packages/mini/mini.mjs +++ b/packages/mini/mini.mjs @@ -7,8 +7,6 @@ This program is free software: you can redistribute it and/or modify it under th import * as krill from './krill-parser.js'; import * as strudel from '@strudel.cycles/core'; -const { pure, Fraction, stack, slowcat, sequence, timeCat, silence, reify } = strudel; - /* var _seedState = 0; const randOffset = 0.0002; @@ -16,7 +14,7 @@ function _nextSeed() { return _seedState++; } */ -const applyOptions = (parent) => (pat, i) => { +const applyOptions = (parent, code) => (pat, i) => { const ast = parent.source_[i]; const options = ast.options_; const operator = options?.operator; @@ -28,10 +26,21 @@ const applyOptions = (parent) => (pat, i) => { if (!legalTypes.includes(type)) { throw new Error(`mini: stretch: type must be one of ${legalTypes.join('|')} but got ${type}`); } - return reify(pat)[type](amount); + return strudel.reify(pat)[type](patternifyAST(amount, code)); } case 'bjorklund': - return pat.euclid(operator.arguments_.pulse, operator.arguments_.step, operator.arguments_.rotation); + if (operator.arguments_.rotation) { + return pat.euclidRot( + patternifyAST(operator.arguments_.pulse, code), + patternifyAST(operator.arguments_.step, code), + patternifyAST(operator.arguments_.rotation, code), + ); + } else { + return pat.euclid( + patternifyAST(operator.arguments_.pulse, code), + patternifyAST(operator.arguments_.step, code), + ); + } case 'degradeBy': // TODO: find out what is right here // example: @@ -48,14 +57,12 @@ const applyOptions = (parent) => (pat, i) => { // this is how it was: /* - return reify(pat)._degradeByWith( + return strudel.reify(pat)._degradeByWith( strudel.rand.early(randOffset * _nextSeed()).segment(1), operator.arguments_.amount ?? 0.5, ); */ - return reify(pat)._degradeBy(operator.arguments_.amount ?? 0.5); - - // TODO: case 'fixed-step': "%" + return strudel.reify(pat).degradeBy(operator.arguments_.amount === null ? 0.5 : operator.arguments_.amount); } console.warn(`operator "${operator.type_}" not implemented`); } @@ -74,94 +81,87 @@ const applyOptions = (parent) => (pat, i) => { }; function resolveReplications(ast) { - // the general idea here: x!3 = [x*3]@3 - // could this be made easier?! - ast.source_ = ast.source_.map((child) => { - const { replicate, ...options } = child.options_ || {}; - if (!replicate) { - return child; - } - return { - ...child, - options_: { ...options, weight: replicate }, - source_: { - type_: 'pattern', - arguments_: { - alignment: 'h', - }, - source_: [ - { - type_: 'element', - source_: child.source_, - location_: child.location_, - options_: { - operator: { - type_: 'stretch', - arguments_: { amount: replicate, type: 'fast' }, - }, - }, - }, - ], - }, - }; - }); + ast.source_ = strudel.flatten( + ast.source_.map((child) => { + const { replicate, ...options } = child.options_ || {}; + if (!replicate) { + return [child]; + } + delete child.options_.replicate; + return Array(replicate).fill(child); + }), + ); } export function patternifyAST(ast, code) { switch (ast.type_) { case 'pattern': { resolveReplications(ast); - const children = ast.source_.map((child) => patternifyAST(child, code)).map(applyOptions(ast)); + const children = ast.source_.map((child) => patternifyAST(child, code)).map(applyOptions(ast, code)); const alignment = ast.arguments_.alignment; - if (alignment === 'v') { - return stack(...children); + if (alignment === 'stack') { + return strudel.stack(...children); } - if (alignment === 'r') { + if (alignment === 'polymeter') { + // polymeter + const stepsPerCycle = ast.arguments_.stepsPerCycle + ? patternifyAST(ast.arguments_.stepsPerCycle, code).fmap((x) => strudel.Fraction(x)) + : strudel.pure(strudel.Fraction(children.length > 0 ? children[0].__weight : 1)); + + const aligned = children.map((child) => child.fast(stepsPerCycle.fmap((x) => x.div(child.__weight || 1)))); + return strudel.stack(...aligned); + } + if (alignment === 'rand') { // https://github.com/tidalcycles/strudel/issues/245#issuecomment-1345406422 // return strudel.chooseInWith(strudel.rand.early(randOffset * _nextSeed()).segment(1), children); return strudel.chooseCycles(...children); } const weightedChildren = ast.source_.some((child) => !!child.options_?.weight); - if (!weightedChildren && alignment === 't') { - return slowcat(...children); + if (!weightedChildren && alignment === 'slowcat') { + return strudel.slowcat(...children); } if (weightedChildren) { - const pat = timeCat(...ast.source_.map((child, i) => [child.options_?.weight || 1, children[i]])); - if (alignment === 't') { - const weightSum = ast.source_.reduce((sum, child) => sum + (child.options_?.weight || 1), 0); + const weightSum = ast.source_.reduce((sum, child) => sum + (child.options_?.weight || 1), 0); + const pat = strudel.timeCat(...ast.source_.map((child, i) => [child.options_?.weight || 1, children[i]])); + if (alignment === 'slowcat') { return pat._slow(weightSum); // timecat + slow } + pat.__weight = weightSum; return pat; } - return sequence(...children); + const pat = strudel.sequence(...children); + pat.__weight = children.length; + return pat; } case 'element': { + return patternifyAST(ast.source_, code); + } + case 'atom': { if (ast.source_ === '~') { - return silence; + return strudel.silence; } - if (typeof ast.source_ !== 'object') { - if (!ast.location_) { - console.warn('no location for', ast); - return ast.source_; - } - const { start, end } = ast.location_; - const value = !isNaN(Number(ast.source_)) ? Number(ast.source_) : ast.source_; - // the following line expects the shapeshifter append .withMiniLocation - // because location_ is only relative to the mini string, but we need it relative to whole code - // make sure whitespaces are not part of the highlight: - const actual = code?.split('').slice(start.offset, end.offset).join(''); - const [offsetStart = 0, offsetEnd = 0] = actual - ? actual.split(ast.source_).map((p) => p.split('').filter((c) => c === ' ').length) - : []; - return pure(value).withLocation( + if (!ast.location_) { + console.warn('no location for', ast); + return ast.source_; + } + const { start, end } = ast.location_; + const value = !isNaN(Number(ast.source_)) ? Number(ast.source_) : ast.source_; + // the following line expects the shapeshifter append .withMiniLocation + // because location_ is only relative to the mini string, but we need it relative to whole code + // make sure whitespaces are not part of the highlight: + const actual = code?.split('').slice(start.offset, end.offset).join(''); + const [offsetStart = 0, offsetEnd = 0] = actual + ? actual.split(ast.source_).map((p) => p.split('').filter((c) => c === ' ').length) + : []; + return strudel + .pure(value) + .withLocation( [start.line, start.column + offsetStart, start.offset + offsetStart], [start.line, end.column - offsetEnd, end.offset - offsetEnd], ); - } - return patternifyAST(ast.source_, code); } case 'stretch': - return patternifyAST(ast.source_, code).slow(ast.arguments_.amount); + return patternifyAST(ast.source_, code).slow(patternifyAST(ast.arguments_.amount, code)); /* case 'scale': let [tonic, scale] = Scale.tokenize(ast.arguments_.scale); const intervals = Scale.get(scale).intervals; @@ -183,10 +183,10 @@ export function patternifyAST(ast, code) { }); */ /* case 'struct': // TODO: - return silence; */ + return strudel.silence; */ default: console.warn(`node type "${ast.type_}" not implemented -> returning silence`); - return silence; + return strudel.silence; } } @@ -197,7 +197,7 @@ export const mini = (...strings) => { const ast = krill.parse(code); return patternifyAST(ast, code); }); - return sequence(...pats); + return strudel.sequence(...pats); }; // includes haskell style (raw krill parsing) @@ -211,5 +211,5 @@ export function minify(thing) { if (typeof thing === 'string') { return mini(thing); } - return reify(thing); + return strudel.reify(thing); } diff --git a/packages/mini/test/mini.test.mjs b/packages/mini/test/mini.test.mjs index 42830ff8..123845ac 100644 --- a/packages/mini/test/mini.test.mjs +++ b/packages/mini/test/mini.test.mjs @@ -21,6 +21,21 @@ describe('mini', () => { expect(minS('a b')).toEqual(['a: 0 - 1/2', 'b: 1/2 - 1']); expect(minS('a b c')).toEqual(['a: 0 - 1/3', 'b: 1/3 - 2/3', 'c: 2/3 - 1']); }); + it('supports fast', () => { + expect(minS('a*3 b')).toEqual(minS('[a a a] b')); + }); + it('supports patterned fast', () => { + expect(minS('[a*<3 5>]*2')).toEqual(minS('[a a a] [a a a a a]')); + }); + it('supports slow', () => { + expect(minS('[a a a]/3 b')).toEqual(minS('a b')); + }); + it('supports patterned slow', () => { + expect(minS('[a a a a a a a a]/[2 4]')).toEqual(minS('[a a] a')); + }); + it('supports patterned fast', () => { + expect(minS('[a*<3 5>]*2')).toEqual(minS('[a a a] [a a a a a]')); + }); it('supports slowcat', () => { expect(minV('')).toEqual(['a']); }); @@ -36,6 +51,16 @@ describe('mini', () => { expect(minS('c3 [d3 e3]')).toEqual(['c3: 0 - 1/2', 'd3: 1/2 - 3/4', 'e3: 3/4 - 1']); expect(minS('c3 [d3 [e3 f3]]')).toEqual(['c3: 0 - 1/2', 'd3: 1/2 - 3/4', 'e3: 3/4 - 7/8', 'f3: 7/8 - 1']); }); + it('supports curly brackets', () => { + expect(minS('{a b, c d e}*3')).toEqual(minS('[a b a b a b, c d e c d e]')); + expect(minS('{a b, c [d e] f}*3')).toEqual(minS('[a b a b a b, c [d e] f c [d e] f]')); + expect(minS('{a b c, d e}*2')).toEqual(minS('[a b c a b c, d e d e d e]')); + }); + it('supports curly brackets with explicit step-per-cycle', () => { + expect(minS('{a b, c d e}%3')).toEqual(minS('[a b a, c d e]')); + expect(minS('{a b, c d e}%5')).toEqual(minS('[a b a b a, c d e c d]')); + expect(minS('{a b, c d e}%6')).toEqual(minS('[a b a b a b, c d e c d e]')); + }); it('supports commas', () => { expect(minS('c3,e3,g3')).toEqual(['c3: 0 - 1', 'e3: 0 - 1', 'g3: 0 - 1']); expect(minS('[c3,e3,g3] f3')).toEqual(['c3: 0 - 1/2', 'e3: 0 - 1/2', 'g3: 0 - 1/2', 'f3: 1/2 - 1']); @@ -46,10 +71,14 @@ describe('mini', () => { }); it('supports replication', () => { expect(minS('a!3 b')).toEqual(['a: 0 - 1/4', 'a: 1/4 - 1/2', 'a: 1/2 - 3/4', 'b: 3/4 - 1']); + expect(minS('[]!3 d')).toEqual(minS(' d')); }); it('supports euclidean rhythms', () => { expect(minS('a(3, 8)')).toEqual(['a: 0 - 1/8', 'a: 3/8 - 1/2', 'a: 3/4 - 7/8']); }); + it('supports patterning euclidean rhythms', () => { + expect(minS('[a(<3 5>, <8 16>)]*2')).toEqual(minS('a(3,8) a(5,16)')); + }); it('supports the ? operator', () => { expect( mini('a?') diff --git a/test/__snapshots__/examples.test.mjs.snap b/test/__snapshots__/examples.test.mjs.snap index 0bc774fb..533c192c 100644 --- a/test/__snapshots__/examples.test.mjs.snap +++ b/test/__snapshots__/examples.test.mjs.snap @@ -1,5 +1,570 @@ // Vitest Snapshot v1 +exports[`runs examples > example "_euclidRot" example index 0 1`] = ` +[ + "[ 1/5 → 2/5 | note:c3 ]", + "[ 3/5 → 4/5 | note:c3 ]", + "[ 6/5 → 7/5 | note:c3 ]", + "[ 8/5 → 9/5 | note:c3 ]", + "[ 11/5 → 12/5 | note:c3 ]", + "[ 13/5 → 14/5 | note:c3 ]", + "[ 16/5 → 17/5 | note:c3 ]", + "[ 18/5 → 19/5 | note:c3 ]", +] +`; + +exports[`runs examples > example "_euclidRot" example index 1 1`] = ` +[ + "[ 1/4 → 1/2 | note:c3 ]", + "[ 1/2 → 3/4 | note:c3 ]", + "[ 3/4 → 1/1 | note:c3 ]", + "[ 5/4 → 3/2 | note:c3 ]", + "[ 3/2 → 7/4 | note:c3 ]", + "[ 7/4 → 2/1 | note:c3 ]", + "[ 9/4 → 5/2 | note:c3 ]", + "[ 5/2 → 11/4 | note:c3 ]", + "[ 11/4 → 3/1 | note:c3 ]", + "[ 13/4 → 7/2 | note:c3 ]", + "[ 7/2 → 15/4 | note:c3 ]", + "[ 15/4 → 4/1 | note:c3 ]", +] +`; + +exports[`runs examples > example "_euclidRot" example index 2 1`] = ` +[ + "[ 1/5 → 2/5 | note:c3 ]", + "[ 2/5 → 3/5 | note:c3 ]", + "[ 4/5 → 1/1 | note:c3 ]", + "[ 6/5 → 7/5 | note:c3 ]", + "[ 7/5 → 8/5 | note:c3 ]", + "[ 9/5 → 2/1 | note:c3 ]", + "[ 11/5 → 12/5 | note:c3 ]", + "[ 12/5 → 13/5 | note:c3 ]", + "[ 14/5 → 3/1 | note:c3 ]", + "[ 16/5 → 17/5 | note:c3 ]", + "[ 17/5 → 18/5 | note:c3 ]", + "[ 19/5 → 4/1 | note:c3 ]", +] +`; + +exports[`runs examples > example "_euclidRot" example index 3 1`] = ` +[ + "[ 1/7 → 2/7 | note:c3 ]", + "[ 3/7 → 4/7 | note:c3 ]", + "[ 5/7 → 6/7 | note:c3 ]", + "[ 8/7 → 9/7 | note:c3 ]", + "[ 10/7 → 11/7 | note:c3 ]", + "[ 12/7 → 13/7 | note:c3 ]", + "[ 15/7 → 16/7 | note:c3 ]", + "[ 17/7 → 18/7 | note:c3 ]", + "[ 19/7 → 20/7 | note:c3 ]", + "[ 22/7 → 23/7 | note:c3 ]", + "[ 24/7 → 25/7 | note:c3 ]", + "[ 26/7 → 27/7 | note:c3 ]", +] +`; + +exports[`runs examples > example "_euclidRot" example index 4 1`] = ` +[ + "[ 0/1 → 1/8 | note:c3 ]", + "[ 3/8 → 1/2 | note:c3 ]", + "[ 3/4 → 7/8 | note:c3 ]", + "[ 1/1 → 9/8 | note:c3 ]", + "[ 11/8 → 3/2 | note:c3 ]", + "[ 7/4 → 15/8 | note:c3 ]", + "[ 2/1 → 17/8 | note:c3 ]", + "[ 19/8 → 5/2 | note:c3 ]", + "[ 11/4 → 23/8 | note:c3 ]", + "[ 3/1 → 25/8 | note:c3 ]", + "[ 27/8 → 7/2 | note:c3 ]", + "[ 15/4 → 31/8 | note:c3 ]", +] +`; + +exports[`runs examples > example "_euclidRot" example index 5 1`] = ` +[ + "[ 0/1 → 1/7 | note:c3 ]", + "[ 2/7 → 3/7 | note:c3 ]", + "[ 4/7 → 5/7 | note:c3 ]", + "[ 6/7 → 1/1 | note:c3 ]", + "[ 1/1 → 8/7 | note:c3 ]", + "[ 9/7 → 10/7 | note:c3 ]", + "[ 11/7 → 12/7 | note:c3 ]", + "[ 13/7 → 2/1 | note:c3 ]", + "[ 2/1 → 15/7 | note:c3 ]", + "[ 16/7 → 17/7 | note:c3 ]", + "[ 18/7 → 19/7 | note:c3 ]", + "[ 20/7 → 3/1 | note:c3 ]", + "[ 3/1 → 22/7 | note:c3 ]", + "[ 23/7 → 24/7 | note:c3 ]", + "[ 25/7 → 26/7 | note:c3 ]", + "[ 27/7 → 4/1 | note:c3 ]", +] +`; + +exports[`runs examples > example "_euclidRot" example index 6 1`] = ` +[ + "[ 1/9 → 2/9 | note:c3 ]", + "[ 1/3 → 4/9 | note:c3 ]", + "[ 5/9 → 2/3 | note:c3 ]", + "[ 7/9 → 8/9 | note:c3 ]", + "[ 10/9 → 11/9 | note:c3 ]", + "[ 4/3 → 13/9 | note:c3 ]", + "[ 14/9 → 5/3 | note:c3 ]", + "[ 16/9 → 17/9 | note:c3 ]", + "[ 19/9 → 20/9 | note:c3 ]", + "[ 7/3 → 22/9 | note:c3 ]", + "[ 23/9 → 8/3 | note:c3 ]", + "[ 25/9 → 26/9 | note:c3 ]", + "[ 28/9 → 29/9 | note:c3 ]", + "[ 10/3 → 31/9 | note:c3 ]", + "[ 32/9 → 11/3 | note:c3 ]", + "[ 34/9 → 35/9 | note:c3 ]", +] +`; + +exports[`runs examples > example "_euclidRot" example index 7 1`] = ` +[ + "[ 0/1 → 1/11 | note:c3 ]", + "[ 3/11 → 4/11 | note:c3 ]", + "[ 6/11 → 7/11 | note:c3 ]", + "[ 9/11 → 10/11 | note:c3 ]", + "[ 1/1 → 12/11 | note:c3 ]", + "[ 14/11 → 15/11 | note:c3 ]", + "[ 17/11 → 18/11 | note:c3 ]", + "[ 20/11 → 21/11 | note:c3 ]", + "[ 2/1 → 23/11 | note:c3 ]", + "[ 25/11 → 26/11 | note:c3 ]", + "[ 28/11 → 29/11 | note:c3 ]", + "[ 31/11 → 32/11 | note:c3 ]", + "[ 3/1 → 34/11 | note:c3 ]", + "[ 36/11 → 37/11 | note:c3 ]", + "[ 39/11 → 40/11 | note:c3 ]", + "[ 42/11 → 43/11 | note:c3 ]", +] +`; + +exports[`runs examples > example "_euclidRot" example index 8 1`] = ` +[ + "[ 1/6 → 1/3 | note:c3 ]", + "[ 1/3 → 1/2 | note:c3 ]", + "[ 1/2 → 2/3 | note:c3 ]", + "[ 2/3 → 5/6 | note:c3 ]", + "[ 5/6 → 1/1 | note:c3 ]", + "[ 7/6 → 4/3 | note:c3 ]", + "[ 4/3 → 3/2 | note:c3 ]", + "[ 3/2 → 5/3 | note:c3 ]", + "[ 5/3 → 11/6 | note:c3 ]", + "[ 11/6 → 2/1 | note:c3 ]", + "[ 13/6 → 7/3 | note:c3 ]", + "[ 7/3 → 5/2 | note:c3 ]", + "[ 5/2 → 8/3 | note:c3 ]", + "[ 8/3 → 17/6 | note:c3 ]", + "[ 17/6 → 3/1 | note:c3 ]", + "[ 19/6 → 10/3 | note:c3 ]", + "[ 10/3 → 7/2 | note:c3 ]", + "[ 7/2 → 11/3 | note:c3 ]", + "[ 11/3 → 23/6 | note:c3 ]", + "[ 23/6 → 4/1 | note:c3 ]", +] +`; + +exports[`runs examples > example "_euclidRot" example index 9 1`] = ` +[ + "[ 0/1 → 1/7 | note:c3 ]", + "[ 2/7 → 3/7 | note:c3 ]", + "[ 3/7 → 4/7 | note:c3 ]", + "[ 5/7 → 6/7 | note:c3 ]", + "[ 6/7 → 1/1 | note:c3 ]", + "[ 1/1 → 8/7 | note:c3 ]", + "[ 9/7 → 10/7 | note:c3 ]", + "[ 10/7 → 11/7 | note:c3 ]", + "[ 12/7 → 13/7 | note:c3 ]", + "[ 13/7 → 2/1 | note:c3 ]", + "[ 2/1 → 15/7 | note:c3 ]", + "[ 16/7 → 17/7 | note:c3 ]", + "[ 17/7 → 18/7 | note:c3 ]", + "[ 19/7 → 20/7 | note:c3 ]", + "[ 20/7 → 3/1 | note:c3 ]", + "[ 3/1 → 22/7 | note:c3 ]", + "[ 23/7 → 24/7 | note:c3 ]", + "[ 24/7 → 25/7 | note:c3 ]", + "[ 26/7 → 27/7 | note:c3 ]", + "[ 27/7 → 4/1 | note:c3 ]", +] +`; + +exports[`runs examples > example "_euclidRot" example index 10 1`] = ` +[ + "[ 1/8 → 1/4 | note:c3 ]", + "[ 1/4 → 3/8 | note:c3 ]", + "[ 1/2 → 5/8 | note:c3 ]", + "[ 5/8 → 3/4 | note:c3 ]", + "[ 7/8 → 1/1 | note:c3 ]", + "[ 9/8 → 5/4 | note:c3 ]", + "[ 5/4 → 11/8 | note:c3 ]", + "[ 3/2 → 13/8 | note:c3 ]", + "[ 13/8 → 7/4 | note:c3 ]", + "[ 15/8 → 2/1 | note:c3 ]", + "[ 17/8 → 9/4 | note:c3 ]", + "[ 9/4 → 19/8 | note:c3 ]", + "[ 5/2 → 21/8 | note:c3 ]", + "[ 21/8 → 11/4 | note:c3 ]", + "[ 23/8 → 3/1 | note:c3 ]", + "[ 25/8 → 13/4 | note:c3 ]", + "[ 13/4 → 27/8 | note:c3 ]", + "[ 7/2 → 29/8 | note:c3 ]", + "[ 29/8 → 15/4 | note:c3 ]", + "[ 31/8 → 4/1 | note:c3 ]", +] +`; + +exports[`runs examples > example "_euclidRot" example index 11 1`] = ` +[ + "[ 0/1 → 1/9 | note:c3 ]", + "[ 2/9 → 1/3 | note:c3 ]", + "[ 4/9 → 5/9 | note:c3 ]", + "[ 2/3 → 7/9 | note:c3 ]", + "[ 8/9 → 1/1 | note:c3 ]", + "[ 1/1 → 10/9 | note:c3 ]", + "[ 11/9 → 4/3 | note:c3 ]", + "[ 13/9 → 14/9 | note:c3 ]", + "[ 5/3 → 16/9 | note:c3 ]", + "[ 17/9 → 2/1 | note:c3 ]", + "[ 2/1 → 19/9 | note:c3 ]", + "[ 20/9 → 7/3 | note:c3 ]", + "[ 22/9 → 23/9 | note:c3 ]", + "[ 8/3 → 25/9 | note:c3 ]", + "[ 26/9 → 3/1 | note:c3 ]", + "[ 3/1 → 28/9 | note:c3 ]", + "[ 29/9 → 10/3 | note:c3 ]", + "[ 31/9 → 32/9 | note:c3 ]", + "[ 11/3 → 34/9 | note:c3 ]", + "[ 35/9 → 4/1 | note:c3 ]", +] +`; + +exports[`runs examples > example "_euclidRot" example index 12 1`] = ` +[ + "[ 1/11 → 2/11 | note:c3 ]", + "[ 3/11 → 4/11 | note:c3 ]", + "[ 5/11 → 6/11 | note:c3 ]", + "[ 7/11 → 8/11 | note:c3 ]", + "[ 9/11 → 10/11 | note:c3 ]", + "[ 12/11 → 13/11 | note:c3 ]", + "[ 14/11 → 15/11 | note:c3 ]", + "[ 16/11 → 17/11 | note:c3 ]", + "[ 18/11 → 19/11 | note:c3 ]", + "[ 20/11 → 21/11 | note:c3 ]", + "[ 23/11 → 24/11 | note:c3 ]", + "[ 25/11 → 26/11 | note:c3 ]", + "[ 27/11 → 28/11 | note:c3 ]", + "[ 29/11 → 30/11 | note:c3 ]", + "[ 31/11 → 32/11 | note:c3 ]", + "[ 34/11 → 35/11 | note:c3 ]", + "[ 36/11 → 37/11 | note:c3 ]", + "[ 38/11 → 39/11 | note:c3 ]", + "[ 40/11 → 41/11 | note:c3 ]", + "[ 42/11 → 43/11 | note:c3 ]", +] +`; + +exports[`runs examples > example "_euclidRot" example index 13 1`] = ` +[ + "[ 0/1 → 1/12 | note:c3 ]", + "[ 1/4 → 1/3 | note:c3 ]", + "[ 5/12 → 1/2 | note:c3 ]", + "[ 2/3 → 3/4 | note:c3 ]", + "[ 5/6 → 11/12 | note:c3 ]", + "[ 1/1 → 13/12 | note:c3 ]", + "[ 5/4 → 4/3 | note:c3 ]", + "[ 17/12 → 3/2 | note:c3 ]", + "[ 5/3 → 7/4 | note:c3 ]", + "[ 11/6 → 23/12 | note:c3 ]", + "[ 2/1 → 25/12 | note:c3 ]", + "[ 9/4 → 7/3 | note:c3 ]", + "[ 29/12 → 5/2 | note:c3 ]", + "[ 8/3 → 11/4 | note:c3 ]", + "[ 17/6 → 35/12 | note:c3 ]", + "[ 3/1 → 37/12 | note:c3 ]", + "[ 13/4 → 10/3 | note:c3 ]", + "[ 41/12 → 7/2 | note:c3 ]", + "[ 11/3 → 15/4 | note:c3 ]", + "[ 23/6 → 47/12 | note:c3 ]", +] +`; + +exports[`runs examples > example "_euclidRot" example index 14 1`] = ` +[ + "[ 1/16 → 1/8 | note:c3 ]", + "[ 1/4 → 5/16 | note:c3 ]", + "[ 7/16 → 1/2 | note:c3 ]", + "[ 5/8 → 11/16 | note:c3 ]", + "[ 13/16 → 7/8 | note:c3 ]", + "[ 17/16 → 9/8 | note:c3 ]", + "[ 5/4 → 21/16 | note:c3 ]", + "[ 23/16 → 3/2 | note:c3 ]", + "[ 13/8 → 27/16 | note:c3 ]", + "[ 29/16 → 15/8 | note:c3 ]", + "[ 33/16 → 17/8 | note:c3 ]", + "[ 9/4 → 37/16 | note:c3 ]", + "[ 39/16 → 5/2 | note:c3 ]", + "[ 21/8 → 43/16 | note:c3 ]", + "[ 45/16 → 23/8 | note:c3 ]", + "[ 49/16 → 25/8 | note:c3 ]", + "[ 13/4 → 53/16 | note:c3 ]", + "[ 55/16 → 7/2 | note:c3 ]", + "[ 29/8 → 59/16 | note:c3 ]", + "[ 61/16 → 31/8 | note:c3 ]", +] +`; + +exports[`runs examples > example "_euclidRot" example index 15 1`] = ` +[ + "[ 1/8 → 1/4 | note:c3 ]", + "[ 1/4 → 3/8 | note:c3 ]", + "[ 3/8 → 1/2 | note:c3 ]", + "[ 1/2 → 5/8 | note:c3 ]", + "[ 5/8 → 3/4 | note:c3 ]", + "[ 3/4 → 7/8 | note:c3 ]", + "[ 7/8 → 1/1 | note:c3 ]", + "[ 9/8 → 5/4 | note:c3 ]", + "[ 5/4 → 11/8 | note:c3 ]", + "[ 11/8 → 3/2 | note:c3 ]", + "[ 3/2 → 13/8 | note:c3 ]", + "[ 13/8 → 7/4 | note:c3 ]", + "[ 7/4 → 15/8 | note:c3 ]", + "[ 15/8 → 2/1 | note:c3 ]", + "[ 17/8 → 9/4 | note:c3 ]", + "[ 9/4 → 19/8 | note:c3 ]", + "[ 19/8 → 5/2 | note:c3 ]", + "[ 5/2 → 21/8 | note:c3 ]", + "[ 21/8 → 11/4 | note:c3 ]", + "[ 11/4 → 23/8 | note:c3 ]", + "[ 23/8 → 3/1 | note:c3 ]", + "[ 25/8 → 13/4 | note:c3 ]", + "[ 13/4 → 27/8 | note:c3 ]", + "[ 27/8 → 7/2 | note:c3 ]", + "[ 7/2 → 29/8 | note:c3 ]", + "[ 29/8 → 15/4 | note:c3 ]", + "[ 15/4 → 31/8 | note:c3 ]", + "[ 31/8 → 4/1 | note:c3 ]", +] +`; + +exports[`runs examples > example "_euclidRot" example index 16 1`] = ` +[ + "[ 1/12 → 1/6 | note:c3 ]", + "[ 1/6 → 1/4 | note:c3 ]", + "[ 1/3 → 5/12 | note:c3 ]", + "[ 1/2 → 7/12 | note:c3 ]", + "[ 7/12 → 2/3 | note:c3 ]", + "[ 3/4 → 5/6 | note:c3 ]", + "[ 11/12 → 1/1 | note:c3 ]", + "[ 13/12 → 7/6 | note:c3 ]", + "[ 7/6 → 5/4 | note:c3 ]", + "[ 4/3 → 17/12 | note:c3 ]", + "[ 3/2 → 19/12 | note:c3 ]", + "[ 19/12 → 5/3 | note:c3 ]", + "[ 7/4 → 11/6 | note:c3 ]", + "[ 23/12 → 2/1 | note:c3 ]", + "[ 25/12 → 13/6 | note:c3 ]", + "[ 13/6 → 9/4 | note:c3 ]", + "[ 7/3 → 29/12 | note:c3 ]", + "[ 5/2 → 31/12 | note:c3 ]", + "[ 31/12 → 8/3 | note:c3 ]", + "[ 11/4 → 17/6 | note:c3 ]", + "[ 35/12 → 3/1 | note:c3 ]", + "[ 37/12 → 19/6 | note:c3 ]", + "[ 19/6 → 13/4 | note:c3 ]", + "[ 10/3 → 41/12 | note:c3 ]", + "[ 7/2 → 43/12 | note:c3 ]", + "[ 43/12 → 11/3 | note:c3 ]", + "[ 15/4 → 23/6 | note:c3 ]", + "[ 47/12 → 4/1 | note:c3 ]", +] +`; + +exports[`runs examples > example "_euclidRot" example index 17 1`] = ` +[ + "[ 1/16 → 1/8 | note:c3 ]", + "[ 3/16 → 1/4 | note:c3 ]", + "[ 5/16 → 3/8 | note:c3 ]", + "[ 1/2 → 9/16 | note:c3 ]", + "[ 5/8 → 11/16 | note:c3 ]", + "[ 3/4 → 13/16 | note:c3 ]", + "[ 7/8 → 15/16 | note:c3 ]", + "[ 17/16 → 9/8 | note:c3 ]", + "[ 19/16 → 5/4 | note:c3 ]", + "[ 21/16 → 11/8 | note:c3 ]", + "[ 3/2 → 25/16 | note:c3 ]", + "[ 13/8 → 27/16 | note:c3 ]", + "[ 7/4 → 29/16 | note:c3 ]", + "[ 15/8 → 31/16 | note:c3 ]", + "[ 33/16 → 17/8 | note:c3 ]", + "[ 35/16 → 9/4 | note:c3 ]", + "[ 37/16 → 19/8 | note:c3 ]", + "[ 5/2 → 41/16 | note:c3 ]", + "[ 21/8 → 43/16 | note:c3 ]", + "[ 11/4 → 45/16 | note:c3 ]", + "[ 23/8 → 47/16 | note:c3 ]", + "[ 49/16 → 25/8 | note:c3 ]", + "[ 51/16 → 13/4 | note:c3 ]", + "[ 53/16 → 27/8 | note:c3 ]", + "[ 7/2 → 57/16 | note:c3 ]", + "[ 29/8 → 59/16 | note:c3 ]", + "[ 15/4 → 61/16 | note:c3 ]", + "[ 31/8 → 63/16 | note:c3 ]", +] +`; + +exports[`runs examples > example "_euclidRot" example index 18 1`] = ` +[ + "[ 1/16 → 1/8 | note:c3 ]", + "[ 1/8 → 3/16 | note:c3 ]", + "[ 1/4 → 5/16 | note:c3 ]", + "[ 3/8 → 7/16 | note:c3 ]", + "[ 1/2 → 9/16 | note:c3 ]", + "[ 9/16 → 5/8 | note:c3 ]", + "[ 11/16 → 3/4 | note:c3 ]", + "[ 13/16 → 7/8 | note:c3 ]", + "[ 15/16 → 1/1 | note:c3 ]", + "[ 17/16 → 9/8 | note:c3 ]", + "[ 9/8 → 19/16 | note:c3 ]", + "[ 5/4 → 21/16 | note:c3 ]", + "[ 11/8 → 23/16 | note:c3 ]", + "[ 3/2 → 25/16 | note:c3 ]", + "[ 25/16 → 13/8 | note:c3 ]", + "[ 27/16 → 7/4 | note:c3 ]", + "[ 29/16 → 15/8 | note:c3 ]", + "[ 31/16 → 2/1 | note:c3 ]", + "[ 33/16 → 17/8 | note:c3 ]", + "[ 17/8 → 35/16 | note:c3 ]", + "[ 9/4 → 37/16 | note:c3 ]", + "[ 19/8 → 39/16 | note:c3 ]", + "[ 5/2 → 41/16 | note:c3 ]", + "[ 41/16 → 21/8 | note:c3 ]", + "[ 43/16 → 11/4 | note:c3 ]", + "[ 45/16 → 23/8 | note:c3 ]", + "[ 47/16 → 3/1 | note:c3 ]", + "[ 49/16 → 25/8 | note:c3 ]", + "[ 25/8 → 51/16 | note:c3 ]", + "[ 13/4 → 53/16 | note:c3 ]", + "[ 27/8 → 55/16 | note:c3 ]", + "[ 7/2 → 57/16 | note:c3 ]", + "[ 57/16 → 29/8 | note:c3 ]", + "[ 59/16 → 15/4 | note:c3 ]", + "[ 61/16 → 31/8 | note:c3 ]", + "[ 63/16 → 4/1 | note:c3 ]", +] +`; + +exports[`runs examples > example "_euclidRot" example index 19 1`] = ` +[ + "[ 1/24 → 1/12 | note:c3 ]", + "[ 1/6 → 5/24 | note:c3 ]", + "[ 1/4 → 7/24 | note:c3 ]", + "[ 1/3 → 3/8 | note:c3 ]", + "[ 5/12 → 11/24 | note:c3 ]", + "[ 1/2 → 13/24 | note:c3 ]", + "[ 7/12 → 5/8 | note:c3 ]", + "[ 17/24 → 3/4 | note:c3 ]", + "[ 19/24 → 5/6 | note:c3 ]", + "[ 7/8 → 11/12 | note:c3 ]", + "[ 23/24 → 1/1 | note:c3 ]", + "[ 25/24 → 13/12 | note:c3 ]", + "[ 7/6 → 29/24 | note:c3 ]", + "[ 5/4 → 31/24 | note:c3 ]", + "[ 4/3 → 11/8 | note:c3 ]", + "[ 17/12 → 35/24 | note:c3 ]", + "[ 3/2 → 37/24 | note:c3 ]", + "[ 19/12 → 13/8 | note:c3 ]", + "[ 41/24 → 7/4 | note:c3 ]", + "[ 43/24 → 11/6 | note:c3 ]", + "[ 15/8 → 23/12 | note:c3 ]", + "[ 47/24 → 2/1 | note:c3 ]", + "[ 49/24 → 25/12 | note:c3 ]", + "[ 13/6 → 53/24 | note:c3 ]", + "[ 9/4 → 55/24 | note:c3 ]", + "[ 7/3 → 19/8 | note:c3 ]", + "[ 29/12 → 59/24 | note:c3 ]", + "[ 5/2 → 61/24 | note:c3 ]", + "[ 31/12 → 21/8 | note:c3 ]", + "[ 65/24 → 11/4 | note:c3 ]", + "[ 67/24 → 17/6 | note:c3 ]", + "[ 23/8 → 35/12 | note:c3 ]", + "[ 71/24 → 3/1 | note:c3 ]", + "[ 73/24 → 37/12 | note:c3 ]", + "[ 19/6 → 77/24 | note:c3 ]", + "[ 13/4 → 79/24 | note:c3 ]", + "[ 10/3 → 27/8 | note:c3 ]", + "[ 41/12 → 83/24 | note:c3 ]", + "[ 7/2 → 85/24 | note:c3 ]", + "[ 43/12 → 29/8 | note:c3 ]", + "[ 89/24 → 15/4 | note:c3 ]", + "[ 91/24 → 23/6 | note:c3 ]", + "[ 31/8 → 47/12 | note:c3 ]", + "[ 95/24 → 4/1 | note:c3 ]", +] +`; + +exports[`runs examples > example "_euclidRot" example index 20 1`] = ` +[ + "[ 0/1 → 1/24 | note:c3 ]", + "[ 1/12 → 1/8 | note:c3 ]", + "[ 1/6 → 5/24 | note:c3 ]", + "[ 1/4 → 7/24 | note:c3 ]", + "[ 7/24 → 1/3 | note:c3 ]", + "[ 3/8 → 5/12 | note:c3 ]", + "[ 11/24 → 1/2 | note:c3 ]", + "[ 13/24 → 7/12 | note:c3 ]", + "[ 5/8 → 2/3 | note:c3 ]", + "[ 17/24 → 3/4 | note:c3 ]", + "[ 3/4 → 19/24 | note:c3 ]", + "[ 5/6 → 7/8 | note:c3 ]", + "[ 11/12 → 23/24 | note:c3 ]", + "[ 1/1 → 25/24 | note:c3 ]", + "[ 13/12 → 9/8 | note:c3 ]", + "[ 7/6 → 29/24 | note:c3 ]", + "[ 5/4 → 31/24 | note:c3 ]", + "[ 31/24 → 4/3 | note:c3 ]", + "[ 11/8 → 17/12 | note:c3 ]", + "[ 35/24 → 3/2 | note:c3 ]", + "[ 37/24 → 19/12 | note:c3 ]", + "[ 13/8 → 5/3 | note:c3 ]", + "[ 41/24 → 7/4 | note:c3 ]", + "[ 7/4 → 43/24 | note:c3 ]", + "[ 11/6 → 15/8 | note:c3 ]", + "[ 23/12 → 47/24 | note:c3 ]", + "[ 2/1 → 49/24 | note:c3 ]", + "[ 25/12 → 17/8 | note:c3 ]", + "[ 13/6 → 53/24 | note:c3 ]", + "[ 9/4 → 55/24 | note:c3 ]", + "[ 55/24 → 7/3 | note:c3 ]", + "[ 19/8 → 29/12 | note:c3 ]", + "[ 59/24 → 5/2 | note:c3 ]", + "[ 61/24 → 31/12 | note:c3 ]", + "[ 21/8 → 8/3 | note:c3 ]", + "[ 65/24 → 11/4 | note:c3 ]", + "[ 11/4 → 67/24 | note:c3 ]", + "[ 17/6 → 23/8 | note:c3 ]", + "[ 35/12 → 71/24 | note:c3 ]", + "[ 3/1 → 73/24 | note:c3 ]", + "[ 37/12 → 25/8 | note:c3 ]", + "[ 19/6 → 77/24 | note:c3 ]", + "[ 13/4 → 79/24 | note:c3 ]", + "[ 79/24 → 10/3 | note:c3 ]", + "[ 27/8 → 41/12 | note:c3 ]", + "[ 83/24 → 7/2 | note:c3 ]", + "[ 85/24 → 43/12 | note:c3 ]", + "[ 29/8 → 11/3 | note:c3 ]", + "[ 89/24 → 15/4 | note:c3 ]", + "[ 15/4 → 91/24 | note:c3 ]", + "[ 23/6 → 31/8 | note:c3 ]", + "[ 47/12 → 95/24 | note:c3 ]", +] +`; + exports[`runs examples > example "accelerate" example index 0 1`] = ` [ "[ (0/1 → 1/1) ⇝ 2/1 | s:sax accelerate:0 ]", @@ -1544,6 +2109,23 @@ exports[`runs examples > example "euclidLegato" example index 0 1`] = ` ] `; +exports[`runs examples > example "euclidRot" example index 0 1`] = ` +[ + "[ 1/4 → 5/16 | note:c3 ]", + "[ 9/16 → 5/8 | note:c3 ]", + "[ 15/16 → 1/1 | note:c3 ]", + "[ 5/4 → 21/16 | note:c3 ]", + "[ 25/16 → 13/8 | note:c3 ]", + "[ 31/16 → 2/1 | note:c3 ]", + "[ 9/4 → 37/16 | note:c3 ]", + "[ 41/16 → 21/8 | note:c3 ]", + "[ 47/16 → 3/1 | note:c3 ]", + "[ 13/4 → 53/16 | note:c3 ]", + "[ 57/16 → 29/8 | note:c3 ]", + "[ 63/16 → 4/1 | note:c3 ]", +] +`; + exports[`runs examples > example "every" example index 0 1`] = ` [ "[ 3/4 → 1/1 | note:c3 ]", diff --git a/website/astro.config.mjs b/website/astro.config.mjs index 6714f548..9dca58df 100644 --- a/website/astro.config.mjs +++ b/website/astro.config.mjs @@ -31,7 +31,7 @@ export default defineConfig({ tailwind(), ], site: `https://strudel.tidalcycles.org`, - base: '', + base: '/', }); /* diff --git a/website/src/components/HeadCommon.astro b/website/src/components/HeadCommon.astro index 410e9b91..ecc6ae21 100644 --- a/website/src/components/HeadCommon.astro +++ b/website/src/components/HeadCommon.astro @@ -1,5 +1,8 @@ --- import '../styles/index.css'; + +const { BASE_URL } = import.meta.env; +const base = BASE_URL; --- @@ -7,9 +10,11 @@ import '../styles/index.css'; - + - + + + @@ -17,7 +22,7 @@ import '../styles/index.css'; - +