What is a suitable software design approach for handling user input from hardware buttons, where the function of each button depends on state?

I'm designing a musical instrument which has a lot of hardware buttons (something like a Novation Launchpad or Monome Grid). I'm used to building screen based software, where you can show and hide buttons depending on context and each button generally only has one function. But hardware buttons are a very different challenge and my code is rapidly turning into an unmanageable mess of if statements.

For example a single button may have these functions:

  • if the transport is playing, pressing it launches the clip
  • unless the Select key is held when pressing it, in which case it selects the clip
  • if the transport is not playing, then pressing it selects the clip, even if Select is not held
  • if Delete is held when pressing the button, it deletes the clip
  • if the button is long pressed, then it copies the clip
  • but if no clip exists in the slot corresponding to that button, then instead of all the above, a clip is created in that slot

... and all of that is just for one view of the app, other views may be completely different.

This is obviously some kind of state machine, but the number of states is huge – practically infinite if you consider that you can program any song into it – so I'm not sure if that's a useful abstraction. I've had a look at the Monome Grid tutorials but their examples are no better than my code.


1 Answer 1


The way I would design such a system is by separating the button-handling logic from the application logic by generating (mostly) state-independent events from the button presses and only dealing with those events in the state machines of the application.

To go with your example, and assuming the button being pressed is nr. 42, then

  • Pressing (or actually, on releasing) only the button would generate a "play clip (42)" event
  • Pressing the button together with having the <Select> button pressed would generate a "select clip (42)" event
  • Pressing the button together with having the <Delete> button pressed would generate a "delete clip (42)" event
  • Long-pressing the button would generate a "copy clip (42)" event

The application handling those events would know that if the transport isn't playing, then a "play clip" event should have the same response as a "select clip" event, or a "create clip" event if no clip exists yet in that spot.

There you could also define that a "delete clip" event on an empty spot should have no effect.

Note how this separation of responsibilities makes both parts much more manageable.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.