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