]> git.donarmstrong.com Git - lilypond.git/blob - lily/figured-bass-engraver.cc
Run `make grand-replace'.
[lilypond.git] / lily / figured-bass-engraver.cc
1 /*
2   figured-bass-engraver.cc -- implement Figured_bass_engraver
3
4   source file of the GNU LilyPond music typesetter
5
6   (c) 2005--2008 Han-Wen Nienhuys <hanwen@xs4all.nl>
7
8 */
9
10 #include "engraver.hh"
11
12 #include "align-interface.hh"
13 #include "axis-group-interface.hh"
14 #include "context.hh"
15 #include "grob-array.hh"
16 #include "item.hh"
17 #include "pointer-group-interface.hh"
18 #include "spanner.hh"
19 #include "stream-event.hh"
20 #include "text-interface.hh"
21
22 #include "translator.icc"
23
24 struct Figure_group
25 {
26   Spanner *group_;
27   Spanner *continuation_line_;
28   
29   SCM number_;
30   SCM alteration_;
31   SCM augmented_;
32   SCM diminished_;
33   SCM augmented_slash_;
34   
35   Item *figure_item_; 
36   Stream_event *current_event_;
37   bool force_no_continuation_;
38   
39   Figure_group ()
40   {
41     figure_item_ = 0;
42     force_no_continuation_ = false;
43     continuation_line_ = 0;
44     number_ = SCM_EOL;
45     alteration_ = SCM_EOL;
46     augmented_ = SCM_EOL;
47     diminished_ = SCM_EOL;
48     augmented_slash_ = SCM_EOL;
49     group_ = 0;
50     current_event_ = 0;
51   }
52   bool is_continuation () const
53   {
54     return
55       current_event_
56       && !force_no_continuation_
57       && ly_is_equal (number_,
58                       current_event_->get_property ("figure"))
59       && ly_is_equal (alteration_,
60                       current_event_->get_property ("alteration"))
61       && ly_is_equal (augmented_,
62                       current_event_->get_property ("augmented"))
63       && ly_is_equal (diminished_,
64                       current_event_->get_property ("diminished"))
65       && ly_is_equal (augmented_slash_,
66                       current_event_->get_property ("augmented-slash"));
67   }
68 };
69
70 struct Figured_bass_engraver : public Engraver
71 {
72   TRANSLATOR_DECLARATIONS (Figured_bass_engraver);
73   void clear_spanners ();
74   void add_brackets ();
75   void create_grobs ();
76
77   void center_continuations (vector<Spanner*> const &consecutive_lines);
78   void center_repeated_continuations ();
79 protected:
80   vector<Figure_group> groups_;
81   Spanner *alignment_;
82   vector<Stream_event *> new_events_;
83   bool continuation_;
84   bool new_event_found_;
85   
86   Moment stop_moment_;
87   Stream_event *rest_event_; 
88
89   DECLARE_TRANSLATOR_LISTENER (rest);
90   DECLARE_TRANSLATOR_LISTENER (bass_figure);
91
92   virtual void derived_mark () const; 
93
94   void start_translation_timestep ();
95   void stop_translation_timestep ();
96   void process_music ();
97 };
98
99 void
100 Figured_bass_engraver::derived_mark () const
101 {
102   for (vsize i = 0; i < groups_.size (); i++)
103     {
104       scm_gc_mark (groups_[i].number_);
105       scm_gc_mark (groups_[i].alteration_);
106       scm_gc_mark (groups_[i].augmented_);
107       scm_gc_mark (groups_[i].diminished_);
108       scm_gc_mark (groups_[i].augmented_slash_);
109     }
110 }
111
112 void
113 Figured_bass_engraver::stop_translation_timestep ()
114 {
115   if (groups_.empty ()
116       || now_mom ().main_part_ < stop_moment_.main_part_
117       || now_mom ().grace_part_ < Rational (0))
118     return ;
119   
120   bool found = false;
121   for (vsize i = 0; !found && i < groups_.size (); i++)
122     found  = found  || groups_[i].current_event_;
123
124   if (!found)
125     clear_spanners ();
126 }
127
128 Figured_bass_engraver::Figured_bass_engraver ()
129 {
130   alignment_ = 0;
131   continuation_ = false;
132   rest_event_ = 0;
133   new_event_found_ = false;
134 }
135
136 void
137 Figured_bass_engraver::start_translation_timestep ()
138 {
139   if (now_mom ().main_part_ < stop_moment_.main_part_
140       || now_mom ().grace_part_ < Rational (0))
141     return ;
142   
143   rest_event_ = 0;
144   new_events_.clear ();
145   for (vsize i = 0; i < groups_.size (); i++)
146     groups_[i].current_event_ = 0;
147
148   continuation_ = false;
149
150   
151 }
152
153 IMPLEMENT_TRANSLATOR_LISTENER (Figured_bass_engraver, rest);
154 void
155 Figured_bass_engraver::listen_rest (Stream_event *ev)
156 {
157   if (to_boolean (get_property ("ignoreFiguredBassRest")))
158     {
159       new_event_found_ = true;
160
161       /*
162         No ASSIGN_EVENT_ONCE () ; otherwise we get warnings about
163         polyphonic rests.
164        */
165       rest_event_ = ev;
166     }
167 }
168
169 IMPLEMENT_TRANSLATOR_LISTENER (Figured_bass_engraver, bass_figure);
170 void
171 Figured_bass_engraver::listen_bass_figure (Stream_event *ev)
172 {
173   new_event_found_ = true;
174   Moment stop  = now_mom () + get_event_length (ev, now_mom ());
175   stop_moment_ = max (stop_moment_, stop);
176
177   if (to_boolean (get_property ("useBassFigureExtenders")))
178     {
179       SCM fig = ev->get_property ("figure");
180       for (vsize i = 0; i < groups_.size (); i++)
181         {
182           if (!groups_[i].current_event_
183               && ly_is_equal (groups_[i].number_, fig))
184             {
185               groups_[i].current_event_ = ev;
186               groups_[i].force_no_continuation_
187                 = to_boolean (ev->get_property ("no-continuation"));
188               continuation_ = true;
189               return; 
190             }
191         }
192     }  
193   new_events_.push_back (ev);
194 }
195
196 void
197 Figured_bass_engraver::center_continuations (vector<Spanner*> const &consecutive_lines)
198 {
199   if (consecutive_lines.size () == 2)
200     {
201       vector<Grob*> left_figs;
202       for (vsize j = consecutive_lines.size (); j--;)
203         left_figs.push_back (consecutive_lines[j]->get_bound (LEFT));
204
205       SCM  ga = Grob_array::make_array ();
206       unsmob_grob_array (ga)->set_array (left_figs);
207
208       for (vsize j = consecutive_lines.size (); j--;)
209         consecutive_lines[j]->set_object ("figures",
210                                           unsmob_grob_array (ga)->smobbed_copy ());
211     }
212 }
213
214 void
215 Figured_bass_engraver::center_repeated_continuations ()
216 {  
217   vector<Spanner*> consecutive_lines;
218   for (vsize i = 0; i <= groups_.size (); i++)
219     {
220       if (i < groups_.size ()
221           && groups_[i].continuation_line_
222           && (consecutive_lines.empty ()
223               || (consecutive_lines[0]->get_bound (LEFT)->get_column ()
224                   == groups_[i].continuation_line_->get_bound (LEFT)->get_column ()
225                   && consecutive_lines[0]->get_bound (RIGHT)->get_column ()
226                   == groups_[i].continuation_line_->get_bound (RIGHT)->get_column ())))
227         consecutive_lines.push_back (groups_[i].continuation_line_);      
228       else 
229         {
230           center_continuations (consecutive_lines);
231           consecutive_lines.clear ();
232         }
233     }
234 }
235
236 void
237 Figured_bass_engraver::clear_spanners ()
238 {
239   if (!alignment_)
240     return;
241
242   if (alignment_)
243     {
244       announce_end_grob (alignment_, SCM_EOL);
245       alignment_ = 0;
246     }
247
248   if (to_boolean (get_property ("figuredBassCenterContinuations")))
249     center_repeated_continuations ();
250   
251   for (vsize i = 0; i < groups_.size (); i++)
252     {
253       if (groups_[i].group_)
254         {
255           announce_end_grob (groups_[i].group_ , SCM_EOL);
256           groups_[i].group_ = 0;
257         }
258       
259       if (groups_[i].continuation_line_)
260         {
261           announce_end_grob (groups_[i].continuation_line_ , SCM_EOL);
262           groups_[i].continuation_line_ = 0;
263         }
264     }
265
266   /* Check me, groups_.clear () ? */
267 }
268
269 void
270 Figured_bass_engraver::add_brackets ()
271 {
272   vector<Grob*> encompass;
273   bool inside = false;
274   for (vsize i = 0; i < groups_.size (); i ++)
275     {
276       if (!groups_[i].current_event_)
277         continue;
278       
279       if (to_boolean (groups_[i].current_event_->get_property ("bracket-start")))       
280         inside = true;
281
282       if (inside && groups_[i].figure_item_)
283         encompass.push_back (groups_[i].figure_item_);
284
285        if (to_boolean (groups_[i].current_event_->get_property ("bracket-stop")))
286         {
287           inside = false;
288
289           Item * brack = make_item ("BassFigureBracket", groups_[i].current_event_->self_scm ());
290           for (vsize j = 0; j < encompass.size (); j++)
291             {
292               Pointer_group_interface::add_grob (brack,
293                                                  ly_symbol2scm ("elements"),
294                                                  encompass[j]);
295             }
296           encompass.clear ();
297         }
298     }
299 }
300
301 void
302 Figured_bass_engraver::process_music ()
303 {
304   if (alignment_ && !to_boolean (get_property ("useBassFigureExtenders")))
305     clear_spanners ();
306         
307   if (rest_event_)
308     {
309       clear_spanners ();
310       groups_.clear ();
311       return;
312     }
313   
314   if (!continuation_
315       && new_events_.empty ())
316     {
317       clear_spanners ();
318       groups_.clear ();
319       return;
320     }
321
322   if (!new_event_found_)
323     return;
324   
325   new_event_found_ = false;
326
327   /*
328     Don't need to sync alignments, if we're not using extenders. 
329    */
330   bool use_extenders = to_boolean (get_property ("useBassFigureExtenders"));
331   if (!use_extenders)
332     {
333       clear_spanners ();
334     }
335   
336   if (!continuation_)
337     {
338       clear_spanners ();
339       groups_.clear ();
340     }
341
342   vsize k = 0;
343   for (vsize i = 0; i < new_events_.size (); i++)
344     {
345       while (k < groups_.size ()
346              && groups_[k].current_event_)
347         k++;
348       
349       if (k >= groups_.size ())
350         {
351           Figure_group group;
352           groups_.push_back (group);
353         }
354       
355       groups_[k].current_event_ = new_events_[i];
356       groups_[k].figure_item_ = 0;
357       k++;
358     }
359
360   for (vsize i = 0; i < groups_.size (); i++)
361     {
362       if (!groups_[i].is_continuation ())
363         {
364           groups_[i].number_ = SCM_BOOL_F;
365           groups_[i].alteration_ = SCM_BOOL_F;
366           groups_[i].augmented_ = SCM_BOOL_F;
367           groups_[i].diminished_ = SCM_BOOL_F;
368           groups_[i].augmented_slash_ = SCM_BOOL_F;
369         }
370     }
371
372   if (use_extenders)
373     {
374       vector<int> junk_continuations;
375       for (vsize i = 0; i < groups_.size (); i++)
376         {
377           Figure_group &group = groups_[i];
378
379           if (group.is_continuation ())
380             {
381               if (!group.continuation_line_)
382                 {
383                   Spanner * line
384                     = make_spanner ("BassFigureContinuation", SCM_EOL);
385                   Item * item = group.figure_item_;
386                   group.continuation_line_ = line;
387                   line->set_bound (LEFT, item);
388
389                   /*
390                     Don't add as child. This will cache the wrong
391                     (pre-break) stencil when callbacks are triggered.
392                   */
393                   line->set_parent (group.group_, Y_AXIS);
394                   Pointer_group_interface::add_grob (line, ly_symbol2scm ("figures"), item);
395
396                   group.figure_item_ = 0;
397                 }
398             }
399           else if (group.continuation_line_) 
400             junk_continuations.push_back (i); 
401         }
402
403       /*
404         Ugh, repeated code.
405        */
406       vector<Spanner*> consecutive;
407       if (to_boolean (get_property ("figuredBassCenterContinuations")))
408         {
409           for (vsize i = 0; i <= junk_continuations.size (); i++)
410             {
411               if (i < junk_continuations.size ()
412                   && (i == 0 || junk_continuations[i-1] == junk_continuations[i] - 1))
413                 consecutive.push_back (groups_[junk_continuations[i]].continuation_line_);
414               else 
415                 {
416                   center_continuations (consecutive);
417                   consecutive.clear ();
418                   if (i < junk_continuations.size ())
419                     consecutive.push_back (groups_[junk_continuations[i]].continuation_line_);
420                 }
421             }
422         }
423       for (vsize i = 0; i < junk_continuations.size (); i++)
424         groups_[junk_continuations[i]].continuation_line_ = 0;
425     }
426   
427   create_grobs ();
428   add_brackets ();
429 }
430
431 void
432 Figured_bass_engraver::create_grobs () 
433 {
434   Grob *muscol
435     = dynamic_cast<Item*> (unsmob_grob (get_property ("currentMusicalColumn")));
436   if (!alignment_)
437     {
438       alignment_ = make_spanner ("BassFigureAlignment", SCM_EOL);
439       alignment_->set_bound (LEFT, muscol);
440     }
441   alignment_->set_bound (RIGHT, muscol);
442
443   SCM proc = get_property ("figuredBassFormatter");
444   for (vsize i = 0; i < groups_.size (); i++)
445     {
446       Figure_group &group = groups_[i];
447       
448       if (group.current_event_)
449         {
450           Item *item
451             = make_item ("BassFigure",
452                          group.current_event_->self_scm ());
453
454           
455           SCM fig = group.current_event_->get_property ("figure");
456           if (!group.group_)
457             {
458               group.group_ = make_spanner ("BassFigureLine", SCM_EOL);
459               group.group_->set_bound (LEFT, muscol);
460               Align_interface::add_element (alignment_,
461                                             group.group_);
462             }
463
464           if (scm_memq (fig, get_property ("implicitBassFigures")) != SCM_BOOL_F)
465             {
466               item->set_property ("transparent", SCM_BOOL_T); 
467               item->set_property ("implicit", SCM_BOOL_T);
468             }
469           
470           group.number_ = fig;
471           group.alteration_ = group.current_event_->get_property ("alteration");
472           group.augmented_ = group.current_event_->get_property ("augmented");
473           group.diminished_ = group.current_event_->get_property ("diminished");
474           group.augmented_slash_ = group.current_event_->get_property ("augmented-slash");
475
476           SCM text = group.current_event_->get_property ("text");
477           if (!Text_interface::is_markup (text)
478               && ly_is_procedure (proc))
479             {
480               text = scm_call_3 (proc, fig, group.current_event_->self_scm (),
481                                  context ()->self_scm ());
482             }
483
484           item->set_property ("text", text);
485           
486           Axis_group_interface::add_element (group.group_, item);
487           group.figure_item_ = item;
488         }
489
490       if (group.continuation_line_)
491         {
492           /*
493             UGH should connect to the bass staff, and get the note heads. 
494           */
495           group.figure_item_->set_property ("transparent", SCM_BOOL_T);
496           group.continuation_line_->set_bound (RIGHT, group.figure_item_);
497         }
498       
499       if (groups_[i].group_)
500         groups_[i].group_->set_bound (RIGHT, muscol);
501
502     }
503
504 }
505
506 ADD_TRANSLATOR (Figured_bass_engraver,
507                 /* doc */
508                 "Make figured bass numbers.",
509
510                 /* create */
511                 "BassFigure "
512                 "BassFigureAlignment "
513                 "BassFigureBracket "
514                 "BassFigureContinuation "
515                 "BassFigureLine ",
516
517                 /* read */
518                 "figuredBassAlterationDirection "
519                 "figuredBassCenterContinuations "
520                 "figuredBassFormatter "
521                 "implicitBassFigures "
522                 "useBassFigureExtenders "
523                 "ignoreFiguredBassRest ",
524
525                 /* write */
526                 ""
527                 );