26#if JUCE_PLUGINHOST_LADSPA && (JUCE_LINUX || JUCE_BSD)
36#define JUCE_LADSPA_LOGGING 1
38#if JUCE_LADSPA_LOGGING
39 #define JUCE_LADSPA_LOG(x) Logger::writeToLog (x);
41 #define JUCE_LADSPA_LOG(x)
72 auto*
module = getActiveModules().getUnchecked (i);
97 DynamicLibrary
module;
101 module.open (file.getFullPathName());
116class LADSPAPluginInstance
final :
public AudioPluginInstance
119 LADSPAPluginInstance (
const LADSPAModuleHandle::Ptr& m)
122 ++insideLADSPACallback;
124 name =
module->file.getFileNameWithoutExtension();
126 JUCE_LADSPA_LOG (
"Creating LADSPA instance: " + name);
128 if (module->moduleMain !=
nullptr)
130 plugin =
module->moduleMain ((size_t) shellLADSPAUIDToCreate);
132 if (plugin ==
nullptr)
134 JUCE_LADSPA_LOG (
"Cannot find any valid descriptor in shared library");
135 --insideLADSPACallback;
141 JUCE_LADSPA_LOG (
"Cannot find any valid plugin in shared library");
142 --insideLADSPACallback;
146 const auto sampleRate = getSampleRate() > 0 ? getSampleRate()
149 handle = plugin->instantiate (plugin, (uint32) sampleRate);
151 --insideLADSPACallback;
154 ~LADSPAPluginInstance()
override
158 jassert (insideLADSPACallback == 0);
160 if (handle !=
nullptr && plugin !=
nullptr)
161 NullCheckedInvocation::invoke (plugin->cleanup, handle);
169 void initialise (
double initialSampleRate,
int initialBlockSize)
171 setPlayConfigDetails (inputs.size(), outputs.size(), initialSampleRate, initialBlockSize);
173 if (initialised || plugin ==
nullptr || handle ==
nullptr)
176 JUCE_LADSPA_LOG (
"Initialising LADSPA: " + name);
182 AudioProcessorParameterGroup newTree;
184 for (
unsigned int i = 0; i < plugin->PortCount; ++i)
186 const auto portDesc = plugin->PortDescriptors[i];
188 if ((portDesc & LADSPA_PORT_CONTROL) != 0)
189 newTree.addChild (std::make_unique<LADSPAParameter> (*
this, (
int) i,
190 String (plugin->PortNames[i]).trim(),
191 (portDesc & LADSPA_PORT_INPUT) != 0));
193 if ((portDesc & LADSPA_PORT_AUDIO) != 0)
195 if ((portDesc & LADSPA_PORT_INPUT) != 0) inputs.add ((
int) i);
196 if ((portDesc & LADSPA_PORT_OUTPUT) != 0) outputs.add ((
int) i);
200 setHostedParameterTree (std::move (newTree));
202 for (
auto* param : getParameters())
203 if (auto* ladspaParam = dynamic_cast<LADSPAParameter*> (param))
204 plugin->connect_port (handle, (
size_t) ladspaParam->paramID, &(ladspaParam->paramValue.scaled));
206 setPlayConfigDetails (inputs.size(), outputs.size(), initialSampleRate, initialBlockSize);
208 setCurrentProgram (0);
209 setLatencySamples (0);
212 NullCheckedInvocation::invoke (plugin->activate, handle);
213 NullCheckedInvocation::invoke (plugin->deactivate, handle);
219 void fillInPluginDescription (PluginDescription& desc)
const override
221 desc.name = getName();
222 desc.fileOrIdentifier =
module->file.getFullPathName();
223 desc.uniqueId = desc.deprecatedUid = getUID();
224 desc.lastFileModTime =
module->file.getLastModificationTime();
225 desc.lastInfoUpdateTime = Time::getCurrentTime();
226 desc.pluginFormatName =
"LADSPA";
227 desc.category = getCategory();
228 desc.manufacturerName = plugin !=
nullptr ? String (plugin->Maker) : String();
229 desc.version = getVersion();
230 desc.numInputChannels = getTotalNumInputChannels();
231 desc.numOutputChannels = getTotalNumOutputChannels();
232 desc.isInstrument =
false;
235 const String getName()
const override
237 if (plugin !=
nullptr && plugin->Label !=
nullptr)
238 return plugin->Label;
245 if (plugin !=
nullptr && plugin->UniqueID != 0)
246 return (
int) plugin->UniqueID;
248 return module->file.hashCode();
251 String getVersion()
const {
return LADSPA_VERSION; }
252 String getCategory()
const {
return "Effect"; }
254 bool acceptsMidi()
const override {
return false; }
255 bool producesMidi()
const override {
return false; }
257 double getTailLengthSeconds()
const override {
return 0.0; }
260 void prepareToPlay (
double newSampleRate,
int samplesPerBlockExpected)
override
262 setLatencySamples (0);
264 initialise (newSampleRate, samplesPerBlockExpected);
268 tempBuffer.setSize (jmax (1, outputs.size()), samplesPerBlockExpected);
271 if (
auto* firstParam = getParameters()[0])
273 const auto old = firstParam->getValue();
274 firstParam->setValue ((old < 0.5f) ? 1.0f : 0.0f);
275 firstParam->setValue (old);
278 NullCheckedInvocation::invoke (plugin->activate, handle);
282 void releaseResources()
override
284 if (handle !=
nullptr && plugin->deactivate !=
nullptr)
285 NullCheckedInvocation::invoke (plugin->deactivate, handle);
287 tempBuffer.setSize (1, 1);
290 void processBlock (AudioBuffer<float>& buffer, MidiBuffer&)
override
292 auto numSamples = buffer.getNumSamples();
294 if (initialised && plugin !=
nullptr && handle !=
nullptr)
296 for (
int i = 0; i < inputs.size(); ++i)
297 plugin->connect_port (handle, (
size_t) inputs[i],
298 i < buffer.getNumChannels() ? buffer.getWritePointer (i) : nullptr);
300 if (plugin->run !=
nullptr)
302 for (
int i = 0; i < outputs.size(); ++i)
303 plugin->connect_port (handle, (
size_t) outputs.getUnchecked (i),
304 i < buffer.getNumChannels() ? buffer.getWritePointer (i) : nullptr);
306 plugin->run (handle, (
size_t) numSamples);
310 if (plugin->run_adding !=
nullptr)
312 tempBuffer.setSize (outputs.size(), numSamples);
315 for (
int i = 0; i < outputs.size(); ++i)
316 plugin->connect_port (handle, (
size_t) outputs.getUnchecked (i), tempBuffer.getWritePointer (i));
318 plugin->run_adding (handle, (
size_t) numSamples);
320 for (
int i = 0; i < outputs.size(); ++i)
321 if (i < buffer.getNumChannels())
322 buffer.copyFrom (i, 0, tempBuffer, i, 0, numSamples);
330 for (
auto i = getTotalNumInputChannels(), e = getTotalNumOutputChannels(); i < e; ++i)
331 buffer.clear (i, 0, numSamples);
334 using AudioPluginInstance::processBlock;
336 bool isInputChannelStereoPair (
int index)
const override {
return isPositiveAndBelow (index, getTotalNumInputChannels()); }
337 bool isOutputChannelStereoPair (
int index)
const override {
return isPositiveAndBelow (index, getTotalNumOutputChannels()); }
339 const String getInputChannelName (
const int index)
const override
341 if (isPositiveAndBelow (index, getTotalNumInputChannels()))
342 return String (plugin->PortNames [inputs [index]]).trim();
347 const String getOutputChannelName (
const int index)
const override
349 if (isPositiveAndBelow (index, getTotalNumInputChannels()))
350 return String (plugin->PortNames [outputs [index]]).trim();
356 int getNumPrograms()
override {
return 0; }
357 int getCurrentProgram()
override {
return 0; }
359 void setCurrentProgram (
int)
override
361 for (
auto* param : getParameters())
362 if (auto* ladspaParam = dynamic_cast<LADSPAParameter*> (param))
363 ladspaParam->reset();
366 const String getProgramName (
int)
override {
return {}; }
367 void changeProgramName (
int,
const String&)
override {}
370 void getStateInformation (MemoryBlock& destData)
override
372 auto numParameters = getParameters().size();
373 destData.setSize ((
size_t) numParameters *
sizeof (
float));
374 destData.fillWith (0);
376 auto* p = unalignedPointerCast<float*> (destData.getData());
378 for (
int i = 0; i < numParameters; ++i)
379 if (
auto* param = getParameters()[i])
380 p[i] = param->getValue();
383 void getCurrentProgramStateInformation (MemoryBlock& destData)
override { getStateInformation (destData); }
384 void setCurrentProgramStateInformation (
const void* data,
int sizeInBytes)
override { setStateInformation (data, sizeInBytes); }
386 void setStateInformation (
const void* data, [[maybe_unused]]
int sizeInBytes)
override
388 auto* p =
static_cast<const float*
> (
data);
390 for (
int i = 0; i < getParameters().size(); ++i)
391 if (
auto* param = getParameters()[i])
392 param->setValue (p[i]);
395 bool hasEditor()
const override {
return false; }
396 AudioProcessorEditor* createEditor()
override {
return nullptr; }
398 bool isValid()
const {
return handle !=
nullptr; }
401 LADSPAModuleHandle::Ptr
module;
402 const LADSPA_Descriptor* plugin =
nullptr;
406 struct LADSPAParameter final :
public Parameter
408 struct ParameterValue
410 inline ParameterValue() noexcept {}
411 inline ParameterValue (
float s,
float u) noexcept : scaled (s), unscaled (u) {}
413 float scaled = 0, unscaled = 0;
416 LADSPAParameter (LADSPAPluginInstance& parent,
int parameterID,
417 const String& parameterName,
bool parameterIsAutomatable)
418 : pluginInstance (parent),
419 paramID (parameterID),
420 name (parameterName),
421 automatable (parameterIsAutomatable)
426 float getValue()
const override
428 if (pluginInstance.plugin !=
nullptr)
432 return paramValue.unscaled;
438 String getCurrentValueAsText()
const override
440 if (
auto* interface = pluginInstance.plugin)
442 const auto& hint = interface->PortRangeHints[paramID];
444 if (LADSPA_IS_HINT_INTEGER (hint.HintDescriptor))
445 return String ((
int) paramValue.scaled);
447 return String (paramValue.scaled, 4);
453 void setValue (
float newValue)
override
455 if (
auto* interface = pluginInstance.plugin)
459 if (! approximatelyEqual (paramValue.unscaled, newValue))
460 paramValue = ParameterValue (getNewParamScaled (interface->PortRangeHints [paramID], newValue), newValue);
464 float getDefaultValue()
const override
469 ParameterValue getDefaultParamValue()
const
471 if (
auto* interface = pluginInstance.plugin)
473 const auto& hint = interface->PortRangeHints[paramID];
474 const auto& desc = hint.HintDescriptor;
476 if (LADSPA_IS_HINT_HAS_DEFAULT (desc))
478 if (LADSPA_IS_HINT_DEFAULT_0 (desc))
return {};
479 if (LADSPA_IS_HINT_DEFAULT_1 (desc))
return { 1.0f, 1.0f };
480 if (LADSPA_IS_HINT_DEFAULT_100 (desc))
return { 100.0f, 0.5f };
481 if (LADSPA_IS_HINT_DEFAULT_440 (desc))
return { 440.0f, 0.5f };
483 const auto scale = LADSPA_IS_HINT_SAMPLE_RATE (desc) ? (
float) pluginInstance.getSampleRate()
485 const auto lower = hint.LowerBound * scale;
486 const auto upper = hint.UpperBound * scale;
488 if (LADSPA_IS_HINT_BOUNDED_BELOW (desc) && LADSPA_IS_HINT_DEFAULT_MINIMUM (desc))
return { lower, 0.0f };
489 if (LADSPA_IS_HINT_BOUNDED_ABOVE (desc) && LADSPA_IS_HINT_DEFAULT_MAXIMUM (desc))
return { upper, 1.0f };
491 if (LADSPA_IS_HINT_BOUNDED_BELOW (desc))
493 auto useLog = LADSPA_IS_HINT_LOGARITHMIC (desc);
495 if (LADSPA_IS_HINT_DEFAULT_LOW (desc))
return { scaledValue (lower, upper, 0.25f, useLog), 0.25f };
496 if (LADSPA_IS_HINT_DEFAULT_MIDDLE (desc))
return { scaledValue (lower, upper, 0.50f, useLog), 0.50f };
497 if (LADSPA_IS_HINT_DEFAULT_HIGH (desc))
return { scaledValue (lower, upper, 0.75f, useLog), 0.75f };
507 paramValue = getDefaultParamValue();
508 defaultValue = paramValue.unscaled;
511 String getName (
int )
const override {
return name; }
512 String getLabel()
const override {
return {}; }
514 bool isAutomatable()
const override {
return automatable; }
516 String getParameterID()
const override
518 return String (paramID);
521 static float scaledValue (
float low,
float high,
float alpha,
bool useLog)
noexcept
523 if (useLog && low > 0 && high > 0)
524 return expf (logf (low) * (1.0f - alpha) + logf (high) * alpha);
526 return low + (high - low) * alpha;
529 static float toIntIfNecessary (
const LADSPA_PortRangeHintDescriptor& desc,
float value)
531 return LADSPA_IS_HINT_INTEGER (desc) ? ((
float) (
int) value) : value;
534 float getNewParamScaled (
const LADSPA_PortRangeHint& hint,
float newValue)
const
536 const auto& desc = hint.HintDescriptor;
538 if (LADSPA_IS_HINT_TOGGLED (desc))
539 return (newValue < 0.5f) ? 0.0f : 1.0f;
541 const auto scale = LADSPA_IS_HINT_SAMPLE_RATE (desc) ? (
float) pluginInstance.getSampleRate()
543 const auto lower = hint.LowerBound * scale;
544 const auto upper = hint.UpperBound * scale;
546 if (LADSPA_IS_HINT_BOUNDED_BELOW (desc) && LADSPA_IS_HINT_BOUNDED_ABOVE (desc))
547 return toIntIfNecessary (desc, scaledValue (lower, upper, newValue, LADSPA_IS_HINT_LOGARITHMIC (desc)));
549 if (LADSPA_IS_HINT_BOUNDED_BELOW (desc))
return toIntIfNecessary (desc, newValue);
550 if (LADSPA_IS_HINT_BOUNDED_ABOVE (desc))
return toIntIfNecessary (desc, newValue * upper);
555 LADSPAPluginInstance& pluginInstance;
558 const bool automatable;
560 ParameterValue paramValue;
561 float defaultValue = 0.0f;
565 LADSPA_Handle handle =
nullptr;
567 CriticalSection
lock;
568 bool initialised =
false;
569 AudioBuffer<float> tempBuffer { 1, 1 };
570 Array<int> inputs, outputs;
578LADSPAPluginFormat::LADSPAPluginFormat() {}
579LADSPAPluginFormat::~LADSPAPluginFormat() {}
586 PluginDescription desc;
587 desc.fileOrIdentifier = fileOrIdentifier;
588 desc.uniqueId = desc.deprecatedUid = 0;
591 auto instance =
dynamic_cast<LADSPAPluginInstance*
> (
createdInstance.get());
593 if (instance ==
nullptr || ! instance->isValid())
596 instance->initialise (44100.0, 512);
597 instance->fillInPluginDescription (desc);
599 if (instance->module->moduleMain !=
nullptr)
601 for (
int uid = 0;; ++uid)
603 if (
auto*
plugin = instance->module->moduleMain ((
size_t) uid))
605 desc.uniqueId = desc.deprecatedUid = uid;
606 desc.name =
plugin->Name !=
nullptr ?
plugin->Name :
"Unknown";
609 results.add (
new PluginDescription (desc));
619void LADSPAPluginFormat::createPluginInstance (
const PluginDescription& desc,
620 double sampleRate,
int blockSize,
621 PluginCreationCallback callback)
627 auto file = File (desc.fileOrIdentifier);
630 file.getParentDirectory().setAsCurrentWorkingDirectory();
632 const LADSPAModuleHandle::Ptr
module (LADSPAModuleHandle::findOrCreateModule (file));
640 if (result->plugin !=
nullptr && result->isValid())
641 result->initialise (sampleRate, blockSize);
651 if (result ==
nullptr)
652 errorMsg =
TRANS (
"Unable to load XXX plug-in file").replace (
"XXX",
"LADSPA");
654 callback (std::move (result),
errorMsg);
657bool LADSPAPluginFormat::requiresUnblockedMessageThreadDuringCreation (
const PluginDescription&)
const
665 return f.existsAsFile() && f.hasFileExtension (
".so");
670 return fileOrIdentifier;
675 return File (desc.fileOrIdentifier).getLastModificationTime() != desc.lastFileModTime;
693void LADSPAPluginFormat::recursiveFileSearch (StringArray& results,
const File& dir,
const bool recursive)
696 for (
const auto& iter : RangedDirectoryIterator (dir,
false,
"*", File::findFilesAndDirectories))
698 auto f = iter.getFile();
704 results.
add (f.getFullPathName());
708 recursiveFileSearch (results, f,
true);
static File getCurrentWorkingDirectory()
Returns the current working directory.
static File createFileWithoutCheckingPath(const String &absolutePath) noexcept
Creates a file that simply contains this string, without doing the sanity-checking that the normal co...
bool exists() const
Checks whether the file actually exists.
void add(String stringToAdd)
Appends a string at the end of the array.
String replace(StringRef stringToReplace, StringRef stringToInsertInstead, bool ignoreCase=false) const
Replaces all occurrences of a substring with another string.
static String getEnvironmentVariable(const String &name, const String &defaultValue)
Returns an environment variable.
#define TRANS(stringLiteral)
Uses the LocalisedStrings class to translate the given string literal.
CriticalSection::ScopedLockType ScopedLock
Automatically locks and unlocks a CriticalSection object.
Type unalignedPointerCast(void *ptr) noexcept
Casts a pointer to another type via void*, which suppresses the cast-align warning which sometimes ar...
bool isPositiveAndBelow(Type1 valueToTest, Type2 upperLimit) noexcept
Returns true if a value is at least zero, and also below a specified upper limit.