summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--opl/opl.c8
-rw-r--r--opl/opl.h5
-rw-r--r--opl/opl_internal.h2
-rw-r--r--opl/opl_linux.c3
-rw-r--r--opl/opl_obsd.c3
-rw-r--r--opl/opl_queue.c13
-rw-r--r--opl/opl_queue.h2
-rw-r--r--opl/opl_sdl.c10
-rw-r--r--opl/opl_timer.c7
-rw-r--r--opl/opl_timer.h1
-rw-r--r--opl/opl_win32.c3
-rw-r--r--src/i_oplmusic.c18
12 files changed, 65 insertions, 10 deletions
diff --git a/opl/opl.c b/opl/opl.c
index 0666476d..cc5b861d 100644
--- a/opl/opl.c
+++ b/opl/opl.c
@@ -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..d6a295b0 100644
--- a/opl/opl.h
+++ b/opl/opl.h
@@ -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.