2 figured-bass-engraver.cc -- implement Figured_bass_engraver
4 source file of the GNU LilyPond music typesetter
6 (c) 2005--2007 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;
147 No ASSIGN_EVENT_ONCE () ; otherwise we get warnings about
154 IMPLEMENT_TRANSLATOR_LISTENER (Figured_bass_engraver
, bass_figure
);
156 Figured_bass_engraver::listen_bass_figure (Stream_event
*ev
)
158 new_event_found_
= true;
159 Moment stop
= now_mom () + get_event_length (ev
, now_mom ());
160 stop_moment_
= max (stop_moment_
, stop
);
162 if (to_boolean (get_property ("useBassFigureExtenders")))
164 SCM fig
= ev
->get_property ("figure");
165 for (vsize i
= 0; i
< groups_
.size (); i
++)
167 if (!groups_
[i
].current_event_
168 && ly_is_equal (groups_
[i
].number_
, fig
))
170 groups_
[i
].current_event_
= ev
;
171 groups_
[i
].force_no_continuation_
172 = to_boolean (ev
->get_property ("no-continuation"));
173 continuation_
= true;
178 new_events_
.push_back (ev
);
182 Figured_bass_engraver::center_continuations (vector
<Spanner
*> const &consecutive_lines
)
184 if (consecutive_lines
.size () == 2)
186 vector
<Grob
*> left_figs
;
187 for (vsize j
= consecutive_lines
.size (); j
--;)
188 left_figs
.push_back (consecutive_lines
[j
]->get_bound (LEFT
));
190 SCM ga
= Grob_array::make_array ();
191 unsmob_grob_array (ga
)->set_array (left_figs
);
193 for (vsize j
= consecutive_lines
.size (); j
--;)
194 consecutive_lines
[j
]->set_object ("figures",
195 unsmob_grob_array (ga
)->smobbed_copy ());
200 Figured_bass_engraver::center_repeated_continuations ()
202 vector
<Spanner
*> consecutive_lines
;
203 for (vsize i
= 0; i
<= groups_
.size (); i
++)
205 if (i
< groups_
.size ()
206 && groups_
[i
].continuation_line_
207 && (consecutive_lines
.empty ()
208 || (consecutive_lines
[0]->get_bound (LEFT
)->get_column ()
209 == groups_
[i
].continuation_line_
->get_bound (LEFT
)->get_column ()
210 && consecutive_lines
[0]->get_bound (RIGHT
)->get_column ()
211 == groups_
[i
].continuation_line_
->get_bound (RIGHT
)->get_column ())))
212 consecutive_lines
.push_back (groups_
[i
].continuation_line_
);
215 center_continuations (consecutive_lines
);
216 consecutive_lines
.clear ();
222 Figured_bass_engraver::clear_spanners ()
229 announce_end_grob (alignment_
, SCM_EOL
);
233 if (to_boolean (get_property ("figuredBassCenterContinuations")))
234 center_repeated_continuations ();
236 for (vsize i
= 0; i
< groups_
.size (); i
++)
238 if (groups_
[i
].group_
)
240 announce_end_grob (groups_
[i
].group_
, SCM_EOL
);
241 groups_
[i
].group_
= 0;
244 if (groups_
[i
].continuation_line_
)
246 announce_end_grob (groups_
[i
].continuation_line_
, SCM_EOL
);
247 groups_
[i
].continuation_line_
= 0;
251 /* Check me, groups_.clear () ? */
255 Figured_bass_engraver::add_brackets ()
257 vector
<Grob
*> encompass
;
259 for (vsize i
= 0; i
< groups_
.size (); i
++)
261 if (!groups_
[i
].current_event_
)
264 if (to_boolean (groups_
[i
].current_event_
->get_property ("bracket-start")))
267 if (inside
&& groups_
[i
].figure_item_
)
268 encompass
.push_back (groups_
[i
].figure_item_
);
270 if (to_boolean (groups_
[i
].current_event_
->get_property ("bracket-stop")))
274 Item
* brack
= make_item ("BassFigureBracket", groups_
[i
].current_event_
->self_scm ());
275 for (vsize j
= 0; j
< encompass
.size (); j
++)
277 Pointer_group_interface::add_grob (brack
,
278 ly_symbol2scm ("elements"),
287 Figured_bass_engraver::process_music ()
289 if (alignment_
&& !to_boolean (get_property ("useBassFigureExtenders")))
300 && new_events_
.empty ())
307 if (!new_event_found_
)
310 new_event_found_
= false;
313 Don't need to sync alignments, if we're not using extenders.
315 bool use_extenders
= to_boolean (get_property ("useBassFigureExtenders"));
328 for (vsize i
= 0; i
< new_events_
.size (); i
++)
330 while (k
< groups_
.size ()
331 && groups_
[k
].current_event_
)
334 if (k
>= groups_
.size ())
337 groups_
.push_back (group
);
340 groups_
[k
].current_event_
= new_events_
[i
];
341 groups_
[k
].figure_item_
= 0;
345 for (vsize i
= 0; i
< groups_
.size (); i
++)
347 if (!groups_
[i
].is_continuation ())
349 groups_
[i
].number_
= SCM_BOOL_F
;
350 groups_
[i
].alteration_
= SCM_BOOL_F
;
356 vector
<int> junk_continuations
;
357 for (vsize i
= 0; i
< groups_
.size (); i
++)
359 Figure_group
&group
= groups_
[i
];
361 if (group
.is_continuation ())
363 if (!group
.continuation_line_
)
366 = make_spanner ("BassFigureContinuation", SCM_EOL
);
367 Item
* item
= group
.figure_item_
;
368 group
.continuation_line_
= line
;
369 line
->set_bound (LEFT
, item
);
372 Don't add as child. This will cache the wrong
373 (pre-break) stencil when callbacks are triggered.
375 line
->set_parent (group
.group_
, Y_AXIS
);
376 Pointer_group_interface::add_grob (line
, ly_symbol2scm ("figures"), item
);
378 group
.figure_item_
= 0;
381 else if (group
.continuation_line_
)
382 junk_continuations
.push_back (i
);
388 vector
<Spanner
*> consecutive
;
389 if (to_boolean (get_property ("figuredBassCenterContinuations")))
391 for (vsize i
= 0; i
<= junk_continuations
.size (); i
++)
393 if (i
< junk_continuations
.size ()
394 && (i
== 0 || junk_continuations
[i
-1] == junk_continuations
[i
] - 1))
395 consecutive
.push_back (groups_
[junk_continuations
[i
]].continuation_line_
);
398 center_continuations (consecutive
);
399 consecutive
.clear ();
400 if (i
< junk_continuations
.size ())
401 consecutive
.push_back (groups_
[junk_continuations
[i
]].continuation_line_
);
405 for (vsize i
= 0; i
< junk_continuations
.size (); i
++)
406 groups_
[junk_continuations
[i
]].continuation_line_
= 0;
414 Figured_bass_engraver::create_grobs ()
417 = dynamic_cast<Item
*> (unsmob_grob (get_property ("currentMusicalColumn")));
420 alignment_
= make_spanner ("BassFigureAlignment", SCM_EOL
);
421 alignment_
->set_bound (LEFT
, muscol
);
423 alignment_
->set_bound (RIGHT
, muscol
);
425 SCM proc
= get_property ("figuredBassFormatter");
426 for (vsize i
= 0; i
< groups_
.size (); i
++)
428 Figure_group
&group
= groups_
[i
];
430 if (group
.current_event_
)
433 = make_item ("BassFigure",
434 group
.current_event_
->self_scm ());
437 SCM fig
= group
.current_event_
->get_property ("figure");
440 group
.group_
= make_spanner ("BassFigureLine", SCM_EOL
);
441 group
.group_
->set_bound (LEFT
, muscol
);
442 Align_interface::add_element (alignment_
,
446 if (scm_memq (fig
, get_property ("implicitBassFigures")) != SCM_BOOL_F
)
448 item
->set_property ("transparent", SCM_BOOL_T
);
449 item
->set_property ("implicit", SCM_BOOL_T
);
453 group
.alteration_
= group
.current_event_
->get_property ("alteration");
455 SCM text
= group
.current_event_
->get_property ("text");
456 if (!Text_interface::is_markup (text
)
457 && ly_is_procedure (proc
))
459 text
= scm_call_3 (proc
, fig
, group
.current_event_
->self_scm (),
460 context ()->self_scm ());
463 item
->set_property ("text", text
);
465 Axis_group_interface::add_element (group
.group_
, item
);
466 group
.figure_item_
= item
;
469 if (group
.continuation_line_
)
472 UGH should connect to the bass staff, and get the note heads.
474 group
.figure_item_
->set_property ("transparent", SCM_BOOL_T
);
475 group
.continuation_line_
->set_bound (RIGHT
, group
.figure_item_
);
478 if (groups_
[i
].group_
)
479 groups_
[i
].group_
->set_bound (RIGHT
, muscol
);
485 ADD_TRANSLATOR (Figured_bass_engraver
,
487 "Make figured bass numbers.",
491 "BassFigureAlignment "
493 "BassFigureContinuation "
497 "figuredBassAlterationDirection "
498 "figuredBassCenterContinuations "
499 "figuredBassFormatter "
500 "implicitBassFigures "
501 "useBassFigureExtenders "
502 "ignoreFiguredBassRest ",