Audacity 3.2.0
VSTInstance.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 VSTInstance.cpp
6
7 Dominic Mazzoni
8
9 Paul Licameli split from VSTEffect.cpp
10
11
12*//********************************************************************/
13#include "VSTInstance.h"
14
15#include <wx/time.h>
16
17std::unique_ptr<EffectInstance::Message> VSTInstance::MakeMessage() const
18{
19 // The purpose here is just to allocate vectors (chunk and paramVector)
20 // with sufficient size, not to get the values too
22 FetchSettings(settings, /* doFetch = */ false);
23
24 VSTMessage::ParamVector paramVector;
25 paramVector.resize(mAEffect->numParams, std::nullopt);
26
27 return std::make_unique<VSTMessage>( std::move(settings.mChunk), std::move(paramVector) );
28}
29
30
31std::unique_ptr<EffectInstance::Message> VSTInstance::MakeMessage(int id, double value) const
32{
33 return std::make_unique<VSTMessage>(id, value, mAEffect->numParams);
34}
35
36
38{
39 return mAudioIns;
40}
41
43{
44 return mAudioOuts;
45}
46
48{
49 // Issue 3935 for IEM plug-ins, VST 2 versions:
50 // It is mysterious why this further limitation of size works to
51 // prevent the crashes in destructive processing, or why this is not
52 // needed for non-destructive, but here it is
53 // Those plugins report many channels (like 64) but most others will not
54 // be affected by these lines with the default size of 8192
55 // Note it may make the Block Size option of the settings dialog misleading
56 auto numChannels = std::max({ 1u, GetAudioInCount(), GetAudioOutCount() });
57 maxBlockSize = std::max(size_t(1),
58 std::min(maxBlockSize, size_t(0x8000u / numChannels)));
59
61 return mBlockSize;
62}
63
65{
66 return mBlockSize;
67}
68
70 const EffectSettings& settings, double sampleRate) const -> SampleCount
71{
72 if (mUseLatency)
73 return mBufferDelay;
74 return 0;
75}
76
78{
79 return mReady;
80}
81
84{
85 // Issue 3942: Copy the contents of settings first.
86 // settings may refer to what is in the RealtimeEffectState, but that might
87 // get reassigned by EffectSettingsAccess::Set, when the validator's
88 // Automate() is called-back by the plug-in during callSetParameter.
89 // So this avoids a dangling reference.
90 auto copiedSettings = GetSettings(settings);
91 StoreSettings(copiedSettings);
92
94}
95
97{
98 // Initialize time info
99 memset(&mTimeInfo, 0, sizeof(mTimeInfo));
101 mTimeInfo.nanoSeconds = wxGetUTCTimeMillis().ToDouble();
102 mTimeInfo.tempo = 120.0;
106
107 // Set processing parameters...power must be off for this
110
111 // Turn on the power
112 PowerOn();
113
114 // Set the initial buffer delay
116
117 mReady = true;
118 return true;
119}
120
122{
123 return GuardedCall<bool>([&] {
124 mReady = false;
125
126 PowerOff();
127
128 return true;
129 });
130
131}
132
134 const float *const *inBlock, float *const *outBlock, size_t blockLen)
135{
136 // Only call the effect if there's something to do...some do not like zero-length block
137 if (blockLen)
138 {
139 // Go let the plugin moleste the samples
140 callProcessReplacing(inBlock, outBlock, blockLen);
141
142 // And track the position
143 mTimeInfo.samplePos += (double) blockLen;
144 }
145
146 return blockLen;
147}
148
150{
151 // Temporarily disconnect from any validator, so that setting the chunk
152 // does not cause Automate() callbacks (as some effects will do) that then
153 // would send slider movement messages that might destroy information in
154 // the settings.
155 auto vr = valueRestorer(mpOwningValidator, (VSTUIWrapper*)nullptr);
157}
158
160 EffectOutputs *, unsigned numChannels, float sampleRate)
161{
162 if (!mRecruited)
163 {
164 // Assign self to the first processor
165 mRecruited = true;
166 return true;
167 }
168
169 auto &effect = static_cast<const PerTrackEffect &>(mProcessor);
170 auto slave = std::make_unique<VSTInstance>(
171 const_cast<PerTrackEffect &>(effect),
173
174 slave->SetBlockSize(mBlockSize);
175
176 if (!slave->ProcessInitialize(settings, sampleRate, ChannelNames()))
177 return false;
178
179 mSlaves.emplace_back(move(slave));
180 return true;
181}
182
184{
185return GuardedCall<bool>([&]{
186
187 if (mpOwningValidator)
188 mpOwningValidator->Flush();
189
190 mRecruited = false;
191
192 for (const auto &slave : mSlaves)
193 slave->ProcessFinalize();
194 mSlaves.clear();
195
196 return ProcessFinalize();
197});
198}
199
201{
202 PowerOff();
203
204 for (const auto &slave : mSlaves)
205 slave->PowerOff();
206
207 return true;
208}
209
211{
212 PowerOn();
213
214 for (const auto &slave : mSlaves)
215 slave->PowerOn();
216
217 return true;
218}
219
221{
222 return mPresetLoadedWhilePlaying.exchange(false);
223}
224
226{
227 std::lock_guard<std::mutex> guard(mDeferredChunkMutex);
228
229 if (! mChunkToSetAtIdleTime.empty() )
230 {
232 mChunkToSetAtIdleTime.resize(0);
233 }
234}
235
236void VSTInstance::ApplyChunk(std::vector<char>& chunk)
237{
238 VstPatchChunkInfo info = {
240
241 const auto len = chunk.size();
242 const auto data = chunk.data();
243
244 callSetChunk(true, len, data, &info);
245 for (auto& slave : mSlaves)
246 slave->callSetChunk(true, len, data, &info);
247}
248
250{
251 // Some plugins (e.g. Melda) can not have their chunk set in the
252 // audio thread, resulting in making the whole app hang.
253 // This is why we defer the setting of the chunk in the main thread.
254
255 const bool IsAudioThread = (mMainThreadId != std::this_thread::get_id());
256
257 return IsAudioThread && mIsMeldaPlugin;
258}
259
260bool VSTInstance::UsesMessages() const noexcept
261{
262 return true;
263}
264
266{
267 const bool applyChunkInMainThread = ChunkMustBeAppliedInMainThread();
268
269 if (applyChunkInMainThread)
270 mDeferredChunkMutex.lock();
271
272 if (!package.pMessage)
273 return true;
274
275 auto& message = static_cast<VSTMessage&>(*package.pMessage);
276
277 auto &chunk = message.mChunk;
278
279 if (!chunk.empty())
280 {
281 if (applyChunkInMainThread)
282 {
283 // Apply the chunk later
284 //
285 mChunkToSetAtIdleTime = chunk;
286 }
287 else
288 {
289 // Apply the chunk now
290 ApplyChunk(chunk);
291 }
292
293 // Don't apply the chunk again until another message supplies a chunk
294 chunk.resize(0);
295
296 // Don't return yet. Maybe some slider movements also accumulated after
297 // the change of the chunk.
298
299 const bool IsAudioThread = (mMainThreadId != std::this_thread::get_id());
300 if (IsAudioThread)
301 {
302 // At the moment, the only reason why this method would be called in the audio thread,
303 // is because a preset was loaded while playing
304
305 mPresetLoadedWhilePlaying.store(true);
306 }
307
308 }
309
310
311 assert(message.mParamsVec.size() == mAEffect->numParams);
312
313 for (size_t paramID=0; paramID < mAEffect->numParams; paramID++)
314 {
315 if (message.mParamsVec[paramID])
316 {
317 float val = (float)(*message.mParamsVec[paramID]);
318
319 // set the change on the recruited "this" instance
320 callSetParameter(paramID, val);
321
322 // set the change on any existing slaves
323 for (auto& slave : mSlaves)
324 {
325 slave->callSetParameter(paramID, val);
326 }
327
328 // clear the used info
329 message.mParamsVec[paramID] = std::nullopt;
330 }
331 }
332
333 return true;
334}
335
337 const float *const *inbuf, float *const *outbuf, size_t numSamples)
338{
339 if (!mRecruited)
340 {
341 // unexpected!
342 return 0;
343 }
344
345 wxASSERT(numSamples <= mBlockSize);
346
347 if (group == 0)
348 {
349 // use the recruited "this" instance
350 return ProcessBlock(settings, inbuf, outbuf, numSamples);
351 }
352 else if (group <= mSlaves.size())
353 {
354 // use the slave which maps to the group
355 return mSlaves[group - 1]->ProcessBlock(settings, inbuf, outbuf, numSamples);
356 }
357 else
358 return 0;
359}
360
362{
363 if ( ChunkMustBeAppliedInMainThread() )
364 mDeferredChunkMutex.unlock();
365
366 return true;
367}
368
370{
372 {
374 }
375}
376
378{
379 if (!mHasPower)
380 {
381 // Turn the power on
382 callDispatcher(effMainsChanged, 0, 1, NULL, 0.0);
383
384 // Tell the effect we're going to start processing
385 if (mVstVersion >= 2)
386 {
387 callDispatcher(effStartProcess, 0, 0, NULL, 0.0);
388 }
389
390 // Set state
391 mHasPower = true;
392 }
393}
394
396{
397 if (mHasPower)
398 {
399 // Tell the effect we're going to stop processing
400 if (mVstVersion >= 2)
401 {
402 callDispatcher(effStopProcess, 0, 0, NULL, 0.0);
403 }
404
405 // Turn the power off
406 callDispatcher(effMainsChanged, 0, 0, NULL, 0.0);
407
408 // Set state
409 mHasPower = false;
410 }
411}
412
413void VSTInstance::SizeWindow(int w, int h)
414{
416 {
418 }
419}
420
422{
423 // We do not support negative delay
424 if (samples >= 0 && mUseLatency)
425 {
426 mBufferDelay = samples;
427 }
428
429 return;
430}
431
432void VSTInstance::callProcessReplacing(const float *const *inputs,
433 float *const *outputs, int sampleframes)
434{
436 const_cast<float**>(inputs),
437 const_cast<float**>(outputs), sampleframes);
438}
439
440void VSTInstance::Automate(int index, float value)
441{
442 if (mMainThreadId != std::this_thread::get_id())
443 return;
444
446 {
447 mpOwningValidator->Automate(index, value);
448 }
449}
450
452(
453 const PerTrackEffect& effect,
454 const PluginPath& path,
455 size_t blockSize,
456 size_t userBlockSize,
457 bool useLatency
458)
459
460 : PerTrackEffect::Instance(effect)
461 , VSTWrapper(path)
462 , mUseLatency{ useLatency }
463{
464 // what also happens in the effect ctor
465 //
466 memset(&mTimeInfo, 0, sizeof(mTimeInfo));
467 mTimeInfo.samplePos = 0.0;
468 mTimeInfo.sampleRate = 44100.0; // this is a bogus value, but it's only for the display
469 mTimeInfo.nanoSeconds = wxGetUTCTimeMillis().ToDouble();
470 mTimeInfo.tempo = 120.0;
474
475 mBlockSize = blockSize;
476 mUserBlockSize = userBlockSize;
477
478 Load();
479
480 if (!IsReady() )
481 {
482 // Set some defaults since some VSTs need them...these will be reset when
483 // normal or realtime processing begins
484 mBlockSize = 8192;
485 DoProcessInitialize(44100.0);
486 }
487
488 mIsMeldaPlugin = (mVendor == "MeldaProduction");
489}
490
492{
493 PowerOff();
494}
495
497{
499}
int min(int a, int b)
ChannelName
wxString PluginPath
type alias for identifying a Plugin supplied by a module, each module defining its own interpretation...
Definition: Identifier.h:214
ValueRestorer< T > valueRestorer(T &var)
inline functions provide convenient parameter type deduction
Definition: MemoryX.h:250
for(int ii=0, nn=names.size();ii< nn;++ii)
static Settings & settings()
Definition: TrackInfo.cpp:69
const int effMainsChanged
Definition: aeffectx.h:104
const int kVstNanosValid
Definition: aeffectx.h:148
const int effStartProcess
Definition: aeffectx.h:141
const int effSetBlockSize
Definition: aeffectx.h:103
const int kVstTransportPlaying
Definition: aeffectx.h:157
const int effStopProcess
Definition: aeffectx.h:142
const int effSetSampleRate
Definition: aeffectx.h:102
const int kVstTempoValid
Definition: aeffectx.h:150
int numParams
Definition: aeffectx.h:274
int32_t version
Definition: aeffectx.h:296
int initialDelay
Definition: aeffectx.h:284
void(* processReplacing)(AEffect *, float **, float **, int)
Definition: aeffectx.h:298
int32_t uniqueID
Definition: aeffectx.h:295
uint64_t SampleCount
Hold values to send to effect output meters.
const PerTrackEffect & mProcessor
Base class for many of the effects in Audacity.
bool mRecruited
Definition: VSTInstance.h:120
size_t mUserBlockSize
Definition: VSTInstance.h:116
VSTInstanceArray mSlaves
Definition: VSTInstance.h:112
size_t GetBlockSize() const override
Definition: VSTInstance.cpp:64
void SizeWindow(int w, int h) override
void NeedIdle() override
bool ProcessInitialize(EffectSettings &settings, double sampleRate, ChannelNames chanMap) override
Definition: VSTInstance.cpp:82
void ApplyChunk(std::vector< char > &chunk)
bool RealtimeAddProcessor(EffectSettings &settings, EffectOutputs *pOutputs, unsigned numChannels, float sampleRate) override
bool RealtimeSuspend() override
bool UsesMessages() const noexcept override
bool RealtimeProcessStart(MessagePackage &package) override
settings are possibly changed, since last call, by an asynchronous dialog
std::mutex mDeferredChunkMutex
Definition: VSTInstance.h:126
bool mHasPower
Definition: VSTInstance.h:114
bool IsReady()
Definition: VSTInstance.cpp:77
void DeferChunkApplication()
unsigned GetAudioInCount() const override
How many input buffers to allocate at once.
Definition: VSTInstance.cpp:37
unsigned GetAudioOutCount() const override
How many output buffers to allocate at once.
Definition: VSTInstance.cpp:42
void Automate(int index, float value) override
bool DoProcessInitialize(double sampleRate)
Definition: VSTInstance.cpp:96
bool RealtimeResume() override
const bool mUseLatency
Definition: VSTInstance.h:82
VSTUIWrapper * mpOwningValidator
Definition: VSTInstance.h:122
void PowerOn()
VSTInstance(const PerTrackEffect &effect, const PluginPath &path, size_t blockSize, size_t userBlockSize, bool useLatency)
bool RealtimeInitialize(EffectSettings &settings, double sampleRate) override
size_t mBlockSize
Definition: VSTInstance.h:84
bool OnePresetWasLoadedWhilePlaying()
std::vector< char > mChunkToSetAtIdleTime
Definition: VSTInstance.h:127
size_t ProcessBlock(EffectSettings &settings, const float *const *inBlock, float *const *outBlock, size_t blockLen) override
Called for destructive effect computation.
~VSTInstance() override
void SetBufferDelay(int samples) override
std::unique_ptr< Message > MakeMessage() const override
Called on the main thread, in which the result may be cloned.
Definition: VSTInstance.cpp:17
bool RealtimeFinalize(EffectSettings &settings) noexcept override
size_t SetBlockSize(size_t maxBlockSize) override
Definition: VSTInstance.cpp:47
void PowerOff()
bool RealtimeProcessEnd(EffectSettings &settings) noexcept override
settings can be updated to let a dialog change appearance at idle
size_t RealtimeProcess(size_t group, EffectSettings &settings, const float *const *inbuf, float *const *outbuf, size_t numSamples) override
void callProcessReplacing(const float *const *inputs, float *const *outputs, int sampleframes)
void SetOwningValidator(VSTUIWrapper *vi)
bool ChunkMustBeAppliedInMainThread() const
bool mIsMeldaPlugin
Definition: VSTInstance.h:133
std::atomic_bool mPresetLoadedWhilePlaying
Definition: VSTInstance.h:124
SampleCount GetLatency(const EffectSettings &settings, double sampleRate) const override
Definition: VSTInstance.cpp:69
bool ProcessFinalize() noexcept override
double tempo
Definition: aeffectx.h:317
double sampleRate
Definition: aeffectx.h:311
double nanoSeconds
Definition: aeffectx.h:313
int timeSigDenominator
Definition: aeffectx.h:327
double samplePos
Definition: aeffectx.h:309
int timeSigNumerator
Definition: aeffectx.h:325
Externalized state of a plug-in.
std::vector< std::optional< double > > ParamVector
Definition: VSTWrapper.h:300
std::vector< char > mChunk
Definition: VSTWrapper.h:324
virtual void SizeWindow(int w, int h)
Definition: VSTWrapper.cpp:643
virtual void Automate(int index, float value)
virtual void NeedIdle()
Definition: VSTWrapper.cpp:623
unsigned mAudioOuts
Definition: VSTWrapper.h:221
int mVstVersion
Definition: VSTWrapper.h:165
bool FetchSettings(VSTSettings &vst3Settings, bool doFetch=true) const
static VSTSettings & GetSettings(EffectSettings &settings)
Definition: VSTWrapper.h:103
int mBufferDelay
Definition: VSTWrapper.h:266
bool StoreSettings(const VSTSettings &vst3settings) const
AEffect * mAEffect
Definition: VSTWrapper.h:124
VstTimeInfo mTimeInfo
Definition: VSTWrapper.h:264
intptr_t callDispatcher(int opcode, int index, intptr_t value, void *ptr, float opt) override
Definition: VSTWrapper.cpp:682
void callSetParameter(int index, float value) const
Definition: VSTWrapper.cpp:704
bool Load()
Definition: VSTWrapper.cpp:307
std::thread::id mMainThreadId
Definition: VSTWrapper.h:125
unsigned mAudioIns
Definition: VSTWrapper.h:220
PluginPath mPath
Definition: VSTWrapper.h:203
wxString mVendor
Definition: VSTWrapper.h:216
void callSetChunk(bool isPgm, int len, void *buf)
Definition: VSTWrapper.cpp:721