1 #include <oboe/Oboe.h>/*
2 * Copyright 2018 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 <gtest/gtest.h>
18 #include <oboe/Oboe.h>
19 #include <android/api-level.h>
20
21 using namespace oboe;
22
23 class CallbackSizeMonitor : public AudioStreamCallback {
24 public:
onAudioReady(AudioStream * oboeStream,void * audioData,int32_t numFrames)25 DataCallbackResult onAudioReady(AudioStream *oboeStream, void *audioData, int32_t numFrames) override {
26 framesPerCallback = numFrames;
27 callbackCount++;
28 return DataCallbackResult::Continue;
29 }
30
31 // This is exposed publicly so that the number of frames per callback can be tested.
32 std::atomic<int32_t> framesPerCallback{0};
33 std::atomic<int32_t> callbackCount{0};
34 };
35
36 class StreamOpen : public ::testing::Test {
37
38 protected:
39
openStream()40 bool openStream() {
41 Result r = mBuilder.openStream(&mStream);
42 EXPECT_EQ(r, Result::OK) << "Failed to open stream " << convertToText(r);
43 EXPECT_EQ(0, openCount) << "Should start with a fresh object every time.";
44 openCount++;
45 return (r == Result::OK);
46 }
47
closeStream()48 void closeStream() {
49 if (mStream != nullptr) {
50 Result r = mStream->close();
51 if (r != Result::OK) {
52 FAIL() << "Failed to close stream. " << convertToText(r);
53 }
54 }
55 usleep(500 * 1000); // give previous stream time to settle
56 }
57
checkSampleRateConversionAdvancing(Direction direction)58 void checkSampleRateConversionAdvancing(Direction direction) {
59 CallbackSizeMonitor callback;
60
61 mBuilder.setDirection(direction);
62 mBuilder.setAudioApi(AudioApi::AAudio);
63 mBuilder.setCallback(&callback);
64 mBuilder.setPerformanceMode(PerformanceMode::LowLatency);
65 mBuilder.setSampleRate(44100);
66 mBuilder.setSampleRateConversionQuality(SampleRateConversionQuality::Medium);
67
68 openStream();
69
70 ASSERT_EQ(mStream->requestStart(), Result::OK);
71 int timeout = 20;
72 while (callback.framesPerCallback == 0 && timeout > 0) {
73 usleep(50 * 1000);
74 timeout--;
75 }
76 ASSERT_GT(callback.callbackCount, 0);
77 ASSERT_GT(callback.framesPerCallback, 0);
78 ASSERT_EQ(mStream->requestStop(), Result::OK);
79
80 closeStream();
81 }
82
83 AudioStreamBuilder mBuilder;
84 AudioStream *mStream = nullptr;
85 int32_t openCount = 0;
86
87 };
88
TEST_F(StreamOpen,ForOpenSLESDefaultSampleRateIsUsed)89 TEST_F(StreamOpen, ForOpenSLESDefaultSampleRateIsUsed){
90
91 DefaultStreamValues::SampleRate = 44100;
92 DefaultStreamValues::FramesPerBurst = 192;
93 mBuilder.setAudioApi(AudioApi::OpenSLES);
94 openStream();
95 ASSERT_EQ(mStream->getSampleRate(), 44100);
96 closeStream();
97 }
98
TEST_F(StreamOpen,ForOpenSLESDefaultFramesPerBurstIsUsed)99 TEST_F(StreamOpen, ForOpenSLESDefaultFramesPerBurstIsUsed){
100
101 DefaultStreamValues::SampleRate = 48000;
102 DefaultStreamValues::FramesPerBurst = 128; // used for low latency
103 mBuilder.setAudioApi(AudioApi::OpenSLES);
104 mBuilder.setPerformanceMode(PerformanceMode::LowLatency);
105 openStream();
106 ASSERT_EQ(mStream->getFramesPerBurst(), 128);
107 closeStream();
108 }
109
TEST_F(StreamOpen,ForOpenSLESDefaultChannelCountIsUsed)110 TEST_F(StreamOpen, ForOpenSLESDefaultChannelCountIsUsed){
111
112 DefaultStreamValues::ChannelCount = 1;
113 mBuilder.setAudioApi(AudioApi::OpenSLES);
114 openStream();
115 ASSERT_EQ(mStream->getChannelCount(), 1);
116 closeStream();
117 }
118
TEST_F(StreamOpen,OutputForOpenSLESPerformanceModeShouldBeNone)119 TEST_F(StreamOpen, OutputForOpenSLESPerformanceModeShouldBeNone){
120 // We will not get a LowLatency stream if we request 16000 Hz.
121 mBuilder.setSampleRate(16000);
122 mBuilder.setPerformanceMode(PerformanceMode::LowLatency);
123 mBuilder.setDirection(Direction::Output);
124 mBuilder.setAudioApi(AudioApi::OpenSLES);
125 openStream();
126 ASSERT_EQ((int)mStream->getPerformanceMode(), (int)PerformanceMode::None);
127 closeStream();
128 }
129
TEST_F(StreamOpen,InputForOpenSLESPerformanceModeShouldBeNone)130 TEST_F(StreamOpen, InputForOpenSLESPerformanceModeShouldBeNone){
131 // We will not get a LowLatency stream if we request 16000 Hz.
132 mBuilder.setSampleRate(16000);
133 mBuilder.setPerformanceMode(PerformanceMode::LowLatency);
134 mBuilder.setDirection(Direction::Input);
135 mBuilder.setAudioApi(AudioApi::OpenSLES);
136 openStream();
137 ASSERT_EQ((int)mStream->getPerformanceMode(), (int)PerformanceMode::None);
138 closeStream();
139 }
140
TEST_F(StreamOpen,ForOpenSlesIllegalFormatRejectedOutput)141 TEST_F(StreamOpen, ForOpenSlesIllegalFormatRejectedOutput) {
142 mBuilder.setAudioApi(AudioApi::OpenSLES);
143 mBuilder.setPerformanceMode(PerformanceMode::LowLatency);
144 mBuilder.setFormat(static_cast<AudioFormat>(666));
145 Result r = mBuilder.openStream(&mStream);
146 EXPECT_NE(r, Result::OK) << "Should not open stream " << convertToText(r);
147 if (mStream != nullptr) {
148 mStream->close(); // just in case it accidentally opened
149 }
150 }
151
TEST_F(StreamOpen,ForOpenSlesIllegalFormatRejectedInput)152 TEST_F(StreamOpen, ForOpenSlesIllegalFormatRejectedInput) {
153 mBuilder.setAudioApi(AudioApi::OpenSLES);
154 mBuilder.setPerformanceMode(PerformanceMode::LowLatency);
155 mBuilder.setDirection(Direction::Input);
156 mBuilder.setFormat(static_cast<AudioFormat>(666));
157 Result r = mBuilder.openStream(&mStream);
158 EXPECT_NE(r, Result::OK) << "Should not open stream " << convertToText(r);
159 if (mStream != nullptr) {
160 mStream->close(); // just in case it accidentally opened
161 }
162 }
163
164 // Make sure the callback is called with the requested FramesPerCallback
TEST_F(StreamOpen,OpenSLESFramesPerCallback)165 TEST_F(StreamOpen, OpenSLESFramesPerCallback) {
166 const int kRequestedFramesPerCallback = 417;
167 CallbackSizeMonitor callback;
168
169 DefaultStreamValues::SampleRate = 48000;
170 DefaultStreamValues::ChannelCount = 2;
171 DefaultStreamValues::FramesPerBurst = 192;
172 mBuilder.setAudioApi(AudioApi::OpenSLES);
173 mBuilder.setFramesPerCallback(kRequestedFramesPerCallback);
174 mBuilder.setCallback(&callback);
175 openStream();
176 ASSERT_EQ(mStream->requestStart(), Result::OK);
177 int timeout = 20;
178 while (callback.framesPerCallback == 0 && timeout > 0) {
179 usleep(50 * 1000);
180 timeout--;
181 }
182 ASSERT_EQ(kRequestedFramesPerCallback, callback.framesPerCallback);
183 ASSERT_EQ(kRequestedFramesPerCallback, mStream->getFramesPerCallback());
184 ASSERT_EQ(mStream->requestStop(), Result::OK);
185 closeStream();
186 }
187
188 /* TODO - This is hanging!
189 // Make sure the LowLatency callback has the requested FramesPerCallback.
190 TEST_F(StreamOpen, AAudioFramesPerCallbackLowLatency) {
191 const int kRequestedFramesPerCallback = 192;
192 CallbackSizeMonitor callback;
193
194 mBuilder.setAudioApi(AudioApi::AAudio);
195 mBuilder.setFramesPerCallback(kRequestedFramesPerCallback);
196 mBuilder.setCallback(&callback);
197 mBuilder.setPerformanceMode(PerformanceMode::LowLatency);
198 openStream();
199 ASSERT_EQ(kRequestedFramesPerCallback, mStream->getFramesPerCallback());
200 ASSERT_EQ(mStream->requestStart(), Result::OK);
201 int timeout = 20;
202 while (callback.framesPerCallback == 0 && timeout > 0) {
203 usleep(50 * 1000);
204 timeout--;
205 }
206 ASSERT_EQ(kRequestedFramesPerCallback, callback.framesPerCallback);
207 ASSERT_EQ(mStream->requestStop(), Result::OK);
208 closeStream();
209 }
210 */
211
212 /* TODO - This is hanging!
213 // Make sure the regular callback has the requested FramesPerCallback.
214 TEST_F(StreamOpen, AAudioFramesPerCallbackNone) {
215 const int kRequestedFramesPerCallback = 1024;
216 CallbackSizeMonitor callback;
217
218 mBuilder.setAudioApi(AudioApi::AAudio);
219 mBuilder.setFramesPerCallback(kRequestedFramesPerCallback);
220 mBuilder.setCallback(&callback);
221 mBuilder.setPerformanceMode(PerformanceMode::None);
222 openStream();
223 ASSERT_EQ(kRequestedFramesPerCallback, mStream->getFramesPerCallback());
224 ASSERT_EQ(mStream->setBufferSizeInFrames(mStream->getBufferCapacityInFrames()), Result::OK);
225 ASSERT_EQ(mStream->requestStart(), Result::OK);
226 int timeout = 20;
227 while (callback.framesPerCallback == 0 && timeout > 0) {
228 usleep(50 * 1000);
229 timeout--;
230 }
231 ASSERT_EQ(kRequestedFramesPerCallback, callback.framesPerCallback);
232 ASSERT_EQ(mStream->requestStop(), Result::OK);
233 closeStream();
234 }
235 */
236
TEST_F(StreamOpen,RecordingFormatUnspecifiedReturnsI16BeforeMarshmallow)237 TEST_F(StreamOpen, RecordingFormatUnspecifiedReturnsI16BeforeMarshmallow){
238
239 if (getSdkVersion() < __ANDROID_API_M__){
240 mBuilder.setDirection(Direction::Input);
241 mBuilder.setFormat(AudioFormat::Unspecified);
242 openStream();
243 ASSERT_EQ(mStream->getFormat(), AudioFormat::I16);
244 closeStream();
245 }
246 }
247
TEST_F(StreamOpen,RecordingFormatUnspecifiedReturnsFloatOnMarshmallowAndLater)248 TEST_F(StreamOpen, RecordingFormatUnspecifiedReturnsFloatOnMarshmallowAndLater){
249
250 if (getSdkVersion() >= __ANDROID_API_M__){
251 mBuilder.setDirection(Direction::Input);
252 mBuilder.setFormat(AudioFormat::Unspecified);
253 openStream();
254 ASSERT_EQ(mStream->getFormat(), AudioFormat::Float);
255 closeStream();
256 }
257 }
258
TEST_F(StreamOpen,RecordingFormatFloatReturnsErrorBeforeMarshmallow)259 TEST_F(StreamOpen, RecordingFormatFloatReturnsErrorBeforeMarshmallow){
260
261 if (getSdkVersion() < __ANDROID_API_M__){
262 mBuilder.setDirection(Direction::Input);
263 mBuilder.setFormat(AudioFormat::Float);
264 Result r = mBuilder.openStream(&mStream);
265 ASSERT_EQ(r, Result::ErrorInvalidFormat) << convertToText(r);
266 closeStream();
267 }
268 }
269
TEST_F(StreamOpen,RecordingFormatFloatReturnsFloatOnMarshmallowAndLater)270 TEST_F(StreamOpen, RecordingFormatFloatReturnsFloatOnMarshmallowAndLater){
271
272 if (getSdkVersion() >= __ANDROID_API_M__){
273 mBuilder.setDirection(Direction::Input);
274 mBuilder.setFormat(AudioFormat::Float);
275 openStream();
276 ASSERT_EQ(mStream->getFormat(), AudioFormat::Float);
277 closeStream();
278 }
279 }
280
TEST_F(StreamOpen,RecordingFormatI16ReturnsI16)281 TEST_F(StreamOpen, RecordingFormatI16ReturnsI16){
282
283 mBuilder.setDirection(Direction::Input);
284 mBuilder.setFormat(AudioFormat::I16);
285 openStream();
286 ASSERT_EQ(mStream->getFormat(), AudioFormat::I16);
287 closeStream();
288 }
289
TEST_F(StreamOpen,PlaybackFormatUnspecifiedReturnsI16BeforeLollipop)290 TEST_F(StreamOpen, PlaybackFormatUnspecifiedReturnsI16BeforeLollipop){
291
292 if (getSdkVersion() < __ANDROID_API_L__){
293 mBuilder.setDirection(Direction::Output);
294 mBuilder.setFormat(AudioFormat::Unspecified);
295 openStream();
296 ASSERT_EQ(mStream->getFormat(), AudioFormat::I16);
297 closeStream();
298 }
299 }
300
TEST_F(StreamOpen,PlaybackFormatUnspecifiedReturnsFloatOnLollipopAndLater)301 TEST_F(StreamOpen, PlaybackFormatUnspecifiedReturnsFloatOnLollipopAndLater){
302
303 if (getSdkVersion() >= __ANDROID_API_L__){
304 mBuilder.setDirection(Direction::Output);
305 mBuilder.setFormat(AudioFormat::Unspecified);
306 openStream();
307 ASSERT_EQ(mStream->getFormat(), AudioFormat::Float);
308 closeStream();
309 }
310 }
311
TEST_F(StreamOpen,PlaybackFormatFloatReturnsErrorBeforeLollipop)312 TEST_F(StreamOpen, PlaybackFormatFloatReturnsErrorBeforeLollipop){
313
314 if (getSdkVersion() < __ANDROID_API_L__){
315 mBuilder.setDirection(Direction::Output);
316 mBuilder.setFormat(AudioFormat::Float);
317 Result r = mBuilder.openStream(&mStream);
318 ASSERT_EQ(r, Result::ErrorInvalidFormat);
319 closeStream();
320 }
321 }
322
TEST_F(StreamOpen,PlaybackFormatFloatReturnsFloatOnLollipopAndLater)323 TEST_F(StreamOpen, PlaybackFormatFloatReturnsFloatOnLollipopAndLater){
324
325 if (getSdkVersion() >= __ANDROID_API_L__){
326 mBuilder.setDirection(Direction::Output);
327 mBuilder.setFormat(AudioFormat::Float);
328 openStream();
329 ASSERT_EQ(mStream->getFormat(), AudioFormat::Float);
330 closeStream();
331 }
332 }
333
TEST_F(StreamOpen,PlaybackFormatI16ReturnsI16)334 TEST_F(StreamOpen, PlaybackFormatI16ReturnsI16) {
335 mBuilder.setDirection(Direction::Output);
336 mBuilder.setFormat(AudioFormat::I16);
337 openStream();
338 ASSERT_EQ(mStream->getFormat(), AudioFormat::I16);
339 closeStream();
340 }
341
TEST_F(StreamOpen,OpenCloseLowLatencyStream)342 TEST_F(StreamOpen, OpenCloseLowLatencyStream){
343 mBuilder.setDirection(Direction::Output);
344 mBuilder.setPerformanceMode(PerformanceMode::LowLatency);
345 float *buf = new float[100];
346 openStream();
347 delete[] buf;
348 closeStream();
349 }
350
TEST_F(StreamOpen,LowLatencyStreamHasSmallBufferSize)351 TEST_F(StreamOpen, LowLatencyStreamHasSmallBufferSize){
352
353 if (mBuilder.isAAudioRecommended()) {
354 mBuilder.setDirection(Direction::Output);
355 mBuilder.setPerformanceMode(PerformanceMode::LowLatency);
356 openStream();
357 int32_t bufferSize = mStream->getBufferSizeInFrames();
358 int32_t burst = mStream->getFramesPerBurst();
359 closeStream();
360 ASSERT_LE(bufferSize, burst * 3);
361 }
362 }
363
364 // See if sample rate conversion by Oboe is calling the callback.
TEST_F(StreamOpen,AAudioOutputSampleRate44100)365 TEST_F(StreamOpen, AAudioOutputSampleRate44100) {
366 checkSampleRateConversionAdvancing(Direction::Output);
367 }
368
369 // See if sample rate conversion by Oboe is calling the callback.
TEST_F(StreamOpen,AAudioInputSampleRate44100)370 TEST_F(StreamOpen, AAudioInputSampleRate44100) {
371 checkSampleRateConversionAdvancing(Direction::Input);
372 }