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