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