From c1910382f8bd841798a5d511a1f8622e2911dffd Mon Sep 17 00:00:00 2001 From: kichikuou Date: Tue, 22 Sep 2026 15:47:24 +0900 Subject: [PATCH] SDL: Add SDL3 BGM backend Add an owner-aware music track shared by the SDL3 BGM, CD, and MIDI backends. Use it to preserve exclusive music playback while moving BGM loading, looping, fades, and volume control to SDL3_mixer. --- src/CMakeLists.txt | 2 +- src/bgm.sdl3mixer.c | 156 +++++++++++++++++++++++++++++++++++++++ src/sdl3_mixer_backend.c | 119 +++++++++++++++++++++++++++++ src/sdl3_mixer_backend.h | 19 +++++ 4 files changed, 295 insertions(+), 1 deletion(-) create mode 100644 src/bgm.sdl3mixer.c diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 26c6400..c6ff02e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -109,7 +109,7 @@ elseif (XSYSTEM35_SDL_VERSION STREQUAL "3") target_sources(xsystem35 PRIVATE pcm.sdl3mixer.c sdl3_mixer_backend.c - bgm.sdlmixer.c) + bgm.sdl3mixer.c) target_compile_definitions(xsystem35 PRIVATE HAVE_SDL3_MIXER_BACKEND) else() target_sources(xsystem35 PRIVATE pcm.sdlmixer.c bgm.sdlmixer.c) diff --git a/src/bgm.sdl3mixer.c b/src/bgm.sdl3mixer.c new file mode 100644 index 0000000..98e4702 --- /dev/null +++ b/src/bgm.sdl3mixer.c @@ -0,0 +1,156 @@ +/* + * bgm.sdl3mixer.c: BGM (*BA.ALD) playback using SDL3_mixer + * + * Copyright (C) 1997-1998 Masaki Chikama (Wren) + * 1998- + * + * 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. + */ + +#include + +#include "ald_manager.h" +#include "audio_meta.h" +#include "bgm.h" +#include "music_private.h" +#include "nact.h" +#include "sdl3_mixer_backend.h" +#include "system.h" + +static DRIFILETYPE dri_type; +static int base_no; +static int current_no; +static int current_vol = 100; +static uint32_t start_time; + +static int clamp_volume(int volume) +{ + if (volume < 0) + return 0; + if (volume > 100) + return 100; + return volume; +} + +static float music_gain(int volume) +{ + float gain = clamp_volume(volume) / 100.0f; + return gain * clamp_volume(prv.volval[BGM_VOLVAL_CH]) / 100.0f; +} + +static void apply_music_volume(int volume) +{ + current_vol = volume; + sdl3_mixer_set_music_gain(SDL3_MIXER_MUSIC_BGM, + music_gain(current_vol)); +} + +void musbgm_reapply_valance(void) +{ + apply_music_volume(current_vol); +} + +static void free_music(void) +{ + current_no = 0; + sdl3_mixer_stop_music(SDL3_MIXER_MUSIC_BGM, 0); +} + +static MIX_Audio *load_music(int no) +{ + free_music(); + int ald_no = no + base_no - 1; + dridata *dfile = ald_getdata(dri_type, ald_no); + if (!dfile) { + WARNING("Failed to open BGM %d", ald_no); + return NULL; + } + + MIX_Audio *audio = sdl3_mixer_load_music_memory( + dfile->data, dfile->size); + ald_freedata(dfile); + if (!audio) + WARNING("Failed to load BGM %d: %s", ald_no, SDL_GetError()); + return audio; +} + +bool musbgm_init(DRIFILETYPE type, int base) +{ + dri_type = type; + base_no = base; + if (type == DRIFILE_BGM) + return bgi_read(nact->files.bgi); + return true; +} + +void musbgm_exit(void) +{ + free_music(); +} + +void musbgm_reset(void) +{ + free_music(); +} + +bool musbgm_play(int no, int time, int volume, int loop_count) +{ + MIX_Audio *audio = load_music(no); + if (!audio) + return false; + + int loops = loop_count == 0 ? -1 : loop_count - 1; + current_vol = volume; + if (!sdl3_mixer_play_music(SDL3_MIXER_MUSIC_BGM, audio, + loops, time * 10, music_gain(current_vol))) + return false; + current_no = no; + start_time = sys_get_ticks(); + return true; +} + +void musbgm_stop(int no, int time) +{ + if (no == current_no) + sdl3_mixer_stop_music(SDL3_MIXER_MUSIC_BGM, time * 10); +} + +void musbgm_fade(int no, int time, int volume) +{ + (void)time; + if (no == current_no) + apply_music_volume(volume); +} + +int musbgm_getpos(int no) +{ + if (!musbgm_isplaying(no)) + return 0; + return (sys_get_ticks() - start_time) / 10; +} + +int musbgm_getlen(int no) +{ + bgi_t *bgi = bgi_find(no); + return bgi ? bgi->len / 441 : 0; +} + +bool musbgm_isplaying(int no) +{ + return no == current_no && + sdl3_mixer_music_active(SDL3_MIXER_MUSIC_BGM); +} + +void musbgm_stopall(int time) +{ + musbgm_stop(current_no, time); +} + +void musbgm_wait(int no, int timeout) +{ + while (timeout-- > 0 && musbgm_isplaying(no)) + SDL_Delay(10); +} diff --git a/src/sdl3_mixer_backend.c b/src/sdl3_mixer_backend.c index 5298b76..e456a98 100644 --- a/src/sdl3_mixer_backend.c +++ b/src/sdl3_mixer_backend.c @@ -5,6 +5,27 @@ #define SAMPLE_RATE 44100 static MIX_Mixer *mixer; +static MIX_Track *music_track; +static MIX_Audio *music_audio; +static enum sdl3_mixer_music_owner music_owner; + +static void clear_music(void) +{ + if (!music_audio) + return; + MIX_StopTrack(music_track, 0); + MIX_SetTrackAudio(music_track, NULL); + MIX_DestroyAudio(music_audio); + music_audio = NULL; + music_owner = SDL3_MIXER_MUSIC_NONE; +} + +static void reap_finished_music(void) +{ + if (music_audio && !MIX_TrackPlaying(music_track) && + !MIX_TrackPaused(music_track)) + clear_music(); +} bool sdl3_mixer_backend_init(int audio_buffer_size) { @@ -33,6 +54,14 @@ bool sdl3_mixer_backend_init(int audio_buffer_size) MIX_Quit(); return false; } + music_track = MIX_CreateTrack(mixer); + if (!music_track) { + WARNING("Cannot create music track: %s", SDL_GetError()); + MIX_DestroyMixer(mixer); + mixer = NULL; + MIX_Quit(); + return false; + } return true; } @@ -40,6 +69,9 @@ void sdl3_mixer_backend_exit(void) { if (!mixer) return; + clear_music(); + MIX_DestroyTrack(music_track); + music_track = NULL; MIX_DestroyMixer(mixer); mixer = NULL; MIX_Quit(); @@ -49,3 +81,90 @@ MIX_Mixer *sdl3_mixer_backend_get(void) { return mixer; } + +MIX_Audio *sdl3_mixer_load_music_memory(const void *data, size_t size) +{ + if (!mixer) + return NULL; + SDL_IOStream *io = SDL_IOFromConstMem(data, size); + if (!io) + return NULL; + return MIX_LoadAudio_IO(mixer, io, false, true); +} + +MIX_Audio *sdl3_mixer_load_music_file(const char *path) +{ + return mixer ? MIX_LoadAudio(mixer, path, false) : NULL; +} + +bool sdl3_mixer_play_music(enum sdl3_mixer_music_owner owner, + MIX_Audio *audio, int loops, int fade_in_ms, float gain) +{ + if (!music_track || !audio || owner == SDL3_MIXER_MUSIC_NONE) { + MIX_DestroyAudio(audio); + return false; + } + + clear_music(); + if (!MIX_SetTrackAudio(music_track, audio)) { + MIX_DestroyAudio(audio); + return false; + } + music_audio = audio; + music_owner = owner; + + SDL_PropertiesID props = SDL_CreateProperties(); + bool success = props && + MIX_SetTrackGain(music_track, gain) && + SDL_SetNumberProperty(props, MIX_PROP_PLAY_LOOPS_NUMBER, loops) && + SDL_SetNumberProperty(props, + MIX_PROP_PLAY_FADE_IN_MILLISECONDS_NUMBER, fade_in_ms) && + MIX_PlayTrack(music_track, props); + if (props) + SDL_DestroyProperties(props); + if (!success) + clear_music(); + return success; +} + +void sdl3_mixer_stop_music(enum sdl3_mixer_music_owner owner, int fade_out_ms) +{ + reap_finished_music(); + if (music_owner != owner) + return; + if (fade_out_ms <= 0) { + clear_music(); + return; + } + Sint64 frames = MIX_TrackMSToFrames(music_track, fade_out_ms); + if (frames <= 0 || !MIX_StopTrack(music_track, frames)) + clear_music(); +} + +void sdl3_mixer_pause_music(enum sdl3_mixer_music_owner owner) +{ + reap_finished_music(); + if (music_owner == owner) + MIX_PauseTrack(music_track); +} + +void sdl3_mixer_resume_music(enum sdl3_mixer_music_owner owner) +{ + reap_finished_music(); + if (music_owner == owner) + MIX_ResumeTrack(music_track); +} + +bool sdl3_mixer_music_active(enum sdl3_mixer_music_owner owner) +{ + reap_finished_music(); + return music_owner == owner && + (MIX_TrackPlaying(music_track) || MIX_TrackPaused(music_track)); +} + +void sdl3_mixer_set_music_gain(enum sdl3_mixer_music_owner owner, float gain) +{ + reap_finished_music(); + if (music_owner == owner) + MIX_SetTrackGain(music_track, gain); +} diff --git a/src/sdl3_mixer_backend.h b/src/sdl3_mixer_backend.h index 7e6ff61..d74c7b6 100644 --- a/src/sdl3_mixer_backend.h +++ b/src/sdl3_mixer_backend.h @@ -4,8 +4,27 @@ #include "portab.h" #include "sdl_mixer_compat.h" +enum sdl3_mixer_music_owner { + SDL3_MIXER_MUSIC_NONE, + SDL3_MIXER_MUSIC_BGM, + SDL3_MIXER_MUSIC_CD, + SDL3_MIXER_MUSIC_MIDI, +}; + bool sdl3_mixer_backend_init(int audio_buffer_size); void sdl3_mixer_backend_exit(void); MIX_Mixer *sdl3_mixer_backend_get(void); +MIX_Audio *sdl3_mixer_load_music_memory(const void *data, size_t size); +MIX_Audio *sdl3_mixer_load_music_file(const char *path); + +/* play_music takes ownership of audio, including when playback fails. */ +bool sdl3_mixer_play_music(enum sdl3_mixer_music_owner owner, + MIX_Audio *audio, int loops, int fade_in_ms, float gain); +void sdl3_mixer_stop_music(enum sdl3_mixer_music_owner owner, int fade_out_ms); +void sdl3_mixer_pause_music(enum sdl3_mixer_music_owner owner); +void sdl3_mixer_resume_music(enum sdl3_mixer_music_owner owner); +bool sdl3_mixer_music_active(enum sdl3_mixer_music_owner owner); +void sdl3_mixer_set_music_gain(enum sdl3_mixer_music_owner owner, float gain); + #endif /* XSYSTEM35_SDL3_MIXER_BACKEND_H */