spacebox/src/Audio.cpp

284 lines
6.5 KiB
C++

/* +=======================================================+
____/ \____ /: Open source game framework licensed to freely use, :
\ / / : copy, and modify - created for dank.game :
+==\ ^__^ /==+ : :
: ~/ \~ : : Download at https://open.shampoo.ooo/shampoo/spacebox :
: ~~~~~~~~~~~~ : +=======================================================+
: 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);
}
void sb::audio::pan_channel(int channel, float panning)
{
/* Pan sound by setting the volume of each stereo channel. If there is no panning, each track will be at max
* volume, causing SDL to unregister its panning special effect function if it was previously active. */
int left = 255;
int right = 255;
if (panning < 0.0f)
{
right *= panning + 1.0f;
}
else if (panning > 0.0f)
{
left *= 1.0f - panning;
}
Mix_SetPanning(channel, left, right);
}
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;
}
void Chunk::pan(float panning)
{
_panning = panning;
}
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);
}
}
/* Once channel is assigned, panning can be set */
pan_channel(assignment, _panning);
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)
{
path(_path);
}
void Music::path(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);
}