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) 2021 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 "mixerpanelmodel.h"
24 :
25 : #include "async/notifylist.h"
26 : #include "defer.h"
27 : #include "log.h"
28 : #include "translation.h"
29 :
30 : #include "notation/imasternotation.h"
31 : #include "notation/inotationautomation.h"
32 : #include "notation/inotationparts.h"
33 : #include "notation/inotationplayback.h"
34 :
35 : #include "project/inotationproject.h"
36 :
37 : using namespace muse;
38 : using namespace mu::playback;
39 : using namespace muse::audio;
40 : using namespace mu::engraving;
41 : using namespace mu::notation;
42 : using namespace mu::project;
43 :
44 : static constexpr int INVALID_INDEX = -1;
45 :
46 0 : MixerPanelModel::MixerPanelModel(QObject* parent)
47 0 : : QAbstractListModel(parent), muse::Contextable(muse::iocCtxForQmlObject(this))
48 : {
49 0 : }
50 :
51 0 : void MixerPanelModel::componentComplete()
52 : {
53 0 : init();
54 0 : }
55 :
56 0 : void MixerPanelModel::init()
57 : {
58 : //! NOTE Must be set from Qml
59 0 : DO_ASSERT(m_navigationSection);
60 :
61 0 : controller()->playbackInitedChanged().onReceive(this, [this](bool) {
62 0 : reload();
63 0 : });
64 :
65 0 : controller()->trackAdded().onReceive(this, [this](const TrackId trackId) {
66 0 : onTrackAdded(trackId);
67 0 : });
68 :
69 0 : controller()->trackRemoved().onReceive(this, [this](const TrackId trackId) {
70 0 : removeItem(trackId);
71 0 : });
72 :
73 0 : reload();
74 0 : }
75 :
76 0 : void MixerPanelModel::reload()
77 : {
78 0 : TRACEFUNC;
79 :
80 0 : reloadItems();
81 0 : }
82 :
83 0 : QVariantMap MixerPanelModel::get(int index)
84 : {
85 0 : QVariantMap result;
86 :
87 0 : QHash<int, QByteArray> names = roleNames();
88 0 : QHashIterator<int, QByteArray> i(names);
89 0 : while (i.hasNext()) {
90 0 : i.next();
91 0 : QModelIndex idx = this->index(index, 0);
92 0 : QVariant data = idx.data(i.key());
93 0 : result[i.value()] = data;
94 0 : }
95 :
96 0 : return result;
97 0 : }
98 :
99 0 : QVariant MixerPanelModel::data(const QModelIndex& index, int role) const
100 : {
101 0 : if (!index.isValid() || index.row() >= rowCount() || role != ChannelItemRole) {
102 0 : return QVariant();
103 : }
104 :
105 0 : return QVariant::fromValue(m_mixerChannelList.at(index.row()));
106 : }
107 :
108 0 : int MixerPanelModel::rowCount(const QModelIndex&) const
109 : {
110 0 : return m_mixerChannelList.count();
111 : }
112 :
113 0 : QHash<int, QByteArray> MixerPanelModel::roleNames() const
114 : {
115 : static const QHash<int, QByteArray> roles = {
116 0 : { ChannelItemRole, "channelItem" }
117 0 : };
118 :
119 0 : return roles;
120 0 : }
121 :
122 0 : void MixerPanelModel::reloadItems()
123 : {
124 0 : TRACEFUNC;
125 :
126 0 : beginResetModel();
127 :
128 0 : DEFER {
129 0 : endResetModel();
130 0 : emit rowCountChanged();
131 0 : };
132 :
133 0 : clear();
134 :
135 0 : if (!controller()->isPlaybackInited()) {
136 0 : return;
137 : }
138 :
139 0 : const auto& instrumentTrackIdMap = controller()->instrumentTrackIdMap();
140 :
141 0 : auto addInstrumentTrack = [this, &instrumentTrackIdMap](const InstrumentTrackId& instrumentTrackId, bool isPrimary = true) {
142 0 : auto search = instrumentTrackIdMap.find(instrumentTrackId);
143 0 : if (search == instrumentTrackIdMap.cend()) {
144 0 : return;
145 : }
146 :
147 0 : m_mixerChannelList.push_back(buildInstrumentChannelItem(search->second, instrumentTrackId, isPrimary));
148 0 : };
149 :
150 0 : async::NotifyList<const Part*> partList = masterNotationParts()->partList();
151 0 : for (const Part* part : partList) {
152 0 : std::string primaryInstrId = part->instrument()->id().toStdString();
153 :
154 0 : for (const InstrumentTrackId& instrumentTrackId : part->instrumentTrackIdList()) {
155 0 : bool isPrimary = instrumentTrackId.instrumentId == primaryInstrId;
156 0 : addInstrumentTrack(instrumentTrackId, isPrimary);
157 0 : }
158 0 : }
159 0 : for (auto it = instrumentTrackIdMap.cbegin(); it != instrumentTrackIdMap.cend(); ++it) {
160 0 : if (notationPlayback()->isChordSymbolsTrack(it->first)) {
161 0 : addInstrumentTrack(it->first);
162 : }
163 : }
164 :
165 0 : addInstrumentTrack(notationPlayback()->metronomeTrackId());
166 :
167 0 : const auto& auxTrackIdMap = controller()->auxTrackIdMap();
168 0 : for (auto it = auxTrackIdMap.cbegin(); it != auxTrackIdMap.cend(); ++it) {
169 0 : if (configuration()->isAuxChannelVisible(it->first)) {
170 0 : m_mixerChannelList.push_back(buildAuxChannelItem(it->first, it->second));
171 : }
172 : }
173 :
174 0 : m_masterChannelItem = buildMasterChannelItem();
175 0 : m_mixerChannelList.append(m_masterChannelItem);
176 :
177 0 : updateItemsPanelsOrder();
178 0 : setupConnections();
179 0 : }
180 :
181 0 : void MixerPanelModel::onTrackAdded(const TrackId& trackId)
182 : {
183 0 : TRACEFUNC;
184 :
185 0 : const IPlaybackController::InstrumentTrackIdMap& instrumentTracks = controller()->instrumentTrackIdMap();
186 0 : auto instrumentIt = std::find_if(instrumentTracks.cbegin(), instrumentTracks.cend(), [trackId](const auto& pair) {
187 0 : return pair.second == trackId;
188 : });
189 :
190 0 : if (instrumentIt != instrumentTracks.end()) {
191 0 : const InstrumentTrackId& instrumentTrackId = instrumentIt->first;
192 0 : const Part* part = masterNotationParts()->part(instrumentTrackId.partId);
193 0 : bool isPrimary = part ? part->instrument()->id() == instrumentTrackId.instrumentId : true;
194 0 : MixerChannelItem* item = buildInstrumentChannelItem(trackId, instrumentTrackId, isPrimary);
195 0 : int index = resolveInsertIndex(instrumentTrackId);
196 :
197 0 : addItem(item, index);
198 0 : return;
199 : }
200 :
201 0 : const IPlaybackController::AuxTrackIdMap& auxTracks = controller()->auxTrackIdMap();
202 0 : auto auxIt = std::find_if(auxTracks.begin(), auxTracks.end(), [trackId](const auto& pair) {
203 0 : return pair.second == trackId;
204 : });
205 :
206 0 : if (auxIt != auxTracks.end()) {
207 0 : if (configuration()->isAuxChannelVisible(auxIt->first)) {
208 0 : addItem(buildAuxChannelItem(auxIt->first, trackId), m_mixerChannelList.size() - 1);
209 : }
210 : }
211 0 : }
212 :
213 0 : void MixerPanelModel::addItem(MixerChannelItem* item, int index)
214 : {
215 0 : TRACEFUNC;
216 :
217 0 : IF_ASSERT_FAILED(item) {
218 0 : return;
219 : }
220 :
221 0 : beginInsertRows(QModelIndex(), index, index);
222 0 : m_mixerChannelList.insert(index, item);
223 0 : updateItemsPanelsOrder();
224 0 : endInsertRows();
225 :
226 0 : emit rowCountChanged();
227 0 : }
228 :
229 0 : void MixerPanelModel::removeItem(const TrackId trackId)
230 : {
231 0 : TRACEFUNC;
232 :
233 0 : int index = indexOf(trackId);
234 0 : if (index == INVALID_INDEX) {
235 0 : return;
236 : }
237 :
238 0 : beginRemoveRows(QModelIndex(), index, index);
239 :
240 0 : m_mixerChannelList.removeAt(index);
241 0 : updateItemsPanelsOrder();
242 :
243 0 : endRemoveRows();
244 :
245 0 : updateOutputResourceItemCount();
246 :
247 0 : emit rowCountChanged();
248 0 : }
249 :
250 0 : void MixerPanelModel::updateItemsPanelsOrder()
251 : {
252 0 : TRACEFUNC;
253 :
254 0 : for (int i = 0; i < m_mixerChannelList.size(); i++) {
255 0 : m_mixerChannelList[i]->setPanelOrder(m_navigationOrderStart + i);
256 : }
257 0 : }
258 :
259 0 : void MixerPanelModel::clear()
260 : {
261 0 : TRACEFUNC;
262 :
263 0 : m_masterChannelItem = nullptr;
264 0 : qDeleteAll(m_mixerChannelList);
265 0 : m_mixerChannelList.clear();
266 0 : }
267 :
268 0 : void MixerPanelModel::setupConnections()
269 : {
270 0 : audioSettings()->auxSoloMuteStateChanged().onReceive(
271 0 : this, [this](const aux_channel_idx_t index,
272 : notation::INotationSoloMuteState::SoloMuteState newSoloMuteState) {
273 0 : const IPlaybackController::AuxTrackIdMap& auxTrackIdMap = controller()->auxTrackIdMap();
274 0 : TrackId trackId = muse::value(auxTrackIdMap, index);
275 :
276 0 : if (MixerChannelItem* item = findChannelItem(trackId)) {
277 0 : item->loadSoloMuteState(newSoloMuteState);
278 : }
279 0 : });
280 :
281 0 : playback()->sourceParamsChanged().onReceive(this, [this](const TrackId trackId, const AudioSourceParams& params) {
282 0 : if (MixerChannelItem* item = findChannelItem(trackId)) {
283 0 : item->loadInputParams(params);
284 : }
285 0 : });
286 :
287 0 : playback()->fxChainParamsChanged().onReceive(this, [this](const TrackId trackId, const AudioFxChain& params) {
288 0 : if (MixerChannelItem* item = findChannelItem(trackId)) {
289 0 : AudioOutputParams outParams = audioSettings()->trackOutputParams(item->instrumentTrackId());
290 0 : outParams.fxChain = params;
291 0 : loadOutputParams(item, outParams);
292 0 : }
293 0 : });
294 :
295 0 : playback()->masterFxChainParamsChanged().onReceive(this, [this](const AudioFxChain& params) {
296 0 : if (m_masterChannelItem) {
297 0 : AudioOutputParams outParams = audioSettings()->masterAudioOutputParams();
298 0 : outParams.fxChain = params;
299 0 : loadOutputParams(m_masterChannelItem, outParams);
300 0 : }
301 0 : }, Asyncable::Mode::SetReplace);
302 :
303 0 : controller()->auxChannelNameChanged().onReceive(this, [this](aux_channel_idx_t index, const std::string& name) {
304 0 : for (MixerChannelItem* item : m_mixerChannelList) {
305 0 : const QMap<aux_channel_idx_t, AuxSendItem*>& items = item->auxSendItems();
306 0 : auto it = items.find(index);
307 :
308 0 : if (it != items.end()) {
309 0 : it.value()->setTitle(QString::fromStdString(name));
310 : }
311 : }
312 0 : });
313 :
314 0 : configuration()->isAuxChannelVisibleChanged().onReceive(this, [this](aux_channel_idx_t index, bool visible) {
315 0 : const auto& auxMap = controller()->auxTrackIdMap();
316 0 : TrackId trackId = muse::value(auxMap, index);
317 0 : if (visible) {
318 0 : int visibleAuxesOnRight = 0;
319 :
320 0 : for (const auto& aux : auxMap) {
321 0 : if (configuration()->isAuxChannelVisible(aux.first) && (aux.first > index)) {
322 0 : visibleAuxesOnRight++;
323 : }
324 : }
325 0 : addItem(buildAuxChannelItem(index, trackId), masterChannelIndex() - visibleAuxesOnRight);
326 : } else {
327 0 : removeItem(trackId);
328 : }
329 0 : });
330 :
331 0 : subscribeOnAutomationChanges();
332 0 : }
333 :
334 0 : void MixerPanelModel::subscribeOnAutomationChanges()
335 : {
336 0 : if (!currentProject()) {
337 0 : return;
338 : }
339 :
340 0 : AutomationDataConstPtr automation = currentProject()->masterNotation()->automation()->automationData();
341 0 : if (!automation) {
342 0 : return;
343 : }
344 :
345 0 : automation->changed().onReceive(this, [this](const AutomationChanges& changes) {
346 0 : if (changes.isFullReset) {
347 0 : for (MixerChannelItem* item : m_mixerChannelList) {
348 0 : item->updateHasAutomationFlags();
349 : }
350 0 : return;
351 : }
352 :
353 0 : InstrumentTrackIdSet affectedTrackIds;
354 0 : for (const AutomationCurveKey& key : changes.affectedKeys) {
355 0 : if (key.type != AutomationType::Volume && key.type != AutomationType::Pan) {
356 0 : continue;
357 : }
358 :
359 0 : if (const std::optional<InstrumentTrackId> trackId = key.trackId()) {
360 0 : affectedTrackIds.insert(*trackId);
361 0 : }
362 : }
363 :
364 0 : if (affectedTrackIds.empty()) {
365 0 : return;
366 : }
367 :
368 0 : for (MixerChannelItem* item : m_mixerChannelList) {
369 0 : if (muse::contains(affectedTrackIds, item->instrumentTrackId())) {
370 0 : item->updateHasAutomationFlags();
371 : }
372 : }
373 0 : });
374 0 : }
375 :
376 0 : int MixerPanelModel::resolveInsertIndex(const engraving::InstrumentTrackId& newInstrumentTrackId) const
377 : {
378 0 : const InstrumentTrackId& metronomeTrackId = notationPlayback()->metronomeTrackId();
379 0 : if (newInstrumentTrackId == metronomeTrackId) {
380 0 : return masterChannelIndex();
381 : }
382 :
383 : // Assumptions:
384 : // - the last channel is always the master channel
385 : // - metronome channel is placed to the immediate left of the master (or auxes if visible)
386 : // - the InstrumentTrackIds from the mixer channel items are always a correctly
387 : // sorted subset of the InstrumentTrackIds from NotationParts
388 0 : if (notationPlayback()->isChordSymbolsTrack(newInstrumentTrackId)) {
389 0 : int metronomeIdx = 0;
390 0 : for (const MixerChannelItem* channelItem : m_mixerChannelList) {
391 0 : const engraving::InstrumentTrackId& instrumentTrackId = channelItem->instrumentTrackId();
392 0 : if (instrumentTrackId.isValid() && instrumentTrackId != metronomeTrackId) {
393 0 : metronomeIdx++;
394 : }
395 : }
396 0 : return metronomeIdx;
397 : }
398 :
399 0 : int mixerChannelListIdx = 0;
400 :
401 0 : async::NotifyList<const Part*> partList = masterNotationParts()->partList();
402 0 : for (const Part* part : partList) {
403 0 : for (const InstrumentTrackId& instrumentTrackId : part->instrumentTrackIdList()) {
404 0 : if (instrumentTrackId == newInstrumentTrackId) {
405 0 : return mixerChannelListIdx;
406 : }
407 :
408 0 : const MixerChannelItem* mixerChannelItem = m_mixerChannelList[mixerChannelListIdx];
409 0 : MixerChannelItem::Type itemType = mixerChannelItem->type();
410 :
411 0 : if (itemType == MixerChannelItem::Type::Master) {
412 0 : return mixerChannelListIdx;
413 : }
414 :
415 0 : const InstrumentTrackId& itemInstrumentTrackId = mixerChannelItem->instrumentTrackId();
416 :
417 0 : if (itemInstrumentTrackId == metronomeTrackId) {
418 0 : return mixerChannelListIdx;
419 : }
420 :
421 0 : if (notationPlayback()->isChordSymbolsTrack(itemInstrumentTrackId)) {
422 0 : return mixerChannelListIdx;
423 : }
424 :
425 0 : if (itemInstrumentTrackId == instrumentTrackId) {
426 0 : if (instrumentTrackId == newInstrumentTrackId) {
427 0 : return INVALID_INDEX;
428 : }
429 :
430 0 : ++mixerChannelListIdx;
431 : }
432 0 : }
433 : }
434 :
435 0 : return INVALID_INDEX;
436 0 : }
437 :
438 0 : int MixerPanelModel::indexOf(const TrackId trackId) const
439 : {
440 0 : for (int i = 0; i < m_mixerChannelList.size(); ++i) {
441 0 : if (trackId == m_mixerChannelList[i]->trackId()) {
442 0 : return i;
443 : }
444 : }
445 :
446 0 : return INVALID_INDEX;
447 : }
448 :
449 0 : MixerChannelItem* MixerPanelModel::buildInstrumentChannelItem(const TrackId trackId,
450 : const engraving::InstrumentTrackId& instrumentTrackId,
451 : bool isPrimary)
452 : {
453 0 : MixerChannelItem::Type type = isPrimary ? MixerChannelItem::Type::PrimaryInstrument
454 : : MixerChannelItem::Type::SecondaryInstrument;
455 :
456 0 : const InstrumentTrackId& metronomeTrackId = notationPlayback()->metronomeTrackId();
457 0 : if (instrumentTrackId == metronomeTrackId) {
458 0 : type = MixerChannelItem::Type::Metronome;
459 : }
460 :
461 0 : MixerChannelItem* item = new MixerChannelItem(this, type, false /*outputOnly*/, trackId);
462 0 : item->setInstrumentTrackId(instrumentTrackId);
463 0 : item->setPanelSection(m_navigationSection);
464 0 : item->loadSoloMuteState(controller()->trackSoloMuteState(instrumentTrackId));
465 :
466 0 : playback()->params(trackId)
467 0 : .onResolve(this, [this, trackId, instrumentTrackId](const TrackParams& params) {
468 0 : if (MixerChannelItem* item = findChannelItem(trackId)) {
469 0 : item->loadInputParams(params.source);
470 :
471 0 : AudioOutputParams outParams = audioSettings()->trackOutputParams(instrumentTrackId);
472 0 : outParams.fxChain = params.fxChain;
473 0 : outParams.auxSends = params.auxSends;
474 0 : outParams.setControl(params.control);
475 0 : loadOutputParams(item, outParams);
476 0 : }
477 0 : })
478 0 : .onReject(this, [](int errCode, std::string text) {
479 0 : LOGE() << "unable to get track output parameters, error code: " << errCode
480 0 : << ", " << text;
481 0 : });
482 :
483 0 : playback()->trackName(trackId)
484 0 : .onResolve(this, [this, trackId](const RetVal<TrackName>& trackName) {
485 0 : if (trackName.ret) {
486 0 : if (MixerChannelItem* item = findChannelItem(trackId)) {
487 0 : item->setTitle(QString::fromStdString(trackName.val));
488 : }
489 : } else {
490 0 : LOGE() << "unable to get track name, error: " << trackName.ret.toString();
491 : }
492 0 : });
493 :
494 0 : playback()->signalChanges(trackId)
495 0 : .onResolve(this, [this, trackId](AudioSignalChanges signalChanges) {
496 0 : if (MixerChannelItem* item = findChannelItem(trackId)) {
497 0 : item->subscribeOnAudioSignalChanges(signalChanges);
498 : }
499 0 : })
500 0 : .onReject(this, [](int errCode, std::string text) {
501 0 : LOGE() << "unable to subscribe on audio signal changes from mixer channel, error code: " << errCode
502 0 : << ", " << text;
503 0 : });
504 :
505 0 : playback()->automatedControlParamsChanges(trackId)
506 0 : .onResolve(this, [this, trackId](AutomatedControlParamsChanges changes) {
507 0 : if (MixerChannelItem* item = findChannelItem(trackId)) {
508 0 : item->subscribeOnAutomatedControlParamsChanges(changes);
509 : }
510 0 : })
511 0 : .onReject(this, [](int errCode, std::string text) {
512 0 : LOGE() << "unable to subscribe on automated control params changes from mixer channel, error code: " << errCode
513 0 : << ", " << text;
514 0 : });
515 :
516 0 : connect(item, &MixerChannelItem::inputParamsChanged, this, [this, trackId](const AudioInputParams& params) {
517 0 : playback()->setSourceParams(trackId, params);
518 0 : });
519 :
520 0 : connect(item, &MixerChannelItem::controlParamsChanged, this, [this, trackId](const AudioOutputParams& params) {
521 0 : playback()->setControlParams(trackId, params.control());
522 0 : });
523 :
524 0 : connect(item, &MixerChannelItem::fxChainParamsChanged, this, [this, trackId](const AudioOutputParams& params) {
525 0 : playback()->setFxChainParams(trackId, params.fxChain);
526 0 : });
527 :
528 0 : connect(item, &MixerChannelItem::auxSendsParamsChanged, this, [this, trackId](const AudioOutputParams& params) {
529 0 : playback()->setAuxSendsParams(trackId, params.auxSends);
530 0 : });
531 :
532 0 : connect(item, &MixerChannelItem::auxSendItemListChanged, this, [this, item]() {
533 0 : const QMap<aux_channel_idx_t, AuxSendItem*>& auxSendItems = item->auxSendItems();
534 0 : for (auto it = auxSendItems.begin(); it != auxSendItems.end(); ++it) {
535 0 : it.value()->setTitle(QString::fromStdString(controller()->auxChannelName(it.key())));
536 : }
537 0 : });
538 :
539 0 : connect(item, &MixerChannelItem::soloMuteStateChanged, this,
540 0 : [this, instrumentTrackId](const notation::INotationSoloMuteState::SoloMuteState& state) {
541 0 : controller()->setTrackSoloMuteState(instrumentTrackId, state);
542 0 : });
543 :
544 0 : return item;
545 : }
546 :
547 0 : MixerChannelItem* MixerPanelModel::buildAuxChannelItem(aux_channel_idx_t index, const TrackId trackId)
548 : {
549 0 : MixerChannelItem* item = new MixerChannelItem(this, MixerChannelItem::Type::Aux, true /*outputOnly*/, trackId);
550 0 : item->setPanelSection(m_navigationSection);
551 0 : item->loadSoloMuteState(audioSettings()->auxSoloMuteState(index));
552 :
553 0 : playback()->trackName(trackId)
554 0 : .onResolve(this, [this, trackId](const RetVal<TrackName>& trackName) {
555 0 : if (trackName.ret) {
556 0 : if (MixerChannelItem* item = findChannelItem(trackId)) {
557 0 : item->setTitle(QString::fromStdString(trackName.val));
558 : }
559 : } else {
560 0 : LOGE() << "unable to get track name, error: " << trackName.ret.toString();
561 : }
562 0 : });
563 :
564 0 : AudioOutputParams outParams = audioSettings()->auxOutputParams(index);
565 0 : loadOutputParams(item, outParams);
566 :
567 0 : playback()->signalChanges(trackId)
568 0 : .onResolve(this, [this, trackId](AudioSignalChanges signalChanges) {
569 0 : if (MixerChannelItem* item = findChannelItem(trackId)) {
570 0 : item->subscribeOnAudioSignalChanges(signalChanges);
571 : }
572 0 : })
573 0 : .onReject(this, [](int errCode, std::string text) {
574 0 : LOGE() << "unable to subscribe on audio signal changes from mixer channel, error code: " << errCode
575 0 : << ", " << text;
576 0 : });
577 :
578 0 : connect(item, &MixerChannelItem::controlParamsChanged, this, [this, trackId](const AudioOutputParams& params) {
579 0 : playback()->setControlParams(trackId, params.control());
580 0 : });
581 0 : connect(item, &MixerChannelItem::fxChainParamsChanged, this, [this, trackId](const AudioOutputParams& params) {
582 0 : playback()->setFxChainParams(trackId, params.fxChain);
583 0 : });
584 0 : connect(item, &MixerChannelItem::auxSendsParamsChanged, this, [this, trackId](const AudioOutputParams& params) {
585 0 : playback()->setAuxSendsParams(trackId, params.auxSends);
586 0 : });
587 :
588 0 : connect(item, &MixerChannelItem::soloMuteStateChanged, this,
589 0 : [this, index](const notation::INotationSoloMuteState::SoloMuteState& state) {
590 0 : audioSettings()->setAuxSoloMuteState(index, state);
591 0 : });
592 :
593 0 : return item;
594 0 : }
595 :
596 0 : MixerChannelItem* MixerPanelModel::buildMasterChannelItem()
597 : {
598 0 : MixerChannelItem* item = new MixerChannelItem(this, MixerChannelItem::Type::Master, true /*outputOnly*/, MASTER_TRACK_ID);
599 0 : item->setPanelSection(m_navigationSection);
600 0 : item->setTitle(muse::qtrc("playback", "Master"));
601 :
602 0 : AudioOutputParams outParams = audioSettings()->masterAudioOutputParams();
603 0 : loadOutputParams(item, outParams);
604 :
605 0 : playback()->masterSignalChanges()
606 0 : .onResolve(this, [this, item](AudioSignalChanges signalChanges) {
607 0 : if (m_masterChannelItem && item == m_masterChannelItem) {
608 0 : item->subscribeOnAudioSignalChanges(signalChanges);
609 : }
610 0 : })
611 0 : .onReject(this, [](int errCode, std::string text) {
612 0 : LOGE() << "unable to subscribe on audio signal changes from master channel, error code: " << errCode
613 0 : << ", " << text;
614 0 : });
615 :
616 0 : connect(item, &MixerChannelItem::controlParamsChanged, this, [this](const AudioOutputParams& params) {
617 0 : playback()->setMasterControlParams(params.control());
618 0 : });
619 :
620 0 : connect(item, &MixerChannelItem::fxChainParamsChanged, this, [this](const AudioOutputParams& params) {
621 0 : playback()->setMasterFxChainParams(params.fxChain);
622 0 : });
623 :
624 0 : connect(item, &MixerChannelItem::auxSendsParamsChanged, this, [this](const AudioOutputParams& params) {
625 0 : playback()->setMasterAuxSendsParams(params.auxSends);
626 0 : });
627 :
628 0 : return item;
629 0 : }
630 :
631 0 : int MixerPanelModel::masterChannelIndex() const
632 : {
633 0 : return m_mixerChannelList.size() - 1;
634 : }
635 :
636 0 : MixerChannelItem* MixerPanelModel::findChannelItem(const TrackId& trackId) const
637 : {
638 0 : for (MixerChannelItem* item : m_mixerChannelList) {
639 0 : if (item->trackId() == trackId) {
640 0 : return item;
641 : }
642 : }
643 :
644 0 : return nullptr;
645 : }
646 :
647 0 : void MixerPanelModel::loadOutputParams(MixerChannelItem* item, const AudioOutputParams& params)
648 : {
649 0 : IF_ASSERT_FAILED(item) {
650 0 : return;
651 : }
652 :
653 0 : item->loadOutputParams(params);
654 0 : updateOutputResourceItemCount();
655 : }
656 :
657 0 : void MixerPanelModel::updateOutputResourceItemCount()
658 : {
659 0 : size_t maxFxCount = 0;
660 :
661 0 : for (const MixerChannelItem* item : m_mixerChannelList) {
662 0 : const AudioFxChain& chain = item->outputParams().fxChain;
663 0 : if (chain.empty()) {
664 0 : continue;
665 : }
666 :
667 0 : AudioFxChainOrder order = std::prev(chain.end())->first;
668 0 : maxFxCount = std::max(maxFxCount, static_cast<size_t>(order) + 1);
669 : }
670 :
671 0 : for (MixerChannelItem* item : m_mixerChannelList) {
672 0 : item->setOutputResourceItemCount(maxFxCount + 1 /* + 1 blank slot */);
673 : }
674 0 : }
675 :
676 0 : INotationProjectPtr MixerPanelModel::currentProject() const
677 : {
678 0 : return context()->project();
679 : }
680 :
681 0 : IProjectAudioSettingsPtr MixerPanelModel::audioSettings() const
682 : {
683 0 : return currentProject() ? currentProject()->audioSettings() : nullptr;
684 : }
685 :
686 0 : INotationPlaybackPtr MixerPanelModel::notationPlayback() const
687 : {
688 0 : return currentProject() ? currentProject()->masterNotation()->playback() : nullptr;
689 : }
690 :
691 0 : INotationPartsPtr MixerPanelModel::masterNotationParts() const
692 : {
693 0 : return currentProject() ? currentProject()->masterNotation()->parts() : nullptr;
694 : }
695 :
696 0 : muse::ui::NavigationSection* MixerPanelModel::navigationSection() const
697 : {
698 0 : return m_navigationSection;
699 : }
700 :
701 0 : void MixerPanelModel::setNavigationSection(muse::ui::NavigationSection* navigationSection)
702 : {
703 0 : if (m_navigationSection == navigationSection) {
704 0 : return;
705 : }
706 :
707 0 : m_navigationSection = navigationSection;
708 0 : emit navigationSectionChanged();
709 : }
710 :
711 0 : int MixerPanelModel::navigationOrderStart() const
712 : {
713 0 : return m_navigationOrderStart;
714 : }
715 :
716 0 : void MixerPanelModel::setNavigationOrderStart(int navigationOrderStart)
717 : {
718 0 : if (m_navigationOrderStart == navigationOrderStart) {
719 0 : return;
720 : }
721 :
722 0 : m_navigationOrderStart = navigationOrderStart;
723 0 : emit navigationOrderStartChanged();
724 :
725 0 : updateItemsPanelsOrder();
726 : }
|