add many more krill operators
This commit is contained in:
parent
c8bf4331c8
commit
27be90358f
8 changed files with 758 additions and 85 deletions
|
|
@ -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>();
|
||||
|
|
|
|||
|
|
@ -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
18
repl/src/parse.d.ts
vendored
Normal 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
101
repl/src/parse.ts
Normal 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);
|
||||
};
|
||||
|
|
@ -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
1
repl/src/types.d.ts
vendored
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue