mirror of
https://github.com/eliasstepanik/strudel-docker.git
synced 2026-01-11 21:58:31 +00:00
Merge branch 'main' into stateful-events
This commit is contained in:
commit
1b6d3734cf
@ -53,7 +53,7 @@ Fraction.prototype.min = function(other) {
|
||||
return this.lt(other) ? this : other;
|
||||
};
|
||||
Fraction.prototype.show = function() {
|
||||
return this.n + "/" + this.d;
|
||||
return this.s * this.n + "/" + this.d;
|
||||
};
|
||||
Fraction.prototype.or = function(other) {
|
||||
return this.eq(0) ? other : this;
|
||||
@ -276,6 +276,12 @@ class Pattern {
|
||||
sub(other) {
|
||||
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) {
|
||||
return this._opleft(other, (a) => (b) => Object.assign({}, a, b));
|
||||
}
|
||||
@ -316,6 +322,12 @@ class Pattern {
|
||||
outerJoin() {
|
||||
return this.outerBind(id);
|
||||
}
|
||||
_apply(func) {
|
||||
return func(this);
|
||||
}
|
||||
layer(...funcs) {
|
||||
return stack(...funcs.map((func) => func(this)));
|
||||
}
|
||||
_patternify(func) {
|
||||
const pat = this;
|
||||
const patterned = function(...args) {
|
||||
@ -384,7 +396,7 @@ class Pattern {
|
||||
return stack(with_pat, without_pat);
|
||||
}
|
||||
off(time_pat, func) {
|
||||
return stack([this, func(this._early(time_pat))]);
|
||||
return stack(this, func(this.late(time_pat)));
|
||||
}
|
||||
every(n, func) {
|
||||
const pat = this;
|
||||
@ -447,7 +459,7 @@ class Pattern {
|
||||
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};
|
||||
const silence = new Pattern((_) => []);
|
||||
function pure(value) {
|
||||
|
||||
@ -1346,4 +1346,4 @@ span.CodeMirror-selectedtext { background: none; }
|
||||
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>
|
||||
<meta charset="utf-8">
|
||||
<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="description" content="Strudel REPL">
|
||||
<title>Strudel Tutorial</title>
|
||||
@ -11,6 +11,6 @@
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<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>
|
||||
</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({
|
||||
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 }) => {
|
||||
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 }) => {
|
||||
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>
|
||||
</div>
|
||||
{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>
|
||||
<button
|
||||
|
||||
@ -25,6 +25,13 @@ export default function enableWebMidi() {
|
||||
const outputByName = (name: string) => WebMidi.getOutputByName(name);
|
||||
|
||||
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) => ({
|
||||
...value,
|
||||
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];
|
||||
if (!device) {
|
||||
throw new Error(
|
||||
`🔌 MIDI device ${output ? output : ''} not found. Use one of ${WebMidi.outputs
|
||||
.map((o: any) => `"${o.name}"`)
|
||||
`🔌 MIDI device '${output ? output : ''}' not found. Use one of ${WebMidi.outputs
|
||||
.map((o: any) => `'${o.name}'`)
|
||||
.join(' | ')}`
|
||||
);
|
||||
}
|
||||
|
||||
@ -91,7 +91,9 @@ export default (code) => {
|
||||
const isMarkable = isPatternArg(parents) || hasModifierCall(parent);
|
||||
// add to location to pure(x) calls
|
||||
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
|
||||
if (node.type === 'IdentifierExpression') {
|
||||
@ -339,4 +341,3 @@ function getLocationObject(node, locations) {
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -58,62 +58,28 @@ export const tetrisWithFunctions = `stack(sequence(
|
||||
).slow(16)`;
|
||||
|
||||
export const tetris = `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 ~"
|
||||
),
|
||||
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",
|
||||
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 ~"
|
||||
),
|
||||
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",
|
||||
)
|
||||
).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]]
|
||||
[a4 [a4 c5] e5 [d5 c5]]
|
||||
[b4 [~ c5] d5 e5]
|
||||
@ -132,52 +98,6 @@ export const tetrisMini = `\`[[e5 [b4 c5] d5 [c5 b4]]
|
||||
[[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)
|
||||
.every(4, fast(2))
|
||||
.every(3, slow(1.5))
|
||||
@ -321,26 +241,6 @@ export const magicSofa = `stack(
|
||||
"<c2 f2 g2> <d2 g2 a2 e2>"
|
||||
).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]"
|
||||
.superimpose(
|
||||
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 drums = stack(
|
||||
"c1*2".tone(kick).bypass("<0@7 1>/8"),
|
||||
"~ <x!7 [x@3 x]>".tone(snare).bypass("<0@7 1>/4"),
|
||||
"c1*2".tone(kick).mask("<x@7 ~>/8"),
|
||||
"~ <x!7 [x@3 x]>".tone(snare).mask("<x@7 ~>/4"),
|
||||
"[~ c4]*2".tone(hihat)
|
||||
);
|
||||
|
||||
const thru = (x) => x.transpose("<0 1>/8").transpose(1);
|
||||
const synths = stack(
|
||||
"<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(
|
||||
drums,
|
||||
synths
|
||||
)
|
||||
//.bypass("<0 1>*4")
|
||||
//.mask("<x ~>*4")
|
||||
//.early("0.25 0");
|
||||
}`;
|
||||
|
||||
|
||||
export const caverave = `() => {
|
||||
const delay = new FeedbackDelay(1/8, .4).chain(vol(0.5), 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 drums = stack(
|
||||
"c1*2".tone(kick).bypass("<0@7 1>/8"),
|
||||
"~ <x!7 [x@3 x]>".tone(snare).bypass("<0@7 1>/4"),
|
||||
"c1*2".tone(kick).mask("<x@7 ~>/8"),
|
||||
"~ <x!7 [x@3 x]>".tone(snare).mask("<x@7 ~>/4"),
|
||||
"[~ c4]*2".tone(hihat)
|
||||
);
|
||||
|
||||
@ -431,9 +332,9 @@ export const caverave = `() => {
|
||||
scaleTranspose(2).early(1/8),
|
||||
scaleTranspose(7).early(1/4),
|
||||
scaleTranspose(8).early(3/8)
|
||||
).edit(thru).tone(keys).bypass("<1 0>/16"),
|
||||
"<C2 Bb1 Ab1 [G1 [G2 G1]]>/2".struct("[x [~ x] <[~ [~ x]]!3 [x x]>@2]/2".fast(2)).edit(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))
|
||||
).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)).apply(thru).tone(bass),
|
||||
"<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(
|
||||
drums.fast(2),
|
||||
@ -441,15 +342,16 @@ export const caverave = `() => {
|
||||
).slow(2);
|
||||
}`;
|
||||
|
||||
|
||||
export const callcenterhero = `()=>{
|
||||
const bpm = 90;
|
||||
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 s = scale(slowcat('F3 minor', 'Ab3 major', 'Bb3 dorian', 'C4 phrygian dominant').slow(4));
|
||||
return stack(
|
||||
"0 2".struct("<x ~> [x ~]").edit(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),
|
||||
"-14".struct("[~ x@0.8]*2".early(0.01)).edit(s).tone(bass),
|
||||
"0 2".struct("<x ~> [x ~]").apply(s).scaleTranspose(stack(0,2)).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)).apply(s).tone(bass),
|
||||
"c2*2".tone(membrane().chain(vol(0.6), out)),
|
||||
"~ c2".tone(noise().chain(vol(0.2), out)),
|
||||
"c4*4".tone(metal(adsr(0,.05,0)).chain(vol(0.03), out))
|
||||
@ -457,3 +359,95 @@ export const callcenterhero = `()=>{
|
||||
.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:
|
||||
|
||||
<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))`} />
|
||||
|
||||
### late(cycles)
|
||||
|
||||
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?
|
||||
|
||||
@ -343,6 +337,8 @@ Will apply the given function every n cycles:
|
||||
|
||||
<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:
|
||||
|
||||
<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({
|
||||
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 }) => {
|
||||
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 }) => {
|
||||
pushLog(`Midi device disconnected! Available: ${outputs.map((o) => `"${o.name}"`).join(', ')}`);
|
||||
pushLog(`Midi device disconnected! Available: ${outputs.map((o) => `'${o.name}'`).join(', ')}`);
|
||||
}, []),
|
||||
}); */
|
||||
|
||||
|
||||
29
strudel.mjs
29
strudel.mjs
@ -73,7 +73,7 @@ Fraction.prototype.min = function(other) {
|
||||
}
|
||||
|
||||
Fraction.prototype.show = function () {
|
||||
return this.n + "/" + this.d
|
||||
return (this.s * this.n) + "/" + this.d
|
||||
}
|
||||
|
||||
Fraction.prototype.or = function(other) {
|
||||
@ -214,7 +214,7 @@ class Hap {
|
||||
}
|
||||
|
||||
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) + ")"
|
||||
}
|
||||
|
||||
setContext(context) {
|
||||
@ -441,6 +441,14 @@ class Pattern {
|
||||
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) {
|
||||
return this._opleft(other, a => b => Object.assign({}, a, b))
|
||||
}
|
||||
@ -497,6 +505,14 @@ class Pattern {
|
||||
return this.outerBind(id)
|
||||
}
|
||||
|
||||
_apply(func) {
|
||||
return func(this)
|
||||
}
|
||||
|
||||
layer(...funcs) {
|
||||
return stack(...funcs.map(func => func(this)))
|
||||
}
|
||||
|
||||
_patternify(func) {
|
||||
const pat = this
|
||||
const patterned = function (...args) {
|
||||
@ -595,7 +611,7 @@ class Pattern {
|
||||
}
|
||||
|
||||
off(time_pat, func) {
|
||||
return stack([this, func(this._early(time_pat))])
|
||||
return stack(this, func(this.late(time_pat)))
|
||||
}
|
||||
|
||||
every(n, func) {
|
||||
@ -640,7 +656,7 @@ class Pattern {
|
||||
const left = this.withValue(val => Object.assign({}, val, {pan: elem_or(val, "pan", 0.5) - by}))
|
||||
const right = this.withValue(val => Object.assign({}, val, {pan: elem_or(val, "pan", 0.5) + by}))
|
||||
|
||||
return stack([left,func(right)])
|
||||
return stack(left,func(right))
|
||||
}
|
||||
|
||||
// is there a different name for those in tidal?
|
||||
@ -658,9 +674,6 @@ class Pattern {
|
||||
edit(...funcs) {
|
||||
return stack(...funcs.map(func => func(this)));
|
||||
}
|
||||
pipe(func) {
|
||||
return func(this);
|
||||
}
|
||||
|
||||
_bypass(on) {
|
||||
on = Boolean(parseInt(on));
|
||||
@ -689,7 +702,7 @@ class Pattern {
|
||||
}
|
||||
|
||||
// methods of Pattern that get callable factories
|
||||
Pattern.prototype.patternified = ['fast', 'slow', 'early', 'late'];
|
||||
Pattern.prototype.patternified = ['apply', 'fast', 'slow', 'early', 'late'];
|
||||
// methods that create patterns, which are added to patternified Pattern methods
|
||||
Pattern.prototype.factories = { pure, stack, slowcat, fastcat, cat, timeCat, sequence, polymeter, pm, polyrhythm, pr};
|
||||
// the magic happens in Pattern constructor. Keeping this in prototype enables adding methods from the outside (e.g. see tonal.ts)
|
||||
|
||||
@ -81,6 +81,16 @@ describe('Pattern', function() {
|
||||
assert.equal(pure(3).sub(pure(4)).query(st(0,1))[0].value, -1)
|
||||
})
|
||||
})
|
||||
describe('mul()', function () {
|
||||
it('Can multiply things', function() {
|
||||
assert.equal(pure(3).mul(pure(2)).firstCycle[0].value, 6)
|
||||
})
|
||||
})
|
||||
describe('div()', function () {
|
||||
it('Can divide things', function() {
|
||||
assert.equal(pure(3).div(pure(2)).firstCycle[0].value, 1.5)
|
||||
})
|
||||
})
|
||||
describe('union()', function () {
|
||||
it('Can union things', function () {
|
||||
assert.deepStrictEqual(pure({a: 4, b: 6}).union(pure({c: 7})).firstCycle[0].value, {a: 4, b: 6, c: 7})
|
||||
@ -343,4 +353,60 @@ describe('Pattern', function() {
|
||||
)
|
||||
})
|
||||
})
|
||||
describe("apply", () => {
|
||||
it('Can apply a function', () => {
|
||||
assert.deepStrictEqual(
|
||||
sequence("a", "b")._apply(fast(2)).firstCycle,
|
||||
sequence("a", "b").fast(2).firstCycle
|
||||
)
|
||||
}),
|
||||
it('Can apply a pattern of functions', () => {
|
||||
assert.deepStrictEqual(
|
||||
sequence("a", "b").apply(fast(2)).firstCycle,
|
||||
sequence("a", "b").fast(2).firstCycle
|
||||
)
|
||||
assert.deepStrictEqual(
|
||||
sequence("a", "b").apply(fast(2),fast(3)).firstCycle,
|
||||
sequence("a", "b").fast(2,3).firstCycle
|
||||
)
|
||||
})
|
||||
})
|
||||
describe("layer", () => {
|
||||
it('Can layer up multiple functions', () => {
|
||||
assert.deepStrictEqual(
|
||||
sequence(1,2,3).layer(fast(2), pat => pat.add(3,4)).firstCycle,
|
||||
stack(sequence(1,2,3).fast(2), sequence(1,2,3).add(3,4)).firstCycle
|
||||
)
|
||||
})
|
||||
})
|
||||
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).late(0.25).add(2)).firstCycle
|
||||
)
|
||||
})
|
||||
})
|
||||
describe("jux", () => {
|
||||
it("Can juxtapose", () => {
|
||||
assert.deepStrictEqual(
|
||||
pure({a: 1}).jux(fast(2))._sortEventsByPart().firstCycle,
|
||||
stack(pure({a:1, pan: 0}), pure({a:1, pan: 1}).fast(2))._sortEventsByPart().firstCycle
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user