LCOV - code coverage report
Current view: top level - src/playback/qml/MuseScore/Playback - mixerchannelitem.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 0.0 % 449 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) 2026 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 "mixerchannelitem.h"
      24              : 
      25              : #include "defer.h"
      26              : #include "translation.h"
      27              : #include "log.h"
      28              : 
      29              : #include "notation/imasternotation.h" // IWYU pragma: keep
      30              : #include "notation/inotationautomation.h"
      31              : #include "notation/inotationplayback.h"
      32              : 
      33              : #include "project/inotationproject.h"
      34              : 
      35              : #include "vst/vstcommands.h"
      36              : 
      37              : using namespace mu::playback;
      38              : using namespace muse;
      39              : using namespace muse::audio;
      40              : using namespace muse::vst;
      41              : using namespace mu::project;
      42              : 
      43              : static constexpr volume_dbfs_t MAX_DISPLAYED_DBFS = volume_dbfs_t::make(0.f);   // 100%
      44              : static constexpr volume_dbfs_t MIN_DISPLAYED_DBFS = volume_dbfs_t::make(-60.f); // 0%
      45              : 
      46              : static constexpr float BALANCE_SCALING_FACTOR = 100.f;
      47              : 
      48              : static constexpr int OUTPUT_RESOURCE_COUNT_LIMIT = 4;
      49              : 
      50              : static const std::string TRACK_ID_KEY("trackId");
      51              : static const std::string RESOURCE_ID_KEY("resourceId");
      52              : static const std::string CHAIN_ORDER_KEY("chainOrder");
      53              : 
      54            0 : MixerChannelItem::MixerChannelItem(QObject* parent, Type type, bool outputOnly, audio::TrackId trackId)
      55            0 :     : QObject(parent), muse::Contextable(muse::iocCtxForQmlObject(this)),
      56            0 :     m_type(type),
      57            0 :     m_trackId(trackId),
      58            0 :     m_outputOnly(outputOnly),
      59            0 :     m_leftChannelPressure(MIN_DISPLAYED_DBFS),
      60            0 :     m_rightChannelPressure(MIN_DISPLAYED_DBFS)
      61              : {
      62            0 :     if (!m_outputOnly) {
      63            0 :         m_inputResourceItem = buildInputResourceItem();
      64              :     }
      65              : 
      66            0 :     m_panel = new ui::NavigationPanel(this);
      67            0 :     m_panel->setDirection(ui::NavigationPanel::Vertical);
      68            0 :     m_panel->setName("MixerChannelPanel " + QString::number(m_trackId));
      69            0 :     m_panel->accessible()->setName(muse::qtrc("playback", "Mixer channel panel %1").arg(m_trackId));
      70            0 :     m_panel->componentComplete();
      71              : 
      72            0 :     connect(this, &MixerChannelItem::mutedChanged, this, [this]() {
      73            0 :         if (muted()) {
      74            0 :             resetAudioChannelsVolumePressure();
      75              :         }
      76            0 :     });
      77            0 : }
      78              : 
      79            0 : MixerChannelItem::~MixerChannelItem()
      80              : {
      81            0 :     m_audioSignalChanges.disconnect(this);
      82            0 : }
      83              : 
      84            0 : MixerChannelItem::Type MixerChannelItem::type() const
      85              : {
      86            0 :     return m_type;
      87              : }
      88              : 
      89            0 : TrackId MixerChannelItem::trackId() const
      90              : {
      91            0 :     return m_trackId;
      92              : }
      93              : 
      94            0 : const mu::engraving::InstrumentTrackId& MixerChannelItem::instrumentTrackId() const
      95              : {
      96            0 :     return m_instrumentTrackId;
      97              : }
      98              : 
      99            0 : void MixerChannelItem::setInstrumentTrackId(const mu::engraving::InstrumentTrackId& instrumentTrackId)
     100              : {
     101            0 :     m_instrumentTrackId = instrumentTrackId;
     102              : 
     103            0 :     updateHasAutomationFlags();
     104            0 : }
     105              : 
     106            0 : QString MixerChannelItem::title() const
     107              : {
     108            0 :     return m_title;
     109              : }
     110              : 
     111            0 : float MixerChannelItem::leftChannelPressure() const
     112              : {
     113            0 :     return m_leftChannelPressure;
     114              : }
     115              : 
     116            0 : float MixerChannelItem::rightChannelPressure() const
     117              : {
     118            0 :     return m_rightChannelPressure;
     119              : }
     120              : 
     121            0 : float MixerChannelItem::volumeLevel() const
     122              : {
     123            0 :     return m_volumeLevel;
     124              : }
     125              : 
     126            0 : float MixerChannelItem::volumeLevelMin() const
     127              : {
     128            0 :     return audio::VOLUME_DB_MIN.raw();
     129              : }
     130              : 
     131            0 : float MixerChannelItem::volumeLevelMax() const
     132              : {
     133            0 :     return audio::VOLUME_DB_MAX.raw();
     134              : }
     135              : 
     136            0 : int MixerChannelItem::balance() const
     137              : {
     138            0 :     return m_balance;
     139              : }
     140              : 
     141            0 : int MixerChannelItem::balanceMin() const
     142              : {
     143            0 :     return audio::BALANCE_MIN.raw() * BALANCE_SCALING_FACTOR;
     144              : }
     145              : 
     146            0 : int MixerChannelItem::balanceMax() const
     147              : {
     148            0 :     return audio::BALANCE_MAX.raw() * BALANCE_SCALING_FACTOR;
     149              : }
     150              : 
     151            0 : bool MixerChannelItem::hasVolumeAutomation() const
     152              : {
     153            0 :     return m_hasVolumeAutomation;
     154              : }
     155              : 
     156            0 : bool MixerChannelItem::hasBalanceAutomation() const
     157              : {
     158            0 :     return m_hasBalanceAutomation;
     159              : }
     160              : 
     161            0 : bool MixerChannelItem::solo() const
     162              : {
     163            0 :     return m_outParams.solo;
     164              : }
     165              : 
     166            0 : bool MixerChannelItem::muted() const
     167              : {
     168            0 :     return m_outParams.muted;
     169              : }
     170              : 
     171            0 : bool MixerChannelItem::forceMute() const
     172              : {
     173            0 :     return m_outParams.forceMute;
     174              : }
     175              : 
     176            0 : muse::ui::NavigationPanel* MixerChannelItem::panel() const
     177              : {
     178            0 :     return m_panel;
     179              : }
     180              : 
     181            0 : void MixerChannelItem::setPanelOrder(int panelOrder)
     182              : {
     183            0 :     m_panel->setOrder(panelOrder);
     184            0 : }
     185              : 
     186            0 : void MixerChannelItem::setPanelSection(muse::ui::INavigationSection* section)
     187              : {
     188            0 :     m_panel->setSection(section);
     189            0 : }
     190              : 
     191            0 : void MixerChannelItem::setOutputResourceItemCount(size_t count)
     192              : {
     193            0 :     IF_ASSERT_FAILED(count >= m_outParams.fxChain.size()) {
     194            0 :         return;
     195              :     }
     196              : 
     197            0 :     count = std::min(count, static_cast<size_t>(OUTPUT_RESOURCE_COUNT_LIMIT));
     198            0 :     size_t itemsSize = static_cast<size_t>(m_outputResourceItems.size());
     199              : 
     200            0 :     if (itemsSize == count) {
     201            0 :         return;
     202              :     }
     203              : 
     204            0 :     if (itemsSize < count) {
     205            0 :         addBlankSlots(count - itemsSize);
     206            0 :     } else if (itemsSize > count) {
     207            0 :         removeBlankSlotsFromEnd(itemsSize - count);
     208              :     }
     209              : }
     210              : 
     211            0 : void MixerChannelItem::addBlankSlots(size_t count)
     212              : {
     213            0 :     TRACEFUNC;
     214              : 
     215            0 :     if (count == 0) {
     216            0 :         return;
     217              :     }
     218              : 
     219            0 :     for (size_t i = 0; i < count; ++i) {
     220            0 :         AudioFxParams params;
     221            0 :         params.chainOrder = resolveNewBlankOutputResourceItemOrder();
     222            0 :         m_outputResourceItems.insert(params.chainOrder, buildOutputResourceItem(params));
     223            0 :     }
     224              : 
     225            0 :     emit outputResourceItemListChanged();
     226            0 : }
     227              : 
     228            0 : void MixerChannelItem::removeBlankSlotsFromEnd(size_t count)
     229              : {
     230            0 :     TRACEFUNC;
     231              : 
     232            0 :     bool itemsRemoved = false;
     233            0 :     DEFER {
     234            0 :         if (itemsRemoved) {
     235            0 :             emit outputResourceItemListChanged();
     236              :         }
     237            0 :     };
     238              : 
     239            0 :     for (size_t i = 0; i < count; ++i) {
     240            0 :         if (m_outputResourceItems.empty()) {
     241            0 :             return;
     242              :         }
     243              : 
     244            0 :         auto lastItemIt = std::prev(m_outputResourceItems.end());
     245            0 :         OutputResourceItem* item = lastItemIt.value();
     246              : 
     247            0 :         if (!item->isBlank()) {
     248            0 :             return;
     249              :         }
     250              : 
     251            0 :         m_outputResourceItems.erase(lastItemIt);
     252            0 :         closeEditor(item);
     253            0 :         item->disconnect();
     254            0 :         item->deleteLater();
     255            0 :         itemsRemoved = true;
     256              :     }
     257            0 : }
     258              : 
     259            0 : void MixerChannelItem::loadInputParams(const AudioInputParams& newParams)
     260              : {
     261            0 :     if (m_outputOnly) {
     262            0 :         return;
     263              :     }
     264              : 
     265            0 :     if (m_inputParams == newParams) {
     266            0 :         return;
     267              :     }
     268              : 
     269            0 :     m_inputParams = newParams;
     270            0 :     m_inputResourceItem->setParams(newParams);
     271              : }
     272              : 
     273            0 : void MixerChannelItem::loadOutputParams(const AudioOutputParams& newParams)
     274              : {
     275            0 :     if (!muse::RealIsEqual(m_outParams.volume, newParams.volume)) {
     276            0 :         m_outParams.volume = newParams.volume;
     277            0 :         if (!m_hasVolumeAutomation) {
     278            0 :             setDisplayedVolumeLevel(m_outParams.volume);
     279              :         }
     280              :     }
     281              : 
     282            0 :     if (!muse::RealIsEqual(m_outParams.balance, newParams.balance)) {
     283            0 :         m_outParams.balance = newParams.balance;
     284            0 :         if (!m_hasBalanceAutomation) {
     285            0 :             setDisplayedBalance(m_outParams.balance.raw() * BALANCE_SCALING_FACTOR);
     286              :         }
     287              :     }
     288              : 
     289            0 :     if (m_outParams.solo != newParams.solo) {
     290            0 :         m_outParams.solo = newParams.solo;
     291            0 :         emit soloChanged();
     292              :     }
     293              : 
     294            0 :     if (m_outParams.muted != newParams.muted) {
     295            0 :         m_outParams.muted = newParams.muted;
     296            0 :         emit mutedChanged();
     297              :     }
     298              : 
     299            0 :     if (m_outParams.forceMute != newParams.forceMute) {
     300            0 :         m_outParams.forceMute = newParams.forceMute;
     301            0 :         emit forceMuteChanged();
     302              :     }
     303              : 
     304            0 :     loadOutputResourceItems(newParams.fxChain);
     305            0 :     loadAuxSendItems(newParams.auxSends);
     306            0 : }
     307              : 
     308            0 : void MixerChannelItem::loadOutputResourceItems(const AudioFxChain& fxChain)
     309              : {
     310            0 :     m_outParams.fxChain = fxChain;
     311              : 
     312            0 :     m_outputResourceItemsLoading = true;
     313            0 :     DEFER {
     314            0 :         m_outputResourceItemsLoading = false;
     315            0 :     };
     316              : 
     317            0 :     QMap<AudioFxChainOrder, OutputResourceItem*> newItems = m_outputResourceItems;
     318              : 
     319            0 :     for (AudioFxChainOrder chainOrder : m_outputResourceItems.keys()) {
     320            0 :         OutputResourceItem* item = m_outputResourceItems.value(chainOrder);
     321              : 
     322            0 :         if (item->isBlank()) {
     323            0 :             continue;
     324              :         }
     325              : 
     326            0 :         if (fxChain.find(chainOrder) == fxChain.cend()) {
     327            0 :             item->disconnect();
     328            0 :             item->deleteLater();
     329            0 :             newItems.remove(chainOrder);
     330              :         }
     331            0 :     }
     332              : 
     333            0 :     for (const auto& pair : fxChain) {
     334            0 :         OutputResourceItem* item = newItems.value(pair.first, nullptr);
     335              : 
     336            0 :         if (item) {
     337            0 :             item->setParams(pair.second);
     338              :         } else {
     339            0 :             newItems.insert(pair.first, buildOutputResourceItem(pair.second));
     340              :         }
     341              :     }
     342              : 
     343            0 :     if (m_outputResourceItems != newItems) {
     344            0 :         m_outputResourceItems = std::move(newItems);
     345            0 :         emit outputResourceItemListChanged();
     346              :     }
     347            0 : }
     348              : 
     349            0 : void MixerChannelItem::loadAuxSendItems(const AuxSendsParams& auxSends)
     350              : {
     351            0 :     if (m_outParams.auxSends == auxSends) {
     352            0 :         return;
     353              :     }
     354              : 
     355            0 :     m_outParams.auxSends = auxSends;
     356              : 
     357            0 :     configuration()->isAuxSendVisibleChanged().onReceive(this, [this](aux_channel_idx_t index, bool visible) {
     358            0 :         if (visible) {
     359            0 :             IF_ASSERT_FAILED(index < m_outParams.auxSends.size()) {
     360            0 :                 return;
     361              :             };
     362              : 
     363            0 :             m_auxSendItems.insert(index, buildAuxSendItem(index, m_outParams.auxSends[index]));
     364              :         } else {
     365            0 :             m_auxSendItems.remove(index);
     366              :         }
     367              : 
     368            0 :         emit auxSendItemListChanged();
     369              :     }, async::Asyncable::Mode::SetReplace);
     370              : 
     371            0 :     if (m_auxSendItems.size() == static_cast<int>(auxSends.size())) {
     372            0 :         return;
     373              :     }
     374              : 
     375            0 :     QMap<aux_channel_idx_t, AuxSendItem*> newItems;
     376              : 
     377            0 :     for (aux_channel_idx_t i = 0; i < auxSends.size(); ++i) {
     378            0 :         if (!configuration()->isAuxSendVisible(i)) {
     379            0 :             continue;
     380              :         }
     381              : 
     382            0 :         auto it = m_auxSendItems.find(i);
     383              : 
     384            0 :         if (it != m_auxSendItems.end()) {
     385            0 :             newItems.insert(it.key(), it.value());
     386              :         } else {
     387            0 :             newItems.insert(i, buildAuxSendItem(i, auxSends[i]));
     388              :         }
     389              :     }
     390              : 
     391            0 :     if (m_auxSendItems != newItems) {
     392            0 :         m_auxSendItems = std::move(newItems);
     393            0 :         emit auxSendItemListChanged();
     394              :     }
     395            0 : }
     396              : 
     397            0 : void MixerChannelItem::loadSoloMuteState(const notation::INotationSoloMuteState::SoloMuteState& newState)
     398              : {
     399            0 :     if (m_outParams.muted != newState.mute) {
     400            0 :         m_outParams.muted = newState.mute;
     401            0 :         emit mutedChanged();
     402              :     }
     403              : 
     404            0 :     if (m_outParams.solo != newState.solo) {
     405            0 :         m_outParams.solo = newState.solo;
     406            0 :         emit soloChanged();
     407              :     }
     408            0 : }
     409              : 
     410            0 : void MixerChannelItem::subscribeOnAudioSignalChanges(AudioSignalChanges& audioSignalChanges)
     411              : {
     412            0 :     m_audioSignalChanges = audioSignalChanges;
     413              : 
     414            0 :     m_audioSignalChanges.onReceive(this, [this](const AudioSignalValuesMap& signalValues) {
     415              :         //!Note There should be no signal changes when the mixer channel is muted.
     416              :         //!     But some audio signal changes still might be "on the way" from the times when the mixer channel wasn't muted
     417              :         //!     So that we have to just ignore them
     418            0 :         if (muted()) {
     419            0 :             return;
     420              :         }
     421              : 
     422            0 :         for (const auto& pair : signalValues) {
     423            0 :             audioch_t audioChNum = pair.first;
     424            0 :             volume_dbfs_t newPressure = pair.second.pressure;
     425              : 
     426            0 :             if (newPressure < MIN_DISPLAYED_DBFS) {
     427            0 :                 setAudioChannelVolumePressure(audioChNum, MIN_DISPLAYED_DBFS);
     428            0 :             } else if (newPressure > MAX_DISPLAYED_DBFS) {
     429            0 :                 setAudioChannelVolumePressure(audioChNum, MAX_DISPLAYED_DBFS);
     430              :             } else {
     431            0 :                 setAudioChannelVolumePressure(audioChNum, newPressure);
     432              :             }
     433              :         }
     434              :     });
     435            0 : }
     436              : 
     437            0 : void MixerChannelItem::subscribeOnAutomatedControlParamsChanges(AutomatedControlParamsChanges& changes)
     438              : {
     439            0 :     m_automatedControlParamsChanges = changes;
     440              : 
     441            0 :     m_automatedControlParamsChanges.onReceive(this, [this](const AutomatedControlParams& params) {
     442            0 :         if (m_hasVolumeAutomation) {
     443            0 :             setDisplayedVolumeLevel(params.volume.raw());
     444              :         }
     445              : 
     446            0 :         if (m_hasBalanceAutomation) {
     447            0 :             setDisplayedBalance(params.balance.raw() * BALANCE_SCALING_FACTOR);
     448              :         }
     449            0 :     });
     450            0 : }
     451              : 
     452            0 : void MixerChannelItem::setTitle(QString title)
     453              : {
     454            0 :     if (m_title == title) {
     455            0 :         return;
     456              :     }
     457              : 
     458            0 :     m_title = title;
     459            0 :     emit titleChanged(m_title);
     460              : }
     461              : 
     462            0 : void MixerChannelItem::setLeftChannelPressure(float leftChannelPressure)
     463              : {
     464            0 :     if (qFuzzyCompare(m_leftChannelPressure, leftChannelPressure)) {
     465            0 :         return;
     466              :     }
     467              : 
     468            0 :     m_leftChannelPressure = leftChannelPressure;
     469            0 :     emit leftChannelPressureChanged(m_leftChannelPressure);
     470              : }
     471              : 
     472            0 : void MixerChannelItem::setRightChannelPressure(float rightChannelPressure)
     473              : {
     474            0 :     if (qFuzzyCompare(m_rightChannelPressure, rightChannelPressure)) {
     475            0 :         return;
     476              :     }
     477              : 
     478            0 :     m_rightChannelPressure = rightChannelPressure;
     479            0 :     emit rightChannelPressureChanged(m_rightChannelPressure);
     480              : }
     481              : 
     482            0 : void MixerChannelItem::setVolumeLevel(float volumeLevel)
     483              : {
     484            0 :     if (qFuzzyCompare(m_outParams.volume, volumeLevel)) {
     485            0 :         return;
     486              :     }
     487              : 
     488            0 :     m_outParams.volume = volumeLevel;
     489            0 :     setDisplayedVolumeLevel(volumeLevel);
     490            0 :     emit controlParamsChanged(m_outParams);
     491              : }
     492              : 
     493            0 : void MixerChannelItem::setBalance(int balance)
     494              : {
     495            0 :     if (m_outParams.balance.raw() * BALANCE_SCALING_FACTOR == balance) {
     496            0 :         return;
     497              :     }
     498              : 
     499            0 :     m_outParams.balance = balance / BALANCE_SCALING_FACTOR;
     500            0 :     setDisplayedBalance(balance);
     501            0 :     emit controlParamsChanged(m_outParams);
     502              : }
     503              : 
     504            0 : void MixerChannelItem::setSolo(bool solo)
     505              : {
     506            0 :     if (m_outParams.solo == solo) {
     507            0 :         return;
     508              :     }
     509              : 
     510            0 :     m_outParams.solo = solo;
     511              : 
     512            0 :     notation::INotationSoloMuteState::SoloMuteState soloMuteState;
     513            0 :     soloMuteState.mute = m_outParams.muted;
     514            0 :     soloMuteState.solo = m_outParams.solo;
     515              : 
     516            0 :     emit soloMuteStateChanged(soloMuteState);
     517            0 :     emit soloChanged();
     518              : 
     519            0 :     if (solo && m_outParams.muted) {
     520            0 :         setMuted(false);
     521              :     }
     522              : }
     523              : 
     524            0 : void MixerChannelItem::setMuted(bool mute)
     525              : {
     526            0 :     if (m_outParams.muted == mute) {
     527            0 :         return;
     528              :     }
     529              : 
     530            0 :     m_outParams.muted = mute;
     531              : 
     532            0 :     notation::INotationSoloMuteState::SoloMuteState soloMuteState;
     533            0 :     soloMuteState.mute = m_outParams.muted;
     534            0 :     soloMuteState.solo = m_outParams.solo;
     535              : 
     536            0 :     emit soloMuteStateChanged(soloMuteState);
     537            0 :     emit mutedChanged();
     538              : 
     539            0 :     if (mute && m_outParams.solo) {
     540            0 :         setSolo(false);
     541              :     }
     542              : }
     543              : 
     544            0 : mu::notation::INotationPlaybackPtr MixerChannelItem::notationPlayback() const
     545              : {
     546            0 :     project::INotationProjectPtr project = context()->project();
     547            0 :     return project ? project->masterNotation()->playback() : nullptr;
     548            0 : }
     549              : 
     550            0 : void MixerChannelItem::updateHasAutomationFlags()
     551              : {
     552            0 :     bool hasVolumeAutomation = false;
     553            0 :     bool hasBalanceAutomation = false;
     554              : 
     555            0 :     INotationProjectPtr project = context()->project();
     556              :     const notation::AutomationDataConstPtr automationData
     557            0 :         = (project && m_instrumentTrackId.isValid()) ? project->masterNotation()->automation()->automationData() : nullptr;
     558              : 
     559            0 :     if (automationData) {
     560            0 :         hasVolumeAutomation = !automationData->curve(
     561            0 :             notation::AutomationCurveKey::instrument(notation::AutomationType::Volume, m_instrumentTrackId)).empty();
     562            0 :         hasBalanceAutomation = !automationData->curve(
     563            0 :             notation::AutomationCurveKey::instrument(notation::AutomationType::Pan, m_instrumentTrackId)).empty();
     564              :     }
     565              : 
     566            0 :     if (m_hasVolumeAutomation != hasVolumeAutomation) {
     567            0 :         m_hasVolumeAutomation = hasVolumeAutomation;
     568            0 :         emit hasVolumeAutomationChanged();
     569            0 :         if (!m_hasVolumeAutomation) {
     570            0 :             setDisplayedVolumeLevel(m_outParams.volume);
     571              :         }
     572              :     }
     573              : 
     574            0 :     if (m_hasBalanceAutomation != hasBalanceAutomation) {
     575            0 :         m_hasBalanceAutomation = hasBalanceAutomation;
     576            0 :         emit hasBalanceAutomationChanged();
     577            0 :         if (!m_hasBalanceAutomation) {
     578            0 :             setDisplayedBalance(m_outParams.balance.raw() * BALANCE_SCALING_FACTOR);
     579              :         }
     580              :     }
     581            0 : }
     582              : 
     583            0 : void MixerChannelItem::setDisplayedVolumeLevel(float volumeLevel)
     584              : {
     585            0 :     if (muse::RealIsEqual(m_volumeLevel, volumeLevel)) {
     586            0 :         return;
     587              :     }
     588              : 
     589            0 :     m_volumeLevel = volumeLevel;
     590            0 :     emit volumeLevelChanged(m_volumeLevel);
     591              : }
     592              : 
     593            0 : void MixerChannelItem::setDisplayedBalance(int balance)
     594              : {
     595            0 :     if (m_balance == balance) {
     596            0 :         return;
     597              :     }
     598              : 
     599            0 :     m_balance = balance;
     600            0 :     emit balanceChanged(m_balance);
     601              : }
     602              : 
     603            0 : void MixerChannelItem::setAudioChannelVolumePressure(const audio::audioch_t chNum, const float newValue)
     604              : {
     605            0 :     if (chNum == 0) {
     606            0 :         setLeftChannelPressure(newValue);
     607              :     } else {
     608            0 :         setRightChannelPressure(newValue);
     609              :     }
     610            0 : }
     611              : 
     612            0 : void MixerChannelItem::resetAudioChannelsVolumePressure()
     613              : {
     614            0 :     setLeftChannelPressure(MIN_DISPLAYED_DBFS);
     615            0 :     setRightChannelPressure(MIN_DISPLAYED_DBFS);
     616            0 : }
     617              : 
     618            0 : InputResourceItem* MixerChannelItem::buildInputResourceItem()
     619              : {
     620            0 :     InputResourceItem* newItem = new InputResourceItem(this);
     621              : 
     622            0 :     connect(newItem, &InputResourceItem::inputParamsChangeRequested, this, [this, newItem](const AudioResourceMeta& newMeta) {
     623            0 :         if (askAboutChangingSound()) {
     624            0 :             newItem->setParamsRecourceMeta(newMeta);
     625              :         }
     626            0 :     });
     627              : 
     628            0 :     connect(newItem, &InputResourceItem::inputParamsChanged, this, [this, newItem]() {
     629            0 :         bool audioSourceChanged = m_inputParams.type() != newItem->params().type();
     630              : 
     631            0 :         m_inputParams = newItem->params();
     632            0 :         emit inputParamsChanged(m_inputParams);
     633              : 
     634            0 :         if (!audioSourceChanged) {
     635            0 :             return;
     636              :         }
     637              : 
     638            0 :         bool auxParamsChanged = false;
     639            0 :         for (aux_channel_idx_t idx = 0; idx < static_cast<size_t>(m_outParams.auxSends.size()); ++idx) {
     640            0 :             const muse::String& soundId = m_inputParams.resourceMeta.attributeVal(PLAYBACK_SETUP_DATA_ATTRIBUTE);
     641            0 :             gain_t newAudioSignalAmount = configuration()->defaultAuxSendValue(idx, m_inputParams.type(), soundId);
     642              : 
     643            0 :             auto it = m_auxSendItems.find(idx);
     644            0 :             if (it == m_auxSendItems.end()) {
     645            0 :                 if (!muse::RealIsEqual(m_outParams.auxSends.at(idx).signalAmount, newAudioSignalAmount)) {
     646            0 :                     m_outParams.auxSends.at(idx).signalAmount = newAudioSignalAmount;
     647            0 :                     auxParamsChanged = true;
     648              :                 }
     649              :             } else {
     650            0 :                 it.value()->setAudioSignalPercentage(newAudioSignalAmount * 100.f);
     651              :             }
     652              :         }
     653              : 
     654            0 :         if (auxParamsChanged) {
     655            0 :             emit auxSendsParamsChanged(m_outParams);
     656              :         }
     657              :     });
     658              : 
     659            0 :     connect(newItem, &InputResourceItem::isBlankChanged, this, &MixerChannelItem::inputResourceItemChanged);
     660              : 
     661            0 :     connect(newItem, &InputResourceItem::nativeEditorViewLaunchRequested, this, [this, newItem]() {
     662            0 :         if (newItem->params().type() != AudioSourceType::Vsti) {
     663            0 :             return;
     664              :         }
     665              : 
     666            0 :         rcommand::CommandQuery aq(VST_OPEN_INSTRUMENT_EDITOR_COMMAND);
     667            0 :         aq.addParam(TRACK_ID_KEY, Val(m_trackId));
     668            0 :         aq.addParam(RESOURCE_ID_KEY, Val(newItem->params().resourceMeta.id));
     669              : 
     670            0 :         openEditor(newItem, aq);
     671            0 :     });
     672              : 
     673            0 :     connect(newItem, &InputResourceItem::nativeEditorViewCloseRequested, this, [this, newItem]() {
     674            0 :         closeEditor(newItem);
     675            0 :     });
     676              : 
     677            0 :     return newItem;
     678              : }
     679              : 
     680            0 : OutputResourceItem* MixerChannelItem::buildOutputResourceItem(const audio::AudioFxParams& fxParams)
     681              : {
     682            0 :     OutputResourceItem* newItem = new OutputResourceItem(this, fxParams);
     683              : 
     684            0 :     connect(newItem, &OutputResourceItem::fxParamsChanged, this, [this]() {
     685            0 :         if (m_outputResourceItemsLoading) {
     686            0 :             return;
     687              :         }
     688              : 
     689            0 :         m_outParams.fxChain.clear();
     690              : 
     691            0 :         for (const OutputResourceItem* item : std::as_const(m_outputResourceItems)) {
     692            0 :             m_outParams.fxChain.insert({ item->params().chainOrder, item->params() });
     693              :         }
     694              : 
     695            0 :         emit fxChainParamsChanged(m_outParams);
     696              :     });
     697              : 
     698            0 :     connect(newItem, &OutputResourceItem::nativeEditorViewLaunchRequested, this, [this, newItem]() {
     699            0 :         if (newItem->params().type() != AudioFxType::VstFx) {
     700            0 :             return;
     701              :         }
     702              : 
     703            0 :         rcommand::CommandQuery aq(VST_OPEN_FX_EDITOR_COMMAND);
     704              : 
     705            0 :         aq.addParam(TRACK_ID_KEY, Val(m_trackId));
     706            0 :         aq.addParam(RESOURCE_ID_KEY, Val(newItem->params().resourceMeta.id));
     707            0 :         aq.addParam(CHAIN_ORDER_KEY, Val(newItem->params().chainOrder));
     708              : 
     709            0 :         openEditor(newItem, aq);
     710            0 :     });
     711              : 
     712            0 :     connect(newItem, &OutputResourceItem::nativeEditorViewCloseRequested, this, [this, newItem]() {
     713            0 :         closeEditor(newItem);
     714            0 :     });
     715              : 
     716            0 :     return newItem;
     717              : }
     718              : 
     719            0 : AuxSendItem* MixerChannelItem::buildAuxSendItem(aux_channel_idx_t index, const AuxSendParams& params)
     720              : {
     721            0 :     AuxSendItem* newItem = new AuxSendItem(this);
     722            0 :     newItem->blockSignals(true);
     723            0 :     newItem->setIsActive(params.active);
     724            0 :     newItem->setAudioSignalPercentage(params.signalAmount * 100.f);
     725            0 :     newItem->setTitle(muse::qtrc("playback", "Aux %1").arg(index + 1));
     726            0 :     newItem->blockSignals(false);
     727              : 
     728            0 :     connect(newItem, &AuxSendItem::isActiveChanged, this, [this, index](bool active) {
     729            0 :         IF_ASSERT_FAILED(index < m_outParams.auxSends.size()) {
     730            0 :             return;
     731              :         }
     732              : 
     733            0 :         m_outParams.auxSends[index].active = active;
     734            0 :         emit auxSendsParamsChanged(m_outParams);
     735              :     });
     736              : 
     737            0 :     connect(newItem, &AuxSendItem::audioSignalPercentageChanged, this, [this, index](int percentage) {
     738            0 :         IF_ASSERT_FAILED(index < m_outParams.auxSends.size()) {
     739            0 :             return;
     740              :         }
     741              : 
     742            0 :         m_outParams.auxSends[index].signalAmount = static_cast<float>(percentage) / 100.f;
     743            0 :         emit auxSendsParamsChanged(m_outParams);
     744              :     });
     745              : 
     746            0 :     return newItem;
     747              : }
     748              : 
     749            0 : void MixerChannelItem::openEditor(AbstractAudioResourceItem* item, const rcommand::CommandQuery& command)
     750              : {
     751            0 :     if (item->editorCommand() != command) {
     752            0 :         if (item->editorCommand().isValid()) {
     753              :             // make and send close
     754            0 :             rcommand::CommandQuery closeCommand = item->editorCommand();
     755            0 :             closeCommand.addParam("operation", Val("close"));
     756            0 :             closeCommand.addParam("sync", Val(true));
     757            0 :             commandDispatcher()->dispatch(closeCommand);
     758            0 :         }
     759              :         // set new action
     760            0 :         item->setEditorCommand(command);
     761              :     }
     762              : 
     763            0 :     commandDispatcher()->dispatch(command);
     764            0 : }
     765              : 
     766            0 : void MixerChannelItem::closeEditor(AbstractAudioResourceItem* item)
     767              : {
     768              :     // make and send close
     769            0 :     rcommand::CommandQuery closeCommand = item->editorCommand();
     770            0 :     closeCommand.addParam("operation", Val("close"));
     771            0 :     closeCommand.addParam("sync", Val(true));
     772            0 :     commandDispatcher()->dispatch(closeCommand);
     773              : 
     774            0 :     item->setEditorCommand({});
     775            0 : }
     776              : 
     777            0 : bool MixerChannelItem::askAboutChangingSound()
     778              : {
     779            0 :     if (!configuration()->needToShowResetSoundFlagsWhenChangeSoundWarning()) {
     780            0 :         return true;
     781              :     }
     782              : 
     783            0 :     if (!notationPlayback()->hasSoundFlags({ m_instrumentTrackId })) {
     784            0 :         return true;
     785              :     }
     786              : 
     787            0 :     int changeBtn = int(IInteractive::Button::Apply);
     788            0 :     IInteractive::Options options = IInteractive::Option::WithIcon | IInteractive::Option::WithDontShowAgainCheckBox;
     789              :     IInteractive::ButtonDatas buttons = {
     790            0 :         interactive()->buttonData(IInteractive::Button::Cancel),
     791            0 :         IInteractive::ButtonData(changeBtn, muse::trc("playback", "Change sound"), true /*accent*/)
     792            0 :     };
     793              : 
     794            0 :     IInteractive::Result result = interactive()->warningSync(muse::trc("playback", "Are you sure you want to change this sound?"),
     795            0 :                                                              muse::trc("playback",
     796              :                                                                        "Sound flags on this instrument may be reset, but staff text will remain. This action can’t be undone."),
     797              :                                                              buttons, changeBtn, options);
     798              : 
     799            0 :     if (result.button() == changeBtn) {
     800            0 :         if (!result.showAgain()) {
     801            0 :             configuration()->setNeedToShowResetSoundFlagsWhenChangeSoundWarning(false);
     802              :         }
     803              : 
     804            0 :         return true;
     805              :     } else {
     806            0 :         return false;
     807              :     }
     808            0 : }
     809              : 
     810            0 : AudioFxChainOrder MixerChannelItem::resolveNewBlankOutputResourceItemOrder() const
     811              : {
     812            0 :     if (m_outputResourceItems.empty()) {
     813            0 :         return 0;
     814              :     }
     815              : 
     816            0 :     AudioFxChainOrder lastChainOrder = m_outputResourceItems.lastKey();
     817            0 :     if (lastChainOrder < OUTPUT_RESOURCE_COUNT_LIMIT - 1) {
     818            0 :         return lastChainOrder + 1;
     819              :     }
     820              : 
     821            0 :     for (AudioFxChainOrder order = 0; order < OUTPUT_RESOURCE_COUNT_LIMIT; ++order) {
     822            0 :         if (!m_outputResourceItems.contains(order)) {
     823            0 :             return order;
     824              :         }
     825              :     }
     826              : 
     827            0 :     return OUTPUT_RESOURCE_COUNT_LIMIT - 1;
     828              : }
     829              : 
     830            0 : bool MixerChannelItem::outputOnly() const
     831              : {
     832            0 :     return m_outputOnly;
     833              : }
     834              : 
     835            0 : const AudioInputParams& MixerChannelItem::inputParams() const
     836              : {
     837            0 :     return m_inputParams;
     838              : }
     839              : 
     840            0 : const AudioOutputParams& MixerChannelItem::outputParams() const
     841              : {
     842            0 :     return m_outParams;
     843              : }
     844              : 
     845            0 : InputResourceItem* MixerChannelItem::inputResourceItem() const
     846              : {
     847            0 :     return m_inputResourceItem;
     848              : }
     849              : 
     850            0 : QList<OutputResourceItem*> MixerChannelItem::outputResourceItemList() const
     851              : {
     852            0 :     return m_outputResourceItems.values();
     853              : }
     854              : 
     855            0 : QList<AuxSendItem*> MixerChannelItem::auxSendItemList() const
     856              : {
     857            0 :     return m_auxSendItems.values();
     858              : }
     859              : 
     860            0 : const QMap<aux_channel_idx_t, AuxSendItem*>& MixerChannelItem::auxSendItems() const
     861              : {
     862            0 :     return m_auxSendItems;
     863              : }
        

Generated by: LCOV version 2.0-1