Skip to content

Krotki/midvi

Repository files navigation

midvi

A cross-platform MIDI library for V, supporting both MIDI 1.0 and MIDI 2.0 (UMP).

Backends are loaded at runtime — no compile-time C headers or static linking required.

Platform Backends (tried in order)
Linux JACK, ALSA
macOS CoreMIDI
Windows Windows MIDI Services (MIDI 2.0), WinMM

Features

  • MIDI 1.0 send/receive with callback or polling
  • MIDI 2.0 / UMP send/receive
  • Hot-plug port observation (device added/removed callbacks)
  • Virtual port creation
  • Automatic backend discovery — falls back gracefully if a backend is unavailable

Installing as a V module

From the V package manager:

v install https://github.com/krotki/midvi

Manually:

git clone https://github.com/krotki/midvi ~/.vmodules/midvi

Then import in your code:

import midvi
import midvi.clients

V usage

MIDI output

import midvi
import midvi.clients

fn main() {
    mut out := midvi.MidiOut.new(clients.discover_midi_out('my-app') or {
        eprintln(err)
        exit(1)
    })
    defer { out.close() }

    // List available ports
    for i, name in out.get_ports() {
        println('[${i}] ${name}')
    }

    out.open_port(0, 'out') or { panic(err) }

    // Send a middle-C note on channel 1
    out.send_message(midvi.note_on(0, 60, 100))
    // ... wait ...
    out.send_message(midvi.note_off(0, 60, 0))
}

MIDI input (callback)

import midvi
import midvi.clients
import time

fn main() {
    mut inp := midvi.MidiIn.new(clients.discover_midi_in('my-app') or {
        eprintln(err)
        exit(1)
    })
    defer { inp.close() }

    inp.open_port(0, 'in') or { panic(err) }

    // MIDI 1.0 events
    inp.set_callback(fn (ev midvi.MidiEvent) {
        ch := ev.channel() + 1
        match ev.msg_type() {
            .note_on        { println('Note On  ch=${ch} note=${ev.data1} vel=${ev.data2}') }
            .note_off       { println('Note Off ch=${ch} note=${ev.data1}') }
            .control_change { println('CC       ch=${ch} ctrl=${ev.data1} val=${ev.data2}') }
            else            { println('MIDI     status=0x${ev.status:02x}') }
        }
    })

    // MIDI 2.0 / UMP events (if the backend supports it)
    inp.set_ump_callback(fn (ev midvi.UmpEvent) {
        println('UMP type=${ev.ump.msg_type()} group=${ev.ump.group()}')
    })

    // Block until Ctrl+C
    for { time.sleep(10 * time.millisecond) }
}

MIDI input (polling)

for {
    if ev := inp.get_message() {
        println('received: status=0x${ev.status:02x}')
    }
    time.sleep(1 * time.millisecond)
}

Hot-plug port observation

import midvi
import midvi.clients
import time

fn main() {
    mut obs := midvi.MidiPortObserver.new(clients.discover_midi_observer('my-app') or {
        eprintln(err)
        exit(1)
    })
    defer { obs.close() }

    obs.set_input_callback(fn (ev midvi.PortEvent) {
        match ev.event {
            .added   { println('input added:   ${ev.name}') }
            .removed { println('input removed: ${ev.name}') }
        }
    })

    obs.set_output_callback(fn (ev midvi.PortEvent) {
        match ev.event {
            .added   { println('output added:   ${ev.name}') }
            .removed { println('output removed: ${ev.name}') }
        }
    })

    for { time.sleep(10 * time.millisecond) }
}

Helper constructors

midvi.note_on(channel, note, velocity)         // -> MidiEvent
midvi.note_off(channel, note, velocity)        // -> MidiEvent
midvi.control_change(channel, controller, val) // -> MidiEvent
midvi.program_change(channel, program)         // -> MidiEvent
midvi.pitch_bend(channel, value)               // -> MidiEvent  (value: -8192..8191)

Using from C / C++

The capi/ directory contains everything needed:

File Purpose
capi/capi.v V module that exports the full C API via @[export]
capi/midvi.h C/C++ header — the only file consumers need to include

1. Build the shared library

Pre-built binaries for all platforms and architectures are attached to each GitHub release. To build from source:

# Linux
v -shared -o libmidvi.so capi/

# macOS
v -shared -o libmidvi.dylib capi/

# Windows (PowerShell)
v -shared -o midvi.dll capi/

Runtime requirements

The shared library loads backends dynamically at startup — no static linking is needed. The relevant runtime libraries must be present on the system:

Platform Required at runtime
Linux libasound.so.2 (ALSA) and/or libjack.so.0 (JACK)
macOS CoreMIDI.framework (always available)
Windows winmm.dll (always available); Windows.Devices.Midi2 for MIDI 2.0 (Windows 11+)

License

MIT

About

Cross platform MIDI library

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors