/* * Copyright (C) 2017 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. */ // Test various AAudio features including AAudioStream_setBufferSizeInFrames(). #include #include #include #include #include #include #include // Callback function that does nothing. aaudio_data_callback_result_t NoopDataCallbackProc( AAudioStream * stream, void * /* userData */, void *audioData, int32_t numFrames ) { int channels = AAudioStream_getChannelCount(stream); int numSamples = channels * numFrames; bool allZeros = true; float * const floatData = reinterpret_cast(audioData); for (int i = 0; i < numSamples; i++) { allZeros &= (floatData[i] == 0.0f); floatData[i] = 0.0f; } EXPECT_TRUE(allZeros); return AAUDIO_CALLBACK_RESULT_CONTINUE; } constexpr int64_t NANOS_PER_MILLISECOND = 1000 * 1000; void checkReleaseThenClose(aaudio_performance_mode_t perfMode, aaudio_sharing_mode_t sharingMode) { AAudioStreamBuilder* aaudioBuilder = nullptr; AAudioStream* aaudioStream = nullptr; // Use an AAudioStreamBuilder to contain requested parameters. ASSERT_EQ(AAUDIO_OK, AAudio_createStreamBuilder(&aaudioBuilder)); // Request stream properties. AAudioStreamBuilder_setDataCallback(aaudioBuilder, NoopDataCallbackProc, nullptr); AAudioStreamBuilder_setPerformanceMode(aaudioBuilder, perfMode); AAudioStreamBuilder_setSharingMode(aaudioBuilder, sharingMode); AAudioStreamBuilder_setFormat(aaudioBuilder, AAUDIO_FORMAT_PCM_FLOAT); // Create an AAudioStream using the Builder. ASSERT_EQ(AAUDIO_OK, AAudioStreamBuilder_openStream(aaudioBuilder, &aaudioStream)); AAudioStreamBuilder_delete(aaudioBuilder); ASSERT_EQ(AAUDIO_OK, AAudioStream_requestStart(aaudioStream)); sleep(1); EXPECT_EQ(AAUDIO_OK, AAudioStream_requestStop(aaudioStream)); EXPECT_EQ(AAUDIO_OK, AAudioStream_release(aaudioStream)); EXPECT_EQ(AAUDIO_STREAM_STATE_CLOSING, AAudioStream_getState(aaudioStream)); // We should be able to call this again without crashing. EXPECT_EQ(AAUDIO_OK, AAudioStream_release(aaudioStream)); EXPECT_EQ(AAUDIO_STREAM_STATE_CLOSING, AAudioStream_getState(aaudioStream)); // We expect these not to crash. AAudioStream_setBufferSizeInFrames(aaudioStream, 0); AAudioStream_setBufferSizeInFrames(aaudioStream, 99999999); // We should NOT be able to start or change a stream after it has been released. EXPECT_EQ(AAUDIO_ERROR_INVALID_STATE, AAudioStream_requestStart(aaudioStream)); EXPECT_EQ(AAUDIO_STREAM_STATE_CLOSING, AAudioStream_getState(aaudioStream)); EXPECT_EQ(AAUDIO_ERROR_INVALID_STATE, AAudioStream_requestPause(aaudioStream)); EXPECT_EQ(AAUDIO_STREAM_STATE_CLOSING, AAudioStream_getState(aaudioStream)); EXPECT_EQ(AAUDIO_ERROR_INVALID_STATE, AAudioStream_requestStop(aaudioStream)); EXPECT_EQ(AAUDIO_STREAM_STATE_CLOSING, AAudioStream_getState(aaudioStream)); // Does this crash? EXPECT_LT(0, AAudioStream_getFramesRead(aaudioStream)); EXPECT_LT(0, AAudioStream_getFramesWritten(aaudioStream)); // Verify Closing State. Does this crash? aaudio_stream_state_t state = AAUDIO_STREAM_STATE_UNKNOWN; EXPECT_EQ(AAUDIO_OK, AAudioStream_waitForStateChange(aaudioStream, AAUDIO_STREAM_STATE_UNKNOWN, &state, 500 * NANOS_PER_MILLISECOND)); EXPECT_EQ(AAUDIO_STREAM_STATE_CLOSING, state); EXPECT_EQ(AAUDIO_OK, AAudioStream_close(aaudioStream)); } TEST(test_various, aaudio_release_close_none) { checkReleaseThenClose(AAUDIO_PERFORMANCE_MODE_NONE, AAUDIO_SHARING_MODE_SHARED); // No EXCLUSIVE streams with MODE_NONE. } TEST(test_various, aaudio_release_close_low_shared) { checkReleaseThenClose(AAUDIO_PERFORMANCE_MODE_LOW_LATENCY, AAUDIO_SHARING_MODE_SHARED); } TEST(test_various, aaudio_release_close_low_exclusive) { checkReleaseThenClose(AAUDIO_PERFORMANCE_MODE_LOW_LATENCY, AAUDIO_SHARING_MODE_EXCLUSIVE); } enum FunctionToCall { CALL_START, CALL_STOP, CALL_PAUSE, CALL_FLUSH, CALL_RELEASE }; void checkStateTransition(aaudio_performance_mode_t perfMode, aaudio_stream_state_t originalState, FunctionToCall functionToCall, aaudio_result_t expectedResult, aaudio_stream_state_t expectedState) { AAudioStreamBuilder *aaudioBuilder = nullptr; AAudioStream *aaudioStream = nullptr; // Use an AAudioStreamBuilder to contain requested parameters. ASSERT_EQ(AAUDIO_OK, AAudio_createStreamBuilder(&aaudioBuilder)); // Request stream properties. AAudioStreamBuilder_setDataCallback(aaudioBuilder, NoopDataCallbackProc, nullptr); AAudioStreamBuilder_setPerformanceMode(aaudioBuilder, perfMode); AAudioStreamBuilder_setFormat(aaudioBuilder, AAUDIO_FORMAT_PCM_FLOAT); // Create an AAudioStream using the Builder. ASSERT_EQ(AAUDIO_OK, AAudioStreamBuilder_openStream(aaudioBuilder, &aaudioStream)); // Verify Open State aaudio_stream_state_t state = AAUDIO_STREAM_STATE_UNKNOWN; EXPECT_EQ(AAUDIO_OK, AAudioStream_waitForStateChange(aaudioStream, AAUDIO_STREAM_STATE_UNKNOWN, &state, 1000 * NANOS_PER_MILLISECOND)); EXPECT_EQ(AAUDIO_STREAM_STATE_OPEN, state); // Put stream into desired state. aaudio_stream_state_t inputState = AAUDIO_STREAM_STATE_UNINITIALIZED; if (originalState != AAUDIO_STREAM_STATE_OPEN) { ASSERT_EQ(AAUDIO_OK, AAudioStream_requestStart(aaudioStream)); if (originalState != AAUDIO_STREAM_STATE_STARTING) { ASSERT_EQ(AAUDIO_OK, AAudioStream_waitForStateChange(aaudioStream, AAUDIO_STREAM_STATE_STARTING, &state, 1000 * NANOS_PER_MILLISECOND)); ASSERT_EQ(AAUDIO_STREAM_STATE_STARTED, state); if (originalState == AAUDIO_STREAM_STATE_STOPPING) { ASSERT_EQ(AAUDIO_OK, AAudioStream_requestStop(aaudioStream)); } else if (originalState == AAUDIO_STREAM_STATE_STOPPED) { ASSERT_EQ(AAUDIO_OK, AAudioStream_requestStop(aaudioStream)); inputState = AAUDIO_STREAM_STATE_STOPPING; } else if (originalState == AAUDIO_STREAM_STATE_PAUSING) { ASSERT_EQ(AAUDIO_OK, AAudioStream_requestPause(aaudioStream)); } else if (originalState == AAUDIO_STREAM_STATE_PAUSED) { ASSERT_EQ(AAUDIO_OK, AAudioStream_requestPause(aaudioStream)); inputState = AAUDIO_STREAM_STATE_PAUSING; } else if (originalState == AAUDIO_STREAM_STATE_FLUSHING) { ASSERT_EQ(AAUDIO_OK, AAudioStream_requestPause(aaudioStream)); // We can only flush() after pause is complete. ASSERT_EQ(AAUDIO_OK, AAudioStream_waitForStateChange(aaudioStream, AAUDIO_STREAM_STATE_PAUSING, &state, 1000 * NANOS_PER_MILLISECOND)); ASSERT_EQ(AAUDIO_STREAM_STATE_PAUSED, state); ASSERT_EQ(AAUDIO_OK, AAudioStream_requestFlush(aaudioStream)); // That will put the stream into the FLUSHING state. // The FLUSHING state will persist until we process functionToCall. // That is because the transition to FLUSHED is caused by the callback, // or by calling write() or waitForStateChange(). But those will not // occur. } else if (originalState == AAUDIO_STREAM_STATE_CLOSING) { ASSERT_EQ(AAUDIO_OK, AAudioStream_release(aaudioStream)); } } } // Wait until we get past the transitional state if requested. if (inputState != AAUDIO_STREAM_STATE_UNINITIALIZED) { ASSERT_EQ(AAUDIO_OK, AAudioStream_waitForStateChange(aaudioStream, inputState, &state, 1000 * NANOS_PER_MILLISECOND)); ASSERT_EQ(originalState, state); } aaudio_stream_state_t transitionalState = originalState; switch(functionToCall) { case FunctionToCall::CALL_START: EXPECT_EQ(expectedResult, AAudioStream_requestStart(aaudioStream)); transitionalState = AAUDIO_STREAM_STATE_STARTING; break; case FunctionToCall::CALL_STOP: EXPECT_EQ(expectedResult, AAudioStream_requestStop(aaudioStream)); transitionalState = AAUDIO_STREAM_STATE_STOPPING; break; case FunctionToCall::CALL_PAUSE: EXPECT_EQ(expectedResult, AAudioStream_requestPause(aaudioStream)); transitionalState = AAUDIO_STREAM_STATE_PAUSING; break; case FunctionToCall::CALL_FLUSH: EXPECT_EQ(expectedResult, AAudioStream_requestFlush(aaudioStream)); transitionalState = AAUDIO_STREAM_STATE_FLUSHING; break; case FunctionToCall::CALL_RELEASE: EXPECT_EQ(expectedResult, AAudioStream_release(aaudioStream)); // Set to UNINITIALIZED so the waitForStateChange() below will // will return immediately with the current state. transitionalState = AAUDIO_STREAM_STATE_UNINITIALIZED; break; } EXPECT_EQ(AAUDIO_OK, AAudioStream_waitForStateChange(aaudioStream, transitionalState, &state, 1000 * NANOS_PER_MILLISECOND)); // We should not change state when a function fails. if (expectedResult != AAUDIO_OK) { ASSERT_EQ(originalState, expectedState); } EXPECT_EQ(expectedState, state); if (state != expectedState) { printf("ERROR - expected %s, actual = %s\n", AAudio_convertStreamStateToText(expectedState), AAudio_convertStreamStateToText(state)); fflush(stdout); } AAudioStream_close(aaudioStream); AAudioStreamBuilder_delete(aaudioBuilder); } // TODO Use parameterized tests instead of these individual specific tests. // OPEN ================================================================= TEST(test_various, aaudio_state_lowlat_open_start) { checkStateTransition(AAUDIO_PERFORMANCE_MODE_LOW_LATENCY, AAUDIO_STREAM_STATE_OPEN, FunctionToCall::CALL_START, AAUDIO_OK, AAUDIO_STREAM_STATE_STARTED); } TEST(test_various, aaudio_state_none_open_start) { checkStateTransition(AAUDIO_PERFORMANCE_MODE_NONE, AAUDIO_STREAM_STATE_OPEN, FunctionToCall::CALL_START, AAUDIO_OK, AAUDIO_STREAM_STATE_STARTED); } TEST(test_various, aaudio_state_lowlat_open_stop) { checkStateTransition(AAUDIO_PERFORMANCE_MODE_LOW_LATENCY, AAUDIO_STREAM_STATE_OPEN, FunctionToCall::CALL_STOP, AAUDIO_OK, AAUDIO_STREAM_STATE_STOPPED); } TEST(test_various, aaudio_state_none_open_stop) { checkStateTransition(AAUDIO_PERFORMANCE_MODE_NONE, AAUDIO_STREAM_STATE_OPEN, FunctionToCall::CALL_STOP, AAUDIO_OK, AAUDIO_STREAM_STATE_STOPPED); } TEST(test_various, aaudio_state_lowlat_open_pause) { checkStateTransition(AAUDIO_PERFORMANCE_MODE_LOW_LATENCY, AAUDIO_STREAM_STATE_OPEN, FunctionToCall::CALL_PAUSE, AAUDIO_OK, AAUDIO_STREAM_STATE_PAUSED); } TEST(test_various, aaudio_state_none_open_pause) { checkStateTransition(AAUDIO_PERFORMANCE_MODE_NONE, AAUDIO_STREAM_STATE_OPEN, FunctionToCall::CALL_PAUSE, AAUDIO_OK, AAUDIO_STREAM_STATE_PAUSED); } TEST(test_various, aaudio_state_lowlat_open_flush) { checkStateTransition(AAUDIO_PERFORMANCE_MODE_LOW_LATENCY, AAUDIO_STREAM_STATE_OPEN, FunctionToCall::CALL_FLUSH, AAUDIO_OK, AAUDIO_STREAM_STATE_FLUSHED); } TEST(test_various, aaudio_state_none_open_flush) { checkStateTransition(AAUDIO_PERFORMANCE_MODE_NONE, AAUDIO_STREAM_STATE_OPEN, FunctionToCall::CALL_FLUSH, AAUDIO_OK, AAUDIO_STREAM_STATE_FLUSHED); } // STARTED ================================================================= TEST(test_various, aaudio_state_lowlat_started_start) { checkStateTransition(AAUDIO_PERFORMANCE_MODE_LOW_LATENCY, AAUDIO_STREAM_STATE_STARTED, FunctionToCall::CALL_START, AAUDIO_ERROR_INVALID_STATE, AAUDIO_STREAM_STATE_STARTED); } TEST(test_various, aaudio_state_none_started_start) { checkStateTransition(AAUDIO_PERFORMANCE_MODE_NONE, AAUDIO_STREAM_STATE_STARTED, FunctionToCall::CALL_START, AAUDIO_ERROR_INVALID_STATE, AAUDIO_STREAM_STATE_STARTED); } TEST(test_various, aaudio_state_lowlat_started_stop) { checkStateTransition(AAUDIO_PERFORMANCE_MODE_LOW_LATENCY, AAUDIO_STREAM_STATE_STARTED, FunctionToCall::CALL_STOP, AAUDIO_OK, AAUDIO_STREAM_STATE_STOPPED); } TEST(test_various, aaudio_state_none_started_stop) { checkStateTransition(AAUDIO_PERFORMANCE_MODE_NONE, AAUDIO_STREAM_STATE_STARTED, FunctionToCall::CALL_STOP, AAUDIO_OK, AAUDIO_STREAM_STATE_STOPPED); } TEST(test_various, aaudio_state_lowlat_started_pause) { checkStateTransition(AAUDIO_PERFORMANCE_MODE_LOW_LATENCY, AAUDIO_STREAM_STATE_STARTED, FunctionToCall::CALL_PAUSE, AAUDIO_OK, AAUDIO_STREAM_STATE_PAUSED); } TEST(test_various, aaudio_state_none_started_pause) { checkStateTransition(AAUDIO_PERFORMANCE_MODE_NONE, AAUDIO_STREAM_STATE_STARTED, FunctionToCall::CALL_PAUSE, AAUDIO_OK, AAUDIO_STREAM_STATE_PAUSED); } TEST(test_various, aaudio_state_lowlat_started_flush) { checkStateTransition(AAUDIO_PERFORMANCE_MODE_LOW_LATENCY, AAUDIO_STREAM_STATE_STARTED, FunctionToCall::CALL_FLUSH, AAUDIO_ERROR_INVALID_STATE, AAUDIO_STREAM_STATE_STARTED); } TEST(test_various, aaudio_state_none_started_flush) { checkStateTransition(AAUDIO_PERFORMANCE_MODE_NONE, AAUDIO_STREAM_STATE_STARTED, FunctionToCall::CALL_FLUSH, AAUDIO_ERROR_INVALID_STATE, AAUDIO_STREAM_STATE_STARTED); } // STOPPED ================================================================= TEST(test_various, aaudio_state_lowlat_stopped_start) { checkStateTransition(AAUDIO_PERFORMANCE_MODE_LOW_LATENCY, AAUDIO_STREAM_STATE_STOPPED, FunctionToCall::CALL_START, AAUDIO_OK, AAUDIO_STREAM_STATE_STARTED); } TEST(test_various, aaudio_state_none_stopped_start) { checkStateTransition(AAUDIO_PERFORMANCE_MODE_NONE, AAUDIO_STREAM_STATE_STOPPED, FunctionToCall::CALL_START, AAUDIO_OK, AAUDIO_STREAM_STATE_STARTED); } TEST(test_various, aaudio_state_lowlat_stopped_stop) { checkStateTransition(AAUDIO_PERFORMANCE_MODE_LOW_LATENCY, AAUDIO_STREAM_STATE_STOPPED, FunctionToCall::CALL_STOP, AAUDIO_OK, AAUDIO_STREAM_STATE_STOPPED); } TEST(test_various, aaudio_state_none_stopped_stop) { checkStateTransition(AAUDIO_PERFORMANCE_MODE_NONE, AAUDIO_STREAM_STATE_STOPPED, FunctionToCall::CALL_STOP, AAUDIO_OK, AAUDIO_STREAM_STATE_STOPPED); } TEST(test_various, aaudio_state_lowlat_stopped_pause) { checkStateTransition(AAUDIO_PERFORMANCE_MODE_LOW_LATENCY, AAUDIO_STREAM_STATE_STOPPED, FunctionToCall::CALL_PAUSE, AAUDIO_OK, AAUDIO_STREAM_STATE_PAUSED); } TEST(test_various, aaudio_state_none_stopped_pause) { checkStateTransition(AAUDIO_PERFORMANCE_MODE_NONE, AAUDIO_STREAM_STATE_STOPPED, FunctionToCall::CALL_PAUSE, AAUDIO_OK, AAUDIO_STREAM_STATE_PAUSED); } TEST(test_various, aaudio_state_lowlat_stopped_flush) { checkStateTransition(AAUDIO_PERFORMANCE_MODE_LOW_LATENCY, AAUDIO_STREAM_STATE_STOPPED, FunctionToCall::CALL_FLUSH, AAUDIO_OK, AAUDIO_STREAM_STATE_FLUSHED); } TEST(test_various, aaudio_state_none_stopped_flush) { checkStateTransition(AAUDIO_PERFORMANCE_MODE_NONE, AAUDIO_STREAM_STATE_STOPPED, FunctionToCall::CALL_FLUSH, AAUDIO_OK, AAUDIO_STREAM_STATE_FLUSHED); } // PAUSED ================================================================= TEST(test_various, aaudio_state_lowlat_paused_start) { checkStateTransition(AAUDIO_PERFORMANCE_MODE_LOW_LATENCY, AAUDIO_STREAM_STATE_PAUSED, FunctionToCall::CALL_START, AAUDIO_OK, AAUDIO_STREAM_STATE_STARTED); } TEST(test_various, aaudio_state_none_paused_start) { checkStateTransition(AAUDIO_PERFORMANCE_MODE_NONE, AAUDIO_STREAM_STATE_PAUSED, FunctionToCall::CALL_START, AAUDIO_OK, AAUDIO_STREAM_STATE_STARTED); } TEST(test_various, aaudio_state_lowlat_paused_stop) { checkStateTransition(AAUDIO_PERFORMANCE_MODE_LOW_LATENCY, AAUDIO_STREAM_STATE_PAUSED, FunctionToCall::CALL_STOP, AAUDIO_OK, AAUDIO_STREAM_STATE_STOPPED); } TEST(test_various, aaudio_state_none_paused_stop) { checkStateTransition(AAUDIO_PERFORMANCE_MODE_NONE, AAUDIO_STREAM_STATE_PAUSED, FunctionToCall::CALL_STOP, AAUDIO_OK, AAUDIO_STREAM_STATE_STOPPED); } TEST(test_various, aaudio_state_lowlat_paused_pause) { checkStateTransition(AAUDIO_PERFORMANCE_MODE_LOW_LATENCY, AAUDIO_STREAM_STATE_PAUSED, FunctionToCall::CALL_PAUSE, AAUDIO_OK, AAUDIO_STREAM_STATE_PAUSED); } TEST(test_various, aaudio_state_none_paused_pause) { checkStateTransition(AAUDIO_PERFORMANCE_MODE_NONE, AAUDIO_STREAM_STATE_PAUSED, FunctionToCall::CALL_PAUSE, AAUDIO_OK, AAUDIO_STREAM_STATE_PAUSED); } TEST(test_various, aaudio_state_lowlat_paused_flush) { checkStateTransition(AAUDIO_PERFORMANCE_MODE_LOW_LATENCY, AAUDIO_STREAM_STATE_PAUSED, FunctionToCall::CALL_FLUSH, AAUDIO_OK, AAUDIO_STREAM_STATE_FLUSHED); } TEST(test_various, aaudio_state_none_paused_flush) { checkStateTransition(AAUDIO_PERFORMANCE_MODE_NONE, AAUDIO_STREAM_STATE_PAUSED, FunctionToCall::CALL_FLUSH, AAUDIO_OK, AAUDIO_STREAM_STATE_FLUSHED); } // FLUSHING ================================================================ TEST(test_various, aaudio_state_lowlat_flushing_start) { checkStateTransition(AAUDIO_PERFORMANCE_MODE_LOW_LATENCY, AAUDIO_STREAM_STATE_FLUSHING, FunctionToCall::CALL_START, AAUDIO_OK, AAUDIO_STREAM_STATE_STARTED); } TEST(test_various, aaudio_state_none_flushing_start) { checkStateTransition(AAUDIO_PERFORMANCE_MODE_NONE, AAUDIO_STREAM_STATE_FLUSHING, FunctionToCall::CALL_START, AAUDIO_OK, AAUDIO_STREAM_STATE_STARTED); } TEST(test_various, aaudio_state_lowlat_flushing_release) { checkStateTransition(AAUDIO_PERFORMANCE_MODE_LOW_LATENCY, AAUDIO_STREAM_STATE_FLUSHING, FunctionToCall::CALL_RELEASE, AAUDIO_OK, AAUDIO_STREAM_STATE_CLOSING); } TEST(test_various, aaudio_state_none_flushing_release) { checkStateTransition(AAUDIO_PERFORMANCE_MODE_NONE, AAUDIO_STREAM_STATE_FLUSHING, FunctionToCall::CALL_RELEASE, AAUDIO_OK, AAUDIO_STREAM_STATE_CLOSING); } TEST(test_various, aaudio_state_lowlat_starting_release) { checkStateTransition(AAUDIO_PERFORMANCE_MODE_LOW_LATENCY, AAUDIO_STREAM_STATE_STARTING, FunctionToCall::CALL_RELEASE, AAUDIO_OK, AAUDIO_STREAM_STATE_CLOSING); } TEST(test_various, aaudio_state_none_starting_release) { checkStateTransition(AAUDIO_PERFORMANCE_MODE_NONE, AAUDIO_STREAM_STATE_STARTING, FunctionToCall::CALL_RELEASE, AAUDIO_OK, AAUDIO_STREAM_STATE_CLOSING); } // CLOSING ================================================================ TEST(test_various, aaudio_state_lowlat_closing_start) { checkStateTransition(AAUDIO_PERFORMANCE_MODE_LOW_LATENCY, AAUDIO_STREAM_STATE_CLOSING, FunctionToCall::CALL_START, AAUDIO_ERROR_INVALID_STATE, AAUDIO_STREAM_STATE_CLOSING); } TEST(test_various, aaudio_state_none_closing_start) { checkStateTransition(AAUDIO_PERFORMANCE_MODE_NONE, AAUDIO_STREAM_STATE_CLOSING, FunctionToCall::CALL_START, AAUDIO_ERROR_INVALID_STATE, AAUDIO_STREAM_STATE_CLOSING); } TEST(test_various, aaudio_state_lowlat_closing_stop) { checkStateTransition(AAUDIO_PERFORMANCE_MODE_LOW_LATENCY, AAUDIO_STREAM_STATE_CLOSING, FunctionToCall::CALL_STOP, AAUDIO_ERROR_INVALID_STATE, AAUDIO_STREAM_STATE_CLOSING); } TEST(test_various, aaudio_state_none_closing_stop) { checkStateTransition(AAUDIO_PERFORMANCE_MODE_NONE, AAUDIO_STREAM_STATE_CLOSING, FunctionToCall::CALL_STOP, AAUDIO_ERROR_INVALID_STATE, AAUDIO_STREAM_STATE_CLOSING); } // ========================================================================== TEST(test_various, aaudio_set_buffer_size) { int32_t bufferCapacity; int32_t framesPerBurst = 0; int32_t actualSize = 0; AAudioStreamBuilder *aaudioBuilder = nullptr; AAudioStream *aaudioStream = nullptr; // Use an AAudioStreamBuilder to contain requested parameters. ASSERT_EQ(AAUDIO_OK, AAudio_createStreamBuilder(&aaudioBuilder)); // Request stream properties. AAudioStreamBuilder_setDataCallback(aaudioBuilder, NoopDataCallbackProc, nullptr); AAudioStreamBuilder_setPerformanceMode(aaudioBuilder, AAUDIO_PERFORMANCE_MODE_LOW_LATENCY); // Create an AAudioStream using the Builder. EXPECT_EQ(AAUDIO_OK, AAudioStreamBuilder_openStream(aaudioBuilder, &aaudioStream)); // This is the number of frames that are read in one chunk by a DMA controller // or a DSP or a mixer. framesPerBurst = AAudioStream_getFramesPerBurst(aaudioStream); bufferCapacity = AAudioStream_getBufferCapacityInFrames(aaudioStream); printf(" bufferCapacity = %d, remainder = %d\n", bufferCapacity, bufferCapacity % framesPerBurst); actualSize = AAudioStream_setBufferSizeInFrames(aaudioStream, 0); EXPECT_GE(actualSize, 0); // 0 is legal in R EXPECT_LE(actualSize, bufferCapacity); actualSize = AAudioStream_setBufferSizeInFrames(aaudioStream, 2 * framesPerBurst); EXPECT_GT(actualSize, framesPerBurst); EXPECT_LE(actualSize, bufferCapacity); actualSize = AAudioStream_setBufferSizeInFrames(aaudioStream, bufferCapacity - 1); EXPECT_GT(actualSize, framesPerBurst); EXPECT_LE(actualSize, bufferCapacity); actualSize = AAudioStream_setBufferSizeInFrames(aaudioStream, bufferCapacity); EXPECT_GT(actualSize, framesPerBurst); EXPECT_LE(actualSize, bufferCapacity); actualSize = AAudioStream_setBufferSizeInFrames(aaudioStream, bufferCapacity + 1); EXPECT_GT(actualSize, framesPerBurst); EXPECT_LE(actualSize, bufferCapacity); actualSize = AAudioStream_setBufferSizeInFrames(aaudioStream, 1234567); EXPECT_GT(actualSize, framesPerBurst); EXPECT_LE(actualSize, bufferCapacity); actualSize = AAudioStream_setBufferSizeInFrames(aaudioStream, INT32_MAX); EXPECT_GT(actualSize, framesPerBurst); EXPECT_LE(actualSize, bufferCapacity); actualSize = AAudioStream_setBufferSizeInFrames(aaudioStream, INT32_MIN); EXPECT_GE(actualSize, 0); // 0 is legal in R EXPECT_LE(actualSize, bufferCapacity); AAudioStream_close(aaudioStream); AAudioStreamBuilder_delete(aaudioBuilder); } // ************************************************************ // Test to make sure that AAUDIO_CALLBACK_RESULT_STOP works. // Callback function that counts calls. aaudio_data_callback_result_t CallbackOnceProc( AAudioStream *stream, void *userData, void *audioData, int32_t numFrames ) { (void) stream; (void) audioData; (void) numFrames; std::atomic *callbackCountPtr = (std::atomic *)userData; (*callbackCountPtr)++; return AAUDIO_CALLBACK_RESULT_STOP; } void checkCallbackOnce(aaudio_performance_mode_t perfMode) { std::atomic callbackCount{0}; AAudioStreamBuilder *aaudioBuilder = nullptr; AAudioStream *aaudioStream = nullptr; // Use an AAudioStreamBuilder to contain requested parameters. ASSERT_EQ(AAUDIO_OK, AAudio_createStreamBuilder(&aaudioBuilder)); // Request stream properties. AAudioStreamBuilder_setDataCallback(aaudioBuilder, CallbackOnceProc, &callbackCount); AAudioStreamBuilder_setPerformanceMode(aaudioBuilder, perfMode); // Create an AAudioStream using the Builder. ASSERT_EQ(AAUDIO_OK, AAudioStreamBuilder_openStream(aaudioBuilder, &aaudioStream)); AAudioStreamBuilder_delete(aaudioBuilder); ASSERT_EQ(AAUDIO_OK, AAudioStream_requestStart(aaudioStream)); sleep(1); // Give callback a chance to run many times. EXPECT_EQ(AAUDIO_OK, AAudioStream_requestStop(aaudioStream)); EXPECT_EQ(1, callbackCount.load()); // should stop after first call EXPECT_EQ(AAUDIO_OK, AAudioStream_close(aaudioStream)); } TEST(test_various, aaudio_callback_once_none) { checkCallbackOnce(AAUDIO_PERFORMANCE_MODE_NONE); } TEST(test_various, aaudio_callback_once_lowlat) { checkCallbackOnce(AAUDIO_PERFORMANCE_MODE_LOW_LATENCY); } // ************************************************************ struct WakeUpCallbackData { void wakeOther() { // signal waiting test to wake up { std::lock_guard lock(mutex); finished = true; } conditionVariable.notify_one(); } void waitForFinished() { std::unique_lock aLock(mutex); conditionVariable.wait(aLock, [=] { return finished; }); } // For signalling foreground test when callback finished std::mutex mutex; std::condition_variable conditionVariable; bool finished = false; }; // Test to make sure we cannot call recursively into the system from a callback. struct DangerousData : public WakeUpCallbackData { aaudio_result_t resultStart = AAUDIO_OK; aaudio_result_t resultStop = AAUDIO_OK; aaudio_result_t resultPause = AAUDIO_OK; aaudio_result_t resultFlush = AAUDIO_OK; aaudio_result_t resultClose = AAUDIO_OK; }; // Callback function that tries to call back into the stream. aaudio_data_callback_result_t DangerousDataCallbackProc( AAudioStream *stream, void *userData, void *audioData, int32_t numFrames) { (void) audioData; (void) numFrames; DangerousData *data = (DangerousData *)userData; data->resultStart = AAudioStream_requestStart(stream); data->resultStop = AAudioStream_requestStop(stream); data->resultPause = AAudioStream_requestPause(stream); data->resultFlush = AAudioStream_requestFlush(stream); data->resultClose = AAudioStream_close(stream); data->wakeOther(); return AAUDIO_CALLBACK_RESULT_STOP; } //int main() { // To fix Android Studio formatting when editing. void checkDangerousCallback(aaudio_performance_mode_t perfMode) { DangerousData dangerousData; AAudioStreamBuilder *aaudioBuilder = nullptr; AAudioStream *aaudioStream = nullptr; // Use an AAudioStreamBuilder to contain requested parameters. ASSERT_EQ(AAUDIO_OK, AAudio_createStreamBuilder(&aaudioBuilder)); // Request stream properties. AAudioStreamBuilder_setDataCallback(aaudioBuilder, DangerousDataCallbackProc, &dangerousData); AAudioStreamBuilder_setPerformanceMode(aaudioBuilder, perfMode); // Create an AAudioStream using the Builder. ASSERT_EQ(AAUDIO_OK, AAudioStreamBuilder_openStream(aaudioBuilder, &aaudioStream)); AAudioStreamBuilder_delete(aaudioBuilder); ASSERT_EQ(AAUDIO_OK, AAudioStream_requestStart(aaudioStream)); dangerousData.waitForFinished(); EXPECT_EQ(AAUDIO_OK, AAudioStream_requestStop(aaudioStream)); EXPECT_EQ(AAUDIO_ERROR_INVALID_STATE, dangerousData.resultStart); EXPECT_EQ(AAUDIO_ERROR_INVALID_STATE, dangerousData.resultStop); EXPECT_EQ(AAUDIO_ERROR_INVALID_STATE, dangerousData.resultPause); EXPECT_EQ(AAUDIO_ERROR_INVALID_STATE, dangerousData.resultFlush); EXPECT_EQ(AAUDIO_ERROR_INVALID_STATE, dangerousData.resultClose); EXPECT_EQ(AAUDIO_OK, AAudioStream_close(aaudioStream)); } //int main() { // To fix Android Studio formatting when editing. TEST(test_various, aaudio_callback_blockers_none) { checkDangerousCallback(AAUDIO_PERFORMANCE_MODE_NONE); } TEST(test_various, aaudio_callback_blockers_lowlat) { checkDangerousCallback(AAUDIO_PERFORMANCE_MODE_LOW_LATENCY); }