Bug 809654 - Implement BiquadFilterNode; r=bzbarsky
authorEhsan Akhgari <ehsan@mozilla.com>
Wed, 07 Nov 2012 20:59:14 -0500
changeset 112686 83e1f7986539dfef7c95c2eaa62c4acc882cb6c5
parent 112685 2f116ae5e387eedb70e0cc2ffc86536da29294ab
child 112687 c1d783e19750d3ede754bcea409b71a933306b2e
push id23835
push userryanvm@gmail.com
push dateFri, 09 Nov 2012 00:52:02 +0000
treeherdermozilla-central@27ae6c86237f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbzbarsky
bugs809654
milestone19.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 809654 - Implement BiquadFilterNode; r=bzbarsky
content/media/webaudio/AudioContext.cpp
content/media/webaudio/AudioContext.h
content/media/webaudio/BiquadFilterNode.cpp
content/media/webaudio/BiquadFilterNode.h
content/media/webaudio/Makefile.in
content/media/webaudio/test/Makefile.in
content/media/webaudio/test/test_badConnect.html
content/media/webaudio/test/test_biquadFilterNode.html
dom/bindings/Bindings.conf
dom/webidl/AudioContext.webidl
dom/webidl/BiquadFilterNode.webidl
dom/webidl/WebIDL.mk
--- a/content/media/webaudio/AudioContext.cpp
+++ b/content/media/webaudio/AudioContext.cpp
@@ -12,16 +12,17 @@
 #include "AudioDestinationNode.h"
 #include "AudioBufferSourceNode.h"
 #include "AudioBuffer.h"
 #include "GainNode.h"
 #include "DelayNode.h"
 #include "PannerNode.h"
 #include "AudioListener.h"
 #include "DynamicsCompressorNode.h"
+#include "BiquadFilterNode.h"
 
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(AudioContext)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_NATIVE(AudioContext)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mWindow)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mDestination)
@@ -119,16 +120,24 @@ AudioContext::CreatePanner()
 already_AddRefed<DynamicsCompressorNode>
 AudioContext::CreateDynamicsCompressor()
 {
   nsRefPtr<DynamicsCompressorNode> compressorNode =
     new DynamicsCompressorNode(this);
   return compressorNode.forget();
 }
 
+already_AddRefed<BiquadFilterNode>
+AudioContext::CreateBiquadFilter()
+{
+  nsRefPtr<BiquadFilterNode> filterNode =
+    new BiquadFilterNode(this);
+  return filterNode.forget();
+}
+
 AudioListener*
 AudioContext::Listener()
 {
   if (!mListener) {
     mListener = new AudioListener(this);
   }
   return mListener;
 }
--- a/content/media/webaudio/AudioContext.h
+++ b/content/media/webaudio/AudioContext.h
@@ -22,16 +22,17 @@ namespace mozilla {
 class ErrorResult;
 
 namespace dom {
 
 class AudioBuffer;
 class AudioBufferSourceNode;
 class AudioDestinationNode;
 class AudioListener;
+class BiquadFilterNode;
 class DelayNode;
 class DynamicsCompressorNode;
 class GainNode;
 class PannerNode;
 
 class AudioContext MOZ_FINAL : public nsWrapperCache,
                                public EnableWebAudioCheck
 {
@@ -75,16 +76,19 @@ public:
   CreateDelay(float aMaxDelayTime);
 
   already_AddRefed<PannerNode>
   CreatePanner();
 
   already_AddRefed<DynamicsCompressorNode>
   CreateDynamicsCompressor();
 
+  already_AddRefed<BiquadFilterNode>
+  CreateBiquadFilter();
+
 private:
   nsCOMPtr<nsIDOMWindow> mWindow;
   nsRefPtr<AudioDestinationNode> mDestination;
   nsRefPtr<AudioListener> mListener;
 };
 
 }
 }
new file mode 100644
--- /dev/null
+++ b/content/media/webaudio/BiquadFilterNode.cpp
@@ -0,0 +1,57 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 "BiquadFilterNode.h"
+#include "mozilla/dom/BiquadFilterNodeBinding.h"
+
+namespace mozilla {
+namespace dom {
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(BiquadFilterNode)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(BiquadFilterNode, AudioNode)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mFrequency)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mQ)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mGain)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(BiquadFilterNode, AudioNode)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_PTR(tmp->mFrequency, AudioParam, "frequency value")
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_PTR(tmp->mQ, AudioParam, "Q value")
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_PTR(tmp->mGain, AudioParam, "gain value")
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(BiquadFilterNode)
+NS_INTERFACE_MAP_END_INHERITING(AudioNode)
+
+NS_IMPL_ADDREF_INHERITED(BiquadFilterNode, AudioNode)
+NS_IMPL_RELEASE_INHERITED(BiquadFilterNode, AudioNode)
+
+static float
+Nyquist(AudioContext* aContext)
+{
+  // TODO: Replace the hardcoded 44100 here with AudioContext::SampleRate()
+  // when we implement that.
+  return 0.5f * 44100;
+}
+
+BiquadFilterNode::BiquadFilterNode(AudioContext* aContext)
+  : AudioNode(aContext)
+  , mType(BiquadTypeEnum::LOWPASS)
+  , mFrequency(new AudioParam(aContext, 350.f, 10.f, Nyquist(aContext)))
+  , mQ(new AudioParam(aContext, 1.f, 0.0001f, 1000.f))
+  , mGain(new AudioParam(aContext, 0.f, -40.f, 40.f))
+{
+}
+
+JSObject*
+BiquadFilterNode::WrapObject(JSContext* aCx, JSObject* aScope,
+                             bool* aTriedToWrap)
+{
+  return BiquadFilterNodeBinding::Wrap(aCx, aScope, this, aTriedToWrap);
+}
+
+}
+}
+
new file mode 100644
--- /dev/null
+++ b/content/media/webaudio/BiquadFilterNode.h
@@ -0,0 +1,92 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+#ifndef BiquadFilterNode_h_
+#define BiquadFilterNode_h_
+
+#include "AudioNode.h"
+#include "AudioParam.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/ErrorResult.h"
+
+namespace mozilla {
+namespace dom {
+
+class AudioContext;
+
+MOZ_BEGIN_ENUM_CLASS(BiquadTypeEnum, uint16_t)
+  LOWPASS = 0,
+  HIGHPASS = 1,
+  BANDPASS = 2,
+  LOWSHELF = 3,
+  HIGHSHELF = 4,
+  PEAKING = 5,
+  NOTCH = 6,
+  ALLPASS = 7,
+  Max = 7
+MOZ_END_ENUM_CLASS(BiquadTypeEnum)
+
+class BiquadFilterNode : public AudioNode
+{
+public:
+  explicit BiquadFilterNode(AudioContext* aContext);
+
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(BiquadFilterNode, AudioNode)
+
+  virtual JSObject* WrapObject(JSContext* aCx, JSObject* aScope,
+                               bool* aTriedToWrap);
+
+  virtual uint32_t MaxNumberOfInputs() const MOZ_FINAL MOZ_OVERRIDE
+  {
+    return 1;
+  }
+  virtual uint32_t MaxNumberOfOutputs() const MOZ_FINAL MOZ_OVERRIDE
+  {
+    return 1;
+  }
+
+  uint16_t Type() const
+  {
+    return static_cast<uint16_t> (mType);
+  }
+  void SetType(uint16_t aType, ErrorResult& aRv)
+  {
+    BiquadTypeEnum type = static_cast<BiquadTypeEnum> (aType);
+    if (type > BiquadTypeEnum::Max) {
+      aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
+    } else {
+      mType = type;
+    }
+  }
+
+  AudioParam* Frequency() const
+  {
+    return mFrequency;
+  }
+
+  AudioParam* Q() const
+  {
+    return mQ;
+  }
+
+  AudioParam* Gain() const
+  {
+    return mGain;
+  }
+
+private:
+  BiquadTypeEnum mType;
+  nsRefPtr<AudioParam> mFrequency;
+  nsRefPtr<AudioParam> mQ;
+  nsRefPtr<AudioParam> mGain;
+};
+
+}
+}
+
+#endif
+
--- a/content/media/webaudio/Makefile.in
+++ b/content/media/webaudio/Makefile.in
@@ -18,32 +18,34 @@ CPPSRCS := \
   AudioBuffer.cpp \
   AudioBufferSourceNode.cpp \
   AudioContext.cpp \
   AudioDestinationNode.cpp \
   AudioListener.cpp \
   AudioNode.cpp \
   AudioParam.cpp \
   AudioSourceNode.cpp \
+  BiquadFilterNode.cpp \
   DelayNode.cpp \
   DynamicsCompressorNode.cpp \
   EnableWebAudioCheck.cpp \
   GainNode.cpp \
   PannerNode.cpp \
   $(NULL)
 
 EXPORTS_NAMESPACES := mozilla/dom
 EXPORTS_mozilla/dom := \
   AudioBuffer.h \
   AudioBufferSourceNode.h \
   AudioDestinationNode.h \
   AudioListener.h \
   AudioNode.h \
   AudioParam.h \
   AudioSourceNode.h \
+  BiquadFilterNode.h \
   DelayNode.h \
   DynamicsCompressorNode.h \
   GainNode.h \
   PannerNode.h \
   $(NULL)
 
 PARALLEL_DIRS := test
 
--- a/content/media/webaudio/test/Makefile.in
+++ b/content/media/webaudio/test/Makefile.in
@@ -6,21 +6,23 @@ DEPTH          := @DEPTH@
 topsrcdir      := @top_srcdir@
 srcdir         := @srcdir@
 VPATH          := @srcdir@
 relativesrcdir := @relativesrcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 MOCHITEST_FILES := \
+  webaudio.js \
   test_bug808374.html \
   test_AudioBuffer.html \
   test_AudioContext.html \
   test_AudioListener.html \
   test_badConnect.html \
+  test_biquadFilterNode.html \
   test_delayNode.html \
   test_dynamicsCompressorNode.html \
   test_gainNode.html \
   test_pannerNode.html \
   test_singleSourceDest.html \
   $(NULL)
 
 include $(topsrcdir)/config/rules.mk
--- a/content/media/webaudio/test/test_badConnect.html
+++ b/content/media/webaudio/test/test_badConnect.html
@@ -2,30 +2,19 @@
 <html>
 <head>
   <title>Test whether we can create an AudioContext interface</title>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <pre id="test">
+<script src="webaudio.js" type="text/javascript"></script>
 <script class="testbody" type="text/javascript">
 
-function expectException(func, exceptionCode) {
-  var threw = false;
-  try {
-    func();
-  } catch (ex) {
-    threw = true;
-    ok(ex instanceof DOMException, "Expect a DOM exception");
-    ok(ex.code, exceptionCode, "Expect the correct exception code");
-  }
-  ok(threw, "The exception was thrown");
-}
-
 SimpleTest.waitForExplicitFinish();
 addLoadEvent(function() {
   SpecialPowers.setBoolPref("media.webaudio.enabled", true);
 
   var context1 = new mozAudioContext();
   var context2 = new mozAudioContext();
 
   var destination1 = context1.destination;
new file mode 100644
--- /dev/null
+++ b/content/media/webaudio/test/test_biquadFilterNode.html
@@ -0,0 +1,73 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test BiquadFilterNode</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<pre id="test">
+<script src="webaudio.js" type="text/javascript"></script>
+<script class="testbody" type="text/javascript">
+
+function near(a, b, msg) {
+  ok(Math.abs(a - b) < 1e-3, msg);
+}
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(function() {
+  SpecialPowers.setBoolPref("media.webaudio.enabled", true);
+
+  var context = new mozAudioContext();
+  var buffer = context.createBuffer(1, 2048, 44100);
+  for (var i = 0; i < 2048; ++i) {
+    buffer.getChannelData(0)[i] = Math.sin(440 * 2 * Math.PI * i / 44100);
+  }
+
+  var destination = context.destination;
+
+  var source = context.createBufferSource();
+
+  var filter = context.createBiquadFilter();
+
+  source.buffer = buffer;
+
+  source.connect(filter);
+  filter.connect(destination);
+
+  // Verify default values
+  is(filter.type, 0, "Correct default value for type");
+  near(filter.frequency.minValue, 10, "Correct min value for filter frequency");
+  near(filter.frequency.maxValue, 22050, "Correct max value for filter frequency");
+  near(filter.frequency.defaultValue, 350, "Correct default value for filter frequency");
+  near(filter.Q.minValue, 0.001, "Correct min value for filter Q");
+  near(filter.Q.maxValue, 1000, "Correct max value for filter Q");
+  near(filter.Q.defaultValue, 1, "Correct default value for filter Q");
+  near(filter.gain.minValue, -40, "Correct min value for filter gain");
+  near(filter.gain.maxValue, 40, "Correct max value for filter gain");
+  near(filter.gain.defaultValue, 0, "Correct default value for filter gain");
+
+  // Make sure that we can set all of the valid type values
+  for (var i = 0; i <= 7; ++i) {
+    filter.type = i;
+  }
+
+  expectException(function() {
+    filter.type = 8;
+  }, DOMException.INDEX_SIZE_ERR);
+
+  source.start(0);
+  SimpleTest.executeSoon(function() {
+    source.stop(0);
+    source.disconnect();
+    filter.disconnect();
+
+    SpecialPowers.clearUserPref("media.webaudio.enabled");
+    SimpleTest.finish();
+  });
+});
+
+</script>
+</pre>
+</body>
+</html>
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -109,16 +109,20 @@ DOMInterfaces = {
 'AudioParam' : {
     'nativeOwnership': 'refcounted'
 },
 
 'AudioSourceNode': {
     'concrete': False,
 },
 
+'BiquadFilterNode': {
+    'resultNotAddRefed': [ 'frequency', 'q', 'gain' ],
+},
+
 'Blob': [
 {
     'headerFile': 'nsIDOMFile.h',
 },
 {
     'workers': True,
 }],
 
--- a/dom/webidl/AudioContext.webidl
+++ b/dom/webidl/AudioContext.webidl
@@ -26,16 +26,18 @@ interface mozAudioContext {
     [Creator]
     AudioBufferSourceNode createBufferSource();
 
     [Creator]
     GainNode createGain();
     [Creator]
     DelayNode createDelay(optional float maxDelayTime = 1);
     [Creator]
+    BiquadFilterNode createBiquadFilter();
+    [Creator]
     PannerNode createPanner();
 
     [Creator]
     DynamicsCompressorNode createDynamicsCompressor();
 
 };
 
 typedef mozAudioContext AudioContext;
new file mode 100644
--- /dev/null
+++ b/dom/webidl/BiquadFilterNode.webidl
@@ -0,0 +1,37 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/.
+ *
+ * The origin of this IDL file is
+ * https://dvcs.w3.org/hg/audio/raw-file/tip/webaudio/specification.html
+ *
+ * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
+ * liability, trademark and document use rules apply.
+ */
+
+[PrefControlled]
+interface BiquadFilterNode : AudioNode {
+
+    // Filter type.
+    const unsigned short LOWPASS = 0;
+    const unsigned short HIGHPASS = 1;
+    const unsigned short BANDPASS = 2;
+    const unsigned short LOWSHELF = 3;
+    const unsigned short HIGHSHELF = 4;
+    const unsigned short PEAKING = 5;
+    const unsigned short NOTCH = 6;
+    const unsigned short ALLPASS = 7;
+
+    [SetterThrows]
+    attribute unsigned short type;
+    readonly attribute AudioParam frequency; // in Hertz
+    readonly attribute AudioParam Q; // Quality factor
+    readonly attribute AudioParam gain; // in Decibels
+
+    // void getFrequencyResponse(Float32Array frequencyHz,
+    //                           Float32Array magResponse,
+    //                           Float32Array phaseResponse);
+
+};
+
--- a/dom/webidl/WebIDL.mk
+++ b/dom/webidl/WebIDL.mk
@@ -12,16 +12,17 @@ webidl_files = \
   AudioBuffer.webidl \
   AudioBufferSourceNode.webidl \
   AudioContext.webidl \
   AudioDestinationNode.webidl \
   AudioListener.webidl \
   AudioNode.webidl \
   AudioParam.webidl \
   AudioSourceNode.webidl \
+  BiquadFilterNode.webidl \
   Blob.webidl \
   CanvasRenderingContext2D.webidl \
   ClientRectList.webidl \
   CSSStyleDeclaration.webidl \
   DelayNode.webidl \
   DOMImplementation.webidl \
   DOMTokenList.webidl \
   DOMSettableTokenList.webidl \