LCOV - code coverage report
Current view: top level - src/playback/qml/MuseScore/Playback - mixerpanelmodel.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 0.0 % 426 0
Test Date: 2026-11-05 04:07:02

            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              : }
        

Generated by: LCOV version 2.0-1