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 <memory>
18
19 #include "OboeDebug.h"
20 #include "FilterAudioStream.h"
21
22 using namespace oboe;
23 using namespace flowgraph;
24
25 // Output callback uses FixedBlockReader::read()
26 // <= SourceFloatCaller::onProcess()
27 // <=== DataConversionFlowGraph::read()
28 // <== FilterAudioStream::onAudioReady()
29 //
30 // Output blocking uses no block adapter because AAudio can accept
31 // writes of any size. It uses DataConversionFlowGraph::read() <== FilterAudioStream::write() <= app
32 //
33 // Input callback uses FixedBlockWriter::write()
34 // <= DataConversionFlowGraph::write()
35 // <= FilterAudioStream::onAudioReady()
36 //
37 // Input blocking uses FixedBlockReader::read() // TODO may not need block adapter
38 // <= SourceFloatCaller::onProcess()
39 // <=== SinkFloat::read()
40 // <= DataConversionFlowGraph::read()
41 // <== FilterAudioStream::read()
42 // <= app
43
configureFlowGraph()44 Result FilterAudioStream::configureFlowGraph() {
45 mFlowGraph = std::make_unique<DataConversionFlowGraph>();
46 bool isOutput = getDirection() == Direction::Output;
47
48 AudioStream *sourceStream = isOutput ? this : mChildStream.get();
49 AudioStream *sinkStream = isOutput ? mChildStream.get() : this;
50
51 mRateScaler = ((double) getSampleRate()) / mChildStream->getSampleRate();
52
53 return mFlowGraph->configure(sourceStream, sinkStream);
54 }
55
56 // Put the data to be written at the source end of the flowgraph.
57 // Then read (pull) the data from the flowgraph and write it to the
58 // child stream.
write(const void * buffer,int32_t numFrames,int64_t timeoutNanoseconds)59 ResultWithValue<int32_t> FilterAudioStream::write(const void *buffer,
60 int32_t numFrames,
61 int64_t timeoutNanoseconds) {
62 int32_t framesWritten = 0;
63 mFlowGraph->setSource(buffer, numFrames);
64 while (true) {
65 int32_t numRead = mFlowGraph->read(mBlockingBuffer.get(),
66 getFramesPerBurst(),
67 timeoutNanoseconds);
68 if (numRead < 0) {
69 return ResultWithValue<int32_t>::createBasedOnSign(numRead);
70 }
71 if (numRead == 0) {
72 break; // finished processing the source buffer
73 }
74 auto writeResult = mChildStream->write(mBlockingBuffer.get(),
75 numRead,
76 timeoutNanoseconds);
77 if (!writeResult) {
78 return writeResult;
79 }
80 framesWritten += writeResult.value();
81 }
82 return ResultWithValue<int32_t>::createBasedOnSign(framesWritten);
83 }
84
85 // Read (pull) the data we want from the sink end of the flowgraph.
86 // The necessary data will be read from the child stream using a flowgraph callback.
read(void * buffer,int32_t numFrames,int64_t timeoutNanoseconds)87 ResultWithValue<int32_t> FilterAudioStream::read(void *buffer,
88 int32_t numFrames,
89 int64_t timeoutNanoseconds) {
90 int32_t framesRead = mFlowGraph->read(buffer, numFrames, timeoutNanoseconds);
91 return ResultWithValue<int32_t>::createBasedOnSign(framesRead);
92 }
93
onAudioReady(AudioStream * oboeStream,void * audioData,int32_t numFrames)94 DataCallbackResult FilterAudioStream::onAudioReady(AudioStream *oboeStream,
95 void *audioData,
96 int32_t numFrames) {
97 int32_t framesProcessed;
98 if (oboeStream->getDirection() == Direction::Output) {
99 framesProcessed = mFlowGraph->read(audioData, numFrames, 0 /* timeout */);
100 } else {
101 framesProcessed = mFlowGraph->write(audioData, numFrames);
102 }
103 return (framesProcessed < numFrames)
104 ? DataCallbackResult::Stop
105 : mFlowGraph->getDataCallbackResult();
106 }
107