blob: 1bbb863241b348dad7f51a788ca4eb9c9fb97dca [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>
Andy Hung068561c2017-01-03 17:09:32 -080024#include <media/AudioMixer.h>
Andy Hunge4fc4232014-06-17 15:10:51 -070025#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);
Andy Hung7f475492014-08-25 16:36:37 -070042 fprintf(stderr, " -f enable floating point input track by default\n");
Andy Hunge4fc4232014-06-17 15:10:51 -070043 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");
Andy Hung7f475492014-08-25 16:36:37 -070050 fprintf(stderr, " <command> can be 'sine:[(i|f),]<channels>,<frequency>,<samplerate>'\n");
51 fprintf(stderr, " 'chirp:[(i|f),]<channels>,<samplerate>'\n");
Andy Hunge4fc4232014-06-17 15:10:51 -070052}
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
Andy Hung7f475492014-08-25 16:36:37 -070081const char *parseFormat(const char *s, bool *useFloat) {
82 if (!strncmp(s, "f,", 2)) {
83 *useFloat = true;
84 return s + 2;
85 }
86 if (!strncmp(s, "i,", 2)) {
87 *useFloat = false;
88 return s + 2;
89 }
90 return s;
91}
92
Andy Hunge4fc4232014-06-17 15:10:51 -070093int main(int argc, char* argv[]) {
94 const char* const progname = argv[0];
95 bool useInputFloat = false;
96 bool useMixerFloat = false;
97 bool useRamp = true;
98 uint32_t outputSampleRate = 48000;
99 uint32_t outputChannels = 2; // stereo for now
100 std::vector<int> Pvalues;
101 const char* outputFilename = NULL;
102 const char* auxFilename = NULL;
Andy Hung7f475492014-08-25 16:36:37 -0700103 std::vector<int32_t> names;
104 std::vector<SignalProvider> providers;
105 std::vector<audio_format_t> formats;
Andy Hunge4fc4232014-06-17 15:10:51 -0700106
Andy Hunge93b6b72014-07-17 21:30:53 -0700107 for (int ch; (ch = getopt(argc, argv, "fmc:s:o:a:P:")) != -1;) {
Andy Hunge4fc4232014-06-17 15:10:51 -0700108 switch (ch) {
109 case 'f':
110 useInputFloat = true;
111 break;
112 case 'm':
113 useMixerFloat = true;
114 break;
Andy Hunge93b6b72014-07-17 21:30:53 -0700115 case 'c':
116 outputChannels = atoi(optarg);
117 break;
Andy Hunge4fc4232014-06-17 15:10:51 -0700118 case 's':
119 outputSampleRate = atoi(optarg);
120 break;
121 case 'o':
122 outputFilename = optarg;
123 break;
124 case 'a':
125 auxFilename = optarg;
126 break;
127 case 'P':
128 if (parseCSV(optarg, Pvalues) < 0) {
129 fprintf(stderr, "incorrect syntax for -P option\n");
130 return EXIT_FAILURE;
131 }
132 break;
133 case '?':
134 default:
135 usage(progname);
136 return EXIT_FAILURE;
137 }
138 }
139 argc -= optind;
140 argv += optind;
141
142 if (argc == 0) {
143 usage(progname);
144 return EXIT_FAILURE;
145 }
Andy Hunge4fc4232014-06-17 15:10:51 -0700146
147 size_t outputFrames = 0;
148
149 // create providers for each track
Andy Hung7f475492014-08-25 16:36:37 -0700150 names.resize(argc);
151 providers.resize(argc);
152 formats.resize(argc);
Andy Hunge4fc4232014-06-17 15:10:51 -0700153 for (int i = 0; i < argc; ++i) {
154 static const char chirp[] = "chirp:";
155 static const char sine[] = "sine:";
156 static const double kSeconds = 1;
Andy Hung7f475492014-08-25 16:36:37 -0700157 bool useFloat = useInputFloat;
Andy Hunge4fc4232014-06-17 15:10:51 -0700158
159 if (!strncmp(argv[i], chirp, strlen(chirp))) {
160 std::vector<int> v;
Andy Hung7f475492014-08-25 16:36:37 -0700161 const char *s = parseFormat(argv[i] + strlen(chirp), &useFloat);
Andy Hunge4fc4232014-06-17 15:10:51 -0700162
Andy Hung7f475492014-08-25 16:36:37 -0700163 parseCSV(s, v);
Andy Hunge4fc4232014-06-17 15:10:51 -0700164 if (v.size() == 2) {
165 printf("creating chirp(%d %d)\n", v[0], v[1]);
Andy Hung7f475492014-08-25 16:36:37 -0700166 if (useFloat) {
167 providers[i].setChirp<float>(v[0], 0, v[1]/2, v[1], kSeconds);
168 formats[i] = AUDIO_FORMAT_PCM_FLOAT;
Andy Hunge4fc4232014-06-17 15:10:51 -0700169 } else {
Andy Hung7f475492014-08-25 16:36:37 -0700170 providers[i].setChirp<int16_t>(v[0], 0, v[1]/2, v[1], kSeconds);
171 formats[i] = AUDIO_FORMAT_PCM_16_BIT;
Andy Hunge4fc4232014-06-17 15:10:51 -0700172 }
Andy Hung7f475492014-08-25 16:36:37 -0700173 providers[i].setIncr(Pvalues);
Andy Hunge4fc4232014-06-17 15:10:51 -0700174 } else {
175 fprintf(stderr, "malformed input '%s'\n", argv[i]);
176 }
177 } else if (!strncmp(argv[i], sine, strlen(sine))) {
178 std::vector<int> v;
Andy Hung7f475492014-08-25 16:36:37 -0700179 const char *s = parseFormat(argv[i] + strlen(sine), &useFloat);
Andy Hunge4fc4232014-06-17 15:10:51 -0700180
Andy Hung7f475492014-08-25 16:36:37 -0700181 parseCSV(s, v);
Andy Hunge4fc4232014-06-17 15:10:51 -0700182 if (v.size() == 3) {
Andy Hunge93b6b72014-07-17 21:30:53 -0700183 printf("creating sine(%d %d %d)\n", v[0], v[1], v[2]);
Andy Hung7f475492014-08-25 16:36:37 -0700184 if (useFloat) {
185 providers[i].setSine<float>(v[0], v[1], v[2], kSeconds);
186 formats[i] = AUDIO_FORMAT_PCM_FLOAT;
Andy Hunge4fc4232014-06-17 15:10:51 -0700187 } else {
Andy Hung7f475492014-08-25 16:36:37 -0700188 providers[i].setSine<int16_t>(v[0], v[1], v[2], kSeconds);
189 formats[i] = AUDIO_FORMAT_PCM_16_BIT;
Andy Hunge4fc4232014-06-17 15:10:51 -0700190 }
Andy Hung7f475492014-08-25 16:36:37 -0700191 providers[i].setIncr(Pvalues);
Andy Hunge4fc4232014-06-17 15:10:51 -0700192 } else {
193 fprintf(stderr, "malformed input '%s'\n", argv[i]);
194 }
195 } else {
196 printf("creating filename(%s)\n", argv[i]);
197 if (useInputFloat) {
Andy Hung7f475492014-08-25 16:36:37 -0700198 providers[i].setFile<float>(argv[i]);
199 formats[i] = AUDIO_FORMAT_PCM_FLOAT;
Andy Hunge4fc4232014-06-17 15:10:51 -0700200 } else {
Andy Hung7f475492014-08-25 16:36:37 -0700201 providers[i].setFile<short>(argv[i]);
202 formats[i] = AUDIO_FORMAT_PCM_16_BIT;
Andy Hunge4fc4232014-06-17 15:10:51 -0700203 }
Andy Hung7f475492014-08-25 16:36:37 -0700204 providers[i].setIncr(Pvalues);
Andy Hunge4fc4232014-06-17 15:10:51 -0700205 }
206 // calculate the number of output frames
Andy Hung7f475492014-08-25 16:36:37 -0700207 size_t nframes = (int64_t) providers[i].getNumFrames() * outputSampleRate
208 / providers[i].getSampleRate();
Andy Hunge4fc4232014-06-17 15:10:51 -0700209 if (i == 0 || outputFrames > nframes) { // choose minimum for outputFrames
210 outputFrames = nframes;
211 }
212 }
213
214 // create the output buffer.
215 const size_t outputFrameSize = outputChannels
216 * (useMixerFloat ? sizeof(float) : sizeof(int16_t));
217 const size_t outputSize = outputFrames * outputFrameSize;
Andy Hunge93b6b72014-07-17 21:30:53 -0700218 const audio_channel_mask_t outputChannelMask =
219 audio_channel_out_mask_from_count(outputChannels);
Andy Hunge4fc4232014-06-17 15:10:51 -0700220 void *outputAddr = NULL;
221 (void) posix_memalign(&outputAddr, 32, outputSize);
222 memset(outputAddr, 0, outputSize);
223
224 // create the aux buffer, if needed.
225 const size_t auxFrameSize = sizeof(int32_t); // Q4.27 always
226 const size_t auxSize = outputFrames * auxFrameSize;
227 void *auxAddr = NULL;
228 if (auxFilename) {
229 (void) posix_memalign(&auxAddr, 32, auxSize);
230 memset(auxAddr, 0, auxSize);
231 }
232
233 // create the mixer.
234 const size_t mixerFrameCount = 320; // typical numbers may range from 240 or 960
235 AudioMixer *mixer = new AudioMixer(mixerFrameCount, outputSampleRate);
Andy Hunge4fc4232014-06-17 15:10:51 -0700236 audio_format_t mixerFormat = useMixerFloat
237 ? AUDIO_FORMAT_PCM_FLOAT : AUDIO_FORMAT_PCM_16_BIT;
Andy Hung7f475492014-08-25 16:36:37 -0700238 float f = AudioMixer::UNITY_GAIN_FLOAT / providers.size(); // normalize volume by # tracks
Andy Hunge4fc4232014-06-17 15:10:51 -0700239 static float f0; // zero
240
241 // set up the tracks.
Andy Hung7f475492014-08-25 16:36:37 -0700242 for (size_t i = 0; i < providers.size(); ++i) {
243 //printf("track %d out of %d\n", i, providers.size());
Mikhail Naganove3b59ac2020-10-01 15:08:13 -0700244 audio_channel_mask_t channelMask =
245 audio_channel_out_mask_from_count(providers[i].getNumChannels());
Andy Hung1bc088a2018-02-09 15:57:31 -0800246 const int name = i;
247 const status_t status = mixer->create(
248 name, channelMask, formats[i], AUDIO_SESSION_OUTPUT_MIX);
249 LOG_ALWAYS_FATAL_IF(status != OK);
Andy Hung7f475492014-08-25 16:36:37 -0700250 names[i] = name;
251 mixer->setBufferProvider(name, &providers[i]);
Andy Hunge4fc4232014-06-17 15:10:51 -0700252 mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::MAIN_BUFFER,
Andy Hunge93b6b72014-07-17 21:30:53 -0700253 (void *)outputAddr);
Andy Hunge4fc4232014-06-17 15:10:51 -0700254 mixer->setParameter(
255 name,
256 AudioMixer::TRACK,
Andy Hunge93b6b72014-07-17 21:30:53 -0700257 AudioMixer::MIXER_FORMAT,
258 (void *)(uintptr_t)mixerFormat);
259 mixer->setParameter(
260 name,
261 AudioMixer::TRACK,
262 AudioMixer::FORMAT,
Andy Hung7f475492014-08-25 16:36:37 -0700263 (void *)(uintptr_t)formats[i]);
Andy Hunge4fc4232014-06-17 15:10:51 -0700264 mixer->setParameter(
265 name,
Andy Hunge93b6b72014-07-17 21:30:53 -0700266 AudioMixer::TRACK,
267 AudioMixer::MIXER_CHANNEL_MASK,
268 (void *)(uintptr_t)outputChannelMask);
269 mixer->setParameter(
270 name,
271 AudioMixer::TRACK,
272 AudioMixer::CHANNEL_MASK,
273 (void *)(uintptr_t)channelMask);
274 mixer->setParameter(
275 name,
Andy Hunge4fc4232014-06-17 15:10:51 -0700276 AudioMixer::RESAMPLE,
277 AudioMixer::SAMPLE_RATE,
Andy Hung7f475492014-08-25 16:36:37 -0700278 (void *)(uintptr_t)providers[i].getSampleRate());
Andy Hunge4fc4232014-06-17 15:10:51 -0700279 if (useRamp) {
280 mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME0, &f0);
281 mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME1, &f0);
282 mixer->setParameter(name, AudioMixer::RAMP_VOLUME, AudioMixer::VOLUME0, &f);
283 mixer->setParameter(name, AudioMixer::RAMP_VOLUME, AudioMixer::VOLUME1, &f);
284 } else {
285 mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME0, &f);
286 mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME1, &f);
287 }
288 if (auxFilename) {
289 mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::AUX_BUFFER,
290 (void *) auxAddr);
291 mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::AUXLEVEL, &f0);
292 mixer->setParameter(name, AudioMixer::RAMP_VOLUME, AudioMixer::AUXLEVEL, &f);
293 }
294 mixer->enable(name);
295 }
296
297 // pump the mixer to process data.
298 size_t i;
299 for (i = 0; i < outputFrames - mixerFrameCount; i += mixerFrameCount) {
Andy Hung7f475492014-08-25 16:36:37 -0700300 for (size_t j = 0; j < names.size(); ++j) {
301 mixer->setParameter(names[j], AudioMixer::TRACK, AudioMixer::MAIN_BUFFER,
Andy Hunge4fc4232014-06-17 15:10:51 -0700302 (char *) outputAddr + i * outputFrameSize);
303 if (auxFilename) {
Andy Hung7f475492014-08-25 16:36:37 -0700304 mixer->setParameter(names[j], AudioMixer::TRACK, AudioMixer::AUX_BUFFER,
Andy Hunge4fc4232014-06-17 15:10:51 -0700305 (char *) auxAddr + i * auxFrameSize);
306 }
307 }
Glenn Kastend79072e2016-01-06 08:41:20 -0800308 mixer->process();
Andy Hunge4fc4232014-06-17 15:10:51 -0700309 }
310 outputFrames = i; // reset output frames to the data actually produced.
311
312 // write to files
313 writeFile(outputFilename, outputAddr,
314 outputSampleRate, outputChannels, outputFrames, useMixerFloat);
315 if (auxFilename) {
Andy Hung1bc088a2018-02-09 15:57:31 -0800316 // Aux buffer is always in q4_27 format for O and earlier.
317 // memcpy_to_i16_from_q4_27((int16_t*)auxAddr, (const int32_t*)auxAddr, outputFrames);
318 // Aux buffer is always in float format for P.
319 memcpy_to_i16_from_float((int16_t*)auxAddr, (const float*)auxAddr, outputFrames);
Andy Hunge4fc4232014-06-17 15:10:51 -0700320 writeFile(auxFilename, auxAddr, outputSampleRate, 1, outputFrames, false);
321 }
322
323 delete mixer;
324 free(outputAddr);
325 free(auxAddr);
326 return EXIT_SUCCESS;
327}