blob: 952aa82587785c0001b33e2c9dc9235f9a967a05 [file] [log] [blame]
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "AAudioService"
//#define LOG_NDEBUG 0
#include <utils/Log.h>
#define ATRACE_TAG ATRACE_TAG_AUDIO
#include <cstring>
#include <utils/Trace.h>
#include "AAudioMixer.h"
#ifndef AAUDIO_MIXER_ATRACE_ENABLED
#define AAUDIO_MIXER_ATRACE_ENABLED 1
#endif
using android::WrappingBuffer;
using android::FifoBuffer;
using android::fifo_frames_t;
AAudioMixer::~AAudioMixer() {
delete[] mOutputBuffer;
}
void AAudioMixer::allocate(int32_t samplesPerFrame, int32_t framesPerBurst) {
mSamplesPerFrame = samplesPerFrame;
mFramesPerBurst = framesPerBurst;
int32_t samplesPerBuffer = samplesPerFrame * framesPerBurst;
mOutputBuffer = new float[samplesPerBuffer];
mBufferSizeInBytes = samplesPerBuffer * sizeof(float);
}
void AAudioMixer::clear() {
memset(mOutputBuffer, 0, mBufferSizeInBytes);
}
bool AAudioMixer::mix(int trackIndex, FifoBuffer *fifo, float volume) {
WrappingBuffer wrappingBuffer;
float *destination = mOutputBuffer;
fifo_frames_t framesLeft = mFramesPerBurst;
#if AAUDIO_MIXER_ATRACE_ENABLED
ATRACE_BEGIN("aaMix");
#endif /* AAUDIO_MIXER_ATRACE_ENABLED */
// Gather the data from the client. May be in two parts.
fifo_frames_t fullFrames = fifo->getFullDataAvailable(&wrappingBuffer);
#if AAUDIO_MIXER_ATRACE_ENABLED
if (ATRACE_ENABLED()) {
char rdyText[] = "aaMixRdy#";
char letter = 'A' + (trackIndex % 26);
rdyText[sizeof(rdyText) - 2] = letter;
ATRACE_INT(rdyText, fullFrames);
}
#else /* MIXER_ATRACE_ENABLED */
(void) trackIndex;
(void) fullFrames;
#endif /* AAUDIO_MIXER_ATRACE_ENABLED */
// Mix data in one or two parts.
int partIndex = 0;
while (framesLeft > 0 && partIndex < WrappingBuffer::SIZE) {
fifo_frames_t framesToMix = framesLeft;
fifo_frames_t framesAvailable = wrappingBuffer.numFrames[partIndex];
if (framesAvailable > 0) {
if (framesToMix > framesAvailable) {
framesToMix = framesAvailable;
}
mixPart(destination, (float *)wrappingBuffer.data[partIndex], framesToMix, volume);
destination += framesToMix * mSamplesPerFrame;
framesLeft -= framesToMix;
}
partIndex++;
}
// Always advance by one burst even if we do not have the data.
// Otherwise the stream timing will drift whenever there is an underflow.
// This actual underflow can then be detected by the client for XRun counting.
fifo->getFifoControllerBase()->advanceReadIndex(mFramesPerBurst);
#if AAUDIO_MIXER_ATRACE_ENABLED
ATRACE_END();
#endif /* AAUDIO_MIXER_ATRACE_ENABLED */
return (framesLeft > 0); // did not get all the frames we needed, ie. "underflow"
}
void AAudioMixer::mixPart(float *destination, float *source, int32_t numFrames, float volume) {
int32_t numSamples = numFrames * mSamplesPerFrame;
// TODO maybe optimize using SIMD
for (int sampleIndex = 0; sampleIndex < numSamples; sampleIndex++) {
*destination++ += *source++ * volume;
}
}
float *AAudioMixer::getOutputBuffer() {
return mOutputBuffer;
}