Recently I’ve been keeping my Python programming skills brushed up by experimenting with the challenge of how to parse ChordPro files, including the ability to transpose them into different keys. As with most programming tasks, there are quite a few small steps required, such as how you take a line of a song, recognise what the chords are and write the altered ones back.
Even chords themselves are not always straightforward. They can be as simple as “C” or as complex as “Bm7b5/F”. One part of the job is to try and break down complex tasks into smaller, easier steps. I’ve realised that, in the chords I can think of, expressed in a way that makes sense in a ChordPro file (eg. no special characters for major 7 or half diminished ones), you can break them down into chord roots and the extensions that add flavour. For example, my two chord roots would be C and B – the “m7b5” (that “half diminished” example) remains the same, not matter what the new chord root is. Slash chords were also not that scary – I just have to break the chord apart by the slashes, deal with each root and then reassemble it.
Initially, I thought I could just line up the “white note” letters – ABCDEFG – and do something with that. However, that founders when you need to account for chord roots like C# and Bb (or even B# and Abb, although both of those are into “edge case” territory). I ended up by using the core letters in the order they occur in the cycle of fifths (FCGDAEB) and extending that by using some loops to give me a list of slots from Fbb (F double flat) to B## (B double sharp). Both ends are safely away from anything I can remember encountering in the wild. I can work out the gap between the original key and the new key: D to A is +1, D to F is -3 and major or minor doesn’t matter as the quality is covered by the extension part. Then I could take a sequence of chords, like D A Bm G in D major and compute what they are in the new key. If we went to A, that would be A E F#m D, with the F# being an example of where the scale wraps around with the first accidental added.
It took a bit of experimentation to iron out bugs in my programming but, once I was asking it what I thought I was asking it, the routine passed all the tests I threw at it. I’ve got a bit more of the overall project to go but I’m reasonably confident that I’ve got the transposition engine into a reliable state.