strudel/packages/core/euclid.mjs
Alex McLean 6422047cff
make 0.5hz cps the default (#931)
* 0.5 default cps

* 1 -> 0.5 cps defaults

* start moving examples to 2Hz

* more 2Hz doc edits

* small tweaks

* format

* adapt cycles page

* adapt pitch page

* tonal page

* accumulation

* synth page

* adapt conditional-modifiers

* audio effects page

* adapt signals doc

* fix: errors for signals

* adapt signals page

* start time modifiers

* adapt time modifiers

* adapt factories

* hydra + pattern intro

* adapt mini notation page

* start recipes

* adapt recipes page

* use code_v1 table

* delete old dbdump + add new csv based tool

* fix: tests

* fix: cpm

* shuffle featured patterns

* fix: snapshot

---------

Co-authored-by: Felix Roos <flix91@gmail.com>
2024-01-22 19:02:34 +00:00

186 lines
6.8 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
euclid.mjs - Bjorklund/Euclidean/Diaspora rhythms
Copyright (C) 2023 Rohan Drape and strudel contributors
See <https://github.com/tidalcycles/strudel/blob/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/>.
*/
import { Pattern, timeCat, register, silence } from './pattern.mjs';
import { rotate, flatten, splitAt, zipWith } from './util.mjs';
import Fraction from './fraction.mjs';
const left = function (n, x) {
const [ons, offs] = n;
const [xs, ys] = x;
const [_xs, __xs] = splitAt(offs, xs);
return [
[offs, ons - offs],
[zipWith((a, b) => a.concat(b), _xs, ys), __xs],
];
};
const right = function (n, x) {
const [ons, offs] = n;
const [xs, ys] = x;
const [_ys, __ys] = splitAt(ons, ys);
const result = [
[ons, offs - ons],
[zipWith((a, b) => a.concat(b), xs, _ys), __ys],
];
return result;
};
const _bjork = function (n, x) {
const [ons, offs] = n;
return Math.min(ons, offs) <= 1 ? [n, x] : _bjork(...(ons > offs ? left(n, x) : right(n, x)));
};
export const bjork = function (ons, steps) {
const offs = steps - ons;
const x = Array(ons).fill([1]);
const y = Array(offs).fill([0]);
const result = _bjork([ons, offs], [x, y]);
return flatten(result[1][0]).concat(flatten(result[1][1]));
};
/**
* Changes the structure of the pattern to form an euclidean rhythm.
* Euclidian rhythms are rhythms obtained using the greatest common
* divisor of two numbers. They were described in 2004 by Godfried
* Toussaint, a canadian computer scientist. Euclidian 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
* @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
* @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 childrens 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)
*/
const _euclidRot = function (pulses, steps, rotation) {
const b = bjork(pulses, steps);
if (rotation) {
return rotate(b, -rotation);
}
return b;
};
export const euclid = register('euclid', function (pulses, steps, pat) {
return pat.struct(_euclidRot(pulses, steps, 0));
});
export const { euclidrot, euclidRot } = register(['euclidrot', 'euclidRot'], function (pulses, steps, rotation, pat) {
return pat.struct(_euclidRot(pulses, steps, rotation));
});
/**
* Similar to `euclid`, but each pulse is held until the next pulse,
* so there will be no gaps.
* @name euclidLegato
* @memberof Pattern
* @param {number} pulses the number of onsets / beats
* @param {number} steps the number of steps to fill
* @example
* note("c3").euclidLegato(3,8)
*/
const _euclidLegato = function (pulses, steps, rotation, pat) {
if (pulses < 1) {
return silence;
}
const bin_pat = _euclidRot(pulses, steps, rotation);
const gapless = bin_pat
.join('')
.split('1')
.slice(1)
.map((s) => [s.length + 1, true]);
return pat.struct(timeCat(...gapless));
};
export const euclidLegato = register(['euclidLegato'], function (pulses, steps, pat) {
return _euclidLegato(pulses, steps, 0, pat);
});
/**
* 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
* @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)
*/
export const euclidLegatoRot = register(['euclidLegatoRot'], function (pulses, steps, rotation, pat) {
return _euclidLegato(pulses, steps, rotation, pat);
});