Bug 865247 - Part 4: Implement ChannelSplitterNode; r=roc
authorEhsan Akhgari <ehsan@mozilla.com>
Sun, 05 May 2013 03:07:04 -0400
changeset 141828 9491bd82c11f654a0fd9eff4f853366ff6c61c78
parent 141827 6e0a8b79cdae4eae2c08d2d38c8e50c7dea7ab36
child 141829 87ee7b242241bcbbeedb89e87e92aa1d856d3a96
push id2579
push userakeybl@mozilla.com
push dateMon, 24 Jun 2013 18:52:47 +0000
treeherdermozilla-beta@b69b7de8a05a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc
bugs865247
milestone23.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 865247 - Part 4: Implement ChannelSplitterNode; r=roc
content/media/webaudio/AudioContext.cpp
content/media/webaudio/AudioContext.h
content/media/webaudio/ChannelSplitterNode.cpp
content/media/webaudio/ChannelSplitterNode.h
content/media/webaudio/Makefile.in
content/media/webaudio/moz.build
content/media/webaudio/test/Makefile.in
content/media/webaudio/test/test_channelSplitterNode.html
dom/webidl/AudioContext.webidl
dom/webidl/ChannelSplitterNode.webidl
dom/webidl/WebIDL.mk
--- a/content/media/webaudio/AudioContext.cpp
+++ b/content/media/webaudio/AudioContext.cpp
@@ -15,21 +15,23 @@
 #include "AudioBuffer.h"
 #include "GainNode.h"
 #include "DelayNode.h"
 #include "PannerNode.h"
 #include "AudioListener.h"
 #include "DynamicsCompressorNode.h"
 #include "BiquadFilterNode.h"
 #include "ScriptProcessorNode.h"
+#include "ChannelSplitterNode.h"
 #include "nsNetUtil.h"
 
 // Note that this number is an arbitrary large value to protect against OOM
 // attacks.
 const unsigned MAX_SCRIPT_PROCESSOR_CHANNELS = 10000;
+const unsigned MAX_CHANNEL_SPLITTER_OUTPUTS = UINT16_MAX;
 
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_CYCLE_COLLECTION_INHERITED_2(AudioContext, nsDOMEventTargetHelper,
                                      mDestination, mListener)
 
 NS_IMPL_ADDREF_INHERITED(AudioContext, nsDOMEventTargetHelper)
@@ -203,16 +205,30 @@ AudioContext::CreateDelay(double aMaxDel
 already_AddRefed<PannerNode>
 AudioContext::CreatePanner()
 {
   nsRefPtr<PannerNode> pannerNode = new PannerNode(this);
   mPannerNodes.PutEntry(pannerNode);
   return pannerNode.forget();
 }
 
+already_AddRefed<ChannelSplitterNode>
+AudioContext::CreateChannelSplitter(uint32_t aNumberOfOutputs, ErrorResult& aRv)
+{
+  if (aNumberOfOutputs == 0 ||
+      aNumberOfOutputs > MAX_CHANNEL_SPLITTER_OUTPUTS) {
+    aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
+    return nullptr;
+  }
+
+  nsRefPtr<ChannelSplitterNode> splitterNode =
+    new ChannelSplitterNode(this, aNumberOfOutputs);
+  return splitterNode.forget();
+}
+
 already_AddRefed<DynamicsCompressorNode>
 AudioContext::CreateDynamicsCompressor()
 {
   nsRefPtr<DynamicsCompressorNode> compressorNode =
     new DynamicsCompressorNode(this);
   return compressorNode.forget();
 }
 
--- a/content/media/webaudio/AudioContext.h
+++ b/content/media/webaudio/AudioContext.h
@@ -39,16 +39,17 @@ struct WebAudioDecodeJob;
 namespace dom {
 
 class AnalyserNode;
 class AudioBuffer;
 class AudioBufferSourceNode;
 class AudioDestinationNode;
 class AudioListener;
 class BiquadFilterNode;
+class ChannelSplitterNode;
 class DelayNode;
 class DynamicsCompressorNode;
 class GainNode;
 class GlobalObject;
 class PannerNode;
 class ScriptProcessorNode;
 
 class AudioContext MOZ_FINAL : public nsDOMEventTargetHelper,
@@ -137,16 +138,19 @@ public:
   CreateDelayNode(double aMaxDelayTime, ErrorResult& aRv)
   {
     return CreateDelay(aMaxDelayTime, aRv);
   }
 
   already_AddRefed<PannerNode>
   CreatePanner();
 
+  already_AddRefed<ChannelSplitterNode>
+  CreateChannelSplitter(uint32_t aNumberOfOutputs, ErrorResult& aRv);
+
   already_AddRefed<DynamicsCompressorNode>
   CreateDynamicsCompressor();
 
   already_AddRefed<BiquadFilterNode>
   CreateBiquadFilter();
 
   void DecodeAudioData(const ArrayBuffer& aBuffer,
                        DecodeSuccessCallback& aSuccessCallback,
new file mode 100644
--- /dev/null
+++ b/content/media/webaudio/ChannelSplitterNode.cpp
@@ -0,0 +1,70 @@
+/* -*- 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 "mozilla/dom/ChannelSplitterNode.h"
+#include "mozilla/dom/ChannelSplitterNodeBinding.h"
+#include "AudioNodeEngine.h"
+#include "AudioNodeStream.h"
+#include "mozilla/PodOperations.h"
+
+namespace mozilla {
+namespace dom {
+
+NS_IMPL_ISUPPORTS_INHERITED0(ChannelSplitterNode, AudioNode)
+
+class ChannelSplitterNodeEngine : public AudioNodeEngine
+{
+public:
+  ChannelSplitterNodeEngine(ChannelSplitterNode* aNode)
+    : AudioNodeEngine(aNode)
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+  }
+
+  virtual void ProduceAudioBlock(AudioNodeStream* aStream,
+                                 const OutputChunks& aInput,
+                                 OutputChunks& aOutput,
+                                 bool* aFinished) MOZ_OVERRIDE
+  {
+    MOZ_ASSERT(aInput.Length() == 1, "Should only have one input port");
+
+    aOutput.SetLength(OutputCount());
+    for (uint16_t i = 0; i < OutputCount(); ++i) {
+      if (i < aInput[0].mChannelData.Length()) {
+        // Split out existing channels
+        AllocateAudioBlock(1, &aOutput[i]);
+        PodCopy(static_cast<float*>(const_cast<void*>(aOutput[i].mChannelData[0])),
+                static_cast<const float*>(aInput[0].mChannelData[i]),
+                WEBAUDIO_BLOCK_SIZE);
+      } else {
+        // Pad with silent channels if needed
+        aOutput[i].SetNull(WEBAUDIO_BLOCK_SIZE);
+      }
+    }
+  }
+};
+
+ChannelSplitterNode::ChannelSplitterNode(AudioContext* aContext,
+                                         uint16_t aOutputCount)
+  : AudioNode(aContext,
+              2,
+              ChannelCountMode::Max,
+              ChannelInterpretation::Speakers)
+  , mOutputCount(aOutputCount)
+{
+  mStream = aContext->Graph()->CreateAudioNodeStream(new ChannelSplitterNodeEngine(this),
+                                                     MediaStreamGraph::INTERNAL_STREAM);
+}
+
+JSObject*
+ChannelSplitterNode::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
+{
+  return ChannelSplitterNodeBinding::Wrap(aCx, aScope, this);
+}
+
+}
+}
+
new file mode 100644
--- /dev/null
+++ b/content/media/webaudio/ChannelSplitterNode.h
@@ -0,0 +1,38 @@
+/* -*- 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 ChannelSplitterNode_h_
+#define ChannelSplitterNode_h_
+
+#include "AudioNode.h"
+
+namespace mozilla {
+namespace dom {
+
+class AudioContext;
+
+class ChannelSplitterNode : public AudioNode
+{
+public:
+  ChannelSplitterNode(AudioContext* aContext,
+                      uint16_t aOutputCount);
+
+  NS_DECL_ISUPPORTS_INHERITED
+
+  virtual JSObject* WrapObject(JSContext* aCx,
+                               JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
+
+  virtual uint16_t NumberOfOutputs() const { return mOutputCount; }
+
+private:
+  const uint16_t mOutputCount;
+};
+
+}
+}
+
+#endif
+
--- a/content/media/webaudio/Makefile.in
+++ b/content/media/webaudio/Makefile.in
@@ -21,16 +21,17 @@ CPPSRCS := \
   AudioBufferSourceNode.cpp \
   AudioContext.cpp \
   AudioDestinationNode.cpp \
   AudioListener.cpp \
   AudioNode.cpp \
   AudioParam.cpp \
   AudioProcessingEvent.cpp \
   BiquadFilterNode.cpp \
+  ChannelSplitterNode.cpp \
   DelayNode.cpp \
   DynamicsCompressorNode.cpp \
   EnableWebAudioCheck.cpp \
   GainNode.cpp \
   MediaBufferDecoder.cpp \
   PannerNode.cpp \
   ScriptProcessorNode.cpp \
   ThreeDPoint.cpp \
--- a/content/media/webaudio/moz.build
+++ b/content/media/webaudio/moz.build
@@ -23,16 +23,17 @@ EXPORTS.mozilla.dom += [
     'AudioBufferSourceNode.h',
     'AudioContext.h',
     'AudioDestinationNode.h',
     'AudioListener.h',
     'AudioNode.h',
     'AudioParam.h',
     'AudioProcessingEvent.h',
     'BiquadFilterNode.h',
+    'ChannelSplitterNode.h',
     'DelayNode.h',
     'DynamicsCompressorNode.h',
     'EnableWebAudioCheck.h',
     'GainNode.h',
     'PannerNode.h',
     'ScriptProcessorNode.h',
 ]
 
--- a/content/media/webaudio/test/Makefile.in
+++ b/content/media/webaudio/test/Makefile.in
@@ -31,16 +31,17 @@ MOCHITEST_FILES := \
   test_audioBufferSourceNode.html \
   test_audioBufferSourceNodeLazyLoopParam.html \
   test_audioBufferSourceNodeLoop.html \
   test_audioBufferSourceNodeLoopStartEnd.html \
   test_audioBufferSourceNodeLoopStartEndSame.html \
   test_audioBufferSourceNodeNullBuffer.html \
   test_badConnect.html \
   test_biquadFilterNode.html \
+  test_channelSplitterNode.html \
   test_currentTime.html \
   test_delayNode.html \
   test_delayNodeWithGain.html \
   test_dynamicsCompressorNode.html \
   test_gainNode.html \
   test_mediaDecoding.html \
   test_mixingRules.html \
   test_nodeToParamConnection.html \
new file mode 100644
--- /dev/null
+++ b/content/media/webaudio/test/test_channelSplitterNode.html
@@ -0,0 +1,67 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test ChannelSplitterNode</title>
+  <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() {
+  SpecialPowers.setBoolPref("media.webaudio.enabled", true);
+
+  var context = new AudioContext();
+  var buffer = context.createBuffer(4, 2048, context.sampleRate);
+  for (var j = 0; j < 4; ++j) {
+    for (var i = 0; i < 2048; ++i) {
+      buffer.getChannelData(j)[i] = Math.sin(440 * 2 * (j + 1) * Math.PI * i / context.sampleRate);
+    }
+  }
+  var emptyBuffer = context.createBuffer(1, 2048, context.sampleRate);
+
+  var destination = context.destination;
+
+  var source = context.createBufferSource();
+
+  var splitter = context.createChannelSplitter();
+
+  source.buffer = buffer;
+  source.connect(splitter);
+
+  var channelsSeen = 0;
+  function createHandler(i) {
+    return function(e) {
+      is(e.inputBuffer.numberOfChannels, 1, "Correct input channel count");
+      if (i < 4) {
+        compareBuffers(e.inputBuffer.getChannelData(0), buffer.getChannelData(i));
+      } else {
+        compareBuffers(e.inputBuffer.getChannelData(0), emptyBuffer.getChannelData(0));
+      }
+      e.target.onaudioprocess = null;
+      ++channelsSeen;
+
+      if (channelsSeen == 6) {
+        SpecialPowers.clearUserPref("media.webaudio.enabled");
+        SimpleTest.finish();
+      }
+    };
+  }
+
+  for (var i = 0; i < 6; ++i) {
+    var sp = context.createScriptProcessor(2048, 1);
+    splitter.connect(sp, i);
+    sp.onaudioprocess = createHandler(i);
+    sp.connect(destination);
+  }
+
+  source.start(0);
+});
+
+</script>
+</pre>
+</body>
+</html>
--- a/dom/webidl/AudioContext.webidl
+++ b/dom/webidl/AudioContext.webidl
@@ -46,16 +46,19 @@ interface AudioContext : EventTarget {
     GainNode createGain();
     [Creator, Throws]
     DelayNode createDelay(optional double maxDelayTime = 1);
     [Creator]
     BiquadFilterNode createBiquadFilter();
     [Creator]
     PannerNode createPanner();
 
+    [Creator, Throws]
+    ChannelSplitterNode createChannelSplitter(optional unsigned long numberOfOutputs = 6);
+
     [Creator]
     DynamicsCompressorNode createDynamicsCompressor();
 
 };
 
 /*
  * The origin of this IDL file is
  * https://dvcs.w3.org/hg/audio/raw-file/tip/webaudio/specification.html#AlternateNames
new file mode 100644
--- /dev/null
+++ b/dom/webidl/ChannelSplitterNode.webidl
@@ -0,0 +1,17 @@
+/* -*- 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 ChannelSplitterNode : AudioNode {
+
+};
+
--- a/dom/webidl/WebIDL.mk
+++ b/dom/webidl/WebIDL.mk
@@ -26,16 +26,17 @@ webidl_files = \
   BatteryManager.webidl \
   BeforeUnloadEvent.webidl \
   BiquadFilterNode.webidl \
   Blob.webidl \
   CanvasRenderingContext2D.webidl \
   CaretPosition.webidl \
   CDATASection.webidl \
   CFStateChangeEvent.webidl \
+  ChannelSplitterNode.webidl \
   CharacterData.webidl \
   ChildNode.webidl \
   ClientRect.webidl \
   ClientRectList.webidl \
   ClipboardEvent.webidl \
   CommandEvent.webidl \
   Comment.webidl \
   CompositionEvent.webidl \