blob: 403bb6d34a9aa08562b071c9d753b5d1a5b6c317 [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>
27#include <time.h>
Mathias Agopian3f717612012-11-04 18:49:14 -080028#include <math.h>
Glenn Kastenf5293642013-12-17 14:49:17 -080029#include <audio_utils/sndfile.h>
Mathias Agopian0fc2cb52012-10-21 01:01:38 -070030
31using namespace android;
32
Glenn Kastene00eefe2013-12-17 13:54:29 -080033bool gVerbose = false;
34
Mathias Agopian0fc2cb52012-10-21 01:01:38 -070035static int usage(const char* name) {
Glenn Kastene00eefe2013-12-17 13:54:29 -080036 fprintf(stderr,"Usage: %s [-p] [-h] [-v] [-s] [-q {dq|lq|mq|hq|vhq}] [-i input-sample-rate] "
Mathias Agopian3f717612012-11-04 18:49:14 -080037 "[-o output-sample-rate] [<input-file>] <output-file>\n", name);
38 fprintf(stderr," -p enable profiling\n");
39 fprintf(stderr," -h create wav file\n");
Glenn Kastene00eefe2013-12-17 13:54:29 -080040 fprintf(stderr," -v verbose : log buffer provider calls\n");
Glenn Kastenbd72d222013-12-17 15:22:08 -080041 fprintf(stderr," -s stereo (ignored if input file is specified)\n");
Mathias Agopian3f717612012-11-04 18:49:14 -080042 fprintf(stderr," -q resampler quality\n");
43 fprintf(stderr," dq : default quality\n");
44 fprintf(stderr," lq : low quality\n");
45 fprintf(stderr," mq : medium quality\n");
46 fprintf(stderr," hq : high quality\n");
47 fprintf(stderr," vhq : very high quality\n");
Glenn Kastenbd72d222013-12-17 15:22:08 -080048 fprintf(stderr," -i input file sample rate (ignored if input file is specified)\n");
Mathias Agopian3f717612012-11-04 18:49:14 -080049 fprintf(stderr," -o output file sample rate\n");
Mathias Agopian0fc2cb52012-10-21 01:01:38 -070050 return -1;
51}
52
53int main(int argc, char* argv[]) {
54
Mathias Agopian3f717612012-11-04 18:49:14 -080055 const char* const progname = argv[0];
Mathias Agopian0fc2cb52012-10-21 01:01:38 -070056 bool profiling = false;
57 bool writeHeader = false;
Mathias Agopian3f717612012-11-04 18:49:14 -080058 int channels = 1;
Mathias Agopian0fc2cb52012-10-21 01:01:38 -070059 int input_freq = 0;
60 int output_freq = 0;
61 AudioResampler::src_quality quality = AudioResampler::DEFAULT_QUALITY;
62
63 int ch;
Glenn Kastene00eefe2013-12-17 13:54:29 -080064 while ((ch = getopt(argc, argv, "phvsq:i:o:")) != -1) {
Mathias Agopian0fc2cb52012-10-21 01:01:38 -070065 switch (ch) {
66 case 'p':
67 profiling = true;
68 break;
69 case 'h':
70 writeHeader = true;
71 break;
Glenn Kastene00eefe2013-12-17 13:54:29 -080072 case 'v':
73 gVerbose = true;
74 break;
Mathias Agopian3f717612012-11-04 18:49:14 -080075 case 's':
76 channels = 2;
77 break;
Mathias Agopian0fc2cb52012-10-21 01:01:38 -070078 case 'q':
79 if (!strcmp(optarg, "dq"))
80 quality = AudioResampler::DEFAULT_QUALITY;
81 else if (!strcmp(optarg, "lq"))
82 quality = AudioResampler::LOW_QUALITY;
83 else if (!strcmp(optarg, "mq"))
84 quality = AudioResampler::MED_QUALITY;
85 else if (!strcmp(optarg, "hq"))
86 quality = AudioResampler::HIGH_QUALITY;
87 else if (!strcmp(optarg, "vhq"))
88 quality = AudioResampler::VERY_HIGH_QUALITY;
89 else {
Mathias Agopian3f717612012-11-04 18:49:14 -080090 usage(progname);
Mathias Agopian0fc2cb52012-10-21 01:01:38 -070091 return -1;
92 }
93 break;
94 case 'i':
95 input_freq = atoi(optarg);
96 break;
97 case 'o':
98 output_freq = atoi(optarg);
99 break;
100 case '?':
101 default:
Mathias Agopian3f717612012-11-04 18:49:14 -0800102 usage(progname);
Mathias Agopian0fc2cb52012-10-21 01:01:38 -0700103 return -1;
104 }
105 }
106 argc -= optind;
Mathias Agopian3f717612012-11-04 18:49:14 -0800107 argv += optind;
Mathias Agopian0fc2cb52012-10-21 01:01:38 -0700108
Mathias Agopian3f717612012-11-04 18:49:14 -0800109 const char* file_in = NULL;
110 const char* file_out = NULL;
111 if (argc == 1) {
112 file_out = argv[0];
113 } else if (argc == 2) {
114 file_in = argv[0];
115 file_out = argv[1];
116 } else {
117 usage(progname);
Mathias Agopian0fc2cb52012-10-21 01:01:38 -0700118 return -1;
119 }
120
Mathias Agopian0fc2cb52012-10-21 01:01:38 -0700121 // ----------------------------------------------------------
122
Mathias Agopian3f717612012-11-04 18:49:14 -0800123 size_t input_size;
124 void* input_vaddr;
125 if (argc == 2) {
Glenn Kastenbd72d222013-12-17 15:22:08 -0800126 SF_INFO info;
127 info.format = 0;
128 SNDFILE *sf = sf_open(file_in, SFM_READ, &info);
129 if (sf == NULL) {
130 perror(file_in);
131 return EXIT_FAILURE;
Mathias Agopian3f717612012-11-04 18:49:14 -0800132 }
Glenn Kastenbd72d222013-12-17 15:22:08 -0800133 input_size = info.frames * info.channels * sizeof(short);
134 input_vaddr = malloc(input_size);
135 (void) sf_readf_short(sf, (short *) input_vaddr, info.frames);
136 sf_close(sf);
137 channels = info.channels;
138 input_freq = info.samplerate;
Mathias Agopian3f717612012-11-04 18:49:14 -0800139 } else {
140 double k = 1000; // Hz / s
141 double time = (input_freq / 2) / k;
142 size_t input_frames = size_t(input_freq * time);
143 input_size = channels * sizeof(int16_t) * input_frames;
144 input_vaddr = malloc(input_size);
145 int16_t* in = (int16_t*)input_vaddr;
146 for (size_t i=0 ; i<input_frames ; i++) {
147 double t = double(i) / input_freq;
148 double y = sin(M_PI * k * t * t);
149 int16_t yi = floor(y * 32767.0 + 0.5);
Glenn Kastenb26e3e92012-11-14 08:32:08 -0800150 for (size_t j=0 ; j<(size_t)channels ; j++) {
Mathias Agopianad9af032012-11-04 15:16:13 -0800151 in[i*channels + j] = yi / (1+j);
Mathias Agopian3f717612012-11-04 18:49:14 -0800152 }
153 }
Mathias Agopian0fc2cb52012-10-21 01:01:38 -0700154 }
155
Mathias Agopian0fc2cb52012-10-21 01:01:38 -0700156 // ----------------------------------------------------------
157
158 class Provider: public AudioBufferProvider {
Glenn Kastene00eefe2013-12-17 13:54:29 -0800159 int16_t* const mAddr; // base address
160 const size_t mNumFrames; // total frames
161 const int mChannels;
162 size_t mNextFrame; // index of next frame to provide
163 size_t mUnrel; // number of frames not yet released
Mathias Agopian0fc2cb52012-10-21 01:01:38 -0700164 public:
Glenn Kastene00eefe2013-12-17 13:54:29 -0800165 Provider(const void* addr, size_t size, int channels)
166 : mAddr((int16_t*) addr),
167 mNumFrames(size / (channels*sizeof(int16_t))),
168 mChannels(channels),
169 mNextFrame(0), mUnrel(0) {
Mathias Agopian0fc2cb52012-10-21 01:01:38 -0700170 }
171 virtual status_t getNextBuffer(Buffer* buffer,
172 int64_t pts = kInvalidPTS) {
Glenn Kastene00eefe2013-12-17 13:54:29 -0800173 size_t requestedFrames = buffer->frameCount;
174 if (requestedFrames > mNumFrames - mNextFrame) {
175 buffer->frameCount = mNumFrames - mNextFrame;
176 }
177 if (gVerbose) {
178 printf("getNextBuffer() requested %u frames out of %u frames available,"
179 " and returned %u frames\n",
180 requestedFrames, mNumFrames - mNextFrame, buffer->frameCount);
181 }
182 mUnrel = buffer->frameCount;
183 if (buffer->frameCount > 0) {
184 buffer->i16 = &mAddr[mChannels * mNextFrame];
185 return NO_ERROR;
186 } else {
187 buffer->i16 = NULL;
188 return NOT_ENOUGH_DATA;
189 }
Mathias Agopian0fc2cb52012-10-21 01:01:38 -0700190 }
191 virtual void releaseBuffer(Buffer* buffer) {
Glenn Kastene00eefe2013-12-17 13:54:29 -0800192 if (buffer->frameCount > mUnrel) {
193 fprintf(stderr, "ERROR releaseBuffer() released %u frames but only %u available "
194 "to release\n", buffer->frameCount, mUnrel);
195 mNextFrame += mUnrel;
196 mUnrel = 0;
197 } else {
198 if (gVerbose) {
199 printf("releaseBuffer() released %u frames out of %u frames available "
200 "to release\n", buffer->frameCount, mUnrel);
201 }
202 mNextFrame += buffer->frameCount;
203 mUnrel -= buffer->frameCount;
204 }
Glenn Kasten47f3f5a2013-12-17 16:14:04 -0800205 buffer->frameCount = 0;
206 buffer->i16 = NULL;
Mathias Agopian0fc2cb52012-10-21 01:01:38 -0700207 }
Mathias Agopian3f717612012-11-04 18:49:14 -0800208 } provider(input_vaddr, input_size, channels);
Mathias Agopian0fc2cb52012-10-21 01:01:38 -0700209
Mathias Agopian3f717612012-11-04 18:49:14 -0800210 size_t input_frames = input_size / (channels * sizeof(int16_t));
Glenn Kastene00eefe2013-12-17 13:54:29 -0800211 if (gVerbose) {
212 printf("%u input frames\n", input_frames);
213 }
Mathias Agopian3f717612012-11-04 18:49:14 -0800214 size_t output_size = 2 * 4 * ((int64_t) input_frames * output_freq) / input_freq;
Mathias Agopian0fc2cb52012-10-21 01:01:38 -0700215 output_size &= ~7; // always stereo, 32-bits
216
217 void* output_vaddr = malloc(output_size);
Mathias Agopian0fc2cb52012-10-21 01:01:38 -0700218
219 if (profiling) {
Mathias Agopian3f717612012-11-04 18:49:14 -0800220 AudioResampler* resampler = AudioResampler::create(16, channels,
221 output_freq, quality);
222
223 size_t out_frames = output_size/8;
224 resampler->setSampleRate(input_freq);
225 resampler->setVolume(0x1000, 0x1000);
226
Mathias Agopian0fc2cb52012-10-21 01:01:38 -0700227 memset(output_vaddr, 0, output_size);
228 timespec start, end;
Glenn Kastenda1a3252013-05-10 09:29:51 -0700229 clock_gettime(CLOCK_MONOTONIC, &start);
Mathias Agopian0fc2cb52012-10-21 01:01:38 -0700230 resampler->resample((int*) output_vaddr, out_frames, &provider);
Mathias Agopian3f717612012-11-04 18:49:14 -0800231 resampler->resample((int*) output_vaddr, out_frames, &provider);
232 resampler->resample((int*) output_vaddr, out_frames, &provider);
233 resampler->resample((int*) output_vaddr, out_frames, &provider);
Glenn Kastenda1a3252013-05-10 09:29:51 -0700234 clock_gettime(CLOCK_MONOTONIC, &end);
Mathias Agopian0fc2cb52012-10-21 01:01:38 -0700235 int64_t start_ns = start.tv_sec * 1000000000LL + start.tv_nsec;
236 int64_t end_ns = end.tv_sec * 1000000000LL + end.tv_nsec;
Mathias Agopian3f717612012-11-04 18:49:14 -0800237 int64_t time = (end_ns - start_ns)/4;
Mathias Agopian0fc2cb52012-10-21 01:01:38 -0700238 printf("%f Mspl/s\n", out_frames/(time/1e9)/1e6);
Mathias Agopian3f717612012-11-04 18:49:14 -0800239
240 delete resampler;
Mathias Agopian0fc2cb52012-10-21 01:01:38 -0700241 }
242
Mathias Agopian3f717612012-11-04 18:49:14 -0800243 AudioResampler* resampler = AudioResampler::create(16, channels,
244 output_freq, quality);
245 size_t out_frames = output_size/8;
246 resampler->setSampleRate(input_freq);
247 resampler->setVolume(0x1000, 0x1000);
248
249 memset(output_vaddr, 0, output_size);
Glenn Kastene00eefe2013-12-17 13:54:29 -0800250 if (gVerbose) {
251 printf("resample() %u output frames\n", out_frames);
252 }
Mathias Agopian3f717612012-11-04 18:49:14 -0800253 resampler->resample((int*) output_vaddr, out_frames, &provider);
Glenn Kastene00eefe2013-12-17 13:54:29 -0800254 if (gVerbose) {
255 printf("resample() complete\n");
256 }
257 resampler->reset();
258 if (gVerbose) {
259 printf("reset() complete\n");
260 }
Mathias Agopian3f717612012-11-04 18:49:14 -0800261
Mathias Agopian0fc2cb52012-10-21 01:01:38 -0700262 // down-mix (we just truncate and keep the left channel)
263 int32_t* out = (int32_t*) output_vaddr;
Mathias Agopian3f717612012-11-04 18:49:14 -0800264 int16_t* convert = (int16_t*) malloc(out_frames * channels * sizeof(int16_t));
Mathias Agopian0fc2cb52012-10-21 01:01:38 -0700265 for (size_t i = 0; i < out_frames; i++) {
Mathias Agopian3f717612012-11-04 18:49:14 -0800266 for (int j=0 ; j<channels ; j++) {
267 int32_t s = out[i * 2 + j] >> 12;
268 if (s > 32767) s = 32767;
269 else if (s < -32768) s = -32768;
270 convert[i * channels + j] = int16_t(s);
271 }
Mathias Agopian0fc2cb52012-10-21 01:01:38 -0700272 }
273
274 // write output to disk
Mathias Agopian0fc2cb52012-10-21 01:01:38 -0700275 if (writeHeader) {
Glenn Kastenf5293642013-12-17 14:49:17 -0800276 SF_INFO info;
277 info.frames = 0;
278 info.samplerate = output_freq;
279 info.channels = channels;
280 info.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16;
281 SNDFILE *sf = sf_open(file_out, SFM_WRITE, &info);
282 if (sf == NULL) {
283 perror(file_out);
284 return EXIT_FAILURE;
285 }
286 (void) sf_writef_short(sf, convert, out_frames);
287 sf_close(sf);
288 } else {
289 int output_fd = open(file_out, O_WRONLY | O_CREAT | O_TRUNC,
290 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
291 if (output_fd < 0) {
292 perror(file_out);
293 return EXIT_FAILURE;
294 }
295 write(output_fd, convert, out_frames * channels * sizeof(int16_t));
296 close(output_fd);
Mathias Agopian0fc2cb52012-10-21 01:01:38 -0700297 }
298
Glenn Kastenf5293642013-12-17 14:49:17 -0800299 return EXIT_SUCCESS;
Mathias Agopian0fc2cb52012-10-21 01:01:38 -0700300}