1 /***
2 This file is part of PulseAudio.
3
4 PulseAudio is free software; you can redistribute it and/or modify
5 it under the terms of the GNU Lesser General Public License as published
6 by the Free Software Foundation; either version 2.1 of the License,
7 or (at your option) any later version.
8
9 PulseAudio is distributed in the hope that it will be useful, but
10 WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 General Public License for more details.
13
14 You should have received a copy of the GNU Lesser General Public License
15 along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
16 ***/
17
18 #ifdef HAVE_CONFIG_H
19 #include <config.h>
20 #endif
21
22 #include <check.h>
23
24 #include <pulse/pulseaudio.h>
25 #include <pulse/sample.h>
26 #include <pulsecore/memblock.h>
27
28 #include <pulsecore/filter/lfe-filter.h>
29
30 struct lfe_filter_test {
31 pa_lfe_filter_t *lf;
32 pa_mempool *pool;
33 pa_sample_spec *ss;
34 };
35
36 static uint8_t *ori_sample_ptr;
37
38 #define ONE_BLOCK_SAMPLES 4096
39 #define TOTAL_SAMPLES 8192
40 #define TOLERANT_VARIATION 1
41
save_data_block(struct lfe_filter_test * lft,void * d,pa_memblock * blk)42 static void save_data_block(struct lfe_filter_test *lft, void *d, pa_memblock *blk) {
43 uint8_t *dst = d, *src;
44 size_t blk_size = pa_frame_size(lft->ss) * ONE_BLOCK_SAMPLES;
45
46 src = pa_memblock_acquire(blk);
47 memcpy(dst, src, blk_size);
48 pa_memblock_release(blk);
49 }
50
generate_data_block(struct lfe_filter_test * lft,int start)51 static pa_memblock* generate_data_block(struct lfe_filter_test *lft, int start) {
52 pa_memblock *r;
53 uint8_t *d, *s = ori_sample_ptr;
54 size_t blk_size = pa_frame_size(lft->ss) * ONE_BLOCK_SAMPLES;
55
56 pa_assert_se(r = pa_memblock_new(lft->pool, blk_size));
57 d = pa_memblock_acquire(r);
58 memcpy(d, s + start, blk_size);
59 pa_memblock_release(r);
60
61 return r;
62 }
63
compare_data_block(struct lfe_filter_test * lft,void * a,void * b)64 static int compare_data_block(struct lfe_filter_test *lft, void *a, void *b) {
65 int ret = 0;
66 uint32_t i;
67 uint16_t *r = a, *u = b;
68
69 pa_assert(lft->ss->format == PA_SAMPLE_S16NE);
70
71 for (i = 0; i < ONE_BLOCK_SAMPLES; i++) {
72 if (abs(*r++ - *u++) > TOLERANT_VARIATION) {
73 pa_log_error("lfe-filter-test: test failed, the output data in the position 0x%x of a block does not equal!", i);
74 ret = -1;
75 break;
76 }
77 }
78 return ret;
79 }
80
81 /* in this test case, we pass two blocks of sample data to lfe-filter, each
82 block contains 4096 samples, and don't let rewind_samples exceed TOTAL_SAMPLES */
lfe_filter_rewind_test(struct lfe_filter_test * lft,int rewind_samples)83 static int lfe_filter_rewind_test(struct lfe_filter_test *lft, int rewind_samples)
84 {
85 int ret = -1, pos, i;
86 pa_memchunk mc;
87 uint8_t *outptr;
88 uint32_t fz = pa_frame_size(lft->ss);
89
90 if (rewind_samples > TOTAL_SAMPLES || rewind_samples < TOTAL_SAMPLES - ONE_BLOCK_SAMPLES) {
91 pa_log_error("lfe-filter-test: Please keep %d samples < rewind_samples < %d samples", TOTAL_SAMPLES - ONE_BLOCK_SAMPLES, TOTAL_SAMPLES);
92 return ret;
93 }
94
95 outptr = pa_xmalloc(fz * TOTAL_SAMPLES);
96
97 /* let lfe-filter process all samples first, and save the processed data to the temp buffer,
98 then rewind back to some position, reprocess some samples and compare the output data with
99 the processed data saved before. */
100 for (i = 0; i < TOTAL_SAMPLES / ONE_BLOCK_SAMPLES; i++) {
101 mc.memblock = generate_data_block(lft, i * ONE_BLOCK_SAMPLES * fz);
102 mc.length = pa_memblock_get_length(mc.memblock);
103 mc.index = 0;
104 pa_lfe_filter_process(lft->lf, &mc);
105 save_data_block(lft, outptr + i * ONE_BLOCK_SAMPLES * fz, mc.memblock);
106 pa_memblock_unref(mc.memblock);
107 }
108
109 pa_lfe_filter_rewind(lft->lf, rewind_samples * fz);
110 pos = (TOTAL_SAMPLES - rewind_samples) * fz;
111 mc.memblock = generate_data_block(lft, pos);
112 mc.length = pa_memblock_get_length(mc.memblock);
113 mc.index = 0;
114 pa_lfe_filter_process(lft->lf, &mc);
115 ret = compare_data_block(lft, outptr + pos, pa_memblock_acquire(mc.memblock));
116 pa_memblock_release(mc.memblock);
117 pa_memblock_unref(mc.memblock);
118
119 pa_xfree(outptr);
120
121 return ret;
122 }
123
START_TEST(lfe_filter_test)124 START_TEST (lfe_filter_test) {
125 pa_sample_spec a;
126 int ret = -1;
127 unsigned i, crossover_freq = 120;
128 pa_channel_map chmapmono = {1, {PA_CHANNEL_POSITION_LFE}};
129 struct lfe_filter_test lft;
130 short *tmp_ptr;
131
132 pa_log_set_level(PA_LOG_DEBUG);
133
134 a.channels = 1;
135 a.rate = 44100;
136 a.format = PA_SAMPLE_S16NE;
137
138 lft.ss = &a;
139 pa_assert_se(lft.pool = pa_mempool_new(PA_MEM_TYPE_PRIVATE, 0, true));
140
141 /* We prepare pseudo-random input audio samples for lfe-filter rewind testing*/
142 ori_sample_ptr = pa_xmalloc(pa_frame_size(lft.ss) * TOTAL_SAMPLES);
143 tmp_ptr = (short *) ori_sample_ptr;
144 for (i = 0; i < pa_frame_size(lft.ss) * TOTAL_SAMPLES / sizeof(short); i++)
145 *tmp_ptr++ = random();
146
147 /* we create a lfe-filter with cutoff frequency 120Hz and max rewind time 10 seconds */
148 pa_assert_se(lft.lf = pa_lfe_filter_new(&a, &chmapmono, crossover_freq, a.rate * 10));
149 /* rewind to a block boundary */
150 ret = lfe_filter_rewind_test(&lft, ONE_BLOCK_SAMPLES);
151 if (ret)
152 pa_log_error("lfe-filer-test: rewind to block boundary test failed!!!");
153 pa_lfe_filter_free(lft.lf);
154
155 /* we create a lfe-filter with cutoff frequency 120Hz and max rewind time 10 seconds */
156 pa_assert_se(lft.lf = pa_lfe_filter_new(&a, &chmapmono, crossover_freq, a.rate * 10));
157 /* rewind to the middle position of a block */
158 ret = lfe_filter_rewind_test(&lft, ONE_BLOCK_SAMPLES + ONE_BLOCK_SAMPLES / 2);
159 if (ret)
160 pa_log_error("lfe-filer-test: rewind to middle of block test failed!!!");
161
162 pa_xfree(ori_sample_ptr);
163
164 pa_lfe_filter_free(lft.lf);
165
166 pa_mempool_unref(lft.pool);
167
168 if (!ret)
169 pa_log_debug("lfe-filter-test: tests for both rewind to block boundary and rewind to middle position of a block passed!");
170
171 fail_unless(ret == 0);
172 }
173 END_TEST
174
main(int argc,char * argv[])175 int main(int argc, char *argv[]) {
176 int failed = 0;
177 Suite *s;
178 TCase *tc;
179 SRunner *sr;
180
181 if (!getenv("MAKE_CHECK"))
182 pa_log_set_level(PA_LOG_DEBUG);
183
184 s = suite_create("lfe-filter");
185 tc = tcase_create("lfe-filter");
186 tcase_add_test(tc, lfe_filter_test);
187 tcase_set_timeout(tc, 10);
188 suite_add_tcase(s, tc);
189
190 sr = srunner_create(s);
191 srunner_run_all(sr, CK_NORMAL);
192 failed = srunner_ntests_failed(sr);
193 srunner_free(sr);
194
195 return (failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
196 }
197