]> git.donarmstrong.com Git - lilypond.git/blobdiff - lily/figured-bass-engraver.cc
Run `make grand-replace'.
[lilypond.git] / lily / figured-bass-engraver.cc
index bed934002ac18d5cb235b4b4f747bf53ea8f41af..8a06ea3a1b8471ef5fcfee3ab3229d25e1f3c3ee 100644 (file)
-/*   
-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 <hanwen@cs.uu.nl>
+  (c) 2005--2008 Han-Wen Nienhuys <hanwen@xs4all.nl>
 
- */
+*/
 
 #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<Music> figures_;
-  Music * rest_req_;
+  void clear_spanners ();
+  void add_brackets ();
+  void create_grobs ();
 
-  Grob * figure_;
+  void center_continuations (vector<Spanner*> const &consecutive_lines);
+  void center_repeated_continuations ();
+protected:
+  vector<Figure_group> groups_;
+  Spanner *alignment_;
+  vector<Stream_event *> 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<Spanner*> const &consecutive_lines)
 {
-  if (m->is_mus_type ("bass-figure-event"))
+  if (consecutive_lines.size () == 2)
     {
-      figures_.push (m);
-      return true;
+      vector<Grob*> 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<Spanner*> 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<Grob*> 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<int> 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<Spanner*> 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<Item*> (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 */
+               ""
+               );