On 1 May 2010 23:58, PálBenkő <benko....@gmail.com> wrote:
> hi all,
>
> patch and an example below.
>
> this may look academical, but I need this feature when
> transcribing renaissance music - for real life examples see
> http://wiki.lilynet.net/index.php/Benkop_projects
> the Ockeghem and la Rue examples.

This looks OK to me.  Can you add a short regression test to go in
input/regression?

> I want to handle rests also; could someone help me
> with advice?  in particular, should I add a listen_rest
> or handle it fully within note handling?

It should be done with a new engraver, which replaces Rest_engraver
and Multi_measure_rest_engraver.

I had a go at creating a Completion_rests_engraver last year (see the
attached patch), but it needs some more work to fix problems when
skipBars = ##t.

Thanks,
Neil
From b50e8ca24526b40d3ad2c382649679fe309132b7 Mon Sep 17 00:00:00 2001
From: Neil Puttock <n.putt...@gmail.com>
Date: Sat, 14 Nov 2009 13:50:56 +0000
Subject: [PATCH] Add Completion_rests_engraver.

---
 lily/completion-rests-engraver.cc |  282 +++++++++++++++++++++++++++++++++++++
 1 files changed, 282 insertions(+), 0 deletions(-)
 create mode 100644 lily/completion-rests-engraver.cc

diff --git a/lily/completion-rests-engraver.cc b/lily/completion-rests-engraver.cc
new file mode 100644
index 0000000..45a78d6
--- /dev/null
+++ b/lily/completion-rests-engraver.cc
@@ -0,0 +1,282 @@
+/*
+  completion-rests-engraver.cc -- Completion_rests_engraver
+
+  (c) 2009 Han-Wen Nienhuys <han...@xs4all.nl>
+*/
+
+#include "duration.hh"
+#include "global-context.hh"
+#include "output-def.hh"
+#include "paper-column.hh"
+#include "pitch.hh"
+#include "rhythmic-head.hh"
+#include "score-engraver.hh"
+#include "spanner.hh"
+#include "staff-symbol-referencer.hh"
+#include "stream-event.hh"
+#include "warn.hh"
+
+#include <iostream>
+using namespace std;
+
+#include "translator.icc"
+
+/*
+  How does this work?
+
+  When we catch the note, we predict the end of the note. We keep the
+  events living until we reach the predicted end-time.
+
+  Every time process_music () is called and there are note events, we
+  figure out how long the note to typeset should be. It should be no
+  longer than what's specified, than what is left to do and it should
+  not cross barlines.
+
+  We copy the events into scratch note events, to make sure that we get
+  all durations exactly right.
+*/
+
+class Completion_rests_engraver : public Engraver
+{
+  vector<Grob *> rests_;
+  vector<Grob *> prev_rests_;
+
+  vector<Stream_event *> rest_events_;
+
+  Moment rest_end_mom_;
+  bool is_first_;
+  Rational left_to_do_;
+  Rational do_nothing_until_;
+
+  Moment next_barline_moment ();
+  Grob *make_rest (Stream_event *);
+
+  int start_measure_;
+
+
+public:
+  TRANSLATOR_DECLARATIONS (Completion_rests_engraver);
+
+protected:
+  virtual void initialize ();
+  void start_translation_timestep ();
+  void process_music ();
+  void stop_translation_timestep ();
+  DECLARE_TRANSLATOR_LISTENER (rest);
+};
+
+void
+Completion_rests_engraver::initialize ()
+{
+  is_first_ = false;
+  start_measure_ = 0;
+}
+
+IMPLEMENT_TRANSLATOR_LISTENER (Completion_rests_engraver, rest);
+void
+Completion_rests_engraver::listen_rest (Stream_event *ev)
+{
+  rest_events_.push_back (ev);
+
+  is_first_ = true;
+  Moment now = now_mom ();
+  Moment musiclen = get_event_length (ev, now);
+
+  rest_end_mom_ = max (rest_end_mom_, (now + musiclen));
+  do_nothing_until_ = Rational (0, 0);
+}
+
+/*
+  The duration _until_ the next bar line.
+*/
+Moment
+Completion_rests_engraver::next_barline_moment ()
+{
+  Moment *e = unsmob_moment (get_property ("measurePosition"));
+  Moment *l = unsmob_moment (get_property ("measureLength"));
+
+  if (!e || !l || !to_boolean (get_property ("timing")))
+    return Moment (0, 0);
+  else
+    return (*l - *e);
+}
+
+Grob *
+Completion_rests_engraver::make_rest (Stream_event *ev)
+{
+  Grob *rest = 0;
+  if (*unsmob_moment (ev->get_property ("length"))
+      % *unsmob_moment (get_property ("measureLength")) == Moment (0, 0))
+    {
+      rest = make_spanner ("MultiMeasureRest", ev->self_scm ());
+      start_measure_ = scm_to_int (get_property ("internalBarNumber"));
+    }
+  else
+    {
+      rest = make_item ("Rest", ev->self_scm ());
+      Pitch *pit = unsmob_pitch (ev->get_property ("pitch"));
+
+      if (pit)
+	{
+	  int pos = pit->steps ();
+	  SCM c0 = get_property ("middleCPosition");
+	  if (scm_is_number (c0))
+	    pos += scm_to_int (c0);
+	  rest->set_property ("staff-position", scm_from_int (pos));
+	}
+    }
+  return rest;
+}
+
+void
+Completion_rests_engraver::process_music ()
+{
+  if (!is_first_ && !left_to_do_)
+    return;
+  is_first_ = false;
+
+  Moment now = now_mom ();
+  if (do_nothing_until_ > now.main_part_)
+    return;
+
+  Duration rest_dur;
+  Duration *orig = 0;
+  if (left_to_do_)
+    {
+      rest_dur = Duration (left_to_do_, false);
+    }
+  else
+    {
+      orig = unsmob_duration (rest_events_[0]->get_property ("duration"));
+      rest_dur = *orig;
+    }
+  Moment nb = next_barline_moment ();
+
+  if (*unsmob_moment (get_property ("measurePosition")) == Moment (0, 0)
+      && to_boolean (get_property ("skipBars"))
+      && (rest_dur.get_length () %
+          *unsmob_moment (get_property ("measureLength")) == Moment (0, 0)))
+    {
+      cout << "\nrest_dur " << rest_dur.to_string ();
+      cout << "\nfull-bar";
+      do_nothing_until_ = now.main_part_ + rest_dur.get_length ();
+    }
+  else if (nb.main_part_ && nb < rest_dur.get_length ())
+    {
+      rest_dur = Duration (nb.main_part_, false);
+
+      do_nothing_until_ = now.main_part_ + rest_dur.get_length ();
+    }
+
+  if (orig)
+    left_to_do_ = orig->get_length ();
+
+  for (vsize i = 0; left_to_do_ && i < rest_events_.size (); i++)
+    {
+      bool need_clone = !orig || *orig != rest_dur;
+      Stream_event *event = rest_events_[i];
+
+      if (need_clone)
+	event = event->clone ();
+
+      SCM pits = rest_events_[i]->get_property ("pitch");
+
+      if (unsmob_pitch (pits))
+	event->set_property ("pitch", pits);
+      event->set_property ("duration", rest_dur.smobbed_copy ());
+      event->set_property ("length",
+                           Moment (rest_dur.get_length ()).smobbed_copy ());
+
+      Grob *rest = make_rest (event);
+      if (need_clone)
+	event->unprotect ();
+      rests_.push_back (rest);
+    }
+
+  left_to_do_ -= rest_dur.get_length ();
+  if (left_to_do_)
+    get_global_context ()->
+      add_moment_to_process (now.main_part_ + rest_dur.get_length());
+  /*
+    don't do complicated arithmetic with grace notes.
+  */
+  if (orig && now_mom ().grace_part_)
+    left_to_do_ = Rational (0, 0);
+}
+
+void
+Completion_rests_engraver::stop_translation_timestep ()
+{
+  if (prev_rests_.size ())
+    {
+      Grob *cmc = unsmob_grob (get_property ("currentCommandColumn"));
+      for (vsize i = 0; i < prev_rests_.size (); i++)
+	{
+	  if (dynamic_cast<Spanner *> (prev_rests_[i]))
+            add_bound_item ((Spanner *) prev_rests_[i], cmc);
+
+	}
+      prev_rests_.clear ();
+    }
+
+  if (rests_.size ())
+    {
+      Grob *cmc = unsmob_grob (get_property ("currentCommandColumn"));
+      for (vsize i = 0; i < rests_.size (); i++)
+	{
+	  if (dynamic_cast<Spanner *> (rests_[i]))
+            add_bound_item ((Spanner *) rests_[i], cmc);
+
+	}
+
+      prev_rests_ = rests_;
+      rests_.clear ();
+    }
+}
+
+void
+Completion_rests_engraver::start_translation_timestep ()
+{
+
+  Moment now = now_mom ();
+  if (rest_end_mom_.main_part_ <= now.main_part_)
+    rest_events_.clear ();
+
+  if (prev_rests_.size ())
+    {
+      for (vsize i = 0; i < prev_rests_.size (); i++)
+        {
+          if (dynamic_cast<Spanner *> (prev_rests_[i]))
+            {
+              int cur = scm_to_int (get_property ("internalBarNumber"));
+              int num = cur - start_measure_;
+              prev_rests_[i]->set_property ("measure-count", scm_from_int (num));
+            }
+
+        }
+    }
+  context ()->set_property ("completionBusy",
+			    ly_bool2scm (rest_events_.size ()));
+}
+
+Completion_rests_engraver::Completion_rests_engraver ()
+{
+}
+
+ADD_TRANSLATOR (Completion_rests_engraver,
+		/* doc */
+		"This engraver replaces @code{Rest_engraver}.  It plays"
+		" some trickery to break long rests at bar lines.",
+
+		/* create */
+		"MultiMeasureRest "
+		"Rest ",
+
+		/* read */
+		"middleCPosition "
+		"measurePosition "
+		"measureLength ",
+
+		/* write */
+		"completionBusy "
+		);
-- 
1.6.3.3

_______________________________________________
lilypond-devel mailing list
lilypond-devel@gnu.org
http://lists.gnu.org/mailman/listinfo/lilypond-devel

Reply via email to