add option for texture resize filter to sprite, add functions for finding loudest mixer channel

This commit is contained in:
ohsqueezy 2023-12-27 19:20:20 -08:00
parent 11c8abcc54
commit 1fff973b46
6 changed files with 105 additions and 31 deletions

View File

@ -2,6 +2,39 @@
using namespace sb::audio; 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) Chunk::Chunk(const fs::path& path)
{ {
load(path); load(path);
@ -18,11 +51,6 @@ void Chunk::load(const fs::path& path)
} }
} }
std::uint8_t Chunk::convert_volume(float volume)
{
return std::clamp(static_cast<int>(std::round(volume * MIX_MAX_VOLUME)), 0, MIX_MAX_VOLUME);
}
float Chunk::volume() const float Chunk::volume() const
{ {
if (Mix_QuerySpec(nullptr, nullptr, nullptr) != 0) if (Mix_QuerySpec(nullptr, nullptr, nullptr) != 0)

View File

@ -24,9 +24,36 @@ namespace sb::audio
{ {
/*! /*!
* Load audio from an OGG or WAV file and play it on multiple channels. * @return channel ID of the SDL mixer channel with the loudest volume setting
*/
int loudest_channel();
/*!
* @return volume of the loudest SDL mixer channel, normalized to between 0.0 and 1.0
*/
float loudest_channel_volume();
/*!
* Convert floating point volume between 0.0 and 1.0 to SDL 8-bit unsigned integer volume between 0 and `MIX_MAX_VOLUME`.
*
* @param volume Volume between 0.0 and 1.0
* @return Same volume represented in the range 0 to `MIX_MAX_VOLUME`
*/
std::uint8_t convert_volume(float volume);
/*!
* Convert volume between 0 and `MIX_MAX_VOLUME` to a float between 0.0 and 1.0.
*
* @param volume Volume between 0 and `MIX_MAX_VOLUME`
* @return Same volume represented as a float between 0.0 and 1.0
*/
float normalize_volume(std::uint8_t volume);
/*!
* Load audio from an OGG or WAV file and play it.
* *
* This class interfaces with SDL mixer's API and its `Mix_Chunk` audio data struct. * Each instance contains an SDL `Mix_Chunk` audio data struct and automatically finds a open channel to play it on when play is requested. The chunk
* can be playing on multiple channels simultaneously.
* *
* There are some differences between how `Mix_Chunk` and `Mix_Music` can be used with the API, most notably that multiple chunks can be playing on * There are some differences between how `Mix_Chunk` and `Mix_Music` can be used with the API, most notably that multiple chunks can be playing on
* multiple channels simultaneously, but only one music object can be playing at a time. This is most likely because `Mix_Music` objects are streamed * multiple channels simultaneously, but only one music object can be playing at a time. This is most likely because `Mix_Music` objects are streamed
@ -42,13 +69,6 @@ namespace sb::audio
/* -1 means loop forever, any other value is the number of times to loop */ /* -1 means loop forever, any other value is the number of times to loop */
int loops = 0; int loops = 0;
/*!
* Convert floating point volume between 0.0 and 1.0 to SDL 8-bit unsigned integer volume between 0 and `MIX_MAX_VOLUME`.
*
* @param volume Volume between 0.0 and 1.0
*/
static std::uint8_t convert_volume(float volume);
public: public:
/*! /*!

View File

@ -104,14 +104,15 @@ namespace sb
* *
* @see sb::Texture::load() * @see sb::Texture::load()
* *
* @param paths list of paths to images * @param paths List of paths to images
* @param scale amount to scale * @param scale Amount to scale
* @param filter Resize filter to use when rendering textures
*/ */
Sprite(std::initializer_list<fs::path> paths, glm::vec2 scale = glm::vec2{1.0f}) : Sprite(scale) Sprite(std::initializer_list<fs::path> paths, glm::vec2 scale = glm::vec2{1.0f}, std::optional<GLint> filter = std::nullopt) : Sprite(scale)
{ {
for (const fs::path& path : paths) for (const fs::path& path : paths)
{ {
this->texture(path); this->texture(path, filter);
} }
} }
@ -120,13 +121,11 @@ namespace sb
* *
* @see ::Sprite(std::initializer_list<fs::path>, glm::vec2) * @see ::Sprite(std::initializer_list<fs::path>, glm::vec2)
* *
* @param path path to an image * @param path Path to an image
* @param scale amount to scale * @param scale Amount to scale
* @param filter Resize filter to use when rendering textures
*/ */
Sprite(const fs::path& path, glm::vec2 scale = glm::vec2{1.0f}) : Sprite({path}, scale) Sprite(const fs::path& path, glm::vec2 scale = glm::vec2{1.0f}, std::optional<GLint> filter = std::nullopt) : Sprite({path}, scale, filter) {};
{
this->texture(path);
}
/*! /*!
* Add a previously constructed sb::Texture to the sprite's plane object. * Add a previously constructed sb::Texture to the sprite's plane object.
@ -144,11 +143,16 @@ namespace sb
* The texture is loaded into GPU memory if the GL context is active. Otherwise, the path is just attached to the texture, * The texture is loaded into GPU memory if the GL context is active. Otherwise, the path is just attached to the texture,
* and it must be loaded with a call to Sprite::load. * and it must be loaded with a call to Sprite::load.
* *
* @param path path to an image * @param path Path to an image
* @param filter Resize filter to use when rendering the texture
*/ */
void texture(const fs::path& path) void texture(const fs::path& path, std::optional<GLint> filter = std::nullopt)
{ {
sb::Texture texture; sb::Texture texture;
if (filter.has_value())
{
texture.filter(filter.value());
}
if (SDL_GL_GetCurrentContext() != nullptr) if (SDL_GL_GetCurrentContext() != nullptr)
{ {
texture.load(path); texture.load(path);

View File

@ -8,6 +8,7 @@
#include "Model.hpp" #include "Model.hpp"
#include "Color.hpp" #include "Color.hpp"
#include "Log.hpp" #include "Log.hpp"
#include "Texture.hpp"
namespace sb namespace sb
{ {

View File

@ -23,12 +23,17 @@ void Texture::associate(fs::path path)
this->path = path; this->path = path;
} }
void Texture::filter(GLint value)
{
_filter = value;
}
void Texture::generate() void Texture::generate()
{ {
GLObject::generate(glGenTextures); GLObject::generate(glGenTextures);
} }
void Texture::generate(glm::vec2 size, GLenum format, GLint filter) void Texture::generate(glm::vec2 size, GLenum format, std::optional<GLint> filter_value)
{ {
/* Only generate a new texture ID and reallocate memory if the current texture ID hasn't been registered by this object as having identically /* Only generate a new texture ID and reallocate memory if the current texture ID hasn't been registered by this object as having identically
* sized memory with the same format. */ * sized memory with the same format. */
@ -44,8 +49,12 @@ void Texture::generate(glm::vec2 size, GLenum format, GLint filter)
} }
/* Set the resizing algorithm of this texture */ /* Set the resizing algorithm of this texture */
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter); if (!filter_value.has_value())
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter); {
filter_value = _filter;
}
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter_value.value());
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter_value.value());
/* Set the texture wrap of this texture */ /* Set the texture wrap of this texture */
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);

View File

@ -44,6 +44,10 @@ namespace sb
std::optional<glm::vec2> _size; std::optional<glm::vec2> _size;
std::optional<GLenum> _format; std::optional<GLenum> _format;
/* The filter will be applied using glTexParameter whenever Texture::generate(glm::vec2, GLenum, std::optional<GLint>) is called without a filter
* specified. */
GLint _filter = GL_NEAREST;
public: public:
/*! /*!
@ -66,6 +70,14 @@ namespace sb
*/ */
void associate(fs::path path); void associate(fs::path path);
/*!
* Set the resize filter of the texture. This must be set before Texture::generate(glm::vec2, GLenum, std::optional<GLint>) is called for it
* to be applied.
*
* @param value Resize filter to use (see `glTexParameter` for options)
*/
void filter(GLint value);
/*! /*!
* Forward the GL texture generate function to the base class * Forward the GL texture generate function to the base class
*/ */
@ -76,9 +88,9 @@ namespace sb
* *
* @param size Width and height of the texture in texels * @param size Width and height of the texture in texels
* @param format Sized internal format to be used to store texture data (for example, `GL_RGBA8`, `GL_RGB8`) * @param format Sized internal format to be used to store texture data (for example, `GL_RGBA8`, `GL_RGB8`)
* @param filter Resize function to use (see `glTexParameter`) * @param filter Resize function to use (see `glTexParameter`). Overrides the value of Texture::filter.
*/ */
void generate(glm::vec2 size, GLenum format = GL_RGBA8, GLint filter = GL_NEAREST); void generate(glm::vec2 size, GLenum format = GL_RGBA8, std::optional<GLint> filter = std::nullopt);
/*! /*!
* Load a texture from the path that was previously set. * Load a texture from the path that was previously set.