Implement LV2 ControlInputPort change request feature
[carla.git] / source / backend / plugin / CarlaPluginSFZero.cpp
blob426a537741592a2c5bf18a47f808fe6bea73ffe7
1 /*
2 * Carla SFZero Plugin
3 * Copyright (C) 2018-2023 Filipe Coelho <falktx@falktx.com>
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation; either version 2 of
8 * the License, or any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * For a full copy of the GNU General Public License see the doc/GPL.txt file.
18 #include "CarlaPluginInternal.hpp"
19 #include "CarlaEngine.hpp"
21 #ifndef STATIC_PLUGIN_TARGET
22 # define HAVE_SFZ
23 #endif
25 #ifdef HAVE_SFZ
27 #include "CarlaBackendUtils.hpp"
29 #include "sfzero/SFZero.h"
31 #include "water/buffers/AudioSampleBuffer.h"
32 #include "water/files/File.h"
33 #include "water/midi/MidiMessage.h"
35 using water::AudioSampleBuffer;
36 using water::File;
37 using water::MidiMessage;
38 using water::String;
40 // -----------------------------------------------------------------------
42 CARLA_BACKEND_START_NAMESPACE
44 // -------------------------------------------------------------------------------------------------------------------
45 // Fallback data
47 static const ExternalMidiNote kExternalMidiNoteFallback = { -1, 0, 0 };
50 static void loadingIdleCallbackFunction(void* ptr)
52 ((CarlaEngine*)ptr)->callback(true, false, ENGINE_CALLBACK_IDLE, 0, 0, 0, 0, 0.0f, nullptr);
55 // -------------------------------------------------------------------------------------------------------------------
57 class CarlaPluginSFZero : public CarlaPlugin
59 public:
60 CarlaPluginSFZero(CarlaEngine* const engine, const uint id)
61 : CarlaPlugin(engine, id),
62 fSynth(),
63 fNumVoices(0.0f),
64 fLabel(nullptr),
65 fRealName(nullptr)
67 carla_debug("CarlaPluginSFZero::CarlaPluginSFZero(%p, %i)", engine, id);
70 ~CarlaPluginSFZero() override
72 carla_debug("CarlaPluginSFZero::~CarlaPluginSFZero()");
74 pData->singleMutex.lock();
75 pData->masterMutex.lock();
77 if (pData->client != nullptr && pData->client->isActive())
78 pData->client->deactivate(true);
80 if (pData->active)
82 deactivate();
83 pData->active = false;
86 if (fLabel != nullptr)
88 delete[] fLabel;
89 fLabel = nullptr;
92 if (fRealName != nullptr)
94 delete[] fRealName;
95 fRealName = nullptr;
98 clearBuffers();
101 // -------------------------------------------------------------------
102 // Information (base)
104 PluginType getType() const noexcept override
106 return PLUGIN_SFZ;
109 PluginCategory getCategory() const noexcept override
111 return PLUGIN_CATEGORY_SYNTH;
114 // -------------------------------------------------------------------
115 // Information (count)
117 // nothing
119 // -------------------------------------------------------------------
120 // Information (current data)
122 // nothing
124 // -------------------------------------------------------------------
125 // Information (per-plugin data)
127 uint getOptionsAvailable() const noexcept override
129 uint options = 0x0;
131 options |= PLUGIN_OPTION_SEND_CONTROL_CHANGES;
132 options |= PLUGIN_OPTION_SEND_CHANNEL_PRESSURE;
133 options |= PLUGIN_OPTION_SEND_NOTE_AFTERTOUCH;
134 options |= PLUGIN_OPTION_SEND_PITCHBEND;
135 options |= PLUGIN_OPTION_SEND_ALL_SOUND_OFF;
136 options |= PLUGIN_OPTION_SKIP_SENDING_NOTES;
138 return options;
141 float getParameterValue(const uint32_t parameterId) const noexcept override
143 CARLA_SAFE_ASSERT_RETURN(parameterId == 0, 0.0f);
145 return fNumVoices;
148 bool getLabel(char* const strBuf) const noexcept override
150 if (fLabel != nullptr)
152 std::strncpy(strBuf, fLabel, STR_MAX);
153 return true;
156 return CarlaPlugin::getLabel(strBuf);
159 bool getMaker(char* const strBuf) const noexcept override
161 std::strncpy(strBuf, "SFZero engine", STR_MAX);
162 return true;
165 bool getCopyright(char* const strBuf) const noexcept override
167 std::strncpy(strBuf, "ISC", STR_MAX);
168 return true;
171 bool getRealName(char* const strBuf) const noexcept override
173 if (fRealName != nullptr)
175 std::strncpy(strBuf, fRealName, STR_MAX);
176 return true;
179 return CarlaPlugin::getRealName(strBuf);
182 bool getParameterName(const uint32_t parameterId, char* const strBuf) const noexcept override
184 CARLA_SAFE_ASSERT_RETURN(parameterId == 0, false);
186 std::strncpy(strBuf, "Voice Count", STR_MAX);
187 return true;
190 // -------------------------------------------------------------------
191 // Set data (state)
193 // nothing
195 // -------------------------------------------------------------------
196 // Set data (internal stuff)
198 // nothing
200 // -------------------------------------------------------------------
201 // Set data (plugin-specific stuff)
203 // nothing
205 // -------------------------------------------------------------------
206 // Set ui stuff
208 // nothing
210 // -------------------------------------------------------------------
211 // Plugin state
213 void reload() override
215 CARLA_SAFE_ASSERT_RETURN(pData->engine != nullptr,);
216 carla_debug("CarlaPluginSFZero::reload() - start");
218 const EngineProcessMode processMode(pData->engine->getProccessMode());
220 // Safely disable plugin for reload
221 const ScopedDisabler sd(this);
223 if (pData->active)
224 deactivate();
226 clearBuffers();
228 pData->audioOut.createNew(2);
229 pData->param.createNew(1, false);
231 const uint portNameSize(pData->engine->getMaxPortNameSize());
232 CarlaString portName;
234 // ---------------------------------------
235 // Audio Outputs
237 // out-left
238 portName.clear();
240 if (processMode == ENGINE_PROCESS_MODE_SINGLE_CLIENT)
242 portName = pData->name;
243 portName += ":";
246 portName += "out-left";
247 portName.truncate(portNameSize);
249 pData->audioOut.ports[0].port = (CarlaEngineAudioPort*)pData->client->addPort(kEnginePortTypeAudio, portName, false, 0);
250 pData->audioOut.ports[0].rindex = 0;
252 // out-right
253 portName.clear();
255 if (processMode == ENGINE_PROCESS_MODE_SINGLE_CLIENT)
257 portName = pData->name;
258 portName += ":";
261 portName += "out-right";
262 portName.truncate(portNameSize);
264 pData->audioOut.ports[1].port = (CarlaEngineAudioPort*)pData->client->addPort(kEnginePortTypeAudio, portName, false, 1);
265 pData->audioOut.ports[1].rindex = 1;
267 // ---------------------------------------
268 // Event Input
270 portName.clear();
272 if (processMode == ENGINE_PROCESS_MODE_SINGLE_CLIENT)
274 portName = pData->name;
275 portName += ":";
278 portName += "events-in";
279 portName.truncate(portNameSize);
281 pData->event.portIn = (CarlaEngineEventPort*)pData->client->addPort(kEnginePortTypeEvent, portName, true, 0);
283 // ---------------------------------------
284 // Parameters
286 pData->param.data[0].type = PARAMETER_OUTPUT;
287 pData->param.data[0].hints = PARAMETER_IS_ENABLED | PARAMETER_IS_AUTOMATABLE | PARAMETER_IS_INTEGER;
288 pData->param.data[0].index = 0;
289 pData->param.data[0].rindex = 0;
290 pData->param.ranges[0].min = 0.0f;
291 pData->param.ranges[0].max = 128;
292 pData->param.ranges[0].def = 0.0f;
293 pData->param.ranges[0].step = 1.0f;
294 pData->param.ranges[0].stepSmall = 1.0f;
295 pData->param.ranges[0].stepLarge = 1.0f;
297 // ---------------------------------------
299 // plugin hints
300 pData->hints = 0x0;
301 pData->hints |= PLUGIN_IS_SYNTH;
302 pData->hints |= PLUGIN_CAN_VOLUME;
303 pData->hints |= PLUGIN_CAN_BALANCE;
305 // extra plugin hints
306 pData->extraHints = 0x0;
307 pData->extraHints |= PLUGIN_EXTRA_HINT_HAS_MIDI_IN;
309 bufferSizeChanged(pData->engine->getBufferSize());
310 reloadPrograms(true);
312 if (pData->active)
313 activate();
315 carla_debug("CarlaPluginSFZero::reload() - end");
318 // -------------------------------------------------------------------
319 // Plugin processing
321 void process(const float* const* const, float** const audioOut, const float* const*, float**, const uint32_t frames) override
323 // --------------------------------------------------------------------------------------------------------
324 // Check if active
326 if (! pData->active)
328 // disable any output sound
329 for (uint32_t i=0; i < pData->audioOut.count; ++i)
330 carla_zeroFloats(audioOut[i], frames);
332 fNumVoices = 0.0f;
333 return;
336 // --------------------------------------------------------------------------------------------------------
337 // Check if needs reset
339 if (pData->needsReset)
341 fSynth.allNotesOff(0, false);
342 pData->needsReset = false;
345 // --------------------------------------------------------------------------------------------------------
346 // Event Input and Processing
349 // ----------------------------------------------------------------------------------------------------
350 // Setup audio buffer
352 AudioSampleBuffer audioOutBuffer(audioOut, 2, frames);
354 // ----------------------------------------------------------------------------------------------------
355 // MIDI Input (External)
357 if (pData->extNotes.mutex.tryLock())
359 for (RtLinkedList<ExternalMidiNote>::Itenerator it = pData->extNotes.data.begin2(); it.valid(); it.next())
361 const ExternalMidiNote& note(it.getValue(kExternalMidiNoteFallback));
362 CARLA_SAFE_ASSERT_CONTINUE(note.channel >= 0 && note.channel < MAX_MIDI_CHANNELS);
364 if (note.velo > 0)
365 fSynth.noteOn(note.channel+1, note.note, static_cast<float>(note.velo)/127.0f);
366 else
367 fSynth.noteOff(note.channel+1, note.note, static_cast<float>(note.velo)/127.0f, true);
370 pData->extNotes.data.clear();
371 pData->extNotes.mutex.unlock();
373 } // End of MIDI Input (External)
375 // ----------------------------------------------------------------------------------------------------
376 // Event Input (System)
378 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
379 bool allNotesOffSent = false;
380 #endif
381 uint32_t timeOffset = 0;
383 for (uint32_t i=0, numEvents=pData->event.portIn->getEventCount(); i < numEvents; ++i)
385 const EngineEvent& event(pData->event.portIn->getEvent(i));
387 uint32_t eventTime = event.time;
388 CARLA_SAFE_ASSERT_UINT2_CONTINUE(eventTime < frames, eventTime, frames);
390 if (eventTime < timeOffset)
392 carla_stderr2("Timing error, eventTime:%u < timeOffset:%u for '%s'",
393 eventTime, timeOffset, pData->name);
394 eventTime = timeOffset;
396 else if (eventTime > timeOffset)
398 if (processSingle(audioOutBuffer, eventTime - timeOffset, timeOffset))
399 timeOffset = eventTime;
402 // Control change
403 switch (event.type)
405 case kEngineEventTypeNull:
406 break;
408 case kEngineEventTypeControl:
410 const EngineControlEvent& ctrlEvent = event.ctrl;
412 switch (ctrlEvent.type)
414 case kEngineControlEventTypeNull:
415 break;
417 case kEngineControlEventTypeParameter:
419 float value;
421 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
422 // Control backend stuff
423 if (event.channel == pData->ctrlChannel)
425 if (MIDI_IS_CONTROL_BREATH_CONTROLLER(ctrlEvent.param) && (pData->hints & PLUGIN_CAN_DRYWET) != 0)
427 value = ctrlEvent.normalizedValue;
428 setDryWetRT(value, true);
431 if (MIDI_IS_CONTROL_CHANNEL_VOLUME(ctrlEvent.param) && (pData->hints & PLUGIN_CAN_VOLUME) != 0)
433 value = ctrlEvent.normalizedValue*127.0f/100.0f;
434 setVolumeRT(value, true);
437 if (MIDI_IS_CONTROL_BALANCE(ctrlEvent.param) && (pData->hints & PLUGIN_CAN_BALANCE) != 0)
439 float left, right;
440 value = ctrlEvent.normalizedValue/0.5f - 1.0f;
442 if (value < 0.0f)
444 left = -1.0f;
445 right = (value*2.0f)+1.0f;
447 else if (value > 0.0f)
449 left = (value*2.0f)-1.0f;
450 right = 1.0f;
452 else
454 left = -1.0f;
455 right = 1.0f;
458 setBalanceLeftRT(left, true);
459 setBalanceRightRT(right, true);
462 #endif
463 // Control plugin parameters
464 for (uint32_t k=0; k < pData->param.count; ++k)
466 if (pData->param.data[k].midiChannel != event.channel)
467 continue;
468 if (pData->param.data[k].mappedControlIndex != ctrlEvent.param)
469 continue;
470 if (pData->param.data[k].hints != PARAMETER_INPUT)
471 continue;
472 if ((pData->param.data[k].hints & PARAMETER_IS_AUTOMATABLE) == 0)
473 continue;
475 value = pData->param.getFinalUnnormalizedValue(k, ctrlEvent.normalizedValue);
476 setParameterValueRT(k, value, eventTime, true);
479 if ((pData->options & PLUGIN_OPTION_SEND_CONTROL_CHANGES) != 0 && ctrlEvent.param < MAX_MIDI_VALUE)
481 fSynth.handleController(event.channel+1, ctrlEvent.param, int(ctrlEvent.normalizedValue*127.0f + 0.5f));
484 break;
487 case kEngineControlEventTypeMidiBank:
488 case kEngineControlEventTypeMidiProgram:
489 case kEngineControlEventTypeAllSoundOff:
490 break;
492 case kEngineControlEventTypeAllNotesOff:
493 if (pData->options & PLUGIN_OPTION_SEND_ALL_SOUND_OFF)
495 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
496 if (event.channel == pData->ctrlChannel && ! allNotesOffSent)
498 allNotesOffSent = true;
499 postponeRtAllNotesOff();
501 #endif
503 fSynth.allNotesOff(event.channel+1, true);
505 break;
507 break;
510 case kEngineEventTypeMidi: {
511 const EngineMidiEvent& midiEvent(event.midi);
513 if (midiEvent.size > EngineMidiEvent::kDataSize)
514 continue;
516 uint8_t status = uint8_t(MIDI_GET_STATUS_FROM_DATA(midiEvent.data));
518 if ((status == MIDI_STATUS_NOTE_OFF || status == MIDI_STATUS_NOTE_ON) && (pData->options & PLUGIN_OPTION_SKIP_SENDING_NOTES))
519 continue;
520 if (status == MIDI_STATUS_CHANNEL_PRESSURE && (pData->options & PLUGIN_OPTION_SEND_CHANNEL_PRESSURE) == 0)
521 continue;
522 if (status == MIDI_STATUS_CONTROL_CHANGE && (pData->options & PLUGIN_OPTION_SEND_CONTROL_CHANGES) == 0)
523 continue;
524 if (status == MIDI_STATUS_POLYPHONIC_AFTERTOUCH && (pData->options & PLUGIN_OPTION_SEND_NOTE_AFTERTOUCH) == 0)
525 continue;
526 if (status == MIDI_STATUS_PITCH_WHEEL_CONTROL && (pData->options & PLUGIN_OPTION_SEND_PITCHBEND) == 0)
527 continue;
529 // Fix bad note-off
530 if (status == MIDI_STATUS_NOTE_ON && midiEvent.data[2] == 0)
531 status = MIDI_STATUS_NOTE_OFF;
533 // put back channel in data
534 uint8_t midiData2[EngineMidiEvent::kDataSize];
535 midiData2[0] = uint8_t(status | (event.channel & MIDI_CHANNEL_BIT));
536 std::memcpy(midiData2 + 1, midiEvent.data + 1, static_cast<std::size_t>(midiEvent.size - 1));
538 const MidiMessage midiMessage(midiData2, static_cast<int>(midiEvent.size), 0.0);
540 fSynth.handleMidiEvent(midiMessage);
542 if (status == MIDI_STATUS_NOTE_ON)
544 pData->postponeNoteOnRtEvent(true, event.channel, midiEvent.data[1], midiEvent.data[2]);
546 else if (status == MIDI_STATUS_NOTE_OFF)
548 pData->postponeNoteOffRtEvent(true, event.channel, midiEvent.data[1]);
550 } break;
554 pData->postRtEvents.trySplice();
556 if (frames > timeOffset)
557 processSingle(audioOutBuffer, frames - timeOffset, timeOffset);
559 } // End of Event Input and Processing
561 // --------------------------------------------------------------------------------------------------------
562 // Parameter outputs
564 fNumVoices = static_cast<float>(fSynth.numVoicesUsed());
567 bool processSingle(AudioSampleBuffer& audioOutBuffer, const uint32_t frames, const uint32_t timeOffset)
569 CARLA_SAFE_ASSERT_RETURN(frames > 0, false);
571 // --------------------------------------------------------------------------------------------------------
572 // Try lock, silence otherwise
574 #ifndef STOAT_TEST_BUILD
575 if (pData->engine->isOffline())
577 pData->singleMutex.lock();
579 else
580 #endif
581 if (! pData->singleMutex.tryLock())
583 audioOutBuffer.clear(timeOffset, frames);
584 return false;
587 // --------------------------------------------------------------------------------------------------------
588 // Run plugin
590 fSynth.renderVoices(audioOutBuffer, static_cast<int>(timeOffset), static_cast<int>(frames));
592 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
593 // --------------------------------------------------------------------------------------------------------
594 // Post-processing (dry/wet, volume and balance)
597 const bool doVolume = carla_isNotEqual(pData->postProc.volume, 1.0f);
598 //const bool doBalance = carla_isNotEqual(pData->postProc.balanceLeft, -1.0f) || carla_isNotEqual(pData->postProc.balanceRight, 1.0f);
600 float* outBufferL = audioOutBuffer.getWritePointer(0, timeOffset);
601 float* outBufferR = audioOutBuffer.getWritePointer(1, timeOffset);
603 #if 0
604 if (doBalance)
606 float* const oldBufLeft = pData->postProc.extraBuffer;
608 // there was a loop here
610 if (i % 2 == 0)
611 carla_copyFloats(oldBufLeft, outBuffer[i], frames);
613 float balRangeL = (pData->postProc.balanceLeft + 1.0f)/2.0f;
614 float balRangeR = (pData->postProc.balanceRight + 1.0f)/2.0f;
616 for (uint32_t k=0; k < frames; ++k)
618 if (i % 2 == 0)
620 // left
621 outBuffer[i][k] = oldBufLeft[k] * (1.0f - balRangeL);
622 outBuffer[i][k] += outBuffer[i+1][k] * (1.0f - balRangeR);
624 else
626 // right
627 outBuffer[i][k] = outBuffer[i][k] * balRangeR;
628 outBuffer[i][k] += oldBufLeft[k] * balRangeL;
633 #endif
635 if (doVolume)
637 const float volume = pData->postProc.volume;
639 for (uint32_t k=0; k < frames; ++k)
641 *outBufferL++ *= volume;
642 *outBufferR++ *= volume;
646 } // End of Post-processing
647 #endif
649 // --------------------------------------------------------------------------------------------------------
651 pData->singleMutex.unlock();
652 return true;
655 void sampleRateChanged(const double newSampleRate) override
657 fSynth.setCurrentPlaybackSampleRate(newSampleRate);
660 // -------------------------------------------------------------------
661 // Plugin buffers
663 // nothing
665 // -------------------------------------------------------------------
667 bool init(const CarlaPluginPtr plugin,
668 const char* const filename, const char* const name, const char* const label, const uint options)
670 CARLA_SAFE_ASSERT_RETURN(pData->engine != nullptr, false);
672 // ---------------------------------------------------------------
673 // first checks
675 if (pData->client != nullptr)
677 pData->engine->setLastError("Plugin client is already registered");
678 return false;
681 if (filename == nullptr || filename[0] == '\0')
683 pData->engine->setLastError("null filename");
684 return false;
687 for (int i = 128; --i >=0;)
688 fSynth.addVoice(new sfzero::Voice());
690 // ---------------------------------------------------------------
691 // Init SFZero stuff
693 fSynth.setCurrentPlaybackSampleRate(pData->engine->getSampleRate());
695 File file(filename);
696 sfzero::Sound* const sound = new sfzero::Sound(file);
698 sfzero::Sound::LoadingIdleCallback cb = {
699 loadingIdleCallbackFunction,
700 pData->engine,
703 sound->loadRegions();
704 sound->loadSamples(cb);
706 if (fSynth.addSound(sound) == nullptr)
708 pData->engine->setLastError("Failed to allocate SFZ sounds in memory");
709 return false;
712 sound->dumpToConsole();
714 // ---------------------------------------------------------------
716 const String basename(File(filename).getFileNameWithoutExtension());
718 CarlaString label2(label != nullptr ? label : basename.toRawUTF8());
720 fLabel = label2.dup();
721 fRealName = carla_strdup(basename.toRawUTF8());
723 pData->filename = carla_strdup(filename);
725 if (name != nullptr && name[0] != '\0')
726 pData->name = pData->engine->getUniquePluginName(name);
727 else if (fRealName[0] != '\0')
728 pData->name = pData->engine->getUniquePluginName(fRealName);
729 else
730 pData->name = pData->engine->getUniquePluginName(fLabel);
732 // ---------------------------------------------------------------
733 // register client
735 pData->client = pData->engine->addClient(plugin);
737 if (pData->client == nullptr || ! pData->client->isOk())
739 pData->engine->setLastError("Failed to register plugin client");
740 return false;
743 // ---------------------------------------------------------------
744 // set options
746 pData->options = 0x0;
748 if (isPluginOptionEnabled(options, PLUGIN_OPTION_SEND_CONTROL_CHANGES))
749 pData->options |= PLUGIN_OPTION_SEND_CONTROL_CHANGES;
750 if (isPluginOptionEnabled(options, PLUGIN_OPTION_SEND_CHANNEL_PRESSURE))
751 pData->options |= PLUGIN_OPTION_SEND_CHANNEL_PRESSURE;
752 if (isPluginOptionEnabled(options, PLUGIN_OPTION_SEND_NOTE_AFTERTOUCH))
753 pData->options |= PLUGIN_OPTION_SEND_NOTE_AFTERTOUCH;
754 if (isPluginOptionEnabled(options, PLUGIN_OPTION_SEND_PITCHBEND))
755 pData->options |= PLUGIN_OPTION_SEND_PITCHBEND;
756 if (isPluginOptionEnabled(options, PLUGIN_OPTION_SEND_ALL_SOUND_OFF))
757 pData->options |= PLUGIN_OPTION_SEND_ALL_SOUND_OFF;
758 if (isPluginOptionInverseEnabled(options, PLUGIN_OPTION_SKIP_SENDING_NOTES))
759 pData->options |= PLUGIN_OPTION_SKIP_SENDING_NOTES;
761 return true;
764 // -------------------------------------------------------------------
766 private:
767 sfzero::Synth fSynth;
768 float fNumVoices;
770 const char* fLabel;
771 const char* fRealName;
773 CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CarlaPluginSFZero)
776 CARLA_BACKEND_END_NAMESPACE
778 #endif // HAVE_SFZ
780 CARLA_BACKEND_START_NAMESPACE
782 // -------------------------------------------------------------------------------------------------------------------
784 CarlaPluginPtr CarlaPlugin::newSFZero(const Initializer& init)
786 carla_debug("CarlaPluginSFZero::newSFZero({%p, \"%s\", \"%s\", \"%s\", " P_INT64 "})",
787 init.engine, init.filename, init.name, init.label, init.uniqueId);
789 #ifdef HAVE_SFZ
790 // -------------------------------------------------------------------
791 // Check if file exists
793 if (! water::File(init.filename).existsAsFile())
795 init.engine->setLastError("Requested file is not valid or does not exist");
796 return nullptr;
799 std::shared_ptr<CarlaPluginSFZero> plugin(new CarlaPluginSFZero(init.engine, init.id));
801 if (! plugin->init(plugin, init.filename, init.name, init.label, init.options))
802 return nullptr;
804 return plugin;
805 #else
806 init.engine->setLastError("SFZ support not available");
807 return nullptr;
808 #endif
811 // -------------------------------------------------------------------------------------------------------------------
813 CARLA_BACKEND_END_NAMESPACE