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 sine waves using an AAudio callback.
18 // If a disconnection occurs then reopen the stream on the new device.
19
20 #include <assert.h>
21 #include <unistd.h>
22 #include <stdlib.h>
23 #include <sched.h>
24 #include <stdio.h>
25 #include <math.h>
26 #include <string.h>
27 #include <time.h>
28 #include <aaudio/AAudio.h>
29
30 #include "AAudioExampleUtils.h"
31 #include "AAudioSimplePlayer.h"
32 #include "AAudioArgsParser.h"
33
34 #define APP_VERSION "0.2.1"
35
36 static constexpr int32_t kDefaultHangTimeMSec = 10;
37 static constexpr int32_t kWorkPeriodSeconds = 6;
38 /**
39 * Open stream, play some sine waves, then close the stream.
40 *
41 * @param argParser
42 * @return AAUDIO_OK or negative error code
43 */
testOpenPlayClose(AAudioArgsParser & argParser,int32_t loopCount,int32_t prefixToneMsec,int32_t hangTimeMSec,int cpuAffinity,double lowWorkLoad,double highWorkLoad,int32_t workPeriodSeconds)44 static aaudio_result_t testOpenPlayClose(AAudioArgsParser &argParser,
45 int32_t loopCount,
46 int32_t prefixToneMsec,
47 int32_t hangTimeMSec,
48 int cpuAffinity,
49 double lowWorkLoad,
50 double highWorkLoad,
51 int32_t workPeriodSeconds)
52 {
53 SineThreadedData_t myData;
54 AAudioSimplePlayer &player = myData.simplePlayer;
55 aaudio_result_t result = AAUDIO_OK;
56 bool disconnected = false;
57 bool bailOut = false;
58 int64_t startedAtNanos;
59
60 printf("----------------------- run complete test --------------------------\n");
61 myData.schedulerChecked = false;
62 myData.callbackCount = 0;
63 myData.hangTimeMSec = hangTimeMSec; // test AAudioStream_getXRunCount()
64 myData.cpuAffinity = cpuAffinity;
65
66 result = player.open(argParser,
67 SimplePlayerDataCallbackProc,
68 SimplePlayerErrorCallbackProc,
69 &myData);
70 if (result != AAUDIO_OK) {
71 fprintf(stderr, "ERROR - player.open() returned %s\n",
72 AAudio_convertResultToText(result));
73 goto error;
74 }
75
76 argParser.compareWithStream(player.getStream());
77
78 myData.sampleRate = player.getSampleRate();
79 myData.prefixToneFrames = prefixToneMsec * myData.sampleRate / 1000;
80 if (myData.prefixToneFrames > 0) {
81 myData.setupSineBlip();
82 } else {
83 myData.setupSineSweeps();
84 }
85
86 #if 0
87 // writes not allowed for callback streams
88 result = player.prime(); // FIXME crashes AudioTrack.cpp
89 if (result != AAUDIO_OK) {
90 fprintf(stderr, "ERROR - player.prime() returned %d\n", result);
91 goto error;
92 }
93 #endif
94
95 for (int loopIndex = 0; loopIndex < loopCount; loopIndex++) {
96 // Only play data on every other loop so we can hear if there is stale data.
97 double amplitude;
98 int32_t durationSeconds;
99 if ((loopIndex & 1) == 0) {
100 printf("--------------- SINE ------\n");
101 amplitude = 0.2;
102 durationSeconds = argParser.getDurationSeconds();
103 } else {
104 printf("--------------- QUIET -----\n");
105 amplitude = 0.0;
106 durationSeconds = 2; // just wait briefly when quiet
107 }
108 for (int i = 0; i < MAX_CHANNELS; ++i) {
109 myData.sineOscillators[i].setAmplitude(amplitude);
110 }
111
112 result = player.start();
113 if (result != AAUDIO_OK) {
114 fprintf(stderr, "ERROR - player.start() returned %d\n", result);
115 goto error;
116 }
117
118 // Play a sine wave in the background.
119 printf("Monitor for %d seconds while audio plays in a callback thread. %d of %d, %d\n",
120 argParser.getDurationSeconds(), (loopIndex + 1), loopCount, workPeriodSeconds);
121 startedAtNanos = getNanoseconds(CLOCK_MONOTONIC);
122 for (int second = 0; second < durationSeconds; second++) {
123 // Sleep a while. Wake up early if there is an error, for example a DISCONNECT.
124 myData.waker.wait(AAUDIO_OK, NANOS_PER_SECOND);
125 int64_t millis =
126 (getNanoseconds(CLOCK_MONOTONIC) - startedAtNanos) / NANOS_PER_MILLISECOND;
127 result = myData.waker.get();
128 const int32_t framesWritten = (int32_t) AAudioStream_getFramesWritten(player.getStream());
129 const int32_t framesRead = (int32_t) AAudioStream_getFramesRead(player.getStream());
130 const int32_t xruns = AAudioStream_getXRunCount(player.getStream());
131 myData.workload = ((second % (2 * workPeriodSeconds)) < workPeriodSeconds)
132 ? lowWorkLoad : highWorkLoad;
133 printf(" waker result = %d, at %6d millis"
134 ", second = %3d, frames written %8d - read %8d = %8d"
135 ", work = %5.1f, underruns = %d\n",
136 result, (int) millis,
137 second,
138 framesWritten,
139 framesRead,
140 framesWritten - framesRead,
141 myData.workload,
142 xruns);
143 if (result != AAUDIO_OK) {
144 disconnected = (result == AAUDIO_ERROR_DISCONNECTED);
145 bailOut = true;
146 break;
147 }
148 }
149 printf("AAudio result = %d = %s\n", result, AAudio_convertResultToText(result));
150
151 // Alternate between using stop or pause for each sine/quiet pair.
152 // Repeat this pattern: {sine-stop-quiet-stop-sine-pause-quiet-pause}
153 if ((loopIndex & 2) == 0) {
154 printf("STOP, callback # = %d\n", myData.callbackCount);
155 result = player.stop();
156 } else {
157 printf("PAUSE/FLUSH, callback # = %d\n", myData.callbackCount);
158 result = player.pause();
159 if (result != AAUDIO_OK) {
160 goto error;
161 }
162 result = player.waitUntilPaused();
163 if (result != AAUDIO_OK) {
164 goto error;
165 }
166 result = player.flush();
167 }
168 if (result != AAUDIO_OK) {
169 goto error;
170 }
171
172 if (bailOut) {
173 break;
174 }
175
176 {
177 aaudio_stream_state_t state = AAudioStream_getState(player.getStream());
178 aaudio_stream_state_t finalState = AAUDIO_STREAM_STATE_UNINITIALIZED;
179 int64_t timeoutNanos = 2000 * NANOS_PER_MILLISECOND;
180 result = AAudioStream_waitForStateChange(player.getStream(), state,
181 &finalState, timeoutNanos);
182 printf("waitForStateChange returns %s, state = %s\n",
183 AAudio_convertResultToText(result),
184 AAudio_convertStreamStateToText(finalState));
185 int64_t written = AAudioStream_getFramesWritten(player.getStream());
186 int64_t read = AAudioStream_getFramesRead(player.getStream());
187 printf(" framesWritten = %lld, framesRead = %lld, diff = %d\n",
188 (long long) written,
189 (long long) read,
190 (int) (written - read));
191 }
192
193 }
194
195 printf("call close()\n");
196 result = player.close();
197 if (result != AAUDIO_OK) {
198 goto error;
199 }
200
201 for (int i = 0; i < myData.timestampCount; i++) {
202 Timestamp *timestamp = &myData.timestamps[i];
203 bool retro = (i > 0 &&
204 ((timestamp->position < (timestamp - 1)->position)
205 || ((timestamp->nanoseconds < (timestamp - 1)->nanoseconds))));
206 const char *message = retro ? " <= RETROGRADE!" : "";
207 printf("Timestamp %3d : %8lld, %8lld %s\n", i,
208 (long long) timestamp->position,
209 (long long) timestamp->nanoseconds,
210 message);
211 }
212
213 if (myData.schedulerChecked) {
214 printf("scheduler = 0x%08x, SCHED_FIFO = 0x%08X\n",
215 myData.scheduler,
216 SCHED_FIFO);
217 }
218
219 printf("min numFrames = %8d\n", (int) myData.minNumFrames);
220 printf("max numFrames = %8d\n", (int) myData.maxNumFrames);
221
222 printf("SUCCESS\n");
223 error:
224 player.close();
225 return disconnected ? AAUDIO_ERROR_DISCONNECTED : result;
226 }
227
usage()228 static void usage() {
229 AAudioArgsParser::usage();
230 printf(" -l{count} loopCount start/stop, every other one is silent\n");
231 printf(" -t{msec} play a high pitched tone at the beginning\n");
232 printf(" -w{workload} set base workload, default 0.0\n");
233 printf(" -W{workload} alternate between this higher workload and base workload\n");
234 printf(" -Z{duration} number of seconds to spend at each workload, default = %d\n",
235 kWorkPeriodSeconds);
236 printf(" -a{cpu} set CPU affinity, default none\n");
237 printf(" -h{msec} force periodic underruns by hanging in callback\n");
238 printf(" If no value specified then %d used.\n",
239 kDefaultHangTimeMSec);
240 }
241
main(int argc,const char ** argv)242 int main(int argc, const char **argv)
243 {
244 AAudioArgsParser argParser;
245 aaudio_result_t result;
246 int32_t loopCount = 1;
247 int32_t prefixToneMsec = 0;
248 int32_t hangTimeMSec = 0;
249 int cpuAffinity = -1;
250 double lowWorkLoad = 0.0;
251 double highWorkLoad = -1.0;
252 int32_t workPeriodSeconds = kWorkPeriodSeconds;
253
254 // Make printf print immediately so that debug info is not stuck
255 // in a buffer if we hang or crash.
256 setvbuf(stdout, nullptr, _IONBF, (size_t) 0);
257
258 printf("%s - Play a sine sweep using an AAudio callback V%s\n",
259 argv[0], APP_VERSION);
260
261 for (int i = 1; i < argc; i++) {
262 const char *arg = argv[i];
263 if (argParser.parseArg(arg)) {
264 // Handle options that are not handled by the ArgParser
265 if (arg[0] == '-') {
266 char option = arg[1];
267 switch (option) {
268 case 'a':
269 cpuAffinity = atoi(&arg[2]);
270 break;
271 case 'l':
272 loopCount = atoi(&arg[2]);
273 break;
274 case 't':
275 prefixToneMsec = atoi(&arg[2]);
276 break;
277 case 'h':
278 hangTimeMSec = (arg[2]) // value specified?
279 ? atoi(&arg[2])
280 : kDefaultHangTimeMSec;
281 break;
282 case 'w':
283 lowWorkLoad = atof(&arg[2]);
284 break;
285 case 'W':
286 highWorkLoad = atof(&arg[2]);
287 break;
288 case 'Z':
289 workPeriodSeconds = atoi(&arg[2]);
290 break;
291 default:
292 usage();
293 exit(EXIT_FAILURE);
294 break;
295 }
296 } else {
297 usage();
298 exit(EXIT_FAILURE);
299 break;
300 }
301 }
302 }
303
304 if (highWorkLoad > 0) {
305 if (highWorkLoad < lowWorkLoad) {
306 printf("ERROR - -W%f workload lower than -w%f workload", highWorkLoad, lowWorkLoad);
307 return EXIT_FAILURE;
308 }
309 } else {
310 highWorkLoad = lowWorkLoad; // high not specified so use low
311 }
312
313 // Keep looping until we can complete the test without disconnecting.
314 while((result = testOpenPlayClose(argParser, loopCount,
315 prefixToneMsec, hangTimeMSec,
316 cpuAffinity,
317 lowWorkLoad, highWorkLoad,
318 workPeriodSeconds))
319 == AAUDIO_ERROR_DISCONNECTED);
320
321 return (result) ? EXIT_FAILURE : EXIT_SUCCESS;
322 }
323