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