undermidi

Build StatusLFE VersionsErlang Versions

An LFE MIDI Port Server

Dependencies & Setup

This application assumes that the following are on your system:

This project's rebar.config.script will set the required Go environment variables.

Build & Run

Build the required Go MIDI server and compile the LFE:

$ make clean && make

Start up the LFE REPL:

$ rebar3 lfe repl

Start the app:

lfe> (undermidi:start)

Depending upon the configured log level, you may see a fair amount of output, including the Go MIDI server being started.

API

Various application-level functions (i.e., "admin stuff") is available in the undermidi* modules, e.g.:

lfe> (undermidi:ping)
pong

Play the example MIDI code (requires the first MIDI device on your system, index 0, to be connected to a MIDI device, hardware or software, listening on channel 1):

lfe> (undermidi:example)
ok

For actually using undermidi to generate music, the um* modules are used:

lfe> (include-lib "undermidi/include/notes.lfe")
lfe> (progn
(um:set-device 0)
(um:set-channel 0)
(set bpm 66)
(set veloc 50)
(set dur (trunc (* 4 (/ 60 bpm) 1000)))
(set ch1 (list (Bb2) (F3) (Db4)))
(um:play-chord ch1 veloc dur))

A few more:

lfe> (progn
(set ch2 (list (Ab2) (F3) (C4)))
(set ch3 (list (Db2) (Db3) (Ab3)))
(set ch4 (list (Gb2) (Gb3) (Bb3)))
(set ch5 (list (C3) (Ab3) (Eb4)))
(set ch6 (list (Db3) (Ab3) (Eb4)))
(set ch7 (list (C3) (Ab3) (F4)))
(set ch8 (list (F2) (Db3) (Ab3)))
(set ch9 (list (Ab2) (F3) (C4)))
(set ch10 (list (Gb2) (Eb3) (Bb3)))
(set ch11 (list (Eb2) (Gb3) (C4)))
'ok)
lfe> (list-comp ((<- ch (list ch1 ch2 ch3 ch4
ch1 ch2 ch3 ch4
ch1 ch5 ch6 ch7
ch1 ch5 ch6 ch2
ch1 ch8 ch4 ch9
ch1 ch8 ch10 ch11)))
(um:play-chord ch veloc dur)
'ok)

Fans of Max Richter will recognise this immediately :-)

Macros

The midi macros is provided as a typing convenience for generating MIDI messages:

lfe> (include-lib "undermidi/include/macros.lfe")
lfe> (midi (midimsg:device 0)
(midimsg:channel 0))
#(midi
#(batch
(#(device 0)
#(channel 0))))

The send macro from the same include saves typing when sending many messages to the Go MIDI server. Additionally, macros for MIDI notes are provided via another include:

lfe> (include-lib "undermidi/include/notes.lfe")
lfe> (set veloc 40)
lfe> (progn
(send (midimsg:device 0)
(midimsg:channel 0))
(send (midimsg:note-on (Bb1) veloc))
(timer:sleep 2000)
(send (midimsg:note-off (Bb1))))

By default, batched messages sent to the MIDI server will be executed serially, in the order they are defined in the batch.

To execute these in parallel, one must annotate the messages appropriately:

lfe> (midi-parallel (midimsg:note-on (Bb2) veloc)
(midimsg:note-on (F3) veloc)
(midimsg:note-on (Db4) veloc))
#(midi
#(batch
(#(id #B(1 5 108 38 116 193 73 249 156 64 81 98 200 199 193 170))
#(parallel? true)
#(messages
(#(note_on (#(pitch 46) #(velocity 40)))
#(note_on (#(pitch 53) #(velocity 40)))
#(note_on (#(pitch 61) #(velocity 40))))))))

You may send this message with either the send-parallel or the shorter cast macro:

lfe> (progn
(cast (midimsg:note-on (Bb2) volume)
(midimsg:note-on (F3) volume)
(midimsg:note-on (Db4) volume))
(timer:sleep 4000)
(cast (midimsg:note-off (Bb2))
(midimsg:note-off (F3))
(midimsg:note-off (Db4))))