Fix ordering of property ops in display method for \with.
[lilypond/mpolesky.git] / scm / define-music-display-methods.scm
blobcda14bcbde459cb3fb5711205f8aaf9300d0c9ae
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 ;;;
7 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
8 ;;;
9 ;;; Display method implementation
10 ;;;
12 (define-module (scm display-lily))
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 ;;;
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)))))
72 ;;;
73 ;;; pitch names
74 ;;;
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)))))
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)))
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 ""))))
104 ;;; durations
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         "")))
134 ;;; post events
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))
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 ""))))
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)))
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))))
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)))
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))))
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)))))
220 (define-post-event-display-method FingeringEvent (event parser) #t
221   (ly:music-property event 'digit))
223 (define-post-event-display-method TextScriptEvent (event parser) #t
224   (markup->lily-string (ly:music-property event 'text)))
226 (define-post-event-display-method MultiMeasureTextEvent (event parser) #t
227   (markup->lily-string (ly:music-property event 'text)))
229 (define-post-event-display-method BendAfterEvent (event parser) #t
230   (format #f "\\bendAfter #~a" (ly:music-property event 'delta-step)))
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)))
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")
253 ;;; Graces
256 (define-display-method GraceMusic (expr parser)
257   (format #f "\\grace ~a"
258           (music->lily-string (ly:music-property expr 'element) parser)))
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))))))
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))))))
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)))))
362 ;;; Music sequences
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))))
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)))))
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))))
436 ;;; Chords
439 (define-display-method EventChord (chord parser)
440   ;; event_chord : simple_element post_events
441   ;;               | command_element
442   ;;               | note_chord_element
444   ;; TODO : tagged post_events
445   ;; post_events : ( post_event | tagged_post_event )*
446   ;; tagged_post_event: '-' \tag embedded_scm post_event
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))))))))
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))
512 (define-display-method SkipMusic (skip parser)
513   (format #f "\\skip ~a" (duration->lily-string (ly:music-property skip 'duration) #:force-duration #t)))
516 ;;; Notes, rests, skips...
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))))
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          "")))
552 (define-display-method ClusterNoteEvent (note parser)
553   (simple-note->lily-string note parser))
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"))
560 (define-display-method MultiMeasureRestEvent (rest parser)
561   "R")
563 (define-display-method SkipEvent (rest parser)
564   "s")
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)))))
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))))))
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))))
596 (define-display-method VoiceSeparator (sep parser)
597   "\\\\")
599 (define-display-method LigatureEvent (ligature parser)
600   (if (= START (ly:music-property ligature 'span-direction))
601       "\\["
602       "\\]"))
604 (define-display-method BarCheck (check parser)
605   (format #f "|~a" (new-line->lily-string)))
607 (define-display-method PesOrFlexaEvent (expr parser)
608   "\\~")
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)))
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) "" "]"))))
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))))
646 (define-display-method BreathingEvent (event parser)
647   "\\breathe")
650 ;;; Staff switches
653 (define-display-method AutoChangeMusic (m parser)
654   (format #f "\\autochange ~a"
655           (music->lily-string (ly:music-property m 'element) parser)))
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)))
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)))))
676 (define-display-method RelativeOctaveMusic (m parser)
677   (music->lily-string (ly:music-property m 'element) parser))
679 (define-display-method TransposedMusic (m parser)
680   (music->lily-string (ly:music-property m 'element) parser))
683 ;;; Repeats
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))))))
699 (define-display-method VoltaRepeatedMusic (expr parser)
700   (repeat->lily-string expr "volta" parser))
702 (define-display-method UnfoldedRepeatedMusic (expr parser)
703   (repeat->lily-string expr "unfold" parser))
705 (define-display-method PercentRepeatedMusic (expr parser)
706   (repeat->lily-string expr "percent" parser))
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))))
740 ;;; Contexts
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                                operations))
767                         (*indent*)))
768             (parameterize ((*current-context* ctype))
769               (music->lily-string music parser)))))
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)))
791 ;;; Context properties
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)))
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)))))
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))))
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)))
841 ;;; Layout properties
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)))
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))))
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))))
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"))
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"))
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)))))
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))))
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))))
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)))))))
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))))
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))))
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))))
1052 (define (moment->duration moment)
1053   (assoc-get (- moment) moment-duration-alist))
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))))))
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))))))))
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))))))))
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))))
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))))
1117 (define-display-method UnrelativableMusic (expr parser)
1118   (music->lily-string (ly:music-property expr 'element) parser))
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))))
1138 ;;; Breaks
1140 (define-display-method LineBreakEvent (expr parser)
1141   (if (null? (ly:music-property expr 'break-permission))
1142       "\\noBreak"
1143       "\\break"))
1145 (define-display-method PageBreakEvent (expr parser)
1146   (if (null? (ly:music-property expr 'break-permission))
1147       "\\noPageBreak"
1148       "\\pageBreak"))
1150 (define-display-method PageTurnEvent (expr parser)
1151   (if (null? (ly:music-property expr 'break-permission))
1152       "\\noPageTurn"
1153       "\\pageTurn"))
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"))
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"))
1174 ;;; Lyrics
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))))
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)))