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