]> git.donarmstrong.com Git - lilypond.git/blob - lily/piano-pedal-engraver.cc
* lily/include/grob-info.hh (class Grob_info): make data member
[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 = _f ("expect 3 strings for piano pedals, found: %d",
225                        scm_ilength (strings));
226       if (m)
227         m->origin ()->warning (msg);
228       else
229         warning (msg);
230
231       return;
232     }
233
234   if (p->event_drul_[STOP] && p->event_drul_[START])
235     {
236       if (!mixed)
237         {
238           if (!p->start_ev_)
239             {
240               p->event_drul_[STOP]->origin ()->warning (_f ("can't find start of piano pedal: `%s'", p->name_));
241             }
242           else
243             {
244               s = scm_cadr (strings);
245             }
246           p->start_ev_ = p->event_drul_[START];
247         }
248     }
249   else if (p->event_drul_[STOP])
250     {
251       if (!mixed)
252         {
253           if (!p->start_ev_)
254             {
255               p->event_drul_[STOP]->origin ()->warning (_f ("can't find start of piano pedal: `%s'", p->name_));
256             }
257           else
258             {
259               s = scm_caddr (strings);
260             }
261           p->start_ev_ = 0;
262         }
263     }
264   else if (p->event_drul_[START])
265     {
266       p->start_ev_ = p->event_drul_[START];
267       s = scm_car (strings);
268       if (!mixed)
269         {
270           /*
271             Code dup?! see below.
272           */
273           if (previous_.size ())
274             // add extra space below the previous already-occuring pedal
275             Side_position_interface::add_support (p->line_spanner_,
276                                                   previous_.top ());
277           previous_.push (p->line_spanner_);
278         }
279     }
280
281   if (scm_is_string (s))
282     {
283       String propname = String (p->name_) + "Pedal";
284
285       p->item_ = make_item (propname.to_str0 (), (p->event_drul_[START]
286                                                   ? p->event_drul_[START]
287                                                   : p->event_drul_[STOP])->self_scm ());
288
289       p->item_->set_property ("text", s);
290       Axis_group_interface::add_element (p->line_spanner_, p->item_);
291     }
292
293   if (!mixed)
294     {
295       p->event_drul_[START] = 0;
296       p->event_drul_[STOP] = 0;
297     }
298 }
299
300 void
301 Piano_pedal_engraver::create_bracket_grobs (Pedal_info *p, bool mixed)
302 {
303   if (!p->bracket_ && p->event_drul_[STOP])
304     {
305       String msg = _f ("can't find start of piano pedal bracket: `%s'", p->name_);
306       p->event_drul_[STOP]->origin ()->warning (msg);
307       p->event_drul_[STOP] = 0;
308     }
309
310   if (p->event_drul_[STOP])
311     {
312       assert (!p->finished_bracket_);
313
314       Grob *cmc = unsmob_grob (get_property ("currentMusicalColumn"));
315
316       if (!p->bracket_->get_bound (RIGHT))
317         p->bracket_->set_bound (RIGHT, cmc);
318
319       /*
320         Set properties so that the stencil-creating function will
321         know whether the right edge should be flared ___/
322       */
323
324       if (!p->event_drul_[START])
325         {
326           SCM flare = p->bracket_->get_property ("bracket-flare");
327           p->bracket_->set_property ("bracket-flare", scm_cons (scm_car (flare),
328                                                                 scm_make_real (0)));
329         }
330
331       p->finished_bracket_ = p->bracket_;
332       p->bracket_ = 0;
333       p->current_bracket_ev_ = 0;
334     }
335
336   if (p->event_drul_[START])
337     {
338       p->start_ev_ = p->event_drul_[START];
339       p->current_bracket_ev_ = p->event_drul_[START];
340
341       p->bracket_ = make_spanner ("PianoPedalBracket", p->event_drul_[START]->self_scm ());
342
343       /*
344         Set properties so that the stencil-creating function will
345         know whether the left edge should be flared \___
346       */
347
348       if (!p->finished_bracket_)
349         {
350           SCM flare = p->bracket_->get_property ("bracket-flare");
351           p->bracket_->set_property ("bracket-flare", scm_cons (scm_make_real (0), scm_cdr (flare)));
352         }
353
354       /* Set this property for 'mixed style' pedals,    Ped._______/\ ,
355          so the stencil function will shorten the ____ line by the length of the Ped. text.
356       */
357
358       if (mixed)
359         {
360           /*
361             Mixed style: Store a pointer to the preceding text for use in
362             calculating the length of the line
363
364
365             TODO:
366
367             WTF is pedal-text not the bound of the object? --hwn
368           */
369           if (p->item_)
370             p->bracket_->set_property ("pedal-text", p->item_->self_scm ());
371         }
372
373       /*
374         We do not use currentMusicalColumn for the left span-point.
375         If the column as accidentals (eg on a different stave), the
376         currentMusicalColumn is too wide, making the bracket too big.
377
378         TODO:
379
380         Hmm. What do we do when there are no notes when the spanner starts?
381
382         TODO:
383
384         what about the right span point?
385
386       */
387       Axis_group_interface::add_element (p->line_spanner_, p->bracket_);
388
389       if (!p->event_drul_[STOP])
390         {
391
392           /*
393             code dup. --hwn.
394
395             // position new pedal spanner below the current one
396             */
397           if (previous_.size ())
398             Side_position_interface::add_support (p->line_spanner_, previous_.top ());
399
400           previous_.push (p->line_spanner_);
401         }
402     }
403
404   p->event_drul_[START] = 0;
405   p->event_drul_[STOP] = 0;
406 }
407
408 void
409 Piano_pedal_engraver::finalize ()
410 {
411   for (Pedal_info *p = info_list_; p && p->name_; p++)
412     {
413       /*
414         suicide?
415       */
416       if (p->line_spanner_
417           && !p->line_spanner_->is_live ())
418         p->line_spanner_ = 0;
419
420       if (p->bracket_
421           && !p->bracket_->is_live ())
422         p->bracket_ = 0;
423
424       if (p->bracket_)
425         {
426           SCM cc = get_property ("currentCommandColumn");
427           Item *c = unsmob_item (cc);
428           if (p->line_spanner_)
429             {
430               p->line_spanner_->set_bound (RIGHT, c);
431             }
432           p->bracket_->set_bound (RIGHT, c);
433
434           p->finished_bracket_ = p->bracket_;
435           p->bracket_ = 0;
436           p->finished_line_spanner_ = p->line_spanner_;
437           p->line_spanner_ = 0;
438           typeset_all (p);
439         }
440
441       if (p->line_spanner_)
442         {
443           p->finished_line_spanner_ = p->line_spanner_;
444           typeset_all (p);
445         }
446     }
447 }
448
449 void
450 Piano_pedal_engraver::del_linespanner (Spanner *g)
451 {
452   int idx = previous_.find_index (g);
453   if (idx >= 0)
454     previous_.del (idx);
455 }
456
457 void
458 Piano_pedal_engraver::stop_translation_timestep ()
459 {
460   for (Pedal_info *p = info_list_; p && p->name_; p++)
461     {
462       if (!p->bracket_)
463         {
464           p->finished_line_spanner_ = p->line_spanner_;
465           p->line_spanner_ = 0;
466           del_linespanner (p->finished_line_spanner_);
467         }
468
469       typeset_all (p);
470     }
471
472   for (Pedal_info *p = info_list_; p->name_; p++)
473     {
474       p->event_drul_[STOP] = 0;
475       p->event_drul_[START] = 0;
476     }
477 }
478
479 void
480 Piano_pedal_engraver::typeset_all (Pedal_info *p)
481 {
482   /*
483     Handle suicide.
484   */
485   if (p->finished_line_spanner_
486       && !p->finished_line_spanner_->is_live ())
487     p->finished_line_spanner_ = 0;
488   if (p->finished_bracket_
489       && !p->finished_bracket_->is_live ())
490     p->finished_bracket_ = 0;
491
492   if (p->item_)
493     {
494       p->item_ = 0;
495     }
496
497   if (p->finished_bracket_)
498     {
499       Grob *r = p->finished_bracket_->get_bound (RIGHT);
500       if (!r)
501         {
502           p->finished_bracket_->set_bound (RIGHT, unsmob_grob (get_property ("currentMusicalColumn")));
503         }
504
505       p->finished_bracket_ = 0;
506     }
507
508   if (p->finished_line_spanner_)
509     {
510       Grob *l = p->finished_line_spanner_->get_bound (LEFT);
511       Grob *r = p->finished_line_spanner_->get_bound (RIGHT);
512       if (!r && l)
513         p->finished_line_spanner_->set_bound (RIGHT, l);
514       else if (!l && r)
515         p->finished_line_spanner_->set_bound (LEFT, r);
516       else if (!r && !l)
517         {
518           Grob *cc = unsmob_grob (get_property ("currentMusicalColumn"));
519           Item *ci = dynamic_cast<Item *> (cc);
520           p->finished_line_spanner_->set_bound (RIGHT, ci);
521           p->finished_line_spanner_->set_bound (LEFT, ci);
522         }
523
524       p->finished_line_spanner_ = 0;
525     }
526 }
527
528 ADD_TRANSLATOR (Piano_pedal_engraver,
529                 /* descr */ "Engrave piano pedal symbols and brackets.",
530                 /* creats*/ "SostenutoPedal SustainPedal UnaCordaPedal SostenutoPedalLineSpanner SustainPedalLineSpanner UnaCordaPedalLineSpanner",
531                 /* accepts */ "pedal-event",
532                 /* acks  */ "note-column-interface",
533                 /* reads */ "currentCommandColumn "
534                 "pedalSostenutoStrings pedalSustainStrings "
535                 "pedalUnaCordaStrings pedalSostenutoStyle "
536                 "pedalSustainStyle pedalUnaCordaStyle",
537                 /* write */ "");