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