lotus / node-addon-lilypond /output /share /lilypond /current /scm /define-music-display-methods.scm
k-l-lambda's picture
added node-addon-lilypond
f65fe85
;;; define-music-display-methods.scm -- data for displaying music
;;; expressions using LilyPond notation.
;;;
;;; Copyright (C) 2005--2020 Nicolas Sceaux <[email protected]>
;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;; Display method implementation
;;;
(define-module (scm display-lily))
;;;
;;; Scheme forms
;;;
(define (scheme-expr->lily-string scm-arg)
(cond ((or (number? scm-arg)
(string? scm-arg)
(boolean? scm-arg))
(format #f "~s" scm-arg))
((or (symbol? scm-arg)
(list? scm-arg))
(format #f "'~s" scm-arg))
((procedure? scm-arg)
(format #f "~a"
(or (procedure-name scm-arg)
(with-output-to-string
(lambda ()
(pretty-print (procedure-source scm-arg)))))))
(else
(format #f "~a"
(with-output-to-string
(lambda ()
(display-scheme-music scm-arg)))))))
;;;
;;; Markups
;;;
(define-public (markup->lily-string markup-expr)
"Return a string describing, in LilyPond syntax, the given markup
expression."
(define (proc->command proc)
(let ((cmd-markup (symbol->string (procedure-name proc))))
(substring cmd-markup 0 (- (string-length cmd-markup)
(string-length "-markup")))))
(define (arg->string arg)
(cond ((string? arg)
(format #f "~s" arg))
((markup? arg) ;; a markup
(markup->lily-string-aux arg))
((and (pair? arg) (every markup? arg)) ;; a markup list
(format #f "{~{ ~a~}}" (map-in-order markup->lily-string-aux arg)))
(else ;; a scheme argument
(format #f "#~a" (scheme-expr->lily-string arg)))))
(define (markup->lily-string-aux expr)
(if (string? expr)
(format #f "~s" expr)
(let ((cmd (car expr))
(args (cdr expr)))
(if (eqv? cmd simple-markup) ;; a simple markup
(format #f "~s" (car args))
(format #f "\\~a~{ ~a~}"
(proc->command cmd)
(map-in-order arg->string args))))))
(cond ((string? markup-expr)
(format #f "~s" markup-expr))
((eqv? (car markup-expr) simple-markup)
(format #f "~s" (second markup-expr)))
(else
(format #f "\\markup ~a"
(markup->lily-string-aux markup-expr)))))
;;;
;;; pitch names
;;;
(define-public (note-name->lily-string ly-pitch)
;; here we define a custom pitch= function, since we do not want to
;; test whether octaves are also equal. (otherwise, we would be using equal?)
(define (pitch= pitch1 pitch2)
(and (= (ly:pitch-notename pitch1) (ly:pitch-notename pitch2))
(= (ly:pitch-alteration pitch1) (ly:pitch-alteration pitch2))))
(let* ((result (rassoc ly-pitch pitchnames pitch=)))
(and result (car result))))
(define-public (octave->lily-string pitch)
(let ((octave (ly:pitch-octave pitch)))
(cond ((>= octave 0)
(make-string (1+ octave) #\'))
((< octave -1)
(make-string (1- (* -1 octave)) #\,))
(else ""))))
;;;
;;; durations
;;;
(define*-public (duration->lily-string ly-duration #:key
(force-duration #f)
(time-scale (*time-scale*)))
(let ((log2 (ly:duration-log ly-duration))
(dots (ly:duration-dot-count ly-duration))
(scale (ly:duration-scale ly-duration)))
(if (or force-duration (not (*omit-duration*)))
(string-append (case log2
((-1) "\\breve")
((-2) "\\longa")
((-3) "\\maxima")
(else (number->string (expt 2 log2))))
(make-string dots #\.)
(let ((end-scale (/ scale time-scale)))
(if (= end-scale 1) ""
(format #f "*~a" end-scale))))
"")))
;;;
;;; post events
;;;
(define post-event? (music-type-predicate 'post-event))
(define* (event-direction->lily-string event #:optional (required #t))
(let ((direction (ly:music-property event 'direction)))
(cond ((or (not direction) (null? direction) (= CENTER direction))
(if required "-" ""))
((= UP direction) "^")
((= DOWN direction) "_")
(else ""))))
(define-macro (define-post-event-display-method type vars direction-required str)
`(define-display-method ,type ,vars
(format #f "~a~a"
(event-direction->lily-string ,(car vars) ,direction-required)
,str)))
(define-macro (define-span-event-display-method type vars direction-required str-start str-stop)
`(define-display-method ,type ,vars
(format #f "~a~a"
(event-direction->lily-string ,(car vars) ,direction-required)
(if (= START (ly:music-property ,(car vars) 'span-direction))
,str-start
,str-stop))))
(define-display-method HyphenEvent (event)
" --")
(define-display-method ExtenderEvent (event)
" __")
(define-display-method TieEvent (event)
" ~")
(define-display-method DurationLineEvent (event)
"\\-")
(define-display-method BeamForbidEvent (event)
"\\noBeam")
(define-display-method StringNumberEvent (event)
(format #f "\\~a" (ly:music-property event 'string-number)))
(define-display-method TremoloEvent (event)
(let ((tremolo-type (ly:music-property event 'tremolo-type 8)))
(format #f ":~a" tremolo-type)))
(define-display-method ArticulationEvent (event) #t
(let* ((articulation (ly:music-property event 'articulation-type))
(shorthand
(case (string->symbol articulation)
((marcato) "^")
((stopped) "+")
((tenuto) "-")
((staccatissimo) "!")
((accent) ">")
((staccato) ".")
((portato) "_")
(else #f))))
(format #f "~a~:[\\~;~]~a"
(event-direction->lily-string event shorthand)
shorthand
(or shorthand articulation))))
(define-display-method MultiMeasureArticulationEvent (event) #t
(let* ((articulation (ly:music-property event 'articulation-type))
(shorthand
(case (string->symbol articulation)
((marcato) "^")
((stopped) "+")
((tenuto) "-")
((staccatissimo) "!")
((accent) ">")
((staccato) ".")
((portato) "_")
(else #f))))
(format #f "~a~:[\\~;~]~a"
(event-direction->lily-string event shorthand)
shorthand
(or shorthand articulation))))
(define-post-event-display-method FingeringEvent (event) #t
(ly:music-property event 'digit))
(define-post-event-display-method TextScriptEvent (event) #t
(markup->lily-string (ly:music-property event 'text)))
(define-post-event-display-method MultiMeasureTextEvent (event) #t
(markup->lily-string (ly:music-property event 'text)))
(define-post-event-display-method BendAfterEvent (event) #f
(format #f "\\bendAfter #~a " (ly:music-property event 'delta-step)))
(define-post-event-display-method HarmonicEvent (event) #f "\\harmonic")
(define-post-event-display-method GlissandoEvent (event) #f "\\glissando")
(define-post-event-display-method ArpeggioEvent (event) #f "\\arpeggio")
(define-post-event-display-method AbsoluteDynamicEvent (event) #f
(format #f "\\~a" (ly:music-property event 'text)))
(define-post-event-display-method StrokeFingerEvent (event) #f
(format #f "\\rightHandFinger #~a " (ly:music-property event 'digit)))
(define-span-event-display-method BeamEvent (event) #f "[" "]")
(define-span-event-display-method SlurEvent (event) #f "(" ")")
(define-span-event-display-method CrescendoEvent (event) #f "\\<" "\\!")
(define-span-event-display-method DecrescendoEvent (event) #f "\\>" "\\!")
(define-span-event-display-method EpisemaEvent (event) #f "\\episemInitium" "\\episemFinis")
(define-span-event-display-method PhrasingSlurEvent (event) #f "\\(" "\\)")
(define-span-event-display-method SustainEvent (event) #f "\\sustainOn" "\\sustainOff")
(define-span-event-display-method SostenutoEvent (event) #f "\\sostenutoOn" "\\sostenutoOff")
(define-span-event-display-method TextSpanEvent (event) #f "\\startTextSpan" "\\stopTextSpan")
(define-span-event-display-method TrillSpanEvent (event) #f "\\startTrillSpan" "\\stopTrillSpan")
(define-span-event-display-method StaffSpanEvent (event) #f "\\startStaff" "\\stopStaff")
(define-span-event-display-method NoteGroupingEvent (event) #f "\\startGroup" "\\stopGroup")
(define-span-event-display-method UnaCordaEvent (event) #f "\\unaCorda" "\\treCorde")
;;;
;;; Graces
;;;
(define-display-method GraceMusic (expr)
(format #f "\\grace ~a"
(music->lily-string (ly:music-property expr 'element))))
;; \acciaccatura \appoggiatura \grace
;; TODO: it would be better to compare ?start and ?stop
;; with startAppoggiaturaMusic and stopAppoggiaturaMusic,
;; using a custom music equality predicate.
(define-extra-display-method GraceMusic (expr)
"Display method for appoggiatura."
(with-music-match (expr (music
'GraceMusic
element (music
'SequentialMusic
elements (?start
?music
?stop))))
;; we check whether ?start and ?stop look like
;; startAppoggiaturaMusic stopAppoggiaturaMusic
(and (with-music-match (?start (music
'SequentialMusic
elements ((music
'EventChord
elements
((music
'SlurEvent
span-direction START))))))
#t)
(with-music-match (?stop (music
'SequentialMusic
elements ((music
'EventChord
elements
((music
'SlurEvent
span-direction STOP))))))
(format #f "\\appoggiatura ~a" (music->lily-string ?music))))))
(define-extra-display-method GraceMusic (expr)
"Display method for acciaccatura."
(with-music-match (expr (music
'GraceMusic
element (music
'SequentialMusic
elements (?start
?music
?stop))))
;; we check whether ?start and ?stop look like
;; startAcciaccaturaMusic stopAcciaccaturaMusic
(and (with-music-match (?start (music
'SequentialMusic
elements ((music
'EventChord
elements
((music
'SlurEvent
span-direction START)))
(music
'ContextSpeccedMusic
element (music
'OverrideProperty
grob-property-path '(stroke-style)
grob-value "grace"
symbol 'Flag)))))
#t)
(with-music-match (?stop (music
'SequentialMusic
elements ((music
'ContextSpeccedMusic
element (music
'RevertProperty
grob-property-path '(stroke-style)
symbol 'Flag))
(music
'EventChord
elements
((music
'SlurEvent
span-direction STOP))))))
(format #f "\\acciaccatura ~a" (music->lily-string ?music))))))
(define-extra-display-method GraceMusic (expr)
"Display method for grace."
(with-music-match (expr (music
'GraceMusic
element (music
'SequentialMusic
elements (?start
?music
?stop))))
;; we check whether ?start and ?stop look like
;; startGraceMusic stopGraceMusic
(and (null? (ly:music-property ?start 'elements))
(null? (ly:music-property ?stop 'elements))
(format #f "\\grace ~a" (music->lily-string ?music)))))
;;;
;;; Music sequences
;;;
(define-display-method SequentialMusic (seq)
(let ((force-line-break (and (*force-line-break*)
;; hm
(> (length (ly:music-property seq 'elements))
(*max-element-number-before-break*))))
(elements (ly:music-property seq 'elements))
(chord? (make-music-type-predicate 'EventChord))
(note-or-chord? (make-music-type-predicate 'EventChord 'NoteEvent
'LyricEvent 'RestEvent
'ClusterNoteEvent))
(cluster? (make-music-type-predicate 'ClusterNoteEvent))
(note? (make-music-type-predicate 'NoteEvent)))
(format #f "~a~a{~v%~v_~{~a~^ ~}~v%~v_}"
(if (any (lambda (e)
(or (cluster? e)
(and (chord? e)
(any cluster? (ly:music-property e 'elements)))))
elements)
"\\makeClusters "
"")
(if (*explicit-mode*)
;; if the sequence contains EventChord which contains figures ==> figuremode
;; if the sequence contains EventChord which contains lyrics ==> lyricmode
;; if the sequence contains EventChord which contains drum notes ==> drummode
(cond ((any (lambda (chord)
(any (make-music-type-predicate 'BassFigureEvent)
(ly:music-property chord 'elements)))
(filter chord? elements))
"\\figuremode ")
((any (lambda (chord)
(any (make-music-type-predicate 'LyricEvent)
(cons chord
(ly:music-property chord 'elements))))
(filter note-or-chord? elements))
"\\lyricmode ")
((any (lambda (chord)
(any (lambda (event)
(and (note? event)
(not (null? (ly:music-property event 'drum-type)))))
(cons chord
(ly:music-property chord 'elements))))
(filter note-or-chord? elements))
"\\drummode ")
(else ;; TODO: other modes?
""))
"")
(if force-line-break 1 0)
(if force-line-break (+ 2 (*indent*)) 1)
(parameterize ((*indent* (+ 2 (*indent*))))
(map-in-order (lambda (music)
(music->lily-string music))
elements))
(if force-line-break 1 0)
(if force-line-break (*indent*) 1))))
(define-display-method SimultaneousMusic (sim)
(parameterize ((*indent* (+ 3 (*indent*))))
(format #f "<< ~{~a ~}>>"
(map-in-order (lambda (music)
(music->lily-string music))
(ly:music-property sim 'elements)))))
;;;
;;; Chords
;;;
(define-display-method EventChord (chord)
;; event_chord : command_element
;; | note_chord_element
;; TODO : tagged post_events
;; post_events : ( post_event | tagged_post_event )*
;; tagged_post_event: '-' \tag embedded_scm post_event
(let* ((elements (append (ly:music-property chord 'elements)
(ly:music-property chord 'articulations)))
(chord-repeat (ly:music-property chord 'duration)))
(call-with-values
(lambda ()
(partition (music-type-predicate 'rhythmic-event)
elements))
(lambda (chord-elements other-elements)
(cond ((pair? chord-elements)
;; note_chord_element :
;; '<' (notepitch | drumpitch)* '>" duration post_events
(let ((duration (duration->lily-string (ly:music-property
(car chord-elements)
'duration))))
;; Format duration first so that it does not appear on
;; chord elements
(format #f "< ~{~a ~}>~a~:{~:[-~;~]~a~^ ~}"
(parameterize ((*omit-duration* #t))
(map-in-order
(lambda (music)
(music->lily-string music))
chord-elements))
duration
(map-in-order (lambda (music)
(list
(post-event? music)
(music->lily-string music)))
other-elements))))
((ly:duration? chord-repeat)
(let ((duration (duration->lily-string chord-repeat)))
(format #f "q~a~:{~:[-~;~]~a~^ ~}"
duration
(map-in-order (lambda (music)
(list
(post-event? music)
(music->lily-string music)))
other-elements))))
((and (= 1 (length other-elements))
(not (post-event? (car other-elements))))
(format #f (music->lily-string (car other-elements))))
(else
(format #f "< >~:{~:[-~;~]~a~^ ~}"
(map-in-order (lambda (music)
(list
(post-event? music)
(music->lily-string music)))
other-elements))))))))
(define-display-method MultiMeasureRestMusic (mmrest)
(format #f "R~a~{~a~^ ~}"
(duration->lily-string (ly:music-property mmrest 'duration))
(map-in-order (lambda (music)
(music->lily-string music))
(ly:music-property mmrest 'articulations))))
(define-display-method SkipMusic (skip)
(format #f "\\skip ~a" (duration->lily-string (ly:music-property skip 'duration) #:force-duration #t)))
(define-display-method OttavaMusic (ottava)
(format #f "\\ottava #~a" (ly:music-property ottava 'ottava-number)))
;;;
;;; Notes, rests, skips...
;;;
(define (simple-note->lily-string event)
(format #f "~a~a~a~a~a~a~:{~:[-~;~]~a~}" ; pitchname octave !? octave-check duration optional_rest articulations
(note-name->lily-string (ly:music-property event 'pitch))
(octave->lily-string (ly:music-property event 'pitch))
(let ((forced (ly:music-property event 'force-accidental))
(cautionary (ly:music-property event 'cautionary)))
(cond ((and (not (null? forced))
forced
(not (null? cautionary))
cautionary)
"?")
((and (not (null? forced)) forced) "!")
(else "")))
(let ((octave-check (ly:music-property event 'absolute-octave)))
(if (not (null? octave-check))
(format #f "=~a" (cond ((>= octave-check 0)
(make-string (1+ octave-check) #\'))
((< octave-check -1)
(make-string (1- (* -1 octave-check)) #\,))
(else "")))
""))
(duration->lily-string (ly:music-property event 'duration))
(if ((make-music-type-predicate 'RestEvent) event)
"\\rest" "")
(map-in-order (lambda (event)
(list
(post-event? event)
(music->lily-string event)))
(ly:music-property event 'articulations))))
(define-display-method NoteEvent (note)
(cond ((not (null? (ly:music-property note 'pitch))) ;; note
(simple-note->lily-string note))
((not (null? (ly:music-property note 'drum-type))) ;; drum
(format #f "~a~a~{~a~}" (ly:music-property note 'drum-type)
(duration->lily-string (ly:music-property note 'duration))
(map-in-order (lambda (event)
(music->lily-string event))
(ly:music-property note 'articulations))))
(else
;; pure duration
(format #f "~a~{~a~}"
(duration->lily-string (ly:music-property note 'duration)
#:force-duration #t)
(map-in-order (lambda (event)
(music->lily-string event))
(ly:music-property note 'articulations))))))
(define-display-method ClusterNoteEvent (note)
(simple-note->lily-string note))
(define-display-method RestEvent (rest)
(if (not (null? (ly:music-property rest 'pitch)))
(simple-note->lily-string rest)
(format #f "r~a~{~a~}"
(duration->lily-string (ly:music-property rest 'duration))
(map-in-order (lambda (event)
(music->lily-string event))
(ly:music-property rest 'articulations)))))
(define-display-method MultiMeasureRestEvent (rest)
(string-append "R" (duration->lily-string (ly:music-property rest 'duration))))
(define-display-method SkipEvent (rest)
(format #f "s~a~{~a~}"
(duration->lily-string (ly:music-property rest 'duration))
(map-in-order (lambda (event)
(music->lily-string event))
(ly:music-property rest 'articulations))))
(define-display-method RepeatedChord (chord)
(music->lily-string (ly:music-property chord 'element)))
(define-display-method MarkEvent (mark)
(let ((label (ly:music-property mark 'label #f)))
(string-append "\\mark "
(if label (value->lily-string label) "\\default"))))
(define-display-method KeyChangeEvent (key)
(let ((pitch-alist (ly:music-property key 'pitch-alist))
(tonic (ly:music-property key 'tonic)))
(if (or (null? pitch-alist)
(null? tonic))
"\\key \\default"
(let ((c-pitch-alist (ly:transpose-key-alist pitch-alist
(ly:pitch-diff (ly:make-pitch 0 0 0) tonic))))
(format #f "\\key ~a \\~a~a"
(note-name->lily-string (ly:music-property key 'tonic))
(any (lambda (mode)
(and (equal? (ly:parser-lookup mode) c-pitch-alist)
(symbol->string mode)))
'(major minor ionian locrian aeolian mixolydian lydian phrygian dorian))
(new-line->lily-string))))))
(define-display-method RelativeOctaveCheck (octave)
(let ((pitch (ly:music-property octave 'pitch)))
(format #f "\\octaveCheck ~a~a"
(note-name->lily-string pitch)
(octave->lily-string pitch))))
(define-display-method VoiceSeparator (sep)
"\\\\")
(define-display-method LigatureEvent (ligature)
(if (= START (ly:music-property ligature 'span-direction))
"\\["
"\\]"))
(define-display-method BarCheck (check)
(format #f "|~a" (new-line->lily-string)))
(define-display-method PesOrFlexaEvent (expr)
"\\~")
(define-display-method BassFigureEvent (figure)
(let ((alteration (ly:music-property figure 'alteration))
(fig (ly:music-property figure 'figure))
(bracket-start (ly:music-property figure 'bracket-start))
(bracket-stop (ly:music-property figure 'bracket-stop)))
(format #f "~a~a~a~a"
(if (null? bracket-start) "" "[")
(cond ((null? fig) "_")
((markup? fig) (second fig)) ;; fig: (<number-markup> "number")
(else fig))
(if (null? alteration)
""
(cond
((= alteration DOUBLE-FLAT) "--")
((= alteration FLAT) "-")
((= alteration NATURAL) "!")
((= alteration SHARP) "+")
((= alteration DOUBLE-SHARP) "++")
(else "")))
(if (null? bracket-stop) "" "]"))))
(define-display-method LyricEvent (lyric)
(format #f "~a~{~a~^ ~}"
(let ((text (ly:music-property lyric 'text)))
(if (or (string? text)
(eqv? (first text) simple-markup))
;; a string or a simple markup
(let ((string (if (string? text)
text
(second text))))
(if (string-match "(\"| |[0-9])" string)
;; TODO check exactly in which cases double quotes should be used
(format #f "~s" string)
string))
(markup->lily-string text)))
(map-in-order music->lily-string
(ly:music-property lyric 'articulations))))
(define-display-method BreathingEvent (event)
"\\breathe")
;;;
;;; Staff switches
;;;
(define-display-method AutoChangeMusic (m)
(format #f "\\autoChange ~a"
(music->lily-string
(ly:music-property (ly:music-property m 'element) 'element))))
(define-display-method ContextChange (m)
(format #f "\\change ~a = \"~a\""
(ly:music-property m 'change-to-type)
(ly:music-property m 'change-to-id)))
;;;
(define-display-method TimeScaledMusic (times)
(let* ((num (ly:music-property times 'numerator))
(den (ly:music-property times 'denominator))
(span (ly:music-property times 'duration #f))
;; need to format before changing time scale
(formatted-span
(and span (duration->lily-string span #:force-duration #t)))
(scale (/ num den))
(time-scale (*time-scale*)))
(let ((result
(parameterize ((*force-line-break* #f)
(*time-scale* (* time-scale scale)))
(format #f "\\tuplet ~a/~a ~@[~a ~]~a"
den
num
formatted-span
(music->lily-string (ly:music-property times 'element))))))
result)))
(define-display-method RelativeOctaveMusic (m)
(format #f "\\absolute ~a"
(music->lily-string (ly:music-property m 'element))))
(define-display-method TransposedMusic (m)
(music->lily-string (ly:music-property m 'element)))
;;;
;;; Repeats
;;;
(define-display-method AlternativeEvent (alternative) "")
(define (repeat->lily-string expr repeat-type)
(let* ((main (music->lily-string (ly:music-property expr 'element))))
(format #f "\\repeat ~a ~a ~a ~a"
repeat-type
(ly:music-property expr 'repeat-count)
main
(let ((alternatives (ly:music-property expr 'elements)))
(if (null? alternatives)
""
(format #f "\\alternative { ~{~a ~}}"
(map-in-order (lambda (music)
(music->lily-string music))
alternatives)))))))
(define-display-method VoltaRepeatedMusic (expr)
(repeat->lily-string expr "volta"))
(define-display-method UnfoldedRepeatedMusic (expr)
(repeat->lily-string expr "unfold"))
(define-display-method PercentRepeatedMusic (expr)
(repeat->lily-string expr "percent"))
(define-display-method TremoloRepeatedMusic (expr)
(repeat->lily-string expr "tremolo"))
;;;
;;; Contexts
;;;
(define-display-method ContextSpeccedMusic (expr)
(let ((id (ly:music-property expr 'context-id))
(create-new (ly:music-property expr 'create-new))
(music (ly:music-property expr 'element))
(operations (ly:music-property expr 'property-operations))
(ctype (ly:music-property expr 'context-type)))
(format #f "~a ~a~a~a ~a"
(if (and (not (null? create-new)) create-new)
"\\new"
"\\context")
ctype
(if (null? id)
""
(format #f " = ~s" id))
(if (null? operations)
""
(format #f " \\with {~{~a~}~%~v_}"
(parameterize ((*indent* (+ (*indent*) 2)))
(map (lambda (op)
(format #f "~%~v_\\~a ~s"
(*indent*)
(first op)
(second op)))
operations))
(*indent*)))
(parameterize ((*current-context* ctype))
(music->lily-string music)))))
;; \afterGrace
(define-extra-display-method ContextSpeccedMusic (expr)
"If `sim' is an \afterGrace expression, return \"\\afterGrace ...\".
Otherwise, return #f."
;; TODO: do something with afterGraceFraction?
(with-music-match
(expr (music 'ContextSpeccedMusic
context-type 'Bottom
element
(music 'SimultaneousMusic
elements (?before-grace
(music 'SequentialMusic
elements ((music 'SkipMusic)
(music 'GraceMusic
element ?grace)))))))
(format #f "\\afterGrace ~a ~a"
(music->lily-string ?before-grace)
(music->lily-string ?grace))))
;; special cases: \figures \lyrics \drums
(define-extra-display-method ContextSpeccedMusic (expr)
(with-music-match (expr (music 'ContextSpeccedMusic
create-new #t
property-operations ?op
context-type ?context-type
element ?sequence))
(if (null? ?op)
(parameterize ((*explicit-mode* #f))
(case ?context-type
((FiguredBass)
(format #f "\\figures ~a" (music->lily-string ?sequence)))
((Lyrics)
(format #f "\\lyrics ~a" (music->lily-string ?sequence)))
((DrumStaff)
(format #f "\\drums ~a" (music->lily-string ?sequence)))
(else
#f)))
#f)))
;;; Context properties
(define-extra-display-method ContextSpeccedMusic (expr)
(let ((element (ly:music-property expr 'element))
(property-tuning? (make-music-type-predicate 'PropertySet
'PropertyUnset
'OverrideProperty
'RevertProperty))
(sequence? (make-music-type-predicate 'SequentialMusic)))
(if (and (ly:music? element)
(or (property-tuning? element)
(and (sequence? element)
(every property-tuning? (ly:music-property element 'elements)))))
(parameterize ((*current-context* (ly:music-property expr 'context-type)))
(music->lily-string element))
#f)))
(define-public (value->lily-string arg)
(cond ((ly:music? arg)
(music->lily-string arg))
((markup? arg)
(markup->lily-string arg))
((ly:duration? arg)
(format #f "##{ ~a #}" (duration->lily-string arg #:force-duration #t)))
((ly:pitch? arg)
(format #f "~a~a"
(note-name->lily-string arg)
(octave->lily-string arg)))
(else
(format #f "#~a" (scheme-expr->lily-string arg)))))
(define-display-method PropertySet (expr)
(let ((property (ly:music-property expr 'symbol))
(value (ly:music-property expr 'value))
(once (ly:music-property expr 'once)))
(format #f "~a\\set ~a~a = ~a~a"
(if (and (not (null? once)))
"\\once "
"")
(if (eq? (*current-context*) 'Bottom)
""
(format #f "~a." (*current-context*)))
property
(value->lily-string value)
(new-line->lily-string))))
(define-display-method PropertyUnset (expr)
(format #f "~a\\unset ~a~a~a"
(if (ly:music-property expr 'once #f) "\\once " "")
(if (eq? (*current-context*) 'Bottom)
""
(format #f "~a." (*current-context*)))
(ly:music-property expr 'symbol)
(new-line->lily-string)))
;;; Layout properties
(define-display-method OverrideProperty (expr)
(let* ((symbol (ly:music-property expr 'symbol))
(properties (ly:music-property expr 'grob-property-path
(list (ly:music-property expr 'grob-property))))
(value (ly:music-property expr 'grob-value))
(once (ly:music-property expr 'once)))
(format #f "~a\\override ~{~a~^.~} = ~a~a"
(if (or (null? once)
(not once))
""
"\\once ")
(if (eqv? (*current-context*) 'Bottom)
(cons symbol properties)
(cons* (*current-context*) symbol properties))
(value->lily-string value)
(new-line->lily-string))))
(define-display-method RevertProperty (expr)
(let* ((symbol (ly:music-property expr 'symbol))
(properties (ly:music-property expr 'grob-property-path
(list (ly:music-property expr
'grob-property))))
(once (ly:music-property expr 'once #f)))
(format #f "~a\\revert ~{~a~^.~}~a"
(if once "\\once " "")
(if (eqv? (*current-context*) 'Bottom)
(cons symbol properties)
(cons* (*current-context*) symbol properties))
(new-line->lily-string))))
(define-display-method TimeSignatureMusic (expr)
(let* ((num (ly:music-property expr 'numerator))
(den (ly:music-property expr 'denominator))
(structure (ly:music-property expr 'beat-structure)))
(if (null? structure)
(format #f
"\\time ~a/~a~a"
num den
(new-line->lily-string))
(format #f
;; This is silly but the latter will also work for #f
;; and other
(if (key-list? structure)
"\\time ~{~a~^,~} ~a/~a~a"
"\\time #'~a ~a/~a~a")
structure num den
(new-line->lily-string)))))
;;; \melisma and \melismaEnd
(define-extra-display-method ContextSpeccedMusic (expr)
"If expr is a melisma, return \"\\melisma\", otherwise, return #f."
(with-music-match (expr (music 'ContextSpeccedMusic
element (music 'PropertySet
value #t
symbol 'melismaBusy)))
"\\melisma"))
(define-extra-display-method ContextSpeccedMusic (expr)
"If expr is a melisma end, return \"\\melismaEnd\", otherwise, return #f."
(with-music-match (expr (music 'ContextSpeccedMusic
element (music 'PropertyUnset
symbol 'melismaBusy)))
"\\melismaEnd"))
;;; \tempo
(define-extra-display-method SequentialMusic (expr)
(with-music-match (expr (music 'SequentialMusic
elements ((music 'TempoChangeEvent
text ?text
tempo-unit ?unit
metronome-count ?count)
(music 'ContextSpeccedMusic
element (music 'PropertySet
symbol 'tempoWholesPerMinute)))))
(format #f "\\tempo ~{~a~a~}~a = ~a~a"
(if (markup? ?text)
(list (markup->lily-string ?text) " ")
'())
(duration->lily-string ?unit #:force-duration #t)
(if (pair? ?count)
(format #f "~a - ~a" (car ?count) (cdr ?count))
?count)
(new-line->lily-string))))
(define-display-method TempoChangeEvent (expr)
(let ((text (ly:music-property expr 'text)))
(format #f "\\tempo ~a~a"
(markup->lily-string text)
(new-line->lily-string))))
;;; \clef
(define clef-name-alist #f)
(define-public (memoize-clef-names clefs)
"Initialize @code{clef-name-alist}, if not already set."
(if (not clef-name-alist)
(set! clef-name-alist
(map (lambda (name+vals)
(cons (cdr name+vals)
(car name+vals)))
clefs))))
(define-extra-display-method ContextSpeccedMusic (expr)
"If @var{expr} is a clef change, return \"\\clef ...\".
Otherwise, return @code{#f}."
(with-music-match (expr (music 'ContextSpeccedMusic
context-type 'Staff
element (music 'SequentialMusic
elements ((music 'PropertySet
value ?clef-glyph
symbol 'clefGlyph)
(music 'PropertySet
symbol 'middleCClefPosition)
(music 'PropertySet
value ?clef-position
symbol 'clefPosition)
(music 'PropertySet
value ?clef-transposition
symbol 'clefTransposition)
(music 'PropertySet
value ?clef-transposition-style
symbol 'clefTranspositionStyle)
(music 'ApplyContext
procedure ly:set-middle-C!)))))
(let ((clef-name (assoc-get (list ?clef-glyph ?clef-position 0)
clef-name-alist)))
(and clef-name
(format #f "\\clef \"~a~?\"~a"
clef-name
(case ?clef-transposition-style
((parenthesized) "~a(~a)")
((bracketed) "~a[~a]")
(else "~a~a"))
(cond ((zero? ?clef-transposition)
(list "" ""))
((positive? ?clef-transposition)
(list "^" (1+ ?clef-transposition)))
(else (list "_" (- 1 ?clef-transposition))))
(new-line->lily-string))))))
;;; \bar
(define-extra-display-method ContextSpeccedMusic (expr)
"If `expr' is a bar, return \"\\bar ...\".
Otherwise, return #f."
(with-music-match (expr (music 'ContextSpeccedMusic
context-type 'Timing
element (music 'PropertySet
value ?bar-type
symbol 'whichBar)))
(format #f "\\bar \"~a\"~a" ?bar-type (new-line->lily-string))))
;;; \partial
(define-extra-display-method ContextSpeccedMusic (expr)
"If `expr' is a partial measure, return \"\\partial ...\".
Otherwise, return #f."
(with-music-match (expr (music
'ContextSpeccedMusic
context-type 'Timing
element (music
'PartialSet
duration ?duration)))
(and ?duration
(format #f "\\partial ~a"
(duration->lily-string ?duration #:force-duration #t)))))
;;;
;;;
(define-display-method ApplyOutputEvent (applyoutput)
(let ((proc (ly:music-property applyoutput 'procedure))
(ctx (ly:music-property applyoutput 'context-type))
(grob (ly:music-property applyoutput 'symbol)))
(format #f "\\applyOutput ~a~@[.~a~] #~a"
ctx
(and (symbol? grob) grob)
(or (procedure-name proc)
(with-output-to-string
(lambda ()
(pretty-print (procedure-source proc))))))))
(define-display-method ApplyContext (applycontext)
(let ((proc (ly:music-property applycontext 'procedure)))
(format #f "\\applyContext #~a"
(or (procedure-name proc)
(with-output-to-string
(lambda ()
(pretty-print (procedure-source proc))))))))
;;; \partCombine
(define-display-method PartCombineMusic (expr)
(let ((dir (ly:music-property expr 'direction)))
(format #f "\\partCombine~a ~a~a~a"
(cond ((equal? dir UP) "Up")
((equal? dir DOWN) "Down")
(else ""))
(music->lily-string (car (ly:music-property expr 'elements)))
(new-line->lily-string)
(music->lily-string (cadr (ly:music-property expr 'elements))))))
(define-display-method PartCombinePartMusic (expr)
(with-music-match ((ly:music-property expr 'element)
(music 'ContextSpeccedMusic element ?part))
(format #f "~a" (music->lily-string ?part))))
(define-extra-display-method ContextSpeccedMusic (expr)
"If `expr' is a \\partCombine expression, return \"\\partCombine ...\".
Otherwise, return #f."
(with-music-match
(expr (music 'ContextSpeccedMusic
context-type 'Staff
element (music 'SimultaneousMusic
elements ((music 'ContextSpeccedMusic
context-id "one"
context-type 'Voice)
(music 'ContextSpeccedMusic
context-id "two"
context-type 'Voice)
(music 'ContextSpeccedMusic
context-id "shared"
context-type 'Voice)
(music 'ContextSpeccedMusic
context-id "solo"
context-type 'Voice)
(music 'ContextSpeccedMusic
context-id "null"
context-type 'NullVoice)
?pc-music
?pc-marks))))
(with-music-match
(?pc-music (music 'PartCombineMusic))
(format #f "~a" (music->lily-string ?pc-music)))))
(define-display-method UnrelativableMusic (expr)
(music->lily-string (ly:music-property expr 'element)))
;;; Cue notes
(define-display-method QuoteMusic (expr)
(or (with-music-match (expr (music
'QuoteMusic
quoted-voice-direction ?quoted-voice-direction
quoted-music-name ?quoted-music-name
quoted-context-id "cue"
quoted-context-type 'CueVoice
element ?music))
(format #f "\\cueDuring ~s #~a ~a"
?quoted-music-name
?quoted-voice-direction
(music->lily-string ?music)))
(format #f "\\quoteDuring ~s ~a"
(ly:music-property expr 'quoted-music-name)
(music->lily-string (ly:music-property expr 'element)))))
;;;
;;; Breaks
;;;
(define-display-method LineBreakEvent (expr)
(if (null? (ly:music-property expr 'break-permission))
"\\noBreak"
"\\break"))
(define-display-method PageBreakEvent (expr)
(if (null? (ly:music-property expr 'break-permission))
"\\noPageBreak"
"\\pageBreak"))
(define-display-method PageTurnEvent (expr)
(if (null? (ly:music-property expr 'break-permission))
"\\noPageTurn"
"\\pageTurn"))
(define-extra-display-method EventChord (expr)
(with-music-match (expr (music 'EventChord
elements ((music 'LineBreakEvent
break-permission 'force)
(music 'PageBreakEvent
break-permission 'force))))
"\\pageBreak"))
(define-extra-display-method EventChord (expr)
(with-music-match (expr (music 'EventChord
elements ((music 'LineBreakEvent
break-permission 'force)
(music 'PageBreakEvent
break-permission 'force)
(music 'PageTurnEvent
break-permission 'force))))
"\\pageTurn"))
;;;
;;; Lyrics
;;;
;;; \lyricsto
(define-display-method LyricCombineMusic (expr)
(format #f "\\lyricsto ~s ~a"
(ly:music-property expr 'associated-context)
(parameterize ((*explicit-mode* #f)
(*omit-duration* #t))
(music->lily-string (ly:music-property expr 'element)))))
;; \autoChange
(define-extra-display-method SimultaneousMusic (expr)
(with-music-match (expr (music 'SimultaneousMusic
elements ((music 'ContextSpeccedMusic
context-id "up"
context-type 'Staff
element ?ac-music)
(music 'ContextSpeccedMusic
context-id "up"
context-type 'Staff)
(music 'ContextSpeccedMusic
context-id "down"
context-type 'Staff))))
(with-music-match (?ac-music (music 'AutoChangeMusic))
(format #f "~a"
(music->lily-string ?ac-music)))))
;; \addlyrics
(define-extra-display-method SimultaneousMusic (expr)
(with-music-match (expr (music 'SimultaneousMusic
elements ((music 'ContextSpeccedMusic
context-id ?id
context-type 'Voice
element ?note-sequence)
(music 'ContextSpeccedMusic
context-type 'Lyrics
create-new #t
element (music 'LyricCombineMusic
associated-context ?associated-id
element ?lyric-sequence)))))
(if (string=? ?id ?associated-id)
(format #f "~a~a \\addlyrics ~a"
(music->lily-string ?note-sequence)
(new-line->lily-string)
(parameterize ((*explicit-mode* #f)
(*omit-duration* #t))
(music->lily-string ?lyric-sequence)))
#f)))
;; Silence internal event sent at end of each lyrics block
(define-display-method CompletizeExtenderEvent (expr)
"")