10

I'm writing software that transposes sheet music between keys and I'm still new at music theory so I was hoping someone who knows more could let me know if my approach is correct. Here's my process for each note:

  1. Find the degree of the note's letter name in the current key's scale.
  2. Find the number of semitones raised or lowered the note is from the actual note in the scale. I'll call this the offset.
  3. Find the note in the new key's scale that is the same degree.
  4. Raise or lower the new note by the offset.

Here's an example. Say we are transposing the note E# from C major to D major. Then we would do the steps above as follows:

  1. E# would be third degree of the scale.
  2. The offset would be +1 because E is actually the third degree of the C major scale, and E# is one semitone above that.
  3. The third degree of the D major scale is F# so that's the new note.
  4. Adding our +1 offset to that would make it F##.

So F## is the result of transposing E# from C major to D major. I realize you wouldn't normally have an E# in the key of C major but I think it's possible and it works to explain the algorithm.

Is this algorithm correct? Should I be approaching this differently?

A secondary question: is it necessary to know the mode of the keys when executing this kind of algorithm? The results seem the same whether I'm using major or minor scales for the degree/offset calculations. Thanks in advance!


Update: Solution

It sounds like the above algorithm is correct, but can be simplified. The piece of the puzzle I was missing was that you can determine the interval without referring to the scale. Here's the updated algorithm. Steps 1 and 2 find the interval between the current key and the new key. Steps 3 and 4 apply that same interval to the note we are transposing.

  1. Find the distance between the current key's letter name and the new key's letter name in the list of all letters (not including sharps or flats): A, B, C, D, E, F, G.
  2. Find the distance in semitones between the current key and the new key.
  3. The new note's letter will be the letter distance (Step 1) above the current note.
  4. Add sharps or flats to the new note's letter until it is the correct number of semitones (from Step 2) above the current note.

Repeating the original example we'll have:

  1. D is 1 letter above C
  2. D is 2 semitones above C
  3. The new letter will be 1 letter above E#, which is F
  4. The new note will be 2 semitones above E#, which is F##

Thank you to everyone who answered, and especially to @MattL and @Dom. All the answers and comments have been very enlightening!

12
  • I was planning to edit my answer once I got a response, but perhaps this is a better way to go about it. Now Jonathan, suppose you want to transpose from C minor to G# minor. Input note is a B natural. Output note is therefore Fx (F##). What are the logical steps? We have C+4 is G, and G# is 8 semitones up from C. So we know that we need the note 8 semitones up from B natural, and we know that we need to call it an F. What are the logical steps to determine that it is a double sharp, as opposed to, say, a flat? ...
    – BobRodes
    Dec 7, 2015 at 7:42
  • ... I'm asking because there appear to be rather more of them than the steps in my suggested algorithm (below) that uses Caleb's idea. But I could be wrong, so perhaps we should look at both.
    – BobRodes
    Dec 7, 2015 at 7:49
  • Other things that your solution has to address: what do you do if you find a Cx in a piece in C that you are transposing to F#? That note would need to be F###, and triple sharps aren't allowed. It would have to read G# instead.
    – BobRodes
    Dec 7, 2015 at 7:53
  • 1
    @CarlWitthoft Thanks for the suggestion, but this question is about correctness, not efficiency. I have the programming skills to optimize it myself; I just don't have the music theory knowledge to know that I'm producing correct results. If you'd like me to state that in the question I can add it. Dec 7, 2015 at 14:51
  • 1
    @BobRodes I think triple-sharps are perfectly valid (in the theoretical sense). If we stick to the example I used in my question, but we transpose an E## instead of E# then that would be a doubly augmented second interval (I think) and the only way to preserve the same interval during transposition would be the result of F###. As far as I can tell, whether to have double or triple flats/sharps is a matter of preference. So it will have to be left up to the user, not the algorithm. Dec 7, 2015 at 21:31

6 Answers 6

6

Matt's right you want to think in intervals when you think about transposition as it will result in you being able to know what you should name the note along with the distance in semitones you need to move the note.

So in this specific case, you are transposing up all the notes by a Major second which means you move every note up two semitones and the name of the note is based on the next letter name up.

Let's say just for simplicity that the value of the note were enumerated like this:

0    1     2     3     4    5     6    7     8     9    10    11
C  C#/Db   D   D#/Eb   E    F   F#/Gb  G   G#/Ab   A   A#/Bb   B  

And let's also say that we have the note letters enumerated like this:

0  1  2  3  4  5  6
C  D  E  F  G  A  B  

So back to your example the E# would map to the note value of 5 (F) and the note letter of E (2) regardless of what key you are in. When you want to move this note up by a Major Second, you would then increase the note value by 2 and the note letter by 1 giving a note value of 7 (G) and note letter of F (3) from these two values you can assign a new name for the note.

3
  • I've updated the question with a new algorithm based on your (and @MattL's) answer. Does it look like I've understood correctly? Dec 7, 2015 at 6:09
  • 1
    Yes, but especially if you are to continue to make musical based software I would try and keep the algorithm in musical terms. Step 1 & 2 are combined if you just denote the interval as a major 2nd (which you know the distance in semitones and the letter name you want to move to) which you could create an infrastructure to do with a little bit of extra work.
    – Dom
    Dec 7, 2015 at 8:04
  • Yes, in my code I've created an abstraction for intervals, but I thought it was helpful in this question to specify explicitly how one would go about finding the interval and then applying it. But I've updated the question to give a better explanation of the intervals. Dec 7, 2015 at 14:56
7

It is indeed not relevant which scale is used. What counts is the interval from the root note to the current note. Using your example, an E# is an augmented third from the root note C. So if the root changes to D, the transposed note is an augmented third from D, which is an F##.

Instead of working with semi-tones (which will give you ambiguity in naming the notes), you need to work with intervals, which allows you to distinguish an E# from an F (for C as the root, the first is an augmented third, the latter a perfect fourth).

3
  • Very informative, thanks! I think that's what I'm doing. So the algorithm I'm using looks correct to you? Dec 6, 2015 at 21:01
  • 1
    @JonathanPotter: Yes, apart from using "the current key's scale", which is not necessary. Take the interval between the original root note and the new root note, and shift the current note by that same interval. This will keep the interval between the note and the root note identical.
    – Matt L.
    Dec 6, 2015 at 21:09
  • I'd like to mark both this and @Dom's answer as correct, but I can only do one :( So you have my thanks and my upvote. Dec 7, 2015 at 6:07
5

If I may suggest a simpler alternative: use a "Line of Fifths" approach to ensure the proper note spelling while not requiring any knowledge of scale degree.

The Line of Fifths is constructed similarly to the better-known Circle of Fifths, but it does not assume enharmonic equivalence, so it does not close in on itself, and stretches to infinity in both directions. It's a convenient structure to use with algorithms that care about spelling notes properly.

...
B♭
F
C
G
D
A
E
B
F♯
C♯
G♯
D♯
A♯
E♯
B♯
F♯♯
...

The algorithm then becomes:

  1. Find out how many spaces along the line you are transposing. In this case, going from C to D is two fifths.
  2. Find the note you want to transpose, and then go that many spaces further. In this case, going two fifths beyond E♯ brings you to F♯♯.

If you'd like to read more, I introduced the Line of Fifths concept in my first-ever stack exchange answer, regarding an algorithmic way to determine interval names: General procedure for determining the name of an interval given a major key / diatonic collection.


Update: Since it isn't quite obvious, and BobRodes asked about it, I'll mention that in this scheme, any note can be represented by a single number -- it's position along the line. Though you could conceivably put the origin anywhere, I find the math works best if F = 0 and numbers increase as you go towards the sharps (C = 1, G = 2).

There is no need to store an entire array of note names, because there is a simple one-to-one mapping between a note's name and it's position on this line. You do need to explicitly store 7 pairs (mapping the range F-B to the numbers 0-6), but once you have that, the position of any pitch name can be found (and vice versa).

The key is to realize that any pitch name can be represented by the pair {letter, numberOfSharps} -- where a negative numberOfSharps means to use flats. For example F♯♯♯ is (F, 3) while G♭♭ is (G, -2). To convert this to a position along the line, you use the formula:

7 * numberOfSharps + letterToNumber[letter]

So for C♯♯♯, since C=1 and there are 3 sharps, the position along the line would be 7*3+1 = 22.

The inverse relation (convert a position to a pitch name) can be found using a combination integer division by 7 to get the number of sharps, and modular division (position mod 7) to get the letter.

7
  • Very interesting Caleb. While I understand that you can theoretically stretch to infinity, in practice we don't go beyond ## and bb as I'm sure you know. So, if you were writing an algorithm, where would you go up from a B## or down from an Fbb? Not that you'll run into either of these 99.9% of the time, but what would you do if you did? I'm thinking you might just go up or down a diminished sixth instead of a fifth, making B## go to G# and Fbb go to Ab. What do you think?
    – BobRodes
    Dec 7, 2015 at 4:17
  • I don't see any reason why wrapping would be necessary, and have updated my answer accordingly. Though if you wanted to, you could just add/subtract 12 to the note's position, in order to move closer to 0. Dec 8, 2015 at 3:20
  • The reason is practical. Nobody uses more than double sharp/double flat in notation (nearly nobody). If you're actually trying to make a piece of software that transposes actual music, you hit a hard boundary at Bx and Fbb.
    – BobRodes
    Dec 8, 2015 at 3:53
  • Sure, you'd have trouble displaying it or printing it, but there's no theoretical reason that the transposition algorithm should be inherently limited. Dec 8, 2015 at 3:56
  • 1
    Thanks! After trying multiple strategies for the problem, I found and implemented this one, and it seems to have worked great. The edit was particularly useful, I was about to create the full array until you pointed how to easily map to a number. It worked great as-is for chords, but I also used the same algorithm to transpose notes on the sheet. For that I needed to add some extra logic in order to handle octave information (e.g. E5 vs E6). I also ended up implementing triple-sharp and triple-flat on my application, at least so I could verify the correctness of some edge cases. Sep 27, 2018 at 22:14
2

You only have 12 keys to worry about (even minor keys are just duplicates note-wise of the relative major) so it would be very simple to create a 2-dimensional String array to use as a look up table for the names of each of the notes for each key. This will allow you to specify what each note will be called for all 12 keys and you can determine whether to call a note an "A#" or a "Bb".

In addition, you can also decide what to call notes outside of the key (many songs contain chords/notes from outside of the key). For example, you might choose to use the name D# in the key of G major (since G major has one sharp), while you might choose to use the name Eb in F major (since F major has one flat).

This will make key changes super simple as all you have to do is change the root and everything else dealing with chords/notes internally within the key will stay exactly the same.

11
  • ???...why the down vote? Jonathan is writing software and asked for an algorithm for transposing keys so I offered him a suggestion as to how to code what he is trying to do. I'm a programmer as well as a musician and I have experience with this topic as I have created music software myself which included the option of transposing keys. My response was directed at another programmer who will likely understand my suggestion. :)
    – Tekkerue
    Dec 7, 2015 at 1:03
  • 1
    Well, I'm another programmer too, and your algorithm doesn't explain how "you can determine" what to call a note. It needs to include this if it is to be considered a solution.
    – BobRodes
    Dec 7, 2015 at 3:09
  • But what you call a note is dependent on the key you're in, so what you actually call a note will be different. The note names can be found from googling the 12 different major keys and then manually entered into the look up table. This will eliminate oddities like "E#" from showing up in the key of C major as the array will always point to the name "F" and not "E#". I guess more info is needed from he op as to what kind of scenarios he's trying to account for.
    – Tekkerue
    Dec 7, 2015 at 3:59
  • I agree that more info is needed. But if the OP needs to actually write transposed sheet music, the "oddities" that you mention have their place and use in music. See here: youtube.com/watch?v=A3j57AdHSvg (it's a bit blurry, sorry, best I could find). Look on the third line, first measure, last beat, top note of the chord. It's an E#. If you wrote it F, you'd have to write a natural on it (because the previous F is sharped), and then put a sharp on the next F. That's not as easy to read.
    – BobRodes
    Dec 7, 2015 at 4:31
  • p.s. If both the input and output of the program are actual music, then enharmonic names of notes don't matter as you say. If they are sheet music, your idea is an unworkable oversimplification, as my example should demonstrate. Most of the software development failures in our business occur from incomplete understanding of the requirements; this is a fine example of that. :)
    – BobRodes
    Dec 7, 2015 at 4:40
1

IMHO, the most efficient way to do what you want from an IT perspective is Caleb Hines' solution. There are fewer moving parts than Dom's solution (sorry Dom). Just create an array (a zero-based array) containing his "Line of Fifths" in it. You'll need to handle the boundary condition of where to go from the ends of the line, which are Bx and Fbb. Absent a better suggestion from him, I would change the fifths to diminished sixths: G# follows Bx, Ab follows Fbb. It's most unlikely that you will ever see either of these notes (or have to transpose across them) but you have to account for them. If you don't and they come up, your program will fail spectacularly and the reason won't be very obvious.

So, your algorithm will look something like this. First (assuming that you have already created the array with the "Line of Fifths" in it), determine the distance between the input key and the output key:

  1. Start with the first array element.
  2. Iterate the array until you find either of the two keys.
  3. Continue iterating the array until you find the other key.
  4. Let X equal the difference between the two offsets.
  5. If the first value found is the target key, let X = -X.

Next, iterate through all the notes in the piece (a non-trivial proposition!). To determine the transposed pitch of one note:

  1. Iterate the array until you find the element containing the note. Let Y equal the offset of this element.
  2. If Y + X is greater than the upper bound of the array (we'll call the upper bound U), let A = the offset of C#. Output the element whose offset is A + Y + X - U.
  3. If Y + X is less than zero, let A = the offset of Ab. Output the element whose offset is A + Y + X.
  4. If otherwise than 2 or 3 (non-boundary condition), output the element whose offset is Y + X.

That should get you started. If you can poke holes in my logic, by all means do so.

2
  • There's no need to waste all that valuable memory, or limit yourself to the bounds of a finite array! Just represent a note as a single number, and create a mapping from that number to the note name. I've updated my answer with more details. With a 32 bit integer, this lets you get at least up to F with 306,783,377 sharps. Which should be more than enough to appear infinite under any possible transposition... Dec 8, 2015 at 3:12
  • That isn't useful, though, in practical terms. You don't generally go above two sharps or flats. As such, you need to work out an enharmonic note to transpose to if the algorithm outputs a triple sharp or flat. You're going to need to do that to solve the problem at hand. homes.soic.indiana.edu/donbyrd/InterestingMusicNotation.html has some interesting material on this.
    – BobRodes
    Dec 8, 2015 at 4:08
-2

There is a slight flaw in that would probably never be an E# in the key of C, but the concept seems to be on the right lines. You need to be more musically aware.

Re-reading your question, it's obvious I misunderstood what you were aiming at. I apologise.

4
  • 3
    The OP mentioned that he knows that there wouldn't normally be an E# in the key of C major, but in theory it could happen. And the example was deliberately chosen to be a bit weird, in order to "challenge" the method.
    – Matt L.
    Dec 6, 2015 at 20:22
  • Any fairly chromatic passage in the key of C might have one. For example, Chopin's Etude Op. 10, number 2 is in C, and bar 7, fourth beat, top note is an E#. Perhaps your criticism of the OP's lack of musical awareness is misplaced, Tim?
    – BobRodes
    Dec 7, 2015 at 3:26
  • @BobRodes, from looking at the piece it appears the E# is so the performer can quickly see at a glance that it's chromatic (all the notes in a nice slope). This isn't a theoretical issue but a visual issue of how it appears on sheet music to make it easier on the performer. A blind algorithm converting to a different key could very well screw up that nice visual slope on the sheet music indicating that it's chromatic. That kind of thing probably won't work to well with a blind algorithm as the algorithm wouldn't know the reason why the E# was used instead of the "theoretically correct" F.
    – Tekkerue
    Dec 7, 2015 at 4:11
  • I'll refer you to my comment on your post. You are quite correct, it is primarily a visual issue. But there are algorithms that will always show this interval as a minor second rather than an augmented unison. Caleb Hines' idea looks like it will, for example, although I would want to test the boundary conditions more thoroughly before making such a pronouncement.
    – BobRodes
    Dec 7, 2015 at 4:44

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.