ref: 495694da29ff736fba2fdc696553ee7197247174
parent: 541267071a118fe5cc702632fdba5817e27b6f76
author: Simon Howard <fraggle@gmail.com>
date: Sat May 10 10:00:41 EDT 2014
opl: Add API to adjust tempo. When the tempo is changed, the times on all active timers must be adjusted to match the new timing values. Add an API to do this and invoke it when a tempo change meta event is read.
--- a/opl/opl.c
+++ b/opl/opl.c
@@ -452,3 +452,11 @@
}
}
+void OPL_AdjustCallbacks(float value)
+{
+ if (driver != NULL)
+ {
+ driver->adjust_callbacks_func(value);
+ }
+}
+
--- a/opl/opl.h
+++ b/opl/opl.h
@@ -104,6 +104,11 @@
void OPL_SetCallback(unsigned int ms, opl_callback_t callback, void *data);
+// Adjust callback times by the specified factor. For example, a value of
+// 0.5 will halve all remaining times.
+
+void OPL_AdjustCallbacks(float factor);
+
// Clear all OPL callbacks that have been set.
void OPL_ClearCallbacks(void);
--- a/opl/opl_internal.h
+++ b/opl/opl_internal.h
@@ -32,6 +32,7 @@
typedef void (*opl_lock_func)(void);
typedef void (*opl_unlock_func)(void);
typedef void (*opl_set_paused_func)(int paused);
+typedef void (*opl_adjust_callbacks_func)(float value);
typedef struct
{
@@ -46,6 +47,7 @@
opl_lock_func lock_func;
opl_unlock_func unlock_func;
opl_set_paused_func set_paused_func;
+ opl_adjust_callbacks_func adjust_callbacks_func;
} opl_driver_t;
// Sample rate to use when doing software emulation.
--- a/opl/opl_linux.c
+++ b/opl/opl_linux.c
@@ -95,7 +95,8 @@
OPL_Timer_ClearCallbacks,
OPL_Timer_Lock,
OPL_Timer_Unlock,
- OPL_Timer_SetPaused
+ OPL_Timer_SetPaused,
+ OPL_Timer_AdjustCallbacks,
};
#endif /* #ifdef HAVE_IOPERM */
--- a/opl/opl_obsd.c
+++ b/opl/opl_obsd.c
@@ -110,7 +110,8 @@
OPL_Timer_ClearCallbacks,
OPL_Timer_Lock,
OPL_Timer_Unlock,
- OPL_Timer_SetPaused
+ OPL_Timer_SetPaused,
+ OPL_Timer_AdjustCallbacks,
};
#endif /* #ifndef NO_OBSD_DRIVER */
--- a/opl/opl_queue.c
+++ b/opl/opl_queue.c
@@ -201,6 +201,19 @@
}
}
+void OPL_Queue_AdjustCallbacks(opl_callback_queue_t *queue,
+ unsigned int time, float factor)
+{
+ int offset;
+ int i;
+
+ for (i = 0; i < queue->num_entries; ++i)
+ {
+ offset = queue->entries[i].time - time;
+ queue->entries[i].time = time + (int) (offset * factor);
+ }
+}
+
#ifdef TEST
#include <assert.h>
--- a/opl/opl_queue.h
+++ b/opl/opl_queue.h
@@ -32,6 +32,8 @@
int OPL_Queue_Pop(opl_callback_queue_t *queue,
opl_callback_t *callback, void **data);
unsigned int OPL_Queue_Peek(opl_callback_queue_t *queue);
+void OPL_Queue_AdjustCallbacks(opl_callback_queue_t *queue,
+ unsigned int time, float factor);
#endif /* #ifndef OPL_QUEUE_H */
--- a/opl/opl_sdl.c
+++ b/opl/opl_sdl.c
@@ -486,6 +486,13 @@
opl_sdl_paused = paused;
}
+static void OPL_SDL_AdjustCallbacks(float factor)
+{
+ SDL_LockMutex(callback_queue_mutex);
+ OPL_Queue_AdjustCallbacks(callback_queue, current_time, factor);
+ SDL_UnlockMutex(callback_queue_mutex);
+}
+
opl_driver_t opl_sdl_driver =
{
"SDL",
@@ -497,6 +504,7 @@
OPL_SDL_ClearCallbacks,
OPL_SDL_Lock,
OPL_SDL_Unlock,
- OPL_SDL_SetPaused
+ OPL_SDL_SetPaused,
+ OPL_SDL_AdjustCallbacks,
};
--- a/opl/opl_timer.c
+++ b/opl/opl_timer.c
@@ -224,6 +224,13 @@
SDL_UnlockMutex(callback_queue_mutex);
}
+void OPL_Timer_AdjustCallbacks(float factor)
+{
+ SDL_LockMutex(callback_queue_mutex);
+ OPL_Queue_AdjustCallbacks(callback_queue, current_time, factor);
+ SDL_UnlockMutex(callback_queue_mutex);
+}
+
void OPL_Timer_Lock(void)
{
SDL_LockMutex(timer_mutex);
--- a/opl/opl_timer.h
+++ b/opl/opl_timer.h
@@ -29,6 +29,7 @@
void OPL_Timer_Lock(void);
void OPL_Timer_Unlock(void);
void OPL_Timer_SetPaused(int paused);
+void OPL_Timer_AdjustCallbacks(float factor);
#endif /* #ifndef OPL_TIMER_H */
--- a/opl/opl_win32.c
+++ b/opl/opl_win32.c
@@ -184,7 +184,8 @@
OPL_Timer_ClearCallbacks,
OPL_Timer_Lock,
OPL_Timer_Unlock,
- OPL_Timer_SetPaused
+ OPL_Timer_SetPaused,
+ OPL_Timer_AdjustCallbacks,
};
#endif /* #ifdef _WIN32 */
--- a/src/i_oplmusic.c
+++ b/src/i_oplmusic.c
@@ -1016,6 +1016,12 @@
}
}
+static void MetaSetTempo(unsigned int tempo)
+{
+ OPL_AdjustCallbacks((float) us_per_beat / tempo);
+ us_per_beat = tempo;
+}
+
// Process a meta event.
static void MetaEvent(opl_track_data_t *track, midi_event_t *event)
@@ -1041,7 +1047,7 @@
case MIDI_META_SET_TEMPO:
if (data_len == 3)
{
- us_per_beat = (data[0] << 16) | (data[1] << 8) | data[2];
+ MetaSetTempo((data[0] << 16) | (data[1] << 8) | data[2]);
}
break;
@@ -1168,19 +1174,19 @@
static void ScheduleTrack(opl_track_data_t *track)
{
unsigned int nticks;
- unsigned int ms;
- static int total = 0;
+ uint64_t ms;
// Get the number of milliseconds until the next event.
nticks = MIDI_GetDeltaTime(track->iter);
- ms = (nticks * us_per_beat * TEMPO_FUDGE_FACTOR) / ticks_per_beat;
- total += ms;
+ ms = nticks;
+ ms *= us_per_beat * TEMPO_FUDGE_FACTOR;
+ ms /= ticks_per_beat;
// Set a timer to be invoked when the next event is
// ready to play.
- OPL_SetCallback(ms, TrackTimerCallback, track);
+ OPL_SetCallback((unsigned int) ms, TrackTimerCallback, track);
}
// Initialize a channel.