]> git.donarmstrong.com Git - lilypond.git/blob - lily/auto-beam-engraver.cc
* lily/include/translator.icc: new file.
[lilypond.git] / lily / auto-beam-engraver.cc
1 /*
2   auto-beam-engraver.cc -- implement Auto_beam_engraver
3
4   source file of the GNU LilyPond music typesetter
5
6   (c) 1999--2005 Jan Nieuwenhuizen <janneke@gnu.org>
7 */
8
9 #include "engraver.hh"
10 #include "beaming.hh"
11 #include "beam.hh"
12 #include "stem.hh"
13 #include "warn.hh"
14 #include "bar-line.hh"
15 #include "rest.hh"
16 #include "item.hh"
17 #include "spanner.hh"
18 #include "context.hh"
19 #include "duration.hh"
20
21 #include "translator.icc"
22
23
24 class Auto_beam_engraver : public Engraver
25 {
26   TRANSLATOR_DECLARATIONS (Auto_beam_engraver);
27
28 protected:
29   PRECOMPUTED_VIRTUAL void stop_translation_timestep ();
30   PRECOMPUTED_VIRTUAL void start_translation_timestep ();
31   PRECOMPUTED_VIRTUAL void process_music ();
32   virtual bool try_music (Music *);
33   virtual void finalize ();
34   virtual void acknowledge_grob (Grob_info);
35   PRECOMPUTED_VIRTUAL void process_acknowledged ();
36
37 private:
38   bool test_moment (Direction, Moment);
39   void consider_begin (Moment);
40   void consider_end (Moment);
41   Spanner *create_beam ();
42   void begin_beam ();
43   void end_beam ();
44   void junk_beam ();
45   bool is_same_grace_state (Grob *e);
46   void typeset_beam ();
47
48   Music *forbid_;
49   /*
50     shortest_mom is the shortest note in the beam.
51   */
52   Moment shortest_mom_;
53   Spanner *finished_beam_;
54   Link_array<Item> *stems_;
55
56   int process_acknowledged_count_;
57   Moment last_add_mom_;
58   /*
59     Projected ending of the  beam we're working on.
60   */
61   Moment extend_mom_;
62   Moment beam_start_moment_;
63   Moment beam_start_location_;
64
65   bool subdivide_beams_;
66   Moment beat_length_;
67
68   // We act as if beam were created, and start a grouping anyway.
69   Beaming_info_list *grouping_;
70   SCM beam_settings_;           // ugh. should protect ? 
71
72   Beaming_info_list *finished_grouping_;
73 };
74
75 void
76 Auto_beam_engraver::process_music ()
77 {
78   if (scm_is_string (get_property ("whichBar")))
79     {
80       consider_end (shortest_mom_);
81       junk_beam ();
82     }
83
84   if (forbid_)
85     {
86       consider_end (shortest_mom_);
87       junk_beam ();
88     }
89 }
90
91 Auto_beam_engraver::Auto_beam_engraver ()
92 {
93   forbid_ = 0;
94 process_acknowledged_count_ = 0;
95   stems_ = 0;
96   shortest_mom_ = Moment (Rational (1, 8));
97   finished_beam_ = 0;
98   finished_grouping_ = 0;
99   grouping_ = 0;
100   beam_settings_ = SCM_EOL;
101 }
102
103 bool
104 Auto_beam_engraver::try_music (Music *m)
105 {
106   if (m->is_mus_type ("beam-forbid-event"))
107     {
108       forbid_ = m;
109       return true;
110     }
111
112   return false;
113 }
114
115 bool
116 Auto_beam_engraver::test_moment (Direction dir, Moment test)
117 {
118   return scm_call_3 (get_property ("autoBeamCheck"),
119                      context ()->self_scm (),
120                      scm_from_int (dir),
121                      test.smobbed_copy ())
122     != SCM_BOOL_F;
123 }
124     
125 void
126 Auto_beam_engraver::consider_begin (Moment test_mom)
127 {
128   bool on = to_boolean (get_property ("autoBeaming"));
129   if (!stems_ && on
130       && !forbid_)
131     {
132       bool b = test_moment (START, test_mom);
133       if (b)
134         begin_beam ();
135     }
136 }
137
138 void
139 Auto_beam_engraver::consider_end (Moment test_mom)
140 {
141   if (stems_)
142     {
143       /* Allow already started autobeam to end:
144          don't check for autoBeaming */
145       bool b = test_moment (STOP, test_mom);
146       if (b)
147         end_beam ();
148     }
149 }
150
151 Spanner *
152 Auto_beam_engraver::create_beam ()
153 {
154   if (to_boolean (get_property ("skipTypesetting")))
155     return 0;
156
157   Spanner *beam = new Spanner (beam_settings_, context ()->get_grob_key ("Beam"));
158   for (int i = 0; i < stems_->size (); i++)
159     {
160       /*
161         watch out for stem tremolos and abbreviation beams
162       */
163       if (Stem::get_beam ((*stems_)[i]))
164         {
165           scm_gc_unprotect_object (beam->self_scm ());
166           return 0;
167         }
168       Beam::add_stem (beam, (*stems_)[i]);
169     }
170
171   announce_grob (beam, (*stems_)[0]->self_scm ());
172
173   return beam;
174 }
175
176 void
177 Auto_beam_engraver::begin_beam ()
178 {
179   if (stems_ || grouping_)
180     {
181       programming_error ("already have autobeam");
182       return;
183     }
184
185   stems_ = new Link_array<Item>;
186   grouping_ = new Beaming_info_list;
187   beam_settings_ = updated_grob_properties (context (), ly_symbol2scm ("Beam"));
188
189   beam_start_moment_ = now_mom ();
190   beam_start_location_
191     = robust_scm2moment (get_property ("measurePosition"), Moment (0));
192   subdivide_beams_ = ly_scm2bool (get_property ("subdivideBeams"));
193   beat_length_ = robust_scm2moment (get_property ("beatLength"), Moment (1, 4));
194 }
195
196 void
197 Auto_beam_engraver::junk_beam ()
198 {
199   if (!stems_)
200     return;
201
202   delete stems_;
203   stems_ = 0;
204   delete grouping_;
205   grouping_ = 0;
206   beam_settings_ = SCM_EOL;
207
208   shortest_mom_ = Moment (Rational (1, 8));
209 }
210
211 void
212 Auto_beam_engraver::end_beam ()
213 {
214   if (stems_->size () < 2)
215     {
216       junk_beam ();
217     }
218   else
219     {
220       finished_beam_ = create_beam ();
221       if (finished_beam_)
222         finished_grouping_ = grouping_;
223       delete stems_;
224       stems_ = 0;
225       grouping_ = 0;
226       beam_settings_ = SCM_EOL;
227     }
228
229   shortest_mom_ = Moment (Rational (1, 8));
230 }
231
232 void
233 Auto_beam_engraver::typeset_beam ()
234 {
235   if (finished_beam_)
236     {
237       finished_grouping_->beamify (beat_length_, subdivide_beams_);
238       Beam::set_beaming (finished_beam_, finished_grouping_);
239       finished_beam_ = 0;
240
241       delete finished_grouping_;
242       finished_grouping_ = 0;
243     }
244 }
245
246 void
247 Auto_beam_engraver::start_translation_timestep ()
248 {
249   process_acknowledged_count_ = 0;
250   /*
251     don't beam over skips
252   */
253   if (stems_)
254     {
255       Moment now = now_mom ();
256       if (extend_mom_ < now)
257         {
258           end_beam ();
259         }
260     }
261   forbid_ = 0;
262 }
263
264 void
265 Auto_beam_engraver::stop_translation_timestep ()
266 {
267   typeset_beam ();
268 }
269
270 void
271 Auto_beam_engraver::finalize ()
272 {
273   /* finished beams may be typeset */
274   typeset_beam ();
275   /* but unfinished may need another announce/acknowledge pass */
276   if (stems_)
277     junk_beam ();
278 }
279
280 void
281 Auto_beam_engraver::acknowledge_grob (Grob_info info)
282 {
283   /* Duplicated from process_music (), since
284      Repeat_acknowledge_engraver::process_music () may also set whichBar.  */
285
286   Moment now = now_mom ();
287   if (scm_is_string (get_property ("whichBar"))
288       && beam_start_moment_ < now)
289     {
290       consider_end (shortest_mom_);
291       junk_beam ();
292     }
293
294   if (stems_)
295     {
296       if (Beam::has_interface (info.grob ()))
297         {
298           end_beam ();
299         }
300       else if (Bar_line::has_interface (info.grob ()))
301         {
302           end_beam ();
303         }
304       else if (Rest::has_interface (info.grob ()))
305         {
306           end_beam ();
307         }
308     }
309
310   if (Stem::has_interface (info.grob ()))
311     {
312       Item *stem = dynamic_cast<Item *> (info.grob ());
313       Music *m = info.music_cause ();
314       if (!m->is_mus_type ("rhythmic-event"))
315         {
316           programming_error ("stem must have rhythmic structure");
317           return;
318         }
319
320       /*
321         Don't (start) auto-beam over empty stems; skips or rests
322       */
323       if (!Stem::head_count (stem))
324         {
325           if (stems_)
326             end_beam ();
327           return;
328         }
329
330       if (Stem::get_beam (stem))
331         {
332           if (stems_)
333             junk_beam ();
334           return;
335         }
336
337       int durlog = unsmob_duration (m->get_property ("duration"))->duration_log ();
338
339       if (durlog <= 2)
340         {
341           if (stems_)
342             end_beam ();
343           return;
344         }
345
346       /*
347         ignore grace notes.
348       */
349       if (bool (beam_start_location_.grace_part_) != bool (now.grace_part_))
350         return;
351
352       Moment dur = unsmob_duration (m->get_property ("duration"))->get_length ();
353
354       consider_end (dur);
355       consider_begin (dur);
356
357       if (dur < shortest_mom_)
358         shortest_mom_ = dur;
359
360       if (!stems_)
361         return;
362
363       grouping_->add_stem (now - beam_start_moment_ + beam_start_location_,
364                            durlog - 2);
365       stems_->push (stem);
366       last_add_mom_ = now;
367       extend_mom_ = max (extend_mom_, now) + m->get_length ();
368     }
369 }
370
371 void
372 Auto_beam_engraver::process_acknowledged ()
373 {
374   if (extend_mom_ > now_mom ())
375     return ; 
376
377   if (!process_acknowledged_count_)
378     {
379       consider_end (shortest_mom_);
380       consider_begin (shortest_mom_);
381     }
382   else if (process_acknowledged_count_ > 1)
383     {
384       if (stems_)
385         {
386           Moment now = now_mom ();
387           if ((extend_mom_ < now)
388               || ((extend_mom_ == now) && (last_add_mom_ != now)))
389             {
390               end_beam ();
391             }
392           else if (!stems_->size ())
393             {
394               junk_beam ();
395             }
396         }
397     }
398
399   process_acknowledged_count_++;
400 }
401
402 ADD_TRANSLATOR (Auto_beam_engraver,
403                 /* descr */ "Generate beams based on measure characteristics and observed "
404                 "Stems.  Uses beatLength, measureLength and measurePosition to decide "
405                 "when to start and stop a beam.  Overriding beaming is done through "
406                 "@ref{Stem_engraver} properties @code{stemLeftBeamCount} and "
407                 "@code{stemRightBeamCount}. ",
408                 /* creats*/ "Beam",
409                 /* accepts */ "beam-forbid-event",
410                 /* acks  */ "stem-interface rest-interface beam-interface bar-line-interface",
411                 /* reads */ "autoBeaming autoBeamSettings beatLength subdivideBeams",
412                 /* write */ "");