Merge pull request 'scale & xen parity' (#1955) from tyow/localstrudel:xen-feats into main

Reviewed-on: https://codeberg.org/uzu/strudel/pulls/1955
This commit is contained in:
froos
2026-02-05 09:47:21 +01:00
5 changed files with 464 additions and 111 deletions
+12
View File
@@ -394,6 +394,18 @@ export const { source, src } = registerControl('source', 'src');
*/
// also see https://codeberg.org/uzu/strudel/pulls/63
export const { n } = registerControl('n');
/**
* Selects the given degree. Currently used in `xen` and `tune`:
*
* @name i
* @tags tonal
* @param {number | Pattern} value
* @example
* i("0 1 2 3 4 5 6 7").xen("<5edo 10edo 15edo hexany15>")
*/
export const { i } = registerControl('i');
/**
* Plays the given note name or midi number. A note name consists of
*
+12 -5
View File
@@ -8,23 +8,23 @@ import Tune from './tunejs.js';
import { register } from '@strudel/core';
/**
* Assumes a numerical pattern of EDO steps. Accepts a scale name or list of frequencies (see all available names at the link on the reference). Returns a new pattern with all values mapped to a frequency ratio. Similar to `xen`.
* Assumes pattern contains numerical scale degrees on the `i` control (see examples below). Accepts a scale name or list of frequencies (see all available names at the link on the reference). Returns a new pattern with all values mapped to a frequency ratio. Similar to `xen`.
* @name tune
* @returns Pattern
* @memberof Pattern
* @param {(string | number[] )} scale
* @example
* "0 1 2 3 4 5".tune("hexany15").mul("220").freq()
* i("0 1 2 3 4 5").tune("hexany15").mul("220").freq()
* @example
* // You can set your root to be a
* // particular note with getFreq:
* "4 8 9 10 - - 5 7 9 11 - -".tune("tranh3")
* i("4 8 9 10 - - 5 7 9 11 - -").tune("tranh3")
* .mul(getFreq('c3'))
* .freq().clip(.5).room(1)
* @example
* // You can also give tune a list of
* // frequencies to use as the scale:
* "0 1 2 3 4".tune([
* i("0 1 2 3 4").tune([
* 261.6255653006,
* 302.72962012827,
* 350.29154279212,
@@ -32,6 +32,7 @@ import { register } from '@strudel/core';
* 469.00678383895,
* 523.2511306012
* ]).mul(220).freq();
*
* @tags tonal
*/
@@ -45,6 +46,12 @@ export const tune = register('tune', (scale, pat) => {
// if the tonic is a frequency, why are we putting in "1"
tune.tonicize(1);
return pat.withHap((hap) => {
return hap.withValue(() => tune.note(hap.value));
if (typeof hap.value !== 'object') {
throw new Error(`Expected hap to have control 'i' set, but received ${hap.value.i}, try wrapping input in i()`);
}
// const { i, ...otherValues } = hap.value;
// hap.value = { ...otherValues, freq: tune.note(i)}
// return hap
return hap.withValue(() => tune.note(hap.value.i));
});
});
+138 -19
View File
@@ -4,7 +4,7 @@ Copyright (C) 2022 Strudel contributors - see <https://codeberg.org/uzu/strudel/
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 { register, _mod, parseNumeral } from '@strudel/core';
import { register, _mod, parseNumeral, removeUndefineds } from '@strudel/core';
import Tune from './tunejs.js';
// returns a list of frequency ratios for given edo scale
@@ -22,18 +22,20 @@ const presets = {
// Given a base frequency such as 220 and an edo scale, returns
// an array of frequencies representing the given edo scale in that base
function withBase(freq, scale) {
function _withBase(freq, scale) {
return scale.map((r) => r * freq);
}
const defaultBase = 220;
const isEdo = (scale) => /^[1-9]+[0-9]*edo$/.test(scale);
// Assumes a base of 220. Returns a filtered scale based on 'indices'
// NOTE: indices functionality is unused
function getXenScale(scale, indices) {
let tune = new Tune();
if (typeof scale === 'string') {
if (/^[1-9]+[0-9]*edo$/.test(scale)) {
if (isEdo(scale)) {
scale = edo(scale);
} else if (presets[scale]) {
scale = presets[scale];
@@ -44,7 +46,7 @@ function getXenScale(scale, indices) {
throw new Error('unknown scale name: "' + scale + '"');
}
}
scale = withBase(defaultBase, scale);
scale = _withBase(defaultBase, scale);
if (!indices) {
return scale;
}
@@ -57,6 +59,8 @@ function xenOffset(xenScale, offset, index = 0) {
return xenScale[i] * Math.pow(2, oct);
}
const trimFreq = (freq) => parseFloat(freq.toPrecision(10));
// accepts a scale name such as 31edo, and a pattern
// pattern expected to follow format such that a value can be mapped
// to an edostep within the scale. Returns the pattern with
@@ -72,39 +76,154 @@ function xenOffset(xenScale, offset, index = 0) {
* @param {(string | number[] )} scaleNameOrRatios
* @tags tonal
* @example
* // A major triad in 31edo:
* "0 8 18".xen("31edo").freq().piano()
* // A minor triad in 31edo:
* i("0 8 18").xen("31edo").piano()
* @example
* // You can also use xen with frequency ratios.
* // This is equivalent to the above:
* "0 1 2".xen([
* i("0 1 2").xen([
* Math.pow(2, 0/31),
* Math.pow(2, 8/31),
* Math.pow(2, 18/31),
* ]).freq().piano()
* ]).piano()
* @example
* // xen also supports all scale names that
* // tune does:
* "0 1 2 3 4 5".xen("hexany15").freq()
* i("0 1 2 3 4 5").xen("hexany15")
* // equiv to:
* // "0 1 2 3 4 5".tune("hexany15").mul("220").freq()
* @example
* i("0 1 2 3 4 5 6 7").xen("<5edo 10edo 15edo hexany15>")
*/
// TODO feat: change root frequency
// TODO add explanation for what "31edo" etc. are
// TODO (maybe): should this return freq ratios like tune does, for parity's sake?
export const xen = register('xen', function (scaleNameOrRatios, pat) {
return pat.withHap((hap) => {
const scale = getXenScale(scaleNameOrRatios);
let frequency = xenOffset(scale, parseNumeral(hap.value));
// 10 is somewhat arbitrary
frequency = parseFloat(frequency.toPrecision(10));
return hap.withValue(() => frequency);
return pat.withHaps((haps) => {
haps = haps.map((hap) => {
let hVal = hap.value;
const isObject = typeof hVal === 'object';
if (!isObject) {
throw new Error(`Expected hap to have control 'i' set, but received ${hap.value.i}, try wrapping input in i()`);
}
const { i, ...otherValues } = hVal;
const scale = getXenScale(scaleNameOrRatios);
let freq = xenOffset(scale, parseNumeral(hVal.i));
// 10 is somewhat arbitrary
freq = trimFreq(freq);
hap.value = { ...otherValues, freq };
return isEdo(scaleNameOrRatios)
? hap.setContext({ ...hap.context, edoSize: scaleNameOrRatios.match(/^([1-9]+[0-9]*)edo$/)[1] })
: hap;
});
return removeUndefineds(haps);
});
});
/**
* Assumes pattern of frequencies tuned to some `base` frequency, such as the output of `xen`
* Because `xen` defaults to `220Hz`, so will `withBase`.
* but you can specify a different original base with the standard optional array syntax '`:`'
* @name withBase
* @param {number} base
* @param {number} (optional) originalBase
* @tags tonal
*
* @example
* i("[0 1 2 3] [3 4] [4 3 2 1]").xen("hexany23").withBase("<220 [300 200]>")
* @example
* mini([1 / 1, 16 / 15, 9 / 8, 6 / 5, 5 / 4].join(' ')).withBase("220:1")
* // mini([1 / 1, 16 / 15, 9 / 8, 6 / 5, 5 / 4].join(' ')).mul(220).freq()
*
* @returns Pattern
*/
export const withBase = register('withBase', (b, pat) => {
let base;
let originalBase = 220;
if (Array.isArray(b)) {
base = b[0];
originalBase = b[1];
} else {
base = b;
}
return pat.withHaps((haps) => {
haps = haps.map((hap) => {
let hVal = hap.value;
const isObject = typeof hVal === 'object';
let freq = isObject ? hVal.freq : hVal;
freq = (freq * base) / originalBase;
hap.value = isObject ? { ...hap.value, freq } : { freq };
return hap;
});
return removeUndefineds(haps);
});
});
/**
* Frequency transpose. Assumes pattern either has `freq` set, or has values that can be interpreted as frequencies
* amt has optional `edoSize` param, defaults to 12.
* If haps have edoSize param set, such as from the output of `xen("31edo")`,
* `ftrans` will fallback to that instead of 12 as the default.
*
* Transposes the frequency by `amt` edoSteps
* @name ftranspose
* @synonyms ftrans, fTrans, ftranspose, fTranspose
* @tags tonal
* @param {number} amt
* @param {number} edoSize (optional)
* @returns {Pattern}
*
* @example
* i("0 1 2").xen("12edo").ftrans("7")
* // n("0 1 2").scale("A:chromatic").trans("7")
* @example
* i("0 8 18").xen("31edo").ftrans("<8 -8>")
* @example
* // to transpose by steps of an edo, use "step:edo" :
* i("0 7 8 18").xen("31edo").ftrans("<0 1:31 1:12>")
* @example
* // it can also work with frequency values directly
* freq("200 300 400").ftrans("<0 7:31 7>")
*/
/* f = frequency (Hz)
n = edo (steps per octave)
x = number of steps
if 0\n = f, then x\n = f * 2^(x/n)
example: 5edo, 0\5 = 220 Hz, then 2\5 = 220*2^(2/5) = 290.29 Hz */
export const { ftrans, fTrans, ftranspose, fTranspose } = register(
['ftrans', 'fTrans', 'ftranspose', 'fTranspose'],
(amt, pat) => {
let edoSize;
let numSteps;
if (Array.isArray(amt)) {
edoSize = amt[1];
numSteps = amt[0];
} else {
numSteps = amt;
}
return pat.withHaps((haps) => {
haps = haps.map((hap) => {
let hVal = hap.value;
const isObject = typeof hVal === 'object';
hVal = isObject ? hVal : { freq: hVal };
let { freq, ...otherValues } = hVal;
if (edoSize == undefined && hap.context.edoSize != undefined) {
edoSize = hap.context.edoSize;
} else if (edoSize == undefined) {
edoSize = 12;
}
freq = freq * Math.pow(2, numSteps / edoSize);
freq = trimFreq(freq);
hap.value = isObject ? { ...otherValues, freq } : freq;
return hap.setContext({ ...hap.context, edoSize });
});
return removeUndefineds(haps);
});
},
);
// not sure there's a point to having this and the above, seems like a proto version of the above.
export const tuning = register('tuning', function (ratios, pat) {
const tuning = register('tuning', function (ratios, pat) {
return pat.withHap((hap) => {
const frequency = xenOffset(ratios, parseNumeral(hap.value));
return hap.withValue(() => frequency);
+292 -76
View File
@@ -4950,6 +4950,78 @@ exports[`runs examples > example "fscope" example index 0 1`] = `
]
`;
exports[`runs examples > example "ftranspose" example index 0 1`] = `
[
"[ 0/1 → 1/3 | freq:329.6275569 ]",
"[ 1/3 → 2/3 | freq:349.2282315 ]",
"[ 2/3 → 1/1 | freq:369.9944227 ]",
"[ 1/1 → 4/3 | freq:329.6275569 ]",
"[ 4/3 → 5/3 | freq:349.2282315 ]",
"[ 5/3 → 2/1 | freq:369.9944227 ]",
"[ 2/1 → 7/3 | freq:329.6275569 ]",
"[ 7/3 → 8/3 | freq:349.2282315 ]",
"[ 8/3 → 3/1 | freq:369.9944227 ]",
"[ 3/1 → 10/3 | freq:329.6275569 ]",
"[ 10/3 → 11/3 | freq:349.2282315 ]",
"[ 11/3 → 4/1 | freq:369.9944227 ]",
]
`;
exports[`runs examples > example "ftranspose" example index 1 1`] = `
[
"[ 0/1 → 1/3 | freq:263.0921203 ]",
"[ 1/3 → 2/3 | freq:314.6248353 ]",
"[ 2/3 → 1/1 | freq:393.4589706 ]",
"[ 1/1 → 4/3 | freq:183.965981 ]",
"[ 4/3 → 5/3 | freq:220 ]",
"[ 5/3 → 2/1 | freq:275.1244143 ]",
"[ 2/1 → 7/3 | freq:263.0921203 ]",
"[ 7/3 → 8/3 | freq:314.6248353 ]",
"[ 8/3 → 3/1 | freq:393.4589706 ]",
"[ 3/1 → 10/3 | freq:183.965981 ]",
"[ 10/3 → 11/3 | freq:220 ]",
"[ 11/3 → 4/1 | freq:275.1244143 ]",
]
`;
exports[`runs examples > example "ftranspose" example index 2 1`] = `
[
"[ 0/1 → 1/4 | freq:220 ]",
"[ 1/4 → 1/2 | freq:257.2747684 ]",
"[ 1/2 → 3/4 | freq:263.0921203 ]",
"[ 3/4 → 1/1 | freq:329.0139341 ]",
"[ 1/1 → 5/4 | freq:224.9745158 ]",
"[ 5/4 → 3/2 | freq:263.0921203 ]",
"[ 3/2 → 7/4 | freq:269.0410108 ]",
"[ 7/4 → 2/1 | freq:336.4534115 ]",
"[ 2/1 → 9/4 | freq:233.0818808 ]",
"[ 9/4 → 5/2 | freq:272.5731222 ]",
"[ 5/2 → 11/4 | freq:278.7363919 ]",
"[ 11/4 → 3/1 | freq:348.5781207 ]",
"[ 3/1 → 13/4 | freq:220 ]",
"[ 13/4 → 7/2 | freq:257.2747684 ]",
"[ 7/2 → 15/4 | freq:263.0921203 ]",
"[ 15/4 → 4/1 | freq:329.0139341 ]",
]
`;
exports[`runs examples > example "ftranspose" example index 3 1`] = `
[
"[ 0/1 → 1/3 | freq:200 ]",
"[ 1/3 → 2/3 | freq:300 ]",
"[ 2/3 → 1/1 | freq:400 ]",
"[ 1/1 → 4/3 | freq:233.8861531 ]",
"[ 4/3 → 5/3 | freq:350.8292297 ]",
"[ 5/3 → 2/1 | freq:467.7723062 ]",
"[ 2/1 → 7/3 | freq:299.6614154 ]",
"[ 7/3 → 8/3 | freq:449.4921231 ]",
"[ 8/3 → 3/1 | freq:599.3228308 ]",
"[ 3/1 → 10/3 | freq:200 ]",
"[ 10/3 → 11/3 | freq:300 ]",
"[ 11/3 → 4/1 | freq:400 ]",
]
`;
exports[`runs examples > example "ftype" example index 0 1`] = `
[
"[ 0/1 → 1/8 | note:f s:sawtooth lpenv:4 cutoff:500 ftype:0 resonance:1 ]",
@@ -5634,6 +5706,43 @@ exports[`runs examples > example "hush" example index 0 1`] = `
]
`;
exports[`runs examples > example "i" example index 0 1`] = `
[
"[ 0/1 → 1/8 | freq:220 ]",
"[ 1/8 → 1/4 | freq:252.7136381 ]",
"[ 1/4 → 3/8 | freq:290.2917404 ]",
"[ 3/8 → 1/2 | freq:333.4576446 ]",
"[ 1/2 → 5/8 | freq:383.0422479 ]",
"[ 5/8 → 3/4 | freq:440 ]",
"[ 3/4 → 7/8 | freq:505.4272762 ]",
"[ 7/8 → 1/1 | freq:580.5834807 ]",
"[ 1/1 → 9/8 | freq:220 ]",
"[ 9/8 → 5/4 | freq:235.7901618 ]",
"[ 5/4 → 11/8 | freq:252.7136381 ]",
"[ 11/8 → 3/2 | freq:270.8517709 ]",
"[ 3/2 → 13/8 | freq:290.2917404 ]",
"[ 13/8 → 7/4 | freq:311.1269837 ]",
"[ 7/4 → 15/8 | freq:333.4576446 ]",
"[ 15/8 → 2/1 | freq:357.3910544 ]",
"[ 2/1 → 17/8 | freq:220 ]",
"[ 17/8 → 9/4 | freq:230.404707 ]",
"[ 9/4 → 19/8 | freq:241.3014955 ]",
"[ 19/8 → 5/2 | freq:252.7136381 ]",
"[ 5/2 → 21/8 | freq:264.6655079 ]",
"[ 21/8 → 11/4 | freq:277.182631 ]",
"[ 11/4 → 23/8 | freq:290.2917404 ]",
"[ 23/8 → 3/1 | freq:304.0208336 ]",
"[ 3/1 → 25/8 | freq:220 ]",
"[ 25/8 → 13/4 | freq:275 ]",
"[ 13/4 → 27/8 | freq:293.3333333 ]",
"[ 27/8 → 7/2 | freq:330 ]",
"[ 7/2 → 29/8 | freq:352 ]",
"[ 29/8 → 15/4 | freq:440 ]",
"[ 15/4 → 31/8 | freq:550 ]",
"[ 31/8 → 4/1 | freq:586.6666667 ]",
]
`;
exports[`runs examples > example "inhabit" example index 0 1`] = `
[
"[ 0/1 → 1/8 | s:bd ]",
@@ -13217,92 +13326,92 @@ exports[`runs examples > example "tri" example index 0 1`] = `
exports[`runs examples > example "tune" example index 0 1`] = `
[
"[ 0/1 → 1/6 | freq:0 ]",
"[ 1/6 → 1/3 | freq:220 ]",
"[ 1/3 → 1/2 | freq:440 ]",
"[ 1/2 → 2/3 | freq:660 ]",
"[ 2/3 → 5/6 | freq:880 ]",
"[ 5/6 → 1/1 | freq:1100 ]",
"[ 1/1 → 7/6 | freq:0 ]",
"[ 7/6 → 4/3 | freq:220 ]",
"[ 4/3 → 3/2 | freq:440 ]",
"[ 3/2 → 5/3 | freq:660 ]",
"[ 5/3 → 11/6 | freq:880 ]",
"[ 11/6 → 2/1 | freq:1100 ]",
"[ 2/1 → 13/6 | freq:0 ]",
"[ 13/6 → 7/3 | freq:220 ]",
"[ 7/3 → 5/2 | freq:440 ]",
"[ 5/2 → 8/3 | freq:660 ]",
"[ 8/3 → 17/6 | freq:880 ]",
"[ 17/6 → 3/1 | freq:1100 ]",
"[ 3/1 → 19/6 | freq:0 ]",
"[ 19/6 → 10/3 | freq:220 ]",
"[ 10/3 → 7/2 | freq:440 ]",
"[ 7/2 → 11/3 | freq:660 ]",
"[ 11/3 → 23/6 | freq:880 ]",
"[ 23/6 → 4/1 | freq:1100 ]",
"[ 0/1 → 1/6 | freq:{i:0} ]",
"[ 1/6 → 1/3 | freq:{i:1} ]",
"[ 1/3 → 1/2 | freq:{i:2} ]",
"[ 1/2 → 2/3 | freq:{i:3} ]",
"[ 2/3 → 5/6 | freq:{i:4} ]",
"[ 5/6 → 1/1 | freq:{i:5} ]",
"[ 1/1 → 7/6 | freq:{i:0} ]",
"[ 7/6 → 4/3 | freq:{i:1} ]",
"[ 4/3 → 3/2 | freq:{i:2} ]",
"[ 3/2 → 5/3 | freq:{i:3} ]",
"[ 5/3 → 11/6 | freq:{i:4} ]",
"[ 11/6 → 2/1 | freq:{i:5} ]",
"[ 2/1 → 13/6 | freq:{i:0} ]",
"[ 13/6 → 7/3 | freq:{i:1} ]",
"[ 7/3 → 5/2 | freq:{i:2} ]",
"[ 5/2 → 8/3 | freq:{i:3} ]",
"[ 8/3 → 17/6 | freq:{i:4} ]",
"[ 17/6 → 3/1 | freq:{i:5} ]",
"[ 3/1 → 19/6 | freq:{i:0} ]",
"[ 19/6 → 10/3 | freq:{i:1} ]",
"[ 10/3 → 7/2 | freq:{i:2} ]",
"[ 7/2 → 11/3 | freq:{i:3} ]",
"[ 11/3 → 23/6 | freq:{i:4} ]",
"[ 23/6 → 4/1 | freq:{i:5} ]",
]
`;
exports[`runs examples > example "tune" example index 1 1`] = `
[
"[ 0/1 → 1/12 | freq:523.2511306011972 clip:0.5 room:1 ]",
"[ 1/12 → 1/6 | freq:1046.5022612023945 clip:0.5 room:1 ]",
"[ 1/6 → 1/4 | freq:1177.3150438526939 clip:0.5 room:1 ]",
"[ 1/4 → 1/3 | freq:1308.1278265029932 clip:0.5 room:1 ]",
"[ 1/2 → 7/12 | freq:654.0639132514966 clip:0.5 room:1 ]",
"[ 7/12 → 2/3 | freq:915.6894785520951 clip:0.5 room:1 ]",
"[ 2/3 → 3/4 | freq:1177.3150438526939 clip:0.5 room:1 ]",
"[ 3/4 → 5/6 | freq:1438.9406091532924 clip:0.5 room:1 ]",
"[ 1/1 → 13/12 | freq:523.2511306011972 clip:0.5 room:1 ]",
"[ 13/12 → 7/6 | freq:1046.5022612023945 clip:0.5 room:1 ]",
"[ 7/6 → 5/4 | freq:1177.3150438526939 clip:0.5 room:1 ]",
"[ 5/4 → 4/3 | freq:1308.1278265029932 clip:0.5 room:1 ]",
"[ 3/2 → 19/12 | freq:654.0639132514966 clip:0.5 room:1 ]",
"[ 19/12 → 5/3 | freq:915.6894785520951 clip:0.5 room:1 ]",
"[ 5/3 → 7/4 | freq:1177.3150438526939 clip:0.5 room:1 ]",
"[ 7/4 → 11/6 | freq:1438.9406091532924 clip:0.5 room:1 ]",
"[ 2/1 → 25/12 | freq:523.2511306011972 clip:0.5 room:1 ]",
"[ 25/12 → 13/6 | freq:1046.5022612023945 clip:0.5 room:1 ]",
"[ 13/6 → 9/4 | freq:1177.3150438526939 clip:0.5 room:1 ]",
"[ 9/4 → 7/3 | freq:1308.1278265029932 clip:0.5 room:1 ]",
"[ 5/2 → 31/12 | freq:654.0639132514966 clip:0.5 room:1 ]",
"[ 31/12 → 8/3 | freq:915.6894785520951 clip:0.5 room:1 ]",
"[ 8/3 → 11/4 | freq:1177.3150438526939 clip:0.5 room:1 ]",
"[ 11/4 → 17/6 | freq:1438.9406091532924 clip:0.5 room:1 ]",
"[ 3/1 → 37/12 | freq:523.2511306011972 clip:0.5 room:1 ]",
"[ 37/12 → 19/6 | freq:1046.5022612023945 clip:0.5 room:1 ]",
"[ 19/6 → 13/4 | freq:1177.3150438526939 clip:0.5 room:1 ]",
"[ 13/4 → 10/3 | freq:1308.1278265029932 clip:0.5 room:1 ]",
"[ 7/2 → 43/12 | freq:654.0639132514966 clip:0.5 room:1 ]",
"[ 43/12 → 11/3 | freq:915.6894785520951 clip:0.5 room:1 ]",
"[ 11/3 → 15/4 | freq:1177.3150438526939 clip:0.5 room:1 ]",
"[ 15/4 → 23/6 | freq:1438.9406091532924 clip:0.5 room:1 ]",
"[ 0/1 → 1/12 | freq:{i:4} clip:0.5 room:1 ]",
"[ 1/12 → 1/6 | freq:{i:8} clip:0.5 room:1 ]",
"[ 1/6 → 1/4 | freq:{i:9} clip:0.5 room:1 ]",
"[ 1/4 → 1/3 | freq:{i:10} clip:0.5 room:1 ]",
"[ 1/2 → 7/12 | freq:{i:5} clip:0.5 room:1 ]",
"[ 7/12 → 2/3 | freq:{i:7} clip:0.5 room:1 ]",
"[ 2/3 → 3/4 | freq:{i:9} clip:0.5 room:1 ]",
"[ 3/4 → 5/6 | freq:{i:11} clip:0.5 room:1 ]",
"[ 1/1 → 13/12 | freq:{i:4} clip:0.5 room:1 ]",
"[ 13/12 → 7/6 | freq:{i:8} clip:0.5 room:1 ]",
"[ 7/6 → 5/4 | freq:{i:9} clip:0.5 room:1 ]",
"[ 5/4 → 4/3 | freq:{i:10} clip:0.5 room:1 ]",
"[ 3/2 → 19/12 | freq:{i:5} clip:0.5 room:1 ]",
"[ 19/12 → 5/3 | freq:{i:7} clip:0.5 room:1 ]",
"[ 5/3 → 7/4 | freq:{i:9} clip:0.5 room:1 ]",
"[ 7/4 → 11/6 | freq:{i:11} clip:0.5 room:1 ]",
"[ 2/1 → 25/12 | freq:{i:4} clip:0.5 room:1 ]",
"[ 25/12 → 13/6 | freq:{i:8} clip:0.5 room:1 ]",
"[ 13/6 → 9/4 | freq:{i:9} clip:0.5 room:1 ]",
"[ 9/4 → 7/3 | freq:{i:10} clip:0.5 room:1 ]",
"[ 5/2 → 31/12 | freq:{i:5} clip:0.5 room:1 ]",
"[ 31/12 → 8/3 | freq:{i:7} clip:0.5 room:1 ]",
"[ 8/3 → 11/4 | freq:{i:9} clip:0.5 room:1 ]",
"[ 11/4 → 17/6 | freq:{i:11} clip:0.5 room:1 ]",
"[ 3/1 → 37/12 | freq:{i:4} clip:0.5 room:1 ]",
"[ 37/12 → 19/6 | freq:{i:8} clip:0.5 room:1 ]",
"[ 19/6 → 13/4 | freq:{i:9} clip:0.5 room:1 ]",
"[ 13/4 → 10/3 | freq:{i:10} clip:0.5 room:1 ]",
"[ 7/2 → 43/12 | freq:{i:5} clip:0.5 room:1 ]",
"[ 43/12 → 11/3 | freq:{i:7} clip:0.5 room:1 ]",
"[ 11/3 → 15/4 | freq:{i:9} clip:0.5 room:1 ]",
"[ 15/4 → 23/6 | freq:{i:11} clip:0.5 room:1 ]",
]
`;
exports[`runs examples > example "tune" example index 2 1`] = `
[
"[ 0/1 → 1/5 | freq:0 ]",
"[ 1/5 → 2/5 | freq:220 ]",
"[ 2/5 → 3/5 | freq:440 ]",
"[ 3/5 → 4/5 | freq:660 ]",
"[ 4/5 → 1/1 | freq:880 ]",
"[ 1/1 → 6/5 | freq:0 ]",
"[ 6/5 → 7/5 | freq:220 ]",
"[ 7/5 → 8/5 | freq:440 ]",
"[ 8/5 → 9/5 | freq:660 ]",
"[ 9/5 → 2/1 | freq:880 ]",
"[ 2/1 → 11/5 | freq:0 ]",
"[ 11/5 → 12/5 | freq:220 ]",
"[ 12/5 → 13/5 | freq:440 ]",
"[ 13/5 → 14/5 | freq:660 ]",
"[ 14/5 → 3/1 | freq:880 ]",
"[ 3/1 → 16/5 | freq:0 ]",
"[ 16/5 → 17/5 | freq:220 ]",
"[ 17/5 → 18/5 | freq:440 ]",
"[ 18/5 → 19/5 | freq:660 ]",
"[ 19/5 → 4/1 | freq:880 ]",
"[ 0/1 → 1/5 | freq:{i:0} ]",
"[ 1/5 → 2/5 | freq:{i:1} ]",
"[ 2/5 → 3/5 | freq:{i:2} ]",
"[ 3/5 → 4/5 | freq:{i:3} ]",
"[ 4/5 → 1/1 | freq:{i:4} ]",
"[ 1/1 → 6/5 | freq:{i:0} ]",
"[ 6/5 → 7/5 | freq:{i:1} ]",
"[ 7/5 → 8/5 | freq:{i:2} ]",
"[ 8/5 → 9/5 | freq:{i:3} ]",
"[ 9/5 → 2/1 | freq:{i:4} ]",
"[ 2/1 → 11/5 | freq:{i:0} ]",
"[ 11/5 → 12/5 | freq:{i:1} ]",
"[ 12/5 → 13/5 | freq:{i:2} ]",
"[ 13/5 → 14/5 | freq:{i:3} ]",
"[ 14/5 → 3/1 | freq:{i:4} ]",
"[ 3/1 → 16/5 | freq:{i:0} ]",
"[ 16/5 → 17/5 | freq:{i:1} ]",
"[ 17/5 → 18/5 | freq:{i:2} ]",
"[ 18/5 → 19/5 | freq:{i:3} ]",
"[ 19/5 → 4/1 | freq:{i:4} ]",
]
`;
@@ -14079,6 +14188,76 @@ exports[`runs examples > example "when" example index 0 1`] = `
exports[`runs examples > example "whenKey" example index 0 1`] = `[]`;
exports[`runs examples > example "withBase" example index 0 1`] = `
[
"[ 0/1 → 1/12 | freq:220 ]",
"[ 1/12 → 1/6 | freq:293.3333333 ]",
"[ 1/6 → 1/4 | freq:302.5 ]",
"[ 1/4 → 1/3 | freq:320 ]",
"[ 1/3 → 1/2 | freq:320 ]",
"[ 1/2 → 2/3 | freq:330 ]",
"[ 2/3 → 3/4 | freq:330 ]",
"[ 3/4 → 5/6 | freq:320 ]",
"[ 5/6 → 11/12 | freq:302.5 ]",
"[ 11/12 → 1/1 | freq:293.3333333 ]",
"[ 1/1 → 13/12 | freq:300 ]",
"[ 13/12 → 7/6 | freq:399.99999995454544 ]",
"[ 7/6 → 5/4 | freq:412.5 ]",
"[ 5/4 → 4/3 | freq:436.3636363636364 ]",
"[ 4/3 → 3/2 | freq:436.3636363636364 ]",
"[ 3/2 → 5/3 | freq:300 ]",
"[ 5/3 → 7/4 | freq:300 ]",
"[ 7/4 → 11/6 | freq:290.90909090909093 ]",
"[ 11/6 → 23/12 | freq:275 ]",
"[ 23/12 → 2/1 | freq:266.66666663636363 ]",
"[ 2/1 → 25/12 | freq:220 ]",
"[ 25/12 → 13/6 | freq:293.3333333 ]",
"[ 13/6 → 9/4 | freq:302.5 ]",
"[ 9/4 → 7/3 | freq:320 ]",
"[ 7/3 → 5/2 | freq:320 ]",
"[ 5/2 → 8/3 | freq:330 ]",
"[ 8/3 → 11/4 | freq:330 ]",
"[ 11/4 → 17/6 | freq:320 ]",
"[ 17/6 → 35/12 | freq:302.5 ]",
"[ 35/12 → 3/1 | freq:293.3333333 ]",
"[ 3/1 → 37/12 | freq:300 ]",
"[ 37/12 → 19/6 | freq:399.99999995454544 ]",
"[ 19/6 → 13/4 | freq:412.5 ]",
"[ 13/4 → 10/3 | freq:436.3636363636364 ]",
"[ 10/3 → 7/2 | freq:436.3636363636364 ]",
"[ 7/2 → 11/3 | freq:300 ]",
"[ 11/3 → 15/4 | freq:300 ]",
"[ 15/4 → 23/6 | freq:290.90909090909093 ]",
"[ 23/6 → 47/12 | freq:275 ]",
"[ 47/12 → 4/1 | freq:266.66666663636363 ]",
]
`;
exports[`runs examples > example "withBase" example index 1 1`] = `
[
"[ 0/1 → 1/5 | freq:220 ]",
"[ 1/5 → 2/5 | freq:234.66666666666666 ]",
"[ 2/5 → 3/5 | freq:247.5 ]",
"[ 3/5 → 4/5 | freq:264 ]",
"[ 4/5 → 1/1 | freq:275 ]",
"[ 1/1 → 6/5 | freq:220 ]",
"[ 6/5 → 7/5 | freq:234.66666666666666 ]",
"[ 7/5 → 8/5 | freq:247.5 ]",
"[ 8/5 → 9/5 | freq:264 ]",
"[ 9/5 → 2/1 | freq:275 ]",
"[ 2/1 → 11/5 | freq:220 ]",
"[ 11/5 → 12/5 | freq:234.66666666666666 ]",
"[ 12/5 → 13/5 | freq:247.5 ]",
"[ 13/5 → 14/5 | freq:264 ]",
"[ 14/5 → 3/1 | freq:275 ]",
"[ 3/1 → 16/5 | freq:220 ]",
"[ 16/5 → 17/5 | freq:234.66666666666666 ]",
"[ 17/5 → 18/5 | freq:247.5 ]",
"[ 18/5 → 19/5 | freq:264 ]",
"[ 19/5 → 4/1 | freq:275 ]",
]
`;
exports[`runs examples > example "withValue" example index 0 1`] = `
[
"[ 0/1 → 1/3 | 10 ]",
@@ -14281,6 +14460,43 @@ exports[`runs examples > example "xen" example index 2 1`] = `
]
`;
exports[`runs examples > example "xen" example index 3 1`] = `
[
"[ 0/1 → 1/8 | freq:220 ]",
"[ 1/8 → 1/4 | freq:252.7136381 ]",
"[ 1/4 → 3/8 | freq:290.2917404 ]",
"[ 3/8 → 1/2 | freq:333.4576446 ]",
"[ 1/2 → 5/8 | freq:383.0422479 ]",
"[ 5/8 → 3/4 | freq:440 ]",
"[ 3/4 → 7/8 | freq:505.4272762 ]",
"[ 7/8 → 1/1 | freq:580.5834807 ]",
"[ 1/1 → 9/8 | freq:220 ]",
"[ 9/8 → 5/4 | freq:235.7901618 ]",
"[ 5/4 → 11/8 | freq:252.7136381 ]",
"[ 11/8 → 3/2 | freq:270.8517709 ]",
"[ 3/2 → 13/8 | freq:290.2917404 ]",
"[ 13/8 → 7/4 | freq:311.1269837 ]",
"[ 7/4 → 15/8 | freq:333.4576446 ]",
"[ 15/8 → 2/1 | freq:357.3910544 ]",
"[ 2/1 → 17/8 | freq:220 ]",
"[ 17/8 → 9/4 | freq:230.404707 ]",
"[ 9/4 → 19/8 | freq:241.3014955 ]",
"[ 19/8 → 5/2 | freq:252.7136381 ]",
"[ 5/2 → 21/8 | freq:264.6655079 ]",
"[ 21/8 → 11/4 | freq:277.182631 ]",
"[ 11/4 → 23/8 | freq:290.2917404 ]",
"[ 23/8 → 3/1 | freq:304.0208336 ]",
"[ 3/1 → 25/8 | freq:220 ]",
"[ 25/8 → 13/4 | freq:275 ]",
"[ 13/4 → 27/8 | freq:293.3333333 ]",
"[ 27/8 → 7/2 | freq:330 ]",
"[ 7/2 → 29/8 | freq:352 ]",
"[ 29/8 → 15/4 | freq:440 ]",
"[ 15/4 → 31/8 | freq:550 ]",
"[ 31/8 → 4/1 | freq:586.6666667 ]",
]
`;
exports[`runs examples > example "xfade" example index 0 1`] = `
[
"[ 0/1 → 1/8 | s:hh gain:0 ]",
+10 -11
View File
@@ -20,7 +20,7 @@ These functions allow the use of scales other than your typical chromatic 12 bas
Here's an example of how to configure a basic hexany scale:
<MiniRepl client:idle tune={`"0 1 2 3 4 5".tune("hexany15").mul("220").freq()`} />
<MiniRepl client:idle tune={`i("0 1 2 3 4 5").tune("hexany15").mul("220").freq()`} />
Try other scales like `hexany1`, `iraq`, `gumbeng`, `gunkali`, or `tranh3`
@@ -30,7 +30,7 @@ You can set your root to be a particular note with `getFreq`
<MiniRepl
client:idle
tune={`"4 8 9 10 - - 5 7 9 11 - -".tune("tranh3")
tune={`i("4 8 9 10 - - 5 7 9 11 - -").tune("tranh3")
.mul(getFreq('c3'))
.freq().clip(.5).room(1)`}
/>
@@ -39,7 +39,7 @@ Some tunings become more pronounced with a longer reverb decay:
<MiniRepl
client:idle
tune={`"<[5 6 8 10] - [5 7 9 12] -> -".tune("gumbeng")
tune={`i("<[5 6 8 10] - [5 7 9 12] -> -").tune("gumbeng")
.mul(getFreq('c3'))
.freq().clip(.8).room("3:10").rdim(10000).rfade(5)`}
/>
@@ -48,7 +48,7 @@ Additionally, you can combo this with `fmap` so that the base note changes:
<MiniRepl
client:idle
tune={`"9 11 12 10 - - -".tune("gunkali")
tune={`i("9 11 12 10 - - -").tune("gunkali")
.mul("<c3 c3 a3 d#3>".fmap(getFreq))
.freq().legato("2 .7").room("1:15").rdim(8500).rlp(14000).rfade(8)`}
/>
@@ -57,8 +57,7 @@ Combining this with various polyrhythm tricks can become very evocative:
<MiniRepl
client:idle
tune={`"<[0 3 1 -] [-1 4 2 8]> ~ ~,<-4 -5>"
.transpose(4)
tune={`i("<[0 3 1 -] [-1 4 2 8]> ~ ~,<-4 -5>".add(4))
.tune("iraq")
.mul("<c3 d3 c#3>".fmap(getFreq))
.freq().clip(.5).room(1).rfade(9)`}
@@ -71,7 +70,7 @@ Take the `sanza` tuning:
<MiniRepl
client:idle
tune={`"4 5 6 7 8 9".tune("sanza")
tune={`i("4 5 6 7 8 9").tune("sanza")
.mul(getFreq('c3'))
.freq()`}
/>
@@ -79,15 +78,15 @@ Take the `sanza` tuning:
Notes 7 and 9 will clash quite a bit if you arp them normally. Many tunings will have this sort of sound, and it can feel distracting on its own.
See how close they are on the pitch wheel?
<MiniRepl client:idle tune={`"[7 9]!3".tune("sanza").mul(getFreq('c3')).freq()._pitchwheel()`} />
<MiniRepl client:idle tune={`i("[7 9]!3").tune("sanza").mul(getFreq('c3')).freq()._pitchwheel()`} />
This quality is often due to how the tunings were formed with instruments that were played differently than a piano.
As such, some tunings are much better strummed, with the subtle clash of the detuned notes actually making the sound much more magical:
<MiniRepl
client:idle
tune={`"[0 1 2 3 4 5 6]@0.3 -"
.transpose("<2 5 8 1>")
tune={`i("[0 1 2 3 4 5 6]@0.3 -"
.add("<2 5 8 1>"))
.tune("sanza")
.mul(getFreq('c3')).freq()
.legato("3").room(1).rfade(5)`}
@@ -102,7 +101,7 @@ You can also give tune a list of frequencies to use as the scale:
<MiniRepl
client:idle
tune={`"0 1 2 3 4".tune([
tune={`i("0 1 2 3 4").tune([
261.6255653006,
302.72962012827,
350.29154279212,