2 figured-bass-engraver.cc -- implement Figured_bass_engraver
4 source file of the GNU LilyPond music typesetter
6 (c) 2005--2009 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_
;
37 Stream_event
*current_event_
;
38 bool force_no_continuation_
;
43 force_no_continuation_
= false;
44 continuation_line_
= 0;
46 alteration_
= SCM_EOL
;
48 diminished_
= SCM_EOL
;
49 augmented_slash_
= SCM_EOL
;
54 bool is_continuation () const
58 && !force_no_continuation_
59 && ly_is_equal (number_
,
60 current_event_
->get_property ("figure"))
61 && ly_is_equal (alteration_
,
62 current_event_
->get_property ("alteration"))
63 && ly_is_equal (augmented_
,
64 current_event_
->get_property ("augmented"))
65 && ly_is_equal (diminished_
,
66 current_event_
->get_property ("diminished"))
67 && ly_is_equal (augmented_slash_
,
68 current_event_
->get_property ("augmented-slash"))
69 && ly_is_equal (text_
,
70 current_event_
->get_property ("text"));
74 struct Figured_bass_engraver
: public Engraver
76 TRANSLATOR_DECLARATIONS (Figured_bass_engraver
);
77 void clear_spanners ();
81 void center_continuations (vector
<Spanner
*> const &consecutive_lines
);
82 void center_repeated_continuations ();
84 vector
<Figure_group
> groups_
;
86 vector
<Stream_event
*> new_events_
;
88 bool new_event_found_
;
91 Stream_event
*rest_event_
;
93 DECLARE_TRANSLATOR_LISTENER (rest
);
94 DECLARE_TRANSLATOR_LISTENER (bass_figure
);
96 virtual void derived_mark () const;
98 void start_translation_timestep ();
99 void stop_translation_timestep ();
100 void process_music ();
104 Figured_bass_engraver::derived_mark () const
106 for (vsize i
= 0; i
< groups_
.size (); i
++)
108 scm_gc_mark (groups_
[i
].number_
);
109 scm_gc_mark (groups_
[i
].alteration_
);
110 scm_gc_mark (groups_
[i
].augmented_
);
111 scm_gc_mark (groups_
[i
].diminished_
);
112 scm_gc_mark (groups_
[i
].augmented_slash_
);
113 scm_gc_mark (groups_
[i
].text_
);
118 Figured_bass_engraver::stop_translation_timestep ()
121 || now_mom ().main_part_
< stop_moment_
.main_part_
122 || now_mom ().grace_part_
< Rational (0))
126 for (vsize i
= 0; !found
&& i
< groups_
.size (); i
++)
127 found
= found
|| groups_
[i
].current_event_
;
133 Figured_bass_engraver::Figured_bass_engraver ()
136 continuation_
= false;
138 new_event_found_
= false;
142 Figured_bass_engraver::start_translation_timestep ()
144 if (now_mom ().main_part_
< stop_moment_
.main_part_
145 || now_mom ().grace_part_
< Rational (0))
149 new_events_
.clear ();
150 for (vsize i
= 0; i
< groups_
.size (); i
++)
151 groups_
[i
].current_event_
= 0;
153 continuation_
= false;
158 IMPLEMENT_TRANSLATOR_LISTENER (Figured_bass_engraver
, rest
);
160 Figured_bass_engraver::listen_rest (Stream_event
*ev
)
162 if (to_boolean (get_property ("ignoreFiguredBassRest")))
164 new_event_found_
= true;
167 No ASSIGN_EVENT_ONCE () ; otherwise we get warnings about
174 IMPLEMENT_TRANSLATOR_LISTENER (Figured_bass_engraver
, bass_figure
);
176 Figured_bass_engraver::listen_bass_figure (Stream_event
*ev
)
178 new_event_found_
= true;
179 Moment stop
= now_mom () + get_event_length (ev
, now_mom ());
180 stop_moment_
= max (stop_moment_
, stop
);
182 if (to_boolean (get_property ("useBassFigureExtenders")))
184 SCM fig
= ev
->get_property ("figure");
185 SCM txt
= ev
->get_property ("text");
186 for (vsize i
= 0; i
< groups_
.size (); i
++)
188 if (!groups_
[i
].current_event_
189 && ly_is_equal (groups_
[i
].number_
, fig
)
190 && ly_is_equal (groups_
[i
].text_
, txt
))
192 groups_
[i
].current_event_
= ev
;
193 groups_
[i
].force_no_continuation_
194 = to_boolean (ev
->get_property ("no-continuation"));
195 continuation_
= true;
200 new_events_
.push_back (ev
);
204 Figured_bass_engraver::center_continuations (vector
<Spanner
*> const &consecutive_lines
)
206 if (consecutive_lines
.size () == 2)
208 vector
<Grob
*> left_figs
;
209 for (vsize j
= consecutive_lines
.size (); j
--;)
210 left_figs
.push_back (consecutive_lines
[j
]->get_bound (LEFT
));
212 SCM ga
= Grob_array::make_array ();
213 unsmob_grob_array (ga
)->set_array (left_figs
);
215 for (vsize j
= consecutive_lines
.size (); j
--;)
216 consecutive_lines
[j
]->set_object ("figures",
217 unsmob_grob_array (ga
)->smobbed_copy ());
222 Figured_bass_engraver::center_repeated_continuations ()
224 vector
<Spanner
*> consecutive_lines
;
225 for (vsize i
= 0; i
<= groups_
.size (); i
++)
227 if (i
< groups_
.size ()
228 && groups_
[i
].continuation_line_
229 && (consecutive_lines
.empty ()
230 || (consecutive_lines
[0]->get_bound (LEFT
)->get_column ()
231 == groups_
[i
].continuation_line_
->get_bound (LEFT
)->get_column ()
232 && consecutive_lines
[0]->get_bound (RIGHT
)->get_column ()
233 == groups_
[i
].continuation_line_
->get_bound (RIGHT
)->get_column ())))
234 consecutive_lines
.push_back (groups_
[i
].continuation_line_
);
237 center_continuations (consecutive_lines
);
238 consecutive_lines
.clear ();
244 Figured_bass_engraver::clear_spanners ()
251 announce_end_grob (alignment_
, SCM_EOL
);
255 if (to_boolean (get_property ("figuredBassCenterContinuations")))
256 center_repeated_continuations ();
258 for (vsize i
= 0; i
< groups_
.size (); i
++)
260 if (groups_
[i
].group_
)
262 announce_end_grob (groups_
[i
].group_
, SCM_EOL
);
263 groups_
[i
].group_
= 0;
266 if (groups_
[i
].continuation_line_
)
268 announce_end_grob (groups_
[i
].continuation_line_
, SCM_EOL
);
269 groups_
[i
].continuation_line_
= 0;
273 /* Check me, groups_.clear () ? */
277 Figured_bass_engraver::add_brackets ()
279 vector
<Grob
*> encompass
;
281 for (vsize i
= 0; i
< groups_
.size (); i
++)
283 if (!groups_
[i
].current_event_
)
286 if (to_boolean (groups_
[i
].current_event_
->get_property ("bracket-start")))
289 if (inside
&& groups_
[i
].figure_item_
)
290 encompass
.push_back (groups_
[i
].figure_item_
);
292 if (to_boolean (groups_
[i
].current_event_
->get_property ("bracket-stop")))
296 Item
* brack
= make_item ("BassFigureBracket", groups_
[i
].current_event_
->self_scm ());
297 for (vsize j
= 0; j
< encompass
.size (); j
++)
299 Pointer_group_interface::add_grob (brack
,
300 ly_symbol2scm ("elements"),
309 Figured_bass_engraver::process_music ()
311 if (alignment_
&& !to_boolean (get_property ("useBassFigureExtenders")))
322 && new_events_
.empty ())
329 if (!new_event_found_
)
332 new_event_found_
= false;
335 Don't need to sync alignments, if we're not using extenders.
337 bool use_extenders
= to_boolean (get_property ("useBassFigureExtenders"));
350 for (vsize i
= 0; i
< new_events_
.size (); i
++)
352 while (k
< groups_
.size ()
353 && groups_
[k
].current_event_
)
356 if (k
>= groups_
.size ())
359 groups_
.push_back (group
);
362 groups_
[k
].current_event_
= new_events_
[i
];
363 groups_
[k
].figure_item_
= 0;
367 for (vsize i
= 0; i
< groups_
.size (); i
++)
369 if (!groups_
[i
].is_continuation ())
371 groups_
[i
].number_
= SCM_BOOL_F
;
372 groups_
[i
].alteration_
= SCM_BOOL_F
;
373 groups_
[i
].augmented_
= SCM_BOOL_F
;
374 groups_
[i
].diminished_
= SCM_BOOL_F
;
375 groups_
[i
].augmented_slash_
= SCM_BOOL_F
;
376 groups_
[i
].text_
= SCM_BOOL_F
;
382 vector
<int> junk_continuations
;
383 for (vsize i
= 0; i
< groups_
.size (); i
++)
385 Figure_group
&group
= groups_
[i
];
387 if (group
.is_continuation ())
389 if (!group
.continuation_line_
)
392 = make_spanner ("BassFigureContinuation", SCM_EOL
);
393 Item
* item
= group
.figure_item_
;
394 group
.continuation_line_
= line
;
395 line
->set_bound (LEFT
, item
);
398 Don't add as child. This will cache the wrong
399 (pre-break) stencil when callbacks are triggered.
401 line
->set_parent (group
.group_
, Y_AXIS
);
402 Pointer_group_interface::add_grob (line
, ly_symbol2scm ("figures"), item
);
404 group
.figure_item_
= 0;
407 else if (group
.continuation_line_
)
408 junk_continuations
.push_back (i
);
414 vector
<Spanner
*> consecutive
;
415 if (to_boolean (get_property ("figuredBassCenterContinuations")))
417 for (vsize i
= 0; i
<= junk_continuations
.size (); i
++)
419 if (i
< junk_continuations
.size ()
420 && (i
== 0 || junk_continuations
[i
-1] == junk_continuations
[i
] - 1))
421 consecutive
.push_back (groups_
[junk_continuations
[i
]].continuation_line_
);
424 center_continuations (consecutive
);
425 consecutive
.clear ();
426 if (i
< junk_continuations
.size ())
427 consecutive
.push_back (groups_
[junk_continuations
[i
]].continuation_line_
);
431 for (vsize i
= 0; i
< junk_continuations
.size (); i
++)
432 groups_
[junk_continuations
[i
]].continuation_line_
= 0;
440 Figured_bass_engraver::create_grobs ()
443 = dynamic_cast<Item
*> (unsmob_grob (get_property ("currentMusicalColumn")));
446 alignment_
= make_spanner ("BassFigureAlignment", SCM_EOL
);
447 alignment_
->set_bound (LEFT
, muscol
);
449 alignment_
->set_bound (RIGHT
, muscol
);
451 SCM proc
= get_property ("figuredBassFormatter");
452 for (vsize i
= 0; i
< groups_
.size (); i
++)
454 Figure_group
&group
= groups_
[i
];
456 if (group
.current_event_
)
459 = make_item ("BassFigure",
460 group
.current_event_
->self_scm ());
463 SCM fig
= group
.current_event_
->get_property ("figure");
466 group
.group_
= make_spanner ("BassFigureLine", SCM_EOL
);
467 group
.group_
->set_bound (LEFT
, muscol
);
468 Align_interface::add_element (alignment_
,
472 if (scm_memq (fig
, get_property ("implicitBassFigures")) != SCM_BOOL_F
)
474 item
->set_property ("transparent", SCM_BOOL_T
);
475 item
->set_property ("implicit", SCM_BOOL_T
);
479 group
.alteration_
= group
.current_event_
->get_property ("alteration");
480 group
.augmented_
= group
.current_event_
->get_property ("augmented");
481 group
.diminished_
= group
.current_event_
->get_property ("diminished");
482 group
.augmented_slash_
= group
.current_event_
->get_property ("augmented-slash");
483 group
.text_
= group
.current_event_
->get_property ("text");
485 SCM text
= group
.text_
;
486 if (!Text_interface::is_markup (text
)
487 && ly_is_procedure (proc
))
489 text
= scm_call_3 (proc
, fig
, group
.current_event_
->self_scm (),
490 context ()->self_scm ());
493 item
->set_property ("text", text
);
495 Axis_group_interface::add_element (group
.group_
, item
);
496 group
.figure_item_
= item
;
499 if (group
.continuation_line_
)
502 UGH should connect to the bass staff, and get the note heads.
504 group
.figure_item_
->set_property ("transparent", SCM_BOOL_T
);
505 group
.continuation_line_
->set_bound (RIGHT
, group
.figure_item_
);
508 if (groups_
[i
].group_
)
509 groups_
[i
].group_
->set_bound (RIGHT
, muscol
);
515 ADD_TRANSLATOR (Figured_bass_engraver
,
517 "Make figured bass numbers.",
521 "BassFigureAlignment "
523 "BassFigureContinuation "
527 "figuredBassAlterationDirection "
528 "figuredBassCenterContinuations "
529 "figuredBassFormatter "
530 "implicitBassFigures "
531 "useBassFigureExtenders "
532 "ignoreFiguredBassRest ",