helgoboss / helgobox

Helgobox: ReaLearn & Playtime
https://www.helgoboss.org/projects/helgobox
GNU General Public License v3.0
211 stars 20 forks source link

"Incremental Button" function from NRPN with 0/1 values (toggle) #269

Closed vonglan closed 3 years ago

vonglan commented 3 years ago

On the OB6, there are many toggle buttons, for example "Filter Envelope Velocity Sensitivity". They send NRPN values 0 and 1 alternatingly (toggle). This can not be changed on the instrument (and I suppose it is the same with most other hardware synthesizers, only they use CCs instead of NRPNs). I would like to use these buttons as "incremental buttons" (like I do in my "hard coded" JSFX mapper that I want to replace with ReaLearn). One obstacle is #268 , which might be a bug. Another is that the button presses that send NRPN 0 are ignored by ReaLearn. I understand that this is intended. But for this case it is not helpful. Can this be overcome, or is there a workaround for this?

helgoboss commented 3 years ago

Ah, such a pity. But I guess one can already be lucky that this kind of synthesizers can be used as controllers at all.

The problem can't be overcome at the moment. I think the way I'm going to implement this is to:

  1. Introduce the "Source character" dropdown also to NRPN sources (not just CC).
  2. Introduce a new source character "Toggle button (avoid if possible!)" which will emit value 1, no matter what.
vonglan commented 3 years ago

Ah, such a pity. But I guess one can already be lucky that this kind of synthesizers can be used as controllers at all.

On the other hand, hardware synthesizers offer very useful labels beside their knobs ("Frequency", "Noise", ...), so that is an advantage when using them as a VST controller :-)

Thanks for implementing this!

vonglan commented 3 years ago

Should this already be in pre9? I see that the commit references pre9, but I do not finde it in the GUI.

helgoboss commented 3 years ago

Yes, it's in pre9 Source Type "(N)RPN value", Character "Toggle-only button".

vonglan commented 3 years ago

I don't see it. Am I blind?

Bildschirmfoto 2021-04-02 um 21 41 48 Bildschirmfoto 2021-04-02 um 21 42 40
helgoboss commented 3 years ago

Character is only available for 7-bit NRPN (same as with CC).

vonglan commented 3 years ago

Ah, now I see it. Unfortunately, the OB6 always sends 14-bit. ReaLearn does currently not "see" the 14-bit messages when source is configured as 7-bit. Could that be adapted? The OB6 sends only 0 for the higher 7 bits for these buttons anyway.

helgoboss commented 3 years ago

What a strange usage of NRPN, using a 14-bit message for a button press. But okay, then I will enable it for 14-bit, too. But the encoder types don't make sense there :/

vonglan commented 3 years ago

It would be sufficient, if I could enter the characteristic for 14-bit-NRPNs in the JSON (if this saves effort for GUI implementation - it is a very special case and not needed often).

helgoboss commented 3 years ago

It's okay, the dropdown is there anyway, just need to enable it. I guess I will let the existing encoder types work on the low-order bits then. Probably nobody will ever use it, but so what.

vonglan commented 3 years ago

When I switch on "toggle-only button" for NRPN 14-bit, the VST parameter does not move at all (mapping Test Cutoff Velo Sensitivity below) When I use character "range element", the VST parameter moves on every other press (as expected) (mapping Osc1Sync aka Test Cutoff Velo Sensitivity below). Rotate works fine.

{ "version": "2.8.0-rc.1", "name": "Test269", "defaultGroup": {}, "mappings": [ { "id": "eb4275f3-4de5-430a-9a75-3adab6c55e21", "name": "Test Cutoff Velo Sensitivity", "source": { "type": 6, "channel": 0, "number": 48, "character": 5, "isRegistered": false, "is14Bit": true, "oscArgIndex": 0 }, "mode": { "type": 1, "maxSourceValue": 0.00006103888176768602, "minStepSize": 0.2, "maxStepSize": 0.2, "rotateIsEnabled": true }, "target": { "category": "virtual", "trackGUID": "3B66155F-0527-C34E-9488-436A345B2E92", "fxAnchor": "id", "fxIndex": 2, "fxGUID": "63A69C0B-6C83-374B-A74A-79222592619A", "paramIndex": 118, "controlElementIndex": "SY/CutoffVelModU", "useProject": true, "moveView": true, "seekPlay": true } }, { "id": "03e1a745-3b1e-4926-9b5d-0c50e1ee4d50", "name": "Osc1Sync aka Test Cutoff Velo Sensitivity", "source": { "type": 6, "channel": 0, "number": 1, "isRegistered": false, "is14Bit": true, "oscArgIndex": 0 }, "mode": { "type": 1, "maxSourceValue": 0.00006103888176768602, "minStepSize": 0.2, "maxStepSize": 0.2, "rotateIsEnabled": true }, "target": { "category": "virtual", "trackGUID": "3B66155F-0527-C34E-9488-436A345B2E92", "fxAnchor": "id", "fxIndex": 2, "fxGUID": "63A69C0B-6C83-374B-A74A-79222592619A", "paramIndex": 118, "controlElementIndex": "SY/CutoffVelModU", "useProject": true, "moveView": true, "seekPlay": true } }, { "id": "dc9e82dd-55b5-4382-a661-71c090aaa2ce", "name": "Select Alternate Layer", "source": { "type": 6, "channel": 0, "number": 71, "isRegistered": false, "is14Bit": true, "oscArgIndex": 0 }, "mode": { "maxSourceValue": 0.00006103888176768602 }, "target": { "category": "virtual", "fxAnchor": "id", "controlElementType": "button", "controlElementIndex": "SY/OtherLayer", "useProject": true, "moveView": true, "seekPlay": true } } ] }

helgoboss commented 3 years ago

@vonglan Can you maybe make a small test project where you record your button presses?

vonglan commented 3 years ago

Is this what you need?

Temp Bug.RPP.zip

helgoboss commented 3 years ago

It works in general. In the attached project, I map your recorded button presses via incremental buttons to the track volume - and it increases. Just play the project and watch it increase.

Temp Bug - working.zip

Maybe has something to do with your particular VST / step size settings.

vonglan commented 3 years ago

I compared the JSON, and found what causes the problem: your example project stops working as soon as Tuning/Source/Max is explicitly set to any value. When I remove the corresponding JSON line "maxSourceValue": 0.00006103888176768602, it works again.

I was wondering anyway: do the Source Min/Max Values have any meaning, when character is "Toggle-only button", and Mode is "Incremental buttons"?

helgoboss commented 3 years ago

The source range makes sense for "Range" and "Button (momentary)". Range should be obvious. In case of a button it makes sense for velocity-sensitive buttons (e.g. to execute a different action if you hit the button very hard).

Can't think of any use case where restricting the source range for "Toggle-only button" would make sense because I doubt that this kind of button is ever velocity-sensitive.

vonglan commented 3 years ago

I think it does not make sense for the encoder characters and for toggle-only. For toggle-only the requirement (as I understand it) is to ignore whichever value comes in (no matter if it is NRPN or CC Value or Note Velocity) and make it 1 (or the internal maximum value) always.

I think it would be good usability (but low prio) if the source min/max fields were hidden for these characters. And also, that the bug (if max is set in whatever way) be fixed.

helgoboss commented 3 years ago

I think it does not make sense for the encoder characters and for toggle-only. For toggle-only the requirement (as I understand it) is to ignore whichever value comes in (no matter if it is NRPN or CC Value or Note Velocity) and make it 1 (or the internal maximum value) always.

I think it would be good usability (but low prio) if the source min/max fields were hidden for these characters.

No, it's still relevant for feedback (forgot to mention).

And also, that the bug (if max is set in whatever way) be fixed.

Which bug? If you set Source Max to a value that's below in the incoming absolute control value, it won't respond. It doesn't distinguish between different source characters.

vonglan commented 3 years ago

I think it is a bug. If I set the max value to any value, it wont' work any more. The incoming values are just 0 and 1.

helgoboss commented 3 years ago

Ah, now I understand it.

It's because toggle-only always sends 100% no matter the incoming value (which I think is documented). So technically not a bug, just a nuisance of the toggle-only character that one must know. Maybe could be solved easily by making toggle-only send 1% instead. However, if I see this needs more than trivial changes, I'm inclined to just keep it that way because this source character has a "workaroundy touch" in the first place.

vonglan commented 3 years ago

I still don't understand it. Especially, why does ReaLearn behave differently if I manually select any value for max, than if I leave it at the default.

Maybe I will write something later in the "Discussions" settings about step size, speed, etc. and how it works together. I think I have no yet understood that completely. (see also #300 ).

helgoboss commented 3 years ago

Okay, tell me the number of the point in which you lose track:

  1. The default for "Source Min" is 0% and the default for "Source Max" is 100%. That means it lets through all absolute values.
  2. The "Toggle-only" character makes ReaLearn send an absolute value of 100% whenever a MIDI messages comes in - no matter if it's 0% or 100% or anything inbetween.
  3. Now, if you change "Source Max" to 99% or really anything < 100% ... then a value of 100% will not be let through anymore!
  4. Because "Toggle-only" sends only 100%, it will not be let through anymore.
helgoboss commented 3 years ago

Ah, or is it possible you are relying on the "Out-of-range behavior" to kick in?

In "Normal" mode, if this set to "Min or max", ReaLearn will actually process an incoming source value that is NOT between "Source Min" and "Max" by setting it to either min or max (see user guide).

In "Toggle" mode it will ignore "Source min/max" and "Out-of-range behavior" altogether for the control direction.

With "Incremental buttons", "Source min/max" are respected (definitely makes sense e.g. in order to react only to very hard button presses). But "Out-of-range behavior" is not respected at the moment, so out-of-range source values will always be ignored. Maybe this is what you find weird? Do you see any use case where this might make sense? I honestly don't. For me "Out-of-range behavior" serves just 2 control element types: a) Momentary-style velocity-enabled buttons and b) Faders/knobs. ... (and of course the feedback direction, but this we can ignore in this discussion).

vonglan commented 3 years ago

Okay, tell me the number of the point in which you lose track:

  1. The default for "Source Min" is 0% and the default for "Source Max" is 100%. That means it lets through all absolute values.

Ah, 100% means the technically possible maximum value (typically 127 or 16383). Now I notice that it works alright if I put 16383 (even manually) into the max field! I had the impression that it fails as soon as any manual value is entered. And I did not think that 16380 would be handled any different from 16383. Also, because the OB6 sends values of 0-127, 0-254, 0-255 for the knobs, and for the "toggle-like" buttons 0-1, or 0-2 (Keytrack 0/half/full), I was used to having much lower values there. And they are necessary, because otherwise the correct scaling for the absolute values does not work. So at first I thought I should enter max = 1 (like for all the other OB6 knobs and buttons, where I enter the actual max value that the device sends).

  • I guess I should respect at least "Source min/max" in order to make it possible e.g. to react only to very hard button presses.
  • I don't see any scenario in which supporting "Out-of-range behaviors" other than "Ignore" would make sense.

No, I wasn't thinking about out-of-range-behavior.

However, it seems to me Source min/max is used for different purposes, and I think these might clash in some cases:

  1. Identifying the range of possible incoming values from the device (like on the OB6, NRPN 14 bit is sent, but different ranges e.g. 0-254 are sent for a particular knob), so they are correctly scaled
  2. Filtering of unwanted value ranges to achieve all kinds of things (in the control direction), e.g. route one control element into different mappings, depending on velocity. (Also, "min" and "min or max" behaviors; use case currently not clear to me.)
  3. Correct scaling for feedback, if the device supports feedback
  4. "min" is great for the feedback direction of selection switches (you explained that to me, and I use it for Bank control)

I think at least 2 and 3 could clash. Or is 2 always used for absolute control elements, and 3 is only for relative ones (encoders with LED-rings)? No, motor-faders are absolute and want to receive correct feedback. I think that is a problem.

With "Incremental buttons", "Source min/max" are respected (definitely makes sense e.g. in order to react only to very hard button presses). But "Out-of-range behavior" is not respected at the moment, so out-of-range source values will always be ignored. Maybe this is what you find weird?

No I had not been thinking about this. But thinking about it now, a theoretical use case would be to use a keyboard key's velocity to decrease/increase the value of a parameter. Value below 64 would decrease, above 64 would increase. But that could be handled with EEL transformation. Also use cases like scaling the 0-127 range down to an increase of 10-20. So no problem.

Do you see any use case where this might make sense? I honestly don't. For me "Out-of-range behavior" serves just 2 control element types: a) Momentary-style velocity-enabled buttons and b) Faders/knobs. ... (and of course the feedback direction, but this we can ignore in this discussion).

See above, I think feedback should be included in the discussion, because I think it can lead to conflicts.

vonglan commented 3 years ago

I am just reading the User Guide. The statement for toggle-only buttons

It emits a 100% value when pressing it,

is wrong for the Sequential synthesizers, as stated above. Their buttons emit a measly 1 (or sometimes 2 or 3) in 14 bits.

helgoboss commented 3 years ago

I think at least 2 and 3 could clash. Or is 2 always used for absolute control elements, and 3 is only for relative ones (encoders with LED-rings)? No, motor-faders are absolute and want to receive correct feedback. I think that is a problem.

They clash, yes, but it's mostly not a big issue. For buttons, the "Control filter" provides an alternative in some cases (e.g. "Press only") - it has no effect on feedback. If you can't avoid a clash, the solution is to separate into a control and a feedback mapping. No big deal. The fact that control and feedback is united in one mapping is already a convenience in itself.

With "Incremental buttons", "Source min/max" are respected (definitely makes sense e.g. in order to react only to very hard button presses). But "Out-of-range behavior" is not respected at the moment, so out-of-range source values will always be ignored. Maybe this is what you find weird?

No I had not been thinking about this. But thinking about it now, a theoretical use case would be to use a keyboard key's velocity to decrease/increase the value of a parameter. Value below 64 would decrease, above 64 would increase. But that could be handled with EEL transformation. Also use cases like scaling the 0-127 range down to an increase of 10-20. So no problem.

Do you see any use case where this might make sense? I honestly don't. For me "Out-of-range behavior" serves just 2 control element types: a) Momentary-style velocity-enabled buttons and b) Faders/knobs. ... (and of course the feedback direction, but this we can ignore in this discussion).

See above, I think feedback should be included in the discussion, because I think it can lead to conflicts.

Ah yes, it could affect existing configurations if someone has fine-tuned the feedback and is relying on the fact that currently it doesn't affect control. Maybe we should leave everything as it is then ...

helgoboss commented 3 years ago

I am just reading the User Guide. The statement for toggle-only buttons

It emits a 100% value when pressing it,

is wrong for the Sequential synthesizers, as stated above. Their buttons emit a measly 1 (or sometimes 2 or 3) in 14 bits.

You are right. Fixing.

vonglan commented 3 years ago

If you can't avoid a clash, the solution is to separate into a control and a feedback mapping. No big deal. The fact that control and feedback is united in one mapping is already a convenience in itself.

Good, so there is always a solution even if there is a clash.

I am still wondering if there is a good solution to prevent other users from falling into the trap that I was in: setting max to 1 (the actual max value emitted by the controller/synth, like I had to do for all other mappings), and then wondering why the mapping wasn't doing anything. For other characters (except "toggle-only"), min and max relate to the actual min and max values of the device. (Except for "encoder", where 0-127 is only important for the feedback direction.)

Option 1: Hide the "Source min/max" controls for toggle-only. Use 100% for control direction, and 0 or 100% for feedback. Does this have any disadvantages?

Option 2: As you suggested above, internally transform all incoming signals to a fixed 1% (instead of 100%). No problem when used with incremental button function, as far as I understand. But if the Main Mapping does not receive this as "Toggle" or changes source max to 1, it won't work as expected. I don't like this option.

helgoboss commented 3 years ago

If you can't avoid a clash, the solution is to separate into a control and a feedback mapping. No big deal. The fact that control and feedback is united in one mapping is already a convenience in itself.

Good, so there is always a solution even if there is a clash.

I am still wondering if there is a good solution to prevent other users from falling into the trap that I was in: setting max to 1 (the actual max value emitted by the controller/synth, like I had to do for all other mappings), and then wondering why the mapping wasn't doing anything. For other characters (except "toggle-only"), min and max relate to the actual min and max values of the device. (Except for "encoder", where 0-127 is only important for the feedback direction.)

Option 1: Hide the "Source min/max" controls for toggle-only. Use 100% for control direction, and 0 or 100% for feedback. Does this have any disadvantages?

Yes, that the usual way of changing LED color via source min/max is not usable in this case. On the other hand ... if the controller is that dumb and doesn't even provide a momentary button mode, then it probably doesn't have feedback, does it? I still consider this "Toggle-only" mode (or rather toggle-only control elements) as an abomination that actually doesn't deserve that much attention as we are giving it now.

Option 2: As you suggested above, internally transform all incoming signals to a fixed 1% (instead of 100%). No problem when used with incremental button function, as far as I understand. But if the Main Mapping does not receive this as "Toggle" or changes source max to 1, it won't work as expected. I don't like this option.

I also don't particularly like it, but it's better then the first option I guess. The "Toggle buttons" mode will consider everything != 0% as a button press, so this is not an issue.

vonglan commented 3 years ago

On the other hand ... if the controller is that dumb and doesn't even provide a momentary button mode, then it probably doesn't have feedback, does it? I still consider this "Toggle-only" mode (or rather toggle-only control elements) as an abomination that actually doesn't deserve that much attention as we are giving it now.

I wouldn't call the OB6 dumb, it is just that its primary purpose is not being a controller. But it is very useful as controller for synth presets, because it has lots of suitably labelled and positioned knobs and buttons (in contrast to an X-Touch Mini or Twister, for example).

It does have feedback, but only on/off. It is very useful for "toggle targets", but not for anything else.

Maybe just leave it as it is (maybe add an explicit note in the User Guide).

helgoboss commented 3 years ago

Maybe just leave it as it is (maybe add an explicit note in the User Guide).

Okay, I also think this would be the best. Could you come up with a small text that you think users understand?

vonglan commented 3 years ago

Add another bullet point after

  • The way this character works: ReaLearn will simply emit 100%, no matter what the hardware sends.