1 /*
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 
36 using namespace android;
37 
usage(const char * name)38 static void usage(const char* name) {
39     fprintf(stderr, "Usage: %s [-f] [-m] [-c channels]"
40                     " [-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 by default\n");
43     fprintf(stderr, "    -m    enable floating point mixer output\n");
44     fprintf(stderr, "    -c    number of mixer output channels\n");
45     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:[(i|f),]<channels>,<frequency>,<samplerate>'\n");
51     fprintf(stderr, "                     'chirp:[(i|f),]<channels>,<samplerate>'\n");
52 }
53 
writeFile(const char * filename,const void * buffer,uint32_t sampleRate,uint32_t channels,size_t frames,bool isBufferFloat)54 static 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);
65     printf("saving file:%s  channels:%u  samplerate:%u  frames:%zu\n",
66             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 
parseFormat(const char * s,bool * useFloat)81 const 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 
main(int argc,char * argv[])93 int 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;
103     std::vector<int32_t> names;
104     std::vector<SignalProvider> providers;
105     std::vector<audio_format_t> formats;
106 
107     for (int ch; (ch = getopt(argc, argv, "fmc:s:o:a:P:")) != -1;) {
108         switch (ch) {
109         case 'f':
110             useInputFloat = true;
111             break;
112         case 'm':
113             useMixerFloat = true;
114             break;
115         case 'c':
116             outputChannels = atoi(optarg);
117             break;
118         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     }
146     if ((unsigned)argc > AudioMixer::MAX_NUM_TRACKS) {
147         fprintf(stderr, "too many tracks: %d > %u", argc, AudioMixer::MAX_NUM_TRACKS);
148         return EXIT_FAILURE;
149     }
150 
151     size_t outputFrames = 0;
152 
153     // create providers for each track
154     names.resize(argc);
155     providers.resize(argc);
156     formats.resize(argc);
157     for (int i = 0; i < argc; ++i) {
158         static const char chirp[] = "chirp:";
159         static const char sine[] = "sine:";
160         static const double kSeconds = 1;
161         bool useFloat = useInputFloat;
162 
163         if (!strncmp(argv[i], chirp, strlen(chirp))) {
164             std::vector<int> v;
165             const char *s = parseFormat(argv[i] + strlen(chirp), &useFloat);
166 
167             parseCSV(s, v);
168             if (v.size() == 2) {
169                 printf("creating chirp(%d %d)\n", v[0], v[1]);
170                 if (useFloat) {
171                     providers[i].setChirp<float>(v[0], 0, v[1]/2, v[1], kSeconds);
172                     formats[i] = AUDIO_FORMAT_PCM_FLOAT;
173                 } else {
174                     providers[i].setChirp<int16_t>(v[0], 0, v[1]/2, v[1], kSeconds);
175                     formats[i] = AUDIO_FORMAT_PCM_16_BIT;
176                 }
177                 providers[i].setIncr(Pvalues);
178             } else {
179                 fprintf(stderr, "malformed input '%s'\n", argv[i]);
180             }
181         } else if (!strncmp(argv[i], sine, strlen(sine))) {
182             std::vector<int> v;
183             const char *s = parseFormat(argv[i] + strlen(sine), &useFloat);
184 
185             parseCSV(s, v);
186             if (v.size() == 3) {
187                 printf("creating sine(%d %d %d)\n", v[0], v[1], v[2]);
188                 if (useFloat) {
189                     providers[i].setSine<float>(v[0], v[1], v[2], kSeconds);
190                     formats[i] = AUDIO_FORMAT_PCM_FLOAT;
191                 } else {
192                     providers[i].setSine<int16_t>(v[0], v[1], v[2], kSeconds);
193                     formats[i] = AUDIO_FORMAT_PCM_16_BIT;
194                 }
195                 providers[i].setIncr(Pvalues);
196             } else {
197                 fprintf(stderr, "malformed input '%s'\n", argv[i]);
198             }
199         } else {
200             printf("creating filename(%s)\n", argv[i]);
201             if (useInputFloat) {
202                 providers[i].setFile<float>(argv[i]);
203                 formats[i] = AUDIO_FORMAT_PCM_FLOAT;
204             } else {
205                 providers[i].setFile<short>(argv[i]);
206                 formats[i] = AUDIO_FORMAT_PCM_16_BIT;
207             }
208             providers[i].setIncr(Pvalues);
209         }
210         // calculate the number of output frames
211         size_t nframes = (int64_t) providers[i].getNumFrames() * outputSampleRate
212                 / providers[i].getSampleRate();
213         if (i == 0 || outputFrames > nframes) { // choose minimum for outputFrames
214             outputFrames = nframes;
215         }
216     }
217 
218     // create the output buffer.
219     const size_t outputFrameSize = outputChannels
220             * (useMixerFloat ? sizeof(float) : sizeof(int16_t));
221     const size_t outputSize = outputFrames * outputFrameSize;
222     const audio_channel_mask_t outputChannelMask =
223             audio_channel_out_mask_from_count(outputChannels);
224     void *outputAddr = NULL;
225     (void) posix_memalign(&outputAddr, 32, outputSize);
226     memset(outputAddr, 0, outputSize);
227 
228     // create the aux buffer, if needed.
229     const size_t auxFrameSize = sizeof(int32_t); // Q4.27 always
230     const size_t auxSize = outputFrames * auxFrameSize;
231     void *auxAddr = NULL;
232     if (auxFilename) {
233         (void) posix_memalign(&auxAddr, 32, auxSize);
234         memset(auxAddr, 0, auxSize);
235     }
236 
237     // create the mixer.
238     const size_t mixerFrameCount = 320; // typical numbers may range from 240 or 960
239     AudioMixer *mixer = new AudioMixer(mixerFrameCount, outputSampleRate);
240     audio_format_t mixerFormat = useMixerFloat
241             ? AUDIO_FORMAT_PCM_FLOAT : AUDIO_FORMAT_PCM_16_BIT;
242     float f = AudioMixer::UNITY_GAIN_FLOAT / providers.size(); // normalize volume by # tracks
243     static float f0; // zero
244 
245     // set up the tracks.
246     for (size_t i = 0; i < providers.size(); ++i) {
247         //printf("track %d out of %d\n", i, providers.size());
248         uint32_t channelMask = audio_channel_out_mask_from_count(providers[i].getNumChannels());
249         int32_t name = mixer->getTrackName(channelMask,
250                 formats[i], AUDIO_SESSION_OUTPUT_MIX);
251         ALOG_ASSERT(name >= 0);
252         names[i] = name;
253         mixer->setBufferProvider(name, &providers[i]);
254         mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::MAIN_BUFFER,
255                 (void *)outputAddr);
256         mixer->setParameter(
257                 name,
258                 AudioMixer::TRACK,
259                 AudioMixer::MIXER_FORMAT,
260                 (void *)(uintptr_t)mixerFormat);
261         mixer->setParameter(
262                 name,
263                 AudioMixer::TRACK,
264                 AudioMixer::FORMAT,
265                 (void *)(uintptr_t)formats[i]);
266         mixer->setParameter(
267                 name,
268                 AudioMixer::TRACK,
269                 AudioMixer::MIXER_CHANNEL_MASK,
270                 (void *)(uintptr_t)outputChannelMask);
271         mixer->setParameter(
272                 name,
273                 AudioMixer::TRACK,
274                 AudioMixer::CHANNEL_MASK,
275                 (void *)(uintptr_t)channelMask);
276         mixer->setParameter(
277                 name,
278                 AudioMixer::RESAMPLE,
279                 AudioMixer::SAMPLE_RATE,
280                 (void *)(uintptr_t)providers[i].getSampleRate());
281         if (useRamp) {
282             mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME0, &f0);
283             mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME1, &f0);
284             mixer->setParameter(name, AudioMixer::RAMP_VOLUME, AudioMixer::VOLUME0, &f);
285             mixer->setParameter(name, AudioMixer::RAMP_VOLUME, AudioMixer::VOLUME1, &f);
286         } else {
287             mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME0, &f);
288             mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME1, &f);
289         }
290         if (auxFilename) {
291             mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::AUX_BUFFER,
292                     (void *) auxAddr);
293             mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::AUXLEVEL, &f0);
294             mixer->setParameter(name, AudioMixer::RAMP_VOLUME, AudioMixer::AUXLEVEL, &f);
295         }
296         mixer->enable(name);
297     }
298 
299     // pump the mixer to process data.
300     size_t i;
301     for (i = 0; i < outputFrames - mixerFrameCount; i += mixerFrameCount) {
302         for (size_t j = 0; j < names.size(); ++j) {
303             mixer->setParameter(names[j], AudioMixer::TRACK, AudioMixer::MAIN_BUFFER,
304                     (char *) outputAddr + i * outputFrameSize);
305             if (auxFilename) {
306                 mixer->setParameter(names[j], AudioMixer::TRACK, AudioMixer::AUX_BUFFER,
307                         (char *) auxAddr + i * auxFrameSize);
308             }
309         }
310         mixer->process();
311     }
312     outputFrames = i; // reset output frames to the data actually produced.
313 
314     // write to files
315     writeFile(outputFilename, outputAddr,
316             outputSampleRate, outputChannels, outputFrames, useMixerFloat);
317     if (auxFilename) {
318         // Aux buffer is always in q4_27 format for now.
319         // memcpy_to_i16_from_q4_27(), but with stereo frame count (not sample count)
320         ditherAndClamp((int32_t*)auxAddr, (int32_t*)auxAddr, outputFrames >> 1);
321         writeFile(auxFilename, auxAddr, outputSampleRate, 1, outputFrames, false);
322     }
323 
324     delete mixer;
325     free(outputAddr);
326     free(auxAddr);
327     return EXIT_SUCCESS;
328 }
329