]> git.donarmstrong.com Git - lilypond.git/blob - lily/piano-pedal-engraver.cc
*** empty log message ***
[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                  Erik Sandberg <mandolaerik@gmail.com>
8
9   Chris Jackson <chris@fluffhouse.org.uk> - extended to support
10   bracketed pedals.
11 */
12
13 #include "engraver.hh"
14
15 #include "axis-group-interface.hh"
16 #include "context.hh"
17 #include "directional-element-interface.hh"
18 #include "international.hh"
19 #include "lily-guile.hh"
20 #include "note-column.hh"
21 #include "side-position-interface.hh"
22 #include "staff-symbol-referencer.hh"
23 #include "stream-event.hh"
24 #include "string-convert.hh"
25 #include "warn.hh"
26 #include "protected-scm.hh"
27 #include "translator.icc"
28
29 /*
30   TODO:
31
32   * Junk hardcoded sustain/sostenuto/una_corda distinction;
33     Softcode using (list (sustain-event SustainPedal PianoPedalBracket) ... )
34
35   * Try to use same engraver for dynamics.
36   
37 */
38
39 /* Ugh: This declaration is duplicated in piano-pedal-performer */
40 typedef enum Pedal_type {
41   SOSTENUTO,
42   SUSTAIN,
43   UNA_CORDA,
44   NUM_PEDAL_TYPES
45 };
46
47 /*
48   Static precalculated data (symbols and strings) for the different
49   pedal types
50 */
51 struct Pedal_type_info
52 {
53   string base_name_;
54   SCM event_class_sym_;
55   SCM style_sym_;
56   SCM strings_sym_;
57   
58   const char *pedal_c_str_;
59
60   Pedal_type_info ()
61   {
62     event_class_sym_ = SCM_EOL;
63     style_sym_ = SCM_EOL;
64     strings_sym_ = SCM_EOL;
65     pedal_c_str_ = 0;
66   }
67   void protect ()
68   {
69     scm_gc_protect_object (event_class_sym_);
70     scm_gc_protect_object (style_sym_);
71     scm_gc_protect_object (strings_sym_);
72   }
73 };
74
75 struct Pedal_info
76 {
77   const Pedal_type_info *type_;
78
79   /*
80     Event for currently running pedal.
81   */
82   Stream_event *current_bracket_ev_;
83
84   /*
85     Event for currently starting pedal, (necessary?
86
87     distinct from current_bracket_ev_, since current_bracket_ev_ only
88     necessary for brackets, not for text style.
89   */
90   Stream_event *start_ev_;
91
92   /*
93     Events that were found in this timestep.
94   */
95   Drul_array<Stream_event *> event_drul_;
96   Item *item_;
97   Spanner *bracket_; // A single portion of a pedal bracket
98   Spanner *finished_bracket_;
99 };
100
101 static Pedal_type_info pedal_types_[NUM_PEDAL_TYPES];
102
103 class Piano_pedal_engraver : public Engraver
104 {
105 public:
106   TRANSLATOR_DECLARATIONS (Piano_pedal_engraver);
107
108 protected:
109   virtual void initialize ();
110   virtual void finalize ();
111   DECLARE_TRANSLATOR_LISTENER (sustain);
112   DECLARE_TRANSLATOR_LISTENER (una_corda);
113   DECLARE_TRANSLATOR_LISTENER (sostenuto);
114   DECLARE_ACKNOWLEDGER (note_column);
115   void stop_translation_timestep ();
116   void process_music ();
117
118 private:
119   Pedal_info info_list_[NUM_PEDAL_TYPES + 1];
120
121   void create_text_grobs (Pedal_info *p, bool);
122   void create_bracket_grobs (Pedal_info *p, bool);
123   void typeset_all (Pedal_info *p);
124 };
125
126
127 static void
128 init_pedal_types ()
129 {
130   const char *names [NUM_PEDAL_TYPES];
131   names[SOSTENUTO] = "Sostenuto";
132   names[SUSTAIN] = "Sustain";
133   names[UNA_CORDA] = "UnaCorda";
134
135   for (int i = 0; i < NUM_PEDAL_TYPES; i++)
136     {
137       const char *name = names[i];
138       /* FooBar */
139       string base_name = name;
140       /* foo-bar */
141       string base_ident = "";
142       int prev_pos=0;
143       int cur_pos;
144       for (cur_pos = 1; name[cur_pos]; cur_pos++)
145         if (isupper (name[cur_pos]))
146           {
147             base_ident = base_ident + String_convert::to_lower (string (name, prev_pos, cur_pos - prev_pos)) + "-";
148             prev_pos = cur_pos;
149           }
150       base_ident += String_convert::to_lower (string (name, prev_pos, cur_pos - prev_pos));
151
152       /*
153         be careful, as we don't want to loose references to the _sym_ members.
154        */
155       Pedal_type_info info;
156       info.event_class_sym_ = scm_str2symbol ((base_ident + "-event").c_str ());
157       info.style_sym_ = scm_str2symbol (("pedal" + base_name + "Style").c_str ());
158       info.strings_sym_ = scm_str2symbol (("pedal" + base_name + "Strings").c_str ());
159       
160       info.base_name_ = name;
161       info.pedal_c_str_ = strdup ((base_name + "Pedal").c_str ());
162
163       info.protect ();
164       
165       pedal_types_[i] = info;
166     }
167 }
168
169 ADD_SCM_INIT_FUNC (Piano_pedal_engraver_init_pedal_types_, init_pedal_types);
170
171 Piano_pedal_engraver::Piano_pedal_engraver ()
172 {
173 }
174
175 void
176 Piano_pedal_engraver::initialize ()
177 {
178   for (int i = 0; i < NUM_PEDAL_TYPES; i++)
179     {
180       Pedal_type_info *s = &pedal_types_[i];
181       Pedal_info *info = &info_list_[i];
182
183       info->type_ = s;
184       info->item_ = 0;
185       info->bracket_ = 0;
186       info->finished_bracket_ = 0;
187       info->current_bracket_ev_ = 0;
188       info->event_drul_[START] = 0;
189       info->event_drul_[STOP] = 0;
190       info->start_ev_ = 0;
191     }
192   info_list_[NUM_PEDAL_TYPES].type_ = 0;
193 }
194
195
196 /*
197   Urg: Code dup
198   I'm a script
199 */
200 void
201 Piano_pedal_engraver::acknowledge_note_column (Grob_info info)
202 {
203   for (Pedal_info *p = info_list_; p->type_; p++)
204     {
205       if (p->bracket_)
206         add_bound_item (p->bracket_, info.grob ());
207       if (p->finished_bracket_)
208         add_bound_item (p->finished_bracket_, info.grob ());
209     }
210 }
211
212 IMPLEMENT_TRANSLATOR_LISTENER (Piano_pedal_engraver, sostenuto);
213 void
214 Piano_pedal_engraver::listen_sostenuto (Stream_event *ev)
215 {
216   Direction d = to_dir (ev->get_property ("span-direction"));
217   ASSIGN_EVENT_ONCE (info_list_[SOSTENUTO].event_drul_[d], ev);
218 }
219
220 IMPLEMENT_TRANSLATOR_LISTENER (Piano_pedal_engraver, sustain);
221 void
222 Piano_pedal_engraver::listen_sustain (Stream_event *ev)
223 {
224   Direction d = to_dir (ev->get_property ("span-direction"));
225   ASSIGN_EVENT_ONCE (info_list_[SUSTAIN].event_drul_[d], ev);
226 }
227
228 IMPLEMENT_TRANSLATOR_LISTENER (Piano_pedal_engraver, una_corda);
229 void
230 Piano_pedal_engraver::listen_una_corda (Stream_event *ev)
231 {
232   Direction d = to_dir (ev->get_property ("span-direction"));
233   ASSIGN_EVENT_ONCE (info_list_[UNA_CORDA].event_drul_[d], ev);
234 }
235
236 void
237 Piano_pedal_engraver::process_music ()
238 {
239   for (Pedal_info *p = info_list_; p->type_; p++)
240     {
241       if (p->event_drul_[STOP] || p->event_drul_[START])
242         {
243           /* Choose the appropriate grobs to add to the line spanner
244              These can be text items or text-spanners
245           */
246
247           /*
248             ugh, code dup, should read grob to create from other
249             property.
250
251             bracket: |_________/\____|
252             text:    Ped.     *Ped.  *
253             mixed:   Ped. _____/\____|
254           */
255
256           SCM style = internal_get_property (p->type_->style_sym_);
257
258           bool mixed = style == ly_symbol2scm ("mixed");
259           bool bracket = (mixed
260                           || style == ly_symbol2scm ("bracket"));
261           bool text = (style == ly_symbol2scm ("text")
262                        || mixed);
263
264           if (text && !p->item_)
265             create_text_grobs (p, mixed);
266           if (bracket)
267             create_bracket_grobs (p, mixed);
268         }
269     }
270 }
271
272 void
273 Piano_pedal_engraver::create_text_grobs (Pedal_info *p, bool mixed)
274 {
275   SCM s = SCM_EOL;
276   SCM strings = internal_get_property (p->type_->strings_sym_);
277
278   if (scm_ilength (strings) < 3)
279     {
280       Stream_event *m = p->event_drul_[START];
281       if (!m) m = p->event_drul_ [STOP];
282
283       string msg = _f ("expect 3 strings for piano pedals, found: %ld",
284                        scm_ilength (strings));
285       if (m)
286         m->origin ()->warning (msg);
287       else
288         warning (msg);
289
290       return;
291     }
292
293   if (p->event_drul_[STOP] && p->event_drul_[START])
294     {
295       if (!mixed)
296         {
297           if (!p->start_ev_)
298             p->event_drul_[STOP]->origin ()->warning (_f ("can't find start of piano pedal: `%s'", p->type_->base_name_.c_str ()));
299           else
300             s = scm_cadr (strings);
301           p->start_ev_ = p->event_drul_[START];
302         }
303     }
304   else if (p->event_drul_[STOP])
305     {
306       if (!mixed)
307         {
308           if (!p->start_ev_)
309             p->event_drul_[STOP]->origin ()->warning (_f ("can't find start of piano pedal: `%s'", p->type_->base_name_.c_str ()));
310           else
311             s = scm_caddr (strings);
312           p->start_ev_ = 0;
313         }
314     }
315   else if (p->event_drul_[START])
316     {
317       p->start_ev_ = p->event_drul_[START];
318       s = scm_car (strings);
319       }
320
321   if (scm_is_string (s))
322     {
323       const char *propname = p->type_->pedal_c_str_;
324
325       p->item_ = make_item (propname, (p->event_drul_[START]
326                                        ? p->event_drul_[START]
327                                        : p->event_drul_[STOP])->self_scm ());
328
329       p->item_->set_property ("text", s);
330     }
331
332   if (!mixed)
333     {
334       p->event_drul_[START] = 0;
335       p->event_drul_[STOP] = 0;
336     }
337 }
338
339 void
340 Piano_pedal_engraver::create_bracket_grobs (Pedal_info *p, bool mixed)
341 {
342   if (!p->bracket_ && p->event_drul_[STOP])
343     {
344       string msg = _f ("can't find start of piano pedal bracket: `%s'", p->type_->base_name_.c_str ());
345       p->event_drul_[STOP]->origin ()->warning (msg);
346       p->event_drul_[STOP] = 0;
347     }
348
349   if (p->event_drul_[STOP])
350     {
351       assert (!p->finished_bracket_);
352
353       Grob *cmc = unsmob_grob (get_property ("currentMusicalColumn"));
354
355       if (!p->bracket_->get_bound (RIGHT))
356         p->bracket_->set_bound (RIGHT, cmc);
357
358       /*
359         Set properties so that the stencil-creating function will
360         know whether the right edge should be flared ___/
361       */
362
363       if (!p->event_drul_[START])
364         {
365           SCM flare = p->bracket_->get_property ("bracket-flare");
366           if (scm_is_pair (flare))
367             p->bracket_->set_property ("bracket-flare", scm_cons (scm_car (flare),
368                                                                   scm_from_double (0)));
369         }
370
371       p->finished_bracket_ = p->bracket_;
372       p->bracket_ = 0;
373
374       announce_end_grob (p->finished_bracket_, p->event_drul_[STOP]->self_scm ());
375       
376       p->current_bracket_ev_ = 0;
377     }
378
379   if (p->event_drul_[START])
380     {
381       p->start_ev_ = p->event_drul_[START];
382       p->current_bracket_ev_ = p->event_drul_[START];
383
384       p->bracket_ = make_spanner ("PianoPedalBracket", p->event_drul_[START]->self_scm ());
385
386       /*
387         Set properties so that the stencil-creating function will
388         know whether the left edge should be flared \___
389       */
390
391       if (!p->finished_bracket_)
392         {
393           SCM flare = p->bracket_->get_property ("bracket-flare");
394           p->bracket_->set_property ("bracket-flare", scm_cons (scm_from_double (0), scm_cdr (flare)));
395         }
396
397       /* Set this property for 'mixed style' pedals,    Ped._______/\ ,
398          so the stencil function will shorten the ____ line by the length of the Ped. text.
399       */
400
401       if (mixed)
402         {
403           /*
404             Mixed style: Store a pointer to the preceding text for use in
405             calculating the length of the line
406
407
408             TODO:
409
410             WTF is pedal-text not the bound of the object? --hwn
411           */
412           if (p->item_)
413             p->bracket_->set_object ("pedal-text", p->item_->self_scm ());
414         }
415     }
416
417   p->event_drul_[START] = 0;
418   p->event_drul_[STOP] = 0;
419 }
420
421 void
422 Piano_pedal_engraver::finalize ()
423 {
424   for (Pedal_info *p = info_list_; p->type_; p++)
425     {
426       if (p->bracket_
427           && !p->bracket_->is_live ())
428         p->bracket_ = 0;
429
430       if (p->bracket_)
431         {
432           SCM cc = get_property ("currentCommandColumn");
433           Item *c = unsmob_item (cc);
434           p->bracket_->set_bound (RIGHT, c);
435
436           p->finished_bracket_ = p->bracket_;
437           p->bracket_ = 0;
438           typeset_all (p);
439         }
440
441     }
442 }
443
444 void
445 Piano_pedal_engraver::stop_translation_timestep ()
446 {
447   for (Pedal_info *p = info_list_; p->type_; p++)
448     {
449       
450       typeset_all (p);
451       if (p->bracket_ && !p->bracket_->get_bound (LEFT))
452         {
453           Grob *cmc = unsmob_grob (get_property ("currentMusicalColumn"));
454
455           if (!p->bracket_->get_bound (LEFT))
456             p->bracket_->set_bound (LEFT, cmc);
457         }
458     }
459
460   for (Pedal_info *p = info_list_; p->type_; p++)
461     {
462       p->event_drul_[STOP] = 0;
463       p->event_drul_[START] = 0;
464     }
465 }
466
467 void
468 Piano_pedal_engraver::typeset_all (Pedal_info *p)
469 {
470   /*
471     Handle suicide.
472   */
473   if (p->finished_bracket_
474       && !p->finished_bracket_->is_live ())
475     p->finished_bracket_ = 0;
476
477   if (p->item_)
478     p->item_ = 0;
479
480   if (p->finished_bracket_)
481     {
482       Grob *r = p->finished_bracket_->get_bound (RIGHT);
483       if (!r)
484         p->finished_bracket_->set_bound (RIGHT, unsmob_grob (get_property ("currentMusicalColumn")));
485
486       p->finished_bracket_ = 0;
487     }
488 }
489
490 ADD_ACKNOWLEDGER (Piano_pedal_engraver, note_column);
491
492 ADD_TRANSLATOR (Piano_pedal_engraver,
493
494                 /* doc */
495                 "Engrave piano pedal symbols and brackets.",
496
497                 /* create */
498                 "PianoPedalBracket "
499                 "SostenutoPedal "
500                 "SustainPedal "
501                 "UnaCordaPedal ",
502
503                 /* read */
504                 "currentCommandColumn "
505                 "pedalSostenutoStrings "
506                 "pedalSostenutoStyle "
507                 "pedalSustainStrings "
508                 "pedalSustainStyle "
509                 "pedalUnaCordaStrings "
510                 "pedalUnaCordaStyle",
511                 
512                 /* write */ "");