summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--opl/examples/droplay.c4
-rw-r--r--opl/opl.c18
-rw-r--r--opl/opl.h21
-rw-r--r--opl/opl_internal.h4
-rw-r--r--opl/opl_linux.c3
-rw-r--r--opl/opl_obsd.c3
-rw-r--r--opl/opl_queue.c19
-rw-r--r--opl/opl_queue.h6
-rw-r--r--opl/opl_sdl.c41
-rw-r--r--opl/opl_timer.c37
-rw-r--r--opl/opl_timer.h5
-rw-r--r--opl/opl_win32.c3
-rw-r--r--src/i_oplmusic.c23
-rw-r--r--src/i_sdlmusic.c6
14 files changed, 131 insertions, 62 deletions
diff --git a/opl/examples/droplay.c b/opl/examples/droplay.c
index 99456050..7b20e03d 100644
--- a/opl/examples/droplay.c
+++ b/opl/examples/droplay.c
@@ -139,7 +139,7 @@ void TimerCallback(void *data)
// Schedule the next timer callback.
- OPL_SetCallback(delay, TimerCallback, timer_data);
+ OPL_SetCallback(delay * OPL_MS, TimerCallback, timer_data);
}
void PlayFile(char *filename)
@@ -183,7 +183,7 @@ void PlayFile(char *filename)
running = timer_data.running;
OPL_Unlock();
- SDL_Delay(100);
+ SDL_Delay(100 * OPL_MS);
} while (running);
fclose(timer_data.fstream);
diff --git a/opl/opl.c b/opl/opl.c
index 0666476d..982fb814 100644
--- a/opl/opl.c
+++ b/opl/opl.c
@@ -295,7 +295,7 @@ int OPL_Detect(void)
OPL_ReadStatus();
}
- OPL_Delay(1);
+ OPL_Delay(1 * OPL_MS);
// Read status
result2 = OPL_ReadStatus();
@@ -357,11 +357,11 @@ void OPL_InitRegisters(void)
// Timer functions.
//
-void OPL_SetCallback(unsigned int ms, opl_callback_t callback, void *data)
+void OPL_SetCallback(unsigned int us, opl_callback_t callback, void *data)
{
if (driver != NULL)
{
- driver->set_callback_func(ms, callback, data);
+ driver->set_callback_func(us, callback, data);
}
}
@@ -409,7 +409,7 @@ static void DelayCallback(void *_delay_data)
SDL_UnlockMutex(delay_data->mutex);
}
-void OPL_Delay(unsigned int ms)
+void OPL_Delay(unsigned int us)
{
delay_data_t delay_data;
@@ -425,7 +425,7 @@ void OPL_Delay(unsigned int ms)
delay_data.mutex = SDL_CreateMutex();
delay_data.cond = SDL_CreateCond();
- OPL_SetCallback(ms, DelayCallback, &delay_data);
+ OPL_SetCallback(us, DelayCallback, &delay_data);
// Wait until the callback is invoked.
@@ -452,3 +452,11 @@ void OPL_SetPaused(int paused)
}
}
+void OPL_AdjustCallbacks(float value)
+{
+ if (driver != NULL)
+ {
+ driver->adjust_callbacks_func(value);
+ }
+}
+
diff --git a/opl/opl.h b/opl/opl.h
index f0879b4d..0af8bc17 100644
--- a/opl/opl.h
+++ b/opl/opl.h
@@ -19,6 +19,8 @@
#ifndef OPL_OPL_H
#define OPL_OPL_H
+#include <inttypes.h>
+
typedef void (*opl_callback_t)(void *data);
typedef enum
@@ -50,6 +52,12 @@ typedef enum
#define OPL_REGS_FREQ_2 0xB0
#define OPL_REGS_FEEDBACK 0xC0
+// Times
+
+#define OPL_SECOND ((uint64_t) 1000 * 1000)
+#define OPL_MS ((uint64_t) 1000)
+#define OPL_US ((uint64_t) 1)
+
//
// Low-level functions.
//
@@ -99,10 +107,15 @@ void OPL_InitRegisters(void);
// Timer callback functions.
//
-// Set a timer callback. After the specified number of milliseconds
+// Set a timer callback. After the specified number of microseconds
// have elapsed, the callback will be invoked.
-void OPL_SetCallback(unsigned int ms, opl_callback_t callback, void *data);
+void OPL_SetCallback(unsigned int us, 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.
@@ -117,9 +130,9 @@ void OPL_Lock(void);
void OPL_Unlock(void);
-// Block until the specified number of milliseconds have elapsed.
+// Block until the specified number of microseconds have elapsed.
-void OPL_Delay(unsigned int ms);
+void OPL_Delay(unsigned int us);
// Pause the OPL callbacks.
diff --git a/opl/opl_internal.h b/opl/opl_internal.h
index 91a275d5..2d027ad8 100644
--- a/opl/opl_internal.h
+++ b/opl/opl_internal.h
@@ -25,13 +25,14 @@ typedef int (*opl_init_func)(unsigned int port_base);
typedef void (*opl_shutdown_func)(void);
typedef unsigned int (*opl_read_port_func)(opl_port_t port);
typedef void (*opl_write_port_func)(opl_port_t port, unsigned int value);
-typedef void (*opl_set_callback_func)(unsigned int ms,
+typedef void (*opl_set_callback_func)(unsigned int us,
opl_callback_t callback,
void *data);
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..4b4e4e38 100644
--- a/opl/opl_queue.c
+++ b/opl/opl_queue.c
@@ -28,7 +28,7 @@ typedef struct
{
opl_callback_t callback;
void *data;
- unsigned int time;
+ uint64_t time;
} opl_queue_entry_t;
struct opl_callback_queue_s
@@ -64,7 +64,7 @@ void OPL_Queue_Clear(opl_callback_queue_t *queue)
void OPL_Queue_Push(opl_callback_queue_t *queue,
opl_callback_t callback, void *data,
- unsigned int time)
+ uint64_t time)
{
int entry_id;
int parent_id;
@@ -189,7 +189,7 @@ int OPL_Queue_Pop(opl_callback_queue_t *queue,
return 1;
}
-unsigned int OPL_Queue_Peek(opl_callback_queue_t *queue)
+uint64_t OPL_Queue_Peek(opl_callback_queue_t *queue)
{
if (queue->num_entries > 0)
{
@@ -201,6 +201,19 @@ unsigned int OPL_Queue_Peek(opl_callback_queue_t *queue)
}
}
+void OPL_Queue_AdjustCallbacks(opl_callback_queue_t *queue,
+ uint64_t time, float factor)
+{
+ int64_t offset;
+ int i;
+
+ for (i = 0; i < queue->num_entries; ++i)
+ {
+ offset = queue->entries[i].time - time;
+ queue->entries[i].time = time + (uint64_t) (offset * factor);
+ }
+}
+
#ifdef TEST
#include <assert.h>
diff --git a/opl/opl_queue.h b/opl/opl_queue.h
index b0f479f4..8b4f0dbe 100644
--- a/opl/opl_queue.h
+++ b/opl/opl_queue.h
@@ -28,10 +28,12 @@ void OPL_Queue_Clear(opl_callback_queue_t *queue);
void OPL_Queue_Destroy(opl_callback_queue_t *queue);
void OPL_Queue_Push(opl_callback_queue_t *queue,
opl_callback_t callback, void *data,
- unsigned int time);
+ uint64_t time);
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);
+uint64_t OPL_Queue_Peek(opl_callback_queue_t *queue);
+void OPL_Queue_AdjustCallbacks(opl_callback_queue_t *queue,
+ uint64_t time, float factor);
#endif /* #ifndef OPL_QUEUE_H */
diff --git a/opl/opl_sdl.c b/opl/opl_sdl.c
index 4cd3dc7b..fa9f047a 100644
--- a/opl/opl_sdl.c
+++ b/opl/opl_sdl.c
@@ -39,7 +39,7 @@ typedef struct
unsigned int rate; // Number of times the timer is advanced per sec.
unsigned int enabled; // Non-zero if timer is enabled.
unsigned int value; // Last value that was set.
- unsigned int expire_time; // Calculated time that timer will expire.
+ uint64_t expire_time; // Calculated time that timer will expire.
} opl_timer_t;
// When the callback mutex is locked using OPL_Lock, callback functions
@@ -55,18 +55,18 @@ static opl_callback_queue_t *callback_queue;
static SDL_mutex *callback_queue_mutex = NULL;
-// Current time, in number of samples since startup:
+// Current time, in us since startup:
-static int current_time;
+static uint64_t current_time;
// If non-zero, playback is currently paused.
static int opl_sdl_paused;
-// Time offset (in samples) due to the fact that callbacks
+// Time offset (in us) due to the fact that callbacks
// were previously paused.
-static unsigned int pause_offset;
+static uint64_t pause_offset;
// OPL software emulator structure.
@@ -106,20 +106,22 @@ static void AdvanceTime(unsigned int nsamples)
{
opl_callback_t callback;
void *callback_data;
+ uint64_t us;
SDL_LockMutex(callback_queue_mutex);
// Advance time.
- current_time += nsamples;
+ us = ((uint64_t) nsamples * OPL_SECOND) / mixing_freq;
+ current_time += us;
if (opl_sdl_paused)
{
- pause_offset += nsamples;
+ pause_offset += us;
}
// Are there callbacks to invoke now? Keep invoking them
- // until there are none more left.
+ // until there are no more left.
while (!OPL_Queue_IsEmpty(callback_queue)
&& current_time >= OPL_Queue_Peek(callback_queue) + pause_offset)
@@ -193,8 +195,8 @@ static void OPL_Mix_Callback(void *udata,
while (filled < buffer_len)
{
- unsigned int next_callback_time;
- unsigned int nsamples;
+ uint64_t next_callback_time;
+ uint64_t nsamples;
SDL_LockMutex(callback_queue_mutex);
@@ -210,7 +212,8 @@ static void OPL_Mix_Callback(void *udata,
{
next_callback_time = OPL_Queue_Peek(callback_queue) + pause_offset;
- nsamples = next_callback_time - current_time;
+ nsamples = (next_callback_time - current_time) * mixing_freq;
+ nsamples = (nsamples + OPL_SECOND - 1) / OPL_SECOND;
if (nsamples > buffer_len - filled)
{
@@ -395,7 +398,7 @@ static void OPLTimer_CalculateEndTime(opl_timer_t *timer)
{
tics = 0x100 - timer->value;
timer->expire_time = current_time
- + (tics * opl_sample_rate) / timer->rate;
+ + ((uint64_t) tics * OPL_SECOND) / timer->rate;
}
}
@@ -454,13 +457,13 @@ static void OPL_SDL_PortWrite(opl_port_t port, unsigned int value)
}
}
-static void OPL_SDL_SetCallback(unsigned int ms,
+static void OPL_SDL_SetCallback(unsigned int us,
opl_callback_t callback,
void *data)
{
SDL_LockMutex(callback_queue_mutex);
OPL_Queue_Push(callback_queue, callback, data,
- current_time - pause_offset + (ms * mixing_freq) / 1000);
+ current_time - pause_offset + us);
SDL_UnlockMutex(callback_queue_mutex);
}
@@ -486,6 +489,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 +507,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..312fe7fa 100644
--- a/opl/opl_timer.c
+++ b/opl/opl_timer.c
@@ -31,16 +31,16 @@ typedef enum
static SDL_Thread *timer_thread = NULL;
static thread_state_t timer_thread_state;
-static int current_time;
+static uint64_t 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
+// Offset in microseconds to adjust time due to the fact that playback
// was paused.
-static unsigned int pause_offset = 0;
+static uint64_t pause_offset = 0;
// Queue of callbacks waiting to be invoked.
// The callback queue mutex is held while the callback queue structure
@@ -58,7 +58,7 @@ static SDL_mutex *timer_mutex;
// to be invoked. Otherwise, next_time is set to the time when the
// timer thread must wake up again to check.
-static int CallbackWaiting(unsigned int *next_time)
+static int CallbackWaiting(uint64_t *next_time)
{
// If paused, just wait in 50ms increments until unpaused.
// Update pause_offset so after we unpause, the callback
@@ -66,8 +66,8 @@ static int CallbackWaiting(unsigned int *next_time)
if (opl_timer_paused)
{
- *next_time = current_time + 50;
- pause_offset += 50;
+ *next_time = current_time + 50 * OPL_MS;
+ pause_offset += 50 * OPL_MS;
return 0;
}
@@ -76,7 +76,7 @@ static int CallbackWaiting(unsigned int *next_time)
if (OPL_Queue_IsEmpty(callback_queue))
{
- *next_time = current_time + 50;
+ *next_time = current_time + 50 * OPL_MS;
return 0;
}
@@ -89,11 +89,11 @@ static int CallbackWaiting(unsigned int *next_time)
return *next_time <= current_time;
}
-static unsigned int GetNextTime(void)
+static uint64_t GetNextTime(void)
{
opl_callback_t callback;
void *callback_data;
- unsigned int next_time;
+ uint64_t next_time;
int have_callback;
// Keep running through callbacks until there are none ready to
@@ -131,8 +131,8 @@ static unsigned int GetNextTime(void)
static int ThreadFunction(void *unused)
{
- unsigned int next_time;
- unsigned int now;
+ uint64_t next_time;
+ uint64_t now;
// Keep running until OPL_Timer_StopThread is called.
@@ -142,11 +142,11 @@ static int ThreadFunction(void *unused)
// wait until that time.
next_time = GetNextTime();
- now = SDL_GetTicks();
+ now = SDL_GetTicks() * OPL_MS;
if (next_time > now)
{
- SDL_Delay(next_time - now);
+ SDL_Delay((next_time - now) / OPL_MS);
}
// Update the current time.
@@ -209,11 +209,11 @@ void OPL_Timer_StopThread(void)
FreeResources();
}
-void OPL_Timer_SetCallback(unsigned int ms, opl_callback_t callback, void *data)
+void OPL_Timer_SetCallback(uint64_t us, opl_callback_t callback, void *data)
{
SDL_LockMutex(callback_queue_mutex);
OPL_Queue_Push(callback_queue, callback, data,
- current_time + ms - pause_offset);
+ current_time + us - pause_offset);
SDL_UnlockMutex(callback_queue_mutex);
}
@@ -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..adf81503 100644
--- a/opl/opl_timer.h
+++ b/opl/opl_timer.h
@@ -22,13 +22,12 @@
int OPL_Timer_StartThread(void);
void OPL_Timer_StopThread(void);
-void OPL_Timer_SetCallback(unsigned int ms,
- opl_callback_t callback,
- void *data);
+void OPL_Timer_SetCallback(uint64_t us, opl_callback_t callback, void *data);
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..5f9ab462 100644
--- a/src/i_oplmusic.c
+++ b/src/i_oplmusic.c
@@ -47,7 +47,7 @@
#define PERCUSSION_LOG_LEN 16
// TODO: Figure out why this is needed.
-#define TEMPO_FUDGE_FACTOR 0.26
+#define TEMPO_FUDGE_FACTOR 260
typedef struct
{
@@ -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;
@@ -1154,7 +1160,7 @@ static void TrackTimerCallback(void *arg)
if (running_tracks <= 0 && song_looping)
{
- OPL_SetCallback(5, RestartSong, NULL);
+ OPL_SetCallback(5000, RestartSong, NULL);
}
return;
@@ -1168,19 +1174,18 @@ 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 us;
- // Get the number of milliseconds until the next event.
+ // Get the number of microseconds until the next event.
nticks = MIDI_GetDeltaTime(track->iter);
- ms = (nticks * us_per_beat * TEMPO_FUDGE_FACTOR) / ticks_per_beat;
- total += ms;
+ us = ((uint64_t) nticks * us_per_beat * TEMPO_FUDGE_FACTOR)
+ / ticks_per_beat;
// Set a timer to be invoked when the next event is
// ready to play.
- OPL_SetCallback(ms, TrackTimerCallback, track);
+ OPL_SetCallback(us, TrackTimerCallback, track);
}
// Initialize a channel.
diff --git a/src/i_sdlmusic.c b/src/i_sdlmusic.c
index 81b28515..e7f0aacb 100644
--- a/src/i_sdlmusic.c
+++ b/src/i_sdlmusic.c
@@ -485,6 +485,11 @@ static char *GetFullPath(char *base_filename, char *path)
}
#endif
+ // Paths in the substitute filenames can contain Unix-style /
+ // path separators, but we should convert this to the separator
+ // for the native platform.
+ path = M_StringReplace(path, "/", DIR_SEPARATOR_S);
+
// Copy config filename and cut off the filename to just get the
// parent dir.
basedir = strdup(base_filename);
@@ -499,6 +504,7 @@ static char *GetFullPath(char *base_filename, char *path)
result = strdup(path);
}
free(basedir);
+ free(path);
return result;
}