@tonaljs/key
Version:
Functions to work with musical keys
228 lines • 6.91 kB
JavaScript
// index.ts
import { transpose, transposeFifths } from "@tonaljs/note";
import { accToAlt, altToAcc, note } from "@tonaljs/pitch-note";
import { get as roman } from "@tonaljs/roman-numeral";
var Empty = Object.freeze([]);
var NoKey = {
type: "major",
tonic: "",
alteration: 0,
keySignature: ""
};
var NoKeyScale = {
tonic: "",
grades: Empty,
intervals: Empty,
scale: Empty,
triads: Empty,
chords: Empty,
chordsHarmonicFunction: Empty,
chordScales: Empty,
secondaryDominants: Empty,
secondaryDominantSupertonics: Empty,
substituteDominantsMinorRelative: Empty,
substituteDominants: Empty,
substituteDominantSupertonics: Empty,
secondaryDominantsMinorRelative: Empty
};
var NoMajorKey = {
...NoKey,
...NoKeyScale,
type: "major",
minorRelative: "",
scale: Empty,
substituteDominants: Empty,
secondaryDominantSupertonics: Empty,
substituteDominantsMinorRelative: Empty
};
var NoMinorKey = {
...NoKey,
type: "minor",
relativeMajor: "",
natural: NoKeyScale,
harmonic: NoKeyScale,
melodic: NoKeyScale
};
var mapScaleToType = (scale, list, sep = "") => list.map((type, i) => `${scale[i]}${sep}${type}`);
function keyScale(grades, triads, chordTypes, harmonicFunctions, chordScales) {
return (tonic) => {
const intervals = grades.map((gr) => roman(gr).interval || "");
const scale = intervals.map((interval) => transpose(tonic, interval));
const chords = mapScaleToType(scale, chordTypes);
const secondaryDominants = scale.map((note2) => transpose(note2, "5P")).map(
(note2) => (
// A secondary dominant is a V chord which:
// 1. is not diatonic to the key,
// 2. it must have a diatonic root.
scale.includes(note2) && !chords.includes(note2 + "7") ? note2 + "7" : ""
)
);
const secondaryDominantSupertonics = supertonics(
secondaryDominants,
triads
);
const substituteDominants = secondaryDominants.map((chord) => {
if (!chord) return "";
const domRoot = chord.slice(0, -1);
const subRoot = transpose(domRoot, "5d");
return subRoot + "7";
});
const substituteDominantSupertonics = supertonics(
substituteDominants,
triads
);
return {
tonic,
grades,
intervals,
scale,
triads: mapScaleToType(scale, triads),
chords,
chordsHarmonicFunction: harmonicFunctions.slice(),
chordScales: mapScaleToType(scale, chordScales, " "),
secondaryDominants,
secondaryDominantSupertonics,
substituteDominants,
substituteDominantSupertonics,
// @deprecated use secondaryDominantsSupertonic
secondaryDominantsMinorRelative: secondaryDominantSupertonics,
// @deprecated use secondaryDominantsSupertonic
substituteDominantsMinorRelative: substituteDominantSupertonics
};
};
}
var supertonics = (dominants, targetTriads) => {
return dominants.map((chord, index) => {
if (!chord) return "";
const domRoot = chord.slice(0, -1);
const minorRoot = transpose(domRoot, "5P");
const target = targetTriads[index];
const isMinor = target.endsWith("m");
return isMinor ? minorRoot + "m7" : minorRoot + "m7b5";
});
};
var distInFifths = (from, to) => {
const f = note(from);
const t = note(to);
return f.empty || t.empty ? 0 : t.coord[0] - f.coord[0];
};
var MajorScale = keyScale(
"I II III IV V VI VII".split(" "),
" m m m dim".split(" "),
"maj7 m7 m7 maj7 7 m7 m7b5".split(" "),
"T SD T SD D T D".split(" "),
"major,dorian,phrygian,lydian,mixolydian,minor,locrian".split(",")
);
var NaturalScale = keyScale(
"I II bIII IV V bVI bVII".split(" "),
"m dim m m ".split(" "),
"m7 m7b5 maj7 m7 m7 maj7 7".split(" "),
"T SD T SD D SD SD".split(" "),
"minor,locrian,major,dorian,phrygian,lydian,mixolydian".split(",")
);
var HarmonicScale = keyScale(
"I II bIII IV V bVI VII".split(" "),
"m dim aug m dim".split(" "),
"mMaj7 m7b5 +maj7 m7 7 maj7 o7".split(" "),
"T SD T SD D SD D".split(" "),
"harmonic minor,locrian 6,major augmented,lydian diminished,phrygian dominant,lydian #9,ultralocrian".split(
","
)
);
var MelodicScale = keyScale(
"I II bIII IV V VI VII".split(" "),
"m m aug dim dim".split(" "),
"m6 m7 +maj7 7 7 m7b5 m7b5".split(" "),
"T SD T SD D ".split(" "),
"melodic minor,dorian b2,lydian augmented,lydian dominant,mixolydian b6,locrian #2,altered".split(
","
)
);
function majorKey(tonic) {
const pc = note(tonic).pc;
if (!pc) return NoMajorKey;
const keyScale2 = MajorScale(pc);
const alteration = distInFifths("C", pc);
return {
...keyScale2,
type: "major",
minorRelative: transpose(pc, "-3m"),
alteration,
keySignature: altToAcc(alteration)
};
}
function majorKeyChords(tonic) {
const key = majorKey(tonic);
const chords = [];
keyChordsOf(key, chords);
return chords;
}
function minorKeyChords(tonic) {
const key = minorKey(tonic);
const chords = [];
keyChordsOf(key.natural, chords);
keyChordsOf(key.harmonic, chords);
keyChordsOf(key.melodic, chords);
return chords;
}
function keyChordsOf(key, chords) {
const updateChord = (name, newRole) => {
if (!name) return;
let keyChord = chords.find((chord) => chord.name === name);
if (!keyChord) {
keyChord = { name, roles: [] };
chords.push(keyChord);
}
if (newRole && !keyChord.roles.includes(newRole)) {
keyChord.roles.push(newRole);
}
};
key.chords.forEach(
(chordName, index) => updateChord(chordName, key.chordsHarmonicFunction[index])
);
key.secondaryDominants.forEach(
(chordName, index) => updateChord(chordName, `V/${key.grades[index]}`)
);
key.secondaryDominantSupertonics.forEach(
(chordName, index) => updateChord(chordName, `ii/${key.grades[index]}`)
);
key.substituteDominants.forEach(
(chordName, index) => updateChord(chordName, `subV/${key.grades[index]}`)
);
key.substituteDominantSupertonics.forEach(
(chordName, index) => updateChord(chordName, `subii/${key.grades[index]}`)
);
}
function minorKey(tnc) {
const pc = note(tnc).pc;
if (!pc) return NoMinorKey;
const alteration = distInFifths("C", pc) - 3;
return {
type: "minor",
tonic: pc,
relativeMajor: transpose(pc, "3m"),
alteration,
keySignature: altToAcc(alteration),
natural: NaturalScale(pc),
harmonic: HarmonicScale(pc),
melodic: MelodicScale(pc)
};
}
function majorTonicFromKeySignature(sig) {
if (typeof sig === "number") {
return transposeFifths("C", sig);
} else if (typeof sig === "string" && /^b+|#+$/.test(sig)) {
return transposeFifths("C", accToAlt(sig));
}
return null;
}
var index_default = { majorKey, majorTonicFromKeySignature, minorKey };
export {
index_default as default,
majorKey,
majorKeyChords,
majorTonicFromKeySignature,
minorKey,
minorKeyChords
};
//# sourceMappingURL=index.mjs.map