]> 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--2002 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 "musical-request.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   Span_req* start_req_l_;
29   Drul_array<Span_req*> req_l_drul_;
30   Item* item_p_;
31   Spanner* bracket_p_;     // A single portion of a pedal bracket
32   Spanner* finished_bracket_p_;
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   Span_req* 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 create_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_p_ [4];
70   
71   int spanner_count_;
72
73   /*
74     Left and right flare widths of a \___/, as specified by the grob
75     property edge-width.
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_p_[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_p_ = 0;
107       p->bracket_p_ = 0;
108       p->finished_bracket_p_ = 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_l_ = 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_l_))
136         {
137           if (p->line_spanner_)
138             {
139               Side_position_interface::add_support (p->line_spanner_, info.grob_l_);
140               
141               add_bound_item (p->line_spanner_,info.grob_l_);
142             }     
143           if (p->bracket_p_)
144             add_bound_item (p->bracket_p_,info.grob_l_);                  
145           
146         }
147     }
148 }
149
150 bool
151 Piano_pedal_engraver::try_music (Music *m)
152 {
153   if (Span_req * s = dynamic_cast<Span_req*> (m))
154     {
155       for (Pedal_info*p = info_list_; p->name_; p ++)
156         {
157           if (ly_scm2string (s->get_mus_property ("span-type")) == "abort")
158             {
159               p->req_l_drul_[START] = 0;
160               p->req_l_drul_[STOP] = 0;
161               
162               if (p->bracket_p_)
163                 p->bracket_p_->suicide (); /* as in dynamic-engraver.cc */
164               p->bracket_p_ = 0;
165             }  
166           if (scm_equal_p (s->get_mus_property ("span-type"),
167                            ly_str02scm (p->name_))==SCM_BOOL_T)
168             {
169               p->req_l_drul_[s->get_span_dir ()] = s;
170               return true;
171             }
172         }
173     }
174   return false;
175 }
176
177 void
178 Piano_pedal_engraver::create_grobs ()
179 {
180   for (Pedal_info*p = info_list_; p && p->name_; p ++)
181     {
182       if (p->req_l_drul_[STOP] || p->req_l_drul_[START])
183         {
184           if (!p->line_spanner_)
185             {
186               String name  = String (p->name_) + "PedalLineSpanner";
187               p->line_spanner_ = new Spanner (get_property (name.ch_C ()));
188               Side_position_interface::set_axis (p->line_spanner_, Y_AXIS);
189               Music * rq = (p->req_l_drul_[START]  ?  p->req_l_drul_[START]  :  p->req_l_drul_[STOP]);
190               announce_grob (p->line_spanner_, rq->self_scm ());
191             }
192       
193           /* Choose the appropriate grobs to add to the line spanner
194            These can be text items or text-spanners
195           */
196           SCM type = ly_cdr (scm_assoc (ly_symbol2scm ("pedal-type"), 
197                                         get_property ( (String (p->name_) + "Pedal").ch_C ())));
198           if (type == ly_symbol2scm ("text") ||      // Ped.     *Ped.  *
199               type == ly_symbol2scm ("mixed")  )    // Ped. _____/\____|
200             {
201               if (! p->item_p_)
202                 create_text_grobs (p, type);
203             }
204           if (type == ly_symbol2scm ("bracket") ||   // |_________/\____|
205               type == ly_symbol2scm ("mixed")  )
206            {
207              create_bracket_grobs (p, type);
208            }
209         }
210     }
211 }
212
213
214 void
215 Piano_pedal_engraver::create_text_grobs (Pedal_info *p, SCM pedaltype)
216 {
217   SCM b;
218   SCM s = SCM_EOL;
219   SCM strings = get_property ( ("pedal" + String (p->name_) + "Strings").ch_C ());
220
221   if (scm_ilength (strings) >= 3)
222     {
223       if (p->req_l_drul_[STOP] && p->req_l_drul_[START]) 
224         {
225           if (pedaltype == ly_symbol2scm ("text")) 
226             {
227               if (!p->start_req_l_)
228                 {
229                   p->req_l_drul_[STOP]->origin ()->warning (_f ("can't find start of piano pedal: `%s'",  p->name_));
230                 }
231               else
232                 {
233                   s = ly_cadr (strings);
234                 }
235               p->start_req_l_ = p->req_l_drul_[START];
236             }
237         }
238       else if (p->req_l_drul_[STOP])
239         { 
240           if (pedaltype == ly_symbol2scm ("text"))
241             {
242               if (!p->start_req_l_)
243                 {
244                   p->req_l_drul_[STOP]->origin ()->warning (_f ("can't find start of piano pedal: `%s'", p->name_));
245                 }
246               else
247                 {
248                   s = ly_caddr (strings);
249                   spanner_count_ --;
250                 }
251               p->start_req_l_ = 0;
252             }
253         }
254       else if (p->req_l_drul_[START])
255         {
256           p->start_req_l_ = p->req_l_drul_[START];
257           s = ly_car (strings);
258           if (pedaltype == ly_symbol2scm ("text"))
259             {
260               spanner_count_ ++;
261               previous_p_[spanner_count_] = p->line_spanner_;
262               if (spanner_count_ > 1)
263                 // add extra space below the previous already-occuring pedal
264                 Side_position_interface::add_support (p->line_spanner_,
265                                                      previous_p_[spanner_count_ - 1]);
266             }
267         }
268       
269       if (gh_string_p (s))
270         {
271           String propname = String (p->name_) + "Pedal";
272           b = get_property (propname.ch_C ());
273           p->item_p_ = new Item (b);
274           p->item_p_->set_grob_property ("text", s);
275           Axis_group_interface::add_element (p->line_spanner_, p->item_p_);
276           
277           announce_grob (p->item_p_,
278                          (p->req_l_drul_[START]
279                          ? p->req_l_drul_[START]
280                          : p->req_l_drul_[STOP])->self_scm ());
281           
282         }
283       if (pedaltype == ly_symbol2scm ("text")) 
284         {
285           p->req_l_drul_[START] = 0;
286           p->req_l_drul_[STOP] = 0;
287         }
288     }
289 }
290
291 void
292 Piano_pedal_engraver::create_bracket_grobs (Pedal_info *p, SCM pedaltype)
293 {
294
295   if (p->req_l_drul_[STOP])
296     {
297       if (!p->start_req_l_)
298         {
299           p->req_l_drul_[STOP]->origin ()->warning (_f ("can't find start of piano pedal: `%s'", p->name_));
300         }
301       else if (!p->req_l_drul_[START])
302         spanner_count_ -- ;
303
304       assert (!p->finished_bracket_p_ && p->bracket_p_);
305
306       Grob *cmc = unsmob_grob (get_property ("currentMusicalColumn"));
307       p->bracket_p_->set_bound (RIGHT, cmc);
308
309       /*
310         Set properties so that the molecule-creating function will
311         know whether the right edge should be flared ___/
312        */
313       SCM eleft = ly_car (p->bracket_p_->get_grob_property ("edge-width"));
314       SCM eright = (p->req_l_drul_[START]  ? edge_width_drul_[RIGHT] : gh_double2scm (0));
315       p->bracket_p_->set_grob_property ("edge-width", gh_cons (eleft, eright));
316       
317       p->finished_bracket_p_ = p->bracket_p_;
318       p->bracket_p_ = 0;
319       p->current_bracket_req_ = 0;
320       p->start_req_l_ = p->req_l_drul_[START];
321     }
322
323   if (p->req_l_drul_[START])
324     {
325       p->start_req_l_ = p->req_l_drul_[START];
326       p->current_bracket_req_ = p->req_l_drul_[START];
327
328       p->bracket_p_  = new Spanner (get_property ("PianoPedalBracket"));
329
330       /*
331         Set properties so that the molecule-creating function will
332         know whether the left edge should be flared \___
333       */
334
335       SCM ew = p->bracket_p_->get_grob_property ("edge-width");
336       edge_width_drul_[LEFT] =  ly_car (ew);
337       edge_width_drul_[RIGHT] = ly_cdr (ew);
338       
339       SCM eleft = ( (bool) p->req_l_drul_[STOP]  ? 
340                     edge_width_drul_[LEFT]  :
341                     gh_double2scm (0));
342       SCM eright = gh_double2scm (0);
343       p->bracket_p_->set_grob_property ("edge-width", gh_cons (eleft, eright));
344
345       /* Set this property for 'mixed style' pedals,    Ped._______/\ ,  
346         so the molecule function will shorten the ____ line by the length of the Ped. text.
347       */
348       
349       p->bracket_p_->set_grob_property ("text-start", 
350                                        pedaltype == ly_symbol2scm ("mixed") ? 
351                                        gh_bool2scm ( (bool) ! p->req_l_drul_[STOP]) :
352                                        gh_bool2scm (false));
353       if (p->item_p_)
354         p->bracket_p_->set_parent (p->item_p_, Y_AXIS);
355
356       p->bracket_p_->set_bound (LEFT, unsmob_grob (get_property ("currentMusicalColumn")));
357       Axis_group_interface::add_element (p->line_spanner_, p->bracket_p_);            
358
359       add_bound_item (p->line_spanner_, p->bracket_p_->get_bound (LEFT));
360       announce_grob (p->bracket_p_, p->req_l_drul_[START]->self_scm ());
361
362       if (!p->req_l_drul_[STOP])
363         {
364           spanner_count_ ++;
365           previous_p_[spanner_count_] = p->line_spanner_;       
366
367           if (spanner_count_ > 1) 
368             // position new pedal spanner below the current one
369             Side_position_interface::add_support (p->line_spanner_, previous_p_[spanner_count_ - 1]);
370         }
371     }
372
373   p->req_l_drul_[START] = 0;
374   p->req_l_drul_[STOP] = 0;
375 }
376
377 void
378 Piano_pedal_engraver::finalize ()
379 {  
380   for (Pedal_info*p = info_list_; p && p->name_; p ++)
381     {
382       /*
383         suicide?
384        */
385       if (p->line_spanner_
386           && p->line_spanner_->immutable_property_alist_ == SCM_EOL)
387         p->line_spanner_ = 0;
388       
389       if (p->line_spanner_)
390         {
391           p->finished_line_spanner_ = p->line_spanner_;
392           typeset_all ();
393         }
394       if (p->bracket_p_
395           && p->bracket_p_->immutable_property_alist_ == SCM_EOL)
396         p->bracket_p_ = 0;
397       if (p->bracket_p_)
398         {
399           p->current_bracket_req_->origin ()->warning (_ ("unterminated pedal bracket"));
400           p->bracket_p_->suicide ();
401           p->bracket_p_ = 0;
402         }
403     }
404 }
405
406   
407 void
408 Piano_pedal_engraver::stop_translation_timestep ()
409 {
410   for (Pedal_info*p = info_list_; p && p->name_; p ++)
411     {
412       if (!p->bracket_p_)
413         {
414           p->finished_line_spanner_ = p->line_spanner_;
415           p->line_spanner_ = 0;
416         }
417     }
418   
419   typeset_all ();
420 }
421
422
423 void
424 Piano_pedal_engraver::typeset_all ()
425 {
426   Item * sustain = 0;
427   for (Pedal_info*p = info_list_; p->name_; p ++)
428     {
429       /*
430         Handle suicide. 
431        */
432       if (p->finished_line_spanner_
433           && p->finished_line_spanner_->immutable_property_alist_ == SCM_EOL)
434         p->finished_line_spanner_ = 0;
435       if (p->finished_bracket_p_
436           && p->finished_bracket_p_->immutable_property_alist_ == SCM_EOL)
437         p->finished_bracket_p_ = 0;
438
439
440       if (p->name_ == String ("Sustain"))
441         sustain = p->item_p_;
442
443       if (p->item_p_)
444         {
445           /*
446             Hmm.
447           */
448           if (p->name_ != String ("Sustain"))
449             {
450               if (sustain)
451                 {
452                   Side_position_interface::add_support (p->item_p_,sustain);
453                 }
454             }
455           typeset_grob (p->item_p_);
456           p->item_p_ = 0;
457         }
458       
459       if (p->finished_bracket_p_)
460         {
461           
462           Grob * l = p->finished_bracket_p_->get_bound (LEFT);
463           Grob * r = p->finished_bracket_p_->get_bound (RIGHT);      
464           if (!r)
465             {
466               p->finished_bracket_p_->set_bound (RIGHT, unsmob_grob (get_property ("currentMusicalColumn")));
467             }
468
469           typeset_grob (p->finished_bracket_p_);
470           p->finished_bracket_p_ =0;
471         }
472
473       if (p->finished_line_spanner_)
474         {
475           Side_position_interface::add_staff_support (p->finished_line_spanner_);
476           Grob * l = p->finished_line_spanner_->get_bound (LEFT);
477           Grob * r = p->finished_line_spanner_->get_bound (RIGHT);      
478           if (!r && l)
479             p->finished_line_spanner_->set_bound (RIGHT, l);
480           else if (!l && r)
481             p->finished_line_spanner_->set_bound (LEFT, r);
482           else if (!r && !l)
483             {
484               Grob * cc = unsmob_grob (get_property ("currentMusicalColumn"));
485               Item * ci = dynamic_cast<Item*> (cc);
486               p->finished_line_spanner_->set_bound (RIGHT, ci);
487               p->finished_line_spanner_->set_bound (LEFT, ci);    
488             }
489           typeset_grob (p->finished_line_spanner_);
490           p->finished_line_spanner_ = 0;
491         }
492     }
493 }
494
495 void
496 Piano_pedal_engraver::start_translation_timestep ()
497 {
498   for (Pedal_info*p = info_list_; p->name_; p ++)
499     {
500       p->req_l_drul_[STOP] = 0;
501       p->req_l_drul_[START] = 0;
502     }
503 }
504 ENTER_DESCRIPTION (Piano_pedal_engraver,
505 /* descr */       "Engrave piano pedal symbols and brackets.",
506 /* creats*/       "SostenutoPedal SustainPedal UnaCordaPedal SostenutoPedalLineSpanner SustainPedalLineSpanner UnaCordaPedalLineSpanner",
507 /* acks  */       "note-column-interface",
508 /* reads */       "pedalSostenutoStrings pedalSustainStrings pedalUnaCordaStrings",
509 /* write */       "");