Bug 1476744 - Pull values from the AudioListener when computing PannerNode values. r=karlt
authorPaul Adenot <paul@paul.cx>
Tue, 17 Jul 2018 12:01:24 +0200
changeset 427479 11b4729e92ecbf99a27d956321295e6a1309ca05
parent 427478 5a481b37f45fb214cfe17d059aec5e0df6f5a113
child 427480 b3cfdf00c7f26d82137bcd23d5d0eca732152d6b
push id34306
push usercsabou@mozilla.com
push dateFri, 20 Jul 2018 21:41:18 +0000
treeherdermozilla-central@d6a5e8aea651 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskarlt
bugs1476744
milestone63.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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,