1 /*
2  * Copyright (C) 2018 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  * Expect the callback to cease.
20  * Check the logcat for bad behavior.
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 #define STOP_AT_MSEC          1000
31 #define LOOP_DURATION_MSEC    4000
32 #define SLEEP_DURATION_MSEC    200
33 
34 static void s_myErrorCallbackProc(
35         AAudioStream *stream,
36         void *userData,
37         aaudio_result_t error);
38 
39 struct AudioEngine {
40     AAudioStreamBuilder *builder = nullptr;
41     AAudioStream        *stream = nullptr;
42     std::thread         *thread = nullptr;
43     int32_t              stopAtFrame = 0;
44     bool                 stopped = false;
45     // These counters are read and written by the callback and the main thread.
46     std::atomic<int32_t> framesRead{};
47     std::atomic<int32_t> startingFramesRead{};
48     std::atomic<int32_t> framesCalled{};
49     std::atomic<int32_t> callbackCount{};
50     std::atomic<int32_t> callbackCountAfterStop{};
51 
resetAudioEngine52     void reset() {
53         framesRead.store(0);
54         startingFramesRead.store(0);
55         framesCalled.store(0);
56         callbackCount.store(0);
57         callbackCountAfterStop.store(0);
58         stopped = false;
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) audioData;
70     (void) numFrames;
71     AudioEngine *engine = (struct AudioEngine *)userData;
72     engine->callbackCount++;
73     if (engine->stopped) {
74         engine->callbackCountAfterStop++;
75     }
76 
77     engine->framesRead = (int32_t)AAudioStream_getFramesRead(stream);
78     if (engine->startingFramesRead == 0) {
79         engine->startingFramesRead.store(engine->framesRead.load());
80     }
81     engine->framesCalled += numFrames;
82     if (engine->framesCalled >= engine->stopAtFrame) {
83         engine->stopped = true;
84         return AAUDIO_CALLBACK_RESULT_STOP;
85     } else {
86         return AAUDIO_CALLBACK_RESULT_CONTINUE;
87     }
88 }
89 
s_OpenAudioStream(struct AudioEngine * engine,aaudio_direction_t direction,aaudio_sharing_mode_t sharingMode,aaudio_performance_mode_t perfMode)90 static aaudio_result_t s_OpenAudioStream(struct AudioEngine *engine,
91                                          aaudio_direction_t direction,
92                                          aaudio_sharing_mode_t sharingMode,
93                                          aaudio_performance_mode_t perfMode) {
94     // Use an AAudioStreamBuilder to contain requested parameters.
95     aaudio_result_t result = AAudio_createStreamBuilder(&engine->builder);
96     if (result != AAUDIO_OK) {
97         printf("AAudio_createStreamBuilder returned %s",
98                AAudio_convertResultToText(result));
99         return result;
100     }
101 
102     // Request stream properties.
103     AAudioStreamBuilder_setFormat(engine->builder, AAUDIO_FORMAT_PCM_FLOAT);
104     AAudioStreamBuilder_setPerformanceMode(engine->builder, perfMode);
105     AAudioStreamBuilder_setSharingMode(engine->builder, sharingMode);
106     AAudioStreamBuilder_setDirection(engine->builder, direction);
107     AAudioStreamBuilder_setDataCallback(engine->builder, s_myDataCallbackProc, engine);
108     AAudioStreamBuilder_setErrorCallback(engine->builder, s_myErrorCallbackProc, engine);
109 
110     // Create an AAudioStream using the Builder.
111     result = AAudioStreamBuilder_openStream(engine->builder, &engine->stream);
112     if (result != AAUDIO_OK) {
113         printf("AAudioStreamBuilder_openStream returned %s",
114                AAudio_convertResultToText(result));
115         return result;
116     }
117 
118     return result;
119 }
120 
s_CloseAudioStream(struct AudioEngine * engine)121 static aaudio_result_t s_CloseAudioStream(struct AudioEngine *engine) {
122     aaudio_result_t result = AAUDIO_OK;
123     if (engine->stream != nullptr) {
124         result = AAudioStream_close(engine->stream);
125         if (result != AAUDIO_OK) {
126             printf("AAudioStream_close returned %s\n",
127                    AAudio_convertResultToText(result));
128         }
129         engine->stream = nullptr;
130     }
131     AAudioStreamBuilder_delete(engine->builder);
132     engine->builder = nullptr;
133     return result;
134 }
135 
s_myErrorCallbackProc(AAudioStream * stream __unused,void * userData __unused,aaudio_result_t error)136 static void s_myErrorCallbackProc(
137         AAudioStream *stream __unused,
138         void *userData __unused,
139         aaudio_result_t error) {
140     printf("%s() - error = %d\n", __func__, error);
141 }
142 
s_usage()143 static void s_usage() {
144     printf("test_return_stop [-i] [-x] [-n] [-c]\n");
145     printf("     -i direction INPUT, otherwise OUTPUT\n");
146     printf("     -x sharing mode EXCLUSIVE, otherwise SHARED\n");
147     printf("     -n performance mode NONE, otherwise LOW_LATENCY\n");
148     printf("     -c always return CONTINUE from callback, not STOP\n");
149 }
150 
151 /**
152  * @return 0 is OK, -1 for error
153  */
s_checkEnginePositions(AudioEngine * engine)154 static int s_checkEnginePositions(AudioEngine *engine) {
155     const int64_t framesRead = AAudioStream_getFramesRead(engine->stream);
156     const int64_t framesWritten = AAudioStream_getFramesWritten(engine->stream);
157     const int32_t delta = (int32_t)(framesWritten - framesRead);
158     printf("playing framesRead = %7d, framesWritten = %7d"
159            ", delta = %4d, framesCalled = %6d, callbackCount = %4d\n",
160            (int32_t) framesRead,
161            (int32_t) framesWritten,
162            delta,
163            engine->framesCalled.load(),
164            engine->callbackCount.load()
165     );
166     if (delta > AAudioStream_getBufferCapacityInFrames(engine->stream)) {
167         printf("ERROR - delta > capacity\n");
168         return -1;
169     }
170     return 0;
171 }
172 
main(int argc,char ** argv)173 int main(int argc, char **argv) {
174     (void) argc;
175     (void) argv;
176     struct AudioEngine engine;
177     aaudio_sharing_mode_t sharingMode = AAUDIO_SHARING_MODE_SHARED;
178     aaudio_performance_mode_t perfMode = AAUDIO_PERFORMANCE_MODE_LOW_LATENCY;
179     aaudio_direction_t direction = AAUDIO_DIRECTION_OUTPUT;
180     aaudio_result_t result = AAUDIO_OK;
181     bool alwaysContinue = false;
182     int errorCount = 0;
183     int callbackResult = EXIT_SUCCESS;
184 
185     // Make printf print immediately so that debug info is not stuck
186     // in a buffer if we hang or crash.
187     setvbuf(stdout, nullptr, _IONBF, (size_t) 0);
188 
189     printf("Test Return Stop V1.0\n");
190     printf("Wait for a few seconds.\n");
191     printf("You should see callbackCount and framesRead stop advancing\n");
192     printf("when callbackCount reaches %d msec\n", STOP_AT_MSEC);
193     printf("\n");
194 
195     for (int i = 1; i < argc; i++) {
196         const char *arg = argv[i];
197         if (arg[0] == '-') {
198             char option = arg[1];
199             switch (option) {
200                 case 'c':
201                     alwaysContinue = true;
202                     break;
203                 case 'i':
204                     direction = AAUDIO_DIRECTION_INPUT;
205                     break;
206                 case 'n':
207                     perfMode = AAUDIO_PERFORMANCE_MODE_NONE;
208                     break;
209                 case 'x':
210                     sharingMode = AAUDIO_SHARING_MODE_EXCLUSIVE;
211                     break;
212                 default:
213                     s_usage();
214                     exit(EXIT_FAILURE);
215                     break;
216             }
217         } else {
218             s_usage();
219             exit(EXIT_FAILURE);
220             break;
221         }
222     }
223 
224     result = s_OpenAudioStream(&engine, direction, sharingMode, perfMode);
225     if (result != AAUDIO_OK) {
226         printf("s_OpenAudioStream returned %s\n",
227                AAudio_convertResultToText(result));
228         errorCount++;
229     }
230 
231     int32_t framesPerBurst = AAudioStream_getFramesPerBurst(engine.stream);
232     // Use double buffered stream.
233     const int32_t bufferSize = AAudioStream_setBufferSizeInFrames(engine.stream, 2 * framesPerBurst);
234     if (bufferSize < 0) {
235         printf("AAudioStream_setBufferSizeInFrames returned %s\n",
236                AAudio_convertResultToText(bufferSize));
237         errorCount++;
238     }
239 
240     // Check to see what kind of stream we actually got.
241     int32_t deviceId = AAudioStream_getDeviceId(engine.stream);
242     aaudio_performance_mode_t actualPerfMode = AAudioStream_getPerformanceMode(engine.stream);
243     printf("-------- opened: deviceId = %3d, framesPerBurst = %3d, perfMode = %d\n",
244            deviceId, framesPerBurst, actualPerfMode);
245 
246     // Calculate how many callbacks needed.
247     if (alwaysContinue) {
248         engine.stopAtFrame = INT32_MAX;
249     } else {
250         int32_t sampleRate = AAudioStream_getSampleRate(engine.stream);
251         engine.stopAtFrame = STOP_AT_MSEC * sampleRate / 1000;
252     }
253 
254     for (int loops = 0; loops < 2 && result == AAUDIO_OK; loops++) {
255         engine.reset();
256 
257         // Start stream.
258         result = AAudioStream_requestStart(engine.stream);
259         printf("AAudioStream_requestStart() returned %d >>>>>>>>>>>>>>>>>>>>>>\n", result);
260         if (result != AAUDIO_OK) {
261             errorCount++;
262             break;
263         }
264 
265         if (result == AAUDIO_OK) {
266             const int watchLoops = LOOP_DURATION_MSEC / SLEEP_DURATION_MSEC;
267             for (int i = watchLoops; i > 0; i--) {
268                 errorCount += s_checkEnginePositions(&engine) ? 1 : 0;
269                 usleep(SLEEP_DURATION_MSEC * 1000);
270             }
271         }
272 
273         if (engine.stopAtFrame != INT32_MAX) {
274             callbackResult = (engine.callbackCountAfterStop == 0) ? EXIT_SUCCESS
275                                                                   : EXIT_FAILURE;
276             if (callbackResult) {
277                 printf("ERROR - Callback count after STOP = %d\n",
278                        engine.callbackCountAfterStop.load());
279                 errorCount++;
280             }
281         }
282 
283         if (engine.startingFramesRead.load() == engine.framesRead.load()) {
284             printf("ERROR - framesRead did not advance across callbacks\n");
285             errorCount++;
286         }
287 
288         result = AAudioStream_requestStop(engine.stream);
289         printf("AAudioStream_requestStop() returned %d <<<<<<<<<<<<<<<<<<<<<\n", result);
290         if (result != AAUDIO_OK) {
291             errorCount++;
292         }
293         usleep(SLEEP_DURATION_MSEC * 1000);
294         errorCount += s_checkEnginePositions(&engine) ? 1 : 0;
295     }
296 
297     s_CloseAudioStream(&engine);
298 
299     printf("aaudio result = %d = %s\n", result, AAudio_convertResultToText(result));
300     printf("test %s\n", errorCount ? "FAILED" : "PASSED");
301 
302     return errorCount ? EXIT_FAILURE : EXIT_SUCCESS;
303 }
304