- [Topic]
- Midishare
Common Music supports reading and writing MIDI data in real time and
non-real time using
Grame's Midishare. CM
supports Midishare on OS X and Linux in OpenMCL and CMUCL. The support
consists of the stream classes
midistream-stream
and
player-stream
that manage IO
connections and a handful of auxiliary functions for opening Midishare
streams and working with Midishare's MidiEv
foreign object. This low-level object is most appropriate for
interactive, real-time work
using rts
and set-receiver!.
It is more convenient to use
midi objects for non-real time output
with the events function.
The Midishare stream
- [Class]
midishare-stream
A subclass
of midi-stream
that
implements direct-to-driver MIDI io. This class is automatically
chosen when you specify a stream with a ".ms" extension. The name of
the stream will become the "client name" used by Midishare. The
convenience
functions midishare-open
and midishare-close
are
provided for the typical case of a single input/output pair using
"Common Music" as the client name.
midishare-stream
supports the following slot initializations:
:connections
list-
A list of two strings specifying the names of the input and output
Midishare client applications to connect to. If a connections list is
not specified it will default to the value of the global variable
*midi-connections*
.
Midishare stream functions
A connection between CM and Midishare must first be established before reading or writing any MIDI data. The easiest way to make this connection is to use the following convenience functions.
Opens a connection to a Midishare stream. If name is not provided it defaults to "midi-port.ms" with "Common Music" set as the client application name.
Closes a connection to optional stream, which defaults to the stream named "midi-port.ms" if it is not provided.
Returns one of :in, :out or :inout if the stream is open, otherwise false. If stream is not provided it defaults to the stream named "midi-port.ms".
The Player stream
- [Class]
player-stream
A subclass
of midishare-stream
that implements input and output to a Midishare Player multi-track
sequencer application. A player-stream
is automatically
created when you specify a file with a .mp extension. The name of
the player stream will become the player's application name used by
Midishare.
player-stream
supports the following slot initializations:
:track
integer-
Events generated to a player are placed in the track number specified
by
:track
according to the value of:seq-mode
. If:seq-mode
is:replace
(the default) then the the track contents at:track
are replaced. If:seq-mode
is:add
then new tracks are created starting at:track
and incrementing by 1 each time theevents
function outputs to the player. :seq-mode
{:replace
|:add
}-
Determines if events sent to the player either replace the current
sequence or are added as a new track in the sequence. The default
value is
:replace
. :play
boolean-
If true then the player application is automatically started once
output to the player has concluded. If
:play
is false then the player is not automatically started. In either case a player-stream can be controlled interactively in the Lisp interpreter using the functionsplayer-start
,player-stop
,player-pause
andplayer-cont
. The default value of:play
is true. :tempo
bmp- The initial tempo of the player, specified in beats per minute. The default value is tempo 60.
Player stream functions
- [Function]
- (
player-cont
stream)
Continues playing the Player application associated with the
player-stream
stream.
- [Function]
- (
player-load-midifile
stream file)
Loads the MIDI file file into the Player application associated with the player-stream
stream.
- [Function]
- (
player-mute
stream track)
Mutes track number in the Player application associated
with the player-stream
stream.
- [Function]
- (
player-pause
stream)
Pauses playing the Player application associated with the player-stream
stream.
- [Function]
- (
player-save-midifile
stream file)
Saves the sequence in the Player application associated with the player-stream
stream to the MIDI file file into
- [Function]
- (
player-set-tempo
stream tempo)
Sets the tempo of the Player application associated
with the player-stream
stream to tempo, in beats per minute.
The default tempo is 60.
- [Function]
- (
player-solo
stream track)
Solos track number in the Player application associated
with the player-stream
stream.
- [Function]
- (
player-start
stream)
Starts playing the Player application associated with the player-stream
stream.
- [Function]
- (
player-stop
stream)
Stops playing the Player application associated with the player-stream
stream.
- [Function]
- (
player-unmute
stream track)
Unmutes track number in the Player application associated
with the player-stream
stream.
- [Function]
- (
player-unsolo
stream track)
Unsolos track number in the Player application associated
with the player-stream
stream.
The MidiEv foreign object
Once a connection between CM and Midishare has been established, MIDI data can be sent to and from Midishare ports in real time. For real time work it is best to work directly with the low-level MIDI objects that Midishare itself uses. These foreign objects are called MidiEvs.. Special care should be taken working with them because:
- they are not part of the Lisp data type system
- they are not managed by Lisp's garbage collection facility
- they are not trapped and handled by Lisp's error system
You are completely responsible for properly managing the MidiEv's you allocate and use. In some cases this may include explicit deallocation after a MidiEv has been sent or received. Be sure to consult the Midishare manual and the Midishare FFI for information about how to create, read, write and deallocate MidiEv structs.
Common Music adds two functions to Midishare's API:
ms:new
, a high level MidiEv constructor, and
ms:MidiPrintEv
, a printer
for MidiEv objects. These two functions allow low-level MidiEvs to be
manipulated in manner consistent with CLOS objects defined in CM. Note
that to reference functions in the Midishare API you must include
the package prefix ms:
in the function name.
- [Function]
(
ms:new
type {keyword value}*)
Allocates, initializes and returns a foreign Midishare event. Every type of MidiEv is identified by a unique integer type id, a Lisp constant (symbol) with an integer value. This value is followed by zero or more keyword parameters as appropriate for the type of MidiEv returned:
Keyword arguments applicable to all types of MidiEvs:
:port
integer- The reference number of the Midishare port to send the event to. Defaults to 0.
:chan
integer- The channel number to send the event to. Defaults to 0.
:date
integer- The time (in milliseconds) of the event. Defaults to 0.
typeNote
(0), typeKeyOn
(1), typeKeyOff
(2):
:pitch
integer- An integer key number 0-127. Defaults to 60.
:vel
integer- An integer velocity 0-127. Defaults to 60.
:dur
integer- Duration in milliseconds, defaults to 500. Only available
for
typeNote
.
typeKeyPress
(3):
:pitch
integer- An integer key number 0-127. Defaults to 60.
:pressure
integer- An integer pressure 0-127. Defaults to 0.
typeCtrlChange
(4):
:controller
integer- An integer controller 0-127. Defaults to 0.
:change
integer- An integer change 0-127. Defaults to 0.
typeProgChange
(5):
:program
integer- An integer program 0-127. Defaults to 0.
typeChanPress
(6):
:pressure
integer- An integer pressure 0-127. Defaults to 0.
typePitchBend
(7),
typePitchWheel
(7):
:bend
integer- An integer bend value -8192 to 8191. Defaults to 0 (no bend).
typeSongPos
(8):
:lsb
integer- An integer 0-127. Defaults to 0.
:msb
integer- An integer 0-127. Defaults to 0.
typeSongSel
(9):
:song
integer- An integer song 0-127. Defaults to 0.
typeClock
(10),
typeStart
(11),
typeContinue
(12),
typeStop
(13),
typeTune
(14),
typeActiveSens
(15),
typeReset
(16):
None
typeSysEx
(17):
:data
list- A list of data bytes. Do not include a leading #xF0 or tailing #xF7 in the list; these markers are added automatically by Midishare.
typeSeqNum
(134):
:number
integer- A sequence integer 0-127.
typeTextual
(135),
typeCopyright
(136),
typeSeqName
(137),
typeInstrName
(138),
typeLyric
(139),
typeMarker
(140),
typeCuePoint
(141):
:text
string- A text string, defaults to "".
typeChannelPrefix
(142):
:prefix
integer- A prefix integer 0-127, defaults to 0.
typeEndTrack
(143):
None
typeTempo
(144):
:tempo
integer- Tempo in quarter notes per minute, defaults to 120.
typeSMPTEOffset
(145):
:offset
list- A list of SMPTE integer offsets (hr min sec frame subframe).
typeTimeSign
(146):
:numerator
integer- The upper number of the time signature, defaults to 4.
:denominator
integer- The lower number of the time signature, defaults to 4.
:clocks
integer- Clocks per quarter, defaults to 24.
:32nds
integer- Thirty-seconds per quarter, defaults to 8.
typeKeySign
(147):
:sign
integer- The number of flats or sharps in the key signature -7 to 7, defaults to 0.
:mode
integer- An integer 0 or 1 where 0 means major and 1 means minor, defaults to 0.
The MIDI Meta message types 134-147 can appear in MIDI files but cannot be sent to an external synthesizer.
- [Function]
(
ms:MidiPrintEv
ev [stream])
Formats the message contents of ev to stream, which defaults to the standard output.
Example 2. Creating and printing a MidiEv.
(define ev (ms:new typeNote :chan 3 :dur 2000)) (ms:MidiPrintEv ev) #<MidiEv Note [0/3 0ms] 60 64 2000ms>
Accessing and modifying MidiEvs
To access values or set the fields of a MidiEv struct you use the functions provided by the MidiShare API. The more important constructors and accessors are listed here.
- [Function]
(
ms:MidiNewEv
typenum)
- [Function]
(
ms:MidiCopyEv
ev)
- [Function]
(
ms:MidiFreeEv
ev)
- [Function]
(
ms:evtype
ev [val])
- [Function]
(
ms:port
ev [val])
- [Function]
(
ms:chan
ev [val])
- [Function]
(
ms:date
ev [val])
- [Function]
(
ms:pitch
ev [val])
- [Function]
(
ms:dur
ev [val])
- [Function]
(
ms:vel
ev [val])
- [Function]
(
ms:bend
ev [val])
- [Function]
(
ms:pgm
ev [val])
- [Function]
(
ms:ctl
ev [val])
- [Function]
(
ms:val
ev [val])
- [Function]
(
ms:field
ev pos [val])
Example 1. Non-realtime Midishare output using midi objects.
(defun simp (len lb ub rhy dur amp) (process repeat len output (new midi :time (now) :keynum (between lb ub) :duration dur :amplitude amp) wait rhy)) (events (simp 20 60 80 .1 .15 .6) "midi-port.ms") #<midishare-stream "midi-port.ms"> (midishare-open?) ⇒ :inout
Example 2. Real-time output using MidiEv objects.
(defparameter *ms* (midishare-open )) (defun zzz (len knum wai vel) ;; a process that creates ms:typeNote objects (process for i below len for k = knum then (between knum (+ knum 12)) ;; IMPORTANT: the 'at (now)' subclause in the output statement ;; is required because MidiEv is a foreign type and has no ;; object-time CLOS method. output (ms:new typeNote :dur 100 :pitch k :vel (round (interp i 0 vel (- len 1) (* vel .3)))) at (now) wait wai)) ;;; start the realtime scheduler... (rts nil *ms*) ;;; ...eval this sprout multiple times, plays in real time... (sprout (zzz 20 (between 40 80) .1 (between 60 80)) ) ;;; ...then stop the rts (rts-stop )
Example 3. Setting and clearing a Midishare receiver.
;;; Set a receiver that prints out incoming events (set-receiver! (lambda (ev) (ms:MidiPrintEv ev)) *ms*) ;;; play the keyboard, then clear the receiver (remove-receiver! *ms*)
Example 4. Receiving and real-time scheduling together.
(defun trigger (ev) ;; triggers zzz process in real time ;; if ev is NoteOn otherwise deallocate event (if (= (ms:evType ev) 1) (sprout (zzz 20 (ms:pitch ev) .1 (ms:vel ev))) (ms:MidiFreeEv ev))) ;;; start rts and receiver running (rts nil *ms*) (set-receiver! #'trigger *ms*) ;;; ...now play the keyboard to sprout processes... ;;; ...then stop rts and clear receiver (rts-stop) (remove-receiver! *ms*)
See also:
events
[Function]- MIDI [Topic]
- Portmidi [Topic]
rts
[Function]set-receiver!
[Function]