clap

CLAP Audio Plugin API
Log | Files | Refs | README | LICENSE

commit bb238cbc782299fa9db88e637d504f648bdcd22f
parent 8e72ff666e92754c77c7f5651bc5435748ce8897
Author: Alexandre BIQUE <bique.alexandre@gmail.com>
Date:   Wed, 23 Jun 2021 19:11:07 +0200

More work on examples

Diffstat:
Mexamples/glue/clap-plugin.cc | 7-------
Mexamples/glue/clap-plugin.hh | 3---
Mexamples/gui/CMakeLists.txt | 9+++++++--
Aexamples/gui/application.cc | 23+++++++++++++++++++++++
Aexamples/gui/application.hh | 16++++++++++++++++
Mexamples/gui/main.cc | 9++++++---
Mexamples/host/application.cc | 8++++----
Mexamples/host/plugin-host.cc | 6+++---
Mexamples/host/plugin-host.hh | 3++-
Mexamples/plugins/CMakeLists.txt | 5++++-
Mexamples/plugins/clap-entry.cc | 2++
Mexamples/plugins/parameters.hh | 57+++++++++++++++++++++++++++++++++++++++++++++++++++++++--
Mexamples/plugins/plugin-helper.cc | 26++++++++++++++++++++++++++
Mexamples/plugins/plugin-helper.hh | 23+++++++++++++++++++----
Aexamples/plugins/stream-helper.hh | 35+++++++++++++++++++++++++++++++++++
15 files changed, 202 insertions(+), 30 deletions(-)

diff --git a/examples/glue/clap-plugin.cc b/examples/glue/clap-plugin.cc @@ -283,13 +283,6 @@ namespace clap { return self.stateLoad(stream); } - bool Plugin::clapStateIsDirty(const clap_plugin *plugin) noexcept { - auto &self = from(plugin); - self.ensureMainThread("clap_plugin_state.is_dirty"); - - return self.stateIsDirty(); - } - //-------------------------// // clap_plugin_preset_load // //-------------------------// diff --git a/examples/glue/clap-plugin.hh b/examples/glue/clap-plugin.hh @@ -66,7 +66,6 @@ namespace clap { 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; } - virtual bool stateIsDirty() const noexcept { return false; } void stateMarkDirty() const noexcept { if (canUseState()) hostState_->mark_dirty(host_); @@ -274,7 +273,6 @@ namespace clap { // 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; - static bool clapStateIsDirty(const clap_plugin *plugin) noexcept; // clap_plugin_preset static bool clapPresetLoadFromFile(const clap_plugin *plugin, const char *path) noexcept; @@ -362,7 +360,6 @@ namespace clap { static const constexpr clap_plugin_state pluginState_ = { clapStateSave, clapStateLoad, - clapStateIsDirty, }; static const constexpr clap_plugin_preset_load pluginPresetLoad_ = { diff --git a/examples/gui/CMakeLists.txt b/examples/gui/CMakeLists.txt @@ -3,9 +3,14 @@ set(CMAKE_AUTOMOC ON) find_package(Qt6Core REQUIRED) find_package(Qt6Widgets REQUIRED) +find_package(Qt6Qml REQUIRED) add_executable(clap-gui main.cc + + application.hh + application.cc ) +target_link_libraries(clap-gui Qt6::Qml Qt6::Widgets Qt6::Core) -set_target_properties(clap-gui PROPERTIES CXX_STANDARD 20) -\ No newline at end of file +set_target_properties(clap-gui PROPERTIES CXX_STANDARD 17) +\ No newline at end of file diff --git a/examples/gui/application.cc b/examples/gui/application.cc @@ -0,0 +1,23 @@ +#include <QCommandLineParser> +#include <QQmlApplicationEngine> + +#include "application.hh" + +Application::Application(int argc, char **argv) + : QGuiApplication(argc, argv), qmlEngine_(new QQmlApplicationEngine(this)) { + + QCommandLineParser parser; + + QCommandLineOption qmlOpt("qml", tr("path to the QML skin"), tr("path")); + QCommandLineOption socketOpt("socket", tr("path to the QML skin"), tr("path")); + + parser.addOption(qmlOpt); + parser.addOption(socketOpt); + parser.addHelpOption(); + + parser.process(*this); + + qmlEngine_->load(parser.value(qmlOpt)); + if (qmlEngine_->rootObjects().empty()) + throw std::invalid_argument("bad qml file"); +} diff --git a/examples/gui/application.hh b/examples/gui/application.hh @@ -0,0 +1,15 @@ +#pragma once + +#include <QGuiApplication> + +class QQmlApplicationEngine; + +class Application : public QGuiApplication { + Q_OBJECT; + +public: + Application(int argc, char **argv); + +private: + QQmlApplicationEngine *qmlEngine_ = nullptr; +}; +\ No newline at end of file diff --git a/examples/gui/main.cc b/examples/gui/main.cc @@ -1,4 +1,7 @@ -int main(int argc, char **argv) -{ - return 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/host/application.cc b/examples/host/application.cc @@ -22,10 +22,10 @@ Application::Application(int argc, char **argv) assert(!instance_); instance_ = this; - QApplication::setOrganizationDomain("u-he.com"); - QApplication::setOrganizationName("u-he"); - QApplication::setApplicationName("uhost"); - QApplication::setApplicationVersion("1.0"); + setOrganizationDomain("u-he.com"); + setOrganizationName("u-he"); + setApplicationName("uhost"); + setApplicationVersion("1.0"); parseCommandLine(); diff --git a/examples/host/plugin-host.cc b/examples/host/plugin-host.cc @@ -53,7 +53,7 @@ PluginHost::PluginHost(Engine &engine) : QObject(&engine), engine_(engine) { hostQuickControls_.pages_changed = PluginHost::clapQuickControlsPagesChanged; hostQuickControls_.selected_page_changed = PluginHost::clapQuickControlsSelectedPageChanged; - hostState_.mark_dirty = PluginHost::clapMarkSetDirty; + hostState_.mark_dirty = PluginHost::clapStateMarkDirty; initThreadPool(); } @@ -977,7 +977,7 @@ bool PluginHost::loadNativePluginPreset(const std::string &path) { return pluginPresetLoad_->from_file(plugin_, path.c_str()); } -void PluginHost::clapMarkSetDirty(const clap_host *host) { +void PluginHost::clapStateMarkDirty(const clap_host *host) { checkForMainThread(); auto h = fromHost(host); @@ -986,7 +986,7 @@ void PluginHost::clapMarkSetDirty(const clap_host *host) { throw std::logic_error("Plugin called clap_host_state.set_dirty() but the host does not " "provide a complete clap_plugin_state interface."); - // TODO set dirty + h->stateIsDirty_ = true; } void PluginHost::setPluginState(PluginState state) { diff --git a/examples/host/plugin-host.hh b/examples/host/plugin-host.hh @@ -117,7 +117,7 @@ private: /* clap host gui callbacks */ static bool clapGuiResize(const clap_host *host, int32_t width, int32_t height); - static void clapMarkSetDirty(const clap_host *host); + static void clapStateMarkDirty(const clap_host *host); private: Engine &engine_; @@ -213,6 +213,7 @@ private: void setPluginState(PluginState state); PluginState state_ = Inactive; + bool stateIsDirty_ = false; bool scheduleDeactivateForParameterScan_ = false; uint32_t scheduleParamsRescanFlags_ = 0; }; diff --git a/examples/plugins/CMakeLists.txt b/examples/plugins/CMakeLists.txt @@ -1,3 +1,5 @@ +find_package(Boost REQUIRED COMPONENTS serialization iostreams) + add_library( clap-plugins SHARED clap-entry.cc @@ -6,9 +8,10 @@ add_library( parameter-interpolator.hh plugin-helper.cc plugin-helper.hh + stream-helper.hh dc-offset/dc-offset.hh dc-offset/dc-offset.cc gain/gain.hh gain/gain.cc) -target_link_libraries(clap-plugins clap-plugin-glue) +target_link_libraries(clap-plugins clap-plugin-glue Boost::serialization Boost::iostreams) set_target_properties(clap-plugins PROPERTIES CXX_STANDARD 20) diff --git a/examples/plugins/clap-entry.cc b/examples/plugins/clap-entry.cc @@ -7,6 +7,7 @@ #include <vector> #include "gain/gain.hh" +#include "dc-offset/dc-offset.hh" struct PluginEntry { using create_func = std::function<const clap_plugin *(const clap_host *)>; @@ -30,6 +31,7 @@ static void addPlugin() { static bool clap_init(const char *plugin_path) { addPlugin<clap::Gain>(); + addPlugin<clap::DcOffset>(); return true; } diff --git a/examples/plugins/parameters.hh b/examples/plugins/parameters.hh @@ -3,6 +3,12 @@ #include <unordered_map> #include <vector> +#include <boost/serialization/serialization.hpp> +#include <boost/serialization/split_member.hpp> +#include <boost/serialization/version.hpp> +#include <boost/serialization/vector.hpp> +#include <boost/serialization/utility.hpp> + #include <clap/all.h> #include "parameter-interpolator.hh" @@ -17,6 +23,10 @@ namespace clap { const double modulatedValue() const noexcept { return value_ + modulation_; } const clap_param_info &info() const noexcept { return info_; } + void setDefaultValue() { + setValue(info_.default_value, 0); + } + void setValue(double val, double mod) { value_ = val; modulation_ = mod; @@ -56,6 +66,16 @@ namespace clap { } private: + friend class boost::serialization::access; + // When the class Archive corresponds to an output archive, the + // & operator is defined similar to <<. Likewise, when the class Archive + // is a type of input archive the & operator is defined similar to >>. + template <class Archive> + void serialize(Archive &ar, const unsigned int version) { + ar &info_.id; + ar &value_; + } + clap_param_info info_; double value_; @@ -83,7 +103,40 @@ namespace clap { 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->setValue(v.second, 0); + } + } + + BOOST_SERIALIZATION_SPLIT_MEMBER() + std::vector<std::unique_ptr<Parameter>> params_; std::unordered_map<clap_id, Parameter *> id2param_; }; -} // namespace clap -\ No newline at end of file + +} // namespace clap + +BOOST_CLASS_VERSION(clap::Parameters, 1) +\ No newline at end of file diff --git a/examples/plugins/plugin-helper.cc b/examples/plugins/plugin-helper.cc @@ -1,4 +1,8 @@ +#include <boost/archive/text_iarchive.hpp> +#include <boost/archive/text_oarchive.hpp> + #include "plugin-helper.hh" +#include "stream-helper.hh" namespace clap { @@ -53,4 +57,26 @@ namespace clap { } bool PluginHelper::audioPortsSetConfig(clap_id config_id) noexcept { return false; } + + bool PluginHelper::stateSave(clap_ostream *stream) noexcept { + try { + OStream os(stream); + boost::archive::text_oarchive ar(os); + ar << parameters_; + } catch (...) { + return false; + } + return true; + } + + bool PluginHelper::stateLoad(clap_istream *stream) noexcept { + try { + IStream is(stream); + boost::archive::text_iarchive ar(is); + ar >> parameters_; + } catch (...) { + return false; + } + return true; + } } // namespace clap \ No newline at end of file diff --git a/examples/plugins/plugin-helper.hh b/examples/plugins/plugin-helper.hh @@ -10,15 +10,21 @@ namespace clap { PluginHelper(const clap_plugin_descriptor *desc, const clap_host *host); protected: - // clap_plugin + //-------------// + // clap_plugin // + //-------------// bool init() noexcept override; void initTrackInfo() noexcept; - // clap_plugin_track_info + //------------------------// + // clap_plugin_track_info // + //------------------------// bool implementsTrackInfo() const noexcept override { return true; } void trackInfoChanged() noexcept override; - // clap_plugin_audio_ports + //-------------------------// + // clap_plugin_audio_ports // + //-------------------------// bool implementsAudioPorts() const noexcept override; uint32_t audioPortsCount(bool is_input) const noexcept override; bool audioPortsInfo(uint32_t index, @@ -29,7 +35,9 @@ namespace clap { clap_audio_ports_config *config) const noexcept override; bool audioPortsSetConfig(clap_id config_id) noexcept override; - // clap_plugin_params + //--------------------// + // clap_plugin_params // + //--------------------// bool implementsParams() const noexcept override { return true; } uint32_t paramsCount() const noexcept override { return parameters_.count(); } @@ -59,6 +67,13 @@ namespace clap { 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; + ////////////////////// // Cached Host Info // ////////////////////// diff --git a/examples/plugins/stream-helper.hh b/examples/plugins/stream-helper.hh @@ -0,0 +1,34 @@ +#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