* lily/kpath.cc:
[lilypond.git] / scm / music-functions.scm
blob28dfc9dc9e9f7ea602d2344dd1fa277194074143
2 (define (denominator-tuplet-formatter mus)
3   (number->string (ly-get-mus-property mus 'denominator)))
5 (define (fraction-tuplet-formatter mus)
6   (string-append (number->string (ly-get-mus-property mus 'numerator))
7                  ":"
8                  (number->string (ly-get-mus-property mus 'denominator))
9                  ))
10 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
13 (define (shift-duration-log music shift dot)
14   "Recurse through music, adding SHIFT to duration-log and optionally 
15   a dot to any note encountered. This scales the music up by a factor 
16   2^shift * (2 - (1/2)^dot)"
17   (let* ((es (ly-get-mus-property music 'elements))
18          (e (ly-get-mus-property music 'element))
19          (n  (ly-music-name music))
20          (f  (lambda (x)  (shift-duration-log x shift dot)))
21          )
22     (if (or (equal? n "Note_req")
23             (equal? n "Rest_req"))
24         (let* (
25                (d (ly-get-mus-property music 'duration))
26                (cp (duration-factor d))
27                (nd (make-duration (+ shift (duration-log d))
28                                   (+ dot (duration-dot-count d))
29                                   (car cp)
30                                   (cdr cp)))
31                
32                )
33           (ly-set-mus-property! music 'duration nd)
34           ))
35     
36     (if (pair? es)
37         (ly-set-mus-property!
38          music 'elements
39          (map f es)))
40     
41     (if (music? e)
42         (ly-set-mus-property!
43          music 'element
44          (f e)))
45     
46     music))
49 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
50 (define (unfold-repeats music)
52 This function replaces all repeats  with unfold repeats. It was 
53 written by Rune Zedeler. "
54   (let* ((es (ly-get-mus-property music 'elements))
55          (e (ly-get-mus-property music 'element))
56          (n  (ly-music-name music)))
58     (if (equal? n "Repeated_music")
59         (begin
60           (if (equal?
61                (ly-get-mus-property music 'iterator-ctor)
62                Chord_tremolo_iterator::constructor)
63               (shift-duration-log music  (intlog2 (ly-get-mus-property music 'repeat-count)) 0)
64               )
65           (ly-set-mus-property!
66            music 'length Repeated_music::unfolded_music_length)
67           (ly-set-mus-property!
68            music 'start-moment-function Repeated_music::first_start)
69           (ly-set-mus-property!
70            music 'iterator-ctor Unfolded_repeat_iterator::constructor)))
72     (if (pair? es)
73         (ly-set-mus-property!
74          music 'elements
75          (map unfold-repeats es)))
77     (if (music? e)
78         (ly-set-mus-property!
79          music 'element
80          (unfold-repeats e)))
82     music))
85 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
86 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
88 (define  (pitchify-scripts music)
89   "Copy the pitch fields of the Note_requests into  Text_script_requests, to aid
90 Fingering_engraver."
91   (define (find-note musics)
92     (filter-list (lambda (m) (equal? (ly-music-name m) "Note_req")) musics)
93     )
94   (define (find-scripts musics)
95     (filter-list (lambda (m) (equal? (ly-music-name m) "Text_script_req")) musics))
97   (let* (
98          (e (ly-get-mus-property music 'element))
99          (es (ly-get-mus-property music 'elements))
100          (notes (find-note es))
101          (pitch (if (pair? notes) (ly-get-mus-property (car  notes) 'pitch) #f))
102          )
104     (if pitch
105         (map (lambda (x) (ly-set-mus-property! x 'pitch pitch)) (find-scripts es))
106         )
107         
108     (if (pair? es)
109         (ly-set-mus-property!
110          music 'elements
111          (map pitchify-scripts es)))
113     (if (music? e)
114         (ly-set-mus-property!
115          music 'element
116          (pitchify-scripts e)))
118     music))
121 ;;;;;;;;;;;;;;;;;
122 ;;;;;;;;;;;;;;;;
123 ;;;;;;;;;;;;;;;;
126 (define (make-grob-property-set grob gprop val)
127   "Make a M-exp that sets GPROP to VAL in GROBS. Does a pop first, i.e.
128 this is not an override 
130   
131    (let* ((m (ly-make-music  "Music")))
132      (ly-set-mus-property! m 'iterator-ctor Push_property_iterator::constructor)
133      (ly-set-mus-property! m 'symbol grob)
134      (ly-set-mus-property! m 'grob-property gprop)
135      (ly-set-mus-property! m 'grob-value val)
136      (ly-set-mus-property! m 'pop-first #t)
137                 
138      m
139    
140    ))
141    
142 (define (make-grob-property-revert grob gprop)
143   "Revert the grob property GPROP for GROB."
144    (let* ((m (ly-make-music  "Music")))
145      (ly-set-mus-property! m 'iterator-ctor Pop_property_iterator::constructor)
146      (ly-set-mus-property! m 'symbol grob)
147      (ly-set-mus-property! m 'grob-property gprop)
148                 
149      m
150    
151    ))
152    
153 (define (make-voice-props-set n)
154   (make-sequential-music
155    (append
156       (map (lambda (x) (make-grob-property-set x 'direction
157                                                (if (odd? n) -1 1)))
158            '(Tie Slur Stem Dots))
159       (list (make-grob-property-set 'NoteColumn 'horizontal-shift (quotient n 2)))
160    )
161   ))
163 (define (make-voice-props-revert)
164   (make-sequential-music
165    (list
166       (make-grob-property-revert 'Tie 'direction)
167       (make-grob-property-revert 'Dots 'direction)
168       (make-grob-property-revert 'Stem 'direction)
169       (make-grob-property-revert 'Slur 'direction)          
170       (make-grob-property-revert 'NoteColumn 'horizontal-shift)
171    ))
172   )
174 (define (context-spec-music m context . rest)
175   "Add \context CONTEXT = foo to M. "
176   
177   (let* ((cm (ly-make-music "Context_specced_music")))
178     (ly-set-mus-property! cm 'element m)
179     (ly-set-mus-property! cm 'context-type context)
180     (if (and  (pair? rest) (string? (car rest)))
181         (ly-set-mus-property! cm 'context-id (car rest))
182     )
183     cm
184   ))
186 (define (make-sequential-music elts)
187   (let*  ((m (ly-make-music "Sequential_music")))
188     (ly-set-mus-property! m 'elements elts)
189     m
190   ))
191 (define (make-simultaneous-music elts)
192   (let*  ((m (ly-make-music "Simultaneous_music")))
193     (ly-set-mus-property! m 'elements elts)
194     m
195     ))
196 (define (music-separator? m)
197   "Is M a separator."
198   (let* ((n (ly-get-mus-property m 'name )))
199     (and (symbol? n) (equal? 'separator n))
200   ))
202 (define (split-one sep?  l acc)
203   "Split off the first parts before separator and return both parts.
206   (if (null? l)
207       (cons acc '())
208       (if (sep? (car l))
209           (cons acc (cdr l))
210           (split-one sep? (cdr l) (cons (car l) acc))
211           )
212       ))
214 (define (split-list l sep?)
215   (if (null? l)
216       '()
217       (let* ((c (split-one sep? l '())))
218         (cons (reverse! (car c) '()) (split-list (cdr c) sep?))
219         )
220       )
221   )
223 ;; test code
224 ; (display (split-list '(a b c / d e f / g) (lambda (x) (equal? x '/))) )
227 ;;; splitting chords into voices.
229 (define (voicify-list lst number)
230    "Make a list of Musics.
232    voicify-list :: [ [Music ] ] -> number -> [Music]
233    LST is a list music-lists.
236    (if (null? lst) '()
237        (cons (context-spec-music
238               (make-sequential-music
239                (list
240                 (make-voice-props-set number)
241                 (make-simultaneous-music (car lst))))
243               "Voice"  (number->string number))
244               (voicify-list (cdr lst) (+ number 1))
245        ))
246    )
248 (define (voicify-chord ch)
249   "Split the parts of a chord into different Voices using separator"
250    (let* ((es (ly-get-mus-property ch 'elements)))
253      (ly-set-mus-property!  ch 'elements
254        (voicify-list (split-list es music-separator?) 0))
255      ch
256    ))
258 (define (voicify-music m)
259    "Recursively split chords that are separated with \\ "
260    
261    (if (not (music? m))
262        (begin (display m)
263        (error "not music!"))
264        )
265    (let*
266        ((es (ly-get-mus-property m 'elements))
267         (e (ly-get-mus-property m 'element))
268         )
269      (if (pair? es)
270          (ly-set-mus-property! m 'elements (map voicify-music es)))
271      (if (music? e)
272          (ly-set-mus-property! m 'element  (voicify-music e)))
273      (if
274       (and (equal? (ly-music-name m) "Simultaneous_music")
275            (reduce (lambda (x y ) (or x y))     (map music-separator? es)))
276       (voicify-chord m)
277       )
279      m
280      ))
284 ;;;;;;;;;;;;;;;;
285 ;;;;;;;;;;;;;;;;
287 (define (has-request-chord elts)
288   (reduce (lambda (x y) (or x y)) (map (lambda (x) (equal? (ly-music-name x)
289                                                            "Request_chord")) elts)
290   ))
292 (define (ly-music-message music msg)
293   (let* (
294       (ip (ly-get-mus-property music 'origin))
295       )
297     (if (ly-input-location? ip)
298         (ly-input-message ip msg)
299         (ly-warn msg))
300   ))
301   
302 (define (check-start-chords music)
303   "Check music expression for a Simultaneous_music containing notes\n(ie. Request_chords), without context specification. Called  from parser."
304   
305      (let*
306        ((es (ly-get-mus-property music 'elements))
307         (e (ly-get-mus-property music 'element))
308         (name (ly-music-name music)) 
309         )
311        (cond 
312          ((equal? name "Context_specced_music") #t)
313          ((equal? name "Simultaneous_music")
315           (if (has-request-chord es)
316               (ly-music-message music "Starting score with a chord.\nPlease insert an explicit \\context before chord")
317               (map check-start-chords es)))
318          
319          ((equal? name "Sequential_music")
320            (if (pair? es)
321                (check-start-chords (car es))))
322           (else (if (music? e) (check-start-chords e )))
323        
324        ))
325      music
326      )
329 (define toplevel-music-functions (list check-start-chords voicify-music))