English | Deutsch | Español | Italiano
A plugin for MuseScore Studio 4 that reads the notes of a score and writes the chords it finds as real chord symbols (Harmony elements, not staff text) above the staff you choose. They stay transposable and playable.
Listed on musescore.org: https://musescore.org/en/project/chordid474plus
Tested with MuseScore Studio 4.7.4 (Qt 6.10, plugin API MuseScore 3.0).
Unversioned QML imports and startCmd with a name exist from 4.4 on, so the
file should load there as well, but only 4.7.4 has actually been tried.
Download the archive from Releases and unzip it so that this file exists:
- macOS and Linux:
~/Documents/MuseScore4/Plugins/ChordID474plus/ChordID474plus.qml - Windows:
C:\Users\<name>\Documents\MuseScore4\Plugins\ChordID474plus\ChordID474plus.qml
Then enable ChordID474plus under Home > Plugins and call it from the Plugins menu.
Without a selection the whole score is analysed, with a selection only the marked range. Every marked staff is listened to, the symbols go to one staff.
| Setting | Meaning |
|---|---|
| Grid | one symbol per measure, per half measure, per beat or per eighth note |
| Scope | triads only, with sixths and sevenths, or with extensions (9, 11, 13) |
| Minimum length | shorter chords count as passing and are left out |
| Target staff | where the symbols are written |
| Language | German, English, Spanish, Italian, or follow the system |
| Slash chord | append the bass note (C/E) |
| Sounding notes | notes from earlier beats count while they still sound |
| Skip repeats | the same chord is not written again |
| Remove existing | clear old chord symbols before writing |
| Jazz symbols | -7, Δ7, ø7, °7 instead of m7, maj7, m7b5, dim7 |
| Dry run | detect and log without touching the score |
Remove symbols takes chord symbols out again, without detecting anything, in the selected range and across all staves. That is the way back when symbols ended up on the wrong staff. A run, and a removal, is one undo step each.
For every window the plugin collects which pitch classes sound and for how long, weighting the start of the window a little higher. For all twelve possible roots it works out what role each note would play (third, fifth, seventh, extension) and how much of the sounding material that reading explains. A missing root, a missing third and a foreign bass cost points, a bass equal to the root gains them. Below an explanation rate of 0.5 nothing is written, which keeps passing passages empty.
The bass comes from the note carrying the beat, not from the lowest note in the window. Without that rule a passing note below the bass turns Abmaj7 into Abmaj7/G. Notes struck before the window count half, so a chord that is still ringing does not cover the next one.
Equal chords are then merged into blocks. A block shorter than the minimum length is dropped and its neighbours close the gap. This also carries an anticipation along: a chord played just before the beat forms one block with the chord it ties into, so its symbol sits at the anticipated position instead of on the beat after it.
The spelling of the root comes from the note as written in the score, so Eb stays Eb, otherwise from the key signature.
German, English, Spanish and Italian, all in the same file. Without an explicit choice the plugin follows the system language. No Qt translation files (.ts or .qm) are needed, which keeps the plugin a single file with no build step.
The table lives in the LANGUAGES section as strings. A new text goes in there
and is fetched with tr(lang, "key").
ChordID474plus/ChordID474plus.qml is the only source and has five sections:
- Properties and defaults
- ENGINE: plain music theory, no MuseScore objects
- LANGUAGES: the text table
- BRIDGE: read notes, build the grid, write symbols
- UI: the dialog
All of these were measured with the probe in ChordID474plusProbe/, not
guessed. They cost a day, so they are written down here:
- Set
harmony.textonly after attaching. Setting the text on a Harmony element that is not in the score yet quits MuseScore instantly, without a crash report. The order iscursor.add(h)and thenh.text = .... - Never rewind the cursor to the end of the score.
cursor.rewindToTick()onto the tick behind the last measure quits MuseScore as well. Walk the measure chaincurScore.firstMeasureandmeasure.nextMeasureinstead. measure.tickis a Fraction object, not a number. The tick of the measure start ismeasure.firstSegment.tick, its lengthmeasure.ticks.ticks.console.logdoes not reach the log,console.warnandconsole.errordo. Use warn for diagnostics.- Plugin files are read once per session. After every change MuseScore has to be restarted, otherwise the old version keeps running.
- New plugins are inactive until an entry with a filled
actionsarray appears inextensions/config.jsonin MuseScore's application support folder.
node test/run_tests.js # 53 cases against the engine inside the QML file
node test/check_qml.js # bracket balance and parsing of every function
node test/check_i18n.js # completeness of the four languages
test/engine.js cuts the ENGINE section out of the QML file, so there is no
second copy of the logic. The BRIDGE can only be checked inside MuseScore,
since MuseScore 4 has no command line call for plugins.
test/make_runner.py builds a dialog-free variant from the same source that
runs straight away, which is how the whole chain gets checked without clicking
inside the dialog.
GNU General Public License v3, see LICENSE.

