From f80ad1377b88b9d3f88a76f1c78b2ac99b04ab7b Mon Sep 17 00:00:00 2001 From: stiffly Date: Mon, 11 Jan 2016 17:52:27 +0100 Subject: [PATCH] Making use of Doppler effect. Added a bunch of events: Pause, stop, continue, variations of play. --- README.md | 3 +- include/Engine/Sound/EContinueSound.h | 17 ++ include/Engine/Sound/EPauseSound.h | 17 ++ include/Engine/Sound/EPlaySound.h | 2 +- include/Engine/Sound/EPlaySoundOnEntity.h | 21 +++ include/Engine/Sound/EPlaySoundOnPosition.h | 26 +++ include/Engine/Sound/EStopSound.h | 17 ++ include/Engine/Sound/SoundSystem.h | 37 ++-- include/Game/Game.h | 4 +- resources/Schema/Components/SoundEmitter.xml | 1 + resources/Schema/Components/SoundEmitter.xsd | 12 +- src/Engine/Sound/SoundSystem.cpp | 179 +++++++++++++++---- src/Game/Game.cpp | 14 +- 13 files changed, 296 insertions(+), 54 deletions(-) create mode 100644 include/Engine/Sound/EContinueSound.h create mode 100644 include/Engine/Sound/EPauseSound.h create mode 100644 include/Engine/Sound/EPlaySoundOnEntity.h create mode 100644 include/Engine/Sound/EPlaySoundOnPosition.h create mode 100644 include/Engine/Sound/EStopSound.h diff --git a/README.md b/README.md index 7c5f2268..7a488299 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,8 @@ Libraries bundled along with binaries for Windows (MSVC14), available as a submo | **[zlib](http://www.zlib.net)** | 1.28 | [zlib License](http://www.zlib.net/zlib_license.html) | | **[libpng](http://www.libpng.org/pub/png/libpng.html)** | 1.6.19 | [libpng License](http://www.libpng.org/pub/png/src/libpng-LICENSE.txt) | | **[Xerces-C++](https://xerces.apache.org/xerces-c)** | 3.1.2 | [Apache License Version 2.0](https://www.apache.org/licenses/LICENSE-2.0) | -| **[ImGui](https://github.com/ocornut/imgui)** | 2015-12-12 | [MIT License](https://github.com/ocornut/imgui/blob/de3a154f3801de22c8e0bd2aeabf663a70c05972/LICENSE) | +| **[ImGui](https://github.com/ocornut/imgui)** | 2015-12-12 | [MIT License](https://github.com/ocornut/imgui/blob/de3a154f3801de22c8e0bd2aeabf663a70c05972/LICENSE) +| **[OpenAL](https://www.openal.org/)** | 1.1 | [OpenAL License](http://www.libpng.org/pub/png/src/libpng-LICENSE.txt) | #### External libraries Libraries that are too big to be bundled with the project. diff --git a/include/Engine/Sound/EContinueSound.h b/include/Engine/Sound/EContinueSound.h new file mode 100644 index 00000000..2c624b0b --- /dev/null +++ b/include/Engine/Sound/EContinueSound.h @@ -0,0 +1,17 @@ +#ifndef Events_ContinueSound_h__ +#define Events_ContinueSound_h__ + +#include "Core/EventBroker.h" +#include "Core/Entity.h" + +namespace Events +{ + +struct ContinueSound : Event +{ + EntityID EmitterID; +}; + +} + +#endif \ No newline at end of file diff --git a/include/Engine/Sound/EPauseSound.h b/include/Engine/Sound/EPauseSound.h new file mode 100644 index 00000000..f9e2e6f0 --- /dev/null +++ b/include/Engine/Sound/EPauseSound.h @@ -0,0 +1,17 @@ +#ifndef Events_PauseSound_h__ +#define Events_PauseSound_h__ + +#include "Core/EventBroker.h" +#include "Core/Entity.h" + +namespace Events +{ + +struct PauseSound : Event +{ + EntityID EmitterID; +}; + +} + +#endif \ No newline at end of file diff --git a/include/Engine/Sound/EPlaySound.h b/include/Engine/Sound/EPlaySound.h index 83fc4629..9c9eab0a 100644 --- a/include/Engine/Sound/EPlaySound.h +++ b/include/Engine/Sound/EPlaySound.h @@ -10,7 +10,7 @@ namespace Events struct PlaySound : Event { std::string FilePath; - EntityID emitter; + EntityID EmitterID; }; } diff --git a/include/Engine/Sound/EPlaySoundOnEntity.h b/include/Engine/Sound/EPlaySoundOnEntity.h new file mode 100644 index 00000000..39dea432 --- /dev/null +++ b/include/Engine/Sound/EPlaySoundOnEntity.h @@ -0,0 +1,21 @@ +#ifndef Events_PlaySoundOnEntity_h__ +#define Events_PlaySoundOnEntity_h__ + +#include +#include "Core/Entity.h" +#include "Core/Event.h" + +namespace Events +{ + +// Plays a sound on an entity with a SoundEmitter component attached. +// Sound behavior is thereby specified in the SoundEmitter component. +struct PlaySoundOnEntity : public Event +{ + EntityID EmitterID = 0; + std::string FilePath = ""; +}; + +} + +#endif \ No newline at end of file diff --git a/include/Engine/Sound/EPlaySoundOnPosition.h b/include/Engine/Sound/EPlaySoundOnPosition.h new file mode 100644 index 00000000..6a6b4d13 --- /dev/null +++ b/include/Engine/Sound/EPlaySoundOnPosition.h @@ -0,0 +1,26 @@ +#ifndef Events_PlaySoundOnPosition_h__ +#define Events_PlaySoundOnPosition_h__ + +#include +#include +#include "Core/Event.h" + +namespace Events +{ + +// Plays a sound on a given position +struct PlaySoundOnPosition : public Event +{ + glm::vec3 Position = glm::vec3(0); + std::string FilePath = ""; + float Gain = 1; + float Pitch = 1; + bool Loop = false; + float MaxDistance = 20; + float RollOffFactor = 1; + float ReferenceDistance = 1; +}; + +} + +#endif \ No newline at end of file diff --git a/include/Engine/Sound/EStopSound.h b/include/Engine/Sound/EStopSound.h new file mode 100644 index 00000000..b6cd37b8 --- /dev/null +++ b/include/Engine/Sound/EStopSound.h @@ -0,0 +1,17 @@ +#ifndef Events_StopSound_h__ +#define Events_StopSound_h__ + +#include "Core/EventBroker.h" +#include "Core/Entity.h" + +namespace Events +{ + +struct StopSound : Event +{ + EntityID EmitterID; +}; + +} + +#endif \ No newline at end of file diff --git a/include/Engine/Sound/SoundSystem.h b/include/Engine/Sound/SoundSystem.h index 5a8225ef..5100a54b 100644 --- a/include/Engine/Sound/SoundSystem.h +++ b/include/Engine/Sound/SoundSystem.h @@ -4,7 +4,7 @@ #include #include "glm/common.hpp" -#include "glm/gtx/rotate_vector.hpp" +#include "glm/gtx/rotate_vector.hpp" // Calculate Up vector #include "OpenAL/al.h" #include "OpenAL/alc.h" @@ -13,9 +13,16 @@ #include "Rendering/RenderQueueFactory.h" // Absolute transform #include "Sound/Sound.h" #include "Sound/EPlaySound.h" +#include "Sound/EPlaySoundOnEntity.h" +#include "Sound/EPlaySoundOnPosition.h" +#include "Sound/EPauseSound.h" +#include "Sound/EStopSound.h" +#include "Sound/EContinueSound.h" + struct Source { + Source() { } Sound* SoundResource; ALuint ALsource; }; @@ -38,32 +45,42 @@ private: void setSourcePos(ALuint source, glm::vec3 pos) { ALfloat spos[3] = { pos.x, pos.y, pos.z }; alSourcefv(source, AL_POSITION, spos); }; void setSourceVel(ALuint source, glm::vec3 vel) { ALfloat svel[3] = { vel.x, vel.y, vel.z }; alSourcefv(source, AL_VELOCITY, svel); }; - bool isPlaying(ALuint source); - // Logic - World* m_World; - EventBroker* m_EventBroker; - void initOpenAL(); void updateEmitters(); void updateListener(); void deleteInactiveEmitters(); void addNewEmitters(); - ALuint createSource(); - void playSound(Source source); - void stopSound(Source source); + Source* createSource(std::string filePath); + void playSound(Source* source); + void stopSound(Source* source); void stopEmitter(EntityID emitter); + void stopEmitters(); + bool isPlaying(ALuint source); + void setSoundProperties(ALuint source, float gain, float pitch, bool loop, float maxDistance, float rollOffFactor, float referenceDistance); // OpenAL system variables ALCdevice* m_ALCdevice = nullptr; ALCcontext* m_ALCcontext = nullptr; // Logic - std::unordered_map m_Sources; + World* m_World; + EventBroker* m_EventBroker; + std::unordered_map m_Sources; // Events EventRelay m_EPlaySound; bool OnPlaySound(const Events::PlaySound &e); + EventRelay m_EPlaySoundOnEntity; + bool OnPlaySoundOnEntity(const Events::PlaySoundOnEntity &e); + EventRelay m_EPlaySoundOnPosition; + bool OnPlaySoundOnPosition(const Events::PlaySoundOnPosition &e); + EventRelay m_EPauseSound; + bool OnPauseSound(const Events::PauseSound &e); + EventRelay m_EStopSound; + bool OnStopSound(const Events::StopSound &e); + EventRelay m_EContinueSound; + bool OnContinueSound(const Events::ContinueSound &e); }; #endif \ No newline at end of file diff --git a/include/Game/Game.h b/include/Game/Game.h index 6b284c74..a4264840 100644 --- a/include/Game/Game.h +++ b/include/Game/Game.h @@ -27,7 +27,9 @@ // Sound #include "Sound/SoundSystem.h" -#include "Sound/EPlaySound.h" +//#include "Sound/EPlaySound.h" +//#include "Sound/EPlaySoundOnEntity.h" +//#include "Sound/EPlaySoundOnPosition.h" class Game diff --git a/resources/Schema/Components/SoundEmitter.xml b/resources/Schema/Components/SoundEmitter.xml index cbcfc440..871919f1 100644 --- a/resources/Schema/Components/SoundEmitter.xml +++ b/resources/Schema/Components/SoundEmitter.xml @@ -2,6 +2,7 @@ Audio/crosscounter.wav 1.0 1.0 + false 20.0 1.0 1.0 diff --git a/resources/Schema/Components/SoundEmitter.xsd b/resources/Schema/Components/SoundEmitter.xsd index 31c18ce9..c734e6ba 100644 --- a/resources/Schema/Components/SoundEmitter.xsd +++ b/resources/Schema/Components/SoundEmitter.xsd @@ -7,15 +7,17 @@ - + The "volume" of the emitter. A value betweeen 0-1 - + The pitch of the emitter. A value betweeen 0-1 - + + If the sound should loop or not. + The distance where there will no longer be any attenuation. - + The rolloff rate of the source. - + The distance that the source will be the loudest. diff --git a/src/Engine/Sound/SoundSystem.cpp b/src/Engine/Sound/SoundSystem.cpp index fd379d50..582ca2db 100644 --- a/src/Engine/Sound/SoundSystem.cpp +++ b/src/Engine/Sound/SoundSystem.cpp @@ -8,9 +8,15 @@ SoundSystem::SoundSystem(World* world, EventBroker* eventBroker) initOpenAL(); alSpeedOfSound(340.29f); // Speed of sound - alDistanceModel(AL_INVERSE_DISTANCE); + alDistanceModel(AL_LINEAR_DISTANCE); + alDopplerFactor(1); EVENT_SUBSCRIBE_MEMBER(m_EPlaySound, &SoundSystem::OnPlaySound); + EVENT_SUBSCRIBE_MEMBER(m_EPlaySoundOnEntity, &SoundSystem::OnPlaySoundOnEntity); + EVENT_SUBSCRIBE_MEMBER(m_EPlaySoundOnPosition, &SoundSystem::OnPlaySoundOnPosition); + EVENT_SUBSCRIBE_MEMBER(m_EStopSound, &SoundSystem::OnStopSound); + EVENT_SUBSCRIBE_MEMBER(m_EPauseSound, &SoundSystem::OnPauseSound); + EVENT_SUBSCRIBE_MEMBER(m_EContinueSound, &SoundSystem::OnContinueSound); } void SoundSystem::initOpenAL() @@ -27,12 +33,31 @@ void SoundSystem::initOpenAL() SoundSystem::~SoundSystem() { + stopEmitters(); // Stopps emitters + deleteInactiveEmitters(); // Deletes stopped emitters + + std::unordered_map::iterator it; + for (it = m_Sources.begin(); it != m_Sources.end(); it++) { + m_World->DeleteEntity((*it).first); + } + m_Sources.clear(); + alcDestroyContext(m_ALCcontext); alcCloseDevice(m_ALCdevice); delete m_ALCcontext; delete m_ALCdevice; } +void SoundSystem::stopEmitters() +{ + std::unordered_map::iterator it; + for (it = m_Sources.begin(); it != m_Sources.end(); it++) { + if (isPlaying((*it).second->ALsource)) { + stopSound((*it).second); + } + } +} + void SoundSystem::Update() { //deleteInactiveEmitters(); // Not tested @@ -44,14 +69,19 @@ void SoundSystem::Update() void SoundSystem::deleteInactiveEmitters() { auto emitterComponents = m_World->GetComponents("SoundEmitter"); - for (auto it = emitterComponents->begin(); it != emitterComponents->end(); it++) { + for (auto it = emitterComponents->begin(); it != emitterComponents->end();) { EntityID emitter = (*it).EntityID; - if (isPlaying(m_Sources[emitter].ALsource)) { // The sound is still playing, do not remove + if (isPlaying(m_Sources[emitter]->ALsource)) { // The sound is still playing, do not remove + it++; continue; } - alDeleteBuffers(1, &m_Sources[emitter].ALsource); - delete m_Sources[emitter].SoundResource; - m_Sources.erase(emitter); + else { + alDeleteBuffers(1, &m_Sources[emitter]->ALsource); + alDeleteSources(1, &m_Sources[emitter]->ALsource); + //delete m_Sources[emitter]->SoundResource; + m_Sources.erase(emitter); + m_World->DeleteEntity(emitter); + } } } @@ -60,12 +90,19 @@ void SoundSystem::addNewEmitters() auto emitterComponents = m_World->GetComponents("SoundEmitter"); for (auto it = emitterComponents->begin(); it != emitterComponents->end(); it++) { EntityID emitter = (*it).EntityID; - std::unordered_map::iterator i; + std::unordered_map::iterator i; i = m_Sources.find(emitter); if (i == m_Sources.end()) { // Did not exist, add it - Source source; - source.ALsource = createSource(); - source.SoundResource = ResourceManager::Load((std::string)(*it)["FilePath"]); + Source* source = createSource((std::string)(*it)["FilePath"]); + setSoundProperties ( + source->ALsource, + (float)(double)(*it)["Gain"], + (float)(double)(*it)["Pitch"], + (bool)(*it)["Loop"], + (float)(double)(*it)["MaxDistance"], + (float)(double)(*it)["RollOffFactor"], + (float)(double)(*it)["ReferenceDistance"] + ); m_Sources[emitter] = source; } } @@ -76,13 +113,26 @@ void SoundSystem::updateEmitters() auto emitterComponents = m_World->GetComponents("SoundEmitter"); for (auto it = emitterComponents->begin(); it != emitterComponents->end(); it++) { EntityID emitter = (*it).EntityID; - std::unordered_map::iterator i; + std::unordered_map::iterator i; i = m_Sources.find(emitter); if (i != m_Sources.end()) { - setSourcePos(m_Sources[emitter].ALsource, RenderQueueFactory::AbsolutePosition(m_World, emitter)); + glm::vec3 previousPos; + alGetSource3f(m_Sources[emitter]->ALsource, AL_POSITION, &previousPos.x, &previousPos.y, &previousPos.z); // Get previous pos + glm::vec3 nextPos = RenderQueueFactory::AbsolutePosition(m_World, emitter); // Get next pos + glm::vec3 velocity = nextPos - previousPos; // Calculate velocity + setSourcePos(m_Sources[emitter]->ALsource, nextPos); // Set next pos + setSourceVel(m_Sources[emitter]->ALsource, velocity); // Set velocity + setSoundProperties( + m_Sources[emitter]->ALsource, + (float)(double)(*it)["Gain"], + (float)(double)(*it)["Pitch"], + (bool)(*it)["Loop"], + (float)(double)(*it)["MaxDistance"], + (float)(double)(*it)["RollOffFactor"], + (float)(double)(*it)["ReferenceDistance"] + ); } // No orientation, emitts in all directions - // Velocity for doppler effect } } @@ -92,47 +142,97 @@ void SoundSystem::updateListener() auto listenerComponents = m_World->GetComponents("Listener"); for (auto it = listenerComponents->begin(); it != listenerComponents->end(); it++) { EntityID listener = (*it).EntityID; - setListenerPos(RenderQueueFactory::AbsolutePosition(m_World, listener)); + glm::vec3 previousPos; + alGetListener3f(AL_POSITION, &previousPos.x, &previousPos.y, &previousPos.z); // Get previous pos + glm::vec3 nextPos = RenderQueueFactory::AbsolutePosition(m_World, listener); // Get next (current) pos + glm::vec3 velocity = nextPos - previousPos; // Calculate velocity + setListenerPos(nextPos); + setListenerVel(velocity); setListenerOri(glm::eulerAngles(RenderQueueFactory::AbsoluteOrientation(m_World, listener))); - // Velocity for doppler effect } } -ALuint SoundSystem::createSource() +Source* SoundSystem::createSource(std::string filePath) { - ALuint source; - alGenSources((ALuint)1, &source); - alSourcei(source, AL_REFERENCE_DISTANCE, 1.0); - alSourcei(source, AL_MAX_DISTANCE, FLT_MAX); + ALuint alSource; + alGenSources((ALuint)1, &alSource); + alSourcei(alSource, AL_REFERENCE_DISTANCE, 1.0); + alSourcei(alSource, AL_MAX_DISTANCE, FLT_MAX); + Source* source = new Source(); + source->ALsource = alSource; + source->SoundResource = ResourceManager::Load(filePath); return source; } -void SoundSystem::playSound(Source source) +void SoundSystem::playSound(Source* source) { - alSourcei(source.ALsource, AL_BUFFER, source.SoundResource->Buffer()); - alSourcePlay(source.ALsource); + alSourcei(source->ALsource, AL_BUFFER, source->SoundResource->Buffer()); + alSourcePlay(source->ALsource); } -void SoundSystem::stopSound(Source source) -{ } +void SoundSystem::stopSound(Source* source) +{ + alSourceStop(source->ALsource); +} void SoundSystem::stopEmitter(EntityID emitter) -{ } +{ + +} bool SoundSystem::OnPlaySound(const Events::PlaySound & e) { - //Sound *sound = ResourceManager::Load(e.FilePath); - //if (sound == nullptr) { - // return false; - //} - //ALuint source = createSource(); - //Source sauce; - //sauce.ALsource = source; - //sauce.SoundResource = sound; - //playSound(sauce); + Source* sauce = createSource(e.FilePath); + playSound(sauce); return false; } +bool SoundSystem::OnPlaySoundOnEntity(const Events::PlaySoundOnEntity & e) +{ + Source* source = createSource(e.FilePath); + m_Sources[e.emitterID] = source; + playSound(source); + return false; +} + +bool SoundSystem::OnPlaySoundOnPosition(const Events::PlaySoundOnPosition & e) +{ + Source* source = createSource(e.FilePath); + auto emitterID = m_World->CreateEntity(); + auto transform = m_World->AttachComponent(emitterID, "Transform"); + (glm::vec3&)transform["Position"] = e.Position; + auto emitter = m_World->AttachComponent(emitterID, "SoundEmitter"); + (float&)(double)emitter["Gain"] = e.Gain; + (float&)(double)emitter["Pitch"] = e.Pitch; + (bool&)emitter["Loop"] = e.Loop; + (float&)(double)emitter["MaxDistance"] = e.MaxDistance; + (float&)(double)emitter["RollOffFactor"] = e.RollOffFactor; + (float&)(double)emitter["ReferenceDistance"] = e.ReferenceDistance; + auto model = m_World->AttachComponent(emitterID, "Model"); + (std::string&)model["Resource"] = "Models/Core/UnitCube.obj"; + m_Sources[emitterID] = source; + playSound(source); + return false; +} + +bool SoundSystem::OnPauseSound(const Events::PauseSound & e) +{ + alSourcePause(m_Sources[e.EmitterID]->ALsource); + return false; +} + +bool SoundSystem::OnStopSound(const Events::StopSound & e) +{ + alSourceStop(m_Sources[e.EmitterID]->ALsource); + return false; +} + +bool SoundSystem::OnContinueSound(const Events::ContinueSound & e) +{ + alSourcePlay(m_Sources[e.EmitterID]->ALsource); + return true; +} + void SoundSystem::setListenerOri(glm::vec3 ori) { glm::vec3 forward = glm::vec3(0.0, 0.0, -1.0); @@ -156,3 +256,12 @@ bool SoundSystem::isPlaying(ALuint source) return (state == AL_PLAYING); } +void SoundSystem::setSoundProperties(ALuint source, float gain, float pitch, bool loop, float maxDistance, float rollOffFactor, float referenceDistance) +{ + alSourcef(source, AL_GAIN, gain); + alSourcef(source, AL_PITCH, pitch); + alSourcei(source, AL_LOOPING, (int)loop); // YOLO + alSourcef(source, AL_MAX_DISTANCE, maxDistance); + alSourcef(source, AL_ROLLOFF_FACTOR, rollOffFactor); + alSourcef(source, AL_REFERENCE_DISTANCE, referenceDistance); +} diff --git a/src/Game/Game.cpp b/src/Game/Game.cpp index 1863fa38..86682591 100644 --- a/src/Game/Game.cpp +++ b/src/Game/Game.cpp @@ -130,8 +130,20 @@ void Game::Tick() bool Game::debugOnInputCommand(const Events::InputCommand & e) { if (e.Command == "PlaySound" && e.Value > 0) { - Events::PlaySound e; + Events::PlaySoundOnEntity e; + e.emitterID = 18; e.FilePath = "Audio/crosscounter.wav"; + //e.emitterID = 18; // rofl + m_EventBroker->Publish(e); + } + if (e.Command == "Reload" && e.Value > 0) { + Events::PauseSound e; + e.EmitterID = 18; + m_EventBroker->Publish(e); + } + if (e.Command == "Crouch" && e.Value > 0) { + Events::ContinueSound e; + e.EmitterID = 18; m_EventBroker->Publish(e); } return true;