]> git.donarmstrong.com Git - lilypond.git/blob - lily/dynamic-engraver.cc
release: 1.3.55
[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 "dimension-cache.hh"
11 #include "crescendo.hh"
12 #include "musical-request.hh"
13 #include "lookup.hh"
14 #include "paper-def.hh"
15 #include "paper-column.hh"
16 #include "staff-symbol.hh"
17 #include "note-column.hh"
18 #include "text-item.hh"
19 #include "side-position-interface.hh"
20 #include "engraver.hh"
21 #include "stem.hh"
22 #include "note-head.hh"
23 #include "group-interface.hh"
24 #include "directional-element-interface.hh"
25 #include "staff-symbol-referencer.hh"
26 #include "translator-group.hh"
27 #include "axis-group-interface.hh"
28
29
30 /*
31   TODO:
32
33   * direction of text-dynamic-request if not equalt to direction
34   of line-spanner
35
36   * FIXME: this has gotten a bit too hairy.
37  */
38
39 class Dynamic_line_spanner : public Spanner
40 {
41 public:
42   Dynamic_line_spanner (SCM);
43   VIRTUAL_COPY_CONS(Score_element);
44   void add_column (Note_column*);
45   void add_element (Score_element*);
46 };
47
48 Dynamic_line_spanner::Dynamic_line_spanner (SCM s)
49   : Spanner (s)
50 {
51   set_elt_property ("transparent", SCM_BOOL_T);
52   Side_position_interface (this).set_axis (Y_AXIS);
53   Axis_group_interface (this).set_interface ();
54   Axis_group_interface (this).set_axes (X_AXIS, Y_AXIS);
55 }
56
57 void
58 Dynamic_line_spanner::add_column (Note_column* n)
59 {
60   if (!get_bound (LEFT))
61     set_bound (LEFT, n);
62   else
63     set_bound (RIGHT, n);
64
65   add_dependency (n);
66 }
67
68
69 void
70 Dynamic_line_spanner::add_element (Score_element* e)
71 {
72   e->set_parent (this, Y_AXIS);
73   Axis_group_interface (this).add_element (e); 
74 }
75
76 /**
77    print text & hairpin dynamics.
78  */
79 class Dynamic_engraver : public Engraver
80 {
81   Text_item * text_p_;
82   Crescendo * finished_cresc_p_;
83   Crescendo * cresc_p_;
84
85   Text_script_req* text_req_l_;
86   Span_req * span_start_req_l_;
87   Drul_array<Span_req*> span_req_l_drul_;
88
89   Dynamic_line_spanner* line_spanner_;
90   Dynamic_line_spanner* finished_line_spanner_;
91   Moment last_request_mom_;
92
93   Array<Note_column*> pending_column_arr_;
94   Link_array<Score_element> pending_element_arr_;
95   
96   void  typeset_all ();
97
98 public:
99   VIRTUAL_COPY_CONS(Translator);
100   Dynamic_engraver ();
101   
102 protected:
103   void announce_element (Score_element_info);
104   
105   virtual void do_removal_processing ();
106   virtual void acknowledge_element (Score_element_info);
107   virtual bool do_try_music (Music *req_l);
108   virtual void do_process_music ();
109   virtual void do_pre_move_processing ();
110   virtual void do_post_move_processing ();
111 };
112
113 ADD_THIS_TRANSLATOR (Dynamic_engraver);
114
115 void
116 Dynamic_engraver::announce_element (Score_element_info i)
117 {
118   Group_interface (i.elem_l_, "interfaces").add_thing (ly_symbol2scm ("dynamic"));
119   
120   Engraver::announce_element (i);
121 }
122
123
124 Dynamic_engraver::Dynamic_engraver ()
125 {
126   text_p_ = 0;
127   finished_cresc_p_ = 0;
128   line_spanner_ = 0;
129   finished_line_spanner_ = 0;
130   span_start_req_l_ = 0;
131   cresc_p_ =0;
132
133   text_req_l_ = 0;
134   span_req_l_drul_[START] = 0;
135   span_req_l_drul_[STOP] = 0;
136 }
137
138 void
139 Dynamic_engraver::do_post_move_processing ()
140 {
141   text_req_l_ = 0;
142   span_req_l_drul_[START] = 0;
143   span_req_l_drul_[STOP] = 0;
144 }
145
146 bool
147 Dynamic_engraver::do_try_music (Music * m)
148 {
149   if (Text_script_req* d = dynamic_cast <Text_script_req*> (m))
150     {
151       if (d->style_str_ == "dynamic")
152         {
153           text_req_l_ = d;
154           return true;
155         }
156     }
157   else if (Span_req* s =  dynamic_cast <Span_req*> (m))
158     {
159       if ((s->span_type_str_ == "crescendo"
160            || s->span_type_str_ == "decrescendo"))
161         {
162           span_req_l_drul_[s->span_dir_] = s;
163           return true;
164         }
165     }
166   return false;
167 }
168
169 void
170 Dynamic_engraver::do_process_music ()
171 {
172   if ((span_req_l_drul_[START] || span_req_l_drul_[STOP] || text_req_l_)
173       && !line_spanner_
174       && pending_element_arr_.size ())
175     {
176       line_spanner_ = new Dynamic_line_spanner (get_property ("basicDynamicLineSpannerProperties"));
177       for (int i = 0; i < pending_column_arr_.size (); i++)
178         line_spanner_->add_column (pending_column_arr_[i]);
179       pending_column_arr_.clear ();
180       announce_element (Score_element_info
181                         (line_spanner_,
182                          text_req_l_ ? text_req_l_ : span_req_l_drul_[START]));
183
184     }
185
186   if (line_spanner_ && pending_element_arr_.size ())
187     {
188       for (int i = 0; i < pending_element_arr_.size (); i++)
189         line_spanner_->add_element (pending_element_arr_[i]);
190       pending_element_arr_.clear ();
191     }
192
193   /*
194     TODO: This should be optionised:
195       * break when group of dynamic requests ends
196       * break now  (only if no cresc. in progress)
197       * continue through piece */
198   if (span_req_l_drul_[START] || span_req_l_drul_[STOP] || text_req_l_)
199     {
200       last_request_mom_ = now_mom ();
201     }
202   else
203     {
204       /*
205         During a (de)crescendo, pending request will not be cleared,
206         and a line-spanner will always be created, as \< \! are already
207         two requests.
208
209         Maybe always creating a line-spanner for a (de)crescendo (see
210         below) is not a good idea:
211
212             a\< b\p \!c
213
214         the \p will be centred on the line-spanner, and thus clash
215         with the hairpin.  When axis-group code is in place, the \p
216         should move below the hairpin, which is probably better?
217
218         Urg, but line-spanner must always have at least same duration
219         as (de)crecsendo, b.o. line-breaking.
220        */
221       if (now_mom () > last_request_mom_ && !span_start_req_l_)
222         {
223           for (int i = 0; i < pending_element_arr_.size (); i++)
224             {
225               Score_element* e = pending_element_arr_[i];
226               Side_position_interface (e).set_axis (Y_AXIS);
227               Side_position_interface (e).add_staff_support ();
228
229               /*
230                 UGH UGH 
231               */
232               Direction d = directional_element (e).get ();
233               if (!d)
234                 {
235                   SCM s = get_property ("dynamicDirection");
236                   if (!isdir_b (s))
237                     s = get_property ("verticalDirection");
238                   if (isdir_b (s))
239                     d = to_dir (s);
240                   directional_element (e).set (d);
241                 }
242           
243               SCM s = get_property ("dynamicPadding");
244               if (gh_number_p (s))
245                 e->set_elt_property ("padding", s);
246               s = get_property ("dynamicMinimumSpace");
247               if (gh_number_p (s))
248                 e->set_elt_property ("minimum-space", s);
249             }
250           pending_element_arr_.clear ();
251           if (line_spanner_)
252             {
253               for (int i = 0; i < pending_column_arr_.size (); i++)
254                 line_spanner_->add_column (pending_column_arr_[i]);
255               pending_column_arr_.clear ();
256               finished_line_spanner_ = line_spanner_;
257               line_spanner_ = 0;
258             }
259         }
260     } 
261
262   if (text_req_l_)
263     {
264       String loud = text_req_l_->text_str_;
265
266       text_p_ = new Text_item (get_property ("basicDynamicTextProperties"));
267       text_p_->set_elt_property ("text", ly_str02scm (loud.ch_C ()));
268       if (Direction d=text_req_l_->get_direction ())
269         directional_element (text_p_).set (d);
270       pending_element_arr_.push (text_p_);
271
272       text_p_->add_offset_callback (Side_position_interface::aligned_on_self,
273                                     Y_AXIS);
274       announce_element (Score_element_info (text_p_, text_req_l_));
275     }
276
277   if (span_req_l_drul_[STOP])
278     {
279       if (!cresc_p_)
280         {
281           span_req_l_drul_[STOP]->warning
282             (_ ("can't find start of (de)crescendo"));
283         }
284       else
285         {
286           assert (!finished_cresc_p_);
287           cresc_p_->set_bound (RIGHT, get_staff_info ().musical_pcol_l ());
288           finished_cresc_p_ = cresc_p_;
289           cresc_p_ = 0;
290           span_start_req_l_ = 0;
291         }
292     }
293
294   if (span_req_l_drul_[START])
295     {
296       if (span_start_req_l_)
297         {
298           span_req_l_drul_[START]->warning
299             (span_start_req_l_->span_dir_ == 1
300              ?
301              _ ("already have a crescendo")
302              : _ ("already have a decrescendo"));
303         }
304       else
305         {
306           span_start_req_l_ = span_req_l_drul_[START];
307           cresc_p_  = new Crescendo (SCM_EOL);
308           cresc_p_->set_elt_property
309             ("grow-direction",
310              gh_int2scm ((span_req_l_drul_[START]->span_type_str_ == "crescendo")
311                          ? BIGGER : SMALLER));
312               
313           SCM s = get_property (span_req_l_drul_[START]->span_type_str_ + "Text");
314           if (gh_string_p (s))
315             {
316               cresc_p_->set_elt_property ("start-text", s);
317               daddy_trans_l_->set_property (span_req_l_drul_[START]->span_type_str_
318                                             + "Text", SCM_UNDEFINED);
319             }
320
321           s = get_property (span_req_l_drul_[START]->span_type_str_ + "Spanner");
322
323
324           /*
325             TODO: Use symbols.
326            */
327           if (gh_string_p (s)) //&& ly_scm2string (s) != "hairpin")
328             {
329               cresc_p_->set_elt_property ("spanner", s);
330               daddy_trans_l_->set_property (span_req_l_drul_[START]->span_type_str_
331                                             + "Spanner", SCM_UNDEFINED);
332             }
333
334           cresc_p_->set_bound (LEFT, get_staff_info ().musical_pcol_l ());
335
336
337           /* 
338               We know how wide the text is, if we can be sure that the
339               text already has relevant pointers into the paperdef,
340               and it has its font-size property set.
341
342               Since font-size may be set by a context higher up, we
343               can not be sure of the size.
344
345
346               We shouldn't try to do this stuff here, the Item should
347               do it when the score is finished.  We could maybe
348               set a callback to have the Item do the alignment if
349               it is not a special symbol, like Crescendo.
350           */
351
352              
353           if (text_p_)
354             {
355               index_set_cell (cresc_p_->get_elt_property ("dynamic-drul"),
356                               LEFT, SCM_BOOL_T);
357               if (finished_cresc_p_)
358                 index_set_cell (finished_cresc_p_->get_elt_property ("dynamic-drul"),
359                                 RIGHT, SCM_BOOL_T);
360             }
361           pending_element_arr_.push (cresc_p_);
362           cresc_p_->set_elt_property ("self-alignment-Y", gh_int2scm (0));
363           cresc_p_->add_offset_callback
364             (Side_position_interface::aligned_on_self, Y_AXIS);
365           announce_element (Score_element_info (cresc_p_, span_req_l_drul_[START]));
366         }
367     }
368 }
369
370 void
371 Dynamic_engraver::do_pre_move_processing ()
372 {
373   typeset_all ();
374 }
375
376 void
377 Dynamic_engraver::do_removal_processing ()
378 {
379   if (cresc_p_)
380     {
381       typeset_element (cresc_p_ );
382       span_start_req_l_->warning (_ ("unterminated (de)crescendo"));
383       cresc_p_ =0;
384     }
385   typeset_all ();
386   if (line_spanner_)
387     {
388       Side_position_interface (line_spanner_).add_staff_support ();
389       typeset_element (line_spanner_);
390       line_spanner_ = 0;
391     }
392 }
393
394 void
395 Dynamic_engraver::typeset_all ()
396 {  
397   if (finished_cresc_p_)
398     {
399       typeset_element (finished_cresc_p_);
400       finished_cresc_p_ =0;
401     }
402   
403   if (text_p_)
404     {
405       typeset_element (text_p_);
406       text_p_ = 0;
407     }
408   if (finished_line_spanner_)
409     {
410       Side_position_interface (finished_line_spanner_).add_staff_support ();
411       typeset_element (finished_line_spanner_);
412       finished_line_spanner_ = 0;
413     }
414 }
415
416 void
417 Dynamic_engraver::acknowledge_element (Score_element_info i)
418 {
419   if (Note_column* n = dynamic_cast<Note_column*> (i.elem_l_))
420     {
421       if (line_spanner_)
422         {
423           Side_position_interface (line_spanner_).add_support (n);
424           line_spanner_->add_column (n);
425         }
426       else
427         {
428           pending_column_arr_.push (n);
429         }
430     }
431 }