javascript - trovare - rilevatore di note musicali



Determina la chiave di una canzone per i suoi accordi (4)

È possibile utilizzare l'array a spirale, un modello 3D per tonalità creato da Elaine Chew, che ha un algoritmo di rilevamento chiave.

Chuan, Ching-Hua e Elaine Chew. " Ricerca di chiavi audio polifoniche utilizzando l'algoritmo CEG a spirale ." Multimedia ed Expo, 2005. ICME 2005. Conferenza internazionale IEEE su. IEEE, 2005.

Il mio modello di tensione recente, che è disponibile in un file .jar qui , emette anche il tasto (oltre alle misure di tensione) basato sull'array a spirale. Può prendere un file musicale XML o un file di testo come input che prende solo un elenco di nomi di intonazione per ogni 'finestra temporale' nel tuo pezzo.

Herremans D., Chew E .. 2016. Nastri di tensione: Quantificare e visualizzare la tensione tonale . Seconda conferenza internazionale sulle tecnologie per la notazione e la rappresentazione della musica (TENOR). 2: 8-18.

https://src-bin.com

Come posso trovare la chiave di una canzone al livello di programmazione solo conoscendo la sequenza di accordi della canzone?
Ho chiesto ad alcune persone come avrebbero determinato la chiave di una canzone e tutti hanno detto che lo facevano "a orecchio" o "per tentativi" e dicendo se una corda risolve una canzone o meno ... Per il musicista medio che probabilmente va bene, ma come programmatore non è davvero la risposta che stavo cercando.

Così ho iniziato a cercare librerie correlate alla musica per vedere se qualcun altro ha ancora scritto un algoritmo. Ma nonostante abbia trovato una libreria molto grande chiamata "tonal" su GitHub: https://danigb.github.io/tonal/api/index.html non sono riuscito a trovare un metodo che potesse accettare una serie di accordi e restituire la chiave .

La mia lingua preferita sarà JavaScript (NodeJs), ma non sono necessariamente alla ricerca di una risposta JavaScript. Lo pseudo codice o una spiegazione che può essere tradotta in codice senza troppi problemi sarebbe assolutamente soddisfacente.

Come alcuni di voi hanno menzionato correttamente, la chiave di una canzone può cambiare. Non sono sicuro che un cambio di chiave possa essere rilevato abbastanza affidabile. Quindi, per ora diciamo solo, sto cercando un algoritmo che faccia una buona approssimazione sulla chiave di una data sequenza di accordi.

... Dopo aver esaminato il cerchio delle quinte, penso di aver trovato un modello per trovare tutti gli accordi che appartengono a ciascuna chiave. Ho scritto una funzione getChordsFromKey(key) per quello. E controllando gli accordi di una sequenza di accordi su ogni tasto, posso creare un array contenente le probabilità di quanto è probabile che la chiave corrisponda alla sequenza di accordi data: calculateKeyProbabilities(chordSequence) . E poi ho aggiunto un'altra funzione estimateKey(chordSequence) , che prende le chiavi con il punteggio di probabilità più alto e poi controlla se l'ultimo accordo della sequenza di accordi è uno di loro. Se questo è il caso, restituisce una matrice contenente solo quell'accordo, altrimenti restituisce una matrice di tutti gli accordi con il punteggio di probabilità più alto. Questo fa un buon lavoro, ma ancora non trova la chiave corretta per molte canzoni o restituisce più chiavi con uguale probabilità. Il problema principale sono gli accordi come A5, Asus2, A+, A°, A7sus4, Am7b5, Aadd9, Adim, C/G ecc. Che non sono nella cerchia dei quinti. E il fatto che ad esempio la chiave C contenga esattamente gli stessi accordi del tasto Am , e G lo stesso di Em e così via ...
Ecco il mio codice:

'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' ]


Answer #1

Data una serie di toni come questo:

var tones = ["G","Fis","D"];

In primo luogo possiamo generare un set di toni unico:

tones = [...new Set(tones)];

Quindi potremmo verificare l'apparenza di # e bs:

var sharps = ["C","G","D","A","E","H","Fis"][["Fis","Cis","Gis","Dis","Ais","Eis"].filter(tone=>tones.includes(tone)).length];

Quindi fai lo stesso con bs e ottieni il risultato con:

var key = sharps === "C" ? bs:sharps;

Tuttavia, non si sa ancora se è maggiore o minore , e molti componenti non si preoccupano delle regole superiori (e hanno cambiato la chiave in mezzo) ...


Answer #2

Gli accordi di una canzone di un particolare tasto sono prevalentemente membri della scala della chiave. Immagino che potresti ottenere una buona approssimazione statisticamente (se ci sono abbastanza dati) confrontando le alterazioni predominanti negli accordi elencati per le chiavi delle chiavi.

Vedi https://en.wikipedia.org/wiki/Circle_of_fifths

Naturalmente, una canzone in qualsiasi tasto può / avrà alterazioni non nella scala delle chiavi, quindi sarebbe probabilmente un'approssimazione statistica. Ma su diverse barre, se si sommano le alterazioni e si filtrano tutte tranne quelle che si verificano più spesso, è possibile associare una firma chiave.

Addendum: come Jonas w indica correttamente, potresti essere in grado di ottenere la firma, ma non sarai in grado di determinare se si tratta di una chiave maggiore o minore.


Answer #3

Potresti anche essere in grado di mantenere una struttura con le chiavi per ogni scala "supportata", con valore come una matrice con accordi corrispondenti a tale scala.

Data una progressione di accordi, puoi iniziare creando una lista di chiavi in ​​base alla tua struttura.

Con più corrispondenze puoi provare a fare un'ipotesi plausibile. Ad esempio, aggiungi altro "peso" a qualsiasi scala che corrisponda alla nota fondamentale.





chord