1 /*
2  * Carla SFZero Plugin
3  * Copyright (C) 2018-2020 Filipe Coelho <falktx@falktx.com>
4  *
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.
9  *
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.
14  *
15  * For a full copy of the GNU General Public License see the doc/GPL.txt file.
16  */
17 
18 #include "CarlaPluginInternal.hpp"
19 #include "CarlaBackendUtils.hpp"
20 #include "CarlaEngine.hpp"
21 
22 #include "sfzero/SFZero.h"
23 
24 #include "water/buffers/AudioSampleBuffer.h"
25 #include "water/files/File.h"
26 #include "water/midi/MidiMessage.h"
27 
28 using water::AudioSampleBuffer;
29 using water::File;
30 using water::MidiMessage;
31 using water::String;
32 
33 // -----------------------------------------------------------------------
34 
35 CARLA_BACKEND_START_NAMESPACE
36 
37 // -------------------------------------------------------------------------------------------------------------------
38 // Fallback data
39 
40 static const ExternalMidiNote kExternalMidiNoteFallback = { -1, 0, 0 };
41 
42 
loadingIdleCallbackFunction(void * ptr)43 static void loadingIdleCallbackFunction(void* ptr)
44 {
45     ((CarlaEngine*)ptr)->callback(true, false, ENGINE_CALLBACK_IDLE, 0, 0, 0, 0, 0.0f, nullptr);
46 }
47 
48 // -------------------------------------------------------------------------------------------------------------------
49 
50 class CarlaPluginSFZero : public CarlaPlugin
51 {
52 public:
CarlaPluginSFZero(CarlaEngine * const engine,const uint id)53     CarlaPluginSFZero(CarlaEngine* const engine, const uint id)
54         : CarlaPlugin(engine, id),
55           fSynth(),
56           fNumVoices(0.0f),
57           fLabel(nullptr),
58           fRealName(nullptr)
59     {
60         carla_debug("CarlaPluginSFZero::CarlaPluginSFZero(%p, %i)", engine, id);
61     }
62 
~CarlaPluginSFZero()63     ~CarlaPluginSFZero() override
64     {
65         carla_debug("CarlaPluginSFZero::~CarlaPluginSFZero()");
66 
67         pData->singleMutex.lock();
68         pData->masterMutex.lock();
69 
70         if (pData->client != nullptr && pData->client->isActive())
71             pData->client->deactivate(true);
72 
73         if (pData->active)
74         {
75             deactivate();
76             pData->active = false;
77         }
78 
79         if (fLabel != nullptr)
80         {
81             delete[] fLabel;
82             fLabel = nullptr;
83         }
84 
85         if (fRealName != nullptr)
86         {
87             delete[] fRealName;
88             fRealName = nullptr;
89         }
90 
91         clearBuffers();
92     }
93 
94     // -------------------------------------------------------------------
95     // Information (base)
96 
getType() const97     PluginType getType() const noexcept override
98     {
99         return PLUGIN_SFZ;
100     }
101 
getCategory() const102     PluginCategory getCategory() const noexcept override
103     {
104         return PLUGIN_CATEGORY_SYNTH;
105     }
106 
107     // -------------------------------------------------------------------
108     // Information (count)
109 
110     // nothing
111 
112     // -------------------------------------------------------------------
113     // Information (current data)
114 
115     // nothing
116 
117     // -------------------------------------------------------------------
118     // Information (per-plugin data)
119 
getOptionsAvailable() const120     uint getOptionsAvailable() const noexcept override
121     {
122         uint options = 0x0;
123 
124         options |= PLUGIN_OPTION_SEND_CONTROL_CHANGES;
125         options |= PLUGIN_OPTION_SEND_CHANNEL_PRESSURE;
126         options |= PLUGIN_OPTION_SEND_NOTE_AFTERTOUCH;
127         options |= PLUGIN_OPTION_SEND_PITCHBEND;
128         options |= PLUGIN_OPTION_SEND_ALL_SOUND_OFF;
129         options |= PLUGIN_OPTION_SKIP_SENDING_NOTES;
130 
131         return options;
132     }
133 
getParameterValue(const uint32_t parameterId) const134     float getParameterValue(const uint32_t parameterId) const noexcept override
135     {
136         CARLA_SAFE_ASSERT_RETURN(parameterId == 0, 0.0f);
137 
138         return fNumVoices;
139     }
140 
getLabel(char * const strBuf) const141     bool getLabel(char* const strBuf) const noexcept override
142     {
143         if (fLabel != nullptr)
144         {
145             std::strncpy(strBuf, fLabel, STR_MAX);
146             return true;
147         }
148 
149         return CarlaPlugin::getLabel(strBuf);
150     }
151 
getMaker(char * const strBuf) const152     bool getMaker(char* const strBuf) const noexcept override
153     {
154         std::strncpy(strBuf, "SFZero engine", STR_MAX);
155         return true;
156     }
157 
getCopyright(char * const strBuf) const158     bool getCopyright(char* const strBuf) const noexcept override
159     {
160         std::strncpy(strBuf, "ISC", STR_MAX);
161         return true;
162     }
163 
getRealName(char * const strBuf) const164     bool getRealName(char* const strBuf) const noexcept override
165     {
166         if (fRealName != nullptr)
167         {
168             std::strncpy(strBuf, fRealName, STR_MAX);
169             return true;
170         }
171 
172         return CarlaPlugin::getRealName(strBuf);
173     }
174 
getParameterName(const uint32_t parameterId,char * const strBuf) const175     bool getParameterName(const uint32_t parameterId, char* const strBuf) const noexcept override
176     {
177         CARLA_SAFE_ASSERT_RETURN(parameterId == 0, false);
178 
179         std::strncpy(strBuf, "Voice Count", STR_MAX);
180         return true;
181     }
182 
183     // -------------------------------------------------------------------
184     // Set data (state)
185 
186     // nothing
187 
188     // -------------------------------------------------------------------
189     // Set data (internal stuff)
190 
191     // nothing
192 
193     // -------------------------------------------------------------------
194     // Set data (plugin-specific stuff)
195 
196     // nothing
197 
198     // -------------------------------------------------------------------
199     // Set ui stuff
200 
201     // nothing
202 
203     // -------------------------------------------------------------------
204     // Plugin state
205 
reload()206     void reload() override
207     {
208         CARLA_SAFE_ASSERT_RETURN(pData->engine != nullptr,);
209         carla_debug("CarlaPluginSFZero::reload() - start");
210 
211         const EngineProcessMode processMode(pData->engine->getProccessMode());
212 
213         // Safely disable plugin for reload
214         const ScopedDisabler sd(this);
215 
216         if (pData->active)
217             deactivate();
218 
219         clearBuffers();
220 
221         pData->audioOut.createNew(2);
222         pData->param.createNew(1, false);
223 
224         const uint portNameSize(pData->engine->getMaxPortNameSize());
225         CarlaString portName;
226 
227         // ---------------------------------------
228         // Audio Outputs
229 
230         // out-left
231         portName.clear();
232 
233         if (processMode == ENGINE_PROCESS_MODE_SINGLE_CLIENT)
234         {
235             portName  = pData->name;
236             portName += ":";
237         }
238 
239         portName += "out-left";
240         portName.truncate(portNameSize);
241 
242         pData->audioOut.ports[0].port   = (CarlaEngineAudioPort*)pData->client->addPort(kEnginePortTypeAudio, portName, false, 0);
243         pData->audioOut.ports[0].rindex = 0;
244 
245         // out-right
246         portName.clear();
247 
248         if (processMode == ENGINE_PROCESS_MODE_SINGLE_CLIENT)
249         {
250             portName  = pData->name;
251             portName += ":";
252         }
253 
254         portName += "out-right";
255         portName.truncate(portNameSize);
256 
257         pData->audioOut.ports[1].port   = (CarlaEngineAudioPort*)pData->client->addPort(kEnginePortTypeAudio, portName, false, 1);
258         pData->audioOut.ports[1].rindex = 1;
259 
260         // ---------------------------------------
261         // Event Input
262 
263         portName.clear();
264 
265         if (processMode == ENGINE_PROCESS_MODE_SINGLE_CLIENT)
266         {
267             portName  = pData->name;
268             portName += ":";
269         }
270 
271         portName += "events-in";
272         portName.truncate(portNameSize);
273 
274         pData->event.portIn = (CarlaEngineEventPort*)pData->client->addPort(kEnginePortTypeEvent, portName, true, 0);
275 
276         // ---------------------------------------
277         // Parameters
278 
279         pData->param.data[0].type   = PARAMETER_OUTPUT;
280         pData->param.data[0].hints  = PARAMETER_IS_ENABLED | PARAMETER_IS_AUTOMABLE | PARAMETER_IS_INTEGER;
281         pData->param.data[0].index  = 0;
282         pData->param.data[0].rindex = 0;
283         pData->param.ranges[0].min = 0.0f;
284         pData->param.ranges[0].max = 128;
285         pData->param.ranges[0].def = 0.0f;
286         pData->param.ranges[0].step = 1.0f;
287         pData->param.ranges[0].stepSmall = 1.0f;
288         pData->param.ranges[0].stepLarge = 1.0f;
289 
290         // ---------------------------------------
291 
292         // plugin hints
293         pData->hints  = 0x0;
294         pData->hints |= PLUGIN_IS_SYNTH;
295         pData->hints |= PLUGIN_CAN_VOLUME;
296         pData->hints |= PLUGIN_CAN_BALANCE;
297 
298         // extra plugin hints
299         pData->extraHints  = 0x0;
300         pData->extraHints |= PLUGIN_EXTRA_HINT_HAS_MIDI_IN;
301 
302         bufferSizeChanged(pData->engine->getBufferSize());
303         reloadPrograms(true);
304 
305         if (pData->active)
306             activate();
307 
308         carla_debug("CarlaPluginSFZero::reload() - end");
309     }
310 
311     // -------------------------------------------------------------------
312     // Plugin processing
313 
process(const float * const * const,float ** const audioOut,const float * const *,float **,const uint32_t frames)314     void process(const float* const* const, float** const audioOut, const float* const*, float**, const uint32_t frames) override
315     {
316         // --------------------------------------------------------------------------------------------------------
317         // Check if active
318 
319         if (! pData->active)
320         {
321             // disable any output sound
322             for (uint32_t i=0; i < pData->audioOut.count; ++i)
323                 carla_zeroFloats(audioOut[i], frames);
324 
325             fNumVoices = 0.0f;
326             return;
327         }
328 
329         // --------------------------------------------------------------------------------------------------------
330         // Check if needs reset
331 
332         if (pData->needsReset)
333         {
334             fSynth.allNotesOff(0, false);
335             pData->needsReset = false;
336         }
337 
338         // --------------------------------------------------------------------------------------------------------
339         // Event Input and Processing
340 
341         {
342             // ----------------------------------------------------------------------------------------------------
343             // Setup audio buffer
344 
345             AudioSampleBuffer audioOutBuffer(audioOut, 2, frames);
346 
347             // ----------------------------------------------------------------------------------------------------
348             // MIDI Input (External)
349 
350             if (pData->extNotes.mutex.tryLock())
351             {
352                 for (RtLinkedList<ExternalMidiNote>::Itenerator it = pData->extNotes.data.begin2(); it.valid(); it.next())
353                 {
354                     const ExternalMidiNote& note(it.getValue(kExternalMidiNoteFallback));
355                     CARLA_SAFE_ASSERT_CONTINUE(note.channel >= 0 && note.channel < MAX_MIDI_CHANNELS);
356 
357                     if (note.velo > 0)
358                         fSynth.noteOn(note.channel+1, note.note, static_cast<float>(note.velo)/127.0f);
359                     else
360                         fSynth.noteOff(note.channel+1, note.note, static_cast<float>(note.velo)/127.0f, true);
361                 }
362 
363                 pData->extNotes.data.clear();
364                 pData->extNotes.mutex.unlock();
365 
366             } // End of MIDI Input (External)
367 
368             // ----------------------------------------------------------------------------------------------------
369             // Event Input (System)
370 
371 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
372             bool allNotesOffSent = false;
373 #endif
374             uint32_t timeOffset = 0;
375 
376             for (uint32_t i=0, numEvents=pData->event.portIn->getEventCount(); i < numEvents; ++i)
377             {
378                 const EngineEvent& event(pData->event.portIn->getEvent(i));
379 
380                 uint32_t eventTime = event.time;
381                 CARLA_SAFE_ASSERT_UINT2_CONTINUE(eventTime < frames, eventTime, frames);
382 
383                 if (eventTime < timeOffset)
384                 {
385                     carla_stderr2("Timing error, eventTime:%u < timeOffset:%u for '%s'",
386                                   eventTime, timeOffset, pData->name);
387                     eventTime = timeOffset;
388                 }
389                 else if (eventTime > timeOffset)
390                 {
391                     if (processSingle(audioOutBuffer, eventTime - timeOffset, timeOffset))
392                         timeOffset = eventTime;
393                 }
394 
395                 // Control change
396                 switch (event.type)
397                 {
398                 case kEngineEventTypeNull:
399                     break;
400 
401                 case kEngineEventTypeControl:
402                 {
403                     const EngineControlEvent& ctrlEvent = event.ctrl;
404 
405                     switch (ctrlEvent.type)
406                     {
407                     case kEngineControlEventTypeNull:
408                         break;
409 
410                     case kEngineControlEventTypeParameter:
411                     {
412                         float value;
413 
414 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
415                         // Control backend stuff
416                         if (event.channel == pData->ctrlChannel)
417                         {
418                             if (MIDI_IS_CONTROL_BREATH_CONTROLLER(ctrlEvent.param) && (pData->hints & PLUGIN_CAN_DRYWET) != 0)
419                             {
420                                 value = ctrlEvent.normalizedValue;
421                                 setDryWetRT(value, true);
422                             }
423 
424                             if (MIDI_IS_CONTROL_CHANNEL_VOLUME(ctrlEvent.param) && (pData->hints & PLUGIN_CAN_VOLUME) != 0)
425                             {
426                                 value = ctrlEvent.normalizedValue*127.0f/100.0f;
427                                 setVolumeRT(value, true);
428                             }
429 
430                             if (MIDI_IS_CONTROL_BALANCE(ctrlEvent.param) && (pData->hints & PLUGIN_CAN_BALANCE) != 0)
431                             {
432                                 float left, right;
433                                 value = ctrlEvent.normalizedValue/0.5f - 1.0f;
434 
435                                 if (value < 0.0f)
436                                 {
437                                     left  = -1.0f;
438                                     right = (value*2.0f)+1.0f;
439                                 }
440                                 else if (value > 0.0f)
441                                 {
442                                     left  = (value*2.0f)-1.0f;
443                                     right = 1.0f;
444                                 }
445                                 else
446                                 {
447                                     left  = -1.0f;
448                                     right = 1.0f;
449                                 }
450 
451                                 setBalanceLeftRT(left, true);
452                                 setBalanceRightRT(right, true);
453                             }
454                         }
455 #endif
456                         // Control plugin parameters
457                         for (uint32_t k=0; k < pData->param.count; ++k)
458                         {
459                             if (pData->param.data[k].midiChannel != event.channel)
460                                 continue;
461                             if (pData->param.data[k].mappedControlIndex != ctrlEvent.param)
462                                 continue;
463                             if (pData->param.data[k].hints != PARAMETER_INPUT)
464                                 continue;
465                             if ((pData->param.data[k].hints & PARAMETER_IS_AUTOMABLE) == 0)
466                                 continue;
467 
468                             value = pData->param.getFinalUnnormalizedValue(k, ctrlEvent.normalizedValue);
469                             setParameterValueRT(k, value, true);
470                         }
471 
472                         if ((pData->options & PLUGIN_OPTION_SEND_CONTROL_CHANGES) != 0 && ctrlEvent.param < MAX_MIDI_VALUE)
473                         {
474                             fSynth.handleController(event.channel+1, ctrlEvent.param, int(ctrlEvent.normalizedValue*127.0f));
475                         }
476 
477                         break;
478                     }
479 
480                     case kEngineControlEventTypeMidiBank:
481                     case kEngineControlEventTypeMidiProgram:
482                     case kEngineControlEventTypeAllSoundOff:
483                         break;
484 
485                     case kEngineControlEventTypeAllNotesOff:
486                         if (pData->options & PLUGIN_OPTION_SEND_ALL_SOUND_OFF)
487                         {
488 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
489                             if (event.channel == pData->ctrlChannel && ! allNotesOffSent)
490                             {
491                                 allNotesOffSent = true;
492                                 postponeRtAllNotesOff();
493                             }
494 #endif
495 
496                             fSynth.allNotesOff(event.channel+1, true);
497                         }
498                         break;
499                     }
500                     break;
501                 }
502 
503                 case kEngineEventTypeMidi: {
504                     const EngineMidiEvent& midiEvent(event.midi);
505 
506                     const uint8_t* const midiData(midiEvent.size > EngineMidiEvent::kDataSize ? midiEvent.dataExt : midiEvent.data);
507 
508                     uint8_t status = uint8_t(MIDI_GET_STATUS_FROM_DATA(midiData));
509 
510                     if ((status == MIDI_STATUS_NOTE_OFF || status == MIDI_STATUS_NOTE_ON) && (pData->options & PLUGIN_OPTION_SKIP_SENDING_NOTES))
511                         continue;
512                     if (status == MIDI_STATUS_CHANNEL_PRESSURE && (pData->options & PLUGIN_OPTION_SEND_CHANNEL_PRESSURE) == 0)
513                         continue;
514                     if (status == MIDI_STATUS_CONTROL_CHANGE && (pData->options & PLUGIN_OPTION_SEND_CONTROL_CHANGES) == 0)
515                         continue;
516                     if (status == MIDI_STATUS_POLYPHONIC_AFTERTOUCH && (pData->options & PLUGIN_OPTION_SEND_NOTE_AFTERTOUCH) == 0)
517                         continue;
518                     if (status == MIDI_STATUS_PITCH_WHEEL_CONTROL && (pData->options & PLUGIN_OPTION_SEND_PITCHBEND) == 0)
519                         continue;
520 
521                     // Fix bad note-off
522                     if (status == MIDI_STATUS_NOTE_ON && midiData[2] == 0)
523                         status = MIDI_STATUS_NOTE_OFF;
524 
525                     // put back channel in data
526                     uint8_t midiData2[midiEvent.size];
527                     midiData2[0] = uint8_t(status | (event.channel & MIDI_CHANNEL_BIT));
528                     std::memcpy(midiData2+1, midiData+1, static_cast<std::size_t>(midiEvent.size-1));
529 
530                     const MidiMessage midiMessage(midiData2, static_cast<int>(midiEvent.size), 0.0);
531 
532                     fSynth.handleMidiEvent(midiMessage);
533 
534                     if (status == MIDI_STATUS_NOTE_ON)
535                     {
536                         pData->postponeNoteOnRtEvent(true, event.channel, midiData[1], midiData[2]);
537                     }
538                     else if (status == MIDI_STATUS_NOTE_OFF)
539                     {
540                         pData->postponeNoteOffRtEvent(true, event.channel, midiData[1]);
541                     }
542                 } break;
543                 }
544             }
545 
546             pData->postRtEvents.trySplice();
547 
548             if (frames > timeOffset)
549                 processSingle(audioOutBuffer, frames - timeOffset, timeOffset);
550 
551         } // End of Event Input and Processing
552 
553         // --------------------------------------------------------------------------------------------------------
554         // Parameter outputs
555 
556         fNumVoices = static_cast<float>(fSynth.numVoicesUsed());
557     }
558 
processSingle(AudioSampleBuffer & audioOutBuffer,const uint32_t frames,const uint32_t timeOffset)559     bool processSingle(AudioSampleBuffer& audioOutBuffer, const uint32_t frames, const uint32_t timeOffset)
560     {
561         CARLA_SAFE_ASSERT_RETURN(frames > 0, false);
562 
563         // --------------------------------------------------------------------------------------------------------
564         // Try lock, silence otherwise
565 
566 #ifndef STOAT_TEST_BUILD
567         if (pData->engine->isOffline())
568         {
569             pData->singleMutex.lock();
570         }
571         else
572 #endif
573         if (! pData->singleMutex.tryLock())
574         {
575             audioOutBuffer.clear(timeOffset, frames);
576             return false;
577         }
578 
579         // --------------------------------------------------------------------------------------------------------
580         // Run plugin
581 
582         fSynth.renderVoices(audioOutBuffer, static_cast<int>(timeOffset), static_cast<int>(frames));
583 
584 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
585         // --------------------------------------------------------------------------------------------------------
586         // Post-processing (dry/wet, volume and balance)
587 
588         {
589             const bool doVolume  = carla_isNotEqual(pData->postProc.volume, 1.0f);
590             //const bool doBalance = carla_isNotEqual(pData->postProc.balanceLeft, -1.0f) || carla_isNotEqual(pData->postProc.balanceRight, 1.0f);
591 
592             float* outBufferL = audioOutBuffer.getWritePointer(0, timeOffset);
593             float* outBufferR = audioOutBuffer.getWritePointer(1, timeOffset);
594 
595 #if 0
596             if (doBalance)
597             {
598                 float oldBufLeft[frames];
599 
600                 // there was a loop here
601                 {
602                     if (i % 2 == 0)
603                         carla_copyFloats(oldBufLeft, outBuffer[i], frames);
604 
605                     float balRangeL = (pData->postProc.balanceLeft  + 1.0f)/2.0f;
606                     float balRangeR = (pData->postProc.balanceRight + 1.0f)/2.0f;
607 
608                     for (uint32_t k=0; k < frames; ++k)
609                     {
610                         if (i % 2 == 0)
611                         {
612                             // left
613                             outBuffer[i][k]  = oldBufLeft[k]     * (1.0f - balRangeL);
614                             outBuffer[i][k] += outBuffer[i+1][k] * (1.0f - balRangeR);
615                         }
616                         else
617                         {
618                             // right
619                             outBuffer[i][k]  = outBuffer[i][k] * balRangeR;
620                             outBuffer[i][k] += oldBufLeft[k]   * balRangeL;
621                         }
622                     }
623                 }
624             }
625 #endif
626 
627             if (doVolume)
628             {
629                 const float volume = pData->postProc.volume;
630 
631                 for (uint32_t k=0; k < frames; ++k)
632                 {
633                     *outBufferL++ *= volume;
634                     *outBufferR++ *= volume;
635                 }
636             }
637 
638         } // End of Post-processing
639 #endif
640 
641         // --------------------------------------------------------------------------------------------------------
642 
643         pData->singleMutex.unlock();
644         return true;
645     }
646 
sampleRateChanged(const double newSampleRate)647     void sampleRateChanged(const double newSampleRate) override
648     {
649         fSynth.setCurrentPlaybackSampleRate(newSampleRate);
650     }
651 
652     // -------------------------------------------------------------------
653     // Plugin buffers
654 
655     // nothing
656 
657     // -------------------------------------------------------------------
658 
init(const CarlaPluginPtr plugin,const char * const filename,const char * const name,const char * const label,const uint options)659     bool init(const CarlaPluginPtr plugin,
660               const char* const filename, const char* const name, const char* const label, const uint options)
661     {
662         CARLA_SAFE_ASSERT_RETURN(pData->engine != nullptr, false);
663 
664         // ---------------------------------------------------------------
665         // first checks
666 
667         if (pData->client != nullptr)
668         {
669             pData->engine->setLastError("Plugin client is already registered");
670             return false;
671         }
672 
673         if (filename == nullptr || filename[0] == '\0')
674         {
675             pData->engine->setLastError("null filename");
676             return false;
677         }
678 
679         for (int i = 128; --i >=0;)
680             fSynth.addVoice(new sfzero::Voice());
681 
682         // ---------------------------------------------------------------
683         // Init SFZero stuff
684 
685         fSynth.setCurrentPlaybackSampleRate(pData->engine->getSampleRate());
686 
687         File file(filename);
688         sfzero::Sound* const sound = new sfzero::Sound(file);
689 
690         sfzero::Sound::LoadingIdleCallback cb = {
691             loadingIdleCallbackFunction,
692             pData->engine,
693         };
694 
695         sound->loadRegions();
696         sound->loadSamples(cb);
697 
698         if (fSynth.addSound(sound) == nullptr)
699         {
700             pData->engine->setLastError("Failed to allocate SFZ sounds in memory");
701             return false;
702         }
703 
704         sound->dumpToConsole();
705 
706         // ---------------------------------------------------------------
707 
708         const String basename(File(filename).getFileNameWithoutExtension());
709 
710         CarlaString label2(label != nullptr ? label : basename.toRawUTF8());
711 
712         fLabel    = label2.dup();
713         fRealName = carla_strdup(basename.toRawUTF8());
714 
715         pData->filename = carla_strdup(filename);
716 
717         if (name != nullptr && name[0] != '\0')
718             pData->name = pData->engine->getUniquePluginName(name);
719         else if (fRealName[0] != '\0')
720             pData->name = pData->engine->getUniquePluginName(fRealName);
721         else
722             pData->name = pData->engine->getUniquePluginName(fLabel);
723 
724         // ---------------------------------------------------------------
725         // register client
726 
727         pData->client = pData->engine->addClient(plugin);
728 
729         if (pData->client == nullptr || ! pData->client->isOk())
730         {
731             pData->engine->setLastError("Failed to register plugin client");
732             return false;
733         }
734 
735         // ---------------------------------------------------------------
736         // set options
737 
738         pData->options = 0x0;
739 
740         if (isPluginOptionEnabled(options, PLUGIN_OPTION_SEND_CONTROL_CHANGES))
741             pData->options |= PLUGIN_OPTION_SEND_CONTROL_CHANGES;
742         if (isPluginOptionEnabled(options, PLUGIN_OPTION_SEND_CHANNEL_PRESSURE))
743             pData->options |= PLUGIN_OPTION_SEND_CHANNEL_PRESSURE;
744         if (isPluginOptionEnabled(options, PLUGIN_OPTION_SEND_NOTE_AFTERTOUCH))
745             pData->options |= PLUGIN_OPTION_SEND_NOTE_AFTERTOUCH;
746         if (isPluginOptionEnabled(options, PLUGIN_OPTION_SEND_PITCHBEND))
747             pData->options |= PLUGIN_OPTION_SEND_PITCHBEND;
748         if (isPluginOptionEnabled(options, PLUGIN_OPTION_SEND_ALL_SOUND_OFF))
749             pData->options |= PLUGIN_OPTION_SEND_ALL_SOUND_OFF;
750         if (isPluginOptionInverseEnabled(options, PLUGIN_OPTION_SKIP_SENDING_NOTES))
751             pData->options |= PLUGIN_OPTION_SKIP_SENDING_NOTES;
752 
753         return true;
754     }
755 
756     // -------------------------------------------------------------------
757 
758 private:
759     sfzero::Synth fSynth;
760     float fNumVoices;
761 
762     const char* fLabel;
763     const char* fRealName;
764 
765     CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CarlaPluginSFZero)
766 };
767 
768 // -------------------------------------------------------------------------------------------------------------------
769 
newSFZero(const Initializer & init)770 CarlaPluginPtr CarlaPlugin::newSFZero(const Initializer& init)
771 {
772     carla_debug("CarlaPluginSFZero::newSFZero({%p, \"%s\", \"%s\", \"%s\", " P_INT64 "})",
773                 init.engine, init.filename, init.name, init.label, init.uniqueId);
774 
775     // -------------------------------------------------------------------
776     // Check if file exists
777 
778     if (! File(init.filename).existsAsFile())
779     {
780         init.engine->setLastError("Requested file is not valid or does not exist");
781         return nullptr;
782     }
783 
784     std::shared_ptr<CarlaPluginSFZero> plugin(new CarlaPluginSFZero(init.engine, init.id));
785 
786     if (! plugin->init(plugin, init.filename, init.name, init.label, init.options))
787         return nullptr;
788 
789     return plugin;
790 }
791 
792 // -------------------------------------------------------------------------------------------------------------------
793 
794 CARLA_BACKEND_END_NAMESPACE
795