mirror of
https://github.com/eliasstepanik/strudel-docker.git
synced 2026-01-26 13:08:28 +00:00
build
This commit is contained in:
parent
1de371e944
commit
f8df4b485d
@ -175,6 +175,13 @@ class Pattern {
|
|||||||
_withEvents(func) {
|
_withEvents(func) {
|
||||||
return new Pattern((span) => func(this.query(span)));
|
return new Pattern((span) => func(this.query(span)));
|
||||||
}
|
}
|
||||||
|
withLocation(location) {
|
||||||
|
return this.fmap((value) => {
|
||||||
|
value = typeof value === "object" && !Array.isArray(value) ? value : {value};
|
||||||
|
const locations = (value.locations || []).concat([location]);
|
||||||
|
return {...value, locations};
|
||||||
|
});
|
||||||
|
}
|
||||||
withValue(func) {
|
withValue(func) {
|
||||||
return new Pattern((span) => this.query(span).map((hap) => hap.withValue(func)));
|
return new Pattern((span) => this.query(span).map((hap) => hap.withValue(func)));
|
||||||
}
|
}
|
||||||
@ -312,6 +319,7 @@ class Pattern {
|
|||||||
_patternify(func) {
|
_patternify(func) {
|
||||||
const pat = this;
|
const pat = this;
|
||||||
const patterned = function(...args) {
|
const patterned = function(...args) {
|
||||||
|
args = args.map((arg) => arg.constructor?.name === "Pattern" ? arg.fmap((value) => value.value || value) : arg);
|
||||||
const pat_arg = sequence(...args);
|
const pat_arg = sequence(...args);
|
||||||
return pat_arg.fmap((arg) => func.call(pat, arg)).outerJoin();
|
return pat_arg.fmap((arg) => func.call(pat, arg)).outerJoin();
|
||||||
};
|
};
|
||||||
|
|||||||
20
docs/dist/App.js
vendored
20
docs/dist/App.js
vendored
@ -1,4 +1,4 @@
|
|||||||
import React, {useCallback, useLayoutEffect, useRef} from "../_snowpack/pkg/react.js";
|
import React, {useCallback, useLayoutEffect, useMemo, useRef, useState} from "../_snowpack/pkg/react.js";
|
||||||
import * as Tone from "../_snowpack/pkg/tone.js";
|
import * as Tone from "../_snowpack/pkg/tone.js";
|
||||||
import CodeMirror from "./CodeMirror.js";
|
import CodeMirror from "./CodeMirror.js";
|
||||||
import cx from "./cx.js";
|
import cx from "./cx.js";
|
||||||
@ -28,9 +28,21 @@ function getRandomTune() {
|
|||||||
}
|
}
|
||||||
const randomTune = getRandomTune();
|
const randomTune = getRandomTune();
|
||||||
function App() {
|
function App() {
|
||||||
|
const [editor, setEditor] = useState();
|
||||||
|
const doc = useMemo(() => editor?.getDoc(), [editor]);
|
||||||
const {setCode, setPattern, error, code, cycle, dirty, log, togglePlay, activateCode, pattern, pushLog} = useRepl({
|
const {setCode, setPattern, error, code, cycle, dirty, log, togglePlay, activateCode, pattern, pushLog} = useRepl({
|
||||||
tune: decoded || randomTune,
|
tune: decoded || randomTune,
|
||||||
defaultSynth
|
defaultSynth,
|
||||||
|
onEvent: useCallback((event) => {
|
||||||
|
const locs = event.value.locations;
|
||||||
|
if (!locs) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const marks = locs.map(({start, end}) => doc.markText({line: start.line - 1, ch: start.column}, {line: end.line - 1, ch: end.column}, {css: "background-color: gray;"}));
|
||||||
|
setTimeout(() => {
|
||||||
|
marks.forEach((mark) => mark.clear());
|
||||||
|
}, event.duration * 0.9 * 1e3);
|
||||||
|
}, [doc])
|
||||||
});
|
});
|
||||||
const logBox = useRef();
|
const logBox = useRef();
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
@ -95,10 +107,12 @@ function App() {
|
|||||||
className: cx("h-full bg-[#2A3236]", error ? "focus:ring-red-500" : "focus:ring-slate-800")
|
className: cx("h-full bg-[#2A3236]", error ? "focus:ring-red-500" : "focus:ring-slate-800")
|
||||||
}, /* @__PURE__ */ React.createElement(CodeMirror, {
|
}, /* @__PURE__ */ React.createElement(CodeMirror, {
|
||||||
value: code,
|
value: code,
|
||||||
|
editorDidMount: setEditor,
|
||||||
options: {
|
options: {
|
||||||
mode: "javascript",
|
mode: "javascript",
|
||||||
theme: "material",
|
theme: "material",
|
||||||
lineNumbers: true
|
lineNumbers: true,
|
||||||
|
styleSelectedText: true
|
||||||
},
|
},
|
||||||
onChange: (_2, __, value) => setCode(value)
|
onChange: (_2, __, value) => setCode(value)
|
||||||
}), /* @__PURE__ */ React.createElement("span", {
|
}), /* @__PURE__ */ React.createElement("span", {
|
||||||
|
|||||||
8
docs/dist/CodeMirror.js
vendored
8
docs/dist/CodeMirror.js
vendored
@ -4,15 +4,17 @@ import "../_snowpack/pkg/codemirror/mode/javascript/javascript.js";
|
|||||||
import "../_snowpack/pkg/codemirror/mode/pegjs/pegjs.js";
|
import "../_snowpack/pkg/codemirror/mode/pegjs/pegjs.js";
|
||||||
import "../_snowpack/pkg/codemirror/theme/material.css.proxy.js";
|
import "../_snowpack/pkg/codemirror/theme/material.css.proxy.js";
|
||||||
import "../_snowpack/pkg/codemirror/lib/codemirror.css.proxy.js";
|
import "../_snowpack/pkg/codemirror/lib/codemirror.css.proxy.js";
|
||||||
export default function CodeMirror({value, onChange, options}) {
|
export default function CodeMirror({value, onChange, options, editorDidMount}) {
|
||||||
options = options || {
|
options = options || {
|
||||||
mode: "javascript",
|
mode: "javascript",
|
||||||
theme: "material",
|
theme: "material",
|
||||||
lineNumbers: true
|
lineNumbers: true,
|
||||||
|
styleSelectedText: true
|
||||||
};
|
};
|
||||||
return /* @__PURE__ */ React.createElement(CodeMirror2, {
|
return /* @__PURE__ */ React.createElement(CodeMirror2, {
|
||||||
value,
|
value,
|
||||||
options,
|
options,
|
||||||
onBeforeChange: onChange
|
onBeforeChange: onChange,
|
||||||
|
editorDidMount
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
169
docs/dist/shapeshifter.js
vendored
169
docs/dist/shapeshifter.js
vendored
@ -1,30 +1,189 @@
|
|||||||
import { parseScript } from './shift-parser/index.js'; // npm module does not work in the browser
|
import { parseScriptWithLocation } from './shift-parser/index.js'; // npm module does not work in the browser
|
||||||
import traverser from './shift-traverser/index.js'; // npm module does not work in the browser
|
import traverser from './shift-traverser/index.js'; // npm module does not work in the browser
|
||||||
const { replace } = traverser;
|
const { replace } = traverser;
|
||||||
import { LiteralStringExpression, IdentifierExpression } from '../_snowpack/pkg/shift-ast.js';
|
import { LiteralStringExpression, IdentifierExpression, CallExpression, StaticMemberExpression } from '../_snowpack/pkg/shift-ast.js';
|
||||||
import codegen from '../_snowpack/pkg/shift-codegen.js';
|
import codegen from '../_snowpack/pkg/shift-codegen.js';
|
||||||
|
import * as strudel from '../_snowpack/link/strudel.js';
|
||||||
|
|
||||||
|
const { Pattern } = strudel;
|
||||||
|
|
||||||
const isNote = (name) => /^[a-gC-G][bs]?[0-9]$/.test(name);
|
const isNote = (name) => /^[a-gC-G][bs]?[0-9]$/.test(name);
|
||||||
|
|
||||||
|
const addLocations = true;
|
||||||
|
|
||||||
export default (code) => {
|
export default (code) => {
|
||||||
const ast = parseScript(code);
|
const ast = parseScriptWithLocation(code);
|
||||||
const shifted = replace(ast, {
|
const nodesWithLocation = [];
|
||||||
|
const parents = [];
|
||||||
|
const shifted = replace(ast.tree, {
|
||||||
enter(node, parent) {
|
enter(node, parent) {
|
||||||
// replace identifiers that are a note with a note string
|
parents.push(parent);
|
||||||
|
const isSynthetic = parents.some((p) => nodesWithLocation.includes(p));
|
||||||
|
if (isSynthetic) {
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
const grandparent = parents[parents.length - 2];
|
||||||
|
const isPatternArg = (parent) =>
|
||||||
|
parent?.type === 'CallExpression' && Object.keys(Pattern.prototype.factories).includes(parent.callee.name);
|
||||||
|
const isTimeCat = parent?.type === 'ArrayExpression' && isPatternArg(grandparent);
|
||||||
|
const isMarkable = isPatternArg(parent) || isTimeCat;
|
||||||
|
// replace pseudo note variables
|
||||||
if (node.type === 'IdentifierExpression') {
|
if (node.type === 'IdentifierExpression') {
|
||||||
if (isNote(node.name)) {
|
if (isNote(node.name)) {
|
||||||
const value = node.name[1] === 's' ? node.name.replace('s', '#') : node.name;
|
const value = node.name[1] === 's' ? node.name.replace('s', '#') : node.name;
|
||||||
|
if (addLocations && isMarkable) {
|
||||||
|
return addPureWithLocation(value, node, ast.locations, nodesWithLocation);
|
||||||
|
}
|
||||||
return new LiteralStringExpression({ value });
|
return new LiteralStringExpression({ value });
|
||||||
}
|
}
|
||||||
if (node.name === 'r') {
|
if (node.name === 'r') {
|
||||||
return new IdentifierExpression({ name: 'silence' });
|
return new IdentifierExpression({ name: 'silence' });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (addLocations && node.type === 'LiteralStringExpression' && isMarkable) {
|
||||||
|
// console.log('add', node);
|
||||||
|
return addPureWithLocation(node.value, node, ast.locations, nodesWithLocation);
|
||||||
|
}
|
||||||
|
return node;
|
||||||
|
},
|
||||||
|
leave() {
|
||||||
|
parents.pop();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
return codegen(shifted);
|
return codegen(shifted);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// turns node in pure(value).withLocation(location), where location is the node's location in the source code
|
||||||
|
// with this, the pure pattern can pass its location to the event, to know where to highlight when it's active
|
||||||
|
function addPureWithLocation(value, node, locations, nodesWithLocation) {
|
||||||
|
// console.log('addPure', value, node);
|
||||||
|
const withLocation = new CallExpression({
|
||||||
|
callee: new StaticMemberExpression({
|
||||||
|
object: new CallExpression({
|
||||||
|
callee: new IdentifierExpression({
|
||||||
|
name: 'pure',
|
||||||
|
}),
|
||||||
|
arguments: [new LiteralStringExpression({ value })],
|
||||||
|
}),
|
||||||
|
property: 'withLocation',
|
||||||
|
}),
|
||||||
|
arguments: [getLocationObject(node, locations)],
|
||||||
|
});
|
||||||
|
nodesWithLocation.push(withLocation);
|
||||||
|
return withLocation;
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns ast for source location object
|
||||||
|
function getLocationObject(node, locations) {
|
||||||
|
/*const locationAST = parseScript(
|
||||||
|
"x=" + JSON.stringify(ast.locations.get(node))
|
||||||
|
).statements[0].expression.expression;
|
||||||
|
|
||||||
|
console.log("locationAST", locationAST);*/
|
||||||
|
|
||||||
|
/*const callAST = parseScript(
|
||||||
|
`pure(${node.name}).withLocation(${JSON.stringify(
|
||||||
|
ast.locations.get(node)
|
||||||
|
)})`
|
||||||
|
).statements[0].expression;*/
|
||||||
|
const loc = locations.get(node);
|
||||||
|
return {
|
||||||
|
type: 'ObjectExpression',
|
||||||
|
properties: [
|
||||||
|
{
|
||||||
|
type: 'DataProperty',
|
||||||
|
name: {
|
||||||
|
type: 'StaticPropertyName',
|
||||||
|
value: 'start',
|
||||||
|
},
|
||||||
|
expression: {
|
||||||
|
type: 'ObjectExpression',
|
||||||
|
properties: [
|
||||||
|
{
|
||||||
|
type: 'DataProperty',
|
||||||
|
name: {
|
||||||
|
type: 'StaticPropertyName',
|
||||||
|
value: 'line',
|
||||||
|
},
|
||||||
|
expression: {
|
||||||
|
type: 'LiteralNumericExpression',
|
||||||
|
value: loc.start.line,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'DataProperty',
|
||||||
|
name: {
|
||||||
|
type: 'StaticPropertyName',
|
||||||
|
value: 'column',
|
||||||
|
},
|
||||||
|
expression: {
|
||||||
|
type: 'LiteralNumericExpression',
|
||||||
|
value: loc.start.column,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'DataProperty',
|
||||||
|
name: {
|
||||||
|
type: 'StaticPropertyName',
|
||||||
|
value: 'offset',
|
||||||
|
},
|
||||||
|
expression: {
|
||||||
|
type: 'LiteralNumericExpression',
|
||||||
|
value: loc.start.offset,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'DataProperty',
|
||||||
|
name: {
|
||||||
|
type: 'StaticPropertyName',
|
||||||
|
value: 'end',
|
||||||
|
},
|
||||||
|
expression: {
|
||||||
|
type: 'ObjectExpression',
|
||||||
|
properties: [
|
||||||
|
{
|
||||||
|
type: 'DataProperty',
|
||||||
|
name: {
|
||||||
|
type: 'StaticPropertyName',
|
||||||
|
value: 'line',
|
||||||
|
},
|
||||||
|
expression: {
|
||||||
|
type: 'LiteralNumericExpression',
|
||||||
|
value: loc.end.line,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'DataProperty',
|
||||||
|
name: {
|
||||||
|
type: 'StaticPropertyName',
|
||||||
|
value: 'column',
|
||||||
|
},
|
||||||
|
expression: {
|
||||||
|
type: 'LiteralNumericExpression',
|
||||||
|
value: loc.end.column,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'DataProperty',
|
||||||
|
name: {
|
||||||
|
type: 'StaticPropertyName',
|
||||||
|
value: 'offset',
|
||||||
|
},
|
||||||
|
expression: {
|
||||||
|
type: 'LiteralNumericExpression',
|
||||||
|
value: loc.end.offset,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: turn x.groove['[~ x]*2'] into x.groove('[~ x]*2'.m)
|
// TODO: turn x.groove['[~ x]*2'] into x.groove('[~ x]*2'.m)
|
||||||
// and ['c1*2'].xx into 'c1*2'.m.xx ??
|
// and ['c1*2'].xx into 'c1*2'.m.xx ??
|
||||||
// or just all templated strings?? x.groove(`[~ x]*2`)
|
// or just all templated strings?? x.groove(`[~ x]*2`)
|
||||||
73
docs/dist/tunes.js
vendored
73
docs/dist/tunes.js
vendored
@ -4,11 +4,11 @@ export const timeCatMini = `stack(
|
|||||||
'[eb4@5 [f4 eb4 d4]@3] [eb4 c4]/2'.mini.slow(8)
|
'[eb4@5 [f4 eb4 d4]@3] [eb4 c4]/2'.mini.slow(8)
|
||||||
)`;
|
)`;
|
||||||
export const timeCat = `stack(
|
export const timeCat = `stack(
|
||||||
timeCat([3, c3], [1, stack(eb3, g3, m(c4, d4).slow(2))]),
|
timeCat([3, c3], [1, stack(eb3, g3, cat(c4, d4).slow(2))]),
|
||||||
m(c2, g2),
|
cat(c2, g2),
|
||||||
sequence(
|
sequence(
|
||||||
timeCat([5, eb4], [3, m(f4, eb4, d4)]),
|
timeCat([5, eb4], [3, cat(f4, eb4, d4)]),
|
||||||
m(eb4, c4).slow(2)
|
cat(eb4, c4).slow(2)
|
||||||
).slow(4)
|
).slow(4)
|
||||||
)`;
|
)`;
|
||||||
export const shapeShifted = `stack(
|
export const shapeShifted = `stack(
|
||||||
@ -120,11 +120,11 @@ export const spanish = `slowcat(
|
|||||||
stack(ab3,c4,eb4),
|
stack(ab3,c4,eb4),
|
||||||
stack(g3,b3,d4)
|
stack(g3,b3,d4)
|
||||||
)`;
|
)`;
|
||||||
export const whirlyStrudel = `mini("[e4 [b2 b3] c4]")
|
export const whirlyStrudel = `sequence(e4, [b2, b3], c4)
|
||||||
.every(4, fast(2))
|
.every(4, fast(2))
|
||||||
.every(3, slow(1.5))
|
.every(3, slow(1.5))
|
||||||
.fast(slowcat(1.25, 1, 1.5))
|
.fast(slowcat(1.25, 1, 1.5))
|
||||||
.every(2, _ => mini("e4 ~ e3 d4 ~"))`;
|
.every(2, _ => sequence(e4, r, e3, d4, r))`;
|
||||||
export const swimming = `stack(
|
export const swimming = `stack(
|
||||||
mini(
|
mini(
|
||||||
'~',
|
'~',
|
||||||
@ -357,64 +357,3 @@ export const caverave = `() => {
|
|||||||
synths
|
synths
|
||||||
).slow(2);
|
).slow(2);
|
||||||
}`;
|
}`;
|
||||||
export const caveravefuture = `() => {
|
|
||||||
const delay = new FeedbackDelay(1/8, .4).chain(vol(0.5), out);
|
|
||||||
const kick = new MembraneSynth().chain(vol(.8), out);
|
|
||||||
const snare = new NoiseSynth().chain(vol(.8), out);
|
|
||||||
const hihat = new MetalSynth().set(adsr(0, .08, 0, .1)).chain(vol(.3).connect(delay),out);
|
|
||||||
const bass = new Synth().set({ ...osc('sawtooth'), ...adsr(0, .1, .4) }).chain(lowpass(900), vol(.5), out);
|
|
||||||
const keys = new PolySynth().set({ ...osc('sawtooth'), ...adsr(0, .5, .2, .7) }).chain(lowpass(1200), vol(.5), out);
|
|
||||||
|
|
||||||
const drums = stack(
|
|
||||||
\`c1*2\`.tone(kick).bypass(\`<0@7 1>/8\`),
|
|
||||||
\`~ <x!7 [x@3 x]>\`.tone(snare).bypass(\`<0@7 1>/4\`),
|
|
||||||
\`[~ c4]*2\`.tone(hihat)
|
|
||||||
);
|
|
||||||
|
|
||||||
const thru = (x) => x.transpose(\`<0 1>/8\`).transpose(-1);
|
|
||||||
const synths = stack(
|
|
||||||
\`<eb4 d4 c4 b3>/2\`.scale(timeCat([3,'C minor'],[1,'C melodic minor']).slow(8)).groove(\`[~ x]*2\`)
|
|
||||||
.edit(
|
|
||||||
scaleTranspose(0).early(0),
|
|
||||||
scaleTranspose(2).early(1/8),
|
|
||||||
scaleTranspose(7).early(1/4),
|
|
||||||
scaleTranspose(8).early(3/8)
|
|
||||||
).edit(thru).tone(keys).bypass(\`<1 0>/16\`),
|
|
||||||
\`<C2 Bb1 Ab1 [G1 [G2 G1]]>/2\`.groove(\`x [~ x] <[~ [~ x]]!3 [x x]>@2\`).edit(thru).tone(bass),
|
|
||||||
\`<Cm7 Bb7 Fm7 G7b13>/2\`.groove(\`~ [x@0.5 ~]\`.fast(2)).voicings().edit(thru).every(2, early(1/8)).tone(keys).bypass(\`<0@7 1>/8\`.early(1/4)),
|
|
||||||
)
|
|
||||||
return stack(
|
|
||||||
drums.fast(2),
|
|
||||||
synths
|
|
||||||
).slow(2);
|
|
||||||
}`;
|
|
||||||
export const caveravefuture2 = `const delay = new FeedbackDelay(1/8, .4).chain(vol(0.5), out);
|
|
||||||
const kick = new MembraneSynth().chain(vol(.8), out);
|
|
||||||
const snare = new NoiseSynth().chain(vol(.8), out);
|
|
||||||
const hihat = new MetalSynth().set(adsr(0, .08, 0, .1)).chain(vol(.3).connect(delay),out);
|
|
||||||
const bass = new Synth().set({ ...osc('sawtooth'), ...adsr(0, .1, .4) }).chain(lowpass(900), vol(.5), out);
|
|
||||||
const keys = new PolySynth().set({ ...osc('sawtooth'), ...adsr(0, .5, .2, .7) }).chain(lowpass(1200), vol(.5), out);
|
|
||||||
|
|
||||||
const drums = stack(
|
|
||||||
"c1*2".tone(kick).bypass("<0@7 1>/8"),
|
|
||||||
"~ <x!7 [x@3 x]>".tone(snare).bypass("<0@7 1>/4"),
|
|
||||||
"[~ c4]*2".tone(hihat)
|
|
||||||
);
|
|
||||||
|
|
||||||
const thru = (x) => x.transpose("<0 1>/8").transpose(-1);
|
|
||||||
const synths = stack(
|
|
||||||
"<eb4 d4 c4 b3>/2".scale(timeCat([3, 'C minor'], [1, 'C melodic minor']).slow(8)).groove("[~ x]*2")
|
|
||||||
.edit(
|
|
||||||
scaleTranspose(0).early(0),
|
|
||||||
scaleTranspose(2).early(1/8),
|
|
||||||
scaleTranspose(7).early(1/4),
|
|
||||||
scaleTranspose(8).early(3/8)
|
|
||||||
).edit(thru).tone(keys).bypass("<1 0>/16"),
|
|
||||||
"<C2 Bb1 Ab1 [G1 [G2 G1]]>/2".groove("x [~ x] <[~ [~ x]]!3 [x x]>@2").edit(thru).tone(bass),
|
|
||||||
"<Cm7 Bb7 Fm7 G7b13>/2".groove("~ [x@0.5 ~]".fast(2)).voicings().edit(thru).every(2, early(1/8)).tone(keys).bypass("<0@7 1>/8".early(1/4)),
|
|
||||||
)
|
|
||||||
$: stack(
|
|
||||||
drums.fast(2),
|
|
||||||
synths
|
|
||||||
).slow(2);
|
|
||||||
`;
|
|
||||||
|
|||||||
5
docs/dist/useRepl.js
vendored
5
docs/dist/useRepl.js
vendored
@ -6,7 +6,7 @@ import usePostMessage from "./usePostMessage.js";
|
|||||||
let s4 = () => {
|
let s4 = () => {
|
||||||
return Math.floor((1 + Math.random()) * 65536).toString(16).substring(1);
|
return Math.floor((1 + Math.random()) * 65536).toString(16).substring(1);
|
||||||
};
|
};
|
||||||
function useRepl({tune, defaultSynth, autolink = true}) {
|
function useRepl({tune, defaultSynth, autolink = true, onEvent}) {
|
||||||
const id = useMemo(() => s4(), []);
|
const id = useMemo(() => s4(), []);
|
||||||
const [code, setCode] = useState(tune);
|
const [code, setCode] = useState(tune);
|
||||||
const [activeCode, setActiveCode] = useState();
|
const [activeCode, setActiveCode] = useState();
|
||||||
@ -43,6 +43,7 @@ function useRepl({tune, defaultSynth, autolink = true}) {
|
|||||||
const cycle = useCycle({
|
const cycle = useCycle({
|
||||||
onEvent: useCallback((time, event) => {
|
onEvent: useCallback((time, event) => {
|
||||||
try {
|
try {
|
||||||
|
onEvent?.(event);
|
||||||
if (!event.value?.onTrigger) {
|
if (!event.value?.onTrigger) {
|
||||||
const note = event.value?.value || event.value;
|
const note = event.value?.value || event.value;
|
||||||
if (!isNote(note)) {
|
if (!isNote(note)) {
|
||||||
@ -62,7 +63,7 @@ function useRepl({tune, defaultSynth, autolink = true}) {
|
|||||||
err.message = "unplayable event: " + err?.message;
|
err.message = "unplayable event: " + err?.message;
|
||||||
pushLog(err.message);
|
pushLog(err.message);
|
||||||
}
|
}
|
||||||
}, []),
|
}, [onEvent]),
|
||||||
onQuery: useCallback((span) => {
|
onQuery: useCallback((span) => {
|
||||||
try {
|
try {
|
||||||
return pattern?.query(span) || [];
|
return pattern?.query(span) || [];
|
||||||
|
|||||||
@ -41619,7 +41619,7 @@ var _usePostMessageDefault = parcelHelpers.interopDefault(_usePostMessage);
|
|||||||
let s4 = ()=>{
|
let s4 = ()=>{
|
||||||
return Math.floor((1 + Math.random()) * 65536).toString(16).substring(1);
|
return Math.floor((1 + Math.random()) * 65536).toString(16).substring(1);
|
||||||
};
|
};
|
||||||
function useRepl({ tune , defaultSynth , autolink =true }) {
|
function useRepl({ tune , defaultSynth , autolink =true , onEvent }) {
|
||||||
const id = _react.useMemo(()=>s4()
|
const id = _react.useMemo(()=>s4()
|
||||||
, []);
|
, []);
|
||||||
const [code, setCode] = _react.useState(tune);
|
const [code, setCode] = _react.useState(tune);
|
||||||
@ -41661,6 +41661,7 @@ function useRepl({ tune , defaultSynth , autolink =true }) {
|
|||||||
const cycle1 = _useCycleDefault.default({
|
const cycle1 = _useCycleDefault.default({
|
||||||
onEvent: _react.useCallback((time, event)=>{
|
onEvent: _react.useCallback((time, event)=>{
|
||||||
try {
|
try {
|
||||||
|
onEvent?.(event);
|
||||||
if (!event.value?.onTrigger) {
|
if (!event.value?.onTrigger) {
|
||||||
const note = event.value?.value || event.value;
|
const note = event.value?.value || event.value;
|
||||||
if (!_tone.isNote(note)) throw new Error('not a note: ' + note);
|
if (!_tone.isNote(note)) throw new Error('not a note: ' + note);
|
||||||
@ -41676,7 +41677,9 @@ function useRepl({ tune , defaultSynth , autolink =true }) {
|
|||||||
err.message = 'unplayable event: ' + err?.message;
|
err.message = 'unplayable event: ' + err?.message;
|
||||||
pushLog(err.message); // not with setError, because then we would have to setError(undefined) on next playable event
|
pushLog(err.message); // not with setError, because then we would have to setError(undefined) on next playable event
|
||||||
}
|
}
|
||||||
}, []),
|
}, [
|
||||||
|
onEvent
|
||||||
|
]),
|
||||||
onQuery: _react.useCallback((span)=>{
|
onQuery: _react.useCallback((span)=>{
|
||||||
try {
|
try {
|
||||||
return pattern?.query(span) || [];
|
return pattern?.query(span) || [];
|
||||||
@ -42107,6 +42110,20 @@ class Pattern {
|
|||||||
return new Pattern((span)=>func(this.query(span))
|
return new Pattern((span)=>func(this.query(span))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
withLocation(location) {
|
||||||
|
return this.fmap((value)=>{
|
||||||
|
value = typeof value === 'object' && !Array.isArray(value) ? value : {
|
||||||
|
value
|
||||||
|
};
|
||||||
|
const locations = (value.locations || []).concat([
|
||||||
|
location
|
||||||
|
]);
|
||||||
|
return {
|
||||||
|
...value,
|
||||||
|
locations
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
withValue(func) {
|
withValue(func) {
|
||||||
// Returns a new pattern, with the function applied to the value of
|
// Returns a new pattern, with the function applied to the value of
|
||||||
// each event. It has the alias 'fmap'.
|
// each event. It has the alias 'fmap'.
|
||||||
@ -42273,7 +42290,14 @@ class Pattern {
|
|||||||
_patternify(func) {
|
_patternify(func) {
|
||||||
const pat = this;
|
const pat = this;
|
||||||
const patterned = function(...args) {
|
const patterned = function(...args) {
|
||||||
|
// the problem here: args could a pattern that has been turned into an object to add location
|
||||||
|
// to avoid object checking for every pattern method, we can remove it here...
|
||||||
|
// in the future, patternified args should be marked as well + some better object handling
|
||||||
|
args = args.map((arg)=>arg.constructor?.name === 'Pattern' ? arg.fmap((value)=>value.value || value
|
||||||
|
) : arg
|
||||||
|
);
|
||||||
const pat_arg = sequence(...args);
|
const pat_arg = sequence(...args);
|
||||||
|
// arg.locations has to go somewhere..
|
||||||
return pat_arg.fmap((arg)=>func.call(pat, arg)
|
return pat_arg.fmap((arg)=>func.call(pat, arg)
|
||||||
).outerJoin();
|
).outerJoin();
|
||||||
};
|
};
|
||||||
@ -62538,17 +62562,32 @@ var _shiftTraverserDefault = parcelHelpers.interopDefault(_shiftTraverser);
|
|||||||
var _shiftAst = require("shift-ast");
|
var _shiftAst = require("shift-ast");
|
||||||
var _shiftCodegen = require("shift-codegen");
|
var _shiftCodegen = require("shift-codegen");
|
||||||
var _shiftCodegenDefault = parcelHelpers.interopDefault(_shiftCodegen);
|
var _shiftCodegenDefault = parcelHelpers.interopDefault(_shiftCodegen);
|
||||||
|
var _strudelMjs = require("../../strudel.mjs");
|
||||||
const { replace } = _shiftTraverserDefault.default;
|
const { replace } = _shiftTraverserDefault.default;
|
||||||
|
const { Pattern } = _strudelMjs;
|
||||||
const isNote = (name)=>/^[a-gC-G][bs]?[0-9]$/.test(name)
|
const isNote = (name)=>/^[a-gC-G][bs]?[0-9]$/.test(name)
|
||||||
;
|
;
|
||||||
|
const addLocations = true;
|
||||||
exports.default = (code)=>{
|
exports.default = (code)=>{
|
||||||
const ast = _indexJs.parseScript(code);
|
const ast = _indexJs.parseScriptWithLocation(code);
|
||||||
const shifted = replace(ast, {
|
const nodesWithLocation = [];
|
||||||
enter (node, parent) {
|
const parents = [];
|
||||||
// replace identifiers that are a note with a note string
|
const shifted = replace(ast.tree, {
|
||||||
|
enter (node, parent1) {
|
||||||
|
parents.push(parent1);
|
||||||
|
const isSynthetic = parents.some((p)=>nodesWithLocation.includes(p)
|
||||||
|
);
|
||||||
|
if (isSynthetic) return node;
|
||||||
|
const grandparent = parents[parents.length - 2];
|
||||||
|
const isPatternArg = (parent)=>parent?.type === 'CallExpression' && Object.keys(Pattern.prototype.factories).includes(parent.callee.name)
|
||||||
|
;
|
||||||
|
const isTimeCat = parent1?.type === 'ArrayExpression' && isPatternArg(grandparent);
|
||||||
|
const isMarkable = isPatternArg(parent1) || isTimeCat;
|
||||||
|
// replace pseudo note variables
|
||||||
if (node.type === 'IdentifierExpression') {
|
if (node.type === 'IdentifierExpression') {
|
||||||
if (isNote(node.name)) {
|
if (isNote(node.name)) {
|
||||||
const value = node.name[1] === 's' ? node.name.replace('s', '#') : node.name;
|
const value = node.name[1] === 's' ? node.name.replace('s', '#') : node.name;
|
||||||
|
if (addLocations && isMarkable) return addPureWithLocation(value, node, ast.locations, nodesWithLocation);
|
||||||
return new _shiftAst.LiteralStringExpression({
|
return new _shiftAst.LiteralStringExpression({
|
||||||
value
|
value
|
||||||
});
|
});
|
||||||
@ -62557,14 +62596,152 @@ exports.default = (code)=>{
|
|||||||
name: 'silence'
|
name: 'silence'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
if (addLocations && node.type === 'LiteralStringExpression' && isMarkable) // console.log('add', node);
|
||||||
|
return addPureWithLocation(node.value, node, ast.locations, nodesWithLocation);
|
||||||
|
return node;
|
||||||
|
},
|
||||||
|
leave () {
|
||||||
|
parents.pop();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return _shiftCodegenDefault.default(shifted);
|
return _shiftCodegenDefault.default(shifted);
|
||||||
}; // TODO: turn x.groove['[~ x]*2'] into x.groove('[~ x]*2'.m)
|
};
|
||||||
|
// turns node in pure(value).withLocation(location), where location is the node's location in the source code
|
||||||
|
// with this, the pure pattern can pass its location to the event, to know where to highlight when it's active
|
||||||
|
function addPureWithLocation(value, node, locations, nodesWithLocation) {
|
||||||
|
// console.log('addPure', value, node);
|
||||||
|
const withLocation = new _shiftAst.CallExpression({
|
||||||
|
callee: new _shiftAst.StaticMemberExpression({
|
||||||
|
object: new _shiftAst.CallExpression({
|
||||||
|
callee: new _shiftAst.IdentifierExpression({
|
||||||
|
name: 'pure'
|
||||||
|
}),
|
||||||
|
arguments: [
|
||||||
|
new _shiftAst.LiteralStringExpression({
|
||||||
|
value
|
||||||
|
})
|
||||||
|
]
|
||||||
|
}),
|
||||||
|
property: 'withLocation'
|
||||||
|
}),
|
||||||
|
arguments: [
|
||||||
|
getLocationObject(node, locations)
|
||||||
|
]
|
||||||
|
});
|
||||||
|
nodesWithLocation.push(withLocation);
|
||||||
|
return withLocation;
|
||||||
|
}
|
||||||
|
// returns ast for source location object
|
||||||
|
function getLocationObject(node, locations) {
|
||||||
|
/*const locationAST = parseScript(
|
||||||
|
"x=" + JSON.stringify(ast.locations.get(node))
|
||||||
|
).statements[0].expression.expression;
|
||||||
|
|
||||||
|
console.log("locationAST", locationAST);*/ /*const callAST = parseScript(
|
||||||
|
`pure(${node.name}).withLocation(${JSON.stringify(
|
||||||
|
ast.locations.get(node)
|
||||||
|
)})`
|
||||||
|
).statements[0].expression;*/ const loc = locations.get(node);
|
||||||
|
return {
|
||||||
|
type: 'ObjectExpression',
|
||||||
|
properties: [
|
||||||
|
{
|
||||||
|
type: 'DataProperty',
|
||||||
|
name: {
|
||||||
|
type: 'StaticPropertyName',
|
||||||
|
value: 'start'
|
||||||
|
},
|
||||||
|
expression: {
|
||||||
|
type: 'ObjectExpression',
|
||||||
|
properties: [
|
||||||
|
{
|
||||||
|
type: 'DataProperty',
|
||||||
|
name: {
|
||||||
|
type: 'StaticPropertyName',
|
||||||
|
value: 'line'
|
||||||
|
},
|
||||||
|
expression: {
|
||||||
|
type: 'LiteralNumericExpression',
|
||||||
|
value: loc.start.line
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'DataProperty',
|
||||||
|
name: {
|
||||||
|
type: 'StaticPropertyName',
|
||||||
|
value: 'column'
|
||||||
|
},
|
||||||
|
expression: {
|
||||||
|
type: 'LiteralNumericExpression',
|
||||||
|
value: loc.start.column
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'DataProperty',
|
||||||
|
name: {
|
||||||
|
type: 'StaticPropertyName',
|
||||||
|
value: 'offset'
|
||||||
|
},
|
||||||
|
expression: {
|
||||||
|
type: 'LiteralNumericExpression',
|
||||||
|
value: loc.start.offset
|
||||||
|
}
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'DataProperty',
|
||||||
|
name: {
|
||||||
|
type: 'StaticPropertyName',
|
||||||
|
value: 'end'
|
||||||
|
},
|
||||||
|
expression: {
|
||||||
|
type: 'ObjectExpression',
|
||||||
|
properties: [
|
||||||
|
{
|
||||||
|
type: 'DataProperty',
|
||||||
|
name: {
|
||||||
|
type: 'StaticPropertyName',
|
||||||
|
value: 'line'
|
||||||
|
},
|
||||||
|
expression: {
|
||||||
|
type: 'LiteralNumericExpression',
|
||||||
|
value: loc.end.line
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'DataProperty',
|
||||||
|
name: {
|
||||||
|
type: 'StaticPropertyName',
|
||||||
|
value: 'column'
|
||||||
|
},
|
||||||
|
expression: {
|
||||||
|
type: 'LiteralNumericExpression',
|
||||||
|
value: loc.end.column
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'DataProperty',
|
||||||
|
name: {
|
||||||
|
type: 'StaticPropertyName',
|
||||||
|
value: 'offset'
|
||||||
|
},
|
||||||
|
expression: {
|
||||||
|
type: 'LiteralNumericExpression',
|
||||||
|
value: loc.end.offset
|
||||||
|
}
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
]
|
||||||
|
};
|
||||||
|
} // TODO: turn x.groove['[~ x]*2'] into x.groove('[~ x]*2'.m)
|
||||||
// and ['c1*2'].xx into 'c1*2'.m.xx ??
|
// and ['c1*2'].xx into 'c1*2'.m.xx ??
|
||||||
// or just all templated strings?? x.groove(`[~ x]*2`)
|
// or just all templated strings?? x.groove(`[~ x]*2`)
|
||||||
|
|
||||||
},{"./shift-parser/index.js":"1kFzJ","./shift-traverser":"bogJs","shift-ast":"ig2Ca","shift-codegen":"1GOrI","@parcel/transformer-js/src/esmodule-helpers.js":"gkKU3"}],"1kFzJ":[function(require,module,exports) {
|
},{"./shift-parser/index.js":"1kFzJ","./shift-traverser":"bogJs","shift-ast":"ig2Ca","shift-codegen":"1GOrI","../../strudel.mjs":"ggZqJ","@parcel/transformer-js/src/esmodule-helpers.js":"gkKU3"}],"1kFzJ":[function(require,module,exports) {
|
||||||
var parcelHelpers = require("@parcel/transformer-js/src/esmodule-helpers.js");
|
var parcelHelpers = require("@parcel/transformer-js/src/esmodule-helpers.js");
|
||||||
parcelHelpers.defineInteropFlag(exports);
|
parcelHelpers.defineInteropFlag(exports);
|
||||||
parcelHelpers.export(exports, "parseModule", ()=>parseModule
|
parcelHelpers.export(exports, "parseModule", ()=>parseModule
|
||||||
@ -97727,16 +97904,18 @@ var _javascriptJs = require("codemirror/mode/javascript/javascript.js");
|
|||||||
var _pegjsJs = require("codemirror/mode/pegjs/pegjs.js");
|
var _pegjsJs = require("codemirror/mode/pegjs/pegjs.js");
|
||||||
var _materialCss = require("codemirror/theme/material.css");
|
var _materialCss = require("codemirror/theme/material.css");
|
||||||
var _codemirrorCss = require("codemirror/lib/codemirror.css");
|
var _codemirrorCss = require("codemirror/lib/codemirror.css");
|
||||||
function CodeMirror({ value , onChange , options }) {
|
function CodeMirror({ value , onChange , options , editorDidMount }) {
|
||||||
options = options || {
|
options = options || {
|
||||||
mode: 'javascript',
|
mode: 'javascript',
|
||||||
theme: 'material',
|
theme: 'material',
|
||||||
lineNumbers: true
|
lineNumbers: true,
|
||||||
|
styleSelectedText: true
|
||||||
};
|
};
|
||||||
return(/*#__PURE__*/ _jsxRuntime.jsx(_reactCodemirror2.Controlled, {
|
return(/*#__PURE__*/ _jsxRuntime.jsx(_reactCodemirror2.Controlled, {
|
||||||
value: value,
|
value: value,
|
||||||
options: options,
|
options: options,
|
||||||
onBeforeChange: onChange
|
onBeforeChange: onChange,
|
||||||
|
editorDidMount: editorDidMount
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
exports.default = CodeMirror;
|
exports.default = CodeMirror;
|
||||||
@ -108958,4 +109137,4 @@ exports.default = cx;
|
|||||||
|
|
||||||
},{"@parcel/transformer-js/src/esmodule-helpers.js":"gkKU3"}]},["3uVTb"], "3uVTb", "parcelRequire94c2")
|
},{"@parcel/transformer-js/src/esmodule-helpers.js":"gkKU3"}]},["3uVTb"], "3uVTb", "parcelRequire94c2")
|
||||||
|
|
||||||
//# sourceMappingURL=index.dc15e374.js.map
|
//# sourceMappingURL=index.98344030.js.map
|
||||||
File diff suppressed because one or more lines are too long
@ -11,6 +11,6 @@
|
|||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||||
<script src="/tutorial/index.dc15e374.js" defer=""></script>
|
<script src="/tutorial/index.98344030.js" defer=""></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user