From a736b537c32e3b07a22ebedf3eacfeb46c57be25 Mon Sep 17 00:00:00 2001
From: Han-Wen Nienhuys <hanwen@xs4all.nl>
Date: Wed, 4 Feb 2004 15:00:15 +0000
Subject: [PATCH] * lily/parser.yy (part_combined_music): remove old PC cruft.

* ly/engraver-init.ly: remove old
PC cruft.

* Documentation/user/refman.itely (The Lyrics context): note about
extender lines.

* scm/part-combiner.scm (determine-split-list): many bugfixes.

* lily/new-part-combine-iterator.cc (kill_mmrest): new function.

* input/regression/new-part-combine-solo-global.ly: new file.

* scm/part-combiner.scm: rewrite.
---
 ChangeLog                                     |   8 +-
 Documentation/user/refman.itely               |  13 +-
 VERSION                                       |   4 +-
 ...-part-combine-a2.ly => part-combine-a2.ly} |   0
 ...-global.ly => part-combine-solo-global.ly} |   0
 ...t-combine-solo.ly => part-combine-solo.ly} |   0
 ...t-combine-text.ly => part-combine-text.ly} |   0
 .../{new-part-combine.ly => part-combine.ly}  |   0
 lily/a2-engraver.cc                           | 215 --------
 lily/include/part-combine-music-iterator.hh   |   0
 lily/my-lily-lexer.cc                         |   1 -
 lily/new-part-combine-iterator.cc             |   1 -
 lily/parser.yy                                |  15 +-
 lily/part-combine-music-iterator.cc           | 513 ------------------
 lily/thread-devnull-engraver.cc               |  58 --
 lily/voice-devnull-engraver.cc                | 116 ----
 ly/engraver-init.ly                           |   6 -
 scm/define-music-types.scm                    |  10 -
 scm/define-translator-properties.scm          |  35 +-
 scm/part-combiner.scm                         |   2 +-
 20 files changed, 24 insertions(+), 973 deletions(-)
 rename input/regression/{new-part-combine-a2.ly => part-combine-a2.ly} (100%)
 rename input/regression/{new-part-combine-solo-global.ly => part-combine-solo-global.ly} (100%)
 rename input/regression/{new-part-combine-solo.ly => part-combine-solo.ly} (100%)
 rename input/regression/{new-part-combine-text.ly => part-combine-text.ly} (100%)
 rename input/regression/{new-part-combine.ly => part-combine.ly} (100%)
 delete mode 100644 lily/a2-engraver.cc
 delete mode 100644 lily/include/part-combine-music-iterator.hh
 delete mode 100644 lily/part-combine-music-iterator.cc
 delete mode 100644 lily/thread-devnull-engraver.cc
 delete mode 100644 lily/voice-devnull-engraver.cc

diff --git a/ChangeLog b/ChangeLog
index 527d4fdc26..13ff20532d 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,7 +1,13 @@
 2004-02-04  Han-Wen Nienhuys   <hanwen@xs4all.nl>
 
+	* lily/parser.yy (part_combined_music): remove old PC cruft.
+
+	* ly/engraver-init.ly: remove old
+	PC cruft.
+
+	* Documentation/user/refman.itely (The Lyrics context): note about
+	extender lines.
 
-	
 	* lily/multi-measure-rest-engraver.cc (process_music): extra check
 	to allow 0-length mmrest events.
 
diff --git a/Documentation/user/refman.itely b/Documentation/user/refman.itely
index ccae49f6d6..ddebd09cf1 100644
--- a/Documentation/user/refman.itely
+++ b/Documentation/user/refman.itely
@@ -3435,7 +3435,9 @@ Internals: @internalsref{LyricEvent}, @internalsref{HyphenEvent}, and
 
 @refbugs
 
-The definition of lyrics mode is too complex. 
+The definition of lyrics mode is too complex.
+
+
 
 @node The Lyrics context
 @subsection  The Lyrics context
@@ -3548,6 +3550,12 @@ Melismata are not detected automatically, and extender lines must be
 inserted by hand.
 
 
+For proper processing of extender lines, the
+@internalsref{LyricsVoice} and @internalsref{Voice} should be
+linked. This can be achieved either by using @code{\lyricsto} or by
+setting corresponding names for both contexts. The latter is explained
+in @ref{More stanzas}.
+
 @node More stanzas
 @subsection More stanzas
 
@@ -3556,7 +3564,8 @@ inserted by hand.
 
 The lyrics should be aligned with the note heads of the melody. To
 achieve this, each line of lyrics should be marked to correspond with
-the melodic line.
+the melodic line. This is done automatically when @code{\lyricsto},
+but it can also be done manually. 
 
 To this end, give the @internalsref{Voice} context an identity:
 @example
diff --git a/VERSION b/VERSION
index 3eb1608264..1c463fd07a 100644
--- a/VERSION
+++ b/VERSION
@@ -1,6 +1,6 @@
 PACKAGE_NAME=LilyPond
 MAJOR_VERSION=2
 MINOR_VERSION=1
-PATCH_LEVEL=17
-MY_PATCH_LEVEL=hwn1
+PATCH_LEVEL=18
+MY_PATCH_LEVEL=
 
diff --git a/input/regression/new-part-combine-a2.ly b/input/regression/part-combine-a2.ly
similarity index 100%
rename from input/regression/new-part-combine-a2.ly
rename to input/regression/part-combine-a2.ly
diff --git a/input/regression/new-part-combine-solo-global.ly b/input/regression/part-combine-solo-global.ly
similarity index 100%
rename from input/regression/new-part-combine-solo-global.ly
rename to input/regression/part-combine-solo-global.ly
diff --git a/input/regression/new-part-combine-solo.ly b/input/regression/part-combine-solo.ly
similarity index 100%
rename from input/regression/new-part-combine-solo.ly
rename to input/regression/part-combine-solo.ly
diff --git a/input/regression/new-part-combine-text.ly b/input/regression/part-combine-text.ly
similarity index 100%
rename from input/regression/new-part-combine-text.ly
rename to input/regression/part-combine-text.ly
diff --git a/input/regression/new-part-combine.ly b/input/regression/part-combine.ly
similarity index 100%
rename from input/regression/new-part-combine.ly
rename to input/regression/part-combine.ly
diff --git a/lily/a2-engraver.cc b/lily/a2-engraver.cc
deleted file mode 100644
index e50fffae20..0000000000
--- a/lily/a2-engraver.cc
+++ /dev/null
@@ -1,215 +0,0 @@
-/*
-  a2-engraver.cc -- implement A2_engraver
-
-  source file of the GNU LilyPond music typesetter
-  
-  (c) 2000--2003 Jan Nieuwenhuizen <janneke@gnu.org>
-*/
-
-#include "engraver.hh"
-#include "item.hh"
-#include "note-head.hh"
-#include "stem.hh"
-#include "slur.hh"
-#include "translator-group.hh"
-#include "side-position-interface.hh"
-#include "directional-element-interface.hh"
-#include "multi-measure-rest.hh"
-#include "tie.hh"
-
-class A2_engraver : public Engraver
-{
-  TRANSLATOR_DECLARATIONS(A2_engraver);
-
-protected:
-  virtual void acknowledge_grob (Grob_info);
-  virtual void process_acknowledged_grobs ();
-  virtual void stop_translation_timestep ();
-private:
-  Item* text_;
-  enum State { SOLO, SPLIT_INTERVAL, UNIRHYTHM, UNISILENCE, UNISON } state_;
-};
-
-
-
-A2_engraver::A2_engraver ()
-{
-  text_ = 0;
-  state_ = UNISILENCE;
-}
-
-void
-A2_engraver::process_acknowledged_grobs ()
-{
-  if (!to_boolean (get_property ("combineParts")))
-    return ;
-  if (!text_)
-    {
-      SCM unison = get_property ("unison");
-      SCM solo = get_property ("solo");
-      SCM solo_adue = get_property ("soloADue");
-
-      if (solo_adue == SCM_BOOL_T
-	  && ((solo == SCM_BOOL_T && state_ != SOLO)
-	      || (unison == SCM_BOOL_T && state_ != UNISON
-		  && daddy_trans_->id_string_.left_string (3) == "one")))
-	{
-	  text_ = make_item ("TextScript");
-	  Side_position_interface::set_axis (text_, Y_AXIS);
-	  announce_grob(text_, SCM_EOL);
-      
-	  Direction dir = UP;
-	  SCM text = SCM_EOL;
-	  if (solo == SCM_BOOL_T)
-	    {
-	      state_ = SOLO;
-	      if (daddy_trans_->id_string_.left_string (3) == "one")
-		{
-		  text = get_property ("soloText");
-		}
-	      else
-		{
-		  text = get_property ("soloIIText");
-		  dir = DOWN;
-		}
-	    }
-	  else if (unison == SCM_BOOL_T)
-	    {
-	      state_ = UNISON;
-	      if (daddy_trans_->id_string_.left_string (3) == "one")
-		text = get_property ("aDueText");
-	    }
-	  
-	  set_grob_direction (text_, dir);
-	  text_->set_grob_property ("text", text);
-	}
-    }
-}
-
-void
-A2_engraver::acknowledge_grob (Grob_info i)
-{
-  if (!to_boolean (get_property ("combineParts")))
-    return ;
-  
-  if (text_)
-    {
-      if (Note_head::has_interface (i.grob_))
-	{
-	  Grob*t = text_;
-	  Side_position_interface::add_support (t, i.grob_);
-	  if (Side_position_interface::get_axis (t) == X_AXIS
-	      && !t->get_parent (Y_AXIS))
-	    t->set_parent (i.grob_, Y_AXIS);
-	}
-      if (Stem::has_interface (i.grob_))
-	{
-	  Side_position_interface::add_support (text_, i.grob_);
-	}
-    }
-	  
-  SCM unisilence = get_property ("unisilence");
-  SCM unison = get_property ("unison");
-  SCM unirhythm = get_property ("unirhythm");
-  SCM solo = get_property ("solo");
-  SCM split_interval = get_property ("split-interval");
-  SCM solo_adue = get_property ("soloADue");
-  
-  State previous_state = state_;
-  if (unisilence == SCM_BOOL_T)
-    /*
-      state_ = UNISILENCE;
-    */
-    ;
-  else if (solo == SCM_BOOL_T)
-    state_ = SOLO;
-  else if (unison == SCM_BOOL_T)
-    state_ = UNISON;
-  else if (unirhythm == SCM_BOOL_T && split_interval == SCM_BOOL_T)
-    state_ = SPLIT_INTERVAL;
-  else if (unirhythm)
-    state_ = UNIRHYTHM;
-
-  Direction d = CENTER;
-  if (daddy_trans_->id_string_.left_string (3) == "one")
-    d =  UP;
-  else if (daddy_trans_->id_string_.left_string (3) == "two")
-    d = DOWN;
-
-  /* Must only set direction for VoiceCombines, not for StaffCombines:
-     we can't detect that here, so we use yet another property */
-  if (!to_boolean (get_property ("noDirection"))
-      && (Stem::has_interface (i.grob_)
-	  || Slur::has_interface (i.grob_)
-	  || Tie::has_interface (i.grob_)
-	  /* Usually, dynamics are removed by *_devnull_engravers for
-	     the second voice.  On the one hand, we don't want all
-	     dynamics for the first voice to be placed above the
-	     staff.  On the other hand, colliding of scripts may be
-	     worse.  So, we don't set directions for these when we're
-	     playing solo. */
-	  || (i.grob_->internal_has_interface (ly_symbol2scm
-						 ("dynamic-interface"))
-	      && state_ != SOLO)
-	  || (i.grob_->internal_has_interface (ly_symbol2scm
-						 ("text-interface"))
-	      && state_ != SOLO)
-	  ))
-    {
-      /* When in solo a due mode, and we have solo, every grob in
-	 other thread gets annihilated, so we don't set dir.
-
-	 Maybe that should be optional? */
-      if ((solo != SCM_BOOL_T && solo_adue == SCM_BOOL_T)
-
-	  /* When not same rhythm, we set dir */
-	  && (unirhythm != SCM_BOOL_T
-	      /* When both have rests, but previously played something
-		 different, we set dir */
-	      || ((unisilence == SCM_BOOL_T && previous_state != UNISON))
-	      /* When same rhythm, and split stems, but not same pitch
-	         or not solo a du mode, we set dir */
-	      || (unirhythm == SCM_BOOL_T && split_interval == SCM_BOOL_T
-		  && (unison != SCM_BOOL_T || solo_adue != SCM_BOOL_T))))
-	{
-	
-	  /* Blunt axe method: every grob gets a propertysetting. */
-	  i.grob_->set_grob_property ("direction", scm_int2num (d));
-	}
-    }
-
-  /* Should we have separate state variable for being "rest
-     while other has solo?"  */
-  if (Multi_measure_rest::has_interface (i.grob_) && d)
-    if (state_ == UNIRHYTHM
-	&& unisilence != SCM_BOOL_T)
-    {
-      i.grob_->set_grob_property ("staff-position", scm_int2num (d * 6));
-    }
-}
-
-void 
-A2_engraver::stop_translation_timestep ()
-{
-  if (text_)
-    {
-      typeset_grob (text_);
-      text_ = 0;
-    }
-}
-
-ENTER_DESCRIPTION(A2_engraver,
-/* descr */       "Part combine engraver for orchestral scores.		"
-"The markings @emph{a2}, @emph{Solo} and @emph{Solo II}, are		"
-"created by this engraver.  It also acts upon instructions of the part	"
-"combiner.  Another thing that the this engraver, is forcing of stem,	"
-"slur and tie directions, always when both threads are not identical;	"
-"up for the musicexpr called @code{one}, down for the musicexpr called	"
-"@code{two}.								"
-,
-/* creats*/       "TextScript",
-/* accepts */     "",
-/* acks  */       "multi-measure-rest-interface "
-"slur-interface stem-interface tie-interface note-head-interface dynamic-interface text-interface"
-,/* reads */       "combineParts noDirection soloADue soloText soloIIText aDueText split-interval unison solo unisilence unirhythm",
-/* write */       "");
diff --git a/lily/include/part-combine-music-iterator.hh b/lily/include/part-combine-music-iterator.hh
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/lily/my-lily-lexer.cc b/lily/my-lily-lexer.cc
index 9eead743d7..c2ee25616e 100644
--- a/lily/my-lily-lexer.cc
+++ b/lily/my-lily-lexer.cc
@@ -60,7 +60,6 @@ static Keyword_ent the_key_tab[]={
   {"midi", MIDI},
   {"name", NAME},
   {"new", NEWCONTEXT},
-  {"newpartcombine", NEWPARTCOMBINE},
   {"notes", NOTES},
   {"octave", OCTAVE},
   {"once", ONCE},
diff --git a/lily/new-part-combine-iterator.cc b/lily/new-part-combine-iterator.cc
index 37a978030a..872941f444 100644
--- a/lily/new-part-combine-iterator.cc
+++ b/lily/new-part-combine-iterator.cc
@@ -6,7 +6,6 @@
      (c) 2004 Han-Wen Nienhuys
 */
 
-#include "part-combine-music-iterator.hh"
 #include "translator-group.hh"
 #include "event.hh"
 #include "music-sequence.hh"
diff --git a/lily/parser.yy b/lily/parser.yy
index 277365e90f..a611eebca9 100644
--- a/lily/parser.yy
+++ b/lily/parser.yy
@@ -283,7 +283,6 @@ yylex (YYSTYPE *s,  void * v)
 %token OVERRIDE SET REVERT 
 %token PAPER
 %token PARTCOMBINE
-%token NEWPARTCOMBINE
 %token PARTIAL
 %token PITCHNAMES
 %token PROPERTY
@@ -1114,20 +1113,10 @@ re_rhythmed_music:
 	;
 
 part_combined_music:
-	PARTCOMBINE STRING Music Music {
-		Music * p= MY_MAKE_MUSIC("PartCombineMusic");
-		p->set_mus_property ("what", scm_string_to_symbol ($2));
-		p->set_mus_property ("elements", gh_list ($3->self_scm (),$4->self_scm (), SCM_UNDEFINED));  
-
-		scm_gc_unprotect_object ($3->self_scm ());
-		scm_gc_unprotect_object ($4->self_scm ());  
-
-		$$ = p;
-	}
-	| NEWPARTCOMBINE Music Music {
+	PARTCOMBINE Music Music {
 		static SCM proc;
 		if (!proc)
-			proc = scm_c_eval_string ("make-new-part-combine-music");
+			proc = scm_c_eval_string ("make-part-combine-music");
 
 		SCM res = scm_call_1 (proc, gh_list ($2->self_scm (),
 			$3->self_scm (), SCM_UNDEFINED));  
diff --git a/lily/part-combine-music-iterator.cc b/lily/part-combine-music-iterator.cc
deleted file mode 100644
index 6ce902c4c8..0000000000
--- a/lily/part-combine-music-iterator.cc
+++ /dev/null
@@ -1,513 +0,0 @@
-/*   
-  part-combine-music-iterator.cc -- implement  Part_combine_music_iterator
-
-  source file of the GNU LilyPond music typesetter
-  
-  (c) 2000--2003 Jan Nieuwenhuizen <janneke@gnu.org>
- */
-
-#include "part-combine-music-iterator.hh"
-#include "translator-group.hh"
-#include "event.hh"
-#include "music-sequence.hh"
-#include "lily-guile.hh"
-#include "warn.hh"
-#include "music-iterator.hh"
-
-class Part_combine_music_iterator : public Music_iterator
-{
-public:
-  VIRTUAL_COPY_CONS (Music_iterator);
-  Part_combine_music_iterator ();
-
-  enum State { UNKNOWN, UNRELATED=1, SOLO1=2, SOLO2=4, UNIRHYTHM=8, UNISON=16, UNISILENCE=32, SPLIT_INTERVAL=64 };
-  DECLARE_SCHEME_CALLBACK(constructor, ()); 
-protected:
-  virtual void derived_substitute (Translator_group*f, Translator_group*t) ;
-
-  virtual void derived_mark () const;
-  Part_combine_music_iterator (Part_combine_music_iterator const &);
-  virtual void construct_children ();
-  virtual Moment pending_moment () const;
-  virtual void do_quit(); 
-  virtual void process (Moment);
-  virtual SCM get_pending_events (Moment)const;
-  virtual Music_iterator *try_music_in_children (Music *) const;
-  virtual bool ok () const;
-
-private:
-  void change_to (Music_iterator*, SCM,  String);
-  int get_state (Moment m);
-
-  Music_iterator * first_iter_;
-  Music_iterator * second_iter_;
-  Moment first_until_;
-  Moment second_until_;
-  int state_;
-  String suffix_;
-};
-
-
-Part_combine_music_iterator::Part_combine_music_iterator ()
-{
-  first_iter_ = 0;
-  second_iter_ = 0;
-  first_until_ = 0;
-  second_until_ = 0;
-  state_ = 0;
-}
-
-void
-Part_combine_music_iterator::derived_mark () const
-{
-  if (first_iter_)
-    scm_gc_mark (first_iter_->self_scm());
-  if (second_iter_)
-    scm_gc_mark(second_iter_->self_scm());
-}
-
-void
-Part_combine_music_iterator::derived_substitute (Translator_group*f,
-						 Translator_group*t)
-{
-  if (first_iter_)
-    first_iter_->substitute_outlet (f,t);
-  if (second_iter_)
-    second_iter_->substitute_outlet (f,t);
-}
-
-void
-Part_combine_music_iterator::do_quit ()
-{
-  if (first_iter_)
-    first_iter_->quit();
-  if (second_iter_)
-    second_iter_->quit();
-}
-
-Part_combine_music_iterator::Part_combine_music_iterator (Part_combine_music_iterator const &src)
-  : Music_iterator (src)
-{
-  first_iter_ = 0;
-  second_iter_ = 0;
-
-  if(src.first_iter_)
-    first_iter_ = src.first_iter_->clone ();
-  if (src.second_iter_)
-    second_iter_ = src.second_iter_->clone ();
-
-  first_until_ = src.first_until_;
-  second_until_ = src.second_until_;
-  state_ = src.state_;
-  suffix_ = src.suffix_;
-
-  if (first_iter_)
-    scm_gc_unprotect_object (first_iter_->self_scm());
-  if (second_iter_)
-    scm_gc_unprotect_object (second_iter_->self_scm());
-}
-
-Moment
-Part_combine_music_iterator::pending_moment () const
-{
-  Moment p;
-  p.set_infinite (1);
-  if (first_iter_->ok ())
-    p = p <? first_iter_->pending_moment ();
-
-  if (second_iter_->ok ())
-    p = p <? second_iter_->pending_moment ();
-  return p;
-}
-
-bool
-Part_combine_music_iterator::ok () const
-{
-  return first_iter_->ok () || second_iter_->ok ();
-}
-
-
-void
-Part_combine_music_iterator::construct_children ()
-{
-  SCM lst = get_music ()->get_mus_property ("elements");
-
-  first_iter_ = unsmob_iterator (get_iterator (unsmob_music (gh_car (lst))));
-  second_iter_ = unsmob_iterator (get_iterator (unsmob_music (gh_cadr (lst))));
-}
-
-void
-Part_combine_music_iterator::change_to (Music_iterator *it, SCM to_type,
-					String to_id)
-{
-  Translator_group * current = it->report_to ();
-  Translator_group * last = 0;
-
-  /*
-    Cut & Paste from from Auto_change_iterator from Change_iterator (ugh).
-
-    TODO: abstract this function 
-   */
-  
-  /* find the type  of translator that we're changing.
-     
-     If \translator Staff = bass, then look for Staff = *
-   */
-  while (current && !current->is_alias (to_type))
-    {
-      last = current;
-      current = current->daddy_trans_;
-    }
-
-  if (current && current->id_string_ == to_id)
-    {
-      String msg;
-      msg += _ ("Can't switch translators, I'm there already");
-    }
-  
-  if (current) 
-    if (last)
-      {
-	Translator_group * dest = 
-	  it->report_to ()->find_create_translator (to_type, to_id, SCM_EOL);
-	current->remove_translator (last);
-	dest->add_used_group_translator (last);
-      }
-    else
-      {
-	/*
-	  We could change the current translator's id, but that would make 
-	  errors hard to catch
-	  
-	   last->translator_id_string_  = get_change ()->change_to_id_string_;
-	*/
-	error (_f ("I'm one myself: `%s'", ly_symbol2string (to_type).to_str0 ()));
-      }
-  else
-    error (_f ("none of these in my family: `%s'", to_id.to_str0 ()));
-}
-
-
-// SCM*, moet / kan dat niet met set_x ofzo?
-static void
-get_music_info (Moment m, Music_iterator* iter, SCM *pitches, SCM *durations)
-{
-  if (iter->ok ())
-    {
-      for (SCM i = iter->get_pending_events (m); gh_pair_p (i); i = ly_cdr (i))
-	{
-	  Music *m = unsmob_music (ly_car (i));
-	  SCM p = m->get_mus_property ("pitch");
-	  SCM d = m->get_mus_property ("duration");
-	  if (unsmob_pitch (p))
-	    *pitches = gh_cons (p, *pitches);
-	  if (unsmob_duration (d))
-	    *durations = gh_cons (d, *durations);
-	}
-    }
-}
-
-int
-Part_combine_music_iterator::get_state (Moment)
-{
-  int state = UNKNOWN;
-  
-  Music *p = get_music ();
-
-  SCM w = p->get_mus_property ("what");
-    
-  
-  Translator_group *first_translator = first_iter_->report_to ()->find_create_translator (w, "one" + suffix_, SCM_EOL);
-
-  SCM s = first_translator->get_property ("changeMoment");
-  if (!gh_pair_p (s))
-    return state;
-
-  Moment change_mom = *unsmob_moment (ly_car (s));
-  Moment diff_mom = *unsmob_moment (ly_cdr (s));
-  
-  Moment now = pending_moment ();
-
-  if (!now.main_part_.mod_rat (change_mom.main_part_))
-    {
-      SCM interval = SCM_BOOL_F;
-      if (first_until_ < now)
-	first_until_ = now;
-      if (second_until_ < now)
-	second_until_ = now;
-
-      Moment first_mom = first_until_;
-      Moment second_mom = second_until_;
-      Moment diff_until = diff_mom + now;
-
-
-      bool first = true;
-      Music_iterator *first_iter = first_iter_->clone ();
-      Music_iterator *second_iter = second_iter_->clone ();
-      
-      Moment last_pending (-1);
-      Moment pending = now;
-      while (now < diff_until
-	      && (first_iter->ok () || second_iter->ok ())
-
-	     // urg, this is a hack, haven't caught this case yet
-	     && (pending != last_pending))
-	{
-	  if (!second_iter->ok ())
-	    pending = first_iter->pending_moment ();
-	  else if (!first_iter->ok ())
-	    pending = second_iter->pending_moment ();
-	  else
-	    pending = first_iter->pending_moment () <? second_iter->pending_moment ();
-	  last_pending = pending;
-
-	  SCM first_pitches = SCM_EOL;
-	  SCM first_durations = SCM_EOL;
-	  get_music_info (pending, first_iter,
-			  &first_pitches, &first_durations);
-      
-	  SCM second_pitches = SCM_EOL;
-	  SCM second_durations = SCM_EOL;
-	  get_music_info (pending, second_iter,
-			  &second_pitches, &second_durations);
-
-	  if (first_pitches != SCM_EOL && second_pitches != SCM_EOL)
-	    {
-	      scm_sort_list_x (first_pitches, Pitch::less_p_proc);
-	      scm_sort_list_x (second_pitches, Pitch::less_p_proc);
-
-	      interval = gh_int2scm (unsmob_pitch (ly_car (first_pitches))->steps ()
-				     - unsmob_pitch (ly_car (scm_last_pair (second_pitches)))->steps ());
-	    }
-
-	  if (first_durations != SCM_EOL)
-	    {
-	      scm_sort_list_x (first_durations,
-			       Duration::less_p_proc);
-	      first_mom += unsmob_duration (ly_car (first_durations))->get_length ();
-	    }
-	  
-	  if (second_durations != SCM_EOL)
-	    {
-	      scm_sort_list_x (second_durations,
-			       Duration::less_p_proc);
-	      second_mom += unsmob_duration (ly_car (second_durations))->get_length ();
-	    }
-	  
-	  if (first_pitches != SCM_EOL && second_pitches == SCM_EOL
-		  && ! (second_until_ > now))
-	    {
-	      state |= UNRELATED;
-	      state &= ~UNISILENCE;
-	      if (! (state & ~ (UNRELATED | SOLO1 | UNISILENCE)))
-		state |= SOLO1;
-	    }
-	  else
-	    state &= ~SOLO1;
-
-	  if (first_pitches == SCM_EOL && second_pitches != SCM_EOL
-	      && ! (first_until_ > now))
-	    {
-	      state |= UNRELATED;
-	      state &= ~UNISILENCE;
-	      if (! (state & ~ (UNRELATED | SOLO2 | UNISILENCE)))
-		state |= SOLO2;
-	    }
-	  else
-	    state &= ~SOLO2;
-
- 	  if (gh_equal_p (first_durations, second_durations))
-	    {
-	      state &= ~UNISILENCE;
-	      if (! (state & ~ (UNIRHYTHM | UNISON)))
-		state |= UNIRHYTHM;
-	    }
-	  else
-	    state &= ~ (UNIRHYTHM | UNISILENCE);
-	  
-	  if (first_pitches != SCM_EOL
-	      && gh_equal_p (first_pitches, second_pitches))
-	    {
-	      state &= ~UNISILENCE;
-	      if (! (state & ~ (UNIRHYTHM | UNISON)))
-		state |= UNISON;
-	    }
-	  else
-	    state &= ~ (UNISON);
-	    
-	  if (first_pitches == SCM_EOL && second_pitches == SCM_EOL)
-	    {
-	      if (! (state & ~ (UNIRHYTHM | UNISILENCE)))
-		state |= UNISILENCE;
-	    }
-	  else if (!state)
-	    state |= UNRELATED;
-	  else
-	    state &= ~ (UNISILENCE);
-
-	  if (gh_number_p (interval))
-	    {
-	      SCM s = first_translator->get_property ("splitInterval");
-	      int i = gh_scm2int (interval);
-	      if (gh_pair_p (s)
-		  && gh_number_p (ly_car (s))
-		  && gh_number_p (ly_cdr (s))
-		  && i >= gh_scm2int (ly_car (s))
-		  && i <= gh_scm2int (ly_cdr (s)))
-		{
-		  if (! (state & ~ (SPLIT_INTERVAL | UNIRHYTHM | UNISON)))
-		    state |= SPLIT_INTERVAL;
-		}
-	      else
-		state &= ~ (SPLIT_INTERVAL);
-	    }
-
-	  if (first && first_pitches != SCM_EOL)
-	    first_until_ = first_mom;
-	  if (first && second_pitches != SCM_EOL)
-	    second_until_ = second_mom;
-	  first = false;
-
-	  if (first_iter->ok ())
-	    first_iter->skip (pending);
-	  if (second_iter->ok ())
-	    second_iter->skip (pending);
-	  now = pending;
-	}
-      scm_gc_unprotect_object (first_iter->self_scm ());
-      scm_gc_unprotect_object (second_iter->self_scm ());
-    }
-
-  return state;
-}
-
-static Music* abort_req = NULL;
-
-void
-Part_combine_music_iterator::process (Moment m)
-{
-
-  /*
-    TODO:
-    - Use three named contexts (be it Thread or Voice): one, two, solo.
-      Let user pre-set (pushproperty) stem direction, remove
-      dynamic-engraver, and such.
-
-      **** Tried this, but won't work:
-
-s      Consider thread switching: threads "one", "two" and "both".
-      User can't pre-set the (most important) stem direction at
-      thread level!
-   */
- 
-  if (suffix_.is_empty ())
-    suffix_ = first_iter_->report_to ()
-      ->daddy_trans_->id_string_.cut_string (3, INT_MAX);
-
-  int state = get_state (m);
-  if (state)
-    state_ = state;
-  else
-    state = state_;
-  
-  Music *p =get_music ();
-
-
-  bool previously_combined_b = first_iter_->report_to ()->daddy_trans_
-    == second_iter_->report_to ()->daddy_trans_;
-
-  bool combine_b = previously_combined_b;
-
-  if (! (state & UNIRHYTHM)
-      || (state & SPLIT_INTERVAL)
-      || (state & (SOLO1 | SOLO2)))
-    combine_b = false;
-  else if (state & (UNIRHYTHM | UNISILENCE))
-    combine_b = true;
-
-  /*
-    When combining, abort all running spanners
-   */
-
-  if (!abort_req)
-    {
-      abort_req = make_music_by_name (ly_symbol2scm ("AbortEvent"));
-    }
-  
-  if (combine_b && combine_b != previously_combined_b)
-    {
-      if (second_iter_ && second_iter_->ok ())
-	second_iter_->try_music (abort_req);
-     }
-  SCM  w = p->get_mus_property ("what");
-  if (combine_b != previously_combined_b)
-    change_to (second_iter_, w, (combine_b ? "one" : "two")
-	       + suffix_);
-  
-  Translator_group *first_translator = first_iter_->report_to ()->find_create_translator (w, "one" + suffix_, SCM_EOL);
-  Translator_group *second_translator = second_iter_->report_to ()->find_create_translator (w, "two" + suffix_, SCM_EOL);
-  
-
-  /* Hmm */
-  first_translator->set_property ("combineParts", SCM_BOOL_T);
-  second_translator ->set_property ("combineParts", SCM_BOOL_T);
- 
- 
-  /* hmm */
-  SCM b = (state & UNIRHYTHM) ? SCM_BOOL_T : SCM_BOOL_F;
-  first_translator->set_property ("unirhythm", b);
-  second_translator->set_property ("unirhythm", b);
-
-  b = (state & SPLIT_INTERVAL) ? SCM_BOOL_T : SCM_BOOL_F;
-  first_translator->set_property ("split-interval", b);
-  second_translator->set_property ("split-interval",  b);
-
-  b = (state & UNISILENCE) ? SCM_BOOL_T : SCM_BOOL_F;
-  first_translator->set_property ("unisilence", b);
-  second_translator->set_property ("unisilence", b);
-
-  // difference in definition...
-  //b = ((state & UNISON) ? SCM_BOOL_T : SCM_BOOL_F;
-  b = ((state & UNISON) && (state & UNIRHYTHM)) ? SCM_BOOL_T : SCM_BOOL_F;
-  first_translator->set_property ("unison", b);
-  second_translator->set_property ("unison", b);
-
-  SCM b1 = (state & SOLO1) ? SCM_BOOL_T : SCM_BOOL_F;
-  SCM b2 = (state & SOLO2) ? SCM_BOOL_T : SCM_BOOL_F;
-  first_translator->set_property ("solo", b1);
-  second_translator->set_property ("solo", b2);
-
-  /* Can't these be computed? */
-  first_translator->set_property ("othersolo", b2);
-  second_translator->set_property ("othersolo", b1);
-
-  if (first_iter_->ok ())
-    first_iter_->process (m);
-  
-  if (second_iter_->ok ())
-    second_iter_->process (m);
-}
-
-Music_iterator*
-Part_combine_music_iterator::try_music_in_children (Music *m) const
-{
-  Music_iterator * i =  first_iter_->try_music (m);
-  if (i)
-    return i;
-  else
-    return second_iter_->try_music (m);
-}
-
-
-SCM
-Part_combine_music_iterator::get_pending_events (Moment m)const
-{
-  SCM s = SCM_EOL;
-  if (first_iter_)
-    s = gh_append2 (s,first_iter_->get_pending_events (m));
-  if (second_iter_)
-    s = gh_append2 (second_iter_->get_pending_events (m),s);
-  return s;
-}
-
-IMPLEMENT_CTOR_CALLBACK (Part_combine_music_iterator);
diff --git a/lily/thread-devnull-engraver.cc b/lily/thread-devnull-engraver.cc
deleted file mode 100644
index 442b9db1d4..0000000000
--- a/lily/thread-devnull-engraver.cc
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
-  thread-devnull-engraver.cc -- implement Thread_devnull_engraver
-
-  source file of the GNU LilyPond music typesetter
-  
-  (c) 2000--2003 Jan Nieuwenhuizen <janneke@gnu.org>
-*/
-
-#include "engraver.hh"
-#include "item.hh"
-#include "event.hh"
-#include "translator-group.hh"
-
-class Thread_devnull_engraver : public Engraver
-{
-public:
-  TRANSLATOR_DECLARATIONS(Thread_devnull_engraver);
-  
-protected:
-  virtual void acknowledge_grob (Grob_info);
-};
-
-
-
-void
-Thread_devnull_engraver::acknowledge_grob (Grob_info i)
-{
-  SCM s = get_property ("devNullThread");
-
-  if (s == ly_symbol2scm ("always")
-      || (s == SCM_EOL
-	  && to_boolean (get_property ("soloADue"))
-	  && ((daddy_trans_->id_string_.left_string (3) == "two"
-	       && (to_boolean (get_property ("unison"))
-		   || to_boolean (get_property ("unisilence"))))
-
-	      /* Maybe this should be optional? */
-	      || to_boolean (get_property ("othersolo")))))
-    {
-      i.grob_->suicide ();
-    }
-}
-Thread_devnull_engraver::Thread_devnull_engraver(){}
-
-ENTER_DESCRIPTION(Thread_devnull_engraver,
-/* descr */       "Kill elements whenever we are Voice called `two' and either "
-"unison, unisilence or soloADue is set.@footnote{On unix systems, the "
-"file @file{/dev/null} is special device: anything written to it is "
-"discarded.}. This engraver works closely together with the part "
-"combiner.  When the part combiner notices that two threads are "
-"identical, it tells the @code{Thread_devnull_engraver} to discard " 
-"everything in the second thread. "
-,
-/* creats*/       "",
-/* accepts */     "",
-/* acks  */      "grob-interface",
-/* reads */       "",
-/* write */       "");
diff --git a/lily/voice-devnull-engraver.cc b/lily/voice-devnull-engraver.cc
deleted file mode 100644
index 81e99a03ad..0000000000
--- a/lily/voice-devnull-engraver.cc
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
-  voice-devnull-engraver.cc -- implement Voice_devnull_engraver
-
-  source file of the GNU LilyPond music typesetter
-  
-  (c) 2000--2003 Jan Nieuwenhuizen <janneke@gnu.org>
-*/
-
-#include "engraver.hh"
-#include "item.hh"
-#include "event.hh"
-#include "translator-group.hh"
-
-class Voice_devnull_engraver : public Engraver
-{
-public:
-  TRANSLATOR_DECLARATIONS(Voice_devnull_engraver);
-  
-protected:
-  virtual bool try_music (Music *m);
-  virtual void acknowledge_grob (Grob_info);
-};
-
-
-/*
-
-ARGH .
-
-
-This really sucks.
-
- */
-static char const *eat_spanners[] = {
-  "beam",
-  "crescendo",
-  "decrescendo",
-  "rest",
-  "slur",
-  0
-};
-
-bool
-Voice_devnull_engraver::try_music (Music *m)
-{
-  SCM s = get_property ("devNullVoice");
-
-  if (gh_equal_p (s, ly_symbol2scm ("always"))
-      || (s == SCM_EOL
-	  && daddy_trans_->id_string_.left_string (3) == "two"
-	  && (to_boolean (get_property ("unison"))
-	      || to_boolean (get_property ("unisilence")))))
-    {
-      if (m->is_mus_type ("span-event"))
-	{
-	  SCM t = m->get_mus_property ("span-type");
-        
-	  for (char const **p = eat_spanners; *p; p++)
-	    {
-	      if (t == scm_makfrom0str (*p))
-		return true;
-	    }
-	}
-      /* Ugh.  Should eat other events, script etc. too. */  
-      else if (m->is_mus_type ("tie-event"))
-	return true;
-    }
-  return false;
-}
-    
-static char const *junk_interfaces[] = {
-  "beam-interface",
-  "dynamic-interface",
-  "hairpin-interface",
-  "multi-measure-rest-interface",
-  "script-interface",
-  "slur-interface",
-  "text-interface",
-  "text-item-interface",
-  "text-script-interface",
-  "text-spanner-interface",
-  "tie-interface",
-  0
-};
-
-void
-Voice_devnull_engraver::acknowledge_grob (Grob_info i)
-{
-  SCM s = get_property ("devNullVoice");
-
-  if (s == ly_symbol2scm ("always")
-      || (s == SCM_EOL
-	  && to_boolean (get_property ("soloADue"))
-	  && ((daddy_trans_->id_string_.left_string (3) == "two"
-	       && (to_boolean (get_property ("unison"))
-		   || to_boolean (get_property ("unisilence"))))
-		
-	      /* Maybe this should be optional? */
-	      || to_boolean (get_property ("othersolo")))))
-    
-    for (char const **p = junk_interfaces; *p; p++)
-      if (i.grob_->internal_has_interface (ly_symbol2scm (*p)))
-	{
-	  i.grob_->suicide ();
-	  return;
-	}
-}
- 
-Voice_devnull_engraver::Voice_devnull_engraver(){}
-  
-ENTER_DESCRIPTION(Voice_devnull_engraver,
-/* descr */       "Kill off certain items and spanners if we're Voice `two' and unison or unisilence is set.",
-/* creats*/       "",
-/* accepts */     "general-music", /*UGH.*/
-/* acks  */      "grob-interface",
-/* reads */       "",
-/* write */       "");
diff --git a/ly/engraver-init.ly b/ly/engraver-init.ly
index 47de3c9cf0..adaf969bd8 100644
--- a/ly/engraver-init.ly
+++ b/ly/engraver-init.ly
@@ -151,8 +151,6 @@
 	localKeySignature = #'()
 	\consists "Font_size_engraver"
 	
-	% must come before all
-	\consists "Voice_devnull_engraver"
 	\consists "Output_property_engraver"	
 	\consists "Arpeggio_engraver"
 	\consists "Multi_measure_rest_engraver"
@@ -191,7 +189,6 @@
 	\consists "Slur_engraver"
 	\consists "Tie_engraver"
 	\consists "Tuplet_engraver"
-	\consists "A2_engraver"
 
 	\consists "Skip_event_swallow_translator"
 	\accepts Thread % bug if you leave out this!
@@ -207,7 +204,6 @@
     style of individual note heads.
 "
 	\consists "Font_size_engraver"	
-	\consists "Thread_devnull_engraver"
 	\consists "Note_heads_engraver"
 	\consists "Rest_engraver"
 
@@ -442,8 +438,6 @@ AncientRemoveEmptyStaffContext = \translator {
 	soloIIText = #"Solo II"
 	aDueText = #"a2"
 	soloADue = ##t
-	splitInterval = #'(0 . 1)
-	changeMoment = #`(,(ly:make-moment 0 0) . ,(ly:make-moment 1 512))
 	systemStartDelimiter =#'SystemStartBar
 
 	melismaBusyProperties = #default-melisma-properties
diff --git a/scm/define-music-types.scm b/scm/define-music-types.scm
index c845f602cb..6b31bd7524 100644
--- a/scm/define-music-types.scm
+++ b/scm/define-music-types.scm
@@ -301,21 +301,11 @@ SYNTAX
 	(types . (general-music layout-instruction))
 	(iterator-ctor . ,	Push_property_iterator::constructor)
 	))
-
     (PartCombineMusic
      . (
 	(description .  "Combine two parts on a staff, either merged or
 as separate voices.")
 
-	(internal-class-name . "Simultaneous_music")
-	(types . (general-music part-combine-music))
-	(iterator-ctor . ,Part_combine_music_iterator::constructor)
-	))
-    (NewPartCombineMusic
-     . (
-	(description .  "Combine two parts on a staff, either merged or
-as separate voices.")
-
 	(internal-class-name . "Simultaneous_music")
 	(types . (general-music part-combine-music))
 	(iterator-ctor . ,New_pc_iterator::constructor)
diff --git a/scm/define-translator-properties.scm b/scm/define-translator-properties.scm
index d8e8eb70a3..617a51fa26 100644
--- a/scm/define-translator-properties.scm
+++ b/scm/define-translator-properties.scm
@@ -149,13 +149,6 @@ Use at your own risk.  This property contains the grobs for which  END-MOMENT >=
 the central C, measured in half staffspaces.  Usually determined by
 looking at clefPosition and clefGlyph.")
 
-(translator-property-description
- 'changeMoment moment-pair?
- "duration that voices are examined for differences, when
-part-combining.  Usually unset or zero when combining threads into one
-voice, and 1 (or the duration of one measure) when combining voices
-into one staff.")
-
 (translator-property-description
  'chordNameFunction procedure?
  "The function that converts lists of pitches to chord names.")
@@ -189,7 +182,6 @@ stafflines, the value 0 puts the clef on the middle staffline; a
 positive value shifts it up, a negative value shifts it down.  The
 unit of this distance is the half staff space.")
 
-(translator-property-description 'combineParts boolean? "try to combine parts?")
 (translator-property-description 'connectArpeggios boolean? " If
 set, connect all arpeggios that are found.  In this way, you can make
 arpeggios that cross staves.
@@ -209,26 +201,7 @@ This variable is typically read at Score level, so overriding
 Staff.defaultBarType will have no effect.
 
 ")
-(translator-property-description 'devNullThread symbol? "User control of Thread_devnull_engraver: one of
-@table @samp
-@item (), or unset
-Behave in normal way: remove one set of grobs when in unisolo.
-@item always:
-Remove any grob that comes along.
-@item never:
-Do nothing.
-@end table
-")
-(translator-property-description 'devNullVoice symbol? "User control of Voice_devnull_engraver: one of
-@table @samp
-@item (), or unset
-Behave in normal way: remove spanners when in unisolo.
-@item always:
-Remove any spanners that come along.
-@item never:
-Do nothing.
-@end table
-")
+
 (translator-property-description 'decrescendoSpanner symbol? "Type of spanner to be used for decrescendi.  One of: @samp{hairpin}, @samp{line}, @samp{dashed-line}, @samp{dotted-line}.  If unset, hairpin type is used.")
 
 (translator-property-description 'dynamicAbsoluteVolumeFunction procedure? "
@@ -348,7 +321,6 @@ this function is called with current context and a list of music
 objects.  The list of contains entries with start times, music objects
 and whether they're processed in this context.")
 
-(translator-property-description 'noDirection boolean? "Don't set directions by a2-engraver when part-combining.")
 (translator-property-description
  'originalCentralCPosition integer?
  "Used for temporary overriding central C in octavation brackets. ")
@@ -403,8 +375,6 @@ help with debugging large scores.")
 (translator-property-description 'soloIIText string? "text for begin of solo for voice ``two'' when part-combining.")
 (translator-property-description 'soloText string? "text for begin of solo when part-combining.")
 (translator-property-description 'sparseTies boolean? "only create one tie per chord.")
-(translator-property-description 'splitInterval number-pair? "part-combiner will separate its two voices (or threads) when interval between the two voices is contained in this range.")
-(translator-property-description 'split-interval boolean? "set if part-combiner separated voices based on splitInterval.")
 (translator-property-description 'squashedPosition integer? " Vertical position of
 squashing for Pitch_squash_engraver.")
 (translator-property-description 'stringOneTopmost boolean? "Whether the 1st string is printed on the
@@ -463,9 +433,6 @@ context Voice 	imes 2/3 @{
 @@end lilypond
 @end example
 .")
-(translator-property-description 'unirhythm boolean? "set if unirhythm is detected by the part combiner.")
-(translator-property-description 'unisilence boolean? "set if unisilence is detected by the part combiner.")
-(translator-property-description 'unison boolean? "set if unisono is detected  by the part combiner. .")
 (translator-property-description 'verticalAlignmentChildCallback
 procedure? "what callback to add to children of a vertical alignment.
 It determines what alignment procedure is used on the alignment
diff --git a/scm/part-combiner.scm b/scm/part-combiner.scm
index 5730a7e005..3d3c796cf2 100644
--- a/scm/part-combiner.scm
+++ b/scm/part-combiner.scm
@@ -257,7 +257,7 @@ Voice-state objects
 
 (define-public (make-new-part-combine-music music-list)
   (let*
-     ((m (make-music-by-name 'NewPartCombineMusic))
+     ((m (make-music-by-name 'PartCombineMusic))
       (m1 (context-spec-music (car music-list) 'Voice "one"))
       (m2 (context-spec-music (cadr music-list) 'Voice "two"))
       (props '((denies Thread)
-- 
2.39.5