Diatonic transposition?

• Jul 14, 2012 - 10:58

Since there's no function for diatonic transposition, I thought I'd try to write a plugin for it. I'm new to writing plugins for MuseScore. I thought it could include the following:
1. A dialog, where you pick the key. If I know how to do it, the code should read the present key and have it as preselected. Maybe an extra check box/radio button for harmonic or melodic minor. And of course a control with which you choose how many diatonic steps you want to transpose.
2. The code that goes through all selected notes and transposes them according to the settings in the dialog.
The plugin would require correct usage of accidentals. Diatonic transposing of a lot of notes that don't belong to the key doesn't make sense.


Comments

I have only dabbled with plugins as yet but if I were doing this I might create a dialog box and have the user enter the key of the piece and the number of semitones to transpose (positive integer for up, negative for down). Then I'd look up the note pitches - //musescore.org/en/plugin-development/note-pitch-values - and do some math to raise or lower the pitch value of each note in turn but with adjustments to keep it in key.

I seem to be able to change note.tpc of a note in the score. It affects how the note looks in the score, but it doesn't change note.pitch. I have to change that, too.
note.tpc defines the tonal name of the note, note.pitch defines the absolute midi number of the note. So note.tpc could contain D##, E or Fb and all three notes would have the same midi note number. But note.tpc doesn't define an octave for the note.
So far I only tested forcing a D# into the note.tpc on all selected notes. My plugin nicely changed all notes to D#, but to different octaves, probably finding the D# in the particular octave range (C to B) of the original note.
This will make it a bit trickier. I guess the steps for each note would be as follows. Say I'm in B minor and I want to transpose D three steps down, using harmonic minor.
1. I would create a harmonic minor scale for B minor, like B C# D E F# G A#. I'd also need some semi tone scale, like 2, 1, 2, 2, 1, 3, 1.
2. Find the midi number for the new note, using the semi tone scale. From the semi tone scale I find out that the transposed note is 4 semi tones lower, so I do something like note.pitch -= 4 (note that not all notes transpose 4 semitones down - that's what diatonic transposition is about).
3. Using the B C# D E F# G A# scale, I go three steps down from D, which takes me to A#. Now, when the note.pitch is already correct, I can set note.tpc to A# (or its equivalent integer number), which should set it to right octave, too.

If I have notes that don't belong to the key, I'm in small trouble here. I should probably find the unaltered equivalent from my scale. Say I have the note F there. The unaltered note in B minor would be F#. Now I could transpose it as it was F# in the first place, and simply let the user alter the notes afterwards. Look at this example:

The key is C major. First bar has been copied and transposed to following bars. Should the Bb accidental be remained in the following bars? That's all up to the user. In bar 2 it could be either A or Ab. In bar 3 the Gb is probably not desired, while G would be prefered. So I guess diatonic transposition would simply mean ignoring the accidentals and let the user alter the notes afterwards.

Attachment Size
ex1.png 19.5 KB

In reply to by jotti

"So I guess diatonic transposition would simply mean ignoring the accidentals and let the user alter the notes afterwards".

Yes, but isn't that a bit like saying just use the arrow keys to move you block of music up or down a few semitones and then correct the accidentals? Personally this is what I would do but I can see the attraction of writing a plugin to do it "properly" and also just for the exercise of writing a plugin.

I had some thoughts about how I would go about creating a plugin to do this. I would start simply and then add in more user options as required. I would start from the premise that you want to take notes that are currently "native" to the key and then move them up or down the desired transposition amount. If they become sharp by doing so then flatten them by a semitone to bring them back into line. Any notes in the original selection that are accidentals then just move them the required amount but don't (for the time being, anyway) attempt to "correct" them.

An algorithm for the plugin would go something like this:

-User selects a passage of music
-User activates Plugin from menu
Plugin brings up a a dialog box asking for the key signature of the current selection (let's assign it to variable P_Sig) and the desired magnitude of transposition (let's assign that to the variable P_Tran).
-User presses Start to initiate conversion.

The processing:
Goto beginning of the selection and read the pitch of the first note into a variable - let's call it P_Base.
Add the transposition factor (P_Tran) to P_Base to get a new pitch value - P_New.
Check that P_New doesn't fall out of bounds (<0 or >127) and adjust it up or down by 12 (12 semitones = one octave) if it does.
Divide P_Base by 12 and take the remainder which will allow you to look up a list (of 12 values - one for each semitone in the chromatic scale) and let you know if the starting note is "native" to the scale or whether it is an accidental - let P_NatB = 1 if it is a native note and P_NatB = 0 if it is an accidental.
Divide P_New by 12, take the remainder and look up the same list to see if the new note is "native" or not and assign it to P_NatN

Adjusting the final note
If P_NatB = 1 AND P_NatN = 1 then the final note value is P_New
If P_NatB = 0 then (regardless of P_NatN) the final note value is P_New
If P_NatB = 1 AND P_NatN = 0 then subtract 1 from P_New to bring it back "native": P_New = P_New - 1

Replace the original note on the stave with the new note, pitch value P_New.
Repeat for next note in line.
Check for end of selection: Repeat as necessary or end.

In reply to by underquark

One of the things I am hopeful the new framework may do is eliminate the need for things like a dialog to ask the current key signature. It may be possible to get that info directly from the data structures now. Of course, hte passage in question might be in a different key from the key signature. But perhaps there could be two differet versions of the plugin - one that operated completely automatically - loke the arrow keys, but diatonic, the other that asked for info. Actually, I'd even propose two that operated automatically - one a step up, one a step down - and then the fancy version. The other thing I am looking forward to in this new facility s that apparently it will be possible to assign keyboard shortcuts to plugins. So you could have diatonic equivalents of the current arrow key commands implemented as plugins.

Of course this is also something that could be implemented within MuseScore itself, which wouldn't break my heart either.

In reply to by Marc Sabatella

I looked at extracting the Key Signature from the score but could only find the value for the Initially-set key signature (curScore.keysig) and not for when it had been changed, hence the need to ask for user input here (although, as you say, the new framework might make this easier). Note that I haven't actually written a working Plugin for this (and nor am I likely to with work looming again after a break) just outlined some ideas.

In reply to by underquark

If I could somehow read the key signature in the selected bars, I would still show the dialog, but the present key signature would be selected as default. The selected measures might still be a passage that goes in say the dominant key.
About the accidentals (or the notes not belonging to the key), I still think they are not first priority here. The more you have of them, the less diatonic is the music in the first place and the less you are in need for diatonic transposition. But I guess I might get into that deeper, as soon as I have anything working and I can test things.
My dialog will have 13 keys to select as radio buttons. C / Am is one button, G / Em is one button etc. F# / D#m is one button and Gb / Ebm is a different button. Then I'll have a different radio button set with (1) major / natural minor, (2) harmonic minor and (3) melodic minor.
I must take a deeper look at your algorithm. I'm not quite sure it works. At first glance it seems to deal with pitches, not the tpc. And it seems to transpose by adding (or subtracting) the same number of semitones, regardless of on which note in the scale we are. Then the final transposed note seems to be a pitch value, not a tpc value. As I pointed out, changing the tpc will change how the note looks like, but it won't affect the pitch, therefore I have to change both. Does the same happen if I write a new value for the pitch? Will the pitch change (into another midi tone) while the visible note remains on its place in the score?

In reply to by underquark

"Yes, but isn't that a bit like saying just use the arrow keys to move you block of music up or down a few semitones and then correct the accidentals? Personally this is what I would do but I can see the attraction of writing a plugin to do it 'properly' and also just for the exercise of writing a plugin."

A bit, yes, but using the arrow keys is simple transposing, leaving a lot of accidentals to be corrected, if one wants a diatonic transposition. I aim for something that focuses on a diatonic transposition. Only the notes not native to the key have to be corrected. I have to make this work, until I can take any further steps in looking at how the non native notes should be treated. Right now I have the feeling that there's no simple answer to that, instead it might require more options for the user to choose in the dialog.

This was way too tricky to be the first plugin to make. I don't even know javascript. I rely on my knowledge of C++, which usually helps one a lot.
Anyway, one thing is to transpose the midi pitches correctly. Another thing is to transpose the tpc or the tonal names of the pitches. So far the former seems to work correctly. I even get natural, harmonic and melodic minors correct.
But the tpc stuff fails. My code simply does nothing to the tpc, it kind of bails out after it has done the midi pitch. This means I can hear the perfect diatonically transposed notes, while the score shows me unaltered notes.
Tricky or not, great fun! And I'm almost there!

Here's a snippet from the code:

// doing the tpc transposition of note
nos = n_of_scale;

nos += transteps;
if (nos < 0) nos += 7;
if (nos > 6) nos -= 7;
note.tpc = all_scales[scale_mode][key][nos];
}

Before this I have already successfully set note.pitch to the right value. But something strange happens here. The debugger shows that all these values are correct. scale_mode, key and nos pick the right integer from the all_scales array. But it seems it doesn't go to note.tpc.
The debugger also shows that "this object is not a Note" for the note object. Nevertheless I succeeded in altering note.pitch.

It seems to work now. Test it if you dare. Some strange things yet happen with the undo function. The whole undo history starts acting strange. If I delete or undo stuff after using my plugin, playback will still play the music as it was before the action. So test the plugin on copies of your work.
The dialog is not polished. It has some faults and maybe I'll turn the slider vertical. I'd also like to be able to save the settings.
The plugin is in the zip and consists of the .js file with the code and the .ui file with the dialog. I also attached a tune, which I made using the plugin. I wrote 2 bars of music, which I then copied and transposed. Bars 3 and 4 were harmonic minor, 5 and 6 natural minor, 7 and 8 melodic minor.

To do:

  • saving settings
  • polishing the dialog
  • polishing the code
  • reading the key from the score and having that preselected in the dialog
  • if keyboard shortcuts could trigger plugins, a version for one up, another for one down
  • maybe a new look at how to deal with non native tones
  • porting to MuseScore 2.0 (if there's no built in thing)
Attachment Size
diatonic_transp.zip 4.51 KB
Another Autumn Song.mscz 2.02 KB

In reply to by Jojo-Schmitz

I know nothing about github. Well "GitHub Social Coding" kind of gives the main idea, which sounds great! Might work better than just having it here as an attachment.

I discovered one bug. If I keep the slider at "prime" and choose melodic minor (maybe just any of the three modes), it hangs up. What it should do is just turn the marked notes into melodic minor, if they were say natural or harmonic.
*me googles*
Ouch, seems English doesn't have a good name for "prime". It should probably be "unison". I have to change that. I mean, in Swedish the intervals are prim, sekund, ters, kvart..., a consequent pattern. But that's another discussion.

Do you still have an unanswered question? Please log in first to post your question.