jevstrudel.git / packages / edo / ratios.mjs
1/*
2ratios.mjs - lists whole number ratios for pitch intervals
3           - Port of pitfalls/lib/ratios.lua - see <https://github.com/robmckinnon/pitfalls/blob/main/lib/ratios.lua>
4Copyright (C) 2025 Rob McKinnon and Strudel contributors - see <https://codeberg.org/uzu/strudel/src/branch/main/packages/edo/ratios.mjs>
5This 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/>.
6*/
7const ratiointervals = {};
8
9// FJS Calculators - https://misotanni.github.io/fjs/en/calc.html
10// List of pitch intervals - https://en.wikipedia.org/wiki/List_of_pitch_intervals
11// Gallery of just intervals - https://en.xen.wiki/w/Gallery_of_just_intervals
12// Two letter codes changed to make different interval labels more unique.
13ratiointervals.list = new Map([
14  [1, ['P1', 'P1', 'P1', 1, 1]], // unison P1
15  [16 / 15, ['m2', 'm2', 'm2_5', 16, 15]], // minor second m2
16  [15 / 14, ['A1', 'A1', 'A1^5_7', 15, 14]], // augmented unison
17  [13 / 12, ['t2', 'm2', 'm2^13', 13, 12]], // tridecimal neutral second
18  [12 / 11, ['N2', 'M2', 'M2_11', 12, 11]], // undecimal neutral second
19  [11 / 10, ['n2', 'm2', 'm2^11_5', 11, 10]], // undecimal submajor second
20  [10 / 9, ['T2', 'M2', 'M2^5', 10, 9]], // classic (whole) tone
21  [9 / 8, ['M2', 'M2', 'M2', 9, 8]], // major second M2
22  [8 / 7, ['S2', 'M2', 'M2_7', 8, 7]], // septimal major second
23  [7 / 6, ['s3', 'm3', 'm3^7', 7, 6]], // septimal minor third
24  [19 / 16, ['o3', 'm3', 'm3^19', 19, 16]], // otonal minor third
25  [6 / 5, ['m3', 'm3', 'm3_5', 6, 5]], // minor third m3
26  [17 / 14, ['t3', 'm3', 'm3^17_7', 17, 14]], // septendecimal supraminor third
27  [11 / 9, ['n3', 'm3', 'm3^11', 11, 9]], // undecimal neutral third
28  [5 / 4, ['M3', 'M3', 'M3^5', 5, 4]], // major third M3
29  [9 / 7, ['S3', 'M3', 'M3_7', 9, 7]], // septimal major third SM3
30  [13 / 10, ['d4', 'd4', 'd4^13_5', 13, 10]], // Barbados third
31  [4 / 3, ['P4', 'P4', 'P4', 4, 3]], // perfect fourth P4
32  [19 / 14, ['N4', 'P4', 'P4^19_7', 19, 14]], // undevicesimal wide fourth
33  [11 / 8, ['n4', 'P4', 'P4^11', 11, 8]], // super-fourth
34  [25 / 18, ['a4', 'A4', 'A4^5,5', 25, 18]], // classic augmented fourth
35  [7 / 5, ['sT', 'd5', 'd5^7_5', 7, 5]], // lesser septimal tritone
36  [45 / 32, ['A4', 'A4', 'A4^5', 45, 32]], // just augmented fourth
37  [17 / 12, ['d5', 'd5', 'd5^17', 17, 12]], // larger septendecimal tritone
38  [10 / 7, ['ST', 'A4', 'A4^5_7', 10, 7]], // greater septimal tritone
39  [13 / 9, ['t5', 'd5', 'd5^13', 13, 9]], // tridecimal diminished fifth
40  [3 / 2, ['P5', 'P5', 'P5', 3, 2]], // perfect fifth P5
41  [14 / 9, ['s6', 'M6', 'm6^7', 14, 9]], // subminor sixth or septimal sixth
42  [25 / 16, ['a5', 'A5', 'A5^5,5', 25, 16]], // classic augmented fifth
43  [11 / 7, ['A5', 'P5', 'P5^11_7', 11, 7]], // undecimal minor sixth
44  [8 / 5, ['m6', 'm6', 'm6_5', 8, 5]], // minor sixth m6
45  [13 / 8, ['N6', 'm6', 'm6^13', 13, 8]], // tridecimal neutral sixth
46  [18 / 11, ['n6', 'M6', 'M6_11', 18, 11]], // undecimal neutral sixth
47  [5 / 3, ['M6', 'M6', 'M6^5', 5, 3]], // just major sixth M6
48  [128 / 75, ['d7', 'd7', 'd7_5,5', 128, 75]], // diminished seventh
49  [17 / 10, ['T6', 'd7', 'd7^17_5', 17, 10]], // septendecimal diminished seventh
50  [12 / 7, ['S6', 'M6', 'M6_7', 12, 7]], // septimal major sixth
51  [7 / 4, ['s7', 'm7', 'm7^7', 7, 4]], // septimal minor seventh
52  [16 / 9, ['m7', 'm7', 'm7', 16, 9]], // lesser minor seventh
53  [9 / 5, ['g7', 'm7', 'm7_5', 9, 5]], // greater just minor seventh
54  [11 / 6, ['n7', 'm7', 'm7^11', 11, 6]], // undecimal neutral seventh
55  [13 / 7, ['N7', 'm7', 'm7^13_7', 13, 7]], // tridecimal neutral seventh
56  [15 / 8, ['M7', 'M7', 'M7^5', 15, 8]], // major seventh
57  [17 / 9, ['T7', 'd8', 'd8^17', 17, 9]], // large septendecimal major seventh
58  [19 / 10, ['d8', 'd8', 'd8^19_5', 19, 10]], // large undevicesimal major seventh
59  [2, ['P8', 'P8', 'P8', 2, 1]], // octave P8
60]);
61
62ratiointervals.key = function (ratio) {
63  return ratio == null ? '' : ratiointervals.list.get(ratio)?.[0] || '';
64};
65
66ratiointervals.label = function (ratio) {
67  return ratio == null ? '' : ratiointervals.list.get(ratio)?.[1] || '';
68};
69
70ratiointervals.fjs = function (ratio) {
71  return ratio == null ? '' : ratiointervals.list.get(ratio)?.[2] || '';
72};
73
74ratiointervals.nom = function (ratio) {
75  return ratio == null ? null : ratiointervals.list.get(ratio)?.[3] || null;
76};
77
78ratiointervals.denom = function (ratio) {
79  return ratio == null ? null : ratiointervals.list.get(ratio)?.[4] || null;
80};
81
82ratiointervals.nearestInterval = function (v) {
83  let min = 1;
84  let match = null;
85  for (const [ratio, _labels] of ratiointervals.list) {
86    const diff = Math.abs((ratio - v) / ratio);
87    if (diff < min) {
88      min = diff;
89      match = ratio;
90    }
91  }
92  return min < 0.01 ? [min, match] : [null, null];
93};
94
95export default ratiointervals;