flowgraph: add PolyphaseResamplerStereo
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 5303f26..50b758f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -28,7 +28,8 @@
     src/flowgraph/ManyToMultiConverter.cpp
     src/flowgraph/MonoToMultiConverter.cpp
     src/flowgraph/MultiChannelResampler.cpp
-    src/flowgraph/PolyphaseSincResampler.cpp
+    src/flowgraph/PolyphaseResampler.cpp
+    src/flowgraph/PolyphaseResamplerStereo.cpp
     src/flowgraph/RampLinear.cpp
     src/flowgraph/SampleRateConverter.cpp
     src/flowgraph/SincResampler.cpp
diff --git a/src/flowgraph/MultiChannelResampler.cpp b/src/flowgraph/MultiChannelResampler.cpp
index a4b15ab..7987ecd 100644
--- a/src/flowgraph/MultiChannelResampler.cpp
+++ b/src/flowgraph/MultiChannelResampler.cpp
@@ -16,10 +16,12 @@
 
 #include <math.h>
 
-#include "MultiChannelResampler.h"
+#include "IntegerRatio.h"
 #include "LinearResampler.h"
+#include "MultiChannelResampler.h"
+#include "PolyphaseResampler.h"
+#include "PolyphaseResamplerStereo.h"
 #include "SincResampler.h"
-#include "PolyphaseSincResampler.h"
 #include "SincResamplerStereo.h"
 
 using namespace flowgraph;
@@ -34,7 +36,11 @@
             return new LinearResampler(channelCount, inputRate, outputRate);
         default:
         case Quality::High:
-            return new PolyphaseSincResampler(channelCount, inputRate, outputRate); // TODO
+            if (channelCount == 2) {
+                return new PolyphaseResamplerStereo(inputRate, outputRate);
+            } else {
+                return new PolyphaseResampler(channelCount, inputRate, outputRate);
+            }
         case Quality::Best:
             if (channelCount == 2) {
                 return new SincResamplerStereo( inputRate, outputRate); // TODO pass spread
diff --git a/src/flowgraph/PolyphaseSincResampler.cpp b/src/flowgraph/PolyphaseResampler.cpp
similarity index 86%
rename from src/flowgraph/PolyphaseSincResampler.cpp
rename to src/flowgraph/PolyphaseResampler.cpp
index 9ed3212..af9b699 100644
--- a/src/flowgraph/PolyphaseSincResampler.cpp
+++ b/src/flowgraph/PolyphaseResampler.cpp
@@ -15,11 +15,11 @@
  */
 
 #include "IntegerRatio.h"
-#include "PolyphaseSincResampler.h"
+#include "PolyphaseResampler.h"
 
 using namespace flowgraph;
 
-PolyphaseSincResampler::PolyphaseSincResampler(int32_t channelCount,
+PolyphaseResampler::PolyphaseResampler(int32_t channelCount,
                              int32_t inputRate,
                              int32_t outputRate)
         : MultiChannelResampler(channelCount, kNumTaps, inputRate, outputRate)
@@ -27,7 +27,7 @@
     generateCoefficients(inputRate, outputRate);
 }
 
-void PolyphaseSincResampler::generateCoefficients(int32_t inputRate, int32_t outputRate) {
+void PolyphaseResampler::generateCoefficients(int32_t inputRate, int32_t outputRate) {
     IntegerRatio ratio(inputRate, outputRate);
     ratio.reduce();
     mNumerator = ratio.getNumerator();
@@ -49,7 +49,7 @@
         }
     }
 }
-void PolyphaseSincResampler::readFrame(float *frame) {
+void PolyphaseResampler::readFrame(float *frame) {
     // Clear accumulator for mix.
     for (int channel = 0; channel < getChannelCount(); channel++) {
         mSingleFrame[channel] = 0.0;
@@ -67,10 +67,7 @@
         xIndex -= getChannelCount();
     }
 
-    mCoefficientCursor += kNumTaps;
-    if (mCoefficientCursor >= mCoefficients.size()) {
-        mCoefficientCursor = 0;
-    }
+    mCoefficientCursor = (mCoefficientCursor + kNumTaps) % mCoefficients.size();
 
     // Copy accumulator to output.
     for (int channel = 0; channel < getChannelCount(); channel++) {
diff --git a/src/flowgraph/PolyphaseSincResampler.h b/src/flowgraph/PolyphaseResampler.h
similarity index 84%
rename from src/flowgraph/PolyphaseSincResampler.h
rename to src/flowgraph/PolyphaseResampler.h
index e64966f..e7b1e8e 100644
--- a/src/flowgraph/PolyphaseSincResampler.h
+++ b/src/flowgraph/PolyphaseResampler.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef FLOWGRAPH_POLYPHASE_SINC_RESAMPLER_H
-#define FLOWGRAPH_POLYPHASE_SINC_RESAMPLER_H
+#ifndef FLOWGRAPH_POLYPHASE_RESAMPLER_H
+#define FLOWGRAPH_POLYPHASE_RESAMPLER_H
 
 
 #include <memory>
@@ -26,11 +26,11 @@
 
 namespace flowgraph {
 
-class PolyphaseSincResampler : public MultiChannelResampler {
+class PolyphaseResampler : public MultiChannelResampler {
 public:
-    PolyphaseSincResampler(int32_t channelCount, int32_t inputRate, int32_t outputRate);
+    PolyphaseResampler(int32_t channelCount, int32_t inputRate, int32_t outputRate);
 
-    virtual ~PolyphaseSincResampler() = default;
+    virtual ~PolyphaseResampler() = default;
 
     void readFrame(float *frame) override;
 
@@ -74,4 +74,4 @@
 
 }
 
-#endif //FLOWGRAPH_POLYPHASE_SINC_RESAMPLER_H
+#endif //FLOWGRAPH_POLYPHASE_RESAMPLER_H
diff --git a/src/flowgraph/PolyphaseResamplerStereo.cpp b/src/flowgraph/PolyphaseResamplerStereo.cpp
new file mode 100644
index 0000000..a64edec
--- /dev/null
+++ b/src/flowgraph/PolyphaseResamplerStereo.cpp
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "PolyphaseResamplerStereo.h"
+
+using namespace flowgraph;
+
+#define STEREO  2
+
+PolyphaseResamplerStereo::PolyphaseResamplerStereo(
+                                       int32_t inputRate,
+                                       int32_t outputRate)
+        : PolyphaseResampler(STEREO, inputRate, outputRate) {}
+
+
+void PolyphaseResamplerStereo::writeFrame(const float *frame) {
+    float *dest = &mX[mCursor * STEREO];
+    // Write each channel twice so we avoid having to wrap when running the FIR.
+    const int offset = kNumTaps * STEREO;
+    const float left =  frame[0];
+    const float right = frame[1];
+    // Put ordered writes together.
+    dest[0] = left;
+    dest[1] = right;
+    dest[offset] = left;
+    dest[1 +  offset] = right;
+    if (++mCursor >= kNumTaps) {
+        mCursor = 0;
+    }
+}
+
+void PolyphaseResamplerStereo::readFrame(float *frame) {
+    // Clear accumulators.
+    float left = 0.0;
+    float right = 0.0;
+
+    // Multiply input times precomputed windowed sinc function.
+    const float *coefficients = &mCoefficients[mCoefficientCursor];
+    int xIndex = (mCursor + kNumTaps) * STEREO;
+    for (int i = 0; i < kNumTaps; i++) {
+        float coefficient = *coefficients++;
+        float *xFrame = &mX[xIndex];
+        left += coefficient * xFrame[0];
+        right += coefficient * xFrame[1];
+        xIndex -= STEREO;
+    }
+
+    mCoefficientCursor = (mCoefficientCursor + kNumTaps) % mCoefficients.size();
+
+    // Copy accumulators to output.
+    frame[0] = left;
+    frame[1] = right;
+}
diff --git a/src/flowgraph/PolyphaseResamplerStereo.h b/src/flowgraph/PolyphaseResamplerStereo.h
new file mode 100644
index 0000000..59452e8
--- /dev/null
+++ b/src/flowgraph/PolyphaseResamplerStereo.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FLOWGRAPH_POLYPHASE_RESAMPLER_STEREO_H
+#define FLOWGRAPH_POLYPHASE_RESAMPLER_STEREO_H
+
+#include <sys/types.h>
+#include <unistd.h>
+#include "PolyphaseResampler.h"
+
+namespace flowgraph {
+
+    class PolyphaseResamplerStereo : public PolyphaseResampler {
+    public:
+        PolyphaseResamplerStereo(int32_t inputRate, int32_t outputRate);
+
+        virtual ~PolyphaseResamplerStereo() = default;
+
+        void writeFrame(const float *frame) override;
+
+        void readFrame(float *frame) override;
+    };
+
+}
+
+#endif //FLOWGRAPH_POLYPHASE_RESAMPLER_STEREO_H
diff --git a/src/flowgraph/SampleRateConverterVariable.cpp b/src/flowgraph/SampleRateConverterVariable.cpp
deleted file mode 100644
index 4a3f103..0000000
--- a/src/flowgraph/SampleRateConverterVariable.cpp
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "SampleRateConverterVariable.h"
-
-using namespace flowgraph;
-
-SampleRateConverterVariable::SampleRateConverterVariable(int32_t channelCount, MultiChannelResampler &resampler)
-        : SampleRateConverterVariable(channelCount, resampler) {
-}
-
-int32_t SampleRateConverterVariable::onProcess(int32_t numFrames) {
-    float *outputBuffer = output.getBuffer();
-    int32_t channelCount = output.getSamplesPerFrame();
-    int framesLeft = numFrames;
-    while (framesLeft > 0) {
-        // Gather input samples as needed.
-        if(mResampler.isWriteReady()) {
-            if (!isInputAvailable()) break;
-            const float *frame = getNextInputFrame();
-            mResampler.writeFrame(frame);
-        }
-
-        // If phase >= 1.0 then we are waiting for input data.
-        if (mResampler.isReadReady()) {
-            // Output frame is interpolated from input samples based on phase.
-            mResampler.readFrame(outputBuffer);
-            outputBuffer += channelCount;
-            framesLeft--;
-        } else {
-            break;
-        }
-    }
-    return numFrames - framesLeft;
-}
diff --git a/src/flowgraph/SampleRateConverterVariable.h b/src/flowgraph/SampleRateConverterVariable.h
deleted file mode 100644
index a215ac1..0000000
--- a/src/flowgraph/SampleRateConverterVariable.h
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef OBOE_SAMPLE_RATE_CONVERTER_VARIABLE_H
-#define OBOE_SAMPLE_RATE_CONVERTER_VARIABLE_H
-
-#include <unistd.h>
-#include <sys/types.h>
-
-#include "AudioProcessorBase.h"
-#include "MultiChannelResampler.h"
-#include "LinearResampler.h"
-#include "SincResampler.h"
-
-namespace flowgraph {
-
-class SampleRateConverterVariable : public SampleRateConverterVariable {
-public:
-    explicit SampleRateConverterVariable(int32_t channelCount, MultiChannelResampler &mResampler);
-
-    double getPhaseIncrement() {
-        return mPhaseIncrement;
-    }
-
-    void setPhaseIncrement(double phaseIncrement) {
-        mPhaseIncrement = phaseIncrement;
-    }
-
-    int32_t onProcess(int32_t numFrames) override;
-
-private:
-    double  mPhase = 1.0;
-    double  mPhaseIncrement = 1.0;
-};
-
-} /* namespace flowgraph */
-
-#endif //OBOE_SAMPLE_RATE_CONVERTER_VARIABLE_H
diff --git a/src/flowgraph/SincResampler.h b/src/flowgraph/SincResampler.h
index a887a04..53357a3 100644
--- a/src/flowgraph/SincResampler.h
+++ b/src/flowgraph/SincResampler.h
@@ -52,7 +52,7 @@
     static constexpr int   kSpread = 10;
     static constexpr int   kNumTaps = kSpread * 2; // TODO should be odd, not even
 
-    std::vector<float> mWindowedSinc;
+    std::vector<float>     mWindowedSinc;
 
 private: