Fix crash with clap plugins without MIDI input
[carla.git] / source / backend / plugin / CarlaPluginCLAP.cpp
blob35da2db7929497ed00e2237418a164df4732201a
1 /*
2 * Carla CLAP Plugin
3 * Copyright (C) 2022-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 #include "CarlaBackendUtils.hpp"
22 #include "CarlaClapUtils.hpp"
23 #include "CarlaMathUtils.hpp"
25 #include "CarlaPluginUI.hpp"
27 #ifdef CARLA_OS_MAC
28 # include "CarlaMacUtils.hpp"
29 # import <Foundation/Foundation.h>
30 #endif
32 #include "water/files/File.h"
33 #include "water/misc/Time.h"
35 #if defined(CLAP_WINDOW_API_NATIVE) && defined(_POSIX_VERSION)
36 # if defined(CARLA_OS_LINUX)
37 # define CARLA_CLAP_POSIX_EPOLL
38 # include <sys/epoll.h>
39 # else
40 # include <sys/event.h>
41 # include <sys/types.h>
42 # endif
43 #endif
45 // FIXME
46 // #ifndef CLAP_WINDOW_API_NATIVE
47 // #define CLAP_WINDOW_API_NATIVE ""
48 // #define HAVE_X11 1
49 // #endif
51 CARLA_BACKEND_START_NAMESPACE
53 // --------------------------------------------------------------------------------------------------------------------
55 struct ClapEventData {
56 uint16_t clapPortIndex;
57 uint32_t supportedDialects;
58 CarlaEngineEventPort* port;
61 struct CarlaPluginClapEventData {
62 uint32_t portCount;
63 ClapEventData* portData;
64 ClapEventData* defaultPort; // either this->portData[x] or pData->portIn/Out
66 CarlaPluginClapEventData() noexcept
67 : portCount(0),
68 portData(nullptr),
69 defaultPort(nullptr) {}
71 ~CarlaPluginClapEventData() noexcept
73 CARLA_SAFE_ASSERT_INT(portCount == 0, portCount);
74 CARLA_SAFE_ASSERT(portData == nullptr);
75 CARLA_SAFE_ASSERT(defaultPort == nullptr);
78 void createNew(const uint32_t newCount)
80 CARLA_SAFE_ASSERT_INT(portCount == 0, portCount);
81 CARLA_SAFE_ASSERT_RETURN(portData == nullptr,);
82 CARLA_SAFE_ASSERT_RETURN(defaultPort == nullptr,);
83 CARLA_SAFE_ASSERT_RETURN(newCount > 0,);
85 portData = new ClapEventData[newCount];
86 portCount = newCount;
88 defaultPort = nullptr;
91 void clear(CarlaEngineEventPort* const portToIgnore) noexcept
93 if (portData != nullptr)
95 for (uint32_t i=0; i < portCount; ++i)
97 if (portData[i].port != nullptr)
99 if (portData[i].port != portToIgnore)
100 delete portData[i].port;
101 portData[i].port = nullptr;
105 delete[] portData;
106 portData = nullptr;
109 portCount = 0;
111 defaultPort = nullptr;
114 void initBuffers() const noexcept
116 for (uint32_t i=0; i < portCount; ++i)
118 if (portData[i].port != nullptr && (defaultPort == nullptr || portData[i].port != defaultPort->port))
119 portData[i].port->initBuffer();
123 CARLA_DECLARE_NON_COPYABLE(CarlaPluginClapEventData)
126 #ifdef _POSIX_VERSION
127 // --------------------------------------------------------------------------------------------------------------------
129 struct HostPosixFileDescriptorDetails {
130 int hostFd;
131 int pluginFd;
132 clap_posix_fd_flags_t flags;
135 static constexpr const HostPosixFileDescriptorDetails kPosixFileDescriptorFallback = { -1, -1, 0x0 };
136 static /* */ HostPosixFileDescriptorDetails kPosixFileDescriptorFallbackNC = { -1, -1, 0x0 };
137 #endif
139 // --------------------------------------------------------------------------------------------------------------------
141 struct HostTimerDetails {
142 clap_id clapId;
143 uint32_t periodInMs;
144 uint32_t lastCallTimeInMs;
147 static constexpr const HostTimerDetails kTimerFallback = { CLAP_INVALID_ID, 0, 0 };
148 static /* */ HostTimerDetails kTimerFallbackNC = { CLAP_INVALID_ID, 0, 0 };
150 // --------------------------------------------------------------------------------------------------------------------
152 struct carla_clap_host : clap_host_t {
153 class Callbacks {
154 public:
155 virtual ~Callbacks() {}
156 virtual void clapRequestRestart() = 0;
157 virtual void clapRequestProcess() = 0;
158 virtual void clapRequestCallback() = 0;
159 virtual void clapMarkDirty() = 0;
160 virtual void clapLatencyChanged() = 0;
161 #ifdef CLAP_WINDOW_API_NATIVE
162 // gui
163 virtual void clapGuiResizeHintsChanged() = 0;
164 virtual bool clapGuiRequestResize(uint width, uint height) = 0;
165 virtual bool clapGuiRequestShow() = 0;
166 virtual bool clapGuiRequestHide() = 0;
167 virtual void clapGuiClosed(bool wasDestroyed) = 0;
168 #ifdef _POSIX_VERSION
169 // posix fd
170 virtual bool clapRegisterPosixFD(int fd, clap_posix_fd_flags_t flags) = 0;
171 virtual bool clapModifyPosixFD(int fd, clap_posix_fd_flags_t flags) = 0;
172 virtual bool clapUnregisterPosixFD(int fd) = 0;
173 #endif
174 // timer
175 virtual bool clapRegisterTimer(uint32_t periodInMs, clap_id* timerId) = 0;
176 virtual bool clapUnregisterTimer(clap_id timerId) = 0;
177 #endif
180 Callbacks* const hostCallbacks;
182 clap_host_latency_t latency;
183 clap_host_state_t state;
184 #ifdef CLAP_WINDOW_API_NATIVE
185 clap_host_gui_t gui;
186 #ifdef _POSIX_VERSION
187 clap_host_posix_fd_support_t posixFD;
188 #endif
189 clap_host_timer_support_t timer;
190 #endif
192 carla_clap_host(Callbacks* const hostCb)
193 : hostCallbacks(hostCb)
195 clap_version = CLAP_VERSION;
196 host_data = this;
197 name = "Carla";
198 vendor = "falkTX";
199 url = "https://kx.studio/carla";
200 version = CARLA_VERSION_STRING;
202 get_extension = carla_get_extension;
203 request_restart = carla_request_restart;
204 request_process = carla_request_process;
205 request_callback = carla_request_callback;
207 latency.changed = carla_latency_changed;
209 state.mark_dirty = carla_mark_dirty;
211 #ifdef CLAP_WINDOW_API_NATIVE
212 gui.resize_hints_changed = carla_resize_hints_changed;
213 gui.request_resize = carla_request_resize;
214 gui.request_show = carla_request_show;
215 gui.request_hide = carla_request_hide;
216 gui.closed = carla_closed;
218 #ifdef _POSIX_VERSION
219 posixFD.register_fd = carla_register_fd;
220 posixFD.modify_fd = carla_modify_fd;
221 posixFD.unregister_fd = carla_unregister_fd;
222 #endif
224 timer.register_timer = carla_register_timer;
225 timer.unregister_timer = carla_unregister_timer;
226 #endif
229 static const void* CLAP_ABI carla_get_extension(const clap_host_t* const host, const char* const extension_id)
231 carla_clap_host* const self = static_cast<carla_clap_host*>(host->host_data);
233 if (std::strcmp(extension_id, CLAP_EXT_LATENCY) == 0)
234 return &self->latency;
235 if (std::strcmp(extension_id, CLAP_EXT_STATE) == 0)
236 return &self->state;
237 #ifdef CLAP_WINDOW_API_NATIVE
238 if (std::strcmp(extension_id, CLAP_EXT_GUI) == 0)
239 return &self->gui;
240 #ifdef _POSIX_VERSION
241 if (std::strcmp(extension_id, CLAP_EXT_POSIX_FD_SUPPORT) == 0)
242 return &self->posixFD;
243 #endif
244 if (std::strcmp(extension_id, CLAP_EXT_TIMER_SUPPORT) == 0)
245 return &self->timer;
246 #endif
248 carla_stderr("Plugin requested unsupported CLAP extension '%s'", extension_id);
249 return nullptr;
252 static void CLAP_ABI carla_request_restart(const clap_host_t* const host)
254 static_cast<const carla_clap_host*>(host->host_data)->hostCallbacks->clapRequestRestart();
257 static void CLAP_ABI carla_request_process(const clap_host_t* const host)
259 static_cast<const carla_clap_host*>(host->host_data)->hostCallbacks->clapRequestProcess();
262 static void CLAP_ABI carla_request_callback(const clap_host_t* const host)
264 static_cast<const carla_clap_host*>(host->host_data)->hostCallbacks->clapRequestCallback();
267 static void CLAP_ABI carla_latency_changed(const clap_host_t* const host)
269 static_cast<const carla_clap_host*>(host->host_data)->hostCallbacks->clapLatencyChanged();
272 static void CLAP_ABI carla_mark_dirty(const clap_host_t* const host)
274 static_cast<const carla_clap_host*>(host->host_data)->hostCallbacks->clapMarkDirty();
277 #ifdef CLAP_WINDOW_API_NATIVE
278 static void CLAP_ABI carla_resize_hints_changed(const clap_host_t* const host)
280 static_cast<const carla_clap_host*>(host->host_data)->hostCallbacks->clapGuiResizeHintsChanged();
283 static bool CLAP_ABI carla_request_resize(const clap_host_t* const host, const uint32_t width, const uint32_t height)
285 return static_cast<const carla_clap_host*>(host->host_data)->hostCallbacks->clapGuiRequestResize(width, height);
288 static bool CLAP_ABI carla_request_show(const clap_host_t* const host)
290 return static_cast<const carla_clap_host*>(host->host_data)->hostCallbacks->clapGuiRequestShow();
293 static bool CLAP_ABI carla_request_hide(const clap_host_t* const host)
295 return static_cast<const carla_clap_host*>(host->host_data)->hostCallbacks->clapGuiRequestHide();
298 static void CLAP_ABI carla_closed(const clap_host_t* const host, bool was_destroyed)
300 static_cast<const carla_clap_host*>(host->host_data)->hostCallbacks->clapGuiClosed(was_destroyed);
303 #ifdef _POSIX_VERSION
304 static bool CLAP_ABI carla_register_fd(const clap_host_t* const host, const int fd, const clap_posix_fd_flags_t flags)
306 return static_cast<const carla_clap_host*>(host->host_data)->hostCallbacks->clapRegisterPosixFD(fd, flags);
309 static bool CLAP_ABI carla_modify_fd(const clap_host_t* const host, const int fd, const clap_posix_fd_flags_t flags)
311 return static_cast<const carla_clap_host*>(host->host_data)->hostCallbacks->clapModifyPosixFD(fd, flags);
314 static bool CLAP_ABI carla_unregister_fd(const clap_host_t* const host, const int fd)
316 return static_cast<const carla_clap_host*>(host->host_data)->hostCallbacks->clapUnregisterPosixFD(fd);
318 #endif
320 static bool CLAP_ABI carla_register_timer(const clap_host_t* const host, const uint32_t period_ms, clap_id* const timer_id)
322 return static_cast<const carla_clap_host*>(host->host_data)->hostCallbacks->clapRegisterTimer(period_ms, timer_id);
325 static bool CLAP_ABI carla_unregister_timer(const clap_host_t* const host, const clap_id timer_id)
327 return static_cast<const carla_clap_host*>(host->host_data)->hostCallbacks->clapUnregisterTimer(timer_id);
329 #endif
332 // --------------------------------------------------------------------------------------------------------------------
334 struct carla_clap_input_audio_buffers {
335 clap_audio_buffer_const_t* buffers;
336 clap_audio_buffer_extra_data_t* extra;
337 uint32_t count;
339 carla_clap_input_audio_buffers() noexcept
340 : buffers(nullptr),
341 extra(nullptr),
342 count(0) {}
344 ~carla_clap_input_audio_buffers()
346 delete[] buffers;
347 delete[] extra;
350 void realloc(const uint32_t portCount)
352 delete[] buffers;
353 delete[] extra;
354 count = portCount;
356 if (portCount != 0)
358 buffers = new clap_audio_buffer_const_t[portCount];
359 extra = new clap_audio_buffer_extra_data_t[portCount];
360 carla_zeroStructs(buffers, portCount);
361 carla_zeroStructs(extra, portCount);
363 else
365 buffers = nullptr;
366 extra = nullptr;
370 const clap_audio_buffer_t* cast() const noexcept
372 return static_cast<const clap_audio_buffer_t*>(static_cast<const void*>(buffers));
376 struct carla_clap_output_audio_buffers {
377 clap_audio_buffer_t* buffers;
378 clap_audio_buffer_extra_data_t* extra;
379 uint32_t count;
381 carla_clap_output_audio_buffers() noexcept
382 : buffers(nullptr),
383 extra(nullptr),
384 count(0) {}
386 ~carla_clap_output_audio_buffers()
388 delete[] buffers;
389 delete[] extra;
392 void realloc(const uint32_t portCount)
394 delete[] buffers;
395 delete[] extra;
396 count = portCount;
398 if (portCount != 0)
400 buffers = new clap_audio_buffer_t[portCount];
401 extra = new clap_audio_buffer_extra_data_t[portCount];
402 carla_zeroStructs(buffers, portCount);
403 carla_zeroStructs(extra, portCount);
405 else
407 buffers = nullptr;
408 extra = nullptr;
413 // --------------------------------------------------------------------------------------------------------------------
415 struct carla_clap_input_events : clap_input_events_t, CarlaPluginClapEventData {
416 union Event {
417 clap_event_header_t header;
418 clap_event_param_value_t param;
419 clap_event_param_gesture_t gesture;
420 clap_event_midi_t midi;
421 clap_event_note_t note;
422 clap_event_midi_sysex_t sysex;
425 struct ScheduledParameterUpdate {
426 bool updated;
427 double value;
428 clap_id clapId;
429 void* cookie;
431 ScheduledParameterUpdate()
432 : updated(false),
433 value(0.f),
434 clapId(0),
435 cookie(0) {}
438 Event* events;
439 ScheduledParameterUpdate* updatedParams;
441 uint32_t numEventsAllocated;
442 uint32_t numEventsUsed;
443 uint32_t numParams;
445 carla_clap_input_events()
446 : CarlaPluginClapEventData(),
447 events(nullptr),
448 updatedParams(nullptr),
449 numEventsAllocated(0),
450 numEventsUsed(0),
451 numParams(0)
453 ctx = this;
454 size = carla_size;
455 get = carla_get;
458 ~carla_clap_input_events()
460 delete[] events;
461 delete[] updatedParams;
464 // called on plugin reload
465 // NOTE: clapId and cookie must be separately set outside this function
466 void realloc(CarlaEngineEventPort* const defPortIn, const uint32_t portCount, const uint32_t paramCount)
468 numEventsUsed = 0;
469 numParams = paramCount;
470 delete[] events;
471 delete[] updatedParams;
473 if (portCount != 0 || paramCount != 0)
475 static_assert(kPluginMaxMidiEvents > MAX_MIDI_NOTE, "Enough space for input events");
477 numEventsAllocated = paramCount * 2 + kPluginMaxMidiEvents * std::max(1u, portCount);
478 events = new Event[numEventsAllocated];
479 updatedParams = new ScheduledParameterUpdate[paramCount];
481 else
483 numEventsAllocated = 0;
484 events = nullptr;
485 updatedParams = nullptr;
488 CarlaPluginClapEventData::clear(defPortIn);
490 if (portCount != 0)
491 CarlaPluginClapEventData::createNew(portCount);
494 // used for temporary copies (this instance must be empty)
495 void reallocEqualTo(const carla_clap_input_events& other)
497 numParams = other.numParams;
498 numEventsAllocated = other.numEventsAllocated;
500 if (numEventsAllocated != 0)
502 events = new Event[numEventsAllocated];
503 updatedParams = new ScheduledParameterUpdate[numParams];
505 for (uint32_t i=0; i<numParams; ++i)
507 updatedParams[i].clapId = other.updatedParams[i].clapId;
508 updatedParams[i].cookie = other.updatedParams[i].cookie;
513 void swap(carla_clap_input_events& other)
515 CARLA_SAFE_ASSERT_RETURN(numParams == other.numParams,);
516 CARLA_SAFE_ASSERT_RETURN(numEventsAllocated == other.numEventsAllocated,);
518 std::swap(numEventsUsed, other.numEventsUsed);
519 std::swap(updatedParams, other.updatedParams);
520 std::swap(events, other.events);
523 const clap_input_events_t* cast() const noexcept
525 return static_cast<const clap_input_events_t*>(this);
528 // called just before plugin processing
529 void handleScheduledParameterUpdates()
531 uint32_t count = 0;
533 for (uint32_t i=0; i<numParams; ++i)
535 if (updatedParams[i].updated)
537 events[count++].param = {
538 { sizeof(clap_event_param_value_t), 0, 0, CLAP_EVENT_PARAM_VALUE, 0 },
539 updatedParams[i].clapId,
540 updatedParams[i].cookie,
541 -1, -1, -1, -1,
542 updatedParams[i].value
545 updatedParams[i].updated = false;
549 numEventsUsed = count;
552 // called when a parameter is set from non-rt thread
553 void setParamValue(const uint32_t index, const float value) noexcept
555 CARLA_SAFE_ASSERT_RETURN(index < numParams,);
557 updatedParams[index].value = value;
558 updatedParams[index].updated = true;
561 // called when a parameter is set from rt thread
562 void setParamValueRT(const uint32_t index, const float value, const uint32_t frameOffset) noexcept
564 CARLA_SAFE_ASSERT_RETURN(index < numParams,);
566 if (numEventsUsed == numEventsAllocated)
567 return;
569 events[numEventsUsed++].param = {
570 { sizeof(clap_event_param_value_t), frameOffset, 0, CLAP_EVENT_PARAM_VALUE, CLAP_EVENT_IS_LIVE },
571 updatedParams[index].clapId,
572 updatedParams[index].cookie,
573 -1, -1, -1, -1,
574 value
578 void addSimpleMidiEvent(const bool isLive, const uint16_t port, const uint32_t frameOffset, const uint8_t data[3])
580 if (numEventsUsed == numEventsAllocated)
581 return;
583 events[numEventsUsed++].midi = {
584 { sizeof(clap_event_midi_t), frameOffset, 0, CLAP_EVENT_MIDI, isLive ? (uint32_t)CLAP_EVENT_IS_LIVE : 0u },
585 port,
586 { data[0], data[1], data[2] }
590 void addSimpleNoteEvent(const bool isLive, const int16_t port, const uint32_t frameOffset,
591 const uint8_t channel, const uint8_t key, const uint8_t velocity)
593 if (numEventsUsed == numEventsAllocated)
594 return;
596 const uint16_t eventType = velocity > 0 ? CLAP_EVENT_NOTE_ON : CLAP_EVENT_NOTE_OFF;
598 events[numEventsUsed++].note = {
599 { sizeof(clap_event_note_t), frameOffset, 0, eventType, isLive ? (uint32_t)CLAP_EVENT_IS_LIVE : 0u },
601 port,
602 channel,
603 key,
604 static_cast<double>(velocity) / 127.0
608 static uint32_t CLAP_ABI carla_size(const clap_input_events_t* const list) noexcept
610 return static_cast<const carla_clap_input_events*>(list->ctx)->numEventsUsed;
613 static const clap_event_header_t* CLAP_ABI carla_get(const clap_input_events_t* const list, const uint32_t index) noexcept
615 return &static_cast<const carla_clap_input_events*>(list->ctx)->events[index].header;
619 // --------------------------------------------------------------------------------------------------------------------
621 struct carla_clap_output_events : clap_output_events_t, CarlaPluginClapEventData {
622 union Event {
623 clap_event_header_t header;
624 clap_event_param_value_t param;
625 clap_event_midi_t midi;
628 Event* events;
629 uint32_t numEventsAllocated;
630 uint32_t numEventsUsed;
632 carla_clap_output_events()
633 : events(nullptr),
634 numEventsAllocated(0),
635 numEventsUsed(0)
637 ctx = this;
638 try_push = carla_try_push;
641 ~carla_clap_output_events()
643 delete[] events;
646 // called on plugin reload
647 void realloc(CarlaEngineEventPort* const defPortOut, const uint32_t portCount, const uint32_t paramCount)
649 numEventsUsed = 0;
650 delete[] events;
652 if (portCount != 0 || paramCount != 0)
654 numEventsAllocated = paramCount + kPluginMaxMidiEvents * std::max(1u, portCount);
655 events = new Event[numEventsAllocated];
657 else
659 numEventsAllocated = 0;
660 events = nullptr;
663 CarlaPluginClapEventData::clear(defPortOut);
665 if (portCount != 0)
666 CarlaPluginClapEventData::createNew(portCount);
669 const clap_output_events_t* cast() const noexcept
671 return static_cast<const clap_output_events_t*>(this);
674 bool tryPush(const clap_event_header_t* const event)
676 if (numEventsUsed == numEventsAllocated)
677 return false;
679 Event e;
680 switch (event->type)
682 case CLAP_EVENT_PARAM_VALUE:
683 e.param = *static_cast<const clap_event_param_value_t*>(static_cast<const void*>(event));
684 break;
685 case CLAP_EVENT_MIDI:
686 e.midi = *static_cast<const clap_event_midi_t*>(static_cast<const void*>(event));
687 break;
688 default:
689 return false;
692 events[numEventsUsed++] = e;
693 return true;
696 static bool CLAP_ABI carla_try_push(const clap_output_events_t* const list, const clap_event_header_t* const event)
698 return static_cast<carla_clap_output_events*>(list->ctx)->tryPush(event);
702 // --------------------------------------------------------------------------------------------------------------------
704 class CarlaPluginCLAP : public CarlaPlugin,
705 #ifdef CLAP_WINDOW_API_NATIVE
706 private CarlaPluginUI::Callback,
707 #endif
708 private carla_clap_host::Callbacks
710 public:
711 CarlaPluginCLAP(CarlaEngine* const engine, const uint id)
712 : CarlaPlugin(engine, id),
713 fPlugin(nullptr),
714 fPluginDescriptor(nullptr),
715 fPluginEntry(nullptr),
716 fHost(this),
717 fExtensions(),
718 fInputAudioBuffers(),
719 fOutputAudioBuffers(),
720 fInputEvents(),
721 fOutputEvents(),
722 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
723 fAudioOutBuffers(nullptr),
724 #endif
725 fLastChunk(nullptr),
726 fLastKnownLatency(0),
727 kEngineHasIdleOnMainThread(engine->hasIdleOnMainThread()),
728 fNeedsParamFlush(false),
729 fNeedsRestart(false),
730 fNeedsProcess(false),
731 fNeedsIdleCallback(false)
733 carla_debug("CarlaPluginCLAP::CarlaPluginCLAP(%p, %i)", engine, id);
736 ~CarlaPluginCLAP() override
738 carla_debug("CarlaPluginCLAP::~CarlaPluginCLAP()");
740 runIdleCallbacksAsNeeded(false);
742 #ifdef CLAP_WINDOW_API_NATIVE
743 // close UI
744 if (fUI.isCreated)
745 showCustomUI(false);
746 #endif
748 pData->singleMutex.lock();
749 pData->masterMutex.lock();
751 if (pData->client != nullptr && pData->client->isActive())
752 pData->client->deactivate(true);
754 if (pData->active)
756 deactivate();
757 pData->active = false;
760 if (fPlugin != nullptr)
762 fPlugin->destroy(fPlugin);
763 fPlugin = nullptr;
766 if (fLastChunk != nullptr)
768 std::free(fLastChunk);
769 fLastChunk = nullptr;
772 clearBuffers();
774 if (fPluginEntry != nullptr)
776 fPluginEntry->deinit();
777 fPluginEntry = nullptr;
781 // -------------------------------------------------------------------
782 // Information (base)
784 PluginType getType() const noexcept override
786 return PLUGIN_CLAP;
789 PluginCategory getCategory() const noexcept override
791 CARLA_SAFE_ASSERT_RETURN(fPluginDescriptor != nullptr, PLUGIN_CATEGORY_NONE);
793 if (fPluginDescriptor->features == nullptr)
794 return PLUGIN_CATEGORY_NONE;
796 return getPluginCategoryFromClapFeatures(fPluginDescriptor->features);
799 uint32_t getLatencyInFrames() const noexcept override
801 // under clap we can only request plugin latency in main thread,
802 // which is unsuitable for this call
803 return fLastKnownLatency;
806 // -------------------------------------------------------------------
807 // Information (count)
809 uint32_t getMidiInCount() const noexcept override
811 return fInputEvents.portCount;
814 uint32_t getMidiOutCount() const noexcept override
816 return fOutputEvents.portCount;
819 // -------------------------------------------------------------------
820 // Information (current data)
822 uint getAudioPortHints(const bool isOutput, const uint32_t portIndex) const noexcept override
824 uint hints = 0x0;
826 if (isOutput)
828 for (uint32_t i=0, j=0; i<fOutputAudioBuffers.count; ++i, j+=fOutputAudioBuffers.buffers[i].channel_count)
830 if (j != portIndex)
831 continue;
833 if (!fOutputAudioBuffers.extra[i].isMain)
834 hints |= AUDIO_PORT_IS_SIDECHAIN;
837 else
839 for (uint32_t i=0, j=0; i<fInputAudioBuffers.count; ++i, j+=fInputAudioBuffers.buffers[i].channel_count)
841 if (j != portIndex)
842 continue;
844 if (!fInputAudioBuffers.extra[i].isMain)
845 hints |= AUDIO_PORT_IS_SIDECHAIN;
849 return hints;
852 std::size_t getChunkData(void** const dataPtr) noexcept override
854 CARLA_SAFE_ASSERT_RETURN(pData->options & PLUGIN_OPTION_USE_CHUNKS, 0);
855 CARLA_SAFE_ASSERT_RETURN(fExtensions.state != nullptr, 0);
856 CARLA_SAFE_ASSERT_RETURN(dataPtr != nullptr, 0);
858 std::free(fLastChunk);
860 clap_ostream_impl stream;
861 if (fExtensions.state->save(fPlugin, &stream))
863 *dataPtr = fLastChunk = stream.buffer;
864 runIdleCallbacksAsNeeded(false);
865 return stream.size;
867 else
869 *dataPtr = fLastChunk = nullptr;
870 runIdleCallbacksAsNeeded(false);
871 return 0;
875 // -------------------------------------------------------------------
876 // Information (per-plugin data)
878 uint getOptionsAvailable() const noexcept override
880 uint options = 0x0;
882 if (fExtensions.state != nullptr)
883 options |= PLUGIN_OPTION_USE_CHUNKS;
885 for (uint32_t i=0; i<fInputEvents.portCount; ++i)
887 if (fInputEvents.portData[i].supportedDialects & CLAP_NOTE_DIALECT_MIDI)
889 options |= PLUGIN_OPTION_SEND_CONTROL_CHANGES;
890 options |= PLUGIN_OPTION_SEND_CHANNEL_PRESSURE;
891 options |= PLUGIN_OPTION_SEND_NOTE_AFTERTOUCH;
892 options |= PLUGIN_OPTION_SEND_PITCHBEND;
893 options |= PLUGIN_OPTION_SEND_ALL_SOUND_OFF;
894 options |= PLUGIN_OPTION_SEND_PROGRAM_CHANGES;
895 options |= PLUGIN_OPTION_SKIP_SENDING_NOTES;
896 break;
898 if (fInputEvents.portData[i].supportedDialects & CLAP_NOTE_DIALECT_CLAP)
900 options |= PLUGIN_OPTION_SKIP_SENDING_NOTES;
901 // nobreak, in case another port supports MIDI
905 return options;
908 double getBestParameterValue(const uint32_t parameterId, const clap_id clapId) const noexcept
910 if (fInputEvents.updatedParams[parameterId].updated)
911 return fInputEvents.updatedParams[parameterId].value;
913 double value;
914 CARLA_SAFE_ASSERT_RETURN(fExtensions.params->get_value(fPlugin, clapId, &value), 0.0);
916 return value;
919 float getParameterValue(const uint32_t parameterId) const noexcept override
921 CARLA_SAFE_ASSERT_RETURN(fPlugin != nullptr, 0.f);
922 CARLA_SAFE_ASSERT_RETURN(fExtensions.params != nullptr, 0.f);
924 return getBestParameterValue(parameterId, pData->param.data[parameterId].rindex);
927 bool getLabel(char* const strBuf) const noexcept override
929 CARLA_SAFE_ASSERT_RETURN(fPluginDescriptor != nullptr, false);
931 std::strncpy(strBuf, fPluginDescriptor->id, STR_MAX);
932 return true;
935 bool getMaker(char* const strBuf) const noexcept override
937 CARLA_SAFE_ASSERT_RETURN(fPluginDescriptor != nullptr, false);
939 std::strncpy(strBuf, fPluginDescriptor->vendor, STR_MAX);
940 return true;
943 bool getCopyright(char* const strBuf) const noexcept override
945 return getMaker(strBuf);
948 bool getRealName(char* const strBuf) const noexcept override
950 CARLA_SAFE_ASSERT_RETURN(fPluginDescriptor != nullptr, false);
952 std::strncpy(strBuf, fPluginDescriptor->name, STR_MAX);
953 return true;
956 bool getParameterName(const uint32_t parameterId, char* const strBuf) const noexcept override
958 CARLA_SAFE_ASSERT_RETURN(fPlugin != nullptr, false);
959 CARLA_SAFE_ASSERT_RETURN(fExtensions.params != nullptr, false);
960 CARLA_SAFE_ASSERT_RETURN(parameterId < pData->param.count, false);
962 clap_param_info_t paramInfo = {};
963 CARLA_SAFE_ASSERT_RETURN(fExtensions.params->get_info(fPlugin, parameterId, &paramInfo), false);
965 std::strncpy(strBuf, paramInfo.name, STR_MAX);
966 strBuf[STR_MAX-1] = '\0';
967 return true;
970 bool getParameterSymbol(const uint32_t parameterId, char* const strBuf) const noexcept override
972 CARLA_SAFE_ASSERT_RETURN(parameterId < pData->param.count, false);
974 const clap_id clapId = pData->param.data[parameterId].rindex;
975 std::snprintf(strBuf, STR_MAX, "%u", clapId);
976 return true;
979 bool getParameterText(const uint32_t parameterId, char* const strBuf) noexcept override
981 CARLA_SAFE_ASSERT_RETURN(fPlugin != nullptr, false);
982 CARLA_SAFE_ASSERT_RETURN(fExtensions.params != nullptr, false);
983 CARLA_SAFE_ASSERT_RETURN(parameterId < pData->param.count, false);
985 const clap_id clapId = pData->param.data[parameterId].rindex;
986 const double value = getBestParameterValue(parameterId, clapId);
988 return fExtensions.params->value_to_text(fPlugin, clapId, value, strBuf, STR_MAX);
991 bool getParameterGroupName(const uint32_t parameterId, char* const strBuf) const noexcept override
993 CARLA_SAFE_ASSERT_RETURN(fPlugin != nullptr, false);
994 CARLA_SAFE_ASSERT_RETURN(fExtensions.params != nullptr, false);
995 CARLA_SAFE_ASSERT_RETURN(parameterId < pData->param.count, false);
997 clap_param_info_t paramInfo = {};
998 CARLA_SAFE_ASSERT_RETURN(fExtensions.params->get_info(fPlugin, parameterId, &paramInfo), false);
1000 if (paramInfo.module[0] == '\0')
1001 return false;
1003 if (char* const sep = std::strrchr(paramInfo.module, '/'))
1005 paramInfo.module[STR_MAX/2-2] = sep[0] = '\0';
1007 char strBuf2[STR_MAX/2];
1008 std::strncpy(strBuf2, paramInfo.module, STR_MAX/2);
1009 strBuf2[STR_MAX/2-1] = '\0';
1011 std::snprintf(strBuf, STR_MAX, "%s:%s", strBuf2, strBuf2);
1012 strBuf[STR_MAX-1] = '\0';
1013 return true;
1016 return false;
1019 // -------------------------------------------------------------------
1020 // Set data (state)
1022 // nothing
1024 // -------------------------------------------------------------------
1025 // Set data (internal stuff)
1027 #ifdef CLAP_WINDOW_API_NATIVE
1028 void setName(const char* const newName) override
1030 CarlaPlugin::setName(newName);
1032 if (fUI.isCreated && pData->uiTitle.isEmpty())
1033 setWindowTitle(nullptr);
1035 #endif
1037 // -------------------------------------------------------------------
1038 // Set data (plugin-specific stuff)
1040 void setParameterValue(const uint32_t parameterId, const float value, const bool sendGui, const bool sendOsc, const bool sendCallback) noexcept override
1042 CARLA_SAFE_ASSERT_RETURN(fPlugin != nullptr,);
1043 CARLA_SAFE_ASSERT_RETURN(parameterId < pData->param.count,);
1045 const float fixedValue = pData->param.getFixedValue(parameterId, value);
1046 fInputEvents.setParamValue(parameterId, fixedValue);
1048 if (!pData->active && fExtensions.params->flush != nullptr)
1049 fNeedsParamFlush = true;
1051 CarlaPlugin::setParameterValue(parameterId, fixedValue, sendGui, sendOsc, sendCallback);
1054 void setParameterValueRT(const uint32_t parameterId, const float value, const uint32_t frameOffset, const bool sendCallbackLater) noexcept override
1056 CARLA_SAFE_ASSERT_RETURN(fPlugin != nullptr,);
1057 CARLA_SAFE_ASSERT_RETURN(parameterId < pData->param.count,);
1059 const float fixedValue = pData->param.getFixedValue(parameterId, value);
1060 fInputEvents.setParamValueRT(parameterId, fixedValue, frameOffset);
1062 CarlaPlugin::setParameterValueRT(parameterId, fixedValue, frameOffset, sendCallbackLater);
1065 void setChunkData(const void* const data, const std::size_t dataSize) override
1067 CARLA_SAFE_ASSERT_RETURN(pData->options & PLUGIN_OPTION_USE_CHUNKS,);
1068 CARLA_SAFE_ASSERT_RETURN(fExtensions.state != nullptr,);
1069 CARLA_SAFE_ASSERT_RETURN(data != nullptr,);
1070 CARLA_SAFE_ASSERT_RETURN(dataSize > 0,);
1072 const clap_istream_impl stream(data, dataSize);
1073 if (fExtensions.state->load(fPlugin, &stream))
1074 pData->updateParameterValues(this, true, true, false);
1076 runIdleCallbacksAsNeeded(false);
1079 // -------------------------------------------------------------------
1080 // Set ui stuff
1082 #ifdef CLAP_WINDOW_API_NATIVE
1083 void setWindowTitle(const char* const title) noexcept
1085 if (!fUI.isCreated)
1086 return;
1088 CarlaString uiTitle;
1090 if (title != nullptr)
1092 uiTitle = title;
1094 else
1096 uiTitle = pData->name;
1097 uiTitle += " (GUI)";
1100 if (fUI.isEmbed)
1102 if (fUI.window != nullptr)
1103 fUI.window->setTitle(uiTitle.buffer());
1105 else
1107 fExtensions.gui->suggest_title(fPlugin, uiTitle.buffer());
1111 void setCustomUITitle(const char* const title) noexcept override
1113 setWindowTitle(title);
1114 CarlaPlugin::setCustomUITitle(title);
1117 void showCustomUI(const bool yesNo) override
1119 CARLA_SAFE_ASSERT_RETURN(fExtensions.gui != nullptr,);
1121 if (fUI.isVisible == yesNo)
1122 return;
1124 if (yesNo)
1126 if (fUI.isVisible)
1128 fExtensions.gui->show(fPlugin);
1130 if (fUI.isEmbed)
1132 CARLA_SAFE_ASSERT_RETURN(fUI.window != nullptr,);
1133 fUI.window->show();
1134 fUI.window->focus();
1137 runIdleCallbacksAsNeeded(false);
1138 return;
1141 if (!fUI.initalized)
1143 fUI.isEmbed = fExtensions.gui->is_api_supported(fPlugin, CLAP_WINDOW_API_NATIVE, false);
1144 fUI.initalized = true;
1147 if (!fUI.isCreated)
1149 if (!fExtensions.gui->create(fPlugin, CLAP_WINDOW_API_NATIVE, !fUI.isEmbed))
1151 pData->engine->callback(true, true,
1152 ENGINE_CALLBACK_UI_STATE_CHANGED,
1153 pData->id,
1155 0, 0, 0.0f,
1156 "Plugin refused to open its own UI");
1157 return;
1159 fUI.isCreated = true;
1162 const bool resizable = fExtensions.gui->can_resize(fPlugin);
1164 const EngineOptions& opts(pData->engine->getOptions());
1166 #if defined(CARLA_OS_WIN)
1167 fUI.window = CarlaPluginUI::newWindows(this, opts.frontendWinId, opts.pluginsAreStandalone, resizable);
1168 #elif defined(CARLA_OS_MAC)
1169 fUI.window = CarlaPluginUI::newCocoa(this, opts.frontendWinId, opts.pluginsAreStandalone, resizable);
1170 #elif defined(HAVE_X11)
1171 fUI.window = CarlaPluginUI::newX11(this, opts.frontendWinId, opts.pluginsAreStandalone, resizable, false);
1172 #else
1173 pData->engine->callback(true, true,
1174 ENGINE_CALLBACK_UI_STATE_CHANGED,
1175 pData->id,
1177 0, 0, 0.0f,
1178 "Unsupported UI type");
1179 return;
1180 #endif
1182 #ifndef CARLA_OS_MAC
1183 if (carla_isNotZero(opts.uiScale))
1184 fExtensions.gui->set_scale(fPlugin, opts.uiScale);
1185 #endif
1187 setWindowTitle(nullptr);
1189 if (fUI.isEmbed)
1191 clap_window_t win = { CLAP_WINDOW_API_NATIVE, {} };
1192 win.ptr = fUI.window->getPtr();
1194 fExtensions.gui->set_parent(fPlugin, &win);
1196 uint32_t width, height;
1197 if (fExtensions.gui->get_size(fPlugin, &width, &height))
1199 fUI.isResizingFromInit = true;
1200 fUI.width = width;
1201 fUI.height = height;
1202 fUI.window->setSize(width, height, true, true);
1205 fExtensions.gui->show(fPlugin);
1206 fUI.window->show();
1208 else
1210 clap_window_t win = { CLAP_WINDOW_API_NATIVE, {} };
1211 win.uptr = opts.frontendWinId;
1212 fExtensions.gui->set_transient(fPlugin, &win);
1213 fExtensions.gui->show(fPlugin);
1214 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
1215 pData->tryTransient();
1216 #endif
1219 fUI.isVisible = true;
1221 else
1223 fUI.isVisible = false;
1225 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
1226 pData->transientTryCounter = 0;
1227 #endif
1229 if (fUI.window != nullptr)
1230 fUI.window->hide();
1232 fExtensions.gui->hide(fPlugin);
1234 if (fUI.isCreated)
1236 fExtensions.gui->destroy(fPlugin);
1237 fUI.isCreated = false;
1240 if (fUI.window != nullptr)
1242 delete fUI.window;
1243 fUI.window = nullptr;
1247 runIdleCallbacksAsNeeded(true);
1250 void* embedCustomUI(void* const ptr) override
1252 CARLA_SAFE_ASSERT_RETURN(fUI.window == nullptr, nullptr);
1254 if (!fUI.initalized)
1256 fUI.isEmbed = fExtensions.gui->is_api_supported(fPlugin, CLAP_WINDOW_API_NATIVE, false);
1257 fUI.initalized = true;
1260 if (!fUI.isCreated)
1262 if (!fExtensions.gui->create(fPlugin, CLAP_WINDOW_API_NATIVE, false))
1264 pData->engine->callback(true, true,
1265 ENGINE_CALLBACK_UI_STATE_CHANGED,
1266 pData->id,
1268 0, 0, 0.0f,
1269 "Plugin refused to open its own UI");
1270 return nullptr;
1272 fUI.isCreated = true;
1275 fUI.isVisible = true;
1277 #ifndef CARLA_OS_MAC
1278 const EngineOptions& opts(pData->engine->getOptions());
1280 if (carla_isNotZero(opts.uiScale))
1281 fExtensions.gui->set_scale(fPlugin, opts.uiScale);
1282 #endif
1284 clap_window_t win = { CLAP_WINDOW_API_NATIVE, {} };
1285 win.ptr = ptr;
1287 fExtensions.gui->set_parent(fPlugin, &win);
1289 uint32_t width, height;
1290 if (fExtensions.gui->get_size(fPlugin, &width, &height))
1292 fUI.isResizingFromInit = true;
1293 fUI.width = width;
1294 fUI.height = height;
1295 pData->engine->callback(true, true,
1296 ENGINE_CALLBACK_EMBED_UI_RESIZED,
1297 pData->id, width, height,
1298 0, 0.0f, nullptr);
1301 fExtensions.gui->show(fPlugin);
1303 return nullptr;
1305 #endif
1307 void idle() override
1309 if (kEngineHasIdleOnMainThread)
1310 runIdleCallbacksAsNeeded(true);
1312 CarlaPlugin::idle();
1315 void uiIdle() override
1317 #ifdef CLAP_WINDOW_API_NATIVE
1318 if (fUI.shouldClose)
1320 fUI.shouldClose = false;
1321 fUI.isResizingFromHost = fUI.isResizingFromInit = false;
1322 fUI.isResizingFromPlugin = 0;
1324 showCustomUI(false);
1325 pData->engine->callback(true, true,
1326 ENGINE_CALLBACK_UI_STATE_CHANGED,
1327 pData->id,
1329 0, 0, 0.0f, nullptr);
1332 if (fUI.isResizingFromHost)
1334 fUI.isResizingFromHost = false;
1336 if (fUI.isResizingFromPlugin == 0 && !fUI.isResizingFromInit == false)
1338 carla_stdout("Host resize restarted");
1339 fExtensions.gui->set_size(fPlugin, fUI.width, fUI.height);
1343 if (fUI.window != nullptr)
1344 fUI.window->idle();
1346 if (fUI.isResizingFromPlugin == 2)
1348 fUI.isResizingFromPlugin = 1;
1350 else if (fUI.isResizingFromPlugin == 1)
1352 fUI.isResizingFromPlugin = 0;
1353 carla_stdout("Plugin resize stopped");
1355 #endif
1357 if (!kEngineHasIdleOnMainThread)
1358 runIdleCallbacksAsNeeded(true);
1360 CarlaPlugin::uiIdle();
1363 // -------------------------------------------------------------------
1364 // Plugin state
1366 void reload() override
1368 CARLA_SAFE_ASSERT_RETURN(pData->engine != nullptr,);
1369 CARLA_SAFE_ASSERT_RETURN(fPlugin != nullptr,);
1370 carla_debug("CarlaPluginCLAP::reload() - start");
1372 // Safely disable plugin for reload
1373 const ScopedDisabler sd(this);
1375 if (pData->active)
1376 deactivate();
1378 clearBuffers();
1380 const clap_plugin_audio_ports_t* audioPortsExt = static_cast<const clap_plugin_audio_ports_t*>(
1381 fPlugin->get_extension(fPlugin, CLAP_EXT_AUDIO_PORTS));
1383 const clap_plugin_latency_t* latencyExt = static_cast<const clap_plugin_latency_t*>(
1384 fPlugin->get_extension(fPlugin, CLAP_EXT_LATENCY));
1386 const clap_plugin_note_ports_t* notePortsExt = static_cast<const clap_plugin_note_ports_t*>(
1387 fPlugin->get_extension(fPlugin, CLAP_EXT_NOTE_PORTS));
1389 const clap_plugin_params_t* paramsExt = static_cast<const clap_plugin_params_t*>(
1390 fPlugin->get_extension(fPlugin, CLAP_EXT_PARAMS));
1392 const clap_plugin_state_t* stateExt = static_cast<const clap_plugin_state_t*>(
1393 fPlugin->get_extension(fPlugin, CLAP_EXT_STATE));
1395 const clap_plugin_timer_support_t* timerExt = static_cast<const clap_plugin_timer_support_t*>(
1396 fPlugin->get_extension(fPlugin, CLAP_EXT_TIMER_SUPPORT));
1398 if (audioPortsExt != nullptr && (audioPortsExt->count == nullptr || audioPortsExt->get == nullptr))
1399 audioPortsExt = nullptr;
1401 if (latencyExt != nullptr && latencyExt->get == nullptr)
1402 latencyExt = nullptr;
1404 if (notePortsExt != nullptr && (notePortsExt->count == nullptr || notePortsExt->get == nullptr))
1405 notePortsExt = nullptr;
1407 if (paramsExt != nullptr && (paramsExt->count == nullptr || paramsExt->get_info == nullptr))
1408 paramsExt = nullptr;
1410 if (stateExt != nullptr && (stateExt->save == nullptr || stateExt->load == nullptr))
1411 stateExt = nullptr;
1413 if (timerExt != nullptr && timerExt->on_timer == nullptr)
1414 timerExt = nullptr;
1416 fExtensions.latency = latencyExt;
1417 fExtensions.params = paramsExt;
1418 fExtensions.state = stateExt;
1419 fExtensions.timer = timerExt;
1421 #ifdef CLAP_WINDOW_API_NATIVE
1422 const clap_plugin_gui_t* guiExt = static_cast<const clap_plugin_gui_t*>(
1423 fPlugin->get_extension(fPlugin, CLAP_EXT_GUI));
1425 if (guiExt != nullptr && (guiExt->is_api_supported == nullptr
1426 || guiExt->create == nullptr
1427 || guiExt->destroy == nullptr
1428 || guiExt->set_scale == nullptr
1429 || guiExt->get_size == nullptr
1430 || guiExt->can_resize == nullptr
1431 || guiExt->get_resize_hints == nullptr
1432 || guiExt->adjust_size == nullptr
1433 || guiExt->set_size == nullptr
1434 || guiExt->set_parent == nullptr
1435 || guiExt->set_transient == nullptr
1436 || guiExt->suggest_title == nullptr
1437 || guiExt->show == nullptr
1438 || guiExt->hide == nullptr))
1439 guiExt = nullptr;
1441 fExtensions.gui = guiExt;
1442 #endif
1444 #if defined(CLAP_WINDOW_API_NATIVE) && defined(_POSIX_VERSION)
1445 const clap_plugin_posix_fd_support_t* posixFdExt = static_cast<const clap_plugin_posix_fd_support_t*>(
1446 fPlugin->get_extension(fPlugin, CLAP_EXT_POSIX_FD_SUPPORT));
1448 if (posixFdExt != nullptr && posixFdExt->on_fd == nullptr)
1449 posixFdExt = nullptr;
1451 fExtensions.posixFD = posixFdExt;
1452 #endif
1454 const uint32_t numAudioInputPorts = audioPortsExt != nullptr ? audioPortsExt->count(fPlugin, true) : 0;
1455 const uint32_t numAudioOutputPorts = audioPortsExt != nullptr ? audioPortsExt->count(fPlugin, false) : 0;
1456 const uint32_t numNoteInputPorts = notePortsExt != nullptr ? notePortsExt->count(fPlugin, true) : 0;
1457 const uint32_t numNoteOutputPorts = notePortsExt != nullptr ? notePortsExt->count(fPlugin, false) : 0;
1458 const uint32_t numParameters = paramsExt != nullptr ? paramsExt->count(fPlugin) : 0;
1460 uint32_t aIns, aOuts, mIns, mOuts, params;
1461 aIns = aOuts = mIns = mOuts = params = 0;
1463 bool needsCtrlIn, needsCtrlOut;
1464 needsCtrlIn = needsCtrlOut = false;
1466 fInputAudioBuffers.realloc(numAudioInputPorts);
1467 fOutputAudioBuffers.realloc(numAudioOutputPorts);
1469 for (uint32_t i=0; i<numAudioInputPorts; ++i)
1471 clap_audio_port_info_t portInfo = {};
1472 CARLA_SAFE_ASSERT_BREAK(audioPortsExt->get(fPlugin, i, true, &portInfo));
1473 CARLA_SAFE_ASSERT(portInfo.channel_count != 0);
1475 fInputAudioBuffers.buffers[i].channel_count = portInfo.channel_count;
1476 fInputAudioBuffers.extra[i].offset = aIns;
1477 fInputAudioBuffers.extra[i].isMain = portInfo.flags & CLAP_AUDIO_PORT_IS_MAIN;
1479 aIns += portInfo.channel_count;
1482 for (uint32_t i=0; i<numAudioOutputPorts; ++i)
1484 clap_audio_port_info_t portInfo = {};
1485 CARLA_SAFE_ASSERT_BREAK(audioPortsExt->get(fPlugin, i, false, &portInfo));
1486 CARLA_SAFE_ASSERT(portInfo.channel_count != 0);
1488 fOutputAudioBuffers.buffers[i].channel_count = portInfo.channel_count;
1489 fOutputAudioBuffers.extra[i].offset = aOuts;
1490 fOutputAudioBuffers.extra[i].isMain = portInfo.flags & CLAP_AUDIO_PORT_IS_MAIN;
1491 for (uint32_t j=0; j<portInfo.channel_count; ++j)
1492 fOutputAudioBuffers.buffers[i].constant_mask |= (1ULL << j);
1494 aOuts += portInfo.channel_count;
1497 for (uint32_t i=0; i<numNoteInputPorts; ++i)
1499 clap_note_port_info_t portInfo = {};
1500 CARLA_SAFE_ASSERT_BREAK(notePortsExt->get(fPlugin, i, true, &portInfo));
1502 if (portInfo.supported_dialects & (CLAP_NOTE_DIALECT_CLAP|CLAP_NOTE_DIALECT_MIDI))
1503 ++mIns;
1506 for (uint32_t i=0; i<numNoteOutputPorts; ++i)
1508 clap_note_port_info_t portInfo = {};
1509 CARLA_SAFE_ASSERT_BREAK(notePortsExt->get(fPlugin, i, false, &portInfo));
1511 if (portInfo.supported_dialects & CLAP_NOTE_DIALECT_MIDI)
1512 ++mOuts;
1515 for (uint32_t i=0; i<numParameters; ++i, ++params)
1517 clap_param_info_t paramInfo = {};
1518 CARLA_SAFE_ASSERT_BREAK(paramsExt->get_info(fPlugin, i, &paramInfo));
1521 if (aIns > 0)
1523 pData->audioIn.createNew(aIns);
1526 if (aOuts > 0)
1528 pData->audioOut.createNew(aOuts);
1529 needsCtrlIn = true;
1531 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
1532 fAudioOutBuffers = new float*[aOuts];
1533 for (uint32_t i=0; i < aOuts; ++i)
1534 fAudioOutBuffers[i] = nullptr;
1535 #endif
1538 if (mIns == 1)
1539 needsCtrlIn = true;
1541 if (mOuts == 1)
1542 needsCtrlOut = true;
1544 if (params > 0)
1546 pData->param.createNew(params, false);
1547 needsCtrlIn = true;
1550 fInputEvents.realloc(pData->event.portIn, mIns, params);
1551 fOutputEvents.realloc(pData->event.portOut, mOuts, params);
1553 const EngineProcessMode processMode = pData->engine->getProccessMode();
1554 const uint portNameSize = pData->engine->getMaxPortNameSize();
1555 CarlaString portName;
1557 // Audio Ins
1558 for (uint32_t j=0; j < aIns; ++j)
1560 portName.clear();
1562 if (processMode == ENGINE_PROCESS_MODE_SINGLE_CLIENT)
1564 portName = pData->name;
1565 portName += ":";
1568 if (aIns > 1)
1570 portName += "input_";
1571 portName += CarlaString(j+1);
1573 else
1574 portName += "input";
1576 portName.truncate(portNameSize);
1578 pData->audioIn.ports[j].port = (CarlaEngineAudioPort*)pData->client->addPort(kEnginePortTypeAudio,
1579 portName, true, j);
1580 pData->audioIn.ports[j].rindex = j;
1583 // Audio Outs
1584 for (uint32_t j=0; j < aOuts; ++j)
1586 portName.clear();
1588 if (processMode == ENGINE_PROCESS_MODE_SINGLE_CLIENT)
1590 portName = pData->name;
1591 portName += ":";
1594 if (aOuts > 1)
1596 portName += "output_";
1597 portName += CarlaString(j+1);
1599 else
1600 portName += "output";
1602 portName.truncate(portNameSize);
1604 pData->audioOut.ports[j].port = (CarlaEngineAudioPort*)pData->client->addPort(kEnginePortTypeAudio,
1605 portName, false, j);
1606 pData->audioOut.ports[j].rindex = j;
1609 // MIDI Ins
1610 for (uint32_t i=0, j=0; i<numNoteInputPorts; ++i)
1612 clap_note_port_info_t portInfo = {};
1613 CARLA_SAFE_ASSERT_BREAK(notePortsExt->get(fPlugin, i, true, &portInfo));
1615 if ((portInfo.supported_dialects & (CLAP_NOTE_DIALECT_CLAP|CLAP_NOTE_DIALECT_MIDI)) == 0x0)
1616 continue;
1618 CARLA_SAFE_ASSERT_UINT2_BREAK(j < mIns, j, mIns);
1620 fInputEvents.portData[j].clapPortIndex = i;
1621 fInputEvents.portData[j].supportedDialects = portInfo.supported_dialects;
1623 if (mIns > 1)
1625 portName.clear();
1627 if (processMode == ENGINE_PROCESS_MODE_SINGLE_CLIENT)
1629 portName = pData->name;
1630 portName += ":";
1633 portName += portInfo.name;
1634 portName.truncate(portNameSize);
1635 fInputEvents.portData[j].port = (CarlaEngineEventPort*)pData->client->addPort(kEnginePortTypeEvent,
1636 portName, true, j);
1638 else
1640 fInputEvents.portData[j].port = nullptr;
1641 fInputEvents.defaultPort = &fInputEvents.portData[0];
1644 ++j;
1647 // MIDI Outs
1648 for (uint32_t i=0, j=0; i<numNoteOutputPorts; ++i)
1650 clap_note_port_info_t portInfo = {};
1651 CARLA_SAFE_ASSERT_BREAK(notePortsExt->get(fPlugin, i, false, &portInfo));
1653 if ((portInfo.supported_dialects & CLAP_NOTE_DIALECT_MIDI) == 0x0)
1654 continue;
1656 CARLA_SAFE_ASSERT_UINT2_BREAK(j < mOuts, j, mOuts);
1658 fOutputEvents.portData[j].clapPortIndex = i;
1659 fOutputEvents.portData[j].supportedDialects = portInfo.supported_dialects;
1661 if (mOuts > 1)
1663 portName.clear();
1665 if (processMode == ENGINE_PROCESS_MODE_SINGLE_CLIENT)
1667 portName = pData->name;
1668 portName += ":";
1671 portName += portInfo.name;
1672 portName.truncate(portNameSize);
1673 fOutputEvents.portData[j].port = (CarlaEngineEventPort*)pData->client->addPort(kEnginePortTypeEvent,
1674 portName, false, j);
1676 else
1678 fOutputEvents.portData[j].port = nullptr;
1679 fOutputEvents.defaultPort = &fOutputEvents.portData[0];
1682 ++j;
1685 // Parameters
1686 for (uint32_t i=0; i<numParameters; ++i)
1688 clap_param_info_t paramInfo = {};
1689 CARLA_SAFE_ASSERT_BREAK(paramsExt->get_info(fPlugin, i, &paramInfo));
1690 CARLA_SAFE_ASSERT_BREAK(i < params);
1692 pData->param.data[i].index = i;
1693 pData->param.data[i].rindex = static_cast<int32_t>(paramInfo.id);
1695 double min, max, def, step, stepSmall, stepLarge;
1697 min = paramInfo.min_value;
1698 max = paramInfo.max_value;
1699 def = paramInfo.default_value;
1701 if (min >= max)
1702 max = min + 0.1;
1704 if (def < min)
1705 def = min;
1706 else if (def > max)
1707 def = max;
1709 if (paramInfo.flags & (CLAP_PARAM_IS_HIDDEN|CLAP_PARAM_IS_BYPASS))
1711 pData->param.data[i].type = PARAMETER_UNKNOWN;
1713 else if (paramInfo.flags & CLAP_PARAM_IS_READONLY)
1715 pData->param.data[i].type = PARAMETER_OUTPUT;
1716 needsCtrlOut = true;
1718 else
1720 pData->param.data[i].type = PARAMETER_INPUT;
1723 if (paramInfo.flags & CLAP_PARAM_IS_STEPPED)
1725 if (carla_isEqual(max - min, 1.0))
1727 step = stepSmall = stepLarge = 1.0;
1728 pData->param.data[i].hints |= PARAMETER_IS_BOOLEAN;
1730 else
1732 step = 1.0;
1733 stepSmall = 1.0;
1734 stepLarge = std::min(max - min, 10.0);
1736 pData->param.data[i].hints |= PARAMETER_IS_INTEGER;
1738 else
1740 double range = max - min;
1741 step = range/100.0;
1742 stepSmall = range/1000.0;
1743 stepLarge = range/10.0;
1746 if (pData->param.data[i].type != PARAMETER_UNKNOWN)
1748 pData->param.data[i].hints |= PARAMETER_IS_ENABLED;
1749 pData->param.data[i].hints |= PARAMETER_USES_CUSTOM_TEXT;
1751 if (paramInfo.flags & CLAP_PARAM_IS_AUTOMATABLE)
1753 pData->param.data[i].hints |= PARAMETER_IS_AUTOMATABLE;
1755 if ((paramInfo.flags & CLAP_PARAM_IS_STEPPED) == 0x0)
1756 pData->param.data[i].hints |= PARAMETER_CAN_BE_CV_CONTROLLED;
1760 pData->param.ranges[i].min = min;
1761 pData->param.ranges[i].max = max;
1762 pData->param.ranges[i].def = def;
1763 pData->param.ranges[i].step = step;
1764 pData->param.ranges[i].stepSmall = stepSmall;
1765 pData->param.ranges[i].stepLarge = stepLarge;
1767 fInputEvents.updatedParams[i].clapId = paramInfo.id;
1768 fInputEvents.updatedParams[i].cookie = paramInfo.cookie;
1771 if (needsCtrlIn)
1773 portName.clear();
1775 if (processMode == ENGINE_PROCESS_MODE_SINGLE_CLIENT)
1777 portName = pData->name;
1778 portName += ":";
1781 portName += "events-in";
1782 portName.truncate(portNameSize);
1784 pData->event.portIn = (CarlaEngineEventPort*)pData->client->addPort(kEnginePortTypeEvent,
1785 portName, true, 0);
1786 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
1787 pData->event.cvSourcePorts = pData->client->createCVSourcePorts();
1788 #endif
1790 if (mIns == 1)
1791 fInputEvents.portData[0].port = pData->event.portIn;
1794 if (needsCtrlOut)
1796 portName.clear();
1798 if (processMode == ENGINE_PROCESS_MODE_SINGLE_CLIENT)
1800 portName = pData->name;
1801 portName += ":";
1804 portName += "events-out";
1805 portName.truncate(portNameSize);
1807 pData->event.portOut = (CarlaEngineEventPort*)pData->client->addPort(kEnginePortTypeEvent,
1808 portName, false, 0);
1810 if (mOuts == 1)
1811 fOutputEvents.portData[0].port = pData->event.portOut;
1814 // plugin hints
1815 pData->hints = PLUGIN_NEEDS_MAIN_THREAD_IDLE;
1817 if (clapFeaturesContainInstrument(fPluginDescriptor->features))
1818 pData->hints |= PLUGIN_IS_SYNTH;
1820 #ifdef CLAP_WINDOW_API_NATIVE
1821 if (guiExt != nullptr)
1823 if (guiExt->is_api_supported(fPlugin, CLAP_WINDOW_API_NATIVE, false))
1825 pData->hints |= PLUGIN_HAS_CUSTOM_UI;
1826 pData->hints |= PLUGIN_HAS_CUSTOM_EMBED_UI;
1827 pData->hints |= PLUGIN_NEEDS_UI_MAIN_THREAD;
1829 else if (guiExt->is_api_supported(fPlugin, CLAP_WINDOW_API_NATIVE, true))
1831 pData->hints |= PLUGIN_HAS_CUSTOM_UI;
1832 pData->hints |= PLUGIN_NEEDS_UI_MAIN_THREAD;
1835 #endif
1837 if (aOuts > 0 && (aIns == aOuts || aIns == 1))
1838 pData->hints |= PLUGIN_CAN_DRYWET;
1840 if (aOuts > 0)
1841 pData->hints |= PLUGIN_CAN_VOLUME;
1843 if (aOuts >= 2 && aOuts % 2 == 0)
1844 pData->hints |= PLUGIN_CAN_BALANCE;
1846 // extra plugin hints
1847 pData->extraHints = 0x0;
1849 if (const uint32_t latency = fExtensions.latency != nullptr ? fExtensions.latency->get(fPlugin) : 0)
1851 fLastKnownLatency = latency;
1852 pData->client->setLatency(latency);
1853 #ifndef BUILD_BRIDGE
1854 pData->latency.recreateBuffers(std::max(aIns, aOuts), latency);
1855 #endif
1857 else
1859 fLastKnownLatency = 0;
1862 bufferSizeChanged(pData->engine->getBufferSize());
1863 reloadPrograms(true);
1865 if (pData->active)
1866 activate();
1867 else
1868 runIdleCallbacksAsNeeded(false);
1870 carla_debug("CarlaPluginCLAP::reload() - end");
1873 void reloadPrograms(const bool doInit) override
1875 carla_debug("CarlaPluginCLAP::reloadPrograms(%s)", bool2str(doInit));
1878 // -------------------------------------------------------------------
1879 // Plugin processing
1881 void activate() noexcept override
1883 CARLA_SAFE_ASSERT_RETURN(fPlugin != nullptr,);
1885 // FIXME check return status
1886 fPlugin->activate(fPlugin, pData->engine->getSampleRate(), 1, pData->engine->getBufferSize());
1887 fPlugin->start_processing(fPlugin);
1889 fNeedsParamFlush = false;
1890 runIdleCallbacksAsNeeded(false);
1893 void deactivate() noexcept override
1895 CARLA_SAFE_ASSERT_RETURN(fPlugin != nullptr,);
1897 // FIXME check return status
1898 fPlugin->stop_processing(fPlugin);
1899 fPlugin->deactivate(fPlugin);
1901 runIdleCallbacksAsNeeded(false);
1904 void process(const float* const* const audioIn,
1905 float** const audioOut,
1906 const float* const* const cvIn,
1907 float** const,
1908 const uint32_t frames) override
1910 // --------------------------------------------------------------------------------------------------------
1911 // Check if active
1913 if (! pData->active)
1915 // disable any output sound
1916 for (uint32_t i=0; i < pData->audioOut.count; ++i)
1917 carla_zeroFloats(audioOut[i], frames);
1918 return;
1921 // --------------------------------------------------------------------------------------------------------
1922 // Check buffers
1924 CARLA_SAFE_ASSERT_RETURN(frames > 0,);
1926 if (pData->audioIn.count > 0)
1928 CARLA_SAFE_ASSERT_RETURN(audioIn != nullptr,);
1930 if (pData->audioOut.count > 0)
1932 CARLA_SAFE_ASSERT_RETURN(audioOut != nullptr,);
1933 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
1934 CARLA_SAFE_ASSERT_RETURN(fAudioOutBuffers != nullptr,);
1935 #endif
1938 // --------------------------------------------------------------------------------------------------------
1939 // Set audio buffers
1941 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
1942 for (uint32_t i=0; i < pData->audioOut.count; ++i)
1943 carla_zeroFloats(fAudioOutBuffers[i], frames);
1944 #endif
1946 // --------------------------------------------------------------------------------------------------------
1947 // Try lock, silence otherwise
1949 if (pData->engine->isOffline())
1951 pData->singleMutex.lock();
1953 else if (! pData->singleMutex.tryLock())
1955 for (uint32_t i=0; i < pData->audioOut.count; ++i)
1956 carla_zeroFloats(audioOut[i], frames);
1957 return;
1960 // --------------------------------------------------------------------------------------------------------
1962 fInputEvents.handleScheduledParameterUpdates();
1964 // --------------------------------------------------------------------------------------------------------
1965 // Check if needs reset
1967 if (pData->needsReset)
1969 // TODO alternative if plugin does not support CLAP_NOTE_DIALECT_MIDI
1971 if (pData->options & PLUGIN_OPTION_SEND_ALL_SOUND_OFF)
1973 for (uint32_t p=0; p<fInputEvents.portCount; ++p)
1975 const uint16_t port = fInputEvents.portData[p].clapPortIndex;
1977 for (uint8_t i=0, k=fInputEvents.numEventsUsed; i < MAX_MIDI_CHANNELS; ++i)
1979 fInputEvents.events[k + i].midi = {
1980 { sizeof(clap_event_midi_t), 0, 0, CLAP_EVENT_MIDI, 0 },
1981 port,
1983 uint8_t(MIDI_STATUS_CONTROL_CHANGE | (i & MIDI_CHANNEL_BIT)),
1984 MIDI_CONTROL_ALL_NOTES_OFF, 0
1987 fInputEvents.events[k + MAX_MIDI_CHANNELS + i].midi = {
1988 { sizeof(clap_event_midi_t), 0, 0, CLAP_EVENT_MIDI, 0 },
1989 port,
1991 uint8_t(MIDI_STATUS_CONTROL_CHANGE | (i & MIDI_CHANNEL_BIT)),
1992 MIDI_CONTROL_ALL_SOUND_OFF, 0
1997 fInputEvents.numEventsUsed += MAX_MIDI_CHANNELS * 2;
2000 else if (pData->ctrlChannel >= 0 && pData->ctrlChannel < MAX_MIDI_CHANNELS)
2002 for (uint32_t p=0; p<fInputEvents.portCount; ++p)
2004 const uint16_t port = fInputEvents.portData[p].clapPortIndex;
2006 for (uint8_t i=0, k=fInputEvents.numEventsUsed; i < MAX_MIDI_NOTE; ++i)
2008 fInputEvents.events[k + i].midi = {
2009 { sizeof(clap_event_midi_t), 0, 0, CLAP_EVENT_MIDI, 0 },
2010 port,
2012 uint8_t(MIDI_STATUS_NOTE_OFF | (pData->ctrlChannel & MIDI_CHANNEL_BIT)),
2013 i, 0
2018 fInputEvents.numEventsUsed += MAX_MIDI_NOTE;
2022 pData->needsReset = false;
2025 // --------------------------------------------------------------------------------------------------------
2026 // Set TimeInfo
2028 const EngineTimeInfo timeInfo(pData->engine->getTimeInfo());
2030 const double sampleRate = pData->engine->getSampleRate();
2032 clap_event_transport_t clapTransport = {
2033 { sizeof(clap_event_transport_t), 0, 0, CLAP_EVENT_TRANSPORT, 0 },
2034 0x0, // flags
2035 0, // song_pos_beats, position in beats
2036 0, // song_pos_seconds, position in seconds
2037 0.0, // tempo, in bpm
2038 0.0, // tempo_inc, tempo increment for each samples and until the next time info event
2039 0, // loop_start_beats
2040 0, // loop_end_beats
2041 0, // loop_start_seconds
2042 0, // loop_end_seconds
2043 0, // bar_start, start pos of the current bar
2044 0, // bar_number, bar at song pos 0 has the number 0
2045 0, // tsig_num, time signature numerator
2046 0, // tsig_denom, time signature denominator
2049 if (timeInfo.playing)
2050 clapTransport.flags |= CLAP_TRANSPORT_IS_PLAYING;
2052 const double positionSeconds = static_cast<double>(timeInfo.frame) / sampleRate;
2053 clapTransport.song_pos_seconds = std::round(CLAP_SECTIME_FACTOR * positionSeconds);
2054 clapTransport.flags |= CLAP_TRANSPORT_HAS_SECONDS_TIMELINE;
2056 if (timeInfo.bbt.valid)
2058 CARLA_SAFE_ASSERT_INT(timeInfo.bbt.bar > 0, timeInfo.bbt.bar);
2059 CARLA_SAFE_ASSERT_INT(timeInfo.bbt.beat > 0, timeInfo.bbt.beat);
2061 const double barStart = static_cast<double>(timeInfo.bbt.beatsPerBar) * (timeInfo.bbt.bar - 1);
2062 const double positionBeats = static_cast<double>(timeInfo.frame)
2063 / (sampleRate * 60 / timeInfo.bbt.beatsPerMinute);
2065 // Bar/Beats
2066 clapTransport.bar_start = std::round(CLAP_BEATTIME_FACTOR * barStart);
2067 clapTransport.bar_number = timeInfo.bbt.bar - 1;
2068 clapTransport.song_pos_beats = std::round(CLAP_BEATTIME_FACTOR * positionBeats);
2069 clapTransport.flags |= CLAP_TRANSPORT_HAS_BEATS_TIMELINE;
2071 // Tempo
2072 clapTransport.tempo = timeInfo.bbt.beatsPerMinute;
2073 clapTransport.flags |= CLAP_TRANSPORT_HAS_TEMPO;
2075 // Time Signature
2076 clapTransport.tsig_num = static_cast<uint16_t>(timeInfo.bbt.beatsPerBar + 0.5f);
2077 clapTransport.tsig_denom = static_cast<uint16_t>(timeInfo.bbt.beatType + 0.5f);
2078 clapTransport.flags |= CLAP_TRANSPORT_HAS_TIME_SIGNATURE;
2080 else
2082 // Tempo
2083 clapTransport.tempo = 120.0;
2084 clapTransport.flags |= CLAP_TRANSPORT_HAS_TEMPO;
2086 // Time Signature
2087 clapTransport.tsig_num = 4;
2088 clapTransport.tsig_denom = 4;
2089 clapTransport.flags |= CLAP_TRANSPORT_HAS_TIME_SIGNATURE;
2092 // --------------------------------------------------------------------------------------------------------
2093 // Event Input (main port)
2095 if (pData->event.portIn != nullptr)
2097 // ----------------------------------------------------------------------------------------------------
2098 // MIDI Input (External)
2100 if (pData->extNotes.mutex.tryLock())
2102 if (fInputEvents.portCount == 0)
2104 // does not handle MIDI
2105 pData->extNotes.data.clear();
2107 else
2109 ExternalMidiNote note = { -1, 0, 0 };
2110 const uint16_t p = fInputEvents.portData[0].clapPortIndex;
2112 for (; fInputEvents.numEventsUsed < fInputEvents.numEventsAllocated && ! pData->extNotes.data.isEmpty();)
2114 note = pData->extNotes.data.getFirst(note, true);
2116 CARLA_SAFE_ASSERT_CONTINUE(note.channel >= 0 && note.channel < MAX_MIDI_CHANNELS);
2118 if (fInputEvents.portData[0].supportedDialects & CLAP_NOTE_DIALECT_MIDI)
2120 const uint8_t data[3] = {
2121 uint8_t((note.velo > 0 ? MIDI_STATUS_NOTE_ON : MIDI_STATUS_NOTE_OFF) | (note.channel & MIDI_CHANNEL_BIT)),
2122 note.note,
2123 note.velo
2125 fInputEvents.addSimpleMidiEvent(true, p, 0, data);
2127 else
2129 fInputEvents.addSimpleNoteEvent(true, -1, 0, note.channel, note.note, note.velo);
2134 pData->extNotes.mutex.unlock();
2136 } // End of MIDI Input (External)
2138 // ----------------------------------------------------------------------------------------------------
2139 // Event Input (System)
2141 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
2142 bool allNotesOffSent = false;
2143 #endif
2144 uint32_t previousEventTime = 0;
2145 uint32_t nextBankId;
2147 if (pData->midiprog.current >= 0 && pData->midiprog.count > 0)
2148 nextBankId = pData->midiprog.data[pData->midiprog.current].bank;
2149 else
2150 nextBankId = 0;
2152 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
2153 if (cvIn != nullptr && pData->event.cvSourcePorts != nullptr)
2154 pData->event.cvSourcePorts->initPortBuffers(cvIn + pData->cvIn.count, frames, true, pData->event.portIn);
2155 #endif
2157 const uint32_t numSysEvents = pData->event.portIn->getEventCount();
2159 for (uint32_t i=0; i < numSysEvents; ++i)
2161 EngineEvent& event(pData->event.portIn->getEvent(i));
2163 uint32_t eventTime = event.time;
2164 CARLA_SAFE_ASSERT_UINT2_CONTINUE(eventTime < frames, eventTime, frames);
2166 if (eventTime < previousEventTime)
2168 carla_stderr2("Timing error, eventTime:%u < previousEventTime:%u for '%s'",
2169 eventTime, previousEventTime, pData->name);
2170 eventTime = previousEventTime;
2173 previousEventTime = eventTime;
2175 switch (event.type)
2177 case kEngineEventTypeNull:
2178 break;
2180 case kEngineEventTypeControl: {
2181 EngineControlEvent& ctrlEvent(event.ctrl);
2183 switch (ctrlEvent.type)
2185 case kEngineControlEventTypeNull:
2186 break;
2188 case kEngineControlEventTypeParameter: {
2189 float value;
2191 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
2192 // non-midi
2193 if (event.channel == kEngineEventNonMidiChannel)
2195 const uint32_t k = ctrlEvent.param;
2196 CARLA_SAFE_ASSERT_CONTINUE(k < pData->param.count);
2198 ctrlEvent.handled = true;
2199 value = pData->param.getFinalUnnormalizedValue(k, ctrlEvent.normalizedValue);
2200 setParameterValueRT(k, value, event.time, true);
2201 continue;
2204 // Control backend stuff
2205 if (event.channel == pData->ctrlChannel)
2207 if (MIDI_IS_CONTROL_BREATH_CONTROLLER(ctrlEvent.param) && (pData->hints & PLUGIN_CAN_DRYWET) != 0)
2209 ctrlEvent.handled = true;
2210 value = ctrlEvent.normalizedValue;
2211 setDryWetRT(value, true);
2213 else if (MIDI_IS_CONTROL_CHANNEL_VOLUME(ctrlEvent.param) && (pData->hints & PLUGIN_CAN_VOLUME) != 0)
2215 ctrlEvent.handled = true;
2216 value = ctrlEvent.normalizedValue*127.0f/100.0f;
2217 setVolumeRT(value, true);
2219 else if (MIDI_IS_CONTROL_BALANCE(ctrlEvent.param) && (pData->hints & PLUGIN_CAN_BALANCE) != 0)
2221 float left, right;
2222 value = ctrlEvent.normalizedValue/0.5f - 1.0f;
2224 if (value < 0.0f)
2226 left = -1.0f;
2227 right = (value*2.0f)+1.0f;
2229 else if (value > 0.0f)
2231 left = (value*2.0f)-1.0f;
2232 right = 1.0f;
2234 else
2236 left = -1.0f;
2237 right = 1.0f;
2240 ctrlEvent.handled = true;
2241 setBalanceLeftRT(left, true);
2242 setBalanceRightRT(right, true);
2245 #endif
2246 // Control plugin parameters
2247 uint32_t k;
2248 for (k=0; k < pData->param.count; ++k)
2250 if (pData->param.data[k].midiChannel != event.channel)
2251 continue;
2252 if (pData->param.data[k].mappedControlIndex != ctrlEvent.param)
2253 continue;
2254 if (pData->param.data[k].type != PARAMETER_INPUT)
2255 continue;
2256 if ((pData->param.data[k].hints & PARAMETER_IS_AUTOMATABLE) == 0)
2257 continue;
2259 ctrlEvent.handled = true;
2260 value = pData->param.getFinalUnnormalizedValue(k, ctrlEvent.normalizedValue);
2261 setParameterValueRT(k, value, event.time, true);
2264 if ((pData->options & PLUGIN_OPTION_SEND_CONTROL_CHANGES) != 0 && ctrlEvent.param < MAX_MIDI_VALUE)
2266 uint8_t midiData[3];
2267 midiData[0] = uint8_t(MIDI_STATUS_CONTROL_CHANGE | (event.channel & MIDI_CHANNEL_BIT));
2268 midiData[1] = uint8_t(ctrlEvent.param);
2269 midiData[2] = uint8_t(ctrlEvent.normalizedValue*127.0f + 0.5f);
2271 fInputEvents.addSimpleMidiEvent(true, 0, eventTime, midiData);
2274 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
2275 if (! ctrlEvent.handled)
2276 checkForMidiLearn(event);
2277 #endif
2278 break;
2279 } // case kEngineControlEventTypeParameter
2281 case kEngineControlEventTypeMidiBank:
2282 if (pData->options & PLUGIN_OPTION_MAP_PROGRAM_CHANGES)
2284 if (event.channel == pData->ctrlChannel)
2285 nextBankId = ctrlEvent.param;
2287 else if (pData->options & PLUGIN_OPTION_SEND_PROGRAM_CHANGES)
2289 uint8_t midiData[3];
2290 midiData[0] = uint8_t(MIDI_STATUS_CONTROL_CHANGE | (event.channel & MIDI_CHANNEL_BIT));
2291 midiData[1] = MIDI_CONTROL_BANK_SELECT;
2292 midiData[2] = uint8_t(ctrlEvent.param);
2294 fInputEvents.addSimpleMidiEvent(true, 0, eventTime, midiData);
2296 break;
2298 case kEngineControlEventTypeMidiProgram:
2299 if (pData->options & PLUGIN_OPTION_MAP_PROGRAM_CHANGES)
2301 if (event.channel == pData->ctrlChannel)
2303 const uint32_t nextProgramId(ctrlEvent.param);
2305 for (uint32_t k=0; k < pData->midiprog.count; ++k)
2307 if (pData->midiprog.data[k].bank == nextBankId && pData->midiprog.data[k].program == nextProgramId)
2309 setMidiProgramRT(k, true);
2310 break;
2315 else if (pData->options & PLUGIN_OPTION_SEND_PROGRAM_CHANGES)
2317 uint8_t midiData[3];
2318 midiData[0] = uint8_t(MIDI_STATUS_PROGRAM_CHANGE | (event.channel & MIDI_CHANNEL_BIT));
2319 midiData[1] = uint8_t(ctrlEvent.param);
2320 midiData[2] = 0;
2322 fInputEvents.addSimpleMidiEvent(true, 0, eventTime, midiData);
2324 break;
2326 case kEngineControlEventTypeAllSoundOff:
2327 if (pData->options & PLUGIN_OPTION_SEND_ALL_SOUND_OFF)
2329 uint8_t midiData[3];
2330 midiData[0] = uint8_t(MIDI_STATUS_CONTROL_CHANGE | (event.channel & MIDI_CHANNEL_BIT));
2331 midiData[1] = MIDI_CONTROL_ALL_SOUND_OFF;
2332 midiData[2] = 0;
2334 fInputEvents.addSimpleMidiEvent(true, 0, eventTime, midiData);
2336 break;
2338 case kEngineControlEventTypeAllNotesOff:
2339 if (pData->options & PLUGIN_OPTION_SEND_ALL_SOUND_OFF)
2341 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
2342 if (event.channel == pData->ctrlChannel && ! allNotesOffSent)
2344 allNotesOffSent = true;
2345 postponeRtAllNotesOff();
2347 #endif
2349 uint8_t midiData[3];
2350 midiData[0] = uint8_t(MIDI_STATUS_CONTROL_CHANGE | (event.channel & MIDI_CHANNEL_BIT));
2351 midiData[1] = MIDI_CONTROL_ALL_NOTES_OFF;
2352 midiData[2] = 0;
2354 fInputEvents.addSimpleMidiEvent(true, 0, eventTime, midiData);
2356 break;
2357 } // switch (ctrlEvent.type)
2358 break;
2359 } // case kEngineEventTypeControl
2361 case kEngineEventTypeMidi: {
2362 const EngineMidiEvent& midiEvent(event.midi);
2364 if (midiEvent.size > 3)
2365 continue;
2367 CARLA_SAFE_ASSERT_BREAK(midiEvent.port < fInputEvents.portCount);
2369 const uint8_t status = uint8_t(MIDI_GET_STATUS_FROM_DATA(midiEvent.data));
2371 if (status == MIDI_STATUS_NOTE_OFF || status == MIDI_STATUS_NOTE_ON)
2373 if (pData->options & PLUGIN_OPTION_SKIP_SENDING_NOTES)
2374 continue;
2376 // plugin does not support MIDI, send a simple note instead
2377 if ((fInputEvents.portData[midiEvent.port].supportedDialects & CLAP_NOTE_DIALECT_MIDI) == 0x0)
2379 fInputEvents.addSimpleNoteEvent(true, midiEvent.port, event.time,
2380 event.channel, midiEvent.data[1], midiEvent.data[2]);
2384 if (fInputEvents.portData[midiEvent.port].supportedDialects & CLAP_NOTE_DIALECT_MIDI)
2386 if (status == MIDI_STATUS_CHANNEL_PRESSURE && (pData->options & PLUGIN_OPTION_SEND_CHANNEL_PRESSURE) == 0)
2387 continue;
2388 if (status == MIDI_STATUS_CONTROL_CHANGE && (pData->options & PLUGIN_OPTION_SEND_CONTROL_CHANGES) == 0)
2389 continue;
2390 if (status == MIDI_STATUS_POLYPHONIC_AFTERTOUCH && (pData->options & PLUGIN_OPTION_SEND_NOTE_AFTERTOUCH) == 0)
2391 continue;
2392 if (status == MIDI_STATUS_PITCH_WHEEL_CONTROL && (pData->options & PLUGIN_OPTION_SEND_PITCHBEND) == 0)
2393 continue;
2395 // put back channel in data
2396 uint8_t midiData[3] = { uint8_t(status | (event.channel & MIDI_CHANNEL_BIT)), 0, 0 };
2398 switch (midiEvent.size)
2400 case 3:
2401 midiData[2] = midiEvent.data[2];
2402 // fall through
2403 case 2:
2404 midiData[1] = midiEvent.data[1];
2405 break;
2408 fInputEvents.addSimpleMidiEvent(true, midiEvent.port, eventTime, midiData);
2411 switch (status)
2413 case MIDI_STATUS_NOTE_ON:
2414 if (midiEvent.data[2] != 0)
2416 pData->postponeNoteOnRtEvent(true, event.channel, midiEvent.data[1], midiEvent.data[2]);
2417 break;
2419 // fall through
2420 case MIDI_STATUS_NOTE_OFF:
2421 pData->postponeNoteOffRtEvent(true, event.channel, midiEvent.data[1]);
2422 break;
2424 } break;
2425 } // switch (event.type)
2428 pData->postRtEvents.trySplice();
2430 } // End of Event Input (main port)
2432 // --------------------------------------------------------------------------------------------------------
2433 // Event input (multi MIDI port)
2435 if (fInputEvents.portCount > 1)
2437 // TODO
2440 // --------------------------------------------------------------------------------------------------------
2441 // Plugin processing
2443 for (uint32_t i=0; i<fInputAudioBuffers.count; ++i)
2444 fInputAudioBuffers.buffers[i].data32 = audioIn + fInputAudioBuffers.extra[i].offset;
2446 for (uint32_t i=0; i<fOutputAudioBuffers.count; ++i)
2448 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
2449 fOutputAudioBuffers.buffers[i].data32 = fAudioOutBuffers + fOutputAudioBuffers.extra[i].offset;
2450 #else
2451 fOutputAudioBuffers.buffers[i].data32 = audioOut + fOutputAudioBuffers.extra[i].offset;
2452 #endif
2455 const clap_process_t process = {
2456 static_cast<int64_t>(timeInfo.frame),
2457 frames,
2458 &clapTransport,
2459 fInputAudioBuffers.cast(),
2460 fOutputAudioBuffers.buffers,
2461 fInputAudioBuffers.count,
2462 fOutputAudioBuffers.count,
2463 fInputEvents.cast(),
2464 fOutputEvents.cast()
2467 fPlugin->process(fPlugin, &process);
2469 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
2470 // --------------------------------------------------------------------------------------------------------
2471 // Post-processing (dry/wet, volume and balance)
2474 const bool doDryWet = (pData->hints & PLUGIN_CAN_DRYWET) != 0 && carla_isNotEqual(pData->postProc.dryWet, 1.0f);
2475 const bool doBalance = (pData->hints & PLUGIN_CAN_BALANCE) != 0 && ! (carla_isEqual(pData->postProc.balanceLeft, -1.0f) && carla_isEqual(pData->postProc.balanceRight, 1.0f));
2476 const bool isMono = (pData->audioIn.count == 1);
2478 bool isPair;
2479 float bufValue;
2480 float* const oldBufLeft = pData->postProc.extraBuffer;
2482 for (uint32_t i=0; i < pData->audioOut.count; ++i)
2484 // Dry/Wet
2485 if (doDryWet)
2487 const uint32_t c = isMono ? 0 : i;
2489 for (uint32_t k=0; k < frames; ++k)
2491 bufValue = audioIn[c][k];
2492 fAudioOutBuffers[i][k] = (fAudioOutBuffers[i][k] * pData->postProc.dryWet) + (bufValue * (1.0f - pData->postProc.dryWet));
2496 // Balance
2497 if (doBalance)
2499 isPair = (i % 2 == 0);
2501 if (isPair)
2503 CARLA_ASSERT(i+1 < pData->audioOut.count);
2504 carla_copyFloats(oldBufLeft, fAudioOutBuffers[i], frames);
2507 float balRangeL = (pData->postProc.balanceLeft + 1.0f)/2.0f;
2508 float balRangeR = (pData->postProc.balanceRight + 1.0f)/2.0f;
2510 for (uint32_t k=0; k < frames; ++k)
2512 if (isPair)
2514 // left
2515 fAudioOutBuffers[i][k] = oldBufLeft[k] * (1.0f - balRangeL);
2516 fAudioOutBuffers[i][k] += fAudioOutBuffers[i+1][k] * (1.0f - balRangeR);
2518 else
2520 // right
2521 fAudioOutBuffers[i][k] = fAudioOutBuffers[i][k] * balRangeR;
2522 fAudioOutBuffers[i][k] += oldBufLeft[k] * balRangeL;
2527 // Volume (and buffer copy)
2529 for (uint32_t k=0; k < frames; ++k)
2530 audioOut[i][k] = fAudioOutBuffers[i][k] * pData->postProc.volume;
2534 } // End of Post-processing
2535 #endif // BUILD_BRIDGE_ALTERNATIVE_ARCH
2537 // --------------------------------------------------------------------------------------------------------
2539 pData->singleMutex.unlock();
2541 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
2542 // --------------------------------------------------------------------------------------------------------
2543 // Control Output
2545 if (pData->event.portOut != nullptr && fExtensions.params != nullptr)
2547 uint8_t channel;
2548 uint16_t param;
2549 double value;
2551 for (uint32_t k=0; k < pData->param.count; ++k)
2553 if (pData->param.data[k].type != PARAMETER_OUTPUT)
2554 continue;
2555 if (pData->param.data[k].mappedControlIndex <= 0)
2556 continue;
2557 if (!fExtensions.params->get_value(fPlugin, pData->param.data[k].rindex, &value))
2558 continue;
2560 channel = pData->param.data[k].midiChannel;
2561 param = static_cast<uint16_t>(pData->param.data[k].mappedControlIndex);
2562 value = pData->param.ranges[k].getNormalizedValue(value);
2564 pData->event.portOut->writeControlEvent(0, channel,
2565 kEngineControlEventTypeParameter, param, -1,
2566 value);
2568 } // End of Control Output
2569 #endif
2571 // --------------------------------------------------------------------------------------------------------
2572 // Events/MIDI Output
2574 for (uint32_t i=0; i<fOutputEvents.numEventsUsed; ++i)
2576 const carla_clap_output_events::Event& ev(fOutputEvents.events[i]);
2578 switch (ev.header.type)
2580 case CLAP_EVENT_PARAM_VALUE:
2581 for (uint32_t j=0; j<pData->param.count; ++j)
2583 if (pData->param.data[j].rindex != static_cast<int32_t>(ev.param.param_id))
2584 continue;
2585 pData->postponeParameterChangeRtEvent(true, static_cast<int32_t>(j), ev.param.value);
2586 break;
2588 break;
2589 case CLAP_EVENT_MIDI:
2590 for (uint32_t j=0; j<fOutputEvents.portCount; ++j)
2592 if (fOutputEvents.portData[j].clapPortIndex != ev.midi.port_index)
2593 continue;
2594 fOutputEvents.portData[j].port->writeMidiEvent(ev.midi.header.time, 3, ev.midi.data);
2595 break;
2597 break;
2601 fOutputEvents.numEventsUsed = 0;
2603 // --------------------------------------------------------------------------------------------------------
2605 #ifdef BUILD_BRIDGE_ALTERNATIVE_ARCH
2606 return;
2608 // unused
2609 (void)cvIn;
2610 #endif
2613 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
2614 void bufferSizeChanged(const uint32_t newBufferSize) override
2616 CARLA_ASSERT_INT(newBufferSize > 0, newBufferSize);
2617 carla_debug("CarlaPluginCLAP::bufferSizeChanged(%i)", newBufferSize);
2619 if (pData->active)
2620 deactivate();
2622 for (uint32_t i=0; i < pData->audioOut.count; ++i)
2624 if (fAudioOutBuffers[i] != nullptr)
2625 delete[] fAudioOutBuffers[i];
2626 fAudioOutBuffers[i] = new float[newBufferSize];
2629 if (pData->active)
2630 activate();
2632 CarlaPlugin::bufferSizeChanged(newBufferSize);
2634 #endif
2636 // -------------------------------------------------------------------
2637 // Plugin buffers
2639 void initBuffers() const noexcept override
2641 fInputEvents.initBuffers();
2642 fOutputEvents.initBuffers();
2644 CarlaPlugin::initBuffers();
2647 void clearBuffers() noexcept override
2649 carla_debug("CarlaPluginCLAP::clearBuffers() - start");
2651 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
2652 if (fAudioOutBuffers != nullptr)
2654 for (uint32_t i=0; i < pData->audioOut.count; ++i)
2656 if (fAudioOutBuffers[i] != nullptr)
2658 delete[] fAudioOutBuffers[i];
2659 fAudioOutBuffers[i] = nullptr;
2663 delete[] fAudioOutBuffers;
2664 fAudioOutBuffers = nullptr;
2666 #endif
2668 fInputEvents.clear(pData->event.portIn);
2669 fOutputEvents.clear(pData->event.portOut);
2671 CarlaPlugin::clearBuffers();
2673 carla_debug("CarlaPluginCLAP::clearBuffers() - end");
2676 // -------------------------------------------------------------------
2677 // Post-poned UI Stuff
2679 // nothing
2681 // -------------------------------------------------------------------
2683 protected:
2684 #ifdef CLAP_WINDOW_API_NATIVE
2685 void handlePluginUIClosed() override
2687 CARLA_SAFE_ASSERT_RETURN(fUI.window != nullptr,);
2688 carla_stdout("CarlaPluginCLAP::handlePluginUIClosed()");
2690 fUI.shouldClose = true;
2693 void handlePluginUIResized(const uint width, const uint height) override
2695 CARLA_SAFE_ASSERT_RETURN(fUI.window != nullptr,);
2696 carla_stdout("CarlaPluginCLAP::handlePluginUIResized(%u, %u | vs %u %u) %d %s %s",
2697 width, height,
2698 fUI.width, fUI.height,
2699 fUI.isResizingFromPlugin, bool2str(fUI.isResizingFromInit), bool2str(fUI.isResizingFromHost));
2701 if (fExtensions.gui == nullptr)
2702 return;
2704 if (fUI.isResizingFromPlugin != 0)
2706 CARLA_SAFE_ASSERT_UINT2_RETURN(fUI.width == width, fUI.width, width,);
2707 CARLA_SAFE_ASSERT_UINT2_RETURN(fUI.height == height, fUI.height, height,);
2708 fUI.isResizingFromPlugin = 2;
2709 return;
2712 if (fUI.isResizingFromInit)
2714 CARLA_SAFE_ASSERT_UINT2_RETURN(fUI.width == width, fUI.width, width,);
2715 CARLA_SAFE_ASSERT_UINT2_RETURN(fUI.height == height, fUI.height, height,);
2716 fUI.isResizingFromInit = false;
2717 return;
2720 if (fUI.isResizingFromHost)
2722 CARLA_SAFE_ASSERT_UINT2_RETURN(fUI.width == width, fUI.width, width,);
2723 CARLA_SAFE_ASSERT_UINT2_RETURN(fUI.height == height, fUI.height, height,);
2724 fUI.isResizingFromHost = false;
2725 return;
2728 if (fUI.width != width || fUI.height != height)
2730 uint width2 = width;
2731 uint height2 = height;
2733 if (fExtensions.gui->adjust_size(fPlugin, &width2, &height2))
2735 if (width2 != width || height2 != height)
2737 fUI.isResizingFromHost = true;
2738 fUI.width = width2;
2739 fUI.height = height2;
2740 fUI.window->setSize(width2, height2, false, false);
2742 else
2744 fExtensions.gui->set_size(fPlugin, width2, height2);
2749 #endif
2751 // -------------------------------------------------------------------
2753 void clapRequestRestart() override
2755 carla_stdout("CarlaPluginCLAP::clapRequestRestart()");
2757 fNeedsRestart = true;
2760 void clapRequestProcess() override
2762 carla_stdout("CarlaPluginCLAP::clapRequestProcess()");
2764 fNeedsProcess = true;
2767 void clapRequestCallback() override
2769 carla_stdout("CarlaPluginCLAP::clapRequestCallback()");
2771 if (fPlugin->on_main_thread != nullptr)
2772 fNeedsIdleCallback = true;
2775 // -------------------------------------------------------------------
2777 void clapLatencyChanged() override
2779 carla_stdout("CarlaPluginCLAP::clapLatencyChanged()");
2780 CARLA_SAFE_ASSERT_RETURN(fExtensions.latency != nullptr,);
2782 fLastKnownLatency = fExtensions.latency->get(fPlugin);
2785 // -------------------------------------------------------------------
2787 void clapMarkDirty() override
2789 carla_stdout("CarlaPluginCLAP::clapMarkDirty()");
2792 // -------------------------------------------------------------------
2794 #ifdef CLAP_WINDOW_API_NATIVE
2795 void clapGuiResizeHintsChanged() override
2797 carla_stdout("CarlaPluginCLAP::clapGuiResizeHintsChanged()");
2800 bool clapGuiRequestResize(const uint width, const uint height) override
2802 CARLA_SAFE_ASSERT_RETURN(fUI.window != nullptr, false);
2803 carla_stdout("CarlaPluginCLAP::hostRequestResize(%u, %u)", width, height);
2805 fUI.isResizingFromPlugin = 3;
2806 fUI.width = width;
2807 fUI.height = height;
2808 fUI.window->setSize(width, height, true, false);
2809 return true;
2812 bool clapGuiRequestShow() override
2814 carla_stdout("CarlaPluginCLAP::clapGuiRequestShow()");
2815 return false;
2818 bool clapGuiRequestHide() override
2820 carla_stdout("CarlaPluginCLAP::clapGuiRequestHide()");
2821 return false;
2824 void clapGuiClosed(const bool wasDestroyed) override
2826 carla_stdout("CarlaPluginCLAP::clapGuiClosed(%s)", bool2str(wasDestroyed));
2827 CARLA_SAFE_ASSERT_RETURN(!fUI.isEmbed,);
2828 CARLA_SAFE_ASSERT_RETURN(fUI.isVisible,);
2830 fUI.isVisible = false;
2832 if (wasDestroyed)
2834 CARLA_SAFE_ASSERT_RETURN(fUI.isCreated,);
2835 fExtensions.gui->destroy(fPlugin);
2836 fUI.isCreated = false;
2839 pData->engine->callback(true, true,
2840 ENGINE_CALLBACK_UI_STATE_CHANGED,
2841 pData->id,
2843 0, 0, 0.0f, nullptr);
2846 // -------------------------------------------------------------------
2848 #ifdef _POSIX_VERSION
2849 bool clapRegisterPosixFD(const int fd, const clap_posix_fd_flags_t flags) override
2851 carla_stdout("CarlaPluginCLAP::clapRegisterPosixFD(%i, %x)", fd, flags);
2853 // NOTE some plugins wont have their posix fd extension ready when first loaded, so try again here
2854 if (fExtensions.posixFD == nullptr)
2856 const clap_plugin_posix_fd_support_t* const posixFdExt = static_cast<const clap_plugin_posix_fd_support_t*>(
2857 fPlugin->get_extension(fPlugin, CLAP_EXT_POSIX_FD_SUPPORT));
2859 if (posixFdExt != nullptr && posixFdExt->on_fd != nullptr)
2860 fExtensions.posixFD = posixFdExt;
2863 CARLA_SAFE_ASSERT_RETURN(fExtensions.posixFD != nullptr, false);
2865 // FIXME events only driven as long as UI is open
2866 // CARLA_SAFE_ASSERT_RETURN(fUI.isCreated, false);
2868 if (flags & (CLAP_POSIX_FD_READ|CLAP_POSIX_FD_WRITE))
2870 #ifdef CARLA_CLAP_POSIX_EPOLL
2871 const int hostFd = ::epoll_create1(0);
2872 #else
2873 const int hostFd = ::kqueue();
2874 #endif
2875 CARLA_SAFE_ASSERT_RETURN(hostFd >= 0, false);
2877 #ifdef CARLA_CLAP_POSIX_EPOLL
2878 struct ::epoll_event ev = {};
2879 if (flags & CLAP_POSIX_FD_READ)
2880 ev.events |= EPOLLIN;
2881 if (flags & CLAP_POSIX_FD_WRITE)
2882 ev.events |= EPOLLOUT;
2883 ev.data.fd = fd;
2885 if (::epoll_ctl(hostFd, EPOLL_CTL_ADD, fd, &ev) < 0)
2887 ::close(hostFd);
2888 return false;
2890 #endif
2892 const HostPosixFileDescriptorDetails posixFD = {
2893 hostFd,
2895 flags,
2897 fPosixFileDescriptors.append(posixFD);
2899 return true;
2902 return false;
2905 bool clapModifyPosixFD(const int fd, const clap_posix_fd_flags_t flags) override
2907 carla_stdout("CarlaPluginCLAP::clapTimerUnregister(%i, %x)", fd, flags);
2908 for (LinkedList<HostPosixFileDescriptorDetails>::Itenerator it = fPosixFileDescriptors.begin2(); it.valid(); it.next())
2910 HostPosixFileDescriptorDetails& posixFD(it.getValue(kPosixFileDescriptorFallbackNC));
2912 if (posixFD.pluginFd == fd)
2914 if (posixFD.flags == flags)
2915 return true;
2917 #ifdef CARLA_CLAP_POSIX_EPOLL
2918 struct ::epoll_event ev = {};
2919 if (flags & CLAP_POSIX_FD_READ)
2920 ev.events |= EPOLLIN;
2921 if (flags & CLAP_POSIX_FD_WRITE)
2922 ev.events |= EPOLLOUT;
2923 ev.data.fd = fd;
2925 if (::epoll_ctl(posixFD.hostFd, EPOLL_CTL_MOD, fd, &ev) < 0)
2926 return false;
2927 #endif
2929 posixFD.flags = flags;
2930 return true;
2934 return false;
2937 bool clapUnregisterPosixFD(const int fd) override
2939 carla_stdout("CarlaPluginCLAP::clapTimerUnregister(%i)", fd);
2940 for (LinkedList<HostPosixFileDescriptorDetails>::Itenerator it = fPosixFileDescriptors.begin2(); it.valid(); it.next())
2942 const HostPosixFileDescriptorDetails& posixFD(it.getValue(kPosixFileDescriptorFallback));
2944 if (posixFD.pluginFd == fd)
2946 #ifdef CARLA_CLAP_POSIX_EPOLL
2947 ::epoll_ctl(posixFD.hostFd, EPOLL_CTL_DEL, fd, nullptr);
2948 #endif
2949 ::close(posixFD.hostFd);
2950 fPosixFileDescriptors.remove(it);
2951 return true;
2955 return false;
2957 #endif // _POSIX_VERSION
2959 // -------------------------------------------------------------------
2961 bool clapRegisterTimer(const uint32_t periodInMs, clap_id* const timerId) override
2963 carla_stdout("CarlaPluginCLAP::clapTimerRegister(%u, %p)", periodInMs, timerId);
2965 // NOTE some plugins wont have their timer extension ready when first loaded, so try again here
2966 if (fExtensions.timer == nullptr)
2968 const clap_plugin_timer_support_t* const timerExt = static_cast<const clap_plugin_timer_support_t*>(
2969 fPlugin->get_extension(fPlugin, CLAP_EXT_TIMER_SUPPORT));
2971 if (timerExt != nullptr && timerExt->on_timer != nullptr)
2972 fExtensions.timer = timerExt;
2975 CARLA_SAFE_ASSERT_RETURN(fExtensions.timer != nullptr, false);
2977 // FIXME events only driven as long as UI is open
2978 // CARLA_SAFE_ASSERT_RETURN(fUI.isCreated, false);
2980 const HostTimerDetails timer = {
2981 fTimers.isNotEmpty() ? fTimers.getLast(kTimerFallback).clapId + 1 : 1,
2982 periodInMs,
2985 fTimers.append(timer);
2987 *timerId = timer.clapId;
2988 return true;
2991 bool clapUnregisterTimer(const clap_id timerId) override
2993 carla_stdout("CarlaPluginCLAP::clapTimerUnregister(%u)", timerId);
2994 for (LinkedList<HostTimerDetails>::Itenerator it = fTimers.begin2(); it.valid(); it.next())
2996 const HostTimerDetails& timer(it.getValue(kTimerFallback));
2998 if (timer.clapId == timerId)
3000 fTimers.remove(it);
3001 return true;
3005 return false;
3007 #endif // CLAP_WINDOW_API_NATIVE
3009 // -------------------------------------------------------------------
3011 public:
3012 bool init(const CarlaPluginPtr plugin,
3013 const char* const filename, const char* const name, const char* const id, const uint options)
3015 CARLA_SAFE_ASSERT_RETURN(pData->engine != nullptr, false);
3017 // ---------------------------------------------------------------
3018 // first checks
3020 if (pData->client != nullptr)
3022 pData->engine->setLastError("Plugin client is already registered");
3023 return false;
3026 if (filename == nullptr || filename[0] == '\0')
3028 pData->engine->setLastError("null filename");
3029 return false;
3032 // ---------------------------------------------------------------
3034 const clap_plugin_entry_t* entry;
3036 #ifdef CARLA_OS_MAC
3037 if (!water::File(filename).existsAsFile())
3039 if (! fBundleLoader.load(filename))
3041 pData->engine->setLastError("Failed to load CLAP bundle executable");
3042 return false;
3045 entry = fBundleLoader.getSymbol<const clap_plugin_entry_t*>(CFSTR("clap_entry"));
3047 else
3048 #endif
3050 if (! pData->libOpen(filename))
3052 pData->engine->setLastError(pData->libError(filename));
3053 return false;
3056 entry = pData->libSymbol<const clap_plugin_entry_t*>("clap_entry");
3059 if (entry == nullptr)
3061 pData->engine->setLastError("Could not find the CLAP entry in the plugin library");
3062 return false;
3065 if (entry->init == nullptr || entry->deinit == nullptr || entry->get_factory == nullptr)
3067 pData->engine->setLastError("CLAP factory entries are null");
3068 return false;
3071 if (!clap_version_is_compatible(entry->clap_version))
3073 pData->engine->setLastError("Incompatible CLAP plugin");
3074 return false;
3077 // ---------------------------------------------------------------
3079 const water::String pluginPath(water::File(filename).getParentDirectory().getFullPathName());
3081 if (!entry->init(pluginPath.toRawUTF8()))
3083 pData->engine->setLastError("Plugin entry failed to initialize");
3084 return false;
3087 fPluginEntry = entry;
3089 // ---------------------------------------------------------------
3091 const clap_plugin_factory_t* const factory = static_cast<const clap_plugin_factory_t*>(
3092 entry->get_factory(CLAP_PLUGIN_FACTORY_ID));
3094 if (factory == nullptr
3095 || factory->get_plugin_count == nullptr
3096 || factory->get_plugin_descriptor == nullptr
3097 || factory->create_plugin == nullptr)
3099 pData->engine->setLastError("Plugin is missing factory methods");
3100 return false;
3103 // ---------------------------------------------------------------
3105 if (const uint32_t count = factory->get_plugin_count(factory))
3107 // null id requested, use first available plugin
3108 if (id == nullptr || id[0] == '\0')
3110 fPluginDescriptor = factory->get_plugin_descriptor(factory, 0);
3112 if (fPluginDescriptor == nullptr)
3114 pData->engine->setLastError("Plugin library does not contain a valid first plugin");
3115 return false;
3118 else
3120 for (uint32_t i=0; i<count; ++i)
3122 const clap_plugin_descriptor_t* const desc = factory->get_plugin_descriptor(factory, i);
3123 CARLA_SAFE_ASSERT_CONTINUE(desc != nullptr);
3124 CARLA_SAFE_ASSERT_CONTINUE(desc->id != nullptr);
3126 if (std::strcmp(desc->id, id) == 0)
3128 fPluginDescriptor = desc;
3129 break;
3133 if (fPluginDescriptor == nullptr)
3135 pData->engine->setLastError("Plugin library does not contain the requested plugin");
3136 return false;
3140 else
3142 pData->engine->setLastError("Plugin library contains no plugins");
3143 return false;
3146 // ---------------------------------------------------------------
3148 fPlugin = factory->create_plugin(factory, &fHost, fPluginDescriptor->id);
3150 if (fPlugin == nullptr)
3152 pData->engine->setLastError("Failed to create CLAP plugin instance");
3153 return false;
3156 if (!fPlugin->init(fPlugin))
3158 pData->engine->setLastError("Failed to initialize CLAP plugin instance");
3159 return false;
3162 // ---------------------------------------------------------------
3163 // get info
3165 pData->name = pData->engine->getUniquePluginName(name != nullptr && name[0] != '\0' ? name
3166 : fPluginDescriptor->name);
3167 pData->filename = carla_strdup(filename);
3169 // ---------------------------------------------------------------
3170 // register client
3172 pData->client = pData->engine->addClient(plugin);
3174 if (pData->client == nullptr || ! pData->client->isOk())
3176 pData->engine->setLastError("Failed to register plugin client");
3177 return false;
3180 // ---------------------------------------------------------------
3181 // set default options
3183 pData->options = PLUGIN_OPTION_FIXED_BUFFERS;
3185 if (const clap_plugin_state_t* const stateExt =
3186 static_cast<const clap_plugin_state_t*>(fPlugin->get_extension(fPlugin, CLAP_EXT_STATE)))
3188 if (stateExt->save != nullptr && stateExt->load != nullptr)
3189 if (isPluginOptionEnabled(options, PLUGIN_OPTION_USE_CHUNKS))
3190 pData->options |= PLUGIN_OPTION_USE_CHUNKS;
3193 if (const clap_plugin_note_ports_t* const notePortsExt =
3194 static_cast<const clap_plugin_note_ports_t*>(fPlugin->get_extension(fPlugin, CLAP_EXT_NOTE_PORTS)))
3196 const uint32_t numNoteInputPorts = notePortsExt->count != nullptr && notePortsExt->get != nullptr
3197 ? notePortsExt->count(fPlugin, true) : 0;
3199 for (uint32_t i=0; i<numNoteInputPorts; ++i)
3201 clap_note_port_info_t portInfo = {};
3202 CARLA_SAFE_ASSERT_BREAK(notePortsExt->get(fPlugin, i, true, &portInfo));
3204 if (portInfo.supported_dialects & CLAP_NOTE_DIALECT_MIDI)
3206 if (isPluginOptionEnabled(options, PLUGIN_OPTION_SEND_CONTROL_CHANGES))
3207 pData->options |= PLUGIN_OPTION_SEND_CONTROL_CHANGES;
3208 if (isPluginOptionEnabled(options, PLUGIN_OPTION_SEND_CHANNEL_PRESSURE))
3209 pData->options |= PLUGIN_OPTION_SEND_CHANNEL_PRESSURE;
3210 if (isPluginOptionEnabled(options, PLUGIN_OPTION_SEND_NOTE_AFTERTOUCH))
3211 pData->options |= PLUGIN_OPTION_SEND_NOTE_AFTERTOUCH;
3212 if (isPluginOptionEnabled(options, PLUGIN_OPTION_SEND_PITCHBEND))
3213 pData->options |= PLUGIN_OPTION_SEND_PITCHBEND;
3214 if (isPluginOptionEnabled(options, PLUGIN_OPTION_SEND_ALL_SOUND_OFF))
3215 pData->options |= PLUGIN_OPTION_SEND_ALL_SOUND_OFF;
3216 if (isPluginOptionEnabled(options, PLUGIN_OPTION_SEND_PROGRAM_CHANGES))
3217 pData->options |= PLUGIN_OPTION_SEND_PROGRAM_CHANGES;
3218 if (isPluginOptionInverseEnabled(options, PLUGIN_OPTION_SKIP_SENDING_NOTES))
3219 pData->options |= PLUGIN_OPTION_SKIP_SENDING_NOTES;
3220 break;
3222 if (portInfo.supported_dialects & CLAP_NOTE_DIALECT_CLAP)
3224 if (isPluginOptionInverseEnabled(options, PLUGIN_OPTION_SKIP_SENDING_NOTES))
3225 pData->options |= PLUGIN_OPTION_SKIP_SENDING_NOTES;
3226 // nobreak, in case another port supports MIDI
3231 // if (fEffect->numPrograms > 1 && (pData->options & PLUGIN_OPTION_SEND_PROGRAM_CHANGES) == 0)
3232 // if (isPluginOptionEnabled(options, PLUGIN_OPTION_MAP_PROGRAM_CHANGES))
3233 // pData->options |= PLUGIN_OPTION_MAP_PROGRAM_CHANGES;
3235 return true;
3238 private:
3239 const clap_plugin_t* fPlugin;
3240 const clap_plugin_descriptor_t* fPluginDescriptor;
3241 const clap_plugin_entry_t* fPluginEntry;
3242 const carla_clap_host fHost;
3244 struct Extensions {
3245 const clap_plugin_latency_t* latency;
3246 const clap_plugin_params_t* params;
3247 const clap_plugin_state_t* state;
3248 const clap_plugin_timer_support_t* timer;
3249 #ifdef CLAP_WINDOW_API_NATIVE
3250 const clap_plugin_gui_t* gui;
3251 #ifdef _POSIX_VERSION
3252 const clap_plugin_posix_fd_support_t* posixFD;
3253 #endif
3254 #endif
3256 Extensions()
3257 : latency(nullptr),
3258 params(nullptr),
3259 state(nullptr),
3260 timer(nullptr)
3261 #ifdef CLAP_WINDOW_API_NATIVE
3262 , gui(nullptr)
3263 #ifdef _POSIX_VERSION
3264 , posixFD(nullptr)
3265 #endif
3266 #endif
3270 CARLA_DECLARE_NON_COPYABLE(Extensions)
3271 } fExtensions;
3273 #ifdef CLAP_WINDOW_API_NATIVE
3274 struct UI {
3275 bool initalized;
3276 bool isCreated;
3277 bool isEmbed;
3278 bool isVisible;
3279 bool isResizingFromHost;
3280 bool isResizingFromInit;
3281 int isResizingFromPlugin;
3282 bool shouldClose;
3283 uint32_t width, height;
3284 CarlaPluginUI* window;
3286 UI()
3287 : initalized(false),
3288 isCreated(false),
3289 isEmbed(false),
3290 isVisible(false),
3291 isResizingFromHost(false),
3292 isResizingFromInit(false),
3293 isResizingFromPlugin(0),
3294 shouldClose(false),
3295 width(0),
3296 height(0),
3297 window(nullptr) {}
3299 ~UI()
3301 CARLA_SAFE_ASSERT(window == nullptr);
3304 CARLA_DECLARE_NON_COPYABLE(UI)
3305 } fUI;
3307 #ifdef _POSIX_VERSION
3308 LinkedList<HostPosixFileDescriptorDetails> fPosixFileDescriptors;
3309 #endif
3310 LinkedList<HostTimerDetails> fTimers;
3311 #endif
3313 carla_clap_input_audio_buffers fInputAudioBuffers;
3314 carla_clap_output_audio_buffers fOutputAudioBuffers;
3315 carla_clap_input_events fInputEvents;
3316 carla_clap_output_events fOutputEvents;
3317 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
3318 float** fAudioOutBuffers;
3319 #endif
3320 void* fLastChunk;
3321 uint32_t fLastKnownLatency;
3322 const bool kEngineHasIdleOnMainThread;
3323 bool fNeedsParamFlush;
3324 bool fNeedsRestart;
3325 bool fNeedsProcess;
3326 bool fNeedsIdleCallback;
3328 #ifdef CARLA_OS_MAC
3329 BundleLoader fBundleLoader;
3330 #endif
3332 void runIdleCallbacksAsNeeded(const bool isIdleCallback)
3334 if (isIdleCallback && (fNeedsRestart || fNeedsProcess))
3336 carla_stdout("runIdleCallbacksAsNeeded %d %d", fNeedsRestart, fNeedsProcess);
3337 const bool needsRestart = fNeedsRestart;
3339 if (needsRestart)
3341 fNeedsRestart = false;
3342 setActive(false, true, true);
3345 if (fNeedsProcess)
3347 fNeedsProcess = false;
3348 setEnabled(true);
3349 setActive(true, true, true);
3351 else if (needsRestart)
3353 setActive(true, true, true);
3357 if (fNeedsParamFlush)
3359 fNeedsParamFlush = false;
3361 carla_clap_input_events copy;
3362 copy.reallocEqualTo(fInputEvents);
3365 const ScopedSingleProcessLocker sspl(this, true);
3366 fInputEvents.handleScheduledParameterUpdates();
3367 fInputEvents.swap(copy);
3370 fExtensions.params->flush(fPlugin, copy.cast(), nullptr);
3373 if (fNeedsIdleCallback)
3375 fNeedsIdleCallback = false;
3376 fPlugin->on_main_thread(fPlugin);
3379 #ifdef CLAP_WINDOW_API_NATIVE
3380 #ifdef _POSIX_VERSION
3381 for (LinkedList<HostPosixFileDescriptorDetails>::Itenerator it = fPosixFileDescriptors.begin2(); it.valid(); it.next())
3383 const HostPosixFileDescriptorDetails& posixFD(it.getValue(kPosixFileDescriptorFallback));
3385 #ifdef CARLA_CLAP_POSIX_EPOLL
3386 struct ::epoll_event event;
3387 #else
3388 const int16_t filter = posixFD.flags & CLAP_POSIX_FD_WRITE ? EVFILT_WRITE : EVFILT_READ;
3389 struct ::kevent kev = {}, event;
3390 struct ::timespec timeout = {};
3391 EV_SET(&kev, posixFD.pluginFd, filter, EV_ADD|EV_ENABLE, 0, 0, nullptr);
3392 #endif
3394 for (int i=0; i<50; ++i)
3396 #ifdef CARLA_CLAP_POSIX_EPOLL
3397 switch (::epoll_wait(posixFD.hostFd, &event, 1, 0))
3398 #else
3399 switch (::kevent(posixFD.hostFd, &kev, 1, &event, 1, &timeout))
3400 #endif
3402 case 1:
3403 fExtensions.posixFD->on_fd(fPlugin, posixFD.pluginFd, posixFD.flags);
3404 break;
3405 case -1:
3406 fExtensions.posixFD->on_fd(fPlugin, posixFD.pluginFd, posixFD.flags | CLAP_POSIX_FD_ERROR);
3407 // fall through
3408 case 0:
3409 i = 50;
3410 break;
3411 default:
3412 carla_safe_exception("posix fd received abnormal value", __FILE__, __LINE__);
3413 i = 50;
3414 break;
3418 #endif // _POSIX_VERSION
3420 for (LinkedList<HostTimerDetails>::Itenerator it = fTimers.begin2(); it.valid(); it.next())
3422 const uint32_t currentTimeInMs = water::Time::getMillisecondCounter();
3423 HostTimerDetails& timer(it.getValue(kTimerFallbackNC));
3425 if (currentTimeInMs > timer.lastCallTimeInMs + timer.periodInMs)
3427 timer.lastCallTimeInMs = currentTimeInMs;
3428 fExtensions.timer->on_timer(fPlugin, timer.clapId);
3431 #endif // CLAP_WINDOW_API_NATIVE
3435 // --------------------------------------------------------------------------------------------------------------------
3437 CarlaPluginPtr CarlaPlugin::newCLAP(const Initializer& init)
3439 carla_debug("CarlaPlugin::newCLAP({%p, \"%s\", \"%s\", \"%s\"})",
3440 init.engine, init.filename, init.name, init.label);
3442 std::shared_ptr<CarlaPluginCLAP> plugin(new CarlaPluginCLAP(init.engine, init.id));
3444 if (! plugin->init(plugin, init.filename, init.name, init.label, init.options))
3445 return nullptr;
3447 return plugin;
3450 // -------------------------------------------------------------------------------------------------------------------
3452 CARLA_BACKEND_END_NAMESPACE