• 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 "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