foxygit / doom Log in
commit 79698ecfd9e025f28350566d2b89b223688a1b45
Author:     Simon Howard <fraggle@gmail.com>
AuthorDate: Mon Sep 21 22:20:33 2009 +0000
Commit:     Simon Howard <fraggle@gmail.com>
CommitDate: Mon Sep 21 22:20:33 2009 +0000

    Implement pausing of music.

    Subversion-branch: /branches/opl-branch
    Subversion-revision: 1688
---
 OPL-TODO           |  1 -
 opl/opl.c          | 10 +++++++++-
 opl/opl.h          |  4 ++++
 opl/opl_internal.h |  2 ++
 opl/opl_linux.c    |  3 ++-
 opl/opl_sdl.c      | 35 +++++++++++++++++++++++++++++------
 opl/opl_timer.c    | 34 ++++++++++++++++++++++++++++++++--
 opl/opl_timer.h    |  1 +
 src/i_oplmusic.c   | 20 ++++++++++++++++++++
 9 files changed, 99 insertions(+), 11 deletions(-)

diff --git a/OPL-TODO b/OPL-TODO
index 87d79685..3def4e36 100644
--- a/OPL-TODO
+++ b/OPL-TODO
@@ -15,7 +15,6 @@ Bad MIDIs:

 Other tasks:

- * Pause music
  * Add option to select MIDI type in setup tool
  * DMXOPTIONS opl3/phase option support.

diff --git a/opl/opl.c b/opl/opl.c
index 6e75c951..9d73fe0e 100644
--- 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;
@@ -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);
+    }
+}
+
diff --git a/opl/opl.h b/opl/opl.h
index a0998404..f5b93a64 100644
--- a/opl/opl.h
+++ b/opl/opl.h
@@ -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)