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