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 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 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 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 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