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