diff --git a/packages/core/pattern.mjs b/packages/core/pattern.mjs index 15425397..c5f7a6be 100644 --- a/packages/core/pattern.mjs +++ b/packages/core/pattern.mjs @@ -1361,6 +1361,49 @@ export class Pattern { } } +////////////////////////////////////////////////////////////////////// +// functions relating to chords/patterns of lists + +// returns Array where each list of haps satisfies eq +function groupHapsBy(eq, haps) { + let groups = []; + haps.forEach((hap) => { + const match = groups.findIndex(([other]) => eq(hap, other)); + if (match === -1) { + groups.push([hap]); + } else { + groups[match].push(hap); + } + }); + return groups; +} + +// congruent haps = haps with equal spans +const congruent = (a, b) => a.spanEquals(b); +// Pattern> -> Pattern> +// returned pattern contains arrays of congruent haps +Pattern.prototype.collect = function () { + return this.withHaps((haps) => + groupHapsBy(congruent, haps).map((_haps) => new Hap(_haps[0].whole, _haps[0].part, _haps, {})), + ); +}; + +// applies func to each array of congruent haps +Pattern.prototype.arpWith = function (func) { + return this.collect() + .fmap((v) => reify(func(v))) + .squeezeJoin() + .withHap((h) => new Hap(h.whole, h.part, h.value.value, h.combineContext(h.value))); +}; + +// applies pattern of indices to each array of congruent haps +Pattern.prototype.arp = function (pat) { + return this.arpWith((haps) => pat.fmap((i) => haps[i % haps.length])); +}; + +////////////////////////////////////////////////////////////////////// +// compose matrix functions + // TODO - adopt value.mjs fully.. function _composeOp(a, b, func) { function _nonFunctionObject(x) { diff --git a/repl/src/test/__snapshots__/tunes.test.mjs.snap b/repl/src/test/__snapshots__/tunes.test.mjs.snap index 1de13d99..4097dc7a 100644 --- a/repl/src/test/__snapshots__/tunes.test.mjs.snap +++ b/repl/src/test/__snapshots__/tunes.test.mjs.snap @@ -39,6 +39,35 @@ exports[`renders tunes > tune: amensister 1`] = ` ] `; +exports[`renders tunes > tune: arpoon 1`] = ` +[ + "0/1 -> 2/3: {\\"note\\":55.000528662214684,\\"cutoff\\":509.2515887569149,\\"resonance\\":12,\\"gain\\":0.5,\\"decay\\":0.16,\\"sustain\\":0.5,\\"delay\\":0.2,\\"room\\":0.5,\\"pan\\":0.5249999999999999,\\"s\\":\\"piano\\",\\"clip\\":1}", + "0/1 -> 2/3: {\\"note\\":55.000528662214684,\\"cutoff\\":509.2515887569149,\\"resonance\\":12,\\"gain\\":0.8,\\"decay\\":0.16,\\"sustain\\":0.5,\\"delay\\":0.2,\\"room\\":0.5,\\"pan\\":0.5249999999999999,\\"s\\":\\"piano\\",\\"clip\\":1}", + "0/1 -> 2/3: {\\"note\\":55.000528662214684,\\"cutoff\\":509.2515887569149,\\"resonance\\":12,\\"gain\\":0.5,\\"decay\\":0.16,\\"sustain\\":0.5,\\"delay\\":0.2,\\"room\\":0.5,\\"pan\\":0.5249999999999999,\\"s\\":\\"piano\\",\\"clip\\":1}", + "0/1 -> 2/3: {\\"note\\":64.00052866221468,\\"cutoff\\":509.2515887569149,\\"resonance\\":12,\\"gain\\":0.5,\\"decay\\":0.16,\\"sustain\\":0.5,\\"delay\\":0.2,\\"room\\":0.5,\\"pan\\":0.5249999999999999,\\"s\\":\\"piano\\",\\"clip\\":1}", + "0/1 -> 2/3: {\\"note\\":64.00052866221468,\\"cutoff\\":509.2515887569149,\\"resonance\\":12,\\"gain\\":0.8,\\"decay\\":0.16,\\"sustain\\":0.5,\\"delay\\":0.2,\\"room\\":0.5,\\"pan\\":0.5249999999999999,\\"s\\":\\"piano\\",\\"clip\\":1}", + "0/1 -> 2/3: {\\"note\\":64.00052866221468,\\"cutoff\\":509.2515887569149,\\"resonance\\":12,\\"gain\\":0.5,\\"decay\\":0.16,\\"sustain\\":0.5,\\"delay\\":0.2,\\"room\\":0.5,\\"pan\\":0.5249999999999999,\\"s\\":\\"piano\\",\\"clip\\":1}", + "1/3 -> 1/1: {\\"note\\":60.003688107962134,\\"cutoff\\":564.5418893373399,\\"resonance\\":12,\\"gain\\":0.8,\\"decay\\":0.16,\\"sustain\\":0.5,\\"delay\\":0.2,\\"room\\":0.5,\\"pan\\":0.5799038105676657,\\"s\\":\\"piano\\",\\"clip\\":1}", + "1/3 -> 1/1: {\\"note\\":60.003688107962134,\\"cutoff\\":564.5418893373399,\\"resonance\\":12,\\"gain\\":0.5,\\"decay\\":0.16,\\"sustain\\":0.5,\\"delay\\":0.2,\\"room\\":0.5,\\"pan\\":0.5799038105676657,\\"s\\":\\"piano\\",\\"clip\\":1}", + "1/3 -> 1/1: {\\"note\\":60.003688107962134,\\"cutoff\\":564.5418893373399,\\"resonance\\":12,\\"gain\\":0.8,\\"decay\\":0.16,\\"sustain\\":0.5,\\"delay\\":0.2,\\"room\\":0.5,\\"pan\\":0.5799038105676657,\\"s\\":\\"piano\\",\\"clip\\":1}", + "2/3 -> 4/3: {\\"note\\":59.010756146386846,\\"cutoff\\":688.232561769837,\\"resonance\\":12,\\"gain\\":0.5,\\"decay\\":0.16,\\"sustain\\":0.5,\\"delay\\":0.2,\\"room\\":0.5,\\"pan\\":0.6,\\"s\\":\\"piano\\",\\"clip\\":1}", + "2/3 -> 4/3: {\\"note\\":59.010756146386846,\\"cutoff\\":688.232561769837,\\"resonance\\":12,\\"gain\\":0.8,\\"decay\\":0.16,\\"sustain\\":0.5,\\"delay\\":0.2,\\"room\\":0.5,\\"pan\\":0.6,\\"s\\":\\"piano\\",\\"clip\\":1}", + "2/3 -> 4/3: {\\"note\\":59.010756146386846,\\"cutoff\\":688.232561769837,\\"resonance\\":12,\\"gain\\":0.5,\\"decay\\":0.16,\\"sustain\\":0.5,\\"delay\\":0.2,\\"room\\":0.5,\\"pan\\":0.6,\\"s\\":\\"piano\\",\\"clip\\":1}", + "2/3 -> 4/3: {\\"note\\":59.010756146386846,\\"cutoff\\":688.232561769837,\\"resonance\\":12,\\"gain\\":0.8,\\"decay\\":0.16,\\"sustain\\":0.5,\\"delay\\":0.2,\\"room\\":0.5,\\"pan\\":0.6,\\"s\\":\\"piano\\",\\"clip\\":1}", + "2/3 -> 4/3: {\\"note\\":64.01075614638685,\\"cutoff\\":688.232561769837,\\"resonance\\":12,\\"gain\\":0.5,\\"decay\\":0.16,\\"sustain\\":0.5,\\"delay\\":0.2,\\"room\\":0.5,\\"pan\\":0.6,\\"s\\":\\"piano\\",\\"clip\\":1}", + "2/3 -> 4/3: {\\"note\\":64.01075614638685,\\"cutoff\\":688.232561769837,\\"resonance\\":12,\\"gain\\":0.8,\\"decay\\":0.16,\\"sustain\\":0.5,\\"delay\\":0.2,\\"room\\":0.5,\\"pan\\":0.6,\\"s\\":\\"piano\\",\\"clip\\":1}", + "2/3 -> 4/3: {\\"note\\":64.01075614638685,\\"cutoff\\":688.232561769837,\\"resonance\\":12,\\"gain\\":0.5,\\"decay\\":0.16,\\"sustain\\":0.5,\\"delay\\":0.2,\\"room\\":0.5,\\"pan\\":0.6,\\"s\\":\\"piano\\",\\"clip\\":1}", + "2/3 -> 4/3: {\\"note\\":64.01075614638685,\\"cutoff\\":688.232561769837,\\"resonance\\":12,\\"gain\\":0.8,\\"decay\\":0.16,\\"sustain\\":0.5,\\"delay\\":0.2,\\"room\\":0.5,\\"pan\\":0.6,\\"s\\":\\"piano\\",\\"clip\\":1}", + "0/1 -> 1/1: {\\"note\\":33,\\"s\\":\\"sawtooth\\",\\"clip\\":1,\\"cutoff\\":300}", + "0/1 -> 1/1: {\\"note\\":33.12,\\"s\\":\\"sawtooth\\",\\"clip\\":1,\\"cutoff\\":300}", + "0/1 -> 1/2: {\\"s\\":\\"bd\\",\\"bank\\":\\"RolandTR909\\",\\"gain\\":0.5}", + "1/2 -> 1/1: {\\"s\\":\\"bd\\",\\"bank\\":\\"RolandTR909\\",\\"gain\\":0.5}", + "1/2 -> 2/3: {\\"s\\":\\"hh\\",\\"bank\\":\\"RolandTR909\\",\\"gain\\":0.5}", + "2/3 -> 5/6: {\\"s\\":\\"hh\\",\\"bank\\":\\"RolandTR909\\",\\"gain\\":0.5}", + "5/6 -> 1/1: {\\"s\\":\\"hh\\",\\"bank\\":\\"RolandTR909\\",\\"gain\\":0.5}", +] +`; + exports[`renders tunes > tune: barryHarris 1`] = ` [ "0/1 -> 2/1: {\\"note\\":\\"C3\\",\\"clip\\":1,\\"s\\":\\"piano\\",\\"release\\":0.1,\\"pan\\":0.4722222222222222}", diff --git a/repl/src/tunes.mjs b/repl/src/tunes.mjs index 1b09a977..fcb7b4db 100644 --- a/repl/src/tunes.mjs +++ b/repl/src/tunes.mjs @@ -818,3 +818,21 @@ stack( , s("bd*2,[~ hh]*2,~ cp").bank('RolandTR909') )`; + +export const arpoon = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ +// "Arpoon" by Felix Roos +await samples('github:tidalcycles/Dirt-Samples/master') + +"< C7 F^7 [Fm7 E7b9]>".voicings() + .arp("[0,3] 2 [1,3] 2".fast(3)).legato(2) + .add(perlin.range(0,0.2)).sub("<0 -12>/8") + .note().cutoff(perlin.range(500,4000)).resonance(12) + .gain("<.5 .8>*16") + .decay(.16).sustain(0.5) + .delay(.2) + .room(.5).pan(sine.range(.3,.6)) + .s('piano').clip(1) + .stack("<!2 F2 [F2 E2]>".add.out("0 -5".fast(2)).add("0,.12").note().s('sawtooth').clip(1).cutoff(300)) + .slow(4) + .stack(s("bd*4, [~ [hh hh? hh?]]*2,~ [sd ~ [sd:2? bd?]]").arp("0|1").bank('RolandTR909').gain(.5).slow(2)) +`;