X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=lily%2Fpage-turn-engraver.cc;h=b294dce7d7f803ca9b083d4a75235870178d2b01;hb=987a995f5a0256b7aff279e6f0018a0f0662292d;hp=1a2bf5f5b46a7928841f1a3e93a4a04494a6a9c1;hpb=6224c5c3bfe0a4c7aef15a060a323c77c99bd3f0;p=lilypond.git diff --git a/lily/page-turn-engraver.cc b/lily/page-turn-engraver.cc index 1a2bf5f5b4..b294dce7d7 100644 --- a/lily/page-turn-engraver.cc +++ b/lily/page-turn-engraver.cc @@ -1,9 +1,20 @@ /* - page-turn-engraver.cc -- implement Page_turn_engraver + This file is part of LilyPond, the GNU music typesetter. - source file of the GNU LilyPond music typesetter + Copyright (C) 2006--2015 Joe Neeman - (c) 2006 Joe Neeman + LilyPond is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + LilyPond is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with LilyPond. If not, see . */ #include "engraver.hh" @@ -12,9 +23,59 @@ #include "duration.hh" #include "grob.hh" #include "international.hh" -#include "moment.hh" +#include "paper-column.hh" +#include "stream-event.hh" #include "warn.hh" +#include "translator.icc" + +class Page_turn_event +{ +public: + SCM permission_; + Real penalty_; + Interval_t duration_; + + Page_turn_event (Rational start, Rational end, SCM perm, Real pen) + { + duration_[LEFT] = start; + duration_[RIGHT] = end; + permission_ = perm; + penalty_ = pen; + } + + /* Suppose we have decided on a possible page turn, only to change + out mind later (for example, if there is a volta repeat and it + would be difficult to turn the page back). Then we need to + re-penalize a region of the piece. Depending on how the events + intersect, we may have to split it into as many as 3 pieces. + */ + vector penalize (Page_turn_event const &penalty) + { + Interval_t intersect = intersection (duration_, penalty.duration_); + vector ret; + + if (intersect.is_empty ()) + { + ret.push_back (*this); + return ret; + } + + Real new_pen = max (penalty_, penalty.penalty_); + + if (duration_[LEFT] < penalty.duration_[LEFT]) + ret.push_back (Page_turn_event (duration_[LEFT], penalty.duration_[LEFT], permission_, penalty_)); + + if (!scm_is_null (penalty.permission_)) + ret.push_back (Page_turn_event (intersect[LEFT], intersect[RIGHT], permission_, new_pen)); + + if (penalty.duration_[RIGHT] < duration_[RIGHT]) + ret.push_back (Page_turn_event (penalty.duration_[RIGHT], duration_[RIGHT], permission_, penalty_)); + + return ret; + } +}; + class Page_turn_engraver : public Engraver { Moment rest_begin_; @@ -22,17 +83,32 @@ class Page_turn_engraver : public Engraver Moment note_end_; Rational repeat_begin_rest_length_; + vector forced_breaks_; + vector automatic_breaks_; + vector repeat_penalties_; + + /* the next 3 are in sync (ie. same number of elements, etc.) */ + vector breakable_moments_; + vector breakable_columns_; + vector special_barlines_; + + SCM max_permission (SCM perm1, SCM perm2); Real penalty (Rational rest_len); + Grob *breakable_column (Page_turn_event const &); protected: - DECLARE_ACKNOWLEDGER (note_head); + void listen_break (Stream_event *); + void acknowledge_note_head (Grob_info); public: TRANSLATOR_DECLARATIONS (Page_turn_engraver); void stop_translation_timestep (); + void start_translation_timestep (); + void finalize (); }; -Page_turn_engraver::Page_turn_engraver () +Page_turn_engraver::Page_turn_engraver (Context *c) + : Engraver (c) { repeat_begin_ = Moment (-1); repeat_begin_rest_length_ = 0; @@ -40,10 +116,29 @@ Page_turn_engraver::Page_turn_engraver () note_end_ = 0; } +Grob * +Page_turn_engraver::breakable_column (Page_turn_event const &brk) +{ + vsize start = lower_bound (breakable_moments_, brk.duration_[LEFT], less ()); + vsize end = upper_bound (breakable_moments_, brk.duration_[RIGHT], less ()); + + if (start == breakable_moments_.size ()) + return NULL; + if (end == 0) + return NULL; + end--; + + for (vsize i = end + 1; i-- > start;) + if (special_barlines_[i]) + return breakable_columns_[i]; + + return breakable_columns_[end]; +} + Real Page_turn_engraver::penalty (Rational rest_len) { - Rational min_turn = robust_scm2moment (get_property ("minPageTurnLength"), Moment (1)).main_part_; + Rational min_turn = robust_scm2moment (get_property ("minimumPageTurnLength"), Moment (1)).main_part_; return (rest_len < min_turn) ? infinity_f : 0; } @@ -51,31 +146,81 @@ Page_turn_engraver::penalty (Rational rest_len) void Page_turn_engraver::acknowledge_note_head (Grob_info gi) { - SCM dur_log_scm = gi.grob ()->get_property ("duration-log"); - if (!scm_is_number (dur_log_scm)) - return; + Stream_event *cause = gi.event_cause (); - int dur_log = scm_to_int (dur_log_scm); - int dot_count = robust_scm2int (gi.grob ()->get_property ("dot-count"), 0); + Duration *dur_ptr = cause + ? unsmob (cause->get_property ("duration")) + : 0; + + if (!dur_ptr) + return; if (rest_begin_ < now_mom ()) { Real pen = penalty ((now_mom () - rest_begin_).main_part_); if (!isinf (pen)) - { - SCM val = scm_cons (rest_begin_.smobbed_copy (), scm_from_double (pen)); - context ()->get_score_context ()->set_property ("allowPageTurn", val); - } + automatic_breaks_.push_back (Page_turn_event (rest_begin_.main_part_, + now_mom ().main_part_, + ly_symbol2scm ("allow"), 0)); } if (rest_begin_ <= repeat_begin_) repeat_begin_rest_length_ = (now_mom () - repeat_begin_).main_part_; - note_end_ = now_mom () + Moment (Duration (dur_log, dot_count).get_length ()); + note_end_ = now_mom () + dur_ptr->get_length (); +} + +void +Page_turn_engraver::listen_break (Stream_event *ev) +{ + string name = ly_symbol2string (scm_car (ev->get_property ("class"))); + + if (name == "page-turn-event") + { + SCM permission = ev->get_property ("break-permission"); + Real penalty = robust_scm2double (ev->get_property ("break-penalty"), 0); + Rational now = now_mom ().main_part_; + + forced_breaks_.push_back (Page_turn_event (now, now, permission, penalty)); + } +} + +void +Page_turn_engraver::start_translation_timestep () +{ + /* What we want to do is to build a list of all the + breakable paper columns. In general, paper-columns won't be marked as + such until the Paper_column_engraver has done stop_translation_timestep. + + Therefore, we just grab /all/ paper columns (in the + stop_translation_timestep, since they're not created here yet) + and remove the non-breakable ones at the beginning of the following + timestep. + */ + + if (breakable_columns_.size () && !Paper_column::is_breakable (breakable_columns_.back ())) + { + breakable_columns_.pop_back (); + breakable_moments_.pop_back (); + special_barlines_.pop_back (); + } } void Page_turn_engraver::stop_translation_timestep () { + Grob *pc = unsmob (get_property ("currentCommandColumn")); + + if (pc) + { + breakable_columns_.push_back (pc); + breakable_moments_.push_back (now_mom ().main_part_); + + SCM bar_scm = get_property ("whichBar"); + string bar = robust_scm2string (bar_scm, ""); + + special_barlines_.push_back (bar != "" && bar != "|"); + } + /* C&P from Repeat_acknowledge_engraver */ SCM cs = get_property ("repeatCommands"); bool start = false; @@ -84,25 +229,28 @@ Page_turn_engraver::stop_translation_timestep () for (; scm_is_pair (cs); cs = scm_cdr (cs)) { SCM command = scm_car (cs); - if (command == ly_symbol2scm ("start-repeat")) - start = true; - else if (command == ly_symbol2scm ("end-repeat")) - end = true; + if (scm_is_eq (command, ly_symbol2scm ("start-repeat"))) + start = true; + else if (scm_is_eq (command, ly_symbol2scm ("end-repeat"))) + end = true; } if (end && repeat_begin_.main_part_ >= Moment (0)) { + Rational now = now_mom ().main_part_; Real pen = penalty ((now_mom () - rest_begin_).main_part_ + repeat_begin_rest_length_); - Moment *m = unsmob_moment (get_property ("minimumRepeatLengthForPageTurn")); + Moment *m = unsmob (get_property ("minimumRepeatLengthForPageTurn")); if (m && *m > (now_mom () - repeat_begin_)) - pen = infinity_f; - if (pen > 0) - { - SCM val = scm_cons (repeat_begin_.smobbed_copy (), scm_from_double (pen)); - context ()->get_score_context ()->set_property ("revokePageTurns", val); - } + pen = infinity_f; + + if (pen == infinity_f) + repeat_penalties_.push_back (Page_turn_event (repeat_begin_.main_part_, now, SCM_EOL, -infinity_f)); + else + repeat_penalties_.push_back (Page_turn_event (repeat_begin_.main_part_, now, ly_symbol2scm ("allow"), pen)); + repeat_begin_ = Moment (-1); } + if (start) { repeat_begin_ = now_mom (); @@ -111,15 +259,108 @@ Page_turn_engraver::stop_translation_timestep () rest_begin_ = note_end_; } -#include "translator.icc" +/* return the most permissive symbol (where force is the most permissive and + forbid is the least +*/ +SCM +Page_turn_engraver::max_permission (SCM perm1, SCM perm2) +{ + if (scm_is_null (perm1)) + return perm2; + if (scm_is_eq (perm1, ly_symbol2scm ("allow")) + && scm_is_eq (perm2, ly_symbol2scm ("force"))) + return perm2; + return perm1; +} + +void +Page_turn_engraver::finalize () +{ + vsize rep_index = 0; + vector auto_breaks; + + /* filter the automatic breaks through the repeat penalties */ + for (vsize i = 0; i < automatic_breaks_.size (); i++) + { + Page_turn_event &brk = automatic_breaks_[i]; + + /* find the next applicable repeat penalty */ + for (; + rep_index < repeat_penalties_.size () + && repeat_penalties_[rep_index].duration_[RIGHT] <= brk.duration_[LEFT]; + rep_index++) + ; + + if (rep_index >= repeat_penalties_.size () + || brk.duration_[RIGHT] <= repeat_penalties_[rep_index].duration_[LEFT]) + auto_breaks.push_back (brk); + else + { + vector split = brk.penalize (repeat_penalties_[rep_index]); + + /* it's possible that the last of my newly-split events overlaps the next repeat_penalty, + in which case we need to refilter that event */ + if (rep_index + 1 < repeat_penalties_.size () + && split.size () + && split.back ().duration_[RIGHT] > repeat_penalties_[rep_index + 1].duration_[LEFT]) + { + automatic_breaks_[i] = split.back (); + split.pop_back (); + i--; + } + auto_breaks.insert (auto_breaks.end (), split.begin (), split.end ()); + } + } + + /* apply the automatic breaks */ + for (vsize i = 0; i < auto_breaks.size (); i++) + { + Page_turn_event const &brk = auto_breaks[i]; + Grob *pc = breakable_column (auto_breaks[i]); + if (pc) + { + SCM perm = max_permission (pc->get_property ("page-turn-permission"), brk.permission_); + Real pen = min (robust_scm2double (pc->get_property ("page-turn-penalty"), infinity_f), brk.penalty_); + pc->set_property ("page-turn-permission", perm); + pc->set_property ("page-turn-penalty", scm_from_double (pen)); + } + } + + /* unless a manual break overrides it, allow a page turn at the end of the piece */ + breakable_columns_.back ()->set_property ("page-turn-permission", ly_symbol2scm ("allow")); + + /* apply the manual breaks */ + for (vsize i = 0; i < forced_breaks_.size (); i++) + { + Page_turn_event const &brk = forced_breaks_[i]; + Grob *pc = breakable_column (forced_breaks_[i]); + if (pc) + { + pc->set_property ("page-turn-permission", brk.permission_); + pc->set_property ("page-turn-penalty", scm_from_double (brk.penalty_)); + } + } +} + + +void +Page_turn_engraver::boot () +{ + ADD_LISTENER (Page_turn_engraver, break); + ADD_ACKNOWLEDGER (Page_turn_engraver, note_head); +} -ADD_ACKNOWLEDGER (Page_turn_engraver, note_head); ADD_TRANSLATOR (Page_turn_engraver, - /* doc */ "Decide where page turns are allowed to go", - /* create */ "", - /* accept */ "", - /* read */ "", + /* doc */ + "Decide where page turns are allowed to go.", + + /* create */ + "", + + /* read */ + "minimumPageTurnLength " + "minimumRepeatLengthForPageTurn ", + /* write */ - "allowPageTurn " - "revokePageTurns " - ); + "" + );