blob: b19779820c157289671ca2241fc04485cbe6c5a0 [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
47// Use 2 for "double buffered"
48#define BUFFER_SIZE_IN_BURSTS 2
49
Phil Burkc0c70e32017-02-09 13:18:38 -080050// The mStreamInternal will use a service interface that does not go through Binder.
51AAudioServiceEndpoint::AAudioServiceEndpoint(AAudioService &audioService)
52 : mStreamInternal(audioService, true)
53 {
54}
55
56AAudioServiceEndpoint::~AAudioServiceEndpoint() {
57}
58
59// Set up an EXCLUSIVE MMAP stream that will be shared.
60aaudio_result_t AAudioServiceEndpoint::open(int32_t deviceId, aaudio_direction_t direction) {
61 AudioStreamBuilder builder;
62 builder.setSharingMode(AAUDIO_SHARING_MODE_EXCLUSIVE);
Phil Burk71f35bb2017-04-13 16:05:07 -070063 // Don't fall back to SHARED because that would cause recursion.
64 builder.setSharingModeMatchRequired(true);
Phil Burkc0c70e32017-02-09 13:18:38 -080065 builder.setDeviceId(deviceId);
66 builder.setDirection(direction);
Phil Burk71f35bb2017-04-13 16:05:07 -070067 builder.setBufferCapacity(DEFAULT_BUFFER_CAPACITY);
68
Phil Burkc0c70e32017-02-09 13:18:38 -080069 aaudio_result_t result = mStreamInternal.open(builder);
70 if (result == AAUDIO_OK) {
71 mMixer.allocate(mStreamInternal.getSamplesPerFrame(), mStreamInternal.getFramesPerBurst());
Phil Burk71f35bb2017-04-13 16:05:07 -070072
73 int32_t desiredBufferSize = BUFFER_SIZE_IN_BURSTS * mStreamInternal.getFramesPerBurst();
74 mStreamInternal.setBufferSize(desiredBufferSize);
Phil Burkc0c70e32017-02-09 13:18:38 -080075 }
76 return result;
77}
78
79aaudio_result_t AAudioServiceEndpoint::close() {
80 return mStreamInternal.close();
81}
82
83// TODO, maybe use an interface to reduce exposure
84aaudio_result_t AAudioServiceEndpoint::registerStream(AAudioServiceStreamShared *sharedStream) {
Phil Burkc0c70e32017-02-09 13:18:38 -080085 std::lock_guard<std::mutex> lock(mLockStreams);
86 mRegisteredStreams.push_back(sharedStream);
87 return AAUDIO_OK;
88}
89
90aaudio_result_t AAudioServiceEndpoint::unregisterStream(AAudioServiceStreamShared *sharedStream) {
Phil Burkc0c70e32017-02-09 13:18:38 -080091 std::lock_guard<std::mutex> lock(mLockStreams);
92 mRegisteredStreams.erase(std::remove(mRegisteredStreams.begin(), mRegisteredStreams.end(), sharedStream),
93 mRegisteredStreams.end());
94 return AAUDIO_OK;
95}
96
97aaudio_result_t AAudioServiceEndpoint::startStream(AAudioServiceStreamShared *sharedStream) {
98 // TODO use real-time technique to avoid mutex, eg. atomic command FIFO
Phil Burkc0c70e32017-02-09 13:18:38 -080099 std::lock_guard<std::mutex> lock(mLockStreams);
100 mRunningStreams.push_back(sharedStream);
101 if (mRunningStreams.size() == 1) {
102 startMixer_l();
103 }
104 return AAUDIO_OK;
105}
106
107aaudio_result_t AAudioServiceEndpoint::stopStream(AAudioServiceStreamShared *sharedStream) {
108 std::lock_guard<std::mutex> lock(mLockStreams);
109 mRunningStreams.erase(std::remove(mRunningStreams.begin(), mRunningStreams.end(), sharedStream),
110 mRunningStreams.end());
111 if (mRunningStreams.size() == 0) {
112 stopMixer_l();
113 }
114 return AAUDIO_OK;
115}
116
117static void *aaudio_mixer_thread_proc(void *context) {
118 AAudioServiceEndpoint *stream = (AAudioServiceEndpoint *) context;
119 //LOGD("AudioStreamAAudio(): oboe_callback_thread, stream = %p", stream);
120 if (stream != NULL) {
121 return stream->callbackLoop();
122 } else {
123 return NULL;
124 }
125}
126
127// Render audio in the application callback and then write the data to the stream.
128void *AAudioServiceEndpoint::callbackLoop() {
Phil Burkc0c70e32017-02-09 13:18:38 -0800129 ALOGD("AAudioServiceEndpoint(): callbackLoop() entering");
Phil Burk71f35bb2017-04-13 16:05:07 -0700130 int32_t underflowCount = 0;
Phil Burkc0c70e32017-02-09 13:18:38 -0800131
Phil Burk71f35bb2017-04-13 16:05:07 -0700132 aaudio_result_t result = mStreamInternal.requestStart();
Phil Burkc0c70e32017-02-09 13:18:38 -0800133
134 // result might be a frame count
135 while (mCallbackEnabled.load() && mStreamInternal.isPlaying() && (result >= 0)) {
136 // Mix data from each active stream.
137 {
138 mMixer.clear();
139 std::lock_guard<std::mutex> lock(mLockStreams);
140 for(AAudioServiceStreamShared *sharedStream : mRunningStreams) {
141 FifoBuffer *fifo = sharedStream->getDataFifoBuffer();
142 float volume = 0.5; // TODO get from system
Phil Burk71f35bb2017-04-13 16:05:07 -0700143 bool underflowed = mMixer.mix(fifo, volume);
144 underflowCount += underflowed ? 1 : 0;
145 // TODO log underflows in each stream
146 sharedStream->markTransferTime(AudioClock::getNanoseconds());
Phil Burkc0c70e32017-02-09 13:18:38 -0800147 }
148 }
149
150 // Write audio data to stream using a blocking write.
Phil Burkc0c70e32017-02-09 13:18:38 -0800151 int64_t timeoutNanos = calculateReasonableTimeout(mStreamInternal.getFramesPerBurst());
152 result = mStreamInternal.write(mMixer.getOutputBuffer(), getFramesPerBurst(), timeoutNanos);
153 if (result == AAUDIO_ERROR_DISCONNECTED) {
154 disconnectRegisteredStreams();
155 break;
156 } else if (result != getFramesPerBurst()) {
157 ALOGW("AAudioServiceEndpoint(): callbackLoop() wrote %d / %d",
158 result, getFramesPerBurst());
159 break;
160 }
161 }
162
Phil Burkc0c70e32017-02-09 13:18:38 -0800163 result = mStreamInternal.requestStop();
164
Phil Burk71f35bb2017-04-13 16:05:07 -0700165 ALOGD("AAudioServiceEndpoint(): callbackLoop() exiting, %d underflows", underflowCount);
Phil Burkc0c70e32017-02-09 13:18:38 -0800166 return NULL; // TODO review
167}
168
169aaudio_result_t AAudioServiceEndpoint::startMixer_l() {
170 // Launch the callback loop thread.
171 int64_t periodNanos = mStreamInternal.getFramesPerBurst()
172 * AAUDIO_NANOS_PER_SECOND
173 / getSampleRate();
174 mCallbackEnabled.store(true);
175 return mStreamInternal.createThread(periodNanos, aaudio_mixer_thread_proc, this);
176}
177
178aaudio_result_t AAudioServiceEndpoint::stopMixer_l() {
179 mCallbackEnabled.store(false);
180 return mStreamInternal.joinThread(NULL, calculateReasonableTimeout(mStreamInternal.getFramesPerBurst()));
181}
182
183// TODO Call method in AudioStreamInternal when that callback CL is merged.
184int64_t AAudioServiceEndpoint::calculateReasonableTimeout(int32_t framesPerOperation) {
185
186 // Wait for at least a second or some number of callbacks to join the thread.
187 int64_t timeoutNanoseconds = (MIN_TIMEOUT_OPERATIONS * framesPerOperation * AAUDIO_NANOS_PER_SECOND)
188 / getSampleRate();
189 if (timeoutNanoseconds < MIN_TIMEOUT_NANOS) { // arbitrary number of seconds
190 timeoutNanoseconds = MIN_TIMEOUT_NANOS;
191 }
192 return timeoutNanoseconds;
193}
194
195void AAudioServiceEndpoint::disconnectRegisteredStreams() {
196 std::lock_guard<std::mutex> lock(mLockStreams);
197 for(AAudioServiceStreamShared *sharedStream : mRunningStreams) {
198 sharedStream->onStop();
199 }
200 mRunningStreams.clear();
201 for(AAudioServiceStreamShared *sharedStream : mRegisteredStreams) {
202 sharedStream->onDisconnect();
203 }
204 mRegisteredStreams.clear();
205}