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