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 : #include "fxchain.h"
24 : #include "fxnode.h"
25 :
26 : #include "log.h"
27 :
28 : using namespace muse;
29 : using namespace muse::audio;
30 : using namespace muse::audio::engine;
31 :
32 0 : void FxChain::setFxList(const std::vector<IFxProcessorPtr>& fxList)
33 : {
34 0 : clear();
35 :
36 0 : for (auto it = fxList.rbegin(); it != fxList.rend(); ++it) {
37 0 : FxNodePtr node = std::make_shared<FxNode>(*it);
38 0 : doAdd(node);
39 :
40 0 : node->paramsChanged().onReceive(this, [this](const AudioFxParams& fxParams) {
41 0 : m_fxChainSpec.insert_or_assign(fxParams.chainOrder, fxParams);
42 0 : m_fxChainSpecChanged.send(m_fxChainSpec);
43 0 : updateShouldProcessDuringSilence();
44 0 : }, async::Asyncable::Mode::SetReplace);
45 0 : }
46 :
47 0 : rebuild();
48 0 : updateShouldProcessDuringSilence();
49 :
50 0 : if (!name().empty() && !m_nodes.empty()) {
51 0 : LOGD() << name() << ": " << m_nodes.front()->dump();
52 : }
53 0 : }
54 :
55 0 : void FxChain::setFxChainSpec(const AudioFxChain& fxChainSpec)
56 : {
57 0 : if (m_fxChainSpec == fxChainSpec) {
58 0 : return;
59 : }
60 :
61 0 : auto findFxNode = [this](const std::pair<AudioFxChainOrder, AudioFxParams>& params) -> FxNodePtr {
62 0 : for (auto& node : m_nodes) {
63 0 : FxNodePtr fx = std::dynamic_pointer_cast<FxNode>(node);
64 0 : IF_ASSERT_FAILED(fx) {
65 0 : continue;
66 : }
67 :
68 0 : if (fx->params().chainOrder != params.first) {
69 0 : continue;
70 : }
71 :
72 0 : if (fx->params().resourceMeta == params.second.resourceMeta) {
73 0 : return fx;
74 : }
75 0 : }
76 :
77 0 : return nullptr;
78 0 : };
79 :
80 0 : m_fxChainSpec = fxChainSpec;
81 0 : for (auto it = m_fxChainSpec.begin(); it != m_fxChainSpec.end();) {
82 0 : if (FxNodePtr fx = findFxNode(*it)) {
83 0 : fx->setBypassed(!it->second.active);
84 0 : ++it;
85 : } else {
86 0 : it = m_fxChainSpec.erase(it);
87 0 : }
88 : }
89 :
90 0 : m_fxChainSpecChanged.send(m_fxChainSpec);
91 0 : updateShouldProcessDuringSilence();
92 : }
93 :
94 0 : const AudioFxChain& FxChain::fxChainSpec() const
95 : {
96 0 : return m_fxChainSpec;
97 : }
98 :
99 0 : async::Channel<AudioFxChain> FxChain::fxChainSpecChanged() const
100 : {
101 0 : return m_fxChainSpecChanged;
102 : }
103 :
104 0 : void FxChain::setPlayheadPosition(PlayheadPositionPtr playheadPosition)
105 : {
106 0 : for (auto& node : m_nodes) {
107 0 : FxNodePtr fx = std::dynamic_pointer_cast<FxNode>(node);
108 0 : IF_ASSERT_FAILED(fx) {
109 0 : continue;
110 : }
111 :
112 0 : fx->setPlayheadPosition(playheadPosition);
113 0 : }
114 0 : }
115 :
116 0 : void FxChain::rebuild()
117 : {
118 0 : for (size_t i = 1; i < m_nodes.size(); ++i) {
119 0 : IAudioNodePtr& prev = m_nodes.at(i - 1);
120 0 : IAudioNodePtr& curr = m_nodes.at(i);
121 0 : curr->disconnectAll();
122 0 : curr->connect(prev); // prev->input = curr
123 : }
124 0 : }
125 :
126 0 : void FxChain::doSelfProcess(float* buffer, samples_t samplesPerChannel)
127 : {
128 0 : if (!m_nodes.empty()) {
129 0 : m_nodes.front()->process(buffer, samplesPerChannel);
130 : }
131 0 : }
132 :
133 0 : void FxChain::updateShouldProcessDuringSilence()
134 : {
135 0 : bool shouldProcessDuringSilence = false;
136 0 : for (const auto& node : m_nodes) {
137 0 : FxNodePtr fx = std::dynamic_pointer_cast<FxNode>(node);
138 0 : IF_ASSERT_FAILED(fx) {
139 0 : continue;
140 : }
141 :
142 0 : if (fx->shouldProcessDuringSilence()) {
143 0 : shouldProcessDuringSilence = true;
144 0 : break;
145 : }
146 0 : }
147 :
148 0 : if (m_shouldProcessDuringSilence != shouldProcessDuringSilence) {
149 0 : m_shouldProcessDuringSilence = shouldProcessDuringSilence;
150 0 : m_shouldProcessDuringSilenceChanged.send(shouldProcessDuringSilence);
151 : }
152 0 : }
153 :
154 0 : bool FxChain::shouldProcessDuringSilence() const
155 : {
156 0 : return m_shouldProcessDuringSilence;
157 : }
158 :
159 0 : async::Channel<bool> FxChain::shouldProcessDuringSilenceChanged() const
160 : {
161 0 : return m_shouldProcessDuringSilenceChanged;
162 : }
|