Line data Source code
1 : /*
2 : * SPDX-License-Identifier: GPL-3.0-only
3 : * MuseScore-Studio-CLA-applies
4 : *
5 : * MuseScore Studio
6 : * Music Composition & Notation
7 : *
8 : * Copyright (C) MuseScore Limited and others
9 : *
10 : * This program is free software: you can redistribute it and/or modify
11 : * it under the terms of the GNU General Public License version 3 as
12 : * published by the Free Software Foundation.
13 : *
14 : * This program is distributed in the hope that it will be useful,
15 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 : * GNU General Public License for more details.
18 : *
19 : * You should have received a copy of the GNU General Public License
20 : * along with this program. If not, see <https://www.gnu.org/licenses/>.
21 : */
22 :
23 : #include "playbackcommandsstate.h"
24 :
25 : #include "notation/inotation.h"
26 : #include "notation/inotationinteraction.h"
27 :
28 : #include "../playbackcommands.h"
29 :
30 : using namespace muse;
31 : using namespace muse::rcommand;
32 : using namespace mu::playback;
33 : using namespace mu::notation;
34 :
35 : static const muse::Uri PROJECT_PAGE_URI("musescore://notation");
36 :
37 0 : std::string PlaybackCommandsState::moduleName() const
38 : {
39 0 : return "playback";
40 : }
41 :
42 0 : void PlaybackCommandsState::init()
43 : {
44 0 : projectSession()->projectChanged().onNotify(this, [this]() {
45 0 : updateCommandStates();
46 0 : });
47 :
48 0 : projectSession()->currentNotationChanged().onNotify(this, [this]() {
49 0 : const INotationPtr currNotation = projectSession()->currentNotation();
50 0 : if (!currNotation) {
51 0 : return;
52 : }
53 :
54 0 : const INotationInteractionPtr interaction = currNotation->interaction();
55 0 : interaction->selectionChanged().onNotify(this, [this]() {
56 0 : updateCommandStates({ PLAY_SELECTION_COMMAND });
57 0 : }, Asyncable::Mode::SetReplace);
58 :
59 0 : interaction->isEditingElementChanged().onNotify(this, [this]() {
60 0 : updateCommandStates({ PLAY_SELECTION_COMMAND });
61 0 : }, Asyncable::Mode::SetReplace);
62 0 : });
63 :
64 0 : if (interactive()) {
65 0 : interactive()->opened().onReceive(this, [this](const muse::Uri&) {
66 0 : updateCommandStates();
67 0 : });
68 : }
69 :
70 0 : playbackController()->isPlayAllowedChanged().onReceive(this, [this](bool) {
71 0 : updateCommandStates();
72 0 : });
73 :
74 0 : playbackController()->isPlayingChanged().onReceive(this, [this](bool) {
75 0 : updateCommandStates();
76 0 : });
77 :
78 0 : playbackController()->loopEnabledChanged().onReceive(this, [this](bool) {
79 0 : updateCommandStates({ LOOP_TOGGLE_COMMAND });
80 0 : });
81 :
82 0 : playbackController()->onlineSoundsChanged().onNotify(this, [this]() {
83 0 : updateCommandStates({ CLEAR_ONLINESOUNDS_CACHE_COMMAND });
84 0 : });
85 :
86 0 : notationConfiguration()->isMetronomeEnabledChanged().onNotify(this, [this]() {
87 0 : updateCommandStates({ METRONOME_TOGGLE_COMMAND });
88 0 : });
89 :
90 0 : notationConfiguration()->isMidiInputEnabledChanged().onNotify(this, [this]() {
91 0 : updateCommandStates({ MIDI_TOGGLE_COMMAND });
92 0 : });
93 :
94 0 : notationConfiguration()->midiUseWrittenPitch().ch.onReceive(this, [this](bool) {
95 0 : updateCommandStates({ MIDI_INPUT_WRITTEN_PITCH_COMMAND, MIDI_INPUT_SOUNDING_PITCH_COMMAND });
96 0 : });
97 :
98 0 : notationConfiguration()->isPlayRepeatsChanged().onNotify(this, [this]() {
99 0 : updateCommandStates({ REPEATS_TOGGLE_COMMAND });
100 0 : });
101 :
102 0 : notationConfiguration()->isPlayChordSymbolsChanged().onNotify(this, [this]() {
103 0 : updateCommandStates({ CHORDSYMBOLS_TOGGLE_COMMAND });
104 0 : });
105 :
106 0 : notationConfiguration()->isAutomaticallyPanEnabledChanged().onNotify(this, [this]() {
107 0 : updateCommandStates({ PAN_TOGGLE_COMMAND });
108 0 : });
109 :
110 0 : notationConfiguration()->isCountInEnabledChanged().onNotify(this, [this]() {
111 0 : updateCommandStates({ COUNTIN_TOGGLE_COMMAND });
112 0 : });
113 :
114 0 : playbackConfiguration()->playNotesWhenEditingChanged().onNotify(this, [this]() {
115 0 : updateCommandStates({ HEAR_PLAYBACK_WHEN_EDITING_TOGGLE_COMMAND });
116 0 : });
117 :
118 0 : m_moduleRegister = commandsRegister()->moduleRegister(moduleName());
119 0 : IF_ASSERT_FAILED(m_moduleRegister) {
120 0 : return;
121 : }
122 :
123 0 : updateCommandStates();
124 : }
125 :
126 0 : void PlaybackCommandsState::deinit()
127 : {
128 0 : projectSession()->projectChanged().disconnect(this);
129 0 : projectSession()->currentNotationChanged().disconnect(this);
130 0 : if (interactive()) {
131 0 : interactive()->opened().disconnect(this);
132 : }
133 0 : playbackController()->isPlayAllowedChanged().disconnect(this);
134 0 : playbackController()->isPlayingChanged().disconnect(this);
135 0 : playbackController()->loopEnabledChanged().disconnect(this);
136 0 : playbackController()->onlineSoundsChanged().disconnect(this);
137 0 : playbackConfiguration()->playNotesWhenEditingChanged().disconnect(this);
138 0 : notationConfiguration()->isMetronomeEnabledChanged().disconnect(this);
139 0 : notationConfiguration()->isMidiInputEnabledChanged().disconnect(this);
140 0 : notationConfiguration()->midiUseWrittenPitch().ch.disconnect(this);
141 0 : notationConfiguration()->isPlayRepeatsChanged().disconnect(this);
142 0 : notationConfiguration()->isPlayChordSymbolsChanged().disconnect(this);
143 0 : notationConfiguration()->isAutomaticallyPanEnabledChanged().disconnect(this);
144 0 : notationConfiguration()->isCountInEnabledChanged().disconnect(this);
145 0 : }
146 :
147 0 : void PlaybackCommandsState::updateCommandStates(const std::vector<Command>& commands)
148 : {
149 0 : IF_ASSERT_FAILED(m_moduleRegister) {
150 0 : return;
151 : }
152 :
153 0 : const auto& commandList = commands.empty() ? m_moduleRegister->commandList() : commands;
154 :
155 0 : for (const auto& command : commandList) {
156 0 : CommandState newState = commandState(command);
157 0 : if (m_commandStates[command] != newState) {
158 0 : m_commandStates[command] = newState;
159 0 : m_commandStateChanged.send(command, newState);
160 : }
161 : }
162 : }
163 :
164 0 : CommandState PlaybackCommandsState::commandState(const Command& command) const
165 : {
166 0 : if (!isProjectOpened()) {
167 0 : return CommandState(false, false);
168 : }
169 :
170 0 : if (!playbackController()->isPlayAllowed()) {
171 0 : return CommandState(false, false);
172 : }
173 :
174 0 : if (command == PLAY_COMMAND) {
175 0 : return CommandState(true, playbackController()->isPlaying());
176 0 : } else if (command == PLAY_SELECTION_COMMAND) {
177 0 : const INotationPtr currNotation = projectSession()->currentNotation();
178 0 : const INotationInteractionPtr interaction = currNotation ? currNotation->interaction() : nullptr;
179 0 : bool enabled = interaction && !interaction->isEditingElement();
180 0 : return CommandState(enabled, false);
181 0 : } else if (command == LOOP_TOGGLE_COMMAND) {
182 0 : return CommandState(true, playbackController()->isLoopEnabled());
183 0 : } else if (command == METRONOME_TOGGLE_COMMAND) {
184 0 : return CommandState(true, notationConfiguration()->isMetronomeEnabled());
185 0 : } else if (command == MIDI_TOGGLE_COMMAND) {
186 0 : return CommandState(true, notationConfiguration()->isMidiInputEnabled());
187 0 : } else if (command == MIDI_INPUT_WRITTEN_PITCH_COMMAND) {
188 0 : return CommandState(true, notationConfiguration()->midiUseWrittenPitch().val);
189 0 : } else if (command == MIDI_INPUT_SOUNDING_PITCH_COMMAND) {
190 0 : return CommandState(true, !notationConfiguration()->midiUseWrittenPitch().val);
191 0 : } else if (command == REPEATS_TOGGLE_COMMAND) {
192 0 : return CommandState(true, notationConfiguration()->isPlayRepeatsEnabled());
193 0 : } else if (command == CHORDSYMBOLS_TOGGLE_COMMAND) {
194 0 : return CommandState(true, notationConfiguration()->isPlayChordSymbolsEnabled());
195 0 : } else if (command == HEAR_PLAYBACK_WHEN_EDITING_TOGGLE_COMMAND) {
196 0 : return CommandState(true, playbackConfiguration()->playNotesWhenEditing());
197 0 : } else if (command == PAN_TOGGLE_COMMAND) {
198 0 : return CommandState(true, notationConfiguration()->isAutomaticallyPanEnabled());
199 0 : } else if (command == COUNTIN_TOGGLE_COMMAND) {
200 0 : return CommandState(true, notationConfiguration()->isCountInEnabled());
201 0 : } else if (command == CLEAR_ONLINESOUNDS_CACHE_COMMAND) {
202 0 : return CommandState(true, !playbackController()->onlineSounds().empty());
203 : }
204 :
205 0 : return CommandState(true, false);
206 : }
207 :
208 0 : async::Channel<Command, CommandState> PlaybackCommandsState::commandStateChanged() const
209 : {
210 0 : return m_commandStateChanged;
211 : }
212 :
213 0 : bool PlaybackCommandsState::isProjectOpened() const
214 : {
215 0 : if (!projectSession()->project()) {
216 0 : return false;
217 : }
218 :
219 0 : return true;
220 : }
|