opl: Add API to adjust tempo.
When the tempo is changed, the times on all active timers must be adjusted to match the new timing values. Add an API to do this and invoke it when a tempo change meta event is read.
This commit is contained in:
parent
541267071a
commit
495694da29
12 changed files with 65 additions and 10 deletions
|
|
@ -452,3 +452,11 @@ void OPL_SetPaused(int paused)
|
|||
}
|
||||
}
|
||||
|
||||
void OPL_AdjustCallbacks(float value)
|
||||
{
|
||||
if (driver != NULL)
|
||||
{
|
||||
driver->adjust_callbacks_func(value);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
Loading…
Reference in a new issue