Here is some code that works (but not 100% tested) :
diff --git a/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp b/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp
index 247f2b3..48487ea 100644
--- a/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp
+++ b/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp
@@ -98,6 +98,7 @@ private:
//==============================================================================
class JuceVST3EditController : public Vst::EditController,
public Vst::IMidiMapping,
+ public Vst::IUnitInfo,
public AudioProcessorListener
{
public:
@@ -130,6 +131,7 @@ public:
TEST_FOR_AND_RETURN_IF_VALID (targetIID, Vst::IEditController2)
TEST_FOR_AND_RETURN_IF_VALID (targetIID, Vst::IConnectionPoint)
TEST_FOR_AND_RETURN_IF_VALID (targetIID, Vst::IMidiMapping)
+ TEST_FOR_AND_RETURN_IF_VALID (targetIID, Vst::IUnitInfo)
TEST_FOR_COMMON_BASE_AND_RETURN_IF_VALID (targetIID, IPluginBase, Vst::IEditController)
TEST_FOR_COMMON_BASE_AND_RETURN_IF_VALID (targetIID, IDependent, Vst::IEditController)
TEST_FOR_COMMON_BASE_AND_RETURN_IF_VALID (targetIID, FUnknown, Vst::IEditController)
@@ -146,6 +148,69 @@ public:
}
//==============================================================================
+ Steinberg::int32 PLUGIN_API getUnitCount () override { return 1; }
+ tresult PLUGIN_API getUnitInfo (Steinberg::int32 unitIndex, Vst::UnitInfo& info /*out*/) override
+ {
+ if (unitIndex == 0)
+ {
+ info.id = Vst::kRootUnitId;
+ info.parentUnitId = Vst::kNoParentUnitId;
+ info.programListId = 'prst';
+
+ toString128 (info.name, TRANS("Root Unit"));
+
+ return kResultTrue;
+ }
+
+ zerostruct (info);
+ return kResultFalse;
+ }
+ Steinberg::int32 PLUGIN_API getProgramListCount () override
+ {
+ AudioProcessor* const pluginInstance = getPluginInstance();
+ if (pluginInstance && pluginInstance->getNumPrograms() > 0)
+ return 1;
+ else
+ return 0;
+ }
+ tresult PLUGIN_API getProgramListInfo (Steinberg::int32 listIndex, Vst::ProgramListInfo& info /*out*/) override
+ {
+ if (listIndex == 0)
+ {
+ info.id = 'prst';
+ info.programCount = (Steinberg::int32) getPluginInstance()->getNumPrograms();
+
+ toString128 (info.name, TRANS("Factory Presets"));
+
+ return kResultTrue;
+ }
+
+ jassertfalse;
+ zerostruct (info);
+ return kResultFalse;
+ }
+ tresult PLUGIN_API getProgramName (Vst::ProgramListID listId, Steinberg::int32 programIndex, Vst::String128 name /*out*/) override
+ {
+ if (listId == 'prst'
+ && isPositiveAndBelow ((int) programIndex, getPluginInstance()->getNumPrograms()))
+ {
+ toString128 (name, getPluginInstance()->getProgramName ((int) programIndex));
+ return kResultTrue;
+ }
+
+ jassertfalse;
+ toString128 (name, juce::String());
+ return kResultFalse;
+ }
+ tresult PLUGIN_API getProgramInfo (Vst::ProgramListID listId, Steinberg::int32 programIndex, Vst::CString attributeId /*in*/, Vst::String128 attributeValue /*out*/) override { return kNotImplemented; }
+ tresult PLUGIN_API hasProgramPitchNames (Vst::ProgramListID listId, Steinberg::int32 programIndex) override { return kNotImplemented; }
+ tresult PLUGIN_API getProgramPitchName (Vst::ProgramListID listId, Steinberg::int32 programIndex, Steinberg::int16 midiPitch, Vst::String128 name /*out*/) override { return kNotImplemented; }
+ Vst::UnitID PLUGIN_API getSelectedUnit () override { return kNotImplemented; }
+ tresult PLUGIN_API selectUnit (Vst::UnitID unitId) override { return kNotImplemented; }
+ tresult PLUGIN_API getUnitByBus (Vst::MediaType type, Vst::BusDirection dir, Steinberg::int32 busIndex, Steinberg::int32 channel, Vst::UnitID& unitId /*out*/) override { return kNotImplemented; }
+ tresult PLUGIN_API setUnitProgramData (Steinberg::int32 listOrUnitId, Steinberg::int32 programIndex, IBStream* data) override { return kNotImplemented; }
+
+ //==============================================================================
tresult PLUGIN_API initialize (FUnknown* context) override
{
if (hostContext != context)
@@ -242,6 +307,43 @@ public:
};
//==============================================================================
+ struct PresetParam : public Vst::StringListParameter
+ {
+ PresetParam (AudioProcessor& p, int index)
+ : Vst::StringListParameter(::toString(String("Factory Presets")), index, 0, Vst::ParameterInfo::kCanAutomate|Vst::ParameterInfo::kIsList|Vst::ParameterInfo::kIsProgramChange, Vst::kRootUnitId)
+ , owner (p)
+ {
+ for (int i = 0; i < owner.getNumPrograms(); ++i)
+ {
+ Vst::String128 str;
+ toString128(str, owner.getProgramName(i));
+ this->appendString(str);
+ }
+ }
+
+ virtual ~PresetParam() {}
+
+ bool setNormalized (Vst::ParamValue v) override
+ {
+ v = jlimit (0.0, 1.0, v);
+
+ if (v != valueNormalized)
+ {
+ valueNormalized = v;
+ changed();
+ owner.setCurrentProgram(v * (owner.getNumPrograms() - 1));
+ return true;
+ }
+
+ return false;
+ }
+ private:
+ AudioProcessor& owner;
+
+ JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PresetParam)
+ };
+
+ //==============================================================================
struct BypassParam : public Vst::Parameter
{
BypassParam (AudioProcessor& p, int index) : owner (p), paramIndex (index)
@@ -474,6 +576,9 @@ private:
parameters.addParameter (new Param (*pluginInstance, i));
parameters.addParameter (new BypassParam (*pluginInstance, numParameters));
+
+ if (pluginInstance->getNumPrograms() > 0)
+ parameters.addParameter (new PresetParam (*pluginInstance, numParameters + 1));
}
// We need to account for the bypass parameter in the numParameters passed to
@@ -741,7 +846,6 @@ private:
//==============================================================================
class JuceVST3Component : public Vst::IComponent,
public Vst::IAudioProcessor,
- public Vst::IUnitInfo,
public Vst::IConnectionPoint,
public AudioPlayHead
{
@@ -795,7 +899,6 @@ public:
TEST_FOR_AND_RETURN_IF_VALID (targetIID, JuceVST3Component)
TEST_FOR_AND_RETURN_IF_VALID (targetIID, Vst::IComponent)
TEST_FOR_AND_RETURN_IF_VALID (targetIID, Vst::IAudioProcessor)
- TEST_FOR_AND_RETURN_IF_VALID (targetIID, Vst::IUnitInfo)
TEST_FOR_AND_RETURN_IF_VALID (targetIID, Vst::IConnectionPoint)
TEST_FOR_COMMON_BASE_AND_RETURN_IF_VALID (targetIID, FUnknown, Vst::IComponent)
@@ -1249,83 +1352,6 @@ public:
}
//==============================================================================
- Steinberg::int32 PLUGIN_API getUnitCount() override
- {
- return 1;
- }
-
- tresult PLUGIN_API getUnitInfo (Steinberg::int32 unitIndex, Vst::UnitInfo& info) override
- {
- if (unitIndex == 0)
- {
- info.id = Vst::kRootUnitId;
- info.parentUnitId = Vst::kNoParentUnitId;
- info.programListId = Vst::kNoProgramListId;
-
- toString128 (info.name, TRANS("Root Unit"));
-
- return kResultTrue;
- }
-
- zerostruct (info);
- return kResultFalse;
- }
-
- Steinberg::int32 PLUGIN_API getProgramListCount() override
- {
- if (getPluginInstance().getNumPrograms() > 0)
- return 1;
-
- return 0;
- }
-
- tresult PLUGIN_API getProgramListInfo (Steinberg::int32 listIndex, Vst::ProgramListInfo& info) override
- {
- if (listIndex == 0)
- {
- info.id = paramPreset;
- info.programCount = (Steinberg::int32) getPluginInstance().getNumPrograms();
-
- toString128 (info.name, TRANS("Factory Presets"));
-
- return kResultTrue;
- }
-
- jassertfalse;
- zerostruct (info);
- return kResultFalse;
- }
-
- tresult PLUGIN_API getProgramName (Vst::ProgramListID listId, Steinberg::int32 programIndex, Vst::String128 name) override
- {
- if (listId == paramPreset
- && isPositiveAndBelow ((int) programIndex, getPluginInstance().getNumPrograms()))
- {
- toString128 (name, getPluginInstance().getProgramName ((int) programIndex));
- return kResultTrue;
- }
-
- jassertfalse;
- toString128 (name, juce::String());
- return kResultFalse;
- }
-
- tresult PLUGIN_API getProgramInfo (Vst::ProgramListID, Steinberg::int32, Vst::CString, Vst::String128) override { return kNotImplemented; }
- tresult PLUGIN_API hasProgramPitchNames (Vst::ProgramListID, Steinberg::int32) override { return kNotImplemented; }
- tresult PLUGIN_API getProgramPitchName (Vst::ProgramListID, Steinberg::int32, Steinberg::int16, Vst::String128) override { return kNotImplemented; }
- tresult PLUGIN_API selectUnit (Vst::UnitID) override { return kNotImplemented; }
- tresult PLUGIN_API setUnitProgramData (Steinberg::int32, Steinberg::int32, IBStream*) override { return kNotImplemented; }
- Vst::UnitID PLUGIN_API getSelectedUnit() override { return Vst::kRootUnitId; }
-
- tresult PLUGIN_API getUnitByBus (Vst::MediaType, Vst::BusDirection,
- Steinberg::int32, Steinberg::int32,
- Vst::UnitID& unitId) override
- {
- zerostruct (unitId);
- return kNotImplemented;
- }
-
- //==============================================================================
bool getCurrentPosition (CurrentPositionInfo& info) override
{
info.timeInSamples = jmax ((juce::int64) 0, processContext.projectTimeSamples);