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