]> git.donarmstrong.com Git - lilypond.git/blob - lily/auto-beam-engraver.cc
Issue 4550 (1/2) Avoid "using namespace std;" in included files
[lilypond.git] / lily / auto-beam-engraver.cc
1 /*
2   This file is part of LilyPond, the GNU music typesetter.
3
4   Copyright (C) 1999--2015 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 "beaming-pattern.hh"
21 #include "beam.hh"
22 #include "context.hh"
23 #include "context-handle.hh"
24 #include "duration.hh"
25 #include "engraver.hh"
26 #include "grob-properties.hh"
27 #include "item.hh"
28 #include "rest.hh"
29 #include "spanner.hh"
30 #include "stream-event.hh"
31 #include "stem.hh"
32 #include "warn.hh"
33
34 #include "translator.icc"
35
36 using std::vector;
37
38 class Auto_beam_engraver : public Engraver
39 {
40   TRANSLATOR_DECLARATIONS (Auto_beam_engraver);
41
42 protected:
43   void stop_translation_timestep ();
44   void process_acknowledged ();
45
46   virtual void process_music ();
47   virtual void finalize ();
48   virtual void derived_mark () const;
49
50   DECLARE_ACKNOWLEDGER (rest);
51   DECLARE_ACKNOWLEDGER (beam);
52   DECLARE_ACKNOWLEDGER (bar_line);
53   DECLARE_ACKNOWLEDGER (breathing_sign);
54   DECLARE_ACKNOWLEDGER (stem);
55   DECLARE_TRANSLATOR_LISTENER (beam_forbid);
56
57 private:
58   virtual bool test_moment (Direction, Moment, Moment);
59   void consider_begin (Moment, Moment);
60   void consider_end (Moment, Moment);
61   Spanner *create_beam ();
62   void begin_beam ();
63   void end_beam ();
64   void junk_beam ();
65   virtual bool is_same_grace_state (Moment, Moment);
66   void recheck_beam ();
67   void typeset_beam ();
68   vector<Item *> *remove_end_stems (vsize);
69
70   Stream_event *forbid_;
71   /*
72     shortest_mom_ is the shortest note in the beam.
73   */
74   Moment shortest_mom_;
75   Spanner *finished_beam_;
76   vector<Item *> *stems_;
77
78   int process_acknowledged_count_;
79   Moment last_add_mom_;
80   /*
81     Projected ending of the  beam we're working on.
82   */
83   Moment extend_mom_;
84   Moment beam_start_moment_;
85   Moment beam_start_location_;
86   /*
87     Handle on the starting staff keeps it alive until beam is comlete
88   */
89   Context_handle beam_start_context_;
90
91   // We act as if beam were created, and start a grouping anyway.
92   Beaming_pattern *grouping_;
93   SCM beam_settings_;
94
95   Beaming_pattern *finished_grouping_;
96
97   Beaming_options beaming_options_;
98   Beaming_options finished_beaming_options_;
99
100   void check_bar_property ();
101 };
102
103 void
104 Auto_beam_engraver::derived_mark () const
105 {
106   scm_gc_mark (beam_settings_);
107 }
108
109 void
110 Auto_beam_engraver::check_bar_property ()
111 {
112   /* Duplicated from process_music (), since
113      Repeat_acknowledge_engraver::process_music () may also set whichBar.  */
114
115   Moment now = now_mom ();
116   if (scm_is_string (get_property ("whichBar"))
117       && beam_start_moment_ < now)
118     {
119       consider_end (measure_position (context ()), shortest_mom_);
120       junk_beam ();
121     }
122 }
123
124 void
125 Auto_beam_engraver::process_music ()
126 {
127   Moment now = now_mom ();
128   /*
129     don't beam over skips
130   */
131   if (stems_)
132     {
133       if (extend_mom_ < now)
134         end_beam ();
135     }
136
137   if (scm_is_string (get_property ("whichBar")))
138     {
139       consider_end (measure_position (context ()), shortest_mom_);
140       junk_beam ();
141     }
142
143   if (forbid_)
144     {
145       if (stems_)
146         end_beam ();
147       else
148         junk_beam ();
149     }
150 }
151
152 Auto_beam_engraver::Auto_beam_engraver ()
153 {
154   forbid_ = 0;
155   process_acknowledged_count_ = 0;
156   stems_ = 0;
157   shortest_mom_ = Moment (Rational (1, 4));
158   extend_mom_ = Moment (-1);
159   finished_beam_ = 0;
160   finished_grouping_ = 0;
161   grouping_ = 0;
162   beam_settings_ = SCM_EOL;
163 }
164
165 IMPLEMENT_TRANSLATOR_LISTENER (Auto_beam_engraver, beam_forbid);
166 void
167 Auto_beam_engraver::listen_beam_forbid (Stream_event *ev)
168 {
169   ASSIGN_EVENT_ONCE (forbid_, ev);
170 }
171
172 bool
173 Auto_beam_engraver::test_moment (Direction dir, Moment test_mom, Moment dur)
174 {
175   return scm_is_true (scm_call_4 (get_property ("autoBeamCheck"),
176                                     context ()->self_scm (),
177                                     scm_from_int (dir),
178                                     test_mom.smobbed_copy (),
179                                     dur.smobbed_copy ()));
180 }
181
182 void
183 Auto_beam_engraver::consider_begin (Moment test_mom, Moment dur)
184 {
185   bool on = to_boolean (get_property ("autoBeaming"));
186   if (!stems_ && on
187       && !forbid_)
188     {
189       bool b = test_moment (START, test_mom, dur);
190       if (b)
191         begin_beam ();
192     }
193 }
194
195 void
196 Auto_beam_engraver::consider_end (Moment test_mom, Moment dur)
197 {
198   if (stems_)
199     {
200       /* Allow already started autobeam to end:
201          don't check for autoBeaming */
202       bool b = test_moment (STOP, test_mom, dur);
203       if (b)
204         end_beam ();
205     }
206 }
207
208 Spanner *
209 Auto_beam_engraver::create_beam ()
210 {
211   if (to_boolean (get_property ("skipTypesetting")))
212     return 0;
213
214   for (vsize i = 0; i < stems_->size (); i++)
215     if (Stem::get_beam ((*stems_)[i]))
216       return 0;
217
218   /*
219     Can't use make_spanner () because we have to use
220     beam_settings_.
221   */
222   Spanner *beam = new Spanner (beam_settings_);
223
224   for (vsize i = 0; i < stems_->size (); i++)
225     Beam::add_stem (beam, (*stems_)[i]);
226
227   Grob_info i = make_grob_info (beam, (*stems_)[0]->self_scm ());
228   i.rerouting_daddy_context_ = beam_start_context_.get_context ();
229   announce_grob (i);
230
231   return beam;
232 }
233
234 void
235 Auto_beam_engraver::begin_beam ()
236 {
237   if (stems_ || grouping_)
238     {
239       programming_error ("already have autobeam");
240       return;
241     }
242
243   stems_ = new vector<Item *>;
244   grouping_ = new Beaming_pattern ();
245   beaming_options_.from_context (context ());
246   beam_settings_ = Grob_property_info (context (), ly_symbol2scm ("Beam")).updated ();
247
248   beam_start_context_.set_context (context ()->get_parent_context ());
249   beam_start_moment_ = now_mom ();
250   beam_start_location_
251     = robust_scm2moment (get_property ("measurePosition"), Moment (0));
252 }
253
254 void
255 Auto_beam_engraver::junk_beam ()
256 {
257   if (!stems_)
258     return;
259
260   delete stems_;
261   stems_ = 0;
262   delete grouping_;
263   grouping_ = 0;
264   beam_settings_ = SCM_EOL;
265
266   shortest_mom_ = Moment (Rational (1, 4));
267 }
268
269 bool
270 Auto_beam_engraver::is_same_grace_state (Moment start, Moment now)
271 {
272   return bool (start.grace_part_) == bool (now.grace_part_);
273 }
274
275
276 void
277 Auto_beam_engraver::end_beam ()
278 {
279   if (stems_->size () < 2)
280     junk_beam ();
281   else
282     {
283       finished_beam_ = create_beam ();
284
285       if (finished_beam_)
286         {
287           Grob_info i = make_grob_info (finished_beam_, SCM_EOL);
288           i.rerouting_daddy_context_ = beam_start_context_.get_context ();
289
290           announce_end_grob (i);
291           finished_grouping_ = grouping_;
292           finished_beaming_options_ = beaming_options_;
293         }
294       delete stems_;
295       stems_ = 0;
296       grouping_ = 0;
297       beam_settings_ = SCM_EOL;
298     }
299
300   beam_start_context_.set_context (NULL);
301   shortest_mom_ = Moment (Rational (1, 4));
302 }
303
304 void
305 Auto_beam_engraver::typeset_beam ()
306 {
307   if (finished_beam_)
308     {
309       if (!finished_beam_->get_bound (RIGHT))
310         finished_beam_->set_bound (RIGHT, finished_beam_->get_bound (LEFT));
311
312       finished_grouping_->beamify (finished_beaming_options_);
313       Beam::set_beaming (finished_beam_, finished_grouping_);
314       finished_beam_ = 0;
315
316       delete finished_grouping_;
317       finished_grouping_ = 0;
318     }
319 }
320
321 void
322 Auto_beam_engraver::stop_translation_timestep ()
323 {
324   typeset_beam ();
325   process_acknowledged_count_ = 0;
326   forbid_ = 0;
327 }
328
329 void
330 Auto_beam_engraver::finalize ()
331 {
332   /* finished beams may be typeset */
333   typeset_beam ();
334   /* but unfinished may need another announce/acknowledge pass */
335   if (stems_)
336     junk_beam ();
337 }
338
339 void
340 Auto_beam_engraver::acknowledge_beam (Grob_info /* info */)
341 {
342   check_bar_property ();
343   if (stems_)
344     end_beam ();
345 }
346
347 void
348 Auto_beam_engraver::acknowledge_bar_line (Grob_info /* info */)
349 {
350   check_bar_property ();
351   if (stems_)
352     end_beam ();
353 }
354
355 void
356 Auto_beam_engraver::acknowledge_breathing_sign (Grob_info /* info */)
357 {
358   check_bar_property ();
359   if (stems_)
360     end_beam ();
361 }
362
363 void
364 Auto_beam_engraver::acknowledge_rest (Grob_info /* info */)
365 {
366   check_bar_property ();
367   if (stems_)
368     end_beam ();
369 }
370
371 void
372 Auto_beam_engraver::acknowledge_stem (Grob_info info)
373 {
374   check_bar_property ();
375   Item *stem = dynamic_cast<Item *> (info.grob ());
376   Stream_event *ev = info.ultimate_event_cause ();
377   if (!ev->in_event_class ("rhythmic-event"))
378     {
379       programming_error ("stem must have rhythmic structure");
380       return;
381     }
382
383   /*
384     Don't (start) auto-beam over empty stems; skips or rests
385   */
386   if (!Stem::head_count (stem))
387     {
388       if (stems_)
389         end_beam ();
390       return;
391     }
392
393   if (Stem::get_beam (stem))
394     {
395       if (stems_)
396         junk_beam ();
397       return;
398     }
399
400   int durlog = unsmob<Duration> (ev->get_property ("duration"))->duration_log ();
401
402   if (durlog <= 2)
403     {
404       if (stems_)
405         end_beam ();
406       return;
407     }
408
409   /*
410     ignore interspersed grace notes.
411   */
412   Moment now = now_mom ();
413   if (!is_same_grace_state (beam_start_location_, now))
414     return;
415
416   Duration *stem_duration = unsmob<Duration> (ev->get_property ("duration"));
417   Moment dur = stem_duration->get_length ();
418
419   //Moment dur = unsmob<Duration> (ev->get_property ("duration"))->get_length ();
420   Moment measure_now = measure_position (context ());
421   bool recheck_needed = false;
422
423   if (dur < shortest_mom_)
424     {
425       /* new shortest moment, so store it and set recheck_needed */
426       shortest_mom_ = dur;
427       recheck_needed = true;
428     }
429
430   /* end should be based on shortest_mom_, begin should be
431      based on current duration  */
432   consider_end (measure_now, shortest_mom_);
433   consider_begin (measure_now, dur);
434
435   if (!stems_)
436     return;
437
438   grouping_->add_stem (now - beam_start_moment_ + beam_start_location_,
439                        durlog - 2,
440                        Stem::is_invisible (stem),
441                        stem_duration->factor (),
442                        (to_boolean (stem->get_property ("tuplet-start"))));
443   stems_->push_back (stem);
444   last_add_mom_ = now;
445   extend_mom_ = max (extend_mom_, now) + get_event_length (ev, now);
446   if (recheck_needed)
447     recheck_beam ();
448 }
449
450 void
451 Auto_beam_engraver::recheck_beam ()
452 {
453   /*
454     Recheck the beam after the shortest duration has changed
455     If shorter duration has created a new break, typeset the
456     first part of the beam and reset the current beam to just
457     the last part of the beam
458   */
459   Beaming_pattern *new_grouping_ = 0;
460   vector<Item *> *new_stems_ = 0;
461   Moment temporary_shortest_mom;
462   SCM temporary_beam_settings;
463
464   bool found_end;
465
466   for (vsize i = 0; i < stems_->size () - 1;)
467     {
468       found_end = test_moment (STOP,
469                                grouping_->end_moment (i),
470                                shortest_mom_);
471       if (!found_end)
472         i++;
473       else
474         {
475           /*
476             Save the current beam settings and shortest_mom_
477             Necessary because end_beam destroys them
478           */
479           temporary_shortest_mom = shortest_mom_;
480           temporary_beam_settings = beam_settings_;
481
482           /* Eliminate (and save) the items no longer part of the first beam */
483
484           new_grouping_ = grouping_->split_pattern (i);
485           new_stems_ = remove_end_stems (i);
486
487           end_beam ();
488           typeset_beam ();
489
490           /* now recreate the unbeamed data structures */
491           stems_ = new_stems_;
492           grouping_ = new_grouping_;
493           shortest_mom_ = temporary_shortest_mom;
494           beam_settings_ = temporary_beam_settings;
495
496           i = 0;
497         }
498
499     }
500
501 }
502
503 /*
504   Remove all stems with an index greater than split_index
505   from stems_, and return a vector containing all of the
506   removed stems
507 */
508 vector <Item *> *
509 Auto_beam_engraver::remove_end_stems (vsize split_index)
510 {
511   vector <Item *> *removed_stems = 0;
512   removed_stems = new vector <Item *>;
513
514   for (vsize j = split_index + 1; j < stems_->size (); j++)
515     removed_stems->push_back ((*stems_).at (j));
516   for (vsize j = split_index + 1; j < stems_->size ();)
517     stems_->pop_back ();
518   return removed_stems;
519 }
520
521 void
522 Auto_beam_engraver::process_acknowledged ()
523 {
524   Moment now = now_mom ();
525   if (extend_mom_ > now)
526     return;
527
528   if (!process_acknowledged_count_)
529     {
530       Moment measure_now = measure_position (context ());
531       consider_end (measure_now, shortest_mom_);
532     }
533   else if (process_acknowledged_count_ > 1)
534     {
535       if (stems_)
536         {
537           if ((extend_mom_ < now)
538               || ((extend_mom_ == now) && (last_add_mom_ != now)))
539             end_beam ();
540           else if (!stems_->size ())
541             junk_beam ();
542         }
543     }
544
545   process_acknowledged_count_++;
546 }
547
548 ADD_ACKNOWLEDGER (Auto_beam_engraver, stem);
549 ADD_ACKNOWLEDGER (Auto_beam_engraver, bar_line);
550 ADD_ACKNOWLEDGER (Auto_beam_engraver, beam);
551 ADD_ACKNOWLEDGER (Auto_beam_engraver, breathing_sign);
552 ADD_ACKNOWLEDGER (Auto_beam_engraver, rest);
553 ADD_TRANSLATOR (Auto_beam_engraver,
554                 /* doc */
555                 "Generate beams based on measure characteristics and observed"
556                 " Stems.  Uses @code{baseMoment}, @code{beatStructure},"
557                 " @code{beamExceptions}, @code{measureLength}, and"
558                 " @code{measurePosition} to decide when to start and stop a"
559                 " beam.  Overriding beaming is done through"
560                 " @ref{Stem_engraver} properties @code{stemLeftBeamCount} and"
561                 " @code{stemRightBeamCount}.",
562
563                 /* create */
564                 "Beam ",
565
566                 /* read */
567                 "autoBeaming "
568                 "baseMoment "
569                 "beamExceptions "
570                 "beamHalfMeasure "
571                 "beatStructure "
572                 "subdivideBeams ",
573
574                 /* write */
575                 ""
576                );
577
578 class Grace_auto_beam_engraver : public Auto_beam_engraver
579 {
580   TRANSLATOR_DECLARATIONS (Grace_auto_beam_engraver);
581   DECLARE_TRANSLATOR_LISTENER (beam_forbid);
582
583 private:
584   Moment last_grace_start_; // Full starting time of last grace group
585   Moment last_grace_position_; // Measure position of same
586   virtual void process_music ();
587   virtual bool is_same_grace_state (Moment, Moment);
588   virtual bool test_moment (Direction, Moment, Moment);
589 };
590
591 Grace_auto_beam_engraver::Grace_auto_beam_engraver ()
592 {
593   last_grace_start_.main_part_.set_infinite (-1);
594   // grace_part_ is zero -> test_moment is false, last_grace_position_
595   // not considered.
596 }
597
598 IMPLEMENT_TRANSLATOR_LISTENER (Grace_auto_beam_engraver, beam_forbid);
599 void
600 Grace_auto_beam_engraver::listen_beam_forbid (Stream_event *ev)
601 {
602   Auto_beam_engraver::listen_beam_forbid (ev);
603 }
604
605 bool
606 Grace_auto_beam_engraver::is_same_grace_state (Moment, Moment)
607 {
608   // This is for ignoring interspersed grace notes in main note
609   // beaming.  We never want to ignore something inside of grace note
610   // beaming, so return true.
611   return true;
612 }
613
614 void
615 Grace_auto_beam_engraver::process_music ()
616 {
617   Moment now = now_mom ();
618   // Update last_grace_start_ and last_grace_position_ only when the
619   // main time advances.
620   if (now.main_part_ > last_grace_start_.main_part_)
621     {
622       last_grace_start_ = now;
623       last_grace_position_ = measure_position (context ());
624     }
625
626   Auto_beam_engraver::process_music ();
627 }
628
629 bool
630 Grace_auto_beam_engraver::test_moment (Direction dir, Moment test_mom, Moment)
631 {
632   // If no grace group started this main moment, we have no business
633   // beaming.  Same if we have left the original main time step.
634   if (!last_grace_start_.grace_part_
635       || last_grace_position_.main_part_ != test_mom.main_part_)
636     return false;
637   // Autobeam start only when at the start of the grace group.
638   if (dir == START)
639       return last_grace_position_ == test_mom;
640   // Autobeam end only when the grace part is finished.
641   return !test_mom.grace_part_;
642 }
643
644 ADD_ACKNOWLEDGER (Grace_auto_beam_engraver, stem);
645 ADD_ACKNOWLEDGER (Grace_auto_beam_engraver, bar_line);
646 ADD_ACKNOWLEDGER (Grace_auto_beam_engraver, beam);
647 ADD_ACKNOWLEDGER (Grace_auto_beam_engraver, breathing_sign);
648 ADD_ACKNOWLEDGER (Grace_auto_beam_engraver, rest);
649 ADD_TRANSLATOR (Grace_auto_beam_engraver,
650                 /* doc */
651                 "Generates one autobeam group across an entire grace phrase. "
652                 " As usual, any manual beaming or @code{\\noBeam} will block"
653                 " autobeaming, just like setting the context property"
654                 " @samp{autoBeaming} to @code{##f}.",
655
656                 /* create */
657                 "Beam ",
658
659                 /* read */
660                 "autoBeaming ",
661
662                 /* write */
663                 ""
664                );