1 /*
2 * Copyright (C) 2016 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 <memory>
18
19 #include "oboe/Oboe.h"
20
21 #include "common/OboeDebug.h"
22 #include "opensles/AudioStreamBuffered.h"
23 #include "common/AudioClock.h"
24
25 namespace oboe {
26
27 constexpr int kDefaultBurstsPerBuffer = 16; // arbitrary, allows dynamic latency tuning
28 constexpr int kMinBurstsPerBuffer = 4; // arbitrary, allows dynamic latency tuning
29 constexpr int kMinFramesPerBuffer = 48 * 32; // arbitrary
30
31 /*
32 * AudioStream with a FifoBuffer
33 */
AudioStreamBuffered(const AudioStreamBuilder & builder)34 AudioStreamBuffered::AudioStreamBuffered(const AudioStreamBuilder &builder)
35 : AudioStream(builder) {
36 }
37
allocateFifo()38 void AudioStreamBuffered::allocateFifo() {
39 // If the caller does not provide a callback use our own internal
40 // callback that reads data from the FIFO.
41 if (usingFIFO()) {
42 // FIFO is configured with the same format and channels as the stream.
43 int32_t capacityFrames = getBufferCapacityInFrames();
44 if (capacityFrames == oboe::kUnspecified) {
45 capacityFrames = getFramesPerBurst() * kDefaultBurstsPerBuffer;
46 } else {
47 int32_t minFramesPerBufferByBursts = getFramesPerBurst() * kMinBurstsPerBuffer;
48 if (capacityFrames <= minFramesPerBufferByBursts) {
49 capacityFrames = minFramesPerBufferByBursts;
50 } else {
51 capacityFrames = std::max(kMinFramesPerBuffer, capacityFrames);
52 // round up to nearest burst
53 int32_t numBursts = (capacityFrames + getFramesPerBurst() - 1)
54 / getFramesPerBurst();
55 capacityFrames = numBursts * getFramesPerBurst();
56 }
57 }
58
59 mFifoBuffer = std::make_unique<FifoBuffer>(getBytesPerFrame(), capacityFrames);
60 mBufferCapacityInFrames = capacityFrames;
61 mBufferSizeInFrames = mBufferCapacityInFrames;
62 }
63 }
64
updateFramesWritten()65 void AudioStreamBuffered::updateFramesWritten() {
66 if (mFifoBuffer) {
67 mFramesWritten = static_cast<int64_t>(mFifoBuffer->getWriteCounter());
68 } // or else it will get updated by processBufferCallback()
69 }
70
updateFramesRead()71 void AudioStreamBuffered::updateFramesRead() {
72 if (mFifoBuffer) {
73 mFramesRead = static_cast<int64_t>(mFifoBuffer->getReadCounter());
74 } // or else it will get updated by processBufferCallback()
75 }
76
77 // This is called by the OpenSL ES callback to read or write the back end of the FIFO.
onDefaultCallback(void * audioData,int numFrames)78 DataCallbackResult AudioStreamBuffered::onDefaultCallback(void *audioData, int numFrames) {
79 int32_t framesTransferred = 0;
80
81 if (getDirection() == oboe::Direction::Output) {
82 // Read from the FIFO and write to audioData, clear part of buffer if not enough data.
83 framesTransferred = mFifoBuffer->readNow(audioData, numFrames);
84 } else {
85 // Read from audioData and write to the FIFO
86 framesTransferred = mFifoBuffer->write(audioData, numFrames); // There is no writeNow()
87 }
88
89 if (framesTransferred < numFrames) {
90 LOGD("AudioStreamBuffered::%s(): xrun! framesTransferred = %d, numFrames = %d",
91 __func__, framesTransferred, numFrames);
92 // TODO If we do not allow FIFO to wrap then our timestamps will drift when there is an XRun!
93 incrementXRunCount();
94 }
95 markCallbackTime(static_cast<int32_t>(numFrames)); // so foreground knows how long to wait.
96 return DataCallbackResult::Continue;
97 }
98
markCallbackTime(int32_t numFrames)99 void AudioStreamBuffered::markCallbackTime(int32_t numFrames) {
100 mLastBackgroundSize = numFrames;
101 mBackgroundRanAtNanoseconds = AudioClock::getNanoseconds();
102 }
103
predictNextCallbackTime()104 int64_t AudioStreamBuffered::predictNextCallbackTime() {
105 if (mBackgroundRanAtNanoseconds == 0) {
106 return 0;
107 }
108 int64_t nanosPerBuffer = (kNanosPerSecond * mLastBackgroundSize) / getSampleRate();
109 const int64_t margin = 200 * kNanosPerMicrosecond; // arbitrary delay so we wake up just after
110 return mBackgroundRanAtNanoseconds + nanosPerBuffer + margin;
111 }
112
113 // Common code for read/write.
114 // @return Result::OK with frames read/written, or Result::Error*
transfer(void * readBuffer,const void * writeBuffer,int32_t numFrames,int64_t timeoutNanoseconds)115 ResultWithValue<int32_t> AudioStreamBuffered::transfer(
116 void *readBuffer,
117 const void *writeBuffer,
118 int32_t numFrames,
119 int64_t timeoutNanoseconds) {
120 // Validate arguments.
121 if (readBuffer != nullptr && writeBuffer != nullptr) {
122 LOGE("AudioStreamBuffered::%s(): both buffers are not NULL", __func__);
123 return ResultWithValue<int32_t>(Result::ErrorInternal);
124 }
125 if (getDirection() == Direction::Input && readBuffer == nullptr) {
126 LOGE("AudioStreamBuffered::%s(): readBuffer is NULL", __func__);
127 return ResultWithValue<int32_t>(Result::ErrorNull);
128 }
129 if (getDirection() == Direction::Output && writeBuffer == nullptr) {
130 LOGE("AudioStreamBuffered::%s(): writeBuffer is NULL", __func__);
131 return ResultWithValue<int32_t>(Result::ErrorNull);
132 }
133 if (numFrames < 0) {
134 LOGE("AudioStreamBuffered::%s(): numFrames is negative", __func__);
135 return ResultWithValue<int32_t>(Result::ErrorOutOfRange);
136 } else if (numFrames == 0) {
137 return ResultWithValue<int32_t>(numFrames);
138 }
139 if (timeoutNanoseconds < 0) {
140 LOGE("AudioStreamBuffered::%s(): timeoutNanoseconds is negative", __func__);
141 return ResultWithValue<int32_t>(Result::ErrorOutOfRange);
142 }
143
144 int32_t result = 0;
145 uint8_t *readData = reinterpret_cast<uint8_t *>(readBuffer);
146 const uint8_t *writeData = reinterpret_cast<const uint8_t *>(writeBuffer);
147 int32_t framesLeft = numFrames;
148 int64_t timeToQuit = 0;
149 bool repeat = true;
150
151 // Calculate when to timeout.
152 if (timeoutNanoseconds > 0) {
153 timeToQuit = AudioClock::getNanoseconds() + timeoutNanoseconds;
154 }
155
156 // Loop until we get the data, or we have an error, or we timeout.
157 do {
158 // read or write
159 if (getDirection() == Direction::Input) {
160 result = mFifoBuffer->read(readData, framesLeft);
161 if (result > 0) {
162 readData += mFifoBuffer->convertFramesToBytes(result);
163 framesLeft -= result;
164 }
165 } else {
166 // between zero and capacity
167 uint32_t fullFrames = mFifoBuffer->getFullFramesAvailable();
168 // Do not write above threshold size.
169 int32_t emptyFrames = getBufferSizeInFrames() - static_cast<int32_t>(fullFrames);
170 int32_t framesToWrite = std::max(0, std::min(framesLeft, emptyFrames));
171 result = mFifoBuffer->write(writeData, framesToWrite);
172 if (result > 0) {
173 writeData += mFifoBuffer->convertFramesToBytes(result);
174 framesLeft -= result;
175 }
176 }
177
178 // If we need more data then sleep and try again.
179 if (framesLeft > 0 && result >= 0 && timeoutNanoseconds > 0) {
180 int64_t timeNow = AudioClock::getNanoseconds();
181 if (timeNow >= timeToQuit) {
182 LOGE("AudioStreamBuffered::%s(): TIMEOUT", __func__);
183 repeat = false; // TIMEOUT
184 } else {
185 // Figure out how long to sleep.
186 int64_t sleepForNanos;
187 int64_t wakeTimeNanos = predictNextCallbackTime();
188 if (wakeTimeNanos <= 0) {
189 // No estimate available. Sleep for one burst.
190 sleepForNanos = (getFramesPerBurst() * kNanosPerSecond) / getSampleRate();
191 } else {
192 // Don't sleep past timeout.
193 if (wakeTimeNanos > timeToQuit) {
194 wakeTimeNanos = timeToQuit;
195 }
196 sleepForNanos = wakeTimeNanos - timeNow;
197 // Avoid rapid loop with no sleep.
198 const int64_t minSleepTime = kNanosPerMillisecond; // arbitrary
199 if (sleepForNanos < minSleepTime) {
200 sleepForNanos = minSleepTime;
201 }
202 }
203
204 AudioClock::sleepForNanos(sleepForNanos);
205 }
206
207 } else {
208 repeat = false;
209 }
210 } while(repeat);
211
212 if (result < 0) {
213 return ResultWithValue<int32_t>(static_cast<Result>(result));
214 } else {
215 int32_t framesWritten = numFrames - framesLeft;
216 return ResultWithValue<int32_t>(framesWritten);
217 }
218 }
219
220 // Write to the FIFO so the callback can read from it.
write(const void * buffer,int32_t numFrames,int64_t timeoutNanoseconds)221 ResultWithValue<int32_t> AudioStreamBuffered::write(const void *buffer,
222 int32_t numFrames,
223 int64_t timeoutNanoseconds) {
224 if (getState() == StreamState::Closed){
225 return ResultWithValue<int32_t>(Result::ErrorClosed);
226 }
227
228 if (getDirection() == Direction::Input) {
229 return ResultWithValue<int32_t>(Result::ErrorUnavailable); // TODO review, better error code?
230 }
231 Result result = updateServiceFrameCounter();
232 if (result != Result::OK) return ResultWithValue<int32_t>(static_cast<Result>(result));
233 return transfer(nullptr, buffer, numFrames, timeoutNanoseconds);
234 }
235
236 // Read data from the FIFO that was written by the callback.
read(void * buffer,int32_t numFrames,int64_t timeoutNanoseconds)237 ResultWithValue<int32_t> AudioStreamBuffered::read(void *buffer,
238 int32_t numFrames,
239 int64_t timeoutNanoseconds) {
240 if (getState() == StreamState::Closed){
241 return ResultWithValue<int32_t>(Result::ErrorClosed);
242 }
243
244 if (getDirection() == Direction::Output) {
245 return ResultWithValue<int32_t>(Result::ErrorUnavailable); // TODO review, better error code?
246 }
247 Result result = updateServiceFrameCounter();
248 if (result != Result::OK) return ResultWithValue<int32_t>(static_cast<Result>(result));
249 return transfer(buffer, nullptr, numFrames, timeoutNanoseconds);
250 }
251
252 // Only supported when we are not using a callback.
setBufferSizeInFrames(int32_t requestedFrames)253 ResultWithValue<int32_t> AudioStreamBuffered::setBufferSizeInFrames(int32_t requestedFrames)
254 {
255 if (getState() == StreamState::Closed){
256 return ResultWithValue<int32_t>(Result::ErrorClosed);
257 }
258
259 if (!mFifoBuffer) {
260 return ResultWithValue<int32_t>(Result::ErrorUnimplemented);
261 }
262
263 if (requestedFrames > mFifoBuffer->getBufferCapacityInFrames()) {
264 requestedFrames = mFifoBuffer->getBufferCapacityInFrames();
265 } else if (requestedFrames < getFramesPerBurst()) {
266 requestedFrames = getFramesPerBurst();
267 }
268 mBufferSizeInFrames = requestedFrames;
269 return ResultWithValue<int32_t>(requestedFrames);
270 }
271
getBufferCapacityInFrames() const272 int32_t AudioStreamBuffered::getBufferCapacityInFrames() const {
273 if (mFifoBuffer) {
274 return mFifoBuffer->getBufferCapacityInFrames();
275 } else {
276 return AudioStream::getBufferCapacityInFrames();
277 }
278 }
279
isXRunCountSupported() const280 bool AudioStreamBuffered::isXRunCountSupported() const {
281 // XRun count is only supported if we're using blocking I/O (not callbacks)
282 return (!isDataCallbackSpecified());
283 }
284
285 } // namespace oboe
286