]> git.donarmstrong.com Git - lilypond.git/blob - lily/piano-pedal-engraver.cc
''
[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 class Piano_pedal_engraver : public Engraver
26 {
27 public:
28   TRANSLATOR_DECLARATIONS(Piano_pedal_engraver);
29   ~Piano_pedal_engraver ();
30 protected:
31   virtual void initialize ();
32   virtual void finalize ();
33   virtual bool try_music (Music*);
34   virtual void stop_translation_timestep ();
35   virtual void start_translation_timestep ();
36   virtual void acknowledge_grob (Grob_info);
37   virtual void create_grobs ();
38
39 private:
40   struct Pedal_info
41   {
42     char const * name_;
43     Span_req* start_req_l_;
44     Drul_array<Span_req*> req_l_drul_;
45     Item* item_p_;
46     Spanner* bracket_p_;     // A single portion of a pedal bracket
47     Spanner* finished_bracket_p_;
48     Spanner* line_spanner_;  // This grob contains all the pedals of the same type on the same staff
49     Spanner* finished_line_spanner_;
50     Span_req* current_bracket_req_;
51   };
52
53
54   Pedal_info *info_list_;
55
56   Spanner *previous_p_ [4]; // Record a stack of the current pedal spanners, so if more than one pedal
57   int nspanners_i;          // occurs simultaneously then extra space can be added between them.
58   Drul_array<SCM> edge_width_drul_; // Left and right flare widths of a \___/, as specified by the grob property edge-width.
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               Music * rq = (p->req_l_drul_[START]  ?  p->req_l_drul_[START]  :  p->req_l_drul_[STOP]);
176               announce_grob (p->line_spanner_, rq->self_scm ());
177             }
178       
179           // Choose the appropriate grobs to add to the line spanner
180           // These can be text items or text-spanners
181           
182           SCM type = ly_cdr (scm_assoc (ly_symbol2scm("pedal-type"), 
183                                         get_property( ( String (p->name_) + "Pedal").ch_C () )));
184           if (type == ly_symbol2scm("text") ||      // Ped.     *Ped.  *
185               type == ly_symbol2scm("mixed")   )    // Ped. _____/\____|
186             if (! p->item_p_)
187               create_text_grobs(p, type);
188
189           if (type == ly_symbol2scm("bracket") ||   // |_________/\____|
190               type == ly_symbol2scm("mixed")   )
191             create_bracket_grobs(p, type);
192         }
193       
194     }
195 }
196
197
198 void
199 Piano_pedal_engraver::create_text_grobs (Pedal_info *p, SCM pedaltype)
200 {
201   SCM b;
202   SCM s = SCM_EOL;
203   SCM strings = get_property (("pedal" + String (p->name_) + "Strings").ch_C ());
204
205   if (! (scm_ilength (strings) < 3))
206     {
207       if (p->req_l_drul_[STOP] && p->req_l_drul_[START]) 
208         {
209           if (pedaltype == ly_symbol2scm("text")) 
210             {
211               if (!p->start_req_l_)
212                 {
213                   p->req_l_drul_[STOP]->origin ()->warning (_f ("can't find start of piano pedal: `%s'",  p->name_));
214                 }
215               else
216                 {
217                   s = ly_cadr (strings);
218                 }
219               p->start_req_l_ = p->req_l_drul_[START];
220             }
221         }
222       
223       else if (p->req_l_drul_[STOP])
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_caddr (strings);
234                   nspanners_i --;
235                 }
236               p->start_req_l_ = 0;
237             }
238         }
239       
240       else if ( p->req_l_drul_[START] )
241         {
242           p->start_req_l_ = p->req_l_drul_[START];
243           s = ly_car (strings);
244           if (pedaltype == ly_symbol2scm("text")) {
245             nspanners_i ++;
246             previous_p_[nspanners_i] = p->line_spanner_;
247             if  (nspanners_i > 1)
248               // add extra space below the previous already-occuring pedal
249               Side_position_interface::add_support(p->line_spanner_, previous_p_[nspanners_i - 1]);
250           }
251         }
252       
253       if (gh_string_p (s))
254         {
255           String propname = String (p->name_) + "Pedal";
256           b = get_property (propname.ch_C ());
257           p->item_p_ = new Item (b);
258           p->item_p_->set_grob_property ("text", s);
259           Axis_group_interface::add_element (p->line_spanner_, p->item_p_);
260           
261           announce_grob (p->item_p_,
262                          (p->req_l_drul_[START]
263                          ? p->req_l_drul_[START]
264                          : p->req_l_drul_[STOP])->self_scm() );
265           
266         }
267       if (pedaltype == ly_symbol2scm("text")) 
268         {
269           p->req_l_drul_[START] = 0;
270           p->req_l_drul_[STOP] = 0;
271         }
272     }
273 }
274
275 void
276 Piano_pedal_engraver::create_bracket_grobs (Pedal_info *p, SCM pedaltype)
277 {
278
279   if (p->req_l_drul_[STOP])
280     {
281       if (!p->start_req_l_)
282         {
283           p->req_l_drul_[STOP]->origin ()->warning (_f ("can't find start of piano pedal: `%s'", p->name_));
284         }
285       else if (!p->req_l_drul_[START])
286         nspanners_i -- ;
287
288       assert (!p->finished_bracket_p_ && p->bracket_p_);
289
290       p->bracket_p_->set_bound (RIGHT, unsmob_grob(get_property ("currentMusicalColumn")));
291
292       // Set properties so that the molecule-creating function will know whether the right edge should be flared ___/
293       SCM eleft = ly_car ( p->bracket_p_->get_grob_property("edge-width") );
294       SCM eright = ( (bool) p->req_l_drul_[START]  ?
295                      edge_width_drul_[RIGHT] : 
296                      gh_double2scm(0) );
297       p->bracket_p_->set_grob_property("edge-width", gh_cons ( eleft, eright ) );
298       add_bound_item (p->line_spanner_, p->bracket_p_->get_bound (RIGHT));        
299       
300       p->finished_bracket_p_ = p->bracket_p_;
301       p->bracket_p_ = 0;
302       p->current_bracket_req_ = 0;
303       p->start_req_l_ = p->req_l_drul_[START];
304     }
305
306   if (p->req_l_drul_[START])
307     {
308       p->start_req_l_ = p->req_l_drul_[START];
309       p->current_bracket_req_ = p->req_l_drul_[START];
310
311       p->bracket_p_  = new Spanner (get_property ("PianoPedalBracket"));
312
313       // Set properties so that the molecule-creating function will know whether the left edge should be flared \___
314       edge_width_drul_[LEFT] =  ly_car ( p->bracket_p_->get_grob_property("edge-width") );
315       edge_width_drul_[RIGHT] = ly_cdr ( p->bracket_p_->get_grob_property("edge-width") );
316       SCM eleft = ( (bool) p->req_l_drul_[STOP]  ? 
317                     edge_width_drul_[LEFT]  :
318                     gh_double2scm(0) );
319       SCM eright = gh_double2scm(0);
320       p->bracket_p_->set_grob_property("edge-width", gh_cons ( eleft, eright ) );
321
322       // Set this property for 'mixed style' pedals,    Ped._______/\ ,  
323       // so the molecule function will shorten the ____ line by the length of the Ped. text. 
324       p->bracket_p_->set_grob_property("text-start", 
325                                        pedaltype == ly_symbol2scm("mixed") ? 
326                                        gh_bool2scm((bool) ! p->req_l_drul_[STOP]) :
327                                        gh_bool2scm(false));
328       if (p->item_p_)
329         p->bracket_p_->set_parent (p->item_p_, Y_AXIS);
330
331       p->bracket_p_->set_bound (LEFT, unsmob_grob (get_property ("currentMusicalColumn")));
332       Axis_group_interface::add_element (p->line_spanner_, p->bracket_p_);            
333
334       add_bound_item (p->line_spanner_, p->bracket_p_->get_bound (LEFT));
335       announce_grob (p->bracket_p_, p->req_l_drul_[START]->self_scm());
336
337       if (!p->req_l_drul_[STOP]) {
338
339         nspanners_i ++;
340         previous_p_[nspanners_i] = p->line_spanner_;    
341
342         if (nspanners_i > 1) 
343           // position new pedal spanner below the current one
344           Side_position_interface::add_support(p->line_spanner_, previous_p_[nspanners_i - 1]);      
345         
346       }
347     }
348
349   p->req_l_drul_[START] = 0;
350   p->req_l_drul_[STOP] = 0;
351 }
352
353 void
354 Piano_pedal_engraver::finalize ()
355 {  
356   for (Pedal_info*p = info_list_; p && p->name_; p ++)
357     {
358       if (p->line_spanner_
359           && p->line_spanner_->immutable_property_alist_ == SCM_EOL)
360         p->line_spanner_ = 0;
361       if (p->line_spanner_)
362         {
363           p->finished_line_spanner_ = p->line_spanner_;
364           typeset_all ();
365         }
366       if (p->bracket_p_
367           && p->bracket_p_->immutable_property_alist_ == SCM_EOL)
368         p->bracket_p_ = 0;
369       if (p->bracket_p_)
370         {
371           p->current_bracket_req_->origin ()->warning (_ ("unterminated pedal bracket"));
372           p->bracket_p_->suicide ();
373           p->bracket_p_ = 0;
374         }
375     }
376 }
377
378   
379 void
380 Piano_pedal_engraver::stop_translation_timestep ()
381 {
382   for (Pedal_info*p = info_list_; p && p->name_; p ++)
383     {
384       p->finished_line_spanner_ = p->line_spanner_;
385     }
386   typeset_all ();
387 }
388
389
390 void
391 Piano_pedal_engraver::typeset_all ()
392 {
393
394
395   Item * sustain = 0;
396   for (Pedal_info*p = info_list_; p->name_; p ++)
397     {
398       if (p->finished_line_spanner_
399           && p->finished_line_spanner_->immutable_property_alist_ == SCM_EOL)
400         p->finished_line_spanner_ = 0;
401       if (p->finished_bracket_p_
402           && p->finished_bracket_p_->immutable_property_alist_ == SCM_EOL)
403         p->finished_bracket_p_ = 0;
404       if (p->name_ == String ("Sustain"))
405         sustain = p->item_p_;
406
407       if (p->item_p_)
408         {
409           /*
410             Hmm.
411           */
412           if (p->name_ != String ("Sustain"))
413             {
414               if (sustain)
415                 {
416                   Side_position_interface::add_support (p->item_p_,sustain);
417                 }
418             }
419           typeset_grob (p->item_p_);
420           p->item_p_ = 0;
421         }
422       
423       if (p->finished_bracket_p_)
424         {
425           if (!p->finished_bracket_p_->get_bound (RIGHT))
426             {
427               p->finished_bracket_p_->set_bound (RIGHT, unsmob_grob (get_property ("currentMusicalColumn")));
428
429               if (p->finished_line_spanner_)
430                 add_bound_item (p->finished_line_spanner_,
431                                 p->finished_bracket_p_->get_bound (RIGHT));
432             }
433           typeset_grob (p->finished_bracket_p_);
434           p->finished_bracket_p_ =0;
435         }
436
437       if (p->finished_line_spanner_)
438         {
439           Side_position_interface::add_staff_support (p->finished_line_spanner_);
440           Grob * l = p->finished_line_spanner_->get_bound (LEFT );
441           Grob * r = p->finished_line_spanner_->get_bound (RIGHT);      
442           if (!r && l)
443             p->finished_line_spanner_->set_bound (RIGHT, l);
444           else if (!l && r)
445             p->finished_line_spanner_->set_bound (LEFT, r);
446           else if (!r && !l)
447             {
448               Grob * cc = unsmob_grob (get_property ("currentMusicalColumn"));
449               Item * ci = dynamic_cast<Item*>(cc);
450               p->finished_line_spanner_->set_bound (RIGHT, ci);
451               p->finished_line_spanner_->set_bound (LEFT, ci);    
452             }
453           typeset_grob (p->finished_line_spanner_);
454           p->finished_line_spanner_ = 0;
455         }
456     }
457 }
458
459 void
460 Piano_pedal_engraver::start_translation_timestep ()
461 {
462   for (Pedal_info*p = info_list_; p->name_; p ++)
463     {
464       p->req_l_drul_[STOP] = 0;
465       p->req_l_drul_[START] = 0;
466     }
467 }
468 ENTER_DESCRIPTION(Piano_pedal_engraver,
469 /* descr */       "Engrave piano pedal symbols and brackets.",
470 /* creats*/       "SostenutoPedal SustainPedal UnaCordaPedal SostenutoPedalLineSpanner SustainPedalLineSpanner UnaCordaPedalLineSpanner",
471 /* acks  */       "note-column-interface",
472 /* reads */       "pedalSostenutoStrings pedalSustainStrings pedalUnaCordaStrings",
473 /* write */       "");