#include /* * Copyright 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include using namespace oboe; class CallbackSizeMonitor : public AudioStreamCallback { public: DataCallbackResult onAudioReady(AudioStream *oboeStream, void *audioData, int32_t numFrames) override { framesPerCallback = numFrames; callbackCount++; return DataCallbackResult::Continue; } // This is exposed publicly so that the number of frames per callback can be tested. std::atomic framesPerCallback{0}; std::atomic callbackCount{0}; }; class StreamOpen : public ::testing::Test { protected: bool openStream() { Result r = mBuilder.openStream(&mStream); EXPECT_EQ(r, Result::OK) << "Failed to open stream " << convertToText(r); EXPECT_EQ(0, openCount) << "Should start with a fresh object every time."; openCount++; return (r == Result::OK); } bool closeStream() { Result r = mStream->close(); EXPECT_EQ(r, Result::OK) << "Failed to close stream. " << convertToText(r); usleep(500 * 1000); // give previous stream time to settle return (r == Result::OK); } void checkSampleRateConversionAdvancing(Direction direction) { CallbackSizeMonitor callback; mBuilder.setDirection(direction); mBuilder.setAudioApi(AudioApi::AAudio); mBuilder.setCallback(&callback); mBuilder.setPerformanceMode(PerformanceMode::LowLatency); mBuilder.setSampleRate(44100); mBuilder.setSampleRateConversionQuality(SampleRateConversionQuality::Medium); ASSERT_TRUE(openStream()); ASSERT_EQ(mStream->requestStart(), Result::OK); int timeout = 20; while (callback.framesPerCallback == 0 && timeout > 0) { usleep(50 * 1000); timeout--; } // Catch Issue #1166 mStream->getTimestamp(CLOCK_MONOTONIC); // should not crash mStream->getTimestamp(CLOCK_MONOTONIC, nullptr, nullptr); // should not crash ASSERT_GT(callback.callbackCount, 0); ASSERT_GT(callback.framesPerCallback, 0); ASSERT_EQ(mStream->requestStop(), Result::OK); ASSERT_TRUE(closeStream()); } AudioStreamBuilder mBuilder; AudioStream *mStream = nullptr; int32_t openCount = 0; }; TEST_F(StreamOpen, ForOpenSLESDefaultSampleRateIsUsed){ DefaultStreamValues::SampleRate = 44100; DefaultStreamValues::FramesPerBurst = 192; mBuilder.setAudioApi(AudioApi::OpenSLES); ASSERT_TRUE(openStream()); ASSERT_EQ(mStream->getSampleRate(), 44100); ASSERT_TRUE(closeStream()); } TEST_F(StreamOpen, ForOpenSLESDefaultFramesPerBurstIsUsed){ DefaultStreamValues::SampleRate = 48000; DefaultStreamValues::FramesPerBurst = 128; // used for low latency mBuilder.setAudioApi(AudioApi::OpenSLES); mBuilder.setPerformanceMode(PerformanceMode::LowLatency); ASSERT_TRUE(openStream()); ASSERT_EQ(mStream->getFramesPerBurst(), 128); ASSERT_TRUE(closeStream()); } TEST_F(StreamOpen, ForOpenSLESDefaultChannelCountIsUsed){ DefaultStreamValues::ChannelCount = 1; mBuilder.setAudioApi(AudioApi::OpenSLES); ASSERT_TRUE(openStream()); ASSERT_EQ(mStream->getChannelCount(), 1); ASSERT_TRUE(closeStream()); } TEST_F(StreamOpen, OutputForOpenSLESPerformanceModeShouldBeNone){ // We will not get a LowLatency stream if we request 16000 Hz. mBuilder.setSampleRate(16000); mBuilder.setPerformanceMode(PerformanceMode::LowLatency); mBuilder.setDirection(Direction::Output); mBuilder.setAudioApi(AudioApi::OpenSLES); ASSERT_TRUE(openStream()); ASSERT_EQ((int)mStream->getPerformanceMode(), (int)PerformanceMode::None); ASSERT_TRUE(closeStream()); } TEST_F(StreamOpen, InputForOpenSLESPerformanceModeShouldBeNone){ // We will not get a LowLatency stream if we request 16000 Hz. mBuilder.setSampleRate(16000); mBuilder.setPerformanceMode(PerformanceMode::LowLatency); mBuilder.setDirection(Direction::Input); mBuilder.setAudioApi(AudioApi::OpenSLES); ASSERT_TRUE(openStream()); ASSERT_EQ((int)mStream->getPerformanceMode(), (int)PerformanceMode::None); ASSERT_TRUE(closeStream()); } TEST_F(StreamOpen, ForOpenSlesIllegalFormatRejectedOutput) { mBuilder.setAudioApi(AudioApi::OpenSLES); mBuilder.setPerformanceMode(PerformanceMode::LowLatency); mBuilder.setFormat(static_cast(666)); Result r = mBuilder.openStream(&mStream); EXPECT_NE(r, Result::OK) << "Should not open stream " << convertToText(r); if (mStream != nullptr) { mStream->close(); // just in case it accidentally opened } } TEST_F(StreamOpen, ForOpenSlesIllegalFormatRejectedInput) { mBuilder.setAudioApi(AudioApi::OpenSLES); mBuilder.setPerformanceMode(PerformanceMode::LowLatency); mBuilder.setDirection(Direction::Input); mBuilder.setFormat(static_cast(666)); Result r = mBuilder.openStream(&mStream); EXPECT_NE(r, Result::OK) << "Should not open stream " << convertToText(r); if (mStream != nullptr) { mStream->close(); // just in case it accidentally opened } } // Make sure the callback is called with the requested FramesPerCallback TEST_F(StreamOpen, OpenSLESFramesPerCallback) { const int kRequestedFramesPerCallback = 417; CallbackSizeMonitor callback; DefaultStreamValues::SampleRate = 48000; DefaultStreamValues::ChannelCount = 2; DefaultStreamValues::FramesPerBurst = 192; mBuilder.setAudioApi(AudioApi::OpenSLES); mBuilder.setFramesPerCallback(kRequestedFramesPerCallback); mBuilder.setCallback(&callback); ASSERT_TRUE(openStream()); ASSERT_EQ(mStream->requestStart(), Result::OK); int timeout = 20; while (callback.framesPerCallback == 0 && timeout > 0) { usleep(50 * 1000); timeout--; } ASSERT_EQ(kRequestedFramesPerCallback, callback.framesPerCallback); ASSERT_EQ(kRequestedFramesPerCallback, mStream->getFramesPerCallback()); ASSERT_EQ(mStream->requestStop(), Result::OK); ASSERT_TRUE(closeStream()); } /* TODO - This is hanging! // Make sure the LowLatency callback has the requested FramesPerCallback. TEST_F(StreamOpen, AAudioFramesPerCallbackLowLatency) { const int kRequestedFramesPerCallback = 192; CallbackSizeMonitor callback; mBuilder.setAudioApi(AudioApi::AAudio); mBuilder.setFramesPerCallback(kRequestedFramesPerCallback); mBuilder.setCallback(&callback); mBuilder.setPerformanceMode(PerformanceMode::LowLatency); ASSERT_TRUE(openStream()); ASSERT_EQ(kRequestedFramesPerCallback, mStream->getFramesPerCallback()); ASSERT_EQ(mStream->requestStart(), Result::OK); int timeout = 20; while (callback.framesPerCallback == 0 && timeout > 0) { usleep(50 * 1000); timeout--; } ASSERT_EQ(kRequestedFramesPerCallback, callback.framesPerCallback); ASSERT_EQ(mStream->requestStop(), Result::OK); ASSERT_TRUE(closeStream()); } */ /* TODO - This is hanging! // Make sure the regular callback has the requested FramesPerCallback. TEST_F(StreamOpen, AAudioFramesPerCallbackNone) { const int kRequestedFramesPerCallback = 1024; CallbackSizeMonitor callback; mBuilder.setAudioApi(AudioApi::AAudio); mBuilder.setFramesPerCallback(kRequestedFramesPerCallback); mBuilder.setCallback(&callback); mBuilder.setPerformanceMode(PerformanceMode::None); ASSERT_TRUE(openStream()); ASSERT_EQ(kRequestedFramesPerCallback, mStream->getFramesPerCallback()); ASSERT_EQ(mStream->setBufferSizeInFrames(mStream->getBufferCapacityInFrames()), Result::OK); ASSERT_EQ(mStream->requestStart(), Result::OK); int timeout = 20; while (callback.framesPerCallback == 0 && timeout > 0) { usleep(50 * 1000); timeout--; } ASSERT_EQ(kRequestedFramesPerCallback, callback.framesPerCallback); ASSERT_EQ(mStream->requestStop(), Result::OK); ASSERT_TRUE(closeStream()); } */ TEST_F(StreamOpen, RecordingFormatUnspecifiedReturnsI16BeforeMarshmallow){ if (getSdkVersion() < __ANDROID_API_M__){ mBuilder.setDirection(Direction::Input); mBuilder.setFormat(AudioFormat::Unspecified); ASSERT_TRUE(openStream()); ASSERT_EQ(mStream->getFormat(), AudioFormat::I16); ASSERT_TRUE(closeStream()); } } TEST_F(StreamOpen, RecordingFormatUnspecifiedReturnsFloatOnMarshmallowAndLater){ if (getSdkVersion() >= __ANDROID_API_M__){ mBuilder.setDirection(Direction::Input); mBuilder.setFormat(AudioFormat::Unspecified); ASSERT_TRUE(openStream()); ASSERT_EQ(mStream->getFormat(), AudioFormat::Float); ASSERT_TRUE(closeStream()); } } TEST_F(StreamOpen, RecordingFormatFloatReturnsErrorBeforeMarshmallow){ if (getSdkVersion() < __ANDROID_API_M__){ mBuilder.setDirection(Direction::Input); mBuilder.setFormat(AudioFormat::Float); Result r = mBuilder.openStream(&mStream); ASSERT_EQ(r, Result::ErrorInvalidFormat) << convertToText(r); ASSERT_TRUE(closeStream()); } } TEST_F(StreamOpen, RecordingFormatFloatReturnsFloatOnMarshmallowAndLater){ if (getSdkVersion() >= __ANDROID_API_M__){ mBuilder.setDirection(Direction::Input); mBuilder.setFormat(AudioFormat::Float); ASSERT_TRUE(openStream()); ASSERT_EQ(mStream->getFormat(), AudioFormat::Float); ASSERT_TRUE(closeStream()); } } TEST_F(StreamOpen, RecordingFormatI16ReturnsI16){ mBuilder.setDirection(Direction::Input); mBuilder.setFormat(AudioFormat::I16); ASSERT_TRUE(openStream()); ASSERT_EQ(mStream->getFormat(), AudioFormat::I16); ASSERT_TRUE(closeStream()); } TEST_F(StreamOpen, PlaybackFormatUnspecifiedReturnsI16BeforeLollipop){ if (getSdkVersion() < __ANDROID_API_L__){ mBuilder.setDirection(Direction::Output); mBuilder.setFormat(AudioFormat::Unspecified); ASSERT_TRUE(openStream()); ASSERT_EQ(mStream->getFormat(), AudioFormat::I16); ASSERT_TRUE(closeStream()); } } TEST_F(StreamOpen, PlaybackFormatUnspecifiedReturnsFloatOnLollipopAndLater){ if (getSdkVersion() >= __ANDROID_API_L__){ mBuilder.setDirection(Direction::Output); mBuilder.setFormat(AudioFormat::Unspecified); ASSERT_TRUE(openStream()); ASSERT_EQ(mStream->getFormat(), AudioFormat::Float); ASSERT_TRUE(closeStream()); } } TEST_F(StreamOpen, PlaybackFormatFloatReturnsErrorBeforeLollipop){ if (getSdkVersion() < __ANDROID_API_L__){ mBuilder.setDirection(Direction::Output); mBuilder.setFormat(AudioFormat::Float); Result r = mBuilder.openStream(&mStream); ASSERT_EQ(r, Result::ErrorInvalidFormat); ASSERT_TRUE(closeStream()); } } TEST_F(StreamOpen, PlaybackFormatFloatReturnsFloatOnLollipopAndLater){ if (getSdkVersion() >= __ANDROID_API_L__){ mBuilder.setDirection(Direction::Output); mBuilder.setFormat(AudioFormat::Float); ASSERT_TRUE(openStream()); ASSERT_EQ(mStream->getFormat(), AudioFormat::Float); ASSERT_TRUE(closeStream()); } } TEST_F(StreamOpen, PlaybackFormatI16ReturnsI16) { mBuilder.setDirection(Direction::Output); mBuilder.setFormat(AudioFormat::I16); ASSERT_TRUE(openStream()); ASSERT_EQ(mStream->getFormat(), AudioFormat::I16); ASSERT_TRUE(closeStream()); } TEST_F(StreamOpen, OpenCloseLowLatencyStream){ mBuilder.setDirection(Direction::Output); mBuilder.setPerformanceMode(PerformanceMode::LowLatency); float *buf = new float[100]; ASSERT_TRUE(openStream()); delete[] buf; ASSERT_TRUE(closeStream()); } TEST_F(StreamOpen, LowLatencyStreamHasSmallBufferSize){ if (mBuilder.isAAudioRecommended()) { mBuilder.setDirection(Direction::Output); mBuilder.setPerformanceMode(PerformanceMode::LowLatency); ASSERT_TRUE(openStream()); int32_t bufferSize = mStream->getBufferSizeInFrames(); int32_t burst = mStream->getFramesPerBurst(); ASSERT_TRUE(closeStream()); ASSERT_LE(bufferSize, burst * 3); } } // See if sample rate conversion by Oboe is calling the callback. TEST_F(StreamOpen, AAudioOutputSampleRate44100) { checkSampleRateConversionAdvancing(Direction::Output); } // See if sample rate conversion by Oboe is calling the callback. TEST_F(StreamOpen, AAudioInputSampleRate44100) { checkSampleRateConversionAdvancing(Direction::Input); }