Skip to content

vst: send note articulations to VST3 instruments as keyswitches - #183

Open
manolo wants to merge 3 commits into
musescore:mainfrom
manolo:vst-articulation-keyswitches
Open

vst: send note articulations to VST3 instruments as keyswitches#183
manolo wants to merge 3 commits into
musescore:mainfrom
manolo:vst-articulation-keyswitches

Conversation

@manolo

@manolo manolo commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Resolves: musescore/MuseScore#34385

MuseScore knows each note's articulation but does not tell third party VST3 instruments about it. When a plugin advertises keyswitches through IKeyswitchController, this queries them at load and, straight from the notation, sends the matching keyswitch note to select pizzicato, tremolo, mute, harmonic, legato and so on. It also forwards the host track name as VST3 channel context, so a plugin can auto select its sound from the staff name. Both are no-ops for plugins that do not support them, and neither affects the FluidSynth or MuseSampler backends.

The goal is to let a third party VST3 instrument render articulations directly from the score, with no hacks or workarounds. The consumer this was built and tested against is Plectro, a free VST3 / AU plucked string instrument I maintain: https://github.com/manolo/vst-plectro

  • I signed the CLA as manolo:
  • The title of the PR describes the problem it addresses.
  • Each commit's message describes its purpose and effects, and references the issue it resolves. If changes are extensive, there is a sequence of easily reviewable commits.
  • The code in the PR follows the coding rules.
  • I understand all aspects of the code I'm contributing and I'm able to explain it if requested.
  • The code compiles and runs on my machine, preferably after each commit individually. I have manually tested and verified that my changes fulfil their intended purpose.
  • No prior attempts to resolve this problem exist, or if they do, I listed them in my PR description and described how I avoided repeating past mistakes.
  • There are no unnecessary changes.
  • I created a unit test or vtest to verify the changes I made (if applicable).

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@framework/mpe/articulationstringutils.h`:
- Around line 31-38: Replace static with inline on the namespace-scope
ARTICULATION_FAMILY_NAMES and ARTICULATION_TYPE_NAMES definitions in
articulationstringutils.h, preserving const and their existing initializers.
Confirm the project uses C++17 or newer so these inline variables provide one
shared definition across translation units.

In `@framework/vst/internal/synth/vstsequencer.cpp`:
- Around line 165-208: Update the prevState lookup in the keyswitch handling
block to use upper_bound with arrangementCtx.actualTimestamp before applying
std::prev, ensuring the most recent state at or before the timestamp is used for
chords and span articulations. Preserve the existing non-chord behavior and
keyswitch emission logic; optionally add a regression test in
vstsequencertest.cpp for multiple same-timestamp notes sharing an articulation.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 686cb25a-cd0b-4151-89fc-ac67339b881d

📥 Commits

Reviewing files that changed from the base of the PR and between 6adb702 and 0060919.

📒 Files selected for processing (18)
  • framework/audio/engine/internal/audiocontext.cpp
  • framework/audio/engine/internal/audiofactory.cpp
  • framework/audio/engine/internal/audiofactory.h
  • framework/audio/engine/internal/iaudiofactory.h
  • framework/audio/engine/internal/nodes/eventaudionode.cpp
  • framework/audio/engine/internal/nodes/eventaudionode.h
  • framework/audio/engine/isynthesizer.h
  • framework/mpe/CMakeLists.txt
  • framework/mpe/articulationstringutils.h
  • framework/mpe/internal/articulationprofilesrepository.cpp
  • framework/mpe/qml/Muse/Mpe/articulationpatternitem.cpp
  • framework/vst/CMakeLists.txt
  • framework/vst/internal/synth/vstsequencer.cpp
  • framework/vst/internal/synth/vstsequencer.h
  • framework/vst/internal/synth/vstsynthesiser.cpp
  • framework/vst/internal/synth/vstsynthesiser.h
  • framework/vst/tests/CMakeLists.txt
  • framework/vst/tests/vstsequencertest.cpp

Comment thread framework/vst/internal/synth/vstsequencer.cpp

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@framework/mpe/articulationstringutils.h`:
- Around line 31-38: Replace static with inline on the namespace-scope
ARTICULATION_FAMILY_NAMES and ARTICULATION_TYPE_NAMES definitions in
articulationstringutils.h, preserving const and their existing initializers.
Confirm the project uses C++17 or newer so these inline variables provide one
shared definition across translation units.

In `@framework/vst/internal/synth/vstsequencer.cpp`:
- Around line 165-208: Update the prevState lookup in the keyswitch handling
block to use upper_bound with arrangementCtx.actualTimestamp before applying
std::prev, ensuring the most recent state at or before the timestamp is used for
chords and span articulations. Preserve the existing non-chord behavior and
keyswitch emission logic; optionally add a regression test in
vstsequencertest.cpp for multiple same-timestamp notes sharing an articulation.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 686cb25a-cd0b-4151-89fc-ac67339b881d

📥 Commits

Reviewing files that changed from the base of the PR and between 6adb702 and 0060919.

📒 Files selected for processing (18)
  • framework/audio/engine/internal/audiocontext.cpp
  • framework/audio/engine/internal/audiofactory.cpp
  • framework/audio/engine/internal/audiofactory.h
  • framework/audio/engine/internal/iaudiofactory.h
  • framework/audio/engine/internal/nodes/eventaudionode.cpp
  • framework/audio/engine/internal/nodes/eventaudionode.h
  • framework/audio/engine/isynthesizer.h
  • framework/mpe/CMakeLists.txt
  • framework/mpe/articulationstringutils.h
  • framework/mpe/internal/articulationprofilesrepository.cpp
  • framework/mpe/qml/Muse/Mpe/articulationpatternitem.cpp
  • framework/vst/CMakeLists.txt
  • framework/vst/internal/synth/vstsequencer.cpp
  • framework/vst/internal/synth/vstsequencer.h
  • framework/vst/internal/synth/vstsynthesiser.cpp
  • framework/vst/internal/synth/vstsynthesiser.h
  • framework/vst/tests/CMakeLists.txt
  • framework/vst/tests/vstsequencertest.cpp
🛑 Comments failed to post (1)
framework/mpe/articulationstringutils.h (1)

31-38: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Use inline instead of static for namespace-scope maps in this public header.

ARTICULATION_FAMILY_NAMES and ARTICULATION_TYPE_NAMES are declared static const at namespace scope. This gives each map internal linkage. Every translation unit that includes this header gets its own private copy of the map. This header moved from internal/ to a public path specifically so more modules (VST3 keyswitch discovery, QML pattern items, the profile repository) can include it. Each new consumer duplicates the ~90-entry ARTICULATION_TYPE_NAMES map in its own object file.

Declare the maps inline const instead. In C++17, inline variables at namespace scope get external linkage and a single shared definition across all translation units.

♻️ Proposed fix
-static const std::unordered_map<ArticulationFamily, QString> ARTICULATION_FAMILY_NAMES {
+inline const std::unordered_map<ArticulationFamily, QString> ARTICULATION_FAMILY_NAMES {
     { ArticulationFamily::Undefined, "Undefined" },
     ...
 };

-static const std::unordered_map<ArticulationType, QString> ARTICULATION_TYPE_NAMES {
+inline const std::unordered_map<ArticulationType, QString> ARTICULATION_TYPE_NAMES {
     { ArticulationType::Undefined, "Undefined" },
     ...
 };

Please confirm the project targets C++17 or newer, so inline variables are available.

Also applies to: 40-166

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@framework/mpe/articulationstringutils.h` around lines 31 - 38, Replace static
with inline on the namespace-scope ARTICULATION_FAMILY_NAMES and
ARTICULATION_TYPE_NAMES definitions in articulationstringutils.h, preserving
const and their existing initializers. Confirm the project uses C++17 or newer
so these inline variables provide one shared definition across translation
units.

@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@manolo, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 26 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 3434a749-bf19-46ae-a924-08a2cdf60205

📥 Commits

Reviewing files that changed from the base of the PR and between 876e441 and 2fb594a.

📒 Files selected for processing (14)
  • framework/audio/engine/internal/audiocontext.cpp
  • framework/audio/engine/internal/audiofactory.cpp
  • framework/audio/engine/internal/audiofactory.h
  • framework/audio/engine/internal/iaudiofactory.h
  • framework/audio/engine/internal/nodes/eventaudionode.cpp
  • framework/audio/engine/internal/nodes/eventaudionode.h
  • framework/audio/engine/isynthesizer.h
  • framework/vst/CMakeLists.txt
  • framework/vst/internal/synth/vstsequencer.cpp
  • framework/vst/internal/synth/vstsequencer.h
  • framework/vst/internal/synth/vstsynthesiser.cpp
  • framework/vst/internal/synth/vstsynthesiser.h
  • framework/vst/tests/CMakeLists.txt
  • framework/vst/tests/vstsequencertest.cpp
📝 Walkthrough

Walkthrough

The audio event-source path now carries host track names into synthesizers. VST3 synthesizers forward supported names through channel context. VST3 keyswitch profiles are discovered from plugin controllers and passed to the sequencer. The sequencer emits latched and ranged keyswitch events for mapped articulations. Public MPE articulation string mappings use the public header path. New VST sequencer tests cover tremolo, legato, ranged modifiers, and chord keyswitch events.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 35.29% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The changes satisfy issue #34385 by adding VST3 keyswitch articulation handling and forwarding host track names through channel context.
Out of Scope Changes check ✅ Passed The public articulation utilities, audio plumbing, VST tests, and CMake updates directly support the linked feature objectives.
Title check ✅ Passed The title clearly and concisely describes the PR's primary change: sending note articulations to VST3 instruments as keyswitches.
Description check ✅ Passed The description identifies the issue, explains both features, states compatibility behavior, and completes all required checklist items.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@manolo
manolo force-pushed the vst-articulation-keyswitches branch 2 times, most recently from 1a89667 to 903cc44 Compare August 3, 2026 20:36
Move articulationstringutils.h out of internal/ so other modules can reuse the
articulation name table.
@manolo
manolo force-pushed the vst-articulation-keyswitches branch from 903cc44 to 876e441 Compare August 4, 2026 06:07

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@framework/vst/internal/synth/vstsequencer.cpp`:
- Around line 247-252: Update VstSequencer::addKeyswitchSpanEvent to avoid
adding duplicate keyswitch note-ons when the same timestamp and keyswitchPitch
already exist in destination, matching the deduplication behavior of the primary
keyswitch path. Preserve retriggers when either the timestamp or pitch differs.
- Around line 173-190: Update the primary-articulation selection logic around
keyswitchFor and the rank calculation so every mapped articulation has a
deterministic priority, including explicit ordering among non-span, non-Standard
types such as pizzicato and staccato. Ensure selection is independent of the
hash-map iteration order, and add a test that inserts the same articulations in
different orders and verifies the identical keyswitch is chosen.
- Around line 203-207: Update the event ordering around
sortNoteOnEventsByPitch() to assign keyswitch note-ons an explicit higher
priority than musical note-ons, rather than relying on MIDI pitch. Ensure every
keyswitch sharing a timestamp is emitted before the played note, including the
related logic at the additional keyswitch event site, and add a test where the
keyswitch pitch is above the musical note.
- Around line 168-170: Update the keyswitch selection in the arrangement-start
handling around keyswitchFor so it remains optional instead of defaulting to
pitch 0. Emit the keyswitch event only when a matching articulation or Standard
mapping exists, and skip emission when neither is advertised.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: c0d7d13a-a641-468a-bd93-67231a393ade

📥 Commits

Reviewing files that changed from the base of the PR and between 6adb702 and 876e441.

📒 Files selected for processing (18)
  • framework/audio/engine/internal/audiocontext.cpp
  • framework/audio/engine/internal/audiofactory.cpp
  • framework/audio/engine/internal/audiofactory.h
  • framework/audio/engine/internal/iaudiofactory.h
  • framework/audio/engine/internal/nodes/eventaudionode.cpp
  • framework/audio/engine/internal/nodes/eventaudionode.h
  • framework/audio/engine/isynthesizer.h
  • framework/mpe/CMakeLists.txt
  • framework/mpe/articulationstringutils.h
  • framework/mpe/internal/articulationprofilesrepository.cpp
  • framework/mpe/qml/Muse/Mpe/articulationpatternitem.cpp
  • framework/vst/CMakeLists.txt
  • framework/vst/internal/synth/vstsequencer.cpp
  • framework/vst/internal/synth/vstsequencer.h
  • framework/vst/internal/synth/vstsynthesiser.cpp
  • framework/vst/internal/synth/vstsynthesiser.h
  • framework/vst/tests/CMakeLists.txt
  • framework/vst/tests/vstsequencertest.cpp

Comment thread framework/vst/internal/synth/vstsequencer.cpp Outdated
Comment thread framework/vst/internal/synth/vstsequencer.cpp Outdated
Comment thread framework/vst/internal/synth/vstsequencer.cpp Outdated
Comment thread framework/vst/internal/synth/vstsequencer.cpp Outdated
@manolo
manolo force-pushed the vst-articulation-keyswitches branch from 876e441 to 4bd7f3e Compare August 4, 2026 06:37
manolo added 2 commits August 4, 2026 08:41
Query the plugin's IKeyswitchController and, when it advertises keyswitches, send
the articulation as a keyswitch note: latched per articulation, re-sent to
retrigger a span (tremolo), and forwarded as a range span for ranged modifiers
(legato). Adds unit tests for the sequencer.
…ntext

Thread the track name to the synthesizer and, for VST instruments, push it as the
VST3 channel context name, so a plugin can auto-select its sound from the staff name.
@manolo
manolo force-pushed the vst-articulation-keyswitches branch from 4bd7f3e to 2fb594a Compare August 4, 2026 06:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature request: automatically send articulation keyswitches to VST instruments

1 participant