Merge develop.
commit
46dec92cf5
|
@ -1,12 +1,6 @@
|
|||
Build/
|
||||
[._]*.s[a-v][a-z]
|
||||
[._]*.sw[a-p]
|
||||
[._]s[a-rt-v][a-z]
|
||||
[._]ss[a-gi-z]
|
||||
[._]sw[a-p]
|
||||
|
||||
.vs/
|
||||
.vscode/
|
||||
|
||||
Documentation/Build/
|
||||
Phoenix/ThirdParty/
|
||||
|
|
|
@ -13,3 +13,6 @@
|
|||
[submodule "Phoenix/ThirdParty/json"]
|
||||
path = Phoenix/ThirdParty/json
|
||||
url = https://github.com/nlohmann/json.git
|
||||
[submodule "Phoenix/ThirdParty/openal-soft"]
|
||||
path = Phoenix/ThirdParty/openal-soft
|
||||
url = https://github.com/kcat/openal-soft.git
|
||||
|
|
|
@ -27,3 +27,10 @@ source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}/Source" PREFIX "Source Files" FIL
|
|||
add_dependencies(${PROJECT_NAME} PhoenixAssets-client)
|
||||
add_dependencies(${PROJECT_NAME} PhoenixModules-client)
|
||||
add_dependencies(${PROJECT_NAME} PhoenixSaves-client)
|
||||
|
||||
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
$<TARGET_FILE:OpenAL>
|
||||
$<TARGET_FILE_DIR:${PROJECT_NAME}>/$<TARGET_FILE_NAME:OpenAL>
|
||||
)
|
||||
|
||||
|
|
|
@ -0,0 +1,185 @@
|
|||
// Copyright 2019-20 Genten Studios
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software without
|
||||
// specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Client/Audio/Listener.hpp>
|
||||
#include <Client/Audio/Source.hpp>
|
||||
|
||||
#include <AL/alc.h>
|
||||
#include <minimp3/minimp3.h>
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
namespace phx::audio
|
||||
{
|
||||
/**
|
||||
* @brief Stores data to pass between the Audio engine and Sources.
|
||||
*/
|
||||
struct AudioData
|
||||
{
|
||||
/**
|
||||
* @brief The buffer ID, more for internal use.
|
||||
*/
|
||||
unsigned int buffer;
|
||||
|
||||
/**
|
||||
* @brief The duration of the audio, minutes and seconds.
|
||||
*/
|
||||
Duration duration;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Loads audio and provides an ability to play them.
|
||||
*
|
||||
* This class is to be used in conjunction with the Source class. Currently
|
||||
* only MP3 is supported, future version may include OGG support.
|
||||
*
|
||||
* @paragraph Usage
|
||||
* Before use, you must call ``initialize()`` to produce the OpenAL context,
|
||||
* otherwise a barrage of errors will be displayed in the console. Before
|
||||
* shutdown of the game, make sure all playing Sources are stopped and you
|
||||
* have called the ``teardown()`` method to clear any existing buffers from
|
||||
* memory.
|
||||
*
|
||||
* The destructor must be called before teardown, you can do this by
|
||||
* initializing before a scope, and tearing down after the end of the scope,
|
||||
* while instantiating the Audio class within the closed scope.
|
||||
*
|
||||
* @code
|
||||
* using namespace phx;
|
||||
*
|
||||
* audio::Audio::initialize();
|
||||
*
|
||||
* {
|
||||
* audio::Audio manager;
|
||||
* manager.loadMP3("core:song1", "Assets/Song1.mp3");
|
||||
* manager.loadMP3("core:song2", "Assets/Song2.mp3");
|
||||
*
|
||||
* audio::Source source;
|
||||
* source.setAudioData(manager.getAudioData("core:song1"));
|
||||
* // also possible to do: manager["core:song1"]
|
||||
*
|
||||
* source.enableSpatial(true);
|
||||
* source.setPosition({ 10.f, 0.f, 0.f });
|
||||
*
|
||||
* audio::Listener* listener = manager.getListener();
|
||||
* listener->setGain(...);
|
||||
* listener->setPosition(...);
|
||||
*
|
||||
* source.play();
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
class Audio
|
||||
{
|
||||
public:
|
||||
Audio() = default;
|
||||
|
||||
/**
|
||||
* @brief Deletes all existing buffers registered on this instance.
|
||||
*
|
||||
* Needs to be called before teardown, so consider creating the Audio
|
||||
* object in a closed scope after initialization and teardown.
|
||||
*/
|
||||
~Audio();
|
||||
|
||||
/**
|
||||
* @brief Initializes the audio subsystem, creates an OpenAL context.
|
||||
* @return true on success
|
||||
* @return false on failure.
|
||||
*
|
||||
* This needs to be called before an Audio object is instantiated. This
|
||||
* function creates an OpenAL context on a specific audio device. This
|
||||
* cannot currently be chosen, it is automatic. Eventually, however,
|
||||
* once a UI is added, extra methods will be implemented to allow the
|
||||
* option to select an output device.
|
||||
*/
|
||||
static bool initialize();
|
||||
|
||||
/**
|
||||
* @brief Shuts down the audio subsystem.
|
||||
*
|
||||
* This needs to be called after all Audio objects have been destructed
|
||||
* since the destructors delete all existing buffers. This function
|
||||
* destroys the OpenAL context and closes the audio device - if all
|
||||
* destructors are not called, there will be a list of errors outputted
|
||||
* to console about existing buffers not being deleted.
|
||||
*/
|
||||
static void teardown();
|
||||
|
||||
/**
|
||||
* @brief Loads an MP3 into a buffer, ready for playing in a Source.
|
||||
* @param uniqueName The unique name for the audio clip.
|
||||
* @param filePath The path including the filename of the audio.
|
||||
*
|
||||
* This method does won't cause a segfault if an unexpected file is
|
||||
* provided, however it will not load that file in, and an error will be
|
||||
* logged. This function will load an MP3 file into an OpenAL buffer
|
||||
* ready for use in Sources.
|
||||
*/
|
||||
void loadMP3(const std::string& uniqueName,
|
||||
const std::string& filePath);
|
||||
|
||||
/**
|
||||
* @brief Gets the audio data for a Source to use before playing.
|
||||
* @param uniqueName The unique name of the audio clip being retrieved.
|
||||
* @return The buffer and duration for a valid unique name,
|
||||
* alternatively, a dummy buffer and duration will be returned.
|
||||
*
|
||||
* This function will return a buffer of -1 if an invalid uniqueName is
|
||||
* provided. Since it's an unsigned integer, -1 doesn't really exist,
|
||||
* but checking for it will in an if statement, so check whether the
|
||||
* buffer is -1 before using on an actual Source.
|
||||
*/
|
||||
AudioData getAudioData(const std::string& uniqueName) const;
|
||||
|
||||
// [] operator, like an unordered_map, just redirected to getAudioData,
|
||||
// essentially just a QoL feature.
|
||||
AudioData operator[](const std::string& uniqueName) const
|
||||
{
|
||||
return getAudioData(uniqueName);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief The listener (you, what you hear in your headphones)
|
||||
* @return The listener object.
|
||||
*/
|
||||
Listener* getListener() { return &m_listener; }
|
||||
|
||||
private:
|
||||
static ALCcontext* m_context;
|
||||
static ALCdevice* m_device;
|
||||
static mp3dec_t m_mp3;
|
||||
|
||||
Listener m_listener;
|
||||
|
||||
// maps uniqueName from loadMP3 to a buffer id + duration.
|
||||
std::unordered_map<std::string, AudioData> m_buffers;
|
||||
};
|
||||
} // namespace phx::audio
|
|
@ -0,0 +1,8 @@
|
|||
set(currentDir ${CMAKE_CURRENT_LIST_DIR})
|
||||
set(audioHeaders
|
||||
${currentDir}/Source.hpp
|
||||
${currentDir}/Listener.hpp
|
||||
${currentDir}/Audio.hpp
|
||||
|
||||
PARENT_SCOPE
|
||||
)
|
|
@ -0,0 +1,113 @@
|
|||
// Copyright 2019-20 Genten Studios
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software without
|
||||
// specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Common/Math/Math.hpp>
|
||||
|
||||
namespace phx::audio
|
||||
{
|
||||
/**
|
||||
* @brief The listener, the player side of positional audio.
|
||||
*
|
||||
* This is basically a dummy class. You can only have one listener per
|
||||
* OpenAL context, and it's global. There are no variables, just OpenAL
|
||||
* calls within these functions. The reason this exists is to provide an
|
||||
* easier, more intuitive way to control the player's positional data.
|
||||
*
|
||||
* @paragraph Usage
|
||||
* @code
|
||||
* audio::Audio manager;
|
||||
* manager.loadMP3("core:cow_going_moo_for_1_minute", "Assets/moo.mp3");
|
||||
*
|
||||
* audio::Source source;
|
||||
* source.setAudioData(manager["core:cow_going_moo_for_1_minute"]);
|
||||
*
|
||||
* source.enableSpatial(true);
|
||||
* source.setPosition({ 0.f, 10.f, 0.f });
|
||||
*
|
||||
* source.play();
|
||||
*
|
||||
* audio::Listener* listener = manager.getListener();
|
||||
* listener.setPosition({ 0.f, 0.f, 0.f });
|
||||
*
|
||||
* // pseudo code
|
||||
* thread::sleep(10 seconds);
|
||||
*
|
||||
* listener.setPosition({ 10.f, 0.f, 0.f });
|
||||
*
|
||||
* // pseudo code
|
||||
* thread::sleep(10 seconds);
|
||||
*
|
||||
* same thing continued...
|
||||
*/
|
||||
class Listener
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Sets the gain the listener will use.
|
||||
* @param gain The gain everything should be finally adjusted to.
|
||||
*
|
||||
* Using a scale of 0 to 1.0, this can be used as a way to control
|
||||
* volume.
|
||||
*/
|
||||
void setGain(float gain);
|
||||
|
||||
/**
|
||||
* @brief Sets the position of the listener.
|
||||
* @param position The position of the listener.
|
||||
*/
|
||||
void setPosition(math::vec3 position);
|
||||
|
||||
/**
|
||||
* @brief Sets the velocity of the listener.
|
||||
* @param velocity The velocity of the listener.
|
||||
*
|
||||
* Velocity is a vector quantity, this means it contains direction and
|
||||
* magnitude. The velocity is a combination of speed and the direction
|
||||
* in which it is moving.
|
||||
*
|
||||
* Velocity is also important in the applying the "Doppler Effect".
|
||||
*/
|
||||
void setVelocity(math::vec3 velocity);
|
||||
|
||||
/**
|
||||
* @brief Sets the orientation of the player.
|
||||
* @param direction The direction of the player.
|
||||
* @param up The up vector of the player (what way up they are.)
|
||||
*/
|
||||
void setOrientation(math::vec3 direction, math::vec3 up);
|
||||
|
||||
private:
|
||||
// private constructor because you shouldn't be able to make a Listener.
|
||||
Listener() = default;
|
||||
~Listener() = default;
|
||||
|
||||
friend class Audio;
|
||||
};
|
||||
} // namespace phx::audio
|
|
@ -0,0 +1,234 @@
|
|||
// Copyright 2019-20 Genten Studios
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software without
|
||||
// specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Common/Math/Math.hpp>
|
||||
|
||||
#include <AL/al.h>
|
||||
|
||||
namespace phx::audio
|
||||
{
|
||||
/**
|
||||
* @brief A struct holding the duration of an audio clip, in minutes and
|
||||
* seconds.
|
||||
*
|
||||
* @ref AudioData
|
||||
*/
|
||||
struct Duration
|
||||
{
|
||||
unsigned int minutes;
|
||||
unsigned int seconds;
|
||||
};
|
||||
|
||||
struct AudioData;
|
||||
|
||||
/**
|
||||
* @brief The source of audio.
|
||||
*
|
||||
* This class represents an audio source, like a speaker or a sheep...
|
||||
*
|
||||
* The source initially has spatial support disabled, as well as looping.
|
||||
* This is idea mainly for things like background music, since they can be
|
||||
* looped and shouldn't be affected by the player's position.
|
||||
*
|
||||
* @paragraph Usage
|
||||
* @code
|
||||
* audio::Audio manager;
|
||||
* manager.loadMP3("core:song", "Assets/song.mp3");
|
||||
* manager.loadMP3("core:song2", "Assets/song.mp3");
|
||||
*
|
||||
* audio::Source source;
|
||||
* source.setAudioData(manager["core:song"]);
|
||||
* source.enableSpatial(true);
|
||||
* source.enableLoop(true);
|
||||
*
|
||||
* // one is same as input.
|
||||
* source.setGain(1.f);
|
||||
* source.setPitch(1.f);
|
||||
*
|
||||
* source.play();
|
||||
*
|
||||
* // wait for source to finish playing whatever it's playing.
|
||||
* while (source.status() != Source::State::Stopped)
|
||||
* {
|
||||
* ;
|
||||
* }
|
||||
*
|
||||
* source.setAudioData(manager["core:song2"]);
|
||||
* ...
|
||||
* @endcode
|
||||
*/
|
||||
class Source
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief The current playing state of the Source.
|
||||
*/
|
||||
enum class State
|
||||
{
|
||||
/**
|
||||
* @brief The Source is playing.
|
||||
*/
|
||||
PLAYING = AL_PLAYING,
|
||||
|
||||
/**
|
||||
* @brief The source is paused.
|
||||
*/
|
||||
PAUSED = AL_PAUSED,
|
||||
|
||||
/**
|
||||
* @brief The source is stopped, so either it hasn't been started,
|
||||
* or it has finished.
|
||||
*/
|
||||
STOPPED = AL_STOPPED
|
||||
};
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Constructs a Source object.
|
||||
*/
|
||||
Source();
|
||||
|
||||
/**
|
||||
* @brief Constructs a Source object but with AudioData from the start.
|
||||
* @param data The audio data to use.
|
||||
*
|
||||
* This one is useful if you know exactly what audio needs to be played.
|
||||
* The AudioData can be set later on so using this constructor is not
|
||||
* particularly necessary.
|
||||
*/
|
||||
explicit Source(AudioData data);
|
||||
|
||||
/**
|
||||
* @brief Destroys a Source object.
|
||||
*/
|
||||
~Source();
|
||||
|
||||
/**
|
||||
* @brief Allows for enabling/disabling spatial audio as required.
|
||||
* @param enable Whether to enable spatial audio or not.
|
||||
*
|
||||
* Spatial audio is disabled by default. This is useful for things like
|
||||
* background music, etc... Spatial audio is required for position-based
|
||||
* audio, such as surrounding entities, world audio, etc...
|
||||
*/
|
||||
void enableSpatial(bool enable);
|
||||
|
||||
/**
|
||||
* @brief Allows for enabling/disabling the audio from looping.
|
||||
* @param enable Whether to enable looping or not.
|
||||
*
|
||||
* Looping is disabled by default, since most sounds are a
|
||||
* play-on-demand rather than all the time. Things like ambient audio or
|
||||
* background music are more likely to be looped, so enabling this is
|
||||
* necessary.
|
||||
*/
|
||||
void enableLoop(bool enable);
|
||||
|
||||
/**
|
||||
* @brief Sets the position of the source.
|
||||
* @param pos The position of the source.
|
||||
*/
|
||||
void setPos(math::vec3 pos);
|
||||
|
||||
/**
|
||||
* @brief Sets the direction of the source.
|
||||
* @param direction The direction the source is facing.
|
||||
*/
|
||||
void setDirection(math::vec3 direction);
|
||||
|
||||
/**
|
||||
* @brief Sets the velocity of the source.
|
||||
* @param velocity The velocity of the source.
|
||||
*
|
||||
* Velocity is a vector quantity, this means it contains direction and
|
||||
* magnitude. The velocity is a combination of speed and the direction
|
||||
* in which it is moving.
|
||||
*
|
||||
* Velocity is also important in the applying the "Doppler Effect".
|
||||
*/
|
||||
void setVelocity(math::vec3 velocity);
|
||||
|
||||
/**
|
||||
* @brief Sets the gain of the source.
|
||||
* @param gain The gain the audio will be played back at.
|
||||
*/
|
||||
void setGain(float gain);
|
||||
|
||||
/**
|
||||
* @brief Sets the pitch of the source.
|
||||
* @param pitch The pitch the audio will be played back at.
|
||||
*/
|
||||
void setPitch(float pitch);
|
||||
|
||||
/**
|
||||
* @brief Returns the duration of the audio clip.
|
||||
* @return The duration of the audio clip.
|
||||
*
|
||||
* This can return 0, 0 due to the seconds being an unsigned int,
|
||||
* lacking the precision of a float.
|
||||
*
|
||||
* @todo Increase the resolution of Duration, to allow for telling the
|
||||
* duration of clips shorter than a second.
|
||||
*/
|
||||
Duration getDuration() const;
|
||||
|
||||
/**
|
||||
* @brief Returns the playing status of the source.
|
||||
* @return The status of the source: playing, paused or finished.
|
||||
*/
|
||||
State status() const;
|
||||
|
||||
/**
|
||||
* @brief Sets the audio for the source to use, comes from audio::Audio.
|
||||
* @param buffer The data associated to the audio requested.
|
||||
*
|
||||
* This method should be used with the audio::Audio class.
|
||||
* audio::Audio::getAudioData("core:song") will return an AudioData
|
||||
* object for the source to use and play.
|
||||
*/
|
||||
void setAudioData(AudioData buffer);
|
||||
|
||||
/**
|
||||
* @brief Plays the audio. Calling again will pause.
|
||||
*/
|
||||
void play() const;
|
||||
|
||||
private:
|
||||
// internal variable for OpenAL.
|
||||
unsigned int m_source = 0;
|
||||
|
||||
math::vec3 m_position;
|
||||
math::vec3 m_direction;
|
||||
math::vec3 m_velocity;
|
||||
float m_gain = 1.f;
|
||||
float m_pitch = 1.f;
|
||||
Duration m_duration;
|
||||
};
|
||||
} // namespace phx::audio
|
|
@ -0,0 +1,187 @@
|
|||
// Copyright 2019-20 Genten Studios
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software without
|
||||
// specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include <Client/Audio/Audio.hpp>
|
||||
|
||||
#include <Common/Logger.hpp>
|
||||
|
||||
#include <AL/al.h>
|
||||
#include <AL/alc.h>
|
||||
|
||||
#pragma warning(disable : 4267 4244)
|
||||
#define MINIMP3_ONLY_MP3
|
||||
#define MINIMP3_IMPLEMENTATION
|
||||
#include <minimp3/minimp3.h>
|
||||
#include <minimp3/minimp3_ex.h>
|
||||
|
||||
using namespace phx::audio;
|
||||
|
||||
ALCcontext* Audio::m_context = nullptr;
|
||||
ALCdevice* Audio::m_device = nullptr;
|
||||
mp3dec_t Audio::m_mp3;
|
||||
|
||||
Audio::~Audio()
|
||||
{
|
||||
// deletes all buffers of registered sounds.
|
||||
for (auto& it : m_buffers)
|
||||
{
|
||||
alDeleteBuffers(1, &it.second.buffer);
|
||||
}
|
||||
}
|
||||
|
||||
bool Audio::initialize()
|
||||
{
|
||||
// opens an audio device.
|
||||
m_device = alcOpenDevice(nullptr);
|
||||
if (!m_device)
|
||||
{
|
||||
LOG_FATAL("AUDIO")
|
||||
<< "Could not open an audio device! No audio will be played.";
|
||||
return false;
|
||||
}
|
||||
|
||||
// creates an OpenAL context.
|
||||
m_context = alcCreateContext(m_device, nullptr);
|
||||
if (!m_context)
|
||||
{
|
||||
LOG_FATAL("AUDIO")
|
||||
<< "Could not create an audio context! No audio will be played.";
|
||||
|
||||
alcCloseDevice(m_device);
|
||||
return false;
|
||||
}
|
||||
|
||||
// makes the context current.
|
||||
if (alcMakeContextCurrent(m_context) == ALC_FALSE)
|
||||
{
|
||||
LOG_FATAL("AUDIO")
|
||||
<< "Could not create an audio context! No audio will be played.";
|
||||
|
||||
alcDestroyContext(m_context);
|
||||
alcCloseDevice(m_device);
|
||||
return false;
|
||||
}
|
||||
|
||||
// initializes the MP3 loaded.
|
||||
mp3dec_init(&m_mp3);
|
||||
|
||||
// place some initial values for the listener, this is all changeable and
|
||||
// should be immediately changed to the actual position of the player once
|
||||
// instantiated.
|
||||
ALfloat listenerPos[] = {0.0, 0.0, 0.0};
|
||||
ALfloat listenerVel[] = {0.0, 0.0, 0.0};
|
||||
ALfloat listenerOri[] = {0.0, 0.0, -1.0, 0.0, 1.0, 0.0};
|
||||
alListenerfv(AL_POSITION, listenerPos);
|
||||
alListenerfv(AL_VELOCITY, listenerVel);
|
||||
alListenerfv(AL_ORIENTATION, listenerOri);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Audio::teardown()
|
||||
{
|
||||
// destroys the context and closes the audio device.
|
||||
alcMakeContextCurrent(nullptr);
|
||||
alcDestroyContext(m_context);
|
||||
alcCloseDevice(m_device);
|
||||
}
|
||||
|
||||
void Audio::loadMP3(const std::string& uniqueName, const std::string& filePath)
|
||||
{
|
||||
// checks if uniqueName already exists, if so, then overwrite what's there.
|
||||
const auto it = m_buffers.find(uniqueName);
|
||||
if (it != m_buffers.end())
|
||||
{
|
||||
LOG_WARNING("AUDIO") << "The sound with the unique name: " << uniqueName
|
||||
<< " already exists, but will be overwritten.";
|
||||
}
|
||||
|
||||
// load the mp3 file. will just return empty if the file cannot be read.
|
||||
mp3dec_file_info_t info;
|
||||
if (mp3dec_load(&m_mp3, filePath.c_str(), &info, nullptr, nullptr))
|
||||
{
|
||||
LOG_FATAL("AUDIO")
|
||||
<< "An unexpected (but recoverable) error occurred while loading: "
|
||||
<< filePath;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// calculates the size of the buffer by the amount of samples and the size of each sample.
|
||||
const unsigned int bufferSize = info.samples * sizeof(mp3d_sample_t);
|
||||
|
||||
// gets the duration of the audio, initially as a simple float, but then converted to minutes and seconds.
|
||||
const float durationSeconds = static_cast<float>(bufferSize) / (static_cast<float>(info.avg_bitrate_kbps) * 1024.f);
|
||||
const Duration duration = {
|
||||
static_cast<unsigned int>(durationSeconds / 60.f),
|
||||
static_cast<unsigned int>(durationSeconds) % 60};
|
||||
|
||||
unsigned int buffer;
|
||||
|
||||
// if uniqueName wasn't found, the buffer doesn't exist, otherwise overwrite
|
||||
// the existing buffer to prevent undefined behaviour and wasted memory.
|
||||
if (it == m_buffers.end())
|
||||
{
|
||||
alGenBuffers(1, &buffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer = it->second.buffer;
|
||||
}
|
||||
|
||||
// fill the buffer with the data.
|
||||
alBufferData(buffer,
|
||||
info.channels == 2 ? AL_FORMAT_STEREO16 : AL_FORMAT_MONO16,
|
||||
info.buffer, bufferSize, info.hz);
|
||||
|
||||
// checks for an error.
|
||||
if (alGetError() != AL_NO_ERROR)
|
||||
{
|
||||
LOG_FATAL("AUDIO")
|
||||
<< "An unexpected (but recoverable) error occurred while loading: "
|
||||
<< filePath;
|
||||
|
||||
}
|
||||
|
||||
// adds the buffer ID and duration to the unordered_map, where it is
|
||||
// associated with the uniqueName. this can then be retrieved in
|
||||
// getAudioData or [].
|
||||
m_buffers.insert_or_assign(uniqueName, AudioData {buffer, duration});
|
||||
}
|
||||
|
||||
AudioData Audio::getAudioData(const std::string& uniqueName) const
|
||||
{
|
||||
auto it = m_buffers.find(uniqueName);
|
||||
if (it == m_buffers.end())
|
||||
{
|
||||
// if not found, return some dummy data.
|
||||
return {static_cast<unsigned int>(-1), {0, 0}};
|
||||
}
|
||||
|
||||
return it->second;
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
set(currentDir ${CMAKE_CURRENT_LIST_DIR})
|
||||
set(audioSources
|
||||
${currentDir}/Source.cpp
|
||||
${currentDir}/Listener.cpp
|
||||
${currentDir}/Audio.cpp
|
||||
|
||||
PARENT_SCOPE
|
||||
)
|
|
@ -0,0 +1,52 @@
|
|||
// Copyright 2019-20 Genten Studios
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software without
|
||||
// specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include <Client/Audio/Listener.hpp>
|
||||
|
||||
#include <AL/al.h>
|
||||
|
||||
using namespace phx::audio;
|
||||
|
||||
void Listener::setGain(float gain) { alListenerf(AL_GAIN, gain); }
|
||||
|
||||
void Listener::setPosition(math::vec3 position)
|
||||
{
|
||||
alListenerfv(AL_POSITION, &position.x);
|
||||
}
|
||||
|
||||
void Listener::setVelocity(math::vec3 velocity)
|
||||
{
|
||||
alListenerfv(AL_VELOCITY, &velocity.x);
|
||||
}
|
||||
|
||||
void Listener::setOrientation(math::vec3 direction, math::vec3 up)
|
||||
{
|
||||
float orientation[6] = {direction.x, direction.y, direction.z,
|
||||
up.x, up.y, up.z};
|
||||
alListenerfv(AL_ORIENTATION, orientation);
|
||||
}
|
|
@ -0,0 +1,116 @@
|
|||
// Copyright 2019-20 Genten Studios
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software without
|
||||
// specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include <Client/Audio/Source.hpp>
|
||||
#include <Client/Audio/Audio.hpp>
|
||||
|
||||
#include <AL/al.h>
|
||||
#include <AL/alext.h>
|
||||
|
||||
using namespace phx::audio;
|
||||
|
||||
Source::Source()
|
||||
{
|
||||
// creates an OpenAL source, ready for setting a buffer and playing back.
|
||||
alGenSources(1, &m_source);
|
||||
}
|
||||
|
||||
Source::Source(AudioData data)
|
||||
{
|
||||
// generates an OpenAL source and sets the buffer to be used during
|
||||
// playback.
|
||||
alGenSources(1, &m_source);
|
||||
alSourcei(m_source, AL_BUFFER, data.buffer);
|
||||
m_duration = data.duration;
|
||||
}
|
||||
|
||||
Source::~Source()
|
||||
{
|
||||
// deletes the OpenAL source that was created.
|
||||
// this will NOT delete the buffer (the storage for the actual audio).
|
||||
alDeleteSources(1, &m_source);
|
||||
}
|
||||
|
||||
void Source::enableSpatial(bool enable)
|
||||
{
|
||||
alSourcei(m_source, AL_SOURCE_SPATIALIZE_SOFT,
|
||||
enable ? AL_TRUE : AL_FALSE);
|
||||
}
|
||||
|
||||
void Source::enableLoop(bool enable)
|
||||
{
|
||||
alSourcei(m_source, AL_LOOPING, enable ? AL_TRUE : AL_FALSE);
|
||||
}
|
||||
|
||||
void Source::setPos(math::vec3 pos)
|
||||
{
|
||||
m_position = pos;
|
||||
alSourcefv(m_source, AL_POSITION, &m_position.x);
|
||||
}
|
||||
|
||||
void Source::setDirection(math::vec3 direction)
|
||||
{
|
||||
m_direction = direction;
|
||||
alSourcefv(m_source, AL_DIRECTION, &m_direction.x);
|
||||
}
|
||||
|
||||
void Source::setVelocity(math::vec3 velocity)
|
||||
{
|
||||
m_velocity = velocity;
|
||||
alSourcefv(m_source, AL_VELOCITY, &velocity.x);
|
||||
}
|
||||
|
||||
void Source::setGain(float gain)
|
||||
{
|
||||
m_gain = gain;
|
||||
alSourcef(m_source, AL_GAIN, gain);
|
||||
}
|
||||
|
||||
void Source::setPitch(float pitch)
|
||||
{
|
||||
m_pitch = pitch;
|
||||
alSourcef(m_source, AL_PITCH, pitch);
|
||||
}
|
||||
|
||||
Duration Source::getDuration() const { return m_duration; }
|
||||
|
||||
Source::State Source::status() const
|
||||
{
|
||||
int state;
|
||||
alGetSourcei(m_source, AL_SOURCE_STATE, &state);
|
||||
|
||||
return static_cast<State>(state);
|
||||
}
|
||||
|
||||
void Source::setAudioData(AudioData data)
|
||||
{
|
||||
m_duration = data.duration;
|
||||
alSourcei(m_source, AL_BUFFER, data.buffer);
|
||||
}
|
||||
|
||||
void Source::play() const { alSourcePlay(m_source); }
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1 @@
|
|||
Subproject commit f5e0eef34db3a3ab94b61a2f99f84f078ba947e7
|
Loading…
Reference in New Issue