]> git.donarmstrong.com Git - lilypond.git/blob - lily/dynamic-engraver.cc
patch::: 1.3.37.jcn1
[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 ();
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 ()
49 {
50   set_elt_property ("transparent", SCM_BOOL_T);
51   side_position (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   Text_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 (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;
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 (e).set_axis (Y_AXIS);
226               side_position (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 Text_item;
266       text_p_->set_elt_property ("text",
267                                           ly_str02scm (loud.ch_C ()));
268       text_p_->set_elt_property ("style", gh_str02scm ("dynamic"));
269       text_p_->set_elt_property ("script-priority",
270                                           gh_int2scm (100));
271       if (Direction d=text_req_l_->get_direction ())
272         directional_element (text_p_).set (d);
273       pending_element_arr_.push (text_p_);
274       text_p_->set_elt_property ("self-alignment-Y", gh_int2scm (0));
275       text_p_->add_offset_callback (Side_position_interface::aligned_on_self,
276                                     Y_AXIS);
277       announce_element (Score_element_info (text_p_, text_req_l_));
278     }
279
280   if (span_req_l_drul_[STOP])
281     {
282       if (!cresc_p_)
283         {
284           span_req_l_drul_[STOP]->warning
285             (_ ("can't find start of (de)crescendo"));
286         }
287       else
288         {
289           assert (!finished_cresc_p_);
290           cresc_p_->set_bound (RIGHT, get_staff_info ().musical_pcol_l ());
291           finished_cresc_p_ = cresc_p_;
292           cresc_p_ = 0;
293           span_start_req_l_ = 0;
294         }
295     }
296
297   if (span_req_l_drul_[START])
298     {
299       if (span_start_req_l_)
300         {
301           span_req_l_drul_[START]->warning
302             (span_start_req_l_->span_dir_ == 1
303              ?
304              _ ("already have a crescendo")
305              : _ ("already have a decrescendo"));
306         }
307       else
308         {
309           span_start_req_l_ = span_req_l_drul_[START];
310           cresc_p_  = new Crescendo;
311           cresc_p_->set_elt_property
312             ("grow-direction",
313              gh_int2scm ((span_req_l_drul_[START]->span_type_str_ == "crescendo")
314                          ? BIGGER : SMALLER));
315               
316           SCM s = get_property (span_req_l_drul_[START]->span_type_str_ + "Text");
317           if (gh_string_p (s))
318             {
319               cresc_p_->set_elt_property ("start-text", s);
320               daddy_trans_l_->set_property (span_req_l_drul_[START]->span_type_str_
321                                             + "Text", SCM_UNDEFINED);
322             }
323
324           s = get_property (span_req_l_drul_[START]->span_type_str_ + "Spanner");
325           if (gh_string_p (s)) //&& ly_scm2string (s) != "hairpin")
326             {
327               cresc_p_->set_elt_property ("spanner", s);
328               daddy_trans_l_->set_property (span_req_l_drul_[START]->span_type_str_
329                                             + "Spanner", SCM_UNDEFINED);
330             }
331
332           cresc_p_->set_bound (LEFT, get_staff_info ().musical_pcol_l ());
333
334
335           /* 
336               We know how wide the text is, if we can be sure that the
337               text already has relevant pointers into the paperdef,
338               and it has its font-size property set.
339
340               Since font-size may be set by a context higher up, we
341               can not be sure of the size.
342
343
344               We shouldn't try to do this stuff here, the Item should
345               do it when the score is finished.  We could maybe
346               set a callback to have the Item do the alignment if
347               it is not a special symbol, like Crescendo.
348           */
349
350              
351           if (text_p_)
352             {
353               index_set_cell (cresc_p_->get_elt_property ("dynamic-drul"),
354                               LEFT, SCM_BOOL_T);
355               if (finished_cresc_p_)
356                 index_set_cell (finished_cresc_p_->get_elt_property ("dynamic-drul"),
357                                 RIGHT, SCM_BOOL_T);
358             }
359           pending_element_arr_.push (cresc_p_);
360           cresc_p_->set_elt_property ("self-alignment-Y", gh_int2scm (0));
361           cresc_p_->add_offset_callback
362             (Side_position_interface::aligned_on_self, Y_AXIS);
363           announce_element (Score_element_info (cresc_p_, span_req_l_drul_[START]));
364         }
365     }
366 }
367
368 void
369 Dynamic_engraver::do_pre_move_processing ()
370 {
371   typeset_all ();
372 }
373
374 void
375 Dynamic_engraver::do_removal_processing ()
376 {
377   if (cresc_p_)
378     {
379       typeset_element (cresc_p_ );
380       span_start_req_l_->warning (_ ("unterminated (de)crescendo"));
381       cresc_p_ =0;
382     }
383   typeset_all ();
384   if (line_spanner_)
385     {
386       side_position (line_spanner_).add_staff_support ();
387       typeset_element (line_spanner_);
388       line_spanner_ = 0;
389     }
390 }
391
392 void
393 Dynamic_engraver::typeset_all ()
394 {  
395   if (finished_cresc_p_)
396     {
397       typeset_element (finished_cresc_p_);
398       finished_cresc_p_ =0;
399     }
400   
401   if (text_p_)
402     {
403       typeset_element (text_p_);
404       text_p_ = 0;
405     }
406   if (finished_line_spanner_)
407     {
408       side_position (finished_line_spanner_).add_staff_support ();
409       typeset_element (finished_line_spanner_);
410       finished_line_spanner_ = 0;
411     }
412 }
413
414 void
415 Dynamic_engraver::acknowledge_element (Score_element_info i)
416 {
417   if (Note_column* n = dynamic_cast<Note_column*> (i.elem_l_))
418     {
419       if (line_spanner_)
420         {
421           side_position (line_spanner_).add_support (n);
422           line_spanner_->add_column (n);
423         }
424       else
425         {
426           pending_column_arr_.push (n);
427         }
428     }
429 }