• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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