@musodojo/music-theory-data 20.3.0 → 20.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/esm/_dnt.test_shims.d.ts.map +1 -0
- package/esm/deps/jsr.io/@std/assert/1.0.18/almost_equals.d.ts.map +1 -0
- package/esm/deps/jsr.io/@std/assert/1.0.18/array_includes.d.ts.map +1 -0
- package/esm/deps/jsr.io/@std/assert/1.0.18/assert.d.ts.map +1 -0
- package/esm/deps/jsr.io/@std/assert/1.0.18/assertion_error.d.ts.map +1 -0
- package/esm/deps/jsr.io/@std/assert/1.0.18/equal.d.ts.map +1 -0
- package/esm/deps/jsr.io/@std/assert/1.0.18/equals.d.ts.map +1 -0
- package/esm/deps/jsr.io/@std/assert/1.0.18/exists.d.ts.map +1 -0
- package/esm/deps/jsr.io/@std/assert/1.0.18/fail.d.ts.map +1 -0
- package/esm/deps/jsr.io/@std/assert/1.0.18/false.d.ts.map +1 -0
- package/esm/deps/jsr.io/@std/assert/1.0.18/greater.d.ts.map +1 -0
- package/esm/deps/jsr.io/@std/assert/1.0.18/greater_or_equal.d.ts.map +1 -0
- package/esm/deps/jsr.io/@std/assert/1.0.18/instance_of.d.ts.map +1 -0
- package/esm/deps/jsr.io/@std/assert/1.0.18/is_error.d.ts.map +1 -0
- package/esm/deps/jsr.io/@std/assert/1.0.18/less.d.ts.map +1 -0
- package/esm/deps/jsr.io/@std/assert/1.0.18/less_or_equal.d.ts.map +1 -0
- package/esm/deps/jsr.io/@std/assert/1.0.18/match.d.ts.map +1 -0
- package/esm/deps/jsr.io/@std/assert/1.0.18/mod.d.ts.map +1 -0
- package/esm/deps/jsr.io/@std/assert/1.0.18/not_equals.d.ts.map +1 -0
- package/esm/deps/jsr.io/@std/assert/1.0.18/not_instance_of.d.ts.map +1 -0
- package/esm/deps/jsr.io/@std/assert/1.0.18/not_match.d.ts.map +1 -0
- package/esm/deps/jsr.io/@std/assert/1.0.18/not_strict_equals.d.ts.map +1 -0
- package/esm/deps/jsr.io/@std/assert/1.0.18/object_match.d.ts.map +1 -0
- package/esm/deps/jsr.io/@std/assert/1.0.18/rejects.d.ts.map +1 -0
- package/esm/deps/jsr.io/@std/assert/1.0.18/strict_equals.d.ts.map +1 -0
- package/esm/deps/jsr.io/@std/assert/1.0.18/string_includes.d.ts.map +1 -0
- package/esm/deps/jsr.io/@std/assert/1.0.18/throws.d.ts.map +1 -0
- package/esm/deps/jsr.io/@std/assert/1.0.18/unimplemented.d.ts.map +1 -0
- package/esm/deps/jsr.io/@std/assert/1.0.18/unreachable.d.ts.map +1 -0
- package/esm/deps/jsr.io/@std/internal/1.0.12/build_message.d.ts.map +1 -0
- package/esm/deps/jsr.io/@std/internal/1.0.12/diff.d.ts.map +1 -0
- package/esm/deps/jsr.io/@std/internal/1.0.12/diff_str.d.ts.map +1 -0
- package/esm/deps/jsr.io/@std/internal/1.0.12/format.d.ts.map +1 -0
- package/esm/deps/jsr.io/@std/internal/1.0.12/styles.d.ts.map +1 -0
- package/esm/deps/jsr.io/@std/internal/1.0.12/types.d.ts.map +1 -0
- package/esm/package.json +3 -0
- package/esm/src/data/chords/mod.d.ts +10 -0
- package/esm/src/data/chords/mod.d.ts.map +1 -0
- package/esm/src/data/chords/mod.js +72 -0
- package/esm/src/data/colors/mod.d.ts +51 -0
- package/esm/src/data/colors/mod.d.ts.map +1 -0
- package/esm/src/data/colors/mod.js +79 -0
- package/esm/src/data/labels/mod.d.ts +3 -0
- package/esm/src/data/labels/mod.d.ts.map +1 -0
- package/esm/src/data/labels/mod.js +2 -0
- package/esm/src/data/labels/note-label-collections.d.ts +62 -0
- package/esm/src/data/labels/note-label-collections.d.ts.map +1 -0
- package/esm/src/data/labels/note-label-collections.js +91 -0
- package/esm/src/data/labels/note-labels.d.ts +314 -0
- package/esm/src/data/labels/note-labels.d.ts.map +1 -0
- package/esm/src/data/labels/note-labels.js +502 -0
- package/esm/src/data/mod.d.ts +5 -0
- package/esm/src/data/mod.d.ts.map +1 -0
- package/esm/src/data/mod.js +4 -0
- package/esm/src/data/note-collections/augmented-variants.d.ts +8 -0
- package/esm/src/data/note-collections/augmented-variants.d.ts.map +1 -0
- package/esm/src/data/note-collections/augmented-variants.js +27 -0
- package/esm/src/data/note-collections/diatonic-modes.d.ts +14 -0
- package/esm/src/data/note-collections/diatonic-modes.d.ts.map +1 -0
- package/esm/src/data/note-collections/diatonic-modes.js +268 -0
- package/esm/src/data/note-collections/diminished-variants.d.ts +11 -0
- package/esm/src/data/note-collections/diminished-variants.d.ts.map +1 -0
- package/esm/src/data/note-collections/diminished-variants.js +72 -0
- package/esm/src/data/note-collections/dominant-variants.d.ts +11 -0
- package/esm/src/data/note-collections/dominant-variants.d.ts.map +1 -0
- package/esm/src/data/note-collections/dominant-variants.js +116 -0
- package/esm/src/data/note-collections/harmonic-minor-modes.d.ts +13 -0
- package/esm/src/data/note-collections/harmonic-minor-modes.d.ts.map +1 -0
- package/esm/src/data/note-collections/harmonic-minor-modes.js +246 -0
- package/esm/src/data/note-collections/major-variants.d.ts +12 -0
- package/esm/src/data/note-collections/major-variants.d.ts.map +1 -0
- package/esm/src/data/note-collections/major-variants.js +123 -0
- package/esm/src/data/note-collections/melodic-minor-modes.d.ts +13 -0
- package/esm/src/data/note-collections/melodic-minor-modes.d.ts.map +1 -0
- package/esm/src/data/note-collections/melodic-minor-modes.js +206 -0
- package/esm/src/data/note-collections/minor-variants.d.ts +13 -0
- package/esm/src/data/note-collections/minor-variants.d.ts.map +1 -0
- package/esm/src/data/note-collections/minor-variants.js +118 -0
- package/esm/src/data/note-collections/mod.d.ts +86 -0
- package/esm/src/data/note-collections/mod.d.ts.map +1 -0
- package/esm/src/data/note-collections/mod.js +86 -0
- package/esm/src/data/note-collections/other-collections.d.ts +10 -0
- package/esm/src/data/note-collections/other-collections.d.ts.map +1 -0
- package/esm/src/data/note-collections/other-collections.js +91 -0
- package/esm/src/data/note-collections/pentatonic-variants.d.ts +11 -0
- package/esm/src/data/note-collections/pentatonic-variants.d.ts.map +1 -0
- package/esm/src/data/note-collections/pentatonic-variants.js +86 -0
- package/esm/src/mod.d.ts +74 -0
- package/esm/src/mod.d.ts.map +1 -0
- package/{src/mod.ts → esm/src/mod.js} +2 -3
- package/esm/src/utils/chords.d.ts +14 -0
- package/esm/src/utils/chords.d.ts.map +1 -0
- package/esm/src/utils/chords.js +93 -0
- package/esm/src/utils/colors.d.ts +14 -0
- package/esm/src/utils/colors.d.ts.map +1 -0
- package/esm/src/utils/colors.js +47 -0
- package/esm/src/utils/intervals.d.ts +26 -0
- package/esm/src/utils/intervals.d.ts.map +1 -0
- package/esm/src/utils/intervals.js +61 -0
- package/esm/src/utils/midi-note-sequences.d.ts +23 -0
- package/esm/src/utils/midi-note-sequences.d.ts.map +1 -0
- package/esm/src/utils/midi-note-sequences.js +109 -0
- package/esm/src/utils/midi.d.ts +7 -0
- package/esm/src/utils/midi.d.ts.map +1 -0
- package/esm/src/utils/midi.js +26 -0
- package/esm/src/utils/mod.d.ts +9 -0
- package/esm/src/utils/mod.d.ts.map +1 -0
- package/esm/src/utils/mod.js +8 -0
- package/esm/src/utils/note-collections.d.ts +24 -0
- package/esm/src/utils/note-collections.d.ts.map +1 -0
- package/esm/src/utils/note-collections.js +132 -0
- package/esm/src/utils/note-names.d.ts +23 -0
- package/esm/src/utils/note-names.d.ts.map +1 -0
- package/esm/src/utils/note-names.js +127 -0
- package/esm/src/utils/rotate-array.d.ts +19 -0
- package/esm/src/utils/rotate-array.d.ts.map +1 -0
- package/{src/utils/rotate-array.ts → esm/src/utils/rotate-array.js} +10 -12
- package/esm/tests/get-chord-details.test.d.ts.map +1 -0
- package/esm/tests/get-note-names.test.d.ts.map +1 -0
- package/esm/tests/midi.test.d.ts.map +1 -0
- package/esm/tests/note-name-strings.test.d.ts.map +1 -0
- package/esm/tests/search-note-collections.test.d.ts.map +1 -0
- package/esm/tests/transform-intervals.test.d.ts.map +1 -0
- package/package.json +21 -6
- package/script/_dnt.test_shims.d.ts.map +1 -0
- package/script/deps/jsr.io/@std/assert/1.0.18/almost_equals.d.ts.map +1 -0
- package/script/deps/jsr.io/@std/assert/1.0.18/array_includes.d.ts.map +1 -0
- package/script/deps/jsr.io/@std/assert/1.0.18/assert.d.ts.map +1 -0
- package/script/deps/jsr.io/@std/assert/1.0.18/assertion_error.d.ts.map +1 -0
- package/script/deps/jsr.io/@std/assert/1.0.18/equal.d.ts.map +1 -0
- package/script/deps/jsr.io/@std/assert/1.0.18/equals.d.ts.map +1 -0
- package/script/deps/jsr.io/@std/assert/1.0.18/exists.d.ts.map +1 -0
- package/script/deps/jsr.io/@std/assert/1.0.18/fail.d.ts.map +1 -0
- package/script/deps/jsr.io/@std/assert/1.0.18/false.d.ts.map +1 -0
- package/script/deps/jsr.io/@std/assert/1.0.18/greater.d.ts.map +1 -0
- package/script/deps/jsr.io/@std/assert/1.0.18/greater_or_equal.d.ts.map +1 -0
- package/script/deps/jsr.io/@std/assert/1.0.18/instance_of.d.ts.map +1 -0
- package/script/deps/jsr.io/@std/assert/1.0.18/is_error.d.ts.map +1 -0
- package/script/deps/jsr.io/@std/assert/1.0.18/less.d.ts.map +1 -0
- package/script/deps/jsr.io/@std/assert/1.0.18/less_or_equal.d.ts.map +1 -0
- package/script/deps/jsr.io/@std/assert/1.0.18/match.d.ts.map +1 -0
- package/script/deps/jsr.io/@std/assert/1.0.18/mod.d.ts.map +1 -0
- package/script/deps/jsr.io/@std/assert/1.0.18/not_equals.d.ts.map +1 -0
- package/script/deps/jsr.io/@std/assert/1.0.18/not_instance_of.d.ts.map +1 -0
- package/script/deps/jsr.io/@std/assert/1.0.18/not_match.d.ts.map +1 -0
- package/script/deps/jsr.io/@std/assert/1.0.18/not_strict_equals.d.ts.map +1 -0
- package/script/deps/jsr.io/@std/assert/1.0.18/object_match.d.ts.map +1 -0
- package/script/deps/jsr.io/@std/assert/1.0.18/rejects.d.ts.map +1 -0
- package/script/deps/jsr.io/@std/assert/1.0.18/strict_equals.d.ts.map +1 -0
- package/script/deps/jsr.io/@std/assert/1.0.18/string_includes.d.ts.map +1 -0
- package/script/deps/jsr.io/@std/assert/1.0.18/throws.d.ts.map +1 -0
- package/script/deps/jsr.io/@std/assert/1.0.18/unimplemented.d.ts.map +1 -0
- package/script/deps/jsr.io/@std/assert/1.0.18/unreachable.d.ts.map +1 -0
- package/script/deps/jsr.io/@std/internal/1.0.12/build_message.d.ts.map +1 -0
- package/script/deps/jsr.io/@std/internal/1.0.12/diff.d.ts.map +1 -0
- package/script/deps/jsr.io/@std/internal/1.0.12/diff_str.d.ts.map +1 -0
- package/script/deps/jsr.io/@std/internal/1.0.12/format.d.ts.map +1 -0
- package/script/deps/jsr.io/@std/internal/1.0.12/styles.d.ts.map +1 -0
- package/script/deps/jsr.io/@std/internal/1.0.12/types.d.ts.map +1 -0
- package/script/package.json +3 -0
- package/script/src/data/chords/mod.d.ts +10 -0
- package/script/src/data/chords/mod.d.ts.map +1 -0
- package/script/src/data/chords/mod.js +75 -0
- package/script/src/data/colors/mod.d.ts +51 -0
- package/script/src/data/colors/mod.d.ts.map +1 -0
- package/script/src/data/colors/mod.js +82 -0
- package/script/src/data/labels/mod.d.ts +3 -0
- package/script/src/data/labels/mod.d.ts.map +1 -0
- package/script/src/data/labels/mod.js +18 -0
- package/script/src/data/labels/note-label-collections.d.ts +62 -0
- package/script/src/data/labels/note-label-collections.d.ts.map +1 -0
- package/script/src/data/labels/note-label-collections.js +94 -0
- package/script/src/data/labels/note-labels.d.ts +314 -0
- package/script/src/data/labels/note-labels.d.ts.map +1 -0
- package/script/src/data/labels/note-labels.js +505 -0
- package/script/src/data/mod.d.ts +5 -0
- package/script/src/data/mod.d.ts.map +1 -0
- package/script/src/data/mod.js +20 -0
- package/script/src/data/note-collections/augmented-variants.d.ts +8 -0
- package/script/src/data/note-collections/augmented-variants.d.ts.map +1 -0
- package/script/src/data/note-collections/augmented-variants.js +30 -0
- package/script/src/data/note-collections/diatonic-modes.d.ts +14 -0
- package/script/src/data/note-collections/diatonic-modes.d.ts.map +1 -0
- package/script/src/data/note-collections/diatonic-modes.js +271 -0
- package/script/src/data/note-collections/diminished-variants.d.ts +11 -0
- package/script/src/data/note-collections/diminished-variants.d.ts.map +1 -0
- package/script/src/data/note-collections/diminished-variants.js +75 -0
- package/script/src/data/note-collections/dominant-variants.d.ts +11 -0
- package/script/src/data/note-collections/dominant-variants.d.ts.map +1 -0
- package/script/src/data/note-collections/dominant-variants.js +119 -0
- package/script/src/data/note-collections/harmonic-minor-modes.d.ts +13 -0
- package/script/src/data/note-collections/harmonic-minor-modes.d.ts.map +1 -0
- package/script/src/data/note-collections/harmonic-minor-modes.js +249 -0
- package/script/src/data/note-collections/major-variants.d.ts +12 -0
- package/script/src/data/note-collections/major-variants.d.ts.map +1 -0
- package/script/src/data/note-collections/major-variants.js +126 -0
- package/script/src/data/note-collections/melodic-minor-modes.d.ts +13 -0
- package/script/src/data/note-collections/melodic-minor-modes.d.ts.map +1 -0
- package/script/src/data/note-collections/melodic-minor-modes.js +209 -0
- package/script/src/data/note-collections/minor-variants.d.ts +13 -0
- package/script/src/data/note-collections/minor-variants.d.ts.map +1 -0
- package/script/src/data/note-collections/minor-variants.js +121 -0
- package/script/src/data/note-collections/mod.d.ts +86 -0
- package/script/src/data/note-collections/mod.d.ts.map +1 -0
- package/script/src/data/note-collections/mod.js +99 -0
- package/script/src/data/note-collections/other-collections.d.ts +10 -0
- package/script/src/data/note-collections/other-collections.d.ts.map +1 -0
- package/script/src/data/note-collections/other-collections.js +94 -0
- package/script/src/data/note-collections/pentatonic-variants.d.ts +11 -0
- package/script/src/data/note-collections/pentatonic-variants.d.ts.map +1 -0
- package/script/src/data/note-collections/pentatonic-variants.js +89 -0
- package/script/src/mod.d.ts +74 -0
- package/script/src/mod.d.ts.map +1 -0
- package/script/src/mod.js +88 -0
- package/script/src/utils/chords.d.ts +14 -0
- package/script/src/utils/chords.d.ts.map +1 -0
- package/script/src/utils/chords.js +102 -0
- package/script/src/utils/colors.d.ts +14 -0
- package/script/src/utils/colors.d.ts.map +1 -0
- package/script/src/utils/colors.js +51 -0
- package/script/src/utils/intervals.d.ts +26 -0
- package/script/src/utils/intervals.d.ts.map +1 -0
- package/script/src/utils/intervals.js +69 -0
- package/script/src/utils/midi-note-sequences.d.ts +23 -0
- package/script/src/utils/midi-note-sequences.d.ts.map +1 -0
- package/script/src/utils/midi-note-sequences.js +112 -0
- package/script/src/utils/midi.d.ts +7 -0
- package/script/src/utils/midi.d.ts.map +1 -0
- package/script/src/utils/midi.js +32 -0
- package/script/src/utils/mod.d.ts +9 -0
- package/script/src/utils/mod.d.ts.map +1 -0
- package/script/src/utils/mod.js +24 -0
- package/script/src/utils/note-collections.d.ts +24 -0
- package/script/src/utils/note-collections.d.ts.map +1 -0
- package/script/src/utils/note-collections.js +137 -0
- package/script/src/utils/note-names.d.ts +23 -0
- package/script/src/utils/note-names.d.ts.map +1 -0
- package/script/src/utils/note-names.js +134 -0
- package/script/src/utils/rotate-array.d.ts +19 -0
- package/script/src/utils/rotate-array.d.ts.map +1 -0
- package/script/src/utils/rotate-array.js +32 -0
- package/script/tests/get-chord-details.test.d.ts.map +1 -0
- package/script/tests/get-note-names.test.d.ts.map +1 -0
- package/script/tests/midi.test.d.ts.map +1 -0
- package/script/tests/note-name-strings.test.d.ts.map +1 -0
- package/script/tests/search-note-collections.test.d.ts.map +1 -0
- package/script/tests/transform-intervals.test.d.ts.map +1 -0
- package/src/data/chords/mod.ts +0 -86
- package/src/data/colors/mod.ts +0 -109
- package/src/data/labels/mod.ts +0 -2
- package/src/data/labels/note-label-collections.ts +0 -124
- package/src/data/labels/note-labels.ts +0 -702
- package/src/data/mod.ts +0 -4
- package/src/data/note-collections/augmented-variants.ts +0 -35
- package/src/data/note-collections/diatonic-modes.ts +0 -281
- package/src/data/note-collections/diminished-variants.ts +0 -87
- package/src/data/note-collections/dominant-variants.ts +0 -131
- package/src/data/note-collections/harmonic-minor-modes.ts +0 -261
- package/src/data/note-collections/major-variants.ts +0 -135
- package/src/data/note-collections/melodic-minor-modes.ts +0 -221
- package/src/data/note-collections/minor-variants.ts +0 -131
- package/src/data/note-collections/mod.ts +0 -108
- package/src/data/note-collections/other-collections.ts +0 -107
- package/src/data/note-collections/pentatonic-variants.ts +0 -99
- package/src/types/chords.d.ts +0 -48
- package/src/types/midi.d.ts +0 -131
- package/src/types/mod.d.ts +0 -3
- package/src/types/note-collections.d.ts +0 -86
- package/src/utils/chords.ts +0 -168
- package/src/utils/colors.ts +0 -57
- package/src/utils/intervals.ts +0 -111
- package/src/utils/midi-note-sequences.ts +0 -171
- package/src/utils/midi.ts +0 -51
- package/src/utils/mod.ts +0 -8
- package/src/utils/note-collections.ts +0 -188
- package/src/utils/note-names.ts +0 -182
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { diatonicSeventhChords, diatonicTriads, harmonicMinorSeventhChords, harmonicMinorTriads, lowerCaseRomanNumerals, melodicMinorSeventhChords, melodicMinorTriads, upperCaseRomanNumerals, } from "../data/chords/mod.js";
|
|
2
|
+
import { rotateArray } from "./rotate-array.js";
|
|
3
|
+
import { harmonicMinorModes, } from "../data/note-collections/harmonic-minor-modes.js";
|
|
4
|
+
import { melodicMinorModes, } from "../data/note-collections/melodic-minor-modes.js";
|
|
5
|
+
import { diatonicModes, } from "../data/note-collections/diatonic-modes.js";
|
|
6
|
+
export function getRomanTriads(triads) {
|
|
7
|
+
return triads.map((quality, i) => {
|
|
8
|
+
switch (quality) {
|
|
9
|
+
case "M":
|
|
10
|
+
return upperCaseRomanNumerals[i];
|
|
11
|
+
case "m":
|
|
12
|
+
return lowerCaseRomanNumerals[i];
|
|
13
|
+
case "°":
|
|
14
|
+
return lowerCaseRomanNumerals[i] + quality;
|
|
15
|
+
case "+":
|
|
16
|
+
return upperCaseRomanNumerals[i] + quality;
|
|
17
|
+
default:
|
|
18
|
+
// This should not happen with valid data. Fail fast if it does.
|
|
19
|
+
throw new Error(`Unhandled triad quality: ${quality}`);
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
export function getRomanSeventhChords(sevenths) {
|
|
24
|
+
return sevenths.map((quality, i) => {
|
|
25
|
+
switch (quality) {
|
|
26
|
+
case "M7":
|
|
27
|
+
return upperCaseRomanNumerals[i] + quality;
|
|
28
|
+
case "m7":
|
|
29
|
+
return lowerCaseRomanNumerals[i] + quality;
|
|
30
|
+
case "7":
|
|
31
|
+
return upperCaseRomanNumerals[i] + quality;
|
|
32
|
+
case "ø7":
|
|
33
|
+
return lowerCaseRomanNumerals[i] + quality;
|
|
34
|
+
case "m7♭5":
|
|
35
|
+
return lowerCaseRomanNumerals[i] + quality;
|
|
36
|
+
case "°7":
|
|
37
|
+
return lowerCaseRomanNumerals[i] + quality;
|
|
38
|
+
case "m(M7)":
|
|
39
|
+
return lowerCaseRomanNumerals[i] + "M7";
|
|
40
|
+
case "+M7":
|
|
41
|
+
return upperCaseRomanNumerals[i] + quality;
|
|
42
|
+
case "M7♯5":
|
|
43
|
+
return upperCaseRomanNumerals[i] + quality;
|
|
44
|
+
default:
|
|
45
|
+
// This should not happen with valid data. Fail fast if it does.
|
|
46
|
+
throw new Error(`Unhandled seventh chord quality: ${quality}`);
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
export function getChordDetailsForMode(intervals, triads, sevenths, romanTriads, romanSeventhChords) {
|
|
51
|
+
return intervals
|
|
52
|
+
.filter((interval) => interval !== "8")
|
|
53
|
+
.map((interval, i) => ({
|
|
54
|
+
interval,
|
|
55
|
+
triad: triads[i],
|
|
56
|
+
seventh: sevenths[i],
|
|
57
|
+
romanTriad: romanTriads[i],
|
|
58
|
+
romanSeventhChord: romanSeventhChords[i],
|
|
59
|
+
}));
|
|
60
|
+
}
|
|
61
|
+
export function getChordDetailsForDiatonicMode(diatonicModeKey) {
|
|
62
|
+
const mode = diatonicModes[diatonicModeKey];
|
|
63
|
+
const rotation = mode.rotation;
|
|
64
|
+
const rotatedTriads = rotateArray(diatonicTriads, rotation);
|
|
65
|
+
const rotatedSeventhChords = rotateArray(diatonicSeventhChords, rotation);
|
|
66
|
+
return getChordDetailsForMode(mode.intervals, rotatedTriads, rotatedSeventhChords, getRomanTriads(rotatedTriads), getRomanSeventhChords(rotatedSeventhChords));
|
|
67
|
+
}
|
|
68
|
+
export function getChordDetailsForHarmonicMinorMode(harmonicMinorModeKey) {
|
|
69
|
+
const mode = harmonicMinorModes[harmonicMinorModeKey];
|
|
70
|
+
const rotation = mode.rotation;
|
|
71
|
+
const rotatedTriads = rotateArray(harmonicMinorTriads, rotation);
|
|
72
|
+
const rotatedSeventhChords = rotateArray(harmonicMinorSeventhChords, rotation);
|
|
73
|
+
return getChordDetailsForMode(mode.intervals, rotatedTriads, rotatedSeventhChords, getRomanTriads(rotatedTriads), getRomanSeventhChords(rotatedSeventhChords));
|
|
74
|
+
}
|
|
75
|
+
export function getChordDetailsForMelodicMinorMode(melodicMinorModeKey) {
|
|
76
|
+
const mode = melodicMinorModes[melodicMinorModeKey];
|
|
77
|
+
const rotation = mode.rotation;
|
|
78
|
+
const rotatedTriads = rotateArray(melodicMinorTriads, rotation);
|
|
79
|
+
const rotatedSeventhChords = rotateArray(melodicMinorSeventhChords, rotation);
|
|
80
|
+
return getChordDetailsForMode(mode.intervals, rotatedTriads, rotatedSeventhChords, getRomanTriads(rotatedTriads), getRomanSeventhChords(rotatedSeventhChords));
|
|
81
|
+
}
|
|
82
|
+
export function getChordDetailsForModeKey(modeKey) {
|
|
83
|
+
if (Object.prototype.hasOwnProperty.call(diatonicModes, modeKey)) {
|
|
84
|
+
return getChordDetailsForDiatonicMode(modeKey);
|
|
85
|
+
}
|
|
86
|
+
if (Object.prototype.hasOwnProperty.call(harmonicMinorModes, modeKey)) {
|
|
87
|
+
return getChordDetailsForHarmonicMinorMode(modeKey);
|
|
88
|
+
}
|
|
89
|
+
if (Object.prototype.hasOwnProperty.call(melodicMinorModes, modeKey)) {
|
|
90
|
+
return getChordDetailsForMelodicMinorMode(modeKey);
|
|
91
|
+
}
|
|
92
|
+
return [];
|
|
93
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parses a hex color string and returns its RGB components.
|
|
3
|
+
* Supports 3-digit (#RGB) and 6-digit (#RRGGBB) formats.
|
|
4
|
+
* @param hex The hex color string.
|
|
5
|
+
* @returns An array [r, g, b] or null if the format is invalid.
|
|
6
|
+
*/
|
|
7
|
+
export declare function parseHexColor(hex: string): [number, number, number] | null;
|
|
8
|
+
/**
|
|
9
|
+
* Calculates whether black or white text has a better contrast ratio against a given background color.
|
|
10
|
+
* @param {string} color - The background color in hex format, e.g. "#E21C48"
|
|
11
|
+
* @returns {'black' | 'white'} - The color that provides better contrast.
|
|
12
|
+
*/
|
|
13
|
+
export declare function getContrastColor(color: string): "black" | "white";
|
|
14
|
+
//# sourceMappingURL=colors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"colors.d.ts","sourceRoot":"","sources":["../../../src/src/utils/colors.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAkB1E;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,GAAG,OAAO,CAyBjE"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parses a hex color string and returns its RGB components.
|
|
3
|
+
* Supports 3-digit (#RGB) and 6-digit (#RRGGBB) formats.
|
|
4
|
+
* @param hex The hex color string.
|
|
5
|
+
* @returns An array [r, g, b] or null if the format is invalid.
|
|
6
|
+
*/
|
|
7
|
+
export function parseHexColor(hex) {
|
|
8
|
+
const hexRegex = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i;
|
|
9
|
+
const shorthandHexRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
|
|
10
|
+
// Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF")
|
|
11
|
+
const fullHex = hex.replace(shorthandHexRegex, (_, r, g, b) => r + r + g + g + b + b);
|
|
12
|
+
const result = hexRegex.exec(fullHex);
|
|
13
|
+
return result
|
|
14
|
+
? [
|
|
15
|
+
parseInt(result[1], 16),
|
|
16
|
+
parseInt(result[2], 16),
|
|
17
|
+
parseInt(result[3], 16),
|
|
18
|
+
]
|
|
19
|
+
: null;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Calculates whether black or white text has a better contrast ratio against a given background color.
|
|
23
|
+
* @param {string} color - The background color in hex format, e.g. "#E21C48"
|
|
24
|
+
* @returns {'black' | 'white'} - The color that provides better contrast.
|
|
25
|
+
*/
|
|
26
|
+
export function getContrastColor(color) {
|
|
27
|
+
// For now, we only support hex colors as that's what's in the library.
|
|
28
|
+
// This can be expanded to support rgb(), hsl(), and color keywords if needed.
|
|
29
|
+
const rgb = parseHexColor(color);
|
|
30
|
+
if (!rgb) {
|
|
31
|
+
return "black"; // Default to black if color parsing fails
|
|
32
|
+
}
|
|
33
|
+
const [r, g, b] = rgb;
|
|
34
|
+
// Formula for relative luminance (from WCAG)
|
|
35
|
+
// https://www.w3.org/TR/WCAG20/#relativeluminancedef
|
|
36
|
+
const getLuminance = (c) => {
|
|
37
|
+
const sRGB = c / 255;
|
|
38
|
+
return sRGB <= 0.03928
|
|
39
|
+
? sRGB / 12.92
|
|
40
|
+
: Math.pow((sRGB + 0.055) / 1.055, 2.4);
|
|
41
|
+
};
|
|
42
|
+
const luminance = 0.2126 * getLuminance(r) +
|
|
43
|
+
0.7152 * getLuminance(g) +
|
|
44
|
+
0.0722 * getLuminance(b);
|
|
45
|
+
// Use a threshold of 0.179 as recommended by WCAG for contrast
|
|
46
|
+
return luminance > 0.179 ? "black" : "white";
|
|
47
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type Interval, type IntervalQuality } from "../data/labels/note-labels.js";
|
|
2
|
+
import type { NoteCollection } from "../types/note-collections";
|
|
3
|
+
export declare function filterOutOctaveIntervals(intervals: readonly Interval[]): Interval[];
|
|
4
|
+
/**
|
|
5
|
+
* Sorts an array of intervals in ascending order based on their integer value.
|
|
6
|
+
* This is a pure function and returns a new sorted array, leaving the original array unchanged.
|
|
7
|
+
* @param intervals The array of intervals to sort.
|
|
8
|
+
* @returns A new array with the sorted intervals.
|
|
9
|
+
*/
|
|
10
|
+
export declare function sortIntervals(intervals: readonly Interval[]): Interval[];
|
|
11
|
+
export type IntervalTransformation = "simpleToExtension" | "extensionToSimple" | "simpleToCompound" | "compoundToSimple";
|
|
12
|
+
export interface TransformIntervalsOptions {
|
|
13
|
+
intervalTransformation?: IntervalTransformation;
|
|
14
|
+
filterOutOctave?: boolean;
|
|
15
|
+
shouldSort?: boolean;
|
|
16
|
+
}
|
|
17
|
+
export declare function transformIntervals(intervals: readonly Interval[], options?: TransformIntervalsOptions): Interval[];
|
|
18
|
+
export declare function getQualitiesFromIntervals(intervals: readonly Interval[]): IntervalQuality[];
|
|
19
|
+
export declare function getIntervalsFromQualities(qualities: IntervalQuality[]): Interval[];
|
|
20
|
+
/**
|
|
21
|
+
* Extracts the interval qualities (e.g., "P1", "M2", "m3") from a NoteCollection.
|
|
22
|
+
* @param collection The NoteCollection object.
|
|
23
|
+
* @returns An array of IntervalQuality strings.
|
|
24
|
+
*/
|
|
25
|
+
export declare function getQualitiesFromNoteCollection(collection: NoteCollection): IntervalQuality[];
|
|
26
|
+
//# sourceMappingURL=intervals.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"intervals.d.ts","sourceRoot":"","sources":["../../../src/src/utils/intervals.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,QAAQ,EACb,KAAK,eAAe,EAMrB,MAAM,+BAA+B,CAAC;AACvC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAEhE,wBAAgB,wBAAwB,CACtC,SAAS,EAAE,SAAS,QAAQ,EAAE,GAC7B,QAAQ,EAAE,CAEZ;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,SAAS,EAAE,SAAS,QAAQ,EAAE,GAAG,QAAQ,EAAE,CAOxE;AAED,MAAM,MAAM,sBAAsB,GAC9B,mBAAmB,GACnB,mBAAmB,GACnB,kBAAkB,GAClB,kBAAkB,CAAC;AAEvB,MAAM,WAAW,yBAAyB;IACxC,sBAAsB,CAAC,EAAE,sBAAsB,CAAC;IAChD,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,SAAS,QAAQ,EAAE,EAC9B,OAAO,GAAE,yBAA8B,GACtC,QAAQ,EAAE,CA+BZ;AAED,wBAAgB,yBAAyB,CACvC,SAAS,EAAE,SAAS,QAAQ,EAAE,GAC7B,eAAe,EAAE,CAKnB;AAED,wBAAgB,yBAAyB,CACvC,SAAS,EAAE,eAAe,EAAE,GAC3B,QAAQ,EAAE,CAKZ;AAED;;;;GAIG;AACH,wBAAgB,8BAA8B,CAC5C,UAAU,EAAE,cAAc,GACzB,eAAe,EAAE,CAEnB"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { compoundToSimpleIntervalMap, extensionToSimpleIntervalMap, intervalQualityToIntervalMap, intervalToIntegerMap, intervalToIntervalQualityMap, simpleToCompoundIntervalMap, simpleToExtensionIntervalMap, } from "../data/labels/note-labels.js";
|
|
2
|
+
export function filterOutOctaveIntervals(intervals) {
|
|
3
|
+
return intervals.filter((i) => i !== "8" && i !== "♮8");
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Sorts an array of intervals in ascending order based on their integer value.
|
|
7
|
+
* This is a pure function and returns a new sorted array, leaving the original array unchanged.
|
|
8
|
+
* @param intervals The array of intervals to sort.
|
|
9
|
+
* @returns A new array with the sorted intervals.
|
|
10
|
+
*/
|
|
11
|
+
export function sortIntervals(intervals) {
|
|
12
|
+
return intervals.toSorted((a, b) => {
|
|
13
|
+
const intA = intervalToIntegerMap.get(a);
|
|
14
|
+
const intB = intervalToIntegerMap.get(b);
|
|
15
|
+
if (intA === undefined || intB === undefined)
|
|
16
|
+
return 0;
|
|
17
|
+
return intA - intB;
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
export function transformIntervals(intervals, options = {}) {
|
|
21
|
+
const { intervalTransformation, filterOutOctave = false, shouldSort = true, } = options;
|
|
22
|
+
const intervalMap = (() => {
|
|
23
|
+
switch (intervalTransformation) {
|
|
24
|
+
case "simpleToExtension":
|
|
25
|
+
return simpleToExtensionIntervalMap;
|
|
26
|
+
case "extensionToSimple":
|
|
27
|
+
return extensionToSimpleIntervalMap;
|
|
28
|
+
case "simpleToCompound":
|
|
29
|
+
return simpleToCompoundIntervalMap;
|
|
30
|
+
case "compoundToSimple":
|
|
31
|
+
return compoundToSimpleIntervalMap;
|
|
32
|
+
default:
|
|
33
|
+
return new Map();
|
|
34
|
+
}
|
|
35
|
+
})();
|
|
36
|
+
const fundamentalIntervals = filterOutOctave
|
|
37
|
+
? filterOutOctaveIntervals(intervals)
|
|
38
|
+
: intervals;
|
|
39
|
+
const finalIntervals = fundamentalIntervals.map((interval) => intervalMap.get(interval) ?? interval);
|
|
40
|
+
return shouldSort ? sortIntervals(finalIntervals) : finalIntervals;
|
|
41
|
+
}
|
|
42
|
+
export function getQualitiesFromIntervals(intervals) {
|
|
43
|
+
return intervals.flatMap((interval) => {
|
|
44
|
+
const quality = intervalToIntervalQualityMap.get(interval);
|
|
45
|
+
return quality ? [quality] : [];
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
export function getIntervalsFromQualities(qualities) {
|
|
49
|
+
return qualities.flatMap((quality) => {
|
|
50
|
+
const interval = intervalQualityToIntervalMap.get(quality);
|
|
51
|
+
return interval ? [interval] : [];
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Extracts the interval qualities (e.g., "P1", "M2", "m3") from a NoteCollection.
|
|
56
|
+
* @param collection The NoteCollection object.
|
|
57
|
+
* @returns An array of IntervalQuality strings.
|
|
58
|
+
*/
|
|
59
|
+
export function getQualitiesFromNoteCollection(collection) {
|
|
60
|
+
return getQualitiesFromIntervals(collection.intervals);
|
|
61
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { Interval } from "../data/labels/note-labels.js";
|
|
2
|
+
import type { MidiNoteNumber, MidiNoteSequence } from "../types/midi";
|
|
3
|
+
export type MidiNoteSequenceDirection = "ascending" | "descending" | "ascending-descending" | "descending-ascending";
|
|
4
|
+
export interface MidiNoteSequenceOptions {
|
|
5
|
+
rootNoteMidi: MidiNoteNumber;
|
|
6
|
+
intervals: Interval[];
|
|
7
|
+
direction: MidiNoteSequenceDirection;
|
|
8
|
+
startFromIndex?: number;
|
|
9
|
+
filterOutOctave?: boolean;
|
|
10
|
+
restsAtEnd?: number;
|
|
11
|
+
numNotes?: number;
|
|
12
|
+
numOctaves?: number;
|
|
13
|
+
extraNotes?: number;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Generates a sequence of MIDI notes based on the provided options.
|
|
17
|
+
* The sequence can be ascending, descending, or a combination of both.
|
|
18
|
+
* @param options The options for generating the MIDI note sequence.
|
|
19
|
+
* @returns A sequence of MIDI notes.
|
|
20
|
+
* @throws {Error} If a MIDI note cannot be calculated for an interval.
|
|
21
|
+
*/
|
|
22
|
+
export declare function getMidiNoteSequence(options: MidiNoteSequenceOptions): MidiNoteSequence;
|
|
23
|
+
//# sourceMappingURL=midi-note-sequences.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"midi-note-sequences.d.ts","sourceRoot":"","sources":["../../../src/src/utils/midi-note-sequences.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,+BAA+B,CAAC;AAC9D,OAAO,KAAK,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAGtE,MAAM,MAAM,yBAAyB,GACjC,WAAW,GACX,YAAY,GACZ,sBAAsB,GACtB,sBAAsB,CAAC;AAE3B,MAAM,WAAW,uBAAuB;IACtC,YAAY,EAAE,cAAc,CAAC;IAC7B,SAAS,EAAE,QAAQ,EAAE,CAAC;IACtB,SAAS,EAAE,yBAAyB,CAAC;IACrC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAyED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,uBAAuB,GAC/B,gBAAgB,CAoElB"}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { noteMidiAndIntervalToMidi } from "./midi.js";
|
|
2
|
+
/**
|
|
3
|
+
* Generates a monotonic sequence of MIDI notes.
|
|
4
|
+
* @param rootNoteMidi The root MIDI note number.
|
|
5
|
+
* @param intervals The array of intervals to generate the sequence from.
|
|
6
|
+
* @param numNotes The number of notes to generate.
|
|
7
|
+
* @param startFromIndex The starting index in the intervals array.
|
|
8
|
+
* @param direction The direction of the sequence ('ascending' or 'descending').
|
|
9
|
+
* @returns An array of MIDI note numbers.
|
|
10
|
+
* @throws {Error} If a MIDI note cannot be calculated for an interval.
|
|
11
|
+
*/
|
|
12
|
+
function getMonotonicMidiNoteSequence(rootNoteMidi, intervals, numNotes, startFromIndex, direction) {
|
|
13
|
+
const notes = [];
|
|
14
|
+
const intervalsLength = intervals.length;
|
|
15
|
+
if (intervalsLength === 0 || numNotes <= 0)
|
|
16
|
+
return [];
|
|
17
|
+
for (let i = 0; i < numNotes; i++) {
|
|
18
|
+
let intervalIndex;
|
|
19
|
+
let octaveOffset;
|
|
20
|
+
if (direction === "ascending") {
|
|
21
|
+
intervalIndex = (startFromIndex + i) % intervalsLength;
|
|
22
|
+
octaveOffset = Math.floor((startFromIndex + i) / intervalsLength) * 12;
|
|
23
|
+
const interval = intervals[intervalIndex];
|
|
24
|
+
const note = noteMidiAndIntervalToMidi(rootNoteMidi, interval);
|
|
25
|
+
if (note === undefined) {
|
|
26
|
+
throw new Error(`Could not calculate MIDI note for interval ${interval} at index ${intervalIndex}`);
|
|
27
|
+
}
|
|
28
|
+
notes.push((note + octaveOffset));
|
|
29
|
+
} // descending...
|
|
30
|
+
else {
|
|
31
|
+
intervalIndex =
|
|
32
|
+
((startFromIndex - i) % intervalsLength + intervalsLength) %
|
|
33
|
+
intervalsLength;
|
|
34
|
+
/**
|
|
35
|
+
* octaveOffset increases every time we've looped through all intervals
|
|
36
|
+
* The Math.max(0, ...) is a piece of defensive programming. It protects the function from producing illogical results if it
|
|
37
|
+
* ever receives invalid input.
|
|
38
|
+
* @example
|
|
39
|
+
* intervalsLength = 7
|
|
40
|
+
* Imagine we accidentally pass startFromIndex = 10. This is not a valid index for the intervals array,
|
|
41
|
+
* but a robust function should handle it gracefully.
|
|
42
|
+
*/
|
|
43
|
+
octaveOffset =
|
|
44
|
+
Math.max(0, Math.ceil((i - startFromIndex) / intervalsLength)) * 12;
|
|
45
|
+
const interval = intervals[intervalIndex];
|
|
46
|
+
// Subtract octaveOffset from root before applying interval
|
|
47
|
+
const note = noteMidiAndIntervalToMidi((rootNoteMidi - octaveOffset), interval);
|
|
48
|
+
if (note === undefined) {
|
|
49
|
+
throw new Error(`Could not calculate MIDI note for interval ${interval} at index ${intervalIndex}`);
|
|
50
|
+
}
|
|
51
|
+
notes.push(note);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return notes;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Generates a sequence of MIDI notes based on the provided options.
|
|
58
|
+
* The sequence can be ascending, descending, or a combination of both.
|
|
59
|
+
* @param options The options for generating the MIDI note sequence.
|
|
60
|
+
* @returns A sequence of MIDI notes.
|
|
61
|
+
* @throws {Error} If a MIDI note cannot be calculated for an interval.
|
|
62
|
+
*/
|
|
63
|
+
export function getMidiNoteSequence(options) {
|
|
64
|
+
const { rootNoteMidi, intervals, direction, startFromIndex = 0, filterOutOctave = true, numNotes, numOctaves = 1, restsAtEnd = 0, extraNotes = 0, } = options;
|
|
65
|
+
const fundamentalIntervals = filterOutOctave
|
|
66
|
+
? intervals.filter((i) => i !== "8" && i !== "♮8")
|
|
67
|
+
: intervals;
|
|
68
|
+
if (fundamentalIntervals.length === 0)
|
|
69
|
+
return [];
|
|
70
|
+
// Calculate the total number of notes for the (first) monotonic part of the sequence.
|
|
71
|
+
// This is simply reversed and sliced to create the ascending-descending or descending-ascending sequences.
|
|
72
|
+
// numOctaves = 0: fundamentalIntervals.length
|
|
73
|
+
// numOctaves = 1: fundamentalIntervals.length + 1
|
|
74
|
+
// numOctaves = 2: 2 * fundamentalIntervals.length + 1
|
|
75
|
+
const monotonicNoteCount = numNotes ??
|
|
76
|
+
(numOctaves === 0
|
|
77
|
+
? fundamentalIntervals.length
|
|
78
|
+
: numOctaves * fundamentalIntervals.length + 1) +
|
|
79
|
+
extraNotes;
|
|
80
|
+
if (monotonicNoteCount <= 0)
|
|
81
|
+
return [];
|
|
82
|
+
let sequence = [];
|
|
83
|
+
const monotonicNotes = (dir) => getMonotonicMidiNoteSequence(rootNoteMidi, fundamentalIntervals, monotonicNoteCount, startFromIndex, dir);
|
|
84
|
+
switch (direction) {
|
|
85
|
+
case "ascending":
|
|
86
|
+
sequence = monotonicNotes("ascending");
|
|
87
|
+
break;
|
|
88
|
+
case "descending":
|
|
89
|
+
sequence = monotonicNotes("descending");
|
|
90
|
+
break;
|
|
91
|
+
case "ascending-descending": {
|
|
92
|
+
const ascendingNotes = monotonicNotes("ascending");
|
|
93
|
+
// Exclude the peak note to avoid duplication when reversing
|
|
94
|
+
const descendingPart = [...ascendingNotes].reverse().slice(1);
|
|
95
|
+
sequence = [...ascendingNotes, ...descendingPart];
|
|
96
|
+
break;
|
|
97
|
+
}
|
|
98
|
+
case "descending-ascending": {
|
|
99
|
+
const descendingNotes = monotonicNotes("descending");
|
|
100
|
+
// Exclude the lowest note to avoid duplication when reversing
|
|
101
|
+
const ascendingPart = [...descendingNotes].reverse().slice(1);
|
|
102
|
+
sequence = [...descendingNotes, ...ascendingPart];
|
|
103
|
+
break;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
if (restsAtEnd > 0)
|
|
107
|
+
sequence.push(...Array(restsAtEnd).fill(null));
|
|
108
|
+
return sequence;
|
|
109
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type Interval, type NoteName } from "../data/labels/note-labels.js";
|
|
2
|
+
import type { MidiNoteNumber } from "../types/midi";
|
|
3
|
+
export declare function noteIntegerAndIntervalToMidi(noteInteger: number, noteOctaveNumber: number, interval: Interval): MidiNoteNumber | undefined;
|
|
4
|
+
export declare function noteNameAndIntervalToMidi(noteName: NoteName, noteOctaveNumber: number, interval: Interval): MidiNoteNumber | undefined;
|
|
5
|
+
export declare function noteMidiAndIntervalToMidi(noteMidi: MidiNoteNumber, interval: Interval): MidiNoteNumber | undefined;
|
|
6
|
+
export declare function noteNameToMidi(noteName: NoteName, noteOctaveNumber: number): MidiNoteNumber | undefined;
|
|
7
|
+
//# sourceMappingURL=midi.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"midi.d.ts","sourceRoot":"","sources":["../../../src/src/utils/midi.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,QAAQ,EAEb,KAAK,QAAQ,EAEd,MAAM,+BAA+B,CAAC;AAEvC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAEpD,wBAAgB,4BAA4B,CAC1C,WAAW,EAAE,MAAM,EACnB,gBAAgB,EAAE,MAAM,EACxB,QAAQ,EAAE,QAAQ,GACjB,cAAc,GAAG,SAAS,CAK5B;AAED,wBAAgB,yBAAyB,CACvC,QAAQ,EAAE,QAAQ,EAClB,gBAAgB,EAAE,MAAM,EACxB,QAAQ,EAAE,QAAQ,GACjB,cAAc,GAAG,SAAS,CAQ5B;AAED,wBAAgB,yBAAyB,CACvC,QAAQ,EAAE,cAAc,EACxB,QAAQ,EAAE,QAAQ,GACjB,cAAc,GAAG,SAAS,CAI5B;AAED,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,QAAQ,EAClB,gBAAgB,EAAE,MAAM,GACvB,cAAc,GAAG,SAAS,CAI5B"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { intervalToIntegerMap, noteNameToIntegerMap, } from "../data/labels/note-labels.js";
|
|
2
|
+
export function noteIntegerAndIntervalToMidi(noteInteger, noteOctaveNumber, interval) {
|
|
3
|
+
const intervalInteger = intervalToIntegerMap.get(interval);
|
|
4
|
+
if (intervalInteger === undefined)
|
|
5
|
+
return undefined;
|
|
6
|
+
return (noteOctaveNumber + 1) * 12 + noteInteger +
|
|
7
|
+
intervalInteger;
|
|
8
|
+
}
|
|
9
|
+
export function noteNameAndIntervalToMidi(noteName, noteOctaveNumber, interval) {
|
|
10
|
+
const noteInteger = noteNameToIntegerMap.get(noteName);
|
|
11
|
+
if (noteInteger === undefined)
|
|
12
|
+
return undefined;
|
|
13
|
+
return noteIntegerAndIntervalToMidi(noteInteger, noteOctaveNumber, interval);
|
|
14
|
+
}
|
|
15
|
+
export function noteMidiAndIntervalToMidi(noteMidi, interval) {
|
|
16
|
+
const intervalValue = intervalToIntegerMap.get(interval);
|
|
17
|
+
if (intervalValue === undefined)
|
|
18
|
+
return undefined;
|
|
19
|
+
return noteMidi + intervalValue;
|
|
20
|
+
}
|
|
21
|
+
export function noteNameToMidi(noteName, noteOctaveNumber) {
|
|
22
|
+
const noteValue = noteNameToIntegerMap.get(noteName);
|
|
23
|
+
if (noteValue === undefined)
|
|
24
|
+
return undefined;
|
|
25
|
+
return noteValue + (noteOctaveNumber + 1) * 12;
|
|
26
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export * from "./colors.js";
|
|
2
|
+
export * from "./chords.js";
|
|
3
|
+
export * from "./intervals.js";
|
|
4
|
+
export * from "./midi-note-sequences.js";
|
|
5
|
+
export * from "./midi.js";
|
|
6
|
+
export * from "./note-collections.js";
|
|
7
|
+
export * from "./note-names.js";
|
|
8
|
+
export * from "./rotate-array.js";
|
|
9
|
+
//# sourceMappingURL=mod.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mod.d.ts","sourceRoot":"","sources":["../../../src/src/utils/mod.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,0BAA0B,CAAC;AACzC,cAAc,WAAW,CAAC;AAC1B,cAAc,uBAAuB,CAAC;AACtC,cAAc,iBAAiB,CAAC;AAChC,cAAc,mBAAmB,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export * from "./colors.js";
|
|
2
|
+
export * from "./chords.js";
|
|
3
|
+
export * from "./intervals.js";
|
|
4
|
+
export * from "./midi-note-sequences.js";
|
|
5
|
+
export * from "./midi.js";
|
|
6
|
+
export * from "./note-collections.js";
|
|
7
|
+
export * from "./note-names.js";
|
|
8
|
+
export * from "./rotate-array.js";
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { type NoteCollectionKey } from "../data/note-collections/mod.js";
|
|
2
|
+
import type { Interval } from "../data/labels/note-labels.js";
|
|
3
|
+
import type { NoteCollection } from "../types/note-collections";
|
|
4
|
+
/**
|
|
5
|
+
* Checks if a given string is a valid `NoteCollectionKey`.
|
|
6
|
+
* @param key The string to check.
|
|
7
|
+
* @returns `true` if the key is a valid `NoteCollectionKey`, `false` otherwise.
|
|
8
|
+
*/
|
|
9
|
+
export declare function isValidNoteCollectionKey(key: string): key is NoteCollectionKey;
|
|
10
|
+
export interface SearchOptions {
|
|
11
|
+
query?: string;
|
|
12
|
+
intervals?: Interval[];
|
|
13
|
+
type?: string;
|
|
14
|
+
}
|
|
15
|
+
export declare function searchNoteCollections(options: SearchOptions): NoteCollection[];
|
|
16
|
+
/**
|
|
17
|
+
* Finds the single best matching `NoteCollection` based on search options.
|
|
18
|
+
* This is a convenience wrapper around `searchNoteCollections` that returns only the first result.
|
|
19
|
+
* The search is prioritized to return the most relevant match first.
|
|
20
|
+
* @param options The search options.
|
|
21
|
+
* @returns The best matching `NoteCollection` or `undefined` if no match is found.
|
|
22
|
+
*/
|
|
23
|
+
export declare function findNoteCollection(options: SearchOptions): NoteCollection | undefined;
|
|
24
|
+
//# sourceMappingURL=note-collections.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"note-collections.d.ts","sourceRoot":"","sources":["../../../src/src/utils/note-collections.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,iBAAiB,EAEvB,MAAM,iCAAiC,CAAC;AACzC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,+BAA+B,CAAC;AAC9D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAEhE;;;;GAIG;AACH,wBAAgB,wBAAwB,CACtC,GAAG,EAAE,MAAM,GACV,GAAG,IAAI,iBAAiB,CAE1B;AAoDD,MAAM,WAAW,aAAa;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,QAAQ,EAAE,CAAC;IACvB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,aAAa,GACrB,cAAc,EAAE,CAkGlB;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,aAAa,GACrB,cAAc,GAAG,SAAS,CAE5B"}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { noteCollections, } from "../data/note-collections/mod.js";
|
|
2
|
+
/**
|
|
3
|
+
* Checks if a given string is a valid `NoteCollectionKey`.
|
|
4
|
+
* @param key The string to check.
|
|
5
|
+
* @returns `true` if the key is a valid `NoteCollectionKey`, `false` otherwise.
|
|
6
|
+
*/
|
|
7
|
+
export function isValidNoteCollectionKey(key) {
|
|
8
|
+
return Object.prototype.hasOwnProperty.call(noteCollections, key);
|
|
9
|
+
}
|
|
10
|
+
const normalizationMap = new Map();
|
|
11
|
+
const aliasSets = {
|
|
12
|
+
"♭": ["b", "flat"],
|
|
13
|
+
"♯": ["#", "sharp"],
|
|
14
|
+
"♮": ["n", "natural"],
|
|
15
|
+
"𝄫": ["bb", "doubleflat"],
|
|
16
|
+
"𝄪": ["##", "doublesharp"],
|
|
17
|
+
"M": ["maj", "major"],
|
|
18
|
+
"m": ["min", "minor"],
|
|
19
|
+
"°": ["dim", "diminished"],
|
|
20
|
+
"+": ["aug", "augmented"],
|
|
21
|
+
"ø": ["halfdiminished"],
|
|
22
|
+
"7": ["seventh"],
|
|
23
|
+
"dominant": ["dom"],
|
|
24
|
+
};
|
|
25
|
+
for (const [canonical, aliases] of Object.entries(aliasSets)) {
|
|
26
|
+
for (const alias of aliases) {
|
|
27
|
+
normalizationMap.set(alias, canonical);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Normalizes a search string by converting aliases to their canonical form,
|
|
32
|
+
* removing non-essential characters, and collapsing whitespace.
|
|
33
|
+
* @param str The string to normalize.
|
|
34
|
+
* @returns A normalized string for searching.
|
|
35
|
+
*/
|
|
36
|
+
function normalizeSearchTerm(str) {
|
|
37
|
+
// Start with trimming, but preserve original case for now.
|
|
38
|
+
let normalized = str.trim();
|
|
39
|
+
// Iteratively replace all aliases, using a case-insensitive regex.
|
|
40
|
+
// This is crucial because we can't simply lowercase the entire string,
|
|
41
|
+
// as that would merge "M" (major) and "m" (minor) into the same character.
|
|
42
|
+
for (const [alias, canonical] of normalizationMap.entries()) {
|
|
43
|
+
// This regex looks for an alias that is either a whole word (\b)
|
|
44
|
+
// or is immediately followed by a digit (for cases like "b2", "maj7").
|
|
45
|
+
// The "i" flag makes the search case-insensitive.
|
|
46
|
+
const regex = new RegExp(`\\b${alias}(?=[0-9]|\\b)`, "gi");
|
|
47
|
+
normalized = normalized.replace(regex, canonical);
|
|
48
|
+
}
|
|
49
|
+
// Final cleanup: remove non-essential characters and collapse whitespace.
|
|
50
|
+
return normalized.replace(/[-()]/g, "").replace(/\s+/g, " ").trim();
|
|
51
|
+
}
|
|
52
|
+
export function searchNoteCollections(options) {
|
|
53
|
+
const { query, intervals, type } = options;
|
|
54
|
+
let candidates = Object.values(noteCollections);
|
|
55
|
+
// 1. Apply hard filters first to narrow down the candidate pool
|
|
56
|
+
if (type) {
|
|
57
|
+
const normalizedType = normalizeSearchTerm(type);
|
|
58
|
+
const searchWords = normalizedType.split(" ");
|
|
59
|
+
candidates = candidates.filter((theme) => {
|
|
60
|
+
const themeTypesString = theme.type.map(normalizeSearchTerm).join(" ");
|
|
61
|
+
return searchWords.every((word) => {
|
|
62
|
+
// Use case-sensitive search for 'M' and 'm'
|
|
63
|
+
const isCaseSensitiveWord = word === "M" || word === "m";
|
|
64
|
+
const regex = new RegExp(`\\b${word}\\b`, isCaseSensitiveWord ? "" : "i");
|
|
65
|
+
return regex.test(themeTypesString);
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
if (intervals && intervals.length > 0) {
|
|
70
|
+
candidates = candidates.filter((theme) => intervals.every((interval) => theme.intervals.includes(interval)));
|
|
71
|
+
}
|
|
72
|
+
// If there's no text query, the filtered list is the final result.
|
|
73
|
+
if (!query) {
|
|
74
|
+
return candidates;
|
|
75
|
+
}
|
|
76
|
+
// 2. Apply prioritized text search on the filtered candidates
|
|
77
|
+
const normalizedQuery = normalizeSearchTerm(query);
|
|
78
|
+
if (!normalizedQuery) {
|
|
79
|
+
return candidates;
|
|
80
|
+
}
|
|
81
|
+
const searchWords = normalizedQuery.split(" ");
|
|
82
|
+
// Filter candidates to those that contain all search words
|
|
83
|
+
const textFilteredCandidates = candidates.filter((theme) => {
|
|
84
|
+
const searchableText = [
|
|
85
|
+
theme.primaryName,
|
|
86
|
+
...theme.names,
|
|
87
|
+
...theme.type,
|
|
88
|
+
...theme.characteristics,
|
|
89
|
+
]
|
|
90
|
+
.map(normalizeSearchTerm)
|
|
91
|
+
.join(" ");
|
|
92
|
+
return searchWords.every((word) => {
|
|
93
|
+
const isCaseSensitiveWord = word === "M" || word === "m";
|
|
94
|
+
const regex = new RegExp(`\\b${word}\\b`, isCaseSensitiveWord ? "" : "i");
|
|
95
|
+
return regex.test(searchableText);
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
// 3. Prioritize the filtered results
|
|
99
|
+
const prioritizedResults = new Set();
|
|
100
|
+
const passes = [
|
|
101
|
+
// Pass 1: Exact match on primaryName
|
|
102
|
+
(theme) => normalizeSearchTerm(theme.primaryName) === normalizedQuery,
|
|
103
|
+
// Pass 2: Exact match on any name
|
|
104
|
+
(theme) => theme.names.some((name) => normalizeSearchTerm(name) === normalizedQuery),
|
|
105
|
+
// Pass 3: Primary name starts with the query
|
|
106
|
+
(theme) => normalizeSearchTerm(theme.primaryName).startsWith(normalizedQuery),
|
|
107
|
+
// Pass 4: Any name starts with the query
|
|
108
|
+
(theme) => theme.names.some((name) => normalizeSearchTerm(name).startsWith(normalizedQuery)),
|
|
109
|
+
];
|
|
110
|
+
for (const pass of passes) {
|
|
111
|
+
for (const theme of textFilteredCandidates) {
|
|
112
|
+
if (pass(theme)) {
|
|
113
|
+
prioritizedResults.add(theme);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
// Add the remaining text-filtered candidates that didn't match a priority pass
|
|
118
|
+
for (const theme of textFilteredCandidates) {
|
|
119
|
+
prioritizedResults.add(theme);
|
|
120
|
+
}
|
|
121
|
+
return Array.from(prioritizedResults);
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Finds the single best matching `NoteCollection` based on search options.
|
|
125
|
+
* This is a convenience wrapper around `searchNoteCollections` that returns only the first result.
|
|
126
|
+
* The search is prioritized to return the most relevant match first.
|
|
127
|
+
* @param options The search options.
|
|
128
|
+
* @returns The best matching `NoteCollection` or `undefined` if no match is found.
|
|
129
|
+
*/
|
|
130
|
+
export function findNoteCollection(options) {
|
|
131
|
+
return searchNoteCollections(options)[0];
|
|
132
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type Interval, type NoteName, type RootNote, type RootNoteInteger } from "../data/labels/note-labels.js";
|
|
2
|
+
import { type NoteCollectionKey } from "../data/note-collections/mod.js";
|
|
3
|
+
import { type TransformIntervalsOptions } from "./intervals.js";
|
|
4
|
+
/**
|
|
5
|
+
* Parses a string and returns a canonical `NoteName` if the string is a valid note name.
|
|
6
|
+
* This is useful for handling user input that may use ASCII characters like 'b' and '#',
|
|
7
|
+
* as well as double accidentals like 'x', '##', and 'bb'.
|
|
8
|
+
* @param name The string to parse.
|
|
9
|
+
* @returns A canonical `NoteName` (e.g., "B♭", "C𝄪") or `undefined` if the input is not a valid note.
|
|
10
|
+
*/
|
|
11
|
+
export declare function normalizeNoteNameString(name: string): NoteName | undefined;
|
|
12
|
+
/**
|
|
13
|
+
* Parses a string and returns a canonical `RootNote` if the string is a valid root note.
|
|
14
|
+
* This is useful for handling user input that may use ASCII characters for accidentals.
|
|
15
|
+
* It is stricter than `normalizeNoteNameString` as it only allows notes present in the `rootNotes` array.
|
|
16
|
+
* @param name The string to parse.
|
|
17
|
+
* @returns A canonical `RootNote` or `undefined` if the input is not a valid root note.
|
|
18
|
+
*/
|
|
19
|
+
export declare function normalizeRootNoteString(name: string): RootNote | undefined;
|
|
20
|
+
export declare function getNoteIntegerFromString(noteName: string): RootNoteInteger | undefined;
|
|
21
|
+
export declare function getNoteNamesFromRootAndIntervals(rootNote: RootNote, intervals: readonly Interval[], options?: TransformIntervalsOptions): NoteName[];
|
|
22
|
+
export declare function getNoteNamesFromRootAndCollectionKey(rootNote: RootNote, noteCollectionKey: NoteCollectionKey, options?: TransformIntervalsOptions): NoteName[];
|
|
23
|
+
//# sourceMappingURL=note-names.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"note-names.d.ts","sourceRoot":"","sources":["../../../src/src/utils/note-names.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,QAAQ,EAIb,KAAK,QAAQ,EAGb,KAAK,QAAQ,EACb,KAAK,eAAe,EAErB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EACL,KAAK,iBAAiB,EAEvB,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAEL,KAAK,yBAAyB,EAC/B,MAAM,gBAAgB,CAAC;AAOxB;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAsE1E;AAED;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAQ1E;AAED,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,MAAM,GACf,eAAe,GAAG,SAAS,CAI7B;AAED,wBAAgB,gCAAgC,CAC9C,QAAQ,EAAE,QAAQ,EAClB,SAAS,EAAE,SAAS,QAAQ,EAAE,EAC9B,OAAO,GAAE,yBAA8B,GACtC,QAAQ,EAAE,CAmCZ;AAED,wBAAgB,oCAAoC,CAClD,QAAQ,EAAE,QAAQ,EAClB,iBAAiB,EAAE,iBAAiB,EACpC,OAAO,GAAE,yBAA8B,GACtC,QAAQ,EAAE,CAQZ"}
|