From b64ccb4522b5e5c36e866f0484fcc6cecd9c7613 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Tue, 15 Feb 2022 23:05:38 +0100 Subject: [PATCH] build --- docs/_snowpack/link/strudel.js | 48 ++++++--- docs/dist/App.js | 31 ++++-- docs/dist/evaluate.js | 30 ++++++ docs/dist/groove.js | 1 + docs/dist/parse.js | 112 ++++++--------------- docs/dist/tonal.js | 8 +- docs/dist/tone.js | 3 + docs/dist/tunes.js | 178 ++++++++++----------------------- docs/dist/voicings.js | 22 +++- docs/global.css | 11 ++ 10 files changed, 208 insertions(+), 236 deletions(-) create mode 100644 docs/dist/evaluate.js diff --git a/docs/_snowpack/link/strudel.js b/docs/_snowpack/link/strudel.js index 1fec6bda..f4572d63 100644 --- a/docs/_snowpack/link/strudel.js +++ b/docs/_snowpack/link/strudel.js @@ -4,7 +4,7 @@ const removeUndefineds = (xs) => xs.filter((x) => x != void 0); const flatten = (arr) => [].concat(...arr); const id = (a) => a; export function curry(func, overload) { - return function curried(...args) { + const fn = function curried(...args) { if (args.length >= func.length) { return func.apply(this, args); } else { @@ -17,6 +17,10 @@ export function curry(func, overload) { return partial; } }; + if (overload) { + overload(fn, []); + } + return fn; } Fraction.prototype.sam = function() { return Fraction(Math.floor(this)); @@ -401,19 +405,12 @@ class Pattern { superimpose(...funcs) { return this.stack(...funcs.map((func) => func(this))); } + edit(...funcs) { + return 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) { @@ -425,7 +422,7 @@ function steady(value) { return new Pattern((span) => Hap(void 0, span, value)); } function reify(thing) { - if (thing.constructor.name == "Pattern") { + if (thing?.constructor?.name == "Pattern") { return thing; } return pure(thing); @@ -538,14 +535,34 @@ const when = curry((binary, f, pat) => pat.when(binary, f)); const off = curry((t, f, pat) => pat.off(t, f)); const jux = curry((f, pat) => pat.jux(f)); const append = curry((a, pat) => pat.append(a)); -Pattern.prototype.composable = {fast, slow, early, late}; +const superimpose = curry((array, pat) => pat.superimpose(...array)); +Pattern.prototype.composable = {fast, slow, early, late, superimpose}; export function makeComposable(func) { Object.entries(Pattern.prototype.composable).forEach(([functionName, composable]) => { func[functionName] = (...args) => { - return compose(func, composable(...args)); + const composition = compose(func, composable(...args)); + return makeComposable(composition); }; }); + return func; } +Pattern.prototype.define = (name, func, options = {}) => { + if (options.composable) { + Pattern.prototype.composable[name] = func; + } + if (options.patternified) { + Pattern.prototype.patternified = Pattern.prototype.patternified.concat([name]); + } +}; +Pattern.prototype.bootstrap = () => { + const bootstrapped = Object.fromEntries(Object.entries(Pattern.prototype.composable).map(([functionName, composable]) => { + if (Pattern.prototype[functionName]) { + Pattern.prototype[functionName] = makeComposable(Pattern.prototype[functionName]); + } + return [functionName, curry(composable, makeComposable)]; + })); + return bootstrapped; +}; export { Fraction, TimeSpan, @@ -578,5 +595,6 @@ export { when, off, jux, - append + append, + superimpose }; diff --git a/docs/dist/App.js b/docs/dist/App.js index 2cf5800e..bc17fd47 100644 --- a/docs/dist/App.js +++ b/docs/dist/App.js @@ -3,13 +3,12 @@ import logo from "./logo.svg.proxy.js"; import cx from "./cx.js"; import * as Tone from "../_snowpack/pkg/tone.js"; import useCycle from "./useCycle.js"; -import defaultTune from "./tunes.js"; -import * as parser from "./parse.js"; +import * as tunes from "./tunes.js"; +import {evaluate} from "./evaluate.js"; import CodeMirror from "./CodeMirror.js"; import hot from "../hot.js"; import {isNote} from "../_snowpack/pkg/tone.js"; import {useWebMidi} from "./midi.js"; -const {parse} = parser; const [_, codeParam] = window.location.href.split("#"); const decoded = atob(codeParam || ""); const getHotCode = async () => { @@ -17,16 +16,22 @@ const getHotCode = async () => { return src.split("export default").slice(-1)[0].trim(); }); }; -const defaultSynth = new Tone.PolySynth().toDestination(); +const defaultSynth = new Tone.PolySynth().chain(new Tone.Gain(0.5), Tone.Destination); defaultSynth.set({ oscillator: {type: "triangle"}, envelope: { release: 0.01 } }); +function getRandomTune() { + const allTunes = Object.values(tunes); + const randomItem = (arr) => arr[Math.floor(Math.random() * arr.length)]; + return randomItem(allTunes); +} +const randomTune = getRandomTune(); function App() { const [mode, setMode] = useState("javascript"); - const [code, setCode] = useState(decoded || defaultTune); + const [code, setCode] = useState(decoded || randomTune); const [log, setLog] = useState(""); const logBox = useRef(); const [error, setError] = useState(); @@ -106,7 +111,7 @@ function App() { } } try { - const parsed = parse(_code); + const parsed = evaluate(_code); setPattern(() => parsed.pattern); if (isHot) { activatePattern(parsed.pattern); @@ -144,13 +149,23 @@ function App() { alt: "logo" }), /* @__PURE__ */ React.createElement("h1", { className: "text-2xl" - }, "Strudel REPL")), window.location.href.includes("http://localhost:8080") && /* @__PURE__ */ React.createElement("button", { + }, "Strudel REPL")), /* @__PURE__ */ React.createElement("div", { + className: "flex space-x-4" + }, /* @__PURE__ */ React.createElement("button", { + onClick: () => { + const _code = getRandomTune(); + console.log("tune", _code); + setCode(_code); + const parsed = evaluate(_code); + setActivePattern(parsed.pattern); + } + }, "🎲 random tune"), window.location.href.includes("http://localhost:8080") && /* @__PURE__ */ React.createElement("button", { onClick: () => { if (isHot || confirm("Really switch? You might loose your current pattern..")) { setIsHot((h) => !h); } } - }, isHot ? "🔥" : " ", " toggle hot mode")), /* @__PURE__ */ React.createElement("section", { + }, "🔥 toggle hot mode"))), /* @__PURE__ */ React.createElement("section", { className: "grow flex flex-col text-gray-100" }, /* @__PURE__ */ React.createElement("div", { className: "grow relative" diff --git a/docs/dist/evaluate.js b/docs/dist/evaluate.js new file mode 100644 index 00000000..5288ce8d --- /dev/null +++ b/docs/dist/evaluate.js @@ -0,0 +1,30 @@ +import * as strudel from "../_snowpack/link/strudel.js"; +import "./tone.js"; +import "./midi.js"; +import "./voicings.js"; +import "./tonal.js"; +import "./groove.js"; +import shapeshifter from "./shapeshifter.js"; +import {minify} from "./parse.js"; +const bootstrapped = {...strudel, ...strudel.Pattern.prototype.bootstrap()}; +function hackLiteral(literal, names, func) { + names.forEach((name) => { + Object.defineProperty(literal.prototype, name, { + get: function() { + return func(String(this)); + } + }); + }); +} +hackLiteral(String, ["mini", "m"], bootstrapped.mini); +hackLiteral(String, ["pure", "p"], bootstrapped.pure); +Object.assign(globalThis, bootstrapped); +export const evaluate = (code) => { + const shapeshifted = shapeshifter(code); + const pattern = minify(eval(shapeshifted)); + 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: "javascript", pattern}; +}; diff --git a/docs/dist/groove.js b/docs/dist/groove.js index d28990bc..2a874048 100644 --- a/docs/dist/groove.js +++ b/docs/dist/groove.js @@ -3,3 +3,4 @@ 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}); diff --git a/docs/dist/parse.js b/docs/dist/parse.js index 101bd679..5ba6a6b1 100644 --- a/docs/dist/parse.js +++ b/docs/dist/parse.js @@ -1,61 +1,7 @@ import * as krill from "../_snowpack/link/repl/krill-parser.js"; import * as strudel from "../_snowpack/link/strudel.js"; import {Scale, Note, Interval} from "../_snowpack/pkg/@tonaljs/tonal.js"; -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, - 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; - } - return pure(thing); -} -function minify(thing) { - if (typeof thing === "string") { - return mini(thing); - } - return reify(thing); -} +const {pure, Pattern, Fraction, stack, slowcat, sequence, timeCat, silence} = strudel; const applyOptions = (parent) => (pat, i) => { const ast = parent.source_[i]; const options = ast.options_; @@ -81,12 +27,20 @@ export function patternifyAST(ast) { switch (ast.type_) { case "pattern": const children = ast.source_.map(patternifyAST).map(applyOptions(ast)); - if (ast.arguments_.alignment === "v") { + const alignment = ast.arguments_.alignment; + if (alignment === "v") { return stack(...children); } const weightedChildren = ast.source_.some((child) => !!child.options_?.weight); + if (!weightedChildren && alignment === "t") { + return slowcat(...children); + } if (weightedChildren) { - return timeCat(...ast.source_.map((child, i) => [child.options_?.weight || 1, children[i]])); + const pat = timeCat(...ast.source_.map((child, i) => [child.options_?.weight || 1, children[i]])); + if (alignment === "t") { + return pat._slow(children.length); + } + return pat; } return sequence(...children); case "element": @@ -112,7 +66,7 @@ export function patternifyAST(ast) { return step; } const octaves = Math.floor(step / intervals.length); - const mod = (n, m2) => n < 0 ? mod(n + m2, m2) : n % m2; + const mod = (n, m) => n < 0 ? mod(n + m, m) : n % m; const index = mod(step, intervals.length); const interval = Interval.add(intervals[index], Interval.fromSemitones(octaves * 12)); return Note.transpose(tonic, interval || "1P"); @@ -123,36 +77,28 @@ export function patternifyAST(ast) { } } export const mini = (...strings) => { - const pattern = sequence(...strings.map((str) => { + const pats = strings.map((str) => { const ast = krill.parse(`"${str}"`); return patternifyAST(ast); - })); - return pattern; + }); + return sequence(...pats); }; -const m = mini; -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); }; -export const parse = (code) => { - let _pattern; - let mode; - 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?" : ".")); +Pattern.prototype.define("mini", mini, {composable: true}); +Pattern.prototype.define("m", mini, {composable: true}); +Pattern.prototype.define("h", h, {composable: true}); +export function reify(thing) { + if (thing?.constructor?.name === "Pattern") { + return thing; } - return {mode, pattern: _pattern}; -}; + return pure(thing); +} +export function minify(thing) { + if (typeof thing === "string") { + return mini(thing); + } + return reify(thing); +} diff --git a/docs/dist/tonal.js b/docs/dist/tonal.js index 0442f44a..60df2dc8 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, curry, makeComposable} from "../_snowpack/link/strudel.js"; +import {Pattern as _Pattern} from "../_snowpack/link/strudel.js"; const Pattern = _Pattern; function toNoteEvent(event) { if (typeof event === "string") { @@ -55,7 +55,6 @@ Pattern.prototype._transpose = function(intervalOrSemitones) { return {value: Note.transpose(value, interval), scale}; }); }; -export const transpose = curry((a, pat) => pat.transpose(a), (partial) => makeComposable(partial)); Pattern.prototype._scaleTranspose = function(offset) { return this._mapNotes(({value, scale}) => { if (!scale) { @@ -67,5 +66,6 @@ Pattern.prototype._scaleTranspose = function(offset) { Pattern.prototype._scale = function(scale) { return this._mapNotes((value) => ({...value, scale})); }; -Pattern.prototype.patternified = Pattern.prototype.patternified.concat(["transpose", "scaleTranspose", "scale"]); -Object.assign(Pattern.prototype.composable, {transpose}); +Pattern.prototype.define("transpose", (a, pat) => pat.transpose(a), {composable: true, patternified: true}); +Pattern.prototype.define("scale", (a, pat) => pat.scale(a), {composable: true, patternified: true}); +Pattern.prototype.define("scaleTranspose", (a, pat) => pat.scaleTranspose(a), {composable: true, patternified: true}); diff --git a/docs/dist/tone.js b/docs/dist/tone.js index be01fc31..f9722c6f 100644 --- a/docs/dist/tone.js +++ b/docs/dist/tone.js @@ -69,3 +69,6 @@ Pattern.prototype.autofilter = function(g) { return this.chain(autofilter(g)); }; Pattern.prototype.patternified = Pattern.prototype.patternified.concat(["synth", "gain", "filter"]); +Pattern.prototype.define("synth", (type, pat) => pat.synth(type), {composable: true, patternified: true}); +Pattern.prototype.define("gain", (gain2, pat) => pat.synth(gain2), {composable: true, patternified: true}); +Pattern.prototype.define("filter", (cutoff, pat) => pat.filter(cutoff), {composable: true, patternified: true}); diff --git a/docs/dist/tunes.js b/docs/dist/tunes.js index 798c5093..82521350 100644 --- a/docs/dist/tunes.js +++ b/docs/dist/tunes.js @@ -33,7 +33,6 @@ export const shapeShifted = `stack( a1, a2, a1, a2, a1, a2, a1, a2, ).rev() ).slow(16)`; -export const tetrisMidi = `${shapeShifted}.midi('IAC-Treiber Bus 1')`; export const tetrisWithFunctions = `stack(sequence( 'e5', sequence('b4', 'c5'), 'd5', sequence('c5', 'b4'), 'a4', sequence('a4', 'c5'), 'e5', sequence('d5', 'c5'), @@ -53,9 +52,8 @@ export const tetrisWithFunctions = `stack(sequence( 'b1', 'b2', 'b1', 'b2', 'e2', 'e3', 'e2', 'e3', 'a1', 'a2', 'a1', 'a2', 'a1', 'a2', 'a1', 'a2', ) -)._slow(16)`; +).slow(16)`; export const tetris = `stack( - sequence( mini( 'e5 [b4 c5] d5 [c5 b4]', 'a4 [a4 c5] e5 [d5 c5]', @@ -65,9 +63,7 @@ export const tetris = `stack( 'e5 [~ c5] e5 [d5 c5]', 'b4 [b4 c5] d5 e5', 'c5 a4 a4 ~' - ) - ), - sequence( + ), mini( 'e2 e3 e2 e3 e2 e3 e2 e3', 'a2 a3 a2 a3 a2 a3 a2 a3', @@ -77,13 +73,9 @@ export const tetris = `stack( '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).synth({ - oscillator: {type: 'sawtooth'} -})`; +).slow(16)`; export const tetrisRev = `stack( - sequence( mini( 'e5 [b4 c5] d5 [c5 b4]', 'a4 [a4 c5] e5 [d5 c5]', @@ -93,9 +85,7 @@ export const tetrisRev = `stack( 'e5 [~ c5] e5 [d5 c5]', 'b4 [b4 c5] d5 e5', 'c5 a4 a4 ~' - ).rev() - ), - sequence( + ).rev(), mini( 'e2 e3 e2 e3 e2 e3 e2 e3', 'a2 a3 a2 a3 a2 a3 a2 a3', @@ -106,10 +96,8 @@ export const tetrisRev = `stack( 'b1 b2 b1 b2 e2 e3 e2 e3', 'a1 a2 a1 a2 a1 a2 a1 a2' ).rev() - ) -).slow(16).synth('sawtooth').filter(1000).gain(0.6)`; -export const tetrisMini1 = `m\`[[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 = `m\`[[e5 [b4 c5] d5 [c5 b4]] +).slow(16)`; +export const tetrisMini = `\`[[e5 [b4 c5] d5 [c5 b4]] [a4 [a4 c5] e5 [d5 c5]] [b4 [~ c5] d5 e5] [c5 a4 a4 ~] @@ -124,53 +112,19 @@ export const tetrisMini = `m\`[[e5 [b4 c5] d5 [c5 b4]] [[d2 d3]*4] [[c2 c3]*4] [[b1 b2]*2 [e2 e3]*2] -[[a1 a2]*4]\`._slow(16); -`; -export const tetrisHaskellH = `h(\`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]*4] -[[a2 a3]*4] -[[g#2 g#3]*2 [e2 e3]*2] -[a2 a3 a2 a3 a2 a3 b1 c2] -[[d2 d3]*4] -[[c2 c3]*4] -[[b1 b2]*2 [e2 e3]*2] -[[a1 a2]*4]"\`) -`; -export const tetrisHaskell = `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]*4] -[[a2 a3]*4] -[[g#2 g#3]*2 [e2 e3]*2] -[a2 a3 a2 a3 a2 a3 b1 c2] -[[d2 d3]*4] -[[c2 c3]*4] -[[b1 b2]*2 [e2 e3]*2] -[[a1 a2]*4]" +[[a1 a2]*4]\`.mini.slow(16) `; export const spanish = `slowcat( - stack('c4','eb4','g4'), - stack('bb3','d4','f4'), - stack('ab3','c4','eb4'), - stack('g3','b3','d4') - )`; + stack(c4,eb4,g4), + stack(bb3,d4,f4), + stack(ab3,c4,eb4), + stack(g3,b3,d4) +)`; export const whirlyStrudel = `mini("[e4 [b2 b3] c4]") - .every(4, x => x.fast(2)) - .every(3, x => x.slow(1.5)) - .fast(slowcat(1.25,1,1.5)) - .every(2, _ => mini("e4 ~ e3 d4 ~"))`; +.every(4, fast(2)) +.every(3, slow(1.5)) +.fast(slowcat(1.25, 1, 1.5)) +.every(2, _ => mini("e4 ~ e3 d4 ~"))`; export const swimming = `stack( mini( '~', @@ -254,12 +208,32 @@ export const giantSteps = `stack( '[B2 F#2] [F2 Bb2] [Eb2 Bb3] [C#2 F#2]' ) ).slow(20);`; -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 giantStepsReggae = `stack( + // melody + mini( + '[F#5 D5] [B4 G4] Bb4 [B4 A4]', + '[D5 Bb4] [G4 Eb4] F#4 [G4 F4]', + 'Bb4 [B4 A4] D5 [D#5 C#5]', + 'F#5 [G5 F5] Bb5 [F#5 [F#5 ~@3]]', + ), + // chords + mini( + '[B^7 D7] [G^7 Bb7] Eb^7 [Am7 D7]', + '[G^7 Bb7] [Eb^7 F#7] B^7 [Fm7 Bb7]', + 'Eb^7 [Am7 D7] G^7 [C#m7 F#7]', + 'B^7 [Fm7 Bb7] Eb^7 [C#m7 F#7]' + ) + .groove('~ [x ~]'.m.fast(4*8)) + .voicings(['E3', 'G4']), + // bass + mini( + '[B2 D2] [G2 D2] [Eb2 Bb2] [A2 D2]', + '[G2 Bb2] [Eb2 F#2] [B2 F#2] [F2 Bb2]', + '[Eb2 Bb2] [A2 D2] [G2 D2] [C#2 F#2]', + '[B2 F#2] [F2 Bb2] [Eb2 Bb2] [C#2 F#2]' + ) + .groove('x ~'.m.fast(4*8)) +).slow(25)`; export const transposedChordsHacked = `stack( 'c2 eb2 g2'.mini, 'Cm7'.pure.voicings(['g2','c4']).slow(2) @@ -269,76 +243,34 @@ export const transposedChordsHacked = `stack( 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)) -.transpose(sequence(0, 1).slow(16)) -.synth('sawtooth') -.filter(800) -.gain(0.5)`; +.transpose(sequence(0, 1).slow(16))`; export const groove = `stack( - m('c2 g2 a2 [e2@2 eb2] d2 a2 g2 [d2 ~ db2]') - .synth('sawtooth') - .filter(500) - .gain(.6), - m('[C^7 A7] [Dm7 G7]') - .groove(m('[x@2 x] [~@2 x] [~ x@2]@2 [x ~@2] ~ [~@2 x@4]@2')) - .voicings(['G3','A4']) - .synth('square') - .filter(1000) - .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), + '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']) - .synth('square') - .filter(1000) - .adsr(.1,.1,.2) - .gain(0.25) -).slow(4.5)`; +).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).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)`; + .transpose.slowcat(0, 2, 3, 4)`; 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 const confusedPhonePartial = `stack('[g2 ~@1.3] [c3 ~@1.3]'.mini.slow(2)) +.synth('triangle').gain(0.5).filter(1500)`; +export const confusedPhone = `'[g2 ~@1.3] [c3 ~@1.3]'.mini .superimpose( transpose(-12).late(0), - transpose(7).late(0.2), - transpose(10).late(0.4), - transpose(12).late(0.6), - transpose(24).late(0.8) + transpose(7).late(0.1), + transpose(10).late(0.2), + transpose(12).late(0.3), + transpose(24).late(0.4) ) -.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; +.scale(slowcat('C dorian', 'C mixolydian')) +.scaleTranspose(slowcat(0,1,2,1)) +.slow(2)`; diff --git a/docs/dist/voicings.js b/docs/dist/voicings.js index 4ad12908..c03cd252 100644 --- a/docs/dist/voicings.js +++ b/docs/dist/voicings.js @@ -1,6 +1,6 @@ import {Pattern as _Pattern, stack, Hap, reify} from "../_snowpack/link/strudel.js"; -import voicings from "../_snowpack/pkg/chord-voicings.js"; -const {dictionaryVoicing, minTopNoteDiff, lefthand} = voicings; +import _voicings from "../_snowpack/pkg/chord-voicings.js"; +const {dictionaryVoicing, minTopNoteDiff, lefthand} = _voicings; const getVoicing = (chord, lastVoicing, range = ["F3", "A4"]) => dictionaryVoicing({ chord, dictionary: lefthand, @@ -12,10 +12,26 @@ const Pattern = _Pattern; Pattern.prototype.fmapNested = function(func) { return new Pattern((span) => this.query(span).map((event) => reify(func(event)).query(span).map((hap) => new Hap(event.whole, event.part, hap.value))).flat()); }; -Pattern.prototype.voicings = function(range = ["F3", "A4"]) { +Pattern.prototype.voicings = function(range) { let lastVoicing; + if (!range?.length) { + range = ["F3", "A4"]; + } return this.fmapNested((event) => { lastVoicing = getVoicing(event.value, lastVoicing, range); return stack(...lastVoicing); }); }; +Pattern.prototype.chordBass = function() { + return this._mapNotes((value) => { + console.log("value", value); + const [_, root] = value.value.match(/^([a-gC-G])[b#]?.*$/); + const bassNote = root + "2"; + return {...value, value: bassNote}; + }); +}; +Pattern.prototype.define("voicings", (range, pat) => pat.voicings(range), {composable: true}); +Pattern.prototype.define("chordBass", (pat) => { + console.log("call chordBass ...", pat); + return pat.chordBass(); +}, {composable: true}); diff --git a/docs/global.css b/docs/global.css index 7a3c9905..a5fc7dfb 100644 --- a/docs/global.css +++ b/docs/global.css @@ -621,6 +621,9 @@ select { .flex { display: flex; } +.contents { + display: contents; +} .h-16 { height: 4rem; } @@ -645,6 +648,9 @@ select { .grow { flex-grow: 1; } +.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)); +} .flex-col { flex-direction: column; } @@ -659,6 +665,11 @@ select { margin-right: calc(0.5rem * var(--tw-space-x-reverse)); margin-left: calc(0.5rem * calc(1 - var(--tw-space-x-reverse))); } +.space-x-4 > :not([hidden]) ~ :not([hidden]) { + --tw-space-x-reverse: 0; + margin-right: calc(1rem * var(--tw-space-x-reverse)); + margin-left: calc(1rem * calc(1 - var(--tw-space-x-reverse))); +} .whitespace-pre { white-space: pre; }