@musodojo/music-theory-data 21.1.1 → 23.0.0

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.
@@ -21,18 +21,42 @@ export interface NoteLabelCollection {
21
21
  readonly labels: NoteLabelGroup;
22
22
  }
23
23
  declare const _noteLabelCollections: {
24
- readonly flat: {
25
- readonly name: "Flat Notes";
26
- readonly shortName: "Flat";
24
+ readonly noteNamesFlat: {
25
+ readonly name: "Flat Note Names";
26
+ readonly shortName: "Flat Notes";
27
27
  readonly isRelative: false;
28
28
  readonly labels: readonly ["C", "D♭", "D", "E♭", "E", "F", "G♭", "G", "A♭", "A", "B♭", "B"];
29
29
  };
30
- readonly sharp: {
31
- readonly name: "Sharp Notes";
32
- readonly shortName: "Sharp";
30
+ readonly noteNamesSharp: {
31
+ readonly name: "Sharp Note Names";
32
+ readonly shortName: "Sharp Notes";
33
33
  readonly isRelative: false;
34
34
  readonly labels: readonly ["C", "C♯", "D", "D♯", "E", "F", "F♯", "G", "G♯", "A", "A♯", "B"];
35
35
  };
36
+ readonly intervalsFlat: {
37
+ readonly name: "Flat Note Intervals";
38
+ readonly shortName: "Flat Intervals";
39
+ readonly isRelative: true;
40
+ readonly labels: readonly ["1", "♭2", "2", "♭3", "3", "4", "♭5", "5", "♭6", "6", "♭7", "7"];
41
+ };
42
+ readonly intervalsSharp: {
43
+ readonly name: "Sharp Note Intervals";
44
+ readonly shortName: "Sharp Intervals";
45
+ readonly isRelative: true;
46
+ readonly labels: readonly ["1", "♯1", "2", "♯2", "3", "4", "♯4", "5", "♯5", "6", "♯6", "7"];
47
+ };
48
+ readonly extensionsFlat: {
49
+ readonly name: "Flat Note Extensions";
50
+ readonly shortName: "Flat Extensions";
51
+ readonly isRelative: true;
52
+ readonly labels: readonly ["1", "♭9", "9", "♭3", "3", "11", "♭5", "5", "♭13", "13", "♭7", "7"];
53
+ };
54
+ readonly extensionsSharp: {
55
+ readonly name: "Sharp Note Extensions";
56
+ readonly shortName: "Sharp Extensions";
57
+ readonly isRelative: true;
58
+ readonly labels: readonly ["1", "♯1", "9", "♯9", "3", "11", "♯11", "5", "♯5", "13", "♯13", "7"];
59
+ };
36
60
  readonly fixedDoFlat: {
37
61
  readonly name: "Solfege Fixed Do Flat Notes";
38
62
  readonly shortName: "Fixed Do Flat";
@@ -1 +1 @@
1
- {"version":3,"file":"note-label-collections.d.ts","sourceRoot":"","sources":["../../../../src/src/data/labels/note-label-collections.ts"],"names":[],"mappings":"AAAA,yFAAyF;AACzF,MAAM,MAAM,cAAc,GAAG,SAAS;IACpC,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;CACP,CAAC;AAEF,iFAAiF;AACjF,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC;CACjC;AAED,QAAA,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8FjB,CAAC;AAEX,+EAA+E;AAC/E,MAAM,MAAM,sBAAsB,GAAG,MAAM,OAAO,qBAAqB,CAAC;AAExE,yGAAyG;AACzG,eAAO,MAAM,oBAAoB,EAAE,MAAM,CACvC,sBAAsB,EACtB,mBAAmB,CACI,CAAC"}
1
+ {"version":3,"file":"note-label-collections.d.ts","sourceRoot":"","sources":["../../../../src/src/data/labels/note-label-collections.ts"],"names":[],"mappings":"AAAA,yFAAyF;AACzF,MAAM,MAAM,cAAc,GAAG,SAAS;IACpC,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;CACP,CAAC;AAEF,iFAAiF;AACjF,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC;CACjC;AAED,QAAA,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoJjB,CAAC;AAEX,+EAA+E;AAC/E,MAAM,MAAM,sBAAsB,GAAG,MAAM,OAAO,qBAAqB,CAAC;AAExE,yGAAyG;AACzG,eAAO,MAAM,oBAAoB,EAAE,MAAM,CACvC,sBAAsB,EACtB,mBAAmB,CACI,CAAC"}
@@ -1,16 +1,66 @@
1
1
  const _noteLabelCollections = {
2
- flat: {
3
- name: "Flat Notes",
4
- shortName: "Flat",
2
+ noteNamesFlat: {
3
+ name: "Flat Note Names",
4
+ shortName: "Flat Notes",
5
5
  isRelative: false,
6
6
  labels: ["C", "D♭", "D", "E♭", "E", "F", "G♭", "G", "A♭", "A", "B♭", "B"],
7
7
  },
8
- sharp: {
9
- name: "Sharp Notes",
10
- shortName: "Sharp",
8
+ noteNamesSharp: {
9
+ name: "Sharp Note Names",
10
+ shortName: "Sharp Notes",
11
11
  isRelative: false,
12
12
  labels: ["C", "C♯", "D", "D♯", "E", "F", "F♯", "G", "G♯", "A", "A♯", "B"],
13
13
  },
14
+ intervalsFlat: {
15
+ name: "Flat Note Intervals",
16
+ shortName: "Flat Intervals",
17
+ isRelative: true,
18
+ labels: ["1", "♭2", "2", "♭3", "3", "4", "♭5", "5", "♭6", "6", "♭7", "7"],
19
+ },
20
+ intervalsSharp: {
21
+ name: "Sharp Note Intervals",
22
+ shortName: "Sharp Intervals",
23
+ isRelative: true,
24
+ labels: ["1", "♯1", "2", "♯2", "3", "4", "♯4", "5", "♯5", "6", "♯6", "7"],
25
+ },
26
+ extensionsFlat: {
27
+ name: "Flat Note Extensions",
28
+ shortName: "Flat Extensions",
29
+ isRelative: true,
30
+ labels: [
31
+ "1",
32
+ "♭9",
33
+ "9",
34
+ "♭3",
35
+ "3",
36
+ "11",
37
+ "♭5",
38
+ "5",
39
+ "♭13",
40
+ "13",
41
+ "♭7",
42
+ "7",
43
+ ],
44
+ },
45
+ extensionsSharp: {
46
+ name: "Sharp Note Extensions",
47
+ shortName: "Sharp Extensions",
48
+ isRelative: true,
49
+ labels: [
50
+ "1",
51
+ "♯1",
52
+ "9",
53
+ "♯9",
54
+ "3",
55
+ "11",
56
+ "♯11",
57
+ "5",
58
+ "♯5",
59
+ "13",
60
+ "♯13",
61
+ "7",
62
+ ],
63
+ },
14
64
  fixedDoFlat: {
15
65
  name: "Solfege Fixed Do Flat Notes",
16
66
  shortName: "Fixed Do Flat",
@@ -1,9 +1,10 @@
1
1
  import { type Interval, type IntervalQuality } from "../data/labels/note-labels.js";
2
2
  import type { NoteCollection } from "../types/note-collections";
3
- import type { NoteCollectionKey } from "../data/note-collections/mod.js";
3
+ import { type NoteCollectionKey } from "../data/note-collections/mod.js";
4
4
  /**
5
5
  * Removes octave intervals (such as "8" or "♮8") from a given list of intervals.
6
- * Highly useful for standardizing chord definitions that conventionally ignore the octave.
6
+ * Highly useful for standardizing chord and scale definitions (scales conventionally include the octave,
7
+ * chords conventionally do not).
7
8
  *
8
9
  * @param intervals The array of intervals to filter.
9
10
  * @returns A new array excluding any octave intervals.
@@ -19,14 +20,56 @@ export declare function sortIntervals(intervals: readonly Interval[]): Interval[
19
20
  /** Specifies a direction for converting between simple and compound/extension intervals. */
20
21
  export type IntervalTransformation = "simpleToExtension" | "extensionToSimple" | "simpleToCompound" | "compoundToSimple";
21
22
  /** Options for grouping and preprocessing intervals before they are evaluated. */
22
- export interface TransformIntervalsOptions {
23
+ export type TransformIntervalsOptions = {
24
+ /**
25
+ * Transforms intervals between simple, compound, and extended forms.
26
+ * For example, "simpleToExtension" might change "2" into "9".
27
+ */
23
28
  intervalTransformation?: IntervalTransformation;
29
+ /**
30
+ * Continues to filter out octave intervals (like "8") from the results.
31
+ * Typically useful for scales where octaves are included by default but not needed for some applications.
32
+ */
24
33
  filterOutOctave?: boolean;
34
+ /**
35
+ * Will sort intervals in ascending order based on their integer value.
36
+ * If `fillChromatic` is true, sorting is ignored as the array is fixed to the 12 semitones.
37
+ */
25
38
  shouldSort?: boolean;
26
- fillChromatic?: boolean;
27
- rotateToRootInteger0?: boolean;
39
+ /**
40
+ * A fixed number of steps to rotate the array left (positive) or right (negative).
41
+ * Rotation pushes elements from the beginning of the array to the end (left) or vice versa.
42
+ */
43
+ rotateLeft?: number;
44
+ } & ({
45
+ /**
46
+ * When true, generates a 12-element array representing the chromatic scale (0-11).
47
+ * Missing semitones are filled with standard flat intervals (like "♭2").
48
+ * Compound intervals are placed in their respective integer modulo 12 slot.
49
+ */
50
+ fillChromatic: true;
51
+ /**
52
+ * If `fillChromatic` is true, this optionally overlays intervals from a known
53
+ * note collection (like a major scale) to provide better enharmonic spelling
54
+ * for the "background" chromatic notes.
55
+ */
28
56
  mostSimilarScale?: NoteCollectionKey;
29
- }
57
+ /**
58
+ * If provided, allows for absolute rotations (like rotating to C=0).
59
+ */
60
+ rootNoteInteger?: number;
61
+ /**
62
+ * Rotates the returned array so that the note corresponding to root C (integer 0)
63
+ * is positioned at index 0. Has no semantic effect on purely relative intervals.
64
+ * Only applicable when fillChromatic is true and rootNoteInteger is provided.
65
+ */
66
+ rotateToRootInteger0?: boolean;
67
+ } | {
68
+ fillChromatic?: false;
69
+ mostSimilarScale?: never;
70
+ rootNoteInteger?: never;
71
+ rotateToRootInteger0?: never;
72
+ });
30
73
  /**
31
74
  * Applies a series of formatting steps to an array of intervals, such as changing compound formats,
32
75
  * filtering octaves, and sorting.
@@ -1 +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;AAChE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AAEzE;;;;;;GAMG;AACH,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,4FAA4F;AAC5F,MAAM,MAAM,sBAAsB,GAC9B,mBAAmB,GACnB,mBAAmB,GACnB,kBAAkB,GAClB,kBAAkB,CAAC;AAEvB,kFAAkF;AAClF,MAAM,WAAW,yBAAyB;IACxC,sBAAsB,CAAC,EAAE,sBAAsB,CAAC;IAChD,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,gBAAgB,CAAC,EAAE,iBAAiB,CAAC;CACtC;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,SAAS,QAAQ,EAAE,EAC9B,OAAO,GAAE,yBAA8B,GACtC,QAAQ,EAAE,CA+BZ;AAED;;;;;GAKG;AACH,wBAAgB,yBAAyB,CACvC,SAAS,EAAE,SAAS,QAAQ,EAAE,GAC7B,eAAe,EAAE,CAKnB;AAED;;;;;GAKG;AACH,wBAAgB,yBAAyB,CACvC,SAAS,EAAE,eAAe,EAAE,GAC3B,QAAQ,EAAE,CAKZ;AAED;;;;GAIG;AACH,wBAAgB,8BAA8B,CAC5C,UAAU,EAAE,cAAc,GACzB,eAAe,EAAE,CAEnB"}
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;AAChE,OAAO,EACL,KAAK,iBAAiB,EAEvB,MAAM,iCAAiC,CAAC;AAIzC;;;;;;;GAOG;AACH,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,4FAA4F;AAC5F,MAAM,MAAM,sBAAsB,GAC9B,mBAAmB,GACnB,mBAAmB,GACnB,kBAAkB,GAClB,kBAAkB,CAAC;AAEvB,kFAAkF;AAClF,MAAM,MAAM,yBAAyB,GAAG;IACtC;;;OAGG;IACH,sBAAsB,CAAC,EAAE,sBAAsB,CAAC;IAChD;;;OAGG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,GAAG,CACA;IACE;;;;OAIG;IACH,aAAa,EAAE,IAAI,CAAC;IACpB;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,iBAAiB,CAAC;IACrC;;OAEG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;;OAIG;IACH,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAChC,GACD;IACE,aAAa,CAAC,EAAE,KAAK,CAAC;IACtB,gBAAgB,CAAC,EAAE,KAAK,CAAC;IACzB,eAAe,CAAC,EAAE,KAAK,CAAC;IACxB,oBAAoB,CAAC,EAAE,KAAK,CAAC;CAC9B,CACJ,CAAC;AAEF;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,SAAS,QAAQ,EAAE,EAC9B,OAAO,GAAE,yBAA8B,GACtC,QAAQ,EAAE,CAiFZ;AAED;;;;;GAKG;AACH,wBAAgB,yBAAyB,CACvC,SAAS,EAAE,SAAS,QAAQ,EAAE,GAC7B,eAAe,EAAE,CAKnB;AAED;;;;;GAKG;AACH,wBAAgB,yBAAyB,CACvC,SAAS,EAAE,eAAe,EAAE,GAC3B,QAAQ,EAAE,CAKZ;AAED;;;;GAIG;AACH,wBAAgB,8BAA8B,CAC5C,UAAU,EAAE,cAAc,GACzB,eAAe,EAAE,CAEnB"}
@@ -1,7 +1,11 @@
1
1
  import { compoundToSimpleIntervalMap, extensionToSimpleIntervalMap, intervalQualityToIntervalMap, intervalToIntegerMap, intervalToIntervalQualityMap, simpleToCompoundIntervalMap, simpleToExtensionIntervalMap, } from "../data/labels/note-labels.js";
2
+ import { noteCollections, } from "../data/note-collections/mod.js";
3
+ import { noteLabelCollections } from "../mod.js";
4
+ import { rotateArrayLeft } from "./rotate-array.js";
2
5
  /**
3
6
  * Removes octave intervals (such as "8" or "♮8") from a given list of intervals.
4
- * Highly useful for standardizing chord definitions that conventionally ignore the octave.
7
+ * Highly useful for standardizing chord and scale definitions (scales conventionally include the octave,
8
+ * chords conventionally do not).
5
9
  *
6
10
  * @param intervals The array of intervals to filter.
7
11
  * @returns A new array excluding any octave intervals.
@@ -33,7 +37,7 @@ export function sortIntervals(intervals) {
33
37
  * @returns A new transformed array of intervals.
34
38
  */
35
39
  export function transformIntervals(intervals, options = {}) {
36
- const { intervalTransformation, filterOutOctave = false, shouldSort = true, } = options;
40
+ const { intervalTransformation, filterOutOctave = false, shouldSort = true, fillChromatic = false, mostSimilarScale, rotateLeft, } = options;
37
41
  const intervalMap = (() => {
38
42
  switch (intervalTransformation) {
39
43
  case "simpleToExtension":
@@ -51,8 +55,46 @@ export function transformIntervals(intervals, options = {}) {
51
55
  const fundamentalIntervals = filterOutOctave
52
56
  ? filterOutOctaveIntervals(intervals)
53
57
  : intervals;
54
- const finalIntervals = fundamentalIntervals.map((interval) => intervalMap.get(interval) ?? interval);
55
- return shouldSort ? sortIntervals(finalIntervals) : finalIntervals;
58
+ const transformedIntervals = fundamentalIntervals.map((interval) => intervalMap.get(interval) ?? interval);
59
+ if (fillChromatic) {
60
+ const chromaticMap = [
61
+ ...noteLabelCollections.intervalsFlat.labels,
62
+ ];
63
+ if (mostSimilarScale) {
64
+ const collection = noteCollections[mostSimilarScale];
65
+ if (collection && collection.intervals !== intervals) {
66
+ collection.intervals.forEach((interval) => {
67
+ const transformed = intervalMap.get(interval) ?? interval;
68
+ const semitones = intervalToIntegerMap.get(transformed);
69
+ if (semitones !== undefined) {
70
+ chromaticMap[semitones % 12] = transformed;
71
+ }
72
+ });
73
+ }
74
+ }
75
+ // Now overlay the provided parsed intervals
76
+ transformedIntervals.forEach((interval) => {
77
+ const semitones = intervalToIntegerMap.get(interval);
78
+ if (semitones !== undefined) {
79
+ chromaticMap[semitones % 12] = interval;
80
+ }
81
+ });
82
+ let result = chromaticMap;
83
+ if (options.rotateToRootInteger0 && options.rootNoteInteger !== undefined) {
84
+ result = rotateArrayLeft(result, -options.rootNoteInteger);
85
+ }
86
+ if (rotateLeft !== undefined) {
87
+ result = rotateArrayLeft(result, rotateLeft);
88
+ }
89
+ return result;
90
+ }
91
+ let result = shouldSort
92
+ ? sortIntervals(transformedIntervals)
93
+ : transformedIntervals;
94
+ if (rotateLeft !== undefined) {
95
+ result = rotateArrayLeft(result, rotateLeft);
96
+ }
97
+ return result;
56
98
  }
57
99
  /**
58
100
  * Extracts generic interval qualities (e.g., "P5", "m3" becomes "P", "m") from a list of intervals.
@@ -44,5 +44,5 @@ export declare function getNoteNamesFromRootAndIntervals(rootNote: RootNote, int
44
44
  * @param options Optional settings for interval transformations or output formatting.
45
45
  * @returns An array of computed note names.
46
46
  */
47
- export declare function getNoteNamesFromRootAndCollectionKey(rootNote: RootNote, noteCollectionKey: NoteCollectionKey, options?: TransformIntervalsOptions): NoteName[];
47
+ export declare function getNoteNamesFromRootAndCollectionKey(rootNote: RootNote, noteCollectionKey: NoteCollectionKey, options?: Omit<TransformIntervalsOptions, "mostSimilarScale">): NoteName[];
48
48
  //# sourceMappingURL=note-names.d.ts.map
@@ -1 +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;AAEvC,OAAO,EACL,KAAK,iBAAiB,EAEvB,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAEL,KAAK,yBAAyB,EAC/B,MAAM,gBAAgB,CAAC;AAQxB;;;;;;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;;;;GAIG;AACH,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,MAAM,GACf,eAAe,GAAG,SAAS,CAI7B;AA4CD;;;;;;;;;GASG;AACH,wBAAgB,gCAAgC,CAC9C,QAAQ,EAAE,QAAQ,EAClB,SAAS,EAAE,SAAS,QAAQ,EAAE,EAC9B,OAAO,GAAE,yBAA8B,GACtC,QAAQ,EAAE,CA4EZ;AAED;;;;;;;;;GASG;AACH,wBAAgB,oCAAoC,CAClD,QAAQ,EAAE,QAAQ,EAClB,iBAAiB,EAAE,iBAAiB,EACpC,OAAO,GAAE,yBAA8B,GACtC,QAAQ,EAAE,CAkBZ"}
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;AAQxB;;;;;;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;;;;GAIG;AACH,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,MAAM,GACf,eAAe,GAAG,SAAS,CAI7B;AA4CD;;;;;;;;;GASG;AACH,wBAAgB,gCAAgC,CAC9C,QAAQ,EAAE,QAAQ,EAClB,SAAS,EAAE,SAAS,QAAQ,EAAE,EAC9B,OAAO,GAAE,yBAA8B,GACtC,QAAQ,EAAE,CAgCZ;AAED;;;;;;;;;GASG;AACH,wBAAgB,oCAAoC,CAClD,QAAQ,EAAE,QAAQ,EAClB,iBAAiB,EAAE,iBAAiB,EACpC,OAAO,GAAE,IAAI,CAAC,yBAAyB,EAAE,kBAAkB,CAAM,GAChE,QAAQ,EAAE,CAuBZ"}
@@ -1,9 +1,7 @@
1
1
  import { enharmonicNoteNameGroups, intervalToIntegerMap, noteLetters, noteNamesSet, noteNameToIntegerMap, rootNotesSet, } from "../data/labels/note-labels.js";
2
- import { noteLabelCollections } from "../data/labels/note-label-collections.js";
3
2
  import { noteCollections, } from "../data/note-collections/mod.js";
4
3
  import { transformIntervals, } from "./intervals.js";
5
4
  import { isValidNoteCollectionKey } from "./note-collections.js";
6
- import { rotateArrayLeft } from "./rotate-array.js";
7
5
  const NOTE_LETTER_REGEX = /^[A-Ga-g]/;
8
6
  const ACCIDENTAL_REGEX = /([#♯xX𝄪]+)|([b♭𝄫]+)/gu;
9
7
  const INTERVAL_NUMBER_REGEX = /\d+/;
@@ -148,56 +146,21 @@ export function getNoteNamesFromRootAndIntervals(rootNote, intervals, options =
148
146
  const rootNoteLetter = rootNote.charAt(0).toUpperCase();
149
147
  const rootNoteLetterIndex = noteLetters.indexOf(rootNoteLetter);
150
148
  // 2. Transform Intervals
151
- const intervalsToConvert = Object.keys(options).length > 0
152
- ? transformIntervals(intervals, options)
149
+ const enhancedOptions = options.fillChromatic
150
+ ? {
151
+ ...options,
152
+ ...(options.rotateToRootInteger0 ? { rootNoteInteger } : {}),
153
+ }
154
+ : options;
155
+ const intervalsToConvert = Object.keys(enhancedOptions).length > 0
156
+ ? transformIntervals(intervals, enhancedOptions)
153
157
  : intervals;
154
- let noteNames;
155
158
  // 3. Generate Note Names
156
- if (options.fillChromatic) {
157
- // Initialize 12-note array with default flat notes relative to the root
158
- // This ensures every semitone slot has a value (e.g., C, Db, D...)
159
- const flatNotes = noteLabelCollections.flat.labels;
160
- noteNames = Array.from({ length: 12 }, (_, i) => {
161
- const absoluteInteger = (rootNoteInteger + i) % 12;
162
- return flatNotes[absoluteInteger];
163
- });
164
- // 3a. Overlay mostSimilarScale if provided (to provide better accidental contexts)
165
- if (options.mostSimilarScale) {
166
- const similarCollection = noteCollections[options.mostSimilarScale];
167
- if (similarCollection && similarCollection.intervals !== intervals) {
168
- similarCollection.intervals.forEach((interval) => {
169
- const result = getNoteFromRootAndInterval(rootNoteInteger, rootNoteLetterIndex, interval);
170
- if (result) {
171
- noteNames[result.semitoneOffset] = result.noteName;
172
- }
173
- });
174
- }
175
- }
176
- // Overwrite default notes with specific notes calculated from intervals
177
- intervalsToConvert.forEach((interval) => {
178
- const result = getNoteFromRootAndInterval(rootNoteInteger, rootNoteLetterIndex, interval);
179
- if (result) {
180
- noteNames[result.semitoneOffset] = result.noteName;
181
- }
182
- });
183
- }
184
- else {
185
- // Map intervals directly to note names
186
- noteNames = intervalsToConvert.flatMap((interval) => {
187
- const result = getNoteFromRootAndInterval(rootNoteInteger, rootNoteLetterIndex, interval);
188
- return result ? [result.noteName] : [];
189
- });
190
- }
191
- // 4. Rotate Array (Optional)
192
- if (options.rotateToRootInteger0) {
193
- // Rotate so that the note corresponding to integer 0 (C) is at index 0.
194
- // e.g., if Root is D (2), the array currently starts at D.
195
- // To move C (currently at index 10) to index 0, we rotate right by 2 (rootNoteInteger).
196
- // rotateArrayLeft performs a left shift for positive values.
197
- // To move C (at index -rootNoteInteger) to 0, we need to shift right by rootNoteInteger.
198
- // So we rotate by -rootNoteInteger.
199
- noteNames = rotateArrayLeft(noteNames, -rootNoteInteger);
200
- }
159
+ // Map intervals directly to note names
160
+ const noteNames = intervalsToConvert.flatMap((interval) => {
161
+ const result = getNoteFromRootAndInterval(rootNoteInteger, rootNoteLetterIndex, interval);
162
+ return result ? [result.noteName] : [];
163
+ });
201
164
  return noteNames;
202
165
  }
203
166
  /**
@@ -214,11 +177,15 @@ export function getNoteNamesFromRootAndCollectionKey(rootNote, noteCollectionKey
214
177
  if (!isValidNoteCollectionKey(noteCollectionKey))
215
178
  return [];
216
179
  const collection = noteCollections[noteCollectionKey];
217
- const mostSimilarScale = "mostSimilarScale" in collection
218
- ? collection.mostSimilarScale
219
- : undefined;
220
- const finalOptions = mostSimilarScale && mostSimilarScale !== noteCollectionKey
221
- ? { ...options, mostSimilarScale: mostSimilarScale }
180
+ const mostSimilarScale = "mostSimilarScale" in collection ? collection.mostSimilarScale : undefined;
181
+ const finalOptions = options.fillChromatic &&
182
+ mostSimilarScale &&
183
+ mostSimilarScale !== noteCollectionKey
184
+ ? {
185
+ ...options,
186
+ fillChromatic: true,
187
+ mostSimilarScale: mostSimilarScale,
188
+ }
222
189
  : options;
223
190
  return getNoteNamesFromRootAndIntervals(rootNote, collection.intervals, finalOptions);
224
191
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@musodojo/music-theory-data",
3
- "version": "21.1.1",
3
+ "version": "23.0.0",
4
4
  "description": "The musician-friendly TypeScript library for scales, modes, chords, and arpeggios.",
5
5
  "keywords": [
6
6
  "music",
@@ -21,18 +21,42 @@ export interface NoteLabelCollection {
21
21
  readonly labels: NoteLabelGroup;
22
22
  }
23
23
  declare const _noteLabelCollections: {
24
- readonly flat: {
25
- readonly name: "Flat Notes";
26
- readonly shortName: "Flat";
24
+ readonly noteNamesFlat: {
25
+ readonly name: "Flat Note Names";
26
+ readonly shortName: "Flat Notes";
27
27
  readonly isRelative: false;
28
28
  readonly labels: readonly ["C", "D♭", "D", "E♭", "E", "F", "G♭", "G", "A♭", "A", "B♭", "B"];
29
29
  };
30
- readonly sharp: {
31
- readonly name: "Sharp Notes";
32
- readonly shortName: "Sharp";
30
+ readonly noteNamesSharp: {
31
+ readonly name: "Sharp Note Names";
32
+ readonly shortName: "Sharp Notes";
33
33
  readonly isRelative: false;
34
34
  readonly labels: readonly ["C", "C♯", "D", "D♯", "E", "F", "F♯", "G", "G♯", "A", "A♯", "B"];
35
35
  };
36
+ readonly intervalsFlat: {
37
+ readonly name: "Flat Note Intervals";
38
+ readonly shortName: "Flat Intervals";
39
+ readonly isRelative: true;
40
+ readonly labels: readonly ["1", "♭2", "2", "♭3", "3", "4", "♭5", "5", "♭6", "6", "♭7", "7"];
41
+ };
42
+ readonly intervalsSharp: {
43
+ readonly name: "Sharp Note Intervals";
44
+ readonly shortName: "Sharp Intervals";
45
+ readonly isRelative: true;
46
+ readonly labels: readonly ["1", "♯1", "2", "♯2", "3", "4", "♯4", "5", "♯5", "6", "♯6", "7"];
47
+ };
48
+ readonly extensionsFlat: {
49
+ readonly name: "Flat Note Extensions";
50
+ readonly shortName: "Flat Extensions";
51
+ readonly isRelative: true;
52
+ readonly labels: readonly ["1", "♭9", "9", "♭3", "3", "11", "♭5", "5", "♭13", "13", "♭7", "7"];
53
+ };
54
+ readonly extensionsSharp: {
55
+ readonly name: "Sharp Note Extensions";
56
+ readonly shortName: "Sharp Extensions";
57
+ readonly isRelative: true;
58
+ readonly labels: readonly ["1", "♯1", "9", "♯9", "3", "11", "♯11", "5", "♯5", "13", "♯13", "7"];
59
+ };
36
60
  readonly fixedDoFlat: {
37
61
  readonly name: "Solfege Fixed Do Flat Notes";
38
62
  readonly shortName: "Fixed Do Flat";
@@ -1 +1 @@
1
- {"version":3,"file":"note-label-collections.d.ts","sourceRoot":"","sources":["../../../../src/src/data/labels/note-label-collections.ts"],"names":[],"mappings":"AAAA,yFAAyF;AACzF,MAAM,MAAM,cAAc,GAAG,SAAS;IACpC,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;CACP,CAAC;AAEF,iFAAiF;AACjF,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC;CACjC;AAED,QAAA,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8FjB,CAAC;AAEX,+EAA+E;AAC/E,MAAM,MAAM,sBAAsB,GAAG,MAAM,OAAO,qBAAqB,CAAC;AAExE,yGAAyG;AACzG,eAAO,MAAM,oBAAoB,EAAE,MAAM,CACvC,sBAAsB,EACtB,mBAAmB,CACI,CAAC"}
1
+ {"version":3,"file":"note-label-collections.d.ts","sourceRoot":"","sources":["../../../../src/src/data/labels/note-label-collections.ts"],"names":[],"mappings":"AAAA,yFAAyF;AACzF,MAAM,MAAM,cAAc,GAAG,SAAS;IACpC,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;CACP,CAAC;AAEF,iFAAiF;AACjF,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC;CACjC;AAED,QAAA,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoJjB,CAAC;AAEX,+EAA+E;AAC/E,MAAM,MAAM,sBAAsB,GAAG,MAAM,OAAO,qBAAqB,CAAC;AAExE,yGAAyG;AACzG,eAAO,MAAM,oBAAoB,EAAE,MAAM,CACvC,sBAAsB,EACtB,mBAAmB,CACI,CAAC"}
@@ -2,18 +2,68 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.noteLabelCollections = void 0;
4
4
  const _noteLabelCollections = {
5
- flat: {
6
- name: "Flat Notes",
7
- shortName: "Flat",
5
+ noteNamesFlat: {
6
+ name: "Flat Note Names",
7
+ shortName: "Flat Notes",
8
8
  isRelative: false,
9
9
  labels: ["C", "D♭", "D", "E♭", "E", "F", "G♭", "G", "A♭", "A", "B♭", "B"],
10
10
  },
11
- sharp: {
12
- name: "Sharp Notes",
13
- shortName: "Sharp",
11
+ noteNamesSharp: {
12
+ name: "Sharp Note Names",
13
+ shortName: "Sharp Notes",
14
14
  isRelative: false,
15
15
  labels: ["C", "C♯", "D", "D♯", "E", "F", "F♯", "G", "G♯", "A", "A♯", "B"],
16
16
  },
17
+ intervalsFlat: {
18
+ name: "Flat Note Intervals",
19
+ shortName: "Flat Intervals",
20
+ isRelative: true,
21
+ labels: ["1", "♭2", "2", "♭3", "3", "4", "♭5", "5", "♭6", "6", "♭7", "7"],
22
+ },
23
+ intervalsSharp: {
24
+ name: "Sharp Note Intervals",
25
+ shortName: "Sharp Intervals",
26
+ isRelative: true,
27
+ labels: ["1", "♯1", "2", "♯2", "3", "4", "♯4", "5", "♯5", "6", "♯6", "7"],
28
+ },
29
+ extensionsFlat: {
30
+ name: "Flat Note Extensions",
31
+ shortName: "Flat Extensions",
32
+ isRelative: true,
33
+ labels: [
34
+ "1",
35
+ "♭9",
36
+ "9",
37
+ "♭3",
38
+ "3",
39
+ "11",
40
+ "♭5",
41
+ "5",
42
+ "♭13",
43
+ "13",
44
+ "♭7",
45
+ "7",
46
+ ],
47
+ },
48
+ extensionsSharp: {
49
+ name: "Sharp Note Extensions",
50
+ shortName: "Sharp Extensions",
51
+ isRelative: true,
52
+ labels: [
53
+ "1",
54
+ "♯1",
55
+ "9",
56
+ "♯9",
57
+ "3",
58
+ "11",
59
+ "♯11",
60
+ "5",
61
+ "♯5",
62
+ "13",
63
+ "♯13",
64
+ "7",
65
+ ],
66
+ },
17
67
  fixedDoFlat: {
18
68
  name: "Solfege Fixed Do Flat Notes",
19
69
  shortName: "Fixed Do Flat",
@@ -1,9 +1,10 @@
1
1
  import { type Interval, type IntervalQuality } from "../data/labels/note-labels.js";
2
2
  import type { NoteCollection } from "../types/note-collections";
3
- import type { NoteCollectionKey } from "../data/note-collections/mod.js";
3
+ import { type NoteCollectionKey } from "../data/note-collections/mod.js";
4
4
  /**
5
5
  * Removes octave intervals (such as "8" or "♮8") from a given list of intervals.
6
- * Highly useful for standardizing chord definitions that conventionally ignore the octave.
6
+ * Highly useful for standardizing chord and scale definitions (scales conventionally include the octave,
7
+ * chords conventionally do not).
7
8
  *
8
9
  * @param intervals The array of intervals to filter.
9
10
  * @returns A new array excluding any octave intervals.
@@ -19,14 +20,56 @@ export declare function sortIntervals(intervals: readonly Interval[]): Interval[
19
20
  /** Specifies a direction for converting between simple and compound/extension intervals. */
20
21
  export type IntervalTransformation = "simpleToExtension" | "extensionToSimple" | "simpleToCompound" | "compoundToSimple";
21
22
  /** Options for grouping and preprocessing intervals before they are evaluated. */
22
- export interface TransformIntervalsOptions {
23
+ export type TransformIntervalsOptions = {
24
+ /**
25
+ * Transforms intervals between simple, compound, and extended forms.
26
+ * For example, "simpleToExtension" might change "2" into "9".
27
+ */
23
28
  intervalTransformation?: IntervalTransformation;
29
+ /**
30
+ * Continues to filter out octave intervals (like "8") from the results.
31
+ * Typically useful for scales where octaves are included by default but not needed for some applications.
32
+ */
24
33
  filterOutOctave?: boolean;
34
+ /**
35
+ * Will sort intervals in ascending order based on their integer value.
36
+ * If `fillChromatic` is true, sorting is ignored as the array is fixed to the 12 semitones.
37
+ */
25
38
  shouldSort?: boolean;
26
- fillChromatic?: boolean;
27
- rotateToRootInteger0?: boolean;
39
+ /**
40
+ * A fixed number of steps to rotate the array left (positive) or right (negative).
41
+ * Rotation pushes elements from the beginning of the array to the end (left) or vice versa.
42
+ */
43
+ rotateLeft?: number;
44
+ } & ({
45
+ /**
46
+ * When true, generates a 12-element array representing the chromatic scale (0-11).
47
+ * Missing semitones are filled with standard flat intervals (like "♭2").
48
+ * Compound intervals are placed in their respective integer modulo 12 slot.
49
+ */
50
+ fillChromatic: true;
51
+ /**
52
+ * If `fillChromatic` is true, this optionally overlays intervals from a known
53
+ * note collection (like a major scale) to provide better enharmonic spelling
54
+ * for the "background" chromatic notes.
55
+ */
28
56
  mostSimilarScale?: NoteCollectionKey;
29
- }
57
+ /**
58
+ * If provided, allows for absolute rotations (like rotating to C=0).
59
+ */
60
+ rootNoteInteger?: number;
61
+ /**
62
+ * Rotates the returned array so that the note corresponding to root C (integer 0)
63
+ * is positioned at index 0. Has no semantic effect on purely relative intervals.
64
+ * Only applicable when fillChromatic is true and rootNoteInteger is provided.
65
+ */
66
+ rotateToRootInteger0?: boolean;
67
+ } | {
68
+ fillChromatic?: false;
69
+ mostSimilarScale?: never;
70
+ rootNoteInteger?: never;
71
+ rotateToRootInteger0?: never;
72
+ });
30
73
  /**
31
74
  * Applies a series of formatting steps to an array of intervals, such as changing compound formats,
32
75
  * filtering octaves, and sorting.
@@ -1 +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;AAChE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AAEzE;;;;;;GAMG;AACH,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,4FAA4F;AAC5F,MAAM,MAAM,sBAAsB,GAC9B,mBAAmB,GACnB,mBAAmB,GACnB,kBAAkB,GAClB,kBAAkB,CAAC;AAEvB,kFAAkF;AAClF,MAAM,WAAW,yBAAyB;IACxC,sBAAsB,CAAC,EAAE,sBAAsB,CAAC;IAChD,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,gBAAgB,CAAC,EAAE,iBAAiB,CAAC;CACtC;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,SAAS,QAAQ,EAAE,EAC9B,OAAO,GAAE,yBAA8B,GACtC,QAAQ,EAAE,CA+BZ;AAED;;;;;GAKG;AACH,wBAAgB,yBAAyB,CACvC,SAAS,EAAE,SAAS,QAAQ,EAAE,GAC7B,eAAe,EAAE,CAKnB;AAED;;;;;GAKG;AACH,wBAAgB,yBAAyB,CACvC,SAAS,EAAE,eAAe,EAAE,GAC3B,QAAQ,EAAE,CAKZ;AAED;;;;GAIG;AACH,wBAAgB,8BAA8B,CAC5C,UAAU,EAAE,cAAc,GACzB,eAAe,EAAE,CAEnB"}
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;AAChE,OAAO,EACL,KAAK,iBAAiB,EAEvB,MAAM,iCAAiC,CAAC;AAIzC;;;;;;;GAOG;AACH,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,4FAA4F;AAC5F,MAAM,MAAM,sBAAsB,GAC9B,mBAAmB,GACnB,mBAAmB,GACnB,kBAAkB,GAClB,kBAAkB,CAAC;AAEvB,kFAAkF;AAClF,MAAM,MAAM,yBAAyB,GAAG;IACtC;;;OAGG;IACH,sBAAsB,CAAC,EAAE,sBAAsB,CAAC;IAChD;;;OAGG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,GAAG,CACA;IACE;;;;OAIG;IACH,aAAa,EAAE,IAAI,CAAC;IACpB;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,iBAAiB,CAAC;IACrC;;OAEG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;;OAIG;IACH,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAChC,GACD;IACE,aAAa,CAAC,EAAE,KAAK,CAAC;IACtB,gBAAgB,CAAC,EAAE,KAAK,CAAC;IACzB,eAAe,CAAC,EAAE,KAAK,CAAC;IACxB,oBAAoB,CAAC,EAAE,KAAK,CAAC;CAC9B,CACJ,CAAC;AAEF;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,SAAS,QAAQ,EAAE,EAC9B,OAAO,GAAE,yBAA8B,GACtC,QAAQ,EAAE,CAiFZ;AAED;;;;;GAKG;AACH,wBAAgB,yBAAyB,CACvC,SAAS,EAAE,SAAS,QAAQ,EAAE,GAC7B,eAAe,EAAE,CAKnB;AAED;;;;;GAKG;AACH,wBAAgB,yBAAyB,CACvC,SAAS,EAAE,eAAe,EAAE,GAC3B,QAAQ,EAAE,CAKZ;AAED;;;;GAIG;AACH,wBAAgB,8BAA8B,CAC5C,UAAU,EAAE,cAAc,GACzB,eAAe,EAAE,CAEnB"}
@@ -7,9 +7,13 @@ exports.getQualitiesFromIntervals = getQualitiesFromIntervals;
7
7
  exports.getIntervalsFromQualities = getIntervalsFromQualities;
8
8
  exports.getQualitiesFromNoteCollection = getQualitiesFromNoteCollection;
9
9
  const note_labels_js_1 = require("../data/labels/note-labels.js");
10
+ const mod_js_1 = require("../data/note-collections/mod.js");
11
+ const mod_js_2 = require("../mod.js");
12
+ const rotate_array_js_1 = require("./rotate-array.js");
10
13
  /**
11
14
  * Removes octave intervals (such as "8" or "♮8") from a given list of intervals.
12
- * Highly useful for standardizing chord definitions that conventionally ignore the octave.
15
+ * Highly useful for standardizing chord and scale definitions (scales conventionally include the octave,
16
+ * chords conventionally do not).
13
17
  *
14
18
  * @param intervals The array of intervals to filter.
15
19
  * @returns A new array excluding any octave intervals.
@@ -41,7 +45,7 @@ function sortIntervals(intervals) {
41
45
  * @returns A new transformed array of intervals.
42
46
  */
43
47
  function transformIntervals(intervals, options = {}) {
44
- const { intervalTransformation, filterOutOctave = false, shouldSort = true, } = options;
48
+ const { intervalTransformation, filterOutOctave = false, shouldSort = true, fillChromatic = false, mostSimilarScale, rotateLeft, } = options;
45
49
  const intervalMap = (() => {
46
50
  switch (intervalTransformation) {
47
51
  case "simpleToExtension":
@@ -59,8 +63,46 @@ function transformIntervals(intervals, options = {}) {
59
63
  const fundamentalIntervals = filterOutOctave
60
64
  ? filterOutOctaveIntervals(intervals)
61
65
  : intervals;
62
- const finalIntervals = fundamentalIntervals.map((interval) => intervalMap.get(interval) ?? interval);
63
- return shouldSort ? sortIntervals(finalIntervals) : finalIntervals;
66
+ const transformedIntervals = fundamentalIntervals.map((interval) => intervalMap.get(interval) ?? interval);
67
+ if (fillChromatic) {
68
+ const chromaticMap = [
69
+ ...mod_js_2.noteLabelCollections.intervalsFlat.labels,
70
+ ];
71
+ if (mostSimilarScale) {
72
+ const collection = mod_js_1.noteCollections[mostSimilarScale];
73
+ if (collection && collection.intervals !== intervals) {
74
+ collection.intervals.forEach((interval) => {
75
+ const transformed = intervalMap.get(interval) ?? interval;
76
+ const semitones = note_labels_js_1.intervalToIntegerMap.get(transformed);
77
+ if (semitones !== undefined) {
78
+ chromaticMap[semitones % 12] = transformed;
79
+ }
80
+ });
81
+ }
82
+ }
83
+ // Now overlay the provided parsed intervals
84
+ transformedIntervals.forEach((interval) => {
85
+ const semitones = note_labels_js_1.intervalToIntegerMap.get(interval);
86
+ if (semitones !== undefined) {
87
+ chromaticMap[semitones % 12] = interval;
88
+ }
89
+ });
90
+ let result = chromaticMap;
91
+ if (options.rotateToRootInteger0 && options.rootNoteInteger !== undefined) {
92
+ result = (0, rotate_array_js_1.rotateArrayLeft)(result, -options.rootNoteInteger);
93
+ }
94
+ if (rotateLeft !== undefined) {
95
+ result = (0, rotate_array_js_1.rotateArrayLeft)(result, rotateLeft);
96
+ }
97
+ return result;
98
+ }
99
+ let result = shouldSort
100
+ ? sortIntervals(transformedIntervals)
101
+ : transformedIntervals;
102
+ if (rotateLeft !== undefined) {
103
+ result = (0, rotate_array_js_1.rotateArrayLeft)(result, rotateLeft);
104
+ }
105
+ return result;
64
106
  }
65
107
  /**
66
108
  * Extracts generic interval qualities (e.g., "P5", "m3" becomes "P", "m") from a list of intervals.
@@ -44,5 +44,5 @@ export declare function getNoteNamesFromRootAndIntervals(rootNote: RootNote, int
44
44
  * @param options Optional settings for interval transformations or output formatting.
45
45
  * @returns An array of computed note names.
46
46
  */
47
- export declare function getNoteNamesFromRootAndCollectionKey(rootNote: RootNote, noteCollectionKey: NoteCollectionKey, options?: TransformIntervalsOptions): NoteName[];
47
+ export declare function getNoteNamesFromRootAndCollectionKey(rootNote: RootNote, noteCollectionKey: NoteCollectionKey, options?: Omit<TransformIntervalsOptions, "mostSimilarScale">): NoteName[];
48
48
  //# sourceMappingURL=note-names.d.ts.map
@@ -1 +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;AAEvC,OAAO,EACL,KAAK,iBAAiB,EAEvB,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAEL,KAAK,yBAAyB,EAC/B,MAAM,gBAAgB,CAAC;AAQxB;;;;;;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;;;;GAIG;AACH,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,MAAM,GACf,eAAe,GAAG,SAAS,CAI7B;AA4CD;;;;;;;;;GASG;AACH,wBAAgB,gCAAgC,CAC9C,QAAQ,EAAE,QAAQ,EAClB,SAAS,EAAE,SAAS,QAAQ,EAAE,EAC9B,OAAO,GAAE,yBAA8B,GACtC,QAAQ,EAAE,CA4EZ;AAED;;;;;;;;;GASG;AACH,wBAAgB,oCAAoC,CAClD,QAAQ,EAAE,QAAQ,EAClB,iBAAiB,EAAE,iBAAiB,EACpC,OAAO,GAAE,yBAA8B,GACtC,QAAQ,EAAE,CAkBZ"}
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;AAQxB;;;;;;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;;;;GAIG;AACH,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,MAAM,GACf,eAAe,GAAG,SAAS,CAI7B;AA4CD;;;;;;;;;GASG;AACH,wBAAgB,gCAAgC,CAC9C,QAAQ,EAAE,QAAQ,EAClB,SAAS,EAAE,SAAS,QAAQ,EAAE,EAC9B,OAAO,GAAE,yBAA8B,GACtC,QAAQ,EAAE,CAgCZ;AAED;;;;;;;;;GASG;AACH,wBAAgB,oCAAoC,CAClD,QAAQ,EAAE,QAAQ,EAClB,iBAAiB,EAAE,iBAAiB,EACpC,OAAO,GAAE,IAAI,CAAC,yBAAyB,EAAE,kBAAkB,CAAM,GAChE,QAAQ,EAAE,CAuBZ"}
@@ -6,11 +6,9 @@ exports.getNoteIntegerFromString = getNoteIntegerFromString;
6
6
  exports.getNoteNamesFromRootAndIntervals = getNoteNamesFromRootAndIntervals;
7
7
  exports.getNoteNamesFromRootAndCollectionKey = getNoteNamesFromRootAndCollectionKey;
8
8
  const note_labels_js_1 = require("../data/labels/note-labels.js");
9
- const note_label_collections_js_1 = require("../data/labels/note-label-collections.js");
10
9
  const mod_js_1 = require("../data/note-collections/mod.js");
11
10
  const intervals_js_1 = require("./intervals.js");
12
11
  const note_collections_js_1 = require("./note-collections.js");
13
- const rotate_array_js_1 = require("./rotate-array.js");
14
12
  const NOTE_LETTER_REGEX = /^[A-Ga-g]/;
15
13
  const ACCIDENTAL_REGEX = /([#♯xX𝄪]+)|([b♭𝄫]+)/gu;
16
14
  const INTERVAL_NUMBER_REGEX = /\d+/;
@@ -155,56 +153,21 @@ function getNoteNamesFromRootAndIntervals(rootNote, intervals, options = {}) {
155
153
  const rootNoteLetter = rootNote.charAt(0).toUpperCase();
156
154
  const rootNoteLetterIndex = note_labels_js_1.noteLetters.indexOf(rootNoteLetter);
157
155
  // 2. Transform Intervals
158
- const intervalsToConvert = Object.keys(options).length > 0
159
- ? (0, intervals_js_1.transformIntervals)(intervals, options)
156
+ const enhancedOptions = options.fillChromatic
157
+ ? {
158
+ ...options,
159
+ ...(options.rotateToRootInteger0 ? { rootNoteInteger } : {}),
160
+ }
161
+ : options;
162
+ const intervalsToConvert = Object.keys(enhancedOptions).length > 0
163
+ ? (0, intervals_js_1.transformIntervals)(intervals, enhancedOptions)
160
164
  : intervals;
161
- let noteNames;
162
165
  // 3. Generate Note Names
163
- if (options.fillChromatic) {
164
- // Initialize 12-note array with default flat notes relative to the root
165
- // This ensures every semitone slot has a value (e.g., C, Db, D...)
166
- const flatNotes = note_label_collections_js_1.noteLabelCollections.flat.labels;
167
- noteNames = Array.from({ length: 12 }, (_, i) => {
168
- const absoluteInteger = (rootNoteInteger + i) % 12;
169
- return flatNotes[absoluteInteger];
170
- });
171
- // 3a. Overlay mostSimilarScale if provided (to provide better accidental contexts)
172
- if (options.mostSimilarScale) {
173
- const similarCollection = mod_js_1.noteCollections[options.mostSimilarScale];
174
- if (similarCollection && similarCollection.intervals !== intervals) {
175
- similarCollection.intervals.forEach((interval) => {
176
- const result = getNoteFromRootAndInterval(rootNoteInteger, rootNoteLetterIndex, interval);
177
- if (result) {
178
- noteNames[result.semitoneOffset] = result.noteName;
179
- }
180
- });
181
- }
182
- }
183
- // Overwrite default notes with specific notes calculated from intervals
184
- intervalsToConvert.forEach((interval) => {
185
- const result = getNoteFromRootAndInterval(rootNoteInteger, rootNoteLetterIndex, interval);
186
- if (result) {
187
- noteNames[result.semitoneOffset] = result.noteName;
188
- }
189
- });
190
- }
191
- else {
192
- // Map intervals directly to note names
193
- noteNames = intervalsToConvert.flatMap((interval) => {
194
- const result = getNoteFromRootAndInterval(rootNoteInteger, rootNoteLetterIndex, interval);
195
- return result ? [result.noteName] : [];
196
- });
197
- }
198
- // 4. Rotate Array (Optional)
199
- if (options.rotateToRootInteger0) {
200
- // Rotate so that the note corresponding to integer 0 (C) is at index 0.
201
- // e.g., if Root is D (2), the array currently starts at D.
202
- // To move C (currently at index 10) to index 0, we rotate right by 2 (rootNoteInteger).
203
- // rotateArrayLeft performs a left shift for positive values.
204
- // To move C (at index -rootNoteInteger) to 0, we need to shift right by rootNoteInteger.
205
- // So we rotate by -rootNoteInteger.
206
- noteNames = (0, rotate_array_js_1.rotateArrayLeft)(noteNames, -rootNoteInteger);
207
- }
166
+ // Map intervals directly to note names
167
+ const noteNames = intervalsToConvert.flatMap((interval) => {
168
+ const result = getNoteFromRootAndInterval(rootNoteInteger, rootNoteLetterIndex, interval);
169
+ return result ? [result.noteName] : [];
170
+ });
208
171
  return noteNames;
209
172
  }
210
173
  /**
@@ -221,11 +184,15 @@ function getNoteNamesFromRootAndCollectionKey(rootNote, noteCollectionKey, optio
221
184
  if (!(0, note_collections_js_1.isValidNoteCollectionKey)(noteCollectionKey))
222
185
  return [];
223
186
  const collection = mod_js_1.noteCollections[noteCollectionKey];
224
- const mostSimilarScale = "mostSimilarScale" in collection
225
- ? collection.mostSimilarScale
226
- : undefined;
227
- const finalOptions = mostSimilarScale && mostSimilarScale !== noteCollectionKey
228
- ? { ...options, mostSimilarScale: mostSimilarScale }
187
+ const mostSimilarScale = "mostSimilarScale" in collection ? collection.mostSimilarScale : undefined;
188
+ const finalOptions = options.fillChromatic &&
189
+ mostSimilarScale &&
190
+ mostSimilarScale !== noteCollectionKey
191
+ ? {
192
+ ...options,
193
+ fillChromatic: true,
194
+ mostSimilarScale: mostSimilarScale,
195
+ }
229
196
  : options;
230
197
  return getNoteNamesFromRootAndIntervals(rootNote, collection.intervals, finalOptions);
231
198
  }