1/* 2euclid.mjs - Bjorklund/Euclidean/Diaspora rhythms 3Copyright (C) 2023 Rohan Drape and strudel contributors 4 5See <https://codeberg.org/uzu/strudel/src/branch/main/packages/core/euclid.mjs> for authors of this file. 6 7The Bjorklund algorithm implementation is ported from the Haskell Music Theory Haskell module by Rohan Drape - 8https://rohandrape.net/?t=hmt 9 10This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>. 11*/ 12 13import { timeCat, register, silence, stack, pure, _morph } from './pattern.mjs'; 14import { rotate, flatten, splitAt, zipWith } from './util.mjs'; 15import Fraction, { lcm } from './fraction.mjs'; 16 17const left = function (n, x) { 18 const [ons, offs] = n; 19 const [xs, ys] = x; 20 const [_xs, __xs] = splitAt(offs, xs); 21 return [ 22 [offs, ons - offs], 23 [zipWith((a, b) => a.concat(b), _xs, ys), __xs], 24 ]; 25}; 26 27const right = function (n, x) { 28 const [ons, offs] = n; 29 const [xs, ys] = x; 30 const [_ys, __ys] = splitAt(ons, ys); 31 const result = [ 32 [ons, offs - ons], 33 [zipWith((a, b) => a.concat(b), xs, _ys), __ys], 34 ]; 35 return result; 36}; 37 38const _bjorklund = function (n, x) { 39 const [ons, offs] = n; 40 return Math.min(ons, offs) <= 1 ? [n, x] : _bjorklund(...(ons > offs ? left(n, x) : right(n, x))); 41}; 42 43export const bjorklund = function (ons, steps) { 44 const inverted = ons < 0; 45 const absOns = Math.abs(ons); 46 const offs = steps - absOns; 47 const ones = Array(absOns).fill([1]); 48 const zeros = Array(offs).fill([0]); 49 const result = _bjorklund([absOns, offs], [ones, zeros]); 50 const pattern = flatten(result[1][0]).concat(flatten(result[1][1])); 51 return inverted ? pattern.map((x) => 1 - x) : pattern; 52}; 53 54/** 55 * Changes the structure of the pattern to form an Euclidean rhythm. 56 * Euclidean rhythms are rhythms obtained using the greatest common 57 * divisor of two numbers. They were described in 2004 by Godfried 58 * Toussaint, a Canadian computer scientist. Euclidean rhythms are 59 * really useful for computer/algorithmic music because they can 60 * describe a large number of rhythms with a couple of numbers. 61 * 62 * @memberof Pattern 63 * @name euclid 64 * @tags temporal 65 * @param {number} pulses the number of onsets/beats 66 * @param {number} steps the number of steps to fill 67 * @returns Pattern 68 * @example 69 * // The Cuban tresillo pattern. 70 * note("c3").euclid(3,8) 71 */ 72 73/** 74 * Like `euclid`, but has an additional parameter for 'rotating' the resulting sequence. 75 * @memberof Pattern 76 * @name euclidRot 77 * @tags temporal 78 * @param {number} pulses the number of onsets/beats 79 * @param {number} steps the number of steps to fill 80 * @param {number} rotation offset in steps 81 * @returns Pattern 82 * @example 83 * // A Samba rhythm necklace from Brazil 84 * note("c3").euclidRot(3,16,14) 85 */ 86 87/** 88 * @example // A thirteenth-century Persian rhythm called Khafif-e-ramal. 89 * note("c3").euclid(2,5) 90 * @example // The archetypal pattern of the Cumbia from Colombia, as well as a Calypso rhythm from Trinidad. 91 * note("c3").euclid(3,4) 92 * @example // Another thirteenth century Persian rhythm by the name of Khafif-e-ramal, as well as a Rumanian folk-dance rhythm. 93 * note("c3").euclidRot(3,5,2) 94 * @example // A Ruchenitza rhythm used in a Bulgarian folk dance. 95 * note("c3").euclid(3,7) 96 * @example // The Cuban tresillo pattern. 97 * note("c3").euclid(3,8) 98 * @example // Another Ruchenitza Bulgarian folk-dance rhythm. 99 * note("c3").euclid(4,7) 100 * @example // The Aksak rhythm of Turkey. 101 * note("c3").euclid(4,9) 102 * @example // The metric pattern used by Frank Zappa in his piece titled Outside Now. 103 * note("c3").euclid(4,11) 104 * @example // Yields the York-Samai pattern, a popular Arab rhythm. 105 * note("c3").euclid(5,6) 106 * @example // The Nawakhat pattern, another popular Arab rhythm. 107 * note("c3").euclid(5,7) 108 * @example // The Cuban cinquillo pattern. 109 * note("c3").euclid(5,8) 110 * @example // A popular Arab rhythm called Agsag-Samai. 111 * note("c3").euclid(5,9) 112 * @example // The metric pattern used by Moussorgsky in Pictures at an Exhibition. 113 * note("c3").euclid(5,11) 114 * @example // The Venda clapping pattern of a South African children’s song. 115 * note("c3").euclid(5,12) 116 * @example // The Bossa-Nova rhythm necklace of Brazil. 117 * note("c3").euclid(5,16) 118 * @example // A typical rhythm played on the Bendir (frame drum). 119 * note("c3").euclid(7,8) 120 * @example // A common West African bell pattern. 121 * note("c3").euclid(7,12) 122 * @example // A Samba rhythm necklace from Brazil. 123 * note("c3").euclidRot(7,16,14) 124 * @example // A rhythm necklace used in the Central African Republic. 125 * note("c3").euclid(9,16) 126 * @example // A rhythm necklace of the Aka Pygmies of Central Africa. 127 * note("c3").euclidRot(11,24,14) 128 * @example // Another rhythm necklace of the Aka Pygmies of the upper Sangha. 129 * note("c3").euclidRot(13,24,5) 130 */ 131 132const _euclidRot = function (pulses, steps, rotation) { 133 const b = bjorklund(pulses, steps); 134 if (rotation) { 135 return rotate(b, -rotation); 136 } 137 return b; 138}; 139 140export const euclid = register('euclid', function (pulses, steps, pat) { 141 return pat.struct(_euclidRot(pulses, steps, 0)); 142}); 143 144export const bjork = register('bjork', function (euc, pat) { 145 if (!Array.isArray(euc)) { 146 euc = [euc]; 147 } 148 const [pulses, steps = pulses, rot = 0] = euc; 149 return pat.struct(_euclidRot(pulses, steps, rot)); 150}); 151 152export const { euclidrot, euclidRot } = register(['euclidrot', 'euclidRot'], function (pulses, steps, rotation, pat) { 153 return pat.struct(_euclidRot(pulses, steps, rotation)); 154}); 155 156/** 157 * Similar to `euclid`, but each pulse is held until the next pulse, 158 * so there will be no gaps. 159 * @name euclidLegato 160 * @memberof Pattern 161 * @tags temporal 162 * @param {number} pulses the number of onsets/beats 163 * @param {number} steps the number of steps to fill 164 * @param rotation offset in steps 165 * @param pat 166 * @example 167 * note("c3").euclidLegato(3,8) 168 */ 169 170const _euclidLegato = function (pulses, steps, rotation, pat) { 171 if (pulses < 1) { 172 return silence; 173 } 174 const bin_pat = _euclidRot(pulses, steps, 0); 175 const gapless = bin_pat 176 .join('') 177 .split('1') 178 .slice(1) 179 .map((s) => [s.length + 1, true]); 180 return pat.struct(timeCat(...gapless)).late(Fraction(rotation).div(steps)); 181}; 182 183export const euclidLegato = register(['euclidLegato'], function (pulses, steps, pat) { 184 return _euclidLegato(pulses, steps, 0, pat); 185}); 186 187/** 188 * Similar to `euclid`, but each pulse is held until the next pulse, 189 * so there will be no gaps, and has an additional parameter for 'rotating' 190 * the resulting sequence 191 * @name euclidLegatoRot 192 * @memberof Pattern 193 * @tags temporal 194 * @param {number} pulses the number of onsets/beats 195 * @param {number} steps the number of steps to fill 196 * @param {number} rotation offset in steps 197 * @example 198 * note("c3").euclidLegatoRot(3,5,2) 199 */ 200export const euclidLegatoRot = register(['euclidLegatoRot'], function (pulses, steps, rotation, pat) { 201 return _euclidLegato(pulses, steps, rotation, pat); 202}); 203 204/** 205 * A 'euclid' variant with an additional parameter that morphs the resulting 206 * rhythm from 0 (no morphing) to 1 (completely 'even'). For example 207 * `sound("bd").euclidish(3,8,0)` would be the same as 208 * `sound("bd").euclid(3,8)`, and `sound("bd").euclidish(3,8,1)` would be the 209 * same as `sound("bd bd bd")`. `sound("bd").euclidish(3,8,0.5)` would have a 210 * groove somewhere between. 211 * Inspired by the work of Malcom Braff. 212 * @name euclidish 213 * @synonyms eish 214 * @memberof Pattern 215 * @tags temporal 216 * @param {number} pulses the number of onsets 217 * @param {number} steps the number of steps to fill 218 * @param {number} groove exists between the extremes of 0 (straight euclidian) and 1 (straight pulse) 219 * @example 220 * sound("hh").euclidish(7,12,sine.slow(8)) 221 * .pan(sine.slow(8)) 222 */ 223export const { euclidish, eish } = register(['euclidish', 'eish'], function (pulses, steps, perc, pat) { 224 const morphed = _morph(bjorklund(pulses, steps), new Array(pulses).fill(1), perc); 225 return pat.struct(morphed).setSteps(steps); 226});