Merge branch 'main' of github.com:yaxu/strudel into main
This commit is contained in:
commit
1b748648e0
15 changed files with 691 additions and 521 deletions
|
|
@ -53,7 +53,7 @@ Fraction.prototype.min = function(other) {
|
||||||
return this.lt(other) ? this : other;
|
return this.lt(other) ? this : other;
|
||||||
};
|
};
|
||||||
Fraction.prototype.show = function() {
|
Fraction.prototype.show = function() {
|
||||||
return this.n + "/" + this.d;
|
return this.s * this.n + "/" + this.d;
|
||||||
};
|
};
|
||||||
Fraction.prototype.or = function(other) {
|
Fraction.prototype.or = function(other) {
|
||||||
return this.eq(0) ? other : this;
|
return this.eq(0) ? other : this;
|
||||||
|
|
@ -276,6 +276,12 @@ class Pattern {
|
||||||
sub(other) {
|
sub(other) {
|
||||||
return this._opleft(other, (a) => (b) => a - b);
|
return this._opleft(other, (a) => (b) => a - b);
|
||||||
}
|
}
|
||||||
|
mul(other) {
|
||||||
|
return this._opleft(other, (a) => (b) => a * b);
|
||||||
|
}
|
||||||
|
div(other) {
|
||||||
|
return this._opleft(other, (a) => (b) => a / b);
|
||||||
|
}
|
||||||
union(other) {
|
union(other) {
|
||||||
return this._opleft(other, (a) => (b) => Object.assign({}, a, b));
|
return this._opleft(other, (a) => (b) => Object.assign({}, a, b));
|
||||||
}
|
}
|
||||||
|
|
@ -316,6 +322,12 @@ class Pattern {
|
||||||
outerJoin() {
|
outerJoin() {
|
||||||
return this.outerBind(id);
|
return this.outerBind(id);
|
||||||
}
|
}
|
||||||
|
_apply(func) {
|
||||||
|
return func(this);
|
||||||
|
}
|
||||||
|
layer(...funcs) {
|
||||||
|
return stack(...funcs.map((func) => func(this)));
|
||||||
|
}
|
||||||
_patternify(func) {
|
_patternify(func) {
|
||||||
const pat = this;
|
const pat = this;
|
||||||
const patterned = function(...args) {
|
const patterned = function(...args) {
|
||||||
|
|
@ -384,7 +396,7 @@ class Pattern {
|
||||||
return stack(with_pat, without_pat);
|
return stack(with_pat, without_pat);
|
||||||
}
|
}
|
||||||
off(time_pat, func) {
|
off(time_pat, func) {
|
||||||
return stack([this, func(this._early(time_pat))]);
|
return stack(this, func(this.late(time_pat)));
|
||||||
}
|
}
|
||||||
every(n, func) {
|
every(n, func) {
|
||||||
const pat = this;
|
const pat = this;
|
||||||
|
|
@ -447,7 +459,7 @@ class Pattern {
|
||||||
return silence;
|
return silence;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Pattern.prototype.patternified = ["fast", "slow", "early", "late"];
|
Pattern.prototype.patternified = ["apply", "fast", "slow", "early", "late"];
|
||||||
Pattern.prototype.factories = {pure, stack, slowcat, fastcat, cat, timeCat, sequence, polymeter, pm, polyrhythm, pr};
|
Pattern.prototype.factories = {pure, stack, slowcat, fastcat, cat, timeCat, sequence, polymeter, pm, polyrhythm, pr};
|
||||||
const silence = new Pattern((_) => []);
|
const silence = new Pattern((_) => []);
|
||||||
function pure(value) {
|
function pure(value) {
|
||||||
|
|
|
||||||
|
|
@ -1346,4 +1346,4 @@ span.CodeMirror-selectedtext { background: none; }
|
||||||
color: white !important;
|
color: white !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*# sourceMappingURL=index.fd7d9b66.css.map */
|
/*# sourceMappingURL=index.a25424f4.css.map */
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -3,7 +3,7 @@
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<link rel="icon" href="/tutorial/favicon.e3ab9dd9.ico">
|
<link rel="icon" href="/tutorial/favicon.e3ab9dd9.ico">
|
||||||
<link rel="stylesheet" type="text/css" href="/tutorial/index.fd7d9b66.css">
|
<link rel="stylesheet" type="text/css" href="/tutorial/index.a25424f4.css">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<meta name="description" content="Strudel REPL">
|
<meta name="description" content="Strudel REPL">
|
||||||
<title>Strudel Tutorial</title>
|
<title>Strudel Tutorial</title>
|
||||||
|
|
@ -11,6 +11,6 @@
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||||
<script src="/tutorial/index.acaced6c.js" defer=""></script>
|
<script src="/tutorial/index.c743025b.js" defer=""></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
128
featurelist.md
Normal file
128
featurelist.md
Normal file
|
|
@ -0,0 +1,128 @@
|
||||||
|
# Tidal Features in Strudel
|
||||||
|
|
||||||
|
## Basics
|
||||||
|
|
||||||
|
- [ ] drawLine
|
||||||
|
- [ ] setcps
|
||||||
|
- [ ] naming patterns? block based evaluation?
|
||||||
|
- [ ] once
|
||||||
|
- [ ] silence
|
||||||
|
- [x] hush
|
||||||
|
- [ ] panic
|
||||||
|
|
||||||
|
## Concatenation
|
||||||
|
|
||||||
|
https://tidalcycles.org/docs/patternlib/tour/concatenation
|
||||||
|
|
||||||
|
- [x] cat: is synonym to fastcat in strudel, while [in tidal, cat is slowcat](https://tidalcycles.org/docs/patternlib/tour/concatenation#cat)
|
||||||
|
- [x] fastcat
|
||||||
|
- [x] timeCat: why is this camel case?
|
||||||
|
- [ ] randcat
|
||||||
|
- [x] append: but is like fastAppend in tidal
|
||||||
|
- [ ] fastAppend
|
||||||
|
- [ ] slowAppend
|
||||||
|
- [ ] wedge
|
||||||
|
- [ ] brak
|
||||||
|
- [ ] flatpat
|
||||||
|
|
||||||
|
## Accumulation
|
||||||
|
|
||||||
|
- [ ] overlay => like stack? "The overlay function is similar to cat" => wrong?
|
||||||
|
- [ ] `<>` operator (=== overlay)
|
||||||
|
- [x] stack
|
||||||
|
- [x] superimpose => strudel supports multiple args => is this layer?
|
||||||
|
- [ ] layer => is this like superimpose but with multiple args?
|
||||||
|
- [ ] steps ?
|
||||||
|
- [ ] iter, iter'
|
||||||
|
|
||||||
|
## Alteration
|
||||||
|
|
||||||
|
- [ ] range, rangex
|
||||||
|
- [ ] quantise
|
||||||
|
- [ ] ply
|
||||||
|
- [ ] stutter
|
||||||
|
- [ ] stripe, slowstripe
|
||||||
|
- [ ] palindrome = every(2, rev)
|
||||||
|
- [ ] trunc
|
||||||
|
- [ ] linger
|
||||||
|
- [ ] chunk, chunk'
|
||||||
|
- [ ] shuffle
|
||||||
|
- [ ] scramble
|
||||||
|
- [ ] rot
|
||||||
|
- [ ] step / step'
|
||||||
|
- [ ] lindenmeyer
|
||||||
|
- [ ] spread / spreadf / fastspread
|
||||||
|
- [ ] spreadChoose / spreadr
|
||||||
|
|
||||||
|
## conditions
|
||||||
|
|
||||||
|
- [x] every
|
||||||
|
- [ ] every'
|
||||||
|
- [ ] whenmod
|
||||||
|
- [ ] sometimes, sometimesBy, someCycles, someCyclesBy
|
||||||
|
- [ ] choose, chooseby, wchoose, wchooseby
|
||||||
|
- [x] struct
|
||||||
|
- [x] mask
|
||||||
|
- [ ] sew
|
||||||
|
- [ ] stitch
|
||||||
|
- [ ] select, selectF
|
||||||
|
- [ ] pickF
|
||||||
|
- [ ] squeeze
|
||||||
|
- [ ] euclid, euclidInv, euclidFull
|
||||||
|
- [ ] ifp
|
||||||
|
|
||||||
|
## Time
|
||||||
|
|
||||||
|
- [x] fast
|
||||||
|
- [x] fastGap
|
||||||
|
- [x] slow
|
||||||
|
- [ ] hurry
|
||||||
|
- [ ] compress: is this compressSpan?
|
||||||
|
- [ ] zoom
|
||||||
|
- [ ] within
|
||||||
|
- [x] off
|
||||||
|
- [ ] rotL / rotR
|
||||||
|
- [x] rev
|
||||||
|
- [ ] jux / juxBy
|
||||||
|
- [ ] swingBy / swing
|
||||||
|
- [ ] ghost
|
||||||
|
- [ ] inside / outside
|
||||||
|
|
||||||
|
## Harmony & Melody
|
||||||
|
|
||||||
|
- [x] scale
|
||||||
|
- [ ] scaleList
|
||||||
|
- [ ] getScale
|
||||||
|
- [ ] toScale
|
||||||
|
- [ ] chordList
|
||||||
|
- [ ] arpeggiate
|
||||||
|
- [ ] arp
|
||||||
|
|
||||||
|
## Transitions
|
||||||
|
|
||||||
|
- [ ] anticipate / anticipateIn
|
||||||
|
- [ ] clutch / clutchIn
|
||||||
|
- [ ] histpan
|
||||||
|
- [ ] interpolate / interpolateIn
|
||||||
|
- [ ] jump / jumpIn / jumpIn' / jumpMod
|
||||||
|
- [ ] wait / waitT
|
||||||
|
- [ ] wash / washIn
|
||||||
|
- [ ] xfade / xfadeIn
|
||||||
|
|
||||||
|
## Sampling
|
||||||
|
|
||||||
|
- [ ] chop
|
||||||
|
- [ ] striate / striateBy
|
||||||
|
- [ ] loopAt
|
||||||
|
- [ ] segment
|
||||||
|
- [ ] discretise
|
||||||
|
|
||||||
|
## Randomness
|
||||||
|
|
||||||
|
- [ ] rand / irand
|
||||||
|
- [ ] perlin / perlinWith / perlin2 / perlin2With
|
||||||
|
|
||||||
|
## Composition
|
||||||
|
|
||||||
|
- [ ] ur
|
||||||
|
- [ ] seqP / seqPLoop
|
||||||
|
|
@ -68,13 +68,13 @@ function App() {
|
||||||
|
|
||||||
useWebMidi({
|
useWebMidi({
|
||||||
ready: useCallback(({ outputs }) => {
|
ready: useCallback(({ outputs }) => {
|
||||||
pushLog(`WebMidi ready! Just add .midi(${outputs.map((o) => `"${o.name}"`).join(' | ')}) to the pattern. `);
|
pushLog(`WebMidi ready! Just add .midi(${outputs.map((o) => `'${o.name}'`).join(' | ')}) to the pattern. `);
|
||||||
}, []),
|
}, []),
|
||||||
connected: useCallback(({ outputs }) => {
|
connected: useCallback(({ outputs }) => {
|
||||||
pushLog(`Midi device connected! Available: ${outputs.map((o) => `"${o.name}"`).join(', ')}`);
|
pushLog(`Midi device connected! Available: ${outputs.map((o) => `'${o.name}'`).join(', ')}`);
|
||||||
}, []),
|
}, []),
|
||||||
disconnected: useCallback(({ outputs }) => {
|
disconnected: useCallback(({ outputs }) => {
|
||||||
pushLog(`Midi device disconnected! Available: ${outputs.map((o) => `"${o.name}"`).join(', ')}`);
|
pushLog(`Midi device disconnected! Available: ${outputs.map((o) => `'${o.name}'`).join(', ')}`);
|
||||||
}, []),
|
}, []),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -123,7 +123,7 @@ function App() {
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
{error && (
|
{error && (
|
||||||
<div className={cx('absolute right-2 bottom-2', 'text-red-500')}>{error?.message || 'unknown error'}</div>
|
<div className={cx('absolute right-2 bottom-2 px-2', 'text-red-500')}>{error?.message || 'unknown error'}</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,13 @@ export default function enableWebMidi() {
|
||||||
const outputByName = (name: string) => WebMidi.getOutputByName(name);
|
const outputByName = (name: string) => WebMidi.getOutputByName(name);
|
||||||
|
|
||||||
Pattern.prototype.midi = function (output: string, channel = 1) {
|
Pattern.prototype.midi = function (output: string, channel = 1) {
|
||||||
|
if (output?.constructor?.name === 'Pattern') {
|
||||||
|
throw new Error(
|
||||||
|
`.midi does not accept Pattern input. Make sure to pass device name with single quotes. Example: .midi('${
|
||||||
|
WebMidi.outputs?.[0]?.name || 'IAC Driver Bus 1'
|
||||||
|
}')`
|
||||||
|
);
|
||||||
|
}
|
||||||
return this.fmap((value: any) => ({
|
return this.fmap((value: any) => ({
|
||||||
...value,
|
...value,
|
||||||
onTrigger: (time: number, event: any) => {
|
onTrigger: (time: number, event: any) => {
|
||||||
|
|
@ -41,8 +48,8 @@ Pattern.prototype.midi = function (output: string, channel = 1) {
|
||||||
const device = output ? outputByName(output) : WebMidi.outputs[0];
|
const device = output ? outputByName(output) : WebMidi.outputs[0];
|
||||||
if (!device) {
|
if (!device) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`🔌 MIDI device ${output ? output : ''} not found. Use one of ${WebMidi.outputs
|
`🔌 MIDI device '${output ? output : ''}' not found. Use one of ${WebMidi.outputs
|
||||||
.map((o: any) => `"${o.name}"`)
|
.map((o: any) => `'${o.name}'`)
|
||||||
.join(' | ')}`
|
.join(' | ')}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -91,7 +91,9 @@ export default (code) => {
|
||||||
const isMarkable = isPatternArg(parents) || hasModifierCall(parent);
|
const isMarkable = isPatternArg(parents) || hasModifierCall(parent);
|
||||||
// add to location to pure(x) calls
|
// add to location to pure(x) calls
|
||||||
if (node.type === 'CallExpression' && node.callee.name === 'pure') {
|
if (node.type === 'CallExpression' && node.callee.name === 'pure') {
|
||||||
return reifyWithLocation(node.arguments[0].name, node.arguments[0], ast.locations, artificialNodes);
|
const literal = node.arguments[0];
|
||||||
|
const value = literal[{ LiteralNumericExpression: 'value', LiteralStringExpression: 'name' }[literal.type]];
|
||||||
|
return reifyWithLocation(value + '', node.arguments[0], ast.locations, artificialNodes);
|
||||||
}
|
}
|
||||||
// replace pseudo note variables
|
// replace pseudo note variables
|
||||||
if (node.type === 'IdentifierExpression') {
|
if (node.type === 'IdentifierExpression') {
|
||||||
|
|
@ -339,4 +341,3 @@ function getLocationObject(node, locations) {
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -58,62 +58,28 @@ export const tetrisWithFunctions = `stack(sequence(
|
||||||
).slow(16)`;
|
).slow(16)`;
|
||||||
|
|
||||||
export const tetris = `stack(
|
export const tetris = `stack(
|
||||||
cat(
|
cat(
|
||||||
"e5 [b4 c5] d5 [c5 b4]",
|
"e5 [b4 c5] d5 [c5 b4]",
|
||||||
"a4 [a4 c5] e5 [d5 c5]",
|
"a4 [a4 c5] e5 [d5 c5]",
|
||||||
"b4 [~ c5] d5 e5",
|
"b4 [~ c5] d5 e5",
|
||||||
"c5 a4 a4 ~",
|
"c5 a4 a4 ~",
|
||||||
"[~ d5] [~ f5] a5 [g5 f5]",
|
"[~ d5] [~ f5] a5 [g5 f5]",
|
||||||
"e5 [~ c5] e5 [d5 c5]",
|
"e5 [~ c5] e5 [d5 c5]",
|
||||||
"b4 [b4 c5] d5 e5",
|
"b4 [b4 c5] d5 e5",
|
||||||
"c5 a4 a4 ~"
|
"c5 a4 a4 ~"
|
||||||
),
|
),
|
||||||
cat(
|
cat(
|
||||||
"e2 e3 e2 e3 e2 e3 e2 e3",
|
"e2 e3 e2 e3 e2 e3 e2 e3",
|
||||||
"a2 a3 a2 a3 a2 a3 a2 a3",
|
"a2 a3 a2 a3 a2 a3 a2 a3",
|
||||||
"g#2 g#3 g#2 g#3 e2 e3 e2 e3",
|
"g#2 g#3 g#2 g#3 e2 e3 e2 e3",
|
||||||
"a2 a3 a2 a3 a2 a3 b1 c2",
|
"a2 a3 a2 a3 a2 a3 b1 c2",
|
||||||
"d2 d3 d2 d3 d2 d3 d2 d3",
|
"d2 d3 d2 d3 d2 d3 d2 d3",
|
||||||
"c2 c3 c2 c3 c2 c3 c2 c3",
|
"c2 c3 c2 c3 c2 c3 c2 c3",
|
||||||
"b1 b2 b1 b2 e2 e3 e2 e3",
|
"b1 b2 b1 b2 e2 e3 e2 e3",
|
||||||
"a1 a2 a1 a2 a1 a2 a1 a2",
|
"a1 a2 a1 a2 a1 a2 a1 a2",
|
||||||
)
|
)
|
||||||
).slow(16)`;
|
).slow(16)`;
|
||||||
|
|
||||||
export const tetrisRev = `stack(
|
|
||||||
cat(
|
|
||||||
"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 ~",
|
|
||||||
).rev(),
|
|
||||||
cat(
|
|
||||||
"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",
|
|
||||||
"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",
|
|
||||||
).rev()
|
|
||||||
).slow(16)`;
|
|
||||||
|
|
||||||
/*
|
|
||||||
.synth({
|
|
||||||
oscillator: {type: 'sawtooth'},
|
|
||||||
envelope: { attack: 0.1 }
|
|
||||||
}).filter(1200).gain(0.8)
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* export const tetrisMini1 =
|
|
||||||
'"[[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 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] [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)';
|
|
||||||
*/
|
|
||||||
export const tetrisMini = `\`[[e5 [b4 c5] d5 [c5 b4]]
|
export const tetrisMini = `\`[[e5 [b4 c5] d5 [c5 b4]]
|
||||||
[a4 [a4 c5] e5 [d5 c5]]
|
[a4 [a4 c5] e5 [d5 c5]]
|
||||||
[b4 [~ c5] d5 e5]
|
[b4 [~ c5] d5 e5]
|
||||||
|
|
@ -132,52 +98,6 @@ export const tetrisMini = `\`[[e5 [b4 c5] d5 [c5 b4]]
|
||||||
[[a1 a2]*4]\`.slow(16)
|
[[a1 a2]*4]\`.slow(16)
|
||||||
`;
|
`;
|
||||||
|
|
||||||
/* export const tetrisHaskellH = `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]"\`)
|
|
||||||
`; */
|
|
||||||
// following syntax is not supported anymore
|
|
||||||
/* export const tetrisHaskell = `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]"\`)`;
|
|
||||||
*/
|
|
||||||
export const spanish = `slowcat(
|
|
||||||
stack(c4,eb4,g4),
|
|
||||||
stack(bb3,d4,f4),
|
|
||||||
stack(ab3,c4,eb4),
|
|
||||||
stack(g3,b3,d4)
|
|
||||||
)`;
|
|
||||||
|
|
||||||
export const whirlyStrudel = `sequence(e4, [b2, b3], c4)
|
export const whirlyStrudel = `sequence(e4, [b2, b3], c4)
|
||||||
.every(4, fast(2))
|
.every(4, fast(2))
|
||||||
.every(3, slow(1.5))
|
.every(3, slow(1.5))
|
||||||
|
|
@ -321,26 +241,6 @@ export const magicSofa = `stack(
|
||||||
"<c2 f2 g2> <d2 g2 a2 e2>"
|
"<c2 f2 g2> <d2 g2 a2 e2>"
|
||||||
).slow(1).transpose.slowcat(0, 2, 3, 4)`;
|
).slow(1).transpose.slowcat(0, 2, 3, 4)`;
|
||||||
|
|
||||||
/* export const confusedPhone = `stack("[g2 ~@1.3] [c3 ~@1.3]".slow(2))
|
|
||||||
.superimpose(
|
|
||||||
x => transpose(-12,x).late(0),
|
|
||||||
x => transpose(7,x).late(0.2),
|
|
||||||
x => transpose(10,x).late(0.4),
|
|
||||||
x => transpose(12,x).late(0.6),
|
|
||||||
x => transpose(24,x).late(0.8)
|
|
||||||
)
|
|
||||||
.scale(sequence('C dorian', 'C mixolydian').slow(4))
|
|
||||||
.scaleTranspose(slowcat(0,1,2,1).slow(2))
|
|
||||||
.synth('triangle').gain(0.5).filter(1500)`; */
|
|
||||||
|
|
||||||
export const confusedPhoneDynamic = `stack("[g2 ~@1.3] [c3 ~@1.3]".slow(2))
|
|
||||||
.superimpose(
|
|
||||||
...[-12,7,10,12,24].slice(0,5).map((t,i,{length}) => x => transpose(t,x).late(i/length))
|
|
||||||
)
|
|
||||||
.scale(sequence('C dorian', 'C mixolydian').slow(4))
|
|
||||||
.scaleTranspose(slowcat(0,1,2,1).slow(2))
|
|
||||||
.synth('triangle').gain(0.5).filter(1500)`;
|
|
||||||
|
|
||||||
export const confusedPhone = `"[g2 ~@1.3] [c3 ~@1.3]"
|
export const confusedPhone = `"[g2 ~@1.3] [c3 ~@1.3]"
|
||||||
.superimpose(
|
.superimpose(
|
||||||
transpose(-12).late(0),
|
transpose(-12).late(0),
|
||||||
|
|
@ -391,24 +291,25 @@ export const loungerave = `() => {
|
||||||
const keys = new PolySynth().set({ ...osc('sawtooth'), ...adsr(0, .5, .2, .7) }).chain(lowpass(1200), vol(.5), out);
|
const keys = new PolySynth().set({ ...osc('sawtooth'), ...adsr(0, .5, .2, .7) }).chain(lowpass(1200), vol(.5), out);
|
||||||
|
|
||||||
const drums = stack(
|
const drums = stack(
|
||||||
"c1*2".tone(kick).bypass("<0@7 1>/8"),
|
"c1*2".tone(kick).mask("<x@7 ~>/8"),
|
||||||
"~ <x!7 [x@3 x]>".tone(snare).bypass("<0@7 1>/4"),
|
"~ <x!7 [x@3 x]>".tone(snare).mask("<x@7 ~>/4"),
|
||||||
"[~ c4]*2".tone(hihat)
|
"[~ c4]*2".tone(hihat)
|
||||||
);
|
);
|
||||||
|
|
||||||
const thru = (x) => x.transpose("<0 1>/8").transpose(1);
|
const thru = (x) => x.transpose("<0 1>/8").transpose(1);
|
||||||
const synths = stack(
|
const synths = stack(
|
||||||
"<C2 Bb1 Ab1 [G1 [G2 G1]]>/2".struct("[x [~ x] <[~ [~ x]]!3 [x x]>@2]/2").edit(thru).tone(bass),
|
"<C2 Bb1 Ab1 [G1 [G2 G1]]>/2".struct("[x [~ x] <[~ [~ x]]!3 [x x]>@2]/2").edit(thru).tone(bass),
|
||||||
"<Cm7 Bb7 Fm7 G7b9>/2".struct("~ [x@0.1 ~]").voicings().edit(thru).every(2, early(1/4)).tone(keys).bypass("<0@7 1>/8".early(1/4))
|
"<Cm7 Bb7 Fm7 G7b9>/2".struct("~ [x@0.1 ~]").voicings().edit(thru).every(2, early(1/4)).tone(keys).mask("<x@7 ~>/8".early(1/4))
|
||||||
)
|
)
|
||||||
return stack(
|
return stack(
|
||||||
drums,
|
drums,
|
||||||
synths
|
synths
|
||||||
)
|
)
|
||||||
//.bypass("<0 1>*4")
|
//.mask("<x ~>*4")
|
||||||
//.early("0.25 0");
|
//.early("0.25 0");
|
||||||
}`;
|
}`;
|
||||||
|
|
||||||
|
|
||||||
export const caverave = `() => {
|
export const caverave = `() => {
|
||||||
const delay = new FeedbackDelay(1/8, .4).chain(vol(0.5), out);
|
const delay = new FeedbackDelay(1/8, .4).chain(vol(0.5), out);
|
||||||
const kick = new MembraneSynth().chain(vol(.8), out);
|
const kick = new MembraneSynth().chain(vol(.8), out);
|
||||||
|
|
@ -418,8 +319,8 @@ export const caverave = `() => {
|
||||||
const keys = new PolySynth().set({ ...osc('sawtooth'), ...adsr(0, .5, .2, .7) }).chain(lowpass(1200), vol(.5), out);
|
const keys = new PolySynth().set({ ...osc('sawtooth'), ...adsr(0, .5, .2, .7) }).chain(lowpass(1200), vol(.5), out);
|
||||||
|
|
||||||
const drums = stack(
|
const drums = stack(
|
||||||
"c1*2".tone(kick).bypass("<0@7 1>/8"),
|
"c1*2".tone(kick).mask("<x@7 ~>/8"),
|
||||||
"~ <x!7 [x@3 x]>".tone(snare).bypass("<0@7 1>/4"),
|
"~ <x!7 [x@3 x]>".tone(snare).mask("<x@7 ~>/4"),
|
||||||
"[~ c4]*2".tone(hihat)
|
"[~ c4]*2".tone(hihat)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -431,9 +332,9 @@ export const caverave = `() => {
|
||||||
scaleTranspose(2).early(1/8),
|
scaleTranspose(2).early(1/8),
|
||||||
scaleTranspose(7).early(1/4),
|
scaleTranspose(7).early(1/4),
|
||||||
scaleTranspose(8).early(3/8)
|
scaleTranspose(8).early(3/8)
|
||||||
).edit(thru).tone(keys).bypass("<1 0>/16"),
|
).apply(thru).tone(keys).mask("<~ x>/16"),
|
||||||
"<C2 Bb1 Ab1 [G1 [G2 G1]]>/2".struct("[x [~ x] <[~ [~ x]]!3 [x x]>@2]/2".fast(2)).edit(thru).tone(bass),
|
"<C2 Bb1 Ab1 [G1 [G2 G1]]>/2".struct("[x [~ x] <[~ [~ x]]!3 [x x]>@2]/2".fast(2)).apply(thru).tone(bass),
|
||||||
"<Cm7 Bb7 Fm7 G7b13>/2".struct("~ [x@0.1 ~]".fast(2)).voicings().edit(thru).every(2, early(1/8)).tone(keys).bypass("<0@7 1>/8".early(1/4))
|
"<Cm7 Bb7 Fm7 G7b13>/2".struct("~ [x@0.1 ~]".fast(2)).voicings().apply(thru).every(2, early(1/8)).tone(keys).mask("<x@7 ~>/8".early(1/4))
|
||||||
)
|
)
|
||||||
return stack(
|
return stack(
|
||||||
drums.fast(2),
|
drums.fast(2),
|
||||||
|
|
@ -441,15 +342,16 @@ export const caverave = `() => {
|
||||||
).slow(2);
|
).slow(2);
|
||||||
}`;
|
}`;
|
||||||
|
|
||||||
|
|
||||||
export const callcenterhero = `()=>{
|
export const callcenterhero = `()=>{
|
||||||
const bpm = 90;
|
const bpm = 90;
|
||||||
const lead = polysynth().set({...osc('sine4'),...adsr(.004)}).chain(vol(0.15),out)
|
const lead = polysynth().set({...osc('sine4'),...adsr(.004)}).chain(vol(0.15),out)
|
||||||
const bass = fmsynth({...osc('sawtooth6'),...adsr(0.05,.6,0.8,0.1)}).chain(vol(0.6), out);
|
const bass = fmsynth({...osc('sawtooth6'),...adsr(0.05,.6,0.8,0.1)}).chain(vol(0.6), out);
|
||||||
const s = scale(slowcat('F3 minor', 'Ab3 major', 'Bb3 dorian', 'C4 phrygian dominant').slow(4));
|
const s = scale(slowcat('F3 minor', 'Ab3 major', 'Bb3 dorian', 'C4 phrygian dominant').slow(4));
|
||||||
return stack(
|
return stack(
|
||||||
"0 2".struct("<x ~> [x ~]").edit(s).scaleTranspose(stack(0,2)).tone(lead),
|
"0 2".struct("<x ~> [x ~]").apply(s).scaleTranspose(stack(0,2)).tone(lead),
|
||||||
"<6 7 9 7>".struct("[~ [x ~]*2]*2").edit(s).scaleTranspose("[0,2] [2,4]".fast(2).every(4,rev)).tone(lead),
|
"<6 7 9 7>".struct("[~ [x ~]*2]*2").apply(s).scaleTranspose("[0,2] [2,4]".fast(2).every(4,rev)).tone(lead),
|
||||||
"-14".struct("[~ x@0.8]*2".early(0.01)).edit(s).tone(bass),
|
"-14".struct("[~ x@0.8]*2".early(0.01)).apply(s).tone(bass),
|
||||||
"c2*2".tone(membrane().chain(vol(0.6), out)),
|
"c2*2".tone(membrane().chain(vol(0.6), out)),
|
||||||
"~ c2".tone(noise().chain(vol(0.2), out)),
|
"~ c2".tone(noise().chain(vol(0.2), out)),
|
||||||
"c4*4".tone(metal(adsr(0,.05,0)).chain(vol(0.03), out))
|
"c4*4".tone(metal(adsr(0,.05,0)).chain(vol(0.03), out))
|
||||||
|
|
@ -457,3 +359,95 @@ export const callcenterhero = `()=>{
|
||||||
.slow(120 / bpm)
|
.slow(120 / bpm)
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export const primalEnemy = `()=>{
|
||||||
|
const f = fast("<1 <2 [4 8]>>");
|
||||||
|
return stack(
|
||||||
|
"c3,g3,c4".struct("[x ~]*2").apply(f).transpose("<0 <3 [5 [7 [9 [11 13]]]]>>"),
|
||||||
|
"c2 [c2 ~]*2".tone(synth(osc('sawtooth8')).chain(vol(0.8),out)),
|
||||||
|
"c1*2".tone(membrane().chain(vol(0.8),out))
|
||||||
|
).slow(1)
|
||||||
|
}`;
|
||||||
|
|
||||||
|
export const drums = `stack(
|
||||||
|
"c1*2".tone(membrane().chain(vol(0.8),out)),
|
||||||
|
"~ c3".tone(noise().chain(vol(0.8),out)),
|
||||||
|
"c3*4".transpose("[-24 0]*2").tone(metal(adsr(0,.015)).chain(vol(0.8),out))
|
||||||
|
)
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const xylophoneCalling = `()=>{
|
||||||
|
const t = x=> x.scaleTranspose("<0 2 4 3>/4").transpose(-2)
|
||||||
|
const s = x => x.scale(slowcat('C3 minor pentatonic','G3 minor pentatonic').slow(4))
|
||||||
|
const delay = new FeedbackDelay(1/8, .6).chain(vol(0.1), out);
|
||||||
|
const chorus = new Chorus(1,2.5,0.5).start();
|
||||||
|
return stack(
|
||||||
|
// melody
|
||||||
|
"<<10 7> <8 3>>/4".struct("x*3").apply(s)
|
||||||
|
.scaleTranspose("<0 3 2> <1 4 3>")
|
||||||
|
.superimpose(scaleTranspose(2).early(1/8))
|
||||||
|
.apply(t).tone(polysynth().set({
|
||||||
|
...osc('triangle4'),
|
||||||
|
...adsr(0,.08,0)
|
||||||
|
}).chain(vol(0.2).connect(delay),chorus,out)).mask("<~@3 x>/16".early(1/8)),
|
||||||
|
// pad
|
||||||
|
"[1,3]/4".scale('G3 minor pentatonic').apply(t).tone(polysynth().set({
|
||||||
|
...osc('square2'),
|
||||||
|
...adsr(0.1,.4,0.8)
|
||||||
|
}).chain(vol(0.2),chorus,out)).mask("<~ x>/32"),
|
||||||
|
// xylophone
|
||||||
|
"c3,g3,c4".struct("<x*2 x>").fast("<1 <2!3 [4 8]>>").apply(s).scaleTranspose("<0 <1 [2 [3 <4 5>]]>>").apply(t).tone(polysynth().set({
|
||||||
|
...osc('sawtooth4'),
|
||||||
|
...adsr(0,.1,0)
|
||||||
|
}).chain(vol(0.4).connect(delay),out)).mask("<x@3 ~>/16".early(1/8)),
|
||||||
|
// bass
|
||||||
|
"c2 [c2 ~]*2".scale('C hirajoshi').apply(t).tone(synth({
|
||||||
|
...osc('sawtooth6'),
|
||||||
|
...adsr(0,.03,.4,.1)
|
||||||
|
}).chain(vol(0.4),out)),
|
||||||
|
// kick
|
||||||
|
"<c1!3 [c1 ~]*2>*2".tone(membrane().chain(vol(0.8),out)),
|
||||||
|
// snare
|
||||||
|
"~ <c3!7 [c3 c3*2]>".tone(noise().chain(vol(0.8),out)),
|
||||||
|
// hihat
|
||||||
|
"c3*4".transpose("[-24 0]*2").tone(metal(adsr(0,.02)).chain(vol(0.5).connect(delay),out))
|
||||||
|
).slow(1)
|
||||||
|
}`;
|
||||||
|
|
||||||
|
export const sowhatelse = `()=> {
|
||||||
|
// mixer
|
||||||
|
const mix = (key) => vol({
|
||||||
|
chords: .2,
|
||||||
|
lead: 0.8,
|
||||||
|
bass: .4,
|
||||||
|
snare: .95,
|
||||||
|
kick: .9,
|
||||||
|
hihat: .35,
|
||||||
|
}[key]||0);
|
||||||
|
const delay = new FeedbackDelay(1/6, .3).chain(vol(.7), out);
|
||||||
|
const delay2 = new FeedbackDelay(1/6, .2).chain(vol(.15), out);
|
||||||
|
const chorus = new Chorus(1,2.5,0.5).start();
|
||||||
|
// instruments
|
||||||
|
const instr = (instrument) => ({
|
||||||
|
organ: polysynth().set({...osc('sawtooth4'), ...adsr(.01,.2,0)}).chain(mix('chords').connect(delay),out),
|
||||||
|
lead: polysynth().set({...osc('triangle4'),...adsr(0.01,.05,0)}).chain(mix('lead').connect(delay2), out),
|
||||||
|
bass: polysynth().set({...osc('sawtooth8'),...adsr(.02,.05,.3,.2)}).chain(mix('bass'),lowpass(3000), out),
|
||||||
|
pad: polysynth().set({...osc('square2'),...adsr(0.1,.4,0.8)}).chain(vol(0.15),chorus,out),
|
||||||
|
hihat: metal(adsr(0, .02, 0)).chain(mix('hihat'), out),
|
||||||
|
snare: noise(adsr(0, .15, 0.01)).chain(mix('snare'), lowpass(5000), out),
|
||||||
|
kick: membrane().chain(mix('kick'), out)
|
||||||
|
}[instrument]);
|
||||||
|
// harmony
|
||||||
|
const t = transpose("<0 0 1 0>/8");
|
||||||
|
const sowhat = scaleTranspose("0,3,6,9,11");
|
||||||
|
// track
|
||||||
|
return stack(
|
||||||
|
"[<0 4 [3 [2 1]]>]/4".struct("[x]*3").mask("[~ x ~]").scale('D5 dorian').off(1/6, scaleTranspose(-7)).off(1/3, scaleTranspose(-5)).apply(t).tone(instr('lead')).mask("<~ ~ x x>/8"),
|
||||||
|
"<<e3 [~@2 a3]> <[d3 ~] [c3 f3] g3>>".scale('D dorian').apply(sowhat).apply(t).tone(instr('organ')).mask("<x x x ~>/8"),
|
||||||
|
"<[d2 [d2 ~]*3]!3 <a1*2 c2*3 [a1 e2]>>".apply(t).tone(instr('bass')),
|
||||||
|
"c1*6".tone(instr('hihat')),
|
||||||
|
"~ c3".tone(instr('snare')),
|
||||||
|
"<[c1@5 c1] <c1 [[c1@2 c1] ~] [c1 ~ c1] [c1!2 ~ c1!3]>>".tone(instr('kick')),
|
||||||
|
"[2,4]/4".scale('D dorian').apply(t).tone(instr('pad')).mask("<x x x ~>/8")
|
||||||
|
).fast(6/8)
|
||||||
|
}`;
|
||||||
|
|
|
||||||
|
|
@ -315,19 +315,13 @@ Like "\*" in mini notation, **fast** will play a pattern times the given number
|
||||||
|
|
||||||
With early, you can nudge a pattern to start earlier in time:
|
With early, you can nudge a pattern to start earlier in time:
|
||||||
|
|
||||||
<MiniRepl tune={`cat(e5, pure(b4).late(0.5))`} />
|
|
||||||
|
|
||||||
Note that we have to wrap b4 in **pure** to be able to call a the pattern modifier **early**.
|
|
||||||
|
|
||||||
There is the shorthand **p** for this:
|
|
||||||
|
|
||||||
<MiniRepl tune={`cat(e5, b4.late(0.5))`} />
|
<MiniRepl tune={`cat(e5, b4.late(0.5))`} />
|
||||||
|
|
||||||
### late(cycles)
|
### late(cycles)
|
||||||
|
|
||||||
Like early, but in the other direction:
|
Like early, but in the other direction:
|
||||||
|
|
||||||
<MiniRepl tune={`cat(e5, b4.p.late(0.5))`} />
|
<MiniRepl tune={`cat(e5, b4.late(0.5))`} />
|
||||||
|
|
||||||
TODO: shouldn't it sound different?
|
TODO: shouldn't it sound different?
|
||||||
|
|
||||||
|
|
@ -343,6 +337,8 @@ Will apply the given function every n cycles:
|
||||||
|
|
||||||
<MiniRepl tune={`cat(e5, pure(b4).every(4, late(0.5)))`} />
|
<MiniRepl tune={`cat(e5, pure(b4).every(4, late(0.5)))`} />
|
||||||
|
|
||||||
|
TODO: should be able to do b4.every => like already possible with fast slow etc..
|
||||||
|
|
||||||
Note that late is called directly. This is a shortcut for:
|
Note that late is called directly. This is a shortcut for:
|
||||||
|
|
||||||
<MiniRepl tune={`cat(e5, pure(b4).every(4, x => x.late(0.5)))`} />
|
<MiniRepl tune={`cat(e5, pure(b4).every(4, x => x.late(0.5)))`} />
|
||||||
|
|
|
||||||
|
|
@ -125,13 +125,13 @@ function useRepl({ tune, defaultSynth, autolink = true, onEvent }: any) {
|
||||||
|
|
||||||
/* useWebMidi({
|
/* useWebMidi({
|
||||||
ready: useCallback(({ outputs }) => {
|
ready: useCallback(({ outputs }) => {
|
||||||
pushLog(`WebMidi ready! Just add .midi(${outputs.map((o) => `"${o.name}"`).join(' | ')}) to the pattern. `);
|
pushLog(`WebMidi ready! Just add .midi(${outputs.map((o) => `'${o.name}'`).join(' | ')}) to the pattern. `);
|
||||||
}, []),
|
}, []),
|
||||||
connected: useCallback(({ outputs }) => {
|
connected: useCallback(({ outputs }) => {
|
||||||
pushLog(`Midi device connected! Available: ${outputs.map((o) => `"${o.name}"`).join(', ')}`);
|
pushLog(`Midi device connected! Available: ${outputs.map((o) => `'${o.name}'`).join(', ')}`);
|
||||||
}, []),
|
}, []),
|
||||||
disconnected: useCallback(({ outputs }) => {
|
disconnected: useCallback(({ outputs }) => {
|
||||||
pushLog(`Midi device disconnected! Available: ${outputs.map((o) => `"${o.name}"`).join(', ')}`);
|
pushLog(`Midi device disconnected! Available: ${outputs.map((o) => `'${o.name}'`).join(', ')}`);
|
||||||
}, []),
|
}, []),
|
||||||
}); */
|
}); */
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -73,7 +73,7 @@ Fraction.prototype.min = function(other) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Fraction.prototype.show = function () {
|
Fraction.prototype.show = function () {
|
||||||
return this.n + "/" + this.d
|
return (this.s * this.n) + "/" + this.d
|
||||||
}
|
}
|
||||||
|
|
||||||
Fraction.prototype.or = function(other) {
|
Fraction.prototype.or = function(other) {
|
||||||
|
|
@ -212,7 +212,7 @@ class Hap {
|
||||||
}
|
}
|
||||||
|
|
||||||
show() {
|
show() {
|
||||||
return "(" + (this.whole == undefined ? "~" : this.whole.show()) + ", " + this.part.show() + ", " + this.value + ")"
|
return "(" + (this.whole == undefined ? "~" : this.whole.show()) + ", " + this.part.show() + ", " + JSON.stringify(this.value?.value ?? this.value) + ")"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -574,7 +574,7 @@ class Pattern {
|
||||||
}
|
}
|
||||||
|
|
||||||
off(time_pat, func) {
|
off(time_pat, func) {
|
||||||
return stack([this, func(this._early(time_pat))])
|
return stack(this, func(this.late(time_pat)))
|
||||||
}
|
}
|
||||||
|
|
||||||
every(n, func) {
|
every(n, func) {
|
||||||
|
|
@ -636,9 +636,6 @@ class Pattern {
|
||||||
edit(...funcs) {
|
edit(...funcs) {
|
||||||
return stack(...funcs.map(func => func(this)));
|
return stack(...funcs.map(func => func(this)));
|
||||||
}
|
}
|
||||||
pipe(func) {
|
|
||||||
return func(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
_bypass(on) {
|
_bypass(on) {
|
||||||
on = Boolean(parseInt(on));
|
on = Boolean(parseInt(on));
|
||||||
|
|
|
||||||
|
|
@ -365,4 +365,26 @@ describe('Pattern', function() {
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
describe("early", () => {
|
||||||
|
it("Can shift an event earlier", () => {
|
||||||
|
assert.deepStrictEqual(
|
||||||
|
pure(30)._late(0.25).query(ts(1,2)),
|
||||||
|
[hap(ts(1/4,5/4), ts(1,5/4), 30), hap(ts(5/4,9/4), ts(5/4,2), 30)]
|
||||||
|
)
|
||||||
|
})
|
||||||
|
it("Can shift an event earlier, into negative time", () => {
|
||||||
|
assert.deepStrictEqual(
|
||||||
|
pure(30)._late(0.25).query(ts(0,1)),
|
||||||
|
[hap(ts(-3/4,1/4), ts(0,1/4), 30), hap(ts(1/4,5/4), ts(1/4,1), 30)]
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
describe("off", () => {
|
||||||
|
it("Can offset a transformed pattern from the original", () => {
|
||||||
|
assert.deepStrictEqual(
|
||||||
|
pure(30).off(0.25, add(2)).firstCycle,
|
||||||
|
stack(pure(30), pure(30).early(0.25).add(2)).firstCycle
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue