Skip to content

Fix Mixer: Master channel's mute button has no effect - #34747

Open
tharos-devs wants to merge 2 commits into
musescore:mainfrom
tharos-devs:fix/mixer-master-mute
Open

Fix Mixer: Master channel's mute button has no effect#34747
tharos-devs wants to merge 2 commits into
musescore:mainfrom
tharos-devs:fix/mixer-master-mute

Conversation

@tharos-devs

@tharos-devs tharos-devs commented Aug 30, 2026

Copy link
Copy Markdown
Contributor

Resolves: #33239

The Mute button on the Mixer's Master channel updated the UI but never
actually silenced the audio.

MixerChannelItem::setMuted() only emits soloMuteStateChanged (never
controlParamsChanged). MixerPanelModel::buildMasterChannelItem() only
connected to controlParamsChanged/fxChainParamsChanged/auxSendsParamsChanged
— unlike every other channel builder (instrument, aux), which also
connect soloMuteStateChanged. So toggling Master mute never reached
playback()->setMasterControlParams(...), and therefore never reached the
audio engine.

The audio engine side already works correctly when actually invoked
(AudioContext::onControlParamsChanged()ControlNode::setMuted()
node disabled), so the fix only adds the missing soloMuteStateChanged
connection in buildMasterChannelItem(), mirroring the forceMute logic
already used in the existing controlParamsChanged handler.

  • I signed the CLA as tharosd:
  • 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).

MixerChannelItem::setMuted() only emits soloMuteStateChanged, never
controlParamsChanged. buildMasterChannelItem() only connected to
controlParamsChanged (unlike instrument/aux/video channels, which all
also connect soloMuteStateChanged), so toggling mute on the Master
channel updated the UI but never reached the audio engine.
The master channel's mute button never applies force-mute logic (that
concept only exists for instrument tracks when another track is
soloed), so the call added in 533592c was both wrong and referenced
a nonexistent controller method. Just push the mute state through.

@mathesoncalum mathesoncalum left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

There's definitely a missing connection here so this is the right idea, but this change alone won't ensure that muting the master channel persists when the score is saved/reopened (that's not a recent regression to my knowledge, but we should fix it).

On that note I'm actually noticing quite a few problems in the mixer right now relating to dirty state, save/reopen, etc. With this in mind @zacjansheski before we start refining this PR could you do a quick sweep of the mixer in 5.0 and report back any problems you find? For example I've noticed that moving the faders doesn't mark the score as edited which looks like a regression against 4.7:

faders.mov

Once we have a better idea of the bigger picture then we'll push ahead with this PR.

@tharos-devs

Copy link
Copy Markdown
Contributor Author

Thanks for flagging this — you're right, mute-state persistence across save/reopen is a separate issue from the one addressed in this PR.

As a starting point for the mixer sweep: while looking into Master-channel-specific issues, I found another "missing connection" bug of the same kind — VST3 FX editor windows never open for plugins added to the Master channel's FX slots (double-clicking does nothing), even though it works fine on instrument tracks. This was already tracked as #33872, its root cause has been identified, and I've just proposed a fix in PR #34763.

I'll keep digging into Master/mixer issues and see what else can be proposed.

@mathesoncalum

Copy link
Copy Markdown
Contributor

@tharos-devs please wait for @zacjansheski to report back to us before making any more changes or opening any more PRs/issues - you already have quite a few in progress.

@tharos-devs

tharos-devs commented Aug 31, 2026

Copy link
Copy Markdown
Contributor Author

Follow-up after digging further into the mixer sweep, as promised.

What's fixed

Applied this PR (#34747), #34676 (volume/pan/solo reset) and #34763 (VST3 FX editor on Master) together on top of main:

What's still broken

Master channel mute still does not persist across save/reopen, and does not mark the file as needing save — this PR fixes the live/runtime side (muting now actually mutes), but doesn't touch file persistence, which is a separate gap.

Root cause: ProjectAudioSettings's JSON schema never had a mute/solo field for the Master channel. Aux and instrument tracks each get a dedicated soloMuteState sub-object written alongside out (see buildAuxObject/buildTrackObject in projectaudiosettings.cpp), but the Master entry only ever writes outputParamsToJson(m_masterOutputParams) — no equivalent field, and no live-state source to populate it from (Master mute isn't tracked by INotationSoloMuteState, which is per-instrument-track only). Since the file is only marked dirty when a ProjectAudioSettings setter actually changes a value, and no such setter is ever called for Master mute, this also explains why toggling it never triggers a save prompt.

Aux send level (the knob) and the aux-send active toggle (e.g. the "Reverb" button) don't persist either, on any channel type — instrument, aux, or Master. Their auxSendsParamsChanged handlers in MixerPanelModel only push to the live engine (playback()->setAuxSendsParams(...)), with no equivalent round-trip back into ProjectAudioSettings. Unlike FX chain changes, IPlayback doesn't even expose an aux-sends-changed channel for the engine to report back through. Same consequence: no persistence, and no dirty-flag trigger.

Separately, not really a bug but worth flagging: the Metronome track's Mute button in the Mixer doesn't behave like every other track's mute — it's wired to NotationConfiguration::isMetronomeEnabled()/setIsMetronomeEnabled(), a global, app-wide preference (Settings::Key IS_METRONOME_ENABLED), not per-score state. So it correctly never marks the current file dirty (nothing in the file actually changed) — but it also means toggling it from one score's Mixer silently changes the metronome state for every other score too, which is likely surprising given the Mixer presents it as if it were a per-track/per-score control like all the others.

Next steps

I can propose a fix for each of the three points above, but I won't submit anything (commits, PRs, or issues) without explicit go-ahead first. Standing by for @zacjansheski's sweep report and your direction on how you'd like this prioritized.

@tharos-devs

tharos-devs commented Sep 2, 2026

Copy link
Copy Markdown
Contributor Author

Two more additions to the "What's still broken" list from my previous comment, found while testing with Aux channels visible:

  1. The "Aux 2" channel is missing its Audio FX slots. Comparing Aux 1 and Aux 2 side by side, Aux 1 correctly shows its Audio FX row (with an effect loaded in the example below), while Aux 2 shows no Audio FX row at all, even though it's the same type of channel. Screenshot attached.
image
  1. The Mixer's "..." menu → View submenu doesn't reflect what's actually visible, for any row. Pan, Volume, Sound, Audio FX, Aux send 1/2, Aux channel 1/2, etc. are all currently shown in the Mixer, yet none of the corresponding items in the View menu are checked/ticked — the menu state doesn't match actual visibility for any of them. Screenshot attached.
image

@tharos-devs

tharos-devs commented Sep 2, 2026

Copy link
Copy Markdown
Contributor Author

Another addition to the "What's still broken" list — a naming inconsistency between an Aux channel's own name and its "Aux send" button.

An Aux channel's name and its "Aux send" button (shown on every instrument track) should be the same thing, and usually are — but not always. For Aux 2, both are consistent: the channel is named "Aux 2", and every instrument's "Aux send 2" button also reads "Aux 2". For Aux 1, they disagree: the channel itself is named "Aux 1", but every instrument's "Aux send 1" button instead reads "Reverb". Either the channel should be named "Reverb" to match its own send button, or the send button should read "Aux 1" to match the channel — right now they don't agree, unlike Aux 2.

Root cause: the channel's own name and its send-button label are computed independently, by two different code paths in playbackcontroller.cpp's resolveAuxTrackTitle(). The channel's own name is fixed once, when the channel is first created, and never updates afterward. The send-button label, on the other hand, is recalculated every time from whatever is currently loaded on that channel: if it's exactly one "Muse Reverb" effect, the label becomes "Reverb" instead of the channel's actual name. Aux 1 has a Reverb effect loaded, so its send button shows "Reverb" while its own name stays "Aux 1" (never updated). Aux 2 has no effect loaded, so its send button falls back to the same generic label as its own name — which is why the two happen to already match there.

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.

Mute on the master strip does not work until touching another control

3 participants