1 /*
2  * Copyright 2019 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 "common/OboeDebug.h"
18 #include "FullDuplexStream.h"
19 
readInput(int32_t numFrames)20 oboe::ResultWithValue<int32_t>  FullDuplexStream::readInput(int32_t numFrames) {
21     oboe::ResultWithValue<int32_t> result = getInputStream()->read(
22             mInputConverter->getInputBuffer(),
23             numFrames,
24             0 /* timeout */);
25     if (result == oboe::Result::OK) {
26         int32_t numSamples = result.value() * getInputStream()->getChannelCount();
27         mInputConverter->convertInternalBuffers(numSamples);
28     }
29     return result;
30 }
31 
onAudioReady(oboe::AudioStream * outputStream,void * audioData,int numFrames)32 oboe::DataCallbackResult FullDuplexStream::onAudioReady(
33         oboe::AudioStream *outputStream,
34         void *audioData,
35         int numFrames) {
36     oboe::DataCallbackResult callbackResult = oboe::DataCallbackResult::Continue;
37     int32_t actualFramesRead = 0;
38 
39     // Silence the output.
40     int32_t numBytes = numFrames * outputStream->getBytesPerFrame();
41     memset(audioData, 0 /* value */, numBytes);
42 
43     if (mCountCallbacksToDrain > 0) {
44         // Drain the input.
45         int32_t totalFramesRead = 0;
46         do {
47             oboe::ResultWithValue<int32_t> result = readInput(numFrames);
48             if (!result) {
49                 // Ignore errors because input stream may not be started yet.
50                 break;
51             }
52             actualFramesRead = result.value();
53             totalFramesRead += actualFramesRead;
54         } while (actualFramesRead > 0);
55         // Only counts if we actually got some data.
56         if (totalFramesRead > 0) {
57             mCountCallbacksToDrain--;
58         }
59 
60     } else if (mCountInputBurstsCushion > 0) {
61         // Let the input fill up a bit so we are not so close to the write pointer.
62         mCountInputBurstsCushion--;
63 
64     } else if (mCountCallbacksToDiscard > 0) {
65         mCountCallbacksToDiscard--;
66         // Ignore. Allow the input to reach to equilibrium with the output.
67         oboe::ResultWithValue<int32_t> resultAvailable = getInputStream()->getAvailableFrames();
68         if (!resultAvailable) {
69             LOGE("%s() getAvailableFrames() returned %s\n",
70                     __func__, convertToText(resultAvailable.error()));
71             callbackResult = oboe::DataCallbackResult::Stop;
72         } else {
73             int32_t framesAvailable = resultAvailable.value();
74             if (framesAvailable >= mMinimumFramesBeforeRead) {
75                 oboe::ResultWithValue<int32_t> resultRead = readInput(numFrames);
76                 if (!resultRead) {
77                     LOGE("%s() read() returned %s\n", __func__, convertToText(resultRead.error()));
78                     callbackResult = oboe::DataCallbackResult::Stop;
79                 }
80             }
81         }
82     } else {
83         int32_t framesRead = 0;
84         oboe::ResultWithValue<int32_t> resultAvailable = getInputStream()->getAvailableFrames();
85         if (!resultAvailable) {
86             LOGE("%s() getAvailableFrames() returned %s\n", __func__, convertToText(resultAvailable.error()));
87             callbackResult = oboe::DataCallbackResult::Stop;
88         } else {
89             int32_t framesAvailable = resultAvailable.value();
90             if (framesAvailable >= mMinimumFramesBeforeRead) {
91                 // Read data into input buffer.
92                 oboe::ResultWithValue<int32_t> resultRead = readInput(numFrames);
93                 if (!resultRead) {
94                     LOGE("%s() read() returned %s\n", __func__, convertToText(resultRead.error()));
95                     callbackResult = oboe::DataCallbackResult::Stop;
96                 } else {
97                     framesRead = resultRead.value();
98                 }
99             }
100         }
101 
102         if (callbackResult == oboe::DataCallbackResult::Continue) {
103             callbackResult = onBothStreamsReady(
104                     (const float *) mInputConverter->getOutputBuffer(),
105                     framesRead,
106                     (float *) mOutputConverter->getInputBuffer(), numFrames);
107             mOutputConverter->convertFromInternalInput( audioData,
108                                        numFrames * getOutputStream()->getChannelCount());
109         }
110     }
111 
112     if (callbackResult == oboe::DataCallbackResult::Stop) {
113         getInputStream()->requestStop();
114     }
115 
116     return callbackResult;
117 }
118 
start()119 oboe::Result FullDuplexStream::start() {
120     mCountCallbacksToDrain = kNumCallbacksToDrain;
121     mCountInputBurstsCushion = mNumInputBurstsCushion;
122     mCountCallbacksToDiscard = kNumCallbacksToDiscard;
123 
124     // Determine maximum size that could possibly be called.
125     int32_t bufferSize = getOutputStream()->getBufferCapacityInFrames()
126             * getOutputStream()->getChannelCount();
127     mInputConverter = std::make_unique<FormatConverterBox>(bufferSize,
128             getInputStream()->getFormat(),
129             oboe::AudioFormat::Float);
130     mOutputConverter = std::make_unique<FormatConverterBox>(bufferSize,
131             oboe::AudioFormat::Float,
132             getOutputStream()->getFormat());
133 
134     oboe::Result result = getInputStream()->requestStart();
135     if (result != oboe::Result::OK) {
136         return result;
137     }
138     return getOutputStream()->requestStart();
139 }
140 
stop()141 oboe::Result FullDuplexStream::stop() {
142     getOutputStream()->requestStop(); // TODO result?
143     return getInputStream()->requestStop();
144 }
145 
getMNumInputBurstsCushion() const146 int32_t FullDuplexStream::getMNumInputBurstsCushion() const {
147     return mNumInputBurstsCushion;
148 }
149 
setMNumInputBurstsCushion(int32_t numBursts)150 void FullDuplexStream::setMNumInputBurstsCushion(int32_t numBursts) {
151     FullDuplexStream::mNumInputBurstsCushion = numBursts;
152 }
153