• 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 
~LiveEffectEngine()26 LiveEffectEngine::~LiveEffectEngine() {
27 
28     mFullDuplexPass.stop();
29     closeStream(mPlayStream);
30     closeStream(mRecordingStream);
31 }
32 
setRecordingDeviceId(int32_t deviceId)33 void LiveEffectEngine::setRecordingDeviceId(int32_t deviceId) {
34     mRecordingDeviceId = deviceId;
35 }
36 
setPlaybackDeviceId(int32_t deviceId)37 void LiveEffectEngine::setPlaybackDeviceId(int32_t deviceId) {
38     mPlaybackDeviceId = deviceId;
39 }
40 
isAAudioSupported()41 bool LiveEffectEngine::isAAudioSupported() {
42     oboe::AudioStreamBuilder builder;
43     return builder.isAAudioSupported();
44 }
45 
setAudioApi(oboe::AudioApi api)46 bool LiveEffectEngine::setAudioApi(oboe::AudioApi api) {
47     if (mIsEffectOn) return false;
48 
49     mAudioApi = api;
50     return true;
51 }
52 
setEffectOn(bool isOn)53 bool LiveEffectEngine::setEffectOn(bool isOn) {
54     bool success = true;
55     if (isOn != mIsEffectOn) {
56         if (isOn) {
57             success = openStreams() == oboe::Result::OK;
58             if (success) {
59                 mFullDuplexPass.start();
60                 mIsEffectOn = isOn;
61             }
62         } else {
63             mFullDuplexPass.stop();
64             /*
65             * Note: The order of events is important here.
66             * The playback stream must be closed before the recording stream. If the
67             * recording stream were to be closed first the playback stream's
68             * callback may attempt to read from the recording stream
69             * which would cause the app to crash since the recording stream would be
70             * null.
71             */
72             closeStream(mPlayStream);
73             closeStream(mRecordingStream);
74             mIsEffectOn = isOn;
75        }
76     }
77     return success;
78 }
79 
openStreams()80 oboe::Result  LiveEffectEngine::openStreams() {
81     // Note: The order of stream creation is important. We create the playback
82     // stream first, then use properties from the playback stream
83     // (e.g. sample rate) to create the recording stream. By matching the
84     // properties we should get the lowest latency path
85     oboe::AudioStreamBuilder inBuilder, outBuilder;
86     setupPlaybackStreamParameters(&outBuilder);
87     oboe::Result result = outBuilder.openStream(mPlayStream);
88     if (result != oboe::Result::OK) {
89         mSampleRate = oboe::kUnspecified;
90         return result;
91     } else {
92         mSampleRate = mPlayStream->getSampleRate();
93     }
94     warnIfNotLowLatency(mPlayStream);
95 
96     setupRecordingStreamParameters(&inBuilder);
97     result = inBuilder.openStream(mRecordingStream);
98     if (result != oboe::Result::OK) {
99         closeStream(mPlayStream);
100         return result;
101     }
102     warnIfNotLowLatency(mRecordingStream);
103 
104     mFullDuplexPass.setInputStream(mRecordingStream.get());
105     mFullDuplexPass.setOutputStream(mPlayStream.get());
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  */
setupRecordingStreamParameters(oboe::AudioStreamBuilder * builder)116 oboe::AudioStreamBuilder *LiveEffectEngine::setupRecordingStreamParameters(
117     oboe::AudioStreamBuilder *builder) {
118     // This sample uses blocking read() by setting callback to null
119     builder->setCallback(nullptr)
120         ->setDeviceId(mRecordingDeviceId)
121         ->setDirection(oboe::Direction::Input)
122         ->setSampleRate(mSampleRate)
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->setCallback(this)
136         ->setDeviceId(mPlaybackDeviceId)
137         ->setDirection(oboe::Direction::Output)
138         ->setChannelCount(mOutputChannelCount);
139 
140     return setupCommonStreamParameters(builder);
141 }
142 
143 /**
144  * Set the stream parameters which are common to both recording and playback
145  * streams.
146  * @param builder The playback or recording stream builder
147  */
setupCommonStreamParameters(oboe::AudioStreamBuilder * builder)148 oboe::AudioStreamBuilder *LiveEffectEngine::setupCommonStreamParameters(
149     oboe::AudioStreamBuilder *builder) {
150     // We request EXCLUSIVE mode since this will give us the lowest possible
151     // latency.
152     // If EXCLUSIVE mode isn't available the builder will fall back to SHARED
153     // mode.
154     builder->setAudioApi(mAudioApi)
155         ->setFormat(mFormat)
156         ->setSharingMode(oboe::SharingMode::Exclusive)
157         ->setPerformanceMode(oboe::PerformanceMode::LowLatency);
158     return builder;
159 }
160 
161 
162 /**
163  * Close the stream. AudioStream::close() is a blocking call so
164  * the application does not need to add synchronization between
165  * onAudioReady() function and the thread calling close().
166  * [the closing thread is the UI thread in this sample].
167  * @param stream the stream to close
168  */
closeStream(std::shared_ptr<oboe::AudioStream> & stream)169 void LiveEffectEngine::closeStream(std::shared_ptr<oboe::AudioStream> &stream) {
170     if (stream) {
171         oboe::Result result = stream->stop();
172         if (result != oboe::Result::OK) {
173             LOGW("Error stopping stream: %s", oboe::convertToText(result));
174         }
175         result = stream->close();
176         if (result != oboe::Result::OK) {
177             LOGE("Error closing stream: %s", oboe::convertToText(result));
178         } else {
179             LOGW("Successfully closed streams");
180         }
181         stream.reset();
182     }
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 mFullDuplexPass.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