Parallax Propeller2 USB driver - the MashUp

Getting started: USB-MIDI
Login

Getting started: USB-MIDI

Status: awaiting hardware validation (ticket 2140) — compile-verified; the bulk and poll-once machinery underneath is bench-proven.

What you need

pnut-ts -d -I <path-to>/src -D USB_MIDI my_midi.spin2

The smallest program

#IFDEF USB_MIDI
#PRAGMA EXPORTDEF USB_MIDI
#ENDIF

CON
  _clkfreq = 200_000_000

OBJ
  usb : "usb_app"

PUB main() | ev
  usb.begin(16)

  repeat until usb.midi_present()
    usb.poll()
    waitms(50)
  debug("controller bound - play something")

  repeat
    repeat                                  ' drain the whole burst each pass -
      ev := usb.midi_read()                 ' one poll can carry 16 events
      if ev == 0
        quit
      if usb.midi_is_note_on(ev)
        debug("note ON  ", udec_(usb.midi_ev_data1(ev)), " vel ", udec_(usb.midi_ev_data2(ev)))
      elseif usb.midi_is_note_off(ev)
        debug("note OFF ", udec_(usb.midi_ev_data1(ev)))
      elseif usb.midi_ev_cin(ev) == usb.CIN_PITCH_BEND
        debug("bend ", sdec_(usb.midi_bend(ev)))
      elseif usb.midi_ev_cin(ev) == usb.CIN_CTRL_CHANGE
        debug("cc", udec_(usb.midi_ev_data1(ev)), " = ", udec_(usb.midi_ev_data2(ev)))
    usb.poll()
    waitms(8)

What bring-up actually does

  1. usb.begin(16) enumerates the bus. A device with a MIDIStreaming interface (USB Audio class, subclass 3) binds to midi_* — including audio interfaces that carry a MIDI port alongside their audio.
  2. midi_present() goes true. There is no rate, protocol, or mode to negotiate — MIDI is stateless per event, which is why this is the shortest bring-up in the stack.
  3. midi_read() returns 32-bit event packets: the low nibble (the CIN) says what each packet is, so there's no running-status parsing — decode with midi_ev_*() / midi_is_note_*().
  4. An idle controller costs ~1 ms per midi_read(); a chord arrives as a burst that one poll buffers and successive calls hand out — hence the inner drain loop above.

Sending works too, if the device has a MIDI OUT jack (midi_out_present()):

  usb.midi_write(usb.midi_pack(0, usb.CIN_NOTE_ON, $90, 60, 100))  ' middle C

Making sound

Pair with the audio plane: decode note on/off into a phase-accumulator oscillator feeding aud_write. The complete pattern — exact equal-temperament note table, velocity scaling, pitch bend — is examples/top_midi_example.spin2 (build with -D USB_MIDI -D USB_AUDIO); see also the audio guide.

Troubleshooting

Symptom Likely cause
midi_present() never true Device isn't class-compliant (very old, or needs vendor drivers), or it's on the other bus — check dev_count()/dev_vid() roster.
Notes stick "on" Handle velocity-0 NOTE ON: it is a note-off (the helpers do this for you — use midi_is_note_off(), not the CIN alone).
Events arrive in clumps Expected — drain until midi_read() returns 0 each pass.

Next