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(