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