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