259 lines
5.9 KiB
C++
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);
|
|
}
|