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