MIDI Program Change not handled correctly in some DAWs

Hello,

I’m developing a VST3 instrument. It’s a synthesizer with 64 presets. I made the plugin react on incoming MIDI Program Change messages in order to change presets, which works fine so far in most of my DAWs.

The correct way for a host to handle MIDI program changes would be to send a normalized value of 1.000 to the plugin when the host receives a program change event with program no. 63 (the highest possible value for a synth with 64 presets). On some DAWs I tested (Ableton Live, Bitwig, Samplitude ProX 8) this works as expected, my plugin selects a preset according to the incoming MIDI program number.

However, some other DAWs ignore the number of steps I set with my program list (64). Instead, they handle incoming MIDI program changes based on the (wrong) assumption that the number of steps is the maximum number of possible values for MIDI program change (128). As a result, these hosts convert incoming MIDI program change #63 events to a normalized value of 0.5000 instead of 1.0000. Therefore my plugin selects the wrong preset (preset #31 instead of preset #63). I’ve got 2 DAWs that act this way: the VST3PluginTestHost by Steinberg and Studio One Pro7 by Presonus. I have no idea why some hosts assume 128 to be the number of steps for program changes despite the fact that my plugin sets this value to 64, while there are other hosts that handle MIDI program change based on the correct value.

This probably is a problem of the host. But I want my plugin to detect the host’s behaviour on program changes automatically and set itself accordingly. A way to achieve this would be sending a MIDI program change event #63 to the host and then watch out which normalized value is passed to the plugin. If it’s 1.0000, nothing has to be done. If it’s 0.5000 however, the plugin has to multiply every incoming normalized value assigned to the program change parameter id by 2 to get the correct value.

I’d like to know if there’s a way to send MIDI events from my plugin to the host as if they came from an external controller. If yes, I would greatly appreciate if someone could tell me how. Thanks in advance.

Regards, Michael

P.S.: Only program changes are affected. All other parameters are handled correctly in any DAW.

Here’s the code which sets up the program list in the “initialize” method of my controller class:

programList = new ProgramList(L"PRESETS", id_ProgramList, kRootUnitId);
updateProgramList(); //populates the program list with exactly 64 entries, which is the no. of presets in my plugin
addProgramList(programList);
if (Parameter* listparam = programList->getParameter()) {
    listparam->getInfo().flags &= ~ParameterInfo::kCanAutomate; //kIsProgramChange is already set
    parameters.addParameter(listparam);
}
addUnit(new Unit(L"ROOT", kRootUnitId, kNoParentUnitId, id_ProgramList));

Am I doing something wrong here? The code works in any DAW (my plugin always reacts on program changes). However, not every DAW generates correct normalized values from incoming program change events. The correct normalized values would be 0.25000 for program #15, 0.50000 for program #31 , 0.75000 for program #47 and 1.00000 for program #63. In DAWs which do not handle this correctly the values are 0.12500 for program #15, 0.25000 for program #31, 0.37500 for program #47 and 0.50000 for program #63. To me this looks as if the DAW assumes a step count for program change of 127 instead of 63, which I set in the above code.

What could be the reason for this?

Regards, Michael

Update: I could not find a solution for this problem that works properly. The only solution was to implement 128 rather than 64 preset memories.

Regarding the problem in Studio One, I contacted Presonus. But as VSTPluginTestHost.exe also is affected, it’s also Steinberg’s turn to fix it.

Regards

Michael

We’ll have a look at the test host.
But I already checked the code in Cubase/Nuendo and it correctly checks the step count of the program list parameter.

I also now checked the test host, and there’s indeed a bug we will fix with the next SDK update.