| /* |
| * 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)); |
| } |