]> git.donarmstrong.com Git - lilypond.git/blob - lily/piano-pedal-engraver.cc
update for the lily-wins.py script.
[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   if (!mixed)
305     {
306       p->event_drul_[START] = 0;
307       p->event_drul_[STOP] = 0;
308     }
309 }
310
311
312 void
313 Piano_pedal_engraver::create_bracket_grobs (Pedal_info *p, bool mixed)
314 {
315   if (!p->bracket_ && p->event_drul_[STOP])
316     {
317       String msg =_f ("can't find start of piano pedal bracket: `%s'", p->name_);
318       p->event_drul_[STOP]->origin ()->warning (msg);
319       p->event_drul_[STOP] =  0;
320     }
321
322   if (p->event_drul_[STOP])
323     {
324       assert (!p->finished_bracket_); 
325
326       Grob *cmc = unsmob_grob (get_property ("currentMusicalColumn"));
327
328       if (!p->bracket_->get_bound (RIGHT))
329         p->bracket_->set_bound (RIGHT, cmc);
330
331       /*
332         Set properties so that the stencil-creating function will
333         know whether the right edge should be flared ___/
334       */
335
336       if (!p->event_drul_[START])
337         {
338           SCM flare = p->bracket_->get_property ("bracket-flare");
339           p->bracket_->set_property ("bracket-flare", scm_cons (ly_car (flare),
340                                                                 scm_make_real (0)));
341         }
342
343       p->finished_bracket_ = p->bracket_;
344       p->bracket_ = 0;
345       p->current_bracket_ev_ = 0;
346     }
347
348   if (p->event_drul_[START])
349     {
350       p->start_ev_ = p->event_drul_[START];
351       p->current_bracket_ev_ = p->event_drul_[START];
352
353       p->bracket_  = make_spanner ("PianoPedalBracket", p->event_drul_[START]->self_scm ());
354
355       /*
356         Set properties so that the stencil-creating function will
357         know whether the left edge should be flared \___
358       */
359
360       if (!p->finished_bracket_)
361         {
362           SCM flare = p->bracket_->get_property ("bracket-flare");
363           p->bracket_->set_property ("bracket-flare", scm_cons (scm_make_real (0),ly_cdr (flare)));
364         }
365
366
367       /* Set this property for 'mixed style' pedals,    Ped._______/\ ,  
368          so the stencil function will shorten the ____ line by the length of the Ped. text.
369       */
370
371       if (mixed)
372         {
373           /*
374             Mixed style: Store a pointer to the preceding text for use in
375             calculating the length of the line
376
377
378             TODO:
379
380             WTF is pedal-text not the bound of the object? --hwn
381           */
382           if (p->item_)
383             p->bracket_->set_property ("pedal-text", p->item_->self_scm ());
384         }
385
386
387       /*
388         We do not use currentMusicalColumn for the left span-point.
389         If the column as accidentals (eg on a different stave), the
390         currentMusicalColumn is too wide, making the bracket too big.
391
392         TODO:
393
394         Hmm. What do we do when there are no notes when the spanner starts?
395
396         TODO:
397
398         what about the right span point?
399         
400       */
401       Axis_group_interface::add_element (p->line_spanner_, p->bracket_);              
402
403       if (!p->event_drul_[STOP])
404         {
405
406           /*
407             code dup. --hwn.
408
409             // position new pedal spanner below the current one
410             */
411           if (previous_.size ()) 
412             Side_position_interface::add_support (p->line_spanner_, previous_.top ());
413
414           previous_.push (p->line_spanner_);    
415         }
416     }
417
418   p->event_drul_[START] = 0;
419   p->event_drul_[STOP] = 0;
420 }
421
422 void
423 Piano_pedal_engraver::finalize ()
424 {  
425   for (Pedal_info*p = info_list_; p && p->name_; p ++)
426     {
427       /*
428         suicide?
429       */
430       if (p->line_spanner_
431           && !p->line_spanner_->live ())
432         p->line_spanner_ = 0;
433       
434       if (p->bracket_
435           && !p->bracket_->live ())
436         p->bracket_ = 0;
437       
438       if (p->bracket_)
439         {
440           SCM cc = get_property ("currentCommandColumn");
441           Item *c = unsmob_item (cc);
442           if (p->line_spanner_)
443             {
444               p->line_spanner_->set_bound (RIGHT, c);
445             }
446           p->bracket_ ->set_bound (RIGHT, c);
447
448           p->finished_bracket_ = p->bracket_;
449           p->bracket_ = 0;
450           p->finished_line_spanner_ = p->line_spanner_;
451           p->line_spanner_ = 0;
452           typeset_all (p);
453         }
454
455       if (p->line_spanner_)
456         {
457           p->finished_line_spanner_ = p->line_spanner_;
458           typeset_all (p);
459         }
460     }
461 }
462
463 void
464 Piano_pedal_engraver::del_linespanner (Spanner *g)
465 {
466   int idx = previous_.find_index (g);
467   if (idx >= 0)
468     previous_.del (idx);
469 }
470
471 void
472 Piano_pedal_engraver::stop_translation_timestep ()
473 {
474   for (Pedal_info*p = info_list_; p && p->name_; p ++)
475     {
476       if (!p->bracket_)
477         {
478           p->finished_line_spanner_ = p->line_spanner_;
479           p->line_spanner_ = 0;
480           del_linespanner (p->finished_line_spanner_);
481         }
482       
483       typeset_all (p);
484     }
485   
486
487   for (Pedal_info*p = info_list_; p->name_; p ++)
488     {
489       p->event_drul_[STOP] = 0;
490       p->event_drul_[START] = 0;
491     }
492 }
493
494
495 void
496 Piano_pedal_engraver::typeset_all (Pedal_info * p)
497 {
498   /*
499     Handle suicide. 
500   */
501   if (p->finished_line_spanner_
502       && !p->finished_line_spanner_->live ())
503     p->finished_line_spanner_ = 0;
504   if (p->finished_bracket_
505       && !p->finished_bracket_->live ())
506     p->finished_bracket_ = 0;
507
508
509   if (p->item_)
510     {
511       typeset_grob (p->item_);
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       typeset_grob (p->finished_bracket_);
524           
525       p->finished_bracket_ =0;
526     }
527
528   if (p->finished_line_spanner_)
529     {
530       Grob * l = p->finished_line_spanner_->get_bound (LEFT);
531       Grob * r = p->finished_line_spanner_->get_bound (RIGHT);      
532       if (!r && l)
533         p->finished_line_spanner_->set_bound (RIGHT, l);
534       else if (!l && r)
535         p->finished_line_spanner_->set_bound (LEFT, r);
536       else if (!r && !l)
537         {
538           Grob * cc = unsmob_grob (get_property ("currentMusicalColumn"));
539           Item * ci = dynamic_cast<Item*> (cc);
540           p->finished_line_spanner_->set_bound (RIGHT, ci);
541           p->finished_line_spanner_->set_bound (LEFT, ci);        
542         }
543       typeset_grob (p->finished_line_spanner_);
544       p->finished_line_spanner_ = 0;
545     }
546 }
547
548 ENTER_DESCRIPTION (Piano_pedal_engraver,
549                    /* descr */       "Engrave piano pedal symbols and brackets.",
550                    /* creats*/       "SostenutoPedal SustainPedal UnaCordaPedal SostenutoPedalLineSpanner SustainPedalLineSpanner UnaCordaPedalLineSpanner",
551                    /* accepts */     "pedal-event",
552                    /* acks  */       "note-column-interface",
553                    /* reads */       "currentCommandColumn "
554                    "pedalSostenutoStrings pedalSustainStrings "
555                    "pedalUnaCordaStrings pedalSostenutoStyle "
556                    "pedalSustainStyle pedalUnaCordaStyle",
557                    /* write */       "");