Merge branch 'tidalcycles:main' into ableton_link_integration

This commit is contained in:
Jade Rowland
2023-10-01 20:44:09 -04:00
committed by GitHub
43 changed files with 1010 additions and 358 deletions
+1 -1
View File
@@ -42,7 +42,7 @@ jobs:
if: matrix.platform == 'ubuntu-latest'
run: |
sudo apt-get update
sudo apt-get install -y libgtk-3-dev webkit2gtk-4.0 libappindicator3-dev librsvg2-dev patchelf
sudo apt-get install -y libgtk-3-dev webkit2gtk-4.0 libappindicator3-dev librsvg2-dev patchelf libasound2-dev
- name: Install app dependencies from lockfile and build web
run: pnpm install
+1
View File
@@ -1,3 +1,4 @@
export * from './codemirror.mjs';
export * from './highlight.mjs';
export * from './flash.mjs';
export * from './slider.mjs';
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel/codemirror",
"version": "0.8.4",
"version": "0.9.0",
"description": "Codemirror Extensions for Strudel",
"main": "index.mjs",
"publishConfig": {
+132
View File
@@ -0,0 +1,132 @@
import { ref, pure } from '@strudel.cycles/core';
import { WidgetType, ViewPlugin, Decoration } from '@codemirror/view';
import { StateEffect, StateField } from '@codemirror/state';
export let sliderValues = {};
const getSliderID = (from) => `slider_${from}`;
export class SliderWidget extends WidgetType {
constructor(value, min, max, from, to, view) {
super();
this.value = value;
this.min = min;
this.max = max;
this.from = from;
this.originalFrom = from;
this.to = to;
this.view = view;
}
eq() {
return false;
}
toDOM() {
let wrap = document.createElement('span');
wrap.setAttribute('aria-hidden', 'true');
wrap.className = 'cm-slider'; // inline-flex items-center
let slider = wrap.appendChild(document.createElement('input'));
slider.type = 'range';
slider.min = this.min;
slider.max = this.max;
slider.step = (this.max - this.min) / 1000;
slider.originalValue = this.value;
// to make sure the code stays in sync, let's save the original value
// becuase .value automatically clamps values so it'll desync with the code
slider.value = slider.originalValue;
slider.from = this.from;
slider.originalFrom = this.originalFrom;
slider.to = this.to;
slider.style = 'width:64px;margin-right:4px;transform:translateY(4px)';
this.slider = slider;
slider.addEventListener('input', (e) => {
const next = e.target.value;
let insert = next;
//let insert = next.toFixed(2);
const to = slider.from + slider.originalValue.length;
let change = { from: slider.from, to, insert };
slider.originalValue = insert;
slider.value = insert;
this.view.dispatch({ changes: change });
const id = getSliderID(slider.originalFrom); // matches id generated in transpiler
window.postMessage({ type: 'cm-slider', value: Number(next), id });
});
return wrap;
}
ignoreEvent(e) {
return true;
}
}
export const setWidgets = StateEffect.define();
export const updateWidgets = (view, widgets) => {
view.dispatch({ effects: setWidgets.of(widgets) });
};
function getWidgets(widgetConfigs, view) {
return widgetConfigs.map(({ from, to, value, min, max }) => {
return Decoration.widget({
widget: new SliderWidget(value, min, max, from, to, view),
side: 0,
}).range(from /* , to */);
});
}
export const sliderPlugin = ViewPlugin.fromClass(
class {
decorations; //: DecorationSet
constructor(view /* : EditorView */) {
this.decorations = Decoration.set([]);
}
update(update /* : ViewUpdate */) {
update.transactions.forEach((tr) => {
if (tr.docChanged) {
this.decorations = this.decorations.map(tr.changes);
const iterator = this.decorations.iter();
while (iterator.value) {
// when the widgets are moved, we need to tell the dom node the current position
// this is important because the updateSliderValue function has to work with the dom node
iterator.value.widget.slider.from = iterator.from;
iterator.value.widget.slider.to = iterator.to;
iterator.next();
}
}
for (let e of tr.effects) {
if (e.is(setWidgets)) {
this.decorations = Decoration.set(getWidgets(e.value, update.view));
}
}
});
}
},
{
decorations: (v) => v.decorations,
},
);
export let slider = (value) => {
console.warn('slider will only work when the transpiler is used... passing value as is');
return pure(value);
};
// function transpiled from slider = (value, min, max)
export let sliderWithID = (id, value, min, max) => {
sliderValues[id] = value; // sync state at eval time (code -> state)
return ref(() => sliderValues[id]); // use state at query time
};
// update state when sliders are moved
if (typeof window !== 'undefined') {
window.addEventListener('message', (e) => {
if (e.data.type === 'cm-slider') {
if (sliderValues[e.data.id] !== undefined) {
// update state when slider is moved
sliderValues[e.data.id] = e.data.value;
} else {
console.warn(`slider with id "${e.data.id}" is not registered. Only ${Object.keys(sliderValues)}`);
}
}
});
}
+69 -13
View File
@@ -299,16 +299,52 @@ const generic_params = [
*/
['end'],
/**
* Loops the sample (from `begin` to `end`) the specified number of times.
* Loops the sample.
* Note that the tempo of the loop is not synced with the cycle tempo.
* To change the loop region, use loopBegin / loopEnd.
*
* @name loop
* @param {number | Pattern} times How often the sample is looped
* @param {number | Pattern} on If 1, the sample is looped
* @example
* s("bd").loop("<1 2 3 4>").osc()
* s("casio").loop(1)
*
*/
['loop'],
/**
* Begin to loop at a specific point in the sample (inbetween `begin` and `end`).
* Note that the loop point must be inbetween `begin` and `end`, and before `loopEnd`!
* Note: Samples starting with wt_ will automatically loop! (wt = wavetable)
*
* @name loopBegin
* @param {number | Pattern} time between 0 and 1, where 1 is the length of the sample
* @synonyms loopb
* @example
* s("space").loop(1)
* .loopBegin("<0 .125 .25>").scope()
*/
['loopBegin', 'loopb'],
/**
*
* End the looping section at a specific point in the sample (inbetween `begin` and `end`).
* Note that the loop point must be inbetween `begin` and `end`, and after `loopBegin`!
*
* @name loopEnd
* @param {number | Pattern} time between 0 and 1, where 1 is the length of the sample
* @synonyms loope
* @example
* s("space").loop(1)
* .loopEnd("<1 .75 .5 .25>").scope()
*/
['loopEnd', 'loope'],
/**
* bit crusher effect.
*
* @name crush
* @param {number | Pattern} depth between 1 (for drastic reduction in bit-depth) to 16 (for barely no reduction).
* @example
* s("<bd sd>,hh*3").fast(2).crush("<16 8 7 6 5 4 3 2>")
*
*/
// TODO: currently duplicated with "native" legato
// TODO: superdirt legato will do more: https://youtu.be/dQPmE1WaD1k?t=419
/**
@@ -323,15 +359,6 @@ const generic_params = [
*/
// ['legato'],
// ['clhatdecay'],
/**
* bit crusher effect.
*
* @name crush
* @param {number | Pattern} depth between 1 (for drastic reduction in bit-depth) to 16 (for barely no reduction).
* @example
* s("<bd sd>,hh*3").fast(2).crush("<16 8 7 6 5 4 3 2>")
*
*/
['crush'],
/**
* fake-resampling for lowering the sample rate. Caution: This effect seems to only work in chromium based browsers
@@ -343,7 +370,6 @@ const generic_params = [
*
*/
['coarse'],
/**
* choose the channel the pattern is sent to in superdirt
*
@@ -614,6 +640,36 @@ const generic_params = [
*/
// currently an alias of 'hcutoff' https://github.com/tidalcycles/strudel/issues/496
// ['hpf'],
/**
* Applies a vibrato to the frequency of the oscillator.
*
* @name vib
* @synonyms vibrato, v
* @param {number | Pattern} frequency of the vibrato in hertz
* @example
* note("a")
* .vib("<.5 1 2 4 8 16>")
* @example
* // change the modulation depth with ":"
* note("a")
* .vib("<.5 1 2 4 8 16>:12")
*/
[['vib', 'vibmod'], 'vibrato', 'v'],
/**
* Sets the vibrato depth in semitones. Only has an effect if `vibrato` | `vib` | `v` is is also set
*
* @name vibmod
* @synonyms vmod
* @param {number | Pattern} depth of vibrato (in semitones)
* @example
* note("a").vib(4)
* .vibmod("<.25 .5 1 2 12>")
* @example
* // change the vibrato frequency with ":"
* note("a")
* .vibmod("<.25 .5 1 2 12>:8")
*/
[['vibmod', 'vib'], 'vmod'],
[['hcutoff', 'hresonance'], 'hpf', 'hp'],
/**
* Controls the **h**igh-**p**ass **q**-value.
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel.cycles/core",
"version": "0.8.2",
"version": "0.9.0",
"description": "Port of Tidal Cycles to JavaScript",
"main": "index.mjs",
"type": "module",
+19 -6
View File
@@ -2273,14 +2273,14 @@ export const slice = register(
false, // turns off auto-patternification
);
/*
/**
* Works the same as slice, but changes the playback speed of each slice to match the duration of its step.
* @name splice
* @memberof Pattern
* @returns Pattern
* @example
* await samples('github:tidalcycles/Dirt-Samples/master')
* s("breaks165").splice(8, "0 1 [2 3 0]@2 3 0@2 7").hurry(0.65)
* s("breaks165")
* .splice(8, "0 1 [2 3 0]@2 3 0@2 7")
* .hurry(0.65)
*/
export const splice = register(
@@ -2307,9 +2307,16 @@ export const { loopAt, loopat } = register(['loopAt', 'loopat'], function (facto
return _loopAt(factor, pat, 1);
});
// this function will be redefined in repl.mjs to use the correct cps value.
// the fit function will be redefined in repl.mjs to use the correct cps value.
// It is still here to work in cases where repl.mjs is not used
/**
* Makes the sample fit its event duration. Good for rhythmical loops like drum breaks.
* Similar to loopAt.
* @name fit
* @example
* samples({ rhodes: 'https://cdn.freesound.org/previews/132/132051_316502-lq.mp3' })
* s("rhodes/4").fit()
*/
export const fit = register('fit', (pat) =>
pat.withHap((hap) =>
hap.withValue((v) => ({
@@ -2336,3 +2343,9 @@ export const fit = register('fit', (pat) =>
export const { loopAtCps, loopatcps } = register(['loopAtCps', 'loopatcps'], function (factor, cps, pat) {
return _loopAt(factor, pat, cps);
});
/** exposes a custom value at query time. basically allows mutating state without evaluation */
export const ref = (accessor) =>
pure(1)
.withValue(() => reify(accessor()))
.innerJoin();
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel.cycles/csound",
"version": "0.8.0",
"version": "0.9.0",
"description": "csound bindings for strudel",
"main": "index.mjs",
"publishConfig": {
+1
View File
@@ -1,6 +1,7 @@
{
"name": "@strudel/desktopbridge",
"version": "0.1.0",
"private": true,
"description": "tools/shims for communicating between the JS and Tauri (Rust) sides of the Studel desktop app",
"main": "index.mjs",
"type": "module",
+71 -18
View File
@@ -5,7 +5,7 @@ This program is free software: you can redistribute it and/or modify it under th
*/
import * as _WebMidi from 'webmidi';
import { Pattern, isPattern, logger } from '@strudel.cycles/core';
import { Pattern, isPattern, logger, ref } from '@strudel.cycles/core';
import { noteToMidi } from '@strudel.cycles/core';
import { Note } from 'webmidi';
// if you use WebMidi from outside of this package, make sure to import that instance:
@@ -15,8 +15,8 @@ function supportsMidi() {
return typeof navigator.requestMIDIAccess === 'function';
}
function getMidiDeviceNamesString(outputs) {
return outputs.map((o) => `'${o.name}'`).join(' | ');
function getMidiDeviceNamesString(devices) {
return devices.map((o) => `'${o.name}'`).join(' | ');
}
export function enableWebMidi(options = {}) {
@@ -52,30 +52,41 @@ export function enableWebMidi(options = {}) {
});
});
}
// const outputByName = (name: string) => WebMidi.getOutputByName(name);
const outputByName = (name) => WebMidi.getOutputByName(name);
// output?: string | number, outputs: typeof WebMidi.outputs
function getDevice(output, outputs) {
if (!outputs.length) {
function getDevice(indexOrName, devices) {
if (!devices.length) {
throw new Error(`🔌 No MIDI devices found. Connect a device or enable IAC Driver.`);
}
if (typeof output === 'number') {
return outputs[output];
if (typeof indexOrName === 'number') {
return devices[indexOrName];
}
if (typeof output === 'string') {
return outputByName(output);
const byName = (name) => devices.find((output) => output.name.includes(name));
if (typeof indexOrName === 'string') {
return byName(indexOrName);
}
// attempt to default to first IAC device if none is specified
const IACOutput = outputs.find((output) => output.name.includes('IAC'));
const device = IACOutput ?? outputs[0];
const IACOutput = byName('IAC');
const device = IACOutput ?? devices[0];
if (!device) {
throw new Error(
`🔌 MIDI device '${output ? output : ''}' not found. Use one of ${getMidiDeviceNamesString(WebMidi.outputs)}`,
`🔌 MIDI device '${device ? device : ''}' not found. Use one of ${getMidiDeviceNamesString(devices)}`,
);
}
return IACOutput ?? outputs[0];
return IACOutput ?? devices[0];
}
// send start/stop messages to outputs when repl starts/stops
if (typeof window !== 'undefined') {
window.addEventListener('message', (e) => {
if (!WebMidi?.enabled) {
return;
}
if (e.data === 'strudel-stop') {
WebMidi.outputs.forEach((output) => output.sendStop());
}
// cannot start here, since we have no timing info, see sendStart below
});
}
Pattern.prototype.midi = function (output) {
@@ -103,6 +114,7 @@ Pattern.prototype.midi = function (output) {
return this.onTrigger((time, hap, currentTime, cps) => {
if (!WebMidi.enabled) {
console.log('not enabled');
return;
}
const device = getDevice(output, WebMidi.outputs);
@@ -113,7 +125,7 @@ Pattern.prototype.midi = function (output) {
const timeOffsetString = `+${offset}`;
// destructure value
const { note, nrpnn, nrpv, ccn, ccv, midichan = 1 } = hap.value;
const { note, nrpnn, nrpv, ccn, ccv, midichan = 1, midicmd } = hap.value;
const velocity = hap.context?.velocity ?? 0.9; // TODO: refactor velocity
// note off messages will often a few ms arrive late, try to prevent glitching by subtracting from the duration length
@@ -125,7 +137,7 @@ Pattern.prototype.midi = function (output) {
time: timeOffsetString,
});
}
if (ccv && ccn) {
if (ccv !== undefined && ccn !== undefined) {
if (typeof ccv !== 'number' || ccv < 0 || ccv > 1) {
throw new Error('expected ccv to be a number between 0 and 1');
}
@@ -135,5 +147,46 @@ Pattern.prototype.midi = function (output) {
const scaled = Math.round(ccv * 127);
device.sendControlChange(ccn, scaled, midichan, { time: timeOffsetString });
}
if (hap.whole.begin + 0 === 0) {
// we need to start here because we have the timing info
device.sendStart({ time: timeOffsetString });
}
if (['clock', 'midiClock'].includes(midicmd)) {
device.sendClock({ time: timeOffsetString });
} else if (['start'].includes(midicmd)) {
device.sendStart({ time: timeOffsetString });
} else if (['stop'].includes(midicmd)) {
device.sendStop({ time: timeOffsetString });
} else if (['continue'].includes(midicmd)) {
device.sendContinue({ time: timeOffsetString });
}
});
};
let listeners = {};
const refs = {};
export async function midin(input) {
const initial = await enableWebMidi(); // only returns on first init
const device = getDevice(input, WebMidi.inputs);
if (initial) {
const otherInputs = WebMidi.inputs.filter((o) => o.name !== device.name);
logger(
`Midi enabled! Using "${device.name}". ${
otherInputs?.length ? `Also available: ${getMidiDeviceNamesString(otherInputs)}` : ''
}`,
);
refs[input] = {};
}
const cc = (cc) => ref(() => refs[input][cc] || 0);
listeners[input] && device.removeListener('midimessage', listeners[input]);
listeners[input] = (e) => {
const cc = e.dataBytes[0];
const v = e.dataBytes[1];
refs[input] && (refs[input][cc] = v / 127);
};
device.addListener('midimessage', listeners[input]);
return cc;
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel.cycles/midi",
"version": "0.8.0",
"version": "0.9.0",
"description": "Midi API for strudel",
"main": "index.mjs",
"publishConfig": {
+246 -191
View File
@@ -200,20 +200,21 @@ function peg$parse(input, options) {
var peg$c23 = "*";
var peg$c24 = "?";
var peg$c25 = ":";
var peg$c26 = "struct";
var peg$c27 = "target";
var peg$c28 = "euclid";
var peg$c29 = "slow";
var peg$c30 = "rotL";
var peg$c31 = "rotR";
var peg$c32 = "fast";
var peg$c33 = "scale";
var peg$c34 = "//";
var peg$c35 = "cat";
var peg$c36 = "$";
var peg$c37 = "setcps";
var peg$c38 = "setbpm";
var peg$c39 = "hush";
var peg$c26 = "..";
var peg$c27 = "struct";
var peg$c28 = "target";
var peg$c29 = "euclid";
var peg$c30 = "slow";
var peg$c31 = "rotL";
var peg$c32 = "rotR";
var peg$c33 = "fast";
var peg$c34 = "scale";
var peg$c35 = "//";
var peg$c36 = "cat";
var peg$c37 = "$";
var peg$c38 = "setcps";
var peg$c39 = "setbpm";
var peg$c40 = "hush";
var peg$r0 = /^[1-9]/;
var peg$r1 = /^[eE]/;
@@ -255,64 +256,67 @@ function peg$parse(input, options) {
var peg$e30 = peg$literalExpectation("*", false);
var peg$e31 = peg$literalExpectation("?", false);
var peg$e32 = peg$literalExpectation(":", false);
var peg$e33 = peg$literalExpectation("struct", false);
var peg$e34 = peg$literalExpectation("target", false);
var peg$e35 = peg$literalExpectation("euclid", false);
var peg$e36 = peg$literalExpectation("slow", false);
var peg$e37 = peg$literalExpectation("rotL", false);
var peg$e38 = peg$literalExpectation("rotR", false);
var peg$e39 = peg$literalExpectation("fast", false);
var peg$e40 = peg$literalExpectation("scale", false);
var peg$e41 = peg$literalExpectation("//", false);
var peg$e42 = peg$classExpectation(["\n"], true, false);
var peg$e43 = peg$literalExpectation("cat", false);
var peg$e44 = peg$literalExpectation("$", false);
var peg$e45 = peg$literalExpectation("setcps", false);
var peg$e46 = peg$literalExpectation("setbpm", false);
var peg$e47 = peg$literalExpectation("hush", false);
var peg$e33 = peg$literalExpectation("..", false);
var peg$e34 = peg$literalExpectation("struct", false);
var peg$e35 = peg$literalExpectation("target", false);
var peg$e36 = peg$literalExpectation("euclid", false);
var peg$e37 = peg$literalExpectation("slow", false);
var peg$e38 = peg$literalExpectation("rotL", false);
var peg$e39 = peg$literalExpectation("rotR", false);
var peg$e40 = peg$literalExpectation("fast", false);
var peg$e41 = peg$literalExpectation("scale", false);
var peg$e42 = peg$literalExpectation("//", false);
var peg$e43 = peg$classExpectation(["\n"], true, false);
var peg$e44 = peg$literalExpectation("cat", false);
var peg$e45 = peg$literalExpectation("$", false);
var peg$e46 = peg$literalExpectation("setcps", false);
var peg$e47 = peg$literalExpectation("setbpm", false);
var peg$e48 = peg$literalExpectation("hush", false);
var peg$f0 = function() { return parseFloat(text()); };
var peg$f1 = function(chars) { return new AtomStub(chars.join("")) };
var peg$f2 = function(s) { return s };
var peg$f3 = function(s, stepsPerCycle) { s.arguments_.stepsPerCycle = stepsPerCycle ; return s; };
var peg$f4 = function(a) { return a };
var peg$f5 = function(s) { s.arguments_.alignment = 'slowcat'; return s; };
var peg$f6 = function(a) { return x => x.options_['weight'] = a };
var peg$f7 = function(a) { return x => x.options_['reps'] = a };
var peg$f8 = function(p, s, r) { return x => x.options_['ops'].push({ type_: "bjorklund", arguments_ :{ pulse: p, step:s, rotation:r }}) };
var peg$f9 = function(a) { return x => x.options_['ops'].push({ type_: "stretch", arguments_ :{ amount:a, type: 'slow' }}) };
var peg$f10 = function(a) { return x => x.options_['ops'].push({ type_: "stretch", arguments_ :{ amount:a, type: 'fast' }}) };
var peg$f11 = function(a) { return x => x.options_['ops'].push({ type_: "degradeBy", arguments_ :{ amount:a, seed: seed++ } }) };
var peg$f12 = function(s) { return x => x.options_['ops'].push({ type_: "tail", arguments_ :{ element:s } }) };
var peg$f13 = function(s, ops) { const result = new ElementStub(s, {ops: [], weight: 1, reps: 1});
var peg$f1 = function() { return parseInt(text()); };
var peg$f2 = function(chars) { return new AtomStub(chars.join("")) };
var peg$f3 = function(s) { return s };
var peg$f4 = function(s, stepsPerCycle) { s.arguments_.stepsPerCycle = stepsPerCycle ; return s; };
var peg$f5 = function(a) { return a };
var peg$f6 = function(s) { s.arguments_.alignment = 'slowcat'; return s; };
var peg$f7 = function(a) { return x => x.options_['weight'] = a };
var peg$f8 = function(a) { return x => x.options_['reps'] = a };
var peg$f9 = function(p, s, r) { return x => x.options_['ops'].push({ type_: "bjorklund", arguments_ :{ pulse: p, step:s, rotation:r }}) };
var peg$f10 = function(a) { return x => x.options_['ops'].push({ type_: "stretch", arguments_ :{ amount:a, type: 'slow' }}) };
var peg$f11 = function(a) { return x => x.options_['ops'].push({ type_: "stretch", arguments_ :{ amount:a, type: 'fast' }}) };
var peg$f12 = function(a) { return x => x.options_['ops'].push({ type_: "degradeBy", arguments_ :{ amount:a, seed: seed++ } }) };
var peg$f13 = function(s) { return x => x.options_['ops'].push({ type_: "tail", arguments_ :{ element:s } }) };
var peg$f14 = function(s) { return x => x.options_['ops'].push({ type_: "range", arguments_ :{ element:s } }) };
var peg$f15 = function(s, ops) { const result = new ElementStub(s, {ops: [], weight: 1, reps: 1});
for (const op of ops) {
op(result);
}
return result;
};
var peg$f14 = function(s) { return new PatternStub(s, 'fastcat'); };
var peg$f15 = function(tail) { return { alignment: 'stack', list: tail }; };
var peg$f16 = function(tail) { return { alignment: 'rand', list: tail, seed: seed++ }; };
var peg$f17 = function(head, tail) { if (tail && tail.list.length > 0) { return new PatternStub([head, ...tail.list], tail.alignment, tail.seed); } else { return head; } };
var peg$f18 = function(head, tail) { return new PatternStub(tail ? [head, ...tail.list] : [head], 'polymeter'); };
var peg$f19 = function(sc) { return sc; };
var peg$f20 = function(s) { return { name: "struct", args: { mini:s }}};
var peg$f21 = function(s) { return { name: "target", args : { name:s}}};
var peg$f22 = function(p, s, r) { return { name: "bjorklund", args :{ pulse: p, step:parseInt(s) }}};
var peg$f23 = function(a) { return { name: "stretch", args :{ amount: a}}};
var peg$f24 = function(a) { return { name: "shift", args :{ amount: "-"+a}}};
var peg$f25 = function(a) { return { name: "shift", args :{ amount: a}}};
var peg$f26 = function(a) { return { name: "stretch", args :{ amount: "1/"+a}}};
var peg$f27 = function(s) { return { name: "scale", args :{ scale: s.join("")}}};
var peg$f28 = function(s, v) { return v};
var peg$f29 = function(s, ss) { ss.unshift(s); return new PatternStub(ss, 'slowcat'); };
var peg$f30 = function(sg) {return sg};
var peg$f31 = function(o, soc) { return new OperatorStub(o.name,o.args,soc)};
var peg$f32 = function(sc) { return sc };
var peg$f33 = function(c) { return c };
var peg$f34 = function(v) { return new CommandStub("setcps", { value: v})};
var peg$f35 = function(v) { return new CommandStub("setcps", { value: (v/120/2)})};
var peg$f36 = function() { return new CommandStub("hush")};
var peg$f16 = function(s) { return new PatternStub(s, 'fastcat'); };
var peg$f17 = function(tail) { return { alignment: 'stack', list: tail }; };
var peg$f18 = function(tail) { return { alignment: 'rand', list: tail, seed: seed++ }; };
var peg$f19 = function(head, tail) { if (tail && tail.list.length > 0) { return new PatternStub([head, ...tail.list], tail.alignment, tail.seed); } else { return head; } };
var peg$f20 = function(head, tail) { return new PatternStub(tail ? [head, ...tail.list] : [head], 'polymeter'); };
var peg$f21 = function(sc) { return sc; };
var peg$f22 = function(s) { return { name: "struct", args: { mini:s }}};
var peg$f23 = function(s) { return { name: "target", args : { name:s}}};
var peg$f24 = function(p, s, r) { return { name: "bjorklund", args :{ pulse: p, step:parseInt(s) }}};
var peg$f25 = function(a) { return { name: "stretch", args :{ amount: a}}};
var peg$f26 = function(a) { return { name: "shift", args :{ amount: "-"+a}}};
var peg$f27 = function(a) { return { name: "shift", args :{ amount: a}}};
var peg$f28 = function(a) { return { name: "stretch", args :{ amount: "1/"+a}}};
var peg$f29 = function(s) { return { name: "scale", args :{ scale: s.join("")}}};
var peg$f30 = function(s, v) { return v};
var peg$f31 = function(s, ss) { ss.unshift(s); return new PatternStub(ss, 'slowcat'); };
var peg$f32 = function(sg) {return sg};
var peg$f33 = function(o, soc) { return new OperatorStub(o.name,o.args,soc)};
var peg$f34 = function(sc) { return sc };
var peg$f35 = function(c) { return c };
var peg$f36 = function(v) { return new CommandStub("setcps", { value: v})};
var peg$f37 = function(v) { return new CommandStub("setcps", { value: (v/120/2)})};
var peg$f38 = function() { return new CommandStub("hush")};
var peg$currPos = 0;
var peg$savedPos = 0;
var peg$posDetailsCache = [{ line: 1, column: 1 }];
@@ -651,6 +655,26 @@ function peg$parse(input, options) {
return s0;
}
function peg$parseintneg() {
var s0, s1, s2;
s0 = peg$currPos;
s1 = peg$parseminus();
if (s1 === peg$FAILED) {
s1 = null;
}
s2 = peg$parseint();
if (s2 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f1();
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
return s0;
}
function peg$parseminus() {
var s0;
@@ -884,7 +908,7 @@ function peg$parse(input, options) {
if (s2 !== peg$FAILED) {
s3 = peg$parsews();
peg$savedPos = s0;
s0 = peg$f1(s2);
s0 = peg$f2(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@@ -920,7 +944,7 @@ function peg$parse(input, options) {
if (s6 !== peg$FAILED) {
s7 = peg$parsews();
peg$savedPos = s0;
s0 = peg$f2(s4);
s0 = peg$f3(s4);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@@ -968,7 +992,7 @@ function peg$parse(input, options) {
}
s8 = peg$parsews();
peg$savedPos = s0;
s0 = peg$f3(s4, s7);
s0 = peg$f4(s4, s7);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@@ -1000,7 +1024,7 @@ function peg$parse(input, options) {
s2 = peg$parseslice();
if (s2 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f4(s2);
s0 = peg$f5(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@@ -1040,7 +1064,7 @@ function peg$parse(input, options) {
if (s6 !== peg$FAILED) {
s7 = peg$parsews();
peg$savedPos = s0;
s0 = peg$f5(s4);
s0 = peg$f6(s4);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@@ -1090,6 +1114,9 @@ function peg$parse(input, options) {
s0 = peg$parseop_degrade();
if (s0 === peg$FAILED) {
s0 = peg$parseop_tail();
if (s0 === peg$FAILED) {
s0 = peg$parseop_range();
}
}
}
}
@@ -1115,7 +1142,7 @@ function peg$parse(input, options) {
s2 = peg$parsenumber();
if (s2 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f6(s2);
s0 = peg$f7(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@@ -1143,7 +1170,7 @@ function peg$parse(input, options) {
s2 = peg$parsenumber();
if (s2 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f7(s2);
s0 = peg$f8(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@@ -1197,7 +1224,7 @@ function peg$parse(input, options) {
}
if (s13 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f8(s3, s7, s11);
s0 = peg$f9(s3, s7, s11);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@@ -1237,7 +1264,7 @@ function peg$parse(input, options) {
s2 = peg$parseslice();
if (s2 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f9(s2);
s0 = peg$f10(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@@ -1265,7 +1292,7 @@ function peg$parse(input, options) {
s2 = peg$parseslice();
if (s2 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f10(s2);
s0 = peg$f11(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@@ -1295,7 +1322,7 @@ function peg$parse(input, options) {
s2 = null;
}
peg$savedPos = s0;
s0 = peg$f11(s2);
s0 = peg$f12(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@@ -1319,7 +1346,35 @@ function peg$parse(input, options) {
s2 = peg$parseslice();
if (s2 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f12(s2);
s0 = peg$f13(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
return s0;
}
function peg$parseop_range() {
var s0, s1, s2;
s0 = peg$currPos;
if (input.substr(peg$currPos, 2) === peg$c26) {
s1 = peg$c26;
peg$currPos += 2;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$e33); }
}
if (s1 !== peg$FAILED) {
s2 = peg$parseslice();
if (s2 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f14(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@@ -1345,7 +1400,7 @@ function peg$parse(input, options) {
s3 = peg$parseslice_op();
}
peg$savedPos = s0;
s0 = peg$f13(s1, s2);
s0 = peg$f15(s1, s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@@ -1370,7 +1425,7 @@ function peg$parse(input, options) {
}
if (s1 !== peg$FAILED) {
peg$savedPos = s0;
s1 = peg$f14(s1);
s1 = peg$f16(s1);
}
s0 = s1;
@@ -1419,7 +1474,7 @@ function peg$parse(input, options) {
}
if (s1 !== peg$FAILED) {
peg$savedPos = s0;
s1 = peg$f15(s1);
s1 = peg$f17(s1);
}
s0 = s1;
@@ -1468,7 +1523,7 @@ function peg$parse(input, options) {
}
if (s1 !== peg$FAILED) {
peg$savedPos = s0;
s1 = peg$f16(s1);
s1 = peg$f18(s1);
}
s0 = s1;
@@ -1489,7 +1544,7 @@ function peg$parse(input, options) {
s2 = null;
}
peg$savedPos = s0;
s0 = peg$f17(s1, s2);
s0 = peg$f19(s1, s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@@ -1509,7 +1564,7 @@ function peg$parse(input, options) {
s2 = null;
}
peg$savedPos = s0;
s0 = peg$f18(s1, s2);
s0 = peg$f20(s1, s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@@ -1532,7 +1587,7 @@ function peg$parse(input, options) {
s6 = peg$parsequote();
if (s6 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f19(s4);
s0 = peg$f21(s4);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@@ -1582,19 +1637,19 @@ function peg$parse(input, options) {
var s0, s1, s2, s3;
s0 = peg$currPos;
if (input.substr(peg$currPos, 6) === peg$c26) {
s1 = peg$c26;
if (input.substr(peg$currPos, 6) === peg$c27) {
s1 = peg$c27;
peg$currPos += 6;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$e33); }
if (peg$silentFails === 0) { peg$fail(peg$e34); }
}
if (s1 !== peg$FAILED) {
s2 = peg$parsews();
s3 = peg$parsemini_or_operator();
if (s3 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f20(s3);
s0 = peg$f22(s3);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@@ -1611,12 +1666,12 @@ function peg$parse(input, options) {
var s0, s1, s2, s3, s4, s5;
s0 = peg$currPos;
if (input.substr(peg$currPos, 6) === peg$c27) {
s1 = peg$c27;
if (input.substr(peg$currPos, 6) === peg$c28) {
s1 = peg$c28;
peg$currPos += 6;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$e34); }
if (peg$silentFails === 0) { peg$fail(peg$e35); }
}
if (s1 !== peg$FAILED) {
s2 = peg$parsews();
@@ -1627,7 +1682,7 @@ function peg$parse(input, options) {
s5 = peg$parsequote();
if (s5 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f21(s4);
s0 = peg$f23(s4);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@@ -1652,12 +1707,12 @@ function peg$parse(input, options) {
var s0, s1, s2, s3, s4, s5, s6, s7;
s0 = peg$currPos;
if (input.substr(peg$currPos, 6) === peg$c28) {
s1 = peg$c28;
if (input.substr(peg$currPos, 6) === peg$c29) {
s1 = peg$c29;
peg$currPos += 6;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$e35); }
if (peg$silentFails === 0) { peg$fail(peg$e36); }
}
if (s1 !== peg$FAILED) {
s2 = peg$parsews();
@@ -1672,7 +1727,7 @@ function peg$parse(input, options) {
s7 = null;
}
peg$savedPos = s0;
s0 = peg$f22(s3, s5, s7);
s0 = peg$f24(s3, s5, s7);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@@ -1692,35 +1747,6 @@ function peg$parse(input, options) {
function peg$parseslow() {
var s0, s1, s2, s3;
s0 = peg$currPos;
if (input.substr(peg$currPos, 4) === peg$c29) {
s1 = peg$c29;
peg$currPos += 4;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$e36); }
}
if (s1 !== peg$FAILED) {
s2 = peg$parsews();
s3 = peg$parsenumber();
if (s3 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f23(s3);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
return s0;
}
function peg$parserotL() {
var s0, s1, s2, s3;
s0 = peg$currPos;
if (input.substr(peg$currPos, 4) === peg$c30) {
s1 = peg$c30;
@@ -1729,35 +1755,6 @@ function peg$parse(input, options) {
s1 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$e37); }
}
if (s1 !== peg$FAILED) {
s2 = peg$parsews();
s3 = peg$parsenumber();
if (s3 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f24(s3);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
return s0;
}
function peg$parserotR() {
var s0, s1, s2, s3;
s0 = peg$currPos;
if (input.substr(peg$currPos, 4) === peg$c31) {
s1 = peg$c31;
peg$currPos += 4;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$e38); }
}
if (s1 !== peg$FAILED) {
s2 = peg$parsews();
s3 = peg$parsenumber();
@@ -1776,16 +1773,16 @@ function peg$parse(input, options) {
return s0;
}
function peg$parsefast() {
function peg$parserotL() {
var s0, s1, s2, s3;
s0 = peg$currPos;
if (input.substr(peg$currPos, 4) === peg$c32) {
s1 = peg$c32;
if (input.substr(peg$currPos, 4) === peg$c31) {
s1 = peg$c31;
peg$currPos += 4;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$e39); }
if (peg$silentFails === 0) { peg$fail(peg$e38); }
}
if (s1 !== peg$FAILED) {
s2 = peg$parsews();
@@ -1805,16 +1802,74 @@ function peg$parse(input, options) {
return s0;
}
function peg$parserotR() {
var s0, s1, s2, s3;
s0 = peg$currPos;
if (input.substr(peg$currPos, 4) === peg$c32) {
s1 = peg$c32;
peg$currPos += 4;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$e39); }
}
if (s1 !== peg$FAILED) {
s2 = peg$parsews();
s3 = peg$parsenumber();
if (s3 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f27(s3);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
return s0;
}
function peg$parsefast() {
var s0, s1, s2, s3;
s0 = peg$currPos;
if (input.substr(peg$currPos, 4) === peg$c33) {
s1 = peg$c33;
peg$currPos += 4;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$e40); }
}
if (s1 !== peg$FAILED) {
s2 = peg$parsews();
s3 = peg$parsenumber();
if (s3 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f28(s3);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
return s0;
}
function peg$parsescale() {
var s0, s1, s2, s3, s4, s5;
s0 = peg$currPos;
if (input.substr(peg$currPos, 5) === peg$c33) {
s1 = peg$c33;
if (input.substr(peg$currPos, 5) === peg$c34) {
s1 = peg$c34;
peg$currPos += 5;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$e40); }
if (peg$silentFails === 0) { peg$fail(peg$e41); }
}
if (s1 !== peg$FAILED) {
s2 = peg$parsews();
@@ -1834,7 +1889,7 @@ function peg$parse(input, options) {
s5 = peg$parsequote();
if (s5 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f27(s4);
s0 = peg$f29(s4);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@@ -1859,12 +1914,12 @@ function peg$parse(input, options) {
var s0, s1, s2, s3;
s0 = peg$currPos;
if (input.substr(peg$currPos, 2) === peg$c34) {
s1 = peg$c34;
if (input.substr(peg$currPos, 2) === peg$c35) {
s1 = peg$c35;
peg$currPos += 2;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$e41); }
if (peg$silentFails === 0) { peg$fail(peg$e42); }
}
if (s1 !== peg$FAILED) {
s2 = [];
@@ -1873,7 +1928,7 @@ function peg$parse(input, options) {
peg$currPos++;
} else {
s3 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$e42); }
if (peg$silentFails === 0) { peg$fail(peg$e43); }
}
while (s3 !== peg$FAILED) {
s2.push(s3);
@@ -1882,7 +1937,7 @@ function peg$parse(input, options) {
peg$currPos++;
} else {
s3 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$e42); }
if (peg$silentFails === 0) { peg$fail(peg$e43); }
}
}
s1 = [s1, s2];
@@ -1899,12 +1954,12 @@ function peg$parse(input, options) {
var s0, s1, s2, s3, s4, s5, s6, s7, s8, s9;
s0 = peg$currPos;
if (input.substr(peg$currPos, 3) === peg$c35) {
s1 = peg$c35;
if (input.substr(peg$currPos, 3) === peg$c36) {
s1 = peg$c36;
peg$currPos += 3;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$e43); }
if (peg$silentFails === 0) { peg$fail(peg$e44); }
}
if (s1 !== peg$FAILED) {
s2 = peg$parsews();
@@ -1926,7 +1981,7 @@ function peg$parse(input, options) {
s9 = peg$parsemini_or_operator();
if (s9 !== peg$FAILED) {
peg$savedPos = s7;
s7 = peg$f28(s5, s9);
s7 = peg$f30(s5, s9);
} else {
peg$currPos = s7;
s7 = peg$FAILED;
@@ -1943,7 +1998,7 @@ function peg$parse(input, options) {
s9 = peg$parsemini_or_operator();
if (s9 !== peg$FAILED) {
peg$savedPos = s7;
s7 = peg$f28(s5, s9);
s7 = peg$f30(s5, s9);
} else {
peg$currPos = s7;
s7 = peg$FAILED;
@@ -1963,7 +2018,7 @@ function peg$parse(input, options) {
}
if (s8 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f29(s5, s6);
s0 = peg$f31(s5, s6);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@@ -2009,7 +2064,7 @@ function peg$parse(input, options) {
s4 = peg$parsecomment();
}
peg$savedPos = s0;
s0 = peg$f30(s1);
s0 = peg$f32(s1);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@@ -2020,18 +2075,18 @@ function peg$parse(input, options) {
if (s1 !== peg$FAILED) {
s2 = peg$parsews();
if (input.charCodeAt(peg$currPos) === 36) {
s3 = peg$c36;
s3 = peg$c37;
peg$currPos++;
} else {
s3 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$e44); }
if (peg$silentFails === 0) { peg$fail(peg$e45); }
}
if (s3 !== peg$FAILED) {
s4 = peg$parsews();
s5 = peg$parsemini_or_operator();
if (s5 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f31(s1, s5);
s0 = peg$f33(s1, s5);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@@ -2056,7 +2111,7 @@ function peg$parse(input, options) {
s1 = peg$parsemini_or_operator();
if (s1 !== peg$FAILED) {
peg$savedPos = s0;
s1 = peg$f32(s1);
s1 = peg$f34(s1);
}
s0 = s1;
if (s0 === peg$FAILED) {
@@ -2089,7 +2144,7 @@ function peg$parse(input, options) {
if (s2 !== peg$FAILED) {
s3 = peg$parsews();
peg$savedPos = s0;
s0 = peg$f33(s2);
s0 = peg$f35(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@@ -2102,19 +2157,19 @@ function peg$parse(input, options) {
var s0, s1, s2, s3;
s0 = peg$currPos;
if (input.substr(peg$currPos, 6) === peg$c37) {
s1 = peg$c37;
if (input.substr(peg$currPos, 6) === peg$c38) {
s1 = peg$c38;
peg$currPos += 6;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$e45); }
if (peg$silentFails === 0) { peg$fail(peg$e46); }
}
if (s1 !== peg$FAILED) {
s2 = peg$parsews();
s3 = peg$parsenumber();
if (s3 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f34(s3);
s0 = peg$f36(s3);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@@ -2131,19 +2186,19 @@ function peg$parse(input, options) {
var s0, s1, s2, s3;
s0 = peg$currPos;
if (input.substr(peg$currPos, 6) === peg$c38) {
s1 = peg$c38;
if (input.substr(peg$currPos, 6) === peg$c39) {
s1 = peg$c39;
peg$currPos += 6;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$e46); }
if (peg$silentFails === 0) { peg$fail(peg$e47); }
}
if (s1 !== peg$FAILED) {
s2 = peg$parsews();
s3 = peg$parsenumber();
if (s3 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f35(s3);
s0 = peg$f37(s3);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@@ -2160,16 +2215,16 @@ function peg$parse(input, options) {
var s0, s1;
s0 = peg$currPos;
if (input.substr(peg$currPos, 4) === peg$c39) {
s1 = peg$c39;
if (input.substr(peg$currPos, 4) === peg$c40) {
s1 = peg$c40;
peg$currPos += 4;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$e47); }
if (peg$silentFails === 0) { peg$fail(peg$e48); }
}
if (s1 !== peg$FAILED) {
peg$savedPos = s0;
s1 = peg$f36();
s1 = peg$f38();
}
s0 = s1;
+7 -1
View File
@@ -79,6 +79,9 @@ frac
int
= zero / (digit1_9 DIGIT*)
intneg
= minus? int { return parseInt(text()); }
minus
= "-"
@@ -123,7 +126,7 @@ slice = step / sub_cycle / polymeter / slow_sequence
// slice modifier affects the timing/size of a slice (e.g. [a b c]@3)
// at this point, we assume we can represent them as regular sequence operators
slice_op = op_weight / op_bjorklund / op_slow / op_fast / op_replicate / op_degrade / op_tail
slice_op = op_weight / op_bjorklund / op_slow / op_fast / op_replicate / op_degrade / op_tail / op_range
op_weight = "@" a:number
{ return x => x.options_['weight'] = a }
@@ -146,6 +149,9 @@ op_degrade = "?"a:number?
op_tail = ":" s:slice
{ return x => x.options_['ops'].push({ type_: "tail", arguments_ :{ element:s } }) }
op_range = ".." s:slice
{ return x => x.options_['ops'].push({ type_: "range", arguments_ :{ element:s } }) }
// a slice with an modifier applied i.e [bd@4 sd@3]@2 hh]
slice_with_ops = s:slice ops:slice_op*
{ const result = new ElementStub(s, {ops: [], weight: 1, reps: 1});
+11
View File
@@ -45,6 +45,17 @@ const applyOptions = (parent, enter) => (pat, i) => {
pat = pat.fmap((a) => (b) => Array.isArray(a) ? [...a, b] : [a, b]).appLeft(friend);
break;
}
case 'range': {
const friend = enter(op.arguments_.element);
pat = strudel.reify(pat);
const arrayRange = (start, stop, step = 1) =>
Array.from({ length: Math.abs(stop - start) / step + 1 }, (value, index) =>
start < stop ? start + index * step : start - index * step,
);
let range = (apat, bpat) => apat.squeezeBind((a) => bpat.bind((b) => strudel.fastcat(...arrayRange(a, b))));
pat = range(pat, friend);
break;
}
default: {
console.warn(`operator "${op.type_}" not implemented`);
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel.cycles/mini",
"version": "0.8.2",
"version": "0.9.0",
"description": "Mini notation for strudel",
"main": "index.mjs",
"type": "module",
+6
View File
@@ -184,6 +184,12 @@ describe('mini', () => {
it('supports lists', () => {
expect(minV('a:b c:d:[e:f] g')).toEqual([['a', 'b'], ['c', 'd', ['e', 'f']], 'g']);
});
it('supports ranges', () => {
expect(minV('0 .. 4')).toEqual([0, 1, 2, 3, 4]);
});
it('supports patterned ranges', () => {
expect(minS('[<0 1> .. <2 4>]*2')).toEqual(minS('[0 1 2] [1 2 3 4]'));
});
});
describe('getLeafLocation', () => {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel.cycles/osc",
"version": "0.8.0",
"version": "0.9.0",
"description": "OSC messaging for strudel",
"main": "osc.mjs",
"publishConfig": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel.cycles/react",
"version": "0.8.0",
"version": "0.9.0",
"description": "React components for strudel",
"main": "src/index.js",
"publishConfig": {
@@ -15,10 +15,11 @@ import {
updateMiniLocations,
} from '@strudel/codemirror';
import './style.css';
import { sliderPlugin } from '@strudel/codemirror/slider.mjs';
export { flash, highlightMiniLocations, updateMiniLocations };
const staticExtensions = [javascript(), flashField, highlightExtension];
const staticExtensions = [javascript(), flashField, highlightExtension, sliderPlugin];
export default function CodeMirror({
value,
+13
View File
@@ -0,0 +1,13 @@
import { useEffect, useState } from 'react';
import { updateWidgets } from '@strudel/codemirror';
// i know this is ugly.. in the future, repl needs to run without react
export function useWidgets(view) {
const [widgets, setWidgets] = useState([]);
useEffect(() => {
if (view) {
updateWidgets(view, widgets);
}
}, [view, widgets]);
return { widgets, setWidgets };
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel.cycles/serial",
"version": "0.8.0",
"version": "0.9.0",
"description": "Webserial API for strudel",
"main": "serial.mjs",
"publishConfig": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel.cycles/soundfonts",
"version": "0.8.2",
"version": "0.9.0",
"description": "Soundsfont support for strudel",
"main": "index.mjs",
"publishConfig": {
+79
View File
@@ -0,0 +1,79 @@
import { getAudioContext } from './superdough.mjs';
let worklet;
export async function dspWorklet(ac, code) {
const name = `dsp-worklet-${Date.now()}`;
const workletCode = `${code}
let __q = []; // trigger queue
class MyProcessor extends AudioWorkletProcessor {
constructor() {
super();
this.t = 0;
this.stopped = false;
this.port.onmessage = (e) => {
if(e.data==='stop') {
this.stopped = true;
} else if(e.data?.dough) {
__q.push(e.data)
} else {
msg?.(e.data)
}
};
}
process(inputs, outputs, parameters) {
const output = outputs[0];
if(__q.length) {
for(let i=0;i<__q.length;++i) {
const deadline = __q[i].time-currentTime;
if(deadline<=0) {
trigger(__q[i].dough)
__q.splice(i,1)
}
}
}
for (let i = 0; i < output[0].length; i++) {
const out = dsp(this.t / sampleRate);
output.forEach((channel) => {
channel[i] = out;
});
this.t++;
}
return !this.stopped;
}
}
registerProcessor('${name}', MyProcessor);
`;
const base64String = btoa(workletCode);
const dataURL = `data:text/javascript;base64,${base64String}`;
await ac.audioWorklet.addModule(dataURL);
const node = new AudioWorkletNode(ac, name);
const stop = () => node.port.postMessage('stop');
return { node, stop };
}
const stop = () => {
if (worklet) {
worklet?.stop();
worklet?.node?.disconnect();
}
};
if (typeof window !== 'undefined') {
window.addEventListener('message', (e) => {
if (e.data === 'strudel-stop') {
stop();
} else if (e.data?.dough) {
worklet?.node.port.postMessage(e.data);
}
});
}
export const dough = async (code) => {
const ac = getAudioContext();
stop();
worklet = await dspWorklet(ac, code);
worklet.node.connect(ac.destination);
};
export function doughTrigger(t, hap, currentTime, duration, cps) {
window.postMessage({ time: t, dough: hap.value, currentTime, duration, cps });
}
+1
View File
@@ -10,3 +10,4 @@ export * from './helpers.mjs';
export * from './synth.mjs';
export * from './zzfx.mjs';
export * from './logger.mjs';
export * from './dspworklet.mjs';
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "superdough",
"version": "0.9.7",
"version": "0.9.9",
"description": "simple web audio synth and sampler intended for live coding. inspired by superdirt and webdirt.",
"main": "index.mjs",
"type": "module",
+11 -14
View File
@@ -196,7 +196,7 @@ export const samples = async (sampleMap, baseUrl = sampleMap._base || '', option
const cutGroups = [];
export async function onTriggerSample(t, value, onended, bank, resolveUrl) {
const {
let {
s,
freq,
unit,
@@ -207,7 +207,9 @@ export async function onTriggerSample(t, value, onended, bank, resolveUrl) {
n = 0,
note,
speed = 1, // sample playback speed
loopBegin = 0,
begin = 0,
loopEnd = 1,
end = 1,
} = value;
// load sample
@@ -215,6 +217,7 @@ export async function onTriggerSample(t, value, onended, bank, resolveUrl) {
// no playback
return;
}
loop = s.startsWith('wt_') ? 1 : value.loop;
const ac = getAudioContext();
// destructure adsr here, because the default should be different for synths and samples
const { attack = 0.001, decay = 0.001, sustain = 1, release = 0.001 } = value;
@@ -242,19 +245,12 @@ export async function onTriggerSample(t, value, onended, bank, resolveUrl) {
// rather than the current playback rate, so even if the sound is playing at twice its normal speed,
// the midway point through a 10-second audio buffer is still 5."
const offset = begin * bufferSource.buffer.duration;
bufferSource.start(time, offset);
const bufferDuration = bufferSource.buffer.duration / bufferSource.playbackRate.value;
/*if (loop) {
// TODO: idea for loopBegin / loopEnd
// if one of [loopBegin,loopEnd] is <= 1, interpret it as normlized
// if [loopBegin,loopEnd] is bigger >= 1, interpret it as sample number
// this will simplify perfectly looping things, while still keeping the normalized option
// the only drawback is that looping between samples 0 and 1 is not possible (which is not real use case)
if (loop) {
bufferSource.loop = true;
bufferSource.loopStart = offset;
bufferSource.loopEnd = offset + duration;
duration = loop * duration;
}*/
bufferSource.loopStart = loopBegin * bufferSource.buffer.duration - offset;
bufferSource.loopEnd = loopEnd * bufferSource.buffer.duration - offset;
}
bufferSource.start(time, offset);
const { node: envelope, stop: releaseEnvelope } = getEnvelope(attack, decay, sustain, release, 1, t);
bufferSource.connect(envelope);
const out = ac.createGain(); // we need a separate gain for the cutgroups because firefox...
@@ -265,9 +261,10 @@ export async function onTriggerSample(t, value, onended, bank, resolveUrl) {
out.disconnect();
onended();
};
const stop = (endTime, playWholeBuffer = clip === undefined) => {
const stop = (endTime, playWholeBuffer = clip === undefined && loop === undefined) => {
let releaseTime = endTime;
if (playWholeBuffer) {
const bufferDuration = bufferSource.buffer.duration / bufferSource.playbackRate.value;
releaseTime = t + (end - begin) * bufferDuration;
}
bufferSource.stop(releaseTime + release);
+33 -6
View File
@@ -40,6 +40,8 @@ export function registerSynthSounds() {
fmrelease: fmRelease,
fmvelocity: fmVelocity,
fmwave: fmWaveform = 'sine',
vib = 0,
vibmod = 0.5,
} = value;
let { n, note, freq } = value;
// with synths, n and note are the same thing
@@ -53,7 +55,14 @@ export function registerSynthSounds() {
}
// maybe pull out the above frequency resolution?? (there is also getFrequency but it has no default)
// make oscillator
const { node: o, stop } = getOscillator({ t, s: wave, freq, partials: n });
const { node: o, stop } = getOscillator({
t,
s: wave,
freq,
vib,
vibmod,
partials: n,
});
// FM + FM envelope
let stopFm, fmEnvelope;
@@ -137,8 +146,8 @@ export function waveformN(partials, type) {
return osc;
}
export function getOscillator({ s, freq, t, partials }) {
// make oscillator
export function getOscillator({ s, freq, t, vib, vibmod, partials }) {
// Make oscillator with partial count
let o;
if (!partials || s === 'sine') {
o = getAudioContext().createOscillator();
@@ -148,7 +157,25 @@ export function getOscillator({ s, freq, t, partials }) {
}
o.frequency.value = Number(freq);
o.start(t);
//o.stop(t + duration + release);
const stop = (time) => o.stop(time);
return { node: o, stop };
// Additional oscillator for vibrato effect
let vibrato_oscillator;
if (vib > 0) {
vibrato_oscillator = getAudioContext().createOscillator();
vibrato_oscillator.frequency.value = vib;
const gain = getAudioContext().createGain();
// Vibmod is the amount of vibrato, in semitones
gain.gain.value = vibmod * 100;
vibrato_oscillator.connect(gain);
gain.connect(o.detune);
vibrato_oscillator.start(t);
}
return {
node: o,
stop: (time) => {
vibrato_oscillator?.stop(time);
o.stop(time);
},
};
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel.cycles/tonal",
"version": "0.8.2",
"version": "0.9.0",
"description": "Tonal functions for strudel",
"main": "index.mjs",
"publishConfig": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel.cycles/transpiler",
"version": "0.8.2",
"version": "0.9.0",
"description": "Transpiler for strudel user code. Converts syntactically correct but semantically meaningless JS into evaluatable strudel code.",
"main": "index.mjs",
"publishConfig": {
+35 -6
View File
@@ -5,7 +5,7 @@ import { isNoteWithOctave } from '@strudel.cycles/core';
import { getLeafLocations } from '@strudel.cycles/mini';
export function transpiler(input, options = {}) {
const { wrapAsync = false, addReturn = true, emitMiniLocations = true } = options;
const { wrapAsync = false, addReturn = true, emitMiniLocations = true, emitWidgets = true } = options;
let ast = parse(input, {
ecmaVersion: 2022,
@@ -16,9 +16,9 @@ export function transpiler(input, options = {}) {
let miniLocations = [];
const collectMiniLocations = (value, node) => {
const leafLocs = getLeafLocations(`"${value}"`, node.start); // stimmt!
//const withOffset = leafLocs.map((offsets) => offsets.map((o) => o + node.start));
miniLocations = miniLocations.concat(leafLocs);
};
let widgets = [];
walk(ast, {
enter(node, parent /* , prop, index */) {
@@ -35,6 +35,17 @@ export function transpiler(input, options = {}) {
emitMiniLocations && collectMiniLocations(value, node);
return this.replace(miniWithLocation(value, node));
}
if (isWidgetFunction(node)) {
emitWidgets &&
widgets.push({
from: node.arguments[0].start,
to: node.arguments[0].end,
value: node.arguments[0].raw, // don't use value!
min: node.arguments[1]?.value ?? 0,
max: node.arguments[2]?.value ?? 1,
});
return this.replace(widgetWithLocation(node));
}
// TODO: remove pseudo note variables?
if (node.type === 'Identifier' && isNoteWithOctave(node.name)) {
this.skip();
@@ -64,15 +75,14 @@ export function transpiler(input, options = {}) {
if (!emitMiniLocations) {
return { output };
}
return { output, miniLocations };
return { output, miniLocations, widgets };
}
function isStringWithDoubleQuotes(node, locations, code) {
const { raw, type } = node;
if (type !== 'Literal') {
if (node.type !== 'Literal') {
return false;
}
return raw[0] === '"';
return node.raw[0] === '"';
}
function isBackTickString(node, parent) {
@@ -94,3 +104,22 @@ function miniWithLocation(value, node) {
optional: false,
};
}
// these functions are connected to @strudel/codemirror -> slider.mjs
// maybe someday there will be pluggable transpiler functions, then move this there
function isWidgetFunction(node) {
return node.type === 'CallExpression' && node.callee.name === 'slider';
}
function widgetWithLocation(node) {
const id = 'slider_' + node.arguments[0].start; // use loc of first arg for id
// add loc as identifier to first argument
// the sliderWithID function is assumed to be sliderWithID(id, value, min?, max?)
node.arguments.unshift({
type: 'Literal',
value: id,
raw: id,
});
node.callee.name = 'sliderWithID';
return node;
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel/web",
"version": "0.8.3",
"version": "0.9.0",
"description": "Easy to setup, opiniated bundle of Strudel for the browser.",
"main": "web.mjs",
"publishConfig": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel.cycles/webaudio",
"version": "0.8.2",
"version": "0.9.0",
"description": "Web Audio helpers for Strudel",
"main": "index.mjs",
"type": "module",
+5 -1
View File
@@ -5,7 +5,7 @@ This program is free software: you can redistribute it and/or modify it under th
*/
import * as strudel from '@strudel.cycles/core';
import { superdough, getAudioContext, setLogger } from 'superdough';
import { superdough, getAudioContext, setLogger, doughTrigger } from 'superdough';
const { Pattern, logger } = strudel;
setLogger(logger);
@@ -35,3 +35,7 @@ export function webaudioScheduler(options = {}) {
onTrigger: strudel.getTrigger({ defaultOutput, getTime }),
});
}
Pattern.prototype.dough = function () {
return this.onTrigger(doughTrigger, 1);
};
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel.cycles/xen",
"version": "0.8.0",
"version": "0.9.0",
"description": "Xenharmonic API for strudel",
"main": "index.mjs",
"publishConfig": {
+18
View File
@@ -588,6 +588,9 @@ importers:
'@strudel.cycles/xen':
specifier: workspace:*
version: link:../packages/xen
'@strudel/codemirror':
specifier: workspace:*
version: link:../packages/codemirror
'@strudel/desktopbridge':
specifier: workspace:*
version: link:../packages/desktopbridge
@@ -1426,6 +1429,7 @@ packages:
/@babel/plugin-proposal-async-generator-functions@7.20.7(@babel/core@7.21.5):
resolution: {integrity: sha512-xMbiLsn/8RK7Wq7VeVytytS2L6qE69bXPB10YCmMdDZbKF4okCqY74pI/jJQ/8U0b/F6NrT2+14b8/P9/3AMGA==}
engines: {node: '>=6.9.0'}
deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-async-generator-functions instead.
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
@@ -1441,6 +1445,7 @@ packages:
/@babel/plugin-proposal-class-properties@7.18.6(@babel/core@7.21.5):
resolution: {integrity: sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==}
engines: {node: '>=6.9.0'}
deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-class-properties instead.
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
@@ -1454,6 +1459,7 @@ packages:
/@babel/plugin-proposal-class-static-block@7.20.7(@babel/core@7.21.5):
resolution: {integrity: sha512-AveGOoi9DAjUYYuUAG//Ig69GlazLnoyzMw68VCDux+c1tsnnH/OkYcpz/5xzMkEFC6UxjR5Gw1c+iY2wOGVeQ==}
engines: {node: '>=6.9.0'}
deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-class-static-block instead.
peerDependencies:
'@babel/core': ^7.12.0
dependencies:
@@ -1468,6 +1474,7 @@ packages:
/@babel/plugin-proposal-dynamic-import@7.18.6(@babel/core@7.21.5):
resolution: {integrity: sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw==}
engines: {node: '>=6.9.0'}
deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-dynamic-import instead.
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
@@ -1479,6 +1486,7 @@ packages:
/@babel/plugin-proposal-export-namespace-from@7.18.9(@babel/core@7.21.5):
resolution: {integrity: sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA==}
engines: {node: '>=6.9.0'}
deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-export-namespace-from instead.
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
@@ -1490,6 +1498,7 @@ packages:
/@babel/plugin-proposal-json-strings@7.18.6(@babel/core@7.21.5):
resolution: {integrity: sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ==}
engines: {node: '>=6.9.0'}
deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-json-strings instead.
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
@@ -1501,6 +1510,7 @@ packages:
/@babel/plugin-proposal-logical-assignment-operators@7.20.7(@babel/core@7.21.5):
resolution: {integrity: sha512-y7C7cZgpMIjWlKE5T7eJwp+tnRYM89HmRvWM5EQuB5BoHEONjmQ8lSNmBUwOyy/GFRsohJED51YBF79hE1djug==}
engines: {node: '>=6.9.0'}
deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-logical-assignment-operators instead.
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
@@ -1512,6 +1522,7 @@ packages:
/@babel/plugin-proposal-nullish-coalescing-operator@7.18.6(@babel/core@7.21.5):
resolution: {integrity: sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==}
engines: {node: '>=6.9.0'}
deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-nullish-coalescing-operator instead.
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
@@ -1523,6 +1534,7 @@ packages:
/@babel/plugin-proposal-numeric-separator@7.18.6(@babel/core@7.21.5):
resolution: {integrity: sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q==}
engines: {node: '>=6.9.0'}
deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-numeric-separator instead.
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
@@ -1534,6 +1546,7 @@ packages:
/@babel/plugin-proposal-object-rest-spread@7.20.7(@babel/core@7.21.5):
resolution: {integrity: sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg==}
engines: {node: '>=6.9.0'}
deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-object-rest-spread instead.
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
@@ -1548,6 +1561,7 @@ packages:
/@babel/plugin-proposal-optional-catch-binding@7.18.6(@babel/core@7.21.5):
resolution: {integrity: sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw==}
engines: {node: '>=6.9.0'}
deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-optional-catch-binding instead.
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
@@ -1559,6 +1573,7 @@ packages:
/@babel/plugin-proposal-optional-chaining@7.20.7(@babel/core@7.21.5):
resolution: {integrity: sha512-T+A7b1kfjtRM51ssoOfS1+wbyCVqorfyZhT99TvxxLMirPShD8CzKMRepMlCBGM5RpHMbn8s+5MMHnPstJH6mQ==}
engines: {node: '>=6.9.0'}
deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-optional-chaining instead.
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
@@ -1571,6 +1586,7 @@ packages:
/@babel/plugin-proposal-private-methods@7.18.6(@babel/core@7.21.5):
resolution: {integrity: sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==}
engines: {node: '>=6.9.0'}
deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-private-methods instead.
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
@@ -1584,6 +1600,7 @@ packages:
/@babel/plugin-proposal-private-property-in-object@7.20.5(@babel/core@7.21.5):
resolution: {integrity: sha512-Vq7b9dUA12ByzB4EjQTPo25sFhY+08pQDBSZRtUAkj7lb7jahaHR5igera16QZ+3my1nYR4dKsNdYj5IjPHilQ==}
engines: {node: '>=6.9.0'}
deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-private-property-in-object instead.
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
@@ -1599,6 +1616,7 @@ packages:
/@babel/plugin-proposal-unicode-property-regex@7.18.6(@babel/core@7.21.5):
resolution: {integrity: sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w==}
engines: {node: '>=4'}
deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-unicode-property-regex instead.
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
+97 -4
View File
@@ -1828,6 +1828,15 @@ exports[`runs examples > example "firstOf" example index 0 1`] = `
]
`;
exports[`runs examples > example "fit" example index 0 1`] = `
[
"[ (0/1 → 1/1) ⇝ 4/1 | s:rhodes speed:0.25 unit:c ]",
"[ 0/1 ⇜ (1/1 → 2/1) ⇝ 4/1 | s:rhodes speed:0.25 unit:c ]",
"[ 0/1 ⇜ (2/1 → 3/1) ⇝ 4/1 | s:rhodes speed:0.25 unit:c ]",
"[ 0/1 ⇜ (3/1 → 4/1) | s:rhodes speed:0.25 unit:c ]",
]
`;
exports[`runs examples > example "floor" example index 0 1`] = `
[
"[ 0/1 → 1/4 | note:42 ]",
@@ -2640,10 +2649,10 @@ exports[`runs examples > example "linger" example index 0 1`] = `
exports[`runs examples > example "loop" example index 0 1`] = `
[
"[ 0/1 → 1/1 | s:bd loop:1 ]",
"[ 1/1 → 2/1 | s:bd loop:2 ]",
"[ 2/1 → 3/1 | s:bd loop:3 ]",
"[ 3/1 → 4/1 | s:bd loop:4 ]",
"[ 0/1 → 1/1 | s:casio loop:1 ]",
"[ 1/1 → 2/1 | s:casio loop:1 ]",
"[ 2/1 → 3/1 | s:casio loop:1 ]",
"[ 3/1 → 4/1 | s:casio loop:1 ]",
]
`;
@@ -2665,6 +2674,24 @@ exports[`runs examples > example "loopAtCps" example index 0 1`] = `
]
`;
exports[`runs examples > example "loopBegin" example index 0 1`] = `
[
"[ 0/1 → 1/1 | s:space loop:1 loopBegin:0 analyze:1 ]",
"[ 1/1 → 2/1 | s:space loop:1 loopBegin:0.125 analyze:1 ]",
"[ 2/1 → 3/1 | s:space loop:1 loopBegin:0.25 analyze:1 ]",
"[ 3/1 → 4/1 | s:space loop:1 loopBegin:0 analyze:1 ]",
]
`;
exports[`runs examples > example "loopEnd" example index 0 1`] = `
[
"[ 0/1 → 1/1 | s:space loop:1 loopEnd:1 analyze:1 ]",
"[ 1/1 → 2/1 | s:space loop:1 loopEnd:0.75 analyze:1 ]",
"[ 2/1 → 3/1 | s:space loop:1 loopEnd:0.5 analyze:1 ]",
"[ 3/1 → 4/1 | s:space loop:1 loopEnd:0.25 analyze:1 ]",
]
`;
exports[`runs examples > example "lpattack" example index 0 1`] = `
[
"[ 0/1 → 1/1 | note:c2 s:sawtooth cutoff:500 lpattack:0.5 lpenv:4 ]",
@@ -4323,6 +4350,36 @@ exports[`runs examples > example "speed" example index 1 1`] = `
]
`;
exports[`runs examples > example "splice" example index 0 1`] = `
[
"[ 0/1 → 5/26 | speed:0.65 unit:c begin:0 end:0.125 _slices:8 s:breaks165 ]",
"[ 5/26 → 5/13 | speed:0.65 unit:c begin:0.125 end:0.25 _slices:8 s:breaks165 ]",
"[ 5/13 → 20/39 | speed:0.9750000000000001 unit:c begin:0.25 end:0.375 _slices:8 s:breaks165 ]",
"[ 20/39 → 25/39 | speed:0.9750000000000001 unit:c begin:0.375 end:0.5 _slices:8 s:breaks165 ]",
"[ 25/39 → 10/13 | speed:0.9750000000000001 unit:c begin:0 end:0.125 _slices:8 s:breaks165 ]",
"[ 10/13 → 25/26 | speed:0.65 unit:c begin:0.375 end:0.5 _slices:8 s:breaks165 ]",
"[ (25/26 → 1/1) ⇝ 35/26 | speed:0.325 unit:c begin:0 end:0.125 _slices:8 s:breaks165 ]",
"[ 25/26 ⇜ (1/1 → 35/26) | speed:0.325 unit:c begin:0 end:0.125 _slices:8 s:breaks165 ]",
"[ 35/26 → 20/13 | speed:0.65 unit:c begin:0.875 end:1 _slices:8 s:breaks165 ]",
"[ 20/13 → 45/26 | speed:0.65 unit:c begin:0 end:0.125 _slices:8 s:breaks165 ]",
"[ 45/26 → 25/13 | speed:0.65 unit:c begin:0.125 end:0.25 _slices:8 s:breaks165 ]",
"[ (25/13 → 2/1) ⇝ 80/39 | speed:0.9750000000000001 unit:c begin:0.25 end:0.375 _slices:8 s:breaks165 ]",
"[ 25/13 ⇜ (2/1 → 80/39) | speed:0.9750000000000001 unit:c begin:0.25 end:0.375 _slices:8 s:breaks165 ]",
"[ 80/39 → 85/39 | speed:0.9750000000000001 unit:c begin:0.375 end:0.5 _slices:8 s:breaks165 ]",
"[ 85/39 → 30/13 | speed:0.9750000000000001 unit:c begin:0 end:0.125 _slices:8 s:breaks165 ]",
"[ 30/13 → 5/2 | speed:0.65 unit:c begin:0.375 end:0.5 _slices:8 s:breaks165 ]",
"[ 5/2 → 75/26 | speed:0.325 unit:c begin:0 end:0.125 _slices:8 s:breaks165 ]",
"[ (75/26 → 3/1) ⇝ 40/13 | speed:0.65 unit:c begin:0.875 end:1 _slices:8 s:breaks165 ]",
"[ 75/26 ⇜ (3/1 → 40/13) | speed:0.65 unit:c begin:0.875 end:1 _slices:8 s:breaks165 ]",
"[ 40/13 → 85/26 | speed:0.65 unit:c begin:0 end:0.125 _slices:8 s:breaks165 ]",
"[ 85/26 → 45/13 | speed:0.65 unit:c begin:0.125 end:0.25 _slices:8 s:breaks165 ]",
"[ 45/13 → 140/39 | speed:0.9750000000000001 unit:c begin:0.25 end:0.375 _slices:8 s:breaks165 ]",
"[ 140/39 → 145/39 | speed:0.9750000000000001 unit:c begin:0.375 end:0.5 _slices:8 s:breaks165 ]",
"[ 145/39 → 50/13 | speed:0.9750000000000001 unit:c begin:0 end:0.125 _slices:8 s:breaks165 ]",
"[ (50/13 → 4/1) ⇝ 105/26 | speed:0.65 unit:c begin:0.375 end:0.5 _slices:8 s:breaks165 ]",
]
`;
exports[`runs examples > example "square" example index 0 1`] = `
[
"[ 0/1 → 1/2 | note:C3 ]",
@@ -4701,6 +4758,42 @@ exports[`runs examples > example "velocity" example index 0 1`] = `
]
`;
exports[`runs examples > example "vib" example index 0 1`] = `
[
"[ 0/1 → 1/1 | note:a vib:0.5 ]",
"[ 1/1 → 2/1 | note:a vib:1 ]",
"[ 2/1 → 3/1 | note:a vib:2 ]",
"[ 3/1 → 4/1 | note:a vib:4 ]",
]
`;
exports[`runs examples > example "vib" example index 1 1`] = `
[
"[ 0/1 → 1/1 | note:a vib:0.5 vibmod:12 ]",
"[ 1/1 → 2/1 | note:a vib:1 vibmod:12 ]",
"[ 2/1 → 3/1 | note:a vib:2 vibmod:12 ]",
"[ 3/1 → 4/1 | note:a vib:4 vibmod:12 ]",
]
`;
exports[`runs examples > example "vibmod" example index 0 1`] = `
[
"[ 0/1 → 1/1 | note:a vib:4 vibmod:0.25 ]",
"[ 1/1 → 2/1 | note:a vib:4 vibmod:0.5 ]",
"[ 2/1 → 3/1 | note:a vib:4 vibmod:1 ]",
"[ 3/1 → 4/1 | note:a vib:4 vibmod:2 ]",
]
`;
exports[`runs examples > example "vibmod" example index 1 1`] = `
[
"[ 0/1 → 1/1 | note:a vibmod:0.25 vib:8 ]",
"[ 1/1 → 2/1 | note:a vibmod:0.5 vib:8 ]",
"[ 2/1 → 3/1 | note:a vibmod:1 vib:8 ]",
"[ 3/1 → 4/1 | note:a vibmod:2 vib:8 ]",
]
`;
exports[`runs examples > example "voicing" example index 0 1`] = `
[
"[ 0/1 → 1/1 | note:E4 ]",
+47 -62
View File
@@ -3,23 +3,23 @@
exports[`renders tunes > tune: amensister 1`] = `
[
"[ 0/1 → 1/16 | s:breath room:1 shape:0.6 begin:0.9375 end:1 ]",
"[ 0/1 → 1/8 | note:Eb1 s:sawtooth decay:0.1 sustain:0 lpattack:0.1 lpenv:4 gain:0.4 cutoff:300.0066107586751 resonance:10 ]",
"[ 0/1 → 1/8 | note:F1 s:sawtooth decay:0.1 sustain:0 lpattack:0.1 lpenv:4 gain:0.4 cutoff:300.0066107586751 resonance:10 ]",
"[ 0/1 → 1/8 | note:Eb1 s:sawtooth gain:0.4 decay:0.1 sustain:0 lpattack:0.1 lpenv:-4 resonance:10 cutoff:300.0066107586751 ]",
"[ 0/1 → 1/8 | note:F1 s:sawtooth gain:0.4 decay:0.1 sustain:0 lpattack:0.1 lpenv:-4 resonance:10 cutoff:300.0066107586751 ]",
"[ 0/1 → 1/4 | n:0 s:amencutup room:0.5 ]",
"[ 1/16 → 1/8 | s:breath room:1 shape:0.6 begin:0.875 end:0.9375 ]",
"[ 1/8 → 3/16 | s:breath room:1 shape:0.6 begin:0.8125 end:0.875 ]",
"[ 1/8 → 1/4 | note:45 s:sawtooth decay:0.1 sustain:0 lpattack:0.1 lpenv:4 gain:0.4 cutoff:300.174310575404 resonance:10 ]",
"[ 1/8 → 1/4 | note:45 s:sawtooth decay:0.1 sustain:0 lpattack:0.1 lpenv:4 gain:0.4 cutoff:300.174310575404 resonance:10 ]",
"[ 1/8 → 1/4 | note:45 s:sawtooth gain:0.4 decay:0.1 sustain:0 lpattack:0.1 lpenv:-4 resonance:10 cutoff:300.174310575404 ]",
"[ 1/8 → 1/4 | note:45 s:sawtooth gain:0.4 decay:0.1 sustain:0 lpattack:0.1 lpenv:-4 resonance:10 cutoff:300.174310575404 ]",
"[ 3/16 → 1/4 | s:breath room:1 shape:0.6 begin:0.75 end:0.8125 ]",
"[ 1/4 → 5/16 | s:breath room:1 shape:0.6 begin:0.6875 end:0.75 ]",
"[ 1/4 → 3/8 | n:1 speed:2 delay:0.5 s:amencutup room:0.5 ]",
"[ 1/4 → 3/8 | note:A1 s:sawtooth decay:0.1 sustain:0 lpattack:0.1 lpenv:4 gain:0.4 cutoff:300.7878869297153 resonance:10 ]",
"[ 1/4 → 3/8 | note:A1 s:sawtooth decay:0.1 sustain:0 lpattack:0.1 lpenv:4 gain:0.4 cutoff:300.7878869297153 resonance:10 ]",
"[ 1/4 → 3/8 | note:A1 s:sawtooth gain:0.4 decay:0.1 sustain:0 lpattack:0.1 lpenv:-4 resonance:10 cutoff:300.7878869297153 ]",
"[ 1/4 → 3/8 | note:A1 s:sawtooth gain:0.4 decay:0.1 sustain:0 lpattack:0.1 lpenv:-4 resonance:10 cutoff:300.7878869297153 ]",
"[ 5/16 → 3/8 | s:breath room:1 shape:0.6 begin:0.625 end:0.6875 ]",
"[ 3/8 → 7/16 | s:breath room:1 shape:0.6 begin:0.5625 end:0.625 ]",
"[ 3/8 → 1/2 | n:1 s:amencutup room:0.5 ]",
"[ 3/8 → 1/2 | note:F1 s:sawtooth decay:0.1 sustain:0 lpattack:0.1 lpenv:4 gain:0.4 cutoff:302.11020572391345 resonance:10 ]",
"[ 3/8 → 1/2 | note:F1 s:sawtooth decay:0.1 sustain:0 lpattack:0.1 lpenv:4 gain:0.4 cutoff:302.11020572391345 resonance:10 ]",
"[ 3/8 → 1/2 | note:F1 s:sawtooth gain:0.4 decay:0.1 sustain:0 lpattack:0.1 lpenv:-4 resonance:10 cutoff:302.11020572391345 ]",
"[ 3/8 → 1/2 | note:F1 s:sawtooth gain:0.4 decay:0.1 sustain:0 lpattack:0.1 lpenv:-4 resonance:10 cutoff:302.11020572391345 ]",
"[ 7/16 → 1/2 | s:breath room:1 shape:0.6 begin:0.5 end:0.5625 ]",
"[ 1/2 → 9/16 | s:breath room:1 shape:0.6 begin:0.4375 end:0.5 ]",
"[ 1/2 → 3/4 | n:2 s:amencutup room:0.5 ]",
@@ -28,13 +28,13 @@ exports[`renders tunes > tune: amensister 1`] = `
"[ 11/16 → 3/4 | s:breath room:1 shape:0.6 begin:0.25 end:0.3125 ]",
"[ 3/4 → 13/16 | s:breath room:1 shape:0.6 begin:0.1875 end:0.25 ]",
"[ 3/4 → 7/8 | n:3 s:amencutup room:0.5 ]",
"[ 3/4 → 7/8 | note:Bb0 s:sawtooth decay:0.1 sustain:0 lpattack:0.1 lpenv:4 gain:0.4 cutoff:312.54769231985796 resonance:10 ]",
"[ 3/4 → 7/8 | note:Bb0 s:sawtooth decay:0.1 sustain:0 lpattack:0.1 lpenv:4 gain:0.4 cutoff:312.54769231985796 resonance:10 ]",
"[ 3/4 → 7/8 | note:Bb0 s:sawtooth gain:0.4 decay:0.1 sustain:0 lpattack:0.1 lpenv:-4 resonance:10 cutoff:312.54769231985796 ]",
"[ 3/4 → 7/8 | note:Bb0 s:sawtooth gain:0.4 decay:0.1 sustain:0 lpattack:0.1 lpenv:-4 resonance:10 cutoff:312.54769231985796 ]",
"[ 13/16 → 7/8 | s:breath room:1 shape:0.6 begin:0.125 end:0.1875 ]",
"[ 7/8 → 15/16 | s:breath room:1 shape:0.6 begin:0.0625 end:0.125 ]",
"[ 7/8 → 1/1 | n:3 s:amencutup room:0.5 ]",
"[ 7/8 → 1/1 | note:D1 s:sawtooth decay:0.1 sustain:0 lpattack:0.1 lpenv:4 gain:0.4 cutoff:318.7927796831686 resonance:10 ]",
"[ 7/8 → 1/1 | note:D1 s:sawtooth decay:0.1 sustain:0 lpattack:0.1 lpenv:4 gain:0.4 cutoff:318.7927796831686 resonance:10 ]",
"[ 7/8 → 1/1 | note:D1 s:sawtooth gain:0.4 decay:0.1 sustain:0 lpattack:0.1 lpenv:-4 resonance:10 cutoff:318.7927796831686 ]",
"[ 7/8 → 1/1 | note:D1 s:sawtooth gain:0.4 decay:0.1 sustain:0 lpattack:0.1 lpenv:-4 resonance:10 cutoff:318.7927796831686 ]",
"[ 15/16 → 1/1 | s:breath room:1 shape:0.6 begin:0 end:0.0625 ]",
]
`;
@@ -44,8 +44,8 @@ exports[`renders tunes > tune: arpoon 1`] = `
"[ (0/1 → 1/4) ⇝ 1/3 | note:55.000070545713186 clip:2 cutoff:501.2345499807435 resonance:12 gain:0.5 decay:0.16 sustain:0.5 delay:0.2 room:0.5 pan:0.48882285676537807 s:piano ]",
"[ (0/1 → 1/4) ⇝ 1/3 | note:64.00007054571319 clip:2 cutoff:501.2345499807435 resonance:12 gain:0.5 decay:0.16 sustain:0.5 delay:0.2 room:0.5 pan:0.48882285676537807 s:piano ]",
"[ 0/1 → 1/2 | s:bd bank:RolandTR909 gain:0.5 ]",
"[ 0/1 → 1/1 | note:33 s:sawtooth clip:1 cutoff:300 ]",
"[ 0/1 → 1/1 | note:33.12 s:sawtooth clip:1 cutoff:300 ]",
"[ 0/1 → 1/1 | note:33 s:sawtooth cutoff:180 lpattack:0.1 lpenv:2 ]",
"[ 0/1 → 1/1 | note:33.12 s:sawtooth cutoff:180 lpattack:0.1 lpenv:2 ]",
"[ 0/1 ⇜ (1/4 → 1/3) | note:55.000070545713186 clip:2 cutoff:501.2345499807435 resonance:12 gain:0.8 decay:0.16 sustain:0.5 delay:0.2 room:0.5 pan:0.48882285676537807 s:piano ]",
"[ 0/1 ⇜ (1/4 → 1/3) | note:64.00007054571319 clip:2 cutoff:501.2345499807435 resonance:12 gain:0.8 decay:0.16 sustain:0.5 delay:0.2 room:0.5 pan:0.48882285676537807 s:piano ]",
"[ (1/3 → 1/2) ⇝ 2/3 | note:60.00166796373806 clip:2 cutoff:529.1893654159594 resonance:12 gain:0.8 decay:0.16 sustain:0.5 delay:0.2 room:0.5 pan:0.5560660171779821 s:piano ]",
@@ -7315,20 +7315,20 @@ exports[`renders tunes > tune: flatrave 1`] = `
"[ 0/1 ⇜ (1/8 → 1/4) | s:hh n:1 end:0.02000058072071123 bank:RolandTR909 room:0.5 gain:0.4 ]",
"[ 1/8 → 1/4 | s:hh n:1 speed:0.5 delay:0.5 end:0.020001936784171157 bank:RolandTR909 room:0.5 gain:0.4 ]",
"[ 1/8 → 1/4 | s:hh n:1 speed:0.5 delay:0.5 end:0.020001936784171157 bank:RolandTR909 room:0.5 gain:0.4 ]",
"[ 1/8 → 1/4 | note:G1 s:sawtooth decay:0.1 sustain:0 ]",
"[ 1/8 → 1/4 | note:G1 s:sawtooth decay:0.1 sustain:0 lpattack:0.1 lpenv:-4 cutoff:800 resonance:8 ]",
"[ 1/4 → 3/8 | s:hh n:1 end:0.02000875429921906 bank:RolandTR909 room:0.5 gain:0.4 ]",
"[ 1/4 → 3/8 | s:hh n:1 end:0.02000875429921906 bank:RolandTR909 room:0.5 gain:0.4 ]",
"[ 1/4 → 3/8 | note:G1 s:sawtooth decay:0.1 sustain:0 ]",
"[ 1/4 → 3/8 | note:G1 s:sawtooth decay:0.1 sustain:0 lpattack:0.1 lpenv:-4 cutoff:800 resonance:8 ]",
"[ 3/8 → 1/2 | s:hh n:1 end:0.020023446730265706 bank:RolandTR909 room:0.5 gain:0.4 ]",
"[ 1/2 → 5/8 | note:G1 s:sawtooth decay:0.1 sustain:0 ]",
"[ 1/2 → 5/8 | note:G1 s:sawtooth decay:0.1 sustain:0 lpattack:0.1 lpenv:-4 cutoff:800 resonance:8 ]",
"[ 1/2 → 1/1 | s:bd bank:RolandTR909 ]",
"[ 1/2 → 1/1 | s:cp bank:RolandTR909 ]",
"[ 1/2 → 1/1 | s:sd bank:RolandTR909 ]",
"[ 5/8 → 3/4 | s:hh n:1 end:0.020086608138500644 bank:RolandTR909 room:0.5 gain:0.4 ]",
"[ 5/8 → 3/4 | s:hh n:1 end:0.020086608138500644 bank:RolandTR909 room:0.5 gain:0.4 ]",
"[ 5/8 → 3/4 | note:G1 s:sawtooth decay:0.1 sustain:0 ]",
"[ 5/8 → 3/4 | note:G1 s:sawtooth decay:0.1 sustain:0 lpattack:0.1 lpenv:-4 cutoff:800 resonance:8 ]",
"[ 3/4 → 7/8 | s:hh n:1 end:0.02013941880355398 bank:RolandTR909 room:0.5 gain:0.4 ]",
"[ 7/8 → 1/1 | note:G1 s:sawtooth decay:0.1 sustain:0 ]",
"[ 7/8 → 1/1 | note:G1 s:sawtooth decay:0.1 sustain:0 lpattack:0.1 lpenv:-4 cutoff:800 resonance:8 ]",
]
`;
@@ -8372,16 +8372,16 @@ exports[`renders tunes > tune: hyperpop 1`] = `
exports[`renders tunes > tune: juxUndTollerei 1`] = `
[
"[ 0/1 → 1/4 | note:bb3 s:sawtooth pan:0 cutoff:1188.2154262966046 decay:0.05 sustain:0 room:0.6 delay:0.5 delaytime:0.1 delayfeedback:0.4 ]",
"[ 0/1 → 1/4 | note:c3 s:sawtooth pan:1 cutoff:1188.2154262966046 decay:0.05 sustain:0 room:0.6 delay:0.5 delaytime:0.1 delayfeedback:0.4 ]",
"[ 1/4 → 1/2 | note:g3 s:sawtooth pan:0 cutoff:1361.2562095290161 decay:0.05 sustain:0 room:0.6 delay:0.5 delaytime:0.1 delayfeedback:0.4 ]",
"[ 1/4 → 1/2 | note:eb3 s:sawtooth pan:1 cutoff:1361.2562095290161 decay:0.05 sustain:0 room:0.6 delay:0.5 delaytime:0.1 delayfeedback:0.4 ]",
"[ 1/2 → 3/4 | note:eb3 s:sawtooth pan:0 cutoff:1524.257063143398 decay:0.05 sustain:0 room:0.6 delay:0.5 delaytime:0.1 delayfeedback:0.4 ]",
"[ 1/2 → 3/4 | note:g3 s:sawtooth pan:1 cutoff:1524.257063143398 decay:0.05 sustain:0 room:0.6 delay:0.5 delaytime:0.1 delayfeedback:0.4 ]",
"[ (101/200 → 1/1) ⇝ 201/200 | note:65 s:triangle pan:0 cutoff:1601.4815730092653 decay:0.05 sustain:0 room:0.6 delay:0.5 delaytime:0.1 delayfeedback:0.4 ]",
"[ (101/200 → 1/1) ⇝ 201/200 | note:55 s:triangle pan:1 cutoff:1601.4815730092653 decay:0.05 sustain:0 room:0.6 delay:0.5 delaytime:0.1 delayfeedback:0.4 ]",
"[ 3/4 → 1/1 | note:c3 s:sawtooth pan:0 cutoff:1670.953955747281 decay:0.05 sustain:0 room:0.6 delay:0.5 delaytime:0.1 delayfeedback:0.4 ]",
"[ 3/4 → 1/1 | note:bb3 s:sawtooth pan:1 cutoff:1670.953955747281 decay:0.05 sustain:0 room:0.6 delay:0.5 delaytime:0.1 delayfeedback:0.4 ]",
"[ 0/1 → 1/4 | note:bb3 s:sawtooth pan:0 cutoff:1188.2154262966046 lpattack:0.2 lpenv:-2 decay:0.05 sustain:0 room:0.6 delay:0.5 delaytime:0.1 delayfeedback:0.4 ]",
"[ 0/1 → 1/4 | note:c3 s:sawtooth pan:1 cutoff:1188.2154262966046 lpattack:0.2 lpenv:-2 decay:0.05 sustain:0 room:0.6 delay:0.5 delaytime:0.1 delayfeedback:0.4 ]",
"[ 1/4 → 1/2 | note:g3 s:sawtooth pan:0 cutoff:1361.2562095290161 lpattack:0.2 lpenv:-2 decay:0.05 sustain:0 room:0.6 delay:0.5 delaytime:0.1 delayfeedback:0.4 ]",
"[ 1/4 → 1/2 | note:eb3 s:sawtooth pan:1 cutoff:1361.2562095290161 lpattack:0.2 lpenv:-2 decay:0.05 sustain:0 room:0.6 delay:0.5 delaytime:0.1 delayfeedback:0.4 ]",
"[ 1/2 → 3/4 | note:eb3 s:sawtooth pan:0 cutoff:1524.257063143398 lpattack:0.2 lpenv:-2 decay:0.05 sustain:0 room:0.6 delay:0.5 delaytime:0.1 delayfeedback:0.4 ]",
"[ 1/2 → 3/4 | note:g3 s:sawtooth pan:1 cutoff:1524.257063143398 lpattack:0.2 lpenv:-2 decay:0.05 sustain:0 room:0.6 delay:0.5 delaytime:0.1 delayfeedback:0.4 ]",
"[ (101/200 → 1/1) ⇝ 201/200 | note:65 s:triangle pan:0 cutoff:1601.4815730092653 lpattack:0.2 lpenv:-2 decay:0.05 sustain:0 room:0.6 delay:0.5 delaytime:0.1 delayfeedback:0.4 ]",
"[ (101/200 → 1/1) ⇝ 201/200 | note:55 s:triangle pan:1 cutoff:1601.4815730092653 lpattack:0.2 lpenv:-2 decay:0.05 sustain:0 room:0.6 delay:0.5 delaytime:0.1 delayfeedback:0.4 ]",
"[ 3/4 → 1/1 | note:c3 s:sawtooth pan:0 cutoff:1670.953955747281 lpattack:0.2 lpenv:-2 decay:0.05 sustain:0 room:0.6 delay:0.5 delaytime:0.1 delayfeedback:0.4 ]",
"[ 3/4 → 1/1 | note:bb3 s:sawtooth pan:1 cutoff:1670.953955747281 lpattack:0.2 lpenv:-2 decay:0.05 sustain:0 room:0.6 delay:0.5 delaytime:0.1 delayfeedback:0.4 ]",
]
`;
@@ -8422,8 +8422,8 @@ exports[`renders tunes > tune: meltingsubmarine 1`] = `
"[ 0/1 → 3/16 | note:93.00057728554401 decay:0.1 sustain:0 s:triangle gain:0.075 ]",
"[ 0/1 → 3/16 | note:93.04057728554402 decay:0.1 sustain:0 s:triangle gain:0.075 ]",
"[ (0/1 → 1/1) ⇝ 3/2 | s:bd n:5 speed:0.7519542165100574 ]",
"[ (0/1 → 1/1) ⇝ 3/2 | note:33.129885541275144 decay:0.15 sustain:0 s:sawtooth gain:0.4 cutoff:3669.6267869262615 ]",
"[ (0/1 → 1/1) ⇝ 3/2 | note:33.17988554127514 decay:0.15 sustain:0 s:sawtooth gain:0.4 cutoff:3669.6267869262615 ]",
"[ (0/1 → 1/1) ⇝ 3/2 | note:33.129885541275144 decay:0.15 sustain:0 s:sawtooth gain:0.4 cutoff:3669.6267869262615 lpattack:0.1 lpenv:-2 ]",
"[ (0/1 → 1/1) ⇝ 3/2 | note:33.17988554127514 decay:0.15 sustain:0 s:sawtooth gain:0.4 cutoff:3669.6267869262615 lpattack:0.1 lpenv:-2 ]",
"[ (0/1 → 1/1) ⇝ 3/2 | note:60.129885541275144 s:sawtooth gain:0.16 cutoff:500 attack:1 ]",
"[ (0/1 → 1/1) ⇝ 3/2 | note:60.16988554127514 s:sawtooth gain:0.16 cutoff:500 attack:1 ]",
"[ (0/1 → 1/1) ⇝ 3/2 | note:64.12988554127514 s:sawtooth gain:0.16 cutoff:500 attack:1 ]",
@@ -8481,21 +8481,6 @@ exports[`renders tunes > tune: orbit 1`] = `
]
`;
exports[`renders tunes > tune: outroMusic 1`] = `
[
"[ 0/1 → 1/2 | s:hh speed:0.9036881079621337 n:3 ]",
"[ 0/1 → 3/4 | note:C2 s:sawtooth attack:0.05 decay:0.1 sustain:0.7 cutoff:864.536878321087 gain:0.3 ]",
"[ 0/1 → 3/4 | s:bd speed:0.9107561463868479 n:3 ]",
"[ (0/1 → 1/1) ⇝ 3/1 | note:B3 s:gm_epiano1 n:1 attack:0.05 decay:0.1 sustain:0.7 cutoff:1111.7252990603447 gain:0.3 ]",
"[ (0/1 → 1/1) ⇝ 3/1 | note:D4 s:gm_epiano1 n:1 attack:0.05 decay:0.1 sustain:0.7 cutoff:1111.7252990603447 gain:0.3 ]",
"[ (0/1 → 1/1) ⇝ 3/1 | note:E4 s:gm_epiano1 n:1 attack:0.05 decay:0.1 sustain:0.7 cutoff:1111.7252990603447 gain:0.3 ]",
"[ (0/1 → 1/1) ⇝ 3/1 | note:G4 s:gm_epiano1 n:1 attack:0.05 decay:0.1 sustain:0.7 cutoff:1111.7252990603447 gain:0.3 ]",
"[ (0/1 → 1/1) ⇝ 9/2 | n:1 note:C5 s:gm_epiano1 attack:0.05 decay:0.1 sustain:0.7 cutoff:1111.7252990603447 gain:0.3 ]",
"[ 1/2 → 1/1 | s:hh speed:0.9519542165100575 n:3 ]",
"[ (3/4 → 1/1) ⇝ 3/2 | s:sd speed:0.9931522866332672 n:3 ]",
]
`;
exports[`renders tunes > tune: randomBells 1`] = `
[
"[ -9/8 ⇜ (0/1 → 3/8) | note:G4 s:bell gain:0.6 delay:0.2 delaytime:0.3333333333333333 delayfeedback:0.8 ]",
@@ -10180,22 +10165,22 @@ exports[`renders tunes > tune: undergroundPlumber 1`] = `
exports[`renders tunes > tune: waa2 1`] = `
[
"[ -1/4 ⇜ (0/1 → 1/4) | n:48 clip:1.1738393178344886 s:sawtooth cutoff:3997.892048359052 gain:0.5 room:0.5 ]",
"[ -1/4 ⇜ (0/1 → 1/4) | n:64 clip:1.1738393178344886 s:sawtooth cutoff:3997.892048359052 gain:0.5 room:0.5 ]",
"[ (0/1 → 1/4) ⇝ 1/2 | n:62 clip:1.197659880151613 s:sawtooth cutoff:3991.5732716763446 gain:0.5 room:0.5 ]",
"[ (0/1 → 1/4) ⇝ 1/2 | n:43 clip:1.197659880151613 s:sawtooth cutoff:3991.5732716763446 gain:0.5 room:0.5 ]",
"[ 0/1 ⇜ (1/4 → 1/2) | n:62 clip:1.197659880151613 s:square cutoff:3991.5732716763446 gain:0.5 room:0.5 ]",
"[ 0/1 ⇜ (1/4 → 1/2) | n:43 clip:1.197659880151613 s:square cutoff:3991.5732716763446 gain:0.5 room:0.5 ]",
"[ (1/4 → 1/2) ⇝ 3/4 | n:74 clip:1.2451698046878117 s:square cutoff:3966.3742407056534 gain:0.5 room:0.5 ]",
"[ (1/4 → 1/2) ⇝ 3/4 | n:55 clip:1.2451698046878117 s:square cutoff:3966.3742407056534 gain:0.5 room:0.5 ]",
"[ 1/4 ⇜ (1/2 → 3/4) | n:74 clip:1.2451698046878117 s:sawtooth cutoff:3966.3742407056534 gain:0.5 room:0.5 ]",
"[ 1/4 ⇜ (1/2 → 3/4) | n:55 clip:1.2451698046878117 s:sawtooth cutoff:3966.3742407056534 gain:0.5 room:0.5 ]",
"[ 1/2 → 3/4 | n:50 clip:1.2688217886051745 s:sawtooth cutoff:3947.554693090452 gain:0.5 room:0.5 ]",
"[ (1/2 → 3/4) ⇝ 1/1 | n:69 clip:1.292380289809026 s:sawtooth cutoff:3924.645587531366 gain:0.5 room:0.5 ]",
"[ 1/2 ⇜ (3/4 → 1/1) | n:69 clip:1.292380289809026 s:square cutoff:3924.645587531366 gain:0.5 room:0.5 ]",
"[ 3/4 → 1/1 | n:41 clip:1.315826773713709 s:square cutoff:3897.7021140702864 gain:0.5 room:0.5 ]",
"[ 3/4 → 1/1 | n:62 clip:1.315826773713709 s:square cutoff:3897.7021140702864 gain:0.5 room:0.5 ]",
"[ (3/4 → 1/1) ⇝ 5/4 | n:81 clip:1.315826773713709 s:square cutoff:3897.7021140702864 gain:0.5 room:0.5 ]",
"[ -1/4 ⇜ (0/1 → 1/4) | note:48 clip:1.1738393178344886 s:sawtooth cutoff:3997.892048359052 gain:0.5 room:0.5 lpattack:0.125 lpenv:-2 vib:8 vibmod:0.125 fanchor:0.25 ]",
"[ -1/4 ⇜ (0/1 → 1/4) | note:64 clip:1.1738393178344886 s:sawtooth cutoff:3997.892048359052 gain:0.5 room:0.5 lpattack:0.125 lpenv:-2 vib:8 vibmod:0.125 fanchor:0.25 ]",
"[ (0/1 → 1/4) ⇝ 1/2 | note:62 clip:1.197659880151613 s:sawtooth cutoff:3991.5732716763446 gain:0.5 room:0.5 lpattack:0.125 lpenv:-2 vib:8 vibmod:0.125 fanchor:0.25 ]",
"[ (0/1 → 1/4) ⇝ 1/2 | note:43 clip:1.197659880151613 s:sawtooth cutoff:3991.5732716763446 gain:0.5 room:0.5 lpattack:0.125 lpenv:-2 vib:8 vibmod:0.125 fanchor:0.25 ]",
"[ 0/1 ⇜ (1/4 → 1/2) | note:62 clip:1.197659880151613 s:square cutoff:3991.5732716763446 gain:0.5 room:0.5 lpattack:0.125 lpenv:-2 vib:8 vibmod:0.125 fanchor:0.25 ]",
"[ 0/1 ⇜ (1/4 → 1/2) | note:43 clip:1.197659880151613 s:square cutoff:3991.5732716763446 gain:0.5 room:0.5 lpattack:0.125 lpenv:-2 vib:8 vibmod:0.125 fanchor:0.25 ]",
"[ (1/4 → 1/2) ⇝ 3/4 | note:74 clip:1.2451698046878117 s:square cutoff:3966.3742407056534 gain:0.5 room:0.5 lpattack:0.125 lpenv:-2 vib:8 vibmod:0.125 fanchor:0.25 ]",
"[ (1/4 → 1/2) ⇝ 3/4 | note:55 clip:1.2451698046878117 s:square cutoff:3966.3742407056534 gain:0.5 room:0.5 lpattack:0.125 lpenv:-2 vib:8 vibmod:0.125 fanchor:0.25 ]",
"[ 1/4 ⇜ (1/2 → 3/4) | note:74 clip:1.2451698046878117 s:sawtooth cutoff:3966.3742407056534 gain:0.5 room:0.5 lpattack:0.125 lpenv:-2 vib:8 vibmod:0.125 fanchor:0.25 ]",
"[ 1/4 ⇜ (1/2 → 3/4) | note:55 clip:1.2451698046878117 s:sawtooth cutoff:3966.3742407056534 gain:0.5 room:0.5 lpattack:0.125 lpenv:-2 vib:8 vibmod:0.125 fanchor:0.25 ]",
"[ 1/2 → 3/4 | note:50 clip:1.2688217886051745 s:sawtooth cutoff:3947.554693090452 gain:0.5 room:0.5 lpattack:0.125 lpenv:-2 vib:8 vibmod:0.125 fanchor:0.25 ]",
"[ (1/2 → 3/4) ⇝ 1/1 | note:69 clip:1.292380289809026 s:sawtooth cutoff:3924.645587531366 gain:0.5 room:0.5 lpattack:0.125 lpenv:-2 vib:8 vibmod:0.125 fanchor:0.25 ]",
"[ 1/2 ⇜ (3/4 → 1/1) | note:69 clip:1.292380289809026 s:square cutoff:3924.645587531366 gain:0.5 room:0.5 lpattack:0.125 lpenv:-2 vib:8 vibmod:0.125 fanchor:0.25 ]",
"[ 3/4 → 1/1 | note:41 clip:1.315826773713709 s:square cutoff:3897.7021140702864 gain:0.5 room:0.5 lpattack:0.125 lpenv:-2 vib:8 vibmod:0.125 fanchor:0.25 ]",
"[ 3/4 → 1/1 | note:62 clip:1.315826773713709 s:square cutoff:3897.7021140702864 gain:0.5 room:0.5 lpattack:0.125 lpenv:-2 vib:8 vibmod:0.125 fanchor:0.25 ]",
"[ (3/4 → 1/1) ⇝ 5/4 | note:81 clip:1.315826773713709 s:square cutoff:3897.7021140702864 gain:0.5 room:0.5 lpattack:0.125 lpenv:-2 vib:8 vibmod:0.125 fanchor:0.25 ]",
]
`;
+1
View File
@@ -28,6 +28,7 @@
"@strudel.cycles/mini": "workspace:*",
"@strudel.cycles/osc": "workspace:*",
"@strudel.cycles/react": "workspace:*",
"@strudel/codemirror": "workspace:*",
"@strudel.cycles/serial": "workspace:*",
"@strudel.cycles/soundfonts": "workspace:*",
"@strudel.cycles/tonal": "workspace:*",
+5 -4
View File
@@ -12,10 +12,11 @@ export function JsDoc({ name, h = 3, hideDescription, punchcard, canvasHeight })
}
const synonyms = getTag('synonyms', item)?.split(', ') || [];
const CustomHeading = `h${h}`;
const description = item.description.replaceAll(/\{@link ([a-zA-Z\.]+)?#?([a-zA-Z]*)\}/g, (_, a, b) => {
// console.log(_, 'a', a, 'b', b);
return `<a href="#${a.replaceAll('.', '').toLowerCase()}${b ? `-${b}` : ''}">${a}${b ? `#${b}` : ''}</a>`;
});
const description =
item.description?.replaceAll(/\{@link ([a-zA-Z\.]+)?#?([a-zA-Z]*)\}/g, (_, a, b) => {
// console.log(_, 'a', a, 'b', b);
return `<a href="#${a.replaceAll('.', '').toLowerCase()}${b ? `-${b}` : ''}">${a}${b ? `#${b}` : ''}</a>`;
}) || '';
return (
<>
{!!h && <CustomHeading>{item.longname}</CustomHeading>}
+20
View File
@@ -303,6 +303,18 @@ Sampler effects are functions that can be used to change the behaviour of sample
<JsDoc client:idle name="Pattern.end" h={0} />
### loop
<JsDoc client:idle name="loop" h={0} />
### loopBegin
<JsDoc client:idle name="loopBegin" h={0} />
### loopEnd
<JsDoc client:idle name="loopEnd" h={0} />
### cut
<JsDoc client:idle name="cut" h={0} />
@@ -315,6 +327,10 @@ Sampler effects are functions that can be used to change the behaviour of sample
<JsDoc client:idle name="Pattern.loopAt" h={0} />
### fit
<JsDoc client:idle name="fit" h={0} />
### chop
<JsDoc client:idle name="Pattern.chop" h={0} />
@@ -323,6 +339,10 @@ Sampler effects are functions that can be used to change the behaviour of sample
<JsDoc client:idle name="Pattern.slice" h={0} />
### splice
<JsDoc client:idle name="splice" h={0} />
### speed
<JsDoc client:idle name="speed" h={0} />
+27
View File
@@ -48,6 +48,16 @@ You can also set `n` directly in mini notation with `sound`:
Note for tidal users: `n` in tidal is synonymous to `note` for synths only.
In strudel, this is not the case, where `n` will always change timbre, be it though different samples or different waveforms.
## Vibrato
### vib
<JsDoc client:idle name="vib" h={0} />
### vibmod
<JsDoc client:idle name="vibmod" h={0} />
## FM Synthesis
FM Synthesis is a technique that changes the frequency of a basic waveform rapidly to alter the timbre.
@@ -78,6 +88,23 @@ You can use fm with any of the above waveforms, although the below examples all
<JsDoc client:idle name="fmenv" h={0} />
## Wavetable Synthesis
Strudel can also use the sampler to load custom waveforms as a replacement of the default waveforms used by WebAudio for the base synth. A default set of more than 1000 wavetables is accessible by default (coming from the [AKWF](https://www.adventurekid.se/akrt/waveforms/adventure-kid-waveforms/) set). You can also import/use your own. A wavetable is a one-cycle waveform, which is then repeated to create a sound at the desired frequency. It is a classic but very effective synthesis technique.
Any sample preceded by the `wt_` prefix will be loaded as a wavetable. This means that the `loop` argument will be set to `1` by defalt. You can scan over the wavetable by using `loopBegin` and `loopEnd` as well.
<MiniRepl
client:idle
tune={`samples('github:Bubobubobubobubo/Dough-Waveforms/main/');
note("<[g3,b3,e4]!2 [a3,c3,e4] [b3,d3,f#4]>")
.n("<1 2 3 4 5 6 7 8 9 10>/2").room(0.5).size(0.9)
.s('wt_flute').velocity(0.25).often(n => n.ply(2))
.release(0.125).decay("<0.1 0.25 0.3 0.4>").sustain(0)
.cutoff(2000).scope({}).cutoff("<1000 2000 4000>").fast(2)`}
/>
## ZZFX
The "Zuper Zmall Zound Zynth" [ZZFX](https://github.com/KilledByAPixel/ZzFX) is also integrated in strudel.
+16 -6
View File
@@ -22,6 +22,7 @@ import Loader from './Loader';
import { settingPatterns } from '../settings.mjs';
import { code2hash, hash2code } from './helpers.mjs';
import { isTauri } from '../tauri.mjs';
import { useWidgets } from '@strudel.cycles/react/src/hooks/useWidgets.mjs';
const { latestCode } = settingsMap.get();
@@ -33,26 +34,27 @@ const supabase = createClient(
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InBpZHhkc3hwaGxoempuem1pZnRoIiwicm9sZSI6ImFub24iLCJpYXQiOjE2NTYyMzA1NTYsImV4cCI6MTk3MTgwNjU1Nn0.bqlw7802fsWRnqU5BLYtmXk_k-D1VFmbkHMywWc15NM',
);
const modules = [
let modules = [
import('@strudel.cycles/core'),
import('@strudel.cycles/tonal'),
import('@strudel.cycles/mini'),
import('@strudel.cycles/xen'),
import('@strudel.cycles/webaudio'),
import('@strudel/codemirror'),
import('@strudel.cycles/serial'),
import('@strudel.cycles/soundfonts'),
import('@strudel.cycles/csound'),
];
if (isTauri()) {
modules.concat([
modules = modules.concat([
import('@strudel/desktopbridge/loggerbridge.mjs'),
import('@strudel/desktopbridge/midibridge.mjs'),
import('@strudel/desktopbridge/oscbridge.mjs'),
import('@strudel/desktopbridge/cyclistbridge.mjs'),
]);
} else {
modules.concat([import('@strudel.cycles/midi'), import('@strudel.cycles/osc')]);
modules = modules.concat([import('@strudel.cycles/midi'), import('@strudel.cycles/osc')]);
}
const modulesLoading = evalScope(
@@ -129,7 +131,7 @@ export function Repl({ embedded = false }) {
} = useSettings();
const paintOptions = useMemo(() => ({ fontFamily }), [fontFamily]);
const { setWidgets } = useWidgets(view);
const { code, setCode, scheduler, evaluate, activateCode, isDirty, activeCode, pattern, started, stop, error } =
useStrudel({
initialCode: '// LOADING...',
@@ -143,6 +145,7 @@ export function Repl({ embedded = false }) {
},
afterEval: ({ code, meta }) => {
setMiniLocations(meta.miniLocations);
setWidgets(meta.widgets);
setPending(false);
setLatestCode(code);
window.location.hash = '#' + code2hash(code);
@@ -150,7 +153,14 @@ export function Repl({ embedded = false }) {
onEvalError: (err) => {
setPending(false);
},
onToggle: (play) => !play && cleanupDraw(false),
onToggle: (play) => {
if (!play) {
cleanupDraw(false);
window.postMessage('strudel-stop');
} else {
window.postMessage('strudel-start');
}
},
drawContext,
// drawTime: [0, 6],
paintOptions,
@@ -213,7 +223,7 @@ export function Repl({ embedded = false }) {
const handleChangeCode = useCallback(
(c) => {
setCode(c);
started && logger('[edit] code changed. hit ctrl+enter to update');
//started && logger('[edit] code changed. hit ctrl+enter to update');
},
[started],
);
+21 -9
View File
@@ -432,7 +432,7 @@ export const waa2 = `// "Waa2"
// @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/
// @by Felix Roos
n(
note(
"a4 [a3 c3] a3 c3"
.sub("<7 12 5 12>".slow(2))
.off(1/4,x=>x.add(7))
@@ -444,7 +444,7 @@ n(
.cutoff(cosine.range(500,4000).slow(16))
.gain(.5)
.room(.5)
`;
.lpa(.125).lpenv(-2).v("8:.125").fanchor(.25)`;
export const hyperpop = `// "Hyperpop"
// @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/
@@ -541,6 +541,7 @@ stack(
.s('sawtooth') // waveform
.gain(.4) // turn down
.cutoff(sine.slow(7).range(300,5000)) // automate cutoff
.lpa(.1).lpenv(-2)
//.hush()
,chord("<Am7!3 <Em7 E7b13 Em7 Ebm7b5>>")
.dict('lefthand').voicing() // chords
@@ -563,6 +564,7 @@ stack(
)
.slow(3/2)`;
/*
export const outroMusic = `// "Outro music"
// @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/
// @by Felix Roos
@@ -593,7 +595,8 @@ chord("<C^7 Am7 Dm7 G7>*2").dict('lefthand').anchor("G4").voicing()
.n(3).color('gray')
).slow(3/2)
//.pianoroll({autorange:1,vertical:1,fold:0})
`;
`;
*/
export const bassFuge = `// "Bass fuge"
// @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/
@@ -776,7 +779,8 @@ stack(
.gain("0.4,0.4(5,8,-1)"),
note("<0 2 5 3>".scale('G1 minor')).struct("x(5,8,-1)")
.s('sawtooth').decay(.1).sustain(0),
.s('sawtooth').decay(.1).sustain(0)
.lpa(.1).lpenv(-4).lpf(800).lpq(8),
note("<G4 A4 Bb4 A4>,Bb3,D3").struct("~ x*2").s('square').clip(1)
.cutoff(sine.range(500,4000).slow(16)).resonance(10)
@@ -807,8 +811,10 @@ stack(
sine.add(saw.slow(4)).range(0,7).segment(8)
.superimpose(x=>x.add(.1))
.scale('G0 minor').note()
.s("sawtooth").decay(.1).sustain(0).lpa(.1).lpenv(4)
.gain(.4).cutoff(perlin.range(300,3000).slow(8)).resonance(10)
.s("sawtooth")
.gain(.4).decay(.1).sustain(0)
.lpa(.1).lpenv(-4).lpq(10)
.cutoff(perlin.range(300,3000).slow(8))
.degradeBy("0 0.1 .5 .1")
.rarely(add(note("12")))
,
@@ -831,8 +837,8 @@ note("c3 eb3 g3 bb3").palindrome()
.s('sawtooth')
.jux(x=>x.rev().color('green').s('sawtooth'))
.off(1/4, x=>x.add(note("<7 12>/2")).slow(2).late(.005).s('triangle'))
//.delay(.5)
.fast(1).cutoff(sine.range(200,2000).slow(8))
.lpf(sine.range(200,2000).slow(8))
.lpa(.2).lpenv(-2)
.decay(.05).sustain(0)
.room(.6)
.delay(.5).delaytime(.1).delayfeedback(.4)
@@ -911,7 +917,13 @@ n("[0,3] 2 [1,3] 2".fast(3).lastOf(4, fast(2))).clip(2)
.delay(.2)
.room(.5).pan(sine.range(.3,.6))
.s('piano')
.stack("<<A1 C2>!2 F2 [F2 E2]>".add.out("0 -5".fast(2)).add("0,.12").note().s('sawtooth').clip(1).cutoff(300))
.stack(
"<<A1 C2>!2 F2 F2>"
.add.out("0 -5".fast(2))
.add("0,.12").note()
.s('sawtooth').cutoff(180)
.lpa(.1).lpenv(2)
)
.slow(4)
.stack(s("bd*4, [~ [hh hh? hh?]]*2,~ [sd ~ [sd:2? bd?]]").bank('RolandTR909').gain(.5).slow(2))
`;