1 /*
2  * Copyright (C) 2019 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  * Return stop from the callback
19  * and then close the stream immediately.
20  */
21 
22 #include <atomic>
23 #include <mutex>
24 #include <stdio.h>
25 #include <thread>
26 #include <unistd.h>
27 
28 #include <aaudio/AAudio.h>
29 
30 #define DURATION_SECONDS   5
31 
32 struct AudioEngine {
33     AAudioStreamBuilder *builder = nullptr;
34     AAudioStream        *stream = nullptr;
35     std::thread         *thread = nullptr;
36 
37     std::atomic<bool>   started{false};
38     std::mutex          doneLock; // Use a mutex so we can sleep on it while join()ing.
39     std::atomic<bool>   done{false};
40 
joinAudioEngine41     aaudio_result_t join() {
42         aaudio_result_t result = AAUDIO_ERROR_INVALID_STATE;
43         if (stream != nullptr) {
44             while (true) {
45                 {
46                     // Will block if the thread is running.
47                     // This mutex is used to close() immediately after the callback returns
48                     // and before the requestStop() is called.
49                     std::lock_guard<std::mutex> lock(doneLock);
50                     if (done) break;
51                 }
52                 printf("join() got mutex but stream not done!");
53                 usleep(10 * 1000); // sleep then check again
54             }
55             result = AAudioStream_close(stream);
56             stream = nullptr;
57         }
58         return result;
59     }
60 };
61 
62 // Callback function that fills the audio output buffer.
s_myDataCallbackProc(AAudioStream * stream,void * userData,void * audioData,int32_t numFrames)63 static aaudio_data_callback_result_t s_myDataCallbackProc(
64         AAudioStream *stream,
65         void *userData,
66         void *audioData,
67         int32_t numFrames
68 ) {
69     (void) stream;
70     (void) audioData;
71     (void) numFrames;
72     AudioEngine *engine = (struct AudioEngine *)userData;
73     std::lock_guard<std::mutex> lock(engine->doneLock);
74     engine->started = true;
75     usleep(DURATION_SECONDS * 1000 * 1000); // Mimic SynthMark procedure.
76     engine->done = true;
77     return AAUDIO_CALLBACK_RESULT_STOP;
78 }
79 
s_myErrorCallbackProc(AAudioStream * stream __unused,void * userData __unused,aaudio_result_t error)80 static void s_myErrorCallbackProc(
81     AAudioStream *stream __unused,
82     void *userData __unused,
83     aaudio_result_t error) {
84     printf("%s() - error = %d\n", __func__, error);
85 }
86 
s_OpenAudioStream(struct AudioEngine * engine)87 static aaudio_result_t s_OpenAudioStream(struct AudioEngine *engine) {
88     // Use an AAudioStreamBuilder to contain requested parameters.
89     aaudio_result_t result = AAudio_createStreamBuilder(&engine->builder);
90     if (result != AAUDIO_OK) {
91         printf("AAudio_createStreamBuilder returned %s",
92                AAudio_convertResultToText(result));
93         return result;
94     }
95 
96     // Request stream properties.
97     AAudioStreamBuilder_setPerformanceMode(engine->builder, AAUDIO_PERFORMANCE_MODE_LOW_LATENCY);
98     AAudioStreamBuilder_setDataCallback(engine->builder, s_myDataCallbackProc, engine);
99     AAudioStreamBuilder_setErrorCallback(engine->builder, s_myErrorCallbackProc, engine);
100 
101     // Create an AAudioStream using the Builder.
102     result = AAudioStreamBuilder_openStream(engine->builder, &engine->stream);
103     if (result != AAUDIO_OK) {
104         printf("AAudioStreamBuilder_openStream returned %s",
105                AAudio_convertResultToText(result));
106         return result;
107     }
108 
109     return result;
110 }
111 
main(int argc,char ** argv)112 int main(int argc, char **argv) {
113     (void) argc;
114     (void) argv;
115     struct AudioEngine engine;
116     aaudio_result_t result = AAUDIO_OK;
117     int errorCount = 0;
118 
119     // Make printf print immediately so that debug info is not stuck
120     // in a buffer if we hang or crash.
121     setvbuf(stdout, nullptr, _IONBF, (size_t) 0);
122 
123     printf("Test Return Stop Hang V1.0\n");
124 
125     result = s_OpenAudioStream(&engine);
126     if (result != AAUDIO_OK) {
127         printf("s_OpenAudioStream returned %s\n",
128                AAudio_convertResultToText(result));
129         errorCount++;
130     }
131 
132     // Check to see what kind of stream we actually got.
133     int32_t deviceId = AAudioStream_getDeviceId(engine.stream);
134     aaudio_performance_mode_t actualPerfMode = AAudioStream_getPerformanceMode(engine.stream);
135     printf("-------- opened: deviceId = %3d, perfMode = %d\n", deviceId, actualPerfMode);
136 
137     // Start stream.
138     result = AAudioStream_requestStart(engine.stream);
139     printf("AAudioStream_requestStart() returned %d >>>>>>>>>>>>>>>>>>>>>>\n", result);
140     if (result != AAUDIO_OK) {
141         errorCount++;
142     } else {
143         int counter = 0;
144         while (!engine.started) {
145             printf("Waiting for stream to start, %d\n", counter++);
146             usleep(5 * 1000);
147         }
148         printf("You should see more messages %d seconds after this. If not then the test failed!\n",
149                DURATION_SECONDS);
150         result = engine.join(); // This might hang!
151         AAudioStreamBuilder_delete(engine.builder);
152         engine.builder = nullptr;
153     }
154 
155     printf("aaudio result = %d = %s\n", result, AAudio_convertResultToText(result));
156     printf("test %s\n", errorCount ? "FAILED" : "PASSED");
157 
158     return errorCount ? EXIT_FAILURE : EXIT_SUCCESS;
159 }
160