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