mirror of
https://github.com/eliasstepanik/strudel-docker.git
synced 2026-01-11 21:58:31 +00:00
Merge branch 'main' into docs
This commit is contained in:
commit
cafde1e910
@ -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 `<your-username>.github.io/strudel`
|
||||
3. edit `website/astro.config.mjs` to use site: `<your-username>.github.io/strudel` and base `/strudel`
|
||||
4. go to Actions -> "Build and Deploy" and click "Run workflow"
|
||||
5. view your patterns at `<your-username>.github.io/strudel/my-patterns`
|
||||
4. go to Actions -> `Build and Deploy` and click `Run workflow`
|
||||
5. view your patterns at `<your-username>.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).
|
||||
|
||||
67
packages/core/animate.mjs
Normal file
67
packages/core/animate.mjs
Normal file
@ -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);
|
||||
});
|
||||
@ -4,44 +4,49 @@ Copyright (C) 2022 Strudel contributors - see <https://github.com/tidalcycles/st
|
||||
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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);
|
||||
});
|
||||
|
||||
@ -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';
|
||||
|
||||
@ -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
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -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
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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('<a b>')).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('[<a b c>]!3 d')).toEqual(minS('<a b c> <a b c> <a b c> 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?')
|
||||
|
||||
@ -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 ]",
|
||||
|
||||
@ -31,7 +31,7 @@ export default defineConfig({
|
||||
tailwind(),
|
||||
],
|
||||
site: `https://strudel.tidalcycles.org`,
|
||||
base: '',
|
||||
base: '/',
|
||||
});
|
||||
|
||||
/*
|
||||
|
||||
@ -1,5 +1,8 @@
|
||||
---
|
||||
import '../styles/index.css';
|
||||
|
||||
const { BASE_URL } = import.meta.env;
|
||||
const base = BASE_URL;
|
||||
---
|
||||
|
||||
<!-- Global Metadata -->
|
||||
@ -7,9 +10,11 @@ import '../styles/index.css';
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<meta name="generator" content={Astro.generator} />
|
||||
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.ico" />
|
||||
<link rel="icon" type="image/svg+xml" href="favicon.ico" />
|
||||
|
||||
<link rel="sitemap" href="/sitemap.xml" />
|
||||
<link rel="sitemap" href="./sitemap.xml" />
|
||||
|
||||
<base href={base} />
|
||||
|
||||
<!-- Preload Fonts -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
@ -17,7 +22,7 @@ import '../styles/index.css';
|
||||
<link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:ital@0;1&display=swap" rel="stylesheet" />
|
||||
|
||||
<!-- Scrollable a11y code helper -->
|
||||
<script src="/make-scrollable-code-focusable.js" is:inline></script>
|
||||
<script src="./make-scrollable-code-focusable.js" is:inline></script>
|
||||
|
||||
<!-- This is intentionally inlined to avoid FOUC -->
|
||||
<script is:inline>
|
||||
|
||||
@ -38,7 +38,7 @@ const sidebar = SIDEBAR[langCode];
|
||||
<div class="search-item h-10">
|
||||
<!-- <Search client:idle /> -->
|
||||
</div>
|
||||
<a href="/" class="hidden md:flex cursor-pointer items-center space-x-1"
|
||||
<a href="./" class="hidden md:flex cursor-pointer items-center space-x-1"
|
||||
><CommandLineIcon className="w-5 h-5" /><span>go to REPL</span>
|
||||
</a>
|
||||
<div class="md:hidden">
|
||||
|
||||
@ -7,7 +7,9 @@ type Props = {
|
||||
};
|
||||
|
||||
const { currentPage } = Astro.props as Props;
|
||||
const currentPageMatch = currentPage.endsWith('/') ? currentPage.slice(1, -1) : currentPage.slice(1);
|
||||
const { BASE_URL } = import.meta.env;
|
||||
let currentPageMatch = currentPage.slice(BASE_URL.length, currentPage.endsWith('/') ? -1 : undefined);
|
||||
|
||||
const langCode = 'en'; // getLanguageFromURL(currentPage);
|
||||
const sidebar = SIDEBAR[langCode];
|
||||
---
|
||||
@ -21,7 +23,7 @@ const sidebar = SIDEBAR[langCode];
|
||||
<h2>{header}</h2>
|
||||
<ul>
|
||||
{children.map((child) => {
|
||||
const url = Astro.site?.pathname + child.link;
|
||||
const url = '.' + Astro.site?.pathname + child.link;
|
||||
return (
|
||||
<li class="">
|
||||
<a
|
||||
|
||||
@ -12,6 +12,7 @@ type Props = {
|
||||
|
||||
const { frontmatter, headings, githubEditUrl } = Astro.props as Props;
|
||||
const title = frontmatter.title;
|
||||
const currentPage = Astro.url.pathname;
|
||||
---
|
||||
|
||||
<article id="article" class="content">
|
||||
@ -19,7 +20,7 @@ const title = frontmatter.title;
|
||||
<!-- TODO: add dropdown toc on mobile -->
|
||||
<!-- <nav class="block sm:hidden mb-8">
|
||||
<span>On this Page:</span>
|
||||
<TableOfContents client:media="(max-width: 50em)" headings={headings} />
|
||||
<TableOfContents client:media="(max-width: 50em)" headings={headings} currentPage={currentPage} />
|
||||
</nav> -->
|
||||
<div class="prose prose-invert max-w-full pb-8">
|
||||
<slot />
|
||||
|
||||
@ -9,9 +9,10 @@ type Props = {
|
||||
};
|
||||
|
||||
const { headings, githubEditUrl } = Astro.props as Props;
|
||||
const currentPage = Astro.url.pathname;
|
||||
---
|
||||
|
||||
<nav aria-labelledby="grid-right">
|
||||
<TableOfContents client:media="(min-width: 50em)" headings={headings} />
|
||||
<TableOfContents client:media="(min-width: 50em)" headings={headings} currentPage={currentPage} />
|
||||
<MoreMenu editHref={githubEditUrl} />
|
||||
</nav>
|
||||
|
||||
@ -8,7 +8,10 @@ type ItemOffsets = {
|
||||
topOffset: number;
|
||||
};
|
||||
|
||||
const TableOfContents: FunctionalComponent<{ headings: MarkdownHeading[] }> = ({ headings = [] }) => {
|
||||
const TableOfContents: FunctionalComponent<{ headings: MarkdownHeading[]; currentPage: string }> = ({
|
||||
headings = [],
|
||||
currentPage,
|
||||
}) => {
|
||||
const toc = useRef<any>();
|
||||
const onThisPageID = 'on-this-page-heading';
|
||||
const itemOffsets = useRef<ItemOffsets[]>([]);
|
||||
@ -73,7 +76,7 @@ const TableOfContents: FunctionalComponent<{ headings: MarkdownHeading[] }> = ({
|
||||
.map((heading) => (
|
||||
<li className={`w-full`}>
|
||||
<a
|
||||
href={`#${heading.slug}`}
|
||||
href={`${currentPage}#${heading.slug}`}
|
||||
onClick={onLinkClick}
|
||||
className={`py-0.5 block cursor-pointer w-full border-l-4 border-header hover:bg-header ${
|
||||
['pl-4', 'pl-9', 'pl-12'][heading.depth - minDepth]
|
||||
|
||||
@ -1,16 +1,20 @@
|
||||
---
|
||||
import * as tunes from '../../../src/repl/tunes.mjs';
|
||||
import HeadCommon from '../../components/HeadCommon.astro';
|
||||
---
|
||||
|
||||
<head>
|
||||
<HeadCommon />
|
||||
</head>
|
||||
<body class="bg-slate-800">
|
||||
<div class="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-6 gap-2 p-2 select-none">
|
||||
{
|
||||
Object.entries(tunes).map(([name, tune]) => (
|
||||
<a class="rounded-md bg-slate-900 hover:bg-slate-700 cursor-pointer relative" href={`/#${btoa(tune)}`}>
|
||||
<a class="rounded-md bg-slate-900 hover:bg-slate-700 cursor-pointer relative" href={`./#${btoa(tune)}`}>
|
||||
<div class="absolute w-full h-full flex justify-center items-center">
|
||||
<span class="bg-slate-800 p-2 rounded-md text-white">{name}</span>
|
||||
</div>
|
||||
<img src={`/img/example-${name}.png`} />
|
||||
<img src={`./img/example-${name}.png`} />
|
||||
</a>
|
||||
))
|
||||
}
|
||||
|
||||
@ -42,7 +42,7 @@ This interactive tutorial will guide you through the basics of Strudel.
|
||||
To see and hear what Strudel can do, visit the [Strudel REPL](https://strudel.tidalcycles.org/) and click the Shuffle icon in the top menu bar.
|
||||
You can get a feel for Strudel by browsing and editing these examples and clicking the Refresh icon to update.
|
||||
|
||||
You can also browse through the examples [here](/examples).
|
||||
You can also browse through the examples [here](./examples).
|
||||
|
||||
Alternatively, you can get a taste of what Strudel can do by clicking play on this track:
|
||||
|
||||
|
||||
@ -2,10 +2,14 @@
|
||||
import { getMyPatterns } from './list.json';
|
||||
|
||||
import { Content } from '../../../../my-patterns/README.md';
|
||||
import HeadCommon from '../../components/HeadCommon.astro';
|
||||
|
||||
const myPatterns = await getMyPatterns();
|
||||
---
|
||||
|
||||
<head>
|
||||
<HeadCommon />
|
||||
</head>
|
||||
<body class="bg-slate-800">
|
||||
{
|
||||
Object.keys(myPatterns).length === 0 && (
|
||||
@ -19,12 +23,12 @@ const myPatterns = await getMyPatterns();
|
||||
Object.entries(myPatterns).map(([name, tune]) => (
|
||||
<a
|
||||
class="rounded-md bg-slate-900 hover:bg-slate-700 cursor-pointer relative"
|
||||
href={`../#${btoa(tune as string)}`}
|
||||
href={`./#${btoa(tune as string)}`}
|
||||
>
|
||||
<div class="absolute w-full h-full flex justify-center items-center">
|
||||
<span class="bg-slate-800 p-2 rounded-md text-white">{name}</span>
|
||||
</div>
|
||||
<img src={`./${name}.png`} />
|
||||
<img src={`./swatch/${name}.png`} />
|
||||
</a>
|
||||
))
|
||||
}
|
||||
@ -1,17 +1,17 @@
|
||||
import { Pattern, toMidi, valueToMidi } from '@strudel.cycles/core';
|
||||
import { samples } from '@strudel.cycles/webaudio';
|
||||
|
||||
export async function prebake({ baseDir = '' } = {}) {
|
||||
export async function prebake() {
|
||||
// https://archive.org/details/SalamanderGrandPianoV3
|
||||
// License: CC-by http://creativecommons.org/licenses/by/3.0/ Author: Alexander Holm
|
||||
return await Promise.all([
|
||||
samples('/piano.json', `${baseDir}/piano/`),
|
||||
samples(`./piano.json`, `./piano/`),
|
||||
// https://github.com/sgossner/VCSL/
|
||||
// https://api.github.com/repositories/126427031/contents/
|
||||
// LICENSE: CC0 general-purpose
|
||||
samples('/vcsl.json', 'github:sgossner/VCSL/master/'),
|
||||
samples('/tidal-drum-machines.json', 'github:ritchse/tidal-drum-machines/main/machines/'),
|
||||
samples('/EmuSP12.json', `${baseDir}/EmuSP12/`),
|
||||
samples(`./vcsl.json`, 'github:sgossner/VCSL/master/'),
|
||||
samples(`./tidal-drum-machines.json`, 'github:ritchse/tidal-drum-machines/main/machines/'),
|
||||
samples(`./EmuSP12.json`, `./EmuSP12/`),
|
||||
// samples('github:tidalcycles/Dirt-Samples/master'),
|
||||
]);
|
||||
}
|
||||
|
||||
@ -425,7 +425,7 @@ stack(
|
||||
.scale(cat('D minor pentatonic')).note()
|
||||
.s('bell').gain(.6).delay(.2).delaytime(1/3).delayfeedback(.8),
|
||||
// bass
|
||||
"<D2 A2 G2 F2>".euclidLegato(6,8,1).note().s('bass').clip(1).gain(.8)
|
||||
"<D2 A2 G2 F2>".euclidLegatoRot(6,8,1).note().s('bass').clip(1).gain(.8)
|
||||
)
|
||||
.slow(6)
|
||||
.pianoroll({minMidi:20,maxMidi:120,background:'transparent'})
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user