How can I get the most appropriate notes for a specific chord? #288
-
Hi guys, I have an input from an external midi device. It gives me the midi-number for the keys that are held down on the keyboard when I play a chord. I then want to take those midi numbers, convert them into note names (with octaves), and show them as written notation. However I would like those note names to be the most correct ones for a chord. So if I play the chord C-minor and I am using sharps i will get ['C4', 'D#4', 'G4'], which is the right notes, but a C-minor chord should be using a b3 instead of a #2. The problem is, that I have to specify if I want to use sharps or flats when converting midi numbers to note names. I would like to automatically get the most common way of representing the notes of a specific chord though. Here is what I have tried: First I import Chord and Midi:
I have an array of midi numbers:
Then I convert those midi numbers to actual note names like this:
That gives me: ['C4', 'D#4', 'G4'] Those are the notes of a C-minor chord, but the D-sharp should actually be an E-flat. I can fix that by setting sharps to false. Then I will get: ['C4', 'Eb4', 'G4'] The problem is however, that I would like to get the most natural way of representing the chord notes automatically. So if I play a C-minor chord and sharps are set to true, instead of getting ['C4', 'D#4', 'G4'] I want to get ['C4', 'Eb4', 'G4']. I can kind of get that by doing the following: First I get the chord:
That will give me an array containing all the possible chords from this note combination. In this case it returns an array with one value: ["Cm"]; Then I can use Tonal's Chord.get method:
So now I have an array of the correct notes ['C', 'Eb', 'G'], however, the octave number is missing, and I need those because I want to display the chord as written notation using VexFlow. I can kind of fix that by taking the octave numbers I got from the midiToNoteName method ['C4', 'D#4', 'G4'], grab the last character of each item (which would be the octave number), and then add them to the end of each entry in the correctNotes array.
That will give me: ['C4', 'Eb4', 'G4'] So far so good.. However, the problem arises if I add a new note to the initial array containing the note names I got from Midi.midiToNoteName: ['C4', 'D#4', 'G4'] If I add an octave to the root note: ['C4', 'D#4', 'G4', 'C5'] (or any other note that is already in the array, but with a different octave number) this method will not work, because the Chord detect will still just return the chord "Cm", which of course makes sense, since an added note that's already part of the array, just in another octave, will not change the quality of the chord. Chord.detect(['C4', 'D#4', 'G4', 'C5']) // will still return ['Cm'] And when I get the notes of that Cm using Chord.get, the length of the array will be 3 even though I am playing 4 notes. So when I run the map on the notes array, I will get ['C4', 'Eb4', 'G4', 'undefined5'], because the arrays have different lengths. I hope this all makes sense. Maybe I'm overthinking this and maybe there's a really simple solution. I really just want to get the most appropriate chord notes for any given chord I play on my midi keyboard. But that's not easy since I have to specify accidentals for the midiToNoteName method. You can see an unfinished demo of what I'm trying to do here: www.chordace.com If you don't have a midi keyboard, you can use the computer's keyboard. A Cm chord would be the keys A, E and G. The accidentals are set to sharps by default. Try to change between flats and sharps using the "Accidentals" dropdown menu, and you'll see what I mean. I hope someone can help. I have really been banging my head against the wall on this one.. :) /Morten |
Beta Was this translation helpful? Give feedback.
Replies: 3 comments 5 replies
-
Here is another example. The chord Ab6#11 contains both sharps and flats. How can I get the note names of the chords including octave numbers. See attached screenshot: |
Beta Was this translation helpful? Give feedback.
-
I think I have a solution? It won't work for double-accidentals though. Basically, we get the set of pitch classes that exist in the chord, and check each note if it's one of those pitch classes. If it's not, flip it with const { Chord, Note } = require("@tonaljs/tonal")
const midiNotes = [59, 62, 66, 71, 74]; // B minor, octave 3
const rawNotes = midiNotes.map(Note.fromMidi); // ["B3", "D4", "Gb4", "B4", "D5"]
const chordName = Chord.detect(rawNotes)[0];
const pitchClassesInChord = Chord.get(chordName).notes;
const correctedNotes = rawNotes.map(rawNote => {
const rawPitchClass = Note.pitchClass(rawNote);
if (pitchClassesInChord.includes(rawPitchClass)) {
return rawNote;
} else {
return Note.enharmonic(rawNote);
}
})
console.log(correctedNotes); // ["B3", "D4", "F#4", "B4", "D5"] |
Beta Was this translation helpful? Give feedback.
-
Hi! @vellebelle Congrats for your project! Looks great! 🎉 @mrjacobbloom solution looks good to me! 👍 Another option could be using the chorma number to find the correct name withing the chord notes array. Something roughly like this: midiNotes.map(midi => pitchClassesInChord.find(pc => Note.chroma(pc) === midi % 12)) Hope it helps EDIT: btw @vellebelle maybe you can create a PR to add your project here: https://github.com/tonaljs/tonal#projects-using-tonal |
Beta Was this translation helpful? Give feedback.
I think I have a solution? It won't work for double-accidentals though. Basically, we get the set of pitch classes that exist in the chord, and check each note if it's one of those pitch classes. If it's not, flip it with
Note.enharmonics