blob: 9a4fad6e0c1a162e38d8f7ea983359f95aada23d [file] [log] [blame]
Andy Hunge4fc4232014-06-17 15:10:51 -07001/*
2 * Copyright (C) 2014 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 <stdio.h>
18#include <inttypes.h>
19#include <math.h>
20#include <vector>
21#include <audio_utils/primitives.h>
22#include <audio_utils/sndfile.h>
23#include <media/AudioBufferProvider.h>
24#include "AudioMixer.h"
25#include "test_utils.h"
26
27/* Testing is typically through creation of an output WAV file from several
28 * source inputs, to be later analyzed by an audio program such as Audacity.
29 *
30 * Sine or chirp functions are typically more useful as input to the mixer
31 * as they show up as straight lines on a spectrogram if successfully mixed.
32 *
33 * A sample shell script is provided: mixer_to_wave_tests.sh
34 */
35
36using namespace android;
37
38static void usage(const char* name) {
Andy Hunge93b6b72014-07-17 21:30:53 -070039 fprintf(stderr, "Usage: %s [-f] [-m] [-c channels]"
Andy Hunge4fc4232014-06-17 15:10:51 -070040 " [-s sample-rate] [-o <output-file>] [-a <aux-buffer-file>] [-P csv]"
41 " (<input-file> | <command>)+\n", name);
42 fprintf(stderr, " -f enable floating point input track\n");
43 fprintf(stderr, " -m enable floating point mixer output\n");
Andy Hunge93b6b72014-07-17 21:30:53 -070044 fprintf(stderr, " -c number of mixer output channels\n");
Andy Hunge4fc4232014-06-17 15:10:51 -070045 fprintf(stderr, " -s mixer sample-rate\n");
46 fprintf(stderr, " -o <output-file> WAV file, pcm16 (or float if -m specified)\n");
47 fprintf(stderr, " -a <aux-buffer-file>\n");
48 fprintf(stderr, " -P # frames provided per call to resample() in CSV format\n");
49 fprintf(stderr, " <input-file> is a WAV file\n");
50 fprintf(stderr, " <command> can be 'sine:<channels>,<frequency>,<samplerate>'\n");
51 fprintf(stderr, " 'chirp:<channels>,<samplerate>'\n");
52}
53
54static int writeFile(const char *filename, const void *buffer,
55 uint32_t sampleRate, uint32_t channels, size_t frames, bool isBufferFloat) {
56 if (filename == NULL) {
57 return 0; // ok to pass in NULL filename
58 }
59 // write output to file.
60 SF_INFO info;
61 info.frames = 0;
62 info.samplerate = sampleRate;
63 info.channels = channels;
64 info.format = SF_FORMAT_WAV | (isBufferFloat ? SF_FORMAT_FLOAT : SF_FORMAT_PCM_16);
Glenn Kastena4daf0b2014-07-28 16:34:45 -070065 printf("saving file:%s channels:%u samplerate:%u frames:%zu\n",
Andy Hunge4fc4232014-06-17 15:10:51 -070066 filename, info.channels, info.samplerate, frames);
67 SNDFILE *sf = sf_open(filename, SFM_WRITE, &info);
68 if (sf == NULL) {
69 perror(filename);
70 return EXIT_FAILURE;
71 }
72 if (isBufferFloat) {
73 (void) sf_writef_float(sf, (float*)buffer, frames);
74 } else {
75 (void) sf_writef_short(sf, (short*)buffer, frames);
76 }
77 sf_close(sf);
78 return EXIT_SUCCESS;
79}
80
81int main(int argc, char* argv[]) {
82 const char* const progname = argv[0];
83 bool useInputFloat = false;
84 bool useMixerFloat = false;
85 bool useRamp = true;
86 uint32_t outputSampleRate = 48000;
87 uint32_t outputChannels = 2; // stereo for now
88 std::vector<int> Pvalues;
89 const char* outputFilename = NULL;
90 const char* auxFilename = NULL;
91 std::vector<int32_t> Names;
92 std::vector<SignalProvider> Providers;
93
Andy Hunge93b6b72014-07-17 21:30:53 -070094 for (int ch; (ch = getopt(argc, argv, "fmc:s:o:a:P:")) != -1;) {
Andy Hunge4fc4232014-06-17 15:10:51 -070095 switch (ch) {
96 case 'f':
97 useInputFloat = true;
98 break;
99 case 'm':
100 useMixerFloat = true;
101 break;
Andy Hunge93b6b72014-07-17 21:30:53 -0700102 case 'c':
103 outputChannels = atoi(optarg);
104 break;
Andy Hunge4fc4232014-06-17 15:10:51 -0700105 case 's':
106 outputSampleRate = atoi(optarg);
107 break;
108 case 'o':
109 outputFilename = optarg;
110 break;
111 case 'a':
112 auxFilename = optarg;
113 break;
114 case 'P':
115 if (parseCSV(optarg, Pvalues) < 0) {
116 fprintf(stderr, "incorrect syntax for -P option\n");
117 return EXIT_FAILURE;
118 }
119 break;
120 case '?':
121 default:
122 usage(progname);
123 return EXIT_FAILURE;
124 }
125 }
126 argc -= optind;
127 argv += optind;
128
129 if (argc == 0) {
130 usage(progname);
131 return EXIT_FAILURE;
132 }
133 if ((unsigned)argc > AudioMixer::MAX_NUM_TRACKS) {
134 fprintf(stderr, "too many tracks: %d > %u", argc, AudioMixer::MAX_NUM_TRACKS);
135 return EXIT_FAILURE;
136 }
137
138 size_t outputFrames = 0;
139
140 // create providers for each track
141 Providers.resize(argc);
142 for (int i = 0; i < argc; ++i) {
143 static const char chirp[] = "chirp:";
144 static const char sine[] = "sine:";
145 static const double kSeconds = 1;
146
147 if (!strncmp(argv[i], chirp, strlen(chirp))) {
148 std::vector<int> v;
149
150 parseCSV(argv[i] + strlen(chirp), v);
151 if (v.size() == 2) {
152 printf("creating chirp(%d %d)\n", v[0], v[1]);
153 if (useInputFloat) {
154 Providers[i].setChirp<float>(v[0], 0, v[1]/2, v[1], kSeconds);
155 } else {
156 Providers[i].setChirp<int16_t>(v[0], 0, v[1]/2, v[1], kSeconds);
157 }
158 Providers[i].setIncr(Pvalues);
159 } else {
160 fprintf(stderr, "malformed input '%s'\n", argv[i]);
161 }
162 } else if (!strncmp(argv[i], sine, strlen(sine))) {
163 std::vector<int> v;
164
165 parseCSV(argv[i] + strlen(sine), v);
166 if (v.size() == 3) {
Andy Hunge93b6b72014-07-17 21:30:53 -0700167 printf("creating sine(%d %d %d)\n", v[0], v[1], v[2]);
Andy Hunge4fc4232014-06-17 15:10:51 -0700168 if (useInputFloat) {
169 Providers[i].setSine<float>(v[0], v[1], v[2], kSeconds);
170 } else {
171 Providers[i].setSine<int16_t>(v[0], v[1], v[2], kSeconds);
172 }
173 Providers[i].setIncr(Pvalues);
174 } else {
175 fprintf(stderr, "malformed input '%s'\n", argv[i]);
176 }
177 } else {
178 printf("creating filename(%s)\n", argv[i]);
179 if (useInputFloat) {
180 Providers[i].setFile<float>(argv[i]);
181 } else {
182 Providers[i].setFile<short>(argv[i]);
183 }
184 Providers[i].setIncr(Pvalues);
185 }
186 // calculate the number of output frames
187 size_t nframes = (int64_t) Providers[i].getNumFrames() * outputSampleRate
188 / Providers[i].getSampleRate();
189 if (i == 0 || outputFrames > nframes) { // choose minimum for outputFrames
190 outputFrames = nframes;
191 }
192 }
193
194 // create the output buffer.
195 const size_t outputFrameSize = outputChannels
196 * (useMixerFloat ? sizeof(float) : sizeof(int16_t));
197 const size_t outputSize = outputFrames * outputFrameSize;
Andy Hunge93b6b72014-07-17 21:30:53 -0700198 const audio_channel_mask_t outputChannelMask =
199 audio_channel_out_mask_from_count(outputChannels);
Andy Hunge4fc4232014-06-17 15:10:51 -0700200 void *outputAddr = NULL;
201 (void) posix_memalign(&outputAddr, 32, outputSize);
202 memset(outputAddr, 0, outputSize);
203
204 // create the aux buffer, if needed.
205 const size_t auxFrameSize = sizeof(int32_t); // Q4.27 always
206 const size_t auxSize = outputFrames * auxFrameSize;
207 void *auxAddr = NULL;
208 if (auxFilename) {
209 (void) posix_memalign(&auxAddr, 32, auxSize);
210 memset(auxAddr, 0, auxSize);
211 }
212
213 // create the mixer.
214 const size_t mixerFrameCount = 320; // typical numbers may range from 240 or 960
215 AudioMixer *mixer = new AudioMixer(mixerFrameCount, outputSampleRate);
216 audio_format_t inputFormat = useInputFloat
217 ? AUDIO_FORMAT_PCM_FLOAT : AUDIO_FORMAT_PCM_16_BIT;
218 audio_format_t mixerFormat = useMixerFloat
219 ? AUDIO_FORMAT_PCM_FLOAT : AUDIO_FORMAT_PCM_16_BIT;
220 float f = AudioMixer::UNITY_GAIN_FLOAT / Providers.size(); // normalize volume by # tracks
221 static float f0; // zero
222
223 // set up the tracks.
224 for (size_t i = 0; i < Providers.size(); ++i) {
225 //printf("track %d out of %d\n", i, Providers.size());
226 uint32_t channelMask = audio_channel_out_mask_from_count(Providers[i].getNumChannels());
227 int32_t name = mixer->getTrackName(channelMask,
228 inputFormat, AUDIO_SESSION_OUTPUT_MIX);
229 ALOG_ASSERT(name >= 0);
230 Names.push_back(name);
231 mixer->setBufferProvider(name, &Providers[i]);
232 mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::MAIN_BUFFER,
Andy Hunge93b6b72014-07-17 21:30:53 -0700233 (void *)outputAddr);
Andy Hunge4fc4232014-06-17 15:10:51 -0700234 mixer->setParameter(
235 name,
236 AudioMixer::TRACK,
Andy Hunge93b6b72014-07-17 21:30:53 -0700237 AudioMixer::MIXER_FORMAT,
238 (void *)(uintptr_t)mixerFormat);
239 mixer->setParameter(
240 name,
241 AudioMixer::TRACK,
242 AudioMixer::FORMAT,
Andy Hunge4fc4232014-06-17 15:10:51 -0700243 (void *)(uintptr_t)inputFormat);
244 mixer->setParameter(
245 name,
Andy Hunge93b6b72014-07-17 21:30:53 -0700246 AudioMixer::TRACK,
247 AudioMixer::MIXER_CHANNEL_MASK,
248 (void *)(uintptr_t)outputChannelMask);
249 mixer->setParameter(
250 name,
251 AudioMixer::TRACK,
252 AudioMixer::CHANNEL_MASK,
253 (void *)(uintptr_t)channelMask);
254 mixer->setParameter(
255 name,
Andy Hunge4fc4232014-06-17 15:10:51 -0700256 AudioMixer::RESAMPLE,
257 AudioMixer::SAMPLE_RATE,
258 (void *)(uintptr_t)Providers[i].getSampleRate());
259 if (useRamp) {
260 mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME0, &f0);
261 mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME1, &f0);
262 mixer->setParameter(name, AudioMixer::RAMP_VOLUME, AudioMixer::VOLUME0, &f);
263 mixer->setParameter(name, AudioMixer::RAMP_VOLUME, AudioMixer::VOLUME1, &f);
264 } else {
265 mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME0, &f);
266 mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME1, &f);
267 }
268 if (auxFilename) {
269 mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::AUX_BUFFER,
270 (void *) auxAddr);
271 mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::AUXLEVEL, &f0);
272 mixer->setParameter(name, AudioMixer::RAMP_VOLUME, AudioMixer::AUXLEVEL, &f);
273 }
274 mixer->enable(name);
275 }
276
277 // pump the mixer to process data.
278 size_t i;
279 for (i = 0; i < outputFrames - mixerFrameCount; i += mixerFrameCount) {
280 for (size_t j = 0; j < Names.size(); ++j) {
281 mixer->setParameter(Names[j], AudioMixer::TRACK, AudioMixer::MAIN_BUFFER,
282 (char *) outputAddr + i * outputFrameSize);
283 if (auxFilename) {
284 mixer->setParameter(Names[j], AudioMixer::TRACK, AudioMixer::AUX_BUFFER,
285 (char *) auxAddr + i * auxFrameSize);
286 }
287 }
288 mixer->process(AudioBufferProvider::kInvalidPTS);
289 }
290 outputFrames = i; // reset output frames to the data actually produced.
291
292 // write to files
293 writeFile(outputFilename, outputAddr,
294 outputSampleRate, outputChannels, outputFrames, useMixerFloat);
295 if (auxFilename) {
296 // Aux buffer is always in q4_27 format for now.
297 // memcpy_to_i16_from_q4_27(), but with stereo frame count (not sample count)
298 ditherAndClamp((int32_t*)auxAddr, (int32_t*)auxAddr, outputFrames >> 1);
299 writeFile(auxFilename, auxAddr, outputSampleRate, 1, outputFrames, false);
300 }
301
302 delete mixer;
303 free(outputAddr);
304 free(auxAddr);
305 return EXIT_SUCCESS;
306}