169 lines
5.5 KiB
C++
169 lines
5.5 KiB
C++
/*
|
|
* Copyright (C) 2017 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.
|
|
*/
|
|
|
|
/**
|
|
* Handle a DISCONNECT by only opening and starting a new stream
|
|
* without stopping and closing the old one.
|
|
* This caused the new stream to use the old disconnected device.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <thread>
|
|
#include <unistd.h>
|
|
|
|
#include <aaudio/AAudio.h>
|
|
|
|
#define DEFAULT_TIMEOUT_NANOS ((int64_t)1000000000)
|
|
|
|
static void s_myErrorCallbackProc(
|
|
AAudioStream *stream,
|
|
void *userData,
|
|
aaudio_result_t error);
|
|
|
|
struct AudioEngine {
|
|
AAudioStreamBuilder *builder = nullptr;
|
|
AAudioStream *stream = nullptr;
|
|
std::thread *thread = nullptr;
|
|
int64_t framesRead = 0;
|
|
};
|
|
|
|
AudioEngine s_AudioEngine;
|
|
|
|
// 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) userData;
|
|
(void) audioData;
|
|
(void) numFrames;
|
|
s_AudioEngine.framesRead = AAudioStream_getFramesRead(stream);
|
|
return AAUDIO_CALLBACK_RESULT_CONTINUE;
|
|
}
|
|
|
|
static aaudio_result_t s_StartAudio() {
|
|
int32_t framesPerBurst = 0;
|
|
int32_t deviceId = 0;
|
|
|
|
// Use an AAudioStreamBuilder to contain requested parameters.
|
|
aaudio_result_t result = AAudio_createStreamBuilder(&s_AudioEngine.builder);
|
|
if (result != AAUDIO_OK) {
|
|
printf("AAudio_createStreamBuilder returned %s",
|
|
AAudio_convertResultToText(result));
|
|
return result;
|
|
}
|
|
|
|
// Request stream properties.
|
|
AAudioStreamBuilder_setFormat(s_AudioEngine.builder, AAUDIO_FORMAT_PCM_FLOAT);
|
|
AAudioStreamBuilder_setPerformanceMode(s_AudioEngine.builder, AAUDIO_PERFORMANCE_MODE_LOW_LATENCY);
|
|
AAudioStreamBuilder_setDataCallback(s_AudioEngine.builder, s_myDataCallbackProc, nullptr);
|
|
AAudioStreamBuilder_setErrorCallback(s_AudioEngine.builder, s_myErrorCallbackProc, nullptr);
|
|
|
|
// Create an AAudioStream using the Builder.
|
|
result = AAudioStreamBuilder_openStream(s_AudioEngine.builder, &s_AudioEngine.stream);
|
|
if (result != AAUDIO_OK) {
|
|
printf("AAudioStreamBuilder_openStream returned %s",
|
|
AAudio_convertResultToText(result));
|
|
return result;
|
|
}
|
|
|
|
result = AAudioStream_requestStart(s_AudioEngine.stream);
|
|
if (result != AAUDIO_OK) {
|
|
printf("AAudioStream_requestStart returned %s",
|
|
AAudio_convertResultToText(result));
|
|
}
|
|
|
|
// Check to see what kind of stream we actually got.
|
|
deviceId = AAudioStream_getDeviceId(s_AudioEngine.stream);
|
|
framesPerBurst = AAudioStream_getFramesPerBurst(s_AudioEngine.stream);
|
|
|
|
printf("-------- started: deviceId = %3d, framesPerBurst = %3d\n", deviceId, framesPerBurst);
|
|
|
|
return result;
|
|
}
|
|
|
|
static aaudio_result_t s_StopAudio() {
|
|
aaudio_result_t result = AAUDIO_OK;
|
|
if (s_AudioEngine.stream != nullptr) {
|
|
result = AAudioStream_requestStop(s_AudioEngine.stream);
|
|
if (result != AAUDIO_OK) {
|
|
printf("AAudioStream_requestStop returned %s\n",
|
|
AAudio_convertResultToText(result));
|
|
}
|
|
result = AAudioStream_close(s_AudioEngine.stream);
|
|
if (result != AAUDIO_OK) {
|
|
printf("AAudioStream_close returned %s\n",
|
|
AAudio_convertResultToText(result));
|
|
}
|
|
s_AudioEngine.stream = nullptr;
|
|
AAudioStreamBuilder_delete(s_AudioEngine.builder);
|
|
s_AudioEngine.builder = nullptr;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
static void s_StartThreadProc() {
|
|
// A good app would call s_StopAudio here! This test simulates a bad app.
|
|
s_StartAudio();
|
|
s_AudioEngine.thread = nullptr;
|
|
}
|
|
|
|
static void s_myErrorCallbackProc(
|
|
AAudioStream *stream __unused,
|
|
void *userData __unused,
|
|
aaudio_result_t error) {
|
|
if (error == AAUDIO_ERROR_DISCONNECTED) {
|
|
// Handle stream restart on a separate thread
|
|
if (s_AudioEngine.thread == nullptr) {
|
|
s_AudioEngine.thread = new std::thread(s_StartThreadProc);
|
|
}
|
|
}
|
|
}
|
|
|
|
int main(int argc, char **argv) {
|
|
(void) argc;
|
|
(void) argv;
|
|
|
|
aaudio_result_t result = AAUDIO_OK;
|
|
|
|
// 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 Bad Disconnect V1.0\n");
|
|
printf("\n=========== Please PLUG and UNPLUG headphones! ==============\n\n");
|
|
printf("You should see the deviceID change on each plug event.\n");
|
|
printf("Headphones will generally get a new deviceId each time.\n");
|
|
printf("Speakers will have the same deviceId each time.\n");
|
|
printf("The framesRead should reset on each plug event then increase over time.\n");
|
|
printf("\n");
|
|
|
|
result = s_StartAudio();
|
|
|
|
if (result == AAUDIO_OK) {
|
|
for (int i = 20; i > 0; i--) {
|
|
sleep(1);
|
|
printf("playing silence #%d, framesRead = %d\n", i, (int) s_AudioEngine.framesRead);
|
|
}
|
|
}
|
|
|
|
s_StopAudio();
|
|
|
|
printf("result = %d = %s\n", result, AAudio_convertResultToText(result));
|
|
}
|