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 25 namespace oboe { 26 27 /** 28 * FullDuplexStream can be used to synchronize an input and output stream. 29 * 30 * For the builder of the output stream, call setDataCallback() with this object. 31 * 32 * When both streams are ready, onAudioReady() of the output stream will call onBothStreamsReady(). 33 * Callers must override onBothStreamsReady(). 34 * 35 * To ensure best results, open an output stream before the input stream. 36 * Call inputBuilder.setBufferCapacityInFrames(mOutputStream->getBufferCapacityInFrames() * 2). 37 * Also, call inputBuilder.setSampleRate(mOutputStream->getSampleRate()). 38 * 39 * Callers must call setInputStream() and setOutputStream(). 40 * Call start() to start both streams and stop() to stop both streams. 41 * Caller is responsible for closing both streams. 42 * 43 * Callers should handle error callbacks with setErrorCallback() for the output stream. 44 * When an error callback occurs for the output stream, Oboe will stop and close the output stream. 45 * The caller is responsible for stopping and closing the input stream. 46 * The caller should also reopen and restart both streams when the error callback is ErrorDisconnected. 47 * See the LiveEffect sample as an example of this. 48 * 49 */ 50 class FullDuplexStream : public AudioStreamDataCallback { 51 public: FullDuplexStream()52 FullDuplexStream() {} 53 virtual ~FullDuplexStream() = default; 54 55 /** 56 * Sets the input stream. Calling this is mandatory. 57 * 58 * @param stream the output stream 59 */ setInputStream(AudioStream * stream)60 void setInputStream(AudioStream *stream) { 61 mInputStream = stream; 62 } 63 64 /** 65 * Gets the input stream 66 * 67 * @return the input stream 68 */ getInputStream()69 AudioStream *getInputStream() { 70 return mInputStream; 71 } 72 73 /** 74 * Sets the output stream. Calling this is mandatory. 75 * 76 * @param stream the output stream 77 */ setOutputStream(AudioStream * stream)78 void setOutputStream(AudioStream *stream) { 79 mOutputStream = stream; 80 } 81 82 /** 83 * Gets the output stream 84 * 85 * @return the output stream 86 */ getOutputStream()87 AudioStream *getOutputStream() { 88 return mOutputStream; 89 } 90 91 /** 92 * Attempts to start both streams. Please call setInputStream() and setOutputStream() before 93 * calling this function. 94 * 95 * @return result of the operation 96 */ start()97 virtual Result start() { 98 mCountCallbacksToDrain = kNumCallbacksToDrain; 99 mCountInputBurstsCushion = mNumInputBurstsCushion; 100 mCountCallbacksToDiscard = kNumCallbacksToDiscard; 101 102 // Determine maximum size that could possibly be called. 103 int32_t bufferSize = getOutputStream()->getBufferCapacityInFrames() 104 * getOutputStream()->getChannelCount(); 105 if (bufferSize > mBufferSize) { 106 mInputBuffer = std::make_unique<float[]>(bufferSize); 107 mBufferSize = bufferSize; 108 } 109 110 oboe::Result result = getInputStream()->requestStart(); 111 if (result != oboe::Result::OK) { 112 return result; 113 } 114 return getOutputStream()->requestStart(); 115 } 116 117 /** 118 * Stops both streams. Returns Result::OK if neither stream had an error during close. 119 * 120 * @return result of the operation 121 */ stop()122 virtual Result stop() { 123 Result outputResult = Result::OK; 124 Result inputResult = Result::OK; 125 if (getOutputStream()) { 126 outputResult = mOutputStream->requestStop(); 127 } 128 if (getInputStream()) { 129 inputResult = mInputStream->requestStop(); 130 } 131 if (outputResult != Result::OK) { 132 return outputResult; 133 } else { 134 return inputResult; 135 } 136 } 137 138 /** 139 * Reads input from the input stream. Callers should not call this directly as this is called 140 * in onAudioReady(). 141 * 142 * @param numFrames 143 * @return result of the operation 144 */ readInput(int32_t numFrames)145 virtual ResultWithValue<int32_t> readInput(int32_t numFrames) { 146 return getInputStream()->read(mInputBuffer.get(), numFrames, 0 /* timeout */); 147 } 148 149 /** 150 * Called when data is available on both streams. 151 * Caller should override this method. 152 * numInputFrames and numOutputFrames may be zero. 153 * 154 * @param inputData buffer containing input data 155 * @param numInputFrames number of input frames 156 * @param outputData a place to put output data 157 * @param numOutputFrames number of output frames 158 * @return DataCallbackResult::Continue or DataCallbackResult::Stop 159 */ 160 virtual DataCallbackResult onBothStreamsReady( 161 const void *inputData, 162 int numInputFrames, 163 void *outputData, 164 int numOutputFrames 165 ) = 0; 166 167 /** 168 * Called when the output stream is ready to process audio. 169 * This in return calls onBothStreamsReady() when data is available on both streams. 170 * Callers should call this function when the output stream is ready. 171 * Callers must override onBothStreamsReady(). 172 * 173 * @param audioStream pointer to the associated stream 174 * @param audioData a place to put output data 175 * @param numFrames number of frames to be processed 176 * @return DataCallbackResult::Continue or DataCallbackResult::Stop 177 * 178 */ onAudioReady(AudioStream *,void * audioData,int numFrames)179 DataCallbackResult onAudioReady( 180 AudioStream * /*audioStream*/, 181 void *audioData, 182 int numFrames) { 183 DataCallbackResult callbackResult = DataCallbackResult::Continue; 184 int32_t actualFramesRead = 0; 185 186 // Silence the output. 187 int32_t numBytes = numFrames * getOutputStream()->getBytesPerFrame(); 188 memset(audioData, 0 /* value */, numBytes); 189 190 if (mCountCallbacksToDrain > 0) { 191 // Drain the input. 192 int32_t totalFramesRead = 0; 193 do { 194 ResultWithValue<int32_t> result = readInput(numFrames); 195 if (!result) { 196 // Ignore errors because input stream may not be started yet. 197 break; 198 } 199 actualFramesRead = result.value(); 200 totalFramesRead += actualFramesRead; 201 } while (actualFramesRead > 0); 202 // Only counts if we actually got some data. 203 if (totalFramesRead > 0) { 204 mCountCallbacksToDrain--; 205 } 206 207 } else if (mCountInputBurstsCushion > 0) { 208 // Let the input fill up a bit so we are not so close to the write pointer. 209 mCountInputBurstsCushion--; 210 211 } else if (mCountCallbacksToDiscard > 0) { 212 mCountCallbacksToDiscard--; 213 // Ignore. Allow the input to reach to equilibrium with the output. 214 ResultWithValue<int32_t> resultAvailable = getInputStream()->getAvailableFrames(); 215 if (!resultAvailable) { 216 callbackResult = DataCallbackResult::Stop; 217 } else { 218 int32_t framesAvailable = resultAvailable.value(); 219 if (framesAvailable >= mMinimumFramesBeforeRead) { 220 ResultWithValue<int32_t> resultRead = readInput(numFrames); 221 if (!resultRead) { 222 callbackResult = DataCallbackResult::Stop; 223 } 224 } 225 } 226 } else { 227 int32_t framesRead = 0; 228 ResultWithValue<int32_t> resultAvailable = getInputStream()->getAvailableFrames(); 229 if (!resultAvailable) { 230 callbackResult = DataCallbackResult::Stop; 231 } else { 232 int32_t framesAvailable = resultAvailable.value(); 233 if (framesAvailable >= mMinimumFramesBeforeRead) { 234 // Read data into input buffer. 235 ResultWithValue<int32_t> resultRead = readInput(numFrames); 236 if (!resultRead) { 237 callbackResult = DataCallbackResult::Stop; 238 } else { 239 framesRead = resultRead.value(); 240 } 241 } 242 } 243 244 if (callbackResult == DataCallbackResult::Continue) { 245 callbackResult = onBothStreamsReady(mInputBuffer.get(), framesRead, 246 audioData, numFrames); 247 } 248 } 249 250 if (callbackResult == DataCallbackResult::Stop) { 251 getInputStream()->requestStop(); 252 } 253 254 return callbackResult; 255 } 256 257 /** 258 * 259 * This is a cushion between the DSP and the application processor cursors to prevent collisions. 260 * Typically 0 for latency measurements or 1 for glitch tests. 261 * 262 * @param numBursts number of bursts to leave in the input buffer as a cushion 263 */ setNumInputBurstsCushion(int32_t numBursts)264 void setNumInputBurstsCushion(int32_t numBursts) { 265 mNumInputBurstsCushion = numBursts; 266 } 267 268 /** 269 * Get the number of bursts left in the input buffer as a cushion. 270 * 271 * @return number of bursts in the input buffer as a cushion 272 */ getNumInputBurstsCushion()273 int32_t getNumInputBurstsCushion() const { 274 return mNumInputBurstsCushion; 275 } 276 277 /** 278 * Minimum number of frames in the input stream buffer before calling readInput(). 279 * 280 * @param numFrames number of bursts in the input buffer as a cushion 281 */ setMinimumFramesBeforeRead(int32_t numFrames)282 void setMinimumFramesBeforeRead(int32_t numFrames) { 283 mMinimumFramesBeforeRead = numFrames; 284 } 285 286 /** 287 * Gets the minimum number of frames in the input stream buffer before calling readInput(). 288 * 289 * @return minimum number of frames before reading 290 */ getMinimumFramesBeforeRead()291 int32_t getMinimumFramesBeforeRead() const { 292 return mMinimumFramesBeforeRead; 293 } 294 295 private: 296 297 // TODO add getters and setters 298 static constexpr int32_t kNumCallbacksToDrain = 20; 299 static constexpr int32_t kNumCallbacksToDiscard = 30; 300 301 // let input fill back up, usually 0 or 1 302 int32_t mNumInputBurstsCushion = 0; 303 int32_t mMinimumFramesBeforeRead = 0; 304 305 // We want to reach a state where the input buffer is empty and 306 // the output buffer is full. 307 // These are used in order. 308 // Drain several callback so that input is empty. 309 int32_t mCountCallbacksToDrain = kNumCallbacksToDrain; 310 // Let the input fill back up slightly so we don't run dry. 311 int32_t mCountInputBurstsCushion = mNumInputBurstsCushion; 312 // Discard some callbacks so the input and output reach equilibrium. 313 int32_t mCountCallbacksToDiscard = kNumCallbacksToDiscard; 314 315 AudioStream *mInputStream = nullptr; 316 AudioStream *mOutputStream = nullptr; 317 318 int32_t mBufferSize = 0; 319 std::unique_ptr<float[]> mInputBuffer; 320 }; 321 322 } // namespace oboe 323 324 #endif //OBOE_FULL_DUPLEX_STREAM_ 325