@tonaljs/chord
Version:
Musical chords and its relations
1 lines • 13 kB
Source Map (JSON)
{"version":3,"sources":["../index.ts"],"sourcesContent":["import { detect } from \"@tonaljs/chord-detect\";\nimport {\n ChordType,\n all as chordTypes,\n get as getChordType,\n} from \"@tonaljs/chord-type\";\nimport { subtract } from \"@tonaljs/interval\";\nimport { isSubsetOf, isSupersetOf } from \"@tonaljs/pcset\";\nimport {\n distance,\n tonicIntervalsTransposer,\n transpose as transposeNote,\n} from \"@tonaljs/pitch-distance\";\nimport { NoteName, note, tokenizeNote } from \"@tonaljs/pitch-note\";\nimport { all as scaleTypes } from \"@tonaljs/scale-type\";\n\nexport { detect } from \"@tonaljs/chord-detect\";\n\ntype ChordNameOrTokens =\n | string // full name to be parsed\n | [string] // only the name\n | [string, string] // tonic, name\n | [string, string, string]; // tonic, name, bass\ntype ChordNameTokens = [string, string, string]; // [TONIC, SCALE TYPE, BASS]\n\nexport interface Chord extends ChordType {\n tonic: string | null;\n type: string;\n root: string;\n bass: string;\n rootDegree: number;\n symbol: string;\n notes: NoteName[];\n}\n\nconst NoChord: Chord = {\n empty: true,\n name: \"\",\n symbol: \"\",\n root: \"\",\n bass: \"\",\n rootDegree: 0,\n type: \"\",\n tonic: null,\n setNum: NaN,\n quality: \"Unknown\",\n chroma: \"\",\n normalized: \"\",\n aliases: [],\n notes: [],\n intervals: [],\n};\n\n// 6, 64, 7, 9, 11 and 13 are consider part of the chord\n// (see https://github.com/danigb/tonal/issues/55)\n//const NUM_TYPES = /^(6|64|7|9|11|13)$/;\n/**\n * Tokenize a chord name. It returns an array with the tonic, chord type and bass\n * If not tonic is found, all the name is considered the chord name.\n *\n * This function does NOT check if the chord type exists or not. It only tries\n * to split the tonic and chord type.\n *\n * This function does NOT check if the bass is part of the chord or not but it\n * only accepts a pitch class as bass\n *\n * @function\n * @param {string} name - the chord name\n * @return {Array} an array with [tonic, type, bass]\n * @example\n * tokenize(\"Cmaj7\") // => [ \"C\", \"maj7\" ]\n * tokenize(\"C7\") // => [ \"C\", \"7\" ]\n * tokenize(\"mMaj7\") // => [ null, \"mMaj7\" ]\n * tokenize(\"Cnonsense\") // => [ null, \"nonsense\" ]\n */\nexport function tokenize(name: string): ChordNameTokens {\n const [letter, acc, oct, type] = tokenizeNote(name);\n if (letter === \"\") {\n return tokenizeBass(\"\", name);\n } else if (letter === \"A\" && type === \"ug\") {\n return tokenizeBass(\"\", \"aug\");\n } else {\n return tokenizeBass(letter + acc, oct + type);\n }\n}\n\nfunction tokenizeBass(note: string, chord: string): ChordNameTokens {\n const split = chord.split(\"/\");\n if (split.length === 1) {\n return [note, split[0], \"\"];\n }\n const [letter, acc, oct, type] = tokenizeNote(split[1]);\n // Only a pitch class is accepted as bass note\n if (letter !== \"\" && oct === \"\" && type === \"\") {\n return [note, split[0], letter + acc];\n } else {\n return [note, chord, \"\"];\n }\n}\n\n/**\n * Get a Chord from a chord name.\n */\nexport function get(src: ChordNameOrTokens): Chord {\n if (Array.isArray(src)) {\n return getChord(src[1] || \"\", src[0], src[2]);\n } else if (src === \"\") {\n return NoChord;\n } else {\n const [tonic, type, bass] = tokenize(src);\n const chord = getChord(type, tonic, bass);\n return chord.empty ? getChord(src) : chord;\n }\n}\n\n/**\n * Get chord properties\n *\n * @param typeName - the chord type name\n * @param [tonic] - Optional tonic\n * @param [root] - Optional root (requires a tonic)\n */\nexport function getChord(\n typeName: string,\n optionalTonic?: string,\n optionalBass?: string,\n): Chord {\n const type = getChordType(typeName);\n const tonic = note(optionalTonic || \"\");\n const bass = note(optionalBass || \"\");\n\n if (\n type.empty ||\n (optionalTonic && tonic.empty) ||\n (optionalBass && bass.empty)\n ) {\n return NoChord;\n }\n\n const bassInterval = distance(tonic.pc, bass.pc);\n const bassIndex = type.intervals.indexOf(bassInterval);\n const hasRoot = bassIndex >= 0;\n const root = hasRoot ? bass : note(\"\");\n const rootDegree = bassIndex === -1 ? NaN : bassIndex + 1;\n const hasBass = bass.pc && bass.pc !== tonic.pc;\n\n const intervals = Array.from(type.intervals);\n\n if (hasRoot) {\n for (let i = 1; i < rootDegree; i++) {\n const num = intervals[0][0];\n const quality = intervals[0][1];\n const newNum = parseInt(num, 10) + 7;\n intervals.push(`${newNum}${quality}`);\n intervals.shift();\n }\n } else if (hasBass) {\n const ivl = subtract(distance(tonic.pc, bass.pc), \"8P\");\n if (ivl) intervals.unshift(ivl);\n }\n\n const notes = tonic.empty\n ? []\n : intervals.map((i) => transposeNote(tonic.pc, i));\n\n typeName = type.aliases.indexOf(typeName) !== -1 ? typeName : type.aliases[0];\n const symbol = `${tonic.empty ? \"\" : tonic.pc}${typeName}${\n hasRoot && rootDegree > 1 ? \"/\" + root.pc : hasBass ? \"/\" + bass.pc : \"\"\n }`;\n const name = `${optionalTonic ? tonic.pc + \" \" : \"\"}${type.name}${\n hasRoot && rootDegree > 1\n ? \" over \" + root.pc\n : hasBass\n ? \" over \" + bass.pc\n : \"\"\n }`;\n return {\n ...type,\n name,\n symbol,\n tonic: tonic.pc,\n type: type.name,\n root: root.pc,\n bass: hasBass ? bass.pc : \"\",\n intervals,\n rootDegree,\n notes,\n };\n}\n\nexport const chord = get;\n\n/**\n * Transpose a chord name\n *\n * @param {string} chordName - the chord name\n * @return {string} the transposed chord\n *\n * @example\n * transpose('Dm7', 'P4') // => 'Gm7\n */\nexport function transpose(chordName: string, interval: string): string {\n const [tonic, type, bass] = tokenize(chordName);\n if (!tonic) {\n return chordName;\n }\n const tr = transposeNote(bass, interval);\n const slash = tr ? \"/\" + tr : \"\";\n return transposeNote(tonic, interval) + type + slash;\n}\n\n/**\n * Get all scales where the given chord fits\n *\n * @example\n * chordScales('C7b9')\n * // => [\"phrygian dominant\", \"flamenco\", \"spanish heptatonic\", \"half-whole diminished\", \"chromatic\"]\n */\nexport function chordScales(name: string): string[] {\n const s = get(name);\n const isChordIncluded = isSupersetOf(s.chroma);\n return scaleTypes()\n .filter((scale) => isChordIncluded(scale.chroma))\n .map((scale) => scale.name);\n}\n/**\n * Get all chords names that are a superset of the given one\n * (has the same notes and at least one more)\n *\n * @function\n * @example\n * extended(\"CMaj7\")\n * // => [ 'Cmaj#4', 'Cmaj7#9#11', 'Cmaj9', 'CM7add13', 'Cmaj13', 'Cmaj9#11', 'CM13#11', 'CM7b9' ]\n */\nexport function extended(chordName: string): string[] {\n const s = get(chordName);\n const isSuperset = isSupersetOf(s.chroma);\n return chordTypes()\n .filter((chord) => isSuperset(chord.chroma))\n .map((chord) => s.tonic + chord.aliases[0]);\n}\n\n/**\n * Find all chords names that are a subset of the given one\n * (has less notes but all from the given chord)\n *\n * @example\n */\nexport function reduced(chordName: string): string[] {\n const s = get(chordName);\n const isSubset = isSubsetOf(s.chroma);\n return chordTypes()\n .filter((chord) => isSubset(chord.chroma))\n .map((chord) => s.tonic + chord.aliases[0]);\n}\n\n/**\n * Return the chord notes\n */\nexport function notes(chordName: ChordNameOrTokens, tonic?: string): string[] {\n const chord = get(chordName);\n const note = tonic || chord.tonic;\n if (!note || chord.empty) return [];\n return chord.intervals.map((ivl) => transposeNote(note, ivl));\n}\n\n/**\n * Returns a function to get a note name from the scale degree.\n *\n * @example\n * [1, 2, 3, 4].map(Chord.degrees(\"C\")) => [\"C\", \"E\", \"G\", \"C\"]\n * [1, 2, 3, 4].map(Chord.degrees(\"C4\")) => [\"C4\", \"E4\", \"G4\", \"C5\"]\n */\nexport function degrees(chordName: ChordNameOrTokens, tonic?: string) {\n const chord = get(chordName);\n const note = tonic || chord.tonic;\n const transpose = tonicIntervalsTransposer(chord.intervals, note);\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(chordName: ChordNameOrTokens, tonic?: string) {\n const chord = get(chordName);\n const note = tonic || chord.tonic;\n return tonicIntervalsTransposer(chord.intervals, note);\n}\n\n/** @deprecated */\nexport default {\n getChord,\n get,\n detect,\n chordScales,\n extended,\n reduced,\n tokenize,\n transpose,\n degrees,\n steps,\n notes,\n chord,\n};\n"],"mappings":"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","names":["detect","note","chord","notes","transpose"]}