static void OPL_AdvanceTime(unsigned int nsamples) { opl_callback_t callback; void *callback_data; // Advance time. current_time += nsamples; if (opl_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) + pause_offset) { // Pop the callback from the queue to invoke it. if (!OPL_Queue_Pop(callback_queue, &callback, &callback_data)) { break; } callback(callback_data); } }
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 // times will be right. if (opl_timer_paused) { *next_time = current_time + 50 * OPL_MS; pause_offset += 50 * OPL_MS; return 0; } // If there are no queued callbacks, sleep for 50ms at a time // until a callback is added. if (OPL_Queue_IsEmpty(callback_queue)) { *next_time = current_time + 50 * OPL_MS; return 0; } // Read the time of the first callback in the queue. // If the time for the callback has not yet arrived, // we must sleep until the callback time. *next_time = OPL_Queue_Peek(callback_queue) + pause_offset; return *next_time <= current_time; }
static void OPL_Mix_Callback(void *udata, Uint8 *byte_buffer, int buffer_bytes) { int16_t *buffer; unsigned int buffer_len; unsigned int filled = 0; // Buffer length in samples (quadrupled, because of 16-bit and stereo) buffer = (int16_t *) byte_buffer; buffer_len = buffer_bytes / 4; // Repeatedly call the OPL emulator update function until the buffer is // full. while (filled < buffer_len) { uint64_t next_callback_time; uint64_t nsamples; SDL_LockMutex(callback_queue_mutex); // Work out the time until the next callback waiting in // the callback queue must be invoked. We can then fill the // buffer with this many samples. if (opl_sdl_paused || OPL_Queue_IsEmpty(callback_queue)) { nsamples = buffer_len - filled; } else { next_callback_time = OPL_Queue_Peek(callback_queue) + pause_offset; nsamples = (next_callback_time - current_time) * mixing_freq; nsamples = (nsamples + OPL_SECOND - 1) / OPL_SECOND; if (nsamples > buffer_len - filled) { nsamples = buffer_len - filled; } } SDL_UnlockMutex(callback_queue_mutex); // Add emulator output to buffer. FillBuffer(buffer + filled * 2, nsamples); filled += nsamples; // Invoke callbacks for this point in time. AdvanceTime(nsamples); } }
void OPL_Render_Samples(void *dest, unsigned buffer_len) { unsigned int filled = 0; short *buffer = (short *)dest; // Repeatedly call the OPL emulator update function until the buffer is // full. while (filled < buffer_len) { unsigned int next_callback_time; unsigned int nsamples; // Work out the time until the next callback waiting in // the callback queue must be invoked. We can then fill the // buffer with this many samples. if (opl_paused || OPL_Queue_IsEmpty(callback_queue)) { nsamples = buffer_len - filled; } else { next_callback_time = OPL_Queue_Peek(callback_queue) + pause_offset; nsamples = next_callback_time - current_time; if (nsamples > buffer_len - filled) { nsamples = buffer_len - filled; } } // Add emulator output to buffer. #ifdef STEREO FillBuffer(buffer + filled * 2, nsamples); #else FillBuffer(buffer + filled, nsamples); #endif filled += nsamples; // Invoke callbacks for this point in time. OPL_AdvanceTime(nsamples); } }
int main() { opl_callback_queue_t *queue; int iteration; queue = OPL_Queue_Create(); for (iteration=0; iteration<5000; ++iteration) { opl_callback_t callback; void *data; unsigned int time; unsigned int newtime; int i; for (i=0; i<MAX_OPL_QUEUE; ++i) { time = rand() % 0x10000; OPL_Queue_Push(queue, NULL, NULL, time); } time = 0; for (i=0; i<MAX_OPL_QUEUE; ++i) { assert(!OPL_Queue_IsEmpty(queue)); newtime = OPL_Queue_Peek(queue); assert(OPL_Queue_Pop(queue, &callback, &data)); assert(newtime >= time); time = newtime; } assert(OPL_Queue_IsEmpty(queue)); assert(!OPL_Queue_Pop(queue, &callback, &data)); } }
static void AdvanceTime(unsigned int nsamples) { opl_callback_t callback; void *callback_data; uint64_t us; SDL_LockMutex(callback_queue_mutex); // Advance time. us = ((uint64_t) nsamples * OPL_SECOND) / mixing_freq; current_time += us; if (opl_sdl_paused) { pause_offset += us; } // Are there callbacks to invoke now? Keep invoking them // until there are no more left. while (!OPL_Queue_IsEmpty(callback_queue) && current_time >= OPL_Queue_Peek(callback_queue) + pause_offset) { // Pop the callback from the queue to invoke it. if (!OPL_Queue_Pop(callback_queue, &callback, &callback_data)) { break; } // The mutex stuff here is a bit complicated. We must // hold callback_mutex when we invoke the callback (so that // the control thread can use OPL_Lock() to prevent callbacks // from being invoked), but we must not be holding // callback_queue_mutex, as the callback must be able to // call OPL_SetCallback to schedule new callbacks. SDL_UnlockMutex(callback_queue_mutex); SDL_LockMutex(callback_mutex); callback(callback_data); SDL_UnlockMutex(callback_mutex); SDL_LockMutex(callback_queue_mutex); } SDL_UnlockMutex(callback_queue_mutex); }