From 22baae10dd5f0689a6b49638f70e14aa8a01fc19 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Mon, 14 Feb 2022 19:57:24 +0100 Subject: [PATCH] build --- docs/_snowpack/link/strudel.js | 46 +++++++++++++++++++-------- docs/dist/App.js | 12 +++++-- docs/dist/midi.js | 1 + docs/dist/parse.js | 56 +++++++++++++++++++++----------- docs/dist/tonal.js | 13 ++------ docs/dist/tone.js | 10 +----- docs/dist/tunes.js | 58 +++++++++++++++++++++++++++++++--- docs/global.css | 4 +-- 8 files changed, 140 insertions(+), 60 deletions(-) diff --git a/docs/_snowpack/link/strudel.js b/docs/_snowpack/link/strudel.js index 13ee30b9..25e8f8aa 100644 --- a/docs/_snowpack/link/strudel.js +++ b/docs/_snowpack/link/strudel.js @@ -2,7 +2,7 @@ import Fraction from "../pkg/fractionjs.js"; const removeUndefineds = (xs) => xs.filter((x) => x != void 0); const flatten = (arr) => [].concat(...arr); const id = (a) => a; -function curry(func) { +export function curry(func) { return function curried(...args) { if (args.length >= func.length) { return func.apply(this, args); @@ -134,6 +134,14 @@ class Hap { class Pattern { constructor(query) { this.query = query; + const proto = Object.getPrototypeOf(this); + proto.patternified.forEach((prop) => { + this[prop] = (...args) => this._patternify(Pattern.prototype["_" + prop])(...args); + Object.assign(this[prop], Object.fromEntries(Object.entries(Pattern.prototype.factories).map(([type, func]) => [ + type, + (...args) => this[prop](func(...args)) + ]))); + }); } _splitQueries() { const pat = this; @@ -321,28 +329,16 @@ class Pattern { const fastQuery = this.withQueryTime((t) => t.mul(factor)); return fastQuery.withEventTime((t) => t.div(factor)); } - fast(...factor) { - return this._patternify(Pattern.prototype._fast)(...factor); - } _slow(factor) { return this._fast(1 / factor); } - slow(...factor) { - return this._patternify(Pattern.prototype._slow)(...factor); - } _early(offset) { offset = Fraction(offset); return this.withQueryTime((t) => t.add(offset)).withEventTime((t) => t.sub(offset)); } - early(...factor) { - return this._patternify(Pattern.prototype._early)(...factor); - } _late(offset) { return this._early(0 - offset); } - late(...factor) { - return this._patternify(Pattern.prototype._late)(...factor); - } when(binary_pat, func) { const true_pat = binary_pat._filterValues(id); const false_pat = binary_pat._filterValues((val) => !val); @@ -391,7 +387,28 @@ class Pattern { const right = this.withValue((val) => Object.assign({}, val, {pan: elem_or(val, "pan", 0.5) + by})); return stack([left, func(right)]); } + stack(...pats) { + return stack(this, ...pats); + } + sequence(...pats) { + return sequence(this, ...pats); + } + superimpose(...funcs) { + return this.stack(...funcs.map((func) => func(this))); + } } +Pattern.prototype.patternified = ["fast", "slow", "early", "late"]; +Pattern.prototype.factories = {pure, stack, slowcat, fastcat, cat, timeCat, sequence, polymeter, pm, polyrhythm, pr}; +const hackStrings = () => { + const pureGetter = { + get: function() { + return pure(String(this)); + } + }; + Object.defineProperty(String.prototype, "pure", pureGetter); + Object.defineProperty(String.prototype, "p", pureGetter); +}; +hackStrings(); const silence = new Pattern((_) => []); function pure(value) { function query(span) { @@ -418,6 +435,9 @@ function slowcat(...pats) { const query = function(span) { const pat_n = Math.floor(span.begin) % pats.length; const pat = pats[pat_n]; + if (!pat) { + return []; + } const offset = span.begin.floor().sub(span.begin.div(pats.length).floor()); return pat.withEventTime((t) => t.add(offset)).query(span.withTime((t) => t.sub(offset))); }; diff --git a/docs/dist/App.js b/docs/dist/App.js index dc37a72b..2cf5800e 100644 --- a/docs/dist/App.js +++ b/docs/dist/App.js @@ -77,8 +77,14 @@ function App() { }); useLayoutEffect(() => { const handleKeyPress = (e) => { - if (e.ctrlKey && e.code === "Enter") { - activatePattern(); + if (e.ctrlKey || e.altKey) { + switch (e.code) { + case "Enter": + activatePattern(); + break; + case "Period": + cycle.stop(); + } } }; document.addEventListener("keypress", handleKeyPress); @@ -164,7 +170,7 @@ function App() { } } }), /* @__PURE__ */ React.createElement("span", { - className: "p-4 absolute bottom-0 right-0 text-xs whitespace-pre text-right" + className: "p-4 absolute top-0 right-0 text-xs whitespace-pre text-right" }, !cycle.started ? `press ctrl+enter to play ` : !isHot && activePattern !== pattern ? `ctrl+enter to update ` : "no changes\n", !isHot && /* @__PURE__ */ React.createElement(React.Fragment, null, {pegjs: "mini"}[mode] || mode, " mode"), isHot && "🔥 hot mode: go to hot.js to edit pattern, then save")), error && /* @__PURE__ */ React.createElement("div", { diff --git a/docs/dist/midi.js b/docs/dist/midi.js index 3cf46f88..89cb0037 100644 --- a/docs/dist/midi.js +++ b/docs/dist/midi.js @@ -24,6 +24,7 @@ Pattern.prototype.midi = function(output, channel = 1) { return this.fmap((value) => ({ ...value, onTrigger: (time, event) => { + value = value.value || value; if (!isNote(value)) { throw new Error("not a note: " + value); } diff --git a/docs/dist/parse.js b/docs/dist/parse.js index 72677bd5..101bd679 100644 --- a/docs/dist/parse.js +++ b/docs/dist/parse.js @@ -5,28 +5,45 @@ import "./tone.js"; import "./midi.js"; import "./voicings.js"; import "./tonal.js"; +import * as tonalStuff from "./tonal.js"; import "./groove.js"; import * as toneStuff from "./tone.js"; import shapeshifter from "./shapeshifter.js"; const { + Fraction, + TimeSpan, + Hap, + Pattern, pure, stack, slowcat, fastcat, cat, + timeCat, sequence, polymeter, pm, polyrhythm, pr, silence, - timeCat, - Fraction, - Pattern, - TimeSpan, - Hap + fast, + slow, + early, + late, + rev, + add, + sub, + mul, + div, + union, + every, + when, + off, + jux, + append } = strudel; const {autofilter, filter, gain} = toneStuff; +const {transpose} = tonalStuff; function reify(thing) { if (thing?.constructor?.name === "Pattern") { return thing; @@ -113,10 +130,16 @@ export const mini = (...strings) => { return pattern; }; const m = mini; -const s = (...strings) => { - const patternified = strings.map((s2) => minify(s2)); - return stack(...patternified); +const hackStrings = () => { + const miniGetter = { + get: function() { + return mini(String(this)); + } + }; + Object.defineProperty(String.prototype, "mini", miniGetter); + Object.defineProperty(String.prototype, "m", miniGetter); }; +hackStrings(); export const h = (string) => { const ast = krill.parse(string); return patternifyAST(ast); @@ -124,17 +147,12 @@ export const h = (string) => { export const parse = (code) => { let _pattern; let mode; - try { - _pattern = h(code); - mode = "pegjs"; - } catch (err) { - mode = "javascript"; - code = shapeshifter(code); - _pattern = eval(code); - if (_pattern?.constructor?.name !== "Pattern") { - const message = `got "${typeof _pattern}" instead of pattern`; - throw new Error(message + (typeof _pattern === "function" ? ", did you forget to call a function?" : ".")); - } + mode = "javascript"; + code = shapeshifter(code); + _pattern = minify(eval(code)); + if (_pattern?.constructor?.name !== "Pattern") { + const message = `got "${typeof _pattern}" instead of pattern`; + throw new Error(message + (typeof _pattern === "function" ? ", did you forget to call a function?" : ".")); } return {mode, pattern: _pattern}; }; diff --git a/docs/dist/tonal.js b/docs/dist/tonal.js index b48bc80e..4f5a0950 100644 --- a/docs/dist/tonal.js +++ b/docs/dist/tonal.js @@ -1,5 +1,5 @@ import {Note, Interval, Scale} from "../_snowpack/pkg/@tonaljs/tonal.js"; -import {Pattern as _Pattern} from "../_snowpack/link/strudel.js"; +import {Pattern as _Pattern, curry} from "../_snowpack/link/strudel.js"; const Pattern = _Pattern; function toNoteEvent(event) { if (typeof event === "string") { @@ -55,9 +55,7 @@ Pattern.prototype._transpose = function(intervalOrSemitones) { return {value: Note.transpose(value, interval), scale}; }); }; -Pattern.prototype.transpose = function(intervalOrSemitones) { - return this._patternify(Pattern.prototype._transpose)(intervalOrSemitones); -}; +export const transpose = curry((a, pat) => pat.transpose(a)); Pattern.prototype._scaleTranspose = function(offset) { return this._mapNotes(({value, scale}) => { if (!scale) { @@ -66,12 +64,7 @@ Pattern.prototype._scaleTranspose = function(offset) { return {value: scaleTranspose(scale, Number(offset), value), scale}; }); }; -Pattern.prototype.scaleTranspose = function(offset) { - return this._patternify(Pattern.prototype._scaleTranspose)(offset); -}; Pattern.prototype._scale = function(scale) { return this._mapNotes((value) => ({...value, scale})); }; -Pattern.prototype.scale = function(scale) { - return this._patternify(Pattern.prototype._scale)(scale); -}; +Pattern.prototype.patternified = Pattern.prototype.patternified.concat(["transpose", "scaleTranspose", "scale"]); diff --git a/docs/dist/tone.js b/docs/dist/tone.js index 7bdd9560..be01fc31 100644 --- a/docs/dist/tone.js +++ b/docs/dist/tone.js @@ -27,9 +27,6 @@ Pattern.prototype._synth = function(type = "triangle") { return {...value, getInstrument, instrumentConfig, onTrigger}; }); }; -Pattern.prototype.synth = function(type = "triangle") { - return this._patternify(Pattern.prototype._synth)(type); -}; Pattern.prototype.adsr = function(attack = 0.01, decay = 0.01, sustain = 0.6, release = 0.01) { return this.fmap((value) => { if (!value?.getInstrument) { @@ -65,15 +62,10 @@ export const gain = (gain2 = 0.9) => () => new Gain(gain2); Pattern.prototype._gain = function(g) { return this.chain(gain(g)); }; -Pattern.prototype.gain = function(g) { - return this._patternify(Pattern.prototype._gain)(g); -}; Pattern.prototype._filter = function(freq, q, type = "lowpass") { return this.chain(filter(freq, q, type)); }; -Pattern.prototype.filter = function(freq) { - return this._patternify(Pattern.prototype._filter)(freq); -}; Pattern.prototype.autofilter = function(g) { return this.chain(autofilter(g)); }; +Pattern.prototype.patternified = Pattern.prototype.patternified.concat(["synth", "gain", "filter"]); diff --git a/docs/dist/tunes.js b/docs/dist/tunes.js index 6875228b..e4b8a408 100644 --- a/docs/dist/tunes.js +++ b/docs/dist/tunes.js @@ -1,7 +1,7 @@ -export const timeCatMini = `s( - 'c3@3 [eb3, g3, [c4 d4]/2]', - 'c2 g2', - m('[eb4@5 [f4 eb4 d4]@3] [eb4 c4]/2').slow(8) +export const timeCatMini = `stack( + 'c3@3 [eb3, g3, [c4 d4]/2]'.mini, + 'c2 g2'.mini, + '[eb4@5 [f4 eb4 d4]@3] [eb4 c4]/2'.mini.slow(8) )`; export const timeCat = `stack( timeCat([3, c3], [1, stack(eb3, g3, m(c4, d4).slow(2))]), @@ -260,6 +260,12 @@ export const transposedChords = `stack( ).transpose( slowcat(1, 2, 3, 2).slow(2) ).transpose(5)`; +export const transposedChordsHacked = `stack( + 'c2 eb2 g2'.mini, + 'Cm7'.pure.voicings(['g2','c4']).slow(2) +).transpose( + slowcat(1, 2, 3, 2).slow(2) +).transpose(5)`; export const scaleTranspose = `stack(f2, f3, c4, ab4) .scale(sequence('F minor', 'F harmonic minor').slow(4)) .scaleTranspose(sequence(0, -1, -2, -3).slow(4)) @@ -280,4 +286,48 @@ export const groove = `stack( .adsr(.1,.1,.2) .gain(0.25) ).slow(4.5)`; +export const grooveHacked = `stack( + 'c2 g2 a2 [e2@2 eb2] d2 a2 g2 [d2 ~ db2]'.mini + .synth('sawtooth') + .filter(500) + .gain(.6), + '[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']) + .synth('square') + .filter(1000) + .adsr(.1,.1,.2) + .gain(0.25) +).slow(4.5)`; +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).midi()`; +export const magicSofaHacked = `stack( + '[C^7 F^7 ~]/3 [Dm7 G7 A7 ~]/4'.mini + .every(2, fast(2)) + .voicings(), + '[c2 f2 g2]/3 [d2 g2 a2 e2]/4'.mini +).slow(1) + .transpose.slowcat(0, 2, 3, 4).midi()`; +export const confusedPhone = `stack('[g2 ~@1.3] [c3 ~@1.3]'.mini.slow(2)) +.superimpose( + x => transpose(-12,x).late(0), + x => transpose(7,x).late(0.2), + x => transpose(10,x).late(0.4), + x => transpose(12,x).late(0.6), + x => transpose(24,x).late(0.8) +) +.scale(sequence('C dorian', 'C mixolydian').slow(4)) +.scaleTranspose(slowcat(0,1,2,1).slow(2)) +.synth('triangle').gain(0.2).filter(1500)`; +export const confusedPhoneDynamic = `stack('[g2 ~@1.3] [c3 ~@1.3]'.mini.slow(2)) +.superimpose( + ...[-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)) +.scaleTranspose(slowcat(0,1,2,1).slow(2)) +.synth('triangle').gain(0.2).filter(1500)`; export default swimming; diff --git a/docs/global.css b/docs/global.css index c07c0480..7a3c9905 100644 --- a/docs/global.css +++ b/docs/global.css @@ -603,8 +603,8 @@ select { position: -webkit-sticky; position: sticky; } -.bottom-0 { - bottom: 0px; +.top-0 { + top: 0px; } .right-0 { right: 0px;