Hinrik Ă–rnSigurĂ°sson <hinrik....@gmail.com> writes:

> When I have overlapping notes in a song, they will both be heard in
> the midi output if they are on separate staffs (see the first measure
> in my code example). But if they occur on the same staff, the latter
> one is not heard (see the second measure).
>
> I'm using timidity+pmidi on Linux. Is this a bug in lilypond's midi
> output or is there something wrong with the midi player I'm using?
> Timidity's --overlap-voice option has no effect on this issue.

It is a feature in Lilypond's midi output.  Discussion about this can be
found at
<URL:http://lists.gnu.org/archive/html/lilypond-user/2004-04/msg00233.html>.

I have a patch which changes this behavior, but I was supposed to allow
it to be an optional feature before resubmitting the patch.  I couldn't
figure a way to do that at the time, and eventually forgot about it as
life got more hectic.  I still have this code, and have kept it
up-to-date with respect to the current development trunk, but it is
still a non-optional feature in this patch; it changes the default
behavior.

If someone wants to help me figure out the right way to make this
feature optional, I'd be willing to work on this again.  Patch included
herein:

diff --git a/flower/include/pqueue.hh b/flower/include/pqueue.hh
index 981a37e..4c7c259 100644
--- a/flower/include/pqueue.hh
+++ b/flower/include/pqueue.hh
@@ -107,7 +107,44 @@ public:
     T max_t;
     return max_t;
   }
-  void delmin ()
+  void del (vsize i) 
+  {
+    assert (i < size ());
+    T last = heap_array_.back ();
+    heap_array_.pop_back ();
+
+    if (i == size ())
+      return;
+     
+    vsize tgt = ++i;
+
+    vsize next = i / 2;
+    while (next && compare (elt (next), last) > 0) 
+      {
+        elt (tgt) = elt (next);
+        tgt = next;
+        next = tgt / 2;
+      }
+
+    if (tgt == i) 
+      {
+        next = tgt * 2;
+        while (next <= size ()) 
+          {
+            if (((next + 1) <= size ()) &&
+                (compare (elt (next + 1), elt (next)) < 0))
+              next++;
+            if (compare (last, elt (next)) < 0)
+              break;
+            elt (tgt) = elt (next);
+            tgt = next;
+            next = tgt * 2;
+          }
+      }
+    elt (tgt) = last;
+    OK ();
+  }
+  void delmin () 
   {
     assert (size ());
     T last = heap_array_.back ();
diff --git a/lily/include/midi-walker.hh b/lily/include/midi-walker.hh
index 4fd1ae3..3f6c410 100644
--- a/lily/include/midi-walker.hh
+++ b/lily/include/midi-walker.hh
@@ -25,7 +25,7 @@
 #include "lily-proto.hh"
 #include "moment.hh"
 
-struct Midi_note_event : PQueue_ent<int, Midi_note *>
+struct Midi_note_event : PQueue_ent<int, std::pair<int, Midi_note *> >
 {
   bool ignore_;
   Midi_note_event ();
diff --git a/lily/lily-guile.cc b/lily/lily-guile.cc
index 1763a5c..6b5a018 100644
--- a/lily/midi-walker.cc
+++ b/lily/midi-walker.cc
@@ -81,47 +81,56 @@ Midi_walker::do_start_note (Midi_note *note)
 {
   Audio_item *ptr = items_[index_];
   assert (note->audio_ == ptr);
-  int stop_ticks = int (moment_to_real (note->audio_->length_mom_) * Real (384 * 4))
-    + ptr->audio_column_->ticks ();
+  int now_ticks = ptr->audio_column_->ticks ();
+  int stop_ticks = int (moment_to_real (note->audio_->length_mom_) * 
+                        Real (384 * 4)) + now_ticks;
 
-  bool play_start = true;
   for (vsize i = 0; i < stop_note_queue.size (); i++)
     {
-      /* if this pith already in queue */
-      if (stop_note_queue[i].val->get_semitone_pitch ()
+      /* if this pitch already in queue */
+      if (stop_note_queue[i].val.second->get_semitone_pitch ()
 	  == note->get_semitone_pitch ())
 	{
-	  if (stop_note_queue[i].key < stop_ticks)
-	    {
-	      /* let stopnote in queue be ignored,
-		 new stop note wins */
-	      stop_note_queue[i].ignore_ = true;
-
-	      /* don't replay start note, */
-	      play_start = false;
-	      break;
-	    }
-	  else
-	    {
-	      /* skip this stopnote,
-		 don't play the start note */
-	      note = 0;
-	      break;
-	    }
+          if (now_ticks == stop_note_queue[i].val.first)
+            {
+              // The two notes started at the same time.  Merge them.
+              if (stop_note_queue[i].key < stop_ticks)
+                {
+                  Midi_note_event e;
+                  e.val = stop_note_queue[i].val;
+                  e.key = stop_ticks;
+                  stop_note_queue.del (i);
+                  stop_note_queue.insert (e);
+                }
+              note = 0;
+              break;
+            }
+	  else 
+            {
+              // A note was played that interruped a played note.
+              // Stop the old note, and continue to the greatest moment
+              // between the two.
+              if (stop_note_queue[i].key > stop_ticks)
+                {
+                  stop_ticks = stop_note_queue[i].key;
+                }
+              output_event (now_ticks, stop_note_queue[i].val.second);
+              stop_note_queue.del(i);
+              break;
+            }
 	}
     }
 
   if (note)
     {
       Midi_note_event e;
-      e.val = new Midi_note_off (note);
+      e.val = std::make_pair(now_ticks, new Midi_note_off (note));
 
-      midi_events_.push_back (e.val);
-      e.key = int (stop_ticks);
+      midi_events_.push_back (e.val.second);
+      e.key = stop_ticks;
       stop_note_queue.insert (e);
 
-      if (play_start)
-	output_event (ptr->audio_column_->ticks (), note);
+      output_event (ptr->audio_column_->ticks (), note);
     }
 }
 
@@ -137,7 +146,7 @@ Midi_walker::do_stop_notes (int max_ticks)
 	}
 
       int stop_ticks = e.key;
-      Midi_note *note = e.val;
+      Midi_note *note = e.val.second;
 
       output_event (stop_ticks, note);
     }
-- 
Michael Welsh Duggan
(m...@md5i.com)
_______________________________________________
lilypond-devel mailing list
lilypond-devel@gnu.org
http://lists.gnu.org/mailman/listinfo/lilypond-devel

Reply via email to