Fix multichannel integer resampling and add tests

Change-Id: I384bf8317d4f03616bf9f2b458a8700965d5cf56
diff --git a/services/audioflinger/AudioResamplerFirProcess.h b/services/audioflinger/AudioResamplerFirProcess.h
index bb0f1c9..d130013 100644
--- a/services/audioflinger/AudioResamplerFirProcess.h
+++ b/services/audioflinger/AudioResamplerFirProcess.h
@@ -109,40 +109,25 @@
     }
 };
 
-/*
- * Helper template functions for interpolating filter coefficients.
- */
-
-template<typename TC, typename T>
-void adjustLerp(T& lerpP __unused)
-{
-}
-
-template<int32_t, typename T>
-void adjustLerp(T& lerpP)
-{
-    lerpP >>= 16;   // lerpP is 32bit for NEON int32_t, but always 16 bit for non-NEON path
-}
-
 template<typename TC, typename TINTERP>
-static inline
+inline
 TC interpolate(TC coef_0, TC coef_1, TINTERP lerp)
 {
     return lerp * (coef_1 - coef_0) + coef_0;
 }
 
-template<int16_t, uint32_t>
-static inline
-int16_t interpolate(int16_t coef_0, int16_t coef_1, uint32_t lerp)
-{
-    return (static_cast<int16_t>(lerp) * ((coef_1-coef_0)<<1)>>16) + coef_0;
+template<>
+inline
+int16_t interpolate<int16_t, uint32_t>(int16_t coef_0, int16_t coef_1, uint32_t lerp)
+{   // in some CPU architectures 16b x 16b multiplies are faster.
+    return (static_cast<int16_t>(lerp) * static_cast<int16_t>(coef_1 - coef_0) >> 15) + coef_0;
 }
 
-template<int32_t, uint32_t>
-static inline
-int32_t interpolate(int32_t coef_0, int32_t coef_1, uint32_t lerp)
+template<>
+inline
+int32_t interpolate<int32_t, uint32_t>(int32_t coef_0, int32_t coef_1, uint32_t lerp)
 {
-    return mulAdd(static_cast<int16_t>(lerp), (coef_1-coef_0)<<1, coef_0);
+    return (lerp * static_cast<int64_t>(coef_1 - coef_0) >> 31) + coef_0;
 }
 
 /* class scope for passing in functions into templates */
@@ -283,7 +268,6 @@
         TINTERP lerpP,
         const TO* const volumeLR)
 {
-    adjustLerp<TC, TINTERP>(lerpP); // coefficient type adjustment for interpolations
     ProcessBase<CHANNELS, STRIDE, InterpCompute>(out, count, coefsP, coefsN, sP, sN, lerpP, volumeLR);
 }
 
diff --git a/services/audioflinger/tests/resampler_tests.cpp b/services/audioflinger/tests/resampler_tests.cpp
index 8624b62..169ce02 100644
--- a/services/audioflinger/tests/resampler_tests.cpp
+++ b/services/audioflinger/tests/resampler_tests.cpp
@@ -29,6 +29,7 @@
 #include <math.h>
 #include <vector>
 #include <utility>
+#include <iostream>
 #include <cutils/log.h>
 #include <gtest/gtest.h>
 #include <media/AudioBufferProvider.h>
@@ -153,6 +154,9 @@
     return accum / count;
 }
 
+// TI = resampler input type, int16_t or float
+// TO = resampler output type, int32_t or float
+template <typename TI, typename TO>
 void testStopbandDownconversion(size_t channels,
         unsigned inputFreq, unsigned outputFreq,
         unsigned passband, unsigned stopband,
@@ -161,20 +165,21 @@
     // create the provider
     std::vector<int> inputIncr;
     SignalProvider provider;
-    provider.setChirp<int16_t>(channels,
+    provider.setChirp<TI>(channels,
             0., inputFreq/2., inputFreq, inputFreq/2000.);
     provider.setIncr(inputIncr);
 
     // calculate the output size
     size_t outputFrames = ((int64_t) provider.getNumFrames() * outputFreq) / inputFreq;
-    size_t outputFrameSize = channels * sizeof(int32_t);
+    size_t outputFrameSize = channels * sizeof(TO);
     size_t outputSize = outputFrameSize * outputFrames;
     outputSize &= ~7;
 
     // create the resampler
     android::AudioResampler* resampler;
 
-    resampler = android::AudioResampler::create(AUDIO_FORMAT_PCM_16_BIT,
+    resampler = android::AudioResampler::create(
+            is_same<TI, int16_t>::value ? AUDIO_FORMAT_PCM_16_BIT : AUDIO_FORMAT_PCM_FLOAT,
             channels, outputFreq, quality);
     resampler->setSampleRate(inputFreq);
     resampler->setVolume(android::AudioResampler::UNITY_GAIN_FLOAT,
@@ -186,7 +191,7 @@
     void* reference = malloc(outputSize);
     resample(channels, reference, outputFrames, refIncr, &provider, resampler);
 
-    int32_t *out = reinterpret_cast<int32_t *>(reference);
+    TO *out = reinterpret_cast<TO *>(reference);
 
     // check signal energy in passband
     const unsigned passbandFrame = passband * outputFreq / 1000.;
@@ -206,10 +211,10 @@
                 provider.getNumFrames(), outputFrames,
                 passbandFrame, stopbandFrame, stopbandEnergy, passbandEnergy, dbAtten);
         for (size_t i = 0; i < 10; ++i) {
-            printf("%d\n", out[i+passbandFrame*channels]);
+            std::cout << out[i+passbandFrame*channels] << std::endl;
         }
         for (size_t i = 0; i < 10; ++i) {
-            printf("%d\n", out[i+stopbandFrame*channels]);
+            std::cout << out[i+stopbandFrame*channels] << std::endl;
         }
 #endif
     }
@@ -292,7 +297,7 @@
  * are properly suppressed.  It uses downsampling because the stopband can be
  * clearly isolated by input frequencies exceeding the output sample rate (nyquist).
  */
-TEST(audioflinger_resampler, stopbandresponse) {
+TEST(audioflinger_resampler, stopbandresponse_integer) {
     // not all of these may work (old resamplers fail on downsampling)
     static const enum android::AudioResampler::src_quality kQualityArray[] = {
             //android::AudioResampler::LOW_QUALITY,
@@ -307,13 +312,100 @@
     // in this test we assume a maximum transition band between 12kHz and 20kHz.
     // there must be at least 60dB relative attenuation between stopband and passband.
     for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) {
-        testStopbandDownconversion(2, 48000, 32000, 12000, 20000, kQualityArray[i]);
+        testStopbandDownconversion<int16_t, int32_t>(
+                2, 48000, 32000, 12000, 20000, kQualityArray[i]);
     }
 
     // in this test we assume a maximum transition band between 7kHz and 15kHz.
     // there must be at least 60dB relative attenuation between stopband and passband.
     // (the weird ratio triggers interpolative resampling)
     for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) {
-        testStopbandDownconversion(2, 48000, 22101, 7000, 15000, kQualityArray[i]);
+        testStopbandDownconversion<int16_t, int32_t>(
+                2, 48000, 22101, 7000, 15000, kQualityArray[i]);
     }
 }
+
+TEST(audioflinger_resampler, stopbandresponse_integer_multichannel) {
+    // not all of these may work (old resamplers fail on downsampling)
+    static const enum android::AudioResampler::src_quality kQualityArray[] = {
+            //android::AudioResampler::LOW_QUALITY,
+            //android::AudioResampler::MED_QUALITY,
+            //android::AudioResampler::HIGH_QUALITY,
+            //android::AudioResampler::VERY_HIGH_QUALITY,
+            android::AudioResampler::DYN_LOW_QUALITY,
+            android::AudioResampler::DYN_MED_QUALITY,
+            android::AudioResampler::DYN_HIGH_QUALITY,
+    };
+
+    // in this test we assume a maximum transition band between 12kHz and 20kHz.
+    // there must be at least 60dB relative attenuation between stopband and passband.
+    for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) {
+        testStopbandDownconversion<int16_t, int32_t>(
+                8, 48000, 32000, 12000, 20000, kQualityArray[i]);
+    }
+
+    // in this test we assume a maximum transition band between 7kHz and 15kHz.
+    // there must be at least 60dB relative attenuation between stopband and passband.
+    // (the weird ratio triggers interpolative resampling)
+    for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) {
+        testStopbandDownconversion<int16_t, int32_t>(
+                8, 48000, 22101, 7000, 15000, kQualityArray[i]);
+    }
+}
+
+TEST(audioflinger_resampler, stopbandresponse_float) {
+    // not all of these may work (old resamplers fail on downsampling)
+    static const enum android::AudioResampler::src_quality kQualityArray[] = {
+            //android::AudioResampler::LOW_QUALITY,
+            //android::AudioResampler::MED_QUALITY,
+            //android::AudioResampler::HIGH_QUALITY,
+            //android::AudioResampler::VERY_HIGH_QUALITY,
+            android::AudioResampler::DYN_LOW_QUALITY,
+            android::AudioResampler::DYN_MED_QUALITY,
+            android::AudioResampler::DYN_HIGH_QUALITY,
+    };
+
+    // in this test we assume a maximum transition band between 12kHz and 20kHz.
+    // there must be at least 60dB relative attenuation between stopband and passband.
+    for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) {
+        testStopbandDownconversion<float, float>(
+                2, 48000, 32000, 12000, 20000, kQualityArray[i]);
+    }
+
+    // in this test we assume a maximum transition band between 7kHz and 15kHz.
+    // there must be at least 60dB relative attenuation between stopband and passband.
+    // (the weird ratio triggers interpolative resampling)
+    for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) {
+        testStopbandDownconversion<float, float>(
+                2, 48000, 22101, 7000, 15000, kQualityArray[i]);
+    }
+}
+
+TEST(audioflinger_resampler, stopbandresponse_float_multichannel) {
+    // not all of these may work (old resamplers fail on downsampling)
+    static const enum android::AudioResampler::src_quality kQualityArray[] = {
+            //android::AudioResampler::LOW_QUALITY,
+            //android::AudioResampler::MED_QUALITY,
+            //android::AudioResampler::HIGH_QUALITY,
+            //android::AudioResampler::VERY_HIGH_QUALITY,
+            android::AudioResampler::DYN_LOW_QUALITY,
+            android::AudioResampler::DYN_MED_QUALITY,
+            android::AudioResampler::DYN_HIGH_QUALITY,
+    };
+
+    // in this test we assume a maximum transition band between 12kHz and 20kHz.
+    // there must be at least 60dB relative attenuation between stopband and passband.
+    for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) {
+        testStopbandDownconversion<float, float>(
+                8, 48000, 32000, 12000, 20000, kQualityArray[i]);
+    }
+
+    // in this test we assume a maximum transition band between 7kHz and 15kHz.
+    // there must be at least 60dB relative attenuation between stopband and passband.
+    // (the weird ratio triggers interpolative resampling)
+    for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) {
+        testStopbandDownconversion<float, float>(
+                8, 48000, 22101, 7000, 15000, kQualityArray[i]);
+    }
+}
+