ref: 3bde136c60ba10dc080bc1060a6c039f94d7d147
parent: 00f6a3e75bc1f396606c50e9b041befcd3a4aaea
author: Simon Howard <[email protected]>
date: Wed Oct 24 16:03:34 EDT 2018
opl: Use SDL_mixer post-mix hook for OPL output. Using Mix_HookMusic() to generate the OPL output stream works fine if you only ever play OPL music and don't want to use the normal music output functionality of SDL_mixer. However, now that it's possible to use substitute music packs with any music output type, this is no longer the case. Using Mix_HookMusic() disables normal output from eg. Ogg and FLAC playback. As an alternative, use the post-mix hook and mix the output from the OPL emulator there. Cleanup fix as part of #440.
--- a/opl/opl_sdl.c
+++ b/opl/opl_sdl.c
@@ -75,7 +75,7 @@
// Temporary mixing buffer used by the mixing callback.
-static int32_t *mix_buffer = NULL;
+static uint8_t *mix_buffer = NULL;
// Register number that was written.
@@ -155,36 +155,32 @@
// Call the OPL emulator code to fill the specified buffer.
-static void FillBuffer(int16_t *buffer, unsigned int nsamples)
+static void FillBuffer(uint8_t *buffer, unsigned int nsamples)
{
// This seems like a reasonable assumption. mix_buffer is
// 1 second long, which should always be much longer than the
// SDL mix buffer.
-
assert(nsamples < mixing_freq);
- OPL3_GenerateStream(&opl_chip, buffer, nsamples);
+ // OPL output is generated into temporary buffer and then mixed
+ // (to avoid overflows etc.)
+ OPL3_GenerateStream(&opl_chip, (Bit16s *) mix_buffer, nsamples);
+ SDL_MixAudioFormat(buffer, mix_buffer, AUDIO_S16SYS, nsamples * 4,
+ SDL_MIX_MAXVOLUME);
}
// Callback function to fill a new sound buffer:
-static void OPL_Mix_Callback(void *udata,
- Uint8 *byte_buffer,
- int buffer_bytes)
+static void OPL_Mix_Callback(void *udata, Uint8 *buffer, int len)
{
- int16_t *buffer;
- unsigned int buffer_len;
- unsigned int filled = 0;
+ unsigned int filled, buffer_samples;
- // 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.
+ filled = 0;
+ buffer_samples = len / 4;
- while (filled < buffer_len)
+ while (filled < buffer_samples)
{
uint64_t next_callback_time;
uint64_t nsamples;
@@ -197,7 +193,7 @@
if (opl_sdl_paused || OPL_Queue_IsEmpty(callback_queue))
{
- nsamples = buffer_len - filled;
+ nsamples = buffer_samples - filled;
}
else
{
@@ -206,9 +202,9 @@
nsamples = (next_callback_time - current_time) * mixing_freq;
nsamples = (nsamples + OPL_SECOND - 1) / OPL_SECOND;
- if (nsamples > buffer_len - filled)
+ if (nsamples > buffer_samples - filled)
{
- nsamples = buffer_len - filled;
+ nsamples = buffer_samples - filled;
}
}
@@ -216,7 +212,7 @@
// Add emulator output to buffer.
- FillBuffer(buffer + filled * 2, nsamples);
+ FillBuffer(buffer + filled * 4, nsamples);
filled += nsamples;
// Invoke callbacks for this point in time.
@@ -340,10 +336,9 @@
return 0;
}
- // Mix buffer:
+ // Mix buffer: four bytes per sample (16 bits * 2 channels):
+ mix_buffer = malloc(mixing_freq * 4);
- mix_buffer = malloc(mixing_freq * sizeof(uint32_t) * 2);
-
// Create the emulator structure:
OPL3_Reset(&opl_chip, mixing_freq);
@@ -352,8 +347,10 @@
callback_mutex = SDL_CreateMutex();
callback_queue_mutex = SDL_CreateMutex();
- // TODO: This should be music callback? or-?
- Mix_HookMusic(OPL_Mix_Callback, NULL);
+ // Set postmix that adds the OPL music. This is deliberately done
+ // as a postmix and not using Mix_HookMusic() as the latter disables
+ // normal SDL_mixer music mixing.
+ Mix_SetPostMix(OPL_Mix_Callback, NULL);
return 1;
}