]> git.donarmstrong.com Git - lilypond.git/blob - lily/piano-pedal-engraver.cc
release: 1.5.47
[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   TODO: support for __| |__ or __| Ped  instead of  ___/\__ for pedal up-down
12
13 */
14
15 #include "engraver.hh"
16 #include "musical-request.hh"
17 #include "grob.hh"
18 #include "item.hh"
19 #include "lily-guile.hh"
20 #include "side-position-interface.hh"
21 #include "staff-symbol-referencer.hh"
22 #include "item.hh"
23 #include "axis-group-interface.hh"
24 #include "translator-group.hh"
25 #include "directional-element-interface.hh"
26 #include "note-column.hh"
27
28 class Piano_pedal_engraver : public Engraver
29 {
30 public:
31   TRANSLATOR_DECLARATIONS(Piano_pedal_engraver);
32   ~Piano_pedal_engraver ();
33 protected:
34   virtual void initialize ();
35   virtual void finalize ();
36   virtual bool try_music (Music*);
37   virtual void stop_translation_timestep ();
38   virtual void start_translation_timestep ();
39   virtual void acknowledge_grob (Grob_info);
40   virtual void create_grobs ();
41
42 private:
43   struct Pedal_info
44   {
45     char const * name_;
46     Span_req* start_req_l_;
47     Drul_array<Span_req*> req_l_drul_;
48     Item* item_p_;
49     Spanner* bracket_p_;     // A single portion of a pedal bracket
50     Spanner* finished_bracket_p_;
51     Spanner* line_spanner_;  // This grob contains all the pedals of the same type on the same staff
52     Spanner* finished_line_spanner_;
53     Span_req* current_bracket_req_;
54   };
55
56
57   Pedal_info *info_list_;
58
59   Spanner *previous_p_ [4]; // Record a stack of the current pedal spanners, so if more than one pedal
60   int nspanners_i;          // occurs simultaneously then extra space can be added between them.
61   void create_text_grobs (Pedal_info *p, SCM pedaltype);
62   void create_bracket_grobs (Pedal_info *p, SCM pedaltype);
63   void typeset_all();
64 };
65
66
67
68 Piano_pedal_engraver::Piano_pedal_engraver ()
69 {
70   info_list_ = 0;
71 }
72 void
73 Piano_pedal_engraver::initialize ()
74 {
75   info_list_ = new Pedal_info[4];
76   Pedal_info *p = info_list_;
77
78   nspanners_i = 0;
79   for (int i = 0; i < 3; ++i)
80     previous_p_[i] = 0; 
81
82
83   char * names [] = { "Sostenuto", "Sustain", "UnaCorda", 0  };
84   char **np = names ;
85   do
86     {
87       p->name_ = *np;
88       p->item_p_ = 0;
89       p->bracket_p_ = 0;
90       p->finished_bracket_p_ = 0;
91       p->line_spanner_ = 0;
92       p->finished_line_spanner_ = 0;
93       p->current_bracket_req_ = 0;
94       p->req_l_drul_[START] = 0;
95       p->req_l_drul_[STOP] = 0;
96       p->start_req_l_ = 0;
97
98       p++;
99     }
100   while (* (np ++));
101 }
102
103 Piano_pedal_engraver::~Piano_pedal_engraver ()
104 {
105   delete[] info_list_;
106 }
107
108 /*
109    Urg: Code dup
110    I'm a script
111   */
112 void
113 Piano_pedal_engraver::acknowledge_grob (Grob_info info)
114 {
115   for (Pedal_info*p = info_list_; p && p->name_; p ++)
116     {
117       Grob *g  = (Grob *) p->item_p_;
118       int i = 0; 
119       while ( i < 2 )
120         {
121           if (g && p->line_spanner_) 
122             {
123               if (Note_column::has_interface (info.grob_l_))
124                 {
125                   Side_position_interface::add_support (p->line_spanner_, info.grob_l_);
126                   add_bound_item (p->line_spanner_,dynamic_cast<Item*> (info.grob_l_));
127                   
128                   if (Side_position_interface::get_axis (g) == X_AXIS
129                       && !g->get_parent (Y_AXIS))
130                     g->set_parent (info.grob_l_, Y_AXIS);
131                 }
132             }
133           g = (Grob *) p->bracket_p_;
134           ++i;
135         }
136     }
137 }
138
139 bool
140 Piano_pedal_engraver::try_music (Music *m)
141 {
142   if (Span_req * s = dynamic_cast<Span_req*> (m))
143     {
144       for (Pedal_info*p = info_list_; p->name_; p ++)
145         {
146           if (ly_scm2string (s->get_mus_property ("span-type")) == "abort")
147             {
148               p->req_l_drul_[START] = 0;
149               p->req_l_drul_[STOP] = 0;
150               
151               if (p->bracket_p_)
152                 p->bracket_p_->suicide (); /* as in dynamic-engraver.cc */
153               p->bracket_p_ = 0;
154             }  
155           if (scm_equal_p (s->get_mus_property ("span-type"),
156                            ly_str02scm (p->name_))==SCM_BOOL_T)
157             {
158               p->req_l_drul_[s->get_span_dir ()] = s;
159               return true;
160             }
161         }
162     }
163   return false;
164 }
165
166 void
167 Piano_pedal_engraver::create_grobs ()
168 {
169   for (Pedal_info*p = info_list_; p && p->name_; p ++)
170     {
171       if (p->req_l_drul_[STOP] || p->req_l_drul_[START])
172         {
173           if (!p->line_spanner_)
174             {
175               p->line_spanner_ = new Spanner (get_property ( ( String (p->name_) + "PedalLineSpanner").ch_C() ));
176               Side_position_interface::set_axis (p->line_spanner_, Y_AXIS);
177               Music * rq = (p->req_l_drul_[START]  ?  p->req_l_drul_[START]  :  p->req_l_drul_[STOP]);
178               announce_grob (p->line_spanner_, rq->self_scm ());
179             }
180       
181           // Choose the appropriate grobs to add to the line spanner
182           // These can be text items or text-spanners
183           
184           SCM type = ly_cdr (scm_assoc (ly_symbol2scm("pedal-type"), 
185                                         get_property( ( String (p->name_) + "Pedal").ch_C () )));
186           if (type == ly_symbol2scm("text") ||      // Ped.     *Ped.  *
187               type == ly_symbol2scm("mixed")   )    // Ped. _____/\____|
188             if (! p->item_p_)
189               create_text_grobs(p, type);
190
191           if (type == ly_symbol2scm("bracket") ||   // |_________/\____|
192               type == ly_symbol2scm("mixed")   )
193             create_bracket_grobs(p, type);
194         }
195       
196     }
197 }
198
199
200 void
201 Piano_pedal_engraver::create_text_grobs (Pedal_info *p, SCM pedaltype)
202 {
203   SCM b;
204   SCM s = SCM_EOL;
205   SCM strings = get_property (("pedal" + String (p->name_) + "Strings").ch_C ());
206
207   if (! (scm_ilength (strings) < 3))
208     {
209       if (p->req_l_drul_[STOP] && p->req_l_drul_[START]) 
210         {
211           if (pedaltype == ly_symbol2scm("text")) 
212             {
213               if (!p->start_req_l_)
214                 {
215                   p->req_l_drul_[STOP]->origin ()->warning (_f ("can't find start of piano pedal: `%s'",  p->name_));
216                 }
217               else
218                 {
219                   s = ly_cadr (strings);
220                 }
221               p->start_req_l_ = p->req_l_drul_[START];
222             }
223         }
224       
225       else if (p->req_l_drul_[STOP])
226         { 
227           if (pedaltype == ly_symbol2scm("text"))
228             {
229               if (!p->start_req_l_)
230                 {
231                   p->req_l_drul_[STOP]->origin ()->warning (_f ("can't find start of piano pedal: `%s'", p->name_));
232                 }
233               else
234                 {
235                   s = ly_caddr (strings);
236                   nspanners_i --;
237                 }
238               p->start_req_l_ = 0;
239             }
240         }
241       
242       else if ( p->req_l_drul_[START] )
243         {
244           p->start_req_l_ = p->req_l_drul_[START];
245           s = ly_car (strings);
246           if (pedaltype == ly_symbol2scm("text")) {
247             nspanners_i ++;
248             previous_p_[nspanners_i] = p->line_spanner_;
249             if  (nspanners_i > 1)
250               // add extra space below the previous already-occuring pedal
251               Side_position_interface::add_support(p->line_spanner_, previous_p_[nspanners_i - 1]);
252           }
253         }
254       
255       if (gh_string_p (s))
256         {
257           String propname = String (p->name_) + "Pedal";
258           b = get_property (propname.ch_C ());
259           p->item_p_ = new Item (b);
260           p->item_p_->set_grob_property ("text", s);
261           Axis_group_interface::add_element (p->line_spanner_, p->item_p_);
262           
263           announce_grob (p->item_p_,
264                          (p->req_l_drul_[START]
265                          ? p->req_l_drul_[START]
266                          : p->req_l_drul_[STOP])->self_scm() );
267           
268         }
269       if (pedaltype == ly_symbol2scm("text")) 
270         {
271           p->req_l_drul_[START] = 0;
272           p->req_l_drul_[STOP] = 0;
273         }
274     }
275 }
276
277 void
278 Piano_pedal_engraver::create_bracket_grobs (Pedal_info *p, SCM pedaltype)
279 {
280
281   if (p->req_l_drul_[STOP])
282     {
283       if (!p->start_req_l_)
284         {
285           p->req_l_drul_[STOP]->origin ()->warning (_f ("can't find start of piano pedal: `%s'", p->name_));
286         }
287       else if (!p->req_l_drul_[START])
288         nspanners_i -- ;
289
290       assert (!p->finished_bracket_p_ && p->bracket_p_);
291
292       p->bracket_p_->set_bound (RIGHT, unsmob_grob(get_property ("currentMusicalColumn")));
293
294       // Set a property so that the molecule-creating function will know whether the right edge should be flared ___/
295       p->bracket_p_->set_grob_property("right-widen", gh_bool2scm((bool) p->req_l_drul_[START]) );
296       add_bound_item (p->line_spanner_, p->bracket_p_->get_bound (RIGHT));        
297
298       p->finished_bracket_p_ = p->bracket_p_;
299       p->bracket_p_ = 0;
300       p->current_bracket_req_ = 0;
301       p->start_req_l_ = p->req_l_drul_[START];
302     }
303
304   if (p->req_l_drul_[START])
305     {
306       p->start_req_l_ = p->req_l_drul_[START];
307       p->current_bracket_req_ = p->req_l_drul_[START];
308
309       p->bracket_p_  = new Spanner (get_property ("PianoPedalBracket"));
310
311       // Set a property so that the molecule-creating function will know whether the left edge should be flared \___
312       p->bracket_p_->set_grob_property("left-widen", gh_bool2scm((bool) p->req_l_drul_[STOP]) );
313
314       // Set this property for 'mixed style' pedals,    Ped._______/\ ,  
315       // so the molecule function will shorten the ____ line by the length of the Ped. text. 
316       p->bracket_p_->set_grob_property("text-start", 
317                                        pedaltype == ly_symbol2scm("mixed") ? 
318                                        gh_bool2scm((bool) ! p->req_l_drul_[STOP]) :
319                                        gh_bool2scm(false));
320       if (p->item_p_)
321         p->bracket_p_->set_parent (p->item_p_, Y_AXIS);
322       
323       p->bracket_p_->set_bound (LEFT, unsmob_grob (get_property ("currentMusicalColumn")));
324       Axis_group_interface::add_element (p->line_spanner_, p->bracket_p_);            
325
326       add_bound_item (p->line_spanner_, p->bracket_p_->get_bound (LEFT));
327       announce_grob (p->bracket_p_, p->req_l_drul_[START]->self_scm());
328
329       if (!p->req_l_drul_[STOP]) {
330
331         nspanners_i ++;
332         previous_p_[nspanners_i] = p->line_spanner_;    
333
334         if (nspanners_i > 1) 
335           // position new pedal spanner below the current one
336           Side_position_interface::add_support(p->line_spanner_, previous_p_[nspanners_i - 1]);      
337         
338       }
339     }
340
341   p->req_l_drul_[START] = 0;
342   p->req_l_drul_[STOP] = 0;
343 }
344
345 void
346 Piano_pedal_engraver::finalize ()
347 {  
348   for (Pedal_info*p = info_list_; p && p->name_; p ++)
349     {
350       if (p->line_spanner_
351           && p->line_spanner_->immutable_property_alist_ == SCM_EOL)
352         p->line_spanner_ = 0;
353       if (p->line_spanner_)
354         {
355           p->finished_line_spanner_ = p->line_spanner_;
356           typeset_all ();
357         }
358       if (p->bracket_p_
359           && p->bracket_p_->immutable_property_alist_ == SCM_EOL)
360         p->bracket_p_ = 0;
361       if (p->bracket_p_)
362         {
363           p->current_bracket_req_->origin ()->warning (_ ("unterminated pedal bracket"));
364           p->bracket_p_->suicide ();
365           p->bracket_p_ = 0;
366         }
367     }
368 }
369
370   
371 void
372 Piano_pedal_engraver::stop_translation_timestep ()
373 {
374   for (Pedal_info*p = info_list_; p && p->name_; p ++)
375     {
376       p->finished_line_spanner_ = p->line_spanner_;
377     }
378   typeset_all ();
379 }
380
381
382 void
383 Piano_pedal_engraver::typeset_all ()
384 {
385
386
387   Item * sustain = 0;
388   for (Pedal_info*p = info_list_; p->name_; p ++)
389     {
390       if (p->finished_line_spanner_
391           && p->finished_line_spanner_->immutable_property_alist_ == SCM_EOL)
392         p->finished_line_spanner_ = 0;
393       if (p->finished_bracket_p_
394           && p->finished_bracket_p_->immutable_property_alist_ == SCM_EOL)
395         p->finished_bracket_p_ = 0;
396       if (p->name_ == String ("Sustain"))
397         sustain = p->item_p_;
398
399       if (p->item_p_)
400         {
401           /*
402             Hmm.
403           */
404           if (p->name_ != String ("Sustain"))
405             {
406               if (sustain)
407                 {
408                   Side_position_interface::add_support (p->item_p_,sustain);
409                 }
410             }
411           typeset_grob (p->item_p_);
412           p->item_p_ = 0;
413         }
414       
415       if (p->finished_bracket_p_)
416         {
417           if (!p->finished_bracket_p_->get_bound (RIGHT))
418             {
419               p->finished_bracket_p_->set_bound (RIGHT, unsmob_grob (get_property ("currentMusicalColumn")));
420
421               if (p->finished_line_spanner_)
422                 add_bound_item (p->finished_line_spanner_,
423                                 p->finished_bracket_p_->get_bound (RIGHT));
424             }
425           typeset_grob (p->finished_bracket_p_);
426           p->finished_bracket_p_ =0;
427         }
428
429       if (p->finished_line_spanner_)
430         {
431           Side_position_interface::add_staff_support (p->finished_line_spanner_);
432           Grob * l = p->finished_line_spanner_->get_bound (LEFT );
433           Grob * r = p->finished_line_spanner_->get_bound (RIGHT);      
434           if (!r && l)
435             p->finished_line_spanner_->set_bound (RIGHT, l);
436           else if (!l && r)
437             p->finished_line_spanner_->set_bound (LEFT, r);
438           else if (!r && !l)
439             {
440               Grob * cc = unsmob_grob (get_property ("currentMusicalColumn"));
441               Item * ci = dynamic_cast<Item*>(cc);
442               p->finished_line_spanner_->set_bound (RIGHT, ci);
443               p->finished_line_spanner_->set_bound (LEFT, ci);    
444             }
445           typeset_grob (p->finished_line_spanner_);
446           p->finished_line_spanner_ = 0;
447         }
448     }
449 }
450
451 void
452 Piano_pedal_engraver::start_translation_timestep ()
453 {
454   for (Pedal_info*p = info_list_; p->name_; p ++)
455     {
456       p->req_l_drul_[STOP] = 0;
457       p->req_l_drul_[START] = 0;
458     }
459 }
460 ENTER_DESCRIPTION(Piano_pedal_engraver,
461 /* descr */       "Engrave piano pedal symbols and brackets.",
462 /* creats*/       "SostenutoPedal SustainPedal UnaCordaPedal SostenutoPedalLineSpanner SustainPedalLineSpanner UnaCordaPedalLineSpanner",
463 /* acks  */       "note-column-interface",
464 /* reads */       "pedalSostenutoStrings pedalSustainStrings pedalUnaCordaStrings",
465 /* write */       "");