@tonaljs/key
Version:
Functions to work with musical keys
257 lines (256 loc) • 8.4 kB
JavaScript
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// index.ts
var index_exports = {};
__export(index_exports, {
default: () => index_default,
majorKey: () => majorKey,
majorKeyChords: () => majorKeyChords,
majorTonicFromKeySignature: () => majorTonicFromKeySignature,
minorKey: () => minorKey,
minorKeyChords: () => minorKeyChords
});
module.exports = __toCommonJS(index_exports);
var import_note = require("@tonaljs/note");
var import_pitch_note = require("@tonaljs/pitch-note");
var import_roman_numeral = require("@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) => (0, import_roman_numeral.get)(gr).interval || "");
const scale = intervals.map((interval) => (0, import_note.transpose)(tonic, interval));
const chords = mapScaleToType(scale, chordTypes);
const secondaryDominants = scale.map((note2) => (0, import_note.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 = (0, import_note.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 = (0, import_note.transpose)(domRoot, "5P");
const target = targetTriads[index];
const isMinor = target.endsWith("m");
return isMinor ? minorRoot + "m7" : minorRoot + "m7b5";
});
};
var distInFifths = (from, to) => {
const f = (0, import_pitch_note.note)(from);
const t = (0, import_pitch_note.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 = (0, import_pitch_note.note)(tonic).pc;
if (!pc) return NoMajorKey;
const keyScale2 = MajorScale(pc);
const alteration = distInFifths("C", pc);
return {
...keyScale2,
type: "major",
minorRelative: (0, import_note.transpose)(pc, "-3m"),
alteration,
keySignature: (0, import_pitch_note.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 = (0, import_pitch_note.note)(tnc).pc;
if (!pc) return NoMinorKey;
const alteration = distInFifths("C", pc) - 3;
return {
type: "minor",
tonic: pc,
relativeMajor: (0, import_note.transpose)(pc, "3m"),
alteration,
keySignature: (0, import_pitch_note.altToAcc)(alteration),
natural: NaturalScale(pc),
harmonic: HarmonicScale(pc),
melodic: MelodicScale(pc)
};
}
function majorTonicFromKeySignature(sig) {
if (typeof sig === "number") {
return (0, import_note.transposeFifths)("C", sig);
} else if (typeof sig === "string" && /^b+|#+$/.test(sig)) {
return (0, import_note.transposeFifths)("C", (0, import_pitch_note.accToAlt)(sig));
}
return null;
}
var index_default = { majorKey, majorTonicFromKeySignature, minorKey };
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
majorKey,
majorKeyChords,
majorTonicFromKeySignature,
minorKey,
minorKeyChords
});
//# sourceMappingURL=index.js.map
;