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 <media/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) {
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
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);
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
81const 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
93int 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
147    size_t outputFrames = 0;
148
149    // create providers for each track
150    names.resize(argc);
151    providers.resize(argc);
152    formats.resize(argc);
153    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;
157        bool useFloat = useInputFloat;
158
159        if (!strncmp(argv[i], chirp, strlen(chirp))) {
160            std::vector<int> v;
161            const char *s = parseFormat(argv[i] + strlen(chirp), &useFloat);
162
163            parseCSV(s, v);
164            if (v.size() == 2) {
165                printf("creating chirp(%d %d)\n", v[0], v[1]);
166                if (useFloat) {
167                    providers[i].setChirp<float>(v[0], 0, v[1]/2, v[1], kSeconds);
168                    formats[i] = AUDIO_FORMAT_PCM_FLOAT;
169                } else {
170                    providers[i].setChirp<int16_t>(v[0], 0, v[1]/2, v[1], kSeconds);
171                    formats[i] = AUDIO_FORMAT_PCM_16_BIT;
172                }
173                providers[i].setIncr(Pvalues);
174            } 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;
179            const char *s = parseFormat(argv[i] + strlen(sine), &useFloat);
180
181            parseCSV(s, v);
182            if (v.size() == 3) {
183                printf("creating sine(%d %d %d)\n", v[0], v[1], v[2]);
184                if (useFloat) {
185                    providers[i].setSine<float>(v[0], v[1], v[2], kSeconds);
186                    formats[i] = AUDIO_FORMAT_PCM_FLOAT;
187                } else {
188                    providers[i].setSine<int16_t>(v[0], v[1], v[2], kSeconds);
189                    formats[i] = AUDIO_FORMAT_PCM_16_BIT;
190                }
191                providers[i].setIncr(Pvalues);
192            } else {
193                fprintf(stderr, "malformed input '%s'\n", argv[i]);
194            }
195        } else {
196            printf("creating filename(%s)\n", argv[i]);
197            if (useInputFloat) {
198                providers[i].setFile<float>(argv[i]);
199                formats[i] = AUDIO_FORMAT_PCM_FLOAT;
200            } else {
201                providers[i].setFile<short>(argv[i]);
202                formats[i] = AUDIO_FORMAT_PCM_16_BIT;
203            }
204            providers[i].setIncr(Pvalues);
205        }
206        // calculate the number of output frames
207        size_t nframes = (int64_t) providers[i].getNumFrames() * outputSampleRate
208                / providers[i].getSampleRate();
209        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;
218    const audio_channel_mask_t outputChannelMask =
219            audio_channel_out_mask_from_count(outputChannels);
220    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);
236    audio_format_t mixerFormat = useMixerFloat
237            ? AUDIO_FORMAT_PCM_FLOAT : AUDIO_FORMAT_PCM_16_BIT;
238    float f = AudioMixer::UNITY_GAIN_FLOAT / providers.size(); // normalize volume by # tracks
239    static float f0; // zero
240
241    // set up the tracks.
242    for (size_t i = 0; i < providers.size(); ++i) {
243        //printf("track %d out of %d\n", i, providers.size());
244        uint32_t channelMask = audio_channel_out_mask_from_count(providers[i].getNumChannels());
245        const int name = i;
246        const status_t status = mixer->create(
247                name, channelMask, formats[i], AUDIO_SESSION_OUTPUT_MIX);
248        LOG_ALWAYS_FATAL_IF(status != OK);
249        names[i] = name;
250        mixer->setBufferProvider(name, &providers[i]);
251        mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::MAIN_BUFFER,
252                (void *)outputAddr);
253        mixer->setParameter(
254                name,
255                AudioMixer::TRACK,
256                AudioMixer::MIXER_FORMAT,
257                (void *)(uintptr_t)mixerFormat);
258        mixer->setParameter(
259                name,
260                AudioMixer::TRACK,
261                AudioMixer::FORMAT,
262                (void *)(uintptr_t)formats[i]);
263        mixer->setParameter(
264                name,
265                AudioMixer::TRACK,
266                AudioMixer::MIXER_CHANNEL_MASK,
267                (void *)(uintptr_t)outputChannelMask);
268        mixer->setParameter(
269                name,
270                AudioMixer::TRACK,
271                AudioMixer::CHANNEL_MASK,
272                (void *)(uintptr_t)channelMask);
273        mixer->setParameter(
274                name,
275                AudioMixer::RESAMPLE,
276                AudioMixer::SAMPLE_RATE,
277                (void *)(uintptr_t)providers[i].getSampleRate());
278        if (useRamp) {
279            mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME0, &f0);
280            mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME1, &f0);
281            mixer->setParameter(name, AudioMixer::RAMP_VOLUME, AudioMixer::VOLUME0, &f);
282            mixer->setParameter(name, AudioMixer::RAMP_VOLUME, AudioMixer::VOLUME1, &f);
283        } else {
284            mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME0, &f);
285            mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME1, &f);
286        }
287        if (auxFilename) {
288            mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::AUX_BUFFER,
289                    (void *) auxAddr);
290            mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::AUXLEVEL, &f0);
291            mixer->setParameter(name, AudioMixer::RAMP_VOLUME, AudioMixer::AUXLEVEL, &f);
292        }
293        mixer->enable(name);
294    }
295
296    // pump the mixer to process data.
297    size_t i;
298    for (i = 0; i < outputFrames - mixerFrameCount; i += mixerFrameCount) {
299        for (size_t j = 0; j < names.size(); ++j) {
300            mixer->setParameter(names[j], AudioMixer::TRACK, AudioMixer::MAIN_BUFFER,
301                    (char *) outputAddr + i * outputFrameSize);
302            if (auxFilename) {
303                mixer->setParameter(names[j], AudioMixer::TRACK, AudioMixer::AUX_BUFFER,
304                        (char *) auxAddr + i * auxFrameSize);
305            }
306        }
307        mixer->process();
308    }
309    outputFrames = i; // reset output frames to the data actually produced.
310
311    // write to files
312    writeFile(outputFilename, outputAddr,
313            outputSampleRate, outputChannels, outputFrames, useMixerFloat);
314    if (auxFilename) {
315        // Aux buffer is always in q4_27 format for O and earlier.
316        // memcpy_to_i16_from_q4_27((int16_t*)auxAddr, (const int32_t*)auxAddr, outputFrames);
317        // Aux buffer is always in float format for P.
318        memcpy_to_i16_from_float((int16_t*)auxAddr, (const float*)auxAddr, outputFrames);
319        writeFile(auxFilename, auxAddr, outputSampleRate, 1, outputFrames, false);
320    }
321
322    delete mixer;
323    free(outputAddr);
324    free(auxAddr);
325    return EXIT_SUCCESS;
326}
327