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) 2025 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 : #pragma once
24 :
25 : #include <optional>
26 : #include <unordered_map>
27 :
28 : #include "modularity/ioc.h"
29 : #include "async/asyncable.h"
30 : #include "project/iprojectsession.h"
31 : #include "engraving/types/types.h"
32 : #include "notation/inotationautomation.h"
33 : #include "notation/inotationconfiguration.h"
34 : #include "notation/inotationplayback.h"
35 : #include "project/iprojectaudiosettings.h"
36 : #include "audio/main/iplayer.h"
37 : #include "audio/main/iplayback.h"
38 : #include "audio/common/audiotypes.h"
39 : #include "tours/itoursservice.h"
40 : #include "interactive/iinteractive.h"
41 :
42 : #include "drumsetloader.h"
43 :
44 : #include "../iplaybackcontroller.h"
45 : #include "../iplaybackconfiguration.h"
46 : #include "../isoundprofilesrepository.h"
47 :
48 : namespace mu::playback {
49 : class OnlineSoundsController;
50 : class PlaybackController : public IPlaybackController, public muse::async::Asyncable, public muse::Contextable
51 : {
52 : muse::GlobalInject<IPlaybackConfiguration> configuration;
53 : muse::GlobalInject<notation::INotationConfiguration> notationConfiguration;
54 : muse::ContextInject<ISoundProfilesRepository> profilesRepo = { this };
55 : muse::ContextInject<muse::audio::IPlayback> playback = { this };
56 : muse::ContextInject<project::IProjectSession> projectSession = { this };
57 : muse::ContextInject<muse::tours::IToursService> tours = { this };
58 : muse::ContextInject<muse::IInteractive> interactive = { this };
59 :
60 : public:
61 : PlaybackController(const muse::modularity::ContextPtr& iocCtx);
62 : ~PlaybackController() override;
63 :
64 : void init();
65 : void deinit();
66 :
67 : // IPlaybackState
68 : bool isPlaying() const override;
69 : muse::async::Channel<bool> isPlayingChanged() const override;
70 :
71 : muse::audio::secs_t playbackPosition() const override;
72 : muse::async::Channel<muse::audio::secs_t> playbackPositionChanged() const override;
73 : // --------------
74 :
75 : // IPlaybackController
76 : bool isPlaybackInited() const override;
77 : muse::async::Channel<bool> playbackInitedChanged() const override;
78 :
79 : bool isPlayAllowed() const override;
80 : muse::async::Channel<bool> isPlayAllowedChanged() const override;
81 :
82 : muse::Ret togglePlay() override;
83 : muse::Ret play(bool showErrors = true) override;
84 : muse::Ret playFromSelection(bool showErrors = true) override;
85 : muse::Ret pause(bool select = false) override;
86 : muse::Ret stop() override;
87 : muse::Ret rewind(muse::secs_t secs) override;
88 :
89 : bool isLoopEnabled() const override;
90 : muse::async::Channel<bool> loopEnabledChanged() const override;
91 : muse::Ret toggleLoopPlayback() override;
92 : muse::Ret addLoopBoundary(LoopBoundaryType type) override;
93 :
94 : muse::Ret toggleMetronome() override;
95 :
96 : muse::Ret toggleMidiInput() override;
97 : muse::Ret setMidiUseWrittenPitch(bool useWrittenPitch) override;
98 :
99 : muse::Ret togglePlayRepeats() override;
100 : muse::Ret togglePlayChordSymbols() override;
101 : muse::Ret toggleAutomaticallyPan() override;
102 : muse::Ret toggleCountIn() override;
103 : muse::Ret toggleHearPlaybackWhenEditing() override;
104 :
105 : muse::Ret reloadPlaybackCache() override;
106 :
107 : const InstrumentTrackIdMap& instrumentTrackIdMap() const override;
108 : const AuxTrackIdMap& auxTrackIdMap() const override;
109 :
110 : muse::async::Channel<muse::audio::TrackId> trackAdded() const override;
111 : muse::async::Channel<muse::audio::TrackId> trackRemoved() const override;
112 :
113 : std::string auxChannelName(muse::audio::aux_channel_idx_t index) const override;
114 : muse::async::Channel<muse::audio::aux_channel_idx_t, std::string> auxChannelNameChanged() const override;
115 :
116 : muse::async::Promise<muse::audio::SoundPresetList> availableSoundPresets(
117 : const engraving::InstrumentTrackId& instrumentTrackId) const override;
118 :
119 : const SoloMuteState& trackSoloMuteState(const engraving::InstrumentTrackId& trackId) const override;
120 : void setTrackSoloMuteState(const engraving::InstrumentTrackId& trackId, const SoloMuteState& state) override;
121 :
122 : void playElements(const std::vector<const engraving::EngravingItem*>& elements,
123 : const PlayParams& params = PlayParams(), bool isMidi = false) override;
124 : void playNotes(const engraving::NoteValList& notes, engraving::staff_idx_t staffIdx, const engraving::Segment* segment,
125 : const PlayParams& params = PlayParams()) override;
126 : void playMetronome(int tick) override;
127 :
128 : void triggerControllers(const muse::mpe::ControllerChangeEventList& list, engraving::staff_idx_t staffIdx, int tick) override;
129 :
130 : void seekElement(const engraving::EngravingItem* element, bool flushSound = true) override;
131 : void seekBeat(int measureIndex, int beatIndex, bool flushSound = true) override;
132 :
133 : muse::secs_t totalPlayTime() const override;
134 : muse::async::Notification totalPlayTimeChanged() const override;
135 :
136 : const notation::Tempo& currentTempo() const override;
137 : muse::async::Notification currentTempoChanged() const override;
138 :
139 : engraving::MeasureBeat currentBeat() const override;
140 : muse::audio::secs_t beatToSecs(int measureIndex, int beatIndex) const override;
141 :
142 : double tempoMultiplier() const override;
143 : void setTempoMultiplier(double multiplier) override;
144 :
145 : muse::Progress loadingProgress() const override;
146 :
147 : void applyProfile(const SoundProfileName& profileName) override;
148 :
149 : void setNotation(notation::INotationPtr notation) override;
150 : void setMasterNotation(notation::IMasterNotationPtr masterNotation);
151 :
152 : void setIsExportingAudio(bool exporting) override;
153 :
154 : const std::map<muse::audio::TrackId, muse::audio::AudioResourceMeta>& onlineSounds() const override;
155 : muse::async::Notification onlineSoundsChanged() const override;
156 : muse::Progress onlineSoundsProcessingProgress() const override;
157 :
158 : private:
159 : muse::audio::IPlayerPtr currentPlayer() const;
160 :
161 : notation::INotationPlaybackPtr notationPlayback() const;
162 : notation::INotationPartsPtr masterNotationParts() const;
163 : notation::INotationSelectionPtr selection() const;
164 : notation::INotationSelectionRangePtr selectionRange() const;
165 : notation::INotationInteractionPtr interaction() const;
166 :
167 : uint64_t notationPlaybackKey() const;
168 :
169 : void updateCurrentTempo();
170 :
171 : void seekRawTick(const muse::midi::tick_t tick, const bool flushSound = true);
172 : void seek(const muse::audio::secs_t secs, const bool flushSound = true);
173 :
174 : bool isPaused() const;
175 : bool isLoaded() const;
176 :
177 : bool loopBoundariesSet() const;
178 :
179 : void onNotationChanged();
180 : void onPartChanged(const engraving::Part* part);
181 : void onPlaybackStatusChanged();
182 :
183 : void onSelectionChanged();
184 : void seekRangeSelection();
185 :
186 : void onAudioResourceChanged(const muse::audio::TrackId trackId, const mu::engraving::InstrumentTrackId& instrumentTrackId,
187 : const muse::audio::AudioResourceMeta& oldMeta, const muse::audio::AudioResourceMeta& newMeta);
188 :
189 : bool shouldLoadDrumset(const engraving::InstrumentTrackId& instrumentTrackId, const muse::audio::AudioResourceMeta& oldMeta,
190 : const muse::audio::AudioResourceMeta& newMeta) const;
191 :
192 : void addSoundFlagsIfNeed(const std::vector<engraving::EngravingItem*>& selection);
193 :
194 : void doRewind(muse::secs_t newPosition);
195 : void doPlay();
196 : void doPause(bool select = false);
197 : void doStop();
198 : void doResume();
199 :
200 : muse::audio::secs_t playbackStartSecs() const;
201 :
202 : engraving::InstrumentTrackIdSet instrumentTrackIdSetForRangePlayback() const;
203 :
204 : void addLoopBoundaryToTick(LoopBoundaryType type, int tick);
205 : void updateLoop();
206 :
207 : void enableLoop();
208 : void disableLoop();
209 :
210 : project::IProjectAudioSettingsPtr audioSettings() const;
211 :
212 : void resetPlayback();
213 : void setupPlaybackIfNeed();
214 : void setupPlayback();
215 : void subscribeOnAudioParamsChanges();
216 : void setupTracks();
217 : void setupPlayer();
218 :
219 : void updateSoloMuteStates();
220 : void updateAuxMuteStates();
221 :
222 : using TrackAddFinished = std::function<void ()>;
223 :
224 : void addTrack(const engraving::InstrumentTrackId& instrumentTrackId, bool projectHadNoAudioSettings,
225 : const TrackAddFinished& onFinished);
226 : void doAddTrack(const engraving::InstrumentTrackId& instrumentTrackId, const std::string& title, bool projectHadNoAudioSettings,
227 : const TrackAddFinished& onFinished);
228 : void addAuxTrack(muse::audio::aux_channel_idx_t index, bool projectHadNoAudioSettings, const TrackAddFinished& onFinished);
229 :
230 : void setTrackActivity(const engraving::InstrumentTrackId& instrumentTrackId, const bool isActive);
231 : project::AudioOutputParams trackOutputParams(const engraving::InstrumentTrackId& instrumentTrackId) const;
232 :
233 : muse::audio::ControlParams trackControlParams(const engraving::InstrumentTrackId& instrumentTrackId,
234 : const project::AudioOutputParams& outParams, bool rebuildVolume = true,
235 : bool rebuildPan = true);
236 : void removeTrack(const engraving::InstrumentTrackId& instrumentTrackId);
237 :
238 : void onAutomationDataChanged(const notation::AutomationChanges& changes);
239 0 : void resendAutomatedControlParams(std::optional<engraving::InstrumentTrackIdSet> volumeTrackIds = std::nullopt,
240 0 : std::optional<engraving::InstrumentTrackIdSet> panTrackIds = std::nullopt);
241 :
242 : void onTrackNewlyAdded(const engraving::InstrumentTrackId& instrumentTrackId);
243 :
244 : muse::audio::secs_t playedTickToSecs(int tick) const;
245 :
246 : notation::INotationPtr m_notation;
247 : notation::IMasterNotationPtr m_masterNotation;
248 : muse::audio::IPlayerPtr m_player;
249 : bool m_needSetupPlayback = false;
250 : bool m_isPlaybackInited = false;
251 : muse::async::Channel<bool> m_playbackInited;
252 :
253 : muse::async::Channel<bool> m_isPlayAllowedChanged;
254 : muse::async::Channel<bool> m_isPlayingChanged;
255 : muse::async::Channel<muse::audio::secs_t> m_playbackPositionChanged;
256 : muse::async::Channel<bool> m_loopEnabledChanged;
257 : muse::async::Notification m_totalPlayTimeChanged;
258 : muse::async::Notification m_currentTempoChanged;
259 :
260 : muse::midi::tick_t m_currentTick = 0;
261 : notation::Tempo m_currentTempo;
262 :
263 : muse::async::Channel<muse::audio::TrackId> m_trackAdded;
264 : muse::async::Channel<muse::audio::TrackId> m_trackRemoved;
265 :
266 : muse::async::Channel<muse::audio::aux_channel_idx_t, std::string> m_auxChannelNameChanged;
267 :
268 : muse::async::Asyncable m_seqAsyncReceiver; //! HACK - see PlaybackController::setupTracks
269 :
270 : InstrumentTrackIdMap m_instrumentTrackIdMap;
271 : AuxTrackIdMap m_auxTrackIdMap;
272 :
273 : std::unordered_map<engraving::InstrumentTrackId, muse::audio::ControlParams> m_automatedControlParamsCache;
274 :
275 : muse::Progress m_loadingProgress;
276 : size_t m_loadingTrackCount = 0;
277 :
278 : bool m_isExportingAudio = false;
279 : bool m_isRangeSelection = false;
280 :
281 : DrumsetLoader m_drumsetLoader;
282 : std::unique_ptr<OnlineSoundsController> m_onlineSoundsController;
283 :
284 : bool m_measureInputLag = false;
285 : };
286 : }
|