• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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