Plugins for 3.x

Updated 1 week ago

    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

    Please share if you find workaround, for example a suitable cmd() command

    Alternatives to QML plugin

    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)

    Useful Resource

    Plugin API doxygen documentation

    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

    Enumerations and element types

    enum2.png

    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)

    Note pitch

    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

    Instrument / staff info eg instrument name, and channel audio status

    Create/update MIDI Play Event

    Fetching webpage

    Starting another program, command line and parameters

    Building UI

    1. Use the QML

    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.

    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.
      search.png

    • 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 &amp;
    • 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