diff options
author | Simon Howard | 2014-05-10 14:00:41 -0400 |
---|---|---|
committer | Simon Howard | 2014-05-10 14:00:41 -0400 |
commit | 495694da29ff736fba2fdc696553ee7197247174 (patch) | |
tree | 49c7cb18a42f9d5302aead68572a8deca8d9445a | |
parent | 541267071a118fe5cc702632fdba5817e27b6f76 (diff) | |
download | chocolate-doom-495694da29ff736fba2fdc696553ee7197247174.tar.gz chocolate-doom-495694da29ff736fba2fdc696553ee7197247174.tar.bz2 chocolate-doom-495694da29ff736fba2fdc696553ee7197247174.zip |
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.
-rw-r--r-- | opl/opl.c | 8 | ||||
-rw-r--r-- | opl/opl.h | 5 | ||||
-rw-r--r-- | opl/opl_internal.h | 2 | ||||
-rw-r--r-- | opl/opl_linux.c | 3 | ||||
-rw-r--r-- | opl/opl_obsd.c | 3 | ||||
-rw-r--r-- | opl/opl_queue.c | 13 | ||||
-rw-r--r-- | opl/opl_queue.h | 2 | ||||
-rw-r--r-- | opl/opl_sdl.c | 10 | ||||
-rw-r--r-- | opl/opl_timer.c | 7 | ||||
-rw-r--r-- | opl/opl_timer.h | 1 | ||||
-rw-r--r-- | opl/opl_win32.c | 3 | ||||
-rw-r--r-- | src/i_oplmusic.c | 18 |
12 files changed, 65 insertions, 10 deletions
@@ -452,3 +452,11 @@ void OPL_SetPaused(int paused) } } +void OPL_AdjustCallbacks(float value) +{ + if (driver != NULL) + { + driver->adjust_callbacks_func(value); + } +} + @@ -104,6 +104,11 @@ void OPL_InitRegisters(void); 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); diff --git a/opl/opl_internal.h b/opl/opl_internal.h index 91a275d5..24e79644 100644 --- a/opl/opl_internal.h +++ b/opl/opl_internal.h @@ -32,6 +32,7 @@ typedef void (*opl_clear_callbacks_func)(void); 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 @@ typedef struct 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. diff --git a/opl/opl_linux.c b/opl/opl_linux.c index 12d21075..5df5d468 100644 --- a/opl/opl_linux.c +++ b/opl/opl_linux.c @@ -95,7 +95,8 @@ opl_driver_t opl_linux_driver = OPL_Timer_ClearCallbacks, OPL_Timer_Lock, OPL_Timer_Unlock, - OPL_Timer_SetPaused + OPL_Timer_SetPaused, + OPL_Timer_AdjustCallbacks, }; #endif /* #ifdef HAVE_IOPERM */ diff --git a/opl/opl_obsd.c b/opl/opl_obsd.c index 3313c1b2..39e0c156 100644 --- a/opl/opl_obsd.c +++ b/opl/opl_obsd.c @@ -110,7 +110,8 @@ opl_driver_t opl_openbsd_driver = OPL_Timer_ClearCallbacks, OPL_Timer_Lock, OPL_Timer_Unlock, - OPL_Timer_SetPaused + OPL_Timer_SetPaused, + OPL_Timer_AdjustCallbacks, }; #endif /* #ifndef NO_OBSD_DRIVER */ diff --git a/opl/opl_queue.c b/opl/opl_queue.c index d05cc6a1..ee87a19b 100644 --- a/opl/opl_queue.c +++ b/opl/opl_queue.c @@ -201,6 +201,19 @@ unsigned int OPL_Queue_Peek(opl_callback_queue_t *queue) } } +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> diff --git a/opl/opl_queue.h b/opl/opl_queue.h index b0f479f4..20ddeda9 100644 --- a/opl/opl_queue.h +++ b/opl/opl_queue.h @@ -32,6 +32,8 @@ void OPL_Queue_Push(opl_callback_queue_t *queue, 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 */ diff --git a/opl/opl_sdl.c b/opl/opl_sdl.c index 4cd3dc7b..3ed7f784 100644 --- a/opl/opl_sdl.c +++ b/opl/opl_sdl.c @@ -486,6 +486,13 @@ static void OPL_SDL_SetPaused(int paused) 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_driver_t opl_sdl_driver = OPL_SDL_ClearCallbacks, OPL_SDL_Lock, OPL_SDL_Unlock, - OPL_SDL_SetPaused + OPL_SDL_SetPaused, + OPL_SDL_AdjustCallbacks, }; diff --git a/opl/opl_timer.c b/opl/opl_timer.c index 97e5ea8b..bab15687 100644 --- a/opl/opl_timer.c +++ b/opl/opl_timer.c @@ -224,6 +224,13 @@ void OPL_Timer_ClearCallbacks(void) 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); diff --git a/opl/opl_timer.h b/opl/opl_timer.h index 1febcac9..73544013 100644 --- a/opl/opl_timer.h +++ b/opl/opl_timer.h @@ -29,6 +29,7 @@ void OPL_Timer_ClearCallbacks(void); 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 */ diff --git a/opl/opl_win32.c b/opl/opl_win32.c index c6c37803..ea7b9eeb 100644 --- a/opl/opl_win32.c +++ b/opl/opl_win32.c @@ -184,7 +184,8 @@ opl_driver_t opl_win32_driver = OPL_Timer_ClearCallbacks, OPL_Timer_Lock, OPL_Timer_Unlock, - OPL_Timer_SetPaused + OPL_Timer_SetPaused, + OPL_Timer_AdjustCallbacks, }; #endif /* #ifdef _WIN32 */ diff --git a/src/i_oplmusic.c b/src/i_oplmusic.c index f38e7162..489b6d55 100644 --- a/src/i_oplmusic.c +++ b/src/i_oplmusic.c @@ -1016,6 +1016,12 @@ static void PitchBendEvent(opl_track_data_t *track, midi_event_t *event) } } +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 @@ static void MetaEvent(opl_track_data_t *track, midi_event_t *event) 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 TrackTimerCallback(void *arg) 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. |