mirror of
https://codeberg.org/uzu/strudel
synced 2026-07-22 21:23:21 -04:00
Compare commits
38 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| dd12b49f4d | |||
| 18d4fe0785 | |||
| 4e38988777 | |||
| 308eda677c | |||
| fdd167d62d | |||
| 85bccdcc19 | |||
| f514cd85b1 | |||
| d53929cf68 | |||
| a4bd0ae100 | |||
| 283a071c86 | |||
| 6350cfdb9c | |||
| b72e71b0bd | |||
| 758b94776d | |||
| e62d5759e7 | |||
| c403a92542 | |||
| 269d6dde31 | |||
| 666c695c63 | |||
| 771eb31d3f | |||
| c05bbf3422 | |||
| 364f511a7c | |||
| 300022d8ac | |||
| 357fbf8c82 | |||
| 1acb675f5a | |||
| 25981092b1 | |||
| e6b55baed0 | |||
| 6adf1ff977 | |||
| b6e277f1ea | |||
| d0510f862a | |||
| 4a87df6426 | |||
| bf90867685 | |||
| 3dc5fd0e7e | |||
| a7650f1af3 | |||
| aadb2fee84 | |||
| bf391ad6ce | |||
| faba4a4e4e | |||
| 94e411aa7a | |||
| 0e6a17fc77 | |||
| 375c68775c |
@@ -1,4 +1,5 @@
|
||||
import { closeBrackets } from '@codemirror/autocomplete';
|
||||
export { toggleComment, toggleBlockComment, toggleLineComment, toggleBlockCommentByLine } from '@codemirror/commands';
|
||||
// import { search, highlightSelectionMatches } from '@codemirror/search';
|
||||
import { history } from '@codemirror/commands';
|
||||
import { javascript } from '@codemirror/lang-javascript';
|
||||
@@ -140,27 +141,25 @@ export class StrudelMirror {
|
||||
this.root = root;
|
||||
this.miniLocations = [];
|
||||
this.widgets = [];
|
||||
this.painters = [];
|
||||
this.drawTime = drawTime;
|
||||
this.drawContext = drawContext;
|
||||
this.onDraw = onDraw || this.draw;
|
||||
this.id = id || s4();
|
||||
|
||||
this.drawer = new Drawer((haps, time) => {
|
||||
this.drawer = new Drawer((haps, time, _, painters) => {
|
||||
const currentFrame = haps.filter((hap) => hap.isActive(time));
|
||||
this.highlight(currentFrame, time);
|
||||
this.onDraw(haps, time, this.painters);
|
||||
this.onDraw(haps, time, painters);
|
||||
}, drawTime);
|
||||
|
||||
this.prebaked = prebake();
|
||||
autodraw && this.drawFirstFrame();
|
||||
|
||||
this.repl = repl({
|
||||
...replOptions,
|
||||
id,
|
||||
onToggle: (started) => {
|
||||
replOptions?.onToggle?.(started);
|
||||
if (started) {
|
||||
this.adjustDrawTime();
|
||||
this.drawer.start(this.repl.scheduler);
|
||||
// stop other repls when this one is started
|
||||
document.dispatchEvent(
|
||||
@@ -171,20 +170,11 @@ export class StrudelMirror {
|
||||
} else {
|
||||
this.drawer.stop();
|
||||
updateMiniLocations(this.editor, []);
|
||||
cleanupDraw(false);
|
||||
cleanupDraw(true, id);
|
||||
}
|
||||
},
|
||||
beforeEval: async () => {
|
||||
cleanupDraw();
|
||||
this.painters = [];
|
||||
const self = this;
|
||||
// this is similar to repl.mjs > injectPatternMethods
|
||||
// maybe there is a solution without prototype hacking, but hey, it works
|
||||
// we need to do this befor every eval to make sure it works with multiple StrudelMirror's side by side
|
||||
Pattern.prototype.onPaint = function (onPaint) {
|
||||
self.painters.push(onPaint);
|
||||
return this;
|
||||
};
|
||||
cleanupDraw(true, id);
|
||||
await this.prebaked;
|
||||
await replOptions?.beforeEval?.();
|
||||
},
|
||||
@@ -198,8 +188,11 @@ export class StrudelMirror {
|
||||
updateWidgets(this.editor, widgets);
|
||||
updateMiniLocations(this.editor, this.miniLocations);
|
||||
replOptions?.afterEval?.(options);
|
||||
this.adjustDrawTime();
|
||||
this.drawer.invalidate();
|
||||
// if no painters are set (.onPaint was not called), then we only need the present moment (for highlighting)
|
||||
const drawTime = options.pattern.getPainters().length ? this.drawTime : [0, 0];
|
||||
this.drawer.setDrawTime(drawTime);
|
||||
// invalidate drawer after we've set the appropriate drawTime
|
||||
this.drawer.invalidate(this.repl.scheduler);
|
||||
},
|
||||
});
|
||||
this.editor = initEditor({
|
||||
@@ -234,13 +227,8 @@ export class StrudelMirror {
|
||||
};
|
||||
document.addEventListener('start-repl', this.onStartRepl);
|
||||
}
|
||||
// adjusts draw time depending on if there are painters
|
||||
adjustDrawTime() {
|
||||
// when no painters are set, [0,0] is enough (just highlighting)
|
||||
this.drawer.setDrawTime(this.painters.length ? this.drawTime : [0, 0]);
|
||||
}
|
||||
draw(haps, time) {
|
||||
this.painters?.forEach((painter) => painter(this.drawContext, time, haps, this.drawTime));
|
||||
draw(haps, time, painters) {
|
||||
painters?.forEach((painter) => painter(this.drawContext, time, haps, this.drawTime));
|
||||
}
|
||||
async drawFirstFrame() {
|
||||
if (!this.onDraw) {
|
||||
@@ -252,7 +240,7 @@ export class StrudelMirror {
|
||||
await this.repl.evaluate(this.code, false);
|
||||
this.drawer.invalidate(this.repl.scheduler, -0.001);
|
||||
// draw at -0.001 to avoid haps at 0 to be visualized as active
|
||||
this.onDraw?.(this.drawer.visibleHaps, -0.001, this.painters);
|
||||
this.onDraw?.(this.drawer.visibleHaps, -0.001, this.drawer.painters);
|
||||
} catch (err) {
|
||||
console.warn('first frame could not be painted');
|
||||
}
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ export const settings = {
|
||||
gutterBackground: 'transparent',
|
||||
gutterForeground: '#0f380f',
|
||||
light: true,
|
||||
customStyle: '.cm-line { line-height: 1 }',
|
||||
// customStyle: '.cm-line { line-height: 1 }',
|
||||
};
|
||||
export default createTheme({
|
||||
theme: 'light',
|
||||
|
||||
+1
-1
@@ -17,7 +17,7 @@ export const settings = {
|
||||
lineBackground: '#00000040',
|
||||
gutterBackground: 'transparent',
|
||||
gutterForeground: '#8a919966',
|
||||
customStyle: '.cm-line { line-height: 1 }',
|
||||
// customStyle: '.cm-line { line-height: 1 }',
|
||||
};
|
||||
|
||||
let punctuation = colorD;
|
||||
|
||||
@@ -191,7 +191,7 @@ export const { attack, att } = registerControl('attack', 'att');
|
||||
* note("c e g b g e")
|
||||
* .fm(4)
|
||||
* .fmh("<1 2 1.5 1.61>")
|
||||
* .scope()
|
||||
* ._scope()
|
||||
*
|
||||
*/
|
||||
export const { fmh } = registerControl(['fmh', 'fmi'], 'fmh');
|
||||
@@ -205,7 +205,7 @@ export const { fmh } = registerControl(['fmh', 'fmi'], 'fmh');
|
||||
* @example
|
||||
* note("c e g b g e")
|
||||
* .fm("<0 1 2 8 32>")
|
||||
* .scope()
|
||||
* ._scope()
|
||||
*
|
||||
*/
|
||||
export const { fmi, fm } = registerControl(['fmi', 'fmh'], 'fm');
|
||||
@@ -221,7 +221,7 @@ export const { fmi, fm } = registerControl(['fmi', 'fmh'], 'fm');
|
||||
* .fmdecay(.2)
|
||||
* .fmsustain(0)
|
||||
* .fmenv("<exp lin>")
|
||||
* .scope()
|
||||
* ._scope()
|
||||
*
|
||||
*/
|
||||
export const { fmenv } = registerControl('fmenv');
|
||||
@@ -234,7 +234,7 @@ export const { fmenv } = registerControl('fmenv');
|
||||
* note("c e g b g e")
|
||||
* .fm(4)
|
||||
* .fmattack("<0 .05 .1 .2>")
|
||||
* .scope()
|
||||
* ._scope()
|
||||
*
|
||||
*/
|
||||
export const { fmattack } = registerControl('fmattack');
|
||||
@@ -248,7 +248,7 @@ export const { fmattack } = registerControl('fmattack');
|
||||
* .fm(4)
|
||||
* .fmdecay("<.01 .05 .1 .2>")
|
||||
* .fmsustain(.4)
|
||||
* .scope()
|
||||
* ._scope()
|
||||
*
|
||||
*/
|
||||
export const { fmdecay } = registerControl('fmdecay');
|
||||
@@ -262,7 +262,7 @@ export const { fmdecay } = registerControl('fmdecay');
|
||||
* .fm(4)
|
||||
* .fmdecay(.1)
|
||||
* .fmsustain("<1 .75 .5 0>")
|
||||
* .scope()
|
||||
* ._scope()
|
||||
*
|
||||
*/
|
||||
export const { fmsustain } = registerControl('fmsustain');
|
||||
@@ -392,7 +392,7 @@ export const { loop } = registerControl('loop');
|
||||
* @synonyms loopb
|
||||
* @example
|
||||
* s("space").loop(1)
|
||||
* .loopBegin("<0 .125 .25>").scope()
|
||||
* .loopBegin("<0 .125 .25>")._scope()
|
||||
*/
|
||||
export const { loopBegin, loopb } = registerControl('loopBegin', 'loopb');
|
||||
/**
|
||||
@@ -405,7 +405,7 @@ export const { loopBegin, loopb } = registerControl('loopBegin', 'loopb');
|
||||
* @synonyms loope
|
||||
* @example
|
||||
* s("space").loop(1)
|
||||
* .loopEnd("<1 .75 .5 .25>").scope()
|
||||
* .loopEnd("<1 .75 .5 .25>")._scope()
|
||||
*/
|
||||
export const { loopEnd, loope } = registerControl('loopEnd', 'loope');
|
||||
/**
|
||||
@@ -800,10 +800,12 @@ export const { fanchor } = registerControl('fanchor');
|
||||
* @example
|
||||
* note("a e")
|
||||
* .vib("<.5 1 2 4 8 16>")
|
||||
* ._scope()
|
||||
* @example
|
||||
* // change the modulation depth with ":"
|
||||
* note("a e")
|
||||
* .vib("<.5 1 2 4 8 16>:12")
|
||||
* ._scope()
|
||||
*/
|
||||
export const { vib, vibrato, v } = registerControl(['vib', 'vibmod'], 'vibrato', 'v');
|
||||
/**
|
||||
@@ -824,10 +826,12 @@ export const { noise } = registerControl('noise');
|
||||
* @example
|
||||
* note("a e").vib(4)
|
||||
* .vibmod("<.25 .5 1 2 12>")
|
||||
* ._scope()
|
||||
* @example
|
||||
* // change the vibrato frequency with ":"
|
||||
* note("a e")
|
||||
* .vibmod("<.25 .5 1 2 12>:8")
|
||||
* ._scope()
|
||||
*/
|
||||
export const { vibmod, vmod } = registerControl(['vibmod', 'vib'], 'vmod');
|
||||
export const { hcutoff, hpf, hp } = registerControl(['hcutoff', 'hresonance', 'hpenv'], 'hpf', 'hp');
|
||||
@@ -1521,7 +1525,7 @@ export const { cps } = registerControl('cps');
|
||||
* note("c a f e").s("piano").clip("<.5 1 2>")
|
||||
*
|
||||
*/
|
||||
export const { clip, legato } = registerControl('clip', 'legato');
|
||||
export const { clip } = registerControl('clip');
|
||||
|
||||
/**
|
||||
* Sets the duration of the event in cycles. Similar to clip / legato, it also cuts samples off at the end if they exceed the duration.
|
||||
|
||||
@@ -8,8 +8,19 @@ import createClock from './zyklus.mjs';
|
||||
import { logger } from './logger.mjs';
|
||||
|
||||
export class Cyclist {
|
||||
constructor({ interval, onTrigger, onToggle, onError, getTime, latency = 0.1, setInterval, clearInterval }) {
|
||||
constructor({
|
||||
interval,
|
||||
onTrigger,
|
||||
onToggle,
|
||||
onError,
|
||||
getTime,
|
||||
latency = 0.1,
|
||||
setInterval,
|
||||
clearInterval,
|
||||
beforeStart,
|
||||
}) {
|
||||
this.started = false;
|
||||
this.beforeStart = beforeStart;
|
||||
this.cps = 0.5;
|
||||
this.num_ticks_since_cps_change = 0;
|
||||
this.lastTick = 0; // absolute time when last tick (clock callback) happened
|
||||
@@ -82,7 +93,8 @@ export class Cyclist {
|
||||
this.started = v;
|
||||
this.onToggle?.(v);
|
||||
}
|
||||
start() {
|
||||
async start() {
|
||||
await this.beforeStart?.();
|
||||
this.num_ticks_since_cps_change = 0;
|
||||
this.num_cycles_at_cps_change = 0;
|
||||
if (!this.pattern) {
|
||||
@@ -103,10 +115,10 @@ export class Cyclist {
|
||||
this.lastEnd = 0;
|
||||
this.setStarted(false);
|
||||
}
|
||||
setPattern(pat, autostart = false) {
|
||||
async setPattern(pat, autostart = false) {
|
||||
this.pattern = pat;
|
||||
if (autostart && !this.started) {
|
||||
this.start();
|
||||
await this.start();
|
||||
}
|
||||
}
|
||||
setCps(cps = 0.5) {
|
||||
|
||||
@@ -37,11 +37,11 @@ function safeEval(str, options = {}) {
|
||||
return Function(body)();
|
||||
}
|
||||
|
||||
export const evaluate = async (code, transpiler) => {
|
||||
export const evaluate = async (code, transpiler, transpilerOptions) => {
|
||||
let meta = {};
|
||||
if (transpiler) {
|
||||
// transform syntactically correct js code to semantically usable code
|
||||
const transpiled = transpiler(code);
|
||||
const transpiled = transpiler(code, transpilerOptions);
|
||||
code = transpiled.output;
|
||||
meta = transpiled;
|
||||
}
|
||||
|
||||
@@ -119,10 +119,10 @@ export const lcm = (...fractions) => {
|
||||
if (fractions.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const x = fractions.pop();
|
||||
return fractions.reduce(
|
||||
(lcm, fraction) => (lcm === undefined || fraction === undefined ? undefined : lcm.lcm(fraction)),
|
||||
fraction(1),
|
||||
x,
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -94,6 +94,14 @@ export class Pattern {
|
||||
return result;
|
||||
}
|
||||
|
||||
// runs func on query state
|
||||
withState(func) {
|
||||
return this.withHaps((haps, state) => {
|
||||
func(state);
|
||||
return haps;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* see `withValue`
|
||||
* @noAutocomplete
|
||||
@@ -1721,6 +1729,75 @@ export const { compressSpan, compressspan } = register(['compressSpan', 'compres
|
||||
return pat._compress(span.begin, span.end);
|
||||
});
|
||||
|
||||
/**
|
||||
* fills in the gap between consecutive notes
|
||||
*
|
||||
* @name fill
|
||||
* @memberof Pattern
|
||||
* @param {Pattern | number | array} fill relative note length
|
||||
* @returns Pattern
|
||||
* @example
|
||||
* s("sawtooth").euclid(11,16).fill("<1 .5>")._pianoroll()
|
||||
* @example
|
||||
* // second array parameter is "lookahead" which is the number of cycles in the future to query for the next event
|
||||
* s("supersaw").euclid(7,16).fill("1:<1 .15>")._pianoroll()
|
||||
*/
|
||||
export const fill = register('fill', function (legato, pat) {
|
||||
let multiplier = legato;
|
||||
let lookahead = 1;
|
||||
if (Array.isArray(legato)) {
|
||||
multiplier = legato[0] ?? 1;
|
||||
lookahead = legato[1] ?? lookahead;
|
||||
}
|
||||
|
||||
let spanEnd;
|
||||
let spanBegin;
|
||||
return pat
|
||||
.withQuerySpan((span) => {
|
||||
spanEnd = span.end;
|
||||
spanBegin = span.begin;
|
||||
|
||||
return span.withEnd((e) => {
|
||||
return e.add(lookahead);
|
||||
})
|
||||
// .withBegin(e => e.sub(lookahead));
|
||||
})
|
||||
.withHaps((haps) => {
|
||||
const newHaps = [];
|
||||
haps
|
||||
.sort((a, b) => {
|
||||
return a.part.begin.sub(b.part.begin).valueOf();
|
||||
})
|
||||
.forEach((hap, i) => {
|
||||
const nextHap = haps[i + 1];
|
||||
const partbegin = hap.part.begin;
|
||||
let partend = hap.part.end;
|
||||
const wholebegin = hap.whole.begin;
|
||||
// filter out haps that were not part of the original span
|
||||
if (wholebegin.gte(spanEnd) || wholebegin.lt(spanBegin)) {
|
||||
return;
|
||||
}
|
||||
let wholeend = hap.whole.end;
|
||||
if (nextHap != null) {
|
||||
partend = nextHap.part.begin;
|
||||
wholeend = nextHap.whole.begin;
|
||||
}
|
||||
|
||||
let wholeduration = wholeend.sub(wholebegin).mul(multiplier);
|
||||
let partduration = partend.sub(partbegin).mul(multiplier);
|
||||
|
||||
partend = partbegin.add(partduration);
|
||||
wholeend = wholebegin.add(wholeduration);
|
||||
|
||||
const newPart = new TimeSpan(partbegin, partend);
|
||||
const newWhole = new TimeSpan(wholebegin, wholeend);
|
||||
|
||||
newHaps.push(new Hap(newWhole, newPart, hap.value, hap.context));
|
||||
});
|
||||
return newHaps;
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* speeds up a pattern like fast, but rather than it playing multiple times as fast would it instead leaves a gap in the remaining space of the cycle. For example, the following will play the sound pattern "bd sn" only once but compressed into the first half of the cycle, i.e. twice as fast.
|
||||
* @name fastGap
|
||||
@@ -2523,7 +2600,7 @@ export function _polymeterListSteps(steps, ...args) {
|
||||
* @param {any[]} patterns one or more patterns
|
||||
* @example
|
||||
* // the same as "{c d, e f g}%4"
|
||||
* s_polymeterSteps(4, "c d", "e f g")
|
||||
* s_polymeterSteps(4, "c d", "e f g").note()
|
||||
*/
|
||||
export function s_polymeterSteps(steps, ...args) {
|
||||
if (args.length == 0) {
|
||||
@@ -2541,8 +2618,8 @@ export function s_polymeterSteps(steps, ...args) {
|
||||
* *EXPERIMENTAL* - Combines the given lists of patterns with the same pulse, creating polymeters when different sized sequences are used.
|
||||
* @synonyms pm
|
||||
* @example
|
||||
* // The same as "{c eb g, c2 g2}"
|
||||
* s_polymeter("c eb g", "c2 g2")
|
||||
* // The same as note("{c eb g, c2 g2}")
|
||||
* s_polymeter("c eb g", "c2 g2").note()
|
||||
*
|
||||
*/
|
||||
export function s_polymeter(...args) {
|
||||
@@ -2571,6 +2648,8 @@ export function s_polymeter(...args) {
|
||||
/** Sequences patterns like `seq`, but each pattern has a length, relative to the whole.
|
||||
* This length can either be provided as a [length, pattern] pair, or inferred from
|
||||
* the pattern's 'tactus', generally inferred by the mininotation. Has the alias `timecat`.
|
||||
* @name s_cat
|
||||
* @synonyms timeCat, timecat
|
||||
* @return {Pattern}
|
||||
* @example
|
||||
* s_cat([3,"e3"],[1, "g3"]).note()
|
||||
@@ -2794,8 +2873,16 @@ export const s_tour = function (pat, ...many) {
|
||||
export const chop = register('chop', function (n, pat) {
|
||||
const slices = Array.from({ length: n }, (x, i) => i);
|
||||
const slice_objects = slices.map((i) => ({ begin: i / n, end: (i + 1) / n }));
|
||||
const merge = function (a, b) {
|
||||
if ('begin' in a && 'end' in a && a.begin !== undefined && a.end !== undefined) {
|
||||
const d = a.end - a.begin;
|
||||
b = { begin: a.begin + b.begin * d, end: a.begin + b.end * d };
|
||||
}
|
||||
// return a;
|
||||
return Object.assign({}, a, b);
|
||||
};
|
||||
const func = function (o) {
|
||||
return sequence(slice_objects.map((slice_o) => Object.assign({}, o, slice_o)));
|
||||
return sequence(slice_objects.map((slice_o) => merge(o, slice_o)));
|
||||
};
|
||||
return pat.squeezeBind(func).setTactus(__tactus ? Fraction(n).mulmaybe(pat.tactus) : undefined);
|
||||
});
|
||||
|
||||
+13
-4
@@ -10,6 +10,7 @@ export function repl({
|
||||
defaultOutput,
|
||||
onEvalError,
|
||||
beforeEval,
|
||||
beforeStart,
|
||||
afterEval,
|
||||
getTime,
|
||||
transpiler,
|
||||
@@ -19,6 +20,7 @@ export function repl({
|
||||
sync = false,
|
||||
setInterval,
|
||||
clearInterval,
|
||||
id,
|
||||
}) {
|
||||
const state = {
|
||||
schedulerError: undefined,
|
||||
@@ -32,6 +34,10 @@ export function repl({
|
||||
started: false,
|
||||
};
|
||||
|
||||
const transpilerOptions = {
|
||||
id,
|
||||
};
|
||||
|
||||
const updateState = (update) => {
|
||||
Object.assign(state, update);
|
||||
state.isDirty = state.code !== state.activeCode;
|
||||
@@ -48,6 +54,7 @@ export function repl({
|
||||
},
|
||||
setInterval,
|
||||
clearInterval,
|
||||
beforeStart,
|
||||
};
|
||||
|
||||
// NeoCyclist uses a shared worker to communicate between instances, which is not supported on mobile chrome
|
||||
@@ -64,9 +71,10 @@ export function repl({
|
||||
return silence;
|
||||
};
|
||||
|
||||
const setPattern = (pattern, autostart = true) => {
|
||||
const setPattern = async (pattern, autostart = true) => {
|
||||
pattern = editPattern?.(pattern) || pattern;
|
||||
scheduler.setPattern(pattern, autostart);
|
||||
await scheduler.setPattern(pattern, autostart);
|
||||
return pattern;
|
||||
};
|
||||
setTime(() => scheduler.now()); // TODO: refactor?
|
||||
|
||||
@@ -139,9 +147,10 @@ export function repl({
|
||||
try {
|
||||
updateState({ code, pending: true });
|
||||
await injectPatternMethods();
|
||||
setTime(() => scheduler.now()); // TODO: refactor?
|
||||
await beforeEval?.({ code });
|
||||
shouldHush && hush();
|
||||
let { pattern, meta } = await _evaluate(code, transpiler);
|
||||
let { pattern, meta } = await _evaluate(code, transpiler, transpilerOptions);
|
||||
if (Object.keys(pPatterns).length) {
|
||||
pattern = stack(...Object.values(pPatterns));
|
||||
}
|
||||
@@ -153,7 +162,7 @@ export function repl({
|
||||
throw new Error(message + (typeof evaluated === 'function' ? ', did you forget to call a function?' : '.'));
|
||||
}
|
||||
logger(`[eval] code updated`);
|
||||
setPattern(pattern, autostart);
|
||||
pattern = await setPattern(pattern, autostart);
|
||||
updateState({
|
||||
miniLocations: meta?.miniLocations || [],
|
||||
widgets: meta?.widgets || [],
|
||||
|
||||
@@ -937,6 +937,9 @@ describe('Pattern', () => {
|
||||
.firstCycle(),
|
||||
);
|
||||
});
|
||||
it('Can chop chops', () => {
|
||||
expect(pure({ s: 'bev' }).chop(2).chop(2).firstCycle()).toStrictEqual(pure({ s: 'bev' }).chop(4).firstCycle());
|
||||
});
|
||||
});
|
||||
describe('range', () => {
|
||||
it('Can be patterned', () => {
|
||||
@@ -1209,4 +1212,40 @@ describe('Pattern', () => {
|
||||
}
|
||||
});
|
||||
});
|
||||
describe('s_expand', () => {
|
||||
it('can expand four things in half', () => {
|
||||
expect(
|
||||
sameFirst(
|
||||
sequence(0, 1, 2, 3).s_expand(1, 0.5),
|
||||
s_cat(sequence(0, 1, 2, 3), sequence(0, 1, 2, 3).s_expand(0.5)),
|
||||
),
|
||||
);
|
||||
});
|
||||
it('can expand five things in half', () => {
|
||||
expect(
|
||||
sameFirst(
|
||||
sequence(0, 1, 2, 3, 4).s_expand(1, 0.5),
|
||||
s_cat(sequence(0, 1, 2, 3, 4), sequence(0, 1, 2, 3, 4).s_expand(0.5)),
|
||||
),
|
||||
);
|
||||
});
|
||||
});
|
||||
describe('stepJoin', () => {
|
||||
it('can join a pattern with a tactus of 2', () => {
|
||||
expect(
|
||||
sameFirst(
|
||||
sequence(pure(pure('a')), pure(pure('b').setTactus(2))).stepJoin(),
|
||||
s_cat(pure('a'), pure('b').setTactus(2)),
|
||||
),
|
||||
);
|
||||
});
|
||||
it('can join a pattern with a tactus of 0.5', () => {
|
||||
expect(
|
||||
sameFirst(
|
||||
sequence(pure(pure('a')), pure(pure('b').setTactus(0.5))).stepJoin(),
|
||||
s_cat(pure('a'), pure('b').setTactus(0.5)),
|
||||
),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -61,6 +61,14 @@ export class TimeSpan {
|
||||
// Applies given function to the end time of the timespan"""
|
||||
return new TimeSpan(this.begin, func_time(this.end));
|
||||
}
|
||||
withBegin(func_time) {
|
||||
// Applies given function to the end time of the timespan"""
|
||||
return new TimeSpan(func_time(this.begin), this.end);
|
||||
}
|
||||
withDuration(func_time) {
|
||||
const duration = this.end.sub(this.begin)
|
||||
return new TimeSpan(this.begin, this.begin.add(func_time(duration)))
|
||||
}
|
||||
|
||||
withCycle(func_time) {
|
||||
// Like withTime, but time is relative to relative to the cycle (i.e. the
|
||||
|
||||
@@ -4,6 +4,7 @@ import { resolve } from 'path';
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
base: './',
|
||||
plugins: [],
|
||||
build: {
|
||||
lib: {
|
||||
|
||||
@@ -53,7 +53,7 @@ Pattern.prototype.animate = function ({ callback, sync = false, smear = 0.5 } =
|
||||
return silence;
|
||||
};
|
||||
|
||||
export const { x, y, w, h, angle, r, fill, smear } = createParams('x', 'y', 'w', 'h', 'angle', 'r', 'fill', 'smear');
|
||||
export const { x, y, w, h, angle, r, fill, smear } = createParams('x', 'y', 'w', 'h', 'angle', 'r', 'smear');
|
||||
|
||||
export const rescale = register('rescale', function (f, pat) {
|
||||
return pat.mul(x(f).w(f).y(f).h(f));
|
||||
|
||||
+23
-13
@@ -36,8 +36,8 @@ function stopAnimationFrame(id) {
|
||||
delete animationFrames[id];
|
||||
}
|
||||
}
|
||||
function stopAllAnimations() {
|
||||
Object.keys(animationFrames).forEach((id) => stopAnimationFrame(id));
|
||||
function stopAllAnimations(replID) {
|
||||
Object.keys(animationFrames).forEach((id) => (!replID || id.startsWith(replID)) && stopAnimationFrame(id));
|
||||
}
|
||||
|
||||
let memory = {};
|
||||
@@ -72,18 +72,25 @@ Pattern.prototype.draw = function (fn, options) {
|
||||
return this;
|
||||
};
|
||||
|
||||
export const cleanupDraw = (clearScreen = true) => {
|
||||
export const cleanupDraw = (clearScreen = true, id) => {
|
||||
const ctx = getDrawContext();
|
||||
clearScreen && ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.width);
|
||||
stopAllAnimations();
|
||||
if (window.strudelScheduler) {
|
||||
clearInterval(window.strudelScheduler);
|
||||
}
|
||||
clearScreen && ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
|
||||
stopAllAnimations(id);
|
||||
};
|
||||
|
||||
Pattern.prototype.onPaint = function () {
|
||||
console.warn('[draw] onPaint was not overloaded. Some drawings might not work');
|
||||
return this;
|
||||
Pattern.prototype.onPaint = function (painter) {
|
||||
return this.withState((state) => {
|
||||
if (!state.controls.painters) {
|
||||
state.controls.painters = [];
|
||||
}
|
||||
state.controls.painters.push(painter);
|
||||
});
|
||||
};
|
||||
|
||||
Pattern.prototype.getPainters = function () {
|
||||
let painters = [];
|
||||
this.queryArc(0, 0, { painters });
|
||||
return painters;
|
||||
};
|
||||
|
||||
// const round = (x) => Math.round(x * 1000) / 1000;
|
||||
@@ -122,6 +129,7 @@ export class Drawer {
|
||||
this.visibleHaps = [];
|
||||
this.lastFrame = null;
|
||||
this.drawTime = drawTime;
|
||||
this.painters = [];
|
||||
this.framer = new Framer(
|
||||
() => {
|
||||
if (!this.scheduler) {
|
||||
@@ -146,7 +154,7 @@ export class Drawer {
|
||||
// add new haps with onset (think right edge bars scrolling in)
|
||||
.concat(haps.filter((h) => h.hasOnset()));
|
||||
const time = phase - lookahead;
|
||||
onDraw(this.visibleHaps, time, this);
|
||||
onDraw(this.visibleHaps, time, this, this.painters);
|
||||
},
|
||||
(err) => {
|
||||
console.warn('draw error', err);
|
||||
@@ -164,11 +172,13 @@ export class Drawer {
|
||||
t = t ?? scheduler.now();
|
||||
this.scheduler = scheduler;
|
||||
let [_, lookahead] = this.drawTime;
|
||||
// +0.1 = workaround for weird holes in query..
|
||||
const [begin, end] = [Math.max(t, 0), t + lookahead + 0.1];
|
||||
// remove all future haps
|
||||
this.visibleHaps = this.visibleHaps.filter((h) => h.whole.begin < t);
|
||||
this.painters = []; // will get populated by .onPaint calls attached to the pattern
|
||||
// query future haps
|
||||
const futureHaps = scheduler.pattern.queryArc(begin, end); // +0.1 = workaround for weird holes in query..
|
||||
const futureHaps = scheduler.pattern.queryArc(begin, end, { painters: this.painters });
|
||||
// append future haps
|
||||
this.visibleHaps = this.visibleHaps.concat(futureHaps);
|
||||
}
|
||||
|
||||
@@ -67,6 +67,7 @@ Pattern.prototype.pianoroll = function (options = {}) {
|
||||
* Displays a midi-style piano roll
|
||||
*
|
||||
* @name pianoroll
|
||||
* @synonyms punchcard
|
||||
* @param {Object} options Object containing all the optional following parameters as key value pairs:
|
||||
* @param {integer} cycles number of cycles to be displayed at the same time - defaults to 4
|
||||
* @param {number} playhead location of the active notes on the time axis - 0 to 1, defaults to 0.5
|
||||
@@ -94,7 +95,11 @@ Pattern.prototype.pianoroll = function (options = {}) {
|
||||
* @param {boolean} autorange automatically calculate the minMidi and maxMidi parameters - 0 by default
|
||||
*
|
||||
* @example
|
||||
* note("C2 A2 G2").euclid(5,8).s('piano').clip(1).color('salmon').pianoroll({vertical:1, labels:1})
|
||||
* note("c2 a2 eb2")
|
||||
* .euclid(5,8)
|
||||
* .s('sawtooth')
|
||||
* .lpenv(4).lpf(300)
|
||||
* ._pianoroll({ labels: 1 })
|
||||
*/
|
||||
export function pianoroll({
|
||||
time,
|
||||
@@ -113,7 +118,7 @@ export function pianoroll({
|
||||
maxMidi = 90,
|
||||
autorange = 0,
|
||||
timeframe: timeframeProp,
|
||||
fold = 0,
|
||||
fold = 1,
|
||||
vertical = 0,
|
||||
labels = false,
|
||||
fill = 1,
|
||||
@@ -266,7 +271,7 @@ export function getDrawOptions(drawTime, options = {}) {
|
||||
let [lookbehind, lookahead] = drawTime;
|
||||
lookbehind = Math.abs(lookbehind);
|
||||
const cycles = lookahead + lookbehind;
|
||||
const playhead = lookbehind / cycles;
|
||||
const playhead = cycles !== 0 ? lookbehind / cycles : 0;
|
||||
return { fold: 1, ...options, cycles, playhead };
|
||||
}
|
||||
|
||||
|
||||
@@ -113,6 +113,23 @@ export function pitchwheel({
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a pitch circle to visualize frequencies within one octave
|
||||
* @name pitchwheel
|
||||
* @param {number} hapcircles
|
||||
* @param {number} circle
|
||||
* @param {number} edo
|
||||
* @param {string} root
|
||||
* @param {number} thickness
|
||||
* @param {number} hapRadius
|
||||
* @param {string} mode
|
||||
* @param {number} margin
|
||||
* @example
|
||||
* n("0 .. 12").scale("C:chromatic")
|
||||
* .s("sawtooth")
|
||||
* .lpf(500)
|
||||
* ._pitchwheel()
|
||||
*/
|
||||
Pattern.prototype.pitchwheel = function (options = {}) {
|
||||
let { ctx = getDrawContext(), id = 1 } = options;
|
||||
return this.tag(id).onPaint((_, time, haps) =>
|
||||
|
||||
@@ -125,6 +125,33 @@ function drawSpiral(options) {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a spiral visual.
|
||||
*
|
||||
* @name spiral
|
||||
* @param {Object} options Object containing all the optional following parameters as key value pairs:
|
||||
* @param {number} stretch controls the rotations per cycle ratio, where 1 = 1 cycle / 360 degrees
|
||||
* @param {number} size the diameter of the spiral
|
||||
* @param {number} thickness line thickness
|
||||
* @param {string} cap style of line ends: butt (default), round, square
|
||||
* @param {string} inset number of rotations before spiral starts (default 3)
|
||||
* @param {string} playheadColor color of playhead, defaults to white
|
||||
* @param {number} playheadLength length of playhead in rotations, defaults to 0.02
|
||||
* @param {number} playheadThickness thickness of playheadrotations, defaults to thickness
|
||||
* @param {number} padding space around spiral
|
||||
* @param {number} steady steadyness of spiral vs playhead. 1 = spiral doesn't move, playhead does.
|
||||
* @param {number} activeColor color of active segment. defaults to foreground of theme
|
||||
* @param {number} inactiveColor color of inactive segments. defaults to gutterForeground of theme
|
||||
* @param {boolean} colorizeInactive wether or not to colorize inactive segments, defaults to 0
|
||||
* @param {boolean} fade wether or not past and future should fade out. defaults to 1
|
||||
* @param {boolean} logSpiral wether or not the spiral should be logarithmic. defaults to 0
|
||||
* @example
|
||||
* note("c2 a2 eb2")
|
||||
* .euclid(5,8)
|
||||
* .s('sawtooth')
|
||||
* .lpenv(4).lpf(300)
|
||||
* ._spiral({ steady: .96 })
|
||||
*/
|
||||
Pattern.prototype.spiral = function (options = {}) {
|
||||
return this.onPaint((ctx, time, haps, drawTime) => drawSpiral({ ctx, time, haps, drawTime, ...options }));
|
||||
};
|
||||
|
||||
@@ -6,6 +6,7 @@ import replace from '@rollup/plugin-replace';
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
base: './',
|
||||
plugins: [],
|
||||
build: {
|
||||
lib: {
|
||||
|
||||
@@ -69,6 +69,7 @@ export function transpiler(input, options = {}) {
|
||||
to: node.end,
|
||||
index,
|
||||
type,
|
||||
id: options.id,
|
||||
};
|
||||
emitWidgets && widgets.push(widgetConfig);
|
||||
return this.replace(widgetWithLocation(node, widgetConfig));
|
||||
@@ -162,7 +163,7 @@ export function getWidgetID(widgetConfig) {
|
||||
// that means, if we use the index index of line position as id, less garbage is generated
|
||||
// return `widget_${widgetConfig.to}`; // more gargabe
|
||||
//return `widget_${widgetConfig.index}_${widgetConfig.to}`; // also more garbage
|
||||
return `widget_${widgetConfig.type}_${widgetConfig.index}`; // less garbage
|
||||
return `${widgetConfig.id || ''}_widget_${widgetConfig.type}_${widgetConfig.index}`; // less garbage
|
||||
}
|
||||
|
||||
function widgetWithLocation(node, widgetConfig) {
|
||||
|
||||
@@ -5,6 +5,7 @@ import replace from '@rollup/plugin-replace';
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
base: './',
|
||||
plugins: [],
|
||||
build: {
|
||||
lib: {
|
||||
|
||||
@@ -130,7 +130,7 @@ Pattern.prototype.fscope = function (config = {}) {
|
||||
* @param {number} pos y-position relative to screen height. 0 = top, 1 = bottom of screen
|
||||
* @param {number} trigger amplitude value that is used to align the scope. defaults to 0.
|
||||
* @example
|
||||
* s("sawtooth").scope()
|
||||
* s("sawtooth")._scope()
|
||||
*/
|
||||
let latestColor = {};
|
||||
Pattern.prototype.tscope = function (config = {}) {
|
||||
|
||||
@@ -2717,175 +2717,175 @@ exports[`runs examples > example "floor" example index 0 1`] = `
|
||||
|
||||
exports[`runs examples > example "fm" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/6 | note:c fmi:0 analyze:1 ]",
|
||||
"[ 1/6 → 1/3 | note:e fmi:0 analyze:1 ]",
|
||||
"[ 1/3 → 1/2 | note:g fmi:0 analyze:1 ]",
|
||||
"[ 1/2 → 2/3 | note:b fmi:0 analyze:1 ]",
|
||||
"[ 2/3 → 5/6 | note:g fmi:0 analyze:1 ]",
|
||||
"[ 5/6 → 1/1 | note:e fmi:0 analyze:1 ]",
|
||||
"[ 1/1 → 7/6 | note:c fmi:1 analyze:1 ]",
|
||||
"[ 7/6 → 4/3 | note:e fmi:1 analyze:1 ]",
|
||||
"[ 4/3 → 3/2 | note:g fmi:1 analyze:1 ]",
|
||||
"[ 3/2 → 5/3 | note:b fmi:1 analyze:1 ]",
|
||||
"[ 5/3 → 11/6 | note:g fmi:1 analyze:1 ]",
|
||||
"[ 11/6 → 2/1 | note:e fmi:1 analyze:1 ]",
|
||||
"[ 2/1 → 13/6 | note:c fmi:2 analyze:1 ]",
|
||||
"[ 13/6 → 7/3 | note:e fmi:2 analyze:1 ]",
|
||||
"[ 7/3 → 5/2 | note:g fmi:2 analyze:1 ]",
|
||||
"[ 5/2 → 8/3 | note:b fmi:2 analyze:1 ]",
|
||||
"[ 8/3 → 17/6 | note:g fmi:2 analyze:1 ]",
|
||||
"[ 17/6 → 3/1 | note:e fmi:2 analyze:1 ]",
|
||||
"[ 3/1 → 19/6 | note:c fmi:8 analyze:1 ]",
|
||||
"[ 19/6 → 10/3 | note:e fmi:8 analyze:1 ]",
|
||||
"[ 10/3 → 7/2 | note:g fmi:8 analyze:1 ]",
|
||||
"[ 7/2 → 11/3 | note:b fmi:8 analyze:1 ]",
|
||||
"[ 11/3 → 23/6 | note:g fmi:8 analyze:1 ]",
|
||||
"[ 23/6 → 4/1 | note:e fmi:8 analyze:1 ]",
|
||||
"[ 0/1 → 1/6 | note:c fmi:0 ]",
|
||||
"[ 1/6 → 1/3 | note:e fmi:0 ]",
|
||||
"[ 1/3 → 1/2 | note:g fmi:0 ]",
|
||||
"[ 1/2 → 2/3 | note:b fmi:0 ]",
|
||||
"[ 2/3 → 5/6 | note:g fmi:0 ]",
|
||||
"[ 5/6 → 1/1 | note:e fmi:0 ]",
|
||||
"[ 1/1 → 7/6 | note:c fmi:1 ]",
|
||||
"[ 7/6 → 4/3 | note:e fmi:1 ]",
|
||||
"[ 4/3 → 3/2 | note:g fmi:1 ]",
|
||||
"[ 3/2 → 5/3 | note:b fmi:1 ]",
|
||||
"[ 5/3 → 11/6 | note:g fmi:1 ]",
|
||||
"[ 11/6 → 2/1 | note:e fmi:1 ]",
|
||||
"[ 2/1 → 13/6 | note:c fmi:2 ]",
|
||||
"[ 13/6 → 7/3 | note:e fmi:2 ]",
|
||||
"[ 7/3 → 5/2 | note:g fmi:2 ]",
|
||||
"[ 5/2 → 8/3 | note:b fmi:2 ]",
|
||||
"[ 8/3 → 17/6 | note:g fmi:2 ]",
|
||||
"[ 17/6 → 3/1 | note:e fmi:2 ]",
|
||||
"[ 3/1 → 19/6 | note:c fmi:8 ]",
|
||||
"[ 19/6 → 10/3 | note:e fmi:8 ]",
|
||||
"[ 10/3 → 7/2 | note:g fmi:8 ]",
|
||||
"[ 7/2 → 11/3 | note:b fmi:8 ]",
|
||||
"[ 11/3 → 23/6 | note:g fmi:8 ]",
|
||||
"[ 23/6 → 4/1 | note:e fmi:8 ]",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "fmattack" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/6 | note:c fmi:4 fmattack:0 analyze:1 ]",
|
||||
"[ 1/6 → 1/3 | note:e fmi:4 fmattack:0 analyze:1 ]",
|
||||
"[ 1/3 → 1/2 | note:g fmi:4 fmattack:0 analyze:1 ]",
|
||||
"[ 1/2 → 2/3 | note:b fmi:4 fmattack:0 analyze:1 ]",
|
||||
"[ 2/3 → 5/6 | note:g fmi:4 fmattack:0 analyze:1 ]",
|
||||
"[ 5/6 → 1/1 | note:e fmi:4 fmattack:0 analyze:1 ]",
|
||||
"[ 1/1 → 7/6 | note:c fmi:4 fmattack:0.05 analyze:1 ]",
|
||||
"[ 7/6 → 4/3 | note:e fmi:4 fmattack:0.05 analyze:1 ]",
|
||||
"[ 4/3 → 3/2 | note:g fmi:4 fmattack:0.05 analyze:1 ]",
|
||||
"[ 3/2 → 5/3 | note:b fmi:4 fmattack:0.05 analyze:1 ]",
|
||||
"[ 5/3 → 11/6 | note:g fmi:4 fmattack:0.05 analyze:1 ]",
|
||||
"[ 11/6 → 2/1 | note:e fmi:4 fmattack:0.05 analyze:1 ]",
|
||||
"[ 2/1 → 13/6 | note:c fmi:4 fmattack:0.1 analyze:1 ]",
|
||||
"[ 13/6 → 7/3 | note:e fmi:4 fmattack:0.1 analyze:1 ]",
|
||||
"[ 7/3 → 5/2 | note:g fmi:4 fmattack:0.1 analyze:1 ]",
|
||||
"[ 5/2 → 8/3 | note:b fmi:4 fmattack:0.1 analyze:1 ]",
|
||||
"[ 8/3 → 17/6 | note:g fmi:4 fmattack:0.1 analyze:1 ]",
|
||||
"[ 17/6 → 3/1 | note:e fmi:4 fmattack:0.1 analyze:1 ]",
|
||||
"[ 3/1 → 19/6 | note:c fmi:4 fmattack:0.2 analyze:1 ]",
|
||||
"[ 19/6 → 10/3 | note:e fmi:4 fmattack:0.2 analyze:1 ]",
|
||||
"[ 10/3 → 7/2 | note:g fmi:4 fmattack:0.2 analyze:1 ]",
|
||||
"[ 7/2 → 11/3 | note:b fmi:4 fmattack:0.2 analyze:1 ]",
|
||||
"[ 11/3 → 23/6 | note:g fmi:4 fmattack:0.2 analyze:1 ]",
|
||||
"[ 23/6 → 4/1 | note:e fmi:4 fmattack:0.2 analyze:1 ]",
|
||||
"[ 0/1 → 1/6 | note:c fmi:4 fmattack:0 ]",
|
||||
"[ 1/6 → 1/3 | note:e fmi:4 fmattack:0 ]",
|
||||
"[ 1/3 → 1/2 | note:g fmi:4 fmattack:0 ]",
|
||||
"[ 1/2 → 2/3 | note:b fmi:4 fmattack:0 ]",
|
||||
"[ 2/3 → 5/6 | note:g fmi:4 fmattack:0 ]",
|
||||
"[ 5/6 → 1/1 | note:e fmi:4 fmattack:0 ]",
|
||||
"[ 1/1 → 7/6 | note:c fmi:4 fmattack:0.05 ]",
|
||||
"[ 7/6 → 4/3 | note:e fmi:4 fmattack:0.05 ]",
|
||||
"[ 4/3 → 3/2 | note:g fmi:4 fmattack:0.05 ]",
|
||||
"[ 3/2 → 5/3 | note:b fmi:4 fmattack:0.05 ]",
|
||||
"[ 5/3 → 11/6 | note:g fmi:4 fmattack:0.05 ]",
|
||||
"[ 11/6 → 2/1 | note:e fmi:4 fmattack:0.05 ]",
|
||||
"[ 2/1 → 13/6 | note:c fmi:4 fmattack:0.1 ]",
|
||||
"[ 13/6 → 7/3 | note:e fmi:4 fmattack:0.1 ]",
|
||||
"[ 7/3 → 5/2 | note:g fmi:4 fmattack:0.1 ]",
|
||||
"[ 5/2 → 8/3 | note:b fmi:4 fmattack:0.1 ]",
|
||||
"[ 8/3 → 17/6 | note:g fmi:4 fmattack:0.1 ]",
|
||||
"[ 17/6 → 3/1 | note:e fmi:4 fmattack:0.1 ]",
|
||||
"[ 3/1 → 19/6 | note:c fmi:4 fmattack:0.2 ]",
|
||||
"[ 19/6 → 10/3 | note:e fmi:4 fmattack:0.2 ]",
|
||||
"[ 10/3 → 7/2 | note:g fmi:4 fmattack:0.2 ]",
|
||||
"[ 7/2 → 11/3 | note:b fmi:4 fmattack:0.2 ]",
|
||||
"[ 11/3 → 23/6 | note:g fmi:4 fmattack:0.2 ]",
|
||||
"[ 23/6 → 4/1 | note:e fmi:4 fmattack:0.2 ]",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "fmdecay" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/6 | note:c fmi:4 fmdecay:0.01 fmsustain:0.4 analyze:1 ]",
|
||||
"[ 1/6 → 1/3 | note:e fmi:4 fmdecay:0.01 fmsustain:0.4 analyze:1 ]",
|
||||
"[ 1/3 → 1/2 | note:g fmi:4 fmdecay:0.01 fmsustain:0.4 analyze:1 ]",
|
||||
"[ 1/2 → 2/3 | note:b fmi:4 fmdecay:0.01 fmsustain:0.4 analyze:1 ]",
|
||||
"[ 2/3 → 5/6 | note:g fmi:4 fmdecay:0.01 fmsustain:0.4 analyze:1 ]",
|
||||
"[ 5/6 → 1/1 | note:e fmi:4 fmdecay:0.01 fmsustain:0.4 analyze:1 ]",
|
||||
"[ 1/1 → 7/6 | note:c fmi:4 fmdecay:0.05 fmsustain:0.4 analyze:1 ]",
|
||||
"[ 7/6 → 4/3 | note:e fmi:4 fmdecay:0.05 fmsustain:0.4 analyze:1 ]",
|
||||
"[ 4/3 → 3/2 | note:g fmi:4 fmdecay:0.05 fmsustain:0.4 analyze:1 ]",
|
||||
"[ 3/2 → 5/3 | note:b fmi:4 fmdecay:0.05 fmsustain:0.4 analyze:1 ]",
|
||||
"[ 5/3 → 11/6 | note:g fmi:4 fmdecay:0.05 fmsustain:0.4 analyze:1 ]",
|
||||
"[ 11/6 → 2/1 | note:e fmi:4 fmdecay:0.05 fmsustain:0.4 analyze:1 ]",
|
||||
"[ 2/1 → 13/6 | note:c fmi:4 fmdecay:0.1 fmsustain:0.4 analyze:1 ]",
|
||||
"[ 13/6 → 7/3 | note:e fmi:4 fmdecay:0.1 fmsustain:0.4 analyze:1 ]",
|
||||
"[ 7/3 → 5/2 | note:g fmi:4 fmdecay:0.1 fmsustain:0.4 analyze:1 ]",
|
||||
"[ 5/2 → 8/3 | note:b fmi:4 fmdecay:0.1 fmsustain:0.4 analyze:1 ]",
|
||||
"[ 8/3 → 17/6 | note:g fmi:4 fmdecay:0.1 fmsustain:0.4 analyze:1 ]",
|
||||
"[ 17/6 → 3/1 | note:e fmi:4 fmdecay:0.1 fmsustain:0.4 analyze:1 ]",
|
||||
"[ 3/1 → 19/6 | note:c fmi:4 fmdecay:0.2 fmsustain:0.4 analyze:1 ]",
|
||||
"[ 19/6 → 10/3 | note:e fmi:4 fmdecay:0.2 fmsustain:0.4 analyze:1 ]",
|
||||
"[ 10/3 → 7/2 | note:g fmi:4 fmdecay:0.2 fmsustain:0.4 analyze:1 ]",
|
||||
"[ 7/2 → 11/3 | note:b fmi:4 fmdecay:0.2 fmsustain:0.4 analyze:1 ]",
|
||||
"[ 11/3 → 23/6 | note:g fmi:4 fmdecay:0.2 fmsustain:0.4 analyze:1 ]",
|
||||
"[ 23/6 → 4/1 | note:e fmi:4 fmdecay:0.2 fmsustain:0.4 analyze:1 ]",
|
||||
"[ 0/1 → 1/6 | note:c fmi:4 fmdecay:0.01 fmsustain:0.4 ]",
|
||||
"[ 1/6 → 1/3 | note:e fmi:4 fmdecay:0.01 fmsustain:0.4 ]",
|
||||
"[ 1/3 → 1/2 | note:g fmi:4 fmdecay:0.01 fmsustain:0.4 ]",
|
||||
"[ 1/2 → 2/3 | note:b fmi:4 fmdecay:0.01 fmsustain:0.4 ]",
|
||||
"[ 2/3 → 5/6 | note:g fmi:4 fmdecay:0.01 fmsustain:0.4 ]",
|
||||
"[ 5/6 → 1/1 | note:e fmi:4 fmdecay:0.01 fmsustain:0.4 ]",
|
||||
"[ 1/1 → 7/6 | note:c fmi:4 fmdecay:0.05 fmsustain:0.4 ]",
|
||||
"[ 7/6 → 4/3 | note:e fmi:4 fmdecay:0.05 fmsustain:0.4 ]",
|
||||
"[ 4/3 → 3/2 | note:g fmi:4 fmdecay:0.05 fmsustain:0.4 ]",
|
||||
"[ 3/2 → 5/3 | note:b fmi:4 fmdecay:0.05 fmsustain:0.4 ]",
|
||||
"[ 5/3 → 11/6 | note:g fmi:4 fmdecay:0.05 fmsustain:0.4 ]",
|
||||
"[ 11/6 → 2/1 | note:e fmi:4 fmdecay:0.05 fmsustain:0.4 ]",
|
||||
"[ 2/1 → 13/6 | note:c fmi:4 fmdecay:0.1 fmsustain:0.4 ]",
|
||||
"[ 13/6 → 7/3 | note:e fmi:4 fmdecay:0.1 fmsustain:0.4 ]",
|
||||
"[ 7/3 → 5/2 | note:g fmi:4 fmdecay:0.1 fmsustain:0.4 ]",
|
||||
"[ 5/2 → 8/3 | note:b fmi:4 fmdecay:0.1 fmsustain:0.4 ]",
|
||||
"[ 8/3 → 17/6 | note:g fmi:4 fmdecay:0.1 fmsustain:0.4 ]",
|
||||
"[ 17/6 → 3/1 | note:e fmi:4 fmdecay:0.1 fmsustain:0.4 ]",
|
||||
"[ 3/1 → 19/6 | note:c fmi:4 fmdecay:0.2 fmsustain:0.4 ]",
|
||||
"[ 19/6 → 10/3 | note:e fmi:4 fmdecay:0.2 fmsustain:0.4 ]",
|
||||
"[ 10/3 → 7/2 | note:g fmi:4 fmdecay:0.2 fmsustain:0.4 ]",
|
||||
"[ 7/2 → 11/3 | note:b fmi:4 fmdecay:0.2 fmsustain:0.4 ]",
|
||||
"[ 11/3 → 23/6 | note:g fmi:4 fmdecay:0.2 fmsustain:0.4 ]",
|
||||
"[ 23/6 → 4/1 | note:e fmi:4 fmdecay:0.2 fmsustain:0.4 ]",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "fmenv" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/6 | note:c fmi:4 fmdecay:0.2 fmsustain:0 fmenv:exp analyze:1 ]",
|
||||
"[ 1/6 → 1/3 | note:e fmi:4 fmdecay:0.2 fmsustain:0 fmenv:exp analyze:1 ]",
|
||||
"[ 1/3 → 1/2 | note:g fmi:4 fmdecay:0.2 fmsustain:0 fmenv:exp analyze:1 ]",
|
||||
"[ 1/2 → 2/3 | note:b fmi:4 fmdecay:0.2 fmsustain:0 fmenv:exp analyze:1 ]",
|
||||
"[ 2/3 → 5/6 | note:g fmi:4 fmdecay:0.2 fmsustain:0 fmenv:exp analyze:1 ]",
|
||||
"[ 5/6 → 1/1 | note:e fmi:4 fmdecay:0.2 fmsustain:0 fmenv:exp analyze:1 ]",
|
||||
"[ 1/1 → 7/6 | note:c fmi:4 fmdecay:0.2 fmsustain:0 fmenv:lin analyze:1 ]",
|
||||
"[ 7/6 → 4/3 | note:e fmi:4 fmdecay:0.2 fmsustain:0 fmenv:lin analyze:1 ]",
|
||||
"[ 4/3 → 3/2 | note:g fmi:4 fmdecay:0.2 fmsustain:0 fmenv:lin analyze:1 ]",
|
||||
"[ 3/2 → 5/3 | note:b fmi:4 fmdecay:0.2 fmsustain:0 fmenv:lin analyze:1 ]",
|
||||
"[ 5/3 → 11/6 | note:g fmi:4 fmdecay:0.2 fmsustain:0 fmenv:lin analyze:1 ]",
|
||||
"[ 11/6 → 2/1 | note:e fmi:4 fmdecay:0.2 fmsustain:0 fmenv:lin analyze:1 ]",
|
||||
"[ 2/1 → 13/6 | note:c fmi:4 fmdecay:0.2 fmsustain:0 fmenv:exp analyze:1 ]",
|
||||
"[ 13/6 → 7/3 | note:e fmi:4 fmdecay:0.2 fmsustain:0 fmenv:exp analyze:1 ]",
|
||||
"[ 7/3 → 5/2 | note:g fmi:4 fmdecay:0.2 fmsustain:0 fmenv:exp analyze:1 ]",
|
||||
"[ 5/2 → 8/3 | note:b fmi:4 fmdecay:0.2 fmsustain:0 fmenv:exp analyze:1 ]",
|
||||
"[ 8/3 → 17/6 | note:g fmi:4 fmdecay:0.2 fmsustain:0 fmenv:exp analyze:1 ]",
|
||||
"[ 17/6 → 3/1 | note:e fmi:4 fmdecay:0.2 fmsustain:0 fmenv:exp analyze:1 ]",
|
||||
"[ 3/1 → 19/6 | note:c fmi:4 fmdecay:0.2 fmsustain:0 fmenv:lin analyze:1 ]",
|
||||
"[ 19/6 → 10/3 | note:e fmi:4 fmdecay:0.2 fmsustain:0 fmenv:lin analyze:1 ]",
|
||||
"[ 10/3 → 7/2 | note:g fmi:4 fmdecay:0.2 fmsustain:0 fmenv:lin analyze:1 ]",
|
||||
"[ 7/2 → 11/3 | note:b fmi:4 fmdecay:0.2 fmsustain:0 fmenv:lin analyze:1 ]",
|
||||
"[ 11/3 → 23/6 | note:g fmi:4 fmdecay:0.2 fmsustain:0 fmenv:lin analyze:1 ]",
|
||||
"[ 23/6 → 4/1 | note:e fmi:4 fmdecay:0.2 fmsustain:0 fmenv:lin analyze:1 ]",
|
||||
"[ 0/1 → 1/6 | note:c fmi:4 fmdecay:0.2 fmsustain:0 fmenv:exp ]",
|
||||
"[ 1/6 → 1/3 | note:e fmi:4 fmdecay:0.2 fmsustain:0 fmenv:exp ]",
|
||||
"[ 1/3 → 1/2 | note:g fmi:4 fmdecay:0.2 fmsustain:0 fmenv:exp ]",
|
||||
"[ 1/2 → 2/3 | note:b fmi:4 fmdecay:0.2 fmsustain:0 fmenv:exp ]",
|
||||
"[ 2/3 → 5/6 | note:g fmi:4 fmdecay:0.2 fmsustain:0 fmenv:exp ]",
|
||||
"[ 5/6 → 1/1 | note:e fmi:4 fmdecay:0.2 fmsustain:0 fmenv:exp ]",
|
||||
"[ 1/1 → 7/6 | note:c fmi:4 fmdecay:0.2 fmsustain:0 fmenv:lin ]",
|
||||
"[ 7/6 → 4/3 | note:e fmi:4 fmdecay:0.2 fmsustain:0 fmenv:lin ]",
|
||||
"[ 4/3 → 3/2 | note:g fmi:4 fmdecay:0.2 fmsustain:0 fmenv:lin ]",
|
||||
"[ 3/2 → 5/3 | note:b fmi:4 fmdecay:0.2 fmsustain:0 fmenv:lin ]",
|
||||
"[ 5/3 → 11/6 | note:g fmi:4 fmdecay:0.2 fmsustain:0 fmenv:lin ]",
|
||||
"[ 11/6 → 2/1 | note:e fmi:4 fmdecay:0.2 fmsustain:0 fmenv:lin ]",
|
||||
"[ 2/1 → 13/6 | note:c fmi:4 fmdecay:0.2 fmsustain:0 fmenv:exp ]",
|
||||
"[ 13/6 → 7/3 | note:e fmi:4 fmdecay:0.2 fmsustain:0 fmenv:exp ]",
|
||||
"[ 7/3 → 5/2 | note:g fmi:4 fmdecay:0.2 fmsustain:0 fmenv:exp ]",
|
||||
"[ 5/2 → 8/3 | note:b fmi:4 fmdecay:0.2 fmsustain:0 fmenv:exp ]",
|
||||
"[ 8/3 → 17/6 | note:g fmi:4 fmdecay:0.2 fmsustain:0 fmenv:exp ]",
|
||||
"[ 17/6 → 3/1 | note:e fmi:4 fmdecay:0.2 fmsustain:0 fmenv:exp ]",
|
||||
"[ 3/1 → 19/6 | note:c fmi:4 fmdecay:0.2 fmsustain:0 fmenv:lin ]",
|
||||
"[ 19/6 → 10/3 | note:e fmi:4 fmdecay:0.2 fmsustain:0 fmenv:lin ]",
|
||||
"[ 10/3 → 7/2 | note:g fmi:4 fmdecay:0.2 fmsustain:0 fmenv:lin ]",
|
||||
"[ 7/2 → 11/3 | note:b fmi:4 fmdecay:0.2 fmsustain:0 fmenv:lin ]",
|
||||
"[ 11/3 → 23/6 | note:g fmi:4 fmdecay:0.2 fmsustain:0 fmenv:lin ]",
|
||||
"[ 23/6 → 4/1 | note:e fmi:4 fmdecay:0.2 fmsustain:0 fmenv:lin ]",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "fmh" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/6 | note:c fmi:4 fmh:1 analyze:1 ]",
|
||||
"[ 1/6 → 1/3 | note:e fmi:4 fmh:1 analyze:1 ]",
|
||||
"[ 1/3 → 1/2 | note:g fmi:4 fmh:1 analyze:1 ]",
|
||||
"[ 1/2 → 2/3 | note:b fmi:4 fmh:1 analyze:1 ]",
|
||||
"[ 2/3 → 5/6 | note:g fmi:4 fmh:1 analyze:1 ]",
|
||||
"[ 5/6 → 1/1 | note:e fmi:4 fmh:1 analyze:1 ]",
|
||||
"[ 1/1 → 7/6 | note:c fmi:4 fmh:2 analyze:1 ]",
|
||||
"[ 7/6 → 4/3 | note:e fmi:4 fmh:2 analyze:1 ]",
|
||||
"[ 4/3 → 3/2 | note:g fmi:4 fmh:2 analyze:1 ]",
|
||||
"[ 3/2 → 5/3 | note:b fmi:4 fmh:2 analyze:1 ]",
|
||||
"[ 5/3 → 11/6 | note:g fmi:4 fmh:2 analyze:1 ]",
|
||||
"[ 11/6 → 2/1 | note:e fmi:4 fmh:2 analyze:1 ]",
|
||||
"[ 2/1 → 13/6 | note:c fmi:4 fmh:1.5 analyze:1 ]",
|
||||
"[ 13/6 → 7/3 | note:e fmi:4 fmh:1.5 analyze:1 ]",
|
||||
"[ 7/3 → 5/2 | note:g fmi:4 fmh:1.5 analyze:1 ]",
|
||||
"[ 5/2 → 8/3 | note:b fmi:4 fmh:1.5 analyze:1 ]",
|
||||
"[ 8/3 → 17/6 | note:g fmi:4 fmh:1.5 analyze:1 ]",
|
||||
"[ 17/6 → 3/1 | note:e fmi:4 fmh:1.5 analyze:1 ]",
|
||||
"[ 3/1 → 19/6 | note:c fmi:4 fmh:1.61 analyze:1 ]",
|
||||
"[ 19/6 → 10/3 | note:e fmi:4 fmh:1.61 analyze:1 ]",
|
||||
"[ 10/3 → 7/2 | note:g fmi:4 fmh:1.61 analyze:1 ]",
|
||||
"[ 7/2 → 11/3 | note:b fmi:4 fmh:1.61 analyze:1 ]",
|
||||
"[ 11/3 → 23/6 | note:g fmi:4 fmh:1.61 analyze:1 ]",
|
||||
"[ 23/6 → 4/1 | note:e fmi:4 fmh:1.61 analyze:1 ]",
|
||||
"[ 0/1 → 1/6 | note:c fmi:4 fmh:1 ]",
|
||||
"[ 1/6 → 1/3 | note:e fmi:4 fmh:1 ]",
|
||||
"[ 1/3 → 1/2 | note:g fmi:4 fmh:1 ]",
|
||||
"[ 1/2 → 2/3 | note:b fmi:4 fmh:1 ]",
|
||||
"[ 2/3 → 5/6 | note:g fmi:4 fmh:1 ]",
|
||||
"[ 5/6 → 1/1 | note:e fmi:4 fmh:1 ]",
|
||||
"[ 1/1 → 7/6 | note:c fmi:4 fmh:2 ]",
|
||||
"[ 7/6 → 4/3 | note:e fmi:4 fmh:2 ]",
|
||||
"[ 4/3 → 3/2 | note:g fmi:4 fmh:2 ]",
|
||||
"[ 3/2 → 5/3 | note:b fmi:4 fmh:2 ]",
|
||||
"[ 5/3 → 11/6 | note:g fmi:4 fmh:2 ]",
|
||||
"[ 11/6 → 2/1 | note:e fmi:4 fmh:2 ]",
|
||||
"[ 2/1 → 13/6 | note:c fmi:4 fmh:1.5 ]",
|
||||
"[ 13/6 → 7/3 | note:e fmi:4 fmh:1.5 ]",
|
||||
"[ 7/3 → 5/2 | note:g fmi:4 fmh:1.5 ]",
|
||||
"[ 5/2 → 8/3 | note:b fmi:4 fmh:1.5 ]",
|
||||
"[ 8/3 → 17/6 | note:g fmi:4 fmh:1.5 ]",
|
||||
"[ 17/6 → 3/1 | note:e fmi:4 fmh:1.5 ]",
|
||||
"[ 3/1 → 19/6 | note:c fmi:4 fmh:1.61 ]",
|
||||
"[ 19/6 → 10/3 | note:e fmi:4 fmh:1.61 ]",
|
||||
"[ 10/3 → 7/2 | note:g fmi:4 fmh:1.61 ]",
|
||||
"[ 7/2 → 11/3 | note:b fmi:4 fmh:1.61 ]",
|
||||
"[ 11/3 → 23/6 | note:g fmi:4 fmh:1.61 ]",
|
||||
"[ 23/6 → 4/1 | note:e fmi:4 fmh:1.61 ]",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "fmsustain" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/6 | note:c fmi:4 fmdecay:0.1 fmsustain:1 analyze:1 ]",
|
||||
"[ 1/6 → 1/3 | note:e fmi:4 fmdecay:0.1 fmsustain:1 analyze:1 ]",
|
||||
"[ 1/3 → 1/2 | note:g fmi:4 fmdecay:0.1 fmsustain:1 analyze:1 ]",
|
||||
"[ 1/2 → 2/3 | note:b fmi:4 fmdecay:0.1 fmsustain:1 analyze:1 ]",
|
||||
"[ 2/3 → 5/6 | note:g fmi:4 fmdecay:0.1 fmsustain:1 analyze:1 ]",
|
||||
"[ 5/6 → 1/1 | note:e fmi:4 fmdecay:0.1 fmsustain:1 analyze:1 ]",
|
||||
"[ 1/1 → 7/6 | note:c fmi:4 fmdecay:0.1 fmsustain:0.75 analyze:1 ]",
|
||||
"[ 7/6 → 4/3 | note:e fmi:4 fmdecay:0.1 fmsustain:0.75 analyze:1 ]",
|
||||
"[ 4/3 → 3/2 | note:g fmi:4 fmdecay:0.1 fmsustain:0.75 analyze:1 ]",
|
||||
"[ 3/2 → 5/3 | note:b fmi:4 fmdecay:0.1 fmsustain:0.75 analyze:1 ]",
|
||||
"[ 5/3 → 11/6 | note:g fmi:4 fmdecay:0.1 fmsustain:0.75 analyze:1 ]",
|
||||
"[ 11/6 → 2/1 | note:e fmi:4 fmdecay:0.1 fmsustain:0.75 analyze:1 ]",
|
||||
"[ 2/1 → 13/6 | note:c fmi:4 fmdecay:0.1 fmsustain:0.5 analyze:1 ]",
|
||||
"[ 13/6 → 7/3 | note:e fmi:4 fmdecay:0.1 fmsustain:0.5 analyze:1 ]",
|
||||
"[ 7/3 → 5/2 | note:g fmi:4 fmdecay:0.1 fmsustain:0.5 analyze:1 ]",
|
||||
"[ 5/2 → 8/3 | note:b fmi:4 fmdecay:0.1 fmsustain:0.5 analyze:1 ]",
|
||||
"[ 8/3 → 17/6 | note:g fmi:4 fmdecay:0.1 fmsustain:0.5 analyze:1 ]",
|
||||
"[ 17/6 → 3/1 | note:e fmi:4 fmdecay:0.1 fmsustain:0.5 analyze:1 ]",
|
||||
"[ 3/1 → 19/6 | note:c fmi:4 fmdecay:0.1 fmsustain:0 analyze:1 ]",
|
||||
"[ 19/6 → 10/3 | note:e fmi:4 fmdecay:0.1 fmsustain:0 analyze:1 ]",
|
||||
"[ 10/3 → 7/2 | note:g fmi:4 fmdecay:0.1 fmsustain:0 analyze:1 ]",
|
||||
"[ 7/2 → 11/3 | note:b fmi:4 fmdecay:0.1 fmsustain:0 analyze:1 ]",
|
||||
"[ 11/3 → 23/6 | note:g fmi:4 fmdecay:0.1 fmsustain:0 analyze:1 ]",
|
||||
"[ 23/6 → 4/1 | note:e fmi:4 fmdecay:0.1 fmsustain:0 analyze:1 ]",
|
||||
"[ 0/1 → 1/6 | note:c fmi:4 fmdecay:0.1 fmsustain:1 ]",
|
||||
"[ 1/6 → 1/3 | note:e fmi:4 fmdecay:0.1 fmsustain:1 ]",
|
||||
"[ 1/3 → 1/2 | note:g fmi:4 fmdecay:0.1 fmsustain:1 ]",
|
||||
"[ 1/2 → 2/3 | note:b fmi:4 fmdecay:0.1 fmsustain:1 ]",
|
||||
"[ 2/3 → 5/6 | note:g fmi:4 fmdecay:0.1 fmsustain:1 ]",
|
||||
"[ 5/6 → 1/1 | note:e fmi:4 fmdecay:0.1 fmsustain:1 ]",
|
||||
"[ 1/1 → 7/6 | note:c fmi:4 fmdecay:0.1 fmsustain:0.75 ]",
|
||||
"[ 7/6 → 4/3 | note:e fmi:4 fmdecay:0.1 fmsustain:0.75 ]",
|
||||
"[ 4/3 → 3/2 | note:g fmi:4 fmdecay:0.1 fmsustain:0.75 ]",
|
||||
"[ 3/2 → 5/3 | note:b fmi:4 fmdecay:0.1 fmsustain:0.75 ]",
|
||||
"[ 5/3 → 11/6 | note:g fmi:4 fmdecay:0.1 fmsustain:0.75 ]",
|
||||
"[ 11/6 → 2/1 | note:e fmi:4 fmdecay:0.1 fmsustain:0.75 ]",
|
||||
"[ 2/1 → 13/6 | note:c fmi:4 fmdecay:0.1 fmsustain:0.5 ]",
|
||||
"[ 13/6 → 7/3 | note:e fmi:4 fmdecay:0.1 fmsustain:0.5 ]",
|
||||
"[ 7/3 → 5/2 | note:g fmi:4 fmdecay:0.1 fmsustain:0.5 ]",
|
||||
"[ 5/2 → 8/3 | note:b fmi:4 fmdecay:0.1 fmsustain:0.5 ]",
|
||||
"[ 8/3 → 17/6 | note:g fmi:4 fmdecay:0.1 fmsustain:0.5 ]",
|
||||
"[ 17/6 → 3/1 | note:e fmi:4 fmdecay:0.1 fmsustain:0.5 ]",
|
||||
"[ 3/1 → 19/6 | note:c fmi:4 fmdecay:0.1 fmsustain:0 ]",
|
||||
"[ 19/6 → 10/3 | note:e fmi:4 fmdecay:0.1 fmsustain:0 ]",
|
||||
"[ 10/3 → 7/2 | note:g fmi:4 fmdecay:0.1 fmsustain:0 ]",
|
||||
"[ 7/2 → 11/3 | note:b fmi:4 fmdecay:0.1 fmsustain:0 ]",
|
||||
"[ 11/3 → 23/6 | note:g fmi:4 fmdecay:0.1 fmsustain:0 ]",
|
||||
"[ 23/6 → 4/1 | note:e fmi:4 fmdecay:0.1 fmsustain:0 ]",
|
||||
]
|
||||
`;
|
||||
|
||||
@@ -3953,6 +3953,88 @@ exports[`runs examples > example "layer" example index 0 1`] = `
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "legato" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/8 | s:sawtooth ]",
|
||||
"[ 1/8 → 3/16 | s:sawtooth ]",
|
||||
"[ 3/16 → 5/16 | s:sawtooth ]",
|
||||
"[ 5/16 → 3/8 | s:sawtooth ]",
|
||||
"[ 3/8 → 1/2 | s:sawtooth ]",
|
||||
"[ 1/2 → 9/16 | s:sawtooth ]",
|
||||
"[ 9/16 → 11/16 | s:sawtooth ]",
|
||||
"[ 11/16 → 3/4 | s:sawtooth ]",
|
||||
"[ 3/4 → 7/8 | s:sawtooth ]",
|
||||
"[ 7/8 → 15/16 | s:sawtooth ]",
|
||||
"[ 15/16 → 1/1 | s:sawtooth ]",
|
||||
"[ 1/1 → 17/16 | s:sawtooth ]",
|
||||
"[ 9/8 → 37/32 | s:sawtooth ]",
|
||||
"[ 19/16 → 5/4 | s:sawtooth ]",
|
||||
"[ 21/16 → 43/32 | s:sawtooth ]",
|
||||
"[ 11/8 → 23/16 | s:sawtooth ]",
|
||||
"[ 3/2 → 49/32 | s:sawtooth ]",
|
||||
"[ 25/16 → 13/8 | s:sawtooth ]",
|
||||
"[ 27/16 → 55/32 | s:sawtooth ]",
|
||||
"[ 7/4 → 29/16 | s:sawtooth ]",
|
||||
"[ 15/8 → 61/32 | s:sawtooth ]",
|
||||
"[ 31/16 → 63/32 | s:sawtooth ]",
|
||||
"[ 2/1 → 17/8 | s:sawtooth ]",
|
||||
"[ 17/8 → 35/16 | s:sawtooth ]",
|
||||
"[ 35/16 → 37/16 | s:sawtooth ]",
|
||||
"[ 37/16 → 19/8 | s:sawtooth ]",
|
||||
"[ 19/8 → 5/2 | s:sawtooth ]",
|
||||
"[ 5/2 → 41/16 | s:sawtooth ]",
|
||||
"[ 41/16 → 43/16 | s:sawtooth ]",
|
||||
"[ 43/16 → 11/4 | s:sawtooth ]",
|
||||
"[ 11/4 → 23/8 | s:sawtooth ]",
|
||||
"[ 23/8 → 47/16 | s:sawtooth ]",
|
||||
"[ 47/16 → 3/1 | s:sawtooth ]",
|
||||
"[ 3/1 → 49/16 | s:sawtooth ]",
|
||||
"[ 25/8 → 101/32 | s:sawtooth ]",
|
||||
"[ 51/16 → 13/4 | s:sawtooth ]",
|
||||
"[ 53/16 → 107/32 | s:sawtooth ]",
|
||||
"[ 27/8 → 55/16 | s:sawtooth ]",
|
||||
"[ 7/2 → 113/32 | s:sawtooth ]",
|
||||
"[ 57/16 → 29/8 | s:sawtooth ]",
|
||||
"[ 59/16 → 119/32 | s:sawtooth ]",
|
||||
"[ 15/4 → 61/16 | s:sawtooth ]",
|
||||
"[ 31/8 → 125/32 | s:sawtooth ]",
|
||||
"[ 63/16 → 127/32 | s:sawtooth ]",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "legato" example index 1 1`] = `
|
||||
[
|
||||
"[ 0/1 → 3/16 | s:supersaw ]",
|
||||
"[ 3/16 → 5/16 | s:supersaw ]",
|
||||
"[ 5/16 → 7/16 | s:supersaw ]",
|
||||
"[ 7/16 → 5/8 | s:supersaw ]",
|
||||
"[ 5/8 → 3/4 | s:supersaw ]",
|
||||
"[ 3/4 → 7/8 | s:supersaw ]",
|
||||
"[ 7/8 → 1/1 | s:supersaw ]",
|
||||
"[ 1/1 → 19/16 | s:supersaw ]",
|
||||
"[ 19/16 → 21/16 | s:supersaw ]",
|
||||
"[ 21/16 → 23/16 | s:supersaw ]",
|
||||
"[ 23/16 → 13/8 | s:supersaw ]",
|
||||
"[ 13/8 → 7/4 | s:supersaw ]",
|
||||
"[ 7/4 → 15/8 | s:supersaw ]",
|
||||
"[ 15/8 → 2/1 | s:supersaw ]",
|
||||
"[ 2/1 → 35/16 | s:supersaw ]",
|
||||
"[ 35/16 → 37/16 | s:supersaw ]",
|
||||
"[ 37/16 → 39/16 | s:supersaw ]",
|
||||
"[ 39/16 → 21/8 | s:supersaw ]",
|
||||
"[ 21/8 → 11/4 | s:supersaw ]",
|
||||
"[ 11/4 → 23/8 | s:supersaw ]",
|
||||
"[ 23/8 → 3/1 | s:supersaw ]",
|
||||
"[ 3/1 → 51/16 | s:supersaw ]",
|
||||
"[ 51/16 → 53/16 | s:supersaw ]",
|
||||
"[ 53/16 → 55/16 | s:supersaw ]",
|
||||
"[ 55/16 → 29/8 | s:supersaw ]",
|
||||
"[ 29/8 → 15/4 | s:supersaw ]",
|
||||
"[ 15/4 → 31/8 | s:supersaw ]",
|
||||
"[ 31/8 → 4/1 | s:supersaw ]",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "leslie" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/1 | n:0 s:supersquare leslie:0 ]",
|
||||
@@ -4042,19 +4124,19 @@ 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 ]",
|
||||
"[ 0/1 → 1/1 | s:space loop:1 loopBegin:0 ]",
|
||||
"[ 1/1 → 2/1 | s:space loop:1 loopBegin:0.125 ]",
|
||||
"[ 2/1 → 3/1 | s:space loop:1 loopBegin:0.25 ]",
|
||||
"[ 3/1 → 4/1 | s:space loop:1 loopBegin:0 ]",
|
||||
]
|
||||
`;
|
||||
|
||||
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 ]",
|
||||
"[ 0/1 → 1/1 | s:space loop:1 loopEnd:1 ]",
|
||||
"[ 1/1 → 2/1 | s:space loop:1 loopEnd:0.75 ]",
|
||||
"[ 2/1 → 3/1 | s:space loop:1 loopEnd:0.5 ]",
|
||||
"[ 3/1 → 4/1 | s:space loop:1 loopEnd:0.25 ]",
|
||||
]
|
||||
`;
|
||||
|
||||
@@ -5085,34 +5167,34 @@ exports[`runs examples > example "phasersweep" example index 0 1`] = `
|
||||
|
||||
exports[`runs examples > example "pianoroll" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/8 | note:C2 s:piano clip:1 color:salmon ]",
|
||||
"[ (1/4 → 1/3) ⇝ 3/8 | note:C2 s:piano clip:1 color:salmon ]",
|
||||
"[ 1/4 ⇜ (1/3 → 3/8) | note:A2 s:piano clip:1 color:salmon ]",
|
||||
"[ 3/8 → 1/2 | note:A2 s:piano clip:1 color:salmon ]",
|
||||
"[ (5/8 → 2/3) ⇝ 3/4 | note:A2 s:piano clip:1 color:salmon ]",
|
||||
"[ 5/8 ⇜ (2/3 → 3/4) | note:G2 s:piano clip:1 color:salmon ]",
|
||||
"[ 3/4 → 7/8 | note:G2 s:piano clip:1 color:salmon ]",
|
||||
"[ 1/1 → 9/8 | note:C2 s:piano clip:1 color:salmon ]",
|
||||
"[ (5/4 → 4/3) ⇝ 11/8 | note:C2 s:piano clip:1 color:salmon ]",
|
||||
"[ 5/4 ⇜ (4/3 → 11/8) | note:A2 s:piano clip:1 color:salmon ]",
|
||||
"[ 11/8 → 3/2 | note:A2 s:piano clip:1 color:salmon ]",
|
||||
"[ (13/8 → 5/3) ⇝ 7/4 | note:A2 s:piano clip:1 color:salmon ]",
|
||||
"[ 13/8 ⇜ (5/3 → 7/4) | note:G2 s:piano clip:1 color:salmon ]",
|
||||
"[ 7/4 → 15/8 | note:G2 s:piano clip:1 color:salmon ]",
|
||||
"[ 2/1 → 17/8 | note:C2 s:piano clip:1 color:salmon ]",
|
||||
"[ (9/4 → 7/3) ⇝ 19/8 | note:C2 s:piano clip:1 color:salmon ]",
|
||||
"[ 9/4 ⇜ (7/3 → 19/8) | note:A2 s:piano clip:1 color:salmon ]",
|
||||
"[ 19/8 → 5/2 | note:A2 s:piano clip:1 color:salmon ]",
|
||||
"[ (21/8 → 8/3) ⇝ 11/4 | note:A2 s:piano clip:1 color:salmon ]",
|
||||
"[ 21/8 ⇜ (8/3 → 11/4) | note:G2 s:piano clip:1 color:salmon ]",
|
||||
"[ 11/4 → 23/8 | note:G2 s:piano clip:1 color:salmon ]",
|
||||
"[ 3/1 → 25/8 | note:C2 s:piano clip:1 color:salmon ]",
|
||||
"[ (13/4 → 10/3) ⇝ 27/8 | note:C2 s:piano clip:1 color:salmon ]",
|
||||
"[ 13/4 ⇜ (10/3 → 27/8) | note:A2 s:piano clip:1 color:salmon ]",
|
||||
"[ 27/8 → 7/2 | note:A2 s:piano clip:1 color:salmon ]",
|
||||
"[ (29/8 → 11/3) ⇝ 15/4 | note:A2 s:piano clip:1 color:salmon ]",
|
||||
"[ 29/8 ⇜ (11/3 → 15/4) | note:G2 s:piano clip:1 color:salmon ]",
|
||||
"[ 15/4 → 31/8 | note:G2 s:piano clip:1 color:salmon ]",
|
||||
"[ 0/1 → 1/8 | note:c2 s:sawtooth lpenv:4 cutoff:300 ]",
|
||||
"[ (1/4 → 1/3) ⇝ 3/8 | note:c2 s:sawtooth lpenv:4 cutoff:300 ]",
|
||||
"[ 1/4 ⇜ (1/3 → 3/8) | note:a2 s:sawtooth lpenv:4 cutoff:300 ]",
|
||||
"[ 3/8 → 1/2 | note:a2 s:sawtooth lpenv:4 cutoff:300 ]",
|
||||
"[ (5/8 → 2/3) ⇝ 3/4 | note:a2 s:sawtooth lpenv:4 cutoff:300 ]",
|
||||
"[ 5/8 ⇜ (2/3 → 3/4) | note:eb2 s:sawtooth lpenv:4 cutoff:300 ]",
|
||||
"[ 3/4 → 7/8 | note:eb2 s:sawtooth lpenv:4 cutoff:300 ]",
|
||||
"[ 1/1 → 9/8 | note:c2 s:sawtooth lpenv:4 cutoff:300 ]",
|
||||
"[ (5/4 → 4/3) ⇝ 11/8 | note:c2 s:sawtooth lpenv:4 cutoff:300 ]",
|
||||
"[ 5/4 ⇜ (4/3 → 11/8) | note:a2 s:sawtooth lpenv:4 cutoff:300 ]",
|
||||
"[ 11/8 → 3/2 | note:a2 s:sawtooth lpenv:4 cutoff:300 ]",
|
||||
"[ (13/8 → 5/3) ⇝ 7/4 | note:a2 s:sawtooth lpenv:4 cutoff:300 ]",
|
||||
"[ 13/8 ⇜ (5/3 → 7/4) | note:eb2 s:sawtooth lpenv:4 cutoff:300 ]",
|
||||
"[ 7/4 → 15/8 | note:eb2 s:sawtooth lpenv:4 cutoff:300 ]",
|
||||
"[ 2/1 → 17/8 | note:c2 s:sawtooth lpenv:4 cutoff:300 ]",
|
||||
"[ (9/4 → 7/3) ⇝ 19/8 | note:c2 s:sawtooth lpenv:4 cutoff:300 ]",
|
||||
"[ 9/4 ⇜ (7/3 → 19/8) | note:a2 s:sawtooth lpenv:4 cutoff:300 ]",
|
||||
"[ 19/8 → 5/2 | note:a2 s:sawtooth lpenv:4 cutoff:300 ]",
|
||||
"[ (21/8 → 8/3) ⇝ 11/4 | note:a2 s:sawtooth lpenv:4 cutoff:300 ]",
|
||||
"[ 21/8 ⇜ (8/3 → 11/4) | note:eb2 s:sawtooth lpenv:4 cutoff:300 ]",
|
||||
"[ 11/4 → 23/8 | note:eb2 s:sawtooth lpenv:4 cutoff:300 ]",
|
||||
"[ 3/1 → 25/8 | note:c2 s:sawtooth lpenv:4 cutoff:300 ]",
|
||||
"[ (13/4 → 10/3) ⇝ 27/8 | note:c2 s:sawtooth lpenv:4 cutoff:300 ]",
|
||||
"[ 13/4 ⇜ (10/3 → 27/8) | note:a2 s:sawtooth lpenv:4 cutoff:300 ]",
|
||||
"[ 27/8 → 7/2 | note:a2 s:sawtooth lpenv:4 cutoff:300 ]",
|
||||
"[ (29/8 → 11/3) ⇝ 15/4 | note:a2 s:sawtooth lpenv:4 cutoff:300 ]",
|
||||
"[ 29/8 ⇜ (11/3 → 15/4) | note:eb2 s:sawtooth lpenv:4 cutoff:300 ]",
|
||||
"[ 15/4 → 31/8 | note:eb2 s:sawtooth lpenv:4 cutoff:300 ]",
|
||||
]
|
||||
`;
|
||||
|
||||
@@ -5234,6 +5316,63 @@ exports[`runs examples > example "pickF" example index 1 1`] = `
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "pitchwheel" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/13 | note:C3 s:sawtooth cutoff:500 ]",
|
||||
"[ 1/13 → 2/13 | note:Db3 s:sawtooth cutoff:500 ]",
|
||||
"[ 2/13 → 3/13 | note:D3 s:sawtooth cutoff:500 ]",
|
||||
"[ 3/13 → 4/13 | note:Eb3 s:sawtooth cutoff:500 ]",
|
||||
"[ 4/13 → 5/13 | note:E3 s:sawtooth cutoff:500 ]",
|
||||
"[ 5/13 → 6/13 | note:F3 s:sawtooth cutoff:500 ]",
|
||||
"[ 6/13 → 7/13 | note:Gb3 s:sawtooth cutoff:500 ]",
|
||||
"[ 7/13 → 8/13 | note:G3 s:sawtooth cutoff:500 ]",
|
||||
"[ 8/13 → 9/13 | note:Ab3 s:sawtooth cutoff:500 ]",
|
||||
"[ 9/13 → 10/13 | note:A3 s:sawtooth cutoff:500 ]",
|
||||
"[ 10/13 → 11/13 | note:Bb3 s:sawtooth cutoff:500 ]",
|
||||
"[ 11/13 → 12/13 | note:B3 s:sawtooth cutoff:500 ]",
|
||||
"[ 12/13 → 1/1 | note:C4 s:sawtooth cutoff:500 ]",
|
||||
"[ 1/1 → 14/13 | note:C3 s:sawtooth cutoff:500 ]",
|
||||
"[ 14/13 → 15/13 | note:Db3 s:sawtooth cutoff:500 ]",
|
||||
"[ 15/13 → 16/13 | note:D3 s:sawtooth cutoff:500 ]",
|
||||
"[ 16/13 → 17/13 | note:Eb3 s:sawtooth cutoff:500 ]",
|
||||
"[ 17/13 → 18/13 | note:E3 s:sawtooth cutoff:500 ]",
|
||||
"[ 18/13 → 19/13 | note:F3 s:sawtooth cutoff:500 ]",
|
||||
"[ 19/13 → 20/13 | note:Gb3 s:sawtooth cutoff:500 ]",
|
||||
"[ 20/13 → 21/13 | note:G3 s:sawtooth cutoff:500 ]",
|
||||
"[ 21/13 → 22/13 | note:Ab3 s:sawtooth cutoff:500 ]",
|
||||
"[ 22/13 → 23/13 | note:A3 s:sawtooth cutoff:500 ]",
|
||||
"[ 23/13 → 24/13 | note:Bb3 s:sawtooth cutoff:500 ]",
|
||||
"[ 24/13 → 25/13 | note:B3 s:sawtooth cutoff:500 ]",
|
||||
"[ 25/13 → 2/1 | note:C4 s:sawtooth cutoff:500 ]",
|
||||
"[ 2/1 → 27/13 | note:C3 s:sawtooth cutoff:500 ]",
|
||||
"[ 27/13 → 28/13 | note:Db3 s:sawtooth cutoff:500 ]",
|
||||
"[ 28/13 → 29/13 | note:D3 s:sawtooth cutoff:500 ]",
|
||||
"[ 29/13 → 30/13 | note:Eb3 s:sawtooth cutoff:500 ]",
|
||||
"[ 30/13 → 31/13 | note:E3 s:sawtooth cutoff:500 ]",
|
||||
"[ 31/13 → 32/13 | note:F3 s:sawtooth cutoff:500 ]",
|
||||
"[ 32/13 → 33/13 | note:Gb3 s:sawtooth cutoff:500 ]",
|
||||
"[ 33/13 → 34/13 | note:G3 s:sawtooth cutoff:500 ]",
|
||||
"[ 34/13 → 35/13 | note:Ab3 s:sawtooth cutoff:500 ]",
|
||||
"[ 35/13 → 36/13 | note:A3 s:sawtooth cutoff:500 ]",
|
||||
"[ 36/13 → 37/13 | note:Bb3 s:sawtooth cutoff:500 ]",
|
||||
"[ 37/13 → 38/13 | note:B3 s:sawtooth cutoff:500 ]",
|
||||
"[ 38/13 → 3/1 | note:C4 s:sawtooth cutoff:500 ]",
|
||||
"[ 3/1 → 40/13 | note:C3 s:sawtooth cutoff:500 ]",
|
||||
"[ 40/13 → 41/13 | note:Db3 s:sawtooth cutoff:500 ]",
|
||||
"[ 41/13 → 42/13 | note:D3 s:sawtooth cutoff:500 ]",
|
||||
"[ 42/13 → 43/13 | note:Eb3 s:sawtooth cutoff:500 ]",
|
||||
"[ 43/13 → 44/13 | note:E3 s:sawtooth cutoff:500 ]",
|
||||
"[ 44/13 → 45/13 | note:F3 s:sawtooth cutoff:500 ]",
|
||||
"[ 45/13 → 46/13 | note:Gb3 s:sawtooth cutoff:500 ]",
|
||||
"[ 46/13 → 47/13 | note:G3 s:sawtooth cutoff:500 ]",
|
||||
"[ 47/13 → 48/13 | note:Ab3 s:sawtooth cutoff:500 ]",
|
||||
"[ 48/13 → 49/13 | note:A3 s:sawtooth cutoff:500 ]",
|
||||
"[ 49/13 → 50/13 | note:Bb3 s:sawtooth cutoff:500 ]",
|
||||
"[ 50/13 → 51/13 | note:B3 s:sawtooth cutoff:500 ]",
|
||||
"[ 51/13 → 4/1 | note:C4 s:sawtooth cutoff:500 ]",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "ply" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/4 | s:bd ]",
|
||||
@@ -6196,67 +6335,67 @@ exports[`runs examples > example "s_cat" example index 1 1`] = `
|
||||
|
||||
exports[`runs examples > example "s_polymeter" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/3 | c ]",
|
||||
"[ 0/1 → 1/3 | c2 ]",
|
||||
"[ 1/3 → 2/3 | eb ]",
|
||||
"[ 1/3 → 2/3 | g2 ]",
|
||||
"[ 2/3 → 1/1 | g ]",
|
||||
"[ 2/3 → 1/1 | c2 ]",
|
||||
"[ 1/1 → 4/3 | c ]",
|
||||
"[ 1/1 → 4/3 | g2 ]",
|
||||
"[ 4/3 → 5/3 | eb ]",
|
||||
"[ 4/3 → 5/3 | c2 ]",
|
||||
"[ 5/3 → 2/1 | g ]",
|
||||
"[ 5/3 → 2/1 | g2 ]",
|
||||
"[ 2/1 → 7/3 | c ]",
|
||||
"[ 2/1 → 7/3 | c2 ]",
|
||||
"[ 7/3 → 8/3 | eb ]",
|
||||
"[ 7/3 → 8/3 | g2 ]",
|
||||
"[ 8/3 → 3/1 | g ]",
|
||||
"[ 8/3 → 3/1 | c2 ]",
|
||||
"[ 3/1 → 10/3 | c ]",
|
||||
"[ 3/1 → 10/3 | g2 ]",
|
||||
"[ 10/3 → 11/3 | eb ]",
|
||||
"[ 10/3 → 11/3 | c2 ]",
|
||||
"[ 11/3 → 4/1 | g ]",
|
||||
"[ 11/3 → 4/1 | g2 ]",
|
||||
"[ 0/1 → 1/3 | note:c ]",
|
||||
"[ 0/1 → 1/3 | note:c2 ]",
|
||||
"[ 1/3 → 2/3 | note:eb ]",
|
||||
"[ 1/3 → 2/3 | note:g2 ]",
|
||||
"[ 2/3 → 1/1 | note:g ]",
|
||||
"[ 2/3 → 1/1 | note:c2 ]",
|
||||
"[ 1/1 → 4/3 | note:c ]",
|
||||
"[ 1/1 → 4/3 | note:g2 ]",
|
||||
"[ 4/3 → 5/3 | note:eb ]",
|
||||
"[ 4/3 → 5/3 | note:c2 ]",
|
||||
"[ 5/3 → 2/1 | note:g ]",
|
||||
"[ 5/3 → 2/1 | note:g2 ]",
|
||||
"[ 2/1 → 7/3 | note:c ]",
|
||||
"[ 2/1 → 7/3 | note:c2 ]",
|
||||
"[ 7/3 → 8/3 | note:eb ]",
|
||||
"[ 7/3 → 8/3 | note:g2 ]",
|
||||
"[ 8/3 → 3/1 | note:g ]",
|
||||
"[ 8/3 → 3/1 | note:c2 ]",
|
||||
"[ 3/1 → 10/3 | note:c ]",
|
||||
"[ 3/1 → 10/3 | note:g2 ]",
|
||||
"[ 10/3 → 11/3 | note:eb ]",
|
||||
"[ 10/3 → 11/3 | note:c2 ]",
|
||||
"[ 11/3 → 4/1 | note:g ]",
|
||||
"[ 11/3 → 4/1 | note:g2 ]",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "s_polymeterSteps" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/4 | c ]",
|
||||
"[ 0/1 → 1/4 | e ]",
|
||||
"[ 1/4 → 1/2 | d ]",
|
||||
"[ 1/4 → 1/2 | f ]",
|
||||
"[ 1/2 → 3/4 | c ]",
|
||||
"[ 1/2 → 3/4 | g ]",
|
||||
"[ 3/4 → 1/1 | d ]",
|
||||
"[ 3/4 → 1/1 | e ]",
|
||||
"[ 1/1 → 5/4 | c ]",
|
||||
"[ 1/1 → 5/4 | f ]",
|
||||
"[ 5/4 → 3/2 | d ]",
|
||||
"[ 5/4 → 3/2 | g ]",
|
||||
"[ 3/2 → 7/4 | c ]",
|
||||
"[ 3/2 → 7/4 | e ]",
|
||||
"[ 7/4 → 2/1 | d ]",
|
||||
"[ 7/4 → 2/1 | f ]",
|
||||
"[ 2/1 → 9/4 | c ]",
|
||||
"[ 2/1 → 9/4 | g ]",
|
||||
"[ 9/4 → 5/2 | d ]",
|
||||
"[ 9/4 → 5/2 | e ]",
|
||||
"[ 5/2 → 11/4 | c ]",
|
||||
"[ 5/2 → 11/4 | f ]",
|
||||
"[ 11/4 → 3/1 | d ]",
|
||||
"[ 11/4 → 3/1 | g ]",
|
||||
"[ 3/1 → 13/4 | c ]",
|
||||
"[ 3/1 → 13/4 | e ]",
|
||||
"[ 13/4 → 7/2 | d ]",
|
||||
"[ 13/4 → 7/2 | f ]",
|
||||
"[ 7/2 → 15/4 | c ]",
|
||||
"[ 7/2 → 15/4 | g ]",
|
||||
"[ 15/4 → 4/1 | d ]",
|
||||
"[ 15/4 → 4/1 | e ]",
|
||||
"[ 0/1 → 1/4 | note:c ]",
|
||||
"[ 0/1 → 1/4 | note:e ]",
|
||||
"[ 1/4 → 1/2 | note:d ]",
|
||||
"[ 1/4 → 1/2 | note:f ]",
|
||||
"[ 1/2 → 3/4 | note:c ]",
|
||||
"[ 1/2 → 3/4 | note:g ]",
|
||||
"[ 3/4 → 1/1 | note:d ]",
|
||||
"[ 3/4 → 1/1 | note:e ]",
|
||||
"[ 1/1 → 5/4 | note:c ]",
|
||||
"[ 1/1 → 5/4 | note:f ]",
|
||||
"[ 5/4 → 3/2 | note:d ]",
|
||||
"[ 5/4 → 3/2 | note:g ]",
|
||||
"[ 3/2 → 7/4 | note:c ]",
|
||||
"[ 3/2 → 7/4 | note:e ]",
|
||||
"[ 7/4 → 2/1 | note:d ]",
|
||||
"[ 7/4 → 2/1 | note:f ]",
|
||||
"[ 2/1 → 9/4 | note:c ]",
|
||||
"[ 2/1 → 9/4 | note:g ]",
|
||||
"[ 9/4 → 5/2 | note:d ]",
|
||||
"[ 9/4 → 5/2 | note:e ]",
|
||||
"[ 5/2 → 11/4 | note:c ]",
|
||||
"[ 5/2 → 11/4 | note:f ]",
|
||||
"[ 11/4 → 3/1 | note:d ]",
|
||||
"[ 11/4 → 3/1 | note:g ]",
|
||||
"[ 3/1 → 13/4 | note:c ]",
|
||||
"[ 3/1 → 13/4 | note:e ]",
|
||||
"[ 13/4 → 7/2 | note:d ]",
|
||||
"[ 13/4 → 7/2 | note:f ]",
|
||||
"[ 7/2 → 15/4 | note:c ]",
|
||||
"[ 7/2 → 15/4 | note:g ]",
|
||||
"[ 15/4 → 4/1 | note:d ]",
|
||||
"[ 15/4 → 4/1 | note:e ]",
|
||||
]
|
||||
`;
|
||||
|
||||
@@ -6554,10 +6693,10 @@ exports[`runs examples > example "scaleTranspose" example index 0 1`] = `
|
||||
|
||||
exports[`runs examples > example "scope" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/1 | s:sawtooth analyze:1 ]",
|
||||
"[ 1/1 → 2/1 | s:sawtooth analyze:1 ]",
|
||||
"[ 2/1 → 3/1 | s:sawtooth analyze:1 ]",
|
||||
"[ 3/1 → 4/1 | s:sawtooth analyze:1 ]",
|
||||
"[ 0/1 → 1/1 | s:sawtooth ]",
|
||||
"[ 1/1 → 2/1 | s:sawtooth ]",
|
||||
"[ 2/1 → 3/1 | s:sawtooth ]",
|
||||
"[ 3/1 → 4/1 | s:sawtooth ]",
|
||||
]
|
||||
`;
|
||||
|
||||
@@ -7158,6 +7297,39 @@ exports[`runs examples > example "speed" example index 1 1`] = `
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "spiral" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/8 | note:c2 s:sawtooth lpenv:4 cutoff:300 ]",
|
||||
"[ (1/4 → 1/3) ⇝ 3/8 | note:c2 s:sawtooth lpenv:4 cutoff:300 ]",
|
||||
"[ 1/4 ⇜ (1/3 → 3/8) | note:a2 s:sawtooth lpenv:4 cutoff:300 ]",
|
||||
"[ 3/8 → 1/2 | note:a2 s:sawtooth lpenv:4 cutoff:300 ]",
|
||||
"[ (5/8 → 2/3) ⇝ 3/4 | note:a2 s:sawtooth lpenv:4 cutoff:300 ]",
|
||||
"[ 5/8 ⇜ (2/3 → 3/4) | note:eb2 s:sawtooth lpenv:4 cutoff:300 ]",
|
||||
"[ 3/4 → 7/8 | note:eb2 s:sawtooth lpenv:4 cutoff:300 ]",
|
||||
"[ 1/1 → 9/8 | note:c2 s:sawtooth lpenv:4 cutoff:300 ]",
|
||||
"[ (5/4 → 4/3) ⇝ 11/8 | note:c2 s:sawtooth lpenv:4 cutoff:300 ]",
|
||||
"[ 5/4 ⇜ (4/3 → 11/8) | note:a2 s:sawtooth lpenv:4 cutoff:300 ]",
|
||||
"[ 11/8 → 3/2 | note:a2 s:sawtooth lpenv:4 cutoff:300 ]",
|
||||
"[ (13/8 → 5/3) ⇝ 7/4 | note:a2 s:sawtooth lpenv:4 cutoff:300 ]",
|
||||
"[ 13/8 ⇜ (5/3 → 7/4) | note:eb2 s:sawtooth lpenv:4 cutoff:300 ]",
|
||||
"[ 7/4 → 15/8 | note:eb2 s:sawtooth lpenv:4 cutoff:300 ]",
|
||||
"[ 2/1 → 17/8 | note:c2 s:sawtooth lpenv:4 cutoff:300 ]",
|
||||
"[ (9/4 → 7/3) ⇝ 19/8 | note:c2 s:sawtooth lpenv:4 cutoff:300 ]",
|
||||
"[ 9/4 ⇜ (7/3 → 19/8) | note:a2 s:sawtooth lpenv:4 cutoff:300 ]",
|
||||
"[ 19/8 → 5/2 | note:a2 s:sawtooth lpenv:4 cutoff:300 ]",
|
||||
"[ (21/8 → 8/3) ⇝ 11/4 | note:a2 s:sawtooth lpenv:4 cutoff:300 ]",
|
||||
"[ 21/8 ⇜ (8/3 → 11/4) | note:eb2 s:sawtooth lpenv:4 cutoff:300 ]",
|
||||
"[ 11/4 → 23/8 | note:eb2 s:sawtooth lpenv:4 cutoff:300 ]",
|
||||
"[ 3/1 → 25/8 | note:c2 s:sawtooth lpenv:4 cutoff:300 ]",
|
||||
"[ (13/4 → 10/3) ⇝ 27/8 | note:c2 s:sawtooth lpenv:4 cutoff:300 ]",
|
||||
"[ 13/4 ⇜ (10/3 → 27/8) | note:a2 s:sawtooth lpenv:4 cutoff:300 ]",
|
||||
"[ 27/8 → 7/2 | note:a2 s:sawtooth lpenv:4 cutoff:300 ]",
|
||||
"[ (29/8 → 11/3) ⇝ 15/4 | note:a2 s:sawtooth lpenv:4 cutoff:300 ]",
|
||||
"[ 29/8 ⇜ (11/3 → 15/4) | note:eb2 s:sawtooth lpenv:4 cutoff:300 ]",
|
||||
"[ 15/4 → 31/8 | note:eb2 s:sawtooth lpenv:4 cutoff:300 ]",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "splice" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/8 | speed:1 unit:c begin:0 end:0.125 _slices:8 s:breaks165 ]",
|
||||
|
||||
@@ -122,6 +122,19 @@ strudel.Pattern.prototype.midi = function () {
|
||||
return this;
|
||||
};
|
||||
|
||||
strudel.Pattern.prototype._scope = function () {
|
||||
return this;
|
||||
};
|
||||
strudel.Pattern.prototype._spiral = function () {
|
||||
return this;
|
||||
};
|
||||
strudel.Pattern.prototype._pitchwheel = function () {
|
||||
return this;
|
||||
};
|
||||
strudel.Pattern.prototype._pianoroll = function () {
|
||||
return this;
|
||||
};
|
||||
|
||||
const uiHelpersMocked = {
|
||||
backgroundImage: id,
|
||||
};
|
||||
@@ -178,6 +191,7 @@ evalScope(
|
||||
},
|
||||
);
|
||||
|
||||
// TBD: use transpiler to support labeled statements
|
||||
export const queryCode = async (code, cycles = 1) => {
|
||||
const { pattern } = await evaluate(code);
|
||||
const haps = pattern.sortHapsByPart().queryArc(0, cycles);
|
||||
|
||||
@@ -78,6 +78,7 @@ export const SIDEBAR: Sidebar = {
|
||||
More: [
|
||||
{ text: 'Recipes', link: 'recipes/recipes' },
|
||||
{ text: 'Mini-Notation', link: 'learn/mini-notation' },
|
||||
{ text: 'Visual Feedback', link: 'learn/visual-feedback' },
|
||||
{ text: 'Offline', link: 'learn/pwa' },
|
||||
{ text: 'Patterns', link: 'technical-manual/patterns' },
|
||||
{ text: 'Music metadata', link: 'learn/metadata' },
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { useState, useRef, useCallback, useMemo, useEffect } from 'react';
|
||||
import { Icon } from './Icon';
|
||||
import { silence, noteToMidi, _mod } from '@strudel/core';
|
||||
import { getPunchcardPainter } from '@strudel/draw';
|
||||
import { clearHydra } from '@strudel/hydra';
|
||||
import { getDrawContext, getPunchcardPainter } from '@strudel/draw';
|
||||
import { transpiler } from '@strudel/transpiler';
|
||||
import { getAudioContext, webaudioOutput, initAudioOnFirstClick } from '@strudel/webaudio';
|
||||
import { StrudelMirror } from '@strudel/codemirror';
|
||||
@@ -28,24 +29,29 @@ export function MiniRepl({
|
||||
claviature,
|
||||
claviatureLabels,
|
||||
maxHeight,
|
||||
autodraw,
|
||||
drawTime,
|
||||
}) {
|
||||
const code = tunes ? tunes[0] : tune;
|
||||
const id = useMemo(() => s4(), []);
|
||||
const canvasId = useMemo(() => `canvas-${id}`, [id]);
|
||||
const shouldDraw = !!punchcard || !!claviature;
|
||||
const shouldShowCanvas = !!punchcard;
|
||||
const drawTime = punchcard ? [0, 4] : [0, 0];
|
||||
const canvasId = shouldShowCanvas ? useMemo(() => `canvas-${id}`, [id]) : null;
|
||||
autodraw = !!punchcard || !!claviature || !!autodraw;
|
||||
drawTime = drawTime ?? punchcard ? [0, 4] : [-2, 2];
|
||||
if (claviature) {
|
||||
drawTime = [0, 0];
|
||||
}
|
||||
const [activeNotes, setActiveNotes] = useState([]);
|
||||
|
||||
const init = useCallback(({ code, shouldDraw }) => {
|
||||
const drawContext = shouldDraw ? document.querySelector('#' + canvasId)?.getContext('2d') : null;
|
||||
const init = useCallback(({ code, autodraw }) => {
|
||||
const drawContext = canvasId ? document.querySelector('#' + canvasId)?.getContext('2d') : getDrawContext();
|
||||
|
||||
const editor = new StrudelMirror({
|
||||
id,
|
||||
defaultOutput: webaudioOutput,
|
||||
getTime: () => getAudioContext().currentTime,
|
||||
transpiler,
|
||||
autodraw: !!shouldDraw,
|
||||
autodraw,
|
||||
root: containerRef.current,
|
||||
initialCode: '// LOADING',
|
||||
pattern: silence,
|
||||
@@ -56,7 +62,7 @@ export function MiniRepl({
|
||||
pat = pat.onTrigger(onTrigger, false);
|
||||
}
|
||||
if (claviature) {
|
||||
editor?.painters.push((ctx, time, haps, drawTime) => {
|
||||
pat = pat.onPaint((ctx, time, haps, drawTime) => {
|
||||
const active = haps
|
||||
.map((hap) => hap.value.note)
|
||||
.filter(Boolean)
|
||||
@@ -65,7 +71,7 @@ export function MiniRepl({
|
||||
});
|
||||
}
|
||||
if (punchcard) {
|
||||
editor?.painters.push(getPunchcardPainter({ labels: !!punchcardLabels }));
|
||||
pat = pat.punchcard({ labels: !!punchcardLabels });
|
||||
}
|
||||
return pat;
|
||||
},
|
||||
@@ -73,7 +79,12 @@ export function MiniRepl({
|
||||
onUpdateState: (state) => {
|
||||
setReplState({ ...state });
|
||||
},
|
||||
beforeEval: () => audioReady, // not doing this in prebake to make sure viz is drawn
|
||||
onToggle: (playing) => {
|
||||
if (!playing) {
|
||||
// clearHydra(); // TBD: doesn't work with multiple MiniRepl's on a page
|
||||
}
|
||||
},
|
||||
beforeStart: () => audioReady,
|
||||
afterEval: ({ code }) => setVersionDefaultsFrom(code),
|
||||
});
|
||||
// init settings
|
||||
@@ -152,7 +163,7 @@ export function MiniRepl({
|
||||
ref={(el) => {
|
||||
if (!editorRef.current) {
|
||||
containerRef.current = el;
|
||||
init({ code, shouldDraw });
|
||||
init({ code, autodraw });
|
||||
}
|
||||
}}
|
||||
></div>
|
||||
|
||||
@@ -11,15 +11,15 @@ import { JsDoc } from '../../docs/JsDoc';
|
||||
The following functions will return a pattern.
|
||||
These are the equivalents used by the Mini Notation:
|
||||
|
||||
| function | mini |
|
||||
| ------------------------------ | ---------------- |
|
||||
| `cat(x, y)` | `"<x y>"` |
|
||||
| `seq(x, y)` | `"x y"` |
|
||||
| `stack(x, y)` | `"x,y"` |
|
||||
| `timeCat([3,x],[2,y])` | `"x@3 y@2"` |
|
||||
| `polymeter([a, b, c], [x, y])` | `"{a b c, x y}"` |
|
||||
| `polymeterSteps(2, x, y, z)` | `"{x y z}%2"` |
|
||||
| `silence` | `"~"` |
|
||||
| function | mini |
|
||||
| -------------------------------- | ---------------- |
|
||||
| `cat(x, y)` | `"<x y>"` |
|
||||
| `seq(x, y)` | `"x y"` |
|
||||
| `stack(x, y)` | `"x,y"` |
|
||||
| `s_cat([3,x],[2,y])` | `"x@3 y@2"` |
|
||||
| `s_polymeter([a, b, c], [x, y])` | `"{a b c, x y}"` |
|
||||
| `s_polymeterSteps(2, x, y, z)` | `"{x y z}%2"` |
|
||||
| `silence` | `"~"` |
|
||||
|
||||
## cat
|
||||
|
||||
@@ -45,21 +45,21 @@ As a chained function:
|
||||
|
||||
<JsDoc client:idle name="Pattern.stack" h={0} hideDescription />
|
||||
|
||||
## timeCat
|
||||
## s_cat
|
||||
|
||||
<JsDoc client:idle name="timeCat" h={0} />
|
||||
<JsDoc client:idle name="s_cat" h={0} />
|
||||
|
||||
## arrange
|
||||
|
||||
<JsDoc client:idle name="arrange" h={0} />
|
||||
|
||||
## polymeter
|
||||
## s_polymeter
|
||||
|
||||
<JsDoc client:idle name="polymeter" h={0} />
|
||||
<JsDoc client:idle name="s_polymeter" h={0} />
|
||||
|
||||
## polymeterSteps
|
||||
## s_polymeterSteps
|
||||
|
||||
<JsDoc client:idle name="polymeterSteps" h={0} />
|
||||
<JsDoc client:idle name="s_polymeterSteps" h={0} />
|
||||
|
||||
## silence
|
||||
|
||||
|
||||
@@ -90,11 +90,13 @@ src(s0).kaleid(H("<4 5 6>"))
|
||||
.modulateScale(osc(2,-0.25,1))
|
||||
.out()
|
||||
//
|
||||
stack(
|
||||
s("bd*4,[hh:0:<.5 1>]*8,~ rim").bank("RolandTR909").speed(.9),
|
||||
note("[<g1!3 <bb1 <f1 d1>>>]*3").s("sawtooth")
|
||||
.room(.75).sometimes(add(note(12))).clip(.3)
|
||||
.lpa(.05).lpenv(-4).lpf(2000).lpq(8).ftype('24db')
|
||||
).fft(4)
|
||||
.scope({pos:0,smear:.95})`}
|
||||
|
||||
$: s("bd*4,[hh:0:<.5 1>]*8,~ rim").bank("RolandTR909").speed(.9)
|
||||
|
||||
$: note("[<g1!3 <bb1 <f1 d1>>>]\*3").s("sawtooth")
|
||||
|
||||
.room(.75).sometimes(add(note(12))).clip(.3)
|
||||
.lpa(.05).lpenv(-4).lpf(2000).lpq(8).ftype('24db')
|
||||
|
||||
all(x=>x.fft(4).scope({pos:0,smear:.95}))`}
|
||||
/>
|
||||
|
||||
@@ -20,11 +20,7 @@ Strudel also supports midi via [webmidi](https://npmjs.com/package/webmidi).
|
||||
Either connect a midi device or use the IAC Driver (Mac) or Midi Through Port (Linux) for internal midi messages.
|
||||
If no outputName is given, it uses the first midi output it finds.
|
||||
|
||||
<MiniRepl
|
||||
client:idle
|
||||
tune={`stack("<C^7 A7 Dm7 G7>".voicings('lefthand'), "<C3 A2 D3 G2>").note()
|
||||
.midi()`}
|
||||
/>
|
||||
<MiniRepl client:idle tune={`chord("<C^7 A7 Dm7 G7>").voicing().midi()`} />
|
||||
|
||||
In the console, you will see a log of the available MIDI devices as soon as you run the code, e.g. `Midi connected! Using "Midi Through Port-0".`
|
||||
|
||||
@@ -45,10 +41,8 @@ But you can also control cc messages separately like this:
|
||||
|
||||
<MiniRepl
|
||||
client:idle
|
||||
tune={`stack(
|
||||
note("c a f e"),
|
||||
ccv(sine.segment(16).slow(4)).ccn(74)
|
||||
).midi()`}
|
||||
tune={`$: note("c a f e").midi()
|
||||
$: ccv(sine.segment(16).slow(4)).ccn(74).midi()`}
|
||||
/>
|
||||
|
||||
# SuperDirt API
|
||||
|
||||
@@ -268,11 +268,10 @@ With it, you can enter any sample name(s) to query from [freesound.org](https://
|
||||
<MiniRepl
|
||||
client:idle
|
||||
tune={`samples('shabda:bass:4,hihat:4,rimshot:2')
|
||||
stack(
|
||||
n("0 1 2 3 0 1 2 3").s('bass'),
|
||||
n("0 1*2 2 3*2").s('hihat'),
|
||||
n("~ 0 ~ 1 ~ 0 0 1").s('rimshot')
|
||||
).clip(1)`}
|
||||
|
||||
$: n("0 1 2 3 0 1 2 3").s('bass')
|
||||
$: n("0 1*2 2 3*2").s('hihat').clip(1)
|
||||
$: n("~ 0 ~ 1 ~ 0 0 1").s('rimshot')`}
|
||||
/>
|
||||
|
||||
You can also generate artificial voice samples with any text, in multiple languages.
|
||||
@@ -282,10 +281,9 @@ Note that the language code and the gender parameters are optional and default t
|
||||
client:idle
|
||||
tune={`samples('shabda/speech:the_drum,forever')
|
||||
samples('shabda/speech/fr-FR/m:magnifique')
|
||||
stack(
|
||||
s("the_drum*2").chop(16).speed(rand.range(0.85,1.1)),
|
||||
s("forever magnifique").slow(4).late(0.125)
|
||||
)`}
|
||||
|
||||
$: s("the_drum*2").chop(16).speed(rand.range(0.85,1.1))
|
||||
$: s("forever magnifique").slow(4).late(0.125)`}
|
||||
/>
|
||||
|
||||
# Sampler Effects
|
||||
|
||||
@@ -112,29 +112,14 @@ Also, samples are always loaded from a URL rather than from the disk, although [
|
||||
## Evaluation
|
||||
|
||||
The Strudel REPL does not support [block based evaluation](https://github.com/tidalcycles/strudel/issues/34) yet.
|
||||
You can use the following "workaround" to create multiple patterns that can be turned on and off:
|
||||
You can use labeled statements and `_` to mute:
|
||||
|
||||
```
|
||||
let a = note("c a f e")
|
||||
<MiniRepl
|
||||
client:visible
|
||||
tune={`$: n("[0 .. 8]*8/9").scale("C:minor:pentatonic")
|
||||
|
||||
let b = s("bd sd")
|
||||
|
||||
stack(
|
||||
a,
|
||||
// b
|
||||
)
|
||||
```
|
||||
|
||||
Alternatively, you could write everything as one `stack` and use `.hush()` to silence a pattern:
|
||||
|
||||
```
|
||||
stack(
|
||||
note("c a f e"),
|
||||
s("bd sd").hush()
|
||||
)
|
||||
```
|
||||
|
||||
Note that strudel will always use the last statement in your code as the pattern for querying
|
||||
\_$: s("bd\*4").bank('RolandTR909')`}
|
||||
/>
|
||||
|
||||
## Tempo
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ The basic waveforms are `sine`, `sawtooth`, `square` and `triangle`, which can b
|
||||
client:idle
|
||||
tune={`note("c2 <eb2 <g2 g1>>".fast(2))
|
||||
.sound("<sawtooth square triangle sine>")
|
||||
.scope()`}
|
||||
._scope()`}
|
||||
/>
|
||||
|
||||
If you don't set a `sound` but a `note` the default value for `sound` is `triangle`!
|
||||
@@ -28,23 +28,23 @@ If you don't set a `sound` but a `note` the default value for `sound` is `triang
|
||||
You can also use noise as a source by setting the waveform to: `white`, `pink` or `brown`. These are different
|
||||
flavours of noise, here written from hard to soft.
|
||||
|
||||
<MiniRepl client:idle tune={`sound("<white pink brown>").scope()`} />
|
||||
<MiniRepl client:idle tune={`sound("<white pink brown>")._scope()`} />
|
||||
|
||||
Here's a more musical example of how to use noise for hihats:
|
||||
|
||||
<MiniRepl
|
||||
client:idle
|
||||
tune={`sound("bd*2,<white pink brown>*8")
|
||||
.decay(.04).sustain(0).scope()`}
|
||||
.decay(.04).sustain(0)._scope()`}
|
||||
/>
|
||||
|
||||
Some amount of pink noise can also be added to any oscillator by using the `noise` paremeter:
|
||||
|
||||
<MiniRepl client:idle tune={`note("c3").noise("<0.1 0.25 0.5>").scope()`} />
|
||||
<MiniRepl client:idle tune={`note("c3").noise("<0.1 0.25 0.5>")._scope()`} />
|
||||
|
||||
You can also use the `crackle` type to play some subtle noise crackles. You can control noise amount by using the `density` parameter:
|
||||
|
||||
<MiniRepl client:idle tune={`s("crackle*4").density("<0.01 0.04 0.2 0.5>".slow(2)).scope()`} />
|
||||
<MiniRepl client:idle tune={`s("crackle*4").density("<0.01 0.04 0.2 0.5>".slow(2))._scope()`} />
|
||||
|
||||
### Additive Synthesis
|
||||
|
||||
@@ -55,7 +55,7 @@ To tame the harsh sound of the basic waveforms, we can set the `n` control to li
|
||||
tune={`note("c2 <eb2 <g2 g1>>".fast(2))
|
||||
.sound("sawtooth")
|
||||
.n("<32 16 8 4>")
|
||||
.scope()`}
|
||||
._scope()`}
|
||||
/>
|
||||
|
||||
When the `n` control is used on a basic waveform, it defines the number of harmonic partials the sound is getting.
|
||||
@@ -65,7 +65,7 @@ You can also set `n` directly in mini notation with `sound`:
|
||||
client:idle
|
||||
tune={`note("c2 <eb2 <g2 g1>>".fast(2))
|
||||
.sound("sawtooth:<32 16 8 4>")
|
||||
.scope()`}
|
||||
._scope()`}
|
||||
/>
|
||||
|
||||
Note for tidal users: `n` in tidal is synonymous to `note` for synths only.
|
||||
@@ -119,13 +119,14 @@ Any sample preceded by the `wt_` prefix will be loaded as a wavetable. This mean
|
||||
|
||||
<MiniRepl
|
||||
client:idle
|
||||
tune={`samples('github:Bubobubobubobubo/Dough-Waveforms/main/');
|
||||
|
||||
tune={`samples('bubo:waveforms');
|
||||
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(4)`}
|
||||
.cutoff(2000).cutoff("<1000 2000 4000>").fast(4)
|
||||
._scope()
|
||||
`}
|
||||
/>
|
||||
|
||||
## ZZFX
|
||||
@@ -159,7 +160,7 @@ It has 20 parameters in total, here is a snippet that uses all:
|
||||
.tremolo(0) // 0-1 lfo volume modulation amount
|
||||
//.duration(.2) // overwrite strudel event duration
|
||||
//.gain(1) // change volume
|
||||
.scope() // vizualise waveform (not zzfx related)
|
||||
._scope() // vizualise waveform (not zzfx related)
|
||||
`}
|
||||
/>
|
||||
|
||||
|
||||
@@ -55,20 +55,6 @@ Transposes notes inside the scale by the number of steps:
|
||||
.note()`}
|
||||
/>
|
||||
|
||||
### voicings(range?)
|
||||
|
||||
Turns chord symbols into voicings, using the smoothest voice leading possible:
|
||||
|
||||
<MiniRepl
|
||||
client:only="react"
|
||||
tune={`stack(
|
||||
"<C^7 A7 Dm7 G7>".voicings('lefthand'),
|
||||
"<C3 A2 D3 G2>"
|
||||
).note()`}
|
||||
/>
|
||||
|
||||
Note: This function might be removed, as `voicing` (without s) is a newer implementation.
|
||||
|
||||
### rootNotes(octave = 2)
|
||||
|
||||
Turns chord symbols into root notes of chords in given octave.
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
---
|
||||
title: Visual Feedback
|
||||
layout: ../../layouts/MainLayout.astro
|
||||
---
|
||||
|
||||
import { MiniRepl } from '../../docs/MiniRepl';
|
||||
import { JsDoc } from '../../docs/JsDoc';
|
||||
|
||||
# Visual Feedback
|
||||
|
||||
There are several function that add visual feedback to your patterns.
|
||||
|
||||
## Mini Notation Highlighting
|
||||
|
||||
When you write mini notation with "double quotes" or \`backticks\`, the active parts of the mini notation will be highlighted:
|
||||
|
||||
<MiniRepl
|
||||
client:idle
|
||||
tune={`n("<0 2 1 3 2>*8")
|
||||
.scale("<A1 D2>/4:minor:pentatonic")
|
||||
.s("supersaw").lpf(300).lpenv("<4 3 2>\*4")`}
|
||||
/>
|
||||
|
||||
You can change the color as well, even pattern it:
|
||||
|
||||
<MiniRepl
|
||||
client:idle
|
||||
tune={`n("<0 2 1 3 2>*8")
|
||||
.scale("<A1 D2>/4:minor:pentatonic")
|
||||
.s("supersaw").lpf(300).lpenv("<4 3 2>*4")
|
||||
.color("cyan magenta")`}
|
||||
/>
|
||||
|
||||
## Global vs Inline Visuals
|
||||
|
||||
The following functions all come with in 2 variants.
|
||||
|
||||
**Without prefix**: renders the visual to the background of the page:
|
||||
|
||||
<MiniRepl client:idle tune={`note("c a f e").color("white").punchcard()`} />
|
||||
|
||||
**With `_` prefix**: renders the visual inside the code. Allows for multiple visuals
|
||||
|
||||
<MiniRepl client:idle tune={`note("c a f e").color("white")._punchcard()`} />
|
||||
|
||||
Here we see the 2 variants for `punchcard`. The same goes for all others below.
|
||||
To improve readability the following demos will all use the inline variant.
|
||||
|
||||
## Punchcard / Pianoroll
|
||||
|
||||
These 2 functions render a pianoroll style visual.
|
||||
The only difference between the 2 is that `pianoroll` will render the pattern directly,
|
||||
while `punchcard` will also take the transformations into account that occur afterwards:
|
||||
|
||||
<MiniRepl
|
||||
client:idle
|
||||
tune={`note("c a f e").color("white")
|
||||
._punchcard()
|
||||
.color("cyan")`}
|
||||
autodraw
|
||||
/>
|
||||
|
||||
Here, the `color` is still visible in the visual, even if it is applied after `_punchcard`.
|
||||
On the contrary, the color is not visible when using `_pianoroll`:
|
||||
|
||||
<MiniRepl
|
||||
client:idle
|
||||
tune={`note("c a f e").color("white")
|
||||
._pianoroll()
|
||||
.color("cyan")`}
|
||||
autodraw
|
||||
/>
|
||||
|
||||
import Box from '@components/Box.astro';
|
||||
|
||||
<br />
|
||||
|
||||
<Box>
|
||||
|
||||
`punchcard` is less resource intensive because it uses the same data as used for the mini notation highlighting.
|
||||
|
||||
</Box>
|
||||
|
||||
The visual can be customized by passing options. Those options are the same for both functions.
|
||||
|
||||
What follows is the API doc of all the options you can pass:
|
||||
|
||||
<JsDoc client:idle name="pianoroll" h={0} />
|
||||
|
||||
## Spiral
|
||||
|
||||
<JsDoc client:idle name="spiral" h={0} />
|
||||
|
||||
## Scope
|
||||
|
||||
<JsDoc client:idle name="scope" h={0} />
|
||||
|
||||
## Pitchwheel
|
||||
|
||||
<JsDoc client:idle name="pitchwheel" h={0} />
|
||||
@@ -60,11 +60,10 @@ We will learn how to automate with waves later...
|
||||
|
||||
<MiniRepl
|
||||
client:visible
|
||||
tune={`stack(
|
||||
sound("hh*16").gain("[.25 1]*4"),
|
||||
sound("bd*4,[~ sd:1]*2")
|
||||
) `}
|
||||
punchcard
|
||||
tune={`$: sound("hh*16").gain("[.25 1]*4")
|
||||
|
||||
$: sound("bd*4,[~ sd:1]*2")`}
|
||||
punchcard
|
||||
/>
|
||||
|
||||
<Box>
|
||||
@@ -76,31 +75,21 @@ Rhythm is all about dynamics!
|
||||
|
||||
</Box>
|
||||
|
||||
**stacks within stacks**
|
||||
|
||||
Let's combine all of the above into a little tune:
|
||||
|
||||
<MiniRepl
|
||||
client:visible
|
||||
tune={`stack(
|
||||
stack(
|
||||
sound("hh*8").gain("[.25 1]*4"),
|
||||
sound("bd*4,[~ sd:1]*2")
|
||||
),
|
||||
note("<[c2 c3]*4 [bb1 bb2]*4 [f2 f3]*4 [eb2 eb3]*4>")
|
||||
.sound("sawtooth").lpf("200 1000 200 1000"),
|
||||
note("<[c3,g3,e4] [bb2,f3,d4] [a2,f3,c4] [bb2,g3,eb4]>")
|
||||
.sound("sawtooth").vowel("<a e i o>")
|
||||
)`}
|
||||
tune={`$: sound("hh*8").gain("[.25 1]*4")
|
||||
|
||||
$: sound("bd*4,[~ sd:1]*2")
|
||||
|
||||
$: note("<[c2 c3]*4 [bb1 bb2]*4 [f2 f3]*4 [eb2 eb3]*4>")
|
||||
.sound("sawtooth").lpf("200 1000 200 1000")
|
||||
|
||||
$: note("<[c3,g3,e4] [bb2,f3,d4] [a2,f3,c4] [bb2,g3,eb4]>")
|
||||
.sound("sawtooth").vowel("<a e i o>")`}
|
||||
/>
|
||||
|
||||
<Box>
|
||||
|
||||
Try to identify the individual parts of the stacks, pay attention to where the commas are.
|
||||
The 3 parts (drums, bassline, chords) are exactly as earlier, just stacked together, separated by comma.
|
||||
|
||||
</Box>
|
||||
|
||||
**shape the sound with an adsr envelope**
|
||||
|
||||
<MiniRepl
|
||||
@@ -151,11 +140,10 @@ Can you guess what they do?
|
||||
|
||||
<MiniRepl
|
||||
client:visible
|
||||
tune={`stack(
|
||||
note("[~ [<[d3,a3,f4]!2 [d3,bb3,g4]!2> ~]]*2")
|
||||
.sound("gm_electric_guitar_muted"),
|
||||
sound("<bd rim>").bank("RolandTR707")
|
||||
).delay(".5")`}
|
||||
tune={`$: note("[~ [<[d3,a3,f4]!2 [d3,bb3,g4]!2> ~]]*2")
|
||||
.sound("gm_electric_guitar_muted").delay(.5)
|
||||
|
||||
$: sound("bd rim").bank("RolandTR707").delay(".5")`}
|
||||
/>
|
||||
|
||||
<Box>
|
||||
@@ -199,31 +187,32 @@ Add a delay too!
|
||||
|
||||
<MiniRepl
|
||||
client:visible
|
||||
tune={`stack(
|
||||
note("[~ [<[d3,a3,f4]!2 [d3,bb3,g4]!2> ~]]*2")
|
||||
.sound("gm_electric_guitar_muted").delay(.5),
|
||||
sound("<bd rim>").bank("RolandTR707").delay(.5),
|
||||
n("<4 [3@3 4] [<2 0> ~@16] ~>")
|
||||
tune={`$: note("[~ [<[d3,a3,f4]!2 [d3,bb3,g4]!2> ~]]*2")
|
||||
.sound("gm_electric_guitar_muted").delay(.5)
|
||||
|
||||
$: sound("bd rim").bank("RolandTR707").delay(.5)
|
||||
|
||||
$: n("<4 [3@3 4] [<2 0> ~@16] ~>")
|
||||
.scale("D4:minor").sound("gm_accordion:2")
|
||||
.room(2).gain(.5)
|
||||
)`}
|
||||
.room(2).gain(.5)`}
|
||||
/>
|
||||
|
||||
Let's add a bass to make this complete:
|
||||
|
||||
<MiniRepl
|
||||
client:visible
|
||||
tune={`stack(
|
||||
note("[~ [<[d3,a3,f4]!2 [d3,bb3,g4]!2> ~]]*2")
|
||||
.sound("gm_electric_guitar_muted").delay(.5),
|
||||
sound("<bd rim>").bank("RolandTR707").delay(.5),
|
||||
n("<4 [3@3 4] [<2 0> ~@16] ~>")
|
||||
tune={`$: note("[~ [<[d3,a3,f4]!2 [d3,bb3,g4]!2> ~]]*2")
|
||||
.sound("gm_electric_guitar_muted").delay(.5)
|
||||
|
||||
$: sound("bd rim").bank("RolandTR707").delay(.5)
|
||||
|
||||
$: n("<4 [3@3 4] [<2 0> ~@16] ~>")
|
||||
.scale("D4:minor").sound("gm_accordion:2")
|
||||
.room(2).gain(.4),
|
||||
n("[0 [~ 0] 4 [3 2] [0 ~] [0 ~] <0 2> ~]/2")
|
||||
.room(2).gain(.4)
|
||||
|
||||
$: n("[0 [~ 0] 4 [3 2] [0 ~] [0 ~] <0 2> ~]/2")
|
||||
.scale("D2:minor")
|
||||
.sound("sawtooth,triangle").lpf(800)
|
||||
)`}
|
||||
.sound("sawtooth,triangle").lpf(800)`}
|
||||
/>
|
||||
|
||||
<Box>
|
||||
@@ -266,7 +255,7 @@ By the way, inside Mini-Notation, `fast` is `*` and `slow` is `/`.
|
||||
|
||||
Instead of changing values stepwise, we can also control them with signals:
|
||||
|
||||
<MiniRepl client:visible tune={`sound("hh*32").gain(sine)`} punchcard punchcardLabels={false} />
|
||||
<MiniRepl client:visible tune={`sound("hh*16").gain(sine)`} punchcard punchcardLabels={false} />
|
||||
|
||||
<Box>
|
||||
|
||||
|
||||
@@ -314,11 +314,11 @@ Let's recap what we've learned in this chapter:
|
||||
|
||||
New functions:
|
||||
|
||||
| Name | Description | Example |
|
||||
| ----- | ----------------------------------- | --------------------------------------------------------------------------------- |
|
||||
| note | set pitch as number or letter | <MiniRepl client:visible tune={`note("b g e c").sound("piano")`} /> |
|
||||
| scale | interpret `n` as scale degree | <MiniRepl client:visible tune={`n("6 4 2 0").scale("C:minor").sound("piano")`} /> |
|
||||
| stack | play patterns in parallel (read on) | <MiniRepl client:visible tune={`stack(s("bd sd"),note("c eb g"))`} /> |
|
||||
| Name | Description | Example |
|
||||
| ----- | ----------------------------- | --------------------------------------------------------------------------------- |
|
||||
| note | set pitch as number or letter | <MiniRepl client:visible tune={`note("b g e c").sound("piano")`} /> |
|
||||
| scale | interpret `n` as scale degree | <MiniRepl client:visible tune={`n("6 4 2 0").scale("C:minor").sound("piano")`} /> |
|
||||
| $: | play patterns in parallel | <MiniRepl client:visible tune={'$: s("bd sd")\n$: note("c eb g")'} /> |
|
||||
|
||||
## Examples
|
||||
|
||||
@@ -356,25 +356,35 @@ New functions:
|
||||
|
||||
<Box>
|
||||
|
||||
It's called `stack` 😙
|
||||
You can use `$:` 😙
|
||||
|
||||
</Box>
|
||||
|
||||
## Playing multiple patterns
|
||||
|
||||
If you want to play multiple patterns at the same time, make sure to write `$:` before each:
|
||||
|
||||
<MiniRepl
|
||||
client:visible
|
||||
tune={`stack(
|
||||
note("<[c2 c3]*4 [bb1 bb2]*4 [f2 f3]*4 [eb2 eb3]*4>")
|
||||
.sound("gm_synth_bass_1").lpf(800),
|
||||
n(\`<
|
||||
tune={`$: note("<[c2 c3]*4 [bb1 bb2]*4 [f2 f3]*4 [eb2 eb3]*4>")
|
||||
.sound("gm_synth_bass_1").lpf(800)
|
||||
|
||||
$: n(\`<
|
||||
[~ 0] 2 [0 2] [~ 2]
|
||||
[~ 0] 1 [0 1] [~ 1]
|
||||
[~ 0] 3 [0 3] [~ 3]
|
||||
[~ 0] 2 [0 2] [~ 2]
|
||||
>*4\`).scale("C4:minor")
|
||||
.sound("gm_synth_strings_1"),
|
||||
sound("bd*4, [~ <sd cp>]*2, [~ hh]*4")
|
||||
.bank("RolandTR909")
|
||||
)`}
|
||||
.sound("gm_synth_strings_1")
|
||||
|
||||
$: sound("bd*4, [~ <sd cp>]*2, [~ hh]*4")
|
||||
.bank("RolandTR909")`}
|
||||
/>
|
||||
|
||||
<Box>
|
||||
|
||||
Try changing `$` to `_$` to mute a part!
|
||||
|
||||
</Box>
|
||||
|
||||
This is starting to sound like actual music! We have sounds, we have notes, now the last piece of the puzzle is missing: [effects](/workshop/first-effects)
|
||||
|
||||
@@ -25,20 +25,16 @@ This is the same as:
|
||||
|
||||
<MiniRepl
|
||||
client:visible
|
||||
tune={`stack(
|
||||
n("0 1 [4 3] 2 0 2 [~ 3] 4").sound("jazz").pan(0),
|
||||
n("0 1 [4 3] 2 0 2 [~ 3] 4").sound("jazz").pan(1).rev()
|
||||
)`}
|
||||
tune={`$: n("0 1 [4 3] 2 0 2 [~ 3] 4").sound("jazz").pan(0)
|
||||
$: n("0 1 [4 3] 2 0 2 [~ 3] 4").sound("jazz").pan(1).rev()`}
|
||||
/>
|
||||
|
||||
Let's visualize what happens here:
|
||||
|
||||
<MiniRepl
|
||||
client:visible
|
||||
tune={`stack(
|
||||
n("0 1 [4 3] 2 0 2 [~ 3] 4").sound("jazz").pan(0).color("cyan"),
|
||||
n("0 1 [4 3] 2 0 2 [~ 3] 4").sound("jazz").pan(1).color("magenta").rev()
|
||||
)`}
|
||||
tune={`$: n("0 1 [4 3] 2 0 2 [~ 3] 4").sound("jazz").pan(0).color("cyan")
|
||||
$: n("0 1 [4 3] 2 0 2 [~ 3] 4").sound("jazz").pan(1).color("magenta").rev()`}
|
||||
punchcard
|
||||
/>
|
||||
|
||||
@@ -56,11 +52,9 @@ This is like doing
|
||||
|
||||
<MiniRepl
|
||||
client:visible
|
||||
tune={`stack(
|
||||
note("c2, eb3 g3 [bb3 c4]").s("piano").slow(0.5).color('cyan'),
|
||||
note("c2, eb3 g3 [bb3 c4]").s("piano").slow(1).color('magenta'),
|
||||
note("c2, eb3 g3 [bb3 c4]").s("piano").slow(1.5).color('yellow')
|
||||
)`}
|
||||
tune={`$: note("c2, eb3 g3 [bb3 c4]").s("piano").slow(0.5).color('cyan')
|
||||
$: note("c2, eb3 g3 [bb3 c4]").s("piano").slow(1).color('magenta')
|
||||
$: note("c2, eb3 g3 [bb3 c4]").s("piano").slow(1.5).color('yellow')`}
|
||||
punchcard
|
||||
/>
|
||||
|
||||
@@ -110,17 +104,15 @@ We can add as often as we like:
|
||||
|
||||
<MiniRepl
|
||||
client:visible
|
||||
tune={`stack(
|
||||
n("0 [2 4] <3 5> [~ <4 1>]".add("<0 [0,2,4]>"))
|
||||
tune={`$: n("0 [2 4] <3 5> [~ <4 1>]".add("<0 [0,2,4]>"))
|
||||
.scale("C5:minor")
|
||||
.sound("gm_xylophone")
|
||||
.room(.4).delay(.125),
|
||||
note("c2 [eb3,g3]".add("<0 <1 -1>>"))
|
||||
.room(.4).delay(.125)
|
||||
$: note("c2 [eb3,g3]".add("<0 <1 -1>>"))
|
||||
.adsr("[.1 0]:.2:[1 0]")
|
||||
.sound("gm_acoustic_bass")
|
||||
.room(.5),
|
||||
n("0 1 [2 3] 2").sound("jazz").jux(rev)
|
||||
)`}
|
||||
.room(.5)
|
||||
$: n("0 1 [2 3] 2").sound("jazz").jux(rev)`}
|
||||
/>
|
||||
|
||||
**ply**
|
||||
@@ -165,7 +157,7 @@ off is also useful for modifying other sounds, and can even be nested:
|
||||
client:visible
|
||||
tune={`s("bd sd [rim bd] sd,[~ hh]*4").bank("CasioRZ1")
|
||||
.off(2/16, x=>x.speed(1.5).gain(.25)
|
||||
.off(3/16, y=>y.vowel("<a e i o>*8")))`}
|
||||
.off(3/16, y=>y.vowel("<a e i o>*8")))`}
|
||||
/>
|
||||
|
||||
| name | description | example |
|
||||
|
||||
@@ -39,7 +39,7 @@ This page is just a listing of all functions covered in the workshop!
|
||||
| --------- | ----------------------------- | --------------------------------------------------------------------------------- |
|
||||
| note | set pitch as number or letter | <MiniRepl client:visible tune={`note("b g e c").sound("piano")`} /> |
|
||||
| n + scale | set note in scale | <MiniRepl client:visible tune={`n("6 4 2 0").scale("C:minor").sound("piano")`} /> |
|
||||
| stack | play patterns in parallel | <MiniRepl client:visible tune={`stack(s("bd sd"),note("c eb g"))`} /> |
|
||||
| $: | play patterns in parallel | <MiniRepl client:visible tune={'$: s("bd sd")\n$: note("c eb g")'} /> |
|
||||
|
||||
## Audio Effects
|
||||
|
||||
|
||||
Reference in New Issue
Block a user