]> git.donarmstrong.com Git - lilypond.git/commitdiff
Break slurs between alternative endings in repeats; issue 1698.
authorMike Solomon <mike@apollinemike.com>
Sat, 6 Apr 2013 07:54:58 +0000 (09:54 +0200)
committerMike Solomon <mike@apollinemike.com>
Sat, 6 Apr 2013 07:54:58 +0000 (09:54 +0200)
Create new event-types BreakPhrasingSlurEvent BreakSlurEvent,
and a music-function \free so that users can create them.
Create these event-types in the volta-repeat-iterator to break
slurs between alternatives in a \repeat volta structure.

14 files changed:
input/regression/repeat-slur.ly [new file with mode: 0644]
lily/include/slur-proto-engraver.hh
lily/phrasing-slur-engraver.cc
lily/slur-engraver.cc
lily/slur-proto-engraver.cc
lily/slur.cc
lily/spanner.cc
lily/volta-repeat-iterator.cc
ly/music-functions-init.ly
ly/spanners-init.ly
scm/define-context-properties.scm
scm/define-event-classes.scm
scm/define-grob-properties.scm
scm/define-music-types.scm

diff --git a/input/regression/repeat-slur.ly b/input/regression/repeat-slur.ly
new file mode 100644 (file)
index 0000000..67420fc
--- /dev/null
@@ -0,0 +1,162 @@
+\version "2.17.14"
+
+\header {
+  texidoc = "Slurs are automatically broken at repeats.
+This behavior can be changed by setting @code{\\slurOverRepeat}
+to @code{#t}. To manually break a slur at a bar line, use
+@code{\\breakSlur}. To manually start a free slur at a bar, use
+@code{\\free} with an opening parenthesis. To manually stop a
+free slur at a bar, use @code{\\free} with a closing parenthesis.
+"
+}
+
+\new Staff {
+  a'4 ( b' c' d'
+  \repeat volta 3
+    { a' ) b' ( c' d' }
+    \alternative { { a' ) b' c' ( d' }
+                   { a' b' ) b'2 ( }
+                   { a'4 b' ) b'2 ( } }
+
+  a'1 ) \bar "|."
+}
+
+\new Staff {
+  a'4 ( b' c' d'
+  \repeat volta 2
+    { a' ) b' ( c' d' }
+    \alternative { { a' ) b' c' d' ( }
+                   { a' b' ) b'2 ( } }
+
+  a'1 ) \bar "|."
+}
+
+\new Staff {
+  a'4 ( b' c' d'
+  \repeat volta 2
+    { a' ) b' ( c' d' }
+    \alternative { { a' ) b' c' ( d' }
+                   { a' ) b' b'2 ( } }
+
+  a'1 ) \bar "|."
+}
+
+\new Staff {
+  a'4 ( b' c' d'
+  \repeat volta 2
+    { a' ) b' ( c' d' }
+    \alternative { { a' ) b' c' d' ( }
+                   { a' ) b' b'2 ( } }
+
+  a'1 ) \bar "|."
+}
+
+\new Staff {
+  \set slurOverRepeat = ##t
+  a'4 ( b' c' d'
+  \repeat volta 2
+    { a' ) b' ( c' d' }
+    \alternative { { a' ) b' c' ( d' }
+                   { a' b' ) b'2 ( } }
+
+  a'1 ) \bar "|."
+}
+
+\new Staff {
+  \repeat volta 2
+    { \free ( a'4 ) b' ( c' d' }
+    \alternative { { a' ) b' c' ( d' }
+                   { a' b' ) b'2 ( } }
+
+  a'1 ) \bar "|."
+}
+
+\new Staff {
+  a'4 ( b' c' d'
+  \repeat volta 2
+    { a' ) b' ( c' d' }
+    \alternative { { a' ) b' c' ( d' \free ) }
+                   { a' b' b'2 ( } }
+
+  a'1 ) \bar "|."
+}
+
+\new Staff {
+  a'4 ( b' c' d' \breakSlur | a' b' c' d' ) |
+}
+
+%% phrasing slurs
+
+\new Staff {
+  a'4 \( b' c' d'
+  \repeat volta 2
+    { a' \) b' \( c' d' }
+    \alternative { { a' \) b' c' \( d' }
+                   { a' b' \) b'2 \( } }
+
+  a'1 \) \bar "|."
+}
+
+\new Staff {
+  a'4 \( b' c' d'
+  \repeat volta 2
+    { a' \) b' \( c' d' }
+    \alternative { { a' \) b' c' d' \( }
+                   { a' b' \) b'2 \( } }
+
+  a'1 \) \bar "|."
+}
+
+\new Staff {
+  a'4 \( b' c' d'
+  \repeat volta 2
+    { a' \) b' \( c' d' }
+    \alternative { { a' \) b' c' \( d' }
+                   { a' \) b' b'2 \( } }
+
+  a'1 \) \bar "|."
+}
+
+\new Staff {
+  a'4 \( b' c' d'
+  \repeat volta 2
+    { a' \) b' \( c' d' }
+    \alternative { { a' \) b' c' d' \( }
+                   { a' \) b' b'2 \( } }
+
+  a'1 \) \bar "|."
+}
+
+\new Staff {
+  \set slurOverRepeat = ##t
+  a'4 \( b' c' d'
+  \repeat volta 2
+    { a' \) b' \( c' d' }
+    \alternative { { a' \) b' c' \( d' }
+                   { a' b' \) b'2 \( } }
+
+  a'1 \) \bar "|."
+}
+
+\new Staff {
+  \repeat volta 2
+    { \free \( a'4 \) b' \( c' d' }
+    \alternative { { a' \) b' c' \( d' }
+                   { a' b' \) b'2 \( } }
+
+  a'1 \) \bar "|."
+}
+
+\new Staff {
+  a'4 \( b' c' d'
+  \repeat volta 2
+    { a' \) b' \( c' d' }
+    \alternative { { a' \) b' c' \( d' \free \) }
+                   { a' b' b'2 \( } }
+
+  a'1 \) \bar "|."
+}
+
+\new Staff {
+  a'4 \( b' c' d' \breakPhrasingSlur | a' b' c' d' \) |
+}
index 0b0861775c767f2558e9ad53520d0fdda44da4a9..d0ae3c0875db2572e2e1721257b642a813f4b850 100644 (file)
@@ -38,6 +38,7 @@ protected:
   vector<Grob *> slurs_;
   vector<Grob *> end_slurs_;
   vector<Grob_info> objects_to_acknowledge_;
+  Stream_event *break_slur_;
   const char* double_property_name_;
   const char* grob_name_;
   const char* object_name_;
@@ -53,6 +54,7 @@ protected:
   DECLARE_ACKNOWLEDGER (tuplet_number);
 
   void internal_listen_slur (Stream_event *ev);
+  void internal_listen_break_slur (Stream_event *ev);
   void acknowledge_extra_object (Grob_info);
   void stop_translation_timestep ();
   void process_music ();
@@ -60,6 +62,7 @@ protected:
   bool can_create_slur (string, vsize, vsize *, Stream_event *);
   void create_slur (string spanner_id, Stream_event *ev_cause, Grob *g_cause, Direction dir, bool left_broken);
   bool try_to_end (Stream_event *ev);
+  void break_slurs ();
 
   virtual void set_melisma (bool);
   virtual void finalize ();
index 6d9aac7af68d64fb5b7220acc2ebccb0f37435d0..34b8cb09eee4f91c5e7a8925db3cc9f834529dc4 100644 (file)
@@ -35,6 +35,7 @@ class Phrasing_slur_engraver : public Slur_proto_engraver
 {
 protected:
   DECLARE_TRANSLATOR_LISTENER (phrasing_slur);
+  DECLARE_TRANSLATOR_LISTENER (break_phrasing_slur);
   DECLARE_ACKNOWLEDGER (slur);
 
 public:
@@ -44,6 +45,7 @@ public:
 Phrasing_slur_engraver::Phrasing_slur_engraver () :
   Slur_proto_engraver (0, "PhrasingSlur", "phrasing slur", "phrasing-slur-event")
 {
+  break_slur_ = 0;
 }
 
 IMPLEMENT_TRANSLATOR_LISTENER (Phrasing_slur_engraver, phrasing_slur);
@@ -53,6 +55,13 @@ Phrasing_slur_engraver::listen_phrasing_slur (Stream_event *ev)
   internal_listen_slur (ev);
 }
 
+IMPLEMENT_TRANSLATOR_LISTENER (Phrasing_slur_engraver, break_phrasing_slur);
+void
+Phrasing_slur_engraver::listen_break_phrasing_slur (Stream_event *ev)
+{
+  internal_listen_break_slur (ev);
+}
+
 void
 Phrasing_slur_engraver::acknowledge_slur (Grob_info info)
 {
index a8a54f535a1b4bdbd0edae79b25f3bdf4d51dafb..3f170355440653f15232e87ee2c28063a8951f75 100644 (file)
@@ -37,6 +37,7 @@ class Slur_engraver : public Slur_proto_engraver
 
 protected:
   DECLARE_TRANSLATOR_LISTENER (slur);
+  DECLARE_TRANSLATOR_LISTENER (break_slur);
 
 public:
   TRANSLATOR_DECLARATIONS (Slur_engraver);
@@ -45,6 +46,7 @@ public:
 Slur_engraver::Slur_engraver () :
   Slur_proto_engraver ("doubleSlurs", "Slur", "slur", "slur-event")
 {
+  break_slur_ = 0;
 }
 
 IMPLEMENT_TRANSLATOR_LISTENER (Slur_engraver, slur);
@@ -54,6 +56,13 @@ Slur_engraver::listen_slur (Stream_event *ev)
   internal_listen_slur (ev);
 }
 
+IMPLEMENT_TRANSLATOR_LISTENER (Slur_engraver, break_slur);
+void
+Slur_engraver::listen_break_slur (Stream_event *ev)
+{
+  internal_listen_break_slur (ev);
+}
+
 void
 Slur_engraver::set_melisma (bool m)
 {
index fbc5f00b844ae5488b6bfb889548e42c01d143fc..a3ced1c2c11d612bf33cff3d76dde72fc957c086 100644 (file)
@@ -52,6 +52,22 @@ Slur_proto_engraver::internal_listen_slur (Stream_event *ev)
                                      event_name_, int (d)));
 }
 
+void
+Slur_proto_engraver::internal_listen_break_slur (Stream_event *ev)
+{
+  // if break_slur_ is set, we only keep events with direction
+  if (break_slur_
+      && robust_scm2dir (ev->get_property ("span-direction"), CENTER))
+    break_slur_ = ev;
+  else if (!break_slur_)
+    break_slur_ = ev;
+  else if (break_slur_
+           && robust_scm2dir (break_slur_->get_property ("span-direction"), CENTER)
+           && robust_scm2dir (ev->get_property ("span-direction"), CENTER))
+    ev->origin ()->warning (_f ("cannot set break slur with two directions"));
+}
+
+
 void
 Slur_proto_engraver::acknowledge_note_column (Grob_info info)
 {
@@ -136,16 +152,20 @@ Slur_proto_engraver::create_slur (string spanner_id, Stream_event *ev_cause, Gro
   slurs_.push_back (slur);
   if (double_property_name_
       && to_boolean (get_property (double_property_name_)))
-  {
-    set_grob_direction (slur, DOWN);
-    slur = make_spanner (grob_name_, cause);
-    slur->set_property ("spanner-id", ly_string2scm (spanner_id));
-    set_grob_direction (slur, UP);
-    if (left_broken)
-      slur->set_bound (LEFT, ccc);
-    slurs_.push_back (slur);
-  }
-
+    {
+      set_grob_direction (slur, DOWN);
+      slur = make_spanner (grob_name_, cause);
+      slur->set_property ("spanner-id", ly_string2scm (spanner_id));
+      set_grob_direction (slur, UP);
+      if (left_broken)
+        slur->set_bound (LEFT, ccc);
+      slurs_.push_back (slur);
+    }
+  else if (g_cause && Slur::has_interface (g_cause) && left_broken)
+    {
+      g_cause->set_object ("other-half", slur->self_scm ());
+      slur->set_object ("other-half", g_cause->self_scm ());
+    }
 }
 
 bool
@@ -224,9 +244,44 @@ Slur_proto_engraver::try_to_end (Stream_event *ev)
   return ended;
 }
 
+void
+Slur_proto_engraver::break_slurs ()
+{
+  for (vsize i = slurs_.size (); i--;)
+    {
+      Grob *ccc = unsmob_grob (get_property ("currentCommandColumn"));
+      Spanner *s = dynamic_cast<Spanner *> (slurs_[i]);
+      s->set_bound (RIGHT, ccc);
+      announce_end_grob (s, SCM_EOL);
+      slurs_.erase (slurs_.begin () + i);
+      SCM maybe_dir = s->get_property_data ("direction");
+      Direction dir = is_direction (maybe_dir)
+                      ? robust_scm2dir (maybe_dir, CENTER)
+                      : CENTER;
+      create_slur (robust_scm2string (s->get_property ("spanner-id"), ""),
+                   0, s, dir, true);
+    }
+}
+
 void
 Slur_proto_engraver::process_music ()
 {
+  // break slurs that span over this column
+  // if break_slur_'s direction is center
+  if (break_slur_
+      && robust_scm2dir (break_slur_->get_property ("span-direction"), CENTER) == CENTER
+      && unsmob_grob (get_property ("currentCommandColumn")))
+    break_slurs ();
+
+  // create broken slurs starting at this column if break_slur_ is left
+  vsize old_slurs = slurs_.size ();
+  if (break_slur_
+      && robust_scm2dir (break_slur_->get_property ("span-direction"), CENTER) == LEFT
+      && can_create_slur ("", old_slurs, 0, break_slur_))
+    create_slur ("", break_slur_, 0,
+                 robust_scm2dir (break_slur_->get_property ("direction"), CENTER),
+                 true);
+
   for (vsize i = 0; i < stop_events_.size (); i++)
     {
       string id = robust_scm2string (stop_events_[i]->get_property ("spanner-id"), "");
@@ -243,18 +298,22 @@ Slur_proto_engraver::process_music ()
       else
         stop_events_[i]->origin ()->warning (_f ("cannot end %s", object_name_));
     }
+  old_slurs = slurs_.size ();
 
-  vsize old_slurs = slurs_.size ();
   for (vsize i = start_events_.size (); i--;)
     {
       Stream_event *ev = start_events_[i];
       string id = robust_scm2string (ev->get_property ("spanner-id"), "");
       Direction updown = to_dir (ev->get_property ("direction"));
-
       if (can_create_slur (id, old_slurs, &i, ev))
         create_slur (id, ev, 0, updown, false);
     }
 
+  // if BreakSlurEvent with span-dir right, we end here
+  if (break_slur_
+      && robust_scm2dir (break_slur_->get_property ("span-direction"), CENTER) == RIGHT)
+    (void) try_to_end (break_slur_);
+
   set_melisma (slurs_.size ());
 }
 
@@ -281,6 +340,11 @@ Slur_proto_engraver::stop_translation_timestep ()
       Spanner *s = dynamic_cast<Spanner *> (end_slurs_[i]);
       if (!s->get_bound (RIGHT))
         s->set_bound (RIGHT, unsmob_grob (get_property ("currentMusicalColumn")));
+      // if BreakSlurEvent with span-dir right,
+      // we set right bound to current command column
+      if (break_slur_
+          && robust_scm2dir (break_slur_->get_property ("span-direction"), CENTER) == RIGHT)
+        s->set_bound (RIGHT, unsmob_grob (get_property ("currentCommandColumn")));
       announce_end_grob (s, SCM_EOL);
     }
 
@@ -291,6 +355,7 @@ Slur_proto_engraver::stop_translation_timestep ()
   end_slurs_.clear ();
   start_events_.clear ();
   stop_events_.clear ();
+  break_slur_ = 0;
 }
 
 // no ADD_ACKNOWLEDGER / ADD_TRANSLATOR macro calls
index 0aa96c787d1d7ba482ed0170e3990ee703258236..9f12df878c20099e56b426d8d5b8263b33b6a990 100644 (file)
@@ -48,7 +48,14 @@ SCM
 Slur::calc_direction (SCM smob)
 {
   Grob *me = unsmob_grob (smob);
-  extract_grob_set (me, "note-columns", encompasses);
+  vector<Grob *> encompasses;
+  extract_grob_set (me, "note-columns", ro_encompasses);
+  encompasses.insert (encompasses.end (), ro_encompasses.begin (), ro_encompasses.end ());
+  if (Grob *other_half = unsmob_grob (me->get_object ("other-half")))
+    {
+      extract_grob_set (other_half, "note-columns", oh_encompasses);
+      encompasses.insert (encompasses.end (), oh_encompasses.begin (), oh_encompasses.end ());
+    }
 
   if (encompasses.empty ())
     {
index af2ea7137f0d9ab4dbddc9127d6dfc03f549e180..a6a2cb7e939423fcadfed6b07c50711106f806b2 100644 (file)
@@ -522,6 +522,7 @@ ADD_INTERFACE (Spanner,
                " point of the spanner.",
 
                /* properties */
+               "other-half "
                "normalized-endpoints "
                "minimum-length "
                "spanner-broken "
index 2e386e6fd18bba885a63cd6e3c1426fe29db2e29..ddcdcb4802e6bfe2890abfb0406bf24ac388357a 100644 (file)
@@ -87,6 +87,20 @@ void
 Volta_repeat_iterator::next_element (bool side_effect)
 {
   done_count_++;
+  if (done_count_ > 1
+      && done_count_ <= alt_count_
+      && !to_boolean (get_outlet ()->get_property ("slurOverRepeat")))
+    {
+      SCM ev_scm = scm_call_1 (ly_lily_module_constant ("make-music"),
+                               ly_symbol2scm ("BreakSlurEvent"));
+      Music *ev = unsmob_music (ev_scm);
+      ev->send_to_context (get_outlet ());
+
+      ev_scm = scm_call_1 (ly_lily_module_constant ("make-music"),
+                           ly_symbol2scm ("BreakPhrasingSlurEvent"));
+      ev = unsmob_music (ev_scm);
+      ev->send_to_context (get_outlet ());
+    }
 
   Sequential_iterator::next_element (side_effect);
 
index a25198f3e01dfaa06fd4cc76aee9e4d5a837d9e8..86a81c35989375ac19ea32fa71a0b342906b1257 100644 (file)
@@ -429,6 +429,26 @@ to the preceding note or rest as a post-event with @code{-}.")
               'footnote-text footnote)))
      #{ \tweak footnote-music #mus #item #}))
 
+free =
+#(define-music-function (parser location music) (ly:music?)
+  (_i "@var{event} should start a free spanner.")
+  (let ((name (ly:music-property music 'name)))
+    (cond
+      ((eq? name 'SlurEvent)
+       (make-music 'BreakSlurEvent
+                   'span-direction (ly:music-property music 'span-direction)
+                   'direction (ly:music-property music 'direction)
+                   'spanner-id (ly:music-property music 'spanner-id)))
+      ((eq? name 'PhrasingSlurEvent)
+       (make-music 'BreakPhrasingSlurEvent
+                   'span-direction (ly:music-property music 'span-direction)
+                   'direction (ly:music-property music 'direction)
+                   'spanner-id (ly:music-property music 'spanner-id)))
+      (else
+        (begin
+          (ly:music-warning music (_ "not a breakable event"))
+        music)))))
+
 grace =
 #(def-grace-function startGraceMusic stopGraceMusic
    (_i "Insert @var{music} as grace notes."))
index 44dcdb663352fb5a5974daf78ed7b0ee7dcb8bb4..ab564127344891f6000812a9002e28ddf0734fe6 100644 (file)
@@ -110,3 +110,7 @@ sostenutoOff = #(make-span-event 'SostenutoEvent STOP)
 newSpacingSection = #(make-event-chord (list (make-music 'SpacingSectionEvent)))
 
 breakDynamicSpan = #(make-music 'BreakDynamicSpanEvent)
+
+breakSlur = #(make-music 'BreakSlurEvent)
+
+breakPhrasingSlur = #(make-music 'BreakPhrasingSlurEvent)
index 98f52aaa09bf89463d39d27032b0e8374ce8a3cc..5bd8f76a45a0446e8b95dd7788c165de812fc288 100644 (file)
@@ -669,6 +669,7 @@ is not set")
 used by the @code{Script_engraver} for typesetting note-superscripts
 and subscripts.  See @file{scm/@/script.scm} for more information.")
      (slurMelismaBusy ,boolean? "Signal if a slur is present.")
+     (slurOverRepeat ,boolean? "A slur should be drawn over a repeat.")
      (stavesFound ,grob-list? "A list of all staff-symbols found.")
 
 
index c8ecbcf22709dd6a6750315d1d9f8e033ad0c570..15aaab53df0d40dc1c9ba610bf561839432c0cfa 100644 (file)
@@ -50,7 +50,8 @@
                                       trill-span-event tremolo-span-event
                                       tuplet-span-event))
     (span-dynamic-event . (decrescendo-event crescendo-event))
-    (break-span-event . (break-dynamic-span-event))
+    (break-span-event . (break-dynamic-span-event break-slur-event
+                                                  break-phrasing-slur-event ))
     (pedal-event . (sostenuto-event sustain-event una-corda-event))
     (rhythmic-event . (lyric-event melodic-event multi-measure-rest-event
                                   double-percent-event percent-event
index 4c5c404dc78a1c06b6e86ace9020b96f32aeb669..2371e5f9bd4da00e93e6c38fbfeffcdd5eda948f 100644 (file)
@@ -1138,6 +1138,8 @@ pure-from-neighbor-interface to determine various grob heights.")
      (note-columns ,ly:grob-array? "An array of @code{NoteColumn} grobs.")
      (note-head ,ly:grob? "A single note head.")
      (note-heads ,ly:grob-array? "An array of note head grobs.")
+     (other-half ,ly:grob? "The other half a broken spanner. For example,
+a slur that breaks at a repeat stores its other half here.")
      (pedal-text ,ly:grob? "A pointer to the text of a mixed-style piano
 pedal.")
      (potential-X-colliding-grobs ,ly:grob-array? "Grobs that can potentially
index 1090d332b49aec21b01fcf852f1f19fc158f7889..5b9e6f6174fde2a451bb8ff1aa447b5a70bbe76c 100644 (file)
@@ -118,6 +118,16 @@ Syntax for manual control: @code{c8-[ c c-] c8}")
        (types . (general-music post-event break-span-event break-dynamic-span-event event))
        ))
 
+    (BreakPhrasingSlurEvent
+     . ((description . "End a phrasing slur here.")
+       (types . (general-music break-span-event break-phrasing-slur-event event))
+       ))
+
+    (BreakSlurEvent
+     . ((description . "End a slur here.")
+       (types . (general-music break-span-event break-slur-event event))
+       ))
+
     (BendAfterEvent
      . ((description . "A drop/@/fall/@/doit jazz articulation.")
        (types . (general-music post-event bend-after-event event))))