Plugins for 3.x
Not to be confused with Musescore 4's VST and VSTi feature
This chapter is for MuseScore 3 plugin developers. For other versions, visit Plugin development. To fork the main program, visit the main sections in Developers' handbook.
For info on what plugins are, how to install and use them, visit Musescore 3 handbook plugin section.
Introduction
This chapter aims to provide knowledge, code snippets and work-arounds for better understanding and programming of MuseScore plugin system. Anyone working on plugins are welcomed to document their learnings and experiences. To add your input, register a musescore.org account and login, click the ⠇ to the right of the title, choose Edit. For discussion about this chapter itself visit this forum thread.
The desktop program MuseScore is mostly written in C++ utilizing Qt framework 5.9.9, developers expose a minimal collection of frequently used functions and data as API to allow plugin development beyond MuseScore's built-in features.
Plugins use the QML language of Qt framework, which includes declarative visual user interface coding, and a restricted version of javascript / ECMAScript 7th edition. Plugin creation does not require C++ knowledge. Plugin script files are plain text. These modern javascripts are not supported:
- Arrow function
- Spread syntax
- Array.fill
Capability and limitation
This non-exclusive list contains observations reported by coders.
- Examples of basic functionality
- Change a note's property such as pitch, color
- Add or remove score elements such as notes, rests, symbol, text
- Read or save tags in File > Score Properties
- Play the audio of a score or part of score
- Create a new staff (Cannot change its name afterwards)
- Create/update text and symbols eg dynamics, lyrics etc
- Create/update tempo
- Change playback speed (cmd('playback-speed-increase'))
- Mute or change volume of an instrument
- Create/update MIDI Play Event
- Conversion of a different score format to and from MuseScore
- Send and receive score data through internet
- Opens a local or online webpage inside MuseScore
- Save or load a score or any file
- Start another program on your Windows/iOS outside MuseScore
- Seems unachievable
- Run plugin when audio is playing, or detect whether audio is playing, or realtime Playback Caret/cursor position.
- Directly changing the Playback Caret (Can read Editing Caret position)
- Change instrument name (Can read, but not write)
- Navigate into content, eg title text, inside a frame (user must select the text inside manually with a mouse click first) (Can get to the frame by cmd('prev-element'))
- Add spanners such as pedal marking to a score
- Create or update key signatures in a score
- Read clef info, see https://musescore.org/en/node/357788
- Send MIDI CC
- Mimic a click on a palette item
- Add plugin as a toolbar button
- Add plugin as an item in right click context menu
- Manipulation of MuseScore's window eg minimize
- Use a non build-into-Musescore text font, see workaround post (Can use with pre setup inside qml, see snippet)
- Expose the program with new hooks or API entry points, or create localhost server / services.
- Inserting image. See https://musescore.org/en/node/373264
Please share if you find workaround, for example a suitable cmd() command
Alternatives to QML plugin
- Run Musescore with Command line
- Edit a score file (.mscx is plaintext):
- save as .mscz : rename as .zip, extract, edit, zip ; or
- save as .mscx and edit.
- elsewhere's parser https://musescore.org/en/project/musicxml-parser
- mirabilos' tools https://musescore.org/en/node/321691#comment-1082043
- yonah_ag's parser (retired) https://musescore.org/en/node/323083
- AutoHotkey Kit for MuseScore.
- Get data of realtime midi output or exported .midi.
- Use a non build-into-Musescore font with this workaround post.
- Install Musescore 3.x Nightly and use the (experimental) scripting/macro function to record and replay sequence of commands "Tools → Script Recorder".
- Write a plugin outside the build-in QML plugin system.
Workflow and external editors
Basic workflow of editing a plugin involves using the Plugin Creator.
QML files are plaintext. You can also use any external text editor, eg Sublime Text, Notepad++ etc, to edit a .qml file. However, at Musescore program startup, Musescore caches all plugin scripts. After you edit and update a plugin in external editor, you must either,
- Restart Musescore and then run from the plugin menu; or
- Load the script inside Plugin Creator
- Inside the Plugin Creator, open an existing .qml file
- Press run
- After subsequent edits in external editor, it is required to press Refresh in the Plugin Creator.
- It is not required to restart Musescore when using this method.
Auto complete (QML syntax only, not auto reference or API auto complete)
- use Qt Creator
- use Notepad++ and the Qt user-defined language
- https://duckduckgo.com/?q=qml+online+editor
Useful Resource
Plugin API doxygen documentation
- The Plugin API Documentation is automatically generated (in Doxygen format) whenever MuseScore developers build the main program. It is normal for beginners to be startled by its complexity.Use the search on the top right, instead of menu navigation.
- The original API overview
- Notes on API written by fellow musescore.org users, feel free to pitch in
- List of all API object types
Quickstart boilerplates, snippets and use cases
This section gathers reusable code snippets and boilerplates to help beginners get things done without a hassle. To alleviate QML frustration see QML boilerplates. Copy and paste to your plugin, tweak it to your liking. Also check out specific use cases and notes. Feel free to add your clever codes.
import module statements versions
Musescore 3.6.2 use Qt 5.9.9, for latest version of common modules use:
import MuseScore 3.0
import QtQuick 2.9
import QtQuick.Controls 2.2
import QtQuick.Layouts 1.2
import QtQuick.Dialogs 1.2
import Qt.labs.settings 1.0
import FileIO 3.0
import QtWebEngine 1.5 // web modules are Windows only
import QtWebChannel 1.0 // web modules are Windows only
The Plugin Base Component Musescore { } (Menu name, behavior, root functions etc of plugin)
- Api doc Plugin API
- Component.onCompleted
Run once, right after the plugin component is created, before console.log() is functional - You should not use Qt.quit() in your plugins, see this post on why, and possible different approaches. Qt.quit() crashes Musescore 4.0 plugins.
- Set pluginType: "dock" for normal behavior eg stay on top; "dialog" is quite outdated.
- Create plugin as an action instead of a persistent object with prop and methods, because when the user close and reopen the plugin window, it may cause unwanted behavior.
- This post explains the nature of onRun() and Qt.quit(). The code Qt.quit() may not work the way you expect.
- The plugin window onClosing Event is not accessable from QML. The [Plugin API base] is in a QQuickView in a QWidget window docked inside a QDockWidget , see code.
- No way to detect plugin's open or closed.
- After plugin window is closed, listeners will continue to run, eg experimental onScoreStateChanged Handler
- Workaround palette-like plugin
- use Qt Labs Settings to save data across sessions
- other tips also check out the snippets and notes page
Score elements, internal score structure
- Musescore Internal Score Representation
Beginners into programming may get confused with API Class Inheritance Hierarchy, read up on inheritance object oriented programming, also try out the debugger. - Use the debugger by doing the following steps
- Install MuseScore Nightly
- Append -e in shortcut and start
- Edit > Debugger
- Try Object Explorer plugin which exhaustively print out element info, also see this well documented code
- Try Makeshift REPL plugin function tester plugin's "Diff an element" example.
- elements has properties inherited from the Element Class, only some properties are meaningful to and used by each specific type, there is no decent way to properly knowing which property is supported by which element except digging into the source code. The alternative is easier but tedious: try them out.
- For element type, see enumeration
Enumerations and element types
Also try log type enum string snippet and Makeshift REPL plugin function tester plugin for enum and keyword autocompletion.
Namespace/enum list
Base Class/Component
All elements has a .name
property to provide understandable info of its type.
To match and compare reliably, use .type
property. It is Enum, it uses a integer number internally. These enums can be referenced from various properties of the Base Class/Component
eg Accidental.FLAT
eg how-to Element.NOTE
find NOTE
under ElementType in the Namespace/enum list,
use Element
because Base Class/Component says Element Contains Ms::ElementType enumeration values.
Most common ways to get element data
- curScore.newCursor() to create a Cursor, transverse and find. For more info, open and study walk.qml
- curScore.newCursor() to create a Cursor, sync with current mouse selection example
- select with mouse click then get curScore.selection.elements[ ]
- listen to state.selectionChanged, get curScore.selection example
- select with curScore.selection.selectRange() , get curScore.selection example
Note starting time, ending time, duration (note value)
- api doc Tick Length values
- Cursor and segment has .tick, snippet
Note pitch
- use case and snippets
- api doc Note Pitch values
- api doc Tonal Pitch Class
Placement of objects, cursor track and tick
cursor.rewindToTick(x) will only work correctly if there is a segment at x. Otherwise it will put the cursor at the next available segment (in the current track) after x, see https://musescore.org/en/node/345409#comment-1206211
Tagging a score or score part with custom data
File saving and loading
- api writeScore, readScore
- custom data in MuseScore several methods see this snippet and notes
- study the Daily Log plugin
- FileIO for OS level op, usage see Object Explorer plugin
Instrument / staff info eg instrument name, and channel audio status
Create/update MIDI Play Event
- API doc PlayEvent
- study the Arpeggiator plugin and the articulation and ornamentation control plugin
Fetching webpage
- snippet
- study the updated online service version of ABC Import plugin
- the Harmony analysis tool plugin
Starting another program, command line and parameters
Building UI
1. Use the QML
- See QML boilerplates
- Note: doc.qt.io search sometimes returns an unexpected outdated page, eg QtQuick 1 Button instead of current QtQuick 2 Button.
- Qt Quick Controls, the anchors and Qt Quick Layouts may suffice. Using Qt Widgets from QML may be overkill not worth your time.
- List of all QML components for Qt 5.9
- Customizing Qt Quick Controls 2
- Anchors enum
- Keys enum list eg Qt.Key_Space
- Mouse button enum list eg Qt.LeftButton, more on mouse see this notes
- QtQuick Type properties for disabled, qmlssed, checked, checkable, focused, highlighted, flat, mirrored, hovered etc.
- Settings{ } for persistence (save to OS, eg registry in windows)
- Get the theme status with
SystemPalette { }
. Source post. Their color and metric details are not exposed as API yet, find them in github repo (then plz share link here). Not engraving style. - Musescore.org users' notes on QML and snippets
and/or
2. Use a WebEngine plus html, css and javascript
Windows only, see QML notes
Use the two boilerplates, which utilize a hybrid view built with html, css and standard javascript thru a 100% width and height WebEngine.
Inside WebEngine, standard set of javascript is used, for tech compatibility check on caniuse.com (find Chrome v56), eg css display:grid is unsupported. See also WebEngine community notes.
The following works:
- javascript
- Arrow function
- template-literals ie `string${var}`
- DOMParser().parseFromString("html")
- css
- Flex
The following css will not work:
- Gap
- Grid
Technical info on WebEngineView, WebChannel, WebView:
WebEngineView fetches url, search data on html and runs javascript from QML side only, its version in MuseScore 3 (Qt 5.9) is based on Chromium version 56.0.2924.122.
To communicate bidirectionally between QML and webpage use it with WebChannel.
WebView 1.1 is an older component and use a different engine which does not offer simple WebChannel support, try WebSocket.
Source code at github
MuseScore 3.6.2
- MuseScore 3.6.2 Backend mainly under /libmscore
- API 3.5 (for Musescore 3.6) under /mscore/plugin/api
- An online code search enabled clone of MuseScore 3.6.2 src (Online code search on the official repo always returns results based on current master src ie MuseScore 4 up-to-date version)
- An essential collection is also shown on the doxygen website.
Latest MuseScore 4 code
- Official src
- MuseScore 4 Backend is rewritten and separated into two layers, under
/src/engraving/libmscore
and various/*/internal
- API (3.5, not yet updated for MuseScore4, as of Nov2022) under
/src/plugins/api
Bundled resource (qrc protocol)
A list of every qrc:/// built-in musescore.qrc (src ref)
Icons, symbols and fonts (not soundfont) see this snippet
MuseScore dev team designs and uses its own two sets of styles, their details are not exposed as API yet, find them in github repo (then plz share link here).
The cmd ( ) method
When certain funtionality is yet to be exposed as user-friendly API, coder can use cmd() to run program sequence seen in musescore and think exist as coroutine. Actions that can be assigned to keyboard shortcuts are possible cmd(). These commands are poorly documented, and often cause prog crash (save your qml before Run). Study main C++ repo to understand more.
- calculuswhiz's handpicked list
- complete list
- complete list in CSV format
- A searchable MuseScore 3.6.2 github repo
- This command parser plugin makes them more scriptable
Javascript issues
musescore 1 plugin api
A different non-QML javascript API was used in MuseScore 1.x. While one can still learn a lot reading those files, they use the old API and will not work with current one. One way to spot the old API is presence of new Cursor(curScore)
. The current QML system use curScore.newCursor()
Class/Object inheritance, composition and .prototype
You cannot edit class/object as you would with .prototype in javascript, see component creation if it suits your need.
Object.assign() (and spread function) also does not work, as opposed to stated on Qt webpage. This probably happens when Qt company add dynamically typed javascript scripting to the strong typed C++ based env. Instead create your own shallow copy with a for loop and then some spaghetti code.
QML list and javascript array
QML property declaration:
A list can only store QML objects:
Item { property list aList: [ ] aList2: [ ] //shorthand of above }
A list is an object: typeof list == 'object'
An array is a variant:
Item { property var anArray: [ ] }
_
inside QML functions:
same syntax as in standard javascript
Item { function a(){ var anArray=[ ] } }
Translation
see Internationalization
one solution to folder structure
Troubleshooting with help from community
If you are having difficulty, try
- Identify whether the problem is related to QML, or javascript, or the MuseScore plugin API. If it is QML or javascript, you can seek help from sites like stackoverflow and forum.qt.io in addition to musescore.org
-
Use google and the musescore.org forum search before posting new questions.
-
Ask politely in the plugins forum, provide a Minimal, Reproducible Example if possible, the community is usually more than happy to help.
- Enclose qml codes between
<qml>
and</qml>
to get better layout and to avoid musescore.org auto escaping eg to show & instead of&
- Come back and help others by adding your valuable discovery to this handbook.
Share your plugin
Plugin could be posted as a project on musescore.org without any github knowledge. Chat with other dev at the plugin forum.
Fill out the API compatibility 3.x or else your plugin with remain hidden to most users.
Licensing
Following is not written by a lawyer, consult a real one if you are concerned with licensing.
Qt licensing does not affect QML scripts solely on the basis that they are written in the QML language per se, more info consult Qt dual licensing.
You do not need to follow MuseScore's GPL licensing as long as your plugin does not use MuseScore code base. MuseScore 3 use GPL which is copyleft, derivative work using MuseScore code base, once conveyed, must follow. This matter is debatable.
Credits
API 3.5 thanks to
dmitrio95,
and many others
Reference footnote
import API FileIO module version
import module version at Qt
import module version at wikipedia
import WebChannel module version