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