]> git.donarmstrong.com Git - lilypond.git/blob - scm/define-music-display-methods.scm
Typeset single grob-property setting correctly as well.
[lilypond.git] / scm / define-music-display-methods.scm
1 ;;; define-music-display-methods.scm -- data for displaying music
2 ;;; expressions using LilyPond notation.
3 ;;;
4 ;;; (c) 2005--2006 Nicolas Sceaux  <nicolas.sceaux@free.fr>
5 ;;;
6
7 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
8 ;;;
9 ;;; Display method implementation
10 ;;;
11
12 (define-module (scm display-lily))
13
14 ;;;
15 ;;; Scheme forms
16 ;;;
17 (define (scheme-expr->lily-string scm-arg)
18   (cond ((or (number? scm-arg)
19              (string? scm-arg))
20          (format #f "~s" scm-arg))
21         ((or (symbol? scm-arg)
22              (list? scm-arg))
23          (format #f "'~s" scm-arg))
24         ((procedure? scm-arg)
25          (format #f "~a"
26                  (or (procedure-name scm-arg)
27                      (with-output-to-string
28                        (lambda ()
29                          (pretty-print (procedure-source scm-arg)))))))
30         (else
31          (format #f "~a"
32                  (with-output-to-string
33                    (lambda ()
34                      (display-scheme-music scm-arg)))))))
35 ;;;
36 ;;; Markups
37 ;;;
38
39 (define-public (markup->lily-string markup-expr)
40   "Return a string describing, in LilyPond syntax, the given markup expression."
41   (define (proc->command proc)
42     (let ((cmd-markup (symbol->string (procedure-name proc))))
43       (substring cmd-markup 0 (- (string-length cmd-markup)
44                                  (string-length "-markup")))))
45   (define (arg->string arg)
46     (cond ((string? arg)
47            (format #f "~s" arg))
48           ((markup? arg) ;; a markup
49            (markup->lily-string-aux arg))
50           ((and (pair? arg) (every markup? arg)) ;; a markup list
51            (format #f "{~{ ~a~}}" (map-in-order markup->lily-string-aux arg)))
52           (else          ;; a scheme argument
53            (format #f "#~a" (scheme-expr->lily-string arg)))))
54   (define (markup->lily-string-aux expr)
55     (if (string? expr)
56         (format #f "~s" expr)
57         (let ((cmd (car expr))
58               (args (cdr expr)))
59           (if (eqv? cmd simple-markup) ;; a simple markup
60               (format #f "~s" (car args))
61               (format #f "\\~a~{ ~a~}" 
62                       (proc->command cmd)
63                       (map-in-order arg->string args))))))
64   (cond ((string? markup-expr)
65          (format #f "~s" markup-expr))
66         ((eqv? (car markup-expr) simple-markup)
67          (format #f "~s" (second markup-expr)))
68         (else
69          (format #f "\\markup ~a"
70                  (markup->lily-string-aux markup-expr)))))
71
72 ;;;
73 ;;; pitch names
74 ;;;
75
76 ;; It is a pity that there is no rassoc in Scheme.
77 (define* (rassoc item alist #:optional (test equal?))
78   (do ((alist alist (cdr alist))
79        (result #f result))
80       ((or result (null? alist)) result)
81     (if (and (car alist) (test item (cdar alist)))
82         (set! result (car alist)))))
83
84 (define (note-name->lily-string ly-pitch parser)
85   ;; here we define a custom pitch= function, since we do not want to
86   ;; test whether octaves are also equal. (otherwise, we would be using equal?)
87   (define (pitch= pitch1 pitch2)
88     (and (= (ly:pitch-notename pitch1) (ly:pitch-notename pitch2))
89          (= (ly:pitch-alteration pitch1) (ly:pitch-alteration pitch2))))
90   (let ((result (rassoc ly-pitch (ly:parser-lookup parser 'pitchnames) pitch=)))
91     (if result
92         (car result)
93         #f)))
94
95 (define (octave->lily-string pitch)
96   (let ((octave (ly:pitch-octave pitch)))
97     (cond ((>= octave 0)
98            (make-string (1+ octave) #\'))
99           ((< octave -1)
100            (make-string (1- (* -1 octave)) #\,))
101           (else ""))))
102
103 ;;;
104 ;;; durations
105 ;;;
106 (define* (duration->lily-string ly-duration #:key (prev-duration (*previous-duration*))
107                         (force-duration (*force-duration*))
108                         (time-factor-numerator (*time-factor-numerator*))
109                         (time-factor-denominator (*time-factor-denominator*)))
110   (let ((log2    (ly:duration-log ly-duration))
111         (dots    (ly:duration-dot-count ly-duration))
112         (num+den (ly:duration-factor ly-duration)))
113     (if (or force-duration (not prev-duration) (not (equal? ly-duration prev-duration)))
114         (string-append (case log2
115                          ((-1) "\\breve")
116                          ((-2) "\\longa")
117                          ((-3) "\\maxima")
118                          (else (number->string (expt 2 log2))))
119                        (make-string dots #\.)
120                        (let ((num? (not (or (= 1 (car num+den))
121                                             (and time-factor-numerator
122                                                  (= (car num+den) time-factor-numerator)))))
123                              (den? (not (or (= 1 (cdr num+den))
124                                             (and time-factor-denominator
125                                                  (= (cdr num+den) time-factor-denominator))))))
126                          (cond (den?
127                                 (format #f "*~a/~a" (car num+den) (cdr num+den)))
128                                (num?
129                                 (format #f "*~a" (car num+den)))
130                                (else ""))))
131         "")))
132
133 ;;;
134 ;;; post events
135 ;;;
136
137 (define post-event? (make-music-type-predicate  
138                      'StringNumberEvent
139                      'ArticulationEvent
140                      'FingeringEvent
141                      'TextScriptEvent
142                      'MultiMeasureTextEvent
143                      'HyphenEvent
144                      'ExtenderEvent
145                      'BeamEvent
146                      'SlurEvent
147                      'TieEvent
148                      'CrescendoEvent
149                      'DecrescendoEvent
150                      'PhrasingSlurEvent
151                      'TremoloEvent
152                      'SustainEvent
153                      'SostenutoEvent
154                      'TextSpanEvent
155                      'HarmonicEvent
156                      'BeamForbidEvent
157                      'AbsoluteDynamicEvent
158                      'TrillSpanEvent
159                      'GlissandoEvent
160                      'ArpeggioEvent
161                      'NoteGroupingEvent
162                      'UnaCordaEvent))
163
164 (define* (event-direction->lily-string event #:optional (required #t))
165   (let ((direction (ly:music-property event 'direction)))
166     (cond ((or (not direction) (null? direction) (= CENTER direction))
167            (if required "-" ""))
168           ((= UP direction) "^")
169           ((= DOWN direction) "_")
170           (else ""))))
171
172 (define-macro (define-post-event-display-method type vars direction-required str)
173   `(define-display-method ,type ,vars
174      (format #f "~a~a"
175              (event-direction->lily-string ,(car vars) ,direction-required)
176              ,str)))
177
178 (define-macro (define-span-event-display-method type vars direction-required str-start str-stop)
179   `(define-display-method ,type ,vars
180      (format #f "~a~a"
181              (event-direction->lily-string ,(car vars) ,direction-required)
182              (if (= START (ly:music-property ,(car vars) 'span-direction))
183                  ,str-start
184                  ,str-stop))))
185
186 (define-display-method HyphenEvent (event parser)
187   " --")
188 (define-display-method ExtenderEvent (event parser)
189   " __")
190 (define-display-method TieEvent (event parser)
191   " ~")
192 (define-display-method BeamForbidEvent (event parser)
193   "\\noBeam")
194 (define-display-method StringNumberEvent (event parser)
195   (format #f "\\~a" (ly:music-property event 'string-number)))
196
197
198 (define-display-method TremoloEvent (event parser)
199   (let ((tremolo-type (ly:music-property event 'tremolo-type)))
200     (format #f ":~a" (if (= 0 tremolo-type)
201                          ""
202                          tremolo-type))))
203
204 (define-post-event-display-method ArticulationEvent (event parser) #t
205   (let ((articulation  (ly:music-property event 'articulation-type)))
206     (case (string->symbol articulation)
207       ((marcato) "^")
208       ((stopped) "+")
209       ((tenuto)  "-")
210       ((staccatissimo) "|")
211       ((accent) ">")
212       ((staccato) ".")
213       ((portato) "_")
214       (else (format #f "\\~a" articulation)))))
215
216 (define-post-event-display-method FingeringEvent (event parser) #t
217   (ly:music-property event 'digit))
218
219 (define-post-event-display-method TextScriptEvent (event parser) #t
220   (markup->lily-string (ly:music-property event 'text)))
221
222 (define-post-event-display-method MultiMeasureTextEvent (event parser) #t
223   (markup->lily-string (ly:music-property event 'text)))
224
225 (define-post-event-display-method HarmonicEvent (event parser) #t "\\harmonic")
226 (define-post-event-display-method GlissandoEvent (event parser) #t "\\glissando")
227 (define-post-event-display-method ArpeggioEvent (event parser) #t "\\arpeggio")
228 (define-post-event-display-method AbsoluteDynamicEvent (event parser) #f
229   (format #f "\\~a" (ly:music-property event 'text)))
230
231 (define-span-event-display-method BeamEvent (event parser) #f "[" "]")
232 (define-span-event-display-method SlurEvent (event parser) #f "(" ")")
233 (define-span-event-display-method CrescendoEvent (event parser) #f "\\<" "\\!")
234 (define-span-event-display-method DecrescendoEvent (event parser) #f "\\>" "\\!")
235 (define-span-event-display-method PhrasingSlurEvent (event parser) #f "\\(" "\\)")
236 (define-span-event-display-method SustainEvent (event parser) #f "\\sustainDown" "\\sustainUp")
237 (define-span-event-display-method SostenutoEvent (event parser) #f "\\sostenutoDown" "\\sostenutoUp")
238 (define-span-event-display-method TextSpanEvent (event parser) #f "\\startTextSpan" "\\stopTextSpan")
239 (define-span-event-display-method TrillSpanEvent (event parser) #f "\\startTrillSpan" "\\stopTrillSpan")
240 (define-span-event-display-method StaffSpanEvent (event parser) #f "\\startStaff" "\\stopStaff")
241 (define-span-event-display-method NoteGroupingEvent (event parser) #f "\\startGroup" "\\stopGroup")
242 (define-span-event-display-method UnaCordaEvent (event parser) #f "\\unaCorda" "\\treCorde")
243
244 ;;;
245 ;;; Graces
246 ;;;
247
248 (define-display-method GraceMusic (expr parser)
249   (format #f "\\grace ~a" 
250           (music->lily-string (ly:music-property expr 'element) parser)))
251
252 ;; \acciaccatura \appoggiatura \grace
253 ;; TODO: it would be better to compare ?start and ?stop
254 ;; with startAppoggiaturaMusic and stopAppoggiaturaMusic,
255 ;; using a custom music equality predicate.
256 (define-extra-display-method GraceMusic (expr parser)
257   "Display method for appoggiatura."
258   (with-music-match (expr (music
259                            'GraceMusic
260                            element (music
261                                     'SequentialMusic
262                                     elements (?start
263                                               ?music
264                                               ?stop))))
265     ;; we check whether ?start and ?stop look like
266     ;; startAppoggiaturaMusic stopAppoggiaturaMusic
267     (and (with-music-match (?start (music 
268                                     'SequentialMusic
269                                     elements ((music
270                                                'EventChord
271                                                elements ((music
272                                                           'SkipEvent
273                                                           duration (ly:make-duration 0 0 0 1))
274                                                          (music
275                                                           'SlurEvent
276                                                           span-direction START))))))
277                            #t)
278           (with-music-match (?stop (music 
279                                     'SequentialMusic
280                                     elements ((music
281                                                'EventChord
282                                                elements ((music
283                                                           'SkipEvent
284                                                           duration (ly:make-duration 0 0 0 1))
285                                                          (music
286                                                           'SlurEvent
287                                                           span-direction STOP))))))
288             (format #f "\\appoggiatura ~a" (music->lily-string ?music parser))))))
289
290
291 (define-extra-display-method GraceMusic (expr parser)
292   "Display method for acciaccatura."
293   (with-music-match (expr (music
294                            'GraceMusic
295                            element (music
296                                     'SequentialMusic
297                                     elements (?start
298                                               ?music
299                                               ?stop))))
300     ;; we check whether ?start and ?stop look like
301     ;; startAcciaccaturaMusic stopAcciaccaturaMusic
302     (and (with-music-match (?start (music 
303                                     'SequentialMusic
304                                     elements ((music
305                                                'EventChord
306                                                elements ((music
307                                                           'SkipEvent
308                                                           duration (ly:make-duration 0 0 0 1))
309                                                          (music
310                                                           'SlurEvent
311                                                           span-direction START)))
312                                               (music
313                                                'ContextSpeccedMusic
314                                                element (music
315                                                         'OverrideProperty
316                                                         grob-property-path '(stroke-style)
317                                                         grob-value "grace"
318                                                         symbol 'Stem)))))
319                            #t)
320          (with-music-match (?stop (music 
321                                    'SequentialMusic
322                                    elements ((music
323                                               'ContextSpeccedMusic
324                                               element (music
325                                                        'RevertProperty
326                                                        grob-property-path '(stroke-style)
327                                                        symbol 'Stem))
328                                              (music
329                                               'EventChord
330                                               elements ((music
331                                                          'SkipEvent
332                                                          duration (ly:make-duration 0 0 0 1))
333                                                         (music
334                                                          'SlurEvent
335                                                          span-direction STOP))))))
336            (format #f "\\acciaccatura ~a" (music->lily-string ?music parser))))))
337
338 (define-extra-display-method GraceMusic (expr parser)
339   "Display method for grace."
340   (with-music-match (expr (music
341                            'GraceMusic
342                            element (music
343                                     'SequentialMusic
344                                     elements (?start
345                                               ?music
346                                               ?stop))))
347     ;; we check whether ?start and ?stop look like
348     ;; startGraceMusic stopGraceMusic
349     (and (null? (ly:music-property ?start 'elements))
350          (null? (ly:music-property ?stop 'elements))
351          (format #f "\\grace ~a" (music->lily-string ?music parser)))))
352
353 ;;;
354 ;;; Music sequences
355 ;;;
356
357 (define-display-method SequentialMusic (seq parser)
358   (let ((force-line-break (and (*force-line-break*)
359                                ;; hm 
360                                (> (length (ly:music-property seq 'elements))
361                                   (*max-element-number-before-break*))))
362         (elements (ly:music-property seq 'elements))
363         (chord? (make-music-type-predicate 'EventChord))
364         (cluster? (make-music-type-predicate 'ClusterNoteEvent))
365         (note? (make-music-type-predicate 'NoteEvent)))
366     (format #f "~a~a{~v%~v_~{~a ~}~v%~v_}"
367             (if (any (lambda (e)
368                        (and (chord? e)
369                             (any cluster? (ly:music-property e 'elements))))
370                      elements)
371                 "\\makeClusters "
372                 "")
373             (if (*explicit-mode*)
374                 ;; if the sequence contains EventChord which contains figures ==> figuremode
375                 ;; if the sequence contains EventChord which contains lyrics ==> lyricmode
376                 ;; if the sequence contains EventChord which contains drum notes ==> drummode
377                 (cond ((any (lambda (chord)
378                               (any (make-music-type-predicate 'BassFigureEvent)
379                                    (ly:music-property chord 'elements)))
380                             (filter chord? elements))
381                        "\\figuremode ")
382                       ((any (lambda (chord)
383                               (any (make-music-type-predicate 'LyricEvent)
384                                    (ly:music-property chord 'elements)))
385                             (filter chord? elements))
386                        "\\lyricmode ")
387                       ((any (lambda (chord)
388                               (any (lambda (event)
389                                      (and (note? event)
390                                           (not (null? (ly:music-property event 'drum-type)))))
391                                    (ly:music-property chord 'elements)))
392                             (filter chord? elements))
393                        "\\drummode ")
394                       (else ;; TODO: other modes?
395                        ""))
396                 "")
397             (if force-line-break 1 0)
398             (if force-line-break (+ 2 (*indent*)) 1)
399             (parameterize ((*indent* (+ 2 (*indent*))))
400                           (map-in-order (lambda (music)
401                                           (music->lily-string music parser))
402                                         elements))
403             (if force-line-break 1 0)
404             (if force-line-break (*indent*) 0))))
405
406 (define-display-method SimultaneousMusic (sim parser)
407   (parameterize ((*indent* (+ 3 (*indent*))))
408     (format #f "<< ~{~a ~}>>"
409             (map-in-order (lambda (music)
410                             (music->lily-string music parser))
411                           (ly:music-property sim 'elements)))))
412
413 (define-extra-display-method SimultaneousMusic (expr parser)
414   "If `sim' is an \afterGrace expression, return \"\\afterGrace ...\".
415 Otherwise, return #f."
416   ;; TODO: do something with afterGraceFraction?
417   (with-music-match (expr (music 'SimultaneousMusic
418                                  elements (?before-grace
419                                            (music 'SequentialMusic
420                                                   elements ((music 'SkipMusic)
421                                                             (music 'GraceMusic
422                                                                    element ?grace))))))
423     (format #f "\\afterGrace ~a ~a"
424             (music->lily-string ?before-grace parser)
425             (music->lily-string ?grace parser))))
426   
427 ;;;
428 ;;; Chords
429 ;;;
430
431 (define-display-method EventChord (chord parser)
432   ;; event_chord : simple_element post_events
433   ;;               | command_element
434   ;;               | note_chord_element
435
436   ;; TODO : tagged post_events
437   ;; post_events : ( post_event | tagged_post_event )*
438   ;; tagged_post_event: '-' \tag embedded_scm post_event
439
440   (let* ((elements (ly:music-property chord 'elements))
441          (simple-elements (filter (make-music-type-predicate 
442                                    'NoteEvent 'ClusterNoteEvent 'RestEvent
443                                    'MultiMeasureRestEvent 'SkipEvent 'LyricEvent)
444                                   elements)))
445     (if ((make-music-type-predicate 'StaffSpanEvent 'BreathingEvent) (car elements))
446         ;; first, a special case: StaffSpanEvent (\startStaff, \stopStaff)
447         ;; and BreathingEvent (\breathe)
448         (music->lily-string (car elements) parser)
449         (if (and (not (null? simple-elements))
450                  (null? (cdr simple-elements)))
451             ;; simple_element : note | figure | rest | mmrest | lyric_element | skip
452             (let* ((simple-element (car simple-elements))
453                    (duration (ly:music-property simple-element 'duration))
454                    (lily-string (format #f "~a~a~a~{~a ~}"
455                                         (music->lily-string simple-element parser)
456                                         (duration->lily-string duration)
457                                         (if (and ((make-music-type-predicate 'RestEvent) simple-element)
458                                                  (ly:pitch? (ly:music-property simple-element 'pitch)))
459                                             "\\rest"
460                                             "")
461                                         (map-in-order (lambda (music)
462                                                         (music->lily-string music parser))
463                                                       (filter post-event? elements)))))
464               (*previous-duration* duration)
465               lily-string)
466             (let ((chord-elements (filter (make-music-type-predicate
467                                            'NoteEvent 'ClusterNoteEvent 'BassFigureEvent)
468                                           elements))
469                   (post-events (filter post-event? elements)))
470               (if (not (null? chord-elements))
471                   ;; note_chord_element : '<' (notepitch | drumpitch)* '>" duration post_events
472                   (let ((lily-string (format #f "< ~{~a ~}>~a~{~a ~}"
473                                              (map-in-order (lambda (music)
474                                                              (music->lily-string music parser))
475                                                            chord-elements)
476                                              (duration->lily-string (ly:music-property (car chord-elements)
477                                                                                      'duration))
478                                              (map-in-order (lambda (music)
479                                                              (music->lily-string music parser))
480                                                            post-events))))
481                     (*previous-duration* (ly:music-property (car chord-elements) 'duration))
482                     lily-string)
483                   ;; command_element
484                   (format #f "~{~a ~}" (map-in-order (lambda (music)
485                                                        (music->lily-string music parser))
486                                                      elements))))))))
487
488 (define-display-method MultiMeasureRestMusic (mmrest parser)
489   (let* ((dur (ly:music-property mmrest 'duration))
490          (ly (format #f "R~a~{~a ~}"
491                      (duration->lily-string dur)
492                      (map-in-order (lambda (music)
493                                      (music->lily-string music parser))
494                                    (ly:music-property mmrest 'articulations)))))
495     (*previous-duration* dur)
496     ly))
497
498 (define-display-method SkipMusic (skip parser)
499   (format #f "\\skip ~a" (duration->lily-string (ly:music-property skip 'duration) #:force-duration #t)))
500
501 ;;;
502 ;;; Notes, rests, skips...
503 ;;;
504
505 (define (simple-note->lily-string event parser)
506   (format #f "~a~a~a~a~{~a~}" ; pitchname octave !? octave-check articulations
507           (note-name->lily-string (ly:music-property event 'pitch) parser)
508           (octave->lily-string (ly:music-property event 'pitch))
509           (let ((forced (ly:music-property event 'force-accidental))
510                 (cautionary (ly:music-property event 'cautionary)))
511             (cond ((and (not (null? forced))
512                         forced
513                         (not (null? cautionary))
514                         cautionary)
515                    "?")
516                   ((and (not (null? forced)) forced) "!")
517                   (else "")))
518           (let ((octave-check (ly:music-property event 'absolute-octave)))
519             (if (not (null? octave-check))
520                 (format #f "=~a" (cond ((>= octave-check 0)
521                                         (make-string (1+ octave-check) #\'))
522                                        ((< octave-check -1)
523                                         (make-string (1- (* -1 octave-check)) #\,))
524                                        (else "")))
525                 ""))
526           (map-in-order (lambda (event)
527                           (music->lily-string event parser))
528                         (ly:music-property event 'articulations))))
529
530 (define-display-method NoteEvent (note parser)
531   (cond ((not (null? (ly:music-property note 'pitch))) ;; note
532          (simple-note->lily-string note parser))
533         ((not (null? (ly:music-property note 'drum-type))) ;; drum
534          (format #f "~a" (ly:music-property note 'drum-type)))
535         (else ;; unknown?
536          "")))
537
538 (define-display-method ClusterNoteEvent (note parser)
539   (simple-note->lily-string note parser))
540
541 (define-display-method RestEvent (rest parser)
542   (if (not (null? (ly:music-property rest 'pitch)))
543       (simple-note->lily-string rest parser)
544       "r"))
545
546 (define-display-method MultiMeasureRestEvent (rest parser)
547   "R")
548
549 (define-display-method SkipEvent (rest parser)
550   "s")
551
552 (define-display-method MarkEvent (mark parser)
553   (let ((label (ly:music-property mark 'label)))
554     (if (null? label)
555         "\\mark \\default"
556         (format #f "\\mark ~a" (markup->lily-string label)))))
557
558 (define-display-method KeyChangeEvent (key parser)
559   (let ((pitch-alist (ly:music-property key 'pitch-alist))
560         (tonic (ly:music-property key 'tonic)))
561     (if (or (null? pitch-alist)
562             (null? tonic))
563         "\\key \\default"
564         (let ((c-pitch-alist (ly:transpose-key-alist pitch-alist 
565                                                      (ly:pitch-diff (ly:make-pitch 0 0 0) tonic))))
566           (format #f "\\key ~a \\~a~a"
567                   (note-name->lily-string (ly:music-property key 'tonic) parser)
568                   (any (lambda (mode)
569                          (if (and parser
570                                   (equal? (ly:parser-lookup parser mode) c-pitch-alist))
571                              (symbol->string mode)
572                              #f))
573                        '(major minor ionian locrian aeolian mixolydian lydian phrygian dorian))
574                   (new-line->lily-string))))))
575
576 (define-display-method RelativeOctaveCheck (octave parser)
577   (let ((pitch (ly:music-property octave 'pitch)))
578     (format #f "\\octave ~a~a"
579             (note-name->lily-string pitch parser)
580             (octave->lily-string pitch))))
581
582 (define-display-method VoiceSeparator (sep parser)
583   "\\\\")
584
585 (define-display-method LigatureEvent (ligature parser)
586   (if (= START (ly:music-property ligature 'span-direction))
587       "\\["
588       "\\]"))
589
590 (define-display-method BarCheck (check parser)
591   (format #f "|~a" (new-line->lily-string)))
592
593 (define-display-method PesOrFlexaEvent (expr parser)
594   "\\~")
595
596 (define-display-method BassFigureEvent (figure parser)
597   (let ((alteration (ly:music-property figure 'alteration))
598         (fig (ly:music-property figure 'figure))
599         (bracket-start (ly:music-property figure 'bracket-start))
600         (bracket-stop (ly:music-property figure 'bracket-stop)))
601     (format #f "~a~a~a~a"
602             (if (null? bracket-start) "" "[")
603             (cond ((null? fig) "_")
604                   ((markup? fig) (second fig)) ;; fig: (<number-markup> "number")
605                   (else fig))
606             (if (null? alteration)
607                 ""
608                 (case alteration
609                   ((-4) "--")
610                   ((-2) "-")
611                   ((0) "!")
612                   ((2) "+")
613                   ((4) "++")
614                   (else "")))
615             (if (null? bracket-stop) "" "]"))))
616
617 (define-display-method LyricEvent (lyric parser)
618   (let ((text (ly:music-property lyric 'text)))
619     (if (or (string? text)
620             (eqv? (first text) simple-markup))
621         ;; a string or a simple markup
622         (let ((string (if (string? text)
623                           text
624                           (second text))))
625           (if (string-match "(\"| |[0-9])" string)
626               ;; TODO check exactly in which cases double quotes should be used
627               (format #f "~s" string)
628               string))
629         (markup->lily-string text))))
630
631 (define-display-method BreathingEvent (event parser)
632   "\\breathe")
633
634 ;;;
635 ;;; Staff switches
636 ;;;
637
638 (define-display-method AutoChangeMusic (m parser)
639   (format #f "\\autochange ~a"
640           (music->lily-string (ly:music-property m 'element) parser)))
641
642 (define-display-method ContextChange (m parser)
643   (format #f "\\change ~a = \"~a\""
644           (ly:music-property m 'change-to-type)
645           (ly:music-property m 'change-to-id)))
646
647 ;;;
648
649 (define-display-method TimeScaledMusic (times parser)
650   (let* ((num (ly:music-property times 'numerator))
651          (den (ly:music-property times 'denominator))
652          (nd-gcd (gcd num den)))
653     (parameterize ((*force-line-break* #f)
654                    (*time-factor-numerator* (/ num nd-gcd))
655                    (*time-factor-denominator* (/ den nd-gcd)))
656       (format #f "\\times ~a/~a ~a" 
657               num
658               den
659               (music->lily-string (ly:music-property times 'element) parser)))))
660
661 (define-display-method RelativeOctaveMusic (m parser)
662   (music->lily-string (ly:music-property m 'element) parser))
663
664 (define-display-method TransposedMusic (m parser)
665   (music->lily-string (ly:music-property m 'element) parser))
666
667 ;;;
668 ;;; Repeats
669 ;;;
670
671 (define (repeat->lily-string expr repeat-type parser)
672   (format #f "\\repeat ~a ~a ~a ~a"
673           repeat-type
674           (ly:music-property expr 'repeat-count)
675           (music->lily-string (ly:music-property expr 'element) parser)
676           (let ((alternatives (ly:music-property expr 'elements)))
677             (if (null? alternatives)
678                 ""
679                 (format #f "\\alternative { ~{~a ~}}"
680                         (map-in-order (lambda (music)
681                                         (music->lily-string music parser))
682                                       alternatives))))))
683
684 (define-display-method VoltaRepeatedMusic (expr parser)
685   (repeat->lily-string expr "volta" parser))
686
687 (define-display-method UnfoldedRepeatedMusic (expr parser)
688   (repeat->lily-string expr "unfold" parser))
689
690 (define-display-method FoldedRepeatedMusic (expr parser)
691   (repeat->lily-string expr "fold" parser))
692
693 (define-display-method PercentRepeatedMusic (expr parser)
694   (repeat->lily-string expr "percent" parser))
695
696 (define-display-method TremoloRepeatedMusic (expr parser)
697   (let* ((count (ly:music-property expr 'repeat-count))
698          (dots (if (= 0 (modulo count 3)) 0 1))
699          (shift (- (log2 (if (= 0 dots)
700                              (/ (* count 2) 3)
701                              count))))
702          (element (ly:music-property expr 'element))
703          (den-mult 1))
704     (if (eqv? (ly:music-property element 'name) 'SequentialMusic)
705         (begin
706           (set! shift (1- shift))
707           (set! den-mult (length (ly:music-property element 'elements)))))
708     (music-map (lambda (m)
709                  (let ((duration (ly:music-property m 'duration)))
710                    (if (ly:duration? duration)
711                        (let* ((dlog (ly:duration-log duration))
712                               (ddots (ly:duration-dot-count duration))
713                               (dfactor (ly:duration-factor duration))
714                               (dnum (car dfactor))
715                               (dden (cdr dfactor)))
716                          (set! (ly:music-property m 'duration)
717                                (ly:make-duration (- dlog shift)
718                                                  ddots ;;(- ddots dots) ; ????
719                                                  dnum
720                                                  (/ dden den-mult))))))
721                  m)
722                element)
723     (format #f "\\repeat tremolo ~a ~a"
724             count
725             (music->lily-string element parser))))
726
727 ;;;
728 ;;; Contexts
729 ;;; 
730
731 (define-display-method ContextSpeccedMusic (expr parser)
732   (let ((id    (ly:music-property expr 'context-id))
733         (create-new (ly:music-property expr 'create-new))
734         (music (ly:music-property expr 'element))
735         (operations (ly:music-property expr 'property-operations))
736         (ctype (ly:music-property expr 'context-type)))
737     (format #f "~a ~a~a~a ~a"
738             (if (and (not (null? create-new)) create-new)
739                 "\\new"
740                 "\\context")
741             ctype
742             (if (null? id)
743                 ""
744                 (format #f " = ~s" id))
745             (if (null? operations)
746                 "" 
747                 (format #f " \\with {~{~a~}~%~v_}" 
748                         (parameterize ((*indent* (+ (*indent*) 2)))
749                           (map (lambda (op)
750                                  (format #f "~%~v_\\~a ~s"
751                                          (*indent*)
752                                          (first op)
753                                          (second op)))
754                                (reverse operations)))
755                         (*indent*)))
756             (parameterize ((*current-context* ctype))
757               (music->lily-string music parser)))))
758
759 ;; special cases: \figures \lyrics \drums
760 (define-extra-display-method ContextSpeccedMusic (expr parser)
761   (with-music-match (expr (music 'ContextSpeccedMusic
762                                  create-new #t
763                                  property-operations ?op
764                                  context-type ?context-type
765                                  element ?sequence))
766     (if (null? ?op)
767         (parameterize ((*explicit-mode* #f))
768           (case ?context-type
769             ((FiguredBass)
770              (format #f "\\figures ~a" (music->lily-string ?sequence parser)))
771             ((Lyrics)
772              (format #f "\\lyrics ~a" (music->lily-string ?sequence parser)))
773             ((DrumStaff)
774              (format #f "\\drums ~a" (music->lily-string ?sequence parser)))
775             (else
776              #f)))
777         #f)))
778
779 ;;; Context properties
780
781 (define-extra-display-method ContextSpeccedMusic (expr parser)
782   (let ((element (ly:music-property expr 'element))
783         (property-tuning? (make-music-type-predicate 'PropertySet
784                                                      'PropertyUnset
785                                                      'OverrideProperty
786                                                      'RevertProperty))
787         (sequence? (make-music-type-predicate 'SequentialMusic)))
788     (if (and (ly:music? element)
789              (or (property-tuning? element)
790                  (and (sequence? element)
791                       (every property-tuning? (ly:music-property element 'elements)))))
792         (parameterize ((*current-context* (ly:music-property expr 'context-type)))
793           (music->lily-string element parser))
794         #f)))
795
796 (define (property-value->lily-string arg parser)
797   (cond ((ly:music? arg)
798          (music->lily-string arg parser))
799         ((string? arg)
800          (format #f "#~s" arg))
801         ((markup? arg)
802          (markup->lily-string arg))
803         (else
804          (format #f "#~a" (scheme-expr->lily-string arg)))))
805
806 (define-display-method PropertySet (expr parser)
807   (let ((property (ly:music-property expr 'symbol))
808         (value (ly:music-property expr 'value))
809         (once (ly:music-property expr 'once)))
810     (format #f "~a\\set ~a~a = ~a~a"
811             (if (and (not (null? once)))
812                 "\\once "
813                 "")
814             (if (eqv? (*current-context*) 'Bottom) 
815                 "" 
816                 (format #f "~a . " (*current-context*)))
817             property
818             (property-value->lily-string value parser)
819             (new-line->lily-string))))
820
821 (define-display-method PropertyUnset (expr parser)
822   (format #f "\\unset ~a~a~a"
823           (if (eqv? (*current-context*) 'Bottom) 
824               "" 
825               (format #f "~a . " (*current-context*)))
826           (ly:music-property expr 'symbol)
827           (new-line->lily-string)))
828
829 ;;; Layout properties
830
831 (define-display-method OverrideProperty (expr parser)
832   (let* ((symbol          (ly:music-property expr 'symbol))
833          (property-path   (ly:music-property expr 'grob-property-path))
834          (properties      (if (pair? property-path)
835                               property-path
836                               (list (ly:music-property expr 'grob-property))))
837          (value   (ly:music-property expr 'grob-value))
838          (once    (ly:music-property expr 'once)))
839
840     (format #f "~a\\override ~a~a #'~a = ~a~a"
841             (if (or (null? once)
842                     (not once))
843                 ""
844                 "\\once ")
845             (if (eqv? (*current-context*) 'Bottom) 
846                 "" 
847                 (format #f "~a . " (*current-context*)))
848             symbol
849             (if (null? (cdr properties))
850                 (car properties)
851                 properties)
852             (property-value->lily-string value parser)
853             (new-line->lily-string))))
854             
855 (define-display-method RevertProperty (expr parser)
856   (let ((symbol (ly:music-property expr 'symbol))
857         (properties (ly:music-property expr 'grob-property-path)))
858     (format #f "\\revert ~a~a #'~a~a"
859             (if (eqv? (*current-context*) 'Bottom) 
860                 "" 
861                 (format #f "~a . " (*current-context*)))
862             symbol
863             (if (null? (cdr properties))
864                 (car properties)
865                 properties)
866             (new-line->lily-string))))
867
868 ;;; \melisma and \melismaEnd
869 (define-extra-display-method ContextSpeccedMusic (expr parser)
870   "If expr is a melisma, return \"\\melisma\", otherwise, return #f."
871   (with-music-match (expr (music 'ContextSpeccedMusic
872                                  element (music 'PropertySet
873                                                 value #t
874                                                 symbol 'melismaBusy)))
875     "\\melisma"))
876
877 (define-extra-display-method ContextSpeccedMusic (expr parser)
878   "If expr is a melisma end, return \"\\melismaEnd\", otherwise, return #f."
879   (with-music-match (expr (music 'ContextSpeccedMusic
880                                  element (music 'PropertyUnset
881                                                 symbol 'melismaBusy)))
882     "\\melismaEnd"))
883
884 ;;; \tempo
885 (define-extra-display-method ContextSpeccedMusic (expr parser)
886   "If expr is a tempo, return \"\\tempo x = nnn\", otherwise return #f."
887   (with-music-match (expr (music 'ContextSpeccedMusic
888                             element (music 'SequentialMusic
889                                       elements ((music 'PropertySet
890                                                   symbol 'tempoWholesPerMinute)
891                                                 (music 'PropertySet
892                                                   value ?unit-duration
893                                                   symbol 'tempoUnitDuration)
894                                                 (music 'PropertySet
895                                                   value ?unit-count
896                                                   symbol 'tempoUnitCount)))))
897     (format #f "\\tempo ~a = ~a"
898             (duration->lily-string ?unit-duration #:force-duration #t)
899             ?unit-count)))
900
901 ;;; \clef 
902 (define clef-name-alist (map (lambda (name+vals)
903                                (cons (cdr name+vals)
904                                      (car name+vals)))
905                              supported-clefs))
906
907 (define-extra-display-method ContextSpeccedMusic (expr parser)
908   "If `expr' is a clef change, return \"\\clef ...\"
909 Otherwise, return #f."
910   (with-music-match (expr (music 'ContextSpeccedMusic
911                                  context-type 'Staff
912                                  element (music 'SequentialMusic
913                                                 elements ((music 'PropertySet
914                                                                  value ?clef-glyph
915                                                                  symbol 'clefGlyph)
916                                                           (music 'PropertySet
917                                                                  symbol 'middleCPosition)
918                                                           (music 'PropertySet
919                                                                  value ?clef-position
920                                                                  symbol 'clefPosition)
921                                                           (music 'PropertySet
922                                                                  value ?clef-octavation
923                                                                  symbol 'clefOctavation)))))
924     (let ((clef-prop+name (assoc (list ?clef-glyph ?clef-position 0)
925                                  clef-name-alist)))
926       (if clef-prop+name
927           (format #f "\\clef \"~a~{~a~a~}\"~a"
928                   (cdr clef-prop+name)
929                   (cond ((= 0 ?clef-octavation)
930                          (list "" ""))
931                         ((> ?clef-octavation 0)
932                          (list "^" (1+ ?clef-octavation)))
933                         (else
934                          (list "_" (- 1 ?clef-octavation))))
935                   (new-line->lily-string))
936           #f))))
937
938 ;;; \time
939 (define-extra-display-method ContextSpeccedMusic (expr parser)
940   "If `expr' is a time signature set, return \"\\time ...\".
941 Otherwise, return #f."
942   (with-music-match (expr (music 
943                            'ContextSpeccedMusic
944                            element (music 
945                                     'ContextSpeccedMusic
946                                     context-type 'Timing
947                                     element (music 
948                                              'SequentialMusic
949                                              elements ((music 
950                                                         'PropertySet
951                                                         value ?num+den
952                                                         symbol 'timeSignatureFraction)
953                                                        (music
954                                                         'PropertySet
955                                                         symbol 'beatLength)
956                                                        (music
957                                                         'PropertySet
958                                                         symbol 'measureLength)
959                                                        (music
960                                                         'PropertySet
961                                                         value ?grouping
962                                                         symbol 'beatGrouping))))))
963     (if (null? ?grouping)
964         (format #f "\\time ~a/~a~a" (car ?num+den) (cdr ?num+den) (new-line->lily-string))
965         (format #f "#(set-time-signature ~a ~a '~s)~a"
966                 (car ?num+den) (cdr ?num+den) ?grouping (new-line->lily-string)))))
967
968 ;;; \bar
969 (define-extra-display-method ContextSpeccedMusic (expr parser)
970   "If `expr' is a bar, return \"\\bar ...\".
971 Otherwise, return #f."
972   (with-music-match (expr (music 'ContextSpeccedMusic
973                                  context-type 'Timing
974                                  element (music 'PropertySet
975                                                 value ?bar-type
976                                                 symbol 'whichBar)))
977      (format #f "\\bar \"~a\"~a" ?bar-type (new-line->lily-string))))
978
979 ;;; \partial
980 (define (duration->moment ly-duration)
981   (let ((log2    (ly:duration-log ly-duration))
982         (dots    (ly:duration-dot-count ly-duration))
983         (num+den (ly:duration-factor ly-duration)))
984     (let* ((m (expt 2 (- log2)))
985            (factor (/ (car num+den) (cdr num+den))))
986       (/ (do ((i 0 (1+ i))
987               (delta (/ m 2) (/ delta 2)))
988              ((= i dots) m)
989            (set! m (+ m delta)))
990          factor))))
991 (define moment-duration-alist (map (lambda (duration)
992                                      (cons (duration->moment duration)
993                                            duration))
994                                    (append-map (lambda (log2)
995                                                  (map (lambda (dots)
996                                                         (ly:make-duration log2 dots 1 1))
997                                                       (list 0 1 2 3)))
998                                                (list 0 1 2 3 4))))
999
1000 (define (moment->duration moment)
1001   (let ((result (assoc (- moment) moment-duration-alist =)))
1002     (and result 
1003          (cdr result))))
1004
1005 (define-extra-display-method ContextSpeccedMusic (expr parser)
1006   "If `expr' is a partial measure, return \"\\partial ...\".
1007 Otherwise, return #f."
1008   (with-music-match (expr (music
1009                            'ContextSpeccedMusic
1010                            element (music
1011                                     'ContextSpeccedMusic
1012                                     context-type 'Timing
1013                                     element (music
1014                                              'PropertySet
1015                                              value ?moment
1016                                              symbol 'measurePosition))))
1017      (let ((duration (moment->duration (/ (ly:moment-main-numerator ?moment)
1018                                           (ly:moment-main-denominator ?moment)))))
1019        (and duration (format #f "\\partial ~a" (duration->lily-string duration
1020                                                  #:force-duration #t))))))
1021
1022 ;;;
1023 ;;;
1024
1025 (define-display-method ApplyOutputEvent (applyoutput parser)
1026   (let ((proc (ly:music-property applyoutput 'procedure))
1027         (ctx  (ly:music-property applyoutput 'context-type)))
1028     (format #f "\\applyOutput #'~a #~a"
1029             ctx
1030             (or (procedure-name proc)
1031                 (with-output-to-string
1032                   (lambda ()
1033                     (pretty-print (procedure-source proc))))))))
1034
1035 (define-display-method ApplyContext (applycontext parser)
1036   (let ((proc (ly:music-property applycontext 'procedure)))
1037     (format #f "\\applyContext #~a"
1038             (or (procedure-name proc)
1039                 (with-output-to-string
1040                   (lambda ()
1041                     (pretty-print (procedure-source proc))))))))
1042
1043 ;;; \partcombine
1044 (define-display-method PartCombineMusic (expr parser)
1045   (format #f "\\partcombine ~{~a ~}"
1046           (map-in-order (lambda (music)
1047                           (music->lily-string music parser))
1048                         (ly:music-property expr 'elements))))
1049
1050 (define-extra-display-method PartCombineMusic (expr parser)
1051   (with-music-match (expr (music 'PartCombineMusic
1052                                  elements ((music 'UnrelativableMusic
1053                                                   element (music 'ContextSpeccedMusic
1054                                                                  context-id "one"
1055                                                                  context-type 'Voice
1056                                                                  element ?sequence1))
1057                                            (music 'UnrelativableMusic
1058                                                   element (music 'ContextSpeccedMusic
1059                                                                  context-id "two"
1060                                                                  context-type 'Voice
1061                                                                  element ?sequence2)))))
1062     (format #f "\\partcombine ~a~a~a"
1063             (music->lily-string ?sequence1 parser)
1064             (new-line->lily-string)
1065             (music->lily-string ?sequence2 parser))))
1066
1067 (define-display-method UnrelativableMusic (expr parser)
1068   (music->lily-string (ly:music-property expr 'element) parser))
1069
1070 ;;; Cue notes
1071 (define-display-method QuoteMusic (expr parser)
1072   (or (with-music-match (expr (music
1073                                'QuoteMusic
1074                                quoted-voice-direction ?quoted-voice-direction
1075                                quoted-music-name ?quoted-music-name
1076                                quoted-context-id "cue"
1077                                quoted-context-type 'Voice
1078                                element ?music))
1079         (format #f "\\cueDuring #~s #~a ~a"
1080                 ?quoted-music-name
1081                 ?quoted-voice-direction
1082                 (music->lily-string ?music parser)))
1083       (format #f "\\quoteDuring #~s ~a"
1084               (ly:music-property expr 'quoted-music-name)
1085               (music->lily-string (ly:music-property expr 'element) parser))))
1086
1087 ;;;
1088 ;;; Breaks
1089 ;;;
1090 (define-display-method LineBreakEvent (expr parser)
1091   (if (null? (ly:music-property expr 'break-permission))
1092       "\\noBreak"
1093       "\\break"))
1094
1095 (define-display-method PageBreakEvent (expr parser)
1096   (if (null? (ly:music-property expr 'break-permission))
1097       "\\noPageBreak"
1098       "\\pageBreak"))
1099
1100 (define-display-method PageTurnEvent (expr parser)
1101   (if (null? (ly:music-property expr 'break-permission))
1102       "\\noPageTurn"
1103       "\\pageTurn"))
1104
1105 (define-extra-display-method EventChord (expr parser)
1106   (with-music-match (expr (music 'EventChord
1107                             elements ((music 'LineBreakEvent
1108                                              break-permission 'force)
1109                                       (music 'PageBreakEvent
1110                                              break-permission 'force))))
1111     "\\pageBreak"))
1112
1113 (define-extra-display-method EventChord (expr parser)
1114   (with-music-match (expr (music 'EventChord
1115                             elements ((music 'LineBreakEvent
1116                                              break-permission 'force)
1117                                       (music 'PageBreakEvent
1118                                              break-permission 'force)
1119                                       (music 'PageTurnEvent
1120                                              break-permission 'force))))
1121     "\\pageTurn"))
1122
1123 ;;;
1124 ;;; Lyrics
1125 ;;;
1126
1127 ;;; \lyricsto
1128 (define-display-method LyricCombineMusic (expr parser)
1129   (format #f "\\lyricsto ~s ~a"
1130           (ly:music-property expr 'associated-context)
1131           (parameterize ((*explicit-mode* #f))
1132             (music->lily-string (ly:music-property expr 'element) parser))))
1133
1134 ;; \addlyrics
1135 (define-extra-display-method SimultaneousMusic (expr parser)
1136   (with-music-match (expr (music 'SimultaneousMusic
1137                                  elements ((music 'ContextSpeccedMusic
1138                                                   context-id ?id
1139                                                   context-type 'Voice
1140                                                   element ?note-sequence)
1141                                            (music 'ContextSpeccedMusic
1142                                                   context-type 'Lyrics
1143                                                   create-new #t
1144                                                   element (music 'LyricCombineMusic
1145                                                                  associated-context ?associated-id
1146                                                                  element ?lyric-sequence)))))
1147     (if (string=? ?id ?associated-id)
1148         (format #f "~a~a \\addlyrics ~a"
1149                 (music->lily-string ?note-sequence parser)
1150                 (new-line->lily-string)
1151                 (parameterize ((*explicit-mode* #f))
1152                   (music->lily-string ?lyric-sequence parser)))
1153         #f)))
1154
1155