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
18 #include <memory>
19 #include "MegaDroneEngine.h"
20
21 /**
22 * Main audio engine for the MegaDrone sample. It is responsible for:
23 *
24 * - Creating the callback object which will be supplied when constructing the audio stream
25 * - Setting the CPU core IDs to which the callback thread should bind to
26 * - Creating the playback stream, including setting the callback object
27 * - Creating `Synth` which will render the audio inside the callback
28 * - Starting the playback stream
29 * - Restarting the playback stream when `restart()` is called by the callback object
30 *
31 * @param cpuIds
32 */
MegaDroneEngine(std::vector<int> cpuIds)33 MegaDroneEngine::MegaDroneEngine(std::vector<int> cpuIds) {
34 createCallback(cpuIds);
35 }
36
~MegaDroneEngine()37 MegaDroneEngine::~MegaDroneEngine() {
38 if (mStream) {
39 LOGE("MegaDroneEngine destructor was called without calling stop()."
40 "Please call stop() to ensure stream resources are not leaked.");
41 stop();
42 }
43 }
44
tap(bool isDown)45 void MegaDroneEngine::tap(bool isDown) {
46 mAudioSource->tap(isDown);
47 }
48
restart()49 void MegaDroneEngine::restart() {
50 stop();
51 start();
52 }
53 // Create the playback stream
createPlaybackStream()54 oboe::Result MegaDroneEngine::createPlaybackStream() {
55 oboe::AudioStreamBuilder builder;
56 return builder.setSharingMode(oboe::SharingMode::Exclusive)
57 ->setPerformanceMode(oboe::PerformanceMode::LowLatency)
58 ->setFormat(oboe::AudioFormat::Float)
59 ->setDataCallback(mDataCallback)
60 ->setErrorCallback(mErrorCallback)
61 ->openStream(mStream);
62 }
63
64 // Create the callback and set its thread affinity to the supplied CPU core IDs
createCallback(std::vector<int> cpuIds)65 void MegaDroneEngine::createCallback(std::vector<int> cpuIds){
66
67 mDataCallback = std::make_shared<DefaultDataCallback>();
68
69 // Create the error callback, we supply ourselves as the parent so that we can restart the stream
70 // when it's disconnected
71 mErrorCallback = std::make_shared<DefaultErrorCallback>(*this);
72
73 // Bind the audio callback to specific CPU cores as this can help avoid underruns caused by
74 // core migrations
75 mDataCallback->setCpuIds(cpuIds);
76 mDataCallback->setThreadAffinityEnabled(true);
77 }
78
start()79 bool MegaDroneEngine::start() {
80 // It is possible for a stream's device to become disconnected during stream open or between
81 // stream open and stream start.
82 // If the stream fails to start, close the old stream and try again.
83 bool didStart = false;
84 int tryCount = 0;
85 do {
86 if (tryCount > 0) {
87 usleep(20 * 1000); // Sleep between tries to give the system time to settle.
88 }
89 didStart = attemptStart();
90 } while (!didStart && tryCount++ < 3);
91 if (!didStart) {
92 LOGE("Failed at starting the stream");
93 }
94 return didStart;
95 }
96
attemptStart()97 bool MegaDroneEngine::attemptStart() {
98 auto result = createPlaybackStream();
99
100 if (result == Result::OK) {
101 // Create our synthesizer audio source using the properties of the stream
102 mAudioSource = std::make_shared<Synth>(mStream->getSampleRate(), mStream->getChannelCount());
103 mDataCallback->reset();
104 mDataCallback->setSource(std::dynamic_pointer_cast<IRenderableAudio>(mAudioSource));
105 result = mStream->start();
106 if (result == Result::OK) {
107 return true;
108 } else {
109 LOGW("Failed attempt at starting the playback stream. Error: %s", convertToText(result));
110 return false;
111 }
112 } else {
113 LOGW("Failed attempt at creating the playback stream. Error: %s", convertToText(result));
114 return false;
115 }
116 }
117
stop()118 bool MegaDroneEngine::stop() {
119 if(mStream && mStream->getState() != oboe::StreamState::Closed) {
120 mStream->stop();
121 mStream->close();
122 }
123 mStream.reset();
124 return true;
125 }
126
127