diff options
-rw-r--r-- | OPL-TODO | 1 | ||||
-rw-r--r-- | opl/opl.c | 10 | ||||
-rw-r--r-- | opl/opl.h | 4 | ||||
-rw-r--r-- | opl/opl_internal.h | 2 | ||||
-rw-r--r-- | opl/opl_linux.c | 3 | ||||
-rw-r--r-- | opl/opl_sdl.c | 35 | ||||
-rw-r--r-- | opl/opl_timer.c | 34 | ||||
-rw-r--r-- | opl/opl_timer.h | 1 | ||||
-rw-r--r-- | src/i_oplmusic.c | 20 |
9 files changed, 99 insertions, 11 deletions
@@ -15,7 +15,6 @@ Bad MIDIs: Other tasks: - * Pause music * Add option to select MIDI type in setup tool * DMXOPTIONS opl3/phase option support. @@ -33,7 +33,7 @@ #include "opl.h" #include "opl_internal.h" -//#define OPL_DEBUG_TRACE +#define OPL_DEBUG_TRACE #ifdef HAVE_IOPERM extern opl_driver_t opl_linux_driver; @@ -195,3 +195,11 @@ void OPL_Delay(unsigned int ms) SDL_DestroyCond(delay_data.cond); } +void OPL_SetPaused(int paused) +{ + if (driver != NULL) + { + driver->set_paused_func(paused); + } +} + @@ -96,5 +96,9 @@ void OPL_Unlock(void); void OPL_Delay(unsigned int ms); +// Pause the OPL callbacks. + +void OPL_SetPaused(int paused); + #endif diff --git a/opl/opl_internal.h b/opl/opl_internal.h index 384b96f8..78cbe7b2 100644 --- a/opl/opl_internal.h +++ b/opl/opl_internal.h @@ -39,6 +39,7 @@ typedef void (*opl_set_callback_func)(unsigned int ms, 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 struct { @@ -52,6 +53,7 @@ typedef struct opl_clear_callbacks_func clear_callbacks_func; opl_lock_func lock_func; opl_unlock_func unlock_func; + opl_set_paused_func set_paused_func; } opl_driver_t; #endif /* #ifndef OPL_INTERNAL_H */ diff --git a/opl/opl_linux.c b/opl/opl_linux.c index 8a61dbf7..089192c9 100644 --- a/opl/opl_linux.c +++ b/opl/opl_linux.c @@ -94,7 +94,8 @@ opl_driver_t opl_linux_driver = OPL_Timer_SetCallback, OPL_Timer_ClearCallbacks, OPL_Timer_Lock, - OPL_Timer_Unlock + OPL_Timer_Unlock, + OPL_Timer_SetPaused }; #endif /* #ifdef HAVE_IOPERM */ diff --git a/opl/opl_sdl.c b/opl/opl_sdl.c index ffbe6820..e38f9f6e 100644 --- a/opl/opl_sdl.c +++ b/opl/opl_sdl.c @@ -60,6 +60,15 @@ static SDL_mutex *callback_queue_mutex = NULL; static int current_time; +// If non-zero, playback is currently paused. + +static int opl_sdl_paused; + +// Time offset (in samples) due to the fact that callbacks +// were previously paused. + +static unsigned int pause_offset; + // OPL software emulator structure. static FM_OPL *opl_emulator = NULL; @@ -96,11 +105,16 @@ static void AdvanceTime(unsigned int nsamples) current_time += nsamples; + if (opl_sdl_paused) + { + pause_offset += nsamples; + } + // Are there callbacks to invoke now? Keep invoking them // until there are none more left. while (!OPL_Queue_IsEmpty(callback_queue) - && current_time >= OPL_Queue_Peek(callback_queue)) + && current_time >= OPL_Queue_Peek(callback_queue) + pause_offset) { // Pop the callback from the queue to invoke it. @@ -180,13 +194,13 @@ static void OPL_Mix_Callback(void *udata, // the callback queue must be invoked. We can then fill the // buffer with this many samples. - if (OPL_Queue_IsEmpty(callback_queue)) + if (opl_sdl_paused || OPL_Queue_IsEmpty(callback_queue)) { nsamples = buffer_len - filled; } else { - next_callback_time = OPL_Queue_Peek(callback_queue); + next_callback_time = OPL_Queue_Peek(callback_queue) + pause_offset; nsamples = next_callback_time - current_time; @@ -260,7 +274,7 @@ static void TimerHandler(int channel, double interval_seconds) SDL_LockMutex(callback_queue_mutex); OPL_Queue_Push(callback_queue, TimerOver, (void *) channel, - current_time + interval_samples); + current_time - pause_offset + interval_samples); SDL_UnlockMutex(callback_queue_mutex); } @@ -297,6 +311,9 @@ static int OPL_SDL_Init(unsigned int port_base) sdl_was_initialised = 0; } + opl_sdl_paused = 0; + pause_offset = 0; + // Queue structure of callbacks to invoke. callback_queue = OPL_Queue_Create(); @@ -370,7 +387,7 @@ static void OPL_SDL_SetCallback(unsigned int ms, { SDL_LockMutex(callback_queue_mutex); OPL_Queue_Push(callback_queue, callback, data, - current_time + (ms * mixing_freq) / 1000); + current_time - pause_offset + (ms * mixing_freq) / 1000); SDL_UnlockMutex(callback_queue_mutex); } @@ -391,6 +408,11 @@ static void OPL_SDL_Unlock(void) SDL_UnlockMutex(callback_mutex); } +static void OPL_SDL_SetPaused(int paused) +{ + opl_sdl_paused = paused; +} + opl_driver_t opl_sdl_driver = { "SDL", @@ -401,6 +423,7 @@ opl_driver_t opl_sdl_driver = OPL_SDL_SetCallback, OPL_SDL_ClearCallbacks, OPL_SDL_Lock, - OPL_SDL_Unlock + OPL_SDL_Unlock, + OPL_SDL_SetPaused }; diff --git a/opl/opl_timer.c b/opl/opl_timer.c index e254a5e2..35b2092f 100644 --- a/opl/opl_timer.c +++ b/opl/opl_timer.c @@ -41,6 +41,15 @@ static SDL_Thread *timer_thread = NULL; static thread_state_t timer_thread_state; static int current_time; +// If non-zero, callbacks are currently paused. + +static int opl_timer_paused; + +// Offset in milliseconds to adjust time due to the fact that playback +// was paused. + +static unsigned int pause_offset = 0; + // Queue of callbacks waiting to be invoked. // The callback queue mutex is held while the callback queue structure // or current_time is being accessed. @@ -59,6 +68,17 @@ static SDL_mutex *timer_mutex; static int CallbackWaiting(unsigned int *next_time) { + // If paused, just wait in 50ms increments until unpaused. + // Update pause_offset so after we unpause, the callback + // times will be right. + + if (opl_timer_paused) + { + *next_time = current_time + 50; + pause_offset += 50; + return 0; + } + // If there are no queued callbacks, sleep for 50ms at a time // until a callback is added. @@ -72,7 +92,7 @@ static int CallbackWaiting(unsigned int *next_time) // If the time for the callback has not yet arrived, // we must sleep until the callback time. - *next_time = OPL_Queue_Peek(callback_queue); + *next_time = OPL_Queue_Peek(callback_queue) + pause_offset; return *next_time <= current_time; } @@ -169,6 +189,8 @@ int OPL_Timer_StartThread(void) timer_thread_state = THREAD_STATE_RUNNING; current_time = SDL_GetTicks(); + opl_timer_paused = 0; + pause_offset = 0; timer_thread = SDL_CreateThread(ThreadFunction, NULL); @@ -198,7 +220,8 @@ void OPL_Timer_StopThread(void) void OPL_Timer_SetCallback(unsigned int ms, opl_callback_t callback, void *data) { SDL_LockMutex(callback_queue_mutex); - OPL_Queue_Push(callback_queue, callback, data, current_time + ms); + OPL_Queue_Push(callback_queue, callback, data, + current_time + ms - pause_offset); SDL_UnlockMutex(callback_queue_mutex); } @@ -219,3 +242,10 @@ void OPL_Timer_Unlock(void) SDL_UnlockMutex(timer_mutex); } +void OPL_Timer_SetPaused(int paused) +{ + SDL_LockMutex(callback_queue_mutex); + opl_timer_paused = paused; + SDL_UnlockMutex(callback_queue_mutex); +} + diff --git a/opl/opl_timer.h b/opl/opl_timer.h index e8657a90..f03fc499 100644 --- a/opl/opl_timer.h +++ b/opl/opl_timer.h @@ -36,6 +36,7 @@ void OPL_Timer_SetCallback(unsigned int ms, void OPL_Timer_ClearCallbacks(void); void OPL_Timer_Lock(void); void OPL_Timer_Unlock(void); +void OPL_Timer_SetPaused(int paused); #endif /* #ifndef OPL_TIMER_H */ diff --git a/src/i_oplmusic.c b/src/i_oplmusic.c index 4ac52754..7bfdc04d 100644 --- a/src/i_oplmusic.c +++ b/src/i_oplmusic.c @@ -1363,10 +1363,28 @@ static void I_OPL_PlaySong(void *handle, int looping) static void I_OPL_PauseSong(void) { + unsigned int i; + if (!music_initialised) { return; } + + // Pause OPL callbacks. + + OPL_SetPaused(1); + + // Turn off all main instrument voices (not percussion). + // This is what Vanilla does. + + for (i=0; i<OPL_NUM_VOICES; ++i) + { + if (voices[i].channel != NULL + && voices[i].current_instr < percussion_instrs) + { + VoiceKeyOff(&voices[i]); + } + } } static void I_OPL_ResumeSong(void) @@ -1375,6 +1393,8 @@ static void I_OPL_ResumeSong(void) { return; } + + OPL_SetPaused(0); } static void I_OPL_StopSong(void) |