1 /**
2 * Copyright 2018 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 #include <cassert>
18 #include <logging_macros.h>
19
20 #include "LiveEffectEngine.h"
21
LiveEffectEngine()22 LiveEffectEngine::LiveEffectEngine() {
23 assert(mOutputChannelCount == mInputChannelCount);
24 }
25
setRecordingDeviceId(int32_t deviceId)26 void LiveEffectEngine::setRecordingDeviceId(int32_t deviceId) {
27 mRecordingDeviceId = deviceId;
28 }
29
setPlaybackDeviceId(int32_t deviceId)30 void LiveEffectEngine::setPlaybackDeviceId(int32_t deviceId) {
31 mPlaybackDeviceId = deviceId;
32 }
33
isAAudioRecommended()34 bool LiveEffectEngine::isAAudioRecommended() {
35 return oboe::AudioStreamBuilder::isAAudioRecommended();
36 }
37
setAudioApi(oboe::AudioApi api)38 bool LiveEffectEngine::setAudioApi(oboe::AudioApi api) {
39 if (mIsEffectOn) return false;
40 mAudioApi = api;
41 return true;
42 }
43
setEffectOn(bool isOn)44 bool LiveEffectEngine::setEffectOn(bool isOn) {
45 bool success = true;
46 if (isOn != mIsEffectOn) {
47 if (isOn) {
48 success = openStreams() == oboe::Result::OK;
49 if (success) {
50 mIsEffectOn = isOn;
51 }
52 } else {
53 closeStreams();
54 mIsEffectOn = isOn;
55 }
56 }
57 return success;
58 }
59
closeStreams()60 void LiveEffectEngine::closeStreams() {
61 /*
62 * Note: The order of events is important here.
63 * The playback stream must be closed before the recording stream. If the
64 * recording stream were to be closed first the playback stream's
65 * callback may attempt to read from the recording stream
66 * which would cause the app to crash since the recording stream would be
67 * null.
68 */
69 mDuplexStream->stop();
70 closeStream(mPlayStream);
71 closeStream(mRecordingStream);
72 mDuplexStream.reset();
73 }
74
openStreams()75 oboe::Result LiveEffectEngine::openStreams() {
76 // Note: The order of stream creation is important. We create the playback
77 // stream first, then use properties from the playback stream
78 // (e.g. sample rate) to create the recording stream. By matching the
79 // properties we should get the lowest latency path
80 oboe::AudioStreamBuilder inBuilder, outBuilder;
81 setupPlaybackStreamParameters(&outBuilder);
82 oboe::Result result = outBuilder.openStream(mPlayStream);
83 if (result != oboe::Result::OK) {
84 LOGE("Failed to open output stream. Error %s", oboe::convertToText(result));
85 mSampleRate = oboe::kUnspecified;
86 return result;
87 } else {
88 // The input stream needs to run at the same sample rate as the output.
89 mSampleRate = mPlayStream->getSampleRate();
90 }
91 warnIfNotLowLatency(mPlayStream);
92
93 setupRecordingStreamParameters(&inBuilder, mSampleRate);
94 result = inBuilder.openStream(mRecordingStream);
95 if (result != oboe::Result::OK) {
96 LOGE("Failed to open input stream. Error %s", oboe::convertToText(result));
97 closeStream(mPlayStream);
98 return result;
99 }
100 warnIfNotLowLatency(mRecordingStream);
101
102 mDuplexStream = std::make_unique<FullDuplexPass>();
103 mDuplexStream->setSharedInputStream(mRecordingStream);
104 mDuplexStream->setSharedOutputStream(mPlayStream);
105 mDuplexStream->start();
106 return result;
107 }
108
109 /**
110 * Sets the stream parameters which are specific to recording,
111 * including the sample rate which is determined from the
112 * playback stream.
113 *
114 * @param builder The recording stream builder
115 * @param sampleRate The desired sample rate of the recording stream
116 */
setupRecordingStreamParameters(oboe::AudioStreamBuilder * builder,int32_t sampleRate)117 oboe::AudioStreamBuilder *LiveEffectEngine::setupRecordingStreamParameters(
118 oboe::AudioStreamBuilder *builder, int32_t sampleRate) {
119 // This sample uses blocking read() because we don't specify a callback
120 builder->setDeviceId(mRecordingDeviceId)
121 ->setDirection(oboe::Direction::Input)
122 ->setSampleRate(sampleRate)
123 ->setChannelCount(mInputChannelCount);
124 return setupCommonStreamParameters(builder);
125 }
126
127 /**
128 * Sets the stream parameters which are specific to playback, including device
129 * id and the dataCallback function, which must be set for low latency
130 * playback.
131 * @param builder The playback stream builder
132 */
setupPlaybackStreamParameters(oboe::AudioStreamBuilder * builder)133 oboe::AudioStreamBuilder *LiveEffectEngine::setupPlaybackStreamParameters(
134 oboe::AudioStreamBuilder *builder) {
135 builder->setDataCallback(this)
136 ->setErrorCallback(this)
137 ->setDeviceId(mPlaybackDeviceId)
138 ->setDirection(oboe::Direction::Output)
139 ->setChannelCount(mOutputChannelCount);
140
141 return setupCommonStreamParameters(builder);
142 }
143
144 /**
145 * Set the stream parameters which are common to both recording and playback
146 * streams.
147 * @param builder The playback or recording stream builder
148 */
setupCommonStreamParameters(oboe::AudioStreamBuilder * builder)149 oboe::AudioStreamBuilder *LiveEffectEngine::setupCommonStreamParameters(
150 oboe::AudioStreamBuilder *builder) {
151 // We request EXCLUSIVE mode since this will give us the lowest possible
152 // latency.
153 // If EXCLUSIVE mode isn't available the builder will fall back to SHARED
154 // mode.
155 builder->setAudioApi(mAudioApi)
156 ->setFormat(mFormat)
157 ->setFormatConversionAllowed(true)
158 ->setSharingMode(oboe::SharingMode::Exclusive)
159 ->setPerformanceMode(oboe::PerformanceMode::LowLatency);
160 return builder;
161 }
162
163 /**
164 * Close the stream. AudioStream::close() is a blocking call so
165 * the application does not need to add synchronization between
166 * onAudioReady() function and the thread calling close().
167 * [the closing thread is the UI thread in this sample].
168 * @param stream the stream to close
169 */
closeStream(std::shared_ptr<oboe::AudioStream> & stream)170 void LiveEffectEngine::closeStream(std::shared_ptr<oboe::AudioStream> &stream) {
171 if (stream) {
172 oboe::Result result = stream->stop();
173 if (result != oboe::Result::OK) {
174 LOGW("Error stopping stream: %s", oboe::convertToText(result));
175 }
176 result = stream->close();
177 if (result != oboe::Result::OK) {
178 LOGE("Error closing stream: %s", oboe::convertToText(result));
179 } else {
180 LOGW("Successfully closed streams");
181 }
182 stream.reset();
183 }
184 }
185
186 /**
187 * Warn in logcat if non-low latency stream is created
188 * @param stream: newly created stream
189 *
190 */
warnIfNotLowLatency(std::shared_ptr<oboe::AudioStream> & stream)191 void LiveEffectEngine::warnIfNotLowLatency(std::shared_ptr<oboe::AudioStream> &stream) {
192 if (stream->getPerformanceMode() != oboe::PerformanceMode::LowLatency) {
193 LOGW(
194 "Stream is NOT low latency."
195 "Check your requested format, sample rate and channel count");
196 }
197 }
198
199 /**
200 * Handles playback stream's audio request. In this sample, we simply block-read
201 * from the record stream for the required samples.
202 *
203 * @param oboeStream: the playback stream that requesting additional samples
204 * @param audioData: the buffer to load audio samples for playback stream
205 * @param numFrames: number of frames to load to audioData buffer
206 * @return: DataCallbackResult::Continue.
207 */
onAudioReady(oboe::AudioStream * oboeStream,void * audioData,int32_t numFrames)208 oboe::DataCallbackResult LiveEffectEngine::onAudioReady(
209 oboe::AudioStream *oboeStream, void *audioData, int32_t numFrames) {
210 return mDuplexStream->onAudioReady(oboeStream, audioData, numFrames);
211 }
212
213 /**
214 * Oboe notifies the application for "about to close the stream".
215 *
216 * @param oboeStream: the stream to close
217 * @param error: oboe's reason for closing the stream
218 */
onErrorBeforeClose(oboe::AudioStream * oboeStream,oboe::Result error)219 void LiveEffectEngine::onErrorBeforeClose(oboe::AudioStream *oboeStream,
220 oboe::Result error) {
221 LOGE("%s stream Error before close: %s",
222 oboe::convertToText(oboeStream->getDirection()),
223 oboe::convertToText(error));
224 }
225
226 /**
227 * Oboe notifies application that "the stream is closed"
228 *
229 * @param oboeStream
230 * @param error
231 */
onErrorAfterClose(oboe::AudioStream * oboeStream,oboe::Result error)232 void LiveEffectEngine::onErrorAfterClose(oboe::AudioStream *oboeStream,
233 oboe::Result error) {
234 LOGE("%s stream Error after close: %s",
235 oboe::convertToText(oboeStream->getDirection()),
236 oboe::convertToText(error));
237
238 closeStreams();
239
240 // Restart the stream if the error is a disconnect.
241 if (error == oboe::Result::ErrorDisconnected) {
242 LOGI("Restarting AudioStream");
243 openStreams();
244 }
245 }
246