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) 2025 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 "vstaudioclient.h"
23 :
24 : #include "log.h"
25 :
26 : using namespace muse;
27 : using namespace muse::vst;
28 : using namespace muse::mpe;
29 : using namespace muse::audio;
30 : using namespace muse::audio::engine;
31 : using namespace muse::audioplugins;
32 : using namespace muse::midiremote;
33 :
34 0 : static size_t noteEventKey(int pitch, int channel)
35 : {
36 0 : std::size_t h1 = std::hash<int> {}(pitch);
37 0 : std::size_t h2 = std::hash<int> {}(channel);
38 0 : return h1 ^ (h2 << 1);
39 : }
40 :
41 0 : static std::optional<TransportEvent> mmcToTransportEvent(const IMMCDecoderPtr& decoder, const MMCMessage& msg)
42 : {
43 0 : switch (msg.command) {
44 0 : case MMCCommand::Play:
45 0 : return TransportEvent::play();
46 0 : case MMCCommand::Pause:
47 0 : return TransportEvent::pause();
48 0 : case MMCCommand::Stop:
49 0 : return TransportEvent::stop();
50 0 : case MMCCommand::Locate: {
51 0 : const std::optional<double> pos = decoder->locateToSeconds(msg);
52 0 : if (pos.has_value()) {
53 0 : return TransportEvent::seek(pos.value());
54 : }
55 0 : } break;
56 0 : default: break;
57 : }
58 :
59 0 : return std::nullopt;
60 : }
61 :
62 0 : VstAudioClient::VstAudioClient()
63 : {
64 0 : m_processContext.state = 0;
65 0 : }
66 :
67 0 : VstAudioClient::~VstAudioClient()
68 : {
69 : // Do not call setActive(false) or terminate() here.
70 : // The component lifecycle is managed by VstPluginInstance,
71 : // which defers cleanup to the main thread so that any open
72 : // editor view is destroyed first (required by ZENOLOGY).
73 0 : }
74 :
75 0 : void VstAudioClient::init(PluginType type, IVstPluginInstancePtr instance)
76 : {
77 0 : IF_ASSERT_FAILED(instance && type != PluginType::Undefined) {
78 0 : return;
79 : }
80 :
81 0 : m_type = type;
82 0 : m_pluginPtr = std::move(instance);
83 :
84 0 : if (mmcDecoderFactory()) {
85 0 : m_mmcDecoder = mmcDecoderFactory()->makeDecoder();
86 : }
87 :
88 0 : transportEventsDispatcher(); // Force resolution outside audio callback
89 : }
90 :
91 0 : void VstAudioClient::loadSupportedParams()
92 : {
93 0 : TRACEFUNC;
94 :
95 0 : IF_ASSERT_FAILED(m_pluginPtr) {
96 0 : return;
97 : }
98 :
99 0 : PluginControllerPtr controller = m_pluginPtr->controller();
100 0 : IF_ASSERT_FAILED(controller) {
101 0 : return;
102 : }
103 :
104 0 : const int paramCount = controller->getParameterCount();
105 0 : m_pluginParamInfoMap.reserve(static_cast<size_t>(paramCount));
106 :
107 0 : for (int i = 0; i < paramCount; ++i) {
108 : PluginParamInfo info;
109 0 : controller->getParameterInfo(i, info);
110 0 : m_pluginParamInfoMap.emplace(info.id, std::move(info));
111 : }
112 0 : }
113 :
114 0 : void VstAudioClient::setIsActive(const bool isActive)
115 : {
116 0 : flushSound();
117 :
118 0 : if (isActive) {
119 0 : ensureActivity();
120 : } else {
121 0 : disableActivity();
122 : }
123 0 : }
124 :
125 0 : void VstAudioClient::setIsPlaying(const bool newPlaying)
126 : {
127 0 : constexpr uint32_t playingFlag = static_cast<uint32_t>(VstProcessContext::kPlaying);
128 0 : const bool playing = (m_processContext.state & playingFlag) != 0;
129 0 : if (playing == newPlaying) {
130 0 : return;
131 : }
132 :
133 0 : if (newPlaying) {
134 0 : m_processContext.state |= playingFlag;
135 : } else {
136 0 : m_processContext.state &= ~playingFlag;
137 : }
138 :
139 0 : m_needUpdateState = m_isActive;
140 : }
141 :
142 0 : void VstAudioClient::setOutputSpec(const audio::OutputSpec& spec)
143 : {
144 0 : if (m_outputSpec == spec) {
145 0 : return;
146 : }
147 :
148 0 : m_processData.numSamples = static_cast<Steinberg::int32>(spec.samplesPerChannel);
149 0 : m_outputSpec = spec;
150 0 : m_needUnprepareProcessData = true;
151 0 : m_needUpdateState = false;
152 :
153 0 : updateProcessSetup();
154 : }
155 :
156 0 : void VstAudioClient::setProcessMode(VstProcessMode mode)
157 : {
158 0 : if (m_processMode == mode) {
159 0 : return;
160 : }
161 :
162 0 : m_processMode = mode;
163 0 : m_needUnprepareProcessData = true;
164 0 : m_needUpdateState = false;
165 :
166 0 : updateProcessSetup();
167 : }
168 :
169 0 : void VstAudioClient::setVolumeGain(const muse::audio::gain_t newVolumeGain)
170 : {
171 0 : m_volumeGain = newVolumeGain;
172 0 : }
173 :
174 0 : bool VstAudioClient::handleEvent(const VstEvent& event)
175 : {
176 0 : ensureActivity();
177 :
178 0 : if (event.type == VstEvent::kNoteOnEvent) {
179 0 : size_t key = noteEventKey(event.noteOn.pitch, event.noteOn.channel);
180 0 : m_playingNotes.insert_or_assign(key, event);
181 0 : } else if (event.type == VstEvent::kNoteOffEvent) {
182 0 : size_t key = noteEventKey(event.noteOff.pitch, event.noteOff.channel);
183 0 : m_playingNotes.erase(key);
184 : }
185 :
186 0 : if (m_inputEvents.addEvent(const_cast<VstEvent&>(event)) == Steinberg::kResultTrue) {
187 0 : return true;
188 : }
189 :
190 0 : return false;
191 : }
192 :
193 0 : bool VstAudioClient::handleParamChange(const ParamChangeEvent& param)
194 : {
195 0 : ensureActivity();
196 0 : addParamChange(param);
197 :
198 0 : m_playingParams.push_back(param.paramId);
199 :
200 0 : return true;
201 : }
202 :
203 0 : void VstAudioClient::flushSound()
204 : {
205 0 : if (m_playingNotes.empty() && m_playingParams.empty()) {
206 0 : return;
207 : }
208 :
209 0 : flushBuffers();
210 :
211 0 : m_inputEvents.clear();
212 0 : m_inputParamChanges.clearQueue();
213 :
214 0 : for (const auto& pair : m_playingNotes) {
215 0 : const VstEvent& noteOn = pair.second;
216 :
217 : VstEvent noteOff;
218 0 : noteOff.type = VstEvent::kNoteOffEvent;
219 0 : noteOff.ppqPosition = 0;
220 0 : noteOff.sampleOffset = 0;
221 0 : noteOff.busIndex = noteOn.busIndex;
222 0 : noteOff.flags = noteOn.flags;
223 0 : noteOff.noteOff.noteId = noteOn.noteOn.noteId;
224 0 : noteOff.noteOff.channel = noteOn.noteOn.channel;
225 0 : noteOff.noteOff.pitch = noteOn.noteOn.pitch;
226 0 : noteOff.noteOff.tuning = noteOn.noteOn.tuning;
227 0 : noteOff.noteOff.velocity = noteOn.noteOn.velocity;
228 :
229 0 : m_inputEvents.addEvent(noteOff);
230 : }
231 :
232 0 : for (PluginParamId id : m_playingParams) {
233 0 : auto infoIt = m_pluginParamInfoMap.find(id);
234 0 : if (infoIt == m_pluginParamInfoMap.end()) {
235 0 : continue;
236 : }
237 :
238 0 : ParamChangeEvent paramOff;
239 0 : paramOff.paramId = id;
240 0 : paramOff.value = infoIt->second.defaultNormalizedValue;
241 :
242 0 : addParamChange(paramOff);
243 : }
244 :
245 0 : m_playingNotes.clear();
246 0 : m_playingParams.clear();
247 : }
248 :
249 0 : audio::samples_t VstAudioClient::process(float* output, samples_t samplesPerChannel,
250 : samples_t playbackPositionSamples)
251 : {
252 0 : IAudioProcessorPtr processor = pluginProcessor();
253 0 : if (!processor || !output) {
254 0 : return 0;
255 : }
256 :
257 0 : if (!m_isActive) {
258 0 : return 0;
259 : }
260 :
261 : //! NOTE: From the VST3 documentation:
262 : //!
263 : //! Note that the ProcessData->numSamples
264 : //! which indicates how many samples are used in a process call can change from call to call,
265 : //! but never bigger than the maxSamplesPerBlock
266 0 : m_processData.numSamples = samplesPerChannel;
267 :
268 0 : m_processContext.projectTimeSamples = playbackPositionSamples;
269 :
270 0 : if (samplesPerChannel > m_outputSpec.samplesPerChannel) {
271 0 : OutputSpec newSpec = m_outputSpec;
272 0 : newSpec.samplesPerChannel = samplesPerChannel;
273 0 : setOutputSpec(newSpec);
274 : }
275 :
276 0 : if (m_type == PluginType::Fx) {
277 0 : extractInputSamples(samplesPerChannel, output);
278 : }
279 :
280 0 : if (processor->process(m_processData) != Steinberg::kResultOk) {
281 0 : return 0;
282 : }
283 :
284 0 : m_needUpdateState = false;
285 :
286 0 : if (m_type == PluginType::Instrument) {
287 0 : m_inputEvents.clear();
288 0 : m_inputParamChanges.clearQueue();
289 :
290 0 : fillOutputBufferInstrument(samplesPerChannel, output);
291 : } else {
292 0 : fillOutputBufferFx(samplesPerChannel, output);
293 : }
294 :
295 0 : processOutputEvents();
296 :
297 0 : return samplesPerChannel;
298 0 : }
299 :
300 0 : ParamsMapping VstAudioClient::paramsMapping(const std::set<Steinberg::Vst::CtrlNumber>& controllers) const
301 : {
302 0 : ParamsMapping result;
303 :
304 0 : if (!m_pluginPtr) {
305 0 : return result;
306 : }
307 :
308 0 : PluginMidiMappingPtr midiMapping = m_pluginPtr->midiMapping();
309 0 : if (!midiMapping) {
310 0 : return result;
311 : }
312 :
313 0 : for (const int busIdx : m_activeInputBusses) {
314 0 : for (const auto& ctrlNum : controllers) {
315 0 : PluginParamId id = 0;
316 :
317 0 : if (midiMapping->getMidiControllerAssignment(busIdx, 0, ctrlNum, id) != Steinberg::kResultOk) {
318 0 : continue;
319 : }
320 :
321 0 : result.emplace(ctrlNum, id);
322 : }
323 : }
324 :
325 0 : return result;
326 0 : }
327 :
328 0 : IAudioProcessorPtr VstAudioClient::pluginProcessor() const
329 : {
330 0 : return static_cast<IAudioProcessorPtr>(pluginComponent());
331 : }
332 :
333 0 : PluginComponentPtr VstAudioClient::pluginComponent() const
334 : {
335 0 : if (!m_pluginComponent) {
336 0 : if (!m_pluginPtr) {
337 0 : return nullptr;
338 : }
339 :
340 0 : m_pluginComponent = m_pluginPtr->component();
341 : }
342 :
343 0 : return m_pluginComponent;
344 : }
345 :
346 0 : void VstAudioClient::setUpProcessData()
347 : {
348 0 : PluginComponentPtr component = pluginComponent();
349 0 : if (!component) {
350 0 : return;
351 : }
352 :
353 0 : m_processContext.sampleRate = m_outputSpec.sampleRate;
354 0 : m_processData.inputEvents = &m_inputEvents;
355 0 : m_processData.inputParameterChanges = &m_inputParamChanges;
356 0 : m_processData.outputEvents = &m_outputEvents;
357 0 : m_processData.processContext = &m_processContext;
358 :
359 0 : if (m_needUnprepareProcessData) {
360 0 : m_processData.unprepare();
361 0 : m_needUnprepareProcessData = false;
362 : }
363 :
364 0 : if (!m_processData.outputs || !m_processData.inputs) {
365 0 : m_processData.prepare(*component, m_outputSpec.samplesPerChannel, Steinberg::Vst::kSample32);
366 : }
367 :
368 0 : if (!m_activeOutputBusses.empty() && !m_activeInputBusses.empty()) {
369 0 : return;
370 : }
371 :
372 : BusInfo busInfo;
373 :
374 0 : for (int busIndex = 0; busIndex < m_processData.numInputs; ++busIndex) {
375 0 : component->getBusInfo(BusMediaType::kAudio, BusDirection::kInput, busIndex, busInfo);
376 :
377 0 : if (busInfo.busType == BusType::kMain && (busInfo.flags & BusInfo::kDefaultActive)) {
378 0 : component->activateBus(BusMediaType::kAudio, BusDirection::kInput, busIndex, true);
379 0 : m_activeInputBusses.emplace_back(busIndex);
380 : }
381 : }
382 :
383 0 : for (int busIndex = 0; busIndex < m_processData.numOutputs; ++busIndex) {
384 0 : component->getBusInfo(BusMediaType::kAudio, BusDirection::kOutput, busIndex, busInfo);
385 :
386 0 : if (busInfo.busType == BusType::kMain && (busInfo.flags & BusInfo::kDefaultActive)) {
387 0 : component->activateBus(BusMediaType::kAudio, BusDirection::kOutput, busIndex, true);
388 0 : m_activeOutputBusses.emplace_back(busIndex);
389 : }
390 :
391 0 : LOGI() << "BusIndex: " << busIndex;
392 :
393 0 : if (busInfo.busType == BusType::kMain) {
394 0 : LOGI() << "BusType: Main";
395 : } else {
396 0 : LOGI() << "BusType: Aux";
397 : }
398 :
399 0 : if (busInfo.flags & BusInfo::kDefaultActive) {
400 0 : LOGI() << "BusFlag: DefaultActive";
401 : } else {
402 0 : LOGI() << "BusFlag: ControlVoltage";
403 : }
404 : }
405 :
406 0 : if (m_activeInputBusses.empty()) {
407 0 : LOGI() << "0 active input buses, activating default bus";
408 0 : component->activateBus(BusMediaType::kAudio, BusDirection::kInput, 0, true);
409 0 : m_activeInputBusses.emplace_back(0);
410 : }
411 :
412 0 : if (m_activeOutputBusses.empty()) {
413 0 : LOGI() << "0 active output buses, activating default bus";
414 0 : component->activateBus(BusMediaType::kAudio, BusDirection::kOutput, 0, true);
415 0 : m_activeOutputBusses.emplace_back(0);
416 : }
417 0 : }
418 :
419 0 : void VstAudioClient::updateProcessSetup()
420 : {
421 0 : if (!m_outputSpec.isValid()) {
422 0 : return;
423 : }
424 :
425 0 : IAudioProcessorPtr processor = pluginProcessor();
426 0 : if (!processor) {
427 0 : return;
428 : }
429 :
430 0 : disableActivity();
431 :
432 : VstProcessSetup setup;
433 0 : setup.processMode = m_processMode;
434 0 : setup.symbolicSampleSize = Steinberg::Vst::kSample32;
435 0 : setup.maxSamplesPerBlock = m_outputSpec.samplesPerChannel;
436 0 : setup.sampleRate = m_outputSpec.sampleRate;
437 :
438 0 : if (processor->setupProcessing(setup) != Steinberg::kResultOk) {
439 0 : return;
440 : }
441 :
442 0 : setUpProcessData();
443 0 : flushBuffers();
444 :
445 0 : ensureActivity();
446 0 : }
447 :
448 0 : void VstAudioClient::extractInputSamples(samples_t sampleCount, const float* sourceBuffer)
449 : {
450 0 : if (!m_processData.inputs || !sourceBuffer) {
451 0 : return;
452 : }
453 :
454 0 : Steinberg::Vst::AudioBusBuffers& bus = m_processData.inputs[0];
455 :
456 0 : for (samples_t sampleIndex = 0; sampleIndex < sampleCount; ++sampleIndex) {
457 0 : size_t offset = sampleIndex * m_outputSpec.audioChannelCount;
458 :
459 0 : for (audioch_t audioChannelIndex = 0; audioChannelIndex < bus.numChannels; ++audioChannelIndex) {
460 0 : bus.channelBuffers32[audioChannelIndex][sampleIndex] = sourceBuffer[offset + audioChannelIndex];
461 : }
462 : }
463 : }
464 :
465 0 : void VstAudioClient::fillOutputBufferInstrument(samples_t sampleCount, float* output)
466 : {
467 0 : if (!m_processData.outputs) {
468 0 : return;
469 : }
470 :
471 0 : for (const int busIndex : m_activeOutputBusses) {
472 0 : Steinberg::Vst::AudioBusBuffers bus = m_processData.outputs[busIndex];
473 :
474 0 : for (samples_t sampleIndex = 0; sampleIndex < sampleCount; ++sampleIndex) {
475 0 : size_t offset = sampleIndex * m_outputSpec.audioChannelCount;
476 :
477 0 : for (audioch_t audioChannelIndex = 0; audioChannelIndex < bus.numChannels; ++audioChannelIndex) {
478 0 : float sample = bus.channelBuffers32[audioChannelIndex][sampleIndex];
479 0 : output[offset + audioChannelIndex] += sample * m_volumeGain;
480 : }
481 : }
482 : }
483 : }
484 :
485 0 : void VstAudioClient::fillOutputBufferFx(samples_t sampleCount, float* output)
486 : {
487 0 : if (!m_processData.outputs) {
488 0 : return;
489 : }
490 :
491 0 : for (const int busIndex : m_activeOutputBusses) {
492 0 : Steinberg::Vst::AudioBusBuffers bus = m_processData.outputs[busIndex];
493 :
494 0 : for (samples_t sampleIndex = 0; sampleIndex < sampleCount; ++sampleIndex) {
495 0 : size_t offset = sampleIndex * m_outputSpec.audioChannelCount;
496 :
497 0 : for (audioch_t audioChannelIndex = 0; audioChannelIndex < bus.numChannels; ++audioChannelIndex) {
498 0 : float sample = bus.channelBuffers32[audioChannelIndex][sampleIndex];
499 0 : output[offset + audioChannelIndex] = sample * m_volumeGain;
500 : }
501 : }
502 : }
503 : }
504 :
505 0 : void VstAudioClient::processOutputEvents()
506 : {
507 0 : const int32_t count = m_outputEvents.getEventCount();
508 0 : if (count == 0) {
509 0 : return;
510 : }
511 :
512 0 : if (!m_mmcDecoder || !transportEventsDispatcher()) {
513 0 : m_outputEvents.clear();
514 0 : return;
515 : }
516 :
517 0 : TransportEvents events;
518 :
519 0 : for (int32_t i = 0; i < count; ++i) {
520 : VstEvent vstEvent;
521 0 : if (m_outputEvents.getEvent(i, vstEvent) != Steinberg::kResultOk) {
522 0 : continue;
523 : }
524 :
525 0 : if (vstEvent.type != Steinberg::Vst::Event::kDataEvent
526 0 : || vstEvent.data.type != Steinberg::Vst::DataEvent::kMidiSysEx) {
527 0 : continue;
528 : }
529 :
530 0 : std::optional<MMCMessage> msg = m_mmcDecoder->decode(vstEvent.data.bytes, vstEvent.data.size);
531 0 : if (!msg.has_value()) {
532 0 : continue;
533 : }
534 :
535 0 : std::optional<TransportEvent> event = mmcToTransportEvent(m_mmcDecoder, msg.value());
536 0 : if (event.has_value()) {
537 0 : events.push_back(event.value());
538 : }
539 0 : }
540 :
541 0 : m_outputEvents.clear();
542 :
543 0 : if (!events.empty()) {
544 0 : transportEventsDispatcher()->dispatch(events);
545 : }
546 0 : }
547 :
548 0 : void VstAudioClient::ensureActivity()
549 : {
550 0 : if (m_isActive) {
551 0 : return;
552 : }
553 :
554 0 : IAudioProcessorPtr processor = pluginProcessor();
555 0 : if (!processor) {
556 0 : return;
557 : }
558 :
559 0 : PluginComponentPtr component = pluginComponent();
560 0 : if (!component) {
561 0 : return;
562 : }
563 :
564 0 : component->setActive(true);
565 0 : processor->setProcessing(true);
566 :
567 0 : m_isActive = true;
568 0 : }
569 :
570 0 : void VstAudioClient::disableActivity()
571 : {
572 0 : if (!m_isActive) {
573 0 : return;
574 : }
575 :
576 0 : IAudioProcessorPtr processor = pluginProcessor();
577 0 : if (!processor) {
578 0 : return;
579 : }
580 :
581 0 : PluginComponentPtr component = pluginComponent();
582 0 : if (!component) {
583 0 : return;
584 : }
585 :
586 0 : if (m_needUpdateState) {
587 0 : processor->process(m_processData);
588 0 : m_needUpdateState = false;
589 : }
590 :
591 0 : processor->setProcessing(false);
592 0 : component->setActive(false);
593 :
594 0 : m_isActive = false;
595 0 : }
596 :
597 0 : void VstAudioClient::flushBuffers()
598 : {
599 0 : for (int inputsNumber = 0; inputsNumber < m_processData.numInputs; ++inputsNumber) {
600 0 : Steinberg::Vst::AudioBusBuffers input = m_processData.inputs[inputsNumber];
601 :
602 0 : for (int i = 0; i < m_processData.numSamples; ++i) {
603 0 : for (int audioChannel = 0; audioChannel < input.numChannels; ++audioChannel) {
604 0 : input.channelBuffers32[audioChannel][i] = 0.f;
605 : }
606 : }
607 : }
608 :
609 0 : for (int outputsNumber = 0; outputsNumber < m_processData.numOutputs; ++outputsNumber) {
610 0 : Steinberg::Vst::AudioBusBuffers output = m_processData.outputs[outputsNumber];
611 :
612 0 : for (int i = 0; i < m_processData.numSamples; ++i) {
613 0 : for (int audioChannel = 0; audioChannel < output.numChannels; ++audioChannel) {
614 0 : output.channelBuffers32[audioChannel][i] = 0.f;
615 : }
616 : }
617 : }
618 0 : }
619 :
620 0 : void VstAudioClient::addParamChange(const ParamChangeEvent& param)
621 : {
622 0 : Steinberg::int32 dummyIdx = 0;
623 0 : Steinberg::Vst::IParamValueQueue* queue = m_inputParamChanges.addParameterData(param.paramId, dummyIdx);
624 0 : if (queue) {
625 0 : queue->addPoint(0, param.value, dummyIdx);
626 : }
627 0 : }
|