Mercurial > hg > amuse
changeset 185:1d3cdca12aeb
Fixes for gsharp output to allow n:n staves:layers
darcs-hash:20080722112038-40ec0-da363f762724456890dd90c1ec362e184a726320.gz
author | d.lewis <d.lewis@gold.ac.uk> |
---|---|
date | Tue, 22 Jul 2008 12:20:38 +0100 |
parents | 94803c723ccd |
children | 03be243f9003 |
files | amuse-ecolm.asd amuse-tabcode.asd implementations/tabcode/amuse-ecolm.asd implementations/tabcode/amuse-tabcode.asd implementations/tabcode/methods.lisp tools/gsharp-output.lisp |
diffstat | 6 files changed, 221 insertions(+), 164 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/amuse-ecolm.asd Tue Jul 22 12:20:38 2008 +0100 @@ -0,0 +1,9 @@ +(asdf:defsystem amuse-ecolm + :depends-on (amuse-tabcode clsql) + :components + ((:module implementations + :components + ((:module tabcode + :components + ((:file "package-ecolm") + (:file "ecolm" :depends-on ("package-ecolm")))))))) \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/amuse-tabcode.asd Tue Jul 22 12:20:38 2008 +0100 @@ -0,0 +1,12 @@ +(asdf:defsystem amuse-tabcode + :name "amuse-tabcode" + :depends-on (amuse tabcode amuse-harmony) + :components + ((:module implementations + :components + ((:module tabcode + :components + ((:file "package") + (:file "classes" :depends-on ("package")) + (:file "methods" :depends-on ("package" "classes")) + (:file "tabcode-import" :depends-on ("package" "classes")))))))) \ No newline at end of file
--- a/implementations/tabcode/amuse-ecolm.asd Mon Jul 21 14:08:17 2008 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,5 +0,0 @@ -(asdf:defsystem amuse-ecolm - :depends-on (amuse-tabcode clsql) - :components - ((:file "package-ecolm") - (:file "ecolm" :depends-on ("package-ecolm")))) \ No newline at end of file
--- a/implementations/tabcode/amuse-tabcode.asd Mon Jul 21 14:08:17 2008 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,7 +0,0 @@ -(asdf:defsystem amuse-tabcode - :depends-on (amuse tabcode amuse-harmony) - :components - ((:file "package") - (:file "classes" :depends-on ("package")) - (:file "methods" :depends-on ("package" "classes")) - (:file "tabcode-import" :depends-on ("package" "classes")))) \ No newline at end of file
--- a/implementations/tabcode/methods.lisp Mon Jul 21 14:08:17 2008 +0100 +++ b/implementations/tabcode/methods.lisp Tue Jul 22 12:20:38 2008 +0100 @@ -108,4 +108,9 @@ (do () ((time> (cut-off beat-period) current-moment) beat-period) (setf (timepoint beat-period) - (timepoint (cut-off beat-period)))))) \ No newline at end of file + (timepoint (cut-off beat-period)))))) + +(defmethod amuse-tools::gsharp-staff-string ((event tabcode-pitched-event)) + (if (< (midi-pitch-number event) 60) + "bass" + "treble")) \ No newline at end of file
--- a/tools/gsharp-output.lisp Mon Jul 21 14:08:17 2008 +0100 +++ b/tools/gsharp-output.lisp Tue Jul 22 12:20:38 2008 +0100 @@ -40,10 +40,11 @@ ;; * Clef is guessed at - we need amuse:get-applicable-clef (and ;; amuse:clef) ;; -;; * Anacruses are ignored unless they have their own time-signatures! +;; [* Anacruses are ignored unless they have their own time-signatures! +;; - not strictly true now. This depends on the implementation of current bar] ;; ;; The stages used are: -;; 1 - find layers (1:1 with staves at the moment) +;; 1 - find layers and stave (was 1:1 with staves. Not anymore) ;; 2 - for each layer, get events and their timings (includes ;; rounding/quantization) ;; 3 - Find all ties needed for graphical/courtesy reasons (includes @@ -76,136 +77,167 @@ (clim:adopt-frame (clim:find-frame-manager :server-path '(:null)) frame) (clim:execute-frame-command frame '(gsharp::com-new-buffer)) ;; Now generate the buffer - (fill-gsharp-buffer-with-constituent (car (esa:buffers frame)) composition) + (make-objects-for-gsharp-buffer composition (car (esa:buffers frame))) + ;; (fill-gsharp-buffer-with-constituent (car (esa:buffers frame)) composition) ;; Refresh and process (setf (gsharp::modified-p (car (esa:buffers frame))) t) (gsharp::recompute-measures (car (esa:buffers frame))) + (setf *foo* (car (esa:buffers frame))) ;; Print - (setf *foo* (car (esa:buffers frame))) (clim:execute-frame-command - frame `(gsharp::com-print-buffer-to-file ,pathname)))) + frame `(gsharp::com-print-buffer-to-file ,pathname)) + (car (esa:buffers frame)))) ;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; Big `walking through data structures' type functions -(defgeneric fill-gsharp-buffer-with-constituent (buffer constituent) - (:documentation "Takes an empty gsharp buffer and a constituent and - fills the buffer based on the contents of constituent. Buffer is - returned, but this is not necessary, since it is modified in - place.")) -(defmethod fill-gsharp-buffer-with-constituent (buffer (composition composition)) - ;; FIXME: Throughout this, I assume that - ;; get-applicable-time-signatures isn't '() - (let ((time-signatures (get-applicable-time-signatures composition composition)) - (key-signatures (handler-bind ((insufficient-information - #'(lambda (c) - (declare (ignore c)) - (invoke-restart 'guess)))) - (get-applicable-key-signatures composition composition))) - (layers)) - (multiple-value-bind (layer-events layer-scores) - ;; Get hash-tables of events by layer and counts of events - ;; below middle C for guessing clef. - (identify-gsharp-layers composition) - ;; For each layer make clef, and one staff per layer - ;; FIXME: this is cheating - (maphash #'(lambda (name events) - (let* ((clef (if (> (gethash name layer-scores) - (/ (length events) 2)) - (gsharp::make-clef :bass) - (gsharp::make-clef :treble))) - (staff (gsharp::make-fiveline-staff :name name - :clef clef))) - ;; Make the layers - (push (gsharp::make-layer (list staff) - :body (gsharp::make-slice :bars nil) - :name name) - layers) - ;; Add the notes and bars - (add-bars-and-events-to-layer (car layers) (reverse events) - time-signatures composition - :key-signatures key-signatures))) - layer-events) - ;; Attach layers to a segment and place segment into buffer - (let* ((segment (make-instance 'gsharp::segment :layers layers :buffer buffer))) - (setf (gsharp::segments buffer) (list segment) - (gsharp::staves buffer) (mapcar #'(lambda (x) (car (gsharp::staves x))) - layers)) - buffer)))) +(defun make-objects-for-gsharp-buffer (composition buffer) + "This replacement for fill-gsharp-buffer-with-constituent generates + staves and layers itself and attaches them to a segment in the + supplied buffer. Clefs are guessed rather than being read. Each + event is asked for its staff and layer using gsharp-staff-string + gsharp-layer-string, and from this, the connections are + made. gsharp-staff-string defaults to calling gsharp-layer-string, + so in most cases there will be a single staff for each layer. Events + are added, and then staves are sorted using staff<. A proper layout + object would be another way of doing this." + (let ((layer-names (make-hash-table :test #'equal)) + (layer-events (make-hash-table)) + (layer-staves (make-hash-table)) + (staff-names (make-hash-table :test #'equal)) + (staff-name)(staff)(layer-name)(layer) + (segment)) + (sequence::dosequence (event composition) + ;; can't do percussion parts yet: + (when (pitchedp event) + (setf layer-name (gsharp-layer-string event) + layer (gethash layer-name layer-names) + staff-name (gsharp-staff-string event) + staff (gethash staff-name staff-names)) + (if staff + ;; this looks a little cryptic, but we're keeping note of + ;; whether the mean pitch is above C for clef guessing. + (setf (third staff) (+ (third staff) 1) + (second staff) (+ (second staff) (min (floor (midi-pitch-number event) 60) 1))) + (setf staff (list (gsharp::make-fiveline-staff :name staff-name) + (min (floor (midi-pitch-number event) 60) + 1) + 1) + (gsharp::buffer (car staff)) buffer + (gethash staff-name staff-names) staff)) + (if layer + (progn + (unless (find (first staff) (gethash layer layer-staves)) + (push (first staff) (gethash layer layer-staves))) + (push event (gethash layer layer-events))) + (if segment + (setf layer (gsharp::make-layer (list (first staff)) + :body (gsharp::make-slice :bars nil) + :name layer-name + :segment segment) + (gsharp::layers segment) (cons layer (gsharp::layers segment)) + (gethash layer-name layer-names) layer + (gethash layer layer-staves) (list staff) + (gethash layer layer-events) (list event)) + (setf layer (gsharp::make-layer (list (first staff)) + :body (gsharp::make-slice :bars nil) + :name layer-name) + segment (make-instance 'gsharp::segment + :buffer buffer + :layers (list layer)) + (gsharp-buffer:segment layer) segment + (gethash layer-name layer-names) layer + (gethash layer layer-staves) (list (first staff)) + (gethash layer layer-events) (list event)))))) + (maphash #'(lambda (key val) + (declare (ignore key)) + (unless (>= (second val) (/ (third val) 2)) + (setf *foo* (first val)) + (setf (gsharp::clef (first val)) (gsharp::make-clef :bass)))) + staff-names) + (maphash #'(lambda (key events) + (add-music-to-layer key + (reverse events) + (gethash key layer-staves) + composition + (handler-bind ((insufficient-information + #'(lambda (c) + (declare (ignore c)) + (invoke-restart 'guess)))) + (get-applicable-key-signatures composition composition)))) + layer-events) + (setf (gsharp::segments buffer) (list segment) + (gsharp::staves buffer) nil) + (let ((staves)) + (maphash #'(lambda (key val) + (declare (ignore key)) + (push (car val) staves)) + staff-names) + (setf staves (sort staves #'stave<)) + (setf (gsharp::staves buffer) staves) + buffer))) +(defgeneric stave< (staff1 staff2) + (:method (s1 s2) + (let* ((clefs '(:treble :bass)) + (c1 (gsharp::clef s1)) + (c2 (gsharp::clef s2)) + (pos1 (position (gsharp::name c1) clefs)) + (pos2 (position (gsharp::name c2) clefs))) + (or (< pos1 pos2) + (and (= pos1 pos2) + (< (gsharp::lineno c1) + (gsharp::lineno c2))) + (and (= pos1 pos2) + (= (gsharp::lineno c1) + (gsharp::lineno c2)) + (string< (gsharp::name s1) + (gsharp::name s2))))))) -(defgeneric identify-gsharp-layers (constituent) - (:documentation "Takes a composition, returns two hash tables, one - of events grouped by layer name and the other of counts of notes - below middle C by layer (for choosing clef). N.B. On two counts this - is a dodgy way of tying clef, staff and layer. Separate later.")) - -(defmethod identify-gsharp-layers ((composition composition)) - (let ((layer-events (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 layer-name layer-events) - (push event (gethash layer-name layer-events)) - (when (< (midi-pitch-number event) 60) - (incf (gethash layer-name layer-scores)))) - (t (setf (gethash layer-name layer-events) - (list event) - (gethash layer-name layer-scores) - (if (< (midi-pitch-number event) 60) - 1 0)))))))) - -(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." - (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 (current-bar (make-standard-moment 0) composition) - (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 - (handler-bind - ((insufficient-information - #'(lambda (c) - (declare (ignore c)) - (invoke-restart 'use-whole-bar)))) - (let ((starts) (current)) - (do* ((bars bar-starts) - (beat-period (current-beat (make-standard-moment 0) composition) - (current-beat (cut-off beat-period) composition)) - (beat-time (timepoint beat-period) (timepoint beat-period))) - ((time>= (cut-off beat-period) (cut-off composition)) - (progn - (when (and (cdr bars) - (>= beat-time (second bars))) - (push (reverse current) starts) - (setf current nil - bars (cdr bars))) - (push beat-time current) - (reverse (cons (reverse current) 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))) +(defun bar-starts-2 (composition) + (let ((starts)) + (do ((bar-period (current-bar (make-standard-moment 0) composition) + (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)))) +(defun beat-starts-2 (bar-starts composition) + ;; FIXME: improve this + (when (get-applicable-time-signatures composition composition) + (handler-bind + ((insufficient-information + #'(lambda (c) + (declare (ignore c)) + (invoke-restart 'use-whole-bar)))) + (let ((starts) (current)) + (do* ((bars bar-starts) + (beat-period (current-beat (make-standard-moment 0) composition) + (current-beat (cut-off beat-period) composition)) + (beat-time (timepoint beat-period) (timepoint beat-period))) + ((time>= (cut-off beat-period) (cut-off composition)) + (progn + (when (and (cdr bars) + (>= beat-time (second bars))) + (push (reverse current) starts) + (setf current nil + bars (cdr bars))) + (push beat-time current) + (reverse (cons (reverse current) starts)))) + (when (and (cdr bars) + (>= beat-time (second bars))) + (push (reverse current) starts) + (setf current nil + bars (cdr bars))) + (push beat-time current)))))) +(defun add-music-to-layer (layer events staves composition key-sigs) + "Creating all the musical objects for the gsharp staves in the +provided layer" + (let* ((bar-moments (bar-starts-2 composition)) + (beat-moments (or (beat-starts-2 bar-moments composition) + bar-moments)) + (body (gsharp::body layer)) + (bar-no 0) + (ons) (position) (clusters) (bar)) ;; this is a cheat to guess timing rounding (quantisation) based ;; on onset times - only affects midi-like data where onsets are ;; already rounded, but durations are not (as in TC's fantasia @@ -220,50 +252,43 @@ ;; most music (Mensurstrich aside), so insert bar starts if not ;; present. Note that, since the events themselves are recorded in ;; the list, the existence of ties shuold be detected. - (when bar-starts - (setf ons (add-bar-starts-if-not-present bar-starts ons))) + (when bar-moments + (setf ons (add-bar-starts-if-not-present bar-moments ons))) ;; Finally, one problem here is that, in midi, there is often a ;; gap or overlap between consecutive notes or chords. Since ;; 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 bar-starts)) + (setf ons (check-ons ons bar-moments)) ;; 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))) + (when key-sigs + (dolist (staff staves) + (setf (gsharp::keysig staff) + (make-gsharp-key-signature (car key-sigs) staff)))) (do ((old-ons nil ons) (ons ons (cdr ons))) ((null (cdr ons))) - (when (member (caar ons) bar-starts) + (when (member (caar ons) bar-moments) ;; 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) (setf position 0)) -#+nil (when (and key-signatures - (<= (timepoint (car key-signatures)) - (caar ons))) - (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 (cdr ons) (not (member (caar ons) - (car beat-starts))) + (car beat-moments))) (find-if #'(lambda (x) (> x (caar ons))) - (car beat-starts)) + (car beat-moments)) (< (find-if #'(lambda (x) (> x (caar ons))) - (car beat-starts)) + (car beat-moments)) (car (second ons)))) (setf (cdr ons) (cons (cons (find-if #'(lambda (x) (> x (caar ons))) - (car beat-starts)) + (car beat-moments)) (cdar ons)) (cdr ons)))) ;; Making clusters just from duration removes the ability to @@ -282,7 +307,7 @@ (let ((now (caar ons)) (first-p t) (pitches)) (do ((clusters clusters (cdr clusters))) ((null clusters)) - (when (member now (car beat-starts)) + (when (member now (car beat-moments)) (setf (gsharp::lbeams (car clusters)) 0)) ;; This function adds cluster at a specific point in the ;; bar. It does a lot of other things that are probably a) @@ -302,7 +327,7 @@ (make-instance 'gsharp::note :pitch (second pitch) :accidentals (third pitch) - :staff (car (gsharp::staves layer)) + :staff (staff-for-note (car pitch) staves) :tie-right (if (or (cdr clusters) (member (car pitch) (second ons))) t @@ -313,11 +338,15 @@ (incf now (* (gsharp::duration (car clusters)) 4)) (setf first-p nil) (incf position))) - (when (and (cdr bar-starts) + (when (and (cdr bar-moments) (= (car (second ons)) - (second bar-starts))) - (setf bar-starts (cdr bar-starts) - beat-starts (cdr beat-starts)))))) + (second bar-moments))) + (setf bar-moments (cdr bar-moments) + beat-moments (cdr beat-moments)))))) + +(defun staff-for-note (event staves) + (find-if #'(lambda (x) (string= (gsharp::name x) (gsharp-staff-string event))) + staves)) (defun check-beams (bar) (do* ((clusters (gsharp::elements bar) (cdr clusters)) @@ -342,8 +371,8 @@ (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) +(defgeneric make-gsharp-key-signature (key-signature object)) +(defmethod make-gsharp-key-signature ((key-signature standard-key-signature) (layer gsharp-buffer::layer)) (let ((alterations (make-array 7 :initial-element :natural)) (order-of-sharps #(3 0 4 1 5 2 6)) (order-of-flats #(6 2 5 1 4 0 3))) @@ -354,6 +383,18 @@ (setf (elt alterations (elt order-of-sharps index)) :sharp))) (gsharp-buffer::make-key-signature (car (gsharp::staves layer)) :alterations alterations))) +(defmethod make-gsharp-key-signature ((key-signature standard-key-signature) + (staff gsharp::staff)) + (let ((alterations (make-array 7 :initial-element :natural)) + (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 staff + :alterations alterations))) ;;;;;;;;;;;;;;;;;;;;;;; ;; @@ -436,9 +477,6 @@ (- (slot-value e 'amuse::number) 21)))) into result finally (return (sort result #'sorter :key #'car))))) - - - ;; Time (defun make-gsharp-clusters-with-duration (duration) @@ -534,6 +572,11 @@ (:documentation "Return a string that uniquely identifies the layer to which event belongs")) +(defgeneric gsharp-staff-string (event) + (:method (e) (gsharp-layer-string e)) + (:documentation "Return a string that uniquely identifies the staff + to which event belongs")) + (defun name-from-layer (event) ;; Uses gsharp layer names. Numbers layers in cases of duplication (let* ((layers (gsharp::layers