Hi,

I finished the scheme part of the part-combiner.  Attached is the patch.
There are still bugs in the C part which I cannot solve:

- When there are two voices unisilence, one of them a multimeasure rest,
and the other a smaller rest, than sometimes the multimeasure rest
swallows the other, with the result that there appears a gap before the
first note.

- Sometimes slur-events are swallowed, so a slur appears to have no end.

- Multimeasure rests are not positioned according to their voice, but
always appear in the middle.

I would solve these problems myself if I could, but it would take to much
time with my limited understanding of lilypond internals.

Kristof Bastiaensen

Changelog:
 2005-07-07  Kristof Bastiaensen  <[EMAIL PROTECTED]>

        * ly/declarations-init.ly: Set the properties to record for the
        part-combiner.

        * scm/part-combiner.scm: Complete rewrite of the part-combiner.

        * lily/moment-scheme.cc: Added the scheme function ly:sub-moment
        to subtract moments.

        * lily/recording-group-engraver.cc: Added the possibility to
        record properties by putting their name in the recordProperties
        property.

? Documentation/out
? Documentation/bibliography/out
? Documentation/misc/out
? Documentation/pictures/out
? Documentation/topdocs/out
? Documentation/user/out
? buildscripts/out
? cygwin/out
? debian/out
? elisp/out
? flower/out
? flower/include/out
? input/out
? input/mutopia/out
? input/mutopia/E.Satie/out
? input/mutopia/F.Schubert/out
? input/mutopia/J.S.Bach/out
? input/mutopia/R.Schumann/out
? input/mutopia/W.A.Mozart/out
? input/no-notation/out
? input/regression/out
? input/test/out
? input/tutorial/out
? kpath-guile/out
? lily/out
? lily/include/out
? ly/out
? make/out
? mf/out
? po/out
? ps/out
? python/out
? scm/out
? scripts/out
? stepmake/out
? stepmake/bin/out
? stepmake/stepmake/out
? tex/out
? vim/out
Index: ChangeLog
===================================================================
RCS file: /cvsroot/lilypond/lilypond/ChangeLog,v
retrieving revision 1.3850
diff -u -r1.3850 ChangeLog
--- ChangeLog	1 Jul 2005 12:53:03 -0000	1.3850
+++ ChangeLog	6 Jul 2005 23:52:42 -0000
@@ -1,3 +1,17 @@
+2005-07-07  Kristof Bastiaensen  <[EMAIL PROTECTED]>
+
+	* ly/declarations-init.ly: Set the properties to record for the
+	part-combiner.
+
+	* scm/part-combiner.scm: Complete rewrite of the part-combiner.
+
+	* lily/moment-scheme.cc: Added the scheme function ly:sub-moment
+	to subtract moments.
+
+	* lily/recording-group-engraver.cc: Added the possibility to
+	record properties by putting their name in the recordProperties
+	property.
+
 2005-07-01  Mats Bengtsson  <[EMAIL PROTECTED]>
 
 	* Documentation/user/advanced-notation.itely (Setting automatic
Index: lily/moment-scheme.cc
===================================================================
RCS file: /cvsroot/lilypond/lilypond/lily/moment-scheme.cc,v
retrieving revision 1.8
diff -u -r1.8 moment-scheme.cc
--- lily/moment-scheme.cc	21 Apr 2005 14:28:31 -0000	1.8
+++ lily/moment-scheme.cc	6 Jul 2005 23:52:45 -0000
@@ -53,6 +53,17 @@
   return (*ma + *mb).smobbed_copy ();
 }
 
+LY_DEFINE (ly_sub_moment, "ly:sub-moment",
+	   2, 0, 0, (SCM a, SCM b),
+	   "Subtract two moments.")
+{
+  Moment *ma = unsmob_moment (a);
+  Moment *mb = unsmob_moment (b);
+  SCM_ASSERT_TYPE (ma, a, SCM_ARG1, __FUNCTION__, "moment");
+  SCM_ASSERT_TYPE (mb, b, SCM_ARG2, __FUNCTION__, "moment");
+  return (*ma - *mb).smobbed_copy ();
+}
+
 LY_DEFINE (ly_mul_moment, "ly:mul-moment",
 	   2, 0, 0, (SCM a, SCM b),
 	   "Multiply two moments.")
Index: lily/recording-group-engraver.cc
===================================================================
RCS file: /cvsroot/lilypond/lilypond/lily/recording-group-engraver.cc,v
retrieving revision 1.24
diff -u -r1.24 recording-group-engraver.cc
--- lily/recording-group-engraver.cc	10 Mar 2005 14:36:13 -0000	1.24
+++ lily/recording-group-engraver.cc	6 Jul 2005 23:52:45 -0000
@@ -16,12 +16,16 @@
   TRANSLATOR_DECLARATIONS (Recording_group_engraver);
   virtual bool try_music (Music *m);
   void add_music (SCM, SCM);
+  void detect_property_differences();
+  void create_property_lists (SCM);
   virtual void stop_translation_timestep ();
   virtual void finalize ();
   virtual void initialize ();
   virtual void derived_mark () const;
   SCM now_events_;
   SCM accumulator_;
+  SCM property_values_;  // a list with the current values for the properties
+  SCM property_alist_;    // an alist containing the properties and their values
 };
 
 void
@@ -30,6 +34,8 @@
   Engraver_group_engraver::derived_mark ();
   scm_gc_mark (now_events_);
   scm_gc_mark (accumulator_);
+  scm_gc_mark (property_values_);
+  scm_gc_mark (property_alist_);
 }
 
 void
@@ -42,6 +48,8 @@
 {
   accumulator_ = SCM_EOL;
   now_events_ = SCM_EOL;
+  property_alist_ = SCM_EOL;
+  property_values_ = SCM_BOOL_F;
 }
 
 void
@@ -51,15 +59,67 @@
 }
 
 void
+Recording_group_engraver::create_property_lists (SCM properties)
+{
+  property_values_ = SCM_EOL;
+  
+  for(; scm_is_pair (properties); properties = scm_cdr(properties))
+    {
+      SCM prop = scm_car (properties);
+      SCM value = internal_get_property(prop);
+
+      property_alist_ = scm_acons (prop, value, property_alist_);
+      property_values_ = scm_cons (value, property_values_);
+    }
+
+  //property_values_ must be in the same order as the properties
+  property_values_ = scm_reverse (property_values_);
+}
+
+void
+Recording_group_engraver::detect_property_differences()
+{
+  SCM property_requests = get_property("recordProperties");
+
+  // add changed properties to property_alist_
+
+  property_alist_ = SCM_EOL;
+  if( scm_is_pair (property_requests) )
+    {
+      if (SCM_BOOL_F == property_values_)
+	create_property_lists (property_requests);
+      else
+	{
+	  for(SCM value_list = property_values_;
+	      scm_is_pair (property_requests);
+	      property_requests = scm_cdr (property_requests),
+		value_list = scm_cdr (value_list))
+	    {
+	      SCM prop = scm_car (property_requests);
+	      SCM new_value = internal_get_property (prop);
+	      if (new_value != scm_car (value_list))
+		{
+		  scm_set_car_x (value_list, new_value);
+		  property_alist_ = scm_acons (prop, new_value, property_alist_);
+		}
+	    }
+	}
+    }
+}
+
+void
 Recording_group_engraver::stop_translation_timestep ()
 {
   Engraver_group_engraver::stop_translation_timestep ();
 
+  detect_property_differences();
   accumulator_ = scm_acons (scm_cons (now_mom ().smobbed_copy (),
-				      get_property ("instrumentTransposition")),
+				      property_alist_),
 			    now_events_,
 			    accumulator_);
+
   now_events_ = SCM_EOL;
+  property_alist_ = SCM_EOL;
 }
 
 void
@@ -70,6 +130,9 @@
 
   if (ly_c_procedure_p (proc))
     scm_call_2 (proc, context ()->self_scm (), scm_cdr (accumulator_));
+
+  property_values_ = SCM_BOOL_F;
+  property_alist_ = SCM_EOL;
 }
 
 bool
Index: ly/declarations-init.ly
===================================================================
RCS file: /cvsroot/lilypond/lilypond/ly/declarations-init.ly,v
retrieving revision 1.87
diff -u -r1.87 declarations-init.ly
--- ly/declarations-init.ly	28 Jun 2005 11:10:23 -0000	1.87
+++ ly/declarations-init.ly	6 Jul 2005 23:52:46 -0000
@@ -97,6 +97,7 @@
 	\consists Rest_engraver
 	\type "Recording_group_engraver"
 	recordEventSequence = #notice-the-events-for-pc
+	recordProperties = #'(timeSignatureFraction measurePosition)
     }
     \context {
 	\Score
Index: scm/part-combiner.scm
===================================================================
RCS file: /cvsroot/lilypond/lilypond/scm/part-combiner.scm,v
retrieving revision 1.43
diff -u -r1.43 part-combiner.scm
--- scm/part-combiner.scm	28 Mar 2005 19:50:16 -0000	1.43
+++ scm/part-combiner.scm	6 Jul 2005 23:52:47 -0000
@@ -4,185 +4,40 @@
 ;;;; 
 ;;;; (c) 2004--2005	Han-Wen Nienhuys <[EMAIL PROTECTED]>
 
-;; todo: figure out how to make module,
-;; without breaking nested ly scopes
+(use-modules (srfi srfi-8))
+(use-modules (srfi srfi-9))
 
-(define-class <Voice-state> ()
-  (event-list #:init-value '() #:accessor events #:init-keyword #:events)
-  (when-moment #:accessor when #:init-keyword #:when)
-  (tuning #:accessor tuning #:init-keyword #:tuning)
-  (split-index #:accessor split-index)
-  (vector-index)
-  (state-vector)
-  ;;;
-  ;; spanner-state is an alist
-  ;; of (SYMBOL . RESULT-INDEX), which indicates where
-  ;; said spanner was started.
-  (spanner-state #:init-value '() #:accessor span-state))
-  
-(define-method (write (x <Voice-state> ) file)
-  (display (when x) file)
-  (display " evs = " file)
-  (display (events x) file)
-  (display " active = " file)
-  (display (span-state x) file)
-  (display "\n" file))
-
-(define-method (note-events (vs <Voice-state>))
-  (define (f? x)
-    (equal? (ly:music-property	x 'name) 'NoteEvent))
-  (filter f? (events vs)))
-
-(define-method (previous-voice-state (vs <Voice-state>))
-  (let ((i (slot-ref vs 'vector-index))
-	(v (slot-ref vs 'state-vector)))
-    (if (< 0 i)
-	(vector-ref v (1- i))
-	#f)))
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(define-class <Split-state> ()
-  (configuration #:init-value '() #:accessor configuration)
-  (when-moment #:accessor when #:init-keyword #:when)
-  ;; voice-states are states starting with the Split-state or later
-  ;;
-  (is #:init-keyword #:voice-states #:accessor voice-states)
-  (synced  #:init-keyword #:synced #:init-value	 #f #:getter synced?))
-			     
-
-(define-method (write (x <Split-state> ) f)
-  (display (when x) f)
-  (display " = " f)
-  (display (configuration x) f)
-  (if (synced? x)
-      (display " synced "))
-  (display "\n" f))
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-
-(define (previous-span-state vs)
-  (let ((p (previous-voice-state vs)))
-    (if p (span-state p) '())))
-
-(define (make-voice-states evl)
-  (let ((vec (list->vector (map (lambda (v)
-				  (make <Voice-state>
-				    #:when (caar v)
-				    #:tuning (cdar v)
-				    #:events (map car (cdr v))))
-				evl))))
-    (do ((i 0 (1+ i)))
-	((= i (vector-length vec)) vec)
-      (slot-set! (vector-ref vec i) 'vector-index i)
-      (slot-set! (vector-ref vec i) 'state-vector vec))))
-
-(define (make-split-state vs1 vs2)
-  "Merge lists VS1 and VS2, containing Voice-state objects into vector
-of Split-state objects, crosslinking the Split-state vector and
-Voice-state objects
-"  
-  (define (helper ss-idx ss-list idx1 idx2)
-    (let* ((state1 (if (< idx1 (vector-length vs1)) (vector-ref vs1 idx1) #f))
-	   (state2 (if (< idx2 (vector-length vs2)) (vector-ref vs2 idx2) #f))
-	   (min (cond ((and state1 state2) (moment-min (when state1) (when state2)))
-		      (state1 (when state1))
-		      (state2 (when state2))
-		      (else #f)))
-	   (inc1 (if (and state1 (equal? min (when state1))) 1 0))
-	   (inc2 (if (and state2 (equal? min (when state2))) 1 0))
-	   (ss-object (if min
-			  (make <Split-state>
-			    #:when min
-			    #:voice-states (cons state1 state2)
-			    #:synced (= inc1 inc2))
-			  #f)))
-      (if state1
-	  (set! (split-index state1) ss-idx))
-      (if state2
-	  (set! (split-index state2) ss-idx))
-      (if min
-	  (helper (1+ ss-idx)
-		  (cons ss-object ss-list)
-		  (+ idx1 inc1)
-		  (+ idx2 inc2))
-	  ss-list)))
-  (list->vector (reverse! (helper 0 '() 0  0) '())))
-
-(define (analyse-spanner-states voice-state-vec)
-
-  (define (helper index active)
-    "Analyse EVS at INDEX, given state ACTIVE."
-    
-    (define (analyse-tie-start active ev)
-      (if (equal? (ly:music-property ev 'name) 'TieEvent)
-	  (acons 'tie (split-index (vector-ref voice-state-vec index))
-		 active)
-	  active))
-    
-    (define (analyse-tie-end active ev)
-      (if (equal? (ly:music-property ev 'name) 'NoteEvent)
-	  (assoc-remove! active 'tie)
-	  active))
-
-    (define (analyse-absdyn-end active ev)
-      (if (or (equal? (ly:music-property ev 'name) 'AbsoluteDynamicEvent)
-	      (and (equal? (ly:music-property ev 'name) 'CrescendoEvent)
-		   (equal? STOP (ly:music-property ev 'span-direction))))
-	  (assoc-remove! (assoc-remove! active 'cresc) 'decr)
-	  active))
-    
-    (define (active<? a b)
-      (cond ((symbol<? (car a) (car b)) #t)
-	    ((symbol<? (car b) (car b)) #f)
-	    (else (< (cdr a) (cdr b)))))
-    
-    (define (analyse-span-event active ev)
-      (let* ((name (ly:music-property ev 'name))
-	     (key (cond ((equal? name 'SlurEvent) 'slur)
-			((equal? name 'PhrasingSlurEvent) 'tie)
-			((equal? name 'BeamEvent) 'beam)
-			((equal? name 'CrescendoEvent) 'cresc)
-			((equal? name 'DecrescendoEvent) 'decr)
-			(else #f)))
-	     (sp (ly:music-property ev 'span-direction)))
-	(if (and (symbol? key) (ly:dir? sp))
-	    (if (= sp STOP)
-		(assoc-remove! active key)
-		(acons key
-		       (split-index (vector-ref voice-state-vec index))
-		       active))
-	    active)))
-
-    (define (analyse-events active evs)
-      "Run all analyzers on ACTIVE and EVS"
-      (define (run-analyzer analyzer active evs)
-	(if (pair? evs)
-	    (run-analyzer analyzer (analyzer active (car evs)) (cdr evs))
-	    active))
-      (define (run-analyzers analyzers active evs)
-	(if (pair? analyzers)
-	    (run-analyzers (cdr analyzers)
-			   (run-analyzer (car analyzers) active evs)
-			   evs)
-	    active))
-      (sort ;; todo: use fold or somesuch.
-       (run-analyzers (list analyse-absdyn-end analyse-span-event
-			    ;; note: tie-start/span comes after tie-end/absdyn.
-			    analyse-tie-end analyse-tie-start)
-		      active evs)
-       active<?))
-
-    ;; must copy, since we use assoc-remove!
-    (if (< index (vector-length voice-state-vec))
-	(begin
-	  (set! active (analyse-events active (events (vector-ref voice-state-vec index))))
-	  (set! (span-state (vector-ref voice-state-vec index))
-		(list-copy active))
-	  (helper (1+ index) active))))
-  
-  (helper 0 '()))
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;; let* with multiple values
+;;;
+;;; works like let*, but allows multiple names to take
+;;; multiple values.
+;;;
+;;; i.e.:
+;;;   (multi-let* ((a b (values 1 2)))
+;;;     (+ a b))   => 3
+;;;
+;;;
+
+(define-macro (multi-let* clauses . bodies)
+  (letrec ((split-last (lambda (head tail)
+			 (if (null? (cdr tail))
+			     (values (reverse! head) (car tail))
+			     (split-last (cons (car tail) head)
+					 (cdr tail))))))
+    (let ((clause1 (car clauses))
+	  (rest (if (null? (cdr clauses))
+		    bodies
+		    `((multi-let* ,(cdr clauses) ,@bodies)))))
+      (if (null? (cddr clause1))
+	  `(let (,clause1) ,@rest)
+	  (call-with-values
+	      (lambda () (split-last (list (car clause1)) (cdr clause1)))
+	    (lambda (names val)
+	      `(call-with-values (lambda () ,val)
+		 (lambda ,names ,@rest))))))))
+	
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
 (define noticed '())
 (define part-combine-listener '())
@@ -204,252 +59,706 @@
     (ly:run-translator m2 part-combine-listener)
     (ly:run-translator m1 part-combine-listener)
     (set! (ly:music-property m 'split-list)
-	  (determine-split-list (reverse! (cdr (assoc "one" noticed)) '())
-				(reverse! (cdr (assoc "two" noticed)) '())))
+	  (determine-split-list (process-list (reverse! (cdr (assoc "one" noticed)) '()))
+				(process-list (reverse! (cdr (assoc "two" noticed)) '()))))
     (set! noticed '())
     m))
 
+(define (process-list evl)
+  (map (lambda (ev)
+	 (cons (car ev) (map car (cdr ev))))
+       evl))
+
 (define-public (determine-split-list evl1 evl2)
   "EVL1 and EVL2 should be ascending"
-  (let* ((pc-debug #f)
-	 (chord-threshold 8)
-	 (voice-state-vec1 (make-voice-states evl1))
-	 (voice-state-vec2 (make-voice-states evl2))
-	 (result (make-split-state voice-state-vec1 voice-state-vec2)))
-    
-    (define (analyse-time-step result-idx)
-      (define (put x . index)
-	"Put the result to X, starting from INDEX backwards.
-
-Only set if not set previously.
-"
-	(let ((i (if (pair? index) (car index) result-idx)))
-	  (if (and (<= 0 i)
-		   (not (symbol? (configuration (vector-ref result i)))))
-	      (begin
-		(set! (configuration (vector-ref result i)) x)
-		(put x (1- i))))))
-      
-      (define (copy-state-from state-vec vs)
-	(define (copy-one-state key-idx)
-	  (let* ((idx (cdr key-idx))
-		 (prev-ss (vector-ref result idx))
-		 (prev (configuration prev-ss)))
-	    (if (symbol? prev)
-		(put prev))))
-	(map copy-one-state (span-state vs)))
-
-      (define (analyse-notes now-state)
-	(let* ((vs1 (car (voice-states now-state)))
-	       (vs2 (cdr (voice-states now-state)))
-	       (notes1 (note-events vs1))
-	       (durs1 (sort (map (lambda (x) (ly:music-property x 'duration))
-				 notes1)
-			    ly:duration<?))
-	       (pitches1 (sort (map (lambda (x) (ly:music-property x 'pitch))
-				    notes1)
-			       ly:pitch<?))
-	       (notes2 (note-events vs2))
-	       (durs2 (sort (map (lambda (x) (ly:music-property x 'duration))
-				 notes2)
-			    ly:duration<?))
-	       (pitches2 (sort (map (lambda (x) (ly:music-property x 'pitch))
-				    notes2)
-			       ly:pitch<?)))
-	  (cond ((> (length notes1) 1) (put 'apart))
-		((> (length notes2) 1) (put 'apart))
-		((= 1 (+ (length notes2) (length notes1))) (put 'apart))
-		((and (= (length durs1) 1)
-		      (= (length durs2) 1)
-		      (not (equal? (car durs1) (car durs2))))
-		 (put 'apart))
-		(else
-		 (if (and (= (length pitches1) (length pitches2)))
-		     (if (and (pair? pitches1)
-			      (pair? pitches2)
-			      (or
-			       (< chord-threshold (ly:pitch-steps
-						   (ly:pitch-diff (car pitches1)
-								  (car pitches2))))
-
-			       ;; voice crossings:
-			       (> 0 (ly:pitch-steps (ly:pitch-diff (car pitches1)
-								   (car pitches2))))
-			       ))
-			 (put 'apart)
-			 ;; copy previous split state from spanner state
-			 (begin
-			   (if (previous-voice-state vs1)
-			       (copy-state-from voice-state-vec1
-						(previous-voice-state vs1)))
-			   (if (previous-voice-state vs2)
-			       (copy-state-from voice-state-vec2
-						(previous-voice-state vs2)))
-			   (if (and (null? (span-state vs1)) (null? (span-state vs2)))
-			       (put 'chords)))))))))
-      
-      (if (< result-idx (vector-length result))
-	  (let* ((now-state (vector-ref result result-idx))
-		 (vs1 (car (voice-states now-state)))
-		 (vs2 (cdr (voice-states now-state))))
-	    
-	    (cond ((not vs1) (put 'apart))
-		  ((not vs2) (put 'apart))
-		  (else
-		   (let ((active1 (previous-span-state vs1))
-			 (active2 (previous-span-state vs2))
-			 (new-active1 (span-state vs1))
-			 (new-active2 (span-state vs2)))
-		     (if #f ; debug
-			 (display (list (when now-state) result-idx
-					active1 "->" new-active1
-					active2 "->" new-active2
-					"\n")))
-		     (if (and (synced? now-state)
-			      (equal? active1 active2)
-			      (equal? new-active1 new-active2))
-			 (analyse-notes now-state)
-			 
-			 ;; active states different:
-			 (put 'apart)))
-		   
-		   ;; go to the next one, if it exists.
-		   (analyse-time-step (1+ result-idx)))))))
-    
-    (define (analyse-a2 result-idx)
-      (if (< result-idx (vector-length result))
-	  (let* ((now-state (vector-ref result result-idx))
-		 (vs1 (car (voice-states now-state)))
-		 (vs2 (cdr (voice-states now-state))))
-	    (if (and (equal? (configuration now-state) 'chords)
-		     vs1 vs2)
-		(let ((notes1 (note-events vs1)) 
-		      (notes2 (note-events vs2)))
-		  (cond ((and (= 1 (length notes1))
-			      (= 1 (length notes2))
-			      (equal? (ly:music-property (car notes1) 'pitch)
-				      (ly:music-property (car notes2) 'pitch)))
-			 (set! (configuration now-state) 'unisono))
-			((and (= 0 (length notes1))
-			      (= 0 (length notes2)))
-			 (set! (configuration now-state) 'unisilence)))))
-	    (analyse-a2 (1+ result-idx)))))
+  (multi-let*
+      ((moments (merge-moments evl1 evl2))
+       (notes1 (note-lengths evl1))
+       (notes2 (note-lengths evl2))
+       (span1 (spanner-lengths evl1))
+       (span2 (spanner-lengths evl2))
+       (measurepos (measure-positions moments evl1 evl2))
+       (music-end longest (music-end+longest evl1 evl2))
+       (measurelimits (measure-limits longest))
+       (types (make-type-context-list moments notes1 notes2
+				      span1 span2 measurepos
+				      measurelimits)))
+    (fix-rests-in-measures! types #f #f)
+    (let loop ()
+      (if (fix-blocks! types #f #t music-end)
+	  (if (fix-rests-in-measures! types #f #f)
+	      loop)))
+    (synthesize-types types)))
+
+(define (event-name event)
+  (ly:music-property event 'name))
+
+(define (ly:moment<=? m1 m2)
+  (not (ly:moment<? m2 m1)))
+
+;; fold with multiple arguments
+(define (multi-fold fun inits list)
+  (if (null? list) inits
+      (multi-fold fun 
+		  (apply fun (car list) inits)
+		  (cdr list))))
+
+;; a type that represents the lengts of items (notes or spanners)
+(define make-item-length list)
+
+(define get-item first)
+(define start-moment second)
+(define end-moment third)
+
+(define mom-events:moment caar)
+(define mom-events:properties cdar)
+(define mom-events:events cdr)
+
+;; return lengths of the notes
+(define (note-lengths events-list)
+  
+  (define (handle-events evmoment collected active)
+    (let ((moment (mom-events:moment evmoment))
+	  (events (mom-events:events evmoment)))
+      (if active
+	  (if (end-note? events)
+	      (handle-events evmoment
+			     (cons (make-item-length (car active)
+						     (cdr active)
+						     moment)
+				   collected)
+			     #f)
+	      (list collected active))
+	  (let ((start (extract-note events)))
+	    (list collected
+		  (if start (cons start moment) #f))))))
+  
+  (reverse! (first (multi-fold handle-events '(() #f) events-list))))
+
+(define (extract-note event-list)
+  (let ((notes (filter (lambda (e)
+			 (equal? (event-name e) 'NoteEvent))
+		       event-list)))
+    (cond ((null? notes) #f)
+	  ((pair? (cdr notes)) 'chord)
+	  (else (car notes)))))
+
+(define (end-note? events)
+  (let loop ((events events)
+	     (note-found? #f))
+    (if (null? events)
+	note-found?
+	(case (event-name (car events))
+	  ((RestEvent MultiMeasureRestEvent) #t)
+	  ((TieEvent) #f)
+	  ((NoteEvent) (loop (cdr events) #t))
+	  (else (loop (cdr events) note-found?))))))
+
+;; return a list of lists: (spanner span-start span-end)
+(define (spanner-lengths evlist)
+
+  (define spanners '(SlurEvent PhrasingSlurEvent BeamEvent
+			       CrescendoEvent DecrescendoEvent))
+
+  (define (starting-spanners events)
+    (sort
+     (filter-map
+      (lambda (event)
+	(let ((name (event-name event)))
+	  (and (member name spanners)
+	       (equal? (ly:music-property event 'span-direction)
+		       START)
+	       name)))
+      events)
+     (lambda (s1 s2)
+       (string<? (symbol->string s1) (symbol->string s2)))))
+
+  (define (stopping-spanners events)
+    (append-map!
+     (lambda (event)
+       (let ((name (event-name event)))
+	 (cond ((and (member name spanners)
+		     (equal? (ly:music-property event 'span-direction)
+			     STOP))
+		(list name))
+	       ((member name '(CrescendoEvent DecrescendoEvent AbsoluteDynamicEvent))
+		(list 'CrescendoEvent 'DecrescendoEvent))
+	       (else '()  ))))
+     events))
+
+  ;; stop all given spanners and move them from active to collected
+  ;; this function checks each active spanner
+  (define (stop-spanners spanners moment collected active new-active)
+    (if (null? active)
+	(list collected (reverse! new-active))
+	(let ((name (caar active))
+	      (start-moment (cdar active)))
+	  (if (member name spanners)
+	      (stop-spanners spanners
+			     moment
+			     (cons (make-item-length name start-moment moment)
+				   collected)
+			     (cdr active)
+			     new-active)
+	      (stop-spanners spanners
+			     moment
+			     collected
+			     (cdr active)
+			     (cons (car active) new-active))))))
+
+  ;; add new spanners to active
+  (define (start-spanners collected spanners active moment)
+    (list collected
+	  (fold (lambda (spanner active)
+		  (alist-cons spanner moment active))
+		active spanners)))
+
+  ;; add starting spanners to active and ended spanners to spanlist
+  (define (update-spanner-list events collected active)
+    (let ((moment (mom-events:moment events))
+	  (events (mom-events:events events)))
+      (let ((stop-span (and (pair? active)
+			    (stopping-spanners events)))
+	    (start-span (starting-spanners events)))
+	(apply
+	 (lambda (collected active)
+	   (start-spanners collected start-span active moment))
+	 (if stop-span
+	     (stop-spanners stop-span moment collected active '())
+	     (list collected active))))))
+
+  (sort (first (multi-fold update-spanner-list '(() ()) evlist))
+	(lambda (s1 s2) (ly:moment<? (start-moment s1)
+				     (start-moment s2)))))
+
+(define (notes-next-moment notes moment)
+  (drop-while (lambda (n)
+		(ly:moment<=? (end-moment n) moment))
+	      notes))
+
+(define (starting-note notes moment)
+  (and (pair? notes)
+       (let ((note (car notes)))
+	 (and (equal? moment (start-moment note))
+	      (get-item note)))))
+
+(define (current-note-end notes)
+  (end-moment (car notes)))
+
+(define (notes-resting-until? notes until)
+  (or (null? notes)
+      (ly:moment<=? until (start-moment (car notes)))))
+
+(define (notes-resting-at? notes moment)
+  (or (null? notes)
+      (ly:moment<? moment (start-moment (car notes)))))
+
+(define (make-span-moment spanner)
+  (cons spanner '()))
+
+(define span-moment:after car)
+(define span-moment:active cdr)
+
+;; this function modifies the previous moment to
+;; avoid creating to many cons cells every iteration
+;; However it doesn't modify the list of spanners
+(define (spanners-next-moment! span-moment moment)
+  (multi-let*
+      ((spanners (span-moment:after span-moment))
+       (active (span-moment:active span-moment))
+       (during-moment? (lambda (s)
+			 (ly:moment<=? moment (end-moment s))))
+       (head tail (span (lambda (s)
+			  (ly:moment<=? (start-moment s) moment))
+			spanners))
+       (new-active (append! (filter! during-moment? head)
+			    (filter! during-moment? active))))
+    (cons tail new-active)))
+
+(define (spanners-equal? span1 span2 until-ev)
+  (let ((before-moment? (lambda (s)
+			  (ly:moment<? (start-moment s) until-ev))))
+    (and (equal? (span-moment:active span1) (span-moment:active span2)) ;active spanners equal?
+	 (equal? (take-while before-moment? (span-moment:after span1))
+		 (take-while before-moment? (span-moment:after span2))))))
+
+(define (spanners-resting-until? span moment)
+  (and (null? (span-moment:active span))		;no active spanners
+       (or (null? (span-moment:after span))
+	   (ly:moment<=? moment (start-moment (car (span-moment:after span)))))))
+
+(define (equal-duration? note1 note2)
+  (equal? (ly:music-property note1 'duration)
+	  (ly:music-property note2 'duration)))
+
+(define (equal-pitch? note1 note2)
+  (equal? (ly:music-property note1 'pitch)
+	  (ly:music-property note2 'pitch)))
+
+(define (within-octave? note1 note2)
+  (let* ((p1 (ly:music-property note1 'pitch))
+	 (p2 (ly:music-property note2 'pitch))
+	 (steps (ly:pitch-steps (ly:pitch-diff p1 p2))))
+    (<= 1 steps 8)))
+
+(define (type-at-moment moment notes1 notes2 span1 span2)
+  (let ((note1 (starting-note notes1 moment))
+	(note2 (starting-note notes2 moment)))
+    (cond ((or (eq? note1 'chord)
+	       (eq? note2 'chord))
+	   '(poly2 apart #t))
+	  ((and note1 note2)
+	   (cond ((not (equal-duration? note1 note2))
+		  '(poly2 apart #t))
+		 ((not (spanners-equal? span1 span2 (current-note-end notes1)))
+		  '(mono apart #t))
+		 ((equal-pitch? note1 note2)
+		  '(mono unisono #t))
+		 ((within-octave? note1 note2)
+		  '(mono chords #t))
+		 (else '(mono apart #t))))
+	  (note1 (cond ((not (notes-resting-until? notes2 (current-note-end notes1)))
+			'(poly1 apart #t))
+		       ((not (spanners-resting-until? span2 (current-note-end notes1)))
+			'(voice1 apart #t))
+		       (else '(voice1 solo1 #t))))
+	  (note2 (cond ((not (notes-resting-until? notes1 (current-note-end notes2)))
+			'(poly1 apart #t))
+		       ((not (spanners-resting-until? span1 (current-note-end notes2)))
+			'(voice2 apart #t))
+		       (else '(voice2 solo2 #t))))
+	  ((and (notes-resting-at? notes1 moment)
+		(notes-resting-at? notes2 moment))
+	   '(none unisilence #f))
+	  (else 'keep))))
+
+(define (synthesize-types types)
+  (map (lambda (type)
+	 (cons (tc-moment type)
+	       (second (tc-types type))))
+       types))
+
+;; efficient modifying unique
+(define (unique! lst)
+  (let uniquify ((lst lst))
+    (if (pair? lst)
+	(let* ((elem (car lst))
+	       (next (drop-while (lambda (e)
+				   (equal? elem e))
+				 lst)))
+	  (set-cdr! lst next)
+	  (uniquify next))))
+  lst)
+
+;; Find the next context such that moment is
+;; smaller that the next moment of context.
+(define (next-moment-fun get-moment)
+  (define (next-moment mc moment)
+    (let ((next (cdr mc)))
+      (cond ((null? next) mc)
+	    ((ly:moment<? moment (get-moment (car next))) mc)
+	    (else (next-moment next moment)))))
+  next-moment)
+
+(define event-list-next
+  (next-moment-fun caar))
+
+(define moment-context-next
+  (next-moment-fun car))
+
+(define (merge-moments evl1 evl2)
+  (unique! (merge (map caar evl1)
+		  (map caar evl2)
+		  ly:moment<?)))
+  
+(define (make-context value adv-func)
+  (cons value adv-func))
+
+(define context:value car)
+(define context:advance-func cdr)
+
+(define (make-notes-context notes)
+  (make-context notes notes-next-moment))
+
+(define (make-spanner-context spanner)
+  (make-context (make-span-moment spanner) spanners-next-moment!))
+
+(define (make-event-list-context evl)
+  (make-context evl event-list-next))
+
+(define (make-moment-context mc)
+  (make-context mc moment-context-next))
+  
+(define-record-type :type-context
+  (make-type-context types moment measurepos minim-block minim-sep)
+  type-context?
+  (types tc-types tc-set-types!)
+  (moment tc-moment)
+  (measurepos tc-measurepos)
+  (minim-block tc-minimum-block)
+  (minim-sep tc-minimum-separation))
+
+(define null-moment (ly:make-moment 0 0))
+
+;; I asume that moments at the beginning of a bar don't
+;; have a grace timing, otherwise we might have a bug
+;; here.
+(define (tc-beginning-of-bar? context)
+  (equal? (tc-measurepos context) null-moment))
+
+;; for each moment, advance the context using it's advancement
+;; function, pass the values values to fun, and collect the
+;; results.
+(define (map-with-contexts fun moments . contexts)
+  (let mwc-aux
+      ((moments moments)
+       (contexts (map context:value contexts))
+       (adv-funcs (map context:advance-func contexts))
+       (acc '()))
+    (if (null? moments)
+	(reverse! acc)
+	(let* ((moment (car moments))
+	       (contexts (map (lambda (f c) (f c moment))
+			      adv-funcs
+			      contexts))
+	       (value (apply fun moment contexts)))
+	  (mwc-aux (cdr moments) contexts adv-funcs (cons value acc))))))
+
+(define (find-last pred? lst)
+  (let aux ((lst lst) (last #f))
+    (cond ((null? lst) last)
+	  ((pred? (car lst))
+	   (aux (cdr lst) (car lst)))
+	  (else
+	   (aux (cdr lst) last)))))
+
+(define (music-end+longest evl1 evl2)
+  (let* ((find-event (lambda (evl)
+		      (find (lambda (e)
+			      (memq (event-name e)
+				    '(NoteEvent RestEvent MultiMeasureRestEvent)))
+			    (cdr evl))))
+	 (last1 (find-last find-event evl1))
+	 (last2 (find-last find-event evl2))
+	 (end1 (ly:add-moment (caar last1)
+			      (ly:music-length (find-event last1))))
+	 (end2 (ly:add-moment (caar last2)
+			      (ly:music-length (find-event last2)))))
+    (if (ly:moment<? end1 end2)
+	(values end2 evl2)
+	(values end1 evl1))))
+
+(define (measure-positions moments evl1 evl2)
+  (map-with-contexts
+   (lambda (moment events1 events2)
+     (cond ((equal? (mom-events:moment (car events1)) moment)
+	    (cons moment (cdr (assq 'measurePosition
+				    (mom-events:properties (car events1))))))
+	   ((equal? (mom-events:moment (car events2)) moment)
+	    (cons moment (cdr (assq 'measurePosition
+				    (mom-events:properties (car events2))))))
+	   (else #f))) ; shouldn't happen
+   moments
+   (make-event-list-context evl1)
+   (make-event-list-context evl2)))
+
+(define (collect-property-values evl prop)
+  (filter-map
+   (lambda (events)
+     (let ((moment (mom-events:moment events))
+	   (pair (assq prop (mom-events:properties events))))
+       (if pair (cons moment (cdr pair)) #f)))
+   evl))
     
-    (define (analyse-solo12 result-idx)
-      
-      (define (previous-config vs)
-	(let* ((pvs (previous-voice-state vs))
-	       (spi (if pvs (split-index pvs) #f))
-	       (prev-split (if spi (vector-ref result spi) #f)))
-	  (if prev-split
-	      (configuration prev-split)
-	      'apart)))
-      
-      (define (put-range x a b)
-	;; (display (list "put range "	x a b "\n"))
-	(do ((i a (1+ i)))
-	    ((> i b) b)
-	  (set! (configuration (vector-ref result i)) x)))
-      
-      (define (put x)
-	;; (display (list "putting "  x "\n"))
-	(set! (configuration (vector-ref result result-idx)) x))
-      
-      (define (current-voice-state now-state voice-num)
-	(define vs ((if (= 1 voice-num) car cdr)
-		    (voice-states now-state)))
-	(if (or (not vs) (equal? (when now-state) (when vs)))
-	    vs
-	    (previous-voice-state vs)))
-      
-      (define (try-solo type start-idx current-idx)
-	"Find a maximum stretch that can be marked as solo. Only set
-the mark when there are no spanners active."
-	(if (< current-idx (vector-length result))
-	    (let* ((now-state (vector-ref result current-idx))
-		   (solo-state (current-voice-state now-state (if (equal? type 'solo1) 1 2)))
-		   (silent-state (current-voice-state now-state (if (equal? type 'solo1) 2 1)))
-		   (silent-notes (if silent-state (note-events silent-state) '()))
-		   (solo-notes (if solo-state (note-events solo-state) '())))
-	      ;; (display (list "trying " type " at "  (when now-state) solo-state silent-state	 "\n"))
-	      (cond ((not (equal? (configuration now-state) 'apart))
-		     current-idx)
-		    ((> (length silent-notes) 0) start-idx)
-		    ((not solo-state)
-		     (put-range type start-idx current-idx)
-		     current-idx)
-		    ((and
-		      (null? (span-state solo-state)))
-		     ;;
-		     ;; This includes rests. This isn't a problem: long rests
-		     ;; will be shared with the silent voice, and be marked
-		     ;; as unisilence. Therefore, long rests won't 
-		     ;;	 accidentally be part of a solo.
-		     ;;
-		     (put-range type start-idx current-idx)
-		     (try-solo type (1+ current-idx) (1+  current-idx)))
-		    (else
-		     (try-solo type start-idx (1+ current-idx)))))
-	    ;; try-solo
-	    start-idx))
-      
-      (define (analyse-moment result-idx)
-	"Analyse 'apart starting at RESULT-IDX. Return next index. "
-	(let* ((now-state (vector-ref result result-idx))
-	       (vs1 (current-voice-state now-state 1))
-	       (vs2 (current-voice-state now-state 2))
-	       ;; (vs1 (car (voice-states now-state)))
-	       ;; (vs2 (cdr (voice-states now-state)))
-	       (notes1 (if vs1 (note-events vs1) '()))
-	       (notes2 (if vs2 (note-events vs2) '()))
-	       (n1 (length notes1))
-	       (n2 (length notes2)))
-	  ;; (display (list "analyzing step " result-idx "  moment " (when now-state) vs1 vs2  "\n"))
-	  (max
-	   ;; we should always increase.
-	   (cond ((and (= n1 0) (= n2 0))
-		  (put 'apart-silence)
-		  (1+ result-idx))
-		 ((and (= n2 0)
-		       (equal? (when vs1) (when now-state))
-		       (null? (previous-span-state vs1)))
-		  (try-solo 'solo1 result-idx result-idx))
-		 ((and (= n1 0)
-		       (equal? (when vs2) (when now-state))
-		       (null? (previous-span-state vs2)))
-		  (try-solo 'solo2 result-idx result-idx))
-		 
-		 (else (1+ result-idx)))
-	   ;; analyse-moment
-	   (1+ result-idx))))
+(define (measure-limits evl)
+  (let ((fractions (collect-property-values evl 'timeSignatureFraction)))
+    (map (lambda (prop)
+	   (let ((moment (car prop))
+		 (minim-block (calc-block-length (cdr prop)))
+		 (minim-sep (calc-separation (cdr prop))))
+	     (list moment minim-block minim-sep)))
+	 fractions)))
+
+(define (calc-block-length fraction)
+  (let ((bars (if (< (car fraction) 4)
+		  3 2)))
+    (ly:make-moment (* (car fraction) bars)
+		    (cdr fraction))))
       
-      (if (< result-idx (vector-length result))
-	  (if (equal? (configuration (vector-ref result result-idx)) 'apart)
-	      (analyse-solo12 (analyse-moment result-idx))
-	      (analyse-solo12 (1+ result-idx))))) ; analyse-solo12
-
-    (analyse-spanner-states voice-state-vec1)
-    (analyse-spanner-states voice-state-vec2)
-    (if #f
-	(begin
-	  (display voice-state-vec1)
-	  (display "***\n")
-	  (display voice-state-vec2)
-	  (display "***\n")
-	  (display result)
-	  (display "***\n")))
-    (analyse-time-step 0)
-    ;; (display result)
-    (analyse-a2 0)
-    ;;(display result)
-    (analyse-solo12 0)
-    ;; (display result)
-    (set! result (map
-		  (lambda (x) (cons (when x) (configuration x)))
-		  (vector->list result)))
-    (if #f ;; pc-debug
-	 (display result))
-    result))
+(define (calc-separation fraction)
+  (let ((bars (if (< (car fraction) 4)
+		  2 1)))
+    (ly:make-moment (* (car fraction) bars)
+		 (cdr fraction))))
+
+(define (make-type-context-list moments notes1
+				notes2 span1 span2
+				measurepos measurelimits)
+  (let ((type-context-list
+	 (map-with-contexts
+	  (lambda (moment notes1 notes2 span1 span2 measurepos measurelimits)
+	    (let ((type (type-at-moment moment notes1 notes2
+					span1 span2)))
+	      (make-type-context type
+				 moment
+				 (cdar measurepos) ; extract value from alist
+				 (second (car measurelimits))
+				 (third (car measurelimits)))))
+	  moments
+	  (make-notes-context notes1)
+	  (make-notes-context notes2)
+	  (make-spanner-context span1)
+	  (make-spanner-context span2)
+	  (make-moment-context measurepos)
+	  (make-moment-context measurelimits))))
+    (follow-kept-types! type-context-list #f)
+    type-context-list))
+
+(define (follow-kept-types! context prev)
+  (if (pair? context)
+      (let ((types (tc-types (car context))))
+	(if (eq? types 'keep)
+	    (let ((new-value (list (first prev) (second prev) #f)))
+	      (tc-set-types! (car context) new-value)
+	      (follow-kept-types! (cdr context) new-value))
+	    (follow-kept-types! (cdr context) types)))))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;
+;; The following rules are used for each bar to ensure that the voices
+;; and rests in the bar flow naturally, for example rests will not
+;; disappear into nothing:
+;;
+;;  - Make a new solovoice
+;;
+;;    * only when preceded by
+;;      - monophonic music
+;;      - merged rest (unisilence)
+;;
+;;    * never when followed by two printed voices with at least one
+;;      resting
+;;
+;;  - Make a merged rest
+;;
+;;    * only when preceded by
+;;      - music without printed rest
+;;      - merged rest
+;;
+;;    * never when followed by two printed voices with at least one
+;;      resting
+;;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(define (two-voices-and-rest? context prev)
+  (cond ((null? context)                      #f)
+	((tc-beginning-of-bar? (car context)) #f) ; next bar
+	(else
+	 (let* ((types (tc-types (car context)))
+		(type1 (first types)))
+	   (cond ((memq type1 '(poly2 mono))           #f)
+		 ((eq? type1 'poly1)                   #t)
+		 ;; type1 == voice1, voice2, or none
+		 ((eq? (second types) 'apart)          #t)
+		 ((and (eq? type1 'voice1)
+		       (eq? (first prev) 'voice2))     #t)
+		 ((and (eq? type1 'voice2)
+		       (eq? (first prev) 'voice1))     #t)
+		 (else (two-voices-and-rest? (cdr context) types)))))))
+
+(define (fix-rests-in-measures! context modified? prev)
+  (if (null? context) modified?
+      (let* ((types (tc-types (car context)))
+	     (type1 (first types))
+	     (fix-next (lambda ()
+			 (fix-rests-in-measures! (cdr context) modified? types)))
+	     (modify-fix-next (lambda ()
+				(let ((new-value `(,type1 apart ,(third types))))
+				  (tc-set-types! (car context) new-value)
+				  (fix-rests-in-measures! (cdr context) #t new-value)))))
+	(cond ((eq? (second types) 'apart)
+	       (fix-next))
+	      ((and (memq type1 '(voice1 voice2))
+		    (put-voice-apart? types context prev))
+	       (modify-fix-next))
+	      ((and (eq? type1 'none)
+		    (put-rests-apart? types context prev))
+	       (modify-fix-next))
+	      (else (fix-next))))))
+
+;; Check if the voice/rests can be merged.
+;; These functions are difficult to understand.
+;; * If we are at the beginning of a bar:
+;;   ->  search forward to see if it is followed by two printed
+;;       voices with at least one rest.
+;; * If we have a monophonic voice preceding we have to check
+;;   again the following notes.
+;; * Otherwise we can take the previous value, since for a merged
+;;   solo or rests the next notes have been checked already, and
+;;   for polyphonic music the notes will never be merged
+;; * merging rests is also allowed after polyphonic music without
+;; rests (poly2)
+
+(define (put-voice-apart? types context prev)
+  (if (or (tc-beginning-of-bar? (car context))
+	  (eq? (first prev) 'mono))
+      (two-voices-and-rest? (cdr context) types)
+      (eq? (second prev) 'apart)))
+
+(define (put-rests-apart? types context prev)
+  (if (or (tc-beginning-of-bar? (car context))
+	  (memq (first prev) '(mono poly2)))
+      (two-voices-and-rest? (cdr context) types)
+      (eq? (second prev) 'apart)))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;
+;; The following rules are used to make sure that a solo or a due part
+;; is only used for continguous blocks, to prevent to many changes in
+;; the score.
+;;
+;; - all notes in a block should be part of the same kind (solo, a
+;; due, chords).
+;;
+;; - a rest is part of the block only if the part of the block that
+;; follows the rest is at least as long as the rest.
+;;
+;; - if a rest is larger than "minimum-separation" it will not be part
+;; of the block
+;;
+;; - for a block to be put into a kind (solo, ...), it has to have at
+;; least three notes, and must have a minimum length of
+;; "minimum-block".
+;;
+;; - an isolated block, preceded and followed by a large rest can have
+;; any size.
+;;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;; puts voices apart for blocks that aren't big enough.  The
+;; separate? argument indicates if the block should be considered
+;; separate from other blocks.
+(define (fix-blocks! context modified? separate? music-end)
+  (if (null? context)
+      modified?
+      (let* ((types (tc-types (car context))))
+	(cond
+	 ((eq? (first types) 'none)  ; skip rests and check if the
+	  (receive (separate? next)  ; next block will be separate
+		   (skip-to-next context music-end)
+	    (fix-blocks! next modified? separate? music-end)))
+	 ((eq? (second types) 'apart) ; skip 'apart
+	  (fix-blocks! (cdr context) modified? #f music-end))
+	 (else
+	  ;; type1  == voice1, voice2, mono
+	  (receive (modified2? next-context)
+		   (fix-following-block! context (second types)
+					 separate? music-end)
+	    (fix-blocks! next-context
+			 (or modified? modified2?)
+			 #f music-end)))))))
+
+(define (fix-following-block! context type separate? music-end)
+  (multi-let*
+      ((after-context num-notes (take-block context type 0 music-end))
+       (block-length (duration-between context after-context music-end))
+       (separate-after? next-context (skip-to-next after-context music-end))
+       (modify? (and (not (and separate-after? separate?)) ; not isolated
+		     (or (< num-notes 3)
+			 (ly:moment<? block-length
+				      (tc-minimum-block (car context)))))))
+    (if modify?
+	(put-block-apart! context after-context)
+	(adjust-rest-types! context after-context type))
+    (values modify? after-context)))
+
+(define (put-block-apart! context after-context)
+  (if (not (eq? context after-context))
+      (let ((types (tc-types (car context))))
+	(if (not (eq? (first types) 'none)) ; don't put rests apart here!
+	    (tc-set-types! (car context)
+			   (list (first types) 'apart (third types))))
+	(put-block-apart! (cdr context) after-context))))
+
+(define (adjust-rest-types! context after-context type)
+  (if (not (eq? context after-context))
+      (let ((types (tc-types (car context))))
+	(if (eq? (first types) 'none)
+	    (tc-set-types! (car context)
+			   (list (first types) type (third types))))
+	(adjust-rest-types! (cdr context) after-context type))))
+
+(define (take-block context block-type numnotes music-end)
+  (let ((types (tc-types (car context))))
+    (cond ((eq? (first types) 'none)
+	   (receive (succeed? next-context numnotes)
+		    (try-after-rest context block-type numnotes music-end)
+	     (if succeed?
+		 (take-block next-context block-type numnotes music-end)
+		 (values context numnotes))))
+	  ((eq? (second types) block-type)
+	   (take-block (cdr context) block-type
+		       (if (third types) (+ numnotes 1) numnotes)
+		       music-end))
+	  (else
+	   (values context numnotes)))))
+
+(define (duration-between context1 context2 music-end)
+  (let ((get-moment (lambda (context)
+		      (if (null? context) music-end
+			  (tc-moment (car context))))))
+    (ly:sub-moment (get-moment context2)
+		   (get-moment context1))))
+
+(define (skip-to-next context music-end)
+  (receive (length next) (skip-rest context music-end)
+    (values (ly:moment<=? (tc-minimum-separation (car context)) length)
+	    next)))
+
+(define (skip-rest context music-end)
+  (let* ((next (drop-while (lambda (c)
+			     (eq? (first (tc-types c)) 'none))
+			   context))
+	 (length (duration-between context next music-end)))
+    (values length next)))
+
+(define (try-after-rest context block-type numnotes music-end)
+  (receive (rest-length block-begin)
+	   (skip-rest context music-end)
+    (if (ly:moment<=? (tc-minimum-separation (car context)) rest-length)
+	(values #f context numnotes)
+	(receive (succeed? next-block numnotes2)
+		 (take-minimum-block rest-length block-begin
+				     block-type numnotes music-end)
+	  (if succeed?
+	      (values #t next-block numnotes2)
+	      (values #f context numnotes))))))
+
+;; Takes the smallest block that is at least min-length long.
+;; If there is no such block return the start-position
+(define (take-minimum-block min-length start block-type numnotes music-end)
+  (let take-one ((context start)
+		 (numnotes numnotes))
+    (let ((types (tc-types (car context)))
+	  (length (duration-between start context music-end)))
+      (cond ((ly:moment<=? min-length length)   ; block is large enough
+	     (values #t context numnotes))
+	    ((eq? (second types) block-type)    ; same type: continue
+	     (take-one (cdr context)
+		       (if (third types) (+ numnotes 1) numnotes)))
+	    ((eq? (first types) 'none)          ; rest
+	     (receive (succeed? block-end numnotes2)
+		      (try-after-rest context block-type music-end)
+	       (if succeed?
+		   (take-one block-end numnotes2) ;continue
+		   ;; otherwise succeed if the distance to the next
+		   ;; notes is least min-length
+		   (multi-let* ((l next-block (skip-rest block-end))
+				(length (duration-between context block-end music-end)))
+		     (if (ly:moment<? length min-length)
+			 (values #f context numnotes)
+			 (values #t block-end numnotes2))))))
+	    (else                               ; end of block before min-length
+	     (values #f numnotes start))))))
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;; autochange - fairly related to part combining.
_______________________________________________
lilypond-devel mailing list
lilypond-devel@gnu.org
http://lists.gnu.org/mailman/listinfo/lilypond-devel

Reply via email to