Bug 865253 - Part 1: Implement the DOM bindings for OscillatorNode; r=roc, a=webaudio
authorEhsan Akhgari <ehsan@mozilla.com>
Mon, 24 Jun 2013 10:40:58 -0700
changeset 153799 19845caac7751d2aeead0715cbd997b88505879b
parent 153798 1a1ff090cd0221d1c94755aaa022cdd2db05c66e
child 153800 5c95ce1874b3dd02387fd1afb961b962746353ed
push id2859
push userakeybl@mozilla.com
push dateMon, 16 Sep 2013 19:14:59 +0000
treeherdermozilla-beta@87d3c51cd2bf [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc, webaudio
bugs865253
milestone25.0a2
Bug 865253 - Part 1: Implement the DOM bindings for OscillatorNode; r=roc, a=webaudio
content/media/webaudio/AudioContext.cpp
content/media/webaudio/AudioContext.h
content/media/webaudio/OscillatorNode.cpp
content/media/webaudio/OscillatorNode.h
content/media/webaudio/moz.build
content/media/webaudio/test/Makefile.in
content/media/webaudio/test/test_oscillatorNode.html
dom/bindings/Bindings.conf
dom/webidl/AudioContext.webidl
dom/webidl/OscillatorNode.webidl
dom/webidl/WebIDL.mk
--- a/content/media/webaudio/AudioContext.cpp
+++ b/content/media/webaudio/AudioContext.cpp
@@ -23,16 +23,17 @@
 #include "BiquadFilterNode.h"
 #include "ScriptProcessorNode.h"
 #include "ChannelMergerNode.h"
 #include "ChannelSplitterNode.h"
 #include "MediaStreamAudioDestinationNode.h"
 #include "WaveShaperNode.h"
 #include "PeriodicWave.h"
 #include "ConvolverNode.h"
+#include "OscillatorNode.h"
 #include "nsNetUtil.h"
 
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_CYCLE_COLLECTION_INHERITED_2(AudioContext, nsDOMEventTargetHelper,
                                      mDestination, mListener)
 
@@ -332,16 +333,24 @@ AudioContext::CreateDynamicsCompressor()
 already_AddRefed<BiquadFilterNode>
 AudioContext::CreateBiquadFilter()
 {
   nsRefPtr<BiquadFilterNode> filterNode =
     new BiquadFilterNode(this);
   return filterNode.forget();
 }
 
+already_AddRefed<OscillatorNode>
+AudioContext::CreateOscillator()
+{
+  nsRefPtr<OscillatorNode> oscillatorNode =
+    new OscillatorNode(this);
+  return oscillatorNode.forget();
+}
+
 already_AddRefed<PeriodicWave>
 AudioContext::CreatePeriodicWave(const Float32Array& aRealData,
                                  const Float32Array& aImagData,
                                  ErrorResult& aRv)
 {
   if (aRealData.Length() != aImagData.Length() ||
       aRealData.Length() == 0 ||
       aRealData.Length() > 4096) {
--- a/content/media/webaudio/AudioContext.h
+++ b/content/media/webaudio/AudioContext.h
@@ -49,16 +49,18 @@ class ChannelMergerNode;
 class ChannelSplitterNode;
 class ConvolverNode;
 class DelayNode;
 class DynamicsCompressorNode;
 class GainNode;
 class GlobalObject;
 class MediaStreamAudioDestinationNode;
 class OfflineRenderSuccessCallback;
+class MediaStreamAudioSourceNode;
+class OscillatorNode;
 class PannerNode;
 class ScriptProcessorNode;
 class WaveShaperNode;
 class PeriodicWave;
 
 class AudioContext MOZ_FINAL : public nsDOMEventTargetHelper,
                                public EnableWebAudioCheck
 {
@@ -183,16 +185,19 @@ public:
   CreateChannelMerger(uint32_t aNumberOfInputs, ErrorResult& aRv);
 
   already_AddRefed<DynamicsCompressorNode>
   CreateDynamicsCompressor();
 
   already_AddRefed<BiquadFilterNode>
   CreateBiquadFilter();
 
+  already_AddRefed<OscillatorNode>
+  CreateOscillator();
+
   already_AddRefed<PeriodicWave>
   CreatePeriodicWave(const Float32Array& aRealData, const Float32Array& aImagData,
                      ErrorResult& aRv);
 
   void DecodeAudioData(const ArrayBuffer& aBuffer,
                        DecodeSuccessCallback& aSuccessCallback,
                        const Optional<OwningNonNull<DecodeErrorCallback> >& aFailureCallback);
 
new file mode 100644
--- /dev/null
+++ b/content/media/webaudio/OscillatorNode.cpp
@@ -0,0 +1,251 @@
+/* -*- 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 "OscillatorNode.h"
+#include "AudioNodeEngine.h"
+#include "AudioNodeStream.h"
+#include "AudioDestinationNode.h"
+#include "WebAudioUtils.h"
+
+namespace mozilla {
+namespace dom {
+
+NS_IMPL_CYCLE_COLLECTION_INHERITED_3(OscillatorNode, AudioNode,
+                                     mPeriodicWave, mFrequency, mDetune)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(OscillatorNode)
+NS_INTERFACE_MAP_END_INHERITING(AudioNode)
+
+NS_IMPL_ADDREF_INHERITED(OscillatorNode, AudioNode)
+NS_IMPL_RELEASE_INHERITED(OscillatorNode, AudioNode)
+
+class OscillatorNodeEngine : public AudioNodeEngine
+{
+public:
+  OscillatorNodeEngine(AudioNode* aNode, AudioDestinationNode* aDestination)
+    : AudioNodeEngine(aNode)
+    , mSource(nullptr)
+    , mDestination(static_cast<AudioNodeStream*> (aDestination->Stream()))
+    , mStart(0)
+    , mStop(TRACK_TICKS_MAX)
+    // Keep the default value in sync with the default value in OscillatorNode::OscillatorNode.
+    , mFrequency(440.f)
+    , mDetune(0.f)
+    , mType(OscillatorType::Sine)
+  {
+  }
+
+  void SetSourceStream(AudioNodeStream* aSource)
+  {
+    mSource = aSource;
+  }
+
+  enum Parameters {
+    FREQUENCY,
+    DETUNE,
+    TYPE,
+    PERIODICWAVE,
+    START,
+    STOP,
+  };
+  void SetTimelineParameter(uint32_t aIndex,
+                            const AudioParamTimeline& aValue,
+                            TrackRate aSampleRate) MOZ_OVERRIDE
+  {
+    switch (aIndex) {
+    case FREQUENCY:
+      MOZ_ASSERT(mSource && mDestination);
+      mFrequency = aValue;
+      WebAudioUtils::ConvertAudioParamToTicks(mFrequency, mSource, mDestination);
+      break;
+    case DETUNE:
+      MOZ_ASSERT(mSource && mDestination);
+      mDetune = aValue;
+      WebAudioUtils::ConvertAudioParamToTicks(mDetune, mSource, mDestination);
+      break;
+    default:
+      NS_ERROR("Bad OscillatorNodeEngine TimelineParameter");
+    }
+  }
+  virtual void SetStreamTimeParameter(uint32_t aIndex, TrackTicks aParam)
+  {
+    switch (aIndex) {
+    case START: mStart = aParam; break;
+    case STOP: mStop = aParam; break;
+    default:
+      NS_ERROR("Bad OscillatorNodeEngine StreamTimeParameter");
+    }
+  }
+  virtual void SetInt32Parameter(uint32_t aIndex, int32_t aParam)
+  {
+    switch (aIndex) {
+    case TYPE: mType = static_cast<OscillatorType>(aParam); break;
+    default:
+      NS_ERROR("Bad OscillatorNodeEngine Int32Parameter");
+    }
+  }
+
+  virtual void ProduceAudioBlock(AudioNodeStream* aStream,
+                                 const AudioChunk& aInput,
+                                 AudioChunk* aOutput,
+                                 bool* aFinished) MOZ_OVERRIDE
+  {
+    MOZ_ASSERT(mSource == aStream, "Invalid source stream");
+
+    // TODO: Synthesize a waveform here.
+    *aOutput = aInput;
+  }
+
+  AudioNodeStream* mSource;
+  AudioNodeStream* mDestination;
+  TrackTicks mStart;
+  TrackTicks mStop;
+  AudioParamTimeline mFrequency;
+  AudioParamTimeline mDetune;
+  OscillatorType mType;
+};
+
+OscillatorNode::OscillatorNode(AudioContext* aContext)
+  : AudioNode(aContext,
+              2,
+              ChannelCountMode::Max,
+              ChannelInterpretation::Speakers)
+  , mType(OscillatorType::Sine)
+  , mFrequency(new AudioParam(MOZ_THIS_IN_INITIALIZER_LIST(),
+               SendFrequencyToStream, 440.0f))
+  , mDetune(new AudioParam(MOZ_THIS_IN_INITIALIZER_LIST(),
+            SendDetuneToStream, 0.0f))
+  , mStartCalled(false)
+  , mStopped(false)
+{
+  OscillatorNodeEngine* engine = new OscillatorNodeEngine(this, aContext->Destination());
+  mStream = aContext->Graph()->CreateAudioNodeStream(engine, MediaStreamGraph::SOURCE_STREAM);
+  engine->SetSourceStream(static_cast<AudioNodeStream*> (mStream.get()));
+}
+
+JSObject*
+OscillatorNode::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
+{
+  return OscillatorNodeBinding::Wrap(aCx, aScope, this);
+}
+
+void
+OscillatorNode::SendFrequencyToStream(AudioNode* aNode)
+{
+  OscillatorNode* This = static_cast<OscillatorNode*>(aNode);
+  SendTimelineParameterToStream(This, OscillatorNodeEngine::FREQUENCY, *This->mFrequency);
+}
+
+void
+OscillatorNode::SendDetuneToStream(AudioNode* aNode)
+{
+  OscillatorNode* This = static_cast<OscillatorNode*>(aNode);
+  SendTimelineParameterToStream(This, OscillatorNodeEngine::DETUNE, *This->mDetune);
+}
+
+void
+OscillatorNode::SendTypeToStream()
+{
+  SendInt32ParameterToStream(OscillatorNodeEngine::TYPE, static_cast<int32_t>(mType));
+  if (mType == OscillatorType::Custom) {
+    // TODO: Send the custom wave table somehow
+  }
+}
+
+void
+OscillatorNode::Start(double aWhen, ErrorResult& aRv)
+{
+  if (!WebAudioUtils::IsTimeValid(aWhen)) {
+    aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+    return;
+  }
+
+  if (mStartCalled) {
+    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+    return;
+  }
+  mStartCalled = true;
+
+  AudioNodeStream* ns = static_cast<AudioNodeStream*>(mStream.get());
+  if (!ns) {
+    // Nothing to play, or we're already dead for some reason
+    return;
+  }
+
+  // TODO: Perhaps we need to do more here.
+  ns->SetStreamTimeParameter(OscillatorNodeEngine::START,
+                             Context()->DestinationStream(),
+                             aWhen);
+
+  MOZ_ASSERT(!mPlayingRef, "We can only accept a successful start() call once");
+  mPlayingRef.Take(this);
+}
+
+void
+OscillatorNode::Stop(double aWhen, ErrorResult& aRv)
+{
+  if (!WebAudioUtils::IsTimeValid(aWhen)) {
+    aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+    return;
+  }
+
+  if (!mStartCalled) {
+    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+    return;
+  }
+
+  mPlayingRef.Drop(this);
+
+  AudioNodeStream* ns = static_cast<AudioNodeStream*>(mStream.get());
+  if (!ns || !Context()) {
+    // We've already stopped and had our stream shut down
+    return;
+  }
+
+  // TODO: Perhaps we need to do more here.
+  ns->SetStreamTimeParameter(OscillatorNodeEngine::STOP,
+                             Context()->DestinationStream(),
+                             std::max(0.0, aWhen));
+}
+
+void
+OscillatorNode::NotifyMainThreadStateChanged()
+{
+  if (mStream->IsFinished()) {
+    class EndedEventDispatcher : public nsRunnable
+    {
+    public:
+      explicit EndedEventDispatcher(OscillatorNode* aNode)
+        : mNode(aNode) {}
+      NS_IMETHODIMP Run()
+      {
+        // If it's not safe to run scripts right now, schedule this to run later
+        if (!nsContentUtils::IsSafeToRunScript()) {
+          nsContentUtils::AddScriptRunner(this);
+          return NS_OK;
+        }
+
+        mNode->DispatchTrustedEvent(NS_LITERAL_STRING("ended"));
+        return NS_OK;
+      }
+    private:
+      nsRefPtr<OscillatorNode> mNode;
+    };
+    if (!mStopped) {
+      // Only dispatch the ended event once
+      NS_DispatchToMainThread(new EndedEventDispatcher(this));
+      mStopped = true;
+    }
+
+    // Drop the playing reference
+    // Warning: The below line might delete this.
+    mPlayingRef.Drop(this);
+  }
+}
+
+}
+}
+
new file mode 100644
--- /dev/null
+++ b/content/media/webaudio/OscillatorNode.h
@@ -0,0 +1,107 @@
+/* -*- 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 OscillatorNode_h_
+#define OscillatorNode_h_
+
+#include "AudioNode.h"
+#include "AudioParam.h"
+#include "PeriodicWave.h"
+#include "mozilla/dom/OscillatorNodeBinding.h"
+
+namespace mozilla {
+namespace dom {
+
+class AudioContext;
+
+class OscillatorNode : public AudioNode,
+                       public MainThreadMediaStreamListener
+{
+public:
+  explicit OscillatorNode(AudioContext* aContext);
+
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(OscillatorNode, AudioNode)
+
+  virtual JSObject* WrapObject(JSContext* aCx,
+                               JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
+
+  virtual void DestroyMediaStream() MOZ_OVERRIDE
+  {
+    if (mStream) {
+      mStream->RemoveMainThreadListener(this);
+    }
+    AudioNode::DestroyMediaStream();
+  }
+  virtual uint16_t NumberOfInputs() const MOZ_FINAL MOZ_OVERRIDE
+  {
+    return 0;
+  }
+
+  OscillatorType Type() const
+  {
+    return mType;
+  }
+  void SetType(OscillatorType aType, ErrorResult& aRv)
+  {
+    if (aType == OscillatorType::Custom) {
+      aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+      return;
+    }
+    mType = aType;
+    SendTypeToStream();
+  }
+
+  AudioParam* Frequency() const
+  {
+    return mFrequency;
+  }
+  AudioParam* Detune() const
+  {
+    return mDetune;
+  }
+
+  void Start(double aWhen, ErrorResult& aRv);
+  void NoteOn(double aWhen, ErrorResult& aRv)
+  {
+    Start(aWhen, aRv);
+  }
+  void Stop(double aWhen, ErrorResult& aRv);
+  void NoteOff(double aWhen, ErrorResult& aRv)
+  {
+    Stop(aWhen, aRv);
+  }
+  void SetPeriodicWave(PeriodicWave& aPeriodicWave)
+  {
+    mPeriodicWave = &aPeriodicWave;
+    mType = OscillatorType::Custom;
+    SendTypeToStream();
+  }
+
+  IMPL_EVENT_HANDLER(ended)
+
+  virtual void NotifyMainThreadStateChanged() MOZ_OVERRIDE;
+
+private:
+  static void SendFrequencyToStream(AudioNode* aNode);
+  static void SendDetuneToStream(AudioNode* aNode);
+  void SendTypeToStream();
+
+private:
+  OscillatorType mType;
+  nsRefPtr<PeriodicWave> mPeriodicWave;
+  nsRefPtr<AudioParam> mFrequency;
+  nsRefPtr<AudioParam> mDetune;
+  SelfReference<OscillatorNode> mPlayingRef;
+  bool mStartCalled;
+  bool mStopped;
+};
+
+}
+}
+
+#endif
+
--- a/content/media/webaudio/moz.build
+++ b/content/media/webaudio/moz.build
@@ -36,16 +36,17 @@ EXPORTS.mozilla.dom += [
     'ChannelSplitterNode.h',
     'ConvolverNode.h',
     'DelayNode.h',
     'DynamicsCompressorNode.h',
     'EnableWebAudioCheck.h',
     'GainNode.h',
     'MediaStreamAudioDestinationNode.h',
     'OfflineAudioCompletionEvent.h',
+    'OscillatorNode.h',
     'PannerNode.h',
     'PeriodicWave.h',
     'ScriptProcessorNode.h',
     'WaveShaperNode.h',
 ]
 
 CPP_SOURCES += [
     'AnalyserNode.cpp',
@@ -65,16 +66,17 @@ CPP_SOURCES += [
     'DelayProcessor.cpp',
     'DynamicsCompressorNode.cpp',
     'EnableWebAudioCheck.cpp',
     'FFTBlock.cpp',
     'GainNode.cpp',
     'MediaBufferDecoder.cpp',
     'MediaStreamAudioDestinationNode.cpp',
     'OfflineAudioCompletionEvent.cpp',
+    'OscillatorNode.cpp',
     'PannerNode.cpp',
     'PeriodicWave.cpp',
     'ScriptProcessorNode.cpp',
     'ThreeDPoint.cpp',
     'WaveShaperNode.cpp',
     'WebAudioUtils.cpp',
 ]
 
--- a/content/media/webaudio/test/Makefile.in
+++ b/content/media/webaudio/test/Makefile.in
@@ -68,16 +68,17 @@ MOCHITEST_FILES := \
   test_maxChannelCount.html \
   test_mediaDecoding.html \
   test_mediaStreamAudioDestinationNode.html \
   test_mixingRules.html \
   test_nodeToParamConnection.html \
   test_OfflineAudioContext.html \
   test_offlineDestinationChannelCountLess.html \
   test_offlineDestinationChannelCountMore.html \
+  test_oscillatorNode.html \
   test_pannerNode.html \
   test_pannerNode_equalPower.html \
   test_periodicWave.html \
   test_scriptProcessorNode.html \
   test_scriptProcessorNodeChannelCount.html \
   test_scriptProcessorNodeZeroInputOutput.html \
   test_singleSourceDest.html \
   test_waveShaper.html \
new file mode 100644
--- /dev/null
+++ b/content/media/webaudio/test/test_oscillatorNode.html
@@ -0,0 +1,38 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test the OscillatorNode interface</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() {
+
+  var context = new AudioContext();
+  var osc = context.createOscillator();
+
+  is(osc.channelCount, 2, "Oscillator node has 2 input channels by default");
+  is(osc.channelCountMode, "max", "Correct channelCountMode for the Oscillator node");
+  is(osc.channelInterpretation, "speakers", "Correct channelCountInterpretation for the Oscillator node");
+  is(osc.type, "sine", "Correct default type");
+  osc.type = "triangle";
+  is(osc.type, "triangle", "Can set the type");
+  expectException(function() {
+    osc.type = "custom";
+  }, DOMException.NOT_SUPPORTED_ERR);
+  is(osc.type, "triangle", "Cannot set the type to custom");
+  is(osc.frequency.value, 440, "Correct default frequency value");
+  is(osc.detune.value, 0, "Correct default detine value");
+
+  SimpleTest.finish();
+});
+
+</script>
+</pre>
+</body>
+</html>
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -786,16 +786,20 @@ DOMInterfaces = {
     'implicitJSContext': [ 'createBuffer' ],
     'resultNotAddRefed': [ 'destination', 'listener' ],
 },
 
 'OfflineResourceList': {
     'nativeType': 'nsDOMOfflineResourceList',
 },
 
+'OscillatorNode': {
+    'resultNotAddRefed': [ 'frequency', 'detune' ],
+},
+
 'PaintRequest': {
     'nativeType': 'nsPaintRequest',
 },
 
 'PaintRequestList': {
     'nativeType': 'nsPaintRequestList',
     'headerFile': 'nsPaintRequest.h',
     'resultNotAddRefed': [ 'item' ]
--- a/dom/webidl/AudioContext.webidl
+++ b/dom/webidl/AudioContext.webidl
@@ -58,16 +58,18 @@ interface AudioContext : EventTarget {
     [Creator, Throws]
     ChannelSplitterNode createChannelSplitter(optional unsigned long numberOfOutputs = 6);
     [Creator, Throws]
     ChannelMergerNode createChannelMerger(optional unsigned long numberOfInputs = 6);
 
     [Creator]
     DynamicsCompressorNode createDynamicsCompressor();
 
+    [Creator]
+    OscillatorNode createOscillator();
     [Creator, Throws]
     PeriodicWave createPeriodicWave(Float32Array real, Float32Array imag);
 
 };
 
 /*
  * 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/OscillatorNode.webidl
@@ -0,0 +1,50 @@
+/* -*- 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.
+ */
+
+enum OscillatorType {
+  "sine",
+  "square",
+  "sawtooth",
+  "triangle",
+  "custom"
+};
+
+[PrefControlled]
+interface OscillatorNode : AudioNode {
+
+    [SetterThrows]
+    attribute OscillatorType type;
+
+    readonly attribute AudioParam frequency; // in Hertz
+    readonly attribute AudioParam detune; // in Cents
+
+    [Throws]
+    void start(double when);
+    [Throws]
+    void stop(double when);
+    void setPeriodicWave(PeriodicWave periodicWave);
+
+    [SetterThrows]
+    attribute EventHandler onended;
+
+};
+
+partial interface OscillatorNode {
+    // Same as start()
+    [Throws]
+    void noteOn(double when);
+
+    // Same as stop()
+    [Throws]
+    void noteOff(double when);
+};
+
--- a/dom/webidl/WebIDL.mk
+++ b/dom/webidl/WebIDL.mk
@@ -208,16 +208,17 @@ webidl_files = \
   NodeIterator.webidl \
   NodeList.webidl \
   Notification.webidl \
   NotifyAudioAvailableEvent.webidl \
   NotifyPaintEvent.webidl \
   OfflineAudioCompletionEvent.webidl \
   OfflineAudioContext.webidl \
   OfflineResourceList.webidl \
+  OscillatorNode.webidl \
   PaintRequest.webidl \
   PaintRequestList.webidl \
   PannerNode.webidl \
   ParentNode.webidl \
   Performance.webidl \
   PerformanceNavigation.webidl \
   PerformanceTiming.webidl \
   PeriodicWave.webidl \