blob: 82c7957b4a609df7ced85b60d316bcd8e842d35e [file] [log] [blame]
// Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <gtest/gtest.h>
#include <math.h>
#include "crossover.h"
#include "crossover2.h"
#include "drc.h"
#include "dsp_util.h"
#include "eq.h"
#include "eq2.h"
namespace {
/* Adds amplitude * sin(pi*freq*i + offset) to the data array. */
static void add_sine(float* data,
size_t len,
float freq,
float offset,
float amplitude) {
for (size_t i = 0; i < len; i++)
data[i] += amplitude * sinf((float)M_PI * freq * i + offset);
}
/* Calculates the magnitude at normalized frequency f. The output is
* the result of DFT, multiplied by 2/len. */
static float magnitude_at(float* data, size_t len, float f) {
double re = 0, im = 0;
f *= (float)M_PI;
for (size_t i = 0; i < len; i++) {
re += data[i] * cos(i * f);
im += data[i] * sin(i * f);
}
return sqrt(re * re + im * im) * (2.0 / len);
}
TEST(InterleaveTest, All) {
const int FRAMES = 12;
const int SAMPLES = FRAMES * 2;
/* Repeat the same data twice, so it will exercise neon/sse
* optimized functions. */
int16_t input[SAMPLES] = {
-32768, -32767, -32766, -2, -1, 0, 1, 2, 3, 32765, 32766, 32767,
-32768, -32767, -32766, -2, -1, 0, 1, 2, 3, 32765, 32766, 32767};
float answer[SAMPLES] = {-1,
-32766 / 32768.0f,
-1 / 32768.0f,
1 / 32768.0f,
3 / 32768.0f,
32766 / 32768.0f,
-1,
-32766 / 32768.0f,
-1 / 32768.0f,
1 / 32768.0f,
3 / 32768.0f,
32766 / 32768.0f,
-32767 / 32768.0f,
-2 / 32768.0f,
0,
2 / 32768.0f,
32765 / 32768.0f,
32767 / 32768.0f,
-32767 / 32768.0f,
-2 / 32768.0f,
0,
2 / 32768.0f,
32765 / 32768.0f,
32767 / 32768.0f};
float output[SAMPLES];
float* out_ptr[] = {output, output + FRAMES};
dsp_util_deinterleave((uint8_t*)input, out_ptr, 2, SND_PCM_FORMAT_S16_LE,
FRAMES);
for (int i = 0; i < SAMPLES; i++) {
EXPECT_EQ(answer[i], output[i]);
}
/* dsp_util_interleave() should round to nearest number. */
for (int i = 0; i < SAMPLES; i += 2) {
output[i] += 0.499 / 32768.0f;
output[i + 1] -= 0.499 / 32768.0f;
}
int16_t output2[SAMPLES];
dsp_util_interleave(out_ptr, (uint8_t*)output2, 2, SND_PCM_FORMAT_S16_LE,
FRAMES);
for (int i = 0; i < SAMPLES; i++) {
EXPECT_EQ(input[i], output2[i]);
}
}
TEST(EqTest, All) {
struct eq* eq;
size_t len = 44100;
float NQ = len / 2;
float f_low = 10 / NQ;
float f_mid = 100 / NQ;
float f_high = 1000 / NQ;
float* data = (float*)malloc(sizeof(float) * len);
dsp_enable_flush_denormal_to_zero();
/* low pass */
memset(data, 0, sizeof(float) * len);
add_sine(data, len, f_low, 0, 1); // 10Hz sine, magnitude = 1
EXPECT_FLOAT_EQ(1, magnitude_at(data, len, f_low));
add_sine(data, len, f_high, 0, 1); // 1000Hz sine, magnitude = 1
EXPECT_FLOAT_EQ(1, magnitude_at(data, len, f_low));
EXPECT_FLOAT_EQ(1, magnitude_at(data, len, f_high));
eq = eq_new();
EXPECT_EQ(0, eq_append_biquad(eq, BQ_LOWPASS, f_mid, 0, 0));
eq_process(eq, data, len);
EXPECT_NEAR(1, magnitude_at(data, len, f_low), 0.01);
EXPECT_NEAR(0, magnitude_at(data, len, f_high), 0.01);
/* Test for empty input */
eq_process(eq, NULL, 0);
eq_free(eq);
/* high pass */
memset(data, 0, sizeof(float) * len);
add_sine(data, len, f_low, 0, 1);
add_sine(data, len, f_high, 0, 1);
eq = eq_new();
EXPECT_EQ(0, eq_append_biquad(eq, BQ_HIGHPASS, f_mid, 0, 0));
eq_process(eq, data, len);
EXPECT_NEAR(0, magnitude_at(data, len, f_low), 0.01);
EXPECT_NEAR(1, magnitude_at(data, len, f_high), 0.01);
eq_free(eq);
/* peaking */
memset(data, 0, sizeof(float) * len);
add_sine(data, len, f_low, 0, 1);
add_sine(data, len, f_high, 0, 1);
eq = eq_new();
EXPECT_EQ(0,
eq_append_biquad(eq, BQ_PEAKING, f_high, 5, 6)); // Q=5, 6dB gain
eq_process(eq, data, len);
EXPECT_NEAR(1, magnitude_at(data, len, f_low), 0.01);
EXPECT_NEAR(2, magnitude_at(data, len, f_high), 0.01);
eq_free(eq);
free(data);
/* Too many biquads */
eq = eq_new();
for (int i = 0; i < MAX_BIQUADS_PER_EQ; i++) {
EXPECT_EQ(0, eq_append_biquad(eq, BQ_PEAKING, f_high, 5, 6));
}
EXPECT_EQ(-1, eq_append_biquad(eq, BQ_PEAKING, f_high, 5, 6));
eq_free(eq);
}
TEST(Eq2Test, All) {
struct eq2* eq2;
size_t len = 44100;
float NQ = len / 2;
float f_low = 10 / NQ;
float f_mid = 100 / NQ;
float f_high = 1000 / NQ;
float* data0 = (float*)malloc(sizeof(float) * len);
float* data1 = (float*)malloc(sizeof(float) * len);
dsp_enable_flush_denormal_to_zero();
/* a mixture of 10Hz an 1000Hz sine */
memset(data0, 0, sizeof(float) * len);
memset(data1, 0, sizeof(float) * len);
add_sine(data0, len, f_low, 0, 1); // 10Hz sine, magnitude = 1
add_sine(data0, len, f_high, 0, 1); // 1000Hz sine, magnitude = 1
add_sine(data1, len, f_low, 0, 1); // 10Hz sine, magnitude = 1
add_sine(data1, len, f_high, 0, 1); // 1000Hz sine, magnitude = 1
/* low pass at left and high pass at right */
eq2 = eq2_new();
EXPECT_EQ(0, eq2_append_biquad(eq2, 0, BQ_LOWPASS, f_mid, 0, 0));
EXPECT_EQ(0, eq2_append_biquad(eq2, 1, BQ_HIGHPASS, f_mid, 0, 0));
eq2_process(eq2, data0, data1, len);
EXPECT_NEAR(1, magnitude_at(data0, len, f_low), 0.01);
EXPECT_NEAR(0, magnitude_at(data0, len, f_high), 0.01);
EXPECT_NEAR(0, magnitude_at(data1, len, f_low), 0.01);
EXPECT_NEAR(1, magnitude_at(data1, len, f_high), 0.01);
/* Test for empty input */
eq2_process(eq2, NULL, NULL, 0);
eq2_free(eq2);
/* a mixture of 10Hz and 1000Hz sine */
memset(data0, 0, sizeof(float) * len);
memset(data1, 0, sizeof(float) * len);
add_sine(data0, len, f_low, 0, 1);
add_sine(data0, len, f_high, 0, 1);
add_sine(data1, len, f_low, 0, 1);
add_sine(data1, len, f_high, 0, 1);
/* one high-shelving biquad at left and two low-shelving biquads at right */
eq2 = eq2_new();
EXPECT_EQ(0, eq2_append_biquad(eq2, 0, BQ_HIGHSHELF, f_mid, 5, 6));
EXPECT_EQ(0, eq2_append_biquad(eq2, 1, BQ_LOWSHELF, f_mid, 0, -6));
EXPECT_EQ(0, eq2_append_biquad(eq2, 1, BQ_LOWSHELF, f_mid, 0, -6));
eq2_process(eq2, data0, data1, len);
EXPECT_NEAR(1, magnitude_at(data0, len, f_low), 0.01);
EXPECT_NEAR(2, magnitude_at(data0, len, f_high), 0.01);
EXPECT_NEAR(0.25, magnitude_at(data1, len, f_low), 0.01);
EXPECT_NEAR(1, magnitude_at(data1, len, f_high), 0.01);
eq2_free(eq2);
free(data0);
free(data1);
/* Too many biquads */
eq2 = eq2_new();
for (int i = 0; i < MAX_BIQUADS_PER_EQ2; i++) {
EXPECT_EQ(0, eq2_append_biquad(eq2, 0, BQ_PEAKING, f_high, 5, 6));
EXPECT_EQ(0, eq2_append_biquad(eq2, 1, BQ_PEAKING, f_high, 5, 6));
}
EXPECT_EQ(-1, eq2_append_biquad(eq2, 0, BQ_PEAKING, f_high, 5, 6));
EXPECT_EQ(-1, eq2_append_biquad(eq2, 1, BQ_PEAKING, f_high, 5, 6));
eq2_free(eq2);
}
TEST(CrossoverTest, All) {
struct crossover xo;
size_t len = 44100;
float NQ = len / 2;
float f0 = 62.5 / NQ;
float f1 = 250 / NQ;
float f2 = 1000 / NQ;
float f3 = 4000 / NQ;
float f4 = 16000 / NQ;
float* data = (float*)malloc(sizeof(float) * len);
float* data1 = (float*)malloc(sizeof(float) * len);
float* data2 = (float*)malloc(sizeof(float) * len);
dsp_enable_flush_denormal_to_zero();
crossover_init(&xo, f1, f3);
memset(data, 0, sizeof(float) * len);
add_sine(data, len, f0, 0, 1);
add_sine(data, len, f2, 0, 1);
add_sine(data, len, f4, 0, 1);
crossover_process(&xo, len, data, data1, data2);
// low band
EXPECT_NEAR(1, magnitude_at(data, len, f0), 0.01);
EXPECT_NEAR(0, magnitude_at(data, len, f2), 0.01);
EXPECT_NEAR(0, magnitude_at(data, len, f4), 0.01);
// mid band
EXPECT_NEAR(0, magnitude_at(data1, len, f0), 0.01);
EXPECT_NEAR(1, magnitude_at(data1, len, f2), 0.01);
EXPECT_NEAR(0, magnitude_at(data1, len, f4), 0.01);
// high band
EXPECT_NEAR(0, magnitude_at(data2, len, f0), 0.01);
EXPECT_NEAR(0, magnitude_at(data2, len, f2), 0.01);
EXPECT_NEAR(1, magnitude_at(data2, len, f4), 0.01);
/* Test for empty input */
crossover_process(&xo, 0, NULL, NULL, NULL);
free(data);
free(data1);
free(data2);
}
TEST(Crossover2Test, All) {
struct crossover2 xo2;
size_t len = 44100;
float NQ = len / 2;
float f0 = 62.5 / NQ;
float f1 = 250 / NQ;
float f2 = 1000 / NQ;
float f3 = 4000 / NQ;
float f4 = 16000 / NQ;
float* data0L = (float*)malloc(sizeof(float) * len);
float* data1L = (float*)malloc(sizeof(float) * len);
float* data2L = (float*)malloc(sizeof(float) * len);
float* data0R = (float*)malloc(sizeof(float) * len);
float* data1R = (float*)malloc(sizeof(float) * len);
float* data2R = (float*)malloc(sizeof(float) * len);
dsp_enable_flush_denormal_to_zero();
crossover2_init(&xo2, f1, f3);
memset(data0L, 0, sizeof(float) * len);
memset(data0R, 0, sizeof(float) * len);
add_sine(data0L, len, f0, 0, 1);
add_sine(data0L, len, f2, 0, 1);
add_sine(data0L, len, f4, 0, 1);
add_sine(data0R, len, f0, 0, 0.5);
add_sine(data0R, len, f2, 0, 0.5);
add_sine(data0R, len, f4, 0, 0.5);
crossover2_process(&xo2, len, data0L, data0R, data1L, data1R, data2L, data2R);
// left low band
EXPECT_NEAR(1, magnitude_at(data0L, len, f0), 0.01);
EXPECT_NEAR(0, magnitude_at(data0L, len, f2), 0.01);
EXPECT_NEAR(0, magnitude_at(data0L, len, f4), 0.01);
// left mid band
EXPECT_NEAR(0, magnitude_at(data1L, len, f0), 0.01);
EXPECT_NEAR(1, magnitude_at(data1L, len, f2), 0.01);
EXPECT_NEAR(0, magnitude_at(data1L, len, f4), 0.01);
// left high band
EXPECT_NEAR(0, magnitude_at(data2L, len, f0), 0.01);
EXPECT_NEAR(0, magnitude_at(data2L, len, f2), 0.01);
EXPECT_NEAR(1, magnitude_at(data2L, len, f4), 0.01);
// right low band
EXPECT_NEAR(0.5, magnitude_at(data0R, len, f0), 0.005);
EXPECT_NEAR(0, magnitude_at(data0R, len, f2), 0.005);
EXPECT_NEAR(0, magnitude_at(data0R, len, f4), 0.005);
// right mid band
EXPECT_NEAR(0, magnitude_at(data1R, len, f0), 0.005);
EXPECT_NEAR(0.5, magnitude_at(data1R, len, f2), 0.005);
EXPECT_NEAR(0, magnitude_at(data1R, len, f4), 0.005);
// right high band
EXPECT_NEAR(0, magnitude_at(data2R, len, f0), 0.005);
EXPECT_NEAR(0, magnitude_at(data2R, len, f2), 0.005);
EXPECT_NEAR(0.5, magnitude_at(data2R, len, f4), 0.005);
/* Test for empty input */
crossover2_process(&xo2, 0, NULL, NULL, NULL, NULL, NULL, NULL);
free(data0L);
free(data1L);
free(data2L);
free(data0R);
free(data1R);
free(data2R);
}
TEST(DrcTest, All) {
size_t len = 44100;
float NQ = len / 2;
float f0 = 62.5 / NQ;
float f1 = 250 / NQ;
float f2 = 1000 / NQ;
float f3 = 4000 / NQ;
float f4 = 16000 / NQ;
float* data_left = (float*)malloc(sizeof(float) * len);
float* data_right = (float*)malloc(sizeof(float) * len);
float* data[] = {data_left, data_right};
float* data_empty[] = {NULL, NULL};
struct drc* drc;
dsp_enable_flush_denormal_to_zero();
drc = drc_new(44100);
drc_set_param(drc, 0, PARAM_CROSSOVER_LOWER_FREQ, 0);
drc_set_param(drc, 0, PARAM_ENABLED, 1);
drc_set_param(drc, 0, PARAM_THRESHOLD, -30);
drc_set_param(drc, 0, PARAM_KNEE, 0);
drc_set_param(drc, 0, PARAM_RATIO, 3);
drc_set_param(drc, 0, PARAM_ATTACK, 0.02);
drc_set_param(drc, 0, PARAM_RELEASE, 0.2);
drc_set_param(drc, 0, PARAM_POST_GAIN, 0);
drc_set_param(drc, 1, PARAM_CROSSOVER_LOWER_FREQ, f1);
drc_set_param(drc, 1, PARAM_ENABLED, 0);
drc_set_param(drc, 1, PARAM_THRESHOLD, -30);
drc_set_param(drc, 1, PARAM_KNEE, 0);
drc_set_param(drc, 1, PARAM_RATIO, 3);
drc_set_param(drc, 1, PARAM_ATTACK, 0.02);
drc_set_param(drc, 1, PARAM_RELEASE, 0.2);
drc_set_param(drc, 1, PARAM_POST_GAIN, 0);
drc_set_param(drc, 2, PARAM_CROSSOVER_LOWER_FREQ, f3);
drc_set_param(drc, 2, PARAM_ENABLED, 1);
drc_set_param(drc, 2, PARAM_THRESHOLD, -30);
drc_set_param(drc, 2, PARAM_KNEE, 0);
drc_set_param(drc, 2, PARAM_RATIO, 1);
drc_set_param(drc, 2, PARAM_ATTACK, 0.02);
drc_set_param(drc, 2, PARAM_RELEASE, 0.2);
drc_set_param(drc, 2, PARAM_POST_GAIN, 20);
drc_init(drc);
memset(data_left, 0, sizeof(float) * len);
memset(data_right, 0, sizeof(float) * len);
add_sine(data_left, len, f0, 0, 1);
add_sine(data_left, len, f2, 0, 1);
add_sine(data_left, len, f4, 0, 1);
add_sine(data_right, len, f0, 0, 1);
add_sine(data_right, len, f2, 0, 1);
add_sine(data_right, len, f4, 0, 1);
for (size_t start = 0; start < len; start += DRC_PROCESS_MAX_FRAMES) {
int chunk = std::min(len - start, (size_t)DRC_PROCESS_MAX_FRAMES);
drc_process(drc, data, chunk);
data[0] += chunk;
data[1] += chunk;
}
/* This is -8dB because there is a 12dB makeup (20dB^0.6) inside the DRC */
EXPECT_NEAR(0.4, magnitude_at(data_right, len, f0), 0.1);
/* This is 0dB because the DRC is disabled */
EXPECT_NEAR(1, magnitude_at(data_right, len, f2), 0.1);
/* This is 20dB because of the post gain */
EXPECT_NEAR(10, magnitude_at(data_right, len, f4), 1);
/* Test for empty input */
drc_process(drc, data_empty, 0);
drc_free(drc);
free(data_left);
free(data_right);
}
} // namespace
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}