test-resample.cpp revision 6582f2b14a21e630654c5522ef9ad64e80d5058d
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 <time.h> 28#include <math.h> 29#include <audio_utils/sndfile.h> 30 31using namespace android; 32 33bool gVerbose = false; 34 35static int usage(const char* name) { 36 fprintf(stderr,"Usage: %s [-p] [-h] [-v] [-s] [-q {dq|lq|mq|hq|vhq|dlq|dmq|dhq}]" 37 " [-i input-sample-rate] [-o output-sample-rate] [<input-file>]" 38 " <output-file>\n", name); 39 fprintf(stderr," -p enable profiling\n"); 40 fprintf(stderr," -h create wav file\n"); 41 fprintf(stderr," -v verbose : log buffer provider calls\n"); 42 fprintf(stderr," -s stereo (ignored if input file is specified)\n"); 43 fprintf(stderr," -q resampler quality\n"); 44 fprintf(stderr," dq : default quality\n"); 45 fprintf(stderr," lq : low quality\n"); 46 fprintf(stderr," mq : medium quality\n"); 47 fprintf(stderr," hq : high quality\n"); 48 fprintf(stderr," vhq : very high quality\n"); 49 fprintf(stderr," dlq : dynamic low quality\n"); 50 fprintf(stderr," dmq : dynamic medium quality\n"); 51 fprintf(stderr," dhq : dynamic high quality\n"); 52 fprintf(stderr," -i input file sample rate (ignored if input file is specified)\n"); 53 fprintf(stderr," -o output file sample rate\n"); 54 return -1; 55} 56 57int main(int argc, char* argv[]) { 58 59 const char* const progname = argv[0]; 60 bool profileResample = false; 61 bool profileFilter = false; 62 bool writeHeader = false; 63 int channels = 1; 64 int input_freq = 0; 65 int output_freq = 0; 66 AudioResampler::src_quality quality = AudioResampler::DEFAULT_QUALITY; 67 68 int ch; 69 while ((ch = getopt(argc, argv, "pfhvsq:i:o:")) != -1) { 70 switch (ch) { 71 case 'p': 72 profileResample = true; 73 break; 74 case 'f': 75 profileFilter = true; 76 break; 77 case 'h': 78 writeHeader = true; 79 break; 80 case 'v': 81 gVerbose = true; 82 break; 83 case 's': 84 channels = 2; 85 break; 86 case 'q': 87 if (!strcmp(optarg, "dq")) 88 quality = AudioResampler::DEFAULT_QUALITY; 89 else if (!strcmp(optarg, "lq")) 90 quality = AudioResampler::LOW_QUALITY; 91 else if (!strcmp(optarg, "mq")) 92 quality = AudioResampler::MED_QUALITY; 93 else if (!strcmp(optarg, "hq")) 94 quality = AudioResampler::HIGH_QUALITY; 95 else if (!strcmp(optarg, "vhq")) 96 quality = AudioResampler::VERY_HIGH_QUALITY; 97 else if (!strcmp(optarg, "dlq")) 98 quality = AudioResampler::DYN_LOW_QUALITY; 99 else if (!strcmp(optarg, "dmq")) 100 quality = AudioResampler::DYN_MED_QUALITY; 101 else if (!strcmp(optarg, "dhq")) 102 quality = AudioResampler::DYN_HIGH_QUALITY; 103 else { 104 usage(progname); 105 return -1; 106 } 107 break; 108 case 'i': 109 input_freq = atoi(optarg); 110 break; 111 case 'o': 112 output_freq = atoi(optarg); 113 break; 114 case '?': 115 default: 116 usage(progname); 117 return -1; 118 } 119 } 120 argc -= optind; 121 argv += optind; 122 123 const char* file_in = NULL; 124 const char* file_out = NULL; 125 if (argc == 1) { 126 file_out = argv[0]; 127 } else if (argc == 2) { 128 file_in = argv[0]; 129 file_out = argv[1]; 130 } else { 131 usage(progname); 132 return -1; 133 } 134 135 // ---------------------------------------------------------- 136 137 size_t input_size; 138 void* input_vaddr; 139 if (argc == 2) { 140 SF_INFO info; 141 info.format = 0; 142 SNDFILE *sf = sf_open(file_in, SFM_READ, &info); 143 if (sf == NULL) { 144 perror(file_in); 145 return EXIT_FAILURE; 146 } 147 input_size = info.frames * info.channels * sizeof(short); 148 input_vaddr = malloc(input_size); 149 (void) sf_readf_short(sf, (short *) input_vaddr, info.frames); 150 sf_close(sf); 151 channels = info.channels; 152 input_freq = info.samplerate; 153 } else { 154 // data for testing is exactly (input sampling rate/1000)/2 seconds 155 // so 44.1khz input is 22.05 seconds 156 double k = 1000; // Hz / s 157 double time = (input_freq / 2) / k; 158 size_t input_frames = size_t(input_freq * time); 159 input_size = channels * sizeof(int16_t) * input_frames; 160 input_vaddr = malloc(input_size); 161 int16_t* in = (int16_t*)input_vaddr; 162 for (size_t i=0 ; i<input_frames ; i++) { 163 double t = double(i) / input_freq; 164 double y = sin(M_PI * k * t * t); 165 int16_t yi = floor(y * 32767.0 + 0.5); 166 for (size_t j=0 ; j<(size_t)channels ; j++) { 167 in[i*channels + j] = yi / (1+j); // right ch. 1/2 left ch. 168 } 169 } 170 } 171 172 // ---------------------------------------------------------- 173 174 class Provider: public AudioBufferProvider { 175 int16_t* const mAddr; // base address 176 const size_t mNumFrames; // total frames 177 const int mChannels; 178 size_t mNextFrame; // index of next frame to provide 179 size_t mUnrel; // number of frames not yet released 180 public: 181 Provider(const void* addr, size_t size, int channels) 182 : mAddr((int16_t*) addr), 183 mNumFrames(size / (channels*sizeof(int16_t))), 184 mChannels(channels), 185 mNextFrame(0), mUnrel(0) { 186 } 187 virtual status_t getNextBuffer(Buffer* buffer, 188 int64_t pts = kInvalidPTS) { 189 (void)pts; // suppress warning 190 size_t requestedFrames = buffer->frameCount; 191 if (requestedFrames > mNumFrames - mNextFrame) { 192 buffer->frameCount = mNumFrames - mNextFrame; 193 } 194 if (gVerbose) { 195 printf("getNextBuffer() requested %u frames out of %u frames available," 196 " and returned %u frames\n", 197 requestedFrames, mNumFrames - mNextFrame, buffer->frameCount); 198 } 199 mUnrel = buffer->frameCount; 200 if (buffer->frameCount > 0) { 201 buffer->i16 = &mAddr[mChannels * mNextFrame]; 202 return NO_ERROR; 203 } else { 204 buffer->i16 = NULL; 205 return NOT_ENOUGH_DATA; 206 } 207 } 208 virtual void releaseBuffer(Buffer* buffer) { 209 if (buffer->frameCount > mUnrel) { 210 fprintf(stderr, "ERROR releaseBuffer() released %u frames but only %u available " 211 "to release\n", buffer->frameCount, mUnrel); 212 mNextFrame += mUnrel; 213 mUnrel = 0; 214 } else { 215 if (gVerbose) { 216 printf("releaseBuffer() released %u frames out of %u frames available " 217 "to release\n", buffer->frameCount, mUnrel); 218 } 219 mNextFrame += buffer->frameCount; 220 mUnrel -= buffer->frameCount; 221 } 222 buffer->frameCount = 0; 223 buffer->i16 = NULL; 224 } 225 void reset() { 226 mNextFrame = 0; 227 } 228 } provider(input_vaddr, input_size, channels); 229 230 size_t input_frames = input_size / (channels * sizeof(int16_t)); 231 if (gVerbose) { 232 printf("%u input frames\n", input_frames); 233 } 234 size_t output_size = 2 * 4 * ((int64_t) input_frames * output_freq) / input_freq; 235 output_size &= ~7; // always stereo, 32-bits 236 237 if (profileFilter) { 238 // Check how fast sample rate changes are that require filter changes. 239 // The delta sample rate changes must indicate a downsampling ratio, 240 // and must be larger than 10% changes. 241 // 242 // On fast devices, filters should be generated between 0.1ms - 1ms. 243 // (single threaded). 244 AudioResampler* resampler = AudioResampler::create(16, channels, 245 8000, quality); 246 int looplimit = 100; 247 timespec start, end; 248 clock_gettime(CLOCK_MONOTONIC, &start); 249 for (int i = 0; i < looplimit; ++i) { 250 resampler->setSampleRate(9000); 251 resampler->setSampleRate(12000); 252 resampler->setSampleRate(20000); 253 resampler->setSampleRate(30000); 254 } 255 clock_gettime(CLOCK_MONOTONIC, &end); 256 int64_t start_ns = start.tv_sec * 1000000000LL + start.tv_nsec; 257 int64_t end_ns = end.tv_sec * 1000000000LL + end.tv_nsec; 258 int64_t time = end_ns - start_ns; 259 printf("%.2f sample rate changes with filter calculation/sec\n", 260 looplimit * 4 / (time / 1e9)); 261 262 // Check how fast sample rate changes are without filter changes. 263 // This should be very fast, probably 0.1us - 1us per sample rate 264 // change. 265 resampler->setSampleRate(1000); 266 looplimit = 1000; 267 clock_gettime(CLOCK_MONOTONIC, &start); 268 for (int i = 0; i < looplimit; ++i) { 269 resampler->setSampleRate(1000+i); 270 } 271 clock_gettime(CLOCK_MONOTONIC, &end); 272 start_ns = start.tv_sec * 1000000000LL + start.tv_nsec; 273 end_ns = end.tv_sec * 1000000000LL + end.tv_nsec; 274 time = end_ns - start_ns; 275 printf("%.2f sample rate changes without filter calculation/sec\n", 276 looplimit / (time / 1e9)); 277 resampler->reset(); 278 delete resampler; 279 } 280 281 void* output_vaddr = malloc(output_size); 282 AudioResampler* resampler = AudioResampler::create(16, channels, 283 output_freq, quality); 284 size_t out_frames = output_size/8; 285 286 /* set volume precision to 12 bits, so the volume scale is 1<<12. 287 * This means the "integer" part fits in the Q19.12 precision 288 * representation of output int32_t. 289 * 290 * Generally 0 < volumePrecision <= 14 (due to the limits of 291 * int16_t values for Volume). volumePrecision cannot be 0 due 292 * to rounding and shifts. 293 */ 294 const int volumePrecision = 12; // in bits 295 296 resampler->setSampleRate(input_freq); 297 resampler->setVolume(1 << volumePrecision, 1 << volumePrecision); 298 299 if (profileResample) { 300 /* 301 * For profiling on mobile devices, upon experimentation 302 * it is better to run a few trials with a shorter loop limit, 303 * and take the minimum time. 304 * 305 * Long tests can cause CPU temperature to build up and thermal throttling 306 * to reduce CPU frequency. 307 * 308 * For frequency checks (index=0, or 1, etc.): 309 * "cat /sys/devices/system/cpu/cpu${index}/cpufreq/scaling_*_freq" 310 * 311 * For temperature checks (index=0, or 1, etc.): 312 * "cat /sys/class/thermal/thermal_zone${index}/temp" 313 * 314 * Another way to avoid thermal throttling is to fix the CPU frequency 315 * at a lower level which prevents excessive temperatures. 316 */ 317 const int trials = 4; 318 const int looplimit = 4; 319 timespec start, end; 320 int64_t time; 321 322 for (int n = 0; n < trials; ++n) { 323 clock_gettime(CLOCK_MONOTONIC, &start); 324 for (int i = 0; i < looplimit; ++i) { 325 resampler->resample((int*) output_vaddr, out_frames, &provider); 326 provider.reset(); // during benchmarking reset only the provider 327 } 328 clock_gettime(CLOCK_MONOTONIC, &end); 329 int64_t start_ns = start.tv_sec * 1000000000LL + start.tv_nsec; 330 int64_t end_ns = end.tv_sec * 1000000000LL + end.tv_nsec; 331 int64_t diff_ns = end_ns - start_ns; 332 if (n == 0 || diff_ns < time) { 333 time = diff_ns; // save the best out of our trials. 334 } 335 } 336 // Mfrms/s is "Millions of output frames per second". 337 printf("quality: %d channels: %d msec: %lld Mfrms/s: %.2lf\n", 338 quality, channels, time/1000000, out_frames * looplimit / (time / 1e9) / 1e6); 339 resampler->reset(); 340 } 341 342 memset(output_vaddr, 0, output_size); 343 if (gVerbose) { 344 printf("resample() %u output frames\n", out_frames); 345 } 346 resampler->resample((int*) output_vaddr, out_frames, &provider); 347 if (gVerbose) { 348 printf("resample() complete\n"); 349 } 350 resampler->reset(); 351 if (gVerbose) { 352 printf("reset() complete\n"); 353 } 354 delete resampler; 355 resampler = NULL; 356 357 // mono takes left channel only 358 // stereo right channel is half amplitude of stereo left channel (due to input creation) 359 int32_t* out = (int32_t*) output_vaddr; 360 int16_t* convert = (int16_t*) malloc(out_frames * channels * sizeof(int16_t)); 361 362 // round to half towards zero and saturate at int16 (non-dithered) 363 const int roundVal = (1<<(volumePrecision-1)) - 1; // volumePrecision > 0 364 365 for (size_t i = 0; i < out_frames; i++) { 366 for (int j = 0; j < channels; j++) { 367 int32_t s = out[i * 2 + j] + roundVal; // add offset here 368 if (s < 0) { 369 s = (s + 1) >> volumePrecision; // round to 0 370 if (s < -32768) { 371 s = -32768; 372 } 373 } else { 374 s = s >> volumePrecision; 375 if (s > 32767) { 376 s = 32767; 377 } 378 } 379 convert[i * channels + j] = int16_t(s); 380 } 381 } 382 383 // write output to disk 384 if (writeHeader) { 385 SF_INFO info; 386 info.frames = 0; 387 info.samplerate = output_freq; 388 info.channels = channels; 389 info.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16; 390 SNDFILE *sf = sf_open(file_out, SFM_WRITE, &info); 391 if (sf == NULL) { 392 perror(file_out); 393 return EXIT_FAILURE; 394 } 395 (void) sf_writef_short(sf, convert, out_frames); 396 sf_close(sf); 397 } else { 398 int output_fd = open(file_out, O_WRONLY | O_CREAT | O_TRUNC, 399 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); 400 if (output_fd < 0) { 401 perror(file_out); 402 return EXIT_FAILURE; 403 } 404 write(output_fd, convert, out_frames * channels * sizeof(int16_t)); 405 close(output_fd); 406 } 407 408 return EXIT_SUCCESS; 409} 410