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