commit e9cdc4605bc6d0bcce0a3d2c9e3f1055bbcca5f3 parent 6d2eb0f6ad57bd9d37e1ae81b8d18f36a669712b Author: Alexandre BIQUE <bique.alexandre@gmail.com> Date: Mon, 1 Nov 2021 20:33:05 +0100 Remove the examples to keep this repository minimal Diffstat:
M | CMakeLists.txt | | | 15 | +-------------- |
D | examples/.clang-format | | | 136 | ------------------------------------------------------------------------------- |
D | examples/CMakeLists.txt | | | 13 | ------------- |
D | examples/README.md | | | 11 | ----------- |
D | examples/common/CMakeLists.txt | | | 4 | ---- |
D | examples/common/param-queue.hh | | | 55 | ------------------------------------------------------- |
D | examples/common/reducing-param-queue.hh | | | 32 | -------------------------------- |
D | examples/common/reducing-param-queue.hxx | | | 60 | ------------------------------------------------------------ |
D | examples/glue/CMakeLists.txt | | | 9 | --------- |
D | examples/glue/clap-plugin.cc | | | 1106 | ------------------------------------------------------------------------------- |
D | examples/glue/clap-plugin.hh | | | 484 | ------------------------------------------------------------------------------- |
D | examples/host/CMakeLists.txt | | | 82 | ------------------------------------------------------------------------------- |
D | examples/host/application.cc | | | 106 | ------------------------------------------------------------------------------- |
D | examples/host/application.hh | | | 46 | ---------------------------------------------- |
D | examples/host/audio-settings-widget.cc | | | 120 | ------------------------------------------------------------------------------- |
D | examples/host/audio-settings-widget.hh | | | 24 | ------------------------ |
D | examples/host/audio-settings.cc | | | 24 | ------------------------ |
D | examples/host/audio-settings.hh | | | 27 | --------------------------- |
D | examples/host/device-reference.hh | | | 8 | -------- |
D | examples/host/engine.cc | | | 252 | ------------------------------------------------------------------------------- |
D | examples/host/engine.hh | | | 85 | ------------------------------------------------------------------------------- |
D | examples/host/main-window.cc | | | 142 | ------------------------------------------------------------------------------- |
D | examples/host/main-window.hh | | | 43 | ------------------------------------------- |
D | examples/host/main.cc | | | 41 | ----------------------------------------- |
D | examples/host/midi-settings-widget.cc | | | 90 | ------------------------------------------------------------------------------- |
D | examples/host/midi-settings-widget.hh | | | 21 | --------------------- |
D | examples/host/midi-settings.cc | | | 20 | -------------------- |
D | examples/host/midi-settings.hh | | | 19 | ------------------- |
D | examples/host/plugin-host.cc | | | 1215 | ------------------------------------------------------------------------------- |
D | examples/host/plugin-host.hh | | | 283 | ------------------------------------------------------------------------------- |
D | examples/host/plugin-info.hh | | | 13 | ------------- |
D | examples/host/plugin-param.cc | | | 47 | ----------------------------------------------- |
D | examples/host/plugin-param.hh | | | 69 | --------------------------------------------------------------------- |
D | examples/host/plugin-parameters-widget.cc | | | 349 | ------------------------------------------------------------------------------- |
D | examples/host/plugin-parameters-widget.hh | | | 101 | ------------------------------------------------------------------------------- |
D | examples/host/plugin-quick-control-widget.cc | | | 88 | ------------------------------------------------------------------------------- |
D | examples/host/plugin-quick-control-widget.hh | | | 39 | --------------------------------------- |
D | examples/host/plugin-quick-controls-widget.cc | | | 66 | ------------------------------------------------------------------ |
D | examples/host/plugin-quick-controls-widget.hh | | | 27 | --------------------------- |
D | examples/host/settings-dialog.cc | | | 27 | --------------------------- |
D | examples/host/settings-dialog.hh | | | 15 | --------------- |
D | examples/host/settings-widget.cc | | | 20 | -------------------- |
D | examples/host/settings-widget.hh | | | 20 | -------------------- |
D | examples/host/settings.cc | | | 13 | ------------- |
D | examples/host/settings.hh | | | 21 | --------------------- |
D | examples/plugins/CMakeLists.txt | | | 37 | ------------------------------------- |
D | examples/plugins/abstract-gui.hh | | | 34 | ---------------------------------- |
D | examples/plugins/clap-entry.cc | | | 87 | ------------------------------------------------------------------------------- |
D | examples/plugins/core-plugin.cc | | | 275 | ------------------------------------------------------------------------------- |
D | examples/plugins/core-plugin.hh | | | 196 | ------------------------------------------------------------------------------- |
D | examples/plugins/gui/CMakeLists.txt | | | 23 | ----------------------- |
D | examples/plugins/gui/application.cc | | | 210 | ------------------------------------------------------------------------------- |
D | examples/plugins/gui/application.hh | | | 39 | --------------------------------------- |
D | examples/plugins/gui/main.cc | | | 8 | -------- |
D | examples/plugins/gui/parameter-proxy.cc | | | 119 | ------------------------------------------------------------------------------- |
D | examples/plugins/gui/parameter-proxy.hh | | | 102 | ------------------------------------------------------------------------------- |
D | examples/plugins/gui/plugin-proxy.cc | | | 22 | ---------------------- |
D | examples/plugins/gui/plugin-proxy.hh | | | 24 | ------------------------ |
D | examples/plugins/gui/transport-proxy.cc | | | 70 | ---------------------------------------------------------------------- |
D | examples/plugins/gui/transport-proxy.hh | | | 161 | ------------------------------------------------------------------------------- |
D | examples/plugins/io/CMakeLists.txt | | | 10 | ---------- |
D | examples/plugins/io/buffer.hh | | | 77 | ----------------------------------------------------------------------------- |
D | examples/plugins/io/messages.hh | | | 164 | ------------------------------------------------------------------------------- |
D | examples/plugins/io/remote-channel.cc | | | 201 | ------------------------------------------------------------------------------- |
D | examples/plugins/io/remote-channel.hh | | | 136 | ------------------------------------------------------------------------------- |
D | examples/plugins/linux-clap-plugins.version | | | 6 | ------ |
D | examples/plugins/parameter-interpolator.hh | | | 69 | --------------------------------------------------------------------- |
D | examples/plugins/parameters.cc | | | 28 | ---------------------------- |
D | examples/plugins/parameters.hh | | | 155 | ------------------------------------------------------------------------------- |
D | examples/plugins/path-provider.cc | | | 92 | ------------------------------------------------------------------------------- |
D | examples/plugins/path-provider.hh | | | 20 | -------------------- |
D | examples/plugins/plugs/dc-offset/dc-offset.cc | | | 102 | ------------------------------------------------------------------------------- |
D | examples/plugins/plugs/dc-offset/dc-offset.hh | | | 25 | ------------------------- |
D | examples/plugins/plugs/gain/gain.cc | | | 87 | ------------------------------------------------------------------------------- |
D | examples/plugins/plugs/gain/gain.hh | | | 26 | -------------------------- |
D | examples/plugins/plugs/transport/transport-info.cc | | | 41 | ----------------------------------------- |
D | examples/plugins/plugs/transport/transport-info.hh | | | 19 | ------------------- |
D | examples/plugins/qml/clap/Knob.qml | | | 164 | ------------------------------------------------------------------------------- |
D | examples/plugins/qml/clap/qmldir | | | 3 | --- |
D | examples/plugins/qml/dc-offset/main.qml | | | 17 | ----------------- |
D | examples/plugins/qml/transport-info/main.qml | | | 72 | ------------------------------------------------------------------------ |
D | examples/plugins/remote-gui.cc | | | 227 | ------------------------------------------------------------------------------- |
D | examples/plugins/remote-gui.hh | | | 60 | ------------------------------------------------------------ |
D | examples/plugins/stream-helper.hh | | | 35 | ----------------------------------- |
84 files changed, 1 insertion(+), 8730 deletions(-)
diff --git a/CMakeLists.txt b/CMakeLists.txt @@ -2,26 +2,14 @@ cmake_minimum_required(VERSION 3.17) cmake_policy(SET CMP0100 NEW) # handle .hh files project(CLAP C CXX) -set(ENABLE_CLAP_GLUE TRUE CACHE BOOL "Enables the helper glue code") -set(ENABLE_CLAP_HOST FALSE CACHE BOOL "Enables the example host") -set(ENABLE_CLAP_PLUGINS FALSE CACHE BOOL "Enables the example plugins") -set(ENABLE_CLAP_GUI FALSE CACHE BOOL "Enables the plugin GUI framework") - -include_directories(include) - add_executable(clap-compile-test-c EXCLUDE_FROM_ALL src/main.c) add_executable(clap-compile-test-cpp EXCLUDE_FROM_ALL src/main.cc) add_custom_target(clap-compile-tests DEPENDS clap-compile-test-c clap-compile-test-cpp) -add_subdirectory(examples) - # If you use clap as a submodule of your plugin you need some interface projects # to allow you to link. clap-core gives you the include directory and clap-plugin-core # gives you the core + plugin-glue. add_library(clap-core INTERFACE) target_include_directories(clap-core INTERFACE include) -add_library(clap-plugin-core INTERFACE) -target_link_libraries(clap-plugin-core INTERFACE clap-core clap-plugin-glue) - -install(DIRECTORY include DESTINATION "${CMAKE_INSTALL_PREFIX}") -\ No newline at end of file +install(DIRECTORY include DESTINATION "${CMAKE_INSTALL_PREFIX}") diff --git a/examples/.clang-format b/examples/.clang-format @@ -1,136 +0,0 @@ ---- -Language: Cpp -AccessModifierOffset: -3 -AlignAfterOpenBracket: Align -AlignConsecutiveMacros: true -AlignConsecutiveAssignments: false -AlignConsecutiveDeclarations: false -AlignEscapedNewlines: Right -AlignOperands: true -AlignTrailingComments: true -AllowAllArgumentsOnNextLine: true -AllowAllConstructorInitializersOnNextLine: true -AllowAllParametersOfDeclarationOnNextLine: true -AllowShortBlocksOnASingleLine: Never -AllowShortCaseLabelsOnASingleLine: false -AllowShortFunctionsOnASingleLine: All -AllowShortLambdasOnASingleLine: All -AllowShortIfStatementsOnASingleLine: Never -AllowShortLoopsOnASingleLine: false -AlwaysBreakAfterDefinitionReturnType: None -AlwaysBreakAfterReturnType: None -AlwaysBreakBeforeMultilineStrings: false -AlwaysBreakTemplateDeclarations: Yes -BinPackArguments: false -BinPackParameters: false -BraceWrapping: - AfterCaseLabel: false - AfterClass: true - AfterControlStatement: true - AfterEnum: true - AfterFunction: true - AfterNamespace: true - AfterObjCDeclaration: true - AfterStruct: true - AfterUnion: true - AfterExternBlock: true - BeforeCatch: true - BeforeElse: true - IndentBraces: false - SplitEmptyFunction: true - SplitEmptyRecord: true - SplitEmptyNamespace: true -BreakBeforeBinaryOperators: None -BreakBeforeBraces: Attach -BreakBeforeInheritanceComma: false -BreakInheritanceList: BeforeColon -BreakBeforeTernaryOperators: true -BreakConstructorInitializersBeforeComma: false -BreakConstructorInitializers: BeforeColon -BreakAfterJavaFieldAnnotations: false -BreakStringLiterals: true -ColumnLimit: 100 -CommentPragmas: '^ IWYU pragma:' -CompactNamespaces: true -ConstructorInitializerAllOnOneLineOrOnePerLine: false -ConstructorInitializerIndentWidth: 3 -ContinuationIndentWidth: 3 -Cpp11BracedListStyle: true -DeriveLineEnding: false -DerivePointerAlignment: false -DisableFormat: false -ExperimentalAutoDetectBinPacking: false -FixNamespaceComments: true -ForEachMacros: - - foreach - - Q_FOREACH - - BOOST_FOREACH -IncludeBlocks: Preserve -IncludeCategories: - - Regex: '^"(llvm|llvm-c|clang|clang-c)/' - Priority: 2 - SortPriority: 0 - - Regex: '^(<|"(gtest|gmock|isl|json)/)' - Priority: 3 - SortPriority: 0 - - Regex: '.*' - Priority: 1 - SortPriority: 0 -IncludeIsMainRegex: '(Test)?$' -IncludeIsMainSourceRegex: '' -IndentCaseLabels: false -IndentGotoLabels: true -IndentPPDirectives: AfterHash -IndentWidth: 3 -IndentWrappedFunctionNames: false -JavaScriptQuotes: Leave -JavaScriptWrapImports: true -KeepEmptyLinesAtTheStartOfBlocks: true -MacroBlockBegin: '' -MacroBlockEnd: '' -MaxEmptyLinesToKeep: 1 -NamespaceIndentation: All -ObjCBinPackProtocolList: Auto -ObjCBlockIndentWidth: 3 -ObjCSpaceAfterProperty: false -ObjCSpaceBeforeProtocolList: true -PenaltyBreakAssignment: 2 -PenaltyBreakBeforeFirstCallParameter: 19 -PenaltyBreakComment: 300 -PenaltyBreakFirstLessLess: 120 -PenaltyBreakString: 1000 -PenaltyBreakTemplateDeclaration: 10 -PenaltyExcessCharacter: 1000000 -PenaltyReturnTypeOnItsOwnLine: 60 -PointerAlignment: Right -ReflowComments: true -SortIncludes: true -SortUsingDeclarations: true -SpaceAfterCStyleCast: false -SpaceAfterLogicalNot: false -SpaceAfterTemplateKeyword: true -SpaceBeforeAssignmentOperators: true -SpaceBeforeCpp11BracedList: false -SpaceBeforeCtorInitializerColon: true -SpaceBeforeInheritanceColon: true -SpaceBeforeParens: ControlStatements -SpaceBeforeRangeBasedForLoopColon: true -SpaceInEmptyBlock: false -SpaceInEmptyParentheses: false -SpacesBeforeTrailingComments: 1 -SpacesInAngles: false -SpacesInConditionalStatement: false -SpacesInContainerLiterals: true -SpacesInCStyleCastParentheses: false -SpacesInParentheses: false -SpacesInSquareBrackets: false -SpaceBeforeSquareBrackets: false -Standard: Latest -StatementMacros: - - Q_UNUSED - - QT_REQUIRE_VERSION -TabWidth: 8 -UseCRLF: false -UseTab: Never -... - diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt @@ -1,13 +0,0 @@ -add_subdirectory(common) - -if(ENABLE_CLAP_GLUE) - add_subdirectory(glue) -endif() - -if(ENABLE_CLAP_HOST) - add_subdirectory(host) -endif() - -if(ENABLE_CLAP_PLUGINS) - add_subdirectory(plugins) -endif() diff --git a/examples/README.md b/examples/README.md @@ -1,10 +0,0 @@ -Building the examples --- - -To get the dependencies on Windows you can do: - -1. Get vcpkg: `git clone git@github.com:microsoft/vcpkg` -2. Bootstrap vcpkg: `cd vcpkg && ./bootstrap-vcpkg.bat` -3. Install dependencies: `vcpkg --triplet=x64-windows install portmidi portaudio qt boost-serialization boost-iostreams` - -On Mac it is probably easier to use `brew` and on Linux your package manager. -\ No newline at end of file diff --git a/examples/common/CMakeLists.txt b/examples/common/CMakeLists.txt @@ -1,3 +0,0 @@ -# add_library(clap-common EXCLUDE_FROM_ALL -# reducing-param-queue.hxx -# reducing-param-queue.hh) -\ No newline at end of file diff --git a/examples/common/param-queue.hh b/examples/common/param-queue.hh @@ -1,54 +0,0 @@ -#pragma once - -#include <array> -#include <atomic> -#include <functional> - -#include <clap/all.h> - -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.load(); // write element - int wn = (w + 1) % CAPACITY; // next write element - - if (wn == _readOffset) - return false; - - _data[w] = value; - _writeOffset = wn; - return true; - } - - bool tryPop(T &value) { - int r = _readOffset; - - if (r == _writeOffset) - return false; // empty - - int rn = (r + 1) % CAPACITY; - value = _data[r]; - _readOffset = rn; - return true; - } - - void reset() { - _writeOffset = 0; - _readOffset = 0; - } - -private: - std::array<T, CAPACITY> _data; - std::atomic<int> _writeOffset = {0}; - std::atomic<int> _readOffset = {0}; -}; -\ No newline at end of file diff --git a/examples/common/reducing-param-queue.hh b/examples/common/reducing-param-queue.hh @@ -1,31 +0,0 @@ -#pragma once - -#include <atomic> -#include <functional> -#include <unordered_map> - -#include <clap/all.h> - -template <typename T> -class ReducingParamQueue { -public: - using value_type = T; - using queue_type = std::unordered_map<clap_id, value_type>; - - ReducingParamQueue(); - - void setCapacity(size_t capacity); - - void set(clap_id id, const value_type &value); - void producerDone(); - - void consume(const std::function<void(clap_id id, const value_type &value)> 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; -}; -\ No newline at end of file diff --git a/examples/common/reducing-param-queue.hxx b/examples/common/reducing-param-queue.hxx @@ -1,60 +0,0 @@ -#pragma once - -#include <cassert> - -#include "reducing-param-queue.hh" - -template<typename T> -ReducingParamQueue<T>::ReducingParamQueue() { reset(); } - -template<typename T> -void ReducingParamQueue<T>::reset() { - for (auto &q : _queues) - q.clear(); - - _free = &_queues[0]; - _producer = &_queues[1]; - _consumer = nullptr; -} - -template<typename T> -void ReducingParamQueue<T>::setCapacity(size_t capacity) { - for (auto &q : _queues) - q.reserve(2 * capacity); -} - -template<typename T> -void ReducingParamQueue<T>::set(clap_id id, const value_type& value) { - _producer.load()->insert_or_assign(id, value); -} - -template<typename T> -void ReducingParamQueue<T>::producerDone() { - if (_consumer) - return; - - auto tmp = _producer.load(); - _producer = _free.load(); - _free = nullptr; - _consumer = tmp; - - assert(_producer); -} - -template<typename T> -void ReducingParamQueue<T>::consume(const std::function<void(clap_id, const value_type& value)> 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; -} diff --git a/examples/glue/CMakeLists.txt b/examples/glue/CMakeLists.txt @@ -1,8 +0,0 @@ -add_library(clap-plugin-glue EXCLUDE_FROM_ALL STATIC clap-plugin.cc - clap-plugin.hh) -set_target_properties(clap-plugin-glue PROPERTIES CXX_STANDARD 17 - POSITION_INDEPENDENT_CODE ON) -target_include_directories(clap-plugin-glue INTERFACE .) - -install(FILES clap-plugin.hh DESTINATION "${CMAKE_INSTALL_PREFIX}/include/clap/glue") -install(TARGETS clap-plugin-glue DESTINATION "${CMAKE_INSTALL_PREFIX}/lib") -\ No newline at end of file diff --git a/examples/glue/clap-plugin.cc b/examples/glue/clap-plugin.cc @@ -1,1105 +0,0 @@ -#include <cassert> -#include <cstring> -#include <filesystem> -#include <iostream> -#include <sstream> -#include <stdexcept> -#include <utility> - -#include "clap-plugin.hh" - -namespace clap { - - Plugin::Plugin(const clap_plugin_descriptor *desc, const clap_host *host) : _host(host) { - _plugin.plugin_data = this; - _plugin.desc = desc; - _plugin.init = Plugin::clapInit; - _plugin.destroy = Plugin::clapDestroy; - _plugin.get_extension = nullptr; - _plugin.process = nullptr; - _plugin.activate = nullptr; - _plugin.deactivate = nullptr; - _plugin.start_processing = nullptr; - _plugin.stop_processing = nullptr; - } - - Plugin::~Plugin() = default; - - ///////////////////// - // CLAP Interfaces // - ///////////////////// - - //-------------// - // clap_plugin // - //-------------// - bool Plugin::clapInit(const clap_plugin *plugin) noexcept { - auto &self = from(plugin); - - self._plugin.get_extension = Plugin::clapExtension; - self._plugin.process = Plugin::clapProcess; - self._plugin.activate = Plugin::clapActivate; - self._plugin.deactivate = Plugin::clapDeactivate; - self._plugin.start_processing = Plugin::clapStartProcessing; - self._plugin.stop_processing = Plugin::clapStopProcessing; - self._plugin.on_main_thread = Plugin::clapOnMainThread; - - self.initInterfaces(); - self.ensureMainThread("clap_plugin.init"); - return self.init(); - } - - void Plugin::clapDestroy(const clap_plugin *plugin) noexcept { - auto &self = from(plugin); - self.ensureMainThread("clap_plugin.destroy"); - delete &from(plugin); - } - - void Plugin::clapOnMainThread(const clap_plugin *plugin) noexcept { - auto &self = from(plugin); - self.ensureMainThread("clap_plugin.on_main_thread"); - self.onMainThread(); - } - - bool Plugin::clapActivate(const clap_plugin *plugin, double sample_rate) noexcept { - auto &self = from(plugin); - self.ensureMainThread("clap_plugin.activate"); - - if (self._isActive) { - self.hostMisbehaving("Plugin was activated twice"); - - if (sample_rate != self._sampleRate) { - std::ostringstream msg; - msg << "The plugin was activated twice and with different sample rates: " - << self._sampleRate << " and " << sample_rate - << ". The host must deactivate the plugin first." << std::endl - << "Simulating deactivation."; - self.hostMisbehaving(msg.str()); - clapDeactivate(plugin); - } - } - - if (sample_rate <= 0) { - std::ostringstream msg; - msg << "The plugin was activated with an invalid sample rates: " << sample_rate; - self.hostMisbehaving(msg.str()); - return false; - } - - assert(!self._isActive); - assert(self._sampleRate == 0); - - if (!self.activate(sample_rate)) { - assert(!self._isActive); - assert(self._sampleRate == 0); - return false; - } - - self._isActive = true; - self._sampleRate = sample_rate; - return true; - } - - void Plugin::clapDeactivate(const clap_plugin *plugin) noexcept { - auto &self = from(plugin); - self.ensureMainThread("clap_plugin.deactivate"); - - if (!self._isActive) { - self.hostMisbehaving("The plugin was deactivated twice."); - return; - } - - self.deactivate(); - self._isActive = false; - self._sampleRate = 0; - } - - bool Plugin::clapStartProcessing(const clap_plugin *plugin) noexcept { - auto &self = from(plugin); - self.ensureAudioThread("clap_plugin.start_processing"); - - if (!self._isActive) { - self.hostMisbehaving("Host called clap_plugin.start_processing() on a deactivated plugin"); - return false; - } - - if (self._isProcessing) { - self.hostMisbehaving("Host called clap_plugin.start_processing() twice"); - return true; - } - - self._isProcessing = self.startProcessing(); - return self._isProcessing; - } - - void Plugin::clapStopProcessing(const clap_plugin *plugin) noexcept { - auto &self = from(plugin); - self.ensureAudioThread("clap_plugin.stop_processing"); - - if (!self._isActive) { - self.hostMisbehaving("Host called clap_plugin.stop_processing() on a deactivated plugin"); - return; - } - - if (!self._isProcessing) { - self.hostMisbehaving("Host called clap_plugin.stop_processing() twice"); - return; - } - - self.stopProcessing(); - self._isProcessing = false; - } - - clap_process_status Plugin::clapProcess(const clap_plugin *plugin, - const clap_process *process) noexcept { - auto &self = from(plugin); - self.ensureAudioThread("clap_plugin.process"); - - if (!self._isActive) { - self.hostMisbehaving("Host called clap_plugin.process() on a deactivated plugin"); - return CLAP_PROCESS_ERROR; - } - - if (!self._isProcessing) { - self.hostMisbehaving( - "Host called clap_plugin.process() without calling clap_plugin.start_processing()"); - return CLAP_PROCESS_ERROR; - } - - return self.process(process); - } - - const void *Plugin::clapExtension(const clap_plugin *plugin, const char *id) noexcept { - auto &self = from(plugin); - self.ensureMainThread("clap_plugin.extension"); - - if (!strcmp(id, CLAP_EXT_STATE) && self.implementsState()) - return &_pluginState; - if (!strcmp(id, CLAP_EXT_PRESET_LOAD) && self.implementsPresetLoad()) - return &_pluginPresetLoad; - if (!strcmp(id, CLAP_EXT_RENDER) && self.implementsRender()) - return &_pluginRender; - if (!strcmp(id, CLAP_EXT_TRACK_INFO) && self.implementsTrackInfo()) - return &_pluginTrackInfo; - if (!strcmp(id, CLAP_EXT_LATENCY) && self.implementsLatency()) - return &_pluginLatency; - if (!strcmp(id, CLAP_EXT_AUDIO_PORTS) && self.implementsAudioPorts()) - return &_pluginAudioPorts; - if (!strcmp(id, CLAP_EXT_PARAMS) && self.implementsParams()) - return &_pluginParams; - if (!strcmp(id, CLAP_EXT_QUICK_CONTROLS) && self.implementQuickControls()) - return &_pluginQuickControls; - if (!strcmp(id, CLAP_EXT_NOTE_NAME) && self.implementsNoteName()) - return &_pluginNoteName; - if (!strcmp(id, CLAP_EXT_THREAD_POOL) && self.implementsThreadPool()) - return &_pluginThreadPool; - if (!strcmp(id, CLAP_EXT_TIMER_SUPPORT) && self.implementsTimerSupport()) - return &_pluginTimerSupport; - if (!strcmp(id, CLAP_EXT_FD_SUPPORT) && self.implementsFdSupport()) - return &_pluginFdSupport; - if (!strcmp(id, CLAP_EXT_GUI) && self.implementsGui()) - return &_pluginGui; - if (!strcmp(id, CLAP_EXT_GUI_X11) && self.implementsGuiX11()) - return &_pluginGuiX11; - if (!strcmp(id, CLAP_EXT_GUI_WIN32) && self.implementsGuiWin32()) - return &_pluginGuiWin32; - if (!strcmp(id, CLAP_EXT_GUI_COCOA) && self.implementsGuiCocoa()) - return &_pluginGuiCocoa; - if (!strcmp(id, CLAP_EXT_GUI_FREE_STANDING) && self.implementsGuiFreeStanding()) - return &_pluginGuiFreeStanding; - - return from(plugin).extension(id); - } - - template <typename T> - void Plugin::initInterface(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)); - } - - void Plugin::initInterfaces() noexcept { - initInterface(_hostLog, CLAP_EXT_LOG); - initInterface(_hostThreadCheck, CLAP_EXT_THREAD_CHECK); - initInterface(_hostThreadPool, CLAP_EXT_THREAD_POOL); - initInterface(_hostAudioPorts, CLAP_EXT_AUDIO_PORTS); - initInterface(_hostTimerSupport, CLAP_EXT_TIMER_SUPPORT); - initInterface(_hostFdSupport, CLAP_EXT_FD_SUPPORT); - initInterface(_hostEventFilter, CLAP_EXT_EVENT_FILTER); - initInterface(_hostFileReference, CLAP_EXT_FILE_REFERENCE); - initInterface(_hostLatency, CLAP_EXT_LATENCY); - initInterface(_hostGui, CLAP_EXT_GUI); - initInterface(_hostParams, CLAP_EXT_PARAMS); - initInterface(_hostTrackInfo, CLAP_EXT_TRACK_INFO); - initInterface(_hostState, CLAP_EXT_STATE); - initInterface(_hostNoteName, CLAP_EXT_NOTE_NAME); - initInterface(_hostQuickControls, CLAP_EXT_QUICK_CONTROLS); - } - - //-------------------// - // clap_plugin_state // - //-------------------// - uint32_t Plugin::clapLatencyGet(const clap_plugin *plugin) noexcept { - auto &self = from(plugin); - self.ensureMainThread("clap_plugin_latency.get"); - - return self.latencyGet(); - } - - //--------------------// - // clap_plugin_render // - //--------------------// - void Plugin::clapRenderSetMode(const clap_plugin *plugin, - clap_plugin_render_mode mode) noexcept { - auto &self = from(plugin); - self.ensureMainThread("clap_plugin_render.set_mode"); - - switch (mode) { - case CLAP_RENDER_REALTIME: - case CLAP_RENDER_OFFLINE: - self.renderSetMode(mode); - break; - - default: { - std::ostringstream msg; - msg << "host called clap_plugin_render.set_mode with an unknown mode : " << mode; - self.hostMisbehaving(msg.str()); - break; - } - } - } - - //-------------------------// - // clap_plugin_thread_pool // - //-------------------------// - void Plugin::clapThreadPoolExec(const clap_plugin *plugin, uint32_t task_index) noexcept { - auto &self = from(plugin); - - self.threadPoolExec(task_index); - } - - //-------------------// - // clap_plugin_state // - //-------------------// - bool Plugin::clapStateSave(const clap_plugin *plugin, clap_ostream *stream) noexcept { - auto &self = from(plugin); - self.ensureMainThread("clap_plugin_state.save"); - - return self.stateSave(stream); - } - - bool Plugin::clapStateLoad(const clap_plugin *plugin, clap_istream *stream) noexcept { - auto &self = from(plugin); - self.ensureMainThread("clap_plugin_state.load"); - - return self.stateLoad(stream); - } - - //-------------------------// - // clap_plugin_preset_load // - //-------------------------// - bool Plugin::clapPresetLoadFromFile(const clap_plugin *plugin, const char *path) noexcept { - auto &self = from(plugin); - self.ensureMainThread("clap_plugin_preset_load.from_file"); - - if (!path) { - self.hostMisbehaving("host called clap_plugin_preset_load.from_file with a null path"); - return false; - } - - // TODO check if the file is readable - - return self.presetLoadFromFile(path); - } - - //------------------------// - // clap_plugin_track_info // - //------------------------// - - void Plugin::clapTrackInfoChanged(const clap_plugin *plugin) noexcept { - auto &self = from(plugin); - self.ensureMainThread("clap_plugin_track_info.changed"); - - if (!self.canUseTrackInfo()) { - self.hostMisbehaving("host called clap_plugin_track_info.changed() but does not provide a " - "complete clap_host_track_info interface"); - return; - } - - self.trackInfoChanged(); - } - - //-------------------------// - // clap_plugin_audio_ports // - //-------------------------// - - uint32_t Plugin::clapAudioPortsCount(const clap_plugin *plugin, bool is_input) noexcept { - auto &self = from(plugin); - self.ensureMainThread("clap_plugin_audio_ports.count"); - - return self.audioPortsCount(is_input); - } - - bool Plugin::clapAudioPortsInfo(const clap_plugin *plugin, - uint32_t index, - bool is_input, - clap_audio_port_info *info) noexcept { - auto &self = from(plugin); - self.ensureMainThread("clap_plugin_audio_ports.info"); - auto count = clapAudioPortsCount(plugin, is_input); - if (index >= count) { - std::ostringstream msg; - msg << "Host called clap_plugin_audio_ports.info() with an index out of bounds: " << index - << " >= " << count; - self.hostMisbehaving(msg.str()); - return false; - } - - return self.audioPortsInfo(index, is_input, info); - } - - uint32_t Plugin::clapAudioPortsConfigCount(const clap_plugin *plugin) noexcept { - auto &self = from(plugin); - self.ensureMainThread("clap_plugin_audio_ports.config_count"); - return self.audioPortsConfigCount(); - } - - bool Plugin::clapAudioPortsGetConfig(const clap_plugin *plugin, - uint32_t index, - clap_audio_ports_config *config) noexcept { - auto &self = from(plugin); - self.ensureMainThread("clap_plugin_audio_ports.get_config"); - - auto count = clapAudioPortsConfigCount(plugin); - if (index >= count) { - std::ostringstream msg; - msg << "called clap_plugin_audio_ports.get_config with an index out of bounds: " << index - << " >= " << count; - self.hostMisbehaving(msg.str()); - return false; - } - return self.audioPortsGetConfig(index, config); - } - - bool Plugin::clapAudioPortsSetConfig(const clap_plugin *plugin, clap_id config_id) noexcept { - auto &self = from(plugin); - self.ensureMainThread("clap_plugin_audio_ports.get_config"); - - if (self.isActive()) - self.hostMisbehaving( - "it is illegal to call clap_audio_ports.set_config if the plugin is active"); - - return self.audioPortsSetConfig(config_id); - } - - uint32_t Plugin::clapParamsCount(const clap_plugin *plugin) noexcept { - auto &self = from(plugin); - self.ensureMainThread("clap_plugin_params.count"); - - return self.paramsCount(); - } - - //--------------------// - // clap_plugin_params // - //--------------------// - bool Plugin::clapParamsInfo(const clap_plugin *plugin, - int32_t param_index, - clap_param_info *param_info) noexcept { - auto &self = from(plugin); - self.ensureMainThread("clap_plugin_params.info"); - - auto count = clapParamsCount(plugin); - if (param_index >= count) { - std::ostringstream msg; - msg << "called clap_plugin_params.info with an index out of bounds: " << param_index - << " >= " << count; - self.hostMisbehaving(msg.str()); - return false; - } - - return self.paramsInfo(param_index, param_info); - } - - bool - Plugin::clapParamsValue(const clap_plugin *plugin, clap_id param_id, double *value) noexcept { - auto &self = from(plugin); - self.ensureMainThread("clap_plugin_params.value"); - - if (!self.isValidParamId(param_id)) { - std::ostringstream msg; - msg << "clap_plugin_params.value called with invalid param_id: " << param_id; - self.hostMisbehaving(msg.str()); - return false; - } - - // TODO extra checks - - return self.paramsValue(param_id, value); - } - - void Plugin::clapParamsFlush(const clap_plugin *plugin, - const clap_event_list *input_parameter_changes, - const clap_event_list *output_parameter_changes) noexcept { - auto &self = from(plugin); - self.ensureParamThread("clap_plugin_params.flush"); - - if (!input_parameter_changes) - self.hostMisbehaving("clap_plugin_params.flush called with an null input parameter change list"); - else { - uint32_t N = input_parameter_changes->size(input_parameter_changes); - for (uint32_t i = 0; i < N; ++i) - { - auto ev = input_parameter_changes->get(input_parameter_changes, i); - if (!ev) { - std::ostringstream msg; - msg << "clap_plugin_params.flush called null event inside the input list at index: " << i; - self.hostMisbehaving(msg.str()); - continue; - } - - if (ev->type != CLAP_EVENT_PARAM_VALUE) { - self.hostMisbehaving("clap_plugin_params.flush must only contain CLAP_EVENT_PARAM_VALUE event type"); - continue; - } - - if (!self.isValidParamId(ev->param_value.param_id)) { - std::ostringstream msg; - msg << "clap_plugin_params.flush called unknown paramId: " << ev->param_value.param_id; - self.hostMisbehaving(msg.str()); - continue; - } - - // TODO: check range? - } - } - - if (!output_parameter_changes) - self.hostMisbehaving("clap_plugin_params.flush called with an null output parameter change list"); - else if (output_parameter_changes->size(output_parameter_changes) > 0) - self.hostMisbehaving("clap_plugin_params.flush called with an non-empty output parameter change list"); - - self.paramsFlush(input_parameter_changes, output_parameter_changes); - } - - bool Plugin::clapParamsValueToText(const clap_plugin *plugin, - clap_id param_id, - double value, - char *display, - uint32_t size) noexcept { - auto &self = from(plugin); - self.ensureMainThread("clap_plugin_params.value_to_text"); - - if (!self.isValidParamId(param_id)) { - std::ostringstream msg; - msg << "clap_plugin_params.value_to_text called with invalid param_id: " << param_id; - self.hostMisbehaving(msg.str()); - return false; - } - - if (!display) { - self.hostMisbehaving( - "clap_plugin_params.value_to_text called with a null display pointer"); - return false; - } - - if (size <= 1) { - self.hostMisbehaving("clap_plugin_params.value_to_text called with a empty buffer (less " - "than one character)"); - return false; - } - - return self.paramsValueToText(param_id, value, display, size); - } - - bool Plugin::clapParamsTextToValue(const clap_plugin *plugin, - clap_id param_id, - const char *display, - double *value) noexcept { - auto &self = from(plugin); - self.ensureMainThread("clap_plugin_params.text_to_value"); - - if (!self.isValidParamId(param_id)) { - std::ostringstream msg; - msg << "clap_plugin_params.text_to_value called with invalid param_id: " << param_id; - self.hostMisbehaving(msg.str()); - return false; - } - - if (!display) { - self.hostMisbehaving( - "clap_plugin_params.text_to_value called with a null display pointer"); - return false; - } - - if (!value) { - self.hostMisbehaving("clap_plugin_params.text_to_value called with a null value pointer"); - return false; - } - - return self.paramsTextToValue(param_id, display, value); - } - - bool Plugin::isValidParamId(clap_id param_id) const noexcept { - checkMainThread(); - - auto count = paramsCount(); - clap_param_info info; - for (uint32_t i = 0; i < count; ++i) { - if (!paramsInfo(i, &info)) - // TODO: fatal error? - continue; - - if (info.id == param_id) - return true; - } - return false; - } - - //----------------------------// - // clap_plugin_quick_controls // - //----------------------------// - uint32_t Plugin::clapQuickControlsPageCount(const clap_plugin *plugin) noexcept { - auto &self = from(plugin); - self.ensureMainThread("clap_plugin_quick_controls.page_count"); - - return self.quickControlsPageCount(); - } - - bool Plugin::clapQuickControlsPageInfo(const clap_plugin *plugin, - uint32_t page_index, - clap_quick_controls_page *page) noexcept { - auto &self = from(plugin); - self.ensureMainThread("clap_plugin_quick_controls.page_info"); - - uint32_t count = clapQuickControlsPageCount(plugin); - if (page_index >= count) { - std::ostringstream msg; - msg << "Host called clap_plugin_quick_controls.page_info() with an index out of bounds: " - << page_index << " >= " << count; - self.hostMisbehaving(msg.str()); - return false; - } - - return self.quickControlsPageInfo(page_index, page); - } - - void Plugin::clapQuickControlsSelectPage(const clap_plugin *plugin, clap_id page_id) noexcept { - auto &self = from(plugin); - self.ensureMainThread("clap_plugin_quick_controls.select_page"); - - return self.quickControlsSelectPage(page_id); - } - - clap_id Plugin::clapQuickControlsSelectedPage(const clap_plugin *plugin) noexcept { - auto &self = from(plugin); - self.ensureMainThread("clap_plugin_quick_controls.selected_page"); - - return self.quickControlsSelectedPage(); - } - - //-----------------------// - // clap_plugin_note_name // - //-----------------------// - - uint32_t Plugin::clapNoteNameCount(const clap_plugin *plugin) noexcept { - auto &self = from(plugin); - self.ensureMainThread("clap_plugin_note_name.count"); - return self.noteNameCount(); - } - - bool Plugin::clapNoteNameGet(const clap_plugin *plugin, - uint32_t index, - clap_note_name *note_name) noexcept { - auto &self = from(plugin); - self.ensureMainThread("clap_plugin_note_name.get"); - - // TODO check index - auto count = clapNoteNameCount(plugin); - if (index >= count) { - std::ostringstream msg; - msg << "host called clap_plugin_note_name.get with an index out of bounds: " << index - << " >= " << count; - self.hostMisbehaving(msg.str()); - return false; - } - - return self.noteNameGet(index, note_name); - } - - //------------------------// - // clap_plugin_event_loop // - //------------------------// - void Plugin::clapOnTimer(const clap_plugin *plugin, clap_id timer_id) noexcept { - auto &self = from(plugin); - self.ensureMainThread("clap_plugin_event_loop.on_timer"); - - if (timer_id == CLAP_INVALID_ID) { - self.hostMisbehaving( - "Host called clap_plugin_event_loop.on_timer with an invalid timer_id"); - return; - } - - self.onTimer(timer_id); - } - - void Plugin::clapOnFd(const clap_plugin *plugin, clap_fd fd, uint32_t flags) noexcept { - auto &self = from(plugin); - self.ensureMainThread("clap_plugin_event_loop.on_fd"); - - self.onFd(fd, flags); - } - - //-----------------// - // clap_plugin_gui // - //-----------------// - bool Plugin::clapGuiSize(const clap_plugin *plugin, uint32_t *width, uint32_t *height) noexcept { - auto &self = from(plugin); - self.ensureMainThread("clap_plugin_gui.size"); - - if (!self._isGuiCreated) { - self.hostMisbehaving("clap_plugin_gui.size() was called without a prior call to " - "clap_plugin_gui.create()"); - return false; - } - - return self.guiSize(width, height); - } - - bool Plugin::clapGuiCanResize(const clap_plugin *plugin) noexcept { - auto &self = from(plugin); - self.ensureMainThread("clap_plugin_gui.can_resize"); - - if (!self._isGuiCreated) { - self.hostMisbehaving("clap_plugin_gui.can_resize() was called without a prior call to " - "clap_plugin_gui.create()"); - return false; - } - - return self.guiCanResize(); - } - - void - Plugin::clapGuiRoundSize(const clap_plugin *plugin, uint32_t *width, uint32_t *height) noexcept { - auto &self = from(plugin); - self.ensureMainThread("clap_plugin_gui.round_size"); - - if (!self._isGuiCreated) { - self.hostMisbehaving("clap_plugin_gui.round_size() was called without a prior call to " - "clap_plugin_gui.create()"); - return; - } - - self.guiRoundSize(width, height); - } - - bool - Plugin::clapGuiSetSize(const clap_plugin *plugin, uint32_t width, uint32_t height) noexcept { - auto &self = from(plugin); - self.ensureMainThread("clap_plugin_gui.set_size"); - - if (!self._isGuiCreated) { - self.hostMisbehaving("clap_plugin_gui.set_size() was called without a prior call to " - "clap_plugin_gui.create()"); - return false; - } - - if (!self.guiCanResize()) { - self.hostMisbehaving("clap_plugin_gui.set_size() was called but the gui is not resizable"); - return false; - } - - uint32_t testWidth = width; - uint32_t testHeight = height; - self.guiRoundSize(&testWidth, &testHeight); - - if (width != testWidth || height != testHeight) { - std::ostringstream os; - os << "clap_plugin_gui.set_size() was called with a size which was not adjusted by " - "clap_plugin_gui.round_size(): " - << width << "x" << height << " vs " << testWidth << "x" << testHeight; - self.hostMisbehaving(os.str()); - } - - return self.guiSetSize(width, height); - } - - void Plugin::clapGuiSetScale(const clap_plugin *plugin, double scale) noexcept { - auto &self = from(plugin); - self.ensureMainThread("clap_plugin_gui.set_scale"); - - if (!self._isGuiCreated) { - self.hostMisbehaving("clap_plugin_gui.set_scale() was called without a prior call to " - "clap_plugin_gui.create()"); - return; - } - - self.guiSetScale(scale); - } - - void Plugin::clapGuiShow(const clap_plugin *plugin) noexcept { - auto &self = from(plugin); - self.ensureMainThread("clap_plugin_gui.show"); - - if (!self._isGuiCreated) { - self.hostMisbehaving( - "clap_plugin_gui.show() was called without a prior call to clap_plugin_gui.create()"); - return; - } - - if (!self._isGuiAttached) { - self.hostMisbehaving("clap_plugin_gui.show() but the gui was not attached first"); - return; - } - - self.guiShow(); - } - - void Plugin::clapGuiHide(const clap_plugin *plugin) noexcept { - auto &self = from(plugin); - self.ensureMainThread("clap_plugin_gui.hide"); - - if (!self._isGuiCreated) { - self.hostMisbehaving( - "clap_plugin_gui.hide() was called without a prior call to clap_plugin_gui.create()"); - return; - } - - if (!self._isGuiAttached) { - self.hostMisbehaving("clap_plugin_gui.hide() but the gui was not attached first"); - return; - } - - self.guiHide(); - } - - bool Plugin::clapGuiCreate(const clap_plugin *plugin) noexcept { - auto &self = from(plugin); - self.ensureMainThread("clap_plugin_gui.create"); - - if (self._isGuiCreated) { - self.hostMisbehaving( - "clap_plugin_gui.create() was called while the plugin gui was already created"); - return true; - } - - if (!self.guiCreate()) - return false; - - self._isGuiCreated = true; - self._isGuiAttached = false; - return true; - } - - void Plugin::clapGuiDestroy(const clap_plugin *plugin) noexcept { - auto &self = from(plugin); - self.ensureMainThread("clap_plugin_gui.destroy"); - - if (!self._isGuiCreated) { - self.hostMisbehaving( - "clap_plugin_gui.destroy() was called while the plugin gui not created"); - return; - } - - self.guiDestroy(); - self._isGuiCreated = false; - self._isGuiAttached = false; - } - - //---------------------// - // clap_plugin_gui_x11 // - //---------------------// - bool Plugin::clapGuiX11Attach(const clap_plugin *plugin, - const char *display_name, - unsigned long window) noexcept { - auto &self = from(plugin); - self.ensureMainThread("clap_plugin_gui_x11.attach"); - - if (!self._isGuiCreated) { - self.hostMisbehaving("clap_plugin_gui_x11.attach() was called without a prior call to " - "clap_plugin_gui.create()"); - return false; - } - - if (self._isGuiAttached) { - self.hostMisbehaving("clap_plugin_gui_x11.attach() but the gui was already attached"); - return true; - } - - if (!self.guiX11Attach(display_name, window)) - return false; - - self._isGuiAttached = true; - return true; - } - - //-----------------------// - // clap_plugin_gui_win32 // - //-----------------------// - bool Plugin::clapGuiWin32Attach(const clap_plugin *plugin, clap_hwnd window) noexcept { - auto &self = from(plugin); - self.ensureMainThread("clap_plugin_gui_win32.attach"); - - if (!self._isGuiCreated) { - self.hostMisbehaving("clap_plugin_gui_win32.attach() was called without a prior call to " - "clap_plugin_gui.create()"); - return false; - } - - if (self._isGuiAttached) { - self.hostMisbehaving("clap_plugin_gui_win32.attach() but the gui was already attached"); - return true; - } - - if (!self.guiWin32Attach(window)) - return false; - - self._isGuiAttached = true; - return true; - } - - //-----------------------// - // clap_plugin_gui_cocoa // - //-----------------------// - bool Plugin::clapGuiCocoaAttach(const clap_plugin *plugin, void *nsView) noexcept { - auto &self = from(plugin); - self.ensureMainThread("clap_plugin_gui_cocoa.attach"); - - if (!self._isGuiCreated) { - self.hostMisbehaving("clap_plugin_gui_cocoa.attach() was called without a prior call to " - "clap_plugin_gui.create()"); - return false; - } - - if (self._isGuiAttached) { - self.hostMisbehaving("clap_plugin_gui_cocoa.attach() but the gui was already attached"); - return true; - } - - if (!self.guiCocoaAttach(nsView)) - return false; - - self._isGuiAttached = true; - return true; - } - - //-------------------------------// - // clap_plugin_gui_free_standing // - //-------------------------------// - bool Plugin::clapGuiFreeStandingOpen(const clap_plugin *plugin) noexcept { - auto &self = from(plugin); - self.ensureMainThread("clap_plugin_gui_free_standing.open"); - - if (!self._isGuiCreated) { - self.hostMisbehaving("clap_plugin_gui_free_standing.open() was called without a prior " - "call to clap_plugin_gui.create()"); - return false; - } - - if (self._isGuiAttached) { - self.hostMisbehaving( - "clap_plugin_gui_free_standing.open() but the gui was already attached"); - return true; - } - - if (!self.guiFreeStandingOpen()) - return false; - - self._isGuiAttached = true; - return true; - } - - ///////////// - // Logging // - ///////////// - void Plugin::log(clap_log_severity severity, const char *msg) const noexcept { - if (canUseHostLog()) - _hostLog->log(_host, severity, msg); - else - std::clog << msg << std::endl; - } - - void Plugin::hostMisbehaving(const char *msg) const noexcept { - log(CLAP_LOG_HOST_MISBEHAVING, msg); - } - - ///////////////////////////////// - // Interface consistency check // - ///////////////////////////////// - - bool Plugin::canUseHostLog() const noexcept { return _hostLog && _hostLog->log; } - - bool Plugin::canUseThreadCheck() const noexcept { - return _hostThreadCheck && _hostThreadCheck->is_audio_thread && - _hostThreadCheck->is_main_thread; - } - - bool Plugin::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; - } - - bool Plugin::canUseFdSupport() const noexcept { - if (!_hostFdSupport) - return false; - - auto &x = *_hostFdSupport; - if (x.modify_fd && x.register_fd && x.unregister_fd) - return true; - - hostMisbehaving("clap_fd_support is partially implemented"); - return false; - } - - bool Plugin::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; - } - - bool Plugin::canUseLatency() const noexcept { - if (!_hostLatency) - return false; - - if (_hostLatency->changed) - return true; - - hostMisbehaving("clap_host_latency is partially implemented"); - return false; - } - - bool Plugin::canUseQuickControls() const noexcept { - if (!_hostQuickControls) - return false; - - if (_hostQuickControls->changed) - return true; - - hostMisbehaving("clap_host_quick_controls is partially implemented"); - return false; - } - - bool Plugin::canUseState() const noexcept { - if (!_hostState) - return false; - - if (_hostState->mark_dirty) - return true; - - hostMisbehaving("clap_host_state is partially implemented"); - return false; - } - - bool Plugin::canUseTrackInfo() const noexcept { - if (!_hostTrackInfo) - return false; - - if (_hostTrackInfo->get) - return true; - - hostMisbehaving("clap_host_track_info is partially implemented"); - return false; - } - - ///////////////////// - // Thread Checking // - ///////////////////// - - void Plugin::checkMainThread() const noexcept { - if (!_hostThreadCheck || !_hostThreadCheck->is_main_thread || - _hostThreadCheck->is_main_thread(_host)) - return; - - std::terminate(); - } - - void Plugin::checkAudioThread() const noexcept { - if (!_hostThreadCheck || !_hostThreadCheck->is_audio_thread || - _hostThreadCheck->is_audio_thread(_host)) - return; - - std::terminate(); - } - - void Plugin::checkParamThread() const noexcept { - if (isActive()) - checkAudioThread(); - else - checkMainThread(); - } - - void Plugin::ensureParamThread(const char *method) const noexcept { - if (isActive()) - ensureAudioThread(method); - else - ensureMainThread(method); - } - - void Plugin::ensureMainThread(const char *method) const noexcept { - if (!_hostThreadCheck || !_hostThreadCheck->is_main_thread || - _hostThreadCheck->is_main_thread(_host)) - return; - - std::ostringstream msg; - msg << "Host called the method " << method - << "() on wrong thread! It must be called on main thread!"; - hostMisbehaving(msg.str()); - std::terminate(); - } - - void Plugin::ensureAudioThread(const char *method) const noexcept { - if (!_hostThreadCheck || !_hostThreadCheck->is_audio_thread || - _hostThreadCheck->is_audio_thread(_host)) - return; - - std::ostringstream msg; - msg << "Host called the method " << method - << "() on wrong thread! It must be called on audio thread!"; - hostMisbehaving(msg.str()); - std::terminate(); - } - - /////////////// - // Utilities // - /////////////// - Plugin &Plugin::from(const clap_plugin *plugin) noexcept { - if (!plugin) { - std::cerr << "called with a null clap_plugin pointer!" << std::endl; - std::terminate(); - } - - if (!plugin->plugin_data) { - std::cerr << "called with a null clap_plugin->plugin_data pointer! The host must never " - "change this pointer!" - << std::endl; - std::terminate(); - } - - return *static_cast<Plugin *>(plugin->plugin_data); - } - - uint32_t Plugin::compareAudioPortsInfo(const clap_audio_port_info &a, - const clap_audio_port_info &b) noexcept { - if (a.sample_size != b.sample_size || a.in_place != b.in_place || a.is_cv != b.is_cv || - a.is_main != b.is_main || a.channel_count != b.channel_count || - a.channel_map != b.channel_map || a.id != b.id) - return CLAP_AUDIO_PORTS_RESCAN_ALL; - - if (strncmp(a.name, b.name, sizeof(a.name))) - return CLAP_AUDIO_PORTS_RESCAN_NAMES; - - return 0; - } -} // namespace clap -\ No newline at end of file diff --git a/examples/glue/clap-plugin.hh b/examples/glue/clap-plugin.hh @@ -1,483 +0,0 @@ -#pragma once - -#include <cassert> -#include <string> -#include <string_view> -#include <vector> - -#include <clap/all.h> - -namespace clap { - /// @brief C++ glue and checks - /// - /// @note for an higher level implementation, see @ref PluginHelper - class Plugin { - public: - const clap_plugin *clapPlugin() noexcept { return &_plugin; } - - protected: - Plugin(const clap_plugin_descriptor *desc, const clap_host *host); - virtual ~Plugin(); - - // 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) 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 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_render // - //--------------------// - virtual bool implementsRender() const noexcept { return false; } - virtual void renderSetMode(clap_plugin_render_mode mode) noexcept {} - - //-------------------------// - // 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(clap_ostream *stream) noexcept { return false; } - virtual bool stateLoad(clap_istream *stream) noexcept { return false; } - void stateMarkDirty() const noexcept { - if (canUseState()) - _hostState->mark_dirty(_host); - } - - //-------------------------// - // 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(int32_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_event_list *input_parameter_changes, - const clap_event_list *output_parameter_changes) 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 quickControlsPageInfo(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_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_fd_support // - //------------------------// - virtual bool implementsFdSupport() const noexcept { return false; } - virtual void onFd(clap_fd fd, uint32_t flags) noexcept {} - - //-----------------// - // clap_plugin_gui // - //-----------------// - virtual bool implementsGui() const noexcept { return false; } - virtual bool guiCreate() noexcept { return false; } - virtual void guiDestroy() noexcept {} - virtual void guiSetScale(double scale) noexcept {} - virtual void guiShow() noexcept {} - virtual void guiHide() noexcept {} - virtual bool guiSize(uint32_t *width, uint32_t *height) noexcept { return false; } - virtual bool guiCanResize() const noexcept { return false; } - virtual void guiRoundSize(uint32_t *width, uint32_t *height) noexcept { - guiSize(width, height); - } - virtual bool guiSetSize(uint32_t width, uint32_t height) noexcept { return false; } - - //---------------------// - // clap_plugin_gui_x11 // - //---------------------// - virtual bool implementsGuiX11() const noexcept { return false; } - virtual bool guiX11Attach(const char *displayName, unsigned long window) noexcept { - return false; - } - - //-----------------------// - // clap_plugin_gui_win32 // - //-----------------------// - virtual bool implementsGuiWin32() const noexcept { return false; } - virtual bool guiWin32Attach(clap_hwnd window) noexcept { return false; } - - //-----------------------// - // clap_plugin_gui_cocoa // - //-----------------------// - virtual bool implementsGuiCocoa() const noexcept { return false; } - virtual bool guiCocoaAttach(void *nsView) noexcept { return false; } - - //-------------------------------// - // clap_plugin_gui_free_standing // - //-------------------------------// - virtual bool implementsGuiFreeStanding() const noexcept { return false; } - virtual bool guiFreeStandingOpen() 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()); } - - ///////////////////////////////// - // Interface consistency check // - ///////////////////////////////// - bool canUseHostLog() const noexcept; - bool canUseThreadCheck() const noexcept; - bool canUseTrackInfo() const noexcept; - bool canUseState() const noexcept; - bool canUseTimerSupport() const noexcept; - bool canUseFdSupport() const noexcept; - bool canUseParams() const noexcept; - bool canUseLatency() const noexcept; - bool canUseQuickControls() 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) noexcept; - - 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; - } - - protected: - const clap_host *const _host = nullptr; - 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_event_filter *_hostEventFilter = 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_fd_support *_hostFdSupport = 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; - - 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) 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 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_render - static void 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, clap_ostream *stream) noexcept; - static bool clapStateLoad(const clap_plugin *plugin, 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, - int32_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_event_list *input_parameter_changes, - const clap_event_list *output_parameter_changes) noexcept; - - // clap_plugin_quick_controls - static uint32_t clapQuickControlsPageCount(const clap_plugin *plugin) noexcept; - static bool clapQuickControlsPageInfo(const clap_plugin *plugin, - uint32_t page_index, - clap_quick_controls_page *page) noexcept; - static void clapQuickControlsSelectPage(const clap_plugin *plugin, clap_id page_id) noexcept; - static clap_id clapQuickControlsSelectedPage(const clap_plugin *plugin) 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 clapOnFd(const clap_plugin *plugin, clap_fd fd, uint32_t flags) noexcept; - - // clap_plugin_gui - static bool clapGuiCreate(const clap_plugin *plugin) noexcept; - static void clapGuiDestroy(const clap_plugin *plugin) noexcept; - static void clapGuiSetScale(const clap_plugin *plugin, double scale) noexcept; - static bool - clapGuiSize(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 void clapGuiShow(const clap_plugin *plugin) noexcept; - static bool clapGuiCanResize(const clap_plugin *plugin) noexcept; - static void - clapGuiRoundSize(const clap_plugin *plugin, uint32_t *width, uint32_t *height) noexcept; - static void clapGuiHide(const clap_plugin *plugin) noexcept; - - // clap_plugin_gui_x11 - static bool clapGuiX11Attach(const clap_plugin *plugin, - const char *display_name, - unsigned long window) noexcept; - - // clap_plugin_gui_win32 - static bool clapGuiWin32Attach(const clap_plugin *plugin, clap_hwnd window) noexcept; - - // clap_plugin_gui_cocoa - static bool clapGuiCocoaAttach(const clap_plugin *plugin, void *nsView) noexcept; - - // clap_plugin_gui_free_standing - static bool clapGuiFreeStandingOpen(const clap_plugin *plugin) noexcept; - - // interfaces - static const constexpr clap_plugin_render _pluginRender = { - clapRenderSetMode, - }; - - static const constexpr clap_plugin_thread_pool _pluginThreadPool = { - clapThreadPoolExec, - }; - - static const constexpr clap_plugin_state _pluginState = { - clapStateSave, - clapStateLoad, - }; - - static const constexpr clap_plugin_preset_load _pluginPresetLoad = { - clapPresetLoadFromFile, - }; - - static const constexpr clap_plugin_track_info _pluginTrackInfo = { - clapTrackInfoChanged, - }; - - static const constexpr clap_plugin_audio_ports _pluginAudioPorts = {clapAudioPortsCount, - clapAudioPortsInfo}; - - static const constexpr clap_plugin_audio_ports_config _pluginAudioPortsConfig = { - clapAudioPortsConfigCount, - clapAudioPortsGetConfig, - clapAudioPortsSetConfig, - }; - - static const constexpr clap_plugin_params _pluginParams = { - clapParamsCount, - clapParamsInfo, - clapParamsValue, - clapParamsValueToText, - clapParamsTextToValue, - clapParamsFlush, - }; - - static const constexpr clap_plugin_quick_controls _pluginQuickControls = { - clapQuickControlsPageCount, - clapQuickControlsPageInfo, - clapQuickControlsSelectPage, - clapQuickControlsSelectedPage, - }; - - static const constexpr clap_plugin_latency _pluginLatency = { - clapLatencyGet, - }; - - static const constexpr clap_plugin_note_name _pluginNoteName = { - clapNoteNameCount, - clapNoteNameGet, - }; - - static const constexpr clap_plugin_timer_support _pluginTimerSupport = {clapOnTimer}; - - static const constexpr clap_plugin_fd_support _pluginFdSupport = { - clapOnFd, - }; - - static const constexpr clap_plugin_gui _pluginGui = { - clapGuiCreate, - clapGuiDestroy, - clapGuiSetScale, - clapGuiSize, - clapGuiCanResize, - clapGuiRoundSize, - clapGuiSetSize, - clapGuiShow, - clapGuiHide, - }; - - static const constexpr clap_plugin_gui_x11 _pluginGuiX11 = { - clapGuiX11Attach, - }; - - static const constexpr clap_plugin_gui_win32 _pluginGuiWin32 = { - clapGuiWin32Attach, - }; - - static const constexpr clap_plugin_gui_cocoa _pluginGuiCocoa = { - clapGuiCocoaAttach, - }; - - static const constexpr clap_plugin_gui_free_standing _pluginGuiFreeStanding = { - clapGuiFreeStandingOpen, - }; - - // state - bool _isActive = false; - bool _isProcessing = false; - double _sampleRate = 0; - - bool _isGuiCreated = false; - bool _isGuiAttached = false; - }; -} // namespace clap -\ No newline at end of file diff --git a/examples/host/CMakeLists.txt b/examples/host/CMakeLists.txt @@ -1,81 +0,0 @@ -set(CMAKE_INCLUDE_CURRENT_DIR ON) -set(CMAKE_AUTOMOC ON) - -find_package(Qt6Core REQUIRED) -find_package(Qt6Widgets REQUIRED) - -find_package(PkgConfig REQUIRED) -pkg_check_modules(PortAudio REQUIRED IMPORTED_TARGET portaudio-2.0) - -# PortMidi do not provide pkgconfig it seems -# if (UNIX AND NOT APPLE) -# pkg_check_modules(PortMidi REQUIRED IMPORTED_TARGET portmidi) -# endif() - -if (APPLE) - add_library(portmidi INTERFACE) - target_link_options(portmidi INTERFACE -L/usr/local/lib) - target_link_libraries(portmidi INTERFACE libportmidi.dylib) -endif() - -add_executable(clap-host - application.cc - application.hh - audio-settings.cc - audio-settings.hh - audio-settings-widget.cc - audio-settings-widget.hh - - plugin-param.cc - plugin-param.hh - plugin-host.cc - plugin-host.hh - plugin-quick-control-widget.cc - plugin-quick-control-widget.hh - plugin-quick-controls-widget.cc - plugin-quick-controls-widget.hh - - CMakeLists.txt - device-reference.hh - engine.cc - engine.hh - main.cc - main-window.cc - main-window.hh - midi-settings.cc - midi-settings.hh - midi-settings-widget.cc - midi-settings-widget.hh - plugin-info.hh - plugin-parameters-widget.cc - plugin-parameters-widget.hh - settings.cc - settings-dialog.cc - settings-dialog.hh - settings.hh - settings-widget.cc - settings-widget.hh - ) - -set_target_properties(clap-host PROPERTIES CXX_STANDARD 17) -target_link_libraries(clap-host Qt6::Widgets Qt6::Core) -target_link_libraries(clap-host portmidi portaudio) - -if (LINUX) - target_link_libraries(clap-host dl pthread) -endif() - -if (APPLE) - set_target_properties(clap-host PROPERTIES OSX_ARCHITECTURES x86_64) - - find_library(CORE_FOUNDATION CoreFoundation) - find_library(CORE_AUDIO CoreAudio) - find_library(CORE_AUDIO CoreServices) - find_library(CORE_MIDI CoreMIDI) - find_library(AUDIO_UNIT AudioUnit) - find_library(AUDIO_TOOLBOX AudioToolbox) - find_library(CARBON Carbon) - target_link_libraries(clap-host ${CARBON} ${AUDIO_UNIT} ${AUDIO_TOOLBOX} ${CORE_MIDI} ${CORE_AUDIO} ${CORE_SERVICES} ${CORE_FOUNDATION}) -endif() - -install(TARGETS clap-host DESTINATION "${CMAKE_INSTALL_PREFIX}/bin") -\ No newline at end of file diff --git a/examples/host/application.cc b/examples/host/application.cc @@ -1,106 +0,0 @@ -#include <cassert> - -#ifdef Q_UNIX -# include <unistd.h> -#endif - -#include <QApplication> -#include <QCommandLineParser> -#include <QSettings> - -#include "application.hh" -#include "main-window.hh" -#include "settings.hh" - -Application *Application::_instance = nullptr; - -Q_DECLARE_METATYPE(int32_t) -Q_DECLARE_METATYPE(uint32_t) - -Application::Application(int argc, char **argv) - : QApplication(argc, argv), _settings(new Settings) { - assert(!_instance); - _instance = this; - - setOrganizationDomain("github.com/free-audio/clap"); - setOrganizationName("clap"); - setApplicationName("uhost"); - setApplicationVersion("1.0"); - - parseCommandLine(); - - loadSettings(); - - _engine = new Engine(*this); - - _mainWindow = new MainWindow(*this); - _mainWindow->show(); - - _engine->setParentWindow(_mainWindow->getEmbedWindowId()); - - /* - * This is here JUST because macOS and QT don't process command lines properly - * and I'm not sure why yet. - */ - if (getenv("CLAP_HOST_FORCE_PLUGIN")) { - qWarning() << "Warning: Loading plugin from ENV, not command line"; - _pluginPath = getenv("CLAP_HOST_FORCE_PLUGIN"); - _pluginIndex = 0; - } - - if (_engine->loadPlugin(_pluginPath, _pluginIndex)) - _engine->start(); -} - -Application::~Application() { - saveSettings(); - - delete _engine; - _engine = nullptr; - - delete _mainWindow; - _mainWindow = nullptr; - - delete _settings; - _settings = nullptr; -} - -void Application::parseCommandLine() { - QCommandLineParser parser; - - QCommandLineOption pluginOpt(QStringList() << "p" - << "plugin", - tr("path to the plugin"), - tr("path")); - QCommandLineOption pluginIndexOpt(QStringList() << "i" - << "plugin-index", - tr("index of the plugin to create"), - tr("plugin-index"), - "0"); - - parser.setApplicationDescription("clap standalone host"); - parser.addHelpOption(); - parser.addVersionOption(); - parser.addOption(pluginOpt); - parser.addOption(pluginIndexOpt); - - parser.process(*this); - - _pluginPath = parser.value(pluginOpt); - _pluginIndex = parser.value(pluginIndexOpt).toInt(); -} - -void Application::loadSettings() { - QSettings s; - _settings->load(s); -} - -void Application::saveSettings() const { - QSettings s; - _settings->save(s); -} - -void Application::restartEngine() { - _engine->stop(); - _engine->start(); -} diff --git a/examples/host/application.hh b/examples/host/application.hh @@ -1,46 +0,0 @@ -#pragma once - -#include <QApplication> - -#include "engine.hh" - -class MainWindow; -class Settings; -class Engine; -class SpectrumAnalyzer; -class OscilloscopeAnalyzer; -class AudioRecorder; - -class Application : public QApplication { - Q_OBJECT - -public: - Application(int argc, char **argv); - ~Application(); - - Settings &settings() { return *_settings; } - - void parseCommandLine(); - - void loadSettings(); - void saveSettings() const; - - MainWindow *mainWindow() const { return _mainWindow; } - - static Application &instance() { return *_instance; } - - Engine *engine() { return _engine; } - -public slots: - void restartEngine(); - -private: - static Application *_instance; - - Settings * _settings = nullptr; - MainWindow *_mainWindow = nullptr; - Engine * _engine = nullptr; - - QString _pluginPath; - int _pluginIndex = 0; -}; diff --git a/examples/host/audio-settings-widget.cc b/examples/host/audio-settings-widget.cc @@ -1,120 +0,0 @@ -#include <iostream> - -#include <QComboBox> -#include <QGridLayout> -#include <QGroupBox> -#include <QLabel> -#include <QVBoxLayout> - -#include <portaudio.h> - -#include "audio-settings-widget.hh" -#include "audio-settings.hh" - -static const std::vector<int> SAMPLE_RATES = { - 44100, - 48000, - 88200, - 96000, - 176400, - 192000, -}; - -static const std::vector<int> BUFFER_SIZES = {32, 48, 64, 96, 128, 192, 256, 384, 512}; - -AudioSettingsWidget::AudioSettingsWidget(AudioSettings &audioSettings) - : _audioSettings(audioSettings) { - /* devices */ - auto deviceComboBox = new QComboBox(this); - auto deviceCount = Pa_GetDeviceCount(); - bool deviceFound = false; - - for (int i = 0; i < deviceCount; ++i) { - auto deviceInfo = Pa_GetDeviceInfo(i); - deviceComboBox->addItem(deviceInfo->name); - - if (!deviceFound && _audioSettings.deviceReference()._index == i && - _audioSettings.deviceReference()._name == deviceInfo->name) { - deviceComboBox->setCurrentIndex(i); - deviceFound = true; - selectedDeviceChanged(i); - } - } - - // try to find the device just by its name. - for (int i = 0; !deviceFound && i < deviceCount; ++i) { - auto deviceInfo = Pa_GetDeviceInfo(i); - if (_audioSettings.deviceReference()._name == deviceInfo->name) { - deviceComboBox->setCurrentIndex(i); - deviceFound = true; - selectedDeviceChanged(i); - } - } - - connect( - deviceComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(selectedDeviceChanged(int))); - - /* sample rate */ - _sampleRateWidget = new QComboBox(this); - for (size_t i = 0; i < SAMPLE_RATES.size(); ++i) { - int sr = SAMPLE_RATES[i]; - _sampleRateWidget->addItem(QString::number(sr)); - if (sr == _audioSettings.sampleRate()) { - _sampleRateWidget->setCurrentIndex(i); - selectedSampleRateChanged(i); - } - } - connect(_sampleRateWidget, - SIGNAL(currentIndexChanged(int)), - this, - SLOT(selectedSampleRateChanged(int))); - - /* buffer size */ - _bufferSizeWidget = new QComboBox(this); - for (size_t i = 0; i < BUFFER_SIZES.size(); ++i) { - int bs = BUFFER_SIZES[i]; - _bufferSizeWidget->addItem(QString::number(bs)); - if (bs == _audioSettings.bufferSize()) { - _bufferSizeWidget->setCurrentIndex(i); - selectedBufferSizeChanged(i); - } - } - connect(_bufferSizeWidget, - SIGNAL(currentIndexChanged(int)), - this, - SLOT(selectedBufferSizeChanged(int))); - - auto layout = new QGridLayout(this); - layout->addWidget(new QLabel(tr("Device")), 0, 0); - layout->addWidget(new QLabel(tr("Sample rate")), 1, 0); - layout->addWidget(new QLabel(tr("Buffer size")), 2, 0); - - layout->addWidget(deviceComboBox, 0, 1); - layout->addWidget(_sampleRateWidget, 1, 1); - layout->addWidget(_bufferSizeWidget, 2, 1); - - QGroupBox *groupBox = new QGroupBox(this); - groupBox->setLayout(layout); - groupBox->setTitle(tr("Audio")); - - QLayout *groupLayout = new QVBoxLayout(); - groupLayout->addWidget(groupBox); - setLayout(groupLayout); -} - -void AudioSettingsWidget::selectedDeviceChanged(int index) { - auto deviceInfo = Pa_GetDeviceInfo(index); - - DeviceReference ref; - ref._index = index; - ref._name = deviceInfo->name; - _audioSettings.setDeviceReference(ref); -} - -void AudioSettingsWidget::selectedSampleRateChanged(int index) { - _audioSettings.setSampleRate(_sampleRateWidget->itemText(index).toInt()); -} - -void AudioSettingsWidget::selectedBufferSizeChanged(int index) { - _audioSettings.setBufferSize(_bufferSizeWidget->itemText(index).toInt()); -} diff --git a/examples/host/audio-settings-widget.hh b/examples/host/audio-settings-widget.hh @@ -1,24 +0,0 @@ -#pragma once - -#include <QWidget> - -class AudioSettings; -class QComboBox; - -class AudioSettingsWidget : public QWidget { - Q_OBJECT -public: - explicit AudioSettingsWidget(AudioSettings &audioSettings); - -signals: - -public slots: - void selectedDeviceChanged(int index); - void selectedSampleRateChanged(int index); - void selectedBufferSizeChanged(int index); - -private: - AudioSettings &_audioSettings; - QComboBox * _sampleRateWidget; - QComboBox * _bufferSizeWidget; -}; diff --git a/examples/host/audio-settings.cc b/examples/host/audio-settings.cc @@ -1,24 +0,0 @@ -#include <QSettings> - -#include "audio-settings.hh" - -static const char SAMPLE_RATE_KEY[] = "Audio/SampleRate"; -static const char BUFFER_SIZE_KEY[] = "Audio/BufferSize"; -static const char DEVICE_NAME_KEY[] = "Audio/DeviceName"; -static const char DEVICE_INDEX_KEY[] = "Audio/DeviceIndex"; - -AudioSettings::AudioSettings() {} - -void AudioSettings::load(QSettings &settings) { - _deviceReference._name = settings.value(DEVICE_NAME_KEY).toString(); - _deviceReference._index = settings.value(DEVICE_INDEX_KEY).toInt(); - _sampleRate = settings.value(SAMPLE_RATE_KEY, 44100).toInt(); - _bufferSize = settings.value(BUFFER_SIZE_KEY, 256).toInt(); -} - -void AudioSettings::save(QSettings &settings) const { - settings.setValue(SAMPLE_RATE_KEY, _sampleRate); - settings.setValue(BUFFER_SIZE_KEY, _bufferSize); - settings.setValue(DEVICE_NAME_KEY, _deviceReference._name); - settings.setValue(DEVICE_INDEX_KEY, _deviceReference._index); -} diff --git a/examples/host/audio-settings.hh b/examples/host/audio-settings.hh @@ -1,27 +0,0 @@ -#pragma once - -#include "device-reference.hh" - -class QSettings; - -class AudioSettings { -public: - AudioSettings(); - - void load(QSettings &settings); - void save(QSettings &settings) const; - - int sampleRate() const { return _sampleRate; } - void setSampleRate(int sampleRate) { _sampleRate = sampleRate; } - - void setDeviceReference(DeviceReference dr) { _deviceReference = dr; } - const DeviceReference &deviceReference() const { return _deviceReference; } - - int bufferSize() const { return _bufferSize; } - void setBufferSize(int bufferSize) { _bufferSize = bufferSize; } - -private: - DeviceReference _deviceReference; - int _sampleRate = 44100; - int _bufferSize = 128; -}; diff --git a/examples/host/device-reference.hh b/examples/host/device-reference.hh @@ -1,8 +0,0 @@ -#pragma once - -#include <QString> - -struct DeviceReference { - QString _name = "(noname)"; - int _index = 0; -}; diff --git a/examples/host/engine.cc b/examples/host/engine.cc @@ -1,252 +0,0 @@ -#include <cassert> -#include <cstdlib> -#include <iostream> -#include <thread> - -#include <QApplication> -#include <QDebug> -#include <QFile> -#include <QThread> -#include <QtGlobal> - -#include "application.hh" -#include "engine.hh" -#include "main-window.hh" -#include "plugin-host.hh" -#include "settings.hh" - -enum MidiStatus { - MIDI_STATUS_NOTE_OFF = 0x8, - MIDI_STATUS_NOTE_ON = 0x9, - MIDI_STATUS_NOTE_AT = 0xA, // after touch - MIDI_STATUS_CC = 0xB, // control change - MIDI_STATUS_PGM_CHANGE = 0xC, - MIDI_STATUS_CHANNEL_AT = 0xD, // after touch - MIDI_STATUS_PITCH_BEND = 0xE, -}; - -Engine::Engine(Application &application) - : QObject(&application), _application(application), _settings(application.settings()), - _idleTimer(this) { - _pluginHost.reset(new PluginHost(*this)); - - connect(&_idleTimer, &QTimer::timeout, this, QOverload<>::of(&Engine::callPluginIdle)); - _idleTimer.start(1000 / 30); -} - -Engine::~Engine() { - std::clog << " ####### STOPING ENGINE #########" << std::endl; - stop(); - unloadPlugin(); - std::clog << " ####### ENGINE STOPPED #########" << std::endl; -} - -void Engine::start() { - assert(!_audio); - assert(_state == kStateStopped); - - auto & as = _settings.audioSettings(); - const int bufferSize = 4 * 2 * as.bufferSize(); - - _inputs[0] = (float *)calloc(1, bufferSize); - _inputs[1] = (float *)calloc(1, bufferSize); - _outputs[0] = (float *)calloc(1, bufferSize); - _outputs[1] = (float *)calloc(1, bufferSize); - - _pluginHost->setPorts(2, _inputs, 2, _outputs); - - /* midi */ - PmError midi_err = Pm_OpenInput( - &_midi, _settings.midiSettings().deviceReference()._index, nullptr, 512, nullptr, nullptr); - if (midi_err != pmNoError) { - _midi = nullptr; - } - - _pluginHost->activate(as.sampleRate()); - - /* audio */ - auto deviceInfo = Pa_GetDeviceInfo(as.deviceReference()._index); - - PaStreamParameters params; - params.channelCount = 2; - params.device = as.deviceReference()._index; - params.hostApiSpecificStreamInfo = nullptr; - params.sampleFormat = paFloat32; - params.suggestedLatency = 0; - - _state = kStateRunning; - _nframes = as.bufferSize(); - PaError err = Pa_OpenStream(&_audio, - deviceInfo->maxInputChannels >= 2 ? ¶ms : nullptr, - ¶ms, - as.sampleRate(), - as.bufferSize(), - paClipOff | paDitherOff, - &Engine::audioCallback, - this); - if (err != paNoError) { - qWarning() << tr("Failed to initialize PortAudio: ") << Pa_GetErrorText(err); - stop(); - return; - } - - err = Pa_StartStream(_audio); -} - -void Engine::stop() { - _pluginHost->deactivate(); - - if (_state == kStateRunning) - _state = kStateStopping; - - if (_audio) { - Pa_StopStream(_audio); - Pa_CloseStream(_audio); - _audio = nullptr; - } - - if (_midi) { - Pm_Close(_midi); - _midi = nullptr; - } - - _state = kStateStopped; -} - -int Engine::audioCallback(const void * input, - void * output, - unsigned long frameCount, - const PaStreamCallbackTimeInfo * /*timeInfo*/, - PaStreamCallbackFlags /*statusFlags*/, - void *userData) { - Engine *const thiz = (Engine *)userData; - const float *const in = (const float *)input; - float *const out = (float *)output; - - assert(thiz->_inputs[0] != nullptr); - assert(thiz->_inputs[1] != nullptr); - assert(thiz->_outputs[0] != nullptr); - assert(thiz->_outputs[1] != nullptr); - assert(frameCount == thiz->_nframes); - - // copy input - if (in) { - for (int i = 0; i < thiz->_nframes; ++i) { - thiz->_inputs[0][i] = in[2 * i]; - thiz->_inputs[1][i] = in[2 * i + 1]; - } - } - - thiz->_pluginHost->processBegin(frameCount); - - MidiSettings &ms = thiz->_settings.midiSettings(); - - if (thiz->_midi) { - PmEvent evBuffer[512]; - int numRead = Pm_Read(thiz->_midi, evBuffer, sizeof(evBuffer) / sizeof(evBuffer[0])); - - const PtTimestamp currentTime = Pt_Time(); - - PmEvent *ev = evBuffer; - for (int i = 0; i < numRead; ++i) { - uint8_t eventType = Pm_MessageStatus(ev->message) >> 4; - uint8_t channel = Pm_MessageStatus(ev->message) & 0xf; - uint8_t data1 = Pm_MessageData1(ev->message); - uint8_t data2 = Pm_MessageData2(ev->message); - - int32_t deltaMs = currentTime - ev->timestamp; - int32_t deltaSample = (deltaMs * thiz->_sampleRate) / 1000; - - if (deltaSample >= thiz->_nframes) - deltaSample = thiz->_nframes - 1; - - int32_t sampleOffset = thiz->_nframes - deltaSample; - - switch (eventType) { - case MIDI_STATUS_NOTE_ON: - thiz->_pluginHost->processNoteOn(sampleOffset, channel, data1, data2); - ++ev; - break; - - case MIDI_STATUS_NOTE_OFF: - thiz->_pluginHost->processNoteOff(sampleOffset, channel, data1, data2); - ++ev; - break; - - case MIDI_STATUS_CC: - thiz->_pluginHost->processCC(sampleOffset, channel, data1, data2); - ++ev; - break; - - case MIDI_STATUS_NOTE_AT: - std::cerr << "Note AT key: " << (int)data1 << ", pres: " << (int)data2 << std::endl; - thiz->_pluginHost->processNoteAt(sampleOffset, channel, data1, data2); - ++ev; - break; - - case MIDI_STATUS_CHANNEL_AT: - ++ev; - std::cerr << "Channel after touch" << std::endl; - break; - - case MIDI_STATUS_PITCH_BEND: - thiz->_pluginHost->processPitchBend(sampleOffset, channel, (data2 << 7) | data1); - ++ev; - break; - - default: - std::cerr << "unknown event type: " << (int)eventType << std::endl; - ++ev; - break; - } - } - } - - thiz->_pluginHost->process(); - - // copy output - for (int i = 0; i < thiz->_nframes; ++i) { - out[2 * i] = thiz->_outputs[0][i]; - out[2 * i + 1] = thiz->_outputs[1][i]; - } - - thiz->_steadyTime += frameCount; - - switch (thiz->_state) { - case kStateRunning: - return paContinue; - case kStateStopping: - thiz->_state = kStateStopped; - return paComplete; - default: - assert(false && "unreachable"); - return paAbort; - } -} - -bool Engine::loadPlugin(const QString &path, int plugin_index) { - if (!_pluginHost->load(path, plugin_index)) - return false; - - _pluginHost->setParentWindow(_parentWindow); - return true; -} - -void Engine::unloadPlugin() { - _pluginHost->unload(); - - free(_inputs[0]); - free(_inputs[1]); - free(_outputs[0]); - free(_outputs[1]); - - _inputs[0] = nullptr; - _inputs[1] = nullptr; - _outputs[0] = nullptr; - _outputs[1] = nullptr; -} - -void Engine::callPluginIdle() { - if (_pluginHost) - _pluginHost->idle(); -} diff --git a/examples/host/engine.hh b/examples/host/engine.hh @@ -1,85 +0,0 @@ -#pragma once - -#include <array> -#include <memory> - -#include <QLibrary> -#include <QString> -#include <QTimer> -#include <QWidget> - -#include <portaudio.h> -#include <portmidi.h> -#include <porttime.h> - -class Application; -class Settings; -class PluginHost; - -class Engine : public QObject { - Q_OBJECT - -public: - Engine(Application &application); - ~Engine(); - - enum State { - kStateStopped, - kStateRunning, - kStateStopping, - }; - - void setParentWindow(WId parentWindow) { _parentWindow = parentWindow; } - void start(); - void stop(); - - bool loadPlugin(const QString &path, int plugin_index); - void unloadPlugin(); - - /* send events to the plugin from GUI */ - void setProgram(int8_t program, int8_t bank_msb, int8_t bank_lsb); - void loadMidiFile(const QString &path); - - bool isRunning() const noexcept { return _state == kStateRunning; } - int sampleRate() const noexcept { return _sampleRate; } - - PluginHost &pluginHost() const { return *_pluginHost; } - -public: - void callPluginIdle(); - -private: - friend class AudioPlugin; - friend class PluginHost; - friend class Vst3Plugin; - - static int audioCallback(const void * input, - void * output, - unsigned long frameCount, - const PaStreamCallbackTimeInfo *timeInfo, - PaStreamCallbackFlags statusFlags, - void * userData); - - Application &_application; - Settings & _settings; - WId _parentWindow; - - State _state = kStateStopped; - - /* audio & midi streams */ - PaStream *_audio = nullptr; - PmStream *_midi = nullptr; - - /* engine context */ - int64_t _steadyTime = 0; - int32_t _sampleRate = 44100; - int32_t _nframes = 0; - - /* audio buffers */ - float *_inputs[2] = {nullptr, nullptr}; - float *_outputs[2] = {nullptr, nullptr}; - - std::unique_ptr<PluginHost> _pluginHost; - - QTimer _idleTimer; -}; diff --git a/examples/host/main-window.cc b/examples/host/main-window.cc @@ -1,141 +0,0 @@ -#include <iostream> - -#include <QCheckBox> -#include <QDoubleSpinBox> -#include <QFileDialog> -#include <QLabel> -#include <QLineEdit> -#include <QMenuBar> -#include <QToolBar> -#include <QWindow> - -#include "application.hh" -#include "engine.hh" -#include "main-window.hh" -#include "plugin-host.hh" -#include "plugin-parameters-widget.hh" -#include "plugin-quick-controls-widget.hh" -#include "settings-dialog.hh" -#include "settings.hh" - -MainWindow::MainWindow(Application &app) - : QMainWindow(nullptr), _application(app), - _settingsDialog(new SettingsDialog(_application.settings(), this)), - _pluginViewWindow(new QWindow()), - _pluginViewWidget(QWidget::createWindowContainer(_pluginViewWindow)) { - - createMenu(); - - setCentralWidget(_pluginViewWidget); - _pluginViewWidget->show(); - _pluginViewWidget->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); - setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); - - connect(_settingsDialog, SIGNAL(accepted()), &_application, SLOT(restartEngine())); - - auto &pluginHost = app.engine()->pluginHost(); - - _pluginParametersWindow = new QMainWindow(this); - _pluginParametersWidget = new PluginParametersWidget(_pluginParametersWindow, pluginHost); - _pluginParametersWindow->setCentralWidget(_pluginParametersWidget); - - _pluginQuickControlsWindow = new QMainWindow(this); - _pluginQuickControlsWidget = - new PluginQuickControlsWidget(_pluginQuickControlsWindow, pluginHost); - _pluginQuickControlsWindow->setCentralWidget(_pluginQuickControlsWidget); -} - -MainWindow::~MainWindow() {} - -void MainWindow::createMenu() { - QMenuBar *menuBar = new QMenuBar(this); - setMenuBar(menuBar); - - QMenu *fileMenu = menuBar->addMenu(tr("File")); - fileMenu->addAction(tr("Load plugin")); - connect(fileMenu->addAction(tr("Load Native Plugin Preset")), - &QAction::triggered, - this, - &MainWindow::loadNativePluginPreset); - fileMenu->addSeparator(); - connect(fileMenu->addAction(tr("Settings")), - &QAction::triggered, - this, - &MainWindow::showSettingsDialog); - fileMenu->addSeparator(); - connect(fileMenu->addAction(tr("Quit")), - &QAction::triggered, - QApplication::instance(), - &Application::quit); - - auto windowsMenu = menuBar->addMenu("Windows"); - connect(windowsMenu->addAction(tr("Show Parameters")), - &QAction::triggered, - this, - &MainWindow::showPluginParametersWindow); - connect(windowsMenu->addAction(tr("Show Quick Controls")), - &QAction::triggered, - this, - &MainWindow::showPluginQuickControlsWindow); - menuBar->addSeparator(); - connect(windowsMenu->addAction(tr("Toggle Plugin Window Visibility")), - &QAction::triggered, - this, - &MainWindow::togglePluginWindowVisibility); - connect(windowsMenu->addAction(tr("Recreate Plugin Window")), - &QAction::triggered, - this, - &MainWindow::recreatePluginWindow); - connect(windowsMenu->addAction(tr("Scale Plugin Window")), - &QAction::triggered, - this, - &MainWindow::scalePluginWindow); - - QMenu *helpMenu = menuBar->addMenu(tr("Help")); - helpMenu->addAction(tr("Manual")); - helpMenu->addAction(tr("About")); -} - -void MainWindow::showSettingsDialog() { - int result = _settingsDialog->exec(); - if (result == QDialog::Accepted) - _application.restartEngine(); -} - -void MainWindow::showPluginParametersWindow() { _pluginParametersWindow->show(); } -void MainWindow::showPluginQuickControlsWindow() { _pluginQuickControlsWindow->show(); } - -WId MainWindow::getEmbedWindowId() { return _pluginViewWidget->winId(); } - -void MainWindow::resizePluginView(int width, int height) { - _pluginViewWidget->setMinimumSize(width, height); - _pluginViewWidget->setMaximumSize(width, height); - _pluginViewWidget->show(); - adjustSize(); -} - -void MainWindow::loadNativePluginPreset() -{ - auto file = QFileDialog::getOpenFileName(this, tr("Load Plugin Native Preset")); - if (file.isEmpty()) - return; - - _application.engine()->pluginHost().loadNativePluginPreset(file.toStdString()); -} - -void MainWindow::togglePluginWindowVisibility() -{ - bool isVisible = !_pluginViewWidget->isVisible(); - _pluginViewWidget->setVisible(isVisible); - _application.engine()->pluginHost().setPluginWindowVisibility(isVisible); -} - -void MainWindow::recreatePluginWindow() -{ - _application.engine()->pluginHost().setParentWindow(getEmbedWindowId()); -} - -void MainWindow::scalePluginWindow() -{ - // TODO -} -\ No newline at end of file diff --git a/examples/host/main-window.hh b/examples/host/main-window.hh @@ -1,43 +0,0 @@ -#pragma once - -#include <QMainWindow> - -class Application; -class SettingsDialog; -class PluginParametersWidget; -class PluginQuickControlsWidget; - -class MainWindow : public QMainWindow { - Q_OBJECT - -public: - explicit MainWindow(Application &app); - ~MainWindow(); - - WId getEmbedWindowId(); - -public: - void loadNativePluginPreset(); - void showSettingsDialog(); - void showPluginParametersWindow(); - void showPluginQuickControlsWindow(); - void resizePluginView(int width, int height); - -private: - void createMenu(); - - void togglePluginWindowVisibility(); - void recreatePluginWindow(); - void scalePluginWindow(); - - Application & _application; - SettingsDialog *_settingsDialog = nullptr; - QWindow * _pluginViewWindow = nullptr; - QWidget * _pluginViewWidget = nullptr; - - QMainWindow * _pluginParametersWindow = nullptr; - PluginParametersWidget *_pluginParametersWidget = nullptr; - - QMainWindow * _pluginQuickControlsWindow = nullptr; - PluginQuickControlsWidget *_pluginQuickControlsWidget = nullptr; -}; diff --git a/examples/host/main.cc b/examples/host/main.cc @@ -1,41 +0,0 @@ -#include <cstdio> - -#include <QApplication> - -#include <portaudio.h> -#include <portmidi.h> - -#include "application.hh" - -int main(int argc, char *argv[]) { - PtError pt_err = Pt_Start(1, nullptr, nullptr); - if (pt_err != ptNoError) { - fprintf(stderr, "Failed to initialize porttime\n"); - return 1; - } - - PaError pa_err = Pa_Initialize(); - if (pa_err != paNoError) { - fprintf(stderr, "Failed to initialize portaudio\n"); - return 1; - } - - PmError pm_err = Pm_Initialize(); - if (pm_err != pmNoError) { - fprintf(stderr, "Failed to initialize portmidi\n"); - return 1; - } - - int ret; - - { - Application app(argc, argv); - - ret = app.exec(); - } - - Pm_Terminate(); - Pa_Terminate(); - Pt_Stop(); - return ret; -} diff --git a/examples/host/midi-settings-widget.cc b/examples/host/midi-settings-widget.cc @@ -1,90 +0,0 @@ -#include <iostream> - -#include <QComboBox> -#include <QGroupBox> -#include <QVBoxLayout> - -#include <portmidi.h> - -#include "midi-settings-widget.hh" -#include "midi-settings.hh" - -MidiSettingsWidget::MidiSettingsWidget(MidiSettings &midiSettings) : _midiSettings(midiSettings) { - auto layout = new QVBoxLayout(this); - - auto deviceComboBox = new QComboBox; - bool deviceFound = false; - auto deviceCount = Pm_CountDevices(); - int inputIndex = 0; - - if (deviceCount <= 0) { - std::cerr << "warning: no midi device found!" << std::endl; - } - - for (int i = 0; i < deviceCount; ++i) { - auto deviceInfo = Pm_GetDeviceInfo(i); - if (!deviceInfo->input) - continue; - - deviceComboBox->addItem(deviceInfo->name); - - if (!deviceFound && _midiSettings.deviceReference()._index == i && - _midiSettings.deviceReference()._name == deviceInfo->name) { - deviceComboBox->setCurrentIndex(inputIndex); - deviceFound = true; - selectedDeviceChanged(inputIndex); - } - - ++inputIndex; - } - - // try to find the device just by its name. - inputIndex = 0; - for (int i = 0; !deviceFound && i < deviceCount; ++i) { - auto deviceInfo = Pm_GetDeviceInfo(i); - if (!deviceInfo->input) - continue; - - if (_midiSettings.deviceReference()._name == deviceInfo->name) { - deviceComboBox->setCurrentIndex(inputIndex); - deviceFound = true; - selectedDeviceChanged(inputIndex); - } - - ++inputIndex; - } - - connect( - deviceComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(selectedDeviceChanged(int))); - - layout->addWidget(deviceComboBox); - - QGroupBox *groupBox = new QGroupBox; - groupBox->setLayout(layout); - groupBox->setTitle(tr("MIDI")); - - QVBoxLayout *groupLayout = new QVBoxLayout; - groupLayout->addWidget(groupBox); - setLayout(groupLayout); -} - -void MidiSettingsWidget::selectedDeviceChanged(int index) { - int inputIndex = 0; - auto deviceCount = Pm_CountDevices(); - for (int i = 0; i < deviceCount; ++i) { - auto deviceInfo = Pm_GetDeviceInfo(i); - if (!deviceInfo->input) - continue; - - if (inputIndex != index) { - ++inputIndex; - continue; - } - - DeviceReference ref; - ref._index = i; - ref._name = deviceInfo->name; - _midiSettings.setDeviceReference(ref); - break; - } -} diff --git a/examples/host/midi-settings-widget.hh b/examples/host/midi-settings-widget.hh @@ -1,21 +0,0 @@ -#pragma once - -#include <vector> - -#include <QWidget> - -class MidiSettings; - -class MidiSettingsWidget : public QWidget { - Q_OBJECT -public: - explicit MidiSettingsWidget(MidiSettings &midiSettings); - -signals: - -public slots: - void selectedDeviceChanged(int index); - -private: - MidiSettings &_midiSettings; -}; diff --git a/examples/host/midi-settings.cc b/examples/host/midi-settings.cc @@ -1,20 +0,0 @@ -#include <QSettings> - -#include "midi-settings.hh" - -static const char DEVICE_NAME_KEY[] = "Midi/DeviceName"; -static const char DEVICE_INDEX_KEY[] = "Midi/DeviceIndex"; -static const char LATCH_KEY[] = "Midi/Latch"; -static const char ARP_KEY[] = "Midi/Arp"; - -MidiSettings::MidiSettings() {} - -void MidiSettings::load(QSettings &settings) { - _deviceReference._name = settings.value(DEVICE_NAME_KEY).toString(); - _deviceReference._index = settings.value(DEVICE_INDEX_KEY).toInt(); -} - -void MidiSettings::save(QSettings &settings) const { - settings.setValue(DEVICE_NAME_KEY, _deviceReference._name); - settings.setValue(DEVICE_INDEX_KEY, _deviceReference._index); -} diff --git a/examples/host/midi-settings.hh b/examples/host/midi-settings.hh @@ -1,19 +0,0 @@ -#pragma once - -#include "device-reference.hh" - -class QSettings; - -class MidiSettings { -public: - MidiSettings(); - - void load(QSettings &settings); - void save(QSettings &settings) const; - - const DeviceReference &deviceReference() const { return _deviceReference; } - void setDeviceReference(const DeviceReference &ref) { _deviceReference = ref; } - -private: - DeviceReference _deviceReference; -}; diff --git a/examples/host/plugin-host.cc b/examples/host/plugin-host.cc @@ -1,1214 +0,0 @@ -#include <exception> -#include <iostream> -#include <memory> -#include <sstream> -#include <stdexcept> -#include <string_view> -#include <unordered_set> - -#include <QDebug> - -#include "application.hh" -#include "engine.hh" -#include "main-window.hh" -#include "plugin-host.hh" - -#include "../common/reducing-param-queue.hxx" - -enum ThreadType { - Unknown, - MainThread, - AudioThread, - AudioThreadPool, -}; - -thread_local ThreadType g_thread_type = Unknown; - -PluginHost::PluginHost(Engine &engine) : QObject(&engine), _engine(engine) { - g_thread_type = MainThread; - - host_.host_data = this; - host_.clap_version = CLAP_VERSION; - host_.name = "Clap Test Host"; - host_.version = "0.1.0"; - host_.vendor = "clap"; - host_.url = "https://github.com/free-audio/clap"; - host_.get_extension = PluginHost::clapExtension; - host_.request_callback = PluginHost::clapRequestCallback; - host_.request_process = PluginHost::clapRequestProcess; - host_.request_restart = PluginHost::clapRequestRestart; - - initThreadPool(); -} - -PluginHost::~PluginHost() { - checkForMainThread(); - - terminateThreadPool(); -} - -void PluginHost::initThreadPool() { - checkForMainThread(); - - _threadPoolStop = false; - _threadPoolTaskIndex = 0; - auto N = QThread::idealThreadCount(); - _threadPool.resize(N); - for (int i = 0; i < N; ++i) { - _threadPool[i].reset(QThread::create(&PluginHost::threadPoolEntry, this)); - _threadPool[i]->start(QThread::HighestPriority); - } -} - -void PluginHost::terminateThreadPool() { - checkForMainThread(); - - _threadPoolStop = true; - _threadPoolSemaphoreProd.release(_threadPool.size()); - for (auto &thr : _threadPool) - if (thr) - thr->wait(); -} - -void PluginHost::threadPoolEntry() { - g_thread_type = AudioThreadPool; - while (true) { - _threadPoolSemaphoreProd.acquire(); - if (_threadPoolStop) - return; - - int taskIndex = _threadPoolTaskIndex++; - _pluginThreadPool->exec(_plugin, taskIndex); - _threadPoolSemaphoreDone.release(); - } -} - -bool PluginHost::load(const QString &path, int pluginIndex) { - checkForMainThread(); - - if (_library.isLoaded()) - unload(); - - _library.setFileName(path); - _library.setLoadHints(QLibrary::ResolveAllSymbolsHint | QLibrary::DeepBindHint); - if (!_library.load()) { - QString err = _library.errorString(); - qWarning() << "Failed to load plugin '" << path << "': " << err; - return false; - } - - _pluginEntry = - reinterpret_cast<const struct clap_plugin_entry *>(_library.resolve("clap_plugin_entry")); - if (!_pluginEntry) { - qWarning() << "Unable to resolve entry point 'clap_plugin_entry' in '" << path << "'"; - _library.unload(); - return false; - } - - _pluginEntry->init(path.toStdString().c_str()); - - auto count = _pluginEntry->get_plugin_count(); - if (pluginIndex > count) { - qWarning() << "plugin index greater than count :" << count; - return false; - } - - auto desc = _pluginEntry->get_plugin_descriptor(pluginIndex); - if (!desc) { - qWarning() << "no plugin descriptor"; - return false; - } - - if (!clap_version_is_compatible(desc->clap_version)) { - qWarning() << "Incompatible clap version: Plugin is: " << desc->clap_version.major << "." - << desc->clap_version.minor << "." << desc->clap_version.revision << " Host is " - << CLAP_VERSION.major << "." << CLAP_VERSION.minor << "." << CLAP_VERSION.revision; - return false; - } - - _plugin = _pluginEntry->create_plugin(&host_, desc->id); - if (!_plugin) { - qWarning() << "could not create the plugin with id: " << desc->id; - return false; - } - _plugin->init(_plugin); - - initPluginExtensions(); - scanParams(); - scanQuickControls(); - return true; -} - -void PluginHost::initPluginExtensions() { - checkForMainThread(); - - if (_pluginExtensionsAreInitialized) - return; - - initPluginExtension(_pluginParams, CLAP_EXT_PARAMS); - initPluginExtension(_pluginQuickControls, CLAP_EXT_QUICK_CONTROLS); - initPluginExtension(_pluginAudioPorts, CLAP_EXT_AUDIO_PORTS); - initPluginExtension(_pluginGui, CLAP_EXT_GUI); - initPluginExtension(_pluginGuiX11, CLAP_EXT_GUI_X11); - initPluginExtension(_pluginGuiWin32, CLAP_EXT_GUI_WIN32); - initPluginExtension(_pluginGuiCocoa, CLAP_EXT_GUI_COCOA); - initPluginExtension(_pluginGuiFreeStanding, CLAP_EXT_GUI_FREE_STANDING); - initPluginExtension(_pluginTimerSupport, CLAP_EXT_TIMER_SUPPORT); - initPluginExtension(_pluginFdSupport, CLAP_EXT_FD_SUPPORT); - initPluginExtension(_pluginThreadPool, CLAP_EXT_THREAD_POOL); - initPluginExtension(_pluginPresetLoad, CLAP_EXT_PRESET_LOAD); - initPluginExtension(_pluginState, CLAP_EXT_STATE); - - _pluginExtensionsAreInitialized = true; -} - -void PluginHost::unload() { - checkForMainThread(); - - if (!_library.isLoaded()) - return; - - if (_isGuiCreated) { - _pluginGui->destroy(_plugin); - _isGuiCreated = false; - _isGuiVisible = false; - } - - deactivate(); - - _plugin->destroy(_plugin); - _plugin = nullptr; - _pluginGui = nullptr; - _pluginGuiX11 = nullptr; - _pluginGuiCocoa = nullptr; - _pluginGuiWin32 = nullptr; - _pluginGuiFreeStanding = nullptr; - _pluginTimerSupport = nullptr; - _pluginFdSupport = nullptr; - _pluginThreadPool = nullptr; - _pluginPresetLoad = nullptr; - _pluginState = nullptr; - _pluginAudioPorts = nullptr; - _pluginParams = nullptr; - _pluginQuickControls = nullptr; - - _pluginEntry->deinit(); - _pluginEntry = nullptr; - - _library.unload(); -} - -bool PluginHost::canActivate() const { - checkForMainThread(); - - if (!_engine.isRunning()) - return false; - if (isPluginActive()) - return false; - if (_scheduleRestart) - return false; - return true; -} - -void PluginHost::activate(int32_t sample_rate) { - checkForMainThread(); - - assert(!isPluginActive()); - if (!_plugin->activate(_plugin, sample_rate)) { - setPluginState(InactiveWithError); - return; - } - - _scheduleProcess = true; - setPluginState(ActiveAndSleeping); -} - -void PluginHost::deactivate() { - checkForMainThread(); - - if (!isPluginActive()) - return; - - while (isPluginProcessing() || isPluginSleeping()) { - _scheduleDeactivate = true; - QThread::msleep(10); - } - _scheduleDeactivate = false; - - _plugin->deactivate(_plugin); - setPluginState(Inactive); -} - -void PluginHost::setPorts(int numInputs, float **inputs, int numOutputs, float **outputs) { - _audioIn.channel_count = numInputs; - _audioIn.data32 = inputs; - _audioIn.data64 = nullptr; - _audioIn.constant_mask = 0; - _audioIn.latency = 0; - - _audioOut.channel_count = numOutputs; - _audioOut.data32 = outputs; - _audioOut.data64 = nullptr; - _audioOut.constant_mask = 0; - _audioOut.latency = 0; -} - -void PluginHost::setParentWindow(WId parentWindow) { - checkForMainThread(); - - if (!canUsePluginGui()) - return; - - if (_isGuiCreated) { - _pluginGui->destroy(_plugin); - _isGuiCreated = false; - _isGuiVisible = false; - } - - if (!_pluginGui->create(_plugin)) { - qWarning() << "could not create the plugin gui"; - return; - } - - _isGuiCreated = true; - assert(_isGuiVisible == false); - - uint32_t width = 0; - uint32_t height = 0; - - if (!_pluginGui->get_size(_plugin, &width, &height)) { - qWarning() << "could not get the size of the plugin gui"; - _isGuiCreated = false; - _pluginGui->destroy(_plugin); - return; - } - - bool didAttach = false; - -#if defined(Q_OS_LINUX) - if (_pluginGuiX11) - didAttach = _pluginGuiX11->attach(_plugin, nullptr, parentWindow); -#elif defined(Q_OS_MACX) - if (_pluginGuiCocoa) - didAttach = _pluginGuiCocoa->attach(_plugin, (void *)parentWindow); -#elif defined(Q_OS_WIN32) - if (_pluginEmbedWin32) - didAttach = _pluginGuiWin32->attach(_plugin, parentWindow); -#endif - // else (_pluginGuiFreeStanding) - // didAttach = _pluginGuiFreeStanding->open(_plugin); - - if (!didAttach) { - qWarning() << "the plugin failed to attach its gui"; - _isGuiCreated = false; - _pluginGui->destroy(_plugin); - return; - } - - Application::instance().mainWindow()->resizePluginView(width, height); - - setPluginWindowVisibility(true); -} - -void PluginHost::setPluginWindowVisibility(bool isVisible) { - checkForMainThread(); - - if (!_isGuiCreated) - return; - - if (isVisible && !_isGuiVisible) { - _pluginGui->show(_plugin); - _isGuiVisible = true; - } else if (!isVisible && _isGuiVisible) { - _pluginGui->hide(_plugin); - _isGuiVisible = false; - } -} - -void PluginHost::clapRequestCallback(const clap_host *host) { - auto h = fromHost(host); - h->_scheduleMainThreadCallback = true; -} - -void PluginHost::clapRequestProcess(const clap_host *host) { - auto h = fromHost(host); - h->_scheduleProcess = true; -} - -void PluginHost::clapRequestRestart(const clap_host *host) { - auto h = fromHost(host); - h->_scheduleRestart = true; -} - -void PluginHost::clapLog(const clap_host *host, clap_log_severity severity, const char *msg) { - switch (severity) { - case CLAP_LOG_DEBUG: - qDebug() << msg; - break; - - case CLAP_LOG_INFO: - qInfo() << msg; - break; - - case CLAP_LOG_WARNING: - case CLAP_LOG_ERROR: - case CLAP_LOG_FATAL: - case CLAP_LOG_HOST_MISBEHAVING: - qWarning() << msg; - break; - } -} - -template <typename T> -void PluginHost::initPluginExtension(const T *&ext, const char *id) { - checkForMainThread(); - - if (!ext) - ext = static_cast<const T *>(_plugin->get_extension(_plugin, id)); -} - -const void *PluginHost::clapExtension(const clap_host *host, const char *extension) { - checkForMainThread(); - - auto h = fromHost(host); - - if (!strcmp(extension, CLAP_EXT_GUI)) - return &h->_hostGui; - if (!strcmp(extension, CLAP_EXT_LOG)) - return &h->_hostLog; - if (!strcmp(extension, CLAP_EXT_THREAD_CHECK)) - return &h->_hostThreadCheck; - if (!strcmp(extension, CLAP_EXT_TIMER_SUPPORT)) - return &h->_hostTimerSupport; - if (!strcmp(extension, CLAP_EXT_FD_SUPPORT)) - return &h->_hostFdSupport; - if (!strcmp(extension, CLAP_EXT_PARAMS)) - return &h->_hostParams; - if (!strcmp(extension, CLAP_EXT_QUICK_CONTROLS)) - return &h->_hostQuickControls; - if (!strcmp(extension, CLAP_EXT_STATE)) - return &h->_hostState; - return nullptr; -} - -PluginHost *PluginHost::fromHost(const clap_host *host) { - if (!host) - throw std::invalid_argument("Passed a null host pointer"); - - auto h = static_cast<PluginHost *>(host->host_data); - if (!h) - throw std::invalid_argument("Passed an invalid host pointer because the host_data is null"); - - if (!h->_plugin) - throw std::logic_error("The plugin can't query for extensions during the create method. Wait " - "for clap_plugin.init() call."); - - return h; -} - -bool PluginHost::clapIsMainThread(const clap_host *host) { return g_thread_type == MainThread; } - -bool PluginHost::clapIsAudioThread(const clap_host *host) { return g_thread_type == AudioThread; } - -void PluginHost::checkForMainThread() { - if (g_thread_type != MainThread) - throw std::logic_error("Requires Main Thread!"); -} - -void PluginHost::checkForAudioThread() { - if (g_thread_type != AudioThread) - throw std::logic_error("Requires Audio Thread!"); -} - -bool PluginHost::clapThreadPoolRequestExec(const clap_host *host, uint32_t num_tasks) { - checkForAudioThread(); - - auto h = fromHost(host); - if (!h->_pluginThreadPool || !h->_pluginThreadPool->exec) - throw std::logic_error("Called request_exec() without providing clap_plugin_thread_pool to " - "execute the job."); - - Q_ASSERT(!h->_threadPoolStop); - Q_ASSERT(!h->_threadPool.empty()); - h->_threadPoolTaskIndex = 0; - h->_threadPoolSemaphoreProd.release(num_tasks); - h->_threadPoolSemaphoreDone.acquire(num_tasks); - return true; -} - -bool PluginHost::clapRegisterTimer(const clap_host *host, uint32_t period_ms, clap_id *timer_id) { - checkForMainThread(); - - auto h = fromHost(host); - h->initPluginExtensions(); - if (!h->_pluginTimerSupport || !h->_pluginTimerSupport->on_timer) - throw std::logic_error( - "Called register_timer() without providing clap_plugin_timer_support.on_timer() to " - "receive the timer event."); - - auto id = h->_nextTimerId++; - *timer_id = id; - auto timer = std::make_unique<QTimer>(); - - QObject::connect(timer.get(), &QTimer::timeout, [h, id] { - checkForMainThread(); - h->_pluginTimerSupport->on_timer(h->_plugin, id); - }); - - auto t = timer.get(); - h->_timers.insert_or_assign(*timer_id, std::move(timer)); - t->start(period_ms); - return true; -} - -bool PluginHost::clapUnregisterTimer(const clap_host *host, clap_id timer_id) { - checkForMainThread(); - - auto h = fromHost(host); - if (!h->_pluginTimerSupport || !h->_pluginTimerSupport->on_timer) - throw std::logic_error( - "Called unregister_timer() without providing clap_plugin_timer_support.on_timer() to " - "receive the timer event."); - - auto it = h->_timers.find(timer_id); - if (it == h->_timers.end()) - throw std::logic_error("Called unregister_timer() for a timer_id that was not registered."); - - h->_timers.erase(it); - return true; -} - -bool PluginHost::clapRegisterFd(const clap_host *host, clap_fd fd, uint32_t flags) { - checkForMainThread(); - - auto h = fromHost(host); - h->initPluginExtensions(); - if (!h->_pluginFdSupport || !h->_pluginFdSupport->on_fd) - throw std::logic_error("Called register_fd() without providing clap_plugin_fd_support to " - "receive the fd event."); - - auto it = h->_fds.find(fd); - if (it != h->_fds.end()) - throw std::logic_error( - "Called register_fd() for a fd that was already registered, use modify_fd() instead."); - - h->_fds.insert_or_assign(fd, std::make_unique<Notifiers>()); - h->eventLoopSetFdNotifierFlags(fd, flags); - return true; -} - -bool PluginHost::clapModifyFd(const clap_host *host, clap_fd fd, uint32_t flags) { - checkForMainThread(); - - auto h = fromHost(host); - if (!h->_pluginFdSupport || !h->_pluginFdSupport->on_fd) - throw std::logic_error("Called modify_fd() without providing clap_plugin_fd_support to " - "receive the timer event."); - - auto it = h->_fds.find(fd); - if (it == h->_fds.end()) - throw std::logic_error( - "Called modify_fd() for a fd that was not registered, use register_fd() instead."); - - h->_fds.insert_or_assign(fd, std::make_unique<Notifiers>()); - h->eventLoopSetFdNotifierFlags(fd, flags); - return true; -} - -bool PluginHost::clapUnregisterFd(const clap_host *host, clap_fd fd) { - checkForMainThread(); - - auto h = fromHost(host); - if (!h->_pluginFdSupport || !h->_pluginFdSupport->on_fd) - throw std::logic_error("Called unregister_fd() without providing clap_plugin_fd_support to " - "receive the fd event."); - - auto it = h->_fds.find(fd); - if (it == h->_fds.end()) - throw std::logic_error("Called unregister_fd() for a fd that was not registered."); - - h->_fds.erase(it); - return true; -} - -void PluginHost::eventLoopSetFdNotifierFlags(clap_fd fd, uint32_t flags) { - checkForMainThread(); - - auto it = _fds.find(fd); - Q_ASSERT(it != _fds.end()); - - if (flags & CLAP_FD_READ) { - if (!it->second->rd) { - it->second->rd.reset(new QSocketNotifier(fd, QSocketNotifier::Read)); - QObject::connect(it->second->rd.get(), &QSocketNotifier::activated, [this, fd] { - checkForMainThread(); - _pluginFdSupport->on_fd(this->_plugin, fd, CLAP_FD_READ); - }); - } - it->second->rd->setEnabled(true); - } else if (it->second->rd) - it->second->rd->setEnabled(false); - - if (flags & CLAP_FD_WRITE) { - if (!it->second->wr) { - it->second->wr.reset(new QSocketNotifier(fd, QSocketNotifier::Write)); - QObject::connect(it->second->wr.get(), &QSocketNotifier::activated, [this, fd] { - checkForMainThread(); - _pluginFdSupport->on_fd(this->_plugin, fd, CLAP_FD_WRITE); - }); - } - it->second->wr->setEnabled(true); - } else if (it->second->wr) - it->second->wr->setEnabled(false); - - if (flags & CLAP_FD_ERROR) { - if (!it->second->err) { - it->second->err.reset(new QSocketNotifier(fd, QSocketNotifier::Exception)); - QObject::connect(it->second->err.get(), &QSocketNotifier::activated, [this, fd] { - checkForMainThread(); - _pluginFdSupport->on_fd(this->_plugin, fd, CLAP_FD_ERROR); - }); - } - it->second->err->setEnabled(true); - } else if (it->second->err) - it->second->err->setEnabled(false); -} - -bool PluginHost::clapGuiResize(const clap_host *host, uint32_t width, uint32_t height) { - checkForMainThread(); - - Application::instance().mainWindow()->resizePluginView(width, height); - return true; -} - -void PluginHost::processBegin(int nframes) { - g_thread_type = AudioThread; - - _process.frames_count = nframes; - _process.steady_time = _engine._steadyTime; -} - -void PluginHost::processEnd(int nframes) { - g_thread_type = Unknown; - - _process.frames_count = nframes; - _process.steady_time = _engine._steadyTime; -} - -void PluginHost::processNoteOn(int sampleOffset, int channel, int key, int velocity) { - checkForAudioThread(); - - clap_event ev; - ev.type = CLAP_EVENT_NOTE_ON; - ev.time = sampleOffset; - ev.note.key = key; - ev.note.channel = channel; - ev.note.velocity = velocity / 127.0; - - _evIn.push_back(ev); -} - -void PluginHost::processNoteOff(int sampleOffset, int channel, int key, int velocity) { - checkForAudioThread(); - - clap_event ev; - ev.type = CLAP_EVENT_NOTE_OFF; - ev.time = sampleOffset; - ev.note.key = key; - ev.note.channel = channel; - ev.note.velocity = velocity / 127.0; - - _evIn.push_back(ev); -} - -void PluginHost::processNoteAt(int sampleOffset, int channel, int key, int pressure) { - checkForAudioThread(); - - // TODO -} - -void PluginHost::processPitchBend(int sampleOffset, int channel, int value) { - checkForAudioThread(); - - // TODO -} - -void PluginHost::processCC(int sampleOffset, int channel, int cc, int value) { - checkForAudioThread(); - - clap_event ev; - - ev.type = CLAP_EVENT_MIDI; - ev.time = sampleOffset; - ev.midi.data[0] = 0xB0 | channel; - ev.midi.data[1] = cc; - ev.midi.data[2] = value; - - _evIn.push_back(ev); -} - -static uint32_t clap_host_event_list_size(const struct clap_event_list *list) { - PluginHost::checkForAudioThread(); - - auto vec = reinterpret_cast<std::vector<clap_event> *>(list->ctx); - return vec->size(); -} - -const struct clap_event *clap_host_event_list_get(const struct clap_event_list *list, - uint32_t index) { - PluginHost::checkForAudioThread(); - - auto vec = reinterpret_cast<std::vector<clap_event> *>(list->ctx); - if (index < 0 || index >= vec->size()) - return nullptr; - return vec->data() + index; -} - -// Makes a copy of the event -void clap_host_event_list_push_back(const struct clap_event_list *list, - const struct clap_event *event) { - PluginHost::checkForAudioThread(); - - auto vec = reinterpret_cast<std::vector<clap_event> *>(list->ctx); - vec->push_back(*event); -} - -void PluginHost::process() { - checkForAudioThread(); - - // Can't process a plugin that is not active - if (!isPluginActive()) - return; - - // Do we want to deactivate the plugin? - if (_scheduleDeactivate) { - _scheduleDeactivate = false; - _plugin->stop_processing(_plugin); - setPluginState(ActiveAndReadyToDeactivate); - return; - } - - // We can't process a plugin which failed to start processing - if (_state == ActiveWithError) - return; - - _process.transport = nullptr; - - clap_event_list in_ev = { - &_evIn, clap_host_event_list_size, clap_host_event_list_get, clap_host_event_list_push_back}; - - clap_event_list out_ev = { - &_evOut, clap_host_event_list_size, clap_host_event_list_get, clap_host_event_list_push_back}; - - _process.in_events = &in_ev; - _process.out_events = &out_ev; - - _process.audio_inputs = &_audioIn; - _process.audio_inputs_count = 1; - _process.audio_outputs = &_audioOut; - _process.audio_outputs_count = 1; - - _evOut.clear(); - _appToEngineValueQueue.consume( - [this](clap_id param_id, const AppToEngineParamQueueValue &value) { - clap_event ev; - ev.time = 0; - ev.type = CLAP_EVENT_PARAM_VALUE; - ev.param_value.param_id = param_id; - ev.param_value.cookie = value.cookie; - ev.param_value.key = -1; - ev.param_value.channel = -1; - ev.param_value.value = value.value; - ev.param_value.flags = 0; - _evIn.push_back(ev); - }); - - _appToEngineModQueue.consume([this](clap_id param_id, const AppToEngineParamQueueValue &value) { - clap_event ev; - ev.time = 0; - ev.type = CLAP_EVENT_PARAM_MOD; - ev.param_mod.param_id = param_id; - ev.param_mod.cookie = value.cookie; - ev.param_mod.key = -1; - ev.param_mod.channel = -1; - ev.param_mod.amount = value.value; - _evIn.push_back(ev); - }); - - if (isPluginSleeping()) { - if (!_scheduleProcess && _evIn.empty()) - // The plugin is sleeping, there is no request to wake it up and there are no events to - // process - return; - - _scheduleProcess = false; - if (!_plugin->start_processing(_plugin)) { - // the plugin failed to start processing - setPluginState(ActiveWithError); - return; - } - - setPluginState(ActiveAndProcessing); - } - - int32_t status = CLAP_PROCESS_SLEEP; - if (_plugin && _plugin->process && isPluginProcessing()) - status = _plugin->process(_plugin, &_process); - - for (auto &ev : _evOut) { - switch (ev.type) { - case CLAP_EVENT_PARAM_VALUE: { - bool &isAdj = _isAdjusting[ev.param_value.param_id]; - - if (ev.param_value.flags & CLAP_EVENT_PARAM_BEGIN_ADJUST) { - if (isAdj) - throw std::logic_error("The plugin sent BEGIN_ADJUST twice"); - isAdj = true; - } - - if (ev.param_value.flags & CLAP_EVENT_PARAM_END_ADJUST) { - if (!isAdj) - throw std::logic_error( - "The plugin sent END_ADJUST without a preceding BEGIN_ADJUST"); - isAdj = false; - } - - _engineToAppValueQueue.set(ev.param_value.param_id, {ev.param_value.value, isAdj}); - break; - } - } - } - _evOut.clear(); - _evIn.clear(); - - _engineToAppValueQueue.producerDone(); - g_thread_type = Unknown; -} - -void PluginHost::idle() { - checkForMainThread(); - - // Try to send events to the audio engine - _appToEngineValueQueue.producerDone(); - _appToEngineModQueue.producerDone(); - - _engineToAppValueQueue.consume( - [this](clap_id param_id, const EngineToAppParamQueueValue &value) { - auto it = _params.find(param_id); - if (it == _params.end()) { - std::ostringstream msg; - msg << "Plugin produced a CLAP_EVENT_PARAM_SET with an unknown param_id: " << param_id; - throw std::invalid_argument(msg.str()); - } - - it->second->setValue(value.value); - it->second->setIsAdjusting(value.isAdjusting); - }); - - if (_scheduleMainThreadCallback) { - _scheduleMainThreadCallback = false; - _plugin->on_main_thread(_plugin); - } - - if (_scheduleRestart) { - deactivate(); - _scheduleRestart = false; - activate(_engine._sampleRate); - } -} - -PluginParam &PluginHost::checkValidParamId(const std::string_view &function, - const std::string_view ¶m_name, - clap_id param_id) { - checkForMainThread(); - - if (param_id == CLAP_INVALID_ID) { - std::ostringstream msg; - msg << "Plugin called " << function << " with " << param_name << " == CLAP_INVALID_ID"; - throw std::invalid_argument(msg.str()); - } - - auto it = _params.find(param_id); - if (it == _params.end()) { - std::ostringstream msg; - msg << "Plugin called " << function << " with an invalid " << param_name - << " == " << param_id; - throw std::invalid_argument(msg.str()); - } - - Q_ASSERT(it->first == param_id); - Q_ASSERT(it->second->info().id == param_id); - return *it->second; -} - -void PluginHost::checkValidParamValue(const PluginParam ¶m, double value) { - checkForMainThread(); - if (!param.isValueValid(value)) { - std::ostringstream msg; - msg << "Invalid value for param. "; - param.printInfo(msg); - msg << "; value: " << value; - // std::cerr << msg.str() << std::endl; - throw std::invalid_argument(msg.str()); - } -} - -void PluginHost::setParamValueByHost(PluginParam ¶m, double value) { - checkForMainThread(); - - param.setValue(value); - - _appToEngineValueQueue.set(param.info().id, {param.info().cookie, value}); - _appToEngineValueQueue.producerDone(); -} - -void PluginHost::setParamModulationByHost(PluginParam ¶m, double value) { - checkForMainThread(); - - param.setModulation(value); - - _appToEngineModQueue.set(param.info().id, {param.info().cookie, value}); - _appToEngineModQueue.producerDone(); -} - -void PluginHost::scanParams() { clapParamsRescan(&host_, CLAP_PARAM_RESCAN_ALL); } - -void PluginHost::clapParamsRescan(const clap_host *host, uint32_t flags) { - checkForMainThread(); - auto h = fromHost(host); - - // 1. it is forbidden to use CLAP_PARAM_RESCAN_ALL if the plugin is active - if (h->isPluginActive() && (flags & CLAP_PARAM_RESCAN_ALL)) { - throw std::logic_error( - "clap_host_params.recan(CLAP_PARAM_RESCAN_ALL) was called while the plugin is active!"); - return; - } - - // 2. scan the params. - auto count = h->_pluginParams->count(h->_plugin); - std::unordered_set<clap_id> paramIds(count * 2); - - for (int32_t i = 0; i < count; ++i) { - clap_param_info info; - if (!h->_pluginParams->get_info(h->_plugin, i, &info)) - throw std::logic_error("clap_plugin_params.get_info did return false!"); - - if (info.id == CLAP_INVALID_ID) { - std::ostringstream msg; - msg << "clap_plugin_params.get_info() reported a parameter with id = CLAP_INVALID_ID" - << std::endl - << " 2. name: " << info.name << ", module: " << info.module << std::endl; - throw std::logic_error(msg.str()); - } - - auto it = h->_params.find(info.id); - - // check that the parameter is not declared twice - if (paramIds.count(info.id) > 0) { - Q_ASSERT(it != h->_params.end()); - - std::ostringstream msg; - msg << "the parameter with id: " << info.id << " was declared twice." << std::endl - << " 1. name: " << it->second->info().name << ", module: " << it->second->info().module - << std::endl - << " 2. name: " << info.name << ", module: " << info.module << std::endl; - throw std::logic_error(msg.str()); - } - paramIds.insert(info.id); - - if (it == h->_params.end()) { - if (!(flags & CLAP_PARAM_RESCAN_ALL)) { - std::ostringstream msg; - msg << "a new parameter was declared, but the flag CLAP_PARAM_RESCAN_ALL was not " - "specified; id: " - << info.id << ", name: " << info.name << ", module: " << info.module << std::endl; - throw std::logic_error(msg.str()); - } - - double value = h->getParamValue(info); - auto param = std::make_unique<PluginParam>(*h, info, value); - h->checkValidParamValue(*param, value); - h->_params.insert_or_assign(info.id, std::move(param)); - } else { - // update param info - if (!it->second->isInfoEqualTo(info)) { - if (!clapParamsRescanMayInfoChange(flags)) { - std::ostringstream msg; - msg << "a parameter's info did change, but the flag CLAP_PARAM_RESCAN_INFO " - "was not specified; id: " - << info.id << ", name: " << info.name << ", module: " << info.module - << std::endl; - throw std::logic_error(msg.str()); - } - - if (!(flags & CLAP_PARAM_RESCAN_ALL) && - !it->second->isInfoCriticallyDifferentTo(info)) { - std::ostringstream msg; - msg << "a parameter's info has critical changes, but the flag CLAP_PARAM_RESCAN_ALL " - "was not specified; id: " - << info.id << ", name: " << info.name << ", module: " << info.module - << std::endl; - throw std::logic_error(msg.str()); - } - - it->second->setInfo(info); - } - - double value = h->getParamValue(info); - if (it->second->value() != value) { - if (!clapParamsRescanMayValueChange(flags)) { - std::ostringstream msg; - msg << "a parameter's value did change but, but the flag CLAP_PARAM_RESCAN_VALUES " - "was not specified; id: " - << info.id << ", name: " << info.name << ", module: " << info.module - << std::endl; - throw std::logic_error(msg.str()); - } - - // update param value - h->checkValidParamValue(*it->second, value); - it->second->setValue(value); - it->second->setModulation(value); - } - } - } - - // remove parameters which are gone - for (auto it = h->_params.begin(); it != h->_params.end();) { - if (paramIds.find(it->first) != paramIds.end()) - ++it; - else { - if (!(flags & CLAP_PARAM_RESCAN_ALL)) { - std::ostringstream msg; - auto &info = it->second->info(); - msg << "a parameter was removed, but the flag CLAP_PARAM_RESCAN_ALL was not " - "specified; id: " - << info.id << ", name: " << info.name << ", module: " << info.module << std::endl; - throw std::logic_error(msg.str()); - } - it = h->_params.erase(it); - } - } - - if (flags & CLAP_PARAM_RESCAN_ALL) - h->paramsChanged(); -} - -void PluginHost::clapParamsClear(const clap_host *host, clap_id param_id, clap_param_clear_flags flags) -{ - checkForMainThread(); -} - -void PluginHost::clapParamsRequestFlush(const clap_host *host) -{ - // Nothing to do we always flush and always process -} - -double PluginHost::getParamValue(const clap_param_info &info) { - checkForMainThread(); - double value; - if (_pluginParams->get_value(_plugin, info.id, &value)) - return value; - - std::ostringstream msg; - msg << "failed to get the param value, id: " << info.id << ", name: " << info.name - << ", module: " << info.module; - throw std::logic_error(msg.str()); -} - -void PluginHost::scanQuickControls() { - checkForMainThread(); - - if (!_pluginQuickControls) - return; - - if (!_pluginQuickControls->get || !_pluginQuickControls->count) { - std::ostringstream msg; - msg << "clap_plugin_quick_controls is partially implemented."; - throw std::logic_error(msg.str()); - } - - quickControlsSetSelectedPage(CLAP_INVALID_ID); - _quickControlsPages.clear(); - - const auto N = _pluginQuickControls->count(_plugin); - if (N == 0) - return; - - _quickControlsPages.reserve(N); - - clap_id firstPageId = CLAP_INVALID_ID; - for (int i = 0; i < N; ++i) { - auto page = std::make_unique<clap_quick_controls_page>(); - if (!_pluginQuickControls->get(_plugin, i, page.get())) { - std::ostringstream msg; - msg << "clap_plugin_quick_controls.get_page(" << i << ") failed, while the page count is " - << N; - throw std::logic_error(msg.str()); - } - - if (page->id == CLAP_INVALID_ID) { - std::ostringstream msg; - msg << "clap_plugin_quick_controls.get_page(" << i << ") gave an invalid page_id"; - throw std::invalid_argument(msg.str()); - } - - if (i == 0) - firstPageId = page->id; - - auto it = _quickControlsPages.find(page->id); - if (it != _quickControlsPages.end()) { - std::ostringstream msg; - msg << "clap_plugin_quick_controls.get_page(" << i - << ") gave twice the same page_id:" << page->id << std::endl - << " 1. name: " << it->second->name << std::endl - << " 2. name: " << page->name; - throw std::invalid_argument(msg.str()); - } - - _quickControlsPages.insert_or_assign(page->id, std::move(page)); - } - - quickControlsPagesChanged(); - - auto pageId = _pluginQuickControls->get_selected(_plugin); - quickControlsSetSelectedPage(pageId == CLAP_INVALID_ID ? firstPageId : pageId); -} - -void PluginHost::quickControlsSetSelectedPage(clap_id pageId) { - checkForMainThread(); - if (pageId == _quickControlsSelectedPage) - return; - - if (pageId != CLAP_INVALID_ID) { - auto it = _quickControlsPages.find(pageId); - if (it == _quickControlsPages.end()) { - std::ostringstream msg; - msg << "quick control page_id " << pageId << " not found"; - throw std::invalid_argument(msg.str()); - } - } - - _quickControlsSelectedPage = pageId; - quickControlsSelectedPageChanged(); -} - -void PluginHost::setQuickControlsSelectedPageByHost(clap_id page_id) { - checkForMainThread(); - Q_ASSERT(page_id != CLAP_INVALID_ID); - - checkForMainThread(); - - _quickControlsSelectedPage = page_id; - - if (_pluginQuickControls && _pluginQuickControls->select) - _pluginQuickControls->select(_plugin, page_id); -} - -void PluginHost::clapQuickControlsChanged(const clap_host *host, - clap_quick_controls_changed_flags flags) { - checkForMainThread(); - - auto h = fromHost(host); - if (!h->_pluginQuickControls) { - std::ostringstream msg; - msg << "Plugin called clap_host_quick_controls.changed() but does not provide " - "clap_plugin_quick_controls"; - throw std::logic_error(msg.str()); - } - - if (flags & CLAP_QUICK_CONTROLS_PAGES_CHANGED) - h->scanQuickControls(); - - if (flags & CLAP_QUICK_CONTROLS_SELECTED_PAGE_CHANGED) { - auto selectedPageId = h->_pluginQuickControls->get_selected(h->_plugin); - h->quickControlsSetSelectedPage(selectedPageId); - } -} - -bool PluginHost::loadNativePluginPreset(const std::string &path) { - checkForMainThread(); - - if (!_pluginPresetLoad) - return false; - - if (!_pluginPresetLoad->from_file) - throw std::logic_error("clap_plugin_preset_load does not implement load_from_file"); - - return _pluginPresetLoad->from_file(_plugin, path.c_str()); -} - -void PluginHost::clapStateMarkDirty(const clap_host *host) { - checkForMainThread(); - - auto h = fromHost(host); - - if (!h->_pluginState || !h->_pluginState->save || !h->_pluginState->load) - throw std::logic_error("Plugin called clap_host_state.set_dirty() but the host does not " - "provide a complete clap_plugin_state interface."); - - h->_stateIsDirty = true; -} - -void PluginHost::setPluginState(PluginState state) { - switch (state) { - case Inactive: - Q_ASSERT(_state == ActiveAndReadyToDeactivate); - break; - - case InactiveWithError: - Q_ASSERT(_state == Inactive); - break; - - case ActiveAndSleeping: - Q_ASSERT(_state == Inactive || _state == ActiveAndProcessing); - break; - - case ActiveAndProcessing: - Q_ASSERT(_state == ActiveAndSleeping); - break; - - case ActiveWithError: - Q_ASSERT(_state == ActiveAndProcessing); - break; - - case ActiveAndReadyToDeactivate: - Q_ASSERT(_state == ActiveAndProcessing || _state == ActiveAndSleeping || - _state == ActiveWithError); - break; - - default: - std::terminate(); - } - - _state = state; -} - -bool PluginHost::isPluginActive() const { - switch (_state) { - case Inactive: - case InactiveWithError: - return false; - default: - return true; - } -} - -bool PluginHost::isPluginProcessing() const { return _state == ActiveAndProcessing; } - -bool PluginHost::isPluginSleeping() const { return _state == ActiveAndSleeping; } - -QString PluginHost::paramValueToText(clap_id paramId, double value) { - std::array<char, 256> buffer; - - if (_pluginParams->value_to_text(_plugin, paramId, value, buffer.data(), buffer.size())) - return buffer.data(); - - return QString::number(value); -} - -bool PluginHost::canUsePluginGui() const noexcept { - return _pluginGui && _pluginGui->create && _pluginGui->destroy && _pluginGui->can_resize && - _pluginGui->get_size && _pluginGui->round_size && _pluginGui->round_size && - _pluginGui->set_size && _pluginGui->set_scale && _pluginGui->hide && _pluginGui->show; -} -\ No newline at end of file diff --git a/examples/host/plugin-host.hh b/examples/host/plugin-host.hh @@ -1,283 +0,0 @@ -#pragma once - -#include <array> -#include <memory> -#include <unordered_map> -#include <unordered_set> - -#include <QLibrary> -#include <QSemaphore> -#include <QSocketNotifier> -#include <QString> -#include <QThread> -#include <QTimer> -#include <QWidget> - -#include <clap/all.h> - -#include "../common/reducing-param-queue.hh" -#include "engine.hh" -#include "plugin-param.hh" - -class Engine; -class PluginHost final : public QObject { - Q_OBJECT; - -public: - PluginHost(Engine &engine); - ~PluginHost(); - - bool load(const QString &path, int pluginIndex); - void unload(); - - bool canActivate() const; - void activate(int32_t sample_rate); - void deactivate(); - - void recreatePluginWindow(); - void setPluginWindowVisibility(bool isVisible); - - void setPorts(int numInputs, float **inputs, int numOutputs, float **outputs); - void setParentWindow(WId parentWindow); - - void processBegin(int nframes); - void processNoteOn(int sampleOffset, int channel, int key, int velocity); - void processNoteOff(int sampleOffset, int channel, int key, int velocity); - void processNoteAt(int sampleOffset, int channel, int key, int pressure); - void processPitchBend(int sampleOffset, int channel, int value); - void processCC(int sampleOffset, int channel, int cc, int value); - void process(); - void processEnd(int nframes); - - void idle(); - - void initPluginExtensions(); - void initThreadPool(); - void terminateThreadPool(); - void threadPoolEntry(); - - void setParamValueByHost(PluginParam ¶m, double value); - void setParamModulationByHost(PluginParam ¶m, double value); - - auto ¶ms() const { return _params; } - auto &quickControlsPages() const { return _quickControlsPages; } - auto quickControlsSelectedPage() const { return _quickControlsSelectedPage; } - void setQuickControlsSelectedPageByHost(clap_id page_id); - - bool loadNativePluginPreset(const std::string &path); - bool loadStateFromFile(const std::string &path); - bool saveStateToFile(const std::string &path); - - static void checkForMainThread(); - static void checkForAudioThread(); - - QString paramValueToText(clap_id paramId, double value); - -signals: - void paramsChanged(); - void quickControlsPagesChanged(); - void quickControlsSelectedPageChanged(); - -private: - static PluginHost *fromHost(const clap_host *host); - template <typename T> - void initPluginExtension(const T *&ext, const char *id); - - /* clap host callbacks */ - static void clapLog(const clap_host *host, clap_log_severity severity, const char *msg); - - static void clapRequestCallback(const clap_host *host); - static void clapRequestRestart(const clap_host *host); - static void clapRequestProcess(const clap_host *host); - - static bool clapIsMainThread(const clap_host *host); - static bool clapIsAudioThread(const clap_host *host); - - static void clapParamsRescan(const clap_host *host, clap_param_rescan_flags flags); - static void clapParamsClear(const clap_host *host, clap_id param_id, clap_param_clear_flags flags); - static void clapParamsRequestFlush(const clap_host *host); - void scanParams(); - void scanParam(int32_t index); - PluginParam &checkValidParamId(const std::string_view &function, - const std::string_view ¶m_name, - clap_id param_id); - void checkValidParamValue(const PluginParam ¶m, double value); - double getParamValue(const clap_param_info &info); - static bool clapParamsRescanMayValueChange(uint32_t flags) { - return flags & (CLAP_PARAM_RESCAN_ALL | CLAP_PARAM_RESCAN_VALUES); - } - static bool clapParamsRescanMayInfoChange(uint32_t flags) { - return flags & (CLAP_PARAM_RESCAN_ALL | CLAP_PARAM_RESCAN_INFO); - } - - void scanQuickControls(); - void quickControlsSetSelectedPage(clap_id pageId); - static void clapQuickControlsChanged(const clap_host *host, - clap_quick_controls_changed_flags flags); - - static bool clapRegisterTimer(const clap_host *host, uint32_t period_ms, clap_id *timer_id); - static bool clapUnregisterTimer(const clap_host *host, clap_id timer_id); - static bool clapRegisterFd(const clap_host *host, clap_fd fd, uint32_t flags); - static bool clapModifyFd(const clap_host *host, clap_fd fd, uint32_t flags); - static bool clapUnregisterFd(const clap_host *host, clap_fd fd); - void eventLoopSetFdNotifierFlags(clap_fd fd, uint32_t flags); - - static bool clapThreadPoolRequestExec(const clap_host *host, uint32_t num_tasks); - - static const void *clapExtension(const clap_host *host, const char *extension); - - /* clap host gui callbacks */ - static bool clapGuiResize(const clap_host *host, uint32_t width, uint32_t height); - - static void clapStateMarkDirty(const clap_host *host); - - bool canUsePluginGui() const noexcept; - -private: - Engine &_engine; - - QLibrary _library; - - clap_host host_; - static const constexpr clap_host_log _hostLog = { - PluginHost::clapLog, - }; - static const constexpr clap_host_gui _hostGui = { - PluginHost::clapGuiResize, - }; - // static const constexpr clap_host_audio_ports hostAudioPorts_; - // static const constexpr clap_host_audio_ports_config hostAudioPortsConfig_; - static const constexpr clap_host_params _hostParams = { - PluginHost::clapParamsRescan, - PluginHost::clapParamsClear, - PluginHost::clapParamsRequestFlush, - }; - static const constexpr clap_host_quick_controls _hostQuickControls = { - PluginHost::clapQuickControlsChanged, - }; - static const constexpr clap_host_timer_support _hostTimerSupport = { - PluginHost::clapRegisterTimer, - PluginHost::clapUnregisterTimer, - }; - static const constexpr clap_host_fd_support _hostFdSupport = { - PluginHost::clapRegisterFd, - PluginHost::clapModifyFd, - PluginHost::clapUnregisterFd, - }; - static const constexpr clap_host_thread_check _hostThreadCheck = { - PluginHost::clapIsMainThread, - PluginHost::clapIsAudioThread, - }; - static const constexpr clap_host_thread_pool _hostThreadPool = { - PluginHost::clapThreadPoolRequestExec, - }; - static const constexpr clap_host_state _hostState = { - PluginHost::clapStateMarkDirty, - }; - - const struct clap_plugin_entry *_pluginEntry = nullptr; - const clap_plugin *_plugin = nullptr; - 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; - - bool _pluginExtensionsAreInitialized = false; - - /* timers */ - clap_id _nextTimerId = 0; - std::unordered_map<clap_id, std::unique_ptr<QTimer>> _timers; - - /* fd events */ - struct Notifiers { - std::unique_ptr<QSocketNotifier> rd; - std::unique_ptr<QSocketNotifier> wr; - std::unique_ptr<QSocketNotifier> err; - }; - std::unordered_map<clap_fd, std::unique_ptr<Notifiers>> _fds; - - /* thread pool */ - std::vector<std::unique_ptr<QThread>> _threadPool; - std::atomic<bool> _threadPoolStop = {false}; - std::atomic<int> _threadPoolTaskIndex = {0}; - QSemaphore _threadPoolSemaphoreProd; - QSemaphore _threadPoolSemaphoreDone; - - /* process stuff */ - clap_audio_buffer _audioIn = {}; - clap_audio_buffer _audioOut = {}; - std::vector<clap_event> _evIn; - std::vector<clap_event> _evOut; - clap_process _process; - - /* param update queues */ - std::unordered_map<clap_id, std::unique_ptr<PluginParam>> _params; - - struct AppToEngineParamQueueValue { - void *cookie; - double value; - }; - - struct EngineToAppParamQueueValue { - double value; - bool isAdjusting; - }; - - ReducingParamQueue<AppToEngineParamQueueValue> _appToEngineValueQueue; - ReducingParamQueue<AppToEngineParamQueueValue> _appToEngineModQueue; - ReducingParamQueue<EngineToAppParamQueueValue> _engineToAppValueQueue; - - std::unordered_map<clap_id, bool> _isAdjusting; - - std::unordered_map<clap_id, std::unique_ptr<clap_quick_controls_page>> _quickControlsPages; - clap_id _quickControlsSelectedPage = CLAP_INVALID_ID; - - /* delayed actions */ - enum PluginState { - // The plugin is inactive, only the main thread uses it - Inactive, - - // Activation failed - InactiveWithError, - - // The plugin is active and sleeping, the audio engine can call set_processing() - ActiveAndSleeping, - - // The plugin is processing - ActiveAndProcessing, - - // The plugin did process but is in error - ActiveWithError, - - // The plugin is not used anymore by the audio engine and can be deactivated on the main - // thread - ActiveAndReadyToDeactivate, - }; - - bool isPluginActive() const; - bool isPluginProcessing() const; - bool isPluginSleeping() const; - void setPluginState(PluginState state); - - PluginState _state = Inactive; - bool _stateIsDirty = false; - - bool _scheduleRestart = false; - bool _scheduleDeactivate = false; - - bool _scheduleProcess = true; - - bool _isGuiCreated = false; - bool _isGuiVisible = false; - - bool _scheduleMainThreadCallback = false; -}; diff --git a/examples/host/plugin-info.hh b/examples/host/plugin-info.hh @@ -1,13 +0,0 @@ -#pragma once - -#include <QString> - -class PluginInfo { -public: - PluginInfo() = default; - -private: - QString _name; - QString _file; - QString _index; // in case of shell plugin -}; diff --git a/examples/host/plugin-param.cc b/examples/host/plugin-param.cc @@ -1,46 +0,0 @@ -#include "plugin-param.hh" -#include "plugin-host.hh" - -PluginParam::PluginParam(PluginHost &pluginHost, const clap_param_info &info, double value) - : QObject(&pluginHost), _info(info), _value(value) {} - -void PluginParam::setValue(double v) { - if (_value == v) - return; - - _value = v; - valueChanged(); -} - -void PluginParam::setModulation(double v) { - if (_modulation == v) - return; - - _modulation = v; - modulatedValueChanged(); -} - -bool PluginParam::isValueValid(const double v) const { - return _info.min_value <= v && v <= _info.max_value; -} - -void PluginParam::printShortInfo(std::ostream &os) const { - os << "id: " << _info.id << ", name: '" << _info.name << "', module: '" << _info.module << "'"; -} - -void PluginParam::printInfo(std::ostream &os) const { - printShortInfo(os); - os << ", min: " << _info.min_value << ", max: " << _info.max_value; -} - -bool PluginParam::isInfoEqualTo(const clap_param_info &info) const { - return !std::memcmp(&info, &_info, sizeof(clap_param_info)); -} - -bool PluginParam::isInfoCriticallyDifferentTo(const clap_param_info &info) const { - assert(_info.id == info.id); - return (_info.flags & CLAP_PARAM_IS_PER_NOTE) == (info.flags & CLAP_PARAM_IS_PER_NOTE) || - (_info.flags & CLAP_PARAM_IS_PER_CHANNEL) == (info.flags & CLAP_PARAM_IS_PER_CHANNEL) || - (_info.flags & CLAP_PARAM_IS_READONLY) == (info.flags & CLAP_PARAM_IS_READONLY) || - _info.min_value != _info.min_value || _info.max_value != _info.max_value; -} -\ No newline at end of file diff --git a/examples/host/plugin-param.hh b/examples/host/plugin-param.hh @@ -1,68 +0,0 @@ -#pragma once - -#include <ostream> -#include <unordered_map> - -#include <QObject> - -#include <clap/all.h> - -class PluginHost; -class PluginParam : public QObject { - Q_OBJECT; - -public: - PluginParam(PluginHost &pluginHost, const clap_param_info &info, double value); - - double value() const { return _value; } - void setValue(double v); - - double modulation() const { return _modulation; } - void setModulation(double v); - - double modulatedValue() const { - return std::min(_info.max_value, std::max(_info.min_value, _value + _modulation)); - } - - bool isValueValid(const double v) const; - - void printShortInfo(std::ostream &os) const; - void printInfo(std::ostream &os) const; - - void setInfo(const clap_param_info &info) noexcept { _info = info; } - bool isInfoEqualTo(const clap_param_info &info) const; - bool isInfoCriticallyDifferentTo(const clap_param_info &info) const; - clap_param_info &info() noexcept { return _info; } - const clap_param_info &info() const noexcept { return _info; } - - bool isBeingAdjusted() const noexcept { return _isBeingAdjusted; } - void setIsAdjusting(bool isAdjusting) { - if (isAdjusting && !_isBeingAdjusted) - beginAdjust(); - else if (!isAdjusting && _isBeingAdjusted) - endAdjust(); - } - void beginAdjust() { - Q_ASSERT(!_isBeingAdjusted); - _isBeingAdjusted = true; - emit isBeingAdjustedChanged(); - } - void endAdjust() { - Q_ASSERT(_isBeingAdjusted); - _isBeingAdjusted = false; - emit isBeingAdjustedChanged(); - } - -signals: - void isBeingAdjustedChanged(); - void infoChanged(); - void valueChanged(); - void modulatedValueChanged(); - -private: - bool _isBeingAdjusted = false; - clap_param_info _info; - double _value = 0; - double _modulation = 0; - std::unordered_map<int64_t, std::string> _enumEntries; -}; -\ No newline at end of file diff --git a/examples/host/plugin-parameters-widget.cc b/examples/host/plugin-parameters-widget.cc @@ -1,348 +0,0 @@ -#include <QFormLayout> -#include <QFrame> -#include <QLabel> -#include <QLayout> -#include <QSlider> -#include <QSplitter> -#include <QTreeWidget> - -#include "plugin-host.hh" -#include "plugin-param.hh" -#include "plugin-parameters-widget.hh" - -/////////////////// -// ParamTreeItem // -/////////////////// - -PluginParametersWidget::ParamTreeItem::ParamTreeItem(ModuleTreeItem *parent, PluginParam ¶m) - : QTreeWidgetItem(parent), param_(param) {} - -QVariant PluginParametersWidget::ParamTreeItem::data(int column, int role) const { - if (column == 0 && role == Qt::DisplayRole) - return param_.info().name; - return {}; -} - -void PluginParametersWidget::ParamTreeItem::setData(int column, int role, const QVariant &value) { - // nothing to do, read-only -} - -//////////////////// -// ModuleTreeItem // -//////////////////// - -PluginParametersWidget::ModuleTreeItem::ModuleTreeItem(QTreeWidget *parent) - : QTreeWidgetItem(parent), name_("/") {} - -PluginParametersWidget::ModuleTreeItem::ModuleTreeItem(ModuleTreeItem *parent, const QString &name) - : QTreeWidgetItem(parent), name_(name) {} - -void PluginParametersWidget::ModuleTreeItem::clear() { - while (childCount() > 0) - removeChild(child(0)); - modules_.clear(); -} - -PluginParametersWidget::ModuleTreeItem & -PluginParametersWidget::ModuleTreeItem::subModule(const QString &name) { - auto it = modules_.find(name); - if (it != modules_.end()) - return *it.value(); - auto module = new ModuleTreeItem(this, name); - addChild(module); - modules_.insert(name, module); - return *module; -} - -void PluginParametersWidget::ModuleTreeItem::addItem(ParamTreeItem *item) { addChild(item); } - -QVariant PluginParametersWidget::ModuleTreeItem::data(int column, int role) const { - if (column == 0 && role == Qt::DisplayRole) - return name_; - return {}; -} - -void PluginParametersWidget::ModuleTreeItem::setData(int column, int role, const QVariant &value) { - // nothing to do, read-only -} - -//////////////////////////// -// PluginParametersWidget // -//////////////////////////// - -PluginParametersWidget::PluginParametersWidget(QWidget *parent, PluginHost &pluginHost) - : QWidget(parent), _pluginHost(pluginHost) { - - _treeWidget = new QTreeWidget(this); - - // Tree - _rootModuleItem = new ModuleTreeItem(_treeWidget); - _treeWidget->addTopLevelItem(_rootModuleItem); - _treeWidget->setHeaderHidden(true); - _treeWidget->setAnimated(true); - _treeWidget->setRootIsDecorated(true); - _treeWidget->setSelectionMode(QAbstractItemView::SingleSelection); - _treeWidget->setSelectionBehavior(QAbstractItemView::SelectItems); - _treeWidget->setVerticalScrollMode(QAbstractItemView::ScrollPerItem); - connect( - &_pluginHost, &PluginHost::paramsChanged, this, &PluginParametersWidget::computeDataModel); - connect(_treeWidget, - &QTreeWidget::currentItemChanged, - this, - &PluginParametersWidget::selectionChanged); - - // Info - auto infoWidget = new QFrame(this); - infoWidget->setLineWidth(1); - infoWidget->setMidLineWidth(1); - infoWidget->setFrameShape(QFrame::StyledPanel); - infoWidget->setFrameShadow(QFrame::Sunken); - - _idLabel = new QLabel; - _nameLabel = new QLabel; - _moduleLabel = new QLabel; - _isPerNoteLabel = new QLabel; - _isPerChannelLabel = new QLabel; - _isPeriodicLabel = new QLabel; - _isReadOnlyLabel = new QLabel; - _isHiddenLabel = new QLabel; - _isBypassLabel = new QLabel; - _isSteppedLabel = new QLabel; - _minValueLabel = new QLabel; - _maxValueLabel = new QLabel; - _defaultValueLabel = new QLabel; - _isBeingAdjusted = new QLabel; - _valueLabel = new QLabel; - - _valueSlider = new QSlider; - _valueSlider->setMinimum(0); - _valueSlider->setMaximum(SLIDER_RANGE); - _valueSlider->setOrientation(Qt::Horizontal); - connect(_valueSlider, &QSlider::valueChanged, this, &PluginParametersWidget::sliderValueChanged); - - _modulationSlider = new QSlider; - _modulationSlider->setMinimum(-SLIDER_RANGE); - _modulationSlider->setMaximum(SLIDER_RANGE); - _modulationSlider->setOrientation(Qt::Horizontal); - connect(_modulationSlider, &QSlider::valueChanged, this, &PluginParametersWidget::sliderModulationChanged); - - auto formLayout = new QFormLayout(infoWidget); - formLayout->addRow(tr("id"), _idLabel); - formLayout->addRow(tr("name"), _nameLabel); - formLayout->addRow(tr("module"), _moduleLabel); - formLayout->addRow(tr("is_per_note"), _isPerNoteLabel); - formLayout->addRow(tr("is_per_channel"), _isPerChannelLabel); - formLayout->addRow(tr("is_periodic"), _isPeriodicLabel); - formLayout->addRow(tr("is_read_only"), _isReadOnlyLabel); - formLayout->addRow(tr("is_hidden"), _isHiddenLabel); - formLayout->addRow(tr("is_bypass"), _isBypassLabel); - formLayout->addRow(tr("is_stepped"), _isSteppedLabel); - formLayout->addRow(tr("min_value"), _minValueLabel); - formLayout->addRow(tr("max_value"), _maxValueLabel); - formLayout->addRow(tr("default_value"), _defaultValueLabel); - formLayout->addRow(tr("is_being_adjusted"), _isBeingAdjusted); - formLayout->addRow(tr("value"), _valueLabel); - formLayout->addRow(tr("value"), _valueSlider); - formLayout->addRow(tr("modulation"), _modulationSlider); - - infoWidget->setLayout(formLayout); - - // Splitter - auto splitter = new QSplitter(); - splitter->addWidget(_treeWidget); - splitter->addWidget(infoWidget); - - auto layout = new QHBoxLayout(this); - layout->addWidget(splitter); - setLayout(layout); - - computeDataModel(); - updateParamInfo(); -} - -void PluginParametersWidget::computeDataModel() { - _rootModuleItem->clear(); - _idToParamTreeItem.clear(); - - for (auto &it : _pluginHost.params()) { - auto ¶m = *it.second; - - QString path(param.info().module); - auto modules = path.split("/", Qt::SkipEmptyParts); - auto module = _rootModuleItem; - for (auto &m : modules) - module = &module->subModule(m); - - auto item = std::make_unique<ParamTreeItem>(module, param); - _idToParamTreeItem.insert_or_assign(param.info().id, std::move(item)); - } - _treeWidget->sortItems(0, Qt::AscendingOrder); -} - -void PluginParametersWidget::selectionChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous) { - if (!current) { - disconnectFromParam(); - return; - } - - auto module = dynamic_cast<ModuleTreeItem *>(current); - if (module) { - disconnectFromParam(); - return; - } - - auto item = dynamic_cast<ParamTreeItem *>(current); - if (item) { - connectToParam(&item->param()); - return; - } -} - -void PluginParametersWidget::connectToParam(PluginParam *param) { - if (_currentParam) - disconnectFromParam(); - - _currentParam = param; - connect(param, &PluginParam::infoChanged, this, &PluginParametersWidget::paramInfoChanged); - connect(param, &PluginParam::valueChanged, this, &PluginParametersWidget::paramValueChanged); - connect(param, - &PluginParam::isBeingAdjustedChanged, - this, - &PluginParametersWidget::updateParamIsBeingAjusted); - - updateAll(); -} - -void PluginParametersWidget::disconnectFromParam() { - if (!_currentParam) - return; - - disconnect( - _currentParam, &PluginParam::infoChanged, this, &PluginParametersWidget::paramInfoChanged); - disconnect( - _currentParam, &PluginParam::valueChanged, this, &PluginParametersWidget::paramValueChanged); - disconnect(_currentParam, - &PluginParam::isBeingAdjustedChanged, - this, - &PluginParametersWidget::updateParamIsBeingAjusted); - - _currentParam = nullptr; - - updateAll(); -} - -void PluginParametersWidget::updateAll() { - updateParamInfo(); - updateParamValue(); - updateParamModulation(); - updateParamIsBeingAjusted(); -} - -void PluginParametersWidget::updateParamInfo() { - if (!_currentParam) { - _idLabel->setText("-"); - _nameLabel->setText("-"); - _moduleLabel->setText("-"); - _isPerNoteLabel->setText("-"); - _isPerChannelLabel->setText("-"); - _isPeriodicLabel->setText("-"); - _isReadOnlyLabel->setText("-"); - _isHiddenLabel->setText("-"); - _isBypassLabel->setText("-"); - _isSteppedLabel->setText("-"); - _minValueLabel->setText("-"); - _maxValueLabel->setText("-"); - _defaultValueLabel->setText("-"); - _isBeingAdjusted->setText("-"); - _valueLabel->setText("-"); - } else { - auto &p = *_currentParam; - auto &i = p.info(); - _idLabel->setText(QString::number(i.id)); - _nameLabel->setText(i.name); - _moduleLabel->setText(i.module); - _isPerNoteLabel->setText(i.flags & CLAP_PARAM_IS_PER_NOTE ? "true" : "false"); - _isPerChannelLabel->setText(i.flags & CLAP_PARAM_IS_PER_CHANNEL ? "true" : "false"); - _isPeriodicLabel->setText(i.flags & CLAP_PARAM_IS_PERIODIC ? "true" : "false"); - _isReadOnlyLabel->setText(i.flags & CLAP_PARAM_IS_READONLY ? "true" : "false"); - _isHiddenLabel->setText(i.flags & CLAP_PARAM_IS_HIDDEN ? "true" : "false"); - _isBypassLabel->setText(i.flags & CLAP_PARAM_IS_BYPASS ? "true" : "false"); - _isBeingAdjusted->setText(p.isBeingAdjusted() ? "true" : "false"); - - _isSteppedLabel->setText("float"); - _minValueLabel->setText(QString::number(i.min_value)); - _maxValueLabel->setText(QString::number(i.max_value)); - _defaultValueLabel->setText(QString::number(i.default_value)); - } -} - -void PluginParametersWidget::updateParamIsBeingAjusted() { - if (!_currentParam) { - _isBeingAdjusted->setText("-"); - } else { - auto &p = *_currentParam; - _isBeingAdjusted->setText(p.isBeingAdjusted() ? "true" : "false"); - } -} - -void PluginParametersWidget::updateParamValue() { - if (_valueSlider->isSliderDown()) - return; - - if (!_currentParam) - return; - - auto info = _currentParam->info(); - auto v = _currentParam->value(); - _valueSlider->setValue(SLIDER_RANGE * (v - info.min_value) / (info.max_value - info.min_value)); - updateParamValueText(); -} - -void PluginParametersWidget::updateParamValueText() -{ - _valueLabel->setText(_pluginHost.paramValueToText(_currentParam->info().id, _currentParam->value())); -} - -void PluginParametersWidget::updateParamModulation() { - if (_valueSlider->isSliderDown()) - return; - - if (!_currentParam) - return; - - auto info = _currentParam->info(); - auto v = _currentParam->value(); - _valueSlider->setValue(SLIDER_RANGE * (v - info.min_value) / (info.max_value - info.min_value)); -} - -void PluginParametersWidget::paramInfoChanged() { updateParamInfo(); } - -void PluginParametersWidget::paramValueChanged() { updateParamValue(); } - -void PluginParametersWidget::sliderValueChanged(int newValue) { - if (!_currentParam) - return; - - if (!_valueSlider->isSliderDown()) - return; - - auto &info = _currentParam->info(); - - double value = newValue * (info.max_value - info.min_value) / SLIDER_RANGE + info.min_value; - _pluginHost.setParamValueByHost(*_currentParam, value); - updateParamValueText(); -} - -void PluginParametersWidget::sliderModulationChanged(int newValue) { - if (!_currentParam) - return; - - if (!_modulationSlider->isSliderDown()) - return; - - auto &info = _currentParam->info(); - - double dist = info.max_value - info.min_value; - double value = newValue * dist / SLIDER_RANGE; - _pluginHost.setParamModulationByHost(*_currentParam, value); -} -\ No newline at end of file diff --git a/examples/host/plugin-parameters-widget.hh b/examples/host/plugin-parameters-widget.hh @@ -1,101 +0,0 @@ -#pragma once - -#include <QHash> -#include <QList> -#include <QTreeWidgetItem> -#include <QWidget> - -#include <clap/clap.h> - -class QTreeWidget; -class QTreeWidgetItem; -class PluginHost; -class PluginParam; -class QLabel; -class QDial; -class QSlider; - -class PluginParametersWidget : public QWidget { - Q_OBJECT -public: - explicit PluginParametersWidget(QWidget *parent, PluginHost &pluginHost); - - class ModuleTreeItem; - class ParamTreeItem : public QTreeWidgetItem { - public: - ParamTreeItem(ModuleTreeItem *parent, PluginParam ¶m); - QVariant data(int column, int role) const override; - void setData(int column, int role, const QVariant &value) override; - - auto ¶m() { return param_; } - auto ¶m() const { return param_; } - - private: - PluginParam ¶m_; - }; - - class ModuleTreeItem : public QTreeWidgetItem { - public: - ModuleTreeItem(QTreeWidget *parent); - ModuleTreeItem(ModuleTreeItem *parent, const QString &name); - - void clear(); - - ModuleTreeItem &subModule(const QString &name); - void addItem(ParamTreeItem *item); - - QVariant data(int column, int role) const override; - void setData(int column, int role, const QVariant &value) override; - - private: - QString name_; - QHash<QString, ModuleTreeItem *> modules_; - }; - -signals: - -private: - void computeDataModel(); - void selectionChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous); - - void connectToParam(PluginParam *param); - void disconnectFromParam(); - - void paramInfoChanged(); - void paramValueChanged(); - void sliderValueChanged(int newValue); - void sliderModulationChanged(int newValue); - - void updateAll(); - void updateParamInfo(); - void updateParamValue(); - void updateParamValueText(); - void updateParamModulation(); - void updateParamIsBeingAjusted(); - - static const constexpr int SLIDER_RANGE = 10000; - - PluginHost &_pluginHost; - QTreeWidget *_treeWidget = nullptr; - std::unordered_map<clap_id, std::unique_ptr<ParamTreeItem>> _idToParamTreeItem; - ModuleTreeItem *_rootModuleItem; - PluginParam *_currentParam = nullptr; - - QLabel *_idLabel = nullptr; - QLabel *_nameLabel = nullptr; - QLabel *_moduleLabel = nullptr; - QLabel *_isPerNoteLabel = nullptr; - QLabel *_isPerChannelLabel = nullptr; - QLabel *_isPeriodicLabel = nullptr; - QLabel *_isReadOnlyLabel = nullptr; - QLabel *_isHiddenLabel = nullptr; - QLabel *_isBypassLabel = nullptr; - QLabel *_isSteppedLabel = nullptr; - QLabel *_minValueLabel = nullptr; - QLabel *_maxValueLabel = nullptr; - QLabel *_defaultValueLabel = nullptr; - QLabel *_isBeingAdjusted = nullptr; - QLabel *_valueLabel = nullptr; - QSlider *_valueSlider = nullptr; - QSlider *_modulationSlider = nullptr; -}; diff --git a/examples/host/plugin-quick-control-widget.cc b/examples/host/plugin-quick-control-widget.cc @@ -1,88 +0,0 @@ -#include <QDial> -#include <QLabel> -#include <QVBoxLayout> - -#include "plugin-host.hh" -#include "plugin-param.hh" -#include "plugin-quick-control-widget.hh" - -PluginQuickControlWidget::PluginQuickControlWidget(QWidget *parent, PluginHost &pluginHost) - : QWidget(parent), pluginHost_(pluginHost) { - _dial = new QDial(this); - _label = new QLabel(this); - - auto layout = new QVBoxLayout(this); - layout->addWidget(_dial); - layout->addWidget(_label); - setLayout(layout); - - setPluginParam(nullptr); -} - -void PluginQuickControlWidget::setPluginParam(PluginParam *param) { - if (_param == param) - return; - - if (_param) - disconnectFromParam(); - - Q_ASSERT(!_param); - - connectToParam(param); -} - -void PluginQuickControlWidget::connectToParam(PluginParam *param) { - Q_ASSERT(!_param); - - _param = param; - connect(_param, &PluginParam::infoChanged, this, &PluginQuickControlWidget::paramInfoChanged); - connect(_param, &PluginParam::valueChanged, this, &PluginQuickControlWidget::paramValueChanged); - updateAll(); -} - -void PluginQuickControlWidget::disconnectFromParam() { - Q_ASSERT(_param); - - disconnect(_param, &PluginParam::infoChanged, this, &PluginQuickControlWidget::paramInfoChanged); - disconnect( - _param, &PluginParam::valueChanged, this, &PluginQuickControlWidget::paramValueChanged); - - updateAll(); -} - -void PluginQuickControlWidget::paramInfoChanged() { updateParamInfo(); } - -void PluginQuickControlWidget::paramValueChanged() { updateParamValue(); } - -void PluginQuickControlWidget::dialValueChanged(int newValue) { - if (!_param) - return; - - if (!_dial->isSliderDown()) - return; - - auto &info = _param->info(); - - double value = newValue * (info.max_value - info.min_value) / DIAL_RANGE + info.min_value; - pluginHost_.setParamValueByHost(*_param, value); -} -void PluginQuickControlWidget::updateParamValue() { - if (!_param) - return; - - if (_dial->isSliderDown()) - return; - - auto info = _param->info(); - auto v = _param->value(); - _dial->setValue(DIAL_RANGE * (v - info.min_value) / (info.max_value - info.min_value)); -} - -void PluginQuickControlWidget::updateParamInfo() { - _label->setText(_param ? _param->info().name : "-"); -} - -void PluginQuickControlWidget::updateAll() { - updateParamInfo(); - updateParamValue(); -} diff --git a/examples/host/plugin-quick-control-widget.hh b/examples/host/plugin-quick-control-widget.hh @@ -1,38 +0,0 @@ -#pragma once - -#include <QWidget> - -#include <clap/all.h> - -class QDial; -class QLabel; -class PluginHost; -class PluginParam; -class PluginQuickControlWidget : public QWidget { - Q_OBJECT; - -public: - PluginQuickControlWidget(QWidget *parent, PluginHost &pluginHost); - - void setPluginParam(PluginParam *param); - -private: - void paramInfoChanged(); - void paramValueChanged(); - void dialValueChanged(int newValue); - - void disconnectFromParam(); - void connectToParam(PluginParam *param); - - void updateParamValue(); - void updateParamInfo(); - void updateAll(); - - static const constexpr int DIAL_RANGE = 10000; - - PluginHost &pluginHost_; - - QDial *_dial = nullptr; - QLabel *_label = nullptr; - PluginParam *_param = nullptr; -}; -\ No newline at end of file diff --git a/examples/host/plugin-quick-controls-widget.cc b/examples/host/plugin-quick-controls-widget.cc @@ -1,66 +0,0 @@ -#include <QComboBox> -#include <QGridLayout> -#include <QVBoxLayout> - -#include "plugin-host.hh" -#include "plugin-quick-control-widget.hh" -#include "plugin-quick-controls-widget.hh" - -PluginQuickControlsWidget::PluginQuickControlsWidget(QWidget *parent, PluginHost &pluginHost) - : QWidget(parent), _pluginHost(pluginHost) { - _chooser = new QComboBox(this); - connect(_chooser, - &QComboBox::activated, - this, - &PluginQuickControlsWidget::selectPageFromChooser); - - for (auto &qc : _controls) - qc = new PluginQuickControlWidget(this, pluginHost); - - auto grid = new QGridLayout(); - grid->setSpacing(3); - - const auto rowSize = CLAP_QUICK_CONTROLS_COUNT / 2; - for (int i = 0; i < CLAP_QUICK_CONTROLS_COUNT; ++i) - grid->addWidget(_controls[i], i / rowSize, i % rowSize); - - auto vbox = new QVBoxLayout(); - vbox->addWidget(_chooser); - vbox->addLayout(grid); - vbox->setSpacing(3); - setLayout(vbox); -} - -void PluginQuickControlsWidget::pagesChanged() { - auto &pages = _pluginHost.quickControlsPages(); - _chooser->clear(); - for (auto &it : pages) - _chooser->addItem(it.second->name, it.second->id); - - selectedPageChanged(); -} - -void PluginQuickControlsWidget::selectedPageChanged() { - auto pageId = _pluginHost.quickControlsSelectedPage(); - auto &pages = _pluginHost.quickControlsPages(); - auto ¶ms = _pluginHost.params(); - auto it = pages.find(pageId); - - for (int i = 0; i < CLAP_QUICK_CONTROLS_COUNT; ++i) { - PluginParam *param = nullptr; - - if (it != pages.end()) { - auto paramId = it->second->param_ids[i]; - auto paramIt = params.find(paramId); - if (paramIt != params.end()) - param = paramIt->second.get(); - } - - _controls[i]->setPluginParam(param); - } -} - -void PluginQuickControlsWidget::selectPageFromChooser(int index) { - clap_id pageId = _chooser->currentData().toUInt(); - _pluginHost.setQuickControlsSelectedPageByHost(pageId); -} diff --git a/examples/host/plugin-quick-controls-widget.hh b/examples/host/plugin-quick-controls-widget.hh @@ -1,26 +0,0 @@ -#pragma once - -#include <QWidget> - -#include <clap/all.h> - -class QComboBox; -class PluginHost; -class PluginQuickControlWidget; -class PluginQuickControlsWidget : public QWidget { - Q_OBJECT; - -public: - PluginQuickControlsWidget(QWidget *parent, PluginHost &pluginHost); - - void pagesChanged(); - void selectedPageChanged(); - -private: - void selectPageFromChooser(int index); - - PluginHost &_pluginHost; - - QComboBox *_chooser = nullptr; - std::array<PluginQuickControlWidget *, CLAP_QUICK_CONTROLS_COUNT> _controls; -}; -\ No newline at end of file diff --git a/examples/host/settings-dialog.cc b/examples/host/settings-dialog.cc @@ -1,27 +0,0 @@ -#include <QDialogButtonBox> -#include <QVBoxLayout> - -#include "settings-widget.hh" -#include "settings.hh" - -#include "settings-dialog.hh" - -SettingsDialog::SettingsDialog(Settings &settings, QWidget *parent) - : QDialog(parent), _settings(settings) { - setModal(true); - setWindowTitle(tr("Settings")); - - QVBoxLayout *vbox = new QVBoxLayout(); - _settingsWidget = new SettingsWidget(settings); - vbox->addWidget(_settingsWidget); - - auto buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); - buttons->show(); - vbox->addWidget(buttons); - connect(buttons, SIGNAL(accepted()), this, SLOT(accept())); - connect(buttons, SIGNAL(rejected()), this, SLOT(reject())); - - setLayout(vbox); - - setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); -} diff --git a/examples/host/settings-dialog.hh b/examples/host/settings-dialog.hh @@ -1,15 +0,0 @@ -#pragma once - -#include <QDialog> - -class Settings; -class SettingsWidget; - -class SettingsDialog : public QDialog { -public: - SettingsDialog(Settings &settings, QWidget *parent = nullptr); - -private: - Settings &_settings; - SettingsWidget *_settingsWidget = nullptr; -}; diff --git a/examples/host/settings-widget.cc b/examples/host/settings-widget.cc @@ -1,20 +0,0 @@ -#include <QTabWidget> -#include <QVBoxLayout> - -#include "audio-settings-widget.hh" -#include "midi-settings-widget.hh" -#include "settings-widget.hh" -#include "settings.hh" - -SettingsWidget::SettingsWidget(Settings &settings) : _settings(settings) { - QVBoxLayout *layout = new QVBoxLayout(); - - _audioSettingsWidget = new AudioSettingsWidget(settings.audioSettings()); - layout->addWidget(_audioSettingsWidget); - - _midiSettingsWidget = new MidiSettingsWidget(settings.midiSettings()); - layout->addWidget(_midiSettingsWidget); - - setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); - setLayout(layout); -} diff --git a/examples/host/settings-widget.hh b/examples/host/settings-widget.hh @@ -1,20 +0,0 @@ -#pragma once - -#include <QWidget> - -class QTabWidget; -class Settings; -class AudioSettingsWidget; -class MidiSettingsWidget; - -class SettingsWidget : public QWidget { - Q_OBJECT -public: - explicit SettingsWidget(Settings &settings); - -private: - QTabWidget *_tabWidget = nullptr; - AudioSettingsWidget *_audioSettingsWidget = nullptr; - MidiSettingsWidget *_midiSettingsWidget = nullptr; - Settings &_settings; -}; diff --git a/examples/host/settings.cc b/examples/host/settings.cc @@ -1,13 +0,0 @@ -#include "settings.hh" - -Settings::Settings() {} - -void Settings::load(QSettings &settings) { - _audioSettings.load(settings); - _midiSettings.load(settings); -} - -void Settings::save(QSettings &settings) const { - _audioSettings.save(settings); - _midiSettings.save(settings); -} diff --git a/examples/host/settings.hh b/examples/host/settings.hh @@ -1,21 +0,0 @@ -#pragma once - -#include "audio-settings.hh" -#include "midi-settings.hh" - -class QSettings; - -class Settings { -public: - Settings(); - - void load(QSettings &settings); - void save(QSettings &settings) const; - - AudioSettings &audioSettings() { return _audioSettings; } - MidiSettings & midiSettings() { return _midiSettings; } - -private: - AudioSettings _audioSettings; - MidiSettings _midiSettings; -}; diff --git a/examples/plugins/CMakeLists.txt b/examples/plugins/CMakeLists.txt @@ -1,36 +0,0 @@ -find_package(Boost REQUIRED COMPONENTS serialization iostreams) - -add_subdirectory(io) -add_subdirectory(gui) - -add_library( - clap-plugins SHARED - clap-entry.cc - parameters.cc - parameters.hh - parameter-interpolator.hh - core-plugin.hh - core-plugin.cc - stream-helper.hh - - path-provider.hh - path-provider.cc - - abstract-gui.hh - remote-gui.hh - remote-gui.cc - - plugs/dc-offset/dc-offset.hh - plugs/dc-offset/dc-offset.cc - plugs/gain/gain.hh - plugs/gain/gain.cc - plugs/transport/transport-info.hh - plugs/transport/transport-info.cc) - -target_link_libraries(clap-plugins clap-plugin-glue clap-io Boost::serialization Boost::iostreams) -target_link_libraries(clap-plugins -Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/linux-clap-plugins.version) -target_link_libraries(clap-plugins -Wl,-z,defs) -set_target_properties(clap-plugins PROPERTIES CXX_STANDARD 20 SUFFIX ".clap" PREFIX "") - -install(TARGETS clap-plugins DESTINATION "${CMAKE_INSTALL_PREFIX}/lib/clap") -install(DIRECTORY qml DESTINATION "${CMAKE_INSTALL_PREFIX}/lib/clap") -\ No newline at end of file diff --git a/examples/plugins/abstract-gui.hh b/examples/plugins/abstract-gui.hh @@ -1,33 +0,0 @@ -#pragma once - -#include <clap/all.h> - -namespace clap { - - class CorePlugin; - class AbstractGui { - public: - AbstractGui(CorePlugin &plugin) : _plugin(plugin) {} - virtual ~AbstractGui() = default; - - virtual void defineParameter(const clap_param_info&) noexcept = 0; - - virtual bool attachCocoa(void *nsView) noexcept = 0; - virtual bool attachWin32(clap_hwnd window) noexcept = 0; - virtual bool attachX11(const char *display_name, unsigned long window) noexcept = 0; - - virtual bool size(uint32_t *width, uint32_t *height) noexcept = 0; - virtual void setScale(double scale) noexcept = 0; - - virtual bool show() noexcept = 0; - virtual bool hide() noexcept = 0; - - virtual void destroy() noexcept = 0; - - protected: - CorePlugin &_plugin; - - bool _isTransportSubscribed = false; - }; - -} // namespace clap -\ No newline at end of file diff --git a/examples/plugins/clap-entry.cc b/examples/plugins/clap-entry.cc @@ -1,86 +0,0 @@ -#include <clap/all.h> - -#include <cstring> -#include <exception> -#include <functional> -#include <sstream> -#include <vector> - -#include "plugs/dc-offset/dc-offset.hh" -#include "plugs/gain/gain.hh" -#include "plugs/transport/transport-info.hh" -#include "path-provider.hh" - -struct PluginEntry { - using create_func = std::function<const clap_plugin *(const clap_host *)>; - - PluginEntry(const clap_plugin_descriptor *d, create_func &&func) - : desc(d), create(std::move(func)) {} - - const clap_plugin_descriptor *desc; - std::function<const clap_plugin *(const clap_host *)> create; -}; - -static std::vector<PluginEntry> g_plugins; -static std::string g_pluginPath; - -template <typename T> -static void addPlugin() { - g_plugins.emplace_back(T::descriptor(), [](const clap_host *host) -> const clap_plugin * { - auto plugin = new T(g_pluginPath, host); - return plugin->clapPlugin(); - }); -} - -static bool clap_init(const char *plugin_path) { - g_pluginPath = plugin_path; - - addPlugin<clap::Gain>(); - addPlugin<clap::DcOffset>(); - addPlugin<clap::TransportInfo>(); - return true; -} - -static void clap_deinit(void) { - g_plugins.clear(); - g_pluginPath.clear(); -} - -static uint32_t clap_get_plugin_count(void) { return g_plugins.size(); } - -static const clap_plugin_descriptor *clap_get_plugin_descriptor(uint32_t index) { - if (index < 0 || index >= g_plugins.size()) { - std::ostringstream msg; - msg << "index out of bounds: " << index << " not in 0.." << g_plugins.size(); - throw std::invalid_argument(msg.str()); - } - - return g_plugins[index].desc; -} - -static const clap_plugin *clap_create_plugin(const clap_host *host, const char *plugin_id) { - for (auto &entry : g_plugins) - if (!strcmp(entry.desc->id, plugin_id)) - return entry.create(host); - return nullptr; -} - -static uint32_t clap_get_invalidation_sources_count(void) { return 0; } - -static const clap_plugin_invalidation_source *clap_get_invalidation_sources(uint32_t index) { - return nullptr; -} - -static void clap_refresh(void) {} - -CLAP_EXPORT const struct clap_plugin_entry clap_plugin_entry = { - CLAP_VERSION, - clap_init, - clap_deinit, - clap_get_plugin_count, - clap_get_plugin_descriptor, - clap_create_plugin, - clap_get_invalidation_sources_count, - clap_get_invalidation_sources, - clap_refresh, -}; -\ No newline at end of file diff --git a/examples/plugins/core-plugin.cc b/examples/plugins/core-plugin.cc @@ -1,274 +0,0 @@ -#include <boost/archive/text_iarchive.hpp> -#include <boost/archive/text_oarchive.hpp> - -#include <chrono> -#include <sstream> -#include <thread> - -#include "core-plugin.hh" -#include "stream-helper.hh" - -namespace clap { - - CorePlugin::CorePlugin(std::unique_ptr<PathProvider> &&pathProvider, - const clap_plugin_descriptor *desc, - const clap_host *host) - : Plugin(desc, host), _pathProvider(std::move(pathProvider)) {} - - bool CorePlugin::init() noexcept { - initTrackInfo(); - return true; - } - - void CorePlugin::initTrackInfo() noexcept { - checkMainThread(); - - assert(!_hasTrackInfo); - if (!canUseTrackInfo()) - return; - - _hasTrackInfo = _hostTrackInfo->get(_host, &_trackInfo); - } - - void CorePlugin::trackInfoChanged() noexcept { - if (!_hostTrackInfo->get(_host, &_trackInfo)) { - _hasTrackInfo = false; - hostMisbehaving( - "clap_host_track_info.get() failed after calling clap_plugin_track_info.changed()"); - return; - } - - _hasTrackInfo = true; - } - - bool CorePlugin::implementsAudioPorts() const noexcept { return true; } - - uint32_t CorePlugin::audioPortsCount(bool is_input) const noexcept { - return is_input ? _audioInputs.size() : _audioOutputs.size(); - } - - bool CorePlugin::audioPortsInfo(uint32_t index, - bool is_input, - clap_audio_port_info *info) const noexcept { - *info = is_input ? _audioInputs[index] : _audioOutputs[index]; - return true; - } - - uint32_t CorePlugin::audioPortsConfigCount() const noexcept { return _audioConfigs.size(); } - - bool CorePlugin::audioPortsGetConfig(uint32_t index, - clap_audio_ports_config *config) const noexcept { - *config = _audioConfigs[index]; - return true; - } - - bool CorePlugin::audioPortsSetConfig(clap_id config_id) noexcept { return false; } - - bool CorePlugin::stateSave(clap_ostream *stream) noexcept { - try { - OStream os(stream); - boost::archive::text_oarchive ar(os); - ar << _parameters; - } catch (...) { - return false; - } - return true; - } - - bool CorePlugin::stateLoad(clap_istream *stream) noexcept { - try { - IStream is(stream); - boost::archive::text_iarchive ar(is); - ar >> _parameters; - } catch (...) { - return false; - } - return true; - } - - bool CorePlugin::guiCreate() noexcept { - _remoteGui.reset(new RemoteGui(*this)); - - if (!_remoteGui->spawn()) { - _remoteGui.reset(); - return false; - } - - if (!_remoteGui) - return false; - - guiDefineParameters(); - return true; - } - - void CorePlugin::guiDefineParameters() { - for (int i = 0; i < paramsCount(); ++i) { - clap_param_info info; - paramsInfo(i, &info); - _remoteGui->defineParameter(info); - } - } - - void CorePlugin::guiDestroy() noexcept { - if (_remoteGui) - _remoteGui.reset(); - } - - bool CorePlugin::guiSize(uint32_t *width, uint32_t *height) noexcept { - if (!_remoteGui) - return false; - - return _remoteGui->size(width, height); - } - - void CorePlugin::guiSetScale(double scale) noexcept { - if (_remoteGui) - _remoteGui->setScale(scale); - } - - void CorePlugin::guiShow() noexcept { - if (_remoteGui) - _remoteGui->show(); - } - - void CorePlugin::guiHide() noexcept { - if (_remoteGui) - _remoteGui->hide(); - } - - void CorePlugin::onFd(clap_fd fd, uint32_t flags) noexcept { - if (_remoteGui && fd == _remoteGui->fd()) - _remoteGui->onFd(flags); - } - - void CorePlugin::onTimer(clap_id timerId) noexcept { - if (_remoteGui && timerId == _remoteGui->timerId()) - _remoteGui->onTimer(); - } - - bool CorePlugin::guiX11Attach(const char *displayName, unsigned long window) noexcept { - if (_remoteGui) - return _remoteGui->attachX11(displayName, window); - - return false; - } - - bool CorePlugin::guiWin32Attach(clap_hwnd window) noexcept { - if (_remoteGui) - return _remoteGui->attachWin32(window); - - return false; - } - - bool CorePlugin::guiCocoaAttach(void *nsView) noexcept { - if (_remoteGui) - return _remoteGui->attachCocoa(nsView); - - return false; - } - - bool CorePlugin::guiFreeStandingOpen() noexcept { - // TODO - return false; - } - - void CorePlugin::guiAdjust(clap_id paramId, double value, clap_event_param_flags flags) { - GuiToPluginValue item{paramId, value, flags}; - - // very highly likely to succeed - while (!_guiToPluginQueue.tryPush(item)) { - if (canUseParams()) - _hostParams->request_flush(_host); - - std::this_thread::sleep_for(std::chrono::milliseconds(1)); - } - } - - void CorePlugin::processGuiEvents(const clap_process *process) { - GuiToPluginValue value; - while (_guiToPluginQueue.tryPop(value)) { - auto p = _parameters.getById(value.paramId); - if (!p) - return; - p->setValueSmoothed(value.value, std::max<int>(process->frames_count, 128)); - - clap_event ev; - ev.time = 0; - ev.type = CLAP_EVENT_PARAM_VALUE; - ev.param_value.param_id = value.paramId; - ev.param_value.value = value.value; - ev.param_value.channel = -1; - ev.param_value.key = -1; - ev.param_value.flags = value.flags; - ev.param_value.cookie = p; - - process->out_events->push_back(process->out_events, &ev); - } - - if (!_hasTransportCopy) { - if (process->transport) { - _hasTransport = true; - _transportCopy = *process->transport; - } else - _hasTransport = false; - - _hasTransportCopy = true; - } - } - - uint32_t CorePlugin::processEvents(const clap_process *process, - uint32_t &index, - uint32_t count, - uint32_t time) { - for (; index < count; ++index) { - auto ev = process->in_events->get(process->in_events, index); - - if (ev->time < time) { - hostMisbehaving("Events must be ordered by time"); - std::terminate(); - } - - if (ev->time > time) { - // This event is in the future - return std::min(ev->time, process->frames_count); - } - - switch (ev->type) { - case CLAP_EVENT_PARAM_VALUE: { - auto p = reinterpret_cast<Parameter *>(ev->param_value.cookie); - if (p) { - if (p->info().id != ev->param_value.param_id) { - std::ostringstream os; - os << "Host provided invalid cookie for param id: " << ev->param_value.param_id; - hostMisbehaving(os.str()); - std::terminate(); - } - - p->setValueSmoothed(ev->param_value.value, _paramSmoothingDuration); - // p->setValueImmediately(ev->param_value.value); - _pluginToGuiQueue.set(p->info().id, {ev->param_value.value, p->modulation()}); - } - break; - } - - case CLAP_EVENT_PARAM_MOD: { - auto p = reinterpret_cast<Parameter *>(ev->param_mod.cookie); - if (p) { - if (p->info().id != ev->param_mod.param_id) { - std::ostringstream os; - os << "Host provided invalid cookie for param id: " << ev->param_mod.param_id; - hostMisbehaving(os.str()); - std::terminate(); - } - - p->setModulationSmoothed(ev->param_mod.amount, _paramSmoothingDuration); - _pluginToGuiQueue.set(p->info().id, {p->value(), ev->param_mod.amount}); - } - break; - } - } - } - - return process->frames_count; - } -} // namespace clap -\ No newline at end of file diff --git a/examples/plugins/core-plugin.hh b/examples/plugins/core-plugin.hh @@ -1,195 +0,0 @@ -#pragma once - -#include <memory> - -#include <clap-plugin.hh> - -#include "parameters.hh" -#include "path-provider.hh" -#include "remote-gui.hh" - -#include "../common/param-queue.hh" -#include "../common/reducing-param-queue.hxx" - -namespace clap { - class CorePlugin : public Plugin { - public: - CorePlugin(std::unique_ptr<PathProvider> &&pathProvider, - const clap_plugin_descriptor *desc, - const clap_host *host); - - const PathProvider &pathProvider() const noexcept { return *_pathProvider; } - - protected: - //-------------// - // clap_plugin // - //-------------// - bool init() noexcept override; - void initTrackInfo() noexcept; - - //------------------------// - // clap_plugin_track_info // - //------------------------// - bool implementsTrackInfo() const noexcept override { return true; } - void trackInfoChanged() noexcept override; - - //-------------------------// - // clap_plugin_audio_ports // - //-------------------------// - bool implementsAudioPorts() const noexcept override; - uint32_t audioPortsCount(bool is_input) const noexcept override; - bool audioPortsInfo(uint32_t index, - bool is_input, - clap_audio_port_info *info) const noexcept override; - uint32_t audioPortsConfigCount() const noexcept override; - bool audioPortsGetConfig(uint32_t index, - clap_audio_ports_config *config) const noexcept override; - bool audioPortsSetConfig(clap_id config_id) noexcept override; - - //--------------------// - // clap_plugin_params // - //--------------------// - bool implementsParams() const noexcept override { return true; } - - uint32_t paramsCount() const noexcept override { return _parameters.count(); } - - bool paramsInfo(int32_t paramIndex, clap_param_info *info) const noexcept override { - *info = _parameters.getByIndex(paramIndex)->info(); - return true; - } - - virtual bool paramsValue(clap_id paramId, double *value) noexcept override { - *value = _parameters.getById(paramId)->value(); - return true; - } - - virtual bool paramsValueToText(clap_id paramId, - double value, - char *display, - uint32_t size) noexcept override { - // TODO - return false; - } - - virtual bool - paramsTextToValue(clap_id param_id, const char *display, double *value) noexcept override { - // TODO - return false; - } - - //-------------------// - // clap_plugin_state // - //-------------------// - bool implementsState() const noexcept override { return true; } - bool stateSave(clap_ostream *stream) noexcept override; - bool stateLoad(clap_istream *stream) noexcept override; - - //-----------------// - // clap_plugin_gui // - //-----------------// - bool implementsGui() const noexcept override { return true; } - bool guiCreate() noexcept override; - void guiDestroy() noexcept override; - bool guiCanResize() const noexcept override { return false; } - bool guiSize(uint32_t *width, uint32_t *height) noexcept override; - void guiRoundSize(uint32_t *width, uint32_t *height) noexcept override { - guiSize(width, height); - } - void guiSetScale(double scale) noexcept override; - void guiShow() noexcept override; - void guiHide() noexcept override; - void guiDefineParameters(); - - //---------------------// - // clap_plugin_gui_x11 // - //---------------------// - bool implementsGuiX11() const noexcept override { return true; } - bool guiX11Attach(const char *displayName, unsigned long window) noexcept override; - - //-----------------------// - // clap_plugin_gui_win32 // - //-----------------------// - bool implementsGuiWin32() const noexcept override { return true; } - bool guiWin32Attach(clap_hwnd window) noexcept override; - - //-----------------------// - // clap_plugin_gui_cocoa // - //-----------------------// - bool implementsGuiCocoa() const noexcept override { return true; } - bool guiCocoaAttach(void *nsView) noexcept override; - - //-------------------------------// - // clap_plugin_gui_free_standing // - //-------------------------------// - bool implementsGuiFreeStanding() const noexcept override { return true; } - bool guiFreeStandingOpen() noexcept override; - - ////////////////////// - // Cached Host Info // - ////////////////////// - bool hasTrackInfo() const noexcept { return _hasTrackInfo; } - const clap_track_info &trackInfo() const noexcept { - assert(_hasTrackInfo); - return _trackInfo; - } - uint32_t trackChannelCount() const noexcept { - return _hasTrackInfo ? _trackInfo.channel_count : 2; - } - clap_chmap trackChannelMap() const noexcept { - return _hasTrackInfo ? _trackInfo.channel_map : CLAP_CHMAP_STEREO; - } - - //---------------------------// - // clap_plugin_timer_support // - //---------------------------// - bool implementsTimerSupport() const noexcept override { return true; } - void onTimer(clap_id timerId) noexcept override; - - //------------------------// - // clap_plugin_fd_support // - //------------------------// - bool implementsFdSupport() const noexcept override { return true; } - void onFd(clap_fd fd, uint32_t flags) noexcept override; - - protected: - friend class RemoteGui; - - void guiAdjust(clap_id paramId, double value, clap_event_param_flags flags); - void processGuiEvents(const clap_process *process); - uint32_t - processEvents(const clap_process *process, uint32_t &index, uint32_t count, uint32_t time); - - struct GuiToPluginValue { - clap_id paramId; - double value; - clap_event_param_flags flags; - }; - - struct PluginToGuiValue { - double value; - double mod; - }; - - ParamQueue<GuiToPluginValue, 32> _guiToPluginQueue; - ReducingParamQueue<PluginToGuiValue> _pluginToGuiQueue; - - std::unique_ptr<PathProvider> _pathProvider; - - bool _hasTrackInfo = false; - clap_track_info _trackInfo; - - std::vector<clap_audio_port_info> _audioInputs; - std::vector<clap_audio_port_info> _audioOutputs; - std::vector<clap_audio_ports_config> _audioConfigs; - - std::unique_ptr<RemoteGui> _remoteGui; - - Parameters _parameters; - - static const constexpr uint32_t _paramSmoothingDuration = 64; - - bool _hasTransportCopy = false; - bool _hasTransport = false; - clap_event_transport _transportCopy; - }; -} // namespace clap -\ No newline at end of file diff --git a/examples/plugins/gui/CMakeLists.txt b/examples/plugins/gui/CMakeLists.txt @@ -1,22 +0,0 @@ -set(CMAKE_INCLUDE_CURRENT_DIR ON) -set(CMAKE_AUTOMOC ON) - -find_package(Qt6 COMPONENTS Quick REQUIRED) - -add_executable(clap-gui - main.cc - - application.hh - application.cc - - parameter-proxy.hh - parameter-proxy.cc - plugin-proxy.hh - plugin-proxy.cc - transport-proxy.hh - transport-proxy.cc -) -target_link_libraries(clap-gui clap-io Qt6::Quick) - -set_target_properties(clap-gui PROPERTIES CXX_STANDARD 17) -install(TARGETS clap-gui DESTINATION "${CMAKE_INSTALL_PREFIX}/bin") -\ No newline at end of file diff --git a/examples/plugins/gui/application.cc b/examples/plugins/gui/application.cc @@ -1,210 +0,0 @@ -#include <QCommandLineParser> -#include <QQmlContext> -#include <QQmlEngine> -#include <QQuickItem> -#include <QQuickView> -#include <QWindow> - -#include "../io/messages.hh" -#include "application.hh" - -Application::Application(int &argc, char **argv) - : QGuiApplication(argc, argv), _quickView(new QQuickView()) { - - bool waitForDebbugger = false; - while (waitForDebbugger) - ; - - QCommandLineParser parser; - - QCommandLineOption skinOpt("skin", tr("path to the skin directory"), tr("path")); - QCommandLineOption socketOpt("socket", tr("path to the QML skin"), tr("path")); - QCommandLineOption qmlLibOpt("qml-import", tr("QML import path"), tr("path")); - - parser.addOption(skinOpt); - parser.addOption(socketOpt); - parser.addOption(qmlLibOpt); - parser.addHelpOption(); - - parser.process(*this); - - qmlRegisterType<ParameterProxy>("org.clap", 1, 0, "ParameterProxy"); - qmlRegisterType<TransportProxy>("org.clap", 1, 0, "TransportProxy"); - qmlRegisterType<PluginProxy>("org.clap", 1, 0, "PluginProxy"); - - _pluginProxy = new PluginProxy(this); - _transportProxy = new TransportProxy(this); - - //////////////////////// - // I/O initialization // - //////////////////////// - - auto socket = parser.value(socketOpt).toULongLong(); - - _remoteChannel.reset(new clap::RemoteChannel( - [this](const clap::RemoteChannel::Message &msg) { onMessage(msg); }, *this, socket, false)); - - _socketReadNotifier.reset(new QSocketNotifier(socket, QSocketNotifier::Read, this)); - connect(_socketReadNotifier.get(), - &QSocketNotifier::activated, - [this](QSocketDescriptor socket, QSocketNotifier::Type type) { - _remoteChannel->onRead(); - if (!_remoteChannel->isOpen()) - quit(); - }); - - _socketWriteNotifier.reset(new QSocketNotifier(socket, QSocketNotifier::Write, this)); - connect(_socketWriteNotifier.get(), - &QSocketNotifier::activated, - [this](QSocketDescriptor socket, QSocketNotifier::Type type) { - _remoteChannel->onWrite(); - if (!_remoteChannel->isOpen()) { - quit(); - } - }); - - _socketErrorNotifier.reset(new QSocketNotifier(socket, QSocketNotifier::Exception, this)); - connect(_socketErrorNotifier.get(), - &QSocketNotifier::activated, - [this](QSocketDescriptor socket, QSocketNotifier::Type type) { - _remoteChannel->onError(); - quit(); - }); - - _socketReadNotifier->setEnabled(true); - _socketWriteNotifier->setEnabled(false); - _socketErrorNotifier->setEnabled(false); - - //////////////////////// - // QML initialization // - //////////////////////// - - auto qmlContext = _quickView->engine()->rootContext(); - for (const auto &str : parser.values(qmlLibOpt)) - _quickView->engine()->addImportPath(str); - qmlContext->setContextProperty("plugin", _pluginProxy); - qmlContext->setContextProperty("transport", _transportProxy); - - _quickView->setSource(parser.value(skinOpt) + "/main.qml"); -} - -void Application::modifyFd(clap_fd_flags flags) { - _socketReadNotifier->setEnabled(flags & CLAP_FD_READ); - _socketWriteNotifier->setEnabled(flags & CLAP_FD_WRITE); - _socketErrorNotifier->setEnabled(flags & CLAP_FD_ERROR); -} - -void Application::removeFd() { - _socketReadNotifier.reset(); - _socketWriteNotifier.reset(); - _socketErrorNotifier.reset(); - quit(); -} - -void Application::onMessage(const clap::RemoteChannel::Message &msg) { - switch (msg.type) { - case clap::messages::kDestroyRequest: - clap::messages::DestroyResponse rp; - _remoteChannel->sendResponseAsync(rp, msg.cookie); - quit(); - break; - - case clap::messages::kUpdateTransportRequest: { - clap::messages::UpdateTransportRequest rq; - msg.get(rq); - _transportProxy->update(rq.hasTransport, rq.transport); - break; - } - - case clap::messages::kDefineParameterRequest: { - clap::messages::DefineParameterRequest rq; - msg.get(rq); - _pluginProxy->defineParameter(rq.info); - break; - } - - case clap::messages::kParameterValueRequest: { - clap::messages::ParameterValueRequest rq; - msg.get(rq); - auto p = _pluginProxy->param(rq.paramId); - p->setValueFromPlugin(rq.value); - p->setModulationFromPlugin(rq.modulation); - break; - } - - case clap::messages::kSizeRequest: { - clap::messages::SizeResponse rp; - auto rootItem = _quickView->rootObject(); - rp.width = rootItem ? rootItem->width() : 500; - rp.height = rootItem ? rootItem->height() : 300; - _remoteChannel->sendResponseAsync(rp, msg.cookie); - break; - } - - case clap::messages::kAttachX11Request: { - clap::messages::AttachX11Request rq; - clap::messages::AttachResponse rp{false}; - msg.get(rq); - -#ifdef Q_OS_LINUX - _hostWindow.reset(QWindow::fromWinId(rq.window)); - _quickView->setParent(_hostWindow.get()); - _quickView->show(); - sync(); - rp.succeed = true; -#endif - - _remoteChannel->sendResponseAsync(rp, msg.cookie); - break; - } - - case clap::messages::kAttachWin32Request: { - clap::messages::AttachWin32Request rq; - clap::messages::AttachResponse rp{false}; - msg.get(rq); - -#ifdef Q_OS_WIN - hostWindow_.reset(QWindow::fromWinId(rq.hwnd)); - quickView_->setParent(hostWindow_.get()); - _quickView->show(); - sync(); - rp.succeed = true; -#endif - - _remoteChannel->sendResponseAsync(rp, msg.cookie); - break; - } - - case clap::messages::kAttachCocoaRequest: { - clap::messages::AttachCocoaRequest rq; - clap::messages::AttachResponse rp{false}; - - msg.get(rq); - -#ifdef Q_OS_MACOS - hostWindow_.reset(QWindow::fromWinId(rq.nsView)); - quickView_->setParent(hostWindow_.get()); - _quickView->show(); - sync(); - rp.succeed = true; -#endif - - _remoteChannel->sendResponseAsync(rp, msg.cookie); - break; - } - - case clap::messages::kShowRequest: { - _quickView->show(); - clap::messages::ShowResponse rp; - _remoteChannel->sendResponseAsync(rp, msg.cookie); - break; - } - - case clap::messages::kHideRequest: { - _quickView->hide(); - clap::messages::HideResponse rp; - _remoteChannel->sendResponseAsync(rp, msg.cookie); - break; - } - } -} diff --git a/examples/plugins/gui/application.hh b/examples/plugins/gui/application.hh @@ -1,38 +0,0 @@ -#pragma once - -#include <QGuiApplication> -#include <QSocketNotifier> -#include <QWindow> - -#include "../io/remote-channel.hh" -#include "plugin-proxy.hh" -#include "transport-proxy.hh" - -class QQuickView; - -class Application : public QGuiApplication, public clap::RemoteChannel::EventControl { - Q_OBJECT; - -public: - Application(int& argc, char **argv); - - clap::RemoteChannel& remoteChannel() const { return *_remoteChannel; } - void modifyFd(clap_fd_flags flags) override; - void removeFd() override; - - static Application& instance() { return *dynamic_cast<Application *>(QGuiApplication::instance()); } - -private: - void onMessage(const clap::RemoteChannel::Message& msg); - - QQuickView *_quickView = nullptr; - std::unique_ptr<QSocketNotifier> _socketReadNotifier; - std::unique_ptr<QSocketNotifier> _socketWriteNotifier; - std::unique_ptr<QSocketNotifier> _socketErrorNotifier; - - std::unique_ptr<QWindow> _hostWindow = nullptr; - - std::unique_ptr<clap::RemoteChannel> _remoteChannel; - PluginProxy *_pluginProxy = nullptr; - TransportProxy *_transportProxy = nullptr; -}; -\ No newline at end of file diff --git a/examples/plugins/gui/main.cc b/examples/plugins/gui/main.cc @@ -1,7 +0,0 @@ -#include "application.hh" - -int main(int argc, char **argv) { - Application app(argc, argv); - - return app.exec(); -} -\ No newline at end of file diff --git a/examples/plugins/gui/parameter-proxy.cc b/examples/plugins/gui/parameter-proxy.cc @@ -1,119 +0,0 @@ -#include "parameter-proxy.hh" -#include "../io/messages.hh" -#include "application.hh" - -ParameterProxy::ParameterProxy(const clap_param_info &info, QObject *parent) - : QObject(parent), _id(info.id), _name(info.name), _module(info.module), - _value(info.default_value), _minValue(info.min_value), _maxValue(info.max_value), - _defaultValue(info.default_value) {} - -ParameterProxy::ParameterProxy(clap_id param_id, QObject *parent) - : QObject(parent), _id(param_id) {} - -void ParameterProxy::redefine(const clap_param_info &info) { - Q_ASSERT(_id == info.id); - - if (_name != info.name) { - _name = info.name; - emit nameChanged(); - } - - if (_module != info.module) { - _module = info.module; - emit moduleChanged(); - } - - setMinValueFromPlugin(info.min_value); - setMaxValueFromPlugin(info.max_value); - setDefaultValueFromPlugin(info.default_value); - - setValueFromPlugin(std::min(_maxValue, std::max(_minValue, _value))); -} - -void ParameterProxy::setIsAdjusting(bool isAdjusting) { - if (isAdjusting == _isAdjusting) - return; - - _isAdjusting = isAdjusting; - clap_event_param_flags flags = CLAP_EVENT_PARAM_SHOULD_RECORD; - flags |= isAdjusting ? CLAP_EVENT_PARAM_BEGIN_ADJUST : CLAP_EVENT_PARAM_END_ADJUST; - clap::messages::AdjustRequest rq{_id, _value, flags}; - Application::instance().remoteChannel().sendRequestAsync(rq); - emit isAdjustingChanged(); -} - -void ParameterProxy::setValueFromUI(double value) { - value = clip(value); - if (value == _value) - return; - - _value = value; - - clap::messages::AdjustRequest rq{_id, _value, CLAP_EVENT_PARAM_SHOULD_RECORD}; - Application::instance().remoteChannel().sendRequestAsync(rq); - emit valueChanged(); - emit finalValueChanged(); -} - -void ParameterProxy::setValueFromPlugin(double value) { - if (_isAdjusting) - return; - - value = clip(value); - if (value == _value) - return; - - _value = value; - emit valueChanged(); - emit finalValueChanged(); -} - -void ParameterProxy::setModulationFromPlugin(double mod) { - if (mod == _modulation) - return; - - _modulation = mod; - emit modulationChanged(); - emit finalValueChanged(); -} - -void ParameterProxy::setMinValueFromPlugin(double minValue) { - if (minValue == _minValue) - return; - - _minValue = minValue; - emit minValueChanged(); -} - -void ParameterProxy::setMaxValueFromPlugin(double maxValue) { - if (maxValue == _maxValue) - return; - - _maxValue = maxValue; - emit maxValueChanged(); -} - -void ParameterProxy::setDefaultValueFromPlugin(double defaultValue) { - if (defaultValue == _defaultValue) - return; - - _defaultValue = defaultValue; - emit defaultValueChanged(); -} - -void ParameterProxy::setToDefault() { - bool wasAdjusting = _isAdjusting; - - if (!wasAdjusting) - setIsAdjusting(true); - setValueFromUI(_defaultValue); - if (!wasAdjusting) - setIsAdjusting(false); -} - -void ParameterProxy::setIsHovered(bool value) { - if (_isHovered == value) - return; - _isHovered = value; - emit isHoveredChanged(); -} diff --git a/examples/plugins/gui/parameter-proxy.hh b/examples/plugins/gui/parameter-proxy.hh @@ -1,101 +0,0 @@ -#pragma once - -#include <QObject> - -#include <clap/all.h> - -class ParameterProxy : public QObject { - Q_OBJECT - Q_PROPERTY(uint32_t id READ getId) - Q_PROPERTY(QString name READ getName NOTIFY nameChanged) - Q_PROPERTY(QString module READ getModule NOTIFY moduleChanged) - Q_PROPERTY(double value READ getValue WRITE setValueFromUI NOTIFY valueChanged) - Q_PROPERTY(double normalizedValue READ getNormalizedValue WRITE setNormalizedValueFromUI NOTIFY - valueChanged) - Q_PROPERTY(double modulation READ getModulation NOTIFY modulationChanged) - Q_PROPERTY(double normalizedModulation READ getNormalizedModulation NOTIFY modulationChanged) - Q_PROPERTY(double finalValue READ getFinalValue NOTIFY finalValueChanged) - Q_PROPERTY(double normalizedFinalValue READ getNormalizedFinalValue NOTIFY finalValueChanged) - Q_PROPERTY(double minValue READ getMinValue NOTIFY minValueChanged) - Q_PROPERTY(double maxValue READ getMaxValue NOTIFY maxValueChanged) - Q_PROPERTY(double defaultValue READ getDefaultValue NOTIFY defaultValueChanged) - Q_PROPERTY(bool isAdjusting READ isAdjusting WRITE setIsAdjusting NOTIFY isAdjustingChanged) - Q_PROPERTY(bool isHovered READ isHovered WRITE setIsHovered NOTIFY isHoveredChanged) - -public: - explicit ParameterProxy(const clap_param_info &info, QObject *parent = nullptr); - explicit ParameterProxy(clap_id param_id, QObject *parent = nullptr); - - void redefine(const clap_param_info &info); - - uint32_t getId() const { return _id; } - const QString &getModule() const { return _module; } - const QString &getName() const { return _name; } - - double getValue() const { return _value; } - void setValueFromUI(double value); - void setValueFromPlugin(double value); - - double getNormalizedValue() const { return normalize(getValue()); } - void setNormalizedValueFromUI(double value) { setValueFromUI(denormalize(value)); } - - double getModulation() const { return _modulation; } - void setModulationFromPlugin(double mod); - - double getNormalizedModulation() const { return normalize(getModulation()); } - - double getFinalValue() const { return clip(_value + _modulation); } - double getNormalizedFinalValue() const { return normalize(getFinalValue()); } - - bool isAdjusting() const { return _isAdjusting; } - void setIsAdjusting(bool isAdjusting); - - double getMinValue() const { return _minValue; } - void setMinValueFromPlugin(double minValue); - double getMaxValue() const { return _maxValue; } - void setMaxValueFromPlugin(double maxValue); - double getDefaultValue() const { return _defaultValue; } - void setDefaultValueFromPlugin(double defaultValue); - - double clip(double v) const { return std::min(_maxValue, std::max(_minValue, v)); } - static double clipNormalized(double v) { return std::min(1., std::max(0., v)); } - - double normalize(double value) const { - double delta = _maxValue - _minValue; - return delta != 0 ? ((value - _minValue) / delta) : 0; - } - - double denormalize(double value) const { - double delta = _maxValue - _minValue; - return _minValue + value * delta; - } - - Q_INVOKABLE void setToDefault(); - - bool isHovered() const { return _isHovered; } - void setIsHovered(bool value); - -signals: - void nameChanged(); - void moduleChanged(); - void valueChanged(); - void modulationChanged(); - void finalValueChanged(); - void minValueChanged(); - void maxValueChanged(); - void defaultValueChanged(); - void isHoveredChanged(); - void isAdjustingChanged(); - -private: - const uint32_t _id; - QString _name; - QString _module; - double _value = 0; - double _modulation = 0; - double _minValue = 0; - double _maxValue = 0; - double _defaultValue = 0; - bool _isAdjusting = false; - bool _isHovered = false; -}; -\ No newline at end of file diff --git a/examples/plugins/gui/plugin-proxy.cc b/examples/plugins/gui/plugin-proxy.cc @@ -1,21 +0,0 @@ -#include "plugin-proxy.hh" - -PluginProxy::PluginProxy(QObject *parent) : QObject(parent) {} - -ParameterProxy *PluginProxy::param(clap_id paramId) { - auto it = _parameters.find(paramId); - if (it != _parameters.end()) - return it->second; - - auto *p = new ParameterProxy(paramId, this); - _parameters.insert_or_assign(paramId, p); - return p; -} - -QString PluginProxy::toString() const { return "Plugin"; } - -void PluginProxy::defineParameter(const clap_param_info &info) { - auto it = _parameters.emplace(info.id, new ParameterProxy(info, this)); - if (!it.second) - it.first->second->redefine(info); -} -\ No newline at end of file diff --git a/examples/plugins/gui/plugin-proxy.hh b/examples/plugins/gui/plugin-proxy.hh @@ -1,23 +0,0 @@ -#pragma once - -#include <QHash> -#include <QObject> - -#include <clap/all.h> - -#include "parameter-proxy.hh" - -class PluginProxy : public QObject { - Q_OBJECT - -public: - explicit PluginProxy(QObject *parent = nullptr); - - void defineParameter(const clap_param_info& info); - - Q_INVOKABLE ParameterProxy *param(clap_id paramId); - Q_INVOKABLE QString toString() const; - -private: - std::unordered_map<clap_id, ParameterProxy *> _parameters; -}; -\ No newline at end of file diff --git a/examples/plugins/gui/transport-proxy.cc b/examples/plugins/gui/transport-proxy.cc @@ -1,70 +0,0 @@ -#include "transport-proxy.hh" -#include "../io/messages.hh" -#include "application.hh" - -TransportProxy::TransportProxy(QObject *parent) : QObject(parent) {} - -void TransportProxy::update(bool hasTransport, const clap_event_transport &t) { - update<bool>(_hasTransport, hasTransport, &TransportProxy::hasTransportChanged); - - update<bool>(_hasTempo, t.flags & CLAP_TRANSPORT_HAS_TEMPO, &TransportProxy::hasTempoChanged); - update<bool>(_hasBeatsTimeline, - t.flags & CLAP_TRANSPORT_HAS_BEATS_TIMELINE, - &TransportProxy::hasBeatsTimelineChanged); - update<bool>(_hasSecondsTimeline, - t.flags & CLAP_TRANSPORT_HAS_SECONDS_TIMELINE, - &TransportProxy::hasSecondsTimelineChanged); - update<bool>(_hasTimeSignature, - t.flags & CLAP_TRANSPORT_HAS_TIME_SIGNATURE, - &TransportProxy::hasTimeSignatureChanged); - update<bool>(_isPlaying, t.flags & CLAP_TRANSPORT_IS_PLAYING, &TransportProxy::isPlayingChanged); - update<bool>( - _isRecording, t.flags & CLAP_TRANSPORT_IS_RECORDING, &TransportProxy::isRecordingChanged); - update<bool>( - _isLoopActive, t.flags & CLAP_TRANSPORT_IS_LOOP_ACTIVE, &TransportProxy::isLoopActiveChanged); - update<bool>(_isWithinPreRoll, - t.flags & CLAP_TRANSPORT_IS_WITHIN_PRE_ROLL, - &TransportProxy::isWithinPreRollChanged); - - update<double>(_songPositionBeats, - t.song_pos_beats / double(CLAP_BEATTIME_FACTOR), - &TransportProxy::songPositionBeatsChanged); - update<double>(_songPositionSeconds, - t.song_pos_seconds / double(CLAP_SECTIME_FACTOR), - &TransportProxy::songPositionSecondsChanged); - - update<double>(_tempo, t.tempo, &TransportProxy::tempoChanged); - - update<double>( - _barStart, t.bar_start / double(CLAP_BEATTIME_FACTOR), &TransportProxy::barStartChanged); - update<int>(_barNumber, t.bar_number, &TransportProxy::barNumberChanged); - - update<double>(_loopStartBeats, - t.loop_start_beats / double(CLAP_BEATTIME_FACTOR), - &TransportProxy::loopStartBeatsChanged); - update<double>(_loopEndBeats, - t.loop_end_beats / double(CLAP_BEATTIME_FACTOR), - &TransportProxy::loopEndBeatsChanged); - update<double>(_loopStartSeconds, - t.loop_start_seconds / double(CLAP_SECTIME_FACTOR), - &TransportProxy::loopStartSecondsChanged); - update<double>(_loopEndSeconds, - t.loop_end_seconds / double(CLAP_SECTIME_FACTOR), - &TransportProxy::loopEndSecondsChanged); - - update<int>(_timeSignatureNumerator, t.tsig_num, &TransportProxy::timeSignatureNumeratorChanged); - update<int>( - _timeSignatureDenominator, t.tsig_denom, &TransportProxy::timeSignatureDenominatorChanged); - - emit updated(); -} - -void TransportProxy::setIsSubscribed(bool value) { - if (value == _isSubscribed) - return; - - _isSubscribed = value; - isSubscribedChanged(); - clap::messages::SubscribeToTransportRequest rq{value}; - Application::instance().remoteChannel().sendRequestAsync(rq); -} diff --git a/examples/plugins/gui/transport-proxy.hh b/examples/plugins/gui/transport-proxy.hh @@ -1,160 +0,0 @@ -#pragma once - -#include <QObject> - -#include <clap/all.h> - -class TransportProxy : public QObject { - Q_OBJECT - Q_PROPERTY(bool hasTransport READ hasTransport NOTIFY hasTransportChanged) - Q_PROPERTY(bool isSubscribed READ isSubscribed WRITE setIsSubscribed NOTIFY isSubscribedChanged) - - Q_PROPERTY(bool hasTempo READ hasTempo NOTIFY hasTempoChanged) - Q_PROPERTY(bool hasBeatsTimeline READ hasBeatsTimeline NOTIFY hasBeatsTimelineChanged) - Q_PROPERTY(bool hasSecondsTimeline READ hasSecondsTimeline NOTIFY hasSecondsTimelineChanged) - Q_PROPERTY(bool hasTimeSignature READ hasTimeSignature NOTIFY hasTimeSignatureChanged) - Q_PROPERTY(bool isPlaying READ isPlaying NOTIFY isPlayingChanged) - Q_PROPERTY(bool isRecording READ isRecording NOTIFY isRecordingChanged) - Q_PROPERTY(bool isLoopActive READ isLoopActive NOTIFY isLoopActiveChanged) - Q_PROPERTY(bool isWithinPreRoll READ isWithinPreRoll NOTIFY isWithinPreRollChanged) - - Q_PROPERTY(double songPositionBeats READ getSongPositionBeats NOTIFY songPositionBeatsChanged) - Q_PROPERTY( - double songPositionSeconds READ getSongPositionSeconds NOTIFY songPositionSecondsChanged) - - Q_PROPERTY(double tempo READ getTempo NOTIFY tempoChanged) - - Q_PROPERTY(double barStart READ getBarStart NOTIFY barStartChanged) - Q_PROPERTY(int barNumber READ getBarNumber NOTIFY barNumberChanged) - - Q_PROPERTY(double loopStartBeats READ getLoopStartBeats NOTIFY loopStartBeatsChanged) - Q_PROPERTY(double loopEndBeats READ getLoopEndBeats NOTIFY loopEndBeatsChanged) - Q_PROPERTY(double loopStartSeconds READ getLoopStartSeconds NOTIFY loopStartSecondsChanged) - Q_PROPERTY(double loopEndSeconds READ getLoopEndSeconds NOTIFY loopEndSecondsChanged) - - Q_PROPERTY(int timeSignatureNumerator READ getTimeSignatureNumerator NOTIFY - timeSignatureNumeratorChanged) - Q_PROPERTY(int timeSignatureDenominator READ getTimeSignatureDenominator NOTIFY - timeSignatureDenominatorChanged) - -public: - explicit TransportProxy(QObject *parent = nullptr); - - void update(bool hasTransport, const clap_event_transport &transport); - - [[nodiscard]] bool isSubscribed() const noexcept { return _isSubscribed; } - void setIsSubscribed(bool value); - - [[nodiscard]] bool hasTransport() const noexcept { return _hasTransport; } - - [[nodiscard]] bool hasTempo() const noexcept { return _hasTempo; } - - [[nodiscard]] bool hasBeatsTimeline() const noexcept { return _hasBeatsTimeline; } - - [[nodiscard]] bool hasSecondsTimeline() const noexcept { return _hasSecondsTimeline; } - - [[nodiscard]] bool hasTimeSignature() const noexcept { return _hasTimeSignature; } - - [[nodiscard]] bool isPlaying() const noexcept { return _isPlaying; } - - [[nodiscard]] bool isRecording() const noexcept { return _isRecording; } - - [[nodiscard]] bool isLoopActive() const noexcept { return _isLoopActive; } - - [[nodiscard]] bool isWithinPreRoll() const noexcept { return _isWithinPreRoll; } - - [[nodiscard]] double getSongPositionBeats() const noexcept { return _songPositionBeats; } - - [[nodiscard]] double getSongPositionSeconds() const noexcept { return _songPositionSeconds; } - - [[nodiscard]] double getTempo() const noexcept { return _tempo; } - - [[nodiscard]] double getBarStart() const noexcept { return _barStart; } - - [[nodiscard]] int getBarNumber() const noexcept { return _barNumber; } - - [[nodiscard]] double getLoopStartBeats() const noexcept { return _loopStartBeats; } - - [[nodiscard]] double getLoopEndBeats() const noexcept { return _loopEndBeats; } - - [[nodiscard]] double getLoopStartSeconds() const noexcept { return _loopStartSeconds; } - - [[nodiscard]] double getLoopEndSeconds() const noexcept { return _loopEndSeconds; } - - [[nodiscard]] int getTimeSignatureNumerator() const noexcept { return _timeSignatureNumerator; } - - [[nodiscard]] int getTimeSignatureDenominator() const noexcept { - return _timeSignatureDenominator; - } - -signals: - void updated(); - - void isSubscribedChanged(); - - void hasTransportChanged(); - - void hasTempoChanged(); - void hasBeatsTimelineChanged(); - void hasSecondsTimelineChanged(); - void hasTimeSignatureChanged(); - void isPlayingChanged(); - void isRecordingChanged(); - void isLoopActiveChanged(); - void isWithinPreRollChanged(); - - void songPositionBeatsChanged(); - void songPositionSecondsChanged(); - - void tempoChanged(); - - void barStartChanged(); - void barNumberChanged(); - - void loopStartBeatsChanged(); - void loopEndBeatsChanged(); - void loopStartSecondsChanged(); - void loopEndSecondsChanged(); - - void timeSignatureNumeratorChanged(); - void timeSignatureDenominatorChanged(); - -private: - using NotifyType = void (TransportProxy::*)(); - - template <typename T> - void update(T &attr, T value, NotifyType notify) { - if (value == attr) - return; - attr = value; - (this->*notify)(); - } - - bool _isSubscribed = false; - bool _hasTransport = false; - - bool _hasTempo = false; - bool _hasBeatsTimeline = false; - bool _hasSecondsTimeline = false; - bool _hasTimeSignature = false; - bool _isPlaying = false; - bool _isRecording = false; - bool _isLoopActive = false; - bool _isWithinPreRoll = false; - - double _songPositionBeats = 0; - double _songPositionSeconds = 0; - - double _tempo = 0; - - double _barStart = 0; - int _barNumber = 0; - - double _loopStartBeats = 0; - double _loopEndBeats = 0; - double _loopStartSeconds = 0; - double _loopEndSeconds = 0; - - int _timeSignatureNumerator = 0; - int _timeSignatureDenominator = 0; -}; -\ No newline at end of file diff --git a/examples/plugins/io/CMakeLists.txt b/examples/plugins/io/CMakeLists.txt @@ -1,9 +0,0 @@ -add_library(clap-io STATIC - remote-channel.hh - remote-channel.cc - buffer.hh -) - -set_target_properties(clap-io PROPERTIES CXX_STANDARD 17) -set_target_properties(clap-io PROPERTIES POSITION_INDEPENDENT_CODE TRUE) -target_include_directories(clap-io INTERFACE .) -\ No newline at end of file diff --git a/examples/plugins/io/buffer.hh b/examples/plugins/io/buffer.hh @@ -1,76 +0,0 @@ -#pragma once - -#include <algorithm> -#include <array> -#include <cassert> -#include <cstddef> - -namespace clap { - template <typename T, size_t CAPACITY> - class Buffer { - public: - Buffer() { assert(checkInvariants()); } - - Buffer(const Buffer<T, CAPACITY> &) = delete; - Buffer(Buffer<T, CAPACITY> &&) = delete; - Buffer<T, CAPACITY> &operator=(const Buffer<T, CAPACITY> &) = delete; - Buffer<T, CAPACITY> &operator=(Buffer<T, CAPACITY> &&) = delete; - - const T *readPtr() const noexcept { return &_data[_roff]; } - size_t readAvail() const noexcept { return _woff - _roff; } - - T *writePtr() noexcept { return &_data[_woff]; } - size_t writeAvail() const noexcept { return CAPACITY - _woff; } - - /* Consume nbytes from the buffer */ - void read(size_t nbytes) noexcept { - _roff += nbytes; - assert(checkInvariants()); - } - - /* Produce nbytes into the buffer */ - void wrote(size_t nbytes) noexcept { - _woff += nbytes; - assert(checkInvariants()); - } - - void write(const T *&data, size_t &size) { - size_t avail = writeAvail(); - avail = std::min(size, avail); - auto end = data + avail; - std::copy(data, end, writePtr()); - wrote(size); - data = end; - size -= avail; - } - - void rewind() noexcept { - if (_woff == 0) - return; - - // this is inefficient but simple - // TODO: use scatter/gather IO - auto rptr = readPtr(); - auto avail = readAvail(); - std::copy(rptr, rptr + avail, &_data[0]); - - _woff -= _roff; - _roff = 0; - - assert(checkInvariants()); - } - - private: -#ifndef NDEBUG - bool checkInvariants() const noexcept { - assert(_woff <= _data.size()); - assert(_roff <= _woff); - return true; - } -#endif - - std::array<T, CAPACITY> _data; - size_t _roff = 0; - size_t _woff = 0; - }; -} // namespace clap -\ No newline at end of file diff --git a/examples/plugins/io/messages.hh b/examples/plugins/io/messages.hh @@ -1,163 +0,0 @@ -#include <stdint.h> - -#include <clap/all.h> - -namespace clap::messages { - - enum Type : uint32_t { - // DSP->GUI - kDefineParameterRequest, - kParameterValueRequest, - kUpdateTransportRequest, - - // GUI->DSP - kAdjustRequest, - kSubscribeToTransportRequest, - - // Gui, Host->Plugin - kSetScaleRequest, - kSizeRequest, - kSizeResponse, - kRoundSizeRequest, - kRoundSizeResponse, - kSetSizeRequest, - kSetSizeResponse, - kShowRequest, - kShowResponse, - kHideRequest, - kHideResponse, - kDestroyRequest, - kDestroyResponse, - kAttachWin32Request, - kAttachCocoaRequest, - kAttachX11Request, - kAttachResponse, - - // Gui, Plugin->Host - kResizeRequest, - kResizeResponse, - }; - - struct AdjustRequest final { - static const constexpr Type type = clap::messages::kAdjustRequest; - - clap_id paramId; - double value; - clap_event_param_flags flags; - }; - - struct DefineParameterRequest final { - static const constexpr Type type = kDefineParameterRequest; - clap_param_info info; - }; - - struct UpdateTransportRequest final { - static const constexpr Type type = kUpdateTransportRequest; - bool hasTransport; - clap_event_transport transport; - }; - - struct SubscribeToTransportRequest final { - static const constexpr Type type = kSubscribeToTransportRequest; - bool isSubscribed; - }; - - struct ParameterValueRequest final { - static const constexpr Type type = kParameterValueRequest; - clap_id paramId; - double value; - double modulation; - }; - - struct SetScaleRequest final { - static const constexpr Type type = kSetScaleRequest; - double scale; - }; - - struct SizeRequest final { - static const constexpr Type type = kSizeRequest; - }; - - struct SizeResponse final { - static const constexpr Type type = kSizeResponse; - uint32_t width; - uint32_t height; - }; - - struct RoundSizeRequest final { - static const constexpr Type type = kRoundSizeRequest; - uint32_t width; - uint32_t height; - }; - - struct RoundSizeResponse final { - static const constexpr Type type = kRoundSizeResponse; - uint32_t width; - uint32_t height; - }; - - struct SetSizeRequest final { - static const constexpr Type type = kSetSizeRequest; - uint32_t width; - uint32_t height; - }; - - struct SetSizeResponse final { - static const constexpr Type type = kSetSizeResponse; - }; - - struct ShowRequest final { - static const constexpr Type type = kShowRequest; - }; - - struct ShowResponse final { - static const constexpr Type type = kShowResponse; - }; - - struct HideRequest final { - static const constexpr Type type = kHideRequest; - }; - - struct HideResponse final { - static const constexpr Type type = kHideResponse; - }; - - struct DestroyRequest final { - static const constexpr Type type = kDestroyRequest; - }; - - struct DestroyResponse final { - static const constexpr Type type = kDestroyResponse; - }; - - struct ResizeRequest final { - static const constexpr Type type = kResizeRequest; - uint32_t width; - uint32_t height; - }; - - struct ResizeResponse final { - static const constexpr Type type = kResizeResponse; - }; - - struct AttachWin32Request final { - static const constexpr Type type = kAttachWin32Request; - clap_hwnd hwnd; - }; - - struct AttachX11Request final { - static const constexpr Type type = kAttachX11Request; - unsigned long window; - char display[128]; - }; - - struct AttachCocoaRequest final { - static const constexpr Type type = kAttachCocoaRequest; - void *nsView; - }; - - struct AttachResponse final { - static const constexpr Type type = kAttachResponse; - bool succeed; - }; -} // namespace clap::messages -\ No newline at end of file diff --git a/examples/plugins/io/remote-channel.cc b/examples/plugins/io/remote-channel.cc @@ -1,201 +0,0 @@ -#ifdef __unix__ -# include <errno.h> -# include <fcntl.h> -# include <poll.h> -# include <unistd.h> -#endif - -#include "remote-channel.hh" - -namespace clap { - - RemoteChannel::RemoteChannel(const MessageHandler &handler, - EventControl &evControl, - int socket, - bool cookieHalf) - : _cookieHalf(cookieHalf), _handler(handler), _evControl(evControl), _socket(socket) { -#ifdef __unix__ - int flags = ::fcntl(_socket, F_GETFL); - ::fcntl(_socket, F_SETFL, flags | O_NONBLOCK); -#endif - } - - RemoteChannel::~RemoteChannel() { close(); } - - void RemoteChannel::onRead() { - ssize_t nbytes = ::read(_socket, _inputBuffer.writePtr(), _inputBuffer.writeAvail()); - if (nbytes < 0) { - if (errno == EWOULDBLOCK || errno == EAGAIN || errno == EINTR) - return; - - close(); - return; - } - - if (nbytes == 0) { - close(); - return; - } - - _inputBuffer.wrote(nbytes); - processInput(); - _inputBuffer.rewind(); - } - - void RemoteChannel::write(const void *_data, size_t size) { - const uint8_t *data = static_cast<const uint8_t *>(_data); - while (size > 0) { - auto &buffer = nextWriteBuffer(); - buffer.write(data, size); - } - - assert(size == 0); - } - - RemoteChannel::WriteBuffer &RemoteChannel::nextWriteBuffer() { - if (_outputBuffers.empty()) { - _outputBuffers.emplace(); - return _outputBuffers.back(); - } - - auto &buffer = _outputBuffers.back(); - if (buffer.writeAvail() > 0) - return buffer; - - _outputBuffers.emplace(); - return _outputBuffers.back(); - } - - void RemoteChannel::onWrite() { - while (!_outputBuffers.empty()) { - auto &buffer = _outputBuffers.front(); - - for (auto avail = buffer.readAvail(); avail > 0; avail = buffer.readAvail()) { - auto nbytes = ::write(_socket, buffer.readPtr(), avail); - if (nbytes == -1) { - if (errno == EWOULDBLOCK || errno == EAGAIN || errno == EINTR) { - modifyFd(CLAP_FD_READ | CLAP_FD_WRITE); - return; - } - - close(); - return; - } - - buffer.read(nbytes); - } - - _outputBuffers.pop(); - } - - modifyFd(CLAP_FD_READ); - } - - void RemoteChannel::onError() { close(); } - - void RemoteChannel::close() { - if (_socket == -1) - return; - - _evControl.removeFd(); - - ::close(_socket); - _socket = -1; - } - - uint32_t RemoteChannel::computeNextCookie() noexcept { - uint32_t cookie = _nextCookie; - if (_cookieHalf) - cookie |= (1ULL << 31); - else - cookie &= ~(1ULL << 31); - - ++_nextCookie; // overflow is fine - return cookie; - } - - void RemoteChannel::processInput() { - while (_inputBuffer.readAvail() >= 12) { - const auto *data = _inputBuffer.readPtr(); - Message msg; - - std::memcpy(&msg.type, data, 4); - std::memcpy(&msg.cookie, data + 4, 4); - std::memcpy(&msg.size, data + 8, 4); - msg.data = data + 12; - - uint32_t totalSize = 12 + msg.size; - if (_inputBuffer.readAvail() < totalSize) - return; - - auto it = _syncHandlers.find(msg.cookie); - if (it != _syncHandlers.end()) { - it->second(msg); - _syncHandlers.erase(it); - } else { - _handler(msg); - } - - _inputBuffer.read(totalSize); - } - } - - bool RemoteChannel::sendMessageAsync(const Message &msg) { - write(&msg.type, sizeof(msg.type)); - write(&msg.cookie, sizeof(msg.cookie)); - write(&msg.size, sizeof(msg.size)); - write(msg.data, msg.size); - onWrite(); - return true; - } - - bool RemoteChannel::sendMessageSync(const Message &msg, const MessageHandler &handler) { - sendMessageAsync(msg); - - auto it = _syncHandlers.emplace(msg.cookie, handler); - assert(it.second); - if (!it.second) - return false; - - while (_syncHandlers.count(msg.cookie) > 0) - runOnce(); - - _syncHandlers.erase(msg.cookie); - return true; - } - - void RemoteChannel::modifyFd(clap_fd_flags flags) { - if (flags == _ioFlags) - return; - - _ioFlags = flags; - _evControl.modifyFd(flags); - } - - void RemoteChannel::runOnce() { - if (!isOpen()) - return; - -#ifdef __unix__ - pollfd pfd; - pfd.fd = _socket; - pfd.events = POLLIN | (_ioFlags & CLAP_FD_WRITE ? POLLOUT : 0); - pfd.revents = 0; - - int ret = ::poll(&pfd, 1, -1); - if (ret < 1) { - if (errno == EAGAIN || errno == EINTR) - return; - close(); - return; - } - - if (pfd.revents & POLLOUT) - onWrite(); - if (isOpen() && pfd.revents & POLLIN) - onRead(); - if (isOpen() && pfd.revents & POLLERR) - close(); -#endif - } -} // namespace clap diff --git a/examples/plugins/io/remote-channel.hh b/examples/plugins/io/remote-channel.hh @@ -1,135 +0,0 @@ -#pragma once - -#include <cstring> -#include <functional> -#include <memory> -#include <queue> -#include <unordered_map> - -#include <clap/all.h> - -#include "buffer.hh" - -namespace clap { - class RemoteChannel final { - public: - class EventControl { - public: - virtual void modifyFd(clap_fd_flags flags) = 0; - virtual void removeFd() = 0; - }; - - struct Message final { - uint32_t type = 0; - uint32_t cookie = 0; - uint32_t size = 0; - const void *data = nullptr; - - Message() = default; - - template <typename T> - Message(const T &msg, uint32_t c) : cookie(c) { - set(msg); - } - - template <typename T> - void get(T &obj) const noexcept { - constexpr const auto sz = sizeof(T); - - if (size != sz) - std::terminate(); - - if (type != T::type) - std::terminate(); - - std::memcpy(&obj, data, sizeof(obj)); - } - - template <typename T> - const T &get() const noexcept { - return *reinterpret_cast<const T *>(data); - } - - template <typename T> - void set(const T &msg) noexcept { - type = T::type; - data = &msg; - size = sizeof(T); - } - }; - - using MessageHandler = std::function<void(const Message &response)>; - - RemoteChannel(const MessageHandler &handler, - EventControl &evControl, - clap_fd socket, - bool cookieHalf); - ~RemoteChannel(); - - RemoteChannel(const RemoteChannel &) = delete; - RemoteChannel(RemoteChannel &&) = delete; - RemoteChannel &operator=(const RemoteChannel &) = delete; - RemoteChannel &operator=(RemoteChannel &&) = delete; - - uint32_t computeNextCookie() noexcept; - - template <typename Request> - bool sendRequestAsync(const Request &request) { - return sendMessageAsync(RemoteChannel::Message(request, computeNextCookie())); - } - - template <typename Response> - bool sendResponseAsync(const Response &response, uint32_t cookie) { - return sendMessageAsync(RemoteChannel::Message(response, cookie)); - } - - template <typename Request, typename Response> - bool sendRequestSync(const Request &request, Response &response) { - sendMessageSync(RemoteChannel::Message(request, computeNextCookie()), - [&response](const RemoteChannel::Message &m) { m.get(response); }); - return true; - } - - void close(); - - // Called when there is data to be read, non-blocking - void onRead(); - - // Called when data can be written, non-blocking - void onWrite(); - - // Called on socket exception - void onError(); - - void runOnce(); - - clap_fd fd() const { return _socket; } - bool isOpen() const noexcept { return _socket != -1; } - - private: - using ReadBuffer = Buffer<uint8_t, 128 * 1024>; - using WriteBuffer = Buffer<uint8_t, 32 * 1024>; - - void write(const void *data, size_t size); - WriteBuffer &nextWriteBuffer(); - - void processInput(); - - void modifyFd(clap_fd_flags flags); - - bool sendMessageAsync(const Message &msg); - bool sendMessageSync(const Message &msg, const MessageHandler &handler); - - const bool _cookieHalf; - uint32_t _nextCookie = 0; - - MessageHandler _handler; - std::unordered_map<uint32_t /* cookie */, const MessageHandler &> _syncHandlers; - EventControl &_evControl; - clap_fd _socket; - clap_fd_flags _ioFlags = 0; - - ReadBuffer _inputBuffer; - std::queue<WriteBuffer> _outputBuffers; - }; -} // namespace clap -\ No newline at end of file diff --git a/examples/plugins/linux-clap-plugins.version b/examples/plugins/linux-clap-plugins.version @@ -1,6 +0,0 @@ -CLAP_PLUGIN_ABI_1.0 { - global: - clap_plugin_entry; - local: - *; -}; diff --git a/examples/plugins/parameter-interpolator.hh b/examples/plugins/parameter-interpolator.hh @@ -1,68 +0,0 @@ -#pragma once - -#include <cstdint> -#include <algorithm> - -namespace clap { - class ParameterInterpolator { - public: - ParameterInterpolator() = default; - ParameterInterpolator(double val, double mod) - { - setValue(val, mod); - } - - void setValue(double val, double mod) - { - _val0 = val; - _val1 = val; - _mod0 = mod; - _mod1 = mod; - _duration = 0; - _offset = 0; - } - - void setInterpolationData(double val0, double val1, double mod0, double mod1, uint32_t duration) noexcept - { - _val0 = val0; - _val1 = val1; - _mod0 = mod0; - _mod1 = mod1; - _duration = duration; - _offset = 0; - } - - double value() noexcept - { - if (_offset >= _duration) - return _val1 + _mod1; - - const double x = static_cast<double>(_offset) / static_cast<double>(_duration); - const double value = (_val1 + _mod1) * x + (_val0 + _mod0) * (1 - x); - return value; - } - - double mod() noexcept - { - if (_offset >= _duration) - return _mod1; - - const double x = static_cast<double>(_offset) / static_cast<double>(_duration); - const double value = (_mod1) * x + (_mod0) * (1 - x); - return value; - } - - void step(uint32_t n) noexcept - { - _offset += n; - } - - private: - double _val0 = 0; - double _val1 = 0; - double _mod0 = 0; - double _mod1 = 0; - uint32_t _duration = 0; - uint32_t _offset = 0; - }; -} // namespace clap -\ No newline at end of file diff --git a/examples/plugins/parameters.cc b/examples/plugins/parameters.cc @@ -1,28 +0,0 @@ -#include <cassert> - -#include "parameters.hh" - -namespace clap { - void clap::Parameters::addParameter(const clap_param_info &info) { - assert(_id2param.find(info.id) == _id2param.end()); - - auto p = std::make_unique<Parameter>(info); - _id2param.insert_or_assign(info.id, p.get()); - _params.emplace_back(std::move(p)); - } - - size_t Parameters::count() const noexcept { return _params.size(); } - - Parameter *Parameters::getByIndex(size_t index) const noexcept { - if (index > _params.size()) - return nullptr; - return _params[index].get(); - } - - Parameter *Parameters::getById(clap_id id) const noexcept { - auto it = _id2param.find(id); - if (it == _id2param.end()) - return nullptr; - return it->second; - } -} // namespace clap diff --git a/examples/plugins/parameters.hh b/examples/plugins/parameters.hh @@ -1,154 +0,0 @@ -#include <memory> -#include <string> -#include <unordered_map> -#include <vector> - -#include <boost/serialization/serialization.hpp> -#include <boost/serialization/split_member.hpp> -#include <boost/serialization/utility.hpp> -#include <boost/serialization/vector.hpp> -#include <boost/serialization/version.hpp> - -#include <clap/all.h> - -#include "parameter-interpolator.hh" - -namespace clap { - class Parameter { - public: - explicit Parameter(const clap_param_info &info) : _info(info) { _info.cookie = this; } - - Parameter(const Parameter &) = delete; - Parameter(Parameter &&) = delete; - Parameter &operator=(const Parameter &) = delete; - Parameter &operator=(Parameter &&) = delete; - - const double value() const noexcept { return _value; } - const double modulation() const noexcept { return _modulation; } - const double modulatedValue() const noexcept { return _value + _modulation; } - const clap_param_info &info() const noexcept { return _info; } - - void setDefaultValue() { - _value = _info.default_value; - _modulation = 0; - } - - void setValueImmediately(double val) { - _value = val; - _valueRamp = 0; - _valueSteps = 0; - } - void setModulationImmediately(double mod) { - _modulation = mod; - _modulationRamp = 0; - _modulationSteps = 0; - } - - void setValueSmoothed(double val, uint16_t steps) { - assert(steps > 0); - _valueRamp = (val - _value) / steps; - _valueSteps = steps; - } - - void setModulationSmoothed(double mod, uint16_t steps) { - assert(steps > 0); - _modulationRamp = (mod - _modulation) / steps; - _modulationSteps = steps; - } - - // Advances the value by 1 samples and return the new value + modulation - double step() { - if (_valueSteps > 0) [[unlikely]] { - _value += _valueRamp; - --_valueSteps; - } - - if (_modulationSteps > 0) [[unlikely]] { - _modulation += _modulationRamp; - --_modulationSteps; - } - - return _value + _modulation; - } - - // Advances the value by n samples and return the new value + modulation - double step(uint32_t n) { - if (_valueSteps > 0) [[unlikely]] { - auto k = std::min<uint32_t>(_valueSteps, n); - _value += k * _valueRamp; - _valueSteps -= k; - } - - if (_modulationSteps > 0) [[unlikely]] { - auto k = std::min<uint32_t>(_valueSteps, n); - _modulation += k * _modulationRamp; - _modulationSteps -= k; - } - - return _value + _modulation; - } - - private: - clap_param_info _info; - - double _value = 0; - double _modulation = 0; - - double _valueRamp = 0; - double _modulationRamp = 0; - - uint16_t _valueSteps = 0; - uint16_t _modulationSteps = 0; - }; - - class Parameters { - public: - Parameters() = default; - Parameters(const Parameters ¶meters); - - void addParameter(const clap_param_info &info); - void addParameter(const Parameter ¶m); - - size_t count() const noexcept; - - Parameter *getByIndex(size_t index) const noexcept; - - Parameter *getById(clap_id id) const noexcept; - - private: - friend class boost::serialization::access; - - template <class Archive> - void save(Archive &ar, const unsigned int version) const { - std::vector<std::pair<clap_id, double>> values; - for (auto &p : _params) - values.emplace_back(p->info().id, p->value()); - - ar << values; - } - - template <class Archive> - void load(Archive &ar, const unsigned int version) { - std::vector<std::pair<clap_id, double>> values; - ar >> values; - - for (auto &p : _params) - p->setDefaultValue(); - - for (auto &v : values) { - auto *p = getById(v.first); - if (!p) - continue; - p->setValueImmediately(v.second); - } - } - - BOOST_SERIALIZATION_SPLIT_MEMBER() - - std::vector<std::unique_ptr<Parameter>> _params; - std::unordered_map<clap_id, Parameter *> _id2param; - }; - -} // namespace clap - -BOOST_CLASS_VERSION(clap::Parameters, 1) -\ No newline at end of file diff --git a/examples/plugins/path-provider.cc b/examples/plugins/path-provider.cc @@ -1,91 +0,0 @@ -#include <cassert> -#include <cstring> -#include <filesystem> -#include <regex> - -#include "path-provider.hh" - -namespace clap { - - class LinuxPathProvider final : public PathProvider { - public: - LinuxPathProvider(const std::string &pluginPath, const std::string &pluginName) : prefix_(computePrefix(pluginPath)), pluginName_(pluginName) {} - - static std::string computePrefix(const std::string &pluginPath) { - static const std::regex r("(/.*)/lib/clap/.*$", std::regex::optimize); - - std::smatch m; - if (!std::regex_match(pluginPath, m, r)) - return "/usr"; - return m[1]; - } - - std::string getGuiExecutable() const override { return prefix_ / "bin/clap-gui"; } - - std::string getSkinDirectory() const override { return prefix_ / "lib/clap/qml" / pluginName_; } - - std::string getQmlLibDirectory() const override { return prefix_ / "lib/clap/qml"; } - - bool isValid() const noexcept override { return !prefix_.empty(); } - - private: - const std::filesystem::path prefix_; - const std::string pluginName_; - }; - - class LinuxDevelopmentPathProvider final : public PathProvider { - public: - LinuxDevelopmentPathProvider(const std::string &pluginPath, const std::string &pluginName) - : srcRoot_(computeSrcRoot(pluginPath)), buildRoot_(computeBuildRoot(pluginPath)), pluginName_(pluginName) {} - - static std::string computeSrcRoot(const std::string &pluginPath) { - static const std::regex r("(/.*)/.*build.*/.*$", std::regex::optimize); - - std::smatch m; - if (!std::regex_match(pluginPath, m, r)) - return ""; - return m[1]; - } - - static std::string computeBuildRoot(const std::string &pluginPath) { - static const std::regex r("(/.*/.*build.*(/.*)?)/examples/plugins/.*\\.clap$", - std::regex::optimize); - - std::smatch m; - if (!std::regex_match(pluginPath, m, r)) - return ""; - return m[1]; - } - - std::string getGuiExecutable() const override { return buildRoot_ / "examples/plugins/gui/clap-gui"; } - - std::string getSkinDirectory() const override { return srcRoot_ / "examples/plugins/qml" / pluginName_; } - - std::string getQmlLibDirectory() const override { return srcRoot_ / "examples/plugins/qml"; } - - bool isValid() const noexcept override { return !srcRoot_.empty() && !buildRoot_.empty(); } - - private: - const std::filesystem::path srcRoot_; - const std::filesystem::path buildRoot_; - const std::string pluginName_; - }; - - std::unique_ptr<PathProvider> PathProvider::create(const std::string &_pluginPath, const std::string& pluginName) { -#ifdef __linux__ - { - auto pluginPath = std::filesystem::absolute(_pluginPath); - auto devPtr = std::make_unique<LinuxDevelopmentPathProvider>(pluginPath, pluginName); - if (devPtr->isValid()) - return std::move(devPtr); - - auto ptr = std::make_unique<LinuxPathProvider>(pluginPath, pluginName); - if (ptr->isValid()) - return std::move(ptr); - } -#endif - - // TODO - return nullptr; - } -} // namespace clap -\ No newline at end of file diff --git a/examples/plugins/path-provider.hh b/examples/plugins/path-provider.hh @@ -1,19 +0,0 @@ -#pragma once - -#include <memory> -#include <string> - -namespace clap { - class PathProvider { - public: - virtual ~PathProvider() = default; - - static std::unique_ptr<PathProvider> create(const std::string &pluginPath, const std::string& pluginName); - - virtual std::string getGuiExecutable() const = 0; - virtual std::string getSkinDirectory() const = 0; - virtual std::string getQmlLibDirectory() const = 0; - - virtual bool isValid() const = 0; - }; -} // namespace clap -\ No newline at end of file diff --git a/examples/plugins/plugs/dc-offset/dc-offset.cc b/examples/plugins/plugs/dc-offset/dc-offset.cc @@ -1,101 +0,0 @@ -#include <cstring> - -#include "../../parameter-interpolator.hh" -#include "dc-offset.hh" - -namespace clap { - const clap_plugin_descriptor *DcOffset::descriptor() { - static const clap_plugin_descriptor desc = { - - CLAP_VERSION, - "com.github.free-audio.clap.dc-offset", - "DC Offset", - "clap", - "https://github.com/free-audio/clap", - nullptr, - nullptr, - "0.1", - "Example DC Offset plugin", - "utility", - CLAP_PLUGIN_AUDIO_EFFECT - - }; - return &desc; - } - - enum { - kParamIdOffset = 0, - }; - - DcOffset::DcOffset(const std::string &pluginPath, const clap_host *host) - : CorePlugin(PathProvider::create(pluginPath, "dc-offset"), descriptor(), host) { - _parameters.addParameter(clap_param_info{ - kParamIdOffset, - CLAP_PARAM_IS_MODULATABLE | CLAP_PARAM_REQUIRES_PROCESS, - nullptr, - "offset", - "/", - -1, - 1, - 0, - }); - - _offsetParam = _parameters.getById(kParamIdOffset); - } - - bool DcOffset::init() noexcept { - if (!super::init()) - return false; - - defineAudioPorts(); - return true; - } - - void DcOffset::defineAudioPorts() noexcept { - assert(!isActive()); - - _channelCount = trackChannelCount(); - - clap_audio_port_info info; - info.id = 0; - strncpy(info.name, "main", sizeof(info.name)); - info.is_main = true; - info.is_cv = false; - info.sample_size = 32; - info.in_place = true; - info.channel_count = _channelCount; - info.channel_map = CLAP_CHMAP_UNSPECIFIED; - - _audioInputs.clear(); - _audioInputs.push_back(info); - _audioOutputs.clear(); - _audioOutputs.push_back(info); - } - - clap_process_status DcOffset::process(const clap_process *process) noexcept { - float **in = process->audio_inputs[0].data32; - float **out = process->audio_outputs[0].data32; - uint32_t evCount = process->in_events->size(process->in_events); - uint32_t nextEvIndex = 0; - uint32_t N = process->frames_count; - - processGuiEvents(process); - - /* foreach frames */ - for (uint32_t i = 0; i < process->frames_count;) { - - N = processEvents(process, nextEvIndex, evCount, i); - - /* Process as many samples as possible until the next event */ - for (; i < N; ++i) { - const float offset = _offsetParam->step(); - for (int c = 0; c < _channelCount; ++c) - out[c][i] = in[c][i] + offset; - } - } - - _pluginToGuiQueue.producerDone(); - - return CLAP_PROCESS_CONTINUE_IF_NOT_QUIET; - } -} // namespace clap -\ No newline at end of file diff --git a/examples/plugins/plugs/dc-offset/dc-offset.hh b/examples/plugins/plugs/dc-offset/dc-offset.hh @@ -1,24 +0,0 @@ -#pragma once - -#include "../core-plugin.hh" - -namespace clap { - class DcOffset final : public CorePlugin { - private: - using super = CorePlugin; - - public: - DcOffset(const std::string& pluginPath, const clap_host *host); - - static const clap_plugin_descriptor *descriptor(); - - protected: - bool init() noexcept override; - void defineAudioPorts() noexcept; - clap_process_status process(const clap_process *process) noexcept override; - - private: - int _channelCount = 2; - Parameter *_offsetParam = nullptr; - }; -} // namespace clap -\ No newline at end of file diff --git a/examples/plugins/plugs/gain/gain.cc b/examples/plugins/plugs/gain/gain.cc @@ -1,86 +0,0 @@ -#include <cstring> - -#include "gain.hh" - -namespace clap { - const clap_plugin_descriptor *Gain::descriptor() { - static const clap_plugin_descriptor desc = { - - CLAP_VERSION, - "com.github.free-audio.clap.gain", - "gain", - "clap", - "https://github.com/free-audio/clap", - nullptr, - nullptr, - "0.1", - "example gain plugin", - "mix;gain", - CLAP_PLUGIN_AUDIO_EFFECT - - }; - return &desc; - } - - enum { - kParamIdGain = 0, - }; - - Gain::Gain(const std::string &pluginPath, const clap_host *host) - : CorePlugin(PathProvider::create(pluginPath, "gain"), descriptor(), host) { - _parameters.addParameter(clap_param_info{ - kParamIdGain, - 0, - nullptr, - "gain", - "/", - -1, - 1, - 0, - }); - } - - bool Gain::init() noexcept { - if (!super::init()) - return false; - - defineAudioPorts(); - return true; - } - - void Gain::defineAudioPorts() noexcept { - assert(!isActive()); - - _channelCount = trackChannelCount(); - - clap_audio_port_info info; - info.id = 0; - strncpy(info.name, "main", sizeof(info.name)); - info.is_main = true; - info.is_cv = false; - info.sample_size = 32; - info.in_place = true; - info.channel_count = _channelCount; - info.channel_map = CLAP_CHMAP_UNSPECIFIED; - - _audioInputs.clear(); - _audioInputs.push_back(info); - _audioOutputs.clear(); - _audioOutputs.push_back(info); - } - - void Gain::deactivate() noexcept { _channelCount = 0; } - - clap_process_status Gain::process(const clap_process *process) noexcept { - float **in = process->audio_inputs[0].data32; - float **out = process->audio_outputs[0].data32; - - float k = 1; - for (int i = 0; i < process->frames_count; ++i) { - for (int c = 0; c < _channelCount; ++c) - out[c][i] = k * in[c][i]; - } - - return CLAP_PROCESS_CONTINUE_IF_NOT_QUIET; - } -} // namespace clap -\ No newline at end of file diff --git a/examples/plugins/plugs/gain/gain.hh b/examples/plugins/plugs/gain/gain.hh @@ -1,25 +0,0 @@ -#pragma once - -#include "../core-plugin.hh" - -namespace clap { - class Gain final : public CorePlugin { - private: - using super = CorePlugin; - - public: - Gain(const std::string& pluginPath, const clap_host *host); - - static const clap_plugin_descriptor *descriptor(); - - protected: - // clap_plugin - bool init() noexcept override; - void defineAudioPorts() noexcept; - void deactivate() noexcept override; - clap_process_status process(const clap_process *process) noexcept override; - - private: - int _channelCount = 1; - }; -} // namespace clap -\ No newline at end of file diff --git a/examples/plugins/plugs/transport/transport-info.cc b/examples/plugins/plugs/transport/transport-info.cc @@ -1,40 +0,0 @@ -#include <cstring> - -#include "../../parameter-interpolator.hh" -#include "transport-info.hh" - -namespace clap { - const clap_plugin_descriptor *TransportInfo::descriptor() { - static const clap_plugin_descriptor desc = { - - CLAP_VERSION, - "com.github.free-audio.clap.transport-info", - "Transport Info", - "clap", - "https://github.com/free-audio/clap", - nullptr, - nullptr, - "0.1", - "Displays transport info", - "utility", - CLAP_PLUGIN_AUDIO_EFFECT - - }; - return &desc; - } - - enum { - kParamIdOffset = 0, - }; - - TransportInfo::TransportInfo(const std::string &pluginPath, const clap_host *host) - : CorePlugin(PathProvider::create(pluginPath, "transport-info"), descriptor(), host) {} - - clap_process_status TransportInfo::process(const clap_process *process) noexcept { - processGuiEvents(process); - - _pluginToGuiQueue.producerDone(); - - return CLAP_PROCESS_CONTINUE_IF_NOT_QUIET; - } -} // namespace clap -\ No newline at end of file diff --git a/examples/plugins/plugs/transport/transport-info.hh b/examples/plugins/plugs/transport/transport-info.hh @@ -1,18 +0,0 @@ -#pragma once - -#include "../../core-plugin.hh" - -namespace clap { - class TransportInfo final : public CorePlugin { - private: - using super = CorePlugin; - - public: - TransportInfo(const std::string& pluginPath, const clap_host *host); - - static const clap_plugin_descriptor *descriptor(); - - protected: - clap_process_status process(const clap_process *process) noexcept override; - }; -} // namespace clap -\ No newline at end of file diff --git a/examples/plugins/qml/clap/Knob.qml b/examples/plugins/qml/clap/Knob.qml @@ -1,164 +0,0 @@ -import QtQuick 2.1 - -Canvas { - property QtObject param - property int size: 20; - property string ringColor: "#544d63"; - property string knobColor: "#0c002b"; - property string knobColorAdjusting: "#25008b"; - property string knobColorHovered: "#1b0064"; - property string valueColor: "#ffffff"; - property string modulationColor: "#10b1ca" - property double modulationMargin: .05; - property double ringAngle: Math.PI * 5 / 3; - - id: knob - width: size - height: size - - property real lastY: 0 - - Connections { - target: param - - function onValueChanged() { - knob.requestPaint(); - } - - function onModulationChanged() { - knob.requestPaint(); - } - } - - MouseArea { - id: mouseArea - anchors.fill: parent - drag.axis: Drag.YAxis - property real lastY: 0 - hoverEnabled: true - acceptedButtons: Qt.LeftButton | Qt.RightButton - - onPressed: (mouse) => { - if (mouse.button === Qt.LeftButton) { - lastY = mouse.y; - knob.param.isAdjusting = true; - knob.requestPaint(); - mouse.accepted = true; - } - } - - onReleased: (mouse) => { - if (mouse.button === Qt.LeftButton) { - knob.param.isAdjusting = false; - knob.requestPaint(); - } - } - - onPositionChanged: (mouse) => { - if (!(mouse.buttons & Qt.LeftButton)) - return; - - knob.param.normalizedValue += ((mouse.modifiers & Qt.ShiftModifier) ? 0.001 : 0.01) * (lastY - mouse.y); - lastY = mouse.y; - mouse.accepted = true; - knob.requestPaint(); - } - - onDoubleClicked: (mouse) => { - if (mouse.button === Qt.LeftButton) { - knob.param.setToDefault(); - knob.requestPaint(); - mouse.accepted = true; - } - } - - onCanceled: (mouse) => { - knob.param.isAdjusting = false; - knob.requestPaint(); - } - - onEntered: () => { - knob.param.isHovered = true; - knob.requestPaint(); - } - - onExited: () => { - knob.param.isHovered = false; - knob.requestPaint(); - } - } - - onPaint: { - var ctx = getContext("2d"); - - drawBackground(ctx); - drawModulation(ctx); - drawValue(ctx); - } - - function drawBackground(ctx) { - ctx.save() - ctx.strokeStyle = "black"; - - ctx.translate(size / 2, size / 2, size / 2); - ctx.rotate(ringAngle + Math.PI); - - var effectiveKnobColor = param.isAdjusting ? knobColorAdjusting : (param.isHovered ? knobColorHovered : knobColor); - ctx.beginPath(); - ctx.arc(0, 0, size / 2 - 2 * modulationMargin * size, 0, 2 * Math.PI, false); - ctx.fillStyle = effectiveKnobColor; - ctx.fill(); - - ctx.beginPath(); - ctx.arc(0, 0, size / 2 - size * modulationMargin, 0, ringAngle, false); - ctx.arc(0, 0, size / 2, ringAngle, 0, true); - ctx.fillStyle = ringColor; - ctx.fill(); - - ctx.restore() - } - - function drawModulation(ctx) { - var radius = size / 15; - - ctx.save(); - - ctx.translate(size / 2, size / 2); - - ctx.fillStyle = modulationColor; - ctx.strokeStyle = "black"; - - ctx.beginPath(); - ctx.rotate((param.normalizedValue - .5) * ringAngle - Math.PI / 2); - - var finalValueAngle = (param.normalizedFinalValue - param.normalizedValue) * ringAngle; - ctx.arc(0, 0, size / 2, 0, finalValueAngle, finalValueAngle < 0); - ctx.arc(0, 0, size / 2 - size * modulationMargin, finalValueAngle, 0, finalValueAngle > 0); - ctx.fill(); - - ctx.restore(); - } - - function drawValue(ctx) { - var radius = size / 40; - - ctx.save(); - - ctx.translate(size / 2, size / 2); - - ctx.fillStyle = valueColor; - ctx.strokeStyle = "black"; - - ctx.rotate((param.normalizedValue - .5) * ringAngle); - ctx.beginPath(); - var y0 = -size / 2 + size * modulationMargin; - var y1 = y0 + size / 3; - ctx.rect(-radius, -size * modulationMargin, 2 * radius, -size / 2 + size * (3 * modulationMargin + 0.01)); - ctx.fill(); - - ctx.arc(0, 0, size * (modulationMargin + 0.03), 0, 2 * Math.PI, false) - ctx.fill(); - - ctx.restore(); - } -} diff --git a/examples/plugins/qml/clap/qmldir b/examples/plugins/qml/clap/qmldir @@ -1,3 +0,0 @@ -module clap - -Knob 1.0 Knob.qml diff --git a/examples/plugins/qml/dc-offset/main.qml b/examples/plugins/qml/dc-offset/main.qml @@ -1,17 +0,0 @@ -import QtQuick 2.1 -import QtQuick.Controls 2.1 -import clap 1.0 - -Rectangle { - width: 200 - height: 200 - color: "#224477" - - Knob { - id: dc_offset_knob - param: plugin.param(0) - size: 160 - x: 20 - y: 20 - } -} diff --git a/examples/plugins/qml/transport-info/main.qml b/examples/plugins/qml/transport-info/main.qml @@ -1,72 +0,0 @@ -import QtQuick 2.1 -import QtQuick.Controls 2.1 -import clap 1.0 - -Rectangle { - width: 450 - height: 490 - color: "#f8f8f8" - - Text { - id: display - text: computeText() - font.pointSize: 20 - } - - function computeText() { - if (!transport.hasTransport) - return "Free Running"; - - var text = ""; - text += "Time Signature: " + computeTimeSignature() + "\n"; - text += "Tempo: " + (transport.hasTempo ? (transport.tempo.toFixed(3) + " (bpm)") : "(none)") + "\n"; - - if (transport.hasBeatsTimeline) - { - text += "song position (beats): " + transport.songPositionBeats.toFixed(3) + "\n"; - text += "bar start: " + transport.barStart.toFixed(3) + "\n"; - text += "bar number: " + transport.barNumber + "\n"; - } - else - text += "No timeline in beats\n"; - - if (transport.hasSecondsTimeline) - text += "song position (seconds): " + transport.songPositionSeconds.toFixed(3) + "s\n"; - else - text += "No timeline in seconds\n"; - - text += "\n"; - - text += "Is playing: " + transport.isPlaying + "\n"; - text += "Is recording: " + transport.isRecording + "\n"; - text += "Is within preroll: " + transport.isWithinPreRoll + "\n"; - text += "\n"; - - if (transport.isLoopActive) { - if (transport.hasBeatsTimeline) - text += "Loop (beats): " + transport.loopStartBeats.toFixed(3) + " .. " + transport.loopEndBeats.toFixed(3) + "\n"; - - if (transport.hasSecondsTimeline) - text += "Loop (seconds): " + transport.loopStartSeconds.toFixed(3) + "s .. " + transport.loopEndSeconds.toFixed(3) + "s\n"; - - text += "\n"; - } - - return text; - } - - function computeTimeSignature() { - if (transport.hasTimeSignature) - return transport.timeSignatureNumerator + "/" + transport.timeSignatureDenominator; - return "(none)"; - } - - function updateText() { - display.text = computeText(); - } - - Component.onCompleted: { - transport.isSubscribed = true; - transport.updated.connect(updateText); - } -} diff --git a/examples/plugins/remote-gui.cc b/examples/plugins/remote-gui.cc @@ -1,226 +0,0 @@ -#ifdef __unix__ -# include <fcntl.h> -# include <sys/socket.h> -#endif - -#include <cassert> - -#include "../io/messages.hh" -#include "core-plugin.hh" -#include "path-provider.hh" -#include "remote-gui.hh" - -namespace clap { - RemoteGui::~RemoteGui() { - if (_channel) - destroy(); - - assert(!_channel); - } - - bool RemoteGui::spawn() { - assert(_child == -1); - assert(!_channel); - - if (!_plugin.canUseTimerSupport() || !_plugin.canUseFdSupport()) - return false; - - auto &pathProvider = _plugin.pathProvider(); - -#ifdef __unix__ - /* create a socket pair */ - int sockets[2]; - if (::socketpair(AF_UNIX, SOCK_STREAM, 0, sockets)) { - return false; - } - - auto path = pathProvider.getGuiExecutable(); - auto skin = pathProvider.getSkinDirectory(); - auto qmlLib = pathProvider.getQmlLibDirectory(); - - printf("About to start GUI: %s --socket %d --skin %s --qml-import %s\n", - path.c_str(), - sockets[0], - skin.c_str(), - qmlLib.c_str()); - - _child = ::fork(); - if (_child == -1) { - ::close(sockets[0]); - ::close(sockets[1]); - return false; - } - - if (_child == 0) { - // Child - ::close(sockets[0]); - char socketStr[16]; - ::snprintf(socketStr, sizeof(socketStr), "%d", sockets[1]); - ::execl(path.c_str(), - path.c_str(), - "--socket", - socketStr, - "--skin", - skin.c_str(), - "--qml-import", - qmlLib.c_str(), - (const char *)nullptr); - printf("Failed to start child process: %m\n"); - std::terminate(); - } else { - // Parent - ::close(sockets[1]); - } - - _timerId = CLAP_INVALID_ID; - _plugin._hostTimerSupport->register_timer(_plugin._host, 1000 / 60, &_timerId); - _plugin._hostFdSupport->register_fd(_plugin._host, sockets[0], CLAP_FD_READ | CLAP_FD_ERROR); - _channel.reset(new RemoteChannel( - [this](const RemoteChannel::Message &msg) { onMessage(msg); }, *this, sockets[0], true)); - - return true; -#else - return false; -#endif - } - - void RemoteGui::modifyFd(clap_fd_flags flags) { - _plugin._hostFdSupport->modify_fd(_plugin._host, _channel->fd(), flags); - } - - void RemoteGui::removeFd() { - _plugin._hostFdSupport->unregister_fd(_plugin._host, _channel->fd()); - _plugin._hostTimerSupport->unregister_timer(_plugin._host, _timerId); - } - - clap_fd RemoteGui::fd() const { return _channel ? _channel->fd() : -1; } - - void RemoteGui::onFd(clap_fd_flags flags) { - if (flags & CLAP_FD_READ) - _channel->onRead(); - if (flags & CLAP_FD_WRITE) - _channel->onWrite(); - if (flags & CLAP_FD_ERROR) - _channel->onError(); - } - - void RemoteGui::onMessage(const RemoteChannel::Message &msg) { - switch (msg.type) { - case messages::kAdjustRequest: { - messages::AdjustRequest rq; - msg.get(rq); - _plugin.guiAdjust(rq.paramId, rq.value, rq.flags); - break; - - case messages::kSubscribeToTransportRequest: { - messages::SubscribeToTransportRequest rq; - msg.get(rq); - _isTransportSubscribed = rq.isSubscribed; - break; - } - } - } - } - - void RemoteGui::defineParameter(const clap_param_info &info) noexcept { - _channel->sendRequestAsync(messages::DefineParameterRequest{info}); - } - - bool RemoteGui::size(uint32_t *width, uint32_t *height) noexcept { - messages::SizeRequest request; - messages::SizeResponse response; - - if (!_channel->sendRequestSync(request, response)) - return false; - - *width = response.width; - *height = response.height; - return true; - } - - void RemoteGui::setScale(double scale) noexcept { - _channel->sendRequestAsync(messages::SetScaleRequest{scale}); - } - - bool RemoteGui::show() noexcept { - messages::ShowRequest request; - messages::ShowResponse response; - - return _channel->sendRequestSync(request, response); - } - - bool RemoteGui::hide() noexcept { - messages::HideRequest request; - messages::HideResponse response; - - return _channel->sendRequestSync(request, response); - } - - void RemoteGui::destroy() noexcept { - if (!_channel) - return; - - messages::DestroyRequest request; - messages::DestroyResponse response; - - _channel->sendRequestSync(request, response); - _channel->close(); - _channel.reset(); - - waitChild(); - } - - void RemoteGui::waitChild() { -#ifdef __unix__ - if (_child == -1) - return; - int stat = 0; - int ret; - - do { - ret = ::waitpid(_child, &stat, 0); - } while (ret == -1 && errno == EINTR); - - _child = -1; -#endif - } - - bool RemoteGui::attachCocoa(void *nsView) noexcept { - messages::AttachCocoaRequest request{nsView}; - messages::AttachResponse response; - - return _channel->sendRequestSync(request, response); - } - - bool RemoteGui::attachWin32(clap_hwnd window) noexcept { - messages::AttachWin32Request request{window}; - messages::AttachResponse response; - - return _channel->sendRequestSync(request, response); - } - - bool RemoteGui::attachX11(const char *display_name, unsigned long window) noexcept { - messages::AttachX11Request request; - messages::AttachResponse response; - - request.window = window; - std::snprintf(request.display, sizeof(request.display), "%s", display_name ?: ""); - - return _channel->sendRequestSync(request, response); - } - - void RemoteGui::onTimer() { - _plugin._pluginToGuiQueue.consume( - [this](clap_id paramId, const CorePlugin::PluginToGuiValue &value) { - messages::ParameterValueRequest rq{paramId, value.value, value.mod}; - _channel->sendRequestAsync(rq); - }); - - if (_isTransportSubscribed && _plugin._hasTransportCopy) { - messages::UpdateTransportRequest rq{_plugin._hasTransport, _plugin._transportCopy}; - _channel->sendRequestAsync(rq); - _plugin._hasTransportCopy = false; - } - } - -} // namespace clap -\ No newline at end of file diff --git a/examples/plugins/remote-gui.hh b/examples/plugins/remote-gui.hh @@ -1,59 +0,0 @@ -#pragma once - -#ifdef __unix__ -# include <sys/wait.h> -#endif - -#include <memory> - -#include "abstract-gui.hh" -#include "remote-channel.hh" - -namespace clap { - class RemoteGui : public AbstractGui, public RemoteChannel::EventControl { - public: - RemoteGui(CorePlugin &plugin) : AbstractGui(plugin) {} - ~RemoteGui(); - - bool spawn(); - - void defineParameter(const clap_param_info&) noexcept override; - - bool attachCocoa(void *nsView) noexcept override; - bool attachWin32(clap_hwnd window) noexcept override; - bool attachX11(const char *display_name, unsigned long window) noexcept override; - - bool size(uint32_t *width, uint32_t *height) noexcept override; - void setScale(double scale) noexcept override; - - bool show() noexcept override; - bool hide() noexcept override; - - void destroy() noexcept override; - - // RemoteChannel::EventControl - void modifyFd(clap_fd_flags flags) override; - void removeFd() override; - - clap_fd fd() const; - void onFd(clap_fd_flags flags); - - clap_id timerId() const noexcept { return _timerId; } - void onTimer(); - - private: - void onMessage(const RemoteChannel::Message& msg); - void waitChild(); - - std::unique_ptr<RemoteChannel> _channel; - - clap_id _timerId = CLAP_INVALID_ID; - -#ifdef __unix__ - pid_t _child = -1; -#else - STARTUPINFO _si; - PROCESS_INFORMATION _childInfo; -#endif - }; -} // namespace clap -\ No newline at end of file diff --git a/examples/plugins/stream-helper.hh b/examples/plugins/stream-helper.hh @@ -1,34 +0,0 @@ -#pragma once - -#include <boost/iostreams/concepts.hpp> -#include <boost/iostreams/stream.hpp> - -#include <clap/stream.h> - -namespace clap { - class Source : public boost::iostreams::source { - public: - explicit Source(clap_istream *is) : _is(is) {} - - std::streamsize read(char *s, std::streamsize n) noexcept { return _is->read(_is, s, n); } - - private: - clap_istream *_is; - }; - - using IStream = boost::iostreams::stream<Source>; - - class Sink : public boost::iostreams::sink { - public: - explicit Sink(clap_ostream *os) : _os(os) {} - - std::streamsize write(const char *s, std::streamsize n) noexcept { - return _os->write(_os, s, n); - } - - private: - clap_ostream *_os; - }; - - using OStream = boost::iostreams::stream<Sink>; -} // namespace clap -\ No newline at end of file