12 of 132 min

A MIDI keyboard and OBS as a hardware controller (Hammerspoon)

I have several MIDI keyboards, so a simple idea came up: use one of them as a physical control panel where keys launch commands. Hammerspoon works here as “firmware”: it receives MIDI events, translates them into actions and controls OBS over WebSocket.

Example:

The config has an action map smkActions[…], and note 48 is assigned:

smkActions[48].label = "OBS: Scene #01"
smkActions[48].fn = function() obsSetSceneByIndex(1) end

What this means:

  • smkActions[48]: the slot for a specific MIDI note (48).

  • .label: a human-readable name (for logs and understanding).

  • .fn = function() … end: the function performed when this MIDI key is pressed.

  • obsSetSceneByIndex(1): switch OBS to the scene with index 1 (the index comes from the OBS scene list).

The MIDI event handler then has this logic: perform actions only in command mode (which is turned on by the sustain pedal/CC64).

The key fragment (the essence):

if smkCommandMode and cmdType == "noteOn" then
  local note = mdTable.note
  local vel  = mdTable.velocity or 0
  if not note or vel <= 0 then return end

  if note < SMK_NOTE_MIN or note > SMK_NOTE_MAX then return end

  local act, layer = smkResolveAction(note)
  if not act then return end
  if not act.hotkey and not act.fn then return end

  -- then debounce and run act.fn / act.hotkey
end
  • smkCommandMode: a boolean flag “command mode on”.

  • cmdType == "noteOn": react to a key press (Note On).

  • mdTable.note: the note number (for example, 48).

  • mdTable.velocity: the force of the press; vel <= 0 is cut off (so as not to trigger “empty” events).

  • SMK_NOTE_MIN/MAX: a range limit, to process only the 25 keys needed.

  • smkResolveAction(note): gets the action for a specific note (taking possible layers and overrides into account).

  • act.fn: if the note has a function, it will be called.

Then obsSetSceneByIndex(1) does roughly this chain:

  1. Gets the list of OBS scenes (through a cache with a small TTL, so as not to poke OBS on every click).

  2. Takes the scene name by index 1.

  3. Sends OBS the SetCurrentProgramScene command over WebSocket.

The final logic of one key

MIDI note 48 → (command mode ON) → find smkActions[48] → call obsSetSceneByIndex(1) → OBS switches the scene to #1.

We meet on the threshold between conditional normality and chaos. Bessmyslennost i skorotechnost
Series contents
  1. Keyboard layers
  2. Karabiner = a set of JSON rules
  3. 1) Caps Lock = Hyper
  4. 2) A layer on Space: tap = space, hold = F18
  5. 3) backtick hold = F17
  6. 4) Russian layout: targeted rules for Russian typing only
  7. Hammerspoon
  8. Window Manager in Hammerspoon
  9. Media control as scenarios in Hammerspoon
  10. Hammerspoon as a local server and a "remote control"
  11. Monitors and a stable workplace (Hammerspoon)
  12. A MIDI keyboard and OBS as a hardware controller (Hammerspoon)
  13. The topic of keyboard layers is closed for now