LCOV - code coverage report
Current view: top level - muse/framework/audio/engine/internal/nodes - audionode.h Coverage Total Hit
Test: filtered_coverage.info Lines: 0.0 % 101 0
Test Date: 2026-11-05 04:07:02

            Line data    Source code
       1              : /*
       2              :  * SPDX-License-Identifier: GPL-3.0-only
       3              :  * MuseScore-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              : #pragma once
      24              : 
      25              : #include <memory>
      26              : #include <string>
      27              : #include <sstream>
      28              : 
      29              : #include "audio/common/audiotypes.h"
      30              : 
      31              : #include "log.h"
      32              : 
      33              : // #define AUDIONODE_LOGGING_ENABLED
      34              : 
      35              : #ifdef AUDIONODE_LOGGING_ENABLED
      36              : #define ANODE_LOG(name) LOGDA() << "[" << name << "] "
      37              : #else
      38              : #define ANODE_LOG(name) LOGN()
      39              : #endif
      40              : 
      41              : //! NOTE Nodes are in the engine de facto namespace,
      42              : // but the engine is not specified here only
      43              : // so that the class names are shorter when
      44              : // debugging in the call stack (the full names are specified there)
      45              : 
      46              : namespace muse::audio {
      47              : template<typename T>
      48              : class AudioNode;
      49              : 
      50              : class IAudioNode : public std::enable_shared_from_this<IAudioNode>
      51              : {
      52              : public:
      53            0 :     virtual ~IAudioNode() = default;
      54              : 
      55              :     virtual void setName(const std::string& name) = 0;
      56              :     virtual const std::string& name() const = 0;
      57              : 
      58              :     virtual void setOutputSpec(const OutputSpec& spec) = 0;
      59              :     virtual const OutputSpec& outputSpec() const = 0;
      60              :     virtual void setMode(const ProcessMode mode) = 0;
      61              :     virtual ProcessMode mode() const = 0;
      62              : 
      63              :     // turns off this node and all connected ones (like mute)
      64              :     virtual void setEnabled(bool enabled) = 0;
      65              :     virtual bool enabled() const = 0;
      66              : 
      67              :     // turns off only this node (like bypass)
      68              :     virtual void setBypassed(bool bypassed) = 0;
      69              :     virtual bool bypassed() const = 0;
      70              : 
      71              :     virtual IAudioNode* connect(std::shared_ptr<IAudioNode> other) = 0;
      72              :     virtual IAudioNode* disconnect(std::shared_ptr<IAudioNode> other) = 0;
      73              :     virtual void disconnectAll() = 0;
      74              : 
      75              :     virtual void process(float* buffer, samples_t samplesPerChannel) = 0;
      76              : 
      77              :     virtual std::string dump() const = 0;
      78              : 
      79              : protected:
      80              : 
      81              :     template<typename T>
      82              :     friend class AudioNode;
      83              : 
      84              :     virtual void onOutputSpecChanged(const OutputSpec& spec) = 0;
      85              :     virtual void onModeChanged(const ProcessMode mode) = 0;
      86              :     virtual void onEnabledChanged(bool enabled) = 0;
      87              :     virtual void onBypassedChanged(bool bypassed) = 0;
      88              : 
      89              :     virtual bool doAddNode(std::shared_ptr<IAudioNode> other) = 0;
      90              :     virtual bool doRemoveNode(std::shared_ptr<IAudioNode> other) = 0;
      91              : 
      92              :     virtual void doSelfProcess(float* buffer, samples_t samplesPerChannel) = 0;
      93              : };
      94              : 
      95              : using IAudioNodePtr = std::shared_ptr<IAudioNode>;
      96              : 
      97              : //! NOTE A template base node class.
      98              : // The tag is primarily used to simplify debugging
      99              : // (for example, to make it easier to understand the call stack).
     100              : template<typename T>
     101              : class AudioNode : public IAudioNode
     102              : {
     103              : public:
     104              : 
     105              :     void setName(const std::string& name) override;
     106              :     const std::string& name() const override;
     107              : 
     108              :     void setOutputSpec(const OutputSpec& spec) final override;
     109              :     const OutputSpec& outputSpec() const final override;
     110              :     void setMode(const ProcessMode mode) final override;
     111              :     ProcessMode mode() const final override;
     112              : 
     113              :     // turns off this node and all connected ones (like mute)
     114              :     void setEnabled(bool enabled) final override;
     115              :     bool enabled() const final override;
     116              : 
     117              :     // turns off only this node (like bypass)
     118              :     void setBypassed(bool bypassed) final override;
     119              :     bool bypassed() const final override;
     120              : 
     121              :     IAudioNode* connect(std::shared_ptr<IAudioNode> other) final override;
     122              :     IAudioNode* disconnect(std::shared_ptr<IAudioNode> other) final override;
     123              :     void disconnectAll() final override;
     124              : 
     125              :     //! NOTE This method needs to be overridden directly
     126              :     // to reduce the stack count for easier debugging.
     127              :     virtual void process(float* buffer, samples_t samplesPerChannel) override;
     128              : 
     129              :     std::string dump() const override;
     130              : 
     131              : protected:
     132              : 
     133              :     AudioNode();
     134              : 
     135              :     virtual void onOutputSpecChanged(const OutputSpec& spec) override;
     136              :     virtual void onModeChanged(const ProcessMode mode) override;
     137              :     virtual void onEnabledChanged(bool enabled) override;
     138              :     virtual void onBypassedChanged(bool bypassed) override;
     139              : 
     140              :     virtual bool doAddNode(std::shared_ptr<IAudioNode> other) override;
     141              :     virtual bool doRemoveNode(std::shared_ptr<IAudioNode> other) override;
     142              : 
     143              :     // virtual void doSelfProcess(float* buffer, samples_t samplesPerChannel) = 0;
     144              : 
     145              :     std::string m_name;
     146              :     OutputSpec m_outputSpec;
     147              :     ProcessMode m_mode = ProcessMode::Undefined;
     148              :     bool m_enabled = true;
     149              :     bool m_bypassed = false;
     150              : 
     151              :     std::vector<std::shared_ptr<IAudioNode> > m_connectedTo;
     152              :     std::shared_ptr<IAudioNode> m_input = nullptr;
     153              : };
     154              : 
     155              : template<typename T>
     156            0 : AudioNode<T>::AudioNode()
     157            0 :     : m_name(T::name)
     158              : {
     159            0 : }
     160              : 
     161              : template<typename T>
     162            0 : void AudioNode<T>::setName(const std::string& name)
     163              : {
     164            0 :     m_name = name;
     165            0 : }
     166              : 
     167              : template<typename T>
     168            0 : const std::string& AudioNode<T>::name() const
     169              : {
     170            0 :     return m_name;
     171              : }
     172              : 
     173              : template<typename T>
     174            0 : void AudioNode<T>::setOutputSpec(const OutputSpec& spec)
     175              : {
     176            0 :     if (m_outputSpec == spec) {
     177            0 :         return;
     178              :     }
     179            0 :     m_outputSpec = spec;
     180              :     ANODE_LOG(name()) << "onOutputSpecChanged: " << spec.dump();
     181            0 :     onOutputSpecChanged(spec);
     182            0 :     if (m_input) {
     183            0 :         m_input->setOutputSpec(spec);
     184              :     }
     185              : }
     186              : 
     187              : template<typename T>
     188            0 : const OutputSpec& AudioNode<T>::outputSpec() const
     189              : {
     190            0 :     return m_outputSpec;
     191              : }
     192              : 
     193              : template<typename T>
     194            0 : void AudioNode<T>::onOutputSpecChanged(const OutputSpec& /*spec*/)
     195              : {
     196            0 : }
     197              : 
     198              : template<typename T>
     199            0 : void AudioNode<T>::setMode(const ProcessMode mode)
     200              : {
     201            0 :     if (m_mode == mode) {
     202            0 :         return;
     203              :     }
     204            0 :     m_mode = mode;
     205              :     ANODE_LOG(name()) << "onModeChanged: " << to_string(mode);
     206            0 :     onModeChanged(mode);
     207            0 :     if (m_input) {
     208            0 :         m_input->setMode(mode);
     209              :     }
     210              : }
     211              : 
     212              : template<typename T>
     213            0 : ProcessMode AudioNode<T>::mode() const
     214              : {
     215            0 :     return m_mode;
     216              : }
     217              : 
     218              : template<typename T>
     219            0 : void AudioNode<T>::onModeChanged(const ProcessMode /*mode*/)
     220              : {
     221            0 : }
     222              : 
     223              : template<typename T>
     224            0 : void AudioNode<T>::setEnabled(bool enabled)
     225              : {
     226            0 :     if (m_enabled == enabled) {
     227            0 :         return;
     228              :     }
     229            0 :     m_enabled = enabled;
     230              :     ANODE_LOG(name()) << "onEnabledChanged: " << m_enabled;
     231            0 :     onEnabledChanged(enabled);
     232            0 :     if (m_input) {
     233            0 :         m_input->setEnabled(enabled);
     234              :     }
     235              : }
     236              : 
     237              : template<typename T>
     238            0 : bool AudioNode<T>::enabled() const
     239              : {
     240            0 :     return m_enabled;
     241              : }
     242              : 
     243              : template<typename T>
     244            0 : void AudioNode<T>::onEnabledChanged(bool /*enabled*/)
     245              : {
     246            0 : }
     247              : 
     248              : template<typename T>
     249            0 : void AudioNode<T>::setBypassed(bool bypassed)
     250              : {
     251            0 :     if (m_bypassed == bypassed) {
     252            0 :         return;
     253              :     }
     254            0 :     m_bypassed = bypassed;
     255              :     ANODE_LOG(name()) << "onBypassedChanged: " << m_bypassed;
     256            0 :     onBypassedChanged(bypassed);
     257              : }
     258              : 
     259              : template<typename T>
     260            0 : bool AudioNode<T>::bypassed() const
     261              : {
     262            0 :     return m_bypassed;
     263              : }
     264              : 
     265              : template<typename T>
     266            0 : void AudioNode<T>::onBypassedChanged(bool /*bypassed*/)
     267              : {
     268            0 : }
     269              : 
     270              : template<typename T>
     271            0 : IAudioNode* AudioNode<T>::connect(std::shared_ptr<IAudioNode> other)
     272              : {
     273            0 :     IF_ASSERT_FAILED(other) {
     274            0 :         return this;
     275              :     }
     276            0 :     bool ok = other->doAddNode(shared_from_this());
     277            0 :     if (ok) {
     278            0 :         m_connectedTo.push_back(other);
     279              :         ANODE_LOG(name()) << "connected to: " << other->name();
     280              :     }
     281            0 :     return this;
     282              : }
     283              : 
     284              : template<typename T>
     285            0 : IAudioNode* AudioNode<T>::disconnect(std::shared_ptr<IAudioNode> other)
     286              : {
     287            0 :     IF_ASSERT_FAILED(other) {
     288            0 :         return this;
     289              :     }
     290            0 :     bool ok = other->doRemoveNode(shared_from_this());
     291            0 :     if (ok) {
     292            0 :         muse::remove(m_connectedTo, other);
     293              :         ANODE_LOG(name()) << "disconnected from: " << other->name();
     294              :     }
     295            0 :     return this;
     296              : }
     297              : 
     298              : template<typename T>
     299            0 : void AudioNode<T>::disconnectAll()
     300              : {
     301            0 :     auto copy = m_connectedTo;
     302            0 :     for (auto& connectedTo : copy) {
     303            0 :         disconnect(connectedTo);
     304              :     }
     305            0 : }
     306              : 
     307              : template<typename T>
     308            0 : bool AudioNode<T>::doAddNode(std::shared_ptr<IAudioNode> other)
     309              : {
     310            0 :     IF_ASSERT_FAILED(other) {
     311            0 :         return false;
     312              :     }
     313              : 
     314            0 :     IF_ASSERT_FAILED(!m_input) {
     315            0 :         LOGE() << "already connected to another node";
     316            0 :         return false;
     317              :     }
     318              : 
     319            0 :     m_input = other;
     320            0 :     return true;
     321              : }
     322              : 
     323              : template<typename T>
     324            0 : bool AudioNode<T>::doRemoveNode(std::shared_ptr<IAudioNode> other)
     325              : {
     326            0 :     IF_ASSERT_FAILED(other) {
     327            0 :         return false;
     328              :     }
     329              : 
     330            0 :     IF_ASSERT_FAILED(m_input == other) {
     331            0 :         LOGE() << "not connected to this node";
     332            0 :         return false;
     333              :     }
     334              : 
     335            0 :     m_input = nullptr;
     336            0 :     return true;
     337              : }
     338              : 
     339              : template<typename T>
     340            0 : void AudioNode<T>::process(float* buffer, samples_t samplesPerChannel)
     341              : {
     342            0 :     if (!m_enabled) {
     343            0 :         return;
     344              :     }
     345              : 
     346            0 :     if (m_input) {
     347            0 :         m_input->process(buffer, samplesPerChannel);
     348              :     }
     349              : 
     350            0 :     if (m_bypassed) {
     351            0 :         return;
     352              :     }
     353              : 
     354            0 :     doSelfProcess(buffer, samplesPerChannel);
     355              : }
     356              : 
     357              : template<typename T>
     358            0 : std::string AudioNode<T>::dump() const
     359              : {
     360            0 :     std::stringstream ss;
     361            0 :     ss << name();
     362            0 :     if (m_input) {
     363            0 :         ss << " <-- " << m_input->dump();
     364              :     }
     365            0 :     return ss.str();
     366            0 : }
     367              : }
        

Generated by: LCOV version 2.0-1