249 lines
9.2 KiB
C++
249 lines
9.2 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 :/
|
|
+=============*/
|
|
|
|
#pragma once
|
|
|
|
#include <regex>
|
|
#include <map>
|
|
#include <functional>
|
|
#include <algorithm>
|
|
#include <cmath>
|
|
#include <optional>
|
|
#include "SDL.h"
|
|
#include "SDL_mixer.h"
|
|
#include "filesystem.hpp"
|
|
#include "Log.hpp"
|
|
|
|
namespace sb::audio
|
|
{
|
|
|
|
/*!
|
|
* @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);
|
|
|
|
/*!
|
|
* Set a channel's panning effect using a value between -1.0 and 1.0. 0 is neutral. Less than 0 means pan left, and
|
|
* greater than 0 means pan right. This can be called with no arguments to turn off panning.
|
|
*
|
|
* @param panning Amount to pan the channel
|
|
*/
|
|
void pan_channel(int channel, float panning = 0.0f);
|
|
|
|
/*!
|
|
* Load audio from an OGG or WAV file and play it.
|
|
*
|
|
* 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. From the SDL mixer docs:
|
|
*
|
|
* > SDL_mixer has two separate data structures for audio data. One it calls a "chunk," which is meant to be a file
|
|
* > completely decoded into memory up front, and the other it calls "music" which is a file intended to be decoded
|
|
* > on demand.
|
|
*/
|
|
class Chunk
|
|
{
|
|
|
|
private:
|
|
|
|
std::shared_ptr<Mix_Chunk> chunk;
|
|
bool _enabled = true;
|
|
float _panning = 0.0f;
|
|
|
|
/* -1 means loop forever, any other value is the number of times to loop */
|
|
int loops = 0;
|
|
|
|
public:
|
|
|
|
/*!
|
|
* Create an empty audio chunk.
|
|
*/
|
|
Chunk() = default;
|
|
|
|
/*!
|
|
* Create an audio chunk and load it with data from an OGG or WAV file at the given path. If the given path does not contain
|
|
* loadable data, a warning will be printed, and an empty chunk will be created.
|
|
*
|
|
* This will call Chunk::load(const fs::path&) automatically.
|
|
*
|
|
* @param path path to an OGG or WAV file
|
|
*/
|
|
Chunk(const fs::path& path);
|
|
|
|
/*!
|
|
* Load audio data from an OGG or WAV file at the given path, replacing any existing audio data in this object. If the given
|
|
* path does not contain loadable data, a warning will be printed, and existing audio data will not be overwritten.
|
|
*
|
|
* @param path path to an OGG or WAV file
|
|
*/
|
|
void load(const fs::path& path);
|
|
|
|
/*!
|
|
* @return The volume of the audio chunk in the range 0.0 - 1.0
|
|
*/
|
|
float volume() const;
|
|
|
|
/*!
|
|
* @param level Set the volume of the chunk to a value between 0.0 and 1.0
|
|
*/
|
|
void volume(float level);
|
|
|
|
/*!
|
|
* Set the volume of all channels currently playing the audio chunk to a given level. This does not modify the
|
|
* volume of the chunk itself.
|
|
*
|
|
* @param level Volume between 0.0 and 1.0
|
|
*/
|
|
void channel_volume(float level);
|
|
|
|
/*!
|
|
* Set chunk to loop forever in subsequent plays. If this chunk is already playing on a channel, it will not
|
|
* apply to that sound.
|
|
*/
|
|
void loop();
|
|
|
|
/*!
|
|
* Set chunk to loop a certain amount of times in subsequent plays. If this chunk is already playing on a
|
|
* channel, it will not apply to that sound.
|
|
*
|
|
* @param count Number of times audio should loop when played
|
|
*/
|
|
void loop(int count);
|
|
|
|
/*!
|
|
* Set the amount to pan a sound to the left of right in subsequent plays. If this chunk is already playing on a
|
|
* channel, this panning value will not apply to that sound.
|
|
*
|
|
* @param amount Pan stereo sound to the left or right. From -1.0 to 0.0 means pan to the left, and from 0.0
|
|
* to 1.0 means pan to the right. The default of 0.0 means don't pan the sound to either side.
|
|
*/
|
|
void pan(float amount);
|
|
|
|
/*!
|
|
* Play the audio data loaded into this object once on the first available free channel. Optionally, fade in
|
|
* playback over a given amount of time in seconds. If the given chunk has been disabled, using
|
|
* `Chunk::enabled(bool)`, do nothing instead, and return `-1`.
|
|
*
|
|
* This always plays the audio from the beginning even if the audio is paused. To resume, use Chunk::resume()
|
|
* instead. If this audio chunk is paused on other channels, those channels will be stopped.
|
|
*
|
|
* If the audio is already playing on another channel, however, it will continue to play on that channel, and it
|
|
* will be played again from the beginning on this channel.
|
|
*
|
|
* By default, if no channel value is given, a free channel is chosen automatically, and the ID of the channel
|
|
* is returned. A channel ID can be passed to play the sound on a specific channel.
|
|
*
|
|
* @param fade Length of fade in in seconds
|
|
* @param channel The SDL mixer channel to play the sound on. A value of -1 means choose a channel
|
|
* automatically.
|
|
*
|
|
* @return The channel assigned to the audio chunk
|
|
*/
|
|
int play(float fade = 0.0f, int channel = -1);
|
|
|
|
/*!
|
|
* Stop playback of this audio chunk on all channels it is playing on. Optionally fade out over the given amount
|
|
* of time in seconds.
|
|
*
|
|
* @param fade length of fade out in seconds
|
|
*/
|
|
void stop(float fade = 0.0f);
|
|
|
|
/*!
|
|
* Pause audio chunk on all channels it is playing on.
|
|
*/
|
|
void pause();
|
|
|
|
/*!
|
|
* Resume audio chunk on all channels it is playing on.
|
|
*/
|
|
void resume();
|
|
|
|
/*!
|
|
* @return True if the audio is playing on any channel, false otherwise. Note that paused or fading audio is
|
|
* considered to be playing.
|
|
*/
|
|
bool playing() const;
|
|
|
|
/*!
|
|
* @return True if the audio is paused on any channels, false otherwise.
|
|
*/
|
|
bool paused() const;
|
|
|
|
/*!
|
|
* @return True if the audio is fading in or out on any channels, false otherwise.
|
|
*/
|
|
bool fading() const;
|
|
|
|
/*!
|
|
* By default, a chunk object is enabled to play audio. If the chunk is disabled, however, the play function will
|
|
* not be able to be used. This function can be used both to check the state and to set the state.
|
|
*
|
|
* @param state Set to false to disable, true to enable, or omit to check the current state
|
|
* @return True if button is set to enabled
|
|
*/
|
|
bool enabled(std::optional<bool> state = std::nullopt);
|
|
};
|
|
|
|
/*!
|
|
* Stream audio from an OGG or WAV file on a single channel dedicated to music. Only one music object can be playing
|
|
* at a time.
|
|
*
|
|
* This class interfaces with SDL mixer's API and Mix_Music audio data pointer.
|
|
*
|
|
* 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. From SDL mixer's docs:
|
|
*
|
|
* > SDL_mixer has two separate data structures for audio data. One it calls a "chunk," which is meant to be a file
|
|
* > completely decoded into memory up front, and the other it calls "music" which is a file intended to be decoded
|
|
* > on demand.
|
|
*/
|
|
class Music
|
|
{
|
|
|
|
private:
|
|
|
|
std::shared_ptr<Mix_Music> music;
|
|
|
|
public:
|
|
|
|
Music() = default;
|
|
Music(const fs::path& _path);
|
|
void path(const fs::path& _path);
|
|
void play(int loops = -1);
|
|
};
|
|
};
|