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