]> git.donarmstrong.com Git - lilypond.git/commitdiff
MIDI: handle overlapping notes; issue 1647
authorMichael Welsh Duggan <md5i@cs.cmu.edu>
Wed, 11 May 2011 03:39:04 +0000 (20:39 -0700)
committerKeith OHara <k-ohara5a5a@oco.net>
Sun, 29 May 2011 20:08:40 +0000 (13:08 -0700)
When notes overlap on the same pitch on the same MIDI channel,
re-arrange the note-off events for MIDI players that cannot handle
overlap, but if midiMergeUnisons = #t, merge such notes.

input/regression/midi-unisons.ly [new file with mode: 0644]
lily/audio-staff.cc
lily/include/audio-staff.hh
lily/include/midi-walker.hh
lily/midi-walker.cc
lily/staff-performer.cc
scm/define-context-properties.scm

diff --git a/input/regression/midi-unisons.ly b/input/regression/midi-unisons.ly
new file mode 100644 (file)
index 0000000..8e3048d
--- /dev/null
@@ -0,0 +1,20 @@
+\header {
+
+  texidoc = "In overlapping unisons, within a single MIDI channel,
+either the first note is truncated, or the notes are merged if
+@code{midiMergeUnisons} is @code{#t}. Run
+@code{timidity -idvvv file.midi |grep Midi} to see midi events."
+
+}
+
+\version "2.15.0"
+
+\score {
+  {
+    \set Score.midiChannelMapping = #'staff
+    \new Staff  << {r8 g'4.} \\ {g'4. r8} >>
+    \new Staff \with { midiMergeUnisons = ##t } << {r8 a'4.} \\ {a'4. r8} >>
+  }
+  \midi {}
+  \layout {}
+}
index d40220ca1c13a2e5237e7d65195295a0362dce63..ddab7e04bc11d2fa9e6b0c7a5a1a740631c1eecd 100644 (file)
@@ -30,7 +30,7 @@ Audio_staff::add_audio_item (Audio_item *ai)
 }
 
 Audio_staff::Audio_staff ()
-  : percussion_ (false)
+  : percussion_ (false), merge_unisons_ (false)
 {
 }
 
index acf31b17a4808d0dbfa5ae921e12508ea0353ddb..665593edd67e090d91de486b0dd93bc1014a621f 100644 (file)
@@ -32,6 +32,7 @@ struct Audio_staff : public Audio_element
   Audio_staff ();
   
   bool percussion_;
+  bool merge_unisons_;
   vector<Audio_item*> audio_items_;
 };
 
index 4fd1ae39fc643a6da565dc9b24aef02039209fdb..3d99b34021659bacee447eed8af898d7c30d265e 100644 (file)
@@ -53,8 +53,8 @@ private:
   void output_event (int, Midi_item *l);
   Midi_item *get_midi (Audio_item*); 
   Midi_track *track_;
-  Audio_staff *staff_;
   bool percussion_;
+  bool merge_unisons_;
   vsize index_;
   vector<Audio_item*> items_;
   PQueue<Midi_note_event> stop_note_queue;
index fdafd91b1d3a6252a13dcc0b6d3644a00ba94688..6fbb780a1ba4c05b98ac9af1c2d40ffa825e6835 100644 (file)
@@ -60,6 +60,7 @@ Midi_walker::Midi_walker (Audio_staff *audio_staff, Midi_track *track)
   vector_sort (items_, audio_item_less);
   last_tick_ = 0;
   percussion_ = audio_staff->percussion_;
+  merge_unisons_ = audio_staff->merge_unisons_;
 }
 
 Midi_walker::~Midi_walker ()
@@ -81,31 +82,43 @@ 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 ();
-
-  bool play_start = true;
+  int now_ticks = ptr->audio_column_->ticks ();
+  int stop_ticks = int (moment_to_real (note->audio_->length_mom_) *
+                        Real (384 * 4)) + now_ticks;
   for (vsize i = 0; i < stop_note_queue.size (); i++)
     {
-      /* if this pith already in queue */
+      /* if this pitch already in queue */
       if (stop_note_queue[i].val->get_semitone_pitch ()
          == note->get_semitone_pitch ())
        {
-         if (stop_note_queue[i].key < stop_ticks)
+         int queued_ticks
+           = stop_note_queue[i].val->audio_->audio_column_->ticks ();
+         // If the two notes started at the same time, or option is set,
+         if (now_ticks == queued_ticks || merge_unisons_)
            {
-             /* let stopnote in queue be ignored,
-                new stop note wins */
-             stop_note_queue[i].ignore_ = true;
-
-             /* don't replay start note, */
-             play_start = false;
+             // 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[i].ignore_ = true;
+                 stop_note_queue.insert (e);
+               }
+             note = 0;
              break;
            }
          else
            {
-             /* skip this stopnote,
-                don't play the start note */
-             note = 0;
+             // 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);
+             stop_note_queue[i].ignore_ = true;
              break;
            }
        }
@@ -117,11 +130,10 @@ Midi_walker::do_start_note (Midi_note *note)
       e.val = new Midi_note_off (note);
 
       midi_events_.push_back (e.val);
-      e.key = int (stop_ticks);
+      e.key = stop_ticks;
       stop_note_queue.insert (e);
 
-      if (play_start)
-       output_event (ptr->audio_column_->ticks (), note);
+      output_event (now_ticks, note);
     }
 }
 
index 502c289e0c9d2ccd943955e9bc7629d5a468ab27..bab8ae025748282b47bb2f49b4035d737f2baedf 100644 (file)
@@ -105,6 +105,8 @@ Audio_staff*
 Staff_performer::new_audio_staff (string voice)
 {
   Audio_staff* audio_staff = new Audio_staff;
+  audio_staff->merge_unisons_
+    = to_boolean (get_property ("midiMergeUnisons"));
   string track_name = context ()->id_string () + ":" + voice;
   if (track_name != ":")
     {
index e9484ba19485df2358101d3ec824d3f5eff530c0..dab5211f14372444171626f23fb0ef3120175ee8 100644 (file)
@@ -358,6 +358,8 @@ is used for ottava brackets.")
 half staff-spaces.  Usually determined by looking at
 @code{middleCClefPosition} and @code{middleCOffset}.")
      (midiInstrument ,string? "Name of the MIDI instrument to use.")
+     (midiMergeUnisons ,boolean? "If true, output only one MIDI note-on
+event when notes with the same pitch, in the same MIDI-file track, overlap.")
      (midiMaximumVolume ,number? "Analogous to
 @code{midiMinimumVolume}.")
      (midiMinimumVolume ,number? "Set the minimum loudness for MIDI.