MXVK Vulkan Framework 0.24.0
C++20 Vulkan rendering framework for practical 2D and 3D application development with SDL3.
Loading...
Searching...
No Matches
mxvk_sound.cpp
Go to the documentation of this file.
1/**
2 * @file mxvk_sound.cpp
3 * @brief Implementation of mxvk::VK_Mixer SDL3_mixer audio manager.
4 */
5
6#include "mxvk/mxvk_sound.hpp"
7
8#if defined(MXVK_WITH_MIXER) || defined(WITH_MIXER)
9
11
12#include <limits>
13#include <string>
14
15namespace mxvk {
16
17 std::size_t VK_Mixer::toIndex(const int value) {
18 return static_cast<std::size_t>(value);
19 }
20
22 init();
23 }
24
26 cleanup();
27 }
28
30 if (initialized) {
31 return;
32 }
33
34 if (!MIX_Init()) {
35 throw mxvk::Exception("Could not initialize SDL3_mixer: " + std::string(SDL_GetError()));
36 }
37
38 SDL_AudioSpec spec{};
39 spec.freq = 44100;
40 spec.format = SDL_AUDIO_S16;
41 spec.channels = 2;
42
43 MIX_Mixer *raw_mixer = MIX_CreateMixerDevice(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &spec);
44 if (raw_mixer == nullptr) {
45 MIX_Quit();
46 throw mxvk::Exception("Could not create mixer device: " + std::string(SDL_GetError()));
47 }
48
49 mixer.reset(raw_mixer);
50 initialized = true;
51 }
52
53 int VK_Mixer::loadMusic(const std::string &filename) {
54 if (filename.empty()) {
55 throw mxvk::Exception("Music filename cannot be empty");
56 }
57 if (!initialized || mixer == nullptr) {
58 throw mxvk::Exception("Audio mixer is not initialized");
59 }
60
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()));
64 }
65
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()));
70 }
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()));
75 }
76
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);
80 }
81
82 int VK_Mixer::loadWav(const std::string &filename) {
83 if (filename.empty()) {
84 throw mxvk::Exception("WAV filename cannot be empty");
85 }
86 if (!initialized || mixer == nullptr) {
87 throw mxvk::Exception("Audio mixer is not initialized");
88 }
89
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()));
93 }
94
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()));
99 }
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()));
104 }
105
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);
109 }
110
111 int VK_Mixer::playMusic(int id, int value) {
112 if (!initialized) {
113 return -1;
114 }
115 if (id < 0 || id >= static_cast<int>(music_tracks.size())) {
116 return -1;
117 }
118
119 MIX_Track *track = music_tracks[toIndex(id)].get();
120 if (track == nullptr) {
121 return -1;
122 }
123 if (!MIX_SetTrackLoops(track, value)) {
124 return -1;
125 }
126 return MIX_PlayTrack(track, 0) ? 0 : -1;
127 }
128
129 int VK_Mixer::playWav(int id, int value, int channel) {
130 if (!initialized) {
131 return -1;
132 }
133 if (id < 0 || id >= static_cast<int>(wav_files.size())) {
134 return -1;
135 }
136
137 int target = id;
138 if (channel >= 0) {
139 if (channel >= static_cast<int>(wav_tracks.size())) {
140 return -1;
141 }
142 target = channel;
143 }
144
145 MIX_Track *track = wav_tracks[toIndex(target)].get();
146 MIX_Audio *audio = wav_files[toIndex(id)].get();
147 if (track == nullptr || audio == nullptr) {
148 return -1;
149 }
150 if (!MIX_SetTrackAudio(track, audio)) {
151 return -1;
152 }
153 if (!MIX_SetTrackLoops(track, value)) {
154 return -1;
155 }
156 return MIX_PlayTrack(track, 0) ? target : -1;
157 }
158
159 [[nodiscard]] bool VK_Mixer::isPlaying(int channel) const {
160 if (!initialized || channel < 0 || channel >= static_cast<int>(wav_tracks.size())) {
161 return false;
162 }
163
164 MIX_Track *track = wav_tracks[toIndex(channel)].get();
165 if (track == nullptr) {
166 return false;
167 }
168 return MIX_TrackPlaying(track);
169 }
170
171 [[nodiscard]] bool VK_Mixer::isMusicPlaying(int id) const {
172 if (!initialized || id < 0 || id >= static_cast<int>(music_tracks.size())) {
173 return false;
174 }
175
176 MIX_Track *track = music_tracks[toIndex(id)].get();
177 if (track == nullptr) {
178 return false;
179 }
180 return MIX_TrackPlaying(track);
181 }
182
184 if (!initialized || mixer == nullptr) {
185 return;
186 }
187
188 for (const auto &track : music_tracks) {
189 if (track != nullptr) {
190 MIX_StopTrack(track.get(), 0);
191 }
192 }
193 MIX_StopAllTracks(mixer.get(), 0);
194 }
195
197 if (!initialized) {
198 return;
199 }
200
201 stopMusic();
202 music_tracks.clear();
203 wav_tracks.clear();
204 music_files.clear();
205 wav_files.clear();
206 mixer.reset();
207 MIX_Quit();
208 initialized = false;
209 }
210
211} // namespace mxvk
212
213#endif
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.
Definition mxvk.hpp:30