UNPKG

@tonaljs/key

Version:

Functions to work with musical keys

228 lines 6.91 kB
// 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