add euclid

This commit is contained in:
Felix Roos 2022-03-17 15:03:57 +01:00
parent eaa250a962
commit 91c0a46ba9
10 changed files with 100 additions and 21 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

@ -104,8 +104,8 @@ slice_weight = "@" a:number
slice_replicate = "!"a:number
{ return { replicate: a } }
slice_bjorklund = "(" ws p:number ws comma ws s:number ws")"
{ return { operator : { type_: "bjorklund", arguments_ :{ pulse: p, step:s } } } }
slice_bjorklund = "(" ws p:number ws comma ws s:number ws comma? ws r:number? ws ")"
{ return { operator : { type_: "bjorklund", arguments_ :{ pulse: p, step:s, rotation:r || 0 } } } }
slice_slow = "/"a:number
{ return { operator : { type_: "stretch", arguments_ :{ amount:a } } } }
@ -145,7 +145,7 @@ struct = "struct" ws s:sequence_or_operator
target = "target" ws quote s:step quote
{ return { name: "target", args : { name:s}}}
bjorklund = "euclid" ws p:int ws s:int
bjorklund = "euclid" ws p:int ws s:int ws r:int?
{ return { name: "bjorklund", args :{ pulse: parseInt(p), step:parseInt(s) }}}
slow = "slow" ws a:number

11
repl/package-lock.json generated
View File

@ -7,6 +7,7 @@
"dependencies": {
"@tonaljs/tonal": "^4.6.5",
"@tonejs/piano": "^0.2.1",
"bjork": "^0.0.1",
"chord-voicings": "^0.0.1",
"codemirror": "^5.65.1",
"estraverse": "^5.3.0",
@ -4195,6 +4196,11 @@
"node": ">=8"
}
},
"node_modules/bjork": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/bjork/-/bjork-0.0.1.tgz",
"integrity": "sha1-br1a3pkWSwvgMIeI1kaRQ9XJrZw="
},
"node_modules/bl": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
@ -15511,6 +15517,11 @@
"integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
"dev": true
},
"bjork": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/bjork/-/bjork-0.0.1.tgz",
"integrity": "sha1-br1a3pkWSwvgMIeI1kaRQ9XJrZw="
},
"bl": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",

View File

@ -13,6 +13,7 @@
"dependencies": {
"@tonaljs/tonal": "^4.6.5",
"@tonejs/piano": "^0.2.1",
"bjork": "^0.0.1",
"chord-voicings": "^0.0.1",
"codemirror": "^5.65.1",
"estraverse": "^5.3.0",

17
repl/src/euclid.mjs Normal file
View File

@ -0,0 +1,17 @@
import { Pattern } from '../../strudel.mjs';
import bjork from 'bjork';
import { rotate } from '../../util.mjs';
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';
import './tonal.mjs';
import './xen.mjs';
import './tune.mjs';
import './tune.mjs';
import './euclid.mjs';
import euclid from './euclid.mjs';
import './pianoroll.mjs';
import './draw.mjs';
import * as uiHelpers from './ui.mjs';
@ -36,7 +37,7 @@ hackLiteral(String, ['mini', 'm'], bootstrapped.mini); // comment out this line
hackLiteral(String, ['pure', 'p'], 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, Tone, toneHelpers, voicingHelpers, drawHelpers, uiHelpers, { gist });
Object.assign(globalThis, bootstrapped, Tone, toneHelpers, voicingHelpers, drawHelpers, uiHelpers, { gist, euclid });
export const evaluate: any = async (code: string) => {
const shapeshifted = shapeshifter(code); // transform syntactically correct js code to semantically usable code

View File

@ -14,6 +14,8 @@ const applyOptions = (parent: any) => (pat: any, i: number) => {
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);
// TODO: case 'fixed-step': "%"
}
console.warn(`operator "${operator.type_}" not implemented`);

View File

@ -543,3 +543,32 @@ 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

@ -180,13 +180,13 @@ In essence, the `x!n` is like a shortcut for `[x*n]@n`.
Compared to [tidal mini notation](https://tidalcycles.org/docs/patternlib/tutorials/mini_notation/), the following mini notation features are missing from Strudel:
- Tie symbols "\_"
- Euclidean algorithm "c3(3,2,1)"
- feet marking "."
- random choice "|"
- Random removal "?"
- Polymetric sequences "{ ... }"
- Fixed steps using "%"
- [x] Euclidean algorithm "c3(3,2,1)" TODO: document
- [ ] Tie symbols "\_"
- [ ] feet marking "."
- [ ] random choice "|"
- [ ] Random removal "?"
- [ ] Polymetric sequences "{ ... }"
- [ ] Fixed steps using "%"
<br />

View File

@ -39,3 +39,6 @@ export const getPlayableNoteValue = (event) => {
}
return note;
};
// rotate array by n steps (to the left)
export const rotate = (arr, n) => arr.slice(n).concat(arr.slice(0, n));