]> git.donarmstrong.com Git - lilypond.git/blob - scm/part-combiner.scm
* lily/new-quote-iterator.cc (quote_ok): new function.
[lilypond.git] / scm / part-combiner.scm
1 ;;;; part-combiner.scm -- Part combining, staff changes.
2 ;;;;
3 ;;;;  source file of the GNU LilyPond music typesetter
4 ;;;; 
5 ;;;; (c)  2004  Han-Wen Nienhuys <hanwen@cs.uu.nl>
6
7 ;; todo: figure out how to make module,
8 ;; without breaking nested ly scopes
9
10 (define-class <Voice-state> ()
11   (event-list #:init-value '() #:accessor events #:init-keyword #:events)
12   (when-moment #:accessor when #:init-keyword #:when)
13   (tuning #:accessor tuning #:init-keyword #:tuning)
14   (split-index #:accessor split-index)
15   (vector-index)
16   (state-vector)
17   ;;;
18   ;; spanner-state is an alist
19   ;; of (SYMBOL . RESULT-INDEX), which indicates where
20   ;; said spanner was started.
21   (spanner-state #:init-value '() #:accessor span-state) )
22   
23 (define-method (write (x <Voice-state> ) file)
24   (display (when x) file)
25   (display " evs = " file)
26   (display (events x) file)
27   (display " active = " file)
28   (display (span-state x) file)
29   (display "\n" file) )
30
31 (define-method (note-events (vs <Voice-state>))
32   (define (f? x)
33     (equal? (ly:music-property  x 'name) 'NoteEvent))
34   (filter f? (events vs)))
35
36 (define-method (previous-voice-state (vs <Voice-state>))
37   (let ((i (slot-ref vs 'vector-index))
38         (v (slot-ref vs 'state-vector)) )
39     (if (< 0 i)
40         (vector-ref v (1- i))
41         #f)))
42
43 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
44
45 (define-class <Split-state> ()
46   (configuration #:init-value '() #:accessor configuration)
47   (when-moment #:accessor when #:init-keyword #:when)
48   ;; voice-states are states starting with the Split-state or later
49   ;;
50   (is #:init-keyword #:voice-states #:accessor voice-states)
51   (synced  #:init-keyword #:synced #:init-value  #f #:getter synced?))
52                              
53
54 (define-method (write (x <Split-state> ) f)
55   (display (when x) f)
56   (display " = " f)
57   (display (configuration x) f)
58   (if (synced? x)
59       (display " synced "))
60   (display "\n" f) )
61
62 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
63
64
65 (define (previous-span-state vs)
66   (let ((p (previous-voice-state vs)))
67     (if p (span-state p) '())))
68
69 (define (make-voice-states evl)
70   (let ((vec (list->vector (map (lambda (v)
71                                   (make <Voice-state>
72                                     #:when (caar v)
73                                     #:tuning (cdar v)
74                                     #:events (map car (cdr v))))
75                                 evl))))
76     (do ( (i 0 (1+ i)) )
77         ( (= i (vector-length vec)) vec)
78       (slot-set! (vector-ref vec i) 'vector-index i)
79       (slot-set! (vector-ref vec i) 'state-vector vec))))
80
81 (define (make-split-state vs1 vs2)
82   "Merge lists VS1 and VS2, containing Voice-state objects into vector
83 of Split-state objects, crosslinking the Split-state vector and
84 Voice-state objects
85 "  
86   (define (helper ss-idx ss-list idx1 idx2)
87     (let* ((state1 (if (< idx1 (vector-length vs1)) (vector-ref vs1 idx1) #f))
88            (state2 (if (< idx2 (vector-length vs2)) (vector-ref vs2 idx2) #f))
89            (min (cond ((and state1 state2) (moment-min (when state1) (when state2)))
90                       (state1 (when state1))
91                       (state2 (when state2))
92                       (else #f)))
93            (inc1 (if (and state1 (equal? min (when state1))) 1 0))
94            (inc2 (if (and state2 (equal? min (when state2))) 1 0))
95            (ss-object (if min
96                           (make <Split-state>
97                             #:when min
98                             #:voice-states (cons state1 state2)
99                             #:synced (= inc1 inc2))
100                           #f)))
101       (if state1
102           (set! (split-index state1) ss-idx))
103       (if state2
104           (set! (split-index state2) ss-idx))
105       (if min
106           (helper (1+ ss-idx)
107                   (cons ss-object ss-list)
108                   (+ idx1 inc1)
109                   (+ idx2 inc2))
110           ss-list)))
111   (list->vector (reverse! (helper 0 '() 0  0) '())))
112
113
114 (define (analyse-spanner-states voice-state-vec)
115
116   (define (helper index active)
117     "Analyse EVS at INDEX, given state ACTIVE."
118     
119     (define (analyse-tie-start active ev)
120       (if (equal? (ly:music-property ev 'name) 'TieEvent)
121           (acons 'tie (split-index (vector-ref voice-state-vec index))
122                  active)
123           active))
124     
125     (define (analyse-tie-end active ev)
126       (if (equal? (ly:music-property ev 'name) 'NoteEvent)
127           (assoc-remove! active 'tie)
128           active))
129
130     (define (analyse-absdyn-end active ev)
131       (if (equal? (ly:music-property ev 'name) 'AbsoluteDynamicEvent)
132           (assoc-remove! (assoc-remove! active 'cresc) 'decr)
133           active))
134     
135     (define (active<? a b)
136       (cond ((symbol<? (car a) (car b)) #t)
137             ((symbol<? (car b) (car b)) #f)
138             (else (< (cdr a) (cdr b)))))
139     
140     (define (analyse-span-event active ev)
141       (let* ((name (ly:music-property ev 'name))
142              (key (cond ((equal? name 'SlurEvent) 'slur)
143                         ((equal? name 'PhrasingSlurEvent) 'tie)
144                         ((equal? name 'BeamEvent) 'beam)
145                         ((equal? name 'CrescendoEvent) 'cresc)
146                         ((equal? name 'DecrescendoEvent) 'decr)
147                         (else #f)))
148              (sp (ly:music-property ev 'span-direction)))
149         (if (and (symbol? key) (ly:dir? sp))
150             (if (= sp STOP)
151                 (assoc-remove! active key)
152                 (acons key
153                        (split-index (vector-ref voice-state-vec index))
154                        active))
155             active)))
156
157     (define (analyse-events active evs)
158       "Run all analyzers on ACTIVE and EVS"
159       (define (run-analyzer analyzer active evs)
160         (if (pair? evs)
161             (run-analyzer analyzer (analyzer active (car evs)) (cdr evs))
162             active))
163       (define (run-analyzers analyzers active evs)
164         (if (pair? analyzers)
165             (run-analyzers (cdr analyzers)
166                            (run-analyzer (car analyzers) active evs)
167                            evs)
168             active))
169       (sort ;; todo: use fold or somesuch.
170        (run-analyzers (list analyse-absdyn-end analyse-span-event
171                             ;; note: tie-start/span comes after tie-end/absdyn.
172                             analyse-tie-end analyse-tie-start)
173                       active evs)
174        active<?))
175
176     ;; must copy, since we use assoc-remove!
177     (if (< index (vector-length voice-state-vec))
178         (begin
179           (set! active (analyse-events active (events (vector-ref voice-state-vec index))))
180           (set! (span-state (vector-ref voice-state-vec index))
181                 (list-copy active))
182           (helper (1+ index) active))))
183   
184   (helper 0 '()))
185
186
187         
188 (define noticed '())
189 (define part-combine-listener '())
190
191 ; UGH - should pass noticed setter to part-combine-listener
192 (define-public (set-part-combine-listener x)
193   (set! part-combine-listener x))
194
195 (define-public (notice-the-events-for-pc context lst)
196   "add CONTEXT-ID, EVENT list to NOTICED variable."
197   
198   (set! noticed (acons (ly:context-id context) lst noticed)))
199
200 (define-public (make-part-combine-music music-list)
201   (let ((m (make-music 'PartCombineMusic))
202         (m1 (make-non-relative-music (context-spec-music (car music-list) 'Voice "one")))
203         (m2  (make-non-relative-music  (context-spec-music (cadr music-list) 'Voice "two"))))
204     (set! (ly:music-property m 'elements) (list m1 m2))
205     (ly:run-translator m2 part-combine-listener)
206     (ly:run-translator m1 part-combine-listener)
207     (set! (ly:music-property m 'split-list)
208           (determine-split-list (reverse! (cdr (assoc "one" noticed)) '())
209                                 (reverse! (cdr (assoc "two" noticed)) '())))
210     (set! noticed '())
211     m))
212
213 (define-public (determine-split-list evl1 evl2)
214   "EVL1 and EVL2 should be ascending"
215   (let* ((pc-debug #f)
216          (chord-threshold 8)
217          (voice-state-vec1 (make-voice-states evl1))
218          (voice-state-vec2 (make-voice-states evl2))
219          (result (make-split-state voice-state-vec1 voice-state-vec2)))
220     
221     (define (analyse-time-step result-idx)
222       (define (put x . index)
223         "Put the result to X, starting from INDEX backwards.
224
225 Only set if not set previously.
226 "
227         (let ((i (if (pair? index) (car index) result-idx)))
228           (if (and (<= 0 i)
229                    (not (symbol? (configuration (vector-ref result i)))))
230               (begin
231                 (set! (configuration (vector-ref result i)) x)
232                 (put x (1- i))))))
233       
234       (define (copy-state-from state-vec vs)
235         (define (copy-one-state key-idx)
236           (let* ((idx (cdr key-idx))
237                  (prev-ss (vector-ref result idx))
238                  (prev (configuration prev-ss)))
239             (if (symbol? prev)
240                 (put prev))))
241         (map copy-one-state (span-state vs)))
242
243       (define (analyse-notes now-state)
244         (let* ((vs1 (car (voice-states now-state)))
245                (vs2 (cdr (voice-states now-state)))
246                (notes1 (note-events vs1))
247                (durs1    (sort (map (lambda (x) (ly:music-property x 'duration))
248                                     notes1)
249                                ly:duration<?))
250                (pitches1 (sort (map (lambda (x) (ly:music-property x 'pitch))
251                                     notes1)
252                                ly:pitch<?))
253                (notes2   (note-events vs2))
254                (durs2    (sort (map (lambda (x) (ly:music-property x 'duration))
255                                     notes2)
256                                ly:duration<?))
257                (pitches2 (sort (map (lambda (x) (ly:music-property x 'pitch))
258                                     notes2)
259                                ly:pitch<?)))
260           (cond ((> (length notes1) 1) (put 'apart))
261                 ((> (length notes2) 1) (put 'apart))
262                 ((= 1 (+ (length notes2) (length notes1))) (put 'apart))
263                 ((and (= (length durs1) 1)
264                       (= (length durs2) 1)
265                       (not (equal? (car durs1) (car durs2))))
266                  (put 'apart))
267                 (else
268                  (if (and (= (length pitches1) (length pitches2)))
269                      (if (and (pair? pitches1)
270                               (pair? pitches2)
271                               (or
272                                (< chord-threshold (ly:pitch-steps
273                                                    (ly:pitch-diff (car pitches1)
274                                                                   (car pitches2))))
275
276                                ;; voice crossings:
277                                (> 0 (ly:pitch-steps (ly:pitch-diff (car pitches1)
278                                                                    (car pitches2))))
279                                ))
280                          (put 'apart)
281                          ;; copy previous split state from spanner state
282                          (begin
283                            (if (previous-voice-state vs1)
284                                (copy-state-from voice-state-vec1
285                                                 (previous-voice-state vs1)))
286                            (if (previous-voice-state vs2)
287                                (copy-state-from voice-state-vec2
288                                                 (previous-voice-state vs2)))
289                            (if (and (null? (span-state vs1)) (null? (span-state vs2)))
290                                (put 'chords)))))))))
291       
292       (if (< result-idx (vector-length result))
293           (let* ((now-state (vector-ref result result-idx))
294                  (vs1 (car (voice-states now-state)))
295                  (vs2 (cdr (voice-states now-state))))
296             
297             (cond ((not vs1) (put 'apart))
298                   ((not vs2) (put 'apart))
299                   (else
300                    (let ((active1 (previous-span-state vs1))
301                          (active2 (previous-span-state vs2))
302                          (new-active1 (span-state vs1))
303                          (new-active2 (span-state vs2)))
304                      (if #f ; debug
305                          (display (list (when now-state) result-idx
306                                         active1 "->" new-active1
307                                         active2 "->" new-active2
308                                         "\n")))
309                      (if (and (synced? now-state)
310                               (equal? active1 active2)
311                               (equal? new-active1 new-active2))
312                          (analyse-notes now-state)
313                          
314                          ;; active states different:
315                          (put 'apart)))
316                    
317                    ;; go to the next one, if it exists.
318                    (analyse-time-step (1+ result-idx)))))))
319     
320     (define (analyse-a2 result-idx)
321       (if (< result-idx (vector-length result))
322           (let* ((now-state (vector-ref result result-idx))
323                  (vs1 (car (voice-states now-state)))
324                  (vs2 (cdr (voice-states now-state))))
325             (if (and (equal? (configuration now-state) 'chords)
326                      vs1 vs2)
327                 (let ((notes1 (note-events vs1)) 
328                       (notes2 (note-events vs2)))
329                   (cond ((and (= 1 (length notes1))
330                               (= 1 (length notes2))
331                               (equal? (ly:music-property (car notes1) 'pitch)
332                                       (ly:music-property (car notes2) 'pitch)))
333                          (set! (configuration now-state) 'unisono))
334                         ((and (= 0 (length notes1))
335                               (= 0 (length notes2)))
336                          (set! (configuration now-state) 'unisilence)))))
337             (analyse-a2 (1+ result-idx)))))
338     
339     (define (analyse-solo12 result-idx)
340       
341       (define (previous-config vs)
342         (let* ((pvs (previous-voice-state vs))
343                (spi (if pvs (split-index pvs) #f))
344                (prev-split (if spi (vector-ref result spi) #f)))
345           (if prev-split
346               (configuration prev-split)
347               'apart)))
348       
349       (define (put-range x a b)
350         ;; (display (list "put range "  x a b "\n"))
351         (do ((i a (1+ i)))
352             ((> i b) b)
353           (set! (configuration (vector-ref result i)) x)))
354       
355       (define (put x)
356         ;; (display (list "putting "  x "\n"))
357         (set! (configuration (vector-ref result result-idx)) x))
358       
359       (define (current-voice-state now-state voice-num)
360         (define vs ((if (= 1 voice-num) car cdr)
361                     (voice-states now-state)))
362         (if (or (not vs) (equal? (when now-state) (when vs)))
363             vs
364             (previous-voice-state vs)))
365       
366       (define (try-solo type start-idx current-idx)
367         "Find a maximum stretch that can be marked as solo. Only set
368 the mark when there are no spanners active."
369         (if (< current-idx (vector-length result))
370             (let* ((now-state (vector-ref result current-idx))
371                    (solo-state (current-voice-state now-state (if (equal? type 'solo1) 1 2)))
372                    (silent-state (current-voice-state now-state (if (equal? type 'solo1) 2 1)))
373                    (silent-notes (if silent-state (note-events silent-state) '()))
374                    (solo-notes (if solo-state (note-events solo-state) '())))
375               ;; (display (list "trying " type " at "  (when now-state) solo-state silent-state  "\n"))
376               (cond ((not (equal? (configuration now-state) 'apart))
377                      current-idx)
378                     ((> (length silent-notes) 0) start-idx)
379                     ((not solo-state)
380                      (put-range type start-idx current-idx)
381                      current-idx)
382                     ((and
383                       (null? (span-state solo-state)))
384                      ;;
385                      ;; This includes rests. This isn't a problem: long rests
386                      ;; will be shared with the silent voice, and be marked
387                      ;; as unisilence. Therefore, long rests won't 
388                      ;;  accidentally be part of a solo.
389                      ;;
390                      (put-range type start-idx current-idx)
391                      (try-solo type (1+ current-idx) (1+  current-idx)))
392                     (else
393                      (try-solo type start-idx (1+ current-idx)))))
394             start-idx)) ; try-solo
395       
396       (define (analyse-moment result-idx)
397         "Analyse 'apart starting at RESULT-IDX. Return next index. "
398         (let* ((now-state (vector-ref result result-idx))
399                (vs1 (current-voice-state now-state 1))
400                (vs2 (current-voice-state now-state 2))
401                ;; (vs1 (car (voice-states now-state)))
402                ;; (vs2 (cdr (voice-states now-state)))
403                (notes1 (if vs1 (note-events vs1) '()))
404                (notes2 (if vs2 (note-events vs2) '()))
405                (n1 (length notes1))
406                (n2 (length notes2)))
407           ;; (display (list "analyzing step " result-idx "  moment " (when now-state) vs1 vs2  "\n"))
408           (max                          ; we should always increase.
409            
410            (cond ((and (= n1 0) (= n2 0))
411                   (put 'apart-silence)
412                   (1+ result-idx))
413                  ((and (= n2 0)
414                        (equal? (when vs1) (when now-state))
415                        (null? (previous-span-state vs1)))
416                   (try-solo 'solo1 result-idx result-idx))
417                  ((and (= n1 0)
418                        (equal? (when vs2) (when now-state))
419                        (null? (previous-span-state vs2)))
420                   (try-solo 'solo2 result-idx result-idx))
421                  
422                  (else (1+ result-idx)))
423            (1+ result-idx)))) ; analyse-moment
424       
425       (if (< result-idx (vector-length result))
426           (if (equal? (configuration (vector-ref result result-idx)) 'apart)
427               (analyse-solo12 (analyse-moment result-idx))
428               (analyse-solo12 (1+ result-idx))))) ; analyse-solo12
429
430     (analyse-spanner-states voice-state-vec1)
431     (analyse-spanner-states voice-state-vec2)
432     (if #f
433         (begin
434           (display voice-state-vec1)
435           (display "***\n")
436           (display voice-state-vec2)
437           (display "***\n")
438           (display result)
439           (display "***\n")))
440     (analyse-time-step 0)
441     ;; (display result)
442     (analyse-a2 0)
443     ;;(display result)
444     (analyse-solo12 0)
445     ;; (display result)
446     (set! result (map
447                   (lambda (x) (cons (when x) (configuration x)))
448                   (vector->list result)))
449     (if #f ;; pc-debug
450          (display result))
451     result))
452
453 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
454 ;; autochange - fairly related to part combining.
455
456 (define-public (make-autochange-music music)
457   (define (generate-split-list change-moment event-list acc)
458     (if (null? event-list)
459         acc
460         (let* ((now-tun (caar event-list))
461                (evs (map car (cdar event-list)))
462                (now (car now-tun))
463                (notes (filter (lambda (x)
464                                 (equal? (ly:music-property  x 'name) 'NoteEvent))
465                               evs))
466                (pitch (if (pair? notes)
467                           (ly:music-property (car notes) 'pitch)
468                           #f)))
469           ;; tail recursive.
470           (if (and pitch (not (= (ly:pitch-steps pitch) 0)))
471               (generate-split-list #f
472                                    (cdr event-list)
473                                    (cons (cons
474
475                                           (if change-moment
476                                               change-moment
477                                               now)
478                                           (sign (ly:pitch-steps pitch))) acc))
479               (generate-split-list
480                (if pitch #f now)
481                (cdr event-list) acc)))))
482   
483   (set! noticed '())
484   (let* ((m (make-music 'AutoChangeMusic))
485          (context (ly:run-translator (make-non-relative-music music) part-combine-listener))
486          (evs (last-pair noticed))
487          (split (reverse! (generate-split-list
488                            #f
489                            (if (pair? evs)
490                                (reverse! (cdar evs) '()) '())
491                            '())
492                           '())))
493     (set! (ly:music-property m 'element) music)
494     (set! (ly:music-property m 'split-list) split)
495     (set! noticed '())
496     m))
497
498
499 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
500
501 (define-public (add-quotable name mus)
502   (set! noticed '())
503   (let* ((tab (eval 'musicQuotes (current-module) ))
504          (context (ly:run-translator (context-spec-music mus 'Voice)
505                                      part-combine-listener))
506          (first-voice-handle (last-pair noticed)))
507
508     ;; (display (last-pair noticed))
509     (if (pair? first-voice-handle)
510         (hash-set! tab name
511                    ;; cdr : skip name string
512                    (list->vector (reverse! (cdar first-voice-handle)
513                                            '()))))))
514