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