csr21@59
|
1 (in-package "AMUSE-GSHARP")
|
csr21@59
|
2
|
csr21@59
|
3 (defun last-bar-p (bar)
|
csr21@59
|
4 ;; I know most of this file is cut-and-pasted, but this is a
|
csr21@59
|
5 ;; particularly horrible example.
|
csr21@59
|
6 (eq bar (car (last (gsharp-buffer:bars (gsharp-buffer:slice bar))))))
|
csr21@59
|
7
|
csr21@59
|
8 (defun find-next-note (note)
|
csr21@59
|
9 (when (gsharp-buffer:tie-right note)
|
csr21@59
|
10 (let ((bar (gsharp-buffer:bar (gsharp-buffer:cluster note)))
|
csr21@59
|
11 (cluster (gsharp-buffer:cluster note))
|
csr21@59
|
12 (next-element nil))
|
csr21@59
|
13 (loop for (x y) on (gsharp-buffer:elements bar)
|
csr21@59
|
14 until (null y)
|
csr21@59
|
15 when (eq x cluster) do (setq next-element y))
|
csr21@59
|
16 (cond
|
csr21@59
|
17 (next-element)
|
csr21@59
|
18 ((last-bar-p bar) (return-from find-next-note nil))
|
csr21@59
|
19 (t (let ((next-bar (gsharp-buffer:barno (gsharp-buffer:slice bar)
|
csr21@59
|
20 (1+ (gsharp-numbering::number bar)))))
|
csr21@59
|
21 (if (gsharp-buffer:elements next-bar)
|
csr21@59
|
22 (setq next-element (car (gsharp-buffer:elements next-bar)))
|
csr21@59
|
23 (return-from find-next-note nil)))))
|
csr21@59
|
24 ;; now NEXT-ELEMENT is the next element!
|
csr21@59
|
25 (when (typep next-element 'gsharp-buffer:cluster)
|
csr21@59
|
26 (loop for n in (gsharp-buffer:notes next-element)
|
csr21@59
|
27 if (and (gsharp-buffer:tie-left n)
|
csr21@59
|
28 (= (gsharp-buffer:pitch n) (gsharp-buffer:pitch note))
|
csr21@59
|
29 (eq (gsharp-buffer:staff n) (gsharp-buffer:staff note))
|
csr21@59
|
30 (eq (gsharp-buffer:accidentals n) (gsharp-buffer:accidentals note)))
|
csr21@59
|
31 return n)))))
|
csr21@59
|
32
|
csr21@59
|
33 (defun compute-duration (note)
|
csr21@59
|
34 (loop for n = note then (find-next-note n)
|
csr21@59
|
35 while n
|
csr21@59
|
36 sum (gsharp-buffer:duration (gsharp-buffer:cluster n))))
|
csr21@59
|
37
|
c@61
|
38 (defun events-from-element (element index time)
|
csr21@59
|
39 (when (typep element 'gsharp-buffer:cluster)
|
csr21@59
|
40 (mapcar (lambda (note)
|
csr21@59
|
41 (make-instance 'gsharp-pitched-event
|
csr21@59
|
42 :note note
|
d@166
|
43 :slice-index index
|
csr21@59
|
44 :number (gsharp-play::midi-pitch note)
|
csr21@59
|
45 :time time
|
csr21@59
|
46 :interval (* 4 (compute-duration note))))
|
csr21@59
|
47 (remove-if #'gsharp-buffer:tie-left (gsharp-buffer:notes element)))))
|
csr21@59
|
48
|
c@61
|
49 (defun events-from-bar (bar index time)
|
csr21@59
|
50 (mapcan (lambda (element)
|
c@61
|
51 (prog1 (events-from-element element index time)
|
csr21@59
|
52 (incf time (* 4 (gsharp-buffer:duration element)))))
|
csr21@59
|
53 (gsharp-buffer:elements bar)))
|
csr21@59
|
54
|
c@61
|
55 (defun events-from-slice (slice index durations)
|
csr21@59
|
56 (let ((time 0))
|
csr21@59
|
57 (mapcan (lambda (bar duration)
|
c@61
|
58 (prog1 (events-from-bar bar index time)
|
csr21@59
|
59 (incf time (* 4 duration))))
|
csr21@59
|
60 (gsharp-buffer:bars slice) durations)))
|
csr21@59
|
61
|
csr21@59
|
62 (defun segment-composition (segment)
|
csr21@59
|
63 (let* ((slices (mapcar #'gsharp-buffer:body (gsharp-buffer::layers segment)))
|
csr21@59
|
64 (durations (gsharp-play::measure-durations slices))
|
csr21@59
|
65 (gsharp-play::*tuning* (gsharp-buffer:tuning segment))
|
csr21@59
|
66 (events (loop for slice in slices
|
csr21@59
|
67 for i from 0
|
csr21@59
|
68 for events = (events-from-slice slice i durations)
|
csr21@59
|
69 then (merge 'list events (events-from-slice slice i durations) 'time<)
|
csr21@59
|
70 finally (return events))))
|
csr21@59
|
71 (let* ((duration (* 4 (reduce #'+ durations)))
|
csr21@59
|
72 (result (make-instance 'gsharp-composition
|
csr21@59
|
73 :buffer (gsharp-buffer:buffer segment)
|
csr21@59
|
74 ;; FIXME: this will break as soon as
|
csr21@59
|
75 ;; gsharp is made to have a sane
|
csr21@59
|
76 ;; divisions value in play.lisp
|
csr21@59
|
77 ;; instead of 25
|
c@150
|
78 :tempi (list (make-standard-tempo-period (* 128 (/ (* 4 25) (gsharp-buffer:tempo segment))) 0 duration))
|
csr21@59
|
79 :time 0
|
csr21@59
|
80 :interval duration)))
|
csr21@59
|
81 (sequence:adjust-sequence result (length events)
|
csr21@59
|
82 :initial-contents events))))
|
csr21@59
|
83
|
csr21@59
|
84 #|
|
csr21@59
|
85
|
csr21@59
|
86 (in-package :clim-user)
|
csr21@59
|
87
|
c@60
|
88 (define-command (com-amuse-play :name t :command-table gsharp::global-gsharp-table)
|
csr21@59
|
89 ()
|
csr21@59
|
90 (let* ((segment (gsharp-cursor::segment (gsharp::current-cursor)))
|
csr21@59
|
91 (composition (amuse-gsharp::segment-composition segment)))
|
csr21@59
|
92 (amuse-utils::play composition)))
|
csr21@59
|
93
|
c@60
|
94 (define-command (com-infer-key :name t :command-table gsharp::global-gsharp-table)
|
csr21@59
|
95 ()
|
csr21@59
|
96 (let* ((segment (gsharp-cursor::segment (gsharp::current-cursor)))
|
csr21@59
|
97 (composition (amuse-gsharp::segment-composition segment))
|
c@94
|
98 (result (amuse-harmony:krumhansl-key-finder composition composition))
|
csr21@59
|
99 (name (aref #("C" "C#" "D" "Eb" "E" "F" "F#" "G" "Ab" "A" "Bb" "B") (car result)))
|
csr21@59
|
100 (string (format nil "Key: ~A ~(~A~)" name (cadr result))))
|
c@91
|
101 (esa:display-message string)))
|
csr21@59
|
102
|
csr21@59
|
103
|
c@60
|
104 (in-package :gsharp-measure)
|
c@60
|
105
|
c@92
|
106 (define-stealth-mixin snote () note
|
c@60
|
107 ((misspeltp :initform nil :accessor misspeltp)))
|
c@60
|
108
|
c@60
|
109 (in-package :gsharp-drawing)
|
c@60
|
110
|
c@60
|
111 ;;; patch DRAW-NOTE
|
c@60
|
112 (defun draw-note (pane note notehead nb-dots x pos dot-xoffset dot-pos)
|
c@60
|
113 (score-pane:with-vertical-score-position (pane (staff-yoffset (staff note)))
|
c@61
|
114 (cond
|
c@61
|
115 ((gsharp-measure::misspeltp note)
|
c@61
|
116 (with-output-as-presentation (pane note 'note)
|
c@61
|
117 (score-pane:draw-notehead pane notehead x pos))
|
c@61
|
118 (let ((ypos (score-pane:staff-step (- pos))))
|
c@61
|
119 (draw-line* pane (- x 5) (+ ypos 3) (+ x 5) (+ ypos 3)
|
c@61
|
120 :line-thickness 3 :ink (make-rgb-color 0.8 0.1 0.1))))
|
c@61
|
121 (t (score-pane:draw-notehead pane notehead x pos)))
|
c@60
|
122 (when (final-accidental note)
|
c@60
|
123 (score-pane:draw-accidental pane (final-accidental note) (final-absolute-accidental-xoffset note) pos))
|
c@60
|
124 (draw-dots pane nb-dots x dot-xoffset dot-pos)))
|
c@60
|
125
|
c@60
|
126 (in-package :clim-user)
|
c@60
|
127
|
c@60
|
128 (defun ocp-list (composition)
|
c@60
|
129 (flet ((sorter (x y)
|
c@60
|
130 (or (amuse:time< x y)
|
c@60
|
131 (and (amuse:time= x y)
|
c@60
|
132 (amuse:pitch< x y)))))
|
c@60
|
133 (loop for e being each element of composition
|
c@60
|
134 if (typep e 'amuse:pitched-event)
|
c@60
|
135 collect (cons e (make-array 2 :initial-contents
|
c@60
|
136 (list (slot-value e 'amuse::time)
|
c@60
|
137 (- (slot-value e 'amuse::number) 21)))) into result
|
c@60
|
138 finally (return (sort result #'sorter :key #'car)))))
|
c@60
|
139
|
c@60
|
140 (let ((accidental-conversions
|
c@60
|
141 '(("ss" . :double-sharp) ("s" . :sharp) ("n" . :natural)
|
c@60
|
142 ("f" . :flat) ("ff" . :double-flat))))
|
c@60
|
143 (defun annotate-misspellings (composition)
|
c@60
|
144 (do* ((ocp-list (ocp-list composition))
|
c@93
|
145 (note-sequence (ps13:ps13-new-imp (map 'list #'cdr ocp-list) 10 42 nil nil nil))
|
c@60
|
146 (list ocp-list (cdr list))
|
c@60
|
147 (spellings note-sequence (cdr spellings)))
|
c@60
|
148 ((null list))
|
c@60
|
149 (let* ((event (caar list))
|
csr21@62
|
150 (note (amuse-gsharp:note event))
|
c@61
|
151 (accidental (elt (second (car spellings)) 1))
|
c@61
|
152 (converted (cdr (assoc accidental accidental-conversions :test #'string=))))
|
c@61
|
153 (unless (eq (gsharp-buffer:accidentals note) converted)
|
c@61
|
154 ;; this pun is not to be preserved once we get into real
|
c@61
|
155 ;; code.
|
c@61
|
156 (setf (gsharp-measure::misspeltp note) (second (car spellings))))))))
|
c@60
|
157
|
c@60
|
158 (define-command (com-spellcheck :name t :command-table gsharp::global-gsharp-table)
|
c@60
|
159 ()
|
c@60
|
160 (let* ((segment (gsharp-cursor::segment (gsharp::current-cursor)))
|
c@60
|
161 (composition (amuse-gsharp::segment-composition segment)))
|
c@60
|
162 (annotate-misspellings composition)
|
c@60
|
163 #+nil
|
c@60
|
164 (esa:display-message "~A" (first (ocp-list composition)))))
|
c@61
|
165
|
c@61
|
166 (let ((accidental-conversions
|
c@61
|
167 '(("ss" . :double-sharp) ("s" . :sharp) ("n" . :natural)
|
c@61
|
168 ("f" . :flat) ("ff" . :double-flat))))
|
c@61
|
169 (define-command (com-correct-misspelt-note :name t :command-table gsharp::global-gsharp-table)
|
c@61
|
170 ((note 'gsharp::note))
|
c@61
|
171 (let ((spelling (gsharp-measure::misspeltp note))
|
c@61
|
172 (element (gsharp-buffer::cluster note))
|
c@61
|
173 (staff (gsharp-buffer::staff note))
|
c@61
|
174 (head (gsharp-buffer::head note))
|
c@61
|
175 (dots (gsharp-buffer::dots note)))
|
c@61
|
176 (let ((pitch (+ (mod (- (digit-char-p (char (elt spelling 0) 0) 17) 12) 7)
|
c@61
|
177 (* (parse-integer (elt spelling 2)) 7)))
|
c@61
|
178 (accidentals (cdr (assoc (elt spelling 1) accidental-conversions :test #'string=))))
|
c@61
|
179 (let ((new-note (gsharp-buffer::make-note pitch staff
|
c@61
|
180 :head head
|
c@61
|
181 :dots dots
|
c@61
|
182 :accidentals accidentals)))
|
c@61
|
183 (gsharp-buffer::remove-note note)
|
c@61
|
184 (gsharp-buffer::add-note element new-note))))))
|
c@61
|
185
|
c@61
|
186 (define-presentation-to-command-translator correct-misspelt-note-translator
|
c@61
|
187 (gsharp::note com-correct-misspelt-note gsharp::global-gsharp-table
|
c@61
|
188 :gesture :select
|
c@61
|
189 :tester ((object) (gsharp-measure::misspeltp object)))
|
c@61
|
190 (object)
|
c@61
|
191 (list object))
|
c@60
|
192
|
csr21@59
|
193 |# |