| /* |
| * Copyright (C) 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. |
| */ |
| |
| // Unit tests for Isochronous Clock Model |
| |
| #include <math.h> |
| #include <stdlib.h> |
| |
| |
| #include <aaudio/AAudio.h> |
| #include <audio_utils/clock.h> |
| #include <client/IsochronousClockModel.h> |
| #include <gtest/gtest.h> |
| |
| using namespace aaudio; |
| |
| // We can use arbitrary values here because we are not opening a real audio stream. |
| #define SAMPLE_RATE 48000 |
| #define HW_FRAMES_PER_BURST 48 |
| #define NANOS_PER_BURST (NANOS_PER_SECOND * HW_FRAMES_PER_BURST / SAMPLE_RATE) |
| |
| class ClockModelTestFixture: public ::testing::Test { |
| public: |
| ClockModelTestFixture() { |
| } |
| |
| void SetUp() { |
| model.setSampleRate(SAMPLE_RATE); |
| model.setFramesPerBurst(HW_FRAMES_PER_BURST); |
| } |
| |
| void TearDown() { |
| } |
| |
| ~ClockModelTestFixture() { |
| // cleanup any pending stuff, but no exceptions allowed |
| } |
| |
| // Test processing of timestamps when the hardware may be slightly off from |
| // the expected sample rate. |
| void checkDriftingClock(double hardwareFramesPerSecond, int numLoops) { |
| const int64_t startTimeNanos = 500000000; // arbitrary |
| model.start(startTimeNanos); |
| |
| const int64_t startPositionFrames = HW_FRAMES_PER_BURST; // hardware |
| // arbitrary time for first burst |
| const int64_t markerTime = startTimeNanos + NANOS_PER_MILLISECOND |
| + (200 * NANOS_PER_MICROSECOND); |
| |
| // Should set initial marker. |
| model.processTimestamp(startPositionFrames, markerTime); |
| ASSERT_EQ(startPositionFrames, model.convertTimeToPosition(markerTime)); |
| |
| double elapsedTimeSeconds = startTimeNanos / (double) NANOS_PER_SECOND; |
| for (int i = 0; i < numLoops; i++) { |
| // Calculate random delay over several bursts. |
| const double timeDelaySeconds = 10.0 * drand48() * NANOS_PER_BURST / NANOS_PER_SECOND; |
| elapsedTimeSeconds += timeDelaySeconds; |
| const int64_t elapsedTimeNanos = (int64_t)(elapsedTimeSeconds * NANOS_PER_SECOND); |
| const int64_t currentTimeNanos = startTimeNanos + elapsedTimeNanos; |
| // Simulate DSP running at the specified rate. |
| const int64_t currentTimeFrames = startPositionFrames + |
| (int64_t)(hardwareFramesPerSecond * elapsedTimeSeconds); |
| const int64_t numBursts = currentTimeFrames / HW_FRAMES_PER_BURST; |
| const int64_t alignedPosition = startPositionFrames + (numBursts * HW_FRAMES_PER_BURST); |
| |
| // Apply drifting timestamp. |
| model.processTimestamp(alignedPosition, currentTimeNanos); |
| |
| ASSERT_EQ(alignedPosition, model.convertTimeToPosition(currentTimeNanos)); |
| } |
| } |
| |
| IsochronousClockModel model; |
| }; |
| |
| // Check default setup. |
| TEST_F(ClockModelTestFixture, clock_setup) { |
| ASSERT_EQ(SAMPLE_RATE, model.getSampleRate()); |
| ASSERT_EQ(HW_FRAMES_PER_BURST, model.getFramesPerBurst()); |
| } |
| |
| // Test delta calculations. |
| TEST_F(ClockModelTestFixture, clock_deltas) { |
| int64_t position = model.convertDeltaTimeToPosition(NANOS_PER_SECOND); |
| ASSERT_EQ(SAMPLE_RATE, position); |
| |
| // Deltas are not quantized. |
| // Compare time to the equivalent position in frames. |
| constexpr int64_t kNanosPerBurst = HW_FRAMES_PER_BURST * NANOS_PER_SECOND / SAMPLE_RATE; |
| position = model.convertDeltaTimeToPosition(NANOS_PER_SECOND + (kNanosPerBurst / 2)); |
| ASSERT_EQ(SAMPLE_RATE + (HW_FRAMES_PER_BURST / 2), position); |
| |
| int64_t time = model.convertDeltaPositionToTime(SAMPLE_RATE); |
| ASSERT_EQ(NANOS_PER_SECOND, time); |
| |
| // Compare position in frames to the equivalent time. |
| time = model.convertDeltaPositionToTime(SAMPLE_RATE + (HW_FRAMES_PER_BURST / 2)); |
| ASSERT_EQ(NANOS_PER_SECOND + (kNanosPerBurst / 2), time); |
| } |
| |
| // start() should force the internal markers |
| TEST_F(ClockModelTestFixture, clock_start) { |
| const int64_t startTime = 100000; |
| model.start(startTime); |
| |
| int64_t position = model.convertTimeToPosition(startTime); |
| EXPECT_EQ(0, position); |
| |
| int64_t time = model.convertPositionToTime(position); |
| EXPECT_EQ(startTime, time); |
| |
| time = startTime + (500 * NANOS_PER_MICROSECOND); |
| position = model.convertTimeToPosition(time); |
| EXPECT_EQ(0, position); |
| } |
| |
| // timestamps moves the window if outside the bounds |
| TEST_F(ClockModelTestFixture, clock_timestamp) { |
| const int64_t startTime = 100000000; |
| model.start(startTime); |
| |
| const int64_t position = HW_FRAMES_PER_BURST; // hardware |
| int64_t markerTime = startTime + NANOS_PER_MILLISECOND + (200 * NANOS_PER_MICROSECOND); |
| |
| // Should set marker. |
| model.processTimestamp(position, markerTime); |
| EXPECT_EQ(position, model.convertTimeToPosition(markerTime)); |
| |
| // convertTimeToPosition rounds down |
| EXPECT_EQ(position, model.convertTimeToPosition(markerTime + (73 * NANOS_PER_MICROSECOND))); |
| |
| // convertPositionToTime rounds up |
| EXPECT_EQ(markerTime + NANOS_PER_BURST, model.convertPositionToTime(position + 17)); |
| } |
| |
| #define NUM_LOOPS_DRIFT 10000 |
| |
| // test nudging the window by using a drifting HW clock |
| TEST_F(ClockModelTestFixture, clock_no_drift) { |
| checkDriftingClock(SAMPLE_RATE, NUM_LOOPS_DRIFT); |
| } |
| |
| // These slow drift rates caused errors when I disabled the code that handles |
| // drifting in the clock model. So I think the test is valid. |
| // It is unlikely that real hardware would be off by more than this amount. |
| TEST_F(ClockModelTestFixture, clock_slow_drift) { |
| checkDriftingClock(0.998 * SAMPLE_RATE, NUM_LOOPS_DRIFT); |
| } |
| |
| TEST_F(ClockModelTestFixture, clock_fast_drift) { |
| checkDriftingClock(1.002 * SAMPLE_RATE, NUM_LOOPS_DRIFT); |
| } |