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