Plugin for automatic transposition and Clef change

• May 30, 2024 - 12:31

Hi to all of you,

In my bands, some of the scores have to be played by different instruments and reader may follow different convention for the same instrument. This is especially true for trombone parts (some readers play in C and read in GClef, some read in FClef and some players play in Bb and read in GClef) and for the bassline, which may be interpreted by a sousaphone (generally in Bb and FClef but some player read only GClef) or by a bass guitar or a contrabass (which needs to be in C and either in F or G Clef).
I have hundreds of scores and I wish to keep as few staves as possible, since any copy imply that any change in the piece must be propagated over all the copies.

Thus, I have been looking for a general automatic way to realize the transposition and the clef change. Transposition is quite documented on this forum and I succeeded rather quickly, even though the result is not 100% satisfying. Clef change, on the other hand, is another piece of cake. There are numerous open conversations on this topic here and none seem to give a start of a hint.

Since I have come up with a working solution. I am publishing it here. It is clearly not perfect, and I am looking for hints and advises to improve it as much as possible. I am ready to contribute, by implementing dedicated functions in the API, if necessary.

First of all, my QML scripts work with musescore_v3.5.2. Don't ask me why. I have gone through so much of pain with all these versions and their different way of handling the plugins. It just works. Don't hesitate to try your own version though!

You will find attached, a *.mscz simple score, the *.qml plugin file and two pdfs obtained with the commands :

mscore-3.5.2 -o ChromaticScale_Bb_FClef.pdf ChromaticScale.mscz

and

mscore-3.5.2 -p transpose_BbToC_GClef.qml -o ChromaticScale_C_GClef.pdf ChromaticScale.mscz

The plugin needs to be placed in the directory indicated in the "Général" tab of the Edit/Preferences menu. ($HOME/Documents/Musescore3/Plugins by me)

1) As one can see, the transposition is not satisfying, since C# is transposed to Cb instead of being transposed to B#, which would be the desired behaviour. Is there a way to cure this problem or the best way is to re-code the transposing function by changing the tpc2 attribute of each note with an adequate transposing table?
2) The GClef has been added to the beginning of the first measure, by use of the API 'clef-violin' command. Using the same cursor instance, I am able to change the header time signature though, by means of the additional code:

var ts=newElement(Element.TIMESIG)
ts.timesig=fraction(12,8) //some values won't work, always check timesigActual
c.add(ts); // c is the cursor instance

If I do this instead :

var clef=newElement(Element.CLEF)
c.add(clef);

The whole staff is translated to GClef, but the clef at the header of the staff won't change. Any idea how to overcome this issue?

Cheers!

Plugin :

import QtQuick 2.2
import MuseScore 3.0

MuseScore {
version: "3.5"
description: qsTr("This plugin transpose a Bb score written in FClef into concert key in GClef")

onRun: {
console.log("Hello! Let me transpose your score to concert key and GClef");

var fullScore = !curScore.selection.elements.length

if (fullScore) {
  cmd("select-all")
  curScore.startCmd()
}

cmd("select-all");
for (var i=0; i<12; i++) {
  cmd("transpose-up"); // One octave higher so the music is written mid-staff in GClef
}
// Transpose to Concert Key
cmd("transpose-down");
cmd("transpose-down");

// Create a cursor at the beginning of the staff
var c=curScore.newCursor()
c.inputStateMode=Cursor.INPUT_STATE_SYNC_WITH_SCORE
c.voice    = 0;
c.staffIdx = 0;
c.rewind(Cursor.SCORE_START);

/* add a G Clef at the beginning of the first measure */
cmd("clef-violin");

/* Comment previous command and uncomment following lines for a different behaviour */

// var clef=newElement(Element.CLEF)
// c.add(clef);

/* Uncomment this part if you want to change the header time signature */

// var ts=newElement(Element.TIMESIG)
// ts.timesig=fraction(12,8) //some values won't work, always check timesigActual
// c.add(ts); // c is the cursor instance

Qt.quit();

if (fullScore) {
  curScore.endCmd()
  cmd("escape")
}

}
}

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