]> git.donarmstrong.com Git - lilypond.git/blob - lily/auto-beam-engraver.cc
Update source file headers. Fixes using standard GNU package conventions.
[lilypond.git] / lily / auto-beam-engraver.cc
1 /*
2   This file is part of LilyPond, the GNU music typesetter.
3
4   Copyright (C) 1999--2009 Jan Nieuwenhuizen <janneke@gnu.org>
5
6   LilyPond is free software: you can redistribute it and/or modify
7   it under the terms of the GNU General Public License as published by
8   the Free Software Foundation, either version 3 of the License, or
9   (at your option) any later version.
10
11   LilyPond is distributed in the hope that it will be useful,
12   but WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14   GNU General Public License for more details.
15
16   You should have received a copy of the GNU General Public License
17   along with LilyPond.  If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "bar-line.hh"
21 #include "beaming-pattern.hh"
22 #include "beam.hh"
23 #include "context.hh"
24 #include "duration.hh"
25 #include "engraver.hh"
26 #include "item.hh"
27 #include "rest.hh"
28 #include "spanner.hh"
29 #include "stream-event.hh"
30 #include "stem.hh"
31 #include "warn.hh"
32
33 #include "translator.icc"
34
35 class Auto_beam_engraver : public Engraver
36 {
37   TRANSLATOR_DECLARATIONS (Auto_beam_engraver);
38
39 protected:
40   void stop_translation_timestep ();
41   void process_music ();
42   virtual void finalize ();
43   virtual void derived_mark () const;
44
45   DECLARE_ACKNOWLEDGER (rest);
46   DECLARE_ACKNOWLEDGER (beam);
47   DECLARE_ACKNOWLEDGER (bar_line);
48   DECLARE_ACKNOWLEDGER (stem);
49   DECLARE_TRANSLATOR_LISTENER (beam_forbid);
50
51   void process_acknowledged ();
52
53 private:
54   bool test_moment (Direction, Moment);
55   void consider_begin (Moment);
56   void consider_end (Moment);
57   Spanner *create_beam ();
58   void begin_beam ();
59   void end_beam ();
60   void junk_beam ();
61   bool is_same_grace_state (Grob *e);
62   void typeset_beam ();
63
64   Stream_event *forbid_;
65   /*
66     shortest_mom is the shortest note in the beam.
67   */
68   Moment shortest_mom_;
69   Spanner *finished_beam_;
70   vector<Item*> *stems_;
71
72   int process_acknowledged_count_;
73   Moment last_add_mom_;
74   /*
75     Projected ending of the  beam we're working on.
76   */
77   Moment extend_mom_;
78   Moment beam_start_moment_;
79   Moment beam_start_location_;
80
81   // We act as if beam were created, and start a grouping anyway.
82   Beaming_pattern *grouping_;
83   SCM beam_settings_;
84
85   Beaming_pattern *finished_grouping_;
86
87
88   Beaming_options beaming_options_;
89   Beaming_options finished_beaming_options_;
90   
91   
92   void check_bar_property ();
93 };
94
95 void
96 Auto_beam_engraver::derived_mark () const
97 {
98   scm_gc_mark (beam_settings_);
99 }
100
101 void
102 Auto_beam_engraver::check_bar_property ()
103 {
104   /* Duplicated from process_music (), since
105      Repeat_acknowledge_engraver::process_music () may also set whichBar.  */
106
107   Moment now = now_mom ();
108   if (scm_is_string (get_property ("whichBar"))
109       && beam_start_moment_ < now)
110     {
111       consider_end (shortest_mom_);
112       junk_beam ();
113     }
114 }
115
116 void
117 Auto_beam_engraver::process_music ()
118 {
119   /*
120     don't beam over skips
121   */
122   if (stems_)
123     {
124       Moment now = now_mom ();
125       if (extend_mom_ < now)
126         end_beam ();
127     }
128
129   if (scm_is_string (get_property ("whichBar")))
130     {
131       consider_end (shortest_mom_);
132       junk_beam ();
133     }
134
135   if (forbid_)
136     {
137       consider_end (shortest_mom_);
138       junk_beam ();
139     }
140 }
141
142 Auto_beam_engraver::Auto_beam_engraver ()
143 {
144   forbid_ = 0;
145   process_acknowledged_count_ = 0;
146   stems_ = 0;
147   shortest_mom_ = Moment (Rational (1, 8));
148   finished_beam_ = 0;
149   finished_grouping_ = 0;
150   grouping_ = 0;
151   beam_settings_ = SCM_EOL;
152 }
153
154 IMPLEMENT_TRANSLATOR_LISTENER (Auto_beam_engraver, beam_forbid);
155 void
156 Auto_beam_engraver::listen_beam_forbid (Stream_event *ev)
157 {
158   ASSIGN_EVENT_ONCE (forbid_, ev);
159 }
160
161 bool
162 Auto_beam_engraver::test_moment (Direction dir, Moment test)
163 {
164   return scm_call_3 (get_property ("autoBeamCheck"),
165                      context ()->self_scm (),
166                      scm_from_int (dir),
167                      test.smobbed_copy ())
168     != SCM_BOOL_F;
169 }
170
171 void
172 Auto_beam_engraver::consider_begin (Moment test_mom)
173 {
174   bool on = to_boolean (get_property ("autoBeaming"));
175   if (!stems_ && on
176       && !forbid_)
177     {
178       bool b = test_moment (START, test_mom);
179       if (b)
180         begin_beam ();
181     }
182 }
183
184 void
185 Auto_beam_engraver::consider_end (Moment test_mom)
186 {
187   if (stems_)
188     {
189       /* Allow already started autobeam to end:
190          don't check for autoBeaming */
191       bool b = test_moment (STOP, test_mom);
192       if (b)
193         end_beam ();
194     }
195 }
196
197 Spanner *
198 Auto_beam_engraver::create_beam ()
199 {
200   if (to_boolean (get_property ("skipTypesetting")))
201     return 0;
202
203   for (vsize i = 0; i < stems_->size (); i++)
204     if (Stem::get_beam ((*stems_)[i]))
205       return 0;
206
207   /*
208     Can't use make_spanner_from_properties () because we have to use
209     beam_settings_.
210   */
211   Spanner *beam = new Spanner (beam_settings_);
212
213   for (vsize i = 0; i < stems_->size (); i++)
214     Beam::add_stem (beam, (*stems_)[i]);
215
216   announce_grob (beam, (*stems_)[0]->self_scm ());
217
218   return beam;
219 }
220
221 void
222 Auto_beam_engraver::begin_beam ()
223 {
224   if (stems_ || grouping_)
225     {
226       programming_error ("already have autobeam");
227       return;
228     }
229
230   stems_ = new vector<Item*>;
231   grouping_ = new Beaming_pattern ();
232   beaming_options_.from_context (context ());
233   beam_settings_ = updated_grob_properties (context (), ly_symbol2scm ("Beam"));
234
235   beam_start_moment_ = now_mom ();
236   beam_start_location_
237     = robust_scm2moment (get_property ("measurePosition"), Moment (0));
238 }
239
240 void
241 Auto_beam_engraver::junk_beam ()
242 {
243   if (!stems_)
244     return;
245
246   delete stems_;
247   stems_ = 0;
248   delete grouping_;
249   grouping_ = 0;
250   beam_settings_ = SCM_EOL;
251
252   shortest_mom_ = Moment (Rational (1, 8));
253 }
254
255 void
256 Auto_beam_engraver::end_beam ()
257 {
258   if (stems_->size () < 2)
259     junk_beam ();
260   else
261     {
262       finished_beam_ = create_beam ();
263       
264       if (finished_beam_)
265         {
266           announce_end_grob (finished_beam_, SCM_EOL);
267           finished_grouping_ = grouping_;
268           finished_beaming_options_ = beaming_options_;
269         }
270       delete stems_;
271       stems_ = 0;
272       grouping_ = 0;
273       beam_settings_ = SCM_EOL;
274     }
275
276   shortest_mom_ = Moment (Rational (1, 8));
277 }
278
279 void
280 Auto_beam_engraver::typeset_beam ()
281 {
282   if (finished_beam_)
283     {
284       if (!finished_beam_->get_bound (RIGHT))
285         finished_beam_->set_bound (RIGHT, finished_beam_->get_bound (LEFT));
286       
287       finished_grouping_->beamify (finished_beaming_options_);
288       Beam::set_beaming (finished_beam_, finished_grouping_);
289       finished_beam_ = 0;
290
291       delete finished_grouping_;
292       finished_grouping_ = 0;
293     }
294 }
295
296 void
297 Auto_beam_engraver::stop_translation_timestep ()
298 {
299   typeset_beam ();
300   process_acknowledged_count_ = 0;
301   forbid_ = 0;
302 }
303
304 void
305 Auto_beam_engraver::finalize ()
306 {
307   /* finished beams may be typeset */
308   typeset_beam ();
309   /* but unfinished may need another announce/acknowledge pass */
310   if (stems_)
311     junk_beam ();
312 }
313
314
315 void
316 Auto_beam_engraver::acknowledge_beam (Grob_info /* info */)
317 {
318   check_bar_property ();
319   if (stems_)
320     end_beam ();
321 }
322
323 void
324 Auto_beam_engraver::acknowledge_bar_line (Grob_info /* info */)
325 {
326   check_bar_property ();
327   if (stems_)
328     end_beam ();
329 }
330
331 void
332 Auto_beam_engraver::acknowledge_rest (Grob_info /* info */)
333 {
334   check_bar_property ();
335   if (stems_)
336     end_beam ();
337 }
338
339 void
340 Auto_beam_engraver::acknowledge_stem (Grob_info info)
341 {
342   check_bar_property ();
343   Item *stem = dynamic_cast<Item *> (info.grob ());
344   Stream_event *ev = info.ultimate_event_cause ();
345   if (!ev->in_event_class ("rhythmic-event"))
346     {
347       programming_error ("stem must have rhythmic structure");
348       return;
349     }
350
351   /*
352     Don't (start) auto-beam over empty stems; skips or rests
353   */
354   if (!Stem::head_count (stem))
355     {
356       if (stems_)
357         end_beam ();
358       return;
359     }
360
361   if (Stem::get_beam (stem))
362     {
363       if (stems_)
364         junk_beam ();
365       return;
366     }
367
368   int durlog = unsmob_duration (ev->get_property ("duration"))->duration_log ();
369
370   if (durlog <= 2)
371     {
372       if (stems_)
373         end_beam ();
374       return;
375     }
376
377   /*
378     ignore grace notes.
379   */
380   Moment now = now_mom ();
381   if (bool (beam_start_location_.grace_part_) != bool (now.grace_part_))
382     return;
383
384   Moment dur = unsmob_duration (ev->get_property ("duration"))->get_length ();
385
386   consider_end (dur);
387   consider_begin (dur);
388
389   if (dur < shortest_mom_)
390     shortest_mom_ = dur;
391
392   if (!stems_)
393     return;
394
395   grouping_->add_stem (now - beam_start_moment_ + beam_start_location_,
396                        durlog - 2,
397                        Stem::is_invisible (stem));
398   stems_->push_back (stem);
399   last_add_mom_ = now;
400   extend_mom_ = max (extend_mom_, now) + get_event_length (ev, now);
401 }
402
403 void
404 Auto_beam_engraver::process_acknowledged ()
405 {
406   if (extend_mom_ > now_mom ())
407     return;
408
409   if (!process_acknowledged_count_)
410     {
411       consider_end (shortest_mom_);
412       consider_begin (shortest_mom_);
413     }
414   else if (process_acknowledged_count_ > 1)
415     {
416       if (stems_)
417         {
418           Moment now = now_mom ();
419           if ((extend_mom_ < now)
420               || ((extend_mom_ == now) && (last_add_mom_ != now)))
421             end_beam ();
422           else if (!stems_->size ())
423             junk_beam ();
424         }
425     }
426
427   process_acknowledged_count_++;
428 }
429
430 ADD_ACKNOWLEDGER (Auto_beam_engraver, stem);
431 ADD_ACKNOWLEDGER (Auto_beam_engraver, bar_line);
432 ADD_ACKNOWLEDGER (Auto_beam_engraver, beam);
433 ADD_ACKNOWLEDGER (Auto_beam_engraver, rest);
434 ADD_TRANSLATOR (Auto_beam_engraver,
435                 /* doc */
436                 "Generate beams based on measure characteristics and observed"
437                 " Stems.  Uses @code{beatLength}, @code{measureLength}, and"
438                 " @code{measurePosition} to decide when to start and stop a"
439                 " beam.  Overriding beaming is done through"
440                 " @ref{Stem_engraver} properties @code{stemLeftBeamCount} and"
441                 " @code{stemRightBeamCount}.",
442
443                 /* create */
444                 "Beam ",
445
446                 /* read */
447                 "autoBeaming "
448                 "beamSettings "
449                 "beatLength "
450                 "subdivideBeams ",
451                 
452                 /* write */
453                 ""
454                 );