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 <stdio.h>
23 #include <getopt.h>
24 #include <locale.h>
25 #include <math.h>
26
27 #include <pulse/pulseaudio.h>
28
29 #include <pulse/rtclock.h>
30 #include <pulse/sample.h>
31 #include <pulse/volume.h>
32
33 #include <pulsecore/i18n.h>
34 #include <pulsecore/log.h>
35 #include <pulsecore/resampler.h>
36 #include <pulsecore/macro.h>
37 #include <pulsecore/endianmacros.h>
38 #include <pulsecore/memblock.h>
39 #include <pulsecore/memblockq.h>
40 #include <pulsecore/sample-util.h>
41 #include <pulsecore/core-util.h>
42
43 #define MEMBLOCKQ_MAXLENGTH (16*1024*1024)
44 #define PA_SILENCE_MAX (pa_page_size()*16)
45 #define MAX_MATCHING_PERIOD 500
46
silence_memblock_new(pa_mempool * pool,uint8_t c)47 static pa_memblock *silence_memblock_new(pa_mempool *pool, uint8_t c) {
48 pa_memblock *b;
49 size_t length;
50 void *data;
51
52 pa_assert(pool);
53
54 length = PA_MIN(pa_mempool_block_size_max(pool), PA_SILENCE_MAX);
55
56 b = pa_memblock_new(pool, length);
57
58 data = pa_memblock_acquire(b);
59 memset(data, c, length);
60 pa_memblock_release(b);
61
62 pa_memblock_set_is_silence(b, true);
63
64 return b;
65 }
66
67 /* Calculate number of history bytes needed for the rewind */
calculate_resampler_history_bytes(pa_resampler * r,size_t in_rewind_frames)68 static size_t calculate_resampler_history_bytes(pa_resampler *r, size_t in_rewind_frames) {
69 size_t history_frames, history_max, matching_period, total_frames, remainder;
70 double delay;
71
72 if (!r)
73 return 0;
74
75 /* Initialize some variables, cut off full seconds from the rewind */
76 total_frames = 0;
77 in_rewind_frames = in_rewind_frames % r->i_ss.rate;
78 history_max = (uint64_t) PA_RESAMPLER_MAX_DELAY_USEC * r->i_ss.rate * 3 / PA_USEC_PER_SEC / 2;
79
80 /* Get the current internal delay of the resampler */
81 delay = pa_resampler_get_delay(r, false);
82
83 /* Calculate the matchiung period */
84 matching_period = r->i_ss.rate / pa_resampler_get_gcd(r);
85 pa_log_debug("Integral period length is %lu input frames", matching_period);
86
87 /* If the delay is larger than the length of the history queue, we can only
88 * replay as much as we have. */
89 if ((size_t)delay >= history_max) {
90 history_frames = history_max;
91 return history_frames * r->i_fz;
92 }
93
94 /* Initially set the history to 3 times the resampler delay. Use at least 2 ms. */
95 history_frames = (size_t)(delay * 3.0);
96 history_frames = PA_MAX(history_frames, r->i_ss.rate / 500);
97
98 /* Check how the rewind fits into multiples of the matching period. */
99 remainder = (in_rewind_frames + history_frames) % matching_period;
100
101 /* If possible, use between 2 and 3 times the resampler delay */
102 if (remainder < (size_t)delay && history_frames - remainder <= history_max)
103 total_frames = in_rewind_frames + history_frames - remainder;
104
105 /* Else, try above 3 times the delay */
106 else if (history_frames + matching_period - remainder <= history_max)
107 total_frames = in_rewind_frames + history_frames + matching_period - remainder;
108
109 if (total_frames != 0)
110 /* We found a perfect match. */
111 history_frames = total_frames - in_rewind_frames;
112 else {
113 /* Try to use 2.5 times the delay. */
114 history_frames = PA_MIN((size_t)(delay * 2.5), history_max);
115 pa_log_debug("No usable integral matching period");
116 }
117
118 return history_frames * r->i_fz;
119 }
120
compare_blocks(const pa_sample_spec * ss,const pa_memchunk * chunk_a,const pa_memchunk * chunk_b)121 static float compare_blocks(const pa_sample_spec *ss, const pa_memchunk *chunk_a, const pa_memchunk *chunk_b) {
122 float *a, *b, max_diff = 0;
123 unsigned i;
124
125 a = pa_memblock_acquire(chunk_a->memblock);
126 b = pa_memblock_acquire(chunk_b->memblock);
127 a += chunk_a->index / pa_frame_size(ss);
128 b += chunk_b->index / pa_frame_size(ss);
129
130 for (i = 0; i < chunk_a->length / pa_frame_size(ss); i++) {
131 if (fabs(a[i] - b[i]) > max_diff)
132 max_diff = fabs(a[i] - b[i]);
133 }
134
135 pa_memblock_release(chunk_a->memblock);
136 pa_memblock_release(chunk_b->memblock);
137
138 return max_diff;
139 }
140
generate_block(pa_mempool * pool,const pa_sample_spec * ss,unsigned frequency,double amplitude,size_t nr_of_samples)141 static pa_memblock* generate_block(pa_mempool *pool, const pa_sample_spec *ss, unsigned frequency, double amplitude, size_t nr_of_samples) {
142 pa_memblock *r;
143 float *d;
144 float val;
145 unsigned i;
146 int n;
147 float t, dt, dt_period;
148
149 pa_assert(frequency);
150 pa_assert(nr_of_samples);
151 pa_assert(ss->channels == 1);
152 pa_assert(ss->format == PA_SAMPLE_FLOAT32NE);
153
154 pa_assert_se(r = pa_memblock_new(pool, pa_frame_size(ss) * nr_of_samples));
155 d = pa_memblock_acquire(r);
156
157 /* Generate square wave with given length, frequency and sample rate. */
158 val = amplitude;
159 t = 0;
160 n = 1;
161 dt = 1.0 / ss->rate;
162 dt_period = 1.0 / frequency;
163 for (i=0; i < nr_of_samples; i++) {
164 d[i] = val;
165
166 if ((int)(2 * t / dt_period) >= n) {
167 n++;
168 if (val >= amplitude)
169 val = - amplitude;
170 else
171 val = amplitude;
172 }
173
174 t += dt;
175 }
176
177 pa_memblock_release(r);
178
179 return r;
180 }
181
help(const char * argv0)182 static void help(const char *argv0) {
183 printf("%s [options]\n\n"
184 "-h, --help Show this help\n"
185 "-v, --verbose Print debug messages\n"
186 " --from-rate=SAMPLERATE From sample rate in Hz (defaults to 44100)\n"
187 " --to-rate=SAMPLERATE To sample rate in Hz (defaults to 44100)\n"
188 " --resample-method=METHOD Resample method (defaults to auto)\n"
189 " --frequency=unsigned Frequency of square wave\n"
190 " --samples=unsigned Number of samples for square wave\n"
191 " --rewind=unsigned Number of output samples to rewind\n"
192 "\n"
193 "This test generates samples for a square wave of given frequency, number of samples\n"
194 "and input sample rate. Then this input data is resampled to the output rate, rewound\n"
195 "by rewind samples and the rewound part is processed again. Then output is compared to\n"
196 "the result of the first pass.\n"
197 "\n"
198 "See --dump-resample-methods for possible values of resample methods.\n",
199 argv0);
200 }
201
202 enum {
203 ARG_VERSION = 256,
204 ARG_FROM_SAMPLERATE,
205 ARG_TO_SAMPLERATE,
206 ARG_FREQUENCY,
207 ARG_SAMPLES,
208 ARG_REWIND,
209 ARG_RESAMPLE_METHOD,
210 ARG_DUMP_RESAMPLE_METHODS
211 };
212
dump_resample_methods(void)213 static void dump_resample_methods(void) {
214 int i;
215
216 for (i = 0; i < PA_RESAMPLER_MAX; i++)
217 if (pa_resample_method_supported(i))
218 printf("%s\n", pa_resample_method_to_string(i));
219
220 }
221
main(int argc,char * argv[])222 int main(int argc, char *argv[]) {
223 pa_mempool *pool = NULL;
224 pa_sample_spec a, b;
225 pa_resample_method_t method;
226 int ret = 1, c;
227 unsigned samples, frequency, rewind;
228 unsigned crossover_freq = 120;
229 pa_resampler *resampler;
230 pa_memchunk in_chunk, out_chunk, rewound_chunk, silence_chunk;
231 pa_usec_t ts;
232 pa_memblockq *history_queue = NULL;
233 size_t in_rewind_size, in_frame_size, history_size, out_rewind_size, old_length, in_resampler_buffer, n_out_expected;
234 float max_diff;
235 double delay_before, delay_after, delay_expected;
236
237 static const struct option long_options[] = {
238 {"help", 0, NULL, 'h'},
239 {"verbose", 0, NULL, 'v'},
240 {"version", 0, NULL, ARG_VERSION},
241 {"from-rate", 1, NULL, ARG_FROM_SAMPLERATE},
242 {"to-rate", 1, NULL, ARG_TO_SAMPLERATE},
243 {"frequency", 1, NULL, ARG_FREQUENCY},
244 {"samples", 1, NULL, ARG_SAMPLES},
245 {"rewind", 1, NULL, ARG_REWIND},
246 {"resample-method", 1, NULL, ARG_RESAMPLE_METHOD},
247 {"dump-resample-methods", 0, NULL, ARG_DUMP_RESAMPLE_METHODS},
248 {NULL, 0, NULL, 0}
249 };
250
251 setlocale(LC_ALL, "");
252 #ifdef ENABLE_NLS
253 bindtextdomain(GETTEXT_PACKAGE, PULSE_LOCALEDIR);
254 #endif
255
256 pa_log_set_level(PA_LOG_WARN);
257 if (!getenv("MAKE_CHECK"))
258 pa_log_set_level(PA_LOG_INFO);
259
260 a.channels = b.channels = 1;
261 a.rate = 48000;
262 b.rate = 44100;
263 a.format = b.format = PA_SAMPLE_FLOAT32NE;
264
265 method = PA_RESAMPLER_AUTO;
266 frequency = 1000;
267 samples = 5000;
268 rewind = 2500;
269
270 while ((c = getopt_long(argc, argv, "hv", long_options, NULL)) != -1) {
271
272 switch (c) {
273 case 'h' :
274 help(argv[0]);
275 ret = 0;
276 goto quit;
277
278 case 'v':
279 pa_log_set_level(PA_LOG_DEBUG);
280 break;
281
282 case ARG_VERSION:
283 printf("%s %s\n", argv[0], PACKAGE_VERSION);
284 ret = 0;
285 goto quit;
286
287 case ARG_DUMP_RESAMPLE_METHODS:
288 dump_resample_methods();
289 ret = 0;
290 goto quit;
291
292 case ARG_FROM_SAMPLERATE:
293 a.rate = (uint32_t) atoi(optarg);
294 break;
295
296 case ARG_TO_SAMPLERATE:
297 b.rate = (uint32_t) atoi(optarg);
298 break;
299
300 case ARG_FREQUENCY:
301 frequency = (unsigned) atoi(optarg);
302 break;
303
304 case ARG_SAMPLES:
305 samples = (unsigned) atoi(optarg);
306 break;
307
308 case ARG_REWIND:
309 rewind = (unsigned) atoi(optarg);
310 break;
311
312 case ARG_RESAMPLE_METHOD:
313 if (*optarg == '\0' || pa_streq(optarg, "help")) {
314 dump_resample_methods();
315 ret = 0;
316 goto quit;
317 }
318 method = pa_parse_resample_method(optarg);
319 break;
320
321 default:
322 goto quit;
323 }
324 }
325
326 pa_log_info("=== Square wave %u Hz, %u samples. Resampling using %s from %u Hz to %u Hz, rewinding %u output samples.", frequency,
327 samples, pa_resample_method_to_string(method), a.rate, b.rate, rewind);
328
329 ret = 0;
330 pa_assert_se(pool = pa_mempool_new(PA_MEM_TYPE_PRIVATE, 0, true));
331
332 pa_log_debug("Compilation CFLAGS: %s", PA_CFLAGS);
333
334 /* Setup resampler */
335 ts = pa_rtclock_now();
336 pa_assert_se(resampler = pa_resampler_new(pool, &a, NULL, &b, NULL, crossover_freq, method, 0));
337 pa_log_info("Init took %llu usec", (long long unsigned)(pa_rtclock_now() - ts));
338
339 /* Generate input data */
340 in_chunk.memblock = generate_block(pool, &a, frequency, 0.5, samples);
341 in_chunk.length = pa_memblock_get_length(in_chunk.memblock);
342 in_chunk.index = 0;
343 in_frame_size = pa_frame_size(&a);
344
345 /* First, resample the full block */
346 ts = pa_rtclock_now();
347 pa_resampler_run(resampler, &in_chunk, &out_chunk);
348 if (!out_chunk.memblock) {
349 pa_memblock_unref(in_chunk.memblock);
350 pa_log_warn("Resampling did not return any output data");
351 ret = 1;
352 goto quit;
353 }
354
355 pa_log_info("resampling took %llu usec.", (long long unsigned)(pa_rtclock_now() - ts));
356 if (rewind > out_chunk.length / pa_frame_size(&b)) {
357 pa_log_warn("Specified number of frames to rewind (%u) larger than number of output frames (%lu), aborting.", rewind, out_chunk.length / pa_frame_size(&b));
358 ret = 1;
359 goto quit1;
360 }
361
362 /* Get delay after first resampling pass */
363 delay_before = pa_resampler_get_delay(resampler, true);
364
365 /* Create and prepare history queue */
366 silence_chunk.memblock = silence_memblock_new(pool, 0);
367 silence_chunk.length = pa_frame_align(pa_memblock_get_length(silence_chunk.memblock), &a);
368 silence_chunk.index = 0;
369 history_queue = pa_memblockq_new("Test-Queue", 0, MEMBLOCKQ_MAXLENGTH, 0, &a, 0, 1, samples * in_frame_size, &silence_chunk);
370 pa_memblock_unref(silence_chunk.memblock);
371
372 pa_memblockq_push(history_queue, &in_chunk);
373 pa_memblockq_drop(history_queue, samples * in_frame_size);
374
375 in_rewind_size = pa_resampler_request(resampler, rewind * pa_frame_size(&b));
376 out_rewind_size = rewind * pa_frame_size(&b);
377 pa_log_debug("Have to rewind %lu input frames", in_rewind_size / in_frame_size);
378 ts = pa_rtclock_now();
379
380 /* Now rewind the resampler */
381 pa_memblockq_rewind(history_queue, in_rewind_size);
382 history_size = calculate_resampler_history_bytes(resampler, in_rewind_size / in_frame_size);
383 pa_log_debug("History is %lu frames.", history_size / in_frame_size);
384 pa_resampler_rewind(resampler, out_rewind_size, history_queue, history_size);
385
386 pa_log_info("Rewind took %llu usec.", (long long unsigned)(pa_rtclock_now() - ts));
387 ts = pa_rtclock_now();
388
389 /* Re-run the resampler */
390 old_length = in_chunk.length;
391 in_chunk.length = in_rewind_size;
392 in_chunk.index = old_length - in_chunk.length;
393 pa_resampler_run(resampler, &in_chunk, &rewound_chunk);
394 if (!rewound_chunk.memblock) {
395 pa_log_warn("Resampler did not return output data for rewind");
396 ret = 1;
397 goto quit1;
398 }
399
400 /* Get delay after rewind */
401 delay_after = pa_resampler_get_delay(resampler, true);
402
403 /* Calculate expected delay */
404 n_out_expected = pa_resampler_result(resampler, in_rewind_size + history_size) / pa_frame_size(&b);
405 delay_expected = delay_before + (double)(in_rewind_size + history_size) / (double)in_frame_size - n_out_expected * (double)a.rate / (double)b.rate;
406
407 /* Check for leftover samples in the resampler buffer */
408 in_resampler_buffer = lround((delay_after - delay_expected) * (double)b.rate / (double)a.rate);
409 if (in_resampler_buffer != 0) {
410 pa_log_debug("%li output frames still in resampler buffer", in_resampler_buffer);
411 }
412
413 pa_log_info("Second resampler run took %llu usec.", (long long unsigned)(pa_rtclock_now() - ts));
414 pa_log_debug("Got %lu output frames", rewound_chunk.length / pa_frame_size(&b));
415 old_length = out_chunk.length;
416 out_chunk.length = rewound_chunk.length;
417 out_chunk.index = old_length - out_chunk.length;
418
419 max_diff = compare_blocks(&b, &out_chunk, &rewound_chunk);
420 pa_log_info("Maximum difference is %.*g", 6, max_diff);
421
422 pa_memblock_unref(rewound_chunk.memblock);
423
424 quit1:
425 pa_memblock_unref(in_chunk.memblock);
426 pa_memblock_unref(out_chunk.memblock);
427
428 pa_resampler_free(resampler);
429 if (history_queue)
430 pa_memblockq_free(history_queue);
431
432 quit:
433 if (pool)
434 pa_mempool_unref(pool);
435
436 return ret;
437 }
438