%%%% This file is part of LilyPond, the GNU music typesetter.
%%%%
-%%%% Copyright (C) 2003--2012 Han-Wen Nienhuys <hanwen@xs4all.nl>
+%%%% Copyright (C) 2003--2014 Han-Wen Nienhuys <hanwen@xs4all.nl>
%%%% Jan Nieuwenhuizen <janneke@gnu.org>
%%%%
%%%% LilyPond is free software: you can redistribute it and/or modify
%%%% You should have received a copy of the GNU General Public License
%%%% along with LilyPond. If not, see <http://www.gnu.org/licenses/>.
-\version "2.17.6"
+\version "2.17.11"
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% TODO: using define-music-function in a .scm causes crash.
+absolute =
+#(define-music-function (parser location music)
+ (ly:music?)
+ (_i "Make @var{music} absolute. This does not actually change the
+music itself but rather hides it from surrounding @code{\\relative}
+commands.")
+ (make-music 'RelativeOctaveMusic 'element music))
+
acciaccatura =
#(def-grace-function startAcciaccaturaMusic stopAcciaccaturaMusic
(_i "Create an acciaccatura from the following music expression"))
(symbol? number-pair? markup?)
(_i "Attach @var{text} to @var{grob-name} at offset @var{offset}
(use like @code{\\once})")
- (make-music 'AnnotateOutputEvent
- 'symbol grob-name
- 'X-offset (car offset)
- 'Y-offset (cdr offset)
- 'text text))
+ (make-event-chord
+ (list
+ (make-music 'AnnotateOutputEvent
+ 'symbol grob-name
+ 'X-offset (car offset)
+ 'Y-offset (cdr offset)
+ 'text text))))
balloonText =
-#(define-music-function (parser location offset text) (number-pair? markup?)
+#(define-event-function (parser location offset text) (number-pair? markup?)
(_i "Attach @var{text} at @var{offset} (use like @code{\\tweak})")
(make-music 'AnnotateOutputEvent
'X-offset (car offset)
"Barcheck failed got ~a expect ~a"
cbn n))))))
+beamExceptions =
+#(define-scheme-function (parser location music) (ly:music?)
+ (_i "Extract a value suitable for setting
+@code{Timing.beamExceptions} from the given pattern with explicit
+beams in @var{music}. A bar check @code{|} has to be used between
+bars of patterns in order to reset the timing.")
+ (extract-beam-exceptions music))
+
bendAfter =
#(define-event-function (parser location delta) (real?)
(_i "Create a fall or doit of pitch interval @var{delta}.")
breathe =
#(define-music-function (parser location) ()
(_i "Insert a breath mark.")
- (make-music 'BreathingEvent))
+ (make-music 'BreathingEvent
+ 'midi-length
+ (lambda (len context)
+ ;;Shorten by half, or by up to a second, but always by a power of 2
+ (let* ((desired (min (ly:moment-main (seconds->moment 1 context))
+ (* (ly:moment-main len) 1/2)))
+ (scale (inexact->exact (ceiling (/ (log desired) (log 1/2)))))
+ (breath (ly:make-moment (expt 1/2 scale))))
+ (ly:moment-sub (ly:make-moment (ly:moment-main len)) breath)))))
clef =
#(define-music-function (parser location type) (string?)
(timesig (cons (ly:moment-main-numerator mlen)
(ly:moment-main-denominator mlen))))
#{
- \once \override Staff.TimeSignature.stencil = #(lambda (grob)
+ \once \override Timing.TimeSignature.stencil = #(lambda (grob)
(grob-interpret-markup grob (format-compound-time args)))
\set Timing.timeSignatureFraction = #timesig
\set Timing.baseMoment = #beat
in a CueVoice oriented by @var{dir}.")
(make-music 'QuoteMusic
'element main-music
- 'quoted-context-type 'Voice
+ 'quoted-context-type 'CueVoice
'quoted-context-id "cue"
'quoted-music-name what
'quoted-voice-direction dir))
in a CueVoice oriented by @var{dir}.")
(make-music 'QuoteMusic
'element main-music
- 'quoted-context-type 'Voice
+ 'quoted-context-type 'CueVoice
'quoted-context-id "cue"
'quoted-music-name what
'quoted-music-clef clef
displayLilyMusic =
-#(define-music-function (parser location music) (ly:music?)
+#(define-music-function (parser location port music) ((output-port?) ly:music?)
(_i "Display the LilyPond input representation of @var{music}
-to the console.")
- (newline)
- (display-lily-music music parser)
+to @var{port}, defaulting to the console.")
+ (let ((port (or port (current-output-port))))
+ (newline port)
+ (display-lily-music music parser port))
music)
displayMusic =
-#(define-music-function (parser location music) (ly:music?)
- (_i "Display the internal representation of @var{music} to the console.")
- (newline)
- (display-scheme-music music)
+#(define-music-function (parser location port music) ((output-port?) ly:music?)
+ (_i "Display the internal representation of @var{music} to
+@var{port}, default to the console.")
+ (let ((port (or port (current-output-port))))
+ (newline port)
+ (display-scheme-music music port))
music)
+displayScheme =
+#(define-scheme-function (parser location port expr) ((output-port?) scheme?)
+ (_i "Display the internal representation of @var{expr} to
+@var{port}, default to the console.")
+ (let ((port (or port (current-output-port))))
+ (newline port)
+ (display-scheme-music expr port))
+ expr)
+
endSpanners =
(extract-typed-music music 'span-event)))
(stop-span-evs
(map (lambda (m)
- (let ((c (music-clone m)))
- (set! (ly:music-property c 'span-direction) STOP)
- c))
- start-span-evs))
+ (music-clone m 'span-direction STOP))
+ start-span-evs))
(end-ev-chord (make-music 'EventChord
'elements stop-span-evs))
(total (make-music 'SequentialMusic
argument))
+finger =
+#(define-event-function (parser location finger) (number-or-markup?)
+ (_i "Apply @var{finger} as a fingering indication.")
+
+ (make-music
+ 'FingeringEvent
+ (if (number? finger) 'digit 'text)
+ finger))
+
footnote =
#(define-music-function (parser location mark offset footnote item)
((markup?) number-pair? markup? symbol-list-or-music?)
'automatically-numbered (not mark)
'text (or mark (make-null-markup))
'footnote-text footnote)))
- #{ \tweak footnote-music #mus #item #}))
+ #{ \once \tweak footnote-music #mus #item #}))
grace =
#(def-grace-function startGraceMusic stopGraceMusic
#{
\set harmonicDots = ##t
\temporary \override TabNoteHead.stencil = #(tab-note-head::print-custom-fret-label (number->string fret))
- \temporary \override NoteHead.Y-extent = #(ly:make-unpure-pure-container ly:grob::stencil-height
- (lambda (grob start end)
- (ly:grob::stencil-height grob)))
+ \temporary \override NoteHead.Y-extent = #grob::always-Y-extent-from-stencil
\temporary \override NoteHead.stencil = #(lambda (grob) (ly:grob-set-property! grob 'style 'harmonic-mixed)
(ly:note-head::print grob))
#(make-harmonic
@code{Context.GrobName}, the result is an override for the grob name
specified by it. If @var{item} is a music expression, the result is
the same music expression with an appropriate tweak applied to it.")
- (if (ly:music? item)
- #{ \tweak transparent ##t #item #}
- #{ \override #item . transparent = ##t #}))
+ #{ \tweak transparent ##t #item #})
inStaffSegno =
#(define-music-function (parser location) ()
(ly:input-warning location (_ "No other language was defined previously. Ignoring."))))
+magnifyMusic =
+#(define-music-function (parser location mag music) (positive? ly:music?)
+ (_i "Magnify the notation of @var{music} without changing the
+staff-size, using @var{mag} as a size factor. Stems, beams,
+slurs, ties, and horizontal spacing are adjusted automatically.")
+
+ ;; these props are NOT allowed to shrink below default size
+ (define unshrinkable-props
+ '(
+ ;; stems
+ (Stem thickness)
+
+ ;; slurs
+ (Slur line-thickness)
+ (Slur thickness)
+ (PhrasingSlur line-thickness)
+ (PhrasingSlur thickness)
+
+ ;; ties
+ (Tie line-thickness)
+ (Tie thickness)
+ (LaissezVibrerTie line-thickness)
+ (LaissezVibrerTie thickness)
+ (RepeatTie line-thickness)
+ (RepeatTie thickness)
+ ))
+
+ ;; these props ARE allowed to shrink below default size
+ (define shrinkable-props
+ '(
+ ;; TODO: uncomment spacing-increment here once Issue 3987 is fixed
+ ;; override at the 'Score level
+ ;(SpacingSpanner spacing-increment)
+
+ ;; lengths and heights
+ (Beam length-fraction)
+ (Stem length-fraction)
+ (Stem beamlet-default-length)
+ (Slur height-limit)
+ (Slur minimum-length)
+ (PhrasingSlur height-limit)
+ (PhrasingSlur minimum-length)
+
+ ;; Beam.beam-thickness is dealt with separately below
+ ))
+ #{
+ \context Bottom {
+ %% TODO: uncomment \newSpacingSection once Issue 3990 is fixed
+ %\newSpacingSection
+ #(scale-fontSize 'magnifyMusic mag)
+ #(scale-props 'magnifyMusic mag #f unshrinkable-props)
+ #(scale-props 'magnifyMusic mag #t shrinkable-props)
+ #(scale-beam-thickness mag)
+
+ #music
+
+ %% TODO: uncomment \newSpacingSection once Issue 3990 is fixed
+ %\newSpacingSection
+ %% reverse engineer the former fontSize value instead of using \unset
+ #(revert-fontSize 'magnifyMusic mag)
+ #(revert-props 'magnifyMusic mag (append unshrinkable-props
+ shrinkable-props
+ '((Beam beam-thickness))))
+ }
+ #})
+
+magnifyStaff =
+#(define-music-function (parser location mag) (positive?)
+ (_i "Change the size of the staff, adjusting notation size and
+horizontal spacing automatically, using @var{mag} as a size factor.")
+
+ ;; these props are NOT allowed to shrink below default size
+ (define unshrinkable-props
+ '((StaffSymbol thickness)))
+
+ ;; these props ARE allowed to shrink below default size
+ (define shrinkable-props
+ (let ((space-alist-props
+ (find-all-space-alist-props all-grob-descriptions)))
+ (append
+ space-alist-props
+ '(
+ ;; override at the 'Score level
+ (SpacingSpanner spacing-increment)
+
+ (StaffSymbol staff-space)
+ (BarLine kern)
+ (BarLine segno-kern)
+ (BarLine hair-thickness)
+ (BarLine thick-thickness)
+ (Stem beamlet-default-length)
+ ))))
+
+ #{
+ \stopStaff
+
+ %% revert settings from last time
+ %% (but only if \magnifyStaff has already been used
+ %% and the staff magnification is changing)
+ #(revert-fontSize 'magnifyStaff mag)
+ #(revert-props 'magnifyStaff mag (append unshrinkable-props
+ shrinkable-props))
+
+ %% scale settings
+ %% (but only if staff magnification is changing)
+ #(scale-fontSize 'magnifyStaff mag)
+ #(scale-props 'magnifyStaff mag #f unshrinkable-props)
+ #(scale-props 'magnifyStaff mag #t shrinkable-props)
+
+ %% this might cause problems until Issue 3990 is fixed
+ \newSpacingSection
+
+ \startStaff
+ \set Staff.magnifyStaffValue = #mag
+ #})
+
makeClusters =
#(define-music-function (parser location arg) (ly:music?)
(_i "Display chords in @var{arg} as clusters.")
(make-music 'RelativeOctaveCheck
'pitch pitch))
+offset =
+#(define-music-function (parser location property offsets item)
+ (symbol-list-or-symbol? scheme? symbol-list-or-music?)
+ (_i "Offset the default value of @var{property} of @var{item} by
+@var{offsets}. If @var{item} is a string, the result is
+@code{\\override} for the specified grob type. If @var{item} is
+a music expression, the result is the same music expression with an
+appropriate tweak applied.")
+ (if (ly:music? item)
+ ; In case of a tweak, grob property path is Grob.property
+ (let ((prop-path (check-grob-path
+ (if (symbol? property)
+ (list property)
+ property)
+ parser location
+ #:start 1 #:default #t #:min 2 #:max 2)))
+ (if prop-path
+ ; If the head of the grob property path is a symbol--i.e.,
+ ; a grob name, produce a directed tweak. Otherwise, create
+ ; an ordinary tweak.
+ (if (symbol? (car prop-path))
+ #{
+ \tweak #prop-path #(offsetter (second prop-path) offsets) #item
+ #}
+ #{
+ \tweak #(second prop-path) #(offsetter (second prop-path) offsets) #item
+ #})
+ item))
+ ; In case of an override, grob property path is Context.Grob.property.
+ (let ((prop-path (check-grob-path
+ (append item
+ (if (symbol? property)
+ (list property)
+ property))
+ parser location
+ #:default 'Bottom #:min 3 #:max 3)))
+ (if prop-path
+ #{
+ \override #prop-path = #(offsetter (third prop-path) offsets)
+ #}
+ (make-music 'Music)))))
+
omit =
#(define-music-function (parser location item) (symbol-list-or-music?)
(_i "Set @var{item}'s @samp{stencil} property to @code{#f},
@code{Context.GrobName}, the result is an override for the grob name
specified by it. If @var{item} is a music expression, the result is
the same music expression with an appropriate tweak applied to it.")
- (if (ly:music? item)
- #{ \tweak stencil ##f #item #}
- #{ \override #item . stencil = ##f #}))
+ #{ \tweak stencil ##f #item #})
once =
#(define-music-function (parser location music) (ly:music?)
- (_i "Set @code{once} to @code{#t} on all layout instruction events in @var{music}.")
- (music-map
- (lambda (m)
- (cond ((music-is-of-type? m 'layout-instruction-event)
- (set! (ly:music-property m 'once) #t))
- ((ly:duration? (ly:music-property m 'duration))
- (ly:music-warning m (_ "Cannot apply \\once to timed music"))))
- m)
- music))
+ (_i "Set @code{once} to @code{#t} on all layout instruction events
+in @var{music}. This will complain about music with an actual
+duration. As a special exception, if @var{music} contains
+@samp{tweaks} it will be silently ignored in order to allow for
+@code{\\once \\tweak} to work as both one-time override and proper
+tweak.")
+ (if (not (pair? (ly:music-property music 'tweaks)))
+ (for-some-music
+ (lambda (m)
+ (cond ((music-is-of-type? m 'layout-instruction-event)
+ (set! (ly:music-property m 'once) #t)
+ #t)
+ ((ly:duration? (ly:music-property m 'duration))
+ (ly:music-warning m (_ "Cannot apply \\once to timed music"))
+ #t)
+ (else #f)))
+ music))
+ music)
ottava =
#(define-music-function (parser location octave) (integer?)
overrideTimeSignatureSettings =
#(define-music-function
(parser location time-signature base-moment beat-structure beam-exceptions)
- (pair? pair? cheap-list? cheap-list?)
+ (fraction? fraction? list? list?)
(_i "Override @code{timeSignatureSettings}
for time signatures of @var{time-signature} to have settings
C = { e e | f f | }
@end verbatim
")
- (let* ((voices (apply circular-list (make-list (length voice-ids) (list))))
- (current-voices voices)
- (current-sequence (list))
- (original music)
- (wrapper #f))
- ;;
- ;; utilities
- (define (push-music m)
- "Push the music expression into the current sequence"
- (set! current-sequence (cons m current-sequence)))
- (define (change-voice)
- "Stores the previously built sequence into the current voice and
- change to the following voice."
- (list-set! current-voices 0 (cons (make-music 'SequentialMusic
- 'elements (reverse! current-sequence))
- (car current-voices)))
- (set! current-sequence (list))
- (set! current-voices (cdr current-voices)))
- (define (bar-check? m)
- "Checks whether m is a bar check."
- (eq? (ly:music-property m 'name) 'BarCheck))
- (define (music-origin music)
- "Recursively search an origin location stored in music."
- (cond ((null? music) #f)
- ((not (null? (ly:music-property music 'origin)))
- (ly:music-property music 'origin))
- (else (or (music-origin (ly:music-property music 'element))
- (let ((origins (remove not (map music-origin
- (ly:music-property music 'elements)))))
- (and (not (null? origins)) (car origins)))))))
- (while (music-is-of-type? music 'music-wrapper-music)
- (set! wrapper music)
- (set! music (ly:music-property wrapper 'element)))
- (if wrapper
- (set! (ly:music-property wrapper 'element)
- (make-music 'SequentialMusic
- 'origin location))
- (set! original
- (make-music 'SequentialMusic
- 'origin location)))
- ;;
- ;; first, split the music and fill in voices
- ;; We flatten direct layers of SequentialMusic since they are
- ;; pretty much impossible to avoid when writing music functions.
- (let rec ((music music))
- (for-each (lambda (m)
- (if (eq? (ly:music-property m 'name) 'SequentialMusic)
- (rec m)
- (begin
- (push-music m)
- (if (bar-check? m) (change-voice)))))
- (ly:music-property music 'elements)))
- (if (not (null? current-sequence)) (change-voice))
- ;; un-circularize `voices' and reorder the voices
- (set! voices (map-in-order (lambda (dummy seqs)
- (reverse! seqs))
- voice-ids voices))
- ;;
- ;; set origin location of each sequence in each voice
- ;; for better type error tracking
- (for-each (lambda (voice)
- (for-each (lambda (seq)
- (set! (ly:music-property seq 'origin)
- (or (music-origin seq) location)))
- voice))
- voices)
- ;;
- ;; check sequence length
- (apply for-each (lambda* (#:rest seqs)
- (let ((moment-reference (ly:music-length (car seqs))))
- (for-each (lambda (seq moment)
- (if (not (equal? moment moment-reference))
- (ly:music-warning seq
- "Bars in parallel music don't have the same length")))
- seqs (map-in-order ly:music-length seqs))))
- voices)
- ;;
- ;; bind voice identifiers to the voices
- (for-each (lambda (voice-id voice)
- (ly:parser-define! parser voice-id
- (let ((v (ly:music-deep-copy original)))
- (set! (ly:music-property
- (car (extract-named-music
- v 'SequentialMusic))
- 'elements) voice)
- v)))
- voice-ids voices)))
+ (define voice-count (length voice-ids))
+ (define (bar-check? m)
+ "Checks whether m is a bar check."
+ (eq? (ly:music-property m 'name) 'BarCheck))
+ (define (recurse-and-split-list lst)
+ "Return either a list of music lists split along barchecks, or @code{#f}."
+ (if (any bar-check? lst)
+ (let* ((voices (apply circular-list (make-list voice-count '())))
+ (current-voices voices)
+ (current-sequence '()))
+ ;;
+ ;; utilities
+ (define (push-music m)
+ "Push the music expression into the current sequence"
+ (set! current-sequence (cons m current-sequence)))
+ (define (change-voice)
+ "Store the previously built sequence into the current voice and
+change to the following voice."
+ (set-car! current-voices
+ (cons (reverse! current-sequence)
+ (car current-voices)))
+ (set! current-sequence '())
+ (set! current-voices (cdr current-voices)))
+ (for-each (lambda (m)
+ (let ((split? (recurse-and-split m)))
+ (if split?
+ (for-each
+ (lambda (m)
+ (push-music m)
+ (change-voice))
+ split?)
+ (begin
+ (push-music m)
+ (if (bar-check? m) (change-voice))))))
+ lst)
+ (if (pair? current-sequence) (change-voice))
+ ;; un-circularize `voices' and reorder the voices
+ (set! voices (map reverse!
+ (list-head voices voice-count)))
+ ;; check sequence length
+ (apply for-each (lambda seqs
+ (define (seq-len seq)
+ (reduce ly:moment-add
+ (ly:make-moment 0)
+ (map ly:music-length seq)))
+ (let ((moment-reference (seq-len (car seqs))))
+ (for-each (lambda (seq)
+ (if (not (equal? (seq-len seq)
+ moment-reference))
+ (ly:music-warning
+ (if (pair? seq)
+ (last seq)
+ (caar seqs))
+ (_ "Bars in parallel music don't have the same length"))))
+ seqs)))
+ voices)
+ (map concatenate! voices))
+ (let ((deeper (map recurse-and-split lst)))
+ (and (any pair? deeper)
+ (apply zip (map
+ (lambda (m split)
+ (or split
+ (ly:music-deep-copy (make-list voice-count m))))
+ lst deeper))))))
+ (define (recurse-and-split music)
+ "This returns either a list of music split along barchecks, or
+@code{#f}."
+ (let* ((elt (ly:music-property music 'element))
+ (elts (ly:music-property music 'elements))
+ (split-elt (and (ly:music? elt) (recurse-and-split elt)))
+ (split-elts (and (pair? elts) (recurse-and-split-list elts))))
+ (and (or split-elt split-elts)
+ (map
+ (lambda (e es)
+ (apply music-clone music
+ (append
+ ;; reassigning the origin of the parent only
+ ;; makes sense if the first expression in the
+ ;; result is from a distributed origin
+ (let ((origin
+ (if (ly:music? elt)
+ (and (ly:music? e) (ly:music-property e 'origin #f))
+ (and (pair? es) (ly:music-property (car es) 'origin #f)))))
+ (if origin (list 'origin origin) '()))
+ (if (ly:music? e) (list 'element e) '())
+ (if (pair? es) (list 'elements es) '()))))
+ (or split-elt (circular-list #f))
+ (or split-elts (circular-list #f))))))
+ (let ((voices (recurse-and-split music)))
+ (if voices
+ ;;
+ ;; bind voice identifiers to the voices
+ (for-each (lambda (voice-id voice)
+ (ly:parser-define! parser voice-id voice))
+ voice-ids voices)
+ (ly:music-warning music
+ (_ "ignoring parallel music without barchecks")))))
parenthesize =
#(define-music-function (parser loc arg) (ly:music?)
(descend-to-context
(context-spec-music (make-music 'PartialSet
'origin location
- 'partial-duration dur)
+ 'duration dur)
'Timing)
'Score))
relative =
#(define-music-function (parser location pitch music)
- ((ly:pitch? (ly:make-pitch 0 0 0)) ly:music?)
- (_i "Make @var{music} relative to @var{pitch} (default @code{c'}).")
- (ly:make-music-relative! music pitch)
+ ((ly:pitch?) ly:music?)
+ (_i "Make @var{music} relative to @var{pitch}. If @var{pitch} is
+omitted, the first note in @var{music} is given in absolute pitch.")
+ ;; When \relative has no clear decision (can only happen with
+ ;; scales with an even number of steps), it goes down (see
+ ;; pitch.cc). The following formula puts out f for both the normal
+ ;; 7-step scale as well as for a "shortened" scale missing the
+ ;; final b. In either case, a first note of c will end up as c,
+ ;; namely pitch (-1, 0, 0).
+ (ly:make-music-relative! music
+ (or pitch
+ (ly:make-pitch
+ -1
+ (quotient
+ ;; size of current scale:
+ (ly:pitch-steps (ly:make-pitch 1 0))
+ 2))))
(make-music 'RelativeOctaveMusic
'element music))
(revert-time-signature-setting time-signature))
rightHandFinger =
-#(define-event-function (parser location finger) (number-or-string?)
+#(define-event-function (parser location finger) (number-or-markup?)
(_i "Apply @var{finger} as a fingering indication.")
(make-music
'StrokeFingerEvent
- 'origin location
- (if (string? finger) 'text 'digit)
+ (if (number? finger) 'digit 'text)
finger))
scaleDurations =
(if (>= total-found 2)
(helper siblings offsets)
(offset-control-points (car offsets)))))
- #{ \tweak control-points #shape-curve #item #})
+ #{ \once \tweak control-points #shape-curve #item #})
shiftDurations =
#(define-music-function (parser location dur dots arg)
(_i "Change the duration of @var{arg} by adding @var{dur} to the
@code{durlog} of @var{arg} and @var{dots} to the @code{dots} of @var{arg}.")
- (music-map
- (lambda (x)
- (shift-one-duration-log x dur dots)) arg))
+ (shift-duration-log arg dur dots))
single =
#(define-music-function (parser location overrides music)
(make-music 'QuoteMusic
'element main-music
- 'quoted-context-type 'Voice
+ 'quoted-context-type 'CueVoice
'quoted-context-id "cue"
'quoted-music-name what
'quoted-voice-direction dir
+ ;; following is inverse of instrumentTransposition for
+ ;; historical reasons
'quoted-transposition pitch))
transposition =
(_i "Set instrument transposition")
(context-spec-music
- (make-property-set 'instrumentTransposition
- (ly:pitch-negate pitch))
+ (make-property-set 'instrumentTransposition pitch)
'Staff))
+tuplet =
+#(define-music-function (parser location ratio tuplet-span music)
+ (fraction? (ly:duration? '()) ly:music?)
+ (_i "Scale the given @var{music} to tuplets. @var{ratio} is a
+fraction that specifies how many notes are played in place of the
+nominal value: it will be @samp{3/2} for triplets, namely three notes
+being played in place of two. If the optional duration
+@var{tuplet-span} is specified, it is used instead of
+@code{tupletSpannerDuration} for grouping the tuplets.
+For example,
+@example
+\\tuplet 3/2 4 @{ c8 c c c c c @}
+@end example
+will result in two groups of three tuplets, each group lasting for a
+quarter note.")
+ (make-music 'TimeScaledMusic
+ 'element (ly:music-compress
+ music
+ (ly:make-moment (cdr ratio) (car ratio)))
+ 'numerator (cdr ratio)
+ 'denominator (car ratio)
+ 'duration tuplet-span))
+
+tupletSpan =
+#(define-music-function (parser location tuplet-span)
+ ((ly:duration?))
+ (_i "Set @code{tupletSpannerDuration}, the length into which
+@code{\\tuplet} without an explicit @samp{tuplet-span} argument of its
+own will group its tuplets, to the duration @var{tuplet-span}. To
+revert to the default of not subdividing the contents of a @code{\\tuplet}
+command without explicit @samp{tuplet-span}, use
+@example
+\\tupletSpan \\default
+@end example
+")
+ (if tuplet-span
+ #{ \set tupletSpannerDuration = #(ly:duration-length tuplet-span) #}
+ #{ \unset tupletSpannerDuration #}))
+
tweak =
#(define-music-function (parser location prop value item)
(symbol-list-or-symbol? scheme? symbol-list-or-music?)
are affected.
As a special case, @var{item} may be a symbol list specifying a grob
-path, in which case @code{\\once\\override} is called on it instead of
+path, in which case @code{\\override} is called on it instead of
creating tweaked music. This is mainly useful when using
@code{\\tweak} as as a component for building other functions.
+If this use case would call for @code{\\once \\override} rather than a
+plain @code{\\override}, writing @code{\\once \\tweak @dots{}} can be
+convenient.
+
@var{prop} can contain additional elements in which case a nested
property (inside of an alist) is tweaked.")
(if (ly:music? item)
;; We could just throw this at \override and let it sort this
;; out on its own, but this way we should get better error
;; diagnostics.
- (let ((a (check-grob-path item parser location
- #:default 'Bottom #:min 2 #:max 2))
- (b (check-grob-path prop parser location
- #:start 2)))
- (if (and a b)
- #{ \once\override #(append a b) = #value #}
+ (let ((p (check-grob-path
+ (append item (if (symbol? prop) (list prop) prop))
+ parser location
+ #:default 'Bottom #:min 3)))
+ (if p
+ #{ \override #p = #value #}
(make-music 'Music)))))
undo =