blob: 80551c90a339bcaea5908dc6ae2f478a6a66ba6f [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.
*/
#include <algorithm>
#include <mutex>
#include <vector>
#include "core/AudioStreamBuilder.h"
#include "AAudioServiceEndpoint.h"
#include "AAudioServiceStreamShared.h"
using namespace android; // TODO just import names needed
using namespace aaudio; // TODO just import names needed
#define MIN_TIMEOUT_NANOS (1000 * AAUDIO_NANOS_PER_MILLISECOND)
// Wait at least this many times longer than the operation should take.
#define MIN_TIMEOUT_OPERATIONS 4
// The mStreamInternal will use a service interface that does not go through Binder.
AAudioServiceEndpoint::AAudioServiceEndpoint(AAudioService &audioService)
: mStreamInternal(audioService, true)
{
}
AAudioServiceEndpoint::~AAudioServiceEndpoint() {
}
// Set up an EXCLUSIVE MMAP stream that will be shared.
aaudio_result_t AAudioServiceEndpoint::open(int32_t deviceId, aaudio_direction_t direction) {
AudioStreamBuilder builder;
builder.setSharingMode(AAUDIO_SHARING_MODE_EXCLUSIVE);
builder.setDeviceId(deviceId);
builder.setDirection(direction);
aaudio_result_t result = mStreamInternal.open(builder);
if (result == AAUDIO_OK) {
mMixer.allocate(mStreamInternal.getSamplesPerFrame(), mStreamInternal.getFramesPerBurst());
}
return result;
}
aaudio_result_t AAudioServiceEndpoint::close() {
return mStreamInternal.close();
}
// TODO, maybe use an interface to reduce exposure
aaudio_result_t AAudioServiceEndpoint::registerStream(AAudioServiceStreamShared *sharedStream) {
ALOGD("AAudioServiceEndpoint::registerStream(%p)", sharedStream);
// TODO use real-time technique to avoid mutex, eg. atomic command FIFO
std::lock_guard<std::mutex> lock(mLockStreams);
mRegisteredStreams.push_back(sharedStream);
return AAUDIO_OK;
}
aaudio_result_t AAudioServiceEndpoint::unregisterStream(AAudioServiceStreamShared *sharedStream) {
ALOGD("AAudioServiceEndpoint::unregisterStream(%p)", sharedStream);
std::lock_guard<std::mutex> lock(mLockStreams);
mRegisteredStreams.erase(std::remove(mRegisteredStreams.begin(), mRegisteredStreams.end(), sharedStream),
mRegisteredStreams.end());
return AAUDIO_OK;
}
aaudio_result_t AAudioServiceEndpoint::startStream(AAudioServiceStreamShared *sharedStream) {
// TODO use real-time technique to avoid mutex, eg. atomic command FIFO
ALOGD("AAudioServiceEndpoint(): startStream() entering");
std::lock_guard<std::mutex> lock(mLockStreams);
mRunningStreams.push_back(sharedStream);
if (mRunningStreams.size() == 1) {
startMixer_l();
}
return AAUDIO_OK;
}
aaudio_result_t AAudioServiceEndpoint::stopStream(AAudioServiceStreamShared *sharedStream) {
std::lock_guard<std::mutex> lock(mLockStreams);
mRunningStreams.erase(std::remove(mRunningStreams.begin(), mRunningStreams.end(), sharedStream),
mRunningStreams.end());
if (mRunningStreams.size() == 0) {
stopMixer_l();
}
return AAUDIO_OK;
}
static void *aaudio_mixer_thread_proc(void *context) {
AAudioServiceEndpoint *stream = (AAudioServiceEndpoint *) context;
//LOGD("AudioStreamAAudio(): oboe_callback_thread, stream = %p", stream);
if (stream != NULL) {
return stream->callbackLoop();
} else {
return NULL;
}
}
// Render audio in the application callback and then write the data to the stream.
void *AAudioServiceEndpoint::callbackLoop() {
aaudio_result_t result = AAUDIO_OK;
ALOGD("AAudioServiceEndpoint(): callbackLoop() entering");
result = mStreamInternal.requestStart();
ALOGD("AAudioServiceEndpoint(): callbackLoop() after requestStart() %d, isPlaying() = %d",
result, (int) mStreamInternal.isPlaying());
// result might be a frame count
while (mCallbackEnabled.load() && mStreamInternal.isPlaying() && (result >= 0)) {
// Mix data from each active stream.
{
mMixer.clear();
std::lock_guard<std::mutex> lock(mLockStreams);
for(AAudioServiceStreamShared *sharedStream : mRunningStreams) {
FifoBuffer *fifo = sharedStream->getDataFifoBuffer();
float volume = 0.5; // TODO get from system
mMixer.mix(fifo, volume);
}
}
// Write audio data to stream using a blocking write.
ALOGD("AAudioServiceEndpoint(): callbackLoop() write(%d)", getFramesPerBurst());
int64_t timeoutNanos = calculateReasonableTimeout(mStreamInternal.getFramesPerBurst());
result = mStreamInternal.write(mMixer.getOutputBuffer(), getFramesPerBurst(), timeoutNanos);
if (result == AAUDIO_ERROR_DISCONNECTED) {
disconnectRegisteredStreams();
break;
} else if (result != getFramesPerBurst()) {
ALOGW("AAudioServiceEndpoint(): callbackLoop() wrote %d / %d",
result, getFramesPerBurst());
break;
}
}
ALOGD("AAudioServiceEndpoint(): callbackLoop() exiting, result = %d, isPlaying() = %d",
result, (int) mStreamInternal.isPlaying());
result = mStreamInternal.requestStop();
return NULL; // TODO review
}
aaudio_result_t AAudioServiceEndpoint::startMixer_l() {
// Launch the callback loop thread.
int64_t periodNanos = mStreamInternal.getFramesPerBurst()
* AAUDIO_NANOS_PER_SECOND
/ getSampleRate();
mCallbackEnabled.store(true);
return mStreamInternal.createThread(periodNanos, aaudio_mixer_thread_proc, this);
}
aaudio_result_t AAudioServiceEndpoint::stopMixer_l() {
mCallbackEnabled.store(false);
return mStreamInternal.joinThread(NULL, calculateReasonableTimeout(mStreamInternal.getFramesPerBurst()));
}
// TODO Call method in AudioStreamInternal when that callback CL is merged.
int64_t AAudioServiceEndpoint::calculateReasonableTimeout(int32_t framesPerOperation) {
// Wait for at least a second or some number of callbacks to join the thread.
int64_t timeoutNanoseconds = (MIN_TIMEOUT_OPERATIONS * framesPerOperation * AAUDIO_NANOS_PER_SECOND)
/ getSampleRate();
if (timeoutNanoseconds < MIN_TIMEOUT_NANOS) { // arbitrary number of seconds
timeoutNanoseconds = MIN_TIMEOUT_NANOS;
}
return timeoutNanoseconds;
}
void AAudioServiceEndpoint::disconnectRegisteredStreams() {
std::lock_guard<std::mutex> lock(mLockStreams);
for(AAudioServiceStreamShared *sharedStream : mRunningStreams) {
sharedStream->onStop();
}
mRunningStreams.clear();
for(AAudioServiceStreamShared *sharedStream : mRegisteredStreams) {
sharedStream->onDisconnect();
}
mRegisteredStreams.clear();
}