Skip to content
This repository has been archived by the owner on Sep 6, 2019. It is now read-only.

VST3 only handles note on / note off events #126

Open
jkeller51 opened this issue Dec 6, 2018 · 6 comments
Open

VST3 only handles note on / note off events #126

jkeller51 opened this issue Dec 6, 2018 · 6 comments

Comments

@jkeller51
Copy link

In IPlugVST3.cpp, IPlugVST3::process(), the code does not handle any MIDI messages outside note-on/note-off. There is even a comment saying, "//process events.. only midi note on and note off?".

This is extremely limiting considering Steinberg has officially discontinued VST2 and you cannot acquire the VST 2 API from them, so all new plugins should be VST3.

When digging deeper I discovered that the IPlug core wasn't written to handle MIDI controllers from VST3. Generally these seem to be handled from a "controller" class that is separate from the "audio effect" class (enumerated as kVstAudioEffectClass and kVstComponentControllerClass). The "controller" class is not implemented in IPlug.

Apparently in VST3 the MIDI messages for pitch bend, control messages, etc must be mapped to a parameter with its own parameter ID, so some rewrites to IPlug are required to remedy this issue.

@jkeller51
Copy link
Author

I was able to make some rewrites to capture control messages. Here is the process (only Mod Wheel and Pitch Bend are implemented -- should be easy to see how to add other messages):

IPlugVST3.h

Modify class declare to inherit from IMidiMapping:

class IPlugVST3 : public IPlugBase, 
    public Steinberg::Vst::SingleComponentEffect, 
    public Steinberg::Vst::IMidiMapping

Under Steinberg::tresult PLUGIN_API process(Steinberg::Vst::ProcessData& data);
place:

virtual Steinberg::tresult PLUGIN_API getMidiControllerAssignment(Steinberg::int32 busIndex, Steinberg::int16 channel, Steinberg::Vst::CtrlNumber midiControllerNumber, Steinberg::Vst::ParamID& id/*out*/);

Under DEFINE_INTERFACES place DEF_INTERFACE(IMidiMapping) so the result looks like:

  OBJ_METHODS (IPlugVST3, SingleComponentEffect)
  DEFINE_INTERFACES
  DEF_INTERFACE(IMidiMapping)
  END_DEFINE_INTERFACES (SingleComponentEffect)
  REFCOUNT_METHODS(SingleComponentEffect)

Modify this enum as desired:

  enum
  {
    kBypassParam = 'bpas',
    kPresetParam = 'prst',
    kModWheelParam = 'modw',
//    kBreathParam = 'brth',
//    kCtrler3Param = 'ct03',
//    kExpressionParam = 'expr',
    kPitchBendParam = 'pitb'
//    kSustainParam = 'sust',
//    kAftertouchParam = 'aftt',
  };

IPlugVST3.cpp

Add getMidiControllerAssignment function (I put it above process()):

tresult PLUGIN_API IPlugVST3::getMidiControllerAssignment(int32 busIndex, int16 channel, CtrlNumber midiControllerNumber, ParamID& id/*out*/) {
	if (busIndex == 0) {
		id = -1;
		switch (midiControllerNumber) {
                case kPitchBend:
			id = kPitchBendParam;
			break;
		case kCtrlModWheel:
			id = kModWheelParam;
			break;
		}

		if (id == -1) {
			id = 0;
			return kResultFalse;
		}
		else
			return kResultTrue;
	}
}

This maps the incoming MIDI to a param.

Next, initialize the parameters under if(DoesMIDI()) in IPlugVST3::initialize():

          Parameter * param;
	  param = new RangeParameter(USTRING("MIDI Mod Wheel"), kModWheelParam, USTRING(""), 0.0, 1.0, 0.5); // outputs values [0.0, 1.0]
	  param->setPrecision(1);
	  parameters.addParameter(param);

	  param = new RangeParameter(USTRING("MIDI Pitch Bend"), kPitchBendParam, USTRING(""), 0.0, 1.0, 0.5);
	  param->setPrecision(1);
	  parameters.addParameter(param);

Next, find this case structure in IPlugVST3::process():

          int idx = paramQueue->getParameterId();
          switch (idx)
          {
            case kBypassParam:
            {
              bool bypassed = (value > 0.5);
              
              if (bypassed != mIsBypassed)
              {
                mIsBypassed = bypassed;
              }

              break;
            }
            case kPresetParam:
              RestorePreset(FromNormalizedParam(value, 0, NPresets(), 1.));
              break;
              //TODO pitch bend, modwheel etc
            default:
              if (idx >= 0 && idx < NParams())
              {
                GetParam(idx)->SetNormalized((double)value);
                if (GetGUI()) GetGUI()->SetParameterFromPlug(idx, (double)value, true);
                OnParamChange(idx);
              }
              break;
          }

Make it look like this:

          int idx = paramQueue->getParameterId();
		  IMidiMsg msg;
          switch (idx)
          {
            case kBypassParam:
            {
              bool bypassed = (value > 0.5);
              
              if (bypassed != mIsBypassed)
              {
                mIsBypassed = bypassed;
              }

              break;
            }
            case kPresetParam:
              RestorePreset(FromNormalizedParam(value, 0, NPresets(), 1.));
              break;
              //TODO pitch bend, modwheel etc
			  // Done. -Jon Keller
			case kPitchBendParam:
				msg.MakePitchWheelMsg(value*2.0-1.0); // convert unipolar to bipolar
				ProcessMidiMsg(&msg);
				break;
			case kModWheelParam:
				msg.MakeControlChangeMsg(IMidiMsg::EControlChangeMsg::kModWheel, value); // TODO: differentiate channels
				ProcessMidiMsg(&msg);
				break;
            default:
              if (idx >= 0 && idx < NParams())
              {
                GetParam(idx)->SetNormalized((double)value);
                if (GetGUI()) GetGUI()->SetParameterFromPlug(idx, (double)value, true);
                OnParamChange(idx);
              }
              break;
          }

Note that VST3 seems to interpret all control messages as being on the scale [0.0, 1.0], so if you want to scale it to [-1.0, 1.0] (like for pitch bend), you have to make sure to do that conversion in IPlugVST3::process() as I have done in the code above.

@olilarkin
Copy link
Owner

hi Jon,

Thanks for your mods.

This is all already done for iPlug 2 (which is currently on a private git repo). I agree, since VST2 is gone... iPlug VST3 support needs to be complete.

oli

@averagecoderguy
Copy link

averagecoderguy commented Dec 7, 2018 via email

@olilarkin
Copy link
Owner

hi James,

not really with the current WDL-OL, but i am working hard to release a new "iPlug2" soon, which will have more instructions/docs

@codecat
Copy link

codecat commented Dec 18, 2018

Is there any ETA for when iPlug 2 is going to be released? I'm very much looking forward to it! 👍

@olilarkin
Copy link
Owner

Before the end of the year!

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants