Note displayed at the wrong octave but played correclty

• Jul 22, 2021 - 13:34


I'm writing a plugin to make Chords progression workouts.
I've got an issue when adding two specific notes : the B and the Bb.
They are played correctly but displayed one octave to high.
B octave.png
From my log, it seems they are set correctly:

Debug: delta:5, pitch:53, tpc:13, name:F3
Debug: delta:7, pitch:55, tpc:15, name:G3
Debug: delta:9, pitch:57, tpc:17, name:A3
Debug: delta:10, pitch:58, tpc:12, name:B♭3 // <---
Debug: delta:12, pitch:60, tpc:14, name:C4
Debug: delta:6, pitch:54, tpc:20, name:F♯3
Debug: delta:8, pitch:56, tpc:22, name:G♯3
Debug: delta:10, pitch:58, tpc:24, name:A♯3 // <---
Debug: delta:11, pitch:59, tpc:19, name:B3 // <---
Debug: delta:13, pitch:61, tpc:21, name:C♯4
Debug: delta:7, pitch:55, tpc:15, name:G3
Debug: delta:9, pitch:57, tpc:17, name:A3
Debug: delta:11, pitch:59, tpc:19, name:B3 // <---
Debug: delta:12, pitch:60, tpc:14, name:C4
Debug: delta:14, pitch:62, tpc:16, name:D4
Debug: delta:8, pitch:56, tpc:10, name:A♭3
Debug: delta:10, pitch:58, tpc:12, name:B♭3
Debug: delta:12, pitch:60, tpc:14, name:C4
Debug: delta:13, pitch:61, tpc:9, name:D♭4
Debug: delta:15, pitch:63, tpc:11, name:E♭4

What could explain this ?
Where should I look at ?

Attachment Size
B octave.png 8.16 KB


Is this for a transposing instrument? We've had bugs before is the two tpc's get out of sync with each other, but as far as I know those are all fixed internally. Make sure your plugin sets them both correctly.

In reply to by Marc Sabatella

Hi Marc, This issue seems to be indeed related to the fact this is a transposing instrument or not.
I picked some similarly-ranged instruments in this MuseScore instrument file.
The same code is giving different results depending on the instrument:
for the bass-flute (non transposing instrument):

for the saxophone (transposing instrument):

BTW: the two tpc1 and tpc2 are set to the same value, the note's resulting tpc being equal to those tpc1 and tpc2

Attachment Size
bass-flute.png 5.72 KB
saxophone.png 6.01 KB

In reply to by lvr124

The tpc's shouldn't be the same for transposing instruments - one needs to be the correct concert pitch spelling, the other the correct transposed spelling. They are allowed to be out of sync (eg, for a Bb instrument, you're allowed to have a concert E that is transposed to Gb instead of F#) but they have to describe the correct pitches.

In reply to by lvr124

I don't know, but internally, this would be a "part" (parent of staff) property. In some contexts this is also known as "instrument". If that property isn't available, then maybe there is some more neutral way of adding a note without setting tpc explicitly, then read the tpc to deduce the transposition?

In reply to by Marc Sabatella

I did, in the past, a lot of trials to add/change notes via a plugin. And the tpc is definitely needed if one wants to have the note be displayed correctly with the correct alteration (e.g. "Bb" and not "A#" or "Cbb")... :-(

And as far as read the doc, one can retrieve the instrument from the part, but only a few properties of it, like name, short name,... Not the transposing values.

Maybe I'll need to hardcode them for the few instruments I use in my plugins. I don't like that. But if there is no other way...

In reply to by jeetee

This was the right approach. Thanks.
Here is how I do:

* Convention :
*   keep toNote.tpc1 undefined to force a representation. For transposing instruments, the pitch will be adapted (e.g. a note displayed at C, will be pitched as D)
*   keep toNote.tpc2 undefined to force a pitch. For transposing instruments, the representation will be adapted  (e.g. a note pitched at C, will be displayed as Bb)
function changeNote(note, toNote) {
    if (note.type != Element.NOTE) {
        debug(level_INFO, "! Changing Note of a non-Note element");

    // compute the transposition. For non tansposing instruments this will be 0
    var transposing=note.tpc2-note.tpc1;

    // compute new pitch/tpc1/tpc2. For non tansposing instruments this will have no effect.
    var p=(toNote.tpc1===undefined)?toNote.pitch-transposing:toNote.pitch;
    var t1=(toNote.tpc1===undefined)?toNote.tpc2-transposing:toNote.tpc1;
    var t2=(toNote.tpc2===undefined)?toNote.tpc1+transposing:toNote.tpc2;
    // adapting the note
    note.tpc1 =t1;
    note.tpc2 = t2;
    note.pitch = p;
    return note;

pitch, tpc1 and tpc2 are set upfront in order to match the desired pitch/representation

In reply to by lvr124

After many, many, ... tests, trials&errors, the tpc are not the solution. I didn't even came to a solution working at 100%.
Deducing the transposition from the tpc doesn't work because the tpc are not sorted by any musical logic (first the ♭♭ then the ♭ then the naturals, then the ...).

My new algorithm works for all transpositions except for G and F instruments.

It is based on some rules-of-thumb, importing the tpc array and adding to it its "semi-tones delta" (e.g. tpc = 7 is for G is therefore a 7 semi-tones deviation).

See an example in annex.

This being said, I think there are some missing methods in the API to better take into account transposing instruments and accidentals.

I have 2 ideas in mind:

Extend the cursor.addNote(pitch) with 2 new optional arguments.

cursor.addNote(pitch, isConcertPitch, accidentalPreference)

isConcertPitch : true/false

If true or blank: the note must be added must have the desired pitch (same as today)
If false, the note must be added transposed in order to look like the desired pitch.

Example, on Eb instrument,
* cursor.addNote(60) and cursor.addNote(60,true)gives a note with pitch=60, tpc1=14 (C), tpc2=16 (A)
* cursor.addNote(60,true)gives a note with pitch=63, tpc1=11 (eb), tpc2=14 (C)

accidentalPreference: default/sharp/flat

This sets for a note if a SHARP or a FLAT must be used.

If default, it let's MuseScore choose the right accidental
If sharp, it forces it to a sharp
if flat, it forces it a flat.

* The 7th degree of the G scale is a F#. "default" gives "F#", "sharp" gives "F#", "flat" gives "Gb".
* The 4th degree of the F scale is a Bb. "default" gives "Bb", "sharp" gives "A#", "flat" gives "Bb".

With that suggestion, the developper has not to take care of the transposition.

Build an API to consult the instruments.xml file order to retreive by instrument its transposeChromatic value.

On top of that we could have a method on the note object to change its accidental mode, such as note.setAccidentalPreference(["default", "sharp", "flat"]) in order for MuseScore to adapt the note tpcs transparently for the developper.

What do you think ?

Attachment Size
Test-Score.mscz 26.94 KB

In reply to by Marc Sabatella

Hi Marc, I don't have issues with the tpc's. The system is working great. My only points is that managing transposing instruments (meaning modifying the pitch of note and carefully adapting its tpc's) or switching the b or # of a note (meaning modifying the note tpc's) is not obvious.

E.g. In order to determine if an instrument is transposing and how, one must:
1. Add a note,
2. Retrieve its tpc1 and tpc2 and then compute chromaticTransposition = ((note.tpc2-note.tpc1)*5)%12
3. Adapt the pitch note.pitch+=chromaticTransposition
4. Realigning the note tpc's (basically note.tpc1-=deltaTpc; note.tpc2-=deltaTpc; Basic, but sometimes ugly. More code is needed to get to a decent result)

My proposition was to hide all that logic within the API.

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