From 5457e0162783d5bbcd549857a244d949e93b5ca1 Mon Sep 17 00:00:00 2001 From: Han-Wen Nienhuys Date: Thu, 1 May 2008 23:22:26 -0300 Subject: [PATCH] New dynamics engravers Dynamic_align_engraver New_dynamic_engraver The New_dynamic_engraver handles creation of (de)crescendi and scripts while Dynamic_align_engraver is responsible for the spanner that keeps related dynamics on a single line. --- lily/dynamic-align-engraver.cc | 164 ++++++++++++++++++++++++ lily/new-dynamic-engraver.cc | 223 +++++++++++++++++++++++++++++++++ ly/engraver-init.ly | 7 +- ly/spanners-init.ly | 15 ++- 4 files changed, 404 insertions(+), 5 deletions(-) create mode 100644 lily/dynamic-align-engraver.cc create mode 100644 lily/new-dynamic-engraver.cc diff --git a/lily/dynamic-align-engraver.cc b/lily/dynamic-align-engraver.cc new file mode 100644 index 0000000000..77feb08628 --- /dev/null +++ b/lily/dynamic-align-engraver.cc @@ -0,0 +1,164 @@ +/* + dynamic-align-engraver.cc -- implement Dynamic_align_engraver + + source file of the GNU LilyPond music typesetter + + (c) 2008 Han-Wen Nienhuys + +*/ + +#include + +#include "engraver.hh" + +#include "axis-group-interface.hh" +#include "directional-element-interface.hh" +#include "item.hh" +#include "side-position-interface.hh" +#include "spanner.hh" +#include "stream-event.hh" + +#include "translator.icc" + +class Dynamic_align_engraver : public Engraver +{ + TRANSLATOR_DECLARATIONS (Dynamic_align_engraver); + DECLARE_ACKNOWLEDGER (note_column); + DECLARE_ACKNOWLEDGER (dynamic); + DECLARE_END_ACKNOWLEDGER (dynamic); + +protected: + virtual void stop_translation_timestep (); + +private: + void create_line_spanner (Stream_event *cause); + Spanner* line_; + + vector ended_; + vector started_; + vector scripts_; + vector support_; + + set running_; +}; + +Dynamic_align_engraver::Dynamic_align_engraver () +{ + line_ = 0; +} + +ADD_ACKNOWLEDGER (Dynamic_align_engraver, dynamic); +ADD_ACKNOWLEDGER (Dynamic_align_engraver, note_column); +ADD_END_ACKNOWLEDGER (Dynamic_align_engraver, dynamic); + +void +Dynamic_align_engraver::create_line_spanner (Stream_event* event) +{ + if (!line_) + line_ = make_spanner ("DynamicLineSpanner", + event ? event->self_scm() : SCM_EOL); +} + +void +Dynamic_align_engraver::acknowledge_end_dynamic (Grob_info info) +{ + if (Spanner::has_interface(info.grob())) + ended_.push_back (info.spanner ()); +} + +void +Dynamic_align_engraver::acknowledge_note_column (Grob_info info) +{ + support_.push_back (info.grob ()); +} + +void +Dynamic_align_engraver::acknowledge_dynamic (Grob_info info) +{ + Stream_event *cause = info.event_cause (); + create_line_spanner (cause); + if (Spanner::has_interface(info.grob())) + started_.push_back (info.spanner ()); + else if (info.item()) + scripts_.push_back (info.item()); + else + info.grob ()->programming_error ("Unknown dynamic grob."); + + Axis_group_interface::add_element (line_, info.grob ()); + + if (cause) + { + if (Direction d = to_dir (cause->get_property ("direction"))) + set_grob_direction (line_, d); + } +} + +void +Dynamic_align_engraver::stop_translation_timestep () +{ + for (vsize i = 0; i < started_.size(); i++) + running_.insert (started_[i]); + for (vsize i = 0; i < ended_.size(); i++) + { + Spanner *sp = ended_[i]; + + set::iterator it = running_.find (sp); + if (it != running_.end()) + running_.erase (it); + else + started_[i]->programming_error ("Lost track of this dynamic spanner."); + } + + bool end = line_ && running_.empty (); + Direction d = LEFT; + do + { + if (line_ + && ((d == LEFT && !line_->get_bound (LEFT)) + || (end && d == RIGHT && !line_->get_bound (RIGHT)))) + { + vector const &spanners = + (d == LEFT) ? started_ : ended_; + + Grob *bound = 0; + if (scripts_.size()) + bound = scripts_[0]; + else if (spanners.size()) + bound = spanners[0]->get_bound (d); + else + { + programming_error ("Started DynamicLineSpanner but have no left bound."); + bound = unsmob_grob (get_property ("currentMusicalColumn")); + } + + line_->set_bound (d, bound); + } + } + while (flip (&d) != LEFT); + + for (vsize i = 0; line_ && i < support_.size (); i++) + Side_position_interface::add_support (line_, support_[i]); + + if (end) + line_ = 0; + + ended_.clear (); + started_.clear (); + scripts_.clear (); + support_.clear (); +} + + +ADD_TRANSLATOR (Dynamic_align_engraver, + /* doc */ + "Align hairpins and dynamic texts on a horizontal line", + + /* create */ + "DynamicLineSpanner ", + + /* read */ + "currentMusicalColumn ", + + /* write */ + "" + ); diff --git a/lily/new-dynamic-engraver.cc b/lily/new-dynamic-engraver.cc new file mode 100644 index 0000000000..0db0587351 --- /dev/null +++ b/lily/new-dynamic-engraver.cc @@ -0,0 +1,223 @@ +/* + new-dynamic-engraver.cc -- implement New_dynamic_engraver + + source file of the GNU LilyPond music typesetter + + (c) 2008 Han-Wen Nienhuys + +*/ + + +#include "engraver.hh" + +#include "item.hh" +#include "pointer-group-interface.hh" +#include "text-interface.hh" +#include "note-column.hh" +#include "self-alignment-interface.hh" +#include "spanner.hh" +#include "stream-event.hh" + +#include "translator.icc" + +class New_dynamic_engraver : public Engraver +{ + TRANSLATOR_DECLARATIONS (New_dynamic_engraver); + DECLARE_ACKNOWLEDGER (note_column); + DECLARE_TRANSLATOR_LISTENER (absolute_dynamic); + DECLARE_TRANSLATOR_LISTENER (span_dynamic); + +protected: + virtual void process_music (); + virtual void stop_translation_timestep (); +private: + Drul_array accepted_spanevents_drul_; + Spanner *current_spanner_; + Spanner *finished_spanner_; + + Item *script_; + Stream_event *script_event_; + Stream_event *current_span_event_; +}; + +New_dynamic_engraver::New_dynamic_engraver () +{ + script_event_ = 0; + current_span_event_ = 0; + script_ = 0; + finished_spanner_ = 0; + current_spanner_ = 0; + accepted_spanevents_drul_.set (0, 0); +} + +IMPLEMENT_TRANSLATOR_LISTENER (New_dynamic_engraver, absolute_dynamic); +void +New_dynamic_engraver::listen_absolute_dynamic (Stream_event *ev) +{ + ASSIGN_EVENT_ONCE (script_event_, ev); +} + +IMPLEMENT_TRANSLATOR_LISTENER (New_dynamic_engraver, span_dynamic); +void +New_dynamic_engraver::listen_span_dynamic (Stream_event *ev) +{ + Direction d = to_dir (ev->get_property ("span-direction")); + + ASSIGN_EVENT_ONCE (accepted_spanevents_drul_[d], ev); +} + + +void +New_dynamic_engraver::process_music () +{ + if (current_spanner_ + && (accepted_spanevents_drul_[STOP] || script_event_ || accepted_spanevents_drul_[START])) + { + Stream_event* ender = accepted_spanevents_drul_[STOP]; + if (!ender) + ender = script_event_; + + if (!ender) + ender = accepted_spanevents_drul_[START]; + + finished_spanner_ = current_spanner_; + announce_end_grob (finished_spanner_, ender->self_scm ()); + current_spanner_ = 0; + current_span_event_ = 0; + } + + if (accepted_spanevents_drul_[START]) + { + current_span_event_ = accepted_spanevents_drul_[START]; + + SCM start_sym = current_span_event_->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; + } + + SCM cresc_type = get_property ((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 ((start_type + "Text").c_str ()); + if (Text_interface::is_markup (text)) + { + current_spanner_->set_property ("text", text); + } + } + else + { + if (cresc_type != ly_symbol2scm ("hairpin")) + { + // Fixme: should put value in error message. + ly_display_scm (cresc_type); + current_span_event_ + ->origin()->warning ("unknown crescendo style; defaulting to hairpin."); + } + current_spanner_ = make_spanner ("Hairpin", + current_span_event_->self_scm ()); + if (finished_spanner_) + { + Pointer_group_interface::add_grob (finished_spanner_, + ly_symbol2scm ("adjacent-hairpins"), + current_spanner_); + + Pointer_group_interface::add_grob (current_spanner_, + ly_symbol2scm ("adjacent-hairpins"), + finished_spanner_); + } + } + } + + if (script_event_) + { + 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_); + set_nested_property (current_spanner_, + scm_list_3 (ly_symbol2scm ("bound-details"), + ly_symbol2scm ("left"), + ly_symbol2scm ("attach-dir") + ), + scm_from_int (RIGHT)); + + } + } +} + + + +void +New_dynamic_engraver::stop_translation_timestep () +{ + 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; +} + +void +New_dynamic_engraver::acknowledge_note_column (Grob_info info) +{ + 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 (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 (New_dynamic_engraver, note_column); +ADD_TRANSLATOR (New_dynamic_engraver, + /* doc */ + "Create hairpins, dynamic texts, and their vertical" + " alignments. The symbols are collected onto a" + " @code{DynamicLineSpanner} grob which takes care of vertical" + " positioning.", + + /* create */ + "DynamicTextSpanner " + "DynamicText " + "Hairpin " + "TextSpanner ", + + /* read */ + "currentMusicalColumn x", + + /* write */ + "" + ); diff --git a/ly/engraver-init.ly b/ly/engraver-init.ly index c2239ec054..480a1ad6dc 100644 --- a/ly/engraver-init.ly +++ b/ly/engraver-init.ly @@ -222,7 +222,9 @@ multiple voices on the same staff." \consists "Part_combine_engraver" \consists "Text_engraver" - \consists "Dynamic_engraver" + \consists "New_dynamic_engraver" + \consists "Dynamic_align_engraver" +% \consists "Dynamic_engraver" \consists "Fingering_engraver" \consists "Bend_engraver" @@ -505,6 +507,9 @@ automatically when an output definition (a @code{\score} or middleCClefPosition = #-6 middleCPosition = #-6 firstClef = ##t + + crescendoSpanner = #'hairpin + decrescendoSpanner = #'hairpin defaultBarType = #"|" barNumberVisibility = #first-bar-number-invisible diff --git a/ly/spanners-init.ly b/ly/spanners-init.ly index 60989ede0c..1425b22b14 100644 --- a/ly/spanners-init.ly +++ b/ly/spanners-init.ly @@ -51,31 +51,38 @@ endcresc = { setTextCresc = { \set crescendoText = \markup { \italic "cresc." } - \set crescendoSpanner = #'dashed-line + \set crescendoSpanner = #'text + \override DynamicTextSpanner #'style = #'dashed-line } setTextDecresc = { \set decrescendoText = \markup { \italic "decresc." } - \set decrescendoSpanner = #'dashed-line + \set decrescendoSpanner = #'text + \override DynamicTextSpanner #'style = #'dashed-line } setTextDecr = { \set decrescendoText = \markup { \italic "decr." } - \set decrescendoSpanner = #'dashed-line + \set decrescendoSpanner = #'text + \override DynamicTextSpanner #'style = #'dashed-line } setTextDim = { \set decrescendoText = \markup { \italic "dim." } - \set decrescendoSpanner = #'dashed-line + \set decrescendoSpanner = #'text + \override DynamicTextSpanner #'style = #'dashed-line } + setHairpinCresc = { \unset crescendoText \unset crescendoSpanner } + setHairpinDecresc = { \unset decrescendoText \unset decrescendoSpanner } + setHairpinDim = { \unset decrescendoText \unset decrescendoSpanner -- 2.39.5