Merge branch 'tidalcycles:main' into phaser
This commit is contained in:
commit
c89f91215c
33 changed files with 2184 additions and 1140 deletions
|
|
@ -1318,6 +1318,17 @@ generic_params.forEach(([names, ...aliases]) => {
|
|||
controls.createParams = (...names) =>
|
||||
names.reduce((acc, name) => Object.assign(acc, { [name]: controls.createParam(name) }), {});
|
||||
|
||||
/**
|
||||
* ADSR envelope: Combination of Attack, Decay, Sustain, and Release.
|
||||
*
|
||||
* @name adsr
|
||||
* @param {number | Pattern} time attack time in seconds
|
||||
* @param {number | Pattern} time decay time in seconds
|
||||
* @param {number | Pattern} gain sustain level (0 to 1)
|
||||
* @param {number | Pattern} time release time in seconds
|
||||
* @example
|
||||
* note("<c3 bb2 f3 eb3>").sound("sawtooth").lpf(600).adsr(".1:.1:.5:.2")
|
||||
*/
|
||||
controls.adsr = register('adsr', (adsr, pat) => {
|
||||
adsr = !Array.isArray(adsr) ? [adsr] : adsr;
|
||||
const [attack, decay, sustain, release] = adsr;
|
||||
|
|
|
|||
|
|
@ -2100,23 +2100,43 @@ export const { iterBack, iterback } = register(['iterBack', 'iterback'], functio
|
|||
return _iter(times, pat, true);
|
||||
});
|
||||
|
||||
/**
|
||||
* Repeats each cycle the given number of times.
|
||||
* @name repeatCycles
|
||||
* @memberof Pattern
|
||||
* @returns Pattern
|
||||
* @example
|
||||
* note(irand(12).add(34)).segment(4).repeatCycles(2).s("gm_acoustic_guitar_nylon")
|
||||
*/
|
||||
const _repeatCycles = function (n, pat) {
|
||||
return slowcat(...Array(n).fill(pat));
|
||||
};
|
||||
|
||||
const { repeatCycles } = register('repeatCycles', _repeatCycles);
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @synonyms slowChunk, slowchunk
|
||||
* @memberof Pattern
|
||||
* @returns Pattern
|
||||
* @example
|
||||
* "0 1 2 3".chunk(4, x=>x.add(7)).scale('A minor').note()
|
||||
*/
|
||||
const _chunk = function (n, func, pat, back = false) {
|
||||
const _chunk = function (n, func, pat, back = false, fast = false) {
|
||||
const binary = Array(n - 1).fill(false);
|
||||
binary.unshift(true);
|
||||
const binary_pat = _iter(n, sequence(...binary), back);
|
||||
// Invert the 'back' because we want to shift the pattern forwards,
|
||||
// and so time backwards
|
||||
const binary_pat = _iter(n, sequence(...binary), !back);
|
||||
if (!fast) {
|
||||
pat = pat.repeatCycles(n);
|
||||
}
|
||||
return pat.when(binary_pat, func);
|
||||
};
|
||||
|
||||
export const chunk = register('chunk', function (n, func, pat) {
|
||||
return _chunk(n, func, pat, false);
|
||||
const { chunk, slowchunk, slowChunk } = register(['chunk', 'slowchunk', 'slowChunk'], function (n, func, pat) {
|
||||
return _chunk(n, func, pat, false, false);
|
||||
});
|
||||
|
||||
/**
|
||||
|
|
@ -2132,6 +2152,21 @@ export const { chunkBack, chunkback } = register(['chunkBack', 'chunkback'], fun
|
|||
return _chunk(n, func, pat, true);
|
||||
});
|
||||
|
||||
/**
|
||||
* Like `chunk`, but the cycles of the source pattern aren't repeated
|
||||
* for each set of chunks.
|
||||
* @name fastChunk
|
||||
* @synonyms fastchunk
|
||||
* @memberof Pattern
|
||||
* @returns Pattern
|
||||
* @example
|
||||
* "<0 8> 1 2 3 4 5 6 7".fastChunk(4, x => x.color('red')).slow(4).scale("C2:major").note()
|
||||
.s("folkharp")
|
||||
*/
|
||||
const { fastchunk, fastChunk } = register(['fastchunk', 'fastChunk'], function (n, func, pat) {
|
||||
return _chunk(n, func, pat, false, true);
|
||||
});
|
||||
|
||||
// TODO - redefine elsewhere in terms of mask
|
||||
export const bypass = register('bypass', function (on, pat) {
|
||||
on = Boolean(parseInt(on));
|
||||
|
|
@ -2357,3 +2392,29 @@ export const ref = (accessor) =>
|
|||
pure(1)
|
||||
.withValue(() => reify(accessor()))
|
||||
.innerJoin();
|
||||
|
||||
let fadeGain = (p) => (p < 0.5 ? 1 : 1 - (p - 0.5) / 0.5);
|
||||
|
||||
/**
|
||||
* Cross-fades between left and right from 0 to 1:
|
||||
* - 0 = (full left, no right)
|
||||
* - .5 = (both equal)
|
||||
* - 1 = (no left, full right)
|
||||
*
|
||||
* @name xfade
|
||||
* @example
|
||||
* xfade(s("bd*2"), "<0 .25 .5 .75 1>", s("hh*8"))
|
||||
*/
|
||||
export let xfade = (a, pos, b) => {
|
||||
pos = reify(pos);
|
||||
a = reify(a);
|
||||
b = reify(b);
|
||||
let gaina = pos.fmap((v) => ({ gain: fadeGain(v) }));
|
||||
let gainb = pos.fmap((v) => ({ gain: fadeGain(1 - v) }));
|
||||
return stack(a.mul(gaina), b.mul(gainb));
|
||||
};
|
||||
|
||||
// the prototype version is actually flipped so left/right makes sense
|
||||
Pattern.prototype.xfade = function (pos, b) {
|
||||
return xfade(this, pos, b);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1003,4 +1003,23 @@ describe('Pattern', () => {
|
|||
);
|
||||
});
|
||||
});
|
||||
describe('chunk', () => {
|
||||
it('Processes each cycle of the source pattern multiple times, once for each chunk', () => {
|
||||
expect(sequence(0, 1, 2, 3).slow(2).chunk(2, add(10)).fast(4).firstCycleValues).toStrictEqual([
|
||||
10, 1, 0, 11, 12, 3, 2, 13,
|
||||
]);
|
||||
});
|
||||
});
|
||||
describe('fastChunk', () => {
|
||||
it('Unlike chunk, cycles of the source pattern proceed cycle-by-cycle', () => {
|
||||
expect(sequence(0, 1, 2, 3).slow(2).fastChunk(2, add(10)).fast(4).firstCycleValues).toStrictEqual([
|
||||
10, 1, 2, 13, 10, 1, 2, 13,
|
||||
]);
|
||||
});
|
||||
});
|
||||
describe('repeatCycles', () => {
|
||||
it('Repeats each cycle of the source pattern the given number of times', () => {
|
||||
expect(slowcat(0, 1).repeatCycles(2).fast(6).firstCycleValues).toStrictEqual([0, 0, 1, 1, 0, 0]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue