Audacity 3.2.0
LV2Ports.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 LV2Ports.cpp
6
7 Paul Licameli split from LV2Effect.cpp
8
9 Audacity(R) is copyright (c) 1999-2013 Audacity Team.
10 License: GPL v2 or later. See License.txt.
11
12*********************************************************************/
13
14#include "LV2Ports.h"
15#include "LV2Symbols.h"
16#include "Internat.h"
17
18#include <wx/log.h>
19#include <cmath>
20
22 LV2_Atom_Forge &forge, const int64_t frameTime, const float speed
23){
24 using namespace LV2Symbols;
25 auto &port = mpPort;
26 const auto buf = mBuffer.get();
27 if (port->mIsInput) {
28 // TAKE from an inter-thread ring buffer;
29 // PUT to an "atom forge" which is a serializer of structured information
30 // and the forge populates the buffer that the atom port was connected to
31 // and it also receives other information about speed and accumulated
32 // number of samples
33 lv2_atom_forge_set_buffer(&forge, buf, port->mMinimumSize);
34 LV2_Atom_Forge_Frame seqFrame;
35 const auto seq = reinterpret_cast<LV2_Atom_Sequence *>(
36 lv2_atom_forge_sequence_head(&forge, &seqFrame, 0));
37 if (port->mWantsPosition) {
38 lv2_atom_forge_frame_time(&forge, frameTime);
39 LV2_Atom_Forge_Frame posFrame;
40 lv2_atom_forge_object(&forge, &posFrame, 0, urid_Position);
41 lv2_atom_forge_key(&forge, urid_Speed);
42 lv2_atom_forge_float(&forge, speed);
43 lv2_atom_forge_key(&forge, urid_Frame);
44 lv2_atom_forge_long(&forge, frameTime);
45 lv2_atom_forge_pop(&forge, &posFrame);
46 }
47 // Copy event information from the UI thread into plugin's atom input,
48 // inserting frame time
49 const auto ring = mRing.get();
50 LV2_Atom atom;
51 while (zix_ring_read(ring, &atom, sizeof(atom))) {
52 if (forge.offset + sizeof(LV2_Atom_Event) + atom.size < forge.size){
53 lv2_atom_forge_frame_time(&forge, frameTime);
54 lv2_atom_forge_write(&forge, &atom, sizeof(atom));
55 zix_ring_read(ring, &forge.buf[forge.offset], atom.size);
56 forge.offset += atom.size;
57 seq->atom.size += atom.size;
58 }
59 else {
60 zix_ring_skip(ring, atom.size);
61 wxLogError(wxT("LV2 sequence buffer overflow"));
62 }
63 }
64 lv2_atom_forge_pop(&forge, &seqFrame);
65#if 0
66 LV2_ATOM_SEQUENCE_FOREACH(seq, ev) {
67 auto o = reinterpret_cast<LV2_Atom_Object *>(&ev->body);
68 wxLogDebug(wxT("ev = %lld ev.size %d ev.type %d"), ev->time.frames, ev->body.size, ev->body.type);
69 }
70#endif
71 }
72 else
73 // PRL: I'm not sure why this must be done both before lilv_instance_run
74 // and after, but that's the legacy
76}
77
79 using namespace LV2Symbols;
80 auto &port = mpPort;
81 if (!port->mIsInput) {
82 const auto buf = mBuffer.get();
83 *reinterpret_cast<LV2_Atom *>(buf) = { port->mMinimumSize, urid_Chunk };
84 }
85}
86
88 std::function<void(const LV2_Atom *atom, uint32_t size)> handler)
89{
90 const auto ring = mRing.get();
91 const auto minimumSize = mpPort->mMinimumSize;
92 const auto space = std::make_unique<char[]>(minimumSize);
93 auto atom = reinterpret_cast<LV2_Atom*>(space.get());
94 // Consume messages from the processing thread and pass to the
95 // foreign UI code, for updating output displays
96 while (zix_ring_read(ring, atom, sizeof(LV2_Atom))) {
97 uint32_t size = lv2_atom_total_size(atom);
98 if (size < minimumSize) {
99 zix_ring_read(ring,
100 LV2_ATOM_CONTENTS(LV2_Atom, atom), atom->size);
101 handler(atom, size);
102 }
103 else {
104 zix_ring_skip(ring, atom->size);
105 wxLogError(wxT("LV2 sequence buffer overflow"));
106 }
107 }
108}
109
111{
112 if (!mpPort->mIsInput) {
113 const auto ring = mRing.get();
114 LV2_ATOM_SEQUENCE_FOREACH(
115 reinterpret_cast<LV2_Atom_Sequence *>(mBuffer.get()), ev
116 )
117 zix_ring_write(ring, &ev->body, ev->body.size + sizeof(LV2_Atom));
118 }
119}
120
122 const void *buffer, uint32_t buffer_size)
123{
124 // Send event information blob from the UI to the processing thread
125 zix_ring_write(mRing.get(), buffer, buffer_size);
126}
127
128size_t LV2ControlPort::Discretize(float value) const
129{
130 auto s = mScaleValues.size();
131 for (; s > 0 && --s;)
132 if (value >= mScaleValues[s])
133 break;
134 return s;
135}
136
138
139auto LV2EffectOutputs::Clone() const -> std::unique_ptr<EffectOutputs>
140{
141 return std::make_unique<LV2EffectOutputs>(*this);
142}
143
145{
146 // Don't really need to modify src
147 const auto &srcValues = static_cast<LV2EffectOutputs&>(src).values;
148 auto &dstValues = values;
149 assert(srcValues.size() == dstValues.size());
150 copy(srcValues.begin(), srcValues.end(), dstValues.data());
151}
152
153LV2Ports::LV2Ports(const LilvPlugin &plug)
154{
155 using namespace LV2Symbols;
156
157 // Collect information in mAudioPorts, mControlPorts, mAtomPorts, mCVPorts
158 // Retrieve the port ranges for all ports (some values may be NaN)
159 auto numPorts = lilv_plugin_get_num_ports(&plug);
160 Floats minimumVals {numPorts};
161 Floats maximumVals {numPorts};
162 Floats defaultVals {numPorts};
163 lilv_plugin_get_port_ranges_float(&plug,
164 minimumVals.get(), maximumVals.get(), defaultVals.get());
165
166 for (size_t i = 0; i < numPorts; ++i) {
167 const auto port = lilv_plugin_get_port_by_index(&plug, i);
168 int index = lilv_port_get_index(&plug, port);
169
170 // It must be input or output, anything else is bogus
171 bool isInput;
172 if (lilv_port_is_a(&plug, port, node_InputPort))
173 isInput = true;
174 else if (lilv_port_is_a(&plug, port, node_OutputPort))
175 isInput = false;
176 else {
177 assert(false);
178 continue;
179 }
180
181 // Get the port name and symbol
182 const auto symbol = LilvString(lilv_port_get_symbol(&plug, port));
183 const auto name = LilvStringMove(lilv_port_get_name(&plug, port));
184
185 // Get the group to which this port belongs or default to the main group
186 TranslatableString groupName{};
187 if (LilvNodePtr group{ lilv_port_get(&plug, port, node_Group) }) {
188 // lilv.h does not say whether return of lilv_world_get() needs to
189 // be freed, but that is easily seen to be so from source
190 auto groupMsg = LilvStringMove(
191 lilv_world_get(gWorld, group.get(), node_Label, nullptr));
192 if (groupMsg.empty())
193 groupMsg = LilvStringMove(
194 lilv_world_get(gWorld, group.get(), node_Name, nullptr));
195 if (groupMsg.empty())
196 groupMsg = LilvString(group.get());
197 groupName = Verbatim(groupMsg);
198 }
199 else
200 groupName = XO("Effect Settings");
201
202 // Get the latency port
203 const auto latencyIndex = lilv_plugin_get_latency_port_index(&plug);
204
205 // Get the ports designation (must be freed)
206 LilvNodePtr designation{ lilv_port_get(&plug, port, node_Designation) };
207
208 // Check for audio ports
209 if (lilv_port_is_a(&plug, port, node_AudioPort)) {
210 mAudioPorts.push_back(std::make_shared<LV2AudioPort>(
211 port, index, isInput, symbol, name, groupName));
212 isInput ? mAudioIn++ : mAudioOut++;
213 }
214 // Check for Control ports
215 else if (lilv_port_is_a(&plug, port, node_ControlPort)) {
216 // Add group if not previously done...
217 if (mGroupMap.find(groupName) == mGroupMap.end())
218 mGroups.push_back(groupName);
219 // ... That maintains the postcondition, after this:
220 mGroupMap[groupName].push_back(mControlPorts.size());
221
222 wxString units;
223 // Get any unit descriptor
224 if (LilvNodePtr unit{ lilv_port_get(&plug, port, node_Unit) })
225 // Really should use lilv_world_get_symbol()
226 if (LilvNodePtr symbol{ lilv_world_get_symbol(gWorld, unit.get()) })
227 units = LilvString(symbol.get());
228
229 // Collect the value and range info
230 bool hasLo = !std::isnan(minimumVals[i]);
231 bool hasHi = !std::isnan(maximumVals[i]);
232 float min = hasLo ? minimumVals[i] : 0.0f;
233 float max = hasHi ? maximumVals[i] : 1.0f;
234 float def = !std::isnan(defaultVals[i])
235 ? defaultVals[i]
236 : hasLo
237 ? min
238 : hasHi
239 ? max
240 : 0.0f;
241
242 // Figure out the type of port we have
243 bool toggle = isInput &&
244 lilv_port_has_property(&plug, port, node_Toggled);
245 bool enumeration = isInput &&
246 lilv_port_has_property(&plug, port, node_Enumeration);
247 bool integer = isInput &&
248 lilv_port_has_property(&plug, port, node_Integer);
249 bool sampleRate = isInput &&
250 lilv_port_has_property(&plug, port, node_SampleRate);
251 // Trigger properties can be combined with other types, but it
252 // seems mostly to be combined with toggle. So, we turn the
253 // checkbox into a button.
254 bool trigger = isInput &&
255 lilv_port_has_property(&plug, port, node_Trigger);
256 // We'll make the slider logarithmic
257 bool logarithmic = isInput &&
258 lilv_port_has_property(&plug, port, node_Logarithmic);
259
260 // Get the scale points
261 std::vector<double> scaleValues;
262 wxArrayString scaleLabels;
263 {
264 using LilvScalePointsPtr =
266 LilvScalePointsPtr points{
267 lilv_port_get_scale_points(&plug, port) };
268 LILV_FOREACH(scale_points, j, points.get()) {
269 const auto point = lilv_scale_points_get(points.get(), j);
270 scaleValues.push_back(
271 lilv_node_as_float(lilv_scale_point_get_value(point)));
272 scaleLabels.push_back(
273 LilvString(lilv_scale_point_get_label(point)));
274 }
275 }
276
277 const auto &controlPort = mControlPorts.emplace_back(
278 std::make_shared<LV2ControlPort>(
279 port, index, isInput, symbol, name, groupName,
280 move(scaleValues), std::move(scaleLabels), units,
281 min, max, def, hasLo, hasHi,
282 toggle, enumeration, integer, sampleRate,
283 trigger, logarithmic));
284 // Figure out the type of port we have
285 if (isInput)
286 mControlPortMap[controlPort->mIndex] = mControlPorts.size() - 1;
287 else if (controlPort->mIndex == latencyIndex)
288 mLatencyPort = i;
289 }
290 // Check for atom ports
291 else if (lilv_port_is_a(&plug, port, node_AtomPort)) {
292 uint32_t minimumSize = 8192;
293 if (LilvNodePtr min{ lilv_port_get(&plug, port, node_MinimumSize) }
294 ; lilv_node_is_int(min.get())
295 ){
296 if (auto value = lilv_node_as_int(min.get())
297 ; value > 0
298 )
299 minimumSize = std::max<uint32_t>(minimumSize, value);
300 }
301 bool wantsPosition =
302 lilv_port_supports_event(&plug, port, node_Position);
303 bool isMidi = lilv_port_supports_event(&plug, port, node_MidiEvent);
304 if (isMidi)
305 (isInput ? mMidiIn : mMidiOut) += 1;
306 mAtomPorts.push_back(std::make_shared<LV2AtomPort>(
307 port, index, isInput, symbol, name, groupName,
308 minimumSize, isMidi, wantsPosition));
309 bool isControl = lilv_node_equals(designation.get(), node_Control);
310 if (isInput) {
311 if (!mControlInIdx || isControl)
312 mControlInIdx = mAtomPorts.size() - 1;
313 }
314 else if (!mControlOutIdx || isControl)
315 mControlOutIdx = mAtomPorts.size() - 1;
316 }
317 // Check for CV ports
318 else if (lilv_port_is_a(&plug, port, node_CVPort)) {
319 // Collect the value and range info
320 float min = 0;
321 float max = 1;
322 float def = 0;
323 bool hasLo = false;
324 bool hasHi = false;
325 if (!std::isnan(minimumVals[i]))
326 hasLo = true, min = minimumVals[i];
327 if (!std::isnan(maximumVals[i]))
328 hasHi = true, max = maximumVals[i];
329 if (!std::isnan(defaultVals[i]))
330 def = defaultVals[i];
331 else if (hasLo)
332 def = min;
333 else if (hasHi)
334 def = max;
335 mCVPorts.push_back(std::make_shared<LV2CVPort>(
336 port, index, isInput, symbol, name, groupName,
337 min, max, def, hasLo, hasHi));
338 }
339 }
340}
341
342namespace {
345};
348const void *get_value_func(
349 const char *port_symbol, void *user_data, uint32_t *size, uint32_t *type)
350{
351 auto &[ports, settings] = *static_cast<GetValueData*>(user_data);
352 return ports.GetPortValue(settings, port_symbol, size, type);
353}
354}
355
357 const char *port_symbol, uint32_t *size, uint32_t *type) const
358{
359 wxString symbol = wxString::FromUTF8(port_symbol);
360 size_t index = 0;
361 for (auto & port : mControlPorts) {
362 if (port->mSymbol == symbol) {
363 *size = sizeof(float);
364 *type = LV2Symbols::urid_Float;
365 return &settings.values[index];
366 }
367 ++index;
368 }
369 *size = 0;
370 *type = 0;
371 return nullptr;
372}
373
374namespace {
377 const char *port_symbol, void *user_data,
378 const void *value, uint32_t size, uint32_t type)
379{
380 auto &[ports, settings] = *static_cast<SetValueData*>(user_data);
381 ports.SetPortValue(settings, port_symbol, value, size, type);
382}
383}
384
386 const char *port_symbol, const void *value, uint32_t size, uint32_t type)
387const
388{
389 wxString symbol = wxString::FromUTF8(port_symbol);
390 size_t index = 0;
391 for (auto & port : mControlPorts) {
392 if (port->mSymbol == symbol) {
393 auto &dst = settings.values[index];
394 using namespace LV2Symbols;
395 if (type == urid_Bool && size == sizeof(bool))
396 dst = *static_cast<const bool *>(value) ? 1.0f : 0.0f;
397 else if (type == urid_Double && size == sizeof(double))
398 dst = *static_cast<const double *>(value);
399 else if (type == urid_Float && size == sizeof(float))
400 dst = *static_cast<const float *>(value);
401 else if (type == urid_Int && size == sizeof(int32_t))
402 dst = *static_cast<const int32_t *>(value);
403 else if (type == urid_Long && size == sizeof(int64_t))
404 dst = *static_cast<const int64_t *>(value);
405 break;
406 }
407 ++index;
408 }
409}
410
412 const LilvState &state, LV2EffectSettings &settings) const
413{
414 SetValueData data{ *this, settings };
415 // Get the control port values from the state into settings
416 lilv_state_emit_port_values(&state, set_value_func, &data);
417}
418
420{
421 for (auto &atomPort : ports.mAtomPorts)
422 mAtomPortStates.emplace_back(
423 std::make_shared<LV2AtomPortState>(atomPort));
424
425 for (auto &cvPort : ports.mCVPorts)
426 mCVPortStates.emplace_back(cvPort);
427}
428
430 const LV2PortStates &portStates, const LV2Ports &ports)
431{
432 // Ignore control designation if one of them is missing
433 if (ports.mControlInIdx && ports.mControlOutIdx) {
434 mControlIn = portStates.mAtomPortStates[*ports.mControlInIdx];
435 mControlOut = portStates.mAtomPortStates[*ports.mControlOutIdx];
436 }
437
438 for (auto &controlPort : ports.mControlPorts) {
439 auto &state = mControlPortStates.emplace_back(controlPort);
440 state.mLo = controlPort->mMin;
441 state.mHi = controlPort->mMax;
442 state.mLst = controlPort->mDef;
443 }
444}
wxT("CloseDown"))
int min(int a, int b)
XO("Cut/Copy/Paste")
Declare URI identifiers used in calls to the lv2 library and a mapping of them to integers.
wxString LilvString(const LilvNode *node)
Definition: LV2Utils.h:37
std::unique_ptr< Type, Lilv_deleter< Type, f > > Lilv_ptr
Generate classes of smart pointers to lv2 resources.
Definition: LV2Utils.h:26
wxString LilvStringMove(LilvNode *node)
Definition: LV2Utils.h:45
Lilv_ptr< LilvNode, lilv_node_free > LilvNodePtr
Definition: LV2Utils.h:33
wxString name
Definition: TagsEditor.cpp:166
static Settings & settings()
Definition: TrackInfo.cpp:51
TranslatableString Verbatim(wxString str)
Require calls to the one-argument constructor to go through this distinct global function name.
Hold values to send to effect output meters.
size_t Discretize(float value) const
Map a real number to one of the scale points.
Definition: LV2Ports.cpp:128
const std::vector< double > mScaleValues
Definition: LV2Ports.h:183
LV2PortStates(const LV2Ports &ports)
Definition: LV2Ports.cpp:419
LV2CVPortStateArray mCVPortStates
Definition: LV2Ports.h:299
LV2AtomPortStateArray mAtomPortStates
Definition: LV2Ports.h:298
LV2AtomPortStatePtr mControlIn
Definition: LV2Ports.h:308
LV2PortUIStates(const LV2PortStates &states, const LV2Ports &ports)
Definition: LV2Ports.cpp:429
LV2ControlPortStateArray mControlPortStates
Definition: LV2Ports.h:310
LV2AtomPortStatePtr mControlOut
Definition: LV2Ports.h:309
std::unordered_map< TranslatableString, std::vector< int > > mGroupMap
Definition: LV2Ports.h:285
void EmitPortValues(const LilvState &state, LV2EffectSettings &settings) const
Definition: LV2Ports.cpp:411
unsigned mMidiIn
Definition: LV2Ports.h:278
std::optional< size_t > mControlInIdx
Definition: LV2Ports.h:276
const void * GetPortValue(const LV2EffectSettings &settings, const char *port_symbol, uint32_t *size, uint32_t *type) const
Definition: LV2Ports.cpp:356
TranslatableStrings mGroups
Definition: LV2Ports.h:284
unsigned mAudioOut
Definition: LV2Ports.h:273
unsigned mAudioIn
Definition: LV2Ports.h:272
LV2Ports(const LilvPlugin &plug)
Definition: LV2Ports.cpp:153
LV2CVPortArray mCVPorts
Definition: LV2Ports.h:281
LV2ControlPortArray mControlPorts
Definition: LV2Ports.h:283
LV2AtomPortArray mAtomPorts
Definition: LV2Ports.h:275
std::optional< size_t > mControlOutIdx
Definition: LV2Ports.h:277
unsigned mMidiOut
Definition: LV2Ports.h:279
void SetPortValue(LV2EffectSettings &settings, const char *port_symbol, const void *value, uint32_t size, uint32_t type) const
Definition: LV2Ports.cpp:385
std::unordered_map< uint32_t, size_t > mControlPortMap
Definition: LV2Ports.h:288
int mLatencyPort
Definition: LV2Ports.h:289
LV2AudioPortArray mAudioPorts
Definition: LV2Ports.h:271
Holds a msgid for the translation catalog; may also bind format arguments.
uint32_t zix_ring_read(ZixRing *ring, void *dst, uint32_t size)
Definition: ring.cpp:172
uint32_t zix_ring_write(ZixRing *ring, const void *src, uint32_t size)
Definition: ring.cpp:201
uint32_t zix_ring_skip(ZixRing *ring, uint32_t size)
Definition: ring.cpp:187
LilvWorld * gWorld
Definition: LV2Symbols.cpp:31
const void * get_value_func(const char *port_symbol, void *user_data, uint32_t *size, uint32_t *type)
Definition: LV2Ports.cpp:348
void set_value_func(const char *port_symbol, void *user_data, const void *value, uint32_t size, uint32_t type)
Definition: LV2Ports.cpp:376
void copy(const T *src, T *dst, int32_t n)
Definition: VectorOps.h:40
STL namespace.
void SendToInstance(LV2_Atom_Forge &forge, int64_t frameTime, float speed)
Transfer incoming events from the ring buffer to the event buffer.
Definition: LV2Ports.cpp:21
const std::unique_ptr< uint8_t[]> mBuffer
Definition: LV2Ports.h:123
const Lilv_ptr< ZixRing, zix_ring_free > mRing
Definition: LV2Ports.h:122
void SendToDialog(std::function< void(const LV2_Atom *atom, uint32_t size)> handler)
Dialog can poll one ring buffer for messages at idle time.
Definition: LV2Ports.cpp:87
void ReceiveFromDialog(const void *buffer, uint32_t buffer_size)
Dialog pushes to other ring buffer when it gets a user interface event.
Definition: LV2Ports.cpp:121
void ReceiveFromInstance()
Take responses from the instance and send cross-thread for the dialog.
Definition: LV2Ports.cpp:110
void ResetForInstanceOutput()
Definition: LV2Ports.cpp:78
const LV2AtomPortPtr mpPort
Definition: LV2Ports.h:121
Carry output control port information back to main thread.
Definition: LV2Ports.h:228
std::unique_ptr< EffectOutputs > Clone() const override
Definition: LV2Ports.cpp:139
void Assign(EffectOutputs &&src) override
Update one Outputs object from another.
Definition: LV2Ports.cpp:144
~LV2EffectOutputs() override
std::vector< float > values
vector of values in correspondence with the control ports
Definition: LV2Ports.h:233
Storage locations to be connected to LV2 control ports.
Definition: LV2Ports.h:206