blob: fb363e7bbaf7e6dcbfc55618cfa7c1b40c9896d3 [file] [log] [blame]
Phil Burk97350f92017-07-21 15:59:44 -07001/*
2 * Copyright (C) 2017 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// Play silence and recover from dead servers or disconnected devices.
18
19#include <stdio.h>
Phil Burkcda5c072017-08-31 17:23:18 -070020#include <stdlib.h>
Phil Burk97350f92017-07-21 15:59:44 -070021#include <unistd.h>
22
23#include <aaudio/AAudio.h>
24#include <aaudio/AAudioTesting.h>
Phil Burk97350f92017-07-21 15:59:44 -070025#include "utils/AAudioExampleUtils.h"
Phil Burkcda5c072017-08-31 17:23:18 -070026#include "../examples/utils/AAudioExampleUtils.h"
Phil Burk97350f92017-07-21 15:59:44 -070027
Phil Burkcda5c072017-08-31 17:23:18 -070028// Arbitrary period for glitches, once per second at 48000 Hz.
29#define FORCED_UNDERRUN_PERIOD_FRAMES 48000
30// How long to sleep in a callback to cause an intentional glitch. For testing.
31#define FORCED_UNDERRUN_SLEEP_MICROS (10 * 1000)
Phil Burk97350f92017-07-21 15:59:44 -070032
Phil Burkcda5c072017-08-31 17:23:18 -070033#define MAX_TIMESTAMPS 1000
Phil Burk97350f92017-07-21 15:59:44 -070034
Phil Burkcda5c072017-08-31 17:23:18 -070035#define DEFAULT_TIMEOUT_NANOS ((int64_t)1000000000)
36
37#define NUM_SECONDS 1
38#define NUM_LOOPS 4
39
40typedef struct TimestampInfo {
41 int64_t framesTotal;
42 int64_t appPosition; // frames
43 int64_t appNanoseconds;
44 int64_t timestampPosition; // frames
45 int64_t timestampNanos;
46 aaudio_result_t result;
47} TimestampInfo;
48
49typedef struct TimestampCallbackData_s {
50 TimestampInfo timestamps[MAX_TIMESTAMPS];
51 int64_t framesTotal = 0;
52 int64_t nextFrameToGlitch = FORCED_UNDERRUN_PERIOD_FRAMES;
53 int32_t timestampCount = 0; // in timestamps
54 bool forceUnderruns = false;
55} TimestampCallbackData_t;
56
57// Callback function that fills the audio output buffer.
58aaudio_data_callback_result_t timestampDataCallbackProc(
59 AAudioStream *stream,
60 void *userData,
61 void *audioData __unused,
62 int32_t numFrames
63) {
64
65 // should not happen but just in case...
66 if (userData == nullptr) {
67 printf("ERROR - SimplePlayerDataCallbackProc needs userData\n");
68 return AAUDIO_CALLBACK_RESULT_STOP;
69 }
70 TimestampCallbackData_t *timestampData = (TimestampCallbackData_t *) userData;
71
72 aaudio_direction_t direction = AAudioStream_getDirection(stream);
73 if (direction == AAUDIO_DIRECTION_INPUT) {
74 timestampData->framesTotal += numFrames;
75 }
76
77 if (timestampData->forceUnderruns) {
78 if (timestampData->framesTotal > timestampData->nextFrameToGlitch) {
79 usleep(FORCED_UNDERRUN_SLEEP_MICROS);
80 printf("Simulate glitch at %lld\n", (long long) timestampData->framesTotal);
81 timestampData->nextFrameToGlitch += FORCED_UNDERRUN_PERIOD_FRAMES;
82 }
83 }
84
85 if (timestampData->timestampCount < MAX_TIMESTAMPS) {
86 TimestampInfo *timestamp = &timestampData->timestamps[timestampData->timestampCount];
87 timestamp->result = AAudioStream_getTimestamp(stream,
88 CLOCK_MONOTONIC,
89 &timestamp->timestampPosition,
90 &timestamp->timestampNanos);
91 timestamp->framesTotal = timestampData->framesTotal;
92 timestamp->appPosition = (direction == AAUDIO_DIRECTION_OUTPUT)
93 ? AAudioStream_getFramesWritten(stream)
94 : AAudioStream_getFramesRead(stream);
95 timestamp->appNanoseconds = getNanoseconds();
96 timestampData->timestampCount++;
97 }
98
99 if (direction == AAUDIO_DIRECTION_OUTPUT) {
100 timestampData->framesTotal += numFrames;
101 }
102 return AAUDIO_CALLBACK_RESULT_CONTINUE;
103}
104
105static TimestampCallbackData_t sTimestampData;
106
107static aaudio_result_t testTimeStamps(aaudio_policy_t mmapPolicy,
108 aaudio_sharing_mode_t sharingMode,
109 aaudio_performance_mode_t performanceMode,
110 aaudio_direction_t direction) {
Phil Burk97350f92017-07-21 15:59:44 -0700111 aaudio_result_t result = AAUDIO_OK;
112
Phil Burk97350f92017-07-21 15:59:44 -0700113 int32_t framesPerBurst = 0;
114 float *buffer = nullptr;
115
116 int32_t actualChannelCount = 0;
117 int32_t actualSampleRate = 0;
118 int32_t originalBufferSize = 0;
119 int32_t requestedBufferSize = 0;
120 int32_t finalBufferSize = 0;
121 aaudio_format_t actualDataFormat = AAUDIO_FORMAT_PCM_FLOAT;
122 aaudio_sharing_mode_t actualSharingMode = AAUDIO_SHARING_MODE_SHARED;
Phil Burkcda5c072017-08-31 17:23:18 -0700123 aaudio_sharing_mode_t actualPerformanceMode = AAUDIO_PERFORMANCE_MODE_NONE;
Phil Burk97350f92017-07-21 15:59:44 -0700124
125 AAudioStreamBuilder *aaudioBuilder = nullptr;
126 AAudioStream *aaudioStream = nullptr;
127
Phil Burkcda5c072017-08-31 17:23:18 -0700128 memset(&sTimestampData, 0, sizeof(sTimestampData));
Phil Burk97350f92017-07-21 15:59:44 -0700129
Phil Burkcda5c072017-08-31 17:23:18 -0700130 printf("------------ testTimeStamps(policy = %d, sharing = %s, perf = %s, dir = %s) -----------\n",
131 mmapPolicy,
132 getSharingModeText(sharingMode),
133 getPerformanceModeText(performanceMode),
134 getDirectionText(direction));
Phil Burk97350f92017-07-21 15:59:44 -0700135
Phil Burkcda5c072017-08-31 17:23:18 -0700136 AAudio_setMMapPolicy(mmapPolicy);
Phil Burk97350f92017-07-21 15:59:44 -0700137
138 // Use an AAudioStreamBuilder to contain requested parameters.
139 result = AAudio_createStreamBuilder(&aaudioBuilder);
140 if (result != AAUDIO_OK) {
141 printf("AAudio_createStreamBuilder returned %s",
142 AAudio_convertResultToText(result));
143 goto finish;
144 }
145
146 // Request stream properties.
Phil Burkcda5c072017-08-31 17:23:18 -0700147 AAudioStreamBuilder_setFormat(aaudioBuilder, AAUDIO_FORMAT_PCM_I16);
148 AAudioStreamBuilder_setSharingMode(aaudioBuilder, sharingMode);
149 AAudioStreamBuilder_setPerformanceMode(aaudioBuilder, performanceMode);
150 AAudioStreamBuilder_setDirection(aaudioBuilder, direction);
151 AAudioStreamBuilder_setDataCallback(aaudioBuilder, timestampDataCallbackProc, &sTimestampData);
Phil Burk97350f92017-07-21 15:59:44 -0700152
153 // Create an AAudioStream using the Builder.
154 result = AAudioStreamBuilder_openStream(aaudioBuilder, &aaudioStream);
155 if (result != AAUDIO_OK) {
156 printf("AAudioStreamBuilder_openStream returned %s",
157 AAudio_convertResultToText(result));
158 goto finish;
159 }
160
161 // Check to see what kind of stream we actually got.
162 actualSampleRate = AAudioStream_getSampleRate(aaudioStream);
163 actualChannelCount = AAudioStream_getChannelCount(aaudioStream);
164 actualDataFormat = AAudioStream_getFormat(aaudioStream);
165
Phil Burkcda5c072017-08-31 17:23:18 -0700166 actualSharingMode = AAudioStream_getSharingMode(aaudioStream);
167 if (actualSharingMode != sharingMode) {
168 printf("did not get expected sharingMode, got %3d, skipping test\n",
169 actualSharingMode);
170 result = AAUDIO_OK;
171 goto finish;
172 }
173 actualPerformanceMode = AAudioStream_getPerformanceMode(aaudioStream);
174 if (actualPerformanceMode != performanceMode) {
175 printf("did not get expected performanceMode, got %3d, skipping test\n",
176 actualPerformanceMode);
177 result = AAUDIO_OK;
178 goto finish;
179 }
180
181 printf(" chans = %3d, rate = %6d format = %d\n",
182 actualChannelCount, actualSampleRate, actualDataFormat);
Phil Burk97350f92017-07-21 15:59:44 -0700183 printf(" Is MMAP used? %s\n", AAudioStream_isMMapUsed(aaudioStream)
Phil Burkcda5c072017-08-31 17:23:18 -0700184 ? "yes" : "no");
Phil Burk97350f92017-07-21 15:59:44 -0700185
186 // This is the number of frames that are read in one chunk by a DMA controller
187 // or a DSP or a mixer.
188 framesPerBurst = AAudioStream_getFramesPerBurst(aaudioStream);
189 printf(" framesPerBurst = %3d\n", framesPerBurst);
190
191 originalBufferSize = AAudioStream_getBufferSizeInFrames(aaudioStream);
Phil Burkcda5c072017-08-31 17:23:18 -0700192 requestedBufferSize = 4 * framesPerBurst;
Phil Burk97350f92017-07-21 15:59:44 -0700193 finalBufferSize = AAudioStream_setBufferSizeInFrames(aaudioStream, requestedBufferSize);
194
195 printf(" BufferSize: original = %4d, requested = %4d, final = %4d\n",
196 originalBufferSize, requestedBufferSize, finalBufferSize);
197
Phil Burkcda5c072017-08-31 17:23:18 -0700198 {
199 int64_t position;
200 int64_t nanoseconds;
201 result = AAudioStream_getTimestamp(aaudioStream, CLOCK_MONOTONIC, &position, &nanoseconds);
202 printf("before start, AAudioStream_getTimestamp() returns %s\n",
Phil Burk97350f92017-07-21 15:59:44 -0700203 AAudio_convertResultToText(result));
Phil Burk97350f92017-07-21 15:59:44 -0700204 }
205
Phil Burkcda5c072017-08-31 17:23:18 -0700206 for (int runs = 0; runs < NUM_LOOPS; runs++) {
207 printf("------------------ loop #%d\n", runs);
208
209 int64_t temp = sTimestampData.framesTotal;
210 memset(&sTimestampData, 0, sizeof(sTimestampData));
211 sTimestampData.framesTotal = temp;
212
213 sTimestampData.forceUnderruns = false;
214
215 result = AAudioStream_requestStart(aaudioStream);
216 if (result != AAUDIO_OK) {
217 printf("AAudioStream_requestStart returned %s",
218 AAudio_convertResultToText(result));
219 goto finish;
220 }
221
222 for (int second = 0; second < NUM_SECONDS; second++) {
223 // Give AAudio callback time to run in the background.
224 sleep(1);
225
226 // Periodically print the progress so we know it hasn't died.
227 printf("framesWritten = %d, XRuns = %d\n",
228 (int) AAudioStream_getFramesWritten(aaudioStream),
229 (int) AAudioStream_getXRunCount(aaudioStream)
230 );
231 }
232
233 result = AAudioStream_requestStop(aaudioStream);
234 if (result != AAUDIO_OK) {
235 printf("AAudioStream_requestStop returned %s\n",
236 AAudio_convertResultToText(result));
237 }
238
239 printf("timestampCount = %d\n", sTimestampData.timestampCount);
240 int printed = 0;
241 for (int i = 0; i < sTimestampData.timestampCount; i++) {
242 TimestampInfo *timestamp = &sTimestampData.timestamps[i];
243 bool posChanged = (timestamp->timestampPosition != (timestamp - 1)->timestampPosition);
244 bool timeChanged = (timestamp->timestampNanos != (timestamp - 1)->timestampNanos);
245 if ((printed < 20) && ((i < 10) || posChanged || timeChanged)) {
246 printf(" %3d : frames %8lld, xferd %8lld", i,
247 (long long) timestamp->framesTotal,
248 (long long) timestamp->appPosition);
249 if (timestamp->result != AAUDIO_OK) {
250 printf(", result = %s\n", AAudio_convertResultToText(timestamp->result));
251 } else {
252 bool negative = timestamp->timestampPosition < 0;
253 bool retro = (i > 0 && (timestamp->timestampPosition <
254 (timestamp - 1)->timestampPosition));
255 const char *message = negative ? " <=NEGATIVE!"
256 : (retro ? " <= RETROGRADE!" : "");
257
258 double latency = calculateLatencyMillis(timestamp->timestampPosition,
259 timestamp->timestampNanos,
260 timestamp->appPosition,
261 timestamp->appNanoseconds,
262 actualSampleRate);
263 printf(", STAMP: pos = %8lld, nanos = %8lld, lat = %7.1f msec %s\n",
264 (long long) timestamp->timestampPosition,
265 (long long) timestamp->timestampNanos,
266 latency,
267 message);
268 }
269 printed++;
Phil Burk97350f92017-07-21 15:59:44 -0700270 }
271 }
272
Phil Burkcda5c072017-08-31 17:23:18 -0700273 // Avoid race conditions in AudioFlinger.
274 // There is normally a delay between a real user stopping and restarting a stream.
275 sleep(1);
Phil Burk97350f92017-07-21 15:59:44 -0700276 }
277
Phil Burk97350f92017-07-21 15:59:44 -0700278finish:
279 if (aaudioStream != nullptr) {
280 AAudioStream_close(aaudioStream);
281 }
282 AAudioStreamBuilder_delete(aaudioBuilder);
Phil Burk97350f92017-07-21 15:59:44 -0700283 printf("result = %d = %s\n", result, AAudio_convertResultToText(result));
Phil Burkcda5c072017-08-31 17:23:18 -0700284
285 return result;
286}
287
288int main(int argc, char **argv) {
289 (void) argc;
290 (void *) argv;
291
292 aaudio_result_t result = AAUDIO_OK;
293
294 // Make printf print immediately so that debug info is not stuck
295 // in a buffer if we hang or crash.
296 setvbuf(stdout, nullptr, _IONBF, (size_t) 0);
297
Phil Burkc75d97f2017-09-08 15:48:36 -0700298 printf("Test Timestamps V0.1.3\n");
299
Phil Burkcda5c072017-08-31 17:23:18 -0700300 // Legacy
Phil Burkc75d97f2017-09-08 15:48:36 -0700301 aaudio_policy_t policy = AAUDIO_POLICY_NEVER;
302 result = testTimeStamps(policy,
303 AAUDIO_SHARING_MODE_SHARED,
304 AAUDIO_PERFORMANCE_MODE_NONE,
305 AAUDIO_DIRECTION_INPUT);
306 result = testTimeStamps(policy,
307 AAUDIO_SHARING_MODE_SHARED,
308 AAUDIO_PERFORMANCE_MODE_LOW_LATENCY,
309 AAUDIO_DIRECTION_INPUT);
310 result = testTimeStamps(policy,
311 AAUDIO_SHARING_MODE_SHARED,
312 AAUDIO_PERFORMANCE_MODE_NONE,
313 AAUDIO_DIRECTION_OUTPUT);
314 result = testTimeStamps(policy,
315 AAUDIO_SHARING_MODE_SHARED,
Phil Burkcda5c072017-08-31 17:23:18 -0700316 AAUDIO_PERFORMANCE_MODE_LOW_LATENCY,
317 AAUDIO_DIRECTION_OUTPUT);
Phil Burkc75d97f2017-09-08 15:48:36 -0700318
319 // MMAP
320 policy = AAUDIO_POLICY_ALWAYS;
321 result = testTimeStamps(policy,
322 AAUDIO_SHARING_MODE_EXCLUSIVE,
323 AAUDIO_PERFORMANCE_MODE_LOW_LATENCY,
324 AAUDIO_DIRECTION_INPUT);
325 result = testTimeStamps(policy,
326 AAUDIO_SHARING_MODE_EXCLUSIVE,
327 AAUDIO_PERFORMANCE_MODE_LOW_LATENCY,
328 AAUDIO_DIRECTION_OUTPUT);
329 result = testTimeStamps(policy,
330 AAUDIO_SHARING_MODE_SHARED,
331 AAUDIO_PERFORMANCE_MODE_LOW_LATENCY,
332 AAUDIO_DIRECTION_INPUT);
333 result = testTimeStamps(policy,
334 AAUDIO_SHARING_MODE_SHARED,
335 AAUDIO_PERFORMANCE_MODE_LOW_LATENCY,
336 AAUDIO_DIRECTION_OUTPUT);
Phil Burkcda5c072017-08-31 17:23:18 -0700337
338 return (result == AAUDIO_OK) ? EXIT_SUCCESS : EXIT_FAILURE;
Phil Burk97350f92017-07-21 15:59:44 -0700339}