nimphea/hid/midi

Search:
Group by:
Source   Edit  

MIDI support for libDaisy Nim wrapper

This module provides MIDI functionality for the Daisy Audio Platform.

Example - USB MIDI input:

import nimphea, hid/midi, per/uart

var daisy = initDaisy()
var midi: MidiUsbHandler
initMidiUsb(midi)

startLog()
printLine("MIDI Input Started")

while true:
  midi.listen()
  
  while midi.hasEvents:
    let event = midi.popEvent()
    
    case event.messageType
    of NoteOn:
      let note = event.note
      print("Note On: ")
      print(note.number)
      print(" Vel: ")
      printLine(note.velocity)
    
    of NoteOff:
      let note = event.note
      print("Note Off: ")
      printLine(note.number)
    
    of ControlChange:
      let cc = event.controlChange
      print("CC ")
      print(cc.number)
      print(": ")
      printLine(cc.value)
    
    else: discard
  
  daisy.delay(1)

Example - UART MIDI with output (TRS/DIN MIDI):

import nimphea, hid/midi

var daisy = initDaisy()
var midi: MidiUartHandler
var config = newMidiUartConfig()
config.transport_config.periph = USART_1
config.transport_config.rx = initPin(PIN_PORTB, 7)
config.transport_config.tx = initPin(PIN_PORTB, 6)
initMidiUart(midi, config)

# Send a note on message
var noteOn = [0x90'u8, 60, 100]  # Channel 1, note C4, velocity 100
midi.sendMessage(noteOn[0].addr, 3)

# Receive and echo messages
while true:
  midi.listen()
  while midi.hasEvents:
    let event = midi.popEvent()
    # Process or echo the event

Types

ControlChangeEvent = object
  number*: uint8
  value*: uint8
Source   Edit  
GPIOPort {.importcpp: "daisy::GPIOPort", size: 4.} = enum
  PORTA = 0, PORTB = 1, PORTC = 2, PORTD = 3, PORTE = 4, PORTF = 5, PORTG = 6,
  PORTH = 7, PORTI = 8, PORTJ = 9, PORTK = 10, PORTX = 255
Source   Edit  
MidiEvent {.importcpp: "daisy::MidiEvent", bycopy.} = object
  mType* {.importcpp: "type".}: MidiMessageType
  channel* {.importcpp: "channel".}: cint
  data* {.importcpp: "data".}: array[2, uint8]
Source   Edit  
MidiMessageType {.importcpp: "daisy::MidiMessageType", size: 4.} = enum
  NoteOff, NoteOn, PolyphonicKeyPressure, ControlChange, ProgramChange,
  ChannelPressure, PitchBend, SystemCommon, SystemRealTime
Source   Edit  
MidiUartHandler {.importcpp: "daisy::MidiUartHandler".} = object
Source   Edit  
MidiUartHandlerConfig {.importcpp: "daisy::MidiUartHandler::Config", bycopy.} = object
  transport_config* {.importcpp: "transport_config".}: MidiUartTransportConfig
Source   Edit  
MidiUartTransport {.importcpp: "daisy::MidiUartTransport".} = object
Source   Edit  
MidiUartTransportConfig {.importcpp: "daisy::MidiUartTransport::Config", bycopy.} = object
  periph* {.importcpp: "periph".}: UartPeriph
  rx* {.importcpp: "rx".}: Pin
  tx* {.importcpp: "tx".}: Pin
  rx_buffer* {.importcpp: "rx_buffer".}: ptr uint8
  rx_buffer_size* {.importcpp: "rx_buffer_size".}: csize_t
Source   Edit  
MidiUsbHandler {.importcpp: "daisy::MidiHandler<daisy::MidiUsbTransport>".} = object
Source   Edit  
MidiUsbHandlerConfig {.importcpp: "daisy::MidiHandler<daisy::MidiUsbTransport>::Config",
                       bycopy.} = object
Source   Edit  
MidiUsbTransport {.importcpp: "daisy::MidiUsbTransport".} = object
Source   Edit  
NoteEvent = object
  number*: uint8
  velocity*: uint8
Source   Edit  
Pin {.importcpp: "daisy::Pin", bycopy.} = object
  port* {.importcpp: "port".}: GPIOPort
  pin* {.importcpp: "pin".}: uint8
Source   Edit  
UartPeriph {.importcpp: "daisy::UartHandler::Config::Peripheral", size: 4.} = enum
  USART_1, USART_2, USART_3, UART_4, UART_5, USART_6, UART_7, UART_8, LPUART_1
Source   Edit  

Procs

proc channel(event: MidiEvent): cint {.importcpp: "#.channel", nodecl,
                                       ...raises: [], tags: [], forbids: [].}
Source   Edit  
proc channelPressure(event: var MidiEvent): uint8 {.inline, ...raises: [],
    tags: [], forbids: [].}
Parse as channel pressure (0-127) Source   Edit  
proc controlChange(event: var MidiEvent): ControlChangeEvent {.inline,
    ...raises: [], tags: [], forbids: [].}
Parse as control change event Source   Edit  
proc hasEvents(midi: var MidiUartHandler): bool {.inline, ...raises: [], tags: [],
    forbids: [].}
Check if there are any UART MIDI events waiting Source   Edit  
proc hasEvents(midi: var MidiUsbHandler): bool {.inline, ...raises: [], tags: [],
    forbids: [].}
Check if there are any USB MIDI events waiting Source   Edit  
proc initMidi(midi: var MidiUsbHandler) {....raises: [], tags: [], forbids: [].}

Backward-compatible alias for initMidiUsb

Deprecated: Use initMidiUsb() for clarity

Source   Edit  
proc initMidiUart(midi: var MidiUartHandler; config: MidiUartHandlerConfig) {.
    ...raises: [], tags: [], forbids: [].}

Initialize MIDI handler for UART (TRS/DIN) MIDI

Usage:

var midi: MidiUartHandler
var config = newMidiUartConfig()

# Configure UART pins (example for USART1)
config.transport_config.periph = USART_1
config.transport_config.rx = initPin(PIN_PORTB, 7)
config.transport_config.tx = initPin(PIN_PORTB, 6)

initMidiUart(midi, config)

Source   Edit  
proc initMidiUsb(midi: var MidiUsbHandler) {....raises: [], tags: [], forbids: [].}

Initialize MIDI handler for USB MIDI

Usage:

var midi: MidiUsbHandler
initMidiUsb(midi)

Source   Edit  
proc initPin(port: GPIOPort; pin: uint8): Pin {.importcpp: "daisy::Pin(@)",
    constructor, header: "hid/midi.h", ...raises: [], tags: [], forbids: [].}
Source   Edit  
proc listen(midi: var MidiUartHandler) {.inline, ...raises: [], tags: [],
    forbids: [].}

Process incoming UART MIDI data (call this regularly in your loop)

Note: This also handles UART error recovery

Source   Edit  
proc listen(midi: var MidiUsbHandler) {.inline, ...raises: [], tags: [],
                                        forbids: [].}
Process incoming USB MIDI data (call this regularly in your loop) Source   Edit  
proc makeMidiControlChange(channel: uint8; ccNumber: uint8; value: uint8): array[
    3, uint8] {.inline, ...raises: [], tags: [], forbids: [].}

Create a MIDI Control Change message

Parameters: channel: MIDI channel (0-15, where 0 = channel 1) ccNumber: Controller number (0-127) value: Controller value (0-127)

Returns: 3-byte array containing the MIDI message

Source   Edit  
proc makeMidiNoteOff(channel: uint8; note: uint8; velocity: uint8 = 0): array[3,
    uint8] {.inline, ...raises: [], tags: [], forbids: [].}

Create a MIDI Note Off message

Parameters: channel: MIDI channel (0-15, where 0 = channel 1) note: Note number (0-127) velocity: Release velocity (0-127, default 0)

Returns: 3-byte array containing the MIDI message

Source   Edit  
proc makeMidiNoteOn(channel: uint8; note: uint8; velocity: uint8): array[3,
    uint8] {.inline, ...raises: [], tags: [], forbids: [].}

Create a MIDI Note On message

Parameters: channel: MIDI channel (0-15, where 0 = channel 1) note: Note number (0-127, where 60 = C4) velocity: Note velocity (1-127, 0 is treated as note off)

Returns: 3-byte array containing the MIDI message

Example:

let noteOn = makeMidiNoteOn(0, 60, 100)  # Channel 1, C4, velocity 100
midi.sendMessage(noteOn[0].addr, 3)

Source   Edit  
proc makeMidiPitchBend(channel: uint8; value: int16): array[3, uint8] {.inline,
    ...raises: [], tags: [], forbids: [].}

Create a MIDI Pitch Bend message

Parameters: channel: MIDI channel (0-15, where 0 = channel 1) value: Pitch bend value (-8192 to +8191, where 0 is center)

Returns: 3-byte array containing the MIDI message

Source   Edit  
proc makeMidiProgramChange(channel: uint8; program: uint8): array[2, uint8] {.
    inline, ...raises: [], tags: [], forbids: [].}

Create a MIDI Program Change message

Parameters: channel: MIDI channel (0-15, where 0 = channel 1) program: Program number (0-127)

Returns: 2-byte array containing the MIDI message

Source   Edit  
proc messageType(event: MidiEvent): MidiMessageType {.importcpp: "#.type",
    nodecl, ...raises: [], tags: [], forbids: [].}
Source   Edit  
proc newMidiUartConfig(): MidiUartHandlerConfig {.
    importcpp: "daisy::MidiUartHandler::Config()", constructor,
    header: "hid/midi.h", ...raises: [], tags: [], forbids: [].}
Source   Edit  
proc newMidiUartTransportConfig(): MidiUartTransportConfig {.
    importcpp: "daisy::MidiUartTransport::Config()", constructor,
    header: "hid/midi.h", ...raises: [], tags: [], forbids: [].}
Source   Edit  
proc newMidiUsbConfig(): MidiUsbHandlerConfig {.
    importcpp: "daisy::MidiHandler<daisy::MidiUsbTransport>::Config()",
    constructor, header: "hid/midi.h", ...raises: [], tags: [], forbids: [].}
Source   Edit  
proc note(event: var MidiEvent): NoteEvent {.inline, ...raises: [], tags: [],
    forbids: [].}
Parse as note event (for NoteOn/NoteOff messages) Source   Edit  
proc pitchBend(event: var MidiEvent): int16 {.inline, ...raises: [], tags: [],
    forbids: [].}
Parse as pitch bend (-8192 to +8191) Source   Edit  
proc popEvent(midi: var MidiUartHandler): MidiEvent {.inline, ...raises: [],
    tags: [], forbids: [].}
Get the next UART MIDI event from the queue Source   Edit  
proc popEvent(midi: var MidiUsbHandler): MidiEvent {.inline, ...raises: [],
    tags: [], forbids: [].}
Get the next USB MIDI event from the queue Source   Edit  
proc programChange(event: var MidiEvent): uint8 {.inline, ...raises: [], tags: [],
    forbids: [].}
Parse as program change (0-127) Source   Edit  
proc sendMessage(midi: var MidiUartHandler; bytes: ptr uint8; size: int) {.
    inline, ...raises: [], tags: [], forbids: [].}

Send raw MIDI bytes over UART (TRS/DIN MIDI)

Parameters: bytes: Pointer to MIDI message bytes size: Number of bytes to send

Example:

# Send note on: channel 1, note 60 (C4), velocity 100
var noteOn = [0x90'u8, 60, 100]
midi.sendMessage(noteOn[0].addr, 3)

# Send control change: channel 1, CC 7 (volume), value 127
var cc = [0xB0'u8, 7, 127]
midi.sendMessage(cc[0].addr, 3)

Source   Edit  
proc sendMessage(midi: var MidiUsbHandler; bytes: ptr uint8; size: int) {.
    inline, ...raises: [], tags: [], forbids: [].}

Send raw MIDI bytes over USB

Parameters: bytes: Pointer to MIDI message bytes size: Number of bytes to send

Example:

# Send note on: channel 1, note 60 (C4), velocity 100
var noteOn = [0x90'u8, 60, 100]
midi.sendMessage(noteOn[0].addr, 3)

# Send note off
var noteOff = [0x80'u8, 60, 0]
midi.sendMessage(noteOff[0].addr, 3)

Source   Edit