Andy Hung | e4fc423 | 2014-06-17 15:10:51 -0700 | [diff] [blame] | 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> |
Andy Hung | 068561c | 2017-01-03 17:09:32 -0800 | [diff] [blame] | 24 | #include <media/AudioMixer.h> |
Andy Hung | e4fc423 | 2014-06-17 15:10:51 -0700 | [diff] [blame] | 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) { |
Andy Hung | e93b6b7 | 2014-07-17 21:30:53 -0700 | [diff] [blame] | 39 | fprintf(stderr, "Usage: %s [-f] [-m] [-c channels]" |
Andy Hung | e4fc423 | 2014-06-17 15:10:51 -0700 | [diff] [blame] | 40 | " [-s sample-rate] [-o <output-file>] [-a <aux-buffer-file>] [-P csv]" |
| 41 | " (<input-file> | <command>)+\n", name); |
Andy Hung | 7f47549 | 2014-08-25 16:36:37 -0700 | [diff] [blame] | 42 | fprintf(stderr, " -f enable floating point input track by default\n"); |
Andy Hung | e4fc423 | 2014-06-17 15:10:51 -0700 | [diff] [blame] | 43 | fprintf(stderr, " -m enable floating point mixer output\n"); |
Andy Hung | e93b6b7 | 2014-07-17 21:30:53 -0700 | [diff] [blame] | 44 | fprintf(stderr, " -c number of mixer output channels\n"); |
Andy Hung | e4fc423 | 2014-06-17 15:10:51 -0700 | [diff] [blame] | 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"); |
Andy Hung | 7f47549 | 2014-08-25 16:36:37 -0700 | [diff] [blame] | 50 | fprintf(stderr, " <command> can be 'sine:[(i|f),]<channels>,<frequency>,<samplerate>'\n"); |
| 51 | fprintf(stderr, " 'chirp:[(i|f),]<channels>,<samplerate>'\n"); |
Andy Hung | e4fc423 | 2014-06-17 15:10:51 -0700 | [diff] [blame] | 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); |
Glenn Kasten | a4daf0b | 2014-07-28 16:34:45 -0700 | [diff] [blame] | 65 | printf("saving file:%s channels:%u samplerate:%u frames:%zu\n", |
Andy Hung | e4fc423 | 2014-06-17 15:10:51 -0700 | [diff] [blame] | 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 | |
Andy Hung | 7f47549 | 2014-08-25 16:36:37 -0700 | [diff] [blame] | 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 | |
Andy Hung | e4fc423 | 2014-06-17 15:10:51 -0700 | [diff] [blame] | 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; |
Andy Hung | 7f47549 | 2014-08-25 16:36:37 -0700 | [diff] [blame] | 103 | std::vector<int32_t> names; |
| 104 | std::vector<SignalProvider> providers; |
| 105 | std::vector<audio_format_t> formats; |
Andy Hung | e4fc423 | 2014-06-17 15:10:51 -0700 | [diff] [blame] | 106 | |
Andy Hung | e93b6b7 | 2014-07-17 21:30:53 -0700 | [diff] [blame] | 107 | for (int ch; (ch = getopt(argc, argv, "fmc:s:o:a:P:")) != -1;) { |
Andy Hung | e4fc423 | 2014-06-17 15:10:51 -0700 | [diff] [blame] | 108 | switch (ch) { |
| 109 | case 'f': |
| 110 | useInputFloat = true; |
| 111 | break; |
| 112 | case 'm': |
| 113 | useMixerFloat = true; |
| 114 | break; |
Andy Hung | e93b6b7 | 2014-07-17 21:30:53 -0700 | [diff] [blame] | 115 | case 'c': |
| 116 | outputChannels = atoi(optarg); |
| 117 | break; |
Andy Hung | e4fc423 | 2014-06-17 15:10:51 -0700 | [diff] [blame] | 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 | } |
Andy Hung | e4fc423 | 2014-06-17 15:10:51 -0700 | [diff] [blame] | 146 | |
| 147 | size_t outputFrames = 0; |
| 148 | |
| 149 | // create providers for each track |
Andy Hung | 7f47549 | 2014-08-25 16:36:37 -0700 | [diff] [blame] | 150 | names.resize(argc); |
| 151 | providers.resize(argc); |
| 152 | formats.resize(argc); |
Andy Hung | e4fc423 | 2014-06-17 15:10:51 -0700 | [diff] [blame] | 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; |
Andy Hung | 7f47549 | 2014-08-25 16:36:37 -0700 | [diff] [blame] | 157 | bool useFloat = useInputFloat; |
Andy Hung | e4fc423 | 2014-06-17 15:10:51 -0700 | [diff] [blame] | 158 | |
| 159 | if (!strncmp(argv[i], chirp, strlen(chirp))) { |
| 160 | std::vector<int> v; |
Andy Hung | 7f47549 | 2014-08-25 16:36:37 -0700 | [diff] [blame] | 161 | const char *s = parseFormat(argv[i] + strlen(chirp), &useFloat); |
Andy Hung | e4fc423 | 2014-06-17 15:10:51 -0700 | [diff] [blame] | 162 | |
Andy Hung | 7f47549 | 2014-08-25 16:36:37 -0700 | [diff] [blame] | 163 | parseCSV(s, v); |
Andy Hung | e4fc423 | 2014-06-17 15:10:51 -0700 | [diff] [blame] | 164 | if (v.size() == 2) { |
| 165 | printf("creating chirp(%d %d)\n", v[0], v[1]); |
Andy Hung | 7f47549 | 2014-08-25 16:36:37 -0700 | [diff] [blame] | 166 | if (useFloat) { |
| 167 | providers[i].setChirp<float>(v[0], 0, v[1]/2, v[1], kSeconds); |
| 168 | formats[i] = AUDIO_FORMAT_PCM_FLOAT; |
Andy Hung | e4fc423 | 2014-06-17 15:10:51 -0700 | [diff] [blame] | 169 | } else { |
Andy Hung | 7f47549 | 2014-08-25 16:36:37 -0700 | [diff] [blame] | 170 | providers[i].setChirp<int16_t>(v[0], 0, v[1]/2, v[1], kSeconds); |
| 171 | formats[i] = AUDIO_FORMAT_PCM_16_BIT; |
Andy Hung | e4fc423 | 2014-06-17 15:10:51 -0700 | [diff] [blame] | 172 | } |
Andy Hung | 7f47549 | 2014-08-25 16:36:37 -0700 | [diff] [blame] | 173 | providers[i].setIncr(Pvalues); |
Andy Hung | e4fc423 | 2014-06-17 15:10:51 -0700 | [diff] [blame] | 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; |
Andy Hung | 7f47549 | 2014-08-25 16:36:37 -0700 | [diff] [blame] | 179 | const char *s = parseFormat(argv[i] + strlen(sine), &useFloat); |
Andy Hung | e4fc423 | 2014-06-17 15:10:51 -0700 | [diff] [blame] | 180 | |
Andy Hung | 7f47549 | 2014-08-25 16:36:37 -0700 | [diff] [blame] | 181 | parseCSV(s, v); |
Andy Hung | e4fc423 | 2014-06-17 15:10:51 -0700 | [diff] [blame] | 182 | if (v.size() == 3) { |
Andy Hung | e93b6b7 | 2014-07-17 21:30:53 -0700 | [diff] [blame] | 183 | printf("creating sine(%d %d %d)\n", v[0], v[1], v[2]); |
Andy Hung | 7f47549 | 2014-08-25 16:36:37 -0700 | [diff] [blame] | 184 | if (useFloat) { |
| 185 | providers[i].setSine<float>(v[0], v[1], v[2], kSeconds); |
| 186 | formats[i] = AUDIO_FORMAT_PCM_FLOAT; |
Andy Hung | e4fc423 | 2014-06-17 15:10:51 -0700 | [diff] [blame] | 187 | } else { |
Andy Hung | 7f47549 | 2014-08-25 16:36:37 -0700 | [diff] [blame] | 188 | providers[i].setSine<int16_t>(v[0], v[1], v[2], kSeconds); |
| 189 | formats[i] = AUDIO_FORMAT_PCM_16_BIT; |
Andy Hung | e4fc423 | 2014-06-17 15:10:51 -0700 | [diff] [blame] | 190 | } |
Andy Hung | 7f47549 | 2014-08-25 16:36:37 -0700 | [diff] [blame] | 191 | providers[i].setIncr(Pvalues); |
Andy Hung | e4fc423 | 2014-06-17 15:10:51 -0700 | [diff] [blame] | 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) { |
Andy Hung | 7f47549 | 2014-08-25 16:36:37 -0700 | [diff] [blame] | 198 | providers[i].setFile<float>(argv[i]); |
| 199 | formats[i] = AUDIO_FORMAT_PCM_FLOAT; |
Andy Hung | e4fc423 | 2014-06-17 15:10:51 -0700 | [diff] [blame] | 200 | } else { |
Andy Hung | 7f47549 | 2014-08-25 16:36:37 -0700 | [diff] [blame] | 201 | providers[i].setFile<short>(argv[i]); |
| 202 | formats[i] = AUDIO_FORMAT_PCM_16_BIT; |
Andy Hung | e4fc423 | 2014-06-17 15:10:51 -0700 | [diff] [blame] | 203 | } |
Andy Hung | 7f47549 | 2014-08-25 16:36:37 -0700 | [diff] [blame] | 204 | providers[i].setIncr(Pvalues); |
Andy Hung | e4fc423 | 2014-06-17 15:10:51 -0700 | [diff] [blame] | 205 | } |
| 206 | // calculate the number of output frames |
Andy Hung | 7f47549 | 2014-08-25 16:36:37 -0700 | [diff] [blame] | 207 | size_t nframes = (int64_t) providers[i].getNumFrames() * outputSampleRate |
| 208 | / providers[i].getSampleRate(); |
Andy Hung | e4fc423 | 2014-06-17 15:10:51 -0700 | [diff] [blame] | 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; |
Andy Hung | e93b6b7 | 2014-07-17 21:30:53 -0700 | [diff] [blame] | 218 | const audio_channel_mask_t outputChannelMask = |
| 219 | audio_channel_out_mask_from_count(outputChannels); |
Andy Hung | e4fc423 | 2014-06-17 15:10:51 -0700 | [diff] [blame] | 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); |
Andy Hung | e4fc423 | 2014-06-17 15:10:51 -0700 | [diff] [blame] | 236 | audio_format_t mixerFormat = useMixerFloat |
| 237 | ? AUDIO_FORMAT_PCM_FLOAT : AUDIO_FORMAT_PCM_16_BIT; |
Andy Hung | 7f47549 | 2014-08-25 16:36:37 -0700 | [diff] [blame] | 238 | float f = AudioMixer::UNITY_GAIN_FLOAT / providers.size(); // normalize volume by # tracks |
Andy Hung | e4fc423 | 2014-06-17 15:10:51 -0700 | [diff] [blame] | 239 | static float f0; // zero |
| 240 | |
| 241 | // set up the tracks. |
Andy Hung | 7f47549 | 2014-08-25 16:36:37 -0700 | [diff] [blame] | 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()); |
Andy Hung | 1bc088a | 2018-02-09 15:57:31 -0800 | [diff] [blame] | 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); |
Andy Hung | 7f47549 | 2014-08-25 16:36:37 -0700 | [diff] [blame] | 249 | names[i] = name; |
| 250 | mixer->setBufferProvider(name, &providers[i]); |
Andy Hung | e4fc423 | 2014-06-17 15:10:51 -0700 | [diff] [blame] | 251 | mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::MAIN_BUFFER, |
Andy Hung | e93b6b7 | 2014-07-17 21:30:53 -0700 | [diff] [blame] | 252 | (void *)outputAddr); |
Andy Hung | e4fc423 | 2014-06-17 15:10:51 -0700 | [diff] [blame] | 253 | mixer->setParameter( |
| 254 | name, |
| 255 | AudioMixer::TRACK, |
Andy Hung | e93b6b7 | 2014-07-17 21:30:53 -0700 | [diff] [blame] | 256 | AudioMixer::MIXER_FORMAT, |
| 257 | (void *)(uintptr_t)mixerFormat); |
| 258 | mixer->setParameter( |
| 259 | name, |
| 260 | AudioMixer::TRACK, |
| 261 | AudioMixer::FORMAT, |
Andy Hung | 7f47549 | 2014-08-25 16:36:37 -0700 | [diff] [blame] | 262 | (void *)(uintptr_t)formats[i]); |
Andy Hung | e4fc423 | 2014-06-17 15:10:51 -0700 | [diff] [blame] | 263 | mixer->setParameter( |
| 264 | name, |
Andy Hung | e93b6b7 | 2014-07-17 21:30:53 -0700 | [diff] [blame] | 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, |
Andy Hung | e4fc423 | 2014-06-17 15:10:51 -0700 | [diff] [blame] | 275 | AudioMixer::RESAMPLE, |
| 276 | AudioMixer::SAMPLE_RATE, |
Andy Hung | 7f47549 | 2014-08-25 16:36:37 -0700 | [diff] [blame] | 277 | (void *)(uintptr_t)providers[i].getSampleRate()); |
Andy Hung | e4fc423 | 2014-06-17 15:10:51 -0700 | [diff] [blame] | 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) { |
Andy Hung | 7f47549 | 2014-08-25 16:36:37 -0700 | [diff] [blame] | 299 | for (size_t j = 0; j < names.size(); ++j) { |
| 300 | mixer->setParameter(names[j], AudioMixer::TRACK, AudioMixer::MAIN_BUFFER, |
Andy Hung | e4fc423 | 2014-06-17 15:10:51 -0700 | [diff] [blame] | 301 | (char *) outputAddr + i * outputFrameSize); |
| 302 | if (auxFilename) { |
Andy Hung | 7f47549 | 2014-08-25 16:36:37 -0700 | [diff] [blame] | 303 | mixer->setParameter(names[j], AudioMixer::TRACK, AudioMixer::AUX_BUFFER, |
Andy Hung | e4fc423 | 2014-06-17 15:10:51 -0700 | [diff] [blame] | 304 | (char *) auxAddr + i * auxFrameSize); |
| 305 | } |
| 306 | } |
Glenn Kasten | d79072e | 2016-01-06 08:41:20 -0800 | [diff] [blame] | 307 | mixer->process(); |
Andy Hung | e4fc423 | 2014-06-17 15:10:51 -0700 | [diff] [blame] | 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) { |
Andy Hung | 1bc088a | 2018-02-09 15:57:31 -0800 | [diff] [blame] | 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); |
Andy Hung | e4fc423 | 2014-06-17 15:10:51 -0700 | [diff] [blame] | 319 | writeFile(auxFilename, auxAddr, outputSampleRate, 1, outputFrames, false); |
| 320 | } |
| 321 | |
| 322 | delete mixer; |
| 323 | free(outputAddr); |
| 324 | free(auxAddr); |
| 325 | return EXIT_SUCCESS; |
| 326 | } |