]> git.donarmstrong.com Git - lilypond.git/blob - lily/dynamic-engraver.cc
release: 1.3.109
[lilypond.git] / lily / dynamic-engraver.cc
1 /*
2   dynamic-engraver.cc -- implement Dynamic_engraver
3
4   source file of the GNU LilyPond music typesetter
5
6   (c)  1997--2000 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7 */
8 #include "debug.hh"
9 #include "dimensions.hh"
10 #include "crescendo.hh"
11 #include "musical-request.hh"
12 #include "paper-column.hh"
13 #include "note-column.hh"
14 #include "item.hh"
15 #include "side-position-interface.hh"
16 #include "engraver.hh"
17 #include "group-interface.hh"
18 #include "directional-element-interface.hh"
19 #include "translator-group.hh"
20 #include "axis-group-interface.hh"
21
22
23 /*
24   TODO:
25
26   * direction of text-dynamic-request if not equal to direction of
27   line-spanner
28 */
29
30 /**
31    print text & hairpin dynamics.
32  */
33 class Dynamic_engraver : public Engraver
34 {
35   Item * script_p_;
36   Spanner * finished_cresc_p_;
37   Spanner * cresc_p_;
38
39   Text_script_req* script_req_l_;
40   
41   Span_req * current_cresc_req_;
42   Drul_array<Span_req*> accepted_spanreqs_drul_;
43
44   Spanner* line_spanner_;
45   Spanner* finished_line_spanner_;
46
47   Link_array<Note_column> pending_column_arr_;
48   Link_array<Grob> pending_element_arr_;
49   
50   void typeset_all ();
51
52 public:
53   VIRTUAL_COPY_CONS(Translator);
54   Dynamic_engraver ();
55   
56 protected:
57   virtual void do_removal_processing ();
58   virtual void acknowledge_grob (Grob_info);
59   virtual bool try_music (Music *req_l);
60   void deprecated_process_music ();
61   virtual void stop_translation_timestep ();
62   virtual void create_grobs ();
63   virtual void start_translation_timestep ();
64 };
65
66 ADD_THIS_TRANSLATOR (Dynamic_engraver);
67
68
69 Dynamic_engraver::Dynamic_engraver ()
70 {
71   script_p_ = 0;
72   finished_cresc_p_ = 0;
73   line_spanner_ = 0;
74   finished_line_spanner_ = 0;
75   current_cresc_req_ = 0;
76   cresc_p_ =0;
77
78   script_req_l_ = 0;
79   accepted_spanreqs_drul_[START] = 0;
80   accepted_spanreqs_drul_[STOP] = 0;
81 }
82
83 void
84 Dynamic_engraver::start_translation_timestep ()
85 {
86   script_req_l_ = 0;
87   accepted_spanreqs_drul_[START] = 0;
88   accepted_spanreqs_drul_[STOP] = 0;
89 }
90
91 bool
92 Dynamic_engraver::try_music (Music * m)
93 {
94   if (dynamic_cast <Text_script_req*> (m)
95       && m->get_mus_property ("text-type") == ly_symbol2scm ("dynamic"))
96     {
97       script_req_l_ = dynamic_cast<Text_script_req*> (m);
98       return true;
99     }
100   else if (Span_req* s =  dynamic_cast <Span_req*> (m))
101     {
102       String t = ly_scm2string (s->get_mus_property ("span-type"));
103       if (t== "abort")
104         {
105           accepted_spanreqs_drul_[LEFT] = 0;
106           accepted_spanreqs_drul_[RIGHT] = 0;
107           if (line_spanner_)
108             line_spanner_->suicide ();
109           line_spanner_ = 0;
110           if (cresc_p_)
111             cresc_p_->suicide ();
112           cresc_p_ = 0;
113         }
114       else if (t == "crescendo"
115            || t == "decrescendo")
116         {
117           accepted_spanreqs_drul_[s->get_span_dir()] = s;
118           return true;
119         }
120     }
121   return false;
122 }
123
124 void
125 Dynamic_engraver::deprecated_process_music ()
126 {
127   if (accepted_spanreqs_drul_[START] || accepted_spanreqs_drul_[STOP] || script_req_l_)
128     
129     {
130       if (!line_spanner_)
131         {
132           line_spanner_ = new Spanner (get_property ("DynamicLineSpanner"));
133
134           Side_position::set_axis (line_spanner_, Y_AXIS);
135           Axis_group_interface::set_interface (line_spanner_);
136           Axis_group_interface::set_axes (line_spanner_, Y_AXIS, Y_AXIS);
137
138           Music * rq = accepted_spanreqs_drul_[START];
139           if (script_req_l_)
140             rq =  script_req_l_ ;
141           announce_grob (line_spanner_, rq);
142                          
143
144         }
145     }
146
147   /*
148     finish side position alignment if the (de)cresc ends here, and
149     there are no new dynamics.
150     
151    */
152   else if (accepted_spanreqs_drul_[STOP]
153            && !accepted_spanreqs_drul_[START] && !script_req_l_)
154     {
155       finished_line_spanner_ = line_spanner_;
156       line_spanner_ = 0;
157     }
158
159         /*
160         todo: resurrect  dynamic{direction, padding,minimumspace}
161         */
162         /*
163         During a (de)crescendo, pending request will not be cleared,
164         and a line-spanner will always be created, as \< \! are already
165         two requests.
166
167         Maybe always creating a line-spanner for a (de)crescendo (see
168         below) is not a good idea:
169
170             a\< b\p \!c
171
172         the \p will be centred on the line-spanner, and thus clash
173         with the hairpin.  When axis-group code is in place, the \p
174         should move below the hairpin, which is probably better?
175
176         Urg, but line-spanner must always have at least same duration
177         as (de)crecsendo, b.o. line-breaking.
178         */
179
180   
181
182   /*
183     maybe we should leave dynamic texts to the text-engraver and
184     simply acknowledge them?
185   */
186   if (script_req_l_)
187     {
188       script_p_ = new Item (get_property ("DynamicText"));
189       script_p_->set_grob_property ("text",
190                                    script_req_l_->get_mus_property ("text"));
191       if (Direction d = script_req_l_->get_direction ())
192         Directional_element_interface::set (line_spanner_, d);
193
194       Axis_group_interface::add_element (line_spanner_, script_p_);
195
196       announce_grob (script_p_, script_req_l_);
197     }
198
199   if (accepted_spanreqs_drul_[STOP])
200     {
201       if (!cresc_p_)
202         {
203           accepted_spanreqs_drul_[STOP]->origin ()->warning
204             (_ ("can't find start of (de)crescendo"));
205         }
206       else
207         {
208           assert (!finished_cresc_p_);
209           Grob* cc = unsmob_element (get_property ("currentMusicalColumn"));
210           
211           cresc_p_->set_bound (RIGHT, cc);
212
213           finished_cresc_p_ = cresc_p_;
214           cresc_p_ = 0;
215           current_cresc_req_ = 0;
216         }
217     }
218
219   if (accepted_spanreqs_drul_[START])
220     {
221       if (current_cresc_req_)
222         {
223           accepted_spanreqs_drul_[START]->origin ()->warning
224             (current_cresc_req_->get_span_dir() == 1
225              ? _ ("already have a crescendo")
226              : _ ("already have a decrescendo"));
227         }
228       else
229         {
230           current_cresc_req_ = accepted_spanreqs_drul_[START];
231
232           /*
233             TODO: Use symbols.
234            */
235
236           String start_type = ly_scm2string (accepted_spanreqs_drul_[START]->get_mus_property ("span-type"));
237
238           /*
239             ugh. Use push/pop?
240            */
241           SCM s = get_property ((start_type + "Spanner").ch_C());
242           if (!gh_string_p (s) || ly_scm2string (s) == "hairpin")
243             {
244               cresc_p_  = new Spanner (get_property ("Crescendo"));
245               cresc_p_->set_grob_property ("grow-direction",
246                                           gh_int2scm ((start_type == "crescendo")
247                                                       ? BIGGER : SMALLER));
248               
249             }
250           /*
251             This is a convenient (and legacy) interface to TextSpanners
252             for use in (de)crescendi.
253             Hmm.
254            */
255           else
256             {
257               cresc_p_  = new Spanner (get_property ("TextSpanner"));
258               cresc_p_->set_grob_property ("type", s);
259               daddy_trans_l_->set_property (start_type
260                                             + "Spanner", SCM_UNDEFINED);
261               s = get_property ((start_type + "Text").ch_C());
262               if (gh_string_p (s))
263                 {
264                   cresc_p_->set_grob_property ("edge-text",
265                                               gh_cons (s, ly_str02scm ("")));
266                   daddy_trans_l_->set_property (start_type + "Text",
267                                                 SCM_UNDEFINED);
268                 }
269             }
270           cresc_p_->set_bound (LEFT, script_p_
271                                ? script_p_
272                                : unsmob_element (get_property ("currentMusicalColumn")));
273           
274           Axis_group_interface::add_element (line_spanner_, cresc_p_);
275           announce_grob (cresc_p_, accepted_spanreqs_drul_[START]);
276         }
277     }
278   script_req_l_ = 0;
279   accepted_spanreqs_drul_[START] = 0;
280   accepted_spanreqs_drul_[STOP] = 0;
281 }
282
283 void
284 Dynamic_engraver::stop_translation_timestep ()
285 {
286   typeset_all ();
287 }
288
289 void
290 Dynamic_engraver::create_grobs ()
291 {
292   deprecated_process_music ();
293 }
294
295 void
296 Dynamic_engraver::do_removal_processing ()
297 {
298   typeset_all ();
299   if (line_spanner_)
300     {
301       finished_line_spanner_ = line_spanner_;
302       typeset_all ();
303     }
304
305   if (cresc_p_)
306     {
307       current_cresc_req_->origin ()->warning (_ ("unterminated (de)crescendo"));
308       cresc_p_->suicide ();
309       cresc_p_ = 0;
310     }
311 }
312
313 void
314 Dynamic_engraver::typeset_all ()
315 {  
316   if (finished_cresc_p_)
317     {
318       finished_cresc_p_->set_bound (RIGHT, script_p_
319                            ? script_p_
320                            : unsmob_element (get_property ("currentMusicalColumn")));
321                 
322       typeset_grob (finished_cresc_p_);
323       finished_cresc_p_ =0;
324     }
325   
326   if (script_p_)
327     {
328       typeset_grob (script_p_);
329       script_p_ = 0;
330     }
331   if (finished_line_spanner_)
332     {
333       Side_position::add_staff_support (finished_line_spanner_);
334       extend_spanner_over_elements (finished_line_spanner_);
335       typeset_grob (finished_line_spanner_);
336       finished_line_spanner_ = 0;
337     }
338 }
339
340 void
341 Dynamic_engraver::acknowledge_grob (Grob_info i)
342 {
343   if (Note_column::has_interface (i.elem_l_))
344     {
345       if (line_spanner_)
346         {
347           Side_position::add_support (line_spanner_,i.elem_l_);
348           add_bound_item (line_spanner_,dynamic_cast<Item*>(i.elem_l_));
349         }
350     }
351 }