@musodojo/music-theory-data 29.0.0 → 31.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.
package/README.md CHANGED
@@ -211,6 +211,21 @@ console.log(
211
211
  );
212
212
  // ["CM", "FM", "GM"]
213
213
 
214
+ // Chord-change references return one item each time the chord changes in
215
+ // progression.chords. Durations are not expanded here.
216
+ console.log(
217
+ music_theory_data.getChordProgressionChordChangeReferences(
218
+ "C",
219
+ "oneOneFiveFive",
220
+ ),
221
+ );
222
+ // [
223
+ // { rootNote: "C", chordName: "CM", noteCollectionKey: "major" },
224
+ // { rootNote: "G", chordName: "GM", noteCollectionKey: "major" },
225
+ // ]
226
+
227
+ // Unique chord references return each distinct chord once, preserving the
228
+ // first-seen order from the chord-change list.
214
229
  console.log(
215
230
  music_theory_data.getChordProgressionUniqueChordReferences(
216
231
  "C",
@@ -219,15 +234,76 @@ console.log(
219
234
  );
220
235
  // [
221
236
  // { rootNote: "C", chordName: "CM", noteCollectionKey: "major" },
222
- // { rootNote: "C", chordName: "FM", noteCollectionKey: "major" },
223
- // { rootNote: "C", chordName: "GM", noteCollectionKey: "major" },
237
+ // { rootNote: "F", chordName: "FM", noteCollectionKey: "major" },
238
+ // { rootNote: "G", chordName: "GM", noteCollectionKey: "major" },
224
239
  // ]
225
240
 
241
+ // Chord references by bar return the song/practice order grouped by bar. A
242
+ // 2-bar chord appears in two bars, and split bars contain multiple references.
243
+ console.log(
244
+ music_theory_data.getChordProgressionChordReferencesByBar(
245
+ "C",
246
+ "oneOneFiveFive",
247
+ ),
248
+ );
249
+ // [
250
+ // [{ rootNote: "C", chordName: "CM", noteCollectionKey: "major" }],
251
+ // [{ rootNote: "C", chordName: "CM", noteCollectionKey: "major" }],
252
+ // [{ rootNote: "G", chordName: "GM", noteCollectionKey: "major" }],
253
+ // [{ rootNote: "G", chordName: "GM", noteCollectionKey: "major" }],
254
+ // ]
255
+
256
+ console.log(
257
+ music_theory_data.getChordProgressionChordReferencesByBar(
258
+ "C",
259
+ "oneFourOneFiveSplitReturn",
260
+ )[6],
261
+ );
262
+ // [
263
+ // { rootNote: "C", chordName: "CM", noteCollectionKey: "major" },
264
+ // { rootNote: "G", chordName: "GM", noteCollectionKey: "major" },
265
+ // ]
266
+
267
+ // Song chord references flatten the bar-structured references into one ordered
268
+ // list for apps that want one diagram/card per chord occurrence.
269
+ console.log(
270
+ music_theory_data.getChordProgressionSongChordReferences(
271
+ "C",
272
+ "oneOneFiveFive",
273
+ ).map((reference) => reference.chordName),
274
+ );
275
+ // ["CM", "CM", "GM", "GM"]
276
+
277
+ // In Muso Dojo, "Each Chord Once" should import
278
+ // getChordProgressionUniqueChordReferences. "Full Song Order" should import
279
+ // getChordProgressionSongChordReferences, or flatten
280
+ // getChordProgressionChordReferencesByBar locally when bar grouping is needed.
281
+
226
282
  console.log(
227
283
  music_theory_data.getChordProgressionTotalDurationInBars("twelveBarBlues"),
228
284
  );
229
285
  // 12
230
286
 
287
+ console.log(
288
+ music_theory_data.getChordProgressionChordReferencesByBar(
289
+ "C",
290
+ "twelveBarBlues",
291
+ ).length,
292
+ );
293
+ // 12
294
+
295
+ console.log(
296
+ music_theory_data.getChordProgressionUniqueChordReferences(
297
+ "C",
298
+ "twelveBarBlues",
299
+ ),
300
+ );
301
+ // [
302
+ // { rootNote: "C", chordName: "C7", noteCollectionKey: "dominant7" },
303
+ // { rootNote: "F", chordName: "F7", noteCollectionKey: "dominant7" },
304
+ // { rootNote: "G", chordName: "G7", noteCollectionKey: "dominant7" },
305
+ // ]
306
+
231
307
  console.log(music_theory_data.chordProgressionBarGroups);
232
308
  // [
233
309
  // { totalBars: 4, progressionKeys: ["oneOneFiveFive", ...] },
@@ -12,7 +12,32 @@ export declare function getChordProgressionChordNames(rootNote: RootNote, progre
12
12
  export declare function getChordProgressionRomanSymbols(progressionOrKey: ChordProgression | ChordProgressionKey): string[];
13
13
  export declare function getChordProgressionKeysForTotalBars(totalBars: number): ChordProgressionKey[];
14
14
  export declare function getChordProgressionUniqueChordNames(rootNote: RootNote, progressionOrKey: ChordProgression | ChordProgressionKey): string[];
15
- export declare function getChordProgressionChordReferences(rootNote: RootNote, progressionOrKey: ChordProgression | ChordProgressionKey): ChordProgressionChordReference[];
15
+ /**
16
+ * Returns one chord reference for each authored chord entry in
17
+ * `progression.chords`.
18
+ *
19
+ * This is the "chord changes" view: a chord lasting 2 bars is returned once,
20
+ * because it is one chord entry/change, while repeated entries are preserved.
21
+ */
22
+ export declare function getChordProgressionChordChangeReferences(rootNote: RootNote, progressionOrKey: ChordProgression | ChordProgressionKey): ChordProgressionChordReference[];
23
+ /**
24
+ * Returns each distinct chord reference once, preserving first-seen order from
25
+ * the chord-change list.
26
+ */
16
27
  export declare function getChordProgressionUniqueChordReferences(rootNote: RootNote, progressionOrKey: ChordProgression | ChordProgressionKey): ChordProgressionChordReference[];
28
+ /**
29
+ * Returns duration-aware chord references grouped by bar.
30
+ *
31
+ * Each outer array item is one bar. A 2-bar chord appears in two bar entries,
32
+ * and a split bar can contain multiple chord references in order.
33
+ */
34
+ export declare function getChordProgressionChordReferencesByBar(rootNote: RootNote, progressionOrKey: ChordProgression | ChordProgressionKey): ChordProgressionChordReference[][];
35
+ /**
36
+ * Returns duration-aware chord references in song/practice order.
37
+ *
38
+ * This flattens `getChordProgressionChordReferencesByBar`, so a 2-bar chord
39
+ * appears twice and split bars preserve their left-to-right order.
40
+ */
41
+ export declare function getChordProgressionSongChordReferences(rootNote: RootNote, progressionOrKey: ChordProgression | ChordProgressionKey): ChordProgressionChordReference[];
17
42
  export declare function getChordProgressionTotalDurationInBars(progressionOrKey: ChordProgression | ChordProgressionKey): number;
18
43
  //# sourceMappingURL=chord-progressions.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"chord-progressions.d.ts","sourceRoot":"","sources":["../../../src/src/utils/chord-progressions.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,mBAAmB,EAEzB,MAAM,mCAAmC,CAAC;AAE3C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AACpE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,+BAA+B,CAAC;AAC9D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AAMzE,MAAM,WAAW,8BAA8B;IAC7C,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAC5B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,iBAAiB,EAAE,iBAAiB,CAAC;CAC/C;AAED,wBAAgB,0BAA0B,CACxC,GAAG,EAAE,MAAM,GACV,GAAG,IAAI,mBAAmB,CAE5B;AASD,wBAAgB,6BAA6B,CAC3C,QAAQ,EAAE,QAAQ,EAClB,gBAAgB,EAAE,gBAAgB,GAAG,mBAAmB,GACvD,MAAM,EAAE,CAYV;AAED,wBAAgB,+BAA+B,CAC7C,gBAAgB,EAAE,gBAAgB,GAAG,mBAAmB,GACvD,MAAM,EAAE,CAKV;AAED,wBAAgB,mCAAmC,CACjD,SAAS,EAAE,MAAM,GAChB,mBAAmB,EAAE,CAKvB;AAED,wBAAgB,mCAAmC,CACjD,QAAQ,EAAE,QAAQ,EAClB,gBAAgB,EAAE,gBAAgB,GAAG,mBAAmB,GACvD,MAAM,EAAE,CAIV;AAED,wBAAgB,kCAAkC,CAChD,QAAQ,EAAE,QAAQ,EAClB,gBAAgB,EAAE,gBAAgB,GAAG,mBAAmB,GACvD,8BAA8B,EAAE,CAmBlC;AAED,wBAAgB,wCAAwC,CACtD,QAAQ,EAAE,QAAQ,EAClB,gBAAgB,EAAE,gBAAgB,GAAG,mBAAmB,GACvD,8BAA8B,EAAE,CAkBlC;AAED,wBAAgB,sCAAsC,CACpD,gBAAgB,EAAE,gBAAgB,GAAG,mBAAmB,GACvD,MAAM,CAQR"}
1
+ {"version":3,"file":"chord-progressions.d.ts","sourceRoot":"","sources":["../../../src/src/utils/chord-progressions.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,mBAAmB,EAEzB,MAAM,mCAAmC,CAAC;AAE3C,OAAO,KAAK,EACV,gBAAgB,EAEjB,MAAM,6BAA6B,CAAC;AACrC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,+BAA+B,CAAC;AAC9D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AAMzE,MAAM,WAAW,8BAA8B;IAC7C,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAC5B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,iBAAiB,EAAE,iBAAiB,CAAC;CAC/C;AASD,wBAAgB,0BAA0B,CACxC,GAAG,EAAE,MAAM,GACV,GAAG,IAAI,mBAAmB,CAE5B;AA2CD,wBAAgB,6BAA6B,CAC3C,QAAQ,EAAE,QAAQ,EAClB,gBAAgB,EAAE,gBAAgB,GAAG,mBAAmB,GACvD,MAAM,EAAE,CAYV;AAED,wBAAgB,+BAA+B,CAC7C,gBAAgB,EAAE,gBAAgB,GAAG,mBAAmB,GACvD,MAAM,EAAE,CAKV;AAED,wBAAgB,mCAAmC,CACjD,SAAS,EAAE,MAAM,GAChB,mBAAmB,EAAE,CAKvB;AAED,wBAAgB,mCAAmC,CACjD,QAAQ,EAAE,QAAQ,EAClB,gBAAgB,EAAE,gBAAgB,GAAG,mBAAmB,GACvD,MAAM,EAAE,CAIV;AAED;;;;;;GAMG;AACH,wBAAgB,wCAAwC,CACtD,QAAQ,EAAE,QAAQ,EAClB,gBAAgB,EAAE,gBAAgB,GAAG,mBAAmB,GACvD,8BAA8B,EAAE,CAKlC;AAED;;;GAGG;AACH,wBAAgB,wCAAwC,CACtD,QAAQ,EAAE,QAAQ,EAClB,gBAAgB,EAAE,gBAAgB,GAAG,mBAAmB,GACvD,8BAA8B,EAAE,CAkBlC;AAED;;;;;GAKG;AACH,wBAAgB,uCAAuC,CACrD,QAAQ,EAAE,QAAQ,EAClB,gBAAgB,EAAE,gBAAgB,GAAG,mBAAmB,GACvD,8BAA8B,EAAE,EAAE,CAiDpC;AAED;;;;;GAKG;AACH,wBAAgB,sCAAsC,CACpD,QAAQ,EAAE,QAAQ,EAClB,gBAAgB,EAAE,gBAAgB,GAAG,mBAAmB,GACvD,8BAA8B,EAAE,CAKlC;AAED,wBAAgB,sCAAsC,CACpD,gBAAgB,EAAE,gBAAgB,GAAG,mBAAmB,GACvD,MAAM,CAQR"}
@@ -1,6 +1,7 @@
1
1
  import { chordProgressionBarGroups, chordProgressions, } from "../data/chord-progressions/mod.js";
2
2
  import { getChordQualityNoteCollectionKey } from "../data/chords/mod.js";
3
3
  import { getNoteNamesForRootAndIntervals, normalizeRootNoteString, } from "./note-names.js";
4
+ const BAR_DURATION_EPSILON = 0.000000001;
4
5
  export function isValidChordProgressionKey(key) {
5
6
  return Object.prototype.hasOwnProperty.call(chordProgressions, key);
6
7
  }
@@ -9,6 +10,28 @@ function resolveProgression(progressionOrKey) {
9
10
  return progressionOrKey;
10
11
  return chordProgressions[progressionOrKey];
11
12
  }
13
+ function createChordProgressionChordReference(chord, chordRootNote) {
14
+ return {
15
+ rootNote: chordRootNote,
16
+ chordName: chordRootNote + chord.quality,
17
+ noteCollectionKey: getChordQualityNoteCollectionKey(chord.quality),
18
+ };
19
+ }
20
+ function getResolvedChordProgressionChordReferences(rootNote, progressionOrKey) {
21
+ const progression = resolveProgression(progressionOrKey);
22
+ if (!progression)
23
+ return [];
24
+ const chordRootNotes = getNoteNamesForRootAndIntervals(rootNote, progression.chords.map((chord) => chord.degree)).map((noteName) => normalizeRootNoteString(noteName));
25
+ return progression.chords.flatMap((chord, index) => {
26
+ const chordRootNote = chordRootNotes[index];
27
+ if (!chordRootNote)
28
+ return [];
29
+ return [{
30
+ chord,
31
+ reference: createChordProgressionChordReference(chord, chordRootNote),
32
+ }];
33
+ });
34
+ }
12
35
  export function getChordProgressionChordNames(rootNote, progressionOrKey) {
13
36
  const progression = resolveProgression(progressionOrKey);
14
37
  if (!progression)
@@ -29,26 +52,24 @@ export function getChordProgressionKeysForTotalBars(totalBars) {
29
52
  export function getChordProgressionUniqueChordNames(rootNote, progressionOrKey) {
30
53
  return Array.from(new Set(getChordProgressionChordNames(rootNote, progressionOrKey)));
31
54
  }
32
- export function getChordProgressionChordReferences(rootNote, progressionOrKey) {
33
- const progression = resolveProgression(progressionOrKey);
34
- if (!progression)
35
- return [];
36
- const chordRootNotes = getNoteNamesForRootAndIntervals(rootNote, progression.chords.map((chord) => chord.degree)).map((noteName) => normalizeRootNoteString(noteName));
37
- return progression.chords.flatMap((chord, index) => {
38
- const chordRootNote = chordRootNotes[index];
39
- if (!chordRootNote)
40
- return [];
41
- return [{
42
- rootNote: chordRootNote,
43
- chordName: chordRootNote + chord.quality,
44
- noteCollectionKey: getChordQualityNoteCollectionKey(chord.quality),
45
- }];
46
- });
55
+ /**
56
+ * Returns one chord reference for each authored chord entry in
57
+ * `progression.chords`.
58
+ *
59
+ * This is the "chord changes" view: a chord lasting 2 bars is returned once,
60
+ * because it is one chord entry/change, while repeated entries are preserved.
61
+ */
62
+ export function getChordProgressionChordChangeReferences(rootNote, progressionOrKey) {
63
+ return getResolvedChordProgressionChordReferences(rootNote, progressionOrKey).map(({ reference }) => reference);
47
64
  }
65
+ /**
66
+ * Returns each distinct chord reference once, preserving first-seen order from
67
+ * the chord-change list.
68
+ */
48
69
  export function getChordProgressionUniqueChordReferences(rootNote, progressionOrKey) {
49
70
  const uniqueReferences = [];
50
71
  const seen = new Set();
51
- for (const reference of getChordProgressionChordReferences(rootNote, progressionOrKey)) {
72
+ for (const reference of getChordProgressionChordChangeReferences(rootNote, progressionOrKey)) {
52
73
  const key = `${reference.rootNote}:${reference.noteCollectionKey}:${reference.chordName}`;
53
74
  if (seen.has(key))
54
75
  continue;
@@ -57,6 +78,51 @@ export function getChordProgressionUniqueChordReferences(rootNote, progressionOr
57
78
  }
58
79
  return uniqueReferences;
59
80
  }
81
+ /**
82
+ * Returns duration-aware chord references grouped by bar.
83
+ *
84
+ * Each outer array item is one bar. A 2-bar chord appears in two bar entries,
85
+ * and a split bar can contain multiple chord references in order.
86
+ */
87
+ export function getChordProgressionChordReferencesByBar(rootNote, progressionOrKey) {
88
+ const bars = [];
89
+ let currentBar = [];
90
+ let remainingBarDuration = 1;
91
+ const pushCurrentBar = () => {
92
+ if (currentBar.length > 0) {
93
+ bars.push(currentBar);
94
+ }
95
+ currentBar = [];
96
+ remainingBarDuration = 1;
97
+ };
98
+ for (const { chord, reference } of getResolvedChordProgressionChordReferences(rootNote, progressionOrKey)) {
99
+ let remainingChordDuration = chord.durationInBars;
100
+ if (!Number.isFinite(remainingChordDuration) ||
101
+ remainingChordDuration <= BAR_DURATION_EPSILON) {
102
+ continue;
103
+ }
104
+ while (remainingChordDuration > BAR_DURATION_EPSILON) {
105
+ currentBar.push(reference);
106
+ const filledDuration = Math.min(remainingChordDuration, remainingBarDuration);
107
+ remainingChordDuration -= filledDuration;
108
+ remainingBarDuration -= filledDuration;
109
+ if (remainingBarDuration <= BAR_DURATION_EPSILON) {
110
+ pushCurrentBar();
111
+ }
112
+ }
113
+ }
114
+ pushCurrentBar();
115
+ return bars;
116
+ }
117
+ /**
118
+ * Returns duration-aware chord references in song/practice order.
119
+ *
120
+ * This flattens `getChordProgressionChordReferencesByBar`, so a 2-bar chord
121
+ * appears twice and split bars preserve their left-to-right order.
122
+ */
123
+ export function getChordProgressionSongChordReferences(rootNote, progressionOrKey) {
124
+ return getChordProgressionChordReferencesByBar(rootNote, progressionOrKey).flat();
125
+ }
60
126
  export function getChordProgressionTotalDurationInBars(progressionOrKey) {
61
127
  const progression = resolveProgression(progressionOrKey);
62
128
  if (!progression)
@@ -1,6 +1,6 @@
1
1
  import { type NoteCollectionKey } from "../data/note-collections/mod.js";
2
2
  import type { ChordQuality, RomanSeventhChord, RomanTriad, SeventhChord, Triad } from "../types/chords";
3
- import type { NoteCollectionKeyTransformOptions, RootAndNoteCollectionKeyTransformOptions } from "./intervals.js";
3
+ import { type NoteCollectionKeyTransformOptions, type RootAndNoteCollectionKeyTransformOptions } from "./intervals.js";
4
4
  import { type RootNote } from "../data/labels/note-labels.js";
5
5
  export declare function getRomanNumeralForScaleIndexAndChordQuality(scaleIndex: number, quality: ChordQuality): string | undefined;
6
6
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"chords.d.ts","sourceRoot":"","sources":["../../../src/src/utils/chords.ts"],"names":[],"mappings":"AAWA,OAAO,EACL,KAAK,iBAAiB,EAEvB,MAAM,iCAAiC,CAAC;AACzC,OAAO,KAAK,EACV,YAAY,EACZ,iBAAiB,EACjB,UAAU,EACV,YAAY,EACZ,KAAK,EACN,MAAM,iBAAiB,CAAC;AAmBzB,OAAO,KAAK,EACV,iCAAiC,EACjC,wCAAwC,EACzC,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EAEL,KAAK,QAAQ,EACd,MAAM,+BAA+B,CAAC;AAEvC,wBAAgB,2CAA2C,CACzD,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,YAAY,GACpB,MAAM,GAAG,SAAS,CAYpB;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,UAAU,EAAE,CAQ5D;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,YAAY,EAAE,GACvB,iBAAiB,EAAE,CAQrB;AA2LD;;GAEG;AACH,wBAAgB,6BAA6B,CAC3C,iBAAiB,EAAE,iBAAiB,EACpC,OAAO,GAAE,iCAAsC,GAC9C,CAAC,KAAK,GAAG,SAAS,CAAC,EAAE,CAMvB;AAED;;GAEG;AACH,wBAAgB,oCAAoC,CAClD,iBAAiB,EAAE,iBAAiB,EACpC,OAAO,GAAE,iCAAsC,GAC9C,CAAC,YAAY,GAAG,SAAS,CAAC,EAAE,CAM9B;AAED;;GAEG;AACH,wBAAgB,kCAAkC,CAChD,iBAAiB,EAAE,iBAAiB,EACpC,OAAO,GAAE,iCAAsC,GAC9C,CAAC,UAAU,GAAG,SAAS,CAAC,EAAE,CAO5B;AAED;;GAEG;AACH,wBAAgB,yCAAyC,CACvD,iBAAiB,EAAE,iBAAiB,EACpC,OAAO,GAAE,iCAAsC,GAC9C,CAAC,iBAAiB,GAAG,SAAS,CAAC,EAAE,CAOnC;AAED;;;GAGG;AACH,wBAAgB,oCAAoC,CAClD,QAAQ,EAAE,QAAQ,EAClB,iBAAiB,EAAE,iBAAiB,EACpC,OAAO,GAAE,wCAA6C,GACrD,CAAC,MAAM,GAAG,SAAS,CAAC,EAAE,CAsBxB;AAED;;;GAGG;AACH,wBAAgB,2CAA2C,CACzD,QAAQ,EAAE,QAAQ,EAClB,iBAAiB,EAAE,iBAAiB,EACpC,OAAO,GAAE,wCAA6C,GACrD,CAAC,MAAM,GAAG,SAAS,CAAC,EAAE,CAsBxB;AAED;;;;GAIG;AACH,wBAAgB,yCAAyC,CACvD,QAAQ,EAAE,QAAQ,EAClB,iBAAiB,EAAE,iBAAiB,EACpC,OAAO,GAAE,wCAA6C,GACrD,CAAC,UAAU,GAAG,SAAS,CAAC,EAAE,CAY5B;AAED;;;;GAIG;AACH,wBAAgB,gDAAgD,CAC9D,QAAQ,EAAE,QAAQ,EAClB,iBAAiB,EAAE,iBAAiB,EACpC,OAAO,GAAE,wCAA6C,GACrD,CAAC,iBAAiB,GAAG,SAAS,CAAC,EAAE,CAYnC"}
1
+ {"version":3,"file":"chords.d.ts","sourceRoot":"","sources":["../../../src/src/utils/chords.ts"],"names":[],"mappings":"AAWA,OAAO,EACL,KAAK,iBAAiB,EAEvB,MAAM,iCAAiC,CAAC;AACzC,OAAO,KAAK,EACV,YAAY,EACZ,iBAAiB,EACjB,UAAU,EACV,YAAY,EACZ,KAAK,EACN,MAAM,iBAAiB,CAAC;AAmBzB,OAAO,EAEL,KAAK,iCAAiC,EACtC,KAAK,wCAAwC,EAC9C,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EAEL,KAAK,QAAQ,EACd,MAAM,+BAA+B,CAAC;AAEvC,wBAAgB,2CAA2C,CACzD,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,YAAY,GACpB,MAAM,GAAG,SAAS,CAYpB;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,UAAU,EAAE,CAQ5D;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,YAAY,EAAE,GACvB,iBAAiB,EAAE,CAQrB;AA0KD;;GAEG;AACH,wBAAgB,6BAA6B,CAC3C,iBAAiB,EAAE,iBAAiB,EACpC,OAAO,GAAE,iCAAsC,GAC9C,CAAC,KAAK,GAAG,SAAS,CAAC,EAAE,CAMvB;AAED;;GAEG;AACH,wBAAgB,oCAAoC,CAClD,iBAAiB,EAAE,iBAAiB,EACpC,OAAO,GAAE,iCAAsC,GAC9C,CAAC,YAAY,GAAG,SAAS,CAAC,EAAE,CAM9B;AAED;;GAEG;AACH,wBAAgB,kCAAkC,CAChD,iBAAiB,EAAE,iBAAiB,EACpC,OAAO,GAAE,iCAAsC,GAC9C,CAAC,UAAU,GAAG,SAAS,CAAC,EAAE,CAO5B;AAED;;GAEG;AACH,wBAAgB,yCAAyC,CACvD,iBAAiB,EAAE,iBAAiB,EACpC,OAAO,GAAE,iCAAsC,GAC9C,CAAC,iBAAiB,GAAG,SAAS,CAAC,EAAE,CAOnC;AAED;;;GAGG;AACH,wBAAgB,oCAAoC,CAClD,QAAQ,EAAE,QAAQ,EAClB,iBAAiB,EAAE,iBAAiB,EACpC,OAAO,GAAE,wCAA6C,GACrD,CAAC,MAAM,GAAG,SAAS,CAAC,EAAE,CAsBxB;AAED;;;GAGG;AACH,wBAAgB,2CAA2C,CACzD,QAAQ,EAAE,QAAQ,EAClB,iBAAiB,EAAE,iBAAiB,EACpC,OAAO,GAAE,wCAA6C,GACrD,CAAC,MAAM,GAAG,SAAS,CAAC,EAAE,CAsBxB;AAED;;;;GAIG;AACH,wBAAgB,yCAAyC,CACvD,QAAQ,EAAE,QAAQ,EAClB,iBAAiB,EAAE,iBAAiB,EACpC,OAAO,GAAE,wCAA6C,GACrD,CAAC,UAAU,GAAG,SAAS,CAAC,EAAE,CAY5B;AAED;;;;GAIG;AACH,wBAAgB,gDAAgD,CAC9D,QAAQ,EAAE,QAAQ,EAClB,iBAAiB,EAAE,iBAAiB,EACpC,OAAO,GAAE,wCAA6C,GACrD,CAAC,iBAAiB,GAAG,SAAS,CAAC,EAAE,CAYnC"}
@@ -5,6 +5,7 @@ import { harmonicMinorModes, } from "../data/note-collections/harmonic-minor-mod
5
5
  import { melodicMinorModes, } from "../data/note-collections/melodic-minor-modes.js";
6
6
  import { intervalToIntegerMap, } from "../data/labels/note-labels.js";
7
7
  import { diatonicModes, } from "../data/note-collections/diatonic-modes.js";
8
+ import { isOctaveInterval, } from "./intervals.js";
8
9
  import { createChromaticTuple, normalizeChromaticIndex } from "./chromatic.js";
9
10
  import { getNoteNamesForRootAndNoteCollectionKey } from "./note-names.js";
10
11
  import { noteNameToIntegerMap, } from "../data/labels/note-labels.js";
@@ -68,7 +69,7 @@ function getChromaticArray(items, intervals) {
68
69
  undefined,
69
70
  ];
70
71
  intervals.forEach((interval, i) => {
71
- if (interval === "8" || interval === "♮8") {
72
+ if (isOctaveInterval(interval)) {
72
73
  return;
73
74
  }
74
75
  const semitones = intervalToIntegerMap.get(interval);
@@ -108,6 +109,14 @@ function getModeData(modeKey) {
108
109
  }
109
110
  return undefined;
110
111
  }
112
+ function getUndefinedChordPlaceholders(intervals, fillChromatic) {
113
+ if (fillChromatic) {
114
+ return createChromaticTuple(Array.from({ length: 12 }, () => undefined));
115
+ }
116
+ return intervals
117
+ .filter((interval) => !isOctaveInterval(interval))
118
+ .map(() => undefined);
119
+ }
111
120
  /**
112
121
  * A generic core helper function to evaluate and return chords for any given NoteCollectionKey.
113
122
  *
@@ -162,33 +171,9 @@ function getChordsForNoteCollectionKey(noteCollectionKey, options, extractChords
162
171
  // 2d. Otherwise, return the sequentially packed list.
163
172
  return applyRotations(processedChords, false);
164
173
  }
165
- // 3. If the collection is NOT a primary mode (e.g., a custom scale like Minor Pentatonic),
166
- // we cannot look up predefined chords. Instead, we fall back to its "most Similar Scale".
167
- const similarScaleKey = collection.mostSimilarScale;
168
- const similarData = getModeData(similarScaleKey);
169
- if (!similarData)
170
- return [];
171
- // 3a. Retrieve and rotate the chords for the similar scale exactly like step 2.
172
- const similarChords = rotateArrayLeft(extractChords(similarData), similarData.rotation);
173
- const similarProcessedChords = transformRoman
174
- ? transformRoman(similarChords)
175
- : similarChords;
176
- // 3b. Pluck out only the chords that align with the intervals actually present in the custom scale.
177
- // E.g. For Minor Pentatonic (intervals 1, b3, 4, 5, b7), we grab the 1st, 3rd, 4th, 5th, and 7th chords of Aeolian.
178
- const collectionChordsByInterval = collection.intervals
179
- .map((interval) => {
180
- const index = similarData.intervals.indexOf(interval);
181
- return index !== -1 ? similarProcessedChords[index] : undefined;
182
- });
183
- // Keep a placeholder-preserving array for fillChromatic;
184
- // otherwise a missing interval, such as the blues scale ♭5, shifts later chords.
185
- const collectionChords = collectionChordsByInterval
186
- .filter((chord) => chord !== undefined);
187
- // 3c. Format and return just like step 2.
188
- if (fillChromatic) {
189
- return applyRotations(getChromaticArray(collectionChordsByInterval, collection.intervals), true);
190
- }
191
- return applyRotations(collectionChords, false);
174
+ // 3. Collections without authored chord systems should not infer harmony from
175
+ // mostSimilarScale. Return undefined placeholders so array positions stay meaningful.
176
+ return applyRotations(getUndefinedChordPlaceholders(collection.intervals, fillChromatic), fillChromatic === true);
192
177
  }
193
178
  /**
194
179
  * Retrieves the triads for a given note collection key.
@@ -2,6 +2,7 @@ import { type CompoundInterval, type Interval, type IntervalQuality, type RootNo
2
2
  import type { ChromaticIndex, ChromaticTuple } from "../data/chromatic.js";
3
3
  import { type NoteCollectionKey } from "../data/note-collections/mod.js";
4
4
  export type ChromaticIntervalTuple = ChromaticTuple<Interval>;
5
+ export declare function isOctaveInterval(interval: Interval): boolean;
5
6
  /**
6
7
  * Removes octave intervals (such as "8" or "♮8") from a given list of intervals.
7
8
  * Highly useful for standardizing chord and scale definitions (scales conventionally include the octave,
@@ -1 +1 @@
1
- {"version":3,"file":"intervals.d.ts","sourceRoot":"","sources":["../../../src/src/utils/intervals.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"intervals.d.ts","sourceRoot":"","sources":["../../../src/src/utils/intervals.ts"],"names":[],"mappings":"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"}
@@ -6,6 +6,9 @@ import { normalizeAccidentalString } from "./accidentals.js";
6
6
  import { isValidNoteCollectionKey } from "./note-collections.js";
7
7
  import { createChromaticTuple, normalizeChromaticIndex } from "./chromatic.js";
8
8
  const INTERVAL_NUMBER_REGEX = /\d+$/;
9
+ export function isOctaveInterval(interval) {
10
+ return interval === "8" || interval === "♮8";
11
+ }
9
12
  /**
10
13
  * Removes octave intervals (such as "8" or "♮8") from a given list of intervals.
11
14
  * Highly useful for standardizing chord and scale definitions (scales conventionally include the octave,
@@ -15,7 +18,7 @@ const INTERVAL_NUMBER_REGEX = /\d+$/;
15
18
  * @returns A new array excluding any octave intervals.
16
19
  */
17
20
  export function filterOutOctaveIntervals(intervals) {
18
- return intervals.filter((i) => i !== "8" && i !== "♮8");
21
+ return intervals.filter((interval) => !isOctaveInterval(interval));
19
22
  }
20
23
  /**
21
24
  * Removes any intervals that mathematically resolve to the root note (0 modulo 12),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@musodojo/music-theory-data",
3
- "version": "29.0.0",
3
+ "version": "31.0.0",
4
4
  "description": "The musician-friendly TypeScript library for scales, modes, chords, and arpeggios.",
5
5
  "keywords": [
6
6
  "music",
@@ -12,7 +12,32 @@ export declare function getChordProgressionChordNames(rootNote: RootNote, progre
12
12
  export declare function getChordProgressionRomanSymbols(progressionOrKey: ChordProgression | ChordProgressionKey): string[];
13
13
  export declare function getChordProgressionKeysForTotalBars(totalBars: number): ChordProgressionKey[];
14
14
  export declare function getChordProgressionUniqueChordNames(rootNote: RootNote, progressionOrKey: ChordProgression | ChordProgressionKey): string[];
15
- export declare function getChordProgressionChordReferences(rootNote: RootNote, progressionOrKey: ChordProgression | ChordProgressionKey): ChordProgressionChordReference[];
15
+ /**
16
+ * Returns one chord reference for each authored chord entry in
17
+ * `progression.chords`.
18
+ *
19
+ * This is the "chord changes" view: a chord lasting 2 bars is returned once,
20
+ * because it is one chord entry/change, while repeated entries are preserved.
21
+ */
22
+ export declare function getChordProgressionChordChangeReferences(rootNote: RootNote, progressionOrKey: ChordProgression | ChordProgressionKey): ChordProgressionChordReference[];
23
+ /**
24
+ * Returns each distinct chord reference once, preserving first-seen order from
25
+ * the chord-change list.
26
+ */
16
27
  export declare function getChordProgressionUniqueChordReferences(rootNote: RootNote, progressionOrKey: ChordProgression | ChordProgressionKey): ChordProgressionChordReference[];
28
+ /**
29
+ * Returns duration-aware chord references grouped by bar.
30
+ *
31
+ * Each outer array item is one bar. A 2-bar chord appears in two bar entries,
32
+ * and a split bar can contain multiple chord references in order.
33
+ */
34
+ export declare function getChordProgressionChordReferencesByBar(rootNote: RootNote, progressionOrKey: ChordProgression | ChordProgressionKey): ChordProgressionChordReference[][];
35
+ /**
36
+ * Returns duration-aware chord references in song/practice order.
37
+ *
38
+ * This flattens `getChordProgressionChordReferencesByBar`, so a 2-bar chord
39
+ * appears twice and split bars preserve their left-to-right order.
40
+ */
41
+ export declare function getChordProgressionSongChordReferences(rootNote: RootNote, progressionOrKey: ChordProgression | ChordProgressionKey): ChordProgressionChordReference[];
17
42
  export declare function getChordProgressionTotalDurationInBars(progressionOrKey: ChordProgression | ChordProgressionKey): number;
18
43
  //# sourceMappingURL=chord-progressions.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"chord-progressions.d.ts","sourceRoot":"","sources":["../../../src/src/utils/chord-progressions.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,mBAAmB,EAEzB,MAAM,mCAAmC,CAAC;AAE3C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AACpE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,+BAA+B,CAAC;AAC9D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AAMzE,MAAM,WAAW,8BAA8B;IAC7C,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAC5B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,iBAAiB,EAAE,iBAAiB,CAAC;CAC/C;AAED,wBAAgB,0BAA0B,CACxC,GAAG,EAAE,MAAM,GACV,GAAG,IAAI,mBAAmB,CAE5B;AASD,wBAAgB,6BAA6B,CAC3C,QAAQ,EAAE,QAAQ,EAClB,gBAAgB,EAAE,gBAAgB,GAAG,mBAAmB,GACvD,MAAM,EAAE,CAYV;AAED,wBAAgB,+BAA+B,CAC7C,gBAAgB,EAAE,gBAAgB,GAAG,mBAAmB,GACvD,MAAM,EAAE,CAKV;AAED,wBAAgB,mCAAmC,CACjD,SAAS,EAAE,MAAM,GAChB,mBAAmB,EAAE,CAKvB;AAED,wBAAgB,mCAAmC,CACjD,QAAQ,EAAE,QAAQ,EAClB,gBAAgB,EAAE,gBAAgB,GAAG,mBAAmB,GACvD,MAAM,EAAE,CAIV;AAED,wBAAgB,kCAAkC,CAChD,QAAQ,EAAE,QAAQ,EAClB,gBAAgB,EAAE,gBAAgB,GAAG,mBAAmB,GACvD,8BAA8B,EAAE,CAmBlC;AAED,wBAAgB,wCAAwC,CACtD,QAAQ,EAAE,QAAQ,EAClB,gBAAgB,EAAE,gBAAgB,GAAG,mBAAmB,GACvD,8BAA8B,EAAE,CAkBlC;AAED,wBAAgB,sCAAsC,CACpD,gBAAgB,EAAE,gBAAgB,GAAG,mBAAmB,GACvD,MAAM,CAQR"}
1
+ {"version":3,"file":"chord-progressions.d.ts","sourceRoot":"","sources":["../../../src/src/utils/chord-progressions.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,mBAAmB,EAEzB,MAAM,mCAAmC,CAAC;AAE3C,OAAO,KAAK,EACV,gBAAgB,EAEjB,MAAM,6BAA6B,CAAC;AACrC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,+BAA+B,CAAC;AAC9D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AAMzE,MAAM,WAAW,8BAA8B;IAC7C,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAC5B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,iBAAiB,EAAE,iBAAiB,CAAC;CAC/C;AASD,wBAAgB,0BAA0B,CACxC,GAAG,EAAE,MAAM,GACV,GAAG,IAAI,mBAAmB,CAE5B;AA2CD,wBAAgB,6BAA6B,CAC3C,QAAQ,EAAE,QAAQ,EAClB,gBAAgB,EAAE,gBAAgB,GAAG,mBAAmB,GACvD,MAAM,EAAE,CAYV;AAED,wBAAgB,+BAA+B,CAC7C,gBAAgB,EAAE,gBAAgB,GAAG,mBAAmB,GACvD,MAAM,EAAE,CAKV;AAED,wBAAgB,mCAAmC,CACjD,SAAS,EAAE,MAAM,GAChB,mBAAmB,EAAE,CAKvB;AAED,wBAAgB,mCAAmC,CACjD,QAAQ,EAAE,QAAQ,EAClB,gBAAgB,EAAE,gBAAgB,GAAG,mBAAmB,GACvD,MAAM,EAAE,CAIV;AAED;;;;;;GAMG;AACH,wBAAgB,wCAAwC,CACtD,QAAQ,EAAE,QAAQ,EAClB,gBAAgB,EAAE,gBAAgB,GAAG,mBAAmB,GACvD,8BAA8B,EAAE,CAKlC;AAED;;;GAGG;AACH,wBAAgB,wCAAwC,CACtD,QAAQ,EAAE,QAAQ,EAClB,gBAAgB,EAAE,gBAAgB,GAAG,mBAAmB,GACvD,8BAA8B,EAAE,CAkBlC;AAED;;;;;GAKG;AACH,wBAAgB,uCAAuC,CACrD,QAAQ,EAAE,QAAQ,EAClB,gBAAgB,EAAE,gBAAgB,GAAG,mBAAmB,GACvD,8BAA8B,EAAE,EAAE,CAiDpC;AAED;;;;;GAKG;AACH,wBAAgB,sCAAsC,CACpD,QAAQ,EAAE,QAAQ,EAClB,gBAAgB,EAAE,gBAAgB,GAAG,mBAAmB,GACvD,8BAA8B,EAAE,CAKlC;AAED,wBAAgB,sCAAsC,CACpD,gBAAgB,EAAE,gBAAgB,GAAG,mBAAmB,GACvD,MAAM,CAQR"}
@@ -5,12 +5,15 @@ exports.getChordProgressionChordNames = getChordProgressionChordNames;
5
5
  exports.getChordProgressionRomanSymbols = getChordProgressionRomanSymbols;
6
6
  exports.getChordProgressionKeysForTotalBars = getChordProgressionKeysForTotalBars;
7
7
  exports.getChordProgressionUniqueChordNames = getChordProgressionUniqueChordNames;
8
- exports.getChordProgressionChordReferences = getChordProgressionChordReferences;
8
+ exports.getChordProgressionChordChangeReferences = getChordProgressionChordChangeReferences;
9
9
  exports.getChordProgressionUniqueChordReferences = getChordProgressionUniqueChordReferences;
10
+ exports.getChordProgressionChordReferencesByBar = getChordProgressionChordReferencesByBar;
11
+ exports.getChordProgressionSongChordReferences = getChordProgressionSongChordReferences;
10
12
  exports.getChordProgressionTotalDurationInBars = getChordProgressionTotalDurationInBars;
11
13
  const mod_js_1 = require("../data/chord-progressions/mod.js");
12
14
  const mod_js_2 = require("../data/chords/mod.js");
13
15
  const note_names_js_1 = require("./note-names.js");
16
+ const BAR_DURATION_EPSILON = 0.000000001;
14
17
  function isValidChordProgressionKey(key) {
15
18
  return Object.prototype.hasOwnProperty.call(mod_js_1.chordProgressions, key);
16
19
  }
@@ -19,6 +22,28 @@ function resolveProgression(progressionOrKey) {
19
22
  return progressionOrKey;
20
23
  return mod_js_1.chordProgressions[progressionOrKey];
21
24
  }
25
+ function createChordProgressionChordReference(chord, chordRootNote) {
26
+ return {
27
+ rootNote: chordRootNote,
28
+ chordName: chordRootNote + chord.quality,
29
+ noteCollectionKey: (0, mod_js_2.getChordQualityNoteCollectionKey)(chord.quality),
30
+ };
31
+ }
32
+ function getResolvedChordProgressionChordReferences(rootNote, progressionOrKey) {
33
+ const progression = resolveProgression(progressionOrKey);
34
+ if (!progression)
35
+ return [];
36
+ const chordRootNotes = (0, note_names_js_1.getNoteNamesForRootAndIntervals)(rootNote, progression.chords.map((chord) => chord.degree)).map((noteName) => (0, note_names_js_1.normalizeRootNoteString)(noteName));
37
+ return progression.chords.flatMap((chord, index) => {
38
+ const chordRootNote = chordRootNotes[index];
39
+ if (!chordRootNote)
40
+ return [];
41
+ return [{
42
+ chord,
43
+ reference: createChordProgressionChordReference(chord, chordRootNote),
44
+ }];
45
+ });
46
+ }
22
47
  function getChordProgressionChordNames(rootNote, progressionOrKey) {
23
48
  const progression = resolveProgression(progressionOrKey);
24
49
  if (!progression)
@@ -39,26 +64,24 @@ function getChordProgressionKeysForTotalBars(totalBars) {
39
64
  function getChordProgressionUniqueChordNames(rootNote, progressionOrKey) {
40
65
  return Array.from(new Set(getChordProgressionChordNames(rootNote, progressionOrKey)));
41
66
  }
42
- function getChordProgressionChordReferences(rootNote, progressionOrKey) {
43
- const progression = resolveProgression(progressionOrKey);
44
- if (!progression)
45
- return [];
46
- const chordRootNotes = (0, note_names_js_1.getNoteNamesForRootAndIntervals)(rootNote, progression.chords.map((chord) => chord.degree)).map((noteName) => (0, note_names_js_1.normalizeRootNoteString)(noteName));
47
- return progression.chords.flatMap((chord, index) => {
48
- const chordRootNote = chordRootNotes[index];
49
- if (!chordRootNote)
50
- return [];
51
- return [{
52
- rootNote: chordRootNote,
53
- chordName: chordRootNote + chord.quality,
54
- noteCollectionKey: (0, mod_js_2.getChordQualityNoteCollectionKey)(chord.quality),
55
- }];
56
- });
67
+ /**
68
+ * Returns one chord reference for each authored chord entry in
69
+ * `progression.chords`.
70
+ *
71
+ * This is the "chord changes" view: a chord lasting 2 bars is returned once,
72
+ * because it is one chord entry/change, while repeated entries are preserved.
73
+ */
74
+ function getChordProgressionChordChangeReferences(rootNote, progressionOrKey) {
75
+ return getResolvedChordProgressionChordReferences(rootNote, progressionOrKey).map(({ reference }) => reference);
57
76
  }
77
+ /**
78
+ * Returns each distinct chord reference once, preserving first-seen order from
79
+ * the chord-change list.
80
+ */
58
81
  function getChordProgressionUniqueChordReferences(rootNote, progressionOrKey) {
59
82
  const uniqueReferences = [];
60
83
  const seen = new Set();
61
- for (const reference of getChordProgressionChordReferences(rootNote, progressionOrKey)) {
84
+ for (const reference of getChordProgressionChordChangeReferences(rootNote, progressionOrKey)) {
62
85
  const key = `${reference.rootNote}:${reference.noteCollectionKey}:${reference.chordName}`;
63
86
  if (seen.has(key))
64
87
  continue;
@@ -67,6 +90,51 @@ function getChordProgressionUniqueChordReferences(rootNote, progressionOrKey) {
67
90
  }
68
91
  return uniqueReferences;
69
92
  }
93
+ /**
94
+ * Returns duration-aware chord references grouped by bar.
95
+ *
96
+ * Each outer array item is one bar. A 2-bar chord appears in two bar entries,
97
+ * and a split bar can contain multiple chord references in order.
98
+ */
99
+ function getChordProgressionChordReferencesByBar(rootNote, progressionOrKey) {
100
+ const bars = [];
101
+ let currentBar = [];
102
+ let remainingBarDuration = 1;
103
+ const pushCurrentBar = () => {
104
+ if (currentBar.length > 0) {
105
+ bars.push(currentBar);
106
+ }
107
+ currentBar = [];
108
+ remainingBarDuration = 1;
109
+ };
110
+ for (const { chord, reference } of getResolvedChordProgressionChordReferences(rootNote, progressionOrKey)) {
111
+ let remainingChordDuration = chord.durationInBars;
112
+ if (!Number.isFinite(remainingChordDuration) ||
113
+ remainingChordDuration <= BAR_DURATION_EPSILON) {
114
+ continue;
115
+ }
116
+ while (remainingChordDuration > BAR_DURATION_EPSILON) {
117
+ currentBar.push(reference);
118
+ const filledDuration = Math.min(remainingChordDuration, remainingBarDuration);
119
+ remainingChordDuration -= filledDuration;
120
+ remainingBarDuration -= filledDuration;
121
+ if (remainingBarDuration <= BAR_DURATION_EPSILON) {
122
+ pushCurrentBar();
123
+ }
124
+ }
125
+ }
126
+ pushCurrentBar();
127
+ return bars;
128
+ }
129
+ /**
130
+ * Returns duration-aware chord references in song/practice order.
131
+ *
132
+ * This flattens `getChordProgressionChordReferencesByBar`, so a 2-bar chord
133
+ * appears twice and split bars preserve their left-to-right order.
134
+ */
135
+ function getChordProgressionSongChordReferences(rootNote, progressionOrKey) {
136
+ return getChordProgressionChordReferencesByBar(rootNote, progressionOrKey).flat();
137
+ }
70
138
  function getChordProgressionTotalDurationInBars(progressionOrKey) {
71
139
  const progression = resolveProgression(progressionOrKey);
72
140
  if (!progression)
@@ -1,6 +1,6 @@
1
1
  import { type NoteCollectionKey } from "../data/note-collections/mod.js";
2
2
  import type { ChordQuality, RomanSeventhChord, RomanTriad, SeventhChord, Triad } from "../types/chords";
3
- import type { NoteCollectionKeyTransformOptions, RootAndNoteCollectionKeyTransformOptions } from "./intervals.js";
3
+ import { type NoteCollectionKeyTransformOptions, type RootAndNoteCollectionKeyTransformOptions } from "./intervals.js";
4
4
  import { type RootNote } from "../data/labels/note-labels.js";
5
5
  export declare function getRomanNumeralForScaleIndexAndChordQuality(scaleIndex: number, quality: ChordQuality): string | undefined;
6
6
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"chords.d.ts","sourceRoot":"","sources":["../../../src/src/utils/chords.ts"],"names":[],"mappings":"AAWA,OAAO,EACL,KAAK,iBAAiB,EAEvB,MAAM,iCAAiC,CAAC;AACzC,OAAO,KAAK,EACV,YAAY,EACZ,iBAAiB,EACjB,UAAU,EACV,YAAY,EACZ,KAAK,EACN,MAAM,iBAAiB,CAAC;AAmBzB,OAAO,KAAK,EACV,iCAAiC,EACjC,wCAAwC,EACzC,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EAEL,KAAK,QAAQ,EACd,MAAM,+BAA+B,CAAC;AAEvC,wBAAgB,2CAA2C,CACzD,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,YAAY,GACpB,MAAM,GAAG,SAAS,CAYpB;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,UAAU,EAAE,CAQ5D;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,YAAY,EAAE,GACvB,iBAAiB,EAAE,CAQrB;AA2LD;;GAEG;AACH,wBAAgB,6BAA6B,CAC3C,iBAAiB,EAAE,iBAAiB,EACpC,OAAO,GAAE,iCAAsC,GAC9C,CAAC,KAAK,GAAG,SAAS,CAAC,EAAE,CAMvB;AAED;;GAEG;AACH,wBAAgB,oCAAoC,CAClD,iBAAiB,EAAE,iBAAiB,EACpC,OAAO,GAAE,iCAAsC,GAC9C,CAAC,YAAY,GAAG,SAAS,CAAC,EAAE,CAM9B;AAED;;GAEG;AACH,wBAAgB,kCAAkC,CAChD,iBAAiB,EAAE,iBAAiB,EACpC,OAAO,GAAE,iCAAsC,GAC9C,CAAC,UAAU,GAAG,SAAS,CAAC,EAAE,CAO5B;AAED;;GAEG;AACH,wBAAgB,yCAAyC,CACvD,iBAAiB,EAAE,iBAAiB,EACpC,OAAO,GAAE,iCAAsC,GAC9C,CAAC,iBAAiB,GAAG,SAAS,CAAC,EAAE,CAOnC;AAED;;;GAGG;AACH,wBAAgB,oCAAoC,CAClD,QAAQ,EAAE,QAAQ,EAClB,iBAAiB,EAAE,iBAAiB,EACpC,OAAO,GAAE,wCAA6C,GACrD,CAAC,MAAM,GAAG,SAAS,CAAC,EAAE,CAsBxB;AAED;;;GAGG;AACH,wBAAgB,2CAA2C,CACzD,QAAQ,EAAE,QAAQ,EAClB,iBAAiB,EAAE,iBAAiB,EACpC,OAAO,GAAE,wCAA6C,GACrD,CAAC,MAAM,GAAG,SAAS,CAAC,EAAE,CAsBxB;AAED;;;;GAIG;AACH,wBAAgB,yCAAyC,CACvD,QAAQ,EAAE,QAAQ,EAClB,iBAAiB,EAAE,iBAAiB,EACpC,OAAO,GAAE,wCAA6C,GACrD,CAAC,UAAU,GAAG,SAAS,CAAC,EAAE,CAY5B;AAED;;;;GAIG;AACH,wBAAgB,gDAAgD,CAC9D,QAAQ,EAAE,QAAQ,EAClB,iBAAiB,EAAE,iBAAiB,EACpC,OAAO,GAAE,wCAA6C,GACrD,CAAC,iBAAiB,GAAG,SAAS,CAAC,EAAE,CAYnC"}
1
+ {"version":3,"file":"chords.d.ts","sourceRoot":"","sources":["../../../src/src/utils/chords.ts"],"names":[],"mappings":"AAWA,OAAO,EACL,KAAK,iBAAiB,EAEvB,MAAM,iCAAiC,CAAC;AACzC,OAAO,KAAK,EACV,YAAY,EACZ,iBAAiB,EACjB,UAAU,EACV,YAAY,EACZ,KAAK,EACN,MAAM,iBAAiB,CAAC;AAmBzB,OAAO,EAEL,KAAK,iCAAiC,EACtC,KAAK,wCAAwC,EAC9C,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EAEL,KAAK,QAAQ,EACd,MAAM,+BAA+B,CAAC;AAEvC,wBAAgB,2CAA2C,CACzD,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,YAAY,GACpB,MAAM,GAAG,SAAS,CAYpB;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,UAAU,EAAE,CAQ5D;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,YAAY,EAAE,GACvB,iBAAiB,EAAE,CAQrB;AA0KD;;GAEG;AACH,wBAAgB,6BAA6B,CAC3C,iBAAiB,EAAE,iBAAiB,EACpC,OAAO,GAAE,iCAAsC,GAC9C,CAAC,KAAK,GAAG,SAAS,CAAC,EAAE,CAMvB;AAED;;GAEG;AACH,wBAAgB,oCAAoC,CAClD,iBAAiB,EAAE,iBAAiB,EACpC,OAAO,GAAE,iCAAsC,GAC9C,CAAC,YAAY,GAAG,SAAS,CAAC,EAAE,CAM9B;AAED;;GAEG;AACH,wBAAgB,kCAAkC,CAChD,iBAAiB,EAAE,iBAAiB,EACpC,OAAO,GAAE,iCAAsC,GAC9C,CAAC,UAAU,GAAG,SAAS,CAAC,EAAE,CAO5B;AAED;;GAEG;AACH,wBAAgB,yCAAyC,CACvD,iBAAiB,EAAE,iBAAiB,EACpC,OAAO,GAAE,iCAAsC,GAC9C,CAAC,iBAAiB,GAAG,SAAS,CAAC,EAAE,CAOnC;AAED;;;GAGG;AACH,wBAAgB,oCAAoC,CAClD,QAAQ,EAAE,QAAQ,EAClB,iBAAiB,EAAE,iBAAiB,EACpC,OAAO,GAAE,wCAA6C,GACrD,CAAC,MAAM,GAAG,SAAS,CAAC,EAAE,CAsBxB;AAED;;;GAGG;AACH,wBAAgB,2CAA2C,CACzD,QAAQ,EAAE,QAAQ,EAClB,iBAAiB,EAAE,iBAAiB,EACpC,OAAO,GAAE,wCAA6C,GACrD,CAAC,MAAM,GAAG,SAAS,CAAC,EAAE,CAsBxB;AAED;;;;GAIG;AACH,wBAAgB,yCAAyC,CACvD,QAAQ,EAAE,QAAQ,EAClB,iBAAiB,EAAE,iBAAiB,EACpC,OAAO,GAAE,wCAA6C,GACrD,CAAC,UAAU,GAAG,SAAS,CAAC,EAAE,CAY5B;AAED;;;;GAIG;AACH,wBAAgB,gDAAgD,CAC9D,QAAQ,EAAE,QAAQ,EAClB,iBAAiB,EAAE,iBAAiB,EACpC,OAAO,GAAE,wCAA6C,GACrD,CAAC,iBAAiB,GAAG,SAAS,CAAC,EAAE,CAYnC"}
@@ -18,6 +18,7 @@ const harmonic_minor_modes_js_1 = require("../data/note-collections/harmonic-min
18
18
  const melodic_minor_modes_js_1 = require("../data/note-collections/melodic-minor-modes.js");
19
19
  const note_labels_js_1 = require("../data/labels/note-labels.js");
20
20
  const diatonic_modes_js_1 = require("../data/note-collections/diatonic-modes.js");
21
+ const intervals_js_1 = require("./intervals.js");
21
22
  const chromatic_js_1 = require("./chromatic.js");
22
23
  const note_names_js_1 = require("./note-names.js");
23
24
  const note_labels_js_2 = require("../data/labels/note-labels.js");
@@ -81,7 +82,7 @@ function getChromaticArray(items, intervals) {
81
82
  undefined,
82
83
  ];
83
84
  intervals.forEach((interval, i) => {
84
- if (interval === "8" || interval === "♮8") {
85
+ if ((0, intervals_js_1.isOctaveInterval)(interval)) {
85
86
  return;
86
87
  }
87
88
  const semitones = note_labels_js_1.intervalToIntegerMap.get(interval);
@@ -121,6 +122,14 @@ function getModeData(modeKey) {
121
122
  }
122
123
  return undefined;
123
124
  }
125
+ function getUndefinedChordPlaceholders(intervals, fillChromatic) {
126
+ if (fillChromatic) {
127
+ return (0, chromatic_js_1.createChromaticTuple)(Array.from({ length: 12 }, () => undefined));
128
+ }
129
+ return intervals
130
+ .filter((interval) => !(0, intervals_js_1.isOctaveInterval)(interval))
131
+ .map(() => undefined);
132
+ }
124
133
  /**
125
134
  * A generic core helper function to evaluate and return chords for any given NoteCollectionKey.
126
135
  *
@@ -175,33 +184,9 @@ function getChordsForNoteCollectionKey(noteCollectionKey, options, extractChords
175
184
  // 2d. Otherwise, return the sequentially packed list.
176
185
  return applyRotations(processedChords, false);
177
186
  }
178
- // 3. If the collection is NOT a primary mode (e.g., a custom scale like Minor Pentatonic),
179
- // we cannot look up predefined chords. Instead, we fall back to its "most Similar Scale".
180
- const similarScaleKey = collection.mostSimilarScale;
181
- const similarData = getModeData(similarScaleKey);
182
- if (!similarData)
183
- return [];
184
- // 3a. Retrieve and rotate the chords for the similar scale exactly like step 2.
185
- const similarChords = (0, rotate_array_js_1.rotateArrayLeft)(extractChords(similarData), similarData.rotation);
186
- const similarProcessedChords = transformRoman
187
- ? transformRoman(similarChords)
188
- : similarChords;
189
- // 3b. Pluck out only the chords that align with the intervals actually present in the custom scale.
190
- // E.g. For Minor Pentatonic (intervals 1, b3, 4, 5, b7), we grab the 1st, 3rd, 4th, 5th, and 7th chords of Aeolian.
191
- const collectionChordsByInterval = collection.intervals
192
- .map((interval) => {
193
- const index = similarData.intervals.indexOf(interval);
194
- return index !== -1 ? similarProcessedChords[index] : undefined;
195
- });
196
- // Keep a placeholder-preserving array for fillChromatic;
197
- // otherwise a missing interval, such as the blues scale ♭5, shifts later chords.
198
- const collectionChords = collectionChordsByInterval
199
- .filter((chord) => chord !== undefined);
200
- // 3c. Format and return just like step 2.
201
- if (fillChromatic) {
202
- return applyRotations(getChromaticArray(collectionChordsByInterval, collection.intervals), true);
203
- }
204
- return applyRotations(collectionChords, false);
187
+ // 3. Collections without authored chord systems should not infer harmony from
188
+ // mostSimilarScale. Return undefined placeholders so array positions stay meaningful.
189
+ return applyRotations(getUndefinedChordPlaceholders(collection.intervals, fillChromatic), fillChromatic === true);
205
190
  }
206
191
  /**
207
192
  * Retrieves the triads for a given note collection key.
@@ -2,6 +2,7 @@ import { type CompoundInterval, type Interval, type IntervalQuality, type RootNo
2
2
  import type { ChromaticIndex, ChromaticTuple } from "../data/chromatic.js";
3
3
  import { type NoteCollectionKey } from "../data/note-collections/mod.js";
4
4
  export type ChromaticIntervalTuple = ChromaticTuple<Interval>;
5
+ export declare function isOctaveInterval(interval: Interval): boolean;
5
6
  /**
6
7
  * Removes octave intervals (such as "8" or "♮8") from a given list of intervals.
7
8
  * Highly useful for standardizing chord and scale definitions (scales conventionally include the octave,
@@ -1 +1 @@
1
- {"version":3,"file":"intervals.d.ts","sourceRoot":"","sources":["../../../src/src/utils/intervals.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,gBAAgB,EAGrB,KAAK,QAAQ,EACb,KAAK,eAAe,EAIpB,KAAK,QAAQ,EACb,KAAK,cAAc,EAGpB,MAAM,+BAA+B,CAAC;AACvC,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3E,OAAO,EACL,KAAK,iBAAiB,EAEvB,MAAM,iCAAiC,CAAC;AASzC,MAAM,MAAM,sBAAsB,GAAG,cAAc,CAAC,QAAQ,CAAC,CAAC;AAE9D;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,CACtC,SAAS,EAAE,SAAS,QAAQ,EAAE,GAC7B,QAAQ,EAAE,CAEZ;AAED;;;;;;;;GAQG;AACH,wBAAgB,0BAA0B,CACxC,SAAS,EAAE,SAAS,QAAQ,EAAE,GAC7B,QAAQ,EAAE,CAuBZ;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAmC1E;AAED;;;;;GAKG;AACH,wBAAgB,4BAA4B,CAC1C,KAAK,EAAE,SAAS,MAAM,EAAE,GACvB,QAAQ,EAAE,CAIZ;AAED;;GAEG;AACH,wBAAgB,6BAA6B,CAC3C,IAAI,EAAE,MAAM,GACX,cAAc,GAAG,SAAS,CAU5B;AAED;;GAEG;AACH,wBAAgB,kCAAkC,CAChD,KAAK,EAAE,SAAS,MAAM,EAAE,GACvB,cAAc,EAAE,CAIlB;AAED;;GAEG;AACH,wBAAgB,+BAA+B,CAC7C,IAAI,EAAE,MAAM,GACX,gBAAgB,GAAG,SAAS,CAU9B;AAED;;GAEG;AACH,wBAAgB,oCAAoC,CAClD,KAAK,EAAE,SAAS,MAAM,EAAE,GACvB,gBAAgB,EAAE,CAIpB;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,kEAAkE;AAClE,MAAM,WAAW,6BAA6B;IAC5C;;;OAGG;IACH,sBAAsB,CAAC,EAAE,sBAAsB,CAAC;IAChD;;;OAGG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,mFAAmF;AACnF,MAAM,WAAW,sCACf,SAAQ,6BAA6B;IACrC;;;;OAIG;IACH,aAAa,EAAE,IAAI,CAAC;IACpB;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,iBAAiB,CAAC;IACrC;;OAEG;IACH,eAAe,CAAC,EAAE,cAAc,CAAC;IACjC;;;;OAIG;IACH,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAChC;AAED,mFAAmF;AACnF,MAAM,WAAW,+BACf,SAAQ,6BAA6B;IACrC,aAAa,CAAC,EAAE,KAAK,CAAC;IACtB,gBAAgB,CAAC,EAAE,KAAK,CAAC;IACzB,eAAe,CAAC,EAAE,KAAK,CAAC;IACxB,oBAAoB,CAAC,EAAE,KAAK,CAAC;CAC9B;AAED,kFAAkF;AAClF,MAAM,MAAM,yBAAyB,GACjC,sCAAsC,GACtC,+BAA+B,CAAC;AAEpC,MAAM,MAAM,iCAAiC,GAAG,IAAI,CAClD,yBAAyB,EACvB,wBAAwB,GACxB,iBAAiB,GACjB,YAAY,GACZ,aAAa,GACb,eAAe,CAClB,CAAC;AAEF,MAAM,MAAM,wCAAwC,GAAG,IAAI,CACzD,yBAAyB,EACvB,wBAAwB,GACxB,iBAAiB,GACjB,YAAY,GACZ,aAAa,GACb,eAAe,GACf,iBAAiB,GACjB,sBAAsB,CACzB,CAAC;AAEF,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,SAAS,QAAQ,EAAE,EAC9B,OAAO,EAAE,sCAAsC,GAC9C,sBAAsB,CAAC;AAC1B,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,SAAS,QAAQ,EAAE,EAC9B,OAAO,CAAC,EAAE,yBAAyB,GAClC,QAAQ,EAAE,CAAC;AA+Fd;;;;;GAKG;AACH,wBAAgB,wBAAwB,CACtC,SAAS,EAAE,SAAS,eAAe,EAAE,GACpC,QAAQ,EAAE,CAKZ;AAED;;;;;;;GAOG;AACH,wBAAgB,gCAAgC,CAC9C,iBAAiB,EAAE,iBAAiB,EACpC,OAAO,EAAE,iCAAiC,GAAG;IAAE,aAAa,EAAE,IAAI,CAAA;CAAE,GACnE,sBAAsB,CAAC;AAC1B,wBAAgB,gCAAgC,CAC9C,iBAAiB,EAAE,iBAAiB,EACpC,OAAO,CAAC,EAAE,iCAAiC,GAC1C,QAAQ,EAAE,CAAC;AAuBd;;;;;;;GAOG;AACH,wBAAgB,iCAAiC,CAC/C,iBAAiB,EAAE,iBAAiB,EACpC,OAAO,EACH,IAAI,CACJ,iCAAiC,EACjC,wBAAwB,CACzB,GACC;IAAE,aAAa,EAAE,IAAI,CAAA;CAAE,GAC1B,sBAAsB,CAAC;AAC1B,wBAAgB,iCAAiC,CAC/C,iBAAiB,EAAE,iBAAiB,EACpC,OAAO,CAAC,EAAE,IAAI,CACZ,iCAAiC,EACjC,wBAAwB,CACzB,GACA,QAAQ,EAAE,CAAC;AAed;;;;;;;GAOG;AACH,wBAAgB,wCAAwC,CACtD,iBAAiB,EAAE,iBAAiB,EACpC,OAAO,EACH,IAAI,CACJ,iCAAiC,EACjC,wBAAwB,CACzB,GACC;IAAE,aAAa,EAAE,IAAI,CAAA;CAAE,GAC1B,sBAAsB,CAAC;AAC1B,wBAAgB,wCAAwC,CACtD,iBAAiB,EAAE,iBAAiB,EACpC,OAAO,CAAC,EAAE,IAAI,CACZ,iCAAiC,EACjC,wBAAwB,CACzB,GACA,QAAQ,EAAE,CAAC;AAed;;;;;;;;GAQG;AACH,wBAAgB,uCAAuC,CACrD,QAAQ,EAAE,QAAQ,EAClB,iBAAiB,EAAE,iBAAiB,EACpC,OAAO,EAAE,wCAAwC,GAAG;IAAE,aAAa,EAAE,IAAI,CAAA;CAAE,GAC1E,sBAAsB,CAAC;AAC1B,wBAAgB,uCAAuC,CACrD,QAAQ,EAAE,QAAQ,EAClB,iBAAiB,EAAE,iBAAiB,EACpC,OAAO,CAAC,EAAE,wCAAwC,GACjD,QAAQ,EAAE,CAAC;AA4Bd;;;;;;;;GAQG;AACH,wBAAgB,wCAAwC,CACtD,QAAQ,EAAE,QAAQ,EAClB,iBAAiB,EAAE,iBAAiB,EACpC,OAAO,EACH,IAAI,CACJ,wCAAwC,EACxC,wBAAwB,CACzB,GACC;IAAE,aAAa,EAAE,IAAI,CAAA;CAAE,GAC1B,sBAAsB,CAAC;AAC1B,wBAAgB,wCAAwC,CACtD,QAAQ,EAAE,QAAQ,EAClB,iBAAiB,EAAE,iBAAiB,EACpC,OAAO,CAAC,EAAE,IAAI,CACZ,wCAAwC,EACxC,wBAAwB,CACzB,GACA,QAAQ,EAAE,CAAC;AAgBd;;;;;;;;GAQG;AACH,wBAAgB,+CAA+C,CAC7D,QAAQ,EAAE,QAAQ,EAClB,iBAAiB,EAAE,iBAAiB,EACpC,OAAO,EACH,IAAI,CACJ,wCAAwC,EACxC,wBAAwB,CACzB,GACC;IAAE,aAAa,EAAE,IAAI,CAAA;CAAE,GAC1B,sBAAsB,CAAC;AAC1B,wBAAgB,+CAA+C,CAC7D,QAAQ,EAAE,QAAQ,EAClB,iBAAiB,EAAE,iBAAiB,EACpC,OAAO,CAAC,EAAE,IAAI,CACZ,wCAAwC,EACxC,wBAAwB,CACzB,GACA,QAAQ,EAAE,CAAC"}
1
+ {"version":3,"file":"intervals.d.ts","sourceRoot":"","sources":["../../../src/src/utils/intervals.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isOctaveInterval = isOctaveInterval;
3
4
  exports.filterOutOctaveIntervals = filterOutOctaveIntervals;
4
5
  exports.filterOutRootLikeIntervals = filterOutRootLikeIntervals;
5
6
  exports.normalizeIntervalString = normalizeIntervalString;
@@ -25,6 +26,9 @@ const accidentals_js_1 = require("./accidentals.js");
25
26
  const note_collections_js_1 = require("./note-collections.js");
26
27
  const chromatic_js_1 = require("./chromatic.js");
27
28
  const INTERVAL_NUMBER_REGEX = /\d+$/;
29
+ function isOctaveInterval(interval) {
30
+ return interval === "8" || interval === "♮8";
31
+ }
28
32
  /**
29
33
  * Removes octave intervals (such as "8" or "♮8") from a given list of intervals.
30
34
  * Highly useful for standardizing chord and scale definitions (scales conventionally include the octave,
@@ -34,7 +38,7 @@ const INTERVAL_NUMBER_REGEX = /\d+$/;
34
38
  * @returns A new array excluding any octave intervals.
35
39
  */
36
40
  function filterOutOctaveIntervals(intervals) {
37
- return intervals.filter((i) => i !== "8" && i !== "♮8");
41
+ return intervals.filter((interval) => !isOctaveInterval(interval));
38
42
  }
39
43
  /**
40
44
  * Removes any intervals that mathematically resolve to the root note (0 modulo 12),