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 #include <thread>
18
19 #include <gtest/gtest.h>
20
21 #include <oboe/Oboe.h>
22
23 using namespace oboe;
24
25 static constexpr int kTimeToSleepMicros = 5 * 1000 * 1000; // 5 s
26
27 using TestFullDuplexStreamParams = std::tuple<AudioApi, PerformanceMode, AudioApi, PerformanceMode>;
28
29 class TestFullDuplexStream : public ::testing::Test,
30 public ::testing::WithParamInterface<TestFullDuplexStreamParams>,
31 public FullDuplexStream {
32 public:
onBothStreamsReady(const void * inputData,int numInputFrames,void * outputData,int numOutputFrames)33 DataCallbackResult onBothStreamsReady(
34 const void *inputData,
35 int numInputFrames,
36 void *outputData,
37 int numOutputFrames) override {
38 mCallbackCount++;
39 if (numInputFrames == numOutputFrames) {
40 mGoodCallbackCount++;
41 }
42 return DataCallbackResult::Continue;
43 }
44
45 protected:
46
openStream(AudioApi inputAudioApi,PerformanceMode inputPerfMode,AudioApi outputAudioApi,PerformanceMode outputPerfMode)47 void openStream(AudioApi inputAudioApi, PerformanceMode inputPerfMode,
48 AudioApi outputAudioApi, PerformanceMode outputPerfMode) {
49 mOutputBuilder.setDirection(Direction::Output);
50 if (mOutputBuilder.isAAudioRecommended()) {
51 mOutputBuilder.setAudioApi(outputAudioApi);
52 }
53 mOutputBuilder.setPerformanceMode(outputPerfMode);
54 mOutputBuilder.setChannelCount(1);
55 mOutputBuilder.setFormat(AudioFormat::Float);
56 mOutputBuilder.setDataCallback(this);
57
58 Result r = mOutputBuilder.openStream(&mOutputStream);
59 ASSERT_EQ(r, Result::OK) << "Failed to open output stream " << convertToText(r);
60
61 mInputBuilder.setDirection(Direction::Input);
62 if (mInputBuilder.isAAudioRecommended()) {
63 mInputBuilder.setAudioApi(inputAudioApi);
64 }
65 mInputBuilder.setPerformanceMode(inputPerfMode);
66 mInputBuilder.setChannelCount(1);
67 mInputBuilder.setFormat(AudioFormat::Float);
68 mInputBuilder.setBufferCapacityInFrames(mOutputStream->getBufferCapacityInFrames() * 2);
69 mInputBuilder.setSampleRate(mOutputStream->getSampleRate());
70
71 r = mInputBuilder.openStream(&mInputStream);
72 ASSERT_EQ(r, Result::OK) << "Failed to open input stream " << convertToText(r);
73
74 setInputStream(mInputStream);
75 setOutputStream(mOutputStream);
76 }
77
startStream()78 void startStream() {
79 Result r = start();
80 ASSERT_EQ(r, Result::OK) << "Failed to start streams " << convertToText(r);
81 }
82
stopStream()83 void stopStream() {
84 Result r = stop();
85 ASSERT_EQ(r, Result::OK) << "Failed to stop streams " << convertToText(r);
86 }
87
closeStream()88 void closeStream() {
89 Result r = mOutputStream->close();
90 ASSERT_EQ(r, Result::OK) << "Failed to close output stream " << convertToText(r);
91 setOutputStream(nullptr);
92 r = mInputStream->close();
93 ASSERT_EQ(r, Result::OK) << "Failed to close input stream " << convertToText(r);
94 setInputStream(nullptr);
95 }
96
checkXRuns()97 void checkXRuns() {
98 // Expect few xRuns with the use of full duplex stream
99 EXPECT_LT(mInputStream->getXRunCount().value(), 10);
100 EXPECT_LT(mOutputStream->getXRunCount().value(), 10);
101 }
102
checkInputAndOutputBufferSizesMatch()103 void checkInputAndOutputBufferSizesMatch() {
104 // Expect the large majority of callbacks to have the same sized input and output
105 EXPECT_GE(mGoodCallbackCount, mCallbackCount * 9 / 10);
106 }
107
108 AudioStreamBuilder mInputBuilder;
109 AudioStreamBuilder mOutputBuilder;
110 AudioStream *mInputStream = nullptr;
111 AudioStream *mOutputStream = nullptr;
112 std::atomic<int32_t> mCallbackCount{0};
113 std::atomic<int32_t> mGoodCallbackCount{0};
114 };
115
TEST_P(TestFullDuplexStream,VerifyFullDuplexStream)116 TEST_P(TestFullDuplexStream, VerifyFullDuplexStream) {
117 const AudioApi inputAudioApi = std::get<0>(GetParam());
118 const PerformanceMode inputPerformanceMode = std::get<1>(GetParam());
119 const AudioApi outputAudioApi = std::get<2>(GetParam());
120 const PerformanceMode outputPerformanceMode = std::get<3>(GetParam());
121
122 openStream(inputAudioApi, inputPerformanceMode, outputAudioApi, outputPerformanceMode);
123 startStream();
124 usleep(kTimeToSleepMicros);
125 checkXRuns();
126 checkInputAndOutputBufferSizesMatch();
127 stopStream();
128 closeStream();
129 }
130
131 INSTANTIATE_TEST_SUITE_P(
132 TestFullDuplexStreamTest,
133 TestFullDuplexStream,
134 ::testing::Values(
135 TestFullDuplexStreamParams({AudioApi::AAudio, PerformanceMode::LowLatency,
136 AudioApi::AAudio, PerformanceMode::LowLatency}),
137 TestFullDuplexStreamParams({AudioApi::AAudio, PerformanceMode::LowLatency,
138 AudioApi::AAudio, PerformanceMode::None}),
139 TestFullDuplexStreamParams({AudioApi::AAudio, PerformanceMode::LowLatency,
140 AudioApi::AAudio, PerformanceMode::PowerSaving}),
141 TestFullDuplexStreamParams({AudioApi::AAudio, PerformanceMode::LowLatency,
142 AudioApi::OpenSLES, PerformanceMode::LowLatency}),
143 TestFullDuplexStreamParams({AudioApi::AAudio, PerformanceMode::LowLatency,
144 AudioApi::OpenSLES, PerformanceMode::None}),
145 TestFullDuplexStreamParams({AudioApi::AAudio, PerformanceMode::LowLatency,
146 AudioApi::OpenSLES, PerformanceMode::PowerSaving}),
147 TestFullDuplexStreamParams({AudioApi::AAudio, PerformanceMode::None,
148 AudioApi::AAudio, PerformanceMode::LowLatency}),
149 TestFullDuplexStreamParams({AudioApi::AAudio, PerformanceMode::None,
150 AudioApi::AAudio, PerformanceMode::None}),
151 TestFullDuplexStreamParams({AudioApi::AAudio, PerformanceMode::None,
152 AudioApi::AAudio, PerformanceMode::PowerSaving}),
153 TestFullDuplexStreamParams({AudioApi::AAudio, PerformanceMode::None,
154 AudioApi::OpenSLES, PerformanceMode::LowLatency}),
155 TestFullDuplexStreamParams({AudioApi::AAudio, PerformanceMode::None,
156 AudioApi::OpenSLES, PerformanceMode::None}),
157 TestFullDuplexStreamParams({AudioApi::AAudio, PerformanceMode::None,
158 AudioApi::OpenSLES, PerformanceMode::PowerSaving}),
159 TestFullDuplexStreamParams({AudioApi::AAudio, PerformanceMode::PowerSaving,
160 AudioApi::AAudio, PerformanceMode::LowLatency}),
161 TestFullDuplexStreamParams({AudioApi::AAudio, PerformanceMode::PowerSaving,
162 AudioApi::AAudio, PerformanceMode::None}),
163 TestFullDuplexStreamParams({AudioApi::AAudio, PerformanceMode::PowerSaving,
164 AudioApi::AAudio, PerformanceMode::PowerSaving}),
165 TestFullDuplexStreamParams({AudioApi::AAudio, PerformanceMode::PowerSaving,
166 AudioApi::OpenSLES, PerformanceMode::LowLatency}),
167 TestFullDuplexStreamParams({AudioApi::AAudio, PerformanceMode::PowerSaving,
168 AudioApi::OpenSLES, PerformanceMode::None}),
169 TestFullDuplexStreamParams({AudioApi::AAudio, PerformanceMode::PowerSaving,
170 AudioApi::OpenSLES, PerformanceMode::PowerSaving}),
171 TestFullDuplexStreamParams({AudioApi::OpenSLES, PerformanceMode::LowLatency,
172 AudioApi::AAudio, PerformanceMode::LowLatency}),
173 TestFullDuplexStreamParams({AudioApi::OpenSLES, PerformanceMode::LowLatency,
174 AudioApi::AAudio, PerformanceMode::None}),
175 TestFullDuplexStreamParams({AudioApi::OpenSLES, PerformanceMode::LowLatency,
176 AudioApi::AAudio, PerformanceMode::PowerSaving}),
177 TestFullDuplexStreamParams({AudioApi::OpenSLES, PerformanceMode::LowLatency,
178 AudioApi::OpenSLES, PerformanceMode::LowLatency}),
179 TestFullDuplexStreamParams({AudioApi::OpenSLES, PerformanceMode::LowLatency,
180 AudioApi::OpenSLES, PerformanceMode::None}),
181 TestFullDuplexStreamParams({AudioApi::OpenSLES, PerformanceMode::LowLatency,
182 AudioApi::OpenSLES, PerformanceMode::PowerSaving}),
183 TestFullDuplexStreamParams({AudioApi::OpenSLES, PerformanceMode::None,
184 AudioApi::AAudio, PerformanceMode::LowLatency}),
185 TestFullDuplexStreamParams({AudioApi::OpenSLES, PerformanceMode::None,
186 AudioApi::AAudio, PerformanceMode::None}),
187 TestFullDuplexStreamParams({AudioApi::OpenSLES, PerformanceMode::None,
188 AudioApi::AAudio, PerformanceMode::PowerSaving}),
189 TestFullDuplexStreamParams({AudioApi::OpenSLES, PerformanceMode::None,
190 AudioApi::OpenSLES, PerformanceMode::LowLatency}),
191 TestFullDuplexStreamParams({AudioApi::OpenSLES, PerformanceMode::None,
192 AudioApi::OpenSLES, PerformanceMode::None}),
193 TestFullDuplexStreamParams({AudioApi::OpenSLES, PerformanceMode::None,
194 AudioApi::OpenSLES, PerformanceMode::PowerSaving}),
195 TestFullDuplexStreamParams({AudioApi::OpenSLES, PerformanceMode::PowerSaving,
196 AudioApi::AAudio, PerformanceMode::LowLatency}),
197 TestFullDuplexStreamParams({AudioApi::OpenSLES, PerformanceMode::PowerSaving,
198 AudioApi::AAudio, PerformanceMode::None}),
199 TestFullDuplexStreamParams({AudioApi::OpenSLES, PerformanceMode::PowerSaving,
200 AudioApi::AAudio, PerformanceMode::PowerSaving}),
201 TestFullDuplexStreamParams({AudioApi::OpenSLES, PerformanceMode::PowerSaving,
202 AudioApi::OpenSLES, PerformanceMode::LowLatency}),
203 TestFullDuplexStreamParams({AudioApi::OpenSLES, PerformanceMode::PowerSaving,
204 AudioApi::OpenSLES, PerformanceMode::None}),
205 TestFullDuplexStreamParams({AudioApi::OpenSLES, PerformanceMode::PowerSaving,
206 AudioApi::OpenSLES, PerformanceMode::PowerSaving})
207 )
208 );
209