Audacity 3.2.0
LV2FeaturesList.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 LV2FeaturesList.cpp
6
7 Paul Licameli split from LV2Effect.cpp
8
9 Audacity(R) is copyright (c) 1999-2008 Audacity Team.
10 License: GPL v2 or later. See License.txt.
11
12**********************************************************************/
13
14
15
16#if defined(USE_LV2)
17
18#if defined(__GNUC__)
19#pragma GCC diagnostic ignored "-Wparentheses"
20#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
21#elif defined(__clang__)
22#pragma clang diagnostic ignored "-Wparentheses"
23#pragma clang diagnostic ignored "-Wdeprecated-declarations"
24#endif
25
26#include "LV2FeaturesList.h"
27#include <wx/crt.h>
28#include <wx/log.h>
29#include "lv2/buf-size/buf-size.h"
30#include "lv2_external_ui.h"
31#include "lv2/worker/worker.h"
32
33LV2FeaturesList::LV2FeaturesList(const LilvPlugin &plug) : mPlug{ plug }
34 , mSuppliesWorkerInterface{ SuppliesWorkerInterface(plug) }
35{
36}
37
39{
40 using namespace LV2Symbols;
41
42 // Construct the null-terminated array describing options, and validate it
43 AddOption(urid_SequenceSize, sizeof(mSeqSize), urid_Int, &mSeqSize);
44 AddOption(urid_MinBlockLength,
45 sizeof(mMinBlockSize), urid_Int, &mMinBlockSize);
46 AddOption(urid_MaxBlockLength,
47 sizeof(mMaxBlockSize), urid_Int, &mMaxBlockSize);
48 // Two options are reset later
49 mBlockSizeOption = AddOption(urid_NominalBlockLength,
50 sizeof(mBlockSize), urid_Int, &mBlockSize);
51 AddOption(urid_SampleRate,
52 sizeof(mSampleRate), urid_Float, &mSampleRate);
53 AddOption(0, 0, 0, nullptr);
54 if (!ValidateOptions(lilv_plugin_get_uri(&mPlug)))
55 return false;
56
57 // Adjust the values in the block size features according to the plugin
58 if (LilvNodePtr minLength{ lilv_world_get(gWorld,
59 lilv_plugin_get_uri(&mPlug), node_MinBlockLength, nullptr) }
60 ; lilv_node_is_int(minLength.get())
61 ){
62 if (auto value = lilv_node_as_int(minLength.get())
63 ; value >= 0
64 )
65 mMinBlockSize = std::max<size_t>(mMinBlockSize, value);
66 }
67 if (LilvNodePtr maxLength{ lilv_world_get(gWorld,
68 lilv_plugin_get_uri(&mPlug), node_MaxBlockLength, nullptr) }
69 ; lilv_node_is_int(maxLength.get())
70 ){
71 if (auto value = lilv_node_as_int(maxLength.get())
72 ; value >= 1
73 )
74 mMaxBlockSize = std::min<size_t>(mMaxBlockSize, value);
75 }
77
78 return true;
79}
80
82{
83 // Construct null-terminated array of "features" describing our capabilities
84 // to lv2, and validate
85 AddFeature(LV2_UI__noUserResize, nullptr);
86 AddFeature(LV2_UI__fixedSize, nullptr);
87 AddFeature(LV2_UI__idleInterface, nullptr);
89 AddFeature(LV2_BUF_SIZE__boundedBlockLength, nullptr);
90 AddFeature(LV2_BUF_SIZE__fixedBlockLength, nullptr);
91 AddFeature(LV2_OPTIONS__options, mOptions.data());
92 AddFeature(LV2_URI_MAP_URI, &mUriMapFeature);
93 AddFeature(LV2_URID__map, &mURIDMapFeature);
94 AddFeature(LV2_URID__unmap, &mURIDUnmapFeature);
95 AddFeature(LV2_LOG__log, &mLogFeature);
96 // Some plugins specify this as a feature
98 AddFeature(LV2_UI__parent, nullptr);
99
100 return true;
101}
102
104{
105 bool result = false;
106 if (LilvNodesPtr extdata{ lilv_plugin_get_extension_data(&plug) }) {
107 LILV_FOREACH(nodes, i, extdata.get()) {
108 const auto node = lilv_nodes_get(extdata.get(), i);
109 const auto uri = lilv_node_as_string(node);
110 if (strcmp(uri, LV2_WORKER__interface) == 0)
111 result = true;
112 }
113 }
114 return result;
115}
116
118 LV2_URID key, uint32_t size, LV2_URID type, const void *value)
119{
120 int ndx = mOptions.size();
121 if (key != 0)
122 mOptions.emplace_back(LV2_Options_Option{
123 LV2_OPTIONS_INSTANCE, 0, key, size, type, value });
124 else
125 mOptions.emplace_back(LV2_Options_Option{});
126 return ndx;
127}
128
129void LV2FeaturesList::AddFeature(const char *uri, const void *data)
130{
131 // This casting to const is innocent
132 // We pass our "virtual function tables" or array of options, which the
133 // library presumably will not change
134 mFeatures.emplace_back(LV2_Feature{ uri, const_cast<void*>(data) });
135}
136
138{
139 FeaturePointers result;
140 for (auto &feature : mFeatures)
141 result.push_back(&feature);
142 result.push_back(nullptr);
143 return result;
144}
145
146const LV2_Options_Option *LV2FeaturesList::NominalBlockLengthOption() const
147{
149 return &mOptions[mBlockSizeOption];
150 else
151 return nullptr;
152}
153
154bool LV2FeaturesList::ValidateFeatures(const LilvNode *subject)
155{
156 return CheckFeatures(subject, true) && CheckFeatures(subject, false);
157}
158
159bool LV2FeaturesList::CheckFeatures(const LilvNode *subject, bool required)
160{
161 using namespace LV2Symbols;
162 bool supported = true;
163 auto predicate = required ? node_RequiredFeature : node_OptionalFeature;
164 if (LilvNodesPtr nodes{
165 lilv_world_find_nodes(gWorld, subject, predicate, nullptr) }) {
166 LILV_FOREACH(nodes, i, nodes.get()) {
167 const auto node = lilv_nodes_get(nodes.get(), i);
168 const auto uri = lilv_node_as_string(node);
169 if ((strcmp(uri, LV2_UI__noUserResize) == 0) ||
170 (strcmp(uri, LV2_UI__fixedSize) == 0))
171 mNoResize = true;
172 else if (strcmp(uri, LV2_WORKER__schedule) == 0) {
173 /* Supported but handled in LV2Wrapper */
174 }
175 else if (required) {
176 const auto end = mFeatures.end();
177 supported = (end != std::find_if(mFeatures.begin(), end,
178 [&](auto &feature){ return strcmp(feature.URI, uri) == 0; }));
179 if (!supported) {
180 wxLogError(wxT("%s requires unsupported feature %s"),
181 lilv_node_as_string(lilv_plugin_get_uri(&mPlug)), uri);
182 break;
183 }
184 }
185 }
186 }
187 return supported;
188}
189
190bool LV2FeaturesList::ValidateOptions(const LilvNode *subject)
191{
192 return CheckOptions(subject, true) && CheckOptions(subject, false);
193}
194
195bool LV2FeaturesList::CheckOptions(const LilvNode *subject, bool required)
196{
197 using namespace LV2Symbols;
198 bool supported = true;
199 const auto predicate =
200 required ? node_RequiredOption : node_SupportedOption;
201 if (LilvNodesPtr nodes{
202 lilv_world_find_nodes(gWorld, subject, predicate, nullptr) }) {
203 LILV_FOREACH(nodes, i, nodes.get()) {
204 const auto node = lilv_nodes_get(nodes.get(), i);
205 const auto uri = lilv_node_as_string(node);
206 const auto urid = URID_Map(uri);
207 if (urid == urid_NominalBlockLength)
209 // else if (urid == urid_SampleRate)
210 // mSupportsSampleRate = true; // supports changing sample rate
211 else if (required) {
212 const auto end = mOptions.end();
213 supported = (end != std::find_if(mOptions.begin(), end,
214 [&](const auto &option){ return option.key == urid; }));
215 if (!supported) {
216 wxLogError(wxT("%s requires unsupported option %s"),
217 lilv_node_as_string(lilv_plugin_get_uri(&mPlug)), uri);
218 break;
219 }
220 }
221 }
222 }
223 return supported;
224}
225
226// ============================================================================
227// Feature handlers
228// ============================================================================
229
230// static callback
232 LV2_URI_Map_Callback_Data callback_data, const char *, const char *uri)
233{
234 return static_cast<LV2FeaturesList *>(callback_data)->URID_Map(uri);
235}
236
237// static callback
238LV2_URID LV2FeaturesList::urid_map(LV2_URID_Map_Handle handle, const char *uri)
239{
240 return static_cast<LV2FeaturesList *>(handle)->URID_Map(uri);
241}
242
243LV2_URID LV2FeaturesList::URID_Map(const char *uri)
244{
245 using namespace LV2Symbols;
246 // Map global URIs to lower indices
247 auto urid = Lookup_URI(gURIDMap, uri, false);
248 if (urid > 0)
249 return urid;
250 // Map local URIs to higher indices
251 urid = Lookup_URI(mURIDMap, uri);
252 if (urid > 0)
253 return urid + gURIDMap.size();
254 return 0;
255}
256
257// static callback
258const char *LV2FeaturesList::urid_unmap(LV2_URID_Unmap_Handle handle, LV2_URID urid)
259{
260 return static_cast<LV2FeaturesList *>(handle)->URID_Unmap(urid);
261}
262
263const char *LV2FeaturesList::URID_Unmap(LV2_URID urid)
264{
265 using namespace LV2Symbols;
266 if (urid > 0) {
267 // Unmap lower indices to global URIs
268 if (urid <= static_cast<LV2_URID>(gURIDMap.size()))
269 return mURIDMap[urid - 1].get();
270 // Unmap higher indices to local URIs
271 urid -= gURIDMap.size();
272 if (urid <= static_cast<LV2_URID>(mURIDMap.size()))
273 return mURIDMap[urid - 1].get();
274 }
275 return nullptr;
276}
277
278// static callback
280 LV2_Log_Handle handle, LV2_URID type, const char *fmt, ...)
281{
282 va_list ap;
283 int len;
284
285 va_start(ap, fmt);
286 len = static_cast<LV2FeaturesList *>(handle)->LogVPrintf(type, fmt, ap);
287 va_end(ap);
288
289 return len;
290}
291
292// static callback
294 LV2_Log_Handle handle, LV2_URID type, const char *fmt, va_list ap)
295{
296 return static_cast<LV2FeaturesList *>(handle)->LogVPrintf(type, fmt, ap);
297}
298
299int LV2FeaturesList::LogVPrintf(LV2_URID type, const char *fmt, va_list ap)
300{
301 using namespace LV2Symbols;
302 long level = wxLOG_Error;
303 if (type == urid_Error)
304 level = wxLOG_Error;
305 else if (type == urid_Note)
306 level = wxLOG_Info;
307 else if (type == urid_Trace)
308 level = wxLOG_Trace;
309 else if (type == urid_Warning)
310 level = wxLOG_Warning;
311 else
312 level = wxLOG_Message;
313 int len = wxCRT_VsnprintfA(nullptr, 0, fmt, ap);
314 auto msg = std::make_unique<char[]>(len + 1);
315 wxCRT_VsnprintfA(msg.get(), len, fmt, ap);
316 wxString text(msg.get());
317 wxLogGeneric(level,
318 wxT("%s: %s"), GetSymbol().Msgid().Translation(), text);
319 return len;
320}
321
322#endif
static const AudacityProject::AttachedObjects::RegisteredFactory key
Lilv_ptr< LilvNodes, lilv_nodes_free > LilvNodesPtr
#define LV2_UI__makeResident
Definition: LV2Symbols.h:31
Lilv_ptr< LilvNode, lilv_node_free > LilvNodePtr
Definition: LV2Utils.h:29
ComponentInterfaceSymbol GetSymbol() const override
Definition: Effect.cpp:74
void AddFeature(const char *uri, const void *data)
const LV2_URID_Map mURIDMapFeature
LV2Symbols::URIDMap mURIDMap
LV2_URID URID_Map(const char *uri)
const LV2_Log_Log mLogFeature
static LV2_URID urid_map(LV2_URID_Map_Handle handle, const char *uri)
LV2FeaturesList(const LilvPlugin &plug)
const LV2_Options_Option * NominalBlockLengthOption() const
const char * URID_Unmap(LV2_URID urid)
std::vector< LV2_Feature > mFeatures
FeaturePointers GetFeaturePointers() const
std::vector< LV2_Options_Option > mOptions
const LV2_URI_Map_Feature mUriMapFeature
size_t AddOption(LV2_URID, uint32_t size, LV2_URID, const void *value)
static uint32_t uri_to_id(LV2_URI_Map_Callback_Data callback_data, const char *map, const char *uri)
bool mSupportsNominalBlockLength
const LV2_URID_Unmap mURIDUnmapFeature
const LilvPlugin & mPlug
static int log_printf(LV2_Log_Handle handle, LV2_URID type, const char *fmt,...)
bool CheckOptions(const LilvNode *subject, bool required)
static const char * urid_unmap(LV2_URID_Unmap_Handle handle, LV2_URID urid)
std::vector< const LV2_Feature * > FeaturePointers
Get vector of pointers to features, whose .data() can be passed to lv2.
bool ValidateOptions(const LilvNode *subject)
static int log_vprintf(LV2_Log_Handle handle, LV2_URID type, const char *fmt, va_list ap)
int LogVPrintf(LV2_URID type, const char *fmt, va_list ap)
bool CheckFeatures(const LilvNode *subject, bool required)
bool SuppliesWorkerInterface() const
bool ValidateFeatures(const LilvNode *subject)
#define LV2_EXTERNAL_UI__Widget
LilvWorld * gWorld
Definition: LV2Symbols.cpp:31
LV2_URID Lookup_URI(URIDMap &map, const char *uri, bool add)
Definition: LV2Symbols.cpp:76
URIDMap gURIDMap
Declare the global map of positive integers to URIs.
Definition: LV2Symbols.cpp:34
auto end(const Ptr< Type, BaseDeleter > &p)
Enables range-for.
Definition: PackedArray.h:159