b=907986 normalize orientation vectors early and keep existing state if directions are undefined r=padenot

Normalizing the AudioListener front orientation vectors before taking their
cross product avoids the possibility of overflow.

The panning effect and the azimuth and elevation calculation in the Web Audio
spec becomes undefined with linearly dependent listener vectors, so keep
existing state in these situations.

PannerNode orientation is normalized for consistency, but zero is permitted
for this vector because the sound cone algorithm in the Web Audio specifies
behavior for this case.

--HG--
extra : transplant_source : %DA%C7e%E7%90%14%AF%EA%08%94x%C1%A2g%F1%9A%EE%16%EB%29
This commit is contained in:
Karl Tomlinson 2013-09-04 21:20:59 +12:00
parent b88e8922d4
commit 2a2ab79432
6 changed files with 74 additions and 53 deletions

View File

@ -19,8 +19,8 @@ NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(AudioListener, Release)
AudioListener::AudioListener(AudioContext* aContext)
: mContext(aContext)
, mPosition()
, mOrientation(0., 0., -1.)
, mUpVector(0., 1., 0.)
, mFrontVector(0., 0., -1.)
, mRightVector(1., 0., 0.)
, mVelocity()
, mDopplerFactor(1.)
, mSpeedOfSound(343.3) // meters/second
@ -35,6 +35,40 @@ AudioListener::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
return AudioListenerBinding::Wrap(aCx, aScope, this);
}
void
AudioListener::SetOrientation(double aX, double aY, double aZ,
double aXUp, double aYUp, double aZUp)
{
ThreeDPoint front(aX, aY, aZ);
// The panning effect and the azimuth and elevation calculation in the Web
// Audio spec becomes undefined with linearly dependent vectors, so keep
// existing state in these situations.
if (front.IsZero()) {
return;
}
// Normalize before using CrossProduct() to avoid overflow.
front.Normalize();
ThreeDPoint up(aXUp, aYUp, aZUp);
if (up.IsZero()) {
return;
}
up.Normalize();
ThreeDPoint right = front.CrossProduct(up);
if (right.IsZero()) {
return;
}
right.Normalize();
if (!mFrontVector.FuzzyEqual(front)) {
mFrontVector = front;
SendThreeDPointParameterToStream(PannerNode::LISTENER_FRONT_VECTOR, front);
}
if (!mRightVector.FuzzyEqual(right)) {
mRightVector = right;
SendThreeDPointParameterToStream(PannerNode::LISTENER_RIGHT_VECTOR, right);
}
}
void
AudioListener::RegisterPannerNode(PannerNode* aPannerNode)
{
@ -42,8 +76,8 @@ AudioListener::RegisterPannerNode(PannerNode* aPannerNode)
// Let the panner node know about our parameters
aPannerNode->SendThreeDPointParameterToStream(PannerNode::LISTENER_POSITION, mPosition);
aPannerNode->SendThreeDPointParameterToStream(PannerNode::LISTENER_ORIENTATION, mOrientation);
aPannerNode->SendThreeDPointParameterToStream(PannerNode::LISTENER_UPVECTOR, mUpVector);
aPannerNode->SendThreeDPointParameterToStream(PannerNode::LISTENER_FRONT_VECTOR, mFrontVector);
aPannerNode->SendThreeDPointParameterToStream(PannerNode::LISTENER_RIGHT_VECTOR, mRightVector);
aPannerNode->SendThreeDPointParameterToStream(PannerNode::LISTENER_VELOCITY, mVelocity);
aPannerNode->SendDoubleParameterToStream(PannerNode::LISTENER_DOPPLER_FACTOR, mDopplerFactor);
aPannerNode->SendDoubleParameterToStream(PannerNode::LISTENER_SPEED_OF_SOUND, mSpeedOfSound);

View File

@ -84,25 +84,7 @@ public:
}
void SetOrientation(double aX, double aY, double aZ,
double aXUp, double aYUp, double aZUp)
{
if (WebAudioUtils::FuzzyEqual(mOrientation.x, aX) &&
WebAudioUtils::FuzzyEqual(mOrientation.y, aY) &&
WebAudioUtils::FuzzyEqual(mOrientation.z, aZ) &&
WebAudioUtils::FuzzyEqual(mUpVector.x, aX) &&
WebAudioUtils::FuzzyEqual(mUpVector.y, aY) &&
WebAudioUtils::FuzzyEqual(mUpVector.z, aZ)) {
return;
}
mOrientation.x = aX;
mOrientation.y = aY;
mOrientation.z = aZ;
mUpVector.x = aXUp;
mUpVector.y = aYUp;
mUpVector.z = aZUp;
SendThreeDPointParameterToStream(PannerNode::LISTENER_ORIENTATION, mOrientation);
SendThreeDPointParameterToStream(PannerNode::LISTENER_UPVECTOR, mUpVector);
}
double aXUp, double aYUp, double aZUp);
const ThreeDPoint& Velocity() const
{
@ -135,8 +117,8 @@ private:
friend class PannerNode;
nsRefPtr<AudioContext> mContext;
ThreeDPoint mPosition;
ThreeDPoint mOrientation;
ThreeDPoint mUpVector;
ThreeDPoint mFrontVector;
ThreeDPoint mRightVector;
ThreeDPoint mVelocity;
double mDopplerFactor;
double mSpeedOfSound;

View File

@ -109,8 +109,8 @@ public:
{
switch (aIndex) {
case PannerNode::LISTENER_POSITION: mListenerPosition = aParam; break;
case PannerNode::LISTENER_ORIENTATION: mListenerOrientation = aParam; break;
case PannerNode::LISTENER_UPVECTOR: mListenerUpVector = aParam; break;
case PannerNode::LISTENER_FRONT_VECTOR: mListenerFrontVector = aParam; break;
case PannerNode::LISTENER_RIGHT_VECTOR: mListenerRightVector = aParam; break;
case PannerNode::LISTENER_VELOCITY: mListenerVelocity = aParam; break;
case PannerNode::POSITION: mPosition = aParam; break;
case PannerNode::ORIENTATION: mOrientation = aParam; break;
@ -178,8 +178,8 @@ public:
double mConeOuterAngle;
double mConeOuterGain;
ThreeDPoint mListenerPosition;
ThreeDPoint mListenerOrientation;
ThreeDPoint mListenerUpVector;
ThreeDPoint mListenerFrontVector;
ThreeDPoint mListenerRightVector;
ThreeDPoint mListenerVelocity;
double mListenerDopplerFactor;
double mListenerSpeedOfSound;
@ -376,7 +376,7 @@ PannerNodeEngine::DistanceAndConeGain(AudioChunk* aChunk, float aGain)
AudioBufferInPlaceScale(samples, channelCount, aGain);
}
// This algorithm is specicied in the webaudio spec.
// This algorithm is specified in the webaudio spec.
void
PannerNodeEngine::ComputeAzimuthAndElevation(float& aAzimuth, float& aElevation)
{
@ -391,14 +391,9 @@ PannerNodeEngine::ComputeAzimuthAndElevation(float& aAzimuth, float& aElevation)
sourceListener.Normalize();
// Project the source-listener vector on the x-z plane.
ThreeDPoint& listenerFront = mListenerOrientation;
ThreeDPoint listenerRightNorm = listenerFront.CrossProduct(mListenerUpVector);
listenerRightNorm.Normalize();
ThreeDPoint listenerFrontNorm(listenerFront);
listenerFrontNorm.Normalize();
ThreeDPoint up = listenerRightNorm.CrossProduct(listenerFrontNorm);
const ThreeDPoint& listenerFront = mListenerFrontVector;
const ThreeDPoint& listenerRight = mListenerRightVector;
ThreeDPoint up = listenerRight.CrossProduct(listenerFront);
double upProjection = sourceListener.DotProduct(up);
@ -406,11 +401,11 @@ PannerNodeEngine::ComputeAzimuthAndElevation(float& aAzimuth, float& aElevation)
projectedSource.Normalize();
// Actually compute the angle, and convert to degrees
double projection = projectedSource.DotProduct(listenerRightNorm);
double projection = projectedSource.DotProduct(listenerRight);
aAzimuth = 180 * acos(projection) / M_PI;
// Compute whether the source is in front or behind the listener.
double frontBack = projectedSource.DotProduct(listenerFrontNorm);
double frontBack = projectedSource.DotProduct(listenerFront);
if (frontBack < 0) {
aAzimuth = 360 - aAzimuth;
}
@ -444,11 +439,8 @@ PannerNodeEngine::ComputeConeGain()
ThreeDPoint sourceToListener = mListenerPosition - mPosition;
sourceToListener.Normalize();
ThreeDPoint normalizedSourceOrientation = mOrientation;
normalizedSourceOrientation.Normalize();
// Angle between the source orientation vector and the source-listener vector
double dotProduct = sourceToListener.DotProduct(normalizedSourceOrientation);
double dotProduct = sourceToListener.DotProduct(mOrientation);
double angle = 180 * acos(dotProduct) / M_PI;
double absAngle = fabs(angle);

View File

@ -120,14 +120,14 @@ public:
void SetOrientation(double aX, double aY, double aZ)
{
if (WebAudioUtils::FuzzyEqual(mOrientation.x, aX) &&
WebAudioUtils::FuzzyEqual(mOrientation.y, aY) &&
WebAudioUtils::FuzzyEqual(mOrientation.z, aZ)) {
ThreeDPoint orientation(aX, aY, aZ);
if (!orientation.IsZero()) {
orientation.Normalize();
}
if (mOrientation.FuzzyEqual(orientation)) {
return;
}
mOrientation.x = aX;
mOrientation.y = aY;
mOrientation.z = aZ;
mOrientation = orientation;
SendThreeDPointParameterToStream(ORIENTATION, mOrientation);
}
@ -233,15 +233,15 @@ private:
friend class PannerNodeEngine;
enum EngineParameters {
LISTENER_POSITION,
LISTENER_ORIENTATION,
LISTENER_UPVECTOR,
LISTENER_FRONT_VECTOR, // unit length
LISTENER_RIGHT_VECTOR, // unit length, orthogonal to LISTENER_FRONT_VECTOR
LISTENER_VELOCITY,
LISTENER_DOPPLER_FACTOR,
LISTENER_SPEED_OF_SOUND,
PANNING_MODEL,
DISTANCE_MODEL,
POSITION,
ORIENTATION,
ORIENTATION, // unit length or zero
VELOCITY,
REF_DISTANCE,
MAX_DISTANCE,

View File

@ -9,11 +9,20 @@
*/
#include "ThreeDPoint.h"
#include "WebAudioUtils.h"
namespace mozilla {
namespace dom {
bool
ThreeDPoint::FuzzyEqual(const ThreeDPoint& other)
{
return WebAudioUtils::FuzzyEqual(x, other.x) &&
WebAudioUtils::FuzzyEqual(y, other.y) &&
WebAudioUtils::FuzzyEqual(z, other.z);
}
ThreeDPoint operator-(const ThreeDPoint& lhs, const ThreeDPoint& rhs)
{
return ThreeDPoint(lhs.x - rhs.x, lhs.y - rhs.y, lhs.z - rhs.z);

View File

@ -68,6 +68,10 @@ struct ThreeDPoint {
{
return x == 0 && y == 0 && z == 0;
}
// For comparing two vectors of close to unit magnitude.
bool FuzzyEqual(const ThreeDPoint& other);
double x, y, z;
private: