• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 // Unit tests for Isochronous Clock Model
18 
19 #include <math.h>
20 #include <stdlib.h>
21 
22 
23 #include <aaudio/AAudio.h>
24 #include <audio_utils/clock.h>
25 #include <client/IsochronousClockModel.h>
26 #include <gtest/gtest.h>
27 
28 using namespace aaudio;
29 
30 // We can use arbitrary values here because we are not opening a real audio stream.
31 #define SAMPLE_RATE             48000
32 #define HW_FRAMES_PER_BURST     48
33 #define NANOS_PER_BURST         (NANOS_PER_SECOND * HW_FRAMES_PER_BURST / SAMPLE_RATE)
34 
35 class ClockModelTestFixture: public ::testing::Test {
36 public:
ClockModelTestFixture()37     ClockModelTestFixture() {
38     }
39 
SetUp()40     void SetUp() {
41         model.setSampleRate(SAMPLE_RATE);
42         model.setFramesPerBurst(HW_FRAMES_PER_BURST);
43     }
44 
TearDown()45     void TearDown() {
46     }
47 
~ClockModelTestFixture()48     ~ClockModelTestFixture()  {
49         // cleanup any pending stuff, but no exceptions allowed
50     }
51 
52     // Test processing of timestamps when the hardware may be slightly off from
53     // the expected sample rate.
checkDriftingClock(double hardwareFramesPerSecond,int numLoops)54     void checkDriftingClock(double hardwareFramesPerSecond, int numLoops) {
55         const int64_t startTimeNanos = 500000000; // arbitrary
56         model.start(startTimeNanos);
57 
58         const int64_t startPositionFrames = HW_FRAMES_PER_BURST; // hardware
59         // arbitrary time for first burst
60         const int64_t markerTime = startTimeNanos + NANOS_PER_MILLISECOND
61                 + (200 * NANOS_PER_MICROSECOND);
62 
63         // Should set initial marker.
64         model.processTimestamp(startPositionFrames, markerTime);
65         ASSERT_EQ(startPositionFrames, model.convertTimeToPosition(markerTime));
66 
67         double elapsedTimeSeconds = startTimeNanos / (double) NANOS_PER_SECOND;
68         for (int i = 0; i < numLoops; i++) {
69             // Calculate random delay over several bursts.
70             const double timeDelaySeconds = 10.0 * drand48() * NANOS_PER_BURST / NANOS_PER_SECOND;
71             elapsedTimeSeconds += timeDelaySeconds;
72             const int64_t elapsedTimeNanos = (int64_t)(elapsedTimeSeconds * NANOS_PER_SECOND);
73             const int64_t currentTimeNanos = startTimeNanos + elapsedTimeNanos;
74             // Simulate DSP running at the specified rate.
75             const int64_t currentTimeFrames = startPositionFrames +
76                                         (int64_t)(hardwareFramesPerSecond * elapsedTimeSeconds);
77             const int64_t numBursts = currentTimeFrames / HW_FRAMES_PER_BURST;
78             const int64_t alignedPosition = startPositionFrames + (numBursts * HW_FRAMES_PER_BURST);
79 
80             // Apply drifting timestamp.
81             model.processTimestamp(alignedPosition, currentTimeNanos);
82 
83             ASSERT_EQ(alignedPosition, model.convertTimeToPosition(currentTimeNanos));
84         }
85     }
86 
87     IsochronousClockModel model;
88 };
89 
90 // Check default setup.
TEST_F(ClockModelTestFixture,clock_setup)91 TEST_F(ClockModelTestFixture, clock_setup) {
92     ASSERT_EQ(SAMPLE_RATE, model.getSampleRate());
93     ASSERT_EQ(HW_FRAMES_PER_BURST, model.getFramesPerBurst());
94 }
95 
96 // Test delta calculations.
TEST_F(ClockModelTestFixture,clock_deltas)97 TEST_F(ClockModelTestFixture, clock_deltas) {
98     int64_t position = model.convertDeltaTimeToPosition(NANOS_PER_SECOND);
99     ASSERT_EQ(SAMPLE_RATE, position);
100 
101     // Deltas are not quantized.
102     // Compare time to the equivalent position in frames.
103     constexpr int64_t kNanosPerBurst = HW_FRAMES_PER_BURST * NANOS_PER_SECOND / SAMPLE_RATE;
104     position = model.convertDeltaTimeToPosition(NANOS_PER_SECOND + (kNanosPerBurst / 2));
105     ASSERT_EQ(SAMPLE_RATE + (HW_FRAMES_PER_BURST / 2), position);
106 
107     int64_t time = model.convertDeltaPositionToTime(SAMPLE_RATE);
108     ASSERT_EQ(NANOS_PER_SECOND, time);
109 
110     // Compare position in frames to the equivalent time.
111     time = model.convertDeltaPositionToTime(SAMPLE_RATE + (HW_FRAMES_PER_BURST / 2));
112     ASSERT_EQ(NANOS_PER_SECOND + (kNanosPerBurst / 2), time);
113 }
114 
115 // start() should force the internal markers
TEST_F(ClockModelTestFixture,clock_start)116 TEST_F(ClockModelTestFixture, clock_start) {
117     const int64_t startTime = 100000;
118     model.start(startTime);
119 
120     int64_t position = model.convertTimeToPosition(startTime);
121     EXPECT_EQ(0, position);
122 
123     int64_t time = model.convertPositionToTime(position);
124     EXPECT_EQ(startTime, time);
125 
126     time = startTime + (500 * NANOS_PER_MICROSECOND);
127     position = model.convertTimeToPosition(time);
128     EXPECT_EQ(0, position);
129 }
130 
131 // timestamps moves the window if outside the bounds
TEST_F(ClockModelTestFixture,clock_timestamp)132 TEST_F(ClockModelTestFixture, clock_timestamp) {
133     const int64_t startTime = 100000000;
134     model.start(startTime);
135 
136     const int64_t position = HW_FRAMES_PER_BURST; // hardware
137     int64_t markerTime = startTime + NANOS_PER_MILLISECOND + (200 * NANOS_PER_MICROSECOND);
138 
139     // Should set marker.
140     model.processTimestamp(position, markerTime);
141     EXPECT_EQ(position, model.convertTimeToPosition(markerTime));
142 
143     // convertTimeToPosition rounds down
144     EXPECT_EQ(position, model.convertTimeToPosition(markerTime + (73 * NANOS_PER_MICROSECOND)));
145 
146     // convertPositionToTime rounds up
147     EXPECT_EQ(markerTime + NANOS_PER_BURST, model.convertPositionToTime(position + 17));
148 }
149 
150 #define NUM_LOOPS_DRIFT   10000
151 
152 // test nudging the window by using a drifting HW clock
TEST_F(ClockModelTestFixture,clock_no_drift)153 TEST_F(ClockModelTestFixture, clock_no_drift) {
154     checkDriftingClock(SAMPLE_RATE, NUM_LOOPS_DRIFT);
155 }
156 
157 // These slow drift rates caused errors when I disabled the code that handles
158 // drifting in the clock model. So I think the test is valid.
159 // It is unlikely that real hardware would be off by more than this amount.
TEST_F(ClockModelTestFixture,clock_slow_drift)160 TEST_F(ClockModelTestFixture, clock_slow_drift) {
161     checkDriftingClock(0.998 * SAMPLE_RATE, NUM_LOOPS_DRIFT);
162 }
163 
TEST_F(ClockModelTestFixture,clock_fast_drift)164 TEST_F(ClockModelTestFixture, clock_fast_drift) {
165     checkDriftingClock(1.002 * SAMPLE_RATE, NUM_LOOPS_DRIFT);
166 }