(use-modules (scm safe-utility-defs))
(use-modules (ice-9 optargs))
+(use-modules (srfi srfi-11))
;;; ly:music-property with setter
;;; (ly:music-property my-music 'elements)
(unfold-repeats e)))
music))
+(define-public (unfold-repeats-fully music)
+ "Unfolds repeats and expands the resulting @code{unfolded-repeated-music}."
+ (map-some-music
+ (lambda (m)
+ (and (music-is-of-type? m 'unfolded-repeated-music)
+ (make-sequential-music
+ (ly:music-deep-copy
+ (let loop ((n (ly:music-property m 'repeat-count))
+ (alts (ly:music-property m 'elements))
+ (body (ly:music-property m 'element)))
+ (cond ((<= n 0) '())
+ ((null? alts)
+ (cons body (loop (1- n) alts body)))
+ (else
+ (cons* body (car alts)
+ (loop (1- n)
+ (if (pair? (cdr alts))
+ (cdr alts)
+ alts)
+ body)))))))))
+ (unfold-repeats music)))
+
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; property setting music objs.
(make-music 'PropertyUnset
'symbol sym))
-(define-safe-public (make-articulation name)
- (make-music 'ArticulationEvent
- 'articulation-type name))
+(define-safe-public (make-articulation name . properties)
+ (apply make-music 'ArticulationEvent
+ 'articulation-type name
+ properties))
(define-public (make-lyric-event string duration)
(make-music 'LyricEvent
(else music))))
-(define-public toplevel-music-functions
+(define-session-public toplevel-music-functions
(list
(lambda (music parser) (expand-repeat-chords!
(cons 'rhythmic-event
(map (lambda (x) (ly:music-property x 'pitch))
(event-chord-notes event-chord)))
-(defmacro-public make-relative (pitches last-pitch music)
- "The list of pitch-carrying variables in @var{pitches} is used as a
-sequence for creating relativable music from @var{music}.
-The variables in @var{pitches} are, when considered inside of
-@code{\\relative}, all considered to be specifications to the preceding
-variable. The first variable is relative to the preceding musical
-context, and @var{last-pitch} specifies the pitch passed as relative
-base onto the following musical context."
+(define-public (event-chord-reduce music)
+ "Reduces event chords in @var{music} to their first note event,
+retaining only the chord articulations. Returns the modified music."
+ (map-some-music
+ (lambda (m)
+ (and (music-is-of-type? m 'event-chord)
+ (let*-values (((notes arts) (partition
+ (lambda (mus)
+ (music-is-of-type? mus 'rhythmic-event))
+ (ly:music-property m 'elements)))
+ ((dur) (ly:music-property m 'duration))
+ ((full-arts) (append arts
+ (ly:music-property m 'articulations)))
+ ((first-note) (and (pair? notes) (car notes))))
+ (cond (first-note
+ (set! (ly:music-property first-note 'articulations)
+ full-arts)
+ first-note)
+ ((ly:duration? dur)
+ ;; A repeat chord. Produce an unpitched note.
+ (make-music 'NoteEvent
+ 'duration dur
+ 'articulations full-arts))
+ (else
+ (ly:music-error m (_ "Missing duration"))
+ (make-music 'NoteEvent
+ 'duration (ly:make-duration 2 0 0)
+ 'articulations full-arts))))))
+ music))
+
+
+(defmacro-public make-relative (variables reference music)
+ "The list of pitch or music variables in @var{variables} is used as
+a sequence for creating relativable music from @var{music}.
+
+When the constructed music is used outside of @code{\\relative}, it
+just reflects plugging in the @var{variables} into @var{music}.
+
+The action inside of @code{\\relative}, however, is determined by
+first relativizing the surrogate @var{reference} with the variables
+plugged in and then using the variables relativized as a side effect
+of relativizing @var{reference} for evaluating @var{music}.
+
+Since pitches don't have the object identity required for tracing the
+effect of the reference call, they are replaced @emph{only} for the
+purpose of evaluating @var{reference} with simple pitched note events.
+
+The surrogate @var{reference} expression has to be written with that
+in mind. In addition, it must @emph{not} contain @emph{copies} of
+music that is supposed to be relativized but rather the
+@emph{originals}. This @emph{includes} the pitch expressions. As a
+rule, inside of @code{#@{@dots{}#@}} variables must @emph{only} be
+introduced using @code{#}, never via the copying construct @code{$}.
+The reference expression will usually just be a sequential or chord
+expression naming all variables in sequence, implying that following
+music will be relativized according to the resulting pitch of the last
+or first variable, respectively.
+
+Since the usual purpose is to create more complex music from general
+arguments and since music expression parts must not occur more than
+once, one @emph{does} generally need to use copying operators in the
+@emph{replacement} expression @var{music} when using an argument more
+than once there. Using an argument more than once in @var{reference},
+in contrast, does not make sense.
+
+There is another fine point to mind: @var{music} must @emph{only}
+contain freshly constructed elements or copied constructs. This will
+be the case anyway for regular LilyPond code inside of
+@code{#@{@dots{}#@}}, but any other elements (apart from the
+@var{variables} themselves which are already copied) must be created
+or copied as well.
+
+The reason is that it is usually permitted to change music in-place as
+long as one does a @var{ly:music-deep-copy} on it, and such a copy of
+the whole resulting expression will @emph{not} be able to copy
+variables/values inside of closures where the information for
+relativization is being stored.
+"
;; pitch and music generator might be stored instead in music
;; properties, and it might make sense to create a music type of its
;; own for this kind of construct rather than using
;; RelativeOctaveMusic
- (define ((make-relative::to-relative-callback pitches p->m p->p) music pitch)
- (let* ((chord (make-event-chord
- (map
- (lambda (p)
- (make-music 'NoteEvent
- 'pitch p))
- pitches)))
- (pitchout (begin
- (ly:make-music-relative! chord pitch)
- (event-chord-pitches chord))))
- (set! (ly:music-property music 'element)
- (apply p->m pitchout))
- (apply p->p pitchout)))
+ (define ((make-relative::to-relative-callback variables music-call ref-call)
+ music pitch)
+ (let* ((ref-vars (map (lambda (v)
+ (if (ly:pitch? v)
+ (make-music 'NoteEvent 'pitch v)
+ (ly:music-deep-copy v)))
+ variables))
+ (after-pitch (ly:make-music-relative! (apply ref-call ref-vars) pitch))
+ (actual-vars (map (lambda (v r)
+ (if (ly:pitch? v)
+ (ly:music-property r 'pitch)
+ r))
+ variables ref-vars))
+ (rel-music (apply music-call actual-vars)))
+ (set! (ly:music-property music 'element) rel-music)
+ after-pitch))
`(make-music 'RelativeOctaveMusic
'to-relative-callback
(,make-relative::to-relative-callback
- (list ,@pitches)
- (lambda ,pitches ,music)
- (lambda ,pitches ,last-pitch))
+ (list ,@variables)
+ (lambda ,variables ,music)
+ (lambda ,variables ,reference))
'element ,music))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;