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