]> git.donarmstrong.com Git - lilypond.git/blob - lily/piano-pedal-engraver.cc
* scripts/lilypond-book.py (option_definitions): typo
[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--2003 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 "event.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   Music* start_req_;
29   Drul_array<Music*> 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   Music* 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   Link_array<Spanner> previous_;
67   
68   int spanner_count_;
69
70   /*
71     Left and right flare widths of a \___/, as specified by the grob
72     property edge-widen.
73
74     UGR. No GC protection.
75     
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   previous_.clear ();
98
99   char * names [] = { "Sostenuto", "Sustain", "UnaCorda", 0  };
100   char **np = names ;
101   do
102     {
103       p->name_ = *np;
104       p->item_ = 0;
105       p->bracket_ = 0;
106       p->finished_bracket_ = 0;
107       p->line_spanner_ = 0;
108       p->finished_line_spanner_ = 0;
109       p->current_bracket_req_ = 0;
110       p->req_l_drul_[START] = 0;
111       p->req_l_drul_[STOP] = 0;
112       p->start_req_ = 0;
113
114       p++;
115     }
116   while (* (np ++));
117 }
118
119 Piano_pedal_engraver::~Piano_pedal_engraver ()
120 {
121   delete[] info_list_;
122 }
123
124 /*
125    Urg: Code dup
126    I'm a script
127   */
128 void
129 Piano_pedal_engraver::acknowledge_grob (Grob_info info)
130 {
131   for (Pedal_info*p = info_list_; p && p->name_; p ++)
132     {
133       if (Note_column::has_interface (info.grob_))
134         {
135           if (p->line_spanner_)
136             {
137               Side_position_interface::add_support (p->line_spanner_, info.grob_);
138               
139               add_bound_item (p->line_spanner_,info.grob_);
140             }     
141           if (p->bracket_)
142             add_bound_item (p->bracket_,info.grob_);              
143           
144         }
145     }
146 }
147
148 bool
149 Piano_pedal_engraver::try_music (Music *m)
150 {
151   if (m->is_mus_type ("abort-event"))
152     {
153       for (Pedal_info*p = info_list_; p->name_; p ++)
154         {
155           p->req_l_drul_[START] = 0;
156           p->req_l_drul_[STOP] = 0;
157           
158               if (p->bracket_)
159                 p->bracket_->suicide (); /* as in dynamic-engraver.cc */
160               p->bracket_ = 0;
161             }
162     }
163   else if  (m->is_mus_type ("pedal-event"))
164     {
165       for (Pedal_info*p = info_list_; p->name_; p ++)
166         {
167           String nm = p->name_ + String ("Event");
168           if (gh_equal_p (m->get_mus_property ("name") ,
169                           gh_symbol2scm (nm.to_str0())))
170             {
171               Direction d = to_dir (m->get_mus_property ("span-direction"));
172               p->req_l_drul_[d] = m;
173               return true;
174             }
175         }
176     }
177   return false;
178 }
179
180 void
181 Piano_pedal_engraver::process_acknowledged_grobs ()
182 {
183   for (Pedal_info*p = info_list_; p && p->name_; p ++)
184     {
185       if (p->req_l_drul_[STOP] || p->req_l_drul_[START])
186         {
187           if (!p->line_spanner_)
188             {
189               String name  = String (p->name_) + "PedalLineSpanner";
190               p->line_spanner_ = new Spanner (get_property (name.to_str0 ()));
191
192               
193               Music * rq = (p->req_l_drul_[START]  ?  p->req_l_drul_[START]  :  p->req_l_drul_[STOP]);
194               announce_grob (p->line_spanner_, rq->self_scm ());
195             }
196       
197           /* Choose the appropriate grobs to add to the line spanner
198            These can be text items or text-spanners
199           */
200           SCM type = ly_cdr (scm_assoc (ly_symbol2scm ("pedal-type"), 
201                                         get_property ( (String (p->name_) + "Pedal").to_str0 ())));
202           if (type == ly_symbol2scm ("text") ||      // Ped.     *Ped.  *
203               type == ly_symbol2scm ("mixed")  )    // Ped. _____/\____|
204             {
205               if (! p->item_)
206                 create_text_grobs (p, type);
207             }
208           if (type == ly_symbol2scm ("bracket") ||   // |_________/\____|
209               type == ly_symbol2scm ("mixed")  )
210            {
211              create_bracket_grobs (p, type);
212            }
213         }
214     }
215 }
216
217
218 void
219 Piano_pedal_engraver::create_text_grobs (Pedal_info *p, SCM pedaltype)
220 {
221   SCM b;
222   SCM s = SCM_EOL;
223   SCM strings = get_property ( ("pedal" + String (p->name_) + "Strings").to_str0 ());
224
225   if (scm_ilength (strings) < 3)
226     {
227       Music * m =       p->req_l_drul_[START]; 
228       if (!m) m = p->req_l_drul_ [STOP];
229
230       String msg = _ ("Need 3 strings for piano pedals. No pedal made. ");
231       if (m)
232         m->origin().warning (msg);
233       else
234         warning (msg)
235       
236       return ;
237     }
238
239   
240   if (p->req_l_drul_[STOP] && p->req_l_drul_[START]) 
241     {
242       if (pedaltype == ly_symbol2scm ("text")) 
243         {
244           if (!p->start_req_)
245             {
246               p->req_l_drul_[STOP]->origin ()->warning (_f ("can't find start of piano pedal: `%s'",  p->name_));
247             }
248           else
249             {
250               s = ly_cadr (strings);
251             }
252           p->start_req_ = p->req_l_drul_[START];
253         }
254     }
255   else if (p->req_l_drul_[STOP])
256     { 
257       if (pedaltype == ly_symbol2scm ("text"))
258         {
259           if (!p->start_req_)
260             {
261               p->req_l_drul_[STOP]->origin ()->warning (_f ("can't find start of piano pedal: `%s'", p->name_));
262             }
263           else
264             {
265               s = ly_caddr (strings);
266               spanner_count_ --;
267               if (previous_.size ())
268                 previous_.pop();
269             }
270           p->start_req_ = 0;
271         }
272     }
273   else if (p->req_l_drul_[START])
274     {
275       p->start_req_ = p->req_l_drul_[START];
276       s = ly_car (strings);
277       if (pedaltype == ly_symbol2scm ("text"))
278         {
279           spanner_count_ ++;
280
281           /*
282             Code dup?! see below.
283           */
284           if (previous_.size ())
285             // add extra space below the previous already-occuring pedal
286             Side_position_interface::add_support (p->line_spanner_,
287                                                   previous_.top ());
288           previous_.push ( p->line_spanner_);
289         }
290     }
291       
292   if (gh_string_p (s))
293     {
294       String propname = String (p->name_) + "Pedal";
295       b = get_property (propname.to_str0 ());
296       p->item_ = new Item (b);
297       p->item_->set_grob_property ("text", s);
298       Axis_group_interface::add_element (p->line_spanner_, p->item_);
299           
300       announce_grob (p->item_,
301                      (p->req_l_drul_[START]
302                       ? p->req_l_drul_[START]
303                       : p->req_l_drul_[STOP])->self_scm ());
304           
305     }
306
307   if (pedaltype == ly_symbol2scm ("text")) 
308     {
309       p->req_l_drul_[START] = 0;
310       p->req_l_drul_[STOP] = 0;
311     }
312 }
313
314 void
315 Piano_pedal_engraver::create_bracket_grobs (Pedal_info *p, SCM pedaltype)
316 {
317
318   if (p->req_l_drul_[STOP])
319     {
320       if (!p->start_req_)
321         {
322           p->req_l_drul_[STOP]->origin ()->warning (_f ("can't find start of piano pedal: `%s'", p->name_));
323         }
324       else if (!p->req_l_drul_[START])
325         {
326           spanner_count_ -- ;
327           if (previous_.size())
328             previous_.pop();
329         }
330       
331       assert (!p->finished_bracket_ && p->bracket_);
332
333       Grob *cmc = unsmob_grob (get_property ("currentMusicalColumn"));
334       p->bracket_->set_bound (RIGHT, cmc);
335
336       /*
337         Set properties so that the molecule-creating function will
338         know whether the right edge should be flared ___/
339        */
340       SCM eleft = ly_car (p->bracket_->get_grob_property ("edge-widen"));
341       SCM eright = (p->req_l_drul_[START]  ? edge_width_drul_[RIGHT] : gh_double2scm (0));
342       p->bracket_->set_grob_property ("edge-widen", gh_cons (eleft, eright));
343       
344       p->finished_bracket_ = p->bracket_;
345       p->bracket_ = 0;
346       p->current_bracket_req_ = 0;
347       p->start_req_ = p->req_l_drul_[START];
348     }
349
350   if (p->req_l_drul_[START])
351     {
352       p->start_req_ = p->req_l_drul_[START];
353       p->current_bracket_req_ = p->req_l_drul_[START];
354
355       p->bracket_  = new Spanner (get_property ("PianoPedalBracket"));
356
357       /*
358         Set properties so that the molecule-creating function will
359         know whether the left edge should be flared \___
360       */
361
362       SCM ew = p->bracket_->get_grob_property ("edge-widen");
363
364       /*
365         WTF is this doing here?
366
367         This must go to the backend.
368
369         --HWN.
370        */
371       edge_width_drul_[LEFT] =  ly_car (ew);
372       edge_width_drul_[RIGHT] = ly_cdr (ew);
373       
374       SCM eleft = ( (bool) p->req_l_drul_[STOP]  ? 
375                     edge_width_drul_[LEFT]  :
376                     gh_double2scm (0));
377       SCM eright = gh_double2scm (0);
378       p->bracket_->set_grob_property ("edge-widen", gh_cons (eleft, eright));
379
380       /* Set this property for 'mixed style' pedals,    Ped._______/\ ,  
381         so the molecule function will shorten the ____ line by the length of the Ped. text.
382       */
383       
384       p->bracket_->set_grob_property ("text-start", 
385                                        pedaltype == ly_symbol2scm ("mixed") ? 
386                                        gh_bool2scm ( (bool) ! p->req_l_drul_[STOP]) :
387                                        gh_bool2scm (false));
388
389       /*
390         Mixed style: Store a pointer to the preceding text for use in
391         calculating the length of the line 
392       */
393       if (p->item_)
394         p->bracket_->set_grob_property ("pedal-text", p->item_->self_scm ());
395       
396       p->bracket_->set_bound (LEFT, unsmob_grob (get_property ("currentMusicalColumn")));
397       Axis_group_interface::add_element (p->line_spanner_, p->bracket_);              
398
399       add_bound_item (p->line_spanner_, p->bracket_->get_bound (LEFT));
400       announce_grob (p->bracket_, p->req_l_drul_[START]->self_scm ());
401
402       if (!p->req_l_drul_[STOP])
403         {
404
405           /*
406             code dup.
407            */
408           spanner_count_ ++;
409           if (previous_.size()) 
410             // position new pedal spanner below the current one
411             Side_position_interface::add_support (p->line_spanner_, previous_.top());
412
413           previous_.push (p->line_spanner_);    
414         }
415     }
416
417   p->req_l_drul_[START] = 0;
418   p->req_l_drul_[STOP] = 0;
419 }
420
421 void
422 Piano_pedal_engraver::finalize ()
423 {  
424   for (Pedal_info*p = info_list_; p && p->name_; p ++)
425     {
426       /*
427         suicide?
428        */
429       if (p->line_spanner_
430           && !p->line_spanner_->live())
431         p->line_spanner_ = 0;
432       
433       if (p->line_spanner_)
434         {
435           p->finished_line_spanner_ = p->line_spanner_;
436           typeset_all ();
437         }
438       if (p->bracket_
439           && !p->bracket_->live())
440         p->bracket_ = 0;
441       if (p->bracket_)
442         {
443           p->current_bracket_req_->origin ()->warning (_ ("unterminated pedal bracket"));
444           p->bracket_->suicide ();
445           p->bracket_ = 0;
446         }
447     }
448 }
449
450   
451 void
452 Piano_pedal_engraver::stop_translation_timestep ()
453 {
454   for (Pedal_info*p = info_list_; p && p->name_; p ++)
455     {
456       if (!p->bracket_)
457         {
458           p->finished_line_spanner_ = p->line_spanner_;
459           p->line_spanner_ = 0;
460         }
461     }
462   
463   typeset_all ();
464 }
465
466
467 void
468 Piano_pedal_engraver::typeset_all ()
469 {
470   Item * sustain = 0;
471   for (Pedal_info*p = info_list_; p->name_; p ++)
472     {
473       /*
474         Handle suicide. 
475        */
476       if (p->finished_line_spanner_
477           && !p->finished_line_spanner_->live ())
478         p->finished_line_spanner_ = 0;
479       if (p->finished_bracket_
480           && !p->finished_bracket_->live())
481         p->finished_bracket_ = 0;
482
483
484       if (p->name_ == String ("Sustain"))
485         sustain = p->item_;
486
487       if (p->item_)
488         {
489           /*
490             Hmm.
491           */
492           if (p->name_ != String ("Sustain"))
493             {
494               if (sustain)
495                 {
496                   Side_position_interface::add_support (p->item_,sustain);
497                 }
498             }
499           typeset_grob (p->item_);
500           p->item_ = 0;
501         }
502       
503       if (p->finished_bracket_)
504         {
505           Grob * r = p->finished_bracket_->get_bound (RIGHT);      
506           if (!r)
507             {
508               p->finished_bracket_->set_bound (RIGHT, unsmob_grob (get_property ("currentMusicalColumn")));
509             }
510
511           typeset_grob (p->finished_bracket_);
512           p->finished_bracket_ =0;
513         }
514
515       if (p->finished_line_spanner_)
516         {
517           Side_position_interface::add_staff_support (p->finished_line_spanner_);
518           Grob * l = p->finished_line_spanner_->get_bound (LEFT);
519           Grob * r = p->finished_line_spanner_->get_bound (RIGHT);      
520           if (!r && l)
521             p->finished_line_spanner_->set_bound (RIGHT, l);
522           else if (!l && r)
523             p->finished_line_spanner_->set_bound (LEFT, r);
524           else if (!r && !l)
525             {
526               Grob * cc = unsmob_grob (get_property ("currentMusicalColumn"));
527               Item * ci = dynamic_cast<Item*> (cc);
528               p->finished_line_spanner_->set_bound (RIGHT, ci);
529               p->finished_line_spanner_->set_bound (LEFT, ci);    
530             }
531           typeset_grob (p->finished_line_spanner_);
532           p->finished_line_spanner_ = 0;
533         }
534     }
535 }
536
537 void
538 Piano_pedal_engraver::start_translation_timestep ()
539 {
540   for (Pedal_info*p = info_list_; p->name_; p ++)
541     {
542       p->req_l_drul_[STOP] = 0;
543       p->req_l_drul_[START] = 0;
544     }
545 }
546 ENTER_DESCRIPTION (Piano_pedal_engraver,
547 /* descr */       "Engrave piano pedal symbols and brackets.",
548 /* creats*/       "SostenutoPedal SustainPedal UnaCordaPedal SostenutoPedalLineSpanner SustainPedalLineSpanner UnaCordaPedalLineSpanner",
549 /* accepts */     "pedal-event abort-event",
550 /* acks  */      "note-column-interface",
551 /* reads */       "pedalSostenutoStrings pedalSustainStrings pedalUnaCordaStrings",
552 /* write */       "");