Home | History | Annotate | Download | only in tests
      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 
     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\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:<channels>,<frequency>,<samplerate>'\n");
     51     fprintf(stderr, "                     'chirp:<channels>,<samplerate>'\n");
     52 }
     53 
     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 
     81 int 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 
     94     for (int ch; (ch = getopt(argc, argv, "fmc:s:o:a:P:")) != -1;) {
     95         switch (ch) {
     96         case 'f':
     97             useInputFloat = true;
     98             break;
     99         case 'm':
    100             useMixerFloat = true;
    101             break;
    102         case 'c':
    103             outputChannels = atoi(optarg);
    104             break;
    105         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) {
    167                 printf("creating sine(%d %d %d)\n", v[0], v[1], v[2]);
    168                 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;
    198     const audio_channel_mask_t outputChannelMask =
    199             audio_channel_out_mask_from_count(outputChannels);
    200     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,
    233                 (void *)outputAddr);
    234         mixer->setParameter(
    235                 name,
    236                 AudioMixer::TRACK,
    237                 AudioMixer::MIXER_FORMAT,
    238                 (void *)(uintptr_t)mixerFormat);
    239         mixer->setParameter(
    240                 name,
    241                 AudioMixer::TRACK,
    242                 AudioMixer::FORMAT,
    243                 (void *)(uintptr_t)inputFormat);
    244         mixer->setParameter(
    245                 name,
    246                 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,
    256                 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 }
    307