• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
39 typedef 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 
48 typedef 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.
timestampDataCallbackProc(AAudioStream * stream,void * userData,void * audioData __unused,int32_t numFrames)57 aaudio_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 
104 static TimestampCallbackData_t sTimestampData;
105 
testTimeStamps(aaudio_policy_t mmapPolicy,aaudio_sharing_mode_t sharingMode,aaudio_performance_mode_t performanceMode,aaudio_direction_t direction)106 static 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) {
110     aaudio_result_t result = AAUDIO_OK;
111 
112     int32_t framesPerBurst = 0;
113     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;
120     aaudio_sharing_mode_t actualPerformanceMode = AAUDIO_PERFORMANCE_MODE_NONE;
121 
122     AAudioStreamBuilder *aaudioBuilder = nullptr;
123     AAudioStream *aaudioStream = nullptr;
124 
125     memset(&sTimestampData, 0, sizeof(sTimestampData));
126 
127     printf("------------ testTimeStamps(policy = %d, sharing = %s, perf = %s, dir = %s) -----------\n",
128            mmapPolicy,
129            getSharingModeText(sharingMode),
130            getPerformanceModeText(performanceMode),
131            getDirectionText(direction));
132 
133     AAudio_setMMapPolicy(mmapPolicy);
134 
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.
144     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);
149 
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 
163     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);
180     printf("    Is MMAP used? %s\n", AAudioStream_isMMapUsed(aaudioStream)
181                                      ? "yes" : "no");
182 
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);
189     requestedBufferSize = 4 * framesPerBurst;
190     finalBufferSize = AAudioStream_setBufferSizeInFrames(aaudioStream, requestedBufferSize);
191 
192     printf("    BufferSize: original = %4d, requested = %4d, final = %4d\n",
193            originalBufferSize, requestedBufferSize, finalBufferSize);
194 
195     {
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",
200                AAudio_convertResultToText(result));
201     }
202 
203     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++;
267             }
268         }
269 
270         // Avoid race conditions in AudioFlinger.
271         // There is normally a delay between a real user stopping and restarting a stream.
272         sleep(1);
273     }
274 
275 finish:
276     if (aaudioStream != nullptr) {
277         AAudioStream_close(aaudioStream);
278     }
279     AAudioStreamBuilder_delete(aaudioBuilder);
280     printf("result = %d = %s\n", result, AAudio_convertResultToText(result));
281 
282     return result;
283 }
284 
main(int argc,char ** argv)285 int main(int argc, char **argv) {
286     (void) argc;
287     (void) argv;
288 
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 
295     printf("Test Timestamps V0.1.3\n");
296 
297     // Legacy
298     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,
313                             AAUDIO_PERFORMANCE_MODE_LOW_LATENCY,
314                             AAUDIO_DIRECTION_OUTPUT);
315 
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);
334 
335     return (result == AAUDIO_OK) ? EXIT_SUCCESS : EXIT_FAILURE;
336 }
337