]> git.donarmstrong.com Git - lilypond.git/blob - lily/dynamic-engraver.cc
release: 1.3.54
[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_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   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_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 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
326
327           /*
328             TODO: Use symbols.
329            */
330           if (gh_string_p (s)) //&& ly_scm2string (s) != "hairpin")
331             {
332               cresc_p_->set_elt_property ("spanner", s);
333               daddy_trans_l_->set_property (span_req_l_drul_[START]->span_type_str_
334                                             + "Spanner", SCM_UNDEFINED);
335             }
336
337           cresc_p_->set_bound (LEFT, get_staff_info ().musical_pcol_l ());
338
339
340           /* 
341               We know how wide the text is, if we can be sure that the
342               text already has relevant pointers into the paperdef,
343               and it has its font-size property set.
344
345               Since font-size may be set by a context higher up, we
346               can not be sure of the size.
347
348
349               We shouldn't try to do this stuff here, the Item should
350               do it when the score is finished.  We could maybe
351               set a callback to have the Item do the alignment if
352               it is not a special symbol, like Crescendo.
353           */
354
355              
356           if (text_p_)
357             {
358               index_set_cell (cresc_p_->get_elt_property ("dynamic-drul"),
359                               LEFT, SCM_BOOL_T);
360               if (finished_cresc_p_)
361                 index_set_cell (finished_cresc_p_->get_elt_property ("dynamic-drul"),
362                                 RIGHT, SCM_BOOL_T);
363             }
364           pending_element_arr_.push (cresc_p_);
365           cresc_p_->set_elt_property ("self-alignment-Y", gh_int2scm (0));
366           cresc_p_->add_offset_callback
367             (Side_position_interface::aligned_on_self, Y_AXIS);
368           announce_element (Score_element_info (cresc_p_, span_req_l_drul_[START]));
369         }
370     }
371 }
372
373 void
374 Dynamic_engraver::do_pre_move_processing ()
375 {
376   typeset_all ();
377 }
378
379 void
380 Dynamic_engraver::do_removal_processing ()
381 {
382   if (cresc_p_)
383     {
384       typeset_element (cresc_p_ );
385       span_start_req_l_->warning (_ ("unterminated (de)crescendo"));
386       cresc_p_ =0;
387     }
388   typeset_all ();
389   if (line_spanner_)
390     {
391       Side_position_interface (line_spanner_).add_staff_support ();
392       typeset_element (line_spanner_);
393       line_spanner_ = 0;
394     }
395 }
396
397 void
398 Dynamic_engraver::typeset_all ()
399 {  
400   if (finished_cresc_p_)
401     {
402       typeset_element (finished_cresc_p_);
403       finished_cresc_p_ =0;
404     }
405   
406   if (text_p_)
407     {
408       typeset_element (text_p_);
409       text_p_ = 0;
410     }
411   if (finished_line_spanner_)
412     {
413       Side_position_interface (finished_line_spanner_).add_staff_support ();
414       typeset_element (finished_line_spanner_);
415       finished_line_spanner_ = 0;
416     }
417 }
418
419 void
420 Dynamic_engraver::acknowledge_element (Score_element_info i)
421 {
422   if (Note_column* n = dynamic_cast<Note_column*> (i.elem_l_))
423     {
424       if (line_spanner_)
425         {
426           Side_position_interface (line_spanner_).add_support (n);
427           line_spanner_->add_column (n);
428         }
429       else
430         {
431           pending_column_arr_.push (n);
432         }
433     }
434 }