'use strict'
const normalizeMap = {
"Cb":"B", "Db":"C#", "Eb":"D#", "Fb":"E", "Gb":"F#", "Ab":"G#", "Bb":"A#", "E#":"F", "B#":"C",
"Cbm":"Bm","Dbm":"C#m","Eb":"D#m","Fbm":"Em","Gb":"F#m","Ab":"G#m","Bbm":"A#m","E#m":"Fm","B#m":"Cm"
}
const circleOfFifths = {
majors: ['C', 'G', 'D', 'A', 'E', 'B', 'F#', 'C#', 'G#','D#','A#','F'],
minors: ['Am','Em','Bm','F#m','C#m','G#m','D#m','A#m','Fm','Cm','Gm','Dm']
}
function estimateKey(chordSequence) {
let keyProbabilities = calculateKeyProbabilities(chordSequence)
let maxProbability = Math.max(...Object.keys(keyProbabilities).map(k=>keyProbabilities[k]))
let mostLikelyKeys = Object.keys(keyProbabilities).filter(k=>keyProbabilities[k]===maxProbability)
let lastChord = chordSequence[chordSequence.length-1]
if (mostLikelyKeys.includes(lastChord))
mostLikelyKeys = [lastChord]
return mostLikelyKeys
}
function calculateKeyProbabilities(chordSequence) {
const usedChords = [ ...new Set(chordSequence) ] // filter out duplicates
let keyProbabilities = []
const keyList = circleOfFifths.majors.concat(circleOfFifths.minors)
keyList.forEach(key=>{
const chords = getChordsFromKey(key)
let matchCount = 0
//usedChords.forEach(usedChord=>{
// if (chords.includes(usedChord))
// matchCount++
//})
chords.forEach(chord=>{
if (usedChords.includes(chord))
matchCount++
})
keyProbabilities[key] = matchCount / usedChords.length
})
return keyProbabilities
}
function getChordsFromKey(key) {
key = normalizeMap[key] || key
const keyPos = circleOfFifths.majors.includes(key) ? circleOfFifths.majors.indexOf(key) : circleOfFifths.minors.indexOf(key)
let chordPositions = [keyPos, keyPos-1, keyPos+1]
// since it's the CIRCLE of fifths we have to remap the positions if they are outside of the array
chordPositions = chordPositions.map(pos=>{
if (pos > 11)
return pos-12
else if (pos < 0)
return pos+12
else
return pos
})
let chords = []
chordPositions.forEach(pos=>{
chords.push(circleOfFifths.majors[pos])
chords.push(circleOfFifths.minors[pos])
})
return chords
}
// TEST
//console.log(getChordsFromKey('C'))
const chordSequence = ['Em','G','D','C','Em','G','D','Am','Em','G','D','C','Am','Bm','C','Am','Bm','C','Em','C','D','Em','Em','C','D','Em','Em','C','D','Em','Em','C','D','Am','Am','Em','C','D','Em','Em','C','D','Em','Em','C','D','Em','Em','C','D','Em','Em','C','D','Em','Em','C','D','Em','Em','C','D','Em','Em','C','D','Em']
const key = estimateKey(chordSequence)
console.log('Example chord sequence:',JSON.stringify(chordSequence))
console.log('Estimated key:',JSON.stringify(key)) // Output: [ 'Em' ]
question from:
https://stackoverflow.com/questions/45399081/determine-the-key-of-a-song-by-its-chords