foxygit / doom Log in
commit 556f7291ea0199144794166af2757aa7ad832a7a
Author:     Simon Howard <fraggle@gmail.com>
AuthorDate: Tue Sep 1 18:17:11 2009 +0000
Commit:     Simon Howard <fraggle@gmail.com>
CommitDate: Tue Sep 1 18:17:11 2009 +0000

    Loop songs (when appropriate)

    Subversion-branch: /branches/opl-branch
    Subversion-revision: 1654
---
 src/i_oplmusic.c | 93 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--
 src/midifile.c   | 10 ++++++
 src/midifile.h   |  8 +++++
 3 files changed, 108 insertions(+), 3 deletions(-)

diff --git a/src/i_oplmusic.c b/src/i_oplmusic.c
index 74a76b00..1ac502f5 100644
--- a/src/i_oplmusic.c
+++ b/src/i_oplmusic.c
@@ -206,6 +206,9 @@ static opl_voice_t *voice_free_list;
 // Track data for playing tracks:

 static opl_track_data_t *tracks;
+static unsigned int num_tracks;
+static unsigned int running_tracks = 0;
+static boolean song_looping;

 // In the initialisation stage, register writes are spaced by reading
 // from the register port (0).  After initialisation, spacing is
@@ -868,11 +871,40 @@ static void ControllerEvent(opl_track_data_t *track, midi_event_t *event)
             SetChannelVolume(channel, param);
             break;

-        case MIDI_CONTROLLER_PAN:
+        default:
+            fprintf(stderr, "Unknown MIDI controller type: %i\n", controller);
+            break;
+    }
+}
+
+// Process a meta event.
+
+static void MetaEvent(opl_track_data_t *track, midi_event_t *event)
+{
+    switch (event->data.meta.type)
+    {
+        // Things we can just ignore.
+
+        case MIDI_META_SEQUENCE_NUMBER:
+        case MIDI_META_TEXT:
+        case MIDI_META_COPYRIGHT:
+        case MIDI_META_TRACK_NAME:
+        case MIDI_META_INSTR_NAME:
+        case MIDI_META_LYRICS:
+        case MIDI_META_MARKER:
+        case MIDI_META_CUE_POINT:
+        case MIDI_META_SEQUENCER_SPECIFIC:
+            break;
+
+        // End of track - actually handled when we run out of events
+        // in the track, see below.
+
+        case MIDI_META_END_OF_TRACK:
             break;

         default:
-            fprintf(stderr, "Unknown MIDI controller type: %i\n", controller);
+            fprintf(stderr, "Unknown MIDI meta event type: %i\n",
+                            event->data.meta.type);
             break;
     }
 }
@@ -899,6 +931,16 @@ static void ProcessEvent(opl_track_data_t *track, midi_event_t *event)
             ProgramChangeEvent(track, event);
             break;

+        case MIDI_EVENT_META:
+            MetaEvent(track, event);
+            break;
+
+        // SysEx events can be ignored.
+
+        case MIDI_EVENT_SYSEX:
+        case MIDI_EVENT_SYSEX_SPLIT:
+            break;
+
         default:
             fprintf(stderr, "Unknown MIDI event type %i\n", event->event_type);
             break;
@@ -907,6 +949,21 @@ static void ProcessEvent(opl_track_data_t *track, midi_event_t *event)

 static void ScheduleTrack(opl_track_data_t *track);

+// Restart a song from the beginning.
+
+static void RestartSong(void)
+{
+    unsigned int i;
+
+    running_tracks = num_tracks;
+
+    for (i=0; i<num_tracks; ++i)
+    {
+        MIDI_RestartIterator(tracks[i].iter);
+        ScheduleTrack(&tracks[i]);
+    }
+}
+
 // Callback function invoked when another event needs to be read from
 // a track.

@@ -924,6 +981,23 @@ static void TrackTimerCallback(void *arg)

     ProcessEvent(track, event);

+    // End of track?
+
+    if (event->event_type == MIDI_EVENT_META
+     && event->data.meta.type == MIDI_META_END_OF_TRACK)
+    {
+        --running_tracks;
+
+        // When all tracks have finished, restart the song.
+
+        if (running_tracks <= 0 && song_looping)
+        {
+            RestartSong();
+        }
+
+        return;
+    }
+
     // Reschedule the callback for the next event in the track.

     ScheduleTrack(track);
@@ -1001,7 +1075,11 @@ static void I_OPL_PlaySong(void *handle, int looping)

     tracks = malloc(MIDI_NumTracks(file) * sizeof(opl_track_data_t));

-    for (i=0; i<MIDI_NumTracks(file); ++i)
+    num_tracks = MIDI_NumTracks(file);
+    running_tracks = num_tracks;
+    song_looping = looping;
+
+    for (i=0; i<num_tracks; ++i)
     {
         StartTrack(file, i);
     }
@@ -1046,6 +1124,15 @@ static void I_OPL_StopSong(void)
             ReleaseVoice(&voices[i]);
         }
     }
+
+    // Free all track data.
+
+    for (i=0; i<num_tracks; ++i)
+    {
+        MIDI_FreeIterator(tracks[i].iter);
+    }
+
+    free(tracks);
 }

 static void I_OPL_UnRegisterSong(void *handle)
diff --git a/src/midifile.c b/src/midifile.c
index f1d8bb93..bd935ca1 100644
--- a/src/midifile.c
+++ b/src/midifile.c
@@ -655,6 +655,11 @@ midi_track_iter_t *MIDI_IterateTrack(midi_file_t *file, unsigned int track)
     return iter;
 }

+void MIDI_FreeIterator(midi_track_iter_t *iter)
+{
+    free(iter);
+}
+
 // Get the time until the next MIDI event in a track.

 unsigned int MIDI_GetDeltaTime(midi_track_iter_t *iter)
@@ -695,6 +700,11 @@ unsigned int MIDI_GetFileTimeDivision(midi_file_t *file)
     return file->header.time_division;
 }

+void MIDI_RestartIterator(midi_track_iter_t *iter)
+{
+    iter->position = 0;
+}
+
 #ifdef TEST

 static char *MIDI_EventTypeToString(midi_event_type_t event_type)
diff --git a/src/midifile.h b/src/midifile.h
index faef549c..4ee0ddb2 100644
--- a/src/midifile.h
+++ b/src/midifile.h
@@ -155,6 +155,10 @@ unsigned int MIDI_NumTracks(midi_file_t *file);

 midi_track_iter_t *MIDI_IterateTrack(midi_file_t *file, unsigned int track_num);

+// Free an iterator.
+
+void MIDI_FreeIterator(midi_track_iter_t *iter);
+
 // Get the time until the next MIDI event in a track.

 unsigned int MIDI_GetDeltaTime(midi_track_iter_t *iter);
@@ -163,5 +167,9 @@ unsigned int MIDI_GetDeltaTime(midi_track_iter_t *iter);

 int MIDI_GetNextEvent(midi_track_iter_t *iter, midi_event_t **event);

+// Reset an iterator to the beginning of a track.
+
+void MIDI_RestartIterator(midi_track_iter_t *iter);
+
 #endif /* #ifndef MIDIFILE_H */