add stereo source support in EqualPowerPanner
https://bugs.webkit.org/show_bug.cgi?id=81092

Patch by Wei James <james.wei@intel.com> on 2012-04-11
Reviewed by Chris Rogers.

Source/WebCore:

Test: webaudio/panner-equalpower-stereo.html

* platform/audio/EqualPowerPanner.cpp:
(WebCore::EqualPowerPanner::pan):

LayoutTests:

* webaudio/panner-equalpower-stereo-expected.txt: Added.
* webaudio/panner-equalpower-stereo.html: Copied from LayoutTests/webaudio/panner-equalpower.html.
* webaudio/panner-equalpower.html:
* webaudio/resources/audio-testing.js:
(createStereoImpulseBuffer):
* webaudio/resources/panner-model-testing.js:
(createGraph):
(createTestAndRun):
(equalPowerGain):
(checkResult):

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@113889 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/WebCore/platform/audio/EqualPowerPanner.cpp b/Source/WebCore/platform/audio/EqualPowerPanner.cpp
index a2d2dff..9035367 100644
--- a/Source/WebCore/platform/audio/EqualPowerPanner.cpp
+++ b/Source/WebCore/platform/audio/EqualPowerPanner.cpp
@@ -51,8 +51,8 @@
 
 void EqualPowerPanner::pan(double azimuth, double /*elevation*/, const AudioBus* inputBus, AudioBus* outputBus, size_t framesToProcess)
 {
-    // FIXME: implement stereo sources
-    bool isInputSafe = inputBus && inputBus->numberOfChannels() == 1 && framesToProcess <= inputBus->length();
+    unsigned numberOfInputChannels = inputBus->numberOfChannels();
+    bool isInputSafe = inputBus && (numberOfInputChannels == 1 || numberOfInputChannels == 2) && framesToProcess <= inputBus->length();
     ASSERT(isInputSafe);
     if (!isInputSafe)
         return;
@@ -62,14 +62,14 @@
     if (!isOutputSafe)
         return;
 
-    const AudioChannel* channel = inputBus->channel(0);
-    const float* sourceP = channel->data();                               
+    const float* sourceL = inputBus->channel(0)->data();                               
+    const float* sourceR = numberOfInputChannels > 1 ? inputBus->channel(1)->data() : sourceL;
     float* destinationL = outputBus->channelByType(AudioBus::ChannelLeft)->mutableData();
     float* destinationR = outputBus->channelByType(AudioBus::ChannelRight)->mutableData();
-
-    if (!sourceP || !destinationL || !destinationR)
+    
+    if (!sourceL || !sourceR || !destinationL || !destinationR)
         return;
-
+    
     // Clamp azimuth to allowed range of -180 -> +180.
     azimuth = max(-180.0, azimuth);
     azimuth = min(180.0, azimuth);
@@ -80,35 +80,73 @@
         azimuth = -180 - azimuth;
     else if (azimuth > 90)
         azimuth = 180 - azimuth;
-    
-    // Pan smoothly from left to right with azimuth going from -90 -> +90 degrees.
-    double desiredPanPosition = (azimuth + 90) / 180;
 
-    double desiredGainL = cos(0.5 * piDouble * desiredPanPosition);
-    double desiredGainR = sin(0.5 * piDouble * desiredPanPosition);
+    double desiredPanPosition;
+    double desiredGainL;
+    double desiredGainR;
 
+    if (numberOfInputChannels == 1) { // For mono source case.
+        // Pan smoothly from left to right with azimuth going from -90 -> +90 degrees.
+        desiredPanPosition = (azimuth + 90) / 180;
+    } else { // For stereo source case.
+        if (azimuth <= 0) { // from -90 -> 0
+            // sourceL -> destL and "equal-power pan" sourceR as in mono case
+            // by transforming the "azimuth" value from -90 -> 0 degrees into the range -90 -> +90.
+            desiredPanPosition = (azimuth + 90) / 90;
+        } else { // from 0 -> +90
+            // sourceR -> destR and "equal-power pan" sourceL as in mono case
+            // by transforming the "azimuth" value from 0 -> +90 degrees into the range -90 -> +90.
+            desiredPanPosition = azimuth / 90;
+        }
+    }
+
+    desiredGainL = cos(0.5 * piDouble * desiredPanPosition);
+    desiredGainR = sin(0.5 * piDouble * desiredPanPosition);
+   
     // Don't de-zipper on first render call.
     if (m_isFirstRender) {
         m_isFirstRender = false;
         m_gainL = desiredGainL;
         m_gainR = desiredGainR;
     }
-
+    
     // Cache in local variables.
     double gainL = m_gainL;
     double gainR = m_gainR;
-
+    
     // Get local copy of smoothing constant.
     const double SmoothingConstant = m_smoothingConstant;
-
+    
     int n = framesToProcess;
 
-    while (n--) {
-        float input = *sourceP++;
-        gainL += (desiredGainL - gainL) * SmoothingConstant;
-        gainR += (desiredGainR - gainR) * SmoothingConstant;
-        *destinationL++ = static_cast<float>(input * gainL);
-        *destinationR++ = static_cast<float>(input * gainR);
+    if (numberOfInputChannels == 1) { // For mono source case.
+        while (n--) {
+            float inputL = *sourceL++;
+            gainL += (desiredGainL - gainL) * SmoothingConstant;
+            gainR += (desiredGainR - gainR) * SmoothingConstant;
+            *destinationL++ = static_cast<float>(inputL * gainL);
+            *destinationR++ = static_cast<float>(inputL * gainR);
+        }
+    } else { // For stereo source case.
+        if (azimuth <= 0) { // from -90 -> 0
+            while (n--) {
+                float inputL = *sourceL++;
+                float inputR = *sourceR++;
+                gainL += (desiredGainL - gainL) * SmoothingConstant;
+                gainR += (desiredGainR - gainR) * SmoothingConstant;
+                *destinationL++ = static_cast<float>(inputL + inputR * gainL);
+                *destinationR++ = static_cast<float>(inputR * gainR);
+            }
+        } else { // from 0 -> +90
+            while (n--) {
+                float inputL = *sourceL++;
+                float inputR = *sourceR++;
+                gainL += (desiredGainL - gainL) * SmoothingConstant;
+                gainR += (desiredGainR - gainR) * SmoothingConstant;
+                *destinationL++ = static_cast<float>(inputL * gainL);
+                *destinationR++ = static_cast<float>(inputR + inputL * gainR);
+            }
+        }
     }
 
     m_gainL = gainL;