Bug 1476744 - Pull values from the AudioListener when computing PannerNode values. r?karlt draft
authorPaul Adenot <paul@paul.cx>
Tue, 17 Jul 2018 12:01:24 +0200
changeset 820818 2272e3d9d2364ca98a9bf2238ad6ce2c3d731b25
parent 820817 a9af028bbe381b4b39b8f32c1dc363544b302a47
push id116943
push userpaul@paul.cx
push dateFri, 20 Jul 2018 13:13:49 +0000
reviewerskarlt
bugs1476744
milestone63.0a1
Bug 1476744 - Pull values from the AudioListener when computing PannerNode values. r?karlt MozReview-Commit-ID: KiaLegWHOZV
dom/media/webaudio/AudioContext.cpp
dom/media/webaudio/AudioContext.h
dom/media/webaudio/AudioListener.cpp
dom/media/webaudio/AudioListener.h
dom/media/webaudio/PannerNode.cpp
dom/media/webaudio/PannerNode.h
--- a/dom/media/webaudio/AudioContext.cpp
+++ b/dom/media/webaudio/AudioContext.cpp
@@ -648,24 +648,16 @@ AudioContext::RegisterActiveNode(AudioNo
 }
 
 void
 AudioContext::UnregisterActiveNode(AudioNode* aNode)
 {
   mActiveNodes.RemoveEntry(aNode);
 }
 
-void
-AudioContext::UnregisterPannerNode(PannerNode* aNode)
-{
-  if (mListener) {
-    mListener->UnregisterPannerNode(aNode);
-  }
-}
-
 uint32_t
 AudioContext::MaxChannelCount() const
 {
   return std::min<uint32_t>(WebAudioUtils::MaxChannelCount,
       mIsOffline ? mNumberOfChannels : CubebUtils::MaxNumberOfChannels());
 }
 
 uint32_t
--- a/dom/media/webaudio/AudioContext.h
+++ b/dom/media/webaudio/AudioContext.h
@@ -304,18 +304,16 @@ public:
   // Nodes unregister when they have finished producing sound for the
   // foreseeable future.
   // Do NOT call UnregisterActiveNode from an AudioNode destructor.
   // If the destructor is called, then the Node has already been unregistered.
   // The destructor may be called during hashtable enumeration, during which
   // unregistering would not be safe.
   void UnregisterActiveNode(AudioNode* aNode);
 
-  void UnregisterPannerNode(PannerNode* aNode);
-
   uint32_t MaxChannelCount() const;
 
   uint32_t ActiveNodeCount() const;
 
   void Mute() const;
   void Unmute() const;
 
   JSObject* GetGlobalJSObject() const;
--- a/dom/media/webaudio/AudioListener.cpp
+++ b/dom/media/webaudio/AudioListener.cpp
@@ -2,27 +2,72 @@
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "AudioListener.h"
 #include "AudioContext.h"
 #include "mozilla/dom/AudioListenerBinding.h"
+#include "MediaStreamGraphImpl.h"
 
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(AudioListener, mContext)
 
 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(AudioListener, AddRef)
 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(AudioListener, Release)
 
+AudioListenerEngine::AudioListenerEngine()
+  : mPosition()
+  , mFrontVector(0., 0., -1.)
+  , mRightVector(1., 0., 0.)
+{
+}
+
+void
+AudioListenerEngine::RecvListenerEngineEvent(
+  AudioListenerEngine::AudioListenerParameter aParameter,
+  const ThreeDPoint& aValue)
+{
+  switch (aParameter) {
+    case AudioListenerParameter::POSITION:
+      mPosition = aValue;
+      break;
+    case AudioListenerParameter::FRONT:
+      mFrontVector = aValue;
+      break;
+    case AudioListenerParameter::RIGHT:
+      mRightVector = aValue;
+      break;
+    default:
+      MOZ_CRASH("Not handled");
+  }
+}
+
+const ThreeDPoint&
+AudioListenerEngine::Position() const
+{
+  return mPosition;
+}
+const ThreeDPoint&
+AudioListenerEngine::FrontVector() const
+{
+  return mFrontVector;
+}
+const ThreeDPoint&
+AudioListenerEngine::RightVector() const
+{
+  return mRightVector;
+}
+
 AudioListener::AudioListener(AudioContext* aContext)
   : mContext(aContext)
+  , mEngine(MakeUnique<AudioListenerEngine>())
   , mPosition()
   , mFrontVector(0., 0., -1.)
   , mRightVector(1., 0., 0.)
 {
   MOZ_ASSERT(aContext);
 }
 
 JSObject*
@@ -52,64 +97,72 @@ AudioListener::SetOrientation(double aX,
   ThreeDPoint right = front.CrossProduct(up);
   if (right.IsZero()) {
     return;
   }
   right.Normalize();
 
   if (!mFrontVector.FuzzyEqual(front)) {
     mFrontVector = front;
-    SendThreeDPointParameterToStream(PannerNode::LISTENER_FRONT_VECTOR, front);
+    SendListenerEngineEvent(AudioListenerEngine::AudioListenerParameter::FRONT,
+                            mFrontVector);
   }
   if (!mRightVector.FuzzyEqual(right)) {
     mRightVector = right;
-    SendThreeDPointParameterToStream(PannerNode::LISTENER_RIGHT_VECTOR, right);
+    SendListenerEngineEvent(AudioListenerEngine::AudioListenerParameter::RIGHT,
+                            mRightVector);
   }
 }
 
 void
-AudioListener::RegisterPannerNode(PannerNode* aPannerNode)
+AudioListener::SetPosition(double aX, double aY, double aZ)
 {
-  mPanners.AppendElement(aPannerNode);
-
-  // Let the panner node know about our parameters
-  aPannerNode->SendThreeDPointParameterToStream(PannerNode::LISTENER_POSITION, mPosition);
-  aPannerNode->SendThreeDPointParameterToStream(PannerNode::LISTENER_FRONT_VECTOR, mFrontVector);
-  aPannerNode->SendThreeDPointParameterToStream(PannerNode::LISTENER_RIGHT_VECTOR, mRightVector);
-}
-
-void AudioListener::UnregisterPannerNode(PannerNode* aPannerNode)
-{
-  mPanners.RemoveElement(aPannerNode);
+  if (WebAudioUtils::FuzzyEqual(mPosition.x, aX) &&
+      WebAudioUtils::FuzzyEqual(mPosition.y, aY) &&
+      WebAudioUtils::FuzzyEqual(mPosition.z, aZ)) {
+    return;
+  }
+  mPosition.x = aX;
+  mPosition.y = aY;
+  mPosition.z = aZ;
+  SendListenerEngineEvent(AudioListenerEngine::AudioListenerParameter::POSITION,
+                          mPosition);
 }
 
 void
-AudioListener::SendDoubleParameterToStream(uint32_t aIndex, double aValue)
+AudioListener::SendListenerEngineEvent(
+  AudioListenerEngine::AudioListenerParameter aParameter,
+  const ThreeDPoint& aValue)
 {
-  for (uint32_t i = 0; i < mPanners.Length(); ++i) {
-    if (mPanners[i]) {
-      mPanners[i]->SendDoubleParameterToStream(aIndex, aValue);
+  class Message final : public ControlMessage
+  {
+  public:
+    Message(AudioListenerEngine* aEngine,
+            AudioListenerEngine::AudioListenerParameter aParameter,
+            const ThreeDPoint& aValue)
+      : ControlMessage(nullptr)
+      , mEngine(aEngine)
+      , mParameter(aParameter)
+      , mValue(aValue)
+    {
     }
-  }
-}
+    void Run() override
+    {
+      mEngine->RecvListenerEngineEvent(mParameter, mValue);
+    }
+    AudioListenerEngine* mEngine;
+    AudioListenerEngine::AudioListenerParameter mParameter;
+    ThreeDPoint mValue;
+  };
 
-void
-AudioListener::SendThreeDPointParameterToStream(uint32_t aIndex, const ThreeDPoint& aValue)
-{
-  for (uint32_t i = 0; i < mPanners.Length(); ++i) {
-    if (mPanners[i]) {
-      mPanners[i]->SendThreeDPointParameterToStream(aIndex, aValue);
-    }
-  }
+  mContext->DestinationStream()->GraphImpl()->AppendMessage(
+    MakeUnique<Message>(mEngine.get(), aParameter, aValue));
 }
 
 size_t
 AudioListener::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
 {
-  size_t amount = aMallocSizeOf(this);
-  // AudioNodes are tracked separately
-  amount += mPanners.ShallowSizeOfExcludingThis(aMallocSizeOf);
-  return amount;
+  return aMallocSizeOf(this);
 }
 
 } // namespace dom
 } // namespace mozilla
 
--- a/dom/media/webaudio/AudioListener.h
+++ b/dom/media/webaudio/AudioListener.h
@@ -16,16 +16,39 @@
 #include "WebAudioUtils.h"
 #include "js/TypeDecls.h"
 #include "mozilla/MemoryReporting.h"
 
 namespace mozilla {
 
 namespace dom {
 
+class AudioListenerEngine final
+{
+public:
+  enum class AudioListenerParameter
+  {
+    POSITION,
+    FRONT, // unit length
+    RIGHT // unit length, orthogonal to FRONT
+  };
+  AudioListenerEngine();
+  void RecvListenerEngineEvent(
+    AudioListenerEngine::AudioListenerParameter aParameter,
+    const ThreeDPoint& aValue);
+  const ThreeDPoint& Position() const;
+  const ThreeDPoint& FrontVector() const;
+  const ThreeDPoint& RightVector() const;
+
+private:
+  ThreeDPoint mPosition;
+  ThreeDPoint mFrontVector;
+  ThreeDPoint mRightVector;
+};
+
 class AudioListener final : public nsWrapperCache
 {
 public:
   explicit AudioListener(AudioContext* aContext);
 
   NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(AudioListener)
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(AudioListener)
 
@@ -33,51 +56,35 @@ public:
 
   AudioContext* GetParentObject() const
   {
     return mContext;
   }
 
   JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
-  void SetPosition(double aX, double aY, double aZ)
-  {
-    if (WebAudioUtils::FuzzyEqual(mPosition.x, aX) &&
-        WebAudioUtils::FuzzyEqual(mPosition.y, aY) &&
-        WebAudioUtils::FuzzyEqual(mPosition.z, aZ)) {
-      return;
-    }
-    mPosition.x = aX;
-    mPosition.y = aY;
-    mPosition.z = aZ;
-    SendThreeDPointParameterToStream(PannerNode::LISTENER_POSITION, mPosition);
-  }
-
-  const ThreeDPoint& Position() const
-  {
-    return mPosition;
-  }
-
+  void SetPosition(double aX, double aY, double aZ);
   void SetOrientation(double aX, double aY, double aZ,
                       double aXUp, double aYUp, double aZUp);
 
-  void RegisterPannerNode(PannerNode* aPannerNode);
-  void UnregisterPannerNode(PannerNode* aPannerNode);
+  const AudioListenerEngine* Engine() { return mEngine.get(); }
 
 private:
-  ~AudioListener() {}
+  void SendListenerEngineEvent(
+    AudioListenerEngine::AudioListenerParameter aParameter,
+    const ThreeDPoint& aValue);
 
-  void SendDoubleParameterToStream(uint32_t aIndex, double aValue);
+  ~AudioListener() = default;
+
   void SendThreeDPointParameterToStream(uint32_t aIndex, const ThreeDPoint& aValue);
 private:
-  friend class PannerNode;
   RefPtr<AudioContext> mContext;
+  const UniquePtr<AudioListenerEngine> mEngine;
   ThreeDPoint mPosition;
   ThreeDPoint mFrontVector;
   ThreeDPoint mRightVector;
-  nsTArray<WeakPtr<PannerNode> > mPanners;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif
 
--- a/dom/media/webaudio/PannerNode.cpp
+++ b/dom/media/webaudio/PannerNode.cpp
@@ -37,20 +37,24 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
 NS_INTERFACE_MAP_END_INHERITING(AudioNode)
 
 NS_IMPL_ADDREF_INHERITED(PannerNode, AudioNode)
 NS_IMPL_RELEASE_INHERITED(PannerNode, AudioNode)
 
 class PannerNodeEngine final : public AudioNodeEngine
 {
 public:
-  explicit PannerNodeEngine(AudioNode* aNode, AudioDestinationNode* aDestination)
+  explicit PannerNodeEngine(AudioNode* aNode,
+                            AudioDestinationNode* aDestination,
+                            const AudioListenerEngine* aListenerEngine)
     : AudioNodeEngine(aNode)
     , mDestination(aDestination->Stream())
-    // Please keep these default values consistent with PannerNode::PannerNode below.
+    , mListenerEngine(aListenerEngine)
+    // Please keep these default values consistent with PannerNode::PannerNode
+    // below.
     , mPanningModelFunction(&PannerNodeEngine::EqualPowerPanningFunction)
     , mDistanceModelFunction(&PannerNodeEngine::InverseGainFunction)
     , mPositionX(0.)
     , mPositionY(0.)
     , mPositionZ(0.)
     , mOrientationX(1.)
     , mOrientationY(0.)
     , mOrientationZ(0.)
@@ -139,19 +143,16 @@ public:
       break;
     default:
       NS_ERROR("Bad PannerNodeEngine Int32Parameter");
     }
   }
   void SetThreeDPointParameter(uint32_t aIndex, const ThreeDPoint& aParam) override
   {
     switch (aIndex) {
-    case PannerNode::LISTENER_POSITION: mListenerPosition = aParam; break;
-    case PannerNode::LISTENER_FRONT_VECTOR: mListenerFrontVector = aParam; break;
-    case PannerNode::LISTENER_RIGHT_VECTOR: mListenerRightVector = aParam; break;
     case PannerNode::POSITION:
       mPositionX.SetValue(aParam.x);
       mPositionY.SetValue(aParam.y);
       mPositionZ.SetValue(aParam.z);
       break;
     case PannerNode::ORIENTATION:
       mOrientationX.SetValue(aParam.x);
       mOrientationY.SetValue(aParam.y);
@@ -250,35 +251,35 @@ public:
     return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
   }
 
   RefPtr<AudioNodeStream> mDestination;
   // This member is set on the main thread, but is not accessed on the rendering
   // thread untile mPanningModelFunction has changed, and this happens strictly
   // later, via a MediaStreamGraph ControlMessage.
   nsAutoPtr<HRTFPanner> mHRTFPanner;
+  // This is set in the ctor, and guaranteed to live longer than this engine:
+  // its lifetime is the same as the AudioContext itself.
+  const AudioListenerEngine* mListenerEngine;
   typedef void (PannerNodeEngine::*PanningModelFunction)(const AudioBlock& aInput, AudioBlock* aOutput, StreamTime tick);
   PanningModelFunction mPanningModelFunction;
   typedef float (PannerNodeEngine::*DistanceModelFunction)(double aDistance);
   DistanceModelFunction mDistanceModelFunction;
   AudioParamTimeline mPositionX;
   AudioParamTimeline mPositionY;
   AudioParamTimeline mPositionZ;
   AudioParamTimeline mOrientationX;
   AudioParamTimeline mOrientationY;
   AudioParamTimeline mOrientationZ;
   double mRefDistance;
   double mMaxDistance;
   double mRolloffFactor;
   double mConeInnerAngle;
   double mConeOuterAngle;
   double mConeOuterGain;
-  ThreeDPoint mListenerPosition;
-  ThreeDPoint mListenerFrontVector;
-  ThreeDPoint mListenerRightVector;
   int mLeftOverData;
 };
 
 PannerNode::PannerNode(AudioContext* aContext)
   : AudioNode(aContext,
               2,
               ChannelCountMode::Clamped_max,
               ChannelInterpretation::Speakers)
@@ -293,29 +294,22 @@ PannerNode::PannerNode(AudioContext* aCo
   , mOrientationZ(new AudioParam(this, PannerNode::ORIENTATIONZ, this->NodeType(), 0.f))
   , mRefDistance(1.)
   , mMaxDistance(10000.)
   , mRolloffFactor(1.)
   , mConeInnerAngle(360.)
   , mConeOuterAngle(360.)
   , mConeOuterGain(0.)
 {
-  mStream = AudioNodeStream::Create(aContext,
-                                    new PannerNodeEngine(this, aContext->Destination()),
-                                    AudioNodeStream::NO_STREAM_FLAGS,
-                                    aContext->Graph());
-  // We should register once we have set up our stream and engine.
-  Context()->Listener()->RegisterPannerNode(this);
-}
-
-PannerNode::~PannerNode()
-{
-  if (Context()) {
-    Context()->UnregisterPannerNode(this);
-  }
+  mStream = AudioNodeStream::Create(
+    aContext,
+    new PannerNodeEngine(
+      this, aContext->Destination(), aContext->Listener()->Engine()),
+    AudioNodeStream::NO_STREAM_FLAGS,
+    aContext->Graph());
 }
 
 /* static */ already_AddRefed<PannerNode>
 PannerNode::Create(AudioContext& aAudioContext,
                    const PannerOptions& aOptions,
                    ErrorResult& aRv)
 {
   if (aAudioContext.CheckClosed(aRv)) {
@@ -370,24 +364,16 @@ PannerNode::SizeOfIncludingThis(MallocSi
 }
 
 JSObject*
 PannerNode::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return PannerNode_Binding::Wrap(aCx, this, aGivenProto);
 }
 
-void PannerNode::DestroyMediaStream()
-{
-  if (Context()) {
-    Context()->UnregisterPannerNode(this);
-  }
-  AudioNode::DestroyMediaStream();
-}
-
 // Those three functions are described in the spec.
 float
 PannerNodeEngine::LinearGainFunction(double aDistance)
 {
   return 1 - mRolloffFactor * (std::max(std::min(aDistance, mMaxDistance), mRefDistance) - mRefDistance) / (mMaxDistance - mRefDistance);
 }
 
 float
@@ -455,20 +441,18 @@ PannerNodeEngine::EqualPowerPanningFunct
     ThreeDPoint position = ConvertAudioParamTimelineTo3DP(mPositionX, mPositionY, mPositionZ, tick);
     ThreeDPoint orientation = ConvertAudioParamTimelineTo3DP(mOrientationX, mOrientationY, mOrientationZ, tick);
     if (!orientation.IsZero()) {
       orientation.Normalize();
     }
 
     // For a stereo source, when both the listener and the panner are in
     // the same spot, and no cone gain is specified, this node is noop.
-    if (inputChannels == 2 &&
-        mListenerPosition ==  position &&
-        mConeInnerAngle == 360 &&
-        mConeOuterAngle == 360) {
+    if (inputChannels == 2 && mListenerEngine->Position() == position &&
+        mConeInnerAngle == 360 && mConeOuterAngle == 360) {
       *aOutput = aInput;
       return;
     }
 
     // The output of this node is always stereo, no matter what the inputs are.
     aOutput->AllocateChannels(2);
 
     ComputeAzimuthAndElevation(position, azimuth, elevation);
@@ -617,28 +601,28 @@ PannerNodeEngine::EqualPowerPanningFunct
     AudioBlockInPlaceScale(outputR, alignedGain);
   }
 }
 
 // This algorithm is specified in the webaudio spec.
 void
 PannerNodeEngine::ComputeAzimuthAndElevation(const ThreeDPoint& position, float& aAzimuth, float& aElevation)
 {
-  ThreeDPoint sourceListener = position - mListenerPosition;
+  ThreeDPoint sourceListener = position - mListenerEngine->Position();
   if (sourceListener.IsZero()) {
     aAzimuth = 0.0;
     aElevation = 0.0;
     return;
   }
 
   sourceListener.Normalize();
 
   // Project the source-listener vector on the x-z plane.
-  const ThreeDPoint& listenerFront = mListenerFrontVector;
-  const ThreeDPoint& listenerRight = mListenerRightVector;
+  const ThreeDPoint& listenerFront = mListenerEngine->FrontVector();
+  const ThreeDPoint& listenerRight = mListenerEngine->RightVector();
   ThreeDPoint up = listenerRight.CrossProduct(listenerFront);
 
   double upProjection = sourceListener.DotProduct(up);
   aElevation = 90 - 180 * acos(upProjection) / M_PI;
 
   if (aElevation > 90) {
     aElevation = 180 - aElevation;
   } else if (aElevation < -90) {
@@ -677,17 +661,17 @@ PannerNodeEngine::ComputeConeGain(const 
                                   const ThreeDPoint& orientation)
 {
   // Omnidirectional source
   if (orientation.IsZero() || ((mConeInnerAngle == 360) && (mConeOuterAngle == 360))) {
     return 1;
   }
 
   // Normalized source-listener vector
-  ThreeDPoint sourceToListener = mListenerPosition - position;
+  ThreeDPoint sourceToListener = mListenerEngine->Position() - position;
   sourceToListener.Normalize();
 
   // Angle between the source orientation vector and the source-listener vector
   double dotProduct = sourceToListener.DotProduct(orientation);
   double angle = 180 * acos(dotProduct) / M_PI;
   double absAngle = fabs(angle);
 
   // Divide by 2 here since API is entire angle (not half-angle)
@@ -709,15 +693,15 @@ PannerNodeEngine::ComputeConeGain(const 
   }
 
   return gain;
 }
 
 double
 PannerNodeEngine::ComputeDistanceGain(const ThreeDPoint& position)
 {
-  ThreeDPoint distanceVec = position - mListenerPosition;
+  ThreeDPoint distanceVec = position - mListenerEngine->Position();
   float distance = sqrt(distanceVec.DotProduct(distanceVec));
   return std::max(0.0f, (this->*mDistanceModelFunction)(distance));
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/media/webaudio/PannerNode.h
+++ b/dom/media/webaudio/PannerNode.h
@@ -36,18 +36,16 @@ public:
   Constructor(const GlobalObject& aGlobal, AudioContext& aAudioContext,
               const PannerOptions& aOptions, ErrorResult& aRv)
   {
     return Create(aAudioContext, aOptions, aRv);
   }
 
   JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
-  void DestroyMediaStream() override;
-
   void SetChannelCount(uint32_t aChannelCount, ErrorResult& aRv) override
   {
     if (aChannelCount > 2) {
       aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
       return;
     }
     AudioNode::SetChannelCount(aChannelCount, aRv);
   }
@@ -218,24 +216,21 @@ public:
     return "PannerNode";
   }
 
   size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override;
   size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override;
 
 private:
   explicit PannerNode(AudioContext* aContext);
-  ~PannerNode();
+  ~PannerNode() = default;
 
   friend class AudioListener;
   friend class PannerNodeEngine;
   enum EngineParameters {
-    LISTENER_POSITION,
-    LISTENER_FRONT_VECTOR, // unit length
-    LISTENER_RIGHT_VECTOR, // unit length, orthogonal to LISTENER_FRONT_VECTOR
     PANNING_MODEL,
     DISTANCE_MODEL,
     POSITION,
     POSITIONX,
     POSITIONY,
     POSITIONZ,
     ORIENTATION, // unit length or zero
     ORIENTATIONX,