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 */