blob: 37b434eb8c99eda6ffc53f07fc5bda85d3bfd644 [file] [log] [blame]
/*
* 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();
}