--- /dev/null
+\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 {} |
+ }
+}
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 ();
{
beam_ = 0;
repeat_ = 0;
- beam_dir_ = CENTER;
+ previous_stem_ = 0;
}
IMPLEMENT_TRANSLATOR_LISTENER (Chord_tremolo_engraver, tremolo_span);
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;
}
}
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
else
::warning (s);
}
+ // Store current grob, so we can possibly end the spanner here (and
+ // reset the beam direction to RIGHT)
+ previous_stem_ = s;
}
}
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;
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);
(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)
(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)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;