X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=lily%2Ffigured-bass-engraver.cc;h=8a06ea3a1b8471ef5fcfee3ab3229d25e1f3c3ee;hb=5b4b0d6e9a197e8f9eb085b7c2ad78b8be3e5cfc;hp=bed934002ac18d5cb235b4b4f747bf53ea8f41af;hpb=e7020dbb36a9e9c10bda48e5197213e8a3bacef6;p=lilypond.git diff --git a/lily/figured-bass-engraver.cc b/lily/figured-bass-engraver.cc index bed934002a..8a06ea3a1b 100644 --- a/lily/figured-bass-engraver.cc +++ b/lily/figured-bass-engraver.cc @@ -1,102 +1,527 @@ -/* -figured-bass-engraver.cc -- implement Figured_bass_engraver +/* + figured-bass-engraver.cc -- implement Figured_bass_engraver -source file of the GNU LilyPond music typesetter + source file of the GNU LilyPond music typesetter -(c) 2002--2004 Han-Wen Nienhuys + (c) 2005--2008 Han-Wen Nienhuys - */ +*/ #include "engraver.hh" -#include "text-item.hh" -#include "event.hh" -#include "item.hh" + +#include "align-interface.hh" +#include "axis-group-interface.hh" #include "context.hh" +#include "grob-array.hh" +#include "item.hh" +#include "pointer-group-interface.hh" +#include "spanner.hh" +#include "stream-event.hh" +#include "text-interface.hh" + +#include "translator.icc" + +struct Figure_group +{ + Spanner *group_; + Spanner *continuation_line_; + + SCM number_; + SCM alteration_; + SCM augmented_; + SCM diminished_; + SCM augmented_slash_; + + Item *figure_item_; + Stream_event *current_event_; + bool force_no_continuation_; + + Figure_group () + { + figure_item_ = 0; + force_no_continuation_ = false; + continuation_line_ = 0; + number_ = SCM_EOL; + alteration_ = SCM_EOL; + augmented_ = SCM_EOL; + diminished_ = SCM_EOL; + augmented_slash_ = SCM_EOL; + group_ = 0; + current_event_ = 0; + } + bool is_continuation () const + { + return + current_event_ + && !force_no_continuation_ + && ly_is_equal (number_, + current_event_->get_property ("figure")) + && ly_is_equal (alteration_, + current_event_->get_property ("alteration")) + && ly_is_equal (augmented_, + current_event_->get_property ("augmented")) + && ly_is_equal (diminished_, + current_event_->get_property ("diminished")) + && ly_is_equal (augmented_slash_, + current_event_->get_property ("augmented-slash")); + } +}; -class Figured_bass_engraver : public Engraver +struct Figured_bass_engraver : public Engraver { TRANSLATOR_DECLARATIONS (Figured_bass_engraver); -protected: - Link_array figures_; - Music * rest_req_; + void clear_spanners (); + void add_brackets (); + void create_grobs (); - Grob * figure_; + void center_continuations (vector const &consecutive_lines); + void center_repeated_continuations (); +protected: + vector groups_; + Spanner *alignment_; + vector new_events_; + bool continuation_; + bool new_event_found_; - virtual bool try_music (Music*); - virtual void stop_translation_timestep (); - virtual void process_music (); + Moment stop_moment_; + Stream_event *rest_event_; + + DECLARE_TRANSLATOR_LISTENER (rest); + DECLARE_TRANSLATOR_LISTENER (bass_figure); + + virtual void derived_mark () const; + + void start_translation_timestep (); + void stop_translation_timestep (); + void process_music (); }; +void +Figured_bass_engraver::derived_mark () const +{ + for (vsize i = 0; i < groups_.size (); i++) + { + scm_gc_mark (groups_[i].number_); + scm_gc_mark (groups_[i].alteration_); + scm_gc_mark (groups_[i].augmented_); + scm_gc_mark (groups_[i].diminished_); + scm_gc_mark (groups_[i].augmented_slash_); + } +} + +void +Figured_bass_engraver::stop_translation_timestep () +{ + if (groups_.empty () + || now_mom ().main_part_ < stop_moment_.main_part_ + || now_mom ().grace_part_ < Rational (0)) + return ; + + bool found = false; + for (vsize i = 0; !found && i < groups_.size (); i++) + found = found || groups_[i].current_event_; + + if (!found) + clear_spanners (); +} Figured_bass_engraver::Figured_bass_engraver () { - figure_ = 0; - rest_req_ = 0; + alignment_ = 0; + continuation_ = false; + rest_event_ = 0; + new_event_found_ = false; } void -Figured_bass_engraver::stop_translation_timestep () +Figured_bass_engraver::start_translation_timestep () +{ + if (now_mom ().main_part_ < stop_moment_.main_part_ + || now_mom ().grace_part_ < Rational (0)) + return ; + + rest_event_ = 0; + new_events_.clear (); + for (vsize i = 0; i < groups_.size (); i++) + groups_[i].current_event_ = 0; + + continuation_ = false; + + +} + +IMPLEMENT_TRANSLATOR_LISTENER (Figured_bass_engraver, rest); +void +Figured_bass_engraver::listen_rest (Stream_event *ev) { - if (figure_) + if (to_boolean (get_property ("ignoreFiguredBassRest"))) { - typeset_grob (figure_); - figure_ = 0; + new_event_found_ = true; + + /* + No ASSIGN_EVENT_ONCE () ; otherwise we get warnings about + polyphonic rests. + */ + rest_event_ = ev; } +} + +IMPLEMENT_TRANSLATOR_LISTENER (Figured_bass_engraver, bass_figure); +void +Figured_bass_engraver::listen_bass_figure (Stream_event *ev) +{ + new_event_found_ = true; + Moment stop = now_mom () + get_event_length (ev, now_mom ()); + stop_moment_ = max (stop_moment_, stop); - figures_.clear (); - rest_req_ = 0; + if (to_boolean (get_property ("useBassFigureExtenders"))) + { + SCM fig = ev->get_property ("figure"); + for (vsize i = 0; i < groups_.size (); i++) + { + if (!groups_[i].current_event_ + && ly_is_equal (groups_[i].number_, fig)) + { + groups_[i].current_event_ = ev; + groups_[i].force_no_continuation_ + = to_boolean (ev->get_property ("no-continuation")); + continuation_ = true; + return; + } + } + } + new_events_.push_back (ev); } -bool -Figured_bass_engraver::try_music (Music*m) +void +Figured_bass_engraver::center_continuations (vector const &consecutive_lines) { - if (m->is_mus_type ("bass-figure-event")) + if (consecutive_lines.size () == 2) { - figures_.push (m); - return true; + vector left_figs; + for (vsize j = consecutive_lines.size (); j--;) + left_figs.push_back (consecutive_lines[j]->get_bound (LEFT)); + + SCM ga = Grob_array::make_array (); + unsmob_grob_array (ga)->set_array (left_figs); + + for (vsize j = consecutive_lines.size (); j--;) + consecutive_lines[j]->set_object ("figures", + unsmob_grob_array (ga)->smobbed_copy ()); } - else if (m->is_mus_type ("rest-event")) +} + +void +Figured_bass_engraver::center_repeated_continuations () +{ + vector consecutive_lines; + for (vsize i = 0; i <= groups_.size (); i++) { - rest_req_ = m; - return true; + if (i < groups_.size () + && groups_[i].continuation_line_ + && (consecutive_lines.empty () + || (consecutive_lines[0]->get_bound (LEFT)->get_column () + == groups_[i].continuation_line_->get_bound (LEFT)->get_column () + && consecutive_lines[0]->get_bound (RIGHT)->get_column () + == groups_[i].continuation_line_->get_bound (RIGHT)->get_column ()))) + consecutive_lines.push_back (groups_[i].continuation_line_); + else + { + center_continuations (consecutive_lines); + consecutive_lines.clear (); + } } - return false; } void -Figured_bass_engraver::process_music () +Figured_bass_engraver::clear_spanners () { - if (rest_req_) + if (!alignment_) + return; + + if (alignment_) { - figure_ = make_item ("BassFigure"); - announce_grob (figure_, rest_req_->self_scm ()); // todo - figure_->set_property ("text" , scm_makfrom0str ("-")); + announce_end_grob (alignment_, SCM_EOL); + alignment_ = 0; + } + + if (to_boolean (get_property ("figuredBassCenterContinuations"))) + center_repeated_continuations (); + + for (vsize i = 0; i < groups_.size (); i++) + { + if (groups_[i].group_) + { + announce_end_grob (groups_[i].group_ , SCM_EOL); + groups_[i].group_ = 0; + } + + if (groups_[i].continuation_line_) + { + announce_end_grob (groups_[i].continuation_line_ , SCM_EOL); + groups_[i].continuation_line_ = 0; + } } - else if (figures_.size ()) + + /* Check me, groups_.clear () ? */ +} + +void +Figured_bass_engraver::add_brackets () +{ + vector encompass; + bool inside = false; + for (vsize i = 0; i < groups_.size (); i ++) { - SCM proc = get_property ("bassFigureFormatFunction"); - if (ly_c_procedure_p (proc)) + if (!groups_[i].current_event_) + continue; + + if (to_boolean (groups_[i].current_event_->get_property ("bracket-start"))) + inside = true; + + if (inside && groups_[i].figure_item_) + encompass.push_back (groups_[i].figure_item_); + + if (to_boolean (groups_[i].current_event_->get_property ("bracket-stop"))) { - SCM l = SCM_EOL; - SCM * t = &l; - for (int i = 0; i < figures_.size (); i++) + inside = false; + + Item * brack = make_item ("BassFigureBracket", groups_[i].current_event_->self_scm ()); + for (vsize j = 0; j < encompass.size (); j++) { - *t = scm_cons (figures_[i]->self_scm (), SCM_EOL); - t = SCM_CDRLOC (*t); + Pointer_group_interface::add_grob (brack, + ly_symbol2scm ("elements"), + encompass[j]); } - figure_ = make_item ("BassFigure"); - scm_call_3 (proc, l, get_parent_context ()->self_scm (), - figure_->self_scm ()); - announce_grob (figure_, figures_[0]->self_scm ()); // todo + encompass.clear (); } } } +void +Figured_bass_engraver::process_music () +{ + if (alignment_ && !to_boolean (get_property ("useBassFigureExtenders"))) + clear_spanners (); + + if (rest_event_) + { + clear_spanners (); + groups_.clear (); + return; + } + + if (!continuation_ + && new_events_.empty ()) + { + clear_spanners (); + groups_.clear (); + return; + } + + if (!new_event_found_) + return; + + new_event_found_ = false; + + /* + Don't need to sync alignments, if we're not using extenders. + */ + bool use_extenders = to_boolean (get_property ("useBassFigureExtenders")); + if (!use_extenders) + { + clear_spanners (); + } + + if (!continuation_) + { + clear_spanners (); + groups_.clear (); + } + + vsize k = 0; + for (vsize i = 0; i < new_events_.size (); i++) + { + while (k < groups_.size () + && groups_[k].current_event_) + k++; + + if (k >= groups_.size ()) + { + Figure_group group; + groups_.push_back (group); + } + + groups_[k].current_event_ = new_events_[i]; + groups_[k].figure_item_ = 0; + k++; + } + + for (vsize i = 0; i < groups_.size (); i++) + { + if (!groups_[i].is_continuation ()) + { + groups_[i].number_ = SCM_BOOL_F; + groups_[i].alteration_ = SCM_BOOL_F; + groups_[i].augmented_ = SCM_BOOL_F; + groups_[i].diminished_ = SCM_BOOL_F; + groups_[i].augmented_slash_ = SCM_BOOL_F; + } + } + + if (use_extenders) + { + vector junk_continuations; + for (vsize i = 0; i < groups_.size (); i++) + { + Figure_group &group = groups_[i]; + + if (group.is_continuation ()) + { + if (!group.continuation_line_) + { + Spanner * line + = make_spanner ("BassFigureContinuation", SCM_EOL); + Item * item = group.figure_item_; + group.continuation_line_ = line; + line->set_bound (LEFT, item); + + /* + Don't add as child. This will cache the wrong + (pre-break) stencil when callbacks are triggered. + */ + line->set_parent (group.group_, Y_AXIS); + Pointer_group_interface::add_grob (line, ly_symbol2scm ("figures"), item); + + group.figure_item_ = 0; + } + } + else if (group.continuation_line_) + junk_continuations.push_back (i); + } + + /* + Ugh, repeated code. + */ + vector consecutive; + if (to_boolean (get_property ("figuredBassCenterContinuations"))) + { + for (vsize i = 0; i <= junk_continuations.size (); i++) + { + if (i < junk_continuations.size () + && (i == 0 || junk_continuations[i-1] == junk_continuations[i] - 1)) + consecutive.push_back (groups_[junk_continuations[i]].continuation_line_); + else + { + center_continuations (consecutive); + consecutive.clear (); + if (i < junk_continuations.size ()) + consecutive.push_back (groups_[junk_continuations[i]].continuation_line_); + } + } + } + for (vsize i = 0; i < junk_continuations.size (); i++) + groups_[junk_continuations[i]].continuation_line_ = 0; + } -ENTER_DESCRIPTION (Figured_bass_engraver, -/* descr */ "Make figured bass numbers.", -/* creats*/ "BassFigure", -/* accepts */ "rest-event bass-figure-event", -/* acks */ "", -/* reads */ "bassFigureFormatFunction", -/* write */ ""); + create_grobs (); + add_brackets (); +} + +void +Figured_bass_engraver::create_grobs () +{ + Grob *muscol + = dynamic_cast (unsmob_grob (get_property ("currentMusicalColumn"))); + if (!alignment_) + { + alignment_ = make_spanner ("BassFigureAlignment", SCM_EOL); + alignment_->set_bound (LEFT, muscol); + } + alignment_->set_bound (RIGHT, muscol); + + SCM proc = get_property ("figuredBassFormatter"); + for (vsize i = 0; i < groups_.size (); i++) + { + Figure_group &group = groups_[i]; + + if (group.current_event_) + { + Item *item + = make_item ("BassFigure", + group.current_event_->self_scm ()); + + + SCM fig = group.current_event_->get_property ("figure"); + if (!group.group_) + { + group.group_ = make_spanner ("BassFigureLine", SCM_EOL); + group.group_->set_bound (LEFT, muscol); + Align_interface::add_element (alignment_, + group.group_); + } + + if (scm_memq (fig, get_property ("implicitBassFigures")) != SCM_BOOL_F) + { + item->set_property ("transparent", SCM_BOOL_T); + item->set_property ("implicit", SCM_BOOL_T); + } + + group.number_ = fig; + group.alteration_ = group.current_event_->get_property ("alteration"); + group.augmented_ = group.current_event_->get_property ("augmented"); + group.diminished_ = group.current_event_->get_property ("diminished"); + group.augmented_slash_ = group.current_event_->get_property ("augmented-slash"); + + SCM text = group.current_event_->get_property ("text"); + if (!Text_interface::is_markup (text) + && ly_is_procedure (proc)) + { + text = scm_call_3 (proc, fig, group.current_event_->self_scm (), + context ()->self_scm ()); + } + + item->set_property ("text", text); + + Axis_group_interface::add_element (group.group_, item); + group.figure_item_ = item; + } + + if (group.continuation_line_) + { + /* + UGH should connect to the bass staff, and get the note heads. + */ + group.figure_item_->set_property ("transparent", SCM_BOOL_T); + group.continuation_line_->set_bound (RIGHT, group.figure_item_); + } + + if (groups_[i].group_) + groups_[i].group_->set_bound (RIGHT, muscol); + + } + +} + +ADD_TRANSLATOR (Figured_bass_engraver, + /* doc */ + "Make figured bass numbers.", + + /* create */ + "BassFigure " + "BassFigureAlignment " + "BassFigureBracket " + "BassFigureContinuation " + "BassFigureLine ", + + /* read */ + "figuredBassAlterationDirection " + "figuredBassCenterContinuations " + "figuredBassFormatter " + "implicitBassFigures " + "useBassFigureExtenders " + "ignoreFiguredBassRest ", + + /* write */ + "" + );