8#if defined(MXVK_WITH_MIXER) || defined(WITH_MIXER)
17 std::size_t VK_Mixer::toIndex(
const int value) {
18 return static_cast<std::size_t
>(value);
35 throw mxvk::Exception(
"Could not initialize SDL3_mixer: " + std::string(SDL_GetError()));
40 spec.format = SDL_AUDIO_S16;
43 MIX_Mixer *raw_mixer = MIX_CreateMixerDevice(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &spec);
44 if (raw_mixer ==
nullptr) {
46 throw mxvk::Exception(
"Could not create mixer device: " + std::string(SDL_GetError()));
49 mixer.reset(raw_mixer);
54 if (filename.empty()) {
57 if (!initialized || mixer ==
nullptr) {
61 MIX_Audio *raw_audio = MIX_LoadAudio(mixer.get(), filename.c_str(),
false);
62 if (raw_audio ==
nullptr) {
63 throw mxvk::Exception(
"Error loading music file '" + filename +
"': " + std::string(SDL_GetError()));
66 MIX_Track *raw_track = MIX_CreateTrack(mixer.get());
67 if (raw_track ==
nullptr) {
68 MIX_DestroyAudio(raw_audio);
69 throw mxvk::Exception(
"Error creating music track: " + std::string(SDL_GetError()));
71 if (!MIX_SetTrackAudio(raw_track, raw_audio)) {
72 MIX_DestroyTrack(raw_track);
73 MIX_DestroyAudio(raw_audio);
74 throw mxvk::Exception(
"Error assigning music track audio: " + std::string(SDL_GetError()));
77 music_files.emplace_back(raw_audio, MIX_DestroyAudio);
78 music_tracks.emplace_back(raw_track, MIX_DestroyTrack);
79 return static_cast<int>(music_files.size() - 1);
83 if (filename.empty()) {
86 if (!initialized || mixer ==
nullptr) {
90 MIX_Audio *raw_audio = MIX_LoadAudio(mixer.get(), filename.c_str(),
true);
91 if (raw_audio ==
nullptr) {
92 throw mxvk::Exception(
"Error loading WAV file '" + filename +
"': " + std::string(SDL_GetError()));
95 MIX_Track *raw_track = MIX_CreateTrack(mixer.get());
96 if (raw_track ==
nullptr) {
97 MIX_DestroyAudio(raw_audio);
98 throw mxvk::Exception(
"Error creating WAV track: " + std::string(SDL_GetError()));
100 if (!MIX_SetTrackAudio(raw_track, raw_audio)) {
101 MIX_DestroyTrack(raw_track);
102 MIX_DestroyAudio(raw_audio);
103 throw mxvk::Exception(
"Error assigning WAV track audio: " + std::string(SDL_GetError()));
106 wav_files.emplace_back(raw_audio, MIX_DestroyAudio);
107 wav_tracks.emplace_back(raw_track, MIX_DestroyTrack);
108 return static_cast<int>(wav_files.size() - 1);
115 if (id < 0 || id >=
static_cast<int>(music_tracks.size())) {
119 MIX_Track *track = music_tracks[toIndex(
id)].get();
120 if (track ==
nullptr) {
123 if (!MIX_SetTrackLoops(track, value)) {
126 return MIX_PlayTrack(track, 0) ? 0 : -1;
133 if (id < 0 || id >=
static_cast<int>(wav_files.size())) {
139 if (channel >=
static_cast<int>(wav_tracks.size())) {
145 MIX_Track *track = wav_tracks[toIndex(target)].get();
146 MIX_Audio *audio = wav_files[toIndex(
id)].get();
147 if (track ==
nullptr || audio ==
nullptr) {
150 if (!MIX_SetTrackAudio(track, audio)) {
153 if (!MIX_SetTrackLoops(track, value)) {
156 return MIX_PlayTrack(track, 0) ? target : -1;
160 if (!initialized || channel < 0 || channel >=
static_cast<int>(wav_tracks.size())) {
164 MIX_Track *track = wav_tracks[toIndex(channel)].get();
165 if (track ==
nullptr) {
168 return MIX_TrackPlaying(track);
172 if (!initialized || id < 0 || id >=
static_cast<int>(music_tracks.size())) {
176 MIX_Track *track = music_tracks[toIndex(
id)].get();
177 if (track ==
nullptr) {
180 return MIX_TrackPlaying(track);
184 if (!initialized || mixer ==
nullptr) {
188 for (
const auto &track : music_tracks) {
189 if (track !=
nullptr) {
190 MIX_StopTrack(track.get(), 0);
193 MIX_StopAllTracks(mixer.get(), 0);
202 music_tracks.clear();
int loadWav(const std::string &filename)
Load a WAV file as a sound effect chunk.
int playWav(int id, int value=0, int channel=-1)
Play a previously loaded WAV chunk.
void stopMusic()
Stop background music playback immediately.
void cleanup()
Free all loaded audio chunks and music tracks.
bool isPlaying(int channel) const
Query whether a sound track is currently playing.
void init()
Open the audio device (called automatically by constructor).
int loadMusic(const std::string &filename)
Load an audio file as streaming background music.
bool isMusicPlaying(int id) const
Query whether a loaded music track is currently playing.
VK_Mixer()
Initialise SDL3_mixer and open the audio device.
int playMusic(int id, int value=0)
Start playing a previously loaded music track.
~VK_Mixer() noexcept
Halt all playback and free all loaded audio resources.
SDL3_mixer audio subsystem wrapper.
Utilities for loading and saving PNG images.