]> git.donarmstrong.com Git - lilypond.git/blob - lily/piano-pedal-engraver.cc
*** empty log message ***
[lilypond.git] / lily / piano-pedal-engraver.cc
1 /*   
2   piano-pedal-engraver.cc --  implement Piano_pedal_engraver
3   
4   source file of the GNU LilyPond music typesetter
5   
6  (c) 2000--2003 Jan Nieuwenhuizen <janneke@gnu.org>
7   
8   Chris Jackson <chris@fluffhouse.org.uk> - extended to support
9   bracketed pedals.
10 */
11
12 #include "engraver.hh"
13 #include "event.hh"
14 #include "grob.hh"
15 #include "item.hh"
16 #include "lily-guile.hh"
17 #include "side-position-interface.hh"
18 #include "staff-symbol-referencer.hh"
19 #include "item.hh"
20 #include "axis-group-interface.hh"
21 #include "translator-group.hh"
22 #include "directional-element-interface.hh"
23 #include "note-column.hh"
24
25 struct Pedal_info
26 {
27   char const * name_;
28   Music* start_req_;
29   Drul_array<Music*> req_l_drul_;
30   Item* item_;
31   Spanner* bracket_;     // A single portion of a pedal bracket
32   Spanner* finished_bracket_;
33
34   /*
35     This grob contains all the pedals of the same type on the same staff
36    */
37   Spanner* line_spanner_;
38   Spanner* finished_line_spanner_;
39   Music* current_bracket_req_;
40 };
41
42
43 class Piano_pedal_engraver : public Engraver
44 {
45 public:
46   TRANSLATOR_DECLARATIONS (Piano_pedal_engraver);
47   ~Piano_pedal_engraver ();
48 protected:
49   virtual void initialize ();
50   virtual void finalize ();
51   virtual bool try_music (Music*);
52   virtual void stop_translation_timestep ();
53   virtual void start_translation_timestep ();
54   virtual void acknowledge_grob (Grob_info);
55   virtual void process_acknowledged_grobs ();
56
57 private:
58
59   Pedal_info *info_list_;
60
61   /*
62     Record a stack of the current pedal spanners, so if more than one pedal
63     occurs simultaneously then extra space can be added between them.
64
65
66     Why 4, why not 3. Why not 3423 ?  ---hwn.
67   */
68   
69   Spanner *previous_ [4];
70   
71   int spanner_count_;
72
73   /*
74     Left and right flare widths of a \___/, as specified by the grob
75     property edge-widen.
76    */
77   Drul_array<SCM> edge_width_drul_;
78   
79   void create_text_grobs (Pedal_info *p, SCM pedaltype);
80   void create_bracket_grobs (Pedal_info *p, SCM pedaltype);
81   void typeset_all ();
82 };
83
84
85 Piano_pedal_engraver::Piano_pedal_engraver ()
86 {
87   info_list_ = 0;
88 }
89
90 void
91 Piano_pedal_engraver::initialize ()
92 {
93   info_list_ = new Pedal_info[4];
94   Pedal_info *p = info_list_;
95
96   spanner_count_ = 0;
97   for (int i = 0; i < 3; ++i)
98     previous_[i] = 0; 
99
100
101   char * names [] = { "Sostenuto", "Sustain", "UnaCorda", 0  };
102   char **np = names ;
103   do
104     {
105       p->name_ = *np;
106       p->item_ = 0;
107       p->bracket_ = 0;
108       p->finished_bracket_ = 0;
109       p->line_spanner_ = 0;
110       p->finished_line_spanner_ = 0;
111       p->current_bracket_req_ = 0;
112       p->req_l_drul_[START] = 0;
113       p->req_l_drul_[STOP] = 0;
114       p->start_req_ = 0;
115
116       p++;
117     }
118   while (* (np ++));
119 }
120
121 Piano_pedal_engraver::~Piano_pedal_engraver ()
122 {
123   delete[] info_list_;
124 }
125
126 /*
127    Urg: Code dup
128    I'm a script
129   */
130 void
131 Piano_pedal_engraver::acknowledge_grob (Grob_info info)
132 {
133   for (Pedal_info*p = info_list_; p && p->name_; p ++)
134     {
135       if (Note_column::has_interface (info.grob_))
136         {
137           if (p->line_spanner_)
138             {
139               Side_position_interface::add_support (p->line_spanner_, info.grob_);
140               
141               add_bound_item (p->line_spanner_,info.grob_);
142             }     
143           if (p->bracket_)
144             add_bound_item (p->bracket_,info.grob_);              
145           
146         }
147     }
148 }
149
150 bool
151 Piano_pedal_engraver::try_music (Music *m)
152 {
153   if (m->is_mus_type ("abort-event"))
154     {
155       for (Pedal_info*p = info_list_; p->name_; p ++)
156         {
157           p->req_l_drul_[START] = 0;
158           p->req_l_drul_[STOP] = 0;
159           
160               if (p->bracket_)
161                 p->bracket_->suicide (); /* as in dynamic-engraver.cc */
162               p->bracket_ = 0;
163             }
164     }
165   else if  (m->is_mus_type ("pedal-event"))
166     {
167       for (Pedal_info*p = info_list_; p->name_; p ++)
168         {
169           String nm = p->name_ + String ("Event");
170           if (gh_equal_p (m->get_mus_property ("name") ,
171                           gh_symbol2scm (nm.to_str0())))
172             {
173               Direction d = to_dir (m->get_mus_property ("span-direction"));
174               p->req_l_drul_[d] = m;
175               return true;
176             }
177         }
178     }
179   return false;
180 }
181
182 void
183 Piano_pedal_engraver::process_acknowledged_grobs ()
184 {
185   for (Pedal_info*p = info_list_; p && p->name_; p ++)
186     {
187       if (p->req_l_drul_[STOP] || p->req_l_drul_[START])
188         {
189           if (!p->line_spanner_)
190             {
191               String name  = String (p->name_) + "PedalLineSpanner";
192               p->line_spanner_ = new Spanner (get_property (name.to_str0 ()));
193
194               
195               Music * rq = (p->req_l_drul_[START]  ?  p->req_l_drul_[START]  :  p->req_l_drul_[STOP]);
196               announce_grob (p->line_spanner_, rq->self_scm ());
197             }
198       
199           /* Choose the appropriate grobs to add to the line spanner
200            These can be text items or text-spanners
201           */
202           SCM type = ly_cdr (scm_assoc (ly_symbol2scm ("pedal-type"), 
203                                         get_property ( (String (p->name_) + "Pedal").to_str0 ())));
204           if (type == ly_symbol2scm ("text") ||      // Ped.     *Ped.  *
205               type == ly_symbol2scm ("mixed")  )    // Ped. _____/\____|
206             {
207               if (! p->item_)
208                 create_text_grobs (p, type);
209             }
210           if (type == ly_symbol2scm ("bracket") ||   // |_________/\____|
211               type == ly_symbol2scm ("mixed")  )
212            {
213              create_bracket_grobs (p, type);
214            }
215         }
216     }
217 }
218
219
220 void
221 Piano_pedal_engraver::create_text_grobs (Pedal_info *p, SCM pedaltype)
222 {
223   SCM b;
224   SCM s = SCM_EOL;
225   SCM strings = get_property ( ("pedal" + String (p->name_) + "Strings").to_str0 ());
226
227   if (scm_ilength (strings) >= 3)
228     {
229       if (p->req_l_drul_[STOP] && p->req_l_drul_[START]) 
230         {
231           if (pedaltype == ly_symbol2scm ("text")) 
232             {
233               if (!p->start_req_)
234                 {
235                   p->req_l_drul_[STOP]->origin ()->warning (_f ("can't find start of piano pedal: `%s'",  p->name_));
236                 }
237               else
238                 {
239                   s = ly_cadr (strings);
240                 }
241               p->start_req_ = p->req_l_drul_[START];
242             }
243         }
244       else if (p->req_l_drul_[STOP])
245         { 
246           if (pedaltype == ly_symbol2scm ("text"))
247             {
248               if (!p->start_req_)
249                 {
250                   p->req_l_drul_[STOP]->origin ()->warning (_f ("can't find start of piano pedal: `%s'", p->name_));
251                 }
252               else
253                 {
254                   s = ly_caddr (strings);
255                   spanner_count_ --;
256                 }
257               p->start_req_ = 0;
258             }
259         }
260       else if (p->req_l_drul_[START])
261         {
262           p->start_req_ = p->req_l_drul_[START];
263           s = ly_car (strings);
264           if (pedaltype == ly_symbol2scm ("text"))
265             {
266               spanner_count_ ++;
267               previous_[spanner_count_] = p->line_spanner_;
268               if (spanner_count_ > 1)
269                 // add extra space below the previous already-occuring pedal
270                 Side_position_interface::add_support (p->line_spanner_,
271                                                      previous_[spanner_count_ - 1]);
272             }
273         }
274       
275       if (gh_string_p (s))
276         {
277           String propname = String (p->name_) + "Pedal";
278           b = get_property (propname.to_str0 ());
279           p->item_ = new Item (b);
280           p->item_->set_grob_property ("text", s);
281           Axis_group_interface::add_element (p->line_spanner_, p->item_);
282           
283           announce_grob (p->item_,
284                          (p->req_l_drul_[START]
285                          ? p->req_l_drul_[START]
286                          : p->req_l_drul_[STOP])->self_scm ());
287           
288         }
289       if (pedaltype == ly_symbol2scm ("text")) 
290         {
291           p->req_l_drul_[START] = 0;
292           p->req_l_drul_[STOP] = 0;
293         }
294     }
295 }
296
297 void
298 Piano_pedal_engraver::create_bracket_grobs (Pedal_info *p, SCM pedaltype)
299 {
300
301   if (p->req_l_drul_[STOP])
302     {
303       if (!p->start_req_)
304         {
305           p->req_l_drul_[STOP]->origin ()->warning (_f ("can't find start of piano pedal: `%s'", p->name_));
306         }
307       else if (!p->req_l_drul_[START])
308         spanner_count_ -- ;
309
310       assert (!p->finished_bracket_ && p->bracket_);
311
312       Grob *cmc = unsmob_grob (get_property ("currentMusicalColumn"));
313       p->bracket_->set_bound (RIGHT, cmc);
314
315       /*
316         Set properties so that the molecule-creating function will
317         know whether the right edge should be flared ___/
318        */
319       SCM eleft = ly_car (p->bracket_->get_grob_property ("edge-widen"));
320       SCM eright = (p->req_l_drul_[START]  ? edge_width_drul_[RIGHT] : gh_double2scm (0));
321       p->bracket_->set_grob_property ("edge-widen", gh_cons (eleft, eright));
322       
323       p->finished_bracket_ = p->bracket_;
324       p->bracket_ = 0;
325       p->current_bracket_req_ = 0;
326       p->start_req_ = p->req_l_drul_[START];
327     }
328
329   if (p->req_l_drul_[START])
330     {
331       p->start_req_ = p->req_l_drul_[START];
332       p->current_bracket_req_ = p->req_l_drul_[START];
333
334       p->bracket_  = new Spanner (get_property ("PianoPedalBracket"));
335
336       /*
337         Set properties so that the molecule-creating function will
338         know whether the left edge should be flared \___
339       */
340
341       SCM ew = p->bracket_->get_grob_property ("edge-widen");
342       edge_width_drul_[LEFT] =  ly_car (ew);
343       edge_width_drul_[RIGHT] = ly_cdr (ew);
344       
345       SCM eleft = ( (bool) p->req_l_drul_[STOP]  ? 
346                     edge_width_drul_[LEFT]  :
347                     gh_double2scm (0));
348       SCM eright = gh_double2scm (0);
349       p->bracket_->set_grob_property ("edge-widen", gh_cons (eleft, eright));
350
351       /* Set this property for 'mixed style' pedals,    Ped._______/\ ,  
352         so the molecule function will shorten the ____ line by the length of the Ped. text.
353       */
354       
355       p->bracket_->set_grob_property ("text-start", 
356                                        pedaltype == ly_symbol2scm ("mixed") ? 
357                                        gh_bool2scm ( (bool) ! p->req_l_drul_[STOP]) :
358                                        gh_bool2scm (false));
359
360       /*
361         Mixed style: Store a pointer to the preceding text for use in
362         calculating the length of the line 
363       */
364       if (p->item_)
365         p->bracket_->set_grob_property ("pedal-text", p->item_->self_scm ());
366       
367       p->bracket_->set_bound (LEFT, unsmob_grob (get_property ("currentMusicalColumn")));
368       Axis_group_interface::add_element (p->line_spanner_, p->bracket_);              
369
370       add_bound_item (p->line_spanner_, p->bracket_->get_bound (LEFT));
371       announce_grob (p->bracket_, p->req_l_drul_[START]->self_scm ());
372
373       if (!p->req_l_drul_[STOP])
374         {
375           spanner_count_ ++;
376           previous_[spanner_count_] = p->line_spanner_; 
377
378           if (spanner_count_ > 1) 
379             // position new pedal spanner below the current one
380             Side_position_interface::add_support (p->line_spanner_, previous_[spanner_count_ - 1]);
381         }
382     }
383
384   p->req_l_drul_[START] = 0;
385   p->req_l_drul_[STOP] = 0;
386 }
387
388 void
389 Piano_pedal_engraver::finalize ()
390 {  
391   for (Pedal_info*p = info_list_; p && p->name_; p ++)
392     {
393       /*
394         suicide?
395        */
396       if (p->line_spanner_
397           && !p->line_spanner_->live())
398         p->line_spanner_ = 0;
399       
400       if (p->line_spanner_)
401         {
402           p->finished_line_spanner_ = p->line_spanner_;
403           typeset_all ();
404         }
405       if (p->bracket_
406           && !p->bracket_->live())
407         p->bracket_ = 0;
408       if (p->bracket_)
409         {
410           p->current_bracket_req_->origin ()->warning (_ ("unterminated pedal bracket"));
411           p->bracket_->suicide ();
412           p->bracket_ = 0;
413         }
414     }
415 }
416
417   
418 void
419 Piano_pedal_engraver::stop_translation_timestep ()
420 {
421   for (Pedal_info*p = info_list_; p && p->name_; p ++)
422     {
423       if (!p->bracket_)
424         {
425           p->finished_line_spanner_ = p->line_spanner_;
426           p->line_spanner_ = 0;
427         }
428     }
429   
430   typeset_all ();
431 }
432
433
434 void
435 Piano_pedal_engraver::typeset_all ()
436 {
437   Item * sustain = 0;
438   for (Pedal_info*p = info_list_; p->name_; p ++)
439     {
440       /*
441         Handle suicide. 
442        */
443       if (p->finished_line_spanner_
444           && !p->finished_line_spanner_->live ())
445         p->finished_line_spanner_ = 0;
446       if (p->finished_bracket_
447           && !p->finished_bracket_->live())
448         p->finished_bracket_ = 0;
449
450
451       if (p->name_ == String ("Sustain"))
452         sustain = p->item_;
453
454       if (p->item_)
455         {
456           /*
457             Hmm.
458           */
459           if (p->name_ != String ("Sustain"))
460             {
461               if (sustain)
462                 {
463                   Side_position_interface::add_support (p->item_,sustain);
464                 }
465             }
466           typeset_grob (p->item_);
467           p->item_ = 0;
468         }
469       
470       if (p->finished_bracket_)
471         {
472           Grob * r = p->finished_bracket_->get_bound (RIGHT);      
473           if (!r)
474             {
475               p->finished_bracket_->set_bound (RIGHT, unsmob_grob (get_property ("currentMusicalColumn")));
476             }
477
478           typeset_grob (p->finished_bracket_);
479           p->finished_bracket_ =0;
480         }
481
482       if (p->finished_line_spanner_)
483         {
484           Side_position_interface::add_staff_support (p->finished_line_spanner_);
485           Grob * l = p->finished_line_spanner_->get_bound (LEFT);
486           Grob * r = p->finished_line_spanner_->get_bound (RIGHT);      
487           if (!r && l)
488             p->finished_line_spanner_->set_bound (RIGHT, l);
489           else if (!l && r)
490             p->finished_line_spanner_->set_bound (LEFT, r);
491           else if (!r && !l)
492             {
493               Grob * cc = unsmob_grob (get_property ("currentMusicalColumn"));
494               Item * ci = dynamic_cast<Item*> (cc);
495               p->finished_line_spanner_->set_bound (RIGHT, ci);
496               p->finished_line_spanner_->set_bound (LEFT, ci);    
497             }
498           typeset_grob (p->finished_line_spanner_);
499           p->finished_line_spanner_ = 0;
500         }
501     }
502 }
503
504 void
505 Piano_pedal_engraver::start_translation_timestep ()
506 {
507   for (Pedal_info*p = info_list_; p->name_; p ++)
508     {
509       p->req_l_drul_[STOP] = 0;
510       p->req_l_drul_[START] = 0;
511     }
512 }
513 ENTER_DESCRIPTION (Piano_pedal_engraver,
514 /* descr */       "Engrave piano pedal symbols and brackets.",
515 /* creats*/       "SostenutoPedal SustainPedal UnaCordaPedal SostenutoPedalLineSpanner SustainPedalLineSpanner UnaCordaPedalLineSpanner",
516 /* accepts */     "pedal-event abort-event",
517 /* acks  */      "note-column-interface",
518 /* reads */       "pedalSostenutoStrings pedalSustainStrings pedalUnaCordaStrings",
519 /* write */       "");