Audacity 3.2.0
LV2Wrapper.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 @file LV2Wrapper.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#if defined(USE_LV2)
15
16#if defined(__GNUC__)
17#pragma GCC diagnostic ignored "-Wparentheses"
18#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
19#elif defined(__clang__)
20#pragma clang diagnostic ignored "-Wparentheses"
21#pragma clang diagnostic ignored "-Wdeprecated-declarations"
22#endif
23
24#include "LV2Wrapper.h"
25#include "LV2FeaturesList.h"
26#include "LV2Ports.h"
27
28#if defined(__WXMSW__)
29#include <wx/msw/wrapwin.h>
30#endif
31
32std::unique_ptr<LV2Wrapper> LV2Wrapper::Create(
33 LV2InstanceFeaturesList &baseFeatures,
34 const LV2Ports &ports, LV2PortStates &portStates,
36 EffectOutputs *pOutputs)
37{
38 auto &plug = baseFeatures.mPlug;
39
40 std::unique_ptr<LV2Wrapper> wrapper;
41 try {
42 wrapper = std::make_unique<LV2Wrapper>(CreateToken{},
43 baseFeatures, plug, sampleRate);
44 } catch(const std::exception&) {
45 return nullptr;
46 }
47
48 const auto instance = &wrapper->GetInstance();
49 wrapper->SendBlockSize();
50 wrapper->ConnectPorts(ports, portStates, settings, pOutputs);
51
52 // Give plugin a chance to initialize. The SWH plugins (like AllPass) need
53 // this before it can be safely deleted.
54 lilv_instance_activate(instance);
55 lilv_instance_deactivate(instance);
56
57 // Send to the dialog whatever was the result of that "pulse"
58 for (auto & state : portStates.mAtomPortStates)
59 state->ReceiveFromInstance();
60
61 return wrapper;
62}
63
65 const LV2Ports &ports, const LV2EffectSettings &settings,
66 EffectOutputs *pOutputs)
67{
68 const auto instance = &GetInstance();
69 static float blackHole;
70 auto pValues = static_cast<LV2EffectOutputs *>(pOutputs);
71
72 // Connect all control ports
73 const auto latencyPort = ports.mLatencyPort;
74 if (latencyPort >= 0)
75 lilv_instance_connect_port(instance, latencyPort, &mLatency);
76
77 auto &values = settings.values;
78 size_t index = 0;
79 for (auto & port : ports.mControlPorts) {
80 void *const location = port->mIsInput
81 // Settings slots for input ports must pass to the library
82 // as nominal pointers to non-const
83 ? &const_cast<float&>(values[index])
84 : pValues ? &pValues->values[index]
85 : &blackHole
86 ;
87 lilv_instance_connect_port(instance, port->mIndex, location);
88 ++index;
89 }
90}
91
92void LV2Wrapper::ConnectPorts(const LV2Ports &ports, LV2PortStates &portStates,
94{
95 ConnectControlPorts(ports, settings, pOutputs);
96
97 const auto instance = &GetInstance();
98
99 // Connect all atom ports
100 for (auto & state : portStates.mAtomPortStates)
101 lilv_instance_connect_port(instance,
102 state->mpPort->mIndex, state->mBuffer.get());
103
104 // We don't fully support CV ports, so connect them to dummy buffers for now.
105 for (auto & state : portStates.mCVPortStates)
106 lilv_instance_connect_port(instance, state.mpPort->mIndex,
107 state.mBuffer.get());
108}
109
111{
112 if (mInstance) {
113 if (mThread.joinable()) {
114 // Even if we have been freewheeling, this unblocks the unused thread
115 // so it can be joined
116 mStopWorker = true;
117 mRequests.Post({ 0, NULL }); // Must do after writing mStopWorker
118 mThread.join();
119 }
120 Deactivate();
121 }
122}
123
125 const LilvPlugin &plugin, float sampleRate
126) : mFeaturesList{ baseFeatures, sampleRate, &mWorkerSchedule }
127, mInstance{ [&instanceFeaturesList = mFeaturesList, &plugin, sampleRate](){
128 auto features = instanceFeaturesList.GetFeaturePointers();
129
130#if defined(__WXMSW__)
131 // Plugins may have dependencies that need to be loaded from the same path
132 // as the main DLL, so add this plugin's path to the DLL search order.
133 const auto libNode = lilv_plugin_get_library_uri(&plugin);
134 const auto libUri = lilv_node_as_uri(libNode);
135 LilvCharsPtr libPath{ lilv_file_uri_parse(libUri, nullptr) };
136 const auto path = wxPathOnly(libPath.get());
137 SetDllDirectory(path.c_str());
138 auto cleanup = finally([]{ SetDllDirectory(nullptr); });
139#endif
140
141 auto result = lilv_plugin_instantiate(&plugin, sampleRate, features.data());
142 return result ? result : throw std::exception{};
143}()}
144, mHandle{ lilv_instance_get_handle(mInstance.get()) }
145, mOptionsInterface{ static_cast<const LV2_Options_Interface *>(
146 lilv_instance_get_extension_data(mInstance.get(), LV2_OPTIONS__interface))
147}
148, mStateInterface{ static_cast<const LV2_State_Interface *>(
149 lilv_instance_get_extension_data(mInstance.get(), LV2_STATE__interface))
150}
151, mWorkerInterface{ static_cast<const LV2_Worker_Interface *>(
152 lilv_instance_get_extension_data(mInstance.get(), LV2_WORKER__interface))
153}
154{
155 if (mWorkerInterface)
156 mThread = std::thread{
157 std::mem_fn( &LV2Wrapper::ThreadFunction ), std::ref(*this)
158 };
159}
160
162{
163 if (!mActivated) {
164 lilv_instance_activate(&GetInstance());
165 mActivated = true;
166 }
167}
168
170{
171 if (mActivated) {
172 lilv_instance_deactivate(&GetInstance());
173 mActivated = false;
174 }
175}
176
177LilvInstance &LV2Wrapper::GetInstance() const
178{
179 return *mInstance;
180}
181
182LV2_Handle LV2Wrapper::GetHandle() const
183{
184 return mHandle;
185}
186
188{
189 return mLatency;
190}
191
192// Where is this called?
194{
195 mFreeWheeling = enable;
196}
197
199{
200 if (auto pOption = mFeaturesList.Base().NominalBlockLengthOption()
201 ; pOption && mOptionsInterface && mOptionsInterface->set
202 ){
203 LV2_Options_Option options[2]{ *pOption, {} };
204 // I assume the pointer to temporary options is not retained by
205 // well written plug-ins, but they may watch the location at the pointer
206 // that is in the option structure.
207 mOptionsInterface->set(mHandle, options);
208 }
209}
210
211// Thread body
213{
214 for (LV2Work work{};
215 // Must test mStopWorker only after reading mRequests
216 mRequests.Receive(work) == wxMSGQUEUE_NO_ERROR && !mStopWorker;
217 )
218 // Call foreign instance code in this thread, which is neither the
219 // main nor the audio thread
220 mWorkerInterface->work(mHandle, respond, this, work.size, work.data);
221}
222
224{
225 if (mWorkerInterface) {
226 LV2Work work{};
227 while (mResponses.ReceiveTimeout(0, work) == wxMSGQUEUE_NO_ERROR)
228 // Invoke foreign instance code in main (destructive) or
229 // audio thread (real-time) processing
230 mWorkerInterface->work_response(mHandle, work.size, work.data);
231 if (mWorkerInterface->end_run)
232 // More foreign code
233 mWorkerInterface->end_run(mHandle);
234 }
235}
236
237// static callback
238LV2_Worker_Status LV2Wrapper::schedule_work(LV2_Worker_Schedule_Handle handle,
239 uint32_t size, const void *data)
240{
241 return static_cast<LV2Wrapper *>(handle)->ScheduleWork(size, data);
242}
243
244LV2_Worker_Status LV2Wrapper::ScheduleWork(uint32_t size, const void *data)
245{
246 if (mFreeWheeling)
247 // Not using another thread
248 return mWorkerInterface->work(mHandle, respond, this, size, data);
249 else {
250 // Put in the queue for the worker thread
251 // which will then do mWorkerInterface->work
252 const auto err = mRequests.Post({ size, data });
253 return (err == wxMSGQUEUE_NO_ERROR)
254 ? LV2_WORKER_SUCCESS : LV2_WORKER_ERR_UNKNOWN;
255 }
256}
257
258// static callback given to mWorkerInterface->work and
259// called back by the foreign instance code
260LV2_Worker_Status LV2Wrapper::respond(
261 LV2_Worker_Respond_Handle handle, uint32_t size, const void *data)
262{
263 return static_cast<LV2Wrapper*>(handle)->Respond(size, data);
264}
265
266LV2_Worker_Status LV2Wrapper::Respond(uint32_t size, const void *data)
267{
268 // Put in the queue, for another thread -- when not "freewheeling."
269 // Otherwise it is just roundabout communication within a thread
270 const auto err = mResponses.Post({ size, data });
271 return (err == wxMSGQUEUE_NO_ERROR)
272 ? LV2_WORKER_SUCCESS : LV2_WORKER_ERR_UNKNOWN;
273}
274
275#endif
const wxChar * values
Lilv_ptr< char, free_chars > LilvCharsPtr
Definition: LV2Utils.h:29
static Settings & settings()
Definition: TrackInfo.cpp:47
Hold values to send to effect output meters.
const LilvPlugin & mPlug
LV2CVPortStateArray mCVPortStates
Definition: LV2Ports.h:299
LV2AtomPortStateArray mAtomPortStates
Definition: LV2Ports.h:298
LV2ControlPortArray mControlPorts
Definition: LV2Ports.h:283
int mLatencyPort
Definition: LV2Ports.h:289
Manager of a handle to an LV2 plug-in instantiation.
Definition: LV2Wrapper.h:45
std::thread mThread
Definition: LV2Wrapper.h:116
static LV2_Worker_Status respond(LV2_Worker_Respond_Handle handle, uint32_t size, const void *data)
Definition: LV2Wrapper.cpp:260
void Activate()
Definition: LV2Wrapper.cpp:161
const LV2_Handle mHandle
Definition: LV2Wrapper.h:105
static std::unique_ptr< LV2Wrapper > Create(LV2InstanceFeaturesList &baseFeatures, const LV2Ports &ports, LV2PortStates &portStates, const LV2EffectSettings &settings, float sampleRate, EffectOutputs *pOutputs)
Factory.
Definition: LV2Wrapper.cpp:32
const LilvInstancePtr mInstance
Definition: LV2Wrapper.h:104
LV2_Worker_Status ScheduleWork(uint32_t size, const void *data)
Definition: LV2Wrapper.cpp:244
void ConsumeResponses()
Definition: LV2Wrapper.cpp:223
void SendBlockSize()
Definition: LV2Wrapper.cpp:198
LV2_Worker_Status Respond(uint32_t size, const void *data)
Definition: LV2Wrapper.cpp:266
wxMessageQueue< LV2Work > mRequests
Definition: LV2Wrapper.h:117
void Deactivate()
Definition: LV2Wrapper.cpp:169
bool mStopWorker
Definition: LV2Wrapper.h:126
void ConnectPorts(const LV2Ports &ports, LV2PortStates &portStates, const LV2EffectSettings &settings, EffectOutputs *pOutputs)
Definition: LV2Wrapper.cpp:92
~LV2Wrapper()
If a thread was started, joins it.
Definition: LV2Wrapper.cpp:110
void ThreadFunction()
Definition: LV2Wrapper.cpp:212
const LV2_Options_Interface *const mOptionsInterface
Definition: LV2Wrapper.h:110
LV2_Handle GetHandle() const
Definition: LV2Wrapper.cpp:182
bool mActivated
Definition: LV2Wrapper.h:127
void SetFreeWheeling(bool enable)
Definition: LV2Wrapper.cpp:193
static LV2_Worker_Status schedule_work(LV2_Worker_Schedule_Handle handle, uint32_t size, const void *data)
Definition: LV2Wrapper.cpp:238
float mLatency
Definition: LV2Wrapper.h:119
LV2WrapperFeaturesList mFeaturesList
Definition: LV2Wrapper.h:101
void ConnectControlPorts(const LV2Ports &ports, const LV2EffectSettings &settings, EffectOutputs *pOutputs)
Definition: LV2Wrapper.cpp:64
const LV2_Worker_Interface *const mWorkerInterface
Definition: LV2Wrapper.h:114
LV2Wrapper(CreateToken &&, LV2InstanceFeaturesList &baseFeatures, const LilvPlugin &plugin, float sampleRate)
Constructor may spawn a thread.
Definition: LV2Wrapper.cpp:124
bool mFreeWheeling
If true, do not spawn extra worker threads.
Definition: LV2Wrapper.h:122
float GetLatency() const
Definition: LV2Wrapper.cpp:187
wxMessageQueue< LV2Work > mResponses
Definition: LV2Wrapper.h:118
LilvInstance & GetInstance() const
Definition: LV2Wrapper.cpp:177
Carry output control port information back to main thread.
Definition: LV2Ports.h:228
Storage locations to be connected to LV2 control ports.
Definition: LV2Ports.h:206
const LV2_Options_Option * NominalBlockLengthOption() const
To compel use of the factory.
Definition: LV2Wrapper.h:47
const LV2InstanceFeaturesList & Base() const