# HG changeset patch # User d.lewis # Date 1205407612 0 # Node ID e5de0895d84399d2b77dde1c45f8d3ed119b9dbf # Parent cddf83554c08de35d4dadf26248c0b9c7f910cb0 Gsharp-output darcs-hash:20080313112652-40ec0-64241751ae1c0bfc32c3e35deac499132728c5bf.gz diff -r cddf83554c08 -r e5de0895d843 implementations/gsharp/methods.lisp --- a/implementations/gsharp/methods.lisp Thu Mar 13 11:25:36 2008 +0000 +++ b/implementations/gsharp/methods.lisp Thu Mar 13 11:26:52 2008 +0000 @@ -58,13 +58,48 @@ (defmethod get-applicable-key-signatures (anchored-period (composition gsharp-composition)) (let ((keysigs)) - (sequence::dosequence (event composition (reverse keysigs)) + (sequence::dosequence (event composition (mapcar #'import-key-signature (reverse keysigs))) (cond ((overlaps event anchored-period) - (unless (member (gsharp::keysig event) keysigs) - (push (gsharp::keysig event) keysigs))) + (unless (member (gsharp::keysig (note event)) keysigs) + (push (gsharp::keysig (note event)) keysigs))) ((not (before event anchored-period)) - (return-from get-applicable-key-signatures (reverse keysigs))))))) + (return-from get-applicable-key-signatures (mapcar #'import-key-signature (reverse keysigs)))))))) + +(defun import-key-signature (gsharp-keysig) + ;; FIXME: This is WRONG - shouldn't be using standard key signature, + ;; since important detail is lost (very rarely) + (make-standard-key-signature-period (- (count :sharp (gsharp::alterations gsharp-keysig)) + (count :flat (gsharp::alterations gsharp-keysig))) + ())) (defmethod crotchet ((object gsharp-object)) - (make-standard-period 1)) \ No newline at end of file + (make-standard-period 1)) + +;;; +;; Experimental + +(defmethod amuse::current-bar ((moment standard-moment) + (composition gsharp-composition)) + ;; No, I don't know how (or if) these work. But it's a hard problem, + ;; so I don't mind cheating. + (let ((bar-lengths (gsharp-play::measure-durations + (mapcar #'gsharp-buffer:body + (gsharp-buffer::layers (car (gsharp::segments + (amuse-gsharp::buffer composition))))))) + (moment-time (timepoint moment)) (now 0)) + (dolist (bar-duration bar-lengths) + (when (> (+ now (* bar-duration 4)) moment-time) + (return-from amuse::current-bar + (make-standard-anchored-period now (* bar-duration 4)))) + (incf now (* bar-duration 4))))) + +(defmethod get-applicable-clefs (anchored-period (composition gsharp-composition)) + (let ((clefs)) + (sequence::dosequence (event composition (mapcar #'import-clef (reverse clefs))) + (cond + ((overlaps event anchored-period) + (unless (member (gsharp::clef (gsharp::staff (note event))) clefs) + (push (gsharp::clef (gsharp::staff (note event))) clefs))) + ((not (before event anchored-period)) + (return-from get-applicable-clefs (mapcar #'import-clef (reverse clefs)))))))) \ No newline at end of file diff -r cddf83554c08 -r e5de0895d843 tools/gsharp-output.lisp --- a/tools/gsharp-output.lisp Thu Mar 13 11:25:36 2008 +0000 +++ b/tools/gsharp-output.lisp Thu Mar 13 11:26:52 2008 +0000 @@ -39,6 +39,19 @@ ;; ;; * Clef is guessed at - we need amuse:get-applicable-clef (and ;; amuse:clef) +;; +;; * Anacruses are ignored unless they have their own time-signatures! +;; +;; The stages used are: +;; 1 - find layers (1:1 with staves at the moment) +;; 2 - for each layer, get events and their timings (includes +;; rounding/quantization) +;; 3 - Find all ties needed for graphical/courtesy reasons (includes +;; removal of extraneous rests and other artefacts) +;; 4 - Generate gsharp clusters +;; 5 - Get gsharp pitch for events and add notes to clusters +;; + (in-package "AMUSE-TOOLS") @@ -51,6 +64,8 @@ ;;;;;;;;;;;;;;;;;;;;; ;; Top-level methods +(defparameter *foo* nil) + (defun write-gsharp-eps (composition pathname) ;; write a score eps from a composition. Most of this can be copied ;; straight (this is copied already from CSR's code) @@ -66,6 +81,7 @@ (setf (gsharp::modified-p (car (esa:buffers frame))) t) (gsharp::recompute-measures (car (esa:buffers frame))) ;; Print + (setf *foo* (car (esa:buffers frame))) (clim:execute-frame-command frame `(gsharp::com-print-buffer-to-file ,pathname)))) @@ -122,17 +138,20 @@ (defmethod identify-gsharp-layers ((composition composition)) (let ((layer-events (make-hash-table :test #'equal)) - (layer-scores (make-hash-table :test #'equal))) + (layer-scores (make-hash-table :test #'equal)) + (layer-clefs (make-hash-table :test #'equal)) + (layer-name)) (sequence:dosequence (event composition (values layer-events layer-scores)) (when (pitchedp event) + (setf layer-name (gsharp-layer-string event)) (cond - ((gethash (gsharp-layer-string event) layer-events) - (push event (gethash (gsharp-layer-string event) layer-events)) + ((gethash layer-name layer-events) + (push event (gethash layer-name layer-events)) (when (< (midi-pitch-number event) 60) - (incf (gethash (gsharp-layer-string event) layer-scores)))) - (t (setf (gethash (gsharp-layer-string event) layer-events) + (incf (gethash layer-name layer-scores)))) + (t (setf (gethash layer-name layer-events) (list event) - (gethash (gsharp-layer-string event) layer-scores) + (gethash layer-name layer-scores) (if (< (midi-pitch-number event) 60) 1 0)))))))) @@ -141,11 +160,35 @@ "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." - (let* ((crotchet-beats (duration (crotchet (car events)))) ;; (or make into semibreves?) - (beat-starts (beat-starts time-signatures - :crotchet crotchet-beats)) - (bar-starts (mapcar #'car beat-starts)) - (ons) + (let* ( +;; (crotchet-beats (duration (crotchet (car events)))) ;; (or make into semibreves?) +;; (beat-starts (beat-starts time-signatures +;; :crotchet crotchet-beats)) +;; (bar-starts (mapcar #'car beat-starts)) + (bar-starts (let ((starts)) + (do ((bar-period (amuse::current-bar (make-standard-moment 0) composition) + (amuse::current-bar (cut-off bar-period) composition))) + ((time>= (cut-off bar-period) (cut-off composition)) + (reverse (cons (timepoint bar-period) starts))) + (push (timepoint bar-period) starts)))) + (beat-starts (if time-signatures + (let ((starts) (current)) + (do* ((bars bar-starts) + (beat-period (amuse::current-beat (make-standard-moment 0) composition) + (amuse::current-beat (cut-off beat-period) composition)) + (beat-time (timepoint beat-period) (timepoint beat-period))) + ((time>= (onset beat-period) (cut-off composition)) + (reverse (if current + (cons (reverse current) starts) + starts))) + (when (and (cdr bars) + (>= beat-time (second bars))) + (push (reverse current) starts) + (setf current nil + bars (cdr bars))) + (push beat-time current))) + (mapcar #'list bar-starts))) + (ons) (position) (clusters) (bar) (bar-no 0) (body (gsharp::body layer))) ;; this is a cheat to guess timing rounding (quantisation) based @@ -169,7 +212,7 @@ ;; rounding happens, but there is no check for bar length here or ;; within g-sharp, this should verify that everything makes ;; sense. At the moment, it just removes short rests... - (setf ons (check-ons ons)) + (setf ons (check-ons ons bar-starts)) ;; Now create the bars and the gsharp clusters (when key-signatures (setf (gsharp::keysig (car (gsharp::staves layer))) @@ -178,27 +221,30 @@ (ons ons (cdr ons))) ((null (cdr ons))) (when (member (caar ons) bar-starts) + ;; We're at the beginning of a bar. + (when bar (check-beams bar)) (setf bar (gsharp::make-melody-bar)) (gsharp::add-bar bar body bar-no) - (incf bar-no)) + (incf bar-no) + (setf position 0)) (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)) + (gsharp::add-element (make-gsharp-key-signature (car key-signatures) layer) + bar + position) + (incf position) (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. - (when (and (not (member (caar ons) + (when (and (cdr ons) + (not (member (caar ons) (car beat-starts))) (find-if #'(lambda (x) (> x (caar ons))) - (car beat-starts)) + (car beat-starts)) (< (find-if #'(lambda (x) (> x (caar ons))) - (car beat-starts)) + (car beat-starts)) (car (second ons)))) (setf (cdr ons) (cons (cons (find-if #'(lambda (x) (> x (caar ons))) @@ -212,13 +258,13 @@ ;; why I've taken as much of the metrical logic out and put it ;; above if there are other straightforward rules, they should, ;; I think go there. - (if (cdr (car ons)) + (if (cdar ons) (setf clusters (make-gsharp-clusters-with-duration (- (car (second ons)) - (car (car ons))))) + (car (car ons))))) (setf clusters (make-gsharp-rests-with-duration (- (car (second ons)) - (car (car ons))) - layer))) - (let ((now (caar ons)) (first-p t)) + (car (car ons))) + layer))) + (let ((now (caar ons)) (first-p t) (pitches)) (do ((clusters clusters (cdr clusters))) ((null clusters)) (when (member now (car beat-starts)) @@ -228,33 +274,58 @@ ;; not necessary or b) should be within the duration logic ;; above. Would be good not to rely on it (which is not to ;; say that it isn't reliable) - (gsharp-mxml::add-element-at-duration (car clusters) - bar - (/ (- now (car bar-starts)) - 4)) - (dolist (note (cdr (car ons))) + (gsharp::add-element (car clusters) bar position) + ;; FIXME: Deleting notes that fall on the same note + ;; name. Stupid thing to do. + (setf pitches (remove-duplicates (mapcar #'(lambda (x) + (cons x (pitch-for-gsharp x composition))) + (cdar ons)) + :key #'second :test #'=)) + (dolist (pitch pitches) (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))))))) + (gsharp::add-note (car clusters) + (make-instance 'gsharp::note + :pitch (second pitch) + :accidentals (third pitch) + :staff (car (gsharp::staves layer)) + :tie-right (if (or (cdr clusters) + (member (car pitch) (second ons))) + t + nil) + :tie-left (if first-p + (member (car pitch) (first old-ons)) + t))))) (incf now (* (gsharp::duration (car clusters)) 4)) - (setf first-p nil))) + (setf first-p nil) + (incf position))) (when (and (cdr bar-starts) (= (car (second ons)) (second bar-starts))) (setf bar-starts (cdr bar-starts) beat-starts (cdr beat-starts)))))) +(defun check-beams (bar) + (do* ((clusters (gsharp::elements bar) (cdr clusters)) + (left) (mid) (right)) + ((null (cddr clusters))) + (setf left (first clusters) + mid (second clusters) + right (third clusters)) + (unless (or (typep mid 'gsharp::rest) + (= (max (gsharp::rbeams mid) + (gsharp::lbeams mid)) + 0)) + (cond + ((or (typep left 'gsharp::rest) + (= (gsharp::rbeams left) 0)) + (setf (gsharp::lbeams mid) 0)) + ((or (typep right 'gsharp::rest) + (= (gsharp::lbeams right) 0)) + (setf (gsharp::rbeams mid) 0)) + ((< (gsharp::rbeams left) + (gsharp::lbeams right)) + (setf (gsharp::lbeams mid) (gsharp::rbeams left))) + (t (setf (gsharp::rbeams mid) (gsharp::lbeams right))))))) (defgeneric make-gsharp-key-signature (key-signature layer)) (defmethod make-gsharp-key-signature ((key-signature standard-key-signature) layer) @@ -309,7 +380,7 @@ ;; 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)) + (event-pos (position event (ocp-list composition) :key #'car)) (note-sequence (get-spelling-list composition)) (spelling (elt note-sequence event-pos)) (note-name (cdr (assoc (aref (second spelling) 0) @@ -449,6 +520,7 @@ to which event belongs")) (defun name-from-layer (event) + ;; Uses gsharp layer names. Numbers layers in cases of duplication (let* ((layers (gsharp::layers (car (gsharp::segments @@ -489,6 +561,10 @@ collect i))) (defun beat-starts (time-signature-list &key (crotchet 1)) + ;; provides a list of bars, each a list of beats. If no timesig, + ;; guesses at 4/4 + ;; FIXME: This is stupid and should disappear if and when proper + ;; beat methods are implemented. (if time-signature-list (loop for time-signature in time-signature-list nconc (loop for i from (timepoint (onset time-signature)) @@ -575,29 +651,66 @@ (t (push (car data) copied-data)))))) +(defun check-ons (ons bar-starts) + "looks for small rests such as might be created by midi performance +of tenuto lines" + (let ((time (caar ons)) (notes (cdar ons))(new-ons)) + (do ((ons (cdr ons) (cdr ons))) + ((null ons) (reverse new-ons)) + (if (or (member (caar ons) bar-starts) + (> (- (caar ons) time) + *rounding-factor*)) + (setf new-ons (cons (cons time notes) new-ons) + time (caar ons) + notes (cdar ons)) + (dolist (note (cdar ons)) + (unless (member note notes) + (push note notes))))))) + +#+nil + (defun check-ons (ons) "looks for small rests such as might be created by midi performance of tenuto lines" - (let ((new-ons)) + (let ((new-ons) (skip)) (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)))) - (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)))))) + (if skip (reverse new-ons) (reverse (cons (car ons) new-ons)))) + (cond + (skip (setf skip nil)) + ((not (and (<= (- (car (second ons)) + (car (first ons))) + *rounding-factor*) + (null (cdr (first ons))))) + (if (<= (- (car (second ons)) + (car (first ons))) + *rounding-factor*) + (progn + (push (cons (caar ons) (remove-duplicates (nconc (cdr (first ons)) + (cdr (second ons))))) + new-ons) + (setf skip t)) + (push (car ons) new-ons))))))) + (defun guess-rounding-factor (events) "Assuming that only durations need quantising, look at the lcd for onsets" + (let ((rounding-factor (/ 1 *rounding-factor*))) + (when events + (let ((crotchet (duration (crotchet (car events))))) + (do ((events events (cdr events))) + ((null (cdr events))) + (do ((divisor rounding-factor (* 2 divisor))) + ((<= (rem (* (timepoint (car events)) crotchet) + divisor) + (/ divisor 3)) + (setf rounding-factor divisor)))))) + (/ 1 rounding-factor))) + +(defun guess-rounding-factor-smart (events) + "Assuming that only durations need quantising, look at the lcd for +onsets" (let ((times (map 'list #'(lambda (x) (denominator (* (timepoint x) (duration (crotchet x)))))