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 47static 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 */ 68static 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 121static 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 141static 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 182static 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 202enum { 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 213static 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 222int 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 424quit1: 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 432quit: 433 if (pool) 434 pa_mempool_unref(pool); 435 436 return ret; 437} 438