Cubase Note Expression Tuning min/max value

I’m having trouble changing the min/max tuning amounts for note expressions within Cubase 14. When I load a new project and add my plugin, I can see these min/max values are set to 12 by default, but when I drag the vertical min/max sliders, they snap straight to 0 and cannot be changed.

When I drag these sliders, is Cubase expecting to communicate with my plugin? Is there some interface or method I need to implement or fix? How should a typical plugin respond to min/max tuning changes?

My INoteExpressionController implementation more or less looks the same as the tutorial here:

// https://steinbergmedia.github.io/vst3_dev_portal/pages/Technical+Documentation/Change+History/3.5.0/INoteExpressionController.html

Did you do the same than the example:

// for Tuning the convert functions are: plain = 240 * (norm - 0.5); norm = plain / 240 + 0.5;
// we want to support only +/- one octave
double kNormTuningOneOctave = 12.0 / 240.0;
info.valueDesc.minimum = 0.5 - kNormTuningOneOctave;
info.valueDesc.maximum = 0.5 + kNormTuningOneOctave;

in the tresult PLUGIN_API MyExampleController::getNoteExpressionInfo (int32 busIndex, int16 channel, int32 noteExpressionIndex,
NoteExpressionTypeInfo& info)
… function

Yes I use that exact same code

strange, did you try to compile the example: VST 3 Plug-in Examples - VST 3 Developer Portal? Note Expression Synth supports Tuning as Note expression…

Thanks for your suggestion. I just tried building the Note Expression Synth sample and running it inside Cubase. This sample appears not to have the problem I had where the tuning min/max ranges snap to zero, however I’ve identified several other problems.

OS: Windows 11
Cubase v14.0.10 Build 144 (x64)

By default the value next to label “Max. Value” reads -12, and for “Min. Value” it is 12. These labels are backwards.

When dragging the tuning ranges, they always snap to the values -12/-11/-7/-2/0/2/7/11/12. If I understand the source code for the Note Expression Synth sample correctly, the tuning range for the plugin is binary, supporting either 1 tone or 1 octave.
This is set up using this parameter here:

// note_expression_synth_controller.cpp
auto* tuningRangeParam = new StringListParameter (USTRING("Tuning Range"), kParamTuningRange, nullptr, ParameterInfo::kIsList);
tuningRangeParam->appendString (USTRING("[-1, +1] Octave"));
tuningRangeParam->appendString (USTRING("[-3, +2] Tunes"));
parameters.addParameter (tuningRangeParam);

Am I correct in my understanding?

Please help me to understand how the change the tuning range like this, and customize them, and please help me to understand how Cubase expects to interact with VST3s when a user changes these ranges.

Do I need to set up an associatedParameterId within ::getNoteExpressionInfo()? Do I need to implement the INoteExpressionPhysicalUIMapping class?

I cannot find anything in the documentation mentioning how to control these ranges, and sample source code is a bit of a wild goose chase through several files and classes, making it hard to understand the logic flow for setting up parameters what each of their requirements are.

Ultimately what I need in my plugin is for our users to have the ability to set a small ranges like 2 semitones, or larger ones like 12 semitones, or even 120. How can I do this?

when changing the range parameter of the min /max detune the host will be informed about this new range (.valueDesc.minimum. maximun of NoteExpressionTypeInfo has to be updated): check public.sdk\samples\vst\note_expression_synth\source\note_expression_synth_controller.cpp

//------------------------------------------------------------------------
tresult PLUGIN_API Controller::setParamNormalized (ParamID tag, ParamValue value)
{
bool newRange = false;
if (tag == kParamTuningRange && getParamNormalized (tag) != value)
{..
..
net->getInfo ().valueDesc.minimum = 0.5 - 3 * VoiceStatics::kNormTuningOneTune;
net->getInfo ().valueDesc.maximum = 0.5 + 2 * VoiceStatics::kNormTuningOneTune;

componentHandler->restartComponent (kNoteExpressionChanged);

You do not need to use INoteExpressionPhysicalUIMapping or associatedParameterId

I managed to get things working on my end, and it was a bug on my end. Thanks for your patience Yvan and sorry for wasting your time.

Specifically my bug was incorrect use of swscanf() inside the function ::getNoteExpressionValueByString(). I used L"%f" as my format string and expected to store the value in a double, when I should have used L"%lf".

Cubase is also showing Min & Max in the correct order now. All the problems I described before may have been a strange side effect of the bug in my plugin.

Now when I set valueDesc.minimum = 0.0 and valueDesc.maximum = 1.0, I can use the Min & Max range to whatever I like between +/- 120. Very cool!

Thanks again.