]> git.donarmstrong.com Git - lilypond.git/blob - lily/piano-pedal-engraver.cc
* lily/text-spanner.cc (brew_molecule): use bracket-flare
[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               
148               add_bound_item (p->line_spanner_,info.grob_);
149             }     
150           if (p->bracket_)
151             add_bound_item (p->bracket_,info.grob_);              
152           
153         }
154     }
155 }
156
157 bool
158 Piano_pedal_engraver::try_music (Music *m)
159 {
160   if (m->is_mus_type ("abort-event"))
161     {
162       for (Pedal_info*p = info_list_; p->name_; p ++)
163         {
164           p->event_drul_[START] = 0;
165           p->event_drul_[STOP] = 0;
166           
167           if (p->bracket_)
168             p->bracket_->suicide ();
169           p->bracket_ = 0;
170         }
171     }
172   else if  (m->is_mus_type ("pedal-event"))
173     {
174       for (Pedal_info*p = info_list_; p->name_; p ++)
175         {
176           String nm = p->name_ + String ("Event");
177           if (gh_equal_p (m->get_mus_property ("name") ,
178                           gh_symbol2scm (nm.to_str0())))
179             {
180               Direction d = to_dir (m->get_mus_property ("span-direction"));
181               p->event_drul_[d] = m;
182               return true;
183             }
184         }
185     }
186   return false;
187 }
188
189 void
190 Piano_pedal_engraver::process_music ()
191 {
192   for (Pedal_info*p = info_list_; p && p->name_; p ++)
193     {
194       if (p->event_drul_[STOP] || p->event_drul_[START])
195         {
196           if (!p->line_spanner_)
197             {
198               String name  = String (p->name_) + "PedalLineSpanner";
199               p->line_spanner_ = new Spanner (get_property (name.to_str0 ()));
200
201               
202               Music * rq = (p->event_drul_[START]  ?  p->event_drul_[START]  :  p->event_drul_[STOP]);
203               announce_grob (p->line_spanner_, rq->self_scm ());
204             }
205       
206           /* Choose the appropriate grobs to add to the line spanner
207            These can be text items or text-spanners
208           */
209
210           /*
211             ugh, code dup, should read grob to create from other
212             property.
213
214               bracket: |_________/\____|
215               text:    Ped.     *Ped.  *
216               mixed:   Ped. _____/\____|
217            */
218
219
220           String prop = String ("pedal")  + p->name_ + "Style";
221           SCM style = get_property (prop.to_str0 ());
222           bool mixed = style == ly_symbol2scm ("mixed");
223           if (style == ly_symbol2scm ("text") ||
224               mixed)    
225             {
226               if (! p->item_)
227                 create_text_grobs (p, mixed);
228             }
229           if (style == ly_symbol2scm ("bracket") ||
230               mixed)
231            {
232              create_bracket_grobs (p, mixed);
233            }
234         }
235     }
236 }
237
238 void
239 Piano_pedal_engraver::create_text_grobs (Pedal_info *p, bool mixed)
240 {
241   SCM s = SCM_EOL;
242   SCM strings = get_property ( ("pedal" + String (p->name_) + "Strings").to_str0 ());
243
244   if (scm_ilength (strings) < 3)
245     {
246       Music * m =       p->event_drul_[START]; 
247       if (!m) m = p->event_drul_ [STOP];
248
249       String msg = _ ("Need 3 strings for piano pedals. No pedal made. ");
250       if (m)
251         m->origin()->warning (msg);
252       else
253         warning (msg);
254       
255       return ;
256     }
257   
258   if (p->event_drul_[STOP] && p->event_drul_[START]) 
259     {
260       if (!mixed)
261         {
262           if (!p->start_ev_)
263             {
264               p->event_drul_[STOP]->origin ()->warning (_f ("can't find start of piano pedal: `%s'",  p->name_));
265             }
266           else
267             {
268               s = ly_cadr (strings);
269             }
270           p->start_ev_ = p->event_drul_[START];
271         }
272     }
273   else if (p->event_drul_[STOP])
274     { 
275       if (!mixed)
276         {
277           if (!p->start_ev_)
278             {
279               p->event_drul_[STOP]->origin ()->warning (_f ("can't find start of piano pedal: `%s'", p->name_));
280             }
281           else
282             {
283               s = ly_caddr (strings);
284               if (previous_.size ())
285                 previous_.pop();
286             }
287           p->start_ev_ = 0;
288         }
289     }
290   else if (p->event_drul_[START])
291     {
292       p->start_ev_ = p->event_drul_[START];
293       s = ly_car (strings);
294       if (!mixed)
295         {
296           /*
297             Code dup?! see below.
298           */
299           if (previous_.size ())
300             // add extra space below the previous already-occuring pedal
301             Side_position_interface::add_support (p->line_spanner_,
302                                                   previous_.top ());
303           previous_.push ( p->line_spanner_);
304         }
305     }
306       
307   if (gh_string_p (s))
308     {
309       String propname = String (p->name_) + "Pedal";
310
311       SCM b = get_property (propname.to_str0 ());
312       p->item_ = new Item (b);
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       p->bracket_->set_bound (RIGHT, cmc);
351
352       /*
353         Set properties so that the molecule-creating function will
354         know whether the right edge should be flared ___/
355        */
356
357       if (!p->event_drul_[START])
358         {
359           SCM flare = p->bracket_->get_grob_property ("bracket-flare");
360           p->bracket_->set_grob_property ("bracket-flare", scm_cons (gh_car (flare),
361                                                                      gh_double2scm (0)));
362         }
363
364       p->finished_bracket_ = p->bracket_;
365       p->bracket_ = 0;
366       p->current_bracket_ev_ = 0;
367     }
368
369   if (p->event_drul_[START])
370     {
371       p->start_ev_ = p->event_drul_[START];
372       p->current_bracket_ev_ = p->event_drul_[START];
373
374       p->bracket_  = new Spanner (get_property ("PianoPedalBracket"));
375
376       /*
377         Set properties so that the molecule-creating function will
378         know whether the left edge should be flared \___
379       */
380
381       if (!p->finished_bracket_)
382         {
383           SCM flare = p->bracket_->get_grob_property ("bracket-flare");
384           p->bracket_->set_grob_property ("bracket-flare", scm_cons (gh_double2scm (0),gh_cdr (flare)));
385         }
386
387
388       /* Set this property for 'mixed style' pedals,    Ped._______/\ ,  
389         so the molecule function will shorten the ____ line by the length of the Ped. text.
390       */
391
392       if (mixed)
393         {
394           /*
395             Mixed style: Store a pointer to the preceding text for use in
396             calculating the length of the line
397
398
399             TODO:
400
401             WTF is pedal-text not the bound of the object? --hwn
402           */
403           if (p->item_)
404             p->bracket_->set_grob_property ("pedal-text", p->item_->self_scm ());
405         }
406
407       p->bracket_->set_bound (LEFT, unsmob_grob (get_property ("currentMusicalColumn")));
408       Axis_group_interface::add_element (p->line_spanner_, p->bracket_);              
409
410       add_bound_item (p->line_spanner_, p->bracket_->get_bound (LEFT));
411       announce_grob (p->bracket_, p->event_drul_[START]->self_scm ());
412
413       if (!p->event_drul_[STOP])
414         {
415
416           /*
417             code dup. --hwn.
418
419             // position new pedal spanner below the current one
420           */
421           if (previous_.size()) 
422             Side_position_interface::add_support (p->line_spanner_, previous_.top());
423
424           previous_.push (p->line_spanner_);    
425         }
426     }
427
428   p->event_drul_[START] = 0;
429   p->event_drul_[STOP] = 0;
430 }
431
432 void
433 Piano_pedal_engraver::finalize ()
434 {  
435   for (Pedal_info*p = info_list_; p && p->name_; p ++)
436     {
437       /*
438         suicide?
439        */
440       if (p->line_spanner_
441           && !p->line_spanner_->live())
442         p->line_spanner_ = 0;
443       
444       if (p->line_spanner_)
445         {
446           p->finished_line_spanner_ = p->line_spanner_;
447           typeset_all ();
448         }
449       if (p->bracket_
450           && !p->bracket_->live())
451         p->bracket_ = 0;
452       
453       if (p->bracket_)
454         {
455           p->current_bracket_ev_->origin ()->warning (_ ("unterminated pedal bracket"));
456           p->bracket_->suicide ();
457           p->bracket_ = 0;
458         }
459     }
460 }
461
462   
463 void
464 Piano_pedal_engraver::stop_translation_timestep ()
465 {
466   for (Pedal_info*p = info_list_; p && p->name_; p ++)
467     {
468       if (!p->bracket_)
469         {
470           p->finished_line_spanner_ = p->line_spanner_;
471           p->line_spanner_ = 0;
472         }
473     }
474   
475   typeset_all ();
476 }
477
478
479 void
480 Piano_pedal_engraver::typeset_all ()
481 {
482   Item * sustain = 0;
483   for (Pedal_info*p = info_list_; p->name_; p ++)
484     {
485       /*
486         Handle suicide. 
487        */
488       if (p->finished_line_spanner_
489           && !p->finished_line_spanner_->live ())
490         p->finished_line_spanner_ = 0;
491       if (p->finished_bracket_
492           && !p->finished_bracket_->live())
493         p->finished_bracket_ = 0;
494
495
496       if (p->name_ == String ("Sustain"))
497         sustain = p->item_;
498
499       if (p->item_)
500         {
501           /*
502             Hmm.
503           */
504           if (p->name_ != String ("Sustain"))
505             {
506               if (sustain)
507                 {
508                   Side_position_interface::add_support (p->item_,sustain);
509                 }
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           p->finished_bracket_ =0;
525         }
526
527       if (p->finished_line_spanner_)
528         {
529           Side_position_interface::add_staff_support (p->finished_line_spanner_);
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
549 void
550 Piano_pedal_engraver::start_translation_timestep ()
551 {
552   for (Pedal_info*p = info_list_; p->name_; p ++)
553     {
554       p->event_drul_[STOP] = 0;
555       p->event_drul_[START] = 0;
556     }
557 }
558
559 ENTER_DESCRIPTION (Piano_pedal_engraver,
560 /* descr */       "Engrave piano pedal symbols and brackets.",
561 /* creats*/       "SostenutoPedal SustainPedal UnaCordaPedal SostenutoPedalLineSpanner SustainPedalLineSpanner UnaCordaPedalLineSpanner",
562 /* accepts */     "pedal-event abort-event",
563 /* acks  */       "note-column-interface",
564 /* reads */       "pedalSostenutoStrings pedalSustainStrings pedalUnaCordaStrings pedalSostenutoStyle pedalSustainStyle pedalUnaCordaStyle",
565 /* write */       "");