text:
/*
  ==============================================================================

   This file is part of the JUCE library.
   Copyright (c) 2017 - ROLI Ltd.

   JUCE is an open source library subject to commercial or open-source
   licensing.

   By using JUCE, you agree to the terms of both the JUCE 5 End-User License
   Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
   27th April 2017).

   End User License Agreement: www.juce.com/juce-5-licence
   Privacy Policy: www.juce.com/juce-5-privacy-policy

   Or: You may also use this code under the terms of the GPL v3 (see
   www.gnu.org/licenses).

   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
   DISCLAIMED.

  ==============================================================================
*/

#if (JUCE_PLUGINHOST_VST || DOXYGEN)

namespace juce
{

//==============================================================================
/**
    Implements a plugin format manager for VSTs.
*/
class JUCE_API  VSTPluginFormat   : public AudioPluginFormat
{
public:
    //==============================================================================
    VSTPluginFormat();
    ~VSTPluginFormat();

    //==============================================================================
    /** Attempts to retrieve the VSTXML data from a plugin.
        Will return nullptr if the plugin isn't a VST, or if it doesn't have any VSTXML.
    */
    static const XmlElement* getVSTXML (AudioPluginInstance* plugin);

    /** Attempts to reload a VST plugin's state from some FXB or FXP data. */
    static bool loadFromFXBFile (AudioPluginInstance* plugin, const void* data, size_t dataSize);

    /** Attempts to save a VST's state to some FXP or FXB data. */
    static bool saveToFXBFile (AudioPluginInstance* plugin, MemoryBlock& result, bool asFXB);

    /** Attempts to get a VST's state as a chunk of memory. */
    static bool getChunkData (AudioPluginInstance* plugin, MemoryBlock& result, bool isPreset);

    /** Attempts to set a VST's state from a chunk of memory. */
    static bool setChunkData (AudioPluginInstance* plugin, const void* data, int size, bool isPreset);

    /** Given a suitable function pointer to a VSTPluginMain function, this will attempt to
        instantiate and return a plugin for it.
    */
    static AudioPluginInstance* createCustomVSTFromMainCall (void* entryPointFunction,
                                                             double initialSampleRate,
                                                             int initialBufferSize);

    //==============================================================================
    /** Base class for some extra functions that can be attached to a VST plugin instance. */
    class ExtraFunctions
    {
    public:
        virtual ~ExtraFunctions() {}

        /** This should return 10000 * the BPM at this position in the current edit. */
        virtual int64 getTempoAt (int64 samplePos) = 0;

        /** This should return the host's automation state.
            @returns 0 = not supported, 1 = off, 2 = read, 3 = write, 4 = read/write
        */
        virtual int getAutomationState() = 0;
    };

    /** Provides an ExtraFunctions callback object for a plugin to use.
        The plugin will take ownership of the object and will delete it automatically.
    */
    static void setExtraFunctions (AudioPluginInstance* plugin, ExtraFunctions* functions);

    //==============================================================================
    /** This simply calls directly to the VST's AEffect::dispatcher() function. */
    static pointer_sized_int JUCE_CALLTYPE dispatcher (AudioPluginInstance*, int32, int32, pointer_sized_int, void*, float);

    /** Given a VstEffectInterface* (aka vst::AEffect*), this will return the juce AudioPluginInstance
        that is being used to wrap it
    */
    static AudioPluginInstance* getPluginInstanceFromVstEffectInterface (void* aEffect);

    //==============================================================================
    String getName() const override                { return "VST"; }
    void findAllTypesForFile (OwnedArray<PluginDescription>&, const String& fileOrIdentifier) override;
    bool fileMightContainThisPluginType (const String& fileOrIdentifier) override;
    String getNameOfPluginFromIdentifier (const String& fileOrIdentifier) override;
    bool pluginNeedsRescanning (const PluginDescription&) override;
    StringArray searchPathsForPlugins (const FileSearchPath&, bool recursive, bool) override;
    bool doesPluginStillExist (const PluginDescription&) override;
    FileSearchPath getDefaultLocationsToSearch() override;
    bool canScanForPlugins() const override        { return true; }

    /** Can be overridden to receive a callback when each member of a shell plugin is about to be
        tested during a call to findAllTypesForFile().
        Only the name and uid members of the PluginDescription are guaranteed to be valid when
        this is called.
    */
    virtual void aboutToScanVSTShellPlugin (const PluginDescription&);

private:
    //==============================================================================
    void createPluginInstance (const PluginDescription&, double initialSampleRate,
                               int initialBufferSize, void* userData,
                               void (*callback) (void*, AudioPluginInstance*, const String&)) override;

    bool requiresUnblockedMessageThreadDuringCreation (const PluginDescription&) const noexcept override;

private:
    void recursiveFileSearch (StringArray&, const File&, bool recursive);

    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (VSTPluginFormat)
};

} // namespace juce

#endif

repo_name: fedden/RenderMan

mime_type: text/x-c++


text:
/*
  ==============================================================================

   This file is part of the JUCE library.
   Copyright (c) 2017 - ROLI Ltd.

   JUCE is an open source library subject to commercial or open-source
   licensing.

   By using JUCE, you agree to the terms of both the JUCE 5 End-User License
   Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
   27th April 2017).

   End User License Agreement: www.juce.com/juce-5-licence
   Privacy Policy: www.juce.com/juce-5-privacy-policy

   Or: You may also use this code under the terms of the GPL v3 (see
   www.gnu.org/licenses).

   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
   DISCLAIMED.

  ==============================================================================
*/

#if JUCE_PLUGINHOST_LADSPA && JUCE_LINUX

#include <ladspa.h>

namespace juce
{

static int shellLADSPAUIDToCreate = 0;
static int insideLADSPACallback = 0;

#define JUCE_LADSPA_LOGGING 1

#if JUCE_LADSPA_LOGGING
 #define JUCE_LADSPA_LOG(x) Logger::writeToLog (x);
#else
 #define JUCE_LADSPA_LOG(x)
#endif

//==============================================================================
class LADSPAModuleHandle    : public ReferenceCountedObject
{
public:
    LADSPAModuleHandle (const File& f)
        : file (f), moduleMain (nullptr)
    {
        getActiveModules().add (this);
    }

    ~LADSPAModuleHandle()
    {
        getActiveModules().removeFirstMatchingValue (this);
        close();
    }

    typedef ReferenceCountedObjectPtr<LADSPAModuleHandle> Ptr;

    static Array<LADSPAModuleHandle*>& getActiveModules()
    {
        static Array<LADSPAModuleHandle*> activeModules;
        return activeModules;
    }

    static LADSPAModuleHandle* findOrCreateModule (const File& file)
    {
        for (int i = getActiveModules().size(); --i >= 0;)
        {
            LADSPAModuleHandle* const module = getActiveModules().getUnchecked(i);

            if (module->file == file)
                return module;
        }

        ++insideLADSPACallback;
        shellLADSPAUIDToCreate = 0;

        JUCE_LADSPA_LOG ("Loading LADSPA module: " + file.getFullPathName());

        ScopedPointer<LADSPAModuleHandle> m (new LADSPAModuleHandle (file));

        if (! m->open())
            m = nullptr;

        --insideLADSPACallback;

        return m.release();
    }

    File file;
    LADSPA_Descriptor_Function moduleMain;

private:
    DynamicLibrary module;

    bool open()
    {
        module.open (file.getFullPathName());
        moduleMain = (LADSPA_Descriptor_Function) module.getFunction ("ladspa_descriptor");
        return moduleMain != nullptr;
    }

    void close()
    {
        module.close();
    }

    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LADSPAModuleHandle)
};

//==============================================================================
class LADSPAPluginInstance     : public AudioPluginInstance
{
public:
    LADSPAPluginInstance (const LADSPAModuleHandle::Ptr& m)
        : module (m), plugin (nullptr), handle (nullptr),
          initialised (false), tempBuffer (1, 1)
    {
        ++insideLADSPACallback;

        name = module->file.getFileNameWithoutExtension();

        JUCE_LADSPA_LOG ("Creating LADSPA instance: " + name);

        if (module->moduleMain != nullptr)
        {
            plugin = module->moduleMain (shellLADSPAUIDToCreate);

            if (plugin == nullptr)
            {
                JUCE_LADSPA_LOG ("Cannot find any valid descriptor in shared library");
                --insideLADSPACallback;
                return;
            }
        }
        else
        {
            JUCE_LADSPA_LOG ("Cannot find any valid plugin in shared library");
            --insideLADSPACallback;
            return;
        }

        const double sampleRate = getSampleRate() > 0 ? getSampleRate() : 44100.0;

        handle = plugin->instantiate (plugin, (uint32) sampleRate);

        --insideLADSPACallback;
    }

    ~LADSPAPluginInstance()
    {
        const ScopedLock sl (lock);

        jassert (insideLADSPACallback == 0);

        if (handle != nullptr && plugin != nullptr && plugin->cleanup != nullptr)
            plugin->cleanup (handle);

        initialised = false;
        module = nullptr;
        plugin = nullptr;
        handle = nullptr;
    }

    void initialise (double initialSampleRate, int initialBlockSize)
    {
        setPlayConfigDetails (inputs.size(), outputs.size(), initialSampleRate, initialBlockSize);

        if (initialised || plugin == nullptr || handle == nullptr)
            return;

        JUCE_LADSPA_LOG ("Initialising LADSPA: " + name);

        initialised = true;

        inputs.clear();
        outputs.clear();
        parameters.clear();

        for (unsigned int i = 0; i < plugin->PortCount; ++i)
        {
            const LADSPA_PortDescriptor portDesc = plugin->PortDescriptors[i];

            if ((portDesc & LADSPA_PORT_CONTROL) != 0)
                parameters.add (i);

            if ((portDesc & LADSPA_PORT_AUDIO) != 0)
            {
                if ((portDesc & LADSPA_PORT_INPUT) != 0)    inputs.add (i);
                if ((portDesc & LADSPA_PORT_OUTPUT) != 0)   outputs.add (i);
            }
        }

        parameterValues.calloc (parameters.size());

        for (int i = 0; i < parameters.size(); ++i)
            plugin->connect_port (handle, parameters[i], &(parameterValues[i].scaled));

        setPlayConfigDetails (inputs.size(), outputs.size(), initialSampleRate, initialBlockSize);

        setCurrentProgram (0);
        setLatencySamples (0);

        // Some plugins crash if this doesn't happen:
        if (plugin->activate   != nullptr)   plugin->activate (handle);
        if (plugin->deactivate != nullptr)   plugin->deactivate (handle);
    }

    //==============================================================================
    // AudioPluginInstance methods:

    void fillInPluginDescription (PluginDescription& desc) const
    {
        desc.name = getName();
        desc.fileOrIdentifier = module->file.getFullPathName();
        desc.uid = getUID();
        desc.lastFileModTime = module->file.getLastModificationTime();
        desc.lastInfoUpdateTime = Time::getCurrentTime();
        desc.pluginFormatName = "LADSPA";
        desc.category = getCategory();
        desc.manufacturerName = plugin != nullptr ? String (plugin->Maker) : String();
        desc.version = getVersion();
        desc.numInputChannels  = getTotalNumInputChannels();
        desc.numOutputChannels = getTotalNumOutputChannels();
        desc.isInstrument = false;
    }

    const String getName() const
    {
        if (plugin != nullptr && plugin->Label != nullptr)
            return plugin->Label;

        return name;
    }

    int getUID() const
    {
        if (plugin != nullptr && plugin->UniqueID != 0)
            return (int) plugin->UniqueID;

        return module->file.hashCode();
    }

    String getVersion() const                 { return LADSPA_VERSION; }
    String getCategory() const                { return "Effect"; }

    bool acceptsMidi() const                  { return false; }
    bool producesMidi() const                 { return false; }

    double getTailLengthSeconds() const       { return 0.0; }

    //==============================================================================
    void prepareToPlay (double newSampleRate, int samplesPerBlockExpected)
    {
        setLatencySamples (0);

        initialise (newSampleRate, samplesPerBlockExpected);

        if (initialised)
        {
            tempBuffer.setSize (jmax (1, outputs.size()), samplesPerBlockExpected);

            // dodgy hack to force some plugins to initialise the sample rate..
            if (getNumParameters() > 0)
            {
                const float old = getParameter (0);
                setParameter (0, (old < 0.5f) ? 1.0f : 0.0f);
                setParameter (0, old);
            }

            if (plugin->activate != nullptr)
                plugin->activate (handle);
        }
    }

    void releaseResources()
    {
        if (handle != nullptr && plugin->deactivate != nullptr)
            plugin->deactivate (handle);

        tempBuffer.setSize (1, 1);
    }

    void processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages)
    {
        const int numSamples = buffer.getNumSamples();

        if (initialised && plugin != nullptr && handle != nullptr)
        {
            for (int i = 0; i < inputs.size(); ++i)
                plugin->connect_port (handle, inputs[i],
                                      i < buffer.getNumChannels() ? buffer.getWritePointer (i) : nullptr);

            if (plugin->run != nullptr)
            {
                for (int i = 0; i < outputs.size(); ++i)
                    plugin->connect_port (handle, outputs.getUnchecked(i),
                                          i < buffer.getNumChannels() ? buffer.getWritePointer (i) : nullptr);

                plugin->run (handle, numSamples);
                return;
            }

            if (plugin->run_adding != nullptr)
            {
                tempBuffer.setSize (outputs.size(), numSamples);
                tempBuffer.clear();

                for (int i = 0; i < outputs.size(); ++i)
                    plugin->connect_port (handle, outputs.getUnchecked(i), tempBuffer.getWritePointer (i));

                plugin->run_adding (handle, numSamples);

                for (int i = 0; i < outputs.size(); ++i)
                    if (i < buffer.getNumChannels())
                        buffer.copyFrom (i, 0, tempBuffer, i, 0, numSamples);

                return;
            }

            jassertfalse; // no callback to use?
        }

        for (int i = getTotalNumInputChannels(), e = getTotalNumOutputChannels(); i < e; ++i)
            buffer.clear (i, 0, numSamples);
    }

    bool isInputChannelStereoPair (int index) const    { return isPositiveAndBelow (index, getTotalNumInputChannels()); }
    bool isOutputChannelStereoPair (int index) const   { return isPositiveAndBelow (index, getTotalNumOutputChannels()); }

    const String getInputChannelName (const int index) const
    {
        if (isPositiveAndBelow (index, getTotalNumInputChannels()))
            return String (plugin->PortNames [inputs [index]]).trim();

        return {};
    }

    const String getOutputChannelName (const int index) const
    {
        if (isPositiveAndBelow (index, getTotalNumInputChannels()))
            return String (plugin->PortNames [outputs [index]]).trim();

        return {};
    }

    //==============================================================================
    int getNumParameters()                              { return handle != nullptr ? parameters.size() : 0; }

    bool isParameterAutomatable (int index) const
    {
        return plugin != nullptr
                 && (plugin->PortDescriptors [parameters[index]] & LADSPA_PORT_INPUT) != 0;
    }

    float getParameter (int index)
    {
        if (plugin != nullptr && isPositiveAndBelow (index, parameters.size()))
        {
            const ScopedLock sl (lock);
            return parameterValues[index].unscaled;
        }

        return 0.0f;
    }

    void setParameter (int index, float newValue)
    {
        if (plugin != nullptr && isPositiveAndBelow (index, parameters.size()))
        {
            const ScopedLock sl (lock);

            ParameterValue& p = parameterValues[index];

            if (p.unscaled != newValue)
                p = ParameterValue (getNewParamScaled (plugin->PortRangeHints [parameters[index]], newValue), newValue);
        }
    }

    const String getParameterName (int index)
    {
        if (plugin != nullptr)
        {
            jassert (isPositiveAndBelow (index, parameters.size()));
            return String (plugin->PortNames [parameters [index]]).trim();
        }

        return {};
    }

    const String getParameterText (int index)
    {
        if (plugin != nullptr)
        {
            jassert (index >= 0 && index < parameters.size());

            const LADSPA_PortRangeHint& hint = plugin->PortRangeHints [parameters [index]];

            if (LADSPA_IS_HINT_INTEGER (hint.HintDescriptor))
                return String ((int) parameterValues[index].scaled);

            return String (parameterValues[index].scaled, 4);
        }

        return {};
    }

    //==============================================================================
    int getNumPrograms()                                { return 0; }
    int getCurrentProgram()                             { return 0; }

    void setCurrentProgram (int newIndex)
    {
        if (plugin != nullptr)
            for (int i = 0; i < parameters.size(); ++i)
                parameterValues[i] = getParamValue (plugin->PortRangeHints [parameters[i]]);
    }

    const String getProgramName (int index)
    {
        // XXX
        return {};
    }

    void changeProgramName (int index, const String& newName)
    {
        // XXX
    }

    //==============================================================================
    void getStateInformation (MemoryBlock& destData)
    {
        destData.setSize (sizeof (float) * getNumParameters());
        destData.fillWith (0);

        float* const p = (float*) ((char*) destData.getData());
        for (int i = 0; i < getNumParameters(); ++i)
            p[i] = getParameter(i);
    }

    void getCurrentProgramStateInformation (MemoryBlock& destData)
    {
        getStateInformation (destData);
    }

    void setStateInformation (const void* data, int sizeInBytes)
    {
        const float* p = static_cast<const float*> (data);

        for (int i = 0; i < getNumParameters(); ++i)
            setParameter (i, p[i]);
    }

    void setCurrentProgramStateInformation (const void* data, int sizeInBytes)
    {
        setStateInformation (data, sizeInBytes);
    }

    bool hasEditor() const
    {
        return false;
    }

    AudioProcessorEditor* createEditor()
    {
        return nullptr;
    }

    bool isValid() const
    {
        return handle != nullptr;
    }

    LADSPAModuleHandle::Ptr module;
    const LADSPA_Descriptor* plugin;

private:
    LADSPA_Handle handle;
    String name;
    CriticalSection lock;
    bool initialised;
    AudioSampleBuffer tempBuffer;
    Array<int> inputs, outputs, parameters;

    struct ParameterValue
    {
        inline ParameterValue() noexcept                   : scaled (0), unscaled (0) {}
        inline ParameterValue (float s, float u) noexcept  : scaled (s), unscaled (u) {}

        float scaled, unscaled;
    };

    HeapBlock<ParameterValue> parameterValues;

    //==============================================================================
    static float scaledValue (float low, float high, float alpha, bool useLog) noexcept
    {
        if (useLog && low > 0 && high > 0)
            return expf (logf (low) * (1.0f - alpha) + logf (high) * alpha);

        return low + (high - low) * alpha;
    }

    static float toIntIfNecessary (const LADSPA_PortRangeHintDescriptor& desc, float value)
    {
        return LADSPA_IS_HINT_INTEGER (desc) ? ((float) (int) value) : value;
    }

    float getNewParamScaled (const LADSPA_PortRangeHint& hint, float newValue) const
    {
        const LADSPA_PortRangeHintDescriptor& desc = hint.HintDescriptor;

        if (LADSPA_IS_HINT_TOGGLED (desc))
            return (newValue < 0.5f) ? 0.0f : 1.0f;

        const float scale = LADSPA_IS_HINT_SAMPLE_RATE (desc) ? (float) getSampleRate() : 1.0f;
        const float lower = hint.LowerBound * scale;
        const float upper = hint.UpperBound * scale;

        if (LADSPA_IS_HINT_BOUNDED_BELOW (desc) && LADSPA_IS_HINT_BOUNDED_ABOVE (desc))
            return toIntIfNecessary (desc, scaledValue (lower, upper, newValue, LADSPA_IS_HINT_LOGARITHMIC (desc)));

        if (LADSPA_IS_HINT_BOUNDED_BELOW (desc))   return toIntIfNecessary (desc, newValue);
        if (LADSPA_IS_HINT_BOUNDED_ABOVE (desc))   return toIntIfNecessary (desc, newValue * upper);

        return 0.0f;
    }

    ParameterValue getParamValue (const LADSPA_PortRangeHint& hint) const
    {
        const LADSPA_PortRangeHintDescriptor& desc = hint.HintDescriptor;

        if (LADSPA_IS_HINT_HAS_DEFAULT (desc))
        {
            if (LADSPA_IS_HINT_DEFAULT_0 (desc))    return ParameterValue();
            if (LADSPA_IS_HINT_DEFAULT_1 (desc))    return ParameterValue (1.0f, 1.0f);
            if (LADSPA_IS_HINT_DEFAULT_100 (desc))  return ParameterValue (100.0f, 0.5f);
            if (LADSPA_IS_HINT_DEFAULT_440 (desc))  return ParameterValue (440.0f, 0.5f);

            const float scale = LADSPA_IS_HINT_SAMPLE_RATE (desc) ? (float) getSampleRate() : 1.0f;
            const float lower = hint.LowerBound * scale;
            const float upper = hint.UpperBound * scale;

            if (LADSPA_IS_HINT_BOUNDED_BELOW (desc) && LADSPA_IS_HINT_DEFAULT_MINIMUM (desc))   return ParameterValue (lower, 0.0f);
            if (LADSPA_IS_HINT_BOUNDED_ABOVE (desc) && LADSPA_IS_HINT_DEFAULT_MAXIMUM (desc))   return ParameterValue (upper, 1.0f);

            if (LADSPA_IS_HINT_BOUNDED_BELOW (desc))
            {
                const bool useLog = LADSPA_IS_HINT_LOGARITHMIC (desc);

                if (LADSPA_IS_HINT_DEFAULT_LOW    (desc))  return ParameterValue (scaledValue (lower, upper, 0.25f, useLog), 0.25f);
                if (LADSPA_IS_HINT_DEFAULT_MIDDLE (desc))  return ParameterValue (scaledValue (lower, upper, 0.50f, useLog), 0.50f);
                if (LADSPA_IS_HINT_DEFAULT_HIGH   (desc))  return ParameterValue (scaledValue (lower, upper, 0.75f, useLog), 0.75f);
            }
        }

        return ParameterValue();
    }

    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LADSPAPluginInstance)
};

//==============================================================================
//==============================================================================
LADSPAPluginFormat::LADSPAPluginFormat() {}
LADSPAPluginFormat::~LADSPAPluginFormat() {}

void LADSPAPluginFormat::findAllTypesForFile (OwnedArray<PluginDescription>& results,
                                              const String& fileOrIdentifier)
{
    if (! fileMightContainThisPluginType (fileOrIdentifier))
        return;

    PluginDescription desc;
    desc.fileOrIdentifier = fileOrIdentifier;
    desc.uid = 0;

    ScopedPointer<LADSPAPluginInstance> instance (dynamic_cast<LADSPAPluginInstance*> (createInstanceFromDescription (desc, 44100.0, 512)));

    if (instance == nullptr || ! instance->isValid())
        return;

    instance->initialise (44100.0, 512);

    instance->fillInPluginDescription (desc);

    if (instance->module->moduleMain != nullptr)
    {
        for (int uid = 0;; ++uid)
        {
            if (const LADSPA_Descriptor* plugin = instance->module->moduleMain (uid))
            {
                desc.uid = uid;
                desc.name = plugin->Name != nullptr ? plugin->Name : "Unknown";

                if (! arrayContainsPlugin (results, desc))
                    results.add (new PluginDescription (desc));
            }
            else
            {
                break;
            }
        }
    }
}

void LADSPAPluginFormat::createPluginInstance (const PluginDescription& desc,
                                               double sampleRate, int blockSize,
                                               void* userData,
                                               void (*callback) (void*, AudioPluginInstance*, const String&))
{
    ScopedPointer<LADSPAPluginInstance> result;

    if (fileMightContainThisPluginType (desc.fileOrIdentifier))
    {
        File file (desc.fileOrIdentifier);

        const File previousWorkingDirectory (File::getCurrentWorkingDirectory());
        file.getParentDirectory().setAsCurrentWorkingDirectory();

        const LADSPAModuleHandle::Ptr module (LADSPAModuleHandle::findOrCreateModule (file));

        if (module != nullptr)
        {
            shellLADSPAUIDToCreate = desc.uid;

            result = new LADSPAPluginInstance (module);

            if (result->plugin != nullptr && result->isValid())
                result->initialise (sampleRate, blockSize);
            else
                result = nullptr;
        }

        previousWorkingDirectory.setAsCurrentWorkingDirectory();
    }

    String errorMsg;

    if (result == nullptr)
        errorMsg = String (NEEDS_TRANS ("Unable to load XXX plug-in file")).replace ("XXX", "LADSPA");

    callback (userData, result.release(), errorMsg);
}

bool LADSPAPluginFormat::requiresUnblockedMessageThreadDuringCreation (const PluginDescription&) const noexcept
{
    return false;
}

bool LADSPAPluginFormat::fileMightContainThisPluginType (const String& fileOrIdentifier)
{
    const File f (File::createFileWithoutCheckingPath (fileOrIdentifier));
    return f.existsAsFile() && f.hasFileExtension (".so");
}

String LADSPAPluginFormat::getNameOfPluginFromIdentifier (const String& fileOrIdentifier)
{
    return fileOrIdentifier;
}

bool LADSPAPluginFormat::pluginNeedsRescanning (const PluginDescription& desc)
{
    return File (desc.fileOrIdentifier).getLastModificationTime() != desc.lastFileModTime;
}

bool LADSPAPluginFormat::doesPluginStillExist (const PluginDescription& desc)
{
    return File::createFileWithoutCheckingPath (desc.fileOrIdentifier).exists();
}

StringArray LADSPAPluginFormat::searchPathsForPlugins (const FileSearchPath& directoriesToSearch, const bool recursive, bool)
{
    StringArray results;

    for (int j = 0; j < directoriesToSearch.getNumPaths(); ++j)
        recursiveFileSearch (results, directoriesToSearch[j], recursive);

    return results;
}

void LADSPAPluginFormat::recursiveFileSearch (StringArray& results, const File& dir, const bool recursive)
{
    DirectoryIterator iter (dir, false, "*", File::findFilesAndDirectories);

    while (iter.next())
    {
        const File f (iter.getFile());
        bool isPlugin = false;

        if (fileMightContainThisPluginType (f.getFullPathName()))
        {
            isPlugin = true;
            results.add (f.getFullPathName());
        }

        if (recursive && (! isPlugin) && f.isDirectory())
            recursiveFileSearch (results, f, true);
    }
}

FileSearchPath LADSPAPluginFormat::getDefaultLocationsToSearch()
{
    return FileSearchPath (SystemStats::getEnvironmentVariable ("LADSPA_PATH",
                                                                "/usr/lib/ladspa;/usr/local/lib/ladspa;~/.ladspa")
                             .replace (":", ";"));
}

} // namespace juce

#endif


text:
/*
  ==============================================================================

   This file is part of the JUCE library.
   Copyright (c) 2017 - ROLI Ltd.

   JUCE is an open source library subject to commercial or open-source
   licensing.

   By using JUCE, you agree to the terms of both the JUCE 5 End-User License
   Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
   27th April 2017).

   End User License Agreement: www.juce.com/juce-5-licence
   Privacy Policy: www.juce.com/juce-5-privacy-policy

   Or: You may also use this code under the terms of the GPL v3 (see
   www.gnu.org/licenses).

   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
   DISCLAIMED.

  ==============================================================================
*/

namespace juce
{

//==============================================================================
struct SpeakerMappings  : private AudioChannelSet // (inheritance only to give easier access to items in the namespace)
{
    struct Mapping
    {
        int32 vst2;
        ChannelType channels[13];

        bool matches (const Array<ChannelType>& chans) const noexcept
        {
            const int n = sizeof (channels) / sizeof (ChannelType);

            for (int i = 0; i < n; ++i)
            {
                if (channels[i] == unknown)  return (i == chans.size());
                if (i == chans.size())       return (channels[i] == unknown);

                if (channels[i] != chans.getUnchecked(i))
                    return false;
            }

            return true;
        }
    };

    static AudioChannelSet vstArrangementTypeToChannelSet (int32 arr, int fallbackNumChannels)
    {
        if      (arr == vstSpeakerConfigTypeEmpty)          return AudioChannelSet::disabled();
        else if (arr == vstSpeakerConfigTypeMono)           return AudioChannelSet::mono();
        else if (arr == vstSpeakerConfigTypeLR)             return AudioChannelSet::stereo();
        else if (arr == vstSpeakerConfigTypeLRC)            return AudioChannelSet::createLCR();
        else if (arr == vstSpeakerConfigTypeLRS)            return AudioChannelSet::createLRS();
        else if (arr == vstSpeakerConfigTypeLRCS)           return AudioChannelSet::createLCRS();
        else if (arr == vstSpeakerConfigTypeLRCLsRs)        return AudioChannelSet::create5point0();
        else if (arr == vstSpeakerConfigTypeLRCLfeLsRs)     return AudioChannelSet::create5point1();
        else if (arr == vstSpeakerConfigTypeLRCLsRsCs)      return AudioChannelSet::create6point0();
        else if (arr == vstSpeakerConfigTypeLRCLfeLsRsCs)   return AudioChannelSet::create6point1();
        else if (arr == vstSpeakerConfigTypeLRLsRsSlSr)     return AudioChannelSet::create6point0Music();
        else if (arr == vstSpeakerConfigTypeLRLfeLsRsSlSr)  return AudioChannelSet::create6point1Music();
        else if (arr == vstSpeakerConfigTypeLRCLsRsSlSr)    return AudioChannelSet::create7point0();
        else if (arr == vstSpeakerConfigTypeLRCLsRsLcRc)    return AudioChannelSet::create7point0SDDS();
        else if (arr == vstSpeakerConfigTypeLRCLfeLsRsSlSr) return AudioChannelSet::create7point1();
        else if (arr == vstSpeakerConfigTypeLRCLfeLsRsLcRc) return AudioChannelSet::create7point1SDDS();
        else if (arr == vstSpeakerConfigTypeLRLsRs)         return AudioChannelSet::quadraphonic();

        for (const Mapping* m = getMappings(); m->vst2 != vstSpeakerConfigTypeEmpty; ++m)
        {
            if (m->vst2 == arr)
            {
                AudioChannelSet s;

                for (int i = 0; m->channels[i] != 0; ++i)
                    s.addChannel (m->channels[i]);

                return s;
            }
        }

        return AudioChannelSet::discreteChannels (fallbackNumChannels);
    }

    static AudioChannelSet vstArrangementTypeToChannelSet (const VstSpeakerConfiguration& arr)
    {
        return vstArrangementTypeToChannelSet (arr.type, arr.numberOfChannels);
    }

    static int32 channelSetToVstArrangementType (AudioChannelSet channels)
    {
        if      (channels == AudioChannelSet::disabled())           return vstSpeakerConfigTypeEmpty;
        else if (channels == AudioChannelSet::mono())               return vstSpeakerConfigTypeMono;
        else if (channels == AudioChannelSet::stereo())             return vstSpeakerConfigTypeLR;
        else if (channels == AudioChannelSet::createLCR())          return vstSpeakerConfigTypeLRC;
        else if (channels == AudioChannelSet::createLRS())          return vstSpeakerConfigTypeLRS;
        else if (channels == AudioChannelSet::createLCRS())         return vstSpeakerConfigTypeLRCS;
        else if (channels == AudioChannelSet::create5point0())      return vstSpeakerConfigTypeLRCLsRs;
        else if (channels == AudioChannelSet::create5point1())      return vstSpeakerConfigTypeLRCLfeLsRs;
        else if (channels == AudioChannelSet::create6point0())      return vstSpeakerConfigTypeLRCLsRsCs;
        else if (channels == AudioChannelSet::create6point1())      return vstSpeakerConfigTypeLRCLfeLsRsCs;
        else if (channels == AudioChannelSet::create6point0Music()) return vstSpeakerConfigTypeLRLsRsSlSr;
        else if (channels == AudioChannelSet::create6point1Music()) return vstSpeakerConfigTypeLRLfeLsRsSlSr;
        else if (channels == AudioChannelSet::create7point0())      return vstSpeakerConfigTypeLRCLsRsSlSr;
        else if (channels == AudioChannelSet::create7point0SDDS())  return vstSpeakerConfigTypeLRCLsRsLcRc;
        else if (channels == AudioChannelSet::create7point1())      return vstSpeakerConfigTypeLRCLfeLsRsSlSr;
        else if (channels == AudioChannelSet::create7point1SDDS())  return vstSpeakerConfigTypeLRCLfeLsRsLcRc;
        else if (channels == AudioChannelSet::quadraphonic())       return vstSpeakerConfigTypeLRLsRs;

        Array<AudioChannelSet::ChannelType> chans (channels.getChannelTypes());

        if (channels == AudioChannelSet::disabled())
            return vstSpeakerConfigTypeEmpty;

        for (const Mapping* m = getMappings(); m->vst2 != vstSpeakerConfigTypeEmpty; ++m)
            if (m->matches (chans))
                return m->vst2;

        return vstSpeakerConfigTypeUser;
    }

    class VstSpeakerConfigurationHolder
    {
    public:
        VstSpeakerConfigurationHolder ()                                           { clear(); }
        VstSpeakerConfigurationHolder (const VstSpeakerConfiguration& vstConfig)   { operator= (vstConfig); }
        VstSpeakerConfigurationHolder (const VstSpeakerConfigurationHolder& other) { operator= (other.get()); }
        VstSpeakerConfigurationHolder (VstSpeakerConfigurationHolder&& other) : storage (static_cast<HeapBlock<VstSpeakerConfiguration>&&> (other.storage)) { other.clear(); }

        VstSpeakerConfigurationHolder (const AudioChannelSet& channels)
        {
            auto numberOfChannels = channels.size();
            VstSpeakerConfiguration& dst = *allocate (numberOfChannels);

            dst.type = channelSetToVstArrangementType (channels);
            dst.numberOfChannels = numberOfChannels;

            for (int i = 0; i < dst.numberOfChannels; ++i)
            {
                VstIndividualSpeakerInfo& speaker = dst.speakers[i];

                zeromem (&speaker, sizeof (VstIndividualSpeakerInfo));
                speaker.type = getSpeakerType (channels.getTypeOfChannel (i));
            }
        }

        VstSpeakerConfigurationHolder& operator= (const VstSpeakerConfigurationHolder& vstConfig) { return operator=(vstConfig.get()); }
        VstSpeakerConfigurationHolder& operator= (const VstSpeakerConfiguration& vstConfig)
        {
            VstSpeakerConfiguration& dst = *allocate (vstConfig.numberOfChannels);

            dst.type             = vstConfig.type;
            dst.numberOfChannels = vstConfig.numberOfChannels;

            for (int i = 0; i < dst.numberOfChannels; ++i)
                dst.speakers[i] = vstConfig.speakers[i];

            return *this;
        }

        VstSpeakerConfigurationHolder& operator= (VstSpeakerConfigurationHolder && vstConfig)
        {
            storage = static_cast<HeapBlock<VstSpeakerConfiguration>&&> (vstConfig.storage);
            vstConfig.clear();

            return *this;
        }

        const VstSpeakerConfiguration& get() const { return *storage.get(); }

    private:
        JUCE_LEAK_DETECTOR (VstSpeakerConfigurationHolder)

        HeapBlock<VstSpeakerConfiguration> storage;

        VstSpeakerConfiguration* allocate (int numChannels)
        {
            auto arrangementSize = sizeof (VstSpeakerConfiguration)
                                     + sizeof (VstIndividualSpeakerInfo) * static_cast<size_t> (jmax (8, numChannels) - 8);

            storage.malloc (1, arrangementSize);
            return storage.get();
        }

        void clear()
        {
            VstSpeakerConfiguration& dst = *allocate (0);

            dst.type = vstSpeakerConfigTypeEmpty;
            dst.numberOfChannels = 0;
        }
    };

    static const Mapping* getMappings() noexcept
    {
        static const Mapping mappings[] =
        {
            { vstSpeakerConfigTypeMono,           { centre, unknown } },
            { vstSpeakerConfigTypeLR,             { left, right, unknown } },
            { vstSpeakerConfigTypeLsRs,           { leftSurround, rightSurround, unknown } },
            { vstSpeakerConfigTypeLcRc,           { leftCentre, rightCentre, unknown } },
            { vstSpeakerConfigTypeSlSr,           { leftSurroundRear, rightSurroundRear, unknown } },
            { vstSpeakerConfigTypeCLfe,           { centre, LFE, unknown } },
            { vstSpeakerConfigTypeLRC,            { left, right, centre, unknown } },
            { vstSpeakerConfigTypeLRS,            { left, right, surround, unknown } },
            { vstSpeakerConfigTypeLRCLfe,         { left, right, centre, LFE, unknown } },
            { vstSpeakerConfigTypeLRLfeS,         { left, right, LFE, surround, unknown } },
            { vstSpeakerConfigTypeLRCS,           { left, right, centre, surround, unknown } },
            { vstSpeakerConfigTypeLRLsRs,         { left, right, leftSurround, rightSurround, unknown } },
            { vstSpeakerConfigTypeLRCLfeS,        { left, right, centre, LFE, surround, unknown } },
            { vstSpeakerConfigTypeLRLfeLsRs,      { left, right, LFE, leftSurround, rightSurround, unknown } },
            { vstSpeakerConfigTypeLRCLsRs,        { left, right, centre, leftSurround, rightSurround, unknown } },
            { vstSpeakerConfigTypeLRCLfeLsRs,     { left, right, centre, LFE, leftSurround, rightSurround, unknown } },
            { vstSpeakerConfigTypeLRCLsRsCs,      { left, right, centre, leftSurround, rightSurround, surround, unknown } },
            { vstSpeakerConfigTypeLRLsRsSlSr,     { left, right, leftSurround, rightSurround, leftSurroundRear, rightSurroundRear, unknown } },
            { vstSpeakerConfigTypeLRCLfeLsRsCs,   { left, right, centre, LFE, leftSurround, rightSurround, surround, unknown } },
            { vstSpeakerConfigTypeLRLfeLsRsSlSr,  { left, right, LFE, leftSurround, rightSurround, leftSurroundRear, rightSurroundRear, unknown } },
            { vstSpeakerConfigTypeLRCLsRsLcRc,    { left, right, centre, leftSurround, rightSurround, topFrontLeft, topFrontRight, unknown } },
            { vstSpeakerConfigTypeLRCLsRsSlSr,    { left, right, centre, leftSurround, rightSurround, leftSurroundRear, rightSurroundRear, unknown } },
            { vstSpeakerConfigTypeLRCLfeLsRsLcRc, { left, right, centre, LFE, leftSurround, rightSurround, topFrontLeft, topFrontRight, unknown } },
            { vstSpeakerConfigTypeLRCLfeLsRsSlSr, { left, right, centre, LFE, leftSurround, rightSurround, leftSurroundRear, rightSurroundRear, unknown } },
            { vstSpeakerConfigTypeLRCLsRsLcRcCs,  { left, right, centre, leftSurround, rightSurround, topFrontLeft, topFrontRight, surround, unknown } },
            { vstSpeakerConfigTypeLRCLsRsCsSlSr,  { left, right, centre, leftSurround, rightSurround, surround, leftSurroundRear, rightSurroundRear, unknown } },
            { vstSpeakerConfigTypeLRCLfeLsRsLcRcCs,   { left, right, centre, LFE, leftSurround, rightSurround, topFrontLeft, topFrontRight, surround, unknown } },
            { vstSpeakerConfigTypeLRCLfeLsRsCsSlSr,   { left, right, centre, LFE, leftSurround, rightSurround, surround, leftSurroundRear, rightSurroundRear, unknown } },
            { vstSpeakerConfigTypeLRCLfeLsRsTflTfcTfrTrlTrrLfe2,            { left, right, centre, LFE, leftSurround, rightSurround, topFrontLeft, topFrontCentre, topFrontRight, topRearLeft, topRearRight, LFE2, unknown } },
            { vstSpeakerConfigTypeEmpty,          { unknown } }
        };

        return mappings;
    }

    static inline int32 getSpeakerType (AudioChannelSet::ChannelType type) noexcept
    {
        switch (type)
        {
            case AudioChannelSet::left:              return vstIndividualSpeakerTypeLeft;
            case AudioChannelSet::right:             return vstIndividualSpeakerTypeRight;
            case AudioChannelSet::centre:            return vstIndividualSpeakerTypeCentre;
            case AudioChannelSet::LFE:           return vstIndividualSpeakerTypeLFE;
            case AudioChannelSet::leftSurround:      return vstIndividualSpeakerTypeLeftSurround;
            case AudioChannelSet::rightSurround:     return vstIndividualSpeakerTypeRightSurround;
            case AudioChannelSet::leftCentre:        return vstIndividualSpeakerTypeLeftCentre;
            case AudioChannelSet::rightCentre:       return vstIndividualSpeakerTypeRightCentre;
            case AudioChannelSet::surround:          return vstIndividualSpeakerTypeSurround;
            case AudioChannelSet::leftSurroundRear:  return vstIndividualSpeakerTypeLeftRearSurround;
            case AudioChannelSet::rightSurroundRear: return vstIndividualSpeakerTypeRightRearSurround;
            case AudioChannelSet::topMiddle:         return vstIndividualSpeakerTypeTopMiddle;
            case AudioChannelSet::topFrontLeft:      return vstIndividualSpeakerTypeTopFrontLeft;
            case AudioChannelSet::topFrontCentre:    return vstIndividualSpeakerTypeTopFrontCentre;
            case AudioChannelSet::topFrontRight:     return vstIndividualSpeakerTypeTopFrontRight;
            case AudioChannelSet::topRearLeft:       return vstIndividualSpeakerTypeTopRearLeft;
            case AudioChannelSet::topRearCentre:     return vstIndividualSpeakerTypeTopRearCentre;
            case AudioChannelSet::topRearRight:      return vstIndividualSpeakerTypeTopRearRight;
            case AudioChannelSet::LFE2:          return vstIndividualSpeakerTypeLFE2;
            default: break;
        }

        return 0;
    }

    static inline AudioChannelSet::ChannelType getChannelType (int32 type) noexcept
    {
        switch (type)
        {
            case vstIndividualSpeakerTypeLeft:                 return AudioChannelSet::left;
            case vstIndividualSpeakerTypeRight:                return AudioChannelSet::right;
            case vstIndividualSpeakerTypeCentre:               return AudioChannelSet::centre;
            case vstIndividualSpeakerTypeLFE:              return AudioChannelSet::LFE;
            case vstIndividualSpeakerTypeLeftSurround:         return AudioChannelSet::leftSurround;
            case vstIndividualSpeakerTypeRightSurround:        return AudioChannelSet::rightSurround;
            case vstIndividualSpeakerTypeLeftCentre:           return AudioChannelSet::leftCentre;
            case vstIndividualSpeakerTypeRightCentre:          return AudioChannelSet::rightCentre;
            case vstIndividualSpeakerTypeSurround:             return AudioChannelSet::surround;
            case vstIndividualSpeakerTypeLeftRearSurround:     return AudioChannelSet::leftSurroundRear;
            case vstIndividualSpeakerTypeRightRearSurround:    return AudioChannelSet::rightSurroundRear;
            case vstIndividualSpeakerTypeTopMiddle:            return AudioChannelSet::topMiddle;
            case vstIndividualSpeakerTypeTopFrontLeft:         return AudioChannelSet::topFrontLeft;
            case vstIndividualSpeakerTypeTopFrontCentre:       return AudioChannelSet::topFrontCentre;
            case vstIndividualSpeakerTypeTopFrontRight:        return AudioChannelSet::topFrontRight;
            case vstIndividualSpeakerTypeTopRearLeft:          return AudioChannelSet::topRearLeft;
            case vstIndividualSpeakerTypeTopRearCentre:        return AudioChannelSet::topRearCentre;
            case vstIndividualSpeakerTypeTopRearRight:         return AudioChannelSet::topRearRight;
            case vstIndividualSpeakerTypeLFE2:             return AudioChannelSet::LFE2;
            default: break;
        }

        return AudioChannelSet::unknown;
    }
};

} // namespace juce