]> git.donarmstrong.com Git - lilypond.git/commitdiff
Fix beamlet pointing wrong way in tuplets (issue 11).
authorCarl Sorensen <c_sorensen@byu.edu>
Sat, 20 Aug 2011 22:41:03 +0000 (16:41 -0600)
committerDavid Kastrup <dak@gnu.org>
Thu, 24 Nov 2011 10:33:26 +0000 (11:33 +0100)
Add data structure to capture the tuplet structure.

Make beaming reflect subdivisions.

Eliminate rule to force minimum beamlets by combining all beams,
and replace it with rule to make beamlets point toward complete
beats (high rhythmic importance).

Add regression tests.

Adjust to handle beats properly for subdivision

Add regression test for beamlets pointing toward beats

input/regression/beam-multiplicity-over-rests.ly
input/regression/beamlet-point-toward-beat.ly [new file with mode: 0644]
input/regression/beamlet-test.ly [new file with mode: 0644]
lily/auto-beam-engraver.cc
lily/beam-engraver.cc
lily/beaming-pattern.cc
lily/include/beaming-pattern.hh

index 5578931633d59e426e17c503d27179cab35e964d..f16c8c6467ecc6c53c58d9624d53e207a98b5190 100644 (file)
@@ -1,14 +1,14 @@
-\version "2.14.0"
+\version "2.15.17"
 
 \header {
-  texidoc = "When a beam goes over a rest, there should not be any 
-beamlets pointing towards the rest unless absolutely necessary."
+  texidoc = "When a beam goes over a rest, beamlets should be as necessary
+  to show the beat structure."
 }
 
 \relative c' {
   c8[ r16 c32 c32]
   c32[ r16 c32 c8]
-  c32[ r16 c64 c8 c64]
+  c32[ r16 c64 c64 ~ c16.. c64]
   c32[ c32 r16 c8]
   c16[ r32 c32 r16 c16]
   c16[ r16 c32 r32 c16]
diff --git a/input/regression/beamlet-point-toward-beat.ly b/input/regression/beamlet-point-toward-beat.ly
new file mode 100644 (file)
index 0000000..b1a761f
--- /dev/null
@@ -0,0 +1,14 @@
+\version "2.15.20"
+
+\header {
+  texidoc = "
+Beamlets should point in the direction of the beat to which they
+belong.
+"
+}
+
+
+\relative c' {
+b16. b32 b32 b16.
+b16.[ b32 b   b b b16.]
+}
diff --git a/input/regression/beamlet-test.ly b/input/regression/beamlet-test.ly
new file mode 100644 (file)
index 0000000..dc8d5f0
--- /dev/null
@@ -0,0 +1,36 @@
+\version "2.15.20"
+
+\header {
+  texidoc = "
+Beamlets should point away from complete beat units and toward off-beat or
+broken beat units.  This should work in tuplets as well as in ordinary time.
+"
+}
+
+\relative c'' {
+    \times 2/3 {
+      c8. c16 c8
+    }
+   \times 2/3 {
+      c8 c16 c8.
+    }
+  \times 4/5 {
+    c8[ c8. c16 c8 c8]
+  }
+  \times 4/5 {
+    c8[ c8 c16 c8. c8]
+  }
+  \times 4/5 {
+    c8 c16 c8. c8 c8
+  }
+  \times 4/5 {
+    c8 c8 c8. c16 c8
+  }
+  c8.[ c16 c8 c8]
+  c8[ c16 c8. c8]
+  c8[ c8. c16 c8]
+  c8.[ c16 c8. c16]
+  \times 4/5 { c8 [ c16 c8 c16 c8 c8 ] }
+  \times 4/5 { a8 a32 a8 a16. a8 a8 }
+}
+
index 76a3023e10bb1d251619270e1040cf8e85459d1a..b3cd6f4bab61d02c7dcbeaa030119cbe2da0c7a7 100644 (file)
@@ -404,7 +404,10 @@ Auto_beam_engraver::acknowledge_stem (Grob_info info)
   if (bool (beam_start_location_.grace_part_) != bool (now.grace_part_))
     return;
 
-  Moment dur = unsmob_duration (ev->get_property ("duration"))->get_length ();
+  Duration *stem_duration = unsmob_duration (ev->get_property ("duration"));
+  Moment dur = stem_duration->get_length ();
+
+  //Moment dur = unsmob_duration (ev->get_property ("duration"))->get_length ();
   Moment measure_now = measure_position (context ());
   bool recheck_needed = false;
 
@@ -425,7 +428,8 @@ Auto_beam_engraver::acknowledge_stem (Grob_info info)
 
   grouping_->add_stem (now - beam_start_moment_ + beam_start_location_,
                        durlog - 2,
-                       Stem::is_invisible (stem));
+                       Stem::is_invisible (stem),
+                       stem_duration->factor ());
   stems_->push_back (stem);
   last_add_mom_ = now;
   extend_mom_ = max (extend_mom_, now) + get_event_length (ev, now);
index 654a7f57d17293c0caf5144a4c5344f0eec3b772..e28d15683619b5b866cedf837d0ddbc355d3ec4b 100644 (file)
@@ -268,7 +268,10 @@ Beam_engraver::acknowledge_stem (Grob_info info)
     }
 
   last_stem_added_at_ = now;
-  int durlog = unsmob_duration (ev->get_property ("duration"))->duration_log ();
+
+  Duration *stem_duration = unsmob_duration (ev->get_property ("duration"));
+  int durlog = stem_duration->duration_log ();
+  //int durlog = unsmob_duration (ev->get_property ("duration"))->duration_log ();
   if (durlog <= 2)
     {
       ev->origin ()->warning (_ ("stem does not fit in beam"));
@@ -287,7 +290,8 @@ Beam_engraver::acknowledge_stem (Grob_info info)
   Moment stem_location = now - beam_start_mom_ + beam_start_location_;
   beam_info_->add_stem (stem_location,
                         max (durlog - 2, 0),
-                        Stem::is_invisible (stem));
+                        Stem::is_invisible (stem),
+                        stem_duration->factor ());
   Beam::add_stem (beam_, stem);
 }
 
index ada6770a5c2e25d4db7493ce8d1d03861abf7f07..916ec145af96d0e438fe33db217439bc2d68660c 100644 (file)
@@ -39,16 +39,18 @@ Beam_rhythmic_element::Beam_rhythmic_element ()
   beam_count_drul_[LEFT] = 0;
   beam_count_drul_[RIGHT] = 0;
   invisible_ = false;
+  factor_ = Rational (1);
 
 }
 
-Beam_rhythmic_element::Beam_rhythmic_element (Moment m, int i, bool inv)
+Beam_rhythmic_element::Beam_rhythmic_element (Moment m, int i, bool inv, Rational factor)
 {
   start_moment_ = m;
   rhythmic_importance_ = 0;
   beam_count_drul_[LEFT] = i;
   beam_count_drul_[RIGHT] = i;
   invisible_ = inv;
+  factor_ = factor;
 }
 
 void
@@ -98,15 +100,9 @@ Beaming_pattern::flag_direction (Beaming_options const &options, vsize i) const
   if (count <= left_count && count <= right_count)
     return CENTER;
 
-  // Try to avoid sticking-out flags as much as possible by pointing my flags
-  // at the neighbour with the most flags.
-  else if (right_count > left_count)
-    return RIGHT;
-  else if (left_count > right_count)
-    return LEFT;
-
   // If all else fails, point the beamlet away from the important moment.
-  return (infos_[i].rhythmic_importance_ <= infos_[i + 1].rhythmic_importance_) ? RIGHT : LEFT;
+  return (infos_[i].rhythmic_importance_ <= infos_[i + 1].rhythmic_importance_)
+         ? RIGHT : LEFT;
 }
 
 void
@@ -135,68 +131,141 @@ Beaming_pattern::beamify (Beaming_options const &options)
 
   find_rhythmic_importance (options);
 
+  vector <Direction> flag_directions;
+  // Get the initial flag directions
+  for (vsize i = 0; i < infos_.size (); i++)
+    flag_directions.push_back (flag_direction (options, i));
+
+  // Correct flag directions for subdivision
   for (vsize i = 1; i < infos_.size () - 1; i++)
     {
-      Direction non_flag_dir = other_dir (flag_direction (options, i));
-      if (non_flag_dir)
+      if ((flag_directions[i] == CENTER) && (flag_directions[i - 1] == LEFT))
+        flag_directions[i] = RIGHT;
+      if ((flag_directions[i] == CENTER) && (flag_directions[i + 1] == RIGHT))
+        flag_directions[i] = LEFT;
+    }
+
+  // Set the count on each side of the stem
+  // We need to run this code twice to make both the
+  // left and the right counts work properly
+  for (int i = 0; i < 2; i++)
+    for (vsize i = 1; i < infos_.size () - 1; i++)
+      {
+        Direction non_flag_dir = other_dir (flag_directions[i]);
+        if (non_flag_dir)
+          {
+            int importance = infos_[i + 1].rhythmic_importance_;
+            int count = (importance < 0 && options.subdivide_beams_)
+                        ? 1 : min (min (infos_[i].count (non_flag_dir),
+                                        infos_[i + non_flag_dir].count (-non_flag_dir)),
+                                   infos_[i - non_flag_dir].count (non_flag_dir));
+
+            infos_[i].beam_count_drul_[non_flag_dir] = count;
+          }
+      }
+}
+
+/*
+   Get the group start position, the next group starting position, and the
+   next beat starting position, given start_moment, base_moment,
+   grouping, and factor
+*/
+void
+find_location (SCM grouping, Moment base_moment, Moment start_moment,
+               Rational factor, Moment *group_pos, Moment *next_group_pos,
+               Moment *next_beat_pos)
+{
+  *group_pos = Moment (0);
+  *next_group_pos = Moment (0);
+  *next_beat_pos = base_moment;
+
+  while (*next_beat_pos <= start_moment)
+    *next_beat_pos += base_moment;
+
+  while (*next_group_pos < *next_beat_pos)
+    {
+      int count = 1;  //default -- 1 base moments in a beam
+      if (scm_is_pair (grouping))
         {
-          int importance = (non_flag_dir == LEFT)
-                           ? infos_[i].rhythmic_importance_ : infos_[i + 1].rhythmic_importance_;
-          int count = (importance < 0 && options.subdivide_beams_)
-                      ? 1 : min (infos_[i].count (non_flag_dir),
-                                 infos_[i + non_flag_dir].count (-non_flag_dir));
+          count = scm_to_int (scm_car (grouping));
+          grouping = scm_cdr (grouping);
+        }
 
-          infos_[i].beam_count_drul_[non_flag_dir] = count;
+      // If we have a tuplet, the count should be determined from
+      // the maximum tuplet size for beamed tuplets.
+      int tuplet_count = factor.num ();
+      if (tuplet_count > 1)
+        {
+          // We use 1/8 as the base moment for the tuplet because it's
+          // the largest beamed value.  If the tuplet is shorter, it's
+          // OK, the code still works
+          int test_count = (tuplet_count * Moment (Rational (1, 8)) / base_moment).num ();
+          if (test_count > count) count = test_count;
         }
+      *group_pos = *next_group_pos;
+      *next_group_pos = *group_pos + count * base_moment;
     }
 }
 
 void
 Beaming_pattern::find_rhythmic_importance (Beaming_options const &options)
 {
-  Moment measure_pos (0);
+  Moment group_pos (0);  // 0 is the start of the first group
+  Moment next_group_pos (0);
+  Moment next_beat_pos (options.base_moment_);
+  int tuplet_count = 1;
+
   SCM grouping = options.grouping_;
   vsize i = 0;
 
+  // Find where we are in the beat structure of the measure
+  if (infos_.size ())
+    find_location (grouping, options.base_moment_, infos_[i].start_moment_,
+                   infos_[i].factor_, &group_pos, &next_group_pos, &next_beat_pos);
+
   // Mark the importance of stems that start at a beat or a beat group.
   while (i < infos_.size ())
     {
-      // If a beat grouping is not specified, default to 2 beats per group.
-      int count = 2;
-      if (scm_is_pair (grouping))
-        {
-          count = scm_to_int (scm_car (grouping));
-          grouping = scm_cdr (grouping);
-        }
-
+      tuplet_count = infos_[i].factor_.den ();
+      if ((next_beat_pos > next_group_pos)
+          || (infos_[i].start_moment_ > next_beat_pos))
+        // Find the new group ending point
+        find_location (grouping, options.base_moment_, infos_[i].start_moment_,
+                       infos_[i].factor_, &group_pos, &next_group_pos, &next_beat_pos);
       // Mark the start of this beat group
-      if (infos_[i].start_moment_ == measure_pos)
+      if (infos_[i].start_moment_ == group_pos)
         infos_[i].rhythmic_importance_ = -2;
-
-      // Mark the start of each unit up to the end of this beat group.
-      for (int unit = 1; unit <= count; unit++)
+      // Work through the end of the beat group or the end of the beam
+      while (i < infos_.size () && infos_[i].start_moment_ < next_group_pos)
         {
-          Moment next_measure_pos = measure_pos + options.base_moment_;
-
-          while (i < infos_.size () && infos_[i].start_moment_ < next_measure_pos)
+          Moment dt = infos_[i].start_moment_ - group_pos;
+          Rational tuplet = infos_[i].factor_;
+          Moment tuplet_moment (tuplet);
+          // set the beat end (if not in a tuplet) and increment the next beat
+          if (tuplet_count == 1 && infos_[i].start_moment_ == next_beat_pos)
             {
-              Moment dt = infos_[i].start_moment_ - measure_pos;
-
-              // The rhythmic importance of a stem between beats depends on its fraction
-              // of a beat: those stems with a lower denominator are deemed more
-              // important.
-              // FIXME: This is not the right way to do things for tuplets. For example,
-              // in an 8th-note triplet with a quarter-note beat, 1/3 of a beat should be
-              // more important than 1/2.
-              if (infos_[i].rhythmic_importance_ >= 0)
-                infos_[i].rhythmic_importance_ = (int) (dt / options.base_moment_).den ();
-
-              i++;
+              infos_[i].rhythmic_importance_ = -1;
+              next_beat_pos += options.base_moment_;
             }
+          // The rhythmic importance of a stem between beats depends on its fraction
+          // of a beat: those stems with a lower denominator are deemed more
+          // important.  For tuplets, we need to make sure that we use
+          // the fraction of the tuplet, instead of the fraction of
+          // a beat.
+          Moment ratio = (dt / options.base_moment_ / tuplet_moment);
+          if (infos_[i].rhythmic_importance_ >= 0)
+            infos_[i].rhythmic_importance_ = (int) ratio.den ();
+          i++;
+        }
 
-          measure_pos = next_measure_pos;
-          if (i < infos_.size () && infos_[i].start_moment_ == measure_pos)
+      if (i < infos_.size () && infos_[i].start_moment_ == next_beat_pos)
+        {
+          if (tuplet_count == 1)
             infos_[i].rhythmic_importance_ = -1;
+          next_beat_pos += options.base_moment_;
+          if (infos_[i].start_moment_ == next_group_pos)
+            infos_[i].rhythmic_importance_ = -2;
+          i++;
         }
     }
 }
@@ -228,9 +297,9 @@ Beaming_pattern::unbeam_invisible_stems ()
 }
 
 void
-Beaming_pattern::add_stem (Moment m, int b, bool invisible)
+Beaming_pattern::add_stem (Moment m, int b, bool invisible, Rational factor)
 {
-  infos_.push_back (Beam_rhythmic_element (m, b, invisible));
+  infos_.push_back (Beam_rhythmic_element (m, b, invisible, factor));
 }
 
 Beaming_pattern::Beaming_pattern ()
@@ -265,8 +334,14 @@ Beaming_pattern::invisibility (int i) const
   return infos_.at (i).invisible_;
 }
 
+Rational
+Beaming_pattern::factor (int i) const
+{
+  return infos_.at (i).factor_;
+}
+
 /*
-    Split a beamin pattern at index i and return a new
+    Split a beaming pattern at index i and return a new
     Beaming_pattern containing the removed elements
 */
 Beaming_pattern *
@@ -281,7 +356,8 @@ Beaming_pattern::split_pattern (int i)
       count = max (beamlet_count (j, LEFT), beamlet_count (j, RIGHT));
       new_pattern->add_stem (start_moment (j),
                              count,
-                             invisibility (j));
+                             invisibility (j),
+                             factor (j));
     }
   for (vsize j = i + 1; j < infos_.size ();)
     infos_.pop_back ();
index 58ec08443b6ed112476457b5cf9257f029aee128..1fe990aaa49392a569b8c5196698c72d38112b5b 100644 (file)
@@ -43,7 +43,9 @@ struct Beam_rhythmic_element
   int rhythmic_importance_;
   bool invisible_;
 
-  Beam_rhythmic_element (Moment, int, bool);
+  Rational factor_;
+
+  Beam_rhythmic_element (Moment, int, bool, Rational);
   Beam_rhythmic_element ();
 
   int count (Direction d) const;
@@ -61,9 +63,10 @@ public:
 
   void beamify (Beaming_options const &);
   void de_grace ();
-  void add_stem (Moment d, int beams, bool invisible);
+  void add_stem (Moment d, int beams, bool invisible, Rational factor);
   int beamlet_count (int idx, Direction d) const;
   bool invisibility (int idx) const;
+  Rational factor (int idx) const;
   Moment start_moment (int idx) const;
   Moment end_moment (int idx) const;
   Beaming_pattern *split_pattern (int idx);