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