#include #include #include #include #include #include #include "plugin.hh" namespace clap { namespace helpers { template const clap_plugin_render Plugin::_pluginRender = { clapRenderHasHardRealtimeRequirement, clapRenderSetMode, }; template const clap_plugin_thread_pool Plugin::_pluginThreadPool = { clapThreadPoolExec, }; template const clap_plugin_state Plugin::_pluginState = { clapStateSave, clapStateLoad, }; template const clap_plugin_preset_load Plugin::_pluginPresetLoad = { clapPresetLoadFromFile, }; template const clap_plugin_track_info Plugin::_pluginTrackInfo = { clapTrackInfoChanged, }; template const clap_plugin_audio_ports Plugin::_pluginAudioPorts = {clapAudioPortsCount, clapAudioPortsInfo}; template const clap_plugin_audio_ports_config Plugin::_pluginAudioPortsConfig = { clapAudioPortsConfigCount, clapAudioPortsGetConfig, clapAudioPortsSetConfig, }; template const clap_plugin_params Plugin::_pluginParams = { clapParamsCount, clapParamsInfo, clapParamsValue, clapParamsValueToText, clapParamsTextToValue, clapParamsFlush, }; template const clap_plugin_quick_controls Plugin::_pluginQuickControls = { clapQuickControlsPageCount, clapQuickControlsPageGet}; template const clap_plugin_latency Plugin::_pluginLatency = { clapLatencyGet, }; template const clap_plugin_note_ports Plugin::_pluginNotePorts = {clapNotePortsCount, clapNotePortsInfo}; template const clap_plugin_note_name Plugin::_pluginNoteName = { clapNoteNameCount, clapNoteNameGet, }; template const clap_plugin_timer_support Plugin::_pluginTimerSupport = {clapOnTimer}; template const clap_plugin_posix_fd_support Plugin::_pluginPosixFdSupport = { clapOnPosixFd, }; template const clap_plugin_gui Plugin::_pluginGui = { clapGuiIsApiSupported, clapGuiGetPreferredApi, clapGuiCreate, clapGuiDestroy, clapGuiSetScale, clapGuiGetSize, clapGuiCanResize, clapGuiGetResizeHints, clapGuiAdjustSize, clapGuiSetSize, clapGuiSetParent, clapGuiSetTransient, clapGuiSuggestTitle, clapGuiShow, clapGuiHide, }; template const clap_plugin_voice_info Plugin::_pluginVoiceInfo = { clapVoiceInfoGet, }; template const clap_plugin_tail Plugin::_pluginTail = { clapTailGet, }; template Plugin::Plugin(const clap_plugin_descriptor *desc, const clap_host *host) : _host(host) { _plugin.plugin_data = this; _plugin.desc = desc; _plugin.init = Plugin::clapInit; _plugin.destroy = Plugin::clapDestroy; _plugin.get_extension = nullptr; _plugin.process = nullptr; _plugin.activate = nullptr; _plugin.deactivate = nullptr; _plugin.start_processing = nullptr; _plugin.stop_processing = nullptr; _plugin.reset = nullptr; _plugin.on_main_thread = nullptr; } ///////////////////// // CLAP Interfaces // ///////////////////// //-------------// // clap_plugin // //-------------// template bool Plugin::clapInit(const clap_plugin *plugin) noexcept { auto &self = from(plugin, false); self._plugin.get_extension = Plugin::clapExtension; self._plugin.process = Plugin::clapProcess; self._plugin.activate = Plugin::clapActivate; self._plugin.deactivate = Plugin::clapDeactivate; self._plugin.start_processing = Plugin::clapStartProcessing; self._plugin.stop_processing = Plugin::clapStopProcessing; self._plugin.reset = Plugin::clapReset; self._plugin.on_main_thread = Plugin::clapOnMainThread; self._wasInitialized = true; self._host.init(); self.ensureMainThread("clap_plugin.init"); return self.init(); } template void Plugin::clapDestroy(const clap_plugin *plugin) noexcept { auto &self = from(plugin, false); self.ensureMainThread("clap_plugin.destroy"); self._isBeingDestroyed = true; self.runCallbacksOnMainThread(); delete &from(plugin); } template void Plugin::clapOnMainThread(const clap_plugin *plugin) noexcept { auto &self = from(plugin); self.ensureMainThread("clap_plugin.on_main_thread"); self.runCallbacksOnMainThread(); self.onMainThread(); } template void Plugin::runCallbacksOnMainThread() { if (l >= CheckingLevel::Minimal) { if (_host.canUseThreadCheck() && !_host.isMainThread()) { _host.pluginMisbehaving( "plugin called runCallbacksOnMainThread(), but not on the main thread!"); return; } } while (true) { std::function cb; { std::lock_guard guard(_mainThredCallbacksLock); if (_mainThredCallbacks.empty()) return; cb = std::move(_mainThredCallbacks.front()); _mainThredCallbacks.pop(); } if (cb) cb(); } } template bool Plugin::clapActivate(const clap_plugin *plugin, double sample_rate, uint32_t minFrameCount, uint32_t maxFrameCount) noexcept { auto &self = from(plugin); self.ensureMainThread("clap_plugin.activate"); if (l >= CheckingLevel::Minimal) { if (self._isActive) { self.hostMisbehaving("Plugin was activated twice"); if (sample_rate != self._sampleRate) { std::ostringstream msg; msg << "The plugin was activated twice and with different sample rates: " << self._sampleRate << " and " << sample_rate << ". The host must deactivate the plugin first." << std::endl << "Simulating deactivation."; self.hostMisbehaving(msg.str()); clapDeactivate(plugin); } } if (sample_rate <= 0) { std::ostringstream msg; msg << "The plugin was activated with an invalid sample rates: " << sample_rate; self.hostMisbehaving(msg.str()); return false; } if (minFrameCount < 1) { std::ostringstream msg; msg << "The plugin was activated with an invalid minimum frame count: " << minFrameCount; self.hostMisbehaving(msg.str()); return false; } if (maxFrameCount > INT32_MAX) { std::ostringstream msg; msg << "The plugin was activated with an invalid maximum frame count: " << maxFrameCount; self.hostMisbehaving(msg.str()); return false; } if (minFrameCount > maxFrameCount) { std::ostringstream msg; msg << "The plugin was activated with an invalid minmum and maximum frame count: min > " "max: " << minFrameCount << " > " << maxFrameCount; self.hostMisbehaving(msg.str()); return false; } } assert(!self._isActive); assert(self._sampleRate == 0); if (!self.activate(sample_rate, minFrameCount, maxFrameCount)) { assert(!self._isActive); assert(self._sampleRate == 0); return false; } self._isActive = true; self._sampleRate = sample_rate; return true; } template void Plugin::clapDeactivate(const clap_plugin *plugin) noexcept { auto &self = from(plugin); self.ensureMainThread("clap_plugin.deactivate"); if (l >= CheckingLevel::Minimal) { if (!self._isActive) { self.hostMisbehaving("The plugin was deactivated twice."); return; } } self.deactivate(); self._isActive = false; self._sampleRate = 0; } template bool Plugin::clapStartProcessing(const clap_plugin *plugin) noexcept { auto &self = from(plugin); self.ensureAudioThread("clap_plugin.start_processing"); if (l >= CheckingLevel::Minimal) { if (!self._isActive) { self.hostMisbehaving( "Host called clap_plugin.start_processing() on a deactivated plugin"); return false; } if (self._isProcessing) { self.hostMisbehaving("Host called clap_plugin.start_processing() twice"); return true; } } self._isProcessing = self.startProcessing(); return self._isProcessing; } template void Plugin::clapStopProcessing(const clap_plugin *plugin) noexcept { auto &self = from(plugin); self.ensureAudioThread("clap_plugin.stop_processing"); if (l >= CheckingLevel::Minimal) { if (!self._isActive) { self.hostMisbehaving( "Host called clap_plugin.stop_processing() on a deactivated plugin"); return; } if (!self._isProcessing) { self.hostMisbehaving("Host called clap_plugin.stop_processing() twice"); return; } } self.stopProcessing(); self._isProcessing = false; } template void Plugin::clapReset(const clap_plugin *plugin) noexcept { auto &self = from(plugin); self.ensureAudioThread("clap_plugin.reset"); if (l >= CheckingLevel::Minimal) { if (!self._isActive) { self.hostMisbehaving("Host called clap_plugin.reset() on a deactivated plugin"); return; } } self.reset(); } template clap_process_status Plugin::clapProcess(const clap_plugin *plugin, const clap_process *process) noexcept { auto &self = from(plugin); self.ensureAudioThread("clap_plugin.process"); if (l >= CheckingLevel::Minimal) { if (!self._isActive) { self.hostMisbehaving("Host called clap_plugin.process() on a deactivated plugin"); return CLAP_PROCESS_ERROR; } if (!self._isProcessing) { self.hostMisbehaving( "Host called clap_plugin.process() without calling clap_plugin.start_processing()"); return CLAP_PROCESS_ERROR; } } return self.process(process); } template const void *Plugin::clapExtension(const clap_plugin *plugin, const char *id) noexcept { auto &self = from(plugin); self.ensureMainThread("clap_plugin.extension"); if (!strcmp(id, CLAP_EXT_STATE) && self.implementsState()) return &_pluginState; if (!strcmp(id, CLAP_EXT_PRESET_LOAD) && self.implementsPresetLoad()) return &_pluginPresetLoad; if (!strcmp(id, CLAP_EXT_RENDER) && self.implementsRender()) return &_pluginRender; if (!strcmp(id, CLAP_EXT_TRACK_INFO) && self.implementsTrackInfo()) return &_pluginTrackInfo; if (!strcmp(id, CLAP_EXT_LATENCY) && self.implementsLatency()) return &_pluginLatency; if (!strcmp(id, CLAP_EXT_AUDIO_PORTS) && self.implementsAudioPorts()) return &_pluginAudioPorts; if (!strcmp(id, CLAP_EXT_PARAMS) && self.implementsParams()) return &_pluginParams; if (!strcmp(id, CLAP_EXT_QUICK_CONTROLS) && self.implementQuickControls()) return &_pluginQuickControls; if (!strcmp(id, CLAP_EXT_NOTE_PORTS) && self.implementsNotePorts()) return &_pluginNotePorts; if (!strcmp(id, CLAP_EXT_NOTE_NAME) && self.implementsNoteName()) return &_pluginNoteName; if (!strcmp(id, CLAP_EXT_THREAD_POOL) && self.implementsThreadPool()) return &_pluginThreadPool; if (!strcmp(id, CLAP_EXT_TIMER_SUPPORT) && self.implementsTimerSupport()) return &_pluginTimerSupport; if (!strcmp(id, CLAP_EXT_POSIX_FD_SUPPORT) && self.implementsPosixFdSupport()) return &_pluginPosixFdSupport; if (!strcmp(id, CLAP_EXT_GUI) && self.implementsGui()) return &_pluginGui; if (!strcmp(id, CLAP_EXT_VOICE_INFO) && self.implementsVoiceInfo()) return &_pluginVoiceInfo; if (!strcmp(id, CLAP_EXT_TAIL) && self.implementsTail()) return &_pluginTail; return self.extension(id); } //-------------------// // clap_plugin_state // //-------------------// template uint32_t Plugin::clapLatencyGet(const clap_plugin *plugin) noexcept { auto &self = from(plugin); self.ensureMainThread("clap_plugin_latency.get"); return self.latencyGet(); } //------------------// // clap_plugin_tail // //------------------// template uint32_t Plugin::clapTailGet(const clap_plugin_t *plugin) noexcept { auto &self = from(plugin); return self.latencyGet(); } //--------------------// // clap_plugin_render // //--------------------// template bool Plugin::clapRenderHasHardRealtimeRequirement(const clap_plugin *plugin) noexcept { auto &self = from(plugin); self.ensureMainThread("clap_plugin_render.has_hard_realtime_requirement"); return self.renderHasHardRealtimeRequirement(); } template bool Plugin::clapRenderSetMode(const clap_plugin *plugin, clap_plugin_render_mode mode) noexcept { auto &self = from(plugin); self.ensureMainThread("clap_plugin_render.set_mode"); switch (mode) { case CLAP_RENDER_REALTIME: case CLAP_RENDER_OFFLINE: return self.renderSetMode(mode); default: { std::ostringstream msg; msg << "host called clap_plugin_render.set_mode with an unknown mode : " << mode; self.hostMisbehaving(msg.str()); return false; } } } //-------------------------// // clap_plugin_thread_pool // //-------------------------// template void Plugin::clapThreadPoolExec(const clap_plugin *plugin, uint32_t task_index) noexcept { auto &self = from(plugin); self.threadPoolExec(task_index); } //-------------------// // clap_plugin_state // //-------------------// template bool Plugin::clapStateSave(const clap_plugin *plugin, const clap_ostream *stream) noexcept { auto &self = from(plugin); self.ensureMainThread("clap_plugin_state.save"); return self.stateSave(stream); } template bool Plugin::clapStateLoad(const clap_plugin *plugin, const clap_istream *stream) noexcept { auto &self = from(plugin); self.ensureMainThread("clap_plugin_state.load"); return self.stateLoad(stream); } //-------------------------// // clap_plugin_preset_load // //-------------------------// template bool Plugin::clapPresetLoadFromFile(const clap_plugin *plugin, const char *path) noexcept { auto &self = from(plugin); self.ensureMainThread("clap_plugin_preset_load.from_file"); if (l >= CheckingLevel::Minimal) { if (!path) { self.hostMisbehaving("host called clap_plugin_preset_load.from_file with a null path"); return false; } } // TODO check if the file is readable return self.presetLoadFromFile(path); } //------------------------// // clap_plugin_track_info // //------------------------// template void Plugin::clapTrackInfoChanged(const clap_plugin *plugin) noexcept { auto &self = from(plugin); self.ensureMainThread("clap_plugin_track_info.changed"); if (l >= CheckingLevel::Minimal) { if (!self._host.canUseTrackInfo()) { self.hostMisbehaving( "host called clap_plugin_track_info.changed() but does not provide a " "complete clap_host_track_info interface"); return; } } self.trackInfoChanged(); } //-------------------------// // clap_plugin_audio_ports // //-------------------------// template uint32_t Plugin::clapAudioPortsCount(const clap_plugin *plugin, bool is_input) noexcept { auto &self = from(plugin); self.ensureMainThread("clap_plugin_audio_ports.count"); return self.audioPortsCount(is_input); } template bool Plugin::clapAudioPortsInfo(const clap_plugin *plugin, uint32_t index, bool is_input, clap_audio_port_info *info) noexcept { auto &self = from(plugin); self.ensureMainThread("clap_plugin_audio_ports.info"); if (l >= CheckingLevel::Minimal) { auto count = clapAudioPortsCount(plugin, is_input); if (index >= count) { std::ostringstream msg; msg << "Host called clap_plugin_audio_ports.info() with an index out of bounds: " << index << " >= " << count; self.hostMisbehaving(msg.str()); return false; } } return self.audioPortsInfo(index, is_input, info); } template uint32_t Plugin::clapAudioPortsConfigCount(const clap_plugin *plugin) noexcept { auto &self = from(plugin); self.ensureMainThread("clap_plugin_audio_ports.config_count"); return self.audioPortsConfigCount(); } template bool Plugin::clapAudioPortsGetConfig(const clap_plugin *plugin, uint32_t index, clap_audio_ports_config *config) noexcept { auto &self = from(plugin); self.ensureMainThread("clap_plugin_audio_ports.get_config"); if (l >= CheckingLevel::Minimal) { auto count = clapAudioPortsConfigCount(plugin); if (index >= count) { std::ostringstream msg; msg << "called clap_plugin_audio_ports.get_config with an index out of bounds: " << index << " >= " << count; self.hostMisbehaving(msg.str()); return false; } } return self.audioPortsGetConfig(index, config); } template bool Plugin::clapAudioPortsSetConfig(const clap_plugin *plugin, clap_id config_id) noexcept { auto &self = from(plugin); self.ensureMainThread("clap_plugin_audio_ports.get_config"); if (l >= CheckingLevel::Minimal) { if (self.isActive()) self.hostMisbehaving( "it is illegal to call clap_audio_ports.set_config if the plugin is active"); } return self.audioPortsSetConfig(config_id); } template uint32_t Plugin::clapParamsCount(const clap_plugin *plugin) noexcept { auto &self = from(plugin); self.ensureMainThread("clap_plugin_params.count"); return self.paramsCount(); } //--------------------// // clap_plugin_params // //--------------------// template bool Plugin::clapParamsInfo(const clap_plugin *plugin, uint32_t param_index, clap_param_info *param_info) noexcept { auto &self = from(plugin); self.ensureMainThread("clap_plugin_params.info"); if (l >= CheckingLevel::Minimal) { auto count = clapParamsCount(plugin); if (param_index >= count) { std::ostringstream msg; msg << "called clap_plugin_params.info with an index out of bounds: " << param_index << " >= " << count; self.hostMisbehaving(msg.str()); return false; } } return self.paramsInfo(param_index, param_info); } template bool Plugin::clapParamsValue(const clap_plugin *plugin, clap_id param_id, double *value) noexcept { auto &self = from(plugin); self.ensureMainThread("clap_plugin_params.value"); if (l >= CheckingLevel::Minimal) { if (!self.isValidParamId(param_id)) { std::ostringstream msg; msg << "clap_plugin_params.value called with invalid param_id: " << param_id; self.hostMisbehaving(msg.str()); return false; } } // TODO extra checks return self.paramsValue(param_id, value); } template void Plugin::clapParamsFlush(const clap_plugin *plugin, const clap_input_events *in, const clap_output_events *out) noexcept { auto &self = from(plugin); self.ensureParamThread("clap_plugin_params.flush"); if (l >= CheckingLevel::Maximal) { if (!in) self.hostMisbehaving( "clap_plugin_params.flush called with an null input parameter change list"); else { uint32_t N = in->size(in); for (uint32_t i = 0; i < N; ++i) { auto ev = in->get(in, i); if (!ev) { std::ostringstream msg; msg << "clap_plugin_params.flush called null event inside the input list at " "index: " << i; self.hostMisbehaving(msg.str()); continue; } const bool isCoreSpace = ev->space_id == CLAP_CORE_EVENT_SPACE_ID; const bool isParamEvent = ev->type == CLAP_EVENT_PARAM_VALUE || ev->type == CLAP_EVENT_PARAM_MOD; if (!isCoreSpace || !isParamEvent) { std::ostringstream msg; msg << "host called clap_plugin_params.flush() with space_id = " << ev->space_id << ", and type = " << ev->type << " but this one must only contain CLAP_EVENT_PARAM_VALUE or " "CLAP_EVENT_PARAM_MOD" " event type from CLAP_CORE_EVENT_SPACE_ID."; self.hostMisbehaving(msg.str()); continue; } auto pev = reinterpret_cast(ev); if (self._host.canUseThreadCheck() && self._host.isMainThread() && !self.isValidParamId(pev->param_id)) { std::ostringstream msg; msg << "clap_plugin_params.flush called unknown paramId: " << pev->param_id; self.hostMisbehaving(msg.str()); continue; } // TODO: check range? } } if (!out) self.hostMisbehaving( "clap_plugin_params.flush called with an null output parameter change list"); } self.paramsFlush(in, out); } template bool Plugin::clapParamsValueToText(const clap_plugin *plugin, clap_id param_id, double value, char *display, uint32_t size) noexcept { auto &self = from(plugin); self.ensureMainThread("clap_plugin_params.value_to_text"); if (l >= CheckingLevel::Minimal) { if (!self.isValidParamId(param_id)) { std::ostringstream msg; msg << "clap_plugin_params.value_to_text called with invalid param_id: " << param_id; self.hostMisbehaving(msg.str()); return false; } if (!display) { self.hostMisbehaving( "clap_plugin_params.value_to_text called with a null display pointer"); return false; } if (size <= 1) { self.hostMisbehaving( "clap_plugin_params.value_to_text called with a empty buffer (less " "than one character)"); return false; } } return self.paramsValueToText(param_id, value, display, size); } template bool Plugin::clapParamsTextToValue(const clap_plugin *plugin, clap_id param_id, const char *display, double *value) noexcept { auto &self = from(plugin); self.ensureMainThread("clap_plugin_params.text_to_value"); if (l >= CheckingLevel::Minimal) { if (!self.isValidParamId(param_id)) { std::ostringstream msg; msg << "clap_plugin_params.text_to_value called with invalid param_id: " << param_id; self.hostMisbehaving(msg.str()); return false; } if (!display) { self.hostMisbehaving( "clap_plugin_params.text_to_value called with a null display pointer"); return false; } if (!value) { self.hostMisbehaving( "clap_plugin_params.text_to_value called with a null value pointer"); return false; } } return self.paramsTextToValue(param_id, display, value); } template bool Plugin::isValidParamId(clap_id param_id) const noexcept { checkMainThread(); auto count = paramsCount(); clap_param_info info; for (uint32_t i = 0; i < count; ++i) { if (!paramsInfo(i, &info)) // TODO: fatal error? continue; if (info.id == param_id) return true; } return false; } //----------------------------// // clap_plugin_quick_controls // //----------------------------// template uint32_t Plugin::clapQuickControlsPageCount(const clap_plugin *plugin) noexcept { auto &self = from(plugin); self.ensureMainThread("clap_plugin_quick_controls.page_count"); return self.quickControlsPageCount(); } template bool Plugin::clapQuickControlsPageGet(const clap_plugin *plugin, uint32_t page_index, clap_quick_controls_page *page) noexcept { auto &self = from(plugin); self.ensureMainThread("clap_plugin_quick_controls.page_info"); if (l >= CheckingLevel::Minimal) { uint32_t count = clapQuickControlsPageCount(plugin); if (page_index >= count) { std::ostringstream msg; msg << "Host called clap_plugin_quick_controls.page_info() with an index out of bounds: " << page_index << " >= " << count; self.hostMisbehaving(msg.str()); return false; } } return self.quickControlsPageGet(page_index, page); } //------------------------// // clap_plugin_note_ports // //------------------------// template uint32_t Plugin::clapNotePortsCount(const clap_plugin *plugin, bool is_input) noexcept { auto &self = from(plugin); self.ensureMainThread("clap_plugin_note_port.count"); return self.notePortsCount(is_input); } template bool Plugin::clapNotePortsInfo(const clap_plugin *plugin, uint32_t index, bool is_input, clap_note_port_info *info) noexcept { auto &self = from(plugin); self.ensureMainThread("clap_plugin_note_ports.info"); if (l >= CheckingLevel::Minimal) { auto count = clapNotePortsCount(plugin, is_input); if (index >= count) { std::ostringstream msg; msg << "Host called clap_plugin_note_ports.info() with an index out of bounds: " << index << " >= " << count; self.hostMisbehaving(msg.str()); return false; } } return self.notePortsInfo(index, is_input, info); } //-----------------------// // clap_plugin_note_name // //-----------------------// template uint32_t Plugin::clapNoteNameCount(const clap_plugin *plugin) noexcept { auto &self = from(plugin); self.ensureMainThread("clap_plugin_note_name.count"); return self.noteNameCount(); } template bool Plugin::clapNoteNameGet(const clap_plugin *plugin, uint32_t index, clap_note_name *note_name) noexcept { auto &self = from(plugin); self.ensureMainThread("clap_plugin_note_name.get"); if (l >= CheckingLevel::Minimal) { auto count = clapNoteNameCount(plugin); if (index >= count) { std::ostringstream msg; msg << "host called clap_plugin_note_name.get with an index out of bounds: " << index << " >= " << count; self.hostMisbehaving(msg.str()); return false; } } return self.noteNameGet(index, note_name); } //------------------------// // clap_plugin_event_loop // //------------------------// template void Plugin::clapOnTimer(const clap_plugin *plugin, clap_id timer_id) noexcept { auto &self = from(plugin); self.ensureMainThread("clap_plugin_timer_support.on_timer"); if (l >= CheckingLevel::Minimal) { if (timer_id == CLAP_INVALID_ID) { self.hostMisbehaving( "Host called clap_plugin_timer_support.on_timer with an invalid timer_id"); return; } } self.onTimer(timer_id); } template void Plugin::clapOnPosixFd(const clap_plugin *plugin, int fd, clap_posix_fd_flags_t flags) noexcept { auto &self = from(plugin); self.ensureMainThread("clap_plugin_event_loop.on_fd"); self.onPosixFd(fd, flags); } //------------------------// // clap_plugin_voice_info // //------------------------// template bool Plugin::clapVoiceInfoGet(const clap_plugin *plugin, clap_voice_info *info) noexcept { auto &self = from(plugin); self.ensureMainThread("clap_plugin_voice_info.get"); if (l >= CheckingLevel::Minimal) { if (!self._isActive) { self.hostMisbehaving( "clap_plugin_voice_info.get() requires the plugin to be activated"); return false; } } return self.voiceInfoGet(info); } //-----------------// // clap_plugin_gui // //-----------------// template bool Plugin::clapGuiGetSize(const clap_plugin *plugin, uint32_t *width, uint32_t *height) noexcept { auto &self = from(plugin); self.ensureMainThread("clap_plugin_gui.size"); if (l >= CheckingLevel::Minimal) { if (!self._isGuiCreated) { self.hostMisbehaving("clap_plugin_gui.size() was called without a prior call to " "clap_plugin_gui.create()"); return false; } } return self.guiGetSize(width, height); } template bool Plugin::clapGuiCanResize(const clap_plugin *plugin) noexcept { auto &self = from(plugin); self.ensureMainThread("clap_plugin_gui.can_resize"); if (l >= CheckingLevel::Minimal) { if (!self._isGuiCreated) { self.hostMisbehaving("clap_plugin_gui.can_resize() was called without a prior call to " "clap_plugin_gui.create()"); return false; } } return self.guiCanResize(); } template bool Plugin::clapGuiGetResizeHints(const clap_plugin_t *plugin, clap_gui_resize_hints_t *hints) noexcept { auto &self = from(plugin); self.ensureMainThread("clap_plugin_gui.get_resize_hints"); if (l >= CheckingLevel::Minimal) { if (!self._isGuiCreated) { self.hostMisbehaving( "clap_plugin_gui.get_resize_hints() was called without a prior call to " "clap_plugin_gui.create()"); return false; } } return self.guiGetResizeHints(hints); } template bool Plugin::clapGuiAdjustSize(const clap_plugin *plugin, uint32_t *width, uint32_t *height) noexcept { auto &self = from(plugin); self.ensureMainThread("clap_plugin_gui.adjust_size"); if (l >= CheckingLevel::Minimal) { if (!self._isGuiCreated) { self.hostMisbehaving("clap_plugin_gui.adjust_size() was called without a prior call to " "clap_plugin_gui.create()"); return false; } } return self.guiAdjustSize(width, height); } template bool Plugin::clapGuiSetSize(const clap_plugin *plugin, uint32_t width, uint32_t height) noexcept { auto &self = from(plugin); self.ensureMainThread("clap_plugin_gui.set_size"); if (l >= CheckingLevel::Minimal) { if (!self._isGuiCreated) { self.hostMisbehaving("clap_plugin_gui.set_size() was called without a prior call to " "clap_plugin_gui.create()"); return false; } if (!self.guiCanResize()) { self.hostMisbehaving( "clap_plugin_gui.set_size() was called but the gui is not resizable"); return false; } uint32_t testWidth = width; uint32_t testHeight = height; if (self.guiAdjustSize(&testWidth, &testHeight) && (width != testWidth || height != testHeight)) { std::ostringstream os; os << "clap_plugin_gui.set_size() was called with a size which was not adjusted by " "clap_plugin_gui.adjust_size(): " << width << "x" << height << " vs " << testWidth << "x" << testHeight; self.hostMisbehaving(os.str()); } } return self.guiSetSize(width, height); } template bool Plugin::clapGuiSetScale(const clap_plugin *plugin, double scale) noexcept { auto &self = from(plugin); self.ensureMainThread("clap_plugin_gui.set_scale"); if (l >= CheckingLevel::Minimal) { if (!self._isGuiCreated) { self.hostMisbehaving("clap_plugin_gui.set_scale() was called without a prior call to " "clap_plugin_gui.create()"); return false; } } return self.guiSetScale(scale); } template bool Plugin::clapGuiShow(const clap_plugin *plugin) noexcept { auto &self = from(plugin); self.ensureMainThread("clap_plugin_gui.show"); if (l >= CheckingLevel::Minimal) { if (!self._isGuiCreated) { self.hostMisbehaving("clap_plugin_gui.show() was called without a prior call to " "clap_plugin_gui.create()"); return false; } } return self.guiShow(); } template bool Plugin::clapGuiHide(const clap_plugin *plugin) noexcept { auto &self = from(plugin); self.ensureMainThread("clap_plugin_gui.hide"); if (l >= CheckingLevel::Minimal) { if (!self._isGuiCreated) { self.hostMisbehaving("clap_plugin_gui.hide() was called without a prior call to " "clap_plugin_gui.create()"); return false; } } return self.guiHide(); } template bool Plugin::clapGuiIsApiSupported(const clap_plugin *plugin, const char *api, bool isFloating) noexcept { auto &self = from(plugin); self.ensureMainThread("clap_plugin_gui.is_api_supported"); return self.guiIsApiSupported(api, isFloating); } template bool Plugin::clapGuiGetPreferredApi(const clap_plugin_t *plugin, const char **api, bool *isFloating) noexcept { auto &self = from(plugin); self.ensureMainThread("clap_plugin_gui.get_preferred_api"); return self.guiGetPreferredApi(api, isFloating); } template bool Plugin::clapGuiCreate(const clap_plugin *plugin, const char *api, bool isFloating) noexcept { auto &self = from(plugin); self.ensureMainThread("clap_plugin_gui.create"); if (l >= CheckingLevel::Minimal) { if (self._isGuiCreated) { self.hostMisbehaving( "clap_plugin_gui.create() was called while the plugin gui was already created"); return false; } if (!isFloating && !api) { self.hostMisbehaving( "clap_plugin_gui.create() was called with a null api and a non floating window"); return false; } } self._guiApi = api; self._isGuiFloating = isFloating; self._isGuiEmbedded = false; if (!self.guiCreate(api, isFloating)) return false; self._isGuiCreated = true; return true; } template void Plugin::clapGuiDestroy(const clap_plugin *plugin) noexcept { auto &self = from(plugin); self.ensureMainThread("clap_plugin_gui.destroy"); if (l >= CheckingLevel::Minimal) { if (!self._isGuiCreated) { self.hostMisbehaving( "clap_plugin_gui.destroy() was called while the plugin gui not created"); return; } } self.guiDestroy(); self._isGuiCreated = false; self._isGuiEmbedded = false; } template bool Plugin::clapGuiSetParent(const clap_plugin *plugin, const clap_window *window) noexcept { auto &self = from(plugin); self.ensureMainThread("clap_plugin_gui.set_parent"); if (l >= CheckingLevel::Minimal) { if (!self._isGuiCreated) { self.hostMisbehaving( "clap_plugin_gui.set_parent() was called while the plugin gui was not created"); return false; } if (self._isGuiFloating) { self.hostMisbehaving("clap_plugin_gui.set_parent() was called while the plugin gui is " "a floating window, use set_transient() instead"); return false; } } if (!self.guiSetParent(window)) return false; self._isGuiEmbedded = true; return true; } template bool Plugin::clapGuiSetTransient(const clap_plugin *plugin, const clap_window *window) noexcept { auto &self = from(plugin); self.ensureMainThread("clap_plugin_gui.set_transient"); if (l >= CheckingLevel::Minimal) { if (!self._isGuiCreated) { self.hostMisbehaving( "clap_plugin_gui.set_transient() was called while the plugin gui was not created"); return false; } if (!self._isGuiFloating) { self.hostMisbehaving("clap_plugin_gui.set_transient() was called while the plugin gui " "isn't a floating window, use set_parent() instead"); return false; } } return self.guiSetTransient(window); } template void Plugin::clapGuiSuggestTitle(const clap_plugin *plugin, const char *title) noexcept { auto &self = from(plugin); self.ensureMainThread("clap_plugin_gui.suggest_title"); if (l >= CheckingLevel::Minimal) { if (!self._isGuiCreated) { self.hostMisbehaving( "clap_plugin_gui.suggest_title() was called without a prior call to " "clap_plugin_gui.create()"); return; } if (!self._isGuiFloating) { self.hostMisbehaving( "clap_plugin_gui.suggest_title() but the gui was not created as a floating window"); return; } if (!title) { self.hostMisbehaving("clap_plugin_gui.suggest_title() was called with a null title"); return; } } return self.guiSuggestTitle(title); } ///////////// // Logging // ///////////// template void Plugin::log(clap_log_severity severity, const char *msg) const noexcept { logTee(severity, msg); _host.log(severity, msg); } template void Plugin::hostMisbehaving(const char *msg) const noexcept { log(CLAP_LOG_HOST_MISBEHAVING, msg); if (h == MisbehaviourHandler::Terminate) std::terminate(); } ///////////////////// // Thread Checking // ///////////////////// template void Plugin::checkMainThread() const noexcept { if (l == CheckingLevel::None) return; if (!_host.canUseThreadCheck() || _host.isMainThread()) return; std::terminate(); } template void Plugin::checkAudioThread() const noexcept { if (l == CheckingLevel::None) return; if (_host.canUseThreadCheck() || _host.isAudioThread()) return; std::terminate(); } template void Plugin::checkParamThread() const noexcept { if (l == CheckingLevel::None) return; if (isActive()) checkAudioThread(); else checkMainThread(); } template void Plugin::ensureParamThread(const char *method) const noexcept { if (l == CheckingLevel::None) return; if (isActive()) ensureAudioThread(method); else ensureMainThread(method); } template void Plugin::ensureMainThread(const char *method) const noexcept { if (l == CheckingLevel::None) return; if (!_host.canUseThreadCheck() || _host.isMainThread()) return; std::ostringstream msg; msg << "Host called the method " << method << "() on wrong thread! It must be called on main thread!"; hostMisbehaving(msg.str()); } template void Plugin::ensureAudioThread(const char *method) const noexcept { if (l == CheckingLevel::None) return; if (!_host.canUseThreadCheck() || _host.isAudioThread()) return; std::ostringstream msg; msg << "Host called the method " << method << "() on wrong thread! It must be called on audio thread!"; hostMisbehaving(msg.str()); } /////////////// // Utilities // /////////////// template Plugin &Plugin::from(const clap_plugin *plugin, bool requireInitialized) noexcept { if (l >= CheckingLevel::Minimal) { if (!plugin) { std::cerr << "called with a null clap_plugin pointer!" << std::endl; std::terminate(); } if (!plugin->plugin_data) { std::cerr << "called with a null clap_plugin->plugin_data pointer! The host must never " "change this pointer!" << std::endl; std::terminate(); } auto &self = *static_cast(plugin->plugin_data); if (requireInitialized && !self._wasInitialized) { self.hostMisbehaving("Host is required to call clap_plugin.init() first"); if (h == MisbehaviourHandler::Terminate) std::terminate(); } } return *static_cast(plugin->plugin_data); } template void Plugin::runOnMainThread(std::function callback) { if (_host.canUseThreadCheck() && _host.isMainThread()) { callback(); return; } std::lock_guard guard(_mainThredCallbacksLock); _mainThredCallbacks.emplace(std::move(callback)); _host.requestCallback(); } template uint32_t Plugin::compareAudioPortsInfo(const clap_audio_port_info &a, const clap_audio_port_info &b) noexcept { uint32_t flags = 0; if (strncmp(a.name, b.name, sizeof(a.name))) flags |= CLAP_AUDIO_PORTS_RESCAN_NAMES; if (a.flags != b.flags) flags |= CLAP_AUDIO_PORTS_RESCAN_FLAGS; if (a.channel_count != b.channel_count) flags |= CLAP_AUDIO_PORTS_RESCAN_CHANNEL_COUNT; if (strcmp(a.port_type, b.port_type)) flags |= CLAP_AUDIO_PORTS_RESCAN_PORT_TYPE; if (a.in_place_pair != b.in_place_pair) flags |= CLAP_AUDIO_PORTS_RESCAN_IN_PLACE_PAIR; return flags; } }} // namespace clap::helpers