2#include <SDL3_mixer/SDL_mixer.h>
6#include "entt/core/type_traits.hpp"
15 std::unordered_map<TrackID, std::pair<MIX_Track*, Track*>>
m_TrackPool;
19 const bool success = MIX_PauseAllTracks(s_Data->m_Mixer);
21 return std::unexpected(
Core::CoriError(std::format(
"Failed to pause all tracks. SDL_Error: {}", SDL_GetError())));
28 const bool success = MIX_ResumeAllTracks(s_Data->m_Mixer);
30 return std::unexpected(
Core::CoriError(std::format(
"Failed to resume all tracks. SDL_Error: {}", SDL_GetError())));
37 const bool success = MIX_SetMasterGain(s_Data->m_Mixer, gain);
39 return std::unexpected(
Core::CoriError<>(std::format(
"Failed to set master gain. SDL_Error: {}", SDL_GetError())));
46 return MIX_GetMasterGain(s_Data->m_Mixer);
50 const SDL_PropertiesID props = SDL_CreateProperties();
51 SDL_SetNumberProperty(props, MIX_PROP_PLAY_LOOPS_NUMBER, params.
Loops);
53 SDL_SetNumberProperty(props, MIX_PROP_PLAY_MAX_MILLISECONDS_NUMBER, params.
MaxMilliseconds);
57 SDL_SetNumberProperty(props, MIX_PROP_PLAY_START_MILLISECOND_NUMBER, params.
StartMillisecond);
61 SDL_SetNumberProperty(props, MIX_PROP_PLAY_LOOP_START_MILLISECOND_NUMBER, params.
LoopStartMillisecond);
65 SDL_SetNumberProperty(props, MIX_PROP_PLAY_FADE_IN_MILLISECONDS_NUMBER, params.
FadeInMilliseconds);
72 const bool success = MIX_PlayTag(s_Data->m_Mixer, tag, props);
74 return std::unexpected(
Core::CoriError(std::format(
"Failed to play mixer Tag '{}'. SDL_Error: {}", tag, SDL_GetError())));
80 std::expected<void, Core::CoriError<>>
Mixer::StopTag(
const char* tag,
const int64_t fadeOutMS) {
81 const bool success = MIX_StopTag(s_Data->m_Mixer, tag, fadeOutMS);
83 return std::unexpected(
Core::CoriError(std::format(
"Failed to stop mixer Tag '{}'. SDL_Error: {}", tag, SDL_GetError())));
90 const bool success = MIX_PauseTag(s_Data->m_Mixer, tag);
92 return std::unexpected(
Core::CoriError(std::format(
"Failed to pause mixer Tag '{}'. SDL_Error: {}", tag, SDL_GetError())));
99 const bool success = MIX_ResumeTag(s_Data->m_Mixer, tag);
101 return std::unexpected(
Core::CoriError(std::format(
"Failed to resume mixer Tag '{}'. SDL_Error: {}", tag, SDL_GetError())));
108 const bool success = MIX_SetTagGain(s_Data->m_Mixer, tag, gain);
110 return std::unexpected(
Core::CoriError(std::format(
"Failed to set Tag '{}' gain. SDL_Error: {}", tag, SDL_GetError())));
117 if (
CORI_CORE_VERIFY(MIX_Init(),
"Failed to initialize SDL_Mixer! SDL_Error: {}", SDL_GetError())) {}
123 s_Data->m_Mixer = MIX_CreateMixerDevice(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK,
nullptr);
126 void Mixer::Shutdown() {
131 std::expected<void, Core::CoriError<std::filesystem::path>> Mixer::LoadSound(
const std::filesystem::path& path,
const bool preDecode,
const SoundID soundID) {
132 const auto result = MIX_LoadAudio(s_Data->m_Mixer, path.string().c_str(), preDecode);
134 return std::unexpected(Core::CoriError<std::filesystem::path>(std::format(
"Failed to load Sound. SDL_Error: {}", SDL_GetError()),
"Path", path));
137 s_Data->m_SDLAudios.insert({ soundID, result });
141 void Mixer::UnloadSound(
const SoundID soundID) {
142 MIX_DestroyAudio(s_Data->m_SDLAudios.at(soundID));
143 s_Data->m_SDLAudios.erase(soundID);
146 std::expected<void, Core::CoriError<>> Mixer::CreateTrack(
Track* track) {
147 const auto result = MIX_CreateTrack(s_Data->m_Mixer);
149 return std::unexpected(Core::CoriError(std::format(
"Failed to create Track. SDL_Error: {}", SDL_GetError())));
153 s_Data->m_TrackPool.insert({ track->GetID(), std::make_pair(result, track) });
157 void Mixer::DestroyTrack(
const TrackID trackID) {
158 MIX_DestroyTrack(s_Data->m_TrackPool.at(trackID).first);
159 s_Data->m_TrackPool.erase(trackID);
162 std::expected<void, Core::CoriError<>> Mixer::SetTrackSound(
const TrackID trackID,
const Sound* sound) {
163 const bool success = MIX_SetTrackAudio(s_Data->m_TrackPool.at(trackID).first, s_Data->m_SDLAudios.at(sound->GetID()));
165 return std::unexpected(Core::CoriError(std::format(
"Failed to assign Sound '{} (SoundID: {})' to Track '{} (TrackID: {})'. SDL_Error: {}", sound->m_Name, sound->GetID(), s_Data->m_TrackPool.at(trackID).second->m_Name, trackID, SDL_GetError())));
171 std::expected<void, Core::CoriError<>> Mixer::PlayTrack(
const TrackID trackID,
const PlayParams& params) {
172 const SDL_PropertiesID props = SDL_CreateProperties();
173 SDL_SetNumberProperty(props, MIX_PROP_PLAY_LOOPS_NUMBER, params.Loops);
174 if (params.MaxMilliseconds != -1.0f) {
175 auto samples = MillisecondsToFrames(trackID, params.MaxMilliseconds);
177 SDL_SetNumberProperty(props, MIX_PROP_PLAY_MAX_FRAME_NUMBER, samples.value());
179 return std::unexpected(samples.error());
183 if (params.StartMillisecond != 0.0f) {
184 auto samples = MillisecondsToFrames(trackID, params.StartMillisecond);
186 SDL_SetNumberProperty(props, MIX_PROP_PLAY_START_FRAME_NUMBER, samples.value());
188 return std::unexpected(samples.error());
192 if (params.LoopStartMillisecond != 0.0f) {
193 auto samples = MillisecondsToFrames(trackID, params.LoopStartMillisecond);
195 SDL_SetNumberProperty(props, MIX_PROP_PLAY_LOOP_START_FRAME_NUMBER, samples.value());
197 return std::unexpected(samples.error());
201 if (params.FadeInMilliseconds != 0.0f) {
202 auto samples = MillisecondsToFrames(trackID, params.FadeInMilliseconds);
204 SDL_SetNumberProperty(props, MIX_PROP_PLAY_FADE_IN_FRAMES_NUMBER, samples.value());
206 return std::unexpected(samples.error());
210 if (params.AppendSilenceMilliseconds != 0.0f) {
211 auto samples = MillisecondsToFrames(trackID, params.AppendSilenceMilliseconds);
213 SDL_SetNumberProperty(props, MIX_PROP_PLAY_APPEND_SILENCE_FRAMES_NUMBER, samples.value());
215 return std::unexpected(samples.error());
219 const bool success = MIX_PlayTrack(s_Data->m_TrackPool.at(trackID).first, props);
221 return std::unexpected(Core::CoriError(std::format(
"Failed to play Track '{} (TrackID: {})'. SDL_Error: {}", s_Data->m_TrackPool.at(trackID).second->m_Name, trackID, SDL_GetError())));
226 std::expected<void, Core::CoriError<>> Mixer::StopTrack(
const TrackID trackID,
const int64_t fadeOutMS) {
227 const bool success = MIX_StopTrack(s_Data->m_TrackPool.at(trackID).first, MIX_TrackMSToFrames(s_Data->m_TrackPool.at(trackID).first, fadeOutMS));
229 return std::unexpected(Core::CoriError(std::format(
"Failed to stop Track '{} (TrackID: {})'. SDL_Error: '{}'", s_Data->m_TrackPool.at(trackID).second->m_Name, trackID, SDL_GetError())));
234 std::expected<void, Core::CoriError<>> Mixer::PauseTrack(
const TrackID trackID) {
235 const bool success = MIX_PauseTrack(s_Data->m_TrackPool.at(trackID).first);
237 return std::unexpected(Core::CoriError(std::format(
"Failed to pause Track '{} (TrackID: {})'. SDL_Error: {}", s_Data->m_TrackPool.at(trackID).second->m_Name, trackID, SDL_GetError())));
242 std::expected<void, Core::CoriError<>> Mixer::ResumeTrack(
const TrackID trackID) {
243 const bool success = MIX_ResumeTrack(s_Data->m_TrackPool.at(trackID).first);
245 return std::unexpected(Core::CoriError(std::format(
"Failed to pause Track '{} (TrackID: {})'. SDL_Error: {}", s_Data->m_TrackPool.at(trackID).second->m_Name, trackID, SDL_GetError())));
250 bool Mixer::IsTrackPaused(
const TrackID trackID) {
251 return MIX_TrackPaused(s_Data->m_TrackPool.at(trackID).first);
254 bool Mixer::IsTrackPlaying(
const TrackID trackID) {
255 return MIX_TrackPlaying(s_Data->m_TrackPool.at(trackID).first);
258 std::expected<void, Core::CoriError<>> Mixer::SetTrackGain(
const TrackID trackID,
const float gain) {
259 const bool success = MIX_SetTrackGain(s_Data->m_TrackPool.at(trackID).first, gain);
261 return std::unexpected(Core::CoriError(std::format(
"Failed to set Track '{} (TrackID: {})' gain. SDL_Error: {}", s_Data->m_TrackPool.at(trackID).second->m_Name, trackID, SDL_GetError())));
267 float Mixer::GetTrackGain(
const TrackID trackID) {
268 return MIX_GetTrackGain(s_Data->m_TrackPool.at(trackID).first);
271 std::expected<void, Core::CoriError<>> Mixer::TagTrack(
const TrackID trackID,
const char* tag) {
272 const bool success = MIX_TagTrack(s_Data->m_TrackPool.at(trackID).first, tag);
274 return std::unexpected(Core::CoriError(std::format(
"Failed to assign the tag the Track '{} (TrackID: {}'. SDL_Error: {}", s_Data->m_TrackPool.at(trackID).second->m_Name, trackID, SDL_GetError())));
280 void Mixer::UntagTrack(
const TrackID trackID,
const char* tag) {
281 MIX_UntagTrack(s_Data->m_TrackPool.at(trackID).first, tag);
284 std::expected<int64_t, Core::CoriError<>> Mixer::MillisecondsToFrames(
const TrackID trackID,
const float milliseconds) {
285 const int64_t sampleRate = MIX_TrackMSToFrames(s_Data->m_TrackPool.at(trackID).first, 1000);
287 if (sampleRate == -1) {
288 return std::unexpected(Core::CoriError(std::format(
"Failed to convert milliseconds to samples for Track '{} (TrackID: {}'. SDL_Error: {}", s_Data->m_TrackPool.at(trackID).second->m_Name, trackID, SDL_GetError())));
291 return milliseconds / 1000.0f *
static_cast<float>(sampleRate);
#define CORI_CORE_TRACE_TAGGED(...)
#define CORI_CORE_VERIFY(x,...)
#define CORI_CORE_INFO_TAGGED(...)
static std::expected< void, Core::CoriError<> > SetTagGain(const char *tag, const float gain)
Sets the gain of all tracks with a specific tag.
static std::expected< void, Core::CoriError<> > SetMasterGain(const float gain)
Sets the master gain of a Mixer.
static std::expected< void, Core::CoriError<> > StartTag(const char *tag, const PlayParams ¶ms=PlayParams{})
Start (ot restart) mixing all Tracks with a specific tag.
static float GetMasterGain()
Returns the current master gain specified for the Mixer. The default gain for a Mixer is 1....
static std::expected< void, Core::CoriError<> > ResumeTag(const char *tag)
Resumes all Tracks with a specific tag.
static std::expected< void, Core::CoriError<> > PauseAllTracks()
Pauses all Track that are currently playing on the Mixer.
static std::expected< void, Core::CoriError<> > PauseTag(const char *tag)
Pauses all tracks with a specific tag.
static std::expected< void, Core::CoriError<> > ResumeAllTracks()
Resumes all Track that are currently paused on the Mixer.
static std::expected< void, Core::CoriError<> > StopTag(const char *tag, const int64_t fadeOutMS=0)
Stops all Track with a specific tab, possibly fading out over time.
Sound asset to be played on a Track.
You use Track to play and mix Sound objects.
static void TrackStopCallback(void *userdata, MIX_Track *track)
For internal use only!
Custom error class mainly used in std::expected.
Everything connected to audio is in this namespace.
std::unordered_map< TrackID, std::pair< MIX_Track *, Track * > > m_TrackPool
std::unordered_map< SoundID, MIX_Audio * > m_SDLAudios
Parameters to be used when playing sound, you can mix your audio playback however you want with these...
float MaxMilliseconds
Mix at most n milliseconds. Default -1.0f, mixes all available audio.
int32_t Loops
Number of times to loop the track when it reaches the end. A value of -1 will result in an infinite l...
float LoopStartMillisecond
Start looping at n'th millisecond. Values <=0 will result in looping from the very beginning of the t...
float FadeInMilliseconds
Fade in the audio over n milliseconds. Will start from silence and reach full volume smoothly....
float StartMillisecond
Start mixing at n'th millisecond. Values <=0 will result in mixing from the very beginning of the tra...
float AppendSilenceMilliseconds
Append n milliseconds to the end of a track after all loops (specified in PlayParams::Loops) are comp...