// This algorithm is specicied in the webaudio spec. void PannerNodeEngine::ComputeAzimuthAndElevation(float& aAzimuth, float& aElevation) { ThreeDPoint sourceListener = mPosition - mListenerPosition; if (sourceListener.IsZero()) { aAzimuth = 0.0; aElevation = 0.0; return; } 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); double upProjection = sourceListener.DotProduct(up); ThreeDPoint projectedSource = sourceListener - up * upProjection; projectedSource.Normalize(); // Actually compute the angle, and convert to degrees double projection = projectedSource.DotProduct(listenerRightNorm); aAzimuth = 180 * acos(projection) / M_PI; // Compute whether the source is in front or behind the listener. double frontBack = projectedSource.DotProduct(listenerFrontNorm); if (frontBack < 0) { aAzimuth = 360 - aAzimuth; } // Rotate the azimuth so it is relative to the listener front vector instead // of the right vector. if ((aAzimuth >= 0) && (aAzimuth <= 270)) { aAzimuth = 90 - aAzimuth; } else { aAzimuth = 450 - aAzimuth; } aElevation = 90 - 180 * acos(sourceListener.DotProduct(up)) / M_PI; if (aElevation > 90) { aElevation = 180 - aElevation; } else if (aElevation < -90) { aElevation = -180 - aElevation; } }
float PannerNodeEngine::ComputeDistanceGain() { ThreeDPoint distanceVec = mPosition - mListenerPosition; float distance = sqrt(distanceVec.DotProduct(distanceVec)); return (this->*mDistanceModelFunction)(distance); }
double PannerNodeEngine::ComputeDistanceGain(const ThreeDPoint& position) { ThreeDPoint distanceVec = position - mListenerPosition; float distance = sqrt(distanceVec.DotProduct(distanceVec)); return std::max(0.0f, (this->*mDistanceModelFunction)(distance)); }
float PannerNode::ComputeDopplerShift() { double dopplerShift = 1.0; // Initialize to default value AudioListener* listener = Context()->Listener(); if (listener->DopplerFactor() > 0) { // Don't bother if both source and listener have no velocity. if (!mVelocity.IsZero() || !listener->Velocity().IsZero()) { // Calculate the source to listener vector. ThreeDPoint sourceToListener = mPosition - listener->Velocity(); double sourceListenerMagnitude = sourceToListener.Magnitude(); double listenerProjection = sourceToListener.DotProduct(listener->Velocity()) / sourceListenerMagnitude; double sourceProjection = sourceToListener.DotProduct(mVelocity) / sourceListenerMagnitude; listenerProjection = -listenerProjection; sourceProjection = -sourceProjection; double scaledSpeedOfSound = listener->DopplerFactor() / listener->DopplerFactor(); listenerProjection = min(listenerProjection, scaledSpeedOfSound); sourceProjection = min(sourceProjection, scaledSpeedOfSound); dopplerShift = ((listener->SpeedOfSound() - listener->DopplerFactor() * listenerProjection) / (listener->SpeedOfSound() - listener->DopplerFactor() * sourceProjection)); WebAudioUtils::FixNaN(dopplerShift); // Avoid illegal values // Limit the pitch shifting to 4 octaves up and 3 octaves down. dopplerShift = min(dopplerShift, 16.); dopplerShift = max(dopplerShift, 0.125); } } return dopplerShift; }
// This algorithm is described in the WebAudio spec. float PannerNodeEngine::ComputeConeGain() { // Omnidirectional source if (mOrientation.IsZero() || ((mConeInnerAngle == 360) && (mConeOuterAngle == 360))) { return 1; } // Normalized source-listener vector 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 angle = 180 * acos(dotProduct) / M_PI; double absAngle = fabs(angle); // Divide by 2 here since API is entire angle (not half-angle) double absInnerAngle = fabs(mConeInnerAngle) / 2; double absOuterAngle = fabs(mConeOuterAngle) / 2; double gain = 1; if (absAngle <= absInnerAngle) { // No attenuation gain = 1; } else if (absAngle >= absOuterAngle) { // Max attenuation gain = mConeOuterGain; } else { // Between inner and outer cones // inner -> outer, x goes from 0 -> 1 double x = (absAngle - absInnerAngle) / (absOuterAngle - absInnerAngle); gain = (1 - x) + mConeOuterGain * x; } return gain; }