// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // OPL SDL interface. // #include #include "doomtype.h" #include "opl.h" #include "opl3.h" #include "opl_internal.h" #include "opl_queue.h" #define MAX_SOUND_SLICE_TIME 100 /* ms */ #define OPL_SILENCE_THRESHOLD 36 // Allow for a worst case of +/-1 ripple in all 36 operators. #define OPL_CHIP_TIMEOUT (OPL_SAMPLE_RATE / 2) // 0.5 seconds of silence with no key-on // should be enough to ensure chip is "off" 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. uint64_t expire_time; // Calculated time that timer will expire. } opl_timer_t; // Queue of callbacks waiting to be invoked. static opl_callback_queue_t *callback_queue; // Current time, in us since startup: static uint64_t current_time; // If non-zero, playback is currently paused. static int opl_sdl_paused; // Time offset (in us) due to the fact that callbacks // were previously paused. static uint64_t pause_offset; // OPL software emulator structure. static opl3_chip opl_chips[OPL_MAX_CHIPS]; static uint32_t opl_chip_keys[OPL_MAX_CHIPS]; static uint32_t opl_chip_timeouts[OPL_MAX_CHIPS]; static int opl_opl3mode; // Register number that was written. static int register_num = 0; // Timers; DBOPL does not do timer stuff itself. static opl_timer_t timer1 = { 12500, 0, 0, 0 }; static opl_timer_t timer2 = { 3125, 0, 0, 0 }; static int mixing_channels; // Advance time by the specified number of samples, invoking any // callback functions as appropriate. static void AdvanceTime(unsigned int nsamples) { opl_callback_t callback; void *callback_data; uint64_t us; // Advance time. us = ((uint64_t) nsamples * OPL_SECOND) / OPL_SAMPLE_RATE; 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; } callback(callback_data); } } // When a chip is re-activated after being idle, we need to bring its // internal global timers back into synch with the main chip to avoid // possible beating artifacts static void ResyncChip (int chip) { // Find an active chip to synchronize with; will usually be chip 0 int sync = -1; for (int c = 0; c < OPL_MAX_CHIPS; ++c) { if (opl_chip_timeouts[c] < OPL_CHIP_TIMEOUT) { sync = c; break; } } if (sync >= 0) { // Synchronize the LFOs opl_chips[chip].vibpos = opl_chips[sync].vibpos; opl_chips[chip].tremolopos = opl_chips[sync].tremolopos; opl_chips[chip].timer = opl_chips[sync].timer; // Synchronize the envelope clock opl_chips[chip].eg_state = opl_chips[sync].eg_state; opl_chips[chip].eg_timer = opl_chips[sync].eg_timer; opl_chips[chip].eg_timerrem = opl_chips[sync].eg_timerrem; } } // Callback function to fill a new sound buffer: int OPL_FillBuffer(byte *buffer, int buffer_samples) { unsigned int filled; // Repeatedly call the OPL emulator update function until the buffer is // full. filled = 0; while (filled < buffer_samples) { uint64_t next_callback_time; uint64_t 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_sdl_paused || OPL_Queue_IsEmpty(callback_queue)) { nsamples = buffer_samples - filled; } else { next_callback_time = OPL_Queue_Peek(callback_queue) + pause_offset; nsamples = (next_callback_time - current_time) * OPL_SAMPLE_RATE; nsamples = (nsamples + OPL_SECOND - 1) / OPL_SECOND; if (nsamples > buffer_samples - filled) { nsamples = buffer_samples - filled; } } // Add emulator output to buffer. Bit16s *cursor = (Bit16s *)(buffer + filled * 4); for (int s = 0; s < nsamples; ++s) { // Check for chip activations before we generate the sample for (int c = 0; c < num_opl_chips; ++c) { // Reset chip timeout if any channels are active if (opl_chip_keys[c]) { // Resync is necessary if the chip was idle if (opl_chip_timeouts[c] >= OPL_CHIP_TIMEOUT) ResyncChip(c); opl_chip_timeouts[c] = 0; } } // Generate a sample from each chip and mix them Bit32s mix[2] = {0, 0}; for (int c = 0; c < num_opl_chips; ++c) { // Run the chip if it's active or if it has pending register writes if (opl_chip_timeouts[c] < OPL_CHIP_TIMEOUT || (opl_chips[c].writebuf[opl_chips[c].writebuf_cur].reg & 0x200)) { Bit16s sample[2]; OPL3_Generate(&opl_chips[c], sample); mix[0] += sample[0]; mix[1] += sample[1]; // Reset chip timeout if it breaks the silence threshold if (MAX(abs(sample[0]), abs(sample[1])) > OPL_SILENCE_THRESHOLD) opl_chip_timeouts[c] = 0; else opl_chip_timeouts[c]++; } } cursor[0] = BETWEEN(-32768, 32767, mix[0]); cursor[1] = BETWEEN(-32768, 32767, mix[1]); cursor += 2; } //OPL3_GenerateStream(&opl_chip, (Bit16s *)(buffer + filled * 4), nsamples); filled += nsamples; // Invoke callbacks for this point in time. AdvanceTime(nsamples); } return buffer_samples; } static void OPL_SDL_Shutdown(void) { OPL_Queue_Destroy(callback_queue); /* if (opl_chip != NULL) { OPLDestroy(opl_chip); opl_chip = NULL; } */ } static int OPL_SDL_Init(unsigned int port_base, int num_chips) { opl_sdl_paused = 0; pause_offset = 0; // Queue structure of callbacks to invoke. callback_queue = OPL_Queue_Create(); current_time = 0; // Get the mixer frequency, format and number of channels. // Only supports AUDIO_S16SYS mixing_channels = 2; // Create the emulator structure: for (int c = 0; c < num_opl_chips; ++c) OPL3_Reset(&opl_chips[c], OPL_SAMPLE_RATE); opl_opl3mode = 0; for (int c = 0; c < OPL_MAX_CHIPS; ++c) { opl_chip_keys[c] = 0; opl_chip_timeouts[c] = OPL_CHIP_TIMEOUT; } return 1; } static unsigned int OPL_SDL_PortRead(int chip, opl_port_t port) { unsigned int result = 0; if (port == OPL_REGISTER_PORT_OPL3) { return 0xff; } if (chip > 0) return result; if (timer1.enabled && current_time > timer1.expire_time) { result |= 0x80; // Either have expired result |= 0x40; // Timer 1 has expired } if (timer2.enabled && current_time > timer2.expire_time) { result |= 0x80; // Either have expired result |= 0x20; // Timer 2 has expired } return result; } static void OPLTimer_CalculateEndTime(opl_timer_t *timer) { int tics; // If the timer is enabled, calculate the time when the timer // will expire. if (timer->enabled) { tics = 0x100 - timer->value; timer->expire_time = current_time + ((uint64_t) tics * OPL_SECOND) / timer->rate; } } static void WriteRegister(int chip, unsigned int reg_num, unsigned int value) { switch (reg_num) { case OPL_REG_TIMER1: // Only allow timers on the first chip if (chip == 0) { timer1.value = value; OPLTimer_CalculateEndTime(&timer1); } break; case OPL_REG_TIMER2: // Only allow timers on the first chip if (chip == 0) { timer2.value = value; OPLTimer_CalculateEndTime(&timer2); } break; case OPL_REG_TIMER_CTRL: if (value & 0x80) { timer1.enabled = 0; timer2.enabled = 0; } else { if ((value & 0x40) == 0) { timer1.enabled = (value & 0x01) != 0; OPLTimer_CalculateEndTime(&timer1); } if ((value & 0x20) == 0) { timer2.enabled = (value & 0x02) != 0; OPLTimer_CalculateEndTime(&timer2); } } break; case OPL_REG_NEW: // Keep all chips synchronized with the first chip's opl3 mode if (chip == 0) { for (int c = 0; c < num_opl_chips; ++c) OPL3_WriteRegBuffered(&opl_chips[c], reg_num, value); opl_opl3mode = value & 0x01; } break; default: // Keep track of which channels are keyed-on so we know when the chip is in use if ((reg_num & 0xff) >= 0xb0 && (reg_num & 0xff) <= 0xb8) { uint32_t key_bit = 1 << (((reg_num & 0x100) ? 9 : 0) + (reg_num & 0xf)); if (value & (1 << 5)) opl_chip_keys[chip] |= key_bit; else opl_chip_keys[chip] &= ~key_bit; } OPL3_WriteRegBuffered(&opl_chips[chip], reg_num, value); break; } } static void OPL_SDL_PortWrite(int chip, opl_port_t port, unsigned int value) { if (port == OPL_REGISTER_PORT) { register_num = value; } else if (port == OPL_REGISTER_PORT_OPL3) { register_num = value | 0x100; } else if (port == OPL_DATA_PORT) { WriteRegister(chip, register_num, value); } } static void OPL_SDL_SetCallback(uint64_t us, opl_callback_t callback, void *data) { OPL_Queue_Push(callback_queue, callback, data, current_time - pause_offset + us); } static void OPL_SDL_ClearCallbacks(void) { OPL_Queue_Clear(callback_queue); } static void OPL_SDL_AdjustCallbacks(float factor) { OPL_Queue_AdjustCallbacks(callback_queue, current_time, factor); } opl_driver_t opl_sdl_driver = { "SDL", OPL_SDL_Init, OPL_SDL_Shutdown, OPL_SDL_PortRead, OPL_SDL_PortWrite, OPL_SDL_SetCallback, OPL_SDL_ClearCallbacks, OPL_SDL_AdjustCallbacks, };