mirror of
https://github.com/eliasstepanik/strudel-docker.git
synced 2026-01-22 19:18:31 +00:00
commit
e8b09f1124
@ -73,7 +73,7 @@ const bjork = function (ons, steps) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Like `iter`, but has an additional parameter for 'rotating' the resulting sequence.
|
* Like `euclid`, but has an additional parameter for 'rotating' the resulting sequence.
|
||||||
* @memberof Pattern
|
* @memberof Pattern
|
||||||
* @name euclidRot
|
* @name euclidRot
|
||||||
* @param {number} pulses the number of onsets / beats
|
* @param {number} pulses the number of onsets / beats
|
||||||
|
|||||||
@ -712,7 +712,7 @@ export class Pattern {
|
|||||||
* @memberof Pattern
|
* @memberof Pattern
|
||||||
* @example
|
* @example
|
||||||
* s("hh*2").stack(
|
* s("hh*2").stack(
|
||||||
* n("c2(3,8)")
|
* note("c2(3,8)")
|
||||||
* )
|
* )
|
||||||
*/
|
*/
|
||||||
stack(...pats) {
|
stack(...pats) {
|
||||||
@ -729,7 +729,7 @@ export class Pattern {
|
|||||||
* @memberof Pattern
|
* @memberof Pattern
|
||||||
* @example
|
* @example
|
||||||
* s("hh*2").seq(
|
* s("hh*2").seq(
|
||||||
* n("c2(3,8)")
|
* note("c2(3,8)")
|
||||||
* )
|
* )
|
||||||
*/
|
*/
|
||||||
seq(...pats) {
|
seq(...pats) {
|
||||||
@ -742,7 +742,7 @@ export class Pattern {
|
|||||||
* @memberof Pattern
|
* @memberof Pattern
|
||||||
* @example
|
* @example
|
||||||
* s("hh*2").cat(
|
* s("hh*2").cat(
|
||||||
* n("c2(3,8)")
|
* note("c2(3,8)")
|
||||||
* )
|
* )
|
||||||
*/
|
*/
|
||||||
cat(...pats) {
|
cat(...pats) {
|
||||||
@ -776,8 +776,10 @@ export class Pattern {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
log(func = (_, hap) => `[hap] ${hap.showWhole(true)}`) {
|
log(func = (_, hap) => `[hap] ${hap.showWhole(true)}`, getData = (_, hap) => ({ hap })) {
|
||||||
return this.onTrigger((...args) => logger(func(...args)), false);
|
return this.onTrigger((...args) => {
|
||||||
|
logger(func(...args), undefined, getData(...args));
|
||||||
|
}, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
logValues(func = id) {
|
logValues(func = id) {
|
||||||
@ -1045,7 +1047,12 @@ Pattern.prototype.factories = {
|
|||||||
|
|
||||||
// Elemental patterns
|
// Elemental patterns
|
||||||
|
|
||||||
// Nothing
|
/**
|
||||||
|
* Does absolutely nothing..
|
||||||
|
* @name silence
|
||||||
|
* @example
|
||||||
|
* silence // "~"
|
||||||
|
*/
|
||||||
export const silence = new Pattern(() => []);
|
export const silence = new Pattern(() => []);
|
||||||
|
|
||||||
/** A discrete value that repeats once per cycle.
|
/** A discrete value that repeats once per cycle.
|
||||||
@ -1215,7 +1222,17 @@ function _sequenceCount(x) {
|
|||||||
}
|
}
|
||||||
return [reify(x), 1];
|
return [reify(x), 1];
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Aligns one or more given sequences to the given number of steps per cycle.
|
||||||
|
*
|
||||||
|
* @name polymeterSteps
|
||||||
|
* @param {number} steps how many items are placed in one cycle
|
||||||
|
* @param {any[]} sequences one or more arrays of Patterns / values
|
||||||
|
* @example
|
||||||
|
* polymeterSteps(2, ["c", "d", "e", "f", "g", "f", "e", "d"])
|
||||||
|
* .note().stack(s("bd")) // 1 cycle = 1 bd = 2 notes
|
||||||
|
* // note("{c d e f g f e d}%2").stack(s("bd"))
|
||||||
|
*/
|
||||||
export function polymeterSteps(steps, ...args) {
|
export function polymeterSteps(steps, ...args) {
|
||||||
const seqs = args.map((a) => _sequenceCount(a));
|
const seqs = args.map((a) => _sequenceCount(a));
|
||||||
if (seqs.length == 0) {
|
if (seqs.length == 0) {
|
||||||
@ -1238,6 +1255,14 @@ export function polymeterSteps(steps, ...args) {
|
|||||||
return stack(...pats);
|
return stack(...pats);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Combines the given lists of patterns with the same pulse. This will create so called polymeters when different sized sequences are used.
|
||||||
|
* @name polymeter
|
||||||
|
* @example
|
||||||
|
* polymeter(["c", "eb", "g"], ["c2", "g2"]).note()
|
||||||
|
* // "{c eb g, c2 g2}".note()
|
||||||
|
*
|
||||||
|
*/
|
||||||
export function polymeter(...args) {
|
export function polymeter(...args) {
|
||||||
return polymeterSteps(0, ...args);
|
return polymeterSteps(0, ...args);
|
||||||
}
|
}
|
||||||
@ -1354,7 +1379,11 @@ export const round = register('round', function (pat) {
|
|||||||
* Assumes a numerical pattern. Returns a new pattern with all values set to
|
* Assumes a numerical pattern. Returns a new pattern with all values set to
|
||||||
* their mathematical floor. E.g. `3.7` replaced with to `3`, and `-4.2`
|
* their mathematical floor. E.g. `3.7` replaced with to `3`, and `-4.2`
|
||||||
* replaced with `-5`.
|
* replaced with `-5`.
|
||||||
|
* @name floor
|
||||||
|
* @memberof Pattern
|
||||||
* @returns Pattern
|
* @returns Pattern
|
||||||
|
* @example
|
||||||
|
* "42 42.1 42.5 43".floor().note()
|
||||||
*/
|
*/
|
||||||
export const floor = register('floor', function (pat) {
|
export const floor = register('floor', function (pat) {
|
||||||
return pat.asNumber().fmap((v) => Math.floor(v));
|
return pat.asNumber().fmap((v) => Math.floor(v));
|
||||||
@ -1364,7 +1393,11 @@ export const floor = register('floor', function (pat) {
|
|||||||
* Assumes a numerical pattern. Returns a new pattern with all values set to
|
* Assumes a numerical pattern. Returns a new pattern with all values set to
|
||||||
* their mathematical ceiling. E.g. `3.2` replaced with `4`, and `-4.2`
|
* their mathematical ceiling. E.g. `3.2` replaced with `4`, and `-4.2`
|
||||||
* replaced with `-4`.
|
* replaced with `-4`.
|
||||||
|
* @name ceil
|
||||||
|
* @memberof Pattern
|
||||||
* @returns Pattern
|
* @returns Pattern
|
||||||
|
* @example
|
||||||
|
* "42 42.1 42.5 43".ceil().note()
|
||||||
*/
|
*/
|
||||||
export const ceil = register('ceil', function (pat) {
|
export const ceil = register('ceil', function (pat) {
|
||||||
return pat.asNumber().fmap((v) => Math.ceil(v));
|
return pat.asNumber().fmap((v) => Math.ceil(v));
|
||||||
|
|||||||
@ -12,6 +12,7 @@ export function repl({
|
|||||||
afterEval,
|
afterEval,
|
||||||
getTime,
|
getTime,
|
||||||
transpiler,
|
transpiler,
|
||||||
|
editPattern,
|
||||||
onToggle,
|
onToggle,
|
||||||
}) {
|
}) {
|
||||||
const scheduler = new Cyclist({
|
const scheduler = new Cyclist({
|
||||||
@ -41,8 +42,10 @@ export function repl({
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
beforeEval?.({ code });
|
beforeEval?.({ code });
|
||||||
const { pattern } = await _evaluate(code, transpiler);
|
let { pattern } = await _evaluate(code, transpiler);
|
||||||
|
|
||||||
logger(`[eval] code updated`);
|
logger(`[eval] code updated`);
|
||||||
|
pattern = editPattern?.(pattern) || pattern;
|
||||||
scheduler.setPattern(pattern, autostart);
|
scheduler.setPattern(pattern, autostart);
|
||||||
afterEval?.({ code, pattern });
|
afterEval?.({ code, pattern });
|
||||||
return pattern;
|
return pattern;
|
||||||
|
|||||||
2
packages/react/dist/index.cjs.js
vendored
2
packages/react/dist/index.cjs.js
vendored
File diff suppressed because one or more lines are too long
461
packages/react/dist/index.es.js
vendored
461
packages/react/dist/index.es.js
vendored
@ -1,15 +1,15 @@
|
|||||||
import i, { useCallback as N, useRef as E, useEffect as F, useMemo as V, useState as _, useLayoutEffect as U } from "react";
|
import l, { useCallback as N, useRef as k, useEffect as _, useMemo as K, useState as w, useLayoutEffect as G } from "react";
|
||||||
import Y from "@uiw/react-codemirror";
|
import Z from "@uiw/react-codemirror";
|
||||||
import { Decoration as y, EditorView as W } from "@codemirror/view";
|
import { Decoration as y, EditorView as J } from "@codemirror/view";
|
||||||
import { StateEffect as $, StateField as G } from "@codemirror/state";
|
import { StateEffect as Q, StateField as X } from "@codemirror/state";
|
||||||
import { javascript as Z } from "@codemirror/lang-javascript";
|
import { javascript as ee } from "@codemirror/lang-javascript";
|
||||||
import { tags as s } from "@lezer/highlight";
|
import { tags as s } from "@lezer/highlight";
|
||||||
import { createTheme as ee } from "@uiw/codemirror-themes";
|
import { createTheme as te } from "@uiw/codemirror-themes";
|
||||||
import { repl as te, pianoroll as re } from "@strudel.cycles/core";
|
import { repl as re, logger as ne, pianoroll as oe } from "@strudel.cycles/core";
|
||||||
import { webaudioOutput as oe, getAudioContext as ne } from "@strudel.cycles/webaudio";
|
import { webaudioOutput as ae, getAudioContext as ce } from "@strudel.cycles/webaudio";
|
||||||
import { useInView as ae } from "react-hook-inview";
|
import { useInView as se } from "react-hook-inview";
|
||||||
import { transpiler as se } from "@strudel.cycles/transpiler";
|
import { transpiler as ie } from "@strudel.cycles/transpiler";
|
||||||
const ce = ee({
|
const le = te({
|
||||||
theme: "dark",
|
theme: "dark",
|
||||||
settings: {
|
settings: {
|
||||||
background: "#222",
|
background: "#222",
|
||||||
@ -42,14 +42,14 @@ const ce = ee({
|
|||||||
{ tag: s.invalid, color: "#ffffff" }
|
{ tag: s.invalid, color: "#ffffff" }
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
const B = $.define(), ie = G.define({
|
const j = Q.define(), ue = X.define({
|
||||||
create() {
|
create() {
|
||||||
return y.none;
|
return y.none;
|
||||||
},
|
},
|
||||||
update(e, r) {
|
update(e, r) {
|
||||||
try {
|
try {
|
||||||
for (let t of r.effects)
|
for (let t of r.effects)
|
||||||
if (t.is(B))
|
if (t.is(j))
|
||||||
if (t.value) {
|
if (t.value) {
|
||||||
const a = y.mark({ attributes: { style: "background-color: #FFCA2880" } });
|
const a = y.mark({ attributes: { style: "background-color: #FFCA2880" } });
|
||||||
e = y.set([a.range(0, r.newDoc.length)]);
|
e = y.set([a.range(0, r.newDoc.length)]);
|
||||||
@ -60,25 +60,25 @@ const B = $.define(), ie = G.define({
|
|||||||
return console.warn("flash error", t), e;
|
return console.warn("flash error", t), e;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
provide: (e) => W.decorations.from(e)
|
provide: (e) => J.decorations.from(e)
|
||||||
}), le = (e) => {
|
}), de = (e) => {
|
||||||
e.dispatch({ effects: B.of(!0) }), setTimeout(() => {
|
e.dispatch({ effects: j.of(!0) }), setTimeout(() => {
|
||||||
e.dispatch({ effects: B.of(!1) });
|
e.dispatch({ effects: j.of(!1) });
|
||||||
}, 200);
|
}, 200);
|
||||||
}, H = $.define(), ue = G.define({
|
}, z = Q.define(), fe = X.define({
|
||||||
create() {
|
create() {
|
||||||
return y.none;
|
return y.none;
|
||||||
},
|
},
|
||||||
update(e, r) {
|
update(e, r) {
|
||||||
try {
|
try {
|
||||||
for (let t of r.effects)
|
for (let t of r.effects)
|
||||||
if (t.is(H)) {
|
if (t.is(z)) {
|
||||||
const a = t.value.map(
|
const a = t.value.map(
|
||||||
(n) => (n.context.locations || []).map(({ start: c, end: l }) => {
|
(o) => (o.context.locations || []).map(({ start: i, end: u }) => {
|
||||||
const d = n.context.color || "#FFCA28";
|
const m = o.context.color || "#FFCA28";
|
||||||
let o = r.newDoc.line(c.line).from + c.column, u = r.newDoc.line(l.line).from + l.column;
|
let n = r.newDoc.line(i.line).from + i.column, d = r.newDoc.line(u.line).from + u.column;
|
||||||
const f = r.newDoc.length;
|
const v = r.newDoc.length;
|
||||||
return o > f || u > f ? void 0 : y.mark({ attributes: { style: `outline: 1.5px solid ${d};` } }).range(o, u);
|
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 = y.set(a, !0);
|
e = y.set(a, !0);
|
||||||
@ -88,291 +88,314 @@ const B = $.define(), ie = G.define({
|
|||||||
return y.set([]);
|
return y.set([]);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
provide: (e) => W.decorations.from(e)
|
provide: (e) => J.decorations.from(e)
|
||||||
}), de = [Z(), ce, ue, ie];
|
}), me = [ee(), le, fe, ue];
|
||||||
function fe({ value: e, onChange: r, onViewChanged: t, onSelectionChange: a, options: n, editorDidMount: c }) {
|
function ge({ value: e, onChange: r, onViewChanged: t, onSelectionChange: a, options: o, editorDidMount: i }) {
|
||||||
const l = N(
|
const u = N(
|
||||||
(u) => {
|
(d) => {
|
||||||
r?.(u);
|
r?.(d);
|
||||||
},
|
},
|
||||||
[r]
|
[r]
|
||||||
), d = N(
|
), m = N(
|
||||||
(u) => {
|
(d) => {
|
||||||
t?.(u);
|
t?.(d);
|
||||||
},
|
},
|
||||||
[t]
|
[t]
|
||||||
), o = N(
|
), n = N(
|
||||||
(u) => {
|
(d) => {
|
||||||
u.selectionSet && a && a?.(u.state.selection);
|
d.selectionSet && a && a?.(d.state.selection);
|
||||||
},
|
},
|
||||||
[a]
|
[a]
|
||||||
);
|
);
|
||||||
return /* @__PURE__ */ i.createElement(i.Fragment, null, /* @__PURE__ */ i.createElement(Y, {
|
return /* @__PURE__ */ l.createElement(l.Fragment, null, /* @__PURE__ */ l.createElement(Z, {
|
||||||
value: e,
|
value: e,
|
||||||
onChange: l,
|
onChange: u,
|
||||||
onCreateEditor: d,
|
onCreateEditor: m,
|
||||||
onUpdate: o,
|
onUpdate: n,
|
||||||
extensions: de
|
extensions: me
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
function j(...e) {
|
function W(...e) {
|
||||||
return e.filter(Boolean).join(" ");
|
return e.filter(Boolean).join(" ");
|
||||||
}
|
}
|
||||||
function me({ view: e, pattern: r, active: t, getTime: a }) {
|
function pe({ view: e, pattern: r, active: t, getTime: a }) {
|
||||||
const n = E([]), c = E();
|
const o = k([]), i = k();
|
||||||
F(() => {
|
_(() => {
|
||||||
if (e)
|
if (e)
|
||||||
if (r && t) {
|
if (r && t) {
|
||||||
let l = requestAnimationFrame(function d() {
|
let u = requestAnimationFrame(function m() {
|
||||||
try {
|
try {
|
||||||
const o = a(), f = [Math.max(c.current || o, o - 1 / 10, 0), o + 1 / 60];
|
const n = a(), v = [Math.max(i.current || n, n - 1 / 10, 0), n + 1 / 60];
|
||||||
c.current = f[1], n.current = n.current.filter((v) => v.whole.end > o);
|
i.current = v[1], o.current = o.current.filter((p) => p.whole.end > n);
|
||||||
const m = r.queryArc(...f).filter((v) => v.hasOnset());
|
const h = r.queryArc(...v).filter((p) => p.hasOnset());
|
||||||
n.current = n.current.concat(m), e.dispatch({ effects: H.of(n.current) });
|
o.current = o.current.concat(h), e.dispatch({ effects: z.of(o.current) });
|
||||||
} catch {
|
} catch {
|
||||||
e.dispatch({ effects: H.of([]) });
|
e.dispatch({ effects: z.of([]) });
|
||||||
}
|
}
|
||||||
l = requestAnimationFrame(d);
|
u = requestAnimationFrame(m);
|
||||||
});
|
});
|
||||||
return () => {
|
return () => {
|
||||||
cancelAnimationFrame(l);
|
cancelAnimationFrame(u);
|
||||||
};
|
};
|
||||||
} else
|
} else
|
||||||
n.current = [], e.dispatch({ effects: H.of([]) });
|
o.current = [], e.dispatch({ effects: z.of([]) });
|
||||||
}, [r, t, e]);
|
}, [r, t, e]);
|
||||||
}
|
}
|
||||||
function ge(e, r = !1) {
|
function he(e, r = !1) {
|
||||||
const t = E(), a = E(), n = (d) => {
|
const t = k(), a = k(), o = (m) => {
|
||||||
if (a.current !== void 0) {
|
if (a.current !== void 0) {
|
||||||
const o = d - a.current;
|
const n = m - a.current;
|
||||||
e(d, o);
|
e(m, n);
|
||||||
}
|
}
|
||||||
a.current = d, t.current = requestAnimationFrame(n);
|
a.current = m, t.current = requestAnimationFrame(o);
|
||||||
}, c = () => {
|
}, i = () => {
|
||||||
t.current = requestAnimationFrame(n);
|
t.current = requestAnimationFrame(o);
|
||||||
}, l = () => {
|
}, u = () => {
|
||||||
t.current && cancelAnimationFrame(t.current), delete t.current;
|
t.current && cancelAnimationFrame(t.current), delete t.current;
|
||||||
};
|
};
|
||||||
return F(() => {
|
return _(() => {
|
||||||
t.current && (l(), c());
|
t.current && (u(), i());
|
||||||
}, [e]), F(() => (r && c(), l), []), {
|
}, [e]), _(() => (r && i(), u), []), {
|
||||||
start: c,
|
start: i,
|
||||||
stop: l
|
stop: u
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
function pe({ pattern: e, started: r, getTime: t, onDraw: a }) {
|
function ve({ pattern: e, started: r, getTime: t, onDraw: a }) {
|
||||||
let n = E([]), c = E(null);
|
let o = k([]), i = k(null);
|
||||||
const { start: l, stop: d } = ge(
|
const { start: u, stop: m } = he(
|
||||||
N(() => {
|
N(() => {
|
||||||
const o = t();
|
const n = t();
|
||||||
if (c.current === null) {
|
if (i.current === null) {
|
||||||
c.current = o;
|
i.current = n;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const u = e.queryArc(Math.max(c.current, o - 1 / 10), o), f = 4;
|
const d = e.queryArc(Math.max(i.current, n - 1 / 10), n), v = 4;
|
||||||
c.current = o, n.current = (n.current || []).filter((m) => m.whole.end > o - f).concat(u.filter((m) => m.hasOnset())), a(o, n.current);
|
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])
|
||||||
);
|
);
|
||||||
F(() => {
|
_(() => {
|
||||||
r ? l() : (n.current = [], d());
|
r ? u() : (o.current = [], m());
|
||||||
}, [r]);
|
}, [r]);
|
||||||
}
|
}
|
||||||
function he(e) {
|
function be(e) {
|
||||||
return F(() => (window.addEventListener("message", e), () => window.removeEventListener("message", e)), [e]), N((r) => window.postMessage(r, "*"), []);
|
return _(() => (window.addEventListener("message", e), () => window.removeEventListener("message", e)), [e]), N((r) => window.postMessage(r, "*"), []);
|
||||||
}
|
}
|
||||||
function ve({
|
function Ee({
|
||||||
defaultOutput: e,
|
defaultOutput: e,
|
||||||
interval: r,
|
interval: r,
|
||||||
getTime: t,
|
getTime: t,
|
||||||
evalOnMount: a = !1,
|
evalOnMount: a = !1,
|
||||||
initialCode: n = "",
|
initialCode: o = "",
|
||||||
autolink: c = !1,
|
autolink: i = !1,
|
||||||
beforeEval: l,
|
beforeEval: u,
|
||||||
afterEval: d,
|
afterEval: m,
|
||||||
onEvalError: o,
|
editPattern: n,
|
||||||
onToggle: u,
|
onEvalError: d,
|
||||||
canvasId: f
|
onToggle: v,
|
||||||
|
canvasId: h
|
||||||
}) {
|
}) {
|
||||||
const m = V(() => be(), []);
|
const p = K(() => we(), []);
|
||||||
f = f || `canvas-${m}`;
|
h = h || `canvas-${p}`;
|
||||||
const [v, k] = _(), [M, T] = _(), [b, A] = _(n), [C, S] = _(), [z, D] = _(), [x, L] = _(!1), h = b !== C, { scheduler: g, evaluate: R, start: J, stop: O, pause: Q } = V(
|
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(
|
||||||
() => te({
|
() => re({
|
||||||
interval: r,
|
interval: r,
|
||||||
defaultOutput: e,
|
defaultOutput: e,
|
||||||
onSchedulerError: k,
|
onSchedulerError: A,
|
||||||
onEvalError: (p) => {
|
onEvalError: (g) => {
|
||||||
T(p), o?.(p);
|
D(g), d?.(g);
|
||||||
},
|
},
|
||||||
getTime: t,
|
getTime: t,
|
||||||
transpiler: se,
|
transpiler: ie,
|
||||||
beforeEval: ({ code: p }) => {
|
beforeEval: ({ code: g }) => {
|
||||||
A(p), l?.();
|
q(g), u?.();
|
||||||
},
|
},
|
||||||
afterEval: ({ pattern: p, code: q }) => {
|
editPattern: n ? (g) => n(g, p) : void 0,
|
||||||
S(q), D(p), T(), k(), c && (window.location.hash = "#" + encodeURIComponent(btoa(q))), d?.();
|
afterEval: ({ pattern: g, code: T }) => {
|
||||||
|
V(T), R(g), D(), A(), i && (window.location.hash = "#" + encodeURIComponent(btoa(T))), m?.();
|
||||||
},
|
},
|
||||||
onToggle: (p) => {
|
onToggle: (g) => {
|
||||||
L(p), u?.(p);
|
B(g), v?.(g);
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
[e, r, t]
|
[e, r, t]
|
||||||
), X = he(({ data: { from: p, type: q } }) => {
|
), Y = be(({ data: { from: g, type: T } }) => {
|
||||||
q === "start" && p !== m && O();
|
T === "start" && g !== p && C();
|
||||||
}), P = N(
|
}), S = N(
|
||||||
async (p = !0) => {
|
async (g = !0) => {
|
||||||
await R(b, p), X({ type: "start", from: m });
|
await c(b, g), Y({ type: "start", from: p });
|
||||||
},
|
},
|
||||||
[R, b]
|
[c, b]
|
||||||
), K = E();
|
), U = k();
|
||||||
return F(() => {
|
return _(() => {
|
||||||
!K.current && a && b && (K.current = !0, P());
|
!U.current && a && b && (U.current = !0, S());
|
||||||
}, [P, a, b]), F(() => () => {
|
}, [S, a, b]), _(() => () => {
|
||||||
g.stop();
|
M.stop();
|
||||||
}, [g]), {
|
}, [M]), {
|
||||||
id: m,
|
id: p,
|
||||||
canvasId: f,
|
canvasId: h,
|
||||||
code: b,
|
code: b,
|
||||||
setCode: A,
|
setCode: q,
|
||||||
error: v || M,
|
error: F || P,
|
||||||
schedulerError: v,
|
schedulerError: F,
|
||||||
scheduler: g,
|
scheduler: M,
|
||||||
evalError: M,
|
evalError: P,
|
||||||
evaluate: R,
|
evaluate: c,
|
||||||
activateCode: P,
|
activateCode: S,
|
||||||
activeCode: C,
|
activeCode: x,
|
||||||
isDirty: h,
|
isDirty: H,
|
||||||
pattern: z,
|
pattern: I,
|
||||||
started: x,
|
started: L,
|
||||||
start: J,
|
start: f,
|
||||||
stop: O,
|
stop: C,
|
||||||
pause: Q,
|
pause: O,
|
||||||
togglePlay: async () => {
|
togglePlay: async () => {
|
||||||
x ? g.pause() : await P();
|
L ? M.pause() : await S();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
function be() {
|
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 I({ type: e }) {
|
function $({ type: e }) {
|
||||||
return /* @__PURE__ */ i.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__ */ i.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__ */ i.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__ */ i.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"
|
||||||
})
|
})
|
||||||
}[e]);
|
}[e]);
|
||||||
}
|
}
|
||||||
const we = "_container_3i85k_1", ye = "_header_3i85k_5", Ee = "_buttons_3i85k_9", ke = "_button_3i85k_9", _e = "_buttonDisabled_3i85k_17", Fe = "_error_3i85k_21", Ce = "_body_3i85k_25", w = {
|
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: we,
|
container: ye,
|
||||||
header: ye,
|
header: ke,
|
||||||
buttons: Ee,
|
buttons: _e,
|
||||||
button: ke,
|
button: Fe,
|
||||||
buttonDisabled: _e,
|
buttonDisabled: Ce,
|
||||||
error: Fe,
|
error: Ne,
|
||||||
body: Ce
|
body: xe
|
||||||
}, Ne = () => ne().currentTime;
|
}, Me = () => ce().currentTime;
|
||||||
function Be({ tune: e, hideOutsideView: r = !1, enableKeyboard: t, withCanvas: a = !1, canvasHeight: n = 200 }) {
|
function je({ tune: e, hideOutsideView: r = !1, enableKeyboard: t, withCanvas: a = !1, canvasHeight: o = 200 }) {
|
||||||
const {
|
const {
|
||||||
code: c,
|
code: i,
|
||||||
setCode: l,
|
setCode: u,
|
||||||
evaluate: d,
|
evaluate: m,
|
||||||
activateCode: o,
|
activateCode: n,
|
||||||
error: u,
|
error: d,
|
||||||
isDirty: f,
|
isDirty: v,
|
||||||
activeCode: m,
|
activeCode: h,
|
||||||
pattern: v,
|
pattern: p,
|
||||||
started: k,
|
started: F,
|
||||||
scheduler: M,
|
scheduler: A,
|
||||||
togglePlay: T,
|
togglePlay: P,
|
||||||
stop: b,
|
stop: D,
|
||||||
canvasId: A
|
canvasId: b,
|
||||||
} = ve({
|
id: q
|
||||||
|
} = Ee({
|
||||||
initialCode: e,
|
initialCode: e,
|
||||||
defaultOutput: oe,
|
defaultOutput: ae,
|
||||||
getTime: Ne
|
getTime: Me,
|
||||||
|
editPattern: (c, f) => c.withContext((C) => ({ ...C, id: f }))
|
||||||
});
|
});
|
||||||
pe({
|
ve({
|
||||||
pattern: v,
|
pattern: p,
|
||||||
started: a && k,
|
started: a && F,
|
||||||
getTime: () => M.now(),
|
getTime: () => A.now(),
|
||||||
onDraw: (h, g) => {
|
onDraw: (c, f) => {
|
||||||
const R = document.querySelector("#" + A).getContext("2d");
|
const C = document.querySelector("#" + b).getContext("2d");
|
||||||
re({ ctx: R, time: h, haps: g, autorange: 1, fold: 1, playhead: 1 });
|
oe({ ctx: C, time: c, haps: f, autorange: 1, fold: 1, playhead: 1 });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
const [C, S] = _(), [z, D] = ae({
|
const [x, V] = w(), [I, R] = se({
|
||||||
threshold: 0.01
|
threshold: 0.01
|
||||||
}), x = E(), L = V(() => ((D || !r) && (x.current = !0), D || x.current), [D, r]);
|
}), L = k(), B = K(() => ((R || !r) && (L.current = !0), R || L.current), [R, r]);
|
||||||
return me({
|
pe({
|
||||||
view: C,
|
view: x,
|
||||||
pattern: v,
|
pattern: p,
|
||||||
active: k && !m?.includes("strudel disable-highlighting"),
|
active: F && !h?.includes("strudel disable-highlighting"),
|
||||||
getTime: () => M.getPhase()
|
getTime: () => A.getPhase()
|
||||||
}), U(() => {
|
}), G(() => {
|
||||||
if (t) {
|
if (t) {
|
||||||
const h = async (g) => {
|
const c = async (f) => {
|
||||||
(g.ctrlKey || g.altKey) && (g.code === "Enter" ? (g.preventDefault(), le(C), await o()) : g.code === "Period" && (b(), g.preventDefault()));
|
(f.ctrlKey || f.altKey) && (f.code === "Enter" ? (f.preventDefault(), de(x), await n()) : f.code === "Period" && (D(), f.preventDefault()));
|
||||||
};
|
};
|
||||||
return window.addEventListener("keydown", h, !0), () => window.removeEventListener("keydown", h, !0);
|
return window.addEventListener("keydown", c, !0), () => window.removeEventListener("keydown", c, !0);
|
||||||
}
|
}
|
||||||
}, [t, v, c, d, b, C]), /* @__PURE__ */ i.createElement("div", {
|
}, [t, p, i, m, D, x]);
|
||||||
className: w.container,
|
const [H, M] = w([]);
|
||||||
ref: z
|
return Ae(
|
||||||
}, /* @__PURE__ */ i.createElement("div", {
|
N((c) => {
|
||||||
className: w.header
|
const { data: f } = c.detail;
|
||||||
}, /* @__PURE__ */ i.createElement("div", {
|
f?.hap?.context?.id === q && M((O) => O.concat([c.detail]).slice(-10));
|
||||||
className: w.buttons
|
}, [])
|
||||||
}, /* @__PURE__ */ i.createElement("button", {
|
), /* @__PURE__ */ l.createElement("div", {
|
||||||
className: j(w.button, k ? "sc-animate-pulse" : ""),
|
className: E.container,
|
||||||
onClick: () => T()
|
ref: I
|
||||||
}, /* @__PURE__ */ i.createElement(I, {
|
}, /* @__PURE__ */ l.createElement("div", {
|
||||||
type: k ? "pause" : "play"
|
className: E.header
|
||||||
})), /* @__PURE__ */ i.createElement("button", {
|
}, /* @__PURE__ */ l.createElement("div", {
|
||||||
className: j(f ? w.button : w.buttonDisabled),
|
className: E.buttons
|
||||||
onClick: () => o()
|
}, /* @__PURE__ */ l.createElement("button", {
|
||||||
}, /* @__PURE__ */ i.createElement(I, {
|
className: W(E.button, F ? "sc-animate-pulse" : ""),
|
||||||
|
onClick: () => P()
|
||||||
|
}, /* @__PURE__ */ l.createElement($, {
|
||||||
|
type: F ? "pause" : "play"
|
||||||
|
})), /* @__PURE__ */ l.createElement("button", {
|
||||||
|
className: W(v ? E.button : E.buttonDisabled),
|
||||||
|
onClick: () => n()
|
||||||
|
}, /* @__PURE__ */ l.createElement($, {
|
||||||
type: "refresh"
|
type: "refresh"
|
||||||
}))), u && /* @__PURE__ */ i.createElement("div", {
|
}))), d && /* @__PURE__ */ l.createElement("div", {
|
||||||
className: w.error
|
className: E.error
|
||||||
}, u.message)), /* @__PURE__ */ i.createElement("div", {
|
}, d.message)), /* @__PURE__ */ l.createElement("div", {
|
||||||
className: w.body
|
className: E.body
|
||||||
}, L && /* @__PURE__ */ i.createElement(fe, {
|
}, B && /* @__PURE__ */ l.createElement(ge, {
|
||||||
value: c,
|
value: i,
|
||||||
onChange: l,
|
onChange: u,
|
||||||
onViewChanged: S
|
onViewChanged: V
|
||||||
})), a && /* @__PURE__ */ i.createElement("canvas", {
|
})), a && /* @__PURE__ */ l.createElement("canvas", {
|
||||||
id: A,
|
id: b,
|
||||||
className: "w-full pointer-events-none",
|
className: "w-full pointer-events-none",
|
||||||
height: n,
|
height: o,
|
||||||
ref: (h) => {
|
ref: (c) => {
|
||||||
h && h.width !== h.clientWidth && (h.width = h.clientWidth);
|
c && c.width !== c.clientWidth && (c.width = c.clientWidth);
|
||||||
}
|
}
|
||||||
}));
|
}), !!H.length && /* @__PURE__ */ l.createElement("div", {
|
||||||
|
className: "sc-bg-gray-800 sc-rounded-md sc-p-2"
|
||||||
|
}, H.map(({ message: c }, f) => /* @__PURE__ */ l.createElement("div", {
|
||||||
|
key: f
|
||||||
|
}, c))));
|
||||||
}
|
}
|
||||||
const Oe = (e) => U(() => (window.addEventListener("keydown", e, !0), () => window.removeEventListener("keydown", e, !0)), [e]);
|
function Ae(e) {
|
||||||
|
De(ne.key, e);
|
||||||
|
}
|
||||||
|
function De(e, r, t = !1) {
|
||||||
|
_(() => (document.addEventListener(e, r, t), () => {
|
||||||
|
document.removeEventListener(e, r, t);
|
||||||
|
}), [r]);
|
||||||
|
}
|
||||||
|
const Ue = (e) => G(() => (window.addEventListener("keydown", e, !0), () => window.removeEventListener("keydown", e, !0)), [e]);
|
||||||
export {
|
export {
|
||||||
fe as CodeMirror,
|
ge as CodeMirror,
|
||||||
Be as MiniRepl,
|
je as MiniRepl,
|
||||||
j as cx,
|
W as cx,
|
||||||
le as flash,
|
de as flash,
|
||||||
me as useHighlighting,
|
pe as useHighlighting,
|
||||||
Oe as useKeydown,
|
Ue as useKeydown,
|
||||||
he as usePostMessage,
|
be as usePostMessage,
|
||||||
ve as useStrudel
|
Ee as useStrudel
|
||||||
};
|
};
|
||||||
|
|||||||
2
packages/react/dist/style.css
vendored
2
packages/react/dist/style.css
vendored
@ -1 +1 @@
|
|||||||
.cm-editor{background-color:transparent!important;height:100%;z-index:11;font-size:18px}.cm-theme-light{width:100%}.cm-line>*{background:#00000095}*,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }.sc-h-5{height:1.25rem}.sc-w-5{width:1.25rem}@keyframes sc-pulse{50%{opacity:.5}}.sc-animate-pulse{animation:sc-pulse 2s cubic-bezier(.4,0,.6,1) infinite}._container_3i85k_1{overflow:hidden;border-radius:.375rem;--tw-bg-opacity: 1;background-color:rgb(34 34 34 / var(--tw-bg-opacity))}._header_3i85k_5{display:flex;justify-content:space-between;border-top-width:1px;--tw-border-opacity: 1;border-color:rgb(100 116 139 / var(--tw-border-opacity));--tw-bg-opacity: 1;background-color:rgb(51 65 85 / var(--tw-bg-opacity))}._buttons_3i85k_9{display:flex}._button_3i85k_9{display:flex;width:4rem;cursor:pointer;align-items:center;justify-content:center;border-right-width:1px;--tw-border-opacity: 1;border-color:rgb(100 116 139 / var(--tw-border-opacity));--tw-bg-opacity: 1;background-color:rgb(51 65 85 / var(--tw-bg-opacity));padding:.25rem;--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity))}._button_3i85k_9:hover{--tw-bg-opacity: 1;background-color:rgb(71 85 105 / var(--tw-bg-opacity))}._buttonDisabled_3i85k_17{display:flex;width:4rem;cursor:pointer;cursor:not-allowed;align-items:center;justify-content:center;--tw-bg-opacity: 1;background-color:rgb(71 85 105 / var(--tw-bg-opacity));padding:.25rem;--tw-text-opacity: 1;color:rgb(148 163 184 / var(--tw-text-opacity))}._error_3i85k_21{padding:.25rem;text-align:right;font-size:.875rem;line-height:1.25rem;--tw-text-opacity: 1;color:rgb(254 202 202 / var(--tw-text-opacity))}._body_3i85k_25{position:relative;overflow:auto}
|
.cm-editor{background-color:transparent!important;height:100%;z-index:11;font-size:18px}.cm-theme-light{width:100%}.cm-line>*{background:#00000095}*,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }.sc-h-5{height:1.25rem}.sc-w-5{width:1.25rem}@keyframes sc-pulse{50%{opacity:.5}}.sc-animate-pulse{animation:sc-pulse 2s cubic-bezier(.4,0,.6,1) infinite}.sc-rounded-md{border-radius:.375rem}.sc-bg-gray-800{--tw-bg-opacity: 1;background-color:rgb(31 41 55 / var(--tw-bg-opacity))}.sc-p-2{padding:.5rem}._container_3i85k_1{overflow:hidden;border-radius:.375rem;--tw-bg-opacity: 1;background-color:rgb(34 34 34 / var(--tw-bg-opacity))}._header_3i85k_5{display:flex;justify-content:space-between;border-top-width:1px;--tw-border-opacity: 1;border-color:rgb(100 116 139 / var(--tw-border-opacity));--tw-bg-opacity: 1;background-color:rgb(51 65 85 / var(--tw-bg-opacity))}._buttons_3i85k_9{display:flex}._button_3i85k_9{display:flex;width:4rem;cursor:pointer;align-items:center;justify-content:center;border-right-width:1px;--tw-border-opacity: 1;border-color:rgb(100 116 139 / var(--tw-border-opacity));--tw-bg-opacity: 1;background-color:rgb(51 65 85 / var(--tw-bg-opacity));padding:.25rem;--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity))}._button_3i85k_9:hover{--tw-bg-opacity: 1;background-color:rgb(71 85 105 / var(--tw-bg-opacity))}._buttonDisabled_3i85k_17{display:flex;width:4rem;cursor:pointer;cursor:not-allowed;align-items:center;justify-content:center;--tw-bg-opacity: 1;background-color:rgb(71 85 105 / var(--tw-bg-opacity));padding:.25rem;--tw-text-opacity: 1;color:rgb(148 163 184 / var(--tw-text-opacity))}._error_3i85k_21{padding:.25rem;text-align:right;font-size:.875rem;line-height:1.25rem;--tw-text-opacity: 1;color:rgb(254 202 202 / var(--tw-text-opacity))}._body_3i85k_25{position:relative;overflow:auto}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { pianoroll } from '@strudel.cycles/core';
|
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 } 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';
|
||||||
@ -11,6 +11,7 @@ import CodeMirror6, { flash } from './CodeMirror6';
|
|||||||
import { Icon } from './Icon';
|
import { Icon } from './Icon';
|
||||||
import styles from './MiniRepl.module.css';
|
import styles from './MiniRepl.module.css';
|
||||||
import './style.css';
|
import './style.css';
|
||||||
|
import { logger } from '@strudel.cycles/core';
|
||||||
|
|
||||||
const getTime = () => getAudioContext().currentTime;
|
const getTime = () => getAudioContext().currentTime;
|
||||||
|
|
||||||
@ -29,10 +30,14 @@ export function MiniRepl({ tune, hideOutsideView = false, enableKeyboard, withCa
|
|||||||
togglePlay,
|
togglePlay,
|
||||||
stop,
|
stop,
|
||||||
canvasId,
|
canvasId,
|
||||||
|
id: replId,
|
||||||
} = useStrudel({
|
} = useStrudel({
|
||||||
initialCode: tune,
|
initialCode: tune,
|
||||||
defaultOutput: webaudioOutput,
|
defaultOutput: webaudioOutput,
|
||||||
getTime,
|
getTime,
|
||||||
|
editPattern: (pat, id) => {
|
||||||
|
return pat.withContext((ctx) => ({ ...ctx, id }));
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
usePatternFrame({
|
usePatternFrame({
|
||||||
@ -86,6 +91,20 @@ export function MiniRepl({ tune, hideOutsideView = false, enableKeyboard, withCa
|
|||||||
}
|
}
|
||||||
}, [enableKeyboard, pattern, code, evaluate, stop, view]);
|
}, [enableKeyboard, pattern, code, evaluate, stop, view]);
|
||||||
|
|
||||||
|
const [log, setLog] = useState([]);
|
||||||
|
useLogger(
|
||||||
|
useCallback((e) => {
|
||||||
|
const { data } = e.detail;
|
||||||
|
const logId = data?.hap?.context?.id;
|
||||||
|
// const logId = data?.pattern?.meta?.id;
|
||||||
|
if (logId === replId) {
|
||||||
|
setLog((l) => {
|
||||||
|
return l.concat([e.detail]).slice(-10);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, []),
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.container} ref={ref}>
|
<div className={styles.container} ref={ref}>
|
||||||
<div className={styles.header}>
|
<div className={styles.header}>
|
||||||
@ -114,6 +133,28 @@ export function MiniRepl({ tune, hideOutsideView = false, enableKeyboard, withCa
|
|||||||
}}
|
}}
|
||||||
></canvas>
|
></canvas>
|
||||||
)}
|
)}
|
||||||
|
{!!log.length && (
|
||||||
|
<div className="sc-bg-gray-800 sc-rounded-md sc-p-2">
|
||||||
|
{log.map(({ message }, i) => (
|
||||||
|
<div key={i}>{message}</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: dedupe
|
||||||
|
function useLogger(onTrigger) {
|
||||||
|
useEvent(logger.key, onTrigger);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: dedupe
|
||||||
|
function useEvent(name, onTrigger, useCapture = false) {
|
||||||
|
useEffect(() => {
|
||||||
|
document.addEventListener(name, onTrigger, useCapture);
|
||||||
|
return () => {
|
||||||
|
document.removeEventListener(name, onTrigger, useCapture);
|
||||||
|
};
|
||||||
|
}, [onTrigger]);
|
||||||
|
}
|
||||||
|
|||||||
@ -12,6 +12,7 @@ function useStrudel({
|
|||||||
autolink = false,
|
autolink = false,
|
||||||
beforeEval,
|
beforeEval,
|
||||||
afterEval,
|
afterEval,
|
||||||
|
editPattern,
|
||||||
onEvalError,
|
onEvalError,
|
||||||
onToggle,
|
onToggle,
|
||||||
canvasId,
|
canvasId,
|
||||||
@ -44,6 +45,7 @@ function useStrudel({
|
|||||||
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);
|
||||||
|
|||||||
@ -195,9 +195,14 @@ export const webaudioOutput = async (hap, deadline, hapDuration) => {
|
|||||||
hap.value = { note: hap.value };
|
hap.value = { note: hap.value };
|
||||||
} */
|
} */
|
||||||
if (typeof hap.value !== 'object') {
|
if (typeof hap.value !== 'object') {
|
||||||
throw new Error(
|
logger(
|
||||||
`hap.value ${hap.value} is not supported by webaudio output. Hint: append .note() or .s() to the end`,
|
`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)
|
// calculate correct time (tone.js workaround)
|
||||||
let t = ac.currentTime + deadline;
|
let t = ac.currentTime + deadline;
|
||||||
|
|||||||
@ -899,14 +899,14 @@ exports[`runs examples > example "cat" example index 0 1`] = `
|
|||||||
[
|
[
|
||||||
"[ 0/1 → 1/2 | s:hh ]",
|
"[ 0/1 → 1/2 | s:hh ]",
|
||||||
"[ 1/2 → 1/1 | s:hh ]",
|
"[ 1/2 → 1/1 | s:hh ]",
|
||||||
"[ 1/1 → 9/8 | n:c2 ]",
|
"[ 1/1 → 9/8 | note:c2 ]",
|
||||||
"[ 11/8 → 3/2 | n:c2 ]",
|
"[ 11/8 → 3/2 | note:c2 ]",
|
||||||
"[ 7/4 → 15/8 | n:c2 ]",
|
"[ 7/4 → 15/8 | note:c2 ]",
|
||||||
"[ 2/1 → 5/2 | s:hh ]",
|
"[ 2/1 → 5/2 | s:hh ]",
|
||||||
"[ 5/2 → 3/1 | s:hh ]",
|
"[ 5/2 → 3/1 | s:hh ]",
|
||||||
"[ 3/1 → 25/8 | n:c2 ]",
|
"[ 3/1 → 25/8 | note:c2 ]",
|
||||||
"[ 27/8 → 7/2 | n:c2 ]",
|
"[ 27/8 → 7/2 | note:c2 ]",
|
||||||
"[ 15/4 → 31/8 | n:c2 ]",
|
"[ 15/4 → 31/8 | note:c2 ]",
|
||||||
]
|
]
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -920,6 +920,27 @@ exports[`runs examples > example "cat" example index 0 2`] = `
|
|||||||
]
|
]
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`runs examples > example "ceil" example index 0 1`] = `
|
||||||
|
[
|
||||||
|
"[ 0/1 → 1/4 | note:42 ]",
|
||||||
|
"[ 1/4 → 1/2 | note:43 ]",
|
||||||
|
"[ 1/2 → 3/4 | note:43 ]",
|
||||||
|
"[ 3/4 → 1/1 | note:43 ]",
|
||||||
|
"[ 1/1 → 5/4 | note:42 ]",
|
||||||
|
"[ 5/4 → 3/2 | note:43 ]",
|
||||||
|
"[ 3/2 → 7/4 | note:43 ]",
|
||||||
|
"[ 7/4 → 2/1 | note:43 ]",
|
||||||
|
"[ 2/1 → 9/4 | note:42 ]",
|
||||||
|
"[ 9/4 → 5/2 | note:43 ]",
|
||||||
|
"[ 5/2 → 11/4 | note:43 ]",
|
||||||
|
"[ 11/4 → 3/1 | note:43 ]",
|
||||||
|
"[ 3/1 → 13/4 | note:42 ]",
|
||||||
|
"[ 13/4 → 7/2 | note:43 ]",
|
||||||
|
"[ 7/2 → 15/4 | note:43 ]",
|
||||||
|
"[ 15/4 → 4/1 | note:43 ]",
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`runs examples > example "chooseCycles" example index 0 1`] = `
|
exports[`runs examples > example "chooseCycles" example index 0 1`] = `
|
||||||
[
|
[
|
||||||
"[ 0/1 → 1/4 | s:bd ]",
|
"[ 0/1 → 1/4 | s:bd ]",
|
||||||
@ -1680,6 +1701,27 @@ exports[`runs examples > example "firstOf" example index 0 1`] = `
|
|||||||
]
|
]
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`runs examples > example "floor" example index 0 1`] = `
|
||||||
|
[
|
||||||
|
"[ 0/1 → 1/4 | note:42 ]",
|
||||||
|
"[ 1/4 → 1/2 | note:42 ]",
|
||||||
|
"[ 1/2 → 3/4 | note:42 ]",
|
||||||
|
"[ 3/4 → 1/1 | note:43 ]",
|
||||||
|
"[ 1/1 → 5/4 | note:42 ]",
|
||||||
|
"[ 5/4 → 3/2 | note:42 ]",
|
||||||
|
"[ 3/2 → 7/4 | note:42 ]",
|
||||||
|
"[ 7/4 → 2/1 | note:43 ]",
|
||||||
|
"[ 2/1 → 9/4 | note:42 ]",
|
||||||
|
"[ 9/4 → 5/2 | note:42 ]",
|
||||||
|
"[ 5/2 → 11/4 | note:42 ]",
|
||||||
|
"[ 11/4 → 3/1 | note:43 ]",
|
||||||
|
"[ 3/1 → 13/4 | note:42 ]",
|
||||||
|
"[ 13/4 → 7/2 | note:42 ]",
|
||||||
|
"[ 7/2 → 15/4 | note:42 ]",
|
||||||
|
"[ 15/4 → 4/1 | note:43 ]",
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
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 ]",
|
||||||
@ -2312,6 +2354,52 @@ exports[`runs examples > example "perlin" example index 0 1`] = `
|
|||||||
]
|
]
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`runs examples > example "polymeter" example index 0 1`] = `
|
||||||
|
[
|
||||||
|
"[ 0/1 → 1/3 | note:c ]",
|
||||||
|
"[ 1/3 → 2/3 | note:eb ]",
|
||||||
|
"[ 2/3 → 1/1 | note:g ]",
|
||||||
|
"[ 1/1 → 4/3 | note:c ]",
|
||||||
|
"[ 4/3 → 5/3 | note:eb ]",
|
||||||
|
"[ 5/3 → 2/1 | note:g ]",
|
||||||
|
"[ 2/1 → 7/3 | note:c ]",
|
||||||
|
"[ 7/3 → 8/3 | note:eb ]",
|
||||||
|
"[ 8/3 → 3/1 | note:g ]",
|
||||||
|
"[ 3/1 → 10/3 | note:c ]",
|
||||||
|
"[ 10/3 → 11/3 | note:eb ]",
|
||||||
|
"[ 11/3 → 4/1 | note:g ]",
|
||||||
|
"[ 0/1 → 1/3 | note:c2 ]",
|
||||||
|
"[ 1/3 → 2/3 | note:g2 ]",
|
||||||
|
"[ 2/3 → 1/1 | note:c2 ]",
|
||||||
|
"[ 1/1 → 4/3 | note:g2 ]",
|
||||||
|
"[ 4/3 → 5/3 | note:c2 ]",
|
||||||
|
"[ 5/3 → 2/1 | note:g2 ]",
|
||||||
|
"[ 2/1 → 7/3 | note:c2 ]",
|
||||||
|
"[ 7/3 → 8/3 | note:g2 ]",
|
||||||
|
"[ 8/3 → 3/1 | note:c2 ]",
|
||||||
|
"[ 3/1 → 10/3 | note:g2 ]",
|
||||||
|
"[ 10/3 → 11/3 | note:c2 ]",
|
||||||
|
"[ 11/3 → 4/1 | note:g2 ]",
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`runs examples > example "polymeterSteps" example index 0 1`] = `
|
||||||
|
[
|
||||||
|
"[ 0/1 → 1/2 | note:c ]",
|
||||||
|
"[ 1/2 → 1/1 | note:d ]",
|
||||||
|
"[ 1/1 → 3/2 | note:e ]",
|
||||||
|
"[ 3/2 → 2/1 | note:f ]",
|
||||||
|
"[ 2/1 → 5/2 | note:g ]",
|
||||||
|
"[ 5/2 → 3/1 | note:f ]",
|
||||||
|
"[ 3/1 → 7/2 | note:e ]",
|
||||||
|
"[ 7/2 → 4/1 | note:d ]",
|
||||||
|
"[ 0/1 → 1/1 | s:bd ]",
|
||||||
|
"[ 1/1 → 2/1 | s:bd ]",
|
||||||
|
"[ 2/1 → 3/1 | s:bd ]",
|
||||||
|
"[ 3/1 → 4/1 | s:bd ]",
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`runs examples > example "pure" example index 0 1`] = `
|
exports[`runs examples > example "pure" example index 0 1`] = `
|
||||||
[
|
[
|
||||||
"[ 0/1 → 1/1 | e4 ]",
|
"[ 0/1 → 1/1 | e4 ]",
|
||||||
@ -2662,24 +2750,24 @@ exports[`runs examples > example "seq" example index 0 1`] = `
|
|||||||
[
|
[
|
||||||
"[ 0/1 → 1/4 | s:hh ]",
|
"[ 0/1 → 1/4 | s:hh ]",
|
||||||
"[ 1/4 → 1/2 | s:hh ]",
|
"[ 1/4 → 1/2 | s:hh ]",
|
||||||
"[ 1/2 → 9/16 | n:c2 ]",
|
"[ 1/2 → 9/16 | note:c2 ]",
|
||||||
"[ 11/16 → 3/4 | n:c2 ]",
|
"[ 11/16 → 3/4 | note:c2 ]",
|
||||||
"[ 7/8 → 15/16 | n:c2 ]",
|
"[ 7/8 → 15/16 | note:c2 ]",
|
||||||
"[ 1/1 → 5/4 | s:hh ]",
|
"[ 1/1 → 5/4 | s:hh ]",
|
||||||
"[ 5/4 → 3/2 | s:hh ]",
|
"[ 5/4 → 3/2 | s:hh ]",
|
||||||
"[ 3/2 → 25/16 | n:c2 ]",
|
"[ 3/2 → 25/16 | note:c2 ]",
|
||||||
"[ 27/16 → 7/4 | n:c2 ]",
|
"[ 27/16 → 7/4 | note:c2 ]",
|
||||||
"[ 15/8 → 31/16 | n:c2 ]",
|
"[ 15/8 → 31/16 | note:c2 ]",
|
||||||
"[ 2/1 → 9/4 | s:hh ]",
|
"[ 2/1 → 9/4 | s:hh ]",
|
||||||
"[ 9/4 → 5/2 | s:hh ]",
|
"[ 9/4 → 5/2 | s:hh ]",
|
||||||
"[ 5/2 → 41/16 | n:c2 ]",
|
"[ 5/2 → 41/16 | note:c2 ]",
|
||||||
"[ 43/16 → 11/4 | n:c2 ]",
|
"[ 43/16 → 11/4 | note:c2 ]",
|
||||||
"[ 23/8 → 47/16 | n:c2 ]",
|
"[ 23/8 → 47/16 | note:c2 ]",
|
||||||
"[ 3/1 → 13/4 | s:hh ]",
|
"[ 3/1 → 13/4 | s:hh ]",
|
||||||
"[ 13/4 → 7/2 | s:hh ]",
|
"[ 13/4 → 7/2 | s:hh ]",
|
||||||
"[ 7/2 → 57/16 | n:c2 ]",
|
"[ 7/2 → 57/16 | note:c2 ]",
|
||||||
"[ 59/16 → 15/4 | n:c2 ]",
|
"[ 59/16 → 15/4 | note:c2 ]",
|
||||||
"[ 31/8 → 63/16 | n:c2 ]",
|
"[ 31/8 → 63/16 | note:c2 ]",
|
||||||
]
|
]
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -2733,6 +2821,8 @@ exports[`runs examples > example "shape" example index 0 1`] = `
|
|||||||
]
|
]
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`runs examples > example "silence" example index 0 1`] = `[]`;
|
||||||
|
|
||||||
exports[`runs examples > example "sine" example index 0 1`] = `
|
exports[`runs examples > example "sine" example index 0 1`] = `
|
||||||
[
|
[
|
||||||
"[ 0/1 → 1/8 | note:Eb4 ]",
|
"[ 0/1 → 1/8 | note:Eb4 ]",
|
||||||
@ -2956,18 +3046,18 @@ exports[`runs examples > example "stack" example index 0 1`] = `
|
|||||||
"[ 5/2 → 3/1 | s:hh ]",
|
"[ 5/2 → 3/1 | s:hh ]",
|
||||||
"[ 3/1 → 7/2 | s:hh ]",
|
"[ 3/1 → 7/2 | s:hh ]",
|
||||||
"[ 7/2 → 4/1 | s:hh ]",
|
"[ 7/2 → 4/1 | s:hh ]",
|
||||||
"[ 0/1 → 1/8 | n:c2 ]",
|
"[ 0/1 → 1/8 | note:c2 ]",
|
||||||
"[ 3/8 → 1/2 | n:c2 ]",
|
"[ 3/8 → 1/2 | note:c2 ]",
|
||||||
"[ 3/4 → 7/8 | n:c2 ]",
|
"[ 3/4 → 7/8 | note:c2 ]",
|
||||||
"[ 1/1 → 9/8 | n:c2 ]",
|
"[ 1/1 → 9/8 | note:c2 ]",
|
||||||
"[ 11/8 → 3/2 | n:c2 ]",
|
"[ 11/8 → 3/2 | note:c2 ]",
|
||||||
"[ 7/4 → 15/8 | n:c2 ]",
|
"[ 7/4 → 15/8 | note:c2 ]",
|
||||||
"[ 2/1 → 17/8 | n:c2 ]",
|
"[ 2/1 → 17/8 | note:c2 ]",
|
||||||
"[ 19/8 → 5/2 | n:c2 ]",
|
"[ 19/8 → 5/2 | note:c2 ]",
|
||||||
"[ 11/4 → 23/8 | n:c2 ]",
|
"[ 11/4 → 23/8 | note:c2 ]",
|
||||||
"[ 3/1 → 25/8 | n:c2 ]",
|
"[ 3/1 → 25/8 | note:c2 ]",
|
||||||
"[ 27/8 → 7/2 | n:c2 ]",
|
"[ 27/8 → 7/2 | note:c2 ]",
|
||||||
"[ 15/4 → 31/8 | n:c2 ]",
|
"[ 15/4 → 31/8 | note:c2 ]",
|
||||||
]
|
]
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
|||||||
@ -49,19 +49,29 @@ export const SIDEBAR: Sidebar = {
|
|||||||
{ text: 'Sounds', link: 'learn/sounds' },
|
{ text: 'Sounds', link: 'learn/sounds' },
|
||||||
{ text: 'Coding syntax', link: 'learn/code' },
|
{ text: 'Coding syntax', link: 'learn/code' },
|
||||||
{ text: 'Mini-Notation', link: 'learn/mini-notation' },
|
{ text: 'Mini-Notation', link: 'learn/mini-notation' },
|
||||||
|
],
|
||||||
|
'Making Sound': [
|
||||||
{ text: 'Samples', link: 'learn/samples' },
|
{ text: 'Samples', link: 'learn/samples' },
|
||||||
{ text: 'Synths', link: 'learn/synths' },
|
{ text: 'Synths', link: 'learn/synths' },
|
||||||
{ text: 'Audio Effects', link: 'learn/effects' },
|
{ text: 'Audio Effects', link: 'learn/effects' },
|
||||||
{ text: 'Functions', link: 'learn/functions' },
|
],
|
||||||
|
'Pattern Functions': [
|
||||||
|
{ text: 'Introduction', link: 'functions/intro' },
|
||||||
|
{ text: 'Pattern Constructors', link: 'learn/factories' },
|
||||||
|
{ text: 'Time Modifiers', link: 'learn/time-modifiers' },
|
||||||
|
{ text: 'Control Parameters', link: 'functions/value-modifiers' },
|
||||||
{ text: 'Signals', link: 'learn/signals' },
|
{ text: 'Signals', link: 'learn/signals' },
|
||||||
{ text: 'Tonal', link: 'learn/tonal' },
|
{ text: 'Conditional Modifiers', link: 'learn/conditional-modifiers' },
|
||||||
|
{ text: 'Tonal Modifiers', link: 'learn/tonal' },
|
||||||
|
],
|
||||||
|
More: [
|
||||||
|
{ text: 'Patterns', link: 'technical-manual/patterns' },
|
||||||
|
{ text: 'Pattern Alignment', link: 'technical-manual/alignment' },
|
||||||
{ text: 'MIDI & OSC', link: 'learn/input-output' },
|
{ text: 'MIDI & OSC', link: 'learn/input-output' },
|
||||||
{ text: 'Strudel vs Tidal', link: 'learn/strudel-vs-tidal' },
|
{ text: 'Strudel vs Tidal', link: 'learn/strudel-vs-tidal' },
|
||||||
],
|
],
|
||||||
'Technical Manual': [
|
Development: [
|
||||||
{ text: 'Patterns', link: 'technical-manual/patterns' },
|
|
||||||
{ text: 'REPL', link: 'technical-manual/repl' },
|
{ text: 'REPL', link: 'technical-manual/repl' },
|
||||||
{ text: 'Pattern Alignment', link: 'technical-manual/alignment' },
|
|
||||||
{ text: 'Docs', link: 'technical-manual/docs' },
|
{ text: 'Docs', link: 'technical-manual/docs' },
|
||||||
{ text: 'Testing', link: 'technical-manual/testing' },
|
{ text: 'Testing', link: 'technical-manual/testing' },
|
||||||
],
|
],
|
||||||
|
|||||||
@ -2,7 +2,7 @@ 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';
|
||||||
|
|
||||||
export function JsDoc({ name, h = 3 }) {
|
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);
|
||||||
@ -16,7 +16,7 @@ export function JsDoc({ name, h = 3 }) {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{!!h && <CustomHeading>{item.longname}</CustomHeading>}
|
{!!h && <CustomHeading>{item.longname}</CustomHeading>}
|
||||||
<div dangerouslySetInnerHTML={{ __html: description }} />
|
{!hideDescription && <div dangerouslySetInnerHTML={{ __html: description }} />}
|
||||||
<ul>
|
<ul>
|
||||||
{item.params?.map((param, i) => (
|
{item.params?.map((param, i) => (
|
||||||
<li key={i}>
|
<li key={i}>
|
||||||
|
|||||||
73
website/src/pages/functions/intro.mdx
Normal file
73
website/src/pages/functions/intro.mdx
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
---
|
||||||
|
title: Introduction
|
||||||
|
layout: ../../layouts/MainLayout.astro
|
||||||
|
---
|
||||||
|
|
||||||
|
import { MiniRepl } from '../../docs/MiniRepl';
|
||||||
|
import { JsDoc } from '../../docs/JsDoc';
|
||||||
|
|
||||||
|
# Functional JavaScript API
|
||||||
|
|
||||||
|
While the mini notation is powerful on its own, there is much more to discover.
|
||||||
|
Internally, the mini notation will expand to use the actual functional JavaScript API.
|
||||||
|
|
||||||
|
For example, this Pattern in Mini Notation:
|
||||||
|
|
||||||
|
<MiniRepl client:only="react" tune={`note("c3 eb3 g3")`} />
|
||||||
|
|
||||||
|
is equivalent to this Pattern without Mini Notation:
|
||||||
|
|
||||||
|
<MiniRepl client:only="react" tune={`note(seq(c3, eb3, g3))`} />
|
||||||
|
|
||||||
|
Similarly, there is an equivalent function for every aspect of the mini notation.
|
||||||
|
|
||||||
|
Which representation to use is a matter of context. As a rule of thumb, you can think of the JavaScript API
|
||||||
|
to fit better for the larger context, while mini notation is more practical for individiual rhythms.
|
||||||
|
|
||||||
|
## Limits of Mini Notation
|
||||||
|
|
||||||
|
While the Mini Notation is a powerful way to write rhythms shortly, it also has its limits. Take this example:
|
||||||
|
|
||||||
|
<MiniRepl
|
||||||
|
client:idle
|
||||||
|
tune={`stack(
|
||||||
|
note("c2 eb2(3,8)").s('sawtooth').cutoff(800),
|
||||||
|
s("bd,~ sd,hh*4")
|
||||||
|
)`}
|
||||||
|
/>
|
||||||
|
|
||||||
|
Here, we are using mini notation for the individual rhythms, while using the function `stack` to mix them.
|
||||||
|
While stack is also available as `,` in mini notation, we cannot use it here, because we have different types of sounds.
|
||||||
|
|
||||||
|
## Combining Patterns
|
||||||
|
|
||||||
|
You can freely mix JS patterns, mini patterns and values! For example, this pattern:
|
||||||
|
|
||||||
|
<MiniRepl
|
||||||
|
client:idle
|
||||||
|
tune={`cat(
|
||||||
|
stack(g3,b3,e4),
|
||||||
|
stack(a3,c3,e4),
|
||||||
|
stack(b3,d3,fs4),
|
||||||
|
stack(b3,e4,g4)
|
||||||
|
).note()`}
|
||||||
|
/>
|
||||||
|
|
||||||
|
...is equivalent to:
|
||||||
|
|
||||||
|
<MiniRepl
|
||||||
|
client:idle
|
||||||
|
tune={`cat(
|
||||||
|
"g3,b3,e4",
|
||||||
|
"a3,c3,e4",
|
||||||
|
"b3,d3,f#4",
|
||||||
|
"b3,e4,g4"
|
||||||
|
).note()`}
|
||||||
|
/>
|
||||||
|
|
||||||
|
... as well as:
|
||||||
|
|
||||||
|
<MiniRepl client:only="react" tune={`note("<[g3,b3,e4] [a3,c3,e4] [b3,d3,f#4] [b3,e4,g4]>")`} />
|
||||||
|
|
||||||
|
While mini notation is almost always shorter, it only has a handful of modifiers: \* / ! @.
|
||||||
|
When using JS patterns, there is a lot more you can do.
|
||||||
154
website/src/pages/functions/value-modifiers.mdx
Normal file
154
website/src/pages/functions/value-modifiers.mdx
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
---
|
||||||
|
title: Control Parameters
|
||||||
|
layout: ../../layouts/MainLayout.astro
|
||||||
|
---
|
||||||
|
|
||||||
|
import { MiniRepl } from '../../docs/MiniRepl';
|
||||||
|
import { JsDoc } from '../../docs/JsDoc';
|
||||||
|
|
||||||
|
# Control Parameters
|
||||||
|
|
||||||
|
Besides functions that control time, we saw earlier that functions like `note` and `cutoff` control different parameters (short params) of an event.
|
||||||
|
Let's now look more closely at how these `param(eter) functions` work.
|
||||||
|
|
||||||
|
# Parameter Functions
|
||||||
|
|
||||||
|
A very powerful feature of tidal patterns is that each parameter can be controlled independently:
|
||||||
|
|
||||||
|
<MiniRepl
|
||||||
|
client:only="react"
|
||||||
|
tune={`note("c a f e")
|
||||||
|
.cutoff("<500 1000 2000 [4000 8000]>")
|
||||||
|
.gain(.8)
|
||||||
|
.s('sawtooth')
|
||||||
|
.log()`}
|
||||||
|
/>
|
||||||
|
|
||||||
|
In this example, the parameters `note`, `cutoff`, `gain` and `s` are controlled independently by either patterns or plain values (numbers / text).
|
||||||
|
After pressing play, we can observe the time and parameter values of each event (hap) in the output created by `.log()`.
|
||||||
|
|
||||||
|
## Plain vs Parameterized Values
|
||||||
|
|
||||||
|
Patterns that are not wrapped inside a param function will contain unlabeled `plain values`:
|
||||||
|
|
||||||
|
<MiniRepl client:only="react" tune={`"<c e g>".log()`} />
|
||||||
|
|
||||||
|
This will not generate any sound output, because Strudel could only guess which param is meant by these letters.
|
||||||
|
|
||||||
|
Now compare that to the version wrapped in `note`:
|
||||||
|
|
||||||
|
<MiniRepl client:only="react" tune={`note("<c e g>").log()`} />
|
||||||
|
|
||||||
|
Now it is clear that these letters are meant to be played as notes.
|
||||||
|
Under the hood, the `note` function (as well as all other param functions)
|
||||||
|
will wrap each plain value in an object. If the note function did not exist, we would need to write:
|
||||||
|
|
||||||
|
<MiniRepl client:only="react" tune={`cat({note:'c'},{note:'e'},{note:'g'}).log()`} />
|
||||||
|
|
||||||
|
This will have the same output, though it is rather unwieldy to read and write.
|
||||||
|
|
||||||
|
## Wrapping Parameter Functions
|
||||||
|
|
||||||
|
To avoid too much nesting, param functions can also be chained like this:
|
||||||
|
|
||||||
|
<MiniRepl client:only="react" tune={`cat('c', 'e', 'g').note().log()`} />
|
||||||
|
|
||||||
|
This is equivalent to `note(cat('c','e','g')).log()`.
|
||||||
|
|
||||||
|
You can use this with any function that declares a type (like `n`, `s`, `note`, `freq` etc), just make sure to leave the parens empty!
|
||||||
|
|
||||||
|
## Plain Value Modification
|
||||||
|
|
||||||
|
Patterns of plain values can be modified with any of the following operators:
|
||||||
|
|
||||||
|
<MiniRepl client:only="react" tune={`"50 60 70".add("<0 1 2>").log()`} />
|
||||||
|
|
||||||
|
Here, the add function modifies the numbers on the left.
|
||||||
|
Again, there is no output because these numbers have no meaning without a param.
|
||||||
|
|
||||||
|
## Param Value Modification
|
||||||
|
|
||||||
|
To modify a parameter value, you can either:
|
||||||
|
|
||||||
|
- Use the operator on the plain value pattern, inside the param function:
|
||||||
|
|
||||||
|
<MiniRepl client:only="react" tune={`note("50 60 70".add("<0 1 2>")).room(.1).log()`} />
|
||||||
|
|
||||||
|
- Similarly, use the operator on the plain value pattern and wrap it later:
|
||||||
|
|
||||||
|
<MiniRepl client:only="react" tune={`"50 60 70".add("<0 1 2>").note().room(.1).log()`} />
|
||||||
|
|
||||||
|
- Specify which param should be modified inside the operator function:
|
||||||
|
|
||||||
|
<MiniRepl client:only="react" tune={`note("50 60 70").room(.1).add(note("<0 1 2>")).log()`} />
|
||||||
|
|
||||||
|
- Modify _all_ numeral params:
|
||||||
|
|
||||||
|
<MiniRepl client:only="react" tune={`note("50 60 70").room(.1).add("<0 1 2>").log()`} />
|
||||||
|
|
||||||
|
Which of these 3 ways to use strongly depends on the context!
|
||||||
|
Note that the order of chaining param functions also matters!
|
||||||
|
In the last example, the `room` value would not have changed if it was applied later:
|
||||||
|
|
||||||
|
<MiniRepl client:only="react" tune={`note("50 60 70").add("<0 1 2>").room(.1).log()`} />
|
||||||
|
|
||||||
|
This shows how the execution of the chained functions goes from left to right.
|
||||||
|
In this case, the `.add` will only modify what's on the left side.
|
||||||
|
|
||||||
|
# Operators
|
||||||
|
|
||||||
|
This group of functions allows to modify the value of events.
|
||||||
|
|
||||||
|
## add
|
||||||
|
|
||||||
|
<JsDoc client:idle name="Pattern.add" h={0} />
|
||||||
|
|
||||||
|
## sub
|
||||||
|
|
||||||
|
<JsDoc client:idle name="Pattern.sub" h={0} />
|
||||||
|
|
||||||
|
## mul
|
||||||
|
|
||||||
|
<JsDoc client:idle name="Pattern.mul" h={0} />
|
||||||
|
|
||||||
|
## div
|
||||||
|
|
||||||
|
<JsDoc client:idle name="Pattern.div" h={0} />
|
||||||
|
|
||||||
|
## round
|
||||||
|
|
||||||
|
<JsDoc client:idle name="Pattern.round" h={0} />
|
||||||
|
|
||||||
|
## floor
|
||||||
|
|
||||||
|
<JsDoc client:idle name="Pattern.floor" h={0} />
|
||||||
|
|
||||||
|
## ceil
|
||||||
|
|
||||||
|
<JsDoc client:idle name="Pattern.ceil" h={0} />
|
||||||
|
|
||||||
|
## range
|
||||||
|
|
||||||
|
<JsDoc client:idle name="Pattern.range" h={0} />
|
||||||
|
|
||||||
|
# Custom Parameters
|
||||||
|
|
||||||
|
You can also create your own parameters:
|
||||||
|
|
||||||
|
<MiniRepl
|
||||||
|
client:only="react"
|
||||||
|
tune={`let x = createParam('x')
|
||||||
|
x(sine.range(0, 200))
|
||||||
|
`}
|
||||||
|
/>
|
||||||
|
|
||||||
|
Multiple params can also be created in a more consice way, using `createParams`:
|
||||||
|
|
||||||
|
<MiniRepl
|
||||||
|
client:only="react"
|
||||||
|
tune={`let { x, y } = createParams('x', 'y');
|
||||||
|
x(sine.range(0, 200)).y(cosine.range(0, 200));
|
||||||
|
`}
|
||||||
|
/>
|
||||||
|
|
||||||
|
Note that these params will not do anything until you give them meaning in your custom output!
|
||||||
43
website/src/pages/learn/conditional-modifiers.mdx
Normal file
43
website/src/pages/learn/conditional-modifiers.mdx
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
---
|
||||||
|
title: Conditional Modifiers
|
||||||
|
layout: ../../layouts/MainLayout.astro
|
||||||
|
---
|
||||||
|
|
||||||
|
import { MiniRepl } from '../../docs/MiniRepl';
|
||||||
|
import { JsDoc } from '../../docs/JsDoc';
|
||||||
|
|
||||||
|
# Conditional Modifiers
|
||||||
|
|
||||||
|
## every
|
||||||
|
|
||||||
|
<JsDoc client:idle name="Pattern.every" h={0} />
|
||||||
|
|
||||||
|
## when
|
||||||
|
|
||||||
|
<JsDoc client:idle name="Pattern.when" h={0} />
|
||||||
|
|
||||||
|
# Accumulation Modifiers
|
||||||
|
|
||||||
|
## stack
|
||||||
|
|
||||||
|
<JsDoc client:idle name="Pattern.stack" h={0} />
|
||||||
|
|
||||||
|
## 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="Pattern.echoWith" h={0} />
|
||||||
63
website/src/pages/learn/factories.mdx
Normal file
63
website/src/pages/learn/factories.mdx
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
---
|
||||||
|
title: Pattern Constructors
|
||||||
|
description: Strudel Tutorial
|
||||||
|
layout: ../../layouts/MainLayout.astro
|
||||||
|
---
|
||||||
|
|
||||||
|
import { MiniRepl } from '../../docs/MiniRepl';
|
||||||
|
import { JsDoc } from '../../docs/JsDoc';
|
||||||
|
|
||||||
|
# Pattern Constructors
|
||||||
|
|
||||||
|
The following functions will return a pattern.
|
||||||
|
These are the equivalents used by the Mini Notation:
|
||||||
|
|
||||||
|
| function | mini |
|
||||||
|
| ------------------------------ | ---------------- |
|
||||||
|
| `cat(x, y)` | `"<x y>"` |
|
||||||
|
| `seq(x, y)` | `"x y"` |
|
||||||
|
| `stack(x, y)` | `"x,y"` |
|
||||||
|
| `timeCat([3,x],[2,y])` | `"x@2 y@2"` |
|
||||||
|
| `polymeter([a, b, c], [x, y])` | `"{a b c, x y}"` |
|
||||||
|
| `polymeterSteps(2, x, y, z)` | `"{x y z}%2"` |
|
||||||
|
| `silence` | `"~"` |
|
||||||
|
|
||||||
|
## cat
|
||||||
|
|
||||||
|
<JsDoc client:idle name="cat" h={0} />
|
||||||
|
|
||||||
|
You can also use cat as a chained function like this:
|
||||||
|
|
||||||
|
<JsDoc client:idle name="Pattern.cat" h={0} hideDescription />
|
||||||
|
|
||||||
|
## seq
|
||||||
|
|
||||||
|
<JsDoc client:idle name="seq" h={0} />
|
||||||
|
|
||||||
|
Or as a chained function:
|
||||||
|
|
||||||
|
<JsDoc client:idle name="Pattern.seq" h={0} hideDescription />
|
||||||
|
|
||||||
|
## stack
|
||||||
|
|
||||||
|
<JsDoc client:idle name="stack" h={0} />
|
||||||
|
|
||||||
|
As a chained function:
|
||||||
|
|
||||||
|
<JsDoc client:idle name="Pattern.stack" h={0} hideDescription />
|
||||||
|
|
||||||
|
## timeCat
|
||||||
|
|
||||||
|
<JsDoc client:idle name="timeCat" h={0} />
|
||||||
|
|
||||||
|
## polymeter
|
||||||
|
|
||||||
|
<JsDoc client:idle name="polymeter" h={0} />
|
||||||
|
|
||||||
|
## polymeterSteps
|
||||||
|
|
||||||
|
<JsDoc client:idle name="polymeterSteps" h={0} />
|
||||||
|
|
||||||
|
## silence
|
||||||
|
|
||||||
|
<JsDoc client:idle name="silence" h={0} />
|
||||||
@ -1,250 +0,0 @@
|
|||||||
---
|
|
||||||
title: What is Strudel?
|
|
||||||
description: Strudel Tutorial
|
|
||||||
layout: ../../layouts/MainLayout.astro
|
|
||||||
---
|
|
||||||
|
|
||||||
import { MiniRepl } from '../../docs/MiniRepl';
|
|
||||||
import { JsDoc } from '../../docs/JsDoc';
|
|
||||||
|
|
||||||
# Functional JavaScript API
|
|
||||||
|
|
||||||
While the mini notation is powerful on its own, there is much more to discover.
|
|
||||||
Internally, the mini notation will expand to use the actual functional JavaScript API.
|
|
||||||
|
|
||||||
For example, this Pattern in Mini Notation:
|
|
||||||
|
|
||||||
<MiniRepl client:only="react" tune={`note("c3 eb3 g3")`} />
|
|
||||||
|
|
||||||
is equivalent to this Pattern without Mini Notation:
|
|
||||||
|
|
||||||
<MiniRepl client:only="react" tune={`note(seq(c3, eb3, g3))`} />
|
|
||||||
|
|
||||||
Similarly, there is an equivalent function for every aspect of the mini notation.
|
|
||||||
|
|
||||||
Which representation to use is a matter of context. As a rule of thumb, you can think of the JavaScript API
|
|
||||||
to fit better for the larger context, while mini notation is more practical for individiual rhythms.
|
|
||||||
|
|
||||||
## Limits of Mini Notation
|
|
||||||
|
|
||||||
While the Mini Notation is a powerful way to write rhythms shortly, it also has its limits. Take this example:
|
|
||||||
|
|
||||||
<MiniRepl
|
|
||||||
client:idle
|
|
||||||
tune={`stack(
|
|
||||||
note("c2 eb2(3,8)").s('sawtooth').cutoff(800),
|
|
||||||
s("bd,~ sd,hh*4")
|
|
||||||
)`}
|
|
||||||
/>
|
|
||||||
|
|
||||||
Here, we are using mini notation for the individual rhythms, while using the function `stack` to mix them.
|
|
||||||
While stack is also available as `,` in mini notation, we cannot use it here, because we have different types of sounds.
|
|
||||||
|
|
||||||
## Notes
|
|
||||||
|
|
||||||
Notes are automatically available as variables:
|
|
||||||
|
|
||||||
<MiniRepl client:only="react" tune={`note(seq(d4, fs4, a4)) // note("d4 f#4 a4")`} />
|
|
||||||
|
|
||||||
An important difference to the mini notation:
|
|
||||||
For sharp notes, the letter "s" is used instead of "#", because JavaScript does not support "#" in a variable name.
|
|
||||||
|
|
||||||
The above is the same as:
|
|
||||||
|
|
||||||
<MiniRepl client:only="react" tune={`note(seq('d4', 'f#4', 'a4'))`} />
|
|
||||||
|
|
||||||
Using strings, you can also use "#".
|
|
||||||
|
|
||||||
## Alternative Syntax
|
|
||||||
|
|
||||||
In the above example, we are nesting a function inside a function, which makes reading the parens a little more difficult.
|
|
||||||
To avoid getting to many nested parens, there is an alternative syntax to add a type to a pattern:
|
|
||||||
|
|
||||||
<MiniRepl client:only="react" tune={`seq(d4, fs4, a4).note()`} />
|
|
||||||
|
|
||||||
You can use this with any function that declares a type (like `n`, `s`, `note`, `freq` etc), just make sure to leave the parens empty!
|
|
||||||
|
|
||||||
## Pattern Factories
|
|
||||||
|
|
||||||
The following functions will return a pattern.
|
|
||||||
|
|
||||||
### cat
|
|
||||||
|
|
||||||
<JsDoc client:idle name="cat" h={0} />
|
|
||||||
|
|
||||||
### seq
|
|
||||||
|
|
||||||
<JsDoc client:idle name="seq" h={0} />
|
|
||||||
|
|
||||||
### stack
|
|
||||||
|
|
||||||
<JsDoc client:idle name="stack" h={0} />
|
|
||||||
|
|
||||||
### timeCat
|
|
||||||
|
|
||||||
<JsDoc client:idle name="timeCat" h={0} />
|
|
||||||
|
|
||||||
## Combining Patterns
|
|
||||||
|
|
||||||
You can freely mix JS patterns, mini patterns and values! For example, this pattern:
|
|
||||||
|
|
||||||
<MiniRepl
|
|
||||||
client:idle
|
|
||||||
tune={`cat(
|
|
||||||
stack(g3,b3,e4),
|
|
||||||
stack(a3,c3,e4),
|
|
||||||
stack(b3,d3,fs4),
|
|
||||||
stack(b3,e4,g4)
|
|
||||||
).note()`}
|
|
||||||
/>
|
|
||||||
|
|
||||||
...is equivalent to:
|
|
||||||
|
|
||||||
<MiniRepl
|
|
||||||
client:idle
|
|
||||||
tune={`cat(
|
|
||||||
"g3,b3,e4",
|
|
||||||
"a3,c3,e4",
|
|
||||||
"b3,d3,f#4",
|
|
||||||
"b3,e4,g4"
|
|
||||||
).note()`}
|
|
||||||
/>
|
|
||||||
|
|
||||||
... as well as:
|
|
||||||
|
|
||||||
<MiniRepl client:only="react" tune={`note("<[g3,b3,e4] [a3,c3,e4] [b3,d3,f#4] [b3,e4,g4]>")`} />
|
|
||||||
|
|
||||||
While mini notation is almost always shorter, it only has a handful of modifiers: \* / ! @.
|
|
||||||
When using JS patterns, there is a lot more you can do.
|
|
||||||
|
|
||||||
## Time Modifiers
|
|
||||||
|
|
||||||
The following functions modify a pattern temporal structure in some way.
|
|
||||||
|
|
||||||
### Pattern.slow
|
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern.slow" h={0} />
|
|
||||||
|
|
||||||
### Pattern.fast
|
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern.fast" h={0} />
|
|
||||||
|
|
||||||
### Pattern.early
|
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern.early" h={0} />
|
|
||||||
|
|
||||||
### Pattern.late
|
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern.late" h={0} />
|
|
||||||
|
|
||||||
### Pattern.legato
|
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern.legato" h={0} />
|
|
||||||
|
|
||||||
### Pattern.struct
|
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern.struct" h={0} />
|
|
||||||
|
|
||||||
### Pattern.euclid
|
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern.euclid" h={0} />
|
|
||||||
|
|
||||||
### Pattern.euclidLegato
|
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern.euclidLegato" h={0} />
|
|
||||||
|
|
||||||
### Pattern.rev
|
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern.rev" h={0} />
|
|
||||||
|
|
||||||
### Pattern.iter
|
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern.iter" h={0} />
|
|
||||||
|
|
||||||
### Pattern.iterBack
|
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern.iterBack" h={0} />
|
|
||||||
|
|
||||||
## Conditional Modifiers
|
|
||||||
|
|
||||||
### Pattern.every
|
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern.every" h={0} />
|
|
||||||
|
|
||||||
### Pattern.when
|
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern.when" h={0} />
|
|
||||||
|
|
||||||
## Accumulation Modifiers
|
|
||||||
|
|
||||||
### Pattern.stack
|
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern.stack" h={0} />
|
|
||||||
|
|
||||||
### Pattern.superimpose
|
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern.superimpose" h={0} />
|
|
||||||
|
|
||||||
### Pattern.layer
|
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern.layer" h={0} />
|
|
||||||
|
|
||||||
### Pattern.off
|
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern.off" h={0} />
|
|
||||||
|
|
||||||
### Pattern.echo
|
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern.echo" h={0} />
|
|
||||||
|
|
||||||
### Pattern.echoWith
|
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern.echoWith" h={0} />
|
|
||||||
|
|
||||||
## Concat Modifiers
|
|
||||||
|
|
||||||
### Pattern.seq
|
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern.seq" h={0} />
|
|
||||||
|
|
||||||
### Pattern.cat
|
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern.cat" h={0} />
|
|
||||||
|
|
||||||
## Value Modifiers
|
|
||||||
|
|
||||||
### Pattern.add
|
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern.add" h={0} />
|
|
||||||
|
|
||||||
### Pattern.sub
|
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern.sub" h={0} />
|
|
||||||
|
|
||||||
### Pattern.mul
|
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern.mul" h={0} />
|
|
||||||
|
|
||||||
### Pattern.div
|
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern.div" h={0} />
|
|
||||||
|
|
||||||
### Pattern.round
|
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern.round" h={0} />
|
|
||||||
|
|
||||||
### Pattern.apply
|
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern.apply" h={0} />
|
|
||||||
|
|
||||||
### Pattern.range
|
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern.range" h={0} />
|
|
||||||
|
|
||||||
### Pattern.chunk
|
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern.chunk" h={0} />
|
|
||||||
|
|
||||||
### Pattern.chunkBack
|
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern.chunkBack" h={0} />
|
|
||||||
@ -10,16 +10,16 @@ import { JsDoc } from '../../docs/JsDoc';
|
|||||||
# Notes
|
# Notes
|
||||||
|
|
||||||
Pitches are an essential building block for music.
|
Pitches are an essential building block for music.
|
||||||
In Strudel, there are three different ways to express a pitch, `note`, `n` and `freq`.
|
In Strudel, pitches can be expressed as note names, note numbers or frequencies.
|
||||||
Here's the same pattern written in three different ways:
|
Here's the same pattern written in three different ways:
|
||||||
|
|
||||||
- `note`: letter notation, good for those who are familiar with western music theory:
|
- `note`: letter notation, good for those who are familiar with western music theory:
|
||||||
|
|
||||||
<MiniRepl client:idle tune={`note("a3 c#4 e4 a4")`} />
|
<MiniRepl client:idle tune={`note("a3 c#4 e4 a4")`} />
|
||||||
|
|
||||||
- `n`: number notation, good for those who want to use recognisable pitches, but don't care about music theory:
|
- `note`: number notation, good for those who want to use recognisable pitches, but don't care about music theory:
|
||||||
|
|
||||||
<MiniRepl client:idle tune={`n("57 61 64 69")`} />
|
<MiniRepl client:idle tune={`note("57 61 64 69")`} />
|
||||||
|
|
||||||
- `freq`: frequency notation, good for those who want to go beyond standardised tuning systems:
|
- `freq`: frequency notation, good for those who want to go beyond standardised tuning systems:
|
||||||
|
|
||||||
@ -27,28 +27,28 @@ Here's the same pattern written in three different ways:
|
|||||||
|
|
||||||
Let's look at `note`, `n` and `freq` in more detail...
|
Let's look at `note`, `n` and `freq` in more detail...
|
||||||
|
|
||||||
# `note`
|
## `note` names
|
||||||
|
|
||||||
Notes are notated with the note letter, followed by the octave number. You can notate flats with `b` and sharps with `#`.
|
Notes names can be notated with the note letter, followed by the octave number. You can notate flats with `b` and sharps with `#`.
|
||||||
|
|
||||||
<MiniRepl client:idle tune={`note("a3 c#4 e4 a4")`} />
|
<MiniRepl client:idle tune={`note("a3 c#4 e4 a4")`} />
|
||||||
|
|
||||||
By the way, you can edit the contents of the player, and press "update" to hear your change!
|
By the way, you can edit the contents of the player, and press "update" to hear your change!
|
||||||
You can also press "play" on the next player without needing to stop the last one.
|
You can also press "play" on the next player without needing to stop the last one.
|
||||||
|
|
||||||
# `n`
|
## `note` numbers
|
||||||
|
|
||||||
If you prefer, you can also use numbers with `n` instead:
|
If you prefer, you can also use numbers with `note` instead:
|
||||||
|
|
||||||
<MiniRepl client:idle tune={`n("57 61 64 69")`} />
|
<MiniRepl client:idle tune={`note("57 61 64 69")`} />
|
||||||
|
|
||||||
These numbers are interpreted as so called [MIDI numbers](https://www.inspiredacoustics.com/en/MIDI_note_numbers_and_center_frequencies), where adjacent whole numbers are one 'semitone' apart.
|
These numbers are interpreted as so called [MIDI numbers](https://www.inspiredacoustics.com/en/MIDI_note_numbers_and_center_frequencies), where adjacent whole numbers are one 'semitone' apart.
|
||||||
|
|
||||||
You could also write decimal numbers to get 'microtonal' pitches (in between the black and white piano notes):
|
You could also write decimal numbers to get 'microtonal' pitches (in between the black and white piano notes):
|
||||||
|
|
||||||
<MiniRepl client:idle tune={`n("74.5 75 75.5 76")`} />
|
<MiniRepl client:idle tune={`note("74.5 75 75.5 76")`} />
|
||||||
|
|
||||||
# `freq`
|
## `freq`
|
||||||
|
|
||||||
To get maximum freedom, you can also use `freq` to directly control the frequency:
|
To get maximum freedom, you can also use `freq` to directly control the frequency:
|
||||||
|
|
||||||
@ -76,13 +76,13 @@ The less distance we can hear between the frequencies!
|
|||||||
|
|
||||||
Why is this? [Human hearing operates logarithmically](https://www.audiocheck.net/soundtests_nonlinear.php).
|
Why is this? [Human hearing operates logarithmically](https://www.audiocheck.net/soundtests_nonlinear.php).
|
||||||
|
|
||||||
# From notes to sounds
|
## From notes to sounds
|
||||||
|
|
||||||
In this page, when we played a pattern of notes like this:
|
In this page, when we played a pattern of notes like this:
|
||||||
|
|
||||||
<MiniRepl client:idle tune={`note("a3 c#4 e4 a4")`} />
|
<MiniRepl client:idle tune={`note("a3 c#4 e4 a4")`} />
|
||||||
|
|
||||||
We heard a simple synthesised sound, in fact we heard a [square wave oscillator](https://en.wikipedia.org/wiki/Square_wave).
|
We heard a simple synthesised sound, in fact we heard a [triangle wave oscillator](https://en.wikipedia.org/wiki/Triangle_wave).
|
||||||
|
|
||||||
This is the default synthesiser used by Strudel, but how do we then make different sounds in Strudel?
|
This is the default synthesiser used by Strudel, but how do we then make different sounds in Strudel?
|
||||||
|
|
||||||
|
|||||||
@ -56,54 +56,54 @@ These methods add random behavior to your Patterns.
|
|||||||
|
|
||||||
<JsDoc client:idle name="chooseCycles" h={0} />
|
<JsDoc client:idle name="chooseCycles" h={0} />
|
||||||
|
|
||||||
## Pattern.degradeBy
|
## degradeBy
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern.degradeBy" h={0} />
|
<JsDoc client:idle name="Pattern.degradeBy" h={0} />
|
||||||
|
|
||||||
## Pattern.degrade
|
## degrade
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern.degrade" h={0} />
|
<JsDoc client:idle name="Pattern.degrade" h={0} />
|
||||||
|
|
||||||
## Pattern.undegradeBy
|
## undegradeBy
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern.undegradeBy" h={0} />
|
<JsDoc client:idle name="Pattern.undegradeBy" h={0} />
|
||||||
|
|
||||||
## Pattern.sometimesBy
|
## sometimesBy
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern.sometimesBy" h={0} />
|
<JsDoc client:idle name="Pattern.sometimesBy" h={0} />
|
||||||
|
|
||||||
## Pattern.sometimes
|
## sometimes
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern.sometimes" h={0} />
|
<JsDoc client:idle name="Pattern.sometimes" h={0} />
|
||||||
|
|
||||||
## Pattern.someCyclesBy
|
## someCyclesBy
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern.someCyclesBy" h={0} />
|
<JsDoc client:idle name="Pattern.someCyclesBy" h={0} />
|
||||||
|
|
||||||
## Pattern.someCycles
|
## someCycles
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern.someCycles" h={0} />
|
<JsDoc client:idle name="Pattern.someCycles" h={0} />
|
||||||
|
|
||||||
## Pattern.often
|
## often
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern.often" h={0} />
|
<JsDoc client:idle name="Pattern.often" h={0} />
|
||||||
|
|
||||||
## Pattern.rarely
|
## rarely
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern.rarely" h={0} />
|
<JsDoc client:idle name="Pattern.rarely" h={0} />
|
||||||
|
|
||||||
## Pattern.almostNever
|
## almostNever
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern.almostNever" h={0} />
|
<JsDoc client:idle name="Pattern.almostNever" h={0} />
|
||||||
|
|
||||||
## Pattern.almostAlways
|
## almostAlways
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern.almostAlways" h={0} />
|
<JsDoc client:idle name="Pattern.almostAlways" h={0} />
|
||||||
|
|
||||||
## Pattern.never
|
## never
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern.never" h={0} />
|
<JsDoc client:idle name="Pattern.never" h={0} />
|
||||||
|
|
||||||
## Pattern.always
|
## always
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern.always" h={0} />
|
<JsDoc client:idle name="Pattern.always" h={0} />
|
||||||
|
|||||||
75
website/src/pages/learn/time-modifiers.mdx
Normal file
75
website/src/pages/learn/time-modifiers.mdx
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
---
|
||||||
|
title: Time Modifiers
|
||||||
|
layout: ../../layouts/MainLayout.astro
|
||||||
|
---
|
||||||
|
|
||||||
|
import { MiniRepl } from '../../docs/MiniRepl';
|
||||||
|
import { JsDoc } from '../../docs/JsDoc';
|
||||||
|
|
||||||
|
# Time Modifiers
|
||||||
|
|
||||||
|
The following functions modify a pattern temporal structure in some way.
|
||||||
|
Some of these have equivalent operators in the Mini Notation:
|
||||||
|
|
||||||
|
| function | mini |
|
||||||
|
| ---------------------- | ------------ |
|
||||||
|
| `"x".slow(2)` | `"x/2"` |
|
||||||
|
| `"x".fast(2)` | `"x*2"` |
|
||||||
|
| `"x".euclid(3,8)` | `"x(3,8)"` |
|
||||||
|
| `"x".euclidRot(3,8,1)` | `"x(3,8,1)"` |
|
||||||
|
|
||||||
|
## slow
|
||||||
|
|
||||||
|
<JsDoc client:idle name="Pattern.slow" h={0} />
|
||||||
|
|
||||||
|
## fast
|
||||||
|
|
||||||
|
<JsDoc client:idle name="Pattern.fast" h={0} />
|
||||||
|
|
||||||
|
## early
|
||||||
|
|
||||||
|
<JsDoc client:idle name="Pattern.early" h={0} />
|
||||||
|
|
||||||
|
## late
|
||||||
|
|
||||||
|
<JsDoc client:idle name="Pattern.late" h={0} />
|
||||||
|
|
||||||
|
## legato
|
||||||
|
|
||||||
|
<JsDoc client:idle name="Pattern.legato" h={0} />
|
||||||
|
|
||||||
|
## struct
|
||||||
|
|
||||||
|
<JsDoc client:idle name="Pattern.struct" h={0} />
|
||||||
|
|
||||||
|
## euclid
|
||||||
|
|
||||||
|
<JsDoc client:idle name="Pattern.euclid" h={0} />
|
||||||
|
|
||||||
|
### euclidRot
|
||||||
|
|
||||||
|
<JsDoc client:idle name="Pattern.euclidRot" h={0} />
|
||||||
|
|
||||||
|
### euclidLegato
|
||||||
|
|
||||||
|
<JsDoc client:idle name="Pattern.euclidLegato" h={0} />
|
||||||
|
|
||||||
|
## rev
|
||||||
|
|
||||||
|
<JsDoc client:idle name="Pattern.rev" h={0} />
|
||||||
|
|
||||||
|
## iter
|
||||||
|
|
||||||
|
<JsDoc client:idle name="Pattern.iter" h={0} />
|
||||||
|
|
||||||
|
### iterBack
|
||||||
|
|
||||||
|
<JsDoc client:idle name="Pattern.iterBack" h={0} />
|
||||||
|
|
||||||
|
## chunk
|
||||||
|
|
||||||
|
<JsDoc client:idle name="Pattern.chunk" h={0} />
|
||||||
|
|
||||||
|
### chunkBack
|
||||||
|
|
||||||
|
<JsDoc client:idle name="Pattern.chunkBack" h={0} />
|
||||||
@ -47,6 +47,7 @@ Usage:
|
|||||||
|
|
||||||
- `name`: function name, as named with `@name` in jsdoc
|
- `name`: function name, as named with `@name` in jsdoc
|
||||||
- `h`: level of heading. `0` will hide the heading. Hiding it allows using a manual heading which results in a nav link being generated in the right sidebar.
|
- `h`: level of heading. `0` will hide the heading. Hiding it allows using a manual heading which results in a nav link being generated in the right sidebar.
|
||||||
|
- `hideDescription`: if set, the description will be hidden
|
||||||
|
|
||||||
### Writing jsdoc
|
### Writing jsdoc
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,7 @@
|
|||||||
/** @type {import('tailwindcss').Config} */
|
/** @type {import('tailwindcss').Config} */
|
||||||
|
|
||||||
|
const defaultTheme = require('tailwindcss/defaultTheme');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
content: ['./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}'],
|
content: ['./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}'],
|
||||||
theme: {
|
theme: {
|
||||||
@ -17,6 +20,20 @@ module.exports = {
|
|||||||
// header: 'transparent',
|
// header: 'transparent',
|
||||||
footer: '#00000050',
|
footer: '#00000050',
|
||||||
},
|
},
|
||||||
|
typography(theme) {
|
||||||
|
return {
|
||||||
|
DEFAULT: {
|
||||||
|
css: {
|
||||||
|
'code::before': {
|
||||||
|
content: 'none', // don’t wrap code in backticks
|
||||||
|
},
|
||||||
|
'code::after': {
|
||||||
|
content: 'none',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
plugins: [require('@tailwindcss/typography')],
|
plugins: [require('@tailwindcss/typography')],
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user