diff --git a/Source/Engine/Audio/AudioBackend.h b/Source/Engine/Audio/AudioBackend.h index 6f94df859..61d2c223a 100644 --- a/Source/Engine/Audio/AudioBackend.h +++ b/Source/Engine/Audio/AudioBackend.h @@ -41,6 +41,7 @@ private: virtual void Source_TransformChanged(AudioSource* source) = 0; virtual void Source_VolumeChanged(AudioSource* source) = 0; virtual void Source_PitchChanged(AudioSource* source) = 0; + virtual void Source_PanChanged(AudioSource* source) = 0; virtual void Source_IsLoopingChanged(AudioSource* source) = 0; virtual void Source_SpatialSetupChanged(AudioSource* source) = 0; virtual void Source_ClipLoaded(AudioSource* source) = 0; @@ -143,6 +144,11 @@ public: Instance->Source_PitchChanged(source); } + FORCE_INLINE static void PanChanged(AudioSource* source) + { + Instance->Source_PanChanged(source); + } + FORCE_INLINE static void IsLoopingChanged(AudioSource* source) { Instance->Source_IsLoopingChanged(source); diff --git a/Source/Engine/Audio/AudioSource.cpp b/Source/Engine/Audio/AudioSource.cpp index 6ad80f617..2e702cbae 100644 --- a/Source/Engine/Audio/AudioSource.cpp +++ b/Source/Engine/Audio/AudioSource.cpp @@ -45,6 +45,16 @@ void AudioSource::SetPitch(float value) AudioBackend::Source::PitchChanged(this); } +void AudioSource::SetPan(float value) +{ + value = Math::Clamp(value, -1.0f, 1.0f); + if (Math::NearEqual(_pan, value)) + return; + _pan = value; + if (SourceIDs.HasItems()) + AudioBackend::Source::PanChanged(this); +} + void AudioSource::SetIsLooping(bool value) { if (_loop == value) @@ -339,6 +349,7 @@ void AudioSource::Serialize(SerializeStream& stream, const void* otherObj) SERIALIZE(Clip); SERIALIZE_MEMBER(Volume, _volume); SERIALIZE_MEMBER(Pitch, _pitch); + SERIALIZE_MEMBER(Pan, _pan); SERIALIZE_MEMBER(MinDistance, _minDistance); SERIALIZE_MEMBER(Attenuation, _attenuation); SERIALIZE_MEMBER(DopplerFactor, _dopplerFactor); @@ -355,6 +366,7 @@ void AudioSource::Deserialize(DeserializeStream& stream, ISerializeModifier* mod DESERIALIZE(Clip); DESERIALIZE_MEMBER(Volume, _volume); DESERIALIZE_MEMBER(Pitch, _pitch); + DESERIALIZE_MEMBER(Pan, _pan); DESERIALIZE_MEMBER(MinDistance, _minDistance); DESERIALIZE_MEMBER(Attenuation, _attenuation); DESERIALIZE_MEMBER(DopplerFactor, _dopplerFactor); diff --git a/Source/Engine/Audio/AudioSource.h b/Source/Engine/Audio/AudioSource.h index 91383d00f..3b6f32153 100644 --- a/Source/Engine/Audio/AudioSource.h +++ b/Source/Engine/Audio/AudioSource.h @@ -46,6 +46,7 @@ private: Vector3 _prevPos; float _volume; float _pitch; + float _pan = 0.0f; float _minDistance; float _attenuation = 1.0f; float _dopplerFactor = 1.0f; @@ -109,6 +110,20 @@ public: /// API_PROPERTY() void SetPitch(float value); + /// + /// Gets the stereo pan of the played audio (-1 is left speaker, 1 is right speaker, 0 is balanced). The default is 1. Used by non-spatial audio only. + /// + API_PROPERTY(Attributes="EditorOrder(30), DefaultValue(0.0f), Limit(-1.0f, 1.0f), EditorDisplay(\"Audio Source\")") + FORCE_INLINE float GetPan() const + { + return _pan; + } + + /// + /// Sets the stereo pan of the played audio (-1 is left speaker, 1 is right speaker, 0 is balanced). The default is 0. Used by non-spatial audio only. + /// + API_PROPERTY() void SetPan(float value); + /// /// Determines whether the audio clip should loop when it finishes playing. /// diff --git a/Source/Engine/Audio/None/AudioBackendNone.cpp b/Source/Engine/Audio/None/AudioBackendNone.cpp index f35ffd0c8..42a31b775 100644 --- a/Source/Engine/Audio/None/AudioBackendNone.cpp +++ b/Source/Engine/Audio/None/AudioBackendNone.cpp @@ -52,6 +52,10 @@ void AudioBackendNone::Source_PitchChanged(AudioSource* source) { } +void AudioBackendNone::Source_PanChanged(AudioSource* source) +{ +} + void AudioBackendNone::Source_IsLoopingChanged(AudioSource* source) { } diff --git a/Source/Engine/Audio/None/AudioBackendNone.h b/Source/Engine/Audio/None/AudioBackendNone.h index 59bc076c7..1e90b42ac 100644 --- a/Source/Engine/Audio/None/AudioBackendNone.h +++ b/Source/Engine/Audio/None/AudioBackendNone.h @@ -25,6 +25,7 @@ public: void Source_TransformChanged(AudioSource* source) override; void Source_VolumeChanged(AudioSource* source) override; void Source_PitchChanged(AudioSource* source) override; + void Source_PanChanged(AudioSource* source) override; void Source_IsLoopingChanged(AudioSource* source) override; void Source_SpatialSetupChanged(AudioSource* source) override; void Source_ClipLoaded(AudioSource* source) override; diff --git a/Source/Engine/Audio/OpenAL/AudioBackendOAL.cpp b/Source/Engine/Audio/OpenAL/AudioBackendOAL.cpp index 98397c17d..cc5036e25 100644 --- a/Source/Engine/Audio/OpenAL/AudioBackendOAL.cpp +++ b/Source/Engine/Audio/OpenAL/AudioBackendOAL.cpp @@ -164,6 +164,11 @@ namespace ALC alSource3f(sourceID, AL_POSITION, 0.0f, 0.0f, 0.0f); alSource3f(sourceID, AL_VELOCITY, 0.0f, 0.0f, 0.0f); } +#ifdef AL_EXT_STEREO_ANGLES + const float panAngle = source->GetPan() * PI_HALF; + const ALfloat panAngles[2] = { (ALfloat)(PI / 6.0 - panAngle), (ALfloat)(-PI / 6.0 - panAngle) }; // Angles are specified counter-clockwise in radians + alSourcefv(sourceID, AL_STEREO_ANGLES, panAngles); +#endif } // Restore state after Cleanup @@ -392,6 +397,18 @@ void AudioBackendOAL::Source_PitchChanged(AudioSource* source) } } +void AudioBackendOAL::Source_PanChanged(AudioSource* source) +{ +#ifdef AL_EXT_STEREO_ANGLES + const float panAngle = source->GetPan() * PI_HALF; + const ALfloat panAngles[2] = { (ALfloat)(PI / 6.0 - panAngle), (ALfloat)(-PI / 6.0 - panAngle) }; // Angles are specified counter-clockwise in radians + ALC_FOR_EACH_CONTEXT() + const uint32 sourceID = source->SourceIDs[i]; + alSourcefv(sourceID, AL_STEREO_ANGLES, panAngles); + } +#endif +} + void AudioBackendOAL::Source_IsLoopingChanged(AudioSource* source) { const bool loop = source->GetIsLooping() && !source->UseStreaming(); @@ -409,6 +426,9 @@ void AudioBackendOAL::Source_SpatialSetupChanged(AudioSource* source) alSourcei(sourceID, AL_SOURCE_RELATIVE, !is3D); if (is3D) { +#ifdef AL_SOFT_source_spatialize + alSourcei(sourceID, AL_SOURCE_SPATIALIZE_SOFT, AL_TRUE); +#endif alSourcef(sourceID, AL_ROLLOFF_FACTOR, source->GetAttenuation()); alSourcef(sourceID, AL_DOPPLER_FACTOR, source->GetDopplerFactor()); alSourcef(sourceID, AL_REFERENCE_DISTANCE, FLAX_DST_TO_OAL(source->GetMinDistance())); diff --git a/Source/Engine/Audio/OpenAL/AudioBackendOAL.h b/Source/Engine/Audio/OpenAL/AudioBackendOAL.h index 82844a7a2..5590de518 100644 --- a/Source/Engine/Audio/OpenAL/AudioBackendOAL.h +++ b/Source/Engine/Audio/OpenAL/AudioBackendOAL.h @@ -25,6 +25,7 @@ public: void Source_TransformChanged(AudioSource* source) override; void Source_VolumeChanged(AudioSource* source) override; void Source_PitchChanged(AudioSource* source) override; + void Source_PanChanged(AudioSource* source) override; void Source_IsLoopingChanged(AudioSource* source) override; void Source_SpatialSetupChanged(AudioSource* source) override; void Source_ClipLoaded(AudioSource* source) override; diff --git a/Source/Engine/Audio/XAudio2/AudioBackendXAudio2.cpp b/Source/Engine/Audio/XAudio2/AudioBackendXAudio2.cpp index 040ae5e02..6172234f4 100644 --- a/Source/Engine/Audio/XAudio2/AudioBackendXAudio2.cpp +++ b/Source/Engine/Audio/XAudio2/AudioBackendXAudio2.cpp @@ -118,6 +118,7 @@ namespace XAudio2 WAVEFORMATEX Format; XAUDIO2_SEND_DESCRIPTOR Destination; float Pitch; + float Pan; float StartTime; float DopplerFactor; uint64 LastBufferStartSamplesPlayed; @@ -140,6 +141,7 @@ namespace XAudio2 Destination.Flags = 0; Destination.pOutputVoice = nullptr; Pitch = 1.0f; + Pan = 0.0f; StartTime = 0.0f; IsDirty = false; Is3D = false; @@ -399,6 +401,7 @@ void AudioBackendXAudio2::Source_OnAdd(AudioSource* source) aSource->Data.InnerRadius = FLAX_DST_TO_XAUDIO(source->GetMinDistance()); aSource->Is3D = source->Is3D(); aSource->Pitch = source->GetPitch(); + aSource->Pan = source->GetPan(); aSource->DopplerFactor = source->GetDopplerFactor(); aSource->UpdateTransform(source); aSource->UpdateVelocity(source); @@ -455,6 +458,16 @@ void AudioBackendXAudio2::Source_PitchChanged(AudioSource* source) } } +void AudioBackendXAudio2::Source_PanChanged(AudioSource* source) +{ + auto aSource = XAudio2::GetSource(source); + if (aSource) + { + aSource->Pan = source->GetPan(); + aSource->IsDirty = true; + } +} + void AudioBackendXAudio2::Source_IsLoopingChanged(AudioSource* source) { auto aSource = XAudio2::GetSource(source); @@ -812,11 +825,12 @@ void AudioBackendXAudio2::Base_Update() dsp.SrcChannelCount = source.Data.ChannelCount; if (source.Is3D && listener) { + // 3D sound X3DAudioCalculate(XAudio2::X3DInstance, &listener->Data, &source.Data, X3DAUDIO_CALCULATE_MATRIX | X3DAUDIO_CALCULATE_DOPPLER, &dsp); } else { - // Stereo + // 2D sound dsp.DopplerFactor = 1.0f; Platform::MemoryClear(dsp.pMatrixCoefficients, sizeof(matrixCoefficients)); dsp.pMatrixCoefficients[0] = 1.0f; @@ -828,6 +842,13 @@ void AudioBackendXAudio2::Base_Update() { dsp.pMatrixCoefficients[3] = 1.0f; } + const float panLeft = Math::Min(1.0f - source.Pan, 1.0f); + const float panRight = Math::Min(1.0f + source.Pan, 1.0f); + if (source.Format.nChannels >= 2) + { + dsp.pMatrixCoefficients[0] *= panLeft; + dsp.pMatrixCoefficients[3] *= panRight; + } } const float frequencyRatio = dopplerFactor * source.Pitch * dsp.DopplerFactor * source.DopplerFactor; diff --git a/Source/Engine/Audio/XAudio2/AudioBackendXAudio2.h b/Source/Engine/Audio/XAudio2/AudioBackendXAudio2.h index 9fff0af03..1257cb2df 100644 --- a/Source/Engine/Audio/XAudio2/AudioBackendXAudio2.h +++ b/Source/Engine/Audio/XAudio2/AudioBackendXAudio2.h @@ -25,6 +25,7 @@ public: void Source_TransformChanged(AudioSource* source) override; void Source_VolumeChanged(AudioSource* source) override; void Source_PitchChanged(AudioSource* source) override; + void Source_PanChanged(AudioSource* source) override; void Source_IsLoopingChanged(AudioSource* source) override; void Source_SpatialSetupChanged(AudioSource* source) override; void Source_ClipLoaded(AudioSource* source) override;