2 figured-bass-engraver.cc -- implement Figured_bass_engraver
4 source file of the GNU LilyPond music typesetter
6 (c) 2005--2006 Han-Wen Nienhuys <hanwen@xs4all.nl>
10 #include "engraver.hh"
12 #include "align-interface.hh"
13 #include "axis-group-interface.hh"
15 #include "grob-array.hh"
17 #include "pointer-group-interface.hh"
19 #include "stream-event.hh"
20 #include "text-interface.hh"
22 #include "translator.icc"
27 Spanner *continuation_line_;
33 Stream_event *current_event_;
34 bool force_no_continuation_;
39 force_no_continuation_ = false;
40 continuation_line_ = 0;
42 alteration_ = SCM_EOL;
46 bool is_continuation () const
50 && !force_no_continuation_
51 && ly_is_equal (number_,
52 current_event_->get_property ("figure"))
53 && ly_is_equal (alteration_,
54 current_event_->get_property ("alteration"));
58 struct Figured_bass_engraver : public Engraver
60 TRANSLATOR_DECLARATIONS(Figured_bass_engraver);
61 void clear_spanners();
65 void center_continuations (vector<Spanner*> const &consecutive_lines);
66 void center_repeated_continuations ();
68 vector<Figure_group> groups_;
70 vector<Stream_event *> new_events_;
72 bool new_event_found_;
75 Stream_event *rest_event_;
77 DECLARE_TRANSLATOR_LISTENER (rest);
78 DECLARE_TRANSLATOR_LISTENER (bass_figure);
80 virtual void derived_mark () const;
82 void start_translation_timestep ();
83 void stop_translation_timestep ();
84 void process_music ();
88 Figured_bass_engraver::derived_mark () const
90 for (vsize i = 0; i < groups_.size (); i++)
92 scm_gc_mark (groups_[i].number_);
93 scm_gc_mark (groups_[i].alteration_);
98 Figured_bass_engraver::stop_translation_timestep ()
101 || now_mom ().main_part_ < stop_moment_.main_part_
102 || now_mom ().grace_part_ < Rational (0))
106 for (vsize i = 0; !found && i < groups_.size (); i++)
107 found = found || groups_[i].current_event_;
113 Figured_bass_engraver::Figured_bass_engraver ()
116 continuation_ = false;
118 new_event_found_ = false;
122 Figured_bass_engraver::start_translation_timestep ()
124 if (now_mom ().main_part_ < stop_moment_.main_part_
125 || now_mom ().grace_part_ < Rational (0))
129 new_events_.clear ();
130 for (vsize i = 0; i < groups_.size (); i++)
131 groups_[i].current_event_ = 0;
133 continuation_ = false;
138 IMPLEMENT_TRANSLATOR_LISTENER (Figured_bass_engraver, rest);
140 Figured_bass_engraver::listen_rest (Stream_event *ev)
142 if (to_boolean (get_property ("ignoreFiguredBassRest")))
144 new_event_found_ = true;
145 ASSIGN_EVENT_ONCE (rest_event_, ev);
149 IMPLEMENT_TRANSLATOR_LISTENER (Figured_bass_engraver, bass_figure);
151 Figured_bass_engraver::listen_bass_figure (Stream_event *ev)
153 new_event_found_ = true;
154 Moment stop = now_mom () + get_event_length (ev);
155 stop_moment_ = max (stop_moment_, stop);
157 if (to_boolean (get_property ("useBassFigureExtenders")))
159 SCM fig = ev->get_property ("figure");
160 for (vsize i = 0; i < groups_.size (); i++)
162 if (!groups_[i].current_event_
163 && ly_is_equal (groups_[i].number_, fig))
165 groups_[i].current_event_ = ev;
166 groups_[i].force_no_continuation_
167 = to_boolean (ev->get_property ("no-continuation"));
168 continuation_ = true;
173 new_events_.push_back (ev);
177 Figured_bass_engraver::center_continuations (vector<Spanner*> const &consecutive_lines)
179 if (consecutive_lines.size () == 2)
181 vector<Grob*> left_figs;
182 for (vsize j = consecutive_lines.size(); j--;)
183 left_figs.push_back (consecutive_lines[j]->get_bound (LEFT));
185 SCM ga = Grob_array::make_array ();
186 unsmob_grob_array (ga)->set_array (left_figs);
188 for (vsize j = consecutive_lines.size(); j--;)
189 consecutive_lines[j]->set_object ("figures",
190 unsmob_grob_array (ga)->smobbed_copy ());
195 Figured_bass_engraver::center_repeated_continuations ()
197 vector<Spanner*> consecutive_lines;
198 for (vsize i = 0; i <= groups_.size(); i++)
200 if (i < groups_.size ()
201 && groups_[i].continuation_line_
202 && (consecutive_lines.empty ()
203 || (consecutive_lines[0]->get_bound(LEFT)->get_column ()
204 == groups_[i].continuation_line_->get_bound (LEFT)->get_column ()
205 && consecutive_lines[0]->get_bound(RIGHT)->get_column ()
206 == groups_[i].continuation_line_->get_bound (RIGHT)->get_column ())))
207 consecutive_lines.push_back (groups_[i].continuation_line_);
210 center_continuations (consecutive_lines);
211 consecutive_lines.clear ();
217 Figured_bass_engraver::clear_spanners ()
224 announce_end_grob (alignment_, SCM_EOL);
228 if (to_boolean (get_property ("figuredBassCenterContinuations")))
229 center_repeated_continuations();
231 for (vsize i = 0; i < groups_.size (); i++)
233 if (groups_[i].group_)
235 announce_end_grob (groups_[i].group_ , SCM_EOL);
236 groups_[i].group_ = 0;
239 if (groups_[i].continuation_line_)
241 announce_end_grob (groups_[i].continuation_line_ , SCM_EOL);
242 groups_[i].continuation_line_ = 0;
246 /* Check me, groups_.clear () ? */
250 Figured_bass_engraver::add_brackets ()
252 vector<Grob*> encompass;
254 for (vsize i = 0; i < groups_.size (); i ++)
256 if (!groups_[i].current_event_)
259 if (to_boolean (groups_[i].current_event_->get_property ("bracket-start")))
262 if (inside && groups_[i].figure_item_)
263 encompass.push_back (groups_[i].figure_item_);
265 if (to_boolean (groups_[i].current_event_->get_property ("bracket-stop")))
269 Item * brack = make_item ("BassFigureBracket", groups_[i].current_event_->self_scm ());
270 for (vsize j = 0; j < encompass.size (); j++)
272 Pointer_group_interface::add_grob (brack,
273 ly_symbol2scm ("elements"),
282 Figured_bass_engraver::process_music ()
284 if (!to_boolean (get_property ("useBassFigureExtenders")))
295 && new_events_.empty ())
302 if (!new_event_found_)
305 new_event_found_ = false;
308 Don't need to sync alignments, if we're not using extenders.
310 bool use_extenders = to_boolean (get_property ("useBassFigureExtenders"));
323 for (vsize i = 0; i < new_events_.size (); i++)
325 while (k < groups_.size ()
326 && groups_[k].current_event_)
329 if (k >= groups_.size ())
332 groups_.push_back (group);
335 groups_[k].current_event_ = new_events_[i];
336 groups_[k].figure_item_ = 0;
340 for (vsize i = 0; i < groups_.size (); i++)
342 if (!groups_[i].is_continuation ())
344 groups_[i].number_ = SCM_BOOL_F;
345 groups_[i].alteration_ = SCM_BOOL_F;
351 vector<int> junk_continuations;
352 for (vsize i = 0; i < groups_.size(); i++)
354 Figure_group &group = groups_[i];
356 if (group.is_continuation ())
358 if (!group.continuation_line_)
360 Spanner * line = make_spanner ("BassFigureContinuation", SCM_EOL);
361 Item * item = group.figure_item_;
362 group.continuation_line_ = line;
363 line->set_bound (LEFT, item);
366 Don't add as child. This will cache the wrong
367 (pre-break) stencil when callbacks are triggered.
369 line->set_parent (group.group_, Y_AXIS);
370 Pointer_group_interface::add_grob (line, ly_symbol2scm ("figures"), item);
372 group.figure_item_ = 0;
375 else if (group.continuation_line_)
376 junk_continuations.push_back (i);
382 vector<Spanner*> consecutive;
383 if (to_boolean (get_property ("figuredBassCenterContinuations")))
385 for (vsize i = 0; i <= junk_continuations.size (); i++)
387 if (i < junk_continuations.size()
388 && (i == 0 || junk_continuations[i-1] == junk_continuations[i] - 1))
389 consecutive.push_back (groups_[junk_continuations[i]].continuation_line_);
392 center_continuations (consecutive);
393 consecutive.clear ();
394 if (i < junk_continuations.size ())
395 consecutive.push_back (groups_[junk_continuations[i]].continuation_line_);
399 for (vsize i = 0; i < junk_continuations.size (); i++)
400 groups_[junk_continuations[i]].continuation_line_ = 0;
408 Figured_bass_engraver::create_grobs ()
410 Grob *muscol = dynamic_cast<Item*> (unsmob_grob (get_property ("currentMusicalColumn")));
413 alignment_ = make_spanner ("BassFigureAlignment", SCM_EOL);
414 alignment_->set_bound (LEFT, muscol);
416 alignment_->set_bound (RIGHT, muscol);
418 SCM proc = get_property ("figuredBassFormatter");
419 for (vsize i = 0; i < groups_.size(); i++)
421 Figure_group &group = groups_[i];
423 if (group.current_event_)
426 = make_item ("BassFigure",
427 group.current_event_->self_scm ());
430 SCM fig = group.current_event_->get_property ("figure");
433 group.group_ = make_spanner ("BassFigureLine", SCM_EOL);
434 group.group_->set_bound (LEFT, muscol);
435 Align_interface::add_element (alignment_,
439 if (scm_memq (fig, get_property ("implicitBassFigures")) != SCM_BOOL_F)
441 item->set_property ("transparent", SCM_BOOL_T);
442 item->set_property ("implicit", SCM_BOOL_T);
446 group.alteration_ = group.current_event_->get_property ("alteration");
448 SCM text = group.current_event_->get_property ("text");
449 if (!Text_interface::is_markup (text)
450 && ly_is_procedure (proc))
452 text = scm_call_3 (proc, fig, group.current_event_->self_scm (),
453 context ()->self_scm ());
456 item->set_property ("text", text);
458 Axis_group_interface::add_element (group.group_, item);
459 group.figure_item_ = item;
462 if (group.continuation_line_)
465 UGH should connect to the bass staff, and get the note heads.
467 group.figure_item_->set_property ("transparent", SCM_BOOL_T);
468 group.continuation_line_->set_bound (RIGHT, group.figure_item_);
471 if (groups_[i].group_)
472 groups_[i].group_->set_bound (RIGHT, muscol);
478 ADD_TRANSLATOR (Figured_bass_engraver,
481 "Make figured bass numbers.",
484 "BassFigureAlignment "
486 "BassFigureContinuation "
491 "figuredBassAlterationDirection "
492 "figuredBassCenterContinuations "
493 "figuredBassFormatter "
494 "implicitBassFigures "
495 "useBassFigureExtenders "
496 "ignoreFiguredBassRest "