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 /** 18 * Handle a DISCONNECT by only opening and starting a new stream 19 * without stopping and closing the old one. 20 * This caused the new stream to use the old disconnected device. 21 */ 22 23 #include <stdio.h> 24 #include <thread> 25 #include <unistd.h> 26 27 #include <aaudio/AAudio.h> 28 29 #define DEFAULT_TIMEOUT_NANOS ((int64_t)1000000000) 30 31 static void s_myErrorCallbackProc( 32 AAudioStream *stream, 33 void *userData, 34 aaudio_result_t error); 35 36 struct AudioEngine { 37 AAudioStreamBuilder *builder = nullptr; 38 AAudioStream *stream = nullptr; 39 std::thread *thread = nullptr; 40 int64_t framesRead = 0; 41 }; 42 43 AudioEngine s_AudioEngine; 44 45 // Callback function that fills the audio output buffer. 46 static aaudio_data_callback_result_t s_myDataCallbackProc( 47 AAudioStream *stream, 48 void *userData, 49 void *audioData, 50 int32_t numFrames 51 ) { 52 (void) userData; 53 (void) audioData; 54 (void) numFrames; 55 s_AudioEngine.framesRead = AAudioStream_getFramesRead(stream); 56 return AAUDIO_CALLBACK_RESULT_CONTINUE; 57 } 58 59 static aaudio_result_t s_StartAudio() { 60 int32_t framesPerBurst = 0; 61 int32_t deviceId = 0; 62 63 // Use an AAudioStreamBuilder to contain requested parameters. 64 aaudio_result_t result = AAudio_createStreamBuilder(&s_AudioEngine.builder); 65 if (result != AAUDIO_OK) { 66 printf("AAudio_createStreamBuilder returned %s", 67 AAudio_convertResultToText(result)); 68 return result; 69 } 70 71 // Request stream properties. 72 AAudioStreamBuilder_setFormat(s_AudioEngine.builder, AAUDIO_FORMAT_PCM_FLOAT); 73 AAudioStreamBuilder_setPerformanceMode(s_AudioEngine.builder, AAUDIO_PERFORMANCE_MODE_LOW_LATENCY); 74 AAudioStreamBuilder_setDataCallback(s_AudioEngine.builder, s_myDataCallbackProc, nullptr); 75 AAudioStreamBuilder_setErrorCallback(s_AudioEngine.builder, s_myErrorCallbackProc, nullptr); 76 77 // Create an AAudioStream using the Builder. 78 result = AAudioStreamBuilder_openStream(s_AudioEngine.builder, &s_AudioEngine.stream); 79 if (result != AAUDIO_OK) { 80 printf("AAudioStreamBuilder_openStream returned %s", 81 AAudio_convertResultToText(result)); 82 return result; 83 } 84 85 result = AAudioStream_requestStart(s_AudioEngine.stream); 86 if (result != AAUDIO_OK) { 87 printf("AAudioStream_requestStart returned %s", 88 AAudio_convertResultToText(result)); 89 } 90 91 // Check to see what kind of stream we actually got. 92 deviceId = AAudioStream_getDeviceId(s_AudioEngine.stream); 93 framesPerBurst = AAudioStream_getFramesPerBurst(s_AudioEngine.stream); 94 95 printf("-------- started: deviceId = %3d, framesPerBurst = %3d\n", deviceId, framesPerBurst); 96 97 return result; 98 } 99 100 static aaudio_result_t s_StopAudio() { 101 aaudio_result_t result = AAUDIO_OK; 102 if (s_AudioEngine.stream != nullptr) { 103 result = AAudioStream_requestStop(s_AudioEngine.stream); 104 if (result != AAUDIO_OK) { 105 printf("AAudioStream_requestStop returned %s\n", 106 AAudio_convertResultToText(result)); 107 } 108 result = AAudioStream_close(s_AudioEngine.stream); 109 if (result != AAUDIO_OK) { 110 printf("AAudioStream_close returned %s\n", 111 AAudio_convertResultToText(result)); 112 } 113 s_AudioEngine.stream = nullptr; 114 AAudioStreamBuilder_delete(s_AudioEngine.builder); 115 s_AudioEngine.builder = nullptr; 116 } 117 return result; 118 } 119 120 static void s_StartThreadProc() { 121 // A good app would call s_StopAudio here! This test simulates a bad app. 122 s_StartAudio(); 123 s_AudioEngine.thread = nullptr; 124 } 125 126 static void s_myErrorCallbackProc( 127 AAudioStream *stream __unused, 128 void *userData __unused, 129 aaudio_result_t error) { 130 if (error == AAUDIO_ERROR_DISCONNECTED) { 131 // Handle stream restart on a separate thread 132 if (s_AudioEngine.thread == nullptr) { 133 s_AudioEngine.thread = new std::thread(s_StartThreadProc); 134 } 135 } 136 } 137 138 int main(int argc, char **argv) { 139 (void) argc; 140 (void) argv; 141 142 aaudio_result_t result = AAUDIO_OK; 143 144 // Make printf print immediately so that debug info is not stuck 145 // in a buffer if we hang or crash. 146 setvbuf(stdout, nullptr, _IONBF, (size_t) 0); 147 148 printf("Test Bad Disconnect V1.0\n"); 149 printf("\n=========== Please PLUG and UNPLUG headphones! ==============\n\n"); 150 printf("You should see the deviceID change on each plug event.\n"); 151 printf("Headphones will generally get a new deviceId each time.\n"); 152 printf("Speakers will have the same deviceId each time.\n"); 153 printf("The framesRead should reset on each plug event then increase over time.\n"); 154 printf("\n"); 155 156 result = s_StartAudio(); 157 158 if (result == AAUDIO_OK) { 159 for (int i = 20; i > 0; i--) { 160 sleep(1); 161 printf("playing silence #%d, framesRead = %d\n", i, (int) s_AudioEngine.framesRead); 162 } 163 } 164 165 s_StopAudio(); 166 167 printf("result = %d = %s\n", result, AAudio_convertResultToText(result)); 168 } 169