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