jean-emmanuel / open-stage-control

Libre and modular OSC / MIDI controller
https://openstagecontrol.ammd.net
GNU General Public License v3.0
702 stars 88 forks source link

[Feature] New button type needed: decoupled #789

Closed AtmanActive closed 1 year ago

AtmanActive commented 1 year ago

Hi,

Kudos and thank you for building this awesome software.

When working with Control Surface Integrator for Reaper, I encountered a problem with current button widget implementation.

All current button modes ( toggle, push, momentaryand tap) always couple the button press with button feedback (LED). But, there are cases where this is undesirable as the DAW should be the one and only one decision maker for this.

This problem is very similar to MIDI Local Control ON/OFF where if one has a MIDI keyboard with built-in sounds, then, one can't comfortably work with a sequencer unless they can turn Local Control OFF, as the sequencer is sending MIDI notes back. So, when Local Control is OFF, then, the keyboard doesn't produce any sound on it's own but sends all MIDI events to a sequencer and responds to MIDI events coming back from the sequencer.

It's the same problem here: OSC sends button press in push mode, Reaper/CSI responds with state ON or OFF depending on whether that action succeded or not, but Open Stage Control overwrites this button LED state with it's own local state, depending on how long the finger was on the button. So, information coming back from the DAW was lost.

Right now, this problem can be worked around by creating two buttons, one on top of the other where one would be mode tap and the other would be mode toggle with interaction set to false. So, the tap button underneath would be the click surface and the toggle button on top would be the LED surface. And this works correctly, no problem, but why force users to go through this if we can implement the proper mode for this, which I would call "decoupled", for now.

A button in decoupled mode would work exactly the same as in push mode, but it would handle LED feedback a bit differently: it would not change the LED feedback itself, but it would leave that to incoming OSC message. Yes, some form of a flash would be required to let the user know they did press the button, but, this flash should not interfere with the button's LED state. In other words, the LED state received back from DAW should be preserved.

Thanks.

AtmanActive commented 1 year ago

Forgot to mention, in current workaround, the two buttons can't have the same ID, of course, hence one needs to take care about one more widget/ID for each button. So we end up with two widgets and two OSC addresses just to make one button behave properly. This is not only confusing, but also cumbersome, time consuming and annoying.

This is how CSI definition looks now:

Widget ButtonMuteSelected Control /ButtonMuteSelected FB_Processor /DisplayMuteSelected WidgetEnd

This is how it should look like once we have a proper decoupled button:

Widget ButtonMuteSelected Control /ButtonMuteSelected FB_Processor /ButtonMuteSelected WidgetEnd

jean-emmanuel commented 1 year ago

Hi,

Thanks for your feedback and for taking the time to explain your issue in depth. There's actually another workaround that doesn't require using 2 widgets, usually I write something like this in the tap button's alphaFillOff property:

#{
OSC{/feeback_address} == 1 ? 0.5 : 0.15
}

However I agree this could be made simpler, I'll will keep it in mind next time I get to work on open stage control.

jean-emmanuel commented 1 year ago

This is how it should look like once we have a proper decoupled button:

Widget ButtonMuteSelected Control /ButtonMuteSelected FB_Processor /ButtonMuteSelected WidgetEnd

In this case, what's the problem with using a simple toggle button ? When the toggle is activated, the value is sent and the local feedback represent assumed state unless the feedback says otherwise. It doesn't sound wrong to me.

AtmanActive commented 1 year ago

There should never be an assumed state. That's the local control problem. The very fact that the button assumes that something happened breaks the whole thing.

It is, I believe, again the timing problem: the feedback message coming back from the DAW comes faster than the finger. The finger is still on the button and DAW message is ignored/overwritten with the local assumption of a state. In other words: desynchronization.

Furthermore, toggle assumes (wrongly) that something did happen and changes the send value, which should never ever happen. The value sent by a button should always be a 'click', or, in other words, 1 when finger landed, 0 when finger let go.

If you want, I can prepare a super-simple Reaper/CSI/OSC project with just one button and send it to you so you can see it in action on your screen.

jean-emmanuel commented 1 year ago

It is, I believe, again the timing problem: the feedback message coming back from the DAW comes faster than the finger.

That's just not possible in open stage control, where actions are treated sequentially in a single thread, the feedback message can't be treated before the local feedback. Also feedback messages are not ignored while the toggle button is still touched.

Furthermore, toggle assumes (wrongly) that something did happen and changes the send value, which should never ever happen. The value sent by a button should always be a 'click', or, in other words, 1 when finger landed, 0 when finger let go.

That's one usage, definitely not the only one. Having a button holding its own state is far from odd from a UI programmation perspective.

There should never be an assumed state.

I don't think assuming something changes upon click is wrong, that's basically what the button is for. Also, faders and knob always assume a value change (feedback-corrected if needed) and nobody finds it irregular, they would feel very weird if they didn't.

If you want, I can prepare a super-simple Reaper/CSI/OSC project with just one button and send it to you so you can see it in action on your screen.

Thanks, but don't bother, I do see the point anyway ;)

AtmanActive commented 1 year ago

That's one usage, definitely not the only one. Having a button holding its own state is far from odd from a UI programmation perspective.

But not from a Local-Control-OFF perspective.

I don't think assuming something changes upon click is wrong, that's basically what the button is for. Also, faders and knob always assume a value change (feedback-corrected if needed) and nobody finds it irregular, they would feel very weird if they didn't.

It is wrong from a Local-Control-OFF perspective. Faders and knobs don't have states.

AtmanActive commented 1 year ago

Hi,

I can see commits regarding this. Congrats and thank you for the quick turnover.

Let me know if you would like me to beta test it.

Thanks.

jean-emmanuel commented 1 year ago

I'm not fully satisfied with the implementation yet but it's functional and flexible, beta testing is welcome :)

jean-emmanuel commented 1 year ago

I went for a different approach because combining the decoupled option with all the existing modes created too many edge cases and didn't feel consistent. There is now a single "decoupled" mode that works like the toggle mode but with local feedback off. I didn't make it work like a "push" because the ambiguity between the state (pushed or not) and the value (from feedback) doesn't feel right to me.

AtmanActive commented 1 year ago

But can it be held then? Will it's OSC-send 1 and 0 follow the finger?

jean-emmanuel commented 1 year ago

No that's what I tried to say, but I the meantime I found another solution that works for all modes... this will be for next patch release

jean-emmanuel commented 1 year ago

It's in sources in case you want to try it.

AtmanActive commented 1 year ago

I would love to try it but I don't know how to use the source.

I'll have to wait for your next compile.

jean-emmanuel commented 1 year ago

v1.18.1 is uploading