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