@musodojo/music-theory-data 18.0.0 → 19.0.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@musodojo/music-theory-data",
3
- "version": "18.0.0",
3
+ "version": "19.0.1",
4
4
  "description": "The musician-friendly TypeScript library for modes, scales, chords, and more.",
5
5
  "keywords": [
6
6
  "music",
@@ -1,12 +1,9 @@
1
- import type {
2
- ModalScaleCollection,
3
- ParentScaleCollection,
4
- ScaleCollection,
5
- } from "../../types/note-collections.d.ts";
1
+ import type { ModalScaleCollection } from "../../types/note-collections.d.ts";
6
2
 
7
- const ionian: ParentScaleCollection = {
3
+ const ionian: ModalScaleCollection = {
8
4
  category: "scale",
9
5
  rotation: 0,
6
+ parentScale: "ionian",
10
7
  primaryName: "Major",
11
8
  names: [
12
9
  "Major",
@@ -280,5 +277,5 @@ const _diatonicModes = {
280
277
 
281
278
  export type DiatonicModeKey = keyof typeof _diatonicModes;
282
279
 
283
- export const diatonicModes: Record<DiatonicModeKey, ScaleCollection> =
280
+ export const diatonicModes: Record<DiatonicModeKey, ModalScaleCollection> =
284
281
  _diatonicModes;
@@ -2,7 +2,6 @@ import type {
2
2
  ChordCollection,
3
3
  ModalScaleCollection,
4
4
  NoteCollection,
5
- ParentScaleCollection,
6
5
  } from "../../types/note-collections.d.ts";
7
6
 
8
7
  const diminishedTriad: ChordCollection = {
@@ -41,9 +40,10 @@ const halfDiminished7: ChordCollection = {
41
40
  patternShort: ["m3", "m3", "M3"],
42
41
  } as const;
43
42
 
44
- const wholeHalfDiminished: ParentScaleCollection = {
43
+ const wholeHalfDiminished: ModalScaleCollection = {
45
44
  category: "scale",
46
45
  rotation: 0,
46
+ parentScale: "wholeHalfDiminished",
47
47
  primaryName: "Whole Half Diminished",
48
48
  names: ["Whole Half Diminished"],
49
49
  intervals: ["1", "2", "♭3", "4", "♭5", "♭6", "6", "7", "8"],
@@ -1,7 +1,7 @@
1
1
  import type {
2
2
  ChordCollection,
3
+ NonModalScaleCollection,
3
4
  NoteCollection,
4
- ScaleCollection,
5
5
  } from "../../types/note-collections.d.ts";
6
6
 
7
7
  const dominant7: ChordCollection = {
@@ -105,7 +105,7 @@ const dominant13: ChordCollection = {
105
105
  patternShort: ["M3", "m3", "m3", "M3", "m3", "m3"],
106
106
  } as const;
107
107
 
108
- const dominantPentatonic: ScaleCollection = {
108
+ const dominantPentatonic: NonModalScaleCollection = {
109
109
  category: "scale",
110
110
  primaryName: "Dominant Pentatonic",
111
111
  names: ["Dominant Pentatonic"],
@@ -1,12 +1,9 @@
1
- import type {
2
- ModalScaleCollection,
3
- ParentScaleCollection,
4
- ScaleCollection,
5
- } from "../../types/note-collections.d.ts";
1
+ import type { ModalScaleCollection } from "../../types/note-collections.d.ts";
6
2
 
7
- const harmonicMinor: ParentScaleCollection = {
3
+ const harmonicMinor: ModalScaleCollection = {
8
4
  category: "scale",
9
5
  rotation: 0,
6
+ parentScale: "harmonicMinor",
10
7
  primaryName: "Harmonic Minor",
11
8
  names: [
12
9
  "Harmonic Minor",
@@ -258,5 +255,7 @@ export const _harmonicMinorModes = {
258
255
 
259
256
  export type HarmonicMinorModeKey = keyof typeof _harmonicMinorModes;
260
257
 
261
- export const harmonicMinorModes: Record<HarmonicMinorModeKey, ScaleCollection> =
262
- _harmonicMinorModes;
258
+ export const harmonicMinorModes: Record<
259
+ HarmonicMinorModeKey,
260
+ ModalScaleCollection
261
+ > = _harmonicMinorModes;
@@ -1,12 +1,9 @@
1
- import type {
2
- ModalScaleCollection,
3
- ParentScaleCollection,
4
- ScaleCollection,
5
- } from "../../types/note-collections.d.ts";
1
+ import type { ModalScaleCollection } from "../../types/note-collections.d.ts";
6
2
 
7
- const melodicMinor: ParentScaleCollection = {
3
+ const melodicMinor: ModalScaleCollection = {
8
4
  category: "scale",
9
5
  rotation: 0,
6
+ parentScale: "melodicMinor",
10
7
  primaryName: "Melodic Minor",
11
8
  names: [
12
9
  "Melodic Minor",
@@ -218,5 +215,7 @@ export const _melodicMinorModes = {
218
215
 
219
216
  export type MelodicMinorModeKey = keyof typeof _melodicMinorModes;
220
217
 
221
- export const melodicMinorModes: Record<MelodicMinorModeKey, ScaleCollection> =
222
- _melodicMinorModes;
218
+ export const melodicMinorModes: Record<
219
+ MelodicMinorModeKey,
220
+ ModalScaleCollection
221
+ > = _melodicMinorModes;
@@ -1,12 +1,9 @@
1
- import type {
2
- ModalScaleCollection,
3
- ParentScaleCollection,
4
- ScaleCollection,
5
- } from "../../types/note-collections.d.ts";
1
+ import type { ModalScaleCollection } from "../../types/note-collections.d.ts";
6
2
 
7
- const majorPentatonic: ParentScaleCollection = {
3
+ const majorPentatonic: ModalScaleCollection = {
8
4
  category: "scale",
9
5
  rotation: 0,
6
+ parentScale: "majorPentatonic",
10
7
  primaryName: "Major Pentatonic",
11
8
  names: ["Major Pentatonic"],
12
9
  intervals: ["1", "2", "3", "5", "6", "8"],
@@ -96,5 +93,7 @@ export const _pentatonicVariants = {
96
93
 
97
94
  export type PentatonicVariantKey = keyof typeof _pentatonicVariants;
98
95
 
99
- export const pentatonicVariants: Record<PentatonicVariantKey, ScaleCollection> =
100
- _pentatonicVariants;
96
+ export const pentatonicVariants: Record<
97
+ PentatonicVariantKey,
98
+ ModalScaleCollection
99
+ > = _pentatonicVariants;
@@ -1,5 +1,3 @@
1
- export type OctaveNumber = -1 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;
2
-
3
1
  export type MidiNoteNumber =
4
2
  | 0
5
3
  | 1
@@ -49,28 +49,20 @@ interface NoteCollectionBase {
49
49
  readonly patternShort: readonly string[];
50
50
  }
51
51
 
52
- /** A scale that is the parent of a set of modes (e.g., Ionian, Melodic Minor). */
53
- interface ParentScaleCollection extends NoteCollectionBase {
52
+ /** A scale that can be thought of as a mode of a parent scale (e.g., Ionian, Dorian, Lydian Augmented). */
53
+ interface ModalScaleCollection extends NoteCollectionBase {
54
54
  /**
55
55
  * The fundamental classification of the collection. For scales, this is always "scale".
56
56
  */
57
57
  readonly category: "scale";
58
58
  /**
59
59
  * The rotation index for a parent scale is always 0.
60
- */
61
- readonly rotation: 0;
62
- readonly parentScale?: never;
63
- }
64
-
65
- /** A scale that is a mode of a parent scale (e.g., Dorian, Lydian Augmented). */
66
- interface ModalScaleCollection extends NoteCollectionBase {
67
- readonly category: "scale";
68
- /**
69
60
  * The rotation index relative to a parent scale. e.g., Dorian is 1.
70
61
  */
71
62
  readonly rotation: number;
72
63
  /**
73
64
  * The key-name (e.g., "ionian") of the parent scale from which this mode is derived.
65
+ * The parent scale has itself as parentScale!
74
66
  */
75
67
  readonly parentScale: string;
76
68
  }
@@ -88,7 +80,6 @@ export interface ChordCollection extends NoteCollectionBase {
88
80
  }
89
81
 
90
82
  export type ScaleCollection =
91
- | ParentScaleCollection
92
83
  | ModalScaleCollection
93
84
  | NonModalScaleCollection;
94
85
 
@@ -100,11 +100,6 @@ export function getChordDetailsForDiatonicMode(
100
100
  diatonicModeKey: DiatonicModeKey,
101
101
  ): ChordDetails[] {
102
102
  const mode = diatonicModes[diatonicModeKey];
103
- if (mode.rotation === undefined) {
104
- throw new Error(
105
- `Mode "${diatonicModeKey}" is missing the 'rotation' property.`,
106
- );
107
- }
108
103
  const rotation = mode.rotation;
109
104
  const rotatedTriads = rotateArray(diatonicTriads, rotation);
110
105
  const rotatedSeventhChords = rotateArray(diatonicSeventhChords, rotation);
@@ -121,11 +116,6 @@ export function getChordDetailsForHarmonicMinorMode(
121
116
  harmonicMinorModeKey: HarmonicMinorModeKey,
122
117
  ): ChordDetails[] {
123
118
  const mode = harmonicMinorModes[harmonicMinorModeKey];
124
- if (mode.rotation === undefined) {
125
- throw new Error(
126
- `Mode "${harmonicMinorModeKey}" is missing the 'rotation' property.`,
127
- );
128
- }
129
119
  const rotation = mode.rotation;
130
120
  const rotatedTriads = rotateArray(harmonicMinorTriads, rotation);
131
121
  const rotatedSeventhChords = rotateArray(
@@ -145,11 +135,6 @@ export function getChordDetailsForMelodicMinorMode(
145
135
  melodicMinorModeKey: MelodicMinorModeKey,
146
136
  ): ChordDetails[] {
147
137
  const mode = melodicMinorModes[melodicMinorModeKey];
148
- if (mode.rotation === undefined) {
149
- throw new Error(
150
- `Mode "${melodicMinorModeKey}" is missing the 'rotation' property.`,
151
- );
152
- }
153
138
  const rotation = mode.rotation;
154
139
  const rotatedTriads = rotateArray(melodicMinorTriads, rotation);
155
140
  const rotatedSeventhChords = rotateArray(melodicMinorSeventhChords, rotation);
@@ -1,6 +1,6 @@
1
1
  import type { Interval } from "../data/labels/note-labels.ts";
2
2
  import type { MidiNoteNumber, MidiNoteSequence } from "../types/midi.d.ts";
3
- import { rootMidiAndIntervalToMidi } from "./midi.ts";
3
+ import { noteMidiAndIntervalToMidi } from "./midi.ts";
4
4
 
5
5
  export type MidiNoteSequenceDirection =
6
6
  | "ascending"
@@ -50,7 +50,7 @@ function getMonotonicMidiNoteSequence(
50
50
  intervalIndex = (startFromIndex + i) % intervalsLength;
51
51
  octaveOffset = Math.floor((startFromIndex + i) / intervalsLength) * 12;
52
52
  const interval = intervals[intervalIndex];
53
- const note = rootMidiAndIntervalToMidi(rootNoteMidi, interval);
53
+ const note = noteMidiAndIntervalToMidi(rootNoteMidi, interval);
54
54
  if (note === undefined) {
55
55
  throw new Error(
56
56
  `Could not calculate MIDI note for interval ${interval} at index ${intervalIndex}`,
@@ -75,7 +75,7 @@ function getMonotonicMidiNoteSequence(
75
75
  Math.max(0, Math.ceil((i - startFromIndex) / intervalsLength)) * 12;
76
76
  const interval = intervals[intervalIndex];
77
77
  // Subtract octaveOffset from root before applying interval
78
- const note = rootMidiAndIntervalToMidi(
78
+ const note = noteMidiAndIntervalToMidi(
79
79
  (rootNoteMidi - octaveOffset) as MidiNoteNumber,
80
80
  interval,
81
81
  );
package/src/utils/midi.ts CHANGED
@@ -3,48 +3,49 @@ import {
3
3
  intervalToIntegerMap,
4
4
  type NoteName,
5
5
  noteNameToIntegerMap,
6
- type RootNoteInteger,
7
6
  } from "../data/labels/note-labels.ts";
8
7
 
9
- import type { MidiNoteNumber, OctaveNumber } from "../types/midi.d.ts";
8
+ import type { MidiNoteNumber } from "../types/midi.d.ts";
10
9
 
11
- export function rootIntegerAndIntervalToMidi(
12
- rootNoteInteger: RootNoteInteger,
10
+ export function noteIntegerAndIntervalToMidi(
11
+ noteInteger: number,
12
+ noteOctaveNumber: number,
13
13
  interval: Interval,
14
- rootNoteOctaveNumber: OctaveNumber = 4,
15
14
  ): MidiNoteNumber | undefined {
16
- const intervalValue = intervalToIntegerMap.get(interval);
17
- if (intervalValue === undefined) return undefined;
18
- return (rootNoteOctaveNumber + 1) * 12 + rootNoteInteger +
19
- intervalValue as MidiNoteNumber;
15
+ const intervalInteger = intervalToIntegerMap.get(interval);
16
+ if (intervalInteger === undefined) return undefined;
17
+ return (noteOctaveNumber + 1) * 12 + noteInteger +
18
+ intervalInteger as MidiNoteNumber;
20
19
  }
21
20
 
22
- export function rootMidiAndIntervalToMidi(
23
- rootNoteMidi: MidiNoteNumber,
21
+ export function noteNameAndIntervalToMidi(
22
+ noteName: NoteName,
23
+ noteOctaveNumber: number,
24
24
  interval: Interval,
25
25
  ): MidiNoteNumber | undefined {
26
- const intervalValue = intervalToIntegerMap.get(interval);
27
- if (intervalValue === undefined) return undefined;
28
- return rootNoteMidi + intervalValue as MidiNoteNumber;
26
+ const noteInteger = noteNameToIntegerMap.get(noteName);
27
+ if (noteInteger === undefined) return undefined;
28
+ return noteIntegerAndIntervalToMidi(
29
+ noteInteger,
30
+ noteOctaveNumber,
31
+ interval,
32
+ );
29
33
  }
30
34
 
31
- export function noteNameToMidi(
32
- noteName: NoteName,
33
- octaveNumber: OctaveNumber = 4,
35
+ export function noteMidiAndIntervalToMidi(
36
+ noteMidi: MidiNoteNumber,
37
+ interval: Interval,
34
38
  ): MidiNoteNumber | undefined {
35
- const noteValue = noteNameToIntegerMap.get(noteName);
36
- if (noteValue === undefined) return undefined;
37
- return noteValue + (octaveNumber + 1) * 12 as MidiNoteNumber;
39
+ const intervalValue = intervalToIntegerMap.get(interval);
40
+ if (intervalValue === undefined) return undefined;
41
+ return noteMidi + intervalValue as MidiNoteNumber;
38
42
  }
39
43
 
40
- export function noteNameAndIntervalToMidi(
44
+ export function noteNameToMidi(
41
45
  noteName: NoteName,
42
- interval: Interval,
43
- octaveNumber: OctaveNumber = 4,
46
+ noteOctaveNumber: number,
44
47
  ): MidiNoteNumber | undefined {
45
48
  const noteValue = noteNameToIntegerMap.get(noteName);
46
49
  if (noteValue === undefined) return undefined;
47
- const intervalValue = intervalToIntegerMap.get(interval);
48
- if (intervalValue === undefined) return undefined;
49
- return noteValue + (octaveNumber + 1) * 12 as MidiNoteNumber;
50
+ return noteValue + (noteOctaveNumber + 1) * 12 as MidiNoteNumber;
50
51
  }
@@ -122,18 +122,12 @@ export function normalizeRootNoteString(name: string): RootNote | undefined {
122
122
  return undefined;
123
123
  }
124
124
 
125
- export function noteNameToInteger(
126
- noteName: NoteName,
127
- ): RootNoteInteger | undefined {
128
- return noteNameToIntegerMap.get(noteName);
129
- }
130
-
131
- export function noteNameStringToInteger(
125
+ export function getNoteIntegerFromString(
132
126
  noteName: string,
133
127
  ): RootNoteInteger | undefined {
134
128
  const normalized = normalizeNoteNameString(noteName);
135
129
  if (!normalized) return undefined;
136
- return noteNameToInteger(normalized);
130
+ return noteNameToIntegerMap.get(normalized);
137
131
  }
138
132
 
139
133
  export function getNoteNamesFromRootAndIntervals(
@@ -141,7 +135,7 @@ export function getNoteNamesFromRootAndIntervals(
141
135
  intervals: readonly Interval[],
142
136
  options: TransformIntervalsOptions = {},
143
137
  ): NoteName[] {
144
- const rootNoteInteger = noteNameToInteger(rootNote);
138
+ const rootNoteInteger = noteNameToIntegerMap.get(rootNote);
145
139
  if (rootNoteInteger === undefined) return [];
146
140
  const rootNoteLetter = rootNote.charAt(0).toUpperCase();
147
141
  const rootNoteLetterIndex = noteLetters.indexOf(rootNoteLetter as NoteLetter);