ref: 79698ecfd9e025f28350566d2b89b223688a1b45
parent: 38b5ee9991eda235bf67aa2260e73e755b1da081
author: Simon Howard <[email protected]>
date: Mon Sep 21 18:20:33 EDT 2009
Implement pausing of music. Subversion-branch: /branches/opl-branch Subversion-revision: 1688
--- a/OPL-TODO
+++ b/OPL-TODO
@@ -15,7 +15,6 @@
Other tasks:
- * Pause music
* Add option to select MIDI type in setup tool
* DMXOPTIONS opl3/phase option support.
--- a/opl/opl.c
+++ b/opl/opl.c
@@ -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;
@@ -193,5 +193,13 @@
SDL_DestroyMutex(delay_data.mutex);
SDL_DestroyCond(delay_data.cond);
+}
+
+void OPL_SetPaused(int paused)
+{
+ if (driver != NULL)
+ {
+ driver->set_paused_func(paused);
+ }
}
--- a/opl/opl.h
+++ b/opl/opl.h
@@ -96,5 +96,9 @@
void OPL_Delay(unsigned int ms);
+// Pause the OPL callbacks.
+
+void OPL_SetPaused(int paused);
+
#endif
--- a/opl/opl_internal.h
+++ b/opl/opl_internal.h
@@ -39,6 +39,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 struct
{
@@ -52,6 +53,7 @@
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 */
--- a/opl/opl_linux.c
+++ b/opl/opl_linux.c
@@ -94,7 +94,8 @@
OPL_Timer_SetCallback,
OPL_Timer_ClearCallbacks,
OPL_Timer_Lock,
- OPL_Timer_Unlock
+ OPL_Timer_Unlock,
+ OPL_Timer_SetPaused
};
#endif /* #ifdef HAVE_IOPERM */
--- a/opl/opl_sdl.c
+++ b/opl/opl_sdl.c
@@ -60,6 +60,15 @@
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 @@
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 @@
// 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 @@
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 @@
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 @@
{
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 @@
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_SDL_SetCallback,
OPL_SDL_ClearCallbacks,
OPL_SDL_Lock,
- OPL_SDL_Unlock
+ OPL_SDL_Unlock,
+ OPL_SDL_SetPaused
};
--- a/opl/opl_timer.c
+++ b/opl/opl_timer.c
@@ -41,6 +41,15 @@
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 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 @@
// 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 @@
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_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);
}
@@ -217,5 +240,12 @@
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);
}
--- a/opl/opl_timer.h
+++ b/opl/opl_timer.h
@@ -36,6 +36,7 @@
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 */
--- a/src/i_oplmusic.c
+++ b/src/i_oplmusic.c
@@ -1363,10 +1363,28 @@
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 @@
{
return;
}
+
+ OPL_SetPaused(0);
}
static void I_OPL_StopSong(void)