diff --git a/src/Audio.cpp b/src/Audio.cpp index 9763c7d..345ab6b 100644 --- a/src/Audio.cpp +++ b/src/Audio.cpp @@ -2,6 +2,39 @@ 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(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); @@ -18,11 +51,6 @@ void Chunk::load(const fs::path& path) } } -std::uint8_t Chunk::convert_volume(float volume) -{ - return std::clamp(static_cast(std::round(volume * MIX_MAX_VOLUME)), 0, MIX_MAX_VOLUME); -} - float Chunk::volume() const { if (Mix_QuerySpec(nullptr, nullptr, nullptr) != 0) diff --git a/src/Audio.hpp b/src/Audio.hpp index 7fe0a43..655f041 100644 --- a/src/Audio.hpp +++ b/src/Audio.hpp @@ -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 * 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 */ 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: /*! diff --git a/src/Sprite.hpp b/src/Sprite.hpp index db79036..ab26c58 100644 --- a/src/Sprite.hpp +++ b/src/Sprite.hpp @@ -104,14 +104,15 @@ namespace sb * * @see sb::Texture::load() * - * @param paths list of paths to images - * @param scale amount to scale + * @param paths List of paths to images + * @param scale Amount to scale + * @param filter Resize filter to use when rendering textures */ - Sprite(std::initializer_list paths, glm::vec2 scale = glm::vec2{1.0f}) : Sprite(scale) + Sprite(std::initializer_list paths, glm::vec2 scale = glm::vec2{1.0f}, std::optional filter = std::nullopt) : Sprite(scale) { for (const fs::path& path : paths) { - this->texture(path); + this->texture(path, filter); } } @@ -120,13 +121,11 @@ namespace sb * * @see ::Sprite(std::initializer_list, glm::vec2) * - * @param path path to an image - * @param scale amount to scale + * @param path Path to an image + * @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) - { - this->texture(path); - } + Sprite(const fs::path& path, glm::vec2 scale = glm::vec2{1.0f}, std::optional filter = std::nullopt) : Sprite({path}, scale, filter) {}; /*! * 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, * 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 filter = std::nullopt) { sb::Texture texture; + if (filter.has_value()) + { + texture.filter(filter.value()); + } if (SDL_GL_GetCurrentContext() != nullptr) { texture.load(path); diff --git a/src/Text.hpp b/src/Text.hpp index 1ec41e5..41ee71f 100644 --- a/src/Text.hpp +++ b/src/Text.hpp @@ -8,6 +8,7 @@ #include "Model.hpp" #include "Color.hpp" #include "Log.hpp" +#include "Texture.hpp" namespace sb { diff --git a/src/Texture.cpp b/src/Texture.cpp index c4aadb8..85db7c4 100644 --- a/src/Texture.cpp +++ b/src/Texture.cpp @@ -23,12 +23,17 @@ void Texture::associate(fs::path path) this->path = path; } +void Texture::filter(GLint value) +{ + _filter = value; +} + void Texture::generate() { GLObject::generate(glGenTextures); } -void Texture::generate(glm::vec2 size, GLenum format, GLint filter) +void Texture::generate(glm::vec2 size, GLenum format, std::optional 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 * 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 */ - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter); + if (!filter_value.has_value()) + { + 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 */ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); diff --git a/src/Texture.hpp b/src/Texture.hpp index 69a5e7a..eb54f33 100644 --- a/src/Texture.hpp +++ b/src/Texture.hpp @@ -44,6 +44,10 @@ namespace sb std::optional _size; std::optional _format; + /* The filter will be applied using glTexParameter whenever Texture::generate(glm::vec2, GLenum, std::optional) is called without a filter + * specified. */ + GLint _filter = GL_NEAREST; + public: /*! @@ -66,6 +70,14 @@ namespace sb */ void associate(fs::path path); + /*! + * Set the resize filter of the texture. This must be set before Texture::generate(glm::vec2, GLenum, std::optional) 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 */ @@ -76,9 +88,9 @@ namespace sb * * @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 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 filter = std::nullopt); /*! * Load a texture from the path that was previously set.