mirror of
https://github.com/eliasstepanik/strudel.git
synced 2026-01-13 14:48:32 +00:00
build
This commit is contained in:
parent
98f91607cd
commit
4487834591
@ -178,12 +178,41 @@ class Pattern {
|
||||
_stripContext() {
|
||||
return this._withEvent((event) => event.setContext({}));
|
||||
}
|
||||
withLocation(location) {
|
||||
withLocation(start, end) {
|
||||
const location = {
|
||||
start: {line: start[0], column: start[1], offset: start[2]},
|
||||
end: {line: end[0], column: end[1], offset: end[2]}
|
||||
};
|
||||
return this._withContext((context) => {
|
||||
const locations = (context.locations || []).concat([location]);
|
||||
return {...context, locations};
|
||||
});
|
||||
}
|
||||
withMiniLocation(start, end) {
|
||||
const offset = {
|
||||
start: {line: start[0], column: start[1], offset: start[2]},
|
||||
end: {line: end[0], column: end[1], offset: end[2]}
|
||||
};
|
||||
return this._withContext((context) => {
|
||||
let locations = context.locations || [];
|
||||
locations = locations.map(({start: start2, end: end2}) => {
|
||||
const colOffset = start2.line === 1 ? offset.start.column : 0;
|
||||
return {
|
||||
start: {
|
||||
...start2,
|
||||
line: start2.line - 1 + (offset.start.line - 1) + 1,
|
||||
column: start2.column - 1 + colOffset
|
||||
},
|
||||
end: {
|
||||
...end2,
|
||||
line: end2.line - 1 + (offset.start.line - 1) + 1,
|
||||
column: end2.column - 1 + colOffset
|
||||
}
|
||||
};
|
||||
});
|
||||
return {...context, locations};
|
||||
});
|
||||
}
|
||||
withValue(func) {
|
||||
return new Pattern((state) => this.query(state).map((hap) => hap.withValue(func)));
|
||||
}
|
||||
@ -705,27 +734,6 @@ Pattern.prototype.define = (name, func, options = {}) => {
|
||||
};
|
||||
Pattern.prototype.define("hush", (pat) => pat.hush(), {patternified: false, composable: true});
|
||||
Pattern.prototype.define("bypass", (pat) => pat.bypass(on), {patternified: true, composable: true});
|
||||
function withLocationOffset(pat, offset) {
|
||||
return pat._withContext((context) => {
|
||||
let locations = context.locations || [];
|
||||
locations = locations.map(({start, end}) => {
|
||||
const colOffset = start.line === 1 ? offset.start.column : 0;
|
||||
return {
|
||||
start: {
|
||||
...start,
|
||||
line: start.line - 1 + (offset.start.line - 1) + 1,
|
||||
column: start.column - 1 + colOffset
|
||||
},
|
||||
end: {
|
||||
...end,
|
||||
line: end.line - 1 + (offset.start.line - 1) + 1,
|
||||
column: end.column - 1 + colOffset
|
||||
}
|
||||
};
|
||||
});
|
||||
return {...context, locations};
|
||||
});
|
||||
}
|
||||
export {
|
||||
Fraction,
|
||||
TimeSpan,
|
||||
@ -763,6 +771,5 @@ export {
|
||||
struct,
|
||||
mask,
|
||||
invert,
|
||||
inv,
|
||||
withLocationOffset
|
||||
inv
|
||||
};
|
||||
|
||||
2
docs/dist/draw.js
vendored
2
docs/dist/draw.js
vendored
@ -7,7 +7,7 @@ export const getDrawContext = (id = "test-canvas") => {
|
||||
canvas.id = id;
|
||||
canvas.width = window.innerWidth;
|
||||
canvas.height = window.innerHeight;
|
||||
canvas.style = "pointer-events:none;width:100%;height:100%;position:fixed;top:0;left:0";
|
||||
canvas.style = "pointer-events:none;width:100%;height:100%;position:fixed;top:0;left:0;z-index:5";
|
||||
document.body.prepend(canvas);
|
||||
}
|
||||
return canvas.getContext("2d");
|
||||
|
||||
15
docs/dist/evaluate.js
vendored
15
docs/dist/evaluate.js
vendored
@ -13,7 +13,7 @@ import * as uiHelpers from "./ui.js";
|
||||
import * as drawHelpers from "./draw.js";
|
||||
import gist from "./gist.js";
|
||||
import shapeshifter from "./shapeshifter.js";
|
||||
import {minify} from "./parse.js";
|
||||
import {mini} from "./parse.js";
|
||||
import * as Tone from "../_snowpack/pkg/tone.js";
|
||||
import * as toneHelpers from "./tone.js";
|
||||
import * as voicingHelpers from "./voicings.js";
|
||||
@ -29,17 +29,16 @@ function hackLiteral(literal, names, func) {
|
||||
}
|
||||
hackLiteral(String, ["mini", "m"], bootstrapped.mini);
|
||||
hackLiteral(String, ["pure", "p"], bootstrapped.pure);
|
||||
Object.assign(globalThis, bootstrapped, Tone, toneHelpers, voicingHelpers, drawHelpers, uiHelpers, {gist, euclid});
|
||||
Object.assign(globalThis, bootstrapped, Tone, toneHelpers, voicingHelpers, drawHelpers, uiHelpers, {
|
||||
gist,
|
||||
euclid,
|
||||
mini
|
||||
});
|
||||
export const evaluate = async (code) => {
|
||||
const shapeshifted = shapeshifter(code);
|
||||
drawHelpers.cleanup();
|
||||
uiHelpers.cleanup();
|
||||
let evaluated = await eval(shapeshifted);
|
||||
if (typeof evaluated === "function") {
|
||||
evaluated = evaluated();
|
||||
}
|
||||
if (typeof evaluated === "string") {
|
||||
evaluated = strudel.withLocationOffset(minify(evaluated), {start: {line: 1, column: -1}});
|
||||
}
|
||||
if (evaluated?.constructor?.name !== "Pattern") {
|
||||
const message = `got "${typeof evaluated}" instead of pattern`;
|
||||
throw new Error(message + (typeof evaluated === "function" ? ", did you forget to call a function?" : "."));
|
||||
|
||||
9
docs/dist/midi.js
vendored
9
docs/dist/midi.js
vendored
@ -37,7 +37,14 @@ Pattern.prototype.midi = function(output, channel = 1) {
|
||||
if (!WebMidi.outputs.length) {
|
||||
throw new Error(`🔌 No MIDI devices found. Connect a device or enable IAC Driver.`);
|
||||
}
|
||||
const device = output ? outputByName(output) : WebMidi.outputs[0];
|
||||
let device;
|
||||
if (typeof output === "number") {
|
||||
device = WebMidi.outputs[output];
|
||||
} else if (typeof output === "string") {
|
||||
device = outputByName(output);
|
||||
} else {
|
||||
device = WebMidi.outputs[0];
|
||||
}
|
||||
if (!device) {
|
||||
throw new Error(`🔌 MIDI device '${output ? output : ""}' not found. Use one of ${WebMidi.outputs.map((o) => `'${o.name}'`).join(" | ")}`);
|
||||
}
|
||||
|
||||
2
docs/dist/parse.js
vendored
2
docs/dist/parse.js
vendored
@ -93,7 +93,7 @@ export function patternifyAST(ast) {
|
||||
}
|
||||
const {start, end} = ast.location_;
|
||||
const value = !isNaN(Number(ast.source_)) ? Number(ast.source_) : ast.source_;
|
||||
return pure(value).withLocation({start, end});
|
||||
return pure(value).withLocation([start.line, start.column, start.offset], [end.line, end.column, end.offset]);
|
||||
}
|
||||
return patternifyAST(ast.source_);
|
||||
case "stretch":
|
||||
|
||||
1
docs/dist/pianoroll.js
vendored
1
docs/dist/pianoroll.js
vendored
@ -13,6 +13,7 @@ Pattern.prototype.pianoroll = function({
|
||||
const height = h / midiRange;
|
||||
this.draw((ctx, events, t) => {
|
||||
ctx.fillStyle = background;
|
||||
ctx.clearRect(0, 0, w, h);
|
||||
ctx.fillRect(0, 0, w, h);
|
||||
events.forEach((event) => {
|
||||
const isActive = event.whole.begin <= t && event.whole.end >= t;
|
||||
|
||||
273
docs/dist/shapeshifter.js
vendored
273
docs/dist/shapeshifter.js
vendored
@ -6,7 +6,9 @@ import {
|
||||
IdentifierExpression,
|
||||
CallExpression,
|
||||
StaticMemberExpression,
|
||||
Script,
|
||||
ReturnStatement,
|
||||
ArrayExpression,
|
||||
LiteralNumericExpression,
|
||||
} from '../_snowpack/pkg/shift-ast.js';
|
||||
import codegen from '../_snowpack/pkg/shift-codegen.js';
|
||||
import * as strudel from '../_snowpack/link/strudel.js';
|
||||
@ -18,14 +20,8 @@ const isNote = (name) => /^[a-gC-G][bs]?[0-9]$/.test(name);
|
||||
const addLocations = true;
|
||||
export const addMiniLocations = true;
|
||||
|
||||
/*
|
||||
not supported for highlighting:
|
||||
- 'b3'.p
|
||||
- mini('b3') / m('b3')
|
||||
- 'b3'.m / 'b3'.mini
|
||||
*/
|
||||
|
||||
export default (code) => {
|
||||
export default (_code) => {
|
||||
const { code, addReturn } = wrapAsync(_code);
|
||||
const ast = parseScriptWithLocation(code);
|
||||
const artificialNodes = [];
|
||||
const parents = [];
|
||||
@ -37,23 +33,20 @@ export default (code) => {
|
||||
return node;
|
||||
}
|
||||
|
||||
// replace template string `xxx` with 'xxx'.m
|
||||
// replace template string `xxx` with mini(`xxx`)
|
||||
if (isBackTickString(node)) {
|
||||
const minified = getMinified(node.elements[0].rawValue);
|
||||
return wrapLocationOffset(minified, node, ast.locations, artificialNodes);
|
||||
return minifyWithLocation(node, node, ast.locations, artificialNodes);
|
||||
}
|
||||
|
||||
// allows to use top level strings, which are normally directives... but we don't need directives
|
||||
if (node.type === 'Script' && node.directives.length === 1 && !node.statements.length) {
|
||||
const minified = getMinified(node.directives[0].rawValue);
|
||||
const wrapped = wrapLocationOffset(minified, node.directives[0], ast.locations, artificialNodes);
|
||||
return new Script({ directives: [], statements: [wrapped] });
|
||||
if (node.directives?.length === 1 && !node.statements?.length) {
|
||||
const str = new LiteralStringExpression({ value: node.directives[0].rawValue });
|
||||
const wrapped = minifyWithLocation(str, node.directives[0], ast.locations, artificialNodes);
|
||||
return { ...node, directives: [], statements: [wrapped] };
|
||||
}
|
||||
|
||||
// replace double quote string "xxx" with 'xxx'.m
|
||||
// replace double quote string "xxx" with mini('xxx')
|
||||
if (isStringWithDoubleQuotes(node, ast.locations, code)) {
|
||||
const minified = getMinified(node.value);
|
||||
return wrapLocationOffset(minified, node, ast.locations, artificialNodes);
|
||||
return minifyWithLocation(node, node, ast.locations, artificialNodes);
|
||||
}
|
||||
|
||||
// operator overloading => still not done
|
||||
@ -87,7 +80,6 @@ export default (code) => {
|
||||
if (node.type === 'CallExpression' && node.callee.name === 'pure') {
|
||||
const literal = node.arguments[0];
|
||||
// const value = literal[{ LiteralNumericExpression: 'value', LiteralStringExpression: 'name' }[literal.type]];
|
||||
// console.log('value',value);
|
||||
return reifyWithLocation(literal, node.arguments[0], ast.locations, artificialNodes);
|
||||
}
|
||||
// replace pseudo note variables
|
||||
@ -103,28 +95,16 @@ export default (code) => {
|
||||
return new IdentifierExpression({ name: 'silence' });
|
||||
}
|
||||
}
|
||||
if (addLocations && node.type === 'LiteralStringExpression' && isMarkable) {
|
||||
// console.log('add', node);
|
||||
if (
|
||||
addLocations &&
|
||||
['LiteralStringExpression' /* , 'LiteralNumericExpression' */].includes(node.type) &&
|
||||
isMarkable
|
||||
) {
|
||||
// TODO: to make LiteralNumericExpression work, we need to make sure we're not inside timeCat...
|
||||
return reifyWithLocation(node, node, ast.locations, artificialNodes);
|
||||
}
|
||||
if (!addMiniLocations) {
|
||||
return wrapFunction('reify', node);
|
||||
}
|
||||
// mini notation location handling
|
||||
const miniFunctions = ['mini', 'm'];
|
||||
const isAlreadyWrapped = parent?.type === 'CallExpression' && parent.callee.name === 'withLocationOffset';
|
||||
if (node.type === 'CallExpression' && miniFunctions.includes(node.callee.name) && !isAlreadyWrapped) {
|
||||
// mini('c3')
|
||||
if (node.arguments.length > 1) {
|
||||
// TODO: transform mini(...args) to cat(...args.map(mini)) ?
|
||||
console.warn('multi arg mini locations not supported yet...');
|
||||
return node;
|
||||
}
|
||||
return wrapLocationOffset(node, node.arguments, ast.locations, artificialNodes);
|
||||
}
|
||||
if (node.type === 'StaticMemberExpression' && miniFunctions.includes(node.property) && !isAlreadyWrapped) {
|
||||
// 'c3'.mini or 'c3'.m
|
||||
return wrapLocationOffset(node, node.object, ast.locations, artificialNodes);
|
||||
if (addMiniLocations) {
|
||||
return addMiniNotationLocations(node, ast.locations, artificialNodes);
|
||||
}
|
||||
return node;
|
||||
},
|
||||
@ -132,9 +112,50 @@ export default (code) => {
|
||||
parents.pop();
|
||||
},
|
||||
});
|
||||
return codegen(shifted);
|
||||
// add return to last statement (because it's wrapped in an async function artificially)
|
||||
addReturn(shifted);
|
||||
const generated = codegen(shifted);
|
||||
return generated;
|
||||
};
|
||||
|
||||
function wrapAsync(code) {
|
||||
// wrap code in async to make await work on top level => this will create 1 line offset to locations
|
||||
// this is why line offset is -1 in getLocationObject calls below
|
||||
code = `(async () => {
|
||||
${code}
|
||||
})()`;
|
||||
const addReturn = (ast) => {
|
||||
const body = ast.statements[0].expression.callee.body; // actual code ast inside async function body
|
||||
body.statements = body.statements
|
||||
.slice(0, -1)
|
||||
.concat([new ReturnStatement({ expression: body.statements.slice(-1)[0] })]);
|
||||
};
|
||||
return {
|
||||
code,
|
||||
addReturn,
|
||||
};
|
||||
}
|
||||
|
||||
function addMiniNotationLocations(node, locations, artificialNodes) {
|
||||
const miniFunctions = ['mini', 'm'];
|
||||
// const isAlreadyWrapped = parent?.type === 'CallExpression' && parent.callee.name === 'withLocationOffset';
|
||||
if (node.type === 'CallExpression' && miniFunctions.includes(node.callee.name)) {
|
||||
// mini('c3')
|
||||
if (node.arguments.length > 1) {
|
||||
// TODO: transform mini(...args) to cat(...args.map(mini)) ?
|
||||
console.warn('multi arg mini locations not supported yet...');
|
||||
return node;
|
||||
}
|
||||
const str = node.arguments[0];
|
||||
return minifyWithLocation(str, str, locations, artificialNodes);
|
||||
}
|
||||
if (node.type === 'StaticMemberExpression' && miniFunctions.includes(node.property)) {
|
||||
// 'c3'.mini or 'c3'.m
|
||||
return minifyWithLocation(node.object, node, locations, artificialNodes);
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
function wrapFunction(name, ...args) {
|
||||
return new CallExpression({
|
||||
callee: new IdentifierExpression({ name }),
|
||||
@ -142,13 +163,6 @@ function wrapFunction(name, ...args) {
|
||||
});
|
||||
}
|
||||
|
||||
function getMinified(value) {
|
||||
return new StaticMemberExpression({
|
||||
object: new LiteralStringExpression({ value }),
|
||||
property: 'm',
|
||||
});
|
||||
}
|
||||
|
||||
function isBackTickString(node) {
|
||||
return node.type === 'TemplateExpression' && node.elements.length === 1;
|
||||
}
|
||||
@ -196,143 +210,52 @@ function canBeOverloaded(node) {
|
||||
// TODO: support sequence(c3).transpose(3).x.y.z
|
||||
}
|
||||
|
||||
// turn node into withLocationOffset(node, location)
|
||||
function wrapLocationOffset(node, stringNode, locations, artificialNodes) {
|
||||
// console.log('wrapppp', stringNode);
|
||||
const expression = {
|
||||
type: 'CallExpression',
|
||||
callee: {
|
||||
type: 'IdentifierExpression',
|
||||
name: 'withLocationOffset',
|
||||
},
|
||||
arguments: [node, getLocationObject(stringNode, locations)],
|
||||
};
|
||||
artificialNodes.push(expression);
|
||||
// console.log('wrapped', codegen(expression));
|
||||
return expression;
|
||||
}
|
||||
|
||||
// turns node in reify(value).withLocation(location), where location is the node's location in the source code
|
||||
// with this, the reified pattern can pass its location to the event, to know where to highlight when it's active
|
||||
function reifyWithLocation(literalNode, node, locations, artificialNodes) {
|
||||
const args = getLocationArguments(node, locations);
|
||||
const withLocation = new CallExpression({
|
||||
callee: new StaticMemberExpression({
|
||||
object: wrapFunction('reify', literalNode),
|
||||
property: 'withLocation',
|
||||
}),
|
||||
arguments: [getLocationObject(node, locations)],
|
||||
arguments: args,
|
||||
});
|
||||
artificialNodes.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(
|
||||
`reify(${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,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
// turns node in reify(value).withLocation(location), where location is the node's location in the source code
|
||||
// with this, the reified pattern can pass its location to the event, to know where to highlight when it's active
|
||||
function minifyWithLocation(literalNode, node, locations, artificialNodes) {
|
||||
const args = getLocationArguments(node, locations);
|
||||
const withLocation = new CallExpression({
|
||||
callee: new StaticMemberExpression({
|
||||
object: wrapFunction('mini', literalNode),
|
||||
property: 'withMiniLocation',
|
||||
}),
|
||||
arguments: args,
|
||||
});
|
||||
artificialNodes.push(withLocation);
|
||||
return withLocation;
|
||||
}
|
||||
|
||||
function getLocationArguments(node, locations) {
|
||||
const loc = locations.get(node);
|
||||
return [
|
||||
new ArrayExpression({
|
||||
elements: [
|
||||
new LiteralNumericExpression({ value: loc.start.line - 1 }), // the minus 1 assumes the code has been wrapped in async iife
|
||||
new LiteralNumericExpression({ value: loc.start.column }),
|
||||
new LiteralNumericExpression({ value: loc.start.offset }),
|
||||
],
|
||||
}),
|
||||
new ArrayExpression({
|
||||
elements: [
|
||||
new LiteralNumericExpression({ value: loc.end.line - 1 }), // the minus 1 assumes the code has been wrapped in async iife
|
||||
new LiteralNumericExpression({ value: loc.end.column }),
|
||||
new LiteralNumericExpression({ value: loc.end.offset }),
|
||||
],
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
||||
176
docs/dist/tunes.js
vendored
176
docs/dist/tunes.js
vendored
@ -335,12 +335,24 @@ stack(
|
||||
"c2 [c2 ~]*2".tone(synth(osc('sawtooth8')).chain(vol(0.8),out())),
|
||||
"c1*2".tone(membrane().chain(vol(0.8),out()))
|
||||
).slow(1)`;
|
||||
export const drums = `stack(
|
||||
export const synthDrums = `stack(
|
||||
"c1*2".tone(membrane().chain(vol(0.8),out())),
|
||||
"~ c3".tone(noise().chain(vol(0.8),out())),
|
||||
"c3*4".transpose("[-24 0]*2").tone(metal(adsr(0,.015)).chain(vol(0.8),out()))
|
||||
)
|
||||
`;
|
||||
export const sampleDrums = `const drums = await players({
|
||||
bd: 'bd/BT0A0D0.wav',
|
||||
sn: 'sn/ST0T0S3.wav',
|
||||
hh: 'hh/000_hh3closedhh.wav'
|
||||
}, 'https://loophole-letters.vercel.app/samples/tidal/')
|
||||
|
||||
stack(
|
||||
"<bd!3 bd(3,4,2)>",
|
||||
"hh*4",
|
||||
"~ <sn!3 sn(3,4,1)>"
|
||||
).tone(drums.chain(out()))
|
||||
`;
|
||||
export const xylophoneCalling = `const t = x => x.scaleTranspose("<0 2 4 3>/4").transpose(-2)
|
||||
const s = x => x.scale(slowcat('C3 minor pentatonic','G3 minor pentatonic').slow(4))
|
||||
const delay = new FeedbackDelay(1/8, .6).chain(vol(0.1), out());
|
||||
@ -411,69 +423,80 @@ stack(
|
||||
"<[c1@5 c1] <c1 [[c1@2 c1] ~] [c1 ~ c1] [c1!2 ~ c1!3]>>".tone(instr('kick')),
|
||||
"[2,4]/4".scale('D dorian').apply(t).tone(instr('pad')).mask("<x x x ~>/8")
|
||||
).fast(6/8)`;
|
||||
export const barryHarris = `piano()
|
||||
.then(p => "0,2,[7 6]"
|
||||
export const barryHarris = `backgroundImage(
|
||||
'https://media.npr.org/assets/img/2017/02/03/barryharris_600dpi_wide-7eb49998aa1af377d62bb098041624c0a0d1a454.jpg',
|
||||
{style:'background-size:cover'})
|
||||
|
||||
"0,2,[7 6]"
|
||||
.add("<0 1 2 3 4 5 7 8>")
|
||||
.scale('C bebop major')
|
||||
.transpose("<0 1 2 1>/8")
|
||||
.slow(2)
|
||||
.tone(p.toDestination()))
|
||||
.tone((await piano()).toDestination())
|
||||
`;
|
||||
export const blippyRhodes = `Promise.all([
|
||||
players({
|
||||
bd: 'samples/tidal/bd/BT0A0D0.wav',
|
||||
sn: 'samples/tidal/sn/ST0T0S3.wav',
|
||||
hh: 'samples/tidal/hh/000_hh3closedhh.wav'
|
||||
}, 'https://loophole-letters.vercel.app/'),
|
||||
sampler({
|
||||
E1: 'samples/rhodes/MK2Md2000.mp3',
|
||||
E2: 'samples/rhodes/MK2Md2012.mp3',
|
||||
E3: 'samples/rhodes/MK2Md2024.mp3',
|
||||
E4: 'samples/rhodes/MK2Md2036.mp3',
|
||||
E5: 'samples/rhodes/MK2Md2048.mp3',
|
||||
E6: 'samples/rhodes/MK2Md2060.mp3',
|
||||
E7: 'samples/rhodes/MK2Md2072.mp3'
|
||||
}, 'https://loophole-letters.vercel.app/')
|
||||
])
|
||||
.then(([drums, rhodes])=>{
|
||||
const delay = new FeedbackDelay(1/12, .4).chain(vol(0.3), out());
|
||||
rhodes = rhodes.chain(vol(0.5).connect(delay), out());
|
||||
const bass = synth(osc('sawtooth8')).chain(vol(.5),out());
|
||||
const scales = sequence('C major', 'C mixolydian', 'F lydian', ['F minor', slowcat('Db major','Db mixolydian')]).slow(4);
|
||||
const t = x => x.scale(scales);
|
||||
return stack(
|
||||
"<bd sn> <hh hh*2 hh*3>".tone(drums.chain(out())),
|
||||
"<g4 c5 a4 [ab4 <eb5 f5>]>".apply(t).struct("x*8").apply(scaleTranspose("0 [-5,-2] -7 [-9,-2]")).legato(.2).slow(2).tone(rhodes),
|
||||
//"<C^7 C7 F^7 [Fm7 <Db^7 Db7>]>".slow(2).voicings().struct("~ x").legato(.25).tone(rhodes),
|
||||
"<c2 c3 f2 [[F2 C2] db2]>".legato("<1@3 [.3 1]>").slow(2).tone(bass),
|
||||
).fast(3/2)
|
||||
})`;
|
||||
export const wavyKalimba = `sampler({
|
||||
export const blippyRhodes = `const delay = new FeedbackDelay(1/12, .4).chain(vol(0.3), out());
|
||||
|
||||
const drums = await players({
|
||||
bd: 'samples/tidal/bd/BT0A0D0.wav',
|
||||
sn: 'samples/tidal/sn/ST0T0S3.wav',
|
||||
hh: 'samples/tidal/hh/000_hh3closedhh.wav'
|
||||
}, 'https://loophole-letters.vercel.app/')
|
||||
|
||||
const rhodes = await sampler({
|
||||
E1: 'samples/rhodes/MK2Md2000.mp3',
|
||||
E2: 'samples/rhodes/MK2Md2012.mp3',
|
||||
E3: 'samples/rhodes/MK2Md2024.mp3',
|
||||
E4: 'samples/rhodes/MK2Md2036.mp3',
|
||||
E5: 'samples/rhodes/MK2Md2048.mp3',
|
||||
E6: 'samples/rhodes/MK2Md2060.mp3',
|
||||
E7: 'samples/rhodes/MK2Md2072.mp3'
|
||||
}, 'https://loophole-letters.vercel.app/')
|
||||
|
||||
const bass = synth(osc('sawtooth8')).chain(vol(.5),out())
|
||||
const scales = sequence('C major', 'C mixolydian', 'F lydian', ['F minor', slowcat('Db major','Db mixolydian')]).slow(4)
|
||||
|
||||
stack(
|
||||
"<bd sn> <hh hh*2 hh*3>"
|
||||
.tone(drums.chain(out())),
|
||||
"<g4 c5 a4 [ab4 <eb5 f5>]>"
|
||||
.scale(scales)
|
||||
.struct("x*8")
|
||||
.scaleTranspose("0 [-5,-2] -7 [-9,-2]")
|
||||
.legato(.3)
|
||||
.slow(2)
|
||||
.tone(rhodes.chain(vol(0.5).connect(delay), out())),
|
||||
//"<C^7 C7 F^7 [Fm7 <Db^7 Db7>]>".slow(2).voicings().struct("~ x").legato(.25).tone(rhodes),
|
||||
"<c2 c3 f2 [[F2 C2] db2]>"
|
||||
.legato("<1@3 [.3 1]>")
|
||||
.slow(2)
|
||||
.tone(bass),
|
||||
).fast(3/2)`;
|
||||
export const wavyKalimba = `const delay = new FeedbackDelay(1/3, .5).chain(vol(.2), out());
|
||||
let kalimba = await sampler({
|
||||
C5: 'https://freesound.org/data/previews/536/536549_11935698-lq.mp3'
|
||||
}).then((kalimba)=>{
|
||||
const delay = new FeedbackDelay(1/3, .5).chain(vol(.2), out());
|
||||
kalimba = kalimba.chain(vol(0.6).connect(delay),out());
|
||||
const scales = sequence('C major', 'C mixolydian', 'F lydian', ['F minor', 'Db major']).slow(4);
|
||||
return stack(
|
||||
"[0 2 4 6 9 2 0 -2]*3"
|
||||
.add("<0 2>/4")
|
||||
.scale(scales)
|
||||
.struct("x*8")
|
||||
.velocity("<.8 .3 .6>*8")
|
||||
.slow(2)
|
||||
.tone(kalimba),
|
||||
"<c2 c2 f2 [[F2 C2] db2]>"
|
||||
.scale(scales)
|
||||
.scaleTranspose("[0 <2 4>]*2")
|
||||
.struct("x*4")
|
||||
.velocity("<.8 .5>*4")
|
||||
.velocity(0.8)
|
||||
.slow(2)
|
||||
.tone(kalimba)
|
||||
)
|
||||
.legato("<.4 .8 1 1.2 1.4 1.6 1.8 2>/8")
|
||||
.fast(1)
|
||||
})`;
|
||||
})
|
||||
kalimba = kalimba.chain(vol(0.6).connect(delay),out());
|
||||
const scales = sequence('C major', 'C mixolydian', 'F lydian', ['F minor', 'Db major']).slow(4);
|
||||
|
||||
stack(
|
||||
"[0 2 4 6 9 2 0 -2]*3"
|
||||
.add("<0 2>/4")
|
||||
.scale(scales)
|
||||
.struct("x*8")
|
||||
.velocity("<.8 .3 .6>*8")
|
||||
.slow(2)
|
||||
.tone(kalimba),
|
||||
"<c2 c2 f2 [[F2 C2] db2]>"
|
||||
.scale(scales)
|
||||
.scaleTranspose("[0 <2 4>]*2")
|
||||
.struct("x*4")
|
||||
.velocity("<.8 .5>*4")
|
||||
.velocity(0.8)
|
||||
.slow(2)
|
||||
.tone(kalimba)
|
||||
)
|
||||
.legato("<.4 .8 1 1.2 1.4 1.6 1.8 2>/8")
|
||||
.fast(1)`;
|
||||
export const jemblung = `const delay = new FeedbackDelay(1/8, .6).chain(vol(0.15), out());
|
||||
const snare = noise({type:'white',...adsr(0,0.2,0)}).chain(lowpass(5000),vol(1.8),out());
|
||||
const s = polysynth().set({...osc('sawtooth4'),...adsr(0.01,.2,.6,0.2)}).chain(vol(.23).connect(delay),out());
|
||||
@ -500,8 +523,7 @@ stack(
|
||||
// hihat
|
||||
"c2*8".tone(noise().chain(highpass(6000),vol(0.5).connect(delay),out())),
|
||||
).slow(3)`;
|
||||
export const risingEnemy = `piano().then((p) =>
|
||||
stack(
|
||||
export const risingEnemy = `stack(
|
||||
"2,6"
|
||||
.scale('F3 dorian')
|
||||
.transpose(sine2.struct("x*64").slow(4).mul(2).round())
|
||||
@ -513,10 +535,9 @@ stack(
|
||||
.transpose("<0 1 2 1>/2".early(0.5))
|
||||
.transpose(5)
|
||||
.fast(2 / 3)
|
||||
.tone(p.toDestination())
|
||||
)`;
|
||||
.tone((await piano()).toDestination())`;
|
||||
export const festivalOfFingers = `const chords = "<Cm7 Fm7 G7 F#7>";
|
||||
piano().then(p=>stack(
|
||||
stack(
|
||||
chords.voicings().struct("x(3,8,-1)").velocity(.5).off(1/7,x=>x.transpose(12).velocity(.2)),
|
||||
chords.rootNotes(2).struct("x(4,8,-2)"),
|
||||
chords.rootNotes(4)
|
||||
@ -524,21 +545,18 @@ piano().then(p=>stack(
|
||||
.struct("x(3,8,-2)".fast(2))
|
||||
.scaleTranspose("0 4 0 6".early(".125 .5")).layer(scaleTranspose("0,<2 [4,6] [5,7]>/4"))
|
||||
).slow(2)
|
||||
//.pianoroll()
|
||||
.velocity(sine.struct("x*8").add(3/5).mul(2/5).fast(8))
|
||||
.tone(p.chain(out())))`;
|
||||
.tone((await piano()).chain(out()))`;
|
||||
export const festivalOfFingers2 = `const chords = "<Cm7 Fm7 G7 F#7 >";
|
||||
const scales = slowcat('C minor','F dorian','G dorian','F# mixolydian')
|
||||
piano().then(p=>stack(
|
||||
chords.voicings().struct("x(3,8,-1)").velocity(.5).off(1/7,x=>x.transpose(12).velocity(.2)),
|
||||
chords.rootNotes(2).struct("x(4,8)"),
|
||||
chords.rootNotes(4)
|
||||
.scale(scales)
|
||||
.struct("x(3,8,-2)".fast(2))
|
||||
.scaleTranspose("0 4 0 6".early(".125 .5")).layer(scaleTranspose("0,<2 [4,6] [5,7]>/3"))
|
||||
).slow(2).transpose(-1)
|
||||
.legato(cosine.struct("x*8").add(4/5).mul(4/5).fast(8))
|
||||
.velocity(sine.struct("x*8").add(3/5).mul(2/5).fast(8))
|
||||
// .pianoroll()
|
||||
.tone(p.chain(out())).fast(3/4)
|
||||
)`;
|
||||
const scales = slowcat('C minor','F dorian','G dorian','F# mixolydian')
|
||||
stack(
|
||||
chords.voicings().struct("x(3,8,-1)").velocity(.5).off(1/7,x=>x.transpose(12).velocity(.2)),
|
||||
chords.rootNotes(2).struct("x(4,8)"),
|
||||
chords.rootNotes(4)
|
||||
.scale(scales)
|
||||
.struct("x(3,8,-2)".fast(2))
|
||||
.scaleTranspose("0 4 0 6".early(".125 .5")).layer(scaleTranspose("0,<2 [4,6] [5,7]>/3"))
|
||||
).slow(2).transpose(-1)
|
||||
.legato(cosine.struct("x*8").add(4/5).mul(4/5).fast(8))
|
||||
.velocity(sine.struct("x*8").add(3/5).mul(2/5).fast(8))
|
||||
.tone((await piano()).chain(out())).fast(3/4)`;
|
||||
|
||||
5
docs/dist/ui.js
vendored
5
docs/dist/ui.js
vendored
@ -34,3 +34,8 @@ export const backgroundImage = function(src, animateOptions = {}) {
|
||||
handleOption(option, value(t));
|
||||
}));
|
||||
};
|
||||
export const cleanup = () => {
|
||||
const container = document.getElementById("code");
|
||||
container.style = "";
|
||||
container.className = "grow relative";
|
||||
};
|
||||
|
||||
14
docs/dist/useCycle.js
vendored
14
docs/dist/useCycle.js
vendored
@ -41,11 +41,19 @@ function useCycle(props) {
|
||||
Tone.getTransport().start("+0.1");
|
||||
};
|
||||
const stop = () => {
|
||||
console.log("stop");
|
||||
setStarted(false);
|
||||
Tone.getTransport().pause();
|
||||
setStarted(false);
|
||||
};
|
||||
const toggle = () => started ? stop() : start();
|
||||
return {start, stop, setStarted, onEvent, started, toggle, query, activeCycle};
|
||||
return {
|
||||
start,
|
||||
stop,
|
||||
onEvent,
|
||||
started,
|
||||
setStarted,
|
||||
toggle,
|
||||
query,
|
||||
activeCycle
|
||||
};
|
||||
}
|
||||
export default useCycle;
|
||||
|
||||
4
docs/dist/useRepl.js
vendored
4
docs/dist/useRepl.js
vendored
@ -20,13 +20,13 @@ function useRepl({tune, defaultSynth, autolink = true, onEvent, onDraw}) {
|
||||
const activateCode = async (_code = code) => {
|
||||
if (activeCode && !dirty) {
|
||||
setError(void 0);
|
||||
!cycle.started && cycle.start();
|
||||
cycle.start();
|
||||
return;
|
||||
}
|
||||
try {
|
||||
setPending(true);
|
||||
const parsed = await evaluate(_code);
|
||||
!cycle.started && cycle.start();
|
||||
cycle.start();
|
||||
broadcast({type: "start", from: id});
|
||||
setPattern(() => parsed.pattern);
|
||||
if (autolink) {
|
||||
|
||||
@ -1207,6 +1207,7 @@ body {
|
||||
height: 100% !important;
|
||||
background-color: transparent !important;
|
||||
font-size: 15px;
|
||||
z-index:20
|
||||
}
|
||||
|
||||
.CodeMirror-line > span {
|
||||
|
||||
@ -41771,13 +41771,13 @@ function useRepl({ tune , defaultSynth , autolink =true , onEvent , onDraw }) {
|
||||
const activateCode = async (_code = code)=>{
|
||||
if (activeCode && !dirty) {
|
||||
setError(undefined);
|
||||
!cycle1.started && cycle1.start();
|
||||
cycle1.start();
|
||||
return;
|
||||
}
|
||||
try {
|
||||
setPending(true);
|
||||
const parsed = await _evaluate.evaluate(_code);
|
||||
!cycle1.started && cycle1.start();
|
||||
cycle1.start();
|
||||
broadcast({
|
||||
type: 'start',
|
||||
from: id
|
||||
@ -42018,20 +42018,14 @@ hackLiteral(String, [
|
||||
// this will add everything to global scope, which is accessed by eval
|
||||
Object.assign(globalThis, bootstrapped, _tone1, _tone, _voicings, _drawMjs, _uiMjs, {
|
||||
gist: _gistJsDefault.default,
|
||||
euclid: _euclidMjsDefault.default
|
||||
euclid: _euclidMjsDefault.default,
|
||||
mini: _parse.mini
|
||||
});
|
||||
const evaluate = async (code)=>{
|
||||
const shapeshifted = _shapeshifterDefault.default(code); // transform syntactically correct js code to semantically usable code
|
||||
// console.log('shapeshifted', shapeshifted);
|
||||
_drawMjs.cleanup();
|
||||
_uiMjs.cleanup();
|
||||
let evaluated = await eval(shapeshifted);
|
||||
if (typeof evaluated === 'function') evaluated = evaluated();
|
||||
if (typeof evaluated === 'string') evaluated = _strudelMjs.withLocationOffset(_parse.minify(evaluated), {
|
||||
start: {
|
||||
line: 1,
|
||||
column: -1
|
||||
}
|
||||
});
|
||||
if (evaluated?.constructor?.name !== 'Pattern') {
|
||||
const message = `got "${typeof evaluated}" instead of pattern`;
|
||||
throw new Error(message + (typeof evaluated === 'function' ? ', did you forget to call a function?' : '.'));
|
||||
@ -42153,8 +42147,6 @@ parcelHelpers.export(exports, "invert", ()=>invert
|
||||
);
|
||||
parcelHelpers.export(exports, "inv", ()=>inv
|
||||
);
|
||||
parcelHelpers.export(exports, "withLocationOffset", ()=>withLocationOffset
|
||||
);
|
||||
var _fractionMjs = require("./fraction.mjs");
|
||||
var _fractionMjsDefault = parcelHelpers.interopDefault(_fractionMjs);
|
||||
var _ramda = require("ramda"); // will remove this as soon as compose is implemented here
|
||||
@ -42382,7 +42374,19 @@ class Pattern {
|
||||
})
|
||||
);
|
||||
}
|
||||
withLocation(location) {
|
||||
withLocation(start, end) {
|
||||
const location = {
|
||||
start: {
|
||||
line: start[0],
|
||||
column: start[1],
|
||||
offset: start[2]
|
||||
},
|
||||
end: {
|
||||
line: end[0],
|
||||
column: end[1],
|
||||
offset: end[2]
|
||||
}
|
||||
};
|
||||
return this._withContext((context)=>{
|
||||
const locations = (context.locations || []).concat([
|
||||
location
|
||||
@ -42393,6 +42397,42 @@ class Pattern {
|
||||
};
|
||||
});
|
||||
}
|
||||
withMiniLocation(start1, end1) {
|
||||
const offset = {
|
||||
start: {
|
||||
line: start1[0],
|
||||
column: start1[1],
|
||||
offset: start1[2]
|
||||
},
|
||||
end: {
|
||||
line: end1[0],
|
||||
column: end1[1],
|
||||
offset: end1[2]
|
||||
}
|
||||
};
|
||||
return this._withContext((context)=>{
|
||||
let locations = context.locations || [];
|
||||
locations = locations.map(({ start , end })=>{
|
||||
const colOffset = start.line === 1 ? offset.start.column : 0;
|
||||
return {
|
||||
start: {
|
||||
...start,
|
||||
line: start.line - 1 + (offset.start.line - 1) + 1,
|
||||
column: start.column - 1 + colOffset
|
||||
},
|
||||
end: {
|
||||
...end,
|
||||
line: end.line - 1 + (offset.start.line - 1) + 1,
|
||||
column: end.column - 1 + colOffset
|
||||
}
|
||||
};
|
||||
});
|
||||
return {
|
||||
...context,
|
||||
locations
|
||||
};
|
||||
});
|
||||
}
|
||||
withValue(func) {
|
||||
// Returns a new pattern, with the function applied to the value of
|
||||
// each event. It has the alias 'fmap'.
|
||||
@ -43118,31 +43158,6 @@ Pattern.prototype.define('bypass', (pat)=>pat.bypass(on)
|
||||
patternified: true,
|
||||
composable: true
|
||||
});
|
||||
// this is wrapped around mini patterns to offset krill parser location into the global js code space
|
||||
function withLocationOffset(pat, offset) {
|
||||
return pat._withContext((context)=>{
|
||||
let locations = context.locations || [];
|
||||
locations = locations.map(({ start , end })=>{
|
||||
const colOffset = start.line === 1 ? offset.start.column : 0;
|
||||
return {
|
||||
start: {
|
||||
...start,
|
||||
line: start.line - 1 + (offset.start.line - 1) + 1,
|
||||
column: start.column - 1 + colOffset
|
||||
},
|
||||
end: {
|
||||
...end,
|
||||
line: end.line - 1 + (offset.start.line - 1) + 1,
|
||||
column: end.column - 1 + colOffset
|
||||
}
|
||||
};
|
||||
});
|
||||
return {
|
||||
...context,
|
||||
locations
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
},{"./fraction.mjs":"8Ovmi","ramda":"10uzi","./util.mjs":"9Z602","@parcel/transformer-js/src/esmodule-helpers.js":"gkKU3"}],"8Ovmi":[function(require,module,exports) {
|
||||
var parcelHelpers = require("@parcel/transformer-js/src/esmodule-helpers.js");
|
||||
@ -59647,7 +59662,10 @@ Pattern.prototype.midi = function(output, channel = 1) {
|
||||
if (!_tone.isNote(note)) throw new Error('not a note: ' + note);
|
||||
if (!WebMidi.enabled) throw new Error(`🎹 WebMidi is not enabled. Supported Browsers: https://caniuse.com/?search=webmidi`);
|
||||
if (!WebMidi.outputs.length) throw new Error(`🔌 No MIDI devices found. Connect a device or enable IAC Driver.`);
|
||||
const device = output ? outputByName(output) : WebMidi.outputs[0];
|
||||
let device;
|
||||
if (typeof output === 'number') device = WebMidi.outputs[output];
|
||||
else if (typeof output === 'string') device = outputByName(output);
|
||||
else device = WebMidi.outputs[0];
|
||||
if (!device) throw new Error(`🔌 MIDI device '${output ? output : ''}' not found. Use one of ${WebMidi.outputs.map((o)=>`'${o.name}'`
|
||||
).join(' | ')}`);
|
||||
// console.log('midi', value, output);
|
||||
@ -136381,6 +136399,7 @@ _strudelMjs.Pattern.prototype.pianoroll = function({ timeframe =10 , inactive ='
|
||||
const height = h / midiRange;
|
||||
this.draw((ctx, events, t)=>{
|
||||
ctx.fillStyle = background;
|
||||
ctx.clearRect(0, 0, w, h);
|
||||
ctx.fillRect(0, 0, w, h);
|
||||
events.forEach((event)=>{
|
||||
const isActive = event.whole.begin <= t && event.whole.end >= t;
|
||||
@ -136413,7 +136432,7 @@ const getDrawContext = (id = 'test-canvas')=>{
|
||||
canvas.id = id;
|
||||
canvas.width = window.innerWidth;
|
||||
canvas.height = window.innerHeight;
|
||||
canvas.style = 'pointer-events:none;width:100%;height:100%;position:fixed;top:0;left:0';
|
||||
canvas.style = 'pointer-events:none;width:100%;height:100%;position:fixed;top:0;left:0;z-index:5';
|
||||
document.body.prepend(canvas);
|
||||
}
|
||||
return canvas.getContext('2d');
|
||||
@ -136455,6 +136474,8 @@ parcelHelpers.export(exports, "hideHeader", ()=>hideHeader
|
||||
);
|
||||
parcelHelpers.export(exports, "backgroundImage", ()=>backgroundImage
|
||||
);
|
||||
parcelHelpers.export(exports, "cleanup", ()=>cleanup
|
||||
);
|
||||
var _tone = require("tone");
|
||||
const hideHeader = ()=>{
|
||||
document.getElementById('header').style = 'display:none';
|
||||
@ -136493,6 +136514,11 @@ const backgroundImage = function(src, animateOptions = {
|
||||
})
|
||||
);
|
||||
};
|
||||
const cleanup = ()=>{
|
||||
const container = document.getElementById('code');
|
||||
container.style = '';
|
||||
container.className = 'grow relative'; // has to match App.tsx
|
||||
};
|
||||
|
||||
},{"tone":"2tCfN","@parcel/transformer-js/src/esmodule-helpers.js":"gkKU3"}],"fYZxP":[function(require,module,exports) {
|
||||
var parcelHelpers = require("@parcel/transformer-js/src/esmodule-helpers.js");
|
||||
@ -136520,7 +136546,8 @@ const isNote = (name)=>/^[a-gC-G][bs]?[0-9]$/.test(name)
|
||||
;
|
||||
const addLocations = true;
|
||||
const addMiniLocations = true;
|
||||
exports.default = (code)=>{
|
||||
exports.default = (_code)=>{
|
||||
const { code , addReturn } = wrapAsync(_code);
|
||||
const ast = _indexJs.parseScriptWithLocation(code);
|
||||
const artificialNodes = [];
|
||||
const parents = [];
|
||||
@ -136530,27 +136557,24 @@ exports.default = (code)=>{
|
||||
const isSynthetic = parents.some((p)=>artificialNodes.includes(p)
|
||||
);
|
||||
if (isSynthetic) return node;
|
||||
// replace template string `xxx` with 'xxx'.m
|
||||
if (isBackTickString(node)) {
|
||||
const minified = getMinified(node.elements[0].rawValue);
|
||||
return wrapLocationOffset(minified, node, ast.locations, artificialNodes);
|
||||
}
|
||||
// replace template string `xxx` with mini(`xxx`)
|
||||
if (isBackTickString(node)) return minifyWithLocation(node, node, ast.locations, artificialNodes);
|
||||
// allows to use top level strings, which are normally directives... but we don't need directives
|
||||
if (node.type === 'Script' && node.directives.length === 1 && !node.statements.length) {
|
||||
const minified = getMinified(node.directives[0].rawValue);
|
||||
const wrapped = wrapLocationOffset(minified, node.directives[0], ast.locations, artificialNodes);
|
||||
return new _shiftAst.Script({
|
||||
if (node.directives?.length === 1 && !node.statements?.length) {
|
||||
const str = new _shiftAst.LiteralStringExpression({
|
||||
value: node.directives[0].rawValue
|
||||
});
|
||||
const wrapped = minifyWithLocation(str, node.directives[0], ast.locations, artificialNodes);
|
||||
return {
|
||||
...node,
|
||||
directives: [],
|
||||
statements: [
|
||||
wrapped
|
||||
]
|
||||
});
|
||||
}
|
||||
// replace double quote string "xxx" with 'xxx'.m
|
||||
if (isStringWithDoubleQuotes(node, ast.locations, code)) {
|
||||
const minified = getMinified(node.value);
|
||||
return wrapLocationOffset(minified, node, ast.locations, artificialNodes);
|
||||
};
|
||||
}
|
||||
// replace double quote string "xxx" with mini('xxx')
|
||||
if (isStringWithDoubleQuotes(node, ast.locations, code)) return minifyWithLocation(node, node, ast.locations, artificialNodes);
|
||||
// operator overloading => still not done
|
||||
const operators = {
|
||||
'*': 'fast',
|
||||
@ -136580,7 +136604,6 @@ exports.default = (code)=>{
|
||||
if (node.type === 'CallExpression' && node.callee.name === 'pure') {
|
||||
const literal = node.arguments[0];
|
||||
// const value = literal[{ LiteralNumericExpression: 'value', LiteralStringExpression: 'name' }[literal.type]];
|
||||
// console.log('value',value);
|
||||
return reifyWithLocation(literal, node.arguments[0], ast.locations, artificialNodes);
|
||||
}
|
||||
// replace pseudo note variables
|
||||
@ -136598,34 +136621,61 @@ exports.default = (code)=>{
|
||||
name: 'silence'
|
||||
});
|
||||
}
|
||||
if (addLocations && node.type === 'LiteralStringExpression' && isMarkable) // console.log('add', node);
|
||||
if (addLocations && [
|
||||
'LiteralStringExpression' /* , 'LiteralNumericExpression' */
|
||||
].includes(node.type) && isMarkable) // TODO: to make LiteralNumericExpression work, we need to make sure we're not inside timeCat...
|
||||
return reifyWithLocation(node, node, ast.locations, artificialNodes);
|
||||
if (!addMiniLocations) return wrapFunction('reify', node);
|
||||
// mini notation location handling
|
||||
const miniFunctions = [
|
||||
'mini',
|
||||
'm'
|
||||
];
|
||||
const isAlreadyWrapped = parent?.type === 'CallExpression' && parent.callee.name === 'withLocationOffset';
|
||||
if (node.type === 'CallExpression' && miniFunctions.includes(node.callee.name) && !isAlreadyWrapped) {
|
||||
// mini('c3')
|
||||
if (node.arguments.length > 1) {
|
||||
// TODO: transform mini(...args) to cat(...args.map(mini)) ?
|
||||
console.warn('multi arg mini locations not supported yet...');
|
||||
return node;
|
||||
}
|
||||
return wrapLocationOffset(node, node.arguments, ast.locations, artificialNodes);
|
||||
}
|
||||
if (node.type === 'StaticMemberExpression' && miniFunctions.includes(node.property) && !isAlreadyWrapped) // 'c3'.mini or 'c3'.m
|
||||
return wrapLocationOffset(node, node.object, ast.locations, artificialNodes);
|
||||
if (addMiniLocations) return addMiniNotationLocations(node, ast.locations, artificialNodes);
|
||||
return node;
|
||||
},
|
||||
leave () {
|
||||
parents.pop();
|
||||
}
|
||||
});
|
||||
return _shiftCodegenDefault.default(shifted);
|
||||
// add return to last statement (because it's wrapped in an async function artificially)
|
||||
addReturn(shifted);
|
||||
const generated = _shiftCodegenDefault.default(shifted);
|
||||
return generated;
|
||||
};
|
||||
function wrapAsync(code) {
|
||||
// wrap code in async to make await work on top level => this will create 1 line offset to locations
|
||||
// this is why line offset is -1 in getLocationObject calls below
|
||||
code = `(async () => {
|
||||
${code}
|
||||
})()`;
|
||||
const addReturn = (ast)=>{
|
||||
const body = ast.statements[0].expression.callee.body; // actual code ast inside async function body
|
||||
body.statements = body.statements.slice(0, -1).concat([
|
||||
new _shiftAst.ReturnStatement({
|
||||
expression: body.statements.slice(-1)[0]
|
||||
})
|
||||
]);
|
||||
};
|
||||
return {
|
||||
code,
|
||||
addReturn
|
||||
};
|
||||
}
|
||||
function addMiniNotationLocations(node, locations, artificialNodes) {
|
||||
const miniFunctions = [
|
||||
'mini',
|
||||
'm'
|
||||
];
|
||||
// const isAlreadyWrapped = parent?.type === 'CallExpression' && parent.callee.name === 'withLocationOffset';
|
||||
if (node.type === 'CallExpression' && miniFunctions.includes(node.callee.name)) {
|
||||
// mini('c3')
|
||||
if (node.arguments.length > 1) {
|
||||
// TODO: transform mini(...args) to cat(...args.map(mini)) ?
|
||||
console.warn('multi arg mini locations not supported yet...');
|
||||
return node;
|
||||
}
|
||||
const str = node.arguments[0];
|
||||
return minifyWithLocation(str, str, locations, artificialNodes);
|
||||
}
|
||||
if (node.type === 'StaticMemberExpression' && miniFunctions.includes(node.property)) // 'c3'.mini or 'c3'.m
|
||||
return minifyWithLocation(node.object, node, locations, artificialNodes);
|
||||
return node;
|
||||
}
|
||||
function wrapFunction(name, ...args) {
|
||||
return new _shiftAst.CallExpression({
|
||||
callee: new _shiftAst.IdentifierExpression({
|
||||
@ -136634,14 +136684,6 @@ function wrapFunction(name, ...args) {
|
||||
arguments: args
|
||||
});
|
||||
}
|
||||
function getMinified(value) {
|
||||
return new _shiftAst.StaticMemberExpression({
|
||||
object: new _shiftAst.LiteralStringExpression({
|
||||
value
|
||||
}),
|
||||
property: 'm'
|
||||
});
|
||||
}
|
||||
function isBackTickString(node) {
|
||||
return node.type === 'TemplateExpression' && node.elements.length === 1;
|
||||
}
|
||||
@ -136673,145 +136715,64 @@ function canBeOverloaded(node) {
|
||||
return node.type === 'IdentifierExpression' && isNote(node.name) || isPatternFactory(node);
|
||||
// TODO: support sequence(c3).transpose(3).x.y.z
|
||||
}
|
||||
// turn node into withLocationOffset(node, location)
|
||||
function wrapLocationOffset(node, stringNode, locations, artificialNodes) {
|
||||
// console.log('wrapppp', stringNode);
|
||||
const expression = {
|
||||
type: 'CallExpression',
|
||||
callee: {
|
||||
type: 'IdentifierExpression',
|
||||
name: 'withLocationOffset'
|
||||
},
|
||||
arguments: [
|
||||
node,
|
||||
getLocationObject(stringNode, locations)
|
||||
]
|
||||
};
|
||||
artificialNodes.push(expression);
|
||||
// console.log('wrapped', codegen(expression));
|
||||
return expression;
|
||||
}
|
||||
// turns node in reify(value).withLocation(location), where location is the node's location in the source code
|
||||
// with this, the reified pattern can pass its location to the event, to know where to highlight when it's active
|
||||
function reifyWithLocation(literalNode, node, locations, artificialNodes) {
|
||||
const args = getLocationArguments(node, locations);
|
||||
const withLocation = new _shiftAst.CallExpression({
|
||||
callee: new _shiftAst.StaticMemberExpression({
|
||||
object: wrapFunction('reify', literalNode),
|
||||
property: 'withLocation'
|
||||
}),
|
||||
arguments: [
|
||||
getLocationObject(node, locations)
|
||||
]
|
||||
arguments: args
|
||||
});
|
||||
artificialNodes.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(
|
||||
`reify(${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
|
||||
}
|
||||
},
|
||||
]
|
||||
}
|
||||
},
|
||||
]
|
||||
};
|
||||
// turns node in reify(value).withLocation(location), where location is the node's location in the source code
|
||||
// with this, the reified pattern can pass its location to the event, to know where to highlight when it's active
|
||||
function minifyWithLocation(literalNode, node, locations, artificialNodes) {
|
||||
const args = getLocationArguments(node, locations);
|
||||
const withLocation = new _shiftAst.CallExpression({
|
||||
callee: new _shiftAst.StaticMemberExpression({
|
||||
object: wrapFunction('mini', literalNode),
|
||||
property: 'withMiniLocation'
|
||||
}),
|
||||
arguments: args
|
||||
});
|
||||
artificialNodes.push(withLocation);
|
||||
return withLocation;
|
||||
}
|
||||
function getLocationArguments(node, locations) {
|
||||
const loc = locations.get(node);
|
||||
return [
|
||||
new _shiftAst.ArrayExpression({
|
||||
elements: [
|
||||
new _shiftAst.LiteralNumericExpression({
|
||||
value: loc.start.line - 1
|
||||
}),
|
||||
new _shiftAst.LiteralNumericExpression({
|
||||
value: loc.start.column
|
||||
}),
|
||||
new _shiftAst.LiteralNumericExpression({
|
||||
value: loc.start.offset
|
||||
}),
|
||||
]
|
||||
}),
|
||||
new _shiftAst.ArrayExpression({
|
||||
elements: [
|
||||
new _shiftAst.LiteralNumericExpression({
|
||||
value: loc.end.line - 1
|
||||
}),
|
||||
new _shiftAst.LiteralNumericExpression({
|
||||
value: loc.end.column
|
||||
}),
|
||||
new _shiftAst.LiteralNumericExpression({
|
||||
value: loc.end.offset
|
||||
}),
|
||||
]
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
||||
},{"./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) {
|
||||
@ -169941,15 +169902,18 @@ function patternifyAST(ast) {
|
||||
return ast.source_;
|
||||
}
|
||||
const { start , end } = ast.location_;
|
||||
// should we really parse as number here? strings wont be fareyed...
|
||||
const value = !isNaN(Number(ast.source_)) ? Number(ast.source_) : ast.source_;
|
||||
// return ast.source_;
|
||||
// the following line expects the shapeshifter to wrap this in withLocationOffset
|
||||
// the following line expects the shapeshifter append .withMiniLocation
|
||||
// because location_ is only relative to the mini string, but we need it relative to whole code
|
||||
return pure(value).withLocation({
|
||||
start,
|
||||
end
|
||||
});
|
||||
return pure(value).withLocation([
|
||||
start.line,
|
||||
start.column,
|
||||
start.offset
|
||||
], [
|
||||
end.line,
|
||||
end.column,
|
||||
end.offset
|
||||
]);
|
||||
}
|
||||
return patternifyAST(ast.source_);
|
||||
case 'stretch':
|
||||
@ -171967,18 +171931,17 @@ function useCycle(props) {
|
||||
_tone.getTransport().start('+0.1');
|
||||
};
|
||||
const stop = ()=>{
|
||||
console.log('stop');
|
||||
setStarted(false);
|
||||
_tone.getTransport().pause();
|
||||
setStarted(false);
|
||||
};
|
||||
const toggle = ()=>started ? stop() : start()
|
||||
;
|
||||
return {
|
||||
start,
|
||||
stop,
|
||||
setStarted,
|
||||
onEvent,
|
||||
started,
|
||||
setStarted,
|
||||
toggle,
|
||||
query,
|
||||
activeCycle
|
||||
@ -183344,4 +183307,4 @@ exports.default = cx;
|
||||
|
||||
},{"@parcel/transformer-js/src/esmodule-helpers.js":"gkKU3"}]},["3uVTb"], "3uVTb", "parcelRequire94c2")
|
||||
|
||||
//# sourceMappingURL=index.f0b57381.js.map
|
||||
//# sourceMappingURL=index.ea7b3aa1.js.map
|
||||
File diff suppressed because one or more lines are too long
@ -11,6 +11,6 @@
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<script src="/tutorial/index.f0b57381.js" defer=""></script>
|
||||
<script src="/tutorial/index.ea7b3aa1.js" defer=""></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user