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