]> git.donarmstrong.com Git - lilypond.git/blob - scm/music-functions.scm
remove tail, filter-list, filter-out-list,
[lilypond.git] / scm / music-functions.scm
1
2 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3
4 (define-public (music-map function music)
5   "Apply @var{function} to @var{music} and all of the music it contains. "
6   (let* ((es (ly:get-mus-property music 'elements))
7          (e (ly:get-mus-property music 'element))
8          )
9
10     (ly:set-mus-property! music 'elements 
11         (map (lambda (y) (music-map  function y)) es))
12         (if (ly:music? e)
13             (ly:set-mus-property! music 'element (music-map function  e)))
14         (function music)
15         ))
16
17 (define-public (display-music music)
18   "Display music, not done with music-map for clarity of presentation."
19   (display music)
20   (display ": { ")
21   
22   (let* ((es (ly:get-mus-property music 'elements))
23          (e (ly:get-mus-property music 'element))
24          )
25
26     (display (ly:get-mutable-properties music))
27
28     (if (pair?  es)
29         (begin (display "\nElements: {\n")
30                (map display-music es)
31                (display "}\n")
32         ))
33     
34     
35     (if (ly:music? e)
36         (begin
37           (display "\nChild:")
38           (display-music e)
39           )
40         )
41     )
42   (display " }\n")
43   music
44   )
45
46
47
48
49   
50 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
51
52 (define (shift-one-duration-log music shift dot)
53   "  add SHIFT to ly:duration-log and optionally 
54   a dot to any note encountered. This scales the music up by a factor 
55   2^shift * (2 - (1/2)^dot)"
56
57   (let*
58       (
59        (d (ly:get-mus-property music 'duration))
60        )
61     (if (ly:duration? d)
62         (let* (
63                (cp (ly:duration-factor d))
64                (nd (ly:make-duration (+ shift (ly:duration-log d))
65                                      (+ dot (ly:duration-dot-count d))
66                                      (car cp)
67                                      (cdr cp)))
68                
69                )
70           (ly:set-mus-property! music 'duration nd)
71           ))
72     music))
73
74
75
76 (define-public (shift-duration-log music shift dot)
77   (music-map (lambda (x) (shift-one-duration-log x shift dot))
78              music))
79   
80
81
82
83 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
84 ;; repeats.
85
86 (define-public (unfold-repeats music)
87 "
88 This function replaces all repeats  with unfold repeats. It was 
89 written by Rune Zedeler. "
90   (let* ((es (ly:get-mus-property music 'elements))
91          (e (ly:get-mus-property music 'element))
92          (n  (ly:music-name music)))
93  
94     (if (equal? n "Repeated_music")
95         (begin
96           (if (equal?
97                (ly:get-mus-property music 'iterator-ctor)
98                Chord_tremolo_iterator::constructor)
99               (shift-duration-log music  (ly:intlog2 (ly:get-mus-property music 'repeat-count)) 0)
100               )
101           (ly:set-mus-property!
102            music 'length Repeated_music::unfolded_music_length)
103           (ly:set-mus-property!
104            music 'start-moment-function Repeated_music::first_start)
105           (ly:set-mus-property!
106            music 'iterator-ctor Unfolded_repeat_iterator::constructor)))
107
108     (if (pair? es)
109         (ly:set-mus-property!
110          music 'elements
111          (map unfold-repeats es)))
112
113     (if (ly:music? e)
114         (ly:set-mus-property!
115          music 'element
116          (unfold-repeats e)))
117
118     music))
119
120
121 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
122 ;; property setting music objs.
123
124 (define-public (make-grob-property-set grob gprop val)
125
126   "Make a Music expression that sets GPROP to VAL in GROB. Does a pop first,
127 i.e.  this is not an override"
128   
129    (let* ((m (make-music-by-name  'OverrideProperty)))
130      (ly:set-mus-property! m 'symbol grob)
131      (ly:set-mus-property! m 'grob-property gprop)
132      (ly:set-mus-property! m 'grob-value val)
133      (ly:set-mus-property! m 'pop-first #t)
134                 
135      m
136    
137    ))
138
139
140 (define-public (make-grob-property-revert grob gprop)
141   "Revert the grob property GPROP for GROB."
142    (let* ((m (make-music-by-name  'OverrideProperty)))
143      (ly:set-mus-property! m 'symbol grob)
144      (ly:set-mus-property! m 'grob-property gprop)
145                 
146      m
147    
148    ))
149
150
151 (define-public (make-voice-props-set n)
152   (make-sequential-music
153    (append
154       (map (lambda (x) (make-grob-property-set x 'direction
155                                                (if (odd? n) -1 1)))
156            '(Tie Slur Stem Dots))
157       (list
158        (make-grob-property-set 'NoteColumn 'horizontal-shift (quotient n 2))
159        (make-grob-property-set 'MultiMeasureRest 'staff-position
160                                (if (odd? n) -4 4)
161                                )
162        
163        )
164    )
165   ))
166
167
168 (define-public (make-voice-props-revert)
169   (make-sequential-music
170    (list
171       (make-grob-property-revert 'Tie 'direction)
172       (make-grob-property-revert 'Dots 'direction)
173       (make-grob-property-revert 'Stem 'direction)
174       (make-grob-property-revert 'Slur 'direction)          
175       (make-grob-property-revert 'NoteColumn 'horizontal-shift)
176    ))
177   )
178
179
180 (define-public (context-spec-music m context . rest)
181   "Add \context CONTEXT = foo to M. "
182   
183   (let* ((cm (make-music-by-name 'ContextSpeccedMusic)))
184     (ly:set-mus-property! cm 'element m)
185     (ly:set-mus-property! cm 'context-type context)
186     (if (and  (pair? rest) (string? (car rest)))
187         (ly:set-mus-property! cm 'context-id (car rest))
188     )
189     cm
190   ))
191
192 (define-public (make-apply-context func)
193   (let*
194       (
195        (m (make-music-by-name 'ApplyContext))
196        )
197
198     (ly:set-mus-property! m 'procedure func)
199     m
200   ))
201
202 (define-public (make-sequential-music elts)
203   (let*  ((m (make-music-by-name 'SequentialMusic)))
204     (ly:set-mus-property! m 'elements elts)
205     m
206   ))
207
208 (define-public (make-simultaneous-music elts)
209   (let*  ((m (make-music-by-name 'SimultaneousMusic)))
210     (ly:set-mus-property! m 'elements elts)
211     m
212     ))
213
214 (define-public (make-event-chord elts)
215   (let*  ((m (make-music-by-name 'EventChord)))
216     (ly:set-mus-property! m 'elements elts)
217     m
218     ))
219
220 ;;;;;;;;;;;;;;;;
221 ;; mmrest
222 (define-public (make-multi-measure-rest duration location)
223   (let*
224       (
225        (start (make-music-by-name 'MultiMeasureRestEvent))
226        (stop  (make-music-by-name 'MultiMeasureRestEvent))
227        (skip ( make-music-by-name 'SkipEvent))
228        (ch (make-music-by-name 'BarCheck))
229        (ch2  (make-music-by-name 'BarCheck))
230        (seq  (make-music-by-name 'MultiMeasureRestMusicGroup))
231        )
232
233     (map (lambda (x) (ly:set-mus-property! x 'origin location))
234          (list start stop skip ch ch2 seq))
235     (ly:set-mus-property! start 'span-direction START)
236     (ly:set-mus-property! stop 'span-direction STOP)    
237     (ly:set-mus-property! skip 'duration duration)
238     (ly:set-mus-property! seq 'elements
239      (list
240       ch
241       (make-event-chord (list start))
242       (make-event-chord (list skip))
243       (make-event-chord (list stop))
244       ch2
245       ))
246
247     seq
248     ))
249
250 (define-public (glue-mm-rest-texts music)
251   "Check if we have R1*4-\markup { .. }, and if applicable convert to
252 a property set for MultiMeasureRestNumber."
253   
254   (define (script-to-mmrest-text script-music)
255     "Extract 'direction and 'text   from SCRIPT-MUSIC, and transform into property sets."
256     
257     (let*
258         (
259          (text (ly:get-mus-property script-music 'text))
260          (dir (ly:get-mus-property script-music 'direction))
261          (p (make-music-by-name 'MultiMeasureTextEvent))
262          )
263
264       (if (ly:dir? dir)
265           (ly:set-mus-property! p  'direction dir))
266       (ly:set-mus-property! p 'text text)
267       p
268     ))
269   
270   (if (eq? (ly:get-mus-property music 'name)  'MultiMeasureRestMusicGroup)
271       (let*
272           (
273            (text? (lambda (x) (memq 'script-event (ly:get-mus-property x 'types))))
274            (es (ly:get-mus-property  music 'elements))
275            (texts (map script-to-mmrest-text  (filter text? es)))
276            (others (remove text? es))
277            )
278         (if (pair? texts)
279             (ly:set-mus-property!
280              music 'elements
281              (cons (make-event-chord texts) others)
282             ))
283       ))
284   music
285   )
286
287
288 (define-public (make-property-set sym val)
289   (let*
290       (
291        (m (make-music-by-name 'PropertySet))
292        )
293     (ly:set-mus-property! m 'symbol sym)
294     (ly:set-mus-property! m 'value val)
295     m
296   ))
297
298
299
300 (define-public (make-ottava-set octavation)
301   (let*
302       (
303        (m (make-music-by-name 'ApplyContext))
304        )
305     
306   
307   (define (ottava-modify context)
308     "Either reset centralCPosition to the stored original,
309 or remember old centralCPosition, add OCTAVATION to centralCPosition,
310 and set OTTAVATION to `8va', or whatever appropriate."
311     (if (number? (ly:get-context-property  context 'centralCPosition))
312         
313         (if (= octavation 0)
314             (let*
315                 ((where (ly:context-property-where-defined context 'centralCPosition))
316                  (oc0 (ly:get-context-property context 'originalCentralCPosition)))
317
318               (ly:set-context-property context 'centralCPosition oc0)
319               (ly:unset-context-property where 'originalCentralCPosition)
320               (ly:unset-context-property where 'ottavation))
321
322             (let*
323                 ((where (ly:context-property-where-defined context 'centralCPosition))
324                  (c0 (ly:get-context-property context 'centralCPosition))
325                  (new-c0 (+ c0 (* -7 octavation)))
326                  (string (cdr
327                           (assoc octavation '((2 . "15ma")
328                                               (1 . "8va")
329                                               (0 . #f)
330                                               (-1 . "8va bassa")
331                                               (-2 . "15ma bassa"))))))
332
333               (ly:set-context-property context 'centralCPosition new-c0)
334               (ly:set-context-property context 'originalCentralCPosition c0)
335               (ly:set-context-property context 'ottavation string)
336               
337               ))))
338
339   (ly:set-mus-property! m 'procedure  ottava-modify)
340   (context-spec-music m "Staff")
341   ))
342
343 (define-public (set-octavation ottavation)
344   (ly:export (make-ottava-set ottavation)))
345
346 (define-public (make-time-signature-set num den . rest)
347   " Set properties for time signature NUM/DEN.
348 Rest can contain a list of beat groupings 
349
350 "
351   
352   (let*
353       (
354        (set1 (make-property-set 'timeSignatureFraction (cons num den) ))
355        (beat (ly:make-moment 1 den))
356        (len  (ly:make-moment num den))
357        (set2 (make-property-set 'beatLength beat))
358        (set3 (make-property-set 'measureLength len))
359        (set4 (make-property-set 'beatGrouping (if (pair? rest)
360                                                   (car rest)
361                                                   '())))
362        (basic  (list set1 set2 set3 set4)))
363
364     (context-spec-music
365      (make-sequential-music basic) "Timing")))
366
367 (define-public (set-time-signature num den . rest)
368   (ly:export (apply make-time-signature-set `(,num ,den . ,rest))))
369
370 (define-public (make-penalty-music pen)
371  (let
372      ((m (make-music-by-name 'BreakEvent)))
373    (ly:set-mus-property! m 'penalty pen)
374    m))
375
376 (define-public (make-articulation name)
377   (let* (
378          (m (make-music-by-name 'ArticulationEvent))
379       )
380       (ly:set-mus-property! m 'articulation-type name)
381       m
382   ))
383
384 (define-public (make-span-event type spandir)
385   (let* (
386          (m (make-music-by-name  type))
387          )
388     (ly:set-mus-property! m 'span-direction spandir)
389     m
390     ))
391
392 (define-public (set-mus-properties! m alist)
393   "Set all of ALIST as properties of M." 
394   (if (pair? alist)
395       (begin
396         (ly:set-mus-property! m (caar alist) (cdar alist))
397         (set-mus-properties! m (cdr alist)))
398   ))
399
400 (define-public (music-separator? m)
401   "Is M a separator?"
402   (let* ((ts (ly:get-mus-property m 'types )))
403     (memq 'separator ts)
404   ))
405
406
407 ;;; splitting chords into voices.
408
409 (define (voicify-list lst number)
410    "Make a list of Musics.
411
412    voicify-list :: [ [Music ] ] -> number -> [Music]
413    LST is a list music-lists.
414 "
415
416    (if (null? lst) '()
417        (cons (context-spec-music
418               (make-sequential-music
419                (list
420                 (make-voice-props-set number)
421                 (make-simultaneous-music (car lst))))
422
423               "Voice"  (number->string number))
424               (voicify-list (cdr lst) (+ number 1))
425        ))
426    )
427
428 (define (voicify-chord ch)
429   "Split the parts of a chord into different Voices using separator"
430    (let* ((es (ly:get-mus-property ch 'elements)))
431
432
433      (ly:set-mus-property!  ch 'elements
434        (voicify-list (split-list es music-separator?) 0))
435      ch
436    ))
437
438 (define (voicify-music m)
439    "Recursively split chords that are separated with \\ "
440    
441    (if (not (ly:music? m))
442        (begin (display m)
443        (error "not music!"))
444        )
445    (let*
446        ((es (ly:get-mus-property m 'elements))
447         (e (ly:get-mus-property m 'element))
448         )
449      (if (pair? es)
450          (ly:set-mus-property! m 'elements (map voicify-music es)))
451      (if (ly:music? e)
452          (ly:set-mus-property! m 'element  (voicify-music e)))
453      (if
454       (and (equal? (ly:music-name m) "Simultaneous_music")
455            (reduce (lambda (x y ) (or x y)) #f (map music-separator? es)))
456       (voicify-chord m)
457       )
458
459      m
460      ))
461
462 (define-public (empty-music)
463   (ly:export (make-music-by-name 'Music))
464   )
465 ;;;
466
467 ; Make a function that checks score element for being of a specific type. 
468 (define-public (make-type-checker symbol)
469   (lambda (elt)
470     ;;(display  symbol)
471     ;;(eq? #t (ly:get-grob-property elt symbol))
472     (not (eq? #f (memq symbol (ly:get-grob-property elt 'interfaces))))))
473
474
475 ;;
476 (define-public (smart-bar-check n)
477   "Make  a bar check that checks for a specific bar number. 
478 "
479   (let*
480       (
481        (m (make-music-by-name 'ApplyContext))
482        )
483     
484     (define (checker tr)
485       (let* ((bn (ly:get-context-property tr 'currentBarNumber)))
486         (if (= bn  n)
487             #t
488             (error
489              (format "Bar check failed, we should have reached ~a, instead at ~a\n"
490                      n bn ))
491             )))
492
493     (ly:set-mus-property! m 'procedure checker)
494     m
495     ))
496
497 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
498 ;; warn for bare chords at start.
499
500 (define (has-request-chord elts)
501   (reduce (lambda (x y) (or x y)) #f (map (lambda (x) (equal? (ly:music-name x)
502                                                            "Request_chord")) elts)
503   ))
504
505 (define (ly:music-message music msg)
506   (let*
507       (
508       (ip (ly:get-mus-property music 'origin))
509       )
510
511     (if (ly:input-location? ip)
512         (ly:input-message ip msg)
513         (ly:warn msg))
514   ))
515   
516 (define (check-start-chords music)
517   "Check music expression for a Simultaneous_music containing notes\n(ie. Request_chords), without context specification. Called  from parser."
518   
519      (let*
520        ((es (ly:get-mus-property music 'elements))
521         (e (ly:get-mus-property music 'element))
522         (name (ly:music-name music)) 
523         )
524
525        (cond 
526          ((equal? name "Context_specced_music") #t)
527          ((equal? name "Simultaneous_music")
528
529           (if (has-request-chord es)
530               (ly:music-message music "Starting score with a chord.\nPlease insert an explicit \\context before chord")
531               (map check-start-chords es)))
532          
533          ((equal? name "Sequential_music")
534            (if (pair? es)
535                (check-start-chords (car es))))
536           (else (if (ly:music? e) (check-start-chords e )))
537        
538        ))
539      music
540      )
541
542 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
543 ;; switch it on here, so parsing and init isn't checked (too slow!)
544
545 ;; automatic music transformations.
546
547 (define (switch-on-debugging m)
548   (set-debug-cell-accesses! 15000)
549   m
550   )
551
552 (define-public toplevel-music-functions
553   (list check-start-chords
554         voicify-music
555         (lambda (x) (music-map glue-mm-rest-texts x))
556 ; switch-on-debugging
557         ))
558
559