git subrepo clone (merge) https://github.com/free-audio/clap-helpers.git deps/clap-juce-extensions/clap-libs/clap-helpers

subrepo:
  subdir:   "deps/clap-juce-extensions/clap-libs/clap-helpers"
  merged:   "2bb43c187"
upstream:
  origin:   "https://github.com/free-audio/clap-helpers.git"
  branch:   "main"
  commit:   "2bb43c187"
git-subrepo:
  version:  "0.4.3"
  origin:   "https://github.com/ingydotnet/git-subrepo.git"
  commit:   "2f68596"
This commit is contained in:
essej 2022-06-14 21:35:21 -04:00
parent 8bbddf66e9
commit 7fe70ed89b
20 changed files with 3325 additions and 1 deletions

@ -1 +0,0 @@
Subproject commit 2bb43c18788c689708ead6f127a2d75e772ab389

View File

@ -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
...

View File

@ -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

View File

@ -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 ".")

View File

@ -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.

View File

@ -0,0 +1,9 @@
#pragma once
namespace clap { namespace helpers {
enum class CheckingLevel {
None,
Minimal,
Maximal,
};
}} // namespace clap::helpers

View File

@ -0,0 +1,148 @@
#pragma once
#include <cstring>
#include <cstdint>
#include <vector>
#include <clap/events.h>
#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<clap_event_header *>(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<clap_event_header *>(ptr);
hdr->size = size;
return hdr;
}
template <typename T>
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<clap_event_header *>(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<const EventList *>(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<const EventList *>(list->ctx);
return self->get(index);
}
static bool clapPushBack(const struct clap_output_events *list,
const clap_event_header *event) {
auto *self = static_cast<EventList *>(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<EventList *>(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<uint32_t> _events;
};
}} // namespace clap::helpers

View File

@ -0,0 +1,84 @@
#pragma once
#include <cassert>
#include <cstdlib>
#include <cstring>
#include <memory>
#include <stdexcept>
#include <vector>
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<uint8_t *>(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<uint8_t *>(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<uint8_t *>(_base) + offset;
}
size_t offsetFromBase(const void *ptr) const {
assert(ptr >= _base && "ptr before heap's base");
size_t offset = static_cast<const uint8_t *>(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

View File

@ -0,0 +1,170 @@
#pragma once
#include <string>
#include <clap/clap.h>
#include "checking-level.hh"
#include "misbehaviour-handler.hh"
namespace clap { namespace helpers {
template <MisbehaviourHandler h, CheckingLevel l>
class HostProxy {
public:
HostProxy(const clap_host *host);
void init();
///////////////
// clap_host //
///////////////
template <typename T>
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

View File

@ -0,0 +1,546 @@
#pragma once
#include <cassert>
#include <iostream>
#include <sstream>
#include "host-proxy.hh"
namespace clap { namespace helpers {
template <MisbehaviourHandler h, CheckingLevel l>
HostProxy<h, l>::HostProxy(const ::clap_host *host) : _host(host) {}
template <MisbehaviourHandler h, CheckingLevel l>
void HostProxy<h, l>::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 <MisbehaviourHandler h, CheckingLevel l>
template <typename T>
void HostProxy<h, l>::getExtension(const T *&ptr, const char *id) noexcept {
assert(!ptr);
assert(id);
if (_host->get_extension)
ptr = static_cast<const T *>(_host->get_extension(_host, id));
}
///////////////
// clap_host //
///////////////
template <MisbehaviourHandler h, CheckingLevel l>
void HostProxy<h, l>::requestCallback() noexcept {
_host->request_callback(_host);
}
template <MisbehaviourHandler h, CheckingLevel l>
void HostProxy<h, l>::requestRestart() noexcept {
_host->request_restart(_host);
}
template <MisbehaviourHandler h, CheckingLevel l>
void HostProxy<h, l>::requestProcess() noexcept {
_host->request_process(_host);
}
/////////////
// Logging //
/////////////
template <MisbehaviourHandler h, CheckingLevel l>
bool HostProxy<h, l>::canUseHostLog() const noexcept {
return _hostLog && _hostLog->log;
}
template <MisbehaviourHandler h, CheckingLevel l>
void HostProxy<h, l>::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 <MisbehaviourHandler h, CheckingLevel l>
void HostProxy<h, l>::hostMisbehaving(const char *msg) const noexcept {
log(CLAP_LOG_HOST_MISBEHAVING, msg);
if (h == MisbehaviourHandler::Terminate)
std::terminate();
}
template <MisbehaviourHandler h, CheckingLevel l>
void HostProxy<h, l>::pluginMisbehaving(const char *msg) const noexcept {
log(CLAP_LOG_PLUGIN_MISBEHAVING, msg);
if (h == MisbehaviourHandler::Terminate)
std::terminate();
}
//////////////////
// Thread Check //
//////////////////
template <MisbehaviourHandler h, CheckingLevel l>
bool HostProxy<h, l>::canUseThreadCheck() const noexcept {
return _hostThreadCheck && _hostThreadCheck->is_audio_thread &&
_hostThreadCheck->is_main_thread;
}
template <MisbehaviourHandler h, CheckingLevel l>
bool HostProxy<h, l>::isMainThread() const noexcept {
assert(canUseThreadCheck());
return _hostThreadCheck->is_main_thread(_host);
}
template <MisbehaviourHandler h, CheckingLevel l>
bool HostProxy<h, l>::isAudioThread() const noexcept {
assert(canUseThreadCheck());
return _hostThreadCheck->is_audio_thread(_host);
}
template <MisbehaviourHandler h, CheckingLevel l>
void HostProxy<h, l>::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 <MisbehaviourHandler h, CheckingLevel l>
void HostProxy<h, l>::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 <MisbehaviourHandler h, CheckingLevel l>
bool HostProxy<h, l>::canUseAudioPortsConfig() const noexcept {
if (!_hostAudioPortsConfig)
return false;
if (_hostAudioPortsConfig->rescan)
return true;
hostMisbehaving("clap_host_audio_ports_config is partially implemented");
return false;
}
template <MisbehaviourHandler h, CheckingLevel l>
void HostProxy<h, l>::audioPortsConfigRescan() const noexcept {
assert(canUseAudioPortsConfig());
ensureMainThread("audio_ports_config.rescan");
_hostAudioPortsConfig->rescan(_host);
}
///////////////////////////
// clap_host_audio_ports //
///////////////////////////
template <MisbehaviourHandler h, CheckingLevel l>
bool HostProxy<h, l>::canUseAudioPorts() const noexcept {
if (!_hostAudioPorts)
return false;
if (_hostAudioPorts->rescan)
return true;
hostMisbehaving("clap_host_audio_ports is partially implemented");
return false;
}
template <MisbehaviourHandler h, CheckingLevel l>
void HostProxy<h, l>::audioPortsRescan(uint32_t flags) const noexcept {
assert(canUseAudioPorts());
ensureMainThread("audio_ports.rescan");
_hostAudioPorts->rescan(_host, flags);
}
//////////////////////////
// clap_host_note_ports //
//////////////////////////
template <MisbehaviourHandler h, CheckingLevel l>
bool HostProxy<h, l>::canUseNotePorts() const noexcept {
if (!_hostNotePorts)
return false;
if (_hostNotePorts->rescan)
return true;
hostMisbehaving("clap_host_note_ports is partially implemented");
return false;
}
template <MisbehaviourHandler h, CheckingLevel l>
void HostProxy<h, l>::notePortsRescan(uint32_t flags) const noexcept {
assert(canUseNotePorts());
ensureMainThread("note_ports.rescan");
_hostNotePorts->rescan(_host, flags);
}
/////////////////////
// clap_host_state //
/////////////////////
template <MisbehaviourHandler h, CheckingLevel l>
bool HostProxy<h, l>::canUseState() const noexcept {
if (!_hostState)
return false;
if (_hostState->mark_dirty)
return true;
hostMisbehaving("clap_host_state is partially implemented");
return false;
}
template <MisbehaviourHandler h, CheckingLevel l>
void HostProxy<h, l>::stateMarkDirty() const noexcept {
assert(canUseState());
ensureMainThread("state.mark_dirty");
_hostState->mark_dirty(_host);
}
///////////////////////
// clap_host_latency //
///////////////////////
template <MisbehaviourHandler h, CheckingLevel l>
bool HostProxy<h, l>::canUseLatency() const noexcept {
if (!_hostLatency)
return false;
if (_hostLatency->changed)
return true;
hostMisbehaving("clap_host_latency is partially implemented");
return false;
}
template <MisbehaviourHandler h, CheckingLevel l>
void HostProxy<h, l>::latencyChanged() const noexcept {
assert(canUseLatency());
ensureMainThread("latency.changed");
_hostLatency->changed(_host);
}
////////////////////
// clap_host_tail //
////////////////////
template <MisbehaviourHandler h, CheckingLevel l>
bool HostProxy<h, l>::canUseTail() const noexcept {
if (!_hostTail)
return false;
if (_hostLatency->changed)
return true;
hostMisbehaving("clap_host_tail is partially implemented");
return false;
}
template <MisbehaviourHandler h, CheckingLevel l>
void HostProxy<h, l>::tailChanged() const noexcept {
assert(canUseTail());
ensureAudioThread("tail.changed");
_hostTail->changed(_host);
}
/////////////////////////
// clap_host_note_name //
/////////////////////////
template <MisbehaviourHandler h, CheckingLevel l>
bool HostProxy<h, l>::canUseNoteName() const noexcept {
if (!_hostNoteName)
return false;
if (_hostNoteName->changed)
return true;
hostMisbehaving("clap_host_note_name is partially implemented");
return false;
}
template <MisbehaviourHandler h, CheckingLevel l>
void HostProxy<h, l>::noteNameChanged() const noexcept {
assert(canUseNoteName());
ensureMainThread("note_name.changed");
_hostNoteName->changed(_host);
}
//////////////////////
// clap_host_params //
//////////////////////
template <MisbehaviourHandler h, CheckingLevel l>
bool HostProxy<h, l>::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 <MisbehaviourHandler h, CheckingLevel l>
void HostProxy<h, l>::paramsRescan(clap_param_rescan_flags flags) const noexcept {
assert(canUseParams());
ensureMainThread("params.rescan");
_hostParams->rescan(_host, flags);
}
template <MisbehaviourHandler h, CheckingLevel l>
void HostProxy<h, l>::paramsClear(clap_id param_id,
clap_param_clear_flags flags) const noexcept {
assert(canUseParams());
ensureMainThread("params.clear");
_hostParams->clear(_host, param_id, flags);
}
template <MisbehaviourHandler h, CheckingLevel l>
void HostProxy<h, l>::paramsRequestFlush() const noexcept {
assert(canUseParams());
_hostParams->request_flush(_host);
}
//////////////////////////
// clap_host_track_info //
//////////////////////////
template <MisbehaviourHandler h, CheckingLevel l>
bool HostProxy<h, l>::canUseTrackInfo() const noexcept {
if (!_hostTrackInfo)
return false;
if (_hostTrackInfo->get)
return true;
hostMisbehaving("clap_host_track_info is partially implemented");
return false;
}
template <MisbehaviourHandler h, CheckingLevel l>
bool HostProxy<h, l>::trackInfoGet(clap_track_info *info) const noexcept {
assert(canUseTrackInfo());
ensureMainThread("track_info.get");
return _hostTrackInfo->get(_host, info);
}
///////////////////
// clap_host_gui //
///////////////////
template <MisbehaviourHandler h, CheckingLevel l>
bool HostProxy<h, l>::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 <MisbehaviourHandler h, CheckingLevel l>
bool HostProxy<h, l>::guiRequestResize(uint32_t width, uint32_t height) const noexcept {
assert(canUseGui());
return _hostGui->request_resize(_host, width, height);
}
template <MisbehaviourHandler h, CheckingLevel l>
bool HostProxy<h, l>::guiRequestShow() const noexcept {
assert(canUseGui());
return _hostGui->request_show(_host);
}
template <MisbehaviourHandler h, CheckingLevel l>
bool HostProxy<h, l>::guiRequestHide() const noexcept {
assert(canUseGui());
return _hostGui->request_hide(_host);
}
template <MisbehaviourHandler h, CheckingLevel l>
void HostProxy<h, l>::guiClosed(bool wasDestroyed) const noexcept {
assert(canUseGui());
_hostGui->closed(_host, wasDestroyed);
}
///////////////////
// Timer Support //
///////////////////
template <MisbehaviourHandler h, CheckingLevel l>
bool HostProxy<h, l>::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 <MisbehaviourHandler h, CheckingLevel l>
bool HostProxy<h, l>::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 <MisbehaviourHandler h, CheckingLevel l>
bool HostProxy<h, l>::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 <MisbehaviourHandler h, CheckingLevel l>
bool HostProxy<h, l>::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 <MisbehaviourHandler h, CheckingLevel l>
bool HostProxy<h, l>::posixFdSupportRegister(int fd, int flags) const noexcept {
assert(canUsePosixFdSupport());
ensureMainThread("posix_fd_support.register");
return _hostPosixFdSupport->register_fd(_host, fd, flags);
}
template <MisbehaviourHandler h, CheckingLevel l>
bool HostProxy<h, l>::posixFdSupportModify(int fd, int flags) const noexcept {
assert(canUsePosixFdSupport());
ensureMainThread("posix_fd_support.modify");
return _hostPosixFdSupport->modify_fd(_host, fd, flags);
}
template <MisbehaviourHandler h, CheckingLevel l>
bool HostProxy<h, l>::posixFdSupportUnregister(int fd) const noexcept {
assert(canUsePosixFdSupport());
ensureMainThread("posix_fd_support.unregister");
return _hostPosixFdSupport->unregister_fd(_host, fd);
}
///////////////////////////
// clap_host_thread_pool //
///////////////////////////
template <MisbehaviourHandler h, CheckingLevel l>
bool HostProxy<h, l>::canUseThreadPool() const noexcept {
if (!_hostThreadPool)
return false;
if (_hostThreadPool->request_exec)
return true;
hostMisbehaving("clap_host_thread_pool is partially implemented");
return false;
}
template <MisbehaviourHandler h, CheckingLevel l>
bool HostProxy<h, l>::threadPoolRequestExec(uint32_t numTasks) const noexcept {
assert(canUseThreadPool());
ensureAudioThread("thread_pool.request_exec");
return _hostThreadPool->request_exec(_host, numTasks);
}
//////////////////////////
// clap_host_voice_info //
//////////////////////////
template <MisbehaviourHandler h, CheckingLevel l>
bool HostProxy<h, l>::canUseVoiceInfo() const noexcept {
if (!_hostVoiceInfo)
return false;
if (_hostVoiceInfo->changed)
return true;
hostMisbehaving("clap_host_voice_info is partially implemented");
return false;
}
template <MisbehaviourHandler h, CheckingLevel l>
void HostProxy<h, l>::voiceInfoChanged() const noexcept {
assert(canUseVoiceInfo());
ensureMainThread("voice_info.changed");
return _hostVoiceInfo->changed(_host);
}
//////////////////////////////
// clap_host_quick_controls //
//////////////////////////////
template <MisbehaviourHandler h, CheckingLevel l>
bool HostProxy<h, l>::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 <MisbehaviourHandler h, CheckingLevel l>
void HostProxy<h, l>::quickControlsChanged() const noexcept {
assert(canUseQuickControls());
ensureMainThread("quick_controls.changed");
_hostQuickControls->changed(_host);
}
template <MisbehaviourHandler h, CheckingLevel l>
void HostProxy<h, l>::quickControlsSuggestPage(clap_id page_id) const noexcept {
assert(canUseQuickControls());
ensureMainThread("quick_controls.suggest_page");
_hostQuickControls->suggest_page(_host, page_id);
}
}} // namespace clap::helpers

View File

@ -0,0 +1,8 @@
#pragma once
namespace clap { namespace helpers {
enum class MisbehaviourHandler {
Ignore,
Terminate,
};
}} // namespace clap::helpers

View File

@ -0,0 +1,75 @@
#include <cassert>
#include <cstddef>
#include <cstdint>
#include <unordered_set>
#include <clap/clap.h>
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<Entry, EntryHash> _voicesToKill;
};
}} // namespace clap::helpers

View File

@ -0,0 +1,71 @@
#pragma once
#include <array>
#include <atomic>
#include <cassert>
#include <cstddef>
namespace clap { namespace helpers {
template <typename T, size_t CAPACITY>
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<T, CAPACITY> _data;
std::atomic<int> _writeOffset = {0};
std::atomic<int> _readOffset = {0};
};
}} // namespace clap::helpers

View File

@ -0,0 +1,37 @@
#pragma once
#include "host-proxy.hh"
namespace clap { namespace helpers {
template <MisbehaviourHandler h, CheckingLevel l>
class PluginProxy {
public:
PluginProxy(const clap_host *host, const clap_plugin *plugin);
void init();
template <typename T>
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<h, l> _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

View File

@ -0,0 +1,434 @@
#pragma once
#include <cassert>
#include <functional>
#include <mutex>
#include <queue>
#include <string>
#include <string_view>
#include <vector>
#include <clap/clap.h>
#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 <MisbehaviourHandler h, CheckingLevel l>
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<void()> callback);
// This actually runs callbacks on the main thread, you should not need to call it
void runCallbacksOnMainThread();
template <typename T>
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<h, l> _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<std::function<void()>> _mainThredCallbacks;
};
}} // namespace clap::helpers

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,48 @@
#pragma once
#include <atomic>
#include <cstddef>
#include <functional>
#include <unordered_map>
#include <clap/private/macros.h>
namespace clap { namespace helpers {
#ifdef CLAP_HAS_CXX20
template <typename T>
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 <typename K, UpdatableValue V>
template <typename K, typename V>
class ReducingParamQueue {
public:
using key_type = K;
using value_type = V;
using queue_type = std::unordered_map<key_type, value_type>;
using consumer_type = const std::function<void(const key_type &key, const value_type &value)>;
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<queue_type *> _free = nullptr;
std::atomic<queue_type *> _producer = nullptr;
std::atomic<queue_type *> _consumer = nullptr;
};
}} // namespace clap::helpers

View File

@ -0,0 +1,72 @@
#pragma once
#include <cassert>
#include "reducing-param-queue.hh"
namespace clap { namespace helpers {
template <typename K, typename V>
ReducingParamQueue<K, V>::ReducingParamQueue() {
reset();
}
template <typename K, typename V>
void ReducingParamQueue<K, V>::reset() {
for (auto &q : _queues)
q.clear();
_free = &_queues[0];
_producer = &_queues[1];
_consumer = nullptr;
}
template <typename K, typename V>
void ReducingParamQueue<K, V>::setCapacity(size_t capacity) {
for (auto &q : _queues)
q.reserve(2 * capacity);
}
template <typename K, typename V>
void ReducingParamQueue<K, V>::set(const key_type &key, const value_type &value) {
_producer.load()->insert_or_assign(key, value);
}
template <typename K, typename V>
void ReducingParamQueue<K, V>::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 <typename K, typename V>
void ReducingParamQueue<K, V>::producerDone() {
if (_consumer)
return;
auto tmp = _producer.load();
_producer = _free.load();
_free = nullptr;
_consumer = tmp;
assert(_producer);
}
template <typename K, typename V>
void ReducingParamQueue<K, V>::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

View File

@ -0,0 +1,2 @@
#define CATCH_CONFIG_MAIN
#include <catch2/catch.hpp>

View File

@ -0,0 +1,18 @@
#include <clap/helpers/param-queue.hh>
#include <clap/helpers/plugin.hh>
#include <clap/helpers/plugin.hxx>
#include <clap/helpers/reducing-param-queue.hh>
#include <clap/helpers/reducing-param-queue.hxx>
#include <catch2/catch.hpp>
CATCH_TEST_CASE("Plugin - Link") {
clap::helpers::Plugin<clap::helpers::MisbehaviourHandler::Terminate,
clap::helpers::CheckingLevel::Maximal> *p0 = nullptr;
clap::helpers::Plugin<clap::helpers::MisbehaviourHandler::Terminate,
clap::helpers::CheckingLevel::Minimal> *p1 = nullptr;
clap::helpers::Plugin<clap::helpers::MisbehaviourHandler::Terminate,
clap::helpers::CheckingLevel::None> *p2 = nullptr;
clap::helpers::Plugin<clap::helpers::MisbehaviourHandler::Ignore,
clap::helpers::CheckingLevel::Maximal> *p3 = nullptr;
}