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.
This commit is contained in:
kichikuou
2026-09-22 15:47:24 +09:00
parent 495d6146f3
commit c1910382f8
4 changed files with 295 additions and 1 deletions
+1 -1
View File
@@ -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)
+156
View File
@@ -0,0 +1,156 @@
/*
* bgm.sdl3mixer.c: BGM (*BA.ALD) playback using SDL3_mixer
*
* Copyright (C) 1997-1998 Masaki Chikama (Wren) <chikama@kasumi.ipl.mech.nagoya-u.ac.jp>
* 1998- <masaki-c@is.aist-nara.ac.jp>
*
* 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 <stdint.h>
#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);
}
+119
View File
@@ -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);
}
+19
View File
@@ -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 */