ref: e898c2f0dec6ea5c6365fef9d494c7f94240bea5
parent: b2e5952c454dd46b29ca6c64e742b4505c20d5b7
author: Simon Howard <fraggle@gmail.com>
date: Wed May 7 20:44:50 EDT 2014
opl: Process MIDI 'set tempo' meta events. The MIDI format includes a special meta event to set the tempo of playback, and some WADs depend on this - notably the music in Alien Vendetta. Move the variables controlling tempo to the global scope (they are not per-track as I previously thought) and set when the tempo events are encountered. This is some progress towards resolving #334, but that bug is not yet completely fixed, because the tempo change does not retroactively apply to OPL timers that have already been set.
--- a/src/i_oplmusic.c
+++ b/src/i_oplmusic.c
@@ -46,6 +46,9 @@
#define PERCUSSION_LOG_LEN 16
+// TODO: Figure out why this is needed.
+#define TEMPO_FUDGE_FACTOR 0.26
+
typedef struct
{
byte tremolo;
@@ -103,11 +106,6 @@
// Track iterator used to read new events.
midi_track_iter_t *iter;
-
- // Tempo control variables
-
- unsigned int ticks_per_beat;
- unsigned int ms_per_beat;
} opl_track_data_t;
typedef struct opl_voice_s opl_voice_t;
@@ -324,6 +322,11 @@
static unsigned int running_tracks = 0;
static boolean song_looping;
+// Tempo control variables
+
+static unsigned int ticks_per_beat;
+static unsigned int us_per_beat;
+
// Mini-log of recently played percussion instruments:
static uint8_t last_perc[PERCUSSION_LOG_LEN];
@@ -1017,6 +1020,9 @@
static void MetaEvent(opl_track_data_t *track, midi_event_t *event)
{
+ byte *data = event->data.meta.data;
+ unsigned int data_len = event->data.meta.length;
+
switch (event->data.meta.type)
{
// Things we can just ignore.
@@ -1032,6 +1038,13 @@
case MIDI_META_SEQUENCER_SPECIFIC:
break;
+ case MIDI_META_SET_TEMPO:
+ if (data_len == 3)
+ {
+ us_per_beat = (data[0] << 16) | (data[1] << 8) | data[2];
+ }
+ break;
+
// End of track - actually handled when we run out of events
// in the track, see below.
@@ -1161,7 +1174,7 @@
// Get the number of milliseconds until the next event.
nticks = MIDI_GetDeltaTime(track->iter);
- ms = (nticks * track->ms_per_beat) / track->ticks_per_beat;
+ ms = (nticks * us_per_beat * TEMPO_FUDGE_FACTOR) / ticks_per_beat;
total += ms;
// Set a timer to be invoked when the next event is
@@ -1190,13 +1203,7 @@
track = &tracks[track_num];
track->iter = MIDI_IterateTrack(file, track_num);
- track->ticks_per_beat = MIDI_GetFileTimeDivision(file);
- // Default is 120 bpm.
- // TODO: this is wrong
-
- track->ms_per_beat = 500 * 260;
-
for (i=0; i<MIDI_CHANNELS_PER_TRACK; ++i)
{
InitChannel(track, &track->channels[i]);
@@ -1228,6 +1235,13 @@
num_tracks = MIDI_NumTracks(file);
running_tracks = num_tracks;
song_looping = looping;
+
+ ticks_per_beat = MIDI_GetFileTimeDivision(file);
+
+ // Default is 120 bpm.
+ // TODO: this is wrong
+
+ us_per_beat = 500 * 1000;
for (i=0; i<num_tracks; ++i)
{