foxygit / doom Log in
commit d8ada5c41da3eec5ef163d9efac9cb5dbdc2cb6b
Author:     Simon Howard <fraggle@gmail.com>
AuthorDate: Wed Apr 15 19:00:25 2009 +0000
Commit:     Simon Howard <fraggle@gmail.com>
CommitDate: Wed Apr 15 19:00:25 2009 +0000

    Parse MIDI events that reuse the event type from the previous event.

    Subversion-branch: /branches/opl-branch
    Subversion-revision: 1499
---
 src/midifile.c | 70 +++++++++++++++++++++++++++++++++++++++++-----------------
 src/midifile.h | 14 ++++++------
 2 files changed, 57 insertions(+), 27 deletions(-)

diff --git a/src/midifile.c b/src/midifile.c
index cca0189e..1be6ec75 100644
--- a/src/midifile.c
+++ b/src/midifile.c
@@ -198,8 +198,8 @@ static boolean ReadChannelEvent(midi_event_t *event,

     // Set basics:

-    event->event_type = event_type >> 4;
-    event->data.channel.channel = event_type & 0xf;
+    event->event_type = event_type & 0xf0;
+    event->data.channel.channel = event_type & 0x0f;

     // Read parameters:

@@ -296,7 +296,8 @@ static boolean ReadMetaEvent(midi_event_t *event, FILE *stream)
     return true;
 }

-static boolean ReadEvent(midi_event_t *event, FILE *stream)
+static boolean ReadEvent(midi_event_t *event, unsigned int *last_event_type,
+                         FILE *stream)
 {
     byte event_type;

@@ -312,9 +313,29 @@ static boolean ReadEvent(midi_event_t *event, FILE *stream)
         return false;
     }

+    // All event types have their top bit set.  Therefore, if
+    // the top bit is not set, it is because we are using the "same
+    // as previous event type" shortcut to save a byte.  Skip back
+    // a byte so that we read this byte again.
+
+    if ((event_type & 0x80) == 0)
+    {
+        event_type = *last_event_type;
+
+        if (fseek(stream, -1, SEEK_CUR) < 0)
+        {
+            fprintf(stderr, "ReadEvent: Unable to seek in stream\n");
+            return false;
+        }
+    }
+    else
+    {
+        *last_event_type = event_type;
+    }
+
     // Check event type:

-    switch (event_type >> 4)
+    switch (event_type & 0xf0)
     {
         // Two parameter channel events:

@@ -331,26 +352,27 @@ static boolean ReadEvent(midi_event_t *event, FILE *stream)
         case MIDI_EVENT_CHAN_AFTERTOUCH:
             return ReadChannelEvent(event, event_type, false, stream);

-        // Other event types:
+        default:
+            break;
+    }
+
+    // Specific value?

-        case 0xf:
-            if (event_type == MIDI_EVENT_SYSEX
-             || event_type == MIDI_EVENT_SYSEX_SPLIT)
-            {
-                return ReadSysExEvent(event, event_type, stream);
-            }
-            else if (event_type == MIDI_EVENT_META)
-            {
-                return ReadMetaEvent(event, stream);
-            }
+    switch (event_type)
+    {
+        case MIDI_EVENT_SYSEX:
+        case MIDI_EVENT_SYSEX_SPLIT:
+            return ReadSysExEvent(event, event_type, stream);

-        // --- Fall-through deliberate ---
-        // Other 0xfx event types are unknown
+        case MIDI_EVENT_META:
+            return ReadMetaEvent(event, stream);

         default:
-            fprintf(stderr, "Unknown MIDI event type: 0x%x\n", event_type);
-            return false;
+            break;
     }
+
+    fprintf(stderr, "ReadEvent: Unknown MIDI event type: 0x%x\n", event_type);
+    return false;
 }

 // Free an event:
@@ -405,6 +427,7 @@ static boolean ReadTrack(midi_track_t *track, FILE *stream)
 {
     midi_event_t *new_events;
     midi_event_t *event;
+    unsigned int last_event_type;

     track->num_events = 0;
     track->events = NULL;
@@ -418,6 +441,8 @@ static boolean ReadTrack(midi_track_t *track, FILE *stream)

     // Then the events:

+    last_event_type = 0;
+
     for (;;)
     {
         // Resize the track slightly larger to hold another event:
@@ -435,7 +460,7 @@ static boolean ReadTrack(midi_track_t *track, FILE *stream)
         // Read the next event:

         event = &track->events[track->num_events];
-        if (!ReadEvent(event, stream))
+        if (!ReadEvent(event, &last_event_type, stream))
         {
             return false;
         }
@@ -642,6 +667,11 @@ void PrintTrack(midi_track_t *track)
     {
         event = &track->events[i];

+        if (event->delta_time > 0)
+        {
+            printf("Delay: %i ticks\n", event->delta_time);
+        }
+
         printf("Event type: %s (%i)\n",
                MIDI_EventTypeToString(event->event_type),
                event->event_type);
diff --git a/src/midifile.h b/src/midifile.h
index 16f911e7..490b0171 100644
--- a/src/midifile.h
+++ b/src/midifile.h
@@ -30,13 +30,13 @@ typedef struct midi_file_s midi_file_t;

 typedef enum
 {
-    MIDI_EVENT_NOTE_OFF        = 0x8,
-    MIDI_EVENT_NOTE_ON         = 0x9,
-    MIDI_EVENT_AFTERTOUCH      = 0xa,
-    MIDI_EVENT_CONTROLLER      = 0xb,
-    MIDI_EVENT_PROGRAM_CHANGE  = 0xc,
-    MIDI_EVENT_CHAN_AFTERTOUCH = 0xd,
-    MIDI_EVENT_PITCH_BEND      = 0xe,
+    MIDI_EVENT_NOTE_OFF        = 0x80,
+    MIDI_EVENT_NOTE_ON         = 0x90,
+    MIDI_EVENT_AFTERTOUCH      = 0xa0,
+    MIDI_EVENT_CONTROLLER      = 0xb0,
+    MIDI_EVENT_PROGRAM_CHANGE  = 0xc0,
+    MIDI_EVENT_CHAN_AFTERTOUCH = 0xd0,
+    MIDI_EVENT_PITCH_BEND      = 0xe0,

     MIDI_EVENT_SYSEX           = 0xf0,
     MIDI_EVENT_SYSEX_SPLIT     = 0xf7,