@tonaljs/scale 4.12.6 → 4.13.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/dist/index.d.mts +8 -3
- package/dist/index.d.ts +8 -3
- package/dist/index.js +18 -20
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +8 -15
- package/dist/index.mjs.map +1 -1
- package/package.json +8 -7
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { NoteName } from '@tonaljs/
|
|
1
|
+
import { NoteName } from '@tonaljs/pitch-note';
|
|
2
2
|
import { ScaleType, names as names$1 } from '@tonaljs/scale-type';
|
|
3
3
|
|
|
4
4
|
type ScaleName = string;
|
|
@@ -34,7 +34,11 @@ declare const names: typeof names$1;
|
|
|
34
34
|
* Get a Scale from a scale name.
|
|
35
35
|
*/
|
|
36
36
|
declare function get(src: ScaleName | ScaleNameTokens): Scale;
|
|
37
|
-
|
|
37
|
+
/**
|
|
38
|
+
* @deprecated
|
|
39
|
+
* @use Scale.get
|
|
40
|
+
*/
|
|
41
|
+
declare const scale: typeof get;
|
|
38
42
|
declare function detect(notes: string[], options?: {
|
|
39
43
|
tonic?: string;
|
|
40
44
|
match?: "exact" | "fit";
|
|
@@ -114,6 +118,7 @@ declare function degrees(scaleName: string | ScaleNameTokens): (degree: number)
|
|
|
114
118
|
* Sames as `degree` but with 0-based index
|
|
115
119
|
*/
|
|
116
120
|
declare function steps(scaleName: string | ScaleNameTokens): (normalized: number) => string;
|
|
121
|
+
/** @deprecated */
|
|
117
122
|
declare const _default: {
|
|
118
123
|
degrees: typeof degrees;
|
|
119
124
|
detect: typeof detect;
|
|
@@ -127,7 +132,7 @@ declare const _default: {
|
|
|
127
132
|
scaleNotes: typeof scaleNotes;
|
|
128
133
|
steps: typeof steps;
|
|
129
134
|
tokenize: typeof tokenize;
|
|
130
|
-
scale:
|
|
135
|
+
scale: typeof get;
|
|
131
136
|
};
|
|
132
137
|
|
|
133
138
|
export { type Scale, _default as default, degrees, detect, extended, get, modeNames, names, rangeOf, reduced, scale, scaleChords, scaleNotes, steps, tokenize };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { NoteName } from '@tonaljs/
|
|
1
|
+
import { NoteName } from '@tonaljs/pitch-note';
|
|
2
2
|
import { ScaleType, names as names$1 } from '@tonaljs/scale-type';
|
|
3
3
|
|
|
4
4
|
type ScaleName = string;
|
|
@@ -34,7 +34,11 @@ declare const names: typeof names$1;
|
|
|
34
34
|
* Get a Scale from a scale name.
|
|
35
35
|
*/
|
|
36
36
|
declare function get(src: ScaleName | ScaleNameTokens): Scale;
|
|
37
|
-
|
|
37
|
+
/**
|
|
38
|
+
* @deprecated
|
|
39
|
+
* @use Scale.get
|
|
40
|
+
*/
|
|
41
|
+
declare const scale: typeof get;
|
|
38
42
|
declare function detect(notes: string[], options?: {
|
|
39
43
|
tonic?: string;
|
|
40
44
|
match?: "exact" | "fit";
|
|
@@ -114,6 +118,7 @@ declare function degrees(scaleName: string | ScaleNameTokens): (degree: number)
|
|
|
114
118
|
* Sames as `degree` but with 0-based index
|
|
115
119
|
*/
|
|
116
120
|
declare function steps(scaleName: string | ScaleNameTokens): (normalized: number) => string;
|
|
121
|
+
/** @deprecated */
|
|
117
122
|
declare const _default: {
|
|
118
123
|
degrees: typeof degrees;
|
|
119
124
|
detect: typeof detect;
|
|
@@ -127,7 +132,7 @@ declare const _default: {
|
|
|
127
132
|
scaleNotes: typeof scaleNotes;
|
|
128
133
|
steps: typeof steps;
|
|
129
134
|
tokenize: typeof tokenize;
|
|
130
|
-
scale:
|
|
135
|
+
scale: typeof get;
|
|
131
136
|
};
|
|
132
137
|
|
|
133
138
|
export { type Scale, _default as default, degrees, detect, extended, get, modeNames, names, rangeOf, reduced, scale, scaleChords, scaleNotes, steps, tokenize };
|
package/dist/index.js
CHANGED
|
@@ -38,9 +38,10 @@ __export(scale_exports, {
|
|
|
38
38
|
module.exports = __toCommonJS(scale_exports);
|
|
39
39
|
var import_chord_type = require("@tonaljs/chord-type");
|
|
40
40
|
var import_collection = require("@tonaljs/collection");
|
|
41
|
-
var import_core = require("@tonaljs/core");
|
|
42
41
|
var import_note = require("@tonaljs/note");
|
|
43
42
|
var import_pcset = require("@tonaljs/pcset");
|
|
43
|
+
var import_pitch_distance = require("@tonaljs/pitch-distance");
|
|
44
|
+
var import_pitch_note = require("@tonaljs/pitch-note");
|
|
44
45
|
var import_scale_type = require("@tonaljs/scale-type");
|
|
45
46
|
var NoScale = {
|
|
46
47
|
empty: true,
|
|
@@ -59,9 +60,9 @@ function tokenize(name) {
|
|
|
59
60
|
return ["", ""];
|
|
60
61
|
}
|
|
61
62
|
const i = name.indexOf(" ");
|
|
62
|
-
const tonic = (0,
|
|
63
|
+
const tonic = (0, import_pitch_note.note)(name.substring(0, i));
|
|
63
64
|
if (tonic.empty) {
|
|
64
|
-
const n = (0,
|
|
65
|
+
const n = (0, import_pitch_note.note)(name);
|
|
65
66
|
return n.empty ? ["", name] : [n.name, ""];
|
|
66
67
|
}
|
|
67
68
|
const type = name.substring(tonic.name.length + 1).toLowerCase();
|
|
@@ -70,20 +71,20 @@ function tokenize(name) {
|
|
|
70
71
|
var names = import_scale_type.names;
|
|
71
72
|
function get(src) {
|
|
72
73
|
const tokens = Array.isArray(src) ? src : tokenize(src);
|
|
73
|
-
const tonic = (0,
|
|
74
|
+
const tonic = (0, import_pitch_note.note)(tokens[0]).name;
|
|
74
75
|
const st = (0, import_scale_type.get)(tokens[1]);
|
|
75
76
|
if (st.empty) {
|
|
76
77
|
return NoScale;
|
|
77
78
|
}
|
|
78
79
|
const type = st.name;
|
|
79
|
-
const notes = tonic ? st.intervals.map((i) => (0,
|
|
80
|
+
const notes = tonic ? st.intervals.map((i) => (0, import_pitch_distance.transpose)(tonic, i)) : [];
|
|
80
81
|
const name = tonic ? tonic + " " + type : type;
|
|
81
82
|
return { ...st, name, type, tonic, notes };
|
|
82
83
|
}
|
|
83
|
-
var scale =
|
|
84
|
+
var scale = get;
|
|
84
85
|
function detect(notes, options = {}) {
|
|
85
86
|
const notesChroma = (0, import_pcset.chroma)(notes);
|
|
86
|
-
const tonic = (0,
|
|
87
|
+
const tonic = (0, import_pitch_note.note)(options.tonic ?? notes[0] ?? "");
|
|
87
88
|
const tonicChroma = tonic.chroma;
|
|
88
89
|
if (tonicChroma === void 0) {
|
|
89
90
|
return [];
|
|
@@ -119,7 +120,7 @@ function reduced(name) {
|
|
|
119
120
|
return (0, import_scale_type.all)().filter((scale2) => isSubset(scale2.chroma)).map((scale2) => scale2.name);
|
|
120
121
|
}
|
|
121
122
|
function scaleNotes(notes) {
|
|
122
|
-
const pcset = notes.map((n) => (0,
|
|
123
|
+
const pcset = notes.map((n) => (0, import_pitch_note.note)(n).pc).filter((x) => x);
|
|
123
124
|
const tonic = pcset[0];
|
|
124
125
|
const scale2 = (0, import_note.sortedUniqNames)(pcset);
|
|
125
126
|
return (0, import_collection.rotate)(scale2.indexOf(tonic), scale2);
|
|
@@ -137,37 +138,34 @@ function modeNames(name) {
|
|
|
137
138
|
}
|
|
138
139
|
function getNoteNameOf(scale2) {
|
|
139
140
|
const names2 = Array.isArray(scale2) ? scaleNotes(scale2) : get(scale2).notes;
|
|
140
|
-
const chromas = names2.map((name) => (0,
|
|
141
|
+
const chromas = names2.map((name) => (0, import_pitch_note.note)(name).chroma);
|
|
141
142
|
return (noteOrMidi) => {
|
|
142
|
-
const currNote = typeof noteOrMidi === "number" ? (0,
|
|
143
|
+
const currNote = typeof noteOrMidi === "number" ? (0, import_pitch_note.note)((0, import_note.fromMidi)(noteOrMidi)) : (0, import_pitch_note.note)(noteOrMidi);
|
|
143
144
|
const height = currNote.height;
|
|
144
|
-
if (height === void 0)
|
|
145
|
-
return void 0;
|
|
145
|
+
if (height === void 0) return void 0;
|
|
146
146
|
const chroma2 = height % 12;
|
|
147
147
|
const position = chromas.indexOf(chroma2);
|
|
148
|
-
if (position === -1)
|
|
149
|
-
return void 0;
|
|
148
|
+
if (position === -1) return void 0;
|
|
150
149
|
return (0, import_note.enharmonic)(currNote.name, names2[position]);
|
|
151
150
|
};
|
|
152
151
|
}
|
|
153
152
|
function rangeOf(scale2) {
|
|
154
153
|
const getName = getNoteNameOf(scale2);
|
|
155
154
|
return (fromNote, toNote) => {
|
|
156
|
-
const from = (0,
|
|
157
|
-
const to = (0,
|
|
158
|
-
if (from === void 0 || to === void 0)
|
|
159
|
-
return [];
|
|
155
|
+
const from = (0, import_pitch_note.note)(fromNote).height;
|
|
156
|
+
const to = (0, import_pitch_note.note)(toNote).height;
|
|
157
|
+
if (from === void 0 || to === void 0) return [];
|
|
160
158
|
return (0, import_collection.range)(from, to).map(getName).filter((x) => x);
|
|
161
159
|
};
|
|
162
160
|
}
|
|
163
161
|
function degrees(scaleName) {
|
|
164
162
|
const { intervals, tonic } = get(scaleName);
|
|
165
|
-
const transpose2 = (0,
|
|
163
|
+
const transpose2 = (0, import_pitch_distance.tonicIntervalsTransposer)(intervals, tonic);
|
|
166
164
|
return (degree) => degree ? transpose2(degree > 0 ? degree - 1 : degree) : "";
|
|
167
165
|
}
|
|
168
166
|
function steps(scaleName) {
|
|
169
167
|
const { intervals, tonic } = get(scaleName);
|
|
170
|
-
return (0,
|
|
168
|
+
return (0, import_pitch_distance.tonicIntervalsTransposer)(intervals, tonic);
|
|
171
169
|
}
|
|
172
170
|
var scale_default = {
|
|
173
171
|
degrees,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../index.ts"],"sourcesContent":["/**\n * References:\n * - https://www.researchgate.net/publication/327567188_An_Algorithm_for_Spelling_the_Pitches_of_Any_Musical_Scale\n * @module scale\n */\nimport { all as chordTypes } from \"@tonaljs/chord-type\";\nimport { range as nums, rotate } from \"@tonaljs/collection\";\nimport {\n deprecate,\n note,\n NoteName,\n tonicIntervalsTransposer,\n transpose,\n} from \"@tonaljs/core\";\nimport { enharmonic, fromMidi, sortedUniqNames } from \"@tonaljs/note\";\nimport {\n chroma,\n isChroma,\n isSubsetOf,\n isSupersetOf,\n modes,\n} from \"@tonaljs/pcset\";\nimport {\n all,\n all as scaleTypes,\n get as getScaleType,\n names as scaleTypeNames,\n ScaleType,\n} from \"@tonaljs/scale-type\";\n\ntype ScaleName = string;\ntype ScaleNameTokens = [string, string]; // [TONIC, SCALE TYPE]\n\nexport interface Scale extends ScaleType {\n tonic: string | null;\n type: string;\n notes: NoteName[];\n}\n\nconst NoScale: Scale = {\n empty: true,\n name: \"\",\n type: \"\",\n tonic: null,\n setNum: NaN,\n chroma: \"\",\n normalized: \"\",\n aliases: [],\n notes: [],\n intervals: [],\n};\n\n/**\n * Given a string with a scale name and (optionally) a tonic, split\n * that components.\n *\n * It retuns an array with the form [ name, tonic ] where tonic can be a\n * note name or null and name can be any arbitrary string\n * (this function doesn\"t check if that scale name exists)\n *\n * @function\n * @param {string} name - the scale name\n * @return {Array} an array [tonic, name]\n * @example\n * tokenize(\"C mixolydean\") // => [\"C\", \"mixolydean\"]\n * tokenize(\"anything is valid\") // => [\"\", \"anything is valid\"]\n * tokenize() // => [\"\", \"\"]\n */\nexport function tokenize(name: ScaleName): ScaleNameTokens {\n if (typeof name !== \"string\") {\n return [\"\", \"\"];\n }\n const i = name.indexOf(\" \");\n const tonic = note(name.substring(0, i));\n if (tonic.empty) {\n const n = note(name);\n return n.empty ? [\"\", name] : [n.name, \"\"];\n }\n\n const type = name.substring(tonic.name.length + 1).toLowerCase();\n return [tonic.name, type.length ? type : \"\"];\n}\n\n/**\n * Get all scale names\n * @function\n */\nexport const names = scaleTypeNames;\n\n/**\n * Get a Scale from a scale name.\n */\nexport function get(src: ScaleName | ScaleNameTokens): Scale {\n const tokens = Array.isArray(src) ? src : tokenize(src);\n const tonic = note(tokens[0]).name;\n const st = getScaleType(tokens[1]);\n if (st.empty) {\n return NoScale;\n }\n\n const type = st.name;\n const notes: string[] = tonic\n ? st.intervals.map((i) => transpose(tonic, i))\n : [];\n\n const name = tonic ? tonic + \" \" + type : type;\n\n return { ...st, name, type, tonic, notes };\n}\n\nexport const scale = deprecate(\"Scale.scale\", \"Scale.get\", get);\n\nexport function detect(\n notes: string[],\n options: { tonic?: string; match?: \"exact\" | \"fit\" } = {},\n): string[] {\n const notesChroma = chroma(notes);\n const tonic = note(options.tonic ?? notes[0] ?? \"\");\n const tonicChroma = tonic.chroma;\n if (tonicChroma === undefined) {\n return [];\n }\n\n const pitchClasses = notesChroma.split(\"\");\n pitchClasses[tonicChroma] = \"1\";\n const scaleChroma = rotate(tonicChroma, pitchClasses).join(\"\");\n const match = all().find((scaleType) => scaleType.chroma === scaleChroma);\n\n const results: string[] = [];\n if (match) {\n results.push(tonic.name + \" \" + match.name);\n }\n if (options.match === \"exact\") {\n return results;\n }\n\n extended(scaleChroma).forEach((scaleName) => {\n results.push(tonic.name + \" \" + scaleName);\n });\n\n return results;\n}\n\n/**\n * Get all chords that fits a given scale\n *\n * @function\n * @param {string} name - the scale name\n * @return {Array<string>} - the chord names\n *\n * @example\n * scaleChords(\"pentatonic\") // => [\"5\", \"64\", \"M\", \"M6\", \"Madd9\", \"Msus2\"]\n */\nexport function scaleChords(name: string): string[] {\n const s = get(name);\n const inScale = isSubsetOf(s.chroma);\n return chordTypes()\n .filter((chord) => inScale(chord.chroma))\n .map((chord) => chord.aliases[0]);\n}\n/**\n * Get all scales names that are a superset of the given one\n * (has the same notes and at least one more)\n *\n * @function\n * @param {string} name\n * @return {Array} a list of scale names\n * @example\n * extended(\"major\") // => [\"bebop\", \"bebop dominant\", \"bebop major\", \"chromatic\", \"ichikosucho\"]\n */\nexport function extended(name: string): string[] {\n const chroma = isChroma(name) ? name : get(name).chroma;\n const isSuperset = isSupersetOf(chroma);\n return scaleTypes()\n .filter((scale) => isSuperset(scale.chroma))\n .map((scale) => scale.name);\n}\n\n/**\n * Find all scales names that are a subset of the given one\n * (has less notes but all from the given scale)\n *\n * @function\n * @param {string} name\n * @return {Array} a list of scale names\n *\n * @example\n * reduced(\"major\") // => [\"ionian pentatonic\", \"major pentatonic\", \"ritusen\"]\n */\nexport function reduced(name: string): string[] {\n const isSubset = isSubsetOf(get(name).chroma);\n return scaleTypes()\n .filter((scale) => isSubset(scale.chroma))\n .map((scale) => scale.name);\n}\n\n/**\n * Given an array of notes, return the scale: a pitch class set starting from\n * the first note of the array\n *\n * @function\n * @param {string[]} notes\n * @return {string[]} pitch classes with same tonic\n * @example\n * scaleNotes(['C4', 'c3', 'C5', 'C4', 'c4']) // => [\"C\"]\n * scaleNotes(['D4', 'c#5', 'A5', 'F#6']) // => [\"D\", \"F#\", \"A\", \"C#\"]\n */\nexport function scaleNotes(notes: NoteName[]) {\n const pcset: string[] = notes.map((n) => note(n).pc).filter((x) => x);\n const tonic = pcset[0];\n const scale = sortedUniqNames(pcset);\n return rotate(scale.indexOf(tonic), scale);\n}\n\ntype ScaleMode = [string, string];\n/**\n * Find mode names of a scale\n *\n * @function\n * @param {string} name - scale name\n * @example\n * modeNames(\"C pentatonic\") // => [\n * [\"C\", \"major pentatonic\"],\n * [\"D\", \"egyptian\"],\n * [\"E\", \"malkos raga\"],\n * [\"G\", \"ritusen\"],\n * [\"A\", \"minor pentatonic\"]\n * ]\n */\nexport function modeNames(name: string): ScaleMode[] {\n const s = get(name);\n if (s.empty) {\n return [];\n }\n\n const tonics = s.tonic ? s.notes : s.intervals;\n return modes(s.chroma)\n .map((chroma: string, i: number): ScaleMode => {\n const modeName = get(chroma).name;\n return modeName ? [tonics[i], modeName] : [\"\", \"\"];\n })\n .filter((x) => x[0]);\n}\n\nfunction getNoteNameOf(scale: string | string[]) {\n const names = Array.isArray(scale) ? scaleNotes(scale) : get(scale).notes;\n const chromas = names.map((name) => note(name).chroma);\n\n return (noteOrMidi: string | number): string | undefined => {\n const currNote =\n typeof noteOrMidi === \"number\"\n ? note(fromMidi(noteOrMidi))\n : note(noteOrMidi);\n const height = currNote.height;\n\n if (height === undefined) return undefined;\n const chroma = height % 12;\n const position = chromas.indexOf(chroma);\n if (position === -1) return undefined;\n return enharmonic(currNote.name, names[position]);\n };\n}\n\nexport function rangeOf(scale: string | string[]) {\n const getName = getNoteNameOf(scale);\n return (fromNote: string, toNote: string) => {\n const from = note(fromNote).height;\n const to = note(toNote).height;\n if (from === undefined || to === undefined) return [];\n\n return nums(from, to)\n .map(getName)\n .filter((x) => x);\n };\n}\n\n/**\n * Returns a function to get a note name from the scale degree.\n *\n * @example\n * [1, 2, 3].map(Scale.degrees(\"C major\")) => [\"C\", \"D\", \"E\"]\n * [1, 2, 3].map(Scale.degrees(\"C4 major\")) => [\"C4\", \"D4\", \"E4\"]\n */\nexport function degrees(scaleName: string | ScaleNameTokens) {\n const { intervals, tonic } = get(scaleName);\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(scaleName: string | ScaleNameTokens) {\n const { intervals, tonic } = get(scaleName);\n return tonicIntervalsTransposer(intervals, tonic);\n}\n\nexport default {\n degrees,\n detect,\n extended,\n get,\n modeNames,\n names,\n rangeOf,\n reduced,\n scaleChords,\n scaleNotes,\n steps,\n tokenize,\n\n // deprecated\n scale,\n};\n"],"mappings":"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","names":["scaleTypeNames","getScaleType","chordTypes","chroma","scaleTypes","scale","names","nums","transpose"]}
|
|
1
|
+
{"version":3,"sources":["../index.ts"],"sourcesContent":["/**\n * References:\n * - https://www.researchgate.net/publication/327567188_An_Algorithm_for_Spelling_the_Pitches_of_Any_Musical_Scale\n * @module scale\n */\nimport { all as chordTypes } from \"@tonaljs/chord-type\";\nimport { range as nums, rotate } from \"@tonaljs/collection\";\nimport { enharmonic, fromMidi, sortedUniqNames } from \"@tonaljs/note\";\nimport {\n chroma,\n isChroma,\n isSubsetOf,\n isSupersetOf,\n modes,\n} from \"@tonaljs/pcset\";\nimport { tonicIntervalsTransposer, transpose } from \"@tonaljs/pitch-distance\";\nimport { note, NoteName } from \"@tonaljs/pitch-note\";\nimport {\n all,\n get as getScaleType,\n ScaleType,\n names as scaleTypeNames,\n all as scaleTypes,\n} from \"@tonaljs/scale-type\";\n\ntype ScaleName = string;\ntype ScaleNameTokens = [string, string]; // [TONIC, SCALE TYPE]\n\nexport interface Scale extends ScaleType {\n tonic: string | null;\n type: string;\n notes: NoteName[];\n}\n\nconst NoScale: Scale = {\n empty: true,\n name: \"\",\n type: \"\",\n tonic: null,\n setNum: NaN,\n chroma: \"\",\n normalized: \"\",\n aliases: [],\n notes: [],\n intervals: [],\n};\n\n/**\n * Given a string with a scale name and (optionally) a tonic, split\n * that components.\n *\n * It retuns an array with the form [ name, tonic ] where tonic can be a\n * note name or null and name can be any arbitrary string\n * (this function doesn\"t check if that scale name exists)\n *\n * @function\n * @param {string} name - the scale name\n * @return {Array} an array [tonic, name]\n * @example\n * tokenize(\"C mixolydean\") // => [\"C\", \"mixolydean\"]\n * tokenize(\"anything is valid\") // => [\"\", \"anything is valid\"]\n * tokenize() // => [\"\", \"\"]\n */\nexport function tokenize(name: ScaleName): ScaleNameTokens {\n if (typeof name !== \"string\") {\n return [\"\", \"\"];\n }\n const i = name.indexOf(\" \");\n const tonic = note(name.substring(0, i));\n if (tonic.empty) {\n const n = note(name);\n return n.empty ? [\"\", name] : [n.name, \"\"];\n }\n\n const type = name.substring(tonic.name.length + 1).toLowerCase();\n return [tonic.name, type.length ? type : \"\"];\n}\n\n/**\n * Get all scale names\n * @function\n */\nexport const names = scaleTypeNames;\n\n/**\n * Get a Scale from a scale name.\n */\nexport function get(src: ScaleName | ScaleNameTokens): Scale {\n const tokens = Array.isArray(src) ? src : tokenize(src);\n const tonic = note(tokens[0]).name;\n const st = getScaleType(tokens[1]);\n if (st.empty) {\n return NoScale;\n }\n\n const type = st.name;\n const notes: string[] = tonic\n ? st.intervals.map((i) => transpose(tonic, i))\n : [];\n\n const name = tonic ? tonic + \" \" + type : type;\n\n return { ...st, name, type, tonic, notes };\n}\n\n/**\n * @deprecated\n * @use Scale.get\n */\nexport const scale = get;\n\nexport function detect(\n notes: string[],\n options: { tonic?: string; match?: \"exact\" | \"fit\" } = {},\n): string[] {\n const notesChroma = chroma(notes);\n const tonic = note(options.tonic ?? notes[0] ?? \"\");\n const tonicChroma = tonic.chroma;\n if (tonicChroma === undefined) {\n return [];\n }\n\n const pitchClasses = notesChroma.split(\"\");\n pitchClasses[tonicChroma] = \"1\";\n const scaleChroma = rotate(tonicChroma, pitchClasses).join(\"\");\n const match = all().find((scaleType) => scaleType.chroma === scaleChroma);\n\n const results: string[] = [];\n if (match) {\n results.push(tonic.name + \" \" + match.name);\n }\n if (options.match === \"exact\") {\n return results;\n }\n\n extended(scaleChroma).forEach((scaleName) => {\n results.push(tonic.name + \" \" + scaleName);\n });\n\n return results;\n}\n\n/**\n * Get all chords that fits a given scale\n *\n * @function\n * @param {string} name - the scale name\n * @return {Array<string>} - the chord names\n *\n * @example\n * scaleChords(\"pentatonic\") // => [\"5\", \"64\", \"M\", \"M6\", \"Madd9\", \"Msus2\"]\n */\nexport function scaleChords(name: string): string[] {\n const s = get(name);\n const inScale = isSubsetOf(s.chroma);\n return chordTypes()\n .filter((chord) => inScale(chord.chroma))\n .map((chord) => chord.aliases[0]);\n}\n/**\n * Get all scales names that are a superset of the given one\n * (has the same notes and at least one more)\n *\n * @function\n * @param {string} name\n * @return {Array} a list of scale names\n * @example\n * extended(\"major\") // => [\"bebop\", \"bebop dominant\", \"bebop major\", \"chromatic\", \"ichikosucho\"]\n */\nexport function extended(name: string): string[] {\n const chroma = isChroma(name) ? name : get(name).chroma;\n const isSuperset = isSupersetOf(chroma);\n return scaleTypes()\n .filter((scale) => isSuperset(scale.chroma))\n .map((scale) => scale.name);\n}\n\n/**\n * Find all scales names that are a subset of the given one\n * (has less notes but all from the given scale)\n *\n * @function\n * @param {string} name\n * @return {Array} a list of scale names\n *\n * @example\n * reduced(\"major\") // => [\"ionian pentatonic\", \"major pentatonic\", \"ritusen\"]\n */\nexport function reduced(name: string): string[] {\n const isSubset = isSubsetOf(get(name).chroma);\n return scaleTypes()\n .filter((scale) => isSubset(scale.chroma))\n .map((scale) => scale.name);\n}\n\n/**\n * Given an array of notes, return the scale: a pitch class set starting from\n * the first note of the array\n *\n * @function\n * @param {string[]} notes\n * @return {string[]} pitch classes with same tonic\n * @example\n * scaleNotes(['C4', 'c3', 'C5', 'C4', 'c4']) // => [\"C\"]\n * scaleNotes(['D4', 'c#5', 'A5', 'F#6']) // => [\"D\", \"F#\", \"A\", \"C#\"]\n */\nexport function scaleNotes(notes: NoteName[]) {\n const pcset: string[] = notes.map((n) => note(n).pc).filter((x) => x);\n const tonic = pcset[0];\n const scale = sortedUniqNames(pcset);\n return rotate(scale.indexOf(tonic), scale);\n}\n\ntype ScaleMode = [string, string];\n/**\n * Find mode names of a scale\n *\n * @function\n * @param {string} name - scale name\n * @example\n * modeNames(\"C pentatonic\") // => [\n * [\"C\", \"major pentatonic\"],\n * [\"D\", \"egyptian\"],\n * [\"E\", \"malkos raga\"],\n * [\"G\", \"ritusen\"],\n * [\"A\", \"minor pentatonic\"]\n * ]\n */\nexport function modeNames(name: string): ScaleMode[] {\n const s = get(name);\n if (s.empty) {\n return [];\n }\n\n const tonics = s.tonic ? s.notes : s.intervals;\n return modes(s.chroma)\n .map((chroma: string, i: number): ScaleMode => {\n const modeName = get(chroma).name;\n return modeName ? [tonics[i], modeName] : [\"\", \"\"];\n })\n .filter((x) => x[0]);\n}\n\nfunction getNoteNameOf(scale: string | string[]) {\n const names = Array.isArray(scale) ? scaleNotes(scale) : get(scale).notes;\n const chromas = names.map((name) => note(name).chroma);\n\n return (noteOrMidi: string | number): string | undefined => {\n const currNote =\n typeof noteOrMidi === \"number\"\n ? note(fromMidi(noteOrMidi))\n : note(noteOrMidi);\n const height = currNote.height;\n\n if (height === undefined) return undefined;\n const chroma = height % 12;\n const position = chromas.indexOf(chroma);\n if (position === -1) return undefined;\n return enharmonic(currNote.name, names[position]);\n };\n}\n\nexport function rangeOf(scale: string | string[]) {\n const getName = getNoteNameOf(scale);\n return (fromNote: string, toNote: string) => {\n const from = note(fromNote).height;\n const to = note(toNote).height;\n if (from === undefined || to === undefined) return [];\n\n return nums(from, to)\n .map(getName)\n .filter((x) => x);\n };\n}\n\n/**\n * Returns a function to get a note name from the scale degree.\n *\n * @example\n * [1, 2, 3].map(Scale.degrees(\"C major\")) => [\"C\", \"D\", \"E\"]\n * [1, 2, 3].map(Scale.degrees(\"C4 major\")) => [\"C4\", \"D4\", \"E4\"]\n */\nexport function degrees(scaleName: string | ScaleNameTokens) {\n const { intervals, tonic } = get(scaleName);\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(scaleName: string | ScaleNameTokens) {\n const { intervals, tonic } = get(scaleName);\n return tonicIntervalsTransposer(intervals, tonic);\n}\n\n/** @deprecated */\nexport default {\n degrees,\n detect,\n extended,\n get,\n modeNames,\n names,\n rangeOf,\n reduced,\n scaleChords,\n scaleNotes,\n steps,\n tokenize,\n\n // deprecated\n scale,\n};\n"],"mappings":"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","names":["scaleTypeNames","getScaleType","chordTypes","chroma","scaleTypes","scale","names","nums","transpose"]}
|
package/dist/index.mjs
CHANGED
|
@@ -1,12 +1,6 @@
|
|
|
1
1
|
// index.ts
|
|
2
2
|
import { all as chordTypes } from "@tonaljs/chord-type";
|
|
3
3
|
import { range as nums, rotate } from "@tonaljs/collection";
|
|
4
|
-
import {
|
|
5
|
-
deprecate,
|
|
6
|
-
note,
|
|
7
|
-
tonicIntervalsTransposer,
|
|
8
|
-
transpose
|
|
9
|
-
} from "@tonaljs/core";
|
|
10
4
|
import { enharmonic, fromMidi, sortedUniqNames } from "@tonaljs/note";
|
|
11
5
|
import {
|
|
12
6
|
chroma,
|
|
@@ -15,11 +9,13 @@ import {
|
|
|
15
9
|
isSupersetOf,
|
|
16
10
|
modes
|
|
17
11
|
} from "@tonaljs/pcset";
|
|
12
|
+
import { tonicIntervalsTransposer, transpose } from "@tonaljs/pitch-distance";
|
|
13
|
+
import { note } from "@tonaljs/pitch-note";
|
|
18
14
|
import {
|
|
19
15
|
all,
|
|
20
|
-
all as scaleTypes,
|
|
21
16
|
get as getScaleType,
|
|
22
|
-
names as scaleTypeNames
|
|
17
|
+
names as scaleTypeNames,
|
|
18
|
+
all as scaleTypes
|
|
23
19
|
} from "@tonaljs/scale-type";
|
|
24
20
|
var NoScale = {
|
|
25
21
|
empty: true,
|
|
@@ -59,7 +55,7 @@ function get(src) {
|
|
|
59
55
|
const name = tonic ? tonic + " " + type : type;
|
|
60
56
|
return { ...st, name, type, tonic, notes };
|
|
61
57
|
}
|
|
62
|
-
var scale =
|
|
58
|
+
var scale = get;
|
|
63
59
|
function detect(notes, options = {}) {
|
|
64
60
|
const notesChroma = chroma(notes);
|
|
65
61
|
const tonic = note(options.tonic ?? notes[0] ?? "");
|
|
@@ -120,12 +116,10 @@ function getNoteNameOf(scale2) {
|
|
|
120
116
|
return (noteOrMidi) => {
|
|
121
117
|
const currNote = typeof noteOrMidi === "number" ? note(fromMidi(noteOrMidi)) : note(noteOrMidi);
|
|
122
118
|
const height = currNote.height;
|
|
123
|
-
if (height === void 0)
|
|
124
|
-
return void 0;
|
|
119
|
+
if (height === void 0) return void 0;
|
|
125
120
|
const chroma2 = height % 12;
|
|
126
121
|
const position = chromas.indexOf(chroma2);
|
|
127
|
-
if (position === -1)
|
|
128
|
-
return void 0;
|
|
122
|
+
if (position === -1) return void 0;
|
|
129
123
|
return enharmonic(currNote.name, names2[position]);
|
|
130
124
|
};
|
|
131
125
|
}
|
|
@@ -134,8 +128,7 @@ function rangeOf(scale2) {
|
|
|
134
128
|
return (fromNote, toNote) => {
|
|
135
129
|
const from = note(fromNote).height;
|
|
136
130
|
const to = note(toNote).height;
|
|
137
|
-
if (from === void 0 || to === void 0)
|
|
138
|
-
return [];
|
|
131
|
+
if (from === void 0 || to === void 0) return [];
|
|
139
132
|
return nums(from, to).map(getName).filter((x) => x);
|
|
140
133
|
};
|
|
141
134
|
}
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../index.ts"],"sourcesContent":["/**\n * References:\n * - https://www.researchgate.net/publication/327567188_An_Algorithm_for_Spelling_the_Pitches_of_Any_Musical_Scale\n * @module scale\n */\nimport { all as chordTypes } from \"@tonaljs/chord-type\";\nimport { range as nums, rotate } from \"@tonaljs/collection\";\nimport {\n deprecate,\n note,\n NoteName,\n tonicIntervalsTransposer,\n transpose,\n} from \"@tonaljs/core\";\nimport { enharmonic, fromMidi, sortedUniqNames } from \"@tonaljs/note\";\nimport {\n chroma,\n isChroma,\n isSubsetOf,\n isSupersetOf,\n modes,\n} from \"@tonaljs/pcset\";\nimport {\n all,\n all as scaleTypes,\n get as getScaleType,\n names as scaleTypeNames,\n ScaleType,\n} from \"@tonaljs/scale-type\";\n\ntype ScaleName = string;\ntype ScaleNameTokens = [string, string]; // [TONIC, SCALE TYPE]\n\nexport interface Scale extends ScaleType {\n tonic: string | null;\n type: string;\n notes: NoteName[];\n}\n\nconst NoScale: Scale = {\n empty: true,\n name: \"\",\n type: \"\",\n tonic: null,\n setNum: NaN,\n chroma: \"\",\n normalized: \"\",\n aliases: [],\n notes: [],\n intervals: [],\n};\n\n/**\n * Given a string with a scale name and (optionally) a tonic, split\n * that components.\n *\n * It retuns an array with the form [ name, tonic ] where tonic can be a\n * note name or null and name can be any arbitrary string\n * (this function doesn\"t check if that scale name exists)\n *\n * @function\n * @param {string} name - the scale name\n * @return {Array} an array [tonic, name]\n * @example\n * tokenize(\"C mixolydean\") // => [\"C\", \"mixolydean\"]\n * tokenize(\"anything is valid\") // => [\"\", \"anything is valid\"]\n * tokenize() // => [\"\", \"\"]\n */\nexport function tokenize(name: ScaleName): ScaleNameTokens {\n if (typeof name !== \"string\") {\n return [\"\", \"\"];\n }\n const i = name.indexOf(\" \");\n const tonic = note(name.substring(0, i));\n if (tonic.empty) {\n const n = note(name);\n return n.empty ? [\"\", name] : [n.name, \"\"];\n }\n\n const type = name.substring(tonic.name.length + 1).toLowerCase();\n return [tonic.name, type.length ? type : \"\"];\n}\n\n/**\n * Get all scale names\n * @function\n */\nexport const names = scaleTypeNames;\n\n/**\n * Get a Scale from a scale name.\n */\nexport function get(src: ScaleName | ScaleNameTokens): Scale {\n const tokens = Array.isArray(src) ? src : tokenize(src);\n const tonic = note(tokens[0]).name;\n const st = getScaleType(tokens[1]);\n if (st.empty) {\n return NoScale;\n }\n\n const type = st.name;\n const notes: string[] = tonic\n ? st.intervals.map((i) => transpose(tonic, i))\n : [];\n\n const name = tonic ? tonic + \" \" + type : type;\n\n return { ...st, name, type, tonic, notes };\n}\n\nexport const scale = deprecate(\"Scale.scale\", \"Scale.get\", get);\n\nexport function detect(\n notes: string[],\n options: { tonic?: string; match?: \"exact\" | \"fit\" } = {},\n): string[] {\n const notesChroma = chroma(notes);\n const tonic = note(options.tonic ?? notes[0] ?? \"\");\n const tonicChroma = tonic.chroma;\n if (tonicChroma === undefined) {\n return [];\n }\n\n const pitchClasses = notesChroma.split(\"\");\n pitchClasses[tonicChroma] = \"1\";\n const scaleChroma = rotate(tonicChroma, pitchClasses).join(\"\");\n const match = all().find((scaleType) => scaleType.chroma === scaleChroma);\n\n const results: string[] = [];\n if (match) {\n results.push(tonic.name + \" \" + match.name);\n }\n if (options.match === \"exact\") {\n return results;\n }\n\n extended(scaleChroma).forEach((scaleName) => {\n results.push(tonic.name + \" \" + scaleName);\n });\n\n return results;\n}\n\n/**\n * Get all chords that fits a given scale\n *\n * @function\n * @param {string} name - the scale name\n * @return {Array<string>} - the chord names\n *\n * @example\n * scaleChords(\"pentatonic\") // => [\"5\", \"64\", \"M\", \"M6\", \"Madd9\", \"Msus2\"]\n */\nexport function scaleChords(name: string): string[] {\n const s = get(name);\n const inScale = isSubsetOf(s.chroma);\n return chordTypes()\n .filter((chord) => inScale(chord.chroma))\n .map((chord) => chord.aliases[0]);\n}\n/**\n * Get all scales names that are a superset of the given one\n * (has the same notes and at least one more)\n *\n * @function\n * @param {string} name\n * @return {Array} a list of scale names\n * @example\n * extended(\"major\") // => [\"bebop\", \"bebop dominant\", \"bebop major\", \"chromatic\", \"ichikosucho\"]\n */\nexport function extended(name: string): string[] {\n const chroma = isChroma(name) ? name : get(name).chroma;\n const isSuperset = isSupersetOf(chroma);\n return scaleTypes()\n .filter((scale) => isSuperset(scale.chroma))\n .map((scale) => scale.name);\n}\n\n/**\n * Find all scales names that are a subset of the given one\n * (has less notes but all from the given scale)\n *\n * @function\n * @param {string} name\n * @return {Array} a list of scale names\n *\n * @example\n * reduced(\"major\") // => [\"ionian pentatonic\", \"major pentatonic\", \"ritusen\"]\n */\nexport function reduced(name: string): string[] {\n const isSubset = isSubsetOf(get(name).chroma);\n return scaleTypes()\n .filter((scale) => isSubset(scale.chroma))\n .map((scale) => scale.name);\n}\n\n/**\n * Given an array of notes, return the scale: a pitch class set starting from\n * the first note of the array\n *\n * @function\n * @param {string[]} notes\n * @return {string[]} pitch classes with same tonic\n * @example\n * scaleNotes(['C4', 'c3', 'C5', 'C4', 'c4']) // => [\"C\"]\n * scaleNotes(['D4', 'c#5', 'A5', 'F#6']) // => [\"D\", \"F#\", \"A\", \"C#\"]\n */\nexport function scaleNotes(notes: NoteName[]) {\n const pcset: string[] = notes.map((n) => note(n).pc).filter((x) => x);\n const tonic = pcset[0];\n const scale = sortedUniqNames(pcset);\n return rotate(scale.indexOf(tonic), scale);\n}\n\ntype ScaleMode = [string, string];\n/**\n * Find mode names of a scale\n *\n * @function\n * @param {string} name - scale name\n * @example\n * modeNames(\"C pentatonic\") // => [\n * [\"C\", \"major pentatonic\"],\n * [\"D\", \"egyptian\"],\n * [\"E\", \"malkos raga\"],\n * [\"G\", \"ritusen\"],\n * [\"A\", \"minor pentatonic\"]\n * ]\n */\nexport function modeNames(name: string): ScaleMode[] {\n const s = get(name);\n if (s.empty) {\n return [];\n }\n\n const tonics = s.tonic ? s.notes : s.intervals;\n return modes(s.chroma)\n .map((chroma: string, i: number): ScaleMode => {\n const modeName = get(chroma).name;\n return modeName ? [tonics[i], modeName] : [\"\", \"\"];\n })\n .filter((x) => x[0]);\n}\n\nfunction getNoteNameOf(scale: string | string[]) {\n const names = Array.isArray(scale) ? scaleNotes(scale) : get(scale).notes;\n const chromas = names.map((name) => note(name).chroma);\n\n return (noteOrMidi: string | number): string | undefined => {\n const currNote =\n typeof noteOrMidi === \"number\"\n ? note(fromMidi(noteOrMidi))\n : note(noteOrMidi);\n const height = currNote.height;\n\n if (height === undefined) return undefined;\n const chroma = height % 12;\n const position = chromas.indexOf(chroma);\n if (position === -1) return undefined;\n return enharmonic(currNote.name, names[position]);\n };\n}\n\nexport function rangeOf(scale: string | string[]) {\n const getName = getNoteNameOf(scale);\n return (fromNote: string, toNote: string) => {\n const from = note(fromNote).height;\n const to = note(toNote).height;\n if (from === undefined || to === undefined) return [];\n\n return nums(from, to)\n .map(getName)\n .filter((x) => x);\n };\n}\n\n/**\n * Returns a function to get a note name from the scale degree.\n *\n * @example\n * [1, 2, 3].map(Scale.degrees(\"C major\")) => [\"C\", \"D\", \"E\"]\n * [1, 2, 3].map(Scale.degrees(\"C4 major\")) => [\"C4\", \"D4\", \"E4\"]\n */\nexport function degrees(scaleName: string | ScaleNameTokens) {\n const { intervals, tonic } = get(scaleName);\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(scaleName: string | ScaleNameTokens) {\n const { intervals, tonic } = get(scaleName);\n return tonicIntervalsTransposer(intervals, tonic);\n}\n\nexport default {\n degrees,\n detect,\n extended,\n get,\n modeNames,\n names,\n rangeOf,\n reduced,\n scaleChords,\n scaleNotes,\n steps,\n tokenize,\n\n // deprecated\n scale,\n};\n"],"mappings":"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","names":["chroma","scale","names","transpose"]}
|
|
1
|
+
{"version":3,"sources":["../index.ts"],"sourcesContent":["/**\n * References:\n * - https://www.researchgate.net/publication/327567188_An_Algorithm_for_Spelling_the_Pitches_of_Any_Musical_Scale\n * @module scale\n */\nimport { all as chordTypes } from \"@tonaljs/chord-type\";\nimport { range as nums, rotate } from \"@tonaljs/collection\";\nimport { enharmonic, fromMidi, sortedUniqNames } from \"@tonaljs/note\";\nimport {\n chroma,\n isChroma,\n isSubsetOf,\n isSupersetOf,\n modes,\n} from \"@tonaljs/pcset\";\nimport { tonicIntervalsTransposer, transpose } from \"@tonaljs/pitch-distance\";\nimport { note, NoteName } from \"@tonaljs/pitch-note\";\nimport {\n all,\n get as getScaleType,\n ScaleType,\n names as scaleTypeNames,\n all as scaleTypes,\n} from \"@tonaljs/scale-type\";\n\ntype ScaleName = string;\ntype ScaleNameTokens = [string, string]; // [TONIC, SCALE TYPE]\n\nexport interface Scale extends ScaleType {\n tonic: string | null;\n type: string;\n notes: NoteName[];\n}\n\nconst NoScale: Scale = {\n empty: true,\n name: \"\",\n type: \"\",\n tonic: null,\n setNum: NaN,\n chroma: \"\",\n normalized: \"\",\n aliases: [],\n notes: [],\n intervals: [],\n};\n\n/**\n * Given a string with a scale name and (optionally) a tonic, split\n * that components.\n *\n * It retuns an array with the form [ name, tonic ] where tonic can be a\n * note name or null and name can be any arbitrary string\n * (this function doesn\"t check if that scale name exists)\n *\n * @function\n * @param {string} name - the scale name\n * @return {Array} an array [tonic, name]\n * @example\n * tokenize(\"C mixolydean\") // => [\"C\", \"mixolydean\"]\n * tokenize(\"anything is valid\") // => [\"\", \"anything is valid\"]\n * tokenize() // => [\"\", \"\"]\n */\nexport function tokenize(name: ScaleName): ScaleNameTokens {\n if (typeof name !== \"string\") {\n return [\"\", \"\"];\n }\n const i = name.indexOf(\" \");\n const tonic = note(name.substring(0, i));\n if (tonic.empty) {\n const n = note(name);\n return n.empty ? [\"\", name] : [n.name, \"\"];\n }\n\n const type = name.substring(tonic.name.length + 1).toLowerCase();\n return [tonic.name, type.length ? type : \"\"];\n}\n\n/**\n * Get all scale names\n * @function\n */\nexport const names = scaleTypeNames;\n\n/**\n * Get a Scale from a scale name.\n */\nexport function get(src: ScaleName | ScaleNameTokens): Scale {\n const tokens = Array.isArray(src) ? src : tokenize(src);\n const tonic = note(tokens[0]).name;\n const st = getScaleType(tokens[1]);\n if (st.empty) {\n return NoScale;\n }\n\n const type = st.name;\n const notes: string[] = tonic\n ? st.intervals.map((i) => transpose(tonic, i))\n : [];\n\n const name = tonic ? tonic + \" \" + type : type;\n\n return { ...st, name, type, tonic, notes };\n}\n\n/**\n * @deprecated\n * @use Scale.get\n */\nexport const scale = get;\n\nexport function detect(\n notes: string[],\n options: { tonic?: string; match?: \"exact\" | \"fit\" } = {},\n): string[] {\n const notesChroma = chroma(notes);\n const tonic = note(options.tonic ?? notes[0] ?? \"\");\n const tonicChroma = tonic.chroma;\n if (tonicChroma === undefined) {\n return [];\n }\n\n const pitchClasses = notesChroma.split(\"\");\n pitchClasses[tonicChroma] = \"1\";\n const scaleChroma = rotate(tonicChroma, pitchClasses).join(\"\");\n const match = all().find((scaleType) => scaleType.chroma === scaleChroma);\n\n const results: string[] = [];\n if (match) {\n results.push(tonic.name + \" \" + match.name);\n }\n if (options.match === \"exact\") {\n return results;\n }\n\n extended(scaleChroma).forEach((scaleName) => {\n results.push(tonic.name + \" \" + scaleName);\n });\n\n return results;\n}\n\n/**\n * Get all chords that fits a given scale\n *\n * @function\n * @param {string} name - the scale name\n * @return {Array<string>} - the chord names\n *\n * @example\n * scaleChords(\"pentatonic\") // => [\"5\", \"64\", \"M\", \"M6\", \"Madd9\", \"Msus2\"]\n */\nexport function scaleChords(name: string): string[] {\n const s = get(name);\n const inScale = isSubsetOf(s.chroma);\n return chordTypes()\n .filter((chord) => inScale(chord.chroma))\n .map((chord) => chord.aliases[0]);\n}\n/**\n * Get all scales names that are a superset of the given one\n * (has the same notes and at least one more)\n *\n * @function\n * @param {string} name\n * @return {Array} a list of scale names\n * @example\n * extended(\"major\") // => [\"bebop\", \"bebop dominant\", \"bebop major\", \"chromatic\", \"ichikosucho\"]\n */\nexport function extended(name: string): string[] {\n const chroma = isChroma(name) ? name : get(name).chroma;\n const isSuperset = isSupersetOf(chroma);\n return scaleTypes()\n .filter((scale) => isSuperset(scale.chroma))\n .map((scale) => scale.name);\n}\n\n/**\n * Find all scales names that are a subset of the given one\n * (has less notes but all from the given scale)\n *\n * @function\n * @param {string} name\n * @return {Array} a list of scale names\n *\n * @example\n * reduced(\"major\") // => [\"ionian pentatonic\", \"major pentatonic\", \"ritusen\"]\n */\nexport function reduced(name: string): string[] {\n const isSubset = isSubsetOf(get(name).chroma);\n return scaleTypes()\n .filter((scale) => isSubset(scale.chroma))\n .map((scale) => scale.name);\n}\n\n/**\n * Given an array of notes, return the scale: a pitch class set starting from\n * the first note of the array\n *\n * @function\n * @param {string[]} notes\n * @return {string[]} pitch classes with same tonic\n * @example\n * scaleNotes(['C4', 'c3', 'C5', 'C4', 'c4']) // => [\"C\"]\n * scaleNotes(['D4', 'c#5', 'A5', 'F#6']) // => [\"D\", \"F#\", \"A\", \"C#\"]\n */\nexport function scaleNotes(notes: NoteName[]) {\n const pcset: string[] = notes.map((n) => note(n).pc).filter((x) => x);\n const tonic = pcset[0];\n const scale = sortedUniqNames(pcset);\n return rotate(scale.indexOf(tonic), scale);\n}\n\ntype ScaleMode = [string, string];\n/**\n * Find mode names of a scale\n *\n * @function\n * @param {string} name - scale name\n * @example\n * modeNames(\"C pentatonic\") // => [\n * [\"C\", \"major pentatonic\"],\n * [\"D\", \"egyptian\"],\n * [\"E\", \"malkos raga\"],\n * [\"G\", \"ritusen\"],\n * [\"A\", \"minor pentatonic\"]\n * ]\n */\nexport function modeNames(name: string): ScaleMode[] {\n const s = get(name);\n if (s.empty) {\n return [];\n }\n\n const tonics = s.tonic ? s.notes : s.intervals;\n return modes(s.chroma)\n .map((chroma: string, i: number): ScaleMode => {\n const modeName = get(chroma).name;\n return modeName ? [tonics[i], modeName] : [\"\", \"\"];\n })\n .filter((x) => x[0]);\n}\n\nfunction getNoteNameOf(scale: string | string[]) {\n const names = Array.isArray(scale) ? scaleNotes(scale) : get(scale).notes;\n const chromas = names.map((name) => note(name).chroma);\n\n return (noteOrMidi: string | number): string | undefined => {\n const currNote =\n typeof noteOrMidi === \"number\"\n ? note(fromMidi(noteOrMidi))\n : note(noteOrMidi);\n const height = currNote.height;\n\n if (height === undefined) return undefined;\n const chroma = height % 12;\n const position = chromas.indexOf(chroma);\n if (position === -1) return undefined;\n return enharmonic(currNote.name, names[position]);\n };\n}\n\nexport function rangeOf(scale: string | string[]) {\n const getName = getNoteNameOf(scale);\n return (fromNote: string, toNote: string) => {\n const from = note(fromNote).height;\n const to = note(toNote).height;\n if (from === undefined || to === undefined) return [];\n\n return nums(from, to)\n .map(getName)\n .filter((x) => x);\n };\n}\n\n/**\n * Returns a function to get a note name from the scale degree.\n *\n * @example\n * [1, 2, 3].map(Scale.degrees(\"C major\")) => [\"C\", \"D\", \"E\"]\n * [1, 2, 3].map(Scale.degrees(\"C4 major\")) => [\"C4\", \"D4\", \"E4\"]\n */\nexport function degrees(scaleName: string | ScaleNameTokens) {\n const { intervals, tonic } = get(scaleName);\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(scaleName: string | ScaleNameTokens) {\n const { intervals, tonic } = get(scaleName);\n return tonicIntervalsTransposer(intervals, tonic);\n}\n\n/** @deprecated */\nexport default {\n degrees,\n detect,\n extended,\n get,\n modeNames,\n names,\n rangeOf,\n reduced,\n scaleChords,\n scaleNotes,\n steps,\n tokenize,\n\n // deprecated\n scale,\n};\n"],"mappings":";AAKA,SAAS,OAAO,kBAAkB;AAClC,SAAS,SAAS,MAAM,cAAc;AACtC,SAAS,YAAY,UAAU,uBAAuB;AACtD;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,0BAA0B,iBAAiB;AACpD,SAAS,YAAsB;AAC/B;AAAA,EACE;AAAA,EACA,OAAO;AAAA,EAEP,SAAS;AAAA,EACT,OAAO;AAAA,OACF;AAWP,IAAM,UAAiB;AAAA,EACrB,OAAO;AAAA,EACP,MAAM;AAAA,EACN,MAAM;AAAA,EACN,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,YAAY;AAAA,EACZ,SAAS,CAAC;AAAA,EACV,OAAO,CAAC;AAAA,EACR,WAAW,CAAC;AACd;AAkBO,SAAS,SAAS,MAAkC;AACzD,MAAI,OAAO,SAAS,UAAU;AAC5B,WAAO,CAAC,IAAI,EAAE;AAAA,EAChB;AACA,QAAM,IAAI,KAAK,QAAQ,GAAG;AAC1B,QAAM,QAAQ,KAAK,KAAK,UAAU,GAAG,CAAC,CAAC;AACvC,MAAI,MAAM,OAAO;AACf,UAAM,IAAI,KAAK,IAAI;AACnB,WAAO,EAAE,QAAQ,CAAC,IAAI,IAAI,IAAI,CAAC,EAAE,MAAM,EAAE;AAAA,EAC3C;AAEA,QAAM,OAAO,KAAK,UAAU,MAAM,KAAK,SAAS,CAAC,EAAE,YAAY;AAC/D,SAAO,CAAC,MAAM,MAAM,KAAK,SAAS,OAAO,EAAE;AAC7C;AAMO,IAAM,QAAQ;AAKd,SAAS,IAAI,KAAyC;AAC3D,QAAM,SAAS,MAAM,QAAQ,GAAG,IAAI,MAAM,SAAS,GAAG;AACtD,QAAM,QAAQ,KAAK,OAAO,CAAC,CAAC,EAAE;AAC9B,QAAM,KAAK,aAAa,OAAO,CAAC,CAAC;AACjC,MAAI,GAAG,OAAO;AACZ,WAAO;AAAA,EACT;AAEA,QAAM,OAAO,GAAG;AAChB,QAAM,QAAkB,QACpB,GAAG,UAAU,IAAI,CAAC,MAAM,UAAU,OAAO,CAAC,CAAC,IAC3C,CAAC;AAEL,QAAM,OAAO,QAAQ,QAAQ,MAAM,OAAO;AAE1C,SAAO,EAAE,GAAG,IAAI,MAAM,MAAM,OAAO,MAAM;AAC3C;AAMO,IAAM,QAAQ;AAEd,SAAS,OACd,OACA,UAAuD,CAAC,GAC9C;AACV,QAAM,cAAc,OAAO,KAAK;AAChC,QAAM,QAAQ,KAAK,QAAQ,SAAS,MAAM,CAAC,KAAK,EAAE;AAClD,QAAM,cAAc,MAAM;AAC1B,MAAI,gBAAgB,QAAW;AAC7B,WAAO,CAAC;AAAA,EACV;AAEA,QAAM,eAAe,YAAY,MAAM,EAAE;AACzC,eAAa,WAAW,IAAI;AAC5B,QAAM,cAAc,OAAO,aAAa,YAAY,EAAE,KAAK,EAAE;AAC7D,QAAM,QAAQ,IAAI,EAAE,KAAK,CAAC,cAAc,UAAU,WAAW,WAAW;AAExE,QAAM,UAAoB,CAAC;AAC3B,MAAI,OAAO;AACT,YAAQ,KAAK,MAAM,OAAO,MAAM,MAAM,IAAI;AAAA,EAC5C;AACA,MAAI,QAAQ,UAAU,SAAS;AAC7B,WAAO;AAAA,EACT;AAEA,WAAS,WAAW,EAAE,QAAQ,CAAC,cAAc;AAC3C,YAAQ,KAAK,MAAM,OAAO,MAAM,SAAS;AAAA,EAC3C,CAAC;AAED,SAAO;AACT;AAYO,SAAS,YAAY,MAAwB;AAClD,QAAM,IAAI,IAAI,IAAI;AAClB,QAAM,UAAU,WAAW,EAAE,MAAM;AACnC,SAAO,WAAW,EACf,OAAO,CAAC,UAAU,QAAQ,MAAM,MAAM,CAAC,EACvC,IAAI,CAAC,UAAU,MAAM,QAAQ,CAAC,CAAC;AACpC;AAWO,SAAS,SAAS,MAAwB;AAC/C,QAAMA,UAAS,SAAS,IAAI,IAAI,OAAO,IAAI,IAAI,EAAE;AACjD,QAAM,aAAa,aAAaA,OAAM;AACtC,SAAO,WAAW,EACf,OAAO,CAACC,WAAU,WAAWA,OAAM,MAAM,CAAC,EAC1C,IAAI,CAACA,WAAUA,OAAM,IAAI;AAC9B;AAaO,SAAS,QAAQ,MAAwB;AAC9C,QAAM,WAAW,WAAW,IAAI,IAAI,EAAE,MAAM;AAC5C,SAAO,WAAW,EACf,OAAO,CAACA,WAAU,SAASA,OAAM,MAAM,CAAC,EACxC,IAAI,CAACA,WAAUA,OAAM,IAAI;AAC9B;AAaO,SAAS,WAAW,OAAmB;AAC5C,QAAM,QAAkB,MAAM,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,EAAE,EAAE,OAAO,CAAC,MAAM,CAAC;AACpE,QAAM,QAAQ,MAAM,CAAC;AACrB,QAAMA,SAAQ,gBAAgB,KAAK;AACnC,SAAO,OAAOA,OAAM,QAAQ,KAAK,GAAGA,MAAK;AAC3C;AAiBO,SAAS,UAAU,MAA2B;AACnD,QAAM,IAAI,IAAI,IAAI;AAClB,MAAI,EAAE,OAAO;AACX,WAAO,CAAC;AAAA,EACV;AAEA,QAAM,SAAS,EAAE,QAAQ,EAAE,QAAQ,EAAE;AACrC,SAAO,MAAM,EAAE,MAAM,EAClB,IAAI,CAACD,SAAgB,MAAyB;AAC7C,UAAM,WAAW,IAAIA,OAAM,EAAE;AAC7B,WAAO,WAAW,CAAC,OAAO,CAAC,GAAG,QAAQ,IAAI,CAAC,IAAI,EAAE;AAAA,EACnD,CAAC,EACA,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;AACvB;AAEA,SAAS,cAAcC,QAA0B;AAC/C,QAAMC,SAAQ,MAAM,QAAQD,MAAK,IAAI,WAAWA,MAAK,IAAI,IAAIA,MAAK,EAAE;AACpE,QAAM,UAAUC,OAAM,IAAI,CAAC,SAAS,KAAK,IAAI,EAAE,MAAM;AAErD,SAAO,CAAC,eAAoD;AAC1D,UAAM,WACJ,OAAO,eAAe,WAClB,KAAK,SAAS,UAAU,CAAC,IACzB,KAAK,UAAU;AACrB,UAAM,SAAS,SAAS;AAExB,QAAI,WAAW,OAAW,QAAO;AACjC,UAAMF,UAAS,SAAS;AACxB,UAAM,WAAW,QAAQ,QAAQA,OAAM;AACvC,QAAI,aAAa,GAAI,QAAO;AAC5B,WAAO,WAAW,SAAS,MAAME,OAAM,QAAQ,CAAC;AAAA,EAClD;AACF;AAEO,SAAS,QAAQD,QAA0B;AAChD,QAAM,UAAU,cAAcA,MAAK;AACnC,SAAO,CAAC,UAAkB,WAAmB;AAC3C,UAAM,OAAO,KAAK,QAAQ,EAAE;AAC5B,UAAM,KAAK,KAAK,MAAM,EAAE;AACxB,QAAI,SAAS,UAAa,OAAO,OAAW,QAAO,CAAC;AAEpD,WAAO,KAAK,MAAM,EAAE,EACjB,IAAI,OAAO,EACX,OAAO,CAAC,MAAM,CAAC;AAAA,EACpB;AACF;AASO,SAAS,QAAQ,WAAqC;AAC3D,QAAM,EAAE,WAAW,MAAM,IAAI,IAAI,SAAS;AAC1C,QAAME,aAAY,yBAAyB,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,SAAO,yBAAyB,WAAW,KAAK;AAClD;AAGA,IAAO,gBAAQ;AAAA,EACb;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAGA;AACF;","names":["chroma","scale","names","transpose"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tonaljs/scale",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.13.0",
|
|
4
4
|
"description": "Musical scales and its relations",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"scale",
|
|
@@ -16,12 +16,13 @@
|
|
|
16
16
|
],
|
|
17
17
|
"types": "dist/index.d.ts",
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@tonaljs/chord-type": "5.0
|
|
20
|
-
"@tonaljs/collection": "4.
|
|
21
|
-
"@tonaljs/
|
|
22
|
-
"@tonaljs/note": "
|
|
23
|
-
"@tonaljs/
|
|
24
|
-
"@tonaljs/
|
|
19
|
+
"@tonaljs/chord-type": "5.1.0",
|
|
20
|
+
"@tonaljs/collection": "4.9.0",
|
|
21
|
+
"@tonaljs/pitch-distance": "5.0.4",
|
|
22
|
+
"@tonaljs/pitch-note": "6.0.0",
|
|
23
|
+
"@tonaljs/note": "4.11.0",
|
|
24
|
+
"@tonaljs/pcset": "4.10.0",
|
|
25
|
+
"@tonaljs/scale-type": "4.9.0"
|
|
25
26
|
},
|
|
26
27
|
"author": "danigb@gmail.com",
|
|
27
28
|
"license": "MIT",
|