spacebox/src/Audio.cpp

259 lines
5.9 KiB
C++

/* +------------------------------------------------------+
____/ \____ /| - Open source game framework licensed to freely use, |
\ / / | copy, modify and sell without restriction |
+--\ ^__^ /--+ | |
| ~/ \~ | | - created for <https://foam.shampoo.ooo> |
| ~~~~~~~~~~~~ | +------------------------------------------------------+
| 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<int>(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_Chunk>(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<float>(sdl_volume) / static_cast<float>(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<int>(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<int>(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<bool> 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_Music>(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);
}