- [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:
:connectionslist-
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:
:trackinteger-
Events generated to a player are placed in the track number specified
by
:trackaccording to the value of:seq-mode. If:seq-modeis:replace(the default) then the the track contents at:trackare replaced. If:seq-modeis:addthen new tracks are created starting at:trackand incrementing by 1 each time theeventsfunction 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. :playboolean-
If true then the player application is automatically started once
output to the player has concluded. If
:playis 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-pauseandplayer-cont. The default value of:playis true. :tempobmp- The initial tempo of the player, specified in beats per minute. The default value is tempo 60.
Player stream functions
- [Function]
- (
player-contstream)
Continues playing the Player application associated with the
player-stream
stream.
- [Function]
- (
player-load-midifilestream file)
Loads the MIDI file file into the Player application associated with the player-stream
stream.
- [Function]
- (
player-mutestream track)
Mutes track number in the Player application associated
with the player-stream
stream.
- [Function]
- (
player-pausestream)
Pauses playing the Player application associated with the player-stream
stream.
- [Function]
- (
player-save-midifilestream file)
Saves the sequence in the Player application associated with the player-stream
stream to the MIDI file file into
- [Function]
- (
player-set-tempostream 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-solostream track)
Solos track number in the Player application associated
with the player-stream
stream.
- [Function]
- (
player-startstream)
Starts playing the Player application associated with the player-stream
stream.
- [Function]
- (
player-stopstream)
Stops playing the Player application associated with the player-stream
stream.
- [Function]
- (
player-unmutestream track)
Unmutes track number in the Player application associated
with the player-stream
stream.
- [Function]
- (
player-unsolostream 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:newtype {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:
:portinteger- The reference number of the Midishare port to send the event to. Defaults to 0.
:chaninteger- The channel number to send the event to. Defaults to 0.
:dateinteger- The time (in milliseconds) of the event. Defaults to 0.
typeNote
(0), typeKeyOn (1), typeKeyOff (2):
:pitchinteger- An integer key number 0-127. Defaults to 60.
:velinteger- An integer velocity 0-127. Defaults to 60.
:durinteger- Duration in milliseconds, defaults to 500. Only available
for
typeNote.
typeKeyPress (3):
:pitchinteger- An integer key number 0-127. Defaults to 60.
:pressureinteger- An integer pressure 0-127. Defaults to 0.
typeCtrlChange (4):
:controllerinteger- An integer controller 0-127. Defaults to 0.
:changeinteger- An integer change 0-127. Defaults to 0.
typeProgChange (5):
:programinteger- An integer program 0-127. Defaults to 0.
typeChanPress (6):
:pressureinteger- An integer pressure 0-127. Defaults to 0.
typePitchBend (7),
typePitchWheel (7):
:bendinteger- An integer bend value -8192 to 8191. Defaults to 0 (no bend).
typeSongPos (8):
:lsbinteger- An integer 0-127. Defaults to 0.
:msbinteger- An integer 0-127. Defaults to 0.
typeSongSel (9):
:songinteger- 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):
:datalist- 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):
:numberinteger- A sequence integer 0-127.
typeTextual (135),
typeCopyright (136),
typeSeqName (137),
typeInstrName (138),
typeLyric (139),
typeMarker (140),
typeCuePoint (141):
:textstring- A text string, defaults to "".
typeChannelPrefix (142):
:prefixinteger- A prefix integer 0-127, defaults to 0.
typeEndTrack (143):
None
typeTempo (144):
:tempointeger- Tempo in quarter notes per minute, defaults to 120.
typeSMPTEOffset (145):
:offsetlist- A list of SMPTE integer offsets (hr min sec frame subframe).
typeTimeSign (146):
:numeratorinteger- The upper number of the time signature, defaults to 4.
:denominatorinteger- The lower number of the time signature, defaults to 4.
:clocksinteger- Clocks per quarter, defaults to 24.
:32ndsinteger- Thirty-seconds per quarter, defaults to 8.
typeKeySign (147):
:signinteger- The number of flats or sharps in the key signature -7 to 7, defaults to 0.
:modeinteger- 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:MidiPrintEvev [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:MidiNewEvtypenum)
- [Function]
(ms:MidiCopyEvev)
- [Function]
(ms:MidiFreeEvev)
- [Function]
(ms:evtypeev [val])
- [Function]
(ms:portev [val])
- [Function]
(ms:chanev [val])
- [Function]
(ms:dateev [val])
- [Function]
(ms:pitchev [val])
- [Function]
(ms:durev [val])
- [Function]
(ms:velev [val])
- [Function]
(ms:bendev [val])
- [Function]
(ms:pgmev [val])
- [Function]
(ms:ctlev [val])
- [Function]
(ms:valev [val])
- [Function]
(ms:fieldev 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]