From 7bc4f24c7c806d840a6cc4a9f8539cbe96b23256 Mon Sep 17 00:00:00 2001 From: Roman Fomin Date: Fri, 1 Mar 2024 18:22:04 +0700 Subject: [PATCH] fix Thread Sanitizer warnings, music modules refactoring (#1531) * Fix TSan warnings with Fluidsynth backend * Convert Fluidsynth and OPL modules to stream_module_t interface * Remove mutexes from OPL module * Remove I_OAL_HookMusic/callback * Move extern declarations to headers --- cmake/WoofSettings.cmake | 11 + opl/CMakeLists.txt | 2 +- opl/opl.c | 173 +--------------- opl/opl.h | 20 +- opl/opl_internal.h | 6 - opl/opl_sdl.c | 98 +-------- src/CMakeLists.txt | 2 +- src/i_flmusic.c | 160 ++++++--------- src/i_oalmusic.c | 153 ++++++-------- src/i_oalmusic.h | 27 --- src/i_oalstream.h | 10 +- src/i_oplmusic.c | 422 ++++++++++++++++----------------------- src/i_sndfile.c | 18 ++ src/i_sound.c | 200 ++++++++++++------- src/i_sound.h | 9 + src/i_winmusic.c | 21 +- src/i_xmp.c | 51 +++-- 17 files changed, 529 insertions(+), 854 deletions(-) delete mode 100644 src/i_oalmusic.h diff --git a/cmake/WoofSettings.cmake b/cmake/WoofSettings.cmake index d1456b15..41982754 100644 --- a/cmake/WoofSettings.cmake +++ b/cmake/WoofSettings.cmake @@ -99,6 +99,17 @@ if(ENABLE_ASAN) _checked_add_link_option(-fsanitize=address) endif() +option(ENABLE_TSAN "Enable TSan" OFF) +if(ENABLE_TSAN) + # Set -Werror to catch "argument unused during compilation" warnings. + # Also needs to be a link flag for test to pass. + set(CMAKE_REQUIRED_FLAGS "-Werror -fsanitize=thread") + _checked_add_compile_option(-g) + _checked_add_compile_option(-fsanitize=thread) + unset(CMAKE_REQUIRED_FLAGS) + _checked_add_link_option(-fsanitize=thread) +endif() + option(ENABLE_HARDENING "Enable hardening flags" OFF) if(ENABLE_HARDENING) _checked_add_compile_option(-fstack-protector-strong) diff --git a/opl/CMakeLists.txt b/opl/CMakeLists.txt index 672cb167..5d5797a0 100644 --- a/opl/CMakeLists.txt +++ b/opl/CMakeLists.txt @@ -12,4 +12,4 @@ target_woof_settings(opl) target_include_directories(opl INTERFACE "." PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/../" "../src/") -target_link_libraries(opl SDL2::SDL2) +target_link_libraries(opl) diff --git a/opl/opl.c b/opl/opl.c index 78b3d777..348cffbb 100644 --- a/opl/opl.c +++ b/opl/opl.c @@ -15,9 +15,8 @@ // OPL interface. // -#include "SDL.h" - #include +#include #include "m_io.h" #include "opl.h" @@ -34,7 +33,6 @@ static opl_driver_t *drivers[] = }; static opl_driver_t *driver = NULL; -static int init_stage_reg_writes = 1; unsigned int opl_sample_rate = 22050; @@ -48,8 +46,6 @@ unsigned int opl_sample_rate = 22050; static opl_init_result_t InitDriver(opl_driver_t *_driver, unsigned int port_base) { - opl_init_result_t result1, result2; - // Initialize the driver. if (!_driver->init_func(port_base)) @@ -63,23 +59,10 @@ static opl_init_result_t InitDriver(opl_driver_t *_driver, // (it's done twice, like how Doom does it). driver = _driver; - init_stage_reg_writes = 1; - - result1 = OPL_Detect(); - result2 = OPL_Detect(); - if (result1 == OPL_INIT_NONE || result2 == OPL_INIT_NONE) - { - printf("OPL_Init: No OPL detected using '%s' driver.\n", _driver->name); - _driver->shutdown_func(); - driver = NULL; - return OPL_INIT_NONE; - } - - init_stage_reg_writes = 0; printf("OPL_Init: Using driver '%s'.\n", driver->name); - return result2; + return OPL_INIT_OPL3; } // Find a driver automatically by trying each in the list. @@ -232,18 +215,7 @@ void OPL_WriteRegister(int reg, int value) for (i=0; i<6; ++i) { - // An oddity of the Doom OPL code: at startup initialization, - // the spacing here is performed by reading from the register - // port; after initialization, the data port is read, instead. - - if (init_stage_reg_writes) - { - OPL_ReadPort(OPL_REGISTER_PORT); - } - else - { - OPL_ReadPort(OPL_DATA_PORT); - } + OPL_ReadPort(OPL_DATA_PORT); } OPL_WritePort(OPL_DATA_PORT, value); @@ -257,66 +229,6 @@ void OPL_WriteRegister(int reg, int value) } } -// Detect the presence of an OPL chip - -opl_init_result_t OPL_Detect(void) -{ - int result1, result2; - int i; - - // Reset both timers: - OPL_WriteRegister(OPL_REG_TIMER_CTRL, 0x60); - - // Enable interrupts: - OPL_WriteRegister(OPL_REG_TIMER_CTRL, 0x80); - - // Read status - result1 = OPL_ReadStatus(); - - // Set timer: - OPL_WriteRegister(OPL_REG_TIMER1, 0xff); - - // Start timer 1: - OPL_WriteRegister(OPL_REG_TIMER_CTRL, 0x21); - - // Wait for 80 microseconds - // This is how Doom does it: - - for (i=0; i<200; ++i) - { - OPL_ReadStatus(); - } - - OPL_Delay(1 * OPL_MS); - - // Read status - result2 = OPL_ReadStatus(); - - // Reset both timers: - OPL_WriteRegister(OPL_REG_TIMER_CTRL, 0x60); - - // Enable interrupts: - OPL_WriteRegister(OPL_REG_TIMER_CTRL, 0x80); - - if ((result1 & 0xe0) == 0x00 && (result2 & 0xe0) == 0xc0) - { - result1 = OPL_ReadPort(OPL_REGISTER_PORT); - result2 = OPL_ReadPort(OPL_REGISTER_PORT_OPL3); - if (result1 == 0x00) - { - return OPL_INIT_OPL3; - } - else - { - return OPL_INIT_OPL2; - } - } - else - { - return OPL_INIT_NONE; - } -} - // Initialize registers on startup void OPL_InitRegisters(int opl3) @@ -414,85 +326,6 @@ void OPL_ClearCallbacks(void) } } -void OPL_Lock(void) -{ - if (driver != NULL) - { - driver->lock_func(); - } -} - -void OPL_Unlock(void) -{ - if (driver != NULL) - { - driver->unlock_func(); - } -} - -typedef struct -{ - int finished; - - SDL_mutex *mutex; - SDL_cond *cond; -} delay_data_t; - -static void DelayCallback(void *_delay_data) -{ - delay_data_t *delay_data = _delay_data; - - SDL_LockMutex(delay_data->mutex); - delay_data->finished = 1; - - SDL_CondSignal(delay_data->cond); - - SDL_UnlockMutex(delay_data->mutex); -} - -void OPL_Delay(uint64_t us) -{ - delay_data_t delay_data; - - if (driver == NULL) - { - return; - } - - // Create a callback that will signal this thread after the - // specified time. - - delay_data.finished = 0; - delay_data.mutex = SDL_CreateMutex(); - delay_data.cond = SDL_CreateCond(); - - OPL_SetCallback(us, DelayCallback, &delay_data); - - // Wait until the callback is invoked. - - SDL_LockMutex(delay_data.mutex); - - while (!delay_data.finished) - { - SDL_CondWait(delay_data.cond, delay_data.mutex); - } - - SDL_UnlockMutex(delay_data.mutex); - - // Clean up. - - SDL_DestroyMutex(delay_data.mutex); - SDL_DestroyCond(delay_data.cond); -} - -void OPL_SetPaused(int paused) -{ - if (driver != NULL) - { - driver->set_paused_func(paused); - } -} - void OPL_AdjustCallbacks(float value) { if (driver != NULL) diff --git a/opl/opl.h b/opl/opl.h index 2c9fc728..a8f3b0ac 100644 --- a/opl/opl.h +++ b/opl/opl.h @@ -104,11 +104,6 @@ unsigned int OPL_ReadStatus(void); void OPL_WriteRegister(int reg, int value); -// Perform a detection sequence to determine that an -// OPL chip is present. - -opl_init_result_t OPL_Detect(void); - // Initialize all registers, performed on startup. void OPL_InitRegisters(int opl3); @@ -131,22 +126,11 @@ void OPL_AdjustCallbacks(float factor); void OPL_ClearCallbacks(void); -// Begin critical section, during which, OPL callbacks will not be -// invoked. - -void OPL_Lock(void); - -// End critical section. - -void OPL_Unlock(void); - -// Block until the specified number of microseconds have elapsed. - -void OPL_Delay(uint64_t us); - // Pause the OPL callbacks. void OPL_SetPaused(int paused); +int OPL_FillBuffer(unsigned char *buffer, int buffer_samples); + #endif diff --git a/opl/opl_internal.h b/opl/opl_internal.h index c67dbff4..f959395f 100644 --- a/opl/opl_internal.h +++ b/opl/opl_internal.h @@ -29,9 +29,6 @@ typedef void (*opl_set_callback_func)(uint64_t us, opl_callback_t callback, void *data); 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 @@ -44,9 +41,6 @@ typedef struct opl_write_port_func write_port_func; opl_set_callback_func set_callback_func; opl_clear_callbacks_func clear_callbacks_func; - 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; diff --git a/opl/opl_sdl.c b/opl/opl_sdl.c index 1005a8fa..6a0fd11a 100644 --- a/opl/opl_sdl.c +++ b/opl/opl_sdl.c @@ -15,10 +15,7 @@ // OPL SDL interface. // -#include "SDL.h" - #include "doomtype.h" -#include "i_oalmusic.h" #include "opl.h" #include "opl3.h" #include "opl_internal.h" @@ -34,19 +31,10 @@ typedef struct uint64_t expire_time; // Calculated time that timer will expire. } opl_timer_t; -// When the callback mutex is locked using OPL_Lock, callback functions -// are not invoked. - -static SDL_mutex *callback_mutex = NULL; - // Queue of callbacks waiting to be invoked. static opl_callback_queue_t *callback_queue; -// Mutex used to control access to the callback queue. - -static SDL_mutex *callback_queue_mutex = NULL; - // Current time, in us since startup: static uint64_t current_time; @@ -85,8 +73,6 @@ static void AdvanceTime(unsigned int nsamples) void *callback_data; uint64_t us; - SDL_LockMutex(callback_queue_mutex); - // Advance time. us = ((uint64_t) nsamples * OPL_SECOND) / mixing_freq; @@ -110,35 +96,13 @@ static void AdvanceTime(unsigned int nsamples) 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); -} - -// Call the OPL emulator code to fill the specified buffer. - -static void FillBuffer(uint8_t *buffer, unsigned int nsamples) -{ - OPL3_GenerateStream(&opl_chip, (Bit16s *) buffer, nsamples); } // Callback function to fill a new sound buffer: -static int OPL_Callback(byte *buffer, int buffer_samples) +int OPL_FillBuffer(byte *buffer, int buffer_samples) { unsigned int filled; @@ -151,8 +115,6 @@ static int OPL_Callback(byte *buffer, int buffer_samples) 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. @@ -174,11 +136,8 @@ static int OPL_Callback(byte *buffer, int buffer_samples) } } - SDL_UnlockMutex(callback_queue_mutex); - // Add emulator output to buffer. - - FillBuffer(buffer + filled * 4, nsamples); + OPL3_GenerateStream(&opl_chip, (Bit16s *)(buffer + filled * 4), nsamples); filled += nsamples; // Invoke callbacks for this point in time. @@ -191,8 +150,6 @@ static int OPL_Callback(byte *buffer, int buffer_samples) static void OPL_SDL_Shutdown(void) { - I_OAL_HookMusic(NULL); - OPL_Queue_Destroy(callback_queue); /* @@ -202,22 +159,8 @@ static void OPL_SDL_Shutdown(void) opl_chip = NULL; } */ - - if (callback_mutex != NULL) - { - SDL_DestroyMutex(callback_mutex); - callback_mutex = NULL; - } - - if (callback_queue_mutex != NULL) - { - SDL_DestroyMutex(callback_queue_mutex); - callback_queue_mutex = NULL; - } } -int opl_gain = 200; - static int OPL_SDL_Init(unsigned int port_base) { opl_sdl_paused = 0; @@ -239,19 +182,6 @@ static int OPL_SDL_Init(unsigned int port_base) OPL3_Reset(&opl_chip, mixing_freq); opl_opl3mode = 0; - callback_mutex = SDL_CreateMutex(); - callback_queue_mutex = SDL_CreateMutex(); - - if (!I_OAL_HookMusic(OPL_Callback)) - { - OPL_Queue_Destroy(callback_queue); - SDL_DestroyMutex(callback_mutex); - SDL_DestroyMutex(callback_queue_mutex); - return 0; - } - - I_OAL_SetGain((float)opl_gain / 100.0f); - return 1; } @@ -359,39 +289,18 @@ static void OPL_SDL_PortWrite(opl_port_t port, unsigned int value) static void OPL_SDL_SetCallback(uint64_t us, opl_callback_t callback, void *data) { - SDL_LockMutex(callback_queue_mutex); OPL_Queue_Push(callback_queue, callback, data, current_time - pause_offset + us); - SDL_UnlockMutex(callback_queue_mutex); } static void OPL_SDL_ClearCallbacks(void) { - SDL_LockMutex(callback_queue_mutex); OPL_Queue_Clear(callback_queue); - SDL_UnlockMutex(callback_queue_mutex); -} - -static void OPL_SDL_Lock(void) -{ - SDL_LockMutex(callback_mutex); -} - -static void OPL_SDL_Unlock(void) -{ - SDL_UnlockMutex(callback_mutex); -} - -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 = @@ -403,9 +312,6 @@ opl_driver_t opl_sdl_driver = OPL_SDL_PortWrite, OPL_SDL_SetCallback, OPL_SDL_ClearCallbacks, - OPL_SDL_Lock, - OPL_SDL_Unlock, - OPL_SDL_SetPaused, OPL_SDL_AdjustCallbacks, }; diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 52263e30..c2659f26 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -38,7 +38,7 @@ set(WOOF_SOURCES i_input.c i_input.h i_main.c i_mbfsound.c - i_oalmusic.c i_oalmusic.h + i_oalmusic.c i_oalsound.c i_oalsound.h i_oalstream.h i_oplmusic.c diff --git a/src/i_flmusic.c b/src/i_flmusic.c index 6715c8b9..02821d25 100644 --- a/src/i_flmusic.c +++ b/src/i_flmusic.c @@ -21,6 +21,7 @@ #include #include "config.h" +#include "i_oalstream.h" #if (FLUIDSYNTH_VERSION_MAJOR < 2 || (FLUIDSYNTH_VERSION_MAJOR == 2 && FLUIDSYNTH_VERSION_MINOR < 2)) typedef int fluid_int_t; @@ -32,7 +33,6 @@ #include "d_iwad.h" // [FG] D_DoomExeDir() #include "doomtype.h" #include "i_glob.h" -#include "i_oalmusic.h" #include "i_printf.h" #include "i_sound.h" #include "m_array.h" @@ -46,7 +46,6 @@ const char *soundfont_path = ""; char *soundfont_dir = ""; boolean mus_chorus; boolean mus_reverb; -int mus_gain = 100; static fluid_synth_t *synth = NULL; static fluid_settings_t *settings = NULL; @@ -54,20 +53,6 @@ static fluid_player_t *player = NULL; static const char **soundfonts = NULL; -static int FL_Callback(byte *buffer, int buffer_samples) -{ - int result; - - result = fluid_synth_write_s16(synth, buffer_samples, buffer, 0, 2, buffer, 1, 2); - - if (result != FLUID_OK) - { - I_Printf(VB_ERROR, "FL_Callback: Error generating FluidSynth audio"); - } - - return buffer_samples; -} - // Load SNDFONT lump static byte *lump; @@ -211,7 +196,7 @@ static void I_FL_Log_Debug(int level, const char *message, void *data) I_Printf(VB_DEBUG, "%s", message); } -static boolean I_FL_InitMusic(int device) +static boolean I_FL_InitStream(int device) { int sf_id; int lumpnum; @@ -308,56 +293,16 @@ static boolean I_FL_InitMusic(int device) return false; } + fluid_synth_set_gain(synth, 1.0f); + I_Printf(VB_INFO, "FluidSynth Init: Using '%s'.", lumpnum >= 0 ? "SNDFONT lump" : soundfont_path); return true; } -static void I_FL_SetMusicVolume(int volume) -{ - if (synth) - { - // FluidSynth's default is 0.2. Make 1.0 the maximum. - // 0 -- 0.2 -- 10.0 - fluid_synth_set_gain(synth, ((float)volume / 15) * ((float)mus_gain / 100)); - } -} - -static void I_FL_PauseSong(void *handle) -{ - if (player) - { - I_OAL_HookMusic(NULL); - } -} - -static void I_FL_ResumeSong(void *handle) -{ - if (player) - { - I_OAL_HookMusic(FL_Callback); - } -} - -static void I_FL_PlaySong(void *handle, boolean looping) -{ - if (player) - { - fluid_player_set_loop(player, looping ? -1 : 1); - fluid_player_play(player); - } -} - -static void I_FL_StopSong(void *handle) -{ - if (player) - { - fluid_player_stop(player); - } -} - -static void *I_FL_RegisterSong(void *data, int len) +static boolean I_FL_OpenStream(void *data, ALsizei size, ALenum *format, + ALsizei *freq, ALsizei *frame_size) { int result = FLUID_FAILED; @@ -367,12 +312,12 @@ static void *I_FL_RegisterSong(void *data, int len) { I_Printf(VB_ERROR, "I_FL_InitMusic: FluidSynth failed to initialize player."); - return NULL; + return false; } - if (IsMid(data, len)) + if (IsMid(data, size)) { - result = fluid_player_add_mem(player, data, len); + result = fluid_player_add_mem(player, data, size); } else { @@ -382,7 +327,7 @@ static void *I_FL_RegisterSong(void *data, int len) void *outbuf; size_t outbuf_len; - instream = mem_fopen_read(data, len); + instream = mem_fopen_read(data, size); outstream = mem_fopen_write(); if (mus2mid(instream, outstream) == 0) @@ -400,38 +345,56 @@ static void *I_FL_RegisterSong(void *data, int len) delete_fluid_player(player); player = NULL; I_Printf(VB_ERROR, "I_FL_RegisterSong: Failed to load in-memory song."); - return NULL; + return false; } - if (!I_OAL_HookMusic(FL_Callback)) - { - delete_fluid_player(player); - player = NULL; - return NULL; - } + *format = AL_FORMAT_STEREO16; + *freq = SND_SAMPLERATE; + *frame_size = 2 * sizeof(short); - return (void *)1; + return true; } -static void I_FL_UnRegisterSong(void *handle) +static int I_FL_FillStream(byte *buffer, int buffer_samples) +{ + int result; + + result = fluid_synth_write_s16(synth, buffer_samples, buffer, 0, 2, buffer, 1, 2); + + if (result != FLUID_OK) + { + I_Printf(VB_ERROR, "FL_Callback: Error generating FluidSynth audio"); + } + + return buffer_samples; +} + + +static void I_FL_PlayStream(boolean looping) { if (player) { + fluid_player_set_loop(player, looping ? -1 : 1); + fluid_player_play(player); + } +} + +static void I_FL_CloseStream(void) +{ + if (player) + { + fluid_player_stop(player); + fluid_synth_program_reset(synth); fluid_synth_system_reset(synth); - I_OAL_HookMusic(NULL); - delete_fluid_player(player); player = NULL; } } -static void I_FL_ShutdownMusic(void) +static void I_FL_ShutdownStream(void) { - I_FL_StopSong(NULL); - I_FL_UnRegisterSong(NULL); - FreeSynthAndSettings(); } @@ -439,9 +402,17 @@ static void I_FL_ShutdownMusic(void) static const char **I_FL_DeviceList(int *current_device) { - const char **devices = NULL; + static const char **devices = NULL; - *current_device = 0; + if (devices) + { + return devices; + } + + if (current_device) + { + *current_device = 0; + } if (W_CheckNumForName("SNDFONT") >= 0) { @@ -461,7 +432,7 @@ static const char **I_FL_DeviceList(int *current_device) array_push(devices, M_StringJoin("FluidSynth (", name, ")", NULL)); - if (!strcasecmp(soundfonts[i], soundfont_path)) + if (current_device && !strcasecmp(soundfonts[i], soundfont_path)) { *current_device = i; } @@ -470,22 +441,13 @@ static const char **I_FL_DeviceList(int *current_device) return devices; } -static void I_FL_UpdateMusic(void) +stream_module_t stream_fl_module = { - ; -} - -music_module_t music_fl_module = -{ - I_FL_InitMusic, - I_FL_ShutdownMusic, - I_FL_SetMusicVolume, - I_FL_PauseSong, - I_FL_ResumeSong, - I_FL_RegisterSong, - I_FL_PlaySong, - I_FL_UpdateMusic, - I_FL_StopSong, - I_FL_UnRegisterSong, + I_FL_InitStream, + I_FL_OpenStream, + I_FL_FillStream, + I_FL_PlayStream, + I_FL_CloseStream, + I_FL_ShutdownStream, I_FL_DeviceList, }; diff --git a/src/i_oalmusic.c b/src/i_oalmusic.c index 200fdab2..9c421bb4 100644 --- a/src/i_oalmusic.c +++ b/src/i_oalmusic.c @@ -22,7 +22,6 @@ #include #include "doomtype.h" -#include "i_oalmusic.h" #include "i_oalstream.h" #include "i_printf.h" #include "i_sound.h" @@ -33,10 +32,7 @@ #define NUM_BUFFERS 4 #define BUFFER_SAMPLES 4096 -extern stream_module_t stream_snd_module; -extern stream_module_t stream_xmp_module; - -stream_module_t *stream_modules[] = +static stream_module_t *stream_modules[] = { &stream_snd_module, #if defined(HAVE_LIBXMP) @@ -66,10 +62,10 @@ static stream_player_t player; static SDL_Thread *player_thread_handle; static int player_thread_running; -static callback_func_t callback; - static boolean music_initialized; +static SDL_mutex *music_lock; + static boolean UpdatePlayer(void) { ALint processed, state; @@ -95,14 +91,7 @@ static boolean UpdatePlayer(void) // Read the next chunk of data, refill the buffer, and queue it back on // the source. - if (callback) - { - frames = callback(player.data, BUFFER_SAMPLES); - } - else - { - frames = active_module->I_FillStream(player.data, BUFFER_SAMPLES); - } + frames = active_module->I_FillStream(player.data, BUFFER_SAMPLES); if (frames > 0) { @@ -150,10 +139,7 @@ static boolean StartPlayer(void) alSourceRewind(player.source); alSourcei(player.source, AL_BUFFER, 0); - if (!callback) - { - active_module->I_PlayStream(player.looping); - } + active_module->I_PlayStream(player.looping); // Fill the buffer queue for (i = 0; i < NUM_BUFFERS; i++) @@ -162,14 +148,7 @@ static boolean StartPlayer(void) ALsizei size; // Get some data to give it to the buffer - if (callback) - { - frames = callback(player.data, BUFFER_SAMPLES); - } - else - { - frames = active_module->I_FillStream(player.data, BUFFER_SAMPLES); - } + frames = active_module->I_FillStream(player.data, BUFFER_SAMPLES); if (frames < 1) break; @@ -196,13 +175,20 @@ static int PlayerThread(void *unused) StartPlayer(); - while (player_thread_running) + while (true) { - boolean result; + boolean keep_going; - result = UpdatePlayer(); + if (!UpdatePlayer()) + { + break; + } - if (result == false) + SDL_LockMutex(music_lock); + keep_going = player_thread_running; + SDL_UnlockMutex(music_lock); + + if (!keep_going) { break; } @@ -243,17 +229,36 @@ static boolean I_OAL_InitMusic(int device) alSourcei(player.source, AL_SOURCE_SPATIALIZE_SOFT, AL_FALSE); } + for (int i = 0; i < arrlen(stream_modules); ++i) + { + stream_modules[i]->I_InitStream(0); + } + music_initialized = true; return true; } +int mus_gain = 100; +int opl_gain = 200; + static void I_OAL_SetMusicVolume(int volume) { if (!music_initialized) return; - alSourcef(player.source, AL_GAIN, (ALfloat)volume / 15.0f); + ALfloat gain = (ALfloat)volume / 15.0f; + + if (active_module == &stream_opl_module) + { + gain *= (ALfloat)opl_gain / 100.0f; + } + else if (active_module == &stream_fl_module) + { + gain *= (ALfloat)mus_gain / 100.0f; + } + + alSourcef(player.source, AL_GAIN, gain); } static void I_OAL_PauseSong(void *handle) @@ -286,6 +291,8 @@ static void I_OAL_PlaySong(void *handle, boolean looping) return; } + music_lock = SDL_CreateMutex(); + player_thread_running = true; player_thread_handle = SDL_CreateThread(PlayerThread, NULL, NULL); if (player_thread_handle == NULL) @@ -297,34 +304,23 @@ static void I_OAL_PlaySong(void *handle, boolean looping) static void I_OAL_StopSong(void *handle) { - ALsizei processed; - - if (!music_initialized) + if (!music_initialized || !player_thread_running) return; - if (!player_thread_running) - { - return; - } - alSourceStop(player.source); + SDL_LockMutex(music_lock); player_thread_running = false; - SDL_WaitThread(player_thread_handle, NULL); + SDL_UnlockMutex(music_lock); - alGetSourcei(player.source, AL_BUFFERS_PROCESSED, &processed); - if (processed > 0) - { - ALuint* al_buf = malloc(processed * sizeof(*al_buf)); - alSourceUnqueueBuffers(player.source, processed, al_buf); - free(al_buf); - } - alSourcei(player.source, AL_BUFFER, 0); + SDL_WaitThread(player_thread_handle, NULL); if (alGetError() != AL_NO_ERROR) { I_Printf(VB_ERROR, "I_OAL_StopSong: Error stopping playback."); } + + SDL_DestroyMutex(music_lock); } static void I_OAL_UnRegisterSong(void *handle) @@ -332,10 +328,7 @@ static void I_OAL_UnRegisterSong(void *handle) if (!music_initialized) return; - if (!callback) - { - active_module->I_CloseStream(); - } + active_module->I_CloseStream(); if (player.data) { @@ -352,6 +345,12 @@ static void I_OAL_ShutdownMusic(void) I_OAL_StopSong(NULL); I_OAL_UnRegisterSong(NULL); + if (midi_stream_module) + { + midi_stream_module->I_ShutdownStream(); + } + active_module->I_ShutdownStream(); + alDeleteSources(1, &player.source); alDeleteBuffers(NUM_BUFFERS, player.buffers); if (alGetError() != AL_NO_ERROR) @@ -373,6 +372,18 @@ static void *I_OAL_RegisterSong(void *data, int len) if (!music_initialized) return NULL; + if (IsMid(data, len) || IsMus(data, len)) + { + if (midi_stream_module && + midi_stream_module->I_OpenStream(data, len, &player.format, + &player.freq, &player.frame_size)) + { + active_module = midi_stream_module; + return (void *)1; + } + return NULL; + } + for (i = 0; i < arrlen(stream_modules); ++i) { if (stream_modules[i]->I_OpenStream(data, len, &player.format, @@ -388,45 +399,9 @@ static void *I_OAL_RegisterSong(void *data, int len) static const char **I_OAL_DeviceList(int *current_device) { - *current_device = 0; return NULL; } -void I_OAL_SetGain(float gain) -{ - if (!music_initialized) - return; - - alSourcef(player.source, AL_GAIN, (ALfloat)gain); -} - -boolean I_OAL_HookMusic(callback_func_t callback_func) -{ - if (!music_initialized) - return false; - - if (callback_func) - { - callback = callback_func; - - player.format = AL_FORMAT_STEREO16; - player.freq = SND_SAMPLERATE; - player.frame_size = 2 * sizeof(short); - - I_OAL_SetGain(1.0f); - I_OAL_PlaySong(NULL, false); - } - else - { - I_OAL_StopSong(NULL); - I_OAL_UnRegisterSong(NULL); - - callback = NULL; - } - - return true; -} - static void I_OAL_UpdateMusic(void) { ; diff --git a/src/i_oalmusic.h b/src/i_oalmusic.h deleted file mode 100644 index cf3fe471..00000000 --- a/src/i_oalmusic.h +++ /dev/null @@ -1,27 +0,0 @@ -// -// Copyright(C) 2023 Roman Fomin -// -// 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: -// - -#ifndef __I_OALMUSIC__ -#define __I_OALMUSIC__ - -#include "doomtype.h" - -typedef int (*callback_func_t)(byte *buffer, int buffer_samples); - -boolean I_OAL_HookMusic(callback_func_t callback_func); -void I_OAL_SetGain(float gain); - -#endif diff --git a/src/i_oalstream.h b/src/i_oalstream.h index ea54e7be..8e09b331 100644 --- a/src/i_oalstream.h +++ b/src/i_oalstream.h @@ -21,13 +21,21 @@ #include "doomtype.h" -typedef struct +typedef struct stream_module_s { + boolean (*I_InitStream)(int device); boolean (*I_OpenStream)(void *data, ALsizei size, ALenum *format, ALsizei *freq, ALsizei *frame_size); int (*I_FillStream)(byte *data, int frames); void (*I_PlayStream)(boolean looping); void (*I_CloseStream)(void); + void (*I_ShutdownStream)(void); + const char **(*I_DeviceList)(int *current_device); } stream_module_t; +extern stream_module_t stream_opl_module; +extern stream_module_t stream_fl_module; +extern stream_module_t stream_snd_module; +extern stream_module_t stream_xmp_module; + #endif diff --git a/src/i_oplmusic.c b/src/i_oplmusic.c index 11d2695c..61d83e88 100644 --- a/src/i_oplmusic.c +++ b/src/i_oplmusic.c @@ -22,6 +22,7 @@ #include "../opl/opl.h" #include "doomtype.h" +#include "i_oalstream.h" #include "i_printf.h" #include "i_sound.h" #include "m_array.h" @@ -652,13 +653,6 @@ static void I_OPL_SetMusicVolume(int volume) { unsigned int i; - volume = volume * 127 / 15; // [FG] adjust volume - - if (current_music_volume == volume) - { - return; - } - // Internal state variable. current_music_volume = volume; @@ -1481,234 +1475,7 @@ static void StartTrack(midi_file_t *file, unsigned int track_num) ScheduleTrack(track); } -// Start playing a mid - -static void I_OPL_PlaySong(void *handle, boolean looping) -{ - midi_file_t *file; - unsigned int i; - - if (!music_initialized || handle == NULL) - { - return; - } - - file = handle; - - // Allocate track data. - - tracks = malloc(MIDI_NumTracks(file) * sizeof(opl_track_data_t)); - - num_tracks = MIDI_NumTracks(file); - running_tracks = num_tracks; - song_looping = looping; - - ticks_per_beat = MIDI_GetFileTimeDivision(file); - - // Default is 120 bpm. - // TODO: this is wrong - - us_per_beat = 500 * 1000; - - start_music_volume = current_music_volume; - - for (i = 0; i < num_tracks; ++i) - { - StartTrack(file, i); - } - - for (i = 0; i < MIDI_CHANNELS_PER_TRACK; ++i) - { - InitChannel(&channels[i]); - } - - // If the music was previously paused, it needs to be unpaused; playing - // a new song implies that we turn off pause. This matches vanilla - // behavior of the DMX library, and some of the higher-level code in - // s_sound.c relies on this. - OPL_SetPaused(0); -} - -static void I_OPL_PauseSong(void *handle) -{ - unsigned int i; - - if (!music_initialized) - { - return; - } - - // Pause OPL callbacks. - - OPL_SetPaused(1); - - // Turn off all main instrument voices (not percussion). - // This is what Vanilla does. - - for (i = 0; i < num_opl_voices; ++i) - { - if (voices[i].channel != NULL - && voices[i].current_instr < percussion_instrs) - { - VoiceKeyOff(&voices[i]); - } - } -} - -static void I_OPL_ResumeSong(void *handle) -{ - if (!music_initialized) - { - return; - } - - OPL_SetPaused(0); -} - -static void I_OPL_StopSong(void *handle) -{ - unsigned int i; - - if (!music_initialized) - { - return; - } - - OPL_Lock(); - - // Stop all playback. - - OPL_ClearCallbacks(); - - // Free all voices. - - for (i = 0; i < MIDI_CHANNELS_PER_TRACK; ++i) - { - AllNotesOff(&channels[i], 0); - } - - // Free all track data. - - for (i = 0; i < num_tracks; ++i) - { - MIDI_FreeIterator(tracks[i].iter); - } - - free(tracks); - - tracks = NULL; - num_tracks = 0; - - OPL_Unlock(); -} - -static void I_OPL_UnRegisterSong(void *handle) -{ - if (!music_initialized) - { - return; - } - - if (handle != NULL) - { - MIDI_FreeFile(handle); - } -} - -static boolean OPL_InitMusic(void); - -static void *I_OPL_RegisterSong(void *data, int len) -{ - midi_file_t *result; - - if (!music_initialized) - { - OPL_InitMusic(); - } - - // MUS files begin with "MUS" - // Reject anything which doesnt have this signature - - // [crispy] remove MID file size limit - if (IsMid(data, len) /* && len < MAXMIDLENGTH */) - { - result = MIDI_LoadFile(data, len); - } - else - { - // Assume a MUS file and try to convert - MEMFILE *instream; - MEMFILE *outstream; - void *outbuf; - size_t outbuf_len; - - instream = mem_fopen_read(data, len); - outstream = mem_fopen_write(); - - if (mus2mid(instream, outstream) == 0) - { - mem_get_buf(outstream, &outbuf, &outbuf_len); - result = MIDI_LoadFile(outbuf, outbuf_len); - } - else - { - result = NULL; - } - - mem_fclose(instream); - mem_fclose(outstream); - } - - if (result == NULL) - { - I_Printf(VB_ERROR, "I_OPL_RegisterSong: Failed to load MID."); - } - - return result; -} - -#if 0 -// Is the song playing? - -static boolean I_OPL_MusicIsPlaying(void) -{ - if (!music_initialized) - { - return false; - } - - return num_tracks > 0; -} -#endif - -// Shutdown music - -static void I_OPL_ShutdownMusic(void) -{ - if (music_initialized) - { - // Stop currently-playing track, if there is one: - - I_OPL_StopSong(NULL); - - OPL_Shutdown(); - - // Release GENMIDI lump - - Z_ChangeTag(lump, PU_CACHE); - - music_initialized = false; - } -} - -// Initialize music subsystem - -static boolean I_OPL_InitMusic(int device) -{ - return true; -} - -static boolean OPL_InitMusic(void) +static boolean I_OPL_InitStream(int device) { char *dmxoption; opl_init_result_t chip_type; @@ -1766,30 +1533,179 @@ static boolean OPL_InitMusic(void) return true; } +static midi_file_t *midifile; + +static boolean I_OPL_OpenStream(void *data, ALsizei size, ALenum *format, + ALsizei *freq, ALsizei *frame_size) +{ + // [crispy] remove MID file size limit + if (IsMid(data, size) /* && size < MAXMIDLENGTH */) + { + midifile = MIDI_LoadFile(data, size); + } + else + { + // Assume a MUS file and try to convert + MEMFILE *instream; + MEMFILE *outstream; + void *outbuf; + size_t outbuf_len; + + instream = mem_fopen_read(data, size); + outstream = mem_fopen_write(); + + if (mus2mid(instream, outstream) == 0) + { + mem_get_buf(outstream, &outbuf, &outbuf_len); + midifile = MIDI_LoadFile(outbuf, outbuf_len); + } + else + { + midifile = NULL; + } + + mem_fclose(instream); + mem_fclose(outstream); + } + + if (midifile == NULL) + { + I_Printf(VB_ERROR, "I_OPL_RegisterSong: Failed to load MID."); + return false; + } + + *format = AL_FORMAT_STEREO16; + *freq = SND_SAMPLERATE; + *frame_size = 2 * sizeof(short); + + return true; +} + +// Start playing a mid + +static void I_OPL_PlayStream(boolean looping) +{ + unsigned int i; + + if (!music_initialized) + { + return; + } + + I_OPL_SetMusicVolume(127); + + // Allocate track data. + + tracks = malloc(MIDI_NumTracks(midifile) * sizeof(opl_track_data_t)); + + num_tracks = MIDI_NumTracks(midifile); + running_tracks = num_tracks; + song_looping = looping; + + ticks_per_beat = MIDI_GetFileTimeDivision(midifile); + + // Default is 120 bpm. + // TODO: this is wrong + + us_per_beat = 500 * 1000; + + start_music_volume = current_music_volume; + + for (i = 0; i < num_tracks; ++i) + { + StartTrack(midifile, i); + } + + for (i = 0; i < MIDI_CHANNELS_PER_TRACK; ++i) + { + InitChannel(&channels[i]); + } +} + +static int I_OPL_FillStream(byte *buffer, int buffer_samples) +{ + return OPL_FillBuffer(buffer, buffer_samples); +} + +static void I_OPL_CloseStream(void) +{ + unsigned int i; + + if (!music_initialized) + { + return; + } + + // Stop all playback. + + OPL_ClearCallbacks(); + + // Free all voices. + + for (i = 0; i < MIDI_CHANNELS_PER_TRACK; ++i) + { + AllNotesOff(&channels[i], 0); + } + + // Free all track data. + + for (i = 0; i < num_tracks; ++i) + { + MIDI_FreeIterator(tracks[i].iter); + } + + free(tracks); + + tracks = NULL; + num_tracks = 0; + + if (midifile) + { + MIDI_FreeFile(midifile); + midifile = NULL; + } +} + +// Shutdown music + +static void I_OPL_ShutdownStream(void) +{ + if (music_initialized) + { + OPL_Shutdown(); + + // Release GENMIDI lump + + Z_ChangeTag(lump, PU_CACHE); + + music_initialized = false; + } +} + static const char **I_OPL_DeviceList(int *current_device) { - const char **devices = NULL; - *current_device = 0; + static const char **devices = NULL; + + if (devices) + { + return devices; + } + + if (current_device) + { + *current_device = 0; + } array_push(devices, "OPL3 Emulation"); return devices; } -static void I_OPL_UpdateMusic(void) +stream_module_t stream_opl_module = { - ; -} - -music_module_t music_opl_module = -{ - I_OPL_InitMusic, - I_OPL_ShutdownMusic, - I_OPL_SetMusicVolume, - I_OPL_PauseSong, - I_OPL_ResumeSong, - I_OPL_RegisterSong, - I_OPL_PlaySong, - I_OPL_UpdateMusic, - I_OPL_StopSong, - I_OPL_UnRegisterSong, + I_OPL_InitStream, + I_OPL_OpenStream, + I_OPL_FillStream, + I_OPL_PlayStream, + I_OPL_CloseStream, + I_OPL_ShutdownStream, I_OPL_DeviceList, }; diff --git a/src/i_sndfile.c b/src/i_sndfile.c index ae9d2dd3..282b98d7 100644 --- a/src/i_sndfile.c +++ b/src/i_sndfile.c @@ -577,6 +577,11 @@ boolean I_SND_LoadFile(void *data, ALenum *format, byte **wavdata, return true; } +static boolean I_SND_InitStream(int device) +{ + return true; +} + static sndfile_t stream; static loop_metadata_t loop; @@ -661,10 +666,23 @@ static void I_SND_CloseStream(void) CloseFile(&stream); } +static void I_SND_ShutdownStream(void) +{ + ; +} + +static const char **I_SND_DeviceList(int *current_device) +{ + return NULL; +} + stream_module_t stream_snd_module = { + I_SND_InitStream, I_SND_OpenStream, I_SND_FillStream, I_SND_PlayStream, I_SND_CloseStream, + I_SND_ShutdownStream, + I_SND_DeviceList, }; diff --git a/src/i_sound.c b/src/i_sound.c index df323183..ef869341 100644 --- a/src/i_sound.c +++ b/src/i_sound.c @@ -24,6 +24,8 @@ #include "i_sound.h" #include "doomstat.h" +#include "doomtype.h" +#include "i_oalstream.h" #include "i_printf.h" #include "i_system.h" #include "m_array.h" @@ -44,33 +46,27 @@ static const sound_module_t *sound_modules[] = static const sound_module_t *sound_module; -// Music modules -extern music_module_t music_win_module; -extern music_module_t music_mac_module; -extern music_module_t music_fl_module; -extern music_module_t music_oal_module; -extern music_module_t music_opl_module; - -typedef struct -{ - music_module_t *module; - int num_devices; -} music_modules_t; - -static music_modules_t music_modules[] = -{ +static music_module_t *native_midi_module = #if defined(_WIN32) - { &music_win_module, 1 }, + &music_win_module; #elif defined(__APPLE__) - { &music_mac_module, 1 }, + &music_mac_module; +#else + NULL; #endif + +static boolean native_midi; + +static stream_module_t *stream_modules[] = +{ #if defined(HAVE_FLUIDSYNTH) - { &music_fl_module, 1 }, + &stream_fl_module, #endif - { &music_opl_module, 1 }, + &stream_opl_module, }; -static music_module_t *midi_player_module = NULL; +stream_module_t *midi_stream_module = NULL; + static music_module_t *active_module = NULL; // haleyjd: safety variables to keep changes to *_card from making @@ -488,15 +484,27 @@ int midi_player; // current music module static void MidiPlayerFallback(void) { // Fall back the the first module that initializes, device 0. - int i; - for (i = 0; i < arrlen(music_modules); i++) + midi_player = 0; + + if (native_midi_module) { - if (music_modules[i].module->I_InitMusic(0)) + if (native_midi_module->I_InitMusic(0)) { - midi_player = i; - midi_player_module = music_modules[midi_player].module; - active_module = midi_player_module; + native_midi = true; + return; + } + midi_player = 1; + } + + native_midi = false; + + for (int i = 0; i < arrlen(stream_modules); ++i) + { + if (stream_modules[i]->I_InitStream(0)) + { + midi_player += i; + midi_stream_module = stream_modules[i]; return; } } @@ -506,36 +514,52 @@ static void MidiPlayerFallback(void) void I_SetMidiPlayer(int device) { - int i, accum; - if (nomusicparm) { return; } - midi_player_module->I_ShutdownMusic(); + int num_devices = 0; - for (i = 0, accum = 0; i < arrlen(music_modules); ++i) + midi_player = 0; + + if (native_midi_module) { - int num_devices = music_modules[i].num_devices; + const char **strings = native_midi_module->I_DeviceList(NULL); + num_devices = array_size(strings); + + if (device < num_devices) + { + native_midi_module->I_ShutdownMusic(); + if (native_midi_module->I_InitMusic(device)) + { + native_midi = true; + return; + } + } + midi_player = 1; + } + + native_midi = false; + + for (int i = 0, accum = num_devices; i < arrlen(stream_modules); ++i) + { + const char **strings = stream_modules[i]->I_DeviceList(NULL); + num_devices = array_size(strings); if (device >= accum && device < accum + num_devices) { - midi_player_module = music_modules[i].module; - midi_player = i; - device -= accum; - break; + midi_player += i; + if (stream_modules[i]->I_InitStream(device - accum)) + { + midi_stream_module = stream_modules[i]; + return; + } } accum += num_devices; } - if (midi_player_module->I_InitMusic(device)) - { - active_module = midi_player_module; - return; - } - MidiPlayerFallback(); } @@ -551,14 +575,32 @@ boolean I_InitMusic(void) music_oal_module.I_InitMusic(0); + active_module = &music_oal_module; + I_AtExit(I_ShutdownMusic, true); - if (midi_player < arrlen(music_modules)) + int module_index = 0; + + if (native_midi_module) { - midi_player_module = music_modules[midi_player].module; - if (midi_player_module->I_InitMusic(DEFAULT_MIDI_DEVICE)) + if (midi_player == 0 && + native_midi_module->I_InitMusic(DEFAULT_MIDI_DEVICE)) { - active_module = midi_player_module; + native_midi = true; + return true; + } + module_index = 1; + } + + native_midi = false; + + module_index = midi_player - module_index; + + if (module_index < arrlen(stream_modules)) + { + if (stream_modules[module_index]->I_InitStream(DEFAULT_MIDI_DEVICE)) + { + midi_stream_module = stream_modules[module_index]; return true; } } @@ -571,7 +613,10 @@ boolean I_InitMusic(void) void I_ShutdownMusic(void) { music_oal_module.I_ShutdownMusic(); - midi_player_module->I_ShutdownMusic(); + if (native_midi) + { + native_midi_module->I_ShutdownMusic(); + } } void I_SetMusicVolume(int volume) @@ -601,27 +646,16 @@ boolean IsMus(byte *mem, int len) void *I_RegisterSong(void *data, int size) { - if (IsMus(data, size) || IsMid(data, size)) + active_module = &music_oal_module; + + if (native_midi && (IsMid(data, size) || IsMus(data, size))) { - active_module = midi_player_module; - } - else - { - // Not a MIDI file. We have to shutdown the OPL module due to - // implementation details. - - if (midi_player_module == &music_opl_module) - { - midi_player_module->I_ShutdownMusic(); - } - - // Try to open file with SndFile or XMP. - - active_module = &music_oal_module; + active_module = native_midi_module; } + void *result = active_module->I_RegisterSong(data, size); active_module->I_SetMusicVolume(snd_MusicVolume); - return active_module->I_RegisterSong(data, size); + return result; } void I_PlaySong(void *handle, boolean looping) @@ -653,25 +687,41 @@ const char **I_DeviceList(int *current_device) *current_device = 0; - for (int i = 0; i < arrlen(music_modules); ++i) + int module_index = 0; + + if (native_midi_module) { - const char **module_devices = NULL; - int module_device; + int device; + const char **strings = native_midi_module->I_DeviceList(&device); - module_devices = music_modules[i].module->I_DeviceList(&module_device); - - if (midi_player == i) + if (midi_player == module_index) { - *current_device = array_size(devices) + module_device; + *current_device = device; } - music_modules[i].num_devices = array_size(module_devices); - - for (int k = 0; k < array_size(module_devices); ++k) + for (int i = 0; i < array_size(strings); ++i) { - array_push(devices, module_devices[k]); + array_push(devices, strings[i]); } - array_free(module_devices); + module_index = 1; + } + + for (int i = 0; i < arrlen(stream_modules); ++i) + { + int device; + const char **strings = stream_modules[i]->I_DeviceList(&device); + + if (midi_player == module_index) + { + *current_device = array_size(devices) + device; + } + + for (int k = 0; k < array_size(strings); ++k) + { + array_push(devices, strings[k]); + } + + module_index++; } return devices; diff --git a/src/i_sound.h b/src/i_sound.h index 19c28a81..792ceada 100644 --- a/src/i_sound.h +++ b/src/i_sound.h @@ -27,6 +27,8 @@ #include "doomtype.h" #include "m_fixed.h" +struct stream_module_s; + // when to clip out sounds // Does not fit the large outdoor areas. #define S_CLIPPING_DIST (1200 << FRACBITS) @@ -170,8 +172,15 @@ typedef struct const char **(*I_DeviceList)(int *current_device); } music_module_t; +// Music modules +extern music_module_t music_oal_module; +extern music_module_t music_win_module; +extern music_module_t music_mac_module; + extern int midi_player; +extern struct stream_module_s *midi_stream_module; + boolean I_InitMusic(void); void I_ShutdownMusic(void); diff --git a/src/i_winmusic.c b/src/i_winmusic.c index 72e56033..e0e0ad5b 100644 --- a/src/i_winmusic.c +++ b/src/i_winmusic.c @@ -51,7 +51,7 @@ enum RESET_TYPE_XG, }; -char *winmm_device = ""; +const char *winmm_device = ""; int winmm_complevel = COMP_STANDARD; int winmm_reset_type = RESET_TYPE_GM; int winmm_reset_delay = 0; @@ -104,7 +104,7 @@ static CRITICAL_SECTION CriticalSection; #define EMIDI_DEVICE (1U << EMIDI_DEVICE_GENERAL_MIDI) -static char **winmm_devices = NULL; +static const char **winmm_devices = NULL; // This is a reduced Windows MIDIEVENT structure for MEVT_F_SHORT // type of events. @@ -1888,9 +1888,17 @@ static void I_WIN_ShutdownMusic(void) static const char **I_WIN_DeviceList(int *current_device) { - const char **devices = NULL; + static const char **devices = NULL; - *current_device = 0; + if (devices) + { + return devices; + } + + if (current_device) + { + *current_device = 0; + } GetDevices(); @@ -1902,12 +1910,13 @@ static const char **I_WIN_DeviceList(int *current_device) for (int i = 0; i < array_size(winmm_devices); ++i) { - array_push(devices, winmm_devices[i]); - if (!strncasecmp(winmm_devices[i], winmm_device, MAXPNAMELEN)) + if (current_device && + !strncasecmp(winmm_devices[i], winmm_device, MAXPNAMELEN)) { *current_device = i; } } + devices = winmm_devices; return devices; } diff --git a/src/i_xmp.c b/src/i_xmp.c index b71a639f..65fbfcb4 100644 --- a/src/i_xmp.c +++ b/src/i_xmp.c @@ -59,18 +59,27 @@ static void PrintError(int e) I_Printf(VB_ERROR, "XMP: %s", msg); } -static boolean I_XMP_OpenStream(void *data, ALsizei size, ALenum *format, - ALsizei *freq, ALsizei *frame_size) +static boolean I_XMP_InitStream(int device) { - int err = 0; - context = xmp_create_context(); + if (!context) { I_Printf(VB_ERROR, "XMP: Failed to create context."); return false; } + return true; +} + +static boolean I_XMP_OpenStream(void *data, ALsizei size, ALenum *format, + ALsizei *freq, ALsizei *frame_size) +{ + if (!context) + return false; + + int err = 0; + err = xmp_load_module_from_memory(context, data, (long)size); if (err < 0) { @@ -101,26 +110,44 @@ static int I_XMP_FillStream(byte *buffer, int buffer_samples) static void I_XMP_PlayStream(boolean looping) { + if (!context) + return; + stream_looping = looping; xmp_start_player(context, SND_SAMPLERATE, 0); } static void I_XMP_CloseStream(void) { - if (context) - { - xmp_stop_module(context); - xmp_end_player(context); - xmp_release_module(context); - xmp_free_context(context); - context = NULL; - } + if (!context) + return; + + xmp_stop_module(context); + xmp_end_player(context); + xmp_release_module(context); +} + +static void I_XMP_ShutdownStream(void) +{ + if (!context) + return; + + xmp_free_context(context); + context = NULL; +} + +static const char **I_XMP_DeviceList(int *current_device) +{ + return NULL; } stream_module_t stream_xmp_module = { + I_XMP_InitStream, I_XMP_OpenStream, I_XMP_FillStream, I_XMP_PlayStream, I_XMP_CloseStream, + I_XMP_ShutdownStream, + I_XMP_DeviceList, };