add many more krill operators

This commit is contained in:
Felix Roos 2022-02-06 20:59:41 +01:00
parent c8bf4331c8
commit 27be90358f
8 changed files with 758 additions and 85 deletions

View file

@ -6,12 +6,12 @@ import * as Tone from 'tone';
import useCycle from './useCycle';
import type { Hap, Pattern } from './types';
import * as tunes from './tunes';
import _mini from './mini';
import * as krill from './parse';
const { tetris, tetrisMini } = tunes;
const { tetris, tetrisMini, tetrisHaskell } = tunes;
const { sequence, pure, reify, slowcat, fastcat, cat, stack, silence } = strudel; // make available to eval
const mini = _mini; // for eval (direct import wont work somehow)
const { mini, h } = krill; // for eval (direct import wont work somehow)
const parse = (code: string): Pattern => eval(code);
const synth = new Tone.PolySynth().toDestination();
@ -23,7 +23,7 @@ synth.set({
});
function App() {
const [code, setCode] = useState<string>(tetrisMini);
const [code, setCode] = useState<string>(tetrisHaskell);
const [log, setLog] = useState('');
const logBox = useRef<any>();
const [error, setError] = useState<Error>();

View file

@ -1,55 +0,0 @@
import * as krill from '../krill-parser';
import * as strudel from '../../strudel.mjs';
const { sequence, stack, silence } = strudel;
export function patternifyAST(ast: any): any {
switch (ast.type_) {
case 'pattern':
if (ast.arguments_.alignment === 'v') {
return stack(...ast.source_.map(patternifyAST));
}
return sequence(...ast.source_.map(patternifyAST));
case 'element':
if (ast.source_ === '~') {
return silence;
}
if (typeof ast.source_ !== 'object') {
return ast.source_;
}
return patternifyAST(ast.source_);
}
// *3 => options_.operator.arguments_.amount = 1/3
}
/* export default (str: string) => patternifyAST(krill.parse(`"${str}"`)); */
export default (...strings: string[]) => {
const pattern = sequence(
...strings.map((str) => {
const ast = krill.parse(`"${str}"`);
// console.log('ast', ast);
return patternifyAST(ast);
})
);
// console.log('mini pattern', pattern);
return pattern;
};
/*
TODO:
export interface Arguments {
alignment: string;
}
export interface ElementStub {
type_: string;
source_: string;
options_?: any;
}
export interface PatternStub {
type_: string; // pattern
arguments_: Arguments;
source_: ElementStub[];
}
*/

18
repl/src/parse.d.ts vendored Normal file
View file

@ -0,0 +1,18 @@
/*
TODO:
export interface Arguments {
alignment: string;
}
export interface ElementStub {
type_: string;
source_: string;
options_?: any;
}
export interface PatternStub {
type_: string; // pattern
arguments_: Arguments;
source_: ElementStub[];
}
*/

101
repl/src/parse.ts Normal file
View file

@ -0,0 +1,101 @@
import * as krill from '../krill-parser';
import * as strudel from '../../strudel.mjs';
import { Scale, Note, Interval } from '@tonaljs/tonal';
const { sequence, stack, silence, Fraction, pure } = strudel;
function reify(thing: any) {
if (thing?.constructor?.name === 'Pattern') {
return thing;
}
return pure(thing);
}
const applyOptions = (parent: any) => (pat: any, i: number) => {
const ast = parent.source_[i];
const options = ast.options_;
const operator = options?.operator;
if (operator) {
switch (operator.type_) {
case 'stretch':
const speed = new Fraction(operator.arguments_.amount).inverse().valueOf();
return reify(pat).fast(speed);
// case 'fixed-step': "%" // https://github.com/Mdashdotdashn/krill#patterns---a-more-traditional-approach
}
console.warn(`operator "${operator.type_}" not implemented`);
}
// TODO: options.weight = "@"
// 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;
};
export function patternifyAST(ast: any): any {
switch (ast.type_) {
case 'pattern':
const children = ast.source_.map(patternifyAST).map(applyOptions(ast));
if (ast.arguments_.alignment === 'v') {
return stack(...children);
}
return sequence(...children);
case 'element':
if (ast.source_ === '~') {
return silence;
}
if (typeof ast.source_ !== 'object') {
return ast.source_;
}
return patternifyAST(ast.source_);
case 'stretch':
return patternifyAST(ast.source_).slow(ast.arguments_.amount);
case 'scale':
let [tonic, scale] = Scale.tokenize(ast.arguments_.scale);
const intervals = Scale.get(scale).intervals;
const pattern = patternifyAST(ast.source_);
tonic = tonic || 'C4';
// console.log('scale', ast, pattern, tonic, scale);
console.log('tonic', tonic);
return pattern.fmap((step: any) => {
step = Number(step);
if (isNaN(step)) {
console.warn(`scale step "${step}" not a number`);
return step;
}
const octaves = Math.floor(step / intervals.length);
const mod = (n: number, m: number): number => (n < 0 ? mod(n + m, m) : n % m);
const index = mod(step, intervals.length); // % with negative numbers. e.g. -1 % 3 = 2
const interval = Interval.add(intervals[index], Interval.fromSemitones(octaves * 12));
return Note.transpose(tonic, interval || '1P');
});
/* case 'struct':
// TODO:
return silence; */
default:
console.warn(`node type "${ast.type_}" not implemented -> returning silence`);
return silence;
}
}
// mini notation only (wraps in "")
export const mini = (...strings: string[]) => {
const pattern = sequence(
...strings.map((str) => {
const ast = krill.parse(`"${str}"`);
// console.log('ast', ast);
return patternifyAST(ast);
})
);
return pattern;
};
// includes haskell style (raw krill parsing)
export const h = (string: string) => {
const ast = krill.parse(string);
console.log('ast', ast);
return patternifyAST(ast);
};

View file

@ -55,16 +55,38 @@ export const tetrisMini = `mini(\`[[e5 [b4 c5] d5 [c5 b4]]
[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]
[[e2 e3]*4]
[[a2 a3]*4]
[[g#2 g#3]*2 [e2 e3]*2]
[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);
[[d2 d3]*4]
[[c2 c3]*4]
[[b1 b2]*2 [e2 e3]*2]
[[a1 a2]*4]\`)._slow(16);
`;
export const tetrisHaskell = `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 = `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]"\`)`;
*/
// "sequence('c3', 'eb3', sequence('g3', 'f3'))" //
/* `sequence(
stack('c4','eb4','g4'),

1
repl/src/types.d.ts vendored
View file

@ -19,4 +19,5 @@ export declare interface Hap<T = any> {
}
export declare interface Pattern<T = any> {
query: (span: TimeSpan) => Hap<T>[];
fmap: (v: T) => T;
}