I was wondering how would one create a javascript function for transposing music chords.
Since I don't expect everyone to be a musician here, I'll try to explain how it works in music theory. I hope I don't forget something. If yes, musicians, please, correct me.
1) The simple chords
The simple chords are almost as simple as an alphabet and it goes like this:
C, C#, D, D#, E, F, F#, G, G#, A, A# B
From B it loops all over again to C. Therefore, If the original chord is E
and we want to transpose +1, the resulting chord is F
. If we transpose +4, the resulting chord is G#
.
2) Expanded chords
They work almost like the simple chords, but contain a few more characters, which can safely be ignored when transposing. For example:
Cmi, C#7, Dsus7, Emi, Fsus4, F#mi, G ...
So again, as with the simple chords, if we transpose Dsus7
+ 3 = Fsus7
3) Non-root bass tone
A problem arises when the bass plays a different tone than the chord root tone. This is marked by a slash after the chord and also needs to be transposed. Examples:
C/G, Dmi/A, F#sus7/A#
As with examples 1 and 2, everything is the same, but the part after the slash needs transpose too, therefore:
C/G
+ 5 = F/C
F#sus7/A#
+ 1 = Gsus7/B
I think this should be all, unless I forgot something.
So basically, imagine you have a javascript variable called chord
and the transpose value transpose
. What code would transpose the chord?
Example:
var chord = 'F#sus7/C#';
var transpose = 3; // remember this value also may be negative, like "-4"
... code here ...
var result; // expected result = 'Asus7/E';
Define your keys with an object:
Parse your chord with a regular expression:
Do a little math to get the new key:
Put it all back together again:
I'll let you figure out the bass part, since it's really just calling the function twice.
Also, you can add the code here before the function for old browsers that don't support
indexOf
.I put a demo on jsFiddle.
EDIT: The issue was with negative modulus. The above will work as long as long as the negative isn't more than the length (e.g. you can't transpose 100 steps down).
chord = C/B, amount = 1: C#/C or chord = Gm7, amount 2: Am7
Ok, so I've thought about this a fair bit now, and I have a functional answer. It's in standard Western Scale tones (sorry Northern Europeans).
To truly transpose a chord you need 3 pieces of information: 1) the CHORD name, 2) the OLDKEY, and 3) the NEWKEY. It's not always enough to modulate by an AMOUNT (is UP-2 from the key of E an F# or a Gb?).
Basically you need to preserve two distances – the distance in pitch between the CHORD and the OLDKEY, and the distance in 'letters' between the CHORD and the OLDKEY – when mapping the CHORD to the NEWKEY.
To simplify(?) this, I've predefined every scale and every possible note (and some impossible ones) in relation to that scale root.
Then you assign your scales based on the OLDKEY and NEWKEY:
Finally, some regex to find and replace all those chord-roots/flats/sharps/doubleflats/etc with their corresponding position in the NEWKEY scale.
There's definitely a better, more 'think-like-a-computer' way to do this, but this will take
and return
How about a little somethin' like this:
Note that I threw in the "F#maj9#11" example just to give you more to think about with regard to what makes up a valid chord name: you may find a "#" sharp symbol that doesn't follow a letter (in this case it belongs to the "11").
And, obviously, my function only understands sharps, not flats, and doesn't understand keys, so, e.g.,
transposeChord("C/E", 1)
will give "C#/F" when it really should be "C#/E#".All of these solutions are lacking the fact that after transposing a note, it needs to be converted to a sharp or flat depending on the key or chord.
So the API must be:
Here is my implementation. It also handles multiple # and b modifiers.
Use it like this