xref: /aosp_15_r20/external/oboe/samples/hello-oboe/src/main/cpp/HelloOboeEngine.cpp (revision 05767d913155b055644481607e6fa1e35e2fe72c)
1 /**
2  * Copyright 2017 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 #include <inttypes.h>
19 #include <memory>
20 
21 #include <Oscillator.h>
22 
23 #include "HelloOboeEngine.h"
24 #include "SoundGenerator.h"
25 
26 
27 /**
28  * Main audio engine for the HelloOboe sample. It is responsible for:
29  *
30  * - Creating a callback object which is supplied when constructing the audio stream, and will be
31  * called when the stream starts
32  * - Restarting the stream when user-controllable properties (Audio API, channel count etc) are
33  * changed, and when the stream is disconnected (e.g. when headphones are attached)
34  * - Calculating the audio latency of the stream
35  *
36  */
HelloOboeEngine()37 HelloOboeEngine::HelloOboeEngine()
38         : mLatencyCallback(std::make_shared<LatencyTuningCallback>()),
39         mErrorCallback(std::make_shared<DefaultErrorCallback>(*this)) {
40 }
41 
getCurrentOutputLatencyMillis()42 double HelloOboeEngine::getCurrentOutputLatencyMillis() {
43     if (!mIsLatencyDetectionSupported) return -1.0;
44 
45     std::lock_guard<std::mutex> lock(mLock);
46     if (!mStream) return -1.0;
47 
48     oboe::ResultWithValue<double> latencyResult = mStream->calculateLatencyMillis();
49     if (latencyResult) {
50         return latencyResult.value();
51     } else {
52         LOGE("Error calculating latency: %s", oboe::convertToText(latencyResult.error()));
53         return -1.0;
54     }
55 }
56 
setBufferSizeInBursts(int32_t numBursts)57 void HelloOboeEngine::setBufferSizeInBursts(int32_t numBursts) {
58     std::lock_guard<std::mutex> lock(mLock);
59     if (!mStream) return;
60 
61     mLatencyCallback->setBufferTuneEnabled(numBursts == kBufferSizeAutomatic);
62     auto result = mStream->setBufferSizeInFrames(
63             numBursts * mStream->getFramesPerBurst());
64     if (result) {
65         LOGD("Buffer size successfully changed to %d", result.value());
66     } else {
67         LOGW("Buffer size could not be changed, %d", result.error());
68     }
69 }
70 
isLatencyDetectionSupported()71 bool HelloOboeEngine::isLatencyDetectionSupported() {
72     return mIsLatencyDetectionSupported;
73 }
74 
isAAudioRecommended()75 bool HelloOboeEngine::isAAudioRecommended() {
76     return oboe::AudioStreamBuilder::isAAudioRecommended();
77 }
78 
tap(bool isDown)79 void HelloOboeEngine::tap(bool isDown) {
80     if (mAudioSource) {
81         mAudioSource->tap(isDown);
82     }
83 }
84 
openPlaybackStream()85 oboe::Result HelloOboeEngine::openPlaybackStream() {
86     oboe::AudioStreamBuilder builder;
87     oboe::Result result = builder.setSharingMode(oboe::SharingMode::Exclusive)
88         ->setPerformanceMode(oboe::PerformanceMode::LowLatency)
89         ->setFormat(oboe::AudioFormat::Float)
90         ->setFormatConversionAllowed(true)
91         ->setDataCallback(mLatencyCallback)
92         ->setErrorCallback(mErrorCallback)
93         ->setAudioApi(mAudioApi)
94         ->setChannelCount(mChannelCount)
95         ->setDeviceId(mDeviceId)
96         ->openStream(mStream);
97     if (result == oboe::Result::OK) {
98         mChannelCount = mStream->getChannelCount();
99     }
100     return result;
101 }
102 
restart()103 void HelloOboeEngine::restart() {
104     // The stream will have already been closed by the error callback.
105     mLatencyCallback->reset();
106     start();
107 }
108 
start(oboe::AudioApi audioApi,int deviceId,int channelCount)109 oboe::Result HelloOboeEngine::start(oboe::AudioApi audioApi, int deviceId, int channelCount) {
110     mAudioApi = audioApi;
111     mDeviceId = deviceId;
112     mChannelCount = channelCount;
113     return start();
114 }
115 
start()116 oboe::Result HelloOboeEngine::start() {
117     std::lock_guard<std::mutex> lock(mLock);
118     oboe::Result result = oboe::Result::OK;
119     // It is possible for a stream's device to become disconnected during the open or between
120     // the Open and the Start.
121     // So if it fails to start, close the old stream and try again.
122     int tryCount = 0;
123     do {
124         if (tryCount > 0) {
125             usleep(20 * 1000); // Sleep between tries to give the system time to settle.
126         }
127         mIsLatencyDetectionSupported = false;
128         result = openPlaybackStream();
129         if (result == oboe::Result::OK) {
130             mAudioSource = std::make_shared<SoundGenerator>(mStream->getSampleRate(),
131                                                             mStream->getChannelCount());
132             mLatencyCallback->setSource(
133                     std::dynamic_pointer_cast<IRenderableAudio>(mAudioSource));
134 
135             LOGD("Stream opened: AudioAPI = %d, channelCount = %d, deviceID = %d",
136                  mStream->getAudioApi(),
137                  mStream->getChannelCount(),
138                  mStream->getDeviceId());
139 
140             result = mStream->requestStart();
141             if (result != oboe::Result::OK) {
142                 LOGE("Error starting playback stream. Error: %s", oboe::convertToText(result));
143                 mStream->close();
144                 mStream.reset();
145             } else {
146                 mIsLatencyDetectionSupported = (mStream->getTimestamp((CLOCK_MONOTONIC)) !=
147                                                 oboe::Result::ErrorUnimplemented);
148             }
149         } else {
150             LOGE("Error creating playback stream. Error: %s", oboe::convertToText(result));
151         }
152     } while (result != oboe::Result::OK && tryCount++ < 3);
153     return result;
154 }
155 
stop()156 oboe::Result HelloOboeEngine::stop() {
157     oboe::Result result = oboe::Result::OK;
158     // Stop, close and delete in case not already closed.
159     std::lock_guard<std::mutex> lock(mLock);
160     if (mStream) {
161         result = mStream->stop();
162         mStream->close();
163         mStream.reset();
164     }
165     return result;
166 }
167 
reopenStream()168 oboe::Result HelloOboeEngine::reopenStream() {
169     if (mStream) {
170         stop();
171         return start();
172     } else {
173         return oboe::Result::OK;
174     }
175 }
176