8#if defined(MXVK_WITH_MIXER) || defined(WITH_MIXER)
17 std::size_t VK_Mixer::toIndex(
const int value) {
return static_cast<std::size_t
>(value); }
29 throw mxvk::Exception(
"Could not initialize SDL3_mixer: " + std::string(SDL_GetError()));
34 spec.format = SDL_AUDIO_S16;
37 MIX_Mixer *raw_mixer = MIX_CreateMixerDevice(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &spec);
38 if (raw_mixer ==
nullptr) {
40 throw mxvk::Exception(
"Could not create mixer device: " + std::string(SDL_GetError()));
43 mixer.reset(raw_mixer);
48 if (filename.empty()) {
51 if (!initialized || mixer ==
nullptr) {
55 MIX_Audio *raw_audio = MIX_LoadAudio(mixer.get(), filename.c_str(),
false);
56 if (raw_audio ==
nullptr) {
57 throw mxvk::Exception(
"Error loading music file '" + filename +
"': " + std::string(SDL_GetError()));
60 MIX_Track *raw_track = MIX_CreateTrack(mixer.get());
61 if (raw_track ==
nullptr) {
62 MIX_DestroyAudio(raw_audio);
63 throw mxvk::Exception(
"Error creating music track: " + std::string(SDL_GetError()));
65 if (!MIX_SetTrackAudio(raw_track, raw_audio)) {
66 MIX_DestroyTrack(raw_track);
67 MIX_DestroyAudio(raw_audio);
68 throw mxvk::Exception(
"Error assigning music track audio: " + std::string(SDL_GetError()));
71 music_files.emplace_back(raw_audio, MIX_DestroyAudio);
72 music_tracks.emplace_back(raw_track, MIX_DestroyTrack);
73 return static_cast<int>(music_files.size() - 1);
77 if (filename.empty()) {
80 if (!initialized || mixer ==
nullptr) {
84 MIX_Audio *raw_audio = MIX_LoadAudio(mixer.get(), filename.c_str(),
true);
85 if (raw_audio ==
nullptr) {
86 throw mxvk::Exception(
"Error loading WAV file '" + filename +
"': " + std::string(SDL_GetError()));
89 MIX_Track *raw_track = MIX_CreateTrack(mixer.get());
90 if (raw_track ==
nullptr) {
91 MIX_DestroyAudio(raw_audio);
92 throw mxvk::Exception(
"Error creating WAV track: " + std::string(SDL_GetError()));
94 if (!MIX_SetTrackAudio(raw_track, raw_audio)) {
95 MIX_DestroyTrack(raw_track);
96 MIX_DestroyAudio(raw_audio);
97 throw mxvk::Exception(
"Error assigning WAV track audio: " + std::string(SDL_GetError()));
100 wav_files.emplace_back(raw_audio, MIX_DestroyAudio);
101 wav_tracks.emplace_back(raw_track, MIX_DestroyTrack);
102 return static_cast<int>(wav_files.size() - 1);
109 if (id < 0 || id >=
static_cast<int>(music_tracks.size())) {
113 MIX_Track *track = music_tracks[toIndex(
id)].get();
114 if (track ==
nullptr) {
117 if (!MIX_SetTrackLoops(track, value)) {
120 return MIX_PlayTrack(track, 0) ? 0 : -1;
127 if (id < 0 || id >=
static_cast<int>(wav_files.size())) {
133 if (channel >=
static_cast<int>(wav_tracks.size())) {
139 MIX_Track *track = wav_tracks[toIndex(target)].get();
140 MIX_Audio *audio = wav_files[toIndex(
id)].get();
141 if (track ==
nullptr || audio ==
nullptr) {
144 if (!MIX_SetTrackAudio(track, audio)) {
147 if (!MIX_SetTrackLoops(track, value)) {
150 return MIX_PlayTrack(track, 0) ? target : -1;
154 if (!initialized || channel < 0 || channel >=
static_cast<int>(wav_tracks.size())) {
158 MIX_Track *track = wav_tracks[toIndex(channel)].get();
159 if (track ==
nullptr) {
162 return MIX_TrackPlaying(track);
166 if (!initialized || id < 0 || id >=
static_cast<int>(music_tracks.size())) {
170 MIX_Track *track = music_tracks[toIndex(
id)].get();
171 if (track ==
nullptr) {
174 return MIX_TrackPlaying(track);
178 if (!initialized || mixer ==
nullptr) {
182 for (
const auto &track : music_tracks) {
183 if (track !=
nullptr) {
184 MIX_StopTrack(track.get(), 0);
187 MIX_StopAllTracks(mixer.get(), 0);
196 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.