diff --git a/source/backend/engine/CarlaEngine.cpp b/source/backend/engine/CarlaEngine.cpp index a9149fa73..f901b4f05 100644 --- a/source/backend/engine/CarlaEngine.cpp +++ b/source/backend/engine/CarlaEngine.cpp @@ -764,6 +764,10 @@ bool CarlaEngine::addPlugin(const BinaryType btype, plugin = CarlaPlugin::newAU(initializer); break; + case PLUGIN_CLAP: + plugin = CarlaPlugin::newCLAP(initializer); + break; + #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH case PLUGIN_INTERNAL: plugin = CarlaPlugin::newNative(initializer); @@ -800,10 +804,6 @@ bool CarlaEngine::addPlugin(const BinaryType btype, plugin = CarlaPlugin::newJSFX(initializer); break; - case PLUGIN_CLAP: - plugin = CarlaPlugin::newCLAP(initializer); - break; - case PLUGIN_JACK: # ifdef HAVE_JACK plugin = CarlaPlugin::newJackApp(initializer); @@ -819,7 +819,6 @@ bool CarlaEngine::addPlugin(const BinaryType btype, case PLUGIN_SFZ: case PLUGIN_JACK: case PLUGIN_JSFX: - case PLUGIN_CLAP: setLastError("Plugin bridges cannot handle this binary"); break; #endif diff --git a/source/backend/plugin/CarlaPluginCLAP.cpp b/source/backend/plugin/CarlaPluginCLAP.cpp index 458aca051..34dee1357 100644 --- a/source/backend/plugin/CarlaPluginCLAP.cpp +++ b/source/backend/plugin/CarlaPluginCLAP.cpp @@ -32,18 +32,24 @@ #include "water/files/File.h" #include "water/misc/Time.h" -// FIXME -#ifndef CLAP_WINDOW_API_NATIVE -#define CLAP_WINDOW_API_NATIVE "" -#define HAVE_X11 1 +#ifdef _POSIX_VERSION +# include +# include #endif +// FIXME +// #ifndef CLAP_WINDOW_API_NATIVE +// #define CLAP_WINDOW_API_NATIVE "" +// #define HAVE_X11 1 +// #endif + CARLA_BACKEND_START_NAMESPACE // -------------------------------------------------------------------------------------------------------------------- struct ClapEventData { uint16_t clapPortIndex; + uint32_t supportedDialects; CarlaEngineEventPort* port; }; @@ -112,6 +118,19 @@ struct CarlaPluginClapEventData { CARLA_DECLARE_NON_COPYABLE(CarlaPluginClapEventData) }; +#ifdef _POSIX_VERSION +// -------------------------------------------------------------------------------------------------------------------- + +struct HostPosixFileDescriptorDetails { + int hostFd; + int pluginFd; + clap_posix_fd_flags_t flags; +}; + +static constexpr const HostPosixFileDescriptorDetails kPosixFileDescriptorFallback = { -1, -1, 0x0 }; +static /* */ HostPosixFileDescriptorDetails kPosixFileDescriptorFallbackNC = { -1, -1, 0x0 }; +#endif + // -------------------------------------------------------------------------------------------------------------------- struct HostTimerDetails { @@ -133,26 +152,35 @@ struct carla_clap_host : clap_host_t { virtual void clapRequestProcess() = 0; virtual void clapRequestCallback() = 0; virtual void clapMarkDirty() = 0; - #ifdef CLAP_WINDOW_API_NATIVE + #ifdef CLAP_WINDOW_API_NATIVE // gui virtual void clapGuiResizeHintsChanged() = 0; virtual bool clapGuiRequestResize(uint width, uint height) = 0; virtual bool clapGuiRequestShow() = 0; virtual bool clapGuiRequestHide() = 0; virtual void clapGuiClosed(bool wasDestroyed) = 0; - // timer - virtual bool clapTimerRegister(uint32_t periodInMs, clap_id* timerId) = 0; - virtual bool clapTimerUnregister(clap_id timerId) = 0; + #ifdef _POSIX_VERSION + // posix fd + virtual bool clapRegisterPosixFD(int fd, clap_posix_fd_flags_t flags) = 0; + virtual bool clapModifyPosixFD(int fd, clap_posix_fd_flags_t flags) = 0; + virtual bool clapUnregisterPosixFD(int fd) = 0; #endif + // timer + virtual bool clapRegisterTimer(uint32_t periodInMs, clap_id* timerId) = 0; + virtual bool clapUnregisterTimer(clap_id timerId) = 0; + #endif }; Callbacks* const hostCallbacks; clap_host_state_t state; - #ifdef CLAP_WINDOW_API_NATIVE + #ifdef CLAP_WINDOW_API_NATIVE clap_host_gui_t gui; - clap_host_timer_support_t timer; + #ifdef _POSIX_VERSION + clap_host_posix_fd_support_t posixFD; #endif + clap_host_timer_support_t timer; + #endif carla_clap_host(Callbacks* const hostCb) : hostCallbacks(hostCb) @@ -171,92 +199,116 @@ struct carla_clap_host : clap_host_t { state.mark_dirty = carla_mark_dirty; - #ifdef CLAP_WINDOW_API_NATIVE + #ifdef CLAP_WINDOW_API_NATIVE gui.resize_hints_changed = carla_resize_hints_changed; gui.request_resize = carla_request_resize; gui.request_show = carla_request_show; gui.request_hide = carla_request_hide; gui.closed = carla_closed; + #ifdef _POSIX_VERSION + posixFD.register_fd = carla_register_fd; + posixFD.modify_fd = carla_modify_fd; + posixFD.unregister_fd = carla_unregister_fd; + #endif + timer.register_timer = carla_register_timer; timer.unregister_timer = carla_unregister_timer; - #endif + #endif } - static const void* carla_get_extension(const clap_host_t* const host, const char* const extension_id) + static CLAP_ABI const void* carla_get_extension(const clap_host_t* const host, const char* const extension_id) { - const carla_clap_host* const self = static_cast(host->host_data); + carla_clap_host* const self = static_cast(host->host_data); - #ifdef CLAP_WINDOW_API_NATIVE + if (std::strcmp(extension_id, CLAP_EXT_STATE) == 0) + return &self->state; + #ifdef CLAP_WINDOW_API_NATIVE if (std::strcmp(extension_id, CLAP_EXT_GUI) == 0) return &self->gui; + #ifdef _POSIX_VERSION + if (std::strcmp(extension_id, CLAP_EXT_POSIX_FD_SUPPORT) == 0) + return &self->posixFD; + #endif if (std::strcmp(extension_id, CLAP_EXT_TIMER_SUPPORT) == 0) return &self->timer; - #endif + #endif + carla_stderr("Plugin requested unsupported CLAP extension '%s'", extension_id); return nullptr; - - #ifndef CLAP_WINDOW_API_NATIVE - // unused - (void)self; - (void)extension_id; - #endif } - static void carla_request_restart(const clap_host_t* const host) + static CLAP_ABI void carla_request_restart(const clap_host_t* const host) { static_cast(host->host_data)->hostCallbacks->clapRequestRestart(); } - static void carla_request_process(const clap_host_t* const host) + static CLAP_ABI void carla_request_process(const clap_host_t* const host) { static_cast(host->host_data)->hostCallbacks->clapRequestProcess(); } - static void carla_request_callback(const clap_host_t* const host) + static CLAP_ABI void carla_request_callback(const clap_host_t* const host) { static_cast(host->host_data)->hostCallbacks->clapRequestCallback(); } - static void carla_mark_dirty(const clap_host_t* const host) + static CLAP_ABI void carla_mark_dirty(const clap_host_t* const host) { static_cast(host->host_data)->hostCallbacks->clapMarkDirty(); } #ifdef CLAP_WINDOW_API_NATIVE - static void carla_resize_hints_changed(const clap_host_t* const host) + static CLAP_ABI void carla_resize_hints_changed(const clap_host_t* const host) { static_cast(host->host_data)->hostCallbacks->clapGuiResizeHintsChanged(); } - static bool carla_request_resize(const clap_host_t* const host, const uint32_t width, const uint32_t height) + static CLAP_ABI bool carla_request_resize(const clap_host_t* const host, const uint32_t width, const uint32_t height) { return static_cast(host->host_data)->hostCallbacks->clapGuiRequestResize(width, height); } - static bool carla_request_show(const clap_host_t* const host) + static CLAP_ABI bool carla_request_show(const clap_host_t* const host) { return static_cast(host->host_data)->hostCallbacks->clapGuiRequestShow(); } - static bool carla_request_hide(const clap_host_t* const host) + static CLAP_ABI bool carla_request_hide(const clap_host_t* const host) { return static_cast(host->host_data)->hostCallbacks->clapGuiRequestHide(); } - static void carla_closed(const clap_host_t* const host, bool was_destroyed) + static CLAP_ABI void carla_closed(const clap_host_t* const host, bool was_destroyed) { static_cast(host->host_data)->hostCallbacks->clapGuiClosed(was_destroyed); } - static bool carla_register_timer(const clap_host_t* const host, const uint32_t period_ms, clap_id* const timer_id) + #ifdef _POSIX_VERSION + static CLAP_ABI bool carla_register_fd(const clap_host_t* const host, const int fd, const clap_posix_fd_flags_t flags) + { + return static_cast(host->host_data)->hostCallbacks->clapRegisterPosixFD(fd, flags); + } + + static CLAP_ABI bool carla_modify_fd(const clap_host_t* const host, const int fd, const clap_posix_fd_flags_t flags) { - return static_cast(host->host_data)->hostCallbacks->clapTimerRegister(period_ms, timer_id); + return static_cast(host->host_data)->hostCallbacks->clapModifyPosixFD(fd, flags); } - static bool carla_unregister_timer(const clap_host_t* const host, const clap_id timer_id) + static CLAP_ABI bool carla_unregister_fd(const clap_host_t* const host, const int fd) { - return static_cast(host->host_data)->hostCallbacks->clapTimerUnregister(timer_id); + return static_cast(host->host_data)->hostCallbacks->clapUnregisterPosixFD(fd); + } + #endif + + static CLAP_ABI bool carla_register_timer(const clap_host_t* const host, const uint32_t period_ms, clap_id* const timer_id) + { + return static_cast(host->host_data)->hostCallbacks->clapRegisterTimer(period_ms, timer_id); + } + + static CLAP_ABI bool carla_unregister_timer(const clap_host_t* const host, const clap_id timer_id) + { + return static_cast(host->host_data)->hostCallbacks->clapUnregisterTimer(timer_id); } #endif }; @@ -341,6 +393,7 @@ struct carla_clap_input_events : clap_input_events_t, CarlaPluginClapEventData { clap_event_param_value_t param; clap_event_param_gesture_t gesture; clap_event_midi_t midi; + clap_event_note_t note; clap_event_midi_sysex_t sysex; }; @@ -480,12 +533,30 @@ struct carla_clap_input_events : clap_input_events_t, CarlaPluginClapEventData { }; } - static uint32_t carla_size(const clap_input_events_t* const list) noexcept + void addSimpleNoteEvent(const bool isLive, const int16_t port, const uint32_t frameOffset, + const uint8_t channel, const uint8_t key, const uint8_t velocity) + { + if (numEventsUsed == numEventsAllocated) + return; + + const uint16_t eventType = velocity > 0 ? CLAP_EVENT_NOTE_ON : CLAP_EVENT_NOTE_OFF; + + events[numEventsUsed++].note = { + { sizeof(clap_event_note_t), frameOffset, 0, eventType, isLive ? (uint32_t)CLAP_EVENT_IS_LIVE : 0u }, + -1, + port, + channel, + key, + static_cast(velocity) / 127.0 + }; + } + + static CLAP_ABI uint32_t carla_size(const clap_input_events_t* const list) noexcept { return static_cast(list->ctx)->numEventsUsed; } - static const clap_event_header_t* carla_get(const clap_input_events_t* const list, const uint32_t index) noexcept + static CLAP_ABI const clap_event_header_t* carla_get(const clap_input_events_t* const list, const uint32_t index) noexcept { return &static_cast(list->ctx)->events[index].header; } @@ -568,7 +639,7 @@ struct carla_clap_output_events : clap_output_events_t, CarlaPluginClapEventData return true; } - static bool carla_try_push(const clap_output_events_t* const list, const clap_event_header_t* const event) + static CLAP_ABI bool carla_try_push(const clap_output_events_t* const list, const clap_event_header_t* const event) { return static_cast(list->ctx)->tryPush(event); } @@ -595,7 +666,8 @@ public: #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH fAudioOutBuffers(nullptr), #endif - fLastChunk(nullptr) + fLastChunk(nullptr), + fNeedsIdleCallback(false) { carla_debug("CarlaPluginCLAP::CarlaPluginCLAP(%p, %i)", engine, id); } @@ -604,6 +676,8 @@ public: { carla_debug("CarlaPluginCLAP::~CarlaPluginCLAP()"); + runIdleCallbacksAsNeeded(); + #ifdef CLAP_WINDOW_API_NATIVE // close UI if (fUI.isCreated) @@ -725,11 +799,13 @@ public: if (fExtensions.state->save(fPlugin, &stream)) { *dataPtr = fLastChunk = stream.buffer; + runIdleCallbacksAsNeeded(); return stream.size; } else { *dataPtr = fLastChunk = nullptr; + runIdleCallbacksAsNeeded(); return 0; } } @@ -744,6 +820,8 @@ public: if (fExtensions.state != nullptr) options |= PLUGIN_OPTION_USE_CHUNKS; + // TODO alternative if plugin does not support CLAP_NOTE_DIALECT_MIDI + if (fInputEvents.portCount != 0) { options |= PLUGIN_OPTION_SEND_CONTROL_CHANGES; @@ -806,10 +884,8 @@ public: CARLA_SAFE_ASSERT_RETURN(fExtensions.params != nullptr, false); CARLA_SAFE_ASSERT_RETURN(parameterId < pData->param.count, false); - const clap_id clapId = pData->param.data[parameterId].rindex; - clap_param_info_t paramInfo = {}; - CARLA_SAFE_ASSERT_RETURN(fExtensions.params->get_info(fPlugin, clapId, ¶mInfo), false); + CARLA_SAFE_ASSERT_RETURN(fExtensions.params->get_info(fPlugin, parameterId, ¶mInfo), false); std::strncpy(strBuf, paramInfo.name, STR_MAX); return true; @@ -844,10 +920,8 @@ public: CARLA_SAFE_ASSERT_RETURN(fExtensions.params != nullptr, false); CARLA_SAFE_ASSERT_RETURN(parameterId < pData->param.count, false); - const clap_id clapId = pData->param.data[parameterId].rindex; - clap_param_info_t paramInfo = {}; - CARLA_SAFE_ASSERT_RETURN(fExtensions.params->get_info(fPlugin, clapId, ¶mInfo), false); + CARLA_SAFE_ASSERT_RETURN(fExtensions.params->get_info(fPlugin, parameterId, ¶mInfo), false); if (paramInfo.module[0] == '\0') return false; @@ -915,6 +989,8 @@ public: const clap_istream_impl stream(data, dataSize); if (fExtensions.state->load(fPlugin, &stream)) pData->updateParameterValues(this, true, true, false); + + runIdleCallbacksAsNeeded(); } // ------------------------------------------------------------------- @@ -971,6 +1047,8 @@ public: fUI.window->show(); fUI.window->focus(); } + + runIdleCallbacksAsNeeded(); return; } @@ -1018,7 +1096,13 @@ public: { clap_window_t win = { CLAP_WINDOW_API_NATIVE, {} }; win.ptr = fUI.window->getPtr(); + fExtensions.gui->set_parent(fPlugin, &win); + + uint32_t width, height; + if (fExtensions.gui->get_size(fPlugin, &width, &height)) + fUI.window->setSize(width, height, false); + fExtensions.gui->show(fPlugin); fUI.window->show(); } @@ -1060,6 +1144,8 @@ public: fUI.window = nullptr; } } + + runIdleCallbacksAsNeeded(); } #endif @@ -1076,19 +1162,7 @@ public: void uiIdle() override { - const uint32_t currentTimeInMs = water::Time::getMillisecondCounter(); - - for (LinkedList::Itenerator it = fTimers.begin2(); it.valid(); it.next()) - { - HostTimerDetails& timer(it.getValue(kTimerFallbackNC)); - - if (currentTimeInMs > timer.lastCallTimeInMs + timer.periodInMs) - { - timer.lastCallTimeInMs = currentTimeInMs; - fExtensions.timer->on_timer(fPlugin, timer.clapId); - } - } - + runIdleCallbacksAsNeeded(); CarlaPlugin::uiIdle(); } @@ -1118,6 +1192,11 @@ public: const clap_plugin_params_t* paramsExt = static_cast( fPlugin->get_extension(fPlugin, CLAP_EXT_PARAMS)); + #ifdef _POSIX_VERSION + const clap_plugin_posix_fd_support_t* posixFdExt = static_cast( + fPlugin->get_extension(fPlugin, CLAP_EXT_POSIX_FD_SUPPORT)); + #endif + const clap_plugin_state_t* stateExt = static_cast( fPlugin->get_extension(fPlugin, CLAP_EXT_STATE)); @@ -1133,6 +1212,11 @@ public: if (paramsExt != nullptr && (paramsExt->count == nullptr || paramsExt->get_info == nullptr)) paramsExt = nullptr; + #ifdef _POSIX_VERSION + if (posixFdExt != nullptr && (posixFdExt->on_fd == nullptr)) + posixFdExt = nullptr; + #endif + if (stateExt != nullptr && (stateExt->save == nullptr || stateExt->load == nullptr)) stateExt = nullptr; @@ -1140,6 +1224,9 @@ public: timerExt = nullptr; fExtensions.params = paramsExt; + #ifdef _POSIX_VERSION + fExtensions.posixFD = posixFdExt; + #endif fExtensions.state = stateExt; fExtensions.timer = timerExt; @@ -1212,7 +1299,7 @@ public: clap_note_port_info_t portInfo = {}; CARLA_SAFE_ASSERT_BREAK(notePortsExt->get(fPlugin, i, true, &portInfo)); - if (portInfo.supported_dialects & CLAP_NOTE_DIALECT_MIDI) + if (portInfo.supported_dialects & (CLAP_NOTE_DIALECT_CLAP|CLAP_NOTE_DIALECT_MIDI)) ++mIns; } @@ -1225,13 +1312,10 @@ public: ++mOuts; } - for (uint32_t i=0; iget_info(fPlugin, i, ¶mInfo)); - - if ((paramInfo.flags & (CLAP_PARAM_IS_HIDDEN|CLAP_PARAM_IS_BYPASS)) == 0x0) - ++params; } if (aIns > 0) @@ -1325,12 +1409,14 @@ public: { clap_note_port_info_t portInfo = {}; CARLA_SAFE_ASSERT_BREAK(notePortsExt->get(fPlugin, i, true, &portInfo)); - CARLA_SAFE_ASSERT_BREAK(j < mIns); - if ((portInfo.supported_dialects & CLAP_NOTE_DIALECT_MIDI) == 0x0) + if ((portInfo.supported_dialects & (CLAP_NOTE_DIALECT_CLAP|CLAP_NOTE_DIALECT_MIDI)) == 0x0) continue; + CARLA_SAFE_ASSERT_UINT2_BREAK(j < mIns, j, mIns); + fInputEvents.portData[j].clapPortIndex = i; + fInputEvents.portData[j].supportedDialects = portInfo.supported_dialects; if (mIns > 1) { @@ -1360,12 +1446,14 @@ public: { clap_note_port_info_t portInfo = {}; CARLA_SAFE_ASSERT_BREAK(notePortsExt->get(fPlugin, i, false, &portInfo)); - CARLA_SAFE_ASSERT_BREAK(j < mOuts); if ((portInfo.supported_dialects & CLAP_NOTE_DIALECT_MIDI) == 0x0) continue; + CARLA_SAFE_ASSERT_UINT2_BREAK(j < mOuts, j, mOuts); + fOutputEvents.portData[j].clapPortIndex = i; + fOutputEvents.portData[j].supportedDialects = portInfo.supported_dialects; if (mOuts > 1) { @@ -1391,17 +1479,14 @@ public: } // Parameters - for (uint32_t i=0, j=0; iget_info(fPlugin, i, ¶mInfo)); - CARLA_SAFE_ASSERT_BREAK(j < params); + CARLA_SAFE_ASSERT_BREAK(i < params); - if (paramInfo.flags & (CLAP_PARAM_IS_HIDDEN|CLAP_PARAM_IS_BYPASS)) - continue; - - pData->param.data[j].index = j; - pData->param.data[j].rindex = static_cast(paramInfo.id); + pData->param.data[i].index = i; + pData->param.data[i].rindex = static_cast(paramInfo.id); double min, max, def, step, stepSmall, stepLarge; @@ -1417,14 +1502,18 @@ public: else if (def > max) def = max; - if (paramInfo.flags & CLAP_PARAM_IS_READONLY) + if (paramInfo.flags & (CLAP_PARAM_IS_HIDDEN|CLAP_PARAM_IS_BYPASS)) + { + pData->param.data[i].type = PARAMETER_UNKNOWN; + } + else if (paramInfo.flags & CLAP_PARAM_IS_READONLY) { - pData->param.data[j].type = PARAMETER_OUTPUT; + pData->param.data[i].type = PARAMETER_OUTPUT; needsCtrlOut = true; } else { - pData->param.data[j].type = PARAMETER_INPUT; + pData->param.data[i].type = PARAMETER_INPUT; } if (paramInfo.flags & CLAP_PARAM_IS_STEPPED) @@ -1432,7 +1521,7 @@ public: if (carla_isEqual(max - min, 1.0)) { step = stepSmall = stepLarge = 1.0; - pData->param.data[j].hints |= PARAMETER_IS_BOOLEAN; + pData->param.data[i].hints |= PARAMETER_IS_BOOLEAN; } else { @@ -1440,7 +1529,7 @@ public: stepSmall = 1.0; stepLarge = std::min(max - min, 10.0); } - pData->param.data[j].hints |= PARAMETER_IS_INTEGER; + pData->param.data[i].hints |= PARAMETER_IS_INTEGER; } else { @@ -1450,28 +1539,29 @@ public: stepLarge = range/10.0; } - pData->param.data[j].hints |= PARAMETER_IS_ENABLED; - pData->param.data[j].hints |= PARAMETER_USES_CUSTOM_TEXT; - - if (paramInfo.flags & CLAP_PARAM_IS_AUTOMATABLE) + if (pData->param.data[i].type != PARAMETER_UNKNOWN) { - pData->param.data[j].hints |= PARAMETER_IS_AUTOMATABLE; + pData->param.data[i].hints |= PARAMETER_IS_ENABLED; + pData->param.data[i].hints |= PARAMETER_USES_CUSTOM_TEXT; - if ((paramInfo.flags & CLAP_PARAM_IS_STEPPED) == 0x0) - pData->param.data[j].hints |= PARAMETER_CAN_BE_CV_CONTROLLED; - } + if (paramInfo.flags & CLAP_PARAM_IS_AUTOMATABLE) + { + pData->param.data[i].hints |= PARAMETER_IS_AUTOMATABLE; - pData->param.ranges[j].min = min; - pData->param.ranges[j].max = max; - pData->param.ranges[j].def = def; - pData->param.ranges[j].step = step; - pData->param.ranges[j].stepSmall = stepSmall; - pData->param.ranges[j].stepLarge = stepLarge; + if ((paramInfo.flags & CLAP_PARAM_IS_STEPPED) == 0x0) + pData->param.data[i].hints |= PARAMETER_CAN_BE_CV_CONTROLLED; + } + } - fInputEvents.updatedParams[j].clapId = paramInfo.id; - fInputEvents.updatedParams[j].cookie = paramInfo.cookie; + pData->param.ranges[i].min = min; + pData->param.ranges[i].max = max; + pData->param.ranges[i].def = def; + pData->param.ranges[i].step = step; + pData->param.ranges[i].stepSmall = stepSmall; + pData->param.ranges[i].stepLarge = stepLarge; - ++j; + fInputEvents.updatedParams[i].clapId = paramInfo.id; + fInputEvents.updatedParams[i].cookie = paramInfo.cookie; } if (needsCtrlIn) @@ -1555,6 +1645,8 @@ public: if (pData->active) activate(); + else + runIdleCallbacksAsNeeded(); carla_debug("CarlaPluginCLAP::reload() - end"); } @@ -1574,6 +1666,8 @@ public: // FIXME check return status fPlugin->activate(fPlugin, pData->engine->getSampleRate(), 1, pData->engine->getBufferSize()); fPlugin->start_processing(fPlugin); + + runIdleCallbacksAsNeeded(); } void deactivate() noexcept override @@ -1583,6 +1677,8 @@ public: // FIXME check return status fPlugin->stop_processing(fPlugin); fPlugin->deactivate(fPlugin); + + runIdleCallbacksAsNeeded(); } void process(const float* const* const audioIn, @@ -1650,6 +1746,8 @@ public: if (pData->needsReset) { + // TODO alternative if plugin does not support CLAP_NOTE_DIALECT_MIDI + if (pData->options & PLUGIN_OPTION_SEND_ALL_SOUND_OFF) { for (uint32_t p=0; p= 0 && note.channel < MAX_MIDI_CHANNELS); - const uint8_t data[3] = { - uint8_t((note.velo > 0 ? MIDI_STATUS_NOTE_ON : MIDI_STATUS_NOTE_OFF) | (note.channel & MIDI_CHANNEL_BIT)), - note.note, - note.velo - }; - fInputEvents.addSimpleMidiEvent(true, p, 0, data); + if (fInputEvents.portData[0].supportedDialects & CLAP_NOTE_DIALECT_MIDI) + { + const uint8_t data[3] = { + uint8_t((note.velo > 0 ? MIDI_STATUS_NOTE_ON : MIDI_STATUS_NOTE_OFF) | (note.channel & MIDI_CHANNEL_BIT)), + note.note, + note.velo + }; + fInputEvents.addSimpleMidiEvent(true, p, 0, data); + } + else + { + fInputEvents.addSimpleNoteEvent(true, -1, 0, note.channel, note.note, note.velo); + } } } @@ -2028,33 +2133,49 @@ public: if (midiEvent.size > sizeof(clap_event_midi::data)/sizeof(clap_event_midi::data[0])) continue; + CARLA_SAFE_ASSERT_BREAK(midiEvent.port < fInputEvents.portCount); + const uint8_t status = uint8_t(MIDI_GET_STATUS_FROM_DATA(midiEvent.data)); - if ((status == MIDI_STATUS_NOTE_OFF || status == MIDI_STATUS_NOTE_ON) && (pData->options & PLUGIN_OPTION_SKIP_SENDING_NOTES)) - continue; - if (status == MIDI_STATUS_CHANNEL_PRESSURE && (pData->options & PLUGIN_OPTION_SEND_CHANNEL_PRESSURE) == 0) - continue; - if (status == MIDI_STATUS_CONTROL_CHANGE && (pData->options & PLUGIN_OPTION_SEND_CONTROL_CHANGES) == 0) - continue; - if (status == MIDI_STATUS_POLYPHONIC_AFTERTOUCH && (pData->options & PLUGIN_OPTION_SEND_NOTE_AFTERTOUCH) == 0) - continue; - if (status == MIDI_STATUS_PITCH_WHEEL_CONTROL && (pData->options & PLUGIN_OPTION_SEND_PITCHBEND) == 0) - continue; + if (status == MIDI_STATUS_NOTE_OFF || status == MIDI_STATUS_NOTE_ON) + { + if (pData->options & PLUGIN_OPTION_SKIP_SENDING_NOTES) + continue; - // put back channel in data - uint8_t midiData[3] = { uint8_t(status | (event.channel & MIDI_CHANNEL_BIT)), 0, 0 }; + // plugin does not support MIDI, send a simple note instead + if ((fInputEvents.portData[midiEvent.port].supportedDialects & CLAP_NOTE_DIALECT_MIDI) == 0x0) + { + fInputEvents.addSimpleNoteEvent(true, midiEvent.port, event.time, + event.channel, midiEvent.data[1], midiEvent.data[2]); + } + } - switch (midiEvent.size) + if (fInputEvents.portData[midiEvent.port].supportedDialects & CLAP_NOTE_DIALECT_MIDI) { - case 3: - midiData[2] = midiEvent.data[2]; - // fall through - case 2: - midiData[1] = midiEvent.data[1]; - break; - } + if (status == MIDI_STATUS_CHANNEL_PRESSURE && (pData->options & PLUGIN_OPTION_SEND_CHANNEL_PRESSURE) == 0) + continue; + if (status == MIDI_STATUS_CONTROL_CHANGE && (pData->options & PLUGIN_OPTION_SEND_CONTROL_CHANGES) == 0) + continue; + if (status == MIDI_STATUS_POLYPHONIC_AFTERTOUCH && (pData->options & PLUGIN_OPTION_SEND_NOTE_AFTERTOUCH) == 0) + continue; + if (status == MIDI_STATUS_PITCH_WHEEL_CONTROL && (pData->options & PLUGIN_OPTION_SEND_PITCHBEND) == 0) + continue; + + // put back channel in data + uint8_t midiData[3] = { uint8_t(status | (event.channel & MIDI_CHANNEL_BIT)), 0, 0 }; + + switch (midiEvent.size) + { + case 3: + midiData[2] = midiEvent.data[2]; + // fall through + case 2: + midiData[1] = midiEvent.data[1]; + break; + } - fInputEvents.addSimpleMidiEvent(true, midiEvent.port, eventTime, midiData); + fInputEvents.addSimpleMidiEvent(true, midiEvent.port, eventTime, midiData); + } switch (status) { @@ -2354,20 +2475,27 @@ protected: void clapRequestRestart() override { + carla_stdout("CarlaPluginCLAP::clapRequestRestart()"); } void clapRequestProcess() override { + carla_stdout("CarlaPluginCLAP::clapRequestProcess()"); } void clapRequestCallback() override { + carla_stdout("CarlaPluginCLAP::clapRequestCallback()"); + + if (fPlugin->on_main_thread != nullptr) + fNeedsIdleCallback = true; } // ------------------------------------------------------------------- void clapMarkDirty() override { + carla_stdout("CarlaPluginCLAP::clapMarkDirty()"); } // ------------------------------------------------------------------- @@ -2375,12 +2503,13 @@ protected: #ifdef CLAP_WINDOW_API_NATIVE void clapGuiResizeHintsChanged() override { + carla_stdout("CarlaPluginCLAP::clapGuiResizeHintsChanged()"); } bool clapGuiRequestResize(const uint width, const uint height) override { CARLA_SAFE_ASSERT_RETURN(fUI.window != nullptr, false); - carla_debug("CarlaPluginCLAP::hostRequestResize(%u, %u)", width, height); + carla_stdout("CarlaPluginCLAP::hostRequestResize(%u, %u)", width, height); fUI.window->setSize(width, height, true); return true; @@ -2388,16 +2517,19 @@ protected: bool clapGuiRequestShow() override { + carla_stdout("CarlaPluginCLAP::clapGuiRequestShow()"); return false; } bool clapGuiRequestHide() override { + carla_stdout("CarlaPluginCLAP::clapGuiRequestHide()"); return false; } void clapGuiClosed(const bool wasDestroyed) override { + carla_stdout("CarlaPluginCLAP::clapGuiClosed(%s)", bool2str(wasDestroyed)); CARLA_SAFE_ASSERT_RETURN(!fUI.isEmbed,); CARLA_SAFE_ASSERT_RETURN(fUI.isVisible,); @@ -2419,10 +2551,127 @@ protected: // ------------------------------------------------------------------- - bool clapTimerRegister(const uint32_t periodInMs, clap_id* const timerId) override + #ifdef _POSIX_VERSION + bool clapRegisterPosixFD(const int fd, const clap_posix_fd_flags_t flags) override + { + carla_stdout("CarlaPluginCLAP::clapRegisterPosixFD(%i, %x)", fd, flags); + + // NOTE some plugins wont have their posix fd extension ready when first loaded, so try again here + if (fExtensions.posixFD == nullptr) + { + const clap_plugin_posix_fd_support_t* const posixFdExt = static_cast( + fPlugin->get_extension(fPlugin, CLAP_EXT_POSIX_FD_SUPPORT)); + + if (posixFdExt != nullptr && posixFdExt->on_fd != nullptr) + fExtensions.posixFD = posixFdExt; + } + + CARLA_SAFE_ASSERT_RETURN(fExtensions.posixFD != nullptr, false); + + // FIXME events only driven as long as UI is open + // CARLA_SAFE_ASSERT_RETURN(fUI.isCreated, false); + + if (flags & (CLAP_POSIX_FD_READ|CLAP_POSIX_FD_WRITE)) + { + const int epollfd = ::epoll_create1(0); + CARLA_SAFE_ASSERT_RETURN(epollfd >= 0, false); + + epoll_event ev = {}; + if (flags & CLAP_POSIX_FD_READ) + ev.events |= EPOLLIN; + if (flags & CLAP_POSIX_FD_WRITE) + ev.events |= EPOLLOUT; + ev.data.fd = fd; + + if (::epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev) < 0) + { + ::close(epollfd); + return false; + } + + const HostPosixFileDescriptorDetails posixFD = { + epollfd, + fd, + flags, + }; + fPosixFileDescriptors.append(posixFD); + + return true; + } + + return false; + } + + bool clapModifyPosixFD(const int fd, const clap_posix_fd_flags_t flags) override + { + carla_stdout("CarlaPluginCLAP::clapTimerUnregister(%i, %x)", fd, flags); + for (LinkedList::Itenerator it = fPosixFileDescriptors.begin2(); it.valid(); it.next()) + { + HostPosixFileDescriptorDetails& posixFD(it.getValue(kPosixFileDescriptorFallbackNC)); + + if (posixFD.pluginFd == fd) + { + if (posixFD.flags == flags) + return true; + + epoll_event ev = {}; + if (flags & CLAP_POSIX_FD_READ) + ev.events |= EPOLLIN; + if (flags & CLAP_POSIX_FD_WRITE) + ev.events |= EPOLLOUT; + ev.data.fd = fd; + + if (::epoll_ctl(posixFD.hostFd, EPOLL_CTL_MOD, fd, &ev) < 0) + return false; + + posixFD.flags = flags; + return true; + } + } + + return false; + } + + bool clapUnregisterPosixFD(const int fd) override + { + carla_stdout("CarlaPluginCLAP::clapTimerUnregister(%i)", fd); + for (LinkedList::Itenerator it = fPosixFileDescriptors.begin2(); it.valid(); it.next()) + { + const HostPosixFileDescriptorDetails& posixFD(it.getValue(kPosixFileDescriptorFallback)); + + if (posixFD.pluginFd == fd) + { + ::epoll_ctl(posixFD.hostFd, EPOLL_CTL_DEL, fd, nullptr); + ::close(posixFD.hostFd); + fPosixFileDescriptors.remove(it); + return true; + } + } + + return false; + } + #endif + + // ------------------------------------------------------------------- + + bool clapRegisterTimer(const uint32_t periodInMs, clap_id* const timerId) override { + carla_stdout("CarlaPluginCLAP::clapTimerRegister(%u, %p)", periodInMs, timerId); + + // NOTE some plugins wont have their timer extension ready when first loaded, so try again here + if (fExtensions.timer == nullptr) + { + const clap_plugin_timer_support_t* const timerExt = static_cast( + fPlugin->get_extension(fPlugin, CLAP_EXT_TIMER_SUPPORT)); + + if (timerExt != nullptr && timerExt->on_timer != nullptr) + fExtensions.timer = timerExt; + } + CARLA_SAFE_ASSERT_RETURN(fExtensions.timer != nullptr, false); - CARLA_SAFE_ASSERT_RETURN(fUI.isCreated, false); + + // FIXME events only driven as long as UI is open + // CARLA_SAFE_ASSERT_RETURN(fUI.isCreated, false); const HostTimerDetails timer = { fTimers.isNotEmpty() ? fTimers.getLast(kTimerFallback).clapId + 1 : 1, @@ -2435,8 +2684,9 @@ protected: return true; } - bool clapTimerUnregister(const clap_id timerId) override + bool clapUnregisterTimer(const clap_id timerId) override { + carla_stdout("CarlaPluginCLAP::clapTimerUnregister(%u)", timerId); for (LinkedList::Itenerator it = fTimers.begin2(); it.valid(); it.next()) { const HostTimerDetails& timer(it.getValue(kTimerFallback)); @@ -2647,6 +2897,8 @@ public: clap_note_port_info_t portInfo = {}; CARLA_SAFE_ASSERT_BREAK(notePortsExt->get(fPlugin, i, true, &portInfo)); + // TODO alternative if plugin does not support CLAP_NOTE_DIALECT_MIDI + if (portInfo.supported_dialects & CLAP_NOTE_DIALECT_MIDI) { if (isPluginOptionEnabled(options, PLUGIN_OPTION_SEND_CONTROL_CHANGES)) @@ -2685,22 +2937,28 @@ private: const clap_plugin_params_t* params; #ifdef CLAP_WINDOW_API_NATIVE const clap_plugin_gui_t* gui; + #endif + #ifdef _POSIX_VERSION + const clap_plugin_posix_fd_support_t* posixFD; #endif const clap_plugin_state_t* state; const clap_plugin_timer_support_t* timer; Extensions() : params(nullptr), - #ifdef CLAP_WINDOW_API_NATIVE + #ifdef CLAP_WINDOW_API_NATIVE gui(nullptr), - #endif + #endif + #ifdef _POSIX_VERSION + posixFD(nullptr), + #endif state(nullptr), timer(nullptr) {} CARLA_DECLARE_NON_COPYABLE(Extensions) } fExtensions; - #ifdef CLAP_WINDOW_API_NATIVE + #ifdef CLAP_WINDOW_API_NATIVE struct UI { bool initalized; bool isCreated; @@ -2721,8 +2979,11 @@ private: } } fUI; - LinkedList fTimers; + #ifdef _POSIX_VERSION + LinkedList fPosixFileDescriptors; #endif + LinkedList fTimers; + #endif carla_clap_input_audio_buffers fInputAudioBuffers; carla_clap_output_audio_buffers fOutputAudioBuffers; @@ -2732,10 +2993,59 @@ private: float** fAudioOutBuffers; #endif void* fLastChunk; + bool fNeedsIdleCallback; #ifdef CARLA_OS_MAC BundleLoader fBundleLoader; #endif + + void runIdleCallbacksAsNeeded() + { + if (fNeedsIdleCallback) + { + fNeedsIdleCallback = false; + fPlugin->on_main_thread(fPlugin); + } + + #ifdef CLAP_WINDOW_API_NATIVE + #ifdef _POSIX_VERSION + for (LinkedList::Itenerator it = fPosixFileDescriptors.begin2(); it.valid(); it.next()) + { + const HostPosixFileDescriptorDetails& posixFD(it.getValue(kPosixFileDescriptorFallback)); + + epoll_event events; + for (int i=0; i<50; ++i) + { + switch (::epoll_wait(posixFD.hostFd, &events, 1, 0)) + { + case 1: + fExtensions.posixFD->on_fd(fPlugin, posixFD.pluginFd, posixFD.flags); + break; + case -1: + fExtensions.posixFD->on_fd(fPlugin, posixFD.pluginFd, posixFD.flags | CLAP_POSIX_FD_ERROR); + // fall through + case 0: + i = 50; + break; + } + } + } + #endif + + const uint32_t currentTimeInMs = water::Time::getMillisecondCounter(); + + for (LinkedList::Itenerator it = fTimers.begin2(); it.valid(); it.next()) + { + HostTimerDetails& timer(it.getValue(kTimerFallbackNC)); + + if (currentTimeInMs > timer.lastCallTimeInMs + timer.periodInMs) + { + timer.lastCallTimeInMs = currentTimeInMs; + fExtensions.timer->on_timer(fPlugin, timer.clapId); + } + } + #endif + } }; // -------------------------------------------------------------------------------------------------------------------- diff --git a/source/backend/utils/Information.cpp b/source/backend/utils/Information.cpp index 67cb7124c..b70cfe8ea 100644 --- a/source/backend/utils/Information.cpp +++ b/source/backend/utils/Information.cpp @@ -166,6 +166,7 @@ const char* const* carla_get_supported_file_extensions() "so", #endif "vst3", + "clap", // Audio files #ifdef HAVE_SNDFILE diff --git a/source/discovery/carla-discovery.cpp b/source/discovery/carla-discovery.cpp index a4af44831..10f7ce63a 100644 --- a/source/discovery/carla-discovery.cpp +++ b/source/discovery/carla-discovery.cpp @@ -1861,10 +1861,10 @@ struct carla_clap_host : clap_host_t { request_callback = carla_request_callback; } - static const void* carla_get_extension(const clap_host_t*, const char*) { return nullptr; } - static void carla_request_restart(const clap_host_t*) {} - static void carla_request_process(const clap_host_t*) {} - static void carla_request_callback(const clap_host_t*) {} + static CLAP_ABI const void* carla_get_extension(const clap_host_t*, const char*) { return nullptr; } + static CLAP_ABI void carla_request_restart(const clap_host_t*) {} + static CLAP_ABI void carla_request_process(const clap_host_t*) {} + static CLAP_ABI void carla_request_callback(const clap_host_t*) {} }; static void do_clap_check(lib_t& libHandle, const char* const filename, const bool doInit) diff --git a/source/includes/clap/entry.h b/source/includes/clap/entry.h index 8f3cc1da2..0beaeed95 100644 --- a/source/includes/clap/entry.h +++ b/source/includes/clap/entry.h @@ -48,16 +48,16 @@ typedef struct clap_plugin_entry { // // If init() returns false, then the host must not call deinit() nor any other clap // related symbols from the DSO. - bool (*init)(const char *plugin_path); + bool (CLAP_ABI *init)(const char *plugin_path); // No more calls into the DSO must be made after calling deinit(). - void (*deinit)(void); + void (CLAP_ABI *deinit)(void); // Get the pointer to a factory. See plugin-factory.h for an example. // // Returns null if the factory is not provided. // The returned pointer must *not* be freed by the caller. - const void *(*get_factory)(const char *factory_id); + const void *(CLAP_ABI *get_factory)(const char *factory_id); } clap_plugin_entry_t; /* Entry point */ diff --git a/source/includes/clap/events.h b/source/includes/clap/events.h index db2314f15..1c24775c5 100644 --- a/source/includes/clap/events.h +++ b/source/includes/clap/events.h @@ -263,10 +263,10 @@ typedef struct clap_event_midi2 { typedef struct clap_input_events { void *ctx; // reserved pointer for the list - uint32_t (*size)(const struct clap_input_events *list); + uint32_t (CLAP_ABI *size)(const struct clap_input_events *list); // Don't free the returned event, it belongs to the list - const clap_event_header_t *(*get)(const struct clap_input_events *list, uint32_t index); + const clap_event_header_t *(CLAP_ABI *get)(const struct clap_input_events *list, uint32_t index); } clap_input_events_t; // Output event list, events must be sorted by time. @@ -275,7 +275,7 @@ typedef struct clap_output_events { // Pushes a copy of the event // returns false if the event could not be pushed to the queue (out of memory?) - bool (*try_push)(const struct clap_output_events *list, const clap_event_header_t *event); + bool (CLAP_ABI *try_push)(const struct clap_output_events *list, const clap_event_header_t *event); } clap_output_events_t; #ifdef __cplusplus diff --git a/source/includes/clap/ext/audio-ports.h b/source/includes/clap/ext/audio-ports.h index 5d0af462e..4539fed45 100644 --- a/source/includes/clap/ext/audio-ports.h +++ b/source/includes/clap/ext/audio-ports.h @@ -69,11 +69,11 @@ typedef struct clap_audio_port_info { typedef struct clap_plugin_audio_ports { // number of ports, for either input or output // [main-thread] - uint32_t (*count)(const clap_plugin_t *plugin, bool is_input); + uint32_t (CLAP_ABI *count)(const clap_plugin_t *plugin, bool is_input); // get info about about an audio port. // [main-thread] - bool (*get)(const clap_plugin_t *plugin, + bool (CLAP_ABI *get)(const clap_plugin_t *plugin, uint32_t index, bool is_input, clap_audio_port_info_t *info); @@ -102,13 +102,13 @@ enum { typedef struct clap_host_audio_ports { // Checks if the host allows a plugin to change a given aspect of the audio ports definition. // [main-thread] - bool (*is_rescan_flag_supported)(const clap_host_t *host, uint32_t flag); + bool (CLAP_ABI *is_rescan_flag_supported)(const clap_host_t *host, uint32_t flag); // Rescan the full list of audio ports according to the flags. // It is illegal to ask the host to rescan with a flag that is not supported. // Certain flags require the plugin to be de-activated. // [main-thread] - void (*rescan)(const clap_host_t *host, uint32_t flags); + void (CLAP_ABI *rescan)(const clap_host_t *host, uint32_t flags); } clap_host_audio_ports_t; #ifdef __cplusplus diff --git a/source/includes/clap/ext/gui.h b/source/includes/clap/ext/gui.h index da43612e5..c0e74739e 100644 --- a/source/includes/clap/ext/gui.h +++ b/source/includes/clap/ext/gui.h @@ -97,12 +97,12 @@ typedef struct clap_gui_resize_hints { typedef struct clap_plugin_gui { // Returns true if the requested gui api is supported // [main-thread] - bool (*is_api_supported)(const clap_plugin_t *plugin, const char *api, bool is_floating); + bool (CLAP_ABI *is_api_supported)(const clap_plugin_t *plugin, const char *api, bool is_floating); // Returns true if the plugin has a preferred api. // The host has no obligation to honor the plugin preferrence, this is just a hint. // [main-thread] - bool (*get_preferred_api)(const clap_plugin_t *plugin, const char **api, bool *is_floating); + bool (CLAP_ABI *get_preferred_api)(const clap_plugin_t *plugin, const char **api, bool *is_floating); // Create and allocate all resources necessary for the gui. // @@ -115,11 +115,11 @@ typedef struct clap_plugin_gui { // // After this call, the GUI may not be visible yet; don't forget to call show(). // [main-thread] - bool (*create)(const clap_plugin_t *plugin, const char *api, bool is_floating); + bool (CLAP_ABI *create)(const clap_plugin_t *plugin, const char *api, bool is_floating); // Free all resources associated with the gui. // [main-thread] - void (*destroy)(const clap_plugin_t *plugin); + void (CLAP_ABI *destroy)(const clap_plugin_t *plugin); // Set the absolute GUI scaling factor, and override any OS info. // Should not be used if the windowing api relies upon logical pixels. @@ -130,21 +130,21 @@ typedef struct clap_plugin_gui { // Returns true if the scaling could be applied // Returns false if the call was ignored, or the scaling could not be applied. // [main-thread] - bool (*set_scale)(const clap_plugin_t *plugin, double scale); + bool (CLAP_ABI *set_scale)(const clap_plugin_t *plugin, double scale); // Get the current size of the plugin UI. // clap_plugin_gui->create() must have been called prior to asking the size. // [main-thread] - bool (*get_size)(const clap_plugin_t *plugin, uint32_t *width, uint32_t *height); + bool (CLAP_ABI *get_size)(const clap_plugin_t *plugin, uint32_t *width, uint32_t *height); // Returns true if the window is resizeable (mouse drag). // Only for embedded windows. // [main-thread] - bool (*can_resize)(const clap_plugin_t *plugin); + bool (CLAP_ABI *can_resize)(const clap_plugin_t *plugin); // Returns true if the plugin can provide hints on how to resize the window. // [main-thread] - bool (*get_resize_hints)(const clap_plugin_t *plugin, clap_gui_resize_hints_t *hints); + bool (CLAP_ABI *get_resize_hints)(const clap_plugin_t *plugin, clap_gui_resize_hints_t *hints); // If the plugin gui is resizable, then the plugin will calculate the closest // usable size which fits in the given size. @@ -152,38 +152,38 @@ typedef struct clap_plugin_gui { // // Only for embedded windows. // [main-thread] - bool (*adjust_size)(const clap_plugin_t *plugin, uint32_t *width, uint32_t *height); + bool (CLAP_ABI *adjust_size)(const clap_plugin_t *plugin, uint32_t *width, uint32_t *height); // Sets the window size. Only for embedded windows. // [main-thread] - bool (*set_size)(const clap_plugin_t *plugin, uint32_t width, uint32_t height); + bool (CLAP_ABI *set_size)(const clap_plugin_t *plugin, uint32_t width, uint32_t height); // Embbeds the plugin window into the given window. // [main-thread & !floating] - bool (*set_parent)(const clap_plugin_t *plugin, const clap_window_t *window); + bool (CLAP_ABI *set_parent)(const clap_plugin_t *plugin, const clap_window_t *window); // Set the plugin floating window to stay above the given window. // [main-thread & floating] - bool (*set_transient)(const clap_plugin_t *plugin, const clap_window_t *window); + bool (CLAP_ABI *set_transient)(const clap_plugin_t *plugin, const clap_window_t *window); // Suggests a window title. Only for floating windows. // [main-thread & floating] - void (*suggest_title)(const clap_plugin_t *plugin, const char *title); + void (CLAP_ABI *suggest_title)(const clap_plugin_t *plugin, const char *title); // Show the window. // [main-thread] - bool (*show)(const clap_plugin_t *plugin); + bool (CLAP_ABI *show)(const clap_plugin_t *plugin); // Hide the window, this method does not free the resources, it just hides // the window content. Yet it may be a good idea to stop painting timers. // [main-thread] - bool (*hide)(const clap_plugin_t *plugin); + bool (CLAP_ABI *hide)(const clap_plugin_t *plugin); } clap_plugin_gui_t; typedef struct clap_host_gui { // The host should call get_resize_hints() again. // [thread-safe] - void (*resize_hints_changed)(const clap_host_t *host); + void (CLAP_ABI *resize_hints_changed)(const clap_host_t *host); /* Request the host to resize the client area to width, height. * Return true if the new size is accepted, false otherwise. @@ -194,24 +194,24 @@ typedef struct clap_host_gui { * satisfied then the host will call set_size() to revert the operation. * * [thread-safe] */ - bool (*request_resize)(const clap_host_t *host, uint32_t width, uint32_t height); + bool (CLAP_ABI *request_resize)(const clap_host_t *host, uint32_t width, uint32_t height); /* Request the host to show the plugin gui. * Return true on success, false otherwise. * [thread-safe] */ - bool (*request_show)(const clap_host_t *host); + bool (CLAP_ABI *request_show)(const clap_host_t *host); /* Request the host to hide the plugin gui. * Return true on success, false otherwise. * [thread-safe] */ - bool (*request_hide)(const clap_host_t *host); + bool (CLAP_ABI *request_hide)(const clap_host_t *host); // The floating window has been closed, or the connection to the gui has been lost. // // If was_destroyed is true, then the host must call clap_plugin_gui->destroy() to acknowledge // the gui destruction. // [thread-safe] - void (*closed)(const clap_host_t *host, bool was_destroyed); + void (CLAP_ABI *closed)(const clap_host_t *host, bool was_destroyed); } clap_host_gui_t; #ifdef __cplusplus diff --git a/source/includes/clap/ext/params.h b/source/includes/clap/ext/params.h index 93ab8419b..6ac3bcd57 100644 --- a/source/includes/clap/ext/params.h +++ b/source/includes/clap/ext/params.h @@ -183,28 +183,28 @@ typedef struct clap_param_info { typedef struct clap_plugin_params { // Returns the number of parameters. // [main-thread] - uint32_t (*count)(const clap_plugin_t *plugin); + uint32_t (CLAP_ABI *count)(const clap_plugin_t *plugin); // Copies the parameter's info to param_info and returns true on success. // [main-thread] - bool (*get_info)(const clap_plugin_t *plugin, + bool (CLAP_ABI *get_info)(const clap_plugin_t *plugin, uint32_t param_index, clap_param_info_t *param_info); // Gets the parameter plain value. // [main-thread] - bool (*get_value)(const clap_plugin_t *plugin, clap_id param_id, double *value); + bool (CLAP_ABI *get_value)(const clap_plugin_t *plugin, clap_id param_id, double *value); // Formats the display text for the given parameter value. // The host should always format the parameter value to text using this function // before displaying it to the user. // [main-thread] - bool (*value_to_text)( + bool (CLAP_ABI *value_to_text)( const clap_plugin_t *plugin, clap_id param_id, double value, char *display, uint32_t size); // Converts the display text to a parameter value. // [main-thread] - bool (*text_to_value)(const clap_plugin_t *plugin, + bool (CLAP_ABI *text_to_value)(const clap_plugin_t *plugin, clap_id param_id, const char *display, double *value); @@ -213,7 +213,7 @@ typedef struct clap_plugin_params { // This method must not be called concurrently to clap_plugin->process(). // // [active ? audio-thread : main-thread] - void (*flush)(const clap_plugin_t *plugin, + void (CLAP_ABI *flush)(const clap_plugin_t *plugin, const clap_input_events_t *in, const clap_output_events_t *out); } clap_plugin_params_t; @@ -272,11 +272,11 @@ typedef uint32_t clap_param_clear_flags; typedef struct clap_host_params { // Rescan the full list of parameters according to the flags. // [main-thread] - void (*rescan)(const clap_host_t *host, clap_param_rescan_flags flags); + void (CLAP_ABI *rescan)(const clap_host_t *host, clap_param_rescan_flags flags); // Clears references to a parameter. // [main-thread] - void (*clear)(const clap_host_t *host, clap_id param_id, clap_param_clear_flags flags); + void (CLAP_ABI *clear)(const clap_host_t *host, clap_id param_id, clap_param_clear_flags flags); // Request a parameter flush. // @@ -288,7 +288,7 @@ typedef struct clap_host_params { // plugin would already be within process() or flush(). // // [thread-safe,!audio-thread] - void (*request_flush)(const clap_host_t *host); + void (CLAP_ABI *request_flush)(const clap_host_t *host); } clap_host_params_t; #ifdef __cplusplus diff --git a/source/includes/clap/ext/posix-fd-support.h b/source/includes/clap/ext/posix-fd-support.h new file mode 100644 index 000000000..c0147ce65 --- /dev/null +++ b/source/includes/clap/ext/posix-fd-support.h @@ -0,0 +1,46 @@ +#pragma once + +#include "../plugin.h" + +// This extension let your plugin hook itself into the host select/poll/epoll/kqueue reactor. +// This is useful to handle asynchronous I/O on the main thread. +static CLAP_CONSTEXPR const char CLAP_EXT_POSIX_FD_SUPPORT[] = "clap.posix-fd-support"; + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + // IO events flags, they can be used to form a mask which describes: + // - which events you are interested in (register_fd/modify_fd) + // - which events happened (on_fd) + CLAP_POSIX_FD_READ = 1 << 0, + CLAP_POSIX_FD_WRITE = 1 << 1, + CLAP_POSIX_FD_ERROR = 1 << 2, +}; +typedef uint32_t clap_posix_fd_flags_t; + +typedef struct clap_plugin_posix_fd_support { + // This callback is "level-triggered". + // It means that a writable fd will continuously produce "on_fd()" events; + // don't forget using modify_fd() to remove the write notification once you're + // done writting. + // + // [main-thread] + void(CLAP_ABI *on_fd)(const clap_plugin_t *plugin, int fd, clap_posix_fd_flags_t flags); +} clap_plugin_posix_fd_support_t; + +typedef struct clap_host_posix_fd_support { + // [main-thread] + bool(CLAP_ABI *register_fd)(const clap_host_t *host, int fd, clap_posix_fd_flags_t flags); + + // [main-thread] + bool(CLAP_ABI *modify_fd)(const clap_host_t *host, int fd, clap_posix_fd_flags_t flags); + + // [main-thread] + bool(CLAP_ABI *unregister_fd)(const clap_host_t *host, int fd); +} clap_host_posix_fd_support_t; + +#ifdef __cplusplus +} +#endif diff --git a/source/includes/clap/ext/state.h b/source/includes/clap/ext/state.h index 42511fe7d..329ba45f6 100644 --- a/source/includes/clap/ext/state.h +++ b/source/includes/clap/ext/state.h @@ -13,19 +13,19 @@ typedef struct clap_plugin_state { // Saves the plugin state into stream. // Returns true if the state was correctly saved. // [main-thread] - bool (*save)(const clap_plugin_t *plugin, const clap_ostream_t *stream); + bool (CLAP_ABI *save)(const clap_plugin_t *plugin, const clap_ostream_t *stream); // Loads the plugin state from stream. // Returns true if the state was correctly restored. // [main-thread] - bool (*load)(const clap_plugin_t *plugin, const clap_istream_t *stream); + bool (CLAP_ABI *load)(const clap_plugin_t *plugin, const clap_istream_t *stream); } clap_plugin_state_t; typedef struct clap_host_state { // Tell the host that the plugin state has changed and should be saved again. // If a parameter value changes, then it is implicit that the state is dirty. // [main-thread] - void (*mark_dirty)(const clap_host_t *host); + void (CLAP_ABI *mark_dirty)(const clap_host_t *host); } clap_host_state_t; #ifdef __cplusplus diff --git a/source/includes/clap/ext/timer-support.h b/source/includes/clap/ext/timer-support.h index 1a8b9761d..3bbf267c0 100644 --- a/source/includes/clap/ext/timer-support.h +++ b/source/includes/clap/ext/timer-support.h @@ -10,7 +10,7 @@ extern "C" { typedef struct clap_plugin_timer_support { // [main-thread] - void (*on_timer)(const clap_plugin_t *plugin, clap_id timer_id); + void (CLAP_ABI *on_timer)(const clap_plugin_t *plugin, clap_id timer_id); } clap_plugin_timer_support_t; typedef struct clap_host_timer_support { @@ -18,10 +18,10 @@ typedef struct clap_host_timer_support { // The host may adjust the period if it is under a certain threshold. // 30 Hz should be allowed. // [main-thread] - bool (*register_timer)(const clap_host_t *host, uint32_t period_ms, clap_id *timer_id); + bool (CLAP_ABI *register_timer)(const clap_host_t *host, uint32_t period_ms, clap_id *timer_id); // [main-thread] - bool (*unregister_timer)(const clap_host_t *host, clap_id timer_id); + bool (CLAP_ABI *unregister_timer)(const clap_host_t *host, clap_id timer_id); } clap_host_timer_support_t; #ifdef __cplusplus diff --git a/source/includes/clap/host.h b/source/includes/clap/host.h index 0b67aad4b..a5eee4697 100644 --- a/source/includes/clap/host.h +++ b/source/includes/clap/host.h @@ -19,21 +19,21 @@ typedef struct clap_host { // Query an extension. // [thread-safe] - const void *(*get_extension)(const struct clap_host *host, const char *extension_id); + const void *(CLAP_ABI *get_extension)(const struct clap_host *host, const char *extension_id); // Request the host to deactivate and then reactivate the plugin. // The operation may be delayed by the host. // [thread-safe] - void (*request_restart)(const struct clap_host *host); + void (CLAP_ABI *request_restart)(const struct clap_host *host); // Request the host to activate and start processing the plugin. // This is useful if you have external IO and need to wake up the plugin from "sleep". // [thread-safe] - void (*request_process)(const struct clap_host *host); + void (CLAP_ABI *request_process)(const struct clap_host *host); // Request the host to schedule a call to plugin->on_main_thread(plugin) on the main thread. // [thread-safe] - void (*request_callback)(const struct clap_host *host); + void (CLAP_ABI *request_callback)(const struct clap_host *host); } clap_host_t; #ifdef __cplusplus diff --git a/source/includes/clap/plugin-factory.h b/source/includes/clap/plugin-factory.h index 07e8560c1..dab0bdc89 100644 --- a/source/includes/clap/plugin-factory.h +++ b/source/includes/clap/plugin-factory.h @@ -15,13 +15,13 @@ extern "C" { typedef struct clap_plugin_factory { // Get the number of plugins available. // [thread-safe] - uint32_t (*get_plugin_count)(const struct clap_plugin_factory *factory); + uint32_t (CLAP_ABI *get_plugin_count)(const struct clap_plugin_factory *factory); // Retrieves a plugin descriptor by its index. // Returns null in case of error. // The descriptor must not be freed. // [thread-safe] - const clap_plugin_descriptor_t *(*get_plugin_descriptor)( + const clap_plugin_descriptor_t *(CLAP_ABI *get_plugin_descriptor)( const struct clap_plugin_factory *factory, uint32_t index); // Create a clap_plugin by its plugin_id. @@ -29,7 +29,7 @@ typedef struct clap_plugin_factory { // The plugin is not allowed to use the host callbacks in the create method. // Returns null in case of error. // [thread-safe] - const clap_plugin_t *(*create_plugin)(const struct clap_plugin_factory *factory, + const clap_plugin_t *(CLAP_ABI *create_plugin)(const struct clap_plugin_factory *factory, const clap_host_t *host, const char *plugin_id); } clap_plugin_factory_t; diff --git a/source/includes/clap/plugin.h b/source/includes/clap/plugin.h index 04cbc8ed8..9d52c5293 100644 --- a/source/includes/clap/plugin.h +++ b/source/includes/clap/plugin.h @@ -38,12 +38,12 @@ typedef struct clap_plugin { // Must be called after creating the plugin. // If init returns false, the host must destroy the plugin instance. // [main-thread] - bool (*init)(const struct clap_plugin *plugin); + bool (CLAP_ABI *init)(const struct clap_plugin *plugin); // Free the plugin and its resources. // It is required to deactivate the plugin prior to this call. // [main-thread & !active] - void (*destroy)(const struct clap_plugin *plugin); + void (CLAP_ABI *destroy)(const struct clap_plugin *plugin); // Activate and deactivate the plugin. // In this call the plugin may allocate memory and prepare everything needed for the process @@ -52,21 +52,21 @@ typedef struct clap_plugin { // Once activated the latency and port configuration must remain constant, until deactivation. // // [main-thread & !active_state] - bool (*activate)(const struct clap_plugin *plugin, + bool (CLAP_ABI *activate)(const struct clap_plugin *plugin, double sample_rate, uint32_t min_frames_count, uint32_t max_frames_count); // [main-thread & active_state] - void (*deactivate)(const struct clap_plugin *plugin); + void (CLAP_ABI *deactivate)(const struct clap_plugin *plugin); // Call start processing before processing. // [audio-thread & active_state & !processing_state] - bool (*start_processing)(const struct clap_plugin *plugin); + bool (CLAP_ABI *start_processing)(const struct clap_plugin *plugin); // Call stop processing before sending the plugin to sleep. // [audio-thread & active_state & processing_state] - void (*stop_processing)(const struct clap_plugin *plugin); + void (CLAP_ABI *stop_processing)(const struct clap_plugin *plugin); // - Clears all buffers, performs a full reset of the processing state (filters, oscillators, // enveloppes, lfo, ...) and kills all voices. @@ -74,21 +74,21 @@ typedef struct clap_plugin { // - clap_process.steady_time may jump backward. // // [audio-thread & active_state] - void (*reset)(const struct clap_plugin *plugin); + void (CLAP_ABI *reset)(const struct clap_plugin *plugin); // process audio, events, ... // [audio-thread & active_state & processing_state] - clap_process_status (*process)(const struct clap_plugin *plugin, const clap_process_t *process); + clap_process_status (CLAP_ABI *process)(const struct clap_plugin *plugin, const clap_process_t *process); // Query an extension. // The returned pointer is owned by the plugin. // [thread-safe] - const void *(*get_extension)(const struct clap_plugin *plugin, const char *id); + const void *(CLAP_ABI *get_extension)(const struct clap_plugin *plugin, const char *id); // Called by the host on the main thread in response to a previous call to: // host->request_callback(host); // [main-thread] - void (*on_main_thread)(const struct clap_plugin *plugin); + void (CLAP_ABI *on_main_thread)(const struct clap_plugin *plugin); } clap_plugin_t; #ifdef __cplusplus diff --git a/source/includes/clap/private/macros.h b/source/includes/clap/private/macros.h index 1d622511e..15b6b1b38 100644 --- a/source/includes/clap/private/macros.h +++ b/source/includes/clap/private/macros.h @@ -17,6 +17,14 @@ # endif #endif +#if !defined(CLAP_ABI) +# if defined _WIN32 || defined __CYGWIN__ +# define CLAP_ABI __cdecl +# else +# define CLAP_ABI +# endif +#endif + #if defined(__cplusplus) && __cplusplus >= 201103L # define CLAP_HAS_CXX11 # define CLAP_CONSTEXPR constexpr diff --git a/source/includes/clap/stream.h b/source/includes/clap/stream.h index ecf059e87..20852d033 100644 --- a/source/includes/clap/stream.h +++ b/source/includes/clap/stream.h @@ -11,14 +11,14 @@ typedef struct clap_istream { void *ctx; // reserved pointer for the stream // returns the number of bytes read; 0 indicates end of file and -1 a read error - int64_t (*read)(const struct clap_istream *stream, void *buffer, uint64_t size); + int64_t (CLAP_ABI *read)(const struct clap_istream *stream, void *buffer, uint64_t size); } clap_istream_t; typedef struct clap_ostream { void *ctx; // reserved pointer for the stream // returns the number of bytes written; -1 on write error - int64_t (*write)(const struct clap_ostream *stream, const void *buffer, uint64_t size); + int64_t (CLAP_ABI *write)(const struct clap_ostream *stream, const void *buffer, uint64_t size); } clap_ostream_t; #ifdef __cplusplus diff --git a/source/utils/CarlaClapUtils.hpp b/source/utils/CarlaClapUtils.hpp index 291542eb4..2f967ea5f 100644 --- a/source/utils/CarlaClapUtils.hpp +++ b/source/utils/CarlaClapUtils.hpp @@ -28,6 +28,7 @@ #include "clap/ext/note-ports.h" #include "clap/ext/gui.h" #include "clap/ext/params.h" +#include "clap/ext/posix-fd-support.h" #include "clap/ext/state.h" #include "clap/ext/timer-support.h" @@ -173,7 +174,7 @@ struct clap_istream_impl : clap_istream_t { read = read_impl; } - static int64_t read_impl(const clap_istream_t* const stream, void* const buffer, const uint64_t size) noexcept + static CLAP_ABI int64_t read_impl(const clap_istream_t* const stream, void* const buffer, const uint64_t size) noexcept { clap_istream_impl* const self = static_cast(stream->ctx); @@ -202,7 +203,7 @@ struct clap_ostream_impl : clap_ostream_t { write = write_impl; } - static int64_t write_impl(const clap_ostream* const stream, const void* const buffer, const uint64_t size) noexcept + static CLAP_ABI int64_t write_impl(const clap_ostream* const stream, const void* const buffer, const uint64_t size) noexcept { CARLA_SAFE_ASSERT_RETURN(size != 0, 0);