BiquadFilter: Add ConstOptions template parameter.

Add ConstOptions template parameter to pass compile time options
to the BiquadFilter class.  This allows selection of
BiquadDirect2Transpose, BiquadStateSpace, or a user defined
Biquad filter kernel.

Test: atest biquad_filter_tests
Bug: 200297980
Change-Id: Id317f1c9400b5fe123f1d03d219ecdb95ace0981
diff --git a/audio_utils/include/audio_utils/BiquadFilter.h b/audio_utils/include/audio_utils/BiquadFilter.h
index 84dafd3..ca86ada 100644
--- a/audio_utils/include/audio_utils/BiquadFilter.h
+++ b/audio_utils/include/audio_utils/BiquadFilter.h
@@ -431,7 +431,7 @@
     }
 }
 
-template <template <typename, typename> typename FilterType,
+template <typename ConstOptions,
         size_t OCCUPANCY, bool SAME_COEF_PER_CHANNEL, typename T, typename F>
 void biquad_filter_func_impl(F *out, const F *in, size_t frames, size_t stride,
         size_t channelCount, F *delays, const F *coefs, size_t localStride) {
@@ -440,12 +440,13 @@
     constexpr size_t elements = sizeof(T) / sizeof(F); // how many float elements in T.
     const size_t coefStride = SAME_COEF_PER_CHANNEL ? 1 : localStride;
     using CoefType = std::conditional_t<SAME_COEF_PER_CHANNEL, F, T>;
+    using KernelType = typename ConstOptions::template FilterType<T, CoefType>;
 
     for (size_t i = 0; i < channelCount; i += elements) {
         T s1 = vld1<T>(&delays[0]);
         T s2 = vld1<T>(&delays[localStride]);
 
-        FilterType<T, CoefType> kernel(
+        KernelType kernel(
                 vld1<CoefType>(coefs), vld1<CoefType>(coefs + coefStride),
                 vld1<CoefType>(coefs + coefStride * 2), vld1<CoefType>(coefs + coefStride * 3),
                 vld1<CoefType>(coefs + coefStride * 4),
@@ -473,24 +474,37 @@
     FILTER_OPTION_SCALAR_ONLY = (1 << 0),
 };
 
-// Default biquad type.
-template <typename T, typename F>
-using BiquadFilterType = BiquadStateSpace<T, F>;
 
-#define BIQUAD_FILTER_CASE(N, FilterType, ... /* type */) \
+/**
+ * DefaultBiquadConstOptions holds the default set of options for customizing
+ * the Biquad filter at compile time.
+ *
+ * Consider inheriting from this structure and overriding the options
+ * desired; this is backward compatible to new options added in the future.
+ */
+struct DefaultBiquadConstOptions {
+
+    // Sets the Biquad filter type.
+    // Can be one of the already defined BiquadDirect2Transpose or BiquadStateSpace
+    // filter kernels; also can be a user defined filter kernel as well.
+    template <typename T, typename F>
+    using FilterType = BiquadStateSpace<T, F>;
+};
+
+#define BIQUAD_FILTER_CASE(N, ... /* type */) \
             case N: { \
                 using VectorType = __VA_ARGS__; \
-                biquad_filter_func_impl< \
-                        FilterType, \
+                biquad_filter_func_impl<ConstOptions, \
                         nearestOccupancy(OCCUPANCY, \
-                                FilterType<VectorType, D>::required_occupancies_), \
+                                ConstOptions::template FilterType<VectorType, D> \
+                                            ::required_occupancies_), \
                         SAME_COEF_PER_CHANNEL, VectorType>( \
                         out + offset, in + offset, frames, stride, remaining, \
                         delays + offset, c, localStride); \
                 goto exit; \
             }
 
-template <size_t OCCUPANCY, bool SAME_COEF_PER_CHANNEL, typename D>
+template <typename ConstOptions, size_t OCCUPANCY, bool SAME_COEF_PER_CHANNEL, typename D>
 void biquad_filter_func(D *out, const D *in, size_t frames, size_t stride,
         size_t channelCount, D *delays, const D *coefs, size_t localStride,
         FILTER_OPTION filterOptions) {
@@ -525,10 +539,10 @@
             default:
                 if (remaining >= 16) {
                     remaining &= ~15;
-                    biquad_filter_func_impl<
-                            BiquadFilterType,
+                    biquad_filter_func_impl<ConstOptions,
                             nearestOccupancy(OCCUPANCY,
-                                    BiquadFilterType<D, D>::required_occupancies_),
+                                    ConstOptions::template FilterType<D, D>
+                                                ::required_occupancies_),
                             SAME_COEF_PER_CHANNEL, alt_16_t>(
                             out + offset, in + offset, frames, stride, remaining,
                             delays + offset, c, localStride);
@@ -536,20 +550,20 @@
                     continue;
                 }
                 break;  // case 1 handled at bottom.
-            BIQUAD_FILTER_CASE(15, BiquadFilterType, intrinsics::internal_array_t<float, 15>)
-            BIQUAD_FILTER_CASE(14, BiquadFilterType, intrinsics::internal_array_t<float, 14>)
-            BIQUAD_FILTER_CASE(13, BiquadFilterType, intrinsics::internal_array_t<float, 13>)
-            BIQUAD_FILTER_CASE(12, BiquadFilterType, intrinsics::internal_array_t<float, 12>)
-            BIQUAD_FILTER_CASE(11, BiquadFilterType, intrinsics::internal_array_t<float, 11>)
-            BIQUAD_FILTER_CASE(10, BiquadFilterType, intrinsics::internal_array_t<float, 10>)
-            BIQUAD_FILTER_CASE(9, BiquadFilterType, intrinsics::internal_array_t<float, 9>)
-            BIQUAD_FILTER_CASE(8, BiquadFilterType, alt_8_t)
-            BIQUAD_FILTER_CASE(7, BiquadFilterType, intrinsics::internal_array_t<float, 7>)
-            BIQUAD_FILTER_CASE(6, BiquadFilterType, intrinsics::internal_array_t<float, 6>)
-            BIQUAD_FILTER_CASE(5, BiquadFilterType, intrinsics::internal_array_t<float, 5>)
-            BIQUAD_FILTER_CASE(4, BiquadFilterType, alt_4_t)
-            BIQUAD_FILTER_CASE(3, BiquadFilterType, intrinsics::internal_array_t<float, 3>)
-            BIQUAD_FILTER_CASE(2, BiquadFilterType, intrinsics::internal_array_t<float, 2>)
+            BIQUAD_FILTER_CASE(15, intrinsics::internal_array_t<float, 15>)
+            BIQUAD_FILTER_CASE(14, intrinsics::internal_array_t<float, 14>)
+            BIQUAD_FILTER_CASE(13, intrinsics::internal_array_t<float, 13>)
+            BIQUAD_FILTER_CASE(12, intrinsics::internal_array_t<float, 12>)
+            BIQUAD_FILTER_CASE(11, intrinsics::internal_array_t<float, 11>)
+            BIQUAD_FILTER_CASE(10, intrinsics::internal_array_t<float, 10>)
+            BIQUAD_FILTER_CASE(9, intrinsics::internal_array_t<float, 9>)
+            BIQUAD_FILTER_CASE(8, alt_8_t)
+            BIQUAD_FILTER_CASE(7, intrinsics::internal_array_t<float, 7>)
+            BIQUAD_FILTER_CASE(6, intrinsics::internal_array_t<float, 6>)
+            BIQUAD_FILTER_CASE(5, intrinsics::internal_array_t<float, 5>)
+            BIQUAD_FILTER_CASE(4, alt_4_t)
+            BIQUAD_FILTER_CASE(3, intrinsics::internal_array_t<float, 3>)
+            BIQUAD_FILTER_CASE(2, intrinsics::internal_array_t<float, 2>)
             // BIQUAD_FILTER_CASE(1, BiquadFilterType, intrinsics::internal_array_t<float, 1>)
             }
         } else if constexpr (std::is_same_v<D, double>) {
@@ -558,9 +572,10 @@
             default:
                 if (remaining >= 8) {
                     remaining &= ~7;
-                    biquad_filter_func_impl<BiquadFilterType,
+                    biquad_filter_func_impl<ConstOptions,
                             nearestOccupancy(OCCUPANCY,
-                                    BiquadFilterType<D, D>::required_occupancies_),
+                                     ConstOptions::template FilterType<D, D>
+                                                 ::required_occupancies_),
                             SAME_COEF_PER_CHANNEL,
                             intrinsics::internal_array_t<double, 8>>(
                             out + offset, in + offset, frames, stride, remaining,
@@ -569,12 +584,12 @@
                     continue;
                 }
                 break; // case 1 handled at bottom.
-            BIQUAD_FILTER_CASE(7, BiquadFilterType, intrinsics::internal_array_t<double, 7>)
-            BIQUAD_FILTER_CASE(6, BiquadFilterType, intrinsics::internal_array_t<double, 6>)
-            BIQUAD_FILTER_CASE(5, BiquadFilterType, intrinsics::internal_array_t<double, 5>)
-            BIQUAD_FILTER_CASE(4, BiquadFilterType, intrinsics::internal_array_t<double, 4>)
-            BIQUAD_FILTER_CASE(3, BiquadFilterType, intrinsics::internal_array_t<double, 3>)
-            BIQUAD_FILTER_CASE(2, BiquadFilterType, intrinsics::internal_array_t<double, 2>)
+            BIQUAD_FILTER_CASE(7, intrinsics::internal_array_t<double, 7>)
+            BIQUAD_FILTER_CASE(6, intrinsics::internal_array_t<double, 6>)
+            BIQUAD_FILTER_CASE(5, intrinsics::internal_array_t<double, 5>)
+            BIQUAD_FILTER_CASE(4, intrinsics::internal_array_t<double, 4>)
+            BIQUAD_FILTER_CASE(3, intrinsics::internal_array_t<double, 3>)
+            BIQUAD_FILTER_CASE(2, intrinsics::internal_array_t<double, 2>)
             };
 #endif
         }
@@ -582,9 +597,9 @@
         // Essentially the code below is scalar, the same as
         // biquad_filter_1fast<OCCUPANCY, SAME_COEF_PER_CHANNEL>,
         // but formulated with NEON intrinsic-like call pattern.
-        biquad_filter_func_impl<BiquadFilterType,
+        biquad_filter_func_impl<ConstOptions,
                 nearestOccupancy(OCCUPANCY,
-                        BiquadFilterType<D, D>::required_occupancies_),
+                        ConstOptions::template FilterType<D, D>::required_occupancies_),
                  SAME_COEF_PER_CHANNEL, D>(
                 out + offset, in + offset, frames, stride, remaining,
                 delays + offset, c, localStride);
@@ -651,7 +666,10 @@
  *         are shared between channels, or false if the Biquad coefficients
  *         may differ between channels. The default is true.
  */
-template <typename D = float, bool SAME_COEF_PER_CHANNEL = true>
+
+template <typename D = float,
+        bool SAME_COEF_PER_CHANNEL = true,
+        typename ConstOptions = details::DefaultBiquadConstOptions>
 class BiquadFilter {
 public:
     template <typename T = std::array<D, kBiquadNumCoefs>>
@@ -1020,8 +1038,9 @@
                 details::FILTER_OPTION filterOptions) {
             constexpr size_t NEAREST_OCCUPANCY =
                 details::nearestOccupancy(
-                        OCCUPANCY, details::BiquadFilterType<D, D>::required_occupancies_);
-            details::biquad_filter_func<NEAREST_OCCUPANCY, SC>(
+                        OCCUPANCY, ConstOptions::template FilterType<D, D>
+                                               ::required_occupancies_);
+            details::biquad_filter_func<ConstOptions, NEAREST_OCCUPANCY, SC>(
                     out, in, frames, stride, channelCount, delays, coef, localStride,
                     filterOptions);
         }
diff --git a/audio_utils/tests/biquad_filter_tests.cpp b/audio_utils/tests/biquad_filter_tests.cpp
index f90b863..660e68d 100644
--- a/audio_utils/tests/biquad_filter_tests.cpp
+++ b/audio_utils/tests/biquad_filter_tests.cpp
@@ -105,7 +105,7 @@
 // The BiquadFilterTest is parameterized on channel count.
 class BiquadFilterTest : public ::testing::TestWithParam<size_t> {
 protected:
-    template <typename T>
+    template <typename ConstOptions, typename T>
     static void testProcess(size_t zeroChannels = 0) {
         const size_t channelCount = static_cast<size_t>(GetParam());
         const size_t stride = channelCount + zeroChannels;
@@ -118,7 +118,8 @@
             populateBuffer(
                     OUTPUT[i], FRAME_COUNT, channelCount, zeroChannels, expectedOutputBuffer[i]);
         }
-        BiquadFilter<T> filter(channelCount, COEFS);
+        BiquadFilter<T, true /* SAME_COEF_PER_CHANNEL */, ConstOptions>
+            filter(channelCount, COEFS);
 
         for (size_t i = 0; i < PERIOD; ++i) {
             filter.process(outputBuffer, inputBuffer[i], FRAME_COUNT, stride);
@@ -136,20 +137,38 @@
     }
 };
 
-TEST_P(BiquadFilterTest, ConstructAndProcessFilterFloat) {
-    testProcess<float>();
+struct StateSpaceOptions {
+    template <typename T, typename F>
+    using FilterType = BiquadStateSpace<T, F>;
+};
+
+struct Direct2TransposeOptions {
+    template <typename T, typename F>
+    using FilterType = BiquadDirect2Transpose<T, F>;
+};
+
+TEST_P(BiquadFilterTest, ConstructAndProcessSSFilterFloat) {
+    testProcess<StateSpaceOptions, float>();
 }
 
-TEST_P(BiquadFilterTest, ConstructAndProcessFilterDouble) {
-    testProcess<double>();
+TEST_P(BiquadFilterTest, ConstructAndProcessSSFilterDouble) {
+    testProcess<StateSpaceOptions, double>();
 }
 
-TEST_P(BiquadFilterTest, ConstructAndProcessFilterFloatZero3) {
-    testProcess<float>(3 /* zeroChannels */);
+TEST_P(BiquadFilterTest, ConstructAndProcessSSFilterFloatZero3) {
+    testProcess<StateSpaceOptions, float>(3 /* zeroChannels */);
 }
 
-TEST_P(BiquadFilterTest, ConstructAndProcessFilterDoubleZero5) {
-    testProcess<double>(5 /* zeroChannels */);
+TEST_P(BiquadFilterTest, ConstructAndProcessSSFilterDoubleZero5) {
+    testProcess<StateSpaceOptions, double>(5 /* zeroChannels */);
+}
+
+TEST_P(BiquadFilterTest, ConstructAndProcessDT2FilterFloat) {
+    testProcess<Direct2TransposeOptions, float>();
+}
+
+TEST_P(BiquadFilterTest, ConstructAndProcessDT2FilterDouble) {
+    testProcess<Direct2TransposeOptions, double>();
 }
 
 INSTANTIATE_TEST_CASE_P(