/*
Copyright 2018 Google Inc. All Rights Reserved.

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 "dsp/filter_coefficient_generators.h"

#include "third_party/googletest/googletest/include/gtest/gtest.h"

namespace vraudio {

namespace {

// Allowable error between filter coefficients.
const float kErrorf = 1e-5f;

// Input into the ComputeLowPassBiquadCoefficients() function.
const int kSampleRate = 44100;
const float kSpecificationFrequency = 13000.0f;
const float kAttenuation = -15.0f;

const float kMonoPoleCoefficient = 0.94863985f;

// These filter coefficients were calculated in MATLAB with a specified Q value
// of 0.17775. The attenuation at the specification frequency was then
// calculated from the MATLAB freqz() calls.
const BiquadCoefficients kIdealCoefficients(3.70224817628938f,
                                            0.555382147099001f,
                                            -1.70224817628938f,
                                            0.63884553677475f, 1.2776910735495f,
                                            0.63884553677475f);

// These filter coefficients were calculated in MATLAB for a centre_frequency of
// 8kHz and a bandwidth of 1 octave.
const BiquadCoefficients kIdealBandpassCoefficients(1.37364799922092f, -1.0f,
                                                    0.626352000779082f,
                                                    0.373647999220918f, 0.0f,
                                                    -0.373647999220918f);

const float kBandPassCenterFrequency = 8000.0f;
const int kBandPassOctaveBandwidth = 1;

// Input into the ComputeDualBandBiquadCoefficients() function.
const int kSampleRate48 = 48000;
const float kCrossoverFrequency = 380.0f;

// Pre-computed coefficients for the phase-matched filter pair (dual-band) with
// the cross-over frequency set at 380Hz and the sample rate 48kHz.
const float kLowPassA[] = {1.0f, -1.9029109f, 0.90526748f};
const float kLowPassB[] = {0.00058914319f, 0.0011782864f, 0.00058914319f};
const float kHighPassA[] = {1.0f, -1.9029109f, 0.90526748f};
const float kHighPassB[] = {0.95204461f, -1.9040892f, 0.95204461f};

TEST(FilterCoefficientGeneratorsTest, ComputeMonoPoleLowpassCoefficientTest) {
  float coefficient =
      ComputeLowPassMonoPoleCoefficient(kCrossoverFrequency, kSampleRate);
  EXPECT_NEAR(kMonoPoleCoefficient, coefficient, kErrorf);

  const float twenty_hertz = 20.0f;
  coefficient = ComputeLowPassMonoPoleCoefficient(twenty_hertz, kSampleRate);
  EXPECT_EQ(0.0f, coefficient);
  // New test for the case where a frequency below 20Hz is passed.
}

// Tests that the BiquadCoefficients generated by
// ComputeBandPassBiquadCoefficients() match those known to be correct as
// directly calculated from MATLAB.
TEST(FilterCoefficientGeneratorsTest, ComputeBandPassBiquadCoefficientsTest) {
  // Perform computation.
  BiquadCoefficients biquad_filter_coefficients =
      ComputeBandPassBiquadCoefficients(kSampleRate48, kBandPassCenterFrequency,
                                        kBandPassOctaveBandwidth);

  // Make sure they are all equal.
  for (size_t i = 0; i < biquad_filter_coefficients.a.size(); ++i) {
    EXPECT_NEAR(kIdealBandpassCoefficients.a[i],
                biquad_filter_coefficients.a[i], kErrorf);
    EXPECT_NEAR(kIdealBandpassCoefficients.b[i],
                biquad_filter_coefficients.b[i], kErrorf);
  }
}

// Tests that the BiquadCoefficients generated by
// ComputeLowPassBiquadCoefficients() match those known to be correct as
// directly calculated from MATLAB. This also tests the validity of the inherent
// 4th order best fit between attenuation at a specified frequency and Q factor.
TEST(FilterCoefficientGeneratorsTest, ComputeLowPassBiquadCoefficientsTest) {
  // Perform computation.
  BiquadCoefficients biquad_filter_coefficients =
      ComputeLowPassBiquadCoefficients(kSampleRate, kSpecificationFrequency,
                                       kAttenuation);

  // Make sure they are all equal.
  for (size_t i = 0; i < biquad_filter_coefficients.a.size(); ++i) {
    EXPECT_NEAR(kIdealCoefficients.a[i], biquad_filter_coefficients.a[i],
                kErrorf);
    EXPECT_NEAR(kIdealCoefficients.b[i], biquad_filter_coefficients.b[i],
                kErrorf);
  }
}

// Tests that the BiquadCoefficients generated by
// ComputeDualBandBiquadCoefficients() match those known to be correct as
// directly calculated from MATLAB. These coefficients are as described in:
// http://www.ai.sri.com/ajh/ambisonics/BLaH3.pdf.
TEST(DualBandBiquadCoefficientsTest, ComputeDualBandBiquadCoefficientsTest) {
  // Generate default bi-quad coefficients and constructs low- and high-pass
  // filter coefficients.
  const BiquadCoefficients kDefaultCoefficients;
  BiquadCoefficients low_pass_coefficients(kDefaultCoefficients);
  BiquadCoefficients high_pass_coefficients(kDefaultCoefficients);

  // Perform computation.
  ComputeDualBandBiquadCoefficients(kSampleRate48, kCrossoverFrequency,
                                    &low_pass_coefficients,
                                    &high_pass_coefficients);

  // Check if arrays with a and b coefficients for both filters are of the
  // same size.
  ASSERT_EQ(low_pass_coefficients.a.size(), low_pass_coefficients.b.size());
  ASSERT_EQ(high_pass_coefficients.a.size(), low_pass_coefficients.a.size());
  ASSERT_EQ(high_pass_coefficients.b.size(), low_pass_coefficients.b.size());

  // Make sure they are all equal.
  for (size_t i = 0; i < low_pass_coefficients.a.size(); ++i) {
    EXPECT_NEAR(low_pass_coefficients.a[i], kLowPassA[i], kErrorf);
    EXPECT_NEAR(low_pass_coefficients.b[i], kLowPassB[i], kErrorf);
    EXPECT_NEAR(high_pass_coefficients.a[i], kHighPassA[i], kErrorf);
    EXPECT_NEAR(high_pass_coefficients.b[i], kHighPassB[i], kErrorf);
  }
}

}  // namespace

}  // namespace vraudio
