/* * Copyright 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "FullDuplexStream.h" oboe::DataCallbackResult FullDuplexStream::onAudioReady( oboe::AudioStream *outputStream, void *audioData, int numFrames) { oboe::DataCallbackResult callbackResult = oboe::DataCallbackResult::Continue; int32_t actualFramesRead = 0; // Silence the output. int32_t numBytes = numFrames * outputStream->getBytesPerFrame(); memset(audioData, 0 /* value */, numBytes); if (mCountCallbacksToDrain > 0) { // Drain the input. int32_t totalFramesRead = 0; do { oboe::ResultWithValue result = mInputStream->read(mInputBuffer.get(), numFrames, 0 /* timeout */); if (!result) { // Ignore errors because input stream may not be started yet. break; } actualFramesRead = result.value(); totalFramesRead += actualFramesRead; } while (actualFramesRead > 0); // Only counts if we actually got some data. if (totalFramesRead > 0) { mCountCallbacksToDrain--; } } else if (mCountInputBurstsCushion > 0) { // Let the input fill up a bit so we are not so close to the write pointer. mCountInputBurstsCushion--; } else if (mCountCallbacksToDiscard > 0) { // Ignore. Allow the input to reach to equilibrium with the output. oboe::ResultWithValue result = mInputStream->read(mInputBuffer.get(), numFrames, 0 /* timeout */); if (!result) { callbackResult = oboe::DataCallbackResult::Stop; } mCountCallbacksToDiscard--; } else { // Read data into input buffer. oboe::ResultWithValue result = mInputStream->read(mInputBuffer.get(), numFrames, 0 /* timeout */); if (!result) { callbackResult = oboe::DataCallbackResult::Stop; } else { int32_t framesRead = result.value(); callbackResult = onBothStreamsReady( mInputStream, mInputBuffer.get(), framesRead, mOutputStream, audioData, numFrames ); } } if (callbackResult == oboe::DataCallbackResult::Stop) { mInputStream->requestStop(); } return callbackResult; } oboe::Result FullDuplexStream::start() { mCountCallbacksToDrain = kNumCallbacksToDrain; mCountInputBurstsCushion = mNumInputBurstsCushion; mCountCallbacksToDiscard = kNumCallbacksToDiscard; // Determine maximum size that could possibly be called. int32_t bufferSize = mOutputStream->getBufferCapacityInFrames() * mOutputStream->getChannelCount(); if (bufferSize > mBufferSize) { mInputBuffer = std::make_unique(bufferSize); mBufferSize = bufferSize; } oboe::Result result = mInputStream->requestStart(); if (result != oboe::Result::OK) { return result; } return mOutputStream->requestStart(); } oboe::Result FullDuplexStream::stop() { oboe::Result outputResult = oboe::Result::OK; oboe::Result inputResult = oboe::Result::OK; if (mOutputStream) { outputResult = mOutputStream->requestStop(); } if (mInputStream) { inputResult = mInputStream->requestStop(); } if (outputResult != oboe::Result::OK) { return outputResult; } else { return inputResult; } } int32_t FullDuplexStream::getNumInputBurstsCushion() const { return mNumInputBurstsCushion; } void FullDuplexStream::setNumInputBurstsCushion(int32_t numBursts) { FullDuplexStream::mNumInputBurstsCushion = numBursts; }