| // 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(); |
| } |