shithub: choc

Download patch

ref: 556f7291ea0199144794166af2757aa7ad832a7a
parent: eb2291030ae0f1e005a6014193fdfeaa796a913a
author: Simon Howard <fraggle@gmail.com>
date: Tue Sep 1 14:17:11 EDT 2009

Loop songs (when appropriate)

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

--- a/src/i_oplmusic.c
+++ b/src/i_oplmusic.c
@@ -206,6 +206,9 @@
 // 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 @@
             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 @@
             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 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 @@
 
     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 @@
 
     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 @@
             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)
--- a/src/midifile.c
+++ b/src/midifile.c
@@ -655,6 +655,11 @@
     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)
@@ -693,6 +698,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
--- a/src/midifile.h
+++ b/src/midifile.h
@@ -155,6 +155,10 @@
 
 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);
@@ -162,6 +166,10 @@
 // Get a pointer to the next MIDI event.
 
 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 */