*** empty log message ***
[lilypond/patrick.git] / scm / define-music-display-methods.scm
blobc6ee2298215fac6b73e41debdef60ee67d4937b0
1 ;;; define-music-display-methods.scm -- data for displaying music
2 ;;; expressions using LilyPond notation.
3 ;;;
4 ;;; (c) 2005 Nicolas Sceaux  <nicolas.sceaux@free.fr>
5 ;;;
7 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
8 ;;;
9 ;;; Display method implementation
10 ;;;
12 (define-module (scm display-lily))
15 ;;; `display-lily-init' must be called before using `display-lily-music'. It
16 ;;; takes a parser object as an argument.
17 (define-public (display-lily-init parser)
18   (*parser* parser)
19   (set-note-names! (ly:parser-lookup (*parser*) 'pitchnames))
20   #t)
22 ;;;
23 ;;; Scheme forms
24 ;;;
25 (define (scheme-expr->lily-string scm-arg)
26   (cond ((or (number? scm-arg)
27              (string? scm-arg))
28          (format #f "~s" scm-arg))
29         ((or (symbol? scm-arg)
30              (list? scm-arg))
31          (format #f "'~s" scm-arg))
32         ((procedure? scm-arg)
33          (format #f "~a"
34                  (or (procedure-name scm-arg)
35                      (with-output-to-string
36                        (lambda ()
37                          (pretty-print (procedure-source scm-arg)))))))
38         (else
39          (format #f "~a"
40                  (with-output-to-string
41                    (lambda ()
42                      (display-scheme-music scm-arg)))))))
43 ;;;
44 ;;; Markups
45 ;;;
47 (define-public (markup->lily-string markup-expr)
48   "Return a string describing, in LilyPond syntax, the given markup expression."
49   (define (proc->command proc)
50     (let ((cmd-markup (symbol->string (procedure-name proc))))
51       (substring cmd-markup 0 (- (string-length cmd-markup)
52                                  (string-length "-markup")))))
53   (define (arg->string arg)
54     (cond ((string? arg)
55            (format #f "~s" arg))
56           ((markup? arg) ;; a markup
57            (markup->lily-string-aux arg))
58           ((and (pair? arg) (every markup? arg)) ;; a markup list
59            (format #f "{~{ ~a~}}" (map-in-order markup->lily-string-aux arg)))
60           (else          ;; a scheme argument
61            (format #f "#~a" (scheme-expr->lily-string arg)))))
62   (define (markup->lily-string-aux expr)
63     (if (string? expr)
64         (format #f "~s" expr)
65         (let ((cmd (car expr))
66               (args (cdr expr)))
67           (if (eqv? cmd simple-markup) ;; a simple markup
68               (format #f "~s" (car args))
69               (format #f "\\~a~{ ~a~}" 
70                       (proc->command cmd)
71                       (map-in-order arg->string args))))))
72   (cond ((string? markup-expr)
73          (format #f "~s" markup-expr))
74         ((eqv? (car markup-expr) simple-markup)
75          (format #f "~s" (second markup-expr)))
76         (else
77          (format #f "\\markup ~a"
78                  (markup->lily-string-aux markup-expr)))))
80 ;;;
81 ;;; pitch names
82 ;;;
83 (define note-names '())
85 (define (set-note-names! pitchnames)
86   (set! note-names (map-in-order (lambda (name+lypitch)
87                                    (cons (cdr name+lypitch) (car name+lypitch)))
88                                  pitchnames)))
90 (define (note-name->lily-string ly-pitch)
91   ;; here we define a custom pitch= function, since we do not want to
92   ;; test whether octaves are also equal. (otherwise, we would be using equal?)
93   (define (pitch= pitch1 pitch2)
94     (and (= (ly:pitch-notename pitch1) (ly:pitch-notename pitch2))
95          (= (ly:pitch-alteration pitch1) (ly:pitch-alteration pitch2))))
96   (let ((result (assoc ly-pitch note-names pitch=))) ;; assoc from srfi-1
97     (if result
98         (cdr result)
99         #f)))
101 (define (octave->lily-string pitch)
102   (let ((octave (ly:pitch-octave pitch)))
103     (cond ((>= octave 0)
104            (make-string (1+ octave) #\'))
105           ((< octave -1)
106            (make-string (1- (* -1 octave)) #\,))
107           (else ""))))
110 ;;; durations
112 (define* (duration->lily-string ly-duration #:key (prev-duration (*previous-duration*))
113                         (force-duration (*force-duration*))
114                         (time-factor-numerator (*time-factor-numerator*))
115                         (time-factor-denominator (*time-factor-denominator*)))
116   (let ((log2    (ly:duration-log ly-duration))
117         (dots    (ly:duration-dot-count ly-duration))
118         (num+den (ly:duration-factor ly-duration)))
119     (if (or force-duration (not prev-duration) (not (equal? ly-duration prev-duration)))
120         (string-append (case log2
121                          ((-1) "\\breve")
122                          ((-2) "\\longa")
123                          ((-3) "\\maxima")
124                          (else (number->string (expt 2 log2))))
125                        (make-string dots #\.)
126                        (let ((num? (not (or (= 1 (car num+den))
127                                             (and time-factor-numerator
128                                                  (= (car num+den) time-factor-numerator)))))
129                              (den? (not (or (= 1 (cdr num+den))
130                                             (and time-factor-denominator
131                                                  (= (cdr num+den) time-factor-denominator))))))
132                          (cond (den?
133                                 (format #f "*~a/~a" (car num+den) (cdr num+den)))
134                                (num?
135                                 (format #f "*~a" (car num+den)))
136                                (else ""))))
137         "")))
140 ;;; post events
143 (define post-event? (make-music-type-predicate  
144                      'StringNumberEvent
145                      'ArticulationEvent
146                      'FingerEvent
147                      'TextScriptEvent
148                      'MultiMeasureTextEvent
149                      'HyphenEvent
150                      'ExtenderEvent
151                      'BeamEvent
152                      'SlurEvent
153                      'TieEvent
154                      'CrescendoEvent
155                      'DecrescendoEvent
156                      'PhrasingSlurEvent
157                      'TremoloEvent
158                      'SustainEvent
159                      'SostenutoEvent
160                      'ManualMelismaEvent
161                      'TextSpanEvent
162                      'HarmonicEvent
163                      'BeamForbidEvent
164                      'AbsoluteDynamicEvent
165                      'TrillSpanEvent
166                      'GlissandoEvent
167                      'ArpeggioEvent
168                      'NoteGroupingEvent
169                      'UnaCordaEvent))
171 (define* (event-direction->lily-string event #:optional (required #t))
172   (let ((direction (ly:music-property event 'direction)))
173     (cond ((or (not direction) (null? direction) (= 0 direction))
174            (if required "-" ""))
175           ((= 1 direction) "^")
176           ((= -1 direction) "_")
177           (else ""))))
179 (define-macro (define-post-event-display-method type vars direction-required str)
180   `(define-display-method ,type ,vars
181      (format #f "~a~a"
182              (event-direction->lily-string ,(car vars) ,direction-required)
183              ,str)))
185 (define-macro (define-span-event-display-method type vars direction-required str-start str-stop)
186   `(define-display-method ,type ,vars
187      (format #f "~a~a"
188              (event-direction->lily-string ,(car vars) ,direction-required)
189              (if (= -1 (ly:music-property ,(car vars) 'span-direction))
190                  ,str-start
191                  ,str-stop))))
193 (define-display-method HyphenEvent (event)
194   " --")
195 (define-display-method ExtenderEvent (event)
196   " __")
197 (define-display-method TieEvent (event)
198   " ~")
199 (define-display-method BeamForbidEvent (event)
200   "\\noBeam")
201 (define-display-method StringNumberEvent (event)
202   (format #f "\\~a" (ly:music-property event 'string-number)))
205 (define-display-method TremoloEvent (event)
206   (let ((tremolo-type (ly:music-property event 'tremolo-type)))
207     (format #f ":~a" (if (= 0 tremolo-type)
208                          ""
209                          tremolo-type))))
211 (define-post-event-display-method ArticulationEvent (event) #t
212   (let ((articulation  (ly:music-property event 'articulation-type)))
213     (case (string->symbol articulation)
214       ((marcato) "^")
215       ((stopped) "+")
216       ((tenuto)  "-")
217       ((staccatissimo) "|")
218       ((accent) ">")
219       ((staccato) ".")
220       ((portato) "_")
221       (else (format #f "\\~a" articulation)))))
223 (define-post-event-display-method FingerEvent (event) #t
224   (ly:music-property event 'digit))
226 (define-post-event-display-method TextScriptEvent (event) #t
227   (markup->lily-string (ly:music-property event 'text)))
229 (define-post-event-display-method MultiMeasureTextEvent (event) #t
230   (markup->lily-string (ly:music-property event 'text)))
232 (define-post-event-display-method HarmonicEvent (event) #t "\\harmonic")
233 (define-post-event-display-method GlissandoEvent (event) #t "\\glissando")
234 (define-post-event-display-method ArpeggioEvent (event) #t "\\arpeggio")
235 (define-post-event-display-method AbsoluteDynamicEvent (event) #f
236   (format #f "\\~a" (ly:music-property event 'text)))
238 (define-span-event-display-method BeamEvent (event) #f "[" "]")
239 (define-span-event-display-method SlurEvent (event) #f "(" ")")
240 (define-span-event-display-method CrescendoEvent (event) #f "\\<" "\\!")
241 (define-span-event-display-method DecrescendoEvent (event) #f "\\>" "\\!")
242 (define-span-event-display-method PhrasingSlurEvent (event) #f "\\(" "\\)")
243 (define-span-event-display-method SustainEvent (event) #f "\\sustainDown" "\\sustainUp")
244 (define-span-event-display-method SostenutoEvent (event) #f "\\sostenutoDown" "\\sostenutoUp")
245 (define-span-event-display-method ManualMelismaEvent (event) #f "\\melisma" "\\melismaEnd")
246 (define-span-event-display-method TextSpanEvent (event) #f "\\startTextSpan" "\\stopTextSpan")
247 (define-span-event-display-method TrillSpanEvent (event) #f "\\startTrillSpan" "\\stopTrillSpan")
248 (define-span-event-display-method StaffSpanEvent (event) #f "\\startStaff" "\\stopStaff")
249 (define-span-event-display-method NoteGroupingEvent (event) #f "\\startGroup" "\\stopGroup")
250 (define-span-event-display-method UnaCordaEvent (event) #f "\\unaCorda" "\\treCorde")
253 ;;; Graces
256 (define-display-method GraceMusic (expr)
257   (format #f "\\grace ~a" 
258           (music->lily-string (ly:music-property expr 'element))))
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)
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 -1))))))
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 1))))))
296             (format #f "\\appoggiatura ~a" (music->lily-string ?music))))))
299 (define-extra-display-method GraceMusic (expr)
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 -1)))
320                                               (music
321                                                'ContextSpeccedMusic
322                                                element (music
323                                                         'OverrideProperty
324                                                         grob-property '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 '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 1))))))
344            (format #f "\\acciaccatura ~a" (music->lily-string ?music))))))
346 (define-extra-display-method GraceMusic (expr)
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)))))
362 ;;; Music sequences
365 (define-display-method SequentialMusic (seq)
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 music->lily-string elements))
409             (if force-line-break 1 0)
410             (if force-line-break (*indent*) 0))))
412 (define-display-method SimultaneousMusic (sim)
413   (parameterize ((*indent* (+ 3 (*indent*))))
414     (format #f "<< ~{~a ~}>>"
415             (map-in-order music->lily-string (ly:music-property sim 'elements)))))
417 (define-extra-display-method SimultaneousMusic (expr)
418   "If `sim' is an \afterGrace expression, return \"\\afterGrace ...\".
419 Otherwise, return #f."
420   ;; TODO: do something with afterGraceFraction?
421   (with-music-match (expr (music 'SimultaneousMusic
422                                  elements (?before-grace
423                                            (music 'SequentialMusic
424                                                   elements ((music 'SkipMusic)
425                                                             (music 'GraceMusic
426                                                                    element ?grace))))))
427     (format #f "\\afterGrace ~a ~a"
428             (music->lily-string ?before-grace)
429             (music->lily-string ?grace))))
430   
432 ;;; Chords
435 (define-display-method EventChord (chord)
436   ;; event_chord : simple_element post_events
437   ;;               | command_element
438   ;;               | note_chord_element
440   ;; TODO : tagged post_events
441   ;; post_events : ( post_event | tagged_post_event )*
442   ;; tagged_post_event: '-' \tag embedded_scm post_event
444   (let* ((elements (ly:music-property chord 'elements))
445          (simple-elements (filter (make-music-type-predicate 
446                                    'NoteEvent 'ClusterNoteEvent 'RestEvent
447                                    'MultiMeasureRestEvent 'SkipEvent 'LyricEvent)
448                                   elements)))
449     (if ((make-music-type-predicate 'StaffSpanEvent 'BreathingSignEvent) (car elements))
450         ;; first, a special case: StaffSpanEvent (\startStaff, \stopStaff)
451         ;; and BreathingSignEvent (\breathe)
452         (music->lily-string (car elements))
453         (if (and (not (null? simple-elements))
454                  (null? (cdr simple-elements)))
455             ;; simple_element : note | figure | rest | mmrest | lyric_element | skip
456             (let* ((simple-element (car simple-elements))
457                    (duration (ly:music-property simple-element 'duration))
458                    (lily-string (format #f "~a~a~a~{~a ~}"
459                                         (music->lily-string simple-element)
460                                         (duration->lily-string duration)
461                                         (if (and ((make-music-type-predicate 'RestEvent) simple-element)
462                                                  (ly:pitch? (ly:music-property simple-element 'pitch)))
463                                             "\\rest"
464                                             "")
465                                         (map-in-order music->lily-string (filter post-event? elements)))))
466               (*previous-duration* duration)
467               lily-string)
468             (let ((chord-elements (filter (make-music-type-predicate
469                                            'NoteEvent 'ClusterNoteEvent 'BassFigureEvent)
470                                           elements))
471                   (post-events (filter post-event? elements)))
472               (if (not (null? chord-elements))
473                   ;; note_chord_element : '<' (notepitch | drumpitch)* '>" duration post_events
474                   (let ((lily-string (format #f "< ~{~a ~}>~a~{~a ~}"
475                                              (map-in-order music->lily-string chord-elements)
476                                              (duration->lily-string (ly:music-property (car chord-elements)
477                                                                                      'duration))
478                                              (map-in-order music->lily-string post-events))))
479                     (*previous-duration* (ly:music-property (car chord-elements) 'duration))
480                     lily-string)
481                   ;; command_element
482                   (format #f "~{~a ~}" (map-in-order music->lily-string elements))))))))
484 (define-display-method MultiMeasureRestMusicGroup (mmrest)
485   (format #f "~{~a ~}"
486           (map-in-order music->lily-string 
487                         (remove (make-music-type-predicate 'BarCheck)
488                                 (ly:music-property mmrest 'elements)))))
490 (define-display-method SkipMusic (skip)
491   (format #f "\\skip ~a" (duration->lily-string (ly:music-property skip 'duration) #:force-duration #t)))
494 ;;; Notes, rests, skips...
497 (define (simple-note->lily-string event)
498   (format #f "~a~a~a~a~{~a~}" ; pitchname octave !? octave-check articulations
499           (note-name->lily-string (ly:music-property event 'pitch))
500           (octave->lily-string (ly:music-property event 'pitch))
501           (let ((forced (ly:music-property event 'force-accidental))
502                 (cautionary (ly:music-property event 'cautionary)))
503             (cond ((and (not (null? forced))
504                         forced
505                         (not (null? cautionary))
506                         cautionary)
507                    "?")
508                   ((and (not (null? forced)) forced) "!")
509                   (else "")))
510           (let ((octave-check (ly:music-property event 'absolute-octave)))
511             (if (not (null? octave-check))
512                 (format #f "=~a" (cond ((>= octave-check 0)
513                                         (make-string (1+ octave-check) #\'))
514                                        ((< octave-check -1)
515                                         (make-string (1- (* -1 octave-check)) #\,))
516                                        (else "")))
517                 ""))
518           (map-in-order music->lily-string (ly:music-property event 'articulations))))
520 (define-display-method NoteEvent (note)
521   (cond ((not (null? (ly:music-property note 'pitch))) ;; note
522          (simple-note->lily-string note))
523         ((not (null? (ly:music-property note 'drum-type))) ;; drum
524          (format #f "~a" (ly:music-property note 'drum-type)))
525         (else ;; unknown?
526          "")))
528 (define-display-method ClusterNoteEvent (note)
529   (simple-note->lily-string note))
531 (define-display-method RestEvent (rest)
532   (if (not (null? (ly:music-property rest 'pitch)))
533       (simple-note->lily-string rest)
534       "r"))
536 (define-display-method MultiMeasureRestEvent (rest)
537   "R")
539 (define-display-method SkipEvent (rest)
540   "s")
542 (define-display-method MarkEvent (mark)
543   (let ((label (ly:music-property mark 'label)))
544     (if (null? label)
545         "\\mark \\default"
546         (format #f "\\mark ~a" (markup->lily-string label)))))
548 (define-display-method MetronomeChangeEvent (tempo)
549   (format #f "\\tempo ~a = ~a"
550           (duration->lily-string (ly:music-property tempo 'tempo-unit) #:force-duration #f #:prev-duration #f)
551           (ly:music-property tempo 'metronome-count)))
553 (define-display-method KeyChangeEvent (key)
554   (let ((pitch-alist (ly:music-property key 'pitch-alist))
555         (tonic (ly:music-property key 'tonic)))
556     (if (or (null? pitch-alist)
557             (null? tonic))
558         "\\key \\default"
559         (let ((c-pitch-alist (ly:transpose-key-alist pitch-alist 
560                                                      (ly:pitch-diff (ly:make-pitch 0 0 0) tonic))))
561           (format #f "\\key ~a \\~a~a"
562                   (note-name->lily-string (ly:music-property key 'tonic))
563                   (any (lambda (mode)
564                          (if (and (*parser*)
565                                   (equal? (ly:parser-lookup (*parser*) mode) c-pitch-alist))
566                              (symbol->string mode)
567                              #f))
568                        '(major minor ionian locrian aeolian mixolydian lydian phrygian dorian))
569                   (new-line->lily-string))))))
571 (define-display-method RelativeOctaveCheck (octave)
572   (let ((pitch (ly:music-property octave 'pitch)))
573     (format #f "\\octave ~a~a"
574             (note-name->lily-string pitch)
575             (octave->lily-string pitch))))
577 (define-display-method VoiceSeparator (sep)
578   "\\\\")
580 (define-display-method LigatureEvent (ligature)
581   (if (= -1 (ly:music-property ligature 'span-direction))
582       "\\["
583       "\\]"))
585 (define-display-method BarCheck (check)
586   (format #f "|~a" (new-line->lily-string)))
588 (define-display-method BreakEvent (br)
589   "\\break") ;; TODO: use page-penalty, penalty properties?
591 (define-display-method PesOrFlexaEvent (expr)
592   "\\~")
594 (define-display-method BassFigureEvent (figure)
595   (let ((alteration (ly:music-property figure 'alteration))
596         (fig (ly:music-property figure 'figure))
597         (bracket-start (ly:music-property figure 'bracket-start))
598         (bracket-stop (ly:music-property figure 'bracket-stop)))
599     (format #f "~a~a~a~a"
600             (if (null? bracket-start) "" "[")
601             (if (null? fig) 
602                 "_"
603                 (second fig)) ;; fig: (<number-markup> "number")
604             (if (null? alteration)
605                 ""
606                 (case alteration
607                   ((-4) "--")
608                   ((-2) "-")
609                   ((0) "!")
610                   ((2) "+")
611                   ((4) "++")
612                   (else "")))
613             (if (null? bracket-stop) "" "]"))))
615 (define-display-method LyricEvent (lyric)
616   (let ((text (ly:music-property lyric 'text)))
617     (if (or (string? text)
618             (eqv? (first text) simple-markup))
619         ;; a string or a simple markup
620         (let ((string (if (string? text)
621                           text
622                           (second text))))
623           (if (string-match "(\"| |[0-9])" string)
624               ;; TODO check exactly in which cases double quotes should be used
625               (format #f "~s" string)
626               string))
627         (markup->lily-string text))))
629 (define-display-method BreathingSignEvent (event)
630   "\\breathe")
633 ;;; Staff switches
636 (define-display-method AutoChangeMusic (m)
637   (format #f "\\autochange ~a"
638           (music->lily-string (ly:music-property m 'element))))
640 (define-display-method ContextChange (m)
641   (format #f "\\change ~a = \"~a\""
642           (ly:music-property m 'change-to-type)
643           (ly:music-property m 'change-to-id)))
647 (define-display-method TimeScaledMusic (times)
648   (let* ((num (ly:music-property times 'numerator))
649          (den (ly:music-property times 'denominator))
650          (nd-gcd (gcd num den)))
651     (parameterize ((*force-line-break* #f)
652                    (*time-factor-numerator* (/ num nd-gcd))
653                    (*time-factor-denominator* (/ den nd-gcd)))
654       (format #f "\\times ~a/~a ~a" 
655               num
656               den
657               (music->lily-string (ly:music-property times 'element))))))
659 (define-display-method RelativeOctaveMusic (m)
660   (music->lily-string (ly:music-property m 'element)))
662 (define-display-method TransposedMusic (m)
663   (music->lily-string (ly:music-property m 'element)))
666 ;;; Repeats
669 (define (repeat->lily-string expr repeat-type)
670   (format #f "\\repeat ~a ~a ~a ~a"
671           repeat-type
672           (ly:music-property expr 'repeat-count)
673           (music->lily-string (ly:music-property expr 'element))
674           (let ((alternatives (ly:music-property expr 'elements)))
675             (if (null? alternatives)
676                 ""
677                 (format #f "\\alternative { ~{~a ~}}"
678                         (map-in-order music->lily-string alternatives))))))
680 (define-display-method VoltaRepeatedMusic (expr)
681   (repeat->lily-string expr "volta"))
683 (define-display-method UnfoldedRepeatedMusic (expr)
684   (repeat->lily-string expr "unfold"))
686 (define-display-method FoldedRepeatedMusic (expr)
687   (repeat->lily-string expr "fold"))
689 (define-display-method PercentRepeatedMusic (expr)
690   (repeat->lily-string expr "percent"))
692 (define-display-method TremoloRepeatedMusic (expr)
693   (let* ((count (ly:music-property expr 'repeat-count))
694          (dots (if (= 0 (modulo count 3)) 0 1))
695          (shift (- (log2 (if (= 0 dots)
696                              (/ (* count 2) 3)
697                              count))))
698          (element (ly:music-property expr 'element))
699          (den-mult 1))
700     (if (eqv? (ly:music-property element 'name) 'SequentialMusic)
701         (begin
702           (set! shift (1- shift))
703           (set! den-mult (length (ly:music-property element 'elements)))))
704     (music-map (lambda (m)
705                  (let ((duration (ly:music-property m 'duration)))
706                    (if (ly:duration? duration)
707                        (let* ((dlog (ly:duration-log duration))
708                               (ddots (ly:duration-dot-count duration))
709                               (dfactor (ly:duration-factor duration))
710                               (dnum (car dfactor))
711                               (dden (cdr dfactor)))
712                          (set! (ly:music-property m 'duration)
713                                (ly:make-duration (- dlog shift)
714                                                  ddots ;;(- ddots dots) ; ????
715                                                  dnum
716                                                  (/ dden den-mult))))))
717                  m)
718                element)
719     (format #f "\\repeat tremolo ~a ~a"
720             count
721             (music->lily-string element))))
724 ;;; Contexts
725 ;;; 
727 (define-display-method ContextSpeccedMusic (expr)
728   (let ((id    (ly:music-property expr 'context-id))
729         (music (ly:music-property expr 'element))
730         (operations (ly:music-property expr 'property-operations))
731         (ctype (ly:music-property expr 'context-type)))
732     (format #f "~a ~a~a~a ~a"
733             (if (and (not (null? id)) 
734                      (equal? id "$uniqueContextId"))
735                 "\\new"
736                 "\\context")
737             ctype
738             (if (or (null? id)
739                     (equal? id "$uniqueContextId"))
740                 ""
741                 (format #f " = ~s" id))
742             (if (null? operations)
743                 "" 
744                 (format #f " \\with {~{~a~}~%~v_}" 
745                         (parameterize ((*indent* (+ (*indent*) 2)))
746                           (map (lambda (op)
747                                  (format #f "~%~v_\\~a ~s"
748                                          (*indent*)
749                                          (first op)
750                                          (second op)))
751                                (reverse operations)))
752                         (*indent*)))
753             (parameterize ((*current-context* ctype))
754               (music->lily-string music)))))
756 ;; special cases: \figures \lyrics \drums
757 (define-extra-display-method ContextSpeccedMusic (expr)
758   (with-music-match (expr (music 'ContextSpeccedMusic
759                                  context-id "$uniqueContextId"
760                                  property-operations ?op
761                                  context-type ?context-type
762                                  element ?sequence))
763     (if (null? ?op)
764         (parameterize ((*explicit-mode* #f))
765           (case ?context-type
766             ((FiguredBass)
767              (format #f "\\figures ~a" (music->lily-string ?sequence)))
768             ((Lyrics)
769              (format #f "\\lyrics ~a" (music->lily-string ?sequence)))
770             ((DrumStaff)
771              (format #f "\\drums ~a" (music->lily-string ?sequence)))
772             (else
773              #f)))
774         #f)))
776 ;;; Context properties
778 (define-extra-display-method ContextSpeccedMusic (expr)
779   (let ((element (ly:music-property expr 'element))
780         (property-tuning? (make-music-type-predicate 'PropertySet
781                                                      'PropertyUnset
782                                                      'OverrideProperty
783                                                      'RevertProperty))
784         (sequence? (make-music-type-predicate 'SequentialMusic)))
785     (if (and (ly:music? element)
786              (or (property-tuning? element)
787                  (and (sequence? element)
788                       (every property-tuning? (ly:music-property element 'elements)))))
789         (parameterize ((*current-context* (ly:music-property expr 'context-type)))
790           (music->lily-string element))
791         #f)))
793 (define (property-value->lily-string arg)
794   (cond ((ly:music? arg)
795          (music->lily-string arg))
796         ((string? arg)
797          (format #f "#~s" arg))
798         ((markup? arg)
799          (markup->lily-string arg))
800         (else
801          (format #f "#~a" (scheme-expr->lily-string arg)))))
803 (define-display-method PropertySet (expr)
804   (let ((property (ly:music-property expr 'symbol))
805         (value (ly:music-property expr 'value))
806         (once (ly:music-property expr 'once)))
807     (format #f "~a\\set ~a~a = ~a~a"
808             (if (and (not (null? once)))
809                 "\\once "
810                 "")
811             (if (eqv? (*current-context*) 'Bottom) 
812                 "" 
813                 (format #f "~a . " (*current-context*)))
814             property
815             (property-value->lily-string value)
816             (new-line->lily-string))))
818 (define-display-method PropertyUnset (expr)
819   (format #f "\\unset ~a~a~a"
820           (if (eqv? (*current-context*) 'Bottom) 
821               "" 
822               (format #f "~a . " (*current-context*)))
823           (ly:music-property expr 'symbol)
824           (new-line->lily-string)))
826 ;;; Layout properties
828 (define-display-method OverrideProperty (expr)
829   (let ((symbol   (ly:music-property expr 'symbol))
830         (property (ly:music-property expr 'grob-property))
831         (value    (ly:music-property expr 'grob-value))
832         (once     (ly:music-property expr 'once)))
833     (format #f "~a\\override ~a~a #'~a = ~a~a"
834             (if (or (null? once)
835                     (not once))
836                 ""
837                 "\\once ")
838             (if (eqv? (*current-context*) 'Bottom) 
839                 "" 
840                 (format #f "~a . " (*current-context*)))
841             symbol
842             property
843             (property-value->lily-string value)
844             (new-line->lily-string))))
845             
846 (define-display-method RevertProperty (expr)
847   (let ((symbol (ly:music-property expr 'symbol))
848         (property (ly:music-property expr 'grob-property)))
849     (format #f "\\revert ~a~a #'~a~a"
850             (if (eqv? (*current-context*) 'Bottom) 
851                 "" 
852                 (format #f "~a . " (*current-context*)))
853             symbol
854             property
855             (new-line->lily-string))))
857 ;;; \clef 
858 (define clef-name-alist (map (lambda (name+vals)
859                                (cons (cdr name+vals)
860                                      (car name+vals)))
861                              supported-clefs))
863 (define-extra-display-method ContextSpeccedMusic (expr)
864   "If `expr' is a clef change, return \"\\clef ...\"
865 Otherwise, return #f."
866   (with-music-match (expr (music 'ContextSpeccedMusic
867                                  context-type 'Staff
868                                  element (music 'SequentialMusic
869                                                 elements ((music 'PropertySet
870                                                                  value ?clef-glyph
871                                                                  symbol 'clefGlyph)
872                                                           (music 'PropertySet
873                                                                  symbol 'middleCPosition)
874                                                           (music 'PropertySet
875                                                                  value ?clef-position
876                                                                  symbol 'clefPosition)
877                                                           (music 'PropertySet
878                                                                  value ?clef-octavation
879                                                                  symbol 'clefOctavation)))))
880     (let ((clef-prop+name (assoc (list ?clef-glyph ?clef-position 0)
881                                  clef-name-alist)))
882       (if clef-prop+name
883           (format #f "\\clef \"~a~{~a~a~}\"~a"
884                   (cdr clef-prop+name)
885                   (cond ((= 0 ?clef-octavation)
886                          (list "" ""))
887                         ((> ?clef-octavation 0)
888                          (list "^" (1+ ?clef-octavation)))
889                         (else
890                          (list "_" (- 1 ?clef-octavation))))
891                   (new-line->lily-string))
892           #f))))
894 ;;; \time
895 (define-extra-display-method ContextSpeccedMusic (expr)
896   "If `expr' is a time signature set, return \"\\time ...\".
897 Otherwise, return #f."
898   (with-music-match (expr (music 
899                            'ContextSpeccedMusic
900                            element (music 
901                                     'ContextSpeccedMusic
902                                     context-type 'Timing
903                                     element (music 
904                                              'SequentialMusic
905                                              elements ((music 
906                                                         'PropertySet
907                                                         value ?num+den
908                                                         symbol 'timeSignatureFraction)
909                                                        (music
910                                                         'PropertySet
911                                                         symbol 'beatLength)
912                                                        (music
913                                                         'PropertySet
914                                                         symbol 'measureLength)
915                                                        (music
916                                                         'PropertySet
917                                                         value ?grouping
918                                                         symbol 'beatGrouping))))))
919     (if (null? ?grouping)
920         (format #f "\\time ~a/~a~a" (car ?num+den) (cdr ?num+den) (new-line->lily-string))
921         (format #f "#(set-time-signature ~a ~a '~s)~a"
922                 (car ?num+den) (cdr ?num+den) ?grouping (new-line->lily-string)))))
924 ;;; \bar
925 (define-extra-display-method ContextSpeccedMusic (expr)
926   "If `expr' is a bar, return \"\\bar ...\".
927 Otherwise, return #f."
928   (with-music-match (expr (music
929                            'ContextSpeccedMusic
930                            element (music
931                                     'ContextSpeccedMusic
932                                     context-type 'Timing
933                                     element (music
934                                              'PropertySet
935                                              value ?bar-type
936                                              symbol 'whichBar))))
937      (format #f "\\bar \"~a\"~a" ?bar-type (new-line->lily-string))))
939 ;;; \partial
940 (define (duration->moment ly-duration)
941   (let ((log2    (ly:duration-log ly-duration))
942         (dots    (ly:duration-dot-count ly-duration))
943         (num+den (ly:duration-factor ly-duration)))
944     (let* ((m (expt 2 (- log2)))
945            (factor (/ (car num+den) (cdr num+den))))
946       (/ (do ((i 0 (1+ i))
947               (delta (/ m 2) (/ delta 2)))
948              ((= i dots) m)
949            (set! m (+ m delta)))
950          factor))))
951 (define moment-duration-alist (map (lambda (duration)
952                                      (cons (duration->moment duration)
953                                            duration))
954                                    (append-map (lambda (log2)
955                                                  (map (lambda (dots)
956                                                         (ly:make-duration log2 dots 1 1))
957                                                       (list 0 1 2 3)))
958                                                (list 0 1 2 3 4))))
960 (define (moment->duration moment)
961   (let ((result (assoc (- moment) moment-duration-alist)))
962     (and result 
963          (cdr result))))
965 (define-extra-display-method ContextSpeccedMusic (expr)
966   "If `expr' is a partial measure, return \"\\partial ...\".
967 Otherwise, return #f."
968   (with-music-match (expr (music
969                            'ContextSpeccedMusic
970                            element (music
971                                     'ContextSpeccedMusic
972                                     context-type 'Timing
973                                     element (music
974                                              'PropertySet
975                                              value ?moment
976                                              symbol 'measurePosition))))
977      (let ((duration (moment->duration (/ (ly:moment-main-numerator ?moment)
978                                           (ly:moment-main-denominator ?moment)))))
979        (and duration (format #f "\\partial ~a" (duration->lily-string duration #:force-duration #t))))))
984 (define-display-method ApplyOutputEvent (applyoutput)
985   (let ((proc (ly:music-property applyoutput 'procedure))))
986     (format #f "\\applyoutput #~a"
987             (or (procedure-name proc)
988                 (with-output-to-string
989                   (lambda ()
990                     (pretty-print (procedure-source proc)))))))
992 (define-display-method ApplyContext (applycontext)
993   (let ((proc (ly:music-property applycontext 'procedure))))
994     (format #f "\\applycontext #~a"
995             (or (procedure-name proc)
996                 (with-output-to-string
997                   (lambda ()
998                     (pretty-print (procedure-source proc)))))))
1000 ;;; \partcombine
1001 (define-display-method PartCombineMusic (expr)
1002   (format #f "\\partcombine ~{~a ~}"
1003           (map-in-order music->lily-string (ly:music-property expr 'elements))))
1005 (define-extra-display-method PartCombineMusic (expr)
1006   (with-music-match (expr (music 'PartCombineMusic
1007                                  elements ((music 'UnrelativableMusic
1008                                                   element (music 'ContextSpeccedMusic
1009                                                                  context-id "one"
1010                                                                  context-type 'Voice
1011                                                                  element ?sequence1))
1012                                            (music 'UnrelativableMusic
1013                                                   element (music 'ContextSpeccedMusic
1014                                                                  context-id "two"
1015                                                                  context-type 'Voice
1016                                                                  element ?sequence2)))))
1017     (format #f "\\partcombine ~a~a~a"
1018             (music->lily-string ?sequence1)
1019             (new-line->lily-string)
1020             (music->lily-string ?sequence2))))
1022 (define-display-method UnrelativableMusic (expr)
1023   (music->lily-string (ly:music-property expr 'element)))
1025 ;;; Cue notes
1026 (define-display-method QuoteMusic (expr)
1027   (or (with-music-match (expr (music
1028                                'QuoteMusic
1029                                quoted-voice-direction ?quoted-voice-direction
1030                                quoted-music-name ?quoted-music-name
1031                                quoted-context-id "cue"
1032                                quoted-context-type 'Voice
1033                                element ?music))
1034         (format #f "\\cueDuring #~s #~a ~a"
1035                 ?quoted-music-name
1036                 ?quoted-voice-direction
1037                 (music->lily-string ?music)))
1038       (format #f "\\quoteDuring #~s ~a"
1039               (ly:music-property expr 'quoted-music-name)
1040               (music->lily-string (ly:music-property expr 'element)))))
1043 ;;; Lyrics
1046 ;;; \lyricsto
1047 (define-display-method LyricCombineMusic (expr)
1048   (format #f "\\lyricsto ~s ~a"
1049           (ly:music-property expr 'associated-context)
1050           (parameterize ((*explicit-mode* #f))
1051             (music->lily-string (ly:music-property expr 'element)))))
1053 (define-display-method OldLyricCombineMusic (expr)
1054   (format #f "\\oldaddlyrics ~a~a~a"
1055           (music->lily-string (first (ly:music-property expr 'elements)))
1056           (new-line->lily-string)
1057           (music->lily-string (second (ly:music-property expr 'elements)))))
1059 ;; \addlyrics
1060 (define-extra-display-method SimultaneousMusic (expr)
1061   (with-music-match (expr (music 'SimultaneousMusic
1062                                  elements ((music 'ContextSpeccedMusic
1063                                                   context-id ?id
1064                                                   ;;property-operations '()
1065                                                   context-type 'Voice
1066                                                   element ?note-sequence)
1067                                            (music 'ContextSpeccedMusic
1068                                                   context-id "$uniqueContextId"
1069                                                   ;;property-operations '()
1070                                                   context-type 'Lyrics
1071                                                   element (music 'LyricCombineMusic
1072                                                                  associated-context ?associated-id
1073                                                                  element ?lyric-sequence)))))
1074     (if (string=? ?id ?associated-id)
1075         (format #f "~a~a \\addlyrics ~a"
1076                 (music->lily-string ?note-sequence)
1077                 (new-line->lily-string)
1078                 (parameterize ((*explicit-mode* #f))
1079                   (music->lily-string ?lyric-sequence)))
1080         #f)))