--- /dev/null
+\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' \) |
+}
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_;
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 ();
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 ();
{
protected:
DECLARE_TRANSLATOR_LISTENER (phrasing_slur);
+ DECLARE_TRANSLATOR_LISTENER (break_phrasing_slur);
DECLARE_ACKNOWLEDGER (slur);
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);
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)
{
protected:
DECLARE_TRANSLATOR_LISTENER (slur);
+ DECLARE_TRANSLATOR_LISTENER (break_slur);
public:
TRANSLATOR_DECLARATIONS (Slur_engraver);
Slur_engraver::Slur_engraver () :
Slur_proto_engraver ("doubleSlurs", "Slur", "slur", "slur-event")
{
+ break_slur_ = 0;
}
IMPLEMENT_TRANSLATOR_LISTENER (Slur_engraver, slur);
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)
{
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)
{
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
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"), "");
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 ());
}
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);
}
end_slurs_.clear ();
start_events_.clear ();
stop_events_.clear ();
+ break_slur_ = 0;
}
// no ADD_ACKNOWLEDGER / ADD_TRANSLATOR macro calls
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 ())
{
" point of the spanner.",
/* properties */
+ "other-half "
"normalized-endpoints "
"minimum-length "
"spanner-broken "
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);
'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."))
newSpacingSection = #(make-event-chord (list (make-music 'SpacingSectionEvent)))
breakDynamicSpan = #(make-music 'BreakDynamicSpanEvent)
+
+breakSlur = #(make-music 'BreakSlurEvent)
+
+breakPhrasingSlur = #(make-music 'BreakPhrasingSlurEvent)
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.")
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
(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
(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))))