1 /*
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>
20 #include <stdlib.h>
21 #include <unistd.h>
22 
23 #include <aaudio/AAudio.h>
24 #include <aaudio/AAudioTesting.h>
25 #include "AAudioExampleUtils.h"
26 
27 // 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)
31 
32 #define MAX_TIMESTAMPS          1000
33 
34 #define DEFAULT_TIMEOUT_NANOS   ((int64_t)1000000000)
35 
36 #define NUM_SECONDS             1
37 #define NUM_LOOPS               4
38 #define MAX_TESTS               20
39 
40 typedef 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 
49 typedef 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 struct TimeStampTestLog {
58     aaudio_policy_t           isMmap;
59     aaudio_sharing_mode_t     sharingMode;
60     aaudio_performance_mode_t performanceMode;
61     aaudio_direction_t        direction;
62     aaudio_result_t           result;
63 };
64 
65 static int s_numTests = 0;
66 // Use a plain old array because we reference this from the callback and do not want any
67 // automatic memory allocation.
68 static TimeStampTestLog s_testLogs[MAX_TESTS]{};
69 
logTestResult(bool isMmap,aaudio_sharing_mode_t sharingMode,aaudio_performance_mode_t performanceMode,aaudio_direction_t direction,aaudio_result_t result)70 static void logTestResult(bool isMmap,
71                           aaudio_sharing_mode_t sharingMode,
72                           aaudio_performance_mode_t performanceMode,
73                           aaudio_direction_t direction,
74                           aaudio_result_t result) {
75     if(s_numTests >= MAX_TESTS) {
76         printf("ERROR - MAX_TESTS too small = %d\n", MAX_TESTS);
77         return;
78     }
79     s_testLogs[s_numTests].isMmap = isMmap;
80     s_testLogs[s_numTests].sharingMode = sharingMode;
81     s_testLogs[s_numTests].performanceMode = performanceMode;
82     s_testLogs[s_numTests].direction = direction;
83     s_testLogs[s_numTests].result = result;
84     s_numTests++;
85 }
86 
printTestResults()87 static void printTestResults() {
88     for (int i = 0; i < s_numTests; i++) {
89         TimeStampTestLog *log = &s_testLogs[i];
90         printf("%2d: mmap = %3s, sharing = %9s, perf = %11s, dir = %6s ---- %4s\n",
91                i,
92                log->isMmap ? "yes" : "no",
93                getSharingModeText(log->sharingMode),
94                getPerformanceModeText(log->performanceMode),
95                getDirectionText(log->direction),
96                log->result ? "FAIL" : "pass");
97     }
98 }
99 
100 // Callback function that fills the audio output buffer.
timestampDataCallbackProc(AAudioStream * stream,void * userData,void * audioData __unused,int32_t numFrames)101 aaudio_data_callback_result_t timestampDataCallbackProc(
102         AAudioStream *stream,
103         void *userData,
104         void *audioData __unused,
105         int32_t numFrames
106 ) {
107 
108     // should not happen but just in case...
109     if (userData == nullptr) {
110         printf("ERROR - SimplePlayerDataCallbackProc needs userData\n");
111         return AAUDIO_CALLBACK_RESULT_STOP;
112     }
113     TimestampCallbackData_t *timestampData = (TimestampCallbackData_t *) userData;
114 
115     aaudio_direction_t direction = AAudioStream_getDirection(stream);
116     if (direction == AAUDIO_DIRECTION_INPUT) {
117         timestampData->framesTotal += numFrames;
118     }
119 
120     if (timestampData->forceUnderruns) {
121         if (timestampData->framesTotal > timestampData->nextFrameToGlitch) {
122             usleep(FORCED_UNDERRUN_SLEEP_MICROS);
123             printf("Simulate glitch at %lld\n", (long long) timestampData->framesTotal);
124             timestampData->nextFrameToGlitch += FORCED_UNDERRUN_PERIOD_FRAMES;
125         }
126     }
127 
128     if (timestampData->timestampCount < MAX_TIMESTAMPS) {
129         TimestampInfo *timestamp = &timestampData->timestamps[timestampData->timestampCount];
130         timestamp->result = AAudioStream_getTimestamp(stream,
131                                                       CLOCK_MONOTONIC,
132                                                       &timestamp->timestampPosition,
133                                                       &timestamp->timestampNanos);
134         timestamp->framesTotal = timestampData->framesTotal;
135         timestamp->appPosition = (direction == AAUDIO_DIRECTION_OUTPUT)
136                 ? AAudioStream_getFramesWritten(stream)
137                 : AAudioStream_getFramesRead(stream);
138         timestamp->appNanoseconds = getNanoseconds();
139         timestampData->timestampCount++;
140     }
141 
142     if (direction == AAUDIO_DIRECTION_OUTPUT) {
143         timestampData->framesTotal += numFrames;
144     }
145     return AAUDIO_CALLBACK_RESULT_CONTINUE;
146 }
147 
148 static TimestampCallbackData_t sTimestampData;
149 
testTimeStamps(aaudio_policy_t mmapPolicy,aaudio_sharing_mode_t sharingMode,aaudio_performance_mode_t performanceMode,aaudio_direction_t direction)150 static aaudio_result_t testTimeStamps(aaudio_policy_t mmapPolicy,
151                            aaudio_sharing_mode_t sharingMode,
152                            aaudio_performance_mode_t performanceMode,
153                            aaudio_direction_t direction) {
154     aaudio_result_t result = AAUDIO_OK;
155 
156     int32_t framesPerBurst = 0;
157     int32_t actualChannelCount = 0;
158     int32_t actualSampleRate = 0;
159     int32_t originalBufferSize = 0;
160     int32_t requestedBufferSize = 0;
161     int32_t finalBufferSize = 0;
162     bool    isMmap = false;
163     aaudio_format_t actualDataFormat = AAUDIO_FORMAT_PCM_FLOAT;
164     aaudio_sharing_mode_t actualSharingMode = AAUDIO_SHARING_MODE_SHARED;
165     aaudio_sharing_mode_t actualPerformanceMode = AAUDIO_PERFORMANCE_MODE_NONE;
166 
167     AAudioStreamBuilder *aaudioBuilder = nullptr;
168     AAudioStream *aaudioStream = nullptr;
169 
170     memset(&sTimestampData, 0, sizeof(sTimestampData));
171 
172     printf("\n=================================================================================\n");
173     printf("--------- testTimeStamps(policy = %d, sharing = %s, perf = %s, dir = %s) --------\n",
174            mmapPolicy,
175            getSharingModeText(sharingMode),
176            getPerformanceModeText(performanceMode),
177            getDirectionText(direction));
178 
179     AAudio_setMMapPolicy(mmapPolicy);
180 
181     // Use an AAudioStreamBuilder to contain requested parameters.
182     result = AAudio_createStreamBuilder(&aaudioBuilder);
183     if (result != AAUDIO_OK) {
184         printf("AAudio_createStreamBuilder returned %s",
185                AAudio_convertResultToText(result));
186         goto finish;
187     }
188 
189     // Request stream properties.
190     AAudioStreamBuilder_setFormat(aaudioBuilder, AAUDIO_FORMAT_PCM_I16);
191     AAudioStreamBuilder_setSharingMode(aaudioBuilder, sharingMode);
192     AAudioStreamBuilder_setPerformanceMode(aaudioBuilder, performanceMode);
193     AAudioStreamBuilder_setDirection(aaudioBuilder, direction);
194     AAudioStreamBuilder_setDataCallback(aaudioBuilder, timestampDataCallbackProc, &sTimestampData);
195 
196     // Create an AAudioStream using the Builder.
197     result = AAudioStreamBuilder_openStream(aaudioBuilder, &aaudioStream);
198     if (result != AAUDIO_OK) {
199         printf("AAudioStreamBuilder_openStream returned %s",
200                AAudio_convertResultToText(result));
201         goto finish;
202     }
203 
204     // Check to see what kind of stream we actually got.
205     actualSampleRate = AAudioStream_getSampleRate(aaudioStream);
206     actualChannelCount = AAudioStream_getChannelCount(aaudioStream);
207     actualDataFormat = AAudioStream_getFormat(aaudioStream);
208 
209     actualSharingMode = AAudioStream_getSharingMode(aaudioStream);
210     if (actualSharingMode != sharingMode) {
211         printf("did not get expected sharingMode, got %3d, skipping test\n",
212                actualSharingMode);
213         result = AAUDIO_OK;
214         goto finish;
215     }
216     actualPerformanceMode = AAudioStream_getPerformanceMode(aaudioStream);
217     if (actualPerformanceMode != performanceMode) {
218         printf("did not get expected performanceMode, got %3d, skipping test\n",
219                actualPerformanceMode);
220         result = AAUDIO_OK;
221         goto finish;
222     }
223 
224     printf("    chans = %3d, rate = %6d format = %d\n",
225            actualChannelCount, actualSampleRate, actualDataFormat);
226     isMmap = AAudioStream_isMMapUsed(aaudioStream);
227     printf("    Is MMAP used? %s\n", isMmap ? "yes" : "no");
228 
229     // This is the number of frames that are read in one chunk by a DMA controller
230     // or a DSP or a mixer.
231     framesPerBurst = AAudioStream_getFramesPerBurst(aaudioStream);
232     printf("    framesPerBurst = %3d\n", framesPerBurst);
233 
234     originalBufferSize = AAudioStream_getBufferSizeInFrames(aaudioStream);
235     requestedBufferSize = 4 * framesPerBurst;
236     finalBufferSize = AAudioStream_setBufferSizeInFrames(aaudioStream, requestedBufferSize);
237 
238     printf("    BufferSize: original = %4d, requested = %4d, final = %4d\n",
239            originalBufferSize, requestedBufferSize, finalBufferSize);
240 
241     {
242         int64_t position;
243         int64_t nanoseconds;
244         result = AAudioStream_getTimestamp(aaudioStream, CLOCK_MONOTONIC, &position, &nanoseconds);
245         printf("before start, AAudioStream_getTimestamp() returns %s\n",
246                AAudio_convertResultToText(result));
247     }
248 
249     for (int runs = 0; runs < NUM_LOOPS; runs++) {
250         printf("------------------ loop #%d\n", runs);
251 
252         int64_t temp = sTimestampData.framesTotal;
253         memset(&sTimestampData, 0, sizeof(sTimestampData));
254         sTimestampData.framesTotal = temp;
255 
256         sTimestampData.forceUnderruns = false;
257 
258         result = AAudioStream_requestStart(aaudioStream);
259         if (result != AAUDIO_OK) {
260             printf("AAudioStream_requestStart returned %s",
261                    AAudio_convertResultToText(result));
262             goto finish;
263         }
264 
265         for (int second = 0; second < NUM_SECONDS; second++) {
266             // Give AAudio callback time to run in the background.
267             usleep(200 * 1000);
268 
269             // Periodically print the progress so we know it hasn't died.
270             printf("framesWritten = %d, XRuns = %d\n",
271                    (int) AAudioStream_getFramesWritten(aaudioStream),
272                    (int) AAudioStream_getXRunCount(aaudioStream)
273             );
274         }
275 
276         result = AAudioStream_requestStop(aaudioStream);
277         if (result != AAUDIO_OK) {
278             printf("AAudioStream_requestStop returned %s\n",
279                    AAudio_convertResultToText(result));
280         }
281 
282         printf("timestampCount = %d\n", sTimestampData.timestampCount);
283         int printedGood = 0;
284         int printedBad = 0;
285         for (int i = 1; i < sTimestampData.timestampCount; i++) {
286             TimestampInfo *timestamp = &sTimestampData.timestamps[i];
287             if (timestamp->result != AAUDIO_OK) {
288                 if (printedBad < 5) {
289                     printf("  %3d : frames %8lld, xferd %8lld, result = %s\n",
290                            i,
291                            (long long) timestamp->framesTotal,
292                            (long long) timestamp->appPosition,
293                            AAudio_convertResultToText(timestamp->result));
294                     printedBad++;
295                 }
296             } else {
297                 const bool posChanged = (timestamp->timestampPosition !=
298                                    (timestamp - 1)->timestampPosition);
299                 const bool timeChanged = (timestamp->timestampNanos
300                         != (timestamp - 1)->timestampNanos);
301                 if ((printedGood < 20) && (posChanged || timeChanged)) {
302                     bool negative = timestamp->timestampPosition < 0;
303                     bool retro = (i > 0 && (timestamp->timestampPosition <
304                                             (timestamp - 1)->timestampPosition));
305                     const char *message = negative ? " <=NEGATIVE!"
306                                                    : (retro ? "  <= RETROGRADE!" : "");
307 
308                     double latency = calculateLatencyMillis(timestamp->timestampPosition,
309                                                             timestamp->timestampNanos,
310                                                             timestamp->appPosition,
311                                                             timestamp->appNanoseconds,
312                                                             actualSampleRate);
313                     printf("  %3d : frames %8lld, xferd %8lld",
314                            i,
315                            (long long) timestamp->framesTotal,
316                            (long long) timestamp->appPosition);
317                     printf(" STAMP: pos = %8lld, nanos = %8lld, lat = %7.1f msec %s\n",
318                            (long long) timestamp->timestampPosition,
319                            (long long) timestamp->timestampNanos,
320                            latency,
321                            message);
322                     printedGood++;
323                 }
324             }
325         }
326 
327         if (printedGood == 0) {
328             printf("ERROR - AAudioStream_getTimestamp() never gave us a valid timestamp\n");
329             result = AAUDIO_ERROR_INTERNAL;
330         } else {
331             // Make sure we do not get timestamps when stopped.
332             int64_t position;
333             int64_t time;
334             aaudio_result_t tempResult = AAudioStream_getTimestamp(aaudioStream,
335                                                                    CLOCK_MONOTONIC,
336                                                                    &position, &time);
337             if (tempResult != AAUDIO_ERROR_INVALID_STATE) {
338                 printf("ERROR - AAudioStream_getTimestamp() should return"
339                        " INVALID_STATE when stopped! %s\n",
340                        AAudio_convertResultToText(tempResult));
341                 result = AAUDIO_ERROR_INTERNAL;
342             }
343         }
344 
345         // Avoid race conditions in AudioFlinger.
346         // There is normally a delay between a real user stopping and restarting a stream.
347         sleep(1);
348     }
349 
350 finish:
351 
352     logTestResult(isMmap, sharingMode, performanceMode, direction, result);
353 
354     if (aaudioStream != nullptr) {
355         AAudioStream_close(aaudioStream);
356     }
357     AAudioStreamBuilder_delete(aaudioBuilder);
358     printf("result = %d = %s\n", result, AAudio_convertResultToText(result));
359     return result;
360 }
361 
main(int argc,char ** argv)362 int main(int argc, char **argv) {
363     (void) argc;
364     (void) argv;
365 
366     aaudio_result_t result = AAUDIO_OK;
367 
368     // Make printf print immediately so that debug info is not stuck
369     // in a buffer if we hang or crash.
370     setvbuf(stdout, nullptr, _IONBF, (size_t) 0);
371 
372     printf("Test Timestamps V0.1.4\n");
373 
374     // Legacy
375     aaudio_policy_t policy = AAUDIO_POLICY_NEVER;
376     result = testTimeStamps(policy,
377                             AAUDIO_SHARING_MODE_SHARED,
378                             AAUDIO_PERFORMANCE_MODE_NONE,
379                             AAUDIO_DIRECTION_INPUT);
380     result = testTimeStamps(policy,
381                             AAUDIO_SHARING_MODE_SHARED,
382                             AAUDIO_PERFORMANCE_MODE_LOW_LATENCY,
383                             AAUDIO_DIRECTION_INPUT);
384     result = testTimeStamps(policy,
385                             AAUDIO_SHARING_MODE_SHARED,
386                             AAUDIO_PERFORMANCE_MODE_NONE,
387                             AAUDIO_DIRECTION_OUTPUT);
388     result = testTimeStamps(policy,
389                             AAUDIO_SHARING_MODE_SHARED,
390                             AAUDIO_PERFORMANCE_MODE_LOW_LATENCY,
391                             AAUDIO_DIRECTION_OUTPUT);
392 
393     // MMAP
394     policy = AAUDIO_POLICY_ALWAYS;
395     result = testTimeStamps(policy,
396                             AAUDIO_SHARING_MODE_EXCLUSIVE,
397                             AAUDIO_PERFORMANCE_MODE_LOW_LATENCY,
398                             AAUDIO_DIRECTION_INPUT);
399     result = testTimeStamps(policy,
400                             AAUDIO_SHARING_MODE_EXCLUSIVE,
401                             AAUDIO_PERFORMANCE_MODE_LOW_LATENCY,
402                             AAUDIO_DIRECTION_OUTPUT);
403     result = testTimeStamps(policy,
404                             AAUDIO_SHARING_MODE_SHARED,
405                             AAUDIO_PERFORMANCE_MODE_LOW_LATENCY,
406                             AAUDIO_DIRECTION_INPUT);
407     result = testTimeStamps(policy,
408                             AAUDIO_SHARING_MODE_SHARED,
409                             AAUDIO_PERFORMANCE_MODE_LOW_LATENCY,
410                             AAUDIO_DIRECTION_OUTPUT);
411 
412     printTestResults();
413 
414     return (result == AAUDIO_OK) ? EXIT_SUCCESS : EXIT_FAILURE;
415 }
416