test-resample.cpp revision 1e4e4f40b4e2c655ca377adbba53df897545c3ae
1/* 2 * Copyright (C) 2012 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#include "AudioResampler.h" 18#include <media/AudioBufferProvider.h> 19#include <unistd.h> 20#include <stdio.h> 21#include <stdlib.h> 22#include <fcntl.h> 23#include <string.h> 24#include <sys/mman.h> 25#include <sys/stat.h> 26#include <errno.h> 27#include <inttypes.h> 28#include <time.h> 29#include <math.h> 30#include <audio_utils/sndfile.h> 31#include <utils/Vector.h> 32 33using namespace android; 34 35bool gVerbose = false; 36 37static int usage(const char* name) { 38 fprintf(stderr,"Usage: %s [-p] [-h] [-v] [-s] [-q {dq|lq|mq|hq|vhq|dlq|dmq|dhq}]" 39 " [-i input-sample-rate] [-o output-sample-rate] [-O csv] [-P csv] [<input-file>]" 40 " <output-file>\n", name); 41 fprintf(stderr," -p enable profiling\n"); 42 fprintf(stderr," -h create wav file\n"); 43 fprintf(stderr," -v verbose : log buffer provider calls\n"); 44 fprintf(stderr," -s stereo (ignored if input file is specified)\n"); 45 fprintf(stderr," -q resampler quality\n"); 46 fprintf(stderr," dq : default quality\n"); 47 fprintf(stderr," lq : low quality\n"); 48 fprintf(stderr," mq : medium quality\n"); 49 fprintf(stderr," hq : high quality\n"); 50 fprintf(stderr," vhq : very high quality\n"); 51 fprintf(stderr," dlq : dynamic low quality\n"); 52 fprintf(stderr," dmq : dynamic medium quality\n"); 53 fprintf(stderr," dhq : dynamic high quality\n"); 54 fprintf(stderr," -i input file sample rate (ignored if input file is specified)\n"); 55 fprintf(stderr," -o output file sample rate\n"); 56 fprintf(stderr," -O # frames output per call to resample() in CSV format\n"); 57 fprintf(stderr," -P # frames provided per call to resample() in CSV format\n"); 58 return -1; 59} 60 61// Convert a list of integers in CSV format to a Vector of those values. 62// Returns the number of elements in the list, or -1 on error. 63int parseCSV(const char *string, Vector<int>& values) 64{ 65 // pass 1: count the number of values and do syntax check 66 size_t numValues = 0; 67 bool hadDigit = false; 68 for (const char *p = string; ; ) { 69 switch (*p++) { 70 case '0': case '1': case '2': case '3': case '4': 71 case '5': case '6': case '7': case '8': case '9': 72 hadDigit = true; 73 break; 74 case '\0': 75 if (hadDigit) { 76 // pass 2: allocate and initialize vector of values 77 values.resize(++numValues); 78 values.editItemAt(0) = atoi(p = optarg); 79 for (size_t i = 1; i < numValues; ) { 80 if (*p++ == ',') { 81 values.editItemAt(i++) = atoi(p); 82 } 83 } 84 return numValues; 85 } 86 // fall through 87 case ',': 88 if (hadDigit) { 89 hadDigit = false; 90 numValues++; 91 break; 92 } 93 // fall through 94 default: 95 return -1; 96 } 97 } 98} 99 100int main(int argc, char* argv[]) { 101 102 const char* const progname = argv[0]; 103 bool profileResample = false; 104 bool profileFilter = false; 105 bool writeHeader = false; 106 int channels = 1; 107 int input_freq = 0; 108 int output_freq = 0; 109 AudioResampler::src_quality quality = AudioResampler::DEFAULT_QUALITY; 110 Vector<int> Ovalues; 111 Vector<int> Pvalues; 112 113 int ch; 114 while ((ch = getopt(argc, argv, "pfhvsq:i:o:O:P:")) != -1) { 115 switch (ch) { 116 case 'p': 117 profileResample = true; 118 break; 119 case 'f': 120 profileFilter = true; 121 break; 122 case 'h': 123 writeHeader = true; 124 break; 125 case 'v': 126 gVerbose = true; 127 break; 128 case 's': 129 channels = 2; 130 break; 131 case 'q': 132 if (!strcmp(optarg, "dq")) 133 quality = AudioResampler::DEFAULT_QUALITY; 134 else if (!strcmp(optarg, "lq")) 135 quality = AudioResampler::LOW_QUALITY; 136 else if (!strcmp(optarg, "mq")) 137 quality = AudioResampler::MED_QUALITY; 138 else if (!strcmp(optarg, "hq")) 139 quality = AudioResampler::HIGH_QUALITY; 140 else if (!strcmp(optarg, "vhq")) 141 quality = AudioResampler::VERY_HIGH_QUALITY; 142 else if (!strcmp(optarg, "dlq")) 143 quality = AudioResampler::DYN_LOW_QUALITY; 144 else if (!strcmp(optarg, "dmq")) 145 quality = AudioResampler::DYN_MED_QUALITY; 146 else if (!strcmp(optarg, "dhq")) 147 quality = AudioResampler::DYN_HIGH_QUALITY; 148 else { 149 usage(progname); 150 return -1; 151 } 152 break; 153 case 'i': 154 input_freq = atoi(optarg); 155 break; 156 case 'o': 157 output_freq = atoi(optarg); 158 break; 159 case 'O': 160 if (parseCSV(optarg, Ovalues) < 0) { 161 fprintf(stderr, "incorrect syntax for -O option\n"); 162 return -1; 163 } 164 break; 165 case 'P': 166 if (parseCSV(optarg, Pvalues) < 0) { 167 fprintf(stderr, "incorrect syntax for -P option\n"); 168 return -1; 169 } 170 break; 171 case '?': 172 default: 173 usage(progname); 174 return -1; 175 } 176 } 177 argc -= optind; 178 argv += optind; 179 180 const char* file_in = NULL; 181 const char* file_out = NULL; 182 if (argc == 1) { 183 file_out = argv[0]; 184 } else if (argc == 2) { 185 file_in = argv[0]; 186 file_out = argv[1]; 187 } else { 188 usage(progname); 189 return -1; 190 } 191 192 // ---------------------------------------------------------- 193 194 size_t input_size; 195 void* input_vaddr; 196 if (argc == 2) { 197 SF_INFO info; 198 info.format = 0; 199 SNDFILE *sf = sf_open(file_in, SFM_READ, &info); 200 if (sf == NULL) { 201 perror(file_in); 202 return EXIT_FAILURE; 203 } 204 input_size = info.frames * info.channels * sizeof(short); 205 input_vaddr = malloc(input_size); 206 (void) sf_readf_short(sf, (short *) input_vaddr, info.frames); 207 sf_close(sf); 208 channels = info.channels; 209 input_freq = info.samplerate; 210 } else { 211 // data for testing is exactly (input sampling rate/1000)/2 seconds 212 // so 44.1khz input is 22.05 seconds 213 double k = 1000; // Hz / s 214 double time = (input_freq / 2) / k; 215 size_t input_frames = size_t(input_freq * time); 216 input_size = channels * sizeof(int16_t) * input_frames; 217 input_vaddr = malloc(input_size); 218 int16_t* in = (int16_t*)input_vaddr; 219 for (size_t i=0 ; i<input_frames ; i++) { 220 double t = double(i) / input_freq; 221 double y = sin(M_PI * k * t * t); 222 int16_t yi = floor(y * 32767.0 + 0.5); 223 for (size_t j=0 ; j<(size_t)channels ; j++) { 224 in[i*channels + j] = yi / (1+j); // right ch. 1/2 left ch. 225 } 226 } 227 } 228 229 // ---------------------------------------------------------- 230 231 class Provider: public AudioBufferProvider { 232 int16_t* const mAddr; // base address 233 const size_t mNumFrames; // total frames 234 const int mChannels; 235 size_t mNextFrame; // index of next frame to provide 236 size_t mUnrel; // number of frames not yet released 237 const Vector<int> mPvalues; // number of frames provided per call 238 size_t mNextPidx; // index of next entry in mPvalues to use 239 public: 240 Provider(const void* addr, size_t size, int channels, const Vector<int>& Pvalues) 241 : mAddr((int16_t*) addr), 242 mNumFrames(size / (channels*sizeof(int16_t))), 243 mChannels(channels), 244 mNextFrame(0), mUnrel(0), mPvalues(Pvalues), mNextPidx(0) { 245 } 246 virtual status_t getNextBuffer(Buffer* buffer, 247 int64_t pts = kInvalidPTS) { 248 (void)pts; // suppress warning 249 size_t requestedFrames = buffer->frameCount; 250 if (requestedFrames > mNumFrames - mNextFrame) { 251 buffer->frameCount = mNumFrames - mNextFrame; 252 } 253 if (!mPvalues.isEmpty()) { 254 size_t provided = mPvalues[mNextPidx++]; 255 printf("mPvalue[%zu]=%zu not %zu\n", mNextPidx-1, provided, buffer->frameCount); 256 if (provided < buffer->frameCount) { 257 buffer->frameCount = provided; 258 } 259 if (mNextPidx >= mPvalues.size()) { 260 mNextPidx = 0; 261 } 262 } 263 if (gVerbose) { 264 printf("getNextBuffer() requested %zu frames out of %zu frames available," 265 " and returned %zu frames\n", 266 requestedFrames, (size_t) (mNumFrames - mNextFrame), buffer->frameCount); 267 } 268 mUnrel = buffer->frameCount; 269 if (buffer->frameCount > 0) { 270 buffer->i16 = &mAddr[mChannels * mNextFrame]; 271 return NO_ERROR; 272 } else { 273 buffer->i16 = NULL; 274 return NOT_ENOUGH_DATA; 275 } 276 } 277 virtual void releaseBuffer(Buffer* buffer) { 278 if (buffer->frameCount > mUnrel) { 279 fprintf(stderr, "ERROR releaseBuffer() released %zu frames but only %zu available " 280 "to release\n", buffer->frameCount, mUnrel); 281 mNextFrame += mUnrel; 282 mUnrel = 0; 283 } else { 284 if (gVerbose) { 285 printf("releaseBuffer() released %zu frames out of %zu frames available " 286 "to release\n", buffer->frameCount, mUnrel); 287 } 288 mNextFrame += buffer->frameCount; 289 mUnrel -= buffer->frameCount; 290 } 291 buffer->frameCount = 0; 292 buffer->i16 = NULL; 293 } 294 void reset() { 295 mNextFrame = 0; 296 } 297 } provider(input_vaddr, input_size, channels, Pvalues); 298 299 size_t input_frames = input_size / (channels * sizeof(int16_t)); 300 if (gVerbose) { 301 printf("%zu input frames\n", input_frames); 302 } 303 size_t output_size = 2 * 4 * ((int64_t) input_frames * output_freq) / input_freq; 304 output_size &= ~7; // always stereo, 32-bits 305 306 if (profileFilter) { 307 // Check how fast sample rate changes are that require filter changes. 308 // The delta sample rate changes must indicate a downsampling ratio, 309 // and must be larger than 10% changes. 310 // 311 // On fast devices, filters should be generated between 0.1ms - 1ms. 312 // (single threaded). 313 AudioResampler* resampler = AudioResampler::create(16, channels, 314 8000, quality); 315 int looplimit = 100; 316 timespec start, end; 317 clock_gettime(CLOCK_MONOTONIC, &start); 318 for (int i = 0; i < looplimit; ++i) { 319 resampler->setSampleRate(9000); 320 resampler->setSampleRate(12000); 321 resampler->setSampleRate(20000); 322 resampler->setSampleRate(30000); 323 } 324 clock_gettime(CLOCK_MONOTONIC, &end); 325 int64_t start_ns = start.tv_sec * 1000000000LL + start.tv_nsec; 326 int64_t end_ns = end.tv_sec * 1000000000LL + end.tv_nsec; 327 int64_t time = end_ns - start_ns; 328 printf("%.2f sample rate changes with filter calculation/sec\n", 329 looplimit * 4 / (time / 1e9)); 330 331 // Check how fast sample rate changes are without filter changes. 332 // This should be very fast, probably 0.1us - 1us per sample rate 333 // change. 334 resampler->setSampleRate(1000); 335 looplimit = 1000; 336 clock_gettime(CLOCK_MONOTONIC, &start); 337 for (int i = 0; i < looplimit; ++i) { 338 resampler->setSampleRate(1000+i); 339 } 340 clock_gettime(CLOCK_MONOTONIC, &end); 341 start_ns = start.tv_sec * 1000000000LL + start.tv_nsec; 342 end_ns = end.tv_sec * 1000000000LL + end.tv_nsec; 343 time = end_ns - start_ns; 344 printf("%.2f sample rate changes without filter calculation/sec\n", 345 looplimit / (time / 1e9)); 346 resampler->reset(); 347 delete resampler; 348 } 349 350 void* output_vaddr = malloc(output_size); 351 AudioResampler* resampler = AudioResampler::create(16, channels, 352 output_freq, quality); 353 size_t out_frames = output_size/8; 354 355 /* set volume precision to 12 bits, so the volume scale is 1<<12. 356 * The output int32_t is represented as Q4.27, with 4 bits of guard 357 * followed by the int16_t Q.15 portion, and then 12 trailing bits of 358 * additional precision. 359 * 360 * Generally 0 < volumePrecision <= 14 (due to the limits of 361 * int16_t values for Volume). volumePrecision cannot be 0 due 362 * to rounding and shifts. 363 */ 364 const int volumePrecision = 12; // in bits 365 366 resampler->setSampleRate(input_freq); 367 resampler->setVolume(1 << volumePrecision, 1 << volumePrecision); 368 369 if (profileResample) { 370 /* 371 * For profiling on mobile devices, upon experimentation 372 * it is better to run a few trials with a shorter loop limit, 373 * and take the minimum time. 374 * 375 * Long tests can cause CPU temperature to build up and thermal throttling 376 * to reduce CPU frequency. 377 * 378 * For frequency checks (index=0, or 1, etc.): 379 * "cat /sys/devices/system/cpu/cpu${index}/cpufreq/scaling_*_freq" 380 * 381 * For temperature checks (index=0, or 1, etc.): 382 * "cat /sys/class/thermal/thermal_zone${index}/temp" 383 * 384 * Another way to avoid thermal throttling is to fix the CPU frequency 385 * at a lower level which prevents excessive temperatures. 386 */ 387 const int trials = 4; 388 const int looplimit = 4; 389 timespec start, end; 390 int64_t time = 0; 391 392 for (int n = 0; n < trials; ++n) { 393 clock_gettime(CLOCK_MONOTONIC, &start); 394 for (int i = 0; i < looplimit; ++i) { 395 resampler->resample((int*) output_vaddr, out_frames, &provider); 396 provider.reset(); // during benchmarking reset only the provider 397 } 398 clock_gettime(CLOCK_MONOTONIC, &end); 399 int64_t start_ns = start.tv_sec * 1000000000LL + start.tv_nsec; 400 int64_t end_ns = end.tv_sec * 1000000000LL + end.tv_nsec; 401 int64_t diff_ns = end_ns - start_ns; 402 if (n == 0 || diff_ns < time) { 403 time = diff_ns; // save the best out of our trials. 404 } 405 } 406 // Mfrms/s is "Millions of output frames per second". 407 printf("quality: %d channels: %d msec: %" PRId64 " Mfrms/s: %.2lf\n", 408 quality, channels, time/1000000, out_frames * looplimit / (time / 1e9) / 1e6); 409 resampler->reset(); 410 } 411 412 memset(output_vaddr, 0, output_size); 413 if (gVerbose) { 414 printf("resample() %zu output frames\n", out_frames); 415 } 416 if (Ovalues.isEmpty()) { 417 Ovalues.push(out_frames); 418 } 419 for (size_t i = 0, j = 0; i < out_frames; ) { 420 size_t thisFrames = Ovalues[j++]; 421 if (j >= Ovalues.size()) { 422 j = 0; 423 } 424 if (thisFrames == 0 || thisFrames > out_frames - i) { 425 thisFrames = out_frames - i; 426 } 427 resampler->resample((int*) output_vaddr + 2*i, thisFrames, &provider); 428 i += thisFrames; 429 } 430 if (gVerbose) { 431 printf("resample() complete\n"); 432 } 433 resampler->reset(); 434 if (gVerbose) { 435 printf("reset() complete\n"); 436 } 437 delete resampler; 438 resampler = NULL; 439 440 // mono takes left channel only 441 // stereo right channel is half amplitude of stereo left channel (due to input creation) 442 int32_t* out = (int32_t*) output_vaddr; 443 int16_t* convert = (int16_t*) malloc(out_frames * channels * sizeof(int16_t)); 444 445 // round to half towards zero and saturate at int16 (non-dithered) 446 const int roundVal = (1<<(volumePrecision-1)) - 1; // volumePrecision > 0 447 448 for (size_t i = 0; i < out_frames; i++) { 449 for (int j = 0; j < channels; j++) { 450 int32_t s = out[i * 2 + j] + roundVal; // add offset here 451 if (s < 0) { 452 s = (s + 1) >> volumePrecision; // round to 0 453 if (s < -32768) { 454 s = -32768; 455 } 456 } else { 457 s = s >> volumePrecision; 458 if (s > 32767) { 459 s = 32767; 460 } 461 } 462 convert[i * channels + j] = int16_t(s); 463 } 464 } 465 466 // write output to disk 467 if (writeHeader) { 468 SF_INFO info; 469 info.frames = 0; 470 info.samplerate = output_freq; 471 info.channels = channels; 472 info.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16; 473 SNDFILE *sf = sf_open(file_out, SFM_WRITE, &info); 474 if (sf == NULL) { 475 perror(file_out); 476 return EXIT_FAILURE; 477 } 478 (void) sf_writef_short(sf, convert, out_frames); 479 sf_close(sf); 480 } else { 481 int output_fd = open(file_out, O_WRONLY | O_CREAT | O_TRUNC, 482 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); 483 if (output_fd < 0) { 484 perror(file_out); 485 return EXIT_FAILURE; 486 } 487 write(output_fd, convert, out_frames * channels * sizeof(int16_t)); 488 close(output_fd); 489 } 490 491 return EXIT_SUCCESS; 492} 493