From 0b434ac8bc4b9b891a3734914eec4591415e847f Mon Sep 17 00:00:00 2001 From: Devon Schudy Date: Fri, 20 Dec 2013 22:55:51 -0500 Subject: [PATCH] Tremolo cleanup. (issue 3745) Scale durations when iterating, not in make-repeat. This means the original repeat body is intact, so unfold-repeats and display methods no longer need to undo the scaling. Build tremolo spanners in Scheme, not C++. Calculate tremolo-type in tremolo::get-music-list rather than storing it as a property. shift-duration-log now clears the cached length, since it changes the length. \shiftDurations no longer duplicates shift-duration-log. Merge make-repeated-music with make-repeat. --- lily/chord-tremolo-iterator.cc | 38 +------- lily/percent-repeat-iterator.cc | 1 + ly/music-functions-init.ly | 4 +- scm/define-music-display-methods.scm | 24 +---- scm/define-music-types.scm | 16 +-- scm/music-functions.scm | 141 ++++++++++++++------------- 6 files changed, 77 insertions(+), 147 deletions(-) diff --git a/lily/chord-tremolo-iterator.cc b/lily/chord-tremolo-iterator.cc index a01c1aa9c1..f73e846bcf 100644 --- a/lily/chord-tremolo-iterator.cc +++ b/lily/chord-tremolo-iterator.cc @@ -20,9 +20,6 @@ #include "chord-tremolo-iterator.hh" -#include "input.hh" -#include "international.hh" -#include "misc.hh" #include "repeated-music.hh" Chord_tremolo_iterator::Chord_tremolo_iterator () @@ -33,39 +30,8 @@ SCM Chord_tremolo_iterator::get_music_list () const { Music *mus = get_music (); - Input *origin = mus->origin (); - Music *body = Repeated_music::body (mus); - bool body_is_sequential = body->is_mus_type ("sequential-music"); - - int elt_count = body_is_sequential ? scm_ilength (body->get_property ("elements")) : 1; - - if (elt_count <= 0) - elt_count = 1; - - if (elt_count == 1) - { - Music *ev = make_music_by_name (ly_symbol2scm ("TremoloEvent")); - ev->set_spot (*origin); - ev->set_property ("repeat-count", mus->get_property ("repeat-count")); - ev->set_property ("tremolo-type", mus->get_property ("tremolo-type")); - return scm_list_2 (ev->unprotect (), body->self_scm ()); - } - else - { - SCM tremolo_symbol = ly_symbol2scm ("TremoloSpanEvent"); - SCM start_event_scm = scm_call_2 (ly_lily_module_constant ("make-span-event"), tremolo_symbol, scm_from_int (START)); - unsmob_music (start_event_scm)->set_spot (*origin); - SCM stop_event_scm = scm_call_2 (ly_lily_module_constant ("make-span-event"), tremolo_symbol, scm_from_int (STOP)); - - Music *start_event = unsmob_music (start_event_scm); - Music *stop_event = unsmob_music (stop_event_scm); - start_event->set_spot (*origin); - stop_event->set_spot (*origin); - start_event->set_property ("repeat-count", mus->get_property ("repeat-count")); - start_event->set_property ("tremolo-type", mus->get_property ("tremolo-type")); - - return scm_list_3 (start_event_scm, body->self_scm (), stop_event_scm); - } + SCM proc = ly_lily_module_constant ("tremolo::get-music-list"); + return scm_call_1 (proc, mus->self_scm ()); } IMPLEMENT_CTOR_CALLBACK (Chord_tremolo_iterator); diff --git a/lily/percent-repeat-iterator.cc b/lily/percent-repeat-iterator.cc index 25aa9bf3ee..c2779f97e3 100644 --- a/lily/percent-repeat-iterator.cc +++ b/lily/percent-repeat-iterator.cc @@ -43,6 +43,7 @@ SCM Percent_repeat_iterator::get_music_list () const { Music *mus = get_music (); + Music *child = Repeated_music::body (mus); SCM length = child->get_length ().smobbed_copy (); SCM child_list = SCM_EOL; diff --git a/ly/music-functions-init.ly b/ly/music-functions-init.ly index 11dcb53556..0ef5b397e1 100644 --- a/ly/music-functions-init.ly +++ b/ly/music-functions-init.ly @@ -1281,9 +1281,7 @@ shiftDurations = (_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) diff --git a/scm/define-music-display-methods.scm b/scm/define-music-display-methods.scm index 0c4674384d..431f9a6e95 100644 --- a/scm/define-music-display-methods.scm +++ b/scm/define-music-display-methods.scm @@ -724,29 +724,7 @@ Otherwise, return #f." (repeat->lily-string expr "percent" parser)) (define-display-method TremoloRepeatedMusic (expr parser) - (let* ((main (ly:music-property expr 'element)) - (children (if (music-is-of-type? main 'sequential-music) - ;; \repeat tremolo n { ... } - (length (extract-named-music main '(EventChord - NoteEvent))) - ;; \repeat tremolo n c4 - 1)) - (times (ly:music-property expr 'repeat-count)) - - ;; # of dots is equal to the 1 in bitwise representation (minus 1)! - (dots (1- (logcount (* times children)))) - ;; The remaining missing multiplicator to scale the notes by - ;; times * children - (mult (/ (* times children (ash 1 dots)) (1- (ash 2 dots)))) - (shift (- (ly:intlog2 (floor mult))))) - (set! main (ly:music-deep-copy main)) - ;; Adjust the time of the notes - (ly:music-compress main (ly:make-moment children 1)) - ;; Adjust the displayed note durations - (shift-duration-log main (- shift) (- dots)) - (format #f "\\repeat tremolo ~a ~a" - times - (music->lily-string main parser)))) + (repeat->lily-string expr "tremolo" parser)) ;;; ;;; Contexts diff --git a/scm/define-music-types.scm b/scm/define-music-types.scm index c71265a3ed..8d0464bf4c 100644 --- a/scm/define-music-types.scm +++ b/scm/define-music-types.scm @@ -685,8 +685,7 @@ Syntax: @code{\\times @var{fraction} @var{music}}, e.g., . ((description . "Repeated notes denoted by tremolo beams.") (iterator-ctor . ,ly:chord-tremolo-iterator::constructor) (start-callback . ,ly:repeated-music::first-start) - ;; the length of the repeat is handled by shifting the note logs - (length-callback . ,ly:repeated-music::folded-music-length) + (length-callback . ,ly:repeated-music::unfolded-music-length) (types . (general-music repeated-music tremolo-repeated-music)) )) @@ -815,16 +814,3 @@ override earlier ones." (ly:error (_ "bad make-music argument: ~S") e)))))) (set-props music-properties) m))) - -(define-public (make-repeated-music name) - (let* ((repeated-music (assoc-get name '(("volta" . VoltaRepeatedMusic) - ("unfold" . UnfoldedRepeatedMusic) - ("percent" . PercentRepeatedMusic) - ("tremolo" . TremoloRepeatedMusic)))) - (repeated-music-name (if repeated-music - repeated-music - (begin - (ly:warning (_ "unknown repeat type `~S'") name) - (ly:warning (_ "See define-music-types.scm for supported repeats")) - 'VoltaRepeatedMusic)))) - (make-music repeated-music-name))) diff --git a/scm/music-functions.scm b/scm/music-functions.scm index 05352b0953..c7f41eaad8 100644 --- a/scm/music-functions.scm +++ b/scm/music-functions.scm @@ -243,18 +243,23 @@ The number of dots in the shifted music may not be less than zero." (max 0 (+ dot (ly:duration-dot-count d))) cp))) (set! (ly:music-property music 'duration) nd))) + ;clear cached length, since it's no longer valid + (set! (ly:music-property music 'length) '()) music)) (define-public (shift-duration-log music shift dot) (music-map (lambda (x) (shift-one-duration-log x shift dot)) music)) -(define-public (make-repeat name times main alts) - "Create a repeat music expression, with all properties initialized -properly." +(define-public (tremolo::get-music-list tremolo) + "Given a tremolo repeat, return a list of music to engrave for it. +This will be a stretched copy of its body, plus a TremoloEvent or +TremoloSpanEvent. + +This is called only by Chord_tremolo_iterator." (define (first-note-duration music) - "Finds the duration of the first NoteEvent by searching depth-first -through MUSIC." + "Finds the duration of the first NoteEvent by searching +depth-first through MUSIC." ;; NoteEvent or a non-expanded chord-repetition ;; We just take anything that actually sports an announced duration. (if (ly:duration? (ly:music-property music 'duration)) @@ -267,46 +272,72 @@ through MUSIC." (if (ly:duration? dur) dur (loop (cdr elts)))))))) - - (let ((talts (if (< times (length alts)) - (begin - (ly:warning (_ "More alternatives than repeats. Junking excess alternatives")) - (take alts times)) - alts)) - (r (make-repeated-music name))) - (set! (ly:music-property r 'element) main) - (set! (ly:music-property r 'repeat-count) (max times 1)) - (set! (ly:music-property r 'elements) talts) - (if (and (equal? name "tremolo") - (pair? (extract-named-music main '(EventChord NoteEvent)))) - ;; This works for single-note and multi-note tremolos! - (let* ((children (if (music-is-of-type? main 'sequential-music) - ;; \repeat tremolo n { ... } - (length (extract-named-music main '(EventChord - NoteEvent))) - ;; \repeat tremolo n c4 - 1)) - ;; # of dots is equal to the 1 in bitwise representation (minus 1)! - (dots (1- (logcount (* times children)))) - ;; The remaining missing multiplicator to scale the notes by + (let* ((times (ly:music-property tremolo 'repeat-count)) + (body (ly:music-property tremolo 'element)) + (children (if (music-is-of-type? body 'sequential-music) + ;; \repeat tremolo n { ... } + (length (extract-named-music body '(EventChord + NoteEvent))) + ;; \repeat tremolo n c4 + 1)) + (tremolo-type (if (positive? children) + (let* ((note-duration (first-note-duration body)) + (duration-log (if (ly:duration? note-duration) + (ly:duration-log note-duration) + 1))) + (ash 1 duration-log)) + '())) + (stretched (ly:music-deep-copy body))) + (if (positive? children) + ;; # of dots is equal to the 1 in bitwise representation (minus 1)! + (let* ((dots (1- (logcount (* times children)))) + ;; The remaining missing multiplier to scale the notes by ;; times * children (mult (/ (* times children (ash 1 dots)) (1- (ash 2 dots)))) - (shift (- (ly:intlog2 (floor mult)))) - (note-duration (first-note-duration r)) - (duration-log (if (ly:duration? note-duration) - (ly:duration-log note-duration) - 1)) - (tremolo-type (ash 1 duration-log))) - (set! (ly:music-property r 'tremolo-type) tremolo-type) + (shift (- (ly:intlog2 (floor mult))))) (if (not (and (integer? mult) (= (logcount mult) 1))) (ly:music-warning - main + body (ly:format (_ "invalid tremolo repeat count: ~a") times))) - ;; Adjust the time of the notes - (ly:music-compress r (ly:make-moment 1 children)) + ;; Make each note take the full duration + (ly:music-compress stretched (ly:make-moment 1 children)) ;; Adjust the displayed note durations - (shift-duration-log r shift dots)) - r))) + (shift-duration-log stretched shift dots))) + ;; Return the stretched body plus a tremolo event + (if (= children 1) + (list (make-music 'TremoloEvent + 'repeat-count times + 'tremolo-type tremolo-type + 'origin (ly:music-property tremolo 'origin)) + stretched) + (list (make-music 'TremoloSpanEvent + 'span-direction START + 'repeat-count times + 'tremolo-type tremolo-type + 'origin (ly:music-property tremolo 'origin)) + stretched + (make-music 'TremoloSpanEvent + 'span-direction STOP + 'origin (ly:music-property tremolo 'origin)))))) + +(define-public (make-repeat name times main alts) + "Create a repeat music expression, with all properties initialized +properly." + (let ((type (or (assoc-get name '(("volta" . VoltaRepeatedMusic) + ("unfold" . UnfoldedRepeatedMusic) + ("percent" . PercentRepeatedMusic) + ("tremolo" . TremoloRepeatedMusic))) + (begin (ly:warning (_ "unknown repeat type `~S': must be volta, unfold, percent, or tremolo") name) + 'VoltaRepeatedMusic))) + (talts (if (< times (length alts)) + (begin + (ly:warning (_ "More alternatives than repeats. Junking excess alternatives")) + (take alts times)) + alts))) + (make-music type + 'element main + 'repeat-count (max times 1) + 'elements talts))) (define (calc-repeat-slash-count music) "Given the child-list @var{music} in @code{PercentRepeatMusic}, @@ -341,40 +372,10 @@ beats to be distinguished." (define-public (unfold-repeats music) "Replace all repeats with unfolded repeats." - (let ((es (ly:music-property music 'elements)) (e (ly:music-property music 'element))) - (if (music-is-of-type? music 'repeated-music) - (let* ((props (ly:music-mutable-properties music)) - (old-name (ly:music-property music 'name)) - (flattened (flatten-alist props))) - (set! music (apply make-music (cons 'UnfoldedRepeatedMusic - flattened))) - - (if (and (equal? old-name 'TremoloRepeatedMusic) - (pair? (extract-named-music e '(EventChord NoteEvent)))) - ;; This works for single-note and multi-note tremolos! - (let* ((children (if (music-is-of-type? e 'sequential-music) - ;; \repeat tremolo n { ... } - (length (extract-named-music e '(EventChord - NoteEvent))) - ;; \repeat tremolo n c4 - 1)) - (times (ly:music-property music 'repeat-count)) - - ;; # of dots is equal to the 1 in bitwise representation (minus 1)! - (dots (1- (logcount (* times children)))) - ;; The remaining missing multiplicator to scale the notes by - ;; times * children - (mult (/ (* times children (ash 1 dots)) (1- (ash 2 dots)))) - (shift (- (ly:intlog2 (floor mult))))) - - ;; Adjust the time of the notes - (ly:music-compress music (ly:make-moment children 1)) - ;; Adjust the displayed note durations - (shift-duration-log music (- shift) (- dots)))))) - + (set! music (make-music 'UnfoldedRepeatedMusic music))) (if (pair? es) (set! (ly:music-property music 'elements) (map unfold-repeats es))) -- 2.39.5