]> git.donarmstrong.com Git - lilypond.git/blob - scm/define-music-display-methods.scm
indent
[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 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   (let ((text (ly:music-property lyric 'text)))
634     (if (or (string? text)
635             (eqv? (first text) simple-markup))
636         ;; a string or a simple markup
637         (let ((string (if (string? text)
638                           text
639                           (second text))))
640           (if (string-match "(\"| |[0-9])" string)
641               ;; TODO check exactly in which cases double quotes should be used
642               (format #f "~s" string)
643               string))
644         (markup->lily-string text))))
645
646 (define-display-method BreathingEvent (event parser)
647   "\\breathe")
648
649 ;;;
650 ;;; Staff switches
651 ;;;
652
653 (define-display-method AutoChangeMusic (m parser)
654   (format #f "\\autochange ~a"
655           (music->lily-string (ly:music-property m 'element) parser)))
656
657 (define-display-method ContextChange (m parser)
658   (format #f "\\change ~a = \"~a\""
659           (ly:music-property m 'change-to-type)
660           (ly:music-property m 'change-to-id)))
661
662 ;;;
663
664 (define-display-method TimeScaledMusic (times parser)
665   (let* ((num (ly:music-property times 'numerator))
666          (den (ly:music-property times 'denominator))
667          (nd-gcd (gcd num den)))
668     (parameterize ((*force-line-break* #f)
669                    (*time-factor-numerator* (/ num nd-gcd))
670                    (*time-factor-denominator* (/ den nd-gcd)))
671       (format #f "\\times ~a/~a ~a"
672               num
673               den
674               (music->lily-string (ly:music-property times 'element) parser)))))
675
676 (define-display-method RelativeOctaveMusic (m parser)
677   (music->lily-string (ly:music-property m 'element) parser))
678
679 (define-display-method TransposedMusic (m parser)
680   (music->lily-string (ly:music-property m 'element) parser))
681
682 ;;;
683 ;;; Repeats
684 ;;;
685
686 (define (repeat->lily-string expr repeat-type parser)
687   (format #f "\\repeat ~a ~a ~a ~a"
688           repeat-type
689           (ly:music-property expr 'repeat-count)
690           (music->lily-string (ly:music-property expr 'element) parser)
691           (let ((alternatives (ly:music-property expr 'elements)))
692             (if (null? alternatives)
693                 ""
694                 (format #f "\\alternative { ~{~a ~}}"
695                         (map-in-order (lambda (music)
696                                         (music->lily-string music parser))
697                                       alternatives))))))
698
699 (define-display-method VoltaRepeatedMusic (expr parser)
700   (repeat->lily-string expr "volta" parser))
701
702 (define-display-method UnfoldedRepeatedMusic (expr parser)
703   (repeat->lily-string expr "unfold" parser))
704
705 (define-display-method PercentRepeatedMusic (expr parser)
706   (repeat->lily-string expr "percent" parser))
707
708 (define-display-method TremoloRepeatedMusic (expr parser)
709   (let* ((count (ly:music-property expr 'repeat-count))
710          (dots (if (= 0 (modulo count 3)) 0 1))
711          (shift (- (log2 (if (= 0 dots)
712                              (/ (* count 2) 3)
713                              count))))
714          (element (ly:music-property expr 'element))
715          (den-mult 1))
716     (if (eqv? (ly:music-property element 'name) 'SequentialMusic)
717         (begin
718           (set! shift (1- shift))
719           (set! den-mult (length (ly:music-property element 'elements)))))
720     (music-map (lambda (m)
721                  (let ((duration (ly:music-property m 'duration)))
722                    (if (ly:duration? duration)
723                        (let* ((dlog (ly:duration-log duration))
724                               (ddots (ly:duration-dot-count duration))
725                               (dfactor (ly:duration-factor duration))
726                               (dnum (car dfactor))
727                               (dden (cdr dfactor)))
728                          (set! (ly:music-property m 'duration)
729                                (ly:make-duration (- dlog shift)
730                                                  ddots ;;(- ddots dots) ; ????
731                                                  dnum
732                                                  (/ dden den-mult))))))
733                  m)
734                element)
735     (format #f "\\repeat tremolo ~a ~a"
736             count
737             (music->lily-string element parser))))
738
739 ;;;
740 ;;; Contexts
741 ;;;
742
743 (define-display-method ContextSpeccedMusic (expr parser)
744   (let ((id    (ly:music-property expr 'context-id))
745         (create-new (ly:music-property expr 'create-new))
746         (music (ly:music-property expr 'element))
747         (operations (ly:music-property expr 'property-operations))
748         (ctype (ly:music-property expr 'context-type)))
749     (format #f "~a ~a~a~a ~a"
750             (if (and (not (null? create-new)) create-new)
751                 "\\new"
752                 "\\context")
753             ctype
754             (if (null? id)
755                 ""
756                 (format #f " = ~s" id))
757             (if (null? operations)
758                 ""
759                 (format #f " \\with {~{~a~}~%~v_}"
760                         (parameterize ((*indent* (+ (*indent*) 2)))
761                           (map (lambda (op)
762                                  (format #f "~%~v_\\~a ~s"
763                                          (*indent*)
764                                          (first op)
765                                          (second op)))
766                                (reverse operations)))
767                         (*indent*)))
768             (parameterize ((*current-context* ctype))
769               (music->lily-string music parser)))))
770
771 ;; special cases: \figures \lyrics \drums
772 (define-extra-display-method ContextSpeccedMusic (expr parser)
773   (with-music-match (expr (music 'ContextSpeccedMusic
774                                  create-new #t
775                                  property-operations ?op
776                                  context-type ?context-type
777                                  element ?sequence))
778     (if (null? ?op)
779         (parameterize ((*explicit-mode* #f))
780           (case ?context-type
781             ((FiguredBass)
782              (format #f "\\figures ~a" (music->lily-string ?sequence parser)))
783             ((Lyrics)
784              (format #f "\\lyrics ~a" (music->lily-string ?sequence parser)))
785             ((DrumStaff)
786              (format #f "\\drums ~a" (music->lily-string ?sequence parser)))
787             (else
788              #f)))
789         #f)))
790
791 ;;; Context properties
792
793 (define-extra-display-method ContextSpeccedMusic (expr parser)
794   (let ((element (ly:music-property expr 'element))
795         (property-tuning? (make-music-type-predicate 'PropertySet
796                                                      'PropertyUnset
797                                                      'OverrideProperty
798                                                      'RevertProperty))
799         (sequence? (make-music-type-predicate 'SequentialMusic)))
800     (if (and (ly:music? element)
801              (or (property-tuning? element)
802                  (and (sequence? element)
803                       (every property-tuning? (ly:music-property element 'elements)))))
804         (parameterize ((*current-context* (ly:music-property expr 'context-type)))
805           (music->lily-string element parser))
806         #f)))
807
808 (define (property-value->lily-string arg parser)
809   (cond ((ly:music? arg)
810          (music->lily-string arg parser))
811         ((string? arg)
812          (format #f "#~s" arg))
813         ((markup? arg)
814          (markup->lily-string arg))
815         (else
816          (format #f "#~a" (scheme-expr->lily-string arg)))))
817
818 (define-display-method PropertySet (expr parser)
819   (let ((property (ly:music-property expr 'symbol))
820         (value (ly:music-property expr 'value))
821         (once (ly:music-property expr 'once)))
822     (format #f "~a\\set ~a~a = ~a~a"
823             (if (and (not (null? once)))
824                 "\\once "
825                 "")
826             (if (eqv? (*current-context*) 'Bottom)
827                 ""
828                 (format #f "~a . " (*current-context*)))
829             property
830             (property-value->lily-string value parser)
831             (new-line->lily-string))))
832
833 (define-display-method PropertyUnset (expr parser)
834   (format #f "\\unset ~a~a~a"
835           (if (eqv? (*current-context*) 'Bottom)
836               ""
837               (format #f "~a . " (*current-context*)))
838           (ly:music-property expr 'symbol)
839           (new-line->lily-string)))
840
841 ;;; Layout properties
842
843 (define-display-method OverrideProperty (expr parser)
844   (let* ((symbol          (ly:music-property expr 'symbol))
845          (property-path   (ly:music-property expr 'grob-property-path))
846          (properties      (if (pair? property-path)
847                               property-path
848                               (list (ly:music-property expr 'grob-property))))
849          (value   (ly:music-property expr 'grob-value))
850          (once    (ly:music-property expr 'once)))
851
852     (format #f "~a\\override ~a~a #'~a = ~a~a"
853             (if (or (null? once)
854                     (not once))
855                 ""
856                 "\\once ")
857             (if (eqv? (*current-context*) 'Bottom)
858                 ""
859                 (format #f "~a . " (*current-context*)))
860             symbol
861             (if (null? (cdr properties))
862                 (car properties)
863                 properties)
864             (property-value->lily-string value parser)
865             (new-line->lily-string))))
866
867 (define-display-method RevertProperty (expr parser)
868   (let ((symbol (ly:music-property expr 'symbol))
869         (properties (ly:music-property expr 'grob-property-path)))
870     (format #f "\\revert ~a~a #'~a~a"
871             (if (eqv? (*current-context*) 'Bottom)
872                 ""
873                 (format #f "~a . " (*current-context*)))
874             symbol
875             (if (null? (cdr properties))
876                 (car properties)
877                 properties)
878             (new-line->lily-string))))
879
880 ;;; \melisma and \melismaEnd
881 (define-extra-display-method ContextSpeccedMusic (expr parser)
882   "If expr is a melisma, return \"\\melisma\", otherwise, return #f."
883   (with-music-match (expr (music 'ContextSpeccedMusic
884                                  element (music 'PropertySet
885                                                 value #t
886                                                 symbol 'melismaBusy)))
887     "\\melisma"))
888
889 (define-extra-display-method ContextSpeccedMusic (expr parser)
890   "If expr is a melisma end, return \"\\melismaEnd\", otherwise, return #f."
891   (with-music-match (expr (music 'ContextSpeccedMusic
892                                  element (music 'PropertyUnset
893                                                 symbol 'melismaBusy)))
894     "\\melismaEnd"))
895
896 ;;; \tempo
897 ;;; Check for all three different syntaxes of tempo:
898 ;;; \tempo string duration=note, \tempo duration=note and \tempo string
899 (define-extra-display-method ContextSpeccedMusic (expr parser)
900   "If expr is a tempo, return \"\\tempo x = nnn\", otherwise return #f."
901   (or   (with-music-match (expr (music 'ContextSpeccedMusic
902                 element (music 'SequentialMusic
903                               elements ((music 'PropertySet
904                                           value ?unit-text
905                                           symbol 'tempoText)
906                                         (music 'PropertySet
907                                           symbol 'tempoWholesPerMinute)
908                                         (music 'PropertySet
909                                           value ?unit-duration
910                                           symbol 'tempoUnitDuration)
911                                         (music 'PropertySet
912                                           value ?unit-count
913                                           symbol 'tempoUnitCount)))))
914                 (format #f "\\tempo ~a ~a = ~a"
915                         (scheme-expr->lily-string ?unit-text)
916                         (duration->lily-string ?unit-duration #:force-duration #t)
917                         ?unit-count))
918         (with-music-match (expr (music 'ContextSpeccedMusic
919                     element (music 'SequentialMusic
920                               elements ((music 'PropertyUnset
921                                           symbol 'tempoText)
922                                         (music 'PropertySet
923                                           symbol 'tempoWholesPerMinute)
924                                         (music 'PropertySet
925                                           value ?unit-duration
926                                           symbol 'tempoUnitDuration)
927                                         (music 'PropertySet
928                                           value ?unit-count
929                                           symbol 'tempoUnitCount)))))
930                         (format #f "\\tempo ~a = ~a"
931                                 (duration->lily-string ?unit-duration #:force-duration #t)
932                                 ?unit-count))
933         (with-music-match (expr (music 'ContextSpeccedMusic
934                             element (music 'SequentialMusic
935                                       elements ((music 'PropertySet
936                                                   value ?tempo-text
937                                                  symbol 'tempoText)))))
938                         (format #f "\\tempo ~a" (scheme-expr->lily-string ?tempo-text)))))
939
940 ;;; \clef
941 (define clef-name-alist #f)
942 (define-public (memoize-clef-names clefs)
943   "Initialize `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 `expr' is a clef change, return \"\\clef ...\"
953 Otherwise, return #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 ;;; \time
985 (define-extra-display-method ContextSpeccedMusic (expr parser)
986   "If `expr' is a time signature set, return \"\\time ...\".
987 Otherwise, return #f.  Note: default grouping is not available."
988   (with-music-match
989    (expr (music
990            'ContextSpeccedMusic
991            element (music
992                     'ContextSpeccedMusic
993                     context-type 'Timing
994                     element (music
995                              'SequentialMusic
996                              elements ?elts))))
997    (and
998     (> (length ?elts) 2)
999     (with-music-match ((cadr ?elts)
1000                        (music 'PropertySet
1001                               symbol 'beatLength))
1002        #t)
1003     (with-music-match ((caddr ?elts)
1004                        (music 'PropertySet
1005                               symbol 'measureLength))
1006        #t)
1007     (with-music-match ((car ?elts)
1008                        (music 'PropertySet
1009                               value ?num+den
1010                               symbol 'timeSignatureFraction))
1011        (if (eq? (length ?elts) 3)
1012            (format
1013              #f "\\time ~a/~a~a"
1014              (car ?num+den) (cdr ?num+den) (new-line->lily-string))
1015            (format
1016              #f "#(set-time-signature ~a ~a '(<grouping-specifier>))~a"
1017              (car ?num+den) (cdr ?num+den)  (new-line->lily-string)))))))
1018
1019 ;;; \bar
1020 (define-extra-display-method ContextSpeccedMusic (expr parser)
1021   "If `expr' is a bar, return \"\\bar ...\".
1022 Otherwise, return #f."
1023   (with-music-match (expr (music 'ContextSpeccedMusic
1024                                  context-type 'Timing
1025                                  element (music 'PropertySet
1026                                                 value ?bar-type
1027                                                 symbol 'whichBar)))
1028      (format #f "\\bar \"~a\"~a" ?bar-type (new-line->lily-string))))
1029
1030 ;;; \partial
1031 (define (duration->moment ly-duration)
1032   (let ((log2    (ly:duration-log ly-duration))
1033         (dots    (ly:duration-dot-count ly-duration))
1034         (num+den (ly:duration-factor ly-duration)))
1035     (let* ((m (expt 2 (- log2)))
1036            (factor (/ (car num+den) (cdr num+den))))
1037       (/ (do ((i 0 (1+ i))
1038               (delta (/ m 2) (/ delta 2)))
1039              ((= i dots) m)
1040            (set! m (+ m delta)))
1041          factor))))
1042
1043 (define moment-duration-alist (map (lambda (duration)
1044                                      (cons (duration->moment duration)
1045                                            duration))
1046                                    (append-map (lambda (log2)
1047                                                  (map (lambda (dots)
1048                                                         (ly:make-duration log2 dots 1 1))
1049                                                       (list 0 1 2 3)))
1050                                                (list 0 1 2 3 4))))
1051
1052 (define (moment->duration moment)
1053   (assoc-get (- moment) moment-duration-alist))
1054
1055 (define-extra-display-method ContextSpeccedMusic (expr parser)
1056   "If `expr' is a partial measure, return \"\\partial ...\".
1057 Otherwise, return #f."
1058   (with-music-match (expr (music
1059                            'ContextSpeccedMusic
1060                            element (music
1061                                     'ContextSpeccedMusic
1062                                     context-type 'Timing
1063                                     element (music
1064                                              'PropertySet
1065                                              value ?moment
1066                                              symbol 'measurePosition))))
1067      (let ((duration (moment->duration (/ (ly:moment-main-numerator ?moment)
1068                                           (ly:moment-main-denominator ?moment)))))
1069        (and duration (format #f "\\partial ~a" (duration->lily-string duration
1070                                                  #:force-duration #t))))))
1071
1072 ;;;
1073 ;;;
1074
1075 (define-display-method ApplyOutputEvent (applyoutput parser)
1076   (let ((proc (ly:music-property applyoutput 'procedure))
1077         (ctx  (ly:music-property applyoutput 'context-type)))
1078     (format #f "\\applyOutput #'~a #~a"
1079             ctx
1080             (or (procedure-name proc)
1081                 (with-output-to-string
1082                   (lambda ()
1083                     (pretty-print (procedure-source proc))))))))
1084
1085 (define-display-method ApplyContext (applycontext parser)
1086   (let ((proc (ly:music-property applycontext 'procedure)))
1087     (format #f "\\applyContext #~a"
1088             (or (procedure-name proc)
1089                 (with-output-to-string
1090                   (lambda ()
1091                     (pretty-print (procedure-source proc))))))))
1092
1093 ;;; \partcombine
1094 (define-display-method PartCombineMusic (expr parser)
1095   (format #f "\\partcombine ~{~a ~}"
1096           (map-in-order (lambda (music)
1097                           (music->lily-string music parser))
1098                         (ly:music-property expr 'elements))))
1099
1100 (define-extra-display-method PartCombineMusic (expr parser)
1101   (with-music-match (expr (music 'PartCombineMusic
1102                                  elements ((music 'UnrelativableMusic
1103                                                   element (music 'ContextSpeccedMusic
1104                                                                  context-id "one"
1105                                                                  context-type 'Voice
1106                                                                  element ?sequence1))
1107                                            (music 'UnrelativableMusic
1108                                                   element (music 'ContextSpeccedMusic
1109                                                                  context-id "two"
1110                                                                  context-type 'Voice
1111                                                                  element ?sequence2)))))
1112     (format #f "\\partcombine ~a~a~a"
1113             (music->lily-string ?sequence1 parser)
1114             (new-line->lily-string)
1115             (music->lily-string ?sequence2 parser))))
1116
1117 (define-display-method UnrelativableMusic (expr parser)
1118   (music->lily-string (ly:music-property expr 'element) parser))
1119
1120 ;;; Cue notes
1121 (define-display-method QuoteMusic (expr parser)
1122   (or (with-music-match (expr (music
1123                                'QuoteMusic
1124                                quoted-voice-direction ?quoted-voice-direction
1125                                quoted-music-name ?quoted-music-name
1126                                quoted-context-id "cue"
1127                                quoted-context-type 'Voice
1128                                element ?music))
1129         (format #f "\\cueDuring #~s #~a ~a"
1130                 ?quoted-music-name
1131                 ?quoted-voice-direction
1132                 (music->lily-string ?music parser)))
1133       (format #f "\\quoteDuring #~s ~a"
1134               (ly:music-property expr 'quoted-music-name)
1135               (music->lily-string (ly:music-property expr 'element) parser))))
1136
1137 ;;;
1138 ;;; Breaks
1139 ;;;
1140 (define-display-method LineBreakEvent (expr parser)
1141   (if (null? (ly:music-property expr 'break-permission))
1142       "\\noBreak"
1143       "\\break"))
1144
1145 (define-display-method PageBreakEvent (expr parser)
1146   (if (null? (ly:music-property expr 'break-permission))
1147       "\\noPageBreak"
1148       "\\pageBreak"))
1149
1150 (define-display-method PageTurnEvent (expr parser)
1151   (if (null? (ly:music-property expr 'break-permission))
1152       "\\noPageTurn"
1153       "\\pageTurn"))
1154
1155 (define-extra-display-method EventChord (expr parser)
1156   (with-music-match (expr (music 'EventChord
1157                             elements ((music 'LineBreakEvent
1158                                              break-permission 'force)
1159                                       (music 'PageBreakEvent
1160                                              break-permission 'force))))
1161     "\\pageBreak"))
1162
1163 (define-extra-display-method EventChord (expr parser)
1164   (with-music-match (expr (music 'EventChord
1165                             elements ((music 'LineBreakEvent
1166                                              break-permission 'force)
1167                                       (music 'PageBreakEvent
1168                                              break-permission 'force)
1169                                       (music 'PageTurnEvent
1170                                              break-permission 'force))))
1171     "\\pageTurn"))
1172
1173 ;;;
1174 ;;; Lyrics
1175 ;;;
1176
1177 ;;; \lyricsto
1178 (define-display-method LyricCombineMusic (expr parser)
1179   (format #f "\\lyricsto ~s ~a"
1180           (ly:music-property expr 'associated-context)
1181           (parameterize ((*explicit-mode* #f))
1182             (music->lily-string (ly:music-property expr 'element) parser))))
1183
1184 ;; \addlyrics
1185 (define-extra-display-method SimultaneousMusic (expr parser)
1186   (with-music-match (expr (music 'SimultaneousMusic
1187                                  elements ((music 'ContextSpeccedMusic
1188                                                   context-id ?id
1189                                                   context-type 'Voice
1190                                                   element ?note-sequence)
1191                                            (music 'ContextSpeccedMusic
1192                                                   context-type 'Lyrics
1193                                                   create-new #t
1194                                                   element (music 'LyricCombineMusic
1195                                                                  associated-context ?associated-id
1196                                                                  element ?lyric-sequence)))))
1197     (if (string=? ?id ?associated-id)
1198         (format #f "~a~a \\addlyrics ~a"
1199                 (music->lily-string ?note-sequence parser)
1200                 (new-line->lily-string)
1201                 (parameterize ((*explicit-mode* #f))
1202                   (music->lily-string ?lyric-sequence parser)))
1203         #f)))
1204
1205