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