284 lines
6.5 KiB
C++
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);
|
|
}
|