Compare commits

..

21 Commits

Author SHA1 Message Date
Felix Roos 4ec1145dce Publish
- @strudel.cycles/core@0.4.1
 - @strudel.cycles/eval@0.4.1
 - @strudel.cycles/midi@0.4.1
 - @strudel.cycles/mini@0.4.1
 - @strudel.cycles/osc@0.3.1
 - @strudel.cycles/react@0.4.2
 - @strudel.cycles/soundfonts@0.4.2
 - @strudel.cycles/tonal@0.4.1
 - @strudel.cycles/tone@0.4.1
 - @strudel.cycles/transpiler@0.4.1
 - @strudel.cycles/webaudio@0.4.2
 - @strudel.cycles/webdirt@0.4.1
 - @strudel.cycles/xen@0.4.1
2022-11-17 11:09:54 +01:00
Felix Roos 874633051b fix: snapshot 2022-11-17 10:56:46 +01:00
Felix Roos 2293f1ba15 fix: worklets now work in the tutorial again 2022-11-17 10:52:07 +01:00
Felix Roos 483c3f61b5 midi now works in the tutorial 2022-11-17 10:49:44 +01:00
Felix Roos 83263968f2 fix: rootNotes example 2022-11-17 10:41:32 +01:00
Felix Roos f279c61792 fix: each example 2022-11-17 10:19:04 +01:00
Felix Roos 9717c696d8 add s for sharp (fixes notes example) 2022-11-17 10:16:29 +01:00
Felix Roos 94a594c777 fix sample examples 2022-11-17 10:15:59 +01:00
Felix Roos f1ae8a17cf fix: could play multiple mini repl at once 2022-11-17 10:07:19 +01:00
Felix Roos add9c43827 Merge pull request #254 from tidalcycles/binaries
Binaries
2022-11-16 13:15:28 +01:00
Felix Roos d3f6a5c296 package-lock 2022-11-16 13:13:50 +01:00
Felix Roos 4aea78b7c0 Merge branch 'main' into binaries 2022-11-16 13:13:11 +01:00
Felix Roos 4313fe670d hotfix: fix share #261 2022-11-13 21:32:46 +01:00
Felix Roos 2e8a1b67ea hotfix: fix prebuild 2022-11-13 20:53:28 +01:00
Felix Roos e73044c533 Publish
- @strudel.cycles/react@0.4.1
 - @strudel.cycles/soundfonts@0.4.1
 - @strudel.cycles/webaudio@0.4.1
2022-11-13 20:48:52 +01:00
Felix Roos 418f09e364 hotfix: remove webaudio build for now 2022-11-13 20:48:32 +01:00
Felix Roos 08f207a396 package-lock 2022-11-13 20:44:05 +01:00
Felix Roos 64275cfd22 revert server changes 2022-11-10 10:12:52 +01:00
Felix Roos f0b099d9f7 Merge remote-tracking branch 'origin/main' into binaries 2022-11-10 10:11:47 +01:00
Felix Roos ad3a324719 Merge branch 'in-source-doc' into binaries 2022-05-25 21:55:53 +02:00
Felix Roos 8c9e178206 add build script for binaries 2022-05-25 21:24:05 +02:00
35 changed files with 944 additions and 279 deletions
+685 -55
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -10,7 +10,7 @@
"test-coverage": "vitest --coverage",
"bootstrap": "lerna bootstrap",
"setup": "npm i && npm run bootstrap && cd repl && npm i && cd ../tutorial && npm i",
"snapshot": "cd repl && npm run snapshot",
"snapshot": "vitest run -u --silent",
"repl": "cd repl && npm run dev",
"osc": "cd packages/osc && npm run server",
"build": "rm -rf out && cd repl && npm run build && cd ../tutorial && npm run build",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel.cycles/core",
"version": "0.4.0",
"version": "0.4.1",
"lockfileVersion": 2,
"requires": true,
"packages": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel.cycles/core",
"version": "0.4.0",
"version": "0.4.1",
"description": "Port of Tidal Cycles to JavaScript",
"main": "index.mjs",
"type": "module",
+1 -1
View File
@@ -1050,7 +1050,7 @@ export class Pattern {
* @param {function} func function to apply
* @returns Pattern
* @example
* note("c3 d3 e3 g3").every(4, x=>x.rev())
* note("c3 d3 e3 g3").each(4, x=>x.rev())
*/
each(n, func) {
const pat = this;
+1 -1
View File
@@ -5,7 +5,7 @@ This program is free software: you can redistribute it and/or modify it under th
*/
// returns true if the given string is a note
export const isNote = (name) => /^[a-gA-G][#b]*[0-9]$/.test(name);
export const isNote = (name) => /^[a-gA-G][#bs]*[0-9]$/.test(name);
export const tokenizeNote = (note) => {
if (typeof note !== 'string') {
return [];
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel.cycles/eval",
"version": "0.4.0",
"version": "0.4.1",
"lockfileVersion": 2,
"requires": true,
"packages": {
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel.cycles/eval",
"version": "0.4.0",
"version": "0.4.1",
"description": "Code evaluator for strudel",
"main": "index.mjs",
"type": "module",
@@ -28,7 +28,7 @@
},
"homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": {
"@strudel.cycles/core": "^0.4.0",
"@strudel.cycles/core": "^0.4.1",
"estraverse": "^5.3.0",
"shift-ast": "^7.0.0",
"shift-codegen": "^8.1.0",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel.cycles/midi",
"version": "0.4.0",
"version": "0.4.1",
"lockfileVersion": 2,
"requires": true,
"packages": {
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel.cycles/midi",
"version": "0.4.0",
"version": "0.4.1",
"description": "Midi API for strudel",
"main": "index.mjs",
"repository": {
@@ -21,7 +21,7 @@
},
"homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": {
"@strudel.cycles/tone": "^0.4.0",
"@strudel.cycles/tone": "^0.4.1",
"tone": "^14.7.77",
"webmidi": "^3.0.21"
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel.cycles/mini",
"version": "0.4.0",
"version": "0.4.1",
"lockfileVersion": 2,
"requires": true,
"packages": {
+4 -4
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel.cycles/mini",
"version": "0.4.0",
"version": "0.4.1",
"description": "Mini notation for strudel",
"main": "index.mjs",
"type": "module",
@@ -26,9 +26,9 @@
},
"homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": {
"@strudel.cycles/core": "^0.4.0",
"@strudel.cycles/eval": "^0.4.0",
"@strudel.cycles/tone": "^0.4.0"
"@strudel.cycles/core": "^0.4.1",
"@strudel.cycles/eval": "^0.4.1",
"@strudel.cycles/tone": "^0.4.1"
},
"devDependencies": {
"peggy": "^2.0.1"
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel.cycles/osc",
"version": "0.3.0",
"version": "0.3.1",
"lockfileVersion": 2,
"requires": true,
"packages": {
+6 -2
View File
@@ -1,13 +1,14 @@
{
"name": "@strudel.cycles/osc",
"version": "0.3.0",
"version": "0.3.1",
"description": "OSC messaging for strudel",
"main": "osc.mjs",
"scripts": {
"test": "echo \"No tests present.\" && exit 0",
"server": "node server.js",
"tidal-sniffer": "node tidal-sniffer.js",
"client": "npx serve -p 4321"
"client": "npx serve -p 4321",
"build": "npx pkg server.js --targets node16-macos-x64,node16-win-x64,node16-linux-x64 --out-path bin"
},
"repository": {
"type": "git",
@@ -31,5 +32,8 @@
"homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": {
"osc-js": "^2.4.0"
},
"devDependencies": {
"pkg": "^5.7.0"
}
}
+1 -1
View File
File diff suppressed because one or more lines are too long
+158 -148
View File
@@ -1,15 +1,15 @@
import n, { useCallback as N, useRef as x, useEffect as R, useState as w, useMemo as I, useLayoutEffect as K } from "react";
import Q from "@uiw/react-codemirror";
import { Decoration as E, EditorView as O } from "@codemirror/view";
import { StateEffect as j, StateField as U } from "@codemirror/state";
import { javascript as W } from "@codemirror/lang-javascript";
import n, { useCallback as _, useRef as H, useEffect as L, useMemo as V, useState as w, useLayoutEffect as j } from "react";
import X from "@uiw/react-codemirror";
import { Decoration as E, EditorView as U } from "@codemirror/view";
import { StateEffect as $, StateField as G } from "@codemirror/state";
import { javascript as Y } from "@codemirror/lang-javascript";
import { tags as r } from "@lezer/highlight";
import { createTheme as X } from "@uiw/codemirror-themes";
import { useInView as Y } from "react-hook-inview";
import { webaudioOutput as Z, getAudioContext as ee } from "@strudel.cycles/webaudio";
import { repl as te } from "@strudel.cycles/core";
import { transpiler as re } from "@strudel.cycles/transpiler";
const oe = X({
import { createTheme as Z } from "@uiw/codemirror-themes";
import { useInView as ee } from "react-hook-inview";
import { webaudioOutput as te, getAudioContext as re } from "@strudel.cycles/webaudio";
import { repl as oe } from "@strudel.cycles/core";
import { transpiler as ne } from "@strudel.cycles/transpiler";
const ae = Z({
theme: "dark",
settings: {
background: "#222",
@@ -42,14 +42,14 @@ const oe = X({
{ tag: r.invalid, color: "#ffffff" }
]
});
const T = j.define(), ne = U.define({
const B = $.define(), se = G.define({
create() {
return E.none;
},
update(e, t) {
try {
for (let o of t.effects)
if (o.is(T))
if (o.is(B))
if (o.value) {
const a = E.mark({ attributes: { style: "background-color: #FFCA2880" } });
e = E.set([a.range(0, t.newDoc.length)]);
@@ -60,25 +60,25 @@ const T = j.define(), ne = U.define({
return console.warn("flash error", o), e;
}
},
provide: (e) => O.decorations.from(e)
}), ae = (e) => {
e.dispatch({ effects: T.of(!0) }), setTimeout(() => {
e.dispatch({ effects: T.of(!1) });
provide: (e) => U.decorations.from(e)
}), ce = (e) => {
e.dispatch({ effects: B.of(!0) }), setTimeout(() => {
e.dispatch({ effects: B.of(!1) });
}, 200);
}, A = j.define(), se = U.define({
}, z = $.define(), ie = G.define({
create() {
return E.none;
},
update(e, t) {
try {
for (let o of t.effects)
if (o.is(A)) {
if (o.is(z)) {
const a = o.value.map(
(s) => (s.context.locations || []).map(({ start: m, end: l }) => {
const d = s.context.color || "#FFCA28";
let c = t.newDoc.line(m.line).from + m.column, i = t.newDoc.line(l.line).from + l.column;
const g = t.newDoc.length;
return c > g || i > g ? void 0 : E.mark({ attributes: { style: `outline: 1.5px solid ${d};` } }).range(c, i);
(s) => (s.context.locations || []).map(({ start: f, end: d }) => {
const u = s.context.color || "#FFCA28";
let c = t.newDoc.line(f.line).from + f.column, i = t.newDoc.line(d.line).from + d.column;
const m = t.newDoc.length;
return c > m || i > m ? void 0 : E.mark({ attributes: { style: `outline: 1.5px solid ${u};` } }).range(c, i);
})
).flat().filter(Boolean) || [];
e = E.set(a, !0);
@@ -88,69 +88,69 @@ const T = j.define(), ne = U.define({
return E.set([]);
}
},
provide: (e) => O.decorations.from(e)
}), ce = [W(), oe, se, ne];
function ie({ value: e, onChange: t, onViewChanged: o, onSelectionChange: a, options: s, editorDidMount: m }) {
const l = N(
provide: (e) => U.decorations.from(e)
}), le = [Y(), ae, ie, se];
function de({ value: e, onChange: t, onViewChanged: o, onSelectionChange: a, options: s, editorDidMount: f }) {
const d = _(
(i) => {
t?.(i);
},
[t]
), d = N(
), u = _(
(i) => {
o?.(i);
},
[o]
), c = N(
), c = _(
(i) => {
i.selectionSet && a && a?.(i.state.selection);
},
[a]
);
return /* @__PURE__ */ n.createElement(n.Fragment, null, /* @__PURE__ */ n.createElement(Q, {
return /* @__PURE__ */ n.createElement(n.Fragment, null, /* @__PURE__ */ n.createElement(X, {
value: e,
onChange: l,
onCreateEditor: d,
onChange: d,
onCreateEditor: u,
onUpdate: c,
extensions: ce
extensions: le
}));
}
function B(...e) {
function K(...e) {
return e.filter(Boolean).join(" ");
}
function le({ view: e, pattern: t, active: o, getTime: a }) {
const s = x([]), m = x();
R(() => {
function ue({ view: e, pattern: t, active: o, getTime: a }) {
const s = H([]), f = H();
L(() => {
if (e)
if (t && o) {
let d = function() {
let u = function() {
try {
const c = a(), g = [Math.max(m.current || c, c - 1 / 10, 0), c + 1 / 60];
m.current = g[1], s.current = s.current.filter((p) => p.whole.end > c);
const v = t.queryArc(...g).filter((p) => p.hasOnset());
s.current = s.current.concat(v), e.dispatch({ effects: A.of(s.current) });
const c = a(), m = [Math.max(f.current || c, c - 1 / 10, 0), c + 1 / 60];
f.current = m[1], s.current = s.current.filter((g) => g.whole.end > c);
const h = t.queryArc(...m).filter((g) => g.hasOnset());
s.current = s.current.concat(h), e.dispatch({ effects: z.of(s.current) });
} catch {
e.dispatch({ effects: A.of([]) });
e.dispatch({ effects: z.of([]) });
}
l = requestAnimationFrame(d);
}, l = requestAnimationFrame(d);
d = requestAnimationFrame(u);
}, d = requestAnimationFrame(u);
return () => {
cancelAnimationFrame(l);
cancelAnimationFrame(d);
};
} else
s.current = [], e.dispatch({ effects: A.of([]) });
s.current = [], e.dispatch({ effects: z.of([]) });
}, [t, o, e]);
}
const de = "_container_3i85k_1", ue = "_header_3i85k_5", fe = "_buttons_3i85k_9", me = "_button_3i85k_9", ge = "_buttonDisabled_3i85k_17", pe = "_error_3i85k_21", he = "_body_3i85k_25", b = {
container: de,
header: ue,
buttons: fe,
button: me,
buttonDisabled: ge,
error: pe,
body: he
const fe = "_container_3i85k_1", me = "_header_3i85k_5", ge = "_buttons_3i85k_9", pe = "_button_3i85k_9", he = "_buttonDisabled_3i85k_17", be = "_error_3i85k_21", ve = "_body_3i85k_25", v = {
container: fe,
header: me,
buttons: ge,
button: pe,
buttonDisabled: he,
error: be,
body: ve
};
function q({ type: e }) {
function O({ type: e }) {
return /* @__PURE__ */ n.createElement("svg", {
xmlns: "http://www.w3.org/2000/svg",
className: "sc-h-5 sc-w-5",
@@ -174,137 +174,147 @@ function q({ type: e }) {
})
}[e]);
}
function ve({
function Ee(e) {
return L(() => (window.addEventListener("message", e), () => window.removeEventListener("message", e)), [e]), _((t) => window.postMessage(t, "*"), []);
}
function we({
defaultOutput: e,
interval: t,
getTime: o,
evalOnMount: a = !1,
initialCode: s = "",
autolink: m = !1,
beforeEval: l,
afterEval: d,
autolink: f = !1,
beforeEval: d,
afterEval: u,
onEvalError: c,
onToggle: i
}) {
const [g, v] = w(), [p, D] = w(), [h, k] = w(s), [y, P] = w(h), [z, _] = w(), [C, H] = w(!1), F = h !== y, { scheduler: u, evaluate: L, start: $, stop: G, pause: J } = I(
() => te({
const m = V(() => ye(), []), [h, g] = w(), [C, N] = w(), [p, y] = w(s), [M, S] = w(), [k, D] = w(), [F, x] = w(!1), b = p !== M, { scheduler: A, evaluate: T, start: J, stop: q, pause: Q } = V(
() => oe({
interval: t,
defaultOutput: e,
onSchedulerError: v,
onEvalError: (f) => {
D(f), c?.(f);
onSchedulerError: g,
onEvalError: (l) => {
N(l), c?.(l);
},
getTime: o,
transpiler: re,
beforeEval: ({ code: f }) => {
k(f), l?.();
transpiler: ne,
beforeEval: ({ code: l }) => {
y(l), d?.();
},
afterEval: ({ pattern: f, code: S }) => {
P(S), _(f), D(), v(), m && (window.location.hash = "#" + encodeURIComponent(btoa(S))), d?.();
afterEval: ({ pattern: l, code: P }) => {
S(P), D(l), N(), g(), f && (window.location.hash = "#" + encodeURIComponent(btoa(P))), u?.();
},
onToggle: (f) => {
H(f), i?.(f);
onToggle: (l) => {
x(l), i?.(l);
}
}),
[e, t, o]
), M = N(async (f = !0) => L(h, f), [L, h]), V = x();
return R(() => {
!V.current && a && h && (V.current = !0, M());
}, [M, a, h]), R(() => () => {
u.stop();
}, [u]), {
code: h,
setCode: k,
error: g || p,
schedulerError: g,
scheduler: u,
evalError: p,
evaluate: L,
activateCode: M,
activeCode: y,
isDirty: F,
pattern: z,
started: C,
start: $,
stop: G,
pause: J,
), W = Ee(({ data: { from: l, type: P } }) => {
P === "start" && l !== m && q();
}), R = _(
async (l = !0) => {
await T(p, l), W({ type: "start", from: m });
},
[T, p]
), I = H();
return L(() => {
!I.current && a && p && (I.current = !0, R());
}, [R, a, p]), L(() => () => {
A.stop();
}, [A]), {
code: p,
setCode: y,
error: h || C,
schedulerError: h,
scheduler: A,
evalError: C,
evaluate: T,
activateCode: R,
activeCode: M,
isDirty: b,
pattern: k,
started: F,
start: J,
stop: q,
pause: Q,
togglePlay: async () => {
C ? u.pause() : await M();
F ? A.pause() : await R();
}
};
}
const be = () => ee().currentTime;
function Pe({ tune: e, hideOutsideView: t = !1, init: o, enableKeyboard: a }) {
function ye() {
return Math.floor((1 + Math.random()) * 65536).toString(16).substring(1);
}
const ke = () => re().currentTime;
function Se({ tune: e, hideOutsideView: t = !1, init: o, enableKeyboard: a }) {
const {
code: s,
setCode: m,
evaluate: l,
activateCode: d,
setCode: f,
evaluate: d,
activateCode: u,
error: c,
isDirty: i,
activeCode: g,
pattern: v,
started: p,
scheduler: D,
togglePlay: h,
stop: k
} = ve({
activeCode: m,
pattern: h,
started: g,
scheduler: C,
togglePlay: N,
stop: p
} = we({
initialCode: e,
defaultOutput: Z,
getTime: be
}), [y, P] = w(), [z, _] = Y({
defaultOutput: te,
getTime: ke
}), [y, M] = w(), [S, k] = ee({
threshold: 0.01
}), C = x(), H = I(() => ((_ || !t) && (C.current = !0), _ || C.current), [_, t]);
return le({
}), D = H(), F = V(() => ((k || !t) && (D.current = !0), k || D.current), [k, t]);
return ue({
view: y,
pattern: v,
active: p && !g?.includes("strudel disable-highlighting"),
getTime: () => D.getPhase()
}), K(() => {
pattern: h,
active: g && !m?.includes("strudel disable-highlighting"),
getTime: () => C.getPhase()
}), j(() => {
if (a) {
const F = async (u) => {
(u.ctrlKey || u.altKey) && (u.code === "Enter" ? (u.preventDefault(), ae(y), await d()) : u.code === "Period" && (k(), u.preventDefault()));
const x = async (b) => {
(b.ctrlKey || b.altKey) && (b.code === "Enter" ? (b.preventDefault(), ce(y), await u()) : b.code === "Period" && (p(), b.preventDefault()));
};
return window.addEventListener("keydown", F, !0), () => window.removeEventListener("keydown", F, !0);
return window.addEventListener("keydown", x, !0), () => window.removeEventListener("keydown", x, !0);
}
}, [a, v, s, l, k, y]), /* @__PURE__ */ n.createElement("div", {
className: b.container,
ref: z
}, [a, h, s, d, p, y]), /* @__PURE__ */ n.createElement("div", {
className: v.container,
ref: S
}, /* @__PURE__ */ n.createElement("div", {
className: b.header
className: v.header
}, /* @__PURE__ */ n.createElement("div", {
className: b.buttons
className: v.buttons
}, /* @__PURE__ */ n.createElement("button", {
className: B(b.button, p ? "sc-animate-pulse" : ""),
onClick: () => h()
}, /* @__PURE__ */ n.createElement(q, {
type: p ? "pause" : "play"
className: K(v.button, g ? "sc-animate-pulse" : ""),
onClick: () => N()
}, /* @__PURE__ */ n.createElement(O, {
type: g ? "pause" : "play"
})), /* @__PURE__ */ n.createElement("button", {
className: B(i ? b.button : b.buttonDisabled),
onClick: () => d()
}, /* @__PURE__ */ n.createElement(q, {
className: K(i ? v.button : v.buttonDisabled),
onClick: () => u()
}, /* @__PURE__ */ n.createElement(O, {
type: "refresh"
}))), c && /* @__PURE__ */ n.createElement("div", {
className: b.error
className: v.error
}, c.message)), /* @__PURE__ */ n.createElement("div", {
className: b.body
}, H && /* @__PURE__ */ n.createElement(ie, {
className: v.body
}, F && /* @__PURE__ */ n.createElement(de, {
value: s,
onChange: m,
onViewChanged: P
onChange: f,
onViewChanged: M
})));
}
function ze(e) {
return R(() => (window.addEventListener("message", e), () => window.removeEventListener("message", e)), [e]), N((t) => window.postMessage(t, "*"), []);
}
const He = (e) => K(() => (window.addEventListener("keydown", e, !0), () => window.removeEventListener("keydown", e, !0)), [e]);
const Te = (e) => j(() => (window.addEventListener("keydown", e, !0), () => window.removeEventListener("keydown", e, !0)), [e]);
export {
ie as CodeMirror,
Pe as MiniRepl,
B as cx,
ae as flash,
le as useHighlighting,
He as useKeydown,
ze as usePostMessage,
ve as useStrudel
de as CodeMirror,
Se as MiniRepl,
K as cx,
ce as flash,
ue as useHighlighting,
Te as useKeydown,
Ee as usePostMessage,
we as useStrudel
};
+5 -5
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel.cycles/react",
"version": "0.4.0",
"version": "0.4.2",
"description": "React components for strudel",
"main": "dist/index.cjs.js",
"module": "dist/index.es.js",
@@ -39,10 +39,10 @@
"homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": {
"@codemirror/lang-javascript": "^6.1.1",
"@strudel.cycles/core": "^0.4.0",
"@strudel.cycles/tone": "^0.4.0",
"@strudel.cycles/transpiler": "^0.4.0",
"@strudel.cycles/webaudio": "^0.4.0",
"@strudel.cycles/core": "^0.4.1",
"@strudel.cycles/tone": "^0.4.1",
"@strudel.cycles/transpiler": "^0.4.1",
"@strudel.cycles/webaudio": "^0.4.2",
"@uiw/codemirror-themes": "^4.12.4",
"@uiw/react-codemirror": "^4.12.4",
"react-hook-inview": "^4.5.0"
+22 -2
View File
@@ -1,6 +1,7 @@
import { useRef, useCallback, useEffect, useMemo, useState } from 'react';
import { repl } from '@strudel.cycles/core';
import { transpiler } from '@strudel.cycles/transpiler';
import usePostMessage from './usePostMessage.mjs';
function useStrudel({
defaultOutput,
@@ -14,11 +15,12 @@ function useStrudel({
onEvalError,
onToggle,
}) {
const id = useMemo(() => s4(), []);
// scheduler
const [schedulerError, setSchedulerError] = useState();
const [evalError, setEvalError] = useState();
const [code, setCode] = useState(initialCode);
const [activeCode, setActiveCode] = useState(code);
const [activeCode, setActiveCode] = useState();
const [pattern, setPattern] = useState();
const [started, setStarted] = useState(false);
const isDirty = code !== activeCode;
@@ -57,7 +59,19 @@ function useStrudel({
}),
[defaultOutput, interval, getTime],
);
const activateCode = useCallback(async (autostart = true) => evaluate(code, autostart), [evaluate, code]);
const broadcast = usePostMessage(({ data: { from, type } }) => {
if (type === 'start' && from !== id) {
// console.log('message', from, type);
stop();
}
});
const activateCode = useCallback(
async (autostart = true) => {
await evaluate(code, autostart);
broadcast({ type: 'start', from: id });
},
[evaluate, code],
);
const inited = useRef();
useEffect(() => {
@@ -103,3 +117,9 @@ function useStrudel({
}
export default useStrudel;
function s4() {
return Math.floor((1 + Math.random()) * 0x10000)
.toString(16)
.substring(1);
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel.cycles/soundfonts",
"version": "0.4.0",
"version": "0.4.2",
"lockfileVersion": 2,
"requires": true,
"packages": {
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel.cycles/soundfonts",
"version": "0.4.0",
"version": "0.4.2",
"description": "Soundsfont support for strudel",
"main": "index.mjs",
"type": "module",
@@ -22,8 +22,8 @@
},
"homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": {
"@strudel.cycles/core": "^0.4.0",
"@strudel.cycles/webaudio": "^0.4.0",
"@strudel.cycles/core": "^0.4.1",
"@strudel.cycles/webaudio": "^0.4.2",
"sfumato": "^0.1.2",
"soundfont2": "^0.4.0"
},
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel.cycles/tonal",
"version": "0.4.0",
"version": "0.4.1",
"lockfileVersion": 2,
"requires": true,
"packages": {
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel.cycles/tonal",
"version": "0.4.0",
"version": "0.4.1",
"description": "Tonal functions for strudel",
"main": "index.mjs",
"type": "module",
@@ -25,7 +25,7 @@
},
"homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": {
"@strudel.cycles/core": "^0.4.0",
"@strudel.cycles/core": "^0.4.1",
"@tonaljs/tonal": "^4.6.5",
"chord-voicings": "^0.0.1",
"webmidi": "^3.0.21"
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel.cycles/tone",
"version": "0.4.0",
"version": "0.4.1",
"lockfileVersion": 2,
"requires": true,
"packages": {
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel.cycles/tone",
"version": "0.4.0",
"version": "0.4.1",
"description": "Tone.js API for strudel",
"main": "index.mjs",
"type": "module",
@@ -22,7 +22,7 @@
},
"homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": {
"@strudel.cycles/core": "^0.4.0",
"@strudel.cycles/core": "^0.4.1",
"tone": "^14.7.77"
}
}
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel.cycles/transpiler",
"version": "0.4.0",
"version": "0.4.1",
"description": "Transpiler for strudel user code. Converts syntactically correct but semantically meaningless JS into evaluatable strudel code.",
"main": "index.mjs",
"scripts": {
@@ -24,7 +24,7 @@
},
"homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": {
"@strudel.cycles/core": "^0.4.0",
"@strudel.cycles/core": "^0.4.1",
"acorn": "^8.8.1",
"escodegen": "^2.0.0",
"estree-walker": "^3.0.1"
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel.cycles/webaudio",
"version": "0.4.0",
"version": "0.4.2",
"lockfileVersion": 2,
"requires": true,
"packages": {
+3 -4
View File
@@ -1,9 +1,8 @@
{
"name": "@strudel.cycles/webaudio",
"version": "0.4.0",
"version": "0.4.2",
"description": "Web Audio helpers for Strudel",
"main": "dist/index.cjs.js",
"module": "dist/index.es.js",
"main": "index.mjs",
"type": "module",
"directories": {
"example": "examples"
@@ -31,6 +30,6 @@
},
"homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": {
"@strudel.cycles/core": "^0.4.0"
"@strudel.cycles/core": "^0.4.1"
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel.cycles/webdirt",
"version": "0.4.0",
"version": "0.4.1",
"lockfileVersion": 2,
"requires": true,
"packages": {
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel.cycles/webdirt",
"version": "0.4.0",
"version": "0.4.1",
"description": "WebDirt integration for Strudel",
"main": "index.mjs",
"type": "module",
@@ -22,7 +22,7 @@
},
"homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": {
"@strudel.cycles/core": "^0.4.0",
"@strudel.cycles/core": "^0.4.1",
"WebDirt": "github:dktr0/WebDirt"
}
}
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel.cycles/xen",
"version": "0.4.0",
"version": "0.4.1",
"description": "Xenharmonic API for strudel",
"main": "index.mjs",
"scripts": {
@@ -24,6 +24,6 @@
},
"homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": {
"@strudel.cycles/core": "^0.4.0"
"@strudel.cycles/core": "^0.4.1"
}
}
+3 -2
View File
@@ -3,13 +3,14 @@
"private": true,
"version": "0.0.0",
"scripts": {
"predev": "cd ${PWD}/../tutorial/ && npm run render",
"render-jsdoc": "cd ${PWD}/../tutorial/ && npm run render",
"predev": "npm run render-jsdoc",
"prebuild": "npm run render-jsdoc",
"dev": "vite --host",
"start": "vite",
"build": "vite build",
"preview": "vite preview",
"test": "vitest run --reporter verbose -v --no-isolate",
"snapshot": "vitest run -u --silent",
"add-license": "cat etc/agpl-header.txt ../docs/static/js/*LICENSE.txt > /tmp/strudel-license.txt && cp /tmp/strudel-license.txt ../docs/static/js/*LICENSE.txt",
"predeploy": "npm run build",
"deploy": "gh-pages -d ../docs",
+1 -1
View File
@@ -19,7 +19,7 @@ evalScope(
import('@strudel.cycles/osc'),
);
prebake();
// prebake();
export function MiniRepl({ tune }) {
return <_MiniRepl tune={tune} hideOutsideView={true} />;
+3
View File
@@ -10,6 +10,9 @@ import Tutorial from './tutorial.rendered.mdx';
// import ApiDoc from './ApiDoc';
import './style.scss';
import '@strudel.cycles/react/dist/style.css';
import { initAudioOnFirstClick } from '@strudel.cycles/webaudio';
initAudioOnFirstClick();
ReactDOM.render(
<React.StrictMode>
@@ -767,10 +767,10 @@ exports[`runs examples > example "dry" example index 0 1`] = `
exports[`runs examples > example "each" example index 0 1`] = `
[
"3/4 -> 1/1: {\\"note\\":\\"c3\\"}",
"1/2 -> 3/4: {\\"note\\":\\"d3\\"}",
"1/4 -> 1/2: {\\"note\\":\\"e3\\"}",
"0/1 -> 1/4: {\\"note\\":\\"g3\\"}",
"0/1 -> 1/4: {\\"note\\":\\"c3\\"}",
"1/4 -> 1/2: {\\"note\\":\\"d3\\"}",
"1/2 -> 3/4: {\\"note\\":\\"e3\\"}",
"3/4 -> 1/1: {\\"note\\":\\"g3\\"}",
"1/1 -> 5/4: {\\"note\\":\\"c3\\"}",
"5/4 -> 3/2: {\\"note\\":\\"d3\\"}",
"3/2 -> 7/4: {\\"note\\":\\"e3\\"}",
@@ -779,10 +779,10 @@ exports[`runs examples > example "each" example index 0 1`] = `
"9/4 -> 5/2: {\\"note\\":\\"d3\\"}",
"5/2 -> 11/4: {\\"note\\":\\"e3\\"}",
"11/4 -> 3/1: {\\"note\\":\\"g3\\"}",
"3/1 -> 13/4: {\\"note\\":\\"c3\\"}",
"13/4 -> 7/2: {\\"note\\":\\"d3\\"}",
"7/2 -> 15/4: {\\"note\\":\\"e3\\"}",
"15/4 -> 4/1: {\\"note\\":\\"g3\\"}",
"15/4 -> 4/1: {\\"note\\":\\"c3\\"}",
"7/2 -> 15/4: {\\"note\\":\\"d3\\"}",
"13/4 -> 7/2: {\\"note\\":\\"e3\\"}",
"3/1 -> 13/4: {\\"note\\":\\"g3\\"}",
]
`;
+14 -16
View File
@@ -253,7 +253,7 @@ Using round brackets, we can create rhythmical sub-divisions based on three para
The first parameter controls how may beats will be played.
The second parameter controls the total amount of segments the beats will be distributed over.
The third (optional) parameter controls the starting position for distributing the beats.
One popular Euclidian rhythm (going by various names, such as "Pop Clave") is "(3,8,1)" or simply "(3,8)",
One popular Euclidian rhythm (going by various names, such as "Pop Clave") is "(3,8,0)" or simply "(3,8)",
resulting in a rhythmical structure of "x ~ ~ x ~ ~ x ~" (3 beats over 8 segments, starting on position 1).
<MiniRepl tune={`note("e5(2,8) b4(3,8) d5(2,8) c5(3,8)").slow(4)`} />
@@ -358,7 +358,7 @@ For pitched sounds, you can use `note`, just like with synths:
<MiniRepl
tune={`samples({
"gtr": 'gtr/0001_cleanC.wav',
'gtr': 'gtr/0001_cleanC.wav',
}, 'github:tidalcycles/Dirt-Samples/master/');
note("g3 [bb3 c4] <g4 f4 eb4 f3>@2").s('gtr').gain(.5)`}
/>
@@ -368,7 +368,7 @@ If we want them to behave more like a synth, we can add `clip(1)`:
<MiniRepl
tune={`samples({
"gtr": 'gtr/0001_cleanC.wav',
'gtr': 'gtr/0001_cleanC.wav',
}, 'github:tidalcycles/Dirt-Samples/master/');
note("g3 [bb3 c4] <g4 f4 eb4 f3>@2").s('gtr').clip(1)
.gain(.5)`}
@@ -380,8 +380,8 @@ If we have 2 samples with different base pitches, we can make them in tune by sp
<MiniRepl
tune={`samples({
"gtr": 'gtr/0001_cleanC.wav',
"moog": { 'g3': 'moog/005_Mighty%20Moog%20G3.wav' },
'gtr': 'gtr/0001_cleanC.wav',
'moog': { 'g3': 'moog/005_Mighty%20Moog%20G3.wav' },
}, 'github:tidalcycles/Dirt-Samples/master/');
note("g3 [bb3 c4] <g4 f4 eb4 f3>@2").s("gtr,moog").clip(1)
.gain(.5)`}
@@ -393,7 +393,7 @@ We can also declare different samples for different regions of the keyboard:
<MiniRepl
tune={`samples({
"moog": {
'moog': {
'g2': 'moog/004_Mighty%20Moog%20G2.wav',
'g3': 'moog/005_Mighty%20Moog%20G3.wav',
'g4': 'moog/006_Mighty%20Moog%20G4.wav',
@@ -769,9 +769,9 @@ Together with layer, struct and voicings, this can be used to create a basic bac
<MiniRepl
tune={`"<C^7 A7b13 Dm7 G7>".layer(
x => x.voicings(['d3','g4']).struct("~ x"),
x => x.rootNotes(2).tone(synth(osc('sawtooth4')).chain(out()))
).note()`}
x => x.voicings(['d3','g4']).struct("~ x").note(),
x => x.rootNotes(2).note().s('sawtooth').cutoff(800)
)`}
/>
<!-- TODO: use range instead of octave. -->
@@ -786,17 +786,15 @@ Strudel also supports midi via [webmidi](https://npmjs.com/package/webmidi).
### midi(outputName?)
Make sure to have a midi device connected or to use an IAC Driver.
Either connect a midi device or use the IAC Driver (Mac) or Midi Through Port (Linux) for internal midi messages.
If no outputName is given, it uses the first midi output it finds.
Midi is currently not supported by the mini repl used here, but you can [open the midi example in the repl](https://strudel.tidalcycles.org/#c3RhY2soIjxDXjcgQTcgRG03IEc3PiIubS52b2ljaW5ncygpLCAnPEMzIEEyIEQzIEcyPicubSkKICAubWlkaSgp).
In the REPL, you will se a log of the available MIDI devices.
<!--<MiniRepl
<MiniRepl
tune={`stack("<C^7 A7 Dm7 G7>".voicings(), "<C3 A2 D3 G2>")
.midi()`}
/>-->
/>
In the console, you will see a log of the available MIDI devices as soon as you run the code, e.g. `Midi connected! Using "Midi Through Port-0".`
# Superdirt API