mirror of
https://codeberg.org/uzu/strudel
synced 2026-08-03 14:01:56 -04:00
Merge remote-tracking branch 'origin/main' into objectify-midi
This commit is contained in:
+239
-39
@@ -513,11 +513,14 @@ export class Pattern {
|
||||
}
|
||||
|
||||
/**
|
||||
* Assumes a numerical pattern, containing unipolar values in the range 0 ..
|
||||
* 1. Returns a new pattern with values scaled to the given min/max range.
|
||||
* @param {Number} min
|
||||
* @param {Number} max
|
||||
* Assumes a numerical pattern, containing unipolar values in the range 0 .. 1.
|
||||
* Returns a new pattern with values scaled to the given min/max range.
|
||||
* Most useful in combination with continuous patterns.
|
||||
* @name range
|
||||
* @memberof Pattern
|
||||
* @returns Pattern
|
||||
* @example
|
||||
* s("bd sd,hh*4").cutoff(sine.range(500,2000).slow(4)).out()
|
||||
*/
|
||||
range(min, max) {
|
||||
return this.mul(max - min).add(min);
|
||||
@@ -683,6 +686,16 @@ export class Pattern {
|
||||
return func(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Layers the result of the given function(s). Like {@link superimpose}, but without the original pattern:
|
||||
* @name layer
|
||||
* @memberof Pattern
|
||||
* @returns Pattern
|
||||
* @example
|
||||
* "<0 2 4 6 ~ 4 ~ 2 0!3 ~!5>*4"
|
||||
* .layer(x=>x.add("0,2"))
|
||||
* .scale('C minor').note().out()
|
||||
*/
|
||||
layer(...funcs) {
|
||||
return stack(...funcs.map((func) => func(this)));
|
||||
}
|
||||
@@ -734,14 +747,14 @@ export class Pattern {
|
||||
}
|
||||
|
||||
/**
|
||||
* Speed up a pattern by the given factor.
|
||||
* Speed up a pattern by the given factor. Used by "*" in mini notation.
|
||||
*
|
||||
* @name fast
|
||||
* @memberof Pattern
|
||||
* @param {number | Pattern} factor speed up factor
|
||||
* @returns Pattern
|
||||
* @example
|
||||
* seq(e5, b4, d5, c5).fast(2)
|
||||
* s("<bd sd> hh").fast(2).out() // s("[<bd sd> hh]*2").out()
|
||||
*/
|
||||
_fast(factor) {
|
||||
const fastQuery = this.withQueryTime((t) => t.mul(factor));
|
||||
@@ -749,14 +762,14 @@ export class Pattern {
|
||||
}
|
||||
|
||||
/**
|
||||
* Slow down a pattern over the given number of cycles.
|
||||
* Slow down a pattern over the given number of cycles. Like the "/" operator in mini notation.
|
||||
*
|
||||
* @name slow
|
||||
* @memberof Pattern
|
||||
* @param {number | Pattern} factor slow down factor
|
||||
* @returns Pattern
|
||||
* @example
|
||||
* seq(e5, b4, d5, c5).slow(2)
|
||||
* s("<bd sd> hh").slow(2).out() // s("[<bd sd> hh]/2").out()
|
||||
*/
|
||||
_slow(factor) {
|
||||
return this._fast(Fraction(1).div(factor));
|
||||
@@ -795,14 +808,32 @@ export class Pattern {
|
||||
return this._fast(cpm / 60);
|
||||
}
|
||||
|
||||
/**
|
||||
* Nudge a pattern to start earlier in time. Equivalent of Tidal's <~ operator
|
||||
*
|
||||
* @name early
|
||||
* @memberof Pattern
|
||||
* @param {number | Pattern} cycles number of cycles to nudge left
|
||||
* @returns Pattern
|
||||
* @example
|
||||
* "bd ~".stack("hh ~".early(.1)).s().out()
|
||||
*/
|
||||
_early(offset) {
|
||||
// Equivalent of Tidal's <~ operator
|
||||
offset = Fraction(offset);
|
||||
return this.withQueryTime((t) => t.add(offset)).withHapTime((t) => t.sub(offset));
|
||||
}
|
||||
|
||||
/**
|
||||
* Nudge a pattern to start later in time. Equivalent of Tidal's ~> operator
|
||||
*
|
||||
* @name late
|
||||
* @memberof Pattern
|
||||
* @param {number | Pattern} cycles number of cycles to nudge right
|
||||
* @returns Pattern
|
||||
* @example
|
||||
* "bd ~".stack("hh ~".late(.1)).s().out()
|
||||
*/
|
||||
_late(offset) {
|
||||
// Equivalent of Tidal's ~> operator
|
||||
offset = Fraction(offset);
|
||||
return this._early(Fraction(0).sub(offset));
|
||||
}
|
||||
@@ -829,6 +860,17 @@ export class Pattern {
|
||||
return this._zoom(0, t)._slow(t);
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the given structure to the pattern:
|
||||
*
|
||||
* @name struct
|
||||
* @memberof Pattern
|
||||
* @returns Pattern
|
||||
* @example
|
||||
* "c3,eb3,g3"
|
||||
* .struct("x ~ x ~ ~ x ~ x ~ ~ ~ x ~ x ~ ~")
|
||||
* .slow(4).note().out()
|
||||
*/
|
||||
// struct(...binary_pats) {
|
||||
// // Re structure the pattern according to a binary pattern (false values are dropped)
|
||||
// const binary_pat = sequence(binary_pats);
|
||||
@@ -876,6 +918,16 @@ export class Pattern {
|
||||
return this.invert();
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the given function whenever the given pattern is in a true state.
|
||||
* @name when
|
||||
* @memberof Pattern
|
||||
* @param {Pattern} binary_pat
|
||||
* @param {function} func
|
||||
* @returns Pattern
|
||||
* @example
|
||||
* "c3 eb3 g3".when("<0 1>/2", x=>x.sub(5))
|
||||
*/
|
||||
when(binary_pat, func) {
|
||||
//binary_pat = sequence(binary_pat)
|
||||
const true_pat = binary_pat._filterValues(id);
|
||||
@@ -885,10 +937,47 @@ export class Pattern {
|
||||
return stack(with_pat, without_pat);
|
||||
}
|
||||
|
||||
/**
|
||||
* Superimposes the function result on top of the original pattern, delayed by the given time.
|
||||
* @name off
|
||||
* @memberof Pattern
|
||||
* @param {Pattern | number} time offset time
|
||||
* @param {function} func function to apply
|
||||
* @returns Pattern
|
||||
* @example
|
||||
* "c3 eb3 g3".off(1/8, x=>x.add(7))
|
||||
*/
|
||||
off(time_pat, func) {
|
||||
return stack(this, func(this.late(time_pat)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the given function every n cycles.
|
||||
* @name every
|
||||
* @memberof Pattern
|
||||
* @param {number} n how many cycles
|
||||
* @param {function} func function to apply
|
||||
* @returns Pattern
|
||||
* @example
|
||||
* note("c3 d3 e3 g3").every(4, x=>x.rev()).out()
|
||||
*/
|
||||
every(n, func) {
|
||||
const pat = this;
|
||||
const pats = Array(n - 1).fill(pat);
|
||||
// pats.unshift(func(pat));
|
||||
pats.push(func(pat));
|
||||
return slowcatPrime(...pats);
|
||||
}
|
||||
/**
|
||||
* Applies the given function every n cycles, starting from the first cycle.
|
||||
* @name every
|
||||
* @memberof Pattern
|
||||
* @param {number} n how many cycles
|
||||
* @param {function} func function to apply
|
||||
* @returns Pattern
|
||||
* @example
|
||||
* note("c3 d3 e3 g3").every(4, x=>x.rev()).out()
|
||||
*/
|
||||
every(n, func) {
|
||||
const pat = this;
|
||||
const pats = Array(n - 1).fill(pat);
|
||||
@@ -896,6 +985,23 @@ export class Pattern {
|
||||
return slowcatPrime(...pats);
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the given function every n cycles, starting from the last cycle.
|
||||
* @name each
|
||||
* @memberof Pattern
|
||||
* @param {number} n how many cycles
|
||||
* @param {function} func function to apply
|
||||
* @returns Pattern
|
||||
* @example
|
||||
* note("c3 d3 e3 g3").every(4, x=>x.rev()).out()
|
||||
*/
|
||||
each(n, func) {
|
||||
const pat = this;
|
||||
const pats = Array(n - 1).fill(pat);
|
||||
pats.push(func(pat));
|
||||
return slowcatPrime(...pats);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new pattern where every other cycle is played once, twice as
|
||||
* fast, and offset in time by one quarter of a cycle. Creates a kind of
|
||||
@@ -906,6 +1012,15 @@ export class Pattern {
|
||||
return this.when(slowcat(false, true), (x) => fastcat(x, silence)._late(0.25));
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse all haps in a pattern
|
||||
*
|
||||
* @name rev
|
||||
* @memberof Pattern
|
||||
* @returns Pattern
|
||||
* @example
|
||||
* "c3 d3 e3 g3".rev()
|
||||
*/
|
||||
rev() {
|
||||
const pat = this;
|
||||
const query = function (state) {
|
||||
@@ -948,6 +1063,15 @@ export class Pattern {
|
||||
return this.juxBy(1, func);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stacks the given pattern(s) to the current pattern.
|
||||
* @name stack
|
||||
* @memberof Pattern
|
||||
* @example
|
||||
* s("hh*2").stack(
|
||||
* n("c2(3,8)")
|
||||
* ).out()
|
||||
*/
|
||||
stack(...pats) {
|
||||
return stack(this, ...pats);
|
||||
}
|
||||
@@ -956,11 +1080,28 @@ export class Pattern {
|
||||
return sequence(this, ...pats);
|
||||
}
|
||||
|
||||
// shorthand for sequence
|
||||
/**
|
||||
* Appends the given pattern(s) to the current pattern. Synonyms: .sequence .fastcat
|
||||
* @name seq
|
||||
* @memberof Pattern
|
||||
* @example
|
||||
* s("hh*2").seq(
|
||||
* n("c2(3,8)")
|
||||
* ).out()
|
||||
*/
|
||||
seq(...pats) {
|
||||
return sequence(this, ...pats);
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends the given pattern(s) to the next cycle. Synonym: .slowcat
|
||||
* @name cat
|
||||
* @memberof Pattern
|
||||
* @example
|
||||
* s("hh*2").cat(
|
||||
* n("c2(3,8)")
|
||||
* ).out()
|
||||
*/
|
||||
cat(...pats) {
|
||||
return cat(this, ...pats);
|
||||
}
|
||||
@@ -973,6 +1114,16 @@ export class Pattern {
|
||||
return slowcat(this, ...pats);
|
||||
}
|
||||
|
||||
/**
|
||||
* Superimposes the result of the given function(s) on top of the original pattern:
|
||||
* @name superimpose
|
||||
* @memberof Pattern
|
||||
* @returns Pattern
|
||||
* @example
|
||||
* "<0 2 4 6 ~ 4 ~ 2 0!3 ~!5>*4"
|
||||
* .superimpose(x=>x.add(2))
|
||||
* .scale('C minor').note().out()
|
||||
*/
|
||||
superimpose(...funcs) {
|
||||
return this.stack(...funcs.map((func) => func(this)));
|
||||
}
|
||||
@@ -985,24 +1136,67 @@ export class Pattern {
|
||||
return this.stutWith(times, time, (pat, i) => pat.velocity(Math.pow(feedback, i)));
|
||||
}
|
||||
|
||||
// these might change with: https://github.com/tidalcycles/Tidal/issues/902
|
||||
/**
|
||||
* Superimpose and offset multiple times, applying the given function each time.
|
||||
* @name echoWith
|
||||
* @memberof Pattern
|
||||
* @returns Pattern
|
||||
* @param {number} times how many times to repeat
|
||||
* @param {number} time cycle offset between iterations
|
||||
* @param {function} func function to apply, given the pattern and the iteration index
|
||||
* @example
|
||||
* "<0 [2 4]>"
|
||||
* .echoWith(4, 1/8, (p,n) => p.add(n*2))
|
||||
* .scale('C minor').note().legato(.2).out()
|
||||
*/
|
||||
_echoWith(times, time, func) {
|
||||
return stack(...listRange(0, times - 1).map((i) => func(this.late(Fraction(time).mul(i)), i)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Superimpose and offset multiple times, gradually decreasing the velocity
|
||||
* @name echo
|
||||
* @memberof Pattern
|
||||
* @returns Pattern
|
||||
* @example
|
||||
* s("bd sd").echo(3, 1/6, .8).out()
|
||||
*/
|
||||
_echo(times, time, feedback) {
|
||||
return this._echoWith(times, time, (pat, i) => pat.velocity(Math.pow(feedback, i)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Divides a pattern into a given number of subdivisions, plays the subdivisions in order, but increments the starting subdivision each cycle. The pattern wraps to the first subdivision after the last subdivision is played.
|
||||
* @name iter
|
||||
* @memberof Pattern
|
||||
* @returns Pattern
|
||||
* @example
|
||||
* note("0 1 2 3".scale('A minor')).iter(4).out()
|
||||
*/
|
||||
iter(times, back = false) {
|
||||
return slowcat(...listRange(0, times - 1).map((i) => (back ? this.late(i / times) : this.early(i / times))));
|
||||
}
|
||||
|
||||
// known as iter' in tidalcycles
|
||||
/**
|
||||
* Like `iter`, but plays the subdivisions in reverse order. Known as iter' in tidalcycles
|
||||
* @name iterBack
|
||||
* @memberof Pattern
|
||||
* @returns Pattern
|
||||
* @example
|
||||
* note("0 1 2 3".scale('A minor')).iterBack(4).out()
|
||||
*/
|
||||
iterBack(times) {
|
||||
return this.iter(times, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Divides a pattern into a given number of parts, then cycles through those parts in turn, applying the given function to each part in turn (one part per cycle).
|
||||
* @name chunk
|
||||
* @memberof Pattern
|
||||
* @returns Pattern
|
||||
* @example
|
||||
* "0 1 2 3".chunk(4, x=>x.add(7)).scale('A minor').note().out()
|
||||
*/
|
||||
_chunk(n, func, back = false) {
|
||||
const binary = Array(n - 1).fill(false);
|
||||
binary.unshift(true);
|
||||
@@ -1010,6 +1204,14 @@ export class Pattern {
|
||||
return this.when(binary_pat, func);
|
||||
}
|
||||
|
||||
/**
|
||||
* Like `chunk`, but cycles through the parts in reverse order. Known as chunk' in tidalcycles
|
||||
* @name chunkBack
|
||||
* @memberof Pattern
|
||||
* @returns Pattern
|
||||
* @example
|
||||
* "0 1 2 3".chunkBack(4, x=>x.add(7)).scale('A minor').note().out()
|
||||
*/
|
||||
_chunkBack(n, func) {
|
||||
return this._chunk(n, func, true);
|
||||
}
|
||||
@@ -1182,6 +1384,11 @@ Pattern.prototype.patternified = [
|
||||
'striate',
|
||||
'slow',
|
||||
];
|
||||
|
||||
// aliases
|
||||
export const polyrhythm = stack;
|
||||
export const pr = stack;
|
||||
|
||||
// methods that create patterns, which are added to patternified Pattern methods
|
||||
Pattern.prototype.factories = {
|
||||
pure,
|
||||
@@ -1204,12 +1411,11 @@ Pattern.prototype.factories = {
|
||||
// Nothing
|
||||
export const silence = new Pattern((_) => []);
|
||||
|
||||
/** A discrete value that repeats once per cycle:
|
||||
/** A discrete value that repeats once per cycle.
|
||||
*
|
||||
* @param {any} value - The value to repeat
|
||||
* @returns {Pattern}
|
||||
* @example
|
||||
* pure('e4')
|
||||
* pure('e4') // "e4"
|
||||
*/
|
||||
export function pure(value) {
|
||||
function query(state) {
|
||||
@@ -1239,12 +1445,11 @@ export function reify(thing) {
|
||||
return pure(thing);
|
||||
}
|
||||
|
||||
/** The given items are played at the same time at the same length:
|
||||
/** The given items are played at the same time at the same length.
|
||||
*
|
||||
* @param {...any} items - The items to stack
|
||||
* @return {Pattern}
|
||||
* @example
|
||||
* stack(g3, b3, [e4, d4])
|
||||
* stack(g3, b3, [e4, d4]) // "g3,b3,[e4,d4]"
|
||||
*/
|
||||
export function stack(...pats) {
|
||||
// Array test here is to avoid infinite recursions..
|
||||
@@ -1257,7 +1462,6 @@ export function stack(...pats) {
|
||||
*
|
||||
* synonyms: {@link cat}
|
||||
*
|
||||
* @param {...any} items - The items to concatenate
|
||||
* @return {Pattern}
|
||||
* @example
|
||||
* slowcat(e5, b4, [d5, c5])
|
||||
@@ -1313,16 +1517,22 @@ export function fastcat(...pats) {
|
||||
return slowcat(...pats)._fast(pats.length);
|
||||
}
|
||||
|
||||
/** See {@link slowcat} */
|
||||
/** The given items are con**cat**enated, where each one takes one cycle. Synonym: slowcat
|
||||
*
|
||||
* @param {...any} items - The items to concatenate
|
||||
* @return {Pattern}
|
||||
* @example
|
||||
* cat(e5, b4, [d5, c5]) // "<e5 b4 [d5 c5]>"
|
||||
*
|
||||
*/
|
||||
export function cat(...pats) {
|
||||
return slowcat(...pats);
|
||||
}
|
||||
|
||||
/** Like {@link fastcat}, but where each step has a temporal weight:
|
||||
* @param {...Array} items - The items to concatenate
|
||||
/** Like {@link seq}, but each step has a length, relative to the whole.
|
||||
* @return {Pattern}
|
||||
* @example
|
||||
* timeCat([3,e3],[1, g3])
|
||||
* timeCat([3,e3],[1, g3]) // "e3@3 g3"
|
||||
*/
|
||||
export function timeCat(...timepats) {
|
||||
const total = timepats.map((a) => a[0]).reduce((a, b) => a.add(b), Fraction(0));
|
||||
@@ -1341,7 +1551,11 @@ export function sequence(...pats) {
|
||||
return fastcat(...pats);
|
||||
}
|
||||
|
||||
/** See {@link fastcat} */
|
||||
/** Like **cat**, but the items are crammed into one cycle. Synonyms: fastcat, sequence
|
||||
* @example
|
||||
* seq(e5, b4, [d5, c5]) // "e5 b4 [d5 c5]"
|
||||
*
|
||||
*/
|
||||
export function seq(...pats) {
|
||||
return fastcat(...pats);
|
||||
}
|
||||
@@ -1390,20 +1604,6 @@ export function pm(...args) {
|
||||
polymeter(...args);
|
||||
}
|
||||
|
||||
export function polyrhythm(...xs) {
|
||||
const seqs = xs.map((a) => sequence(a));
|
||||
|
||||
if (seqs.length == 0) {
|
||||
return silence;
|
||||
}
|
||||
return stack(...seqs);
|
||||
}
|
||||
|
||||
// alias
|
||||
export function pr(args) {
|
||||
polyrhythm(args);
|
||||
}
|
||||
|
||||
export const add = curry((a, pat) => pat.add(a));
|
||||
export const chop = curry((a, pat) => pat.chop(a));
|
||||
export const chunk = curry((a, pat) => pat.chunk(a));
|
||||
|
||||
Reference in New Issue
Block a user