]> git.donarmstrong.com Git - lilypond.git/blob - scm/define-music-display-methods.scm
782552dfeb80f2f62a10abc185440b8371776c1f
[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 (append (ly:music-property chord 'elements)
430                            (ly:music-property chord 'articulations)))
431          (chord-repeat (ly:music-property chord 'duration)))
432     (call-with-values
433         (lambda ()
434           (partition (lambda (m) (music-is-of-type? m 'rhythmic-event))
435                      elements))
436       (lambda (chord-elements other-elements)
437         (cond ((pair? chord-elements)
438                ;; note_chord_element :
439                ;; '<' (notepitch | drumpitch)* '>" duration post_events
440                (let ((duration (duration->lily-string (ly:music-property
441                                                        (car chord-elements)
442                                                        'duration)
443                                                       #:remember #t)))
444                  ;; Format duration first so that it does not appear on
445                  ;; chord elements
446                  (format #f "< ~{~a ~}>~a~:{~:[-~;~]~a~^ ~}"
447                          (map-in-order (lambda (music)
448                                          (music->lily-string music parser))
449                                        chord-elements)
450                          duration
451                          (map-in-order (lambda (music)
452                                          (list
453                                           (post-event? music)
454                                           (music->lily-string music parser)))
455                                        other-elements))))
456               ((ly:duration? chord-repeat)
457                (let ((duration (duration->lily-string chord-repeat
458                                                       #:remember #t)))
459                  (format #f "q~a~:{~:[-~;~]~a~^ ~}"
460                          duration
461                          (map-in-order (lambda (music)
462                                          (list
463                                           (post-event? music)
464                                           (music->lily-string music parser)))
465                                        other-elements))))
466
467               ((and (= 1 (length other-elements))
468                     (not (post-event? (car other-elements))))
469                (format #f (music->lily-string (car other-elements) parser)))
470               (else
471                (format #f "< >~:{~:[-~;~]~a~^ ~}"
472                        (map-in-order (lambda (music)
473                                        (list
474                                         (post-event? music)
475                                         (music->lily-string music parser)))
476                                      other-elements))))))))
477
478 (define-display-method MultiMeasureRestMusic (mmrest parser)
479   (format #f "R~a~{~a~^ ~}"
480           (duration->lily-string (ly:music-property mmrest 'duration)
481                                  #:remember #t)
482           (map-in-order (lambda (music)
483                           (music->lily-string music parser))
484                         (ly:music-property mmrest 'articulations))))
485
486 (define-display-method SkipMusic (skip parser)
487   (format #f "\\skip ~a" (duration->lily-string (ly:music-property skip 'duration) #:force-duration #t)))
488
489 (define-display-method OttavaMusic (ottava parser)
490   (format #f "\\ottava #~a" (ly:music-property ottava 'ottava-number)))
491
492 ;;;
493 ;;; Notes, rests, skips...
494 ;;;
495
496 (define (simple-note->lily-string event parser)
497   (format #f "~a~a~a~a~a~a~{~a~}" ; pitchname octave !? octave-check duration optional_rest articulations
498           (note-name->lily-string (ly:music-property event 'pitch) parser)
499           (octave->lily-string (ly:music-property event 'pitch))
500           (let ((forced (ly:music-property event 'force-accidental))
501                 (cautionary (ly:music-property event 'cautionary)))
502             (cond ((and (not (null? forced))
503                         forced
504                         (not (null? cautionary))
505                         cautionary)
506                    "?")
507                   ((and (not (null? forced)) forced) "!")
508                   (else "")))
509           (let ((octave-check (ly:music-property event 'absolute-octave)))
510             (if (not (null? octave-check))
511                 (format #f "=~a" (cond ((>= octave-check 0)
512                                         (make-string (1+ octave-check) #\'))
513                                        ((< octave-check -1)
514                                         (make-string (1- (* -1 octave-check)) #\,))
515                                        (else "")))
516                 ""))
517           (duration->lily-string (ly:music-property event 'duration)
518                                  #:remember #t)
519           (if ((make-music-type-predicate 'RestEvent) event)
520               "\\rest" "")
521           (map-in-order (lambda (event)
522                           (music->lily-string event parser))
523                         (ly:music-property event 'articulations))))
524
525 (define-display-method NoteEvent (note parser)
526   (cond ((not (null? (ly:music-property note 'pitch))) ;; note
527          (simple-note->lily-string note parser))
528         ((not (null? (ly:music-property note 'drum-type))) ;; drum
529          (format #f "~a~a~{~a~}" (ly:music-property note 'drum-type)
530                  (duration->lily-string (ly:music-property note 'duration)
531                                         #:remember #t)
532                  (map-in-order (lambda (event)
533                                  (music->lily-string event parser))
534                                (ly:music-property note 'articulations))))
535         (else ;; unknown?
536          "")))
537
538 (define-display-method ClusterNoteEvent (note parser)
539   (simple-note->lily-string note parser))
540
541 (define-display-method RestEvent (rest parser)
542   (if (not (null? (ly:music-property rest 'pitch)))
543       (simple-note->lily-string rest parser)
544       (format #f "r~a~{~a~}"
545               (duration->lily-string (ly:music-property rest 'duration)
546                                      #:remember #t)
547               (map-in-order (lambda (event)
548                               (music->lily-string event parser))
549                             (ly:music-property rest 'articulations)))))
550
551 (define-display-method MultiMeasureRestEvent (rest parser)
552   (string-append "R" (duration->lily-string (ly:music-property rest 'duration)
553                                             #:remember #t)))
554
555 (define-display-method SkipEvent (rest parser)
556   (format #f "s~a~{~a~}"
557           (duration->lily-string (ly:music-property rest 'duration)
558                                  #:remember #t)
559           (map-in-order (lambda (event)
560                           (music->lily-string event parser))
561                         (ly:music-property rest 'articulations))))
562
563 (define-display-method RepeatedChord (chord parser)
564   (music->lily-string (ly:music-property chord 'element) parser))
565
566 (define-display-method MarkEvent (mark parser)
567   (let ((label (ly:music-property mark 'label)))
568     (if (null? label)
569         "\\mark \\default"
570         (format #f "\\mark ~a" (markup->lily-string label)))))
571
572 (define-display-method KeyChangeEvent (key parser)
573   (let ((pitch-alist (ly:music-property key 'pitch-alist))
574         (tonic (ly:music-property key 'tonic)))
575     (if (or (null? pitch-alist)
576             (null? tonic))
577         "\\key \\default"
578         (let ((c-pitch-alist (ly:transpose-key-alist pitch-alist
579                                                      (ly:pitch-diff (ly:make-pitch 0 0 0) tonic))))
580           (format #f "\\key ~a \\~a~a"
581                   (note-name->lily-string (ly:music-property key 'tonic) parser)
582                   (any (lambda (mode)
583                          (if (and parser
584                                   (equal? (ly:parser-lookup parser mode) c-pitch-alist))
585                              (symbol->string mode)
586                              #f))
587                        '(major minor ionian locrian aeolian mixolydian lydian phrygian dorian))
588                   (new-line->lily-string))))))
589
590 (define-display-method RelativeOctaveCheck (octave parser)
591   (let ((pitch (ly:music-property octave 'pitch)))
592     (format #f "\\octaveCheck ~a~a"
593             (note-name->lily-string pitch parser)
594             (octave->lily-string pitch))))
595
596 (define-display-method VoiceSeparator (sep parser)
597   "\\\\")
598
599 (define-display-method LigatureEvent (ligature parser)
600   (if (= START (ly:music-property ligature 'span-direction))
601       "\\["
602       "\\]"))
603
604 (define-display-method BarCheck (check parser)
605   (format #f "|~a" (new-line->lily-string)))
606
607 (define-display-method PesOrFlexaEvent (expr parser)
608   "\\~")
609
610 (define-display-method BassFigureEvent (figure parser)
611   (let ((alteration (ly:music-property figure 'alteration))
612         (fig (ly:music-property figure 'figure))
613         (bracket-start (ly:music-property figure 'bracket-start))
614         (bracket-stop (ly:music-property figure 'bracket-stop)))
615
616     (format #f "~a~a~a~a"
617             (if (null? bracket-start) "" "[")
618             (cond ((null? fig) "_")
619                   ((markup? fig) (second fig)) ;; fig: (<number-markup> "number")
620                   (else fig))
621             (if (null? alteration)
622                 ""
623                 (cond
624                   ((= alteration DOUBLE-FLAT) "--")
625                   ((= alteration FLAT) "-")
626                   ((= alteration NATURAL) "!")
627                   ((= alteration SHARP) "+")
628                   ((= alteration DOUBLE-SHARP) "++")
629                   (else "")))
630             (if (null? bracket-stop) "" "]"))))
631
632 (define-display-method LyricEvent (lyric parser)
633   (format "~a~{~a~^ ~}"
634           (let ((text (ly:music-property lyric 'text)))
635             (if (or (string? text)
636                     (eqv? (first text) simple-markup))
637                 ;; a string or a simple markup
638                 (let ((string (if (string? text)
639                                   text
640                                   (second text))))
641                   (if (string-match "(\"| |[0-9])" string)
642                       ;; TODO check exactly in which cases double quotes should be used
643                       (format #f "~s" string)
644                       string))
645                 (markup->lily-string text)))
646           (map-in-order (lambda (m) (music->lily-string m parser))
647                         (ly:music-property lyric 'articulations))))
648
649 (define-display-method BreathingEvent (event parser)
650   "\\breathe")
651
652 ;;;
653 ;;; Staff switches
654 ;;;
655
656 (define-display-method AutoChangeMusic (m parser)
657   (format #f "\\autochange ~a"
658           (music->lily-string (ly:music-property m 'element) parser)))
659
660 (define-display-method ContextChange (m parser)
661   (format #f "\\change ~a = \"~a\""
662           (ly:music-property m 'change-to-type)
663           (ly:music-property m 'change-to-id)))
664
665 ;;;
666
667 (define-display-method TimeScaledMusic (times parser)
668   (let* ((num (ly:music-property times 'numerator))
669          (den (ly:music-property times 'denominator))
670          (nd-gcd (gcd num den)))
671     (parameterize ((*force-line-break* #f)
672                    (*time-factor-numerator* (/ num nd-gcd))
673                    (*time-factor-denominator* (/ den nd-gcd)))
674       (format #f "\\times ~a/~a ~a"
675               num
676               den
677               (music->lily-string (ly:music-property times 'element) parser)))))
678
679 (define-display-method RelativeOctaveMusic (m parser)
680   (music->lily-string (ly:music-property m 'element) parser))
681
682 (define-display-method TransposedMusic (m parser)
683   (music->lily-string (ly:music-property m 'element) parser))
684
685 ;;;
686 ;;; Repeats
687 ;;;
688
689 (define-display-method AlternativeEvent (alternative parser) "")
690
691 (define (repeat->lily-string expr repeat-type parser)
692   (format #f "\\repeat ~a ~a ~a ~a"
693           repeat-type
694           (ly:music-property expr 'repeat-count)
695           (music->lily-string (ly:music-property expr 'element) parser)
696           (let ((alternatives (ly:music-property expr 'elements)))
697             (if (null? alternatives)
698                 ""
699                 (format #f "\\alternative { ~{~a ~}}"
700                         (map-in-order (lambda (music)
701                                         (music->lily-string music parser))
702                                       alternatives))))))
703
704 (define-display-method VoltaRepeatedMusic (expr parser)
705   (repeat->lily-string expr "volta" parser))
706
707 (define-display-method UnfoldedRepeatedMusic (expr parser)
708   (repeat->lily-string expr "unfold" parser))
709
710 (define-display-method PercentRepeatedMusic (expr parser)
711   (repeat->lily-string expr "percent" parser))
712
713 (define-display-method TremoloRepeatedMusic (expr parser)
714   (let* ((count (ly:music-property expr 'repeat-count))
715          (dots (if (= 0 (modulo count 3)) 0 1))
716          (shift (- (log2 (if (= 0 dots)
717                              (/ (* count 2) 3)
718                              count))))
719          (element (ly:music-property expr 'element))
720          (den-mult 1))
721     (if (eqv? (ly:music-property element 'name) 'SequentialMusic)
722         (begin
723           (set! shift (1- shift))
724           (set! den-mult (length (ly:music-property element 'elements)))))
725     (music-map (lambda (m)
726                  (let ((duration (ly:music-property m 'duration)))
727                    (if (ly:duration? duration)
728                        (let* ((dlog (ly:duration-log duration))
729                               (ddots (ly:duration-dot-count duration))
730                               (dfactor (ly:duration-factor duration))
731                               (dnum (car dfactor))
732                               (dden (cdr dfactor)))
733                          (set! (ly:music-property m 'duration)
734                                (ly:make-duration (- dlog shift)
735                                                  ddots ;;(- ddots dots) ; ????
736                                                  dnum
737                                                  (/ dden den-mult))))))
738                  m)
739                element)
740     (format #f "\\repeat tremolo ~a ~a"
741             count
742             (music->lily-string element parser))))
743
744 ;;;
745 ;;; Contexts
746 ;;;
747
748 (define-display-method ContextSpeccedMusic (expr parser)
749   (let ((id    (ly:music-property expr 'context-id))
750         (create-new (ly:music-property expr 'create-new))
751         (music (ly:music-property expr 'element))
752         (operations (ly:music-property expr 'property-operations))
753         (ctype (ly:music-property expr 'context-type)))
754     (format #f "~a ~a~a~a ~a"
755             (if (and (not (null? create-new)) create-new)
756                 "\\new"
757                 "\\context")
758             ctype
759             (if (null? id)
760                 ""
761                 (format #f " = ~s" id))
762             (if (null? operations)
763                 ""
764                 (format #f " \\with {~{~a~}~%~v_}"
765                         (parameterize ((*indent* (+ (*indent*) 2)))
766                           (map (lambda (op)
767                                  (format #f "~%~v_\\~a ~s"
768                                          (*indent*)
769                                          (first op)
770                                          (second op)))
771                                operations))
772                         (*indent*)))
773             (parameterize ((*current-context* ctype))
774               (music->lily-string music parser)))))
775
776 ;; special cases: \figures \lyrics \drums
777 (define-extra-display-method ContextSpeccedMusic (expr parser)
778   (with-music-match (expr (music 'ContextSpeccedMusic
779                                  create-new #t
780                                  property-operations ?op
781                                  context-type ?context-type
782                                  element ?sequence))
783     (if (null? ?op)
784         (parameterize ((*explicit-mode* #f))
785           (case ?context-type
786             ((FiguredBass)
787              (format #f "\\figures ~a" (music->lily-string ?sequence parser)))
788             ((Lyrics)
789              (format #f "\\lyrics ~a" (music->lily-string ?sequence parser)))
790             ((DrumStaff)
791              (format #f "\\drums ~a" (music->lily-string ?sequence parser)))
792             (else
793              #f)))
794         #f)))
795
796 ;;; Context properties
797
798 (define-extra-display-method ContextSpeccedMusic (expr parser)
799   (let ((element (ly:music-property expr 'element))
800         (property-tuning? (make-music-type-predicate 'PropertySet
801                                                      'PropertyUnset
802                                                      'OverrideProperty
803                                                      'RevertProperty))
804         (sequence? (make-music-type-predicate 'SequentialMusic)))
805     (if (and (ly:music? element)
806              (or (property-tuning? element)
807                  (and (sequence? element)
808                       (every property-tuning? (ly:music-property element 'elements)))))
809         (parameterize ((*current-context* (ly:music-property expr 'context-type)))
810           (music->lily-string element parser))
811         #f)))
812
813 (define (property-value->lily-string arg parser)
814   (cond ((ly:music? arg)
815          (music->lily-string arg parser))
816         ((string? arg)
817          (format #f "#~s" arg))
818         ((markup? arg)
819          (markup->lily-string arg))
820         (else
821          (format #f "#~a" (scheme-expr->lily-string arg)))))
822
823 (define-display-method PropertySet (expr parser)
824   (let ((property (ly:music-property expr 'symbol))
825         (value (ly:music-property expr 'value))
826         (once (ly:music-property expr 'once)))
827     (format #f "~a\\set ~a~a = ~a~a"
828             (if (and (not (null? once)))
829                 "\\once "
830                 "")
831             (if (eqv? (*current-context*) 'Bottom)
832                 ""
833                 (format #f "~a . " (*current-context*)))
834             property
835             (property-value->lily-string value parser)
836             (new-line->lily-string))))
837
838 (define-display-method PropertyUnset (expr parser)
839   (format #f "\\unset ~a~a~a"
840           (if (eqv? (*current-context*) 'Bottom)
841               ""
842               (format #f "~a . " (*current-context*)))
843           (ly:music-property expr 'symbol)
844           (new-line->lily-string)))
845
846 ;;; Layout properties
847
848 (define-display-method OverrideProperty (expr parser)
849   (let* ((symbol          (ly:music-property expr 'symbol))
850          (properties   (ly:music-property expr 'grob-property-path
851                                              (list (ly:music-property expr 'grob-property))))
852          (value   (ly:music-property expr 'grob-value))
853          (once    (ly:music-property expr 'once)))
854
855     (format #f "~a\\override ~a~a #'~a = ~a~a"
856             (if (or (null? once)
857                     (not once))
858                 ""
859                 "\\once ")
860             (if (eqv? (*current-context*) 'Bottom)
861                 ""
862                 (format #f "~a . " (*current-context*)))
863             symbol
864             (if (null? (cdr properties))
865                 (car properties)
866                 properties)
867             (property-value->lily-string value parser)
868             (new-line->lily-string))))
869
870 (define-display-method RevertProperty (expr parser)
871   (let* ((symbol (ly:music-property expr 'symbol))
872          (properties (ly:music-property expr 'grob-property-path
873                                              (list (ly:music-property expr 'grob-property)))))
874     (format #f "\\revert ~a~a #'~a~a"
875             (if (eqv? (*current-context*) 'Bottom)
876                 ""
877                 (format #f "~a . " (*current-context*)))
878             symbol
879             (if (null? (cdr properties))
880                 (car properties)
881                 properties)
882             (new-line->lily-string))))
883
884 (define-display-method TimeSignatureMusic (expr parser)
885   (let* ((num (ly:music-property expr 'numerator))
886          (den (ly:music-property expr 'denominator))
887          (structure (ly:music-property expr 'beat-structure)))
888     (if (null? structure)
889         (format #f
890                 "\\time ~a/~a~a"
891                 num den
892                 (new-line->lily-string))
893         (format #f
894                 "\\time #'~a ~a/~a~a" 
895                 structure num den
896                 (new-line->lily-string)))))
897
898 ;;; \melisma and \melismaEnd
899 (define-extra-display-method ContextSpeccedMusic (expr parser)
900   "If expr is a melisma, return \"\\melisma\", otherwise, return #f."
901   (with-music-match (expr (music 'ContextSpeccedMusic
902                                  element (music 'PropertySet
903                                                 value #t
904                                                 symbol 'melismaBusy)))
905     "\\melisma"))
906
907 (define-extra-display-method ContextSpeccedMusic (expr parser)
908   "If expr is a melisma end, return \"\\melismaEnd\", otherwise, return #f."
909   (with-music-match (expr (music 'ContextSpeccedMusic
910                                  element (music 'PropertyUnset
911                                                 symbol 'melismaBusy)))
912     "\\melismaEnd"))
913
914 ;;; \tempo
915 (define-extra-display-method SequentialMusic (expr parser)
916   (with-music-match (expr (music 'SequentialMusic
917                                  elements ((music 'TempoChangeEvent
918                                                   text ?text
919                                                   tempo-unit ?unit
920                                                   metronome-count ?count)
921                                            (music 'ContextSpeccedMusic
922                                                   element (music 'PropertySet
923                                                                  symbol 'tempoWholesPerMinute)))))
924     (format #f "\\tempo ~{~a~a~}~a = ~a~a"
925             (if (markup? ?text)
926                 (list (markup->lily-string ?text) " ")
927                 '())
928             (duration->lily-string ?unit #:force-duration #t)
929             (if (pair? ?count)
930                 (format #f "~a ~~ ~a" (car ?count) (cdr ?count))
931                 ?count)
932             (new-line->lily-string))))
933
934 (define-display-method TempoChangeEvent (expr parser)
935   (let ((text (ly:music-property expr 'text)))
936     (format #f "\\tempo ~a~a"
937             (markup->lily-string text)
938             (new-line->lily-string))))
939
940 ;;; \clef
941 (define clef-name-alist #f)
942 (define-public (memoize-clef-names clefs)
943   "Initialize @code{clef-name-alist}, if not already set."
944   (if (not clef-name-alist)
945       (set! clef-name-alist
946             (map (lambda (name+vals)
947                    (cons (cdr name+vals)
948                          (car name+vals)))
949                  clefs))))
950
951 (define-extra-display-method ContextSpeccedMusic (expr parser)
952   "If @var{expr} is a clef change, return \"\\clef ...\".
953 Otherwise, return @code{#f}."
954   (with-music-match (expr (music 'ContextSpeccedMusic
955                                  context-type 'Staff
956                                  element (music 'SequentialMusic
957                                                 elements ((music 'PropertySet
958                                                                  value ?clef-glyph
959                                                                  symbol 'clefGlyph)
960                                                           (music 'PropertySet
961                                                                  symbol 'middleCClefPosition)
962                                                           (music 'PropertySet
963                                                                  value ?clef-position
964                                                                  symbol 'clefPosition)
965                                                           (music 'PropertySet
966                                                                  value ?clef-octavation
967                                                                  symbol 'clefOctavation)
968                                                           (music 'ApplyContext
969                                                                  procedure ly:set-middle-C!)))))
970     (let ((clef-name (assoc-get (list ?clef-glyph ?clef-position 0)
971                                 clef-name-alist)))
972       (if clef-name
973           (format #f "\\clef \"~a~{~a~a~}\"~a"
974                   clef-name
975                   (cond ((= 0 ?clef-octavation)
976                          (list "" ""))
977                         ((> ?clef-octavation 0)
978                          (list "^" (1+ ?clef-octavation)))
979                         (else
980                          (list "_" (- 1 ?clef-octavation))))
981                   (new-line->lily-string))
982           #f))))
983
984 ;;; \bar
985 (define-extra-display-method ContextSpeccedMusic (expr parser)
986   "If `expr' is a bar, return \"\\bar ...\".
987 Otherwise, return #f."
988   (with-music-match (expr (music 'ContextSpeccedMusic
989                                  context-type 'Timing
990                                  element (music 'PropertySet
991                                                 value ?bar-type
992                                                 symbol 'whichBar)))
993     (format #f "\\bar \"~a\"~a" ?bar-type (new-line->lily-string))))
994
995 ;;; \partial
996 (define-extra-display-method ContextSpeccedMusic (expr parser)
997   "If `expr' is a partial measure, return \"\\partial ...\".
998 Otherwise, return #f."
999   (with-music-match (expr (music
1000                            'ContextSpeccedMusic
1001                            element (music
1002                                     'ContextSpeccedMusic
1003                                     context-type 'Timing
1004                                     element (music
1005                                              'PartialSet
1006                                              partial-duration ?duration))))
1007
1008     (and ?duration
1009          (format #f "\\partial ~a"
1010                  (duration->lily-string ?duration #:force-duration #t)))))
1011
1012 ;;;
1013 ;;;
1014
1015 (define-display-method ApplyOutputEvent (applyoutput parser)
1016   (let ((proc (ly:music-property applyoutput 'procedure))
1017         (ctx  (ly:music-property applyoutput 'context-type)))
1018     (format #f "\\applyOutput #'~a #~a"
1019             ctx
1020             (or (procedure-name proc)
1021                 (with-output-to-string
1022                   (lambda ()
1023                     (pretty-print (procedure-source proc))))))))
1024
1025 (define-display-method ApplyContext (applycontext parser)
1026   (let ((proc (ly:music-property applycontext 'procedure)))
1027     (format #f "\\applyContext #~a"
1028             (or (procedure-name proc)
1029                 (with-output-to-string
1030                   (lambda ()
1031                     (pretty-print (procedure-source proc))))))))
1032
1033 ;;; \partcombine
1034 (define-display-method PartCombineMusic (expr parser)
1035   (format #f "\\partcombine ~{~a ~}"
1036           (map-in-order (lambda (music)
1037                           (music->lily-string music parser))
1038                         (ly:music-property expr 'elements))))
1039
1040 (define-extra-display-method PartCombineMusic (expr parser)
1041   (with-music-match (expr (music 'PartCombineMusic
1042                                  elements ((music 'UnrelativableMusic
1043                                                   element (music 'ContextSpeccedMusic
1044                                                                  context-id "one"
1045                                                                  context-type 'Voice
1046                                                                  element ?sequence1))
1047                                            (music 'UnrelativableMusic
1048                                                   element (music 'ContextSpeccedMusic
1049                                                                  context-id "two"
1050                                                                  context-type 'Voice
1051                                                                  element ?sequence2)))))
1052     (format #f "\\partcombine ~a~a~a"
1053             (music->lily-string ?sequence1 parser)
1054             (new-line->lily-string)
1055             (music->lily-string ?sequence2 parser))))
1056
1057 (define-display-method UnrelativableMusic (expr parser)
1058   (music->lily-string (ly:music-property expr 'element) parser))
1059
1060 ;;; Cue notes
1061 (define-display-method QuoteMusic (expr parser)
1062   (or (with-music-match (expr (music
1063                                'QuoteMusic
1064                                quoted-voice-direction ?quoted-voice-direction
1065                                quoted-music-name ?quoted-music-name
1066                                quoted-context-id "cue"
1067                                quoted-context-type 'Voice
1068                                element ?music))
1069         (format #f "\\cueDuring #~s #~a ~a"
1070                 ?quoted-music-name
1071                 ?quoted-voice-direction
1072                 (music->lily-string ?music parser)))
1073       (format #f "\\quoteDuring #~s ~a"
1074               (ly:music-property expr 'quoted-music-name)
1075               (music->lily-string (ly:music-property expr 'element) parser))))
1076
1077 ;;;
1078 ;;; Breaks
1079 ;;;
1080 (define-display-method LineBreakEvent (expr parser)
1081   (if (null? (ly:music-property expr 'break-permission))
1082       "\\noBreak"
1083       "\\break"))
1084
1085 (define-display-method PageBreakEvent (expr parser)
1086   (if (null? (ly:music-property expr 'break-permission))
1087       "\\noPageBreak"
1088       "\\pageBreak"))
1089
1090 (define-display-method PageTurnEvent (expr parser)
1091   (if (null? (ly:music-property expr 'break-permission))
1092       "\\noPageTurn"
1093       "\\pageTurn"))
1094
1095 (define-extra-display-method EventChord (expr parser)
1096   (with-music-match (expr (music 'EventChord
1097                             elements ((music 'LineBreakEvent
1098                                              break-permission 'force)
1099                                       (music 'PageBreakEvent
1100                                              break-permission 'force))))
1101     "\\pageBreak"))
1102
1103 (define-extra-display-method EventChord (expr parser)
1104   (with-music-match (expr (music 'EventChord
1105                             elements ((music 'LineBreakEvent
1106                                              break-permission 'force)
1107                                       (music 'PageBreakEvent
1108                                              break-permission 'force)
1109                                       (music 'PageTurnEvent
1110                                              break-permission 'force))))
1111     "\\pageTurn"))
1112
1113 ;;;
1114 ;;; Lyrics
1115 ;;;
1116
1117 ;;; \lyricsto
1118 (define-display-method LyricCombineMusic (expr parser)
1119   (format #f "\\lyricsto ~s ~a"
1120           (ly:music-property expr 'associated-context)
1121           (parameterize ((*explicit-mode* #f))
1122             (music->lily-string (ly:music-property expr 'element) parser))))
1123
1124 ;; \addlyrics
1125 (define-extra-display-method SimultaneousMusic (expr parser)
1126   (with-music-match (expr (music 'SimultaneousMusic
1127                                  elements ((music 'ContextSpeccedMusic
1128                                                   context-id ?id
1129                                                   context-type 'Voice
1130                                                   element ?note-sequence)
1131                                            (music 'ContextSpeccedMusic
1132                                                   context-type 'Lyrics
1133                                                   create-new #t
1134                                                   element (music 'LyricCombineMusic
1135                                                                  associated-context ?associated-id
1136                                                                  element ?lyric-sequence)))))
1137     (if (string=? ?id ?associated-id)
1138         (format #f "~a~a \\addlyrics ~a"
1139                 (music->lily-string ?note-sequence parser)
1140                 (new-line->lily-string)
1141                 (parameterize ((*explicit-mode* #f))
1142                   (music->lily-string ?lyric-sequence parser)))
1143         #f)))
1144
1145 ;; Silence internal event sent at end of each lyrics block
1146 (define-display-method CompletizeExtenderEvent (expr parser)
1147   "")