From dcdf07191b87f2bcaf4ee6ace7a9dd4b90fb2e69 Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Tue, 27 Jun 2017 11:29:44 -0700 Subject: [PATCH 1/6] Fast acosf() using polynomial approximation --- libraries/shared/src/AudioHelpers.h | 42 +++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/libraries/shared/src/AudioHelpers.h b/libraries/shared/src/AudioHelpers.h index b43764ef5d..d75733e678 100644 --- a/libraries/shared/src/AudioHelpers.h +++ b/libraries/shared/src/AudioHelpers.h @@ -66,6 +66,48 @@ static inline float fastExp2f(float x) { return x * xi.f; } +// +// on x86 architecture, assume that SSE2 is present +// +#if defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || defined(__x86_64__) + +#include +// inline sqrtss, without requiring /fp:fast +static inline float fastSqrtf(float x) { + return _mm_cvtss_f32(_mm_sqrt_ss(_mm_set_ss(x))); +} + +#else + +static inline float fastSqrtf(float x) { + return sqrtf(x); +} + +#endif + +// +// for -1 <= x <= 1, returns acos(x) +// otherwise, returns NaN +// +// abs |error| < 7e-5, smooth +// +static inline float fastAcosf(float x) { + + union { float f; int32_t i; } xi = { x }; + + int32_t sign = xi.i & 0x80000000; + xi.i ^= sign; // fabs(x) + + // compute sqrt(1-x) in parallel + float r = fastSqrtf(1.0f - xi.f); + + // polynomial for acos(x)/sqrt(1-x) over x=[0,1] + xi.f = ((-0.0198439236f * xi.f + 0.0762021306f) * xi.f + -0.212940971f) * xi.f + 1.57079633f; + + xi.f *= r; + return (sign ? 3.141592654f - xi.f : xi.f); +} + // // Quantize a non-negative gain value to the nearest 0.5dB, and pack to a byte. // From ad970c9f5c8ec1712ecd7974f83086df4d8a3bcb Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Tue, 27 Jun 2017 13:45:18 -0700 Subject: [PATCH 2/6] fast compute of HRTF azimuth --- assignment-client/src/audio/AudioMixerSlave.cpp | 17 +++++++++++------ libraries/audio-client/src/AudioClient.cpp | 17 ++++++++++------- 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/assignment-client/src/audio/AudioMixerSlave.cpp b/assignment-client/src/audio/AudioMixerSlave.cpp index 2d800c3561..14b4fdea68 100644 --- a/assignment-client/src/audio/AudioMixerSlave.cpp +++ b/assignment-client/src/audio/AudioMixerSlave.cpp @@ -545,7 +545,6 @@ float computeAzimuth(const AvatarAudioStream& listeningNodeStream, const Positio const glm::vec3& relativePosition) { glm::quat inverseOrientation = glm::inverse(listeningNodeStream.getOrientation()); - // Compute sample delay for the two ears to create phase panning glm::vec3 rotatedSourcePosition = inverseOrientation * relativePosition; // project the rotated source position vector onto the XZ plane @@ -553,11 +552,17 @@ float computeAzimuth(const AvatarAudioStream& listeningNodeStream, const Positio const float SOURCE_DISTANCE_THRESHOLD = 1e-30f; - if (glm::length2(rotatedSourcePosition) > SOURCE_DISTANCE_THRESHOLD) { + float rotatedSourcePositionLength2 = glm::length2(rotatedSourcePosition); + if (rotatedSourcePositionLength2 > SOURCE_DISTANCE_THRESHOLD) { + // produce an oriented angle about the y-axis - return glm::orientedAngle(glm::vec3(0.0f, 0.0f, -1.0f), glm::normalize(rotatedSourcePosition), glm::vec3(0.0f, -1.0f, 0.0f)); - } else { - // there is no distance between listener and source - return no azimuth - return 0; + // return glm::orientedAngle(glm::vec3(0.0f, 0.0f, -1.0f), glm::normalize(rotatedSourcePosition), glm::vec3(0.0f, -1.0f, 0.0f)); + glm::vec3 direction = rotatedSourcePosition * (1.0f / fastSqrtf(rotatedSourcePositionLength2)); + float angle = fastAcosf(glm::clamp(-direction.z, -1.0f, 1.0f)); + return (direction.x < 0.0f) ? -angle : angle; + + } else { + // no azimuth if they are in same spot + return 0.0f; } } diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index fc54a04a5e..27e70e5f01 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -48,6 +48,7 @@ #include "AudioClientLogging.h" #include "AudioLogging.h" +#include "AudioHelpers.h" #include "AudioClient.h" @@ -1688,23 +1689,25 @@ int AudioClient::calculateNumberOfFrameSamples(int numBytes) const { } float AudioClient::azimuthForSource(const glm::vec3& relativePosition) { - // copied from AudioMixer, more or less glm::quat inverseOrientation = glm::inverse(_orientationGetter()); - // compute sample delay for the 2 ears to create phase panning glm::vec3 rotatedSourcePosition = inverseOrientation * relativePosition; - // project the rotated source position vector onto x-y plane + // project the rotated source position vector onto the XZ plane rotatedSourcePosition.y = 0.0f; static const float SOURCE_DISTANCE_THRESHOLD = 1e-30f; - if (glm::length2(rotatedSourcePosition) > SOURCE_DISTANCE_THRESHOLD) { + float rotatedSourcePositionLength2 = glm::length2(rotatedSourcePosition); + if (rotatedSourcePositionLength2 > SOURCE_DISTANCE_THRESHOLD) { // produce an oriented angle about the y-axis - return glm::orientedAngle(glm::vec3(0.0f, 0.0f, -1.0f), glm::normalize(rotatedSourcePosition), glm::vec3(0.0f, -1.0f, 0.0f)); - } else { - + // return glm::orientedAngle(glm::vec3(0.0f, 0.0f, -1.0f), glm::normalize(rotatedSourcePosition), glm::vec3(0.0f, -1.0f, 0.0f)); + glm::vec3 direction = rotatedSourcePosition * (1.0f / fastSqrtf(rotatedSourcePositionLength2)); + float angle = fastAcosf(glm::clamp(-direction.z, -1.0f, 1.0f)); + return (direction.x < 0.0f) ? -angle : angle; + + } else { // no azimuth if they are in same spot return 0.0f; } From a00cd425668ac30b92bc3fe8c612da25c3a94830 Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Tue, 27 Jun 2017 14:01:28 -0700 Subject: [PATCH 3/6] fast compute of HRTF gain --- assignment-client/src/audio/AudioMixerSlave.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/assignment-client/src/audio/AudioMixerSlave.cpp b/assignment-client/src/audio/AudioMixerSlave.cpp index 14b4fdea68..9cfc659c14 100644 --- a/assignment-client/src/audio/AudioMixerSlave.cpp +++ b/assignment-client/src/audio/AudioMixerSlave.cpp @@ -497,13 +497,14 @@ float computeGain(const AvatarAudioStream& listeningNodeStream, const Positional // avatar: apply fixed off-axis attenuation to make them quieter as they turn away } else if (!isEcho && (streamToAdd.getType() == PositionalAudioStream::Microphone)) { glm::vec3 rotatedListenerPosition = glm::inverse(streamToAdd.getOrientation()) * relativePosition; - float angleOfDelivery = glm::angle(glm::vec3(0.0f, 0.0f, -1.0f), - glm::normalize(rotatedListenerPosition)); + + // float angleOfDelivery = glm::angle(glm::vec3(0.0f, 0.0f, -1.0f), glm::normalize(rotatedListenerPosition)); + glm::vec3 direction = glm::normalize(rotatedListenerPosition); + float angleOfDelivery = fastAcosf(glm::clamp(-direction.z, -1.0f, 1.0f)); const float MAX_OFF_AXIS_ATTENUATION = 0.2f; const float OFF_AXIS_ATTENUATION_STEP = (1 - MAX_OFF_AXIS_ATTENUATION) / 2.0f; - float offAxisCoefficient = MAX_OFF_AXIS_ATTENUATION + - (angleOfDelivery * (OFF_AXIS_ATTENUATION_STEP / PI_OVER_TWO)); + float offAxisCoefficient = MAX_OFF_AXIS_ATTENUATION + (angleOfDelivery * (OFF_AXIS_ATTENUATION_STEP / PI_OVER_TWO)); gain *= offAxisCoefficient; } From 0acbdc7755e925e7ba498583ca0252e1a04a10db Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Wed, 28 Jun 2017 10:18:54 -0700 Subject: [PATCH 4/6] CR fix: use predefined PI --- libraries/shared/src/AudioHelpers.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libraries/shared/src/AudioHelpers.h b/libraries/shared/src/AudioHelpers.h index d75733e678..1dcc11af0c 100644 --- a/libraries/shared/src/AudioHelpers.h +++ b/libraries/shared/src/AudioHelpers.h @@ -14,6 +14,8 @@ #include +#include + const int IEEE754_MANT_BITS = 23; const int IEEE754_EXPN_BIAS = 127; @@ -105,7 +107,7 @@ static inline float fastAcosf(float x) { xi.f = ((-0.0198439236f * xi.f + 0.0762021306f) * xi.f + -0.212940971f) * xi.f + 1.57079633f; xi.f *= r; - return (sign ? 3.141592654f - xi.f : xi.f); + return (sign ? PI - xi.f : xi.f); } // From 0dbb7a71f3831b69b86b8960e4e6b07cdd1d9143 Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Wed, 28 Jun 2017 11:32:24 -0700 Subject: [PATCH 5/6] CR fix: comments instead of ref code --- libraries/audio-client/src/AudioClient.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 27e70e5f01..43af7afdef 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -1702,9 +1702,8 @@ float AudioClient::azimuthForSource(const glm::vec3& relativePosition) { if (rotatedSourcePositionLength2 > SOURCE_DISTANCE_THRESHOLD) { // produce an oriented angle about the y-axis - // return glm::orientedAngle(glm::vec3(0.0f, 0.0f, -1.0f), glm::normalize(rotatedSourcePosition), glm::vec3(0.0f, -1.0f, 0.0f)); glm::vec3 direction = rotatedSourcePosition * (1.0f / fastSqrtf(rotatedSourcePositionLength2)); - float angle = fastAcosf(glm::clamp(-direction.z, -1.0f, 1.0f)); + float angle = fastAcosf(glm::clamp(-direction.z, -1.0f, 1.0f)); // UNIT_NEG_Z is "forward" return (direction.x < 0.0f) ? -angle : angle; } else { From a7a049434d733c3b3a8ed112697ce7a9c4824781 Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Wed, 28 Jun 2017 11:32:41 -0700 Subject: [PATCH 6/6] CR fix: comments instead of ref code --- assignment-client/src/audio/AudioMixerSlave.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/assignment-client/src/audio/AudioMixerSlave.cpp b/assignment-client/src/audio/AudioMixerSlave.cpp index 9cfc659c14..ed63bbc298 100644 --- a/assignment-client/src/audio/AudioMixerSlave.cpp +++ b/assignment-client/src/audio/AudioMixerSlave.cpp @@ -498,9 +498,9 @@ float computeGain(const AvatarAudioStream& listeningNodeStream, const Positional } else if (!isEcho && (streamToAdd.getType() == PositionalAudioStream::Microphone)) { glm::vec3 rotatedListenerPosition = glm::inverse(streamToAdd.getOrientation()) * relativePosition; - // float angleOfDelivery = glm::angle(glm::vec3(0.0f, 0.0f, -1.0f), glm::normalize(rotatedListenerPosition)); + // source directivity is based on angle of emission, in local coordinates glm::vec3 direction = glm::normalize(rotatedListenerPosition); - float angleOfDelivery = fastAcosf(glm::clamp(-direction.z, -1.0f, 1.0f)); + float angleOfDelivery = fastAcosf(glm::clamp(-direction.z, -1.0f, 1.0f)); // UNIT_NEG_Z is "forward" const float MAX_OFF_AXIS_ATTENUATION = 0.2f; const float OFF_AXIS_ATTENUATION_STEP = (1 - MAX_OFF_AXIS_ATTENUATION) / 2.0f; @@ -557,9 +557,8 @@ float computeAzimuth(const AvatarAudioStream& listeningNodeStream, const Positio if (rotatedSourcePositionLength2 > SOURCE_DISTANCE_THRESHOLD) { // produce an oriented angle about the y-axis - // return glm::orientedAngle(glm::vec3(0.0f, 0.0f, -1.0f), glm::normalize(rotatedSourcePosition), glm::vec3(0.0f, -1.0f, 0.0f)); glm::vec3 direction = rotatedSourcePosition * (1.0f / fastSqrtf(rotatedSourcePositionLength2)); - float angle = fastAcosf(glm::clamp(-direction.z, -1.0f, 1.0f)); + float angle = fastAcosf(glm::clamp(-direction.z, -1.0f, 1.0f)); // UNIT_NEG_Z is "forward" return (direction.x < 0.0f) ? -angle : angle; } else {