• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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