/* +------------------------------------------------------+ ____/ \____ /| - Open source game framework licensed to freely use, | \ / / | copy, modify and sell without restriction | +--\ ^__^ /--+ | | | ~/ \~ | | - created for | | ~~~~~~~~~~~~ | +------------------------------------------------------+ | SPACE ~~~~~ | / | ~~~~~~~ BOX |/ +-------------*/ #include "Audio.hpp" using namespace sb::audio; int sb::audio::loudest_channel() { int loudest_volume = 0; int loudest_channel = 0; int volume = 0; int count = Mix_GroupCount(-1); for (int channel = 0; channel < count; channel++) { volume = Mix_Volume(channel, -1); if (volume > loudest_volume) { loudest_volume = volume; loudest_channel = channel; } } return loudest_channel; } float sb::audio::loudest_channel_volume() { return normalize_volume(Mix_Volume(loudest_channel(), -1)); } std::uint8_t sb::audio::convert_volume(float volume) { return std::clamp(static_cast(std::round(volume * MIX_MAX_VOLUME)), 0, MIX_MAX_VOLUME); } float sb::audio::normalize_volume(std::uint8_t volume) { return float(volume) / float(MIX_MAX_VOLUME); } Chunk::Chunk(const fs::path& path) { load(path); } void Chunk::load(const fs::path& path) { chunk = std::shared_ptr(Mix_LoadWAV(path.string().c_str()), Mix_FreeChunk); if (chunk.get() == nullptr) { std::ostringstream message; message << "Unable to load audio chunk at " << path << "."; Log::sdl_error(message.str(), Log::Level::WARN); } } float Chunk::volume() const { if (Mix_QuerySpec(nullptr, nullptr, nullptr) != 0) { std::uint8_t sdl_volume = Mix_VolumeChunk(chunk.get(), -1); return static_cast(sdl_volume) / static_cast(MIX_MAX_VOLUME); } else { Log::log("Cannot check volume. Audio device is not open.", Log::WARN); return 0.0f; } } void Chunk::volume(float level) { if (Mix_QuerySpec(nullptr, nullptr, nullptr) != 0) { Mix_VolumeChunk(chunk.get(), convert_volume(level)); } else { Log::log("Cannot set volume. Audio device is not open.", Log::WARN); } } void Chunk::channel_volume(float volume) { int count = Mix_GroupCount(-1); for (int channel = 0; channel < count; channel++) { if (Mix_GetChunk(channel) == chunk.get()) { Mix_Volume(channel, convert_volume(volume)); } } } void Chunk::loop() { loops = -1; } void Chunk::loop(int count) { loops = count; } int Chunk::play(float fade, int channel) { if (enabled()) { /* Play the audio with a fade in time if any was specified. */ int assignment; if (fade <= 0.0f) { assignment = Mix_PlayChannel(channel, chunk.get(), loops); } else { int milliseconds = static_cast(std::round(fade * 1000.0f)); assignment = Mix_FadeInChannel(channel, chunk.get(), loops, milliseconds); } /* Check if the audio is paused on other channels. If so, stop the audio on those channels. */ int count = Mix_GroupCount(-1); for (int index = 0; index < count; index++) { if (index != assignment && Mix_GetChunk(index) == chunk.get() && Mix_Paused(index)) { Mix_HaltChannel(index); } } return assignment; } else { return -1; } } void Chunk::stop(float fade) { int count = Mix_GroupCount(-1); for (int channel = 0; channel < count; channel++) { if (Mix_GetChunk(channel) == chunk.get()) { if (fade <= 0.0f) { Mix_HaltChannel(channel); } else { int milliseconds = static_cast(std::round(fade * 1000.0f)); Mix_FadeOutChannel(channel, milliseconds); } } } } void Chunk::pause() { int count = Mix_GroupCount(-1); for (int channel = 0; channel < count; channel++) { if (Mix_GetChunk(channel) == chunk.get()) { Mix_Pause(channel); } } } void Chunk::resume() { if (enabled()) { int count = Mix_GroupCount(-1); for (int channel = 0; channel < count; channel++) { if (Mix_GetChunk(channel) == chunk.get()) { Mix_Resume(channel); } } } } bool Chunk::playing() const { int count = Mix_GroupCount(-1); for (int channel = 0; channel < count; channel++) { if (Mix_GetChunk(channel) == chunk.get() && Mix_Playing(channel)) { return true; } } return false; } bool Chunk::paused() const { int count = Mix_GroupCount(-1); for (int channel = 0; channel < count; channel++) { if (Mix_GetChunk(channel) == chunk.get() && Mix_Paused(channel)) { return true; } } return false; } bool Chunk::fading() const { int count = Mix_GroupCount(-1); for (int channel = 0; channel < count; channel++) { if (Mix_GetChunk(channel) == chunk.get() && Mix_FadingChannel(channel)) { return true; } } return false; } bool Chunk::enabled(std::optional state) { if (state.has_value()) { _enabled = state.value(); } return _enabled; } Music::Music(const fs::path& path) { load(path); } void Music::load(const fs::path& path) { music = std::shared_ptr(Mix_LoadMUS(path.string().c_str()), Mix_FreeMusic); if (music.get() == nullptr) { Log::sdl_error(); } } void Music::play(int loops) { Mix_PlayMusic(music.get(), loops); }