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) 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 : #include "mixer.h"
23 :
24 : #include <sstream>
25 :
26 : #include "audio/common/audiosanitizer.h"
27 : #include "audio/common/audioerrors.h"
28 :
29 : #include "nodes/signalnode.h"
30 :
31 : #include "log.h"
32 :
33 : using namespace muse;
34 : using namespace muse::async;
35 : using namespace muse::audio;
36 : using namespace muse::audio::engine;
37 :
38 : constexpr size_t MIN_TRACK_COUNT_FOR_MULTITHREADING = 2;
39 :
40 0 : static bool isChainSilent(const TrackChainPtr& chain)
41 : {
42 0 : if (auto signal = chain->signal()) {
43 0 : return signal->isSilent();
44 0 : }
45 0 : return false;
46 : }
47 :
48 0 : Mixer::~Mixer()
49 : {
50 : ONLY_AUDIO_MAIN_OR_ENGINE_THREAD;
51 0 : }
52 :
53 0 : void Mixer::init()
54 : {
55 : ONLY_AUDIO_ENGINE_THREAD;
56 0 : }
57 :
58 0 : Ret Mixer::addTrack(TrackChainPtr trackChain, const AuxSendsParams& auxSends)
59 : {
60 0 : const size_t outBufferSize = m_outputSpec.samplesPerChannel * m_outputSpec.audioChannelCount;
61 :
62 0 : TrackData trackData;
63 0 : trackData.trackId = trackChain->trackId();
64 0 : trackData.chain = trackChain;
65 0 : trackData.buffer.resize(outBufferSize);
66 :
67 0 : m_tracks.emplace_back(std::move(trackData));
68 0 : m_trackTasks.reserve(m_tracks.size());
69 :
70 0 : setAuxSends(trackData.trackId, auxSends);
71 :
72 0 : return make_ok();
73 0 : }
74 :
75 0 : Ret Mixer::addAuxTrack(TrackChainPtr trackChain)
76 : {
77 : ONLY_AUDIO_ENGINE_THREAD;
78 0 : const size_t outBufferSize = m_outputSpec.samplesPerChannel * m_outputSpec.audioChannelCount;
79 :
80 0 : TrackData trackData;
81 0 : trackData.trackId = trackChain->trackId();
82 0 : trackData.chain = trackChain;
83 0 : trackData.buffer.resize(outBufferSize);
84 :
85 0 : m_auxTracks.emplace_back(std::move(trackData));
86 :
87 0 : return make_ok();
88 0 : }
89 :
90 0 : Ret Mixer::removeTrack(const TrackId trackId)
91 : {
92 : ONLY_AUDIO_ENGINE_THREAD;
93 :
94 0 : bool removed = muse::remove_if(m_tracks, [trackId](const TrackData& track) {
95 0 : return track.trackId == trackId;
96 : });
97 :
98 0 : if (removed) {
99 0 : m_auxSends.erase(trackId);
100 : }
101 :
102 0 : if (!removed) {
103 0 : removed = muse::remove_if(m_auxTracks, [trackId](const TrackData& track) {
104 0 : return track.trackId == trackId;
105 : });
106 : }
107 :
108 0 : return removed ? make_ret(Ret::Code::Ok) : make_ret(Err::InvalidTrackId);
109 : }
110 :
111 0 : void Mixer::setAuxSends(const TrackId trackId, const AuxSendsParams& auxSends)
112 : {
113 : ONLY_AUDIO_ENGINE_THREAD;
114 0 : m_auxSends[trackId] = auxSends;
115 0 : }
116 :
117 0 : void Mixer::onOutputSpecChanged(const OutputSpec& spec)
118 : {
119 : ONLY_AUDIO_ENGINE_THREAD;
120 0 : for (auto& t : m_tracks) {
121 0 : t.chain->setOutputSpec(spec);
122 : }
123 :
124 0 : for (auto& t : m_auxTracks) {
125 0 : t.chain->setOutputSpec(spec);
126 : }
127 0 : }
128 :
129 0 : void Mixer::onModeChanged(const ProcessMode mode)
130 : {
131 : ONLY_AUDIO_ENGINE_THREAD;
132 :
133 0 : for (auto& t : m_tracks) {
134 0 : t.chain->setMode(mode);
135 : }
136 :
137 0 : for (auto& t : m_auxTracks) {
138 0 : t.chain->setMode(mode);
139 : }
140 0 : }
141 :
142 0 : void Mixer::process(float* outBuffer, samples_t samplesPerChannel)
143 : {
144 : ONLY_AUDIO_PROC_THREAD;
145 :
146 0 : if (!m_enabled) {
147 0 : return;
148 : }
149 :
150 0 : const size_t outBufferSize = samplesPerChannel * m_outputSpec.audioChannelCount;
151 :
152 0 : processTrackChannels(outBufferSize, samplesPerChannel);
153 :
154 0 : prepareAuxBuffers(outBufferSize);
155 :
156 0 : for (auto& t : m_tracks) {
157 0 : if (!t.processed) {
158 0 : continue;
159 : }
160 :
161 : //! NOTE If the signal is silent, do not write to the output buffer
162 : //! or the aux buffers
163 0 : if (isChainSilent(t.chain)) {
164 0 : continue;
165 : }
166 :
167 0 : mixOutputFromChannel(outBuffer, t.buffer.data(), outBufferSize);
168 0 : writeTrackToAuxBuffers(t.buffer.data(), outBufferSize, m_auxSends[t.trackId]);
169 : }
170 :
171 0 : processAuxChannels(outBuffer, samplesPerChannel);
172 : }
173 :
174 0 : void Mixer::processTrackChannels(size_t outBufferSize,
175 : size_t samplesPerChannel)
176 : {
177 0 : auto processChannel = [outBufferSize, samplesPerChannel](TrackData& trackData) {
178 0 : IF_ASSERT_FAILED(trackData.chain) {
179 0 : return;
180 : }
181 :
182 0 : if (trackData.buffer.size() < outBufferSize) {
183 0 : trackData.buffer.resize(outBufferSize);
184 : }
185 :
186 0 : std::fill(trackData.buffer.begin(), trackData.buffer.begin() + outBufferSize, 0.f);
187 0 : trackData.chain->process(trackData.buffer.data(), samplesPerChannel);
188 0 : trackData.processed = true;
189 0 : };
190 :
191 0 : bool filterTracks = (m_mode == ProcessMode::Idle) && !m_tracksToProcessWhenIdle.empty();
192 :
193 : #ifdef MUSE_THREADS_SUPPORT
194 0 : if (useMultithreading()) {
195 0 : m_trackTasks.clear();
196 0 : for (auto& t : m_tracks) {
197 0 : t.processed = false;
198 :
199 0 : if (filterTracks && !muse::contains(m_tracksToProcessWhenIdle, t.trackId)) {
200 0 : continue;
201 : }
202 :
203 0 : m_trackTasks.emplace_back([tPtr = &t, processChannel] {
204 0 : processChannel(*tPtr);
205 0 : });
206 : }
207 0 : audioTaskScheduler()->submitRealtimeTasksAndWait(m_trackTasks);
208 : } else
209 : #endif
210 : {
211 0 : for (auto& t : m_tracks) {
212 0 : t.processed = false;
213 :
214 0 : if (filterTracks && !muse::contains(m_tracksToProcessWhenIdle, t.trackId)) {
215 0 : continue;
216 : }
217 :
218 0 : processChannel(t);
219 : }
220 : }
221 0 : }
222 :
223 0 : void Mixer::setNonMutedTrackCount(size_t count)
224 : {
225 0 : m_nonMutedTrackCount = count;
226 0 : }
227 :
228 0 : bool Mixer::useMultithreading() const
229 : {
230 : #ifdef MUSE_THREADS_SUPPORT
231 : //! NOTE Offline the render runs on the engine thread while it holds its async queue lock,
232 : //! and a pool worker needs that same lock to register a channel port, which deadlocks.
233 0 : if (m_mode == ProcessMode::PlayingOffline) {
234 0 : return false;
235 : }
236 :
237 0 : if (m_nonMutedTrackCount < MIN_TRACK_COUNT_FOR_MULTITHREADING) {
238 0 : return false;
239 : }
240 :
241 0 : if (m_mode == ProcessMode::Idle) {
242 0 : if (m_tracksToProcessWhenIdle.size() < MIN_TRACK_COUNT_FOR_MULTITHREADING) {
243 0 : return false;
244 : }
245 : }
246 :
247 0 : return true;
248 : #else
249 : return false;
250 : #endif
251 : }
252 :
253 0 : void Mixer::setTracksToProcessWhenIdle(const std::unordered_set<TrackId>& trackIds)
254 : {
255 : ONLY_AUDIO_ENGINE_THREAD;
256 :
257 0 : m_tracksToProcessWhenIdle = trackIds;
258 0 : }
259 :
260 0 : void Mixer::mixOutputFromChannel(float* outBuffer, const float* inBuffer, size_t bufferSize) const
261 : {
262 0 : IF_ASSERT_FAILED(outBuffer && inBuffer) {
263 0 : return;
264 : }
265 :
266 0 : for (size_t i = 0; i < bufferSize; ++i) {
267 0 : outBuffer[i] += inBuffer[i];
268 : }
269 : }
270 :
271 0 : void Mixer::prepareAuxBuffers(size_t outBufferSize)
272 : {
273 0 : for (auto& aux : m_auxTracks) {
274 0 : aux.processed = false;
275 0 : if (!aux.chain->fxChain()) {
276 0 : continue;
277 : }
278 0 : aux.buffer.resize(outBufferSize);
279 0 : std::fill(aux.buffer.begin(), aux.buffer.begin() + outBufferSize, 0.f);
280 : }
281 0 : }
282 :
283 0 : void Mixer::writeTrackToAuxBuffers(const float* trackBuffer, size_t outBufferSize, const AuxSendsParams& auxSends)
284 : {
285 0 : for (aux_channel_idx_t auxIdx = 0; auxIdx < auxSends.size(); ++auxIdx) {
286 0 : if (auxIdx >= m_auxTracks.size()) {
287 0 : break;
288 : }
289 :
290 0 : TrackData& aux = m_auxTracks.at(auxIdx);
291 0 : if (!aux.chain->fxChain()) {
292 0 : continue;
293 : }
294 :
295 0 : const AuxSendParams& auxSend = auxSends.at(auxIdx);
296 0 : if (!auxSend.active || muse::is_zero(auxSend.signalAmount)) {
297 0 : continue;
298 : }
299 :
300 0 : float* auxBuffer = aux.buffer.data();
301 0 : float signalAmount = auxSend.signalAmount;
302 :
303 0 : for (size_t i = 0; i < outBufferSize; ++i) {
304 0 : auxBuffer[i] += trackBuffer[i] * signalAmount;
305 : }
306 :
307 0 : aux.processed = true;
308 : }
309 0 : }
310 :
311 0 : void Mixer::processAuxChannels(float* buffer, samples_t samplesPerChannel)
312 : {
313 0 : const size_t outBufferSize = samplesPerChannel * m_outputSpec.audioChannelCount;
314 :
315 0 : for (TrackData& aux : m_auxTracks) {
316 : //! NOTE Process when the aux received a signal this block (aux.processed) and/or if
317 : //! it's not yet silent (e.g. reverb is still ringing out)
318 0 : if (!aux.processed && isChainSilent(aux.chain)) {
319 0 : continue;
320 : }
321 :
322 0 : float* auxBuffer = aux.buffer.data();
323 0 : aux.chain->process(auxBuffer, samplesPerChannel);
324 :
325 : //! NOTE If the signal is silent, do not write to the output buffer
326 0 : if (!isChainSilent(aux.chain)) {
327 0 : mixOutputFromChannel(buffer, auxBuffer, outBufferSize);
328 : }
329 : }
330 0 : }
331 :
332 0 : std::string Mixer::dump() const
333 : {
334 0 : std::stringstream ss;
335 0 : ss << "\n";
336 0 : ss << name() << ":";
337 :
338 0 : int indent = 2;
339 :
340 0 : ss << "\n";
341 0 : ss << std::string(indent, ' ') << "tracks: " << m_tracks.size();
342 0 : for (const auto& track : m_tracks) {
343 0 : ss << "\n";
344 0 : ss << std::string(indent, ' ') << "<--[" << track.trackId << "] " << track.chain->dump();
345 : }
346 :
347 0 : ss << "\n";
348 0 : ss << std::string(indent, ' ') << "auxs: " << m_auxTracks.size();
349 0 : for (const auto& aux : m_auxTracks) {
350 0 : ss << "\n";
351 0 : ss << std::string(indent, ' ') << "<--[" << aux.trackId << "] " << aux.chain->dump();
352 : }
353 :
354 0 : return ss.str();
355 0 : }
|