mirror of
https://github.com/eliasstepanik/strudel-docker.git
synced 2026-01-11 21:58:31 +00:00
Merge branch 'main' of https://github.com/tidalcycles/strudel
This commit is contained in:
commit
ecec96c943
43
strudel.mjs
43
strudel.mjs
@ -294,6 +294,10 @@ class Pattern {
|
||||
return new Pattern(span => this.query(span).filter(hap => value_test(hap.value)))
|
||||
}
|
||||
|
||||
_removeUndefineds() {
|
||||
return(this._filterValues(val => val != undefined))
|
||||
}
|
||||
|
||||
onsetsOnly() {
|
||||
// Returns a new pattern that will only return events where the start
|
||||
// of the 'whole' timespan matches the start of the 'part'
|
||||
@ -507,6 +511,28 @@ class Pattern {
|
||||
return this._early(0-offset)
|
||||
}
|
||||
|
||||
struct(...binary_pats) {
|
||||
// Re structure the pattern according to a binary pattern (false values are dropped)
|
||||
const binary_pat = sequence(binary_pats)
|
||||
return binary_pat.fmap(b => val => b ? val : undefined).appLeft(this)._removeUndefineds()
|
||||
}
|
||||
|
||||
mask(...binary_pats) {
|
||||
// Only let through parts of pattern corresponding to true values in the given binary pattern
|
||||
const binary_pat = sequence(binary_pats)
|
||||
return binary_pat.fmap(b => val => b ? val : undefined).appRight(this)._removeUndefineds()
|
||||
}
|
||||
|
||||
invert() {
|
||||
// Swap true/false in a binary pattern
|
||||
return this.fmap(x => !x)
|
||||
}
|
||||
|
||||
inv() {
|
||||
// alias for invert()
|
||||
return this.invert()
|
||||
}
|
||||
|
||||
when(binary_pat, func) {
|
||||
//binary_pat = sequence(binary_pat)
|
||||
const true_pat = binary_pat._filterValues(id)
|
||||
@ -612,10 +638,13 @@ Pattern.prototype.patternified = ['fast', 'slow', 'early', 'late'];
|
||||
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)
|
||||
|
||||
// Elemental patterns
|
||||
|
||||
// Nothing
|
||||
const silence = new Pattern(_ => [])
|
||||
|
||||
function pure(value) {
|
||||
// Returns a pattern that repeats the given value once per cycle
|
||||
// A discrete value that repeats once per cycle
|
||||
function query(span) {
|
||||
return span.spanCycles.map(subspan => new Hap(Fraction(subspan.begin).wholeCycle(), subspan, value))
|
||||
}
|
||||
@ -623,16 +652,20 @@ function pure(value) {
|
||||
}
|
||||
|
||||
function steady(value) {
|
||||
// A continuous value
|
||||
return new Pattern(span => Hap(undefined, span, value))
|
||||
}
|
||||
|
||||
function reify(thing) {
|
||||
// Tunrs something into a pattern, unless it's already a pattern
|
||||
if (thing?.constructor?.name == "Pattern") {
|
||||
return thing
|
||||
}
|
||||
return pure(thing)
|
||||
}
|
||||
|
||||
// Basic functions for combining patterns
|
||||
|
||||
function stack(...pats) {
|
||||
const reified = pats.map(pat => reify(pat))
|
||||
const query = span => flatten(reified.map(pat => pat.query(span)))
|
||||
@ -682,6 +715,7 @@ function cat(...pats) {
|
||||
}
|
||||
|
||||
function timeCat(...timepats) {
|
||||
// Like cat, but where each step has a temporal 'weight'
|
||||
const total = timepats.map(a => a[0]).reduce((a,b) => a.add(b), Fraction(0))
|
||||
let begin = Fraction(0)
|
||||
const pats = []
|
||||
@ -768,6 +802,10 @@ const off = curry((t, f, pat) => pat.off(t,f))
|
||||
const jux = curry((f, pat) => pat.jux(f))
|
||||
const append = curry((a, pat) => pat.append(a))
|
||||
const superimpose = curry((array, pat) => pat.superimpose(...array))
|
||||
const struct = curry((a, pat) => pat.struct(a))
|
||||
const mask = curry((a, pat) => pat.mask(a))
|
||||
const invert = pat => pat.invert()
|
||||
const inv = pat => pat.inv()
|
||||
|
||||
// problem: curried functions with spread arguments must have pat at the beginning
|
||||
// with this, we cannot keep the pattern open at the end.. solution for now: use array to keep using pat as last arg
|
||||
@ -822,6 +860,7 @@ Pattern.prototype.bootstrap = () => {
|
||||
export {Fraction, TimeSpan, Hap, Pattern,
|
||||
pure, stack, slowcat, fastcat, cat, timeCat, sequence, polymeter, pm, polyrhythm, pr, reify, silence,
|
||||
fast, slow, early, late, rev,
|
||||
add, sub, mul, div, union, every, when, off, jux, append, superimpose
|
||||
add, sub, mul, div, union, every, when, off, jux, append, superimpose,
|
||||
struct, mask, invert, inv
|
||||
}
|
||||
|
||||
|
||||
@ -10,6 +10,9 @@ const { Time } = pkg;
|
||||
const ts = (begin, end) => new TimeSpan(Fraction(begin), Fraction(end));
|
||||
const hap = (whole, part, value) => new Hap(whole, part, value)
|
||||
|
||||
const third = Fraction(1,3)
|
||||
const twothirds = Fraction(2,3)
|
||||
|
||||
describe('TimeSpan', function() {
|
||||
describe('equals()', function() {
|
||||
it('Should be equal to the same value', function() {
|
||||
@ -250,4 +253,58 @@ describe('Pattern', function() {
|
||||
)
|
||||
})
|
||||
})
|
||||
describe('struct()', function() {
|
||||
it('Can restructure a pattern', function() {
|
||||
assert.deepStrictEqual(
|
||||
sequence("a", "b").struct(sequence(true, true, true)).firstCycle,
|
||||
[hap(ts(0,third), ts(0,third), "a"),
|
||||
hap(ts(third, twothirds), ts(third, 0.5), "a"),
|
||||
hap(ts(third, twothirds), ts(0.5, twothirds), "b"),
|
||||
hap(ts(twothirds, 1), ts(twothirds, 1), "b")
|
||||
]
|
||||
)
|
||||
assert.deepStrictEqual(
|
||||
pure("a").struct(sequence(true, [true,false], true)).firstCycle,
|
||||
sequence("a", ["a", silence], "a").firstCycle,
|
||||
)
|
||||
assert.deepStrictEqual(
|
||||
pure("a").struct(sequence(true, [true,false], true).invert()).firstCycle,
|
||||
sequence(silence, [silence, "a"], silence).firstCycle,
|
||||
)
|
||||
assert.deepStrictEqual(
|
||||
pure("a").struct(sequence(true, [true,silence], true)).firstCycle,
|
||||
sequence("a", ["a", silence], "a").firstCycle,
|
||||
)
|
||||
})
|
||||
})
|
||||
describe('mask()', function() {
|
||||
it('Can fragment a pattern', function() {
|
||||
assert.deepStrictEqual(
|
||||
sequence("a", "b").mask(sequence(true, true, true)).firstCycle,
|
||||
[hap(ts(0, 0.5), ts(0,third), "a"),
|
||||
hap(ts(0, 0.5), ts(third, 0.5), "a"),
|
||||
hap(ts(0.5, 1), ts(0.5, twothirds), "b"),
|
||||
hap(ts(0.5, 1), ts(twothirds, 1), "b")
|
||||
]
|
||||
)
|
||||
})
|
||||
it('Can mask off parts of a pattern', function() {
|
||||
assert.deepStrictEqual(
|
||||
sequence(["a", "b"], "c").mask(sequence(true, false)).firstCycle,
|
||||
sequence(["a","b"], silence).firstCycle
|
||||
)
|
||||
assert.deepStrictEqual(
|
||||
sequence("a").mask(sequence(true, false)).firstCycle,
|
||||
[hap(ts(0,1),ts(0,0.5), "a")]
|
||||
)
|
||||
})
|
||||
})
|
||||
describe('invert()', function() {
|
||||
it('Can invert a binary pattern', function() {
|
||||
assert.deepStrictEqual(
|
||||
sequence(true, false, [true, false]).invert().firstCycle,
|
||||
sequence(false, true, [false, true]).firstCycle
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user