X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=lily%2Fdynamic-engraver.cc;h=d1b8cf00b52d931605eacb16c9a179d898d74597;hb=9e781b7dc83b60a543ce218aa1a5f139f74c760f;hp=fe6c2d029be69e396027b0b0d85d4f52abc96d2c;hpb=c2f6fa509ca6d3ac483ec943bdcc6bf8cd4cebe3;p=lilypond.git diff --git a/lily/dynamic-engraver.cc b/lily/dynamic-engraver.cc index fe6c2d029b..d1b8cf00b5 100644 --- a/lily/dynamic-engraver.cc +++ b/lily/dynamic-engraver.cc @@ -1,101 +1,79 @@ /* - dynamic-engraver.cc -- implement Dynamic_engraver + This file is part of LilyPond, the GNU music typesetter. - source file of the GNU LilyPond music typesetter + Copyright (C) 2008--2014 Han-Wen Nienhuys - (c) 1997--2007 Han-Wen Nienhuys + 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 "axis-group-interface.hh" -#include "context.hh" -#include "dimensions.hh" -#include "directional-element-interface.hh" #include "engraver.hh" #include "hairpin.hh" #include "international.hh" -#include "interval.hh" +#include "item.hh" #include "note-column.hh" -#include "paper-column.hh" #include "pointer-group-interface.hh" -#include "script-interface.hh" #include "self-alignment-interface.hh" -#include "side-position-interface.hh" -#include "staff-symbol-referencer.hh" -#include "stream-event.hh" -#include "warn.hh" #include "spanner.hh" +#include "stream-event.hh" #include "text-interface.hh" #include "translator.icc" -/* - TODO: - - * direction of text-dynamic-event if not equal to direction of - line-spanner - - - TODO: this engraver is too complicated. We should split it into - the handling of the basic grobs and the linespanner - - - TODO: the line-spanner is not killed after the (de)crescs are - finished. -*/ - -/** - print text & hairpin dynamics. -*/ class Dynamic_engraver : public Engraver { - Item *script_; - Spanner *line_spanner_; - Spanner *cresc_; - - Spanner *finished_line_spanner_; - Spanner *finished_cresc_; - - Stream_event *script_ev_; - Stream_event *current_cresc_ev_; - - Drul_array accepted_spanevents_drul_; - - vector pending_columns_; - vector pending_elements_; - - void typeset_all (); - TRANSLATOR_DECLARATIONS (Dynamic_engraver); DECLARE_ACKNOWLEDGER (note_column); DECLARE_TRANSLATOR_LISTENER (absolute_dynamic); DECLARE_TRANSLATOR_LISTENER (span_dynamic); + DECLARE_TRANSLATOR_LISTENER (break_span); protected: + virtual void process_music (); + virtual void stop_translation_timestep (); virtual void finalize (); - void stop_translation_timestep (); - void process_music (); + +private: + SCM get_property_setting (Stream_event *evt, char const *evprop, + char const *ctxprop); + string get_spanner_type (Stream_event *ev); + + Drul_array accepted_spanevents_drul_; + Spanner *current_spanner_; + Spanner *finished_spanner_; + + Item *script_; + Stream_event *script_event_; + Stream_event *current_span_event_; + bool end_new_spanner_; }; Dynamic_engraver::Dynamic_engraver () { + script_event_ = 0; + current_span_event_ = 0; script_ = 0; - finished_cresc_ = 0; - line_spanner_ = 0; - finished_line_spanner_ = 0; - current_cresc_ev_ = 0; - cresc_ = 0; - - script_ev_ = 0; - accepted_spanevents_drul_[START] = 0; - accepted_spanevents_drul_[STOP] = 0; + finished_spanner_ = 0; + current_spanner_ = 0; + accepted_spanevents_drul_.set (0, 0); + end_new_spanner_ = false; } IMPLEMENT_TRANSLATOR_LISTENER (Dynamic_engraver, absolute_dynamic); void Dynamic_engraver::listen_absolute_dynamic (Stream_event *ev) { - /* - TODO: probably broken. - */ - ASSIGN_EVENT_ONCE (script_ev_, ev); + ASSIGN_EVENT_ONCE (script_event_, ev); } IMPLEMENT_TRANSLATOR_LISTENER (Dynamic_engraver, span_dynamic); @@ -104,349 +82,221 @@ Dynamic_engraver::listen_span_dynamic (Stream_event *ev) { Direction d = to_dir (ev->get_property ("span-direction")); - if (d == START) - ASSIGN_EVENT_ONCE (accepted_spanevents_drul_[START], ev); - - /* Cancel any ongoing crescendo, either explicitly by \! or - implicitly by a new crescendo. Also avoid warning if cresc is - cancelled both implicitly and explicitly. */ - if ((d == STOP || current_cresc_ev_) && !accepted_spanevents_drul_[STOP]) - ASSIGN_EVENT_ONCE (accepted_spanevents_drul_[STOP], ev); + ASSIGN_EVENT_ONCE (accepted_spanevents_drul_[d], ev); } +IMPLEMENT_TRANSLATOR_LISTENER (Dynamic_engraver, break_span); void -Dynamic_engraver::process_music () +Dynamic_engraver::listen_break_span (Stream_event *event) { - if (accepted_spanevents_drul_[START] || accepted_spanevents_drul_[STOP] || script_ev_) + if (event->in_event_class ("break-dynamic-span-event")) { - if (!line_spanner_) - { - Stream_event *rq = accepted_spanevents_drul_[START]; - line_spanner_ = make_spanner ("DynamicLineSpanner", rq ? rq->self_scm () : SCM_EOL); - if (script_ev_) - rq = script_ev_; - } + // Case 1: Already have a start dynamic event -> break applies to new + // spanner (created later) -> set a flag + // Case 2: no new spanner, but spanner already active -> break it now + if (accepted_spanevents_drul_[START]) + end_new_spanner_ = true; + else if (current_spanner_) + current_spanner_->set_property ("spanner-broken", SCM_BOOL_T); } +} - /* - During a (de)crescendo, pending event will not be cleared, - and a line-spanner will always be created, as \< \! are already - two events. - - Note: line-spanner must always have at least same duration - as (de)crecsendo, b.o. line-breaking. - */ +SCM +Dynamic_engraver::get_property_setting (Stream_event *evt, + char const *evprop, + char const *ctxprop) +{ + SCM spanner_type = evt->get_property (evprop); + if (spanner_type == SCM_EOL) + spanner_type = get_property (ctxprop); + return spanner_type; +} - /* - maybe we should leave dynamic texts to the text-engraver and - simply acknowledge them? - */ - if (script_ev_) +void +Dynamic_engraver::process_music () +{ + if (current_spanner_ + && (accepted_spanevents_drul_[STOP] + || script_event_ + || accepted_spanevents_drul_[START])) { - script_ = make_item ("DynamicText", script_ev_->self_scm ()); - script_->set_property ("text", - script_ev_->get_property ("text")); + Stream_event *ender = accepted_spanevents_drul_[STOP]; + if (!ender) + ender = script_event_; - if (Direction d = to_dir (script_ev_->get_property ("direction"))) - set_grob_direction (line_spanner_, d); - else if (Direction d = to_dir (line_spanner_->get_property ("direction"))) - set_grob_direction (script_, d); + if (!ender) + ender = accepted_spanevents_drul_[START]; - Axis_group_interface::add_element (line_spanner_, script_); + finished_spanner_ = current_spanner_; + announce_end_grob (finished_spanner_, ender->self_scm ()); + current_spanner_ = 0; + current_span_event_ = 0; } - Stream_event *stop_ev = accepted_spanevents_drul_ [STOP] - ? accepted_spanevents_drul_[STOP] : script_ev_; - - if (accepted_spanevents_drul_[STOP] || script_ev_) + if (accepted_spanevents_drul_[START]) { - /* - finish side position alignment if the (de)cresc ends here, and - there are no new dynamics. - */ - - if (cresc_) - { - assert (!finished_cresc_ && cresc_); - - if (script_) - { - cresc_->set_bound (RIGHT, script_); - add_bound_item (line_spanner_, script_); - } - - finished_cresc_ = cresc_; - - /* backwards compatibility with hairpinToBarline */ - bool use_bar = to_boolean (get_property ("hairpinToBarline")) - && scm_is_string (get_property ("whichBar")) - && !script_ev_; - - finished_cresc_->set_property ("to-barline", scm_from_bool (use_bar)); - - announce_end_grob (finished_cresc_, SCM_EOL); - cresc_ = 0; - current_cresc_ev_ = 0; - } - else if (accepted_spanevents_drul_[STOP]) - { - accepted_spanevents_drul_[STOP]->origin ()->warning (_ ("cannot find start of (de)crescendo")); - stop_ev = 0; - } + current_span_event_ = accepted_spanevents_drul_[START]; + + string start_type = get_spanner_type (current_span_event_); + SCM cresc_type = get_property_setting (current_span_event_, "span-type", + (start_type + "Spanner").c_str ()); + + if (cresc_type == ly_symbol2scm ("text")) + { + current_spanner_ + = make_spanner ("DynamicTextSpanner", + accepted_spanevents_drul_[START]->self_scm ()); + + SCM text = get_property_setting (current_span_event_, "span-text", + (start_type + "Text").c_str ()); + if (Text_interface::is_markup (text)) + current_spanner_->set_property ("text", text); + /* + If the line of a text spanner is hidden, end the alignment spanner + early: this allows dynamics to be spaced individually instead of + being linked together. + */ + if (current_spanner_->get_property ("style") == ly_symbol2scm ("none")) + current_spanner_->set_property ("spanner-broken", SCM_BOOL_T); + } + else + { + if (cresc_type != ly_symbol2scm ("hairpin")) + { + string as_string = ly_scm_write_string (cresc_type); + current_span_event_ + ->origin ()->warning (_f ("unknown crescendo style: %s\ndefaulting to hairpin.", as_string.c_str ())); + } + current_spanner_ = make_spanner ("Hairpin", + current_span_event_->self_scm ()); + } + // if we have a break-dynamic-span event right after the start dynamic, break the new spanner immediately + if (end_new_spanner_) + { + current_spanner_->set_property ("spanner-broken", SCM_BOOL_T); + end_new_spanner_ = false; + } + if (finished_spanner_) + { + if (Hairpin::has_interface (finished_spanner_)) + Pointer_group_interface::add_grob (finished_spanner_, + ly_symbol2scm ("adjacent-spanners"), + current_spanner_); + if (Hairpin::has_interface (current_spanner_)) + Pointer_group_interface::add_grob (current_spanner_, + ly_symbol2scm ("adjacent-spanners"), + finished_spanner_); + } } - if (accepted_spanevents_drul_[START]) + if (script_event_) { - if (current_cresc_ev_) - { - string msg = _ ("already have a decrescendo"); - if (current_cresc_ev_->in_event_class ("crescendo-event")) - msg = _ ("already have a crescendo"); - - accepted_spanevents_drul_[START]->origin ()->warning (msg); - current_cresc_ev_->origin ()->warning (_ ("cresc starts here")); - } - else - { - current_cresc_ev_ = accepted_spanevents_drul_[START]; - - if (Direction d = to_dir (current_cresc_ev_->get_property ("direction"))) - set_grob_direction (line_spanner_, d); - - /* - TODO: Use symbols. - */ - - SCM start_sym = current_cresc_ev_->get_property ("class"); - string start_type; - - if (start_sym == ly_symbol2scm ("decrescendo-event")) - start_type = "decrescendo"; - else if (start_sym == ly_symbol2scm ("crescendo-event")) - start_type = "crescendo"; - else - { - programming_error ("unknown dynamic spanner type"); - return; - } - - /* - UGH. TODO: should read from original event, so appearance - may be altered with \tweak. - */ - SCM s = get_property ((start_type + "Spanner").c_str ()); - if (!scm_is_symbol (s) || s == ly_symbol2scm ("hairpin")) - { - cresc_ = make_spanner ("Hairpin", accepted_spanevents_drul_[START]->self_scm ()); - if (finished_cresc_) - { - Pointer_group_interface::add_grob (finished_cresc_, - ly_symbol2scm ("adjacent-hairpins"), - cresc_); - - Pointer_group_interface::add_grob (cresc_, - ly_symbol2scm ("adjacent-hairpins"), - finished_cresc_); - } - } - - /* - This is a convenient (and legacy) interface to TextSpanners - for use in (de)crescendi. - Hmm. - */ - else - { - cresc_ = make_spanner ("DynamicTextSpanner", accepted_spanevents_drul_[START]->self_scm ()); - cresc_->set_property ("style", s); - context ()->set_property ((start_type - + "Spanner").c_str (), SCM_EOL); - s = get_property ((start_type + "Text").c_str ()); - if (Text_interface::is_markup (s)) - { - cresc_->set_property ("text", s); - context ()->set_property ((start_type + "Text").c_str (), - SCM_EOL); - } - - if (script_) - { - set_nested_property (cresc_, - scm_list_3 (ly_symbol2scm ("bound-details"), - ly_symbol2scm ("left"), - ly_symbol2scm ("attach-dir") - ), - scm_from_int (RIGHT)); - } - } - - if (script_) - { - cresc_->set_bound (LEFT, script_); - add_bound_item (line_spanner_, cresc_->get_bound (LEFT)); - } - - Axis_group_interface::add_element (line_spanner_, cresc_); - } + script_ = make_item ("DynamicText", script_event_->self_scm ()); + script_->set_property ("text", + script_event_->get_property ("text")); + + if (finished_spanner_) + finished_spanner_->set_bound (RIGHT, script_); + if (current_spanner_) + current_spanner_->set_bound (LEFT, script_); } } void Dynamic_engraver::stop_translation_timestep () { - if (!current_cresc_ev_ && line_spanner_) - { - assert (!finished_line_spanner_); - finished_line_spanner_ = line_spanner_; - line_spanner_ = 0; - } - - typeset_all (); - - if (cresc_ && !cresc_->get_bound (LEFT)) - { - cresc_->set_bound (LEFT, unsmob_grob (get_property ("currentMusicalColumn"))); - add_bound_item (line_spanner_, cresc_->get_bound (LEFT)); - } - - script_ev_ = 0; - accepted_spanevents_drul_[START] = 0; - accepted_spanevents_drul_[STOP] = 0; + if (finished_spanner_ && !finished_spanner_->get_bound (RIGHT)) + finished_spanner_ + ->set_bound (RIGHT, + unsmob_grob (get_property ("currentMusicalColumn"))); + + if (current_spanner_ && !current_spanner_->get_bound (LEFT)) + current_spanner_ + ->set_bound (LEFT, + unsmob_grob (get_property ("currentMusicalColumn"))); + script_ = 0; + script_event_ = 0; + accepted_spanevents_drul_.set (0, 0); + finished_spanner_ = 0; + end_new_spanner_ = false; } void Dynamic_engraver::finalize () { - typeset_all (); - - if (line_spanner_ - && !line_spanner_->is_live ()) - line_spanner_ = 0; - if (line_spanner_) + if (current_spanner_ + && !current_spanner_->is_live ()) + current_spanner_ = 0; + if (current_spanner_) { - finished_line_spanner_ = line_spanner_; - typeset_all (); - } - - if (cresc_ - && !cresc_->is_live ()) - cresc_ = 0; - if (cresc_) - { - current_cresc_ev_->origin ()->warning (_ ("unterminated (de)crescendo")); - cresc_->suicide (); - cresc_ = 0; + current_span_event_ + ->origin ()->warning (_f ("unterminated %s", + get_spanner_type (current_span_event_) + .c_str ())); + current_spanner_->suicide (); + current_spanner_ = 0; } } -void -Dynamic_engraver::typeset_all () +string +Dynamic_engraver::get_spanner_type (Stream_event *ev) { - if (finished_cresc_) - { - if (!finished_cresc_->get_bound (RIGHT)) - { - - Grob *column_bound = unsmob_grob (get_property ("currentMusicalColumn")); - - finished_cresc_->set_bound (RIGHT, script_ - ? script_ - : column_bound); - - if (finished_line_spanner_) - add_bound_item (finished_line_spanner_, - finished_cresc_->get_bound (RIGHT)); - } - finished_cresc_ = 0; - } - - script_ = 0; - if (finished_line_spanner_) - { - /* - We used to have + string type; + SCM start_sym = scm_car (ev->get_property ("class")); - extend-spanner-over-elements (finished_line_spanner_); + if (start_sym == ly_symbol2scm ("decrescendo-event")) + type = "decrescendo"; + else if (start_sym == ly_symbol2scm ("crescendo-event")) + type = "crescendo"; + else + programming_error ("unknown dynamic spanner type"); - but this is rather kludgy, since finished_line_spanner_ - typically has a staff-symbol field set , extending it over the - entire staff. - - */ - - Grob *l = finished_line_spanner_->get_bound (LEFT); - Grob *r = finished_line_spanner_->get_bound (RIGHT); - if (!r && l) - finished_line_spanner_->set_bound (RIGHT, l); - else if (!l && r) - finished_line_spanner_->set_bound (LEFT, r); - else if (!r && !l) - { - /* - This is a isolated dynamic apparently, and does not even have - any interesting support item. - */ - Grob *cc = unsmob_grob (get_property ("currentMusicalColumn")); - Item *ci = dynamic_cast (cc); - finished_line_spanner_->set_bound (RIGHT, ci); - finished_line_spanner_->set_bound (LEFT, ci); - } - finished_line_spanner_ = 0; - } + return type; } void Dynamic_engraver::acknowledge_note_column (Grob_info info) { - if (!line_spanner_) - return; - - if (line_spanner_ - /* Don't refill killed spanner */ - && line_spanner_->is_live ()) - { - Side_position_interface::add_support (line_spanner_, info.grob ()); - add_bound_item (line_spanner_, dynamic_cast (info.grob ())); - } - if (script_ && !script_->get_parent (X_AXIS)) { extract_grob_set (info.grob (), "note-heads", heads); - if (heads.size ()) - { - Grob *head = heads[0]; - script_->set_parent (head, X_AXIS); - Self_alignment_interface::set_center_parent (script_, X_AXIS); - } - } - - if (cresc_) - { - if (!cresc_->get_bound (LEFT)) - { - cresc_->set_bound (LEFT, info.grob ()); - add_bound_item (line_spanner_, cresc_->get_bound (LEFT)); - } + /* + Spacing constraints may require dynamics to be aligned on rests, + so check for a rest if this note column has no note heads. + */ + Grob *x_parent = (heads.size () + ? heads[0] + : unsmob_grob (info.grob ()->get_object ("rest"))); + if (x_parent) + { + script_->set_parent (x_parent, X_AXIS); + Self_alignment_interface::set_center_parent (script_, X_AXIS); + } } - if (finished_cresc_ && !finished_cresc_->get_bound (RIGHT)) - finished_cresc_->set_bound (RIGHT, info.grob ()); + if (current_spanner_ && !current_spanner_->get_bound (LEFT)) + current_spanner_->set_bound (LEFT, info.grob ()); + if (finished_spanner_ && !finished_spanner_->get_bound (RIGHT)) + finished_spanner_->set_bound (RIGHT, info.grob ()); } ADD_ACKNOWLEDGER (Dynamic_engraver, note_column); - ADD_TRANSLATOR (Dynamic_engraver, - /* doc */ - "This engraver creates hairpins, dynamic texts, and their vertical\n" - "alignments. The symbols are collected onto a DynamicLineSpanner grob\n" - "which takes care of vertical positioning. ", - - /* create */ - "DynamicLineSpanner " - "DynamicTextSpanner " - "DynamicText " - "Hairpin " - "TextSpanner ", - - /* read */ "", - /* write */ ""); + /* doc */ + "Create hairpins, dynamic texts and dynamic text spanners.", + + /* create */ + "DynamicTextSpanner " + "DynamicText " + "Hairpin ", + + /* read */ + "crescendoSpanner " + "crescendoText " + "currentMusicalColumn " + "decrescendoSpanner " + "decrescendoText ", + + /* write */ + "" + );