Topic
Portmidi
Common Music supports reading and writing MIDI messages to and from
Portmidi, an
open-source, cross-platform MIDI device library. CM supports Portmidi
on OS X and Linux in OpenMCL, SBCL and CMUCL. The support consists of
a portmidi-stream class to
manage different Portmidi input/output devices and a handful of
auxiliary functions for querying Portmidi about its current
configuration and for accessing its millisecond timer. To work with
Portmidi you must compile the Portmidi and Porttime libraries as
shared libs (libport*.dylib on OS X and libport*.so on Linux) and install in
/usr/local/lib.
Note also that on Linux you may have to load the snd-seq-* kernel
modules before starting CM with Portmidi. These modules are not loaded
by default, perhaps something like this will work:
$ modprobe snd_seq_midi snd_seq_oss snd_seq_midi_emul
portmidi-stream
Manages resources and connections between CM and Portmidi input and
output devices. This class is automatically chosen when you specify a
stream with a .pm extension. The convenience
functions portmidi-open
and portmidi-close are
provided for the typical case of a single input/output pair. Opening
a portmidi-stream initializes the library and starts the millisecond
timer if they have not been initialized.
portmidi-stream supports the
following slot initializations:
:input {string | integer | boolean}
*portmidi-default-input*.
:output {string | integer | boolean}
*portmidi-default-output*.
:latency integer
*portmidi-default-latency*.
:receive-mode {:message | :raw}
:message then the receiver
hook is passed two values: the
incoming MIDI message and
its millisecond timestamp. If the value
is :raw then the hook is passed the
Portmidi event buffer (a pointer to a foreign array) and the number of
events read. See the EventBuffer functions
in portmidi.lisp for more
information about reading raw events from Portmidi.
:inbuf-size integer
*portmidi-default-inbuf-size*.
:outbuf-size integer
*portmidi-default-outbuf-size*.
:filter integer
If no value is specified the filter defaults to the global variable
pm:filt-active pm:filt-sysex pm:filt-clock pm:filt-play pm:filt-f9 pm:filt-fd pm:filt-reset pm:filt-note pm:filt-channel-aftertouch pm:filt-poly-aftertouch pm:filt-program pm:filt-control pm:filt-pitchbend pm:filt-mtc filt-song-position pm:filt-song-select pm:filt-tune pm:filt-tick pm:filt-undefined pm:filt-realtime pm:filt-aftertouch pm:filt-systemcommon.
*portmidi-default-filter*.
:mask integer
*portmidi-default-mask*.
(portmidi-open . args )Opens a portmidi-stream called "midi-port.pm" according to the keyword initialization args passed to the stream. The function pm:GetDeviceInfo can be used to determine the names and ids of available Portmidi devices.
(portmidi-open?)
Tests to see if "midi-port.pm" has already
been opened, returns one of four possible
values: :in, :out, :inout or
false.
(portmidi-close)Closes the portmidi-stream called "midi-port.pm" if it is open, otherwise has no effect.
(portmidi-record! seq [stream])
Records MIDI data from the open input stream
into seq. Returns no
values. If stream is not supplied it defaults
to "midi-port.pm". If seq
already contains objects when the recording starts the new objects
will be inserted into the existing list. The first recorded object is
always placed at time 0.0 in seq. To stop recording
call portmidi-record! with false for seq.
(pm:CountDevices)Returns the number of available Portmidi devices.
(pm:GetDeviceInfo . id)If id is not specified, returns a list of the device ids (integer), names (string) device types (keyword) and open status (boolean) of each available Portmidi device. If id is specifed only information for that device is returned. Calling this function initializes the Portmidi library if it has not already been initialized. Note that MIDI devices added after the Portmidi library has been initializd will not be reflected in the list of descriptions.
(pm:GetDefaultInputDeviceID)Returns the id (integer) of Portmidi's default input device.
(pm:GetDefaultOutputDeviceID)Returns the id (integer) of Portmidi's default output device.
(pm:Time)
Returns the current millisecond time from Porttime. This function
should not be called unless a Portmidi stream has already been opened
or you have called the pm:TimeStart function to
initialize Porttime first.
*portmidi-default-input*The input device to open if none specified to portmidi-open. The default value is true. The variable can be respecified in a .cminit.lisp file.
*portmidi-default-output*The output device to open if none specified to portmidi-open. The default value is true. The variable can be respecified in a .cminit.lisp file.
*portmidi-default-latency*The portmidi latency if none specified to portmidi-open. The default value is 100 milliseconds. The variable can be respecified in a .cminit.lisp file.
*portmidi-default-outbuf-size*The input buffer size if none specified to portmidi-open. The default value is 64. The variable can be respecified in a .cminit.lisp file.
*portmidi-default-outbuf-size*The output buffer size if none specified to portmidi-open. The default value is 256. The variable can be respecified in a .cminit.lisp file.
*portmidi-default-filter*The message filter specification if none specified to portmidi-open. The default value is (). The variable can be respecified in a .cminit.lisp file.
*portmidi-default-mask*The channel mask specification if none specified to portmidi-open. The default value is 0. The variable can be respecified in a .cminit.lisp file.
;; Accessing Portmidi information and opening a Portmidi stream. (pm:CountDevices) ⇒ 4 (pm:GetDeviceInfo) ⇒ ((:id 0 :name "IAC Driver: IAC Bus 1" :type :input :open #f) (:id 1 :name "1x1: Port 1" :type :input :open #f) (:id 2 :name "IAC Driver: IAC Bus 1" :type :output :open #f) (:id 3 :name "1x1: Port 1" :type :output :open #f)) (pm:GetDefaultInputDeviceID) ⇒ 0 (pm:GetDefaultOutputDeviceID) ⇒ 2 (portmidi-open :output 3 :input #f) ⇒ #<portmidi-stream "midi-port.pm" (out:3)> (portmidi-open?) ⇒ :out (pm:Time) ⇒ 36993 (portmidi-close) ⇒ #t ;; Writing MIDI events. ;; midi output should be open with latency>0 (portmidi-open :output 3 :input #f :latency 500) ⇒ #<portmidi-stream "midi-port.pm" (out:3)> (define (playsome len lb ub) (process repeat len for d = (pick .4 .2 .2) output (new midi :time (now) :duration (* d 1.5) :keynum (between lb ub) :amplitude (odds .25 .8 .4)) wait d)) (events (playsome 20 60 90) "midi-port.pm") ⇒ #<portmidi-stream "midi-port.pm" (out:3)> (portmidi-close) ⇒ #t ;; Recording messages. (define *pm* (portmidi-open :latency 0 :input 1 :output 3)) (define myseq (new seq)) (portmidi-record! myseq) ;; stop recording (portmidi-record! #f) (list-objects myseq) 0. #i(midi time 0.0 keynum 60 duration 0.159 amplitude 0.46456692 channel 0) 1. #i(midi time 0.495 keynum 64 duration 0.147 amplitude 0.6771653 channel 0) 2. #i(midi time 0.834 keynum 67 duration 0.181 amplitude 0.61417323 channel 0) 3. #i(midi time 1.108 keynum 65 duration 0.144 amplitude 0.6062992 channel 0) 4. #i(midi time 1.47 keynum 62 duration 0.214 amplitude 0.61417323 channel 0) 5. #i(midi time 2.848 keynum 60 duration 0.155 amplitude 0.62204725 channel 0) 6. #i(midi time 2.837 keynum 64 duration 0.179 amplitude 0.72440946 channel 0) 7. #i(midi time 2.847 keynum 67 duration 0.191 amplitude 0.56692916 channel 0)