/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Return stop from the callback
* and then close the stream immediately.
*/
#include <atomic>
#include <mutex>
#include <stdio.h>
#include <thread>
#include <unistd.h>
#include <aaudio/AAudio.h>
#define DURATION_SECONDS 5
struct AudioEngine {
AAudioStreamBuilder *builder = nullptr;
AAudioStream *stream = nullptr;
std::thread *thread = nullptr;
std::atomic<bool> started{false};
std::mutex doneLock; // Use a mutex so we can sleep on it while join()ing.
std::atomic<bool> done{false};
aaudio_result_t join() {
aaudio_result_t result = AAUDIO_ERROR_INVALID_STATE;
if (stream != nullptr) {
while (true) {
{
// Will block if the thread is running.
// This mutex is used to close() immediately after the callback returns
// and before the requestStop() is called.
std::lock_guard<std::mutex> lock(doneLock);
if (done) break;
}
printf("join() got mutex but stream not done!");
usleep(10 * 1000); // sleep then check again
}
result = AAudioStream_close(stream);
stream = nullptr;
}
return result;
}
};
// Callback function that fills the audio output buffer.
static aaudio_data_callback_result_t s_myDataCallbackProc(
AAudioStream *stream,
void *userData,
void *audioData,
int32_t numFrames
) {
(void) stream;
(void) audioData;
(void) numFrames;
AudioEngine *engine = (struct AudioEngine *)userData;
std::lock_guard<std::mutex> lock(engine->doneLock);
engine->started = true;
usleep(DURATION_SECONDS * 1000 * 1000); // Mimic SynthMark procedure.
engine->done = true;
return AAUDIO_CALLBACK_RESULT_STOP;
}
static void s_myErrorCallbackProc(
AAudioStream *stream __unused,
void *userData __unused,
aaudio_result_t error) {
printf("%s() - error = %d\n", __func__, error);
}
static aaudio_result_t s_OpenAudioStream(struct AudioEngine *engine) {
// Use an AAudioStreamBuilder to contain requested parameters.
aaudio_result_t result = AAudio_createStreamBuilder(&engine->builder);
if (result != AAUDIO_OK) {
printf("AAudio_createStreamBuilder returned %s",
AAudio_convertResultToText(result));
return result;
}
// Request stream properties.
AAudioStreamBuilder_setPerformanceMode(engine->builder, AAUDIO_PERFORMANCE_MODE_LOW_LATENCY);
AAudioStreamBuilder_setDataCallback(engine->builder, s_myDataCallbackProc, engine);
AAudioStreamBuilder_setErrorCallback(engine->builder, s_myErrorCallbackProc, engine);
// Create an AAudioStream using the Builder.
result = AAudioStreamBuilder_openStream(engine->builder, &engine->stream);
if (result != AAUDIO_OK) {
printf("AAudioStreamBuilder_openStream returned %s",
AAudio_convertResultToText(result));
return result;
}
return result;
}
int main(int argc, char **argv) {
(void) argc;
(void) argv;
struct AudioEngine engine;
aaudio_result_t result = AAUDIO_OK;
int errorCount = 0;
// Make printf print immediately so that debug info is not stuck
// in a buffer if we hang or crash.
setvbuf(stdout, nullptr, _IONBF, (size_t) 0);
printf("Test Return Stop Hang V1.0\n");
result = s_OpenAudioStream(&engine);
if (result != AAUDIO_OK) {
printf("s_OpenAudioStream returned %s\n",
AAudio_convertResultToText(result));
errorCount++;
}
// Check to see what kind of stream we actually got.
int32_t deviceId = AAudioStream_getDeviceId(engine.stream);
aaudio_performance_mode_t actualPerfMode = AAudioStream_getPerformanceMode(engine.stream);
printf("-------- opened: deviceId = %3d, perfMode = %d\n", deviceId, actualPerfMode);
// Start stream.
result = AAudioStream_requestStart(engine.stream);
printf("AAudioStream_requestStart() returned %d >>>>>>>>>>>>>>>>>>>>>>\n", result);
if (result != AAUDIO_OK) {
errorCount++;
} else {
int counter = 0;
while (!engine.started) {
printf("Waiting for stream to start, %d\n", counter++);
usleep(5 * 1000);
}
printf("You should see more messages %d seconds after this. If not then the test failed!\n",
DURATION_SECONDS);
result = engine.join(); // This might hang!
AAudioStreamBuilder_delete(engine.builder);
engine.builder = nullptr;
}
printf("aaudio result = %d = %s\n", result, AAudio_convertResultToText(result));
printf("test %s\n", errorCount ? "FAILED" : "PASSED");
return errorCount ? EXIT_FAILURE : EXIT_SUCCESS;
}