undermidi
An LFE MIDI Port Server
Dependencies & Setup
This application assumes that the following are on your system:
git- GNU
make - A modern install of Erlang (v20+)
- rebar3 (Erlang build tool)
- Golang
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))))