mirror of
https://github.com/eliasstepanik/strudel-docker.git
synced 2026-01-11 05:38:34 +00:00
Merge remote-tracking branch 'upstream/main'
This commit is contained in:
commit
74355e86da
@ -1,12 +1,12 @@
|
||||
export const tetris = `stack(sequence(
|
||||
'e5', sequence('b4', 'c5'), 'd5', sequence('c5', 'b4'),
|
||||
'a4', sequence('a4', 'c5'), 'e5', sequence('d5', 'c5'),
|
||||
'b4', sequence(silence(), 'c5'), 'd5', 'e5',
|
||||
'c5', 'a4', 'a4', silence(),
|
||||
sequence(silence(), 'd5'), sequence(silence(), 'f5'), 'a5', sequence('g5', 'f5'),
|
||||
'e5', sequence(silence(), 'c5'), 'e5', sequence('d5', 'c5'),
|
||||
'b4', sequence(silence, 'c5'), 'd5', 'e5',
|
||||
'c5', 'a4', 'a4', silence,
|
||||
sequence(silence, 'd5'), sequence(silence, 'f5'), 'a5', sequence('g5', 'f5'),
|
||||
'e5', sequence(silence, 'c5'), 'e5', sequence('d5', 'c5'),
|
||||
'b4', sequence('b4', 'c5'), 'd5', 'e5',
|
||||
'c5', 'a4', 'a4', silence()),
|
||||
'c5', 'a4', 'a4', silence),
|
||||
sequence(
|
||||
'e2', 'e3', 'e2', 'e3', 'e2', 'e3', 'e2', 'e3',
|
||||
'a2', 'a3', 'a2', 'a3', 'a2', 'a3', 'a2', 'a3',
|
||||
|
||||
93
strudel.mjs
93
strudel.mjs
@ -11,6 +11,19 @@ function flatten(arr) {
|
||||
|
||||
var id = a => a
|
||||
|
||||
function curry(func) {
|
||||
return function curried(...args) {
|
||||
if (args.length >= func.length) {
|
||||
return func.apply(this, args)
|
||||
}
|
||||
else {
|
||||
return function(...args2) {
|
||||
return curried.apply(this, args.concat(args2))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the start of the cycle.
|
||||
Fraction.prototype.sam = function() {
|
||||
return Fraction(Math.floor(this))
|
||||
@ -42,6 +55,10 @@ Fraction.prototype.gte = function(other) {
|
||||
return this.compare(other) >= 0
|
||||
}
|
||||
|
||||
Fraction.prototype.eq = function(other) {
|
||||
return this.compare(other) == 0
|
||||
}
|
||||
|
||||
Fraction.prototype.max = function(other) {
|
||||
return this.gt(other) ? this : other
|
||||
}
|
||||
@ -51,7 +68,11 @@ Fraction.prototype.min = function(other) {
|
||||
}
|
||||
|
||||
Fraction.prototype.show = function () {
|
||||
return this.n + "/" + this.d
|
||||
return this.n + "/" + this.d
|
||||
}
|
||||
|
||||
Fraction.prototype.or = function(other) {
|
||||
return this.eq(0) ? other : this
|
||||
}
|
||||
|
||||
class TimeSpan {
|
||||
@ -225,6 +246,10 @@ class Pattern {
|
||||
return this.withEventSpan(span => span.withTime(func))
|
||||
}
|
||||
|
||||
_withEvents(func) {
|
||||
return new Pattern(span => func(this.query(span)))
|
||||
}
|
||||
|
||||
withValue(func) {
|
||||
// Returns a new pattern, with the function applied to the value of
|
||||
// each event. It has the alias 'fmap'.
|
||||
@ -326,6 +351,10 @@ class Pattern {
|
||||
return this.query(new TimeSpan(Fraction(0), Fraction(1)))
|
||||
}
|
||||
|
||||
_sortEventsByPart() {
|
||||
return this._withEvents(events => events.sort((a,b) => a.part.begin.sub(b.part.begin).or(a.part.end.sub(b.part.end)).or(a.whole.begin.sub(b.whole.begin).or(a.whole.end.sub(b.whole.end)))))
|
||||
}
|
||||
|
||||
_opleft(other, func) {
|
||||
return this.fmap(func).appLeft(reify(other))
|
||||
}
|
||||
@ -353,7 +382,7 @@ class Pattern {
|
||||
var match = function (a) {
|
||||
return func(a.value).query(a.part).map(b => withWhole(a, b))
|
||||
}
|
||||
return flatten(pat_val.query(span).map(match))
|
||||
return flatten(pat_val.query(span).map(a => match(a)))
|
||||
}
|
||||
return new Pattern(query)
|
||||
}
|
||||
@ -430,19 +459,15 @@ class Pattern {
|
||||
var false_pat = binary_pat._filterValues(val => !val)
|
||||
var with_pat = true_pat.fmap(_ => y => y).appRight(func(this))
|
||||
var without_pat = false_pat.fmap(_ => y => y).appRight(this)
|
||||
return stack([with_pat, without_pat])
|
||||
return stack(with_pat, without_pat)
|
||||
}
|
||||
|
||||
off(time_pat, func) {
|
||||
return stack([this, func(this._early(time_pat))])
|
||||
}
|
||||
|
||||
off(time_pat, func) {
|
||||
return stack(this, func(this.early(time_pat)))
|
||||
}
|
||||
|
||||
every(n, func) {
|
||||
const pats = Array(n-1).fill(this)
|
||||
var pats = Array(n-1).fill(this)
|
||||
pats.unshift(this)
|
||||
return slowcat(...pats)
|
||||
}
|
||||
@ -485,12 +510,7 @@ class Pattern {
|
||||
}
|
||||
}
|
||||
|
||||
function reify(thing) {
|
||||
if (thing.constructor.name == "Pattern") {
|
||||
return thing
|
||||
}
|
||||
return pure(thing)
|
||||
}
|
||||
const silence = new Pattern(_ => [])
|
||||
|
||||
function pure(value) {
|
||||
// Returns a pattern that repeats the given value once per cycle
|
||||
@ -504,8 +524,15 @@ function steady(value) {
|
||||
return new Pattern(span => Hap(undefined, span, value))
|
||||
}
|
||||
|
||||
function reify(thing) {
|
||||
if (thing.constructor.name == "Pattern") {
|
||||
return thing
|
||||
}
|
||||
return pure(thing)
|
||||
}
|
||||
|
||||
function stack(...pats) {
|
||||
pats = pats.map(reify)
|
||||
var pats = pats.map(pat => reify(pat))
|
||||
var query = function(span) {
|
||||
return flatten(pats.map(pat => pat.query(span)))
|
||||
}
|
||||
@ -537,7 +564,7 @@ function cat(...pats) {
|
||||
function _sequenceCount(x) {
|
||||
if(Array.isArray(x)) {
|
||||
if (x.length == 0) {
|
||||
return [silence(),0]
|
||||
return [silence,0]
|
||||
}
|
||||
if (x.length == 1) {
|
||||
return _sequenceCount(x[0])
|
||||
@ -552,9 +579,9 @@ function sequence(...xs) {
|
||||
}
|
||||
|
||||
function polymeter(steps=0, ...args) {
|
||||
var seqs = args.map(_sequenceCount)
|
||||
var seqs = args.map(a => _sequenceCount(a))
|
||||
if (seqs.length == 0) {
|
||||
return silence()
|
||||
return silence
|
||||
}
|
||||
if (steps == 0) {
|
||||
steps = seqs[0][1]
|
||||
@ -574,25 +601,25 @@ function polymeter(steps=0, ...args) {
|
||||
return stack(pats)
|
||||
}
|
||||
|
||||
function silence() {
|
||||
return new Pattern(_ => [])
|
||||
// alias
|
||||
function pm(args) {
|
||||
polymeter(args)
|
||||
}
|
||||
|
||||
// # alias
|
||||
// pm = polymeter
|
||||
function polyrhythm(...xs) {
|
||||
var seqs = xs.map(a => sequence(a))
|
||||
|
||||
// def polyrhythm(*xs):
|
||||
// seqs = [sequence(x) for x in xs]
|
||||
|
||||
// if len(seqs) == 0:
|
||||
// return silence()
|
||||
|
||||
// return stack(seqs)
|
||||
|
||||
// # alias
|
||||
// pr = polyrhythm
|
||||
if (seqs.length == 0) {
|
||||
return silence
|
||||
}
|
||||
return stack(...seqs)
|
||||
}
|
||||
|
||||
// alias
|
||||
function pr(args) {
|
||||
polyrhythm(args)
|
||||
}
|
||||
|
||||
export {Fraction, TimeSpan, Hap, Pattern,
|
||||
pure, reify, stack, slowcat, fastcat, cat, sequence, polymeter, silence}
|
||||
pure, stack, slowcat, fastcat, cat, sequence, polymeter, pm, polyrhythm, pr, reify, silence}
|
||||
|
||||
|
||||
@ -2,7 +2,7 @@ import Fraction from 'fraction.js'
|
||||
|
||||
import { strict as assert } from 'assert';
|
||||
|
||||
import {TimeSpan, Hap, Pattern, pure, stack, fastcat, slowcat, cat, sequence} from "../strudel.mjs";
|
||||
import {TimeSpan, Hap, Pattern, pure, stack, fastcat, slowcat, cat, sequence, polyrhythm} from "../strudel.mjs";
|
||||
|
||||
describe('TimeSpan', function() {
|
||||
describe('equals()', function() {
|
||||
@ -78,7 +78,7 @@ describe('Pattern', function() {
|
||||
})
|
||||
describe('stack()', function () {
|
||||
it('Can stack things', function () {
|
||||
assert.deepStrictEqual(stack([pure("a"), pure("b"), pure("c")]).firstCycle.map(h => h.value), ["a", "b", "c"])
|
||||
assert.deepStrictEqual(stack(pure("a"), pure("b"), pure("c")).firstCycle.map(h => h.value), ["a", "b", "c"])
|
||||
})
|
||||
})
|
||||
describe('_fast()', function () {
|
||||
@ -120,9 +120,25 @@ describe('Pattern', function() {
|
||||
assert.deepStrictEqual(fastcat([pure("a"), pure("b"), pure("c")]).rev().firstCycle.sort((a,b) => a.part.begin.sub(b.part.begin)).map(a => a.value), ["c", "b","a"])
|
||||
})
|
||||
})
|
||||
describe('sequence()', function () {
|
||||
it('Can work like fastcat', function () {
|
||||
describe('sequence()', () => {
|
||||
it('Can work like fastcat', () => {
|
||||
assert.deepStrictEqual(sequence(1,2,3).firstCycle, fastcat([pure(1), pure(2), pure(3)]).firstCycle)
|
||||
})
|
||||
})
|
||||
describe('polyrhythm()', () => {
|
||||
it('Can layer up cycles', () => {
|
||||
assert.deepStrictEqual(
|
||||
polyrhythm(["a","b"],["c"])._sortEventsByPart().firstCycle,
|
||||
stack([fastcat(pure("a"),pure("b")),pure("c")])._sortEventsByPart().firstCycle
|
||||
)
|
||||
})
|
||||
})
|
||||
describe('every()', () => {
|
||||
it('Can apply a function every 3rd time', () => {
|
||||
assert.deepStrictEqual(
|
||||
pure("a").every(3, x => x._fast(2)._fast(3)).firstCycle,
|
||||
sequence(sequence("a", "a"), "a", "a").firstCycle
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user