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