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 :
23 : #include "vstplugininstance.h"
24 :
25 : #include "modularity/ioc.h"
26 : #include "vstpluginprovider.h"
27 :
28 : #include "async/async.h"
29 :
30 : #include "defer.h"
31 : #include "log.h"
32 :
33 : using namespace muse;
34 : using namespace muse::vst;
35 : using namespace muse::async;
36 :
37 : static const std::string_view COMPONENT_STATE_KEY = "componentState";
38 : static const std::string_view CONTROLLER_STATE_KEY = "controllerState";
39 :
40 : static VstPluginInstanceId s_lastId = 0;
41 :
42 0 : static void stateBufferFromString(VstMemoryStream& buffer, char* strData, const size_t strSize)
43 : {
44 0 : if (strSize == 0) {
45 0 : return;
46 : }
47 :
48 0 : buffer.setSize(0);
49 0 : buffer.write(strData, static_cast<Steinberg::int32>(strSize), nullptr);
50 0 : buffer.seek(0, Steinberg::IBStream::kIBSeekSet, nullptr);
51 : }
52 :
53 0 : VstPluginInstance::VstPluginInstance(const muse::audio::AudioResourceId& resourceId)
54 0 : : m_resourceId(resourceId), m_componentHandlerPtr(new VstComponentHandler())
55 : {
56 0 : ONLY_AUDIO_THREAD(threadSecurer);
57 :
58 0 : m_id = ++s_lastId;
59 :
60 0 : m_componentHandlerPtr->pluginParamsChanged().onNotify(this, [this]() {
61 0 : Async::call(this, [this]() {
62 0 : rescanParams();
63 0 : }, threadSecurer()->mainThreadId());
64 0 : });
65 0 : }
66 :
67 0 : VstPluginInstance::~VstPluginInstance()
68 : {
69 : //! NOTE: Signal early so any rescanParams() already queued to the main thread
70 : //! returns before touching members (provider, etc.)
71 0 : m_isLoaded = false;
72 :
73 0 : muse::audio::AudioResourceId resourceId = m_resourceId;
74 0 : std::shared_ptr<VstPluginProvider> provider = std::move(m_pluginProvider);
75 0 : PluginModulePtr module = std::move(m_module);
76 :
77 0 : auto securer = threadSecurer;
78 0 : auto repo = modulesRepo;
79 :
80 0 : Async::call(nullptr, [resourceId, provider, module, securer, repo]() mutable {
81 0 : ONLY_MAIN_THREAD(securer);
82 :
83 0 : repo()->removePluginModule(resourceId);
84 :
85 : //! NOTE: the order of destruction is important here.
86 : //! Deactivate the component before the provider destroys it.
87 : //! This must happen on the main thread, after any editor view
88 : //! has been closed, to avoid crashes in plugins (e.g. ZENOLOGY)
89 : //! that free editor-dependent resources in setActive(false).
90 0 : if (provider) {
91 0 : auto component = provider->component();
92 0 : if (component) {
93 0 : component->setActive(false);
94 : }
95 0 : }
96 :
97 0 : provider.reset();
98 0 : module.reset();
99 0 : }, threadSecurer()->mainThreadId());
100 0 : }
101 :
102 0 : const muse::audio::AudioResourceId& VstPluginInstance::resourceId() const
103 : {
104 0 : return m_resourceId;
105 : }
106 :
107 0 : const std::string& VstPluginInstance::name() const
108 : {
109 0 : return m_resourceId;
110 : }
111 :
112 0 : VstPluginInstanceId VstPluginInstance::id() const
113 : {
114 0 : return m_id;
115 : }
116 :
117 : /**
118 : * @brief VstPluginInstance::load
119 : * @note Some Vst plugins might not support loading in the background threads.
120 : * So we have to ensure that this function being executed in the main thread
121 : */
122 0 : void VstPluginInstance::load()
123 : {
124 0 : Async::call(this, [this]() {
125 0 : ONLY_MAIN_THREAD(threadSecurer);
126 :
127 0 : m_module = modulesRepo()->pluginModule(m_resourceId);
128 0 : if (!m_module) {
129 0 : modulesRepo()->addPluginModule(m_resourceId);
130 0 : m_module = modulesRepo()->pluginModule(m_resourceId);
131 : }
132 :
133 0 : if (!m_module) {
134 0 : LOGE() << "Unable to find vst plugin module, resourceId: " << m_resourceId;
135 0 : return;
136 : }
137 :
138 0 : const auto& factory = m_module->getFactory();
139 :
140 0 : for (const ClassInfo& classInfo : factory.classInfos()) {
141 0 : if (classInfo.category() != kVstAudioEffectClass) {
142 0 : continue;
143 : }
144 :
145 0 : m_pluginProvider = std::make_unique<VstPluginProvider>(factory, classInfo);
146 0 : break;
147 0 : }
148 :
149 0 : if (!m_pluginProvider) {
150 0 : LOGE() << "Unable to load vst plugin provider";
151 0 : return;
152 : }
153 :
154 0 : if (!m_pluginProvider->init()) {
155 0 : LOGE() << "Unable to initialize vst plugin provider";
156 0 : return;
157 : }
158 :
159 0 : PluginControllerPtr controller = m_pluginProvider->controller();
160 0 : if (!controller) {
161 0 : return;
162 : }
163 :
164 0 : controller->setComponentHandler(m_componentHandlerPtr);
165 0 : syncControllerToComponentState();
166 :
167 0 : m_isLoaded = true;
168 0 : m_loadingCompleted.notify();
169 0 : }, threadSecurer()->mainThreadId());
170 0 : }
171 :
172 0 : void VstPluginInstance::syncControllerToComponentState()
173 : {
174 0 : ONLY_MAIN_THREAD(threadSecurer);
175 :
176 : // Synchronize controller to the component's default state.
177 : // Some plugins (e.g. Roland Cloud ZENOLOGY) rely on this to
178 : // fully initialize internal data structures; without it the
179 : // controller may crash when the editor UI is opened.
180 0 : PluginComponentPtr component = m_pluginProvider->component();
181 0 : PluginControllerPtr controller = m_pluginProvider->controller();
182 :
183 0 : if (!component || !controller) {
184 0 : return;
185 : }
186 :
187 0 : m_componentStateBuffer.seek(0, Steinberg::IBStream::kIBSeekSet, nullptr);
188 0 : m_componentStateBuffer.setSize(0);
189 :
190 0 : if (component->getState(&m_componentStateBuffer) != Steinberg::kResultOk) {
191 0 : return;
192 : }
193 :
194 0 : if (m_componentStateBuffer.getSize() > 0) {
195 0 : m_componentStateBuffer.seek(0, Steinberg::IBStream::kIBSeekSet, nullptr);
196 0 : controller->setComponentState(&m_componentStateBuffer);
197 : }
198 0 : }
199 :
200 0 : void VstPluginInstance::rescanParams()
201 : {
202 0 : ONLY_MAIN_THREAD(threadSecurer);
203 :
204 0 : if (!m_isLoaded) {
205 0 : return;
206 : }
207 :
208 0 : if (!m_pluginProvider) {
209 0 : LOGE() << "Plugin provider is not initialized";
210 0 : return;
211 : }
212 :
213 0 : PluginComponentPtr component = m_pluginProvider->component();
214 0 : PluginControllerPtr controller = m_pluginProvider->controller();
215 :
216 0 : if (!controller || !component) {
217 0 : return;
218 : }
219 :
220 0 : m_componentStateBuffer.seek(0, Steinberg::IBStream::kIBSeekSet, nullptr);
221 0 : m_componentStateBuffer.setSize(0);
222 :
223 0 : Steinberg::tresult res = component->getState(&m_componentStateBuffer);
224 0 : if (res != Steinberg::kResultOk && res != Steinberg::kNotImplemented) {
225 0 : LOGW() << "Component state scan failed: " << m_resourceId;
226 0 : return;
227 : }
228 :
229 0 : m_controllerStateBuffer.seek(0, Steinberg::IBStream::kIBSeekSet, nullptr);
230 0 : m_controllerStateBuffer.setSize(0);
231 :
232 0 : res = controller->getState(&m_controllerStateBuffer);
233 0 : if (res != Steinberg::kResultOk && res != Steinberg::kNotImplemented) {
234 0 : LOGW() << "Controller state scan failed: " << m_resourceId;
235 0 : return;
236 : }
237 :
238 0 : muse::audio::AudioUnitConfig updatedConfig;
239 :
240 0 : if (m_componentStateBuffer.getSize() > 0) {
241 0 : updatedConfig.emplace(COMPONENT_STATE_KEY, std::string(m_componentStateBuffer.getData(), m_componentStateBuffer.getSize()));
242 : }
243 :
244 0 : if (m_controllerStateBuffer.getSize() > 0) {
245 0 : updatedConfig.emplace(CONTROLLER_STATE_KEY, std::string(m_controllerStateBuffer.getData(), m_controllerStateBuffer.getSize()));
246 : }
247 :
248 0 : m_pluginSettingsChanges.send(updatedConfig);
249 0 : }
250 :
251 0 : void VstPluginInstance::setPluginConfig(const audio::AudioUnitConfig& config)
252 : {
253 0 : ONLY_MAIN_THREAD(threadSecurer);
254 :
255 0 : if (!m_isLoaded) {
256 0 : return;
257 : }
258 :
259 0 : if (!m_pluginProvider) {
260 0 : LOGE() << "Plugin provider is not initialized";
261 0 : return;
262 : }
263 :
264 0 : PluginControllerPtr controller = m_pluginProvider->controller();
265 0 : PluginComponentPtr component = m_pluginProvider->component();
266 :
267 0 : if (!controller || !component) {
268 0 : LOGE() << "Unable to update settings for VST plugin";
269 0 : return;
270 : }
271 :
272 0 : auto componentState = config.find(COMPONENT_STATE_KEY.data());
273 0 : auto controllerState = config.find(CONTROLLER_STATE_KEY.data());
274 :
275 0 : if (componentState == config.end() && controllerState == config.end()) {
276 0 : return;
277 : }
278 :
279 0 : m_componentHandlerPtr->setSuppressNotify(true);
280 0 : DEFER {
281 0 : m_componentHandlerPtr->setSuppressNotify(false);
282 0 : };
283 :
284 : try {
285 0 : if (componentState != config.end() && !componentState->second.empty()) {
286 0 : stateBufferFromString(m_componentStateBuffer, const_cast<char*>(componentState->second.c_str()), componentState->second.size());
287 :
288 0 : if (component->setState(&m_componentStateBuffer) == Steinberg::kResultOk) {
289 0 : m_componentStateBuffer.seek(0, Steinberg::IBStream::kIBSeekSet, nullptr);
290 0 : controller->setComponentState(&m_componentStateBuffer);
291 : }
292 : }
293 0 : } catch (...) {
294 0 : LOGW() << "Component state restore failed: " << m_resourceId;
295 0 : }
296 :
297 : try {
298 0 : if (controllerState != config.end() && !controllerState->second.empty()) {
299 0 : stateBufferFromString(m_controllerStateBuffer, const_cast<char*>(controllerState->second.data()),
300 0 : controllerState->second.size());
301 0 : controller->setState(&m_controllerStateBuffer);
302 : }
303 0 : } catch (...) {
304 0 : LOGW() << "Controller state restore failed: " << m_resourceId;
305 0 : }
306 0 : }
307 :
308 0 : PluginViewPtr VstPluginInstance::createView() const
309 : {
310 0 : ONLY_MAIN_THREAD(threadSecurer);
311 :
312 0 : if (!m_isLoaded || !m_pluginProvider) {
313 0 : return nullptr;
314 : }
315 :
316 0 : PluginControllerPtr controller = m_pluginProvider->controller();
317 0 : if (!controller) {
318 0 : return nullptr;
319 : }
320 :
321 0 : return owned(controller->createView(PluginEditorViewType::kEditor));
322 0 : }
323 :
324 0 : PluginControllerPtr VstPluginInstance::controller() const
325 : {
326 0 : ONLY_AUDIO_THREAD(threadSecurer);
327 :
328 0 : if (!m_isLoaded || !m_pluginProvider) {
329 0 : return nullptr;
330 : }
331 :
332 0 : return m_pluginProvider->controller();
333 : }
334 :
335 0 : PluginComponentPtr VstPluginInstance::component() const
336 : {
337 : // TODO: Audio engine or process thread
338 : // ONLY_AUDIO_THREAD(threadSecurer);
339 :
340 0 : if (!m_isLoaded || !m_pluginProvider) {
341 0 : return nullptr;
342 : }
343 :
344 0 : return m_pluginProvider->component();
345 : }
346 :
347 0 : PluginMidiMappingPtr VstPluginInstance::midiMapping() const
348 : {
349 0 : ONLY_AUDIO_THREAD(threadSecurer);
350 :
351 0 : if (!m_isLoaded || !m_pluginProvider) {
352 0 : return nullptr;
353 : }
354 :
355 0 : return m_pluginProvider->midiMapping();
356 : }
357 :
358 0 : void VstPluginInstance::updatePluginConfig(const audio::AudioUnitConfig& config)
359 : {
360 0 : ONLY_AUDIO_THREAD(threadSecurer);
361 :
362 0 : Async::call(this, [this, config]() {
363 0 : setPluginConfig(config);
364 0 : }, threadSecurer()->mainThreadId());
365 0 : }
366 :
367 0 : void VstPluginInstance::refreshConfig()
368 : {
369 0 : ONLY_MAIN_THREAD(threadSecurer);
370 :
371 0 : rescanParams();
372 0 : }
373 :
374 0 : bool VstPluginInstance::isLoaded() const
375 : {
376 0 : ONLY_AUDIO_OR_MAIN_THREAD(threadSecurer);
377 :
378 0 : return m_isLoaded;
379 : }
380 :
381 0 : Notification VstPluginInstance::loadingCompleted() const
382 : {
383 0 : return m_loadingCompleted;
384 : }
385 :
386 0 : async::Channel<muse::audio::AudioUnitConfig> VstPluginInstance::pluginSettingsChanged() const
387 : {
388 0 : ONLY_AUDIO_THREAD(threadSecurer);
389 :
390 0 : return m_pluginSettingsChanges;
391 : }
|