shithub: choc

Download patch

ref: 2b64dfc84981dbbd9de1a0d9cb11995bf0ba9379
parent: b96cdbe82fa6cb51ac91072ad86e084d739fd9be
author: Simon Howard <fraggle@gmail.com>
date: Sun Mar 29 18:39:25 EDT 2009

Parse SysEx, meta events

Subversion-branch: /branches/opl-branch
Subversion-revision: 1490

--- a/src/midifile.c
+++ b/src/midifile.c
@@ -53,6 +53,10 @@
     FILE *stream;
     midi_header_t header;
     unsigned int data_len;
+
+    // Data buffer used to store data read for SysEx or meta events:
+    byte *buffer;
+    unsigned int buffer_size;
 };
 
 static boolean CheckChunkHeader(chunk_header_t *chunk,
@@ -137,6 +141,9 @@
         return NULL;
     }
 
+    file->buffer = NULL;
+    file->buffer_size = 0;
+
     // Open file
 
     file->stream = fopen(filename, "rb");
@@ -172,9 +179,116 @@
 void MIDI_CloseFile(midi_file_t *file)
 {
     fclose(file->stream);
+    free(file->buffer);
     free(file);
 }
 
+// Read a single byte.  Returns false on error.
+
+static boolean ReadByte(midi_file_t *file, byte *result)
+{
+    int c;
+
+    c = fgetc(file->stream);
+
+    if (c == EOF)
+    {
+        return false;
+    }
+    else
+    {
+        *result = (byte) c;
+
+        return true;
+    }
+}
+
+// Read a variable-length value.
+
+static boolean ReadVariableLength(midi_file_t *file, unsigned int *result)
+{
+    int i;
+    byte b;
+
+    *result = 0;
+
+    for (i=0; i<4; ++i)
+    {
+        if (!ReadByte(file, &b))
+        {
+            fprintf(stderr, "Error while reading variable-length value\n");
+            return false;
+        }
+
+        // Insert the bottom seven bits from this byte.
+
+        *result <<= 7;
+        *result |= b & 0x7f;
+
+        // If the top bit is not set, this is the end.
+
+        if ((b & 0x80) == 0)
+        {
+            return true;
+        }
+    }
+
+    fprintf(stderr, "Variable-length value too long: maximum of four bytes!\n");;
+    return false;
+}
+
+// Expand the size of the buffer used for SysEx/Meta events:
+
+static boolean ExpandBuffer(midi_file_t *file, unsigned int new_size)
+{
+    byte *new_buffer;
+
+    if (file->buffer_size < new_size)
+    {
+        // Reallocate to a larger size:
+
+        new_buffer = realloc(file->buffer, new_size);
+
+        if (new_buffer == NULL)
+        {
+            fprintf(stderr, "ExpandBuffer: Failed to expand buffer to %u "
+                            "bytes\n", new_size);
+            return false;
+        }
+
+        file->buffer = new_buffer;
+        file->buffer_size = new_size;
+    }
+
+    return true;
+}
+
+// Read a byte sequence into the data buffer.
+
+static boolean ReadByteSequence(midi_file_t *file, unsigned int num_bytes)
+{
+    unsigned int i;
+
+    // Check that we have enough space:
+
+    if (!ExpandBuffer(file, num_bytes))
+    {
+        return false;
+    }
+
+    for (i=0; i<num_bytes; ++i)
+    {
+        if (!ReadByte(file, &file->buffer[i]))
+        {
+            fprintf(stderr, "ReadByteSequence: Error while reading byte %u\n",
+                            i);
+            return false;
+        }
+    }
+
+    return true;
+}
+
 // Read a MIDI channel event.
 // two_param indicates that the event type takes two parameters
 // (three byte) otherwise it is single parameter (two byte)
@@ -182,7 +296,7 @@
 static boolean ReadChannelEvent(midi_file_t *file, midi_event_t *event,
                                 int event_type, boolean two_param)
 {
-    int c;
+    byte b;
 
     // Set basics:
 
@@ -191,27 +305,23 @@
 
     // Read parameters:
 
-    c = fgetc(file->stream);
-
-    if (c == EOF)
+    if (!ReadByte(file, &b))
     {
         return false;
     }
 
-    event->data.channel.param1 = c;
+    event->data.channel.param1 = b;
 
     // Second parameter:
 
     if (two_param)
     {
-        c = fgetc(file->stream);
-
-        if (c == EOF)
+        if (!ReadByte(file, &b))
         {
             return false;
         }
 
-        event->data.channel.param2 = c;
+        event->data.channel.param2 = b;
     }
 
     return true;
@@ -222,8 +332,26 @@
 static boolean ReadSysExEvent(midi_file_t *file, midi_event_t *event,
                               int event_type)
 {
-    // TODO
-    return false;
+    event->event_type = event_type;
+
+    if (!ReadVariableLength(file, &event->data.sysex.length))
+    {
+        fprintf(stderr, "ReadSysExEvent: Failed to read length of "
+                                        "SysEx block\n");
+        return false;
+    }
+
+    // Read the byte sequence:
+
+    if (!ReadByteSequence(file, event->data.sysex.length))
+    {
+        fprintf(stderr, "ReadSysExEvent: Failed while reading SysEx event\n");
+        return false;
+    }
+
+    event->data.sysex.data = file->buffer;
+
+    return true;
 }
 
 // Read meta event:
@@ -230,18 +358,53 @@
 
 static boolean ReadMetaEvent(midi_file_t *file, midi_event_t *event)
 {
-    // TODO
+    byte b;
+
+    // Read meta event type:
+
+    if (!ReadByte(file, &b))
+    {
+        fprintf(stderr, "ReadMetaEvent: Failed to read meta event type\n");
+        return false;
+    }
+
+    event->data.meta.type = b;
+
+    // Read length of meta event data:
+
+    if (!ReadVariableLength(file, &event->data.meta.length))
+    {
+        fprintf(stderr, "ReadSysExEvent: Failed to read length of "
+                                        "SysEx block\n");
+        return false;
+    }
+
+    // Read the byte sequence:
+
+    if (!ReadByteSequence(file, event->data.meta.length))
+    {
+        fprintf(stderr, "ReadSysExEvent: Failed while reading SysEx event\n");
+        return false;
+    }
+
+    event->data.meta.data = file->buffer;
+
     return false;
 }
 
 boolean MIDI_ReadEvent(midi_file_t *file, midi_event_t *event)
 {
-    int event_type;
+    byte event_type;
 
-    event_type = fgetc(file->stream);
+    if (!ReadVariableLength(file, &event->delta_time))
+    {
+        fprintf(stderr, "MIDI_ReadEvent: Failed to read event timestamp\n");
+        return false;
+    }
 
-    if (event_type == EOF)
+    if (!ReadByte(file, &event_type))
     {
+        fprintf(stderr, "MIDI_ReadEvent: Failed to read event type\n");
         return false;
     }
 
@@ -277,7 +440,8 @@
                 return ReadMetaEvent(file, event);
             }
 
-        // Fall-through deliberate -
+        // --- Fall-through deliberate ---
+        // Other 0xfx event types are unknown
 
         default:
             fprintf(stderr, "Unknown MIDI event type: 0x%x\n", event_type);