diff --git a/deps/clap-juce-extensions/clap-libs/clap-helpers b/deps/clap-juce-extensions/clap-libs/clap-helpers deleted file mode 160000 index 2bb43c1..0000000 --- a/deps/clap-juce-extensions/clap-libs/clap-helpers +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 2bb43c18788c689708ead6f127a2d75e772ab389 diff --git a/deps/clap-juce-extensions/clap-libs/clap-helpers/.clang-format b/deps/clap-juce-extensions/clap-libs/clap-helpers/.clang-format new file mode 100644 index 0000000..726ee85 --- /dev/null +++ b/deps/clap-juce-extensions/clap-libs/clap-helpers/.clang-format @@ -0,0 +1,136 @@ +--- +Language: Cpp +AccessModifierOffset: -3 +AlignAfterOpenBracket: Align +AlignConsecutiveMacros: true +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlines: Right +AlignOperands: true +AlignTrailingComments: true +AllowAllArgumentsOnNextLine: true +AllowAllConstructorInitializersOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: Never +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: All +AllowShortLambdasOnASingleLine: All +AllowShortIfStatementsOnASingleLine: Never +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: Yes +BinPackArguments: false +BinPackParameters: false +BraceWrapping: + AfterCaseLabel: false + AfterClass: true + AfterControlStatement: true + AfterEnum: true + AfterFunction: true + AfterNamespace: true + AfterObjCDeclaration: true + AfterStruct: true + AfterUnion: true + AfterExternBlock: true + BeforeCatch: true + BeforeElse: true + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Attach +BreakBeforeInheritanceComma: false +BreakInheritanceList: BeforeColon +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: false +BreakConstructorInitializers: BeforeColon +BreakAfterJavaFieldAnnotations: false +BreakStringLiterals: true +ColumnLimit: 100 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: true +ConstructorInitializerAllOnOneLineOrOnePerLine: false +ConstructorInitializerIndentWidth: 3 +ContinuationIndentWidth: 3 +Cpp11BracedListStyle: true +DeriveLineEnding: false +DerivePointerAlignment: false +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IncludeBlocks: Preserve +IncludeCategories: + - Regex: '^"(llvm|llvm-c|clang|clang-c)/' + Priority: 2 + SortPriority: 0 + - Regex: '^(<|"(gtest|gmock|isl|json)/)' + Priority: 3 + SortPriority: 0 + - Regex: '.*' + Priority: 1 + SortPriority: 0 +IncludeIsMainRegex: '(Test)?$' +IncludeIsMainSourceRegex: '' +IndentCaseLabels: false +IndentGotoLabels: true +IndentPPDirectives: AfterHash +IndentWidth: 3 +IndentWrappedFunctionNames: false +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: true +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: All +ObjCBinPackProtocolList: Auto +ObjCBlockIndentWidth: 3 +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 60 +PointerAlignment: Right +ReflowComments: true +SortIncludes: true +SortUsingDeclarations: true +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyBlock: false +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: false +SpacesInConditionalStatement: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +SpaceBeforeSquareBrackets: false +Standard: Latest +StatementMacros: + - Q_UNUSED + - QT_REQUIRE_VERSION +TabWidth: 8 +UseCRLF: false +UseTab: Never +... + diff --git a/deps/clap-juce-extensions/clap-libs/clap-helpers/.gitrepo b/deps/clap-juce-extensions/clap-libs/clap-helpers/.gitrepo new file mode 100644 index 0000000..2f10a29 --- /dev/null +++ b/deps/clap-juce-extensions/clap-libs/clap-helpers/.gitrepo @@ -0,0 +1,12 @@ +; DO NOT EDIT (unless you know what you are doing) +; +; This subdirectory is a git "subrepo", and this file is maintained by the +; git-subrepo command. See https://github.com/git-commands/git-subrepo#readme +; +[subrepo] + remote = https://github.com/free-audio/clap-helpers.git + branch = main + commit = 2bb43c18788c689708ead6f127a2d75e772ab389 + parent = 8bbddf66e9d6b4b8e0a4fb98122ace8d70a5c14d + method = merge + cmdver = 0.4.3 diff --git a/deps/clap-juce-extensions/clap-libs/clap-helpers/CMakeLists.txt b/deps/clap-juce-extensions/clap-libs/clap-helpers/CMakeLists.txt new file mode 100644 index 0000000..82be7b1 --- /dev/null +++ b/deps/clap-juce-extensions/clap-libs/clap-helpers/CMakeLists.txt @@ -0,0 +1,25 @@ +cmake_minimum_required(VERSION 3.17) +cmake_policy(SET CMP0100 NEW) # handle .hh files +project(CLAP_HELPERS C CXX) +enable_testing() +find_package(Catch2 QUIET) + +add_library(clap-helpers INTERFACE) +set_target_properties(clap-helpers PROPERTIES + POSITION_INDEPENDENT_CODE ON + CXX_STANDARD 11) +target_include_directories(clap-helpers INTERFACE include) +target_link_libraries(clap-helpers INTERFACE clap-core) + +if (Catch2_FOUND) + add_executable(clap-helpers-tests + tests/plugin.cc + tests/main.cc) + set_target_properties(clap-helpers-tests PROPERTIES CXX_STANDARD 11) + target_link_libraries(clap-helpers-tests clap-helpers clap-core Catch2::Catch2) + target_compile_definitions(clap-helpers-tests PUBLIC -DCATCH_CONFIG_PREFIX_ALL) + add_test(NAME test-clap-helpers COMMAND clap-helpers-tests) + add_dependencies(clap-tests clap-helpers-tests) +endif() + +install(DIRECTORY include DESTINATION ".") diff --git a/deps/clap-juce-extensions/clap-libs/clap-helpers/LICENSE b/deps/clap-juce-extensions/clap-libs/clap-helpers/LICENSE new file mode 100644 index 0000000..428a3e5 --- /dev/null +++ b/deps/clap-juce-extensions/clap-libs/clap-helpers/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 Alexandre BIQUE + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/deps/clap-juce-extensions/clap-libs/clap-helpers/include/clap/helpers/checking-level.hh b/deps/clap-juce-extensions/clap-libs/clap-helpers/include/clap/helpers/checking-level.hh new file mode 100644 index 0000000..b0a9619 --- /dev/null +++ b/deps/clap-juce-extensions/clap-libs/clap-helpers/include/clap/helpers/checking-level.hh @@ -0,0 +1,9 @@ +#pragma once + +namespace clap { namespace helpers { + enum class CheckingLevel { + None, + Minimal, + Maximal, + }; +}} // namespace clap::helpers \ No newline at end of file diff --git a/deps/clap-juce-extensions/clap-libs/clap-helpers/include/clap/helpers/event-list.hh b/deps/clap-juce-extensions/clap-libs/clap-helpers/include/clap/helpers/event-list.hh new file mode 100644 index 0000000..10d4aad --- /dev/null +++ b/deps/clap-juce-extensions/clap-libs/clap-helpers/include/clap/helpers/event-list.hh @@ -0,0 +1,148 @@ +#pragma once + +#include +#include +#include + +#include + +#include "heap.hh" + +namespace clap { namespace helpers { + + class EventList { + public: + explicit EventList(uint32_t initialHeapSize = 4096, + uint32_t initialEventsCapacity = 128, + uint32_t maxEventSize = 1024) + : _maxEventSize(maxEventSize), _heap(initialHeapSize) { + _events.reserve(initialEventsCapacity); + } + + EventList(const EventList &) = delete; + EventList(EventList &&) = delete; + EventList &operator=(const EventList &) = delete; + EventList &operator=(EventList &&) = delete; + + static const constexpr size_t SAFE_ALIGN = 8; + + void reserveEvents(size_t capacity) { _events.reserve(capacity); } + + void reserveHeap(size_t size) { _heap.reserve(size); } + + clap_event_header *allocate(size_t align, size_t size) { + assert(size >= sizeof(clap_event_header)); + if (size > _maxEventSize) + return nullptr; + + // ensure we have space to store into the vector + if (_events.capacity() == _events.size()) + _events.reserve(_events.capacity() * 2); + + auto ptr = _heap.allocate(align, size); + _events.push_back(_heap.offsetFromBase(ptr)); + auto hdr = static_cast(ptr); + hdr->size = size; + return hdr; + } + + clap_event_header *tryAllocate(size_t align, size_t size) { + assert(size >= sizeof(clap_event_header)); + if (size > _maxEventSize) + return nullptr; + + // ensure we have space to store into the vector + if (_events.capacity() == _events.size()) + return nullptr; + + auto ptr = _heap.tryAllocate(align, size); + if (!ptr) + return nullptr; + + _events.push_back(_heap.offsetFromBase(ptr)); + auto hdr = static_cast(ptr); + hdr->size = size; + return hdr; + } + + template + T *allocate() { + return allocate(alignof(T), sizeof(T)); + } + + void push(const clap_event_header *h) { + auto ptr = allocate(SAFE_ALIGN, h->size); + if (!ptr) + return; + + std::memcpy(ptr, h, h->size); + } + + bool tryPush(const clap_event_header *h) { + auto ptr = tryAllocate(SAFE_ALIGN, h->size); + if (!ptr) + return false; + + std::memcpy(ptr, h, h->size); + return true; + } + + clap_event_header *get(uint32_t index) const { + const auto offset = _events.at(index); + auto const ptr = _heap.ptrFromBase(offset); + return static_cast(ptr); + } + + size_t size() const { return _events.size(); } + + bool empty() const { return _events.empty(); } + + void clear() { + _heap.clear(); + _events.clear(); + } + + const clap_input_events *clapInputEvents() const noexcept { return &_inputEvents; } + + const clap_output_events *clapOutputEvents() const noexcept { return &_outputEvents; } + + const clap_output_events *clapBoundedOutputEvents() const noexcept { + return &_boundedOutputEvents; + } + + private: + static uint32_t clapSize(const struct clap_input_events *list) { + auto *self = static_cast(list->ctx); + return self->size(); + } + + static const clap_event_header_t *clapGet(const struct clap_input_events *list, + uint32_t index) { + auto *self = static_cast(list->ctx); + return self->get(index); + } + + static bool clapPushBack(const struct clap_output_events *list, + const clap_event_header *event) { + auto *self = static_cast(list->ctx); + self->push(event); + return true; + } + + static bool clapBoundedPushBack(const struct clap_output_events *list, + const clap_event_header_t *event) { + auto *self = static_cast(list->ctx); + return self->tryPush(event); + } + + const clap_input_events _inputEvents = {this, &clapSize, &clapGet}; + const clap_output_events _outputEvents = {this, &clapPushBack}; + const clap_output_events _boundedOutputEvents = {this, &clapBoundedPushBack}; + + const uint32_t _maxEventSize; + + Heap _heap; + std::vector _events; + }; + +}} // namespace clap::helpers \ No newline at end of file diff --git a/deps/clap-juce-extensions/clap-libs/clap-helpers/include/clap/helpers/heap.hh b/deps/clap-juce-extensions/clap-libs/clap-helpers/include/clap/helpers/heap.hh new file mode 100644 index 0000000..e698b94 --- /dev/null +++ b/deps/clap-juce-extensions/clap-libs/clap-helpers/include/clap/helpers/heap.hh @@ -0,0 +1,84 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace clap { namespace helpers { + + /** Simple heap allocator which ensures alignment. */ + class Heap { + public: + explicit Heap(uint32_t initialSize = 4096) { reserve(initialSize); } + ~Heap() { std::free(_base); } + + Heap(const Heap &) = delete; + Heap(Heap &&) = delete; + Heap &operator=(const Heap &) = delete; + Heap &operator=(Heap &&) = delete; + + void reserve(const size_t heapSize) { + if (heapSize <= _size) + return; + + auto *const ptr = static_cast(std::realloc(_base, heapSize)); + if (!ptr) + throw std::bad_alloc(); + + _base = ptr; + _size = heapSize; + } + + void clear() { _brk = 0; } + + void *tryAllocate(uint32_t align, uint32_t size) { + assert(_brk <= _size); + void *ptr = _base + _brk; + size_t space = _size - _brk; + + if (!std::align(align, size, ptr, space)) + return nullptr; + + auto offset = static_cast(ptr) - _base; + _brk = offset + size; + std::memset(ptr, 0, size); + return ptr; + } + + void *allocate(uint32_t align, uint32_t size) { + assert(_brk <= _size); + if (size + _brk > _size) + reserve(_size * 2); + + auto ptr = tryAllocate(align, size); + assert(ptr); + return ptr; + } + + void *ptrFromBase(size_t offset) const { + assert(offset <= _brk && "out of range"); + + return static_cast(_base) + offset; + } + + size_t offsetFromBase(const void *ptr) const { + assert(ptr >= _base && "ptr before heap's base"); + size_t offset = static_cast(ptr) - _base; + assert(offset < _size && "ptr after heap's end"); + return offset; + } + + size_t size() const { return _size; } + + bool empty() const { return _brk == 0; } + + private: + size_t _size = 0; + size_t _brk = 0; + uint8_t *_base = nullptr; + }; + +}} // namespace clap::helpers \ No newline at end of file diff --git a/deps/clap-juce-extensions/clap-libs/clap-helpers/include/clap/helpers/host-proxy.hh b/deps/clap-juce-extensions/clap-libs/clap-helpers/include/clap/helpers/host-proxy.hh new file mode 100644 index 0000000..f5c94d3 --- /dev/null +++ b/deps/clap-juce-extensions/clap-libs/clap-helpers/include/clap/helpers/host-proxy.hh @@ -0,0 +1,170 @@ +#pragma once + +#include + +#include + +#include "checking-level.hh" +#include "misbehaviour-handler.hh" + +namespace clap { namespace helpers { + template + class HostProxy { + public: + HostProxy(const clap_host *host); + + void init(); + + /////////////// + // clap_host // + /////////////// + template + void getExtension(const T *&ptr, const char *id) noexcept; + void requestCallback() noexcept; + void requestRestart() noexcept; + void requestProcess() noexcept; + + /////////////////// + // clap_host_log // + /////////////////// + bool canUseHostLog() const noexcept; + void log(clap_log_severity severity, const char *msg) const noexcept; + void hostMisbehaving(const char *msg) const noexcept; + void hostMisbehaving(const std::string &msg) const noexcept { hostMisbehaving(msg.c_str()); } + void pluginMisbehaving(const char *msg) const noexcept; + void pluginMisbehaving(const std::string &msg) const noexcept { + pluginMisbehaving(msg.c_str()); + } + + //////////////////////////// + // clap_host_thread_check // + //////////////////////////// + bool canUseThreadCheck() const noexcept; + bool isMainThread() const noexcept; + bool isAudioThread() const noexcept; + + ////////////////////////////////// + // clap_host_audio_ports_config // + ////////////////////////////////// + bool canUseAudioPortsConfig() const noexcept; + void audioPortsConfigRescan() const noexcept; + + /////////////////////////// + // clap_host_audio_ports // + /////////////////////////// + bool canUseAudioPorts() const noexcept; + void audioPortsRescan(uint32_t flags) const noexcept; + + ////////////////////////// + // clap_host_note_ports // + ////////////////////////// + bool canUseNotePorts() const noexcept; + void notePortsRescan(uint32_t flags) const noexcept; + + ///////////////////// + // clap_host_state // + ///////////////////// + bool canUseState() const noexcept; + void stateMarkDirty() const noexcept; + + /////////////////////// + // clap_host_latency // + /////////////////////// + bool canUseLatency() const noexcept; + void latencyChanged() const noexcept; + + //////////////////// + // clap_host_tail // + //////////////////// + bool canUseTail() const noexcept; + void tailChanged() const noexcept; + + ///////////////////////// + // clap_host_note_name // + ///////////////////////// + bool canUseNoteName() const noexcept; + void noteNameChanged() const noexcept; + + ////////////////////// + // clap_host_params // + ////////////////////// + bool canUseParams() const noexcept; + void paramsRescan(clap_param_rescan_flags flags) const noexcept; + void paramsClear(clap_id param_id, clap_param_clear_flags flags) const noexcept; + void paramsRequestFlush() const noexcept; + + ////////////////////////// + // clap_host_track_info // + ////////////////////////// + bool canUseTrackInfo() const noexcept; + bool trackInfoGet(clap_track_info *info) const noexcept; + + /////////////////// + // clap_host_gui // + /////////////////// + bool canUseGui() const noexcept; + bool guiRequestResize(uint32_t width, uint32_t height) const noexcept; + bool guiRequestShow() const noexcept; + bool guiRequestHide() const noexcept; + void guiClosed(bool wasDestroyed) const noexcept; + + ///////////////////////////// + // clap_host_timer_support // + ///////////////////////////// + bool canUseTimerSupport() const noexcept; + bool timerSupportRegister(uint32_t period_ms, clap_id *timer_id) const noexcept; + bool timerSupportUnregister(clap_id timer_id) const noexcept; + + ////////////////////////// + // clap_host_fd_support // + ////////////////////////// + bool canUsePosixFdSupport() const noexcept; + bool posixFdSupportRegister(int fd, int flags) const noexcept; + bool posixFdSupportModify(int fd, int flags) const noexcept; + bool posixFdSupportUnregister(int fd) const noexcept; + + ////////////////////////////// + // clap_host_quick_controls // + ////////////////////////////// + bool canUseQuickControls() const noexcept; + void quickControlsChanged() const noexcept; + void quickControlsSuggestPage(clap_id page_id) const noexcept; + + /////////////////////////// + // clap_host_thread_pool // + /////////////////////////// + bool canUseThreadPool() const noexcept; + bool threadPoolRequestExec(uint32_t numTasks) const noexcept; + + ////////////////////////// + // clap_host_voice_info // + ////////////////////////// + bool canUseVoiceInfo() const noexcept; + void voiceInfoChanged() const noexcept; + + protected: + void ensureMainThread(const char *method) const noexcept; + void ensureAudioThread(const char *method) const noexcept; + + const clap_host *const _host; + + const clap_host_log *_hostLog = nullptr; + const clap_host_thread_check *_hostThreadCheck = nullptr; + const clap_host_thread_pool *_hostThreadPool = nullptr; + const clap_host_audio_ports *_hostAudioPorts = nullptr; + const clap_host_audio_ports_config *_hostAudioPortsConfig = nullptr; + const clap_host_note_ports *_hostNotePorts = nullptr; + const clap_host_file_reference *_hostFileReference = nullptr; + const clap_host_latency *_hostLatency = nullptr; + const clap_host_gui *_hostGui = nullptr; + const clap_host_timer_support *_hostTimerSupport = nullptr; + const clap_host_posix_fd_support *_hostPosixFdSupport = nullptr; + const clap_host_params *_hostParams = nullptr; + const clap_host_track_info *_hostTrackInfo = nullptr; + const clap_host_state *_hostState = nullptr; + const clap_host_note_name *_hostNoteName = nullptr; + const clap_host_quick_controls *_hostQuickControls = nullptr; + const clap_host_voice_info *_hostVoiceInfo = nullptr; + const clap_host_tail *_hostTail = nullptr; + }; +}} // namespace clap::helpers \ No newline at end of file diff --git a/deps/clap-juce-extensions/clap-libs/clap-helpers/include/clap/helpers/host-proxy.hxx b/deps/clap-juce-extensions/clap-libs/clap-helpers/include/clap/helpers/host-proxy.hxx new file mode 100644 index 0000000..e825878 --- /dev/null +++ b/deps/clap-juce-extensions/clap-libs/clap-helpers/include/clap/helpers/host-proxy.hxx @@ -0,0 +1,546 @@ +#pragma once + +#include +#include +#include + +#include "host-proxy.hh" + +namespace clap { namespace helpers { + template + HostProxy::HostProxy(const ::clap_host *host) : _host(host) {} + + template + void HostProxy::init() { + getExtension(_hostLog, CLAP_EXT_LOG); + getExtension(_hostThreadCheck, CLAP_EXT_THREAD_CHECK); + getExtension(_hostThreadPool, CLAP_EXT_THREAD_POOL); + getExtension(_hostAudioPorts, CLAP_EXT_AUDIO_PORTS); + getExtension(_hostAudioPortsConfig, CLAP_EXT_AUDIO_PORTS_CONFIG); + getExtension(_hostNotePorts, CLAP_EXT_NOTE_PORTS); + getExtension(_hostTimerSupport, CLAP_EXT_TIMER_SUPPORT); + getExtension(_hostPosixFdSupport, CLAP_EXT_POSIX_FD_SUPPORT); + getExtension(_hostFileReference, CLAP_EXT_FILE_REFERENCE); + getExtension(_hostLatency, CLAP_EXT_LATENCY); + getExtension(_hostGui, CLAP_EXT_GUI); + getExtension(_hostParams, CLAP_EXT_PARAMS); + getExtension(_hostTrackInfo, CLAP_EXT_TRACK_INFO); + getExtension(_hostState, CLAP_EXT_STATE); + getExtension(_hostNoteName, CLAP_EXT_NOTE_NAME); + getExtension(_hostQuickControls, CLAP_EXT_QUICK_CONTROLS); + getExtension(_hostVoiceInfo, CLAP_EXT_VOICE_INFO); + } + + template + template + void HostProxy::getExtension(const T *&ptr, const char *id) noexcept { + assert(!ptr); + assert(id); + + if (_host->get_extension) + ptr = static_cast(_host->get_extension(_host, id)); + } + + /////////////// + // clap_host // + /////////////// + template + void HostProxy::requestCallback() noexcept { + _host->request_callback(_host); + } + + template + void HostProxy::requestRestart() noexcept { + _host->request_restart(_host); + } + + template + void HostProxy::requestProcess() noexcept { + _host->request_process(_host); + } + + ///////////// + // Logging // + ///////////// + template + bool HostProxy::canUseHostLog() const noexcept { + return _hostLog && _hostLog->log; + } + + template + void HostProxy::log(clap_log_severity severity, const char *msg) const noexcept { + if (canUseHostLog()) { + _hostLog->log(_host, severity, msg); + return; + } + + switch (severity) { + case CLAP_LOG_ERROR: + case CLAP_LOG_HOST_MISBEHAVING: + std::cerr << msg << std::endl; + break; + + default: + std::clog << msg << std::endl; + break; + } + } + + template + void HostProxy::hostMisbehaving(const char *msg) const noexcept { + log(CLAP_LOG_HOST_MISBEHAVING, msg); + + if (h == MisbehaviourHandler::Terminate) + std::terminate(); + } + + template + void HostProxy::pluginMisbehaving(const char *msg) const noexcept { + log(CLAP_LOG_PLUGIN_MISBEHAVING, msg); + + if (h == MisbehaviourHandler::Terminate) + std::terminate(); + } + + ////////////////// + // Thread Check // + ////////////////// + template + bool HostProxy::canUseThreadCheck() const noexcept { + return _hostThreadCheck && _hostThreadCheck->is_audio_thread && + _hostThreadCheck->is_main_thread; + } + + template + bool HostProxy::isMainThread() const noexcept { + assert(canUseThreadCheck()); + return _hostThreadCheck->is_main_thread(_host); + } + + template + bool HostProxy::isAudioThread() const noexcept { + assert(canUseThreadCheck()); + return _hostThreadCheck->is_audio_thread(_host); + } + + template + void HostProxy::ensureMainThread(const char *method) const noexcept { + if (l == CheckingLevel::None) + return; + + if (!canUseThreadCheck() || isMainThread()) + return; + + std::ostringstream msg; + msg << "Plugin called the method clap_host_" << method + << "() on wrong thread! It must be called on main thread!"; + pluginMisbehaving(msg.str()); + } + + template + void HostProxy::ensureAudioThread(const char *method) const noexcept { + if (l == CheckingLevel::None) + return; + + if (!canUseThreadCheck() || isAudioThread()) + return; + + std::ostringstream msg; + msg << "Plugin called the method clap_host_" << method + << "() on wrong thread! It must be called on audio thread!"; + pluginMisbehaving(msg.str()); + } + + ////////////////////////////////// + // clap_host_audio_ports_config // + ////////////////////////////////// + template + bool HostProxy::canUseAudioPortsConfig() const noexcept { + if (!_hostAudioPortsConfig) + return false; + + if (_hostAudioPortsConfig->rescan) + return true; + + hostMisbehaving("clap_host_audio_ports_config is partially implemented"); + return false; + } + + template + void HostProxy::audioPortsConfigRescan() const noexcept { + assert(canUseAudioPortsConfig()); + ensureMainThread("audio_ports_config.rescan"); + _hostAudioPortsConfig->rescan(_host); + } + + /////////////////////////// + // clap_host_audio_ports // + /////////////////////////// + template + bool HostProxy::canUseAudioPorts() const noexcept { + if (!_hostAudioPorts) + return false; + + if (_hostAudioPorts->rescan) + return true; + + hostMisbehaving("clap_host_audio_ports is partially implemented"); + return false; + } + + template + void HostProxy::audioPortsRescan(uint32_t flags) const noexcept { + assert(canUseAudioPorts()); + ensureMainThread("audio_ports.rescan"); + _hostAudioPorts->rescan(_host, flags); + } + + ////////////////////////// + // clap_host_note_ports // + ////////////////////////// + template + bool HostProxy::canUseNotePorts() const noexcept { + if (!_hostNotePorts) + return false; + + if (_hostNotePorts->rescan) + return true; + + hostMisbehaving("clap_host_note_ports is partially implemented"); + return false; + } + + template + void HostProxy::notePortsRescan(uint32_t flags) const noexcept { + assert(canUseNotePorts()); + ensureMainThread("note_ports.rescan"); + _hostNotePorts->rescan(_host, flags); + } + + ///////////////////// + // clap_host_state // + ///////////////////// + template + bool HostProxy::canUseState() const noexcept { + if (!_hostState) + return false; + + if (_hostState->mark_dirty) + return true; + + hostMisbehaving("clap_host_state is partially implemented"); + return false; + } + + template + void HostProxy::stateMarkDirty() const noexcept { + assert(canUseState()); + ensureMainThread("state.mark_dirty"); + _hostState->mark_dirty(_host); + } + + /////////////////////// + // clap_host_latency // + /////////////////////// + template + bool HostProxy::canUseLatency() const noexcept { + if (!_hostLatency) + return false; + + if (_hostLatency->changed) + return true; + + hostMisbehaving("clap_host_latency is partially implemented"); + return false; + } + + template + void HostProxy::latencyChanged() const noexcept { + assert(canUseLatency()); + ensureMainThread("latency.changed"); + _hostLatency->changed(_host); + } + + //////////////////// + // clap_host_tail // + //////////////////// + template + bool HostProxy::canUseTail() const noexcept { + if (!_hostTail) + return false; + + if (_hostLatency->changed) + return true; + + hostMisbehaving("clap_host_tail is partially implemented"); + return false; + } + + template + void HostProxy::tailChanged() const noexcept { + assert(canUseTail()); + ensureAudioThread("tail.changed"); + _hostTail->changed(_host); + } + + ///////////////////////// + // clap_host_note_name // + ///////////////////////// + template + bool HostProxy::canUseNoteName() const noexcept { + if (!_hostNoteName) + return false; + + if (_hostNoteName->changed) + return true; + + hostMisbehaving("clap_host_note_name is partially implemented"); + return false; + } + + template + void HostProxy::noteNameChanged() const noexcept { + assert(canUseNoteName()); + ensureMainThread("note_name.changed"); + _hostNoteName->changed(_host); + } + + ////////////////////// + // clap_host_params // + ////////////////////// + template + bool HostProxy::canUseParams() const noexcept { + if (!_hostParams) + return false; + + if (_hostParams->rescan && _hostParams->clear && _hostParams->request_flush) + return true; + + hostMisbehaving("clap_host_params is partially implemented"); + return false; + } + + template + void HostProxy::paramsRescan(clap_param_rescan_flags flags) const noexcept { + assert(canUseParams()); + ensureMainThread("params.rescan"); + _hostParams->rescan(_host, flags); + } + + template + void HostProxy::paramsClear(clap_id param_id, + clap_param_clear_flags flags) const noexcept { + assert(canUseParams()); + ensureMainThread("params.clear"); + _hostParams->clear(_host, param_id, flags); + } + + template + void HostProxy::paramsRequestFlush() const noexcept { + assert(canUseParams()); + _hostParams->request_flush(_host); + } + + ////////////////////////// + // clap_host_track_info // + ////////////////////////// + template + bool HostProxy::canUseTrackInfo() const noexcept { + if (!_hostTrackInfo) + return false; + + if (_hostTrackInfo->get) + return true; + + hostMisbehaving("clap_host_track_info is partially implemented"); + return false; + } + + template + bool HostProxy::trackInfoGet(clap_track_info *info) const noexcept { + assert(canUseTrackInfo()); + ensureMainThread("track_info.get"); + return _hostTrackInfo->get(_host, info); + } + + /////////////////// + // clap_host_gui // + /////////////////// + template + bool HostProxy::canUseGui() const noexcept { + if (!_hostGui) + return false; + + if (_hostGui->resize_hints_changed && _hostGui->request_resize && _hostGui->request_hide && + _hostGui->request_show && _hostGui->closed) + return true; + + hostMisbehaving("clap_host_gui is partially implemented"); + return false; + } + + template + bool HostProxy::guiRequestResize(uint32_t width, uint32_t height) const noexcept { + assert(canUseGui()); + return _hostGui->request_resize(_host, width, height); + } + + template + bool HostProxy::guiRequestShow() const noexcept { + assert(canUseGui()); + return _hostGui->request_show(_host); + } + + template + bool HostProxy::guiRequestHide() const noexcept { + assert(canUseGui()); + return _hostGui->request_hide(_host); + } + + template + void HostProxy::guiClosed(bool wasDestroyed) const noexcept { + assert(canUseGui()); + _hostGui->closed(_host, wasDestroyed); + } + + /////////////////// + // Timer Support // + /////////////////// + template + bool HostProxy::canUseTimerSupport() const noexcept { + if (!_hostTimerSupport) + return false; + + auto &x = *_hostTimerSupport; + if (x.register_timer && x.unregister_timer) + return true; + + hostMisbehaving("clap_timer_support is partially implemented"); + return false; + } + + template + bool HostProxy::timerSupportRegister(uint32_t period_ms, + clap_id *timer_id) const noexcept { + assert(canUseTimerSupport()); + ensureMainThread("timer_support.register_timer"); + return _hostTimerSupport->register_timer(_host, period_ms, timer_id); + } + + template + bool HostProxy::timerSupportUnregister(clap_id timer_id) const noexcept { + assert(canUseTimerSupport()); + ensureMainThread("timer_support.unregister_timer"); + return _hostTimerSupport->unregister_timer(_host, timer_id); + } + + ////////////////////////// + // clap_host_fd_support // + ////////////////////////// + template + bool HostProxy::canUsePosixFdSupport() const noexcept { + if (!_hostPosixFdSupport) + return false; + + auto &x = *_hostPosixFdSupport; + if (x.modify_fd && x.register_fd && x.unregister_fd) + return true; + + hostMisbehaving("clap_posix_fd_support is partially implemented"); + return false; + } + + template + bool HostProxy::posixFdSupportRegister(int fd, int flags) const noexcept { + assert(canUsePosixFdSupport()); + ensureMainThread("posix_fd_support.register"); + return _hostPosixFdSupport->register_fd(_host, fd, flags); + } + + template + bool HostProxy::posixFdSupportModify(int fd, int flags) const noexcept { + assert(canUsePosixFdSupport()); + ensureMainThread("posix_fd_support.modify"); + return _hostPosixFdSupport->modify_fd(_host, fd, flags); + } + + template + bool HostProxy::posixFdSupportUnregister(int fd) const noexcept { + assert(canUsePosixFdSupport()); + ensureMainThread("posix_fd_support.unregister"); + return _hostPosixFdSupport->unregister_fd(_host, fd); + } + + /////////////////////////// + // clap_host_thread_pool // + /////////////////////////// + template + bool HostProxy::canUseThreadPool() const noexcept { + if (!_hostThreadPool) + return false; + + if (_hostThreadPool->request_exec) + return true; + + hostMisbehaving("clap_host_thread_pool is partially implemented"); + return false; + } + + template + bool HostProxy::threadPoolRequestExec(uint32_t numTasks) const noexcept { + assert(canUseThreadPool()); + ensureAudioThread("thread_pool.request_exec"); + return _hostThreadPool->request_exec(_host, numTasks); + } + + ////////////////////////// + // clap_host_voice_info // + ////////////////////////// + template + bool HostProxy::canUseVoiceInfo() const noexcept { + if (!_hostVoiceInfo) + return false; + + if (_hostVoiceInfo->changed) + return true; + + hostMisbehaving("clap_host_voice_info is partially implemented"); + return false; + } + + template + void HostProxy::voiceInfoChanged() const noexcept { + assert(canUseVoiceInfo()); + ensureMainThread("voice_info.changed"); + return _hostVoiceInfo->changed(_host); + } + + ////////////////////////////// + // clap_host_quick_controls // + ////////////////////////////// + template + bool HostProxy::canUseQuickControls() const noexcept { + if (!_hostQuickControls) + return false; + + if (_hostQuickControls->changed && _hostQuickControls->suggest_page) + return true; + + hostMisbehaving("clap_host_quick_controls is partially implemented"); + return false; + } + + template + void HostProxy::quickControlsChanged() const noexcept { + assert(canUseQuickControls()); + ensureMainThread("quick_controls.changed"); + _hostQuickControls->changed(_host); + } + + template + void HostProxy::quickControlsSuggestPage(clap_id page_id) const noexcept { + assert(canUseQuickControls()); + ensureMainThread("quick_controls.suggest_page"); + _hostQuickControls->suggest_page(_host, page_id); + } +}} // namespace clap::helpers \ No newline at end of file diff --git a/deps/clap-juce-extensions/clap-libs/clap-helpers/include/clap/helpers/misbehaviour-handler.hh b/deps/clap-juce-extensions/clap-libs/clap-helpers/include/clap/helpers/misbehaviour-handler.hh new file mode 100644 index 0000000..754a46e --- /dev/null +++ b/deps/clap-juce-extensions/clap-libs/clap-helpers/include/clap/helpers/misbehaviour-handler.hh @@ -0,0 +1,8 @@ +#pragma once + +namespace clap { namespace helpers { + enum class MisbehaviourHandler { + Ignore, + Terminate, + }; +}} // namespace clap::helpers \ No newline at end of file diff --git a/deps/clap-juce-extensions/clap-libs/clap-helpers/include/clap/helpers/note-end-queue.hh b/deps/clap-juce-extensions/clap-libs/clap-helpers/include/clap/helpers/note-end-queue.hh new file mode 100644 index 0000000..f6bc2f3 --- /dev/null +++ b/deps/clap-juce-extensions/clap-libs/clap-helpers/include/clap/helpers/note-end-queue.hh @@ -0,0 +1,75 @@ +#include +#include +#include +#include + +#include + +namespace clap { namespace helpers { + class NoteEndQueue { + public: + explicit NoteEndQueue(std::size_t initialBucketSize = 19) + : _voicesToKill(initialBucketSize) {} + + void onNoteOn(int32_t noteId, int16_t port, int16_t channel, int16_t key) { + assert(checkValidEntry(noteId, port, channel, key)); + _voicesToKill.erase(Entry(noteId, port, channel, key)); + } + + void onNoteEnd(int32_t noteId, int16_t port, int16_t channel, int16_t key) { + assert(checkValidEntry(noteId, port, channel, key)); + _voicesToKill.insert(Entry(noteId, port, channel, key)); + } + + void flush(const clap_output_events *out) { + clap_event_note ev; + ev.velocity = 0; + ev.header.flags = 0; + ev.header.size = sizeof(ev); + ev.header.space_id = CLAP_CORE_EVENT_SPACE_ID; + ev.header.time = 0; // time is irrelevant here + ev.header.type = CLAP_EVENT_NOTE_END; + + for (auto &e : _voicesToKill) { + ev.port_index = e._port; + ev.channel = e._channel; + ev.key = e._key; + ev.note_id = e._noteId; + out->try_push(out, &ev.header); + } + + _voicesToKill.clear(); + } + + private: + bool checkValidEntry(int32_t noteId, int16_t port, int16_t channel, int16_t key) { + assert(noteId >= -1); + assert(port >= 0); + assert(channel >= 0 && channel < 16); + assert(key >= 0 && key < 127); + return true; + } + + struct Entry { + constexpr Entry(int32_t noteId, int16_t port, int16_t channel, int16_t key) + : _noteId(noteId), _port(port), _channel(channel), _key(key) {} + + constexpr bool operator==(const Entry &o) const noexcept { + return _port == o._port && _channel == o._channel && _key == o._key; + } + + const int32_t _noteId; + const int16_t _port; + const int16_t _channel; + const int16_t _key; + }; + + struct EntryHash { + std::size_t operator()(const Entry &e) const noexcept { + return e._key ^ (e._channel << 8) ^ (e._port << 16) ^ (int64_t(e._noteId) << 32LL); + } + }; + + std::unordered_set _voicesToKill; + }; +}} // namespace clap::helpers diff --git a/deps/clap-juce-extensions/clap-libs/clap-helpers/include/clap/helpers/param-queue.hh b/deps/clap-juce-extensions/clap-libs/clap-helpers/include/clap/helpers/param-queue.hh new file mode 100644 index 0000000..d2fb168 --- /dev/null +++ b/deps/clap-juce-extensions/clap-libs/clap-helpers/include/clap/helpers/param-queue.hh @@ -0,0 +1,71 @@ +#pragma once + +#include +#include +#include +#include + +namespace clap { namespace helpers { + template + class ParamQueue { + public: + using value_type = T; + + ParamQueue() = default; + + void push(const T &value) { + while (!tryPush(value)) + ; + } + + bool tryPush(const T &value) { + int w = _writeOffset; // write element + int wn = (w + 1) % CAPACITY; // next write element + + if (wn == _readOffset) + return false; + + _data[w] = value; + _writeOffset = wn; + return true; + } + + bool tryPeek(T &value) { + int r = _readOffset; + if (r == _writeOffset) + return false; // empty + + value = _data[r]; + return true; + } + + void consume() { + int r = _readOffset; + if (r == _writeOffset) { + assert(false && "consume should not have been called"); + return; // empty + } + + int rn = (r + 1) % CAPACITY; + _readOffset = rn; + } + + bool tryPop(T &value) { + if (!tryPeek(value)) + return false; + + consume(); + return true; + } + + void reset() { + _writeOffset = 0; + _readOffset = 0; + } + + private: + std::array _data; + std::atomic _writeOffset = {0}; + std::atomic _readOffset = {0}; + }; +}} // namespace clap::helpers \ No newline at end of file diff --git a/deps/clap-juce-extensions/clap-libs/clap-helpers/include/clap/helpers/plugin-proxy.hh b/deps/clap-juce-extensions/clap-libs/clap-helpers/include/clap/helpers/plugin-proxy.hh new file mode 100644 index 0000000..dfa4f9d --- /dev/null +++ b/deps/clap-juce-extensions/clap-libs/clap-helpers/include/clap/helpers/plugin-proxy.hh @@ -0,0 +1,37 @@ +#pragma once + +#include "host-proxy.hh" + +namespace clap { namespace helpers { + template + class PluginProxy { + public: + PluginProxy(const clap_host *host, const clap_plugin *plugin); + + void init(); + + template + void getExtension(const T *&ptr, const char *id) noexcept; + + protected: + void ensureMainThread(const char *method) const noexcept; + void ensureAudioThread(const char *method) const noexcept; + + HostProxy _host; + const clap_plugin *const _plugin; + + const clap_plugin_params *_pluginParams = nullptr; + const clap_plugin_quick_controls *_pluginQuickControls = nullptr; + const clap_plugin_audio_ports *_pluginAudioPorts = nullptr; + const clap_plugin_gui *_pluginGui = nullptr; + const clap_plugin_gui_x11 *_pluginGuiX11 = nullptr; + const clap_plugin_gui_win32 *_pluginGuiWin32 = nullptr; + const clap_plugin_gui_cocoa *_pluginGuiCocoa = nullptr; + const clap_plugin_gui_free_standing *_pluginGuiFreeStanding = nullptr; + const clap_plugin_timer_support *_pluginTimerSupport = nullptr; + const clap_plugin_fd_support *_pluginFdSupport = nullptr; + const clap_plugin_thread_pool *_pluginThreadPool = nullptr; + const clap_plugin_preset_load *_pluginPresetLoad = nullptr; + const clap_plugin_state *_pluginState = nullptr; + }; +}} // namespace clap::helpers \ No newline at end of file diff --git a/deps/clap-juce-extensions/clap-libs/clap-helpers/include/clap/helpers/plugin.hh b/deps/clap-juce-extensions/clap-libs/clap-helpers/include/clap/helpers/plugin.hh new file mode 100644 index 0000000..0498c61 --- /dev/null +++ b/deps/clap-juce-extensions/clap-libs/clap-helpers/include/clap/helpers/plugin.hh @@ -0,0 +1,434 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "checking-level.hh" +#include "host-proxy.hh" +#include "misbehaviour-handler.hh" + +namespace clap { namespace helpers { + + /// @brief C++ glue and checks + /// + /// @note for an higher level implementation, see @ref PluginHelper + template + class Plugin { + public: + const clap_plugin *clapPlugin() noexcept { return &_plugin; } + + protected: + Plugin(const clap_plugin_descriptor *desc, const clap_host *host); + virtual ~Plugin() = default; + + // not copyable, not moveable + Plugin(const Plugin &) = delete; + Plugin(Plugin &&) = delete; + Plugin &operator=(const Plugin &) = delete; + Plugin &operator=(Plugin &&) = delete; + + ///////////////////////// + // Methods to override // + ///////////////////////// + + //-------------// + // clap_plugin // + //-------------// + virtual bool init() noexcept { return true; } + virtual bool + activate(double sampleRate, uint32_t minFrameCount, uint32_t maxFrameCount) noexcept { + return true; + } + virtual void deactivate() noexcept {} + virtual bool startProcessing() noexcept { return true; } + virtual void stopProcessing() noexcept {} + virtual clap_process_status process(const clap_process *process) noexcept { + return CLAP_PROCESS_SLEEP; + } + virtual void reset() noexcept {} + virtual void onMainThread() noexcept {} + virtual const void *extension(const char *id) noexcept { return nullptr; } + + //---------------------// + // clap_plugin_latency // + //---------------------// + virtual bool implementsLatency() const noexcept { return false; } + virtual uint32_t latencyGet() const noexcept { return 0; } + + //------------------// + // clap_plugin_tail // + //------------------// + virtual bool implementsTail() const noexcept { return false; } + virtual uint32_t tailGet(const clap_plugin_t *plugin) const noexcept { return 0; } + + //--------------------// + // clap_plugin_render // + //--------------------// + virtual bool implementsRender() const noexcept { return false; } + virtual bool renderHasHardRealtimeRequirement() noexcept { return false; } + virtual bool renderSetMode(clap_plugin_render_mode mode) noexcept { return false; } + + //-------------------------// + // clap_plugin_thread_pool // + //-------------------------// + virtual bool implementsThreadPool() const noexcept { return false; } + virtual void threadPoolExec(uint32_t taskIndex) noexcept {} + + //-------------------// + // clap_plugin_state // + //-------------------// + virtual bool implementsState() const noexcept { return false; } + virtual bool stateSave(const clap_ostream *stream) noexcept { return false; } + virtual bool stateLoad(const clap_istream *stream) noexcept { return false; } + + //-------------------------// + // clap_plugin_preset_load // + //-------------------------// + virtual bool implementsPresetLoad() const noexcept { return false; } + virtual bool presetLoadFromFile(const char *path) noexcept { return false; } + + //------------------------// + // clap_plugin_track_info // + //------------------------// + virtual bool implementsTrackInfo() const noexcept { return false; } + virtual void trackInfoChanged() noexcept {} + + //-------------------------// + // clap_plugin_audio_ports // + //-------------------------// + virtual bool implementsAudioPorts() const noexcept { return false; } + virtual uint32_t audioPortsCount(bool isInput) const noexcept { return 0; } + virtual bool + audioPortsInfo(uint32_t index, bool isInput, clap_audio_port_info *info) const noexcept { + return false; + } + virtual uint32_t audioPortsConfigCount() const noexcept { return 0; } + virtual bool audioPortsGetConfig(uint32_t index, + clap_audio_ports_config *config) const noexcept { + return false; + } + virtual bool audioPortsSetConfig(clap_id configId) noexcept { return false; } + + //--------------------// + // clap_plugin_params // + //--------------------// + virtual bool implementsParams() const noexcept { return false; } + virtual uint32_t paramsCount() const noexcept { return 0; } + virtual bool paramsInfo(uint32_t paramIndex, clap_param_info *info) const noexcept { + return false; + } + virtual bool paramsValue(clap_id paramId, double *value) noexcept { return false; } + virtual bool + paramsValueToText(clap_id paramId, double value, char *display, uint32_t size) noexcept { + return false; + } + virtual bool paramsTextToValue(clap_id paramId, const char *display, double *value) noexcept { + return false; + } + virtual void paramsFlush(const clap_input_events *in, + const clap_output_events *out) noexcept {} + virtual bool isValidParamId(clap_id paramId) const noexcept; + + //----------------------------// + // clap_plugin_quick_controls // + //----------------------------// + virtual bool implementQuickControls() const noexcept { return false; } + virtual uint32_t quickControlsPageCount() noexcept { return 0; } + virtual bool quickControlsPageGet(uint32_t pageIndex, + clap_quick_controls_page *page) noexcept { + return false; + } + virtual void quickControlsSelectPage(clap_id pageId) noexcept {} + virtual clap_id quickControlsSelectedPage() noexcept { return CLAP_INVALID_ID; } + + //------------------------// + // clap_plugin_note_ports // + //------------------------// + virtual bool implementsNotePorts() const noexcept { return false; } + virtual uint32_t notePortsCount(bool isInput) const noexcept { return 0; } + virtual bool + notePortsInfo(uint32_t index, bool isInput, clap_note_port_info *info) const noexcept { + return false; + } + + //-----------------------// + // clap_plugin_note_name // + //-----------------------// + virtual bool implementsNoteName() const noexcept { return false; } + virtual int noteNameCount() noexcept { return 0; } + virtual bool noteNameGet(int index, clap_note_name *noteName) noexcept { return false; } + + //---------------------------// + // clap_plugin_timer_support // + //---------------------------// + virtual bool implementsTimerSupport() const noexcept { return false; } + virtual void onTimer(clap_id timerId) noexcept {} + + //------------------------------// + // clap_plugin_posix_fd_support // + //------------------------------// + virtual bool implementsPosixFdSupport() const noexcept { return false; } + virtual void onPosixFd(int fd, int flags) noexcept {} + + //-----------------// + // clap_plugin_gui // + //-----------------// + virtual bool implementsGui() const noexcept { return false; } + virtual bool guiIsApiSupported(const char *api, bool isFloating) noexcept { return false; } + virtual bool guiGetPreferredApi(const char **api, bool *is_floating) noexcept { + return false; + } + virtual bool guiCreate(const char *api, bool isFloating) noexcept { return false; } + virtual void guiDestroy() noexcept {} + virtual bool guiSetScale(double scale) noexcept { return false; } + virtual bool guiShow() noexcept { return false; } + virtual bool guiHide() noexcept { return false; } + virtual bool guiGetSize(uint32_t *width, uint32_t *height) noexcept { return false; } + virtual bool guiCanResize() const noexcept { return false; } + virtual bool guiGetResizeHints(clap_gui_resize_hints_t *hints) noexcept { return false; } + virtual bool guiAdjustSize(uint32_t *width, uint32_t *height) noexcept { + return guiGetSize(width, height); + } + virtual bool guiSetSize(uint32_t width, uint32_t height) noexcept { return false; } + virtual void guiSuggestTitle(const char *title) noexcept {} + virtual bool guiSetParent(const clap_window *window) noexcept { return false; } + virtual bool guiSetTransient(const clap_window *window) noexcept { return false; } + + //------------------------// + // clap_plugin_voice_info // + //------------------------// + virtual bool implementsVoiceInfo() const noexcept { return false; } + virtual bool voiceInfoGet(clap_voice_info *info) noexcept { return false; } + + ///////////// + // Logging // + ///////////// + void log(clap_log_severity severity, const char *msg) const noexcept; + void hostMisbehaving(const char *msg) const noexcept; + void hostMisbehaving(const std::string &msg) const noexcept { hostMisbehaving(msg.c_str()); } + + // Receives a copy of all the logging messages sent to the host. + // This is useful to have the messages in both the host's logs and the plugin's logs. + virtual void logTee(clap_log_severity severity, const char *msg) const noexcept {} + + ///////////////////// + // Thread Checking // + ///////////////////// + void checkMainThread() const noexcept; + void checkAudioThread() const noexcept; + void checkParamThread() const noexcept; + void ensureMainThread(const char *method) const noexcept; + void ensureAudioThread(const char *method) const noexcept; + void ensureParamThread(const char *method) const noexcept; + + /////////////// + // Utilities // + /////////////// + static Plugin &from(const clap_plugin *plugin, bool requireInitialized = true) noexcept; + + // runs the callback immediately if on the main thread, otherwise queue it. + // be aware that the callback may be ran during the plugin destruction phase, + // so check isBeingDestroyed() and ajust your code. + void runOnMainThread(std::function callback); + + // This actually runs callbacks on the main thread, you should not need to call it + void runCallbacksOnMainThread(); + + template + void initInterface(const T *&ptr, const char *id) noexcept; + void initInterfaces() noexcept; + + static uint32_t compareAudioPortsInfo(const clap_audio_port_info &a, + const clap_audio_port_info &b) noexcept; + + ////////////////////// + // Processing State // + ////////////////////// + bool isActive() const noexcept { return _isActive; } + bool isProcessing() const noexcept { return _isProcessing; } + int sampleRate() const noexcept { + assert(_isActive && "sample rate is only known if the plugin is active"); + assert(_sampleRate > 0); + return _sampleRate; + } + + bool isBeingDestroyed() const noexcept { return _isBeingDestroyed; } + + protected: + HostProxy _host; + + private: + ///////////////////// + // CLAP Interfaces // + ///////////////////// + + clap_plugin _plugin; + // clap_plugin + static bool clapInit(const clap_plugin *plugin) noexcept; + static void clapDestroy(const clap_plugin *plugin) noexcept; + static bool clapActivate(const clap_plugin *plugin, + double sample_rate, + uint32_t minFrameCount, + uint32_t maxFrameCount) noexcept; + static void clapDeactivate(const clap_plugin *plugin) noexcept; + static bool clapStartProcessing(const clap_plugin *plugin) noexcept; + static void clapStopProcessing(const clap_plugin *plugin) noexcept; + static void clapReset(const clap_plugin *plugin) noexcept; + static clap_process_status clapProcess(const clap_plugin *plugin, + const clap_process *process) noexcept; + static void clapOnMainThread(const clap_plugin *plugin) noexcept; + static const void *clapExtension(const clap_plugin *plugin, const char *id) noexcept; + + // latency + static uint32_t clapLatencyGet(const clap_plugin *plugin) noexcept; + + // clap_plugin_tail + static uint32_t clapTailGet(const clap_plugin_t *plugin) noexcept; + + // clap_plugin_render + static bool clapRenderHasHardRealtimeRequirement(const clap_plugin_t *plugin) noexcept; + static bool clapRenderSetMode(const clap_plugin *plugin, + clap_plugin_render_mode mode) noexcept; + + // clap_plugin_thread_pool + static void clapThreadPoolExec(const clap_plugin *plugin, uint32_t task_index) noexcept; + + // clap_plugin_state + static bool clapStateSave(const clap_plugin *plugin, const clap_ostream *stream) noexcept; + static bool clapStateLoad(const clap_plugin *plugin, const clap_istream *stream) noexcept; + + // clap_plugin_preset + static bool clapPresetLoadFromFile(const clap_plugin *plugin, const char *path) noexcept; + + // clap_plugin_track_info + static void clapTrackInfoChanged(const clap_plugin *plugin) noexcept; + + // clap_plugin_audio_ports + static uint32_t clapAudioPortsCount(const clap_plugin *plugin, bool is_input) noexcept; + static bool clapAudioPortsInfo(const clap_plugin *plugin, + uint32_t index, + bool is_input, + clap_audio_port_info *info) noexcept; + static uint32_t clapAudioPortsConfigCount(const clap_plugin *plugin) noexcept; + static bool clapAudioPortsGetConfig(const clap_plugin *plugin, + uint32_t index, + clap_audio_ports_config *config) noexcept; + static bool clapAudioPortsSetConfig(const clap_plugin *plugin, clap_id config_id) noexcept; + + // clap_plugin_params + static uint32_t clapParamsCount(const clap_plugin *plugin) noexcept; + static bool clapParamsInfo(const clap_plugin *plugin, + uint32_t param_index, + clap_param_info *param_info) noexcept; + static bool + clapParamsValue(const clap_plugin *plugin, clap_id param_id, double *value) noexcept; + static bool clapParamsValueToText(const clap_plugin *plugin, + clap_id param_id, + double value, + char *display, + uint32_t size) noexcept; + static bool clapParamsTextToValue(const clap_plugin *plugin, + clap_id param_id, + const char *display, + double *value) noexcept; + static void clapParamsFlush(const clap_plugin *plugin, + const clap_input_events *in, + const clap_output_events *out) noexcept; + + // clap_plugin_quick_controls + static uint32_t clapQuickControlsPageCount(const clap_plugin *plugin) noexcept; + static bool clapQuickControlsPageGet(const clap_plugin *plugin, + uint32_t page_index, + clap_quick_controls_page *page) noexcept; + + // clap_plugin_note_port + static uint32_t clapNotePortsCount(const clap_plugin *plugin, bool is_input) noexcept; + static bool clapNotePortsInfo(const clap_plugin *plugin, + uint32_t index, + bool is_input, + clap_note_port_info *info) noexcept; + + // clap_plugin_note_name + static uint32_t clapNoteNameCount(const clap_plugin *plugin) noexcept; + static bool clapNoteNameGet(const clap_plugin *plugin, + uint32_t index, + clap_note_name *note_name) noexcept; + + // clap_plugin_timer_support + static void clapOnTimer(const clap_plugin *plugin, clap_id timer_id) noexcept; + + // clap_plugin_fd_support + static void + clapOnPosixFd(const clap_plugin *plugin, int fd, clap_posix_fd_flags_t flags) noexcept; + + // clap_plugin_gui + static bool + clapGuiIsApiSupported(const clap_plugin *plugin, const char *api, bool isFloating) noexcept; + static bool clapGuiGetPreferredApi(const clap_plugin_t *plugin, + const char **api, + bool *is_floating) noexcept; + static bool + clapGuiCreate(const clap_plugin *plugin, const char *api, bool isFloating) noexcept; + static void clapGuiDestroy(const clap_plugin *plugin) noexcept; + static bool clapGuiSetScale(const clap_plugin *plugin, double scale) noexcept; + static bool + clapGuiGetSize(const clap_plugin *plugin, uint32_t *width, uint32_t *height) noexcept; + static bool + clapGuiSetSize(const clap_plugin *plugin, uint32_t width, uint32_t height) noexcept; + static bool clapGuiCanResize(const clap_plugin *plugin) noexcept; + static bool clapGuiGetResizeHints(const clap_plugin_t *plugin, + clap_gui_resize_hints_t *hints) noexcept; + static bool + clapGuiAdjustSize(const clap_plugin *plugin, uint32_t *width, uint32_t *height) noexcept; + static bool clapGuiShow(const clap_plugin *plugin) noexcept; + static bool clapGuiHide(const clap_plugin *plugin) noexcept; + static bool clapGuiSetParent(const clap_plugin *plugin, const clap_window *window) noexcept; + static bool clapGuiSetTransient(const clap_plugin *plugin, + const clap_window *window) noexcept; + + static void clapGuiSuggestTitle(const clap_plugin *plugin, const char *title) noexcept; + + static bool clapVoiceInfoGet(const clap_plugin *plugin, clap_voice_info *info) noexcept; + + // interfaces + static const clap_plugin_render _pluginRender; + static const clap_plugin_thread_pool _pluginThreadPool; + static const clap_plugin_state _pluginState; + static const clap_plugin_preset_load _pluginPresetLoad; + static const clap_plugin_track_info _pluginTrackInfo; + static const clap_plugin_audio_ports _pluginAudioPorts; + static const clap_plugin_audio_ports_config _pluginAudioPortsConfig; + static const clap_plugin_params _pluginParams; + static const clap_plugin_quick_controls _pluginQuickControls; + static const clap_plugin_latency _pluginLatency; + static const clap_plugin_note_ports _pluginNotePorts; + static const clap_plugin_note_name _pluginNoteName; + static const clap_plugin_timer_support _pluginTimerSupport; + static const clap_plugin_posix_fd_support _pluginPosixFdSupport; + static const clap_plugin_gui _pluginGui; + static const clap_plugin_voice_info _pluginVoiceInfo; + static const clap_plugin_tail _pluginTail; + + // state + bool _wasInitialized = false; + bool _isActive = false; + bool _isProcessing = false; + bool _isBeingDestroyed = false; + double _sampleRate = 0; + + std::string _guiApi; + bool _isGuiCreated = false; + bool _isGuiFloating = false; + bool _isGuiEmbedded = false; + + std::mutex _mainThredCallbacksLock; + std::queue> _mainThredCallbacks; + }; +}} // namespace clap::helpers diff --git a/deps/clap-juce-extensions/clap-libs/clap-helpers/include/clap/helpers/plugin.hxx b/deps/clap-juce-extensions/clap-libs/clap-helpers/include/clap/helpers/plugin.hxx new file mode 100644 index 0000000..9305d8d --- /dev/null +++ b/deps/clap-juce-extensions/clap-libs/clap-helpers/include/clap/helpers/plugin.hxx @@ -0,0 +1,1409 @@ +#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 diff --git a/deps/clap-juce-extensions/clap-libs/clap-helpers/include/clap/helpers/reducing-param-queue.hh b/deps/clap-juce-extensions/clap-libs/clap-helpers/include/clap/helpers/reducing-param-queue.hh new file mode 100644 index 0000000..13c3934 --- /dev/null +++ b/deps/clap-juce-extensions/clap-libs/clap-helpers/include/clap/helpers/reducing-param-queue.hh @@ -0,0 +1,48 @@ +#pragma once + +#include +#include +#include +#include + +#include + +namespace clap { namespace helpers { + +#ifdef CLAP_HAS_CXX20 + template + concept UpdatableValue = requires(T &a, const T &b) { + // update a with b, b being newer than a + { a.update(b) }; + }; +#endif + + // TODO: when switching to C++20 + // template + template + class ReducingParamQueue { + public: + using key_type = K; + using value_type = V; + using queue_type = std::unordered_map; + using consumer_type = const std::function; + + ReducingParamQueue(); + + void setCapacity(size_t capacity); + + void set(const key_type &key, const value_type &value); + void setOrUpdate(const key_type &key, const value_type &value); + void producerDone(); + + void consume(const consumer_type &consumer); + + void reset(); + + private: + queue_type _queues[2]; + std::atomic _free = nullptr; + std::atomic _producer = nullptr; + std::atomic _consumer = nullptr; + }; +}} // namespace clap::helpers \ No newline at end of file diff --git a/deps/clap-juce-extensions/clap-libs/clap-helpers/include/clap/helpers/reducing-param-queue.hxx b/deps/clap-juce-extensions/clap-libs/clap-helpers/include/clap/helpers/reducing-param-queue.hxx new file mode 100644 index 0000000..99e5936 --- /dev/null +++ b/deps/clap-juce-extensions/clap-libs/clap-helpers/include/clap/helpers/reducing-param-queue.hxx @@ -0,0 +1,72 @@ +#pragma once + +#include + +#include "reducing-param-queue.hh" + +namespace clap { namespace helpers { + template + ReducingParamQueue::ReducingParamQueue() { + reset(); + } + + template + void ReducingParamQueue::reset() { + for (auto &q : _queues) + q.clear(); + + _free = &_queues[0]; + _producer = &_queues[1]; + _consumer = nullptr; + } + + template + void ReducingParamQueue::setCapacity(size_t capacity) { + for (auto &q : _queues) + q.reserve(2 * capacity); + } + + template + void ReducingParamQueue::set(const key_type &key, const value_type &value) { + _producer.load()->insert_or_assign(key, value); + } + + template + void ReducingParamQueue::setOrUpdate(const key_type &key, const value_type &value) { + auto prod = _producer.load(); + auto res = prod->insert({key, value}); + if (!res.second && res.first != prod->end()) + res.first->second.update(value); + } + + template + void ReducingParamQueue::producerDone() { + if (_consumer) + return; + + auto tmp = _producer.load(); + _producer = _free.load(); + _free = nullptr; + _consumer = tmp; + + assert(_producer); + } + + template + void ReducingParamQueue::consume(const consumer_type &consumer) { + assert(consumer); + + if (!_consumer) + return; + + for (auto &x : *_consumer) + consumer(x.first, x.second); + + _consumer.load()->clear(); + if (_free) + return; + + _free = _consumer.load(); + _consumer = nullptr; + } +}} // namespace clap::helpers \ No newline at end of file diff --git a/deps/clap-juce-extensions/clap-libs/clap-helpers/tests/main.cc b/deps/clap-juce-extensions/clap-libs/clap-helpers/tests/main.cc new file mode 100644 index 0000000..2380d6b --- /dev/null +++ b/deps/clap-juce-extensions/clap-libs/clap-helpers/tests/main.cc @@ -0,0 +1,2 @@ +#define CATCH_CONFIG_MAIN +#include \ No newline at end of file diff --git a/deps/clap-juce-extensions/clap-libs/clap-helpers/tests/plugin.cc b/deps/clap-juce-extensions/clap-libs/clap-helpers/tests/plugin.cc new file mode 100644 index 0000000..02975f1 --- /dev/null +++ b/deps/clap-juce-extensions/clap-libs/clap-helpers/tests/plugin.cc @@ -0,0 +1,18 @@ +#include +#include +#include +#include +#include + +#include + +CATCH_TEST_CASE("Plugin - Link") { + clap::helpers::Plugin *p0 = nullptr; + clap::helpers::Plugin *p1 = nullptr; + clap::helpers::Plugin *p2 = nullptr; + clap::helpers::Plugin *p3 = nullptr; +} \ No newline at end of file