@tonaljs/chord 4.8.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
@@ -66,6 +66,47 @@ Important: currently chord with roots are NOT allowed (will be implemented in ne
66
66
  Chord.get("Cmaj7/E"); // => { empty: true }
67
67
  ```
68
68
 
69
+ ### `Chord.degrees(chordName: string) => (degree: number) => string`
70
+
71
+ `Scale.degrees` returns a function to get a note name from a scale degree:
72
+
73
+ ```js
74
+ const c4m7 = Chord.degrees("C4m7");
75
+ c4m7(1); // => "C4"
76
+ c4m7(2); // => "Eb4"
77
+ c4m7(3); // => "G4"
78
+ c4m7(4); // => "Bb4"
79
+ c4m7(1); // => "C5"
80
+ ```
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
+
91
+ Bear in mind that degree numbers starts with 1 and 0 returns an empty string:
92
+
93
+ ```js
94
+ c4m7(0); // => ""
95
+ ```
96
+
97
+ See [`Scale.degrees`](https://github.com/tonaljs/tonal/tree/main/packages/scale#scaledegreesscalename-string--degree-number--string)
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
+
69
110
  #### `Chord.detect(notes: string[]) => string[]`
70
111
 
71
112
  Given a list of notes, get the possible chord names:
package/dist/index.d.ts CHANGED
@@ -78,6 +78,18 @@ declare function extended(chordName: string): string[];
78
78
  * @example
79
79
  */
80
80
  declare function reduced(chordName: string): string[];
81
+ /**
82
+ * Returns a function to get a note name from the scale degree.
83
+ *
84
+ * @example
85
+ * [1, 2, 3, 4].map(Chord.degrees("C")) => ["C", "E", "G", "C"]
86
+ * [1, 2, 3, 4].map(Chord.degrees("C4")) => ["C4", "E4", "G4", "C5"]
87
+ */
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;
81
93
  declare const _default: {
82
94
  getChord: typeof getChord;
83
95
  get: typeof get;
@@ -87,7 +99,9 @@ declare const _default: {
87
99
  reduced: typeof reduced;
88
100
  tokenize: typeof tokenize;
89
101
  transpose: typeof transpose;
102
+ degrees: typeof degrees;
103
+ steps: typeof steps;
90
104
  chord: (this: unknown, ...args: unknown[]) => Chord;
91
105
  };
92
106
 
93
- export { Chord, chord, chordScales, _default as default, 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
@@ -23,11 +23,13 @@ __export(chord_exports, {
23
23
  chord: () => chord,
24
24
  chordScales: () => chordScales,
25
25
  default: () => chord_default,
26
+ degrees: () => degrees,
26
27
  detect: () => import_chord_detect2.detect,
27
28
  extended: () => extended,
28
29
  get: () => get,
29
30
  getChord: () => getChord,
30
31
  reduced: () => reduced,
32
+ steps: () => steps,
31
33
  tokenize: () => tokenize,
32
34
  transpose: () => transpose
33
35
  });
@@ -35,6 +37,7 @@ module.exports = __toCommonJS(chord_exports);
35
37
  var import_chord_detect = require("@tonaljs/chord-detect");
36
38
  var import_chord_type = require("@tonaljs/chord-type");
37
39
  var import_core = require("@tonaljs/core");
40
+ var import_core2 = require("@tonaljs/core");
38
41
  var import_pcset = require("@tonaljs/pcset");
39
42
  var import_scale_type = require("@tonaljs/scale-type");
40
43
  var import_chord_detect2 = require("@tonaljs/chord-detect");
@@ -56,7 +59,7 @@ var NoChord = {
56
59
  };
57
60
  var NUM_TYPES = /^(6|64|7|9|11|13)$/;
58
61
  function tokenize(name) {
59
- const [letter, acc, oct, type] = (0, import_core.tokenizeNote)(name);
62
+ const [letter, acc, oct, type] = (0, import_core2.tokenizeNote)(name);
60
63
  if (letter === "") {
61
64
  return ["", name];
62
65
  }
@@ -86,12 +89,12 @@ function get(src) {
86
89
  }
87
90
  function getChord(typeName, optionalTonic, optionalRoot) {
88
91
  const type = (0, import_chord_type.get)(typeName);
89
- const tonic = (0, import_core.note)(optionalTonic || "");
90
- const root = (0, import_core.note)(optionalRoot || "");
92
+ const tonic = (0, import_core2.note)(optionalTonic || "");
93
+ const root = (0, import_core2.note)(optionalRoot || "");
91
94
  if (type.empty || optionalTonic && tonic.empty || optionalRoot && root.empty) {
92
95
  return NoChord;
93
96
  }
94
- const rootInterval = (0, import_core.distance)(tonic.pc, root.pc);
97
+ const rootInterval = (0, import_core2.distance)(tonic.pc, root.pc);
95
98
  const rootDegree = type.intervals.indexOf(rootInterval) + 1;
96
99
  if (!root.empty && !rootDegree) {
97
100
  return NoChord;
@@ -104,7 +107,7 @@ function getChord(typeName, optionalTonic, optionalRoot) {
104
107
  intervals.push(`${newNum}${quality}`);
105
108
  intervals.shift();
106
109
  }
107
- const notes = tonic.empty ? [] : intervals.map((i) => (0, import_core.transpose)(tonic, i));
110
+ const notes = tonic.empty ? [] : intervals.map((i) => (0, import_core2.transpose)(tonic, i));
108
111
  typeName = type.aliases.indexOf(typeName) !== -1 ? typeName : type.aliases[0];
109
112
  const symbol = `${tonic.empty ? "" : tonic.pc}${typeName}${root.empty || rootDegree <= 1 ? "" : "/" + root.pc}`;
110
113
  const name = `${optionalTonic ? tonic.pc + " " : ""}${type.name}${rootDegree > 1 && optionalRoot ? " over " + root.pc : ""}`;
@@ -120,13 +123,13 @@ function getChord(typeName, optionalTonic, optionalRoot) {
120
123
  notes
121
124
  };
122
125
  }
123
- var chord = (0, import_core.deprecate)("Chord.chord", "Chord.get", get);
126
+ var chord = (0, import_core2.deprecate)("Chord.chord", "Chord.get", get);
124
127
  function transpose(chordName, interval) {
125
128
  const [tonic, type] = tokenize(chordName);
126
129
  if (!tonic) {
127
130
  return chordName;
128
131
  }
129
- return (0, import_core.transpose)(tonic, interval) + type;
132
+ return (0, import_core2.transpose)(tonic, interval) + type;
130
133
  }
131
134
  function chordScales(name) {
132
135
  const s = get(name);
@@ -143,6 +146,15 @@ function reduced(chordName) {
143
146
  const isSubset = (0, import_pcset.isSubsetOf)(s.chroma);
144
147
  return (0, import_chord_type.all)().filter((chord2) => isSubset(chord2.chroma)).map((chord2) => s.tonic + chord2.aliases[0]);
145
148
  }
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);
157
+ }
146
158
  var chord_default = {
147
159
  getChord,
148
160
  get,
@@ -152,17 +164,21 @@ var chord_default = {
152
164
  reduced,
153
165
  tokenize,
154
166
  transpose,
167
+ degrees,
168
+ steps,
155
169
  chord
156
170
  };
157
171
  // Annotate the CommonJS export names for ESM import in node:
158
172
  0 && (module.exports = {
159
173
  chord,
160
174
  chordScales,
175
+ degrees,
161
176
  detect,
162
177
  extended,
163
178
  get,
164
179
  getChord,
165
180
  reduced,
181
+ steps,
166
182
  tokenize,
167
183
  transpose
168
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\";\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\nexport default {\n getChord,\n get,\n detect,\n chordScales,\n extended,\n reduced,\n tokenize,\n transpose,\n // deprecate\n chord,\n};\n"],"mappings":"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","names":["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":"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","names":["import_core","import_chord_detect","chord","getChordType","transposeNote","scaleTypes","chordTypes","transpose"]}
package/dist/index.mjs CHANGED
@@ -4,6 +4,7 @@ import {
4
4
  all as chordTypes,
5
5
  get as getChordType
6
6
  } from "@tonaljs/chord-type";
7
+ import { tonicIntervalsTransposer } from "@tonaljs/core";
7
8
  import {
8
9
  deprecate,
9
10
  distance,
@@ -119,6 +120,15 @@ function reduced(chordName) {
119
120
  const isSubset = isSubsetOf(s.chroma);
120
121
  return chordTypes().filter((chord2) => isSubset(chord2.chroma)).map((chord2) => s.tonic + chord2.aliases[0]);
121
122
  }
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);
131
+ }
122
132
  var chord_default = {
123
133
  getChord,
124
134
  get,
@@ -128,17 +138,21 @@ var chord_default = {
128
138
  reduced,
129
139
  tokenize,
130
140
  transpose,
141
+ degrees,
142
+ steps,
131
143
  chord
132
144
  };
133
145
  export {
134
146
  chord,
135
147
  chordScales,
136
148
  chord_default as default,
149
+ degrees,
137
150
  detect2 as detect,
138
151
  extended,
139
152
  get,
140
153
  getChord,
141
154
  reduced,
155
+ steps,
142
156
  tokenize,
143
157
  transpose
144
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\";\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\nexport default {\n getChord,\n get,\n detect,\n chordScales,\n extended,\n reduced,\n tokenize,\n transpose,\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.8.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.8.0",
22
+ "@tonaljs/core": "^4.10.0",
23
23
  "@tonaljs/pcset": "^4.8.0",
24
24
  "@tonaljs/scale-type": "^4.8.0"
25
25
  },