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.
s_myDataCallbackProc(AAudioStream * stream,void * userData,void * audioData,int32_t numFrames)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
s_StartAudio()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
s_StopAudio()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
s_StartThreadProc()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
s_myErrorCallbackProc(AAudioStream * stream __unused,void * userData __unused,aaudio_result_t error)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
main(int argc,char ** argv)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