view implementations/gsharp/gsharp-import.lisp @ 226:64b795c2ff18

Fix bug in move-to-first-bar. Ignore-this: 52a48e8771d159294e9ad51cbe04034d darcs-hash:20090905200027-16a00-539b473b27ebd6b75282dac335cc8617403ed3ad.gz committer: Jamie Forth <j.forth@gold.ac.uk>
author j.forth <j.forth@gold.ac.uk>
date Thu, 24 Feb 2011 11:23:18 +0000
parents 22ac5ec1733c
children bc893627f92d
line wrap: on
line source
(in-package "AMUSE-GSHARP")

(defun make-gsharp-composition (events buffer timepoint interval &key tempi key-signatures time-signatures)
  (let ((comp (make-instance 'gsharp-composition :buffer buffer :time timepoint :interval interval
                             :tempi tempi :key-signatures key-signatures :time-signatures time-signatures)))
    (sequence:adjust-sequence comp (length events) :initial-contents events)))

(defun last-bar-p (bar)
  ;; I know most of this file is cut-and-pasted, but this is a
  ;; particularly horrible example.
  (eq bar (car (last (gsharp-buffer:bars (gsharp-buffer:slice bar))))))

(defun find-next-note (note)
  (when (gsharp-buffer:tie-right note)
    (let ((bar (gsharp-buffer:bar (gsharp-buffer:cluster note)))
	  (cluster (gsharp-buffer:cluster note))
	  (next-element nil))
      (loop for (x y) on (gsharp-buffer:elements bar)
	    until (null y)
	    when (eq x cluster) do (setq next-element y))
      (cond
	(next-element)
	((last-bar-p bar) (return-from find-next-note nil))
	(t (let ((next-bar (gsharp-buffer:barno (gsharp-buffer:slice bar)
						(1+ (gsharp-numbering::number bar)))))
	     (if (gsharp-buffer:elements next-bar)
		 (setq next-element (car (gsharp-buffer:elements next-bar)))
		 (return-from find-next-note nil)))))
      ;; now NEXT-ELEMENT is the next element!
      (when (typep next-element 'gsharp-buffer:cluster)
	(loop for n in (gsharp-buffer:notes next-element)
	      if (and (gsharp-buffer:tie-left n)
		      (= (gsharp-buffer:pitch n) (gsharp-buffer:pitch note))
		      (eq (gsharp-buffer:staff n) (gsharp-buffer:staff note))
		      (eq (gsharp-buffer:accidentals n) (gsharp-buffer:accidentals note)))
	      return n)))))
		      
(defun compute-duration (note)
  (loop for n = note then (find-next-note n)
	while n
	sum (gsharp-buffer:duration (gsharp-buffer:cluster n))))

(defun events-from-element (element index time)
  (typecase element
    (gsharp-buffer:cluster
     (mapcar (lambda (note)
               (make-instance 'gsharp-pitched-event
                              :note note
                              :slice-index index
                              :number (gsharp-play::midi-pitch note)
                              :time time
                              :interval (* 4 (compute-duration note))))
             (remove-if #'gsharp-buffer:tie-left (gsharp-buffer:notes element))))
    (gsharp-buffer:key-signature
     (list (make-gsharp-key-signature-period element time nil)))
    (gsharp-buffer::time-signature
     (list (make-gsharp-time-signature-period element time nil)))))

(defun events-from-bar (bar index time)
  (mapcan (lambda (element)
	    (prog1 (events-from-element element index time)
	      (incf time (* 4 (gsharp-buffer:duration element)))))
	  (gsharp-buffer:elements bar)))

(defun events-from-slice (slice index durations)
  (let ((time 0))
    (mapcan (lambda (bar duration)
	      (prog1 (events-from-bar bar index time)
		(incf time (* 4 duration))))
	    (gsharp-buffer:bars slice) durations)))

(defun segment-composition (segment)
  (let* ((slices (mapcar #'gsharp-buffer:body (gsharp-buffer::layers segment)))
         (durations (gsharp-play::measure-durations slices))
         (gsharp-play::*tuning* (gsharp-buffer:tuning segment))
         (key-signatures (get-initial-keysigs segment)) 
         (time-signatures)
         (events (loop for slice in slices
                    for i from 0
                    for events = (events-from-slice slice i durations)
                    then (merge 'list events (events-from-slice slice i durations) 'time<)
                    finally (return events)))
         (duration (* 4 (reduce #'+ durations))))
    (multiple-value-setq (events key-signatures time-signatures)
      (filter-event-list-for-signatures events key-signatures duration))

    ;; FIXME: TEMPI here will break as soon as gsharp is made to have
    ;; a sane divisions value in play.lisp instead of 25
    (make-gsharp-composition events (gsharp::buffer segment) 0 duration
                             :tempi (list (make-standard-tempo-period (* 128 (/ (* 4 25) (gsharp-buffer:tempo segment))) 
                                                                      0 duration))
                             :key-signatures key-signatures
                             :time-signatures time-signatures)))

(defun filter-event-list-for-signatures (events key-signatures duration)
  "key-signatures here are initial `staff-level' signatures (what
  MusicXML calls attributes). MusicXML also has time sigs in the
  attributes, but GSharp converts them to normal elements."
  (let ((filtered-events) (time-signatures)
        (staves-data (mapcar #'(lambda (k)
                                 (list (gsharp::staff (gsh-source k)) k nil)) 
                             key-signatures)))
    (dolist (event events)
      (typecase event
        (gsharp-pitched-event (push event filtered-events))
        (gsharp-key-signature-period 
         (if (assoc (gsharp::staff (gsh-source event)) staves-data)
             (let ((data (assoc (gsharp::staff (gsh-source event)) staves-data)))
               (if (second data)
                   (setf (duration (second data)) (- (timepoint event) (timepoint (second data)))
                         (second data) event)
                   (setf (second data) event)))
             (acons (gsharp::staff (gsh-source event)) (list event nil) staves-data))
         (push event key-signatures))
        (gsharp-time-signature-period 
         (if (assoc (gsharp::staff (gsh-source event)) staves-data)
             (let ((data (assoc (gsharp::staff (gsh-source event)) staves-data)))
               (if (third data)
                   (setf (duration (third data)) (- (timepoint event) (timepoint (third data)))
                         (third data) event)
                   (setf (third data) event)))
             (acons (gsharp::staff (gsh-source event)) (list nil event) staves-data))
         (push event time-signatures))))
    (loop for item in staves-data
       when (second item)
       do (setf (duration (second item)) duration)
       when (third item)
       do (setf (duration (third item)) duration))
    (values (reverse filtered-events) (reverse key-signatures) (reverse time-signatures))))

(defun get-initial-keysigs (segment)
  (let ((staves (remove-duplicates
                 (loop for layer in (gsharp::layers segment)
                    nconc (gsharp::staves layer)))))
    (loop for staff in staves
       collect (make-gsharp-key-signature-period (gsharp::keysig staff) 0 nil))))

#|

(in-package :clim-user)

(define-command (com-amuse-play :name t :command-table gsharp::global-gsharp-table)
    ()
  (let* ((segment (gsharp-cursor::segment (gsharp::current-cursor)))
	 (composition (amuse-gsharp::segment-composition segment)))
    (amuse-utils::play composition)))
    
(define-command (com-infer-key :name t :command-table gsharp::global-gsharp-table)
    ()
  (let* ((segment (gsharp-cursor::segment (gsharp::current-cursor)))
	 (composition (amuse-gsharp::segment-composition segment))
    	 (result (amuse-harmony:krumhansl-key-finder composition composition))
	 (name (aref #("C" "C#" "D" "Eb" "E" "F" "F#" "G" "Ab" "A" "Bb" "B") (car result)))
	 (string (format nil "Key: ~A ~(~A~)" name (cadr result))))
    (esa:display-message string)))
    

(in-package :gsharp-measure)

(define-stealth-mixin snote () note
  ((misspeltp :initform nil :accessor misspeltp)))

(in-package :gsharp-drawing)

;;; patch DRAW-NOTE
(defun draw-note (pane note notehead nb-dots x pos dot-xoffset dot-pos)
  (score-pane:with-vertical-score-position (pane (staff-yoffset (staff note)))
    (cond
      ((gsharp-measure::misspeltp note)
       (with-output-as-presentation (pane note 'note)
         (score-pane:draw-notehead pane notehead x pos))
       (let ((ypos (score-pane:staff-step (- pos))))
         (draw-line* pane (- x 5) (+ ypos 3) (+ x 5) (+ ypos 3)
                     :line-thickness 3 :ink (make-rgb-color 0.8 0.1 0.1))))
      (t (score-pane:draw-notehead pane notehead x pos)))
    (when (final-accidental note)
      (score-pane:draw-accidental pane (final-accidental note) (final-absolute-accidental-xoffset note) pos))
    (draw-dots pane nb-dots x dot-xoffset dot-pos)))

(in-package :clim-user)

(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)))))

(let ((accidental-conversions
       '(("ss" . :double-sharp) ("s" . :sharp) ("n" . :natural)
         ("f" . :flat) ("ff" . :double-flat))))
  (defun annotate-misspellings (composition)
    (do* ((ocp-list (ocp-list composition))
          (note-sequence (ps13:ps13-new-imp (map 'list #'cdr ocp-list) 10 42 nil nil nil))
          (list ocp-list (cdr list))
          (spellings note-sequence (cdr spellings)))
         ((null list))
      (let* ((event (caar list))
              (note (amuse-gsharp:note event))
             (accidental (elt (second (car spellings)) 1))
             (converted (cdr (assoc accidental accidental-conversions :test #'string=))))
        (unless (eq (gsharp-buffer:accidentals note) converted)
          ;; this pun is not to be preserved once we get into real
          ;; code.
          (setf (gsharp-measure::misspeltp note) (second (car spellings))))))))

(define-command (com-spellcheck :name t :command-table gsharp::global-gsharp-table)
    ()
  (let* ((segment (gsharp-cursor::segment (gsharp::current-cursor)))
	 (composition (amuse-gsharp::segment-composition segment)))
    (annotate-misspellings composition)
    #+nil
    (esa:display-message "~A" (first (ocp-list composition)))))

(let ((accidental-conversions
       '(("ss" . :double-sharp) ("s" . :sharp) ("n" . :natural)
         ("f" . :flat) ("ff" . :double-flat))))
  (define-command (com-correct-misspelt-note :name t :command-table gsharp::global-gsharp-table)
      ((note 'gsharp::note))
    (let ((spelling (gsharp-measure::misspeltp note))
          (element (gsharp-buffer::cluster note))
          (staff (gsharp-buffer::staff note))
          (head (gsharp-buffer::head note))
          (dots (gsharp-buffer::dots note)))
      (let ((pitch (+ (mod (- (digit-char-p (char (elt spelling 0) 0) 17) 12) 7)
                      (* (parse-integer (elt spelling 2)) 7)))
            (accidentals (cdr (assoc (elt spelling 1) accidental-conversions :test #'string=))))
        (let ((new-note (gsharp-buffer::make-note pitch staff 
                                                  :head head
                                                  :dots dots
                                                  :accidentals accidentals)))
          (gsharp-buffer::remove-note note)
          (gsharp-buffer::add-note element new-note))))))

(define-presentation-to-command-translator correct-misspelt-note-translator
    (gsharp::note com-correct-misspelt-note gsharp::global-gsharp-table
          :gesture :select
          :tester ((object) (gsharp-measure::misspeltp object)))
  (object)
  (list object))

|#