Plugins API design for MuseScore 3

• Jan 18, 2019 - 10:00

Full plugins support is the commonly requested feature for MuseScore 3, and I am currently working on its implementation. Regarding that there are some questions on certain aspects of plugins API designs which can be resolved differently, at least until this gets merged into release builds. So I would like to ask for your opinion on how would it be better to make these API parts.

Properties of elements

In its recent revision the handbook page on plugins for 3.0 says that the new way to access elements properties is via get() and set() functions (where set is not working actually). Still I see some drawbacks of this solution:
1) It is less convenient to use (as it seems to me). For example (from Note Names plugin),
text.text = qsTranslate("InspectorAmbitus", "F♭♭") + text.text;
would become
text.set("text", qsTranslate("InspectorAmbitus", "F♭♭") + text.get("text"));
2) It causes more changes on code updating from 2.X plugins API.
3) It may cause inconsistencies with other properties for which it doesn't make sense to include them to the common libmscore properties system (which makes plugins updating even more difficult). For example, position offsets for elements (which would be, for example, offsetX and offsetY), score properties (number of staves, measures etc.) or container properties (e.g. list of notes in chord) are not included to this system now. On the other side it is always possible to implement getting them by name too.

On the other side, there are some advantages in this approach:
1) It is very easy to implement on C++ side (unless we decide to add a large number of new Pids to resolve the issue 3) from the list above).
2) In case we need some operations other than getting, setting and resetting properties, this approach will allow to make such operations available in a similar way.

I can try to make properties available via the old syntax, but maybe there drawback are not so critical, or there are some other reasons why the get("name")/set("name") approach is more preferable?

Properties vs functions

The mentioned above handbook page mentions a change from cursor.segment and cursor.element properties to cursor.segment() and cursor.element() functions. I don't see any reasons for such a change so I reverted it in my branch, but perhaps there are still some reasons to prefer the latter way?

Element types

The page also mentions change between namespaces for element types enumeration. I suggest having a Element::type property that actually returns a string instead of enumeration value. The reasons are:

  • This seems to be a proper JavaScript-like solution (even typeof returns a string rather than any possible numbers or special enumerations).
  • This solution does not depend on enumeration namespaces in any way.
  • It is actually already used: see the source code of Chord Identifier plugin where Element::_name() is used for checking elements types. I just propose to make this a main way to check elements types.

The same would apply for element creation: instead of
var text = newElement(Element.STAFF_TEXT);
one would have to write
var text = newElement("StaffText");

Enumeration properties values

These suffer from the same problem as element types: most of enumerations have renamed, and once we expose enumerations used inside libmscore again we are bound not to rename its values and not to move it anywhere. So there is an option to make some string-based interface also available for them. Personally I would prefer not to do it right now as the benefit for them is not so large as it seems to be for element types, and it it much more difficult to implement for them than for element types. So maybe it would be sufficient to have just renamed enumerations for such cases.

Possible API expansions

Just a quick list of what may be useful (or not useful) to add:

  • Fractions instead of / alongside with ticks: fractions are more natural to use than ticks (for example, 1440 ticks will become 3/4 in fractions), and they may be more precise in some situations.
  • Selection API: I saw some mysterious tricks with Cursor object in plugins code just to define whether a score has a selection. Having an explicit selection API will help to avoid the necessity for those tricks.
  • Styles API: maybe it could be useful to have an API to access and modify styles values. Not sure whether it is needed but If you find it useful please let me know about that.
  • Persistent dock widgets: currently there are "dock" type plugins which provide some docking widgets. Still one has to relaunch such plugins on each MuseScore restart. Maybe it would be convenient to have MuseScore to remember plugins widgets positions and relaunch them on start. This will be useful, of course, only if there is an API that is powerful enough to make some often tasks more convenient, maybe some API for updating a plugin state on score change would be needed for that. That is a more distant perspective though, and is not too relevant to this topic right now.

So it would be great to hear your feedback on these issues. Other suggestions on plugin API design are also welcome!


My main concern about 3.0 plugins is that curently it isn't possible to create a plugin that works in 2.x and 3.x, i.e. there is nothing in a plugin to determin whether it is using one or the other version.
Between 1.x and 2.x this was possible in a way, as you could maintain a .js and a .qml version in the same package and MuseSore would pick the right one automagically, but between 2.x and 3.x it would both be .qml, so such a branching would need to be done inside the plugin code. The check for MuseScore version would only come after the #import statements, but those already depend on what MuseScore version we're on, nice catch-22 :-(
So how would we make it possible to have 1.x, 2.x and 3.x plugins (for the same job, obviously) to be maintained in a single package, a single GutHub branch?
Having said that, I don't have any strong opinion on any of your points, but would rather stay as compatible as we possibly can, to ease the porting as much as possible

In reply to by Jojo-Schmitz

If there are two separate files for different versions then there should be no problem in launching them. Plugins directories are anyway separate for 2.X and 3.0 by default, if I am correct.

Supporting two versions in a single file should indeed be difficult though. Still one can move some core logic to a separate file and import it from two files corresponding to two MuseScore versions. If supporting two versions in a single file is a really common request then we could try not updating version number for the imported MuseScore module but then we need to implement some version checking ourselves.

In reply to by sambaji

No, I didn't look at those issues, thanks for pointing them out! Still the priority task now is restoring the functionality that was available in MuseScore 2 so most of those issues that request some extra functionality will probably be considered later.

I have made a couple of plugins for old version of Musescore. I don't mind if they don't work in 3.0 or later versions. The only wish from my side is the plugin API to have all (or almost all) functions a user has in the UI. So that it is possible to fully automate some processes.
Use cases for my plugins were:
* Chord detection. I don't want to enter chord names manually, especially if one of the staves, the accompaniment, has chords written in sheet music/note notation. So I did a plugin. The main issue in the old API found while writing the plugin was:
- not possible to know the key signature (stave key), had to workaround it by blind guessing stave key based on notes used in N last measures and the Circle of fifth/ fourth.
* Auto accompaniment based on entered chords. I want to prototype my song and for writing melody I want to enter some simple accompaniment automatically. The plugin never saw a release, but I still have this idea. Would be great to write melody while I hear the harmony.

To sum it up and quoting myself: "plugin API to have all (or almost all) functions a user has in the UI". The rest is not that important. Of course I don't want the API to be ugly android-like, but what I see in the topic start, get/set functions are fine for me.

P.S. I am a professional programmer, and an amateur musician/ guitar player.

In reply to by anatoly.gorbunov

coming back to the initial comment from Dimitri95, I would suggest to keep as much as possible from the syntax of V2.0.
Because it makes life simpler.
At least for the developers of plug-ins.
The changing of properties to functions (e.g. segment) makes no sense for me.
The usage of setters and getters is C++-Style, but normally not used for scripting language. It makes programming of plug-ins more complicated and esp. provokes a need to touch each "old" plug-in in order to make it work in V3.0.
In my opinion the ideal solution would be to keep the old API interfaces completely and only extend them with new features (so if this means that there are two interfaces for the same function). So there would be no need to have two versions for 2.0 and 3.0. And I suppose that most of the plug-in programmiers are not willing to support 2 versions.
For me actually I don't use V3.0 in daily usage just because of the lack of working plug-ins. I tried to update a plug-in step-by step - but I gave up after 2 hours of try-and-error...

In reply to by Bacchushlg

There is a balance to be struck between improving the API to simplify coding and add functionality and supporting the older framework. As long as there is detailed documentation of the new Plugin API framework to aid programmers in transitioning V. 2 plugins to 3, I am in favour of doing whatever makes the most logical sense for future development, even if that means major syntax changes, for the sake of new plugin developers. I am in process of slowly learning qml so I am not in a position to suggest what needs to change or be retained in the API.

In reply to by Bacchushlg

I understand that plugins developers would like to see the new API as compatible with the old code as possible but unfortunately some parts of the old API are hard to maintain without introducing some potentially less maintainable solutions. One of the most prominent issues is moving a large number of enumerations between classes and namespaces in C++ code which took place between MuseScore 2 and MuseScore 3. It seems the only possible way to handle it with Qt meta-object system is just redeclaration of the same enumerations at locations that are exposed under the old names to QML plugins. But I don't like this solution since it leads to duplicating these values so every time we update something about them in the core library we would have to do the same in plugins framework. That is why I am still inclined to change these names, and that is why I propose moving from enumerations to something more persistent like element names instead of element type enumeration.

Fortunately, from my experience of converting standard and some other plugins, this doesn't cause really big changes in plugins code so they can be launched within the new plugins framework after just a few plugin code edits. Of course, some documentation explaining which changes are necessary to make should be available for that.

Transition to get/set functions is not caused by any technical challenges of such sort so if there is no obvious benefit from that syntax (is seems there really isn't) I would rather have old properties syntax implemented. That way you shouldn't have to do a large number of changes to plugins code.

Properties of elements

Your analysis is spot on. I don't have a good solution. As a plugin developer, I don't like get/set... As a C++ developer I understand very well that it's a lot easier to implement...

Note that get/set is currently working for "pure MuseScore properties" only and so for example it's not possible to get the list of notes of a chord or the list of grace note chords of a given chord. If we do provide chord.notes() it doesn't really make sense to have chord.notes()[0].get("color") right ? So what do we do ? chord.getList("notes")[0].get("color")... it's even more ugly, and the plugin developer inside me doesn't like it...


I don't think it's a great idea to keep this function as it... One of the main challenge when designing the plugin API is to make sure that we don't leak memory and that a plugin cannot crash MuseScore. By providing newElement() I don't see how we can do that... Who is the owner of the new Element, JS or C++ ? Who will delete it ? I'm afraid a better API will need to be specialized. Something like cursor.addText(string, style), chord.addNote(pitch), segment.addChord(pitch, pitch1, pitch3) etc... the actual Element would be C++ owned and would be inserted in the score tree. If needed, these functions could send back and ElementW owned by JS and acting as a proxy to modify the properties of the element.

More features ?

Right now, it's only possible to run a plugin on a click of a button. It could be convenient to have a way to run a plugin after each layout, or after any command or a given command is run... For example, the color notes plugin is great, but you need to run it every time you add a note or change a pitch or the color is not updated...

In reply to by lasconic

I should note that, as I figured out a bit later than the initial post was created, it is not really hard to implement the old syntax too based on the libmscore properties framework. The only consequence it has is that binding of properties names to properties IDs should be moved out of the single properties table in libmscore/properties.cpp (if we aim to avoid duplication of information) so it will be much more difficult to do such mapping in other applications besides properties getting/setting/resetting from QML code. If we have no such applications then there are no real reasons to move to get/set syntax.

Concerning the newElement function, I don't believe that object ownership is really a big issue for it. After creation of the element via newElement() call the created element should belong to a plugin, that is, to JavaScript side of the system. When this element gets added to a score the ownership should change to C++ side. As we don't have many ways to add the newly created object to a score it should not be difficult to implement that transition properly, and that is the way I am trying to follow in the new API implementation. That is even easier to implement with wrapper objects since they make it possible to fully restrict the possible ways of elements ownership transition, and wrapper objects themselves can be always managed by JavaScript engine.

The idea of registering plugins to events is indeed great, that would be good to develop it further.

In reply to by lasconic

The alternative(s) suggested by lasconic make a lot of sense, in particular, because each addSonething() function could only be called from a relevant parent element and each could have appropriate parameters.

However, I am afraid it will make things tough for plug-in developers. Last time I checked the number of element type was approaching the hundred and there are probably more by now. This would mean dozens and dozens of someElementType.newSomething(params...) functions, each as member of some specific other element type, some possibly of more than one, each with its specific parameters and so on.

Of course, if the alternative is something hacked, which risks to crash MuseScore and which will be difficult to maintain, then these considerations are secondary. But, I believe, usability by common mortals is not totally irrelevant...

Incidentally: I do not understand the apparently major concern of lasconic about ownership and life cycle of the new element: if segment.addchord() can return an element with a 'good' ownership and life cycle, the same could do segment.newElement(CHORD), could it?

In reply to by Miwarre

The concern of @lasconic is about the "global" newElement() function which just creates a new element without adding it to a score's object tree. Plugin can add it then to a score via a cursor.addElement() call (or something similar). That way the ownership of the created object does not always belong to C++ code. Still, as I noted above, the ownership transition should not be a problem even in this case.

Please do not get rid of enumerations in favor of strings. Enumerations are discoverable - I can look up the enumeration definition and know what values I can expect. If you return a string, the I have no decent way to find out what I can expect.

Strings instead of enumerations may be "more javascript like." That doesn't make it a good idea.

I don't have a strong opinion on the mentioned items one way or the other, but please, please, please, have the API well documented such that developers don't have to go digging in the different libraries or rely on trial and error to get something working.

Back in 2012, when Werner presented the QML framework he wrote: "The most important point is that the needed bindings to MuseScore internals are much easier. It is possible to expose the original class hierarchy of MuseScore to the scripting engine." And indeed, the plugin API in MuseScore v2 seemed to mostly expose C++ methods and types to the plugin engine. This means, that "simple" things were often not easy to do, because MuseScore's core did not have an exposable method for them. One example would be creating a chord, containing several notes.
Solving the "find the selection" problem has been discussed several times but never lead to an agreement, because different solutions fitted different needs, and finding a universal easy-to-use solution to the problem turned out to be quite challenging. Still, I think I'd like the plugin API to be more functionality-oriented (e.g. by extending the Cursor class) and I like lasconic's ideas to add methods like addText, addNote and addChord.

1) Properties
I am strongly against textual strings used as identifiers: they are prone to typos ("text"? "Text"? even "test"?...) particularly in a multi-native-lingual environment as MuseScore users are and no compiler / interpreter / lint can catch them. So, please abstain from things like "myText.get("text")". If you really, really, really have to do something like that, please, please, please use somethign along the line of "myText.get(MYCUTEENUMERATION.Text)".

The fact that they are (or might be taken as) more "JavaScript oriented" is totally irrelevant to me.

2) Properties vs Function
True, I am an old C/C++ addict (and not much fond of latest C++ "improvements" either), but I never understood the need for the distinction between properties and functions in the first place, so I do not see any need for extending the use of properties in the plug-in API; actually I would prefer to get rid of them entirely and using only functions, which are clearer and simpler, but I understand the importance of not changing too much of the old API.

3) Element types
The same confutation of textual string identifiers as in point 1) applies here too; in addition, as
Joseph Eoff notes, enumerations are discoverable, strings are not.

4) Expansions: to my perspective, by far the most useful expansion quoted is the Styles API. The other would be nice to have but would mostly make easier something which is already possible.

In reply to by Miwarre

The arguments in favor of enumerations instead of strings described by you and Joseph Eoff are totally valid for compiled languages (and some interpreted languages) and for a single-language environment. But for the case of having JavaScript (QML) bindings consider the following:

1) JavaScript does not really help in preventing typos or non-existent properties usage. Trying to access a non-existent property of any object results in returning undefined value or setting a new property rather than raising any kind of error. Enumerations are exposed in QML exactly as some object's properties so It could help only in case some tool or IDE capable of analyzing QML bindings of C++ enumerations catches such mistakes. Are such tools used in MuseScore plugins development?

2) Discovering enumeration definition requires finding it in the C++ source code of MuseScore. When developing plugins within MuseScore the only source of such information is API documentation where string-based values can be listed in a way similar to enumeration values.

Anyway, for most cases I believe the most feasible option would be to use the old approach with enumerations (which got renamed though), the same option can be left for elements types either alongside with the names option. Still the benefits of this are not so clear for me.

Concerning the properties access syntax, as I noted above the get/set option makes sense only if some wider possibilities about properties are needed. One of the main reasons I added this question to this discussion is that the code that was intended to be a part of the new plugins system was already switched to the get/set syntax before I started working on this issue.

In reply to by dmitrio95

If you assume the strings are all defined in one spot, then it is possible to find them in code.

In practice, they are usually scattered around such that finding all possible definitions is impossible.

I write software in my day job, and deal with "string APIs" more often than I like.

Properties V Functions:
As an experienced Java programmer, I much prefer the object-oriented style of using get() and set().

Element Types:
It is much better to use:
var text = newElement(Element.STAFF_TEXT);
as this avoids pitfalls due to spelling mistakes when passing strings.
Perhaps in future there will be a MuseScore IDE that can immediately (and generically) indicate an error when the enumeration syntax is used, but cannot generically do a spelling check on the valid values for a parameter. The IDE could suggest valid values after the programmer has typed in "Element.", just like Eclipse or IntelliJ.

Possible API Expansions:
Better features to read/write files.
Better windowing and layout features. I tried to create a dialog box a couple of years back & it looked dire.
Access to the MIDI events received by MuseScore e.g. when you have a MIDI keyboard plugged in.

In general it is easier to write programs in a strongly-typed language, as the compiler does lots of checks before the code is run.
Weakly typed languages take so much longer to debug, as many errors are only manifested at run time.

There are a few comments above about JavaScript. JavaScript is easily one of the worst languages invented, so I would suggest avoiding anything that works like JavaScript. The earlier versions of Java had a very clean syntax, although modern Java is now getting more complex with the addition of Lambda functions.

I am a music theorist/composer by profession, although now retired. I have limited experience programming tools for algorithmic composition (in Lisp, mainly). I couldn't realistically hope to work productively with the C++ source code of MuseScore itself. But I've been able to teach myself enough QML/Javascript to create a handful of plugins for MuseScore 2. I'd like to update them for v3, as well as developing new tools.

To (mostly) echo what others have said, my priorities for the plugin API are:

  • Straightforward programmatic access to more of the actions that can performed from the UI in normal use, particularly regarding creation and manipulation of rhythmic/time-based features such as ties, tuplets and time-signatures.

  • Better documentation of objects and methods, possibly using a "wiki" model to which we could all contribute. Ideally this should be presented in a way that doesn't require knowledge of the source code itself.

  • Access to score information that seems out-of-reach to v2 plugins, such as Instrument names and staff assignments, octave transpositions (unless I've missed it, there's no way to write a note at a particular octave in a transposed score/part), and so on.

In reply to by PaulSC

For the documentation, there needs to be a plugin development page, preferably in the developers handbook, rather than the users' handbook, but link to it from the plugins page. "Look at existing plugins" to understand them is very insufficient.


As I created a plugin, I received an email which proposes me to participate to this discussion.
I created a plugin for getting key numbers for a diatonic accordion tablature by modifying an existing plugin for concertinas, I understood it enough to modify it for my diatonic accordion.
When MuseScore 2 was released, lasconic (I thank you once again lasconic) modified the plugin for being compatible with MS 2 : I am still using it very much.
Today I am anxious (!!): what is happening for "my" diatonic accordion plugin when I use MS 3 ?????

Sorry, even I developed some software (50 - 20 years ago !) I am unable to participate.


I forward @dmitrio95's message:
"From those plugins that are shipped with MuseScore at least the following work:

  • ABC import (worked in 3.0 as well)
  • Color notes
  • Create score
  • Note names
  • Panel
  • Random
  • Random2
  • Walk (this one is not indented to produce any user-visible results).

Jojo also ported and checked some plugins to 3.0 which are listed in the common plugins page:

Concerning Chord Identifier, it can be ported to 3.0 but as it is not shipped with MuseScore its publicly available versions will not work with MuseScore 3.0.2 right away. I could make a patch to that plugin though and send it to the plugin's author as I tested MuseScore with the altered version of this plugin.

I should also note that some plugins may work not fully as some parts of API are missing but a large part of them should be able to be ported to the available API."

In reply to by Jojo-Schmitz


I was just talking to a harp music publisher at a conference a few days ago, he is very interested in switching to MuseScore, but I wasn't sure what to tell him regarding the harp diagrams. Looking forward to following up with him and giving him the good news!

In reply to by Jojo-Schmitz

@Jojo-Schmitz - thank you very much!

I installed the plugin on MuseScore, and I then allocated a shortcut to invoke the plugin. It's now some months since I had used the Harp Pedal Diagram, and I could not get the screen to pop up properly (neither on MS 2.3.2, nor on MS 3.0.2). The screen flashed up for an instant and disappeared...

For the benefit of other users, here is my mistake: don't select a note or a rest when invoking the Harp Pedal Diagram plugin. You first have to select the entire measure, and then use the shortcut or the option on the Plugins menu!

Problem now resolved... thanks again.

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