VST3 Validator failing with error: IUnitInfo interface is missing. AudioProcessorParameter group issue?

Hi All,

Writing a plugin using the AudioProcessorValueTreeState.

Basically our architecture is:

OuterShell

|- BlockFoo

|- BlockBar

When I run the VST3 validator that comes with the sdk, I am having a constant failure on the Scan Parameters test. The output is as follows:

[Scan Parameters]

Info: ===Scan Parameters ====================================

Info: This component exports 8 parameter(s)

Info: Param 000 (ID = 1473324883): [title="Show Info Pane"] [unit=""] [type = T, default = 1.000000, unit = 696685926]

ERROR: IUnitInfo interface is missing, but ParameterInfo::unitID is not 000 (kRootUnitId).

[XXXXXXX Failed]

So first off, I have confirmed that if I hardcode the unit to 0 (by changing the return value in juce_VST3_Wrapper.cpp::JuceAudioProcessor::getUnitID to be 0 always) the test passes. So that is good to go. This means that the second half of the error code is correct, that if the ParameterInfo::unitID is 000/kRootUnitID everything is fine.

The issue is that when we collect all the parameter groups from each of BlockFoo and BlockBar, and throw them together to pass to the OuterShell AudioProcessorValueTreeState, something is going wrong. The group is set, so the unit number is non zero, and additionally the IUnitInfo interface is missing.

I’m not exactly sure what that means, that that interface is missing. Is it an error on our side or in the JUCE VST3 hooks?

Here’s some code from the project.

We are building the processor blocks parameter groups like so:

std::unique_ptr<AudioProcessorParameterGroup> FooBlock::createParameterLayout()
{
    auto group = std::make_unique<AudioProcessorParameterGroup>("fooProcessBlockID", "fooProcessBlockName", " | ");

    group->addChild(std::make_unique<AudioParameterFloat>(kFoo1Id.toString(), "Foo 1", cControlFooRoomMin, cControlFooRoomMax, cControlFooDefault));
    group->addChild(std::make_unique<AudioParameterFloat>(kFoo2Id.toString(), "Foo 2", cControlFooRoomMin, cControlFooRoomMax, cControlFooDefault));
    group->addChild(std::make_unique<AudioParameterFloat>(kFoo3Id.toString(), "Foo 3", cControlFooMin, cControlFooMax, cControlFooDefault));

    for (auto param : group->getParameters(false))
    {
        param->addListener(this);
    }

    return group;
}

and assembling like this in OuterShell:

OuterPluginProcessor::OuterPluginProcessor(int x, int y)
{
    // Collect parameters from all the submodules
    std::vector<std::unique_ptr<AudioProcessorParameterGroup> > layoutList;
    std::vector<std::unique_ptr<AudioProcessorParameterGroup> > layoutList;
    {
        layoutList.push_back(OuterParameterManager::createParameterLayout());

        for (auto& submodule : submodules)
        {
            layoutList.push_back(submodule->createParameterLayout());
        }
    }

    AudioProcessorValueTreeState::ParameterLayout finalLayout;

    for (auto& subLayout : layoutList)
    {
        finalLayout.add(std::move(subLayout));
    }

    mVTS = std::make_unique<AudioProcessorValueTreeState>(
            *this,
            nullptr,
            kVTS_ID,
            std::move(finalLayout)
        );

    // Initialize our processing nodes.
    for (auto& submodule : submodules)
    {
        submodule->SetValueTreeState(mVTS.get());
    }

    mGraph->initializeGraph(submodules);
}

Does anyone have any experience building plugins using the audioValueTreeState like this? Is there something obvious that I’m missing?

Thank you!

Have you tried using the develop branch?

This commit from a few weeks ago might sort things out:

1 Like

Beautiful! That works perfectly. Thanks so much for the response.