test-mixer.cpp revision e4fc42359cdd9786e521054a3a0491d6bc3a9e1c
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 36using namespace android; 37 38static void usage(const char* name) { 39 fprintf(stderr, "Usage: %s [-f] [-m]" 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, " -s mixer sample-rate\n"); 45 fprintf(stderr, " -o <output-file> WAV file, pcm16 (or float if -m specified)\n"); 46 fprintf(stderr, " -a <aux-buffer-file>\n"); 47 fprintf(stderr, " -P # frames provided per call to resample() in CSV format\n"); 48 fprintf(stderr, " <input-file> is a WAV file\n"); 49 fprintf(stderr, " <command> can be 'sine:<channels>,<frequency>,<samplerate>'\n"); 50 fprintf(stderr, " 'chirp:<channels>,<samplerate>'\n"); 51} 52 53static int writeFile(const char *filename, const void *buffer, 54 uint32_t sampleRate, uint32_t channels, size_t frames, bool isBufferFloat) { 55 if (filename == NULL) { 56 return 0; // ok to pass in NULL filename 57 } 58 // write output to file. 59 SF_INFO info; 60 info.frames = 0; 61 info.samplerate = sampleRate; 62 info.channels = channels; 63 info.format = SF_FORMAT_WAV | (isBufferFloat ? SF_FORMAT_FLOAT : SF_FORMAT_PCM_16); 64 printf("saving file:%s channels:%d samplerate:%d frames:%d\n", 65 filename, info.channels, info.samplerate, frames); 66 SNDFILE *sf = sf_open(filename, SFM_WRITE, &info); 67 if (sf == NULL) { 68 perror(filename); 69 return EXIT_FAILURE; 70 } 71 if (isBufferFloat) { 72 (void) sf_writef_float(sf, (float*)buffer, frames); 73 } else { 74 (void) sf_writef_short(sf, (short*)buffer, frames); 75 } 76 sf_close(sf); 77 return EXIT_SUCCESS; 78} 79 80int main(int argc, char* argv[]) { 81 const char* const progname = argv[0]; 82 bool useInputFloat = false; 83 bool useMixerFloat = false; 84 bool useRamp = true; 85 uint32_t outputSampleRate = 48000; 86 uint32_t outputChannels = 2; // stereo for now 87 std::vector<int> Pvalues; 88 const char* outputFilename = NULL; 89 const char* auxFilename = NULL; 90 std::vector<int32_t> Names; 91 std::vector<SignalProvider> Providers; 92 93 for (int ch; (ch = getopt(argc, argv, "fms:o:a:P:")) != -1;) { 94 switch (ch) { 95 case 'f': 96 useInputFloat = true; 97 break; 98 case 'm': 99 useMixerFloat = true; 100 break; 101 case 's': 102 outputSampleRate = atoi(optarg); 103 break; 104 case 'o': 105 outputFilename = optarg; 106 break; 107 case 'a': 108 auxFilename = optarg; 109 break; 110 case 'P': 111 if (parseCSV(optarg, Pvalues) < 0) { 112 fprintf(stderr, "incorrect syntax for -P option\n"); 113 return EXIT_FAILURE; 114 } 115 break; 116 case '?': 117 default: 118 usage(progname); 119 return EXIT_FAILURE; 120 } 121 } 122 argc -= optind; 123 argv += optind; 124 125 if (argc == 0) { 126 usage(progname); 127 return EXIT_FAILURE; 128 } 129 if ((unsigned)argc > AudioMixer::MAX_NUM_TRACKS) { 130 fprintf(stderr, "too many tracks: %d > %u", argc, AudioMixer::MAX_NUM_TRACKS); 131 return EXIT_FAILURE; 132 } 133 134 size_t outputFrames = 0; 135 136 // create providers for each track 137 Providers.resize(argc); 138 for (int i = 0; i < argc; ++i) { 139 static const char chirp[] = "chirp:"; 140 static const char sine[] = "sine:"; 141 static const double kSeconds = 1; 142 143 if (!strncmp(argv[i], chirp, strlen(chirp))) { 144 std::vector<int> v; 145 146 parseCSV(argv[i] + strlen(chirp), v); 147 if (v.size() == 2) { 148 printf("creating chirp(%d %d)\n", v[0], v[1]); 149 if (useInputFloat) { 150 Providers[i].setChirp<float>(v[0], 0, v[1]/2, v[1], kSeconds); 151 } else { 152 Providers[i].setChirp<int16_t>(v[0], 0, v[1]/2, v[1], kSeconds); 153 } 154 Providers[i].setIncr(Pvalues); 155 } else { 156 fprintf(stderr, "malformed input '%s'\n", argv[i]); 157 } 158 } else if (!strncmp(argv[i], sine, strlen(sine))) { 159 std::vector<int> v; 160 161 parseCSV(argv[i] + strlen(sine), v); 162 if (v.size() == 3) { 163 printf("creating sine(%d %d)\n", v[0], v[1]); 164 if (useInputFloat) { 165 Providers[i].setSine<float>(v[0], v[1], v[2], kSeconds); 166 } else { 167 Providers[i].setSine<int16_t>(v[0], v[1], v[2], kSeconds); 168 } 169 Providers[i].setIncr(Pvalues); 170 } else { 171 fprintf(stderr, "malformed input '%s'\n", argv[i]); 172 } 173 } else { 174 printf("creating filename(%s)\n", argv[i]); 175 if (useInputFloat) { 176 Providers[i].setFile<float>(argv[i]); 177 } else { 178 Providers[i].setFile<short>(argv[i]); 179 } 180 Providers[i].setIncr(Pvalues); 181 } 182 // calculate the number of output frames 183 size_t nframes = (int64_t) Providers[i].getNumFrames() * outputSampleRate 184 / Providers[i].getSampleRate(); 185 if (i == 0 || outputFrames > nframes) { // choose minimum for outputFrames 186 outputFrames = nframes; 187 } 188 } 189 190 // create the output buffer. 191 const size_t outputFrameSize = outputChannels 192 * (useMixerFloat ? sizeof(float) : sizeof(int16_t)); 193 const size_t outputSize = outputFrames * outputFrameSize; 194 void *outputAddr = NULL; 195 (void) posix_memalign(&outputAddr, 32, outputSize); 196 memset(outputAddr, 0, outputSize); 197 198 // create the aux buffer, if needed. 199 const size_t auxFrameSize = sizeof(int32_t); // Q4.27 always 200 const size_t auxSize = outputFrames * auxFrameSize; 201 void *auxAddr = NULL; 202 if (auxFilename) { 203 (void) posix_memalign(&auxAddr, 32, auxSize); 204 memset(auxAddr, 0, auxSize); 205 } 206 207 // create the mixer. 208 const size_t mixerFrameCount = 320; // typical numbers may range from 240 or 960 209 AudioMixer *mixer = new AudioMixer(mixerFrameCount, outputSampleRate); 210 audio_format_t inputFormat = useInputFloat 211 ? AUDIO_FORMAT_PCM_FLOAT : AUDIO_FORMAT_PCM_16_BIT; 212 audio_format_t mixerFormat = useMixerFloat 213 ? AUDIO_FORMAT_PCM_FLOAT : AUDIO_FORMAT_PCM_16_BIT; 214 float f = AudioMixer::UNITY_GAIN_FLOAT / Providers.size(); // normalize volume by # tracks 215 static float f0; // zero 216 217 // set up the tracks. 218 for (size_t i = 0; i < Providers.size(); ++i) { 219 //printf("track %d out of %d\n", i, Providers.size()); 220 uint32_t channelMask = audio_channel_out_mask_from_count(Providers[i].getNumChannels()); 221 int32_t name = mixer->getTrackName(channelMask, 222 inputFormat, AUDIO_SESSION_OUTPUT_MIX); 223 ALOG_ASSERT(name >= 0); 224 Names.push_back(name); 225 mixer->setBufferProvider(name, &Providers[i]); 226 mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::MAIN_BUFFER, 227 (void *) outputAddr); 228 mixer->setParameter( 229 name, 230 AudioMixer::TRACK, 231 AudioMixer::MIXER_FORMAT, (void *)mixerFormat); 232 mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::FORMAT, 233 (void *)(uintptr_t)inputFormat); 234 mixer->setParameter( 235 name, 236 AudioMixer::RESAMPLE, 237 AudioMixer::SAMPLE_RATE, 238 (void *)(uintptr_t)Providers[i].getSampleRate()); 239 if (useRamp) { 240 mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME0, &f0); 241 mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME1, &f0); 242 mixer->setParameter(name, AudioMixer::RAMP_VOLUME, AudioMixer::VOLUME0, &f); 243 mixer->setParameter(name, AudioMixer::RAMP_VOLUME, AudioMixer::VOLUME1, &f); 244 } else { 245 mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME0, &f); 246 mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME1, &f); 247 } 248 if (auxFilename) { 249 mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::AUX_BUFFER, 250 (void *) auxAddr); 251 mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::AUXLEVEL, &f0); 252 mixer->setParameter(name, AudioMixer::RAMP_VOLUME, AudioMixer::AUXLEVEL, &f); 253 } 254 mixer->enable(name); 255 } 256 257 // pump the mixer to process data. 258 size_t i; 259 for (i = 0; i < outputFrames - mixerFrameCount; i += mixerFrameCount) { 260 for (size_t j = 0; j < Names.size(); ++j) { 261 mixer->setParameter(Names[j], AudioMixer::TRACK, AudioMixer::MAIN_BUFFER, 262 (char *) outputAddr + i * outputFrameSize); 263 if (auxFilename) { 264 mixer->setParameter(Names[j], AudioMixer::TRACK, AudioMixer::AUX_BUFFER, 265 (char *) auxAddr + i * auxFrameSize); 266 } 267 } 268 mixer->process(AudioBufferProvider::kInvalidPTS); 269 } 270 outputFrames = i; // reset output frames to the data actually produced. 271 272 // write to files 273 writeFile(outputFilename, outputAddr, 274 outputSampleRate, outputChannels, outputFrames, useMixerFloat); 275 if (auxFilename) { 276 // Aux buffer is always in q4_27 format for now. 277 // memcpy_to_i16_from_q4_27(), but with stereo frame count (not sample count) 278 ditherAndClamp((int32_t*)auxAddr, (int32_t*)auxAddr, outputFrames >> 1); 279 writeFile(auxFilename, auxAddr, outputSampleRate, 1, outputFrames, false); 280 } 281 282 delete mixer; 283 free(outputAddr); 284 free(auxAddr); 285 return EXIT_SUCCESS; 286} 287