-
Notifications
You must be signed in to change notification settings - Fork 3.2k
Implement Reset Mixer Settings to Default Button #33799
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -69,6 +69,7 @@ static const ActionCode REPEAT_CODE("repeat"); | |
| static const ActionCode PLAY_CHORD_SYMBOLS_CODE("play-chord-symbols"); | ||
| static const ActionCode PLAYBACK_SETUP("playback-setup"); | ||
| static const ActionCode TOGGLE_HEAR_PLAYBACK_WHEN_EDITING_CODE("toggle-hear-playback-when-editing"); | ||
| static const ActionCode RESET_MIXER_CODE("reset-mixer"); | ||
|
|
||
| static AudioOutputParams makeReverbOutputParams() | ||
| { | ||
|
|
@@ -127,6 +128,7 @@ void PlaybackController::init() | |
| dispatcher()->reg(this, PLAYBACK_SETUP, this, &PlaybackController::openPlaybackSetupDialog); | ||
| dispatcher()->reg(this, TOGGLE_HEAR_PLAYBACK_WHEN_EDITING_CODE, this, &PlaybackController::toggleHearPlaybackWhenEditing); | ||
| dispatcher()->reg(this, "playback-reload-cache", this, &PlaybackController::reloadPlaybackCache); | ||
| dispatcher()->reg(this, RESET_MIXER_CODE, this, &PlaybackController::resetMixerToDefaults); | ||
|
|
||
| m_onlineSoundsController->regActions(); | ||
|
|
||
|
|
@@ -956,6 +958,84 @@ void PlaybackController::reloadPlaybackCache() | |
| } | ||
| } | ||
|
|
||
| void PlaybackController::resetMixerToDefaults() | ||
| { | ||
| IF_ASSERT_FAILED(audioSettings() && notationPlayback()) { | ||
| return; | ||
| } | ||
|
|
||
| InstrumentTrackIdSet existingTrackIdSet = notationPlayback()->existingTrackIdSet(); | ||
|
|
||
| for (const InstrumentTrackId& instrumentTrackId : existingTrackIdSet) { | ||
| if (!muse::contains(m_instrumentTrackIdMap, instrumentTrackId)) { | ||
| continue; | ||
| } | ||
|
|
||
| AudioOutputParams outParams = trackOutputParams(instrumentTrackId); | ||
| const bool wasMuted = outParams.muted; | ||
| const bool wasForceMute = outParams.forceMute; | ||
|
|
||
| outParams.volume = 0.f; | ||
| outParams.balance = 0.f; | ||
|
|
||
| const AudioInputParams inParams = audioSettings()->trackInputParams(instrumentTrackId); | ||
| const AudioSourceType sourceType = inParams.isValid() ? inParams.type() : AudioSourceType::Fluid; | ||
| const muse::String instrumentSoundId = inParams.resourceMeta.attributeVal(PLAYBACK_SETUP_DATA_ATTRIBUTE); | ||
|
|
||
| const bool isMetronome = instrumentTrackId == notationPlayback()->metronomeTrackId(); | ||
|
|
||
| if (isMetronome) { | ||
| outParams.muted = wasMuted; | ||
| } else { | ||
| const auto soloMuteState = trackSoloMuteState(instrumentTrackId); | ||
| outParams.solo = soloMuteState.solo; | ||
| outParams.muted = soloMuteState.mute; | ||
| } | ||
| outParams.forceMute = wasForceMute; | ||
|
|
||
| if (!isMetronome) { | ||
| if (outParams.auxSends.empty()) { | ||
| const auto& auxMap = m_auxTrackIdMap; | ||
| for (aux_channel_idx_t idx = 0; idx < static_cast<aux_channel_idx_t>(auxMap.size()); ++idx) { | ||
| gain_t signalAmount = configuration()->defaultAuxSendValue(idx, sourceType, instrumentSoundId); | ||
| outParams.auxSends.emplace_back(AuxSendParams { signalAmount, true }); | ||
| } | ||
| } else { | ||
| for (aux_channel_idx_t idx = 0; idx < static_cast<aux_channel_idx_t>(outParams.auxSends.size()); ++idx) { | ||
| gain_t signalAmount = configuration()->defaultAuxSendValue(idx, sourceType, instrumentSoundId); | ||
| outParams.auxSends[idx] = AuxSendParams { signalAmount, true }; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| audioSettings()->setTrackOutputParams(instrumentTrackId, outParams); | ||
|
|
||
| audio::TrackId trackId = m_instrumentTrackIdMap.at(instrumentTrackId); | ||
| playback()->setControlParams(trackId, outParams.control()); | ||
| playback()->setAuxSendsParams(trackId, outParams.auxSends); | ||
| } | ||
|
|
||
| AudioOutputParams masterParams = audioSettings()->masterAudioOutputParams(); | ||
| const bool masterWasMuted = masterParams.muted; | ||
| const bool masterWasForceMute = masterParams.forceMute; | ||
|
|
||
| masterParams.volume = 0.f; | ||
| masterParams.balance = 0.f; | ||
|
|
||
| masterParams.muted = masterWasMuted; | ||
| masterParams.forceMute = masterWasForceMute; | ||
|
|
||
| audioSettings()->setMasterAudioOutputParams(masterParams); | ||
| playback()->setMasterControlParams(masterParams.control()); | ||
|
|
||
| m_mixerResetRequested.notify(); | ||
| } | ||
|
Comment on lines
+961
to
+1032
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Reset the aux/reverb outputs, not just track sends. Lines 996-1029 rebuild per-track sends and master control params, but they never restore 🤖 Prompt for AI Agents |
||
|
|
||
| muse::async::Notification PlaybackController::mixerResetRequested() const | ||
| { | ||
| return m_mixerResetRequested; | ||
| } | ||
|
|
||
| void PlaybackController::openPlaybackSetupDialog() | ||
| { | ||
| interactive()->open("musescore://playback/soundprofilesdialog"); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -42,6 +42,10 @@ ColumnLayout { | |
| property Component toolbarComponent: MixerPanelToolbar { | ||
| navigation.section: root.navigationSection | ||
| navigation.order: root.contentNavigationPanelOrderStart | ||
|
|
||
| onResetMixerRequested: { | ||
| root.resetMixer() | ||
| } | ||
|
Comment on lines
+46
to
+48
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Route reset through the controller action, not a local QML reset implementation. Line 46 and Line 47 send the button flow to Also applies to: 96-113 🤖 Prompt for AI Agents |
||
| } | ||
|
|
||
| signal resizeRequested(var newWidth, var newHeight) | ||
|
|
@@ -89,6 +93,25 @@ ColumnLayout { | |
| } | ||
| } | ||
|
|
||
| function resetMixer() { | ||
| for (let i = 0; i < mixerPanelModel.count; i++) { | ||
| let item = mixerPanelModel.get(i) | ||
| if (!item || !item.channelItem) { | ||
| continue | ||
| } | ||
|
|
||
| item.channelItem.volumeLevel = 0 | ||
| item.channelItem.balance = 0 | ||
|
|
||
| let auxList = item.channelItem.auxSendItemList | ||
| for (let j = 0; j < auxList.length; j++) { | ||
| let aux = auxList[j] | ||
| aux.isActive = true | ||
| aux.audioSignalPercentage = 30 | ||
| } | ||
| } | ||
| } | ||
|
|
||
| MixerPanelModel { | ||
| id: mixerPanelModel | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,147 @@ | ||
| /* | ||
| * SPDX-License-Identifier: GPL-3.0-only | ||
| * MuseScore-CLA-applies | ||
| * | ||
| * MuseScore Studio | ||
| * Music Composition & Notation | ||
| * | ||
| * Copyright (C) 2021 MuseScore Limited and others | ||
| * | ||
| * This program is free software: you can redistribute it and/or modify | ||
| * it under the terms of the GNU General Public License version 3 as | ||
| * published by the Free Software Foundation. | ||
| * | ||
| * This program is distributed in the hope that it will be useful, | ||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| * GNU General Public License for more details. | ||
| * | ||
| * You should have received a copy of the GNU General Public License | ||
| * along with this program. If not, see <https://www.gnu.org/licenses/>. | ||
| */ | ||
|
|
||
| import QtQuick | ||
| import QtQuick.Layouts | ||
|
|
||
| import Muse.Ui | ||
| import Muse.UiComponents | ||
|
|
||
| Item { | ||
| id: root | ||
|
|
||
| property NavigationPanel navigationPanel: null | ||
|
|
||
| signal resetMixerRequested() | ||
|
|
||
| anchors.fill: parent | ||
|
|
||
| FlatButton { | ||
| id: resetMixerButton | ||
|
|
||
| anchors.right: parent.right | ||
| anchors.rightMargin: 6 | ||
| anchors.verticalCenter: parent.verticalCenter | ||
|
|
||
| width: 90 | ||
|
|
||
| text: qsTrc("playback", "Reset Mixer") | ||
| transparent: false | ||
| accentButton: false | ||
|
|
||
| navigation.name: "Reset mixer" | ||
| navigation.panel: root.navigationPanel | ||
| navigation.row: 0 | ||
|
|
||
| onClicked: { | ||
| resetVolumesConfirmPopup.toggleOpened() | ||
| } | ||
| } | ||
|
|
||
| StyledPopupView { | ||
| id: resetVolumesConfirmPopup | ||
|
|
||
| anchorItem: root | ||
|
|
||
| contentWidth: 360 | ||
| contentHeight: popupContentColumn.implicitHeight | ||
|
|
||
| placementPolicies: PopupView.PreferBelow | PopupView.PreferAbove | ||
| closePolicies: PopupView.CloseOnEscape | PopupView.CloseOnPressOutsideParent | ||
|
|
||
| ColumnLayout { | ||
| id: popupContentColumn | ||
|
|
||
| width: 360 | ||
| spacing: 12 | ||
|
|
||
| RowLayout { | ||
| Layout.fillWidth: true | ||
|
|
||
| StyledTextLabel { | ||
| Layout.fillWidth: true | ||
| font: ui.theme.bodyBoldFont | ||
| text: qsTrc("playback", "Confirm mixer reset") | ||
| horizontalAlignment: Text.AlignLeft | ||
| } | ||
|
|
||
| FlatButton { | ||
| icon: IconCode.CLOSE_X_ROUNDED | ||
| transparent: true | ||
| accentButton: false | ||
|
|
||
| navigation.name: qsTrc("global", "Close") | ||
| navigation.panel: root.navigationPanel | ||
| navigation.row: 1 | ||
|
|
||
| onClicked: { | ||
| resetVolumesConfirmPopup.close() | ||
| } | ||
| } | ||
| } | ||
|
|
||
| StyledTextLabel { | ||
| Layout.fillWidth: true | ||
| text: qsTrc("playback", "This will reset the mixer to default settings. Are you sure you want to continue?") | ||
| wrapMode: Text.WordWrap | ||
| horizontalAlignment: Text.AlignLeft | ||
| } | ||
|
|
||
| RowLayout { | ||
| Layout.fillWidth: true | ||
| spacing: 8 | ||
|
|
||
| Item { | ||
| Layout.fillWidth: true | ||
| } | ||
|
|
||
| FlatButton { | ||
| text: qsTrc("global", "Cancel") | ||
| transparent: true | ||
| accentButton: false | ||
|
|
||
| navigation.panel: root.navigationPanel | ||
| navigation.row: 2 | ||
|
|
||
| onClicked: { | ||
| resetVolumesConfirmPopup.close() | ||
| } | ||
| } | ||
|
|
||
| FlatButton { | ||
| text: qsTrc("global", "Confirm") | ||
| transparent: false | ||
| accentButton: true | ||
|
|
||
| navigation.panel: root.navigationPanel | ||
| navigation.row: 3 | ||
|
|
||
| onClicked: { | ||
| resetVolumesConfirmPopup.close() | ||
| root.resetMixerRequested() | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Route the shortcut through the same confirmation gate.
Line 131 binds
reset-mixerdirectly toresetMixerToDefaults(). With the newCtrl+Shift+Rshortcut, that makes the destructive reset execute immediately, while the confirmation in this PR exists only in the button/QML flow. The shortcut needs the same guard or it can wipe mixer state in one keystroke.🤖 Prompt for AI Agents