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

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