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