Coverage report: /home/ellis/comp/core/lib/dat/midi.lisp
Kind | Covered | All | % |
expression | 39 | 1007 | 3.9 |
branch | 2 | 88 | 2.3 |
Key
Not instrumented
Conditionalized out
Executed
Not executed
Both branches taken
One branch taken
Neither branch taken
1
;;; dat/midi.lisp --- MIDI data
3
;;; (c) copyright 2003 by Mathieu Chabanne, Camille Constant,
4
;;; Emmanuel Necibar and Stephanie Recco
6
;;; (c) copyright 2003 by Robert Strandh (strandh@labri.fr)
8
;;; (c) copyright 2007 by David Lewis, Marcus Pearce, Christophe
9
;;; Rhodes and contributors
11
;;; This library is free software; you can redistribute it and/or
12
;;; modify it under the terms of version 2 of the GNU Lesser General
13
;;; Public License as published by the Free Software Foundation.
15
;;; This library is distributed in the hope that it will be useful,
16
;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18
;;; Lesser General Public License for more details.
20
;;; You should have received a copy of the GNU Lesser General Public
21
;;; License along with this library; if not, write to the
22
;;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
23
;;; Boston, MA 02111-1307 USA.
25
;;; This file contains library for MIDI and Midifiles. Messages are
26
;;; represented as CLOS class instances in a class hierarchy that
27
;;; reflects interesting aspects of the messages themselves.
28
(in-package :dat/midi)
31
(defgeneric midifile-format (midifile))
32
(defgeneric (setf midifile-format) (format midifile))
33
(defgeneric midifile-division (midifile))
34
(defgeneric midifile-tracks (midifile))
37
(defgeneric message-time (message))
38
(defgeneric (setf message-time) (time message))
39
(defgeneric message-status (message))
40
(defgeneric message-channel (message))
41
(defgeneric message-key (message))
42
(defgeneric message-velocity (message))
43
(defgeneric message-tempo (message))
44
(defgeneric message-numerator (message))
45
(defgeneric message-denominator (message))
46
(defgeneric message-sf (message))
47
(defgeneric message-mi (message))
49
(defgeneric message-program (message))
52
(eval-when (:compile-toplevel :load-toplevel)
53
(defun string-code (s)
54
"compute the ASCII-based numerical value of the string [warning:
55
works only if the chars are coded in ASCII]"
57
(loop for i from 0 to (1- (length s))
58
do (setf v (+ (* v 256) (char-code (aref s i)))))
61
(defconstant +header-mthd+ #.(string-code "MThd"))
62
(defconstant +header-mtrk+ #.(string-code "MTrk"))
63
(defconstant +header-mthd-length+ 6 "value of the header MThd data's length")
65
(defparameter *midi-input* nil "stream for reading a Midifile")
66
(defparameter *input-buffer* '() "used for unreading bytes from *midi-input")
67
(defparameter *midi-output* nil "stream for writing a Midifile")
69
(define-condition unknown-event ()
70
((status :initarg :status :reader error-status)
71
(data-byte :initform "" :initarg :data-byte :reader data-byte))
72
(:documentation "condition when the event does not exist in the library"))
74
(define-condition header ()
75
((header-type :initarg :header :reader header-type))
76
(:documentation "condition when the header is not correct"))
78
(defun read-next-byte ()
79
"read an unsigned 8-bit byte from *midi-input* checking for unread bytes"
82
(read-byte *midi-input*)))
84
(defun unread-byte (byte)
85
"unread a byte from *midi-input*"
86
(push byte *input-buffer*))
88
(defun write-bytes (&rest bytes)
89
"write an arbitrary number of bytes to *midi-output*"
90
(mapc #'(lambda (byte) (write-byte byte *midi-output*)) bytes))
92
(defun read-fixed-length-quantity (nb-bytes)
93
"read an unsigned integer of nb-bytes bytes from *midi-input*"
95
for i from 1 to nb-bytes
96
do (setf result (logior (ash result 8) (read-next-byte)))
97
finally (return result)))
99
(defun write-fixed-length-quantity (quantity nb-bytes)
100
"write an unsigned integer of nb-bytes bytes to *midi-output*"
101
(unless (zerop nb-bytes)
102
(write-fixed-length-quantity (ash quantity -8) (1- nb-bytes))
103
(write-bytes (logand quantity #xff))))
105
(defmacro with-midi-input ((pathname &rest open-args &key &allow-other-keys) &body body)
106
"execute body with *midi-input* assigned to a stream from pathname"
107
`(with-open-file (*midi-input* ,pathname
108
:direction :input :element-type '(unsigned-byte 8)
112
(defmacro with-midi-output ((pathname &rest open-args &key &allow-other-keys) &body body)
113
"execute body with *midi-output* assigned to a stream from pathname"
114
`(with-open-file (*midi-output* ,pathname
115
:direction :output :element-type '(unsigned-byte 8)
119
(defun read-variable-length-quantity ()
120
"read a MIDI variable length quantity from *midi-input*"
121
(loop with result = 0
123
do (setf byte (read-next-byte)
124
result (logior (ash result 7) (logand byte #x7f)))
126
finally (return result)))
128
(defun write-variable-length-quantity (quantity &optional (termination 0))
129
(when (> quantity 127)
130
(write-variable-length-quantity (ash quantity -7) #x80))
131
(write-bytes (logior (logand quantity #x7f) termination)))
133
(defun length-of-variables-length-quantity (quantity)
134
(1+ (if (< quantity 128)
136
(length-of-variables-length-quantity (ash quantity -7)))))
138
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
140
;;; MIDI file representation
142
(defclass midifile ()
143
((format :initarg :format :reader midifile-format)
144
(division :initarg :division :reader midifile-division)
145
(tracks :initarg :tracks :reader midifile-tracks))
146
(:documentation "the class that represents a Midifile in core"))
148
(defparameter *status* nil "the status while reading an event")
149
(defparameter *running-status* nil "the running status while reading an event")
150
(defparameter *dispatch-table* (make-array 256 :initial-element nil)
151
"given values of status (and perhaps data1), find a class to create")
153
(defun read-message ()
154
"read a message without time indication from *midi-input*"
155
(let ((classname-or-subtype (aref *dispatch-table* *status*)))
156
(unless classname-or-subtype
157
(error (make-condition 'unknown-event
159
(if (symbolp classname-or-subtype)
160
(make-instance classname-or-subtype)
161
(let* ((data-byte (read-next-byte))
162
(classname (aref classname-or-subtype data-byte)))
164
(error (make-condition 'unknown-event
166
:data-byte data-byte)))
167
(unread-byte data-byte)
168
(make-instance classname)))))
170
(defparameter *time* 0 "accumulated time from the start of the track")
172
(defun read-timed-message ()
173
"read a message preceded with a delta-time indication"
174
(let ((delta-time (read-variable-length-quantity))
175
(status-or-data (read-next-byte)))
176
(if (>= status-or-data #x80)
177
(progn (setf *status* status-or-data)
178
(when (<= *status* #xef)
179
(setf *running-status* *status*)))
180
(progn (unread-byte status-or-data)
181
(setf *status* *running-status*)))
182
(let ((message (read-message)))
183
(fill-message message)
184
(setf (message-time message) (incf *time* delta-time))
187
(defun write-timed-message (message)
188
"write a message preceded with a delta-time indication"
189
(write-variable-length-quantity (- (message-time message) *time*))
190
(setf *time* (message-time message))
191
(write-message message))
194
"read a track as a list of timed messages, excluding the end-of-track message"
195
(let ((type (read-fixed-length-quantity 4))
196
(length (read-fixed-length-quantity 4)))
197
(declare (ignore length))
198
(unless (= type +header-mtrk+)
199
(error (make-condition 'header :header "MTrk")))
200
(loop with message = nil
201
do (setf message (read-timed-message))
202
until (typep message 'end-of-track-message)
205
(defun write-track (track)
206
"write a track (which does not contain the end-of-track message"
207
(write-fixed-length-quantity +header-mtrk+ 4)
208
(let ((end-of-track-message (make-instance 'end-of-track-message)))
209
;; write the length of the track
210
(write-fixed-length-quantity
211
(+ (reduce #'+ track :key #'length-message)
212
(length-message end-of-track-message)
213
(loop with time = *time*
215
sum (prog1 (length-of-variables-length-quantity
216
(- (message-time message) time))
217
(setf time (message-time message))))
218
1) ; the delta time of the end-of-track message
220
(dolist (message track)
221
(write-timed-message message))
222
(setf (message-time end-of-track-message) *time*)
223
(write-timed-message end-of-track-message)))
225
(defun read-midi-file (filename)
226
"read an entire Midifile from the file with name given as argument"
228
(with-midi-input (filename)
229
(let ((type (read-fixed-length-quantity 4))
230
(length (read-fixed-length-quantity 4))
231
(format (read-fixed-length-quantity 2))
232
(nb-tracks (read-fixed-length-quantity 2))
233
(division (read-fixed-length-quantity 2)))
234
(unless (and (= length +header-mthd-length+) (= type +header-mthd+))
235
(error (make-condition 'header :header "MThd")))
236
(make-instance 'midifile
239
:tracks (loop repeat nb-tracks
240
do (when (= format 1) (setf *time* 0))
241
collect (read-track))))))
243
(defun write-midi-file (midifile filename)
244
(with-midi-output (filename :if-exists :supersede)
245
(write-fixed-length-quantity +header-mthd+ 4)
246
(write-fixed-length-quantity +header-mthd-length+ 4)
247
(with-slots (format division tracks) midifile
248
(write-fixed-length-quantity format 2)
249
(write-fixed-length-quantity (length tracks) 2)
250
(write-fixed-length-quantity division 2)
252
(loop for track in tracks do
254
(when (= (slot-value midifile 'format) 1)
257
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
259
;;; Conversion routines
261
(defun format1-tracks-to-format0-tracks (tracks)
262
(list (reduce (lambda (t1 t2) (merge 'list t1 t2 #'< :key #'message-time))
263
(copy-tree tracks))))
265
(defun format0-tracks-to-format1-tracks (tracks)
266
(assert (null (cdr tracks)))
267
(let (tempo-map track)
268
(dolist (message (car tracks) (list (nreverse tempo-map) (nreverse track)))
269
(if (typep message 'tempo-map-message)
270
(push message tempo-map)
271
(push message track)))))
273
(defun change-to-format-0 (midifile)
274
(assert (= (midifile-format midifile) 1))
275
(setf (slot-value midifile 'format) 0
276
(slot-value midifile 'tracks) (format1-tracks-to-format0-tracks (midifile-tracks midifile))))
278
(defun change-to-format-1 (midifile)
279
(assert (= (midifile-format midifile) 0))
280
(setf (slot-value midifile 'format) 1
281
(slot-value midifile 'tracks) (format0-tracks-to-format1-tracks (midifile-tracks midifile))))
283
(defmethod (setf midifile-format) (new-value midifile)
285
((= (midifile-format midifile) new-value) new-value)
286
((and (= new-value 0) (= (midifile-format midifile) 1))
287
(change-to-format-0 midifile)
289
((and (= new-value 1) (= (midifile-format midifile) 0))
290
(change-to-format-1 midifile)
292
(t (error "Unsupported conversion from format ~S to format ~S"
293
(midifile-format midifile) new-value))))
295
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
297
;;; Macro for defining midi messages
299
(defparameter *status-min* (make-hash-table :test #'eq)
300
"given a class name, find the minimum status value for the type of message")
301
(defparameter *status-max* (make-hash-table :test #'eq)
302
"given a class name, find the maximum status value for the type of message")
303
(defparameter *data-min* (make-hash-table :test #'eq)
304
"given a class name, find the minimum data1 value for the type of message")
305
(defparameter *data-max* (make-hash-table :test #'eq)
306
"given a class name, find the maximum data1 value for the type of message")
308
(defun register-class (class superclass status-min status-max data-min data-max)
310
(setf status-min (gethash superclass *status-min*)))
312
(setf status-max (gethash superclass *status-max*)))
314
(setf data-min (gethash superclass *data-min*)))
316
(setf data-max (gethash superclass *data-max*)))
317
;; set status values for this class
318
(setf (gethash class *status-min*) status-min)
319
(setf (gethash class *status-max*) status-max)
320
(setf (gethash class *data-min*) data-min)
321
(setf (gethash class *data-max*) data-max)
322
;; update the dispatch table
325
(progn (unless (arrayp (aref *dispatch-table* status-min))
326
(let ((secondary-dispatch (make-array 256
327
:initial-element nil)))
328
(loop for i from status-min to status-max do
329
(setf (aref *dispatch-table* i) secondary-dispatch))))
330
(loop for i from data-min to data-max do
331
(setf (aref (aref *dispatch-table* status-min) i)
333
(loop for i from status-min to status-max do
334
(setf (aref *dispatch-table* i)
337
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
339
;;; main filler, length, and writer methods
341
(defgeneric fill-message (message))
342
(defgeneric write-message (message))
343
(defgeneric length-message (message)
344
(:method-combination +))
346
(defmethod fill-message (message)
347
(declare (ignore message))
350
(defmethod length-message + (message)
351
(declare (ignore message))
354
(defmethod write-message (message)
355
(declare (ignore message))
358
(defparameter *midi-channel* 0
359
"Default MIDI channel for midi-messages for which status-min and status-max
360
have a difference of 15. When bound to an \(<= 0 integer 15\), the :status
361
default value will automatically combine the message's status-min and
364
(defmacro define-midi-message (name superclasses
365
&key slots filler (length 0) writer
366
status-min status-max data-min data-max)
369
(register-class ',name ',(car superclasses)
370
,status-min ,status-max ,data-min ,data-max)
372
(defclass ,name ,superclasses
373
((status-min :initform ,status-min :allocation :class)
374
(status-max :initform ,status-max :allocation :class)
375
(data-min :initform ,data-min :allocation :class)
376
(data-max :initform ,data-max :allocation :class)
378
,@(when (and (numberp status-min) (numberp status-max))
379
(cond ((= status-min status-max)
380
`((:default-initargs :status ,status-min)))
381
((= 15 (- status-max status-min))
382
`((:default-initargs :status (if (and (integerp *midi-channel*)
383
(<= 0 *midi-channel* 15))
384
(logior ,(logand status-min status-max)
386
(error "*midi-channel*=~A not supported"
387
*midi-channel*))))))))
389
(defmethod fill-message :after ((message ,name))
390
(with-slots ,(mapcar #'car slots) message
391
(symbol-macrolet ((next-byte (read-next-byte)))
394
(defmethod length-message + ((message ,name))
395
(with-slots (status-min status-max data-min data-max ,@(mapcar #'car slots))
399
(defmethod write-message :after ((message ,name))
400
(with-slots (status-min status-max data-min data-max ,@(mapcar #'car slots))
404
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
408
(define-midi-message message ()
409
:slots ((time :initarg :time :accessor message-time)
410
(status :initarg :status :reader message-status :initform 0))
412
:filler (setf status *status*)
413
:writer (write-bytes status))
415
(defgeneric print-midi-message (object stream)
416
(:method ((object message) stream)
417
(when (slot-boundp object 'time)
418
(format stream " T=~A" (slot-value object 'time)))
419
(when (slot-boundp object 'status)
420
(format stream " S=~X" (slot-value object 'status))))
422
"One PRINT-OBJECT method is defined for the MIDI message class
423
\(common ancestor\): that method prints the wrapping, then calls
424
the PRINT-MIDI-MESSAGE method to print the slots."))
426
(defmethod print-object ((obj message) stream)
427
(print-unreadable-object (obj stream :type t :identity t)
428
(print-midi-message obj stream))
431
(define-midi-message channel-message (message)
432
:slots ((channel :reader message-channel))
433
:filler (setf channel (logand *status* #x0f)))
435
(defmethod print-midi-message ((object channel-message) stream)
437
(when (slot-boundp object 'channel)
438
(format stream " C=~X" (slot-value object 'channel))))
440
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
444
(define-midi-message voice-message (channel-message))
446
(define-midi-message note-off-message (voice-message)
447
:status-min #x80 :status-max #x8f
448
:slots ((key :initarg :key :reader message-key)
449
(velocity :initarg :velocity :reader message-velocity))
450
:filler (setf key next-byte
453
:writer (write-bytes key velocity))
455
(defmethod print-midi-message ((object note-off-message) stream)
457
(when (slot-boundp object 'key)
458
(format stream " k=~A" (slot-value object 'key)))
459
(when (slot-boundp object 'velocity)
460
(format stream " v=~A" (slot-value object 'velocity))))
462
(define-midi-message note-on-message (voice-message)
463
:status-min #x90 :status-max #x9f
464
:slots ((key :initarg :key :reader message-key)
465
(velocity :initarg :velocity :reader message-velocity))
466
:filler (setf key next-byte
469
:writer (write-bytes key velocity))
471
(defmethod print-midi-message ((object note-on-message) stream)
473
(when (slot-boundp object 'key)
474
(format stream " K=~A" (slot-value object 'key)))
475
(when (slot-boundp object 'velocity)
476
(format stream " V=~A" (slot-value object 'velocity))))
478
(define-midi-message polyphonic-key-pressure-message (voice-message)
479
:status-min #xa0 :status-max #xaf
482
:filler (setf key next-byte
485
:writer (write-bytes key pressure))
487
(define-midi-message control-change-message (voice-message)
488
:status-min #xb0 :status-max #xbf
489
:data-min #x00 :data-max #x78
490
:slots ((controller :initarg :controller)
491
(value :initarg value))
492
:filler (setf controller next-byte
495
:writer (write-bytes controller value))
497
(define-midi-message program-change-message (voice-message)
498
:status-min #xc0 :status-max #xcf
499
:slots ((program :initarg :program :reader message-program))
500
:filler (setf program next-byte)
502
:writer (write-bytes program))
504
(defmethod print-midi-message ((object program-change-message) stream)
506
(when (slot-boundp object 'program)
507
(format stream " P=~A" (slot-value object 'program))))
509
(define-midi-message channel-pressure-message (voice-message)
510
:status-min #xd0 :status-max #xdf
512
:filler (setf pressure next-byte)
514
:writer (write-bytes pressure))
516
(define-midi-message pitch-bend-message (voice-message)
517
:status-min #xe0 :status-max #xef
518
:slots ((value :initarg :value :reader message-value))
519
:filler (setf value (logior next-byte (ash next-byte 7)))
521
:writer (write-bytes (logand value #x7f) (logand (ash value -7) #x7f)))
523
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
527
(define-midi-message mode-message (channel-message)
528
:filler next-byte) ; consume data byte
530
(define-midi-message reset-all-controllers-message (mode-message)
531
:status-min #xb0 :status-max #xbf
532
:data-min #x79 :data-max #x79
533
:filler next-byte ; consume unused byte
535
:writer (write-bytes #x79 0))
537
(define-midi-message local-control-message (mode-message)
538
:status-min #xb0 :status-max #xbf
539
:data-min #x7a :data-max #x7a
541
:filler (setf mode (if (= next-byte 0) :off :on))
543
:writer (write-bytes #x7a (if (eq mode :off) 0 127)))
545
(define-midi-message all-notes-off-message (mode-message)
546
:status-min #xb0 :status-max #xbf
547
:data-min #x7b :data-max #x7b
548
:filler next-byte ; consume unused byte
550
:writer (write-bytes #x7b 0))
552
(define-midi-message omni-mode-off-message (mode-message)
553
:status-min #xb0 :status-max #xbf
554
:data-min #x7c :data-max #x7c
555
:filler next-byte ; consume unused byte
557
:writer (write-bytes #x7c 0))
559
(define-midi-message omni-mode-on-message (mode-message)
560
:status-min #xb0 :status-max #xbf
561
:data-min #x7d :data-max #x7d
562
:filler next-byte ; consume unused byte
564
:writer (write-bytes #x7d 0))
566
(define-midi-message mono-mode-on-message (mode-message)
567
:status-min #xb0 :status-max #xbf
568
:data-min #x7e :data-max #x7e
569
:slots ((nb-channels))
570
:filler (setf nb-channels next-byte)
572
:writer (write-bytes #x7e nb-channels))
574
(define-midi-message poly-mode-on-message (mode-message)
575
:status-min #xb0 :status-max #xbf
576
:data-min #x7f :data-max #x7f
577
:filler next-byte ; consume unused byte
579
:writer (write-bytes #x7f 0))
581
(define-midi-message system-message (message))
583
(define-midi-message tempo-map-message (message))
585
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
587
;;; system common messages
589
(define-midi-message common-message (system-message))
591
(define-midi-message timing-code-message (common-message)
592
:status-min #xf1 :status-max #xf1
594
:filler (setf code next-byte)
596
:writer (write-bytes code))
598
(defmethod print-midi-message ((object timing-code-message) stream)
600
(when (slot-boundp object 'code)
601
(format stream " code=~A" (slot-value object 'code))))
603
(define-midi-message song-position-pointer-message (common-message)
604
:status-min #xf2 :status-max #xf2
606
:filler (setf pointer (logior next-byte (ash next-byte 7)))
608
:writer (write-bytes (logand pointer #x7f) (logand (ash pointer -7) #x7f)))
610
(define-midi-message song-select-message (common-message)
611
:status-min #xf3 :status-max #xf3
613
:filler (setf song next-byte)
615
:writer (write-bytes song))
617
(define-midi-message tune-request-message (common-message)
618
:status-min #xf6 :status-max #xf6)
620
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
622
;;; system real-time messages
624
(define-midi-message real-time-message (system-message))
626
(define-midi-message timing-clock-message (real-time-message)
627
:status-min #xf8 :status-max #xf8)
629
(define-midi-message start-sequence-message (real-time-message)
630
:status-min #xfa :status-max #xfa)
632
(define-midi-message continue-sequence-message (real-time-message)
633
:status-min #xfb :status-max #xfb)
635
(define-midi-message stop-sequence-message (real-time-message)
636
:status-min #xfc :status-max #xfc)
638
(define-midi-message active-sensing-message (real-time-message)
639
:status-min #xfe :status-max #xfe)
641
;; (define-midi-message tune-request-message (real-time-message)
642
;; :status-min #xf6 :status-max #xf6)
644
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
646
;;; system exclusive messages
648
(define-midi-message system-exclusive-message (system-message)
649
:status-min #xf0 :status-max #xf0
651
:filler (loop with len = (read-variable-length-quantity)
652
initially (setf data (make-array
653
len :element-type '(unsigned-byte 8)))
654
for i from 0 below len
655
do (setf (aref data i) next-byte))
656
:length (+ (length-of-variables-length-quantity (length data))
658
:writer (progn (write-variable-length-quantity (length data))
659
(loop for elem across data do (write-bytes elem))))
661
(define-midi-message authorization-system-exclusive-message (system-message)
662
:status-min #xf7 :status-max #xf7
664
:filler (loop with len = (read-variable-length-quantity)
665
initially (setf data (make-array
666
len :element-type '(unsigned-byte 8)))
667
for i from 0 below len
668
do (setf (aref data i) next-byte))
669
:length (+ (length-of-variables-length-quantity (length data))
671
:writer (progn (write-variable-length-quantity (length data))
672
(loop for elem across data do (write-bytes elem))))
674
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
678
(define-midi-message meta-message (message)
679
:status-min #xff :status-max #xff
680
:length 2 ; the first data byte and the length byte
681
:filler next-byte ; the first data byte which gives the type of meta message
682
:writer (write-bytes data-min))
684
(define-midi-message sequence-number-message (meta-message tempo-map-message)
685
:data-min #x00 :data-max #x00
687
:filler (let ((data2 next-byte))
688
(setf sequence (if (zerop data2)
690
(logior (ash next-byte 8) next-byte))))
691
:length (if (zerop sequence) 0 2)
692
:writer (unless (zerop sequence)
693
(write-bytes (ash sequence -8) (logand sequence #xf))))
695
(define-midi-message text-message (meta-message)
697
:filler (setf text (loop with len = next-byte
698
with str = (make-string len)
699
for i from 0 below len
700
do (setf (aref str i)
701
(code-char next-byte))
702
finally (return str)))
703
:length (length text)
704
:writer (progn (write-bytes (length text))
705
(loop for char across text do
706
(write-bytes (char-code char)))))
708
(defmethod print-midi-message ((object text-message) stream)
710
(when (slot-boundp object 'text)
711
(format stream " [~A]" (slot-value object 'text))))
713
(define-midi-message general-text-message (text-message)
714
:data-min #x01 :data-max #x01)
716
(define-midi-message copyright-message (text-message)
717
:data-min #x02 :data-max #x02)
719
(define-midi-message sequence/track-name-message (text-message tempo-map-message)
720
:data-min #x03 :data-max #x03)
722
(define-midi-message instrument-message (text-message)
723
:data-min #x04 :data-max #x04)
725
(define-midi-message lyric-message (text-message)
726
:data-min #x05 :data-max #x05)
728
(define-midi-message marker-message (text-message tempo-map-message)
729
:data-min #x06 :data-max #x06)
731
(define-midi-message cue-point-message (text-message)
732
:data-min #x07 :data-max #x07)
734
(define-midi-message program-name-message (text-message)
735
:data-min #x08 :data-max #x08)
737
(define-midi-message device-name-message (text-message)
738
:data-min #x09 :data-max #x09)
740
(define-midi-message channel-prefix-message (meta-message)
741
:data-min #x20 :data-max #x20
744
:filler (progn next-byte (setf channel next-byte))
745
:writer (write-bytes 1 channel))
747
(define-midi-message midi-port-message (meta-message)
748
:data-min #x21 :data-max #x21
751
:filler (progn next-byte (setf port next-byte))
752
:writer (write-bytes 1 port))
754
(define-midi-message end-of-track-message (meta-message)
755
:data-min #x2f :data-max #x2f
756
:slots ((status :initform #xff))
759
:writer (write-bytes 0))
761
(define-midi-message tempo-message (meta-message tempo-map-message)
762
:data-min #x51 :data-max #x51
763
:slots ((tempo :initarg :tempo :reader message-tempo))
764
:filler (progn next-byte (setf tempo (read-fixed-length-quantity 3)))
766
:writer (progn (write-bytes 3) (write-fixed-length-quantity tempo 3)))
768
(defmethod print-midi-message ((object tempo-message) stream)
770
(when (slot-boundp object 'tempo)
771
(format stream " tempo=~A" (slot-value object 'tempo))))
773
(define-midi-message smpte-offset-message (meta-message tempo-map-message)
774
:data-min #x54 :data-max #x54
775
:slots ((hr) (mn) (se) (fr) (ff))
776
:filler (progn next-byte (setf hr next-byte mn next-byte se next-byte
777
fr next-byte ff next-byte))
779
:writer (write-bytes 5 hr mn se fr ff))
781
(defmethod print-midi-message ((object smpte-offset-message) stream)
783
(when (or (slot-boundp object 'hr)
784
(slot-boundp object 'mn)
785
(slot-boundp object 'se)
786
(slot-boundp object 'fr)
787
(slot-boundp object 'ff))
789
" hmsff=~A/~A/~A/~A/~A"
790
(ignore-errors (slot-value object 'hr))
791
(ignore-errors (slot-value object 'mn))
792
(ignore-errors (slot-value object 'se))
793
(ignore-errors (slot-value object 'fr))
794
(ignore-errors (slot-value object 'ff)))))
796
(define-midi-message time-signature-message (meta-message tempo-map-message)
797
:data-min #x58 :data-max #x58
798
:slots ((nn :reader message-numerator)
799
(dd :reader message-denominator)
801
:filler (progn next-byte (setf nn next-byte dd next-byte
802
cc next-byte bb next-byte))
804
:writer (write-bytes 4 nn dd cc bb))
806
(defmethod print-midi-message ((object time-signature-message) stream)
808
(when (or (slot-boundp object 'nn)
809
(slot-boundp object 'dd)
810
(slot-boundp object 'cc)
811
(slot-boundp object 'bb))
814
(ignore-errors (slot-value object 'nn))
815
(ignore-errors (slot-value object 'dd))
816
(ignore-errors (slot-value object 'cc))
817
(ignore-errors (slot-value object 'bb)))))
819
(define-midi-message key-signature-message (meta-message)
820
:data-min #x59 :data-max #x59
821
:slots ((sf :reader message-sf)
822
(mi :reader message-mi))
823
:filler (progn next-byte (setf sf (let ((temp-sf next-byte))
829
:writer (write-bytes 2 (if (< sf 0) (+ sf 256) sf) mi))
831
(define-midi-message proprietary-event (meta-message)
832
:data-min #x7f :data-max #x7f
834
:filler (setf data (loop with len = (read-variable-length-quantity)
835
with vec = (make-array
837
:element-type '(unsigned-byte 8))
838
for i from 0 below len
839
do (setf (aref vec i) next-byte)
840
finally (return vec)))
841
:writer (map nil (lambda (byte) (write-bytes byte))