Oboe
A library for creating real-time audio apps on Android
Loading...
Searching...
No Matches
FullDuplexStream.h
1/*
2 * Copyright 2023 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#ifndef OBOE_FULL_DUPLEX_STREAM_
18#define OBOE_FULL_DUPLEX_STREAM_
19
20#include <cstdint>
21#include "oboe/Definitions.h"
22#include "oboe/AudioStream.h"
23#include "oboe/AudioStreamCallback.h"
24
25namespace oboe {
26
51public:
53 virtual ~FullDuplexStream() = default;
54
62 mRawInputStream = stream;
63 }
64
70 void setSharedInputStream(std::shared_ptr<AudioStream> &stream) {
71 mSharedInputStream = stream;
72 }
73
81 if (mSharedInputStream) {
82 return mSharedInputStream.get();
83 } else {
84 return mRawInputStream;
85 }
86 }
87
95 mRawOutputStream = stream;
96 }
97
103 void setSharedOutputStream(std::shared_ptr<AudioStream> &stream) {
104 mSharedOutputStream = stream;
105 }
106
114 if (mSharedOutputStream) {
115 return mSharedOutputStream.get();
116 } else {
117 return mRawOutputStream;
118 }
119 }
120
127 virtual Result start() {
128 mCountCallbacksToDrain = kNumCallbacksToDrain;
129 mCountInputBurstsCushion = mNumInputBurstsCushion;
130 mCountCallbacksToDiscard = kNumCallbacksToDiscard;
131
132 // Determine maximum size that could possibly be called.
135 if (bufferSize > mBufferSize) {
136 mInputBuffer = std::make_unique<float[]>(bufferSize);
137 mBufferSize = bufferSize;
138 }
139
140 oboe::Result result = getInputStream()->requestStart();
141 if (result != oboe::Result::OK) {
142 return result;
143 }
144 return getOutputStream()->requestStart();
145 }
146
152 virtual Result stop() {
153 Result outputResult = Result::OK;
154 Result inputResult = Result::OK;
155 if (getOutputStream()) {
157 }
158 if (getInputStream()) {
160 }
161 if (outputResult != Result::OK) {
162 return outputResult;
163 } else {
164 return inputResult;
165 }
166 }
167
176 return getInputStream()->read(mInputBuffer.get(), numFrames, 0 /* timeout */);
177 }
178
190 virtual DataCallbackResult onBothStreamsReady(
191 const void *inputData,
192 int numInputFrames,
193 void *outputData,
195 ) = 0;
196
209 DataCallbackResult onAudioReady(
210 AudioStream * /*audioStream*/,
211 void *audioData,
212 int numFrames) {
213 DataCallbackResult callbackResult = DataCallbackResult::Continue;
215
216 // Silence the output.
218 memset(audioData, 0 /* value */, numBytes);
219
220 if (mCountCallbacksToDrain > 0) {
221 // Drain the input.
223 do {
225 if (!result) {
226 // Ignore errors because input stream may not be started yet.
227 break;
228 }
231 } while (actualFramesRead > 0);
232 // Only counts if we actually got some data.
233 if (totalFramesRead > 0) {
234 mCountCallbacksToDrain--;
235 }
236
237 } else if (mCountInputBurstsCushion > 0) {
238 // Let the input fill up a bit so we are not so close to the write pointer.
239 mCountInputBurstsCushion--;
240
241 } else if (mCountCallbacksToDiscard > 0) {
242 mCountCallbacksToDiscard--;
243 // Ignore. Allow the input to reach to equilibrium with the output.
245 if (!resultAvailable) {
246 callbackResult = DataCallbackResult::Stop;
247 } else {
249 if (framesAvailable >= mMinimumFramesBeforeRead) {
251 if (!resultRead) {
252 callbackResult = DataCallbackResult::Stop;
253 }
254 }
255 }
256 } else {
259 if (!resultAvailable) {
260 callbackResult = DataCallbackResult::Stop;
261 } else {
263 if (framesAvailable >= mMinimumFramesBeforeRead) {
264 // Read data into input buffer.
266 if (!resultRead) {
267 callbackResult = DataCallbackResult::Stop;
268 } else {
270 }
271 }
272 }
273
274 if (callbackResult == DataCallbackResult::Continue) {
275 callbackResult = onBothStreamsReady(mInputBuffer.get(), framesRead,
277 }
278 }
279
280 if (callbackResult == DataCallbackResult::Stop) {
282 }
283
284 return callbackResult;
285 }
286
295 mNumInputBurstsCushion = numBursts;
296 }
297
304 return mNumInputBurstsCushion;
305 }
306
313 mMinimumFramesBeforeRead = numFrames;
314 }
315
322 return mMinimumFramesBeforeRead;
323 }
324
325private:
326
327 // TODO add getters and setters
328 static constexpr int32_t kNumCallbacksToDrain = 20;
329 static constexpr int32_t kNumCallbacksToDiscard = 30;
330
331 // let input fill back up, usually 0 or 1
332 int32_t mNumInputBurstsCushion = 0;
333 int32_t mMinimumFramesBeforeRead = 0;
334
335 // We want to reach a state where the input buffer is empty and
336 // the output buffer is full.
337 // These are used in order.
338 // Drain several callback so that input is empty.
339 int32_t mCountCallbacksToDrain = kNumCallbacksToDrain;
340 // Let the input fill back up slightly so we don't run dry.
341 int32_t mCountInputBurstsCushion = mNumInputBurstsCushion;
342 // Discard some callbacks so the input and output reach equilibrium.
343 int32_t mCountCallbacksToDiscard = kNumCallbacksToDiscard;
344
345 AudioStream *mRawInputStream = nullptr;
346 AudioStream *mRawOutputStream = nullptr;
347 std::shared_ptr<AudioStream> mSharedInputStream;
348 std::shared_ptr<AudioStream> mSharedOutputStream;
349
350 int32_t mBufferSize = 0;
351 std::unique_ptr<float[]> mInputBuffer;
352};
353
354} // namespace oboe
355
356#endif //OBOE_FULL_DUPLEX_STREAM_
int32_t getChannelCount() const
Definition AudioStreamBase.h:53
virtual int32_t getBufferCapacityInFrames() const
Definition AudioStreamBase.h:91
Definition AudioStreamCallback.h:34
Definition AudioStream.h:42
virtual Result requestStart()=0
virtual ResultWithValue< int32_t > read(void *, int32_t, int64_t)
Definition AudioStream.h:384
int32_t getBytesPerFrame() const
Definition AudioStream.h:247
virtual Result requestStop()=0
ResultWithValue< int32_t > getAvailableFrames()
Definition FullDuplexStream.h:50
void setOutputStream(AudioStream *stream)
Definition FullDuplexStream.h:94
int32_t getMinimumFramesBeforeRead() const
Definition FullDuplexStream.h:321
virtual Result stop()
Definition FullDuplexStream.h:152
void setNumInputBurstsCushion(int32_t numBursts)
Definition FullDuplexStream.h:294
virtual ResultWithValue< int32_t > readInput(int32_t numFrames)
Definition FullDuplexStream.h:175
AudioStream * getOutputStream()
Definition FullDuplexStream.h:113
virtual DataCallbackResult onBothStreamsReady(const void *inputData, int numInputFrames, void *outputData, int numOutputFrames)=0
AudioStream * getInputStream()
Definition FullDuplexStream.h:80
DataCallbackResult onAudioReady(AudioStream *, void *audioData, int numFrames)
Definition FullDuplexStream.h:209
int32_t getNumInputBurstsCushion() const
Definition FullDuplexStream.h:303
void setInputStream(AudioStream *stream)
Definition FullDuplexStream.h:61
void setMinimumFramesBeforeRead(int32_t numFrames)
Definition FullDuplexStream.h:312
virtual Result start()
Definition FullDuplexStream.h:127
void setSharedOutputStream(std::shared_ptr< AudioStream > &stream)
Definition FullDuplexStream.h:103
void setSharedInputStream(std::shared_ptr< AudioStream > &stream)
Definition FullDuplexStream.h:70
Definition ResultWithValue.h:47
T value() const
Definition ResultWithValue.h:81