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