# HG changeset patch # User d.lewis # Date 1199367932 0 # Node ID f1d0ea63581c5fe1a555f310881e2c987e1bfda5 # Parent 4cb3ec07831f4dc2636e0aaa186272647d7ff3a5 Improved spelling for score output. amuse-gsharp now depends on ps13 darcs-hash:20080103134532-40ec0-e36c64cecc14ce9f821da7381d546c9e87bc7f63.gz diff -r 4cb3ec07831f -r f1d0ea63581c amuse-gsharp.asd --- a/amuse-gsharp.asd Tue Jan 01 13:22:15 2008 +0000 +++ b/amuse-gsharp.asd Thu Jan 03 13:45:32 2008 +0000 @@ -1,5 +1,5 @@ (asdf:defsystem amuse-gsharp - :depends-on (amuse gsharp) + :depends-on (amuse gsharp ps13) :components ((:module implementations :components diff -r 4cb3ec07831f -r f1d0ea63581c implementations/gsharp/classes.lisp --- a/implementations/gsharp/classes.lisp Tue Jan 01 13:22:15 2008 +0000 +++ b/implementations/gsharp/classes.lisp Thu Jan 03 13:45:32 2008 +0000 @@ -1,14 +1,16 @@ (cl:in-package "AMUSE-GSHARP") -(defclass gsharp-composition (amuse:standard-composition) +(defclass gsharp-object ()()) + +(defclass gsharp-composition (amuse:standard-composition gsharp-object) ((buffer :initarg :buffer :reader buffer) (tempi :initarg :tempi :reader tempi))) -(defclass gsharp-pitched-event (standard-chromatic-pitched-event) +(defclass gsharp-pitched-event (standard-chromatic-pitched-event gsharp-object) ((note :initarg :note :reader note) (slice-index :initarg :slice-index))) -(defclass gsharp-identifier (identifier) +(defclass gsharp-identifier (identifier gsharp-object) ((pathname :initarg :path :reader %gsharp-identifier-pathname :initform 'nil)) diff -r 4cb3ec07831f -r f1d0ea63581c implementations/gsharp/methods.lisp --- a/implementations/gsharp/methods.lisp Tue Jan 01 13:22:15 2008 +0000 +++ b/implementations/gsharp/methods.lisp Thu Jan 03 13:45:32 2008 +0000 @@ -55,3 +55,16 @@ (gsharp::frame-find-file frame (%gsharp-identifier-pathname id))) (defmethod import-from-identifier (frame (id gsharp-mxml-identifier)) (clim:execute-frame-command frame `(gsharp::com-import-musicxml ,(%gsharp-identifier-pathname id)))) + +(defmethod get-applicable-key-signatures (anchored-period (composition gsharp-composition)) + (let ((keysigs)) + (sequence::dosequence (event composition (reverse keysigs)) + (cond + ((overlaps event anchored-period) + (unless (member (gsharp::keysig event) keysigs) + (push (gsharp::keysig event) keysigs))) + ((not (before event anchored-period)) + (return-from get-applicable-key-signatures (reverse keysigs))))))) + +(defmethod crotchet ((object gsharp-object)) + (make-standard-period 1)) \ No newline at end of file diff -r 4cb3ec07831f -r f1d0ea63581c tools/gsharp-output.lisp --- a/tools/gsharp-output.lisp Tue Jan 01 13:22:15 2008 +0000 +++ b/tools/gsharp-output.lisp Thu Jan 03 13:45:32 2008 +0000 @@ -104,7 +104,7 @@ layers) ;; Add the notes and bars (add-bars-and-events-to-layer (car layers) (reverse events) - time-signatures + time-signatures composition :key-signatures key-signatures))) layer-events) ;; Attach layers to a segment and place segment into buffer @@ -136,7 +136,8 @@ (if (< (midi-pitch-number event) 60) 1 0)))))))) -(defun add-bars-and-events-to-layer (layer events time-signatures &key (key-signatures nil)) +(defun add-bars-and-events-to-layer (layer events time-signatures composition + &key (key-signatures nil)) "Given list of events to be attached to a layer, along with applicable time signatures, clumsily waddle through them all and slap an approximation to the events into place. Improve this." @@ -170,6 +171,9 @@ ;; sense. At the moment, it just removes short rests... (setf ons (check-ons ons)) ;; Now create the bars and the gsharp clusters + (when key-signatures + (setf (gsharp::keysig (car (gsharp::staves layer))) + (make-gsharp-key-signature (car key-signatures) layer))) (do ((old-ons nil ons) (ons ons (cdr ons))) ((null (cdr ons))) @@ -177,6 +181,15 @@ (setf bar (gsharp::make-melody-bar)) (gsharp::add-bar bar body bar-no) (incf bar-no)) + (when (and key-signatures + (<= (timepoint (car key-signatures)) + (caar ons))) + (gsharp-mxml::add-element-at-duration (make-gsharp-key-signature (car key-signatures) layer) + bar + (/ (- (timepoint (car key-signatures)) + (caar beat-starts)) + 4)) + (setf key-signatures (cdr key-signatures))) ;; A quick check for notes which span beats and don't start at ;; the beginning of their beats. IMO, this makes them more ;; likely to require a tie. @@ -220,41 +233,41 @@ (/ (- now (car bar-starts)) 4)) (dolist (note (cdr (car ons))) - (when note - (let ((pitch (pitch-for-gsharp note))) - (gsharp::add-note (car clusters) - (make-instance 'gsharp::note - :pitch (first pitch) - :accidentals (second pitch) - :staff (car (gsharp::staves layer)) - :tie-right (if (cdr clusters) - t - (member note (second ons))) - :tie-left (if first-p - (member note (first old-ons)) - t)))))) + (with-simple-restart (ignore "Ignore note") + (when note + (let ((pitch (pitch-for-gsharp note composition))) + (gsharp::add-note (car clusters) + (make-instance 'gsharp::note + :pitch (first pitch) + :accidentals (second pitch) + :staff (car (gsharp::staves layer)) + :tie-right (if (cdr clusters) + t + (member note (second ons))) + :tie-left (if first-p + (member note (first old-ons)) + t))))))) (incf now (* (gsharp::duration (car clusters)) 4)) (setf first-p nil))) (when (and (cdr bar-starts) (= (car (second ons)) (second bar-starts))) (setf bar-starts (cdr bar-starts) - beat-starts (cdr beat-starts)))) - (dolist (key-signature key-signatures) - ;; code half-inched from mxml.lisp (maybe there's a useful - ;; function to be abstracted here?), but I don't really - ;; understand how key changes work...? - (let ((alterations (make-array 7)) - (order-of-sharps #(3 0 4 1 5 2 6)) - (order-of-flats #(6 2 5 1 4 0 3))) - (if (< (key-signature-sharps key-signature) 0) - (dotimes (index (abs (key-signature-sharps key-signature))) - (setf (elt alterations (elt order-of-flats index)) :flat)) - (dotimes (index (key-signature-sharps key-signature)) - (setf (elt alterations (elt order-of-sharps index)) :sharp))) - (setf (gsharp::keysig (car (gsharp::staves layer))) - (gsharp-buffer::make-key-signature (car (gsharp::staves layer)) - :alteration alterations)))))) + beat-starts (cdr beat-starts)))))) + + +(defgeneric make-gsharp-key-signature (key-signature layer)) +(defmethod make-gsharp-key-signature ((key-signature standard-key-signature) layer) + (let ((alterations (make-array 7)) + (order-of-sharps #(3 0 4 1 5 2 6)) + (order-of-flats #(6 2 5 1 4 0 3))) + (if (< (key-signature-sharps key-signature) 0) + (dotimes (index (abs (key-signature-sharps key-signature))) + (setf (elt alterations (elt order-of-flats index)) :flat)) + (dotimes (index (key-signature-sharps key-signature)) + (setf (elt alterations (elt order-of-sharps index)) :sharp))) + (gsharp-buffer::make-key-signature (car (gsharp::staves layer)) + :alterations alterations))) ;;;;;;;;;;;;;;;;;;;;;;; ;; @@ -262,10 +275,11 @@ ;; Pitch -(defgeneric pitch-for-gsharp (pitch) +(defgeneric pitch-for-gsharp (pitch composition) (:documentation "Given a pitch object, return a list of gsharp's pitch number and accidental keyword")) -(defmethod pitch-for-gsharp ((pitch diatonic-pitch)) +(defmethod pitch-for-gsharp ((pitch diatonic-pitch) composition) + (declare (ignore composition)) ;; Easy for diatonic pitch, although behaviour for extreme or ;; fractional alterations is unclear. (list (1+ (diatonic-pitch-mp pitch)) @@ -276,9 +290,9 @@ (-2 :double-flat) (0 :natural) (otherwise (error "gsharp can't handle this pitch"))))) -(defmethod pitch-for-gsharp ((pitch chromatic-pitch)) - ;; Just go for line-of-fifths proximity spelling. Could always try - ;; to spell it, but... +(defmethod pitch-for-gsharp ((pitch chromatic-pitch) composition) + ;; Just go for line-of-fifths proximity spelling, but based on + ;; keysig if present. Could always try to spell it with ps13, but.. (let* ((octave (octave pitch)) (pitch-class (pitch-class pitch)) (diatonic-pitch-number (aref #(0 0 1 2 2 3 3 4 4 5 6 6) pitch-class))) @@ -291,6 +305,53 @@ :natural ;; A :flat :natural) ;; Bb B pitch-class)))) +(defmethod pitch-for-gsharp ((event standard-chromatic-pitched-event) composition) + ;; Should probably go for line-of-fifths proximity spelling, + ;; if keysig present, but ps13ing for now. + (let* ((octave (octave event)) + (event-pos (position event composition)) + (note-sequence (get-spelling-list composition)) + (spelling (elt note-sequence event-pos)) + (note-name (cdr (assoc (aref (second spelling) 0) + '(("C" . 0) ("D" . 1) ("E" . 2) ("F" . 3) + ("G" . 4) ("A" . 5) ("B" . 6)) + :test #'string=))) + (accidental (cdr (assoc (aref (second spelling) 1) + '(("ss" . :double-sharp) ("s" . :sharp) ("n" . :natural) + ("f" . :flat) ("ff" . :double-flat)) + :test #'string=)))) + (list (+ (* 7 octave) note-name) accidental))) + + +(defparameter *spelling-cache* (make-hash-table)) +(defparameter *ocp-cache* (make-hash-table)) + +(defun get-spelling-list (composition) + (unless (gethash composition *spelling-cache*) + (setf (gethash composition *spelling-cache*) + (ps13:ps13-new-imp (map 'list #'cdr (get-ocp-list composition)) + 10 42 nil nil nil))) + (gethash composition *spelling-cache*)) + +(defun get-ocp-list (composition) + (unless (gethash composition *ocp-cache*) + (setf (gethash composition *ocp-cache*) (ocp-list composition))) + (gethash composition *ocp-cache*)) + +(defun ocp-list (composition) + (flet ((sorter (x y) + (or (amuse:time< x y) + (and (amuse:time= x y) + (amuse:pitch< x y))))) + (loop for e being each element of composition + if (typep e 'amuse:pitched-event) + collect (cons e (make-array 2 :initial-contents + (list (slot-value e 'amuse::time) + (- (slot-value e 'amuse::number) 21)))) into result + finally (return (sort result #'sorter :key #'car))))) + + + ;; Time @@ -382,9 +443,35 @@ (defgeneric gsharp-layer-string (event) (:method (e) (name-from-channel-and-patch e)) + (:method ((e amuse-gsharp::gsharp-object)) + (name-from-layer e)) (:documentation "Return a string that uniquely identifies the layer to which event belongs")) +(defun name-from-layer (event) + (let* ((layers (gsharp::layers + (car + (gsharp::segments + (gsharp::buffer + (gsharp::staff + (amuse-gsharp::note event))))))) + (layer (gsharp::layer + (gsharp::slice + (gsharp::bar + (gsharp::cluster + (amuse-gsharp::note event)))))) + (name (gsharp::name layer)) + (count)) + (dolist (cand-layer layers) + (if (eq cand-layer layer) + (if count + (return-from name-from-layer (concatenate 'string + name + (princ-to-string count))) + (return-from name-from-layer name)) + (when (string= name (gsharp::name cand-layer)) + (setf count (if count (+ count 1) 2))))))) + (defun name-from-channel-and-patch (event) "Generate layer-identifying string from the patch and channel that would be used for midi export. For MIDI, this is guaranteed to @@ -402,16 +489,21 @@ collect i))) (defun beat-starts (time-signature-list &key (crotchet 1)) - (loop for time-signature in time-signature-list - nconc (loop for i from (timepoint (onset time-signature)) - to (1- (timepoint (cut-off time-signature))) - by (* (crotchets-in-a-bar time-signature) crotchet) - collect (loop for j from 0 + (if time-signature-list + (loop for time-signature in time-signature-list + nconc (loop for i from (timepoint (onset time-signature)) + to (1- (timepoint (cut-off time-signature))) + by (* (crotchets-in-a-bar time-signature) crotchet) + collect (loop for j from 0 to (* (crotchets-in-a-bar time-signature) crotchet) by (* (tactus-duration time-signature) crotchet) - collect (+ j i))))) + collect (+ j i)))) + ;; FIXME: fudge + (loop for i from 0 to 1000 by 4 + collect (loop for j from i to (+ i 3) + collect j)))) ;;;;;;;;;;;;;;;;;; ;; @@ -490,11 +582,18 @@ (do ((ons ons (cdr ons))) ((null (cdr ons)) (reverse (cons (car ons) new-ons))) - (unless (and (<= (- (car (second ons)) - (car (first ons))) - *rounding-factor*) - (null (cdr (first ons)))) - (push (car ons) new-ons))))) + (unless + (and (<= (- (car (second ons)) + (car (first ons))) + *rounding-factor*) + (null (cdr (first ons)))) + (if (= (- (car (second ons)) + (car (first ons))) + 0) + (push (cons (caar ons) (remove-duplicates (nconc (cdr (first ons)) + (cdr (second ons))))) + new-ons) + (push (car ons) new-ons)))))) (defun guess-rounding-factor (events) "Assuming that only durations need quantising, look at the lcd for