Skip to content

External control input: drive chords, key & transport from a MIDI controller #2

Description

@philoking

Summary

Let Modular Riffs listen to a MIDI controller, not just send to one. Today js/midi.js is output-only (notes + clock + transport). Open the MIDI inputs so a hardware keyboard or control surface can drive the jam hands-free: select the live chord, nudge key/scale, run the transport, and ride the macro knobs (Evolve / Energy / per-part Activity) — all while your hands stay on the gear.

Why it fits

Modular Riffs is meant to be performed, not just configured. When you're patching cables with both hands, reaching for the mouse to reroll a section or lean on the energy is the thing that breaks flow. A controller mapping closes that gap and turns the app into a playable front-end for the rig it's already clocking.

Proposed scope

Input plumbing (js/midi.js)

  • requestMIDIAccess already exposes access.inputs; add listInputs(), an input picker, and onmidimessage handling alongside the existing output code.
  • Parse Note On/Off, CC, and (optionally) transport bytes. Ignore the app's own clock if it's echoed back.

Dispatch (js/app.js)

  • Route incoming events into the existing state + performable edit queue, so chord/section changes land on the next bar boundary (the quantize hook already lives in js/scheduler.js) rather than glitching mid-bar.
  • Targets, in priority order:
    • Chord degree / quality — keys or pads pick the current chord (I–VII + 7th/sus/6 toggles); punches in over the arrangement at the next boundary. (Foundation for a future live chord-pad UI.)
    • Transport — Play / Pause / Stop, plus Reroll-section / next-section as note or CC triggers.
    • Key & scale — step the root around the circle of fifths; cycle the scale.
    • Macros — map CCs to Evolve, Energy, Swing, and each part's Activity / mute / gate.

MIDI-learn + a documented default map

  • A small "MIDI Learn" affordance: click a control, wiggle a knob, it binds. Persist bindings in state (already plain JSON, so it rides presets/export).
  • Ship a documented default CC map in the README (a compact table) so a generic controller works out of the box.

Where

js/midi.js (input handling), js/app.js (dispatch + learn UI + persistence), README (CC map table).

Done when

  • An input device can be selected and its events are received.
  • A keyboard can drive the current chord and the transport, quantized to the bar.
  • At least Evolve / Energy / Activity are CC-mappable via MIDI-learn.
  • Bindings persist in presets / JSON export.
  • README documents the default map.

Open questions

  • Default chord layout: white-keys-as-degrees, or a dedicated pad bank?
  • Should transport-in also imply clock-in (slaving), or keep that a separate effort?

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions