]> git.donarmstrong.com Git - lilypond.git/blob - scm/music-functions.scm
* scm/music-functions.scm (set-octavation): new function.
[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-list text? es)))
276            (others (filter-out-list 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 "
312     (if (= octavation 0)
313         (let*
314             ((where (ly:context-property-where-defined context 'centralCPosition))
315              (oc0 (ly:get-context-property context 'originalCentralCPosition))
316
317              )
318           
319           (ly:set-context-property context 'centralCPosition oc0)
320           (ly:unset-context-property where 'originalCentralCPosition)
321           (ly:unset-context-property where 'ottavation)
322           )
323         
324         (let*
325             ((where (ly:context-property-where-defined context 'centralCPosition))
326              (c0 (ly:get-context-property context 'centralCPosition))
327              (new-c0 (+ c0 (* -7 octavation)))
328              (string (cdr
329                       (assoc octavation '((2 . "15ma")
330                                           (1 . "8va")
331                                           (0 . #f)
332                                           (-1 . "8va bassa")
333                                           (-2 . "15ma bassa")))))
334              )
335
336           (ly:set-context-property where 'centralCPosition new-c0)
337           (ly:set-context-property where 'originalCentralCPosition c0)
338           (ly:set-context-property where 'ottavation string)
339           
340           )))
341
342   (ly:set-mus-property! m 'procedure  ottava-modify)
343   m
344   ))
345
346 (define-public (set-octavation ottavation)
347   (ly:export (make-ottava-set ottavation)))
348
349 (define-public (make-time-signature-set num den . rest)
350   " Set properties for time signature NUM/DEN.
351 Rest can contain a list of beat groupings 
352
353 "
354   
355   (let*
356       (
357        (set1 (make-property-set 'timeSignatureFraction (cons num den) ))
358        (beat (ly:make-moment 1 den))
359        (len  (ly:make-moment num den))
360        (set2 (make-property-set 'beatLength beat))
361        (set3 (make-property-set 'measureLength len))
362        (set4 (make-property-set 'beatGrouping (if (pair? rest)
363                                                   (car rest)
364                                                   '())))
365        (basic  (list set1 set2 set3 set4))
366        
367        )
368
369     (context-spec-music
370      (make-sequential-music basic) "Timing")))
371
372 (define-public (set-time-signature num den . rest)
373   (ly:export (apply make-time-signature-set `(,num ,den . ,rest)))
374   )
375
376 (define-public (make-penalty-music pen)
377  (let
378      ((m (make-music-by-name 'BreakEvent)))
379    (ly:set-mus-property! m 'penalty pen)
380    m))
381
382 (define-public (make-articulation name)
383   (let* (
384          (m (make-music-by-name 'ArticulationEvent))
385       )
386       (ly:set-mus-property! m 'articulation-type name)
387       m
388   ))
389
390 (define-public (make-span-event type spandir)
391   (let* (
392          (m (make-music-by-name  type))
393          )
394     (ly:set-mus-property! m 'span-direction spandir)
395     m
396     ))
397
398 (define-public (set-mus-properties! m alist)
399   "Set all of ALIST as properties of M." 
400   (if (pair? alist)
401       (begin
402         (ly:set-mus-property! m (caar alist) (cdar alist))
403         (set-mus-properties! m (cdr alist)))
404   ))
405
406 (define-public (music-separator? m)
407   "Is M a separator?"
408   (let* ((ts (ly:get-mus-property m 'types )))
409     (memq 'separator ts)
410   ))
411
412
413 ;;; splitting chords into voices.
414
415 (define (voicify-list lst number)
416    "Make a list of Musics.
417
418    voicify-list :: [ [Music ] ] -> number -> [Music]
419    LST is a list music-lists.
420 "
421
422    (if (null? lst) '()
423        (cons (context-spec-music
424               (make-sequential-music
425                (list
426                 (make-voice-props-set number)
427                 (make-simultaneous-music (car lst))))
428
429               "Voice"  (number->string number))
430               (voicify-list (cdr lst) (+ number 1))
431        ))
432    )
433
434 (define (voicify-chord ch)
435   "Split the parts of a chord into different Voices using separator"
436    (let* ((es (ly:get-mus-property ch 'elements)))
437
438
439      (ly:set-mus-property!  ch 'elements
440        (voicify-list (split-list es music-separator?) 0))
441      ch
442    ))
443
444 (define (voicify-music m)
445    "Recursively split chords that are separated with \\ "
446    
447    (if (not (ly:music? m))
448        (begin (display m)
449        (error "not music!"))
450        )
451    (let*
452        ((es (ly:get-mus-property m 'elements))
453         (e (ly:get-mus-property m 'element))
454         )
455      (if (pair? es)
456          (ly:set-mus-property! m 'elements (map voicify-music es)))
457      (if (ly:music? e)
458          (ly:set-mus-property! m 'element  (voicify-music e)))
459      (if
460       (and (equal? (ly:music-name m) "Simultaneous_music")
461            (reduce (lambda (x y ) (or x y))     (map music-separator? es)))
462       (voicify-chord m)
463       )
464
465      m
466      ))
467
468 (define-public (empty-music)
469   (ly:export (make-music-by-name 'Music))
470   )
471 ;;;
472
473 ; Make a function that checks score element for being of a specific type. 
474 (define-public (make-type-checker symbol)
475   (lambda (elt)
476     ;;(display  symbol)
477     ;;(eq? #t (ly:get-grob-property elt symbol))
478     (not (eq? #f (memq symbol (ly:get-grob-property elt 'interfaces))))))
479
480
481 ;;
482 (define-public (smart-bar-check n)
483   "Make  a bar check that checks for a specific bar number. 
484 "
485   (let*
486       (
487        (m (make-music-by-name 'ApplyContext))
488        )
489     
490     (define (checker tr)
491       (let* ((bn (ly:get-context-property tr 'currentBarNumber)))
492         (if (= bn  n)
493             #t
494             (error
495              (format "Bar check failed, we should have reached ~a, instead at ~a\n"
496                      n bn ))
497             )))
498
499     (ly:set-mus-property! m 'procedure checker)
500     m
501     ))
502
503 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
504 ;; warn for bare chords at start.
505
506 (define (has-request-chord elts)
507   (reduce (lambda (x y) (or x y)) (map (lambda (x) (equal? (ly:music-name x)
508                                                            "Request_chord")) elts)
509   ))
510
511 (define (ly:music-message music msg)
512   (let*
513       (
514       (ip (ly:get-mus-property music 'origin))
515       )
516
517     (if (ly:input-location? ip)
518         (ly:input-message ip msg)
519         (ly:warn msg))
520   ))
521   
522 (define (check-start-chords music)
523   "Check music expression for a Simultaneous_music containing notes\n(ie. Request_chords), without context specification. Called  from parser."
524   
525      (let*
526        ((es (ly:get-mus-property music 'elements))
527         (e (ly:get-mus-property music 'element))
528         (name (ly:music-name music)) 
529         )
530
531        (cond 
532          ((equal? name "Context_specced_music") #t)
533          ((equal? name "Simultaneous_music")
534
535           (if (has-request-chord es)
536               (ly:music-message music "Starting score with a chord.\nPlease insert an explicit \\context before chord")
537               (map check-start-chords es)))
538          
539          ((equal? name "Sequential_music")
540            (if (pair? es)
541                (check-start-chords (car es))))
542           (else (if (ly:music? e) (check-start-chords e )))
543        
544        ))
545      music
546      )
547
548 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
549 ;; switch it on here, so parsing and init isn't checked (too slow!)
550
551 ;; automatic music transformations.
552
553 (define (switch-on-debugging m)
554   (set-debug-cell-accesses! 15000)
555   m
556   )
557
558 (define-public toplevel-music-functions
559   (list check-start-chords
560         voicify-music
561         (lambda (x) (music-map glue-mm-rest-texts x))
562 ; switch-on-debugging
563         ))
564
565