1 /*
2 * Copyright 2019 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 <android/log.h>
18
19 // parselib includes
20 #include <stream/MemInputStream.h>
21 #include <wav/WavStreamReader.h>
22
23 // local includes
24 #include "OneShotSampleSource.h"
25 #include "SimpleMultiPlayer.h"
26
27 static const char* TAG = "SimpleMultiPlayer";
28
29 using namespace oboe;
30 using namespace parselib;
31
32 namespace iolib {
33
34 constexpr int32_t kBufferSizeInBursts = 2; // Use 2 bursts as the buffer size (double buffer)
35
SimpleMultiPlayer()36 SimpleMultiPlayer::SimpleMultiPlayer()
37 : mChannelCount(0), mOutputReset(false)
38 {}
39
onAudioReady(AudioStream * oboeStream,void * audioData,int32_t numFrames)40 DataCallbackResult SimpleMultiPlayer::onAudioReady(AudioStream *oboeStream, void *audioData,
41 int32_t numFrames) {
42
43 StreamState streamState = oboeStream->getState();
44 if (streamState != StreamState::Open && streamState != StreamState::Started) {
45 __android_log_print(ANDROID_LOG_ERROR, TAG, " streamState:%d", streamState);
46 }
47 if (streamState == StreamState::Disconnected) {
48 __android_log_print(ANDROID_LOG_ERROR, TAG, " streamState::Disconnected");
49 }
50
51 memset(audioData, 0, numFrames * mChannelCount * sizeof(float));
52
53 // OneShotSampleSource* sources = mSampleSources.get();
54 for(int32_t index = 0; index < mNumSampleBuffers; index++) {
55 if (mSampleSources[index]->isPlaying()) {
56 mSampleSources[index]->mixAudio((float*)audioData, mChannelCount, numFrames);
57 }
58 }
59
60 return DataCallbackResult::Continue;
61 }
62
onErrorAfterClose(AudioStream * oboeStream,Result error)63 void SimpleMultiPlayer::onErrorAfterClose(AudioStream *oboeStream, Result error) {
64 __android_log_print(ANDROID_LOG_INFO, TAG, "==== onErrorAfterClose() error:%d", error);
65
66 resetAll();
67 if (openStream() && startStream()) {
68 mOutputReset = true;
69 }
70 }
71
onErrorBeforeClose(AudioStream *,Result error)72 void SimpleMultiPlayer::onErrorBeforeClose(AudioStream *, Result error) {
73 __android_log_print(ANDROID_LOG_INFO, TAG, "==== onErrorBeforeClose() error:%d", error);
74 }
75
openStream()76 bool SimpleMultiPlayer::openStream() {
77 __android_log_print(ANDROID_LOG_INFO, TAG, "openStream()");
78
79 // Create an audio stream
80 AudioStreamBuilder builder;
81 builder.setChannelCount(mChannelCount);
82 // we will resample source data to device rate, so take default sample rate
83 builder.setCallback(this);
84 builder.setPerformanceMode(PerformanceMode::LowLatency);
85 builder.setSharingMode(SharingMode::Exclusive);
86 builder.setSampleRateConversionQuality(SampleRateConversionQuality::Medium);
87
88 Result result = builder.openStream(mAudioStream);
89 if (result != Result::OK){
90 __android_log_print(
91 ANDROID_LOG_ERROR,
92 TAG,
93 "openStream failed. Error: %s", convertToText(result));
94 return false;
95 }
96
97 // Reduce stream latency by setting the buffer size to a multiple of the burst size
98 // Note: this will fail with ErrorUnimplemented if we are using a callback with OpenSL ES
99 // See oboe::AudioStreamBuffered::setBufferSizeInFrames
100 result = mAudioStream->setBufferSizeInFrames(
101 mAudioStream->getFramesPerBurst() * kBufferSizeInBursts);
102 if (result != Result::OK) {
103 __android_log_print(
104 ANDROID_LOG_WARN,
105 TAG,
106 "setBufferSizeInFrames failed. Error: %s", convertToText(result));
107 }
108
109 mSampleRate = mAudioStream->getSampleRate();
110
111 return true;
112 }
113
startStream()114 bool SimpleMultiPlayer::startStream() {
115 Result result = mAudioStream->requestStart();
116 if (result != Result::OK){
117 __android_log_print(
118 ANDROID_LOG_ERROR,
119 TAG,
120 "requestStart failed. Error: %s", convertToText(result));
121 return false;
122 }
123
124 return true;
125 }
126
setupAudioStream(int32_t channelCount)127 void SimpleMultiPlayer::setupAudioStream(int32_t channelCount) {
128 __android_log_print(ANDROID_LOG_INFO, TAG, "setupAudioStream()");
129 mChannelCount = channelCount;
130
131 openStream();
132 }
133
teardownAudioStream()134 void SimpleMultiPlayer::teardownAudioStream() {
135 __android_log_print(ANDROID_LOG_INFO, TAG, "teardownAudioStream()");
136 // tear down the player
137 if (mAudioStream) {
138 mAudioStream->stop();
139 mAudioStream->close();
140 mAudioStream.reset();
141 }
142 }
143
addSampleSource(SampleSource * source,SampleBuffer * buffer)144 void SimpleMultiPlayer::addSampleSource(SampleSource* source, SampleBuffer* buffer) {
145 buffer->resampleData(mSampleRate);
146
147 mSampleBuffers.push_back(buffer);
148 mSampleSources.push_back(source);
149 mNumSampleBuffers++;
150 }
151
unloadSampleData()152 void SimpleMultiPlayer::unloadSampleData() {
153 __android_log_print(ANDROID_LOG_INFO, TAG, "unloadSampleData()");
154 resetAll();
155
156 for (int32_t bufferIndex = 0; bufferIndex < mNumSampleBuffers; bufferIndex++) {
157 delete mSampleBuffers[bufferIndex];
158 delete mSampleSources[bufferIndex];
159 }
160
161 mSampleBuffers.clear();
162 mSampleSources.clear();
163
164 mNumSampleBuffers = 0;
165 }
166
triggerDown(int32_t index)167 void SimpleMultiPlayer::triggerDown(int32_t index) {
168 if (index < mNumSampleBuffers) {
169 mSampleSources[index]->setPlayMode();
170 }
171 }
172
triggerUp(int32_t index)173 void SimpleMultiPlayer::triggerUp(int32_t index) {
174 if (index < mNumSampleBuffers) {
175 mSampleSources[index]->setStopMode();
176 }
177 }
178
resetAll()179 void SimpleMultiPlayer::resetAll() {
180 for (int32_t bufferIndex = 0; bufferIndex < mNumSampleBuffers; bufferIndex++) {
181 mSampleSources[bufferIndex]->setStopMode();
182 }
183 }
184
setPan(int index,float pan)185 void SimpleMultiPlayer::setPan(int index, float pan) {
186 mSampleSources[index]->setPan(pan);
187 }
188
getPan(int index)189 float SimpleMultiPlayer::getPan(int index) {
190 return mSampleSources[index]->getPan();
191 }
192
setGain(int index,float gain)193 void SimpleMultiPlayer::setGain(int index, float gain) {
194 mSampleSources[index]->setGain(gain);
195 }
196
getGain(int index)197 float SimpleMultiPlayer::getGain(int index) {
198 return mSampleSources[index]->getGain();
199 }
200
201 }
202