blob: 6279665a8ac637e328cf757c6d8a8af479036148 [file] [log] [blame]
Mathias Agopian0fc2cb52012-10-21 01:01:38 -07001/*
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>
Glenn Kasten1e4e4f42014-04-08 10:41:02 -070027#include <inttypes.h>
Mathias Agopian0fc2cb52012-10-21 01:01:38 -070028#include <time.h>
Mathias Agopian3f717612012-11-04 18:49:14 -080029#include <math.h>
Glenn Kastenf5293642013-12-17 14:49:17 -080030#include <audio_utils/sndfile.h>
Glenn Kastenc52b0332014-02-21 11:35:14 -080031#include <utils/Vector.h>
Mathias Agopian0fc2cb52012-10-21 01:01:38 -070032
33using namespace android;
34
Glenn Kastene00eefe2013-12-17 13:54:29 -080035bool gVerbose = false;
36
Mathias Agopian0fc2cb52012-10-21 01:01:38 -070037static int usage(const char* name) {
Andy Hung86eae0e2013-12-09 12:12:46 -080038 fprintf(stderr,"Usage: %s [-p] [-h] [-v] [-s] [-q {dq|lq|mq|hq|vhq|dlq|dmq|dhq}]"
Glenn Kastenc52b0332014-02-21 11:35:14 -080039 " [-i input-sample-rate] [-o output-sample-rate] [-O csv] [-P csv] [<input-file>]"
Andy Hung86eae0e2013-12-09 12:12:46 -080040 " <output-file>\n", name);
Mathias Agopian3f717612012-11-04 18:49:14 -080041 fprintf(stderr," -p enable profiling\n");
42 fprintf(stderr," -h create wav file\n");
Glenn Kastene00eefe2013-12-17 13:54:29 -080043 fprintf(stderr," -v verbose : log buffer provider calls\n");
Glenn Kastenbd72d222013-12-17 15:22:08 -080044 fprintf(stderr," -s stereo (ignored if input file is specified)\n");
Mathias Agopian3f717612012-11-04 18:49:14 -080045 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");
Andy Hung86eae0e2013-12-09 12:12:46 -080051 fprintf(stderr," dlq : dynamic low quality\n");
52 fprintf(stderr," dmq : dynamic medium quality\n");
53 fprintf(stderr," dhq : dynamic high quality\n");
Glenn Kastenbd72d222013-12-17 15:22:08 -080054 fprintf(stderr," -i input file sample rate (ignored if input file is specified)\n");
Mathias Agopian3f717612012-11-04 18:49:14 -080055 fprintf(stderr," -o output file sample rate\n");
Glenn Kastenc52b0332014-02-21 11:35:14 -080056 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");
Mathias Agopian0fc2cb52012-10-21 01:01:38 -070058 return -1;
59}
60
Glenn Kastenc52b0332014-02-21 11:35:14 -080061// 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
Mathias Agopian0fc2cb52012-10-21 01:01:38 -0700100int main(int argc, char* argv[]) {
101
Mathias Agopian3f717612012-11-04 18:49:14 -0800102 const char* const progname = argv[0];
Andy Hung6582f2b2014-01-03 12:30:41 -0800103 bool profileResample = false;
104 bool profileFilter = false;
Mathias Agopian0fc2cb52012-10-21 01:01:38 -0700105 bool writeHeader = false;
Mathias Agopian3f717612012-11-04 18:49:14 -0800106 int channels = 1;
Mathias Agopian0fc2cb52012-10-21 01:01:38 -0700107 int input_freq = 0;
108 int output_freq = 0;
109 AudioResampler::src_quality quality = AudioResampler::DEFAULT_QUALITY;
Glenn Kastenc52b0332014-02-21 11:35:14 -0800110 Vector<int> Ovalues;
111 Vector<int> Pvalues;
Mathias Agopian0fc2cb52012-10-21 01:01:38 -0700112
113 int ch;
Glenn Kastenc52b0332014-02-21 11:35:14 -0800114 while ((ch = getopt(argc, argv, "pfhvsq:i:o:O:P:")) != -1) {
Mathias Agopian0fc2cb52012-10-21 01:01:38 -0700115 switch (ch) {
116 case 'p':
Andy Hung6582f2b2014-01-03 12:30:41 -0800117 profileResample = true;
118 break;
119 case 'f':
120 profileFilter = true;
Mathias Agopian0fc2cb52012-10-21 01:01:38 -0700121 break;
122 case 'h':
123 writeHeader = true;
124 break;
Glenn Kastene00eefe2013-12-17 13:54:29 -0800125 case 'v':
126 gVerbose = true;
127 break;
Mathias Agopian3f717612012-11-04 18:49:14 -0800128 case 's':
129 channels = 2;
130 break;
Mathias Agopian0fc2cb52012-10-21 01:01:38 -0700131 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;
Andy Hung86eae0e2013-12-09 12:12:46 -0800142 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;
Mathias Agopian0fc2cb52012-10-21 01:01:38 -0700148 else {
Mathias Agopian3f717612012-11-04 18:49:14 -0800149 usage(progname);
Mathias Agopian0fc2cb52012-10-21 01:01:38 -0700150 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;
Glenn Kasten56df9ff2014-02-17 14:57:27 -0800159 case 'O':
Glenn Kastenc52b0332014-02-21 11:35:14 -0800160 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 }
Glenn Kasten56df9ff2014-02-17 14:57:27 -0800170 break;
Mathias Agopian0fc2cb52012-10-21 01:01:38 -0700171 case '?':
172 default:
Mathias Agopian3f717612012-11-04 18:49:14 -0800173 usage(progname);
Mathias Agopian0fc2cb52012-10-21 01:01:38 -0700174 return -1;
175 }
176 }
177 argc -= optind;
Mathias Agopian3f717612012-11-04 18:49:14 -0800178 argv += optind;
Mathias Agopian0fc2cb52012-10-21 01:01:38 -0700179
Mathias Agopian3f717612012-11-04 18:49:14 -0800180 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);
Mathias Agopian0fc2cb52012-10-21 01:01:38 -0700189 return -1;
190 }
191
Mathias Agopian0fc2cb52012-10-21 01:01:38 -0700192 // ----------------------------------------------------------
193
Mathias Agopian3f717612012-11-04 18:49:14 -0800194 size_t input_size;
195 void* input_vaddr;
196 if (argc == 2) {
Glenn Kastenbd72d222013-12-17 15:22:08 -0800197 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;
Mathias Agopian3f717612012-11-04 18:49:14 -0800203 }
Glenn Kastenbd72d222013-12-17 15:22:08 -0800204 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;
Mathias Agopian3f717612012-11-04 18:49:14 -0800210 } else {
Andy Hung86eae0e2013-12-09 12:12:46 -0800211 // data for testing is exactly (input sampling rate/1000)/2 seconds
212 // so 44.1khz input is 22.05 seconds
Mathias Agopian3f717612012-11-04 18:49:14 -0800213 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);
Glenn Kastenb26e3e92012-11-14 08:32:08 -0800223 for (size_t j=0 ; j<(size_t)channels ; j++) {
Andy Hung86eae0e2013-12-09 12:12:46 -0800224 in[i*channels + j] = yi / (1+j); // right ch. 1/2 left ch.
Mathias Agopian3f717612012-11-04 18:49:14 -0800225 }
226 }
Mathias Agopian0fc2cb52012-10-21 01:01:38 -0700227 }
228
Mathias Agopian0fc2cb52012-10-21 01:01:38 -0700229 // ----------------------------------------------------------
230
231 class Provider: public AudioBufferProvider {
Glenn Kastene00eefe2013-12-17 13:54:29 -0800232 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
Glenn Kastenc52b0332014-02-21 11:35:14 -0800237 const Vector<int> mPvalues; // number of frames provided per call
238 size_t mNextPidx; // index of next entry in mPvalues to use
Mathias Agopian0fc2cb52012-10-21 01:01:38 -0700239 public:
Glenn Kastenc52b0332014-02-21 11:35:14 -0800240 Provider(const void* addr, size_t size, int channels, const Vector<int>& Pvalues)
Glenn Kastene00eefe2013-12-17 13:54:29 -0800241 : mAddr((int16_t*) addr),
242 mNumFrames(size / (channels*sizeof(int16_t))),
243 mChannels(channels),
Glenn Kastenc52b0332014-02-21 11:35:14 -0800244 mNextFrame(0), mUnrel(0), mPvalues(Pvalues), mNextPidx(0) {
Mathias Agopian0fc2cb52012-10-21 01:01:38 -0700245 }
246 virtual status_t getNextBuffer(Buffer* buffer,
247 int64_t pts = kInvalidPTS) {
Andy Hung86eae0e2013-12-09 12:12:46 -0800248 (void)pts; // suppress warning
Glenn Kastene00eefe2013-12-17 13:54:29 -0800249 size_t requestedFrames = buffer->frameCount;
250 if (requestedFrames > mNumFrames - mNextFrame) {
251 buffer->frameCount = mNumFrames - mNextFrame;
252 }
Glenn Kastenc52b0332014-02-21 11:35:14 -0800253 if (!mPvalues.isEmpty()) {
254 size_t provided = mPvalues[mNextPidx++];
Glenn Kasten1e4e4f42014-04-08 10:41:02 -0700255 printf("mPvalue[%zu]=%zu not %zu\n", mNextPidx-1, provided, buffer->frameCount);
Glenn Kastenc52b0332014-02-21 11:35:14 -0800256 if (provided < buffer->frameCount) {
257 buffer->frameCount = provided;
258 }
259 if (mNextPidx >= mPvalues.size()) {
260 mNextPidx = 0;
261 }
262 }
Glenn Kastene00eefe2013-12-17 13:54:29 -0800263 if (gVerbose) {
Glenn Kasten1e4e4f42014-04-08 10:41:02 -0700264 printf("getNextBuffer() requested %zu frames out of %zu frames available,"
265 " and returned %zu frames\n",
266 requestedFrames, (size_t) (mNumFrames - mNextFrame), buffer->frameCount);
Glenn Kastene00eefe2013-12-17 13:54:29 -0800267 }
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 }
Mathias Agopian0fc2cb52012-10-21 01:01:38 -0700276 }
277 virtual void releaseBuffer(Buffer* buffer) {
Glenn Kastene00eefe2013-12-17 13:54:29 -0800278 if (buffer->frameCount > mUnrel) {
Glenn Kasten1e4e4f42014-04-08 10:41:02 -0700279 fprintf(stderr, "ERROR releaseBuffer() released %zu frames but only %zu available "
Glenn Kastene00eefe2013-12-17 13:54:29 -0800280 "to release\n", buffer->frameCount, mUnrel);
281 mNextFrame += mUnrel;
282 mUnrel = 0;
283 } else {
284 if (gVerbose) {
Glenn Kasten1e4e4f42014-04-08 10:41:02 -0700285 printf("releaseBuffer() released %zu frames out of %zu frames available "
Glenn Kastene00eefe2013-12-17 13:54:29 -0800286 "to release\n", buffer->frameCount, mUnrel);
287 }
288 mNextFrame += buffer->frameCount;
289 mUnrel -= buffer->frameCount;
290 }
Glenn Kasten47f3f5a2013-12-17 16:14:04 -0800291 buffer->frameCount = 0;
292 buffer->i16 = NULL;
Mathias Agopian0fc2cb52012-10-21 01:01:38 -0700293 }
Andy Hung86eae0e2013-12-09 12:12:46 -0800294 void reset() {
295 mNextFrame = 0;
296 }
Glenn Kastenc52b0332014-02-21 11:35:14 -0800297 } provider(input_vaddr, input_size, channels, Pvalues);
Mathias Agopian0fc2cb52012-10-21 01:01:38 -0700298
Mathias Agopian3f717612012-11-04 18:49:14 -0800299 size_t input_frames = input_size / (channels * sizeof(int16_t));
Glenn Kastene00eefe2013-12-17 13:54:29 -0800300 if (gVerbose) {
Glenn Kasten1e4e4f42014-04-08 10:41:02 -0700301 printf("%zu input frames\n", input_frames);
Glenn Kastene00eefe2013-12-17 13:54:29 -0800302 }
Mathias Agopian3f717612012-11-04 18:49:14 -0800303 size_t output_size = 2 * 4 * ((int64_t) input_frames * output_freq) / input_freq;
Mathias Agopian0fc2cb52012-10-21 01:01:38 -0700304 output_size &= ~7; // always stereo, 32-bits
305
Andy Hung6582f2b2014-01-03 12:30:41 -0800306 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;
Andy Hung86eae0e2013-12-09 12:12:46 -0800316 timespec start, end;
317 clock_gettime(CLOCK_MONOTONIC, &start);
318 for (int i = 0; i < looplimit; ++i) {
Andy Hung6582f2b2014-01-03 12:30:41 -0800319 resampler->setSampleRate(9000);
320 resampler->setSampleRate(12000);
321 resampler->setSampleRate(20000);
322 resampler->setSampleRate(30000);
Andy Hung86eae0e2013-12-09 12:12:46 -0800323 }
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;
Andy Hung6582f2b2014-01-03 12:30:41 -0800328 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.
Andy Hung84a0c6e2014-04-02 11:24:53 -0700356 * 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.
Andy Hung6582f2b2014-01-03 12:30:41 -0800359 *
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;
Glenn Kasten1e4e4f42014-04-08 10:41:02 -0700390 int64_t time = 0;
Andy Hung6582f2b2014-01-03 12:30:41 -0800391
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".
Glenn Kasten1e4e4f42014-04-08 10:41:02 -0700407 printf("quality: %d channels: %d msec: %" PRId64 " Mfrms/s: %.2lf\n",
Andy Hung6582f2b2014-01-03 12:30:41 -0800408 quality, channels, time/1000000, out_frames * looplimit / (time / 1e9) / 1e6);
Andy Hung86eae0e2013-12-09 12:12:46 -0800409 resampler->reset();
410 }
411
Mathias Agopian3f717612012-11-04 18:49:14 -0800412 memset(output_vaddr, 0, output_size);
Glenn Kastene00eefe2013-12-17 13:54:29 -0800413 if (gVerbose) {
Glenn Kasten1e4e4f42014-04-08 10:41:02 -0700414 printf("resample() %zu output frames\n", out_frames);
Glenn Kastene00eefe2013-12-17 13:54:29 -0800415 }
Glenn Kastenc52b0332014-02-21 11:35:14 -0800416 if (Ovalues.isEmpty()) {
417 Ovalues.push(out_frames);
Glenn Kasten56df9ff2014-02-17 14:57:27 -0800418 }
Glenn Kastenc52b0332014-02-21 11:35:14 -0800419 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 }
Glenn Kasten56df9ff2014-02-17 14:57:27 -0800427 resampler->resample((int*) output_vaddr + 2*i, thisFrames, &provider);
428 i += thisFrames;
429 }
Glenn Kastene00eefe2013-12-17 13:54:29 -0800430 if (gVerbose) {
431 printf("resample() complete\n");
432 }
433 resampler->reset();
434 if (gVerbose) {
435 printf("reset() complete\n");
436 }
Andy Hung6582f2b2014-01-03 12:30:41 -0800437 delete resampler;
438 resampler = NULL;
Mathias Agopian3f717612012-11-04 18:49:14 -0800439
Andy Hung86eae0e2013-12-09 12:12:46 -0800440 // mono takes left channel only
441 // stereo right channel is half amplitude of stereo left channel (due to input creation)
Mathias Agopian0fc2cb52012-10-21 01:01:38 -0700442 int32_t* out = (int32_t*) output_vaddr;
Mathias Agopian3f717612012-11-04 18:49:14 -0800443 int16_t* convert = (int16_t*) malloc(out_frames * channels * sizeof(int16_t));
Andy Hung86eae0e2013-12-09 12:12:46 -0800444
Andy Hung6582f2b2014-01-03 12:30:41 -0800445 // round to half towards zero and saturate at int16 (non-dithered)
446 const int roundVal = (1<<(volumePrecision-1)) - 1; // volumePrecision > 0
447
Mathias Agopian0fc2cb52012-10-21 01:01:38 -0700448 for (size_t i = 0; i < out_frames; i++) {
Andy Hung86eae0e2013-12-09 12:12:46 -0800449 for (int j = 0; j < channels; j++) {
Andy Hung6582f2b2014-01-03 12:30:41 -0800450 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 }
Mathias Agopian3f717612012-11-04 18:49:14 -0800462 convert[i * channels + j] = int16_t(s);
463 }
Mathias Agopian0fc2cb52012-10-21 01:01:38 -0700464 }
465
466 // write output to disk
Mathias Agopian0fc2cb52012-10-21 01:01:38 -0700467 if (writeHeader) {
Glenn Kastenf5293642013-12-17 14:49:17 -0800468 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);
Mathias Agopian0fc2cb52012-10-21 01:01:38 -0700489 }
490
Glenn Kastenf5293642013-12-17 14:49:17 -0800491 return EXIT_SUCCESS;
Mathias Agopian0fc2cb52012-10-21 01:01:38 -0700492}