void PannerNode::calculateAzimuthElevation(double* outAzimuth, double* outElevation) { double azimuth = 0.0; // Calculate the source-listener vector FloatPoint3D listenerPosition = listener()->position(); FloatPoint3D sourceListener = m_position - listenerPosition; // normalize() does nothing if the length of |sourceListener| is zero. sourceListener.normalize(); // Align axes FloatPoint3D listenerFront = listener()->orientation(); FloatPoint3D listenerUp = listener()->upVector(); FloatPoint3D listenerRight = listenerFront.cross(listenerUp); listenerRight.normalize(); FloatPoint3D listenerFrontNorm = listenerFront; listenerFrontNorm.normalize(); FloatPoint3D up = listenerRight.cross(listenerFrontNorm); float upProjection = sourceListener.dot(up); FloatPoint3D projectedSource = sourceListener - upProjection * up; projectedSource.normalize(); azimuth = 180.0 * acos(projectedSource.dot(listenerRight)) / piDouble; fixNANs(azimuth); // avoid illegal values // Source in front or behind the listener double frontBack = projectedSource.dot(listenerFrontNorm); if (frontBack < 0.0) azimuth = 360.0 - azimuth; // Make azimuth relative to "front" and not "right" listener vector if ((azimuth >= 0.0) && (azimuth <= 270.0)) azimuth = 90.0 - azimuth; else azimuth = 450.0 - azimuth; // Elevation double elevation = 90.0 - 180.0 * acos(sourceListener.dot(up)) / piDouble; fixNANs(elevation); // avoid illegal values if (elevation > 90.0) elevation = 180.0 - elevation; else if (elevation < -90.0) elevation = -180.0 - elevation; if (outAzimuth) *outAzimuth = azimuth; if (outElevation) *outElevation = elevation; }
double PannerNode::calculateDopplerRate() { double dopplerShift = 1.0; double dopplerFactor = listener()->dopplerFactor(); if (dopplerFactor > 0.0) { double speedOfSound = listener()->speedOfSound(); const FloatPoint3D &sourceVelocity = m_velocity; const FloatPoint3D &listenerVelocity = listener()->velocity(); // Don't bother if both source and listener have no velocity bool sourceHasVelocity = !sourceVelocity.isZero(); bool listenerHasVelocity = !listenerVelocity.isZero(); if (sourceHasVelocity || listenerHasVelocity) { // Calculate the source to listener vector FloatPoint3D listenerPosition = listener()->position(); FloatPoint3D sourceToListener = m_position - listenerPosition; double sourceListenerMagnitude = sourceToListener.length(); if (!sourceListenerMagnitude) { // Source and listener are at the same position. Skip the computation of the doppler // shift, and just return the cached value. dopplerShift = m_cachedDopplerRate; } else { double listenerProjection = sourceToListener.dot(listenerVelocity) / sourceListenerMagnitude; double sourceProjection = sourceToListener.dot(sourceVelocity) / sourceListenerMagnitude; listenerProjection = -listenerProjection; sourceProjection = -sourceProjection; double scaledSpeedOfSound = speedOfSound / dopplerFactor; listenerProjection = std::min(listenerProjection, scaledSpeedOfSound); sourceProjection = std::min(sourceProjection, scaledSpeedOfSound); dopplerShift = ((speedOfSound - dopplerFactor * listenerProjection) / (speedOfSound - dopplerFactor * sourceProjection)); fixNANs(dopplerShift); // avoid illegal values // Limit the pitch shifting to 4 octaves up and 3 octaves down. if (dopplerShift > 16.0) dopplerShift = 16.0; else if (dopplerShift < 0.125) dopplerShift = 0.125; } } } return dopplerShift; }
float PannerNode::dopplerRate(ContextRenderLock& r) { double dopplerShift = 1.0; // FIXME: optimize for case when neither source nor listener has changed... double dopplerFactor = listener(r)->dopplerFactor(); if (dopplerFactor > 0.0) { double speedOfSound = listener(r)->speedOfSound(); const FloatPoint3D &sourceVelocity = m_velocity; const FloatPoint3D &listenerVelocity = listener(r)->velocity(); // Don't bother if both source and listener have no velocity bool sourceHasVelocity = !sourceVelocity.isZero(); bool listenerHasVelocity = !listenerVelocity.isZero(); if (sourceHasVelocity || listenerHasVelocity) { // Calculate the source to listener vector FloatPoint3D listenerPosition = listener(r)->position(); FloatPoint3D sourceToListener = m_position - listenerPosition; double sourceListenerMagnitude = sourceToListener.length(); double listenerProjection = sourceToListener.dot(listenerVelocity) / sourceListenerMagnitude; double sourceProjection = sourceToListener.dot(sourceVelocity) / sourceListenerMagnitude; listenerProjection = -listenerProjection; sourceProjection = -sourceProjection; double scaledSpeedOfSound = speedOfSound / dopplerFactor; listenerProjection = min(listenerProjection, scaledSpeedOfSound); sourceProjection = min(sourceProjection, scaledSpeedOfSound); dopplerShift = ((speedOfSound - dopplerFactor * listenerProjection) / (speedOfSound - dopplerFactor * sourceProjection)); fixNANs(dopplerShift); // avoid illegal values // Limit the pitch shifting to 4 octaves up and 3 octaves down. if (dopplerShift > 16.0) dopplerShift = 16.0; else if (dopplerShift < 0.125) dopplerShift = 0.125; } } return static_cast<float>(dopplerShift); }
bool RotateTransformOperation::shareSameAxis(const RotateTransformOperation* from, const RotateTransformOperation* to, FloatPoint3D* axis, double* fromAngle, double* toAngle) { *axis = FloatPoint3D(0, 0, 1); *fromAngle = 0; *toAngle = 0; if (!from && !to) return true; bool fromZero = !from || from->axis().isZero(); bool toZero = !to || to->axis().isZero(); if (fromZero && toZero) return true; if (fromZero) { *axis = to->axis(); *toAngle = to->angle(); return true; } if (toZero) { *axis = from->axis(); *fromAngle = from->angle(); return true; } FloatPoint3D fromAxis = from->axis(); FloatPoint3D toAxis = to->axis(); double fromSquared = fromAxis.lengthSquared(); double toSquared = toAxis.lengthSquared(); double dot = fromAxis.dot(toAxis); double error = std::abs(1 - (dot * dot) / (fromSquared * toSquared)); if (error > angleEpsilon) return false; *axis = from->axis(); *fromAngle = from->angle(); *toAngle = to->angle(); return true; }
double ConeEffect::gain(FloatPoint3D sourcePosition, FloatPoint3D sourceOrientation, FloatPoint3D listenerPosition) { if (sourceOrientation.isZero() || ((m_innerAngle == 360.0) && (m_outerAngle == 360.0))) return 1.0; // no cone specified - unity gain // Normalized source-listener vector FloatPoint3D sourceToListener = listenerPosition - sourcePosition; sourceToListener.normalize(); FloatPoint3D normalizedSourceOrientation = sourceOrientation; normalizedSourceOrientation.normalize(); // Angle between the source orientation vector and the source-listener vector double dotProduct = sourceToListener.dot(normalizedSourceOrientation); double angle = 180.0 * acos(dotProduct) / piDouble; double absAngle = fabs(angle); // Divide by 2.0 here since API is entire angle (not half-angle) double absInnerAngle = fabs(m_innerAngle) / 2.0; double absOuterAngle = fabs(m_outerAngle) / 2.0; double gain = 1.0; if (absAngle <= absInnerAngle) // No attenuation gain = 1.0; else if (absAngle >= absOuterAngle) // Max attenuation gain = m_outerGain; else { // Between inner and outer cones // inner -> outer, x goes from 0 -> 1 double x = (absAngle - absInnerAngle) / (absOuterAngle - absInnerAngle); gain = (1.0 - x) + m_outerGain * x; } return gain; }
void PannerNode::getAzimuthElevation(double* outAzimuth, double* outElevation) { // FIXME: we should cache azimuth and elevation (if possible), so we only re-calculate if a change has been made. double azimuth = 0.0; // Calculate the source-listener vector FloatPoint3D listenerPosition = listener()->position(); FloatPoint3D sourceListener = m_position - listenerPosition; if (sourceListener.isZero()) { // degenerate case if source and listener are at the same point *outAzimuth = 0.0; *outElevation = 0.0; return; } sourceListener.normalize(); // Align axes FloatPoint3D listenerFront = listener()->orientation(); FloatPoint3D listenerUp = listener()->upVector(); FloatPoint3D listenerRight = listenerFront.cross(listenerUp); listenerRight.normalize(); FloatPoint3D listenerFrontNorm = listenerFront; listenerFrontNorm.normalize(); FloatPoint3D up = listenerRight.cross(listenerFrontNorm); float upProjection = sourceListener.dot(up); FloatPoint3D projectedSource = sourceListener - upProjection * up; projectedSource.normalize(); azimuth = 180.0 * acos(projectedSource.dot(listenerRight)) / piDouble; fixNANs(azimuth); // avoid illegal values // Source in front or behind the listener double frontBack = projectedSource.dot(listenerFrontNorm); if (frontBack < 0.0) azimuth = 360.0 - azimuth; // Make azimuth relative to "front" and not "right" listener vector if ((azimuth >= 0.0) && (azimuth <= 270.0)) azimuth = 90.0 - azimuth; else azimuth = 450.0 - azimuth; // Elevation double elevation = 90.0 - 180.0 * acos(sourceListener.dot(up)) / piDouble; fixNANs(elevation); // avoid illegal values if (elevation > 90.0) elevation = 180.0 - elevation; else if (elevation < -90.0) elevation = -180.0 - elevation; if (outAzimuth) *outAzimuth = azimuth; if (outElevation) *outElevation = elevation; }