diff --git a/packages/core/controls.mjs b/packages/core/controls.mjs
index 291cfa9e6..1a3f326aa 100644
--- a/packages/core/controls.mjs
+++ b/packages/core/controls.mjs
@@ -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
*
diff --git a/packages/xen/tune.mjs b/packages/xen/tune.mjs
index 7584b7f2f..a070515b3 100644
--- a/packages/xen/tune.mjs
+++ b/packages/xen/tune.mjs
@@ -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));
});
});
diff --git a/packages/xen/xen.mjs b/packages/xen/xen.mjs
index 54b81a2b9..828cab6ea 100644
--- a/packages/xen/xen.mjs
+++ b/packages/xen/xen.mjs
@@ -4,7 +4,7 @@ Copyright (C) 2022 Strudel contributors - see .
*/
-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);
diff --git a/test/__snapshots__/examples.test.mjs.snap b/test/__snapshots__/examples.test.mjs.snap
index 7d7b12e1a..831e48767 100644
--- a/test/__snapshots__/examples.test.mjs.snap
+++ b/test/__snapshots__/examples.test.mjs.snap
@@ -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 ]",
diff --git a/website/src/pages/learn/xen.mdx b/website/src/pages/learn/xen.mdx
index a661c7b08..144a40f1a 100644
--- a/website/src/pages/learn/xen.mdx
+++ b/website/src/pages/learn/xen.mdx
@@ -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:
-
+
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`
@@ -39,7 +39,7 @@ Some tunings become more pronounced with a longer reverb decay:
-".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:
".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:
~ ~,<-4 -5>"
- .transpose(4)
+ tune={`i("<[0 3 1 -] [-1 4 2 8]> ~ ~,<-4 -5>".add(4))
.tune("iraq")
.mul("".fmap(getFreq))
.freq().clip(.5).room(1).rfade(9)`}
@@ -71,7 +70,7 @@ Take the `sanza` tuning:
@@ -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?
-
+
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:
")
+ 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: