]> git.donarmstrong.com Git - lilypond.git/blob - lily/piano-pedal-engraver.cc
remove
[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 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   void del_linespanner (Spanner*);
90   
91   void create_text_grobs (Pedal_info *p, bool);
92   void create_bracket_grobs (Pedal_info *p, bool);
93   void typeset_all (Pedal_info*p);
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   char * names [] = { "Sostenuto", "Sustain", "UnaCorda", 0  };
106
107   info_list_ = new Pedal_info[sizeof (names)/ sizeof (const char*)]; 
108   Pedal_info *p = info_list_;
109
110   char **np = names ;
111   do
112     {
113       p->name_ = *np;
114       p->item_ = 0;
115       p->bracket_ = 0;
116       p->finished_bracket_ = 0;
117       p->line_spanner_ = 0;
118       p->finished_line_spanner_ = 0;
119       p->current_bracket_ev_ = 0;
120       p->event_drul_[START] = 0;
121       p->event_drul_[STOP] = 0;
122       p->start_ev_ = 0;
123
124       p++;
125     }
126   while (* (np ++));
127 }
128
129 Piano_pedal_engraver::~Piano_pedal_engraver ()
130 {
131   delete[] info_list_;
132 }
133
134 /*
135   Urg: Code dup
136   I'm a script
137 */
138 void
139 Piano_pedal_engraver::acknowledge_grob (Grob_info info)
140 {
141   for (Pedal_info*p = info_list_; p && p->name_; p ++)
142     {
143       if (Note_column::has_interface (info.grob_))
144         {
145           if (p->line_spanner_)
146             {
147               Side_position_interface::add_support (p->line_spanner_, info.grob_);
148               add_bound_item (p->line_spanner_,info.grob_);
149             }     
150           if (p->bracket_)
151             add_bound_item (p->bracket_,info.grob_);
152           if (p->finished_bracket_)
153             add_bound_item (p->finished_bracket_,info.grob_);             
154         }
155     }
156 }
157
158 bool
159 Piano_pedal_engraver::try_music (Music *m)
160 {
161   if (m->is_mus_type ("pedal-event"))
162     {
163       for (Pedal_info*p = info_list_; p->name_; p ++)
164         {
165           String nm = p->name_ + String ("Event");
166           if (ly_c_equal_p (m->get_property ("name") ,
167                           scm_str2symbol(nm.to_str0())))
168             {
169               Direction d = to_dir (m->get_property ("span-direction"));
170               p->event_drul_[d] = m;
171               return true;
172             }
173         }
174     }
175   return false;
176 }
177
178 void
179 Piano_pedal_engraver::process_music ()
180 {
181   for (Pedal_info*p = info_list_; p && p->name_; p ++)
182     {
183       if (p->event_drul_[STOP] || p->event_drul_[START])
184         {
185           if (!p->line_spanner_)
186             {
187               String name  = String (p->name_) + "PedalLineSpanner";
188               Music * rq = (p->event_drul_[START]  ?  p->event_drul_[START]  :  p->event_drul_[STOP]);
189               p->line_spanner_ = make_spanner (name.to_str0 (), rq->self_scm ());
190
191
192               
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
212           bool mixed = style == ly_symbol2scm ("mixed");
213           bool bracket = (mixed
214                           || style == ly_symbol2scm ("bracket"));
215           bool text = (style == ly_symbol2scm ("text")
216                        || mixed);
217           
218           if (text && !p->item_)
219             create_text_grobs (p, mixed);
220           if (bracket)
221             create_bracket_grobs (p, mixed);
222         }
223     }
224 }
225
226 void
227 Piano_pedal_engraver::create_text_grobs (Pedal_info *p, bool mixed)
228 {
229   SCM s = SCM_EOL;
230   SCM strings = get_property ( ("pedal" + String (p->name_) + "Strings").to_str0 ());
231
232   if (scm_ilength (strings) < 3)
233     {
234       Music * m =       p->event_drul_[START]; 
235       if (!m) m = p->event_drul_ [STOP];
236
237       String msg = _ ("Need 3 strings for piano pedals. No pedal made. ");
238       if (m)
239         m->origin ()->warning (msg);
240       else
241         warning (msg);
242       
243       return ;
244     }
245   
246   if (p->event_drul_[STOP] && p->event_drul_[START]) 
247     {
248       if (!mixed)
249         {
250           if (!p->start_ev_)
251             {
252               p->event_drul_[STOP]->origin ()->warning (_f ("can't find start of piano pedal: `%s'",  p->name_));
253             }
254           else
255             {
256               s = ly_cadr (strings);
257             }
258           p->start_ev_ = p->event_drul_[START];
259         }
260     }
261   else if (p->event_drul_[STOP])
262     { 
263       if (!mixed)
264         {
265           if (!p->start_ev_)
266             {
267               p->event_drul_[STOP]->origin ()->warning (_f ("can't find start of piano pedal: `%s'", p->name_));
268             }
269           else
270             {
271               s = ly_caddr (strings);
272             }
273           p->start_ev_ = 0;
274         }
275     }
276   else if (p->event_drul_[START])
277     {
278       p->start_ev_ = p->event_drul_[START];
279       s = ly_car (strings);
280       if (!mixed)
281         {
282           /*
283             Code dup?! see below.
284           */
285           if (previous_.size ())
286             // add extra space below the previous already-occuring pedal
287             Side_position_interface::add_support (p->line_spanner_,
288                                                   previous_.top ());
289           previous_.push (p->line_spanner_);
290         }
291     }
292       
293   if (ly_c_string_p (s))
294     {
295       String propname = String (p->name_) + "Pedal";
296
297       p->item_ = make_item (propname.to_str0 (), (p->event_drul_[START]
298                                                   ? p->event_drul_[START]
299                                                   : p->event_drul_[STOP])->self_scm ());
300
301       p->item_->set_property ("text", s);
302       Axis_group_interface::add_element (p->line_spanner_, p->item_);
303     }
304   
305   if (!mixed)
306     {
307       p->event_drul_[START] = 0;
308       p->event_drul_[STOP] = 0;
309     }
310 }
311
312
313 void
314 Piano_pedal_engraver::create_bracket_grobs (Pedal_info *p, bool mixed)
315 {
316   if (!p->bracket_ && p->event_drul_[STOP])
317     {
318       String msg =_f ("can't find start of piano pedal bracket: `%s'", p->name_);
319       p->event_drul_[STOP]->origin ()->warning (msg);
320       p->event_drul_[STOP] =  0;
321     }
322
323   if (p->event_drul_[STOP])
324     {
325       assert (!p->finished_bracket_); 
326
327       Grob *cmc = unsmob_grob (get_property ("currentMusicalColumn"));
328
329       if (!p->bracket_->get_bound (RIGHT))
330         p->bracket_->set_bound (RIGHT, cmc);
331
332       /*
333         Set properties so that the stencil-creating function will
334         know whether the right edge should be flared ___/
335       */
336
337       if (!p->event_drul_[START])
338         {
339           SCM flare = p->bracket_->get_property ("bracket-flare");
340           p->bracket_->set_property ("bracket-flare", scm_cons (ly_car (flare),
341                                                                 scm_make_real (0)));
342         }
343
344       p->finished_bracket_ = p->bracket_;
345       p->bracket_ = 0;
346       p->current_bracket_ev_ = 0;
347     }
348
349   if (p->event_drul_[START])
350     {
351       p->start_ev_ = p->event_drul_[START];
352       p->current_bracket_ev_ = p->event_drul_[START];
353
354       p->bracket_  = make_spanner ("PianoPedalBracket", p->event_drul_[START]->self_scm ());
355
356       /*
357         Set properties so that the stencil-creating function will
358         know whether the left edge should be flared \___
359       */
360
361       if (!p->finished_bracket_)
362         {
363           SCM flare = p->bracket_->get_property ("bracket-flare");
364           p->bracket_->set_property ("bracket-flare", scm_cons (scm_make_real (0),ly_cdr (flare)));
365         }
366
367
368       /* Set this property for 'mixed style' pedals,    Ped._______/\ ,  
369          so the stencil function will shorten the ____ line by the length of the Ped. text.
370       */
371
372       if (mixed)
373         {
374           /*
375             Mixed style: Store a pointer to the preceding text for use in
376             calculating the length of the line
377
378
379             TODO:
380
381             WTF is pedal-text not the bound of the object? --hwn
382           */
383           if (p->item_)
384             p->bracket_->set_property ("pedal-text", p->item_->self_scm ());
385         }
386
387
388       /*
389         We do not use currentMusicalColumn for the left span-point.
390         If the column as accidentals (eg on a different stave), the
391         currentMusicalColumn is too wide, making the bracket too big.
392
393         TODO:
394
395         Hmm. What do we do when there are no notes when the spanner starts?
396
397         TODO:
398
399         what about the right span point?
400         
401       */
402       Axis_group_interface::add_element (p->line_spanner_, p->bracket_);              
403
404       if (!p->event_drul_[STOP])
405         {
406
407           /*
408             code dup. --hwn.
409
410             // position new pedal spanner below the current one
411             */
412           if (previous_.size ()) 
413             Side_position_interface::add_support (p->line_spanner_, previous_.top ());
414
415           previous_.push (p->line_spanner_);    
416         }
417     }
418
419   p->event_drul_[START] = 0;
420   p->event_drul_[STOP] = 0;
421 }
422
423 void
424 Piano_pedal_engraver::finalize ()
425 {  
426   for (Pedal_info*p = info_list_; p && p->name_; p ++)
427     {
428       /*
429         suicide?
430       */
431       if (p->line_spanner_
432           && !p->line_spanner_->live ())
433         p->line_spanner_ = 0;
434       
435       if (p->bracket_
436           && !p->bracket_->live ())
437         p->bracket_ = 0;
438       
439       if (p->bracket_)
440         {
441           SCM cc = get_property ("currentCommandColumn");
442           Item *c = unsmob_item (cc);
443           if (p->line_spanner_)
444             {
445               p->line_spanner_->set_bound (RIGHT, c);
446             }
447           p->bracket_ ->set_bound (RIGHT, c);
448
449           p->finished_bracket_ = p->bracket_;
450           p->bracket_ = 0;
451           p->finished_line_spanner_ = p->line_spanner_;
452           p->line_spanner_ = 0;
453           typeset_all (p);
454         }
455
456       if (p->line_spanner_)
457         {
458           p->finished_line_spanner_ = p->line_spanner_;
459           typeset_all (p);
460         }
461     }
462 }
463
464 void
465 Piano_pedal_engraver::del_linespanner (Spanner *g)
466 {
467   int idx = previous_.find_index (g);
468   if (idx >= 0)
469     previous_.del (idx);
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           del_linespanner (p->finished_line_spanner_);
482         }
483       
484       typeset_all (p);
485     }
486   
487
488   for (Pedal_info*p = info_list_; p->name_; p ++)
489     {
490       p->event_drul_[STOP] = 0;
491       p->event_drul_[START] = 0;
492     }
493 }
494
495
496 void
497 Piano_pedal_engraver::typeset_all (Pedal_info * p)
498 {
499   /*
500     Handle suicide. 
501   */
502   if (p->finished_line_spanner_
503       && !p->finished_line_spanner_->live ())
504     p->finished_line_spanner_ = 0;
505   if (p->finished_bracket_
506       && !p->finished_bracket_->live ())
507     p->finished_bracket_ = 0;
508
509
510   if (p->item_)
511     {
512       p->item_ = 0;
513     }
514       
515   if (p->finished_bracket_)
516     {
517       Grob * r = p->finished_bracket_->get_bound (RIGHT);      
518       if (!r)
519         {
520           p->finished_bracket_->set_bound (RIGHT, unsmob_grob (get_property ("currentMusicalColumn")));
521         }
522
523       p->finished_bracket_ = 0;
524     }
525
526   if (p->finished_line_spanner_)
527     {
528       Grob * l = p->finished_line_spanner_->get_bound (LEFT);
529       Grob * r = p->finished_line_spanner_->get_bound (RIGHT);      
530       if (!r && l)
531         p->finished_line_spanner_->set_bound (RIGHT, l);
532       else if (!l && r)
533         p->finished_line_spanner_->set_bound (LEFT, r);
534       else if (!r && !l)
535         {
536           Grob * cc = unsmob_grob (get_property ("currentMusicalColumn"));
537           Item * ci = dynamic_cast<Item*> (cc);
538           p->finished_line_spanner_->set_bound (RIGHT, ci);
539           p->finished_line_spanner_->set_bound (LEFT, ci);        
540         }
541       
542       p->finished_line_spanner_ = 0;
543     }
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",
550                    /* acks  */       "note-column-interface",
551                    /* reads */       "currentCommandColumn "
552                    "pedalSostenutoStrings pedalSustainStrings "
553                    "pedalUnaCordaStrings pedalSostenutoStyle "
554                    "pedalSustainStyle pedalUnaCordaStyle",
555                    /* write */       "");