changeset 64:c8f1b0ab0007

midi-output is now in tools darcs-hash:20070628144900-f76cc-f96cd99afbd2d9cf3274717ade3578e4c97559e8.gz
author David Lewis <d.lewis@gold.ac.uk>
date Thu, 28 Jun 2007 15:49:00 +0100
parents 32314fefc706
children 5b02163ade2a
files amuse.asd implementations/midi/package.lisp tools/midi-output.lisp tools/package.lisp utils/midi-output.lisp utils/package.lisp
diffstat 6 files changed, 173 insertions(+), 167 deletions(-) [+]
line wrap: on
line diff
--- a/amuse.asd	Thu Jun 28 14:53:53 2007 +0100
+++ b/amuse.asd	Thu Jun 28 15:49:00 2007 +0100
@@ -17,8 +17,11 @@
             :components
             ((:file "package")
              (:file "utils" :depends-on ("package"))
-	     (:file "n-grams" :depends-on ("package"))
-	     (:file "midi-output" :depends-on ("package" "utils"))))
+	     (:file "n-grams" :depends-on ("package"))))
+   (:module tools            
+            :components
+	    ((:file "package")
+	     (:file "midi-output" :depends-on ("package"))))
    (:module implementations
             :components 
             ((:module midi
--- a/implementations/midi/package.lisp	Thu Jun 28 14:53:53 2007 +0100
+++ b/implementations/midi/package.lisp	Thu Jun 28 15:49:00 2007 +0100
@@ -1,5 +1,5 @@
 (cl:defpackage #:amuse-midi 
-  (:use #:common-lisp #:amuse #:amuse-utils)
+  (:use #:common-lisp #:amuse #:amuse-utils #:amuse-tools)
   (:export #:midi-composition
 	   #:midi-pitched-event
            #:midi-percussive-event
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/midi-output.lisp	Thu Jun 28 15:49:00 2007 +0100
@@ -0,0 +1,155 @@
+;; Make midifiles from basic amuse objects methods here can be
+;; overridden for more specific types
+;; 
+
+;; FIXME: Need to push some structures from geerdes to make this work.
+
+(in-package #:amuse-tools)
+
+(defgeneric play (music)
+  (:method (m) (play-midifile (make-midi m))))
+(defmethod play ((music composition))
+  (play-midifile (make-midi music)))
+
+(defun play-midifile (midifile)
+  ;; coremidi is easy as an alternative, but we'll probably want midi
+  ;; file export anyway, so it makes some sense to focus our efforts
+  ;; on this first. That said, is there a CoreAudio midi file player
+  ;; routine?
+  (midi:write-midi-file midifile "tmp.mid")
+  #+darwin
+  (when (sb-impl::find-executable-in-search-path "open")
+    (asdf:run-shell-command "open tmp.mid")
+    (return-from play-midifile T))
+  (when (sb-impl::find-executable-in-search-path "timidity")
+    (asdf:run-shell-command "timidity tmp.mid")
+    (return-from play-midifile T)))
+
+(defgeneric make-midi (sequence))
+(defmethod make-midi ((sequence sequence))
+  ;; Make a midifile object. Collects global midi messages (that
+  ;; require a sequence) and event-based messages (that don't).
+  ;; FIXME: Something about this strikes me as very stupid. Must
+  ;; revisit
+  ;; FIXME: Only making type 0. Is this a problem?
+  (let* ((events (event-sequence-messages sequence))
+	 (globals (global-messages sequence))
+	 (patches (patch-messages sequence)))
+    (make-midifile-from-messages (nconc events globals patches)
+				 :type 0)))
+
+(defun make-midifile-from-messages (events &key (type 0))
+  ;; FIXME: clearly broken if type 1
+  ;; First have to get rid of all fractional times and choose a
+  ;; timebase
+  (let* ((timebase (apply #'lcm (mapcar #'(lambda (x)
+					    (denominator
+					     (midi:message-time x)))
+					events))))
+    (when (< timebase 4)
+      (setf timebase (* 4 timebase)))
+    (loop for e in events
+       do (setf (midi:message-time e) (* timebase
+					 (midi:message-time e))))
+    (make-instance 'midi:midifile
+		   :format type
+		   :division timebase
+		   :tracks (list (sort-midi-messages-for-output events)))))
+
+(defun sort-midi-messages-for-output (messages)
+  (sort messages #'(lambda (x y) (or (< (midi:message-time x)
+					(midi:message-time y))
+				     (and (= (midi:message-time x)
+					     (midi:message-time y))
+					  (< (midi::message-status x)
+					     (midi::message-status y)))))))
+
+(defun event-sequence-messages (sequence)
+  (let ((midinotes))
+    (sequence:dosequence (event sequence midinotes)
+      (let ((messages (event-messages event)))
+	(dolist (message messages)
+	  (push message midinotes))))))
+
+(defun patch-messages (sequence)
+  (let ((patches (make-array 16 :initial-element nil))
+	(patch-list)
+	(channel 0)
+	(patch 0))
+    (sequence:dosequence (event sequence patch-list)
+      (setf channel (get-channel-for-midi event)
+	    patch (get-patch-for-midi event))
+      (when (or (not (aref patches channel))
+		(not (= (aref patches channel)
+			patch)))
+	(push (make-instance 'midi:program-change-message
+			     :program patch
+			     :time (timepoint event)
+			     :status (+ channel 192))
+	      patch-list)
+	(setf (aref patches channel) patch)))))
+		
+
+(defgeneric global-messages (sequence)
+  (:method (s) (declare (ignore s)) nil))
+(defmethod global-messages ((sequence composition))
+  ;; FIXME: missing plenty of other messages
+  ;; FIXME: messy
+  (let ((tempi (tempi sequence))
+	(temp)
+	(time-sigs (time-signatures sequence))
+	(events))
+    (dolist (tempo tempi)
+      (setf temp (tempo-message tempo))
+      (when temp
+	(push temp events)))
+    (dolist (time-sig time-sigs events)
+      (setf temp (time-sig-message time-sig))
+      (when temp
+	(push temp events)))))
+
+(defgeneric tempo-message (tempo)
+  (:method (tp)
+    (progn
+      (let ((speed (make-instance 'midi:tempo-message
+				  :time (timepoint tp)
+				  :status 255)))
+	(setf (slot-value speed 'midi::tempo) (microseconds-per-crotchet tp))
+	speed))))
+
+(defgeneric time-sig-message (time-sig)
+  (:method (ts) (declare (ignore ts)) nil))
+
+(defgeneric event-messages (event)
+  (:method (e) (declare (ignore e)) nil))
+(defmethod event-messages ((event pitched-event))
+  (list (make-instance 'midi:note-on-message
+		       :status (+ (get-channel-for-midi event) 144)
+		       :key (midi-pitch-number event)
+		       :velocity (get-velocity-for-midi event)
+		       :time (timepoint event))
+	(make-instance 'midi:note-off-message
+		       :status (+ (get-channel-for-midi event) 128)
+		       :key (midi-pitch-number event)
+		       :velocity (get-velocity-for-midi event)
+		       :time (timepoint (cut-off event)))))
+
+(defmethod event-messages ((event percussive-event))
+  (list (make-instance 'midi:note-on-message
+		       :status 153
+		       :key (get-pitch-for-midi event)
+		       :velocity (get-velocity-for-midi event)
+		       :time (timepoint event))
+	(make-instance 'midi:note-off-message
+		       :status 137
+		       :key (get-pitch-for-midi event)
+		       :velocity (get-velocity-for-midi event)
+		       :time (timepoint (cut-off event)))))
+
+(defgeneric get-pitch-for-midi (event))
+(defgeneric get-velocity-for-midi (event)
+  (:method (e) (declare (ignore e)) 100))
+(defgeneric get-patch-for-midi (event)
+  (:method (e) (declare (ignore e)) 0))
+(defgeneric get-channel-for-midi (event)
+  (:method (e) (declare (ignore e)) 0))
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/package.lisp	Thu Jun 28 15:49:00 2007 +0100
@@ -0,0 +1,12 @@
+(cl:defpackage #:amuse-tools
+  (:use #:common-lisp #:amuse #:amuse-utils #:midi)
+  (:export #:play
+	   #:make-midi
+	   #:global-messages
+	   #:tempo-message
+	   #:event-messages
+	   #:get-patch-for-midi
+	   #:get-channel-for-midi
+	   #:get-pitch-for-midi
+	   #:get-velocity-for-midi
+	   ))
--- a/utils/midi-output.lisp	Thu Jun 28 14:53:53 2007 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,155 +0,0 @@
-;; Make midifiles from basic amuse objects methods here can be
-;; overridden for more specific types
-;; 
-
-;; FIXME: Need to push some structures from geerdes to make this work.
-
-(in-package #:amuse-utils)
-
-(defgeneric play (music)
-  (:method (m) (play-midifile (make-midi m))))
-(defmethod play ((music composition))
-  (play-midifile (make-midi music)))
-
-(defun play-midifile (midifile)
-  ;; coremidi is easy as an alternative, but we'll probably want midi
-  ;; file export anyway, so it makes some sense to focus our efforts
-  ;; on this first. That said, is there a CoreAudio midi file player
-  ;; routine?
-  (midi:write-midi-file midifile "tmp.mid")
-  #+darwin
-  (when (sb-impl::find-executable-in-search-path "open")
-    (asdf:run-shell-command "open tmp.mid")
-    (return-from play-midifile T))
-  (when (sb-impl::find-executable-in-search-path "timidity")
-    (asdf:run-shell-command "timidity tmp.mid")
-    (return-from play-midifile T)))
-
-(defgeneric make-midi (sequence))
-(defmethod make-midi ((sequence sequence))
-  ;; Make a midifile object. Collects global midi messages (that
-  ;; require a sequence) and event-based messages (that don't).
-  ;; FIXME: Something about this strikes me as very stupid. Must
-  ;; revisit
-  ;; FIXME: Only making type 0. Is this a problem?
-  (let* ((events (event-sequence-messages sequence))
-	 (globals (global-messages sequence))
-	 (patches (patch-messages sequence)))
-    (make-midifile-from-messages (nconc events globals patches)
-				 :type 0)))
-
-(defun make-midifile-from-messages (events &key (type 0))
-  ;; FIXME: clearly broken if type 1
-  ;; First have to get rid of all fractional times and choose a
-  ;; timebase
-  (let* ((timebase (apply #'lcm (mapcar #'(lambda (x)
-					    (denominator
-					     (midi:message-time x)))
-					events))))
-    (when (< timebase 4)
-      (setf timebase (* 4 timebase)))
-    (loop for e in events
-       do (setf (midi:message-time e) (* timebase
-					 (midi:message-time e))))
-    (make-instance 'midi:midifile
-		   :format type
-		   :division timebase
-		   :tracks (list (sort-midi-messages-for-output events)))))
-
-(defun sort-midi-messages-for-output (messages)
-  (sort messages #'(lambda (x y) (or (< (midi:message-time x)
-					(midi:message-time y))
-				     (and (= (midi:message-time x)
-					     (midi:message-time y))
-					  (< (midi::message-status x)
-					     (midi::message-status y)))))))
-
-(defun event-sequence-messages (sequence)
-  (let ((midinotes))
-    (sequence:dosequence (event sequence midinotes)
-      (let ((messages (event-messages event)))
-	(dolist (message messages)
-	  (push message midinotes))))))
-
-(defun patch-messages (sequence)
-  (let ((patches (make-array 16 :initial-element nil))
-	(patch-list)
-	(channel 0)
-	(patch 0))
-    (sequence:dosequence (event sequence patch-list)
-      (setf channel (get-channel-for-midi event)
-	    patch (get-patch-for-midi event))
-      (when (or (not (aref patches channel))
-		(not (= (aref patches channel)
-			patch)))
-	(push (make-instance 'midi:program-change-message
-			     :program patch
-			     :time (timepoint event)
-			     :status (+ channel 192))
-	      patch-list)
-	(setf (aref patches channel) patch)))))
-		
-
-(defgeneric global-messages (sequence)
-  (:method (s) (declare (ignore s)) nil))
-(defmethod global-messages ((sequence composition))
-  ;; FIXME: missing plenty of other messages
-  ;; FIXME: messy
-  (let ((tempi (tempi sequence))
-	(temp)
-	(time-sigs (time-signatures sequence))
-	(events))
-    (dolist (tempo tempi)
-      (setf temp (tempo-message tempo))
-      (when temp
-	(push temp events)))
-    (dolist (time-sig time-sigs events)
-      (setf temp (time-sig-message time-sig))
-      (when temp
-	(push temp events)))))
-
-(defgeneric tempo-message (tempo)
-  (:method (tp)
-    (progn
-      (let ((speed (make-instance 'midi:tempo-message
-				  :time (timepoint tp)
-				  :status 255)))
-	(setf (slot-value speed 'midi::tempo) (microseconds-per-crotchet tp))
-	speed))))
-
-(defgeneric time-sig-message (time-sig)
-  (:method (ts) (declare (ignore ts)) nil))
-
-(defgeneric event-messages (event)
-  (:method (e) (declare (ignore e)) nil))
-(defmethod event-messages ((event pitched-event))
-  (list (make-instance 'midi:note-on-message
-		       :status (+ (get-channel-for-midi event) 144)
-		       :key (midi-pitch-number event)
-		       :velocity (get-velocity-for-midi event)
-		       :time (timepoint event))
-	(make-instance 'midi:note-off-message
-		       :status (+ (get-channel-for-midi event) 128)
-		       :key (midi-pitch-number event)
-		       :velocity (get-velocity-for-midi event)
-		       :time (timepoint (cut-off event)))))
-
-(defmethod event-messages ((event percussive-event))
-  (list (make-instance 'midi:note-on-message
-		       :status 153
-		       :key (get-pitch-for-midi event)
-		       :velocity (get-velocity-for-midi event)
-		       :time (timepoint event))
-	(make-instance 'midi:note-off-message
-		       :status 137
-		       :key (get-pitch-for-midi event)
-		       :velocity (get-velocity-for-midi event)
-		       :time (timepoint (cut-off event)))))
-
-(defgeneric get-pitch-for-midi (event))
-(defgeneric get-velocity-for-midi (event)
-  (:method (e) (declare (ignore e)) 100))
-(defgeneric get-patch-for-midi (event)
-  (:method (e) (declare (ignore e)) 0))
-(defgeneric get-channel-for-midi (event)
-  (:method (e) (declare (ignore e)) 0))
--- a/utils/package.lisp	Thu Jun 28 14:53:53 2007 +0100
+++ b/utils/package.lisp	Thu Jun 28 15:49:00 2007 +0100
@@ -7,15 +7,6 @@
 	   #:bass-note
 	   #:crotchets-in-a-bar
 	   #:sounding-events
-	   #:play
-	   #:make-midi
-	   #:global-messages
-	   #:tempo-message
-	   #:event-messages
-	   #:get-patch-for-midi
-	   #:get-channel-for-midi
-	   #:get-pitch-for-midi
-	   #:get-velocity-for-midi
 	   #:levenshtein-distance
 	   #:beats-to-seconds
 	   #:get-n-grams