Loading...
Searching...
No Matches
score-plugin-clap/Clap/EffectModel.hpp
1#pragma once
2#include <optional>
3#include <Process/GenericProcessFactory.hpp>
4#include <Process/Process.hpp>
5#include <Process/ProcessMetadata.hpp>
6
7#include <Control/DefaultEffectItem.hpp>
8#include <Effect/EffectFactory.hpp>
9
11#include <score/serialization/JSONVisitor.hpp>
12
13#include <ossia/detail/flat_map.hpp>
14#include <ossia/detail/hash_map.hpp>
15
16#include <QSocketNotifier>
17
18#include <clap/all.h>
19
20#include <memory>
21#include <verdigris>
22
23namespace Clap
24{
25class Model;
26}
27
28PROCESS_METADATA(
29 , Clap::Model, "10e8d26d-1d82-4bfc-a9fb-d85ffdf04e5f", "CLAP", "CLAP",
30 Process::ProcessCategory::Other, "Plugins", "CLever Audio Plug-in", "ossia score",
31 {}, {}, {},
32 QUrl(
33 "https://ossia.io/score-docs/processes/"
34 "audio-plugins.html#common-formats-vst-vst3-lv2-jsfx"),
35 Process::ProcessFlags::ExternalEffect)
36DESCRIPTION_METADATA(, Clap::Model, "Clap")
37
38namespace Clap
39{
40class Window;
41class Model;
42struct PluginHandle
43{
44 PluginHandle();
45 PluginHandle(const PluginHandle&) = delete;
46 PluginHandle(PluginHandle&&) = delete;
47 PluginHandle& operator=(const PluginHandle&) = delete;
48 PluginHandle& operator=(PluginHandle&&) = delete;
49 ~PluginHandle();
50
51 void load(Model& context, QByteArray path, QByteArray id);
52 void* library{};
53 const clap_plugin_entry_t* entry{};
54 const clap_plugin_factory_t* factory{};
55 const clap_plugin_t* plugin{};
56 const clap_plugin_descriptor_t* desc{};
57
58 clap_host_t host{};
59
60 // Common plugin extensions
61 const clap_plugin_params_t* ext_params{};
62 const clap_plugin_timer_support_t* ext_timer_support{};
63
64 // Metadata we parsed
65 std::vector<clap_param_info_t> m_parameters_ins;
66 std::vector<clap_param_info_t> m_parameters_outs;
67 std::vector<clap_audio_port_info_t> m_audio_ins;
68 std::vector<clap_audio_port_info_t> m_audio_outs;
69 std::vector<clap_note_port_info_t> m_midi_ins;
70 std::vector<clap_note_port_info_t> m_midi_outs;
71
72 ossia::flat_map<clap_id, std::uint32_t> param_outs_by_id;
73
74 QPointer<Model> model;
75
76 bool activated{};
77};
78
79class Model final : public Process::ProcessModel
80{
81 W_OBJECT(Model)
82 SCORE_SERIALIZE_FRIENDS
83
84public:
85 MODEL_METADATA_IMPL(Model)
86
87 Model(
88 const TimeVal& duration, const QString& pluginPath,
89 const Id<Process::ProcessModel>& id, QObject* parent);
90
91 Model(DataStream::Deserializer& vis, QObject* parent);
92 Model(JSONObject::Deserializer& vis, QObject* parent);
93 Model(DataStream::Deserializer&& vis, QObject* parent);
94 Model(JSONObject::Deserializer&& vis, QObject* parent);
95
96 ~Model() override;
97
99 QString effect() const noexcept override;
100
101 QString prettyShortName() const noexcept override
102 {
104 }
105 QString category() const noexcept override
106 {
108 }
109 QStringList tags() const noexcept override { return Metadata<Tags_k, Model>::get(); }
110 Process::ProcessFlags flags() const noexcept override;
111 bool hasExternalUI() const noexcept;
112 const std::shared_ptr<PluginHandle>& handle() const noexcept { return m_plugin; }
113
114 void closeUI() const;
115
116 const QString& pluginId() const noexcept { return m_pluginId; }
117 bool supports64() const noexcept { return m_supports64; }
118
119 void mapExternalFiles(Process::ExternalFileMap& map) override;
120
121 const std::vector<clap_param_info_t>& parameterInputs() const noexcept
122 {
123 return m_plugin->m_parameters_ins;
124 }
125 const std::vector<clap_param_info_t>& parameterOutputs() const noexcept
126 {
127 return m_plugin->m_parameters_outs;
128 }
129 const std::vector<clap_note_port_info_t>& midiInputs() const noexcept
130 {
131 return m_plugin->m_midi_ins;
132 }
133 const std::vector<clap_note_port_info_t>& midiOutputs() const noexcept
134 {
135 return m_plugin->m_midi_outs;
136 }
137 const std::vector<clap_audio_port_info_t>& audioInputs() const noexcept
138 {
139 return m_plugin->m_audio_ins;
140 }
141 const std::vector<clap_audio_port_info_t>& audioOutputs() const noexcept
142 {
143 return m_plugin->m_audio_outs;
144 }
145
146 // Preset functionality
147 void loadPreset(const Process::Preset& preset) override;
150 bool presetChangesPorts(const Process::Preset&) const noexcept override
151 {
152 return false;
153 }
156 bool presetMatches(const Process::Preset& preset) const noexcept override;
157 Process::Preset savePreset() const noexcept override;
158 std::vector<Process::Preset> builtinPresets() const noexcept override;
159
160 void restartPlugin() W_SIGNAL(restartPlugin);
161 Clap::Window* window{};
162 std::vector<std::pair<clap_id, QTimer*>> timers;
163
164 struct FdNotifiers
165 {
166 std::unique_ptr<QSocketNotifier> read;
167 std::unique_ptr<QSocketNotifier> write;
168 std::unique_ptr<QSocketNotifier> error;
169 };
170 ossia::hash_map<int, std::unique_ptr<FdNotifiers>> fd_notifiers;
171
172 void requestFlush() W_SIGNAL(requestFlush);
173
174 void flushFromPluginToHost();
175
176 bool currentlyReadingValues{};
177
178 void markStateDirty();
179 void stateSnapshotChanged(const QByteArray& blob)
180 W_SIGNAL(stateSnapshotChanged, blob);
181
182 void onParamRescan(clap_param_rescan_flags flags);
183
184private:
185 void loadPlugin();
186 void createControls(bool loading);
187
188 void refreshParamValues();
190 std::vector<Process::Preset> discoverPresets() const noexcept;
191 mutable std::optional<std::vector<Process::Preset>> m_builtinPresets;
192 // Returns false on structural mismatch — caller should treat as RESCAN_ALL.
193 bool refreshParamInfoIfStable();
194 void setupControlInlet(
195 const clap_plugin_params_t&, const clap_param_info_t& info, int index,
196 Process::ControlInlet* ctl);
197 void setupControlOutlet(
198 const clap_plugin_params_t&, const clap_param_info_t& info, int index,
199 Process::ControlOutlet* ctl);
200
201 void sendParamChange(const clap_param_info_t& info, double value);
202
203 QTimer* m_stateDirtyTimer{};
204
205 QString m_pluginPath;
206 QString m_pluginId;
207
208 std::shared_ptr<PluginHandle> m_plugin;
209 QByteArray m_loadedState;
210
211 bool m_supports64{};
212
213 clap_param_rescan_flags m_pendingRescanFlags{};
214};
215}
216
217namespace Process
218{
219template <>
220QString EffectProcessFactory_T<Clap::Model>::customConstructionData() const noexcept;
221
222template <>
223Process::Descriptor
224EffectProcessFactory_T<Clap::Model>::descriptor(QString d) const noexcept;
225}
226
227namespace Clap
228{
229class Window;
232}
Definition DataStreamVisitor.hpp:202
Definition JSONVisitor.hpp:423
Definition EffectFactory.hpp:86
Definition EffectFactory.hpp:14
Collects — and in the same pass rewrites — the files a document references.
Definition ExternalFiles.hpp:93
The Process class.
Definition score-lib-process/Process/Process.hpp:82
virtual void mapExternalFiles(Process::ExternalFileMap &map)
Definition ExternalFiles.cpp:153
virtual bool presetMatches(const Preset &preset) const noexcept
Whether the preset applies to this process: same process key and same effect.
Definition score-lib-process/Process/Process.cpp:259
virtual bool presetChangesPorts(const Preset &preset) const noexcept
Definition score-lib-process/Process/Process.cpp:264
The id_base_t class.
Definition Identifier.hpp:59
Base classes and tools to implement processes and layers.
Definition JSONVisitor.hpp:1115
bool loadPreset(const Process::ProcessModel &proc, const Process::Preset &preset, const score::DocumentContext &ctx)
Definition PresetTree.cpp:167
ProcessFlags
Various settings for processes.
Definition ProcessFlags.hpp:17
@ Window
A separate top-level window.
STL namespace.
Static metadata implementation.
Definition lib/score/tools/Metadata.hpp:36
Definition Preset.hpp:32
Definition TimeValue.hpp:21