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 <unordered_map>
26 : #include <map>
27 : #include <functional>
28 :
29 : #include "async/asyncable.h"
30 : #include "async/channel.h"
31 : #include "async/notification.h"
32 : #include "types/id.h"
33 : #include "modularity/ioc.h"
34 : #include "mpe/events.h"
35 : #include "mpe/iarticulationprofilesrepository.h"
36 :
37 : #include "../types/types.h"
38 : #include "playbackeventsrenderer.h"
39 : #include "playbacksetupdataresolver.h"
40 : #include "playbackcontext.h"
41 :
42 : namespace mu::engraving {
43 : class Score;
44 : class Note;
45 : class EngravingItem;
46 : class Segment;
47 : class Instrument;
48 : class RepeatList;
49 :
50 : class PlaybackModel : public muse::Contextable, public muse::async::Asyncable
51 : {
52 : public:
53 : muse::GlobalInject<muse::mpe::IArticulationProfilesRepository> profilesRepository;
54 :
55 : public:
56 23 : PlaybackModel(const muse::modularity::ContextPtr& iocCtx)
57 23 : : muse::Contextable(iocCtx) {}
58 :
59 : void load(Score* score);
60 : void reload();
61 :
62 : void setSendEventsOnScoreChange(const InstrumentTrackId& trackId, bool send);
63 : void sendEventsForChangedTracks();
64 :
65 : muse::async::Channel<InstrumentTrackIdSet> tracksDataChanged() const;
66 :
67 : bool isPlayRepeatsEnabled() const;
68 : void setPlayRepeats(const bool isEnabled);
69 :
70 : bool isPlayChordSymbolsEnabled() const;
71 : void setPlayChordSymbols(const bool isEnabled);
72 :
73 : bool useScoreDynamicsForOffstreamPlayback() const;
74 : void setUseScoreDynamicsForOffstreamPlayback(bool use);
75 :
76 : bool isMetronomeEnabled() const;
77 : void setIsMetronomeEnabled(const bool isEnabled);
78 :
79 : const InstrumentTrackId& metronomeTrackId() const;
80 : InstrumentTrackId chordSymbolsTrackId(const ID& partId) const;
81 : bool isChordSymbolsTrack(const InstrumentTrackId& trackId) const;
82 :
83 : bool hasSoundFlags(const InstrumentTrackId& trackId) const;
84 :
85 : muse::mpe::PlaybackData& resolveTrackPlaybackData(const InstrumentTrackId& trackId);
86 : muse::mpe::PlaybackData& resolveTrackPlaybackData(const ID& partId, const String& instrumentId);
87 :
88 : void triggerEventsForItems(const std::vector<const EngravingItem*>& items, muse::mpe::duration_t duration, bool flushSound);
89 : void triggerMetronome(int tick);
90 : void triggerCountIn(int tick, muse::mpe::duration_t& countInDuration);
91 :
92 : InstrumentTrackIdSet existingTrackIdSet() const;
93 : muse::async::Channel<InstrumentTrackId> trackAdded() const;
94 : muse::async::Channel<InstrumentTrackId> trackRemoved() const;
95 :
96 : private:
97 : static const InstrumentTrackId METRONOME_TRACK_ID;
98 : static const InstrumentTrackId CHORD_SYMBOLS_TRACK_ID;
99 :
100 : using ChangedTrackIdSet = InstrumentTrackIdSet;
101 :
102 : struct TickBoundaries
103 : {
104 : int tickFrom = -1;
105 : int tickTo = -1;
106 : };
107 :
108 : struct TrackBoundaries
109 : {
110 : track_idx_t trackFrom = muse::nidx;
111 : track_idx_t trackTo = muse::nidx;
112 : };
113 :
114 : struct PartData
115 : {
116 : muse::mpe::DynamicAutomationLayers dynamics;
117 : std::map<muse::mpe::timestamp_t, muse::mpe::SoundPresetChangeEventList> soundPresets;
118 : std::map<muse::mpe::timestamp_t, muse::mpe::TextArticulationEventList> textArticulations;
119 : std::map<muse::mpe::timestamp_t, muse::mpe::SyllableEventList> syllables;
120 : };
121 :
122 : InstrumentTrackId idKey(const EngravingItem* item) const;
123 : InstrumentTrackId idKey(const std::vector<const EngravingItem*>& items) const;
124 : InstrumentTrackId idKey(const ID& partId, const String& instrumentId) const;
125 :
126 : void update(const int tickFrom, const int tickTo, const track_idx_t trackFrom, const track_idx_t trackTo,
127 : ChangedTrackIdSet* trackChanges = nullptr);
128 : void updateSetupData();
129 : void updateContext(const track_idx_t trackFrom, const track_idx_t trackTo, const int tickFrom, const int tickTo);
130 : void applyContextToTrackData(const InstrumentTrackId& trackId, const PartData& data);
131 : void updateEvents(const int tickFrom, const int tickTo, const track_idx_t trackFrom, const track_idx_t trackTo,
132 : ChangedTrackIdSet* trackChanges = nullptr);
133 :
134 : void reloadMetronomeEvents();
135 :
136 : void processSegment(const int tickPositionOffset, const Segment* segment, const std::set<staff_idx_t>& staffIdxSet,
137 : bool isFirstChordRestSegmentOfMeasure, ChangedTrackIdSet* trackChanges);
138 : void processMeasureRepeat(const int tickPositionOffset, const MeasureRepeat* measureRepeat, const Measure* currentMeasure,
139 : const staff_idx_t staffIdx, ChangedTrackIdSet* trackChanges);
140 :
141 : bool hasToReloadTracks(const ScoreChanges& changes) const;
142 : bool hasToReloadScore(const ScoreChanges& changes) const;
143 : bool hasAutomationChange(const ScoreChanges& changes) const;
144 :
145 : void clearExpiredTracks();
146 : void clearExpiredEvents(const int tickFrom, const int tickTo, const track_idx_t trackFrom, const track_idx_t trackTo,
147 : ChangedTrackIdSet* trackChanges = nullptr);
148 : void collectChangesTracks(const InstrumentTrackId& trackId, ChangedTrackIdSet* result);
149 : void notifyAboutChanges(const InstrumentTrackIdSet& oldTracks, const InstrumentTrackIdSet& changedTracks);
150 :
151 : void sendEvents(const InstrumentTrackId& trackId);
152 :
153 : void removeEventsFromRange(const track_idx_t trackFrom, const track_idx_t trackTo, const muse::mpe::timestamp_t timestampFrom = -1,
154 : const muse::mpe::timestamp_t timestampTo = -1, ChangedTrackIdSet* trackChanges = nullptr);
155 : void removeTrackEvents(const InstrumentTrackId& trackId, const muse::mpe::timestamp_t timestampFrom = -1,
156 : const muse::mpe::timestamp_t timestampTo = -1, ChangedTrackIdSet* trackChanges = nullptr);
157 :
158 : bool shouldSkipChanges(const ScoreChanges& changes) const;
159 :
160 : TrackBoundaries trackBoundaries(const ScoreChanges& changes) const;
161 : TickBoundaries tickBoundaries(const ScoreChanges& changes) const;
162 :
163 : const RepeatList& repeatList() const;
164 :
165 : muse::mpe::ArticulationsProfilePtr defaultActiculationProfile(const InstrumentTrackId& trackId) const;
166 :
167 : static void applyTiedNotesTickBoundaries(const Note* note, TickBoundaries& tickBoundaries);
168 : static void applyTieTickBoundaries(const Tie* tie, TickBoundaries& tickBoundaries);
169 :
170 : Score* m_score = nullptr;
171 : bool m_expandRepeats = true;
172 : bool m_playChordSymbols = true;
173 : bool m_useScoreDynamicsForOffstreamPlayback = true;
174 : bool m_metronomeEnabled = true;
175 :
176 : PlaybackEventsRenderer m_renderer;
177 : PlaybackSetupDataResolver m_setupResolver;
178 :
179 : PlaybackContextPtr m_playbackCtx;
180 : std::unordered_map<InstrumentTrackId, muse::mpe::PlaybackData> m_playbackDataMap;
181 : std::unordered_map<InstrumentTrackId, bool> m_sendEventsOnScoreChangeMap;
182 :
183 : InstrumentTrackIdSet m_changedTrackIdSet;
184 :
185 : muse::async::Channel<InstrumentTrackIdSet> m_tracksDataChanged;
186 : muse::async::Channel<InstrumentTrackId> m_trackAdded;
187 : muse::async::Channel<InstrumentTrackId> m_trackRemoved;
188 : };
189 : }
|