]> git.donarmstrong.com Git - lilypond.git/blob - lily/piano-pedal-engraver.cc
* lily/accidental-engraver.cc (acknowledge_grob): add property
[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 (gh_equal_p (m->get_property ("name") ,
167                           gh_symbol2scm (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               p->line_spanner_ = make_spanner (name.to_str0 ());
189
190               Music * rq = (p->event_drul_[START]  ?  p->event_drul_[START]  :  p->event_drul_[STOP]);
191
192               
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
213           bool mixed = style == ly_symbol2scm ("mixed");
214           bool bracket = (mixed
215                           || style == ly_symbol2scm ("bracket"));
216           bool text = (style == ly_symbol2scm ("text")
217                        || mixed);
218           
219           if (text && !p->item_)
220             create_text_grobs (p, mixed);
221           if (bracket)
222             create_bracket_grobs (p, mixed);
223         }
224     }
225 }
226
227 void
228 Piano_pedal_engraver::create_text_grobs (Pedal_info *p, bool mixed)
229 {
230   SCM s = SCM_EOL;
231   SCM strings = get_property ( ("pedal" + String (p->name_) + "Strings").to_str0 ());
232
233   if (scm_ilength (strings) < 3)
234     {
235       Music * m =       p->event_drul_[START]; 
236       if (!m) m = p->event_drul_ [STOP];
237
238       String msg = _ ("Need 3 strings for piano pedals. No pedal made. ");
239       if (m)
240         m->origin ()->warning (msg);
241       else
242         warning (msg);
243       
244       return ;
245     }
246   
247   if (p->event_drul_[STOP] && p->event_drul_[START]) 
248     {
249       if (!mixed)
250         {
251           if (!p->start_ev_)
252             {
253               p->event_drul_[STOP]->origin ()->warning (_f ("can't find start of piano pedal: `%s'",  p->name_));
254             }
255           else
256             {
257               s = ly_cadr (strings);
258             }
259           p->start_ev_ = p->event_drul_[START];
260         }
261     }
262   else if (p->event_drul_[STOP])
263     { 
264       if (!mixed)
265         {
266           if (!p->start_ev_)
267             {
268               p->event_drul_[STOP]->origin ()->warning (_f ("can't find start of piano pedal: `%s'", p->name_));
269             }
270           else
271             {
272               s = ly_caddr (strings);
273             }
274           p->start_ev_ = 0;
275         }
276     }
277   else if (p->event_drul_[START])
278     {
279       p->start_ev_ = p->event_drul_[START];
280       s = ly_car (strings);
281       if (!mixed)
282         {
283           /*
284             Code dup?! see below.
285           */
286           if (previous_.size ())
287             // add extra space below the previous already-occuring pedal
288             Side_position_interface::add_support (p->line_spanner_,
289                                                   previous_.top ());
290           previous_.push (p->line_spanner_);
291         }
292     }
293       
294   if (gh_string_p (s))
295     {
296       String propname = String (p->name_) + "Pedal";
297
298       p->item_ = make_item (propname.to_str0 ());
299       p->item_->set_property ("text", s);
300       Axis_group_interface::add_element (p->line_spanner_, p->item_);
301           
302       announce_grob (p->item_,
303                      (p->event_drul_[START]
304                       ? p->event_drul_[START]
305                       : p->event_drul_[STOP])->self_scm ());
306     }
307
308   if (!mixed)
309     {
310       p->event_drul_[START] = 0;
311       p->event_drul_[STOP] = 0;
312     }
313 }
314
315
316 void
317 Piano_pedal_engraver::create_bracket_grobs (Pedal_info *p, bool mixed)
318 {
319   if (!p->bracket_ && p->event_drul_[STOP])
320     {
321       String msg =_f ("can't find start of piano pedal bracket: `%s'", p->name_);
322       p->event_drul_[STOP]->origin ()->warning (msg);
323       p->event_drul_[STOP] =  0;
324     }
325
326   if (p->event_drul_[STOP])
327     {
328       assert (!p->finished_bracket_); 
329
330       Grob *cmc = unsmob_grob (get_property ("currentMusicalColumn"));
331
332       if (!p->bracket_->get_bound (RIGHT))
333         p->bracket_->set_bound (RIGHT, cmc);
334
335       /*
336         Set properties so that the stencil-creating function will
337         know whether the right edge should be flared ___/
338       */
339
340       if (!p->event_drul_[START])
341         {
342           SCM flare = p->bracket_->get_property ("bracket-flare");
343           p->bracket_->set_property ("bracket-flare", scm_cons (gh_car (flare),
344                                                                 gh_double2scm (0)));
345         }
346
347       p->finished_bracket_ = p->bracket_;
348       p->bracket_ = 0;
349       p->current_bracket_ev_ = 0;
350     }
351
352   if (p->event_drul_[START])
353     {
354       p->start_ev_ = p->event_drul_[START];
355       p->current_bracket_ev_ = p->event_drul_[START];
356
357       p->bracket_  = make_spanner ("PianoPedalBracket");
358
359       /*
360         Set properties so that the stencil-creating function will
361         know whether the left edge should be flared \___
362       */
363
364       if (!p->finished_bracket_)
365         {
366           SCM flare = p->bracket_->get_property ("bracket-flare");
367           p->bracket_->set_property ("bracket-flare", scm_cons (gh_double2scm (0),gh_cdr (flare)));
368         }
369
370
371       /* Set this property for 'mixed style' pedals,    Ped._______/\ ,  
372          so the stencil function will shorten the ____ line by the length of the Ped. text.
373       */
374
375       if (mixed)
376         {
377           /*
378             Mixed style: Store a pointer to the preceding text for use in
379             calculating the length of the line
380
381
382             TODO:
383
384             WTF is pedal-text not the bound of the object? --hwn
385           */
386           if (p->item_)
387             p->bracket_->set_property ("pedal-text", p->item_->self_scm ());
388         }
389
390
391       /*
392         We do not use currentMusicalColumn for the left span-point.
393         If the column as accidentals (eg on a different stave), the
394         currentMusicalColumn is too wide, making the bracket too big.
395
396         TODO:
397
398         Hmm. What do we do when there are no notes when the spanner starts?
399
400         TODO:
401
402         what about the right span point?
403         
404       */
405       Axis_group_interface::add_element (p->line_spanner_, p->bracket_);              
406       announce_grob (p->bracket_, p->event_drul_[START]->self_scm ());
407
408       if (!p->event_drul_[STOP])
409         {
410
411           /*
412             code dup. --hwn.
413
414             // position new pedal spanner below the current one
415             */
416           if (previous_.size ()) 
417             Side_position_interface::add_support (p->line_spanner_, previous_.top ());
418
419           previous_.push (p->line_spanner_);    
420         }
421     }
422
423   p->event_drul_[START] = 0;
424   p->event_drul_[STOP] = 0;
425 }
426
427 void
428 Piano_pedal_engraver::finalize ()
429 {  
430   for (Pedal_info*p = info_list_; p && p->name_; p ++)
431     {
432       /*
433         suicide?
434       */
435       if (p->line_spanner_
436           && !p->line_spanner_->live ())
437         p->line_spanner_ = 0;
438       
439       if (p->bracket_
440           && !p->bracket_->live ())
441         p->bracket_ = 0;
442       
443       if (p->bracket_)
444         {
445           SCM cc = get_property ("currentCommandColumn");
446           Item *c = unsmob_item (cc);
447           if (p->line_spanner_)
448             {
449               p->line_spanner_->set_bound (RIGHT, c);
450             }
451           p->bracket_ ->set_bound (RIGHT, c);
452
453           p->finished_bracket_ = p->bracket_;
454           p->bracket_ = 0;
455           p->finished_line_spanner_ = p->line_spanner_;
456           p->line_spanner_ = 0;
457           typeset_all (p);
458         }
459
460       if (p->line_spanner_)
461         {
462           p->finished_line_spanner_ = p->line_spanner_;
463           typeset_all (p);
464         }
465     }
466 }
467
468 void
469 Piano_pedal_engraver::del_linespanner (Spanner *g)
470 {
471   int idx = previous_.find_index (g);
472   if (idx >= 0)
473     previous_.del (idx);
474 }
475
476 void
477 Piano_pedal_engraver::stop_translation_timestep ()
478 {
479   for (Pedal_info*p = info_list_; p && p->name_; p ++)
480     {
481       if (!p->bracket_)
482         {
483           p->finished_line_spanner_ = p->line_spanner_;
484           p->line_spanner_ = 0;
485           del_linespanner (p->finished_line_spanner_);
486         }
487       
488       typeset_all (p);
489     }
490   
491
492   for (Pedal_info*p = info_list_; p->name_; p ++)
493     {
494       p->event_drul_[STOP] = 0;
495       p->event_drul_[START] = 0;
496     }
497 }
498
499
500 void
501 Piano_pedal_engraver::typeset_all (Pedal_info * p)
502 {
503   /*
504     Handle suicide. 
505   */
506   if (p->finished_line_spanner_
507       && !p->finished_line_spanner_->live ())
508     p->finished_line_spanner_ = 0;
509   if (p->finished_bracket_
510       && !p->finished_bracket_->live ())
511     p->finished_bracket_ = 0;
512
513
514   if (p->item_)
515     {
516       typeset_grob (p->item_);
517       p->item_ = 0;
518     }
519       
520   if (p->finished_bracket_)
521     {
522       Grob * r = p->finished_bracket_->get_bound (RIGHT);      
523       if (!r)
524         {
525           p->finished_bracket_->set_bound (RIGHT, unsmob_grob (get_property ("currentMusicalColumn")));
526         }
527
528       typeset_grob (p->finished_bracket_);
529           
530       p->finished_bracket_ =0;
531     }
532
533   if (p->finished_line_spanner_)
534     {
535       Grob * l = p->finished_line_spanner_->get_bound (LEFT);
536       Grob * r = p->finished_line_spanner_->get_bound (RIGHT);      
537       if (!r && l)
538         p->finished_line_spanner_->set_bound (RIGHT, l);
539       else if (!l && r)
540         p->finished_line_spanner_->set_bound (LEFT, r);
541       else if (!r && !l)
542         {
543           Grob * cc = unsmob_grob (get_property ("currentMusicalColumn"));
544           Item * ci = dynamic_cast<Item*> (cc);
545           p->finished_line_spanner_->set_bound (RIGHT, ci);
546           p->finished_line_spanner_->set_bound (LEFT, ci);        
547         }
548       typeset_grob (p->finished_line_spanner_);
549       p->finished_line_spanner_ = 0;
550     }
551 }
552
553 ENTER_DESCRIPTION (Piano_pedal_engraver,
554                    /* descr */       "Engrave piano pedal symbols and brackets.",
555                    /* creats*/       "SostenutoPedal SustainPedal UnaCordaPedal SostenutoPedalLineSpanner SustainPedalLineSpanner UnaCordaPedalLineSpanner",
556                    /* accepts */     "pedal-event",
557                    /* acks  */       "note-column-interface",
558                    /* reads */       "currentCommandColumn "
559                    "pedalSostenutoStrings pedalSustainStrings "
560                    "pedalUnaCordaStrings pedalSostenutoStyle "
561                    "pedalSustainStyle pedalUnaCordaStyle",
562                    /* write */       "");