Module: midi Synopsis: Raw midi interface Author: Keith Playford Copyright: Original Code is Copyright (c) 1995-2004 Functional Objects, Inc. All rights reserved. License: Functional Objects Library Public License Version 1.0 Dual-license: GNU Lesser General Public License Warranty: Distributed WITHOUT WARRANTY OF ANY KIND define sealed concrete class () constant slot midi-id :: , required-init-keyword: id:; constant slot midi-channels :: , required-init-keyword: channels:; slot midi-open? :: = #f; slot midi-handle :: ; end class; define sealed concrete class () constant slot midi-number :: , required-init-keyword: number:; slot midi-current-instrument :: , required-init-keyword: current-instrument:; end class; define constant $default-instrument = find-midi-instrument("Acoustic Grand Piano"); define constant $midi-mapper-device = make(, id: as(, $MIDIMAPPER), channels: map-as(, method (number) make(, number: number, current-instrument: $default-instrument) end, range(from: 0, below: 16))); define sealed method default-midi-device () => (md :: ) $midi-mapper-device end method; define sealed method open-midi-device (md :: ) => () if (midi-open?(md)) error("The MIDI device %= cannot be opened because it is already open.", md); end; let (code, handle) = midiOutOpen(midi-id(md), 0, 0, 0); if (code ~== 0) error("Failed to open midi device %=: %s", md, midi-error-string(code)); end; midi-handle(md) := handle; midi-open?(md) := #t; end method; define sealed method reset-midi-device (md :: ) => () if (~midi-open?(md)) error("The MIDI device %= cannot be reset because it is not open.", md); end; midiOutReset(midi-handle(md)); end method; define sealed method close-midi-device (md :: ) => () if (~midi-open?(md)) error("The MIDI device %= cannot be closed because it is not open.", md); end; midi-open?(md) := #f; midiOutClose(midi-handle(md)); end method; define sealed method select-midi-instrument (md :: , mc :: , mv :: ) => () if (~midi-open?(md)) error("The MIDI device %= cannot used because it is not open.", md); end; send-midi-message (midi-handle(md), $MIDI-patch, midi-number(mc), midi-number(mv), 0); end method; define sealed method midi-on (md :: , mc :: , pitch :: , velocity :: ) => () if (~midi-open?(md)) error("The MIDI device %= cannot used because it is not open.", md); end; send-midi-message (midi-handle(md), $MIDI-on, midi-number(mc), pitch, velocity); end method; define sealed method midi-off (md :: , mc :: , pitch :: ) => () if (~midi-open?(md)) error("The MIDI device %= cannot used because it is not open.", md); end; send-midi-message (midi-handle(md), $MIDI-off, midi-number(mc), pitch, 0); end method; define sealed method midi-pitch-bend (md :: , mc :: , bend :: ) => () if (~midi-open?(md)) error("The MIDI device %= cannot used because it is not open.", md); end; let lo = logand(bend, #x7f); let hi = ash(bend, -7); send-midi-message (midi-handle(md), $MIDI-pitch-bend, midi-number(mc), lo, hi); end method; //// Utilities define constant $max-error-string-size = 255; define method midi-error-string (id :: ) => (message :: ) with-stack-structure (buf :: , element-count: $max-error-string-size) midiOutGetErrorText(id, buf, $max-error-string-size); as(, buf); end; end method; define method send-midi-message (handle :: , status :: , channel :: , data1 :: , data2 :: ) => () let code = midiOutShortMsg(handle, logior(status, channel, ash(data1, 8), ash(data2, 16))); if (code ~== 0) error("Error sending MIDI message: %s.", midi-error-string(code)); end; code end method; define method send-midi-message (md :: , status :: , channel :: , data1 :: , data2 :: ) => () send-midi-message (midi-handle(md), status, midi-number(channel), data1, data2); end method; // eof