Source/WebCore: Add Oscillator/WaveTable implementation and tests
authorcrogers@google.com <crogers@google.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 2 Apr 2012 21:07:37 +0000 (21:07 +0000)
committercrogers@google.com <crogers@google.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 2 Apr 2012 21:07:37 +0000 (21:07 +0000)
https://bugs.webkit.org/show_bug.cgi?id=82414

Oscillator represents an audio source generating a periodic waveform.  It can be set to
a few commonly used waveforms.  Additionally, it can be set to an arbitrary periodic
waveform through the use of a WaveTable object.

Reviewed by Kenneth Russell.

Tests: webaudio/oscillator-custom.html
       webaudio/oscillator-sawtooth.html
       webaudio/oscillator-sine.html
       webaudio/oscillator-square.html
       webaudio/oscillator-triangle.html

* DerivedSources.make:
* GNUmakefile.list.am:
Add Oscillator and WaveTable to build files.
* Modules/webaudio/AudioContext.cpp:
(WebCore::AudioContext::createOscillator):
(WebCore):
(WebCore::AudioContext::createWaveTable):
* Modules/webaudio/AudioContext.h:
Add create methods for Oscillator and WaveTable.
(WebCore):
(AudioContext):
* Modules/webaudio/AudioContext.idl:
* Modules/webaudio/AudioNode.h:
* Modules/webaudio/Oscillator.cpp: Added.
(WebCore):
(WebCore::Oscillator::create):
(WebCore::Oscillator::Oscillator):
(WebCore::Oscillator::~Oscillator):
(WebCore::Oscillator::setType):
(WebCore::Oscillator::calculateSampleAccuratePhaseIncrements):
(WebCore::Oscillator::process):
(WebCore::Oscillator::reset):
(WebCore::Oscillator::setWaveTable):
* Modules/webaudio/Oscillator.h: Added.
(WebCore):
(Oscillator):
(WebCore::Oscillator::type):
(WebCore::Oscillator::frequency):
(WebCore::Oscillator::detune):
Implement Oscillator as AudioSourceNode.
* Modules/webaudio/Oscillator.idl: Added.
* Modules/webaudio/WaveTable.cpp: Added.
(WebCore):
(WebCore::WaveTable::create):
(WebCore::WaveTable::createSine):
(WebCore::WaveTable::createSquare):
(WebCore::WaveTable::createSawtooth):
(WebCore::WaveTable::createTriangle):
(WebCore::WaveTable::WaveTable):
(WebCore::WaveTable::waveDataForFundamentalFrequency):
(WebCore::WaveTable::maxNumberOfPartials):
(WebCore::WaveTable::numberOfPartialsForRange):
(WebCore::WaveTable::createBandLimitedTables):
(WebCore::WaveTable::generateBasicWaveform):
* Modules/webaudio/WaveTable.h: Added.
(WebCore):
(WaveTable):
(WebCore::WaveTable::rateScale):
(WebCore::WaveTable::waveTableSize):
(WebCore::WaveTable::sampleRate):
(WebCore::WaveTable::numberOfRanges):
Implement WaveTable which is constructed given a set of Fourier coefficients.
* Modules/webaudio/WaveTable.idl: Added.
* WebCore.gypi:
* WebCore.xcodeproj/project.pbxproj:
Add Oscillator and WaveTable files to build files.

LayoutTests: Add Oscillator/WaveTable implementation and tests
https://bugs.webkit.org/show_bug.cgi?id=82414

Reviewed by Kenneth Russell.

* webaudio/oscillator-custom-expected.wav: Added.
* webaudio/oscillator-custom.html: Added.
* webaudio/oscillator-sawtooth-expected.wav: Added.
* webaudio/oscillator-sawtooth.html: Added.
* webaudio/oscillator-sine-expected.wav: Added.
* webaudio/oscillator-sine.html: Added.
* webaudio/oscillator-square-expected.wav: Added.
* webaudio/oscillator-square.html: Added.
* webaudio/oscillator-triangle-expected.wav: Added.
* webaudio/oscillator-triangle.html: Added.
* webaudio/resources/oscillator-testing.js: Added.
(generateExponentialOscillatorSweep):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@112938 268f45cc-cd09-0410-ab3c-d52691b4dbfc

27 files changed:
LayoutTests/ChangeLog
LayoutTests/webaudio/oscillator-custom-expected.wav [new file with mode: 0644]
LayoutTests/webaudio/oscillator-custom.html [new file with mode: 0644]
LayoutTests/webaudio/oscillator-sawtooth-expected.wav [new file with mode: 0644]
LayoutTests/webaudio/oscillator-sawtooth.html [new file with mode: 0644]
LayoutTests/webaudio/oscillator-sine-expected.wav [new file with mode: 0644]
LayoutTests/webaudio/oscillator-sine.html [new file with mode: 0644]
LayoutTests/webaudio/oscillator-square-expected.wav [new file with mode: 0644]
LayoutTests/webaudio/oscillator-square.html [new file with mode: 0644]
LayoutTests/webaudio/oscillator-triangle-expected.wav [new file with mode: 0644]
LayoutTests/webaudio/oscillator-triangle.html [new file with mode: 0644]
LayoutTests/webaudio/resources/oscillator-testing.js [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/DerivedSources.make
Source/WebCore/GNUmakefile.list.am
Source/WebCore/Modules/webaudio/AudioContext.cpp
Source/WebCore/Modules/webaudio/AudioContext.h
Source/WebCore/Modules/webaudio/AudioContext.idl
Source/WebCore/Modules/webaudio/AudioNode.h
Source/WebCore/Modules/webaudio/Oscillator.cpp [new file with mode: 0644]
Source/WebCore/Modules/webaudio/Oscillator.h [new file with mode: 0644]
Source/WebCore/Modules/webaudio/Oscillator.idl [new file with mode: 0644]
Source/WebCore/Modules/webaudio/WaveTable.cpp [new file with mode: 0644]
Source/WebCore/Modules/webaudio/WaveTable.h [new file with mode: 0644]
Source/WebCore/Modules/webaudio/WaveTable.idl [new file with mode: 0644]
Source/WebCore/WebCore.gypi
Source/WebCore/WebCore.xcodeproj/project.pbxproj

index 05aca94..c5c4202 100644 (file)
@@ -1,3 +1,23 @@
+2012-04-02  Chris Rogers  <crogers@google.com>
+
+        Add Oscillator/WaveTable implementation and tests
+        https://bugs.webkit.org/show_bug.cgi?id=82414
+
+        Reviewed by Kenneth Russell.
+
+        * webaudio/oscillator-custom-expected.wav: Added.
+        * webaudio/oscillator-custom.html: Added.
+        * webaudio/oscillator-sawtooth-expected.wav: Added.
+        * webaudio/oscillator-sawtooth.html: Added.
+        * webaudio/oscillator-sine-expected.wav: Added.
+        * webaudio/oscillator-sine.html: Added.
+        * webaudio/oscillator-square-expected.wav: Added.
+        * webaudio/oscillator-square.html: Added.
+        * webaudio/oscillator-triangle-expected.wav: Added.
+        * webaudio/oscillator-triangle.html: Added.
+        * webaudio/resources/oscillator-testing.js: Added.
+        (generateExponentialOscillatorSweep):
+
 2012-04-02  David Dorwin  <ddorwin@chromium.org>
 
         Add layout test expectations for Chromium to exhibit support WebM types
diff --git a/LayoutTests/webaudio/oscillator-custom-expected.wav b/LayoutTests/webaudio/oscillator-custom-expected.wav
new file mode 100644 (file)
index 0000000..75ad713
Binary files /dev/null and b/LayoutTests/webaudio/oscillator-custom-expected.wav differ
diff --git a/LayoutTests/webaudio/oscillator-custom.html b/LayoutTests/webaudio/oscillator-custom.html
new file mode 100644 (file)
index 0000000..2529e69
--- /dev/null
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+
+<!--
+Create an oscillator with a custom WaveTable and generate a slow exponential tone sweep.
+The result can be checked for the correct wave shape and for aliasing artifacts.
+See oscillator-testing.js for details.
+-->
+
+<html>
+<head>
+<script type="text/javascript" src="resources/audio-testing.js"></script>
+<script type="text/javascript" src="resources/oscillator-testing.js"></script>
+</head>
+<body>
+
+<script>
+
+window.onload = init;
+
+function init() {
+    if (!window.layoutTestController)
+        return;
+
+    generateExponentialOscillatorSweep(OSC.CUSTOM);
+}
+
+</script>
+
+</body>
+</html>
diff --git a/LayoutTests/webaudio/oscillator-sawtooth-expected.wav b/LayoutTests/webaudio/oscillator-sawtooth-expected.wav
new file mode 100644 (file)
index 0000000..5ae970e
Binary files /dev/null and b/LayoutTests/webaudio/oscillator-sawtooth-expected.wav differ
diff --git a/LayoutTests/webaudio/oscillator-sawtooth.html b/LayoutTests/webaudio/oscillator-sawtooth.html
new file mode 100644 (file)
index 0000000..f4937e3
--- /dev/null
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+
+<!--
+Create an oscillator of type SAWTOOTH and generate a slow exponential tone sweep.
+The result can be checked for the correct wave shape and for aliasing artifacts.
+See oscillator-testing.js for details.
+-->
+
+<html>
+<head>
+<script type="text/javascript" src="resources/audio-testing.js"></script>
+<script type="text/javascript" src="resources/oscillator-testing.js"></script>
+</head>
+<body>
+
+<script>
+
+window.onload = init;
+
+function init() {
+    if (!window.layoutTestController)
+        return;
+
+    generateExponentialOscillatorSweep(OSC.SAWTOOTH);
+}
+
+</script>
+
+</body>
+</html>
diff --git a/LayoutTests/webaudio/oscillator-sine-expected.wav b/LayoutTests/webaudio/oscillator-sine-expected.wav
new file mode 100644 (file)
index 0000000..3f5b82d
Binary files /dev/null and b/LayoutTests/webaudio/oscillator-sine-expected.wav differ
diff --git a/LayoutTests/webaudio/oscillator-sine.html b/LayoutTests/webaudio/oscillator-sine.html
new file mode 100644 (file)
index 0000000..2d6fedb
--- /dev/null
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+
+<!--
+Create an oscillator of type SINE and generate a slow exponential tone sweep.
+The result can be checked for the correct wave shape and for aliasing artifacts.
+See oscillator-testing.js for details.
+-->
+
+<html>
+<head>
+<script type="text/javascript" src="resources/audio-testing.js"></script>
+<script type="text/javascript" src="resources/oscillator-testing.js"></script>
+</head>
+<body>
+
+<script>
+
+window.onload = init;
+
+function init() {
+    if (!window.layoutTestController)
+        return;
+
+    generateExponentialOscillatorSweep(OSC.SINE);
+}
+
+</script>
+
+</body>
+</html>
diff --git a/LayoutTests/webaudio/oscillator-square-expected.wav b/LayoutTests/webaudio/oscillator-square-expected.wav
new file mode 100644 (file)
index 0000000..85a9adc
Binary files /dev/null and b/LayoutTests/webaudio/oscillator-square-expected.wav differ
diff --git a/LayoutTests/webaudio/oscillator-square.html b/LayoutTests/webaudio/oscillator-square.html
new file mode 100644 (file)
index 0000000..b62fc75
--- /dev/null
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+
+<!--
+Create an oscillator of type SQUARE and generate a slow exponential tone sweep.
+The result can be checked for the correct wave shape and for aliasing artifacts.
+See oscillator-testing.js for details.
+-->
+
+<html>
+<head>
+<script type="text/javascript" src="resources/audio-testing.js"></script>
+<script type="text/javascript" src="resources/oscillator-testing.js"></script>
+</head>
+<body>
+
+<script>
+
+window.onload = init;
+
+function init() {
+    if (!window.layoutTestController)
+        return;
+
+    generateExponentialOscillatorSweep(OSC.SQUARE);
+}
+
+</script>
+
+</body>
+</html>
diff --git a/LayoutTests/webaudio/oscillator-triangle-expected.wav b/LayoutTests/webaudio/oscillator-triangle-expected.wav
new file mode 100644 (file)
index 0000000..324b8b3
Binary files /dev/null and b/LayoutTests/webaudio/oscillator-triangle-expected.wav differ
diff --git a/LayoutTests/webaudio/oscillator-triangle.html b/LayoutTests/webaudio/oscillator-triangle.html
new file mode 100644 (file)
index 0000000..2d1f471
--- /dev/null
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+
+<!--
+Create an oscillator of type TRIANGLE and generate a slow exponential tone sweep.
+The result can be checked for the correct wave shape and for aliasing artifacts.
+See oscillator-testing.js for details.
+-->
+
+<html>
+<head>
+<script type="text/javascript" src="resources/audio-testing.js"></script>
+<script type="text/javascript" src="resources/oscillator-testing.js"></script>
+</head>
+<body>
+
+<script>
+
+window.onload = init;
+
+function init() {
+    if (!window.layoutTestController)
+        return;
+
+    generateExponentialOscillatorSweep(OSC.TRIANGLE);
+}
+
+</script>
+
+</body>
+</html>
diff --git a/LayoutTests/webaudio/resources/oscillator-testing.js b/LayoutTests/webaudio/resources/oscillator-testing.js
new file mode 100644 (file)
index 0000000..78c6aa7
--- /dev/null
@@ -0,0 +1,54 @@
+// Notes about generated waveforms:
+//
+// QUESTION: Why does the wave shape not look like the exact shape (sharp edges)?
+// ANSWER: Because a shape with sharp edges has infinitely high frequency content.
+// Since a digital audio signal must be band-limited based on the nyquist frequency (half the sample-rate)
+// in order to avoid aliasing, this creates more rounded edges and "ringing" in the
+// appearance of the waveform.  See Nyquist-Shannon sampling theorem:
+// http://en.wikipedia.org/wiki/Nyquist%E2%80%93Shannon_sampling_theorem
+//
+// QUESTION: Why does the very end of the generated signal appear to get slightly weaker?
+// ANSWER: This is an artifact of the algorithm to avoid aliasing.
+
+var sampleRate = 44100.0;
+var nyquist = 0.5 * sampleRate;
+var lengthInSeconds = 4;
+var lowFrequency = 10;
+var highFrequency = nyquist + 2000; // go slightly higher than nyquist to make sure we generate silence there
+var context = 0;
+
+var OSC = {
+    SINE: 0,
+    SQUARE: 1,
+    SAWTOOTH: 2,
+    TRIANGLE: 3,
+    CUSTOM: 4,
+};
+
+function generateExponentialOscillatorSweep(oscillatorType) {
+    // Create offline audio context.
+    context = new webkitAudioContext(1, sampleRate * lengthInSeconds, sampleRate);
+
+    var osc = context.createOscillator();
+    if (oscillatorType == OSC.CUSTOM) {
+        // Create a simple waveform with three Fourier coefficients.
+        // Note the first values are expected to be zero (DC for coeffA and Nyquist for coeffB).
+        var coeffA = new Float32Array([0, 1, 0.5]);
+        var coeffB = new Float32Array([0, 0, 0]);        
+        var wavetable = context.createWaveTable(coeffA, coeffB);
+        osc.setWaveTable(wavetable);
+    } else {
+        osc.type = oscillatorType;
+    }
+
+    osc.connect(context.destination);
+
+    var nyquist = 0.5 * sampleRate;
+    osc.frequency.setValueAtTime(10, 0);
+    osc.frequency.exponentialRampToValueAtTime(highFrequency, lengthInSeconds);
+
+    context.oncomplete = finishAudioTest;
+    context.startRendering();    
+
+    layoutTestController.waitUntilDone();
+}
index a41be3f..94c7e9b 100644 (file)
@@ -1,3 +1,77 @@
+2012-04-02  Chris Rogers  <crogers@google.com>
+
+        Add Oscillator/WaveTable implementation and tests
+        https://bugs.webkit.org/show_bug.cgi?id=82414
+        
+        Oscillator represents an audio source generating a periodic waveform.  It can be set to
+        a few commonly used waveforms.  Additionally, it can be set to an arbitrary periodic
+        waveform through the use of a WaveTable object.
+
+        Reviewed by Kenneth Russell.
+
+        Tests: webaudio/oscillator-custom.html
+               webaudio/oscillator-sawtooth.html
+               webaudio/oscillator-sine.html
+               webaudio/oscillator-square.html
+               webaudio/oscillator-triangle.html
+
+        * DerivedSources.make:
+        * GNUmakefile.list.am:
+        Add Oscillator and WaveTable to build files.
+        * Modules/webaudio/AudioContext.cpp:
+        (WebCore::AudioContext::createOscillator):
+        (WebCore):
+        (WebCore::AudioContext::createWaveTable):
+        * Modules/webaudio/AudioContext.h:
+        Add create methods for Oscillator and WaveTable.
+        (WebCore):
+        (AudioContext):
+        * Modules/webaudio/AudioContext.idl:
+        * Modules/webaudio/AudioNode.h:
+        * Modules/webaudio/Oscillator.cpp: Added.
+        (WebCore):
+        (WebCore::Oscillator::create):
+        (WebCore::Oscillator::Oscillator):
+        (WebCore::Oscillator::~Oscillator):
+        (WebCore::Oscillator::setType):
+        (WebCore::Oscillator::calculateSampleAccuratePhaseIncrements):
+        (WebCore::Oscillator::process):
+        (WebCore::Oscillator::reset):
+        (WebCore::Oscillator::setWaveTable):
+        * Modules/webaudio/Oscillator.h: Added.
+        (WebCore):
+        (Oscillator):
+        (WebCore::Oscillator::type):
+        (WebCore::Oscillator::frequency):
+        (WebCore::Oscillator::detune):
+        Implement Oscillator as AudioSourceNode.
+        * Modules/webaudio/Oscillator.idl: Added.
+        * Modules/webaudio/WaveTable.cpp: Added.
+        (WebCore):
+        (WebCore::WaveTable::create):
+        (WebCore::WaveTable::createSine):
+        (WebCore::WaveTable::createSquare):
+        (WebCore::WaveTable::createSawtooth):
+        (WebCore::WaveTable::createTriangle):
+        (WebCore::WaveTable::WaveTable):
+        (WebCore::WaveTable::waveDataForFundamentalFrequency):
+        (WebCore::WaveTable::maxNumberOfPartials):
+        (WebCore::WaveTable::numberOfPartialsForRange):
+        (WebCore::WaveTable::createBandLimitedTables):
+        (WebCore::WaveTable::generateBasicWaveform):
+        * Modules/webaudio/WaveTable.h: Added.
+        (WebCore):
+        (WaveTable):
+        (WebCore::WaveTable::rateScale):
+        (WebCore::WaveTable::waveTableSize):
+        (WebCore::WaveTable::sampleRate):
+        (WebCore::WaveTable::numberOfRanges):
+        Implement WaveTable which is constructed given a set of Fourier coefficients.
+        * Modules/webaudio/WaveTable.idl: Added.
+        * WebCore.gypi:
+        * WebCore.xcodeproj/project.pbxproj:
+        Add Oscillator and WaveTable files to build files.
+
 2012-04-02  Levi Weintraub  <leviw@chromium.org>
 
         Correct remaining LayoutUnit misuse in RenderReplaced and RenderFlexibleBox
index 30b7dde..803eb83 100644 (file)
@@ -125,9 +125,11 @@ BINDING_IDLS = \
     $(WebCore)/Modules/webaudio/DynamicsCompressorNode.idl \
     $(WebCore)/Modules/webaudio/JavaScriptAudioNode.idl \
     $(WebCore)/Modules/webaudio/MediaElementAudioSourceNode.idl \
+    $(WebCore)/Modules/webaudio/Oscillator.idl \
     $(WebCore)/Modules/webaudio/OfflineAudioCompletionEvent.idl \
     $(WebCore)/Modules/webaudio/RealtimeAnalyserNode.idl \
     $(WebCore)/Modules/webaudio/WaveShaperNode.idl \
+    $(WebCore)/Modules/webaudio/WaveTable.idl \
     $(WebCore)/Modules/webdatabase/DOMWindowWebDatabase.idl \
     $(WebCore)/Modules/webdatabase/Database.idl \
     $(WebCore)/Modules/webdatabase/DatabaseCallback.idl \
index c1e58b9..a10bed8 100644 (file)
@@ -434,6 +434,8 @@ webcore_built_sources += \
        DerivedSources/WebCore/JSOESVertexArrayObject.h \
        DerivedSources/WebCore/JSOperationNotAllowedException.cpp \
        DerivedSources/WebCore/JSOperationNotAllowedException.h \
+       DerivedSources/WebCore/JSOscillator.cpp \
+       DerivedSources/WebCore/JSOscillator.h \
        DerivedSources/WebCore/JSOverflowEvent.cpp \
        DerivedSources/WebCore/JSOverflowEvent.h \
        DerivedSources/WebCore/JSPageTransitionEvent.cpp \
@@ -551,6 +553,8 @@ webcore_built_sources += \
        DerivedSources/WebCore/JSVoidCallback.h \
        DerivedSources/WebCore/JSWaveShaperNode.cpp \
        DerivedSources/WebCore/JSWaveShaperNode.h \
+       DerivedSources/WebCore/JSWaveTable.cpp \
+       DerivedSources/WebCore/JSWaveTable.h \
        DerivedSources/WebCore/JSWebGLActiveInfo.cpp \
        DerivedSources/WebCore/JSWebGLActiveInfo.h \
        DerivedSources/WebCore/JSWebGLBuffer.cpp \
@@ -5481,6 +5485,8 @@ webcore_sources += \
        Source/WebCore/Modules/webaudio/OfflineAudioDestinationNode.cpp \
        Source/WebCore/Modules/webaudio/OfflineAudioCompletionEvent.h \
        Source/WebCore/Modules/webaudio/OfflineAudioCompletionEvent.cpp \
+       Source/WebCore/Modules/webaudio/Oscillator.h \
+       Source/WebCore/Modules/webaudio/Oscillator.cpp \
        Source/WebCore/Modules/webaudio/RealtimeAnalyser.cpp \
        Source/WebCore/Modules/webaudio/RealtimeAnalyser.h \
        Source/WebCore/Modules/webaudio/RealtimeAnalyserNode.cpp \
@@ -5491,6 +5497,8 @@ webcore_sources += \
        Source/WebCore/Modules/webaudio/WaveShaperDSPKernel.h \
        Source/WebCore/Modules/webaudio/WaveShaperProcessor.cpp \
        Source/WebCore/Modules/webaudio/WaveShaperNode.h \
+       Source/WebCore/Modules/webaudio/WaveTable.cpp \
+       Source/WebCore/Modules/webaudio/WaveTable.h \
        Source/WebCore/bindings/js/JSAudioBufferSourceNodeCustom.cpp \
        Source/WebCore/bindings/js/JSAudioContextCustom.cpp \
        Source/WebCore/bindings/js/JSConvolverNodeCustom.cpp \
@@ -5635,7 +5643,9 @@ dom_binding_idls += \
        $(WebCore)/Modules/webaudio/JavaScriptAudioNode.idl \
        $(WebCore)/Modules/webaudio/MediaElementAudioSourceNode.idl \
        $(WebCore)/Modules/webaudio/OfflineAudioCompletionEvent.idl \
-       $(WebCore)/Modules/webaudio/RealtimeAnalyserNode.idl
+    $(WebCore)/Modules/webaudio/Oscillator.idl \
+       $(WebCore)/Modules/webaudio/RealtimeAnalyserNode.idl \
+    $(WebCore)/Modules/webaudio/WaveTable.idl
 endif
 
 
index 18c5ad8..f123fd9 100644 (file)
 #include "JavaScriptAudioNode.h"
 #include "OfflineAudioCompletionEvent.h"
 #include "OfflineAudioDestinationNode.h"
+#include "Oscillator.h"
 #include "PlatformString.h"
 #include "RealtimeAnalyserNode.h"
-#include "WaveShaperNode.h"
 #include "ScriptCallStack.h"
+#include "WaveShaperNode.h"
+#include "WaveTable.h"
 
 #if ENABLE(VIDEO)
 #include "HTMLMediaElement.h"
@@ -453,6 +455,26 @@ PassRefPtr<AudioChannelMerger> AudioContext::createChannelMerger()
     return AudioChannelMerger::create(this, m_destinationNode->sampleRate());
 }
 
+PassRefPtr<Oscillator> AudioContext::createOscillator()
+{
+    ASSERT(isMainThread());
+    lazyInitialize();
+    return Oscillator::create(this, m_destinationNode->sampleRate());
+}
+
+PassRefPtr<WaveTable> AudioContext::createWaveTable(Float32Array* real, Float32Array* imag, ExceptionCode& ec)
+{
+    ASSERT(isMainThread());
+    
+    if (!real || !imag || (real->length() != imag->length())) {
+        ec = SYNTAX_ERR;
+        return 0;
+    }
+    
+    lazyInitialize();
+    return WaveTable::create(sampleRate(), real, imag);
+}
+
 void AudioContext::notifyNodeFinishedProcessing(AudioNode* node)
 {
     ASSERT(isAudioThread());
index ee2019c..adc3982 100644 (file)
@@ -63,6 +63,8 @@ class DynamicsCompressorNode;
 class RealtimeAnalyserNode;
 class WaveShaperNode;
 class JavaScriptAudioNode;
+class Oscillator;
+class WaveTable;
 
 // AudioContext is the cornerstone of the web audio API and all AudioNodes are created from it.
 // For thread safety between the audio thread and the main thread, it has a rendering graph locking mechanism. 
@@ -124,6 +126,8 @@ public:
     PassRefPtr<JavaScriptAudioNode> createJavaScriptNode(size_t bufferSize);
     PassRefPtr<AudioChannelSplitter> createChannelSplitter();
     PassRefPtr<AudioChannelMerger> createChannelMerger();
+    PassRefPtr<Oscillator> createOscillator();
+    PassRefPtr<WaveTable> createWaveTable(Float32Array* real, Float32Array* imag, ExceptionCode&);
 
     // When a source node has no more processing to do (has finished playing), then it tells the context to dereference it.
     void notifyNodeFinishedProcessing(AudioNode*);
index 2ebca3f..1f81d66 100644 (file)
@@ -72,6 +72,9 @@ module webaudio {
         DynamicsCompressorNode createDynamicsCompressor();
         RealtimeAnalyserNode createAnalyser();
         JavaScriptAudioNode createJavaScriptNode(in unsigned long bufferSize);
+        Oscillator createOscillator();
+        WaveTable createWaveTable(in Float32Array real, in Float32Array imag)
+            raises(DOMException);
 
         // Channel splitting and merging
         AudioChannelSplitter createChannelSplitter();
index 460abae..56b847b 100644 (file)
@@ -58,6 +58,7 @@ public:
     enum NodeType {
         NodeTypeUnknown,
         NodeTypeDestination,
+        NodeTypeOscillator,
         NodeTypeAudioBufferSource,
         NodeTypeMediaElementAudioSource,
         NodeTypeJavaScript,
diff --git a/Source/WebCore/Modules/webaudio/Oscillator.cpp b/Source/WebCore/Modules/webaudio/Oscillator.cpp
new file mode 100644 (file)
index 0000000..47d8494
--- /dev/null
@@ -0,0 +1,288 @@
+/*
+ * Copyright (C) 2012, Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1.  Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2.  Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if ENABLE(WEB_AUDIO)
+
+#include "Oscillator.h"
+
+#include "AudioContext.h"
+#include "AudioNodeOutput.h"
+#include "AudioUtilities.h"
+#include "VectorMath.h"
+#include "WaveTable.h"
+#include <algorithm>
+#include <wtf/MathExtras.h>
+
+using namespace std;
+
+namespace WebCore {
+
+using namespace VectorMath;
+
+PassRefPtr<Oscillator> Oscillator::create(AudioContext* context, float sampleRate)
+{
+    return adoptRef(new Oscillator(context, sampleRate));
+}
+
+Oscillator::Oscillator(AudioContext* context, float sampleRate)
+    : AudioSourceNode(context, sampleRate)
+    , m_type(SINE)
+    , m_virtualReadIndex(0)
+    , m_phaseIncrements(AudioNode::ProcessingSizeInFrames)
+    , m_detuneValues(AudioNode::ProcessingSizeInFrames)
+{
+    setNodeType(NodeTypeOscillator);
+
+    // Use musical pitch standard A440 as a default.
+    m_frequency = AudioParam::create("frequency", 440, 0, 100000);
+    // Default to no detuning.
+    m_detune = AudioParam::create("detune", 0, -4800, 4800);
+    m_frequency->setContext(context);
+    m_detune->setContext(context);
+
+    // Sets up default wavetable.
+    setType(m_type);
+
+    // An oscillator is always mono.
+    addOutput(adoptPtr(new AudioNodeOutput(this, 1)));
+
+    initialize();
+}
+
+Oscillator::~Oscillator()
+{
+    uninitialize();
+}
+
+void Oscillator::setType(unsigned short type)
+{
+    RefPtr<WaveTable> waveTable;
+    float sampleRate = this->sampleRate();
+
+    switch (type) {
+    case SINE:
+        waveTable = WaveTable::createSine(sampleRate);
+        break;
+    case SQUARE:
+        waveTable = WaveTable::createSquare(sampleRate);
+        break;
+    case SAWTOOTH:
+        waveTable = WaveTable::createSawtooth(sampleRate);
+        break;
+    case TRIANGLE:
+        waveTable = WaveTable::createTriangle(sampleRate);
+        break;
+    case CUSTOM:
+        // FIXME: throw exception since setWaveTable() method must be called explicitly.
+        return;
+        break;
+    }
+
+    m_type = type;
+    setWaveTable(waveTable.get());
+}
+
+bool Oscillator::calculateSampleAccuratePhaseIncrements(size_t framesToProcess)
+{
+    bool isGood = framesToProcess <= m_phaseIncrements.size() && framesToProcess <= m_detuneValues.size();
+    ASSERT(isGood);
+    if (!isGood)
+        return false;
+
+    bool hasTimelineValues = false;
+    bool hasFrequencyChanges = false;
+    float* phaseIncrements = m_phaseIncrements.data();
+
+    float finalScale = m_waveTable->rateScale();
+
+    if (m_frequency->hasTimelineValues()) {
+        hasTimelineValues = true;
+        hasFrequencyChanges = true;
+
+        // Get the sample-accurate frequency values and convert to phase increments.
+        // They will be converted to phase increments below.
+        m_frequency->calculateSampleAccurateValues(phaseIncrements, framesToProcess);
+    } else {
+        // Handle ordinary parameter smoothing/de-zippering if there are no scheduled changes.
+        m_frequency->smooth();
+        float frequency = m_frequency->smoothedValue();
+        finalScale *= frequency;
+    }
+
+    if (m_detune->hasTimelineValues()) {
+        hasTimelineValues = true;
+
+        // Get the sample-accurate detune values.
+        float* detuneValues = hasFrequencyChanges ? m_detuneValues.data() : phaseIncrements;
+        m_detune->calculateSampleAccurateValues(detuneValues, framesToProcess);
+
+        // Convert from cents to rate scalar.
+        float k = 1.0 / 1200;
+        vsmul(detuneValues, 1, &k, detuneValues, 1, framesToProcess);
+        for (unsigned i = 0; i < framesToProcess; ++i)
+            detuneValues[i] = powf(2, detuneValues[i]); // FIXME: converting to expf() will be faster.
+
+        if (hasFrequencyChanges) {
+            // Multiply frequencies by detune scalings.
+            vmul(detuneValues, 1, phaseIncrements, 1, phaseIncrements, 1, framesToProcess);
+        }
+    } else {
+        // Handle ordinary parameter smoothing/de-zippering if there are no scheduled changes.
+        m_detune->smooth();
+        float detune = m_detune->smoothedValue();
+        float detuneScale = powf(2, detune / 1200);
+        finalScale *= detuneScale;
+    }
+
+    if (hasTimelineValues) {
+        // Convert from frequency to wavetable increment.
+        vsmul(phaseIncrements, 1, &finalScale, phaseIncrements, 1, framesToProcess);
+    }
+
+    return hasTimelineValues;
+}
+
+void Oscillator::process(size_t framesToProcess)
+{
+    AudioBus* outputBus = output(0)->bus();
+
+    outputBus->zero();
+
+    if (!isInitialized() || !outputBus->numberOfChannels()) {
+        outputBus->zero();
+        return;
+    }
+
+    ASSERT(framesToProcess <= m_phaseIncrements.size());
+    if (framesToProcess > m_phaseIncrements.size())
+        return;
+
+    // The audio thread can't block on this lock, so we call tryLock() instead.
+    // Careful - this is a tryLock() and not an autolocker, so we must unlock() before every return.
+    if (!m_processLock.tryLock()) {
+        // Too bad - the tryLock() failed. We must be in the middle of changing wave-tables.
+        outputBus->zero();
+        return;
+    }
+
+    // We must access m_waveTable only inside the lock.
+    if (!m_waveTable.get()) {
+        outputBus->zero();
+        m_processLock.unlock();
+        return;
+    }
+
+    unsigned waveTableSize = m_waveTable->waveTableSize();
+    double invWaveTableSize = 1.0 / waveTableSize;
+
+    float* destP = outputBus->channel(0)->mutableData();
+
+    int n = framesToProcess;
+
+    // We keep virtualReadIndex double-precision since we're accumulating values.
+    double virtualReadIndex = m_virtualReadIndex;
+
+    float rateScale = m_waveTable->rateScale();
+    float invRateScale = 1 / rateScale;
+    bool hasTimelineValues = calculateSampleAccuratePhaseIncrements(framesToProcess);
+
+    float frequency = 0;
+    float* higherWaveData = 0;
+    float* lowerWaveData = 0;
+    float tableInterpolationFactor;
+
+    if (!hasTimelineValues) {
+        frequency = m_frequency->smoothedValue();
+        float detune = m_detune->smoothedValue();
+        float detuneScale = powf(2, detune / 1200);
+        frequency *= detuneScale;
+        m_waveTable->waveDataForFundamentalFrequency(frequency, lowerWaveData, higherWaveData, tableInterpolationFactor);
+    }
+
+    float incr = frequency * rateScale;
+    float* phaseIncrements = m_phaseIncrements.data();
+
+    unsigned readIndexMask = waveTableSize - 1;
+
+    while (n--) {
+        unsigned readIndex = static_cast<unsigned>(virtualReadIndex);
+        unsigned readIndex2 = readIndex + 1;
+
+        // Contain within valid range.
+        readIndex = readIndex & readIndexMask;
+        readIndex2 = readIndex2 & readIndexMask;
+
+        if (hasTimelineValues) {
+            incr = *phaseIncrements++;
+
+            frequency = invRateScale * incr;
+            m_waveTable->waveDataForFundamentalFrequency(frequency, lowerWaveData, higherWaveData, tableInterpolationFactor);
+        }
+
+        float sample1Lower = lowerWaveData[readIndex];
+        float sample2Lower = lowerWaveData[readIndex2];
+        float sample1Higher = higherWaveData[readIndex];
+        float sample2Higher = higherWaveData[readIndex2];
+
+        // Linearly interpolate within each table (lower and higher).
+        float interpolationFactor = static_cast<float>(virtualReadIndex) - readIndex;
+        float sampleHigher = (1 - interpolationFactor) * sample1Higher + interpolationFactor * sample2Higher;
+        float sampleLower = (1 - interpolationFactor) * sample1Lower + interpolationFactor * sample2Lower;
+
+        // Then interpolate between the two tables.
+        float sample = (1 - tableInterpolationFactor) * sampleHigher + tableInterpolationFactor * sampleLower;
+
+        *destP++ = sample;
+
+        // Increment virtual read index and wrap virtualReadIndex into the range 0 -> waveTableSize.
+        virtualReadIndex += incr;
+        virtualReadIndex -= floor(virtualReadIndex * invWaveTableSize) * waveTableSize;
+    }
+
+    m_virtualReadIndex = virtualReadIndex;
+
+    m_processLock.unlock();
+}
+
+void Oscillator::reset()
+{
+    m_virtualReadIndex = 0;
+}
+
+void Oscillator::setWaveTable(WaveTable* waveTable)
+{
+    ASSERT(isMainThread());
+
+    // This synchronizes with process().
+    MutexLocker processLocker(m_processLock);
+    m_waveTable = waveTable;
+    m_type = CUSTOM;
+    m_virtualReadIndex = 0;
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_AUDIO)
diff --git a/Source/WebCore/Modules/webaudio/Oscillator.h b/Source/WebCore/Modules/webaudio/Oscillator.h
new file mode 100644 (file)
index 0000000..2b1ad9f
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2012, Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1.  Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2.  Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef Oscillator_h
+#define Oscillator_h
+
+#include "AudioBus.h"
+#include "AudioParam.h"
+#include "AudioSourceNode.h"
+#include <wtf/OwnArrayPtr.h>
+#include <wtf/PassRefPtr.h>
+#include <wtf/RefPtr.h>
+#include <wtf/Threading.h>
+
+namespace WebCore {
+
+class AudioContext;
+class WaveTable;
+
+// Oscillator is an audio generator of periodic waveforms.
+
+class Oscillator : public AudioSourceNode {
+public:
+    // The waveform type.
+    // These must be defined as in the .idl file.
+    enum {
+        SINE = 0,
+        SQUARE = 1,
+        SAWTOOTH = 2,
+        TRIANGLE = 3,
+        CUSTOM = 4
+    };
+
+    static PassRefPtr<Oscillator> create(AudioContext*, float sampleRate);
+
+    virtual ~Oscillator();
+    
+    // AudioNode
+    virtual void process(size_t framesToProcess);
+    virtual void reset();
+
+    unsigned short type() const { return m_type; }
+    void setType(unsigned short);
+
+    AudioParam* frequency() { return m_frequency.get(); }
+    AudioParam* detune() { return m_detune.get(); }
+
+    void setWaveTable(WaveTable*);
+
+private:
+    Oscillator(AudioContext*, float sampleRate);
+
+    // Returns true if there are sample-accurate timeline parameter changes.
+    bool calculateSampleAccuratePhaseIncrements(size_t framesToProcess);
+
+    // One of the waveform types defined in the enum.
+    unsigned short m_type;
+    
+    // Frequency value in Hertz.
+    RefPtr<AudioParam> m_frequency;
+
+    // Detune value (deviating from the frequency) in Cents.
+    RefPtr<AudioParam> m_detune;
+
+    // m_virtualReadIndex is a sample-frame index into our buffer representing the current playback position.
+    // Since it's floating-point, it has sub-sample accuracy.
+    double m_virtualReadIndex;
+
+    // This synchronizes process().
+    mutable Mutex m_processLock;
+
+    // Stores sample-accurate values calculated according to frequency and detune.
+    AudioFloatArray m_phaseIncrements;
+    AudioFloatArray m_detuneValues;
+    
+    RefPtr<WaveTable> m_waveTable;
+};
+
+} // namespace WebCore
+
+#endif // Oscillator_h
diff --git a/Source/WebCore/Modules/webaudio/Oscillator.idl b/Source/WebCore/Modules/webaudio/Oscillator.idl
new file mode 100644 (file)
index 0000000..c53a951
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2012, Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1.  Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2.  Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+module audio {
+    // Oscillator is an audio generator of periodic waveforms.
+    interface [
+        Conditional=WEB_AUDIO,
+        JSGenerateToJSObject
+    ] Oscillator : AudioSourceNode {
+
+        // Type constants.
+        const unsigned short SINE = 0;
+        const unsigned short SQUARE = 1;
+        const unsigned short SAWTOOTH = 2;
+        const unsigned short TRIANGLE = 3;
+        const unsigned short CUSTOM = 4;
+        
+        attribute unsigned short type;
+
+        readonly attribute AudioParam frequency; // in Hertz
+        readonly attribute AudioParam detune; // in Cents
+
+        void setWaveTable(in WaveTable waveTable);
+
+    };
+}
diff --git a/Source/WebCore/Modules/webaudio/WaveTable.cpp b/Source/WebCore/Modules/webaudio/WaveTable.cpp
new file mode 100644 (file)
index 0000000..9bfb204
--- /dev/null
@@ -0,0 +1,280 @@
+/*
+ * Copyright (C) 2012 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1.  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ * 2.  Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ *     its contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if ENABLE(WEB_AUDIO)
+
+#include "WaveTable.h"
+
+#include "FFTFrame.h"
+#include "Oscillator.h"
+#include "VectorMath.h"
+#include <algorithm>
+#include <wtf/OwnPtr.h>
+
+const unsigned WaveTableSize = 4096; // This must be a power of two.
+const unsigned NumberOfRanges = 36; // There should be 3 * log2(WaveTableSize) 1/3 octave ranges.
+const float CentsPerRange = 1200 / 3; // 1/3 Octave.
+
+namespace WebCore {
+    
+using namespace VectorMath;
+
+PassRefPtr<WaveTable> WaveTable::create(float sampleRate, Float32Array* real, Float32Array* imag)
+{
+    bool isGood = real && imag && real->length() == imag->length();
+    ASSERT(isGood);
+    if (isGood) {
+        RefPtr<WaveTable> waveTable = adoptRef(new WaveTable(sampleRate));
+        size_t numberOfComponents = real->length();
+        waveTable->createBandLimitedTables(real->data(), imag->data(), numberOfComponents);
+        return waveTable;
+    }
+    return 0;
+}
+
+PassRefPtr<WaveTable> WaveTable::createSine(float sampleRate)
+{
+    RefPtr<WaveTable> waveTable = adoptRef(new WaveTable(sampleRate));
+    waveTable->generateBasicWaveform(Oscillator::SINE);
+    return waveTable;
+}
+
+PassRefPtr<WaveTable> WaveTable::createSquare(float sampleRate)
+{
+    RefPtr<WaveTable> waveTable = adoptRef(new WaveTable(sampleRate));
+    waveTable->generateBasicWaveform(Oscillator::SQUARE);
+    return waveTable;
+}
+
+PassRefPtr<WaveTable> WaveTable::createSawtooth(float sampleRate)
+{
+    RefPtr<WaveTable> waveTable = adoptRef(new WaveTable(sampleRate));
+    waveTable->generateBasicWaveform(Oscillator::SAWTOOTH);
+    return waveTable;
+}
+
+PassRefPtr<WaveTable> WaveTable::createTriangle(float sampleRate)
+{
+    RefPtr<WaveTable> waveTable = adoptRef(new WaveTable(sampleRate));
+    waveTable->generateBasicWaveform(Oscillator::TRIANGLE);
+    return waveTable;
+}
+
+WaveTable::WaveTable(float sampleRate)
+    : m_sampleRate(sampleRate)
+    , m_waveTableSize(WaveTableSize)
+    , m_numberOfRanges(NumberOfRanges)
+    , m_centsPerRange(CentsPerRange)
+{
+    float nyquist = 0.5 * m_sampleRate;
+    m_lowestFundamentalFrequency = nyquist / maxNumberOfPartials();
+    m_rateScale = m_waveTableSize / m_sampleRate;
+}
+
+void WaveTable::waveDataForFundamentalFrequency(float fundamentalFrequency, float* &lowerWaveData, float* &higherWaveData, float& tableInterpolationFactor)
+{
+    // Calculate the pitch range.
+    float ratio = fundamentalFrequency > 0 ? fundamentalFrequency / m_lowestFundamentalFrequency : 0.5;
+    float centsAboveLowestFrequency = log2f(ratio) * 1200;
+
+    // Add one to round-up to the next range just in time to truncate partials before aliasing occurs.
+    float pitchRange = 1 + centsAboveLowestFrequency / m_centsPerRange;
+
+    pitchRange = std::max(pitchRange, 0.0f);
+    pitchRange = std::min(pitchRange, static_cast<float>(m_numberOfRanges - 1));
+
+    // The words "lower" and "higher" refer to the table data having the lower and higher numbers of partials.
+    // It's a little confusing since the range index gets larger the more partials we cull out.
+    // So the lower table data will have a larger range index.
+    unsigned rangeIndex1 = static_cast<unsigned>(pitchRange);
+    unsigned rangeIndex2 = rangeIndex1 < m_numberOfRanges - 1 ? rangeIndex1 + 1 : rangeIndex1;
+
+    lowerWaveData = m_bandLimitedTables[rangeIndex2]->data();
+    higherWaveData = m_bandLimitedTables[rangeIndex1]->data();
+    
+    // Ranges from 0 -> 1 to interpolate between lower -> higher.
+    tableInterpolationFactor = pitchRange - rangeIndex1;
+}
+
+unsigned WaveTable::maxNumberOfPartials() const
+{
+    return m_waveTableSize / 2;
+}
+
+unsigned WaveTable::numberOfPartialsForRange(unsigned rangeIndex) const
+{
+    // Number of cents below nyquist where we cull partials.
+    float centsToCull = rangeIndex * m_centsPerRange;
+
+    // A value from 0 -> 1 representing what fraction of the partials to keep.
+    float cullingScale = pow(2, -centsToCull / 1200);
+
+    // The very top range will have all the partials culled.
+    unsigned numberOfPartials = cullingScale * maxNumberOfPartials();
+
+    return numberOfPartials;
+}
+
+// Convert into time-domain wave tables.
+// One table is created for each range for non-aliasing playback at different playback rates.
+// Thus, higher ranges have more high-frequency partials culled out.
+void WaveTable::createBandLimitedTables(const float* realData, const float* imagData, unsigned numberOfComponents)
+{
+    float normalizationScale = 1;
+
+    unsigned fftSize = m_waveTableSize;
+    unsigned halfSize = fftSize / 2;
+    unsigned i;
+    
+    numberOfComponents = std::min(numberOfComponents, halfSize);
+
+    m_bandLimitedTables.reserveCapacity(m_numberOfRanges);
+
+    for (unsigned rangeIndex = 0; rangeIndex < m_numberOfRanges; ++rangeIndex) {
+        // This FFTFrame is used to cull partials (represented by frequency bins).
+        FFTFrame frame(fftSize);
+        float* realP = frame.realData();
+        float* imagP = frame.imagData();
+
+        // Copy from loaded frequency data and scale.
+        float scale = fftSize;
+        vsmul(realData, 1, &scale, realP, 1, numberOfComponents);
+        vsmul(imagData, 1, &scale, imagP, 1, numberOfComponents);
+
+        // If fewer components were provided than 1/2 FFT size, then clear the remaining bins.
+        for (i = numberOfComponents; i < halfSize; ++i) {
+            realP[i] = 0;
+            imagP[i] = 0;
+        }
+        
+        // Generate complex conjugate because of the way the inverse FFT is defined.
+        float minusOne = -1;
+        vsmul(imagP, 1, &minusOne, imagP, 1, halfSize);
+
+        // Find the starting bin where we should start culling.
+        // We need to clear out the highest frequencies to band-limit the waveform.
+        unsigned numberOfPartials = numberOfPartialsForRange(rangeIndex);
+
+        // Cull the aliasing partials for this pitch range.
+        for (i = numberOfPartials + 1; i < halfSize; ++i) {
+            realP[i] = 0;
+            imagP[i] = 0;
+        }
+        // Clear packed-nyquist if necessary.
+        if (numberOfPartials < halfSize)
+            imagP[0] = 0;
+
+        // Clear any DC-offset.
+        realP[0] = 0;
+
+        // Create the band-limited table.
+        OwnPtr<AudioFloatArray> table = adoptPtr(new AudioFloatArray(m_waveTableSize));
+        m_bandLimitedTables.append(table.release());
+
+        // Apply an inverse FFT to generate the time-domain table data.
+        float* data = m_bandLimitedTables[rangeIndex]->data();
+        frame.doInverseFFT(data);
+
+        // For the first range (which has the highest power), calculate its peak value then compute normalization scale.
+        if (!rangeIndex) {
+            float maxValue;
+            vmaxmgv(data, 1, &maxValue, m_waveTableSize);
+
+            if (maxValue)
+                normalizationScale = 0.5f / maxValue;
+        }
+
+        // Apply normalization scale.
+        vsmul(data, 1, &normalizationScale, data, 1, m_waveTableSize);          
+    }
+}
+
+void WaveTable::generateBasicWaveform(int shape)
+{
+    unsigned fftSize = waveTableSize();
+    unsigned halfSize = fftSize / 2;
+
+    AudioFloatArray real(halfSize);
+    AudioFloatArray imag(halfSize);
+    float* realP = real.data();
+    float* imagP = imag.data();
+
+    // Clear DC and Nyquist.
+    realP[0] = 0;
+    imagP[0] = 0;
+
+    for (unsigned n = 1; n < halfSize; ++n) {
+        float omega = 2 * piFloat * n;
+        float invOmega = 1 / omega;
+
+        // Fourier coefficients according to standard definition.
+        float a; // Coefficient for cos().
+        float b; // Coefficient for sin().
+
+        // Calculate Fourier coefficients depending on the shape.
+        // Note that the overall scaling (magnitude) of the waveforms is normalized in createBandLimitedTables().
+        switch (shape) {
+        case Oscillator::SINE:
+            // Standard sine wave function.
+            a = 0;
+            b = (n == 1) ? 1 : 0;
+            break;
+        case Oscillator::SQUARE:
+            // Square-shaped waveform with the first half its maximum value and the second half its minimum value.
+            a = 0;
+            b = invOmega * ((n & 1) ? 2 : 0);
+            break;
+        case Oscillator::SAWTOOTH:
+            // Sawtooth-shaped waveform with the first half ramping from zero to maximum and the second half from minimum to zero.
+            a = 0;
+            b = -invOmega * cos(0.5 * omega);
+            break;
+        case Oscillator::TRIANGLE:
+            // Triangle-shaped waveform going from its maximum value to its minimum value then back to the maximum value.
+            a = (4 - 4 * cos(0.5 * omega)) / (n * n * piFloat * piFloat);
+            b = 0;
+            break;
+        default:
+            ASSERT_NOT_REACHED();
+            a = 0;
+            b = 0;
+            break;
+        }
+
+        realP[n] = a;
+        imagP[n] = b;
+    }
+
+    createBandLimitedTables(realP, imagP, halfSize);
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_AUDIO)
diff --git a/Source/WebCore/Modules/webaudio/WaveTable.h b/Source/WebCore/Modules/webaudio/WaveTable.h
new file mode 100644 (file)
index 0000000..a84eb93
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2012 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1.  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ * 2.  Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ *     its contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef WaveTable_h
+#define WaveTable_h
+
+#include "AudioArray.h"
+#include <wtf/Float32Array.h>
+#include <wtf/OwnPtr.h>
+#include <wtf/PassRefPtr.h>
+#include <wtf/RefCounted.h>
+#include <wtf/RefPtr.h>
+#include <wtf/Vector.h>
+
+namespace WebCore {
+
+class WaveTable : public RefCounted<WaveTable> {
+public:
+    static PassRefPtr<WaveTable> createSine(float sampleRate);
+    static PassRefPtr<WaveTable> createSquare(float sampleRate);
+    static PassRefPtr<WaveTable> createSawtooth(float sampleRate);
+    static PassRefPtr<WaveTable> createTriangle(float sampleRate);
+
+    // Creates an arbitrary wavetable given the frequency components (Fourier coefficients).
+    static PassRefPtr<WaveTable> create(float sampleRate, Float32Array* real, Float32Array* imag);
+
+    // Returns pointers to the lower and higher wavetable data for the pitch range containing
+    // the given fundamental frequency. These two tables are in adjacent "pitch" ranges
+    // where the higher table will have the maximum number of partials which won't alias when played back
+    // at this fundamental frequency. The lower wavetable is the next range containing fewer partials than the higher wavetable.
+    // Interpolation between these two tables can be made according to tableInterpolationFactor.
+    // Where values from 0 -> 1 interpolate between lower -> higher.
+    void waveDataForFundamentalFrequency(float, float* &lowerWaveData, float* &higherWaveData, float& tableInterpolationFactor);
+
+    // Returns the scalar multiplier to the oscillator frequency to calculate wave table phase increment.
+    float rateScale() const { return m_rateScale; }
+
+    unsigned waveTableSize() const { return m_waveTableSize; }
+    float sampleRate() const { return m_sampleRate; }
+
+private:
+    WaveTable(float sampleRate);
+
+    void generateBasicWaveform(int);
+
+    float m_sampleRate;
+    unsigned m_waveTableSize;
+    unsigned m_numberOfRanges;
+    float m_centsPerRange;
+
+    // The lowest frequency (in Hertz) where playback will include all of the partials.
+    // Playing back lower than this frequency will gradually lose more high-frequency information.
+    // This frequency is quite low (~10Hz @ 44.1KHz)
+    float m_lowestFundamentalFrequency;
+
+    float m_rateScale;
+
+    unsigned numberOfRanges() const { return m_numberOfRanges; }
+
+    // Maximum possible number of partials (before culling).
+    unsigned maxNumberOfPartials() const;
+
+    unsigned numberOfPartialsForRange(unsigned rangeIndex) const;
+
+    // Creates tables based on numberOfComponents Fourier coefficients.
+    void createBandLimitedTables(const float* real, const float* imag, unsigned numberOfComponents);
+    Vector<OwnPtr<AudioFloatArray> > m_bandLimitedTables;
+};
+
+} // namespace WebCore
+
+#endif // WaveTable_h
diff --git a/Source/WebCore/Modules/webaudio/WaveTable.idl b/Source/WebCore/Modules/webaudio/WaveTable.idl
new file mode 100644 (file)
index 0000000..22403c3
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2012, Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1.  Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2.  Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+module audio {
+    // WaveTable represents a periodic audio waveform given by its Fourier coefficients.
+    interface [
+        Conditional=WEB_AUDIO
+    ] WaveTable {
+
+    };
+}
index 9dc589f..ca4a209 100644 (file)
             'Modules/webaudio/JavaScriptAudioNode.idl',
             'Modules/webaudio/MediaElementAudioSourceNode.idl',
             'Modules/webaudio/OfflineAudioCompletionEvent.idl',
+            'Modules/webaudio/Oscillator.idl',
             'Modules/webaudio/RealtimeAnalyserNode.idl',
             'Modules/webaudio/WaveShaperNode.idl',
+            'Modules/webaudio/WaveTable.idl',
             'Modules/webdatabase/DOMWindowWebDatabase.idl',
             'Modules/webdatabase/Database.idl',
             'Modules/webdatabase/DatabaseCallback.idl',
             'Modules/webaudio/OfflineAudioCompletionEvent.h',
             'Modules/webaudio/OfflineAudioDestinationNode.cpp',
             'Modules/webaudio/OfflineAudioDestinationNode.h',
+            'Modules/webaudio/Oscillator.cpp',
+            'Modules/webaudio/Oscillator.h',
             'Modules/webaudio/RealtimeAnalyser.cpp',
             'Modules/webaudio/RealtimeAnalyser.h',
             'Modules/webaudio/RealtimeAnalyserNode.cpp',
             'Modules/webaudio/WaveShaperNode.h',
             'Modules/webaudio/WaveShaperProcessor.cpp',
             'Modules/webaudio/WaveShaperProcessor.h',
+            'Modules/webaudio/WaveTable.cpp',
+            'Modules/webaudio/WaveTable.h',
             'Modules/webdatabase/AbstractDatabase.cpp',
             'Modules/webdatabase/ChangeVersionWrapper.cpp',
             'Modules/webdatabase/ChangeVersionWrapper.h',
index 229aaf0..5d9bbf0 100644 (file)
                FD5686C913AC180200B69C68 /* AsyncAudioDecoder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FD5686C713AC180200B69C68 /* AsyncAudioDecoder.cpp */; };
                FD5686CA13AC180200B69C68 /* AsyncAudioDecoder.h in Headers */ = {isa = PBXBuildFile; fileRef = FD5686C813AC180200B69C68 /* AsyncAudioDecoder.h */; };
                FD5686CC13AC181400B69C68 /* AudioBufferCallback.h in Headers */ = {isa = PBXBuildFile; fileRef = FD5686CB13AC181400B69C68 /* AudioBufferCallback.h */; };
+               FD581FAE1520F91F003A7A75 /* Oscillator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FD581FAB1520F91F003A7A75 /* Oscillator.cpp */; };
+               FD581FAF1520F91F003A7A75 /* Oscillator.h in Headers */ = {isa = PBXBuildFile; fileRef = FD581FAC1520F91F003A7A75 /* Oscillator.h */; };
+               FD581FB41520F93B003A7A75 /* WaveTable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FD581FB11520F93B003A7A75 /* WaveTable.cpp */; };
+               FD581FB51520F93B003A7A75 /* WaveTable.h in Headers */ = {isa = PBXBuildFile; fileRef = FD581FB21520F93B003A7A75 /* WaveTable.h */; };
                FD62F52E145898D80094B0ED /* AudioSourceProviderClient.h in Headers */ = {isa = PBXBuildFile; fileRef = FD62F52D145898D80094B0ED /* AudioSourceProviderClient.h */; };
                FD6ED2C3136B8E42003CF072 /* DynamicsCompressorNode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FD6ED2C1136B8E42003CF072 /* DynamicsCompressorNode.cpp */; };
                FD6ED2C4136B8E42003CF072 /* DynamicsCompressorNode.h in Headers */ = {isa = PBXBuildFile; fileRef = FD6ED2C2136B8E42003CF072 /* DynamicsCompressorNode.h */; };
                FDB1700614A2BAB200A2B5D9 /* MultiChannelResampler.h in Headers */ = {isa = PBXBuildFile; fileRef = FDB1700414A2BAB200A2B5D9 /* MultiChannelResampler.h */; };
                FDC54F041399B0DA008D9117 /* BiquadFilterNode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FDC54F011399B0DA008D9117 /* BiquadFilterNode.cpp */; };
                FDC54F051399B0DA008D9117 /* BiquadFilterNode.h in Headers */ = {isa = PBXBuildFile; fileRef = FDC54F021399B0DA008D9117 /* BiquadFilterNode.h */; };
+               FDEA6242152102E200479DF0 /* JSOscillator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FDEA6240152102E200479DF0 /* JSOscillator.cpp */; };
+               FDEA6243152102E200479DF0 /* JSOscillator.h in Headers */ = {isa = PBXBuildFile; fileRef = FDEA6241152102E200479DF0 /* JSOscillator.h */; };
+               FDEA6246152102FC00479DF0 /* JSWaveTable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FDEA6244152102FC00479DF0 /* JSWaveTable.cpp */; };
+               FDEA6247152102FC00479DF0 /* JSWaveTable.h in Headers */ = {isa = PBXBuildFile; fileRef = FDEA6245152102FC00479DF0 /* JSWaveTable.h */; };
                FDEAAAF312B02EE400DCF33B /* JSAudioBufferSourceNodeCustom.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FDEAAAEF12B02EE400DCF33B /* JSAudioBufferSourceNodeCustom.cpp */; };
                FDEAAAF412B02EE400DCF33B /* JSAudioContextCustom.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FDEAAAF012B02EE400DCF33B /* JSAudioContextCustom.cpp */; };
                FDEAAAF612B02EE400DCF33B /* JSConvolverNodeCustom.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FDEAAAF212B02EE400DCF33B /* JSConvolverNodeCustom.cpp */; };
                FD5686C813AC180200B69C68 /* AsyncAudioDecoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AsyncAudioDecoder.h; sourceTree = "<group>"; };
                FD5686CB13AC181400B69C68 /* AudioBufferCallback.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AudioBufferCallback.h; sourceTree = "<group>"; };
                FD5686CD13AC183E00B69C68 /* AudioBufferCallback.idl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = AudioBufferCallback.idl; sourceTree = "<group>"; };
+               FD581FAB1520F91F003A7A75 /* Oscillator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Oscillator.cpp; sourceTree = "<group>"; };
+               FD581FAC1520F91F003A7A75 /* Oscillator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Oscillator.h; sourceTree = "<group>"; };
+               FD581FAD1520F91F003A7A75 /* Oscillator.idl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Oscillator.idl; sourceTree = "<group>"; };
+               FD581FB11520F93B003A7A75 /* WaveTable.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WaveTable.cpp; sourceTree = "<group>"; };
+               FD581FB21520F93B003A7A75 /* WaveTable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WaveTable.h; sourceTree = "<group>"; };
+               FD581FB31520F93B003A7A75 /* WaveTable.idl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = WaveTable.idl; sourceTree = "<group>"; };
                FD62F52D145898D80094B0ED /* AudioSourceProviderClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AudioSourceProviderClient.h; sourceTree = "<group>"; };
                FD6ED2C1136B8E42003CF072 /* DynamicsCompressorNode.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DynamicsCompressorNode.cpp; sourceTree = "<group>"; };
                FD6ED2C2136B8E42003CF072 /* DynamicsCompressorNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DynamicsCompressorNode.h; sourceTree = "<group>"; };
                FDC54F011399B0DA008D9117 /* BiquadFilterNode.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BiquadFilterNode.cpp; sourceTree = "<group>"; };
                FDC54F021399B0DA008D9117 /* BiquadFilterNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BiquadFilterNode.h; sourceTree = "<group>"; };
                FDC54F031399B0DA008D9117 /* BiquadFilterNode.idl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = BiquadFilterNode.idl; sourceTree = "<group>"; };
+               FDEA6240152102E200479DF0 /* JSOscillator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSOscillator.cpp; sourceTree = "<group>"; };
+               FDEA6241152102E200479DF0 /* JSOscillator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSOscillator.h; sourceTree = "<group>"; };
+               FDEA6244152102FC00479DF0 /* JSWaveTable.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSWaveTable.cpp; sourceTree = "<group>"; };
+               FDEA6245152102FC00479DF0 /* JSWaveTable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSWaveTable.h; sourceTree = "<group>"; };
                FDEAAAEF12B02EE400DCF33B /* JSAudioBufferSourceNodeCustom.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSAudioBufferSourceNodeCustom.cpp; sourceTree = "<group>"; };
                FDEAAAF012B02EE400DCF33B /* JSAudioContextCustom.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSAudioContextCustom.cpp; sourceTree = "<group>"; };
                FDEAAAF212B02EE400DCF33B /* JSConvolverNodeCustom.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSConvolverNodeCustom.cpp; sourceTree = "<group>"; };
                                FDA3E95D134A49FF008D4B5A /* OfflineAudioCompletionEvent.idl */,
                                FDA3E957134A49EF008D4B5A /* OfflineAudioDestinationNode.cpp */,
                                FDA3E958134A49EF008D4B5A /* OfflineAudioDestinationNode.h */,
+                               FD581FAB1520F91F003A7A75 /* Oscillator.cpp */,
+                               FD581FAC1520F91F003A7A75 /* Oscillator.h */,
+                               FD581FAD1520F91F003A7A75 /* Oscillator.idl */,
                                FD315FF112B0267600C1A359 /* RealtimeAnalyser.cpp */,
                                FD315FF212B0267600C1A359 /* RealtimeAnalyser.h */,
                                FD315FF312B0267600C1A359 /* RealtimeAnalyserNode.cpp */,
                                FD7F298E13D4C0CB00AD9535 /* WaveShaperNode.idl */,
                                FD7F298F13D4C0CB00AD9535 /* WaveShaperProcessor.cpp */,
                                FD7F299013D4C0CB00AD9535 /* WaveShaperProcessor.h */,
+                               FD581FB11520F93B003A7A75 /* WaveTable.cpp */,
+                               FD581FB21520F93B003A7A75 /* WaveTable.h */,
+                               FD581FB31520F93B003A7A75 /* WaveTable.idl */,
                        );
                        path = Modules/webaudio;
                        sourceTree = "<group>";
                                FD23A12413F5FA5900F67001 /* JSMediaElementAudioSourceNode.h */,
                                FDF6BAF6134A4C9800822920 /* JSOfflineAudioCompletionEvent.cpp */,
                                FDF6BAF7134A4C9800822920 /* JSOfflineAudioCompletionEvent.h */,
+                               FDEA6240152102E200479DF0 /* JSOscillator.cpp */,
+                               FDEA6241152102E200479DF0 /* JSOscillator.h */,
                                FDA15EC712B03F50003A583A /* JSRealtimeAnalyserNode.cpp */,
                                FDA15EC812B03F50003A583A /* JSRealtimeAnalyserNode.h */,
                                FD82D7F513D4C8BD004E4372 /* JSWaveShaperNode.cpp */,
                                FD82D7F613D4C8BD004E4372 /* JSWaveShaperNode.h */,
+                               FDEA6244152102FC00479DF0 /* JSWaveTable.cpp */,
+                               FDEA6245152102FC00479DF0 /* JSWaveTable.h */,
                        );
                        name = WebAudio;
                        sourceTree = "<group>";
                                93F199ED08245E59001E9ABC /* XSLTProcessor.h in Headers */,
                                E1BE512E0CF6C512002EA959 /* XSLTUnicodeSort.h in Headers */,
                                977E2E0F12F0FC9C00C13379 /* XSSAuditor.h in Headers */,
+                FD581FAF1520F91F003A7A75 /* Oscillator.h in Headers */,
+                FD581FB51520F93B003A7A75 /* WaveTable.h in Headers */,
+                FDEA6243152102E200479DF0 /* JSOscillator.h in Headers */,
+                FDEA6247152102FC00479DF0 /* JSWaveTable.h in Headers */,
                                FD537353137B651800008DCE /* ZeroPole.h in Headers */,
                                1A3586E015264C450022A659 /* RenderMultiColumnFlowThread.h in Headers */,
                                E1B25107152A0BB00069B779 /* StylePropertyShorthand.h in Headers */,
                                93F19B0508245E59001E9ABC /* XSLTProcessorLibxslt.cpp in Sources */,
                                E1BE512D0CF6C512002EA959 /* XSLTUnicodeSort.cpp in Sources */,
                                977E2E0E12F0FC9C00C13379 /* XSSAuditor.cpp in Sources */,
+                               FD581FAE1520F91F003A7A75 /* Oscillator.cpp in Sources */,
+                               FD581FB41520F93B003A7A75 /* WaveTable.cpp in Sources */,
+                               FDEA6242152102E200479DF0 /* JSOscillator.cpp in Sources */,
+                               FDEA6246152102FC00479DF0 /* JSWaveTable.cpp in Sources */,
                                FD537352137B651800008DCE /* ZeroPole.cpp in Sources */,
                                1A35862C152522540022A659 /* ScrollingTreeMac.mm in Sources */,
                                1A3586DF15264C450022A659 /* RenderMultiColumnFlowThread.cpp in Sources */,