shithub: choc

Download patch

ref: b96cdbe82fa6cb51ac91072ad86e084d739fd9be
parent: 543b9bdc85dfeb84ed855084595407d6bf49d8f6
author: Simon Howard <fraggle@gmail.com>
date: Fri Mar 27 20:24:50 EDT 2009

Initial MIDI file parsing code.

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

--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -156,6 +156,7 @@
 i_sdlsound.c                               \
 i_sdlmusic.c                               \
 i_oplmusic.c                               \
+midifile.c           midifile.h            \
 mus2mid.c            mus2mid.h
 
 SOURCE_FILES = $(MAIN_SOURCE_FILES)                \
--- /dev/null
+++ b/src/midifile.c
@@ -1,0 +1,287 @@
+// Emacs style mode select   -*- C++ -*- 
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 2009 Simon Howard
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+// 02111-1307, USA.
+//
+// DESCRIPTION:
+//    Reading of MIDI files.
+//
+//-----------------------------------------------------------------------------
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "doomtype.h"
+#include "i_swap.h"
+#include "midifile.h"
+
+#define HEADER_CHUNK_ID "MThd"
+#define TRACK_CHUNK_ID  "MTrk"
+
+typedef struct
+{
+    byte chunk_id[4];
+    unsigned int chunk_size;
+} chunk_header_t;
+
+typedef struct
+{
+    chunk_header_t chunk_header;
+    unsigned short format_type;
+    unsigned short num_tracks;
+    unsigned int time_division;
+} midi_header_t;
+
+struct midi_file_s
+{
+    FILE *stream;
+    midi_header_t header;
+    unsigned int data_len;
+};
+
+static boolean CheckChunkHeader(chunk_header_t *chunk,
+                                char *expected_id)
+{
+    boolean result;
+    
+    result = (strcmp((char *) chunk->chunk_id, expected_id) == 0);
+
+    if (!result)
+    {
+        fprintf(stderr, "CheckChunkHeader: Expected '%s' chunk header!\n",
+                        expected_id);
+    }
+
+    return result;
+}
+
+// Read and check the header chunk.
+
+static boolean ReadHeaderChunk(midi_file_t *file)
+{
+    size_t bytes_read;
+
+    bytes_read = fread(&file->header, sizeof(midi_header_t), 1, file->stream);
+
+    if (bytes_read < sizeof(midi_header_t))
+    {
+        return false;
+    }
+
+    if (!CheckChunkHeader(&file->header.chunk_header, HEADER_CHUNK_ID)
+     || LONG(file->header.chunk_header.chunk_size) != 6)
+    {
+        fprintf(stderr, "ReadHeaderChunk: Invalid MIDI chunk header!\n");
+        return false;
+    }
+
+    if (SHORT(file->header.format_type) != 0
+     || SHORT(file->header.num_tracks) != 1)
+    {
+        fprintf(stderr, "ReadHeaderChunk: Only single track, "
+                                        "type 0 MIDI files supported!\n");
+        return false;
+    }
+
+    return true;
+}
+
+// Read and check the track chunk header
+
+static boolean ReadTrackChunk(midi_file_t *file)
+{
+    size_t bytes_read;
+    chunk_header_t chunk_header;
+
+    bytes_read = fread(&chunk_header, sizeof(chunk_header_t), 1, file->stream);
+
+    if (bytes_read < sizeof(chunk_header))
+    {
+        return false;
+    }
+
+    if (!CheckChunkHeader(&chunk_header, TRACK_CHUNK_ID))
+    {
+        return false;
+    }
+
+    file->data_len = LONG(chunk_header.chunk_size);
+
+    return true;
+}
+
+midi_file_t *MIDI_OpenFile(char *filename)
+{
+    midi_file_t *file;
+
+    file = malloc(sizeof(midi_file_t));
+
+    if (file == NULL)
+    {
+        return NULL;
+    }
+
+    // Open file
+
+    file->stream = fopen(filename, "rb");
+
+    if (file->stream == NULL)
+    {
+        fprintf(stderr, "Failed to open '%s'\n", filename);
+        free(file);
+        return NULL;
+    }
+
+    // Read MIDI file header
+
+    if (!ReadHeaderChunk(file))
+    {
+        fclose(file->stream);
+        free(file);
+        return NULL;
+    }
+
+    // Read track header
+
+    if (!ReadTrackChunk(file))
+    {
+        fclose(file->stream);
+        free(file);
+        return NULL;
+    }
+
+    return file;
+}
+
+void MIDI_CloseFile(midi_file_t *file)
+{
+    fclose(file->stream);
+    free(file);
+}
+
+// Read a MIDI channel event.
+// two_param indicates that the event type takes two parameters
+// (three byte) otherwise it is single parameter (two byte)
+
+static boolean ReadChannelEvent(midi_file_t *file, midi_event_t *event,
+                                int event_type, boolean two_param)
+{
+    int c;
+
+    // Set basics:
+
+    event->event_type = event_type >> 4;
+    event->data.channel.channel = event_type & 0xf;
+
+    // Read parameters:
+
+    c = fgetc(file->stream);
+
+    if (c == EOF)
+    {
+        return false;
+    }
+
+    event->data.channel.param1 = c;
+
+    // Second parameter:
+
+    if (two_param)
+    {
+        c = fgetc(file->stream);
+
+        if (c == EOF)
+        {
+            return false;
+        }
+
+        event->data.channel.param2 = c;
+    }
+
+    return true;
+}
+
+// Read sysex event:
+
+static boolean ReadSysExEvent(midi_file_t *file, midi_event_t *event,
+                              int event_type)
+{
+    // TODO
+    return false;
+}
+
+// Read meta event:
+
+static boolean ReadMetaEvent(midi_file_t *file, midi_event_t *event)
+{
+    // TODO
+    return false;
+}
+
+boolean MIDI_ReadEvent(midi_file_t *file, midi_event_t *event)
+{
+    int event_type;
+
+    event_type = fgetc(file->stream);
+
+    if (event_type == EOF)
+    {
+        return false;
+    }
+
+    // Check event type:
+
+    switch (event_type >> 4)
+    {
+        // Two parameter channel events:
+
+        case MIDI_EVENT_NOTE_OFF:
+        case MIDI_EVENT_NOTE_ON:
+        case MIDI_EVENT_AFTERTOUCH:
+        case MIDI_EVENT_CONTROLLER:
+        case MIDI_EVENT_PITCH_BEND:
+            return ReadChannelEvent(file, event, event_type, true);
+
+        // Single parameter channel events:
+
+        case MIDI_EVENT_PROGRAM_CHANGE:
+        case MIDI_EVENT_CHAN_AFTERTOUCH:
+            return ReadChannelEvent(file, event, event_type, false);
+
+        // Other event types:
+
+        case 0xf:
+            if (event_type == MIDI_EVENT_SYSEX
+             || event_type == MIDI_EVENT_SYSEX_SPLIT)
+            {
+                return ReadSysExEvent(file, event, event_type);
+            }
+            else if (event_type == MIDI_EVENT_META)
+            {
+                return ReadMetaEvent(file, event);
+            }
+
+        // Fall-through deliberate -
+
+        default:
+            fprintf(stderr, "Unknown MIDI event type: 0x%x\n", event_type);
+            return false;
+    }
+}
+
--- /dev/null
+++ b/src/midifile.h
@@ -1,0 +1,135 @@
+// Emacs style mode select   -*- C++ -*- 
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 2009 Simon Howard
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+// 02111-1307, USA.
+//
+// DESCRIPTION:
+//     MIDI file parsing.
+//
+//-----------------------------------------------------------------------------
+
+#ifndef MIDIFILE_H
+#define MIDIFILE_H
+
+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_SYSEX           = 0xf0,
+    MIDI_EVENT_SYSEX_SPLIT     = 0xf7,
+    MIDI_EVENT_META            = 0xff,
+} midi_event_type_t;
+
+typedef enum
+{
+    MIDI_CONTROLLER_BANK_SELECT     = 0x0,
+    MIDI_CONTROLLER_MODULATION      = 0x1,
+    MIDI_CONTROLLER_BREATH_CONTROL  = 0x2,
+    MIDI_CONTROLLER_FOOT_CONTROL    = 0x3,
+    MIDI_CONTROLLER_PORTAMENTO      = 0x4,
+    MIDI_CONTROLLER_DATA_ENTRY      = 0x5,
+} midi_controller_t;
+
+typedef enum
+{
+    MIDI_META_SEQUENCE_NUMBER       = 0x0,
+
+    MIDI_META_TEXT                  = 0x1,
+    MIDI_META_COPYRIGHT             = 0x2,
+    MIDI_META_TRACK_NAME            = 0x3,
+    MIDI_META_INSTR_NAME            = 0x4,
+    MIDI_META_LYRICS                = 0x5,
+    MIDI_META_MARKER                = 0x6,
+    MIDI_META_CUE_POINT             = 0x7,
+
+    MIDI_META_CHANNEL_PREFIX        = 0x8,
+    MIDI_META_END_OF_TRACK          = 0x9,
+    MIDI_META_SET_TEMPO             = 0xa,
+    MIDI_META_SMPTE_OFFSET          = 0xb,
+    MIDI_META_TIME_SIGNATURE        = 0xc,
+    MIDI_META_KEY_SIGNATURE         = 0xd,
+    MIDI_META_SEQUENCER_SPECIFIC    = 0xe,
+} midi_meta_event_type_t;
+
+typedef struct
+{
+    // Meta event type:
+
+    unsigned int type;
+
+    // Length:
+
+    unsigned int length;
+
+    // Meta event data:
+
+    byte *data;
+} midi_meta_event_data_t;
+
+typedef struct
+{
+    // Length:
+
+    unsigned int length;
+
+    // Event data:
+
+    byte *data;
+} midi_sysex_event_data_t;
+
+typedef struct
+{
+    // The channel number to which this applies:
+
+    unsigned int channel;
+
+    // Extra parameters:
+
+    unsigned int param1;
+    unsigned int param2;
+} midi_channel_event_data_t;
+
+typedef struct
+{
+    // Time between the previous event and this event.
+    unsigned int delta_time;
+
+    // Type of event:
+    midi_event_type_t event_type;
+
+    union
+    {
+        midi_channel_event_data_t channel;
+        midi_meta_event_data_t meta;
+        midi_sysex_event_data_t sysex;
+    } data;
+} midi_event_t;
+
+midi_file_t *MIDI_OpenFile(char *filename);
+void MIDI_CloseFile(midi_file_t *file);
+
+#endif /* #ifndef MIDIFILE_H */
+