Fail to load VST3

Hello,
I have problems to load and host VST3 in my project. Everything works on my intel i5 CPU but when I compile and run my code on raspberry, my loader does not find the VST3.
On my raspberry, my VST3 are compiled with the armv6l-linux format. When I look for the shared object I find it in the vst3. I add some debug in JUCE code and scanAndAddDragAndDroppedFiles goes and try to load the shared object in the recursive part:

But even if the JUCE code is trying to load the shared object it fail somehow.

Here is the code from my project that call the JUCE loader:

Do you guys have any clue about how to solve this ?

Thanks in advance

Is the AudioPluginHost definitely compiled for exactly the same architecture as your VST3 plug-ins?

Yes, both the host and my plugin have been compiled on the raspberry. In fact they are compiled in the same project with the same CMakeList.txt at the root of the project.


(Here each red dot correspond to a CMakeList.txt)

Is there a chance that juce::KnownPluginList does not work with the armv6l-linux architecture?

This is often also caused by a shared library dependency issue on Linux. What does ldd output when you use it to inspect the vst3’s shared object file.

Hello,

Here is the output:

	linux-vdso.so.1 (0xbefb6000)
	/usr/lib/arm-linux-gnueabihf/libarmmem-${PLATFORM}.so => /usr/lib/arm-linux-gnueabihf/libarmmem-v7l.so (0xb6700000)
	libfreetype.so.6 => /lib/arm-linux-gnueabihf/libfreetype.so.6 (0xb663c000)
	libatomic.so.1 => /lib/arm-linux-gnueabihf/libatomic.so.1 (0xb6623000)
	libdl.so.2 => /lib/arm-linux-gnueabihf/libdl.so.2 (0xb660f000)
	libpthread.so.0 => /lib/arm-linux-gnueabihf/libpthread.so.0 (0xb65e3000)
	libstdc++.so.6 => /lib/arm-linux-gnueabihf/libstdc++.so.6 (0xb645b000)
	libm.so.6 => /lib/arm-linux-gnueabihf/libm.so.6 (0xb63ec000)
	libgcc_s.so.1 => /lib/arm-linux-gnueabihf/libgcc_s.so.1 (0xb63bf000)
	libc.so.6 => /lib/arm-linux-gnueabihf/libc.so.6 (0xb626b000)
	/lib/ld-linux-armhf.so.3 (0xb6f77000)
	libpng16.so.16 => /lib/arm-linux-gnueabihf/libpng16.so.16 (0xb622c000)
	libz.so.1 => /lib/arm-linux-gnueabihf/libz.so.1 (0xb6204000)
	libbrotlidec.so.1 => /lib/arm-linux-gnueabihf/libbrotlidec.so.1 (0xb61e9000)
	libbrotlicommon.so.1 => /lib/arm-linux-gnueabihf/libbrotlicommon.so.1 (0xb61b8000)

Plug-in loading definitely works on the Raspberry Pi 4 - I just tried building the AudioPluginHost and AudioPluginDemo_VST3 on my Raspberry Pi, and the host is able to load the plug-in with no problems.

As a first step, I recommend building the AudioPluginHost from the JUCE repository, and checking whether that program is able to load your plug-ins. If it is, then the problem you’re seeing is likely due to the plug-in loading code in your own host (the call to juce::MessageManager::deleteInstance() looks very suspicious…).

I tried to compile AudioPluginHost but I get the following errors:

undefined reference to __atomic_load_8
undefined reference to __atomic_store_8

I don’t really know where does it come from ?

Are you building with CMake or with Make?

If you’re building with CMake, then this should just work. It would be helpful to see the commands you’re using to configure and build.

If you’re using Make, you may need to manually link the atomic library, e.g. make LDFLAGS=-latomic

Ok thank you for your answer, it finally worked. The AudioPluginHost compiled.
I tried to load my own VST3, the ones my host fail to load. The thing is AudioPluginHost also fail to load them and popup the following error message: “The following files appeared to be plugin files, but failed to load correctly: Filter.vst3, Gain.vst3, Phaser.vst3, Reverb.vst3”.

Like I said my host successfuly load my VST3 on desktop version. They are very simple. Here is the Reverb for example:

CMakeList.txt:

project(Reverb VERSION 0.0.1)

set (BaseTargetName Reverb)

set(CMAKE_DEBUG_POSTFIX "")

juce_add_plugin("${BaseTargetName}"
        # VERSION ...                               # Set this if the plugin version is different to the project version
        # ICON_BIG ...                              # ICON_* arguments specify a path to an image file to use as an icon for the Standalone
        # ICON_SMALL ...
        COMPANY_NAME "MyCompany"
        IS_SYNTH FALSE
        NEEDS_MIDI_INPUT TRUE
        NEEDS_MIDI_OUTPUT FALSE
        IS_MIDI_EFFECT FALSE
        EDITOR_WANTS_KEYBOARD_FOCUS FALSE
        COPY_PLUGIN_AFTER_BUILD TRUE
        PLUGIN_MANUFACTURER_CODE Juce
        PLUGIN_CODE Dem0
        FORMATS AU VST3 Standalone
        PRODUCT_NAME "Reverb")

target_sources(${BaseTargetName} PRIVATE
        Source/PluginProcessor.cpp)

target_compile_definitions(${BaseTargetName}
        PUBLIC
        JUCE_DISABLE_JUCE_VERSION_PRINTING=1
        JUCE_WEB_BROWSER=0
        JUCE_USE_CURL=0
        JUCE_VST3_CAN_REPLACE_VST2=0)

target_link_libraries(${BaseTargetName} PRIVATE
        juce::juce_core
        juce::juce_dsp
        juce::juce_audio_processors
        juce::juce_audio_utils
        juce::juce_audio_devices
        juce::juce_audio_utils
        juce::juce_recommended_config_flags
        juce::juce_recommended_lto_flags
        juce::juce_recommended_warning_flags)

PluginProcessor.cpp

#include "PluginProcessor.h"

//A little helper to get the parameter ID
juce::String getParamID(juce::AudioProcessorParameter *param) {
    if (auto paramWithID = dynamic_cast<juce::AudioProcessorParameterWithID *>(param))
        return paramWithID->paramID;

    return param->getName(50);
}

ReverbAudioProcessor::ReverbAudioProcessor()
        : juce::AudioProcessor(getBuses()),
          _roomSizeParam(new juce::AudioParameterFloat("roomSize", "RoomSize",
                                                       juce::NormalisableRange<float>(0.0f, 1.0f), 0.5f)),
          _dampingParam(new juce::AudioParameterFloat("damping", "Damping",
                                                      juce::NormalisableRange<float>(0.0f, 1.0f), 0.5f)),
          _wetLevelParam(new juce::AudioParameterFloat("wetLevel", "WetLevel",
                                                       juce::NormalisableRange<float>(0.0f, 1.0f), 0.33f)),
          _dryLevelParam(new juce::AudioParameterFloat("dryLevel", "DryLevel",
                                                       juce::NormalisableRange<float>(0.0f, 1.0f), 0.4f)),
          _widthParam(new juce::AudioParameterFloat("width", "Width",
                                                    juce::NormalisableRange<float>(0.0f, 1.0f), 1.0f)),
          _freezeModeParam(new juce::AudioParameterFloat("freezeMode", "FreezeMode",
                                                         juce::NormalisableRange<float>(0.0f, 1.0f), 0.0f)),
          _reverb(std::make_unique<juce::dsp::Reverb>()) {
    addParameter(_roomSizeParam);
    addParameter(_dampingParam);
    addParameter(_wetLevelParam);
    addParameter(_dryLevelParam);
    addParameter(_widthParam);
    addParameter(_freezeModeParam);
    updateReverb();
}

void ReverbAudioProcessor::processBlock(juce::AudioBuffer<float> &buffer,
                                        juce::MidiBuffer & /*midiMessages*/) {
    juce::ScopedNoDenormals noDenormals;
    auto totalNumInputChannels = getTotalNumInputChannels();
    auto totalNumOutputChannels = getTotalNumOutputChannels();

    for (auto i = totalNumInputChannels; i < totalNumOutputChannels; i++)
        buffer.clear(i, 0, buffer.getNumSamples());

    juce::dsp::AudioBlock<float> block(buffer);
    updateReverb();
    juce::dsp::ProcessContextReplacing<float> context(block);
    this->_reverb->process(context);
}

void ReverbAudioProcessor::getStateInformation(juce::MemoryBlock &destData) {
    //Serializes your parameters, and any other potential data into an XML:

    juce::ValueTree params("Params");

    for (auto &param: getParameters()) {
        juce::ValueTree paramTree(getParamID(param));
        paramTree.setProperty("Value", param->getValue(), nullptr);
        params.appendChild(paramTree, nullptr);
    }

    juce::ValueTree pluginPreset("MyPlugin");
    pluginPreset.appendChild(params, nullptr);
    //This a good place to add any non-parameters to your preset

    copyXmlToBinary(*pluginPreset.createXml(), destData);
}

void ReverbAudioProcessor::setStateInformation(const void *data,
                                               int sizeInBytes) {
    //Loads your parameters, and any other potential data from an XML:

    auto xml = getXmlFromBinary(data, sizeInBytes);

    if (xml != nullptr) {
        auto preset = juce::ValueTree::fromXml(*xml);
        auto params = preset.getChildWithName("Params");

        for (auto &param: getParameters()) {
            auto paramTree = params.getChildWithName(getParamID(param));

            if (paramTree.isValid())
                param->setValueNotifyingHost(paramTree["Value"]);
        }
        //Load your non-parameter data now
    }
}

juce::AudioProcessorEditor *ReverbAudioProcessor::createEditor() {
    return nullptr;
}

void ReverbAudioProcessor::reset() {
    return this->_reverb.reset();
}

juce::AudioProcessor::BusesProperties ReverbAudioProcessor::getBuses() {
    const auto stereo = juce::AudioChannelSet::stereo();

    return BusesProperties()
            .withInput("Input", stereo, true)
            .withOutput("Output", stereo, true);
}

void ReverbAudioProcessor::updateReverb() {
    juce::Reverb::Parameters params{};

    params.roomSize = _roomSizeParam->get();
    params.damping = _dampingParam->get();
    params.wetLevel = _wetLevelParam->get();
    params.dryLevel = _dryLevelParam->get();
    params.width = _widthParam->get();
    params.freezeMode = _freezeModeParam->get();
    _reverb->setParameters(params);
}

juce::AudioProcessor *JUCE_CALLTYPE createPluginFilter() {
    return new ReverbAudioProcessor();
}

PluginProcessor.h

#pragma once

#include <juce_audio_processors/juce_audio_processors.h>
#include <juce_dsp/juce_dsp.h>

class ReverbAudioProcessor : public juce::AudioProcessor {
public:
    inline static const std::string NAME = "Reverb";

    ReverbAudioProcessor();

    void prepareToPlay(double /*sampleRate*/, int /*blockSize*/) override {}

    void reset() override;

    void releaseResources() override {}

    void updateReverb();

    bool isBusesLayoutSupported(const BusesLayout &) const override { return true; }

    void processBlock(juce::AudioBuffer<float> &, juce::MidiBuffer &) override;

    bool hasEditor() const override { return true; }

    juce::AudioProcessorEditor *createEditor() override;

    const juce::String getName() const override { return JucePlugin_Name; }

    bool acceptsMidi() const override { return true; }

    bool producesMidi() const override { return false; }

    bool isMidiEffect() const override { return false; }

    double getTailLengthSeconds() const override { return 0.0; }

    int getNumPrograms() override { return 1; }

    int getCurrentProgram() override { return 0; }

    void setCurrentProgram(int) override {}

    const juce::String getProgramName(int) override { return juce::String(); }

    void changeProgramName(int, const juce::String & /*newName*/) override {}

    void getStateInformation(juce::MemoryBlock &destData) override;

    void setStateInformation(const void *data, int sizeInBytes) override;

private:
    static BusesProperties getBuses();

    juce::AudioParameterFloat *_roomSizeParam;
    juce::AudioParameterFloat *_dampingParam;
    juce::AudioParameterFloat *_wetLevelParam;
    juce::AudioParameterFloat *_dryLevelParam;
    juce::AudioParameterFloat *_widthParam;
    juce::AudioParameterFloat *_freezeModeParam;

    std::unique_ptr<juce::dsp::Reverb> _reverb;
};

Are you using the same PLUGIN_CODE for all of your plug-ins? This should be unique for each plug-in.

Yes my codes were the same. I just changed them. The problem is still the same :confused:
Btw thank you for your time, I am very grateful that you try to help me.

Here is the output of ldd on my host:

	linux-vdso.so.1 (0xbec43000)
	/usr/lib/arm-linux-gnueabihf/libarmmem-${PLATFORM}.so => /usr/lib/arm-linux-gnueabihf/libarmmem-v7l.so (0xb6f56000)
	libPocoNet.so.93 => /usr/local/lib/libPocoNet.so.93 (0xb6e29000)
	libPocoUtil.so.93 => /usr/local/lib/libPocoUtil.so.93 (0xb6db5000)
	libPocoJSON.so.93 => /usr/local/lib/libPocoJSON.so.93 (0xb6d5a000)
	libPocoFoundation.so.93 => /usr/local/lib/libPocoFoundation.so.93 (0xb6b82000)
	libfreetype.so.6 => /lib/arm-linux-gnueabihf/libfreetype.so.6 (0xb6abe000)
	libasound.so.2 => /lib/arm-linux-gnueabihf/libasound.so.2 (0xb69db000)
	libatomic.so.1 => /lib/arm-linux-gnueabihf/libatomic.so.1 (0xb69c2000)
	libdl.so.2 => /lib/arm-linux-gnueabihf/libdl.so.2 (0xb69ae000)
	libpthread.so.0 => /lib/arm-linux-gnueabihf/libpthread.so.0 (0xb6982000)
	libstdc++.so.6 => /lib/arm-linux-gnueabihf/libstdc++.so.6 (0xb67fa000)
	libm.so.6 => /lib/arm-linux-gnueabihf/libm.so.6 (0xb678b000)
	libgcc_s.so.1 => /lib/arm-linux-gnueabihf/libgcc_s.so.1 (0xb675e000)
	libc.so.6 => /lib/arm-linux-gnueabihf/libc.so.6 (0xb660a000)
	libPocoXML.so.93 => /usr/local/lib/libPocoXML.so.93 (0xb6570000)
	librt.so.1 => /lib/arm-linux-gnueabihf/librt.so.1 (0xb6558000)
	libpng16.so.16 => /lib/arm-linux-gnueabihf/libpng16.so.16 (0xb6519000)
	libz.so.1 => /lib/arm-linux-gnueabihf/libz.so.1 (0xb64f1000)
	libbrotlidec.so.1 => /lib/arm-linux-gnueabihf/libbrotlidec.so.1 (0xb64d6000)
	/lib/ld-linux-armhf.so.3 (0xb6f6b000)
	libbrotlicommon.so.1 => /lib/arm-linux-gnueabihf/libbrotlicommon.so.1 (0xb64a5000)

I am not an expert so I may miss something ?

When I use the following command:

cat /proc/cpuinfo | grep 'model name'

The output is:

model name: ARMv7 Processor rev 3 (v7l)

but my plugins seem to be for armv6l-linux architectures.

I don’t really understand everything aha

Or could it be a problem with my vst3 c++ code, that is not made for 32bit systems ?

Any clue ?

Try setting the environment variable LD_DEBUG=all when running the host. You will see a ton of debug messages. So best to set a breakpoint just before your vst3 loads and one just after and see which messages were printed to the console on why the vst3 is failing to load.