script.scm: Quantize staccatissimo; de-quantize accent, espressivo.
[lilypond/mpolesky.git] / lily / figured-bass-engraver.cc
blob4783b8ab80e5cf702665ffd822950dafaebd4dd1
1 /*
2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 2005--2009 Han-Wen Nienhuys <hanwen@xs4all.nl>
7 LilyPond is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
12 LilyPond is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with LilyPond. If not, see <http://www.gnu.org/licenses/>.
21 #include "engraver.hh"
23 #include "align-interface.hh"
24 #include "axis-group-interface.hh"
25 #include "context.hh"
26 #include "grob-array.hh"
27 #include "item.hh"
28 #include "pointer-group-interface.hh"
29 #include "spanner.hh"
30 #include "stream-event.hh"
31 #include "text-interface.hh"
33 #include "translator.icc"
35 struct Figure_group
37 Spanner *group_;
38 Spanner *continuation_line_;
40 SCM number_;
41 SCM alteration_;
42 SCM augmented_;
43 SCM diminished_;
44 SCM augmented_slash_;
45 SCM text_;
47 Item *figure_item_;
48 Stream_event *current_event_;
49 bool force_no_continuation_;
51 Figure_group ()
53 figure_item_ = 0;
54 force_no_continuation_ = false;
55 continuation_line_ = 0;
56 number_ = SCM_EOL;
57 alteration_ = SCM_EOL;
58 augmented_ = SCM_EOL;
59 diminished_ = SCM_EOL;
60 augmented_slash_ = SCM_EOL;
61 text_ = SCM_EOL;
62 group_ = 0;
63 current_event_ = 0;
65 bool is_continuation () const
67 return
68 current_event_
69 && !force_no_continuation_
70 && ly_is_equal (number_,
71 current_event_->get_property ("figure"))
72 && ly_is_equal (alteration_,
73 current_event_->get_property ("alteration"))
74 && ly_is_equal (augmented_,
75 current_event_->get_property ("augmented"))
76 && ly_is_equal (diminished_,
77 current_event_->get_property ("diminished"))
78 && ly_is_equal (augmented_slash_,
79 current_event_->get_property ("augmented-slash"))
80 && ly_is_equal (text_,
81 current_event_->get_property ("text"));
85 struct Figured_bass_engraver : public Engraver
87 TRANSLATOR_DECLARATIONS (Figured_bass_engraver);
88 void clear_spanners ();
89 void add_brackets ();
90 void create_grobs ();
92 void center_continuations (vector<Spanner*> const &consecutive_lines);
93 void center_repeated_continuations ();
94 protected:
95 vector<Figure_group> groups_;
96 Spanner *alignment_;
97 vector<Stream_event *> new_events_;
98 bool continuation_;
99 bool new_event_found_;
101 Moment stop_moment_;
102 Stream_event *rest_event_;
104 DECLARE_TRANSLATOR_LISTENER (rest);
105 DECLARE_TRANSLATOR_LISTENER (bass_figure);
107 virtual void derived_mark () const;
109 void start_translation_timestep ();
110 void stop_translation_timestep ();
111 void process_music ();
114 void
115 Figured_bass_engraver::derived_mark () const
117 for (vsize i = 0; i < groups_.size (); i++)
119 scm_gc_mark (groups_[i].number_);
120 scm_gc_mark (groups_[i].alteration_);
121 scm_gc_mark (groups_[i].augmented_);
122 scm_gc_mark (groups_[i].diminished_);
123 scm_gc_mark (groups_[i].augmented_slash_);
124 scm_gc_mark (groups_[i].text_);
128 void
129 Figured_bass_engraver::stop_translation_timestep ()
131 if (groups_.empty ()
132 || now_mom ().main_part_ < stop_moment_.main_part_
133 || now_mom ().grace_part_ < Rational (0))
134 return ;
136 bool found = false;
137 for (vsize i = 0; !found && i < groups_.size (); i++)
138 found = found || groups_[i].current_event_;
140 if (!found)
141 clear_spanners ();
144 Figured_bass_engraver::Figured_bass_engraver ()
146 alignment_ = 0;
147 continuation_ = false;
148 rest_event_ = 0;
149 new_event_found_ = false;
152 void
153 Figured_bass_engraver::start_translation_timestep ()
155 if (now_mom ().main_part_ < stop_moment_.main_part_
156 || now_mom ().grace_part_ < Rational (0))
157 return ;
159 rest_event_ = 0;
160 new_events_.clear ();
161 for (vsize i = 0; i < groups_.size (); i++)
162 groups_[i].current_event_ = 0;
164 continuation_ = false;
169 IMPLEMENT_TRANSLATOR_LISTENER (Figured_bass_engraver, rest);
170 void
171 Figured_bass_engraver::listen_rest (Stream_event *ev)
173 if (to_boolean (get_property ("ignoreFiguredBassRest")))
175 new_event_found_ = true;
178 No ASSIGN_EVENT_ONCE () ; otherwise we get warnings about
179 polyphonic rests.
181 rest_event_ = ev;
185 IMPLEMENT_TRANSLATOR_LISTENER (Figured_bass_engraver, bass_figure);
186 void
187 Figured_bass_engraver::listen_bass_figure (Stream_event *ev)
189 new_event_found_ = true;
190 Moment stop = now_mom () + get_event_length (ev, now_mom ());
191 stop_moment_ = max (stop_moment_, stop);
193 if (to_boolean (get_property ("useBassFigureExtenders")))
195 SCM fig = ev->get_property ("figure");
196 SCM txt = ev->get_property ("text");
197 for (vsize i = 0; i < groups_.size (); i++)
199 if (!groups_[i].current_event_
200 && ly_is_equal (groups_[i].number_, fig)
201 && ly_is_equal (groups_[i].text_, txt))
203 groups_[i].current_event_ = ev;
204 groups_[i].force_no_continuation_
205 = to_boolean (ev->get_property ("no-continuation"));
206 continuation_ = true;
207 return;
211 new_events_.push_back (ev);
214 void
215 Figured_bass_engraver::center_continuations (vector<Spanner*> const &consecutive_lines)
217 if (consecutive_lines.size () == 2)
219 vector<Grob*> left_figs;
220 for (vsize j = consecutive_lines.size (); j--;)
221 left_figs.push_back (consecutive_lines[j]->get_bound (LEFT));
223 SCM ga = Grob_array::make_array ();
224 unsmob_grob_array (ga)->set_array (left_figs);
226 for (vsize j = consecutive_lines.size (); j--;)
227 consecutive_lines[j]->set_object ("figures",
228 unsmob_grob_array (ga)->smobbed_copy ());
232 void
233 Figured_bass_engraver::center_repeated_continuations ()
235 vector<Spanner*> consecutive_lines;
236 for (vsize i = 0; i <= groups_.size (); i++)
238 if (i < groups_.size ()
239 && groups_[i].continuation_line_
240 && (consecutive_lines.empty ()
241 || (consecutive_lines[0]->get_bound (LEFT)->get_column ()
242 == groups_[i].continuation_line_->get_bound (LEFT)->get_column ()
243 && consecutive_lines[0]->get_bound (RIGHT)->get_column ()
244 == groups_[i].continuation_line_->get_bound (RIGHT)->get_column ())))
245 consecutive_lines.push_back (groups_[i].continuation_line_);
246 else
248 center_continuations (consecutive_lines);
249 consecutive_lines.clear ();
254 void
255 Figured_bass_engraver::clear_spanners ()
257 if (!alignment_)
258 return;
260 if (alignment_)
262 announce_end_grob (alignment_, SCM_EOL);
263 alignment_ = 0;
266 if (to_boolean (get_property ("figuredBassCenterContinuations")))
267 center_repeated_continuations ();
269 for (vsize i = 0; i < groups_.size (); i++)
271 if (groups_[i].group_)
273 announce_end_grob (groups_[i].group_ , SCM_EOL);
274 groups_[i].group_ = 0;
277 if (groups_[i].continuation_line_)
279 announce_end_grob (groups_[i].continuation_line_ , SCM_EOL);
280 groups_[i].continuation_line_ = 0;
284 /* Check me, groups_.clear () ? */
287 void
288 Figured_bass_engraver::add_brackets ()
290 vector<Grob*> encompass;
291 bool inside = false;
292 for (vsize i = 0; i < groups_.size (); i ++)
294 if (!groups_[i].current_event_)
295 continue;
297 if (to_boolean (groups_[i].current_event_->get_property ("bracket-start")))
298 inside = true;
300 if (inside && groups_[i].figure_item_)
301 encompass.push_back (groups_[i].figure_item_);
303 if (to_boolean (groups_[i].current_event_->get_property ("bracket-stop")))
305 inside = false;
307 Item * brack = make_item ("BassFigureBracket", groups_[i].current_event_->self_scm ());
308 for (vsize j = 0; j < encompass.size (); j++)
310 Pointer_group_interface::add_grob (brack,
311 ly_symbol2scm ("elements"),
312 encompass[j]);
314 encompass.clear ();
319 void
320 Figured_bass_engraver::process_music ()
322 if (alignment_ && !to_boolean (get_property ("useBassFigureExtenders")))
323 clear_spanners ();
325 if (rest_event_)
327 clear_spanners ();
328 groups_.clear ();
329 return;
332 if (!continuation_
333 && new_events_.empty ())
335 clear_spanners ();
336 groups_.clear ();
337 return;
340 if (!new_event_found_)
341 return;
343 new_event_found_ = false;
346 Don't need to sync alignments, if we're not using extenders.
348 bool use_extenders = to_boolean (get_property ("useBassFigureExtenders"));
349 if (!use_extenders)
351 clear_spanners ();
354 if (!continuation_)
356 clear_spanners ();
357 groups_.clear ();
360 vsize k = 0;
361 for (vsize i = 0; i < new_events_.size (); i++)
363 while (k < groups_.size ()
364 && groups_[k].current_event_)
365 k++;
367 if (k >= groups_.size ())
369 Figure_group group;
370 groups_.push_back (group);
373 groups_[k].current_event_ = new_events_[i];
374 groups_[k].figure_item_ = 0;
375 k++;
378 for (vsize i = 0; i < groups_.size (); i++)
380 if (!groups_[i].is_continuation ())
382 groups_[i].number_ = SCM_BOOL_F;
383 groups_[i].alteration_ = SCM_BOOL_F;
384 groups_[i].augmented_ = SCM_BOOL_F;
385 groups_[i].diminished_ = SCM_BOOL_F;
386 groups_[i].augmented_slash_ = SCM_BOOL_F;
387 groups_[i].text_ = SCM_BOOL_F;
391 if (use_extenders)
393 vector<int> junk_continuations;
394 for (vsize i = 0; i < groups_.size (); i++)
396 Figure_group &group = groups_[i];
398 if (group.is_continuation ())
400 if (!group.continuation_line_)
402 Spanner * line
403 = make_spanner ("BassFigureContinuation", SCM_EOL);
404 Item * item = group.figure_item_;
405 group.continuation_line_ = line;
406 line->set_bound (LEFT, item);
409 Don't add as child. This will cache the wrong
410 (pre-break) stencil when callbacks are triggered.
412 line->set_parent (group.group_, Y_AXIS);
413 Pointer_group_interface::add_grob (line, ly_symbol2scm ("figures"), item);
415 group.figure_item_ = 0;
418 else if (group.continuation_line_)
419 junk_continuations.push_back (i);
423 Ugh, repeated code.
425 vector<Spanner*> consecutive;
426 if (to_boolean (get_property ("figuredBassCenterContinuations")))
428 for (vsize i = 0; i <= junk_continuations.size (); i++)
430 if (i < junk_continuations.size ()
431 && (i == 0 || junk_continuations[i-1] == junk_continuations[i] - 1))
432 consecutive.push_back (groups_[junk_continuations[i]].continuation_line_);
433 else
435 center_continuations (consecutive);
436 consecutive.clear ();
437 if (i < junk_continuations.size ())
438 consecutive.push_back (groups_[junk_continuations[i]].continuation_line_);
442 for (vsize i = 0; i < junk_continuations.size (); i++)
443 groups_[junk_continuations[i]].continuation_line_ = 0;
446 create_grobs ();
447 add_brackets ();
450 void
451 Figured_bass_engraver::create_grobs ()
453 Grob *muscol
454 = dynamic_cast<Item*> (unsmob_grob (get_property ("currentMusicalColumn")));
455 if (!alignment_)
457 alignment_ = make_spanner ("BassFigureAlignment", SCM_EOL);
458 alignment_->set_bound (LEFT, muscol);
460 alignment_->set_bound (RIGHT, muscol);
462 SCM proc = get_property ("figuredBassFormatter");
463 for (vsize i = 0; i < groups_.size (); i++)
465 Figure_group &group = groups_[i];
467 if (group.current_event_)
469 Item *item
470 = make_item ("BassFigure",
471 group.current_event_->self_scm ());
474 SCM fig = group.current_event_->get_property ("figure");
475 if (!group.group_)
477 group.group_ = make_spanner ("BassFigureLine", SCM_EOL);
478 group.group_->set_bound (LEFT, muscol);
479 Align_interface::add_element (alignment_,
480 group.group_);
483 if (scm_memq (fig, get_property ("implicitBassFigures")) != SCM_BOOL_F)
485 item->set_property ("transparent", SCM_BOOL_T);
486 item->set_property ("implicit", SCM_BOOL_T);
489 group.number_ = fig;
490 group.alteration_ = group.current_event_->get_property ("alteration");
491 group.augmented_ = group.current_event_->get_property ("augmented");
492 group.diminished_ = group.current_event_->get_property ("diminished");
493 group.augmented_slash_ = group.current_event_->get_property ("augmented-slash");
494 group.text_ = group.current_event_->get_property ("text");
496 SCM text = group.text_;
497 if (!Text_interface::is_markup (text)
498 && ly_is_procedure (proc))
500 text = scm_call_3 (proc, fig, group.current_event_->self_scm (),
501 context ()->self_scm ());
504 item->set_property ("text", text);
506 Axis_group_interface::add_element (group.group_, item);
507 group.figure_item_ = item;
510 if (group.continuation_line_)
513 UGH should connect to the bass staff, and get the note heads.
515 group.figure_item_->set_property ("transparent", SCM_BOOL_T);
516 group.continuation_line_->set_bound (RIGHT, group.figure_item_);
519 if (groups_[i].group_)
520 groups_[i].group_->set_bound (RIGHT, muscol);
526 ADD_TRANSLATOR (Figured_bass_engraver,
527 /* doc */
528 "Make figured bass numbers.",
530 /* create */
531 "BassFigure "
532 "BassFigureAlignment "
533 "BassFigureBracket "
534 "BassFigureContinuation "
535 "BassFigureLine ",
537 /* read */
538 "figuredBassAlterationDirection "
539 "figuredBassCenterContinuations "
540 "figuredBassFormatter "
541 "implicitBassFigures "
542 "useBassFigureExtenders "
543 "ignoreFiguredBassRest ",
545 /* write */