blob: a2e6d336ec8007708b9c9e539d3033a9f10ee1fc [file] [log] [blame]
Phil Burkc0c70e32017-02-09 13:18:38 -08001/*
2 * Copyright (C) 2017 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
Phil Burk71f35bb2017-04-13 16:05:07 -070017#define LOG_TAG "AAudioService"
18//#define LOG_NDEBUG 0
19#include <utils/Log.h>
20
21#include <assert.h>
22#include <map>
23#include <mutex>
24#include <utils/Singleton.h>
25
26#include "AAudioEndpointManager.h"
27#include "AAudioServiceEndpoint.h"
Phil Burkc0c70e32017-02-09 13:18:38 -080028#include <algorithm>
29#include <mutex>
30#include <vector>
31
32#include "core/AudioStreamBuilder.h"
33#include "AAudioServiceEndpoint.h"
34#include "AAudioServiceStreamShared.h"
35
36using namespace android; // TODO just import names needed
37using namespace aaudio; // TODO just import names needed
38
39#define MIN_TIMEOUT_NANOS (1000 * AAUDIO_NANOS_PER_MILLISECOND)
40
41// Wait at least this many times longer than the operation should take.
42#define MIN_TIMEOUT_OPERATIONS 4
43
Phil Burk71f35bb2017-04-13 16:05:07 -070044// This is the maximum size in frames. The effective size can be tuned smaller at runtime.
45#define DEFAULT_BUFFER_CAPACITY (48 * 8)
46
Phil Burkc0c70e32017-02-09 13:18:38 -080047// The mStreamInternal will use a service interface that does not go through Binder.
48AAudioServiceEndpoint::AAudioServiceEndpoint(AAudioService &audioService)
49 : mStreamInternal(audioService, true)
50 {
51}
52
53AAudioServiceEndpoint::~AAudioServiceEndpoint() {
54}
55
56// Set up an EXCLUSIVE MMAP stream that will be shared.
57aaudio_result_t AAudioServiceEndpoint::open(int32_t deviceId, aaudio_direction_t direction) {
58 AudioStreamBuilder builder;
59 builder.setSharingMode(AAUDIO_SHARING_MODE_EXCLUSIVE);
Phil Burk71f35bb2017-04-13 16:05:07 -070060 // Don't fall back to SHARED because that would cause recursion.
61 builder.setSharingModeMatchRequired(true);
Phil Burkc0c70e32017-02-09 13:18:38 -080062 builder.setDeviceId(deviceId);
63 builder.setDirection(direction);
Phil Burk71f35bb2017-04-13 16:05:07 -070064 builder.setBufferCapacity(DEFAULT_BUFFER_CAPACITY);
65
Phil Burkc0c70e32017-02-09 13:18:38 -080066 aaudio_result_t result = mStreamInternal.open(builder);
67 if (result == AAUDIO_OK) {
68 mMixer.allocate(mStreamInternal.getSamplesPerFrame(), mStreamInternal.getFramesPerBurst());
Phil Burk71f35bb2017-04-13 16:05:07 -070069
Phil Burkc8f69a02017-05-11 15:53:06 -070070 int32_t burstsPerBuffer = AAudioProperty_getMixerBursts();
71 if (burstsPerBuffer == 0) {
72 mLatencyTuningEnabled = true;
73 burstsPerBuffer = 2;
74 }
75 ALOGD("AAudioServiceEndpoint(): burstsPerBuffer = %d", burstsPerBuffer);
76 int32_t desiredBufferSize = burstsPerBuffer * mStreamInternal.getFramesPerBurst();
Phil Burk71f35bb2017-04-13 16:05:07 -070077 mStreamInternal.setBufferSize(desiredBufferSize);
Phil Burkc0c70e32017-02-09 13:18:38 -080078 }
79 return result;
80}
81
82aaudio_result_t AAudioServiceEndpoint::close() {
83 return mStreamInternal.close();
84}
85
86// TODO, maybe use an interface to reduce exposure
87aaudio_result_t AAudioServiceEndpoint::registerStream(AAudioServiceStreamShared *sharedStream) {
Phil Burkc0c70e32017-02-09 13:18:38 -080088 std::lock_guard<std::mutex> lock(mLockStreams);
89 mRegisteredStreams.push_back(sharedStream);
90 return AAUDIO_OK;
91}
92
93aaudio_result_t AAudioServiceEndpoint::unregisterStream(AAudioServiceStreamShared *sharedStream) {
Phil Burkc0c70e32017-02-09 13:18:38 -080094 std::lock_guard<std::mutex> lock(mLockStreams);
95 mRegisteredStreams.erase(std::remove(mRegisteredStreams.begin(), mRegisteredStreams.end(), sharedStream),
96 mRegisteredStreams.end());
97 return AAUDIO_OK;
98}
99
100aaudio_result_t AAudioServiceEndpoint::startStream(AAudioServiceStreamShared *sharedStream) {
101 // TODO use real-time technique to avoid mutex, eg. atomic command FIFO
Phil Burkc0c70e32017-02-09 13:18:38 -0800102 std::lock_guard<std::mutex> lock(mLockStreams);
103 mRunningStreams.push_back(sharedStream);
104 if (mRunningStreams.size() == 1) {
105 startMixer_l();
106 }
107 return AAUDIO_OK;
108}
109
110aaudio_result_t AAudioServiceEndpoint::stopStream(AAudioServiceStreamShared *sharedStream) {
111 std::lock_guard<std::mutex> lock(mLockStreams);
112 mRunningStreams.erase(std::remove(mRunningStreams.begin(), mRunningStreams.end(), sharedStream),
113 mRunningStreams.end());
114 if (mRunningStreams.size() == 0) {
115 stopMixer_l();
116 }
117 return AAUDIO_OK;
118}
119
120static void *aaudio_mixer_thread_proc(void *context) {
121 AAudioServiceEndpoint *stream = (AAudioServiceEndpoint *) context;
Phil Burkc0c70e32017-02-09 13:18:38 -0800122 if (stream != NULL) {
123 return stream->callbackLoop();
124 } else {
125 return NULL;
126 }
127}
128
129// Render audio in the application callback and then write the data to the stream.
130void *AAudioServiceEndpoint::callbackLoop() {
Phil Burkc0c70e32017-02-09 13:18:38 -0800131 ALOGD("AAudioServiceEndpoint(): callbackLoop() entering");
Phil Burk71f35bb2017-04-13 16:05:07 -0700132 int32_t underflowCount = 0;
Phil Burkc0c70e32017-02-09 13:18:38 -0800133
Phil Burk71f35bb2017-04-13 16:05:07 -0700134 aaudio_result_t result = mStreamInternal.requestStart();
Phil Burkc0c70e32017-02-09 13:18:38 -0800135
136 // result might be a frame count
137 while (mCallbackEnabled.load() && mStreamInternal.isPlaying() && (result >= 0)) {
138 // Mix data from each active stream.
139 {
140 mMixer.clear();
141 std::lock_guard<std::mutex> lock(mLockStreams);
142 for(AAudioServiceStreamShared *sharedStream : mRunningStreams) {
143 FifoBuffer *fifo = sharedStream->getDataFifoBuffer();
144 float volume = 0.5; // TODO get from system
Phil Burk71f35bb2017-04-13 16:05:07 -0700145 bool underflowed = mMixer.mix(fifo, volume);
146 underflowCount += underflowed ? 1 : 0;
147 // TODO log underflows in each stream
148 sharedStream->markTransferTime(AudioClock::getNanoseconds());
Phil Burkc0c70e32017-02-09 13:18:38 -0800149 }
150 }
151
152 // Write audio data to stream using a blocking write.
Phil Burkc0c70e32017-02-09 13:18:38 -0800153 int64_t timeoutNanos = calculateReasonableTimeout(mStreamInternal.getFramesPerBurst());
154 result = mStreamInternal.write(mMixer.getOutputBuffer(), getFramesPerBurst(), timeoutNanos);
155 if (result == AAUDIO_ERROR_DISCONNECTED) {
156 disconnectRegisteredStreams();
157 break;
158 } else if (result != getFramesPerBurst()) {
159 ALOGW("AAudioServiceEndpoint(): callbackLoop() wrote %d / %d",
160 result, getFramesPerBurst());
161 break;
162 }
163 }
164
Phil Burkc0c70e32017-02-09 13:18:38 -0800165 result = mStreamInternal.requestStop();
166
Phil Burk71f35bb2017-04-13 16:05:07 -0700167 ALOGD("AAudioServiceEndpoint(): callbackLoop() exiting, %d underflows", underflowCount);
Phil Burkc0c70e32017-02-09 13:18:38 -0800168 return NULL; // TODO review
169}
170
171aaudio_result_t AAudioServiceEndpoint::startMixer_l() {
172 // Launch the callback loop thread.
173 int64_t periodNanos = mStreamInternal.getFramesPerBurst()
174 * AAUDIO_NANOS_PER_SECOND
175 / getSampleRate();
176 mCallbackEnabled.store(true);
177 return mStreamInternal.createThread(periodNanos, aaudio_mixer_thread_proc, this);
178}
179
180aaudio_result_t AAudioServiceEndpoint::stopMixer_l() {
181 mCallbackEnabled.store(false);
182 return mStreamInternal.joinThread(NULL, calculateReasonableTimeout(mStreamInternal.getFramesPerBurst()));
183}
184
185// TODO Call method in AudioStreamInternal when that callback CL is merged.
186int64_t AAudioServiceEndpoint::calculateReasonableTimeout(int32_t framesPerOperation) {
187
188 // Wait for at least a second or some number of callbacks to join the thread.
189 int64_t timeoutNanoseconds = (MIN_TIMEOUT_OPERATIONS * framesPerOperation * AAUDIO_NANOS_PER_SECOND)
190 / getSampleRate();
191 if (timeoutNanoseconds < MIN_TIMEOUT_NANOS) { // arbitrary number of seconds
192 timeoutNanoseconds = MIN_TIMEOUT_NANOS;
193 }
194 return timeoutNanoseconds;
195}
196
197void AAudioServiceEndpoint::disconnectRegisteredStreams() {
198 std::lock_guard<std::mutex> lock(mLockStreams);
199 for(AAudioServiceStreamShared *sharedStream : mRunningStreams) {
200 sharedStream->onStop();
201 }
202 mRunningStreams.clear();
203 for(AAudioServiceStreamShared *sharedStream : mRegisteredStreams) {
204 sharedStream->onDisconnect();
205 }
206 mRegisteredStreams.clear();
207}