blob: 80551c90a339bcaea5908dc6ae2f478a6a66ba6f [file] [log] [blame]
Phil Burk7f6b40d2017-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
17#include <algorithm>
18#include <mutex>
19#include <vector>
20
21#include "core/AudioStreamBuilder.h"
22#include "AAudioServiceEndpoint.h"
23#include "AAudioServiceStreamShared.h"
24
25using namespace android; // TODO just import names needed
26using namespace aaudio; // TODO just import names needed
27
28#define MIN_TIMEOUT_NANOS (1000 * AAUDIO_NANOS_PER_MILLISECOND)
29
30// Wait at least this many times longer than the operation should take.
31#define MIN_TIMEOUT_OPERATIONS 4
32
33// The mStreamInternal will use a service interface that does not go through Binder.
34AAudioServiceEndpoint::AAudioServiceEndpoint(AAudioService &audioService)
35 : mStreamInternal(audioService, true)
36 {
37}
38
39AAudioServiceEndpoint::~AAudioServiceEndpoint() {
40}
41
42// Set up an EXCLUSIVE MMAP stream that will be shared.
43aaudio_result_t AAudioServiceEndpoint::open(int32_t deviceId, aaudio_direction_t direction) {
44 AudioStreamBuilder builder;
45 builder.setSharingMode(AAUDIO_SHARING_MODE_EXCLUSIVE);
46 builder.setDeviceId(deviceId);
47 builder.setDirection(direction);
48 aaudio_result_t result = mStreamInternal.open(builder);
49 if (result == AAUDIO_OK) {
50 mMixer.allocate(mStreamInternal.getSamplesPerFrame(), mStreamInternal.getFramesPerBurst());
51 }
52 return result;
53}
54
55aaudio_result_t AAudioServiceEndpoint::close() {
56 return mStreamInternal.close();
57}
58
59// TODO, maybe use an interface to reduce exposure
60aaudio_result_t AAudioServiceEndpoint::registerStream(AAudioServiceStreamShared *sharedStream) {
61 ALOGD("AAudioServiceEndpoint::registerStream(%p)", sharedStream);
62 // TODO use real-time technique to avoid mutex, eg. atomic command FIFO
63 std::lock_guard<std::mutex> lock(mLockStreams);
64 mRegisteredStreams.push_back(sharedStream);
65 return AAUDIO_OK;
66}
67
68aaudio_result_t AAudioServiceEndpoint::unregisterStream(AAudioServiceStreamShared *sharedStream) {
69 ALOGD("AAudioServiceEndpoint::unregisterStream(%p)", sharedStream);
70 std::lock_guard<std::mutex> lock(mLockStreams);
71 mRegisteredStreams.erase(std::remove(mRegisteredStreams.begin(), mRegisteredStreams.end(), sharedStream),
72 mRegisteredStreams.end());
73 return AAUDIO_OK;
74}
75
76aaudio_result_t AAudioServiceEndpoint::startStream(AAudioServiceStreamShared *sharedStream) {
77 // TODO use real-time technique to avoid mutex, eg. atomic command FIFO
78 ALOGD("AAudioServiceEndpoint(): startStream() entering");
79 std::lock_guard<std::mutex> lock(mLockStreams);
80 mRunningStreams.push_back(sharedStream);
81 if (mRunningStreams.size() == 1) {
82 startMixer_l();
83 }
84 return AAUDIO_OK;
85}
86
87aaudio_result_t AAudioServiceEndpoint::stopStream(AAudioServiceStreamShared *sharedStream) {
88 std::lock_guard<std::mutex> lock(mLockStreams);
89 mRunningStreams.erase(std::remove(mRunningStreams.begin(), mRunningStreams.end(), sharedStream),
90 mRunningStreams.end());
91 if (mRunningStreams.size() == 0) {
92 stopMixer_l();
93 }
94 return AAUDIO_OK;
95}
96
97static void *aaudio_mixer_thread_proc(void *context) {
98 AAudioServiceEndpoint *stream = (AAudioServiceEndpoint *) context;
99 //LOGD("AudioStreamAAudio(): oboe_callback_thread, stream = %p", stream);
100 if (stream != NULL) {
101 return stream->callbackLoop();
102 } else {
103 return NULL;
104 }
105}
106
107// Render audio in the application callback and then write the data to the stream.
108void *AAudioServiceEndpoint::callbackLoop() {
109 aaudio_result_t result = AAUDIO_OK;
110
111 ALOGD("AAudioServiceEndpoint(): callbackLoop() entering");
112
113 result = mStreamInternal.requestStart();
114 ALOGD("AAudioServiceEndpoint(): callbackLoop() after requestStart() %d, isPlaying() = %d",
115 result, (int) mStreamInternal.isPlaying());
116
117 // result might be a frame count
118 while (mCallbackEnabled.load() && mStreamInternal.isPlaying() && (result >= 0)) {
119 // Mix data from each active stream.
120 {
121 mMixer.clear();
122 std::lock_guard<std::mutex> lock(mLockStreams);
123 for(AAudioServiceStreamShared *sharedStream : mRunningStreams) {
124 FifoBuffer *fifo = sharedStream->getDataFifoBuffer();
125 float volume = 0.5; // TODO get from system
126 mMixer.mix(fifo, volume);
127 }
128 }
129
130 // Write audio data to stream using a blocking write.
131 ALOGD("AAudioServiceEndpoint(): callbackLoop() write(%d)", getFramesPerBurst());
132 int64_t timeoutNanos = calculateReasonableTimeout(mStreamInternal.getFramesPerBurst());
133 result = mStreamInternal.write(mMixer.getOutputBuffer(), getFramesPerBurst(), timeoutNanos);
134 if (result == AAUDIO_ERROR_DISCONNECTED) {
135 disconnectRegisteredStreams();
136 break;
137 } else if (result != getFramesPerBurst()) {
138 ALOGW("AAudioServiceEndpoint(): callbackLoop() wrote %d / %d",
139 result, getFramesPerBurst());
140 break;
141 }
142 }
143
144 ALOGD("AAudioServiceEndpoint(): callbackLoop() exiting, result = %d, isPlaying() = %d",
145 result, (int) mStreamInternal.isPlaying());
146
147 result = mStreamInternal.requestStop();
148
149 return NULL; // TODO review
150}
151
152aaudio_result_t AAudioServiceEndpoint::startMixer_l() {
153 // Launch the callback loop thread.
154 int64_t periodNanos = mStreamInternal.getFramesPerBurst()
155 * AAUDIO_NANOS_PER_SECOND
156 / getSampleRate();
157 mCallbackEnabled.store(true);
158 return mStreamInternal.createThread(periodNanos, aaudio_mixer_thread_proc, this);
159}
160
161aaudio_result_t AAudioServiceEndpoint::stopMixer_l() {
162 mCallbackEnabled.store(false);
163 return mStreamInternal.joinThread(NULL, calculateReasonableTimeout(mStreamInternal.getFramesPerBurst()));
164}
165
166// TODO Call method in AudioStreamInternal when that callback CL is merged.
167int64_t AAudioServiceEndpoint::calculateReasonableTimeout(int32_t framesPerOperation) {
168
169 // Wait for at least a second or some number of callbacks to join the thread.
170 int64_t timeoutNanoseconds = (MIN_TIMEOUT_OPERATIONS * framesPerOperation * AAUDIO_NANOS_PER_SECOND)
171 / getSampleRate();
172 if (timeoutNanoseconds < MIN_TIMEOUT_NANOS) { // arbitrary number of seconds
173 timeoutNanoseconds = MIN_TIMEOUT_NANOS;
174 }
175 return timeoutNanoseconds;
176}
177
178void AAudioServiceEndpoint::disconnectRegisteredStreams() {
179 std::lock_guard<std::mutex> lock(mLockStreams);
180 for(AAudioServiceStreamShared *sharedStream : mRunningStreams) {
181 sharedStream->onStop();
182 }
183 mRunningStreams.clear();
184 for(AAudioServiceStreamShared *sharedStream : mRegisteredStreams) {
185 sharedStream->onDisconnect();
186 }
187 mRegisteredStreams.clear();
188}