@tonaljs/chord 4.9.0 → 4.10.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
@@ -79,6 +79,15 @@ c4m7(4); // => "Bb4"
79
79
  c4m7(1); // => "C5"
80
80
  ```
81
81
 
82
+ It can be used to find chord inversions:
83
+
84
+ ```js
85
+ [1, 2, 3, 4].map(chord); // => ["C4", "Eb4", "G4", "Bb4"]
86
+ [2, 3, 4, 5].map(chord); // => ["Eb4", "G4", "Bb4", "C5"]
87
+ [3, 4, 5, 6].map(chord); // => ["G4", "Bb4", "C5", "Eb5"]
88
+ [4, 5, 6, 7].map(chord); // => ["Bb4", "C5", "Eb5", "G5"]
89
+ ```
90
+
82
91
  Bear in mind that degree numbers starts with 1 and 0 returns an empty string:
83
92
 
84
93
  ```js
@@ -87,6 +96,17 @@ c4m7(0); // => ""
87
96
 
88
97
  See [`Scale.degrees`](https://github.com/tonaljs/tonal/tree/main/packages/scale#scaledegreesscalename-string--degree-number--string)
89
98
 
99
+ ### `Chord.steps(chordName: string) => (degree: number) => string`
100
+
101
+ Same as `Chord.degrees` but 0 is the tonic. Plays better with numeric ranges:
102
+
103
+ ```js
104
+ import { Range, Chord } from "tonal";
105
+
106
+ Range.numeric([-3, 3]).map(Chord.steps(["C4", "aug"]));
107
+ // => ["G#3", "E3", "C3", "C4", "E4", "G#4", "C5"]
108
+ ```
109
+
90
110
  #### `Chord.detect(notes: string[]) => string[]`
91
111
 
92
112
  Given a list of notes, get the possible chord names:
package/dist/index.d.ts CHANGED
@@ -83,9 +83,13 @@ declare function reduced(chordName: string): string[];
83
83
  *
84
84
  * @example
85
85
  * [1, 2, 3, 4].map(Chord.degrees("C")) => ["C", "E", "G", "C"]
86
- * [1, 2, 3, 4].map(Chord.degrees("C4")) => ["C4", "D4", "E4", "C5"]
86
+ * [1, 2, 3, 4].map(Chord.degrees("C4")) => ["C4", "E4", "G4", "C5"]
87
87
  */
88
- declare function degrees(scaleName: string): (degree: number) => string;
88
+ declare function degrees(chordName: string | ChordNameTokens): (degree: number) => string;
89
+ /**
90
+ * Sames as `degree` but with 0-based index
91
+ */
92
+ declare function steps(chordName: string | ChordNameTokens): (normalized: number) => string;
89
93
  declare const _default: {
90
94
  getChord: typeof getChord;
91
95
  get: typeof get;
@@ -96,7 +100,8 @@ declare const _default: {
96
100
  tokenize: typeof tokenize;
97
101
  transpose: typeof transpose;
98
102
  degrees: typeof degrees;
103
+ steps: typeof steps;
99
104
  chord: (this: unknown, ...args: unknown[]) => Chord;
100
105
  };
101
106
 
102
- export { Chord, chord, chordScales, _default as default, degrees, extended, get, getChord, reduced, tokenize, transpose };
107
+ export { Chord, chord, chordScales, _default as default, degrees, extended, get, getChord, reduced, steps, tokenize, transpose };
package/dist/index.js CHANGED
@@ -29,6 +29,7 @@ __export(chord_exports, {
29
29
  get: () => get,
30
30
  getChord: () => getChord,
31
31
  reduced: () => reduced,
32
+ steps: () => steps,
32
33
  tokenize: () => tokenize,
33
34
  transpose: () => transpose
34
35
  });
@@ -145,9 +146,14 @@ function reduced(chordName) {
145
146
  const isSubset = (0, import_pcset.isSubsetOf)(s.chroma);
146
147
  return (0, import_chord_type.all)().filter((chord2) => isSubset(chord2.chroma)).map((chord2) => s.tonic + chord2.aliases[0]);
147
148
  }
148
- function degrees(scaleName) {
149
- const chord2 = get(scaleName);
150
- return (0, import_core.transposeIntervalSetByDegree)(chord2.intervals, chord2.tonic ?? "");
149
+ function degrees(chordName) {
150
+ const { intervals, tonic } = get(chordName);
151
+ const transpose2 = (0, import_core.tonicIntervalsTransposer)(intervals, tonic);
152
+ return (degree) => degree ? transpose2(degree > 0 ? degree - 1 : degree) : "";
153
+ }
154
+ function steps(chordName) {
155
+ const { intervals, tonic } = get(chordName);
156
+ return (0, import_core.tonicIntervalsTransposer)(intervals, tonic);
151
157
  }
152
158
  var chord_default = {
153
159
  getChord,
@@ -159,6 +165,7 @@ var chord_default = {
159
165
  tokenize,
160
166
  transpose,
161
167
  degrees,
168
+ steps,
162
169
  chord
163
170
  };
164
171
  // Annotate the CommonJS export names for ESM import in node:
@@ -171,6 +178,7 @@ var chord_default = {
171
178
  get,
172
179
  getChord,
173
180
  reduced,
181
+ steps,
174
182
  tokenize,
175
183
  transpose
176
184
  });
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../index.ts"],"sourcesContent":["import { detect } from \"@tonaljs/chord-detect\";\nimport {\n all as chordTypes,\n ChordType,\n get as getChordType,\n} from \"@tonaljs/chord-type\";\nimport { transposeIntervalSetByDegree } from \"@tonaljs/core\";\n\nimport {\n deprecate,\n distance,\n note,\n NoteName,\n tokenizeNote,\n transpose as transposeNote,\n} from \"@tonaljs/core\";\n\nimport { isSubsetOf, isSupersetOf } from \"@tonaljs/pcset\";\n\nimport { all as scaleTypes } from \"@tonaljs/scale-type\";\nexport { detect } from \"@tonaljs/chord-detect\";\n\ntype ChordName = string;\ntype ChordNameTokens = [string, string]; // [TONIC, SCALE TYPE]\n\nexport interface Chord extends ChordType {\n tonic: string | null;\n type: string;\n root: string;\n rootDegree: number;\n symbol: string;\n notes: NoteName[];\n}\n\nconst NoChord: Chord = {\n empty: true,\n name: \"\",\n symbol: \"\",\n root: \"\",\n rootDegree: 0,\n type: \"\",\n tonic: null,\n setNum: NaN,\n quality: \"Unknown\",\n chroma: \"\",\n normalized: \"\",\n aliases: [],\n notes: [],\n intervals: [],\n};\n\n// 6, 64, 7, 9, 11 and 13 are consider part of the chord\n// (see https://github.com/danigb/tonal/issues/55)\nconst NUM_TYPES = /^(6|64|7|9|11|13)$/;\n/**\n * Tokenize a chord name. It returns an array with the tonic and chord type\n * If not tonic is found, all the name is considered the chord name.\n *\n * This function does NOT check if the chord type exists or not. It only tries\n * to split the tonic and chord type.\n *\n * @function\n * @param {string} name - the chord name\n * @return {Array} an array with [tonic, type]\n * @example\n * tokenize(\"Cmaj7\") // => [ \"C\", \"maj7\" ]\n * tokenize(\"C7\") // => [ \"C\", \"7\" ]\n * tokenize(\"mMaj7\") // => [ null, \"mMaj7\" ]\n * tokenize(\"Cnonsense\") // => [ null, \"nonsense\" ]\n */\nexport function tokenize(name: string): ChordNameTokens {\n const [letter, acc, oct, type] = tokenizeNote(name);\n if (letter === \"\") {\n return [\"\", name];\n }\n // aug is augmented (see https://github.com/danigb/tonal/issues/55)\n if (letter === \"A\" && type === \"ug\") {\n return [\"\", \"aug\"];\n }\n // see: https://github.com/tonaljs/tonal/issues/70\n if (!type && (oct === \"4\" || oct === \"5\")) {\n return [letter + acc, oct];\n }\n\n if (NUM_TYPES.test(oct)) {\n return [letter + acc, oct + type];\n } else {\n return [letter + acc + oct, type];\n }\n}\n\n/**\n * Get a Chord from a chord name.\n */\nexport function get(src: ChordName | ChordNameTokens): Chord {\n if (src === \"\") {\n return NoChord;\n }\n if (Array.isArray(src) && src.length === 2) {\n return getChord(src[1], src[0]);\n } else {\n const [tonic, type] = tokenize(src);\n const chord = getChord(type, tonic);\n return chord.empty ? getChord(src) : chord;\n }\n}\n\n/**\n * Get chord properties\n *\n * @param typeName - the chord type name\n * @param [tonic] - Optional tonic\n * @param [root] - Optional root (requires a tonic)\n */\nexport function getChord(\n typeName: string,\n optionalTonic?: string,\n optionalRoot?: string\n): Chord {\n const type = getChordType(typeName);\n const tonic = note(optionalTonic || \"\");\n const root = note(optionalRoot || \"\");\n\n if (\n type.empty ||\n (optionalTonic && tonic.empty) ||\n (optionalRoot && root.empty)\n ) {\n return NoChord;\n }\n\n const rootInterval = distance(tonic.pc, root.pc);\n const rootDegree = type.intervals.indexOf(rootInterval) + 1;\n if (!root.empty && !rootDegree) {\n return NoChord;\n }\n\n const intervals = Array.from(type.intervals);\n\n for (let i = 1; i < rootDegree; i++) {\n const num = intervals[0][0];\n const quality = intervals[0][1];\n const newNum = parseInt(num, 10) + 7;\n intervals.push(`${newNum}${quality}`);\n intervals.shift();\n }\n\n const notes = tonic.empty\n ? []\n : intervals.map((i) => transposeNote(tonic, i));\n\n typeName = type.aliases.indexOf(typeName) !== -1 ? typeName : type.aliases[0];\n const symbol = `${tonic.empty ? \"\" : tonic.pc}${typeName}${\n root.empty || rootDegree <= 1 ? \"\" : \"/\" + root.pc\n }`;\n const name = `${optionalTonic ? tonic.pc + \" \" : \"\"}${type.name}${\n rootDegree > 1 && optionalRoot ? \" over \" + root.pc : \"\"\n }`;\n return {\n ...type,\n name,\n symbol,\n type: type.name,\n root: root.name,\n intervals,\n rootDegree,\n tonic: tonic.name,\n notes,\n };\n}\n\nexport const chord = deprecate(\"Chord.chord\", \"Chord.get\", get);\n\n/**\n * Transpose a chord name\n *\n * @param {string} chordName - the chord name\n * @return {string} the transposed chord\n *\n * @example\n * transpose('Dm7', 'P4') // => 'Gm7\n */\nexport function transpose(chordName: string, interval: string): string {\n const [tonic, type] = tokenize(chordName);\n if (!tonic) {\n return chordName;\n }\n return transposeNote(tonic, interval) + type;\n}\n\n/**\n * Get all scales where the given chord fits\n *\n * @example\n * chordScales('C7b9')\n * // => [\"phrygian dominant\", \"flamenco\", \"spanish heptatonic\", \"half-whole diminished\", \"chromatic\"]\n */\nexport function chordScales(name: string): string[] {\n const s = get(name);\n const isChordIncluded = isSupersetOf(s.chroma);\n return scaleTypes()\n .filter((scale) => isChordIncluded(scale.chroma))\n .map((scale) => scale.name);\n}\n/**\n * Get all chords names that are a superset of the given one\n * (has the same notes and at least one more)\n *\n * @function\n * @example\n * extended(\"CMaj7\")\n * // => [ 'Cmaj#4', 'Cmaj7#9#11', 'Cmaj9', 'CM7add13', 'Cmaj13', 'Cmaj9#11', 'CM13#11', 'CM7b9' ]\n */\nexport function extended(chordName: string): string[] {\n const s = get(chordName);\n const isSuperset = isSupersetOf(s.chroma);\n return chordTypes()\n .filter((chord) => isSuperset(chord.chroma))\n .map((chord) => s.tonic + chord.aliases[0]);\n}\n\n/**\n * Find all chords names that are a subset of the given one\n * (has less notes but all from the given chord)\n *\n * @example\n */\nexport function reduced(chordName: string): string[] {\n const s = get(chordName);\n const isSubset = isSubsetOf(s.chroma);\n return chordTypes()\n .filter((chord) => isSubset(chord.chroma))\n .map((chord) => s.tonic + chord.aliases[0]);\n}\n\n/**\n * Returns a function to get a note name from the scale degree.\n *\n * @example\n * [1, 2, 3, 4].map(Chord.degrees(\"C\")) => [\"C\", \"E\", \"G\", \"C\"]\n * [1, 2, 3, 4].map(Chord.degrees(\"C4\")) => [\"C4\", \"D4\", \"E4\", \"C5\"]\n */\nexport function degrees(scaleName: string) {\n const chord = get(scaleName);\n return transposeIntervalSetByDegree(chord.intervals, chord.tonic ?? \"\");\n}\n\nexport default {\n getChord,\n get,\n detect,\n chordScales,\n extended,\n reduced,\n tokenize,\n transpose,\n degrees,\n // deprecate\n chord,\n};\n"],"mappings":"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","names":["import_core","import_chord_detect","chord","getChordType","transposeNote","scaleTypes","chordTypes"]}
1
+ {"version":3,"sources":["../index.ts"],"sourcesContent":["import { detect } from \"@tonaljs/chord-detect\";\nimport {\n all as chordTypes,\n ChordType,\n get as getChordType,\n} from \"@tonaljs/chord-type\";\nimport { tonicIntervalsTransposer } from \"@tonaljs/core\";\n\nimport {\n deprecate,\n distance,\n note,\n NoteName,\n tokenizeNote,\n transpose as transposeNote,\n} from \"@tonaljs/core\";\n\nimport { isSubsetOf, isSupersetOf } from \"@tonaljs/pcset\";\n\nimport { all as scaleTypes } from \"@tonaljs/scale-type\";\nexport { detect } from \"@tonaljs/chord-detect\";\n\ntype ChordName = string;\ntype ChordNameTokens = [string, string]; // [TONIC, SCALE TYPE]\n\nexport interface Chord extends ChordType {\n tonic: string | null;\n type: string;\n root: string;\n rootDegree: number;\n symbol: string;\n notes: NoteName[];\n}\n\nconst NoChord: Chord = {\n empty: true,\n name: \"\",\n symbol: \"\",\n root: \"\",\n rootDegree: 0,\n type: \"\",\n tonic: null,\n setNum: NaN,\n quality: \"Unknown\",\n chroma: \"\",\n normalized: \"\",\n aliases: [],\n notes: [],\n intervals: [],\n};\n\n// 6, 64, 7, 9, 11 and 13 are consider part of the chord\n// (see https://github.com/danigb/tonal/issues/55)\nconst NUM_TYPES = /^(6|64|7|9|11|13)$/;\n/**\n * Tokenize a chord name. It returns an array with the tonic and chord type\n * If not tonic is found, all the name is considered the chord name.\n *\n * This function does NOT check if the chord type exists or not. It only tries\n * to split the tonic and chord type.\n *\n * @function\n * @param {string} name - the chord name\n * @return {Array} an array with [tonic, type]\n * @example\n * tokenize(\"Cmaj7\") // => [ \"C\", \"maj7\" ]\n * tokenize(\"C7\") // => [ \"C\", \"7\" ]\n * tokenize(\"mMaj7\") // => [ null, \"mMaj7\" ]\n * tokenize(\"Cnonsense\") // => [ null, \"nonsense\" ]\n */\nexport function tokenize(name: string): ChordNameTokens {\n const [letter, acc, oct, type] = tokenizeNote(name);\n if (letter === \"\") {\n return [\"\", name];\n }\n // aug is augmented (see https://github.com/danigb/tonal/issues/55)\n if (letter === \"A\" && type === \"ug\") {\n return [\"\", \"aug\"];\n }\n // see: https://github.com/tonaljs/tonal/issues/70\n if (!type && (oct === \"4\" || oct === \"5\")) {\n return [letter + acc, oct];\n }\n\n if (NUM_TYPES.test(oct)) {\n return [letter + acc, oct + type];\n } else {\n return [letter + acc + oct, type];\n }\n}\n\n/**\n * Get a Chord from a chord name.\n */\nexport function get(src: ChordName | ChordNameTokens): Chord {\n if (src === \"\") {\n return NoChord;\n }\n if (Array.isArray(src) && src.length === 2) {\n return getChord(src[1], src[0]);\n } else {\n const [tonic, type] = tokenize(src);\n const chord = getChord(type, tonic);\n return chord.empty ? getChord(src) : chord;\n }\n}\n\n/**\n * Get chord properties\n *\n * @param typeName - the chord type name\n * @param [tonic] - Optional tonic\n * @param [root] - Optional root (requires a tonic)\n */\nexport function getChord(\n typeName: string,\n optionalTonic?: string,\n optionalRoot?: string\n): Chord {\n const type = getChordType(typeName);\n const tonic = note(optionalTonic || \"\");\n const root = note(optionalRoot || \"\");\n\n if (\n type.empty ||\n (optionalTonic && tonic.empty) ||\n (optionalRoot && root.empty)\n ) {\n return NoChord;\n }\n\n const rootInterval = distance(tonic.pc, root.pc);\n const rootDegree = type.intervals.indexOf(rootInterval) + 1;\n if (!root.empty && !rootDegree) {\n return NoChord;\n }\n\n const intervals = Array.from(type.intervals);\n\n for (let i = 1; i < rootDegree; i++) {\n const num = intervals[0][0];\n const quality = intervals[0][1];\n const newNum = parseInt(num, 10) + 7;\n intervals.push(`${newNum}${quality}`);\n intervals.shift();\n }\n\n const notes = tonic.empty\n ? []\n : intervals.map((i) => transposeNote(tonic, i));\n\n typeName = type.aliases.indexOf(typeName) !== -1 ? typeName : type.aliases[0];\n const symbol = `${tonic.empty ? \"\" : tonic.pc}${typeName}${\n root.empty || rootDegree <= 1 ? \"\" : \"/\" + root.pc\n }`;\n const name = `${optionalTonic ? tonic.pc + \" \" : \"\"}${type.name}${\n rootDegree > 1 && optionalRoot ? \" over \" + root.pc : \"\"\n }`;\n return {\n ...type,\n name,\n symbol,\n type: type.name,\n root: root.name,\n intervals,\n rootDegree,\n tonic: tonic.name,\n notes,\n };\n}\n\nexport const chord = deprecate(\"Chord.chord\", \"Chord.get\", get);\n\n/**\n * Transpose a chord name\n *\n * @param {string} chordName - the chord name\n * @return {string} the transposed chord\n *\n * @example\n * transpose('Dm7', 'P4') // => 'Gm7\n */\nexport function transpose(chordName: string, interval: string): string {\n const [tonic, type] = tokenize(chordName);\n if (!tonic) {\n return chordName;\n }\n return transposeNote(tonic, interval) + type;\n}\n\n/**\n * Get all scales where the given chord fits\n *\n * @example\n * chordScales('C7b9')\n * // => [\"phrygian dominant\", \"flamenco\", \"spanish heptatonic\", \"half-whole diminished\", \"chromatic\"]\n */\nexport function chordScales(name: string): string[] {\n const s = get(name);\n const isChordIncluded = isSupersetOf(s.chroma);\n return scaleTypes()\n .filter((scale) => isChordIncluded(scale.chroma))\n .map((scale) => scale.name);\n}\n/**\n * Get all chords names that are a superset of the given one\n * (has the same notes and at least one more)\n *\n * @function\n * @example\n * extended(\"CMaj7\")\n * // => [ 'Cmaj#4', 'Cmaj7#9#11', 'Cmaj9', 'CM7add13', 'Cmaj13', 'Cmaj9#11', 'CM13#11', 'CM7b9' ]\n */\nexport function extended(chordName: string): string[] {\n const s = get(chordName);\n const isSuperset = isSupersetOf(s.chroma);\n return chordTypes()\n .filter((chord) => isSuperset(chord.chroma))\n .map((chord) => s.tonic + chord.aliases[0]);\n}\n\n/**\n * Find all chords names that are a subset of the given one\n * (has less notes but all from the given chord)\n *\n * @example\n */\nexport function reduced(chordName: string): string[] {\n const s = get(chordName);\n const isSubset = isSubsetOf(s.chroma);\n return chordTypes()\n .filter((chord) => isSubset(chord.chroma))\n .map((chord) => s.tonic + chord.aliases[0]);\n}\n\n/**\n * Returns a function to get a note name from the scale degree.\n *\n * @example\n * [1, 2, 3, 4].map(Chord.degrees(\"C\")) => [\"C\", \"E\", \"G\", \"C\"]\n * [1, 2, 3, 4].map(Chord.degrees(\"C4\")) => [\"C4\", \"E4\", \"G4\", \"C5\"]\n */\nexport function degrees(chordName: string | ChordNameTokens) {\n const { intervals, tonic } = get(chordName);\n const transpose = tonicIntervalsTransposer(intervals, tonic);\n return (degree: number) =>\n degree ? transpose(degree > 0 ? degree - 1 : degree) : \"\";\n}\n\n/**\n * Sames as `degree` but with 0-based index\n */\nexport function steps(chordName: string | ChordNameTokens) {\n const { intervals, tonic } = get(chordName);\n return tonicIntervalsTransposer(intervals, tonic);\n}\n\nexport default {\n getChord,\n get,\n detect,\n chordScales,\n extended,\n reduced,\n tokenize,\n transpose,\n degrees,\n steps,\n\n // deprecate\n chord,\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,0BAAuB;AACvB,wBAIO;AACP,kBAAyC;AAEzC,IAAAA,eAOO;AAEP,mBAAyC;AAEzC,wBAAkC;AAClC,IAAAC,uBAAuB;AAcvB,IAAM,UAAiB;AAAA,EACrB,OAAO;AAAA,EACP,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,YAAY;AAAA,EACZ,MAAM;AAAA,EACN,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,YAAY;AAAA,EACZ,SAAS,CAAC;AAAA,EACV,OAAO,CAAC;AAAA,EACR,WAAW,CAAC;AACd;AAIA,IAAM,YAAY;AAiBX,SAAS,SAAS,MAA+B;AACtD,QAAM,CAAC,QAAQ,KAAK,KAAK,IAAI,QAAI,2BAAa,IAAI;AAClD,MAAI,WAAW,IAAI;AACjB,WAAO,CAAC,IAAI,IAAI;AAAA,EAClB;AAEA,MAAI,WAAW,OAAO,SAAS,MAAM;AACnC,WAAO,CAAC,IAAI,KAAK;AAAA,EACnB;AAEA,MAAI,CAAC,SAAS,QAAQ,OAAO,QAAQ,MAAM;AACzC,WAAO,CAAC,SAAS,KAAK,GAAG;AAAA,EAC3B;AAEA,MAAI,UAAU,KAAK,GAAG,GAAG;AACvB,WAAO,CAAC,SAAS,KAAK,MAAM,IAAI;AAAA,EAClC,OAAO;AACL,WAAO,CAAC,SAAS,MAAM,KAAK,IAAI;AAAA,EAClC;AACF;AAKO,SAAS,IAAI,KAAyC;AAC3D,MAAI,QAAQ,IAAI;AACd,WAAO;AAAA,EACT;AACA,MAAI,MAAM,QAAQ,GAAG,KAAK,IAAI,WAAW,GAAG;AAC1C,WAAO,SAAS,IAAI,IAAI,IAAI,EAAE;AAAA,EAChC,OAAO;AACL,UAAM,CAAC,OAAO,IAAI,IAAI,SAAS,GAAG;AAClC,UAAMC,SAAQ,SAAS,MAAM,KAAK;AAClC,WAAOA,OAAM,QAAQ,SAAS,GAAG,IAAIA;AAAA,EACvC;AACF;AASO,SAAS,SACd,UACA,eACA,cACO;AACP,QAAM,WAAO,kBAAAC,KAAa,QAAQ;AAClC,QAAM,YAAQ,mBAAK,iBAAiB,EAAE;AACtC,QAAM,WAAO,mBAAK,gBAAgB,EAAE;AAEpC,MACE,KAAK,SACJ,iBAAiB,MAAM,SACvB,gBAAgB,KAAK,OACtB;AACA,WAAO;AAAA,EACT;AAEA,QAAM,mBAAe,uBAAS,MAAM,IAAI,KAAK,EAAE;AAC/C,QAAM,aAAa,KAAK,UAAU,QAAQ,YAAY,IAAI;AAC1D,MAAI,CAAC,KAAK,SAAS,CAAC,YAAY;AAC9B,WAAO;AAAA,EACT;AAEA,QAAM,YAAY,MAAM,KAAK,KAAK,SAAS;AAE3C,WAAS,IAAI,GAAG,IAAI,YAAY,KAAK;AACnC,UAAM,MAAM,UAAU,GAAG;AACzB,UAAM,UAAU,UAAU,GAAG;AAC7B,UAAM,SAAS,SAAS,KAAK,EAAE,IAAI;AACnC,cAAU,KAAK,GAAG,SAAS,SAAS;AACpC,cAAU,MAAM;AAAA,EAClB;AAEA,QAAM,QAAQ,MAAM,QAChB,CAAC,IACD,UAAU,IAAI,CAAC,UAAM,aAAAC,WAAc,OAAO,CAAC,CAAC;AAEhD,aAAW,KAAK,QAAQ,QAAQ,QAAQ,MAAM,KAAK,WAAW,KAAK,QAAQ;AAC3E,QAAM,SAAS,GAAG,MAAM,QAAQ,KAAK,MAAM,KAAK,WAC9C,KAAK,SAAS,cAAc,IAAI,KAAK,MAAM,KAAK;AAElD,QAAM,OAAO,GAAG,gBAAgB,MAAM,KAAK,MAAM,KAAK,KAAK,OACzD,aAAa,KAAK,eAAe,WAAW,KAAK,KAAK;AAExD,SAAO;AAAA,IACL,GAAG;AAAA,IACH;AAAA,IACA;AAAA,IACA,MAAM,KAAK;AAAA,IACX,MAAM,KAAK;AAAA,IACX;AAAA,IACA;AAAA,IACA,OAAO,MAAM;AAAA,IACb;AAAA,EACF;AACF;AAEO,IAAM,YAAQ,wBAAU,eAAe,aAAa,GAAG;AAWvD,SAAS,UAAU,WAAmB,UAA0B;AACrE,QAAM,CAAC,OAAO,IAAI,IAAI,SAAS,SAAS;AACxC,MAAI,CAAC,OAAO;AACV,WAAO;AAAA,EACT;AACA,aAAO,aAAAA,WAAc,OAAO,QAAQ,IAAI;AAC1C;AASO,SAAS,YAAY,MAAwB;AAClD,QAAM,IAAI,IAAI,IAAI;AAClB,QAAM,sBAAkB,2BAAa,EAAE,MAAM;AAC7C,aAAO,kBAAAC,KAAW,EACf,OAAO,CAAC,UAAU,gBAAgB,MAAM,MAAM,CAAC,EAC/C,IAAI,CAAC,UAAU,MAAM,IAAI;AAC9B;AAUO,SAAS,SAAS,WAA6B;AACpD,QAAM,IAAI,IAAI,SAAS;AACvB,QAAM,iBAAa,2BAAa,EAAE,MAAM;AACxC,aAAO,kBAAAC,KAAW,EACf,OAAO,CAACJ,WAAU,WAAWA,OAAM,MAAM,CAAC,EAC1C,IAAI,CAACA,WAAU,EAAE,QAAQA,OAAM,QAAQ,EAAE;AAC9C;AAQO,SAAS,QAAQ,WAA6B;AACnD,QAAM,IAAI,IAAI,SAAS;AACvB,QAAM,eAAW,yBAAW,EAAE,MAAM;AACpC,aAAO,kBAAAI,KAAW,EACf,OAAO,CAACJ,WAAU,SAASA,OAAM,MAAM,CAAC,EACxC,IAAI,CAACA,WAAU,EAAE,QAAQA,OAAM,QAAQ,EAAE;AAC9C;AASO,SAAS,QAAQ,WAAqC;AAC3D,QAAM,EAAE,WAAW,MAAM,IAAI,IAAI,SAAS;AAC1C,QAAMK,iBAAY,sCAAyB,WAAW,KAAK;AAC3D,SAAO,CAAC,WACN,SAASA,WAAU,SAAS,IAAI,SAAS,IAAI,MAAM,IAAI;AAC3D;AAKO,SAAS,MAAM,WAAqC;AACzD,QAAM,EAAE,WAAW,MAAM,IAAI,IAAI,SAAS;AAC1C,aAAO,sCAAyB,WAAW,KAAK;AAClD;AAEA,IAAO,gBAAQ;AAAA,EACb;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAGA;AACF;","names":["import_core","import_chord_detect","chord","getChordType","transposeNote","scaleTypes","chordTypes","transpose"]}
package/dist/index.mjs CHANGED
@@ -4,7 +4,7 @@ import {
4
4
  all as chordTypes,
5
5
  get as getChordType
6
6
  } from "@tonaljs/chord-type";
7
- import { transposeIntervalSetByDegree } from "@tonaljs/core";
7
+ import { tonicIntervalsTransposer } from "@tonaljs/core";
8
8
  import {
9
9
  deprecate,
10
10
  distance,
@@ -120,9 +120,14 @@ function reduced(chordName) {
120
120
  const isSubset = isSubsetOf(s.chroma);
121
121
  return chordTypes().filter((chord2) => isSubset(chord2.chroma)).map((chord2) => s.tonic + chord2.aliases[0]);
122
122
  }
123
- function degrees(scaleName) {
124
- const chord2 = get(scaleName);
125
- return transposeIntervalSetByDegree(chord2.intervals, chord2.tonic ?? "");
123
+ function degrees(chordName) {
124
+ const { intervals, tonic } = get(chordName);
125
+ const transpose2 = tonicIntervalsTransposer(intervals, tonic);
126
+ return (degree) => degree ? transpose2(degree > 0 ? degree - 1 : degree) : "";
127
+ }
128
+ function steps(chordName) {
129
+ const { intervals, tonic } = get(chordName);
130
+ return tonicIntervalsTransposer(intervals, tonic);
126
131
  }
127
132
  var chord_default = {
128
133
  getChord,
@@ -134,6 +139,7 @@ var chord_default = {
134
139
  tokenize,
135
140
  transpose,
136
141
  degrees,
142
+ steps,
137
143
  chord
138
144
  };
139
145
  export {
@@ -146,6 +152,7 @@ export {
146
152
  get,
147
153
  getChord,
148
154
  reduced,
155
+ steps,
149
156
  tokenize,
150
157
  transpose
151
158
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../index.ts"],"sourcesContent":["import { detect } from \"@tonaljs/chord-detect\";\nimport {\n all as chordTypes,\n ChordType,\n get as getChordType,\n} from \"@tonaljs/chord-type\";\nimport { transposeIntervalSetByDegree } from \"@tonaljs/core\";\n\nimport {\n deprecate,\n distance,\n note,\n NoteName,\n tokenizeNote,\n transpose as transposeNote,\n} from \"@tonaljs/core\";\n\nimport { isSubsetOf, isSupersetOf } from \"@tonaljs/pcset\";\n\nimport { all as scaleTypes } from \"@tonaljs/scale-type\";\nexport { detect } from \"@tonaljs/chord-detect\";\n\ntype ChordName = string;\ntype ChordNameTokens = [string, string]; // [TONIC, SCALE TYPE]\n\nexport interface Chord extends ChordType {\n tonic: string | null;\n type: string;\n root: string;\n rootDegree: number;\n symbol: string;\n notes: NoteName[];\n}\n\nconst NoChord: Chord = {\n empty: true,\n name: \"\",\n symbol: \"\",\n root: \"\",\n rootDegree: 0,\n type: \"\",\n tonic: null,\n setNum: NaN,\n quality: \"Unknown\",\n chroma: \"\",\n normalized: \"\",\n aliases: [],\n notes: [],\n intervals: [],\n};\n\n// 6, 64, 7, 9, 11 and 13 are consider part of the chord\n// (see https://github.com/danigb/tonal/issues/55)\nconst NUM_TYPES = /^(6|64|7|9|11|13)$/;\n/**\n * Tokenize a chord name. It returns an array with the tonic and chord type\n * If not tonic is found, all the name is considered the chord name.\n *\n * This function does NOT check if the chord type exists or not. It only tries\n * to split the tonic and chord type.\n *\n * @function\n * @param {string} name - the chord name\n * @return {Array} an array with [tonic, type]\n * @example\n * tokenize(\"Cmaj7\") // => [ \"C\", \"maj7\" ]\n * tokenize(\"C7\") // => [ \"C\", \"7\" ]\n * tokenize(\"mMaj7\") // => [ null, \"mMaj7\" ]\n * tokenize(\"Cnonsense\") // => [ null, \"nonsense\" ]\n */\nexport function tokenize(name: string): ChordNameTokens {\n const [letter, acc, oct, type] = tokenizeNote(name);\n if (letter === \"\") {\n return [\"\", name];\n }\n // aug is augmented (see https://github.com/danigb/tonal/issues/55)\n if (letter === \"A\" && type === \"ug\") {\n return [\"\", \"aug\"];\n }\n // see: https://github.com/tonaljs/tonal/issues/70\n if (!type && (oct === \"4\" || oct === \"5\")) {\n return [letter + acc, oct];\n }\n\n if (NUM_TYPES.test(oct)) {\n return [letter + acc, oct + type];\n } else {\n return [letter + acc + oct, type];\n }\n}\n\n/**\n * Get a Chord from a chord name.\n */\nexport function get(src: ChordName | ChordNameTokens): Chord {\n if (src === \"\") {\n return NoChord;\n }\n if (Array.isArray(src) && src.length === 2) {\n return getChord(src[1], src[0]);\n } else {\n const [tonic, type] = tokenize(src);\n const chord = getChord(type, tonic);\n return chord.empty ? getChord(src) : chord;\n }\n}\n\n/**\n * Get chord properties\n *\n * @param typeName - the chord type name\n * @param [tonic] - Optional tonic\n * @param [root] - Optional root (requires a tonic)\n */\nexport function getChord(\n typeName: string,\n optionalTonic?: string,\n optionalRoot?: string\n): Chord {\n const type = getChordType(typeName);\n const tonic = note(optionalTonic || \"\");\n const root = note(optionalRoot || \"\");\n\n if (\n type.empty ||\n (optionalTonic && tonic.empty) ||\n (optionalRoot && root.empty)\n ) {\n return NoChord;\n }\n\n const rootInterval = distance(tonic.pc, root.pc);\n const rootDegree = type.intervals.indexOf(rootInterval) + 1;\n if (!root.empty && !rootDegree) {\n return NoChord;\n }\n\n const intervals = Array.from(type.intervals);\n\n for (let i = 1; i < rootDegree; i++) {\n const num = intervals[0][0];\n const quality = intervals[0][1];\n const newNum = parseInt(num, 10) + 7;\n intervals.push(`${newNum}${quality}`);\n intervals.shift();\n }\n\n const notes = tonic.empty\n ? []\n : intervals.map((i) => transposeNote(tonic, i));\n\n typeName = type.aliases.indexOf(typeName) !== -1 ? typeName : type.aliases[0];\n const symbol = `${tonic.empty ? \"\" : tonic.pc}${typeName}${\n root.empty || rootDegree <= 1 ? \"\" : \"/\" + root.pc\n }`;\n const name = `${optionalTonic ? tonic.pc + \" \" : \"\"}${type.name}${\n rootDegree > 1 && optionalRoot ? \" over \" + root.pc : \"\"\n }`;\n return {\n ...type,\n name,\n symbol,\n type: type.name,\n root: root.name,\n intervals,\n rootDegree,\n tonic: tonic.name,\n notes,\n };\n}\n\nexport const chord = deprecate(\"Chord.chord\", \"Chord.get\", get);\n\n/**\n * Transpose a chord name\n *\n * @param {string} chordName - the chord name\n * @return {string} the transposed chord\n *\n * @example\n * transpose('Dm7', 'P4') // => 'Gm7\n */\nexport function transpose(chordName: string, interval: string): string {\n const [tonic, type] = tokenize(chordName);\n if (!tonic) {\n return chordName;\n }\n return transposeNote(tonic, interval) + type;\n}\n\n/**\n * Get all scales where the given chord fits\n *\n * @example\n * chordScales('C7b9')\n * // => [\"phrygian dominant\", \"flamenco\", \"spanish heptatonic\", \"half-whole diminished\", \"chromatic\"]\n */\nexport function chordScales(name: string): string[] {\n const s = get(name);\n const isChordIncluded = isSupersetOf(s.chroma);\n return scaleTypes()\n .filter((scale) => isChordIncluded(scale.chroma))\n .map((scale) => scale.name);\n}\n/**\n * Get all chords names that are a superset of the given one\n * (has the same notes and at least one more)\n *\n * @function\n * @example\n * extended(\"CMaj7\")\n * // => [ 'Cmaj#4', 'Cmaj7#9#11', 'Cmaj9', 'CM7add13', 'Cmaj13', 'Cmaj9#11', 'CM13#11', 'CM7b9' ]\n */\nexport function extended(chordName: string): string[] {\n const s = get(chordName);\n const isSuperset = isSupersetOf(s.chroma);\n return chordTypes()\n .filter((chord) => isSuperset(chord.chroma))\n .map((chord) => s.tonic + chord.aliases[0]);\n}\n\n/**\n * Find all chords names that are a subset of the given one\n * (has less notes but all from the given chord)\n *\n * @example\n */\nexport function reduced(chordName: string): string[] {\n const s = get(chordName);\n const isSubset = isSubsetOf(s.chroma);\n return chordTypes()\n .filter((chord) => isSubset(chord.chroma))\n .map((chord) => s.tonic + chord.aliases[0]);\n}\n\n/**\n * Returns a function to get a note name from the scale degree.\n *\n * @example\n * [1, 2, 3, 4].map(Chord.degrees(\"C\")) => [\"C\", \"E\", \"G\", \"C\"]\n * [1, 2, 3, 4].map(Chord.degrees(\"C4\")) => [\"C4\", \"D4\", \"E4\", \"C5\"]\n */\nexport function degrees(scaleName: string) {\n const chord = get(scaleName);\n return transposeIntervalSetByDegree(chord.intervals, chord.tonic ?? \"\");\n}\n\nexport default {\n getChord,\n get,\n detect,\n chordScales,\n extended,\n reduced,\n tokenize,\n transpose,\n degrees,\n // deprecate\n chord,\n};\n"],"mappings":"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","names":["detect","chord"]}
1
+ {"version":3,"sources":["../index.ts"],"sourcesContent":["import { detect } from \"@tonaljs/chord-detect\";\nimport {\n all as chordTypes,\n ChordType,\n get as getChordType,\n} from \"@tonaljs/chord-type\";\nimport { tonicIntervalsTransposer } from \"@tonaljs/core\";\n\nimport {\n deprecate,\n distance,\n note,\n NoteName,\n tokenizeNote,\n transpose as transposeNote,\n} from \"@tonaljs/core\";\n\nimport { isSubsetOf, isSupersetOf } from \"@tonaljs/pcset\";\n\nimport { all as scaleTypes } from \"@tonaljs/scale-type\";\nexport { detect } from \"@tonaljs/chord-detect\";\n\ntype ChordName = string;\ntype ChordNameTokens = [string, string]; // [TONIC, SCALE TYPE]\n\nexport interface Chord extends ChordType {\n tonic: string | null;\n type: string;\n root: string;\n rootDegree: number;\n symbol: string;\n notes: NoteName[];\n}\n\nconst NoChord: Chord = {\n empty: true,\n name: \"\",\n symbol: \"\",\n root: \"\",\n rootDegree: 0,\n type: \"\",\n tonic: null,\n setNum: NaN,\n quality: \"Unknown\",\n chroma: \"\",\n normalized: \"\",\n aliases: [],\n notes: [],\n intervals: [],\n};\n\n// 6, 64, 7, 9, 11 and 13 are consider part of the chord\n// (see https://github.com/danigb/tonal/issues/55)\nconst NUM_TYPES = /^(6|64|7|9|11|13)$/;\n/**\n * Tokenize a chord name. It returns an array with the tonic and chord type\n * If not tonic is found, all the name is considered the chord name.\n *\n * This function does NOT check if the chord type exists or not. It only tries\n * to split the tonic and chord type.\n *\n * @function\n * @param {string} name - the chord name\n * @return {Array} an array with [tonic, type]\n * @example\n * tokenize(\"Cmaj7\") // => [ \"C\", \"maj7\" ]\n * tokenize(\"C7\") // => [ \"C\", \"7\" ]\n * tokenize(\"mMaj7\") // => [ null, \"mMaj7\" ]\n * tokenize(\"Cnonsense\") // => [ null, \"nonsense\" ]\n */\nexport function tokenize(name: string): ChordNameTokens {\n const [letter, acc, oct, type] = tokenizeNote(name);\n if (letter === \"\") {\n return [\"\", name];\n }\n // aug is augmented (see https://github.com/danigb/tonal/issues/55)\n if (letter === \"A\" && type === \"ug\") {\n return [\"\", \"aug\"];\n }\n // see: https://github.com/tonaljs/tonal/issues/70\n if (!type && (oct === \"4\" || oct === \"5\")) {\n return [letter + acc, oct];\n }\n\n if (NUM_TYPES.test(oct)) {\n return [letter + acc, oct + type];\n } else {\n return [letter + acc + oct, type];\n }\n}\n\n/**\n * Get a Chord from a chord name.\n */\nexport function get(src: ChordName | ChordNameTokens): Chord {\n if (src === \"\") {\n return NoChord;\n }\n if (Array.isArray(src) && src.length === 2) {\n return getChord(src[1], src[0]);\n } else {\n const [tonic, type] = tokenize(src);\n const chord = getChord(type, tonic);\n return chord.empty ? getChord(src) : chord;\n }\n}\n\n/**\n * Get chord properties\n *\n * @param typeName - the chord type name\n * @param [tonic] - Optional tonic\n * @param [root] - Optional root (requires a tonic)\n */\nexport function getChord(\n typeName: string,\n optionalTonic?: string,\n optionalRoot?: string\n): Chord {\n const type = getChordType(typeName);\n const tonic = note(optionalTonic || \"\");\n const root = note(optionalRoot || \"\");\n\n if (\n type.empty ||\n (optionalTonic && tonic.empty) ||\n (optionalRoot && root.empty)\n ) {\n return NoChord;\n }\n\n const rootInterval = distance(tonic.pc, root.pc);\n const rootDegree = type.intervals.indexOf(rootInterval) + 1;\n if (!root.empty && !rootDegree) {\n return NoChord;\n }\n\n const intervals = Array.from(type.intervals);\n\n for (let i = 1; i < rootDegree; i++) {\n const num = intervals[0][0];\n const quality = intervals[0][1];\n const newNum = parseInt(num, 10) + 7;\n intervals.push(`${newNum}${quality}`);\n intervals.shift();\n }\n\n const notes = tonic.empty\n ? []\n : intervals.map((i) => transposeNote(tonic, i));\n\n typeName = type.aliases.indexOf(typeName) !== -1 ? typeName : type.aliases[0];\n const symbol = `${tonic.empty ? \"\" : tonic.pc}${typeName}${\n root.empty || rootDegree <= 1 ? \"\" : \"/\" + root.pc\n }`;\n const name = `${optionalTonic ? tonic.pc + \" \" : \"\"}${type.name}${\n rootDegree > 1 && optionalRoot ? \" over \" + root.pc : \"\"\n }`;\n return {\n ...type,\n name,\n symbol,\n type: type.name,\n root: root.name,\n intervals,\n rootDegree,\n tonic: tonic.name,\n notes,\n };\n}\n\nexport const chord = deprecate(\"Chord.chord\", \"Chord.get\", get);\n\n/**\n * Transpose a chord name\n *\n * @param {string} chordName - the chord name\n * @return {string} the transposed chord\n *\n * @example\n * transpose('Dm7', 'P4') // => 'Gm7\n */\nexport function transpose(chordName: string, interval: string): string {\n const [tonic, type] = tokenize(chordName);\n if (!tonic) {\n return chordName;\n }\n return transposeNote(tonic, interval) + type;\n}\n\n/**\n * Get all scales where the given chord fits\n *\n * @example\n * chordScales('C7b9')\n * // => [\"phrygian dominant\", \"flamenco\", \"spanish heptatonic\", \"half-whole diminished\", \"chromatic\"]\n */\nexport function chordScales(name: string): string[] {\n const s = get(name);\n const isChordIncluded = isSupersetOf(s.chroma);\n return scaleTypes()\n .filter((scale) => isChordIncluded(scale.chroma))\n .map((scale) => scale.name);\n}\n/**\n * Get all chords names that are a superset of the given one\n * (has the same notes and at least one more)\n *\n * @function\n * @example\n * extended(\"CMaj7\")\n * // => [ 'Cmaj#4', 'Cmaj7#9#11', 'Cmaj9', 'CM7add13', 'Cmaj13', 'Cmaj9#11', 'CM13#11', 'CM7b9' ]\n */\nexport function extended(chordName: string): string[] {\n const s = get(chordName);\n const isSuperset = isSupersetOf(s.chroma);\n return chordTypes()\n .filter((chord) => isSuperset(chord.chroma))\n .map((chord) => s.tonic + chord.aliases[0]);\n}\n\n/**\n * Find all chords names that are a subset of the given one\n * (has less notes but all from the given chord)\n *\n * @example\n */\nexport function reduced(chordName: string): string[] {\n const s = get(chordName);\n const isSubset = isSubsetOf(s.chroma);\n return chordTypes()\n .filter((chord) => isSubset(chord.chroma))\n .map((chord) => s.tonic + chord.aliases[0]);\n}\n\n/**\n * Returns a function to get a note name from the scale degree.\n *\n * @example\n * [1, 2, 3, 4].map(Chord.degrees(\"C\")) => [\"C\", \"E\", \"G\", \"C\"]\n * [1, 2, 3, 4].map(Chord.degrees(\"C4\")) => [\"C4\", \"E4\", \"G4\", \"C5\"]\n */\nexport function degrees(chordName: string | ChordNameTokens) {\n const { intervals, tonic } = get(chordName);\n const transpose = tonicIntervalsTransposer(intervals, tonic);\n return (degree: number) =>\n degree ? transpose(degree > 0 ? degree - 1 : degree) : \"\";\n}\n\n/**\n * Sames as `degree` but with 0-based index\n */\nexport function steps(chordName: string | ChordNameTokens) {\n const { intervals, tonic } = get(chordName);\n return tonicIntervalsTransposer(intervals, tonic);\n}\n\nexport default {\n getChord,\n get,\n detect,\n chordScales,\n extended,\n reduced,\n tokenize,\n transpose,\n degrees,\n steps,\n\n // deprecate\n chord,\n};\n"],"mappings":"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","names":["detect","chord","transpose"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tonaljs/chord",
3
- "version": "4.9.0",
3
+ "version": "4.10.0",
4
4
  "description": "Musical chords and its relations",
5
5
  "keywords": [
6
6
  "chord",
@@ -19,7 +19,7 @@
19
19
  "@tonaljs/chord-detect": "^4.8.0",
20
20
  "@tonaljs/chord-type": "^4.8.0",
21
21
  "@tonaljs/collection": "^4.8.0",
22
- "@tonaljs/core": "^4.9.0",
22
+ "@tonaljs/core": "^4.10.0",
23
23
  "@tonaljs/pcset": "^4.8.0",
24
24
  "@tonaljs/scale-type": "^4.8.0"
25
25
  },