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