]> git.donarmstrong.com Git - lilypond.git/blob - lily/piano-pedal-engraver.cc
b9068bea0cb6911870007ddbc9846dde5e6b56dd
[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--2004 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 #include "warn.hh"
25
26 /*
27   Urgh. This engraver is too complex. rewrite. --hwn
28
29 */
30
31 struct Pedal_info
32 {
33   char const * name_;
34
35   /*
36     Event for currently running pedal.
37   */
38   Music* current_bracket_ev_;
39
40   /*
41     Event for currently starting pedal, (necessary?
42     
43     distinct from current_bracket_ev_, since current_bracket_ev_ only
44     necessary for brackets, not for text style.
45    */
46   Music* start_ev_;
47
48
49   
50   /*
51     Events that were found in this timestep.
52    */
53   Drul_array<Music*> event_drul_;
54   Item* item_;
55   Spanner* bracket_;     // A single portion of a pedal bracket
56   Spanner* finished_bracket_;
57
58   /*
59     This grob contains all the pedals of the same type on the same staff
60    */
61   Spanner* line_spanner_;
62   Spanner* finished_line_spanner_;
63 };
64
65
66 class Piano_pedal_engraver : public Engraver
67 {
68 public:
69   TRANSLATOR_DECLARATIONS (Piano_pedal_engraver);
70   ~Piano_pedal_engraver ();
71 protected:
72   virtual void initialize ();
73   virtual void finalize ();
74   virtual bool try_music (Music*);
75   virtual void stop_translation_timestep ();
76   virtual void acknowledge_grob (Grob_info);
77   virtual void process_music ();
78
79 private:
80
81   Pedal_info *info_list_;
82
83   /*
84     Record a stack of the current pedal spanners, so if more than one pedal
85     occurs simultaneously then extra space can be added between them.
86   */
87   
88   Link_array<Spanner> previous_;
89   
90   void create_text_grobs (Pedal_info *p, bool);
91   void create_bracket_grobs (Pedal_info *p, bool);
92   void typeset_all ();
93 };
94
95
96 Piano_pedal_engraver::Piano_pedal_engraver ()
97 {
98   info_list_ = 0;
99 }
100
101 void
102 Piano_pedal_engraver::initialize ()
103 {
104   previous_.clear ();
105
106   char * names [] = { "Sostenuto", "Sustain", "UnaCorda", 0  };
107
108   info_list_ = new Pedal_info[sizeof (names)/ sizeof (const char*)]; 
109   Pedal_info *p = info_list_;
110
111   char **np = names ;
112   do
113     {
114       p->name_ = *np;
115       p->item_ = 0;
116       p->bracket_ = 0;
117       p->finished_bracket_ = 0;
118       p->line_spanner_ = 0;
119       p->finished_line_spanner_ = 0;
120       p->current_bracket_ev_ = 0;
121       p->event_drul_[START] = 0;
122       p->event_drul_[STOP] = 0;
123       p->start_ev_ = 0;
124
125       p++;
126     }
127   while (* (np ++));
128 }
129
130 Piano_pedal_engraver::~Piano_pedal_engraver ()
131 {
132   delete[] info_list_;
133 }
134
135 /*
136    Urg: Code dup
137    I'm a script
138   */
139 void
140 Piano_pedal_engraver::acknowledge_grob (Grob_info info)
141 {
142   for (Pedal_info*p = info_list_; p && p->name_; p ++)
143     {
144       if (Note_column::has_interface (info.grob_))
145         {
146           if (p->line_spanner_)
147             {
148               Side_position_interface::add_support (p->line_spanner_, info.grob_);
149               add_bound_item (p->line_spanner_,info.grob_);
150             }     
151           if (p->bracket_)
152             add_bound_item (p->bracket_,info.grob_);
153           if (p->finished_bracket_)
154             add_bound_item (p->finished_bracket_,info.grob_);             
155         }
156     }
157 }
158
159 bool
160 Piano_pedal_engraver::try_music (Music *m)
161 {
162  if  (m->is_mus_type ("pedal-event"))
163     {
164       for (Pedal_info*p = info_list_; p->name_; p ++)
165         {
166           String nm = p->name_ + String ("Event");
167           if (gh_equal_p (m->get_mus_property ("name") ,
168                           gh_symbol2scm (nm.to_str0())))
169             {
170               Direction d = to_dir (m->get_mus_property ("span-direction"));
171               p->event_drul_[d] = m;
172               return true;
173             }
174         }
175     }
176   return false;
177 }
178
179 void
180 Piano_pedal_engraver::process_music ()
181 {
182   for (Pedal_info*p = info_list_; p && p->name_; p ++)
183     {
184       if (p->event_drul_[STOP] || p->event_drul_[START])
185         {
186           if (!p->line_spanner_)
187             {
188               String name  = String (p->name_) + "PedalLineSpanner";
189               p->line_spanner_ = make_spanner (name.to_str0 ());
190
191               Music * rq = (p->event_drul_[START]  ?  p->event_drul_[START]  :  p->event_drul_[STOP]);
192               announce_grob (p->line_spanner_, rq->self_scm ());
193             }
194       
195           /* Choose the appropriate grobs to add to the line spanner
196            These can be text items or text-spanners
197           */
198
199           /*
200             ugh, code dup, should read grob to create from other
201             property.
202
203               bracket: |_________/\____|
204               text:    Ped.     *Ped.  *
205               mixed:   Ped. _____/\____|
206            */
207
208
209           String prop = String ("pedal")  + p->name_ + "Style";
210           SCM style = get_property (prop.to_str0 ());
211           bool mixed = style == ly_symbol2scm ("mixed");
212           if (style == ly_symbol2scm ("text") ||
213               mixed)    
214             {
215               if (! p->item_)
216                 create_text_grobs (p, mixed);
217             }
218           if (style == ly_symbol2scm ("bracket") ||
219               mixed)
220            {
221              create_bracket_grobs (p, mixed);
222            }
223         }
224     }
225 }
226
227 void
228 Piano_pedal_engraver::create_text_grobs (Pedal_info *p, bool mixed)
229 {
230   SCM s = SCM_EOL;
231   SCM strings = get_property ( ("pedal" + String (p->name_) + "Strings").to_str0 ());
232
233   if (scm_ilength (strings) < 3)
234     {
235       Music * m =       p->event_drul_[START]; 
236       if (!m) m = p->event_drul_ [STOP];
237
238       String msg = _ ("Need 3 strings for piano pedals. No pedal made. ");
239       if (m)
240         m->origin()->warning (msg);
241       else
242         warning (msg);
243       
244       return ;
245     }
246   
247   if (p->event_drul_[STOP] && p->event_drul_[START]) 
248     {
249       if (!mixed)
250         {
251           if (!p->start_ev_)
252             {
253               p->event_drul_[STOP]->origin ()->warning (_f ("can't find start of piano pedal: `%s'",  p->name_));
254             }
255           else
256             {
257               s = ly_cadr (strings);
258             }
259           p->start_ev_ = p->event_drul_[START];
260         }
261     }
262   else if (p->event_drul_[STOP])
263     { 
264       if (!mixed)
265         {
266           if (!p->start_ev_)
267             {
268               p->event_drul_[STOP]->origin ()->warning (_f ("can't find start of piano pedal: `%s'", p->name_));
269             }
270           else
271             {
272               s = ly_caddr (strings);
273               if (previous_.size ())
274                 previous_.pop();
275             }
276           p->start_ev_ = 0;
277         }
278     }
279   else if (p->event_drul_[START])
280     {
281       p->start_ev_ = p->event_drul_[START];
282       s = ly_car (strings);
283       if (!mixed)
284         {
285           /*
286             Code dup?! see below.
287           */
288           if (previous_.size ())
289             // add extra space below the previous already-occuring pedal
290             Side_position_interface::add_support (p->line_spanner_,
291                                                   previous_.top ());
292           previous_.push ( p->line_spanner_);
293         }
294     }
295       
296   if (gh_string_p (s))
297     {
298       String propname = String (p->name_) + "Pedal";
299
300       p->item_ = make_item (propname.to_str0 ());
301       p->item_->set_grob_property ("text", s);
302       Axis_group_interface::add_element (p->line_spanner_, p->item_);
303           
304       announce_grob (p->item_,
305                      (p->event_drul_[START]
306                       ? p->event_drul_[START]
307                       : p->event_drul_[STOP])->self_scm ());
308     }
309
310   if (!mixed)
311     {
312       p->event_drul_[START] = 0;
313       p->event_drul_[STOP] = 0;
314     }
315 }
316
317 void
318 Piano_pedal_engraver::create_bracket_grobs (Pedal_info *p, bool mixed)
319 {
320   if (!p->bracket_ && p->event_drul_[STOP])
321     {
322       String msg =_f ("can't find start of piano pedal bracket: `%s'", p->name_);
323       p->event_drul_[STOP]->origin ()->warning (msg);
324       p->event_drul_[STOP] =  0;
325     }
326
327   if (p->event_drul_[STOP])
328     {
329       if (!p->event_drul_[START])
330         {
331           if (previous_.size())
332             previous_.pop();
333         }
334       
335       assert (!p->finished_bracket_); 
336
337       Grob *cmc = unsmob_grob (get_property ("currentMusicalColumn"));
338
339       if (!p->bracket_->get_bound (RIGHT))
340         p->bracket_->set_bound (RIGHT, cmc);
341
342       /*
343         Set properties so that the molecule-creating function will
344         know whether the right edge should be flared ___/
345        */
346
347       if (!p->event_drul_[START])
348         {
349           SCM flare = p->bracket_->get_grob_property ("bracket-flare");
350           p->bracket_->set_grob_property ("bracket-flare", scm_cons (gh_car (flare),
351                                                                      gh_double2scm (0)));
352         }
353
354       p->finished_bracket_ = p->bracket_;
355       p->bracket_ = 0;
356       p->current_bracket_ev_ = 0;
357     }
358
359   if (p->event_drul_[START])
360     {
361       p->start_ev_ = p->event_drul_[START];
362       p->current_bracket_ev_ = p->event_drul_[START];
363
364       p->bracket_  = make_spanner ("PianoPedalBracket");
365
366       /*
367         Set properties so that the molecule-creating function will
368         know whether the left edge should be flared \___
369       */
370
371       if (!p->finished_bracket_)
372         {
373           SCM flare = p->bracket_->get_grob_property ("bracket-flare");
374           p->bracket_->set_grob_property ("bracket-flare", scm_cons (gh_double2scm (0),gh_cdr (flare)));
375         }
376
377
378       /* Set this property for 'mixed style' pedals,    Ped._______/\ ,  
379         so the molecule function will shorten the ____ line by the length of the Ped. text.
380       */
381
382       if (mixed)
383         {
384           /*
385             Mixed style: Store a pointer to the preceding text for use in
386             calculating the length of the line
387
388
389             TODO:
390
391             WTF is pedal-text not the bound of the object? --hwn
392           */
393           if (p->item_)
394             p->bracket_->set_grob_property ("pedal-text", p->item_->self_scm ());
395         }
396
397
398       /*
399         We do not use currentMusicalColumn for the left span-point.
400         If the column as accidentals (eg on a different stave), the
401         currentMusicalColumn is too wide, making the bracket too big.
402
403         TODO:
404
405         Hmm. What do we do when there are no notes when the spanner starts?
406
407         TODO:
408
409         what about the right span point?
410         
411        */
412       Axis_group_interface::add_element (p->line_spanner_, p->bracket_);              
413       announce_grob (p->bracket_, p->event_drul_[START]->self_scm ());
414
415       if (!p->event_drul_[STOP])
416         {
417
418           /*
419             code dup. --hwn.
420
421             // position new pedal spanner below the current one
422           */
423           if (previous_.size()) 
424             Side_position_interface::add_support (p->line_spanner_, previous_.top());
425
426           previous_.push (p->line_spanner_);    
427         }
428     }
429
430   p->event_drul_[START] = 0;
431   p->event_drul_[STOP] = 0;
432 }
433
434 void
435 Piano_pedal_engraver::finalize ()
436 {  
437   for (Pedal_info*p = info_list_; p && p->name_; p ++)
438     {
439       /*
440         suicide?
441        */
442       if (p->line_spanner_
443           && !p->line_spanner_->live())
444         p->line_spanner_ = 0;
445       
446       if (p->line_spanner_)
447         {
448           p->finished_line_spanner_ = p->line_spanner_;
449           typeset_all ();
450         }
451       if (p->bracket_
452           && !p->bracket_->live())
453         p->bracket_ = 0;
454       
455       if (p->bracket_)
456         {
457           p->current_bracket_ev_->origin ()->warning (_ ("unterminated pedal bracket"));
458           p->bracket_->suicide ();
459           p->bracket_ = 0;
460         }
461     }
462 }
463
464   
465 void
466 Piano_pedal_engraver::stop_translation_timestep ()
467 {
468   for (Pedal_info*p = info_list_; p && p->name_; p ++)
469     {
470       if (!p->bracket_)
471         {
472           p->finished_line_spanner_ = p->line_spanner_;
473           p->line_spanner_ = 0;
474         }
475     }
476   
477   typeset_all ();
478
479   for (Pedal_info*p = info_list_; p->name_; p ++)
480     {
481       p->event_drul_[STOP] = 0;
482       p->event_drul_[START] = 0;
483     }
484 }
485
486
487 void
488 Piano_pedal_engraver::typeset_all ()
489 {
490   Item * sustain = 0;
491   for (Pedal_info*p = info_list_; p->name_; p ++)
492     {
493       /*
494         Handle suicide. 
495        */
496       if (p->finished_line_spanner_
497           && !p->finished_line_spanner_->live ())
498         p->finished_line_spanner_ = 0;
499       if (p->finished_bracket_
500           && !p->finished_bracket_->live())
501         p->finished_bracket_ = 0;
502
503
504       if (p->name_ == String ("Sustain"))
505         sustain = p->item_;
506
507       if (p->item_)
508         {
509           /*
510             Hmm.
511           */
512           if (p->name_ != String ("Sustain") && sustain)
513             {
514               Side_position_interface::add_support (p->item_,sustain);
515             }
516           typeset_grob (p->item_);
517           p->item_ = 0;
518         }
519       
520       if (p->finished_bracket_)
521         {
522           Grob * r = p->finished_bracket_->get_bound (RIGHT);      
523           if (!r)
524             {
525               p->finished_bracket_->set_bound (RIGHT, unsmob_grob (get_property ("currentMusicalColumn")));
526             }
527
528           typeset_grob (p->finished_bracket_);
529           
530           p->finished_bracket_ =0;
531         }
532
533       if (p->finished_line_spanner_)
534         {
535           Grob * l = p->finished_line_spanner_->get_bound (LEFT);
536           Grob * r = p->finished_line_spanner_->get_bound (RIGHT);      
537           if (!r && l)
538             p->finished_line_spanner_->set_bound (RIGHT, l);
539           else if (!l && r)
540             p->finished_line_spanner_->set_bound (LEFT, r);
541           else if (!r && !l)
542             {
543               Grob * cc = unsmob_grob (get_property ("currentMusicalColumn"));
544               Item * ci = dynamic_cast<Item*> (cc);
545               p->finished_line_spanner_->set_bound (RIGHT, ci);
546               p->finished_line_spanner_->set_bound (LEFT, ci);    
547             }
548           typeset_grob (p->finished_line_spanner_);
549           p->finished_line_spanner_ = 0;
550         }
551     }
552 }
553
554 ENTER_DESCRIPTION (Piano_pedal_engraver,
555 /* descr */       "Engrave piano pedal symbols and brackets.",
556 /* creats*/       "SostenutoPedal SustainPedal UnaCordaPedal SostenutoPedalLineSpanner SustainPedalLineSpanner UnaCordaPedalLineSpanner",
557 /* accepts */     "pedal-event",
558 /* acks  */       "note-column-interface",
559 /* reads */       "pedalSostenutoStrings pedalSustainStrings pedalUnaCordaStrings pedalSostenutoStyle pedalSustainStyle pedalUnaCordaStyle",
560 /* write */       "");