| /* |
| * Copyright 2016 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. |
| */ |
| |
| #define LOG_NDEBUG 0 |
| #define LOG_TAG "AAudioTest" |
| |
| #include <gtest/gtest.h> |
| #include <utils/Log.h> |
| |
| #include <aaudio/AAudio.h> |
| #include "test_aaudio.h" |
| |
| int64_t getNanoseconds(clockid_t clockId) { |
| struct timespec time; |
| int result = clock_gettime(clockId, &time); |
| if (result < 0) { |
| return -errno; |
| } |
| return (time.tv_sec * NANOS_PER_SECOND) + time.tv_nsec; |
| } |
| |
| // Test AAudioStreamBuilder |
| TEST(test_aaudio, aaudio_stream_builder) { |
| |
| AAudioStreamBuilder* aaudioBuilder1 = nullptr; |
| AAudioStreamBuilder* aaudioBuilder2 = nullptr; |
| |
| // Use an AAudioStreamBuilder to define the stream. |
| aaudio_result_t result = AAudio_createStreamBuilder(&aaudioBuilder1); |
| ASSERT_EQ(AAUDIO_OK, result); |
| ASSERT_NE(nullptr, aaudioBuilder1); |
| |
| // Create a second builder and make sure they do not collide. |
| ASSERT_EQ(AAUDIO_OK, AAudio_createStreamBuilder(&aaudioBuilder2)); |
| ASSERT_NE(nullptr, aaudioBuilder2); |
| |
| ASSERT_NE(aaudioBuilder1, aaudioBuilder2); |
| |
| // Delete the first builder. |
| EXPECT_EQ(AAUDIO_OK, AAudioStreamBuilder_delete(aaudioBuilder1)); |
| |
| // Delete the second builder. |
| EXPECT_EQ(AAUDIO_OK, AAudioStreamBuilder_delete(aaudioBuilder2)); |
| |
| } |
| |
| // Test creating a default stream with everything unspecified. |
| TEST(test_aaudio, aaudio_stream_unspecified) { |
| AAudioStreamBuilder *aaudioBuilder = nullptr; |
| AAudioStream *aaudioStream = nullptr; |
| aaudio_result_t result = AAUDIO_OK; |
| |
| // Use an AAudioStreamBuilder to define the stream. |
| result = AAudio_createStreamBuilder(&aaudioBuilder); |
| ASSERT_EQ(AAUDIO_OK, result); |
| ASSERT_NE(nullptr, aaudioBuilder); |
| |
| // Create an AAudioStream using the Builder. |
| ASSERT_EQ(AAUDIO_OK, AAudioStreamBuilder_openStream(aaudioBuilder, &aaudioStream)); |
| ASSERT_NE(nullptr, aaudioStream); |
| |
| // Cleanup |
| EXPECT_EQ(AAUDIO_OK, AAudioStreamBuilder_delete(aaudioBuilder)); |
| EXPECT_EQ(AAUDIO_OK, AAudioStream_close(aaudioStream)); |
| } |
| |
| // Test Writing to an AAudioStream |
| void runtest_aaudio_stream(aaudio_sharing_mode_t requestedSharingMode) { |
| const int32_t requestedSampleRate = 48000; |
| const int32_t requestedSamplesPerFrame = 2; |
| const aaudio_audio_format_t requestedDataFormat = AAUDIO_FORMAT_PCM_I16; |
| |
| int32_t actualSampleRate = -1; |
| int32_t actualSamplesPerFrame = -1; |
| aaudio_audio_format_t actualDataFormat = AAUDIO_FORMAT_INVALID; |
| aaudio_sharing_mode_t actualSharingMode; |
| int32_t framesPerBurst = -1; |
| int writeLoops = 0; |
| |
| int32_t framesWritten = 0; |
| int32_t actualBufferSize = 0; |
| int64_t framesTotal = 0; |
| int64_t aaudioFramesRead = 0; |
| int64_t aaudioFramesRead1 = 0; |
| int64_t aaudioFramesRead2 = 0; |
| int64_t aaudioFramesWritten = 0; |
| |
| int64_t timeoutNanos; |
| |
| aaudio_stream_state_t state = AAUDIO_STREAM_STATE_UNINITIALIZED; |
| AAudioStreamBuilder *aaudioBuilder = nullptr; |
| AAudioStream *aaudioStream = nullptr; |
| |
| aaudio_result_t result = AAUDIO_OK; |
| |
| // Use an AAudioStreamBuilder to define the stream. |
| result = AAudio_createStreamBuilder(&aaudioBuilder); |
| ASSERT_EQ(AAUDIO_OK, result); |
| |
| // Request stream properties. |
| AAudioStreamBuilder_setDeviceId(aaudioBuilder, AAUDIO_DEVICE_UNSPECIFIED); |
| AAudioStreamBuilder_setDirection(aaudioBuilder, AAUDIO_DIRECTION_OUTPUT); |
| AAudioStreamBuilder_setSampleRate(aaudioBuilder, requestedSampleRate); |
| AAudioStreamBuilder_setSamplesPerFrame(aaudioBuilder, requestedSamplesPerFrame); |
| AAudioStreamBuilder_setFormat(aaudioBuilder, requestedDataFormat); |
| AAudioStreamBuilder_setSharingMode(aaudioBuilder, requestedSharingMode); |
| AAudioStreamBuilder_setBufferCapacityInFrames(aaudioBuilder, 2000); |
| |
| // Create an AAudioStream using the Builder. |
| ASSERT_EQ(AAUDIO_OK, AAudioStreamBuilder_openStream(aaudioBuilder, &aaudioStream)); |
| EXPECT_EQ(AAUDIO_OK, AAudioStreamBuilder_delete(aaudioBuilder)); |
| |
| EXPECT_EQ(AAUDIO_STREAM_STATE_OPEN, AAudioStream_getState(aaudioStream)); |
| EXPECT_EQ(AAUDIO_DIRECTION_OUTPUT, AAudioStream_getDirection(aaudioStream)); |
| |
| // Check to see what kind of stream we actually got. |
| actualSampleRate = AAudioStream_getSampleRate(aaudioStream); |
| ASSERT_GE(actualSampleRate, 44100); |
| ASSERT_LE(actualSampleRate, 96000); // TODO what is min/max? |
| |
| actualSamplesPerFrame = AAudioStream_getSamplesPerFrame(aaudioStream); |
| ASSERT_GE(actualSamplesPerFrame, 1); |
| ASSERT_LE(actualSamplesPerFrame, 16); // TODO what is min/max? |
| |
| actualSharingMode = AAudioStream_getSharingMode(aaudioStream); |
| ASSERT_TRUE(actualSharingMode == AAUDIO_SHARING_MODE_EXCLUSIVE |
| || actualSharingMode == AAUDIO_SHARING_MODE_SHARED); |
| |
| actualDataFormat = AAudioStream_getFormat(aaudioStream); |
| |
| // TODO test this on full build |
| // ASSERT_NE(AAUDIO_DEVICE_UNSPECIFIED, AAudioStream_getDeviceId(aaudioStream)); |
| |
| framesPerBurst = AAudioStream_getFramesPerBurst(aaudioStream); |
| ASSERT_GE(framesPerBurst, 16); |
| ASSERT_LE(framesPerBurst, 10000); // TODO what is min/max? |
| |
| // Allocate a buffer for the audio data. |
| // TODO handle possibility of other data formats |
| ASSERT_TRUE(actualDataFormat == AAUDIO_FORMAT_PCM_I16); |
| size_t dataSizeSamples = framesPerBurst * actualSamplesPerFrame; |
| int16_t *data = (int16_t *) calloc(dataSizeSamples, sizeof(int16_t)); |
| ASSERT_TRUE(nullptr != data); |
| |
| actualBufferSize = AAudioStream_getBufferSizeInFrames(aaudioStream); |
| actualBufferSize = AAudioStream_setBufferSizeInFrames(aaudioStream, actualBufferSize); |
| ASSERT_TRUE(actualBufferSize > 0); |
| |
| // Prime the buffer. |
| timeoutNanos = 0; |
| do { |
| framesWritten = AAudioStream_write(aaudioStream, data, framesPerBurst, timeoutNanos); |
| // There should be some room for priming the buffer. |
| framesTotal += framesWritten; |
| ASSERT_GE(framesWritten, 0); |
| ASSERT_LE(framesWritten, framesPerBurst); |
| } while (framesWritten > 0); |
| ASSERT_TRUE(framesTotal > 0); |
| |
| // Start/write/pause more than once to see if it fails after the first time. |
| // Write some data and measure the rate to see if the timing is OK. |
| for (int numLoops = 0; numLoops < 2; numLoops++) { |
| // Start and wait for server to respond. |
| ASSERT_EQ(AAUDIO_OK, AAudioStream_requestStart(aaudioStream)); |
| ASSERT_EQ(AAUDIO_OK, AAudioStream_waitForStateChange(aaudioStream, |
| AAUDIO_STREAM_STATE_STARTING, |
| &state, |
| DEFAULT_STATE_TIMEOUT)); |
| EXPECT_EQ(AAUDIO_STREAM_STATE_STARTED, state); |
| |
| // Write some data while we are running. Read counter should be advancing. |
| writeLoops = 1 * actualSampleRate / framesPerBurst; // 1 second |
| ASSERT_LT(2, writeLoops); // detect absurdly high framesPerBurst |
| timeoutNanos = 10 * NANOS_PER_SECOND * framesPerBurst / actualSampleRate; // bursts |
| framesWritten = 1; |
| aaudioFramesRead = AAudioStream_getFramesRead(aaudioStream); |
| aaudioFramesRead1 = aaudioFramesRead; |
| int64_t beginTime = getNanoseconds(CLOCK_MONOTONIC); |
| do { |
| framesWritten = AAudioStream_write(aaudioStream, data, framesPerBurst, timeoutNanos); |
| ASSERT_GE(framesWritten, 0); |
| ASSERT_LE(framesWritten, framesPerBurst); |
| |
| framesTotal += framesWritten; |
| aaudioFramesWritten = AAudioStream_getFramesWritten(aaudioStream); |
| EXPECT_EQ(framesTotal, aaudioFramesWritten); |
| |
| // Try to get a more accurate measure of the sample rate. |
| if (beginTime == 0) { |
| aaudioFramesRead = AAudioStream_getFramesRead(aaudioStream); |
| if (aaudioFramesRead > aaudioFramesRead1) { // is read pointer advancing |
| beginTime = getNanoseconds(CLOCK_MONOTONIC); |
| aaudioFramesRead1 = aaudioFramesRead; |
| } |
| } |
| } while (framesWritten > 0 && writeLoops-- > 0); |
| |
| aaudioFramesRead2 = AAudioStream_getFramesRead(aaudioStream); |
| int64_t endTime = getNanoseconds(CLOCK_MONOTONIC); |
| ASSERT_GT(aaudioFramesRead2, 0); |
| ASSERT_GT(aaudioFramesRead2, aaudioFramesRead1); |
| ASSERT_LE(aaudioFramesRead2, aaudioFramesWritten); |
| |
| // TODO why is AudioTrack path so inaccurate? |
| const double rateTolerance = 200.0; // arbitrary tolerance for sample rate |
| if (requestedSharingMode != AAUDIO_SHARING_MODE_SHARED) { |
| // Calculate approximate sample rate and compare with stream rate. |
| double seconds = (endTime - beginTime) / (double) NANOS_PER_SECOND; |
| double measuredRate = (aaudioFramesRead2 - aaudioFramesRead1) / seconds; |
| ASSERT_NEAR(actualSampleRate, measuredRate, rateTolerance); |
| } |
| |
| // Request async pause and wait for server to say that it has completed the pause. |
| ASSERT_EQ(AAUDIO_OK, AAudioStream_requestPause(aaudioStream)); |
| EXPECT_EQ(AAUDIO_OK, AAudioStream_waitForStateChange(aaudioStream, |
| AAUDIO_STREAM_STATE_PAUSING, |
| &state, |
| DEFAULT_STATE_TIMEOUT)); |
| EXPECT_EQ(AAUDIO_STREAM_STATE_PAUSED, state); |
| } |
| |
| // Make sure the read counter is not advancing when we are paused. |
| aaudioFramesRead = AAudioStream_getFramesRead(aaudioStream); |
| ASSERT_GE(aaudioFramesRead, aaudioFramesRead2); // monotonic increase |
| |
| // Use this to sleep by waiting for something that won't happen. |
| AAudioStream_waitForStateChange(aaudioStream, AAUDIO_STREAM_STATE_PAUSED, &state, timeoutNanos); |
| aaudioFramesRead2 = AAudioStream_getFramesRead(aaudioStream); |
| EXPECT_EQ(aaudioFramesRead, aaudioFramesRead2); |
| |
| // ------------------- TEST FLUSH ----------------- |
| // Prime the buffer. |
| timeoutNanos = 0; |
| writeLoops = 100; |
| do { |
| framesWritten = AAudioStream_write(aaudioStream, data, framesPerBurst, timeoutNanos); |
| framesTotal += framesWritten; |
| } while (framesWritten > 0 && writeLoops-- > 0); |
| EXPECT_EQ(0, framesWritten); |
| |
| // Flush and wait for server to respond. |
| ASSERT_EQ(AAUDIO_OK, AAudioStream_requestFlush(aaudioStream)); |
| EXPECT_EQ(AAUDIO_OK, AAudioStream_waitForStateChange(aaudioStream, |
| AAUDIO_STREAM_STATE_FLUSHING, |
| &state, |
| DEFAULT_STATE_TIMEOUT)); |
| EXPECT_EQ(AAUDIO_STREAM_STATE_FLUSHED, state); |
| |
| // After a flush, the read counter should be caught up with the write counter. |
| aaudioFramesWritten = AAudioStream_getFramesWritten(aaudioStream); |
| EXPECT_EQ(framesTotal, aaudioFramesWritten); |
| aaudioFramesRead = AAudioStream_getFramesRead(aaudioStream); |
| EXPECT_EQ(aaudioFramesRead, aaudioFramesWritten); |
| |
| sleep(1); // FIXME - The write returns 0 if we remove this sleep! Why? |
| |
| // The buffer should be empty after a flush so we should be able to write. |
| framesWritten = AAudioStream_write(aaudioStream, data, framesPerBurst, timeoutNanos); |
| // There should be some room for priming the buffer. |
| ASSERT_GT(framesWritten, 0); |
| ASSERT_LE(framesWritten, framesPerBurst); |
| |
| EXPECT_EQ(AAUDIO_OK, AAudioStream_close(aaudioStream)); |
| free(data); |
| } |
| |
| // Test Writing to an AAudioStream using SHARED mode. |
| TEST(test_aaudio, aaudio_stream_shared) { |
| runtest_aaudio_stream(AAUDIO_SHARING_MODE_SHARED); |
| } |
| |
| /* TODO Enable exclusive mode test. |
| // Test Writing to an AAudioStream using EXCLUSIVE sharing mode. |
| TEST(test_aaudio, aaudio_stream_exclusive) { |
| runtest_aaudio_stream(AAUDIO_SHARING_MODE_EXCLUSIVE); |
| } |
| */ |
| |
| int main(int argc, char **argv) { |
| testing::InitGoogleTest(&argc, argv); |
| |
| return RUN_ALL_TESTS(); |
| } |