mirror of
https://codeberg.org/uzu/strudel
synced 2026-07-25 06:58:15 -04:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e99e53ff6d |
@@ -1,17 +0,0 @@
|
|||||||
// this barrel export is currently only used to find undocumented exports
|
|
||||||
export * from './packages/core/index.mjs';
|
|
||||||
export * from './packages/csound/index.mjs';
|
|
||||||
export * from './packages/embed/index.mjs';
|
|
||||||
export * from './packages/eval/index.mjs';
|
|
||||||
export * from './packages/midi/index.mjs';
|
|
||||||
export * from './packages/mini/index.mjs';
|
|
||||||
export * from './packages/osc/index.mjs';
|
|
||||||
export * from './packages/react/index.mjs';
|
|
||||||
export * from './packages/serial/index.mjs';
|
|
||||||
export * from './packages/soundfonts/index.mjs';
|
|
||||||
export * from './packages/tonal/index.mjs';
|
|
||||||
export * from './packages/tone/index.mjs';
|
|
||||||
export * from './packages/transpiler/index.mjs';
|
|
||||||
export * from './packages/webaudio/index.mjs';
|
|
||||||
export * from './packages/webdirt/index.mjs';
|
|
||||||
export * from './packages/xen/index.mjs';
|
|
||||||
Generated
+44
-1259
File diff suppressed because it is too large
Load Diff
@@ -22,7 +22,6 @@
|
|||||||
"lint": "eslint . --ext mjs,js --quiet",
|
"lint": "eslint . --ext mjs,js --quiet",
|
||||||
"codeformat": "prettier --write .",
|
"codeformat": "prettier --write .",
|
||||||
"format-check": "prettier --check .",
|
"format-check": "prettier --check .",
|
||||||
"report-undocumented": "npm run jsdoc-json && node undocumented.mjs > undocumented.json",
|
|
||||||
"check": "npm run format-check && npm run lint && npm run test",
|
"check": "npm run format-check && npm run lint && npm run test",
|
||||||
"iclc": "cd paper && pandoc --template=pandoc/iclc.html --citeproc --number-sections iclc2023.md -o iclc2023.html && pandoc --template=pandoc/iclc.latex --citeproc --number-sections iclc2023.md -o iclc2023.pdf"
|
"iclc": "cd paper && pandoc --template=pandoc/iclc.html --citeproc --number-sections iclc2023.md -o iclc2023.html && pandoc --template=pandoc/iclc.latex --citeproc --number-sections iclc2023.md -o iclc2023.pdf"
|
||||||
},
|
},
|
||||||
@@ -50,7 +49,6 @@
|
|||||||
"@vitest/ui": "^0.25.7",
|
"@vitest/ui": "^0.25.7",
|
||||||
"c8": "^7.12.0",
|
"c8": "^7.12.0",
|
||||||
"canvas": "^2.11.0",
|
"canvas": "^2.11.0",
|
||||||
"dependency-tree": "^9.0.0",
|
|
||||||
"eslint": "^8.28.0",
|
"eslint": "^8.28.0",
|
||||||
"events": "^3.3.0",
|
"events": "^3.3.0",
|
||||||
"gh-pages": "^4.0.0",
|
"gh-pages": "^4.0.0",
|
||||||
|
|||||||
@@ -621,15 +621,14 @@ const generic_params = [
|
|||||||
/**
|
/**
|
||||||
* Sets the room size of the reverb, see {@link room}.
|
* Sets the room size of the reverb, see {@link room}.
|
||||||
*
|
*
|
||||||
* @name roomsize
|
* @name size
|
||||||
* @synonyms size
|
|
||||||
* @param {number | Pattern} size between 0 and 10
|
* @param {number | Pattern} size between 0 and 10
|
||||||
* @example
|
* @example
|
||||||
* s("bd sd").room(.8).roomsize("<0 1 2 4 8>")
|
* s("bd sd").room(.8).size("<0 1 2 4 8>")
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
// TODO: find out why :
|
// TODO: find out why :
|
||||||
// s("bd sd").room(.8).roomsize("<0 .2 .4 .6 .8 [1,0]>").osc()
|
// s("bd sd").room(.8).size("<0 .2 .4 .6 .8 [1,0]>").osc()
|
||||||
// .. does not work. Is it because room is only one effect?
|
// .. does not work. Is it because room is only one effect?
|
||||||
[
|
[
|
||||||
'f',
|
'f',
|
||||||
@@ -820,23 +819,13 @@ const _setter = (func, name) =>
|
|||||||
};
|
};
|
||||||
|
|
||||||
generic_params.forEach(([type, name, description]) => {
|
generic_params.forEach(([type, name, description]) => {
|
||||||
controls[name] = function (...pats) {
|
controls[name] = (...pats) => _name(name, ...pats);
|
||||||
const result = _name(name, ...pats);
|
|
||||||
|
|
||||||
// Add a function for composing this control with another pattern
|
|
||||||
result.__as_function = function (pat) {
|
|
||||||
return pat[name](...pats);
|
|
||||||
};
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
Pattern.__registered.push(name);
|
|
||||||
Pattern.prototype[name] = _setter(controls[name], name);
|
Pattern.prototype[name] = _setter(controls[name], name);
|
||||||
});
|
});
|
||||||
|
|
||||||
// create custom param
|
// create custom param
|
||||||
controls.createParam = (name) => {
|
controls.createParam = (name) => {
|
||||||
const func = (...pats) => _name(name, ...pats);
|
const func = (...pats) => _name(name, ...pats);
|
||||||
Pattern.__registered.push(name);
|
|
||||||
Pattern.prototype[name] = _setter(func, name);
|
Pattern.prototype[name] = _setter(func, name);
|
||||||
return (...pats) => _name(name, ...pats);
|
return (...pats) => _name(name, ...pats);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8,10 +8,13 @@ import createClock from './zyklus.mjs';
|
|||||||
import { logger } from './logger.mjs';
|
import { logger } from './logger.mjs';
|
||||||
|
|
||||||
export class Cyclist {
|
export class Cyclist {
|
||||||
|
worker;
|
||||||
|
pattern;
|
||||||
|
started = false;
|
||||||
|
cps = 1; // TODO
|
||||||
|
getTime;
|
||||||
|
phase = 0;
|
||||||
constructor({ interval, onTrigger, onToggle, onError, getTime, latency = 0.1 }) {
|
constructor({ interval, onTrigger, onToggle, onError, getTime, latency = 0.1 }) {
|
||||||
this.started = false;
|
|
||||||
this.cps = 1; // TODO
|
|
||||||
this.phase = 0;
|
|
||||||
this.getTime = getTime;
|
this.getTime = getTime;
|
||||||
this.onToggle = onToggle;
|
this.onToggle = onToggle;
|
||||||
this.latency = latency;
|
this.latency = latency;
|
||||||
|
|||||||
@@ -59,9 +59,3 @@ export const cleanupDraw = (clearScreen = true) => {
|
|||||||
clearInterval(window.strudelScheduler);
|
clearInterval(window.strudelScheduler);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Pattern.prototype.onPaint = function (onPaint) {
|
|
||||||
// this is evil! TODO: add pattern.context
|
|
||||||
this.context = { onPaint };
|
|
||||||
return this;
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ const _bjork = function (n, x) {
|
|||||||
return Math.min(ons, offs) <= 1 ? [n, x] : _bjork(...(ons > offs ? left(n, x) : right(n, x)));
|
return Math.min(ons, offs) <= 1 ? [n, x] : _bjork(...(ons > offs ? left(n, x) : right(n, x)));
|
||||||
};
|
};
|
||||||
|
|
||||||
export const bjork = function (ons, steps) {
|
const bjork = function (ons, steps) {
|
||||||
const offs = steps - ons;
|
const offs = steps - ons;
|
||||||
const x = Array(ons).fill([1]);
|
const x = Array(ons).fill([1]);
|
||||||
const y = Array(offs).fill([0]);
|
const y = Array(offs).fill([0]);
|
||||||
|
|||||||
+87
-336
@@ -23,40 +23,24 @@ export const setStringParser = (parser) => (stringParser = parser);
|
|||||||
|
|
||||||
/** @class Class representing a pattern. */
|
/** @class Class representing a pattern. */
|
||||||
export class Pattern {
|
export class Pattern {
|
||||||
|
_Pattern = true; // this property is used to detect if a pattern that fails instanceof Pattern is an instance of another Pattern
|
||||||
/**
|
/**
|
||||||
* Create a pattern. As an end user, you will most likely not create a Pattern directly.
|
* Create a pattern. As an end user, you will most likely not create a Pattern directly.
|
||||||
*
|
*
|
||||||
* @param {function} query - The function that maps a {@link State} to an array of {@link Hap}.
|
* @param {function} query - The function that maps a {@link State} to an array of {@link Hap}.
|
||||||
*/
|
*/
|
||||||
constructor(query, as_function) {
|
constructor(query) {
|
||||||
this.query = query;
|
this.query = query;
|
||||||
this._Pattern = true; // this property is used to detect if a pattern that fails instanceof Pattern is an instance of another Pattern
|
|
||||||
this.__as_function = as_function;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO - would a default 'as_function' be useful?
|
|
||||||
// /**
|
|
||||||
// * Accessor for pattern-as-function
|
|
||||||
// */
|
|
||||||
// get as_function() {
|
|
||||||
// if (!this.__as_function) {
|
|
||||||
// // TODO - add other alignments
|
|
||||||
// this.__as_function = x => x.set(this);
|
|
||||||
// }
|
|
||||||
// return this.__as_function;
|
|
||||||
// }
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
// Haskell-style functor, applicative and monadic operations
|
// Haskell-style functor, applicative and monadic operations
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a new pattern, with the function applied to the value of
|
* Returns a new pattern, with the function applied to the value of
|
||||||
* each hap. It has the alias {@link Pattern#fmap}.
|
* each hap. It has the alias {@link Pattern#fmap}.
|
||||||
* @synonyms fmap
|
* @param {Function} func
|
||||||
* @param {Function} func to to apply to the value
|
|
||||||
* @returns Pattern
|
* @returns Pattern
|
||||||
* @example
|
|
||||||
* "0 1 2".withValue(v => v + 10).log()
|
|
||||||
*/
|
*/
|
||||||
withValue(func) {
|
withValue(func) {
|
||||||
return new Pattern((state) => this.query(state).map((hap) => hap.withValue(func)));
|
return new Pattern((state) => this.query(state).map((hap) => hap.withValue(func)));
|
||||||
@@ -69,19 +53,10 @@ export class Pattern {
|
|||||||
return this.withValue(func);
|
return this.withValue(func);
|
||||||
}
|
}
|
||||||
|
|
||||||
applyValue(func) {
|
|
||||||
return this.withValue((x) => x(func));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Assumes 'this' is a pattern of functions, and given a function to
|
|
||||||
* resolve wholes, applies a given pattern of values to that
|
|
||||||
* pattern of functions.
|
|
||||||
* @param {Function} whole_func
|
|
||||||
* @param {Function} func
|
|
||||||
* @returns Pattern
|
|
||||||
*/
|
|
||||||
appWhole(whole_func, pat_val) {
|
appWhole(whole_func, pat_val) {
|
||||||
|
// Assumes 'this' is a pattern of functions, and given a function to
|
||||||
|
// resolve wholes, applies a given pattern of values to that
|
||||||
|
// pattern of functions.
|
||||||
const pat_func = this;
|
const pat_func = this;
|
||||||
const query = function (state) {
|
const query = function (state) {
|
||||||
const hap_funcs = pat_func.query(state);
|
const hap_funcs = pat_func.query(state);
|
||||||
@@ -351,12 +326,7 @@ export class Pattern {
|
|||||||
* silence
|
* silence
|
||||||
*/
|
*/
|
||||||
queryArc(begin, end) {
|
queryArc(begin, end) {
|
||||||
try {
|
|
||||||
return this.query(new State(new TimeSpan(begin, end)));
|
return this.query(new State(new TimeSpan(begin, end)));
|
||||||
} catch (err) {
|
|
||||||
logger(`[query]: ${err.message}`, 'error');
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -709,7 +679,6 @@ export class Pattern {
|
|||||||
* Layers the result of the given function(s). Like {@link Pattern.superimpose}, but without the original pattern:
|
* Layers the result of the given function(s). Like {@link Pattern.superimpose}, but without the original pattern:
|
||||||
* @name layer
|
* @name layer
|
||||||
* @memberof Pattern
|
* @memberof Pattern
|
||||||
* @synonyms apply
|
|
||||||
* @returns Pattern
|
* @returns Pattern
|
||||||
* @example
|
* @example
|
||||||
* "<0 2 4 6 ~ 4 ~ 2 0!3 ~!5>*4"
|
* "<0 2 4 6 ~ 4 ~ 2 0!3 ~!5>*4"
|
||||||
@@ -755,10 +724,9 @@ export class Pattern {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Appends the given pattern(s) to the current pattern.
|
* Appends the given pattern(s) to the current pattern. Synonyms: .sequence .fastcat
|
||||||
* @name seq
|
* @name seq
|
||||||
* @memberof Pattern
|
* @memberof Pattern
|
||||||
* @synonyms sequence, fastcat
|
|
||||||
* @example
|
* @example
|
||||||
* s("hh*2").seq(
|
* s("hh*2").seq(
|
||||||
* note("c2(3,8)")
|
* note("c2(3,8)")
|
||||||
@@ -769,10 +737,9 @@ export class Pattern {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Appends the given pattern(s) to the next cycle.
|
* Appends the given pattern(s) to the next cycle. Synonym: .slowcat
|
||||||
* @name cat
|
* @name cat
|
||||||
* @memberof Pattern
|
* @memberof Pattern
|
||||||
* @synonyms slowcat
|
|
||||||
* @example
|
* @example
|
||||||
* s("hh*2").cat(
|
* s("hh*2").cat(
|
||||||
* note("c2(3,8)")
|
* note("c2(3,8)")
|
||||||
@@ -855,25 +822,15 @@ Pattern.prototype.collect = function () {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
// applies func to each array of congruent haps
|
||||||
* Selects indices in in stacked notes.
|
|
||||||
* @example
|
|
||||||
* note("<[c,eb,g]!2 [c,f,ab] [d,f,ab]>")
|
|
||||||
* .arpWith(haps => haps[2])
|
|
||||||
* */
|
|
||||||
Pattern.prototype.arpWith = function (func) {
|
Pattern.prototype.arpWith = function (func) {
|
||||||
return this.collect()
|
return this.collect()
|
||||||
.fmap((v) => reify(func(v)))
|
.fmap((v) => reify(func(v)))
|
||||||
.innerJoin()
|
.squeezeJoin()
|
||||||
.withHap((h) => new Hap(h.whole, h.part, h.value.value, h.combineContext(h.value)));
|
.withHap((h) => new Hap(h.whole, h.part, h.value.value, h.combineContext(h.value)));
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
// applies pattern of indices to each array of congruent haps
|
||||||
* Selects indices in in stacked notes.
|
|
||||||
* @example
|
|
||||||
* note("<[c,eb,g]!2 [c,f,ab] [d,f,ab]>")
|
|
||||||
* .arp("0 [0,2] 1 [0,2]").slow(2)
|
|
||||||
* */
|
|
||||||
Pattern.prototype.arp = function (pat) {
|
Pattern.prototype.arp = function (pat) {
|
||||||
return this.arpWith((haps) => pat.fmap((i) => haps[i % haps.length]));
|
return this.arpWith((haps) => pat.fmap((i) => haps[i % haps.length]));
|
||||||
};
|
};
|
||||||
@@ -1033,6 +990,9 @@ function _composeOp(a, b, func) {
|
|||||||
/**
|
/**
|
||||||
* Applies the given structure to the pattern:
|
* Applies the given structure to the pattern:
|
||||||
*
|
*
|
||||||
|
* @name struct
|
||||||
|
* @memberof Pattern
|
||||||
|
* @returns Pattern
|
||||||
* @example
|
* @example
|
||||||
* note("c3,eb3,g3")
|
* note("c3,eb3,g3")
|
||||||
* .struct("x ~ x ~ ~ x ~ x ~ ~ ~ x ~ x ~ ~")
|
* .struct("x ~ x ~ ~ x ~ x ~ ~ ~ x ~ x ~ ~")
|
||||||
@@ -1044,37 +1004,18 @@ function _composeOp(a, b, func) {
|
|||||||
Pattern.prototype.structAll = function (...args) {
|
Pattern.prototype.structAll = function (...args) {
|
||||||
return this.keep.out(...args);
|
return this.keep.out(...args);
|
||||||
};
|
};
|
||||||
/**
|
|
||||||
* Returns silence when mask is 0 or "~"
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* note("c [eb,g] d [eb,g]").mask("<1 [0 1]>").slow(2)
|
|
||||||
*/
|
|
||||||
Pattern.prototype.mask = function (...args) {
|
Pattern.prototype.mask = function (...args) {
|
||||||
return this.keepif.in(...args);
|
return this.keepif.in(...args);
|
||||||
};
|
};
|
||||||
Pattern.prototype.maskAll = function (...args) {
|
Pattern.prototype.maskAll = function (...args) {
|
||||||
return this.keep.in(...args);
|
return this.keep.in(...args);
|
||||||
};
|
};
|
||||||
/**
|
|
||||||
* Resets the pattern to the start of the cycle for each onset of the reset pattern.
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* s("<bd lt> sd, hh*4").reset("<x@3 x(3,8)>")
|
|
||||||
*/
|
|
||||||
Pattern.prototype.reset = function (...args) {
|
Pattern.prototype.reset = function (...args) {
|
||||||
return this.keepif.trig(...args);
|
return this.keepif.trig(...args);
|
||||||
};
|
};
|
||||||
Pattern.prototype.resetAll = function (...args) {
|
Pattern.prototype.resetAll = function (...args) {
|
||||||
return this.keep.trig(...args);
|
return this.keep.trig(...args);
|
||||||
};
|
};
|
||||||
/**
|
|
||||||
* Restarts the pattern for each onset of the restart pattern.
|
|
||||||
* While reset will only reset the current cycle, restart will start from cycle 0.
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* s("<bd lt> sd, hh*4").restart("<x@3 x(3,8)>")
|
|
||||||
*/
|
|
||||||
Pattern.prototype.restart = function (...args) {
|
Pattern.prototype.restart = function (...args) {
|
||||||
return this.keepif.trigzero(...args);
|
return this.keepif.trigzero(...args);
|
||||||
};
|
};
|
||||||
@@ -1088,7 +1029,6 @@ export const polyrhythm = stack;
|
|||||||
export const pr = stack;
|
export const pr = stack;
|
||||||
|
|
||||||
// methods that create patterns, which are added to patternified Pattern methods
|
// methods that create patterns, which are added to patternified Pattern methods
|
||||||
// TODO: remove? this is only used in old transpiler (shapeshifter)
|
|
||||||
Pattern.prototype.factories = {
|
Pattern.prototype.factories = {
|
||||||
pure,
|
pure,
|
||||||
stack,
|
stack,
|
||||||
@@ -1158,7 +1098,6 @@ export function reify(thing) {
|
|||||||
/** The given items are played at the same time at the same length.
|
/** The given items are played at the same time at the same length.
|
||||||
*
|
*
|
||||||
* @return {Pattern}
|
* @return {Pattern}
|
||||||
* @synonyms polyrhythm, pr
|
|
||||||
* @example
|
* @example
|
||||||
* stack(g3, b3, [e4, d4]).note() // "g3,b3,[e4,d4]".note()
|
* stack(g3, b3, [e4, d4]).note() // "g3,b3,[e4,d4]".note()
|
||||||
*/
|
*/
|
||||||
@@ -1213,10 +1152,24 @@ export function slowcatPrime(...pats) {
|
|||||||
return new Pattern(query).splitQueries();
|
return new Pattern(query).splitQueries();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** The given items are con**cat**enated, where each one takes one cycle.
|
/** Concatenation: as with {@link slowcat}, but squashes a cycle from each pattern into one cycle
|
||||||
|
*
|
||||||
|
* Synonyms: {@link Pattern.seq}, {@link Pattern.sequence}
|
||||||
|
*
|
||||||
|
* @param {...any} items - The items to concatenate
|
||||||
|
* @return {Pattern}
|
||||||
|
* @example
|
||||||
|
* fastcat(e5, b4, [d5, c5])
|
||||||
|
* // sequence(e5, b4, [d5, c5])
|
||||||
|
* // seq(e5, b4, [d5, c5])
|
||||||
|
*/
|
||||||
|
export function fastcat(...pats) {
|
||||||
|
return slowcat(...pats)._fast(pats.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The given items are con**cat**enated, where each one takes one cycle. Synonym: slowcat
|
||||||
*
|
*
|
||||||
* @param {...any} items - The items to concatenate
|
* @param {...any} items - The items to concatenate
|
||||||
* @synonyms slowcat
|
|
||||||
* @return {Pattern}
|
* @return {Pattern}
|
||||||
* @example
|
* @example
|
||||||
* cat(e5, b4, [d5, c5]).note() // "<e5 b4 [d5 c5]>".note()
|
* cat(e5, b4, [d5, c5]).note() // "<e5 b4 [d5 c5]>".note()
|
||||||
@@ -1243,17 +1196,12 @@ export function timeCat(...timepats) {
|
|||||||
return stack(...pats);
|
return stack(...pats);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function fastcat(...pats) {
|
|
||||||
return slowcat(...pats)._fast(pats.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** See {@link fastcat} */
|
/** See {@link fastcat} */
|
||||||
export function sequence(...pats) {
|
export function sequence(...pats) {
|
||||||
return fastcat(...pats);
|
return fastcat(...pats);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Like **cat**, but the items are crammed into one cycle.
|
/** Like **cat**, but the items are crammed into one cycle. Synonyms: fastcat, sequence
|
||||||
* @synonyms fastcat, sequence
|
|
||||||
* @example
|
* @example
|
||||||
* seq(e5, b4, [d5, c5]).note() // "e5 b4 [d5 c5]".note()
|
* seq(e5, b4, [d5, c5]).note() // "e5 b4 [d5 c5]".note()
|
||||||
*
|
*
|
||||||
@@ -1309,7 +1257,7 @@ export function polymeterSteps(steps, ...args) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Combines the given lists of patterns with the same pulse. This will create so called polymeters when different sized sequences are used.
|
* Combines the given lists of patterns with the same pulse. This will create so called polymeters when different sized sequences are used.
|
||||||
* @synonyms pm
|
* @name polymeter
|
||||||
* @example
|
* @example
|
||||||
* polymeter(["c", "eb", "g"], ["c2", "g2"]).note()
|
* polymeter(["c", "eb", "g"], ["c2", "g2"]).note()
|
||||||
* // "{c eb g, c2 g2}".note()
|
* // "{c eb g, c2 g2}".note()
|
||||||
@@ -1355,18 +1303,11 @@ export const and = curry((a, b) => reify(b).and(a));
|
|||||||
export const or = curry((a, b) => reify(b).or(a));
|
export const or = curry((a, b) => reify(b).or(a));
|
||||||
export const func = curry((a, b) => reify(b).func(a));
|
export const func = curry((a, b) => reify(b).func(a));
|
||||||
|
|
||||||
/**
|
export function register(name, func) {
|
||||||
* Registers a new pattern method. The method is added to the Pattern class + the standalone function is returned from register.
|
|
||||||
*
|
|
||||||
* @param {string} name name of the function
|
|
||||||
* @param {function} func function with 1 or more params, where last is the current pattern
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
export function register(name, func, f_params = null) {
|
|
||||||
if (Array.isArray(name)) {
|
if (Array.isArray(name)) {
|
||||||
const result = {};
|
const result = {};
|
||||||
for (const name_item of name) {
|
for (const name_item of name) {
|
||||||
result[name_item] = register(name_item, func, f_params);
|
result[name_item] = register(name_item, func);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -1390,32 +1331,9 @@ export function register(name, func, f_params = null) {
|
|||||||
return func(...args, pat);
|
return func(...args, pat);
|
||||||
};
|
};
|
||||||
mapFn = curry(mapFn, null, arity - 1);
|
mapFn = curry(mapFn, null, arity - 1);
|
||||||
|
return right.reduce((acc, p) => acc.appLeft(p), left.fmap(mapFn)).innerJoin();
|
||||||
// Don't use applicative for arguments that a) have a '__as_function' function and b) are
|
|
||||||
// marked as being a higher order function parameter
|
|
||||||
function app(acc, p, i) {
|
|
||||||
if (f_params != null && f_params.length > i + 1 && f_params[i + 1] && '__as_function' in p) {
|
|
||||||
return acc.applyValue(p.__as_function);
|
|
||||||
}
|
|
||||||
return acc.appLeft(p);
|
|
||||||
}
|
|
||||||
|
|
||||||
var start;
|
|
||||||
// Do the same check for the first parameter.. a bit repetitive
|
|
||||||
if (f_params != null && f_params.length > 0 && f_params[0] && '__as_function' in left) {
|
|
||||||
start = pure(mapFn(left.__as_function));
|
|
||||||
} else {
|
|
||||||
start = left.fmap(mapFn);
|
|
||||||
}
|
|
||||||
|
|
||||||
return right.reduce(app, start).innerJoin();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!Pattern.__registered) {
|
|
||||||
Pattern.__registered = [];
|
|
||||||
}
|
|
||||||
Pattern.__registered.push(name);
|
|
||||||
|
|
||||||
Pattern.prototype[name] = function (...args) {
|
Pattern.prototype[name] = function (...args) {
|
||||||
args = args.map(reify);
|
args = args.map(reify);
|
||||||
// For methods that take a single argument (plus 'this'), allow
|
// For methods that take a single argument (plus 'this'), allow
|
||||||
@@ -1425,19 +1343,7 @@ export function register(name, func, f_params = null) {
|
|||||||
} else if (arity !== args.length + 1) {
|
} else if (arity !== args.length + 1) {
|
||||||
throw new Error(`.${name}() expects ${arity - 1} inputs but got ${args.length}.`);
|
throw new Error(`.${name}() expects ${arity - 1} inputs but got ${args.length}.`);
|
||||||
}
|
}
|
||||||
const result = pfunc(...args, this);
|
return pfunc(...args, this);
|
||||||
|
|
||||||
// speed(2,3).__as_function(pat) = pat.speed(2,3)
|
|
||||||
// and so
|
|
||||||
// speed(2,3).fast(2).__as_function(pat) = pat.speed(2,3).fast(2)
|
|
||||||
if ('__as_function' in this) {
|
|
||||||
const pata = this;
|
|
||||||
result.__as_function = function (patb) {
|
|
||||||
return pata.__as_function(patb)[name](...args);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (arity > 1) {
|
if (arity > 1) {
|
||||||
@@ -1529,27 +1435,23 @@ export const range = register('range', function (min, max, pat) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Assumes a numerical pattern, containing unipolar values in the range 0 .. 1
|
* Assumes a numerical pattern, containing unipolar values in the range 0 ..
|
||||||
* Returns a new pattern with values scaled to the given min/max range,
|
* 1. Returns a new pattern with values scaled to the given min/max range,
|
||||||
* following an exponential curve.
|
* following an exponential curve.
|
||||||
* @name rangex
|
* @param {Number} min
|
||||||
* @memberof Pattern
|
* @param {Number} max
|
||||||
* @returns Pattern
|
* @returns Pattern
|
||||||
* @example
|
|
||||||
* s("bd sd,hh*4").cutoff(sine.rangex(500,2000).slow(4))
|
|
||||||
*/
|
*/
|
||||||
export const rangex = register('rangex', function (min, max, pat) {
|
export const rangex = register('rangex', function (min, max, pat) {
|
||||||
return pat._range(Math.log(min), Math.log(max)).fmap(Math.exp);
|
return pat._range(Math.log(min), Math.log(max)).fmap(Math.exp);
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Assumes a numerical pattern, containing bipolar values in the range -1 .. 1
|
* Assumes a numerical pattern, containing bipolar values in the range -1 ..
|
||||||
* Returns a new pattern with values scaled to the given min/max range.
|
* 1. Returns a new pattern with values scaled to the given min/max range.
|
||||||
* @name range2
|
* @param {Number} min
|
||||||
* @memberof Pattern
|
* @param {Number} max
|
||||||
* @returns Pattern
|
* @returns Pattern
|
||||||
* @example
|
|
||||||
* s("bd sd,hh*4").cutoff(sine2.range2(500,2000).slow(4))
|
|
||||||
*/
|
*/
|
||||||
export const range2 = register('range2', function (min, max, pat) {
|
export const range2 = register('range2', function (min, max, pat) {
|
||||||
return pat.fromBipolar()._range(min, max);
|
return pat.fromBipolar()._range(min, max);
|
||||||
@@ -1558,16 +1460,8 @@ export const range2 = register('range2', function (min, max, pat) {
|
|||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
// Structural and temporal transformations
|
// Structural and temporal transformations
|
||||||
|
|
||||||
/** Compress each cycle into the given timespan, leaving a gap
|
// Compress each cycle into the given timespan, leaving a gap
|
||||||
* @example
|
|
||||||
* cat(
|
|
||||||
* s("bd sd").compress(.25,.75),
|
|
||||||
* s("~ bd sd ~")
|
|
||||||
* )
|
|
||||||
*/
|
|
||||||
export const compress = register('compress', function (b, e, pat) {
|
export const compress = register('compress', function (b, e, pat) {
|
||||||
b = Fraction(b);
|
|
||||||
e = Fraction(e);
|
|
||||||
if (b.gt(e) || b.gt(1) || e.gt(1) || b.lt(0) || e.lt(0)) {
|
if (b.gt(e) || b.gt(1) || e.gt(1) || b.lt(0) || e.lt(0)) {
|
||||||
return silence;
|
return silence;
|
||||||
}
|
}
|
||||||
@@ -1578,13 +1472,6 @@ export const { compressSpan, compressspan } = register(['compressSpan', 'compres
|
|||||||
return pat._compress(span.begin, span.end);
|
return pat._compress(span.begin, span.end);
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
|
||||||
* 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
|
|
||||||
* @synonyms fastgap
|
|
||||||
* @example
|
|
||||||
* s("bd sd").fastGap(2)
|
|
||||||
*/
|
|
||||||
export const { fastGap, fastgap } = register(['fastGap', 'fastgap'], function (factor, pat) {
|
export const { fastGap, fastgap } = register(['fastGap', 'fastgap'], function (factor, pat) {
|
||||||
// A bit fiddly, to drop zero-width queries at the start of the next cycle
|
// A bit fiddly, to drop zero-width queries at the start of the next cycle
|
||||||
const qf = function (span) {
|
const qf = function (span) {
|
||||||
@@ -1615,14 +1502,10 @@ export const { fastGap, fastgap } = register(['fastGap', 'fastgap'], function (f
|
|||||||
return pat.withQuerySpanMaybe(qf).withHap(ef).splitQueries();
|
return pat.withQuerySpanMaybe(qf).withHap(ef).splitQueries();
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
// Similar to compress, but doesn't leave gaps, and the 'focus' can be
|
||||||
* Similar to compress, but doesn't leave gaps, and the 'focus' can be bigger than a cycle
|
// bigger than a cycle
|
||||||
* @example
|
|
||||||
* s("bd hh sd hh").focus(1/4, 3/4)
|
|
||||||
*/
|
|
||||||
export const focus = register('focus', function (b, e, pat) {
|
export const focus = register('focus', function (b, e, pat) {
|
||||||
b = Fraction(b);
|
|
||||||
e = Fraction(e);
|
|
||||||
return pat._fast(Fraction(1).div(e.sub(b))).late(b.cyclePos());
|
return pat._fast(Fraction(1).div(e.sub(b))).late(b.cyclePos());
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1630,10 +1513,6 @@ export const { focusSpan, focusspan } = register(['focusSpan', 'focusspan'], fun
|
|||||||
return pat._focus(span.begin, span.end);
|
return pat._focus(span.begin, span.end);
|
||||||
});
|
});
|
||||||
|
|
||||||
/** The ply function repeats each event the given number of times.
|
|
||||||
* @example
|
|
||||||
* s("bd ~ sd cp").ply("<1 2 3>")
|
|
||||||
*/
|
|
||||||
export const ply = register('ply', function (factor, pat) {
|
export const ply = register('ply', function (factor, pat) {
|
||||||
return pat.fmap((x) => pure(x)._fast(factor)).squeezeJoin();
|
return pat.fmap((x) => pure(x)._fast(factor)).squeezeJoin();
|
||||||
});
|
});
|
||||||
@@ -1642,7 +1521,6 @@ export const ply = register('ply', function (factor, pat) {
|
|||||||
* Speed up a pattern by the given factor. Used by "*" in mini notation.
|
* Speed up a pattern by the given factor. Used by "*" in mini notation.
|
||||||
*
|
*
|
||||||
* @name fast
|
* @name fast
|
||||||
* @synonyms density
|
|
||||||
* @memberof Pattern
|
* @memberof Pattern
|
||||||
* @param {number | Pattern} factor speed up factor
|
* @param {number | Pattern} factor speed up factor
|
||||||
* @returns Pattern
|
* @returns Pattern
|
||||||
@@ -1659,7 +1537,6 @@ export const { fast, density } = register(['fast', 'density'], function (factor,
|
|||||||
* Slow down a pattern over the given number of cycles. Like the "/" operator in mini notation.
|
* Slow down a pattern over the given number of cycles. Like the "/" operator in mini notation.
|
||||||
*
|
*
|
||||||
* @name slow
|
* @name slow
|
||||||
* @synonyms sparsity
|
|
||||||
* @memberof Pattern
|
* @memberof Pattern
|
||||||
* @param {number | Pattern} factor slow down factor
|
* @param {number | Pattern} factor slow down factor
|
||||||
* @returns Pattern
|
* @returns Pattern
|
||||||
@@ -1670,33 +1547,15 @@ export const { slow, sparsity } = register(['slow', 'sparsity'], function (facto
|
|||||||
return pat._fast(Fraction(1).div(factor));
|
return pat._fast(Fraction(1).div(factor));
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
// Should these really be in alphabetical order? a shame to split
|
||||||
* Carries out an operation 'inside' a cycle.
|
// fast/slow, inside/outside..
|
||||||
* @example
|
export const inside = register('inside', function (factor, f, pat) {
|
||||||
* "0 1 2 3 4 3 2 1".inside(4, rev).scale('C major').note()
|
|
||||||
* // "0 1 2 3 4 3 2 1".slow(4).rev().fast(4).scale('C major').note()
|
|
||||||
*/
|
|
||||||
export const inside = register(
|
|
||||||
'inside',
|
|
||||||
function (factor, f, pat) {
|
|
||||||
return f(pat._slow(factor))._fast(factor);
|
return f(pat._slow(factor))._fast(factor);
|
||||||
},
|
});
|
||||||
[false, true],
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
export const outside = register('outside', function (factor, f, pat) {
|
||||||
* Carries out an operation 'outside' a cycle.
|
|
||||||
* @example
|
|
||||||
* "<[0 1] 2 [3 4] 5>".outside(4, rev).scale('C major').note()
|
|
||||||
* // "<[0 1] 2 [3 4] 5>".fast(4).rev().slow(4).scale('C major').note()
|
|
||||||
*/
|
|
||||||
export const outside = register(
|
|
||||||
'outside',
|
|
||||||
function (factor, f, pat) {
|
|
||||||
return f(pat._fast(factor))._slow(factor);
|
return f(pat._fast(factor))._slow(factor);
|
||||||
},
|
});
|
||||||
[false, true],
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Applies the given function every n cycles, starting from the last cycle.
|
* Applies the given function every n cycles, starting from the last cycle.
|
||||||
@@ -1708,16 +1567,11 @@ export const outside = register(
|
|||||||
* @example
|
* @example
|
||||||
* note("c3 d3 e3 g3").lastOf(4, x=>x.rev())
|
* note("c3 d3 e3 g3").lastOf(4, x=>x.rev())
|
||||||
*/
|
*/
|
||||||
export const lastOf = register(
|
export const lastOf = register('lastOf', function (n, func, pat) {
|
||||||
'lastOf',
|
|
||||||
function (n, func, pat) {
|
|
||||||
const pats = Array(n - 1).fill(pat);
|
const pats = Array(n - 1).fill(pat);
|
||||||
pats.push(func(pat));
|
pats.push(func(pat));
|
||||||
return slowcatPrime(...pats);
|
return slowcatPrime(...pats);
|
||||||
},
|
});
|
||||||
// second parameter is a function
|
|
||||||
[false, true],
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Applies the given function every n cycles, starting from the first cycle.
|
* Applies the given function every n cycles, starting from the first cycle.
|
||||||
@@ -1740,16 +1594,11 @@ export const lastOf = register(
|
|||||||
* @example
|
* @example
|
||||||
* note("c3 d3 e3 g3").every(4, x=>x.rev())
|
* note("c3 d3 e3 g3").every(4, x=>x.rev())
|
||||||
*/
|
*/
|
||||||
export const { firstOf, every } = register(
|
export const { firstOf, every } = register(['firstOf', 'every'], function (n, func, pat) {
|
||||||
['firstOf', 'every'],
|
|
||||||
function (n, func, pat) {
|
|
||||||
const pats = Array(n - 1).fill(pat);
|
const pats = Array(n - 1).fill(pat);
|
||||||
pats.unshift(func(pat));
|
pats.unshift(func(pat));
|
||||||
return slowcatPrime(...pats);
|
return slowcatPrime(...pats);
|
||||||
},
|
});
|
||||||
// second parameter is a function
|
|
||||||
[false, true],
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Like layer, but with a single function:
|
* Like layer, but with a single function:
|
||||||
@@ -1758,20 +1607,11 @@ export const { firstOf, every } = register(
|
|||||||
* @example
|
* @example
|
||||||
* "<c3 eb3 g3>".scale('C minor').apply(scaleTranspose("0,2,4")).note()
|
* "<c3 eb3 g3>".scale('C minor').apply(scaleTranspose("0,2,4")).note()
|
||||||
*/
|
*/
|
||||||
// TODO: remove or dedupe with layer?
|
export const apply = register('apply', function (func, pat) {
|
||||||
export const apply = register(
|
|
||||||
'apply',
|
|
||||||
function (func, pat) {
|
|
||||||
return func(pat);
|
return func(pat);
|
||||||
},
|
});
|
||||||
[true],
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
// cpm = cycles per minute
|
||||||
* Plays the pattern at the given cycles per minute.
|
|
||||||
* @example
|
|
||||||
* s("<bd sd>,hh*2").cpm(90) // = 90 bpm
|
|
||||||
*/
|
|
||||||
// TODO - global clock
|
// TODO - global clock
|
||||||
export const cpm = register('cpm', function (cpm, pat) {
|
export const cpm = register('cpm', function (cpm, pat) {
|
||||||
return pat._fast(cpm / 60);
|
return pat._fast(cpm / 60);
|
||||||
@@ -1807,13 +1647,6 @@ export const late = register('late', function (offset, pat) {
|
|||||||
return pat._early(Fraction(0).sub(offset));
|
return pat._early(Fraction(0).sub(offset));
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
|
||||||
* Plays a portion of a pattern, specified by the beginning and end of a time span. The new resulting pattern is played over the time period of the original pattern:
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* s("bd*2 hh*3 [sd bd]*2 perc").zoom(0.25, 0.75)
|
|
||||||
* // s("hh*3 [sd bd]*2") // equivalent
|
|
||||||
*/
|
|
||||||
export const zoom = register('zoom', function (s, e, pat) {
|
export const zoom = register('zoom', function (s, e, pat) {
|
||||||
e = Fraction(e);
|
e = Fraction(e);
|
||||||
s = Fraction(s);
|
s = Fraction(s);
|
||||||
@@ -1828,12 +1661,6 @@ export const { zoomArc, zoomarc } = register(['zoomArc', 'zoomarc'], function (a
|
|||||||
return pat.zoom(a.begin, a.end);
|
return pat.zoom(a.begin, a.end);
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
|
||||||
* Selects the given fraction of the pattern and repeats that part to fill the remainder of the cycle.
|
|
||||||
* @param {number} fraction fraction to select
|
|
||||||
* @example
|
|
||||||
* s("lt ht mt cp, [hh oh]*2").linger("<1 .5 .25 .125>")
|
|
||||||
*/
|
|
||||||
export const linger = register('linger', function (t, pat) {
|
export const linger = register('linger', function (t, pat) {
|
||||||
if (t == 0) {
|
if (t == 0) {
|
||||||
return silence;
|
return silence;
|
||||||
@@ -1843,23 +1670,10 @@ export const linger = register('linger', function (t, pat) {
|
|||||||
return pat._zoom(0, t)._slow(t);
|
return pat._zoom(0, t)._slow(t);
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
|
||||||
* Samples the pattern at a rate of n events per cycle. Useful for turning a continuous pattern into a discrete one.
|
|
||||||
* @param {number} segments number of segments per cycle
|
|
||||||
* @example
|
|
||||||
* note(saw.range(0,12).segment(24)).add(40)
|
|
||||||
*/
|
|
||||||
export const segment = register('segment', function (rate, pat) {
|
export const segment = register('segment', function (rate, pat) {
|
||||||
return pat.struct(pure(true)._fast(rate));
|
return pat.struct(pure(true)._fast(rate));
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
|
||||||
* Swaps 1s and 0s in a binary pattern.
|
|
||||||
* @name invert
|
|
||||||
* @synonyms inv
|
|
||||||
* @example
|
|
||||||
* s("bd").struct("1 0 0 1 0 0 1 0".lastOf(4, invert))
|
|
||||||
*/
|
|
||||||
export const { invert, inv } = register(['invert', 'inv'], function (pat) {
|
export const { invert, inv } = register(['invert', 'inv'], function (pat) {
|
||||||
// Swap true/false in a binary pattern
|
// Swap true/false in a binary pattern
|
||||||
return pat.fmap((x) => !x);
|
return pat.fmap((x) => !x);
|
||||||
@@ -1875,13 +1689,9 @@ export const { invert, inv } = register(['invert', 'inv'], function (pat) {
|
|||||||
* @example
|
* @example
|
||||||
* "c3 eb3 g3".when("<0 1>/2", x=>x.sub(5)).note()
|
* "c3 eb3 g3".when("<0 1>/2", x=>x.sub(5)).note()
|
||||||
*/
|
*/
|
||||||
export const when = register(
|
export const when = register('when', function (on, func, pat) {
|
||||||
'when',
|
|
||||||
function (on, func, pat) {
|
|
||||||
return on ? func(pat) : pat;
|
return on ? func(pat) : pat;
|
||||||
},
|
});
|
||||||
[false, true],
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Superimposes the function result on top of the original pattern, delayed by the given time.
|
* Superimposes the function result on top of the original pattern, delayed by the given time.
|
||||||
@@ -1893,13 +1703,9 @@ export const when = register(
|
|||||||
* @example
|
* @example
|
||||||
* "c3 eb3 g3".off(1/8, x=>x.add(7)).note()
|
* "c3 eb3 g3".off(1/8, x=>x.add(7)).note()
|
||||||
*/
|
*/
|
||||||
export const off = register(
|
export const off = register('off', function (time_pat, func, pat) {
|
||||||
'off',
|
|
||||||
function (time_pat, func, pat) {
|
|
||||||
return stack(pat, func(pat.late(time_pat)));
|
return stack(pat, func(pat.late(time_pat)));
|
||||||
},
|
});
|
||||||
[false, true],
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a new pattern where every other cycle is played once, twice as
|
* Returns a new pattern where every other cycle is played once, twice as
|
||||||
@@ -1939,37 +1745,15 @@ export const rev = register('rev', function (pat) {
|
|||||||
return new Pattern(query).splitQueries();
|
return new Pattern(query).splitQueries();
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
|
||||||
* Silences a pattern.
|
|
||||||
* @example
|
|
||||||
* stack(
|
|
||||||
* s("bd").hush(),
|
|
||||||
* s("hh*3")
|
|
||||||
* )
|
|
||||||
*/
|
|
||||||
export const hush = register('hush', function (pat) {
|
export const hush = register('hush', function (pat) {
|
||||||
return silence;
|
return silence;
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
|
||||||
* Applies `rev` to a pattern every other cycle, so that the pattern alternates between forwards and backwards.
|
|
||||||
* @example
|
|
||||||
* note("c d e g").palindrome()
|
|
||||||
*/
|
|
||||||
export const palindrome = register('palindrome', function (pat) {
|
export const palindrome = register('palindrome', function (pat) {
|
||||||
return pat.every(2, rev);
|
return pat.every(2, rev);
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
export const { juxBy, juxby } = register(['juxBy', 'juxby'], function (by, func, pat) {
|
||||||
* Jux with adjustable stereo width. 0 = mono, 1 = full stereo.
|
|
||||||
* @name juxBy
|
|
||||||
* @synonyms juxby
|
|
||||||
* @example
|
|
||||||
* s("lt ht mt ht hh").juxBy("<0 .5 1>/2", rev)
|
|
||||||
*/
|
|
||||||
export const { juxBy, juxby } = register(
|
|
||||||
['juxBy', 'juxby'],
|
|
||||||
function (by, func, pat) {
|
|
||||||
by /= 2;
|
by /= 2;
|
||||||
const elem_or = function (dict, key, dflt) {
|
const elem_or = function (dict, key, dflt) {
|
||||||
if (key in dict) {
|
if (key in dict) {
|
||||||
@@ -1981,27 +1765,25 @@ export const { juxBy, juxby } = register(
|
|||||||
const right = pat.withValue((val) => Object.assign({}, val, { pan: elem_or(val, 'pan', 0.5) + by }));
|
const right = pat.withValue((val) => Object.assign({}, val, { pan: elem_or(val, 'pan', 0.5) + by }));
|
||||||
|
|
||||||
return stack(left, func(right));
|
return stack(left, func(right));
|
||||||
},
|
});
|
||||||
[false, true],
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
export const jux = register('jux', function (func, pat) {
|
||||||
* The jux function creates strange stereo effects, by applying a function to a pattern, but only in the right-hand channel.
|
|
||||||
* @example
|
|
||||||
* s("lt ht mt ht hh").jux(rev)
|
|
||||||
*/
|
|
||||||
export const jux = register(
|
|
||||||
'jux',
|
|
||||||
function (func, pat) {
|
|
||||||
return pat._juxBy(1, func, pat);
|
return pat._juxBy(1, func, pat);
|
||||||
},
|
});
|
||||||
[true],
|
|
||||||
);
|
export const { stutWith, stutwith } = register(['stutWith', 'stutwith'], function (times, time, func, pat) {
|
||||||
|
return stack(...listRange(0, times - 1).map((i) => func(pat.late(Fraction(time).mul(i)), i)));
|
||||||
|
});
|
||||||
|
|
||||||
|
export const stut = register('stut', function (times, feedback, time, pat) {
|
||||||
|
return pat._stutWith(times, time, (pat, i) => pat.velocity(Math.pow(feedback, i)));
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Superimpose and offset multiple times, applying the given function each time.
|
* Superimpose and offset multiple times, applying the given function each time.
|
||||||
* @name echoWith
|
* @name echoWith
|
||||||
* @synonyms echowith, stutWith, stutwith
|
* @memberof Pattern
|
||||||
|
* @returns Pattern
|
||||||
* @param {number} times how many times to repeat
|
* @param {number} times how many times to repeat
|
||||||
* @param {number} time cycle offset between iterations
|
* @param {number} time cycle offset between iterations
|
||||||
* @param {function} func function to apply, given the pattern and the iteration index
|
* @param {function} func function to apply, given the pattern and the iteration index
|
||||||
@@ -2010,13 +1792,9 @@ export const jux = register(
|
|||||||
* .echoWith(4, 1/8, (p,n) => p.add(n*2))
|
* .echoWith(4, 1/8, (p,n) => p.add(n*2))
|
||||||
* .scale('C minor').note().legato(.2)
|
* .scale('C minor').note().legato(.2)
|
||||||
*/
|
*/
|
||||||
export const { echoWith, echowith, stutWith, stutwith } = register(
|
export const { echoWith, echowith } = register(['echoWith', 'echowith'], function (times, time, func, pat) {
|
||||||
['echoWith', 'echowith', 'stutWith', 'stutwith'],
|
|
||||||
function (times, time, func, pat) {
|
|
||||||
return stack(...listRange(0, times - 1).map((i) => func(pat.late(Fraction(time).mul(i)), i)));
|
return stack(...listRange(0, times - 1).map((i) => func(pat.late(Fraction(time).mul(i)), i)));
|
||||||
},
|
});
|
||||||
[false, false, true],
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Superimpose and offset multiple times, gradually decreasing the velocity
|
* Superimpose and offset multiple times, gradually decreasing the velocity
|
||||||
@@ -2033,19 +1811,6 @@ export const echo = register('echo', function (times, time, feedback, pat) {
|
|||||||
return pat._echoWith(times, time, (pat, i) => pat.velocity(Math.pow(feedback, i)));
|
return pat._echoWith(times, time, (pat, i) => pat.velocity(Math.pow(feedback, i)));
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
|
||||||
* Deprecated. Like echo, but the last 2 parameters are flipped.
|
|
||||||
* @name stut
|
|
||||||
* @param {number} times how many times to repeat
|
|
||||||
* @param {number} feedback velocity multiplicator for each iteration
|
|
||||||
* @param {number} time cycle offset between iterations
|
|
||||||
* @example
|
|
||||||
* s("bd sd").stut(3, .8, 1/6)
|
|
||||||
*/
|
|
||||||
export const stut = register('stut', function (times, feedback, time, pat) {
|
|
||||||
return pat._echoWith(times, time, (pat, i) => pat.velocity(Math.pow(feedback, i)));
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Divides a pattern into a given number of subdivisions, plays the subdivisions in order, but increments the starting subdivision each cycle. The pattern wraps to the first subdivision after the last subdivision is played.
|
* Divides a pattern into a given number of subdivisions, plays the subdivisions in order, but increments the starting subdivision each cycle. The pattern wraps to the first subdivision after the last subdivision is played.
|
||||||
* @name iter
|
* @name iter
|
||||||
@@ -2071,7 +1836,6 @@ export const iter = register('iter', function (times, pat) {
|
|||||||
/**
|
/**
|
||||||
* Like `iter`, but plays the subdivisions in reverse order. Known as iter' in tidalcycles
|
* Like `iter`, but plays the subdivisions in reverse order. Known as iter' in tidalcycles
|
||||||
* @name iterBack
|
* @name iterBack
|
||||||
* @synonyms iterback
|
|
||||||
* @memberof Pattern
|
* @memberof Pattern
|
||||||
* @returns Pattern
|
* @returns Pattern
|
||||||
* @example
|
* @example
|
||||||
@@ -2096,35 +1860,26 @@ const _chunk = function (n, func, pat, back = false) {
|
|||||||
return pat.when(binary_pat, func);
|
return pat.when(binary_pat, func);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const chunk = register(
|
export const chunk = register('chunk', function (n, func, pat) {
|
||||||
'chunk',
|
|
||||||
function (n, func, pat) {
|
|
||||||
return _chunk(n, func, pat, false);
|
return _chunk(n, func, pat, false);
|
||||||
},
|
});
|
||||||
[false, true],
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Like `chunk`, but cycles through the parts in reverse order. Known as chunk' in tidalcycles
|
* Like `chunk`, but cycles through the parts in reverse order. Known as chunk' in tidalcycles
|
||||||
* @name chunkBack
|
* @name chunkBack
|
||||||
* @synonyms chunkback
|
|
||||||
* @memberof Pattern
|
* @memberof Pattern
|
||||||
* @returns Pattern
|
* @returns Pattern
|
||||||
* @example
|
* @example
|
||||||
* "0 1 2 3".chunkBack(4, x=>x.add(7)).scale('A minor').note()
|
* "0 1 2 3".chunkBack(4, x=>x.add(7)).scale('A minor').note()
|
||||||
*/
|
*/
|
||||||
export const { chunkBack, chunkback } = register(
|
export const { chunkBack, chunkback } = register(['chunkBack', 'chunkback'], function (n, func, pat) {
|
||||||
['chunkBack', 'chunkback'],
|
|
||||||
function (n, func, pat) {
|
|
||||||
return _chunk(n, func, pat, true);
|
return _chunk(n, func, pat, true);
|
||||||
},
|
});
|
||||||
[false, true],
|
|
||||||
);
|
|
||||||
|
|
||||||
// TODO - redefine elsewhere in terms of mask
|
// TODO - redefine elsewhere in terms of mask
|
||||||
export const bypass = register('bypass', function (on, pat) {
|
export const bypass = register('bypass', function (on, pat) {
|
||||||
on = Boolean(parseInt(on));
|
on = Boolean(parseInt(on));
|
||||||
return on ? silence : pat;
|
return on ? silence : this;
|
||||||
});
|
});
|
||||||
|
|
||||||
// sets absolute duration of haps
|
// sets absolute duration of haps
|
||||||
@@ -2133,10 +1888,7 @@ export const duration = register('duration', function (value, pat) {
|
|||||||
return pat.withHapSpan((span) => new TimeSpan(span.begin, span.begin.add(value)));
|
return pat.withHapSpan((span) => new TimeSpan(span.begin, span.begin.add(value)));
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
// TODO - make control?
|
||||||
* Sets the color of the hap in visualizations like pianoroll or highlighting.
|
|
||||||
*/
|
|
||||||
// TODO: move this to controls https://github.com/tidalcycles/strudel/issues/288
|
|
||||||
export const { color, colour } = register(['color', 'colour'], function (color, pat) {
|
export const { color, colour } = register(['color', 'colour'], function (color, pat) {
|
||||||
return pat.withContext((context) => ({ ...context, color }));
|
return pat.withContext((context) => ({ ...context, color }));
|
||||||
});
|
});
|
||||||
@@ -2164,7 +1916,6 @@ export const velocity = register('velocity', function (velocity, pat) {
|
|||||||
*/
|
*/
|
||||||
// TODO - fix
|
// TODO - fix
|
||||||
export const legato = register('legato', function (value, pat) {
|
export const legato = register('legato', function (value, pat) {
|
||||||
value = Fraction(value);
|
|
||||||
return pat.withHapSpan((span) => new TimeSpan(span.begin, span.begin.add(span.end.sub(span.begin).mul(value))));
|
return pat.withHapSpan((span) => new TimeSpan(span.begin, span.begin.add(span.end.sub(span.begin).mul(value))));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -283,15 +283,3 @@ export function pianoroll({
|
|||||||
ctx.stroke();
|
ctx.stroke();
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getOptions(drawTime, options = {}) {
|
|
||||||
let [lookbehind, lookahead] = drawTime;
|
|
||||||
lookbehind = Math.abs(lookbehind);
|
|
||||||
const cycles = lookahead + lookbehind;
|
|
||||||
const playhead = lookbehind / cycles;
|
|
||||||
return { fold: 1, ...options, cycles, playhead };
|
|
||||||
}
|
|
||||||
|
|
||||||
Pattern.prototype.punchcard = function (options) {
|
|
||||||
return this.onPaint((ctx, time, haps, drawTime) => pianoroll({ ctx, time, haps, ...getOptions(drawTime, options) }));
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ export function repl({
|
|||||||
afterEval,
|
afterEval,
|
||||||
getTime,
|
getTime,
|
||||||
transpiler,
|
transpiler,
|
||||||
onToggle,
|
|
||||||
editPattern,
|
editPattern,
|
||||||
|
onToggle,
|
||||||
}) {
|
}) {
|
||||||
const scheduler = new Cyclist({
|
const scheduler = new Cyclist({
|
||||||
interval,
|
interval,
|
||||||
@@ -35,7 +35,7 @@ export function repl({
|
|||||||
getTime,
|
getTime,
|
||||||
onToggle,
|
onToggle,
|
||||||
});
|
});
|
||||||
setTime(() => scheduler.now()); // TODO: refactor?
|
setTime(() => scheduler.getPhase()); // TODO: refactor?
|
||||||
const evaluate = async (code, autostart = true) => {
|
const evaluate = async (code, autostart = true) => {
|
||||||
if (!code) {
|
if (!code) {
|
||||||
throw new Error('no code to evaluate');
|
throw new Error('no code to evaluate');
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ Copyright (C) 2022 Strudel contributors - see <https://github.com/tidalcycles/st
|
|||||||
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Pattern } from './pattern.mjs';
|
|
||||||
|
|
||||||
// returns true if the given string is a note
|
// returns true if the given string is a note
|
||||||
export const isNoteWithOctave = (name) => /^[a-gA-G][#bs]*[0-9]$/.test(name);
|
export const isNoteWithOctave = (name) => /^[a-gA-G][#bs]*[0-9]$/.test(name);
|
||||||
export const isNote = (name) => /^[a-gA-G][#bs]*[0-9]?$/.test(name);
|
export const isNote = (name) => /^[a-gA-G][#bs]*[0-9]?$/.test(name);
|
||||||
@@ -151,25 +149,6 @@ export function curry(func, overload, arity = func.length) {
|
|||||||
const partial = function (...args2) {
|
const partial = function (...args2) {
|
||||||
return curried.apply(this, args.concat(args2));
|
return curried.apply(this, args.concat(args2));
|
||||||
};
|
};
|
||||||
|
|
||||||
if (args.length == arity - 1) {
|
|
||||||
// The penultimate arg.. so add some composition magic
|
|
||||||
// TODO - To make this useful, we also need to add stub functions for every pattern method to
|
|
||||||
// do the actual composing
|
|
||||||
for (const r of Pattern.__registered) {
|
|
||||||
partial[r] = function (...args) {
|
|
||||||
const result = new Pattern(() => []);
|
|
||||||
result.__compose = function (pat) {
|
|
||||||
return partial(pat)[r](...args);
|
|
||||||
};
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
partial.__compose = function (pat) {
|
|
||||||
return partial(pat);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (overload) {
|
if (overload) {
|
||||||
overload(partial, args);
|
overload(partial, args);
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+293
-334
@@ -1,15 +1,15 @@
|
|||||||
import d, { useCallback as E, useRef as A, useEffect as k, useMemo as Q, useState as _, useLayoutEffect as te } from "react";
|
import l, { useCallback as N, useRef as k, useEffect as _, useMemo as K, useState as w, useLayoutEffect as G } from "react";
|
||||||
import ue from "@uiw/react-codemirror";
|
import Z from "@uiw/react-codemirror";
|
||||||
import { Decoration as M, EditorView as re } from "@codemirror/view";
|
import { Decoration as y, EditorView as J } from "@codemirror/view";
|
||||||
import { StateEffect as ne, StateField as oe } from "@codemirror/state";
|
import { StateEffect as Q, StateField as X } from "@codemirror/state";
|
||||||
import { javascript as de } from "@codemirror/lang-javascript";
|
import { javascript as ee } from "@codemirror/lang-javascript";
|
||||||
import { tags as u } from "@lezer/highlight";
|
import { tags as s } from "@lezer/highlight";
|
||||||
import { createTheme as fe } from "@uiw/codemirror-themes";
|
import { createTheme as te } from "@uiw/codemirror-themes";
|
||||||
import { webaudioOutput as me, getAudioContext as he } from "@strudel.cycles/webaudio";
|
import { repl as re, logger as ne, pianoroll as oe } from "@strudel.cycles/core";
|
||||||
import { useInView as ge } from "react-hook-inview";
|
import { webaudioOutput as ae, getAudioContext as ce } from "@strudel.cycles/webaudio";
|
||||||
import { repl as pe, logger as ve } from "@strudel.cycles/core";
|
import { useInView as se } from "react-hook-inview";
|
||||||
import { transpiler as be } from "@strudel.cycles/transpiler";
|
import { transpiler as ie } from "@strudel.cycles/transpiler";
|
||||||
const Ee = fe({
|
const le = te({
|
||||||
theme: "dark",
|
theme: "dark",
|
||||||
settings: {
|
settings: {
|
||||||
background: "#222",
|
background: "#222",
|
||||||
@@ -22,421 +22,380 @@ const Ee = fe({
|
|||||||
gutterForeground: "#8a919966"
|
gutterForeground: "#8a919966"
|
||||||
},
|
},
|
||||||
styles: [
|
styles: [
|
||||||
{ tag: u.keyword, color: "#c792ea" },
|
{ tag: s.keyword, color: "#c792ea" },
|
||||||
{ tag: u.operator, color: "#89ddff" },
|
{ tag: s.operator, color: "#89ddff" },
|
||||||
{ tag: u.special(u.variableName), color: "#eeffff" },
|
{ tag: s.special(s.variableName), color: "#eeffff" },
|
||||||
{ tag: u.typeName, color: "#c3e88d" },
|
{ tag: s.typeName, color: "#c3e88d" },
|
||||||
{ tag: u.atom, color: "#f78c6c" },
|
{ tag: s.atom, color: "#f78c6c" },
|
||||||
{ tag: u.number, color: "#c3e88d" },
|
{ tag: s.number, color: "#c3e88d" },
|
||||||
{ tag: u.definition(u.variableName), color: "#82aaff" },
|
{ tag: s.definition(s.variableName), color: "#82aaff" },
|
||||||
{ tag: u.string, color: "#c3e88d" },
|
{ tag: s.string, color: "#c3e88d" },
|
||||||
{ tag: u.special(u.string), color: "#c3e88d" },
|
{ tag: s.special(s.string), color: "#c3e88d" },
|
||||||
{ tag: u.comment, color: "#7d8799" },
|
{ tag: s.comment, color: "#7d8799" },
|
||||||
{ tag: u.variableName, color: "#c792ea" },
|
{ tag: s.variableName, color: "#c792ea" },
|
||||||
{ tag: u.tagName, color: "#c3e88d" },
|
{ tag: s.tagName, color: "#c3e88d" },
|
||||||
{ tag: u.bracket, color: "#525154" },
|
{ tag: s.bracket, color: "#525154" },
|
||||||
{ tag: u.meta, color: "#ffcb6b" },
|
{ tag: s.meta, color: "#ffcb6b" },
|
||||||
{ tag: u.attributeName, color: "#c792ea" },
|
{ tag: s.attributeName, color: "#c792ea" },
|
||||||
{ tag: u.propertyName, color: "#c792ea" },
|
{ tag: s.propertyName, color: "#c792ea" },
|
||||||
{ tag: u.className, color: "#decb6b" },
|
{ tag: s.className, color: "#decb6b" },
|
||||||
{ tag: u.invalid, color: "#ffffff" }
|
{ tag: s.invalid, color: "#ffffff" }
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
const X = ne.define(), ye = oe.define({
|
const j = Q.define(), ue = X.define({
|
||||||
create() {
|
create() {
|
||||||
return M.none;
|
return y.none;
|
||||||
},
|
},
|
||||||
update(e, t) {
|
update(e, r) {
|
||||||
try {
|
try {
|
||||||
for (let r of t.effects)
|
for (let t of r.effects)
|
||||||
if (r.is(X))
|
if (t.is(j))
|
||||||
if (r.value) {
|
if (t.value) {
|
||||||
const n = M.mark({ attributes: { style: "background-color: #FFCA2880" } });
|
const a = y.mark({ attributes: { style: "background-color: #FFCA2880" } });
|
||||||
e = M.set([n.range(0, t.newDoc.length)]);
|
e = y.set([a.range(0, r.newDoc.length)]);
|
||||||
} else
|
} else
|
||||||
e = M.set([]);
|
e = y.set([]);
|
||||||
return e;
|
return e;
|
||||||
} catch (r) {
|
} catch (t) {
|
||||||
return console.warn("flash error", r), e;
|
return console.warn("flash error", t), e;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
provide: (e) => re.decorations.from(e)
|
provide: (e) => J.decorations.from(e)
|
||||||
}), we = (e) => {
|
}), de = (e) => {
|
||||||
e.dispatch({ effects: X.of(!0) }), setTimeout(() => {
|
e.dispatch({ effects: j.of(!0) }), setTimeout(() => {
|
||||||
e.dispatch({ effects: X.of(!1) });
|
e.dispatch({ effects: j.of(!1) });
|
||||||
}, 200);
|
}, 200);
|
||||||
}, B = ne.define(), ke = oe.define({
|
}, z = Q.define(), fe = X.define({
|
||||||
create() {
|
create() {
|
||||||
return M.none;
|
return y.none;
|
||||||
},
|
},
|
||||||
update(e, t) {
|
update(e, r) {
|
||||||
try {
|
try {
|
||||||
for (let r of t.effects)
|
for (let t of r.effects)
|
||||||
if (r.is(B)) {
|
if (t.is(z)) {
|
||||||
const n = r.value.map(
|
const a = t.value.map(
|
||||||
(l) => (l.context.locations || []).map(({ start: m, end: f }) => {
|
(o) => (o.context.locations || []).map(({ start: i, end: u }) => {
|
||||||
const c = l.context.color || "#FFCA28";
|
const m = o.context.color || "#FFCA28";
|
||||||
let s = t.newDoc.line(m.line).from + m.column, g = t.newDoc.line(f.line).from + f.column;
|
let n = r.newDoc.line(i.line).from + i.column, d = r.newDoc.line(u.line).from + u.column;
|
||||||
const b = t.newDoc.length;
|
const v = r.newDoc.length;
|
||||||
return s > b || g > b ? void 0 : M.mark({ attributes: { style: `outline: 1.5px solid ${c};` } }).range(s, g);
|
return n > v || d > v ? void 0 : y.mark({ attributes: { style: `outline: 1.5px solid ${m};` } }).range(n, d);
|
||||||
})
|
})
|
||||||
).flat().filter(Boolean) || [];
|
).flat().filter(Boolean) || [];
|
||||||
e = M.set(n, !0);
|
e = y.set(a, !0);
|
||||||
}
|
}
|
||||||
return e;
|
return e;
|
||||||
} catch {
|
} catch {
|
||||||
return M.set([]);
|
return y.set([]);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
provide: (e) => re.decorations.from(e)
|
provide: (e) => J.decorations.from(e)
|
||||||
}), Fe = [de(), Ee, ke, ye];
|
}), me = [ee(), le, fe, ue];
|
||||||
function _e({ value: e, onChange: t, onViewChanged: r, onSelectionChange: n, options: l, editorDidMount: m }) {
|
function ge({ value: e, onChange: r, onViewChanged: t, onSelectionChange: a, options: o, editorDidMount: i }) {
|
||||||
const f = E(
|
const u = N(
|
||||||
(g) => {
|
(d) => {
|
||||||
t?.(g);
|
r?.(d);
|
||||||
},
|
|
||||||
[t]
|
|
||||||
), c = E(
|
|
||||||
(g) => {
|
|
||||||
r?.(g);
|
|
||||||
},
|
},
|
||||||
[r]
|
[r]
|
||||||
), s = E(
|
), m = N(
|
||||||
(g) => {
|
(d) => {
|
||||||
g.selectionSet && n && n?.(g.state.selection);
|
t?.(d);
|
||||||
},
|
},
|
||||||
[n]
|
[t]
|
||||||
|
), n = N(
|
||||||
|
(d) => {
|
||||||
|
d.selectionSet && a && a?.(d.state.selection);
|
||||||
|
},
|
||||||
|
[a]
|
||||||
);
|
);
|
||||||
return /* @__PURE__ */ d.createElement(d.Fragment, null, /* @__PURE__ */ d.createElement(ue, {
|
return /* @__PURE__ */ l.createElement(l.Fragment, null, /* @__PURE__ */ l.createElement(Z, {
|
||||||
value: e,
|
value: e,
|
||||||
onChange: f,
|
onChange: u,
|
||||||
onCreateEditor: c,
|
onCreateEditor: m,
|
||||||
onUpdate: s,
|
onUpdate: n,
|
||||||
extensions: Fe
|
extensions: me
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
function T(...e) {
|
function W(...e) {
|
||||||
return e.filter(Boolean).join(" ");
|
return e.filter(Boolean).join(" ");
|
||||||
}
|
}
|
||||||
function Me({ view: e, pattern: t, active: r, getTime: n }) {
|
function pe({ view: e, pattern: r, active: t, getTime: a }) {
|
||||||
const l = A([]), m = A(0);
|
const o = k([]), i = k();
|
||||||
k(() => {
|
_(() => {
|
||||||
if (e)
|
if (e)
|
||||||
if (t && r) {
|
if (r && t) {
|
||||||
m.current = 0;
|
let u = requestAnimationFrame(function m() {
|
||||||
let f = requestAnimationFrame(function c() {
|
|
||||||
try {
|
try {
|
||||||
const s = n(), b = [Math.max(m.current ?? s, s - 1 / 10, -0.01), s + 1 / 60];
|
const n = a(), v = [Math.max(i.current || n, n - 1 / 10, 0), n + 1 / 60];
|
||||||
m.current = b[1], l.current = l.current.filter((h) => h.whole.end > s);
|
i.current = v[1], o.current = o.current.filter((p) => p.whole.end > n);
|
||||||
const i = t.queryArc(...b).filter((h) => h.hasOnset());
|
const h = r.queryArc(...v).filter((p) => p.hasOnset());
|
||||||
l.current = l.current.concat(i), e.dispatch({ effects: B.of(l.current) });
|
o.current = o.current.concat(h), e.dispatch({ effects: z.of(o.current) });
|
||||||
} catch {
|
} catch {
|
||||||
e.dispatch({ effects: B.of([]) });
|
e.dispatch({ effects: z.of([]) });
|
||||||
}
|
}
|
||||||
f = requestAnimationFrame(c);
|
u = requestAnimationFrame(m);
|
||||||
});
|
});
|
||||||
return () => {
|
return () => {
|
||||||
cancelAnimationFrame(f);
|
cancelAnimationFrame(u);
|
||||||
};
|
};
|
||||||
} else
|
} else
|
||||||
l.current = [], e.dispatch({ effects: B.of([]) });
|
o.current = [], e.dispatch({ effects: z.of([]) });
|
||||||
}, [t, r, e]);
|
}, [r, t, e]);
|
||||||
}
|
}
|
||||||
function Ae(e, t = !1) {
|
function he(e, r = !1) {
|
||||||
const r = A(), n = A(), l = (c) => {
|
const t = k(), a = k(), o = (m) => {
|
||||||
if (n.current !== void 0) {
|
if (a.current !== void 0) {
|
||||||
const s = c - n.current;
|
const n = m - a.current;
|
||||||
e(c, s);
|
e(m, n);
|
||||||
}
|
}
|
||||||
n.current = c, r.current = requestAnimationFrame(l);
|
a.current = m, t.current = requestAnimationFrame(o);
|
||||||
}, m = () => {
|
}, i = () => {
|
||||||
r.current = requestAnimationFrame(l);
|
t.current = requestAnimationFrame(o);
|
||||||
}, f = () => {
|
}, u = () => {
|
||||||
r.current && cancelAnimationFrame(r.current), delete r.current;
|
t.current && cancelAnimationFrame(t.current), delete t.current;
|
||||||
};
|
};
|
||||||
return k(() => {
|
return _(() => {
|
||||||
r.current && (f(), m());
|
t.current && (u(), i());
|
||||||
}, [e]), k(() => (t && m(), f), []), {
|
}, [e]), _(() => (r && i(), u), []), {
|
||||||
start: m,
|
start: i,
|
||||||
stop: f
|
stop: u
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
function Ne({ pattern: e, started: t, getTime: r, onDraw: n, drawTime: l = [-2, 2] }) {
|
function ve({ pattern: e, started: r, getTime: t, onDraw: a }) {
|
||||||
let [m, f] = l;
|
let o = k([]), i = k(null);
|
||||||
m = Math.abs(m);
|
const { start: u, stop: m } = he(
|
||||||
let c = A([]), s = A(null);
|
N(() => {
|
||||||
k(() => {
|
const n = t();
|
||||||
if (e && t) {
|
if (i.current === null) {
|
||||||
const i = r(), h = e.queryArc(Math.max(i, 0), i + f + 0.1);
|
i.current = n;
|
||||||
c.current = c.current.filter((p) => p.whole.begin < i), c.current = c.current.concat(h);
|
|
||||||
}
|
|
||||||
}, [e, t]);
|
|
||||||
const { start: g, stop: b } = Ae(
|
|
||||||
E(() => {
|
|
||||||
const i = r() + f;
|
|
||||||
if (s.current === null) {
|
|
||||||
s.current = i;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const h = e.queryArc(Math.max(s.current, i - 1 / 10), i);
|
const d = e.queryArc(Math.max(i.current, n - 1 / 10), n), v = 4;
|
||||||
s.current = i, c.current = (c.current || []).filter((p) => p.whole.end >= i - m - f).concat(h.filter((p) => p.hasOnset())), n(e, i - f, c.current, l);
|
i.current = n, o.current = (o.current || []).filter((h) => h.whole.end > n - v).concat(d.filter((h) => h.hasOnset())), a(n, o.current);
|
||||||
}, [e])
|
}, [e])
|
||||||
);
|
);
|
||||||
return k(() => {
|
_(() => {
|
||||||
t ? g() : (c.current = [], b());
|
r ? u() : (o.current = [], m());
|
||||||
}, [t]), {
|
}, [r]);
|
||||||
clear: () => {
|
|
||||||
c.current = [];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
function Ce(e) {
|
function be(e) {
|
||||||
return k(() => (window.addEventListener("message", e), () => window.removeEventListener("message", e)), [e]), E((t) => window.postMessage(t, "*"), []);
|
return _(() => (window.addEventListener("message", e), () => window.removeEventListener("message", e)), [e]), N((r) => window.postMessage(r, "*"), []);
|
||||||
}
|
}
|
||||||
function De({
|
function Ee({
|
||||||
defaultOutput: e,
|
defaultOutput: e,
|
||||||
interval: t,
|
interval: r,
|
||||||
getTime: r,
|
getTime: t,
|
||||||
evalOnMount: n = !1,
|
evalOnMount: a = !1,
|
||||||
initialCode: l = "",
|
initialCode: o = "",
|
||||||
autolink: m = !1,
|
autolink: i = !1,
|
||||||
beforeEval: f,
|
beforeEval: u,
|
||||||
afterEval: c,
|
afterEval: m,
|
||||||
editPattern: s,
|
editPattern: n,
|
||||||
onEvalError: g,
|
onEvalError: d,
|
||||||
onToggle: b,
|
onToggle: v,
|
||||||
canvasId: i,
|
canvasId: h
|
||||||
drawContext: h,
|
|
||||||
drawTime: p = [-2, 2]
|
|
||||||
}) {
|
}) {
|
||||||
const D = Q(() => Re(), []);
|
const p = K(() => we(), []);
|
||||||
i = i || `canvas-${D}`;
|
h = h || `canvas-${p}`;
|
||||||
const [P, R] = _(), [z, H] = _(), [y, S] = _(l), [V, q] = _(), [x, I] = _(), [N, O] = _(!1), K = y !== V, L = E((a) => !!(a?.context?.onPaint && h), [h]), { scheduler: C, evaluate: o, start: v, stop: j, pause: U } = Q(
|
const [F, A] = w(), [P, D] = w(), [b, q] = w(o), [x, V] = w(), [I, R] = w(), [L, B] = w(!1), H = b !== x, { scheduler: M, evaluate: c, start: f, stop: C, pause: O } = K(
|
||||||
() => pe({
|
() => re({
|
||||||
interval: t,
|
interval: r,
|
||||||
defaultOutput: e,
|
defaultOutput: e,
|
||||||
onSchedulerError: R,
|
onSchedulerError: A,
|
||||||
onEvalError: (a) => {
|
onEvalError: (g) => {
|
||||||
H(a), g?.(a);
|
D(g), d?.(g);
|
||||||
},
|
},
|
||||||
getTime: r,
|
getTime: t,
|
||||||
drawContext: h,
|
transpiler: ie,
|
||||||
transpiler: be,
|
beforeEval: ({ code: g }) => {
|
||||||
editPattern: s,
|
q(g), u?.();
|
||||||
beforeEval: ({ code: a }) => {
|
|
||||||
S(a), f?.();
|
|
||||||
},
|
},
|
||||||
afterEval: ({ pattern: a, code: w }) => {
|
editPattern: n ? (g) => n(g, p) : void 0,
|
||||||
q(w), I(a), H(), R(), m && (window.location.hash = "#" + encodeURIComponent(btoa(w))), c?.();
|
afterEval: ({ pattern: g, code: T }) => {
|
||||||
|
V(T), R(g), D(), A(), i && (window.location.hash = "#" + encodeURIComponent(btoa(T))), m?.();
|
||||||
},
|
},
|
||||||
onToggle: (a) => {
|
onToggle: (g) => {
|
||||||
O(a), b?.(a);
|
B(g), v?.(g);
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
[e, t, r]
|
[e, r, t]
|
||||||
), ce = Ce(({ data: { from: a, type: w } }) => {
|
), Y = be(({ data: { from: g, type: T } }) => {
|
||||||
w === "start" && a !== D && j();
|
T === "start" && g !== p && C();
|
||||||
}), Y = E(
|
}), S = N(
|
||||||
async (a = !0) => {
|
async (g = !0) => {
|
||||||
const w = await o(y, a);
|
await c(b, g), Y({ type: "start", from: p });
|
||||||
return ce({ type: "start", from: D }), w;
|
|
||||||
},
|
},
|
||||||
[o, y]
|
[c, b]
|
||||||
), W = E(
|
), U = k();
|
||||||
(a, w, G, J) => {
|
return _(() => {
|
||||||
const { onPaint: ie } = a.context || {}, le = typeof h == "function" ? h(i) : h;
|
!U.current && a && b && (U.current = !0, S());
|
||||||
ie?.(le, w, G, J);
|
}, [S, a, b]), _(() => () => {
|
||||||
},
|
M.stop();
|
||||||
[h, i]
|
}, [M]), {
|
||||||
), $ = E(
|
id: p,
|
||||||
(a) => {
|
canvasId: h,
|
||||||
if (L(a)) {
|
code: b,
|
||||||
const [w, G] = p, J = a.queryArc(0, G);
|
setCode: q,
|
||||||
W(a, -1e-3, J, p);
|
error: F || P,
|
||||||
|
schedulerError: F,
|
||||||
|
scheduler: M,
|
||||||
|
evalError: P,
|
||||||
|
evaluate: c,
|
||||||
|
activateCode: S,
|
||||||
|
activeCode: x,
|
||||||
|
isDirty: H,
|
||||||
|
pattern: I,
|
||||||
|
started: L,
|
||||||
|
start: f,
|
||||||
|
stop: C,
|
||||||
|
pause: O,
|
||||||
|
togglePlay: async () => {
|
||||||
|
L ? M.pause() : await S();
|
||||||
}
|
}
|
||||||
},
|
|
||||||
[p, W, L]
|
|
||||||
), Z = A();
|
|
||||||
k(() => {
|
|
||||||
!Z.current && n && y && (Z.current = !0, o(y, !1).then((a) => $(a)));
|
|
||||||
}, [n, y, o, $]), k(() => () => {
|
|
||||||
C.stop();
|
|
||||||
}, [C]);
|
|
||||||
const ae = async () => {
|
|
||||||
N ? (C.stop(), $(x)) : await Y();
|
|
||||||
}, se = P || z;
|
|
||||||
return Ne({
|
|
||||||
pattern: x,
|
|
||||||
started: L(x) && N,
|
|
||||||
getTime: () => C.now(),
|
|
||||||
drawTime: p,
|
|
||||||
onDraw: W
|
|
||||||
}), {
|
|
||||||
id: D,
|
|
||||||
canvasId: i,
|
|
||||||
code: y,
|
|
||||||
setCode: S,
|
|
||||||
error: se,
|
|
||||||
schedulerError: P,
|
|
||||||
scheduler: C,
|
|
||||||
evalError: z,
|
|
||||||
evaluate: o,
|
|
||||||
activateCode: Y,
|
|
||||||
activeCode: V,
|
|
||||||
isDirty: K,
|
|
||||||
pattern: x,
|
|
||||||
started: N,
|
|
||||||
start: v,
|
|
||||||
stop: j,
|
|
||||||
pause: U,
|
|
||||||
togglePlay: ae
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
function Re() {
|
function we() {
|
||||||
return Math.floor((1 + Math.random()) * 65536).toString(16).substring(1);
|
return Math.floor((1 + Math.random()) * 65536).toString(16).substring(1);
|
||||||
}
|
}
|
||||||
function ee({ type: e }) {
|
function $({ type: e }) {
|
||||||
return /* @__PURE__ */ d.createElement("svg", {
|
return /* @__PURE__ */ l.createElement("svg", {
|
||||||
xmlns: "http://www.w3.org/2000/svg",
|
xmlns: "http://www.w3.org/2000/svg",
|
||||||
className: "sc-h-5 sc-w-5",
|
className: "sc-h-5 sc-w-5",
|
||||||
viewBox: "0 0 20 20",
|
viewBox: "0 0 20 20",
|
||||||
fill: "currentColor"
|
fill: "currentColor"
|
||||||
}, {
|
}, {
|
||||||
refresh: /* @__PURE__ */ d.createElement("path", {
|
refresh: /* @__PURE__ */ l.createElement("path", {
|
||||||
fillRule: "evenodd",
|
fillRule: "evenodd",
|
||||||
d: "M4 2a1 1 0 011 1v2.101a7.002 7.002 0 0111.601 2.566 1 1 0 11-1.885.666A5.002 5.002 0 005.999 7H9a1 1 0 010 2H4a1 1 0 01-1-1V3a1 1 0 011-1zm.008 9.057a1 1 0 011.276.61A5.002 5.002 0 0014.001 13H11a1 1 0 110-2h5a1 1 0 011 1v5a1 1 0 11-2 0v-2.101a7.002 7.002 0 01-11.601-2.566 1 1 0 01.61-1.276z",
|
d: "M4 2a1 1 0 011 1v2.101a7.002 7.002 0 0111.601 2.566 1 1 0 11-1.885.666A5.002 5.002 0 005.999 7H9a1 1 0 010 2H4a1 1 0 01-1-1V3a1 1 0 011-1zm.008 9.057a1 1 0 011.276.61A5.002 5.002 0 0014.001 13H11a1 1 0 110-2h5a1 1 0 011 1v5a1 1 0 11-2 0v-2.101a7.002 7.002 0 01-11.601-2.566 1 1 0 01.61-1.276z",
|
||||||
clipRule: "evenodd"
|
clipRule: "evenodd"
|
||||||
}),
|
}),
|
||||||
play: /* @__PURE__ */ d.createElement("path", {
|
play: /* @__PURE__ */ l.createElement("path", {
|
||||||
fillRule: "evenodd",
|
fillRule: "evenodd",
|
||||||
d: "M10 18a8 8 0 100-16 8 8 0 000 16zM9.555 7.168A1 1 0 008 8v4a1 1 0 001.555.832l3-2a1 1 0 000-1.664l-3-2z",
|
d: "M10 18a8 8 0 100-16 8 8 0 000 16zM9.555 7.168A1 1 0 008 8v4a1 1 0 001.555.832l3-2a1 1 0 000-1.664l-3-2z",
|
||||||
clipRule: "evenodd"
|
clipRule: "evenodd"
|
||||||
}),
|
}),
|
||||||
pause: /* @__PURE__ */ d.createElement("path", {
|
pause: /* @__PURE__ */ l.createElement("path", {
|
||||||
fillRule: "evenodd",
|
fillRule: "evenodd",
|
||||||
d: "M18 10a8 8 0 11-16 0 8 8 0 0116 0zM7 8a1 1 0 012 0v4a1 1 0 11-2 0V8zm5-1a1 1 0 00-1 1v4a1 1 0 102 0V8a1 1 0 00-1-1z",
|
d: "M18 10a8 8 0 11-16 0 8 8 0 0116 0zM7 8a1 1 0 012 0v4a1 1 0 11-2 0V8zm5-1a1 1 0 00-1 1v4a1 1 0 102 0V8a1 1 0 00-1-1z",
|
||||||
clipRule: "evenodd"
|
clipRule: "evenodd"
|
||||||
}),
|
|
||||||
stop: /* @__PURE__ */ d.createElement("path", {
|
|
||||||
fillRule: "evenodd",
|
|
||||||
d: "M2 10a8 8 0 1116 0 8 8 0 01-16 0zm5-2.25A.75.75 0 017.75 7h4.5a.75.75 0 01.75.75v4.5a.75.75 0 01-.75.75h-4.5a.75.75 0 01-.75-.75v-4.5z",
|
|
||||||
clipRule: "evenodd"
|
|
||||||
})
|
})
|
||||||
}[e]);
|
}[e]);
|
||||||
}
|
}
|
||||||
const xe = "_container_3i85k_1", Le = "_header_3i85k_5", Pe = "_buttons_3i85k_9", qe = "_button_3i85k_9", ze = "_buttonDisabled_3i85k_17", He = "_error_3i85k_21", Se = "_body_3i85k_25", F = {
|
const ye = "_container_3i85k_1", ke = "_header_3i85k_5", _e = "_buttons_3i85k_9", Fe = "_button_3i85k_9", Ce = "_buttonDisabled_3i85k_17", Ne = "_error_3i85k_21", xe = "_body_3i85k_25", E = {
|
||||||
container: xe,
|
container: ye,
|
||||||
header: Le,
|
header: ke,
|
||||||
buttons: Pe,
|
buttons: _e,
|
||||||
button: qe,
|
button: Fe,
|
||||||
buttonDisabled: ze,
|
buttonDisabled: Ce,
|
||||||
error: He,
|
error: Ne,
|
||||||
body: Se
|
body: xe
|
||||||
}, Ve = () => he().currentTime;
|
}, Me = () => ce().currentTime;
|
||||||
function Ze({ tune: e, hideOutsideView: t = !1, enableKeyboard: r, drawTime: n, punchcard: l, canvasHeight: m = 200 }) {
|
function je({ tune: e, hideOutsideView: r = !1, enableKeyboard: t, withCanvas: a = !1, canvasHeight: o = 200 }) {
|
||||||
n = n || (l ? [0, 4] : void 0);
|
const {
|
||||||
const f = !!n, c = E(
|
code: i,
|
||||||
n ? (o) => document.querySelector("#" + o)?.getContext("2d") : null,
|
setCode: u,
|
||||||
[n]
|
evaluate: m,
|
||||||
), {
|
activateCode: n,
|
||||||
code: s,
|
error: d,
|
||||||
setCode: g,
|
isDirty: v,
|
||||||
evaluate: b,
|
activeCode: h,
|
||||||
activateCode: i,
|
pattern: p,
|
||||||
error: h,
|
started: F,
|
||||||
isDirty: p,
|
scheduler: A,
|
||||||
activeCode: D,
|
togglePlay: P,
|
||||||
pattern: P,
|
stop: D,
|
||||||
started: R,
|
canvasId: b,
|
||||||
scheduler: z,
|
id: q
|
||||||
togglePlay: H,
|
} = Ee({
|
||||||
stop: y,
|
|
||||||
canvasId: S,
|
|
||||||
id: V
|
|
||||||
} = De({
|
|
||||||
initialCode: e,
|
initialCode: e,
|
||||||
defaultOutput: me,
|
defaultOutput: ae,
|
||||||
editPattern: (o) => l ? o.punchcard() : o,
|
getTime: Me,
|
||||||
getTime: Ve,
|
editPattern: (c, f) => c.withContext((C) => ({ ...C, id: f }))
|
||||||
evalOnMount: f,
|
});
|
||||||
drawContext: c,
|
ve({
|
||||||
drawTime: n
|
pattern: p,
|
||||||
}), [q, x] = _(), [I, N] = ge({
|
started: a && F,
|
||||||
|
getTime: () => A.now(),
|
||||||
|
onDraw: (c, f) => {
|
||||||
|
const C = document.querySelector("#" + b).getContext("2d");
|
||||||
|
oe({ ctx: C, time: c, haps: f, autorange: 1, fold: 1, playhead: 1 });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const [x, V] = w(), [I, R] = se({
|
||||||
threshold: 0.01
|
threshold: 0.01
|
||||||
}), O = A(), K = Q(() => ((N || !t) && (O.current = !0), N || O.current), [N, t]);
|
}), L = k(), B = K(() => ((R || !r) && (L.current = !0), R || L.current), [R, r]);
|
||||||
Me({
|
pe({
|
||||||
view: q,
|
view: x,
|
||||||
pattern: P,
|
pattern: p,
|
||||||
active: R && !D?.includes("strudel disable-highlighting"),
|
active: F && !h?.includes("strudel disable-highlighting"),
|
||||||
getTime: () => z.now()
|
getTime: () => A.getPhase()
|
||||||
}), te(() => {
|
}), G(() => {
|
||||||
if (r) {
|
if (t) {
|
||||||
const o = async (v) => {
|
const c = async (f) => {
|
||||||
(v.ctrlKey || v.altKey) && (v.code === "Enter" ? (v.preventDefault(), we(q), await i()) : v.code === "Period" && (y(), v.preventDefault()));
|
(f.ctrlKey || f.altKey) && (f.code === "Enter" ? (f.preventDefault(), de(x), await n()) : f.code === "Period" && (D(), f.preventDefault()));
|
||||||
};
|
};
|
||||||
return window.addEventListener("keydown", o, !0), () => window.removeEventListener("keydown", o, !0);
|
return window.addEventListener("keydown", c, !0), () => window.removeEventListener("keydown", c, !0);
|
||||||
}
|
}
|
||||||
}, [r, P, s, b, y, q]);
|
}, [t, p, i, m, D, x]);
|
||||||
const [L, C] = _([]);
|
const [H, M] = w([]);
|
||||||
return Oe(
|
return Ae(
|
||||||
E((o) => {
|
N((c) => {
|
||||||
const { data: v } = o.detail;
|
const { data: f } = c.detail;
|
||||||
v?.hap?.context?.id === V && C((U) => U.concat([o.detail]).slice(-10));
|
f?.hap?.context?.id === q && M((O) => O.concat([c.detail]).slice(-10));
|
||||||
}, [])
|
}, [])
|
||||||
), /* @__PURE__ */ d.createElement("div", {
|
), /* @__PURE__ */ l.createElement("div", {
|
||||||
className: F.container,
|
className: E.container,
|
||||||
ref: I
|
ref: I
|
||||||
}, /* @__PURE__ */ d.createElement("div", {
|
}, /* @__PURE__ */ l.createElement("div", {
|
||||||
className: F.header
|
className: E.header
|
||||||
}, /* @__PURE__ */ d.createElement("div", {
|
}, /* @__PURE__ */ l.createElement("div", {
|
||||||
className: F.buttons
|
className: E.buttons
|
||||||
}, /* @__PURE__ */ d.createElement("button", {
|
}, /* @__PURE__ */ l.createElement("button", {
|
||||||
className: T(F.button, R ? "sc-animate-pulse" : ""),
|
className: W(E.button, F ? "sc-animate-pulse" : ""),
|
||||||
onClick: () => H()
|
onClick: () => P()
|
||||||
}, /* @__PURE__ */ d.createElement(ee, {
|
}, /* @__PURE__ */ l.createElement($, {
|
||||||
type: R ? "stop" : "play"
|
type: F ? "pause" : "play"
|
||||||
})), /* @__PURE__ */ d.createElement("button", {
|
})), /* @__PURE__ */ l.createElement("button", {
|
||||||
className: T(p ? F.button : F.buttonDisabled),
|
className: W(v ? E.button : E.buttonDisabled),
|
||||||
onClick: () => i()
|
onClick: () => n()
|
||||||
}, /* @__PURE__ */ d.createElement(ee, {
|
}, /* @__PURE__ */ l.createElement($, {
|
||||||
type: "refresh"
|
type: "refresh"
|
||||||
}))), h && /* @__PURE__ */ d.createElement("div", {
|
}))), d && /* @__PURE__ */ l.createElement("div", {
|
||||||
className: F.error
|
className: E.error
|
||||||
}, h.message)), /* @__PURE__ */ d.createElement("div", {
|
}, d.message)), /* @__PURE__ */ l.createElement("div", {
|
||||||
className: F.body
|
className: E.body
|
||||||
}, K && /* @__PURE__ */ d.createElement(_e, {
|
}, B && /* @__PURE__ */ l.createElement(ge, {
|
||||||
value: s,
|
value: i,
|
||||||
onChange: g,
|
onChange: u,
|
||||||
onViewChanged: x
|
onViewChanged: V
|
||||||
})), n && /* @__PURE__ */ d.createElement("canvas", {
|
})), a && /* @__PURE__ */ l.createElement("canvas", {
|
||||||
id: S,
|
id: b,
|
||||||
className: "w-full pointer-events-none",
|
className: "w-full pointer-events-none",
|
||||||
height: m,
|
height: o,
|
||||||
ref: (o) => {
|
ref: (c) => {
|
||||||
o && o.width !== o.clientWidth && (o.width = o.clientWidth);
|
c && c.width !== c.clientWidth && (c.width = c.clientWidth);
|
||||||
}
|
}
|
||||||
}), !!L.length && /* @__PURE__ */ d.createElement("div", {
|
}), !!H.length && /* @__PURE__ */ l.createElement("div", {
|
||||||
className: "sc-bg-gray-800 sc-rounded-md sc-p-2"
|
className: "sc-bg-gray-800 sc-rounded-md sc-p-2"
|
||||||
}, L.map(({ message: o }, v) => /* @__PURE__ */ d.createElement("div", {
|
}, H.map(({ message: c }, f) => /* @__PURE__ */ l.createElement("div", {
|
||||||
key: v
|
key: f
|
||||||
}, o))));
|
}, c))));
|
||||||
}
|
}
|
||||||
function Oe(e) {
|
function Ae(e) {
|
||||||
Be(ve.key, e);
|
De(ne.key, e);
|
||||||
}
|
}
|
||||||
function Be(e, t, r = !1) {
|
function De(e, r, t = !1) {
|
||||||
k(() => (document.addEventListener(e, t, r), () => {
|
_(() => (document.addEventListener(e, r, t), () => {
|
||||||
document.removeEventListener(e, t, r);
|
document.removeEventListener(e, r, t);
|
||||||
}), [t]);
|
}), [r]);
|
||||||
}
|
}
|
||||||
const Te = (e) => te(() => (window.addEventListener("keydown", e, !0), () => window.removeEventListener("keydown", e, !0)), [e]);
|
const Ue = (e) => G(() => (window.addEventListener("keydown", e, !0), () => window.removeEventListener("keydown", e, !0)), [e]);
|
||||||
export {
|
export {
|
||||||
_e as CodeMirror,
|
ge as CodeMirror,
|
||||||
Ze as MiniRepl,
|
je as MiniRepl,
|
||||||
T as cx,
|
W as cx,
|
||||||
we as flash,
|
de as flash,
|
||||||
Me as useHighlighting,
|
pe as useHighlighting,
|
||||||
Te as useKeydown,
|
Ue as useKeydown,
|
||||||
Ce as usePostMessage,
|
be as usePostMessage,
|
||||||
De as useStrudel
|
Ee as useStrudel
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -26,13 +26,6 @@ export function Icon({ type }) {
|
|||||||
clipRule="evenodd"
|
clipRule="evenodd"
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
stop: (
|
|
||||||
<path
|
|
||||||
fillRule="evenodd"
|
|
||||||
d="M2 10a8 8 0 1116 0 8 8 0 01-16 0zm5-2.25A.75.75 0 017.75 7h4.5a.75.75 0 01.75.75v4.5a.75.75 0 01-.75.75h-4.5a.75.75 0 01-.75-.75v-4.5z"
|
|
||||||
clipRule="evenodd"
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
}[type]
|
}[type]
|
||||||
}
|
}
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
|
import { pianoroll } from '@strudel.cycles/core';
|
||||||
import { getAudioContext, webaudioOutput } from '@strudel.cycles/webaudio';
|
import { getAudioContext, webaudioOutput } from '@strudel.cycles/webaudio';
|
||||||
import React, { useLayoutEffect, useMemo, useRef, useState, useCallback, useEffect } from 'react';
|
import React, { useLayoutEffect, useMemo, useRef, useState, useCallback, useEffect } from 'react';
|
||||||
import { useInView } from 'react-hook-inview';
|
import { useInView } from 'react-hook-inview';
|
||||||
import 'tailwindcss/tailwind.css';
|
import 'tailwindcss/tailwind.css';
|
||||||
import cx from '../cx';
|
import cx from '../cx';
|
||||||
import useHighlighting from '../hooks/useHighlighting.mjs';
|
import useHighlighting from '../hooks/useHighlighting.mjs';
|
||||||
|
import usePatternFrame from '../hooks/usePatternFrame.mjs';
|
||||||
import useStrudel from '../hooks/useStrudel.mjs';
|
import useStrudel from '../hooks/useStrudel.mjs';
|
||||||
import CodeMirror6, { flash } from './CodeMirror6';
|
import CodeMirror6, { flash } from './CodeMirror6';
|
||||||
import { Icon } from './Icon';
|
import { Icon } from './Icon';
|
||||||
@@ -13,13 +15,7 @@ import { logger } from '@strudel.cycles/core';
|
|||||||
|
|
||||||
const getTime = () => getAudioContext().currentTime;
|
const getTime = () => getAudioContext().currentTime;
|
||||||
|
|
||||||
export function MiniRepl({ tune, hideOutsideView = false, enableKeyboard, drawTime, punchcard, canvasHeight = 200 }) {
|
export function MiniRepl({ tune, hideOutsideView = false, enableKeyboard, withCanvas = false, canvasHeight = 200 }) {
|
||||||
drawTime = drawTime || (punchcard ? [0, 4] : undefined);
|
|
||||||
const evalOnMount = !!drawTime;
|
|
||||||
const drawContext = useCallback(
|
|
||||||
!!drawTime ? (canvasId) => document.querySelector('#' + canvasId)?.getContext('2d') : null,
|
|
||||||
[drawTime],
|
|
||||||
);
|
|
||||||
const {
|
const {
|
||||||
code,
|
code,
|
||||||
setCode,
|
setCode,
|
||||||
@@ -38,13 +34,25 @@ export function MiniRepl({ tune, hideOutsideView = false, enableKeyboard, drawTi
|
|||||||
} = useStrudel({
|
} = useStrudel({
|
||||||
initialCode: tune,
|
initialCode: tune,
|
||||||
defaultOutput: webaudioOutput,
|
defaultOutput: webaudioOutput,
|
||||||
editPattern: (pat) => (punchcard ? pat.punchcard() : pat),
|
|
||||||
getTime,
|
getTime,
|
||||||
evalOnMount,
|
editPattern: (pat, id) => {
|
||||||
drawContext,
|
return pat.withContext((ctx) => ({ ...ctx, id }));
|
||||||
drawTime,
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
usePatternFrame({
|
||||||
|
pattern,
|
||||||
|
started: withCanvas && started,
|
||||||
|
getTime: () => scheduler.now(),
|
||||||
|
onDraw: (time, haps) => {
|
||||||
|
const ctx = document.querySelector('#' + canvasId).getContext('2d');
|
||||||
|
pianoroll({ ctx, time, haps, autorange: 1, fold: 1, playhead: 1 });
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
/* useEffect(() => {
|
||||||
|
init && activateCode();
|
||||||
|
}, [init, activateCode]); */
|
||||||
const [view, setView] = useState();
|
const [view, setView] = useState();
|
||||||
const [ref, isVisible] = useInView({
|
const [ref, isVisible] = useInView({
|
||||||
threshold: 0.01,
|
threshold: 0.01,
|
||||||
@@ -60,7 +68,7 @@ export function MiniRepl({ tune, hideOutsideView = false, enableKeyboard, drawTi
|
|||||||
view,
|
view,
|
||||||
pattern,
|
pattern,
|
||||||
active: started && !activeCode?.includes('strudel disable-highlighting'),
|
active: started && !activeCode?.includes('strudel disable-highlighting'),
|
||||||
getTime: () => scheduler.now(),
|
getTime: () => scheduler.getPhase(),
|
||||||
});
|
});
|
||||||
|
|
||||||
// set active pattern on ctrl+enter
|
// set active pattern on ctrl+enter
|
||||||
@@ -102,7 +110,7 @@ export function MiniRepl({ tune, hideOutsideView = false, enableKeyboard, drawTi
|
|||||||
<div className={styles.header}>
|
<div className={styles.header}>
|
||||||
<div className={styles.buttons}>
|
<div className={styles.buttons}>
|
||||||
<button className={cx(styles.button, started ? 'sc-animate-pulse' : '')} onClick={() => togglePlay()}>
|
<button className={cx(styles.button, started ? 'sc-animate-pulse' : '')} onClick={() => togglePlay()}>
|
||||||
<Icon type={started ? 'stop' : 'play'} />
|
<Icon type={started ? 'pause' : 'play'} />
|
||||||
</button>
|
</button>
|
||||||
<button className={cx(isDirty ? styles.button : styles.buttonDisabled)} onClick={() => activateCode()}>
|
<button className={cx(isDirty ? styles.button : styles.buttonDisabled)} onClick={() => activateCode()}>
|
||||||
<Icon type="refresh" />
|
<Icon type="refresh" />
|
||||||
@@ -113,7 +121,7 @@ export function MiniRepl({ tune, hideOutsideView = false, enableKeyboard, drawTi
|
|||||||
<div className={styles.body}>
|
<div className={styles.body}>
|
||||||
{show && <CodeMirror6 value={code} onChange={setCode} onViewChanged={setView} />}
|
{show && <CodeMirror6 value={code} onChange={setCode} onViewChanged={setView} />}
|
||||||
</div>
|
</div>
|
||||||
{drawTime && (
|
{withCanvas && (
|
||||||
<canvas
|
<canvas
|
||||||
id={canvasId}
|
id={canvasId}
|
||||||
className="w-full pointer-events-none"
|
className="w-full pointer-events-none"
|
||||||
|
|||||||
@@ -3,17 +3,16 @@ import { setHighlights } from '../components/CodeMirror6';
|
|||||||
|
|
||||||
function useHighlighting({ view, pattern, active, getTime }) {
|
function useHighlighting({ view, pattern, active, getTime }) {
|
||||||
const highlights = useRef([]);
|
const highlights = useRef([]);
|
||||||
const lastEnd = useRef(0);
|
const lastEnd = useRef();
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (view) {
|
if (view) {
|
||||||
if (pattern && active) {
|
if (pattern && active) {
|
||||||
lastEnd.current = 0;
|
|
||||||
let frame = requestAnimationFrame(function updateHighlights() {
|
let frame = requestAnimationFrame(function updateHighlights() {
|
||||||
try {
|
try {
|
||||||
const audioTime = getTime();
|
const audioTime = getTime();
|
||||||
// force min framerate of 10 fps => fixes crash on tab refocus, where lastEnd could be far away
|
// force min framerate of 10 fps => fixes crash on tab refocus, where lastEnd could be far away
|
||||||
// see https://github.com/tidalcycles/strudel/issues/108
|
// see https://github.com/tidalcycles/strudel/issues/108
|
||||||
const begin = Math.max(lastEnd.current ?? audioTime, audioTime - 1 / 10, -0.01); // negative time seems buggy
|
const begin = Math.max(lastEnd.current || audioTime, audioTime - 1 / 10, 0); // negative time seems buggy
|
||||||
const span = [begin, audioTime + 1 / 60];
|
const span = [begin, audioTime + 1 / 60];
|
||||||
lastEnd.current = span[1];
|
lastEnd.current = span[1];
|
||||||
highlights.current = highlights.current.filter((hap) => hap.whole.end > audioTime); // keep only highlights that are still active
|
highlights.current = highlights.current.filter((hap) => hap.whole.end > audioTime); // keep only highlights that are still active
|
||||||
|
|||||||
@@ -2,32 +2,23 @@ import { useCallback, useEffect, useRef } from 'react';
|
|||||||
import 'tailwindcss/tailwind.css';
|
import 'tailwindcss/tailwind.css';
|
||||||
import useFrame from '../hooks/useFrame.mjs';
|
import useFrame from '../hooks/useFrame.mjs';
|
||||||
|
|
||||||
function usePatternFrame({ pattern, started, getTime, onDraw, drawTime = [-2, 2] }) {
|
function usePatternFrame({ pattern, started, getTime, onDraw }) {
|
||||||
let [lookbehind, lookahead] = drawTime;
|
|
||||||
lookbehind = Math.abs(lookbehind);
|
|
||||||
let visibleHaps = useRef([]);
|
let visibleHaps = useRef([]);
|
||||||
let lastFrame = useRef(null);
|
let lastFrame = useRef(null);
|
||||||
useEffect(() => {
|
|
||||||
if (pattern && started) {
|
|
||||||
const t = getTime();
|
|
||||||
const futureHaps = pattern.queryArc(Math.max(t, 0), t + lookahead + 0.1); // +0.1 = workaround for weird holes in query..
|
|
||||||
visibleHaps.current = visibleHaps.current.filter((h) => h.whole.begin < t);
|
|
||||||
visibleHaps.current = visibleHaps.current.concat(futureHaps);
|
|
||||||
}
|
|
||||||
}, [pattern, started]);
|
|
||||||
const { start: startFrame, stop: stopFrame } = useFrame(
|
const { start: startFrame, stop: stopFrame } = useFrame(
|
||||||
useCallback(() => {
|
useCallback(() => {
|
||||||
const phase = getTime() + lookahead;
|
const phase = getTime();
|
||||||
if (lastFrame.current === null) {
|
if (lastFrame.current === null) {
|
||||||
lastFrame.current = phase;
|
lastFrame.current = phase;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const haps = pattern.queryArc(Math.max(lastFrame.current, phase - 1 / 10), phase);
|
const haps = pattern.queryArc(Math.max(lastFrame.current, phase - 1 / 10), phase);
|
||||||
|
const cycles = 4;
|
||||||
lastFrame.current = phase;
|
lastFrame.current = phase;
|
||||||
visibleHaps.current = (visibleHaps.current || [])
|
visibleHaps.current = (visibleHaps.current || [])
|
||||||
.filter((h) => h.whole.end >= phase - lookbehind - lookahead) // in frame
|
.filter((h) => h.whole.end > phase - cycles) // in frame
|
||||||
.concat(haps.filter((h) => h.hasOnset()));
|
.concat(haps.filter((h) => h.hasOnset()));
|
||||||
onDraw(pattern, phase - lookahead, visibleHaps.current, drawTime);
|
onDraw(phase, visibleHaps.current);
|
||||||
}, [pattern]),
|
}, [pattern]),
|
||||||
);
|
);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -38,11 +29,6 @@ function usePatternFrame({ pattern, started, getTime, onDraw, drawTime = [-2, 2]
|
|||||||
stopFrame();
|
stopFrame();
|
||||||
}
|
}
|
||||||
}, [started]);
|
}, [started]);
|
||||||
return {
|
|
||||||
clear: () => {
|
|
||||||
visibleHaps.current = [];
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default usePatternFrame;
|
export default usePatternFrame;
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { useRef, useCallback, useEffect, useMemo, useState } from 'react';
|
import { useRef, useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
import { repl } from '@strudel.cycles/core';
|
import { repl } from '@strudel.cycles/core';
|
||||||
import { transpiler } from '@strudel.cycles/transpiler';
|
import { transpiler } from '@strudel.cycles/transpiler';
|
||||||
import usePatternFrame from './usePatternFrame';
|
|
||||||
import usePostMessage from './usePostMessage.mjs';
|
import usePostMessage from './usePostMessage.mjs';
|
||||||
|
|
||||||
function useStrudel({
|
function useStrudel({
|
||||||
@@ -17,8 +16,6 @@ function useStrudel({
|
|||||||
onEvalError,
|
onEvalError,
|
||||||
onToggle,
|
onToggle,
|
||||||
canvasId,
|
canvasId,
|
||||||
drawContext,
|
|
||||||
drawTime = [-2, 2],
|
|
||||||
}) {
|
}) {
|
||||||
const id = useMemo(() => s4(), []);
|
const id = useMemo(() => s4(), []);
|
||||||
canvasId = canvasId || `canvas-${id}`;
|
canvasId = canvasId || `canvas-${id}`;
|
||||||
@@ -30,7 +27,6 @@ function useStrudel({
|
|||||||
const [pattern, setPattern] = useState();
|
const [pattern, setPattern] = useState();
|
||||||
const [started, setStarted] = useState(false);
|
const [started, setStarted] = useState(false);
|
||||||
const isDirty = code !== activeCode;
|
const isDirty = code !== activeCode;
|
||||||
const shouldPaint = useCallback((pat) => !!(pat?.context?.onPaint && drawContext), [drawContext]);
|
|
||||||
|
|
||||||
// TODO: make sure this hook reruns when scheduler.started changes
|
// TODO: make sure this hook reruns when scheduler.started changes
|
||||||
const { scheduler, evaluate, start, stop, pause } = useMemo(
|
const { scheduler, evaluate, start, stop, pause } = useMemo(
|
||||||
@@ -44,13 +40,12 @@ function useStrudel({
|
|||||||
onEvalError?.(err);
|
onEvalError?.(err);
|
||||||
},
|
},
|
||||||
getTime,
|
getTime,
|
||||||
drawContext,
|
|
||||||
transpiler,
|
transpiler,
|
||||||
editPattern,
|
|
||||||
beforeEval: ({ code }) => {
|
beforeEval: ({ code }) => {
|
||||||
setCode(code);
|
setCode(code);
|
||||||
beforeEval?.();
|
beforeEval?.();
|
||||||
},
|
},
|
||||||
|
editPattern: editPattern ? (pat) => editPattern(pat, id) : undefined,
|
||||||
afterEval: ({ pattern: _pattern, code }) => {
|
afterEval: ({ pattern: _pattern, code }) => {
|
||||||
setActiveCode(code);
|
setActiveCode(code);
|
||||||
setPattern(_pattern);
|
setPattern(_pattern);
|
||||||
@@ -76,41 +71,19 @@ function useStrudel({
|
|||||||
});
|
});
|
||||||
const activateCode = useCallback(
|
const activateCode = useCallback(
|
||||||
async (autostart = true) => {
|
async (autostart = true) => {
|
||||||
const res = await evaluate(code, autostart);
|
await evaluate(code, autostart);
|
||||||
broadcast({ type: 'start', from: id });
|
broadcast({ type: 'start', from: id });
|
||||||
return res;
|
|
||||||
},
|
},
|
||||||
[evaluate, code],
|
[evaluate, code],
|
||||||
);
|
);
|
||||||
|
|
||||||
const onDraw = useCallback(
|
|
||||||
(pattern, time, haps, drawTime) => {
|
|
||||||
const { onPaint } = pattern.context || {};
|
|
||||||
const ctx = typeof drawContext === 'function' ? drawContext(canvasId) : drawContext;
|
|
||||||
onPaint?.(ctx, time, haps, drawTime);
|
|
||||||
},
|
|
||||||
[drawContext, canvasId],
|
|
||||||
);
|
|
||||||
|
|
||||||
const drawFirstFrame = useCallback(
|
|
||||||
(pat) => {
|
|
||||||
if (shouldPaint(pat)) {
|
|
||||||
const [_, lookahead] = drawTime;
|
|
||||||
const haps = pat.queryArc(0, lookahead);
|
|
||||||
// draw at -0.001 to avoid activating haps at 0
|
|
||||||
onDraw(pat, -0.001, haps, drawTime);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[drawTime, onDraw, shouldPaint],
|
|
||||||
);
|
|
||||||
|
|
||||||
const inited = useRef();
|
const inited = useRef();
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!inited.current && evalOnMount && code) {
|
if (!inited.current && evalOnMount && code) {
|
||||||
inited.current = true;
|
inited.current = true;
|
||||||
evaluate(code, false).then((pat) => drawFirstFrame(pat));
|
activateCode();
|
||||||
}
|
}
|
||||||
}, [evalOnMount, code, evaluate, drawFirstFrame]);
|
}, [activateCode, evalOnMount, code]);
|
||||||
|
|
||||||
// this will stop the scheduler when hot reloading in development
|
// this will stop the scheduler when hot reloading in development
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -121,22 +94,12 @@ function useStrudel({
|
|||||||
|
|
||||||
const togglePlay = async () => {
|
const togglePlay = async () => {
|
||||||
if (started) {
|
if (started) {
|
||||||
scheduler.stop();
|
scheduler.pause();
|
||||||
drawFirstFrame(pattern);
|
|
||||||
} else {
|
} else {
|
||||||
await activateCode();
|
await activateCode();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const error = schedulerError || evalError;
|
const error = schedulerError || evalError;
|
||||||
|
|
||||||
usePatternFrame({
|
|
||||||
pattern,
|
|
||||||
started: shouldPaint(pattern) && started,
|
|
||||||
getTime: () => scheduler.now(),
|
|
||||||
drawTime,
|
|
||||||
onDraw,
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id,
|
id,
|
||||||
canvasId,
|
canvasId,
|
||||||
|
|||||||
@@ -0,0 +1,313 @@
|
|||||||
|
import { toMidi, fromMidi } from '@strudel.cycles/core/util.mjs';
|
||||||
|
// import './feedbackdelay.mjs';
|
||||||
|
// import './reverb.mjs';
|
||||||
|
|
||||||
|
// TODO: reuse as much logic as possible between webaudio.mjs and render.mjs
|
||||||
|
|
||||||
|
const logger = console.log;
|
||||||
|
|
||||||
|
export const splitSN = (s, n) => {
|
||||||
|
if (!s.includes(':')) {
|
||||||
|
return [s, n];
|
||||||
|
}
|
||||||
|
let [s2, n2] = s.split(':');
|
||||||
|
if (isNaN(Number(n2))) {
|
||||||
|
return [s, n];
|
||||||
|
}
|
||||||
|
return [s2, n2];
|
||||||
|
};
|
||||||
|
|
||||||
|
const getOscillator = (ac, { s, freq, t, duration, release }) => {
|
||||||
|
// make oscillator
|
||||||
|
const o = ac.createOscillator();
|
||||||
|
o.type = s || 'triangle';
|
||||||
|
o.frequency.value = Number(freq);
|
||||||
|
o.start(t);
|
||||||
|
o.stop(t + duration + release);
|
||||||
|
return o;
|
||||||
|
};
|
||||||
|
const getADSR = (ac, attack, decay, sustain, release, velocity, begin, end) => {
|
||||||
|
const gainNode = ac.createGain();
|
||||||
|
gainNode.gain.setValueAtTime(0, begin);
|
||||||
|
gainNode.gain.linearRampToValueAtTime(velocity, begin + attack); // attack
|
||||||
|
gainNode.gain.linearRampToValueAtTime(sustain * velocity, begin + attack + decay); // sustain start
|
||||||
|
gainNode.gain.setValueAtTime(sustain * velocity, end); // sustain end
|
||||||
|
gainNode.gain.linearRampToValueAtTime(0, end + release); // release
|
||||||
|
// for some reason, using exponential ramping creates little cracklings
|
||||||
|
/* let t = begin;
|
||||||
|
gainNode.gain.setValueAtTime(0, t);
|
||||||
|
gainNode.gain.exponentialRampToValueAtTime(velocity, (t += attack));
|
||||||
|
const sustainGain = Math.max(sustain * velocity, 0.001);
|
||||||
|
gainNode.gain.exponentialRampToValueAtTime(sustainGain, (t += decay));
|
||||||
|
if (end - begin < attack + decay) {
|
||||||
|
gainNode.gain.cancelAndHoldAtTime(end);
|
||||||
|
} else {
|
||||||
|
gainNode.gain.setValueAtTime(sustainGain, end);
|
||||||
|
}
|
||||||
|
gainNode.gain.exponentialRampToValueAtTime(0.001, end + release); // release */
|
||||||
|
return gainNode;
|
||||||
|
};
|
||||||
|
|
||||||
|
function gainNode(ac, value) {
|
||||||
|
const node = ac.createGain();
|
||||||
|
node.gain.value = value;
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* let delays = {};
|
||||||
|
function getDelay(ac, orbit, delaytime, delayfeedback, t) {
|
||||||
|
if (!delays[orbit]) {
|
||||||
|
const dly = ac.createFeedbackDelay(1, delaytime, delayfeedback);
|
||||||
|
dly.start?.(t); // for some reason, this throws when audion extension is installed..
|
||||||
|
dly.connect(ac.destination);
|
||||||
|
delays[orbit] = dly;
|
||||||
|
}
|
||||||
|
delays[orbit].delayTime.value !== delaytime && delays[orbit].delayTime.setValueAtTime(delaytime, t);
|
||||||
|
delays[orbit].feedback.value !== delayfeedback && delays[orbit].feedback.setValueAtTime(delayfeedback, t);
|
||||||
|
return delays[orbit];
|
||||||
|
}
|
||||||
|
|
||||||
|
let reverbs = {};
|
||||||
|
function getReverb(ac, orbit, duration = 2) {
|
||||||
|
if (!reverbs[orbit]) {
|
||||||
|
const reverb = ac.createReverb(duration);
|
||||||
|
reverb.connect(ac.destination);
|
||||||
|
reverbs[orbit] = reverb;
|
||||||
|
}
|
||||||
|
if (reverbs[orbit].duration !== duration) {
|
||||||
|
reverbs[orbit] = reverbs[orbit].setDuration(duration);
|
||||||
|
reverbs[orbit].duration = duration;
|
||||||
|
}
|
||||||
|
return reverbs[orbit];
|
||||||
|
} */
|
||||||
|
|
||||||
|
function effectSend(input, effect, wet) {
|
||||||
|
const send = gainNode(wet);
|
||||||
|
input.connect(send);
|
||||||
|
send.connect(effect);
|
||||||
|
return send;
|
||||||
|
}
|
||||||
|
|
||||||
|
const getFilter = (ac, type, frequency, Q) => {
|
||||||
|
const filter = ac.createBiquadFilter();
|
||||||
|
filter.type = type;
|
||||||
|
filter.frequency.value = frequency;
|
||||||
|
filter.Q.value = Q;
|
||||||
|
return filter;
|
||||||
|
};
|
||||||
|
|
||||||
|
// export const webaudioOutput = async (t, hap, ct, cps) => {
|
||||||
|
export const renderHap = async (ac, hap, deadline) => {
|
||||||
|
const hapDuration = hap.duration;
|
||||||
|
/* if (isNote(hap.value)) {
|
||||||
|
// supports primitive hap values that look like notes
|
||||||
|
hap.value = { note: hap.value };
|
||||||
|
} */
|
||||||
|
if (typeof hap.value !== 'object') {
|
||||||
|
logger(
|
||||||
|
`hap.value "${hap.value}" is not supported by webaudio output. Hint: append .note() or .s() to the end`,
|
||||||
|
'error',
|
||||||
|
);
|
||||||
|
/* throw new Error(
|
||||||
|
`hap.value "${hap.value}"" is not supported by webaudio output. Hint: append .note() or .s() to the end`,
|
||||||
|
); */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// calculate correct time (tone.js workaround)
|
||||||
|
let t = ac.currentTime + deadline;
|
||||||
|
// destructure value
|
||||||
|
let {
|
||||||
|
freq,
|
||||||
|
s,
|
||||||
|
bank,
|
||||||
|
sf,
|
||||||
|
clip = 0, // if 1, samples will be cut off when the hap ends
|
||||||
|
n = 0,
|
||||||
|
note,
|
||||||
|
gain = 0.8,
|
||||||
|
// low pass
|
||||||
|
lpf,
|
||||||
|
cutoff = lpf,
|
||||||
|
lpq = 1,
|
||||||
|
resonance = lpq,
|
||||||
|
// high pass
|
||||||
|
hpf,
|
||||||
|
hcutoff = hpf,
|
||||||
|
hpq = 1,
|
||||||
|
hresonance = hpq,
|
||||||
|
// band pass
|
||||||
|
bpf,
|
||||||
|
bandf = bpf,
|
||||||
|
bpq = 1,
|
||||||
|
bandq = bpq,
|
||||||
|
//
|
||||||
|
coarse,
|
||||||
|
crush,
|
||||||
|
shape,
|
||||||
|
pan,
|
||||||
|
speed = 1, // sample playback speed
|
||||||
|
begin = 0,
|
||||||
|
end = 1,
|
||||||
|
vowel,
|
||||||
|
delay = 0,
|
||||||
|
delayfeedback = 0.5,
|
||||||
|
delaytime = 0.25,
|
||||||
|
unit,
|
||||||
|
nudge = 0, // TODO: is this in seconds?
|
||||||
|
cut,
|
||||||
|
loop,
|
||||||
|
orbit = 1,
|
||||||
|
room,
|
||||||
|
size = 2,
|
||||||
|
roomsize = size,
|
||||||
|
} = hap.value;
|
||||||
|
const { velocity = 1 } = hap.context;
|
||||||
|
gain *= velocity; // legacy fix for velocity
|
||||||
|
// the chain will hold all audio nodes that connect to each other
|
||||||
|
const chain = [];
|
||||||
|
if (bank && s) {
|
||||||
|
s = `${bank}_${s}`;
|
||||||
|
}
|
||||||
|
if (typeof s === 'string') {
|
||||||
|
[s, n] = splitSN(s, n);
|
||||||
|
}
|
||||||
|
if (typeof note === 'string') {
|
||||||
|
[note, n] = splitSN(note, n);
|
||||||
|
}
|
||||||
|
if (!s || ['sine', 'square', 'triangle', 'sawtooth'].includes(s)) {
|
||||||
|
// destructure adsr here, because the default should be different for synths and samples
|
||||||
|
const { attack = 0.001, decay = 0.05, sustain = 0.6, release = 0.01 } = hap.value;
|
||||||
|
// with synths, n and note are the same thing
|
||||||
|
n = note || n || 36;
|
||||||
|
if (typeof n === 'string') {
|
||||||
|
n = toMidi(n); // e.g. c3 => 48
|
||||||
|
}
|
||||||
|
// get frequency
|
||||||
|
if (!freq && typeof n === 'number') {
|
||||||
|
freq = fromMidi(n); // + 48);
|
||||||
|
}
|
||||||
|
// make oscillator
|
||||||
|
const o = getOscillator(ac, { t, s, freq, duration: hapDuration, release });
|
||||||
|
chain.push(o);
|
||||||
|
// level down oscillators as they are really loud compared to samples i've tested
|
||||||
|
chain.push(gainNode(ac, 0.3));
|
||||||
|
// TODO: make adsr work with samples without pops
|
||||||
|
// envelope
|
||||||
|
const adsr = getADSR(ac, attack, decay, sustain, release, 1, t, t + hapDuration);
|
||||||
|
chain.push(adsr);
|
||||||
|
} else {
|
||||||
|
// TODO: refactor sampler to node
|
||||||
|
/*
|
||||||
|
// destructure adsr here, because the default should be different for synths and samples
|
||||||
|
const { attack = 0.001, decay = 0.001, sustain = 1, release = 0.001 } = hap.value;
|
||||||
|
// load sample
|
||||||
|
if (speed === 0) {
|
||||||
|
// no playback
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!s) {
|
||||||
|
console.warn('no sample specified');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const soundfont = getSoundfontKey(s);
|
||||||
|
let bufferSource;
|
||||||
|
|
||||||
|
if (soundfont) {
|
||||||
|
// is soundfont
|
||||||
|
bufferSource = await globalThis.getFontBufferSource(soundfont, note || n, ac, freq);
|
||||||
|
} else {
|
||||||
|
// is sample from loaded samples(..)
|
||||||
|
bufferSource = await getSampleBufferSource(s, n, note, speed, freq);
|
||||||
|
}
|
||||||
|
// asny stuff above took too long?
|
||||||
|
if (ac.currentTime > t) {
|
||||||
|
logger(`[sampler] still loading sound "${s}:${n}"`, 'highlight');
|
||||||
|
// console.warn('sample still loading:', s, n);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!bufferSource) {
|
||||||
|
console.warn('no buffer source');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
bufferSource.playbackRate.value = Math.abs(speed) * bufferSource.playbackRate.value;
|
||||||
|
if (unit === 'c') {
|
||||||
|
// are there other units?
|
||||||
|
bufferSource.playbackRate.value = bufferSource.playbackRate.value * bufferSource.buffer.duration;
|
||||||
|
}
|
||||||
|
let duration = soundfont || clip ? hapDuration : bufferSource.buffer.duration / bufferSource.playbackRate.value;
|
||||||
|
// "The computation of the offset into the sound is performed using the sound buffer's natural sample rate,
|
||||||
|
// rather than the current playback rate, so even if the sound is playing at twice its normal speed,
|
||||||
|
// the midway point through a 10-second audio buffer is still 5."
|
||||||
|
const offset = begin * duration * bufferSource.playbackRate.value;
|
||||||
|
duration = (end - begin) * duration;
|
||||||
|
if (loop) {
|
||||||
|
bufferSource.loop = true;
|
||||||
|
bufferSource.loopStart = offset;
|
||||||
|
bufferSource.loopEnd = offset + duration;
|
||||||
|
duration = loop * duration;
|
||||||
|
}
|
||||||
|
t += nudge;
|
||||||
|
|
||||||
|
bufferSource.start(t, offset);
|
||||||
|
if (cut !== undefined) {
|
||||||
|
cutGroups[cut]?.stop(t); // fade out?
|
||||||
|
cutGroups[cut] = bufferSource;
|
||||||
|
}
|
||||||
|
chain.push(bufferSource);
|
||||||
|
bufferSource.stop(t + duration + release);
|
||||||
|
const adsr = getADSR(ac, attack, decay, sustain, release, 1, t, t + duration);
|
||||||
|
chain.push(adsr); */
|
||||||
|
}
|
||||||
|
|
||||||
|
// gain stage
|
||||||
|
chain.push(gainNode(ac, gain));
|
||||||
|
|
||||||
|
// filters
|
||||||
|
cutoff !== undefined && chain.push(getFilter(ac, 'lowpass', cutoff, resonance));
|
||||||
|
hcutoff !== undefined && chain.push(getFilter(ac, 'highpass', hcutoff, hresonance));
|
||||||
|
bandf !== undefined && chain.push(getFilter(ac, 'bandpass', bandf, bandq));
|
||||||
|
vowel !== undefined && chain.push(ac.createVowelFilter(vowel));
|
||||||
|
|
||||||
|
// effects
|
||||||
|
/* coarse !== undefined && chain.push(getWorklet(ac, 'coarse-processor', { coarse }));
|
||||||
|
crush !== undefined && chain.push(getWorklet(ac, 'crush-processor', { crush }));
|
||||||
|
shape !== undefined && chain.push(getWorklet(ac, 'shape-processor', { shape })); */
|
||||||
|
|
||||||
|
// panning
|
||||||
|
if (pan !== undefined) {
|
||||||
|
const panner = ac.createStereoPanner();
|
||||||
|
panner.pan.value = 2 * pan - 1;
|
||||||
|
chain.push(panner);
|
||||||
|
}
|
||||||
|
|
||||||
|
// last gain
|
||||||
|
const post = gainNode(ac, 1);
|
||||||
|
chain.push(post);
|
||||||
|
// post.connect(getDestination());
|
||||||
|
post.connect(ac.destination);
|
||||||
|
|
||||||
|
// TODO: refactor delay / reverb for node: extending nodes does not work..
|
||||||
|
// delay
|
||||||
|
/* let delaySend;
|
||||||
|
if (delay > 0 && delaytime > 0 && delayfeedback > 0) {
|
||||||
|
const delyNode = getDelay(ac, orbit, delaytime, delayfeedback, t);
|
||||||
|
delaySend = effectSend(post, delyNode, delay);
|
||||||
|
}
|
||||||
|
// reverb
|
||||||
|
let reverbSend;
|
||||||
|
if (room > 0 && roomsize > 0) {
|
||||||
|
const reverbNode = getReverb(ac, orbit, roomsize);
|
||||||
|
reverbSend = effectSend(post, reverbNode, room);
|
||||||
|
} */
|
||||||
|
|
||||||
|
// connect chain elements together
|
||||||
|
chain.slice(1).reduce((last, current) => last.connect(current), chain[0]);
|
||||||
|
|
||||||
|
// disconnect all nodes when source node has ended:
|
||||||
|
chain[0].onended = () =>
|
||||||
|
chain
|
||||||
|
.concat([
|
||||||
|
/*delaySend , reverbSend */
|
||||||
|
])
|
||||||
|
.forEach((n) => n?.disconnect());
|
||||||
|
};
|
||||||
@@ -124,8 +124,7 @@ let sampleCache = { current: undefined };
|
|||||||
export const samples = async (sampleMap, baseUrl = sampleMap._base || '') => {
|
export const samples = async (sampleMap, baseUrl = sampleMap._base || '') => {
|
||||||
if (typeof sampleMap === 'string') {
|
if (typeof sampleMap === 'string') {
|
||||||
if (sampleMap.startsWith('github:')) {
|
if (sampleMap.startsWith('github:')) {
|
||||||
let [_, path] = sampleMap.split('github:');
|
const [_, path] = sampleMap.split('github:');
|
||||||
path = path.endsWith('/') ? path.slice(0, -1) : path;
|
|
||||||
sampleMap = `https://raw.githubusercontent.com/${path}/strudel.json`;
|
sampleMap = `https://raw.githubusercontent.com/${path}/strudel.json`;
|
||||||
}
|
}
|
||||||
if (typeof fetch !== 'function') {
|
if (typeof fetch !== 'function') {
|
||||||
|
|||||||
@@ -822,32 +822,6 @@ exports[`runs examples > example "apply" example index 0 1`] = `
|
|||||||
]
|
]
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`runs examples > example "arp" example index 0 1`] = `
|
|
||||||
[
|
|
||||||
"[ 0/1 → 1/2 | note:c ]",
|
|
||||||
"[ 1/2 → 1/1 | note:c ]",
|
|
||||||
"[ 1/2 → 1/1 | note:g ]",
|
|
||||||
"[ 1/1 → 3/2 | note:eb ]",
|
|
||||||
"[ 3/2 → 2/1 | note:c ]",
|
|
||||||
"[ 3/2 → 2/1 | note:g ]",
|
|
||||||
"[ 2/1 → 5/2 | note:c ]",
|
|
||||||
"[ 5/2 → 3/1 | note:c ]",
|
|
||||||
"[ 5/2 → 3/1 | note:g ]",
|
|
||||||
"[ 3/1 → 7/2 | note:eb ]",
|
|
||||||
"[ 7/2 → 4/1 | note:c ]",
|
|
||||||
"[ 7/2 → 4/1 | note:g ]",
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`runs examples > example "arpWith" example index 0 1`] = `
|
|
||||||
[
|
|
||||||
"[ 0/1 → 1/1 | note:g ]",
|
|
||||||
"[ 1/1 → 2/1 | note:g ]",
|
|
||||||
"[ 2/1 → 3/1 | note:ab ]",
|
|
||||||
"[ 3/1 → 4/1 | note:ab ]",
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`runs examples > example "attack" example index 0 1`] = `
|
exports[`runs examples > example "attack" example index 0 1`] = `
|
||||||
[
|
[
|
||||||
"[ 0/1 → 1/2 | note:c3 attack:0 ]",
|
"[ 0/1 → 1/2 | note:c3 attack:0 ]",
|
||||||
@@ -1089,19 +1063,6 @@ exports[`runs examples > example "coarse" example index 0 1`] = `
|
|||||||
]
|
]
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`runs examples > example "compress" example index 0 1`] = `
|
|
||||||
[
|
|
||||||
"[ 1/4 → 1/2 | s:bd ]",
|
|
||||||
"[ 1/2 → 3/4 | s:sd ]",
|
|
||||||
"[ 5/4 → 3/2 | s:bd ]",
|
|
||||||
"[ 3/2 → 7/4 | s:sd ]",
|
|
||||||
"[ 9/4 → 5/2 | s:bd ]",
|
|
||||||
"[ 5/2 → 11/4 | s:sd ]",
|
|
||||||
"[ 13/4 → 7/2 | s:bd ]",
|
|
||||||
"[ 7/2 → 15/4 | s:sd ]",
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`runs examples > example "cosine" example index 0 1`] = `
|
exports[`runs examples > example "cosine" example index 0 1`] = `
|
||||||
[
|
[
|
||||||
"[ 0/1 → 1/8 | note:Eb4 ]",
|
"[ 0/1 → 1/8 | note:Eb4 ]",
|
||||||
@@ -1171,31 +1132,6 @@ exports[`runs examples > example "cosine" example index 0 1`] = `
|
|||||||
]
|
]
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`runs examples > example "cpm" example index 0 1`] = `
|
|
||||||
[
|
|
||||||
"[ 0/1 → 2/3 | s:bd ]",
|
|
||||||
"[ 0/1 → 1/3 | s:hh ]",
|
|
||||||
"[ 1/3 → 2/3 | s:hh ]",
|
|
||||||
"[ (2/3 → 1/1) ⇝ 4/3 | s:sd ]",
|
|
||||||
"[ 2/3 → 1/1 | s:hh ]",
|
|
||||||
"[ 2/3 ⇜ (1/1 → 4/3) | s:sd ]",
|
|
||||||
"[ 1/1 → 4/3 | s:hh ]",
|
|
||||||
"[ 4/3 → 2/1 | s:bd ]",
|
|
||||||
"[ 4/3 → 5/3 | s:hh ]",
|
|
||||||
"[ 5/3 → 2/1 | s:hh ]",
|
|
||||||
"[ 2/1 → 8/3 | s:sd ]",
|
|
||||||
"[ 2/1 → 7/3 | s:hh ]",
|
|
||||||
"[ 7/3 → 8/3 | s:hh ]",
|
|
||||||
"[ (8/3 → 3/1) ⇝ 10/3 | s:bd ]",
|
|
||||||
"[ 8/3 → 3/1 | s:hh ]",
|
|
||||||
"[ 8/3 ⇜ (3/1 → 10/3) | s:bd ]",
|
|
||||||
"[ 3/1 → 10/3 | s:hh ]",
|
|
||||||
"[ 10/3 → 4/1 | s:sd ]",
|
|
||||||
"[ 10/3 → 11/3 | s:hh ]",
|
|
||||||
"[ 11/3 → 4/1 | s:hh ]",
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`runs examples > example "crush" example index 0 1`] = `
|
exports[`runs examples > example "crush" example index 0 1`] = `
|
||||||
[
|
[
|
||||||
"[ 0/1 → 1/2 | s:bd crush:16 ]",
|
"[ 0/1 → 1/2 | s:bd crush:16 ]",
|
||||||
@@ -1723,19 +1659,6 @@ exports[`runs examples > example "fast" example index 0 1`] = `
|
|||||||
]
|
]
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`runs examples > example "fastGap" example index 0 1`] = `
|
|
||||||
[
|
|
||||||
"[ 0/1 → 1/4 | s:bd ]",
|
|
||||||
"[ 1/4 → 1/2 | s:sd ]",
|
|
||||||
"[ 1/1 → 5/4 | s:bd ]",
|
|
||||||
"[ 5/4 → 3/2 | s:sd ]",
|
|
||||||
"[ 2/1 → 9/4 | s:bd ]",
|
|
||||||
"[ 9/4 → 5/2 | s:sd ]",
|
|
||||||
"[ 3/1 → 13/4 | s:bd ]",
|
|
||||||
"[ 13/4 → 7/2 | s:sd ]",
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`runs examples > example "fastcat" example index 0 1`] = `
|
exports[`runs examples > example "fastcat" example index 0 1`] = `
|
||||||
[
|
[
|
||||||
"[ 0/1 → 1/3 | e5 ]",
|
"[ 0/1 → 1/3 | e5 ]",
|
||||||
@@ -1799,43 +1722,6 @@ exports[`runs examples > example "floor" example index 0 1`] = `
|
|||||||
]
|
]
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`runs examples > example "focus" example index 0 1`] = `
|
|
||||||
[
|
|
||||||
"[ 0/1 → 1/8 | s:sd ]",
|
|
||||||
"[ 1/8 → 1/4 | s:hh ]",
|
|
||||||
"[ 1/4 → 3/8 | s:bd ]",
|
|
||||||
"[ 3/8 → 1/2 | s:hh ]",
|
|
||||||
"[ 1/2 → 5/8 | s:sd ]",
|
|
||||||
"[ 5/8 → 3/4 | s:hh ]",
|
|
||||||
"[ 3/4 → 7/8 | s:bd ]",
|
|
||||||
"[ 7/8 → 1/1 | s:hh ]",
|
|
||||||
"[ 1/1 → 9/8 | s:sd ]",
|
|
||||||
"[ 9/8 → 5/4 | s:hh ]",
|
|
||||||
"[ 5/4 → 11/8 | s:bd ]",
|
|
||||||
"[ 11/8 → 3/2 | s:hh ]",
|
|
||||||
"[ 3/2 → 13/8 | s:sd ]",
|
|
||||||
"[ 13/8 → 7/4 | s:hh ]",
|
|
||||||
"[ 7/4 → 15/8 | s:bd ]",
|
|
||||||
"[ 15/8 → 2/1 | s:hh ]",
|
|
||||||
"[ 2/1 → 17/8 | s:sd ]",
|
|
||||||
"[ 17/8 → 9/4 | s:hh ]",
|
|
||||||
"[ 9/4 → 19/8 | s:bd ]",
|
|
||||||
"[ 19/8 → 5/2 | s:hh ]",
|
|
||||||
"[ 5/2 → 21/8 | s:sd ]",
|
|
||||||
"[ 21/8 → 11/4 | s:hh ]",
|
|
||||||
"[ 11/4 → 23/8 | s:bd ]",
|
|
||||||
"[ 23/8 → 3/1 | s:hh ]",
|
|
||||||
"[ 3/1 → 25/8 | s:sd ]",
|
|
||||||
"[ 25/8 → 13/4 | s:hh ]",
|
|
||||||
"[ 13/4 → 27/8 | s:bd ]",
|
|
||||||
"[ 27/8 → 7/2 | s:hh ]",
|
|
||||||
"[ 7/2 → 29/8 | s:sd ]",
|
|
||||||
"[ 29/8 → 15/4 | s:hh ]",
|
|
||||||
"[ 15/4 → 31/8 | s:bd ]",
|
|
||||||
"[ 31/8 → 4/1 | s:hh ]",
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`runs examples > example "freq" example index 0 1`] = `
|
exports[`runs examples > example "freq" example index 0 1`] = `
|
||||||
[
|
[
|
||||||
"[ 0/1 → 1/4 | freq:220 s:superzow ]",
|
"[ 0/1 → 1/4 | freq:220 s:superzow ]",
|
||||||
@@ -1977,79 +1863,6 @@ exports[`runs examples > example "hresonance" example index 0 1`] = `
|
|||||||
]
|
]
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`runs examples > example "hush" example index 0 1`] = `
|
|
||||||
[
|
|
||||||
"[ 0/1 → 1/3 | s:hh ]",
|
|
||||||
"[ 1/3 → 2/3 | s:hh ]",
|
|
||||||
"[ 2/3 → 1/1 | s:hh ]",
|
|
||||||
"[ 1/1 → 4/3 | s:hh ]",
|
|
||||||
"[ 4/3 → 5/3 | s:hh ]",
|
|
||||||
"[ 5/3 → 2/1 | s:hh ]",
|
|
||||||
"[ 2/1 → 7/3 | s:hh ]",
|
|
||||||
"[ 7/3 → 8/3 | s:hh ]",
|
|
||||||
"[ 8/3 → 3/1 | s:hh ]",
|
|
||||||
"[ 3/1 → 10/3 | s:hh ]",
|
|
||||||
"[ 10/3 → 11/3 | s:hh ]",
|
|
||||||
"[ 11/3 → 4/1 | s:hh ]",
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`runs examples > example "inside" example index 0 1`] = `
|
|
||||||
[
|
|
||||||
"[ 1/8 → 1/4 | note:C3 ]",
|
|
||||||
"[ 0/1 → 1/8 | note:D3 ]",
|
|
||||||
"[ 3/8 → 1/2 | note:E3 ]",
|
|
||||||
"[ 1/4 → 3/8 | note:F3 ]",
|
|
||||||
"[ 5/8 → 3/4 | note:G3 ]",
|
|
||||||
"[ 1/2 → 5/8 | note:F3 ]",
|
|
||||||
"[ 7/8 → 1/1 | note:E3 ]",
|
|
||||||
"[ 3/4 → 7/8 | note:D3 ]",
|
|
||||||
"[ 9/8 → 5/4 | note:C3 ]",
|
|
||||||
"[ 1/1 → 9/8 | note:D3 ]",
|
|
||||||
"[ 11/8 → 3/2 | note:E3 ]",
|
|
||||||
"[ 5/4 → 11/8 | note:F3 ]",
|
|
||||||
"[ 13/8 → 7/4 | note:G3 ]",
|
|
||||||
"[ 3/2 → 13/8 | note:F3 ]",
|
|
||||||
"[ 15/8 → 2/1 | note:E3 ]",
|
|
||||||
"[ 7/4 → 15/8 | note:D3 ]",
|
|
||||||
"[ 17/8 → 9/4 | note:C3 ]",
|
|
||||||
"[ 2/1 → 17/8 | note:D3 ]",
|
|
||||||
"[ 19/8 → 5/2 | note:E3 ]",
|
|
||||||
"[ 9/4 → 19/8 | note:F3 ]",
|
|
||||||
"[ 21/8 → 11/4 | note:G3 ]",
|
|
||||||
"[ 5/2 → 21/8 | note:F3 ]",
|
|
||||||
"[ 23/8 → 3/1 | note:E3 ]",
|
|
||||||
"[ 11/4 → 23/8 | note:D3 ]",
|
|
||||||
"[ 25/8 → 13/4 | note:C3 ]",
|
|
||||||
"[ 3/1 → 25/8 | note:D3 ]",
|
|
||||||
"[ 27/8 → 7/2 | note:E3 ]",
|
|
||||||
"[ 13/4 → 27/8 | note:F3 ]",
|
|
||||||
"[ 29/8 → 15/4 | note:G3 ]",
|
|
||||||
"[ 7/2 → 29/8 | note:F3 ]",
|
|
||||||
"[ 31/8 → 4/1 | note:E3 ]",
|
|
||||||
"[ 15/4 → 31/8 | note:D3 ]",
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`runs examples > example "invert" example index 0 1`] = `
|
|
||||||
[
|
|
||||||
"[ 0/1 → 1/8 | s:bd ]",
|
|
||||||
"[ 3/8 → 1/2 | s:bd ]",
|
|
||||||
"[ 3/4 → 7/8 | s:bd ]",
|
|
||||||
"[ 1/1 → 9/8 | s:bd ]",
|
|
||||||
"[ 11/8 → 3/2 | s:bd ]",
|
|
||||||
"[ 7/4 → 15/8 | s:bd ]",
|
|
||||||
"[ 2/1 → 17/8 | s:bd ]",
|
|
||||||
"[ 19/8 → 5/2 | s:bd ]",
|
|
||||||
"[ 11/4 → 23/8 | s:bd ]",
|
|
||||||
"[ 25/8 → 13/4 | s:bd ]",
|
|
||||||
"[ 13/4 → 27/8 | s:bd ]",
|
|
||||||
"[ 7/2 → 29/8 | s:bd ]",
|
|
||||||
"[ 29/8 → 15/4 | s:bd ]",
|
|
||||||
"[ 31/8 → 4/1 | s:bd ]",
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`runs examples > example "irand" example index 0 1`] = `
|
exports[`runs examples > example "irand" example index 0 1`] = `
|
||||||
[
|
[
|
||||||
"[ 0/1 → 1/8 | note:Bb3 ]",
|
"[ 0/1 → 1/8 | note:Bb3 ]",
|
||||||
@@ -2109,96 +1922,6 @@ exports[`runs examples > example "iterBack" example index 0 1`] = `
|
|||||||
]
|
]
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`runs examples > example "jux" example index 0 1`] = `
|
|
||||||
[
|
|
||||||
"[ 0/1 → 1/5 | s:lt pan:0 ]",
|
|
||||||
"[ 1/5 → 2/5 | s:ht pan:0 ]",
|
|
||||||
"[ 2/5 → 3/5 | s:mt pan:0 ]",
|
|
||||||
"[ 3/5 → 4/5 | s:ht pan:0 ]",
|
|
||||||
"[ 4/5 → 1/1 | s:hh pan:0 ]",
|
|
||||||
"[ 4/5 → 1/1 | s:lt pan:1 ]",
|
|
||||||
"[ 3/5 → 4/5 | s:ht pan:1 ]",
|
|
||||||
"[ 2/5 → 3/5 | s:mt pan:1 ]",
|
|
||||||
"[ 1/5 → 2/5 | s:ht pan:1 ]",
|
|
||||||
"[ 0/1 → 1/5 | s:hh pan:1 ]",
|
|
||||||
"[ 1/1 → 6/5 | s:lt pan:0 ]",
|
|
||||||
"[ 6/5 → 7/5 | s:ht pan:0 ]",
|
|
||||||
"[ 7/5 → 8/5 | s:mt pan:0 ]",
|
|
||||||
"[ 8/5 → 9/5 | s:ht pan:0 ]",
|
|
||||||
"[ 9/5 → 2/1 | s:hh pan:0 ]",
|
|
||||||
"[ 9/5 → 2/1 | s:lt pan:1 ]",
|
|
||||||
"[ 8/5 → 9/5 | s:ht pan:1 ]",
|
|
||||||
"[ 7/5 → 8/5 | s:mt pan:1 ]",
|
|
||||||
"[ 6/5 → 7/5 | s:ht pan:1 ]",
|
|
||||||
"[ 1/1 → 6/5 | s:hh pan:1 ]",
|
|
||||||
"[ 2/1 → 11/5 | s:lt pan:0 ]",
|
|
||||||
"[ 11/5 → 12/5 | s:ht pan:0 ]",
|
|
||||||
"[ 12/5 → 13/5 | s:mt pan:0 ]",
|
|
||||||
"[ 13/5 → 14/5 | s:ht pan:0 ]",
|
|
||||||
"[ 14/5 → 3/1 | s:hh pan:0 ]",
|
|
||||||
"[ 14/5 → 3/1 | s:lt pan:1 ]",
|
|
||||||
"[ 13/5 → 14/5 | s:ht pan:1 ]",
|
|
||||||
"[ 12/5 → 13/5 | s:mt pan:1 ]",
|
|
||||||
"[ 11/5 → 12/5 | s:ht pan:1 ]",
|
|
||||||
"[ 2/1 → 11/5 | s:hh pan:1 ]",
|
|
||||||
"[ 3/1 → 16/5 | s:lt pan:0 ]",
|
|
||||||
"[ 16/5 → 17/5 | s:ht pan:0 ]",
|
|
||||||
"[ 17/5 → 18/5 | s:mt pan:0 ]",
|
|
||||||
"[ 18/5 → 19/5 | s:ht pan:0 ]",
|
|
||||||
"[ 19/5 → 4/1 | s:hh pan:0 ]",
|
|
||||||
"[ 19/5 → 4/1 | s:lt pan:1 ]",
|
|
||||||
"[ 18/5 → 19/5 | s:ht pan:1 ]",
|
|
||||||
"[ 17/5 → 18/5 | s:mt pan:1 ]",
|
|
||||||
"[ 16/5 → 17/5 | s:ht pan:1 ]",
|
|
||||||
"[ 3/1 → 16/5 | s:hh pan:1 ]",
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`runs examples > example "juxBy" example index 0 1`] = `
|
|
||||||
[
|
|
||||||
"[ 0/1 → 1/5 | s:lt pan:0.5 ]",
|
|
||||||
"[ 1/5 → 2/5 | s:ht pan:0.5 ]",
|
|
||||||
"[ 2/5 → 3/5 | s:mt pan:0.5 ]",
|
|
||||||
"[ 3/5 → 4/5 | s:ht pan:0.5 ]",
|
|
||||||
"[ 4/5 → 1/1 | s:hh pan:0.5 ]",
|
|
||||||
"[ 4/5 → 1/1 | s:lt pan:0.5 ]",
|
|
||||||
"[ 3/5 → 4/5 | s:ht pan:0.5 ]",
|
|
||||||
"[ 2/5 → 3/5 | s:mt pan:0.5 ]",
|
|
||||||
"[ 1/5 → 2/5 | s:ht pan:0.5 ]",
|
|
||||||
"[ 0/1 → 1/5 | s:hh pan:0.5 ]",
|
|
||||||
"[ 1/1 → 6/5 | s:lt pan:0.5 ]",
|
|
||||||
"[ 6/5 → 7/5 | s:ht pan:0.5 ]",
|
|
||||||
"[ 7/5 → 8/5 | s:mt pan:0.5 ]",
|
|
||||||
"[ 8/5 → 9/5 | s:ht pan:0.5 ]",
|
|
||||||
"[ 9/5 → 2/1 | s:hh pan:0.5 ]",
|
|
||||||
"[ 9/5 → 2/1 | s:lt pan:0.5 ]",
|
|
||||||
"[ 8/5 → 9/5 | s:ht pan:0.5 ]",
|
|
||||||
"[ 7/5 → 8/5 | s:mt pan:0.5 ]",
|
|
||||||
"[ 6/5 → 7/5 | s:ht pan:0.5 ]",
|
|
||||||
"[ 1/1 → 6/5 | s:hh pan:0.5 ]",
|
|
||||||
"[ 2/1 → 11/5 | s:lt pan:0.25 ]",
|
|
||||||
"[ 11/5 → 12/5 | s:ht pan:0.25 ]",
|
|
||||||
"[ 12/5 → 13/5 | s:mt pan:0.25 ]",
|
|
||||||
"[ 13/5 → 14/5 | s:ht pan:0.25 ]",
|
|
||||||
"[ 14/5 → 3/1 | s:hh pan:0.25 ]",
|
|
||||||
"[ 14/5 → 3/1 | s:lt pan:0.75 ]",
|
|
||||||
"[ 13/5 → 14/5 | s:ht pan:0.75 ]",
|
|
||||||
"[ 12/5 → 13/5 | s:mt pan:0.75 ]",
|
|
||||||
"[ 11/5 → 12/5 | s:ht pan:0.75 ]",
|
|
||||||
"[ 2/1 → 11/5 | s:hh pan:0.75 ]",
|
|
||||||
"[ 3/1 → 16/5 | s:lt pan:0.25 ]",
|
|
||||||
"[ 16/5 → 17/5 | s:ht pan:0.25 ]",
|
|
||||||
"[ 17/5 → 18/5 | s:mt pan:0.25 ]",
|
|
||||||
"[ 18/5 → 19/5 | s:ht pan:0.25 ]",
|
|
||||||
"[ 19/5 → 4/1 | s:hh pan:0.25 ]",
|
|
||||||
"[ 19/5 → 4/1 | s:lt pan:0.75 ]",
|
|
||||||
"[ 18/5 → 19/5 | s:ht pan:0.75 ]",
|
|
||||||
"[ 17/5 → 18/5 | s:mt pan:0.75 ]",
|
|
||||||
"[ 16/5 → 17/5 | s:ht pan:0.75 ]",
|
|
||||||
"[ 3/1 → 16/5 | s:hh pan:0.75 ]",
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`runs examples > example "lastOf" example index 0 1`] = `
|
exports[`runs examples > example "lastOf" example index 0 1`] = `
|
||||||
[
|
[
|
||||||
"[ 0/1 → 1/4 | note:c3 ]",
|
"[ 0/1 → 1/4 | note:c3 ]",
|
||||||
@@ -2315,51 +2038,6 @@ exports[`runs examples > example "leslie" example index 0 1`] = `
|
|||||||
]
|
]
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`runs examples > example "linger" example index 0 1`] = `
|
|
||||||
[
|
|
||||||
"[ 0/1 → 1/4 | s:lt ]",
|
|
||||||
"[ 1/4 → 1/2 | s:ht ]",
|
|
||||||
"[ 1/2 → 3/4 | s:mt ]",
|
|
||||||
"[ 3/4 → 1/1 | s:cp ]",
|
|
||||||
"[ 0/1 → 1/4 | s:hh ]",
|
|
||||||
"[ 1/2 → 3/4 | s:hh ]",
|
|
||||||
"[ 1/4 → 1/2 | s:oh ]",
|
|
||||||
"[ 3/4 → 1/1 | s:oh ]",
|
|
||||||
"[ 1/1 → 5/4 | s:lt ]",
|
|
||||||
"[ 5/4 → 3/2 | s:ht ]",
|
|
||||||
"[ 1/1 → 5/4 | s:hh ]",
|
|
||||||
"[ 5/4 → 3/2 | s:oh ]",
|
|
||||||
"[ 3/2 → 7/4 | s:lt ]",
|
|
||||||
"[ 7/4 → 2/1 | s:ht ]",
|
|
||||||
"[ 3/2 → 7/4 | s:hh ]",
|
|
||||||
"[ 7/4 → 2/1 | s:oh ]",
|
|
||||||
"[ 2/1 → 9/4 | s:lt ]",
|
|
||||||
"[ 2/1 → 9/4 | s:hh ]",
|
|
||||||
"[ 9/4 → 5/2 | s:lt ]",
|
|
||||||
"[ 9/4 → 5/2 | s:hh ]",
|
|
||||||
"[ 5/2 → 11/4 | s:lt ]",
|
|
||||||
"[ 5/2 → 11/4 | s:hh ]",
|
|
||||||
"[ 11/4 → 3/1 | s:lt ]",
|
|
||||||
"[ 11/4 → 3/1 | s:hh ]",
|
|
||||||
"[ (3/1 → 25/8) ⇝ 13/4 | s:lt ]",
|
|
||||||
"[ (3/1 → 25/8) ⇝ 13/4 | s:hh ]",
|
|
||||||
"[ (25/8 → 13/4) ⇝ 27/8 | s:lt ]",
|
|
||||||
"[ (25/8 → 13/4) ⇝ 27/8 | s:hh ]",
|
|
||||||
"[ (13/4 → 27/8) ⇝ 7/2 | s:lt ]",
|
|
||||||
"[ (13/4 → 27/8) ⇝ 7/2 | s:hh ]",
|
|
||||||
"[ (27/8 → 7/2) ⇝ 29/8 | s:lt ]",
|
|
||||||
"[ (27/8 → 7/2) ⇝ 29/8 | s:hh ]",
|
|
||||||
"[ (7/2 → 29/8) ⇝ 15/4 | s:lt ]",
|
|
||||||
"[ (7/2 → 29/8) ⇝ 15/4 | s:hh ]",
|
|
||||||
"[ (29/8 → 15/4) ⇝ 31/8 | s:lt ]",
|
|
||||||
"[ (29/8 → 15/4) ⇝ 31/8 | s:hh ]",
|
|
||||||
"[ (15/4 → 31/8) ⇝ 4/1 | s:lt ]",
|
|
||||||
"[ (15/4 → 31/8) ⇝ 4/1 | s:hh ]",
|
|
||||||
"[ (31/8 → 4/1) ⇝ 33/8 | s:lt ]",
|
|
||||||
"[ (31/8 → 4/1) ⇝ 33/8 | s:hh ]",
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`runs examples > example "loop" example index 0 1`] = `
|
exports[`runs examples > example "loop" example index 0 1`] = `
|
||||||
[
|
[
|
||||||
"[ 0/1 → 1/1 | s:bd loop:1 ]",
|
"[ 0/1 → 1/1 | s:bd loop:1 ]",
|
||||||
@@ -2421,20 +2099,6 @@ exports[`runs examples > example "lsize" example index 0 1`] = `
|
|||||||
]
|
]
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`runs examples > example "mask" example index 0 1`] = `
|
|
||||||
[
|
|
||||||
"[ 0/1 → 1/2 | note:c ]",
|
|
||||||
"[ 1/2 → 1/1 | note:eb ]",
|
|
||||||
"[ 1/2 → 1/1 | note:g ]",
|
|
||||||
"[ 1/1 → 3/2 | note:d ]",
|
|
||||||
"[ 3/2 → 2/1 | note:eb ]",
|
|
||||||
"[ 3/2 → 2/1 | note:g ]",
|
|
||||||
"[ 3/1 → 7/2 | note:d ]",
|
|
||||||
"[ 7/2 → 4/1 | note:eb ]",
|
|
||||||
"[ 7/2 → 4/1 | note:g ]",
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`runs examples > example "mul" example index 0 1`] = `
|
exports[`runs examples > example "mul" example index 0 1`] = `
|
||||||
[
|
[
|
||||||
"[ 0/1 → 1/3 | freq:150 ]",
|
"[ 0/1 → 1/3 | freq:150 ]",
|
||||||
@@ -2640,38 +2304,6 @@ exports[`runs examples > example "orbit" example index 0 1`] = `
|
|||||||
]
|
]
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`runs examples > example "outside" example index 0 1`] = `
|
|
||||||
[
|
|
||||||
"[ 0/1 → 1/1 | note:A3 ]",
|
|
||||||
"[ 3/2 → 2/1 | note:F3 ]",
|
|
||||||
"[ 1/1 → 3/2 | note:G3 ]",
|
|
||||||
"[ 2/1 → 3/1 | note:E3 ]",
|
|
||||||
"[ 7/2 → 4/1 | note:C3 ]",
|
|
||||||
"[ 3/1 → 7/2 | note:D3 ]",
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`runs examples > example "palindrome" example index 0 1`] = `
|
|
||||||
[
|
|
||||||
"[ 3/4 → 1/1 | note:c ]",
|
|
||||||
"[ 1/2 → 3/4 | note:d ]",
|
|
||||||
"[ 1/4 → 1/2 | note:e ]",
|
|
||||||
"[ 0/1 → 1/4 | note:g ]",
|
|
||||||
"[ 1/1 → 5/4 | note:c ]",
|
|
||||||
"[ 5/4 → 3/2 | note:d ]",
|
|
||||||
"[ 3/2 → 7/4 | note:e ]",
|
|
||||||
"[ 7/4 → 2/1 | note:g ]",
|
|
||||||
"[ 11/4 → 3/1 | note:c ]",
|
|
||||||
"[ 5/2 → 11/4 | note:d ]",
|
|
||||||
"[ 9/4 → 5/2 | note:e ]",
|
|
||||||
"[ 2/1 → 9/4 | note:g ]",
|
|
||||||
"[ 3/1 → 13/4 | note:c ]",
|
|
||||||
"[ 13/4 → 7/2 | note:d ]",
|
|
||||||
"[ 7/2 → 15/4 | note:e ]",
|
|
||||||
"[ 15/4 → 4/1 | note:g ]",
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`runs examples > example "pan" example index 0 1`] = `
|
exports[`runs examples > example "pan" example index 0 1`] = `
|
||||||
[
|
[
|
||||||
"[ 0/1 → 1/4 | s:bd pan:0.5 ]",
|
"[ 0/1 → 1/4 | s:bd pan:0.5 ]",
|
||||||
@@ -2722,32 +2354,6 @@ exports[`runs examples > example "perlin" example index 0 1`] = `
|
|||||||
]
|
]
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`runs examples > example "ply" example index 0 1`] = `
|
|
||||||
[
|
|
||||||
"[ 0/1 → 1/4 | s:bd ]",
|
|
||||||
"[ 1/2 → 3/4 | s:sd ]",
|
|
||||||
"[ 3/4 → 1/1 | s:cp ]",
|
|
||||||
"[ 1/1 → 9/8 | s:bd ]",
|
|
||||||
"[ 9/8 → 5/4 | s:bd ]",
|
|
||||||
"[ 3/2 → 13/8 | s:sd ]",
|
|
||||||
"[ 13/8 → 7/4 | s:sd ]",
|
|
||||||
"[ 7/4 → 15/8 | s:cp ]",
|
|
||||||
"[ 15/8 → 2/1 | s:cp ]",
|
|
||||||
"[ 2/1 → 25/12 | s:bd ]",
|
|
||||||
"[ 25/12 → 13/6 | s:bd ]",
|
|
||||||
"[ 13/6 → 9/4 | s:bd ]",
|
|
||||||
"[ 5/2 → 31/12 | s:sd ]",
|
|
||||||
"[ 31/12 → 8/3 | s:sd ]",
|
|
||||||
"[ 8/3 → 11/4 | s:sd ]",
|
|
||||||
"[ 11/4 → 17/6 | s:cp ]",
|
|
||||||
"[ 17/6 → 35/12 | s:cp ]",
|
|
||||||
"[ 35/12 → 3/1 | s:cp ]",
|
|
||||||
"[ 3/1 → 13/4 | s:bd ]",
|
|
||||||
"[ 7/2 → 15/4 | s:sd ]",
|
|
||||||
"[ 15/4 → 4/1 | s:cp ]",
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`runs examples > example "polymeter" example index 0 1`] = `
|
exports[`runs examples > example "polymeter" example index 0 1`] = `
|
||||||
[
|
[
|
||||||
"[ 0/1 → 1/3 | note:c ]",
|
"[ 0/1 → 1/3 | note:c ]",
|
||||||
@@ -2863,64 +2469,6 @@ exports[`runs examples > example "range" example index 0 1`] = `
|
|||||||
]
|
]
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`runs examples > example "range2" example index 0 1`] = `
|
|
||||||
[
|
|
||||||
"[ 0/1 → 1/2 | s:bd cutoff:1537.0125742738173 ]",
|
|
||||||
"[ 1/2 → 1/1 | s:sd cutoff:1942.909649383465 ]",
|
|
||||||
"[ 0/1 → 1/4 | s:hh cutoff:1396.3177415120963 ]",
|
|
||||||
"[ 1/4 → 1/2 | s:hh cutoff:1666.6776747647016 ]",
|
|
||||||
"[ 1/2 → 3/4 | s:hh cutoff:1873.602209226909 ]",
|
|
||||||
"[ 3/4 → 1/1 | s:hh cutoff:1985.5889603024227 ]",
|
|
||||||
"[ 1/1 → 3/2 | s:bd cutoff:1942.909649383465 ]",
|
|
||||||
"[ 3/2 → 2/1 | s:sd cutoff:1537.0125742738173 ]",
|
|
||||||
"[ 1/1 → 5/4 | s:hh cutoff:1985.5889603024227 ]",
|
|
||||||
"[ 5/4 → 3/2 | s:hh cutoff:1873.602209226909 ]",
|
|
||||||
"[ 3/2 → 7/4 | s:hh cutoff:1666.6776747647016 ]",
|
|
||||||
"[ 7/4 → 2/1 | s:hh cutoff:1396.3177415120965 ]",
|
|
||||||
"[ 2/1 → 5/2 | s:bd cutoff:962.9874257261828 ]",
|
|
||||||
"[ 5/2 → 3/1 | s:sd cutoff:557.0903506165351 ]",
|
|
||||||
"[ 2/1 → 9/4 | s:hh cutoff:1103.6822584879037 ]",
|
|
||||||
"[ 9/4 → 5/2 | s:hh cutoff:833.3223252352985 ]",
|
|
||||||
"[ 5/2 → 11/4 | s:hh cutoff:626.3977907730911 ]",
|
|
||||||
"[ 11/4 → 3/1 | s:hh cutoff:514.4110396975773 ]",
|
|
||||||
"[ 3/1 → 7/2 | s:bd cutoff:557.090350616535 ]",
|
|
||||||
"[ 7/2 → 4/1 | s:sd cutoff:962.9874257261822 ]",
|
|
||||||
"[ 3/1 → 13/4 | s:hh cutoff:514.4110396975772 ]",
|
|
||||||
"[ 13/4 → 7/2 | s:hh cutoff:626.397790773091 ]",
|
|
||||||
"[ 7/2 → 15/4 | s:hh cutoff:833.3223252352984 ]",
|
|
||||||
"[ 15/4 → 4/1 | s:hh cutoff:1103.6822584879035 ]",
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`runs examples > example "rangex" example index 0 1`] = `
|
|
||||||
[
|
|
||||||
"[ 0/1 → 1/2 | s:bd cutoff:1303.7646214531312 ]",
|
|
||||||
"[ 1/2 → 1/1 | s:sd cutoff:1897.2102033890155 ]",
|
|
||||||
"[ 0/1 → 1/4 | s:hh cutoff:1144.7958299590086 ]",
|
|
||||||
"[ 1/4 → 1/2 | s:hh cutoff:1469.7494449054268 ]",
|
|
||||||
"[ 1/2 → 3/4 | s:hh cutoff:1779.4971377133336 ]",
|
|
||||||
"[ 3/4 → 1/1 | s:hh cutoff:1973.5393435463043 ]",
|
|
||||||
"[ 1/1 → 3/2 | s:bd cutoff:1897.2102033890155 ]",
|
|
||||||
"[ 3/2 → 2/1 | s:sd cutoff:1303.7646214531312 ]",
|
|
||||||
"[ 1/1 → 5/4 | s:hh cutoff:1973.5393435463043 ]",
|
|
||||||
"[ 5/4 → 3/2 | s:hh cutoff:1779.4971377133336 ]",
|
|
||||||
"[ 3/2 → 7/4 | s:hh cutoff:1469.7494449054268 ]",
|
|
||||||
"[ 7/4 → 2/1 | s:hh cutoff:1144.7958299590086 ]",
|
|
||||||
"[ 2/1 → 5/2 | s:bd cutoff:767.0096147304824 ]",
|
|
||||||
"[ 5/2 → 3/1 | s:sd cutoff:527.0897226958216 ]",
|
|
||||||
"[ 2/1 → 9/4 | s:hh cutoff:873.5182063301246 ]",
|
|
||||||
"[ 9/4 → 5/2 | s:hh cutoff:680.3880780266911 ]",
|
|
||||||
"[ 5/2 → 11/4 | s:hh cutoff:561.9565094018676 ]",
|
|
||||||
"[ 11/4 → 3/1 | s:hh cutoff:506.70385835991164 ]",
|
|
||||||
"[ 3/1 → 7/2 | s:bd cutoff:527.0897226958216 ]",
|
|
||||||
"[ 7/2 → 4/1 | s:sd cutoff:767.0096147304824 ]",
|
|
||||||
"[ 3/1 → 13/4 | s:hh cutoff:506.7038583599112 ]",
|
|
||||||
"[ 13/4 → 7/2 | s:hh cutoff:561.9565094018676 ]",
|
|
||||||
"[ 7/2 → 15/4 | s:hh cutoff:680.3880780266911 ]",
|
|
||||||
"[ 15/4 → 4/1 | s:hh cutoff:873.5182063301246 ]",
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`runs examples > example "rarely" example index 0 1`] = `
|
exports[`runs examples > example "rarely" example index 0 1`] = `
|
||||||
[
|
[
|
||||||
"[ 0/1 → 1/8 | s:hh ]",
|
"[ 0/1 → 1/8 | s:hh ]",
|
||||||
@@ -2979,35 +2527,6 @@ exports[`runs examples > example "release" example index 0 1`] = `
|
|||||||
]
|
]
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`runs examples > example "reset" example index 0 1`] = `
|
|
||||||
[
|
|
||||||
"[ 0/1 → 1/2 | s:bd ]",
|
|
||||||
"[ 1/2 → 1/1 | s:sd ]",
|
|
||||||
"[ 0/1 → 1/4 | s:hh ]",
|
|
||||||
"[ 1/4 → 1/2 | s:hh ]",
|
|
||||||
"[ 1/2 → 3/4 | s:hh ]",
|
|
||||||
"[ 3/4 → 1/1 | s:hh ]",
|
|
||||||
"[ 1/1 → 3/2 | s:lt ]",
|
|
||||||
"[ 3/2 → 2/1 | s:sd ]",
|
|
||||||
"[ 1/1 → 5/4 | s:hh ]",
|
|
||||||
"[ 5/4 → 3/2 | s:hh ]",
|
|
||||||
"[ 3/2 → 7/4 | s:hh ]",
|
|
||||||
"[ 7/4 → 2/1 | s:hh ]",
|
|
||||||
"[ 2/1 → 5/2 | s:bd ]",
|
|
||||||
"[ 5/2 → 3/1 | s:sd ]",
|
|
||||||
"[ 2/1 → 9/4 | s:hh ]",
|
|
||||||
"[ 9/4 → 5/2 | s:hh ]",
|
|
||||||
"[ 5/2 → 11/4 | s:hh ]",
|
|
||||||
"[ 11/4 → 3/1 | s:hh ]",
|
|
||||||
"[ 3/1 → 25/8 | s:lt ]",
|
|
||||||
"[ 3/1 → 25/8 | s:hh ]",
|
|
||||||
"[ 27/8 → 7/2 | s:lt ]",
|
|
||||||
"[ 27/8 → 7/2 | s:hh ]",
|
|
||||||
"[ 15/4 → 31/8 | s:lt ]",
|
|
||||||
"[ 15/4 → 31/8 | s:hh ]",
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`runs examples > example "resonance" example index 0 1`] = `
|
exports[`runs examples > example "resonance" example index 0 1`] = `
|
||||||
[
|
[
|
||||||
"[ 0/1 → 1/2 | s:bd cutoff:2000 resonance:0 ]",
|
"[ 0/1 → 1/2 | s:bd cutoff:2000 resonance:0 ]",
|
||||||
@@ -3037,35 +2556,6 @@ exports[`runs examples > example "resonance" example index 0 1`] = `
|
|||||||
]
|
]
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`runs examples > example "restart" example index 0 1`] = `
|
|
||||||
[
|
|
||||||
"[ 0/1 → 1/2 | s:bd ]",
|
|
||||||
"[ 1/2 → 1/1 | s:sd ]",
|
|
||||||
"[ 0/1 → 1/4 | s:hh ]",
|
|
||||||
"[ 1/4 → 1/2 | s:hh ]",
|
|
||||||
"[ 1/2 → 3/4 | s:hh ]",
|
|
||||||
"[ 3/4 → 1/1 | s:hh ]",
|
|
||||||
"[ 1/1 → 3/2 | s:lt ]",
|
|
||||||
"[ 3/2 → 2/1 | s:sd ]",
|
|
||||||
"[ 1/1 → 5/4 | s:hh ]",
|
|
||||||
"[ 5/4 → 3/2 | s:hh ]",
|
|
||||||
"[ 3/2 → 7/4 | s:hh ]",
|
|
||||||
"[ 7/4 → 2/1 | s:hh ]",
|
|
||||||
"[ 2/1 → 5/2 | s:bd ]",
|
|
||||||
"[ 5/2 → 3/1 | s:sd ]",
|
|
||||||
"[ 2/1 → 9/4 | s:hh ]",
|
|
||||||
"[ 9/4 → 5/2 | s:hh ]",
|
|
||||||
"[ 5/2 → 11/4 | s:hh ]",
|
|
||||||
"[ 11/4 → 3/1 | s:hh ]",
|
|
||||||
"[ 3/1 → 25/8 | s:bd ]",
|
|
||||||
"[ 3/1 → 25/8 | s:hh ]",
|
|
||||||
"[ 27/8 → 7/2 | s:bd ]",
|
|
||||||
"[ 27/8 → 7/2 | s:hh ]",
|
|
||||||
"[ 15/4 → 31/8 | s:bd ]",
|
|
||||||
"[ 15/4 → 31/8 | s:hh ]",
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`runs examples > example "rev" example index 0 1`] = `
|
exports[`runs examples > example "rev" example index 0 1`] = `
|
||||||
[
|
[
|
||||||
"[ 3/4 → 1/1 | note:c3 ]",
|
"[ 3/4 → 1/1 | note:c3 ]",
|
||||||
@@ -3100,19 +2590,6 @@ exports[`runs examples > example "room" example index 0 1`] = `
|
|||||||
]
|
]
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`runs examples > example "roomsize" example index 0 1`] = `
|
|
||||||
[
|
|
||||||
"[ 0/1 → 1/2 | s:bd room:0.8 roomsize:0 ]",
|
|
||||||
"[ 1/2 → 1/1 | s:sd room:0.8 roomsize:0 ]",
|
|
||||||
"[ 1/1 → 3/2 | s:bd room:0.8 roomsize:1 ]",
|
|
||||||
"[ 3/2 → 2/1 | s:sd room:0.8 roomsize:1 ]",
|
|
||||||
"[ 2/1 → 5/2 | s:bd room:0.8 roomsize:2 ]",
|
|
||||||
"[ 5/2 → 3/1 | s:sd room:0.8 roomsize:2 ]",
|
|
||||||
"[ 3/1 → 7/2 | s:bd room:0.8 roomsize:4 ]",
|
|
||||||
"[ 7/2 → 4/1 | s:sd room:0.8 roomsize:4 ]",
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`runs examples > example "rootNotes" example index 0 1`] = `
|
exports[`runs examples > example "rootNotes" example index 0 1`] = `
|
||||||
[
|
[
|
||||||
"[ 0/1 → 1/1 | note:C2 ]",
|
"[ 0/1 → 1/1 | note:C2 ]",
|
||||||
@@ -3269,107 +2746,6 @@ exports[`runs examples > example "scaleTranspose" example index 0 1`] = `
|
|||||||
]
|
]
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`runs examples > example "segment" example index 0 1`] = `
|
|
||||||
[
|
|
||||||
"[ 0/1 → 1/24 | note:40.25 ]",
|
|
||||||
"[ 1/24 → 1/12 | note:40.75 ]",
|
|
||||||
"[ 1/12 → 1/8 | note:41.25 ]",
|
|
||||||
"[ 1/8 → 1/6 | note:41.75 ]",
|
|
||||||
"[ 1/6 → 5/24 | note:42.25 ]",
|
|
||||||
"[ 5/24 → 1/4 | note:42.75 ]",
|
|
||||||
"[ 1/4 → 7/24 | note:43.25 ]",
|
|
||||||
"[ 7/24 → 1/3 | note:43.75 ]",
|
|
||||||
"[ 1/3 → 3/8 | note:44.25 ]",
|
|
||||||
"[ 3/8 → 5/12 | note:44.75 ]",
|
|
||||||
"[ 5/12 → 11/24 | note:45.25 ]",
|
|
||||||
"[ 11/24 → 1/2 | note:45.75 ]",
|
|
||||||
"[ 1/2 → 13/24 | note:46.25 ]",
|
|
||||||
"[ 13/24 → 7/12 | note:46.75 ]",
|
|
||||||
"[ 7/12 → 5/8 | note:47.25 ]",
|
|
||||||
"[ 5/8 → 2/3 | note:47.75 ]",
|
|
||||||
"[ 2/3 → 17/24 | note:48.25 ]",
|
|
||||||
"[ 17/24 → 3/4 | note:48.75 ]",
|
|
||||||
"[ 3/4 → 19/24 | note:49.25 ]",
|
|
||||||
"[ 19/24 → 5/6 | note:49.75 ]",
|
|
||||||
"[ 5/6 → 7/8 | note:50.25 ]",
|
|
||||||
"[ 7/8 → 11/12 | note:50.75 ]",
|
|
||||||
"[ 11/12 → 23/24 | note:51.25 ]",
|
|
||||||
"[ 23/24 → 1/1 | note:51.75 ]",
|
|
||||||
"[ 1/1 → 25/24 | note:40.25 ]",
|
|
||||||
"[ 25/24 → 13/12 | note:40.75 ]",
|
|
||||||
"[ 13/12 → 9/8 | note:41.25 ]",
|
|
||||||
"[ 9/8 → 7/6 | note:41.75 ]",
|
|
||||||
"[ 7/6 → 29/24 | note:42.25 ]",
|
|
||||||
"[ 29/24 → 5/4 | note:42.75 ]",
|
|
||||||
"[ 5/4 → 31/24 | note:43.25 ]",
|
|
||||||
"[ 31/24 → 4/3 | note:43.75 ]",
|
|
||||||
"[ 4/3 → 11/8 | note:44.25 ]",
|
|
||||||
"[ 11/8 → 17/12 | note:44.75 ]",
|
|
||||||
"[ 17/12 → 35/24 | note:45.25 ]",
|
|
||||||
"[ 35/24 → 3/2 | note:45.75 ]",
|
|
||||||
"[ 3/2 → 37/24 | note:46.25 ]",
|
|
||||||
"[ 37/24 → 19/12 | note:46.75 ]",
|
|
||||||
"[ 19/12 → 13/8 | note:47.25 ]",
|
|
||||||
"[ 13/8 → 5/3 | note:47.75 ]",
|
|
||||||
"[ 5/3 → 41/24 | note:48.25 ]",
|
|
||||||
"[ 41/24 → 7/4 | note:48.75 ]",
|
|
||||||
"[ 7/4 → 43/24 | note:49.25 ]",
|
|
||||||
"[ 43/24 → 11/6 | note:49.75 ]",
|
|
||||||
"[ 11/6 → 15/8 | note:50.25 ]",
|
|
||||||
"[ 15/8 → 23/12 | note:50.75 ]",
|
|
||||||
"[ 23/12 → 47/24 | note:51.25 ]",
|
|
||||||
"[ 47/24 → 2/1 | note:51.75 ]",
|
|
||||||
"[ 2/1 → 49/24 | note:40.25 ]",
|
|
||||||
"[ 49/24 → 25/12 | note:40.75 ]",
|
|
||||||
"[ 25/12 → 17/8 | note:41.25 ]",
|
|
||||||
"[ 17/8 → 13/6 | note:41.75 ]",
|
|
||||||
"[ 13/6 → 53/24 | note:42.25 ]",
|
|
||||||
"[ 53/24 → 9/4 | note:42.75 ]",
|
|
||||||
"[ 9/4 → 55/24 | note:43.25 ]",
|
|
||||||
"[ 55/24 → 7/3 | note:43.75 ]",
|
|
||||||
"[ 7/3 → 19/8 | note:44.25 ]",
|
|
||||||
"[ 19/8 → 29/12 | note:44.75 ]",
|
|
||||||
"[ 29/12 → 59/24 | note:45.25 ]",
|
|
||||||
"[ 59/24 → 5/2 | note:45.75 ]",
|
|
||||||
"[ 5/2 → 61/24 | note:46.25 ]",
|
|
||||||
"[ 61/24 → 31/12 | note:46.75 ]",
|
|
||||||
"[ 31/12 → 21/8 | note:47.25 ]",
|
|
||||||
"[ 21/8 → 8/3 | note:47.75 ]",
|
|
||||||
"[ 8/3 → 65/24 | note:48.25 ]",
|
|
||||||
"[ 65/24 → 11/4 | note:48.75 ]",
|
|
||||||
"[ 11/4 → 67/24 | note:49.25 ]",
|
|
||||||
"[ 67/24 → 17/6 | note:49.75 ]",
|
|
||||||
"[ 17/6 → 23/8 | note:50.25 ]",
|
|
||||||
"[ 23/8 → 35/12 | note:50.75 ]",
|
|
||||||
"[ 35/12 → 71/24 | note:51.25 ]",
|
|
||||||
"[ 71/24 → 3/1 | note:51.75 ]",
|
|
||||||
"[ 3/1 → 73/24 | note:40.25 ]",
|
|
||||||
"[ 73/24 → 37/12 | note:40.75 ]",
|
|
||||||
"[ 37/12 → 25/8 | note:41.25 ]",
|
|
||||||
"[ 25/8 → 19/6 | note:41.75 ]",
|
|
||||||
"[ 19/6 → 77/24 | note:42.25 ]",
|
|
||||||
"[ 77/24 → 13/4 | note:42.75 ]",
|
|
||||||
"[ 13/4 → 79/24 | note:43.25 ]",
|
|
||||||
"[ 79/24 → 10/3 | note:43.75 ]",
|
|
||||||
"[ 10/3 → 27/8 | note:44.25 ]",
|
|
||||||
"[ 27/8 → 41/12 | note:44.75 ]",
|
|
||||||
"[ 41/12 → 83/24 | note:45.25 ]",
|
|
||||||
"[ 83/24 → 7/2 | note:45.75 ]",
|
|
||||||
"[ 7/2 → 85/24 | note:46.25 ]",
|
|
||||||
"[ 85/24 → 43/12 | note:46.75 ]",
|
|
||||||
"[ 43/12 → 29/8 | note:47.25 ]",
|
|
||||||
"[ 29/8 → 11/3 | note:47.75 ]",
|
|
||||||
"[ 11/3 → 89/24 | note:48.25 ]",
|
|
||||||
"[ 89/24 → 15/4 | note:48.75 ]",
|
|
||||||
"[ 15/4 → 91/24 | note:49.25 ]",
|
|
||||||
"[ 91/24 → 23/6 | note:49.75 ]",
|
|
||||||
"[ 23/6 → 31/8 | note:50.25 ]",
|
|
||||||
"[ 31/8 → 47/12 | note:50.75 ]",
|
|
||||||
"[ 47/12 → 95/24 | note:51.25 ]",
|
|
||||||
"[ 95/24 → 4/1 | note:51.75 ]",
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`runs examples > example "seq" example index 0 1`] = `
|
exports[`runs examples > example "seq" example index 0 1`] = `
|
||||||
[
|
[
|
||||||
"[ 0/1 → 1/4 | s:hh ]",
|
"[ 0/1 → 1/4 | s:hh ]",
|
||||||
@@ -3729,43 +3105,6 @@ exports[`runs examples > example "struct" example index 0 1`] = `
|
|||||||
]
|
]
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`runs examples > example "stut" example index 0 1`] = `
|
|
||||||
[
|
|
||||||
"[ 0/1 → 1/2 | s:bd ]",
|
|
||||||
"[ 1/2 → 1/1 | s:sd ]",
|
|
||||||
"[ -1/3 ⇜ (0/1 → 1/6) | s:sd ]",
|
|
||||||
"[ 1/6 → 2/3 | s:bd ]",
|
|
||||||
"[ (2/3 → 1/1) ⇝ 7/6 | s:sd ]",
|
|
||||||
"[ -1/6 ⇜ (0/1 → 1/3) | s:sd ]",
|
|
||||||
"[ 1/3 → 5/6 | s:bd ]",
|
|
||||||
"[ (5/6 → 1/1) ⇝ 4/3 | s:sd ]",
|
|
||||||
"[ 1/1 → 3/2 | s:bd ]",
|
|
||||||
"[ 3/2 → 2/1 | s:sd ]",
|
|
||||||
"[ 2/3 ⇜ (1/1 → 7/6) | s:sd ]",
|
|
||||||
"[ 7/6 → 5/3 | s:bd ]",
|
|
||||||
"[ (5/3 → 2/1) ⇝ 13/6 | s:sd ]",
|
|
||||||
"[ 5/6 ⇜ (1/1 → 4/3) | s:sd ]",
|
|
||||||
"[ 4/3 → 11/6 | s:bd ]",
|
|
||||||
"[ (11/6 → 2/1) ⇝ 7/3 | s:sd ]",
|
|
||||||
"[ 2/1 → 5/2 | s:bd ]",
|
|
||||||
"[ 5/2 → 3/1 | s:sd ]",
|
|
||||||
"[ 5/3 ⇜ (2/1 → 13/6) | s:sd ]",
|
|
||||||
"[ 13/6 → 8/3 | s:bd ]",
|
|
||||||
"[ (8/3 → 3/1) ⇝ 19/6 | s:sd ]",
|
|
||||||
"[ 11/6 ⇜ (2/1 → 7/3) | s:sd ]",
|
|
||||||
"[ 7/3 → 17/6 | s:bd ]",
|
|
||||||
"[ (17/6 → 3/1) ⇝ 10/3 | s:sd ]",
|
|
||||||
"[ 3/1 → 7/2 | s:bd ]",
|
|
||||||
"[ 7/2 → 4/1 | s:sd ]",
|
|
||||||
"[ 8/3 ⇜ (3/1 → 19/6) | s:sd ]",
|
|
||||||
"[ 19/6 → 11/3 | s:bd ]",
|
|
||||||
"[ (11/3 → 4/1) ⇝ 25/6 | s:sd ]",
|
|
||||||
"[ 17/6 ⇜ (3/1 → 10/3) | s:sd ]",
|
|
||||||
"[ 10/3 → 23/6 | s:bd ]",
|
|
||||||
"[ (23/6 → 4/1) ⇝ 13/3 | s:sd ]",
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`runs examples > example "sub" example index 0 1`] = `
|
exports[`runs examples > example "sub" example index 0 1`] = `
|
||||||
[
|
[
|
||||||
"[ 0/1 → 1/3 | note:C4 ]",
|
"[ 0/1 → 1/3 | note:C4 ]",
|
||||||
@@ -4079,53 +3418,3 @@ exports[`runs examples > example "when" example index 0 1`] = `
|
|||||||
"[ 11/3 → 4/1 | note:50 ]",
|
"[ 11/3 → 4/1 | note:50 ]",
|
||||||
]
|
]
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`runs examples > example "withValue" example index 0 1`] = `
|
|
||||||
[
|
|
||||||
"[ 0/1 → 1/3 | 10 ]",
|
|
||||||
"[ 1/3 → 2/3 | 11 ]",
|
|
||||||
"[ 2/3 → 1/1 | 12 ]",
|
|
||||||
"[ 1/1 → 4/3 | 10 ]",
|
|
||||||
"[ 4/3 → 5/3 | 11 ]",
|
|
||||||
"[ 5/3 → 2/1 | 12 ]",
|
|
||||||
"[ 2/1 → 7/3 | 10 ]",
|
|
||||||
"[ 7/3 → 8/3 | 11 ]",
|
|
||||||
"[ 8/3 → 3/1 | 12 ]",
|
|
||||||
"[ 3/1 → 10/3 | 10 ]",
|
|
||||||
"[ 10/3 → 11/3 | 11 ]",
|
|
||||||
"[ 11/3 → 4/1 | 12 ]",
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`runs examples > example "zoom" example index 0 1`] = `
|
|
||||||
[
|
|
||||||
"[ 0/1 → 1/6 | s:hh ]",
|
|
||||||
"[ 1/6 → 1/3 | s:hh ]",
|
|
||||||
"[ 1/3 → 1/2 | s:hh ]",
|
|
||||||
"[ 1/2 → 5/8 | s:sd ]",
|
|
||||||
"[ 3/4 → 7/8 | s:sd ]",
|
|
||||||
"[ 5/8 → 3/4 | s:bd ]",
|
|
||||||
"[ 7/8 → 1/1 | s:bd ]",
|
|
||||||
"[ 1/1 → 7/6 | s:hh ]",
|
|
||||||
"[ 7/6 → 4/3 | s:hh ]",
|
|
||||||
"[ 4/3 → 3/2 | s:hh ]",
|
|
||||||
"[ 3/2 → 13/8 | s:sd ]",
|
|
||||||
"[ 7/4 → 15/8 | s:sd ]",
|
|
||||||
"[ 13/8 → 7/4 | s:bd ]",
|
|
||||||
"[ 15/8 → 2/1 | s:bd ]",
|
|
||||||
"[ 2/1 → 13/6 | s:hh ]",
|
|
||||||
"[ 13/6 → 7/3 | s:hh ]",
|
|
||||||
"[ 7/3 → 5/2 | s:hh ]",
|
|
||||||
"[ 5/2 → 21/8 | s:sd ]",
|
|
||||||
"[ 11/4 → 23/8 | s:sd ]",
|
|
||||||
"[ 21/8 → 11/4 | s:bd ]",
|
|
||||||
"[ 23/8 → 3/1 | s:bd ]",
|
|
||||||
"[ 3/1 → 19/6 | s:hh ]",
|
|
||||||
"[ 19/6 → 10/3 | s:hh ]",
|
|
||||||
"[ 10/3 → 7/2 | s:hh ]",
|
|
||||||
"[ 7/2 → 29/8 | s:sd ]",
|
|
||||||
"[ 15/4 → 31/8 | s:sd ]",
|
|
||||||
"[ 29/8 → 15/4 | s:bd ]",
|
|
||||||
"[ 31/8 → 4/1 | s:bd ]",
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|||||||
+1
-1
@@ -116,7 +116,7 @@ strudel.Pattern.prototype.filter = function () {
|
|||||||
strudel.Pattern.prototype.adsr = function () {
|
strudel.Pattern.prototype.adsr = function () {
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
strudel.Pattern.prototype.webaudio = function () {
|
strudel.Pattern.prototype.out = function () {
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
strudel.Pattern.prototype.soundfont = function () {
|
strudel.Pattern.prototype.soundfont = function () {
|
||||||
|
|||||||
@@ -1,269 +0,0 @@
|
|||||||
{
|
|
||||||
"/home/felix/projects/strudel/packages/core/fraction.mjs": [
|
|
||||||
"gcd"
|
|
||||||
],
|
|
||||||
"/home/felix/projects/strudel/packages/core/timespan.mjs": [
|
|
||||||
"TimeSpan"
|
|
||||||
],
|
|
||||||
"/home/felix/projects/strudel/packages/core/hap.mjs": [
|
|
||||||
"Hap"
|
|
||||||
],
|
|
||||||
"/home/felix/projects/strudel/packages/core/state.mjs": [
|
|
||||||
"State"
|
|
||||||
],
|
|
||||||
"/home/felix/projects/strudel/packages/core/util.mjs": [
|
|
||||||
"isNoteWithOctave",
|
|
||||||
"isNote",
|
|
||||||
"tokenizeNote",
|
|
||||||
"toMidi",
|
|
||||||
"fromMidi",
|
|
||||||
"freqToMidi",
|
|
||||||
"valueToMidi",
|
|
||||||
"_mod",
|
|
||||||
"getPlayableNoteValue",
|
|
||||||
"getFrequency",
|
|
||||||
"rotate",
|
|
||||||
"pipe",
|
|
||||||
"compose",
|
|
||||||
"flatten",
|
|
||||||
"id",
|
|
||||||
"constant",
|
|
||||||
"listRange",
|
|
||||||
"curry",
|
|
||||||
"parseNumeral",
|
|
||||||
"mapArgs",
|
|
||||||
"numeralArgs",
|
|
||||||
"parseFractional",
|
|
||||||
"fractionalArgs"
|
|
||||||
],
|
|
||||||
"/home/felix/projects/strudel/packages/core/value.mjs": [
|
|
||||||
"unionWithObj",
|
|
||||||
"valued",
|
|
||||||
"Value",
|
|
||||||
"map"
|
|
||||||
],
|
|
||||||
"/home/felix/projects/strudel/packages/core/drawLine.mjs": [],
|
|
||||||
"/home/felix/projects/strudel/packages/core/logger.mjs": [
|
|
||||||
"logKey",
|
|
||||||
"logger"
|
|
||||||
],
|
|
||||||
"/home/felix/projects/strudel/packages/core/pattern.mjs": [
|
|
||||||
"setStringParser",
|
|
||||||
"isPattern",
|
|
||||||
"reify",
|
|
||||||
"set",
|
|
||||||
"keep",
|
|
||||||
"keepif",
|
|
||||||
"mod",
|
|
||||||
"pow",
|
|
||||||
"band",
|
|
||||||
"bor",
|
|
||||||
"bxor",
|
|
||||||
"blshift",
|
|
||||||
"brshift",
|
|
||||||
"lt",
|
|
||||||
"gt",
|
|
||||||
"lte",
|
|
||||||
"gte",
|
|
||||||
"eq",
|
|
||||||
"eqt",
|
|
||||||
"ne",
|
|
||||||
"net",
|
|
||||||
"and",
|
|
||||||
"or",
|
|
||||||
"func",
|
|
||||||
"compressSpan",
|
|
||||||
"compressspan",
|
|
||||||
"focusSpan",
|
|
||||||
"focusspan",
|
|
||||||
"zoomArc",
|
|
||||||
"zoomarc",
|
|
||||||
"bypass",
|
|
||||||
"duration",
|
|
||||||
"color",
|
|
||||||
"colour",
|
|
||||||
"striate"
|
|
||||||
],
|
|
||||||
"/home/felix/projects/strudel/packages/core/controls.mjs": [],
|
|
||||||
"/home/felix/projects/strudel/packages/core/euclid.mjs": [
|
|
||||||
"euclidrot",
|
|
||||||
"euclidLegatoRot"
|
|
||||||
],
|
|
||||||
"/home/felix/projects/strudel/packages/core/signal.mjs": [
|
|
||||||
"steady",
|
|
||||||
"signal",
|
|
||||||
"isaw",
|
|
||||||
"isaw2",
|
|
||||||
"saw2",
|
|
||||||
"sine2",
|
|
||||||
"cosine2",
|
|
||||||
"square2",
|
|
||||||
"tri2",
|
|
||||||
"time",
|
|
||||||
"_brandBy",
|
|
||||||
"brandBy",
|
|
||||||
"brand",
|
|
||||||
"_irand",
|
|
||||||
"__chooseWith",
|
|
||||||
"randcat",
|
|
||||||
"wchoose",
|
|
||||||
"wchooseCycles",
|
|
||||||
"perlinWith",
|
|
||||||
"degradeByWith",
|
|
||||||
"undegrade"
|
|
||||||
],
|
|
||||||
"/home/felix/projects/strudel/packages/core/speak.mjs": [
|
|
||||||
"speak"
|
|
||||||
],
|
|
||||||
"/home/felix/projects/strudel/packages/core/evaluate.mjs": [
|
|
||||||
"evalScope",
|
|
||||||
"evaluate"
|
|
||||||
],
|
|
||||||
"/home/felix/projects/strudel/packages/core/zyklus.mjs": [],
|
|
||||||
"/home/felix/projects/strudel/packages/core/cyclist.mjs": [
|
|
||||||
"Cyclist"
|
|
||||||
],
|
|
||||||
"/home/felix/projects/strudel/packages/core/time.mjs": [
|
|
||||||
"getTime",
|
|
||||||
"setTime"
|
|
||||||
],
|
|
||||||
"/home/felix/projects/strudel/packages/core/repl.mjs": [
|
|
||||||
"repl"
|
|
||||||
],
|
|
||||||
"/home/felix/projects/strudel/packages/core/draw.mjs": [
|
|
||||||
"getDrawContext",
|
|
||||||
"cleanupDraw"
|
|
||||||
],
|
|
||||||
"/home/felix/projects/strudel/packages/core/animate.mjs": [
|
|
||||||
"x",
|
|
||||||
"y",
|
|
||||||
"w",
|
|
||||||
"h",
|
|
||||||
"a",
|
|
||||||
"r",
|
|
||||||
"fill",
|
|
||||||
"smear",
|
|
||||||
"rescale",
|
|
||||||
"moveXY",
|
|
||||||
"zoomIn"
|
|
||||||
],
|
|
||||||
"/home/felix/projects/strudel/packages/core/pianoroll.mjs": [
|
|
||||||
"pianoroll"
|
|
||||||
],
|
|
||||||
"/home/felix/projects/strudel/packages/core/ui.mjs": [
|
|
||||||
"backgroundImage",
|
|
||||||
"cleanupUi"
|
|
||||||
],
|
|
||||||
"/home/felix/projects/strudel/packages/core/gist.js": [],
|
|
||||||
"/home/felix/projects/strudel/packages/core/index.mjs": [],
|
|
||||||
"/home/felix/projects/strudel/packages/eval/shift-traverser/index.js": [],
|
|
||||||
"/home/felix/projects/strudel/packages/eval/shapeshifter.mjs": [
|
|
||||||
"addMiniLocations",
|
|
||||||
"minifyStrings",
|
|
||||||
"wrappedAsync",
|
|
||||||
"shouldAddReturn"
|
|
||||||
],
|
|
||||||
"/home/felix/projects/strudel/packages/eval/evaluate.mjs": [
|
|
||||||
"evaluate"
|
|
||||||
],
|
|
||||||
"/home/felix/projects/strudel/packages/eval/index.mjs": [],
|
|
||||||
"/home/felix/projects/strudel/packages/midi/midi.mjs": [
|
|
||||||
"WebMidi",
|
|
||||||
"enableWebMidi"
|
|
||||||
],
|
|
||||||
"/home/felix/projects/strudel/packages/midi/index.mjs": [],
|
|
||||||
"/home/felix/projects/strudel/packages/mini/krill-parser.js": [],
|
|
||||||
"/home/felix/projects/strudel/packages/mini/mini.mjs": [
|
|
||||||
"patternifyAST",
|
|
||||||
"mini",
|
|
||||||
"h",
|
|
||||||
"minify"
|
|
||||||
],
|
|
||||||
"/home/felix/projects/strudel/packages/mini/index.mjs": [],
|
|
||||||
"/home/felix/projects/strudel/packages/soundfonts/fontloader.mjs": [
|
|
||||||
"getFontBufferSource",
|
|
||||||
"getFontPitch"
|
|
||||||
],
|
|
||||||
"/home/felix/projects/strudel/packages/soundfonts/list.mjs": [
|
|
||||||
"instruments",
|
|
||||||
"drums",
|
|
||||||
"instrumentNames"
|
|
||||||
],
|
|
||||||
"/home/felix/projects/strudel/packages/soundfonts/sfumato.mjs": [
|
|
||||||
"loadSoundfont"
|
|
||||||
],
|
|
||||||
"/home/felix/projects/strudel/packages/soundfonts/index.mjs": [],
|
|
||||||
"/home/felix/projects/strudel/packages/tonal/tonal.mjs": [],
|
|
||||||
"/home/felix/projects/strudel/packages/tonal/voicings.mjs": [
|
|
||||||
"voicingRegistry",
|
|
||||||
"setVoicingRange"
|
|
||||||
],
|
|
||||||
"/home/felix/projects/strudel/packages/tonal/index.mjs": [],
|
|
||||||
"/home/felix/projects/strudel/packages/tone/tone.mjs": [
|
|
||||||
"Tone",
|
|
||||||
"getDefaultSynth",
|
|
||||||
"tone",
|
|
||||||
"amsynth",
|
|
||||||
"duosynth",
|
|
||||||
"fmsynth",
|
|
||||||
"membrane",
|
|
||||||
"metal",
|
|
||||||
"monosynth",
|
|
||||||
"noise",
|
|
||||||
"pluck",
|
|
||||||
"polysynth",
|
|
||||||
"sampler",
|
|
||||||
"players",
|
|
||||||
"synth",
|
|
||||||
"vol",
|
|
||||||
"lowpass",
|
|
||||||
"highpass",
|
|
||||||
"adsr",
|
|
||||||
"out"
|
|
||||||
],
|
|
||||||
"/home/felix/projects/strudel/packages/tone/index.mjs": [],
|
|
||||||
"/home/felix/projects/strudel/packages/transpiler/transpiler.mjs": [
|
|
||||||
"transpiler"
|
|
||||||
],
|
|
||||||
"/home/felix/projects/strudel/packages/transpiler/index.mjs": [
|
|
||||||
"evaluate"
|
|
||||||
],
|
|
||||||
"/home/felix/projects/strudel/packages/webaudio/feedbackdelay.mjs": [],
|
|
||||||
"/home/felix/projects/strudel/packages/webaudio/reverb.mjs": [],
|
|
||||||
"/home/felix/projects/strudel/packages/webaudio/sampler.mjs": [
|
|
||||||
"getCachedBuffer",
|
|
||||||
"getSampleBufferSource",
|
|
||||||
"loadBuffer",
|
|
||||||
"reverseBuffer",
|
|
||||||
"getLoadedBuffer",
|
|
||||||
"resetLoadedSamples",
|
|
||||||
"getLoadedSamples"
|
|
||||||
],
|
|
||||||
"/home/felix/projects/strudel/packages/webaudio/vowel.mjs": [
|
|
||||||
"vowelFormant"
|
|
||||||
],
|
|
||||||
"/home/felix/projects/strudel/packages/webaudio/webaudio.mjs": [
|
|
||||||
"getAudioContext",
|
|
||||||
"panic",
|
|
||||||
"initAudio",
|
|
||||||
"initAudioOnFirstClick",
|
|
||||||
"webaudioOutput",
|
|
||||||
"webaudioOutputTrigger"
|
|
||||||
],
|
|
||||||
"/home/felix/projects/strudel/packages/webaudio/index.mjs": [],
|
|
||||||
"/home/felix/projects/strudel/packages/webdirt/webdirt.mjs": [
|
|
||||||
"loadWebDirt"
|
|
||||||
],
|
|
||||||
"/home/felix/projects/strudel/packages/webdirt/index.mjs": [],
|
|
||||||
"/home/felix/projects/strudel/packages/xen/xen.mjs": [
|
|
||||||
"edo",
|
|
||||||
"xen",
|
|
||||||
"tuning"
|
|
||||||
],
|
|
||||||
"/home/felix/projects/strudel/packages/xen/tunejs.js": [],
|
|
||||||
"/home/felix/projects/strudel/packages/xen/tune.mjs": [
|
|
||||||
"tune"
|
|
||||||
],
|
|
||||||
"/home/felix/projects/strudel/packages/xen/index.mjs": [],
|
|
||||||
"/home/felix/projects/strudel/index.mjs": []
|
|
||||||
}
|
|
||||||
@@ -1,82 +0,0 @@
|
|||||||
import { readFile } from 'node:fs/promises';
|
|
||||||
import { dirname, resolve } from 'path';
|
|
||||||
import { fileURLToPath } from 'url';
|
|
||||||
import { parse } from 'acorn';
|
|
||||||
import dependencyTree from 'dependency-tree';
|
|
||||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
||||||
|
|
||||||
function getExports(code) {
|
|
||||||
// parse it with acorn
|
|
||||||
let ast;
|
|
||||||
try {
|
|
||||||
ast = parse(code, {
|
|
||||||
ecmaVersion: 11,
|
|
||||||
sourceType: 'module',
|
|
||||||
});
|
|
||||||
} catch (err) {
|
|
||||||
return [`acorn parse error: ${err.name}: ${err.messsage}`];
|
|
||||||
}
|
|
||||||
|
|
||||||
// find all names exported in the file
|
|
||||||
return ast.body
|
|
||||||
.filter((node) => node?.type === 'ExportNamedDeclaration')
|
|
||||||
.map((node) => {
|
|
||||||
const { declaration } = node;
|
|
||||||
if (!declaration) {
|
|
||||||
// e.g. "export { Fraction, controls }"
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
switch (declaration.type) {
|
|
||||||
case 'VariableDeclaration':
|
|
||||||
return declaration.declarations
|
|
||||||
.map((d) => {
|
|
||||||
switch (d.id.type) {
|
|
||||||
case 'Identifier':
|
|
||||||
return d.id.name;
|
|
||||||
case 'ObjectPattern':
|
|
||||||
return d.id.properties.map((p) => p.value.name);
|
|
||||||
default:
|
|
||||||
return 'unknown declaration: ' + declaration;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.flat();
|
|
||||||
default:
|
|
||||||
// FunctionDeclaration, ClassDeclaration
|
|
||||||
return declaration.id.name;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.flat();
|
|
||||||
}
|
|
||||||
|
|
||||||
function isDocumented(name, docs) {
|
|
||||||
return docs.find(
|
|
||||||
(d) => d.name === name || d.tags?.find((t) => t.title === 'synonyms' && t.value.split(', ').includes(name)),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getUndocumented(path, docs) {
|
|
||||||
try {
|
|
||||||
// load the code of pattern.mjs as a string
|
|
||||||
const code = await readFile(path, 'utf8');
|
|
||||||
return getExports(code).filter((name) => !isDocumented(name, docs));
|
|
||||||
} catch (err) {
|
|
||||||
return [`parse error: ${err.name}: ${err.message}`];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// read doc.json file
|
|
||||||
const { docs } = JSON.parse(await readFile(resolve(__dirname, 'doc.json'), 'utf8'));
|
|
||||||
|
|
||||||
const paths = dependencyTree.toList({
|
|
||||||
filename: 'index.mjs',
|
|
||||||
// filename: 'packages/core/index.mjs',
|
|
||||||
directory: resolve(__dirname),
|
|
||||||
filter: (path) => !path.includes('node_modules'),
|
|
||||||
});
|
|
||||||
// const paths = ['../packages/core/pattern.mjs', '../packages/core/hap.mjs'].map((rel) => resolve(__dirname, rel));
|
|
||||||
|
|
||||||
const undocumented = Object.fromEntries(
|
|
||||||
await Promise.all(paths.map(async (path) => [path, await getUndocumented(path, docs)])),
|
|
||||||
);
|
|
||||||
|
|
||||||
console.log(JSON.stringify(undocumented, null, 2));
|
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
import { AudioContext } from 'node-web-audio-api';
|
||||||
|
import { evaluate } from '@strudel.cycles/transpiler';
|
||||||
|
import { renderHap } from '@strudel.cycles/webaudio/render.mjs';
|
||||||
|
import { evalScope, controls } from '@strudel.cycles/core';
|
||||||
|
import { zeldasRescue as tune } from './src/repl/tunes.mjs';
|
||||||
|
|
||||||
|
// tunes with cracks and pops: meltingsubmarine, hyperpop, juxUndTollerei (once)
|
||||||
|
// tunes not working: loungeSponge (csound), undergroundPlumber (draw)
|
||||||
|
|
||||||
|
await evalScope(
|
||||||
|
controls, //
|
||||||
|
import('@strudel.cycles/core'), //
|
||||||
|
import('@strudel.cycles/mini'), //
|
||||||
|
import('@strudel.cycles/tonal'), //
|
||||||
|
// import('@strudel.cycles/webaudio'), // currently fails in node: uses document
|
||||||
|
{
|
||||||
|
samples: () => {
|
||||||
|
console.log('samples() skipped');
|
||||||
|
},
|
||||||
|
loadCsound: () => {
|
||||||
|
console.log('csound output is not available in node yet..');
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const audioContext = new AudioContext();
|
||||||
|
const latency = 0.2;
|
||||||
|
const seconds = 20;
|
||||||
|
|
||||||
|
const { pattern } = await evaluate(tune);
|
||||||
|
pattern
|
||||||
|
.queryArc(0, seconds)
|
||||||
|
.filter((hap) => hap.hasOnset() && hap.whole.begin >= 0)
|
||||||
|
.map((hap) => {
|
||||||
|
renderHap(audioContext, hap, hap.whole.begin + latency);
|
||||||
|
});
|
||||||
Generated
+90
-1
@@ -33,7 +33,9 @@
|
|||||||
"tailwindcss": "^3.2.4"
|
"tailwindcss": "^3.2.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"html-escaper": "^3.0.3"
|
"audiobuffer-to-wav": "^1.0.0",
|
||||||
|
"html-escaper": "^3.0.3",
|
||||||
|
"node-web-audio-api": "^0.5.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@algolia/autocomplete-core": {
|
"node_modules/@algolia/autocomplete-core": {
|
||||||
@@ -910,6 +912,37 @@
|
|||||||
"rollup": ">=2"
|
"rollup": ">=2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@napi-rs/cli": {
|
||||||
|
"version": "2.14.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@napi-rs/cli/-/cli-2.14.2.tgz",
|
||||||
|
"integrity": "sha512-g2bGIhu+p/+wZJlNYGYbb2feQs7vF0dKIMJY8jOBdhU4r14TqxBPEr7P/xoFzHiJ7vCNdJ0rldcC+nOSnJwCTw==",
|
||||||
|
"dev": true,
|
||||||
|
"bin": {
|
||||||
|
"napi": "scripts/index.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/Brooooooklyn"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@napi-rs/triples": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@napi-rs/triples/-/triples-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-XQr74QaLeMiqhStEhLn1im9EOMnkypp7MZOwQhGzqp2Weu5eQJbpPxWxixxlYRKWPOmJjsk6qYfYH9kq43yc2w==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/@node-rs/helper": {
|
||||||
|
"version": "1.3.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@node-rs/helper/-/helper-1.3.3.tgz",
|
||||||
|
"integrity": "sha512-p4OdfQObGN9YFy5WZaGwlPYICQSe7xZYyXB0sxREmvj1HzGKp5bPg2PlfgfMZEfnjIA882B9ZrnagYzZihIwjA==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@napi-rs/triples": "^1.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@nodelib/fs.scandir": {
|
"node_modules/@nodelib/fs.scandir": {
|
||||||
"version": "2.1.5",
|
"version": "2.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
||||||
@@ -1594,6 +1627,12 @@
|
|||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/audiobuffer-to-wav": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/audiobuffer-to-wav/-/audiobuffer-to-wav-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-CAoir4NRrAzAgYo20tEMiKZR84coE8bq/L+H2kwAaULVY4+0xySsEVtNT5raqpzmH6y0pqzY6EmoViLd9W8F/w==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/autoprefixer": {
|
"node_modules/autoprefixer": {
|
||||||
"version": "10.4.13",
|
"version": "10.4.13",
|
||||||
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.13.tgz",
|
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.13.tgz",
|
||||||
@@ -5167,6 +5206,19 @@
|
|||||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.8.tgz",
|
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.8.tgz",
|
||||||
"integrity": "sha512-dFSmB8fFHEH/s81Xi+Y/15DQY6VHW81nXRj86EMSL3lmuTmK1e+aT4wrFCkTbm+gSwkw4KpX+rT/pMM2c1mF+A=="
|
"integrity": "sha512-dFSmB8fFHEH/s81Xi+Y/15DQY6VHW81nXRj86EMSL3lmuTmK1e+aT4wrFCkTbm+gSwkw4KpX+rT/pMM2c1mF+A=="
|
||||||
},
|
},
|
||||||
|
"node_modules/node-web-audio-api": {
|
||||||
|
"version": "0.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/node-web-audio-api/-/node-web-audio-api-0.5.0.tgz",
|
||||||
|
"integrity": "sha512-HMFQHti2RnhgAzIrK6PSpDqgwKHMdeWsVILBwEQcCWzol9nGDK+QQQCJPWpMTfvTuGsfEletzRqz+p6fhcjDuA==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@napi-rs/cli": "^2.10.0",
|
||||||
|
"@node-rs/helper": "^1.3.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 14"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/normalize-path": {
|
"node_modules/normalize-path": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
|
||||||
@@ -8171,6 +8223,27 @@
|
|||||||
"vfile": "^5.0.0"
|
"vfile": "^5.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@napi-rs/cli": {
|
||||||
|
"version": "2.14.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@napi-rs/cli/-/cli-2.14.2.tgz",
|
||||||
|
"integrity": "sha512-g2bGIhu+p/+wZJlNYGYbb2feQs7vF0dKIMJY8jOBdhU4r14TqxBPEr7P/xoFzHiJ7vCNdJ0rldcC+nOSnJwCTw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"@napi-rs/triples": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@napi-rs/triples/-/triples-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-XQr74QaLeMiqhStEhLn1im9EOMnkypp7MZOwQhGzqp2Weu5eQJbpPxWxixxlYRKWPOmJjsk6qYfYH9kq43yc2w==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"@node-rs/helper": {
|
||||||
|
"version": "1.3.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@node-rs/helper/-/helper-1.3.3.tgz",
|
||||||
|
"integrity": "sha512-p4OdfQObGN9YFy5WZaGwlPYICQSe7xZYyXB0sxREmvj1HzGKp5bPg2PlfgfMZEfnjIA882B9ZrnagYzZihIwjA==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@napi-rs/triples": "^1.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@nodelib/fs.scandir": {
|
"@nodelib/fs.scandir": {
|
||||||
"version": "2.1.5",
|
"version": "2.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
||||||
@@ -8760,6 +8833,12 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"audiobuffer-to-wav": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/audiobuffer-to-wav/-/audiobuffer-to-wav-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-CAoir4NRrAzAgYo20tEMiKZR84coE8bq/L+H2kwAaULVY4+0xySsEVtNT5raqpzmH6y0pqzY6EmoViLd9W8F/w==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"autoprefixer": {
|
"autoprefixer": {
|
||||||
"version": "10.4.13",
|
"version": "10.4.13",
|
||||||
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.13.tgz",
|
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.13.tgz",
|
||||||
@@ -11056,6 +11135,16 @@
|
|||||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.8.tgz",
|
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.8.tgz",
|
||||||
"integrity": "sha512-dFSmB8fFHEH/s81Xi+Y/15DQY6VHW81nXRj86EMSL3lmuTmK1e+aT4wrFCkTbm+gSwkw4KpX+rT/pMM2c1mF+A=="
|
"integrity": "sha512-dFSmB8fFHEH/s81Xi+Y/15DQY6VHW81nXRj86EMSL3lmuTmK1e+aT4wrFCkTbm+gSwkw4KpX+rT/pMM2c1mF+A=="
|
||||||
},
|
},
|
||||||
|
"node-web-audio-api": {
|
||||||
|
"version": "0.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/node-web-audio-api/-/node-web-audio-api-0.5.0.tgz",
|
||||||
|
"integrity": "sha512-HMFQHti2RnhgAzIrK6PSpDqgwKHMdeWsVILBwEQcCWzol9nGDK+QQQCJPWpMTfvTuGsfEletzRqz+p6fhcjDuA==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@napi-rs/cli": "^2.10.0",
|
||||||
|
"@node-rs/helper": "^1.3.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
"normalize-path": {
|
"normalize-path": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
|
||||||
|
|||||||
@@ -37,6 +37,8 @@
|
|||||||
"tailwindcss": "^3.2.4"
|
"tailwindcss": "^3.2.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"html-escaper": "^3.0.3"
|
"audiobuffer-to-wav": "^1.0.0",
|
||||||
|
"html-escaper": "^3.0.3",
|
||||||
|
"node-web-audio-api": "^0.5.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ const { headings, githubEditUrl } = Astro.props as Props;
|
|||||||
const currentPage = Astro.url.pathname;
|
const currentPage = Astro.url.pathname;
|
||||||
---
|
---
|
||||||
|
|
||||||
<nav aria-labelledby="grid-right" class="w-64">
|
<nav aria-labelledby="grid-right">
|
||||||
<TableOfContents client:media="(min-width: 50em)" headings={headings} currentPage={currentPage} />
|
<TableOfContents client:media="(min-width: 50em)" headings={headings} currentPage={currentPage} />
|
||||||
<MoreMenu editHref={githubEditUrl} />
|
<MoreMenu editHref={githubEditUrl} />
|
||||||
</nav>
|
</nav>
|
||||||
|
|||||||
@@ -62,7 +62,6 @@ export const SIDEBAR: Sidebar = {
|
|||||||
{ text: 'Control Parameters', link: 'functions/value-modifiers' },
|
{ text: 'Control Parameters', link: 'functions/value-modifiers' },
|
||||||
{ text: 'Signals', link: 'learn/signals' },
|
{ text: 'Signals', link: 'learn/signals' },
|
||||||
{ text: 'Conditional Modifiers', link: 'learn/conditional-modifiers' },
|
{ text: 'Conditional Modifiers', link: 'learn/conditional-modifiers' },
|
||||||
{ text: 'Accumulation', link: 'learn/accumulation' },
|
|
||||||
{ text: 'Tonal Modifiers', link: 'learn/tonal' },
|
{ text: 'Tonal Modifiers', link: 'learn/tonal' },
|
||||||
],
|
],
|
||||||
More: [
|
More: [
|
||||||
@@ -75,7 +74,6 @@ export const SIDEBAR: Sidebar = {
|
|||||||
{ text: 'REPL', link: 'technical-manual/repl' },
|
{ text: 'REPL', link: 'technical-manual/repl' },
|
||||||
{ text: 'Docs', link: 'technical-manual/docs' },
|
{ text: 'Docs', link: 'technical-manual/docs' },
|
||||||
{ text: 'Testing', link: 'technical-manual/testing' },
|
{ text: 'Testing', link: 'technical-manual/testing' },
|
||||||
// { text: 'Internals', link: 'technical-manual/internals' },
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,15 +2,12 @@ import jsdoc from '../../../doc.json'; // doc.json is built with `npm run jsdoc-
|
|||||||
const docs = jsdoc.docs.reduce((acc, obj) => Object.assign(acc, { [obj.longname]: obj }), {});
|
const docs = jsdoc.docs.reduce((acc, obj) => Object.assign(acc, { [obj.longname]: obj }), {});
|
||||||
import { MiniRepl } from './MiniRepl';
|
import { MiniRepl } from './MiniRepl';
|
||||||
|
|
||||||
const getTag = (title, item) => item.tags?.find((t) => t.title === title)?.text;
|
|
||||||
|
|
||||||
export function JsDoc({ name, h = 3, hideDescription }) {
|
export function JsDoc({ name, h = 3, hideDescription }) {
|
||||||
const item = docs[name];
|
const item = docs[name];
|
||||||
if (!item) {
|
if (!item) {
|
||||||
console.warn('Not found: ' + name);
|
console.warn('Not found: ' + name);
|
||||||
return <div />;
|
return <div />;
|
||||||
}
|
}
|
||||||
const synonyms = getTag('synonyms', item)?.split(', ') || [];
|
|
||||||
const CustomHeading = `h${h}`;
|
const CustomHeading = `h${h}`;
|
||||||
const description = item.description.replaceAll(/\{@link ([a-zA-Z\.]+)?#?([a-zA-Z]*)\}/g, (_, a, b) => {
|
const description = item.description.replaceAll(/\{@link ([a-zA-Z\.]+)?#?([a-zA-Z]*)\}/g, (_, a, b) => {
|
||||||
// console.log(_, 'a', a, 'b', b);
|
// console.log(_, 'a', a, 'b', b);
|
||||||
@@ -19,16 +16,7 @@ export function JsDoc({ name, h = 3, hideDescription }) {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{!!h && <CustomHeading>{item.longname}</CustomHeading>}
|
{!!h && <CustomHeading>{item.longname}</CustomHeading>}
|
||||||
{!hideDescription && (
|
{!hideDescription && <div dangerouslySetInnerHTML={{ __html: description }} />}
|
||||||
<>
|
|
||||||
{!!synonyms.length && (
|
|
||||||
<span>
|
|
||||||
Synonyms: <code>{synonyms.join(', ')}</code>
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
<div dangerouslySetInnerHTML={{ __html: description }} />
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
<ul>
|
<ul>
|
||||||
{item.params?.map((param, i) => (
|
{item.params?.map((param, i) => (
|
||||||
<li key={i}>
|
<li key={i}>
|
||||||
|
|||||||
@@ -3,9 +3,8 @@ import { initAudioOnFirstClick } from '@strudel.cycles/webaudio';
|
|||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { prebake } from '../repl/prebake';
|
import { prebake } from '../repl/prebake';
|
||||||
|
|
||||||
let modules;
|
|
||||||
if (typeof window !== 'undefined') {
|
if (typeof window !== 'undefined') {
|
||||||
modules = evalScope(
|
evalScope(
|
||||||
controls,
|
controls,
|
||||||
import('@strudel.cycles/core'),
|
import('@strudel.cycles/core'),
|
||||||
// import('@strudel.cycles/tone'),
|
// import('@strudel.cycles/tone'),
|
||||||
@@ -23,20 +22,14 @@ if (typeof window !== 'undefined') {
|
|||||||
prebake();
|
prebake();
|
||||||
}
|
}
|
||||||
|
|
||||||
export function MiniRepl({ tune, drawTime, punchcard, canvasHeight = 100 }) {
|
export function MiniRepl({ tune, withCanvas }) {
|
||||||
const [Repl, setRepl] = useState();
|
const [Repl, setRepl] = useState();
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// we have to load this package on the client
|
// we have to load this package on the client
|
||||||
// because codemirror throws an error on the server
|
// because codemirror throws an error on the server
|
||||||
Promise.all([import('@strudel.cycles/react'), modules])
|
import('@strudel.cycles/react').then((res) => {
|
||||||
.then(([res]) => setRepl(() => res.MiniRepl))
|
setRepl(() => res.MiniRepl);
|
||||||
.catch((err) => console.error(err));
|
});
|
||||||
}, []);
|
}, []);
|
||||||
return Repl ? (
|
return Repl ? <Repl tune={tune} hideOutsideView={true} withCanvas={withCanvas} /> : <pre>{tune}</pre>;
|
||||||
<div className="mb-4">
|
|
||||||
<Repl tune={tune} hideOutsideView={true} drawTime={drawTime} punchcard={punchcard} canvasHeight={canvasHeight} />
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<pre>{tune}</pre>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
import { OfflineAudioContext } from 'node-web-audio-api';
|
||||||
|
import toWav from 'audiobuffer-to-wav';
|
||||||
|
import { evaluate } from '@strudel.cycles/transpiler';
|
||||||
|
import { renderHap } from '@strudel.cycles/webaudio/render.mjs';
|
||||||
|
import { evalScope, controls } from '@strudel.cycles/core';
|
||||||
|
import * as tunes from '../../repl/tunes.mjs';
|
||||||
|
|
||||||
|
await evalScope(
|
||||||
|
controls, //
|
||||||
|
import('@strudel.cycles/core'), //
|
||||||
|
import('@strudel.cycles/mini'), //
|
||||||
|
import('@strudel.cycles/tonal'), //
|
||||||
|
// import('@strudel.cycles/webaudio'), // need document..
|
||||||
|
{
|
||||||
|
samples: () => {
|
||||||
|
console.log('samples() skipped');
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
export async function get({ params, request }) {
|
||||||
|
const { name } = params;
|
||||||
|
const tune = tunes[name];
|
||||||
|
const channels = 1;
|
||||||
|
const seconds = 20;
|
||||||
|
const sampleRate = 44100;
|
||||||
|
const offline = new OfflineAudioContext(channels, sampleRate * seconds, sampleRate);
|
||||||
|
|
||||||
|
// const { pattern } = await evaluate(`"<c a f e>(3,8)".off(1/8, add(7)).note().s('sawtooth').lpf(2000)`);
|
||||||
|
const { pattern } = await evaluate(tune);
|
||||||
|
pattern
|
||||||
|
.queryArc(0, seconds)
|
||||||
|
.filter((hap) => hap.hasOnset() && hap.whole.begin >= 0)
|
||||||
|
.map((hap) => {
|
||||||
|
renderHap(offline, hap, hap.whole.begin);
|
||||||
|
});
|
||||||
|
|
||||||
|
const buffer = await offline.startRendering();
|
||||||
|
const wav = toWav(buffer);
|
||||||
|
return {
|
||||||
|
body: wav,
|
||||||
|
encoding: 'binary',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getStaticPaths() {
|
||||||
|
return Object.keys(tunes).map((name) => ({
|
||||||
|
params: {
|
||||||
|
name,
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
}
|
||||||
@@ -131,14 +131,6 @@ This group of functions allows to modify the value of events.
|
|||||||
|
|
||||||
<JsDoc client:idle name="Pattern.range" h={0} />
|
<JsDoc client:idle name="Pattern.range" h={0} />
|
||||||
|
|
||||||
## rangex
|
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern.rangex" h={0} />
|
|
||||||
|
|
||||||
## range2
|
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern.range2" h={0} />
|
|
||||||
|
|
||||||
# Custom Parameters
|
# Custom Parameters
|
||||||
|
|
||||||
You can also create your own parameters:
|
You can also create your own parameters:
|
||||||
|
|||||||
@@ -1,34 +0,0 @@
|
|||||||
---
|
|
||||||
title: Accumulation Modifiers
|
|
||||||
description: Strudel Tutorial - Coding syntax
|
|
||||||
layout: ../../layouts/MainLayout.astro
|
|
||||||
---
|
|
||||||
|
|
||||||
import { MiniRepl } from '../../docs/MiniRepl';
|
|
||||||
import { JsDoc } from '../../docs/JsDoc';
|
|
||||||
|
|
||||||
# Accumulation Modifiers
|
|
||||||
|
|
||||||
## superimpose
|
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern.superimpose" h={0} />
|
|
||||||
|
|
||||||
## layer
|
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern.layer" h={0} />
|
|
||||||
|
|
||||||
## off
|
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern.off" h={0} />
|
|
||||||
|
|
||||||
## echo
|
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern.echo" h={0} />
|
|
||||||
|
|
||||||
## echoWith
|
|
||||||
|
|
||||||
<JsDoc client:idle name="echoWith" h={0} />
|
|
||||||
|
|
||||||
## stut
|
|
||||||
|
|
||||||
<JsDoc client:idle name="stut" h={0} />
|
|
||||||
@@ -73,8 +73,7 @@ You can also use the keyboard shortcut `cmd-/` to toggle comments on and off.
|
|||||||
Ok, so what about the content inside the quotes (e.g. `"a3 c#4 e4 a4"`)?
|
Ok, so what about the content inside the quotes (e.g. `"a3 c#4 e4 a4"`)?
|
||||||
In JavaScript, as in most programming languages, this content is referred to as being a [_string_](<https://en.wikipedia.org/wiki/String_(computer_science)>).
|
In JavaScript, as in most programming languages, this content is referred to as being a [_string_](<https://en.wikipedia.org/wiki/String_(computer_science)>).
|
||||||
A string is simply a sequence of individual characters.
|
A string is simply a sequence of individual characters.
|
||||||
In TidalCycles, double quoted strings are used to write _patterns_ using the mini-notation, and you may hear the phrase _pattern string_ from time to time.
|
In TidalCycles, strings are used to write _patterns_ using the mini-notation, and you may hear the phrase _pattern string_ from time to time.
|
||||||
If you want to create a regular string and not a pattern, you can use single quotes, e.g. `'C minor'` will not be parsed as Mini Notation.
|
|
||||||
|
|
||||||
The good news is, that this covers 99% of the JavaScript syntax needed for Strudel!
|
The good news is, that this covers 99% of the JavaScript syntax needed for Strudel!
|
||||||
|
|
||||||
|
|||||||
@@ -8,54 +8,36 @@ import { JsDoc } from '../../docs/JsDoc';
|
|||||||
|
|
||||||
# Conditional Modifiers
|
# Conditional Modifiers
|
||||||
|
|
||||||
## lastOf
|
## every
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern.lastOf" h={0} />
|
<JsDoc client:idle name="Pattern.every" h={0} />
|
||||||
|
|
||||||
## firstOf
|
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern.firstOf" h={0} />
|
|
||||||
|
|
||||||
## when
|
## when
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern.when" h={0} />
|
<JsDoc client:idle name="Pattern.when" h={0} />
|
||||||
|
|
||||||
## chunk
|
# Accumulation Modifiers
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern.chunk" h={0} />
|
## stack
|
||||||
|
|
||||||
### chunkBack
|
<JsDoc client:idle name="Pattern.stack" h={0} />
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern.chunkBack" h={0} />
|
## superimpose
|
||||||
|
|
||||||
## arp
|
<JsDoc client:idle name="Pattern.superimpose" h={0} />
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern#arp" h={0} />
|
## layer
|
||||||
|
|
||||||
## arpWith 🧪
|
<JsDoc client:idle name="Pattern.layer" h={0} />
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern#arpWith" h={0} />
|
## off
|
||||||
|
|
||||||
## struct
|
<JsDoc client:idle name="Pattern.off" h={0} />
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern#struct" h={0} />
|
## echo
|
||||||
|
|
||||||
## mask
|
<JsDoc client:idle name="Pattern.echo" h={0} />
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern#mask" h={0} />
|
## echoWith
|
||||||
|
|
||||||
## reset
|
<JsDoc client:idle name="Pattern.echoWith" h={0} />
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern#reset" h={0} />
|
|
||||||
|
|
||||||
## restart
|
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern#restart" h={0} />
|
|
||||||
|
|
||||||
## hush
|
|
||||||
|
|
||||||
<JsDoc client:idle name="hush" h={0} />
|
|
||||||
|
|
||||||
## invert
|
|
||||||
|
|
||||||
<JsDoc client:idle name="invert" h={0} />
|
|
||||||
|
|||||||
@@ -64,14 +64,6 @@ As you might suspect, the effects can be chained together, and they accept a pat
|
|||||||
|
|
||||||
<JsDoc client:idle name="vowel" h={0} />
|
<JsDoc client:idle name="vowel" h={0} />
|
||||||
|
|
||||||
## jux
|
|
||||||
|
|
||||||
<JsDoc client:idle name="jux" h={0} />
|
|
||||||
|
|
||||||
## juxBy
|
|
||||||
|
|
||||||
<JsDoc client:idle name="juxBy" h={0} />
|
|
||||||
|
|
||||||
# Global Effects
|
# Global Effects
|
||||||
|
|
||||||
## Local vs Global Effects
|
## Local vs Global Effects
|
||||||
@@ -99,6 +91,6 @@ global effects use the same chain for all events of the same orbit:
|
|||||||
|
|
||||||
<JsDoc client:idle name="room" h={0} />
|
<JsDoc client:idle name="room" h={0} />
|
||||||
|
|
||||||
## roomsize
|
## size / roomsize
|
||||||
|
|
||||||
<JsDoc client:idle name="roomsize" h={0} />
|
<JsDoc client:idle name="size" h={0} />
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ These are the equivalents used by the Mini Notation:
|
|||||||
| `cat(x, y)` | `"<x y>"` |
|
| `cat(x, y)` | `"<x y>"` |
|
||||||
| `seq(x, y)` | `"x y"` |
|
| `seq(x, y)` | `"x y"` |
|
||||||
| `stack(x, y)` | `"x,y"` |
|
| `stack(x, y)` | `"x,y"` |
|
||||||
| `timeCat([3,x],[2,y])` | `"x@3 y@2"` |
|
| `timeCat([3,x],[2,y])` | `"x@2 y@2"` |
|
||||||
| `polymeter([a, b, c], [x, y])` | `"{a b c, x y}"` |
|
| `polymeter([a, b, c], [x, y])` | `"{a b c, x y}"` |
|
||||||
| `polymeterSteps(2, x, y, z)` | `"{x y z}%2"` |
|
| `polymeterSteps(2, x, y, z)` | `"{x y z}%2"` |
|
||||||
| `silence` | `"~"` |
|
| `silence` | `"~"` |
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ Strudel however runs directly in your web browser, does not require any custom s
|
|||||||
The main place to actually make music with Strudel is the [Strudel REPL](https://strudel.tidalcycles.org/) ([what is a REPL?](https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop)), but in these pages you will also encounter interactive "MiniREPLs" where you can listen to and edit Strudel patterns.
|
The main place to actually make music with Strudel is the [Strudel REPL](https://strudel.tidalcycles.org/) ([what is a REPL?](https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop)), but in these pages you will also encounter interactive "MiniREPLs" where you can listen to and edit Strudel patterns.
|
||||||
Try clicking the play icon below:
|
Try clicking the play icon below:
|
||||||
|
|
||||||
<MiniRepl client:idle tune={`s("bd sd")`} punchcard />
|
<MiniRepl client:idle tune={`s("bd sd")`} />
|
||||||
|
|
||||||
Then edit the text so it reads `s("bd sd cp hh")` and click the refresh icon.
|
Then edit the text so it reads `s("bd sd cp hh")` and click the refresh icon.
|
||||||
Congratulations, you have now live coded your first Strudel pattern!
|
Congratulations, you have now live coded your first Strudel pattern!
|
||||||
@@ -54,7 +54,7 @@ Alternatively, you can get a taste of what Strudel can do by clicking play on th
|
|||||||
hh: ['hh27/000_hh27closedhh.wav','hh/000_hh3closedhh.wav'],
|
hh: ['hh27/000_hh27closedhh.wav','hh/000_hh3closedhh.wav'],
|
||||||
}, 'github:tidalcycles/Dirt-Samples/master/');
|
}, 'github:tidalcycles/Dirt-Samples/master/');
|
||||||
stack(
|
stack(
|
||||||
s("bd,[~ <sd!3 sd(3,4,2)>],hh*8") // drums
|
s("bd,[~ <sd!3 sd(3,4,2)>],hh(3,4)") // drums
|
||||||
.speed(perlin.range(.7,.9)) // random sample speed variation
|
.speed(perlin.range(.7,.9)) // random sample speed variation
|
||||||
,"<a1 b1\*2 a1(3,8) e2>" // bassline
|
,"<a1 b1\*2 a1(3,8) e2>" // bassline
|
||||||
.off(1/8,x=>x.add(12).degradeBy(.5)) // random octave jumps
|
.off(1/8,x=>x.add(12).degradeBy(.5)) // random octave jumps
|
||||||
|
|||||||
@@ -39,24 +39,20 @@ Before diving deeper into the details, here is a flavour of how the mini-notatio
|
|||||||
]/16\`)`}
|
]/16\`)`}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
## Mini Notation Format
|
Note that the snippet above is enclosed in backticks (`), which allows you to write multi-line strings.
|
||||||
|
|
||||||
The snippet above is enclosed in backticks (`), which allows you to write multi-line strings.
|
|
||||||
|
|
||||||
You can also use regular double quotes (`"`) for single line mini-notation, as we have done already.
|
You can also use regular double quotes (`"`) for single line mini-notation, as we have done already.
|
||||||
|
|
||||||
If you do just want to get a regular string that is _not_ parsed as mini-notation, use single quotes (`'`).
|
# Sequences of events in a cycle
|
||||||
|
|
||||||
## Sequences of events in a cycle
|
|
||||||
|
|
||||||
We can play more notes by separating them with spaces:
|
We can play more notes by separating them with spaces:
|
||||||
|
|
||||||
<MiniRepl client:idle tune={`note("c e g b")`} punchcard />
|
<MiniRepl client:idle tune={`note("e5 b4 d5 c5")`} withCanvas />
|
||||||
|
|
||||||
Here, those four notes are squashed into one cycle, so each note is a quarter second long.
|
Here, those four notes are squashed into one cycle, so each note is a quarter second long.
|
||||||
Try adding or removing notes and notice how the tempo changes!
|
Try adding or removing notes and notice how the tempo changes!
|
||||||
|
|
||||||
<MiniRepl client:idle tune={`note("c d e f g a b")`} punchcard />
|
<MiniRepl client:idle tune={`note("e5 b4 d5 c5 e5 b4 d5 c5")`} withCanvas />
|
||||||
|
|
||||||
Note that the overall duration of time does not change, and instead each note length descreases.
|
Note that the overall duration of time does not change, and instead each note length descreases.
|
||||||
This is a key idea, as it illustrates the 'Cycle' in TidalCycles!
|
This is a key idea, as it illustrates the 'Cycle' in TidalCycles!
|
||||||
@@ -68,48 +64,50 @@ Taking the two examples above, we have four and eight events respectively, and s
|
|||||||
This is perhaps counter-intuitive if you are used to adding notes in a sequencer or piano roll and the overall length increasing.
|
This is perhaps counter-intuitive if you are used to adding notes in a sequencer or piano roll and the overall length increasing.
|
||||||
But, it will begin to make sense as we go through more elements of mini-notation.
|
But, it will begin to make sense as we go through more elements of mini-notation.
|
||||||
|
|
||||||
## Division
|
# Division
|
||||||
|
|
||||||
We can slow the sequence down by enclosing it in brackets and dividing it by a number (`/2`):
|
We can slow the sequence down by enclosing it in brackets and dividing it by a number (`/2`):
|
||||||
|
|
||||||
<MiniRepl client:idle tune={`note("[e5 b4 d5 c5]/2")`} punchcard />
|
<MiniRepl client:idle tune={`note("[e5 b4 d5 c5]/2")`} />
|
||||||
|
|
||||||
The division by two means that the sequence will be played over the course of two cycles.
|
The division by two means that the sequence will be played over the course of two cycles.
|
||||||
You can also use decimal numbers for any tempo you like (`/2.75`).
|
You can also use decimal numbers for any tempo you like (`/2.75`).
|
||||||
|
|
||||||
<MiniRepl client:idle tune={`note("[e5 b4 d5 c5]/2.75")`} punchcard />
|
<MiniRepl client:idle tune={`note("[e5 b4 d5 c5]/2.75")`} />
|
||||||
|
|
||||||
## Angle Brackets
|
# Angle Brackets
|
||||||
|
|
||||||
Using angle brackets `<>`, we can define the sequence length based on the number of events:
|
Using angle brackets `<>`, we can define the sequence length based on the number of events:
|
||||||
|
|
||||||
<MiniRepl client:idle tune={`note("<e5 b4 d5 c5>")`} punchcard />
|
<MiniRepl client:idle tune={`note("<e5 b4 d5 c5>")`} />
|
||||||
|
|
||||||
The above snippet is the same as:
|
The above snippet is the same as:
|
||||||
|
|
||||||
<MiniRepl client:idle tune={`note("[e5 b4 d5 c5]/4")`} punchcard />
|
<MiniRepl client:idle tune={`note("[e5 b4 d5 c5]/4")`} />
|
||||||
|
|
||||||
The advantage of the angle brackets, is that we can add more events without needing to change the number at the end.
|
The advantage of the angle brackets, is that we can add more events without needing to change the number at the end.
|
||||||
|
|
||||||
<MiniRepl client:idle tune={`note("<e5 b4 d5 c5 e5>")`} punchcard />
|
<MiniRepl client:idle tune={`note("<e5 b4 d5 c5 e5>")`} />
|
||||||
|
|
||||||
<MiniRepl client:idle tune={`note("<e5 b4 d5 c5 e5 b4>")`} punchcard />
|
<MiniRepl client:idle tune={`note("<e5 b4 d5 c5 e5 b4>")`} />
|
||||||
|
|
||||||
This is more similar to traditional music sequencers and piano rolls, where adding a note increases the perceived overall duration.
|
This is more similar to traditional music sequencers and piano rolls, where adding a note increases the perceived overall duration.
|
||||||
|
|
||||||
## Multiplication
|
# Multiplication
|
||||||
|
|
||||||
Contrary to division, a sequence can be sped up by multiplying it by a number using the asterisk symbol (`*`):
|
Contrary to division, a sequence can be sped up by multiplying it by a number using the asterisk symbol (`*`):
|
||||||
|
|
||||||
<MiniRepl client:idle tune={`note("[e5 b4 d5 c5]*2")`} punchcard />
|
<MiniRepl client:idle tune={`note("[e5 b4 d5 c5]*2")`} />
|
||||||
|
|
||||||
The multiplication by two here means that the sequence will play twice a cycle.
|
The multiplication by two here means that the sequence will play twice a cycle.
|
||||||
|
|
||||||
As with divisions, multiplications can be decimal (`*2.75`):
|
As with divisions, multiplications can be decimal (`*2.75`):
|
||||||
|
|
||||||
<MiniRepl client:idle tune={`note("[e5 b4 d5 c5]*2.75")`} punchcard />
|
<MiniRepl client:idle tune={`note("[e5 b4 d5 c5]*2.75")`} />
|
||||||
|
|
||||||
## Subdividing time with bracket nesting
|
Actually, this is not true, but this will be [fixed](https://github.com/tidalcycles/strudel/issues/314) :)
|
||||||
|
|
||||||
|
# Subdividing time with bracket nesting
|
||||||
|
|
||||||
To create more interesting rhythms, you can _nest_ or _enclose_ sequences (put sequences inside sequences) with brackets `[]`, like this:
|
To create more interesting rhythms, you can _nest_ or _enclose_ sequences (put sequences inside sequences) with brackets `[]`, like this:
|
||||||
|
|
||||||
@@ -127,41 +125,41 @@ This is a very simple change to make, but it has profound consequences.
|
|||||||
Remember what we said earlier about how the cycles in tidal stay the same length, and the individual event lengths are divided up in this cycle?
|
Remember what we said earlier about how the cycles in tidal stay the same length, and the individual event lengths are divided up in this cycle?
|
||||||
Well, what this means is that in TidalCycles, not only can you divide time any way you want, and you can also subdivide time any way you want!
|
Well, what this means is that in TidalCycles, not only can you divide time any way you want, and you can also subdivide time any way you want!
|
||||||
|
|
||||||
## Rests
|
# Rests
|
||||||
|
|
||||||
The "~" represents a rest, and will create silence between other events:
|
The "~" represents a rest, and will create silence between other events:
|
||||||
|
|
||||||
<MiniRepl client:idle tune={`note("[b4 [~ c5] d5 e5]")`} punchcard />
|
<MiniRepl client:idle tune={`note("[b4 [~ c5] d5 e5]")`} />
|
||||||
|
|
||||||
## Parallel / polyphony
|
# Parallel / polyphony
|
||||||
|
|
||||||
Using commas, we can play chords.
|
Using commas, we can play chords.
|
||||||
The following are the same:
|
The following are the same:
|
||||||
|
|
||||||
<MiniRepl client:idle tune={`note("[g3,b3,e4]")`} />
|
<MiniRepl client:idle tune={`note("[g3,b3,e4]")`} />
|
||||||
<MiniRepl client:idle tune={`note("g3,b3,e4")`} punchcard canvasHeight={80} />
|
<MiniRepl client:idle tune={`note("g3,b3,e4")`} />
|
||||||
|
|
||||||
But to play multiple chords in a sequence, we have to wrap them in brackets:
|
But to play multiple chords in a sequence, we have to wrap them in brackets:
|
||||||
|
|
||||||
<MiniRepl client:idle tune={`note("<[g3,b3,e4] [a3,c3,e4] [b3,d3,f#4] [b3,e4,g4]>")`} punchcard />
|
<MiniRepl client:idle tune={`note("<[g3,b3,e4] [a3,c3,e4] [b3,d3,f#4] [b3,e4,g4]>")`} />
|
||||||
|
|
||||||
## Elongation
|
# Elongation
|
||||||
|
|
||||||
With the "@" symbol, we can specify temporal "weight" of a sequence child:
|
With the "@" symbol, we can specify temporal "weight" of a sequence child:
|
||||||
|
|
||||||
<MiniRepl client:idle tune={`note("<[g3,b3,e4]@2 [a3,c3,e4] [b3,d3,f#4]>")`} punchcard />
|
<MiniRepl client:idle tune={`note("<[g3,b3,e4]@2 [a3,c3,e4] [b3,d3,f#4]>")`} />
|
||||||
|
|
||||||
Here, the first chord has a weight of 2, making it twice the length of the other chords. The default weight is 1.
|
Here, the first chord has a weight of 2, making it twice the length of the other chords. The default weight is 1.
|
||||||
|
|
||||||
## Replication
|
# Replication
|
||||||
|
|
||||||
Using "!" we can repeat without speeding up:
|
Using "!" we can repeat without speeding up:
|
||||||
|
|
||||||
<MiniRepl client:idle tune={`note("<[g3,b3,e4]!2 [a3,c3,e4] [b3,d3,f#4]>")`} punchcard />
|
<MiniRepl client:idle tune={`note("<[g3,b3,e4]!2 [a3,c3,e4] [b3,d3,f#4]>")`} />
|
||||||
|
|
||||||
In essence, the `x!n` is like a shortcut for `[x*n]@n`.
|
In essence, the `x!n` is like a shortcut for `[x*n]@n`.
|
||||||
|
|
||||||
## Mini-notation review
|
# Mini-notation review
|
||||||
|
|
||||||
To recap what we've learned so far, compare the following patterns:
|
To recap what we've learned so far, compare the following patterns:
|
||||||
|
|
||||||
@@ -173,60 +171,60 @@ To recap what we've learned so far, compare the following patterns:
|
|||||||
<MiniRepl client:idle tune={`note("<[g3,b3,e4]@2 [a3,c3,e4] [b3,d3,f#4]>")`} />
|
<MiniRepl client:idle tune={`note("<[g3,b3,e4]@2 [a3,c3,e4] [b3,d3,f#4]>")`} />
|
||||||
<MiniRepl client:idle tune={`note("<[g3,b3,e4]!2 [a3,c3,e4] [b3,d3,f#4]>")`} />
|
<MiniRepl client:idle tune={`note("<[g3,b3,e4]!2 [a3,c3,e4] [b3,d3,f#4]>")`} />
|
||||||
|
|
||||||
## Euclidian rhythms
|
# Euclidian rhythms
|
||||||
|
|
||||||
Using round brackets after an event, we can create rhythmical sub-divisions based on three parameters: `beats`, `segments` and `offset`.
|
Using round brackets after an event, we can create rhythmical sub-divisions based on three parameters: `beats`, `segments` and `offset`.
|
||||||
This algorithm can be found in many different types of music software, and is often referred to as a [Euclidean rhythm](https://en.wikipedia.org/wiki/Euclidean_rhythm) sequencer, after computer scientist Godfriend Toussaint.
|
This algorithm can be found in many different types of music software, and is often referred to as a [Euclidean rhythm](https://en.wikipedia.org/wiki/Euclidean_rhythm) sequencer, after computer scientist Godfriend Toussaint.
|
||||||
Why is it interesting? Well, consider the following simple example:
|
Why is it interesting? Well, consider the following simple example:
|
||||||
|
|
||||||
<MiniRepl client:idle tune={`s("bd(3,8,0)")`} punchcard canvasHeight={50} />
|
<MiniRepl client:idle tune={`s("bd(3,8,0)")`} />
|
||||||
|
|
||||||
Sound familiar?
|
Sound familiar?
|
||||||
This is a popular Euclidian rhythm going by various names, such as "Pop Clave".
|
This is a popular Euclidian rhythm going by various names, such as "Pop Clave".
|
||||||
These rhythms can be found in all musical cultures, and the Euclidian rhythm algorithm allows us to express them extremely easily.
|
These rhythms can be found in all musical cultures, and the Euclidian rhythm algorithm allows us to express them extremely easily.
|
||||||
Writing this rhythm out in full require describing:
|
Writing this rhythm out in full require describing:
|
||||||
|
|
||||||
<MiniRepl client:idle tune={`s("bd ~ ~ bd ~ ~ bd ~")`} punchcard canvasHeight={50} />
|
<MiniRepl client:idle tune={`s("bd ~ ~ bd ~ ~ bd ~")`} />
|
||||||
|
|
||||||
But using the Euclidian rhythm notation, we only need to express "3 beats over 8 segments, starting on position 1".
|
But using the Euclidian rhythm notation, we only need to express "3 beats over 8 segments, starting on position 1".
|
||||||
|
|
||||||
This makes it easy to write patterns with interesting rhythmic structures and variations that still sound familiar:
|
This makes it easy to write patterns with interesting rhythmic structures and variations that still sound familiar:
|
||||||
|
|
||||||
<MiniRepl client:idle tune={`note("e5(2,8) b4(3,8) d5(2,8) c5(3,8)").slow(4)`} punchcard canvasHeight={50} />
|
<MiniRepl client:idle tune={`note("e5(2,8) b4(3,8) d5(2,8) c5(3,8)").slow(4)`} />
|
||||||
|
|
||||||
Note that since the example above does not use the third `offset` parameter, it can be written simply as `"(3,8)"`.
|
Note that since the example above does not use the third `offset` parameter, it can be written simply as `"(3,8)"`.
|
||||||
|
|
||||||
<MiniRepl client:idle tune={`s("bd(3,8)")`} punchcard canvasHeight={50} />
|
<MiniRepl client:idle tune={`s("bd(3,8)")`} />
|
||||||
|
|
||||||
Let's look at those three parameters in detail.
|
Let's look at those three parameters in detail.
|
||||||
|
|
||||||
### Beats
|
## Beats
|
||||||
|
|
||||||
`beats`: the first parameter controls how may beats will be played.
|
`beats`: the first parameter controls how may beats will be played.
|
||||||
Compare these:
|
Compare these:
|
||||||
|
|
||||||
<MiniRepl client:idle tune={`s("bd(2,8)")`} punchcard canvasHeight={50} />
|
<MiniRepl client:idle tune={`s("bd(2,8)")`} />
|
||||||
<MiniRepl client:idle tune={`s("bd(5,8)")`} punchcard canvasHeight={50} />
|
<MiniRepl client:idle tune={`s("bd(5,8)")`} />
|
||||||
<MiniRepl client:idle tune={`s("bd(7,8)")`} punchcard canvasHeight={50} />
|
<MiniRepl client:idle tune={`s("bd(7,8)")`} />
|
||||||
|
|
||||||
### Segments
|
## Segments
|
||||||
|
|
||||||
`segments`: the second parameter controls the total amount of segments the beats will be distributed over:
|
`segments`: the second parameter controls the total amount of segments the beats will be distributed over:
|
||||||
|
|
||||||
<MiniRepl client:idle tune={`s("bd(3,4)")`} punchcard canvasHeight={50} />
|
<MiniRepl client:idle tune={`s("bd(3,4)")`} />
|
||||||
<MiniRepl client:idle tune={`s("bd(3,8)")`} punchcard canvasHeight={50} />
|
<MiniRepl client:idle tune={`s("bd(3,8)")`} />
|
||||||
<MiniRepl client:idle tune={`s("bd(3,13)")`} punchcard canvasHeight={50} />
|
<MiniRepl client:idle tune={`s("bd(3,13)")`} />
|
||||||
|
|
||||||
### Offsets
|
## Offsets
|
||||||
|
|
||||||
`offset`: the third (optional) parameter controls the starting position for distributing the beats.
|
`offset`: the third (optional) parameter controls the starting position for distributing the beats.
|
||||||
We need a secondary rhythm to hear the difference:
|
We need a secondary rhythm to hear the difference:
|
||||||
|
|
||||||
<MiniRepl client:idle tune={`s("bd(3,8,0), hh cp")`} punchcard />
|
<MiniRepl client:idle tune={`s("bd(3,8,0), hh cp")`} />
|
||||||
<MiniRepl client:idle tune={`s("bd(3,8,3), hh cp")`} punchcard />
|
<MiniRepl client:idle tune={`s("bd(3,8,3), hh cp")`} />
|
||||||
<MiniRepl client:idle tune={`s("bd(3,8,5), hh cp")`} punchcard />
|
<MiniRepl client:idle tune={`s("bd(3,8,5), hh cp")`} />
|
||||||
|
|
||||||
## Mini-notation exercise
|
# Mini-notation exercise
|
||||||
|
|
||||||
The most fun thing about the mini-notation, is that everything you have just learned can be combined in various ways!
|
The most fun thing about the mini-notation, is that everything you have just learned can be combined in various ways!
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ Here's the same pattern written in three different ways:
|
|||||||
|
|
||||||
<MiniRepl client:idle tune={`freq("220 275 330 440")`} />
|
<MiniRepl client:idle tune={`freq("220 275 330 440")`} />
|
||||||
|
|
||||||
Let's look at those in more detail...
|
Let's look at `note`, `n` and `freq` in more detail...
|
||||||
|
|
||||||
## `note` names
|
## `note` names
|
||||||
|
|
||||||
|
|||||||
@@ -18,13 +18,13 @@ Strudel is written in JavaScript, while Tidal is written in Haskell.
|
|||||||
|
|
||||||
This difference is most obvious when looking at the syntax:
|
This difference is most obvious when looking at the syntax:
|
||||||
|
|
||||||
```haskell
|
```hs
|
||||||
iter 4 $ every 3 (||+ n "10 20") $ (n "0 1 3") # s "triangle" # crush 4
|
iter 4 $ every 3 (||+ n "10 20") $ (n "0 1 3") # s "triangle" # crush 4
|
||||||
```
|
```
|
||||||
|
|
||||||
One _could_ express that pattern to Strudel like so:
|
One _could_ express that pattern to Strudel like so:
|
||||||
|
|
||||||
```
|
```txt
|
||||||
iter(4, every(3, add.squeeze("10 20"), n("0 1 3").s("triangle").crush(4)))
|
iter(4, every(3, add.squeeze("10 20"), n("0 1 3").s("triangle").crush(4)))
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -37,7 +37,7 @@ operators, or change the meaning of existing ones.
|
|||||||
|
|
||||||
Before you discard Strudel as an unwieldy paren monster, look at this alternative way to write the above:
|
Before you discard Strudel as an unwieldy paren monster, look at this alternative way to write the above:
|
||||||
|
|
||||||
```
|
```txt
|
||||||
n("0 1 3").every(3, add.squeeze("10 20")).iter(4).s("triangle").crush(4)
|
n("0 1 3").every(3, add.squeeze("10 20")).iter(4).s("triangle").crush(4)
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -114,7 +114,7 @@ Also, samples are always loaded from a URL rather than from the disk, although [
|
|||||||
The Strudel REPL does not support [block based evaluation](https://github.com/tidalcycles/strudel/issues/34) yet.
|
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 the following "workaround" to create multiple patterns that can be turned on and off:
|
||||||
|
|
||||||
```
|
```txt
|
||||||
let a = note("c a f e")
|
let a = note("c a f e")
|
||||||
|
|
||||||
let b = s("bd sd")
|
let b = s("bd sd")
|
||||||
@@ -127,7 +127,7 @@ stack(
|
|||||||
|
|
||||||
Alternatively, you could write everything as one `stack` and use `.hush()` to silence a pattern:
|
Alternatively, you could write everything as one `stack` and use `.hush()` to silence a pattern:
|
||||||
|
|
||||||
```
|
```txt
|
||||||
stack(
|
stack(
|
||||||
note("c a f e"),
|
note("c a f e"),
|
||||||
s("bd sd").hush()
|
s("bd sd").hush()
|
||||||
@@ -141,6 +141,6 @@ Note that strudel will always use the last statement in your code as the pattern
|
|||||||
Strudels tempo is 1 cycle per second, while tidal defaults to `0.5625`.
|
Strudels tempo is 1 cycle per second, while tidal defaults to `0.5625`.
|
||||||
You can get the same tempo as tidal with:
|
You can get the same tempo as tidal with:
|
||||||
|
|
||||||
```
|
```txt
|
||||||
note("c a f e").fast(.5625);
|
note("c a f e").fast(.5625);
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -38,6 +38,10 @@ Some of these have equivalent operators in the Mini Notation:
|
|||||||
|
|
||||||
<JsDoc client:idle name="Pattern.legato" h={0} />
|
<JsDoc client:idle name="Pattern.legato" h={0} />
|
||||||
|
|
||||||
|
## struct
|
||||||
|
|
||||||
|
<JsDoc client:idle name="Pattern.struct" h={0} />
|
||||||
|
|
||||||
## euclid
|
## euclid
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern.euclid" h={0} />
|
<JsDoc client:idle name="Pattern.euclid" h={0} />
|
||||||
@@ -54,10 +58,6 @@ Some of these have equivalent operators in the Mini Notation:
|
|||||||
|
|
||||||
<JsDoc client:idle name="Pattern.rev" h={0} />
|
<JsDoc client:idle name="Pattern.rev" h={0} />
|
||||||
|
|
||||||
## palindrome
|
|
||||||
|
|
||||||
<JsDoc client:idle name="palindrome" h={0} />
|
|
||||||
|
|
||||||
## iter
|
## iter
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern.iter" h={0} />
|
<JsDoc client:idle name="Pattern.iter" h={0} />
|
||||||
@@ -66,38 +66,10 @@ Some of these have equivalent operators in the Mini Notation:
|
|||||||
|
|
||||||
<JsDoc client:idle name="Pattern.iterBack" h={0} />
|
<JsDoc client:idle name="Pattern.iterBack" h={0} />
|
||||||
|
|
||||||
## ply
|
## chunk
|
||||||
|
|
||||||
<JsDoc client:idle name="ply" h={0} />
|
<JsDoc client:idle name="Pattern.chunk" h={0} />
|
||||||
|
|
||||||
## segment
|
### chunkBack
|
||||||
|
|
||||||
<JsDoc client:idle name="segment" h={0} />
|
<JsDoc client:idle name="Pattern.chunkBack" h={0} />
|
||||||
|
|
||||||
## compress
|
|
||||||
|
|
||||||
<JsDoc client:idle name="compress" h={0} />
|
|
||||||
|
|
||||||
## zoom
|
|
||||||
|
|
||||||
<JsDoc client:idle name="zoom" h={0} />
|
|
||||||
|
|
||||||
## linger
|
|
||||||
|
|
||||||
<JsDoc client:idle name="linger" h={0} />
|
|
||||||
|
|
||||||
## fastGap
|
|
||||||
|
|
||||||
<JsDoc client:idle name="fastGap" h={0} />
|
|
||||||
|
|
||||||
## inside
|
|
||||||
|
|
||||||
<JsDoc client:idle name="inside" h={0} />
|
|
||||||
|
|
||||||
## outside
|
|
||||||
|
|
||||||
<JsDoc client:idle name="outside" h={0} />
|
|
||||||
|
|
||||||
## cpm
|
|
||||||
|
|
||||||
<JsDoc client:idle name="cpm" h={0} />
|
|
||||||
|
|||||||
@@ -29,11 +29,7 @@ add a mini repl with
|
|||||||
|
|
||||||
- `client:idle` is required to tell astro that the repl should be interactive, see [Client Directive](https://docs.astro.build/en/reference/directives-reference/#client-directives)
|
- `client:idle` is required to tell astro that the repl should be interactive, see [Client Directive](https://docs.astro.build/en/reference/directives-reference/#client-directives)
|
||||||
- `tune`: be any valid pattern code
|
- `tune`: be any valid pattern code
|
||||||
- `punchcard`: if added, a punchcard / pianoroll visualization is renderd
|
- `withCanvas`: If set, a canvas will be rendered below that shows a pianoroll (WIP)
|
||||||
- `drawTime`: time window for drawing, defaults to `[0, 4]`
|
|
||||||
- `canvasHeight`: height of the canvas, defaults to 100px
|
|
||||||
|
|
||||||
See `mini-notation.mdx` for usage examples
|
|
||||||
|
|
||||||
## In-Source Documentation
|
## In-Source Documentation
|
||||||
|
|
||||||
|
|||||||
@@ -1,265 +0,0 @@
|
|||||||
---
|
|
||||||
title: Internals
|
|
||||||
layout: ../../layouts/MainLayout.astro
|
|
||||||
---
|
|
||||||
|
|
||||||
import { MiniRepl } from '../../docs/MiniRepl';
|
|
||||||
import { JsDoc } from '../../docs/JsDoc';
|
|
||||||
|
|
||||||
# Internal Functions
|
|
||||||
|
|
||||||
These functions are more low level, probably not needed by the live coder.
|
|
||||||
|
|
||||||
# Haskell-style functor, applicative and monadic operations
|
|
||||||
|
|
||||||
## withValue
|
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern#withValue" h={0} />
|
|
||||||
|
|
||||||
## appWhole
|
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern#appWhole" h={0} />
|
|
||||||
|
|
||||||
## appBoth
|
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern#appBoth" h={0} />
|
|
||||||
|
|
||||||
## appLeft
|
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern#appLeft" h={0} />
|
|
||||||
|
|
||||||
## appRight
|
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern#appRight" h={0} />
|
|
||||||
|
|
||||||
## bindWhole
|
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern#bindWhole" h={0} />
|
|
||||||
|
|
||||||
## bind
|
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern#bind" h={0} />
|
|
||||||
|
|
||||||
## join
|
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern#join" h={0} />
|
|
||||||
|
|
||||||
## outerBind
|
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern#outerBind" h={0} />
|
|
||||||
|
|
||||||
## outerJoin
|
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern#outerJoin" h={0} />
|
|
||||||
|
|
||||||
## innerBind
|
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern#innerBind" h={0} />
|
|
||||||
|
|
||||||
## innerJoin
|
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern#innerJoin" h={0} />
|
|
||||||
|
|
||||||
## trigJoin
|
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern#trigJoin" h={0} />
|
|
||||||
|
|
||||||
## trigzeroJoin
|
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern#trigzeroJoin" h={0} />
|
|
||||||
|
|
||||||
## squeezeJoin
|
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern#squeezeJoin" h={0} />
|
|
||||||
|
|
||||||
## squeezeBind
|
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern#squeezeBind" h={0} />
|
|
||||||
|
|
||||||
# Utility methods mainly for internal use
|
|
||||||
|
|
||||||
## queryArc
|
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern#queryArc" h={0} />
|
|
||||||
|
|
||||||
## splitQueries
|
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern#splitQueries" h={0} />
|
|
||||||
|
|
||||||
## withQuerySpan
|
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern#withQuerySpan" h={0} />
|
|
||||||
|
|
||||||
## withQuerySpanMaybe
|
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern#withQuerySpanMaybe" h={0} />
|
|
||||||
|
|
||||||
## withQueryTime
|
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern#withQueryTime" h={0} />
|
|
||||||
|
|
||||||
## withHapSpan
|
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern#withHapSpan" h={0} />
|
|
||||||
|
|
||||||
## withHapTime
|
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern#withHapTime" h={0} />
|
|
||||||
|
|
||||||
## withHaps
|
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern#withHaps" h={0} />
|
|
||||||
|
|
||||||
## withHap
|
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern#withHap" h={0} />
|
|
||||||
|
|
||||||
## setContext
|
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern#setContext" h={0} />
|
|
||||||
|
|
||||||
## withContext
|
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern#setContext" h={0} />
|
|
||||||
|
|
||||||
## stripContext
|
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern#stripContext" h={0} />
|
|
||||||
|
|
||||||
## withLocation
|
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern#withLocation" h={0} />
|
|
||||||
|
|
||||||
## withMiniLocation
|
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern#withMiniLocation" h={0} />
|
|
||||||
|
|
||||||
## filterHaps
|
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern#filterHaps" h={0} />
|
|
||||||
|
|
||||||
## filterValues
|
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern#filterValues" h={0} />
|
|
||||||
|
|
||||||
## removeUndefineds
|
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern#filterValues" h={0} />
|
|
||||||
|
|
||||||
## onsetsOnly
|
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern#onsetsOnly" h={0} />
|
|
||||||
|
|
||||||
## discreteOnly
|
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern#discreteOnly" h={0} />
|
|
||||||
|
|
||||||
## defragmentHaps
|
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern#defragmentHaps" h={0} />
|
|
||||||
|
|
||||||
## firstCycle
|
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern#firstCycle" h={0} />
|
|
||||||
|
|
||||||
## firstCycleValues
|
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern#firstCycleValues" h={0} />
|
|
||||||
|
|
||||||
## showFirstCycle
|
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern#showFirstCycle" h={0} />
|
|
||||||
|
|
||||||
## sortHapsByPart
|
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern#sortHapsByPart" h={0} />
|
|
||||||
|
|
||||||
## asNumber
|
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern#sortHapsByPart" h={0} />
|
|
||||||
|
|
||||||
# Operators
|
|
||||||
|
|
||||||
- \_opIn
|
|
||||||
- \_opOut
|
|
||||||
- \_opMix
|
|
||||||
- \_opSqueeze
|
|
||||||
- \_opSqueezeOut
|
|
||||||
- \_opTrig
|
|
||||||
- \_opTrigzero
|
|
||||||
|
|
||||||
# Other
|
|
||||||
|
|
||||||
## onTrigger
|
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern#onTrigger" h={0} />
|
|
||||||
|
|
||||||
## log
|
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern#log" h={0} />
|
|
||||||
|
|
||||||
## logValues
|
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern#logValues" h={0} />
|
|
||||||
|
|
||||||
## drawLine
|
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern#drawLine" h={0} />
|
|
||||||
|
|
||||||
## collect
|
|
||||||
|
|
||||||
# Functions
|
|
||||||
|
|
||||||
## groupHapsBy
|
|
||||||
|
|
||||||
<JsDoc client:idle name="groupHapsBy" h={0} />
|
|
||||||
|
|
||||||
## pure
|
|
||||||
|
|
||||||
<JsDoc client:idle name="pure" h={0} />
|
|
||||||
|
|
||||||
## reify
|
|
||||||
|
|
||||||
<JsDoc client:idle name="reify" h={0} />
|
|
||||||
|
|
||||||
## slowcatPrime
|
|
||||||
|
|
||||||
<JsDoc client:idle name="slowcatPrime" h={0} />
|
|
||||||
|
|
||||||
## isPattern
|
|
||||||
|
|
||||||
<JsDoc client:idle name="isPattern" h={0} />
|
|
||||||
|
|
||||||
## register
|
|
||||||
|
|
||||||
<JsDoc client:idle name="register" h={0} />
|
|
||||||
|
|
||||||
## toBipolar
|
|
||||||
|
|
||||||
<JsDoc client:idle name="toBipolar" h={0} />
|
|
||||||
|
|
||||||
## fromBipolar
|
|
||||||
|
|
||||||
<JsDoc client:idle name="fromBipolar" h={0} />
|
|
||||||
|
|
||||||
## compressSpan
|
|
||||||
|
|
||||||
<JsDoc client:idle name="compressSpan" h={0} />
|
|
||||||
|
|
||||||
## focus
|
|
||||||
|
|
||||||
<JsDoc client:idle name="focus" h={0} />
|
|
||||||
|
|
||||||
## focusSpan
|
|
||||||
|
|
||||||
## \_composeOp
|
|
||||||
|
|
||||||
# Composers
|
|
||||||
|
|
||||||
```
|
|
||||||
set keep keepif add sub mul div mod pow band bor bxor blshift brshift lt gt lte gte eq eqt ne net and or func
|
|
||||||
```
|
|
||||||
|
|
||||||
```
|
|
||||||
In Out Mix Squeeze SqueezeOut Trig Trigzero
|
|
||||||
```
|
|
||||||
@@ -4,7 +4,7 @@ Copyright (C) 2022 Strudel contributors - see <https://github.com/tidalcycles/st
|
|||||||
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { cleanupDraw, cleanupUi, controls, evalScope, getDrawContext, logger } from '@strudel.cycles/core';
|
import { cleanupDraw, cleanupUi, controls, evalScope, logger } from '@strudel.cycles/core';
|
||||||
import { CodeMirror, cx, flash, useHighlighting, useStrudel } from '@strudel.cycles/react';
|
import { CodeMirror, cx, flash, useHighlighting, useStrudel } from '@strudel.cycles/react';
|
||||||
import {
|
import {
|
||||||
getAudioContext,
|
getAudioContext,
|
||||||
@@ -54,12 +54,6 @@ evalScope(
|
|||||||
export let loadedSamples = [];
|
export let loadedSamples = [];
|
||||||
const presets = prebake();
|
const presets = prebake();
|
||||||
|
|
||||||
let drawContext, clearCanvas;
|
|
||||||
if (typeof window !== 'undefined') {
|
|
||||||
drawContext = getDrawContext();
|
|
||||||
clearCanvas = () => drawContext.clearRect(0, 0, drawContext.canvas.height, drawContext.canvas.width);
|
|
||||||
}
|
|
||||||
|
|
||||||
Promise.all([...modules, presets]).then((data) => {
|
Promise.all([...modules, presets]).then((data) => {
|
||||||
// console.log('modules and sample registry loade', data);
|
// console.log('modules and sample registry loade', data);
|
||||||
loadedSamples = Object.entries(getLoadedSamples() || {});
|
loadedSamples = Object.entries(getLoadedSamples() || {});
|
||||||
@@ -131,7 +125,6 @@ export function Repl({ embedded = false }) {
|
|||||||
setPending(false);
|
setPending(false);
|
||||||
},
|
},
|
||||||
onToggle: (play) => !play && cleanupDraw(false),
|
onToggle: (play) => !play && cleanupDraw(false),
|
||||||
drawContext,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// init code
|
// init code
|
||||||
@@ -174,7 +167,7 @@ export function Repl({ embedded = false }) {
|
|||||||
view,
|
view,
|
||||||
pattern,
|
pattern,
|
||||||
active: started && !activeCode?.includes('strudel disable-highlighting'),
|
active: started && !activeCode?.includes('strudel disable-highlighting'),
|
||||||
getTime: () => scheduler.now(),
|
getTime: () => scheduler.getPhase(),
|
||||||
});
|
});
|
||||||
|
|
||||||
//
|
//
|
||||||
@@ -210,7 +203,6 @@ export function Repl({ embedded = false }) {
|
|||||||
const handleShuffle = async () => {
|
const handleShuffle = async () => {
|
||||||
const { code, name } = getRandomTune();
|
const { code, name } = getRandomTune();
|
||||||
logger(`[repl] ✨ loading random tune "${name}"`);
|
logger(`[repl] ✨ loading random tune "${name}"`);
|
||||||
clearCanvas();
|
|
||||||
resetLoadedSamples();
|
resetLoadedSamples();
|
||||||
await prebake(); // declare default samples
|
await prebake(); // declare default samples
|
||||||
await evaluate(code, false);
|
await evaluate(code, false);
|
||||||
|
|||||||
@@ -857,7 +857,7 @@ export const arpoon = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.
|
|||||||
await samples('github:tidalcycles/Dirt-Samples/master')
|
await samples('github:tidalcycles/Dirt-Samples/master')
|
||||||
|
|
||||||
"<<Am7 C^7> C7 F^7 [Fm7 E7b9]>".voicings('lefthand')
|
"<<Am7 C^7> C7 F^7 [Fm7 E7b9]>".voicings('lefthand')
|
||||||
.arp("[0,3] 2 [1,3] 2".fast(3).lastOf(4, fast(2))).legato(2)
|
.arp("[0,3] 2 [1,3] 2".fast(3)).legato(2)
|
||||||
.add(perlin.range(0,0.2)).sub("<0 -12>/8")
|
.add(perlin.range(0,0.2)).sub("<0 -12>/8")
|
||||||
.note().cutoff(perlin.range(500,4000)).resonance(12)
|
.note().cutoff(perlin.range(500,4000)).resonance(12)
|
||||||
.gain("<.5 .8>*16")
|
.gain("<.5 .8>*16")
|
||||||
@@ -867,5 +867,5 @@ await samples('github:tidalcycles/Dirt-Samples/master')
|
|||||||
.s('piano').clip(1)
|
.s('piano').clip(1)
|
||||||
.stack("<<A1 C2>!2 F2 [F2 E2]>".add.out("0 -5".fast(2)).add("0,.12").note().s('sawtooth').clip(1).cutoff(300))
|
.stack("<<A1 C2>!2 F2 [F2 E2]>".add.out("0 -5".fast(2)).add("0,.12").note().s('sawtooth').clip(1).cutoff(300))
|
||||||
.slow(4)
|
.slow(4)
|
||||||
.stack(s("bd*4, [~ [hh hh? hh?]]*2,~ [sd ~ [sd:2? bd?]]").bank('RolandTR909').gain(.5).slow(2))
|
.stack(s("bd*4, [~ [hh hh? hh?]]*2,~ [sd ~ [sd:2? bd?]]").arp("0|1").bank('RolandTR909').gain(.5).slow(2))
|
||||||
`;
|
`;
|
||||||
|
|||||||
Reference in New Issue
Block a user