euclid.mjs - Bjorklund/Euclidean/Diaspora rhythms Copyright (C) 2023 Rohan Drape and strudel contributors
See https://codeberg.org/uzu/strudel/src/branch/main/packages/core/euclid.mjs for authors of this file.
The Bjorklund algorithm implementation is ported from the Haskell Music Theory Haskell module by Rohan Drape - https://rohandrape.net/?t=hmt
This 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/.
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};
Changes the structure of the pattern to form an Euclidean rhythm. Euclidean rhythms are rhythms obtained using the greatest common divisor of two numbers. They were described in 2004 by Godfried Toussaint, a Canadian computer scientist. Euclidean rhythms are really useful for computer/algorithmic music because they can describe a large number of rhythms with a couple of numbers.
@memberof Pattern @name euclid @tags temporal @param {number} pulses the number of onsets/beats @param {number} steps the number of steps to fill @returns Pattern @example // The Cuban tresillo pattern. note("c3").euclid(3,8)
Like euclid, but has an additional parameter for 'rotating' the resulting sequence.
@memberof Pattern
@name euclidRot
@tags temporal
@param {number} pulses the number of onsets/beats
@param {number} steps the number of steps to fill
@param {number} rotation offset in steps
@returns Pattern
@example
// A Samba rhythm necklace from Brazil
note("c3").euclidRot(3,16,14)
@example // A thirteenth-century Persian rhythm called Khafif-e-ramal. note("c3").euclid(2,5) @example // The archetypal pattern of the Cumbia from Colombia, as well as a Calypso rhythm from Trinidad. note("c3").euclid(3,4) @example // Another thirteenth century Persian rhythm by the name of Khafif-e-ramal, as well as a Rumanian folk-dance rhythm. note("c3").euclidRot(3,5,2) @example // A Ruchenitza rhythm used in a Bulgarian folk dance. note("c3").euclid(3,7) @example // The Cuban tresillo pattern. note("c3").euclid(3,8) @example // Another Ruchenitza Bulgarian folk-dance rhythm. note("c3").euclid(4,7) @example // The Aksak rhythm of Turkey. note("c3").euclid(4,9) @example // The metric pattern used by Frank Zappa in his piece titled Outside Now. note("c3").euclid(4,11) @example // Yields the York-Samai pattern, a popular Arab rhythm. note("c3").euclid(5,6) @example // The Nawakhat pattern, another popular Arab rhythm. note("c3").euclid(5,7) @example // The Cuban cinquillo pattern. note("c3").euclid(5,8) @example // A popular Arab rhythm called Agsag-Samai. note("c3").euclid(5,9) @example // The metric pattern used by Moussorgsky in Pictures at an Exhibition. note("c3").euclid(5,11) @example // The Venda clapping pattern of a South African children’s song. note("c3").euclid(5,12) @example // The Bossa-Nova rhythm necklace of Brazil. note("c3").euclid(5,16) @example // A typical rhythm played on the Bendir (frame drum). note("c3").euclid(7,8) @example // A common West African bell pattern. note("c3").euclid(7,12) @example // A Samba rhythm necklace from Brazil. note("c3").euclidRot(7,16,14) @example // A rhythm necklace used in the Central African Republic. note("c3").euclid(9,16) @example // A rhythm necklace of the Aka Pygmies of Central Africa. note("c3").euclidRot(11,24,14) @example // Another rhythm necklace of the Aka Pygmies of the upper Sangha. note("c3").euclidRot(13,24,5)
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});
Similar to euclid, but each pulse is held until the next pulse,
so there will be no gaps.
@name euclidLegato
@memberof Pattern
@tags temporal
@param {number} pulses the number of onsets/beats
@param {number} steps the number of steps to fill
@param rotation offset in steps
@param pat
@example
note("c3").euclidLegato(3,8)
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};
Similar to euclid, but each pulse is held until the next pulse,
so there will be no gaps, and has an additional parameter for 'rotating'
the resulting sequence
@name euclidLegatoRot
@memberof Pattern
@tags temporal
@param {number} pulses the number of onsets/beats
@param {number} steps the number of steps to fill
@param {number} rotation offset in steps
@example
note("c3").euclidLegatoRot(3,5,2)
A 'euclid' variant with an additional parameter that morphs the resulting
rhythm from 0 (no morphing) to 1 (completely 'even'). For example
sound("bd").euclidish(3,8,0) would be the same as
sound("bd").euclid(3,8), and sound("bd").euclidish(3,8,1) would be the
same as sound("bd bd bd"). sound("bd").euclidish(3,8,0.5) would have a
groove somewhere between.
Inspired by the work of Malcom Braff.
@name euclidish
@synonyms eish
@memberof Pattern
@tags temporal
@param {number} pulses the number of onsets
@param {number} steps the number of steps to fill
@param {number} groove exists between the extremes of 0 (straight euclidian) and 1 (straight pulse)
@example
sound("hh").euclidish(7,12,sine.slow(8))
.pan(sine.slow(8))