Merge branch 'main' into stateful-events
This commit is contained in:
commit
0418517c3e
36 changed files with 2275 additions and 916 deletions
|
|
@ -1936,6 +1936,7 @@ function peg$parse(input, options) {
|
||||||
this.type_ = "element";
|
this.type_ = "element";
|
||||||
this.source_ = source;
|
this.source_ = source;
|
||||||
this.options_ = options;
|
this.options_ = options;
|
||||||
|
this.location_ = location();
|
||||||
}
|
}
|
||||||
|
|
||||||
var CommandStub = function(name, options)
|
var CommandStub = function(name, options)
|
||||||
|
|
|
||||||
|
|
@ -107,7 +107,7 @@ class TimeSpan {
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
get midpoint() {
|
midpoint() {
|
||||||
return this.begin.add(this.end.sub(this.begin).div(Fraction(2)));
|
return this.begin.add(this.end.sub(this.begin).div(Fraction(2)));
|
||||||
}
|
}
|
||||||
equals(other) {
|
equals(other) {
|
||||||
|
|
@ -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();
|
||||||
};
|
};
|
||||||
|
|
@ -428,6 +436,9 @@ class Pattern {
|
||||||
edit(...funcs) {
|
edit(...funcs) {
|
||||||
return stack(...funcs.map((func) => func(this)));
|
return stack(...funcs.map((func) => func(this)));
|
||||||
}
|
}
|
||||||
|
pipe(func) {
|
||||||
|
return func(this);
|
||||||
|
}
|
||||||
_bypass(on2) {
|
_bypass(on2) {
|
||||||
on2 = Boolean(parseInt(on2));
|
on2 = Boolean(parseInt(on2));
|
||||||
return on2 ? silence : this;
|
return on2 ? silence : this;
|
||||||
|
|
@ -448,6 +459,24 @@ function pure(value) {
|
||||||
function steady(value) {
|
function steady(value) {
|
||||||
return new Pattern((span) => Hap(void 0, span, value));
|
return new Pattern((span) => Hap(void 0, span, value));
|
||||||
}
|
}
|
||||||
|
export const signal = (func) => {
|
||||||
|
const query = (span) => [new Hap(void 0, span, func(span.midpoint()))];
|
||||||
|
return new Pattern(query);
|
||||||
|
};
|
||||||
|
const _toBipolar = (pat) => pat.fmap((x) => x * 2 - 1);
|
||||||
|
const _fromBipolar = (pat) => pat.fmap((x) => (x + 1) / 2);
|
||||||
|
export const sine2 = signal((t) => Math.sin(Math.PI * 2 * t));
|
||||||
|
export const sine = _fromBipolar(sine2);
|
||||||
|
export const cosine2 = sine2._early(0.25);
|
||||||
|
export const cosine = sine._early(0.25);
|
||||||
|
export const saw = signal((t) => t % 1);
|
||||||
|
export const saw2 = _toBipolar(saw);
|
||||||
|
export const isaw = signal((t) => 1 - t % 1);
|
||||||
|
export const isaw2 = _toBipolar(isaw);
|
||||||
|
export const tri2 = fastcat(isaw2, saw2);
|
||||||
|
export const tri = fastcat(isaw, saw);
|
||||||
|
export const square = signal((t) => Math.floor(t * 2 % 2));
|
||||||
|
export const square2 = _toBipolar(square);
|
||||||
function reify(thing) {
|
function reify(thing) {
|
||||||
if (thing?.constructor?.name == "Pattern") {
|
if (thing?.constructor?.name == "Pattern") {
|
||||||
return thing;
|
return thing;
|
||||||
|
|
@ -596,6 +625,28 @@ Pattern.prototype.bootstrap = () => {
|
||||||
}));
|
}));
|
||||||
return bootstrapped;
|
return bootstrapped;
|
||||||
};
|
};
|
||||||
|
function withLocationOffset(pat, offset) {
|
||||||
|
return pat.fmap((value) => {
|
||||||
|
value = typeof value === "object" && !Array.isArray(value) ? value : {value};
|
||||||
|
let locations = value.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 {...value, locations};
|
||||||
|
});
|
||||||
|
}
|
||||||
export {
|
export {
|
||||||
Fraction,
|
Fraction,
|
||||||
TimeSpan,
|
TimeSpan,
|
||||||
|
|
@ -633,5 +684,6 @@ export {
|
||||||
struct,
|
struct,
|
||||||
mask,
|
mask,
|
||||||
invert,
|
invert,
|
||||||
inv
|
inv,
|
||||||
|
withLocationOffset
|
||||||
};
|
};
|
||||||
|
|
|
||||||
13
docs/dist/App.js
vendored
13
docs/dist/App.js
vendored
|
|
@ -1,6 +1,6 @@
|
||||||
import React, {useCallback, useLayoutEffect, useRef} from "../_snowpack/pkg/react.js";
|
import React, {useCallback, useLayoutEffect, 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, {markEvent} from "./CodeMirror.js";
|
||||||
import cx from "./cx.js";
|
import cx from "./cx.js";
|
||||||
import {evaluate} from "./evaluate.js";
|
import {evaluate} from "./evaluate.js";
|
||||||
import logo from "./logo.svg.proxy.js";
|
import logo from "./logo.svg.proxy.js";
|
||||||
|
|
@ -28,9 +28,11 @@ function getRandomTune() {
|
||||||
}
|
}
|
||||||
const randomTune = getRandomTune();
|
const randomTune = getRandomTune();
|
||||||
function App() {
|
function App() {
|
||||||
|
const [editor, setEditor] = useState();
|
||||||
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(markEvent(editor), [editor])
|
||||||
});
|
});
|
||||||
const logBox = useRef();
|
const logBox = useRef();
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
|
|
@ -95,10 +97,13 @@ 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,
|
||||||
|
cursorBlinkRate: 0
|
||||||
},
|
},
|
||||||
onChange: (_2, __, value) => setCode(value)
|
onChange: (_2, __, value) => setCode(value)
|
||||||
}), /* @__PURE__ */ React.createElement("span", {
|
}), /* @__PURE__ */ React.createElement("span", {
|
||||||
|
|
|
||||||
21
docs/dist/CodeMirror.js
vendored
21
docs/dist/CodeMirror.js
vendored
|
|
@ -2,17 +2,30 @@ import React from "../_snowpack/pkg/react.js";
|
||||||
import {Controlled as CodeMirror2} from "../_snowpack/pkg/react-codemirror2.js";
|
import {Controlled as CodeMirror2} from "../_snowpack/pkg/react-codemirror2.js";
|
||||||
import "../_snowpack/pkg/codemirror/mode/javascript/javascript.js";
|
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/lib/codemirror.css.proxy.js";
|
import "../_snowpack/pkg/codemirror/lib/codemirror.css.proxy.js";
|
||||||
export default function CodeMirror({value, onChange, options}) {
|
import "../_snowpack/pkg/codemirror/theme/material.css.proxy.js";
|
||||||
|
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,
|
||||||
|
cursorBlinkRate: 500
|
||||||
};
|
};
|
||||||
return /* @__PURE__ */ React.createElement(CodeMirror2, {
|
return /* @__PURE__ */ React.createElement(CodeMirror2, {
|
||||||
value,
|
value,
|
||||||
options,
|
options,
|
||||||
onBeforeChange: onChange
|
onBeforeChange: onChange,
|
||||||
|
editorDidMount
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
export const markEvent = (editor) => (event) => {
|
||||||
|
const locs = event.value.locations;
|
||||||
|
if (!locs || !editor) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const marks = locs.map(({start, end}) => editor.getDoc().markText({line: start.line - 1, ch: start.column}, {line: end.line - 1, ch: end.column}, {css: "background-color: #FFCA28; color: black"}));
|
||||||
|
setTimeout(() => {
|
||||||
|
marks.forEach((mark) => mark.clear());
|
||||||
|
}, event.duration * 0.9 * 1e3);
|
||||||
|
};
|
||||||
|
|
|
||||||
13
docs/dist/evaluate.js
vendored
13
docs/dist/evaluate.js
vendored
|
|
@ -3,7 +3,6 @@ import "./tone.js";
|
||||||
import "./midi.js";
|
import "./midi.js";
|
||||||
import "./voicings.js";
|
import "./voicings.js";
|
||||||
import "./tonal.js";
|
import "./tonal.js";
|
||||||
import "./groove.js";
|
|
||||||
import shapeshifter from "./shapeshifter.js";
|
import shapeshifter from "./shapeshifter.js";
|
||||||
import {minify} from "./parse.js";
|
import {minify} from "./parse.js";
|
||||||
import * as Tone from "../_snowpack/pkg/tone.js";
|
import * as Tone from "../_snowpack/pkg/tone.js";
|
||||||
|
|
@ -27,10 +26,12 @@ export const evaluate = (code) => {
|
||||||
if (typeof evaluated === "function") {
|
if (typeof evaluated === "function") {
|
||||||
evaluated = evaluated();
|
evaluated = evaluated();
|
||||||
}
|
}
|
||||||
const pattern = minify(evaluated);
|
if (typeof evaluated === "string") {
|
||||||
if (pattern?.constructor?.name !== "Pattern") {
|
evaluated = strudel.withLocationOffset(minify(evaluated), {start: {line: 1, column: -1}});
|
||||||
const message = `got "${typeof pattern}" instead of pattern`;
|
|
||||||
throw new Error(message + (typeof pattern === "function" ? ", did you forget to call a function?" : "."));
|
|
||||||
}
|
}
|
||||||
return {mode: "javascript", pattern};
|
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?" : "."));
|
||||||
|
}
|
||||||
|
return {mode: "javascript", pattern: evaluated};
|
||||||
};
|
};
|
||||||
|
|
|
||||||
6
docs/dist/groove.js
vendored
6
docs/dist/groove.js
vendored
|
|
@ -1,6 +0,0 @@
|
||||||
import {Pattern as _Pattern} from "../_snowpack/link/strudel.js";
|
|
||||||
const Pattern = _Pattern;
|
|
||||||
Pattern.prototype.groove = function(groove) {
|
|
||||||
return groove.fmap(() => (v) => v).appLeft(this);
|
|
||||||
};
|
|
||||||
Pattern.prototype.define("groove", (groove, pat) => pat.groove(groove), {composable: true});
|
|
||||||
12
docs/dist/parse.js
vendored
12
docs/dist/parse.js
vendored
|
|
@ -1,6 +1,7 @@
|
||||||
import * as krill from "../_snowpack/link/repl/krill-parser.js";
|
import * as krill from "../_snowpack/link/repl/krill-parser.js";
|
||||||
import * as strudel from "../_snowpack/link/strudel.js";
|
import * as strudel from "../_snowpack/link/strudel.js";
|
||||||
import {Scale, Note, Interval} from "../_snowpack/pkg/@tonaljs/tonal.js";
|
import {Scale, Note, Interval} from "../_snowpack/pkg/@tonaljs/tonal.js";
|
||||||
|
import {addMiniLocations} from "./shapeshifter.js";
|
||||||
const {pure, Pattern, Fraction, stack, slowcat, sequence, timeCat, silence} = strudel;
|
const {pure, Pattern, Fraction, stack, slowcat, sequence, timeCat, silence} = strudel;
|
||||||
const applyOptions = (parent) => (pat, i) => {
|
const applyOptions = (parent) => (pat, i) => {
|
||||||
const ast = parent.source_[i];
|
const ast = parent.source_[i];
|
||||||
|
|
@ -39,6 +40,7 @@ function resolveReplications(ast) {
|
||||||
{
|
{
|
||||||
type_: "element",
|
type_: "element",
|
||||||
source_: child.source_,
|
source_: child.source_,
|
||||||
|
location_: child.location_,
|
||||||
options_: {
|
options_: {
|
||||||
operator: {
|
operator: {
|
||||||
type_: "stretch",
|
type_: "stretch",
|
||||||
|
|
@ -80,7 +82,15 @@ export function patternifyAST(ast) {
|
||||||
return silence;
|
return silence;
|
||||||
}
|
}
|
||||||
if (typeof ast.source_ !== "object") {
|
if (typeof ast.source_ !== "object") {
|
||||||
return ast.source_;
|
if (!addMiniLocations) {
|
||||||
|
return ast.source_;
|
||||||
|
}
|
||||||
|
if (!ast.location_) {
|
||||||
|
console.warn("no location for", ast);
|
||||||
|
return ast.source_;
|
||||||
|
}
|
||||||
|
const {start, end} = ast.location_;
|
||||||
|
return pure(ast.source_).withLocation({start, end});
|
||||||
}
|
}
|
||||||
return patternifyAST(ast.source_);
|
return patternifyAST(ast.source_);
|
||||||
case "stretch":
|
case "stretch":
|
||||||
|
|
|
||||||
328
docs/dist/shapeshifter.js
vendored
328
docs/dist/shapeshifter.js
vendored
|
|
@ -1,30 +1,342 @@
|
||||||
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,
|
||||||
|
Script,
|
||||||
|
} 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 const addMiniLocations = true;
|
||||||
|
|
||||||
|
/*
|
||||||
|
not supported for highlighting:
|
||||||
|
- 'b3'.p
|
||||||
|
- mini('b3') / m('b3')
|
||||||
|
- 'b3'.m / 'b3'.mini
|
||||||
|
*/
|
||||||
|
|
||||||
export default (code) => {
|
export default (code) => {
|
||||||
const ast = parseScript(code);
|
const ast = parseScriptWithLocation(code);
|
||||||
const shifted = replace(ast, {
|
const artificialNodes = [];
|
||||||
|
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) => 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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] });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 'xxx'.m
|
||||||
|
if (isStringWithDoubleQuotes(node, ast.locations, code)) {
|
||||||
|
const minified = getMinified(node.value);
|
||||||
|
return wrapLocationOffset(minified, node, ast.locations, artificialNodes);
|
||||||
|
}
|
||||||
|
|
||||||
|
// operator overloading => still not done
|
||||||
|
const operators = {
|
||||||
|
'*': 'fast',
|
||||||
|
'/': 'slow',
|
||||||
|
'&': 'stack',
|
||||||
|
'&&': 'append',
|
||||||
|
};
|
||||||
|
if (
|
||||||
|
node.type === 'BinaryExpression' &&
|
||||||
|
operators[node.operator] &&
|
||||||
|
['LiteralNumericExpression', 'LiteralStringExpression', 'IdentifierExpression'].includes(node.right?.type) &&
|
||||||
|
canBeOverloaded(node.left)
|
||||||
|
) {
|
||||||
|
let arg = node.left;
|
||||||
|
if (node.left.type === 'IdentifierExpression') {
|
||||||
|
arg = wrapFunction('reify', node.left);
|
||||||
|
}
|
||||||
|
return new CallExpression({
|
||||||
|
callee: new StaticMemberExpression({
|
||||||
|
property: operators[node.operator],
|
||||||
|
object: wrapFunction('reify', arg),
|
||||||
|
}),
|
||||||
|
arguments: [node.right],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const isMarkable = isPatternArg(parents) || hasModifierCall(parent);
|
||||||
|
// add to location to pure(x) calls
|
||||||
|
if (node.type === 'CallExpression' && node.callee.name === 'pure') {
|
||||||
|
return reifyWithLocation(node.arguments[0].name, node.arguments[0], ast.locations, artificialNodes);
|
||||||
|
}
|
||||||
|
// 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 reifyWithLocation(value, node, ast.locations, artificialNodes);
|
||||||
|
}
|
||||||
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 reifyWithLocation(node.value, 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);
|
||||||
|
}
|
||||||
|
return node;
|
||||||
|
},
|
||||||
|
leave() {
|
||||||
|
parents.pop();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
return codegen(shifted);
|
return codegen(shifted);
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: turn x.groove['[~ x]*2'] into x.groove('[~ x]*2'.m)
|
function wrapFunction(name, ...args) {
|
||||||
// and ['c1*2'].xx into 'c1*2'.m.xx ??
|
return new CallExpression({
|
||||||
// or just all templated strings?? x.groove(`[~ x]*2`)
|
callee: new IdentifierExpression({ name }),
|
||||||
|
arguments: args,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function getMinified(value) {
|
||||||
|
return new StaticMemberExpression({
|
||||||
|
object: new LiteralStringExpression({ value }),
|
||||||
|
property: 'm',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function isBackTickString(node) {
|
||||||
|
return node.type === 'TemplateExpression' && node.elements.length === 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isStringWithDoubleQuotes(node, locations, code) {
|
||||||
|
if (node.type !== 'LiteralStringExpression') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const loc = locations.get(node);
|
||||||
|
const snippet = code.slice(loc.start.offset, loc.end.offset);
|
||||||
|
return snippet[0] === '"'; // we can trust the end is also ", as the parsing did not fail
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns true if the given parents belong to a pattern argument node
|
||||||
|
// this is used to check if a node should receive a location for highlighting
|
||||||
|
function isPatternArg(parents) {
|
||||||
|
if (!parents.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const ancestors = parents.slice(0, -1);
|
||||||
|
const parent = parents[parents.length - 1];
|
||||||
|
if (isPatternFactory(parent)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (parent?.type === 'ArrayExpression') {
|
||||||
|
return isPatternArg(ancestors);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasModifierCall(parent) {
|
||||||
|
// TODO: modifiers are more than composables, for example every is not composable but should be seen as modifier..
|
||||||
|
// need all prototypes of Pattern
|
||||||
|
return (
|
||||||
|
parent?.type === 'StaticMemberExpression' && Object.keys(Pattern.prototype.composable).includes(parent.property)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isPatternFactory(node) {
|
||||||
|
return node?.type === 'CallExpression' && Object.keys(Pattern.prototype.factories).includes(node.callee.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
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(value, node, locations, artificialNodes) {
|
||||||
|
const withLocation = new CallExpression({
|
||||||
|
callee: new StaticMemberExpression({
|
||||||
|
object: wrapFunction('reify', new LiteralStringExpression({ value })),
|
||||||
|
property: 'withLocation',
|
||||||
|
}),
|
||||||
|
arguments: [getLocationObject(node, locations)],
|
||||||
|
});
|
||||||
|
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,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
||||||
11
docs/dist/tonal.js
vendored
11
docs/dist/tonal.js
vendored
|
|
@ -2,7 +2,7 @@ import {Note, Interval, Scale} from "../_snowpack/pkg/@tonaljs/tonal.js";
|
||||||
import {Pattern as _Pattern} from "../_snowpack/link/strudel.js";
|
import {Pattern as _Pattern} from "../_snowpack/link/strudel.js";
|
||||||
const Pattern = _Pattern;
|
const Pattern = _Pattern;
|
||||||
function toNoteEvent(event) {
|
function toNoteEvent(event) {
|
||||||
if (typeof event === "string") {
|
if (typeof event === "string" || typeof event === "number") {
|
||||||
return {value: event};
|
return {value: event};
|
||||||
}
|
}
|
||||||
if (event.value) {
|
if (event.value) {
|
||||||
|
|
@ -53,13 +53,20 @@ Pattern.prototype._mapNotes = function(func) {
|
||||||
Pattern.prototype._transpose = function(intervalOrSemitones) {
|
Pattern.prototype._transpose = function(intervalOrSemitones) {
|
||||||
return this._mapNotes(({value, scale}) => {
|
return this._mapNotes(({value, scale}) => {
|
||||||
const interval = !isNaN(Number(intervalOrSemitones)) ? Interval.fromSemitones(intervalOrSemitones) : String(intervalOrSemitones);
|
const interval = !isNaN(Number(intervalOrSemitones)) ? Interval.fromSemitones(intervalOrSemitones) : String(intervalOrSemitones);
|
||||||
|
if (typeof value === "number") {
|
||||||
|
const semitones = typeof interval === "string" ? Interval.semitones(interval) || 0 : interval;
|
||||||
|
return {value: value + semitones};
|
||||||
|
}
|
||||||
return {value: Note.transpose(value, interval), scale};
|
return {value: Note.transpose(value, interval), scale};
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
Pattern.prototype._scaleTranspose = function(offset) {
|
Pattern.prototype._scaleTranspose = function(offset) {
|
||||||
return this._mapNotes(({value, scale}) => {
|
return this._mapNotes(({value, scale}) => {
|
||||||
if (!scale) {
|
if (!scale) {
|
||||||
throw new Error("can only use scaleOffset after .scale");
|
throw new Error("can only use scaleTranspose after .scale");
|
||||||
|
}
|
||||||
|
if (typeof value !== "string") {
|
||||||
|
throw new Error("can only use scaleTranspose with notes");
|
||||||
}
|
}
|
||||||
return {value: scaleTranspose(scale, Number(offset), value), scale};
|
return {value: scaleTranspose(scale, Number(offset), value), scale};
|
||||||
});
|
});
|
||||||
|
|
|
||||||
397
docs/dist/tunes.js
vendored
397
docs/dist/tunes.js
vendored
|
|
@ -1,14 +1,14 @@
|
||||||
export const timeCatMini = `stack(
|
export const timeCatMini = `stack(
|
||||||
'c3@3 [eb3, g3, [c4 d4]/2]'.mini,
|
"c3@3 [eb3, g3, [c4 d4]/2]",
|
||||||
'c2 g2'.mini,
|
"c2 g2",
|
||||||
'[eb4@5 [f4 eb4 d4]@3] [eb4 c4]/2'.mini.slow(8)
|
"[eb4@5 [f4 eb4 d4]@3] [eb4 c4]/2".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(
|
||||||
|
|
@ -54,47 +54,47 @@ export const tetrisWithFunctions = `stack(sequence(
|
||||||
)
|
)
|
||||||
).slow(16)`;
|
).slow(16)`;
|
||||||
export const tetris = `stack(
|
export const tetris = `stack(
|
||||||
mini(
|
cat(
|
||||||
'e5 [b4 c5] d5 [c5 b4]',
|
"e5 [b4 c5] d5 [c5 b4]",
|
||||||
'a4 [a4 c5] e5 [d5 c5]',
|
"a4 [a4 c5] e5 [d5 c5]",
|
||||||
'b4 [~ c5] d5 e5',
|
"b4 [~ c5] d5 e5",
|
||||||
'c5 a4 a4 ~',
|
"c5 a4 a4 ~",
|
||||||
'[~ d5] [~ f5] a5 [g5 f5]',
|
"[~ d5] [~ f5] a5 [g5 f5]",
|
||||||
'e5 [~ c5] e5 [d5 c5]',
|
"e5 [~ c5] e5 [d5 c5]",
|
||||||
'b4 [b4 c5] d5 e5',
|
"b4 [b4 c5] d5 e5",
|
||||||
'c5 a4 a4 ~'
|
"c5 a4 a4 ~"
|
||||||
),
|
),
|
||||||
mini(
|
cat(
|
||||||
'e2 e3 e2 e3 e2 e3 e2 e3',
|
"e2 e3 e2 e3 e2 e3 e2 e3",
|
||||||
'a2 a3 a2 a3 a2 a3 a2 a3',
|
"a2 a3 a2 a3 a2 a3 a2 a3",
|
||||||
'g#2 g#3 g#2 g#3 e2 e3 e2 e3',
|
"g#2 g#3 g#2 g#3 e2 e3 e2 e3",
|
||||||
'a2 a3 a2 a3 a2 a3 b1 c2',
|
"a2 a3 a2 a3 a2 a3 b1 c2",
|
||||||
'd2 d3 d2 d3 d2 d3 d2 d3',
|
"d2 d3 d2 d3 d2 d3 d2 d3",
|
||||||
'c2 c3 c2 c3 c2 c3 c2 c3',
|
"c2 c3 c2 c3 c2 c3 c2 c3",
|
||||||
'b1 b2 b1 b2 e2 e3 e2 e3',
|
"b1 b2 b1 b2 e2 e3 e2 e3",
|
||||||
'a1 a2 a1 a2 a1 a2 a1 a2'
|
"a1 a2 a1 a2 a1 a2 a1 a2",
|
||||||
)
|
)
|
||||||
).slow(16)`;
|
).slow(16)`;
|
||||||
export const tetrisRev = `stack(
|
export const tetrisRev = `stack(
|
||||||
mini(
|
cat(
|
||||||
'e5 [b4 c5] d5 [c5 b4]',
|
"e5 [b4 c5] d5 [c5 b4]",
|
||||||
'a4 [a4 c5] e5 [d5 c5]',
|
"a4 [a4 c5] e5 [d5 c5]",
|
||||||
'b4 [~ c5] d5 e5',
|
"b4 [~ c5] d5 e5",
|
||||||
'c5 a4 a4 ~',
|
"c5 a4 a4 ~",
|
||||||
'[~ d5] [~ f5] a5 [g5 f5]',
|
"[~ d5] [~ f5] a5 [g5 f5]",
|
||||||
'e5 [~ c5] e5 [d5 c5]',
|
"e5 [~ c5] e5 [d5 c5]",
|
||||||
'b4 [b4 c5] d5 e5',
|
"b4 [b4 c5] d5 e5",
|
||||||
'c5 a4 a4 ~'
|
"c5 a4 a4 ~",
|
||||||
).rev(),
|
).rev(),
|
||||||
mini(
|
cat(
|
||||||
'e2 e3 e2 e3 e2 e3 e2 e3',
|
"e2 e3 e2 e3 e2 e3 e2 e3",
|
||||||
'a2 a3 a2 a3 a2 a3 a2 a3',
|
"a2 a3 a2 a3 a2 a3 a2 a3",
|
||||||
'g#2 g#3 g#2 g#3 e2 e3 e2 e3',
|
"g#2 g#3 g#2 g#3 e2 e3 e2 e3",
|
||||||
'a2 a3 a2 a3 a2 a3 b1 c2',
|
"a2 a3 a2 a3 a2 a3 b1 c2",
|
||||||
'd2 d3 d2 d3 d2 d3 d2 d3',
|
"d2 d3 d2 d3 d2 d3 d2 d3",
|
||||||
'c2 c3 c2 c3 c2 c3 c2 c3',
|
"c2 c3 c2 c3 c2 c3 c2 c3",
|
||||||
'b1 b2 b1 b2 e2 e3 e2 e3',
|
"b1 b2 b1 b2 e2 e3 e2 e3",
|
||||||
'a1 a2 a1 a2 a1 a2 a1 a2'
|
"a1 a2 a1 a2 a1 a2 a1 a2",
|
||||||
).rev()
|
).rev()
|
||||||
).slow(16)`;
|
).slow(16)`;
|
||||||
export const tetrisMini = `\`[[e5 [b4 c5] d5 [c5 b4]]
|
export const tetrisMini = `\`[[e5 [b4 c5] d5 [c5 b4]]
|
||||||
|
|
@ -112,7 +112,7 @@ export const tetrisMini = `\`[[e5 [b4 c5] d5 [c5 b4]]
|
||||||
[[d2 d3]*4]
|
[[d2 d3]*4]
|
||||||
[[c2 c3]*4]
|
[[c2 c3]*4]
|
||||||
[[b1 b2]*2 [e2 e3]*2]
|
[[b1 b2]*2 [e2 e3]*2]
|
||||||
[[a1 a2]*4]\`.mini.slow(16)
|
[[a1 a2]*4]\`.slow(16)
|
||||||
`;
|
`;
|
||||||
export const spanish = `slowcat(
|
export const spanish = `slowcat(
|
||||||
stack(c4,eb4,g4),
|
stack(c4,eb4,g4),
|
||||||
|
|
@ -120,123 +120,123 @@ 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(
|
cat(
|
||||||
'~',
|
"~",
|
||||||
'~',
|
"~",
|
||||||
'~',
|
"~",
|
||||||
'A5 [F5@2 C5] [D5@2 F5] F5',
|
"A5 [F5@2 C5] [D5@2 F5] F5",
|
||||||
'[C5@2 F5] [F5@2 C6] A5 G5',
|
"[C5@2 F5] [F5@2 C6] A5 G5",
|
||||||
'A5 [F5@2 C5] [D5@2 F5] F5',
|
"A5 [F5@2 C5] [D5@2 F5] F5",
|
||||||
'[C5@2 F5] [Bb5 A5 G5] F5@2',
|
"[C5@2 F5] [Bb5 A5 G5] F5@2",
|
||||||
'A5 [F5@2 C5] [D5@2 F5] F5',
|
"A5 [F5@2 C5] [D5@2 F5] F5",
|
||||||
'[C5@2 F5] [F5@2 C6] A5 G5',
|
"[C5@2 F5] [F5@2 C6] A5 G5",
|
||||||
'A5 [F5@2 C5] [D5@2 F5] F5',
|
"A5 [F5@2 C5] [D5@2 F5] F5",
|
||||||
'[C5@2 F5] [Bb5 A5 G5] F5@2',
|
"[C5@2 F5] [Bb5 A5 G5] F5@2",
|
||||||
'A5 [F5@2 C5] A5 F5',
|
"A5 [F5@2 C5] A5 F5",
|
||||||
'Ab5 [F5@2 Ab5] G5@2',
|
"Ab5 [F5@2 Ab5] G5@2",
|
||||||
'A5 [F5@2 C5] A5 F5',
|
"A5 [F5@2 C5] A5 F5",
|
||||||
'Ab5 [F5@2 C5] C6@2',
|
"Ab5 [F5@2 C5] C6@2",
|
||||||
'A5 [F5@2 C5] [D5@2 F5] F5',
|
"A5 [F5@2 C5] [D5@2 F5] F5",
|
||||||
'[C5@2 F5] [Bb5 A5 G5] F5@2'
|
"[C5@2 F5] [Bb5 A5 G5] F5@2"
|
||||||
),
|
),
|
||||||
mini(
|
cat(
|
||||||
'[F4,Bb4,D5] [[D4,G4,Bb4]@2 [Bb3,D4,F4]] [[G3,C4,E4]@2 [[Ab3,F4] [A3,Gb4]]] [Bb3,E4,G4]',
|
"[F4,Bb4,D5] [[D4,G4,Bb4]@2 [Bb3,D4,F4]] [[G3,C4,E4]@2 [[Ab3,F4] [A3,Gb4]]] [Bb3,E4,G4]",
|
||||||
'[~ [F3, A3, C3] [F3, A3, C3]] [~ [F3, A3, C3] [F3, A3, C3]] [~ [F3, Bb3, D3] [F3, Bb3, D3]] [~ [F3, Bb3, Db3] [F3, Bb3, Db3]]',
|
"[~ [F3, A3, C3] [F3, A3, C3]] [~ [F3, A3, C3] [F3, A3, C3]] [~ [F3, Bb3, D3] [F3, Bb3, D3]] [~ [F3, Bb3, Db3] [F3, Bb3, Db3]]",
|
||||||
'[~ [F3, A3, C3] [F3, A3, C3]] [~ [F3, A3, C3] [F3, A3, C3]] [~ [F3, Bb3, D3] [F3, Bb3, D3]] [~ [F3, B3, D3] [F3, B3, D3]]',
|
"[~ [F3, A3, C3] [F3, A3, C3]] [~ [F3, A3, C3] [F3, A3, C3]] [~ [F3, Bb3, D3] [F3, Bb3, D3]] [~ [F3, B3, D3] [F3, B3, D3]]",
|
||||||
'[~ [F3, A3, C3] [F3, A3, C3]] [~ [F3, A3, C3] [F3, A3, C3]] [~ [F3, Bb3, D3] [F3, Bb3, D3]] [~ [F3, B3, D3] [F3, B3, D3]]',
|
"[~ [F3, A3, C3] [F3, A3, C3]] [~ [F3, A3, C3] [F3, A3, C3]] [~ [F3, Bb3, D3] [F3, Bb3, D3]] [~ [F3, B3, D3] [F3, B3, D3]]",
|
||||||
'[~ [A3, C4, E4] [A3, C4, E4]] [~ [Ab3, C4, Eb4] [Ab3, C4, Eb4]] [~ [F3, Bb3, D3] [F3, Bb3, D3]] [~ [G3, C4, E4] [G3, C4, E4]]',
|
"[~ [A3, C4, E4] [A3, C4, E4]] [~ [Ab3, C4, Eb4] [Ab3, C4, Eb4]] [~ [F3, Bb3, D3] [F3, Bb3, D3]] [~ [G3, C4, E4] [G3, C4, E4]]",
|
||||||
'[~ [F3, A3, C4] [F3, A3, C4]] [~ [F3, A3, C4] [F3, A3, C4]] [~ [F3, Bb3, D3] [F3, Bb3, D3]] [~ [F3, B3, D3] [F3, B3, D3]]',
|
"[~ [F3, A3, C4] [F3, A3, C4]] [~ [F3, A3, C4] [F3, A3, C4]] [~ [F3, Bb3, D3] [F3, Bb3, D3]] [~ [F3, B3, D3] [F3, B3, D3]]",
|
||||||
'[~ [F3, Bb3, D4] [F3, Bb3, D4]] [~ [F3, Bb3, C4] [F3, Bb3, C4]] [~ [F3, A3, C4] [F3, A3, C4]] [~ [F3, A3, C4] [F3, A3, C4]]',
|
"[~ [F3, Bb3, D4] [F3, Bb3, D4]] [~ [F3, Bb3, C4] [F3, Bb3, C4]] [~ [F3, A3, C4] [F3, A3, C4]] [~ [F3, A3, C4] [F3, A3, C4]]",
|
||||||
'[~ [F3, A3, C3] [F3, A3, C3]] [~ [F3, A3, C3] [F3, A3, C3]] [~ [F3, Bb3, D3] [F3, Bb3, D3]] [~ [F3, B3, D3] [F3, B3, D3]]',
|
"[~ [F3, A3, C3] [F3, A3, C3]] [~ [F3, A3, C3] [F3, A3, C3]] [~ [F3, Bb3, D3] [F3, Bb3, D3]] [~ [F3, B3, D3] [F3, B3, D3]]",
|
||||||
'[~ [A3, C4, E4] [A3, C4, E4]] [~ [Ab3, C4, Eb4] [Ab3, C4, Eb4]] [~ [F3, Bb3, D3] [F3, Bb3, D3]] [~ [G3, C4, E4] [G3, C4, E4]]',
|
"[~ [A3, C4, E4] [A3, C4, E4]] [~ [Ab3, C4, Eb4] [Ab3, C4, Eb4]] [~ [F3, Bb3, D3] [F3, Bb3, D3]] [~ [G3, C4, E4] [G3, C4, E4]]",
|
||||||
'[~ [F3, A3, C3] [F3, A3, C3]] [~ [F3, A3, C3] [F3, A3, C3]] [~ [F3, Bb3, D3] [F3, Bb3, D3]] [~ [F3, B3, D3] [F3, B3, D3]]',
|
"[~ [F3, A3, C3] [F3, A3, C3]] [~ [F3, A3, C3] [F3, A3, C3]] [~ [F3, Bb3, D3] [F3, Bb3, D3]] [~ [F3, B3, D3] [F3, B3, D3]]",
|
||||||
'[~ [F3, Bb3, D4] [F3, Bb3, D4]] [~ [F3, Bb3, C4] [F3, Bb3, C4]] [~ [F3, A3, C4] [F3, A3, C4]] [~ [F3, A3, C4] [F3, A3, C4]]',
|
"[~ [F3, Bb3, D4] [F3, Bb3, D4]] [~ [F3, Bb3, C4] [F3, Bb3, C4]] [~ [F3, A3, C4] [F3, A3, C4]] [~ [F3, A3, C4] [F3, A3, C4]]",
|
||||||
'[~ [Bb3, D3, F4] [Bb3, D3, F4]] [~ [Bb3, D3, F4] [Bb3, D3, F4]] [~ [A3, C4, F4] [A3, C4, F4]] [~ [A3, C4, F4] [A3, C4, F4]]',
|
"[~ [Bb3, D3, F4] [Bb3, D3, F4]] [~ [Bb3, D3, F4] [Bb3, D3, F4]] [~ [A3, C4, F4] [A3, C4, F4]] [~ [A3, C4, F4] [A3, C4, F4]]",
|
||||||
'[~ [Ab3, B3, F4] [Ab3, B3, F4]] [~ [Ab3, B3, F4] [Ab3, B3, F4]] [~ [G3, Bb3, F4] [G3, Bb3, F4]] [~ [G3, Bb3, E4] [G3, Bb3, E4]]',
|
"[~ [Ab3, B3, F4] [Ab3, B3, F4]] [~ [Ab3, B3, F4] [Ab3, B3, F4]] [~ [G3, Bb3, F4] [G3, Bb3, F4]] [~ [G3, Bb3, E4] [G3, Bb3, E4]]",
|
||||||
'[~ [Bb3, D3, F4] [Bb3, D3, F4]] [~ [Bb3, D3, F4] [Bb3, D3, F4]] [~ [A3, C4, F4] [A3, C4, F4]] [~ [A3, C4, F4] [A3, C4, F4]]',
|
"[~ [Bb3, D3, F4] [Bb3, D3, F4]] [~ [Bb3, D3, F4] [Bb3, D3, F4]] [~ [A3, C4, F4] [A3, C4, F4]] [~ [A3, C4, F4] [A3, C4, F4]]",
|
||||||
'[~ [Ab3, B3, F4] [Ab3, B3, F4]] [~ [Ab3, B3, F4] [Ab3, B3, F4]] [~ [G3, Bb3, F4] [G3, Bb3, F4]] [~ [G3, Bb3, E4] [G3, Bb3, E4]]',
|
"[~ [Ab3, B3, F4] [Ab3, B3, F4]] [~ [Ab3, B3, F4] [Ab3, B3, F4]] [~ [G3, Bb3, F4] [G3, Bb3, F4]] [~ [G3, Bb3, E4] [G3, Bb3, E4]]",
|
||||||
'[~ [F3, A3, C3] [F3, A3, C3]] [~ [F3, A3, C3] [F3, A3, C3]] [~ [F3, Bb3, D3] [F3, Bb3, D3]] [~ [F3, B3, D3] [F3, B3, D3]]',
|
"[~ [F3, A3, C3] [F3, A3, C3]] [~ [F3, A3, C3] [F3, A3, C3]] [~ [F3, Bb3, D3] [F3, Bb3, D3]] [~ [F3, B3, D3] [F3, B3, D3]]",
|
||||||
'[~ [F3, Bb3, D4] [F3, Bb3, D4]] [~ [F3, Bb3, C4] [F3, Bb3, C4]] [~ [F3, A3, C4] [F3, A3, C4]] [~ [F3, A3, C4] [F3, A3, C4]]'
|
"[~ [F3, Bb3, D4] [F3, Bb3, D4]] [~ [F3, Bb3, C4] [F3, Bb3, C4]] [~ [F3, A3, C4] [F3, A3, C4]] [~ [F3, A3, C4] [F3, A3, C4]]"
|
||||||
),
|
),
|
||||||
mini(
|
cat(
|
||||||
'[G3 G3 C3 E3]',
|
"[G3 G3 C3 E3]",
|
||||||
'[F2 D2 G2 C2]',
|
"[F2 D2 G2 C2]",
|
||||||
'[F2 D2 G2 C2]',
|
"[F2 D2 G2 C2]",
|
||||||
'[F2 A2 Bb2 B2]',
|
"[F2 A2 Bb2 B2]",
|
||||||
'[A2 Ab2 G2 C2]',
|
"[A2 Ab2 G2 C2]",
|
||||||
'[F2 A2 Bb2 B2]',
|
"[F2 A2 Bb2 B2]",
|
||||||
'[G2 C2 F2 F2]',
|
"[G2 C2 F2 F2]",
|
||||||
'[F2 A2 Bb2 B2]',
|
"[F2 A2 Bb2 B2]",
|
||||||
'[A2 Ab2 G2 C2]',
|
"[A2 Ab2 G2 C2]",
|
||||||
'[F2 A2 Bb2 B2]',
|
"[F2 A2 Bb2 B2]",
|
||||||
'[G2 C2 F2 F2]',
|
"[G2 C2 F2 F2]",
|
||||||
'[Bb2 Bb2 A2 A2]',
|
"[Bb2 Bb2 A2 A2]",
|
||||||
'[Ab2 Ab2 G2 [C2 D2 E2]]',
|
"[Ab2 Ab2 G2 [C2 D2 E2]]",
|
||||||
'[Bb2 Bb2 A2 A2]',
|
"[Bb2 Bb2 A2 A2]",
|
||||||
'[Ab2 Ab2 G2 [C2 D2 E2]]',
|
"[Ab2 Ab2 G2 [C2 D2 E2]]",
|
||||||
'[F2 A2 Bb2 B2]',
|
"[F2 A2 Bb2 B2]",
|
||||||
'[G2 C2 F2 F2]'
|
"[G2 C2 F2 F2]"
|
||||||
)
|
)
|
||||||
).slow(51);
|
).slow(51);
|
||||||
`;
|
`;
|
||||||
export const giantSteps = `stack(
|
export const giantSteps = `stack(
|
||||||
// melody
|
// melody
|
||||||
mini(
|
cat(
|
||||||
'[F#5 D5] [B4 G4] Bb4 [B4 A4]',
|
"[F#5 D5] [B4 G4] Bb4 [B4 A4]",
|
||||||
'[D5 Bb4] [G4 Eb4] F#4 [G4 F4]',
|
"[D5 Bb4] [G4 Eb4] F#4 [G4 F4]",
|
||||||
'Bb4 [B4 A4] D5 [D#5 C#5]',
|
"Bb4 [B4 A4] D5 [D#5 C#5]",
|
||||||
'F#5 [G5 F5] Bb5 [F#5 F#5]',
|
"F#5 [G5 F5] Bb5 [F#5 F#5]",
|
||||||
),
|
),
|
||||||
// chords
|
// chords
|
||||||
mini(
|
cat(
|
||||||
'[B^7 D7] [G^7 Bb7] Eb^7 [Am7 D7]',
|
"[B^7 D7] [G^7 Bb7] Eb^7 [Am7 D7]",
|
||||||
'[G^7 Bb7] [Eb^7 F#7] B^7 [Fm7 Bb7]',
|
"[G^7 Bb7] [Eb^7 F#7] B^7 [Fm7 Bb7]",
|
||||||
'Eb^7 [Am7 D7] G^7 [C#m7 F#7]',
|
"Eb^7 [Am7 D7] G^7 [C#m7 F#7]",
|
||||||
'B^7 [Fm7 Bb7] Eb^7 [C#m7 F#7]'
|
"B^7 [Fm7 Bb7] Eb^7 [C#m7 F#7]"
|
||||||
).voicings(['E3', 'G4']),
|
).voicings(['E3', 'G4']),
|
||||||
// bass
|
// bass
|
||||||
mini(
|
cat(
|
||||||
'[B2 D2] [G2 Bb2] [Eb2 Bb3] [A2 D2]',
|
"[B2 D2] [G2 Bb2] [Eb2 Bb3] [A2 D2]",
|
||||||
'[G2 Bb2] [Eb2 F#2] [B2 F#2] [F2 Bb2]',
|
"[G2 Bb2] [Eb2 F#2] [B2 F#2] [F2 Bb2]",
|
||||||
'[Eb2 Bb2] [A2 D2] [G2 D2] [C#2 F#2]',
|
"[Eb2 Bb2] [A2 D2] [G2 D2] [C#2 F#2]",
|
||||||
'[B2 F#2] [F2 Bb2] [Eb2 Bb3] [C#2 F#2]'
|
"[B2 F#2] [F2 Bb2] [Eb2 Bb3] [C#2 F#2]"
|
||||||
)
|
)
|
||||||
).slow(20);`;
|
).slow(20);`;
|
||||||
export const giantStepsReggae = `stack(
|
export const giantStepsReggae = `stack(
|
||||||
// melody
|
// melody
|
||||||
mini(
|
cat(
|
||||||
'[F#5 D5] [B4 G4] Bb4 [B4 A4]',
|
"[F#5 D5] [B4 G4] Bb4 [B4 A4]",
|
||||||
'[D5 Bb4] [G4 Eb4] F#4 [G4 F4]',
|
"[D5 Bb4] [G4 Eb4] F#4 [G4 F4]",
|
||||||
'Bb4 [B4 A4] D5 [D#5 C#5]',
|
"Bb4 [B4 A4] D5 [D#5 C#5]",
|
||||||
'F#5 [G5 F5] Bb5 [F#5 [F#5 ~@3]]',
|
"F#5 [G5 F5] Bb5 [F#5 [F#5 ~@3]]",
|
||||||
),
|
),
|
||||||
// chords
|
// chords
|
||||||
mini(
|
cat(
|
||||||
'[B^7 D7] [G^7 Bb7] Eb^7 [Am7 D7]',
|
"[B^7 D7] [G^7 Bb7] Eb^7 [Am7 D7]",
|
||||||
'[G^7 Bb7] [Eb^7 F#7] B^7 [Fm7 Bb7]',
|
"[G^7 Bb7] [Eb^7 F#7] B^7 [Fm7 Bb7]",
|
||||||
'Eb^7 [Am7 D7] G^7 [C#m7 F#7]',
|
"Eb^7 [Am7 D7] G^7 [C#m7 F#7]",
|
||||||
'B^7 [Fm7 Bb7] Eb^7 [C#m7 F#7]'
|
"B^7 [Fm7 Bb7] Eb^7 [C#m7 F#7]"
|
||||||
)
|
)
|
||||||
.groove('~ [x ~]'.m.fast(4*8))
|
.struct("~ [x ~]".fast(4*8))
|
||||||
.voicings(['E3', 'G4']),
|
.voicings(['E3', 'G4']),
|
||||||
// bass
|
// bass
|
||||||
mini(
|
cat(
|
||||||
'[B2 D2] [G2 D2] [Eb2 Bb2] [A2 D2]',
|
"[B2 D2] [G2 D2] [Eb2 Bb2] [A2 D2]",
|
||||||
'[G2 Bb2] [Eb2 F#2] [B2 F#2] [F2 Bb2]',
|
"[G2 Bb2] [Eb2 F#2] [B2 F#2] [F2 Bb2]",
|
||||||
'[Eb2 Bb2] [A2 D2] [G2 D2] [C#2 F#2]',
|
"[Eb2 Bb2] [A2 D2] [G2 D2] [C#2 F#2]",
|
||||||
'[B2 F#2] [F2 Bb2] [Eb2 Bb2] [C#2 F#2]'
|
"[B2 F#2] [F2 Bb2] [Eb2 Bb2] [C#2 F#2]"
|
||||||
)
|
)
|
||||||
.groove('x ~'.m.fast(4*8))
|
.struct("x ~".fast(4*8))
|
||||||
).slow(25)`;
|
).slow(25)`;
|
||||||
export const transposedChordsHacked = `stack(
|
export const transposedChordsHacked = `stack(
|
||||||
'c2 eb2 g2'.mini,
|
"c2 eb2 g2",
|
||||||
'Cm7'.pure.voicings(['g2','c4']).slow(2)
|
"Cm7".voicings(['g2','c4']).slow(2)
|
||||||
).transpose(
|
).transpose(
|
||||||
slowcat(1, 2, 3, 2).slow(2)
|
slowcat(1, 2, 3, 2).slow(2)
|
||||||
).transpose(5)`;
|
).transpose(5)`;
|
||||||
|
|
@ -244,25 +244,25 @@ export const scaleTranspose = `stack(f2, f3, c4, ab4)
|
||||||
.scale(sequence('F minor', 'F harmonic minor').slow(4))
|
.scale(sequence('F minor', 'F harmonic minor').slow(4))
|
||||||
.scaleTranspose(sequence(0, -1, -2, -3).slow(4))
|
.scaleTranspose(sequence(0, -1, -2, -3).slow(4))
|
||||||
.transpose(sequence(0, 1).slow(16))`;
|
.transpose(sequence(0, 1).slow(16))`;
|
||||||
export const groove = `stack(
|
export const struct = `stack(
|
||||||
'c2 g2 a2 [e2@2 eb2] d2 a2 g2 [d2 ~ db2]'.mini,
|
"c2 g2 a2 [e2@2 eb2] d2 a2 g2 [d2 ~ db2]",
|
||||||
'[C^7 A7] [Dm7 G7]'.mini.groove('[x@2 x] [~@2 x] [~ x@2]@2 [x ~@2] ~ [~@2 x@4]@2'.mini)
|
"[C^7 A7] [Dm7 G7]".struct("[x@2 x] [~@2 x] [~ x@2]@2 [x ~@2] ~ [~@2 x@4]@2")
|
||||||
.voicings(['G3','A4'])
|
.voicings(['G3','A4'])
|
||||||
).slow(4)`;
|
).slow(4)`;
|
||||||
export const magicSofa = `stack(
|
export const magicSofa = `stack(
|
||||||
'<C^7 F^7 ~> <Dm7 G7 A7 ~>'.m
|
"<C^7 F^7 ~> <Dm7 G7 A7 ~>"
|
||||||
.every(2, fast(2))
|
.every(2, fast(2))
|
||||||
.voicings(),
|
.voicings(),
|
||||||
'<c2 f2 g2> <d2 g2 a2 e2>'.m
|
"<c2 f2 g2> <d2 g2 a2 e2>"
|
||||||
).slow(1).transpose.slowcat(0, 2, 3, 4)`;
|
).slow(1).transpose.slowcat(0, 2, 3, 4)`;
|
||||||
export const confusedPhoneDynamic = `stack('[g2 ~@1.3] [c3 ~@1.3]'.mini.slow(2))
|
export const confusedPhoneDynamic = `stack("[g2 ~@1.3] [c3 ~@1.3]".slow(2))
|
||||||
.superimpose(
|
.superimpose(
|
||||||
...[-12,7,10,12,24].slice(0,5).map((t,i,{length}) => x => transpose(t,x).late(i/length))
|
...[-12,7,10,12,24].slice(0,5).map((t,i,{length}) => x => transpose(t,x).late(i/length))
|
||||||
)
|
)
|
||||||
.scale(sequence('C dorian', 'C mixolydian').slow(4))
|
.scale(sequence('C dorian', 'C mixolydian').slow(4))
|
||||||
.scaleTranspose(slowcat(0,1,2,1).slow(2))
|
.scaleTranspose(slowcat(0,1,2,1).slow(2))
|
||||||
.synth('triangle').gain(0.5).filter(1500)`;
|
.synth('triangle').gain(0.5).filter(1500)`;
|
||||||
export const confusedPhone = `'[g2 ~@1.3] [c3 ~@1.3]'.mini
|
export const confusedPhone = `"[g2 ~@1.3] [c3 ~@1.3]"
|
||||||
.superimpose(
|
.superimpose(
|
||||||
transpose(-12).late(0),
|
transpose(-12).late(0),
|
||||||
transpose(7).late(0.1),
|
transpose(7).late(0.1),
|
||||||
|
|
@ -280,14 +280,14 @@ export const zeldasRescue = `stack(
|
||||||
[B3@2 D4] [A3@2 [G3 A3]] [B3@2 D4] [A3]
|
[B3@2 D4] [A3@2 [G3 A3]] [B3@2 D4] [A3]
|
||||||
[B3@2 D4] [A4@2 G4] D5@2
|
[B3@2 D4] [A4@2 G4] D5@2
|
||||||
[D5@2 [C5 B4]] [[C5 B4] G4@2] [C5@2 [B4 A4]] [[B4 A4] E4@2]
|
[D5@2 [C5 B4]] [[C5 B4] G4@2] [C5@2 [B4 A4]] [[B4 A4] E4@2]
|
||||||
[D5@2 [C5 B4]] [[C5 B4] G4 C5] [G5] [~ ~ B3]\`.mini,
|
[D5@2 [C5 B4]] [[C5 B4] G4 C5] [G5] [~ ~ B3]\`,
|
||||||
// bass
|
// bass
|
||||||
\`[[C2 G2] E3@2] [[C2 G2] F#3@2] [[C2 G2] E3@2] [[C2 G2] F#3@2]
|
\`[[C2 G2] E3@2] [[C2 G2] F#3@2] [[C2 G2] E3@2] [[C2 G2] F#3@2]
|
||||||
[[B1 D3] G3@2] [[Bb1 Db3] G3@2] [[A1 C3] G3@2] [[D2 C3] F#3@2]
|
[[B1 D3] G3@2] [[Bb1 Db3] G3@2] [[A1 C3] G3@2] [[D2 C3] F#3@2]
|
||||||
[[C2 G2] E3@2] [[C2 G2] F#3@2] [[C2 G2] E3@2] [[C2 G2] F#3@2]
|
[[C2 G2] E3@2] [[C2 G2] F#3@2] [[C2 G2] E3@2] [[C2 G2] F#3@2]
|
||||||
[[B1 D3] G3@2] [[Bb1 Db3] G3@2] [[A1 C3] G3@2] [[D2 C3] F#3@2]
|
[[B1 D3] G3@2] [[Bb1 Db3] G3@2] [[A1 C3] G3@2] [[D2 C3] F#3@2]
|
||||||
[[F2 C3] E3@2] [[E2 B2] D3@2] [[D2 A2] C3@2] [[C2 G2] B2@2]
|
[[F2 C3] E3@2] [[E2 B2] D3@2] [[D2 A2] C3@2] [[C2 G2] B2@2]
|
||||||
[[F2 C3] E3@2] [[E2 B2] D3@2] [[Eb2 Bb2] Db3@2] [[D2 A2] C3 [F3,G2]]\`.mini
|
[[F2 C3] E3@2] [[E2 B2] D3@2] [[Eb2 Bb2] Db3@2] [[D2 A2] C3 [F3,G2]]\`
|
||||||
).transpose(12).slow(48).tone(
|
).transpose(12).slow(48).tone(
|
||||||
new PolySynth().chain(
|
new PolySynth().chain(
|
||||||
new Gain(0.3),
|
new Gain(0.3),
|
||||||
|
|
@ -296,9 +296,9 @@ export const zeldasRescue = `stack(
|
||||||
Destination)
|
Destination)
|
||||||
)`;
|
)`;
|
||||||
export const technoDrums = `stack(
|
export const technoDrums = `stack(
|
||||||
'c1*2'.m.tone(new Tone.MembraneSynth().toDestination()),
|
"c1*2".tone(new Tone.MembraneSynth().toDestination()),
|
||||||
'~ x'.m.tone(new Tone.NoiseSynth().toDestination()),
|
"~ x".tone(new Tone.NoiseSynth().toDestination()),
|
||||||
'[~ c4]*2'.m.tone(new Tone.MetalSynth().set({envelope:{decay:0.06,sustain:0}}).chain(new Gain(0.5),Destination))
|
"[~ c4]*2".tone(new Tone.MetalSynth().set({envelope:{decay:0.06,sustain:0}}).chain(new Gain(0.5),Destination))
|
||||||
)`;
|
)`;
|
||||||
export const loungerave = `() => {
|
export const loungerave = `() => {
|
||||||
const delay = new FeedbackDelay(1/8, .2).chain(vol(0.5), out);
|
const delay = new FeedbackDelay(1/8, .2).chain(vol(0.5), out);
|
||||||
|
|
@ -309,22 +309,22 @@ export const loungerave = `() => {
|
||||||
const keys = new PolySynth().set({ ...osc('sawtooth'), ...adsr(0, .5, .2, .7) }).chain(lowpass(1200), vol(.5), out);
|
const keys = new PolySynth().set({ ...osc('sawtooth'), ...adsr(0, .5, .2, .7) }).chain(lowpass(1200), vol(.5), out);
|
||||||
|
|
||||||
const drums = stack(
|
const drums = stack(
|
||||||
'c1*2'.m.tone(kick).bypass('<0@7 1>/8'.m),
|
"c1*2".tone(kick).bypass("<0@7 1>/8"),
|
||||||
'~ <x!7 [x@3 x]>'.m.tone(snare).bypass('<0@7 1>/4'.m),
|
"~ <x!7 [x@3 x]>".tone(snare).bypass("<0@7 1>/4"),
|
||||||
'[~ c4]*2'.m.tone(hihat)
|
"[~ c4]*2".tone(hihat)
|
||||||
);
|
);
|
||||||
|
|
||||||
const thru = (x) => x.transpose('<0 1>/8'.m).transpose(1);
|
const thru = (x) => x.transpose("<0 1>/8").transpose(1);
|
||||||
const synths = stack(
|
const synths = stack(
|
||||||
'<C2 Bb1 Ab1 [G1 [G2 G1]]>/2'.m.groove('[x [~ x] <[~ [~ x]]!3 [x x]>@2]/2'.m).edit(thru).tone(bass),
|
"<C2 Bb1 Ab1 [G1 [G2 G1]]>/2".struct("[x [~ x] <[~ [~ x]]!3 [x x]>@2]/2").edit(thru).tone(bass),
|
||||||
'<Cm7 Bb7 Fm7 G7b9>/2'.m.groove('~ [x@0.1 ~]'.m).voicings().edit(thru).every(2, early(1/4)).tone(keys).bypass('<0@7 1>/8'.m.early(1/4))
|
"<Cm7 Bb7 Fm7 G7b9>/2".struct("~ [x@0.1 ~]").voicings().edit(thru).every(2, early(1/4)).tone(keys).bypass("<0@7 1>/8".early(1/4))
|
||||||
)
|
)
|
||||||
return stack(
|
return stack(
|
||||||
drums,
|
drums,
|
||||||
synths
|
synths
|
||||||
)
|
)
|
||||||
//.bypass('<0 1>*4'.m)
|
//.bypass("<0 1>*4")
|
||||||
//.early('0.25 0'.m);
|
//.early("0.25 0");
|
||||||
}`;
|
}`;
|
||||||
export const caverave = `() => {
|
export const caverave = `() => {
|
||||||
const delay = new FeedbackDelay(1/8, .4).chain(vol(0.5), out);
|
const delay = new FeedbackDelay(1/8, .4).chain(vol(0.5), out);
|
||||||
|
|
@ -335,86 +335,41 @@ export const caverave = `() => {
|
||||||
const keys = new PolySynth().set({ ...osc('sawtooth'), ...adsr(0, .5, .2, .7) }).chain(lowpass(1200), vol(.5), out);
|
const keys = new PolySynth().set({ ...osc('sawtooth'), ...adsr(0, .5, .2, .7) }).chain(lowpass(1200), vol(.5), out);
|
||||||
|
|
||||||
const drums = stack(
|
const drums = stack(
|
||||||
'c1*2'.m.tone(kick).bypass('<0@7 1>/8'.m),
|
"c1*2".tone(kick).bypass("<0@7 1>/8"),
|
||||||
'~ <x!7 [x@3 x]>'.m.tone(snare).bypass('<0@7 1>/4'.m),
|
"~ <x!7 [x@3 x]>".tone(snare).bypass("<0@7 1>/4"),
|
||||||
'[~ c4]*2'.m.tone(hihat)
|
"[~ c4]*2".tone(hihat)
|
||||||
);
|
);
|
||||||
|
|
||||||
const thru = (x) => x.transpose('<0 1>/8'.m).transpose(-1);
|
const thru = (x) => x.transpose("<0 1>/8").transpose(-1);
|
||||||
const synths = stack(
|
const synths = stack(
|
||||||
'<eb4 d4 c4 b3>/2'.m.scale(timeCat([3,'C minor'],[1,'C melodic minor']).slow(8)).groove('[~ x]*2'.m)
|
"<eb4 d4 c4 b3>/2".scale(timeCat([3,'C minor'],[1,'C melodic minor']).slow(8)).struct("[~ x]*2")
|
||||||
.edit(
|
.edit(
|
||||||
scaleTranspose(0).early(0),
|
scaleTranspose(0).early(0),
|
||||||
scaleTranspose(2).early(1/8),
|
scaleTranspose(2).early(1/8),
|
||||||
scaleTranspose(7).early(1/4),
|
scaleTranspose(7).early(1/4),
|
||||||
scaleTranspose(8).early(3/8)
|
scaleTranspose(8).early(3/8)
|
||||||
).edit(thru).tone(keys).bypass('<1 0>/16'.m),
|
).edit(thru).tone(keys).bypass("<1 0>/16"),
|
||||||
'<C2 Bb1 Ab1 [G1 [G2 G1]]>/2'.m.groove('[x [~ x] <[~ [~ x]]!3 [x x]>@2]/2'.m.fast(2)).edit(thru).tone(bass),
|
"<C2 Bb1 Ab1 [G1 [G2 G1]]>/2".struct("[x [~ x] <[~ [~ x]]!3 [x x]>@2]/2".fast(2)).edit(thru).tone(bass),
|
||||||
'<Cm7 Bb7 Fm7 G7b13>/2'.m.groove('~ [x@0.1 ~]'.m.fast(2)).voicings().edit(thru).every(2, early(1/8)).tone(keys).bypass('<0@7 1>/8'.m.early(1/4))
|
"<Cm7 Bb7 Fm7 G7b13>/2".struct("~ [x@0.1 ~]".fast(2)).voicings().edit(thru).every(2, early(1/8)).tone(keys).bypass("<0@7 1>/8".early(1/4))
|
||||||
)
|
)
|
||||||
return stack(
|
return stack(
|
||||||
drums.fast(2),
|
drums.fast(2),
|
||||||
synths
|
synths
|
||||||
).slow(2);
|
).slow(2);
|
||||||
}`;
|
}`;
|
||||||
export const caveravefuture = `() => {
|
export const callcenterhero = `()=>{
|
||||||
const delay = new FeedbackDelay(1/8, .4).chain(vol(0.5), out);
|
const bpm = 90;
|
||||||
const kick = new MembraneSynth().chain(vol(.8), out);
|
const lead = polysynth().set({...osc('sine4'),...adsr(.004)}).chain(vol(0.15),out)
|
||||||
const snare = new NoiseSynth().chain(vol(.8), out);
|
const bass = fmsynth({...osc('sawtooth6'),...adsr(0.05,.6,0.8,0.1)}).chain(vol(0.6), out);
|
||||||
const hihat = new MetalSynth().set(adsr(0, .08, 0, .1)).chain(vol(.3).connect(delay),out);
|
const s = scale(slowcat('F3 minor', 'Ab3 major', 'Bb3 dorian', 'C4 phrygian dominant').slow(4));
|
||||||
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(
|
return stack(
|
||||||
drums.fast(2),
|
"0 2".struct("<x ~> [x ~]").edit(s).scaleTranspose(stack(0,2)).tone(lead),
|
||||||
synths
|
"<6 7 9 7>".struct("[~ [x ~]*2]*2").edit(s).scaleTranspose("[0,2] [2,4]".fast(2).every(4,rev)).tone(lead),
|
||||||
).slow(2);
|
"-14".struct("[~ x@0.8]*2".early(0.01)).edit(s).tone(bass),
|
||||||
}`;
|
"c2*2".tone(membrane().chain(vol(0.6), out)),
|
||||||
export const caveravefuture2 = `const delay = new FeedbackDelay(1/8, .4).chain(vol(0.5), out);
|
"~ c2".tone(noise().chain(vol(0.2), out)),
|
||||||
const kick = new MembraneSynth().chain(vol(.8), out);
|
"c4*4".tone(metal(adsr(0,.05,0)).chain(vol(0.03), 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);
|
.slow(120 / bpm)
|
||||||
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);
|
|
||||||
`;
|
`;
|
||||||
|
|
|
||||||
16
docs/dist/useRepl.js
vendored
16
docs/dist/useRepl.js
vendored
|
|
@ -6,14 +6,16 @@ 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();
|
||||||
const [log, setLog] = useState("");
|
const [log, setLog] = useState("");
|
||||||
const [error, setError] = useState();
|
const [error, setError] = useState();
|
||||||
|
const [hash, setHash] = useState("");
|
||||||
const [pattern, setPattern] = useState();
|
const [pattern, setPattern] = useState();
|
||||||
const dirty = code !== activeCode;
|
const dirty = code !== activeCode || error;
|
||||||
|
const generateHash = () => encodeURIComponent(btoa(code));
|
||||||
const activateCode = (_code = code) => {
|
const activateCode = (_code = code) => {
|
||||||
!cycle.started && cycle.start();
|
!cycle.started && cycle.start();
|
||||||
broadcast({type: "start", from: id});
|
broadcast({type: "start", from: id});
|
||||||
|
|
@ -27,9 +29,12 @@ function useRepl({tune, defaultSynth, autolink = true}) {
|
||||||
if (autolink) {
|
if (autolink) {
|
||||||
window.location.hash = "#" + encodeURIComponent(btoa(code));
|
window.location.hash = "#" + encodeURIComponent(btoa(code));
|
||||||
}
|
}
|
||||||
|
setHash(generateHash());
|
||||||
setError(void 0);
|
setError(void 0);
|
||||||
setActiveCode(_code);
|
setActiveCode(_code);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
err.message = "evaluation error: " + err.message;
|
||||||
|
console.warn(err);
|
||||||
setError(err);
|
setError(err);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -43,6 +48,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,11 +68,12 @@ 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) || [];
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
err.message = "query error: " + err.message;
|
||||||
setError(err);
|
setError(err);
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
@ -99,7 +106,8 @@ function useRepl({tune, defaultSynth, autolink = true}) {
|
||||||
togglePlay,
|
togglePlay,
|
||||||
activateCode,
|
activateCode,
|
||||||
activeCode,
|
activeCode,
|
||||||
pushLog
|
pushLog,
|
||||||
|
hash
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
export default useRepl;
|
export default useRepl;
|
||||||
|
|
|
||||||
2
docs/dist/voicings.js
vendored
2
docs/dist/voicings.js
vendored
|
|
@ -18,7 +18,7 @@ Pattern.prototype.voicings = function(range) {
|
||||||
range = ["F3", "A4"];
|
range = ["F3", "A4"];
|
||||||
}
|
}
|
||||||
return this.fmapNested((event) => {
|
return this.fmapNested((event) => {
|
||||||
lastVoicing = getVoicing(event.value, lastVoicing, range);
|
lastVoicing = getVoicing(event.value?.value || event.value, lastVoicing, range);
|
||||||
return stack(...lastVoicing);
|
return stack(...lastVoicing);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -958,6 +958,9 @@ select {
|
||||||
.flex {
|
.flex {
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
.inline-flex {
|
||||||
|
display: inline-flex;
|
||||||
|
}
|
||||||
.contents {
|
.contents {
|
||||||
display: contents;
|
display: contents;
|
||||||
}
|
}
|
||||||
|
|
@ -967,6 +970,9 @@ select {
|
||||||
.h-full {
|
.h-full {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
.h-5 {
|
||||||
|
height: 1.25rem;
|
||||||
|
}
|
||||||
.min-h-screen {
|
.min-h-screen {
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
}
|
}
|
||||||
|
|
@ -979,6 +985,9 @@ select {
|
||||||
.w-16 {
|
.w-16 {
|
||||||
width: 4rem;
|
width: 4rem;
|
||||||
}
|
}
|
||||||
|
.w-5 {
|
||||||
|
width: 1.25rem;
|
||||||
|
}
|
||||||
.max-w-3xl {
|
.max-w-3xl {
|
||||||
max-width: 48rem;
|
max-width: 48rem;
|
||||||
}
|
}
|
||||||
|
|
@ -991,6 +1000,22 @@ select {
|
||||||
.transform {
|
.transform {
|
||||||
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
|
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
|
||||||
}
|
}
|
||||||
|
@-webkit-keyframes pulse {
|
||||||
|
|
||||||
|
50% {
|
||||||
|
opacity: .5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@keyframes pulse {
|
||||||
|
|
||||||
|
50% {
|
||||||
|
opacity: .5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.animate-pulse {
|
||||||
|
-webkit-animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
|
||||||
|
animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
|
||||||
|
}
|
||||||
.cursor-not-allowed {
|
.cursor-not-allowed {
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
}
|
}
|
||||||
|
|
@ -1024,9 +1049,15 @@ select {
|
||||||
.overflow-auto {
|
.overflow-auto {
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
.overflow-hidden {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
.whitespace-pre {
|
.whitespace-pre {
|
||||||
white-space: pre;
|
white-space: pre;
|
||||||
}
|
}
|
||||||
|
.rounded-md {
|
||||||
|
border-radius: 0.375rem;
|
||||||
|
}
|
||||||
.border {
|
.border {
|
||||||
border-width: 1px;
|
border-width: 1px;
|
||||||
}
|
}
|
||||||
|
|
@ -1036,6 +1067,12 @@ select {
|
||||||
.border-b {
|
.border-b {
|
||||||
border-bottom-width: 1px;
|
border-bottom-width: 1px;
|
||||||
}
|
}
|
||||||
|
.border-t {
|
||||||
|
border-top-width: 1px;
|
||||||
|
}
|
||||||
|
.border-r {
|
||||||
|
border-right-width: 1px;
|
||||||
|
}
|
||||||
.border-gray-200 {
|
.border-gray-200 {
|
||||||
--tw-border-opacity: 1;
|
--tw-border-opacity: 1;
|
||||||
border-color: rgb(229 231 235 / var(--tw-border-opacity));
|
border-color: rgb(229 231 235 / var(--tw-border-opacity));
|
||||||
|
|
@ -1074,10 +1111,16 @@ select {
|
||||||
.p-2 {
|
.p-2 {
|
||||||
padding: 0.5rem;
|
padding: 0.5rem;
|
||||||
}
|
}
|
||||||
|
.p-1 {
|
||||||
|
padding: 0.25rem;
|
||||||
|
}
|
||||||
.px-2 {
|
.px-2 {
|
||||||
padding-left: 0.5rem;
|
padding-left: 0.5rem;
|
||||||
padding-right: 0.5rem;
|
padding-right: 0.5rem;
|
||||||
}
|
}
|
||||||
|
.pr-2 {
|
||||||
|
padding-right: 0.5rem;
|
||||||
|
}
|
||||||
.text-right {
|
.text-right {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
|
@ -1089,6 +1132,10 @@ select {
|
||||||
font-size: 0.75rem;
|
font-size: 0.75rem;
|
||||||
line-height: 1rem;
|
line-height: 1rem;
|
||||||
}
|
}
|
||||||
|
.text-sm {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
line-height: 1.25rem;
|
||||||
|
}
|
||||||
.text-gray-100 {
|
.text-gray-100 {
|
||||||
--tw-text-opacity: 1;
|
--tw-text-opacity: 1;
|
||||||
color: rgb(243 244 246 / var(--tw-text-opacity));
|
color: rgb(243 244 246 / var(--tw-text-opacity));
|
||||||
|
|
@ -1105,6 +1152,10 @@ select {
|
||||||
--tw-text-opacity: 1;
|
--tw-text-opacity: 1;
|
||||||
color: rgb(148 163 184 / var(--tw-text-opacity));
|
color: rgb(148 163 184 / var(--tw-text-opacity));
|
||||||
}
|
}
|
||||||
|
.text-red-200 {
|
||||||
|
--tw-text-opacity: 1;
|
||||||
|
color: rgb(254 202 202 / var(--tw-text-opacity));
|
||||||
|
}
|
||||||
.filter {
|
.filter {
|
||||||
filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);
|
filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load diff
File diff suppressed because one or more lines are too long
|
|
@ -695,12 +695,16 @@ Ensure the default browser behavior of the `hidden` attribute.
|
||||||
display: block;
|
display: block;
|
||||||
}.flex {
|
}.flex {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
}.inline-flex {
|
||||||
|
display: inline-flex;
|
||||||
}.contents {
|
}.contents {
|
||||||
display: contents;
|
display: contents;
|
||||||
}.h-16 {
|
}.h-16 {
|
||||||
height: 4rem;
|
height: 4rem;
|
||||||
}.h-full {
|
}.h-full {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
}.h-5 {
|
||||||
|
height: 1.25rem;
|
||||||
}.min-h-screen {
|
}.min-h-screen {
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
}.min-h-\[200px\] {
|
}.min-h-\[200px\] {
|
||||||
|
|
@ -709,6 +713,8 @@ Ensure the default browser behavior of the `hidden` attribute.
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}.w-16 {
|
}.w-16 {
|
||||||
width: 4rem;
|
width: 4rem;
|
||||||
|
}.w-5 {
|
||||||
|
width: 1.25rem;
|
||||||
}.max-w-3xl {
|
}.max-w-3xl {
|
||||||
max-width: 48rem;
|
max-width: 48rem;
|
||||||
}.flex-none {
|
}.flex-none {
|
||||||
|
|
@ -717,6 +723,19 @@ Ensure the default browser behavior of the `hidden` attribute.
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
}.transform {
|
}.transform {
|
||||||
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
|
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
|
||||||
|
}@-webkit-keyframes pulse {
|
||||||
|
|
||||||
|
50% {
|
||||||
|
opacity: .5;
|
||||||
|
}
|
||||||
|
}@keyframes pulse {
|
||||||
|
|
||||||
|
50% {
|
||||||
|
opacity: .5;
|
||||||
|
}
|
||||||
|
}.animate-pulse {
|
||||||
|
-webkit-animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
|
||||||
|
animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
|
||||||
}.cursor-not-allowed {
|
}.cursor-not-allowed {
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
}.flex-col {
|
}.flex-col {
|
||||||
|
|
@ -741,14 +760,22 @@ Ensure the default browser behavior of the `hidden` attribute.
|
||||||
margin-bottom: calc(0px * var(--tw-space-y-reverse));
|
margin-bottom: calc(0px * var(--tw-space-y-reverse));
|
||||||
}.overflow-auto {
|
}.overflow-auto {
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
|
}.overflow-hidden {
|
||||||
|
overflow: hidden;
|
||||||
}.whitespace-pre {
|
}.whitespace-pre {
|
||||||
white-space: pre;
|
white-space: pre;
|
||||||
|
}.rounded-md {
|
||||||
|
border-radius: 0.375rem;
|
||||||
}.border {
|
}.border {
|
||||||
border-width: 1px;
|
border-width: 1px;
|
||||||
}.border-0 {
|
}.border-0 {
|
||||||
border-width: 0px;
|
border-width: 0px;
|
||||||
}.border-b {
|
}.border-b {
|
||||||
border-bottom-width: 1px;
|
border-bottom-width: 1px;
|
||||||
|
}.border-t {
|
||||||
|
border-top-width: 1px;
|
||||||
|
}.border-r {
|
||||||
|
border-right-width: 1px;
|
||||||
}.border-gray-200 {
|
}.border-gray-200 {
|
||||||
--tw-border-opacity: 1;
|
--tw-border-opacity: 1;
|
||||||
border-color: rgb(229 231 235 / var(--tw-border-opacity));
|
border-color: rgb(229 231 235 / var(--tw-border-opacity));
|
||||||
|
|
@ -777,9 +804,13 @@ Ensure the default browser behavior of the `hidden` attribute.
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
}.p-2 {
|
}.p-2 {
|
||||||
padding: 0.5rem;
|
padding: 0.5rem;
|
||||||
|
}.p-1 {
|
||||||
|
padding: 0.25rem;
|
||||||
}.px-2 {
|
}.px-2 {
|
||||||
padding-left: 0.5rem;
|
padding-left: 0.5rem;
|
||||||
padding-right: 0.5rem;
|
padding-right: 0.5rem;
|
||||||
|
}.pr-2 {
|
||||||
|
padding-right: 0.5rem;
|
||||||
}.text-right {
|
}.text-right {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}.text-2xl {
|
}.text-2xl {
|
||||||
|
|
@ -788,6 +819,9 @@ Ensure the default browser behavior of the `hidden` attribute.
|
||||||
}.text-xs {
|
}.text-xs {
|
||||||
font-size: 0.75rem;
|
font-size: 0.75rem;
|
||||||
line-height: 1rem;
|
line-height: 1rem;
|
||||||
|
}.text-sm {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
line-height: 1.25rem;
|
||||||
}.text-gray-100 {
|
}.text-gray-100 {
|
||||||
--tw-text-opacity: 1;
|
--tw-text-opacity: 1;
|
||||||
color: rgb(243 244 246 / var(--tw-text-opacity));
|
color: rgb(243 244 246 / var(--tw-text-opacity));
|
||||||
|
|
@ -800,6 +834,9 @@ Ensure the default browser behavior of the `hidden` attribute.
|
||||||
}.text-slate-400 {
|
}.text-slate-400 {
|
||||||
--tw-text-opacity: 1;
|
--tw-text-opacity: 1;
|
||||||
color: rgb(148 163 184 / var(--tw-text-opacity));
|
color: rgb(148 163 184 / var(--tw-text-opacity));
|
||||||
|
}.text-red-200 {
|
||||||
|
--tw-text-opacity: 1;
|
||||||
|
color: rgb(254 202 202 / var(--tw-text-opacity));
|
||||||
}.filter {
|
}.filter {
|
||||||
filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);
|
filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);
|
||||||
}.react-codemirror2,
|
}.react-codemirror2,
|
||||||
|
|
@ -822,148 +859,6 @@ Ensure the default browser behavior of the `hidden` attribute.
|
||||||
--tw-ring-color: rgb(30 41 59 / var(--tw-ring-opacity));
|
--tw-ring-color: rgb(30 41 59 / var(--tw-ring-opacity));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
Name: material
|
|
||||||
Author: Mattia Astorino (http://github.com/equinusocio)
|
|
||||||
Website: https://material-theme.site/
|
|
||||||
*/
|
|
||||||
|
|
||||||
.cm-s-material.CodeMirror {
|
|
||||||
background-color: #263238;
|
|
||||||
color: #EEFFFF;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-s-material .CodeMirror-gutters {
|
|
||||||
background: #263238;
|
|
||||||
color: #546E7A;
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-s-material .CodeMirror-guttermarker,
|
|
||||||
.cm-s-material .CodeMirror-guttermarker-subtle,
|
|
||||||
.cm-s-material .CodeMirror-linenumber {
|
|
||||||
color: #546E7A;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-s-material .CodeMirror-cursor {
|
|
||||||
border-left: 1px solid #FFCC00;
|
|
||||||
}
|
|
||||||
.cm-s-material.cm-fat-cursor .CodeMirror-cursor {
|
|
||||||
background-color: #5d6d5c80 !important;
|
|
||||||
}
|
|
||||||
.cm-s-material .cm-animate-fat-cursor {
|
|
||||||
background-color: #5d6d5c80 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-s-material div.CodeMirror-selected {
|
|
||||||
background: rgba(128, 203, 196, 0.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-s-material.CodeMirror-focused div.CodeMirror-selected {
|
|
||||||
background: rgba(128, 203, 196, 0.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-s-material .CodeMirror-line::selection,
|
|
||||||
.cm-s-material .CodeMirror-line>span::selection,
|
|
||||||
.cm-s-material .CodeMirror-line>span>span::selection {
|
|
||||||
background: rgba(128, 203, 196, 0.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-s-material .CodeMirror-line::-moz-selection,
|
|
||||||
.cm-s-material .CodeMirror-line>span::-moz-selection,
|
|
||||||
.cm-s-material .CodeMirror-line>span>span::-moz-selection {
|
|
||||||
background: rgba(128, 203, 196, 0.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-s-material .CodeMirror-activeline-background {
|
|
||||||
background: rgba(0, 0, 0, 0.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-s-material .cm-keyword {
|
|
||||||
color: #C792EA;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-s-material .cm-operator {
|
|
||||||
color: #89DDFF;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-s-material .cm-variable-2 {
|
|
||||||
color: #EEFFFF;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-s-material .cm-variable-3,
|
|
||||||
.cm-s-material .cm-type {
|
|
||||||
color: #f07178;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-s-material .cm-builtin {
|
|
||||||
color: #FFCB6B;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-s-material .cm-atom {
|
|
||||||
color: #F78C6C;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-s-material .cm-number {
|
|
||||||
color: #FF5370;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-s-material .cm-def {
|
|
||||||
color: #82AAFF;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-s-material .cm-string {
|
|
||||||
color: #C3E88D;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-s-material .cm-string-2 {
|
|
||||||
color: #f07178;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-s-material .cm-comment {
|
|
||||||
color: #546E7A;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-s-material .cm-variable {
|
|
||||||
color: #f07178;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-s-material .cm-tag {
|
|
||||||
color: #FF5370;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-s-material .cm-meta {
|
|
||||||
color: #FFCB6B;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-s-material .cm-attribute {
|
|
||||||
color: #C792EA;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-s-material .cm-property {
|
|
||||||
color: #C792EA;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-s-material .cm-qualifier {
|
|
||||||
color: #DECB6B;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-s-material .cm-variable-3,
|
|
||||||
.cm-s-material .cm-type {
|
|
||||||
color: #DECB6B;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.cm-s-material .cm-error {
|
|
||||||
color: rgba(255, 255, 255, 1.0);
|
|
||||||
background-color: #FF5370;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-s-material .CodeMirror-matchingbracket {
|
|
||||||
text-decoration: underline;
|
|
||||||
color: white !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* BASICS */
|
/* BASICS */
|
||||||
|
|
||||||
.CodeMirror {
|
.CodeMirror {
|
||||||
|
|
@ -1309,4 +1204,146 @@ div.CodeMirror-dragcursors {
|
||||||
/* Help users use markselection to safely style text background */
|
/* Help users use markselection to safely style text background */
|
||||||
span.CodeMirror-selectedtext { background: none; }
|
span.CodeMirror-selectedtext { background: none; }
|
||||||
|
|
||||||
/*# sourceMappingURL=index.1e09ac22.css.map */
|
/*
|
||||||
|
Name: material
|
||||||
|
Author: Mattia Astorino (http://github.com/equinusocio)
|
||||||
|
Website: https://material-theme.site/
|
||||||
|
*/
|
||||||
|
|
||||||
|
.cm-s-material.CodeMirror {
|
||||||
|
background-color: #263238;
|
||||||
|
color: #EEFFFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cm-s-material .CodeMirror-gutters {
|
||||||
|
background: #263238;
|
||||||
|
color: #546E7A;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cm-s-material .CodeMirror-guttermarker,
|
||||||
|
.cm-s-material .CodeMirror-guttermarker-subtle,
|
||||||
|
.cm-s-material .CodeMirror-linenumber {
|
||||||
|
color: #546E7A;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cm-s-material .CodeMirror-cursor {
|
||||||
|
border-left: 1px solid #FFCC00;
|
||||||
|
}
|
||||||
|
.cm-s-material.cm-fat-cursor .CodeMirror-cursor {
|
||||||
|
background-color: #5d6d5c80 !important;
|
||||||
|
}
|
||||||
|
.cm-s-material .cm-animate-fat-cursor {
|
||||||
|
background-color: #5d6d5c80 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cm-s-material div.CodeMirror-selected {
|
||||||
|
background: rgba(128, 203, 196, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cm-s-material.CodeMirror-focused div.CodeMirror-selected {
|
||||||
|
background: rgba(128, 203, 196, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cm-s-material .CodeMirror-line::selection,
|
||||||
|
.cm-s-material .CodeMirror-line>span::selection,
|
||||||
|
.cm-s-material .CodeMirror-line>span>span::selection {
|
||||||
|
background: rgba(128, 203, 196, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cm-s-material .CodeMirror-line::-moz-selection,
|
||||||
|
.cm-s-material .CodeMirror-line>span::-moz-selection,
|
||||||
|
.cm-s-material .CodeMirror-line>span>span::-moz-selection {
|
||||||
|
background: rgba(128, 203, 196, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cm-s-material .CodeMirror-activeline-background {
|
||||||
|
background: rgba(0, 0, 0, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cm-s-material .cm-keyword {
|
||||||
|
color: #C792EA;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cm-s-material .cm-operator {
|
||||||
|
color: #89DDFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cm-s-material .cm-variable-2 {
|
||||||
|
color: #EEFFFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cm-s-material .cm-variable-3,
|
||||||
|
.cm-s-material .cm-type {
|
||||||
|
color: #f07178;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cm-s-material .cm-builtin {
|
||||||
|
color: #FFCB6B;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cm-s-material .cm-atom {
|
||||||
|
color: #F78C6C;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cm-s-material .cm-number {
|
||||||
|
color: #FF5370;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cm-s-material .cm-def {
|
||||||
|
color: #82AAFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cm-s-material .cm-string {
|
||||||
|
color: #C3E88D;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cm-s-material .cm-string-2 {
|
||||||
|
color: #f07178;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cm-s-material .cm-comment {
|
||||||
|
color: #546E7A;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cm-s-material .cm-variable {
|
||||||
|
color: #f07178;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cm-s-material .cm-tag {
|
||||||
|
color: #FF5370;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cm-s-material .cm-meta {
|
||||||
|
color: #FFCB6B;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cm-s-material .cm-attribute {
|
||||||
|
color: #C792EA;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cm-s-material .cm-property {
|
||||||
|
color: #C792EA;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cm-s-material .cm-qualifier {
|
||||||
|
color: #DECB6B;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cm-s-material .cm-variable-3,
|
||||||
|
.cm-s-material .cm-type {
|
||||||
|
color: #DECB6B;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.cm-s-material .cm-error {
|
||||||
|
color: rgba(255, 255, 255, 1.0);
|
||||||
|
background-color: #FF5370;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cm-s-material .CodeMirror-matchingbracket {
|
||||||
|
text-decoration: underline;
|
||||||
|
color: white !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*# sourceMappingURL=index.fd7d9b66.css.map */
|
||||||
1
docs/tutorial/index.fd7d9b66.css.map
Normal file
1
docs/tutorial/index.fd7d9b66.css.map
Normal file
File diff suppressed because one or more lines are too long
|
|
@ -3,7 +3,7 @@
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<link rel="icon" href="/tutorial/favicon.e3ab9dd9.ico">
|
<link rel="icon" href="/tutorial/favicon.e3ab9dd9.ico">
|
||||||
<link rel="stylesheet" type="text/css" href="/tutorial/index.1e09ac22.css">
|
<link rel="stylesheet" type="text/css" href="/tutorial/index.fd7d9b66.css">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<meta name="description" content="Strudel REPL">
|
<meta name="description" content="Strudel REPL">
|
||||||
<title>Strudel Tutorial</title>
|
<title>Strudel Tutorial</title>
|
||||||
|
|
@ -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.acaced6c.js" defer=""></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
|
|
@ -1936,6 +1936,7 @@ function peg$parse(input, options) {
|
||||||
this.type_ = "element";
|
this.type_ = "element";
|
||||||
this.source_ = source;
|
this.source_ = source;
|
||||||
this.options_ = options;
|
this.options_ = options;
|
||||||
|
this.location_ = location();
|
||||||
}
|
}
|
||||||
|
|
||||||
var CommandStub = function(name, options)
|
var CommandStub = function(name, options)
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@
|
||||||
this.type_ = "element";
|
this.type_ = "element";
|
||||||
this.source_ = source;
|
this.source_ = source;
|
||||||
this.options_ = options;
|
this.options_ = options;
|
||||||
|
this.location_ = location();
|
||||||
}
|
}
|
||||||
|
|
||||||
var CommandStub = function(name, options)
|
var CommandStub = function(name, options)
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import React, { useCallback, useLayoutEffect, useRef } from 'react';
|
import React, { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
|
||||||
import * as Tone from 'tone';
|
import * as Tone from 'tone';
|
||||||
import CodeMirror from './CodeMirror';
|
import CodeMirror, { markEvent } from './CodeMirror';
|
||||||
import cx from './cx';
|
import cx from './cx';
|
||||||
import { evaluate } from './evaluate';
|
import { evaluate } from './evaluate';
|
||||||
import logo from './logo.svg';
|
import logo from './logo.svg';
|
||||||
|
|
@ -35,9 +35,11 @@ function getRandomTune() {
|
||||||
const randomTune = getRandomTune();
|
const randomTune = getRandomTune();
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
|
const [editor, setEditor] = useState<any>();
|
||||||
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(markEvent(editor), [editor]),
|
||||||
});
|
});
|
||||||
const logBox = useRef<any>();
|
const logBox = useRef<any>();
|
||||||
// scroll log box to bottom when log changes
|
// scroll log box to bottom when log changes
|
||||||
|
|
@ -106,10 +108,13 @@ function App() {
|
||||||
<div className={cx('h-full bg-[#2A3236]', error ? 'focus:ring-red-500' : 'focus:ring-slate-800')}>
|
<div className={cx('h-full bg-[#2A3236]', error ? 'focus:ring-red-500' : 'focus:ring-slate-800')}>
|
||||||
<CodeMirror
|
<CodeMirror
|
||||||
value={code}
|
value={code}
|
||||||
|
editorDidMount={setEditor}
|
||||||
options={{
|
options={{
|
||||||
mode: 'javascript',
|
mode: 'javascript',
|
||||||
theme: 'material',
|
theme: 'material',
|
||||||
lineNumbers: true,
|
lineNumbers: true,
|
||||||
|
styleSelectedText: true,
|
||||||
|
cursorBlinkRate: 0,
|
||||||
}}
|
}}
|
||||||
onChange={(_: any, __: any, value: any) => setCode(value)}
|
onChange={(_: any, __: any, value: any) => setCode(value)}
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -2,14 +2,39 @@ import React from 'react';
|
||||||
import { Controlled as CodeMirror2 } from 'react-codemirror2';
|
import { Controlled as CodeMirror2 } from 'react-codemirror2';
|
||||||
import 'codemirror/mode/javascript/javascript.js';
|
import 'codemirror/mode/javascript/javascript.js';
|
||||||
import 'codemirror/mode/pegjs/pegjs.js';
|
import 'codemirror/mode/pegjs/pegjs.js';
|
||||||
import 'codemirror/theme/material.css';
|
// import 'codemirror/theme/material.css';
|
||||||
import 'codemirror/lib/codemirror.css';
|
import 'codemirror/lib/codemirror.css';
|
||||||
|
import 'codemirror/theme/material.css';
|
||||||
|
|
||||||
export default function CodeMirror({ value, onChange, options }: any) {
|
export default function CodeMirror({ value, onChange, options, editorDidMount }: any) {
|
||||||
options = options || {
|
options = options || {
|
||||||
mode: 'javascript',
|
mode: 'javascript',
|
||||||
theme: 'material',
|
theme: 'material',
|
||||||
lineNumbers: true,
|
lineNumbers: true,
|
||||||
|
styleSelectedText: true,
|
||||||
|
cursorBlinkRate: 500,
|
||||||
};
|
};
|
||||||
return <CodeMirror2 value={value} options={options} onBeforeChange={onChange} />;
|
return <CodeMirror2 value={value} options={options} onBeforeChange={onChange} editorDidMount={editorDidMount} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const markEvent = (editor) => (event) => {
|
||||||
|
const locs = event.value.locations;
|
||||||
|
if (!locs || !editor) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// mark active event
|
||||||
|
const marks = locs.map(({ start, end }) =>
|
||||||
|
editor
|
||||||
|
.getDoc()
|
||||||
|
.markText(
|
||||||
|
{ line: start.line - 1, ch: start.column },
|
||||||
|
{ line: end.line - 1, ch: end.column },
|
||||||
|
{ css: 'background-color: #FFCA28; color: black' }
|
||||||
|
)
|
||||||
|
);
|
||||||
|
//Tone.Transport.schedule(() => { // problem: this can be cleared by scheduler...
|
||||||
|
setTimeout(() => {
|
||||||
|
marks.forEach((mark) => mark.clear());
|
||||||
|
// }, '+' + event.duration * 0.5);
|
||||||
|
}, event.duration * 0.9 * 1000);
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@ import './tone';
|
||||||
import './midi';
|
import './midi';
|
||||||
import './voicings';
|
import './voicings';
|
||||||
import './tonal';
|
import './tonal';
|
||||||
import './groove';
|
|
||||||
import shapeshifter from './shapeshifter';
|
import shapeshifter from './shapeshifter';
|
||||||
import { minify } from './parse';
|
import { minify } from './parse';
|
||||||
import * as Tone from 'tone';
|
import * as Tone from 'tone';
|
||||||
|
|
@ -32,14 +31,17 @@ Object.assign(globalThis, bootstrapped, Tone, toneHelpers);
|
||||||
|
|
||||||
export const evaluate: any = (code: string) => {
|
export const evaluate: any = (code: string) => {
|
||||||
const shapeshifted = shapeshifter(code); // transform syntactically correct js code to semantically usable code
|
const shapeshifted = shapeshifter(code); // transform syntactically correct js code to semantically usable code
|
||||||
|
// console.log('shapeshifted', shapeshifted);
|
||||||
let evaluated = eval(shapeshifted);
|
let evaluated = eval(shapeshifted);
|
||||||
if (typeof evaluated === 'function') {
|
if (typeof evaluated === 'function') {
|
||||||
evaluated = evaluated();
|
evaluated = evaluated();
|
||||||
}
|
}
|
||||||
const pattern = minify(evaluated); // eval and minify (if user entered a string)
|
if (typeof evaluated === 'string') {
|
||||||
if (pattern?.constructor?.name !== 'Pattern') {
|
evaluated = strudel.withLocationOffset(minify(evaluated), { start: { line: 1, column: -1 } });
|
||||||
const message = `got "${typeof pattern}" instead of pattern`;
|
|
||||||
throw new Error(message + (typeof pattern === 'function' ? ', did you forget to call a function?' : '.'));
|
|
||||||
}
|
}
|
||||||
return { mode: 'javascript', pattern: pattern };
|
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?' : '.'));
|
||||||
|
}
|
||||||
|
return { mode: 'javascript', pattern: evaluated };
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
import { Pattern as _Pattern } from '../../strudel.mjs';
|
|
||||||
|
|
||||||
const Pattern = _Pattern as any;
|
|
||||||
|
|
||||||
// is this the same as struct?
|
|
||||||
Pattern.prototype.groove = function (groove) {
|
|
||||||
return groove.fmap(() => (v) => v).appLeft(this);
|
|
||||||
};
|
|
||||||
|
|
||||||
Pattern.prototype.define('groove', (groove, pat) => pat.groove(groove), { composable: true });
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import * as krill from '../krill-parser';
|
import * as krill from '../krill-parser';
|
||||||
import * as strudel from '../../strudel.mjs';
|
import * as strudel from '../../strudel.mjs';
|
||||||
import { Scale, Note, Interval } from '@tonaljs/tonal';
|
import { Scale, Note, Interval } from '@tonaljs/tonal';
|
||||||
|
import { addMiniLocations } from './shapeshifter';
|
||||||
|
|
||||||
const { pure, Pattern, Fraction, stack, slowcat, sequence, timeCat, silence } = strudel;
|
const { pure, Pattern, Fraction, stack, slowcat, sequence, timeCat, silence } = strudel;
|
||||||
|
|
||||||
|
|
@ -49,6 +50,7 @@ function resolveReplications(ast) {
|
||||||
{
|
{
|
||||||
type_: 'element',
|
type_: 'element',
|
||||||
source_: child.source_,
|
source_: child.source_,
|
||||||
|
location_: child.location_,
|
||||||
options_: {
|
options_: {
|
||||||
operator: {
|
operator: {
|
||||||
type_: 'stretch',
|
type_: 'stretch',
|
||||||
|
|
@ -91,7 +93,18 @@ export function patternifyAST(ast: any): any {
|
||||||
return silence;
|
return silence;
|
||||||
}
|
}
|
||||||
if (typeof ast.source_ !== 'object') {
|
if (typeof ast.source_ !== 'object') {
|
||||||
return ast.source_;
|
if (!addMiniLocations) {
|
||||||
|
return ast.source_;
|
||||||
|
}
|
||||||
|
if (!ast.location_) {
|
||||||
|
console.warn('no location for', ast);
|
||||||
|
return ast.source_;
|
||||||
|
}
|
||||||
|
const { start, end } = ast.location_;
|
||||||
|
// return ast.source_;
|
||||||
|
// the following line expects the shapeshifter to wrap this in withLocationOffset
|
||||||
|
// because location_ is only relative to the mini string, but we need it relative to whole code
|
||||||
|
return pure(ast.source_).withLocation({ start, end });
|
||||||
}
|
}
|
||||||
return patternifyAST(ast.source_);
|
return patternifyAST(ast.source_);
|
||||||
case 'stretch':
|
case 'stretch':
|
||||||
|
|
|
||||||
|
|
@ -1,30 +1,342 @@
|
||||||
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'; // npm module does not work in the browser
|
import traverser from './shift-traverser'; // npm module does not work in the browser
|
||||||
const { replace } = traverser;
|
const { replace } = traverser;
|
||||||
import { LiteralStringExpression, IdentifierExpression } from 'shift-ast';
|
import {
|
||||||
|
LiteralStringExpression,
|
||||||
|
IdentifierExpression,
|
||||||
|
CallExpression,
|
||||||
|
StaticMemberExpression,
|
||||||
|
Script,
|
||||||
|
} from 'shift-ast';
|
||||||
import codegen from 'shift-codegen';
|
import codegen from 'shift-codegen';
|
||||||
|
import * as strudel from '../../strudel.mjs';
|
||||||
|
|
||||||
|
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 const addMiniLocations = true;
|
||||||
|
|
||||||
|
/*
|
||||||
|
not supported for highlighting:
|
||||||
|
- 'b3'.p
|
||||||
|
- mini('b3') / m('b3')
|
||||||
|
- 'b3'.m / 'b3'.mini
|
||||||
|
*/
|
||||||
|
|
||||||
export default (code) => {
|
export default (code) => {
|
||||||
const ast = parseScript(code);
|
const ast = parseScriptWithLocation(code);
|
||||||
const shifted = replace(ast, {
|
const artificialNodes = [];
|
||||||
|
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) => 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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] });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 'xxx'.m
|
||||||
|
if (isStringWithDoubleQuotes(node, ast.locations, code)) {
|
||||||
|
const minified = getMinified(node.value);
|
||||||
|
return wrapLocationOffset(minified, node, ast.locations, artificialNodes);
|
||||||
|
}
|
||||||
|
|
||||||
|
// operator overloading => still not done
|
||||||
|
const operators = {
|
||||||
|
'*': 'fast',
|
||||||
|
'/': 'slow',
|
||||||
|
'&': 'stack',
|
||||||
|
'&&': 'append',
|
||||||
|
};
|
||||||
|
if (
|
||||||
|
node.type === 'BinaryExpression' &&
|
||||||
|
operators[node.operator] &&
|
||||||
|
['LiteralNumericExpression', 'LiteralStringExpression', 'IdentifierExpression'].includes(node.right?.type) &&
|
||||||
|
canBeOverloaded(node.left)
|
||||||
|
) {
|
||||||
|
let arg = node.left;
|
||||||
|
if (node.left.type === 'IdentifierExpression') {
|
||||||
|
arg = wrapFunction('reify', node.left);
|
||||||
|
}
|
||||||
|
return new CallExpression({
|
||||||
|
callee: new StaticMemberExpression({
|
||||||
|
property: operators[node.operator],
|
||||||
|
object: wrapFunction('reify', arg),
|
||||||
|
}),
|
||||||
|
arguments: [node.right],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const isMarkable = isPatternArg(parents) || hasModifierCall(parent);
|
||||||
|
// add to location to pure(x) calls
|
||||||
|
if (node.type === 'CallExpression' && node.callee.name === 'pure') {
|
||||||
|
return reifyWithLocation(node.arguments[0].name, node.arguments[0], ast.locations, artificialNodes);
|
||||||
|
}
|
||||||
|
// 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 reifyWithLocation(value, node, ast.locations, artificialNodes);
|
||||||
|
}
|
||||||
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 reifyWithLocation(node.value, 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);
|
||||||
|
}
|
||||||
|
return node;
|
||||||
|
},
|
||||||
|
leave() {
|
||||||
|
parents.pop();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
return codegen(shifted);
|
return codegen(shifted);
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: turn x.groove['[~ x]*2'] into x.groove('[~ x]*2'.m)
|
function wrapFunction(name, ...args) {
|
||||||
// and ['c1*2'].xx into 'c1*2'.m.xx ??
|
return new CallExpression({
|
||||||
// or just all templated strings?? x.groove(`[~ x]*2`)
|
callee: new IdentifierExpression({ name }),
|
||||||
|
arguments: args,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function getMinified(value) {
|
||||||
|
return new StaticMemberExpression({
|
||||||
|
object: new LiteralStringExpression({ value }),
|
||||||
|
property: 'm',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function isBackTickString(node) {
|
||||||
|
return node.type === 'TemplateExpression' && node.elements.length === 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isStringWithDoubleQuotes(node, locations, code) {
|
||||||
|
if (node.type !== 'LiteralStringExpression') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const loc = locations.get(node);
|
||||||
|
const snippet = code.slice(loc.start.offset, loc.end.offset);
|
||||||
|
return snippet[0] === '"'; // we can trust the end is also ", as the parsing did not fail
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns true if the given parents belong to a pattern argument node
|
||||||
|
// this is used to check if a node should receive a location for highlighting
|
||||||
|
function isPatternArg(parents) {
|
||||||
|
if (!parents.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const ancestors = parents.slice(0, -1);
|
||||||
|
const parent = parents[parents.length - 1];
|
||||||
|
if (isPatternFactory(parent)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (parent?.type === 'ArrayExpression') {
|
||||||
|
return isPatternArg(ancestors);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasModifierCall(parent) {
|
||||||
|
// TODO: modifiers are more than composables, for example every is not composable but should be seen as modifier..
|
||||||
|
// need all prototypes of Pattern
|
||||||
|
return (
|
||||||
|
parent?.type === 'StaticMemberExpression' && Object.keys(Pattern.prototype.composable).includes(parent.property)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isPatternFactory(node) {
|
||||||
|
return node?.type === 'CallExpression' && Object.keys(Pattern.prototype.factories).includes(node.callee.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
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(value, node, locations, artificialNodes) {
|
||||||
|
const withLocation = new CallExpression({
|
||||||
|
callee: new StaticMemberExpression({
|
||||||
|
object: wrapFunction('reify', new LiteralStringExpression({ value })),
|
||||||
|
property: 'withLocation',
|
||||||
|
}),
|
||||||
|
arguments: [getLocationObject(node, locations)],
|
||||||
|
});
|
||||||
|
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,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,12 +4,12 @@ import { Pattern as _Pattern } from '../../strudel.mjs';
|
||||||
const Pattern = _Pattern as any;
|
const Pattern = _Pattern as any;
|
||||||
|
|
||||||
export declare interface NoteEvent {
|
export declare interface NoteEvent {
|
||||||
value: string;
|
value: string | number;
|
||||||
scale?: string;
|
scale?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
function toNoteEvent(event: string | NoteEvent): NoteEvent {
|
function toNoteEvent(event: string | NoteEvent): NoteEvent {
|
||||||
if (typeof event === 'string') {
|
if (typeof event === 'string' || typeof event === 'number') {
|
||||||
return { value: event };
|
return { value: event };
|
||||||
}
|
}
|
||||||
if (event.value) {
|
if (event.value) {
|
||||||
|
|
@ -73,6 +73,10 @@ Pattern.prototype._transpose = function (intervalOrSemitones: string | number) {
|
||||||
const interval = !isNaN(Number(intervalOrSemitones))
|
const interval = !isNaN(Number(intervalOrSemitones))
|
||||||
? Interval.fromSemitones(intervalOrSemitones as number)
|
? Interval.fromSemitones(intervalOrSemitones as number)
|
||||||
: String(intervalOrSemitones);
|
: String(intervalOrSemitones);
|
||||||
|
if (typeof value === 'number') {
|
||||||
|
const semitones = typeof interval === 'string' ? Interval.semitones(interval) || 0 : interval;
|
||||||
|
return { value: value + semitones };
|
||||||
|
}
|
||||||
return { value: Note.transpose(value, interval), scale };
|
return { value: Note.transpose(value, interval), scale };
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
@ -86,7 +90,10 @@ Pattern.prototype._transpose = function (intervalOrSemitones: string | number) {
|
||||||
Pattern.prototype._scaleTranspose = function (offset: number | string) {
|
Pattern.prototype._scaleTranspose = function (offset: number | string) {
|
||||||
return this._mapNotes(({ value, scale }: NoteEvent) => {
|
return this._mapNotes(({ value, scale }: NoteEvent) => {
|
||||||
if (!scale) {
|
if (!scale) {
|
||||||
throw new Error('can only use scaleOffset after .scale');
|
throw new Error('can only use scaleTranspose after .scale');
|
||||||
|
}
|
||||||
|
if (typeof value !== 'string') {
|
||||||
|
throw new Error('can only use scaleTranspose with notes');
|
||||||
}
|
}
|
||||||
return { value: scaleTranspose(scale, Number(offset), value), scale };
|
return { value: scaleTranspose(scale, Number(offset), value), scale };
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,15 @@
|
||||||
export const timeCatMini = `stack(
|
export const timeCatMini = `stack(
|
||||||
'c3@3 [eb3, g3, [c4 d4]/2]'.mini,
|
"c3@3 [eb3, g3, [c4 d4]/2]",
|
||||||
'c2 g2'.mini,
|
"c2 g2",
|
||||||
'[eb4@5 [f4 eb4 d4]@3] [eb4 c4]/2'.mini.slow(8)
|
"[eb4@5 [f4 eb4 d4]@3] [eb4 c4]/2".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)
|
||||||
)`;
|
)`;
|
||||||
|
|
||||||
|
|
@ -58,48 +58,48 @@ export const tetrisWithFunctions = `stack(sequence(
|
||||||
).slow(16)`;
|
).slow(16)`;
|
||||||
|
|
||||||
export const tetris = `stack(
|
export const tetris = `stack(
|
||||||
mini(
|
cat(
|
||||||
'e5 [b4 c5] d5 [c5 b4]',
|
"e5 [b4 c5] d5 [c5 b4]",
|
||||||
'a4 [a4 c5] e5 [d5 c5]',
|
"a4 [a4 c5] e5 [d5 c5]",
|
||||||
'b4 [~ c5] d5 e5',
|
"b4 [~ c5] d5 e5",
|
||||||
'c5 a4 a4 ~',
|
"c5 a4 a4 ~",
|
||||||
'[~ d5] [~ f5] a5 [g5 f5]',
|
"[~ d5] [~ f5] a5 [g5 f5]",
|
||||||
'e5 [~ c5] e5 [d5 c5]',
|
"e5 [~ c5] e5 [d5 c5]",
|
||||||
'b4 [b4 c5] d5 e5',
|
"b4 [b4 c5] d5 e5",
|
||||||
'c5 a4 a4 ~'
|
"c5 a4 a4 ~"
|
||||||
),
|
),
|
||||||
mini(
|
cat(
|
||||||
'e2 e3 e2 e3 e2 e3 e2 e3',
|
"e2 e3 e2 e3 e2 e3 e2 e3",
|
||||||
'a2 a3 a2 a3 a2 a3 a2 a3',
|
"a2 a3 a2 a3 a2 a3 a2 a3",
|
||||||
'g#2 g#3 g#2 g#3 e2 e3 e2 e3',
|
"g#2 g#3 g#2 g#3 e2 e3 e2 e3",
|
||||||
'a2 a3 a2 a3 a2 a3 b1 c2',
|
"a2 a3 a2 a3 a2 a3 b1 c2",
|
||||||
'd2 d3 d2 d3 d2 d3 d2 d3',
|
"d2 d3 d2 d3 d2 d3 d2 d3",
|
||||||
'c2 c3 c2 c3 c2 c3 c2 c3',
|
"c2 c3 c2 c3 c2 c3 c2 c3",
|
||||||
'b1 b2 b1 b2 e2 e3 e2 e3',
|
"b1 b2 b1 b2 e2 e3 e2 e3",
|
||||||
'a1 a2 a1 a2 a1 a2 a1 a2'
|
"a1 a2 a1 a2 a1 a2 a1 a2",
|
||||||
)
|
)
|
||||||
).slow(16)`;
|
).slow(16)`;
|
||||||
|
|
||||||
export const tetrisRev = `stack(
|
export const tetrisRev = `stack(
|
||||||
mini(
|
cat(
|
||||||
'e5 [b4 c5] d5 [c5 b4]',
|
"e5 [b4 c5] d5 [c5 b4]",
|
||||||
'a4 [a4 c5] e5 [d5 c5]',
|
"a4 [a4 c5] e5 [d5 c5]",
|
||||||
'b4 [~ c5] d5 e5',
|
"b4 [~ c5] d5 e5",
|
||||||
'c5 a4 a4 ~',
|
"c5 a4 a4 ~",
|
||||||
'[~ d5] [~ f5] a5 [g5 f5]',
|
"[~ d5] [~ f5] a5 [g5 f5]",
|
||||||
'e5 [~ c5] e5 [d5 c5]',
|
"e5 [~ c5] e5 [d5 c5]",
|
||||||
'b4 [b4 c5] d5 e5',
|
"b4 [b4 c5] d5 e5",
|
||||||
'c5 a4 a4 ~'
|
"c5 a4 a4 ~",
|
||||||
).rev(),
|
).rev(),
|
||||||
mini(
|
cat(
|
||||||
'e2 e3 e2 e3 e2 e3 e2 e3',
|
"e2 e3 e2 e3 e2 e3 e2 e3",
|
||||||
'a2 a3 a2 a3 a2 a3 a2 a3',
|
"a2 a3 a2 a3 a2 a3 a2 a3",
|
||||||
'g#2 g#3 g#2 g#3 e2 e3 e2 e3',
|
"g#2 g#3 g#2 g#3 e2 e3 e2 e3",
|
||||||
'a2 a3 a2 a3 a2 a3 b1 c2',
|
"a2 a3 a2 a3 a2 a3 b1 c2",
|
||||||
'd2 d3 d2 d3 d2 d3 d2 d3',
|
"d2 d3 d2 d3 d2 d3 d2 d3",
|
||||||
'c2 c3 c2 c3 c2 c3 c2 c3',
|
"c2 c3 c2 c3 c2 c3 c2 c3",
|
||||||
'b1 b2 b1 b2 e2 e3 e2 e3',
|
"b1 b2 b1 b2 e2 e3 e2 e3",
|
||||||
'a1 a2 a1 a2 a1 a2 a1 a2'
|
"a1 a2 a1 a2 a1 a2 a1 a2",
|
||||||
).rev()
|
).rev()
|
||||||
).slow(16)`;
|
).slow(16)`;
|
||||||
|
|
||||||
|
|
@ -112,7 +112,7 @@ export const tetrisRev = `stack(
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* export const tetrisMini1 =
|
/* export const tetrisMini1 =
|
||||||
"'[[e5 [b4 c5] d5 [c5 b4]] [a4 [a4 c5] e5 [d5 c5]] [b4 [~ c5] d5 e5] [c5 a4 a4 ~] [[~ d5] [~ f5] a5 [g5 f5]] [e5 [~ c5] e5 [d5 c5]] [b4 [b4 c5] d5 e5] [c5 a4 a4 ~]],[[e2 e3 e2 e3 e2 e3 e2 e3] [a2 a3 a2 a3 a2 a3 a2 a3] [g#2 g#3 g#2 g#3 e2 e3 e2 e3] [a2 a3 a2 a3 a2 a3 b1 c2] [d2 d3 d2 d3 d2 d3 d2 d3] [c2 c3 c2 c3 c2 c3 c2 c3] [b1 b2 b1 b2 e2 e3 e2 e3] [a1 a2 a1 a2 a1 a2 a1 a2]]'.mini.slow(16)";
|
'"[[e5 [b4 c5] d5 [c5 b4]] [a4 [a4 c5] e5 [d5 c5]] [b4 [~ c5] d5 e5] [c5 a4 a4 ~] [[~ d5] [~ f5] a5 [g5 f5]] [e5 [~ c5] e5 [d5 c5]] [b4 [b4 c5] d5 e5] [c5 a4 a4 ~]],[[e2 e3 e2 e3 e2 e3 e2 e3] [a2 a3 a2 a3 a2 a3 a2 a3] [g#2 g#3 g#2 g#3 e2 e3 e2 e3] [a2 a3 a2 a3 a2 a3 b1 c2] [d2 d3 d2 d3 d2 d3 d2 d3] [c2 c3 c2 c3 c2 c3 c2 c3] [b1 b2 b1 b2 e2 e3 e2 e3] [a1 a2 a1 a2 a1 a2 a1 a2]]".slow(16)';
|
||||||
*/
|
*/
|
||||||
export const tetrisMini = `\`[[e5 [b4 c5] d5 [c5 b4]]
|
export const tetrisMini = `\`[[e5 [b4 c5] d5 [c5 b4]]
|
||||||
[a4 [a4 c5] e5 [d5 c5]]
|
[a4 [a4 c5] e5 [d5 c5]]
|
||||||
|
|
@ -129,7 +129,7 @@ export const tetrisMini = `\`[[e5 [b4 c5] d5 [c5 b4]]
|
||||||
[[d2 d3]*4]
|
[[d2 d3]*4]
|
||||||
[[c2 c3]*4]
|
[[c2 c3]*4]
|
||||||
[[b1 b2]*2 [e2 e3]*2]
|
[[b1 b2]*2 [e2 e3]*2]
|
||||||
[[a1 a2]*4]\`.mini.slow(16)
|
[[a1 a2]*4]\`.slow(16)
|
||||||
`;
|
`;
|
||||||
|
|
||||||
/* export const tetrisHaskellH = `h(\`slow 16 $ "[[e5 [b4 c5] d5 [c5 b4]]
|
/* export const tetrisHaskellH = `h(\`slow 16 $ "[[e5 [b4 c5] d5 [c5 b4]]
|
||||||
|
|
@ -178,134 +178,127 @@ export const spanish = `slowcat(
|
||||||
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(
|
cat(
|
||||||
'~',
|
"~",
|
||||||
'~',
|
"~",
|
||||||
'~',
|
"~",
|
||||||
'A5 [F5@2 C5] [D5@2 F5] F5',
|
"A5 [F5@2 C5] [D5@2 F5] F5",
|
||||||
'[C5@2 F5] [F5@2 C6] A5 G5',
|
"[C5@2 F5] [F5@2 C6] A5 G5",
|
||||||
'A5 [F5@2 C5] [D5@2 F5] F5',
|
"A5 [F5@2 C5] [D5@2 F5] F5",
|
||||||
'[C5@2 F5] [Bb5 A5 G5] F5@2',
|
"[C5@2 F5] [Bb5 A5 G5] F5@2",
|
||||||
'A5 [F5@2 C5] [D5@2 F5] F5',
|
"A5 [F5@2 C5] [D5@2 F5] F5",
|
||||||
'[C5@2 F5] [F5@2 C6] A5 G5',
|
"[C5@2 F5] [F5@2 C6] A5 G5",
|
||||||
'A5 [F5@2 C5] [D5@2 F5] F5',
|
"A5 [F5@2 C5] [D5@2 F5] F5",
|
||||||
'[C5@2 F5] [Bb5 A5 G5] F5@2',
|
"[C5@2 F5] [Bb5 A5 G5] F5@2",
|
||||||
'A5 [F5@2 C5] A5 F5',
|
"A5 [F5@2 C5] A5 F5",
|
||||||
'Ab5 [F5@2 Ab5] G5@2',
|
"Ab5 [F5@2 Ab5] G5@2",
|
||||||
'A5 [F5@2 C5] A5 F5',
|
"A5 [F5@2 C5] A5 F5",
|
||||||
'Ab5 [F5@2 C5] C6@2',
|
"Ab5 [F5@2 C5] C6@2",
|
||||||
'A5 [F5@2 C5] [D5@2 F5] F5',
|
"A5 [F5@2 C5] [D5@2 F5] F5",
|
||||||
'[C5@2 F5] [Bb5 A5 G5] F5@2'
|
"[C5@2 F5] [Bb5 A5 G5] F5@2"
|
||||||
),
|
),
|
||||||
mini(
|
cat(
|
||||||
'[F4,Bb4,D5] [[D4,G4,Bb4]@2 [Bb3,D4,F4]] [[G3,C4,E4]@2 [[Ab3,F4] [A3,Gb4]]] [Bb3,E4,G4]',
|
"[F4,Bb4,D5] [[D4,G4,Bb4]@2 [Bb3,D4,F4]] [[G3,C4,E4]@2 [[Ab3,F4] [A3,Gb4]]] [Bb3,E4,G4]",
|
||||||
'[~ [F3, A3, C3] [F3, A3, C3]] [~ [F3, A3, C3] [F3, A3, C3]] [~ [F3, Bb3, D3] [F3, Bb3, D3]] [~ [F3, Bb3, Db3] [F3, Bb3, Db3]]',
|
"[~ [F3, A3, C3] [F3, A3, C3]] [~ [F3, A3, C3] [F3, A3, C3]] [~ [F3, Bb3, D3] [F3, Bb3, D3]] [~ [F3, Bb3, Db3] [F3, Bb3, Db3]]",
|
||||||
'[~ [F3, A3, C3] [F3, A3, C3]] [~ [F3, A3, C3] [F3, A3, C3]] [~ [F3, Bb3, D3] [F3, Bb3, D3]] [~ [F3, B3, D3] [F3, B3, D3]]',
|
"[~ [F3, A3, C3] [F3, A3, C3]] [~ [F3, A3, C3] [F3, A3, C3]] [~ [F3, Bb3, D3] [F3, Bb3, D3]] [~ [F3, B3, D3] [F3, B3, D3]]",
|
||||||
'[~ [F3, A3, C3] [F3, A3, C3]] [~ [F3, A3, C3] [F3, A3, C3]] [~ [F3, Bb3, D3] [F3, Bb3, D3]] [~ [F3, B3, D3] [F3, B3, D3]]',
|
"[~ [F3, A3, C3] [F3, A3, C3]] [~ [F3, A3, C3] [F3, A3, C3]] [~ [F3, Bb3, D3] [F3, Bb3, D3]] [~ [F3, B3, D3] [F3, B3, D3]]",
|
||||||
'[~ [A3, C4, E4] [A3, C4, E4]] [~ [Ab3, C4, Eb4] [Ab3, C4, Eb4]] [~ [F3, Bb3, D3] [F3, Bb3, D3]] [~ [G3, C4, E4] [G3, C4, E4]]',
|
"[~ [A3, C4, E4] [A3, C4, E4]] [~ [Ab3, C4, Eb4] [Ab3, C4, Eb4]] [~ [F3, Bb3, D3] [F3, Bb3, D3]] [~ [G3, C4, E4] [G3, C4, E4]]",
|
||||||
'[~ [F3, A3, C4] [F3, A3, C4]] [~ [F3, A3, C4] [F3, A3, C4]] [~ [F3, Bb3, D3] [F3, Bb3, D3]] [~ [F3, B3, D3] [F3, B3, D3]]',
|
"[~ [F3, A3, C4] [F3, A3, C4]] [~ [F3, A3, C4] [F3, A3, C4]] [~ [F3, Bb3, D3] [F3, Bb3, D3]] [~ [F3, B3, D3] [F3, B3, D3]]",
|
||||||
'[~ [F3, Bb3, D4] [F3, Bb3, D4]] [~ [F3, Bb3, C4] [F3, Bb3, C4]] [~ [F3, A3, C4] [F3, A3, C4]] [~ [F3, A3, C4] [F3, A3, C4]]',
|
"[~ [F3, Bb3, D4] [F3, Bb3, D4]] [~ [F3, Bb3, C4] [F3, Bb3, C4]] [~ [F3, A3, C4] [F3, A3, C4]] [~ [F3, A3, C4] [F3, A3, C4]]",
|
||||||
'[~ [F3, A3, C3] [F3, A3, C3]] [~ [F3, A3, C3] [F3, A3, C3]] [~ [F3, Bb3, D3] [F3, Bb3, D3]] [~ [F3, B3, D3] [F3, B3, D3]]',
|
"[~ [F3, A3, C3] [F3, A3, C3]] [~ [F3, A3, C3] [F3, A3, C3]] [~ [F3, Bb3, D3] [F3, Bb3, D3]] [~ [F3, B3, D3] [F3, B3, D3]]",
|
||||||
'[~ [A3, C4, E4] [A3, C4, E4]] [~ [Ab3, C4, Eb4] [Ab3, C4, Eb4]] [~ [F3, Bb3, D3] [F3, Bb3, D3]] [~ [G3, C4, E4] [G3, C4, E4]]',
|
"[~ [A3, C4, E4] [A3, C4, E4]] [~ [Ab3, C4, Eb4] [Ab3, C4, Eb4]] [~ [F3, Bb3, D3] [F3, Bb3, D3]] [~ [G3, C4, E4] [G3, C4, E4]]",
|
||||||
'[~ [F3, A3, C3] [F3, A3, C3]] [~ [F3, A3, C3] [F3, A3, C3]] [~ [F3, Bb3, D3] [F3, Bb3, D3]] [~ [F3, B3, D3] [F3, B3, D3]]',
|
"[~ [F3, A3, C3] [F3, A3, C3]] [~ [F3, A3, C3] [F3, A3, C3]] [~ [F3, Bb3, D3] [F3, Bb3, D3]] [~ [F3, B3, D3] [F3, B3, D3]]",
|
||||||
'[~ [F3, Bb3, D4] [F3, Bb3, D4]] [~ [F3, Bb3, C4] [F3, Bb3, C4]] [~ [F3, A3, C4] [F3, A3, C4]] [~ [F3, A3, C4] [F3, A3, C4]]',
|
"[~ [F3, Bb3, D4] [F3, Bb3, D4]] [~ [F3, Bb3, C4] [F3, Bb3, C4]] [~ [F3, A3, C4] [F3, A3, C4]] [~ [F3, A3, C4] [F3, A3, C4]]",
|
||||||
'[~ [Bb3, D3, F4] [Bb3, D3, F4]] [~ [Bb3, D3, F4] [Bb3, D3, F4]] [~ [A3, C4, F4] [A3, C4, F4]] [~ [A3, C4, F4] [A3, C4, F4]]',
|
"[~ [Bb3, D3, F4] [Bb3, D3, F4]] [~ [Bb3, D3, F4] [Bb3, D3, F4]] [~ [A3, C4, F4] [A3, C4, F4]] [~ [A3, C4, F4] [A3, C4, F4]]",
|
||||||
'[~ [Ab3, B3, F4] [Ab3, B3, F4]] [~ [Ab3, B3, F4] [Ab3, B3, F4]] [~ [G3, Bb3, F4] [G3, Bb3, F4]] [~ [G3, Bb3, E4] [G3, Bb3, E4]]',
|
"[~ [Ab3, B3, F4] [Ab3, B3, F4]] [~ [Ab3, B3, F4] [Ab3, B3, F4]] [~ [G3, Bb3, F4] [G3, Bb3, F4]] [~ [G3, Bb3, E4] [G3, Bb3, E4]]",
|
||||||
'[~ [Bb3, D3, F4] [Bb3, D3, F4]] [~ [Bb3, D3, F4] [Bb3, D3, F4]] [~ [A3, C4, F4] [A3, C4, F4]] [~ [A3, C4, F4] [A3, C4, F4]]',
|
"[~ [Bb3, D3, F4] [Bb3, D3, F4]] [~ [Bb3, D3, F4] [Bb3, D3, F4]] [~ [A3, C4, F4] [A3, C4, F4]] [~ [A3, C4, F4] [A3, C4, F4]]",
|
||||||
'[~ [Ab3, B3, F4] [Ab3, B3, F4]] [~ [Ab3, B3, F4] [Ab3, B3, F4]] [~ [G3, Bb3, F4] [G3, Bb3, F4]] [~ [G3, Bb3, E4] [G3, Bb3, E4]]',
|
"[~ [Ab3, B3, F4] [Ab3, B3, F4]] [~ [Ab3, B3, F4] [Ab3, B3, F4]] [~ [G3, Bb3, F4] [G3, Bb3, F4]] [~ [G3, Bb3, E4] [G3, Bb3, E4]]",
|
||||||
'[~ [F3, A3, C3] [F3, A3, C3]] [~ [F3, A3, C3] [F3, A3, C3]] [~ [F3, Bb3, D3] [F3, Bb3, D3]] [~ [F3, B3, D3] [F3, B3, D3]]',
|
"[~ [F3, A3, C3] [F3, A3, C3]] [~ [F3, A3, C3] [F3, A3, C3]] [~ [F3, Bb3, D3] [F3, Bb3, D3]] [~ [F3, B3, D3] [F3, B3, D3]]",
|
||||||
'[~ [F3, Bb3, D4] [F3, Bb3, D4]] [~ [F3, Bb3, C4] [F3, Bb3, C4]] [~ [F3, A3, C4] [F3, A3, C4]] [~ [F3, A3, C4] [F3, A3, C4]]'
|
"[~ [F3, Bb3, D4] [F3, Bb3, D4]] [~ [F3, Bb3, C4] [F3, Bb3, C4]] [~ [F3, A3, C4] [F3, A3, C4]] [~ [F3, A3, C4] [F3, A3, C4]]"
|
||||||
),
|
),
|
||||||
mini(
|
cat(
|
||||||
'[G3 G3 C3 E3]',
|
"[G3 G3 C3 E3]",
|
||||||
'[F2 D2 G2 C2]',
|
"[F2 D2 G2 C2]",
|
||||||
'[F2 D2 G2 C2]',
|
"[F2 D2 G2 C2]",
|
||||||
'[F2 A2 Bb2 B2]',
|
"[F2 A2 Bb2 B2]",
|
||||||
'[A2 Ab2 G2 C2]',
|
"[A2 Ab2 G2 C2]",
|
||||||
'[F2 A2 Bb2 B2]',
|
"[F2 A2 Bb2 B2]",
|
||||||
'[G2 C2 F2 F2]',
|
"[G2 C2 F2 F2]",
|
||||||
'[F2 A2 Bb2 B2]',
|
"[F2 A2 Bb2 B2]",
|
||||||
'[A2 Ab2 G2 C2]',
|
"[A2 Ab2 G2 C2]",
|
||||||
'[F2 A2 Bb2 B2]',
|
"[F2 A2 Bb2 B2]",
|
||||||
'[G2 C2 F2 F2]',
|
"[G2 C2 F2 F2]",
|
||||||
'[Bb2 Bb2 A2 A2]',
|
"[Bb2 Bb2 A2 A2]",
|
||||||
'[Ab2 Ab2 G2 [C2 D2 E2]]',
|
"[Ab2 Ab2 G2 [C2 D2 E2]]",
|
||||||
'[Bb2 Bb2 A2 A2]',
|
"[Bb2 Bb2 A2 A2]",
|
||||||
'[Ab2 Ab2 G2 [C2 D2 E2]]',
|
"[Ab2 Ab2 G2 [C2 D2 E2]]",
|
||||||
'[F2 A2 Bb2 B2]',
|
"[F2 A2 Bb2 B2]",
|
||||||
'[G2 C2 F2 F2]'
|
"[G2 C2 F2 F2]"
|
||||||
)
|
)
|
||||||
).slow(51);
|
).slow(51);
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const giantSteps = `stack(
|
export const giantSteps = `stack(
|
||||||
// melody
|
// melody
|
||||||
mini(
|
cat(
|
||||||
'[F#5 D5] [B4 G4] Bb4 [B4 A4]',
|
"[F#5 D5] [B4 G4] Bb4 [B4 A4]",
|
||||||
'[D5 Bb4] [G4 Eb4] F#4 [G4 F4]',
|
"[D5 Bb4] [G4 Eb4] F#4 [G4 F4]",
|
||||||
'Bb4 [B4 A4] D5 [D#5 C#5]',
|
"Bb4 [B4 A4] D5 [D#5 C#5]",
|
||||||
'F#5 [G5 F5] Bb5 [F#5 F#5]',
|
"F#5 [G5 F5] Bb5 [F#5 F#5]",
|
||||||
),
|
),
|
||||||
// chords
|
// chords
|
||||||
mini(
|
cat(
|
||||||
'[B^7 D7] [G^7 Bb7] Eb^7 [Am7 D7]',
|
"[B^7 D7] [G^7 Bb7] Eb^7 [Am7 D7]",
|
||||||
'[G^7 Bb7] [Eb^7 F#7] B^7 [Fm7 Bb7]',
|
"[G^7 Bb7] [Eb^7 F#7] B^7 [Fm7 Bb7]",
|
||||||
'Eb^7 [Am7 D7] G^7 [C#m7 F#7]',
|
"Eb^7 [Am7 D7] G^7 [C#m7 F#7]",
|
||||||
'B^7 [Fm7 Bb7] Eb^7 [C#m7 F#7]'
|
"B^7 [Fm7 Bb7] Eb^7 [C#m7 F#7]"
|
||||||
).voicings(['E3', 'G4']),
|
).voicings(['E3', 'G4']),
|
||||||
// bass
|
// bass
|
||||||
mini(
|
cat(
|
||||||
'[B2 D2] [G2 Bb2] [Eb2 Bb3] [A2 D2]',
|
"[B2 D2] [G2 Bb2] [Eb2 Bb3] [A2 D2]",
|
||||||
'[G2 Bb2] [Eb2 F#2] [B2 F#2] [F2 Bb2]',
|
"[G2 Bb2] [Eb2 F#2] [B2 F#2] [F2 Bb2]",
|
||||||
'[Eb2 Bb2] [A2 D2] [G2 D2] [C#2 F#2]',
|
"[Eb2 Bb2] [A2 D2] [G2 D2] [C#2 F#2]",
|
||||||
'[B2 F#2] [F2 Bb2] [Eb2 Bb3] [C#2 F#2]'
|
"[B2 F#2] [F2 Bb2] [Eb2 Bb3] [C#2 F#2]"
|
||||||
)
|
)
|
||||||
).slow(20);`;
|
).slow(20);`;
|
||||||
|
|
||||||
export const giantStepsReggae = `stack(
|
export const giantStepsReggae = `stack(
|
||||||
// melody
|
// melody
|
||||||
mini(
|
cat(
|
||||||
'[F#5 D5] [B4 G4] Bb4 [B4 A4]',
|
"[F#5 D5] [B4 G4] Bb4 [B4 A4]",
|
||||||
'[D5 Bb4] [G4 Eb4] F#4 [G4 F4]',
|
"[D5 Bb4] [G4 Eb4] F#4 [G4 F4]",
|
||||||
'Bb4 [B4 A4] D5 [D#5 C#5]',
|
"Bb4 [B4 A4] D5 [D#5 C#5]",
|
||||||
'F#5 [G5 F5] Bb5 [F#5 [F#5 ~@3]]',
|
"F#5 [G5 F5] Bb5 [F#5 [F#5 ~@3]]",
|
||||||
),
|
),
|
||||||
// chords
|
// chords
|
||||||
mini(
|
cat(
|
||||||
'[B^7 D7] [G^7 Bb7] Eb^7 [Am7 D7]',
|
"[B^7 D7] [G^7 Bb7] Eb^7 [Am7 D7]",
|
||||||
'[G^7 Bb7] [Eb^7 F#7] B^7 [Fm7 Bb7]',
|
"[G^7 Bb7] [Eb^7 F#7] B^7 [Fm7 Bb7]",
|
||||||
'Eb^7 [Am7 D7] G^7 [C#m7 F#7]',
|
"Eb^7 [Am7 D7] G^7 [C#m7 F#7]",
|
||||||
'B^7 [Fm7 Bb7] Eb^7 [C#m7 F#7]'
|
"B^7 [Fm7 Bb7] Eb^7 [C#m7 F#7]"
|
||||||
)
|
)
|
||||||
.groove('~ [x ~]'.m.fast(4*8))
|
.struct("~ [x ~]".fast(4*8))
|
||||||
.voicings(['E3', 'G4']),
|
.voicings(['E3', 'G4']),
|
||||||
// bass
|
// bass
|
||||||
mini(
|
cat(
|
||||||
'[B2 D2] [G2 D2] [Eb2 Bb2] [A2 D2]',
|
"[B2 D2] [G2 D2] [Eb2 Bb2] [A2 D2]",
|
||||||
'[G2 Bb2] [Eb2 F#2] [B2 F#2] [F2 Bb2]',
|
"[G2 Bb2] [Eb2 F#2] [B2 F#2] [F2 Bb2]",
|
||||||
'[Eb2 Bb2] [A2 D2] [G2 D2] [C#2 F#2]',
|
"[Eb2 Bb2] [A2 D2] [G2 D2] [C#2 F#2]",
|
||||||
'[B2 F#2] [F2 Bb2] [Eb2 Bb2] [C#2 F#2]'
|
"[B2 F#2] [F2 Bb2] [Eb2 Bb2] [C#2 F#2]"
|
||||||
)
|
)
|
||||||
.groove('x ~'.m.fast(4*8))
|
.struct("x ~".fast(4*8))
|
||||||
).slow(25)`;
|
).slow(25)`;
|
||||||
|
|
||||||
/* export const transposedChords = `stack(
|
|
||||||
m('c2 eb2 g2'),
|
|
||||||
m('Cm7').voicings(['g2','c4']).slow(2)
|
|
||||||
).transpose(
|
|
||||||
slowcat(1, 2, 3, 2).slow(2)
|
|
||||||
).transpose(5)`; */
|
|
||||||
|
|
||||||
export const transposedChordsHacked = `stack(
|
export const transposedChordsHacked = `stack(
|
||||||
'c2 eb2 g2'.mini,
|
"c2 eb2 g2",
|
||||||
'Cm7'.pure.voicings(['g2','c4']).slow(2)
|
"Cm7".voicings(['g2','c4']).slow(2)
|
||||||
).transpose(
|
).transpose(
|
||||||
slowcat(1, 2, 3, 2).slow(2)
|
slowcat(1, 2, 3, 2).slow(2)
|
||||||
).transpose(5)`;
|
).transpose(5)`;
|
||||||
|
|
@ -315,35 +308,20 @@ export const scaleTranspose = `stack(f2, f3, c4, ab4)
|
||||||
.scaleTranspose(sequence(0, -1, -2, -3).slow(4))
|
.scaleTranspose(sequence(0, -1, -2, -3).slow(4))
|
||||||
.transpose(sequence(0, 1).slow(16))`;
|
.transpose(sequence(0, 1).slow(16))`;
|
||||||
|
|
||||||
/* export const groove = `stack(
|
export const struct = `stack(
|
||||||
m('c2 g2 a2 [e2@2 eb2] d2 a2 g2 [d2 ~ db2]'),
|
"c2 g2 a2 [e2@2 eb2] d2 a2 g2 [d2 ~ db2]",
|
||||||
m('[C^7 A7] [Dm7 G7]')
|
"[C^7 A7] [Dm7 G7]".struct("[x@2 x] [~@2 x] [~ x@2]@2 [x ~@2] ~ [~@2 x@4]@2")
|
||||||
.groove(m('[x@2 x] [~@2 x] [~ x@2]@2 [x ~@2] ~ [~@2 x@4]@2'))
|
|
||||||
.voicings(['G3','A4'])
|
|
||||||
).slow(4.5)`; */
|
|
||||||
|
|
||||||
export const groove = `stack(
|
|
||||||
'c2 g2 a2 [e2@2 eb2] d2 a2 g2 [d2 ~ db2]'.mini,
|
|
||||||
'[C^7 A7] [Dm7 G7]'.mini.groove('[x@2 x] [~@2 x] [~ x@2]@2 [x ~@2] ~ [~@2 x@4]@2'.mini)
|
|
||||||
.voicings(['G3','A4'])
|
.voicings(['G3','A4'])
|
||||||
).slow(4)`;
|
).slow(4)`;
|
||||||
|
|
||||||
/* export const magicSofa = `stack(
|
|
||||||
m('[C^7 F^7 ~]/3 [Dm7 G7 A7 ~]/4')
|
|
||||||
.every(2, fast(2))
|
|
||||||
.voicings(),
|
|
||||||
m('[c2 f2 g2]/3 [d2 g2 a2 e2]/4')
|
|
||||||
).slow(1)
|
|
||||||
.transpose.slowcat(0, 2, 3, 4)`; */
|
|
||||||
|
|
||||||
export const magicSofa = `stack(
|
export const magicSofa = `stack(
|
||||||
'<C^7 F^7 ~> <Dm7 G7 A7 ~>'.m
|
"<C^7 F^7 ~> <Dm7 G7 A7 ~>"
|
||||||
.every(2, fast(2))
|
.every(2, fast(2))
|
||||||
.voicings(),
|
.voicings(),
|
||||||
'<c2 f2 g2> <d2 g2 a2 e2>'.m
|
"<c2 f2 g2> <d2 g2 a2 e2>"
|
||||||
).slow(1).transpose.slowcat(0, 2, 3, 4)`;
|
).slow(1).transpose.slowcat(0, 2, 3, 4)`;
|
||||||
|
|
||||||
/* export const confusedPhone = `stack('[g2 ~@1.3] [c3 ~@1.3]'.mini.slow(2))
|
/* export const confusedPhone = `stack("[g2 ~@1.3] [c3 ~@1.3]".slow(2))
|
||||||
.superimpose(
|
.superimpose(
|
||||||
x => transpose(-12,x).late(0),
|
x => transpose(-12,x).late(0),
|
||||||
x => transpose(7,x).late(0.2),
|
x => transpose(7,x).late(0.2),
|
||||||
|
|
@ -355,7 +333,7 @@ export const magicSofa = `stack(
|
||||||
.scaleTranspose(slowcat(0,1,2,1).slow(2))
|
.scaleTranspose(slowcat(0,1,2,1).slow(2))
|
||||||
.synth('triangle').gain(0.5).filter(1500)`; */
|
.synth('triangle').gain(0.5).filter(1500)`; */
|
||||||
|
|
||||||
export const confusedPhoneDynamic = `stack('[g2 ~@1.3] [c3 ~@1.3]'.mini.slow(2))
|
export const confusedPhoneDynamic = `stack("[g2 ~@1.3] [c3 ~@1.3]".slow(2))
|
||||||
.superimpose(
|
.superimpose(
|
||||||
...[-12,7,10,12,24].slice(0,5).map((t,i,{length}) => x => transpose(t,x).late(i/length))
|
...[-12,7,10,12,24].slice(0,5).map((t,i,{length}) => x => transpose(t,x).late(i/length))
|
||||||
)
|
)
|
||||||
|
|
@ -363,7 +341,7 @@ export const confusedPhoneDynamic = `stack('[g2 ~@1.3] [c3 ~@1.3]'.mini.slow(2))
|
||||||
.scaleTranspose(slowcat(0,1,2,1).slow(2))
|
.scaleTranspose(slowcat(0,1,2,1).slow(2))
|
||||||
.synth('triangle').gain(0.5).filter(1500)`;
|
.synth('triangle').gain(0.5).filter(1500)`;
|
||||||
|
|
||||||
export const confusedPhone = `'[g2 ~@1.3] [c3 ~@1.3]'.mini
|
export const confusedPhone = `"[g2 ~@1.3] [c3 ~@1.3]"
|
||||||
.superimpose(
|
.superimpose(
|
||||||
transpose(-12).late(0),
|
transpose(-12).late(0),
|
||||||
transpose(7).late(0.1),
|
transpose(7).late(0.1),
|
||||||
|
|
@ -382,14 +360,14 @@ export const zeldasRescue = `stack(
|
||||||
[B3@2 D4] [A3@2 [G3 A3]] [B3@2 D4] [A3]
|
[B3@2 D4] [A3@2 [G3 A3]] [B3@2 D4] [A3]
|
||||||
[B3@2 D4] [A4@2 G4] D5@2
|
[B3@2 D4] [A4@2 G4] D5@2
|
||||||
[D5@2 [C5 B4]] [[C5 B4] G4@2] [C5@2 [B4 A4]] [[B4 A4] E4@2]
|
[D5@2 [C5 B4]] [[C5 B4] G4@2] [C5@2 [B4 A4]] [[B4 A4] E4@2]
|
||||||
[D5@2 [C5 B4]] [[C5 B4] G4 C5] [G5] [~ ~ B3]\`.mini,
|
[D5@2 [C5 B4]] [[C5 B4] G4 C5] [G5] [~ ~ B3]\`,
|
||||||
// bass
|
// bass
|
||||||
\`[[C2 G2] E3@2] [[C2 G2] F#3@2] [[C2 G2] E3@2] [[C2 G2] F#3@2]
|
\`[[C2 G2] E3@2] [[C2 G2] F#3@2] [[C2 G2] E3@2] [[C2 G2] F#3@2]
|
||||||
[[B1 D3] G3@2] [[Bb1 Db3] G3@2] [[A1 C3] G3@2] [[D2 C3] F#3@2]
|
[[B1 D3] G3@2] [[Bb1 Db3] G3@2] [[A1 C3] G3@2] [[D2 C3] F#3@2]
|
||||||
[[C2 G2] E3@2] [[C2 G2] F#3@2] [[C2 G2] E3@2] [[C2 G2] F#3@2]
|
[[C2 G2] E3@2] [[C2 G2] F#3@2] [[C2 G2] E3@2] [[C2 G2] F#3@2]
|
||||||
[[B1 D3] G3@2] [[Bb1 Db3] G3@2] [[A1 C3] G3@2] [[D2 C3] F#3@2]
|
[[B1 D3] G3@2] [[Bb1 Db3] G3@2] [[A1 C3] G3@2] [[D2 C3] F#3@2]
|
||||||
[[F2 C3] E3@2] [[E2 B2] D3@2] [[D2 A2] C3@2] [[C2 G2] B2@2]
|
[[F2 C3] E3@2] [[E2 B2] D3@2] [[D2 A2] C3@2] [[C2 G2] B2@2]
|
||||||
[[F2 C3] E3@2] [[E2 B2] D3@2] [[Eb2 Bb2] Db3@2] [[D2 A2] C3 [F3,G2]]\`.mini
|
[[F2 C3] E3@2] [[E2 B2] D3@2] [[Eb2 Bb2] Db3@2] [[D2 A2] C3 [F3,G2]]\`
|
||||||
).transpose(12).slow(48).tone(
|
).transpose(12).slow(48).tone(
|
||||||
new PolySynth().chain(
|
new PolySynth().chain(
|
||||||
new Gain(0.3),
|
new Gain(0.3),
|
||||||
|
|
@ -399,9 +377,9 @@ export const zeldasRescue = `stack(
|
||||||
)`;
|
)`;
|
||||||
|
|
||||||
export const technoDrums = `stack(
|
export const technoDrums = `stack(
|
||||||
'c1*2'.m.tone(new Tone.MembraneSynth().toDestination()),
|
"c1*2".tone(new Tone.MembraneSynth().toDestination()),
|
||||||
'~ x'.m.tone(new Tone.NoiseSynth().toDestination()),
|
"~ x".tone(new Tone.NoiseSynth().toDestination()),
|
||||||
'[~ c4]*2'.m.tone(new Tone.MetalSynth().set({envelope:{decay:0.06,sustain:0}}).chain(new Gain(0.5),Destination))
|
"[~ c4]*2".tone(new Tone.MetalSynth().set({envelope:{decay:0.06,sustain:0}}).chain(new Gain(0.5),Destination))
|
||||||
)`;
|
)`;
|
||||||
|
|
||||||
export const loungerave = `() => {
|
export const loungerave = `() => {
|
||||||
|
|
@ -413,22 +391,22 @@ export const loungerave = `() => {
|
||||||
const keys = new PolySynth().set({ ...osc('sawtooth'), ...adsr(0, .5, .2, .7) }).chain(lowpass(1200), vol(.5), out);
|
const keys = new PolySynth().set({ ...osc('sawtooth'), ...adsr(0, .5, .2, .7) }).chain(lowpass(1200), vol(.5), out);
|
||||||
|
|
||||||
const drums = stack(
|
const drums = stack(
|
||||||
'c1*2'.m.tone(kick).bypass('<0@7 1>/8'.m),
|
"c1*2".tone(kick).bypass("<0@7 1>/8"),
|
||||||
'~ <x!7 [x@3 x]>'.m.tone(snare).bypass('<0@7 1>/4'.m),
|
"~ <x!7 [x@3 x]>".tone(snare).bypass("<0@7 1>/4"),
|
||||||
'[~ c4]*2'.m.tone(hihat)
|
"[~ c4]*2".tone(hihat)
|
||||||
);
|
);
|
||||||
|
|
||||||
const thru = (x) => x.transpose('<0 1>/8'.m).transpose(1);
|
const thru = (x) => x.transpose("<0 1>/8").transpose(1);
|
||||||
const synths = stack(
|
const synths = stack(
|
||||||
'<C2 Bb1 Ab1 [G1 [G2 G1]]>/2'.m.groove('[x [~ x] <[~ [~ x]]!3 [x x]>@2]/2'.m).edit(thru).tone(bass),
|
"<C2 Bb1 Ab1 [G1 [G2 G1]]>/2".struct("[x [~ x] <[~ [~ x]]!3 [x x]>@2]/2").edit(thru).tone(bass),
|
||||||
'<Cm7 Bb7 Fm7 G7b9>/2'.m.groove('~ [x@0.1 ~]'.m).voicings().edit(thru).every(2, early(1/4)).tone(keys).bypass('<0@7 1>/8'.m.early(1/4))
|
"<Cm7 Bb7 Fm7 G7b9>/2".struct("~ [x@0.1 ~]").voicings().edit(thru).every(2, early(1/4)).tone(keys).bypass("<0@7 1>/8".early(1/4))
|
||||||
)
|
)
|
||||||
return stack(
|
return stack(
|
||||||
drums,
|
drums,
|
||||||
synths
|
synths
|
||||||
)
|
)
|
||||||
//.bypass('<0 1>*4'.m)
|
//.bypass("<0 1>*4")
|
||||||
//.early('0.25 0'.m);
|
//.early("0.25 0");
|
||||||
}`;
|
}`;
|
||||||
|
|
||||||
export const caverave = `() => {
|
export const caverave = `() => {
|
||||||
|
|
@ -440,22 +418,22 @@ export const caverave = `() => {
|
||||||
const keys = new PolySynth().set({ ...osc('sawtooth'), ...adsr(0, .5, .2, .7) }).chain(lowpass(1200), vol(.5), out);
|
const keys = new PolySynth().set({ ...osc('sawtooth'), ...adsr(0, .5, .2, .7) }).chain(lowpass(1200), vol(.5), out);
|
||||||
|
|
||||||
const drums = stack(
|
const drums = stack(
|
||||||
'c1*2'.m.tone(kick).bypass('<0@7 1>/8'.m),
|
"c1*2".tone(kick).bypass("<0@7 1>/8"),
|
||||||
'~ <x!7 [x@3 x]>'.m.tone(snare).bypass('<0@7 1>/4'.m),
|
"~ <x!7 [x@3 x]>".tone(snare).bypass("<0@7 1>/4"),
|
||||||
'[~ c4]*2'.m.tone(hihat)
|
"[~ c4]*2".tone(hihat)
|
||||||
);
|
);
|
||||||
|
|
||||||
const thru = (x) => x.transpose('<0 1>/8'.m).transpose(-1);
|
const thru = (x) => x.transpose("<0 1>/8").transpose(-1);
|
||||||
const synths = stack(
|
const synths = stack(
|
||||||
'<eb4 d4 c4 b3>/2'.m.scale(timeCat([3,'C minor'],[1,'C melodic minor']).slow(8)).groove('[~ x]*2'.m)
|
"<eb4 d4 c4 b3>/2".scale(timeCat([3,'C minor'],[1,'C melodic minor']).slow(8)).struct("[~ x]*2")
|
||||||
.edit(
|
.edit(
|
||||||
scaleTranspose(0).early(0),
|
scaleTranspose(0).early(0),
|
||||||
scaleTranspose(2).early(1/8),
|
scaleTranspose(2).early(1/8),
|
||||||
scaleTranspose(7).early(1/4),
|
scaleTranspose(7).early(1/4),
|
||||||
scaleTranspose(8).early(3/8)
|
scaleTranspose(8).early(3/8)
|
||||||
).edit(thru).tone(keys).bypass('<1 0>/16'.m),
|
).edit(thru).tone(keys).bypass("<1 0>/16"),
|
||||||
'<C2 Bb1 Ab1 [G1 [G2 G1]]>/2'.m.groove('[x [~ x] <[~ [~ x]]!3 [x x]>@2]/2'.m.fast(2)).edit(thru).tone(bass),
|
"<C2 Bb1 Ab1 [G1 [G2 G1]]>/2".struct("[x [~ x] <[~ [~ x]]!3 [x x]>@2]/2".fast(2)).edit(thru).tone(bass),
|
||||||
'<Cm7 Bb7 Fm7 G7b13>/2'.m.groove('~ [x@0.1 ~]'.m.fast(2)).voicings().edit(thru).every(2, early(1/8)).tone(keys).bypass('<0@7 1>/8'.m.early(1/4))
|
"<Cm7 Bb7 Fm7 G7b13>/2".struct("~ [x@0.1 ~]".fast(2)).voicings().edit(thru).every(2, early(1/8)).tone(keys).bypass("<0@7 1>/8".early(1/4))
|
||||||
)
|
)
|
||||||
return stack(
|
return stack(
|
||||||
drums.fast(2),
|
drums.fast(2),
|
||||||
|
|
@ -463,65 +441,19 @@ export const caverave = `() => {
|
||||||
).slow(2);
|
).slow(2);
|
||||||
}`;
|
}`;
|
||||||
|
|
||||||
export const caveravefuture = `() => {
|
export const callcenterhero = `()=>{
|
||||||
const delay = new FeedbackDelay(1/8, .4).chain(vol(0.5), out);
|
const bpm = 90;
|
||||||
const kick = new MembraneSynth().chain(vol(.8), out);
|
const lead = polysynth().set({...osc('sine4'),...adsr(.004)}).chain(vol(0.15),out)
|
||||||
const snare = new NoiseSynth().chain(vol(.8), out);
|
const bass = fmsynth({...osc('sawtooth6'),...adsr(0.05,.6,0.8,0.1)}).chain(vol(0.6), out);
|
||||||
const hihat = new MetalSynth().set(adsr(0, .08, 0, .1)).chain(vol(.3).connect(delay),out);
|
const s = scale(slowcat('F3 minor', 'Ab3 major', 'Bb3 dorian', 'C4 phrygian dominant').slow(4));
|
||||||
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(
|
return stack(
|
||||||
drums.fast(2),
|
"0 2".struct("<x ~> [x ~]").edit(s).scaleTranspose(stack(0,2)).tone(lead),
|
||||||
synths
|
"<6 7 9 7>".struct("[~ [x ~]*2]*2").edit(s).scaleTranspose("[0,2] [2,4]".fast(2).every(4,rev)).tone(lead),
|
||||||
).slow(2);
|
"-14".struct("[~ x@0.8]*2".early(0.01)).edit(s).tone(bass),
|
||||||
}`;
|
"c2*2".tone(membrane().chain(vol(0.6), out)),
|
||||||
|
"~ c2".tone(noise().chain(vol(0.2), out)),
|
||||||
export const caveravefuture2 = `const delay = new FeedbackDelay(1/8, .4).chain(vol(0.5), out);
|
"c4*4".tone(metal(adsr(0,.05,0)).chain(vol(0.03), out))
|
||||||
const kick = new MembraneSynth().chain(vol(.8), out);
|
)
|
||||||
const snare = new NoiseSynth().chain(vol(.8), out);
|
.slow(120 / bpm)
|
||||||
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);
|
|
||||||
`;
|
`;
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import React, { useMemo } from 'react';
|
import React, { useCallback, useMemo, useState } from 'react';
|
||||||
import * as Tone from 'tone';
|
import * as Tone from 'tone';
|
||||||
import useRepl from '../useRepl';
|
import useRepl from '../useRepl';
|
||||||
import CodeMirror from '../CodeMirror';
|
import CodeMirror, { markEvent } from '../CodeMirror';
|
||||||
import cx from '../cx';
|
import cx from '../cx';
|
||||||
|
|
||||||
const defaultSynth = new Tone.PolySynth().chain(new Tone.Gain(0.5), Tone.Destination).set({
|
const defaultSynth = new Tone.PolySynth().chain(new Tone.Gain(0.5), Tone.Destination).set({
|
||||||
|
|
@ -11,42 +11,87 @@ const defaultSynth = new Tone.PolySynth().chain(new Tone.Gain(0.5), Tone.Destina
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
function MiniRepl({ tune, height = 100 }) {
|
function MiniRepl({ tune, maxHeight = 500 }) {
|
||||||
const { code, setCode, activateCode, activeCode, setPattern, error, cycle, dirty, log, togglePlay } = useRepl({
|
const [editor, setEditor] = useState<any>();
|
||||||
|
const { code, setCode, activateCode, activeCode, setPattern, error, cycle, dirty, log, togglePlay, hash } = useRepl({
|
||||||
tune,
|
tune,
|
||||||
defaultSynth,
|
defaultSynth,
|
||||||
autolink: false,
|
autolink: false,
|
||||||
|
onEvent: useCallback(markEvent(editor), [editor]),
|
||||||
});
|
});
|
||||||
|
const lines = code.split('\n').length;
|
||||||
|
const height = Math.min(lines * 30 + 30, maxHeight);
|
||||||
return (
|
return (
|
||||||
<div className="flex space-y-0 overflow-auto" style={{ height }}>
|
<div className="rounded-md overflow-hidden">
|
||||||
<div className="w-16 flex flex-col">
|
<div className="flex justify-between bg-slate-700 border-t border-slate-500">
|
||||||
<button
|
<div className="flex">
|
||||||
className="grow bg-slate-700 border-b border-slate-500 text-white hover:bg-slate-600 "
|
<button
|
||||||
onClick={() => togglePlay()}
|
className={cx(
|
||||||
>
|
'w-16 flex items-center justify-center p-1 bg-slate-700 border-r border-slate-500 text-white hover:bg-slate-600',
|
||||||
{cycle.started ? 'pause' : 'play'}
|
cycle.started ? 'animate-pulse' : ''
|
||||||
</button>
|
)}
|
||||||
<button
|
onClick={() => togglePlay()}
|
||||||
className={cx(
|
>
|
||||||
'grow border-slate-500 hover:bg-slate-600',
|
{!cycle.started ? (
|
||||||
activeCode && dirty ? 'bg-slate-700 text-white' : 'bg-slate-600 text-slate-400 cursor-not-allowed'
|
<svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
|
||||||
)}
|
<path
|
||||||
onClick={() => activateCode()}
|
fillRule="evenodd"
|
||||||
>
|
d="M10 18a8 8 0 100-16 8 8 0 000 16zM9.555 7.168A1 1 0 008 8v4a1 1 0 001.555.832l3-2a1 1 0 000-1.664l-3-2z"
|
||||||
update
|
clipRule="evenodd"
|
||||||
</button>
|
/>
|
||||||
|
</svg>
|
||||||
|
) : (
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
|
||||||
|
<path
|
||||||
|
fillRule="evenodd"
|
||||||
|
d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zM7 8a1 1 0 012 0v4a1 1 0 11-2 0V8zm5-1a1 1 0 00-1 1v4a1 1 0 102 0V8a1 1 0 00-1-1z"
|
||||||
|
clipRule="evenodd"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className={cx(
|
||||||
|
'w-16 flex items-center justify-center p-1 border-slate-500 hover:bg-slate-600',
|
||||||
|
dirty
|
||||||
|
? 'bg-slate-700 border-r border-slate-500 text-white'
|
||||||
|
: 'bg-slate-600 text-slate-400 cursor-not-allowed'
|
||||||
|
)}
|
||||||
|
onClick={() => activateCode()}
|
||||||
|
>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
|
||||||
|
<path
|
||||||
|
fillRule="evenodd"
|
||||||
|
d="M4 2a1 1 0 011 1v2.101a7.002 7.002 0 0111.601 2.566 1 1 0 11-1.885.666A5.002 5.002 0 005.999 7H9a1 1 0 010 2H4a1 1 0 01-1-1V3a1 1 0 011-1zm.008 9.057a1 1 0 011.276.61A5.002 5.002 0 0014.001 13H11a1 1 0 110-2h5a1 1 0 011 1v5a1 1 0 11-2 0v-2.101a7.002 7.002 0 01-11.601-2.566 1 1 0 01.61-1.276z"
|
||||||
|
clipRule="evenodd"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div className="text-right p-1 text-sm">{error && <span className="text-red-200">{error.message}</span>}</div>{' '}
|
||||||
</div>
|
</div>
|
||||||
<CodeMirror
|
<div className="flex space-y-0 overflow-auto" style={{ height }}>
|
||||||
className="w-full"
|
<CodeMirror
|
||||||
value={code}
|
className="w-full"
|
||||||
options={{
|
value={code}
|
||||||
mode: 'javascript',
|
editorDidMount={setEditor}
|
||||||
theme: 'material',
|
options={{
|
||||||
lineNumbers: true,
|
mode: 'javascript',
|
||||||
}}
|
theme: 'material',
|
||||||
onChange={(_: any, __: any, value: any) => setCode(value)}
|
lineNumbers: true,
|
||||||
/>
|
}}
|
||||||
{/* <textarea className="w-full" value={code} onChange={(e) => setCode(e.target.value)} /> */}
|
onChange={(_: any, __: any, value: any) => setCode(value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{/* <div className="bg-slate-700 border-t border-slate-500 content-right pr-2 text-right">
|
||||||
|
<a href={`https://strudel.tidalcycles.org/#${hash}`} className="text-white items-center inline-flex">
|
||||||
|
<span>open in REPL</span>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
|
||||||
|
<path d="M11 3a1 1 0 100 2h2.586l-6.293 6.293a1 1 0 101.414 1.414L15 6.414V9a1 1 0 102 0V4a1 1 0 00-1-1h-5z" />
|
||||||
|
<path d="M5 5a2 2 0 00-2 2v8a2 2 0 002 2h8a2 2 0 002-2v-3a1 1 0 10-2 0v3H5V7h3a1 1 0 000-2H5z" />
|
||||||
|
</svg>
|
||||||
|
</a>
|
||||||
|
</div> */}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,29 +25,28 @@ To get a taste of what Strudel can do, check out this track:
|
||||||
const keys = new PolySynth().set({ ...osc('sawtooth'), ...adsr(0, .5, .2, .7) }).chain(lowpass(1200), vol(.5), out);
|
const keys = new PolySynth().set({ ...osc('sawtooth'), ...adsr(0, .5, .2, .7) }).chain(lowpass(1200), vol(.5), out);
|
||||||
|
|
||||||
const drums = stack(
|
const drums = stack(
|
||||||
'c1*2'.m.tone(kick).bypass('<0@7 1>/8'.m),
|
"c1*2".tone(kick).bypass("<0@7 1>/8"),
|
||||||
'~ <x!7 [x@3 x]>'.m.tone(snare).bypass('<0@7 1>/4'.m),
|
"~ <x!7 [x@3 x]>".tone(snare).bypass("<0@7 1>/4"),
|
||||||
'[~ c4]*2'.m.tone(hihat)
|
"[~ c4]*2".tone(hihat)
|
||||||
);
|
);
|
||||||
|
|
||||||
const thru = (x) => x.transpose('<0 1>/8'.m).transpose(-1);
|
const thru = (x) => x.transpose("<0 1>/8").transpose(-1);
|
||||||
const synths = stack(
|
const synths = stack(
|
||||||
'<eb4 d4 c4 b3>/2'.m.scale(timeCat([3,'C minor'],[1,'C melodic minor']).slow(8)).groove('[~ x]*2'.m)
|
"<eb4 d4 c4 b3>/2".scale(timeCat([3,'C minor'],[1,'C melodic minor']).slow(8)).struct("[~ x]*2")
|
||||||
.edit(
|
.edit(
|
||||||
scaleTranspose(0).early(0),
|
scaleTranspose(0).early(0),
|
||||||
scaleTranspose(2).early(1/8),
|
scaleTranspose(2).early(1/8),
|
||||||
scaleTranspose(7).early(1/4),
|
scaleTranspose(7).early(1/4),
|
||||||
scaleTranspose(8).early(3/8)
|
scaleTranspose(8).early(3/8)
|
||||||
).edit(thru).tone(keys).bypass('<1 0>/16'.m),
|
).edit(thru).tone(keys).bypass("<1 0>/16"),
|
||||||
'<C2 Bb1 Ab1 [G1 [G2 G1]]>/2'.m.groove('[x [~ x] <[~ [~ x]]!3 [x x]>@2]/2'.m.fast(2)).edit(thru).tone(bass),
|
"<C2 Bb1 Ab1 [G1 [G2 G1]]>/2".struct("[x [~ x] <[~ [~ x]]!3 [x x]>@2]/2".fast(2)).edit(thru).tone(bass),
|
||||||
'<Cm7 Bb7 Fm7 G7b13>/2'.m.groove('~ [x@0.1 ~]'.m.fast(2)).voicings().edit(thru).every(2, early(1/8)).tone(keys).bypass('<0@7 1>/8'.m.early(1/4))
|
"<Cm7 Bb7 Fm7 G7b13>/2".struct("~ [x@0.1 ~]".fast(2)).voicings().edit(thru).every(2, early(1/8)).tone(keys).bypass("<0@7 1>/8".early(1/4))
|
||||||
)
|
)
|
||||||
return stack(
|
return stack(
|
||||||
drums.fast(2),
|
drums.fast(2),
|
||||||
synths
|
synths
|
||||||
).slow(2);
|
).slow(2);
|
||||||
}`}
|
}`}
|
||||||
height={400}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
[Open this track in the REPL](https://strudel.tidalcycles.org/#KCkgPT4gewogIGNvbnN0IGRlbGF5ID0gbmV3IEZlZWRiYWNrRGVsYXkoMS84LCAuNCkuY2hhaW4odm9sKDAuNSksIG91dCk7CiAgY29uc3Qga2ljayA9IG5ldyBNZW1icmFuZVN5bnRoKCkuY2hhaW4odm9sKC44KSwgb3V0KTsKICBjb25zdCBzbmFyZSA9IG5ldyBOb2lzZVN5bnRoKCkuY2hhaW4odm9sKC44KSwgb3V0KTsKICBjb25zdCBoaWhhdCA9IG5ldyBNZXRhbFN5bnRoKCkuc2V0KGFkc3IoMCwgLjA4LCAwLCAuMSkpLmNoYWluKHZvbCguMykuY29ubmVjdChkZWxheSksb3V0KTsKICBjb25zdCBiYXNzID0gbmV3IFN5bnRoKCkuc2V0KHsgLi4ub3NjKCdzYXd0b290aCcpLCAuLi5hZHNyKDAsIC4xLCAuNCkgfSkuY2hhaW4obG93cGFzcyg5MDApLCB2b2woLjUpLCBvdXQpOwogIGNvbnN0IGtleXMgPSBuZXcgUG9seVN5bnRoKCkuc2V0KHsgLi4ub3NjKCdzYXd0b290aCcpLCAuLi5hZHNyKDAsIC41LCAuMiwgLjcpIH0pLmNoYWluKGxvd3Bhc3MoMTIwMCksIHZvbCguNSksIG91dCk7CiAgCiAgY29uc3QgZHJ1bXMgPSBzdGFjaygKICAgICdjMSoyJy5tLnRvbmUoa2ljaykuYnlwYXNzKCc8MEA3IDE%2BLzgnLm0pLAogICAgJ34gPHghNyBbeEAzIHhdPicubS50b25lKHNuYXJlKS5ieXBhc3MoJzwwQDcgMT4vNCcubSksCiAgICAnW34gYzRdKjInLm0udG9uZShoaWhhdCkKICApOwogIAogIGNvbnN0IHRocnUgPSAoeCkgPT4geC50cmFuc3Bvc2UoJzwwIDE%2BLzgnLm0pLnRyYW5zcG9zZSgtMSk7CiAgY29uc3Qgc3ludGhzID0gc3RhY2soCiAgICAnPGViNCBkNCBjNCBiMz4vMicubS5zY2FsZSh0aW1lQ2F0KFszLCdDIG1pbm9yJ10sWzEsJ0MgbWVsb2RpYyBtaW5vciddKS5zbG93KDgpKS5ncm9vdmUoJ1t%2BIHhdKjInLm0pCiAgICAuZWRpdCgKICAgICAgc2NhbGVUcmFuc3Bvc2UoMCkuZWFybHkoMCksCiAgICAgIHNjYWxlVHJhbnNwb3NlKDIpLmVhcmx5KDEvOCksCiAgICAgIHNjYWxlVHJhbnNwb3NlKDcpLmVhcmx5KDEvNCksCiAgICAgIHNjYWxlVHJhbnNwb3NlKDgpLmVhcmx5KDMvOCkKICAgICkuZWRpdCh0aHJ1KS50b25lKGtleXMpLmJ5cGFzcygnPDEgMD4vMTYnLm0pLAogICAgJzxDMiBCYjEgQWIxIFtHMSBbRzIgRzFdXT4vMicubS5ncm9vdmUoJ1t4IFt%2BIHhdIDxbfiBbfiB4XV0hMyBbeCB4XT5AMl0vMicubS5mYXN0KDIpKS5lZGl0KHRocnUpLnRvbmUoYmFzcyksCiAgICAnPENtNyBCYjcgRm03IEc3YjEzPi8yJy5tLmdyb292ZSgnfiBbeEAwLjEgfl0nLm0uZmFzdCgyKSkudm9pY2luZ3MoKS5lZGl0KHRocnUpLmV2ZXJ5KDIsIGVhcmx5KDEvOCkpLnRvbmUoa2V5cykuYnlwYXNzKCc8MEA3IDE%2BLzgnLm0uZWFybHkoMS80KSkKICApCiAgcmV0dXJuIHN0YWNrKAogICAgZHJ1bXMuZmFzdCgyKSwgCiAgICBzeW50aHMKICApLnNsb3coMik7Cn0%3D)
|
[Open this track in the REPL](https://strudel.tidalcycles.org/#KCkgPT4gewogIGNvbnN0IGRlbGF5ID0gbmV3IEZlZWRiYWNrRGVsYXkoMS84LCAuNCkuY2hhaW4odm9sKDAuNSksIG91dCk7CiAgY29uc3Qga2ljayA9IG5ldyBNZW1icmFuZVN5bnRoKCkuY2hhaW4odm9sKC44KSwgb3V0KTsKICBjb25zdCBzbmFyZSA9IG5ldyBOb2lzZVN5bnRoKCkuY2hhaW4odm9sKC44KSwgb3V0KTsKICBjb25zdCBoaWhhdCA9IG5ldyBNZXRhbFN5bnRoKCkuc2V0KGFkc3IoMCwgLjA4LCAwLCAuMSkpLmNoYWluKHZvbCguMykuY29ubmVjdChkZWxheSksb3V0KTsKICBjb25zdCBiYXNzID0gbmV3IFN5bnRoKCkuc2V0KHsgLi4ub3NjKCdzYXd0b290aCcpLCAuLi5hZHNyKDAsIC4xLCAuNCkgfSkuY2hhaW4obG93cGFzcyg5MDApLCB2b2woLjUpLCBvdXQpOwogIGNvbnN0IGtleXMgPSBuZXcgUG9seVN5bnRoKCkuc2V0KHsgLi4ub3NjKCdzYXd0b290aCcpLCAuLi5hZHNyKDAsIC41LCAuMiwgLjcpIH0pLmNoYWluKGxvd3Bhc3MoMTIwMCksIHZvbCguNSksIG91dCk7CiAgCiAgY29uc3QgZHJ1bXMgPSBzdGFjaygKICAgICdjMSoyJy5tLnRvbmUoa2ljaykuYnlwYXNzKCc8MEA3IDE%2BLzgnLm0pLAogICAgJ34gPHghNyBbeEAzIHhdPicubS50b25lKHNuYXJlKS5ieXBhc3MoJzwwQDcgMT4vNCcubSksCiAgICAnW34gYzRdKjInLm0udG9uZShoaWhhdCkKICApOwogIAogIGNvbnN0IHRocnUgPSAoeCkgPT4geC50cmFuc3Bvc2UoJzwwIDE%2BLzgnLm0pLnRyYW5zcG9zZSgtMSk7CiAgY29uc3Qgc3ludGhzID0gc3RhY2soCiAgICAnPGViNCBkNCBjNCBiMz4vMicubS5zY2FsZSh0aW1lQ2F0KFszLCdDIG1pbm9yJ10sWzEsJ0MgbWVsb2RpYyBtaW5vciddKS5zbG93KDgpKS5ncm9vdmUoJ1t%2BIHhdKjInLm0pCiAgICAuZWRpdCgKICAgICAgc2NhbGVUcmFuc3Bvc2UoMCkuZWFybHkoMCksCiAgICAgIHNjYWxlVHJhbnNwb3NlKDIpLmVhcmx5KDEvOCksCiAgICAgIHNjYWxlVHJhbnNwb3NlKDcpLmVhcmx5KDEvNCksCiAgICAgIHNjYWxlVHJhbnNwb3NlKDgpLmVhcmx5KDMvOCkKICAgICkuZWRpdCh0aHJ1KS50b25lKGtleXMpLmJ5cGFzcygnPDEgMD4vMTYnLm0pLAogICAgJzxDMiBCYjEgQWIxIFtHMSBbRzIgRzFdXT4vMicubS5ncm9vdmUoJ1t4IFt%2BIHhdIDxbfiBbfiB4XV0hMyBbeCB4XT5AMl0vMicubS5mYXN0KDIpKS5lZGl0KHRocnUpLnRvbmUoYmFzcyksCiAgICAnPENtNyBCYjcgRm03IEc3YjEzPi8yJy5tLmdyb292ZSgnfiBbeEAwLjEgfl0nLm0uZmFzdCgyKSkudm9pY2luZ3MoKS5lZGl0KHRocnUpLmV2ZXJ5KDIsIGVhcmx5KDEvOCkpLnRvbmUoa2V5cykuYnlwYXNzKCc8MEA3IDE%2BLzgnLm0uZWFybHkoMS80KSkKICApCiAgcmV0dXJuIHN0YWNrKAogICAgZHJ1bXMuZmFzdCgyKSwgCiAgICBzeW50aHMKICApLnNsb3coMik7Cn0%3D)
|
||||||
|
|
@ -86,7 +85,6 @@ Before diving deeper into the details, here is a flavor of how the mini language
|
||||||
[[a1 a2]*4]
|
[[a1 a2]*4]
|
||||||
]
|
]
|
||||||
]/16\``}
|
]/16\``}
|
||||||
height={600}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
The snippet above is enclosed in backticks (`), which allows you to write multi-line strings.
|
The snippet above is enclosed in backticks (`), which allows you to write multi-line strings.
|
||||||
|
|
@ -263,7 +261,6 @@ You can nest functions inside one another:
|
||||||
stack(b3,d3,fs4),
|
stack(b3,d3,fs4),
|
||||||
stack(b3,e4,g4)
|
stack(b3,e4,g4)
|
||||||
)`}
|
)`}
|
||||||
height={200}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
The above is equivalent to
|
The above is equivalent to
|
||||||
|
|
@ -324,7 +321,7 @@ Note that we have to wrap b4 in **pure** to be able to call a the pattern modifi
|
||||||
|
|
||||||
There is the shorthand **p** for this:
|
There is the shorthand **p** for this:
|
||||||
|
|
||||||
<MiniRepl tune={`cat(e5, b4.p.late(0.5))`} />
|
<MiniRepl tune={`cat(e5, b4.late(0.5))`} />
|
||||||
|
|
||||||
### late(cycles)
|
### late(cycles)
|
||||||
|
|
||||||
|
|
@ -344,11 +341,11 @@ Will reverse the pattern:
|
||||||
|
|
||||||
Will apply the given function every n cycles:
|
Will apply the given function every n cycles:
|
||||||
|
|
||||||
<MiniRepl tune={`cat(e5, b4.p.every(4, late(0.5)))`} />
|
<MiniRepl tune={`cat(e5, pure(b4).every(4, late(0.5)))`} />
|
||||||
|
|
||||||
Note that late is called directly. This is a shortcut for:
|
Note that late is called directly. This is a shortcut for:
|
||||||
|
|
||||||
<MiniRepl tune={`cat(e5, b4.p.every(4, x => x.late(0.5)))`} />
|
<MiniRepl tune={`cat(e5, pure(b4).every(4, x => x.late(0.5)))`} />
|
||||||
|
|
||||||
TODO: should the function really run the first cycle?
|
TODO: should the function really run the first cycle?
|
||||||
|
|
||||||
|
|
@ -367,7 +364,7 @@ TODO: should the function really run the first cycle?
|
||||||
- append
|
- append
|
||||||
- superimpose
|
- superimpose
|
||||||
- internal Pattern functions?
|
- internal Pattern functions?
|
||||||
- groove, TODO move to core from https://github.com/tidalcycles/strudel/blob/main/repl/src/groove.ts
|
- struct
|
||||||
|
|
||||||
## Tone API
|
## Tone API
|
||||||
|
|
||||||
|
|
@ -377,15 +374,14 @@ To make the sounds more interesting, we can use Tone.js instruments ands effects
|
||||||
|
|
||||||
<MiniRepl
|
<MiniRepl
|
||||||
tune={`stack(
|
tune={`stack(
|
||||||
"[c5 c5 bb4 c5] [~ g4 ~ g4] [c5 f5 e5 c5] ~".m
|
"[c5 c5 bb4 c5] [~ g4 ~ g4] [c5 f5 e5 c5] ~"
|
||||||
.tone(synth(adsr(0,.1,0,0)).chain(out)),
|
.tone(synth(adsr(0,.1,0,0)).chain(out)),
|
||||||
"[c2 c3]*8".m
|
"[c2 c3]*8"
|
||||||
.tone(synth({
|
.tone(synth({
|
||||||
...osc('sawtooth'),
|
...osc('sawtooth'),
|
||||||
...adsr(0,.1,0.4,0)
|
...adsr(0,.1,0.4,0)
|
||||||
}).chain(lowpass(300), out))
|
}).chain(lowpass(300), out))
|
||||||
).slow(4)`}
|
).slow(4)`}
|
||||||
height={300}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
### tone(instrument)
|
### tone(instrument)
|
||||||
|
|
@ -393,7 +389,7 @@ To make the sounds more interesting, we can use Tone.js instruments ands effects
|
||||||
To change the instrument of a pattern, you can pass any [Tone.js Source](https://tonejs.github.io/docs/14.7.77/index.html) to .tone:
|
To change the instrument of a pattern, you can pass any [Tone.js Source](https://tonejs.github.io/docs/14.7.77/index.html) to .tone:
|
||||||
|
|
||||||
<MiniRepl
|
<MiniRepl
|
||||||
tune={`"[c4 c4 bb3 c4] [~ g3 ~ g3] [c4 f4 e4 c4] ~".m.slow(4)
|
tune={`"[c4 c4 bb3 c4] [~ g3 ~ g3] [c4 f4 e4 c4] ~".slow(4)
|
||||||
.tone(new FMSynth().toDestination())`}
|
.tone(new FMSynth().toDestination())`}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|
@ -418,7 +414,7 @@ const synth = (options) => new Synth(options);
|
||||||
Shortcut for Tone.Destination. Intended to be used with Tone's .chain:
|
Shortcut for Tone.Destination. Intended to be used with Tone's .chain:
|
||||||
|
|
||||||
<MiniRepl
|
<MiniRepl
|
||||||
tune={`"[c4 c4 bb3 c4] [~ g3 ~ g3] [c4 f4 e4 c4] ~".m.slow(4)
|
tune={`"[c4 c4 bb3 c4] [~ g3 ~ g3] [c4 f4 e4 c4] ~".slow(4)
|
||||||
.tone(membrane().chain(out))`}
|
.tone(membrane().chain(out))`}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|
@ -429,7 +425,7 @@ This alone is not really useful, so read on..
|
||||||
Helper that returns a Gain Node with the given volume. Intended to be used with Tone's .chain:
|
Helper that returns a Gain Node with the given volume. Intended to be used with Tone's .chain:
|
||||||
|
|
||||||
<MiniRepl
|
<MiniRepl
|
||||||
tune={`"[c4 c4 bb3 c4] [~ g3 ~ g3] [c4 f4 e4 c4] ~".m.slow(4)
|
tune={`"[c4 c4 bb3 c4] [~ g3 ~ g3] [c4 f4 e4 c4] ~".slow(4)
|
||||||
.tone(noise().chain(vol(0.5), out))`}
|
.tone(noise().chain(vol(0.5), out))`}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|
@ -438,7 +434,7 @@ Helper that returns a Gain Node with the given volume. Intended to be used with
|
||||||
Helper to set the waveform of a synth, monosynth or polysynth:
|
Helper to set the waveform of a synth, monosynth or polysynth:
|
||||||
|
|
||||||
<MiniRepl
|
<MiniRepl
|
||||||
tune={`"[c4 c4 bb3 c4] [~ g3 ~ g3] [c4 f4 e4 c4] ~".m.slow(4)
|
tune={`"[c4 c4 bb3 c4] [~ g3 ~ g3] [c4 f4 e4 c4] ~".slow(4)
|
||||||
.tone(synth(osc('sawtooth4')).chain(out))`}
|
.tone(synth(osc('sawtooth4')).chain(out))`}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|
@ -449,7 +445,7 @@ The base types are `sine`, `square`, `sawtooth`, `triangle`. You can also append
|
||||||
Helper that returns a Filter Node of type lowpass with the given cutoff. Intended to be used with Tone's .chain:
|
Helper that returns a Filter Node of type lowpass with the given cutoff. Intended to be used with Tone's .chain:
|
||||||
|
|
||||||
<MiniRepl
|
<MiniRepl
|
||||||
tune={`"[c4 c4 bb3 c4] [~ g3 ~ g3] [c4 f4 e4 c4] ~".m.slow(4)
|
tune={`"[c4 c4 bb3 c4] [~ g3 ~ g3] [c4 f4 e4 c4] ~".slow(4)
|
||||||
.tone(synth(osc('sawtooth')).chain(lowpass(800), out))`}
|
.tone(synth(osc('sawtooth')).chain(lowpass(800), out))`}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|
@ -458,7 +454,7 @@ Helper that returns a Filter Node of type lowpass with the given cutoff. Intende
|
||||||
Helper that returns a Filter Node of type highpass with the given cutoff. Intended to be used with Tone's .chain:
|
Helper that returns a Filter Node of type highpass with the given cutoff. Intended to be used with Tone's .chain:
|
||||||
|
|
||||||
<MiniRepl
|
<MiniRepl
|
||||||
tune={`"[c4 c4 bb3 c4] [~ g3 ~ g3] [c4 f4 e4 c4] ~".m.slow(4)
|
tune={`"[c4 c4 bb3 c4] [~ g3 ~ g3] [c4 f4 e4 c4] ~".slow(4)
|
||||||
.tone(synth(osc('sawtooth')).chain(highpass(2000), out))`}
|
.tone(synth(osc('sawtooth')).chain(highpass(2000), out))`}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|
@ -467,7 +463,7 @@ Helper that returns a Filter Node of type highpass with the given cutoff. Intend
|
||||||
Helper to set the envelope of a Tone.js instrument. Intended to be used with Tone's .set:
|
Helper to set the envelope of a Tone.js instrument. Intended to be used with Tone's .set:
|
||||||
|
|
||||||
<MiniRepl
|
<MiniRepl
|
||||||
tune={`"[c4 c4 bb3 c4] [~ g3 ~ g3] [c4 f4 e4 c4] ~".m.slow(4)
|
tune={`"[c4 c4 bb3 c4] [~ g3 ~ g3] [c4 f4 e4 c4] ~".slow(4)
|
||||||
.tone(synth(adsr(0,.1,0,0)).chain(out))`}
|
.tone(synth(adsr(0,.1,0,0)).chain(out))`}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|
@ -482,8 +478,8 @@ It would be great to get this to work without glitches though, because it is fun
|
||||||
With .synth, you can create a synth with a variable wave type:
|
With .synth, you can create a synth with a variable wave type:
|
||||||
|
|
||||||
<MiniRepl
|
<MiniRepl
|
||||||
tune={`"[c4 c4 bb3 c4] [~ g3 ~ g3] [c4 f4 e4 c4] ~".m.
|
tune={`"[c4 c4 bb3 c4] [~ g3 ~ g3] [c4 f4 e4 c4] ~"
|
||||||
synth('<sawtooth8 square8>'.m).slow(4)`}
|
.synth("<sawtooth8 square8>").slow(4)`}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
#### adsr(attack, decay?, sustain?, release?)
|
#### adsr(attack, decay?, sustain?, release?)
|
||||||
|
|
@ -491,8 +487,8 @@ synth('<sawtooth8 square8>'.m).slow(4)`}
|
||||||
Chainable Envelope helper:
|
Chainable Envelope helper:
|
||||||
|
|
||||||
<MiniRepl
|
<MiniRepl
|
||||||
tune={`"[c5 c5 bb4 c5] [~ g4 ~ g4] [c5 f5 e5 c5] ~".m.slow(4).
|
tune={`"[c5 c5 bb4 c5] [~ g4 ~ g4] [c5 f5 e5 c5] ~".slow(4)
|
||||||
synth('sawtooth16').adsr(0,.1,0,0)`}
|
.synth('sawtooth16').adsr(0,.1,0,0)`}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
Due to having more than one argument, this method is not patternified.
|
Due to having more than one argument, this method is not patternified.
|
||||||
|
|
@ -502,8 +498,8 @@ Due to having more than one argument, this method is not patternified.
|
||||||
Patternified filter:
|
Patternified filter:
|
||||||
|
|
||||||
<MiniRepl
|
<MiniRepl
|
||||||
tune={`"[c4 c4 bb3 c4] [~ g3 ~ g3] [c4 f4 e4 c4] ~".m.
|
tune={`"[c4 c4 bb3 c4] [~ g3 ~ g3] [c4 f4 e4 c4] ~"
|
||||||
synth('sawtooth16').filter('[500 2000]*8'.m).slow(4)`}
|
.synth('sawtooth16').filter("[500 2000]*8").slow(4)`}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
#### gain(value)
|
#### gain(value)
|
||||||
|
|
@ -511,8 +507,8 @@ synth('sawtooth16').filter('[500 2000]*8'.m).slow(4)`}
|
||||||
Patternified gain:
|
Patternified gain:
|
||||||
|
|
||||||
<MiniRepl
|
<MiniRepl
|
||||||
tune={`"[c4 c4 bb3 c4] [~ g3 ~ g3] [c4 f4 e4 c4] ~".m.
|
tune={`"[c4 c4 bb3 c4] [~ g3 ~ g3] [c4 f4 e4 c4] ~"
|
||||||
synth('sawtooth16').gain('[.2 .8]*8'.m).slow(4)`}
|
.synth('sawtooth16').gain("[.2 .8]*8").slow(4)`}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
#### autofilter(value)
|
#### autofilter(value)
|
||||||
|
|
@ -520,8 +516,8 @@ synth('sawtooth16').gain('[.2 .8]*8'.m).slow(4)`}
|
||||||
Patternified autofilter:
|
Patternified autofilter:
|
||||||
|
|
||||||
<MiniRepl
|
<MiniRepl
|
||||||
tune={`"c2 c3".m.
|
tune={`"c2 c3"
|
||||||
synth('sawtooth16').autofilter('<1 4 8>'.m)`}
|
.synth('sawtooth16').autofilter("<1 4 8>"")`}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
## Tonal API
|
## Tonal API
|
||||||
|
|
@ -532,20 +528,20 @@ The Tonal API, uses [tonaljs](https://github.com/tonaljs/tonal) to provide helpe
|
||||||
|
|
||||||
Transposes all notes to the given number of semitones:
|
Transposes all notes to the given number of semitones:
|
||||||
|
|
||||||
<MiniRepl tune={`"c2 c3".m.fast(2).transpose('<0 -2 5 3>'.m.slow(2)).transpose(0)`} />
|
<MiniRepl tune={`"c2 c3".fast(2).transpose("<0 -2 5 3>".slow(2)).transpose(0)`} />
|
||||||
|
|
||||||
This method gets really exciting when we use it with a pattern as above.
|
This method gets really exciting when we use it with a pattern as above.
|
||||||
|
|
||||||
Instead of numbers, scientific interval notation can be used as well:
|
Instead of numbers, scientific interval notation can be used as well:
|
||||||
|
|
||||||
<MiniRepl tune={`"c2 c3".m.fast(2).transpose('<1P -2M 4P 3m>'.m.slow(2)).transpose(1)`} />
|
<MiniRepl tune={`"c2 c3".fast(2).transpose("<1P -2M 4P 3m>".slow(2)).transpose(1)`} />
|
||||||
|
|
||||||
### scale(name)
|
### scale(name)
|
||||||
|
|
||||||
Turns numbers into notes in the scale (zero indexed). Also sets scale for other scale operations, like scaleTranpose.
|
Turns numbers into notes in the scale (zero indexed). Also sets scale for other scale operations, like scaleTranpose.
|
||||||
|
|
||||||
<MiniRepl
|
<MiniRepl
|
||||||
tune={`"0 2 4 6 4 2".m
|
tune={`"0 2 4 6 4 2"
|
||||||
.scale(slowcat('C2 major', 'C2 minor').slow(2))`}
|
.scale(slowcat('C2 major', 'C2 minor').slow(2))`}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|
@ -558,16 +554,16 @@ All the available scale names can be found [here](https://github.com/tonaljs/ton
|
||||||
Transposes notes inside the scale by the number of steps:
|
Transposes notes inside the scale by the number of steps:
|
||||||
|
|
||||||
<MiniRepl
|
<MiniRepl
|
||||||
tune={`"-8 [2,4,6]".m
|
tune={`"-8 [2,4,6]"
|
||||||
.scale('C4 bebop major')
|
.scale('C4 bebop major')
|
||||||
.scaleTranspose('<0 -1 -2 -3 -4 -5 -6 -4>'.m)`}
|
.scaleTranspose("<0 -1 -2 -3 -4 -5 -6 -4>")`}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
### voicings(range?)
|
### voicings(range?)
|
||||||
|
|
||||||
Turns chord symbols into voicings, using the smoothest voice leading possible:
|
Turns chord symbols into voicings, using the smoothest voice leading possible:
|
||||||
|
|
||||||
<MiniRepl tune={`stack("<C^7 A7 Dm7 G7>".m.voicings(), '<C3 A2 D3 G2>'.m)`} />
|
<MiniRepl tune={`stack("<C^7 A7 Dm7 G7>".voicings(), "<C3 A2 D3 G2>")`} />
|
||||||
|
|
||||||
TODO: use voicing collection as first param + patternify.
|
TODO: use voicing collection as first param + patternify.
|
||||||
|
|
||||||
|
|
@ -575,16 +571,15 @@ TODO: use voicing collection as first param + patternify.
|
||||||
|
|
||||||
Turns chord symbols into root notes of chords in given octave.
|
Turns chord symbols into root notes of chords in given octave.
|
||||||
|
|
||||||
<MiniRepl tune={`"<C^7 A7b13 Dm7 G7>".m.rootNotes(3)`} />
|
<MiniRepl tune={`"<C^7 A7b13 Dm7 G7>".rootNotes(3)`} />
|
||||||
|
|
||||||
Together with edit, groove and voicings, this can be used to create a basic backing track:
|
Together with edit, struct and voicings, this can be used to create a basic backing track:
|
||||||
|
|
||||||
<MiniRepl
|
<MiniRepl
|
||||||
tune={`"<C^7 A7b13 Dm7 G7>".m.edit(
|
tune={`"<C^7 A7b13 Dm7 G7>".edit(
|
||||||
x => x.voicings(['d3','g4']).groove('~ x'.m),
|
x => x.voicings(['d3','g4']).struct("~ x"),
|
||||||
x => x.rootNotes(2).tone(synth(osc('sawtooth4')).chain(out))
|
x => x.rootNotes(2).tone(synth(osc('sawtooth4')).chain(out))
|
||||||
)`}
|
)`}
|
||||||
height={150}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
TODO: use range instead of octave.
|
TODO: use range instead of octave.
|
||||||
|
|
@ -604,7 +599,7 @@ Midi is currently not supported by the mini repl used here, but you can [open th
|
||||||
In the REPL, you will se a log of the available MIDI devices.
|
In the REPL, you will se a log of the available MIDI devices.
|
||||||
|
|
||||||
<!--<MiniRepl
|
<!--<MiniRepl
|
||||||
tune={`stack("<C^7 A7 Dm7 G7>".m.voicings(), '<C3 A2 D3 G2>'.m)
|
tune={`stack("<C^7 A7 Dm7 G7>".voicings(), "<C3 A2 D3 G2>")
|
||||||
.midi()`}
|
.midi()`}
|
||||||
/>-->
|
/>-->
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@ import type { ToneEventCallback } from 'tone';
|
||||||
import * as Tone from 'tone';
|
import * as Tone from 'tone';
|
||||||
import { TimeSpan } from '../../strudel.mjs';
|
import { TimeSpan } from '../../strudel.mjs';
|
||||||
import type { Hap } from './types';
|
import type { Hap } from './types';
|
||||||
import usePostMessage from './usePostMessage';
|
|
||||||
|
|
||||||
export declare interface UseCycleProps {
|
export declare interface UseCycleProps {
|
||||||
onEvent: ToneEventCallback<any>;
|
onEvent: ToneEventCallback<any>;
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
import { useCallback, useLayoutEffect, useState, useMemo, useEffect } from 'react';
|
import { useCallback, useState, useMemo } from 'react';
|
||||||
import { isNote } from 'tone';
|
import { isNote } from 'tone';
|
||||||
import { evaluate } from './evaluate';
|
import { evaluate } from './evaluate';
|
||||||
import { useWebMidi } from './midi';
|
|
||||||
import type { Pattern } from './types';
|
import type { Pattern } from './types';
|
||||||
import useCycle from './useCycle';
|
import useCycle from './useCycle';
|
||||||
import usePostMessage from './usePostMessage';
|
import usePostMessage from './usePostMessage';
|
||||||
|
|
@ -12,14 +11,16 @@ let s4 = () => {
|
||||||
.substring(1);
|
.substring(1);
|
||||||
};
|
};
|
||||||
|
|
||||||
function useRepl({ tune, defaultSynth, autolink = true }) {
|
function useRepl({ tune, defaultSynth, autolink = true, onEvent }: any) {
|
||||||
const id = useMemo(() => s4(), []);
|
const id = useMemo(() => s4(), []);
|
||||||
const [code, setCode] = useState<string>(tune);
|
const [code, setCode] = useState<string>(tune);
|
||||||
const [activeCode, setActiveCode] = useState<string>();
|
const [activeCode, setActiveCode] = useState<string>();
|
||||||
const [log, setLog] = useState('');
|
const [log, setLog] = useState('');
|
||||||
const [error, setError] = useState<Error>();
|
const [error, setError] = useState<Error>();
|
||||||
|
const [hash, setHash] = useState('');
|
||||||
const [pattern, setPattern] = useState<Pattern>();
|
const [pattern, setPattern] = useState<Pattern>();
|
||||||
const dirty = code !== activeCode;
|
const dirty = code !== activeCode || error;
|
||||||
|
const generateHash = () => encodeURIComponent(btoa(code));
|
||||||
const activateCode = (_code = code) => {
|
const activateCode = (_code = code) => {
|
||||||
!cycle.started && cycle.start();
|
!cycle.started && cycle.start();
|
||||||
broadcast({ type: 'start', from: id });
|
broadcast({ type: 'start', from: id });
|
||||||
|
|
@ -33,9 +34,12 @@ function useRepl({ tune, defaultSynth, autolink = true }) {
|
||||||
if (autolink) {
|
if (autolink) {
|
||||||
window.location.hash = '#' + encodeURIComponent(btoa(code));
|
window.location.hash = '#' + encodeURIComponent(btoa(code));
|
||||||
}
|
}
|
||||||
|
setHash(generateHash());
|
||||||
setError(undefined);
|
setError(undefined);
|
||||||
setActiveCode(_code);
|
setActiveCode(_code);
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
|
err.message = 'evaluation error: ' + err.message;
|
||||||
|
console.warn(err);
|
||||||
setError(err);
|
setError(err);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -48,35 +52,40 @@ function useRepl({ tune, defaultSynth, autolink = true }) {
|
||||||
};
|
};
|
||||||
// cycle hook to control scheduling
|
// cycle hook to control scheduling
|
||||||
const cycle = useCycle({
|
const cycle = useCycle({
|
||||||
onEvent: useCallback((time, event) => {
|
onEvent: useCallback(
|
||||||
try {
|
(time, event) => {
|
||||||
if (!event.value?.onTrigger) {
|
try {
|
||||||
const note = event.value?.value || event.value;
|
onEvent?.(event);
|
||||||
if (!isNote(note)) {
|
if (!event.value?.onTrigger) {
|
||||||
throw new Error('not a note: ' + note);
|
const note = event.value?.value || event.value;
|
||||||
}
|
if (!isNote(note)) {
|
||||||
if (defaultSynth) {
|
throw new Error('not a note: ' + note);
|
||||||
defaultSynth.triggerAttackRelease(note, event.duration, time);
|
}
|
||||||
} else {
|
if (defaultSynth) {
|
||||||
throw new Error('no defaultSynth passed to useRepl.');
|
defaultSynth.triggerAttackRelease(note, event.duration, time);
|
||||||
}
|
} else {
|
||||||
/* console.warn('no instrument chosen', event);
|
throw new Error('no defaultSynth passed to useRepl.');
|
||||||
|
}
|
||||||
|
/* console.warn('no instrument chosen', event);
|
||||||
throw new Error(`no instrument chosen for ${JSON.stringify(event)}`); */
|
throw new Error(`no instrument chosen for ${JSON.stringify(event)}`); */
|
||||||
} else {
|
} else {
|
||||||
const { onTrigger } = event.value;
|
const { onTrigger } = event.value;
|
||||||
onTrigger(time, event);
|
onTrigger(time, event);
|
||||||
|
}
|
||||||
|
} catch (err: any) {
|
||||||
|
console.warn(err);
|
||||||
|
err.message = 'unplayable event: ' + err?.message;
|
||||||
|
pushLog(err.message); // not with setError, because then we would have to setError(undefined) on next playable event
|
||||||
}
|
}
|
||||||
} catch (err: any) {
|
},
|
||||||
console.warn(err);
|
[onEvent]
|
||||||
err.message = 'unplayable event: ' + err?.message;
|
),
|
||||||
pushLog(err.message); // not with setError, because then we would have to setError(undefined) on next playable event
|
|
||||||
}
|
|
||||||
}, []),
|
|
||||||
onQuery: useCallback(
|
onQuery: useCallback(
|
||||||
(span) => {
|
(span) => {
|
||||||
try {
|
try {
|
||||||
return pattern?.query(span) || [];
|
return pattern?.query(span) || [];
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
|
err.message = 'query error: ' + err.message;
|
||||||
setError(err);
|
setError(err);
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
@ -147,6 +156,7 @@ function useRepl({ tune, defaultSynth, autolink = true }) {
|
||||||
activateCode,
|
activateCode,
|
||||||
activeCode,
|
activeCode,
|
||||||
pushLog,
|
pushLog,
|
||||||
|
hash,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ Pattern.prototype.voicings = function (range) {
|
||||||
range = ['F3', 'A4'];
|
range = ['F3', 'A4'];
|
||||||
}
|
}
|
||||||
return this.fmapNested((event) => {
|
return this.fmapNested((event) => {
|
||||||
lastVoicing = getVoicing(event.value, lastVoicing, range);
|
lastVoicing = getVoicing(event.value?.value || event.value, lastVoicing, range);
|
||||||
return stack(...lastVoicing);
|
return stack(...lastVoicing);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
||||||
72
strudel.mjs
72
strudel.mjs
|
|
@ -148,7 +148,7 @@ class TimeSpan {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
get midpoint() {
|
midpoint() {
|
||||||
return(this.begin.add((this.end.sub(this.begin)).div(Fraction(2))))
|
return(this.begin.add((this.end.sub(this.begin)).div(Fraction(2))))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -298,6 +298,14 @@ class Pattern {
|
||||||
return new Pattern(state => func(this.query(state)))
|
return new Pattern(state => func(this.query(state)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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'.
|
||||||
|
|
@ -478,7 +486,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)).outerJoin()
|
return pat_arg.fmap(arg => func.call(pat,arg)).outerJoin()
|
||||||
}
|
}
|
||||||
return patterned
|
return patterned
|
||||||
|
|
@ -629,6 +644,9 @@ class Pattern {
|
||||||
edit(...funcs) {
|
edit(...funcs) {
|
||||||
return stack(...funcs.map(func => func(this)));
|
return stack(...funcs.map(func => func(this)));
|
||||||
}
|
}
|
||||||
|
pipe(func) {
|
||||||
|
return func(this);
|
||||||
|
}
|
||||||
|
|
||||||
_bypass(on) {
|
_bypass(on) {
|
||||||
on = Boolean(parseInt(on));
|
on = Boolean(parseInt(on));
|
||||||
|
|
@ -680,6 +698,32 @@ function steady(value) {
|
||||||
return new Pattern(span => Hap(undefined, span, value))
|
return new Pattern(span => Hap(undefined, span, value))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const signal = func => {
|
||||||
|
const query = span => [new Hap(undefined, span, func(span.midpoint()))]
|
||||||
|
return new Pattern(query)
|
||||||
|
}
|
||||||
|
|
||||||
|
const _toBipolar = pat => pat.fmap(x => (x * 2) - 1)
|
||||||
|
const _fromBipolar = pat => pat.fmap(x => (x + 1) / 2)
|
||||||
|
|
||||||
|
export const sine2 = signal(t => Math.sin(Math.PI * 2 * t))
|
||||||
|
export const sine = _fromBipolar(sine2)
|
||||||
|
|
||||||
|
export const cosine2 = sine2._early(0.25)
|
||||||
|
export const cosine = sine._early(0.25)
|
||||||
|
|
||||||
|
export const saw = signal(t => t % 1)
|
||||||
|
export const saw2 = _toBipolar(saw)
|
||||||
|
|
||||||
|
export const isaw = signal(t => 1 - (t % 1))
|
||||||
|
export const isaw2 = _toBipolar(isaw)
|
||||||
|
|
||||||
|
export const tri2 = fastcat(isaw2, saw2)
|
||||||
|
export const tri = fastcat(isaw, saw)
|
||||||
|
|
||||||
|
export const square = signal(t => Math.floor((t*2) % 2))
|
||||||
|
export const square2 = _toBipolar(square)
|
||||||
|
|
||||||
function reify(thing) {
|
function reify(thing) {
|
||||||
// Tunrs something into a pattern, unless it's already a pattern
|
// Tunrs something into a pattern, unless it's already a pattern
|
||||||
if (thing?.constructor?.name == "Pattern") {
|
if (thing?.constructor?.name == "Pattern") {
|
||||||
|
|
@ -882,10 +926,34 @@ Pattern.prototype.bootstrap = () => {
|
||||||
return bootstrapped;
|
return bootstrapped;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// this is wrapped around mini patterns to offset krill parser location into the global js code space
|
||||||
|
function withLocationOffset(pat, offset) {
|
||||||
|
return pat.fmap((value) => {
|
||||||
|
value = typeof value === 'object' && !Array.isArray(value) ? value : { value };
|
||||||
|
let locations = (value.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 {...value, locations }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export {Fraction, TimeSpan, Hap, Pattern,
|
export {Fraction, TimeSpan, Hap, Pattern,
|
||||||
pure, stack, slowcat, fastcat, cat, timeCat, sequence, polymeter, pm, polyrhythm, pr, reify, silence,
|
pure, stack, slowcat, fastcat, cat, timeCat, sequence, polymeter, pm, polyrhythm, pr, reify, silence,
|
||||||
fast, slow, early, late, rev,
|
fast, slow, early, late, rev,
|
||||||
add, sub, mul, div, union, every, when, off, jux, append, superimpose,
|
add, sub, mul, div, union, every, when, off, jux, append, superimpose,
|
||||||
struct, mask, invert, inv
|
struct, mask, invert, inv,
|
||||||
|
withLocationOffset
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ import Fraction from 'fraction.js'
|
||||||
|
|
||||||
import { strict as assert } from 'assert';
|
import { strict as assert } from 'assert';
|
||||||
|
|
||||||
import {TimeSpan, Hap, Pattern, pure, stack, fastcat, slowcat, cat, sequence, polyrhythm, silence, fast, timeCat,add,sub,mul,div, State} from "../strudel.mjs";
|
import {TimeSpan, Hap, State, Pattern, pure, stack, fastcat, slowcat, cat, sequence, polyrhythm, silence, fast, timeCat,add,sub,mul,div,saw,saw2,isaw,isaw2,sine,sine2,square,square2,tri,tri2} from "../strudel.mjs";
|
||||||
//import { Time } from 'tone';
|
//import { Time } from 'tone';
|
||||||
import pkg from 'tone';
|
import pkg from 'tone';
|
||||||
const { Time } = pkg;
|
const { Time } = pkg;
|
||||||
|
|
@ -308,4 +308,26 @@ describe('Pattern', function() {
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
describe('signal()', function() {
|
||||||
|
it('Can make saw/saw2', function() {
|
||||||
|
assert.deepStrictEqual(
|
||||||
|
saw.struct(true,true,true,true).firstCycle,
|
||||||
|
sequence(1/8,3/8,5/8,7/8).firstCycle
|
||||||
|
)
|
||||||
|
assert.deepStrictEqual(
|
||||||
|
saw2.struct(true,true,true,true).firstCycle,
|
||||||
|
sequence(-3/4,-1/4,1/4,3/4).firstCycle
|
||||||
|
)
|
||||||
|
})
|
||||||
|
it('Can make isaw/isaw2', function() {
|
||||||
|
assert.deepStrictEqual(
|
||||||
|
isaw.struct(true,true,true,true).firstCycle,
|
||||||
|
sequence(7/8,5/8,3/8,1/8).firstCycle
|
||||||
|
)
|
||||||
|
assert.deepStrictEqual(
|
||||||
|
isaw2.struct(true,true,true,true).firstCycle,
|
||||||
|
sequence(3/4,1/4,-1/4,-3/4).firstCycle
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue