1 /*
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 #ifndef __ANDROID_API_S__
22 #define __ANDROID_API_S__ 31
23 #endif
24
25 using namespace oboe;
26
27 class CallbackSizeMonitor : public AudioStreamCallback {
28 public:
onAudioReady(AudioStream * oboeStream,void * audioData,int32_t numFrames)29 DataCallbackResult onAudioReady(AudioStream *oboeStream, void *audioData, int32_t numFrames) override {
30 framesPerCallback = numFrames;
31 callbackCount++;
32 return DataCallbackResult::Continue;
33 }
34
35 // This is exposed publicly so that the number of frames per callback can be tested.
36 std::atomic<int32_t> framesPerCallback{0};
37 std::atomic<int32_t> callbackCount{0};
38 };
39
40 class StreamOpen : public ::testing::Test {
41
42 protected:
43
openStream()44 bool openStream() {
45 EXPECT_EQ(mStream, nullptr);
46 Result r = mBuilder.openStream(&mStream);
47 EXPECT_EQ(r, Result::OK) << "Failed to open stream " << convertToText(r);
48 EXPECT_EQ(0, openCount) << "Should start with a fresh object every time.";
49 openCount++;
50 return (r == Result::OK);
51 }
52
closeStream()53 bool closeStream() {
54 if (mStream != nullptr){
55 Result r = mStream->close();
56 EXPECT_EQ(r, Result::OK) << "Failed to close stream. " << convertToText(r);
57 usleep(500 * 1000); // give previous stream time to settle
58 mStream = nullptr;
59 return (r == Result::OK);
60 } else {
61 return true;
62 }
63 }
64
checkSampleRateConversionAdvancing(Direction direction)65 void checkSampleRateConversionAdvancing(Direction direction) {
66 CallbackSizeMonitor callback;
67
68 mBuilder.setDirection(direction);
69 if (mBuilder.isAAudioRecommended()) {
70 mBuilder.setAudioApi(AudioApi::AAudio);
71 }
72 mBuilder.setCallback(&callback);
73 mBuilder.setPerformanceMode(PerformanceMode::LowLatency);
74 mBuilder.setSampleRate(44100);
75 mBuilder.setSampleRateConversionQuality(SampleRateConversionQuality::Medium);
76
77 ASSERT_TRUE(openStream());
78
79 ASSERT_EQ(mStream->requestStart(), Result::OK);
80 int timeout = 20;
81 while (callback.framesPerCallback == 0 && timeout > 0) {
82 usleep(50 * 1000);
83 timeout--;
84 }
85
86 // Catch Issue #1166
87 mStream->getTimestamp(CLOCK_MONOTONIC); // should not crash
88 mStream->getTimestamp(CLOCK_MONOTONIC, nullptr, nullptr); // should not crash
89
90 ASSERT_GT(callback.callbackCount, 0);
91 ASSERT_GT(callback.framesPerCallback, 0);
92 ASSERT_EQ(mStream->requestStop(), Result::OK);
93
94 ASSERT_TRUE(closeStream());
95 }
96
97 AudioStreamBuilder mBuilder;
98 AudioStream *mStream = nullptr;
99 int32_t openCount = 0;
100
101 };
102
103 class StreamOpenOutput : public StreamOpen {};
104 class StreamOpenInput : public StreamOpen {};
105
TEST_F(StreamOpenOutput,ForOpenSLESDefaultSampleRateIsUsed)106 TEST_F(StreamOpenOutput, ForOpenSLESDefaultSampleRateIsUsed){
107
108 DefaultStreamValues::SampleRate = 44100;
109 DefaultStreamValues::FramesPerBurst = 192;
110 mBuilder.setAudioApi(AudioApi::OpenSLES);
111 ASSERT_TRUE(openStream());
112 ASSERT_EQ(mStream->getSampleRate(), 44100);
113 ASSERT_TRUE(closeStream());
114 }
115
TEST_F(StreamOpenOutput,ForOpenSLESDefaultFramesPerBurstIsUsed)116 TEST_F(StreamOpenOutput, ForOpenSLESDefaultFramesPerBurstIsUsed){
117
118 DefaultStreamValues::SampleRate = 48000;
119 DefaultStreamValues::FramesPerBurst = 128; // used for low latency
120 mBuilder.setAudioApi(AudioApi::OpenSLES);
121 mBuilder.setPerformanceMode(PerformanceMode::LowLatency);
122 ASSERT_TRUE(openStream());
123 // Some devices like emulators may not support Low Latency
124 if (mStream->getPerformanceMode() == PerformanceMode::LowLatency) {
125 ASSERT_EQ(mStream->getFramesPerBurst(), 128);
126 }
127 ASSERT_TRUE(closeStream());
128 }
129
TEST_F(StreamOpenOutput,ForOpenSLESDefaultChannelCountIsUsed)130 TEST_F(StreamOpenOutput, ForOpenSLESDefaultChannelCountIsUsed){
131
132 DefaultStreamValues::ChannelCount = 1;
133 mBuilder.setAudioApi(AudioApi::OpenSLES);
134 ASSERT_TRUE(openStream());
135 ASSERT_EQ(mStream->getChannelCount(), 1);
136 ASSERT_TRUE(closeStream());
137 }
138
TEST_F(StreamOpenOutput,OutputForOpenSLESPerformanceModeShouldBeNone)139 TEST_F(StreamOpenOutput, OutputForOpenSLESPerformanceModeShouldBeNone){
140 // We will not get a LowLatency stream if we request 16000 Hz.
141 mBuilder.setSampleRate(16000);
142 mBuilder.setPerformanceMode(PerformanceMode::LowLatency);
143 mBuilder.setDirection(Direction::Output);
144 mBuilder.setAudioApi(AudioApi::OpenSLES);
145 ASSERT_TRUE(openStream());
146 ASSERT_EQ((int)mStream->getPerformanceMode(), (int)PerformanceMode::None);
147 ASSERT_TRUE(closeStream());
148 }
149
TEST_F(StreamOpenInput,InputForOpenSLESPerformanceModeShouldBeNone)150 TEST_F(StreamOpenInput, InputForOpenSLESPerformanceModeShouldBeNone){
151 // We will not get a LowLatency stream if we request 16000 Hz.
152 mBuilder.setSampleRate(16000);
153 mBuilder.setPerformanceMode(PerformanceMode::LowLatency);
154 mBuilder.setDirection(Direction::Input);
155 mBuilder.setAudioApi(AudioApi::OpenSLES);
156 ASSERT_TRUE(openStream());
157 ASSERT_EQ((int)mStream->getPerformanceMode(), (int)PerformanceMode::None);
158 ASSERT_TRUE(closeStream());
159 }
160
TEST_F(StreamOpenOutput,ForOpenSlesIllegalFormatRejectedOutput)161 TEST_F(StreamOpenOutput, ForOpenSlesIllegalFormatRejectedOutput) {
162 mBuilder.setAudioApi(AudioApi::OpenSLES);
163 mBuilder.setPerformanceMode(PerformanceMode::LowLatency);
164 mBuilder.setFormat(static_cast<AudioFormat>(666));
165 Result r = mBuilder.openStream(&mStream);
166 EXPECT_NE(r, Result::OK) << "Should not open stream " << convertToText(r);
167 if (mStream != nullptr) {
168 mStream->close(); // just in case it accidentally opened
169 }
170 }
171
TEST_F(StreamOpenInput,ForOpenSlesIllegalFormatRejectedInput)172 TEST_F(StreamOpenInput, ForOpenSlesIllegalFormatRejectedInput) {
173 mBuilder.setAudioApi(AudioApi::OpenSLES);
174 mBuilder.setPerformanceMode(PerformanceMode::LowLatency);
175 mBuilder.setDirection(Direction::Input);
176 mBuilder.setFormat(static_cast<AudioFormat>(666));
177 Result r = mBuilder.openStream(&mStream);
178 EXPECT_NE(r, Result::OK) << "Should not open stream " << convertToText(r);
179 if (mStream != nullptr) {
180 mStream->close(); // just in case it accidentally opened
181 }
182 }
183
184 // Make sure the callback is called with the requested FramesPerCallback
TEST_F(StreamOpenOutput,OpenSLESFramesPerCallback)185 TEST_F(StreamOpenOutput, OpenSLESFramesPerCallback) {
186 const int kRequestedFramesPerCallback = 417;
187 CallbackSizeMonitor callback;
188
189 DefaultStreamValues::SampleRate = 48000;
190 DefaultStreamValues::ChannelCount = 2;
191 DefaultStreamValues::FramesPerBurst = 192;
192 mBuilder.setAudioApi(AudioApi::OpenSLES);
193 mBuilder.setFramesPerCallback(kRequestedFramesPerCallback);
194 mBuilder.setCallback(&callback);
195 ASSERT_TRUE(openStream());
196 ASSERT_EQ(mStream->requestStart(), Result::OK);
197 int timeout = 20;
198 while (callback.framesPerCallback == 0 && timeout > 0) {
199 usleep(50 * 1000);
200 timeout--;
201 }
202 ASSERT_EQ(kRequestedFramesPerCallback, callback.framesPerCallback);
203 ASSERT_EQ(kRequestedFramesPerCallback, mStream->getFramesPerCallback());
204 ASSERT_EQ(mStream->requestStop(), Result::OK);
205 ASSERT_TRUE(closeStream());
206 }
207
208 // Make sure the LowLatency callback has the requested FramesPerCallback.
TEST_F(StreamOpen,AAudioFramesPerCallbackLowLatency)209 TEST_F(StreamOpen, AAudioFramesPerCallbackLowLatency) {
210 const int kRequestedFramesPerCallback = 192;
211 CallbackSizeMonitor callback;
212
213 mBuilder.setAudioApi(AudioApi::AAudio);
214 mBuilder.setFramesPerCallback(kRequestedFramesPerCallback);
215 mBuilder.setCallback(&callback);
216 mBuilder.setPerformanceMode(PerformanceMode::LowLatency);
217 ASSERT_TRUE(openStream());
218 ASSERT_EQ(kRequestedFramesPerCallback, mStream->getFramesPerCallback());
219 ASSERT_EQ(mStream->requestStart(), Result::OK);
220 int timeout = 20;
221 while (callback.framesPerCallback == 0 && timeout > 0) {
222 usleep(50 * 1000);
223 timeout--;
224 }
225 ASSERT_EQ(kRequestedFramesPerCallback, callback.framesPerCallback);
226 ASSERT_EQ(mStream->requestStop(), Result::OK);
227 ASSERT_TRUE(closeStream());
228 }
229
230 // Make sure the regular callback has the requested FramesPerCallback.
TEST_F(StreamOpen,AAudioFramesPerCallbackNone)231 TEST_F(StreamOpen, AAudioFramesPerCallbackNone) {
232 const int kRequestedFramesPerCallback = 1024;
233 CallbackSizeMonitor callback;
234
235 mBuilder.setAudioApi(AudioApi::AAudio);
236 mBuilder.setFramesPerCallback(kRequestedFramesPerCallback);
237 mBuilder.setCallback(&callback);
238 mBuilder.setPerformanceMode(PerformanceMode::None);
239 ASSERT_TRUE(openStream());
240 ASSERT_EQ(kRequestedFramesPerCallback, mStream->getFramesPerCallback());
241 ASSERT_EQ(mStream->requestStart(), Result::OK);
242 int timeout = 20;
243 while (callback.framesPerCallback == 0 && timeout > 0) {
244 usleep(50 * 1000);
245 timeout--;
246 }
247 ASSERT_EQ(kRequestedFramesPerCallback, callback.framesPerCallback);
248 ASSERT_EQ(mStream->requestStop(), Result::OK);
249 ASSERT_TRUE(closeStream());
250 }
251
TEST_F(StreamOpenInput,RecordingFormatUnspecifiedReturnsI16BeforeMarshmallow)252 TEST_F(StreamOpenInput, RecordingFormatUnspecifiedReturnsI16BeforeMarshmallow){
253
254 if (getSdkVersion() < __ANDROID_API_M__){
255 mBuilder.setDirection(Direction::Input);
256 mBuilder.setFormat(AudioFormat::Unspecified);
257 ASSERT_TRUE(openStream());
258 ASSERT_EQ(mStream->getFormat(), AudioFormat::I16);
259 ASSERT_TRUE(closeStream());
260 }
261 }
262
TEST_F(StreamOpenInput,RecordingFormatUnspecifiedReturnsFloatOnMarshmallowAndLater)263 TEST_F(StreamOpenInput, RecordingFormatUnspecifiedReturnsFloatOnMarshmallowAndLater){
264
265 if (getSdkVersion() >= __ANDROID_API_M__){
266 mBuilder.setDirection(Direction::Input);
267 mBuilder.setFormat(AudioFormat::Unspecified);
268 ASSERT_TRUE(openStream());
269 ASSERT_EQ(mStream->getFormat(), AudioFormat::Float);
270 ASSERT_TRUE(closeStream());
271 }
272 }
273
TEST_F(StreamOpenInput,RecordingFormatFloatReturnsErrorBeforeMarshmallow)274 TEST_F(StreamOpenInput, RecordingFormatFloatReturnsErrorBeforeMarshmallow){
275
276 if (getSdkVersion() < __ANDROID_API_M__){
277 mBuilder.setDirection(Direction::Input);
278 mBuilder.setFormat(AudioFormat::Float);
279 Result r = mBuilder.openStream(&mStream);
280 ASSERT_EQ(r, Result::ErrorInvalidFormat) << convertToText(r);
281 ASSERT_TRUE(closeStream());
282 }
283 }
284
TEST_F(StreamOpenInput,RecordingFormatFloatReturnsFloatOnMarshmallowAndLater)285 TEST_F(StreamOpenInput, RecordingFormatFloatReturnsFloatOnMarshmallowAndLater){
286
287 if (getSdkVersion() >= __ANDROID_API_M__){
288 mBuilder.setDirection(Direction::Input);
289 mBuilder.setFormat(AudioFormat::Float);
290 ASSERT_TRUE(openStream());
291 ASSERT_EQ(mStream->getFormat(), AudioFormat::Float);
292 ASSERT_TRUE(closeStream());
293 }
294 }
295
TEST_F(StreamOpenInput,RecordingFormatI16ReturnsI16)296 TEST_F(StreamOpenInput, RecordingFormatI16ReturnsI16){
297
298 mBuilder.setDirection(Direction::Input);
299 mBuilder.setFormat(AudioFormat::I16);
300 ASSERT_TRUE(openStream());
301 ASSERT_EQ(mStream->getFormat(), AudioFormat::I16);
302 ASSERT_TRUE(closeStream());
303 }
304
TEST_F(StreamOpenOutput,PlaybackFormatUnspecifiedReturnsI16BeforeLollipop)305 TEST_F(StreamOpenOutput, PlaybackFormatUnspecifiedReturnsI16BeforeLollipop){
306
307 if (getSdkVersion() < __ANDROID_API_L__){
308 mBuilder.setDirection(Direction::Output);
309 mBuilder.setFormat(AudioFormat::Unspecified);
310 ASSERT_TRUE(openStream());
311 ASSERT_EQ(mStream->getFormat(), AudioFormat::I16);
312 ASSERT_TRUE(closeStream());
313 }
314 }
315
TEST_F(StreamOpenOutput,PlaybackFormatUnspecifiedReturnsFloatOnLollipopAndLater)316 TEST_F(StreamOpenOutput, PlaybackFormatUnspecifiedReturnsFloatOnLollipopAndLater){
317
318 if (getSdkVersion() >= __ANDROID_API_L__){
319 mBuilder.setDirection(Direction::Output);
320 mBuilder.setFormat(AudioFormat::Unspecified);
321 ASSERT_TRUE(openStream());
322 ASSERT_EQ(mStream->getFormat(), AudioFormat::Float);
323 ASSERT_TRUE(closeStream());
324 }
325 }
326
TEST_F(StreamOpenOutput,PlaybackFormatFloatReturnsErrorBeforeLollipop)327 TEST_F(StreamOpenOutput, PlaybackFormatFloatReturnsErrorBeforeLollipop){
328
329 if (getSdkVersion() < __ANDROID_API_L__){
330 mBuilder.setDirection(Direction::Output);
331 mBuilder.setFormat(AudioFormat::Float);
332 Result r = mBuilder.openStream(&mStream);
333 ASSERT_EQ(r, Result::ErrorInvalidFormat);
334 ASSERT_TRUE(closeStream());
335 }
336 }
337
TEST_F(StreamOpenOutput,PlaybackFormatFloatReturnsFloatWithFormatConversionAllowed)338 TEST_F(StreamOpenOutput, PlaybackFormatFloatReturnsFloatWithFormatConversionAllowed){
339 mBuilder.setDirection(Direction::Output);
340 mBuilder.setFormat(AudioFormat::Float);
341 mBuilder.setFormatConversionAllowed(true);
342 ASSERT_TRUE(openStream());
343 ASSERT_EQ(mStream->getFormat(), AudioFormat::Float);
344 ASSERT_TRUE(closeStream());
345 }
346
TEST_F(StreamOpenOutput,PlaybackFormatFloatReturnsFloatOnLollipopAndLater)347 TEST_F(StreamOpenOutput, PlaybackFormatFloatReturnsFloatOnLollipopAndLater){
348
349 if (getSdkVersion() >= __ANDROID_API_L__){
350 mBuilder.setDirection(Direction::Output);
351 mBuilder.setFormat(AudioFormat::Float);
352 ASSERT_TRUE(openStream());
353 ASSERT_EQ(mStream->getFormat(), AudioFormat::Float);
354 ASSERT_TRUE(closeStream());
355 }
356 }
357
TEST_F(StreamOpenOutput,PlaybackFormatI16ReturnsI16)358 TEST_F(StreamOpenOutput, PlaybackFormatI16ReturnsI16) {
359 mBuilder.setDirection(Direction::Output);
360 mBuilder.setFormat(AudioFormat::I16);
361 ASSERT_TRUE(openStream());
362 ASSERT_EQ(mStream->getFormat(), AudioFormat::I16);
363 ASSERT_TRUE(closeStream());
364 }
365
TEST_F(StreamOpenOutput,OpenCloseLowLatencyStream)366 TEST_F(StreamOpenOutput, OpenCloseLowLatencyStream){
367 mBuilder.setDirection(Direction::Output);
368 mBuilder.setPerformanceMode(PerformanceMode::LowLatency);
369 float *buf = new float[100];
370 ASSERT_TRUE(openStream());
371 delete[] buf;
372 ASSERT_TRUE(closeStream());
373 }
374
TEST_F(StreamOpenOutput,LowLatencyStreamHasSmallBufferSize)375 TEST_F(StreamOpenOutput, LowLatencyStreamHasSmallBufferSize){
376
377 if (mBuilder.isAAudioRecommended()) {
378 mBuilder.setDirection(Direction::Output);
379 mBuilder.setPerformanceMode(PerformanceMode::LowLatency);
380 ASSERT_TRUE(openStream());
381 int32_t bufferSize = mStream->getBufferSizeInFrames();
382 int32_t burst = mStream->getFramesPerBurst();
383 ASSERT_TRUE(closeStream());
384 ASSERT_LE(bufferSize, burst * 3);
385 }
386 }
387
388 // See if sample rate conversion by Oboe is calling the callback.
TEST_F(StreamOpenOutput,AAudioOutputSampleRate44100)389 TEST_F(StreamOpenOutput, AAudioOutputSampleRate44100) {
390 checkSampleRateConversionAdvancing(Direction::Output);
391 }
392
393 // See if sample rate conversion by Oboe is calling the callback.
TEST_F(StreamOpenInput,AAudioInputSampleRate44100)394 TEST_F(StreamOpenInput, AAudioInputSampleRate44100) {
395 checkSampleRateConversionAdvancing(Direction::Input);
396 }
397
TEST_F(StreamOpenOutput,AAudioOutputSetPackageName)398 TEST_F(StreamOpenOutput, AAudioOutputSetPackageName){
399 if (getSdkVersion() >= __ANDROID_API_S__){
400 mBuilder.setAudioApi(AudioApi::AAudio);
401 mBuilder.setPackageName("com.google.oboe.tests.unittestrunner");
402 ASSERT_TRUE(openStream());
403 ASSERT_EQ(mStream->requestStart(), Result::OK);
404 ASSERT_TRUE(closeStream());
405 }
406 }
407
TEST_F(StreamOpenInput,AAudioInputSetPackageName)408 TEST_F(StreamOpenInput, AAudioInputSetPackageName){
409 if (getSdkVersion() >= __ANDROID_API_S__){
410 mBuilder.setDirection(Direction::Input);
411 mBuilder.setAudioApi(AudioApi::AAudio);
412 mBuilder.setPackageName("com.google.oboe.tests.unittestrunner");
413 ASSERT_TRUE(openStream());
414 ASSERT_EQ(mStream->requestStart(), Result::OK);
415 ASSERT_TRUE(closeStream());
416 }
417 }
418
TEST_F(StreamOpenOutput,AAudioOutputSetAttributionTag)419 TEST_F(StreamOpenOutput, AAudioOutputSetAttributionTag){
420 if (getSdkVersion() >= __ANDROID_API_S__){
421 mBuilder.setAudioApi(AudioApi::AAudio);
422 mBuilder.setAttributionTag("TestSetOutputAttributionTag");
423 ASSERT_TRUE(openStream());
424 ASSERT_EQ(mStream->requestStart(), Result::OK);
425 ASSERT_TRUE(closeStream());
426 }
427 }
428
TEST_F(StreamOpenInput,AAudioInputSetAttributionTag)429 TEST_F(StreamOpenInput, AAudioInputSetAttributionTag){
430 if (getSdkVersion() >= __ANDROID_API_S__){
431 mBuilder.setDirection(Direction::Input);
432 mBuilder.setAudioApi(AudioApi::AAudio);
433 mBuilder.setAttributionTag("TestSetInputAttributionTag");
434 ASSERT_TRUE(openStream());
435 ASSERT_EQ(mStream->requestStart(), Result::OK);
436 ASSERT_TRUE(closeStream());
437 }
438 }
439
TEST_F(StreamOpenOutput,OutputForOpenSLESPerformanceModeNoneGetBufferSizeInFrames)440 TEST_F(StreamOpenOutput, OutputForOpenSLESPerformanceModeNoneGetBufferSizeInFrames){
441 mBuilder.setPerformanceMode(PerformanceMode::None);
442 mBuilder.setAudioApi(AudioApi::OpenSLES);
443 ASSERT_TRUE(openStream());
444 EXPECT_GT(mStream->getBufferSizeInFrames(), 0);
445 ASSERT_TRUE(closeStream());
446 }
447
TEST_F(StreamOpenOutput,OboeExtensions)448 TEST_F(StreamOpenOutput, OboeExtensions){
449 if (OboeExtensions::isMMapSupported()) {
450 ASSERT_EQ(OboeExtensions::setMMapEnabled(true), 0);
451 ASSERT_TRUE(OboeExtensions::isMMapEnabled());
452
453 ASSERT_EQ(OboeExtensions::setMMapEnabled(false), 0);
454 ASSERT_FALSE(OboeExtensions::isMMapEnabled());
455 ASSERT_TRUE(openStream());
456 EXPECT_FALSE(OboeExtensions::isMMapUsed(mStream));
457 ASSERT_TRUE(closeStream());
458
459 ASSERT_EQ(OboeExtensions::setMMapEnabled(true), 0);
460 ASSERT_TRUE(OboeExtensions::isMMapEnabled());
461 }
462 }
463