xref: /aosp_15_r20/external/oboe/samples/SoundBoard/src/main/cpp/SoundBoardEngine.cpp (revision 05767d913155b055644481607e6fa1e35e2fe72c)
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