changelog shortlog graph tags branches changeset files revisions annotate raw help

Mercurial > core / lisp/lib/dat/midi.lisp

changeset 698: 96958d3eb5b0
parent: 4e6838e03f61
author: Richard Westhaver <ellis@rwest.io>
date: Fri, 04 Oct 2024 22:04:59 -0400
permissions: -rw-r--r--
description: fixes
1 ;;; dat/midi.lisp --- MIDI data
2 
3 ;;; (c) copyright 2003 by Mathieu Chabanne, Camille Constant,
4 ;;; Emmanuel Necibar and Stephanie Recco
5 ;;;
6 ;;; (c) copyright 2003 by Robert Strandh (strandh@labri.fr)
7 ;;;
8 ;;; (c) copyright 2007 by David Lewis, Marcus Pearce, Christophe
9 ;;; Rhodes and contributors
10 ;;;
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.
14 ;;;
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.
19 ;;;
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.
24 ;;;
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)
29 
30 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
31 ;;;
32 ;;; Midifile protocol
33 
34 (defgeneric midifile-format (midifile))
35 (defgeneric (setf midifile-format) (format midifile))
36 (defgeneric midifile-division (midifile))
37 (defgeneric midifile-tracks (midifile))
38 
39 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
40 ;;;
41 ;;; Message protocol
42 
43 (defgeneric message-time (message))
44 (defgeneric (setf message-time) (time message))
45 (defgeneric message-status (message))
46 (defgeneric message-channel (message))
47 (defgeneric message-key (message))
48 (defgeneric message-velocity (message))
49 (defgeneric message-tempo (message))
50 (defgeneric message-numerator (message))
51 (defgeneric message-denominator (message))
52 (defgeneric message-sf (message))
53 (defgeneric message-mi (message))
54 ;; added 03-05-07
55 (defgeneric message-program (message))
56 
57 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
58 ;;;
59 ;;; File support
60 (eval-when (:compile-toplevel :load-toplevel)
61  (defun string-code (s)
62  "compute the ASCII-based numerical value of the string [warning:
63 works only if the chars are coded in ASCII]"
64  (let ((v 0))
65  (loop for i from 0 to (1- (length s))
66  do (setf v (+ (* v 256) (char-code (aref s i)))))
67  v)))
68 
69 (defconstant +header-mthd+ #.(string-code "MThd"))
70 (defconstant +header-mtrk+ #.(string-code "MTrk"))
71 (defconstant +header-mthd-length+ 6 "value of the header MThd data's length")
72 
73 (defparameter *midi-input* nil "stream for reading a Midifile")
74 (defparameter *input-buffer* '() "used for unreading bytes from *midi-input")
75 (defparameter *midi-output* nil "stream for writing a Midifile")
76 
77 (define-condition unknown-event ()
78  ((status :initarg :status :reader status)
79  (data-byte :initform "" :initarg :data-byte :reader data-byte))
80  (:documentation "condition when the event does not exist in the library"))
81 
82 (define-condition header ()
83  ((header-type :initarg :header :reader header-type))
84  (:documentation "condition when the header is not correct"))
85 
86 (defun read-next-byte ()
87  "read an unsigned 8-bit byte from *midi-input* checking for unread bytes"
88  (if *input-buffer*
89  (pop *input-buffer*)
90  (read-byte *midi-input*)))
91 
92 (defun unread-byte (byte)
93  "unread a byte from *midi-input*"
94  (push byte *input-buffer*))
95 
96 (defun write-bytes (&rest bytes)
97  "write an arbitrary number of bytes to *midi-output*"
98  (mapc #'(lambda (byte) (write-byte byte *midi-output*)) bytes))
99 
100 (defun read-fixed-length-quantity (nb-bytes)
101  "read an unsigned integer of nb-bytes bytes from *midi-input*"
102  (loop with result = 0
103  for i from 1 to nb-bytes
104  do (setf result (logior (ash result 8) (read-next-byte)))
105  finally (return result)))
106 
107 (defun write-fixed-length-quantity (quantity nb-bytes)
108  "write an unsigned integer of nb-bytes bytes to *midi-output*"
109  (unless (zerop nb-bytes)
110  (write-fixed-length-quantity (ash quantity -8) (1- nb-bytes))
111  (write-bytes (logand quantity #xff))))
112 
113 (defmacro with-midi-input ((pathname &rest open-args &key &allow-other-keys) &body body)
114  "execute body with *midi-input* assigned to a stream from pathname"
115  `(with-open-file (*midi-input* ,pathname
116  :direction :input :element-type '(unsigned-byte 8)
117  ,@open-args)
118  ,@body))
119 
120 (defmacro with-midi-output ((pathname &rest open-args &key &allow-other-keys) &body body)
121  "execute body with *midi-output* assigned to a stream from pathname"
122  `(with-open-file (*midi-output* ,pathname
123  :direction :output :element-type '(unsigned-byte 8)
124  ,@open-args)
125  ,@body))
126 
127 (defun read-variable-length-quantity ()
128  "read a MIDI variable length quantity from *midi-input*"
129  (loop with result = 0
130  with byte
131  do (setf byte (read-next-byte)
132  result (logior (ash result 7) (logand byte #x7f)))
133  until (< byte #x80)
134  finally (return result)))
135 
136 (defun write-variable-length-quantity (quantity &optional (termination 0))
137  (when (> quantity 127)
138  (write-variable-length-quantity (ash quantity -7) #x80))
139  (write-bytes (logior (logand quantity #x7f) termination)))
140 
141 (defun length-of-variables-length-quantity (quantity)
142  (1+ (if (< quantity 128)
143  0
144  (length-of-variables-length-quantity (ash quantity -7)))))
145 
146 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
147 ;;;
148 ;;; MIDI file representation
149 
150 (defclass midifile ()
151  ((format :initarg :format :reader midifile-format)
152  (division :initarg :division :reader midifile-division)
153  (tracks :initarg :tracks :reader midifile-tracks))
154  (:documentation "the class that represents a Midifile in core"))
155 
156 (defparameter *status* nil "the status while reading an event")
157 (defparameter *running-status* nil "the running status while reading an event")
158 (defparameter *dispatch-table* (make-array 256 :initial-element nil)
159  "given values of status (and perhaps data1), find a class to create")
160 
161 (defun read-message ()
162  "read a message without time indication from *midi-input*"
163  (let ((classname-or-subtype (aref *dispatch-table* *status*)))
164  (unless classname-or-subtype
165  (error (make-condition 'unknown-event
166  :status *status*)))
167  (if (symbolp classname-or-subtype)
168  (make-instance classname-or-subtype)
169  (let* ((data-byte (read-next-byte))
170  (classname (aref classname-or-subtype data-byte)))
171  (unless classname
172  (error (make-condition 'unknown-event
173  :status *status*
174  :data-byte data-byte)))
175  (unread-byte data-byte)
176  (make-instance classname)))))
177 
178 (defparameter *time* 0 "accumulated time from the start of the track")
179 
180 (defun read-timed-message ()
181  "read a message preceded with a delta-time indication"
182  (let ((delta-time (read-variable-length-quantity))
183  (status-or-data (read-next-byte)))
184  (if (>= status-or-data #x80)
185  (progn (setf *status* status-or-data)
186  (when (<= *status* #xef)
187  (setf *running-status* *status*)))
188  (progn (unread-byte status-or-data)
189  (setf *status* *running-status*)))
190  (let ((message (read-message)))
191  (fill-message message)
192  (setf (message-time message) (incf *time* delta-time))
193  message)))
194 
195 (defun write-timed-message (message)
196  "write a message preceded with a delta-time indication"
197  (write-variable-length-quantity (- (message-time message) *time*))
198  (setf *time* (message-time message))
199  (write-message message))
200 
201 (defun read-track ()
202  "read a track as a list of timed messages, excluding the end-of-track message"
203  (let ((type (read-fixed-length-quantity 4))
204  (length (read-fixed-length-quantity 4)))
205  (declare (ignore length))
206  (unless (= type +header-mtrk+)
207  (error (make-condition 'header :header "MTrk")))
208  (loop with message = nil
209  do (setf message (read-timed-message))
210  until (typep message 'end-of-track-message)
211  collect message)))
212 
213 (defun write-track (track)
214  "write a track (which does not contain the end-of-track message"
215  (write-fixed-length-quantity +header-mtrk+ 4)
216  (let ((end-of-track-message (make-instance 'end-of-track-message)))
217  ;; write the length of the track
218  (write-fixed-length-quantity
219  (+ (reduce #'+ track :key #'length-message)
220  (length-message end-of-track-message)
221  (loop with time = *time*
222  for message in track
223  sum (prog1 (length-of-variables-length-quantity
224  (- (message-time message) time))
225  (setf time (message-time message))))
226  1) ; the delta time of the end-of-track message
227  4)
228  (dolist (message track)
229  (write-timed-message message))
230  (setf (message-time end-of-track-message) *time*)
231  (write-timed-message end-of-track-message)))
232 
233 (defun read-midi-file (filename)
234  "read an entire Midifile from the file with name given as argument"
235  (setf *time* 0)
236  (with-midi-input (filename)
237  (let ((type (read-fixed-length-quantity 4))
238  (length (read-fixed-length-quantity 4))
239  (format (read-fixed-length-quantity 2))
240  (nb-tracks (read-fixed-length-quantity 2))
241  (division (read-fixed-length-quantity 2)))
242  (unless (and (= length +header-mthd-length+) (= type +header-mthd+))
243  (error (make-condition 'header :header "MThd")))
244  (make-instance 'midifile
245  :format format
246  :division division
247  :tracks (loop repeat nb-tracks
248  do (when (= format 1) (setf *time* 0))
249  collect (read-track))))))
250 
251 (defun write-midi-file (midifile filename)
252  (with-midi-output (filename :if-exists :supersede)
253  (write-fixed-length-quantity +header-mthd+ 4)
254  (write-fixed-length-quantity +header-mthd-length+ 4)
255  (with-slots (format division tracks) midifile
256  (write-fixed-length-quantity format 2)
257  (write-fixed-length-quantity (length tracks) 2)
258  (write-fixed-length-quantity division 2)
259  (setf *time* 0)
260  (loop for track in tracks do
261  (write-track track)
262  (when (= (slot-value midifile 'format) 1)
263  (setf *time* 0))))))
264 
265 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
266 ;;;
267 ;;; Conversion routines
268 
269 (defun format1-tracks-to-format0-tracks (tracks)
270  (list (reduce (lambda (t1 t2) (merge 'list t1 t2 #'< :key #'message-time))
271  (copy-tree tracks))))
272 
273 (defun format0-tracks-to-format1-tracks (tracks)
274  (assert (null (cdr tracks)))
275  (let (tempo-map track)
276  (dolist (message (car tracks) (list (nreverse tempo-map) (nreverse track)))
277  (if (typep message 'tempo-map-message)
278  (push message tempo-map)
279  (push message track)))))
280 
281 (defun change-to-format-0 (midifile)
282  (assert (= (midifile-format midifile) 1))
283  (setf (slot-value midifile 'format) 0
284  (slot-value midifile 'tracks) (format1-tracks-to-format0-tracks (midifile-tracks midifile))))
285 
286 (defun change-to-format-1 (midifile)
287  (assert (= (midifile-format midifile) 0))
288  (setf (slot-value midifile 'format) 1
289  (slot-value midifile 'tracks) (format0-tracks-to-format1-tracks (midifile-tracks midifile))))
290 
291 (defmethod (setf midifile-format) (new-value midifile)
292  (cond
293  ((= (midifile-format midifile) new-value) new-value)
294  ((and (= new-value 0) (= (midifile-format midifile) 1))
295  (change-to-format-0 midifile)
296  new-value)
297  ((and (= new-value 1) (= (midifile-format midifile) 0))
298  (change-to-format-1 midifile)
299  new-value)
300  (t (error "Unsupported conversion from format ~S to format ~S"
301  (midifile-format midifile) new-value))))
302 
303 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
304 ;;;
305 ;;; Macro for defining midi messages
306 
307 (defparameter *status-min* (make-hash-table :test #'eq)
308  "given a class name, find the minimum status value for the type of message")
309 (defparameter *status-max* (make-hash-table :test #'eq)
310  "given a class name, find the maximum status value for the type of message")
311 (defparameter *data-min* (make-hash-table :test #'eq)
312  "given a class name, find the minimum data1 value for the type of message")
313 (defparameter *data-max* (make-hash-table :test #'eq)
314  "given a class name, find the maximum data1 value for the type of message")
315 
316 (defun register-class (class superclass status-min status-max data-min data-max)
317  (unless status-min
318  (setf status-min (gethash superclass *status-min*)))
319  (unless status-max
320  (setf status-max (gethash superclass *status-max*)))
321  (unless data-min
322  (setf data-min (gethash superclass *data-min*)))
323  (unless data-max
324  (setf data-max (gethash superclass *data-max*)))
325  ;; set status values for this class
326  (setf (gethash class *status-min*) status-min)
327  (setf (gethash class *status-max*) status-max)
328  (setf (gethash class *data-min*) data-min)
329  (setf (gethash class *data-max*) data-max)
330  ;; update the dispatch table
331  (when status-min
332  (if data-min
333  (progn (unless (arrayp (aref *dispatch-table* status-min))
334  (let ((secondary-dispatch (make-array 256
335  :initial-element nil)))
336  (loop for i from status-min to status-max do
337  (setf (aref *dispatch-table* i) secondary-dispatch))))
338  (loop for i from data-min to data-max do
339  (setf (aref (aref *dispatch-table* status-min) i)
340  class)))
341  (loop for i from status-min to status-max do
342  (setf (aref *dispatch-table* i)
343  class)))))
344 
345 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
346 ;;;
347 ;;; main filler, length, and writer methods
348 
349 (defgeneric fill-message (message))
350 (defgeneric write-message (message))
351 (defgeneric length-message (message)
352  (:method-combination +))
353 
354 (defmethod fill-message (message)
355  (declare (ignore message))
356  nil)
357 
358 (defmethod length-message + (message)
359  (declare (ignore message))
360  0)
361 
362 (defmethod write-message (message)
363  (declare (ignore message))
364  nil)
365 
366 (defparameter *midi-channel* 0
367  "Default MIDI channel for midi-messages for which status-min and status-max
368 have a difference of 15. When bound to an \(<= 0 integer 15\), the :status
369 default value will automatically combine the message's status-min and
370 *midi-channel*.")
371 
372 (defmacro define-midi-message (name superclasses
373  &key slots filler (length 0) writer
374  status-min status-max data-min data-max)
375  `(progn
376 
377  (register-class ',name ',(car superclasses)
378  ,status-min ,status-max ,data-min ,data-max)
379 
380  (defclass ,name ,superclasses
381  ((status-min :initform ,status-min :allocation :class)
382  (status-max :initform ,status-max :allocation :class)
383  (data-min :initform ,data-min :allocation :class)
384  (data-max :initform ,data-max :allocation :class)
385  ,@slots)
386  ,@(when (and (numberp status-min) (numberp status-max))
387  (cond ((= status-min status-max)
388  `((:default-initargs :status ,status-min)))
389  ((= 15 (- status-max status-min))
390  `((:default-initargs :status (if (and (integerp *midi-channel*)
391  (<= 0 *midi-channel* 15))
392  (logior ,(logand status-min status-max)
393  *midi-channel*)
394  (error "*midi-channel*=~A not supported"
395  *midi-channel*))))))))
396 
397  (defmethod fill-message :after ((message ,name))
398  (with-slots ,(mapcar #'car slots) message
399  (symbol-macrolet ((next-byte (read-next-byte)))
400  ,filler)))
401 
402  (defmethod length-message + ((message ,name))
403  (with-slots (status-min status-max data-min data-max ,@(mapcar #'car slots))
404  message
405  ,length))
406 
407  (defmethod write-message :after ((message ,name))
408  (with-slots (status-min status-max data-min data-max ,@(mapcar #'car slots))
409  message
410  ,writer))))
411 
412 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
413 ;;;
414 ;;; midi messages
415 
416 (define-midi-message message ()
417  :slots ((time :initarg :time :accessor message-time)
418  (status :initarg :status :reader message-status :initform 0))
419  :length 1
420  :filler (setf status *status*)
421  :writer (write-bytes status))
422 
423 (defgeneric print-midi-message (object stream)
424  (:method ((object message) stream)
425  (when (slot-boundp object 'time)
426  (format stream " T=~A" (slot-value object 'time)))
427  (when (slot-boundp object 'status)
428  (format stream " S=~X" (slot-value object 'status))))
429  (:documentation
430  "One PRINT-OBJECT method is defined for the MIDI message class
431 \(common ancestor\): that method prints the wrapping, then calls
432 the PRINT-MIDI-MESSAGE method to print the slots."))
433 
434 (defmethod print-object ((obj message) stream)
435  (print-unreadable-object (obj stream :type t :identity t)
436  (print-midi-message obj stream))
437  obj)
438 
439 (define-midi-message channel-message (message)
440  :slots ((channel :reader message-channel))
441  :filler (setf channel (logand *status* #x0f)))
442 
443 (defmethod print-midi-message ((object channel-message) stream)
444  (call-next-method)
445  (when (slot-boundp object 'channel)
446  (format stream " C=~X" (slot-value object 'channel))))
447 
448 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
449 ;;;
450 ;;; voice messages
451 
452 (define-midi-message voice-message (channel-message))
453 
454 (define-midi-message note-off-message (voice-message)
455  :status-min #x80 :status-max #x8f
456  :slots ((key :initarg :key :reader message-key)
457  (velocity :initarg :velocity :reader message-velocity))
458  :filler (setf key next-byte
459  velocity next-byte)
460  :length 2
461  :writer (write-bytes key velocity))
462 
463 (defmethod print-midi-message ((object note-off-message) stream)
464  (call-next-method)
465  (when (slot-boundp object 'key)
466  (format stream " k=~A" (slot-value object 'key)))
467  (when (slot-boundp object 'velocity)
468  (format stream " v=~A" (slot-value object 'velocity))))
469 
470 (define-midi-message note-on-message (voice-message)
471  :status-min #x90 :status-max #x9f
472  :slots ((key :initarg :key :reader message-key)
473  (velocity :initarg :velocity :reader message-velocity))
474  :filler (setf key next-byte
475  velocity next-byte)
476  :length 2
477  :writer (write-bytes key velocity))
478 
479 (defmethod print-midi-message ((object note-on-message) stream)
480  (call-next-method)
481  (when (slot-boundp object 'key)
482  (format stream " K=~A" (slot-value object 'key)))
483  (when (slot-boundp object 'velocity)
484  (format stream " V=~A" (slot-value object 'velocity))))
485 
486 (define-midi-message polyphonic-key-pressure-message (voice-message)
487  :status-min #xa0 :status-max #xaf
488  :slots ((key)
489  (pressure))
490  :filler (setf key next-byte
491  pressure next-byte)
492  :length 2
493  :writer (write-bytes key pressure))
494 
495 (define-midi-message control-change-message (voice-message)
496  :status-min #xb0 :status-max #xbf
497  :data-min #x00 :data-max #x78
498  :slots ((controller :initarg :controller)
499  (value :initarg value))
500  :filler (setf controller next-byte
501  value next-byte)
502  :length 2
503  :writer (write-bytes controller value))
504 
505 (define-midi-message program-change-message (voice-message)
506  :status-min #xc0 :status-max #xcf
507  :slots ((program :initarg :program :reader message-program))
508  :filler (setf program next-byte)
509  :length 1
510  :writer (write-bytes program))
511 
512 (defmethod print-midi-message ((object program-change-message) stream)
513  (call-next-method)
514  (when (slot-boundp object 'program)
515  (format stream " P=~A" (slot-value object 'program))))
516 
517 (define-midi-message channel-pressure-message (voice-message)
518  :status-min #xd0 :status-max #xdf
519  :slots ((pressure))
520  :filler (setf pressure next-byte)
521  :length 1
522  :writer (write-bytes pressure))
523 
524 (define-midi-message pitch-bend-message (voice-message)
525  :status-min #xe0 :status-max #xef
526  :slots ((value :initarg :value :reader message-value))
527  :filler (setf value (logior next-byte (ash next-byte 7)))
528  :length 2
529  :writer (write-bytes (logand value #x7f) (logand (ash value -7) #x7f)))
530 
531 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
532 ;;;
533 ;;; mode messages
534 
535 (define-midi-message mode-message (channel-message)
536  :filler next-byte) ; consume data byte
537 
538 (define-midi-message reset-all-controllers-message (mode-message)
539  :status-min #xb0 :status-max #xbf
540  :data-min #x79 :data-max #x79
541  :filler next-byte ; consume unused byte
542  :length 2
543  :writer (write-bytes #x79 0))
544 
545 (define-midi-message local-control-message (mode-message)
546  :status-min #xb0 :status-max #xbf
547  :data-min #x7a :data-max #x7a
548  :slots ((mode))
549  :filler (setf mode (if (= next-byte 0) :off :on))
550  :length 2
551  :writer (write-bytes #x7a (if (eq mode :off) 0 127)))
552 
553 (define-midi-message all-notes-off-message (mode-message)
554  :status-min #xb0 :status-max #xbf
555  :data-min #x7b :data-max #x7b
556  :filler next-byte ; consume unused byte
557  :length 2
558  :writer (write-bytes #x7b 0))
559 
560 (define-midi-message omni-mode-off-message (mode-message)
561  :status-min #xb0 :status-max #xbf
562  :data-min #x7c :data-max #x7c
563  :filler next-byte ; consume unused byte
564  :length 2
565  :writer (write-bytes #x7c 0))
566 
567 (define-midi-message omni-mode-on-message (mode-message)
568  :status-min #xb0 :status-max #xbf
569  :data-min #x7d :data-max #x7d
570  :filler next-byte ; consume unused byte
571  :length 2
572  :writer (write-bytes #x7d 0))
573 
574 (define-midi-message mono-mode-on-message (mode-message)
575  :status-min #xb0 :status-max #xbf
576  :data-min #x7e :data-max #x7e
577  :slots ((nb-channels))
578  :filler (setf nb-channels next-byte)
579  :length 2
580  :writer (write-bytes #x7e nb-channels))
581 
582 (define-midi-message poly-mode-on-message (mode-message)
583  :status-min #xb0 :status-max #xbf
584  :data-min #x7f :data-max #x7f
585  :filler next-byte ; consume unused byte
586  :length 2
587  :writer (write-bytes #x7f 0))
588 
589 (define-midi-message system-message (message))
590 
591 (define-midi-message tempo-map-message (message))
592 
593 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
594 ;;;
595 ;;; system common messages
596 
597 (define-midi-message common-message (system-message))
598 
599 (define-midi-message timing-code-message (common-message)
600  :status-min #xf1 :status-max #xf1
601  :slots ((code))
602  :filler (setf code next-byte)
603  :length 1
604  :writer (write-bytes code))
605 
606 (defmethod print-midi-message ((object timing-code-message) stream)
607  (call-next-method)
608  (when (slot-boundp object 'code)
609  (format stream " code=~A" (slot-value object 'code))))
610 
611 (define-midi-message song-position-pointer-message (common-message)
612  :status-min #xf2 :status-max #xf2
613  :slots ((pointer))
614  :filler (setf pointer (logior next-byte (ash next-byte 7)))
615  :length 2
616  :writer (write-bytes (logand pointer #x7f) (logand (ash pointer -7) #x7f)))
617 
618 (define-midi-message song-select-message (common-message)
619  :status-min #xf3 :status-max #xf3
620  :slots ((song))
621  :filler (setf song next-byte)
622  :length 1
623  :writer (write-bytes song))
624 
625 (define-midi-message tune-request-message (common-message)
626  :status-min #xf6 :status-max #xf6)
627 
628 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
629 ;;;
630 ;;; system real-time messages
631 
632 (define-midi-message real-time-message (system-message))
633 
634 (define-midi-message timing-clock-message (real-time-message)
635  :status-min #xf8 :status-max #xf8)
636 
637 (define-midi-message start-sequence-message (real-time-message)
638  :status-min #xfa :status-max #xfa)
639 
640 (define-midi-message continue-sequence-message (real-time-message)
641  :status-min #xfb :status-max #xfb)
642 
643 (define-midi-message stop-sequence-message (real-time-message)
644  :status-min #xfc :status-max #xfc)
645 
646 (define-midi-message active-sensing-message (real-time-message)
647  :status-min #xfe :status-max #xfe)
648 
649 ;; (define-midi-message tune-request-message (real-time-message)
650 ;; :status-min #xf6 :status-max #xf6)
651 
652 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
653 ;;;
654 ;;; system exclusive messages
655 
656 (define-midi-message system-exclusive-message (system-message)
657  :status-min #xf0 :status-max #xf0
658  :slots ((data))
659  :filler (loop with len = (read-variable-length-quantity)
660  initially (setf data (make-array
661  len :element-type '(unsigned-byte 8)))
662  for i from 0 below len
663  do (setf (aref data i) next-byte))
664  :length (+ (length-of-variables-length-quantity (length data))
665  (length data))
666  :writer (progn (write-variable-length-quantity (length data))
667  (loop for elem across data do (write-bytes elem))))
668 
669 (define-midi-message authorization-system-exclusive-message (system-message)
670  :status-min #xf7 :status-max #xf7
671  :slots ((data))
672  :filler (loop with len = (read-variable-length-quantity)
673  initially (setf data (make-array
674  len :element-type '(unsigned-byte 8)))
675  for i from 0 below len
676  do (setf (aref data i) next-byte))
677  :length (+ (length-of-variables-length-quantity (length data))
678  (length data))
679  :writer (progn (write-variable-length-quantity (length data))
680  (loop for elem across data do (write-bytes elem))))
681 
682 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
683 ;;;
684 ;;; meta messages
685 
686 (define-midi-message meta-message (message)
687  :status-min #xff :status-max #xff
688  :length 2 ; the first data byte and the length byte
689  :filler next-byte ; the first data byte which gives the type of meta message
690  :writer (write-bytes data-min))
691 
692 (define-midi-message sequence-number-message (meta-message tempo-map-message)
693  :data-min #x00 :data-max #x00
694  :slots ((sequence))
695  :filler (let ((data2 next-byte))
696  (setf sequence (if (zerop data2)
697  0
698  (logior (ash next-byte 8) next-byte))))
699  :length (if (zerop sequence) 0 2)
700  :writer (unless (zerop sequence)
701  (write-bytes (ash sequence -8) (logand sequence #xf))))
702 
703 (define-midi-message text-message (meta-message)
704  :slots ((text))
705  :filler (setf text (loop with len = next-byte
706  with str = (make-string len)
707  for i from 0 below len
708  do (setf (aref str i)
709  (code-char next-byte))
710  finally (return str)))
711  :length (length text)
712  :writer (progn (write-bytes (length text))
713  (loop for char across text do
714  (write-bytes (char-code char)))))
715 
716 (defmethod print-midi-message ((object text-message) stream)
717  (call-next-method)
718  (when (slot-boundp object 'text)
719  (format stream " [~A]" (slot-value object 'text))))
720 
721 (define-midi-message general-text-message (text-message)
722  :data-min #x01 :data-max #x01)
723 
724 (define-midi-message copyright-message (text-message)
725  :data-min #x02 :data-max #x02)
726 
727 (define-midi-message sequence/track-name-message (text-message tempo-map-message)
728  :data-min #x03 :data-max #x03)
729 
730 (define-midi-message instrument-message (text-message)
731  :data-min #x04 :data-max #x04)
732 
733 (define-midi-message lyric-message (text-message)
734  :data-min #x05 :data-max #x05)
735 
736 (define-midi-message marker-message (text-message tempo-map-message)
737  :data-min #x06 :data-max #x06)
738 
739 (define-midi-message cue-point-message (text-message)
740  :data-min #x07 :data-max #x07)
741 
742 (define-midi-message program-name-message (text-message)
743  :data-min #x08 :data-max #x08)
744 
745 (define-midi-message device-name-message (text-message)
746  :data-min #x09 :data-max #x09)
747 
748 (define-midi-message channel-prefix-message (meta-message)
749  :data-min #x20 :data-max #x20
750  :slots ((channel))
751  :length 1
752  :filler (progn next-byte (setf channel next-byte))
753  :writer (write-bytes 1 channel))
754 
755 (define-midi-message midi-port-message (meta-message)
756  :data-min #x21 :data-max #x21
757  :slots ((port))
758  :length 1
759  :filler (progn next-byte (setf port next-byte))
760  :writer (write-bytes 1 port))
761 
762 (define-midi-message end-of-track-message (meta-message)
763  :data-min #x2f :data-max #x2f
764  :slots ((status :initform #xff))
765  :filler next-byte
766  :length 0
767  :writer (write-bytes 0))
768 
769 (define-midi-message tempo-message (meta-message tempo-map-message)
770  :data-min #x51 :data-max #x51
771  :slots ((tempo :initarg :tempo :reader message-tempo))
772  :filler (progn next-byte (setf tempo (read-fixed-length-quantity 3)))
773  :length 3
774  :writer (progn (write-bytes 3) (write-fixed-length-quantity tempo 3)))
775 
776 (defmethod print-midi-message ((object tempo-message) stream)
777  (call-next-method)
778  (when (slot-boundp object 'tempo)
779  (format stream " tempo=~A" (slot-value object 'tempo))))
780 
781 (define-midi-message smpte-offset-message (meta-message tempo-map-message)
782  :data-min #x54 :data-max #x54
783  :slots ((hr) (mn) (se) (fr) (ff))
784  :filler (progn next-byte (setf hr next-byte mn next-byte se next-byte
785  fr next-byte ff next-byte))
786  :length 5
787  :writer (write-bytes 5 hr mn se fr ff))
788 
789 (defmethod print-midi-message ((object smpte-offset-message) stream)
790  (call-next-method)
791  (when (or (slot-boundp object 'hr)
792  (slot-boundp object 'mn)
793  (slot-boundp object 'se)
794  (slot-boundp object 'fr)
795  (slot-boundp object 'ff))
796  (format stream
797  " hmsff=~A/~A/~A/~A/~A"
798  (ignore-errors (slot-value object 'hr))
799  (ignore-errors (slot-value object 'mn))
800  (ignore-errors (slot-value object 'se))
801  (ignore-errors (slot-value object 'fr))
802  (ignore-errors (slot-value object 'ff)))))
803 
804 (define-midi-message time-signature-message (meta-message tempo-map-message)
805  :data-min #x58 :data-max #x58
806  :slots ((nn :reader message-numerator)
807  (dd :reader message-denominator)
808  (cc) (bb))
809  :filler (progn next-byte (setf nn next-byte dd next-byte
810  cc next-byte bb next-byte))
811  :length 4
812  :writer (write-bytes 4 nn dd cc bb))
813 
814 (defmethod print-midi-message ((object time-signature-message) stream)
815  (call-next-method)
816  (when (or (slot-boundp object 'nn)
817  (slot-boundp object 'dd)
818  (slot-boundp object 'cc)
819  (slot-boundp object 'bb))
820  (format stream
821  " n/dcb=~A/~A/~A/~A"
822  (ignore-errors (slot-value object 'nn))
823  (ignore-errors (slot-value object 'dd))
824  (ignore-errors (slot-value object 'cc))
825  (ignore-errors (slot-value object 'bb)))))
826 
827 (define-midi-message key-signature-message (meta-message)
828  :data-min #x59 :data-max #x59
829  :slots ((sf :reader message-sf)
830  (mi :reader message-mi))
831  :filler (progn next-byte (setf sf (let ((temp-sf next-byte))
832  (if (> temp-sf 127)
833  (- temp-sf 256)
834  temp-sf))
835  mi next-byte))
836  :length 2
837  :writer (write-bytes 2 (if (< sf 0) (+ sf 256) sf) mi))
838 
839 (define-midi-message proprietary-event (meta-message)
840  :data-min #x7f :data-max #x7f
841  :slots ((data))
842  :filler (setf data (loop with len = (read-variable-length-quantity)
843  with vec = (make-array
844  len
845  :element-type '(unsigned-byte 8))
846  for i from 0 below len
847  do (setf (aref vec i) next-byte)
848  finally (return vec)))
849  :writer (map nil (lambda (byte) (write-bytes byte))
850  data)) ; FIXME