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