diff --git a/packages/core/pattern.mjs b/packages/core/pattern.mjs index dcf5da1b..370776f3 100644 --- a/packages/core/pattern.mjs +++ b/packages/core/pattern.mjs @@ -2392,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); +}; diff --git a/test/__snapshots__/examples.test.mjs.snap b/test/__snapshots__/examples.test.mjs.snap index 0963638f..e920d931 100644 --- a/test/__snapshots__/examples.test.mjs.snap +++ b/test/__snapshots__/examples.test.mjs.snap @@ -5235,6 +5235,51 @@ exports[`runs examples > example "withValue" example index 0 1`] = ` ] `; +exports[`runs examples > example "xfade" example index 0 1`] = ` +[ + "[ 0/1 → 1/8 | s:hh gain:0 ]", + "[ 0/1 → 1/2 | s:bd gain:1 ]", + "[ 1/8 → 1/4 | s:hh gain:0 ]", + "[ 1/4 → 3/8 | s:hh gain:0 ]", + "[ 3/8 → 1/2 | s:hh gain:0 ]", + "[ 1/2 → 5/8 | s:hh gain:0 ]", + "[ 1/2 → 1/1 | s:bd gain:1 ]", + "[ 5/8 → 3/4 | s:hh gain:0 ]", + "[ 3/4 → 7/8 | s:hh gain:0 ]", + "[ 7/8 → 1/1 | s:hh gain:0 ]", + "[ 1/1 → 9/8 | s:hh gain:0.5 ]", + "[ 1/1 → 3/2 | s:bd gain:1 ]", + "[ 9/8 → 5/4 | s:hh gain:0.5 ]", + "[ 5/4 → 11/8 | s:hh gain:0.5 ]", + "[ 11/8 → 3/2 | s:hh gain:0.5 ]", + "[ 3/2 → 13/8 | s:hh gain:0.5 ]", + "[ 3/2 → 2/1 | s:bd gain:1 ]", + "[ 13/8 → 7/4 | s:hh gain:0.5 ]", + "[ 7/4 → 15/8 | s:hh gain:0.5 ]", + "[ 15/8 → 2/1 | s:hh gain:0.5 ]", + "[ 2/1 → 17/8 | s:hh gain:1 ]", + "[ 2/1 → 5/2 | s:bd gain:1 ]", + "[ 17/8 → 9/4 | s:hh gain:1 ]", + "[ 9/4 → 19/8 | s:hh gain:1 ]", + "[ 19/8 → 5/2 | s:hh gain:1 ]", + "[ 5/2 → 21/8 | s:hh gain:1 ]", + "[ 5/2 → 3/1 | s:bd gain:1 ]", + "[ 21/8 → 11/4 | s:hh gain:1 ]", + "[ 11/4 → 23/8 | s:hh gain:1 ]", + "[ 23/8 → 3/1 | s:hh gain:1 ]", + "[ 3/1 → 25/8 | s:hh gain:1 ]", + "[ 3/1 → 7/2 | s:bd gain:0.5 ]", + "[ 25/8 → 13/4 | s:hh gain:1 ]", + "[ 13/4 → 27/8 | s:hh gain:1 ]", + "[ 27/8 → 7/2 | s:hh gain:1 ]", + "[ 7/2 → 29/8 | s:hh gain:1 ]", + "[ 7/2 → 4/1 | s:bd gain:0.5 ]", + "[ 29/8 → 15/4 | s:hh gain:1 ]", + "[ 15/4 → 31/8 | s:hh gain:1 ]", + "[ 31/8 → 4/1 | s:hh gain:1 ]", +] +`; + exports[`runs examples > example "zoom" example index 0 1`] = ` [ "[ 0/1 → 1/6 | s:hh ]", diff --git a/website/src/pages/learn/effects.mdx b/website/src/pages/learn/effects.mdx index 30804240..b1323a8e 100644 --- a/website/src/pages/learn/effects.mdx +++ b/website/src/pages/learn/effects.mdx @@ -156,6 +156,10 @@ There is one filter envelope for each filter type and thus one set of envelope f +## xfade + + + # Panning ## jux