• 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 
isAAudioSupported()34 bool LiveEffectEngine::isAAudioSupported() {
35     oboe::AudioStreamBuilder builder;
36     return builder.isAAudioSupported();
37 }
38 
setAudioApi(oboe::AudioApi api)39 bool LiveEffectEngine::setAudioApi(oboe::AudioApi api) {
40     if (mIsEffectOn) return false;
41 
42     mAudioApi = api;
43     return true;
44 }
45 
setEffectOn(bool isOn)46 bool LiveEffectEngine::setEffectOn(bool isOn) {
47     bool success = true;
48     if (isOn != mIsEffectOn) {
49         if (isOn) {
50             success = openStreams() == oboe::Result::OK;
51             if (success) {
52                 mFullDuplexPass.start();
53                 mIsEffectOn = isOn;
54             }
55         } else {
56             mFullDuplexPass.stop();
57             closeStreams();
58             mIsEffectOn = isOn;
59        }
60     }
61     return success;
62 }
63 
closeStreams()64 void LiveEffectEngine::closeStreams() {
65     /*
66     * Note: The order of events is important here.
67     * The playback stream must be closed before the recording stream. If the
68     * recording stream were to be closed first the playback stream's
69     * callback may attempt to read from the recording stream
70     * which would cause the app to crash since the recording stream would be
71     * null.
72     */
73     closeStream(mPlayStream);
74     mFullDuplexPass.setOutputStream(nullptr);
75 
76     closeStream(mRecordingStream);
77     mFullDuplexPass.setInputStream(nullptr);
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         // The input stream needs to run at the same sample rate as the output.
93         mSampleRate = mPlayStream->getSampleRate();
94     }
95     warnIfNotLowLatency(mPlayStream);
96 
97     setupRecordingStreamParameters(&inBuilder, mSampleRate);
98     result = inBuilder.openStream(mRecordingStream);
99     if (result != oboe::Result::OK) {
100         closeStream(mPlayStream);
101         return result;
102     }
103     warnIfNotLowLatency(mRecordingStream);
104 
105     mFullDuplexPass.setInputStream(mRecordingStream);
106     mFullDuplexPass.setOutputStream(mPlayStream);
107     return result;
108 }
109 
110 /**
111  * Sets the stream parameters which are specific to recording,
112  * including the sample rate which is determined from the
113  * playback stream.
114  *
115  * @param builder The recording stream builder
116  * @param sampleRate The desired sample rate of the recording stream
117  */
setupRecordingStreamParameters(oboe::AudioStreamBuilder * builder,int32_t sampleRate)118 oboe::AudioStreamBuilder *LiveEffectEngine::setupRecordingStreamParameters(
119     oboe::AudioStreamBuilder *builder, int32_t sampleRate) {
120     // This sample uses blocking read() because we don't specify a callback
121     builder->setDeviceId(mRecordingDeviceId)
122         ->setDirection(oboe::Direction::Input)
123         ->setSampleRate(sampleRate)
124         ->setChannelCount(mInputChannelCount);
125     return setupCommonStreamParameters(builder);
126 }
127 
128 /**
129  * Sets the stream parameters which are specific to playback, including device
130  * id and the dataCallback function, which must be set for low latency
131  * playback.
132  * @param builder The playback stream builder
133  */
setupPlaybackStreamParameters(oboe::AudioStreamBuilder * builder)134 oboe::AudioStreamBuilder *LiveEffectEngine::setupPlaybackStreamParameters(
135     oboe::AudioStreamBuilder *builder) {
136     builder->setDataCallback(this)
137         ->setErrorCallback(this)
138         ->setDeviceId(mPlaybackDeviceId)
139         ->setDirection(oboe::Direction::Output)
140         ->setChannelCount(mOutputChannelCount);
141 
142     return setupCommonStreamParameters(builder);
143 }
144 
145 /**
146  * Set the stream parameters which are common to both recording and playback
147  * streams.
148  * @param builder The playback or recording stream builder
149  */
setupCommonStreamParameters(oboe::AudioStreamBuilder * builder)150 oboe::AudioStreamBuilder *LiveEffectEngine::setupCommonStreamParameters(
151     oboe::AudioStreamBuilder *builder) {
152     // We request EXCLUSIVE mode since this will give us the lowest possible
153     // latency.
154     // If EXCLUSIVE mode isn't available the builder will fall back to SHARED
155     // mode.
156     builder->setAudioApi(mAudioApi)
157         ->setFormat(mFormat)
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 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