blob: dfa781521d205daba5e26927961159b8645b3803 [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>
Dan Willemsen32959132017-10-30 13:36:57 -070025#include "AAudioExampleUtils.h"
Phil Burk97350f92017-07-21 15:59:44 -070026
Phil Burkcda5c072017-08-31 17:23:18 -070027// Arbitrary period for glitches, once per second at 48000 Hz.
28#define FORCED_UNDERRUN_PERIOD_FRAMES 48000
29// How long to sleep in a callback to cause an intentional glitch. For testing.
30#define FORCED_UNDERRUN_SLEEP_MICROS (10 * 1000)
Phil Burk97350f92017-07-21 15:59:44 -070031
Phil Burkcda5c072017-08-31 17:23:18 -070032#define MAX_TIMESTAMPS 1000
Phil Burk97350f92017-07-21 15:59:44 -070033
Phil Burkcda5c072017-08-31 17:23:18 -070034#define DEFAULT_TIMEOUT_NANOS ((int64_t)1000000000)
35
36#define NUM_SECONDS 1
37#define NUM_LOOPS 4
38
39typedef struct TimestampInfo {
40 int64_t framesTotal;
41 int64_t appPosition; // frames
42 int64_t appNanoseconds;
43 int64_t timestampPosition; // frames
44 int64_t timestampNanos;
45 aaudio_result_t result;
46} TimestampInfo;
47
48typedef struct TimestampCallbackData_s {
49 TimestampInfo timestamps[MAX_TIMESTAMPS];
50 int64_t framesTotal = 0;
51 int64_t nextFrameToGlitch = FORCED_UNDERRUN_PERIOD_FRAMES;
52 int32_t timestampCount = 0; // in timestamps
53 bool forceUnderruns = false;
54} TimestampCallbackData_t;
55
56// Callback function that fills the audio output buffer.
57aaudio_data_callback_result_t timestampDataCallbackProc(
58 AAudioStream *stream,
59 void *userData,
60 void *audioData __unused,
61 int32_t numFrames
62) {
63
64 // should not happen but just in case...
65 if (userData == nullptr) {
66 printf("ERROR - SimplePlayerDataCallbackProc needs userData\n");
67 return AAUDIO_CALLBACK_RESULT_STOP;
68 }
69 TimestampCallbackData_t *timestampData = (TimestampCallbackData_t *) userData;
70
71 aaudio_direction_t direction = AAudioStream_getDirection(stream);
72 if (direction == AAUDIO_DIRECTION_INPUT) {
73 timestampData->framesTotal += numFrames;
74 }
75
76 if (timestampData->forceUnderruns) {
77 if (timestampData->framesTotal > timestampData->nextFrameToGlitch) {
78 usleep(FORCED_UNDERRUN_SLEEP_MICROS);
79 printf("Simulate glitch at %lld\n", (long long) timestampData->framesTotal);
80 timestampData->nextFrameToGlitch += FORCED_UNDERRUN_PERIOD_FRAMES;
81 }
82 }
83
84 if (timestampData->timestampCount < MAX_TIMESTAMPS) {
85 TimestampInfo *timestamp = &timestampData->timestamps[timestampData->timestampCount];
86 timestamp->result = AAudioStream_getTimestamp(stream,
87 CLOCK_MONOTONIC,
88 &timestamp->timestampPosition,
89 &timestamp->timestampNanos);
90 timestamp->framesTotal = timestampData->framesTotal;
91 timestamp->appPosition = (direction == AAUDIO_DIRECTION_OUTPUT)
92 ? AAudioStream_getFramesWritten(stream)
93 : AAudioStream_getFramesRead(stream);
94 timestamp->appNanoseconds = getNanoseconds();
95 timestampData->timestampCount++;
96 }
97
98 if (direction == AAUDIO_DIRECTION_OUTPUT) {
99 timestampData->framesTotal += numFrames;
100 }
101 return AAUDIO_CALLBACK_RESULT_CONTINUE;
102}
103
104static TimestampCallbackData_t sTimestampData;
105
106static aaudio_result_t testTimeStamps(aaudio_policy_t mmapPolicy,
107 aaudio_sharing_mode_t sharingMode,
108 aaudio_performance_mode_t performanceMode,
109 aaudio_direction_t direction) {
Phil Burk97350f92017-07-21 15:59:44 -0700110 aaudio_result_t result = AAUDIO_OK;
111
Phil Burk97350f92017-07-21 15:59:44 -0700112 int32_t framesPerBurst = 0;
Phil Burk97350f92017-07-21 15:59:44 -0700113 int32_t actualChannelCount = 0;
114 int32_t actualSampleRate = 0;
115 int32_t originalBufferSize = 0;
116 int32_t requestedBufferSize = 0;
117 int32_t finalBufferSize = 0;
118 aaudio_format_t actualDataFormat = AAUDIO_FORMAT_PCM_FLOAT;
119 aaudio_sharing_mode_t actualSharingMode = AAUDIO_SHARING_MODE_SHARED;
Phil Burkcda5c072017-08-31 17:23:18 -0700120 aaudio_sharing_mode_t actualPerformanceMode = AAUDIO_PERFORMANCE_MODE_NONE;
Phil Burk97350f92017-07-21 15:59:44 -0700121
122 AAudioStreamBuilder *aaudioBuilder = nullptr;
123 AAudioStream *aaudioStream = nullptr;
124
Phil Burkcda5c072017-08-31 17:23:18 -0700125 memset(&sTimestampData, 0, sizeof(sTimestampData));
Phil Burk97350f92017-07-21 15:59:44 -0700126
Phil Burkcda5c072017-08-31 17:23:18 -0700127 printf("------------ testTimeStamps(policy = %d, sharing = %s, perf = %s, dir = %s) -----------\n",
128 mmapPolicy,
129 getSharingModeText(sharingMode),
130 getPerformanceModeText(performanceMode),
131 getDirectionText(direction));
Phil Burk97350f92017-07-21 15:59:44 -0700132
Phil Burkcda5c072017-08-31 17:23:18 -0700133 AAudio_setMMapPolicy(mmapPolicy);
Phil Burk97350f92017-07-21 15:59:44 -0700134
135 // Use an AAudioStreamBuilder to contain requested parameters.
136 result = AAudio_createStreamBuilder(&aaudioBuilder);
137 if (result != AAUDIO_OK) {
138 printf("AAudio_createStreamBuilder returned %s",
139 AAudio_convertResultToText(result));
140 goto finish;
141 }
142
143 // Request stream properties.
Phil Burkcda5c072017-08-31 17:23:18 -0700144 AAudioStreamBuilder_setFormat(aaudioBuilder, AAUDIO_FORMAT_PCM_I16);
145 AAudioStreamBuilder_setSharingMode(aaudioBuilder, sharingMode);
146 AAudioStreamBuilder_setPerformanceMode(aaudioBuilder, performanceMode);
147 AAudioStreamBuilder_setDirection(aaudioBuilder, direction);
148 AAudioStreamBuilder_setDataCallback(aaudioBuilder, timestampDataCallbackProc, &sTimestampData);
Phil Burk97350f92017-07-21 15:59:44 -0700149
150 // Create an AAudioStream using the Builder.
151 result = AAudioStreamBuilder_openStream(aaudioBuilder, &aaudioStream);
152 if (result != AAUDIO_OK) {
153 printf("AAudioStreamBuilder_openStream returned %s",
154 AAudio_convertResultToText(result));
155 goto finish;
156 }
157
158 // Check to see what kind of stream we actually got.
159 actualSampleRate = AAudioStream_getSampleRate(aaudioStream);
160 actualChannelCount = AAudioStream_getChannelCount(aaudioStream);
161 actualDataFormat = AAudioStream_getFormat(aaudioStream);
162
Phil Burkcda5c072017-08-31 17:23:18 -0700163 actualSharingMode = AAudioStream_getSharingMode(aaudioStream);
164 if (actualSharingMode != sharingMode) {
165 printf("did not get expected sharingMode, got %3d, skipping test\n",
166 actualSharingMode);
167 result = AAUDIO_OK;
168 goto finish;
169 }
170 actualPerformanceMode = AAudioStream_getPerformanceMode(aaudioStream);
171 if (actualPerformanceMode != performanceMode) {
172 printf("did not get expected performanceMode, got %3d, skipping test\n",
173 actualPerformanceMode);
174 result = AAUDIO_OK;
175 goto finish;
176 }
177
178 printf(" chans = %3d, rate = %6d format = %d\n",
179 actualChannelCount, actualSampleRate, actualDataFormat);
Phil Burk97350f92017-07-21 15:59:44 -0700180 printf(" Is MMAP used? %s\n", AAudioStream_isMMapUsed(aaudioStream)
Phil Burkcda5c072017-08-31 17:23:18 -0700181 ? "yes" : "no");
Phil Burk97350f92017-07-21 15:59:44 -0700182
183 // This is the number of frames that are read in one chunk by a DMA controller
184 // or a DSP or a mixer.
185 framesPerBurst = AAudioStream_getFramesPerBurst(aaudioStream);
186 printf(" framesPerBurst = %3d\n", framesPerBurst);
187
188 originalBufferSize = AAudioStream_getBufferSizeInFrames(aaudioStream);
Phil Burkcda5c072017-08-31 17:23:18 -0700189 requestedBufferSize = 4 * framesPerBurst;
Phil Burk97350f92017-07-21 15:59:44 -0700190 finalBufferSize = AAudioStream_setBufferSizeInFrames(aaudioStream, requestedBufferSize);
191
192 printf(" BufferSize: original = %4d, requested = %4d, final = %4d\n",
193 originalBufferSize, requestedBufferSize, finalBufferSize);
194
Phil Burkcda5c072017-08-31 17:23:18 -0700195 {
196 int64_t position;
197 int64_t nanoseconds;
198 result = AAudioStream_getTimestamp(aaudioStream, CLOCK_MONOTONIC, &position, &nanoseconds);
199 printf("before start, AAudioStream_getTimestamp() returns %s\n",
Phil Burk97350f92017-07-21 15:59:44 -0700200 AAudio_convertResultToText(result));
Phil Burk97350f92017-07-21 15:59:44 -0700201 }
202
Phil Burkcda5c072017-08-31 17:23:18 -0700203 for (int runs = 0; runs < NUM_LOOPS; runs++) {
204 printf("------------------ loop #%d\n", runs);
205
206 int64_t temp = sTimestampData.framesTotal;
207 memset(&sTimestampData, 0, sizeof(sTimestampData));
208 sTimestampData.framesTotal = temp;
209
210 sTimestampData.forceUnderruns = false;
211
212 result = AAudioStream_requestStart(aaudioStream);
213 if (result != AAUDIO_OK) {
214 printf("AAudioStream_requestStart returned %s",
215 AAudio_convertResultToText(result));
216 goto finish;
217 }
218
219 for (int second = 0; second < NUM_SECONDS; second++) {
220 // Give AAudio callback time to run in the background.
221 sleep(1);
222
223 // Periodically print the progress so we know it hasn't died.
224 printf("framesWritten = %d, XRuns = %d\n",
225 (int) AAudioStream_getFramesWritten(aaudioStream),
226 (int) AAudioStream_getXRunCount(aaudioStream)
227 );
228 }
229
230 result = AAudioStream_requestStop(aaudioStream);
231 if (result != AAUDIO_OK) {
232 printf("AAudioStream_requestStop returned %s\n",
233 AAudio_convertResultToText(result));
234 }
235
236 printf("timestampCount = %d\n", sTimestampData.timestampCount);
237 int printed = 0;
238 for (int i = 0; i < sTimestampData.timestampCount; i++) {
239 TimestampInfo *timestamp = &sTimestampData.timestamps[i];
240 bool posChanged = (timestamp->timestampPosition != (timestamp - 1)->timestampPosition);
241 bool timeChanged = (timestamp->timestampNanos != (timestamp - 1)->timestampNanos);
242 if ((printed < 20) && ((i < 10) || posChanged || timeChanged)) {
243 printf(" %3d : frames %8lld, xferd %8lld", i,
244 (long long) timestamp->framesTotal,
245 (long long) timestamp->appPosition);
246 if (timestamp->result != AAUDIO_OK) {
247 printf(", result = %s\n", AAudio_convertResultToText(timestamp->result));
248 } else {
249 bool negative = timestamp->timestampPosition < 0;
250 bool retro = (i > 0 && (timestamp->timestampPosition <
251 (timestamp - 1)->timestampPosition));
252 const char *message = negative ? " <=NEGATIVE!"
253 : (retro ? " <= RETROGRADE!" : "");
254
255 double latency = calculateLatencyMillis(timestamp->timestampPosition,
256 timestamp->timestampNanos,
257 timestamp->appPosition,
258 timestamp->appNanoseconds,
259 actualSampleRate);
260 printf(", STAMP: pos = %8lld, nanos = %8lld, lat = %7.1f msec %s\n",
261 (long long) timestamp->timestampPosition,
262 (long long) timestamp->timestampNanos,
263 latency,
264 message);
265 }
266 printed++;
Phil Burk97350f92017-07-21 15:59:44 -0700267 }
268 }
269
Phil Burkcda5c072017-08-31 17:23:18 -0700270 // Avoid race conditions in AudioFlinger.
271 // There is normally a delay between a real user stopping and restarting a stream.
272 sleep(1);
Phil Burk97350f92017-07-21 15:59:44 -0700273 }
274
Phil Burk97350f92017-07-21 15:59:44 -0700275finish:
276 if (aaudioStream != nullptr) {
277 AAudioStream_close(aaudioStream);
278 }
279 AAudioStreamBuilder_delete(aaudioBuilder);
Phil Burk97350f92017-07-21 15:59:44 -0700280 printf("result = %d = %s\n", result, AAudio_convertResultToText(result));
Phil Burkcda5c072017-08-31 17:23:18 -0700281
282 return result;
283}
284
285int main(int argc, char **argv) {
286 (void) argc;
Chih-Hung Hsieh68326fe2017-10-31 13:49:24 -0700287 (void) argv;
Phil Burkcda5c072017-08-31 17:23:18 -0700288
289 aaudio_result_t result = AAUDIO_OK;
290
291 // Make printf print immediately so that debug info is not stuck
292 // in a buffer if we hang or crash.
293 setvbuf(stdout, nullptr, _IONBF, (size_t) 0);
294
Phil Burkc75d97f2017-09-08 15:48:36 -0700295 printf("Test Timestamps V0.1.3\n");
296
Phil Burkcda5c072017-08-31 17:23:18 -0700297 // Legacy
Phil Burkc75d97f2017-09-08 15:48:36 -0700298 aaudio_policy_t policy = AAUDIO_POLICY_NEVER;
299 result = testTimeStamps(policy,
300 AAUDIO_SHARING_MODE_SHARED,
301 AAUDIO_PERFORMANCE_MODE_NONE,
302 AAUDIO_DIRECTION_INPUT);
303 result = testTimeStamps(policy,
304 AAUDIO_SHARING_MODE_SHARED,
305 AAUDIO_PERFORMANCE_MODE_LOW_LATENCY,
306 AAUDIO_DIRECTION_INPUT);
307 result = testTimeStamps(policy,
308 AAUDIO_SHARING_MODE_SHARED,
309 AAUDIO_PERFORMANCE_MODE_NONE,
310 AAUDIO_DIRECTION_OUTPUT);
311 result = testTimeStamps(policy,
312 AAUDIO_SHARING_MODE_SHARED,
Phil Burkcda5c072017-08-31 17:23:18 -0700313 AAUDIO_PERFORMANCE_MODE_LOW_LATENCY,
314 AAUDIO_DIRECTION_OUTPUT);
Phil Burkc75d97f2017-09-08 15:48:36 -0700315
316 // MMAP
317 policy = AAUDIO_POLICY_ALWAYS;
318 result = testTimeStamps(policy,
319 AAUDIO_SHARING_MODE_EXCLUSIVE,
320 AAUDIO_PERFORMANCE_MODE_LOW_LATENCY,
321 AAUDIO_DIRECTION_INPUT);
322 result = testTimeStamps(policy,
323 AAUDIO_SHARING_MODE_EXCLUSIVE,
324 AAUDIO_PERFORMANCE_MODE_LOW_LATENCY,
325 AAUDIO_DIRECTION_OUTPUT);
326 result = testTimeStamps(policy,
327 AAUDIO_SHARING_MODE_SHARED,
328 AAUDIO_PERFORMANCE_MODE_LOW_LATENCY,
329 AAUDIO_DIRECTION_INPUT);
330 result = testTimeStamps(policy,
331 AAUDIO_SHARING_MODE_SHARED,
332 AAUDIO_PERFORMANCE_MODE_LOW_LATENCY,
333 AAUDIO_DIRECTION_OUTPUT);
Phil Burkcda5c072017-08-31 17:23:18 -0700334
335 return (result == AAUDIO_OK) ? EXIT_SUCCESS : EXIT_FAILURE;
Phil Burk97350f92017-07-21 15:59:44 -0700336}