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 */
39static 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");
198error:
199    player.close();
200    return disconnected ? AAUDIO_ERROR_DISCONNECTED : result;
201}
202
203static 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
210int 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