Bug 1322883 - AudioNode constructors - part 0 - AnalyserNode, r=padenot
authorAndrea Marchesini <amarchesini@mozilla.com>
Thu, 15 Dec 2016 19:24:41 +0100
changeset 326031 339a11acf17154434a4b6e19bb2fc57f36796030
parent 326030 a1b114f7412fed1ec7bbe22f9effebbbb6afc383
child 326032 64e8b5cc289f501bb46335bae47d723fc56cf950
push id31084
push userphilringnalda@gmail.com
push dateFri, 16 Dec 2016 01:47:54 +0000
treeherdermozilla-central@63b447888a64 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspadenot
bugs1322883
milestone53.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 1322883 - AudioNode constructors - part 0 - AnalyserNode, r=padenot
dom/media/webaudio/AnalyserNode.cpp
dom/media/webaudio/AnalyserNode.h
dom/media/webaudio/AudioContext.cpp
dom/media/webaudio/AudioContext.h
dom/media/webaudio/AudioNode.cpp
dom/media/webaudio/AudioNode.h
dom/media/webaudio/test/test_analyserNode.html
dom/webidl/AnalyserNode.webidl
dom/webidl/AudioNode.webidl
--- a/dom/media/webaudio/AnalyserNode.cpp
+++ b/dom/media/webaudio/AnalyserNode.cpp
@@ -96,16 +96,55 @@ public:
   virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override
   {
     return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
   }
 
   uint32_t mChunksToProcess = 0;
 };
 
+/* static */ already_AddRefed<AnalyserNode>
+AnalyserNode::Create(AudioContext& aAudioContext,
+                     const AnalyserOptions& aOptions,
+                     ErrorResult& aRv)
+{
+  if (aAudioContext.CheckClosed(aRv)) {
+    return nullptr;
+  }
+
+  RefPtr<AnalyserNode> analyserNode = new AnalyserNode(&aAudioContext);
+
+  analyserNode->Initialize(aOptions, aRv);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
+  }
+
+  analyserNode->SetFftSize(aOptions.mFftSize, aRv);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
+  }
+
+  analyserNode->SetMinDecibels(aOptions.mMinDecibels, aRv);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
+  }
+
+  analyserNode->SetMaxDecibels(aOptions.mMaxDecibels, aRv);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
+  }
+
+  analyserNode->SetSmoothingTimeConstant(aOptions.mSmoothingTimeConstant, aRv);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
+  }
+
+  return analyserNode.forget();
+}
+
 AnalyserNode::AnalyserNode(AudioContext* aContext)
   : AudioNode(aContext,
               1,
               ChannelCountMode::Max,
               ChannelInterpretation::Speakers)
   , mAnalysisBlock(2048)
   , mMinDecibels(-100.)
   , mMaxDecibels(-30.)
--- a/dom/media/webaudio/AnalyserNode.h
+++ b/dom/media/webaudio/AnalyserNode.h
@@ -10,26 +10,36 @@
 #include "AudioNode.h"
 #include "FFTBlock.h"
 #include "AlignedTArray.h"
 
 namespace mozilla {
 namespace dom {
 
 class AudioContext;
+struct AnalyserOptions;
 
 class AnalyserNode final : public AudioNode
 {
 public:
-  explicit AnalyserNode(AudioContext* aContext);
+  static already_AddRefed<AnalyserNode>
+  Create(AudioContext& aAudioContext, const AnalyserOptions& aOptions,
+         ErrorResult& aRv);
 
   NS_DECL_ISUPPORTS_INHERITED
 
   virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
+  static already_AddRefed<AnalyserNode>
+  Constructor(const GlobalObject& aGlobal, AudioContext& aAudioContext,
+              const AnalyserOptions& aOptions, ErrorResult& aRv)
+  {
+    return Create(aAudioContext, aOptions, aRv);
+  }
+
   void GetFloatFrequencyData(const Float32Array& aArray);
   void GetByteFrequencyData(const Uint8Array& aArray);
   void GetFloatTimeDomainData(const Float32Array& aArray);
   void GetByteTimeDomainData(const Uint8Array& aArray);
   uint32_t FftSize() const
   {
     return mAnalysisBlock.FFTSize();
   }
@@ -57,28 +67,29 @@ public:
   virtual const char* NodeType() const override
   {
     return "AnalyserNode";
   }
 
   virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override;
   virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override;
 
-protected:
-  ~AnalyserNode() {}
+private:
+  ~AnalyserNode() = default;
 
-private:
   friend class AnalyserNodeEngine;
   void AppendChunk(const AudioChunk& aChunk);
   bool AllocateBuffer();
   bool FFTAnalysis();
   void ApplyBlackmanWindow(float* aBuffer, uint32_t aSize);
   void GetTimeDomainData(float* aData, size_t aLength);
 
 private:
+  explicit AnalyserNode(AudioContext* aContext);
+
   FFTBlock mAnalysisBlock;
   nsTArray<AudioChunk> mChunks;
   double mMinDecibels;
   double mMaxDecibels;
   double mSmoothingTimeConstant;
   size_t mCurrentChunk = 0;
   AlignedTArray<float> mOutputBuffer;
 };
--- a/dom/media/webaudio/AudioContext.cpp
+++ b/dom/media/webaudio/AudioContext.cpp
@@ -335,22 +335,17 @@ AudioContext::CreateScriptProcessor(uint
     new ScriptProcessorNode(this, aBufferSize, aNumberOfInputChannels,
                             aNumberOfOutputChannels);
   return scriptProcessor.forget();
 }
 
 already_AddRefed<AnalyserNode>
 AudioContext::CreateAnalyser(ErrorResult& aRv)
 {
-  if (CheckClosed(aRv)) {
-    return nullptr;
-  }
-
-  RefPtr<AnalyserNode> analyserNode = new AnalyserNode(this);
-  return analyserNode.forget();
+  return AnalyserNode::Create(*this, AnalyserOptions(), aRv);
 }
 
 already_AddRefed<StereoPannerNode>
 AudioContext::CreateStereoPanner(ErrorResult& aRv)
 {
   if (CheckClosed(aRv)) {
     return nullptr;
   }
--- a/dom/media/webaudio/AudioContext.h
+++ b/dom/media/webaudio/AudioContext.h
@@ -318,28 +318,28 @@ public:
 
   void OnStateChanged(void* aPromise, AudioContextState aNewState);
 
   BasicWaveFormCache* GetBasicWaveFormCache();
 
   IMPL_EVENT_HANDLER(mozinterruptbegin)
   IMPL_EVENT_HANDLER(mozinterruptend)
 
+  bool CheckClosed(ErrorResult& aRv);
+
 private:
   void DisconnectFromWindow();
   void RemoveFromDecodeQueue(WebAudioDecodeJob* aDecodeJob);
   void ShutdownDecoder();
 
   size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
   NS_DECL_NSIMEMORYREPORTER
 
   friend struct ::mozilla::WebAudioDecodeJob;
 
-  bool CheckClosed(ErrorResult& aRv);
-
   nsTArray<MediaStream*> GetAllStreams() const;
 
 private:
   // Each AudioContext has an id, that is passed down the MediaStreams that
   // back the AudioNodes, so we can easily compute the set of all the
   // MediaStreams for a given context, on the MediasStreamGraph side.
   const AudioContextId mId;
   // Note that it's important for mSampleRate to be initialized before
--- a/dom/media/webaudio/AudioNode.cpp
+++ b/dom/media/webaudio/AudioNode.cpp
@@ -67,16 +67,38 @@ AudioNode::~AudioNode()
   MOZ_ASSERT(mOutputParams.IsEmpty());
   MOZ_ASSERT(!mStream,
              "The webaudio-node-demise notification must have been sent");
   if (mContext) {
     mContext->UnregisterNode(this);
   }
 }
 
+void
+AudioNode::Initialize(const AudioNodeOptions& aOptions, ErrorResult& aRv)
+{
+  if (aOptions.mChannelCount.WasPassed()) {
+    SetChannelCount(aOptions.mChannelCount.Value(), aRv);
+    if (NS_WARN_IF(aRv.Failed())) {
+      return;
+    }
+  }
+
+  if (aOptions.mChannelCountMode.WasPassed()) {
+    SetChannelCountModeValue(aOptions.mChannelCountMode.Value(), aRv);
+    if (NS_WARN_IF(aRv.Failed())) {
+      return;
+    }
+  }
+
+  if (aOptions.mChannelInterpretation.WasPassed()) {
+    SetChannelInterpretationValue(aOptions.mChannelInterpretation.Value());
+  }
+}
+
 size_t
 AudioNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
 {
   // Not owned:
   // - mContext
   // - mStream
   size_t amount = 0;
 
--- a/dom/media/webaudio/AudioNode.h
+++ b/dom/media/webaudio/AudioNode.h
@@ -246,16 +246,19 @@ private:
   }
   // Callers must hold a reference to 'this'.
   void DisconnectFromGraph();
 
   template<typename DestinationType>
   bool DisconnectFromOutputIfConnected(uint32_t aOutputIndex, uint32_t aInputIndex);
 
 protected:
+  // Helper for the Constructors for nodes.
+  void Initialize(const AudioNodeOptions& aOptions, ErrorResult& aRv);
+
   // Helpers for sending different value types to streams
   void SendDoubleParameterToStream(uint32_t aIndex, double aValue);
   void SendInt32ParameterToStream(uint32_t aIndex, int32_t aValue);
   void SendThreeDPointParameterToStream(uint32_t aIndex, const ThreeDPoint& aValue);
   void SendChannelMixingParametersToStream();
 
 private:
   RefPtr<AudioContext> mContext;
--- a/dom/media/webaudio/test/test_analyserNode.html
+++ b/dom/media/webaudio/test/test_analyserNode.html
@@ -5,19 +5,17 @@
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="text/javascript" src="webaudio.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
-SimpleTest.waitForExplicitFinish();
-addLoadEvent(function() {
-
+function testNode() {
   var context = new AudioContext();
   var buffer = context.createBuffer(1, 2048, context.sampleRate);
   for (var i = 0; i < 2048; ++i) {
     buffer.getChannelData(0)[i] = Math.sin(440 * 2 * Math.PI * i / context.sampleRate);
   }
 
   var destination = context.destination;
 
@@ -79,25 +77,102 @@ addLoadEvent(function() {
   }, DOMException.INDEX_SIZE_ERR);
   expectException(function() {
     analyser.maxDecibels = -100;
   }, DOMException.INDEX_SIZE_ERR);
   expectException(function() {
     analyser.maxDecibels = -101;
   }, DOMException.INDEX_SIZE_ERR);
 
-  is(analyser.smoothingTimeConstant, 0.8, "Correct default value for smoothingTimeConstant");
+  ok(Math.abs(analyser.smoothingTimeConstant - 0.8) < 0.001, "Correct default value for smoothingTimeConstant");
   expectException(function() {
     analyser.smoothingTimeConstant = -0.1;
   }, DOMException.INDEX_SIZE_ERR);
   expectException(function() {
     analyser.smoothingTimeConstant = 1.1;
   }, DOMException.INDEX_SIZE_ERR);
   analyser.smoothingTimeConstant = 0;
   analyser.smoothingTimeConstant = 1;
+}
+
+function testConstructor() {
+  var context = new AudioContext();
+
+  var analyser = new AnalyserNode(context);
+  is(analyser.channelCount, 1, "analyser node has 1 input channels by default");
+  is(analyser.channelCountMode, "max", "Correct channelCountMode for the analyser node");
+  is(analyser.channelInterpretation, "speakers", "Correct channelCountInterpretation for the analyser node");
+
+  is(analyser.fftSize, 2048, "Correct default value for fftSize");
+  is(analyser.frequencyBinCount, 1024, "Correct default value for frequencyBinCount");
+  is(analyser.minDecibels, -100, "Correct default value for minDecibels");
+  is(analyser.maxDecibels, -30, "Correct default value for maxDecibels");
+  ok(Math.abs(analyser.smoothingTimeConstant - 0.8) < 0.001, "Correct default value for smoothingTimeConstant");
+
+  expectException(function() {
+    analyser = new AnalyserNode(context, { fftSize: 0 });
+  }, DOMException.INDEX_SIZE_ERR);
+  expectException(function() {
+    analyser = new AnalyserNode(context, { fftSize: 1 });
+  }, DOMException.INDEX_SIZE_ERR);
+  expectException(function() {
+    analyser = new AnalyserNode(context, { fftSize: 8 });
+  }, DOMException.INDEX_SIZE_ERR);
+  expectException(function() {
+    analyser = new AnalyserNode(context, { fftSize: 100 }); // non-power of two
+  }, DOMException.INDEX_SIZE_ERR);
+  expectException(function() {
+    analyser = new AnalyserNode(context, { fftSize: 2049 });
+  }, DOMException.INDEX_SIZE_ERR);
+  expectException(function() {
+    analyser = new AnalyserNode(context, { fftSize: 4097 });
+  }, DOMException.INDEX_SIZE_ERR);
+  expectException(function() {
+    analyser = new AnalyserNode(context, { fftSize: 8193 });
+  }, DOMException.INDEX_SIZE_ERR);
+  expectException(function() {
+    analyser = new AnalyserNode(context, { fftSize: 16385 });
+  }, DOMException.INDEX_SIZE_ERR);
+  expectException(function() {
+    analyser = new AnalyserNode(context, { fftSize: 32769 });
+  }, DOMException.INDEX_SIZE_ERR);
+  expectException(function() {
+    analyser = new AnalyserNode(context, { fftSize: 65536 });
+  }, DOMException.INDEX_SIZE_ERR);
+  analyser = new AnalyserNode(context, { fftSize: 1024 });
+  is(analyser.frequencyBinCount, 512, "Correct new value for frequencyBinCount");
+
+  expectException(function() {
+    analyser = new AnalyserNode(context, { minDecibels: -30 });
+  }, DOMException.INDEX_SIZE_ERR);
+  expectException(function() {
+    analyser = new AnalyserNode(context, { minDecibels: -29 });
+  }, DOMException.INDEX_SIZE_ERR);
+  expectException(function() {
+    analyser = new AnalyserNode(context, { maxDecibels: -100 });
+  }, DOMException.INDEX_SIZE_ERR);
+  expectException(function() {
+    analyser = new AnalyserNode(context, { maxDecibels: -101 });
+  }, DOMException.INDEX_SIZE_ERR);
+
+  expectException(function() {
+    analyser = new AnalyserNode(context, { smoothingTimeConstant: -0.1 });
+  }, DOMException.INDEX_SIZE_ERR);
+  expectException(function() {
+    analyser = new AnalyserNode(context, { smoothingTimeConstant: -1.1 });
+  }, DOMException.INDEX_SIZE_ERR);
+  analyser = new AnalyserNode(context, { smoothingTimeConstant: 0 });
+  analyser = new AnalyserNode(context, { smoothingTimeConstant: 1 });
+}
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(function() {
+
+  testNode();
+  testConstructor();
 
   SimpleTest.finish();
 });
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/webidl/AnalyserNode.webidl
+++ b/dom/webidl/AnalyserNode.webidl
@@ -5,17 +5,25 @@
  *
  * The origin of this IDL file is
  * https://webaudio.github.io/web-audio-api/
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
-[Pref="dom.webaudio.enabled"]
+dictionary AnalyserOptions : AudioNodeOptions {
+             unsigned long fftSize = 2048;
+             float         maxDecibels = -30;
+             float         minDecibels = -100;
+             float         smoothingTimeConstant = 0.8;
+};
+
+[Pref="dom.webaudio.enabled",
+ Constructor(AudioContext context, optional AnalyserOptions options)]
 interface AnalyserNode : AudioNode {
 
     // Real-time frequency-domain data
     void getFloatFrequencyData(Float32Array array);
     void getByteFrequencyData(Uint8Array array);
 
     // Real-time waveform data
     void getFloatTimeDomainData(Float32Array array);
@@ -33,9 +41,8 @@ interface AnalyserNode : AudioNode {
 
     [SetterThrows, Pure]
     attribute double smoothingTimeConstant;
 
 };
 
 // Mozilla extension
 AnalyserNode implements AudioNodePassThrough;
-
--- a/dom/webidl/AudioNode.webidl
+++ b/dom/webidl/AudioNode.webidl
@@ -16,16 +16,22 @@ enum ChannelCountMode {
     "explicit"
 };
 
 enum ChannelInterpretation {
     "speakers",
     "discrete"
 };
 
+dictionary AudioNodeOptions {
+             unsigned long         channelCount;
+             ChannelCountMode      channelCountMode;
+             ChannelInterpretation channelInterpretation;
+};
+
 [Pref="dom.webaudio.enabled"]
 interface AudioNode : EventTarget {
 
     [Throws]
     AudioNode connect(AudioNode destination, optional unsigned long output = 0, optional unsigned long input = 0);
     [Throws]
     void connect(AudioParam destination, optional unsigned long output = 0);
     [Throws]