markusschloesser / MackieC4_P3

A Mackie C4 Midi Remote Script for Ableton 11
20 stars 2 forks source link

handle vpot_pressed + vpot_rotation for bigger steps #83

Closed markusschloesser closed 2 years ago

markusschloesser commented 3 years ago

I know this from other types of encoders, where when you press the encoder and turn it, the steps are bigger (Traktor with the controllers for DJing do this). I think usability would be improved if we implemented this, question would be how big the steps? possible implementations:

  1. min<>max
  2. steps of 8 (cos of 7bits)
  3. fancy: calculate the full resolution of the mapped param and divide that by 10

My suggestion would be 1.

BilldarBagdar commented 3 years ago

Man, it has been my experience that one quick twist of an encoder can take the value all the way like that. If you turn slowly, the value changes in small amounts, down to a step at a time; and if you turn swiftly, the value changes in large amounts quickly, up to the top or down to the zero. Do you not have that same experience? My problem has always been finding a "sweet speed" to get smooth smaller medium sized value changes. (fast enough to not be "small increments" and slow enough to not be "large increments")

Or is that what you are talking about? Some way to always get precisely spaced/sized increments like 8 or 16, instead of a variable "turn speed based" increment?

The below const ranges were empirically derived. (I watched the midi data in midi-ox while I turned the encoders) The faster the encoder is turned, the larger the value in these ranges gets sent out from the C4 for each "click" you feel as you turn an encoder. (Only 12 "clicks" at "click size 10" to go from 0 to 127 on the parameter in Live, but 128 "clicks" at "click size 1")

encoder_cw_values = range(0x01, 0x10)  # larger values means knob is turning faster / bigger CW increments
encoder_ccw_values = range(0x41, 0x50)  # larger values means knob is turning faster / bigger CCW increments

I suspect the above means, to always get, say, a step size of 8 - you would need to somehow tell Live to ignore the value actually sent from the C4 and substitute 8 or whatever as the value of the CC message. I'm not sure we have "control" in that area, especially not "temporary, is shift pressed?" override control, but you know the LOM more than I.

I think the below code is only related to the associated LED ring display, but there is a feedback relationship between the "click size" (how fast the encoder is turning) and the LED ring illumination sequence (how fast the ring fills up to full enlightened, for example)

display_mode_cc_base = encoder_ring_led_mode_cc_values[self.__v_pot_display_mode][0]
range_end = encoder_ring_led_mode_cc_values[self.__v_pot_display_mode][1] - display_mode_cc_base
feedback_rule.cc_value_map = tuple([display_mode_cc_base + x for x in range(range_end)])
markusschloesser commented 3 years ago

I do like the way it is implemented a lot! But some plugins seem to ignore certain logic, plus I always liked that functionality on Traktor. So absolutely low prio but "min to max" with one step when vpot pressed pressed (or with the now working modifier buttons) would still be nice imho. And I'm not saying to override that function but to do it similarly to the quantized approach

BilldarBagdar commented 3 years ago

The main problem with trying stuff like that is not starting a feedback loop, I think. I mean, say the script "knows" Alt is pressed, and that means jump to full. So when the script receives midi CC event info indicating an encoder turned, it needs to send midi CC info back to Live to say "boost to full" for that parameter (or whatever), and subsequently it needs to ignore (not be triggered again) by any midi feedback resulting from the jump and coming back from Live. (You are already essentially "looping back once". If you can't catch yourself coming back around the first time, it becomes a runaway feedback loop.)

markusschloesser commented 3 years ago

there are lots of vst3 plugins which do not expose parameters the proper way (Repro, Mono/poly e.g.) and for those where there should be discrete steps (switches for example), a lot of the times the exposed params are continuous, which is a pita. So for that use case (beside nagging developers to properly expose params), this enhancement would be helpful.

As for feedback loops: I don't think that's a problem when implemented like quantized param changes (while we're at it, why does the quantization not work for those?). What I mean is, let Live handle the logic and only then send the feedback. So "when alt pressed" and "vpot rotate" > "range(param min, param max)".

BilldarBagdar commented 3 years ago

Do any of those VSTs expose any control parameters by an "improper way"? How are a lot of "continuous parameters" exposed? Improperly? Are you talking about CC? (midi continuous controller messages?) Are you talking about "endless" parameters that "never" top out or bottom out, just keep incrementing or decrementing by some fixed value?

I understand how it would be nice to be able to set-and-forget some fixed value to increment for each "encoder click" and then not have to worry about your "encoder turn speed". "Superior Drummer 2.0" only properly exposes "device on/off". I haven't looked into whether or not it has any alternate "improper ways" exposed and/or what control any exposure offers.

Have you heard of a VST called Unify? It can be described as a vst "host instrument". I don't know if that's a real category, but Unify can load other instruments and effects "inside Unify". (disclosure, I am a PluginGuru.com customer several times over and I "follow" the YouTube channel) I only mention it here because Unify by default exposes 32 "proper parameters" (Unify calls them "macro knobs") to Live, and then "inside Unify" you can map as many (inner device and/or FX) parameters to one (or more) exposed "macro knobs" as you wish. Plus you can "draw" the parameter "value curve" in any (reasonable) shape (such as a fixed stair-step quantized curve). I haven't done any focused research to back up these words, but if some VST device is programmed with parameters that can in any way be automated, then Unify exposes them for "easy mapping" to the "macro knobs" (which are in turn automatically mapped to the C4 encoders in device mode).

markusschloesser commented 3 years ago

Do any of those VSTs expose any control parameters by an "improper way"? How are a lot of "continuous parameters" exposed? Improperly?

I mean a 2 (3,4) state button in the GUI (e.g on/off, which I call "discrete" states), which is represented in Live (aka the exposed vst3 param values) as integer going from 0.0001 to 1 and the value only switches once you go over 0.5000, which means instead of being able to directly switch between on and off, I have turn the knob a massive amount of times to go from on to off.

Are you talking about CC? (midi continuous controller messages?) Are you talking about "endless" parameters that "never" top out or bottom out, just keep incrementing or decrementing by some fixed value?

I am not talking about CC, i am specifically talking about vst3 plugins (vst2 had a different way of exposing params) (see https://steinbergmedia.github.io/vst3_doc/vstsdk/classSteinberg_1_1Vst_1_1Parameter.html )

I understand how it would be nice to be able to set-and-forget some fixed value to increment for each "encoder click" and then not have to worry about your "encoder turn speed". "Superior Drummer 2.0" only properly exposes "device on/off". I haven't looked into whether or not it has any alternate "improper ways" exposed and/or what control any exposure offers.

Have you heard of a VST called Unify? It can be described as a vst "host instrument". I don't know if that's a real category, but Unify can load other instruments and effects "inside Unify". (disclosure, I am a PluginGuru.com customer several times over and I "follow" the YouTube channel) I only mention it here because Unify by default exposes 32 "proper parameters" (Unify calls them "macro knobs") to Live, and then "inside Unify" you can map as many (inner device and/or FX) parameters to one (or more) exposed "macro knobs" as you wish. Plus you can "draw" the parameter "value curve" in any (reasonable) shape (such as a fixed stair-step quantized curve). I haven't done any focused research to back up these words, but if some VST device is programmed with parameters that can in any way be automated, then Unify exposes them for "easy mapping" to the "macro knobs" (which are in turn automatically mapped to the C4 encoders in device mode).

Will take a look, thanks! I already have a couple of "container/host/chain" vst3 plugins, but in general I am trying to avoid those, because the non-hassle of mapping is what makes the C4 with the remote script so great. Also one can do the same with the macro knobs in Live, when a device is grouped, but we currently then cannot access the other params when a device is grouped. (in fact, you can click the device in Live, but that throws an error message for the script)

BilldarBagdar commented 3 years ago

What other container/host/chain plugins?

I only mentioned Unify because of the deep ways you can customize the behavior of its "macro knobs", and specifically the fact that you can edit the "response value" curve of the responses so freely. Step-wise "quantized" curves are easy to draw and use. It's also super easy to set up, for example, 10 device parameters (from 10 different devices if you want) all mapped to one macro knob with each parameter following its own custom "response value" curve and the macro knob just works with the C4. (You can also map any macro knob to also "send CC" somewhere [works well with externals and/or plugins that don't expose automation but do respond to CC])

I also happen to like the whole "Plugin Guru" concept. The owner/operator seems like a "cool dude" who loves what he does and part of what he loves to do is share knowledge. When he makes a "support video" for one of his "plugin libraries", in addition to walking through the patches in the library, he generally goes into a LOT of details about how to program the plugin the library goes with when he is explaining "how cool the patches are", and his "Saturday Livestreams" often feature "live patch building" exercises that can be very informative. (specifically these days, mostly "patch building in Unify")

which means instead of being able to directly switch between on and off, I have turn the knob a massive amount of times to go from on to off.

There are two "LED ring" settings that might help in this situation, one definitely "works" that way for on/off parameters when you use the "encoder ring mode" VPOT_DISPLAY_BOOLEAN it only takes like 5 "clicks" of a turn to toggle the ring on/off (ccw for off). The other mode that might "work" that way for parameters with only a small set of discrete values is 'VPOT_DISPLAY_SINGLE_DOT' if you only map, say, the "first 3" encoder_ring_led_mode_cc_values

I mean:

display_mode_cc_base = encoder_ring_led_mode_cc_values[self.__v_pot_display_mode][0]
range_end = encoder_ring_led_mode_cc_values[self.__v_pot_display_mode][1] - display_mode_cc_base
feedback_rule.cc_value_map = tuple([display_mode_cc_base + x for x in range(range_end)])

...this maps the entire range of "mode cc values" into the "feedback response curve" that controls the LED rings. If you strategically replace the default "encoder cc mode" with the BOOLEAN mode, for example, and Live interprets that mapping the way we need, then I think you can get the behavior you seek. For the other "small set" modifier, I'm kind of guessing maybe if you set SINGLE DOT mode and only map a subset of the LED range for feedback, say

range_end = display_mode_cc_base + 3

...and Live interprets that mapping the way we need, then you could also get the behavior you seek. The "trick" would be identifying the device parameters in need of such special treatment at "encoder feedback mapping" time, and of course Live's cooperation. (Although the behavior is "coded manually" here, I've seen this behavior actually happen when turning the "record arm" and "mute" encoders. But only under "error conditions" when other stuff was really fucked up, and I haven't figured out exactly why I can't get it to work under "normal conditions". However, I suspect the behavior I have witnessed means the chance is better than even that "Live will interpret feedback mapping" the way we would need.)

Edit: upon further thought, in order to "get it right", this tuple would probably need to be mapped differently than some continuous range like

feedback_rule.cc_value_map = tuple([display_mode_cc_base + x for x in range(range_end)])

...or range_end = display_mode_cc_base + 3

I mean, for BOOLEAN, the feedback rule value map tuple might need to be size 2:

feedback_rule.cc_value_map = tuple([display_mode_cc_base, range_end])

and for the SINGLE DOT (any other?) mode, the tuple might need to contain some number of values less than the full range. So if the parameter has only 3 distinct states, the feedback rule value map tuple would be size 3:

midpoint_value = int((range_end - display_mode_cc_base)/2) + display_mode_cc_base 
feedback_rule.cc_value_map = tuple([display_mode_cc_base, midpoint_value, range_end])

Calculating the correct number and which values to map between "cc base" and "range end" for parameters with 4, 5, 6 distinct states only could become "ugly" pretty fast if you are looking for "evenly distributed" LED ring results between "cc base" and "range end". (unless you pre-calculate "lookup tables" for the distributions between "cc base" and "range end") But if you are more concerned with the "turn responses" than the intermediate "LED ring appearance", all you probably need is the correct number of "any" values between "cc base" and "range end" in the tuple that gets mapped.

BilldarBagdar commented 3 years ago

I am specifically talking about vst3 plugins

I haven't gone down the rabbit hole there, but, specifically about the "normalized value" pattern implementation?

Also, trying to be a little more clear, the actual runtime assigned "encoder ring mode" assignment only impacts the way the LEDS light up when the C4 receives those CC values in midi CC messages to the assigned encoder. (and also) Since we can't really control how the C4 sends midi CC message data from assigned encoders, maybe the way we map feedback can control how Live responds to the CC already sent by the C4. and then I was riffing on what you said about having to go all the way across .500 just to flip an on/off parameter. (and also) I'm not sure this "feedback mapping tuple size" idea holds any water. I have an Arsenal script that behaves the same .500 way (an on/off parameter doesn't toggle unless the (BCR2000) encoder cc value crosses 63/64 (CC value mid-point) boundary) Maybe it's a compromise the author needed to make to get a "common framework" for his scripts across many devices, but in general I feel like if my Arsenal script doesn't recognize on/off parameters and respond more appropriately than the 50/50 line (and that author is so good), why would such a simple idea as mapping only two feedback values in the tuple actually work (and Arsenal not use it)...

BilldarBagdar commented 3 years ago

The Parameter styles / 'Step Count' description here seems to be saying to me that by definition, for VST3, a "binary" on/off parameter value will get "normalized" for automation such that the "automation value" must cross the .500 line to change states on/off. This is an image from that page showing how normalization transformations work for a parameter with 4 discrete states, (step count 3)

image

On/off means two states, and the above transformations mean .500 is the normalized line between the two.

That rule can be "bent" by a plugin like Unify. You could, if you want, define a "macro knob curve" that jumps up and down between 0 and .500 every 10 "clicks" through the entire "sweep" of an encoder (every 12 CC values the C4 sends between 0 and 128). But that's "manually defined" override behavior that still technically obeys the normalization rules.

But otherwise, I suspect you/I/we are all doomed to spinning a knob past .500 just to "turn on a filter" or whatever. The upside would be if you are drawing automation with a mouse in Arrangement View for a parameter with 3 discrete states, it would be pretty easy to visualize on the vertical axis when the automation curve will transition to each state of the parameter.

BilldarBagdar commented 2 years ago

In case this Issue is still relevant, our "new to us" (more or less) make_interpolator() method should work for you here too. That .500 threshold is firm by VST3 standard, but a scale factor can influence how fast you get there in practice.

You could scale it so intensely two little clicks in either direction is "guaranteed" to cross .500.

markusschloesser commented 2 years ago

Thinking about it, I don't we can/want to interfere with the automated Live feedback map. So imho it's best to nag devs to implement it better. At least I don't, currently :-) I'm gonna close this here, feel free to reopen if you feel like it!