;;; define-music-display-methods.scm -- data for displaying music ;;; expressions using LilyPond notation. ;;; ;;; Copyright (C) 2005--2020 Nicolas Sceaux ;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; ;;; 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") (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) "")