Run time set for Instrument or FX Plugin

Please, could you add an option so I could set during run time if my plugin is an instrument or FX? I ask this so I wouldn’t need to compile yet another pair (32/64 bits) set of files… I’m also trying to figure a way to set a MultiOutput version during run time :wink:

I posted everything in the following thread:

Thank you.

I don’t understand how that would work… For you plugin to know at run time whether it is an instrument or an effect, the plugin host must load it, but the plugin host needs to know the type of the plugin before loading it, right?

As I said, the filename would have information. EG: Wusik Station V9 is a regular instrument, Wusik Station V9 FX is the FX version.

Edit: I explained my ideas in the other thread :wink:

Well, you proposed macros, but they are resolved at compilation time, so there is nothing that could be changed once the compilation happened, especially not at runtime.

To identify a plugin, usually a triplet of 4 characters each are used, to identify the plugin. The filename of the component or .vst3 doesn’t matter at all. For AU, one of the identifiers is for the type of the plugin, e.g. aufx for effect, augn for generators or aumi for instrument. This will define, where the host will list the plugin (e.g. as insert or as instrument).

So I guess, with how current hosts and SDKs are working, you will have to compile one version for each supported case…

EDIT: some companies have created wrappers, like WaveShell, so there might be a way to pack more than one plugin into one component / vst… I don’t know, how that works internally though…

VST3 allows multiple plugins in the same binary. You’d have to hack the VST3 wrapping code to get that working, since right now plugin projects assume that there is only one plugin is supposed to be wrapped.

Although I have no idea how well that particular feature is supported in hosts.

Ok, let’s go by parts. Instrument x FX version. The VST_Wrapper code has this:

   #if JucePlugin_IsSynth
    vstEffect.flags |= Vst2::effFlagsIsSynth;
   #else
    if (processor->getTailLengthSeconds() == 0.0)
        vstEffect.flags |= Vst2::effFlagsNoSoundInStop;
   #endif

But, what it JUCE had an option of run-time too? That way I could put some code that checks the filename for FX in the name and enable FX mode instead of instrument mode.

I can MOD that file myself and it should work, unless I’m overseen something. But I would need to MOD every single new JUCE release. A PITA…

Merging with git usually works well or well enough. I pull and merge from the Juce develop branch almost daily and I have rarely got any massive merge conflicts because of my modified Juce bits. They are not likely to change the VST2 wrapper code much anymore in the future anyway.

Ah, stupid me, never used that feature. Thanks. :slight_smile: something to learn now. :wink:

Ok, this seems to work. But in Reaper it still shows as an Instrument. I will try to clear the caches and also test in other hosts. On Wusik P2000 it does see a file with FX in the name as an Effect.

	JuceVSTWrapper(Vst2::audioMasterCallback cb, AudioProcessor* af)
		: hostCallback(cb),
		processor(af)
	{
		inParameterChangedCallback = false;
		bool isInstrument = !File::getSpecialLocation(File::SpecialLocationType::currentApplicationFile).getFileNameWithoutExtension().containsIgnoreCase(" fx ");

		// VST-2 does not support disabling buses: so always enable all of them
		processor->enableAllBuses();

		findMaxTotalChannels(maxNumInChannels, maxNumOutChannels);

		// You must at least have some channels
		jassert(processor->isMidiEffect() || (maxNumInChannels > 0 || maxNumOutChannels > 0));

		if (processor->isMidiEffect())
			maxNumInChannels = maxNumOutChannels = 2;

#ifdef JucePlugin_PreferredChannelConfigurations
		processor->setPlayConfigDetails(maxNumInChannels, maxNumOutChannels, 44100.0, 1024);
#endif

		processor->setRateAndBufferSizeDetails(0, 0);
		processor->setPlayHead(this);
		processor->addListener(this);

		if (auto* juceParam = processor->getBypassParameter())
			juceParam->addListener(this);

		juceParameters.update(*processor, false);

		memset(&vstEffect, 0, sizeof(vstEffect));
		vstEffect.magic = 0x56737450 /* 'VstP' */;
		vstEffect.dispatcher = (Vst2::AEffectDispatcherProc) dispatcherCB;
		vstEffect.process = nullptr;
		vstEffect.setParameter = (Vst2::AEffectSetParameterProc) setParameterCB;
		vstEffect.getParameter = (Vst2::AEffectGetParameterProc) getParameterCB;
		vstEffect.numPrograms = jmax(1, af->getNumPrograms());
		vstEffect.numParams = juceParameters.getNumParameters();
		vstEffect.numInputs = maxNumInChannels;
		vstEffect.numOutputs = maxNumOutChannels;
		vstEffect.initialDelay = processor->getLatencySamples();
		vstEffect.object = this;
		//
		if (isInstrument) vstEffect.uniqueID = JucePlugin_VSTUniqueID;
		else vstEffect.uniqueID = JucePlugin_VSTUniqueID_FX;

#ifdef JucePlugin_VSTChunkStructureVersion
		vstEffect.version = JucePlugin_VSTChunkStructureVersion;
#else
		vstEffect.version = JucePlugin_VersionCode;
#endif

		vstEffect.processReplacing = (Vst2::AEffectProcessProc) processReplacingCB;
		vstEffect.processDoubleReplacing = (Vst2::AEffectProcessDoubleProc) processDoubleReplacingCB;

		vstEffect.flags |= Vst2::effFlagsHasEditor;

		vstEffect.flags |= Vst2::effFlagsCanReplacing;
		if (processor->supportsDoublePrecisionProcessing())
			vstEffect.flags |= Vst2::effFlagsCanDoubleReplacing;

		vstEffect.flags |= Vst2::effFlagsProgramChunks;


		if (isInstrument)
		{
			vstEffect.flags |= Vst2::effFlagsIsSynth; 
		}
		else
		{
			if (processor->getTailLengthSeconds() == 0.0)
				vstEffect.flags |= Vst2::effFlagsNoSoundInStop;
		}

        activePlugins.add (this);
    }

OK, this works great. I will do the VST3 now. I checked AU (OSX) but could not find anything. Doesn’t AU allow effects? Where is that set? I couldn’t find anything by searching the juce modules folder.

The AU has the four letter types, aufx as default. In the code it is named kAudioUnitType_Effect.
I followed the symbol, and it ends up in this struct:
macOS 10.13/Frameworks/AudioToolbox/AudioComponent.h:

/*!
    @struct         AudioComponentDescription
    @discussion     A structure used to describe the unique and identifying IDs of an audio component 
    @field          componentType
                        A unique 4-byte code identifying the generic type of an audio component
    @field          componentSubType
                        the particular flavor of this instance
    @field          componentManufacturer
                        vendor identification
    @field          componentFlags
                        must be set to zero unless a known specific value is requested
    @field          componentFlagsMask
                        must be set to zero unless a known specific value is requested
*/
#pragma pack(push, 4)
typedef struct AudioComponentDescription {
    OSType              componentType;
    OSType              componentSubType;
    OSType              componentManufacturer;
    UInt32              componentFlags;
    UInt32              componentFlagsMask;
} AudioComponentDescription;
#pragma pack(pop)

But I don’t know enough about the linking/loading of plugins mechanism to be of further help I’m afraid…

I found the following on the file juce_AU_Wrapper.mm

bool BusCountWritable (AudioUnitScope scope) override
{
   #ifdef JucePlugin_PreferredChannelConfigurations
    ignoreUnused (scope);

    return false;
   #else
    bool isInput;

    if (scopeToDirection (scope, isInput) != noErr)
        return false;

   #if JucePlugin_IsMidiEffect
    return false;
   #elif JucePlugin_IsSynth
    if (isInput) return false;
   #endif

    const int busCount = AudioUnitHelpers::getBusCount (juceFilter.get(), isInput);
    return (juceFilter->canAddBus (isInput) || (busCount > 0 && juceFilter->canRemoveBus (isInput)));
   #endif
}

So far here’s my VST3 code:

JUCE_EXPORTED_FUNCTION IPluginFactory* PLUGIN_API GetPluginFactory()
{
    PluginHostType::jucePlugInClientCurrentWrapperType = AudioProcessor::wrapperType_VST3;

   #if JUCE_WINDOWS
    // Cunning trick to force this function to be exported. Life's too short to
    // faff around creating .def files for this kind of thing.
    #pragma comment(linker, "/EXPORT:" __FUNCTION__ "=" __FUNCDNAME__)
   #endif

    if (globalFactory == nullptr)
    {
        globalFactory = new JucePluginFactory();

        static const PClassInfo2 componentClass (JuceVST3Component::iid,
                                                 PClassInfo::kManyInstances,
                                                 kVstAudioEffectClass,
												 File::getSpecialLocation(File::SpecialLocationType::currentApplicationFile).getFileNameWithoutExtension().replace(" 32", "").replace(" 64", ""),
												 File::getSpecialLocation(File::SpecialLocationType::currentApplicationFile).getFileNameWithoutExtension().containsIgnoreCase(" fx ") ? 0 : Vst::kSimpleModeSupported,                                                
												 File::getSpecialLocation(File::SpecialLocationType::currentApplicationFile).getFileNameWithoutExtension().containsIgnoreCase(" fx ") ? Vst::PlugType::kFx : Vst::PlugType::kInstrumentSynth,
                                                 JucePlugin_Manufacturer,
                                                 JucePlugin_VersionString,
                                                 kVstVersionString);
		 
        globalFactory->registerClass (componentClass, createComponentInstance);

        static const PClassInfo2 controllerClass (JuceVST3EditController::iid,
                                                  PClassInfo::kManyInstances,
                                                  kVstComponentControllerClass,
												  File::getSpecialLocation(File::SpecialLocationType::currentApplicationFile).getFileNameWithoutExtension().replace(" 32", "").replace(" 64", ""),
												  File::getSpecialLocation(File::SpecialLocationType::currentApplicationFile).getFileNameWithoutExtension().containsIgnoreCase(" fx ") ? 0 : Vst::kSimpleModeSupported,
												  File::getSpecialLocation(File::SpecialLocationType::currentApplicationFile).getFileNameWithoutExtension().containsIgnoreCase(" fx ") ? Vst::PlugType::kFx : Vst::PlugType::kInstrumentSynth,
                                                  JucePlugin_Manufacturer,
                                                  JucePlugin_VersionString,
                                                  kVstVersionString);

        globalFactory->registerClass (controllerClass, createControllerInstance);
    }
    else
    {
        globalFactory->addRef();
    }

    return dynamic_cast<IPluginFactory*> (globalFactory);
}

Now I need to understand how to handle the number of outputs in runtime… I just need to be able to set 1 stereo out OR 16 stereo outputs.

I’m testing
JucePlugin_PreferredChannelConfigurations {2, 32}

To see if maybe that would be the easiest way.

Well, I just did the following and it works. :-o

#define JucePlugin_PreferredChannelConfigurations {2, File::getSpecialLocation(File::SpecialLocationType::currentApplicationFile).getFileNameWithoutExtension().containsIgnoreCase(" multiout ") ? 32 : 2}

Nice, now, from 6 file compilations (that takes several minutes to process) I was able to go down to 1 compilation per product. NICE! Why 6? I also did something for the Full x Demonstration files, but that I can’t share. :wink: And them for each the Regular Stereo Out, Multi Out and FX version.

Now I need to test on OSX… gahh…

Took me a while to setup my new computer, but I’m back. :wink: Here’s the latest code to setup the extra channels for the MultiOut mode. Seems to be working with SONAR, will try to test with other hosts too.

#define WCODE(wmultiout, wfx, wnormal) File::getSpecialLocation(File::SpecialLocationType::currentApplicationFile).getFileNameWithoutExtension().containsIgnoreCase(" multiout ") ? wmultiout : File::getSpecialLocation(File::SpecialLocationType::currentApplicationFile).getFileNameWithoutExtension().containsIgnoreCase(" fx ") ? wfx : wnormal
#define WNAME(wmultiout, wfx, wnormal) File::getSpecialLocation(File::SpecialLocationType::currentApplicationFile).getFileNameWithoutExtension().containsIgnoreCase(" multiout ") ? wmultiout : File::getSpecialLocation(File::SpecialLocationType::currentApplicationFile).getFileNameWithoutExtension().containsIgnoreCase(" fx ") ? wfx : wnormal
#define JucePlugin_PreferredChannelConfigurations {2, File::getSpecialLocation(File::SpecialLocationType::currentApplicationFile).getFileNameWithoutExtension().containsIgnoreCase(" multiout ") ? 32 : 2}
#define JucePlugin_MaxNumOutputChannels File::getSpecialLocation(File::SpecialLocationType::currentApplicationFile).getFileNameWithoutExtension().containsIgnoreCase(" multiout ") ? 32 : 2

Here’s an example of using it. (EVE doesn’t have a MultiOut and FX versions, do I didn’t create unique IDs)

    	#define JucePlugin_PluginCode WCODE('nb0m', 'nb0x', 'nb0w')
	#define SYNTH_NAME WNAME("Wusik EVE V5 MultiOut", "Wusik EVE V5 FX", "Wusik EVE V5")

But I found a problem with the StandAlone version. It will reset to zero inputs and outputs unless I out this:

		audioChannelInput = AudioChannelSet::stereo();
		audioChannelOutput = AudioChannelSet::stereo();
		//
		setChannelLayoutOfBus(true, 0, audioChannelInput);
		setChannelLayoutOfBus(false, 0, audioChannelOutput);