]> git.donarmstrong.com Git - lilypond.git/blob - lily/piano-pedal-engraver.cc
''
[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 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_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-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_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::process_acknowledged_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-widen"));
314       SCM eright = (p->req_l_drul_[START]  ? edge_width_drul_[RIGHT] : gh_double2scm (0));
315       p->bracket_p_->set_grob_property ("edge-widen", 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-widen");
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-widen", 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
354       /*
355         Mixed style: Store a pointer to the preceding text for use in
356         calculating the length of the line 
357       */
358       if (p->item_p_)
359         p->bracket_p_->set_grob_property ("pedal-text", p->item_p_->self_scm ());
360       
361       p->bracket_p_->set_bound (LEFT, unsmob_grob (get_property ("currentMusicalColumn")));
362       Axis_group_interface::add_element (p->line_spanner_, p->bracket_p_);            
363
364       add_bound_item (p->line_spanner_, p->bracket_p_->get_bound (LEFT));
365       announce_grob (p->bracket_p_, p->req_l_drul_[START]->self_scm ());
366
367       if (!p->req_l_drul_[STOP])
368         {
369           spanner_count_ ++;
370           previous_p_[spanner_count_] = p->line_spanner_;       
371
372           if (spanner_count_ > 1) 
373             // position new pedal spanner below the current one
374             Side_position_interface::add_support (p->line_spanner_, previous_p_[spanner_count_ - 1]);
375         }
376     }
377
378   p->req_l_drul_[START] = 0;
379   p->req_l_drul_[STOP] = 0;
380 }
381
382 void
383 Piano_pedal_engraver::finalize ()
384 {  
385   for (Pedal_info*p = info_list_; p && p->name_; p ++)
386     {
387       /*
388         suicide?
389        */
390       if (p->line_spanner_
391           && p->line_spanner_->immutable_property_alist_ == SCM_EOL)
392         p->line_spanner_ = 0;
393       
394       if (p->line_spanner_)
395         {
396           p->finished_line_spanner_ = p->line_spanner_;
397           typeset_all ();
398         }
399       if (p->bracket_p_
400           && p->bracket_p_->immutable_property_alist_ == SCM_EOL)
401         p->bracket_p_ = 0;
402       if (p->bracket_p_)
403         {
404           p->current_bracket_req_->origin ()->warning (_ ("unterminated pedal bracket"));
405           p->bracket_p_->suicide ();
406           p->bracket_p_ = 0;
407         }
408     }
409 }
410
411   
412 void
413 Piano_pedal_engraver::stop_translation_timestep ()
414 {
415   for (Pedal_info*p = info_list_; p && p->name_; p ++)
416     {
417       if (!p->bracket_p_)
418         {
419           p->finished_line_spanner_ = p->line_spanner_;
420           p->line_spanner_ = 0;
421         }
422     }
423   
424   typeset_all ();
425 }
426
427
428 void
429 Piano_pedal_engraver::typeset_all ()
430 {
431   Item * sustain = 0;
432   for (Pedal_info*p = info_list_; p->name_; p ++)
433     {
434       /*
435         Handle suicide. 
436        */
437       if (p->finished_line_spanner_
438           && p->finished_line_spanner_->immutable_property_alist_ == SCM_EOL)
439         p->finished_line_spanner_ = 0;
440       if (p->finished_bracket_p_
441           && p->finished_bracket_p_->immutable_property_alist_ == SCM_EOL)
442         p->finished_bracket_p_ = 0;
443
444
445       if (p->name_ == String ("Sustain"))
446         sustain = p->item_p_;
447
448       if (p->item_p_)
449         {
450           /*
451             Hmm.
452           */
453           if (p->name_ != String ("Sustain"))
454             {
455               if (sustain)
456                 {
457                   Side_position_interface::add_support (p->item_p_,sustain);
458                 }
459             }
460           typeset_grob (p->item_p_);
461           p->item_p_ = 0;
462         }
463       
464       if (p->finished_bracket_p_)
465         {
466           Grob * l = p->finished_bracket_p_->get_bound (LEFT);
467           Grob * r = p->finished_bracket_p_->get_bound (RIGHT);      
468           if (!r)
469             {
470               p->finished_bracket_p_->set_bound (RIGHT, unsmob_grob (get_property ("currentMusicalColumn")));
471             }
472
473           typeset_grob (p->finished_bracket_p_);
474           p->finished_bracket_p_ =0;
475         }
476
477       if (p->finished_line_spanner_)
478         {
479           Side_position_interface::add_staff_support (p->finished_line_spanner_);
480           Grob * l = p->finished_line_spanner_->get_bound (LEFT);
481           Grob * r = p->finished_line_spanner_->get_bound (RIGHT);      
482           if (!r && l)
483             p->finished_line_spanner_->set_bound (RIGHT, l);
484           else if (!l && r)
485             p->finished_line_spanner_->set_bound (LEFT, r);
486           else if (!r && !l)
487             {
488               Grob * cc = unsmob_grob (get_property ("currentMusicalColumn"));
489               Item * ci = dynamic_cast<Item*> (cc);
490               p->finished_line_spanner_->set_bound (RIGHT, ci);
491               p->finished_line_spanner_->set_bound (LEFT, ci);    
492             }
493           typeset_grob (p->finished_line_spanner_);
494           p->finished_line_spanner_ = 0;
495         }
496     }
497 }
498
499 void
500 Piano_pedal_engraver::start_translation_timestep ()
501 {
502   for (Pedal_info*p = info_list_; p->name_; p ++)
503     {
504       p->req_l_drul_[STOP] = 0;
505       p->req_l_drul_[START] = 0;
506     }
507 }
508 ENTER_DESCRIPTION (Piano_pedal_engraver,
509 /* descr */       "Engrave piano pedal symbols and brackets.",
510 /* creats*/       "SostenutoPedal SustainPedal UnaCordaPedal SostenutoPedalLineSpanner SustainPedalLineSpanner UnaCordaPedalLineSpanner",
511 /* acks  */       "note-column-interface",
512 /* reads */       "pedalSostenutoStrings pedalSustainStrings pedalUnaCordaStrings",
513 /* write */       "");