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