From: Reinhold Kainhofer Date: Sat, 10 Jul 2010 09:10:02 +0000 (+0200) Subject: Add support for multi-note tremolo X-Git-Tag: release/2.13.29-1~79 X-Git-Url: https://git.donarmstrong.com/?a=commitdiff_plain;h=30d01cfe87eabe3c84471ac33f8d55e53b2c9240;p=lilypond.git Add support for multi-note tremolo -) Remove all checks for exactly one or two notes as arguments to a tremolo repeat -) Create proper beaming for multi-note tremolo (so far, the chord-tremolo-engraver always assumed exactly two beamed notes) -) Extend the make-repeat function to scale the notes properly also in the case of multi-note tremolos patch from issue 1786041 on codereview.appspot.com --- diff --git a/input/regression/repeat-tremolo-three-notes.ly b/input/regression/repeat-tremolo-three-notes.ly new file mode 100644 index 0000000000..210ce39e55 --- /dev/null +++ b/input/regression/repeat-tremolo-three-notes.ly @@ -0,0 +1,19 @@ +\version "2.13.28" + +\header { + texidoc = "A tremolo can have more than two notes. Also check that +linebreaks between tremolos still work and that empty tremolos don't crash." +} + +\paper { ragged-right = ##t } +\score { + \new Staff \relative c' { + \time 3/4 + \repeat tremolo 16 { a64 c e } | + \repeat tremolo 8 { a,64 c e } + \repeat tremolo 4 { a,64 c e } + \repeat tremolo 2 { a,32 c e } |\break + \repeat tremolo 16 { a64 c e } + \repeat tremolo 8 {} | + } +} diff --git a/lily/chord-tremolo-engraver.cc b/lily/chord-tremolo-engraver.cc index 141f7313e4..f0ac33d25a 100644 --- a/lily/chord-tremolo-engraver.cc +++ b/lily/chord-tremolo-engraver.cc @@ -54,10 +54,10 @@ class Chord_tremolo_engraver : public Engraver protected: Stream_event *repeat_; - // current direction of beam (first RIGHT, then LEFT) - Direction beam_dir_; - Spanner *beam_; + // Store the pointer to the previous stem, so we can create a beam if + // necessary and end the spanner + Grob *previous_stem_; protected: virtual void finalize (); @@ -70,7 +70,7 @@ Chord_tremolo_engraver::Chord_tremolo_engraver () { beam_ = 0; repeat_ = 0; - beam_dir_ = CENTER; + previous_stem_ = 0; } IMPLEMENT_TRANSLATOR_LISTENER (Chord_tremolo_engraver, tremolo_span); @@ -80,18 +80,15 @@ Chord_tremolo_engraver::listen_tremolo_span (Stream_event *ev) Direction span_dir = to_dir (ev->get_property ("span-direction")); if (span_dir == START) { - if (ASSIGN_EVENT_ONCE (repeat_, ev)) - { - beam_dir_ = RIGHT; - } + ASSIGN_EVENT_ONCE (repeat_, ev); } else if (span_dir == STOP) { if (!repeat_) - ev->origin ()->warning (_ ("No tremolo to end")); + ev->origin ()->warning (_ ("No tremolo to end")); repeat_ = 0; beam_ = 0; - beam_dir_ = CENTER; + previous_stem_ = 0; } } @@ -126,17 +123,26 @@ Chord_tremolo_engraver::acknowledge_stem (Grob_info info) int gap_count = min (flags, intlog2 (repeat_count) + 1); Grob *s = info.grob (); - Stem::set_beaming (s, flags, beam_dir_); + if (previous_stem_) + { + // FIXME: We know that the beam has ended only in listen_tremolo_span + // but then it is too late for Spanner_break_forbid_engraver + // to allow a line break... So, as a nasty hack, announce the + // spanner's end after each note except the first. The only + // "drawback" is that for multi-note tremolos a break would + // theoretically be allowed after the second note (but since + // that note is typically not at a barline, I don't think + // anyone will ever notice!) + announce_end_grob (beam_, previous_stem_->self_scm ()); + // Create the whole beam between previous and current note + Stem::set_beaming (previous_stem_, flags, RIGHT); + Stem::set_beaming (s, flags, LEFT); + } if (Stem::duration_log (s) != 1) - beam_->set_property ("gap-count", scm_from_int (gap_count)); + beam_->set_property ("gap-count", scm_from_int (gap_count)); + - if (beam_dir_ == RIGHT) - { - beam_dir_ = LEFT; - announce_end_grob (beam_, s->self_scm ()); - } - if (info.ultimate_event_cause ()->in_event_class ("rhythmic-event")) Beam::add_stem (beam_, s); else @@ -147,6 +153,9 @@ Chord_tremolo_engraver::acknowledge_stem (Grob_info info) else ::warning (s); } + // Store current grob, so we can possibly end the spanner here (and + // reset the beam direction to RIGHT) + previous_stem_ = s; } } diff --git a/lily/chord-tremolo-iterator.cc b/lily/chord-tremolo-iterator.cc index e6c4dd604a..4a64221fa9 100644 --- a/lily/chord-tremolo-iterator.cc +++ b/lily/chord-tremolo-iterator.cc @@ -40,11 +40,6 @@ Chord_tremolo_iterator::get_music_list () const int elt_count = body_is_sequential ? scm_ilength (body->get_property ("elements")) : 1; - if (body_is_sequential && - (elt_count != 2 - && elt_count != 1)) - mus->origin ()->warning (_f ("expect 2 elements for chord tremolo, found %d", elt_count)); - if (elt_count <= 0) elt_count = 1; @@ -57,7 +52,7 @@ Chord_tremolo_iterator::get_music_list () const 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); diff --git a/scm/music-functions.scm b/scm/music-functions.scm index 57f17baaa2..9f173ff764 100644 --- a/scm/music-functions.scm +++ b/scm/music-functions.scm @@ -265,9 +265,14 @@ through MUSIC." (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 (equal? name "tremolo") - (let* ((dots (1- (logcount times))) - (mult (/ (* times (ash 1 dots)) (1- (ash 2 dots)))) + (if (and (equal? name "tremolo") (> (length (ly:music-property main 'elements)) 0)) + ;; This works for single-note and multi-note tremolos! + (let* ((children (length (ly:music-property main 'elements))) + ;; # 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)))) (note-duration (first-note-duration r)) (duration-log (if (ly:duration? note-duration) @@ -277,20 +282,11 @@ through MUSIC." (set! (ly:music-property r 'tremolo-type) tremolo-type) (if (not (integer? mult)) (ly:warning (_ "invalid tremolo repeat count: ~a") times)) - (if (memq 'sequential-music (ly:music-property main 'types)) - ;; \repeat "tremolo" { c4 d4 } - (let ((children (length (ly:music-property main 'elements)))) - - ;; fixme: should be more generic. - (if (and (not (= children 2)) - (not (= children 1))) - (ly:warning (_ "expecting 2 elements for chord tremolo, found ~a") children)) - (ly:music-compress r (ly:make-moment 1 children)) - (shift-duration-log r - (if (= children 2) (1- shift) shift) - dots)) - ;; \repeat "tremolo" c4 - (shift-duration-log r shift dots))) + ;; \repeat tremolo n c4 + ;; Adjust the time of the notes + (ly:music-compress r (ly:make-moment 1 children)) + ;; Adjust the displayed note durations + (shift-duration-log r shift dots)) r))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;