Merge remote-tracking branch 'upstream/main'

This commit is contained in:
Felix Roos 2022-02-05 21:36:36 +01:00
commit 74355e86da
3 changed files with 85 additions and 42 deletions

View File

@ -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',

View File

@ -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}

View File

@ -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
)
})
})
})