]> git.donarmstrong.com Git - lilypond.git/blob - lily/piano-pedal-engraver.cc
release: 1.5.43
[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   TODO: support for __| |__ or __| Ped  instead of  ___/\__ for pedal up-down
11  */
12
13 #include "engraver.hh"
14 #include "musical-request.hh"
15 #include "grob.hh"
16 #include "item.hh"
17 #include "lily-guile.hh"
18 #include "side-position-interface.hh"
19 #include "staff-symbol-referencer.hh"
20 #include "item.hh"
21 #include "axis-group-interface.hh"
22 #include "translator-group.hh"
23 #include "directional-element-interface.hh"
24 #include "note-column.hh"
25
26 class Piano_pedal_engraver : public Engraver
27 {
28 public:
29   TRANSLATOR_DECLARATIONS(Piano_pedal_engraver);
30   ~Piano_pedal_engraver ();
31 protected:
32   virtual void initialize ();
33   virtual void finalize ();
34   virtual bool try_music (Music*);
35   virtual void stop_translation_timestep ();
36   virtual void start_translation_timestep ();
37   virtual void acknowledge_grob (Grob_info);
38   virtual void create_grobs ();
39
40 private:
41   struct Pedal_info
42   {
43     char const * name_;
44     Span_req* start_req_l_;
45     Drul_array<Span_req*> req_l_drul_;
46     Item* item_p_;
47     Spanner* bracket_p_;     // A single portion of a pedal bracket
48     Spanner* finished_bracket_p_;
49     Spanner* line_spanner_;  // This grob contains all the pedals of the same type on the same staff
50     Spanner* finished_line_spanner_;
51     Span_req* current_bracket_req_;
52   };
53
54
55   Pedal_info *info_list_;
56
57   Spanner *previous_p_ [4]; // Record a stack of the current pedal spanners, so if more than one pedal
58   int nspanners_i;          // occurs simultaneously then extra space can be added between them.
59   void create_text_grobs (Pedal_info *p, SCM pedaltype);
60   void create_bracket_grobs (Pedal_info *p, SCM pedaltype);
61   void typeset_all();
62 };
63
64
65
66 Piano_pedal_engraver::Piano_pedal_engraver ()
67 {
68   info_list_ = 0;
69 }
70 void
71 Piano_pedal_engraver::initialize ()
72 {
73   info_list_ = new Pedal_info[4];
74   Pedal_info *p = info_list_;
75
76   nspanners_i = 0;
77   for (int i = 0; i < 3; ++i)
78     previous_p_[i] = 0; 
79
80
81   char * names [] = { "Sostenuto", "Sustain", "UnaCorda", 0  };
82   char **np = names ;
83   do
84     {
85       p->name_ = *np;
86       p->item_p_ = 0;
87       p->bracket_p_ = 0;
88       p->finished_bracket_p_ = 0;
89       p->line_spanner_ = 0;
90       p->finished_line_spanner_ = 0;
91       p->current_bracket_req_ = 0;
92       p->req_l_drul_[START] = 0;
93       p->req_l_drul_[STOP] = 0;
94       p->start_req_l_ = 0;
95
96       p++;
97     }
98   while (* (np ++));
99 }
100
101 Piano_pedal_engraver::~Piano_pedal_engraver ()
102 {
103   delete[] info_list_;
104 }
105
106 /*
107    Urg: Code dup
108    I'm a script
109   */
110 void
111 Piano_pedal_engraver::acknowledge_grob (Grob_info info)
112 {
113   for (Pedal_info*p = info_list_; p && p->name_; p ++)
114     {
115       Grob *g  = (Grob *) p->item_p_;
116       int i = 0; 
117       while ( i < 2 )
118         {
119           if (g && p->line_spanner_) 
120             {
121               if (Note_column::has_interface (info.grob_l_))
122                 {
123                   Side_position_interface::add_support (p->line_spanner_, info.grob_l_);
124                   add_bound_item (p->line_spanner_,dynamic_cast<Item*> (info.grob_l_));
125                   
126                   if (Side_position_interface::get_axis (g) == X_AXIS
127                       && !g->get_parent (Y_AXIS))
128                     g->set_parent (info.grob_l_, Y_AXIS);
129                 }
130             }
131           g = (Grob *) p->bracket_p_;
132           ++i;
133         }
134     }
135 }
136
137 bool
138 Piano_pedal_engraver::try_music (Music *m)
139 {
140   if (Span_req * s = dynamic_cast<Span_req*> (m))
141     {
142       for (Pedal_info*p = info_list_; p->name_; p ++)
143         {
144           if (ly_scm2string (s->get_mus_property ("span-type")) == "abort")
145             {
146               p->req_l_drul_[START] = 0;
147               p->req_l_drul_[STOP] = 0;
148               
149               if (p->bracket_p_)
150                 p->bracket_p_->suicide (); /* as in dynamic-engraver.cc */
151               p->bracket_p_ = 0;
152             }  
153           if (scm_equal_p (s->get_mus_property ("span-type"),
154                            ly_str02scm (p->name_))==SCM_BOOL_T)
155             {
156               p->req_l_drul_[s->get_span_dir ()] = s;
157               return true;
158             }
159         }
160     }
161   return false;
162 }
163
164 void
165 Piano_pedal_engraver::create_grobs ()
166 {
167   for (Pedal_info*p = info_list_; p && p->name_; p ++)
168     {
169       if (p->req_l_drul_[STOP] || p->req_l_drul_[START])
170         {
171           if (!p->line_spanner_)
172             {
173               p->line_spanner_ = new Spanner (get_property ( ( String (p->name_) + "PedalLineSpanner").ch_C() ));
174               Side_position_interface::set_axis (p->line_spanner_, Y_AXIS);
175               Axis_group_interface::set_interface (p->line_spanner_);
176               Axis_group_interface::set_axes (p->line_spanner_, Y_AXIS, 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       p->bracket_p_->set_interface (ly_symbol2scm ("piano-pedal-interface"));
311
312       // Set a property so that the molecule-creating function will know whether the left edge should be flared \___
313       p->bracket_p_->set_grob_property("left-widen", gh_bool2scm((bool) p->req_l_drul_[STOP]) );
314
315       // Set this property for 'mixed style' pedals,    Ped._______/\ ,  
316       // so the molecule function will shorten the ____ line by the length of the Ped. text. 
317       p->bracket_p_->set_grob_property("text-start", 
318                                        pedaltype == ly_symbol2scm("mixed") ? 
319                                        gh_bool2scm((bool) ! p->req_l_drul_[STOP]) :
320                                        gh_bool2scm(false));
321       if (p->item_p_)
322         p->bracket_p_->set_parent (p->item_p_, Y_AXIS);
323       
324       p->bracket_p_->set_bound (LEFT, unsmob_grob (get_property ("currentMusicalColumn")));
325       Axis_group_interface::add_element (p->line_spanner_, p->bracket_p_);            
326
327       add_bound_item (p->line_spanner_, p->bracket_p_->get_bound (LEFT));
328       announce_grob (p->bracket_p_, p->req_l_drul_[START]->self_scm());
329
330       if (!p->req_l_drul_[STOP]) {
331
332         nspanners_i ++;
333         previous_p_[nspanners_i] = p->line_spanner_;    
334
335         if (nspanners_i > 1) 
336           // position new pedal spanner below the current one
337           Side_position_interface::add_support(p->line_spanner_, previous_p_[nspanners_i - 1]);      
338         
339       }
340     }
341
342   p->req_l_drul_[START] = 0;
343   p->req_l_drul_[STOP] = 0;
344 }
345
346 void
347 Piano_pedal_engraver::finalize ()
348 {  
349   for (Pedal_info*p = info_list_; p && p->name_; p ++)
350     {
351       if (p->line_spanner_
352           && p->line_spanner_->immutable_property_alist_ == SCM_EOL)
353         p->line_spanner_ = 0;
354       if (p->line_spanner_)
355         {
356           p->finished_line_spanner_ = p->line_spanner_;
357           typeset_all ();
358         }
359       if (p->bracket_p_
360           && p->bracket_p_->immutable_property_alist_ == SCM_EOL)
361         p->bracket_p_ = 0;
362       if (p->bracket_p_)
363         {
364           p->current_bracket_req_->origin ()->warning (_ ("unterminated pedal bracket"));
365           p->bracket_p_->suicide ();
366           p->bracket_p_ = 0;
367         }
368     }
369 }
370
371   
372 void
373 Piano_pedal_engraver::stop_translation_timestep ()
374 {
375   for (Pedal_info*p = info_list_; p && p->name_; p ++)
376     {
377       p->finished_line_spanner_ = p->line_spanner_;
378     }
379   typeset_all ();
380 }
381
382
383 void
384 Piano_pedal_engraver::typeset_all ()
385 {
386
387
388   Item * sustain = 0;
389   for (Pedal_info*p = info_list_; p->name_; p ++)
390     {
391       if (p->finished_line_spanner_
392           && p->finished_line_spanner_->immutable_property_alist_ == SCM_EOL)
393         p->finished_line_spanner_ = 0;
394       if (p->finished_bracket_p_
395           && p->finished_bracket_p_->immutable_property_alist_ == SCM_EOL)
396         p->finished_bracket_p_ = 0;
397       if (p->name_ == String ("Sustain"))
398         sustain = p->item_p_;
399
400       if (p->item_p_)
401         {
402           /*
403             Hmm.
404           */
405           if (p->name_ != String ("Sustain"))
406             {
407               if (sustain)
408                 {
409                   Side_position_interface::add_support (p->item_p_,sustain);
410                 }
411             }
412           typeset_grob (p->item_p_);
413           p->item_p_ = 0;
414         }
415       
416       if (p->finished_bracket_p_)
417         {
418           if (!p->finished_bracket_p_->get_bound (RIGHT))
419             {
420               p->finished_bracket_p_->set_bound (RIGHT, unsmob_grob (get_property ("currentMusicalColumn")));
421
422               if (p->finished_line_spanner_)
423                 add_bound_item (p->finished_line_spanner_,
424                                 p->finished_bracket_p_->get_bound (RIGHT));
425             }
426           typeset_grob (p->finished_bracket_p_);
427           p->finished_bracket_p_ =0;
428         }
429
430       if (p->finished_line_spanner_)
431         {
432           Side_position_interface::add_staff_support (p->finished_line_spanner_);
433           Grob * l = p->finished_line_spanner_->get_bound (LEFT );
434           Grob * r = p->finished_line_spanner_->get_bound (RIGHT);      
435           if (!r && l)
436             p->finished_line_spanner_->set_bound (RIGHT, l);
437           else if (!l && r)
438             p->finished_line_spanner_->set_bound (LEFT, r);
439           else if (!r && !l)
440             {
441               Grob * cc = unsmob_grob (get_property ("currentMusicalColumn"));
442               Item * ci = dynamic_cast<Item*>(cc);
443               p->finished_line_spanner_->set_bound (RIGHT, ci);
444               p->finished_line_spanner_->set_bound (LEFT, ci);    
445             }
446           typeset_grob (p->finished_line_spanner_);
447           p->finished_line_spanner_ = 0;
448         }
449     }
450 }
451
452 void
453 Piano_pedal_engraver::start_translation_timestep ()
454 {
455   for (Pedal_info*p = info_list_; p->name_; p ++)
456     {
457       p->req_l_drul_[STOP] = 0;
458       p->req_l_drul_[START] = 0;
459     }
460 }
461 ENTER_DESCRIPTION(Piano_pedal_engraver,
462 /* descr */       "Engrave piano pedal symbols and brackets.",
463 /* creats*/       "SostenutoPedal SustainPedal UnaCordaPedal SostenutoPedalLineSpanner SustainPedalLineSpanner UnaCordaPedalLineSpanner",
464 /* acks  */       "note-column-interface",
465 /* reads */       "pedalSostenutoStrings pedalSustainStrings pedalUnaCordaStrings",
466 /* write */       "");