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"
28 # include "CarlaMacUtils.hpp"
29 # import <Foundation/Foundation.h>
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>
40 # include <sys/event.h>
41 # include <sys/types.h>
46 // #ifndef CLAP_WINDOW_API_NATIVE
47 // #define CLAP_WINDOW_API_NATIVE ""
51 CARLA_BACKEND_START_NAMESPACE
53 // --------------------------------------------------------------------------------------------------------------------
55 struct ClapEventData
{
56 uint16_t clapPortIndex
;
57 uint32_t supportedDialects
;
58 CarlaEngineEventPort
* port
;
61 struct CarlaPluginClapEventData
{
63 ClapEventData
* portData
;
64 ClapEventData
* defaultPort
; // either this->portData[x] or pData->portIn/Out
66 CarlaPluginClapEventData() noexcept
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
];
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;
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
{
132 clap_posix_fd_flags_t flags
;
135 static constexpr const HostPosixFileDescriptorDetails kPosixFileDescriptorFallback
= { -1, -1, 0x0 };
136 static /* */ HostPosixFileDescriptorDetails kPosixFileDescriptorFallbackNC
= { -1, -1, 0x0 };
139 // --------------------------------------------------------------------------------------------------------------------
141 struct HostTimerDetails
{
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
{
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
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
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;
175 virtual bool clapRegisterTimer(uint32_t periodInMs
, clap_id
* timerId
) = 0;
176 virtual bool clapUnregisterTimer(clap_id timerId
) = 0;
180 Callbacks
* const hostCallbacks
;
182 clap_host_latency_t latency
;
183 clap_host_state_t state
;
184 #ifdef CLAP_WINDOW_API_NATIVE
186 #ifdef _POSIX_VERSION
187 clap_host_posix_fd_support_t posixFD
;
189 clap_host_timer_support_t timer
;
192 carla_clap_host(Callbacks
* const hostCb
)
193 : hostCallbacks(hostCb
)
195 clap_version
= CLAP_VERSION
;
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
;
224 timer
.register_timer
= carla_register_timer
;
225 timer
.unregister_timer
= carla_unregister_timer
;
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)
237 #ifdef CLAP_WINDOW_API_NATIVE
238 if (std::strcmp(extension_id
, CLAP_EXT_GUI
) == 0)
240 #ifdef _POSIX_VERSION
241 if (std::strcmp(extension_id
, CLAP_EXT_POSIX_FD_SUPPORT
) == 0)
242 return &self
->posixFD
;
244 if (std::strcmp(extension_id
, CLAP_EXT_TIMER_SUPPORT
) == 0)
248 carla_stderr("Plugin requested unsupported CLAP extension '%s'", extension_id
);
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
);
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
);
332 // --------------------------------------------------------------------------------------------------------------------
334 struct carla_clap_input_audio_buffers
{
335 clap_audio_buffer_const_t
* buffers
;
336 clap_audio_buffer_extra_data_t
* extra
;
339 carla_clap_input_audio_buffers() noexcept
344 ~carla_clap_input_audio_buffers()
350 void realloc(const uint32_t portCount
)
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
);
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
;
381 carla_clap_output_audio_buffers() noexcept
386 ~carla_clap_output_audio_buffers()
392 void realloc(const uint32_t portCount
)
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
);
413 // --------------------------------------------------------------------------------------------------------------------
415 struct carla_clap_input_events
: clap_input_events_t
, CarlaPluginClapEventData
{
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
{
431 ScheduledParameterUpdate()
439 ScheduledParameterUpdate
* updatedParams
;
441 uint32_t numEventsAllocated
;
442 uint32_t numEventsUsed
;
445 carla_clap_input_events()
446 : CarlaPluginClapEventData(),
448 updatedParams(nullptr),
449 numEventsAllocated(0),
458 ~carla_clap_input_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
)
469 numParams
= paramCount
;
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
];
483 numEventsAllocated
= 0;
485 updatedParams
= nullptr;
488 CarlaPluginClapEventData::clear(defPortIn
);
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()
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
,
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
)
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
,
578 void addSimpleMidiEvent(const bool isLive
, const uint16_t port
, const uint32_t frameOffset
, const uint8_t data
[3])
580 if (numEventsUsed
== numEventsAllocated
)
583 events
[numEventsUsed
++].midi
= {
584 { sizeof(clap_event_midi_t
), frameOffset
, 0, CLAP_EVENT_MIDI
, isLive
? (uint32_t)CLAP_EVENT_IS_LIVE
: 0u },
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
)
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 },
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
{
623 clap_event_header_t header
;
624 clap_event_param_value_t param
;
625 clap_event_midi_t midi
;
629 uint32_t numEventsAllocated
;
630 uint32_t numEventsUsed
;
632 carla_clap_output_events()
634 numEventsAllocated(0),
638 try_push
= carla_try_push
;
641 ~carla_clap_output_events()
646 // called on plugin reload
647 void realloc(CarlaEngineEventPort
* const defPortOut
, const uint32_t portCount
, const uint32_t paramCount
)
652 if (portCount
!= 0 || paramCount
!= 0)
654 numEventsAllocated
= paramCount
+ kPluginMaxMidiEvents
* std::max(1u, portCount
);
655 events
= new Event
[numEventsAllocated
];
659 numEventsAllocated
= 0;
663 CarlaPluginClapEventData::clear(defPortOut
);
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
)
682 case CLAP_EVENT_PARAM_VALUE
:
683 e
.param
= *static_cast<const clap_event_param_value_t
*>(static_cast<const void*>(event
));
685 case CLAP_EVENT_MIDI
:
686 e
.midi
= *static_cast<const clap_event_midi_t
*>(static_cast<const void*>(event
));
692 events
[numEventsUsed
++] = e
;
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
,
708 private carla_clap_host::Callbacks
711 CarlaPluginCLAP(CarlaEngine
* const engine
, const uint id
)
712 : CarlaPlugin(engine
, id
),
714 fPluginDescriptor(nullptr),
715 fPluginEntry(nullptr),
718 fInputAudioBuffers(),
719 fOutputAudioBuffers(),
722 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
723 fAudioOutBuffers(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
748 pData
->singleMutex
.lock();
749 pData
->masterMutex
.lock();
751 if (pData
->client
!= nullptr && pData
->client
->isActive())
752 pData
->client
->deactivate(true);
757 pData
->active
= false;
760 if (fPlugin
!= nullptr)
762 fPlugin
->destroy(fPlugin
);
766 if (fLastChunk
!= nullptr)
768 std::free(fLastChunk
);
769 fLastChunk
= nullptr;
774 if (fPluginEntry
!= nullptr)
776 fPluginEntry
->deinit();
777 fPluginEntry
= nullptr;
781 // -------------------------------------------------------------------
782 // Information (base)
784 PluginType
getType() const noexcept override
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
828 for (uint32_t i
=0, j
=0; i
<fOutputAudioBuffers
.count
; ++i
, j
+=fOutputAudioBuffers
.buffers
[i
].channel_count
)
833 if (!fOutputAudioBuffers
.extra
[i
].isMain
)
834 hints
|= AUDIO_PORT_IS_SIDECHAIN
;
839 for (uint32_t i
=0, j
=0; i
<fInputAudioBuffers
.count
; ++i
, j
+=fInputAudioBuffers
.buffers
[i
].channel_count
)
844 if (!fInputAudioBuffers
.extra
[i
].isMain
)
845 hints
|= AUDIO_PORT_IS_SIDECHAIN
;
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);
869 *dataPtr
= fLastChunk
= nullptr;
870 runIdleCallbacksAsNeeded(false);
875 // -------------------------------------------------------------------
876 // Information (per-plugin data)
878 uint
getOptionsAvailable() const noexcept override
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
;
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
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
;
914 CARLA_SAFE_ASSERT_RETURN(fExtensions
.params
->get_value(fPlugin
, clapId
, &value
), 0.0);
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
);
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
);
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
);
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
, ¶mInfo
), false);
965 std::strncpy(strBuf
, paramInfo
.name
, STR_MAX
);
966 strBuf
[STR_MAX
-1] = '\0';
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
);
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
, ¶mInfo
), false);
1000 if (paramInfo
.module
[0] == '\0')
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';
1019 // -------------------------------------------------------------------
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);
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 // -------------------------------------------------------------------
1082 #ifdef CLAP_WINDOW_API_NATIVE
1083 void setWindowTitle(const char* const title
) noexcept
1088 CarlaString uiTitle
;
1090 if (title
!= nullptr)
1096 uiTitle
= pData
->name
;
1097 uiTitle
+= " (GUI)";
1102 if (fUI
.window
!= nullptr)
1103 fUI
.window
->setTitle(uiTitle
.buffer());
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
)
1128 fExtensions
.gui
->show(fPlugin
);
1132 CARLA_SAFE_ASSERT_RETURN(fUI
.window
!= nullptr,);
1134 fUI
.window
->focus();
1137 runIdleCallbacksAsNeeded(false);
1141 if (!fUI
.initalized
)
1143 fUI
.isEmbed
= fExtensions
.gui
->is_api_supported(fPlugin
, CLAP_WINDOW_API_NATIVE
, false);
1144 fUI
.initalized
= true;
1149 if (!fExtensions
.gui
->create(fPlugin
, CLAP_WINDOW_API_NATIVE
, !fUI
.isEmbed
))
1151 pData
->engine
->callback(true, true,
1152 ENGINE_CALLBACK_UI_STATE_CHANGED
,
1156 "Plugin refused to open its own UI");
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);
1173 pData
->engine
->callback(true, true,
1174 ENGINE_CALLBACK_UI_STATE_CHANGED
,
1178 "Unsupported UI type");
1182 #ifndef CARLA_OS_MAC
1183 if (carla_isNotZero(opts
.uiScale
))
1184 fExtensions
.gui
->set_scale(fPlugin
, opts
.uiScale
);
1187 setWindowTitle(nullptr);
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;
1201 fUI
.height
= height
;
1202 fUI
.window
->setSize(width
, height
, true, true);
1205 fExtensions
.gui
->show(fPlugin
);
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();
1219 fUI
.isVisible
= true;
1223 fUI
.isVisible
= false;
1225 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
1226 pData
->transientTryCounter
= 0;
1229 if (fUI
.window
!= nullptr)
1232 fExtensions
.gui
->hide(fPlugin
);
1236 fExtensions
.gui
->destroy(fPlugin
);
1237 fUI
.isCreated
= false;
1240 if (fUI
.window
!= nullptr)
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;
1262 if (!fExtensions
.gui
->create(fPlugin
, CLAP_WINDOW_API_NATIVE
, false))
1264 pData
->engine
->callback(true, true,
1265 ENGINE_CALLBACK_UI_STATE_CHANGED
,
1269 "Plugin refused to open its own UI");
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
);
1284 clap_window_t win
= { CLAP_WINDOW_API_NATIVE
, {} };
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;
1294 fUI
.height
= height
;
1295 pData
->engine
->callback(true, true,
1296 ENGINE_CALLBACK_EMBED_UI_RESIZED
,
1297 pData
->id
, width
, height
,
1301 fExtensions
.gui
->show(fPlugin
);
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
,
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)
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");
1357 if (!kEngineHasIdleOnMainThread
)
1358 runIdleCallbacksAsNeeded(true);
1360 CarlaPlugin::uiIdle();
1363 // -------------------------------------------------------------------
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);
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))
1413 if (timerExt
!= nullptr && timerExt
->on_timer
== 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))
1441 fExtensions
.gui
= guiExt
;
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
;
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
))
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
)
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
, ¶mInfo
));
1523 pData
->audioIn
.createNew(aIns
);
1528 pData
->audioOut
.createNew(aOuts
);
1531 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
1532 fAudioOutBuffers
= new float*[aOuts
];
1533 for (uint32_t i
=0; i
< aOuts
; ++i
)
1534 fAudioOutBuffers
[i
] = nullptr;
1542 needsCtrlOut
= true;
1546 pData
->param
.createNew(params
, false);
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
;
1558 for (uint32_t j
=0; j
< aIns
; ++j
)
1562 if (processMode
== ENGINE_PROCESS_MODE_SINGLE_CLIENT
)
1564 portName
= pData
->name
;
1570 portName
+= "input_";
1571 portName
+= CarlaString(j
+1);
1574 portName
+= "input";
1576 portName
.truncate(portNameSize
);
1578 pData
->audioIn
.ports
[j
].port
= (CarlaEngineAudioPort
*)pData
->client
->addPort(kEnginePortTypeAudio
,
1580 pData
->audioIn
.ports
[j
].rindex
= j
;
1584 for (uint32_t j
=0; j
< aOuts
; ++j
)
1588 if (processMode
== ENGINE_PROCESS_MODE_SINGLE_CLIENT
)
1590 portName
= pData
->name
;
1596 portName
+= "output_";
1597 portName
+= CarlaString(j
+1);
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
;
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)
1618 CARLA_SAFE_ASSERT_UINT2_BREAK(j
< mIns
, j
, mIns
);
1620 fInputEvents
.portData
[j
].clapPortIndex
= i
;
1621 fInputEvents
.portData
[j
].supportedDialects
= portInfo
.supported_dialects
;
1627 if (processMode
== ENGINE_PROCESS_MODE_SINGLE_CLIENT
)
1629 portName
= pData
->name
;
1633 portName
+= portInfo
.name
;
1634 portName
.truncate(portNameSize
);
1635 fInputEvents
.portData
[j
].port
= (CarlaEngineEventPort
*)pData
->client
->addPort(kEnginePortTypeEvent
,
1640 fInputEvents
.portData
[j
].port
= nullptr;
1641 fInputEvents
.defaultPort
= &fInputEvents
.portData
[0];
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)
1656 CARLA_SAFE_ASSERT_UINT2_BREAK(j
< mOuts
, j
, mOuts
);
1658 fOutputEvents
.portData
[j
].clapPortIndex
= i
;
1659 fOutputEvents
.portData
[j
].supportedDialects
= portInfo
.supported_dialects
;
1665 if (processMode
== ENGINE_PROCESS_MODE_SINGLE_CLIENT
)
1667 portName
= pData
->name
;
1671 portName
+= portInfo
.name
;
1672 portName
.truncate(portNameSize
);
1673 fOutputEvents
.portData
[j
].port
= (CarlaEngineEventPort
*)pData
->client
->addPort(kEnginePortTypeEvent
,
1674 portName
, false, j
);
1678 fOutputEvents
.portData
[j
].port
= nullptr;
1679 fOutputEvents
.defaultPort
= &fOutputEvents
.portData
[0];
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
, ¶mInfo
));
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
;
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;
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
;
1734 stepLarge
= std::min(max
- min
, 10.0);
1736 pData
->param
.data
[i
].hints
|= PARAMETER_IS_INTEGER
;
1740 double range
= max
- min
;
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
;
1775 if (processMode
== ENGINE_PROCESS_MODE_SINGLE_CLIENT
)
1777 portName
= pData
->name
;
1781 portName
+= "events-in";
1782 portName
.truncate(portNameSize
);
1784 pData
->event
.portIn
= (CarlaEngineEventPort
*)pData
->client
->addPort(kEnginePortTypeEvent
,
1786 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
1787 pData
->event
.cvSourcePorts
= pData
->client
->createCVSourcePorts();
1791 fInputEvents
.portData
[0].port
= pData
->event
.portIn
;
1798 if (processMode
== ENGINE_PROCESS_MODE_SINGLE_CLIENT
)
1800 portName
= pData
->name
;
1804 portName
+= "events-out";
1805 portName
.truncate(portNameSize
);
1807 pData
->event
.portOut
= (CarlaEngineEventPort
*)pData
->client
->addPort(kEnginePortTypeEvent
,
1808 portName
, false, 0);
1811 fOutputEvents
.portData
[0].port
= pData
->event
.portOut
;
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
;
1837 if (aOuts
> 0 && (aIns
== aOuts
|| aIns
== 1))
1838 pData
->hints
|= PLUGIN_CAN_DRYWET
;
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
);
1859 fLastKnownLatency
= 0;
1862 bufferSizeChanged(pData
->engine
->getBufferSize());
1863 reloadPrograms(true);
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
,
1908 const uint32_t frames
) override
1910 // --------------------------------------------------------------------------------------------------------
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
);
1921 // --------------------------------------------------------------------------------------------------------
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,);
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
);
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
);
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 },
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 },
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 },
2012 uint8_t(MIDI_STATUS_NOTE_OFF
| (pData
->ctrlChannel
& MIDI_CHANNEL_BIT
)),
2018 fInputEvents
.numEventsUsed
+= MAX_MIDI_NOTE
;
2022 pData
->needsReset
= false;
2025 // --------------------------------------------------------------------------------------------------------
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 },
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
);
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
;
2072 clapTransport
.tempo
= timeInfo
.bbt
.beatsPerMinute
;
2073 clapTransport
.flags
|= CLAP_TRANSPORT_HAS_TEMPO
;
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
;
2083 clapTransport
.tempo
= 120.0;
2084 clapTransport
.flags
|= CLAP_TRANSPORT_HAS_TEMPO
;
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();
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
)),
2125 fInputEvents
.addSimpleMidiEvent(true, p
, 0, data
);
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;
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
;
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
);
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
;
2177 case kEngineEventTypeNull
:
2180 case kEngineEventTypeControl
: {
2181 EngineControlEvent
& ctrlEvent(event
.ctrl
);
2183 switch (ctrlEvent
.type
)
2185 case kEngineControlEventTypeNull
:
2188 case kEngineControlEventTypeParameter
: {
2191 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
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);
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)
2222 value
= ctrlEvent
.normalizedValue
/0.5f
- 1.0f
;
2227 right
= (value
*2.0f
)+1.0f
;
2229 else if (value
> 0.0f
)
2231 left
= (value
*2.0f
)-1.0f
;
2240 ctrlEvent
.handled
= true;
2241 setBalanceLeftRT(left
, true);
2242 setBalanceRightRT(right
, true);
2246 // Control plugin parameters
2248 for (k
=0; k
< pData
->param
.count
; ++k
)
2250 if (pData
->param
.data
[k
].midiChannel
!= event
.channel
)
2252 if (pData
->param
.data
[k
].mappedControlIndex
!= ctrlEvent
.param
)
2254 if (pData
->param
.data
[k
].type
!= PARAMETER_INPUT
)
2256 if ((pData
->param
.data
[k
].hints
& PARAMETER_IS_AUTOMATABLE
) == 0)
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
);
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
);
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);
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
);
2322 fInputEvents
.addSimpleMidiEvent(true, 0, eventTime
, midiData
);
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
;
2334 fInputEvents
.addSimpleMidiEvent(true, 0, eventTime
, midiData
);
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();
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
;
2354 fInputEvents
.addSimpleMidiEvent(true, 0, eventTime
, midiData
);
2357 } // switch (ctrlEvent.type)
2359 } // case kEngineEventTypeControl
2361 case kEngineEventTypeMidi
: {
2362 const EngineMidiEvent
& midiEvent(event
.midi
);
2364 if (midiEvent
.size
> 3)
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
)
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)
2388 if (status
== MIDI_STATUS_CONTROL_CHANGE
&& (pData
->options
& PLUGIN_OPTION_SEND_CONTROL_CHANGES
) == 0)
2390 if (status
== MIDI_STATUS_POLYPHONIC_AFTERTOUCH
&& (pData
->options
& PLUGIN_OPTION_SEND_NOTE_AFTERTOUCH
) == 0)
2392 if (status
== MIDI_STATUS_PITCH_WHEEL_CONTROL
&& (pData
->options
& PLUGIN_OPTION_SEND_PITCHBEND
) == 0)
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
)
2401 midiData
[2] = midiEvent
.data
[2];
2404 midiData
[1] = midiEvent
.data
[1];
2408 fInputEvents
.addSimpleMidiEvent(true, midiEvent
.port
, eventTime
, midiData
);
2413 case MIDI_STATUS_NOTE_ON
:
2414 if (midiEvent
.data
[2] != 0)
2416 pData
->postponeNoteOnRtEvent(true, event
.channel
, midiEvent
.data
[1], midiEvent
.data
[2]);
2420 case MIDI_STATUS_NOTE_OFF
:
2421 pData
->postponeNoteOffRtEvent(true, event
.channel
, midiEvent
.data
[1]);
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)
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
;
2451 fOutputAudioBuffers
.buffers
[i
].data32
= audioOut
+ fOutputAudioBuffers
.extra
[i
].offset
;
2455 const clap_process_t process
= {
2456 static_cast<int64_t>(timeInfo
.frame
),
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);
2480 float* const oldBufLeft
= pData
->postProc
.extraBuffer
;
2482 for (uint32_t i
=0; i
< pData
->audioOut
.count
; ++i
)
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
));
2499 isPair
= (i
% 2 == 0);
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
)
2515 fAudioOutBuffers
[i
][k
] = oldBufLeft
[k
] * (1.0f
- balRangeL
);
2516 fAudioOutBuffers
[i
][k
] += fAudioOutBuffers
[i
+1][k
] * (1.0f
- balRangeR
);
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 // --------------------------------------------------------------------------------------------------------
2545 if (pData
->event
.portOut
!= nullptr && fExtensions
.params
!= nullptr)
2551 for (uint32_t k
=0; k
< pData
->param
.count
; ++k
)
2553 if (pData
->param
.data
[k
].type
!= PARAMETER_OUTPUT
)
2555 if (pData
->param
.data
[k
].mappedControlIndex
<= 0)
2557 if (!fExtensions
.params
->get_value(fPlugin
, pData
->param
.data
[k
].rindex
, &value
))
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,
2568 } // End of Control Output
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
))
2585 pData
->postponeParameterChangeRtEvent(true, static_cast<int32_t>(j
), ev
.param
.value
);
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
)
2594 fOutputEvents
.portData
[j
].port
->writeMidiEvent(ev
.midi
.header
.time
, 3, ev
.midi
.data
);
2601 fOutputEvents
.numEventsUsed
= 0;
2603 // --------------------------------------------------------------------------------------------------------
2605 #ifdef BUILD_BRIDGE_ALTERNATIVE_ARCH
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
);
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
];
2632 CarlaPlugin::bufferSizeChanged(newBufferSize
);
2636 // -------------------------------------------------------------------
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;
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
2681 // -------------------------------------------------------------------
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",
2698 fUI
.width
, fUI
.height
,
2699 fUI
.isResizingFromPlugin
, bool2str(fUI
.isResizingFromInit
), bool2str(fUI
.isResizingFromHost
));
2701 if (fExtensions
.gui
== nullptr)
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;
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;
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;
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;
2739 fUI
.height
= height2
;
2740 fUI
.window
->setSize(width2
, height2
, false, false);
2744 fExtensions
.gui
->set_size(fPlugin
, width2
, height2
);
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;
2807 fUI
.height
= height
;
2808 fUI
.window
->setSize(width
, height
, true, false);
2812 bool clapGuiRequestShow() override
2814 carla_stdout("CarlaPluginCLAP::clapGuiRequestShow()");
2818 bool clapGuiRequestHide() override
2820 carla_stdout("CarlaPluginCLAP::clapGuiRequestHide()");
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;
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
,
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);
2873 const int hostFd
= ::kqueue();
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
;
2885 if (::epoll_ctl(hostFd
, EPOLL_CTL_ADD
, fd
, &ev
) < 0)
2892 const HostPosixFileDescriptorDetails posixFD
= {
2897 fPosixFileDescriptors
.append(posixFD
);
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
)
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
;
2925 if (::epoll_ctl(posixFD
.hostFd
, EPOLL_CTL_MOD
, fd
, &ev
) < 0)
2929 posixFD
.flags
= flags
;
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);
2949 ::close(posixFD
.hostFd
);
2950 fPosixFileDescriptors
.remove(it
);
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,
2985 fTimers
.append(timer
);
2987 *timerId
= timer
.clapId
;
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
)
3007 #endif // CLAP_WINDOW_API_NATIVE
3009 // -------------------------------------------------------------------
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 // ---------------------------------------------------------------
3020 if (pData
->client
!= nullptr)
3022 pData
->engine
->setLastError("Plugin client is already registered");
3026 if (filename
== nullptr || filename
[0] == '\0')
3028 pData
->engine
->setLastError("null filename");
3032 // ---------------------------------------------------------------
3034 const clap_plugin_entry_t
* entry
;
3037 if (!water::File(filename
).existsAsFile())
3039 if (! fBundleLoader
.load(filename
))
3041 pData
->engine
->setLastError("Failed to load CLAP bundle executable");
3045 entry
= fBundleLoader
.getSymbol
<const clap_plugin_entry_t
*>(CFSTR("clap_entry"));
3050 if (! pData
->libOpen(filename
))
3052 pData
->engine
->setLastError(pData
->libError(filename
));
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");
3065 if (entry
->init
== nullptr || entry
->deinit
== nullptr || entry
->get_factory
== nullptr)
3067 pData
->engine
->setLastError("CLAP factory entries are null");
3071 if (!clap_version_is_compatible(entry
->clap_version
))
3073 pData
->engine
->setLastError("Incompatible CLAP plugin");
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");
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");
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");
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
;
3133 if (fPluginDescriptor
== nullptr)
3135 pData
->engine
->setLastError("Plugin library does not contain the requested plugin");
3142 pData
->engine
->setLastError("Plugin library contains no plugins");
3146 // ---------------------------------------------------------------
3148 fPlugin
= factory
->create_plugin(factory
, &fHost
, fPluginDescriptor
->id
);
3150 if (fPlugin
== nullptr)
3152 pData
->engine
->setLastError("Failed to create CLAP plugin instance");
3156 if (!fPlugin
->init(fPlugin
))
3158 pData
->engine
->setLastError("Failed to initialize CLAP plugin instance");
3162 // ---------------------------------------------------------------
3165 pData
->name
= pData
->engine
->getUniquePluginName(name
!= nullptr && name
[0] != '\0' ? name
3166 : fPluginDescriptor
->name
);
3167 pData
->filename
= carla_strdup(filename
);
3169 // ---------------------------------------------------------------
3172 pData
->client
= pData
->engine
->addClient(plugin
);
3174 if (pData
->client
== nullptr || ! pData
->client
->isOk())
3176 pData
->engine
->setLastError("Failed to register plugin client");
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
;
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;
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
;
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
;
3261 #ifdef CLAP_WINDOW_API_NATIVE
3263 #ifdef _POSIX_VERSION
3270 CARLA_DECLARE_NON_COPYABLE(Extensions
)
3273 #ifdef CLAP_WINDOW_API_NATIVE
3279 bool isResizingFromHost
;
3280 bool isResizingFromInit
;
3281 int isResizingFromPlugin
;
3283 uint32_t width
, height
;
3284 CarlaPluginUI
* window
;
3287 : initalized(false),
3291 isResizingFromHost(false),
3292 isResizingFromInit(false),
3293 isResizingFromPlugin(0),
3301 CARLA_SAFE_ASSERT(window
== nullptr);
3304 CARLA_DECLARE_NON_COPYABLE(UI
)
3307 #ifdef _POSIX_VERSION
3308 LinkedList
<HostPosixFileDescriptorDetails
> fPosixFileDescriptors
;
3310 LinkedList
<HostTimerDetails
> fTimers
;
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
;
3321 uint32_t fLastKnownLatency
;
3322 const bool kEngineHasIdleOnMainThread
;
3323 bool fNeedsParamFlush
;
3326 bool fNeedsIdleCallback
;
3329 BundleLoader fBundleLoader
;
3332 void runIdleCallbacksAsNeeded(const bool isIdleCallback
)
3334 if (isIdleCallback
&& (fNeedsRestart
|| fNeedsProcess
))
3336 carla_stdout("runIdleCallbacksAsNeeded %d %d", fNeedsRestart
, fNeedsProcess
);
3337 const bool needsRestart
= fNeedsRestart
;
3341 fNeedsRestart
= false;
3342 setActive(false, true, true);
3347 fNeedsProcess
= false;
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
;
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);
3394 for (int i
=0; i
<50; ++i
)
3396 #ifdef CARLA_CLAP_POSIX_EPOLL
3397 switch (::epoll_wait(posixFD
.hostFd
, &event
, 1, 0))
3399 switch (::kevent(posixFD
.hostFd
, &kev
, 1, &event
, 1, &timeout
))
3403 fExtensions
.posixFD
->on_fd(fPlugin
, posixFD
.pluginFd
, posixFD
.flags
);
3406 fExtensions
.posixFD
->on_fd(fPlugin
, posixFD
.pluginFd
, posixFD
.flags
| CLAP_POSIX_FD_ERROR
);
3412 carla_safe_exception("posix fd received abnormal value", __FILE__
, __LINE__
);
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
))
3450 // -------------------------------------------------------------------------------------------------------------------
3452 CARLA_BACKEND_END_NAMESPACE