This commit is contained in:
Felix Roos 2022-03-17 15:05:01 +01:00
parent 91c0a46ba9
commit 15e49952f9
12 changed files with 253 additions and 44 deletions

View File

@ -262,7 +262,7 @@ function peg$parse(input, options) {
var peg$f3 = function(sc) { sc.arguments_.alignment = "t"; return sc;};
var peg$f4 = function(a) { return { weight: a} };
var peg$f5 = function(a) { return { replicate: a } };
var peg$f6 = function(p, s) { return { operator : { type_: "bjorklund", arguments_ :{ pulse: p, step:s } } } };
var peg$f6 = function(p, s, r) { return { operator : { type_: "bjorklund", arguments_ :{ pulse: p, step:s, rotation:r || 0 } } } };
var peg$f7 = function(a) { return { operator : { type_: "stretch", arguments_ :{ amount:a } } } };
var peg$f8 = function(a) { return { operator : { type_: "stretch", arguments_ :{ amount:"1/"+a } } } };
var peg$f9 = function(a) { return { operator : { type_: "fixed-step", arguments_ :{ amount:a } } } };
@ -273,7 +273,7 @@ function peg$parse(input, options) {
var peg$f14 = function(s) { return s; };
var peg$f15 = function(s) { return { name: "struct", args: { sequence:s }}};
var peg$f16 = function(s) { return { name: "target", args : { name:s}}};
var peg$f17 = function(p, s) { return { name: "bjorklund", args :{ pulse: parseInt(p), step:parseInt(s) }}};
var peg$f17 = function(p, s, r) { return { name: "bjorklund", args :{ pulse: parseInt(p), step:parseInt(s) }}};
var peg$f18 = function(a) { return { name: "stretch", args :{ amount: a}}};
var peg$f19 = function(a) { return { name: "shift", args :{ amount: "-"+a}}};
var peg$f20 = function(a) { return { name: "shift", args :{ amount: a}}};
@ -1022,7 +1022,7 @@ function peg$parse(input, options) {
}
function peg$parseslice_bjorklund() {
var s0, s1, s2, s3, s4, s5, s6, s7, s8, s9;
var s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13;
s0 = peg$currPos;
if (input.charCodeAt(peg$currPos) === 40) {
@ -1043,16 +1043,26 @@ function peg$parse(input, options) {
s7 = peg$parsenumber();
if (s7 !== peg$FAILED) {
s8 = peg$parsews();
s9 = peg$parsecomma();
if (s9 === peg$FAILED) {
s9 = null;
}
s10 = peg$parsews();
s11 = peg$parsenumber();
if (s11 === peg$FAILED) {
s11 = null;
}
s12 = peg$parsews();
if (input.charCodeAt(peg$currPos) === 41) {
s9 = peg$c17;
s13 = peg$c17;
peg$currPos++;
} else {
s9 = peg$FAILED;
s13 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$e24); }
}
if (s9 !== peg$FAILED) {
if (s13 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f6(s3, s7);
s0 = peg$f6(s3, s7, s11);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@ -1383,7 +1393,7 @@ function peg$parse(input, options) {
}
function peg$parsebjorklund() {
var s0, s1, s2, s3, s4, s5;
var s0, s1, s2, s3, s4, s5, s6, s7;
s0 = peg$currPos;
if (input.substr(peg$currPos, 6) === peg$c23) {
@ -1400,8 +1410,13 @@ function peg$parse(input, options) {
s4 = peg$parsews();
s5 = peg$parseint();
if (s5 !== peg$FAILED) {
s6 = peg$parsews();
s7 = peg$parseint();
if (s7 === peg$FAILED) {
s7 = null;
}
peg$savedPos = s0;
s0 = peg$f17(s3, s5);
s0 = peg$f17(s3, s5, s7);
} else {
peg$currPos = s0;
s0 = peg$FAILED;

View File

@ -31,3 +31,4 @@ export const getPlayableNoteValue = (event) => {
}
return note;
};
export const rotate = (arr, n) => arr.slice(n).concat(arr.slice(0, n));

View File

@ -0,0 +1,41 @@
function bjorklund(slots, pulses){
var pattern = [],
count = [],
remainder = [pulses],
divisor = slots - pulses,
level = 0,
build_pattern = function(lv){
if( lv == -1 ){ pattern.push(0); }
else if( lv == -2 ){ pattern.push(1); }
else {
for(var x=0; x<count[lv]; x++){
build_pattern(lv-1);
}
if(remainder[lv]){
build_pattern(lv-2);
}
}
}
;
while(remainder[level] > 1){
count.push(Math.floor(divisor/remainder[level]));
remainder.push(divisor%remainder[level]);
divisor = remainder[level];
level++;
}
count.push(divisor);
build_pattern(level);
return pattern.reverse();
}
var bjork = function(m, k){
if(m > k) return bjorklund(m, k);
else return bjorklund(k, m);
};
export default bjork;

View File

@ -2,6 +2,7 @@
"imports": {
"@tonaljs/tonal": "./@tonaljs/tonal.js",
"@tonejs/piano": "./@tonejs/piano.js",
"bjork": "./bjork.js",
"chord-voicings": "./chord-voicings.js",
"codemirror/lib/codemirror.css": "./codemirror/lib/codemirror.css",
"codemirror/mode/javascript/javascript.js": "./codemirror/mode/javascript/javascript.js",

14
docs/dist/euclid.js vendored Normal file
View File

@ -0,0 +1,14 @@
import {Pattern} from "../_snowpack/link/strudel.js";
import bjork from "../_snowpack/pkg/bjork.js";
import {rotate} from "../_snowpack/link/util.js";
const euclid = (pulses, steps, rotation = 0) => {
const b = bjork(steps, pulses);
if (rotation) {
return rotate(b, -rotation);
}
return b;
};
Pattern.prototype.euclid = function(pulses, steps, rotation = 0) {
return this.struct(euclid(pulses, steps, rotation));
};
export default euclid;

View File

@ -5,7 +5,8 @@ import "./voicings.js";
import "./tonal.js";
import "./xen.js";
import "./tune.js";
import "./tune.js";
import "./euclid.js";
import euclid from "./euclid.js";
import "./pianoroll.js";
import "./draw.js";
import * as uiHelpers from "./ui.js";
@ -28,7 +29,7 @@ function hackLiteral(literal, names, func) {
}
hackLiteral(String, ["mini", "m"], bootstrapped.mini);
hackLiteral(String, ["pure", "p"], bootstrapped.pure);
Object.assign(globalThis, bootstrapped, Tone, toneHelpers, voicingHelpers, drawHelpers, uiHelpers, {gist});
Object.assign(globalThis, bootstrapped, Tone, toneHelpers, voicingHelpers, drawHelpers, uiHelpers, {gist, euclid});
export const evaluate = async (code) => {
const shapeshifted = shapeshifter(code);
drawHelpers.cleanup();

2
docs/dist/parse.js vendored
View File

@ -12,6 +12,8 @@ const applyOptions = (parent) => (pat, i) => {
case "stretch":
const speed = new Fraction(operator.arguments_.amount).inverse().valueOf();
return reify(pat).fast(speed);
case "bjorklund":
return pat.euclid(operator.arguments_.pulse, operator.arguments_.step, operator.arguments_.rotation);
}
console.warn(`operator "${operator.type_}" not implemented`);
}

27
docs/dist/tunes.js vendored
View File

@ -515,3 +515,30 @@ stack(
.fast(2 / 3)
.tone(p.toDestination())
)`;
export const festivalOfFingers = `const chords = "<Cm7 Fm7 G7 F#7>";
piano().then(p=>stack(
chords.voicings().struct("x(3,8,-1)").velocity(.5).off(1/7,x=>x.transpose(12).velocity(.2)),
chords.rootNotes(2).struct("x(4,8,-2)"),
chords.rootNotes(4)
.scale(slowcat('C minor','F dorian','G dorian','F# mixolydian'))
.struct("x(3,8,-2)".fast(2))
.scaleTranspose("0 4 0 6".early(".125 .5")).layer(scaleTranspose("0,<2 [4,6] [5,7]>/4"))
).slow(2)
//.pianoroll()
.velocity(sine.struct("x*8").add(3/5).mul(2/5).fast(8))
.tone(p.chain(out())))`;
export const festivalOfFingers2 = `const chords = "<Cm7 Fm7 G7 F#7 >";
const scales = slowcat('C minor','F dorian','G dorian','F# mixolydian')
piano().then(p=>stack(
chords.voicings().struct("x(3,8,-1)").velocity(.5).off(1/7,x=>x.transpose(12).velocity(.2)),
chords.rootNotes(2).struct("x(4,8)"),
chords.rootNotes(4)
.scale(scales)
.struct("x(3,8,-2)".fast(2))
.scaleTranspose("0 4 0 6".early(".125 .5")).layer(scaleTranspose("0,<2 [4,6] [5,7]>/3"))
).slow(2).transpose(-1)
.legato(cosine.struct("x*8").add(4/5).mul(4/5).fast(8))
.velocity(sine.struct("x*8").add(3/5).mul(2/5).fast(8))
// .pianoroll()
.tone(p.chain(out())).fast(3/4)
)`;

View File

@ -26,7 +26,7 @@ Pattern.prototype.voicings = function(range) {
};
Pattern.prototype.rootNotes = function(octave = 2) {
return this.fmap((value) => {
const [_, root] = value.match(/^([a-gA-G])[b#]?.*$/);
const [_, root] = value.match(/^([a-gA-G][b#]?).*$/);
return root + octave;
});
};

View File

@ -7439,21 +7439,65 @@ You can also use decimal numbers for any tempo you like.`), /*#__PURE__*/ _react
}, `[x*n]@n`), `.`), /*#__PURE__*/ _react1.mdx("h2", null, `Mini Notation TODO`), /*#__PURE__*/ _react1.mdx("p", null, `Compared to `, /*#__PURE__*/ _react1.mdx("a", {
parentName: "p",
"href": "https://tidalcycles.org/docs/patternlib/tutorials/mini_notation/"
}, `tidal mini notation`), `, the following mini notation features are missing from Strudel:`), /*#__PURE__*/ _react1.mdx("ul", null, /*#__PURE__*/ _react1.mdx("li", {
parentName: "ul"
}, `Tie symbols "`, `_`, `"`), /*#__PURE__*/ _react1.mdx("li", {
parentName: "ul"
}, `Euclidean algorithm "c3(3,2,1)"`), /*#__PURE__*/ _react1.mdx("li", {
parentName: "ul"
}, `feet marking "."`), /*#__PURE__*/ _react1.mdx("li", {
parentName: "ul"
}, `random choice "|"`), /*#__PURE__*/ _react1.mdx("li", {
parentName: "ul"
}, `Random removal "?"`), /*#__PURE__*/ _react1.mdx("li", {
parentName: "ul"
}, `Polymetric sequences "{ ... }"`), /*#__PURE__*/ _react1.mdx("li", {
parentName: "ul"
}, `Fixed steps using "%"`)), /*#__PURE__*/ _react1.mdx("br", null), /*#__PURE__*/ _react1.mdx("h1", null, `Core API`), /*#__PURE__*/ _react1.mdx("p", null, `While the mini notation is powerful on its own, there is much more to discover.
}, `tidal mini notation`), `, the following mini notation features are missing from Strudel:`), /*#__PURE__*/ _react1.mdx("ul", {
"className": "contains-task-list"
}, /*#__PURE__*/ _react1.mdx("li", {
parentName: "ul",
"className": "task-list-item"
}, /*#__PURE__*/ _react1.mdx("input", {
parentName: "li",
"type": "checkbox",
"checked": true,
"disabled": true
}), ` `, `Euclidean algorithm "c3(3,2,1)" TODO: document`), /*#__PURE__*/ _react1.mdx("li", {
parentName: "ul",
"className": "task-list-item"
}, /*#__PURE__*/ _react1.mdx("input", {
parentName: "li",
"type": "checkbox",
"checked": false,
"disabled": true
}), ` `, `Tie symbols "`, `_`, `"`), /*#__PURE__*/ _react1.mdx("li", {
parentName: "ul",
"className": "task-list-item"
}, /*#__PURE__*/ _react1.mdx("input", {
parentName: "li",
"type": "checkbox",
"checked": false,
"disabled": true
}), ` `, `feet marking "."`), /*#__PURE__*/ _react1.mdx("li", {
parentName: "ul",
"className": "task-list-item"
}, /*#__PURE__*/ _react1.mdx("input", {
parentName: "li",
"type": "checkbox",
"checked": false,
"disabled": true
}), ` `, `random choice "|"`), /*#__PURE__*/ _react1.mdx("li", {
parentName: "ul",
"className": "task-list-item"
}, /*#__PURE__*/ _react1.mdx("input", {
parentName: "li",
"type": "checkbox",
"checked": false,
"disabled": true
}), ` `, `Random removal "?"`), /*#__PURE__*/ _react1.mdx("li", {
parentName: "ul",
"className": "task-list-item"
}, /*#__PURE__*/ _react1.mdx("input", {
parentName: "li",
"type": "checkbox",
"checked": false,
"disabled": true
}), ` `, `Polymetric sequences "{ ... }"`), /*#__PURE__*/ _react1.mdx("li", {
parentName: "ul",
"className": "task-list-item"
}, /*#__PURE__*/ _react1.mdx("input", {
parentName: "li",
"type": "checkbox",
"checked": false,
"disabled": true
}), ` `, `Fixed steps using "%"`)), /*#__PURE__*/ _react1.mdx("br", null), /*#__PURE__*/ _react1.mdx("h1", null, `Core API`), /*#__PURE__*/ _react1.mdx("p", null, `While the mini notation is powerful on its own, there is much more to discover.
Internally, the mini notation will expand to use the actual functional JavaScript API.`), /*#__PURE__*/ _react1.mdx("h2", null, `Notes`), /*#__PURE__*/ _react1.mdx("p", null, `Notes are automatically available as variables:`), /*#__PURE__*/ _react1.mdx(_miniReplDefault.default, {
tune: `e4`,
mdxType: "MiniRepl"
@ -41870,6 +41914,8 @@ parcelHelpers.export(exports, "mod", ()=>mod
);
parcelHelpers.export(exports, "getPlayableNoteValue", ()=>getPlayableNoteValue
);
parcelHelpers.export(exports, "rotate", ()=>rotate
);
const isNote = (name)=>/^[a-gA-G][#b]*[0-9]$/.test(name)
;
const tokenizeNote = (note)=>{
@ -41913,6 +41959,8 @@ const getPlayableNoteValue = (event)=>{
else if (typeof note === 'string' && !isNote(note)) throw new Error('not a note: ' + note);
return note;
};
const rotate = (arr, n)=>arr.slice(n).concat(arr.slice(0, n))
;
},{"@parcel/transformer-js/src/esmodule-helpers.js":"gkKU3"}],"80V3z":[function(require,module,exports) {
var parcelHelpers = require("@parcel/transformer-js/src/esmodule-helpers.js");
@ -41926,6 +41974,8 @@ var _voicings = require("./voicings");
var _tonalMjs = require("./tonal.mjs");
var _xenMjs = require("./xen.mjs");
var _tuneMjs = require("./tune.mjs");
var _euclidMjs = require("./euclid.mjs");
var _euclidMjsDefault = parcelHelpers.interopDefault(_euclidMjs);
var _pianorollMjs = require("./pianoroll.mjs");
var _drawMjs = require("./draw.mjs");
var _uiMjs = require("./ui.mjs");
@ -41961,7 +42011,8 @@ hackLiteral(String, [
], bootstrapped.pure); // comment out this line if you panic
// this will add everything to global scope, which is accessed by eval
Object.assign(globalThis, bootstrapped, _tone1, _tone, _voicings, _drawMjs, _uiMjs, {
gist: _gistJsDefault.default
gist: _gistJsDefault.default,
euclid: _euclidMjsDefault.default
});
const evaluate = async (code)=>{
const shapeshifted = _shapeshifterDefault.default(code); // transform syntactically correct js code to semantically usable code
@ -41985,7 +42036,7 @@ const evaluate = async (code)=>{
};
};
},{"../../strudel.mjs":"ggZqJ","./tone":"aBpVm","./midi":"kToux","./voicings":"bexb4","./tonal.mjs":"j8DFK","./xen.mjs":"2MdCD","./tune.mjs":"ggPHE","./pianoroll.mjs":"6BZJ6","./draw.mjs":"dHJhg","./ui.mjs":"dezzj","./gist.js":"fYZxP","./shapeshifter":"67UCx","./parse":"dddDq","tone":"2tCfN","@parcel/transformer-js/src/esmodule-helpers.js":"gkKU3"}],"ggZqJ":[function(require,module,exports) {
},{"../../strudel.mjs":"ggZqJ","./tone":"aBpVm","./midi":"kToux","./voicings":"bexb4","./tonal.mjs":"j8DFK","./xen.mjs":"2MdCD","./tune.mjs":"ggPHE","./euclid.mjs":"cR2iT","./pianoroll.mjs":"6BZJ6","./draw.mjs":"dHJhg","./ui.mjs":"dezzj","./gist.js":"fYZxP","./shapeshifter":"67UCx","./parse":"dddDq","tone":"2tCfN","@parcel/transformer-js/src/esmodule-helpers.js":"gkKU3"}],"ggZqJ":[function(require,module,exports) {
var parcelHelpers = require("@parcel/transformer-js/src/esmodule-helpers.js");
parcelHelpers.defineInteropFlag(exports);
parcelHelpers.export(exports, "curry", ()=>curry
@ -59636,7 +59687,7 @@ Pattern.prototype.voicings = function(range) {
Pattern.prototype.rootNotes = function(octave = 2) {
// range = ['G1', 'C3']
return this.fmap((value)=>{
const [_, root] = value.match(/^([a-gA-G])[b#]?.*$/);
const [_, root] = value.match(/^([a-gA-G][b#]?).*$/);
return root + octave;
});
};
@ -136200,7 +136251,51 @@ Tune.prototype.isValidScale = function(name) {
}
};
},{"@parcel/transformer-js/src/esmodule-helpers.js":"gkKU3"}],"6BZJ6":[function(require,module,exports) {
},{"@parcel/transformer-js/src/esmodule-helpers.js":"gkKU3"}],"cR2iT":[function(require,module,exports) {
var parcelHelpers = require("@parcel/transformer-js/src/esmodule-helpers.js");
parcelHelpers.defineInteropFlag(exports);
var _strudelMjs = require("../../strudel.mjs");
var _bjork = require("bjork");
var _bjorkDefault = parcelHelpers.interopDefault(_bjork);
var _utilMjs = require("../../util.mjs");
const euclid = (pulses, steps, rotation = 0)=>{
const b = _bjorkDefault.default(steps, pulses);
if (rotation) return _utilMjs.rotate(b, -rotation);
return b;
};
_strudelMjs.Pattern.prototype.euclid = function(pulses, steps, rotation = 0) {
return this.struct(euclid(pulses, steps, rotation));
};
exports.default = euclid;
},{"../../strudel.mjs":"ggZqJ","bjork":"hTZTb","../../util.mjs":"9Z602","@parcel/transformer-js/src/esmodule-helpers.js":"gkKU3"}],"hTZTb":[function(require,module,exports) {
function bjorklund(slots, pulses) {
var pattern = [], count = [], remainder = [
pulses
], divisor = slots - pulses, level = 0, build_pattern = function(lv) {
if (lv == -1) pattern.push(0);
else if (lv == -2) pattern.push(1);
else {
for(var x = 0; x < count[lv]; x++)build_pattern(lv - 1);
if (remainder[lv]) build_pattern(lv - 2);
}
};
while(remainder[level] > 1){
count.push(Math.floor(divisor / remainder[level]));
remainder.push(divisor % remainder[level]);
divisor = remainder[level];
level++;
}
count.push(divisor);
build_pattern(level);
return pattern.reverse();
}
module.exports = function(m, k) {
if (m > k) return bjorklund(m, k);
else return bjorklund(k, m);
};
},{}],"6BZJ6":[function(require,module,exports) {
var _strudelMjs = require("../../strudel.mjs");
_strudelMjs.Pattern.prototype.pianoroll = function({ timeframe =10 , inactive ='#C9E597' , active ='#FFCA28' , background ='#2A3236' , maxMidi =90 , minMidi =0 , } = {
}) {
@ -169689,6 +169784,8 @@ const applyOptions = (parent)=>(pat, i)=>{
case 'stretch':
const speed = new Fraction(operator.arguments_.amount).inverse().valueOf();
return reify(pat).fast(speed);
case 'bjorklund':
return pat.euclid(operator.arguments_.pulse, operator.arguments_.step, operator.arguments_.rotation);
}
console.warn(`operator "${operator.type_}" not implemented`);
}
@ -170115,13 +170212,14 @@ function peg$parse(input, options1) {
replicate: a
};
};
var peg$f6 = function(p, s) {
var peg$f6 = function(p, s, r) {
return {
operator: {
type_: "bjorklund",
arguments_: {
pulse: p,
step: s
step: s,
rotation: r || 0
}
}
};
@ -170191,7 +170289,7 @@ function peg$parse(input, options1) {
}
};
};
var peg$f17 = function(p, s) {
var peg$f17 = function(p, s, r) {
return {
name: "bjorklund",
args: {
@ -170899,7 +170997,7 @@ function peg$parse(input, options1) {
return s0;
}
function peg$parseslice_bjorklund() {
var s0, s1, s2, s3, s4, s5, s6, s7, s8, s9;
var s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13;
s0 = peg$currPos;
if (input.charCodeAt(peg$currPos) === 40) {
s1 = peg$c16;
@ -170919,16 +171017,22 @@ function peg$parse(input, options1) {
s7 = peg$parsenumber();
if (s7 !== peg$FAILED) {
s8 = peg$parsews();
s9 = peg$parsecomma();
if (s9 === peg$FAILED) s9 = null;
s10 = peg$parsews();
s11 = peg$parsenumber();
if (s11 === peg$FAILED) s11 = null;
s12 = peg$parsews();
if (input.charCodeAt(peg$currPos) === 41) {
s9 = peg$c17;
s13 = peg$c17;
peg$currPos++;
} else {
s9 = peg$FAILED;
s13 = peg$FAILED;
if (peg$silentFails === 0) peg$fail(peg$e24);
}
if (s9 !== peg$FAILED) {
if (s13 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f6(s3, s7);
s0 = peg$f6(s3, s7, s11);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@ -171220,7 +171324,7 @@ function peg$parse(input, options1) {
return s0;
}
function peg$parsebjorklund() {
var s0, s1, s2, s3, s4, s5;
var s0, s1, s2, s3, s4, s5, s6, s7;
s0 = peg$currPos;
if (input.substr(peg$currPos, 6) === peg$c23) {
s1 = peg$c23;
@ -171236,8 +171340,11 @@ function peg$parse(input, options1) {
s4 = peg$parsews();
s5 = peg$parseint();
if (s5 !== peg$FAILED) {
s6 = peg$parsews();
s7 = peg$parseint();
if (s7 === peg$FAILED) s7 = null;
peg$savedPos = s0;
s0 = peg$f17(s3, s5);
s0 = peg$f17(s3, s5, s7);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@ -183164,4 +183271,4 @@ exports.default = cx;
},{"@parcel/transformer-js/src/esmodule-helpers.js":"gkKU3"}]},["3uVTb"], "3uVTb", "parcelRequire94c2")
//# sourceMappingURL=index.5d784197.js.map
//# sourceMappingURL=index.86c7b8fe.js.map

File diff suppressed because one or more lines are too long

View File

@ -11,6 +11,6 @@
<body>
<div id="root"></div>
<noscript>You need to enable JavaScript to run this app.</noscript>
<script src="/tutorial/index.5d784197.js" defer=""></script>
<script src="/tutorial/index.86c7b8fe.js" defer=""></script>
</body>
</html>