Support for multiple mininotation operators WIP (#350)
* support for multiple operators * fix false test failures by sorting by part * snapshot
This commit is contained in:
parent
45842b1f05
commit
f57fe182ed
6 changed files with 503 additions and 493 deletions
|
|
@ -274,13 +274,20 @@ function peg$parse(input, options) {
|
||||||
var peg$f3 = function(s, stepsPerCycle) { s.arguments_.stepsPerCycle = stepsPerCycle ; return s; };
|
var peg$f3 = function(s, stepsPerCycle) { s.arguments_.stepsPerCycle = stepsPerCycle ; return s; };
|
||||||
var peg$f4 = function(a) { return a };
|
var peg$f4 = function(a) { return a };
|
||||||
var peg$f5 = function(s) { s.arguments_.alignment = 'slowcat'; return s; };
|
var peg$f5 = function(s) { s.arguments_.alignment = 'slowcat'; return s; };
|
||||||
var peg$f6 = function(a) { return { weight: a} };
|
var peg$f6 = function(a) { return x => x.options_['weight'] = a };
|
||||||
var peg$f7 = function(a) { return { replicate: a } };
|
var peg$f7 = function(a) { return x => x.options_['reps'] = a };
|
||||||
var peg$f8 = function(p, s, r) { return { operator : { type_: "bjorklund", arguments_ :{ pulse: p, step:s, rotation:r } } } };
|
var peg$f8 = function(p, s, r) { return x => x.options_['ops'].push({ type_: "bjorklund", arguments_ :{ pulse: p, step:s, rotation:r }}) };
|
||||||
var peg$f9 = function(a) { return { operator : { type_: "stretch", arguments_ :{ amount:a, type: 'slow' } } } };
|
var peg$f9 = function(a) { return x => x.options_['ops'].push({ type_: "stretch", arguments_ :{ amount:a, type: 'slow' }}) };
|
||||||
var peg$f10 = function(a) { return { operator : { type_: "stretch", arguments_ :{ amount:a, type: 'fast' } } } };
|
var peg$f10 = function(a) { return x => x.options_['ops'].push({ type_: "stretch", arguments_ :{ amount:a, type: 'fast' }}) };
|
||||||
var peg$f11 = function(a) { return { operator : { type_: "degradeBy", arguments_ :{ amount:a } } } };
|
var peg$f11 = function(a) { return x => x.options_['ops'].push({ type_: "degradeBy", arguments_ :{ amount:a } }) };
|
||||||
var peg$f12 = function(s, o) { return new ElementStub(s, o);};
|
var peg$f12 = function(s, ops) { const result = new ElementStub(s, {ops: [], weight: 1, reps: 1});
|
||||||
|
for (const op of ops) {
|
||||||
|
console.log("hmm");
|
||||||
|
op(result);
|
||||||
|
console.log("ha");
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
};
|
||||||
var peg$f13 = function(s) { return new PatternStub(s, 'fastcat'); };
|
var peg$f13 = function(s) { return new PatternStub(s, 'fastcat'); };
|
||||||
var peg$f14 = function(tail) { return { alignment: 'stack', list: tail }; };
|
var peg$f14 = function(tail) { return { alignment: 'stack', list: tail }; };
|
||||||
var peg$f15 = function(tail) { return { alignment: 'rand', list: tail }; };
|
var peg$f15 = function(tail) { return { alignment: 'rand', list: tail }; };
|
||||||
|
|
@ -1069,20 +1076,20 @@ function peg$parse(input, options) {
|
||||||
return s0;
|
return s0;
|
||||||
}
|
}
|
||||||
|
|
||||||
function peg$parseslice_modifier() {
|
function peg$parseslice_op() {
|
||||||
var s0;
|
var s0;
|
||||||
|
|
||||||
s0 = peg$parseslice_weight();
|
s0 = peg$parseop_weight();
|
||||||
if (s0 === peg$FAILED) {
|
if (s0 === peg$FAILED) {
|
||||||
s0 = peg$parseslice_bjorklund();
|
s0 = peg$parseop_bjorklund();
|
||||||
if (s0 === peg$FAILED) {
|
if (s0 === peg$FAILED) {
|
||||||
s0 = peg$parseslice_slow();
|
s0 = peg$parseop_slow();
|
||||||
if (s0 === peg$FAILED) {
|
if (s0 === peg$FAILED) {
|
||||||
s0 = peg$parseslice_fast();
|
s0 = peg$parseop_fast();
|
||||||
if (s0 === peg$FAILED) {
|
if (s0 === peg$FAILED) {
|
||||||
s0 = peg$parseslice_replicate();
|
s0 = peg$parseop_replicate();
|
||||||
if (s0 === peg$FAILED) {
|
if (s0 === peg$FAILED) {
|
||||||
s0 = peg$parseslice_degrade();
|
s0 = peg$parseop_degrade();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1092,7 +1099,7 @@ function peg$parse(input, options) {
|
||||||
return s0;
|
return s0;
|
||||||
}
|
}
|
||||||
|
|
||||||
function peg$parseslice_weight() {
|
function peg$parseop_weight() {
|
||||||
var s0, s1, s2;
|
var s0, s1, s2;
|
||||||
|
|
||||||
s0 = peg$currPos;
|
s0 = peg$currPos;
|
||||||
|
|
@ -1120,7 +1127,7 @@ function peg$parse(input, options) {
|
||||||
return s0;
|
return s0;
|
||||||
}
|
}
|
||||||
|
|
||||||
function peg$parseslice_replicate() {
|
function peg$parseop_replicate() {
|
||||||
var s0, s1, s2;
|
var s0, s1, s2;
|
||||||
|
|
||||||
s0 = peg$currPos;
|
s0 = peg$currPos;
|
||||||
|
|
@ -1148,7 +1155,7 @@ function peg$parse(input, options) {
|
||||||
return s0;
|
return s0;
|
||||||
}
|
}
|
||||||
|
|
||||||
function peg$parseslice_bjorklund() {
|
function peg$parseop_bjorklund() {
|
||||||
var s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13;
|
var s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13;
|
||||||
|
|
||||||
s0 = peg$currPos;
|
s0 = peg$currPos;
|
||||||
|
|
@ -1161,13 +1168,13 @@ function peg$parse(input, options) {
|
||||||
}
|
}
|
||||||
if (s1 !== peg$FAILED) {
|
if (s1 !== peg$FAILED) {
|
||||||
s2 = peg$parsews();
|
s2 = peg$parsews();
|
||||||
s3 = peg$parseslice_with_modifier();
|
s3 = peg$parseslice_with_ops();
|
||||||
if (s3 !== peg$FAILED) {
|
if (s3 !== peg$FAILED) {
|
||||||
s4 = peg$parsews();
|
s4 = peg$parsews();
|
||||||
s5 = peg$parsecomma();
|
s5 = peg$parsecomma();
|
||||||
if (s5 !== peg$FAILED) {
|
if (s5 !== peg$FAILED) {
|
||||||
s6 = peg$parsews();
|
s6 = peg$parsews();
|
||||||
s7 = peg$parseslice_with_modifier();
|
s7 = peg$parseslice_with_ops();
|
||||||
if (s7 !== peg$FAILED) {
|
if (s7 !== peg$FAILED) {
|
||||||
s8 = peg$parsews();
|
s8 = peg$parsews();
|
||||||
s9 = peg$parsecomma();
|
s9 = peg$parsecomma();
|
||||||
|
|
@ -1175,7 +1182,7 @@ function peg$parse(input, options) {
|
||||||
s9 = null;
|
s9 = null;
|
||||||
}
|
}
|
||||||
s10 = peg$parsews();
|
s10 = peg$parsews();
|
||||||
s11 = peg$parseslice_with_modifier();
|
s11 = peg$parseslice_with_ops();
|
||||||
if (s11 === peg$FAILED) {
|
if (s11 === peg$FAILED) {
|
||||||
s11 = null;
|
s11 = null;
|
||||||
}
|
}
|
||||||
|
|
@ -1214,7 +1221,7 @@ function peg$parse(input, options) {
|
||||||
return s0;
|
return s0;
|
||||||
}
|
}
|
||||||
|
|
||||||
function peg$parseslice_slow() {
|
function peg$parseop_slow() {
|
||||||
var s0, s1, s2;
|
var s0, s1, s2;
|
||||||
|
|
||||||
s0 = peg$currPos;
|
s0 = peg$currPos;
|
||||||
|
|
@ -1242,7 +1249,7 @@ function peg$parse(input, options) {
|
||||||
return s0;
|
return s0;
|
||||||
}
|
}
|
||||||
|
|
||||||
function peg$parseslice_fast() {
|
function peg$parseop_fast() {
|
||||||
var s0, s1, s2;
|
var s0, s1, s2;
|
||||||
|
|
||||||
s0 = peg$currPos;
|
s0 = peg$currPos;
|
||||||
|
|
@ -1270,7 +1277,7 @@ function peg$parse(input, options) {
|
||||||
return s0;
|
return s0;
|
||||||
}
|
}
|
||||||
|
|
||||||
function peg$parseslice_degrade() {
|
function peg$parseop_degrade() {
|
||||||
var s0, s1, s2;
|
var s0, s1, s2;
|
||||||
|
|
||||||
s0 = peg$currPos;
|
s0 = peg$currPos;
|
||||||
|
|
@ -1296,15 +1303,17 @@ function peg$parse(input, options) {
|
||||||
return s0;
|
return s0;
|
||||||
}
|
}
|
||||||
|
|
||||||
function peg$parseslice_with_modifier() {
|
function peg$parseslice_with_ops() {
|
||||||
var s0, s1, s2;
|
var s0, s1, s2, s3;
|
||||||
|
|
||||||
s0 = peg$currPos;
|
s0 = peg$currPos;
|
||||||
s1 = peg$parseslice();
|
s1 = peg$parseslice();
|
||||||
if (s1 !== peg$FAILED) {
|
if (s1 !== peg$FAILED) {
|
||||||
s2 = peg$parseslice_modifier();
|
s2 = [];
|
||||||
if (s2 === peg$FAILED) {
|
s3 = peg$parseslice_op();
|
||||||
s2 = null;
|
while (s3 !== peg$FAILED) {
|
||||||
|
s2.push(s3);
|
||||||
|
s3 = peg$parseslice_op();
|
||||||
}
|
}
|
||||||
peg$savedPos = s0;
|
peg$savedPos = s0;
|
||||||
s0 = peg$f12(s1, s2);
|
s0 = peg$f12(s1, s2);
|
||||||
|
|
@ -1321,11 +1330,11 @@ function peg$parse(input, options) {
|
||||||
|
|
||||||
s0 = peg$currPos;
|
s0 = peg$currPos;
|
||||||
s1 = [];
|
s1 = [];
|
||||||
s2 = peg$parseslice_with_modifier();
|
s2 = peg$parseslice_with_ops();
|
||||||
if (s2 !== peg$FAILED) {
|
if (s2 !== peg$FAILED) {
|
||||||
while (s2 !== peg$FAILED) {
|
while (s2 !== peg$FAILED) {
|
||||||
s1.push(s2);
|
s1.push(s2);
|
||||||
s2 = peg$parseslice_with_modifier();
|
s2 = peg$parseslice_with_ops();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
s1 = peg$FAILED;
|
s1 = peg$FAILED;
|
||||||
|
|
|
||||||
|
|
@ -119,32 +119,37 @@ slice = step / sub_cycle / polymeter / slow_sequence
|
||||||
|
|
||||||
// slice modifier affects the timing/size of a slice (e.g. [a b c]@3)
|
// slice modifier affects the timing/size of a slice (e.g. [a b c]@3)
|
||||||
// at this point, we assume we can represent them as regular sequence operators
|
// at this point, we assume we can represent them as regular sequence operators
|
||||||
slice_modifier = slice_weight / slice_bjorklund / slice_slow / slice_fast / slice_replicate / slice_degrade
|
slice_op = op_weight / op_bjorklund / op_slow / op_fast / op_replicate / op_degrade
|
||||||
|
|
||||||
slice_weight = "@" a:number
|
op_weight = "@" a:number
|
||||||
{ return { weight: a} }
|
{ return x => x.options_['weight'] = a }
|
||||||
|
|
||||||
slice_replicate = "!"a:number
|
op_replicate = "!"a:number
|
||||||
{ return { replicate: a } }
|
{ return x => x.options_['reps'] = a }
|
||||||
|
|
||||||
slice_bjorklund = "(" ws p:slice_with_modifier ws comma ws s:slice_with_modifier ws comma? ws r:slice_with_modifier? ws ")"
|
op_bjorklund = "(" ws p:slice_with_ops ws comma ws s:slice_with_ops ws comma? ws r:slice_with_ops? ws ")"
|
||||||
{ return { operator : { type_: "bjorklund", arguments_ :{ pulse: p, step:s, rotation:r } } } }
|
{ return x => x.options_['ops'].push({ type_: "bjorklund", arguments_ :{ pulse: p, step:s, rotation:r }}) }
|
||||||
|
|
||||||
slice_slow = "/"a:slice
|
op_slow = "/"a:slice
|
||||||
{ return { operator : { type_: "stretch", arguments_ :{ amount:a, type: 'slow' } } } }
|
{ return x => x.options_['ops'].push({ type_: "stretch", arguments_ :{ amount:a, type: 'slow' }}) }
|
||||||
|
|
||||||
slice_fast = "*"a:slice
|
op_fast = "*"a:slice
|
||||||
{ return { operator : { type_: "stretch", arguments_ :{ amount:a, type: 'fast' } } } }
|
{ return x => x.options_['ops'].push({ type_: "stretch", arguments_ :{ amount:a, type: 'fast' }}) }
|
||||||
|
|
||||||
slice_degrade = "?"a:number?
|
op_degrade = "?"a:number?
|
||||||
{ return { operator : { type_: "degradeBy", arguments_ :{ amount:a } } } }
|
{ return x => x.options_['ops'].push({ type_: "degradeBy", arguments_ :{ amount:a } }) }
|
||||||
|
|
||||||
// a slice with an modifier applied i.e [bd@4 sd@3]@2 hh]
|
// a slice with an modifier applied i.e [bd@4 sd@3]@2 hh]
|
||||||
slice_with_modifier = s:slice o:slice_modifier?
|
slice_with_ops = s:slice ops:slice_op*
|
||||||
{ return new ElementStub(s, o);}
|
{ const result = new ElementStub(s, {ops: [], weight: 1, reps: 1});
|
||||||
|
for (const op of ops) {
|
||||||
|
op(result);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
// a sequence is a combination of one or more successive slices (as an array)
|
// a sequence is a combination of one or more successive slices (as an array)
|
||||||
sequence = s:(slice_with_modifier)+
|
sequence = s:(slice_with_ops)+
|
||||||
{ return new PatternStub(s, 'fastcat'); }
|
{ return new PatternStub(s, 'fastcat'); }
|
||||||
|
|
||||||
// a stack is a series of vertically aligned sequence, separated by a comma
|
// a stack is a series of vertically aligned sequence, separated by a comma
|
||||||
|
|
|
||||||
|
|
@ -17,78 +17,74 @@ function _nextSeed() {
|
||||||
const applyOptions = (parent, code) => (pat, i) => {
|
const applyOptions = (parent, code) => (pat, i) => {
|
||||||
const ast = parent.source_[i];
|
const ast = parent.source_[i];
|
||||||
const options = ast.options_;
|
const options = ast.options_;
|
||||||
const operator = options?.operator;
|
const ops = options?.ops;
|
||||||
if (operator) {
|
if (ops) {
|
||||||
switch (operator.type_) {
|
for (const op of ops) {
|
||||||
case 'stretch': {
|
switch (op.type_) {
|
||||||
const legalTypes = ['fast', 'slow'];
|
case 'stretch': {
|
||||||
const { type, amount } = operator.arguments_;
|
const legalTypes = ['fast', 'slow'];
|
||||||
if (!legalTypes.includes(type)) {
|
const { type, amount } = op.arguments_;
|
||||||
throw new Error(`mini: stretch: type must be one of ${legalTypes.join('|')} but got ${type}`);
|
if (!legalTypes.includes(type)) {
|
||||||
|
throw new Error(`mini: stretch: type must be one of ${legalTypes.join('|')} but got ${type}`);
|
||||||
|
}
|
||||||
|
pat = strudel.reify(pat)[type](patternifyAST(amount, code));
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return strudel.reify(pat)[type](patternifyAST(amount, code));
|
case 'bjorklund': {
|
||||||
}
|
if (op.arguments_.rotation) {
|
||||||
case 'bjorklund':
|
pat = pat.euclidRot(
|
||||||
if (operator.arguments_.rotation) {
|
patternifyAST(op.arguments_.pulse, code),
|
||||||
return pat.euclidRot(
|
patternifyAST(op.arguments_.step, code),
|
||||||
patternifyAST(operator.arguments_.pulse, code),
|
patternifyAST(op.arguments_.rotation, code),
|
||||||
patternifyAST(operator.arguments_.step, code),
|
);
|
||||||
patternifyAST(operator.arguments_.rotation, code),
|
} else {
|
||||||
);
|
pat = pat.euclid(patternifyAST(op.arguments_.pulse, code), patternifyAST(op.arguments_.step, code));
|
||||||
} else {
|
}
|
||||||
return pat.euclid(
|
break;
|
||||||
patternifyAST(operator.arguments_.pulse, code),
|
|
||||||
patternifyAST(operator.arguments_.step, code),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
case 'degradeBy':
|
case 'degradeBy': {
|
||||||
// TODO: find out what is right here
|
// TODO: find out what is right here
|
||||||
// example:
|
// example:
|
||||||
/*
|
/*
|
||||||
stack(
|
stack(
|
||||||
s("hh*8").degrade(),
|
s("hh*8").degrade(),
|
||||||
s("[ht*8]?")
|
s("[ht*8]?")
|
||||||
)
|
)
|
||||||
*/
|
*/
|
||||||
// above example will only be in sync when _degradeBy is used...
|
// above example will only be in sync when _degradeBy is used...
|
||||||
// it also seems that the nextSeed will create undeterministic behaviour
|
// it also seems that the nextSeed will create undeterministic behaviour
|
||||||
// as it uses a global _seedState. This is probably the reason for
|
// as it uses a global _seedState. This is probably the reason for
|
||||||
// https://github.com/tidalcycles/strudel/issues/245
|
// https://github.com/tidalcycles/strudel/issues/245
|
||||||
|
|
||||||
// this is how it was:
|
// this is how it was:
|
||||||
/*
|
/*
|
||||||
return strudel.reify(pat)._degradeByWith(
|
return strudel.reify(pat)._degradeByWith(
|
||||||
strudel.rand.early(randOffset * _nextSeed()).segment(1),
|
strudel.rand.early(randOffset * _nextSeed()).segment(1),
|
||||||
operator.arguments_.amount ?? 0.5,
|
op.arguments_.amount ?? 0.5,
|
||||||
);
|
);
|
||||||
*/
|
*/
|
||||||
return strudel.reify(pat).degradeBy(operator.arguments_.amount === null ? 0.5 : operator.arguments_.amount);
|
pat = strudel.reify(pat).degradeBy(op.arguments_.amount === null ? 0.5 : op.arguments_.amount);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
console.warn(`operator "${op.type_}" not implemented`);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
console.warn(`operator "${operator.type_}" not implemented`);
|
|
||||||
}
|
|
||||||
if (options?.weight) {
|
|
||||||
// weight is handled by parent
|
|
||||||
return pat;
|
|
||||||
}
|
|
||||||
// TODO: bjorklund e.g. "c3(5,8)"
|
|
||||||
const unimplemented = Object.keys(options || {}).filter((key) => key !== 'operator');
|
|
||||||
if (unimplemented.length) {
|
|
||||||
console.warn(
|
|
||||||
`option${unimplemented.length > 1 ? 's' : ''} ${unimplemented.map((o) => `"${o}"`).join(', ')} not implemented`,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return pat;
|
return pat;
|
||||||
};
|
};
|
||||||
|
|
||||||
function resolveReplications(ast) {
|
function resolveReplications(ast) {
|
||||||
ast.source_ = strudel.flatten(
|
ast.source_ = strudel.flatten(
|
||||||
ast.source_.map((child) => {
|
ast.source_.map((child) => {
|
||||||
const { replicate, ...options } = child.options_ || {};
|
const { reps } = child.options_ || {};
|
||||||
if (!replicate) {
|
if (!reps) {
|
||||||
return [child];
|
return [child];
|
||||||
}
|
}
|
||||||
delete child.options_.replicate;
|
delete child.options_.reps;
|
||||||
return Array(replicate).fill(child);
|
return Array(reps).fill(child);
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,8 @@ import '@strudel.cycles/core/euclid.mjs';
|
||||||
import { describe, expect, it } from 'vitest';
|
import { describe, expect, it } from 'vitest';
|
||||||
|
|
||||||
describe('mini', () => {
|
describe('mini', () => {
|
||||||
const minV = (v) => mini(v).firstCycleValues;
|
const minV = (v) => mini(v).sortHapsByPart().firstCycleValues;
|
||||||
const minS = (v) => mini(v).showFirstCycle;
|
const minS = (v) => mini(v).sortHapsByPart().showFirstCycle;
|
||||||
it('supports single elements', () => {
|
it('supports single elements', () => {
|
||||||
expect(minV('a')).toEqual(['a']);
|
expect(minV('a')).toEqual(['a']);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -2265,20 +2265,20 @@ exports[`runs examples > example "orbit" example index 0 1`] = `
|
||||||
exports[`runs examples > example "pan" example index 0 1`] = `
|
exports[`runs examples > example "pan" example index 0 1`] = `
|
||||||
[
|
[
|
||||||
"[ 0/1 → 1/4 | s:bd pan:0.5 ]",
|
"[ 0/1 → 1/4 | s:bd pan:0.5 ]",
|
||||||
"[ 1/4 → 1/2 | s:hh pan:0.5 ]",
|
|
||||||
"[ 1/2 → 3/4 | s:bd pan:0.5 ]",
|
"[ 1/2 → 3/4 | s:bd pan:0.5 ]",
|
||||||
|
"[ 1/4 → 1/2 | s:hh pan:0.5 ]",
|
||||||
"[ 3/4 → 1/1 | s:hh pan:0.5 ]",
|
"[ 3/4 → 1/1 | s:hh pan:0.5 ]",
|
||||||
"[ 1/1 → 5/4 | s:bd pan:1 ]",
|
"[ 1/1 → 5/4 | s:bd pan:1 ]",
|
||||||
"[ 5/4 → 3/2 | s:hh pan:1 ]",
|
|
||||||
"[ 3/2 → 7/4 | s:bd pan:1 ]",
|
"[ 3/2 → 7/4 | s:bd pan:1 ]",
|
||||||
|
"[ 5/4 → 3/2 | s:hh pan:1 ]",
|
||||||
"[ 7/4 → 2/1 | s:hh pan:1 ]",
|
"[ 7/4 → 2/1 | s:hh pan:1 ]",
|
||||||
"[ 2/1 → 9/4 | s:bd pan:0.5 ]",
|
"[ 2/1 → 9/4 | s:bd pan:0.5 ]",
|
||||||
"[ 9/4 → 5/2 | s:hh pan:0.5 ]",
|
|
||||||
"[ 5/2 → 11/4 | s:bd pan:0.5 ]",
|
"[ 5/2 → 11/4 | s:bd pan:0.5 ]",
|
||||||
|
"[ 9/4 → 5/2 | s:hh pan:0.5 ]",
|
||||||
"[ 11/4 → 3/1 | s:hh pan:0.5 ]",
|
"[ 11/4 → 3/1 | s:hh pan:0.5 ]",
|
||||||
"[ 3/1 → 13/4 | s:bd pan:0 ]",
|
"[ 3/1 → 13/4 | s:bd pan:0 ]",
|
||||||
"[ 13/4 → 7/2 | s:hh pan:0 ]",
|
|
||||||
"[ 7/2 → 15/4 | s:bd pan:0 ]",
|
"[ 7/2 → 15/4 | s:bd pan:0 ]",
|
||||||
|
"[ 13/4 → 7/2 | s:hh pan:0 ]",
|
||||||
"[ 15/4 → 4/1 | s:hh pan:0 ]",
|
"[ 15/4 → 4/1 | s:hh pan:0 ]",
|
||||||
]
|
]
|
||||||
`;
|
`;
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue