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