aboutsummaryrefslogtreecommitdiff
path: root/externals/oboe/samples/iolib/src/main/cpp
diff options
context:
space:
mode:
authorswurl <swurl@swurl.xyz>2025-05-31 02:33:02 -0400
committerswurl <swurl@swurl.xyz>2025-05-31 02:33:02 -0400
commit6c655321e6b04b8648802e296bb5ca11d290ae68 (patch)
tree5c906511e4a2910ce8b806bb9d763b524c28619e /externals/oboe/samples/iolib/src/main/cpp
parentc0cceff365f754f8859f4c9a0362ebdba7ea388b (diff)
Move dead submodules in-tree
Signed-off-by: swurl <swurl@swurl.xyz>
Diffstat (limited to 'externals/oboe/samples/iolib/src/main/cpp')
-rw-r--r--externals/oboe/samples/iolib/src/main/cpp/CMakeLists.txt62
-rw-r--r--externals/oboe/samples/iolib/src/main/cpp/player/DataSource.h36
-rw-r--r--externals/oboe/samples/iolib/src/main/cpp/player/OneShotSampleSource.cpp82
-rw-r--r--externals/oboe/samples/iolib/src/main/cpp/player/OneShotSampleSource.h38
-rw-r--r--externals/oboe/samples/iolib/src/main/cpp/player/SampleBuffer.cpp118
-rw-r--r--externals/oboe/samples/iolib/src/main/cpp/player/SampleBuffer.h57
-rw-r--r--externals/oboe/samples/iolib/src/main/cpp/player/SampleSource.cpp21
-rw-r--r--externals/oboe/samples/iolib/src/main/cpp/player/SampleSource.h106
-rw-r--r--externals/oboe/samples/iolib/src/main/cpp/player/SimpleMultiPlayer.cpp224
-rw-r--r--externals/oboe/samples/iolib/src/main/cpp/player/SimpleMultiPlayer.h119
10 files changed, 863 insertions, 0 deletions
diff --git a/externals/oboe/samples/iolib/src/main/cpp/CMakeLists.txt b/externals/oboe/samples/iolib/src/main/cpp/CMakeLists.txt
new file mode 100644
index 0000000000..dd8c62634f
--- /dev/null
+++ b/externals/oboe/samples/iolib/src/main/cpp/CMakeLists.txt
@@ -0,0 +1,62 @@
+# For more information about using CMake with Android Studio, read the
+# documentation: https://d.android.com/studio/projects/add-native-code.html
+
+# Sets the minimum version of CMake required to build the native library.
+cmake_minimum_required(VERSION 3.4.1)
+
+#PROJECT(wavlib C CXX)
+
+#message("CMAKE_CURRENT_LIST_DIR = " ${CMAKE_CURRENT_LIST_DIR})
+
+#message("HOME is " ${HOME})
+
+# SET(NDK "")
+#message("NDK is " ${NDK})
+
+# Set the path to the Oboe library directory
+set (OBOE_DIR ../../../../../)
+#message("OBOE_DIR = " + ${OBOE_DIR})
+
+# Pull in parselib
+set (PARSELIB_DIR ../../../../parselib)
+#message("PARSELIB_DIR = " + ${PARSELIB_DIR})
+
+# compiler flags
+# -mhard-float -D_NDK_MATH_NO_SOFTFP=1
+#SET( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mhard-float -D_NDK_MATH_NO_SOFTFP=1" )
+
+# include folders
+include_directories(
+ ${PARSELIB_DIR}/src/main/cpp
+ ${OBOE_DIR}/include
+ ${OBOE_DIR}/src/flowgraph
+ ${CMAKE_CURRENT_LIST_DIR}
+ ../../../../shared)
+
+# Creates and names a library, sets it as either STATIC
+# or SHARED, and provides the relative paths to its source code.
+# You can define multiple libraries, and CMake builds them for you.
+# Gradle automatically packages shared libraries with your APK.
+
+add_library( # Sets the name of the library.
+ iolib
+
+ # Sets the library as a static library.
+ STATIC
+
+ # source
+ ${CMAKE_CURRENT_LIST_DIR}/player/SampleSource.cpp
+ ${CMAKE_CURRENT_LIST_DIR}/player/SampleBuffer.cpp
+ ${CMAKE_CURRENT_LIST_DIR}/player/OneShotSampleSource.cpp
+ ${CMAKE_CURRENT_LIST_DIR}/player/SimpleMultiPlayer.cpp)
+
+# Specifies libraries CMake should link to your target library. You
+# can link multiple libraries, such as libraries you define in this
+# build script, prebuilt third-party libraries, or system libraries.
+
+target_link_libraries( # Specifies the target library.
+ iolib
+
+ # Links the target library to the log library
+ # included in the NDK.
+ log)
diff --git a/externals/oboe/samples/iolib/src/main/cpp/player/DataSource.h b/externals/oboe/samples/iolib/src/main/cpp/player/DataSource.h
new file mode 100644
index 0000000000..5f5cefed9e
--- /dev/null
+++ b/externals/oboe/samples/iolib/src/main/cpp/player/DataSource.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _PLAYER_AUDIOSOURCE_H_
+#define _PLAYER_AUDIOSOURCE_H_
+
+#include <cstdint>
+
+namespace iolib {
+
+/*
+ * Defines an interface for audio data sources for the SimpleMultiPlayer class.
+ */
+class DataSource {
+public:
+ virtual ~DataSource() {};
+
+ virtual void mixAudio(float* outBuff, int numChannels, int numFrames) = 0;
+};
+
+}
+
+#endif //_PLAYER_AUDIOSOURCE_H_
diff --git a/externals/oboe/samples/iolib/src/main/cpp/player/OneShotSampleSource.cpp b/externals/oboe/samples/iolib/src/main/cpp/player/OneShotSampleSource.cpp
new file mode 100644
index 0000000000..74fef60bd3
--- /dev/null
+++ b/externals/oboe/samples/iolib/src/main/cpp/player/OneShotSampleSource.cpp
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <string.h>
+
+#include "wav/WavStreamReader.h"
+
+#include "OneShotSampleSource.h"
+
+namespace iolib {
+
+void OneShotSampleSource::mixAudio(float* outBuff, int numChannels, int32_t numFrames) {
+ int32_t numSamples = mSampleBuffer->getNumSamples();
+ int32_t sampleChannels = mSampleBuffer->getProperties().channelCount;
+ int32_t totalSamplesNeeded = numFrames * numChannels; // Total samples to fill the output buffer
+ int32_t samplesProcessed = 0;
+ bool isLoopMode = mIsLoopMode;
+
+ while (samplesProcessed < totalSamplesNeeded && mIsPlaying) {
+ int32_t samplesLeft = numSamples - mCurSampleIndex;
+ int32_t framesLeft = (totalSamplesNeeded - samplesProcessed) / numChannels;
+ int32_t numWriteFrames = std::min(framesLeft, samplesLeft / sampleChannels);
+
+ if (numWriteFrames > 0) {
+ const float* data = mSampleBuffer->getSampleData();
+ if ((sampleChannels == 1) && (numChannels == 1)) {
+ // MONO output from MONO samples
+ for (int32_t frameIndex = 0; frameIndex < numWriteFrames; frameIndex++) {
+ outBuff[samplesProcessed + frameIndex] += data[mCurSampleIndex++] * mGain;
+ }
+ } else if ((sampleChannels == 1) && (numChannels == 2)) {
+ // STEREO output from MONO samples
+ int dstSampleIndex = samplesProcessed;
+ for (int32_t frameIndex = 0; frameIndex < numWriteFrames; frameIndex++) {
+ outBuff[dstSampleIndex++] += data[mCurSampleIndex] * mLeftGain;
+ outBuff[dstSampleIndex++] += data[mCurSampleIndex++] * mRightGain;
+ }
+ } else if ((sampleChannels == 2) && (numChannels == 1)) {
+ // MONO output from STEREO samples
+ int dstSampleIndex = samplesProcessed;
+ for (int32_t frameIndex = 0; frameIndex < numWriteFrames; frameIndex++) {
+ outBuff[dstSampleIndex++] += data[mCurSampleIndex++] * mLeftGain +
+ data[mCurSampleIndex++] * mRightGain;
+ }
+ } else if ((sampleChannels == 2) && (numChannels == 2)) {
+ // STEREO output from STEREO samples
+ int dstSampleIndex = samplesProcessed;
+ for (int32_t frameIndex = 0; frameIndex < numWriteFrames; frameIndex++) {
+ outBuff[dstSampleIndex++] += data[mCurSampleIndex++] * mLeftGain;
+ outBuff[dstSampleIndex++] += data[mCurSampleIndex++] * mRightGain;
+ }
+ }
+
+ samplesProcessed += numWriteFrames * numChannels;
+
+ if (mCurSampleIndex >= numSamples) {
+ if (isLoopMode) {
+ mCurSampleIndex = 0;
+ } else {
+ mIsPlaying = false;
+ }
+ }
+ } else {
+ break; // No more samples to write in the current chunk
+ }
+ }
+}
+
+} // namespace wavlib
diff --git a/externals/oboe/samples/iolib/src/main/cpp/player/OneShotSampleSource.h b/externals/oboe/samples/iolib/src/main/cpp/player/OneShotSampleSource.h
new file mode 100644
index 0000000000..a08a704538
--- /dev/null
+++ b/externals/oboe/samples/iolib/src/main/cpp/player/OneShotSampleSource.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _PLAYER_ONESHOTSAMPLESOURCE_
+#define _PLAYER_ONESHOTSAMPLESOURCE_
+
+#include "SampleSource.h"
+
+namespace iolib {
+
+/**
+ * Provides audio data which will play through ONCE when triggered
+ * Currently the sample data is assumed to be MONO
+ */
+class OneShotSampleSource: public SampleSource {
+public:
+ OneShotSampleSource(SampleBuffer *sampleBuffer, float pan) : SampleSource(sampleBuffer, pan) {};
+ virtual ~OneShotSampleSource() {};
+
+ virtual void mixAudio(float* outBuff, int numChannels, int32_t numFrames);
+};
+
+} // namespace iolib
+
+#endif //_PLAYER_ONESHOTSAMPLESOURCE_
diff --git a/externals/oboe/samples/iolib/src/main/cpp/player/SampleBuffer.cpp b/externals/oboe/samples/iolib/src/main/cpp/player/SampleBuffer.cpp
new file mode 100644
index 0000000000..f3694b3c00
--- /dev/null
+++ b/externals/oboe/samples/iolib/src/main/cpp/player/SampleBuffer.cpp
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SampleBuffer.h"
+
+// Resampler Includes
+#include <resampler/MultiChannelResampler.h>
+
+#include "wav/WavStreamReader.h"
+
+using namespace RESAMPLER_OUTER_NAMESPACE::resampler;
+
+namespace iolib {
+
+void SampleBuffer::loadSampleData(parselib::WavStreamReader* reader) {
+ mAudioProperties.channelCount = reader->getNumChannels();
+ mAudioProperties.sampleRate = reader->getSampleRate();
+
+ reader->positionToAudio();
+
+ mNumSamples = reader->getNumSampleFrames() * reader->getNumChannels();
+ mSampleData = new float[mNumSamples];
+
+ reader->getDataFloat(mSampleData, reader->getNumSampleFrames());
+}
+
+void SampleBuffer::unloadSampleData() {
+ if (mSampleData != nullptr) {
+ delete[] mSampleData;
+ mSampleData = nullptr;
+ }
+ mNumSamples = 0;
+}
+
+class ResampleBlock {
+public:
+ int32_t mSampleRate;
+ float* mBuffer;
+ int32_t mNumSamples;
+};
+
+void resampleData(const ResampleBlock& input, ResampleBlock* output, int numChannels) {
+ // Calculate output buffer size
+ double temp =
+ ((double)input.mNumSamples * (double)output->mSampleRate) / (double)input.mSampleRate;
+
+ // round up
+ int32_t numOutFramesAllocated = (int32_t)(temp + 0.5);
+ // We iterate thousands of times through the loop. Roundoff error could accumulate
+ // so add a few more frames for padding
+ numOutFramesAllocated += 8;
+
+ MultiChannelResampler *resampler = MultiChannelResampler::make(
+ numChannels, // channel count
+ input.mSampleRate, // input sampleRate
+ output->mSampleRate, // output sampleRate
+ MultiChannelResampler::Quality::Medium); // conversion quality
+
+ float *inputBuffer = input.mBuffer;; // multi-channel buffer to be consumed
+ float *outputBuffer = new float[numOutFramesAllocated]; // multi-channel buffer to be filled
+ output->mBuffer = outputBuffer;
+
+ int numOutputSamples = 0;
+ int inputSamplesLeft = input.mNumSamples;
+ while ((inputSamplesLeft > 0) && (numOutputSamples < numOutFramesAllocated)) {
+ if(resampler->isWriteNeeded()) {
+ resampler->writeNextFrame(inputBuffer);
+ inputBuffer += numChannels;
+ inputSamplesLeft -= numChannels;
+ } else {
+ resampler->readNextFrame(outputBuffer);
+ outputBuffer += numChannels;
+ numOutputSamples += numChannels;
+ }
+ }
+ output->mNumSamples = numOutputSamples;
+
+ delete resampler;
+}
+
+void SampleBuffer::resampleData(int sampleRate) {
+ if (mAudioProperties.sampleRate == sampleRate) {
+ // nothing to do
+ return;
+ }
+
+ ResampleBlock inputBlock;
+ inputBlock.mBuffer = mSampleData;
+ inputBlock.mNumSamples = mNumSamples;
+ inputBlock.mSampleRate = mAudioProperties.sampleRate;
+
+ ResampleBlock outputBlock;
+ outputBlock.mSampleRate = sampleRate;
+ iolib::resampleData(inputBlock, &outputBlock, mAudioProperties.channelCount);
+
+ // delete previous samples
+ delete[] mSampleData;
+
+ // install the resampled data
+ mSampleData = outputBlock.mBuffer;
+ mNumSamples = outputBlock.mNumSamples;
+ mAudioProperties.sampleRate = outputBlock.mSampleRate;
+}
+
+} // namespace iolib
diff --git a/externals/oboe/samples/iolib/src/main/cpp/player/SampleBuffer.h b/externals/oboe/samples/iolib/src/main/cpp/player/SampleBuffer.h
new file mode 100644
index 0000000000..7cc25c1f55
--- /dev/null
+++ b/externals/oboe/samples/iolib/src/main/cpp/player/SampleBuffer.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _PLAYER_SAMPLEBUFFER_
+#define _PLAYER_SAMPLEBUFFER_
+
+#include <wav/WavStreamReader.h>
+
+namespace iolib {
+
+/*
+ * Defines the relevant properties of the audio data being sourced.
+ */
+struct AudioProperties {
+ int32_t channelCount;
+ int32_t sampleRate;
+};
+
+class SampleBuffer {
+public:
+ SampleBuffer() : mNumSamples(0) {};
+ ~SampleBuffer() { unloadSampleData(); }
+
+ // Data load/unload
+ void loadSampleData(parselib::WavStreamReader* reader);
+ void unloadSampleData();
+
+ void resampleData(int sampleRate);
+
+ virtual AudioProperties getProperties() const { return mAudioProperties; }
+
+ float* getSampleData() { return mSampleData; }
+ int32_t getNumSamples() { return mNumSamples; }
+
+protected:
+ AudioProperties mAudioProperties;
+
+ float* mSampleData;
+ int32_t mNumSamples;
+};
+
+}
+
+#endif //_PLAYER_SAMPLEBUFFER_
diff --git a/externals/oboe/samples/iolib/src/main/cpp/player/SampleSource.cpp b/externals/oboe/samples/iolib/src/main/cpp/player/SampleSource.cpp
new file mode 100644
index 0000000000..7c53708c32
--- /dev/null
+++ b/externals/oboe/samples/iolib/src/main/cpp/player/SampleSource.cpp
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SampleSource.h"
+
+namespace iolib {
+ // for now, all methods of SampleSource are either in-line or pure virtual
+}
diff --git a/externals/oboe/samples/iolib/src/main/cpp/player/SampleSource.h b/externals/oboe/samples/iolib/src/main/cpp/player/SampleSource.h
new file mode 100644
index 0000000000..54edbf1f9b
--- /dev/null
+++ b/externals/oboe/samples/iolib/src/main/cpp/player/SampleSource.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _PLAYER_SAMPLESOURCE_
+#define _PLAYER_SAMPLESOURCE_
+
+#include <cstdint>
+
+#include "DataSource.h"
+
+#include "SampleBuffer.h"
+
+namespace iolib {
+
+/**
+ * Defines an interface for audio data provided to a player object.
+ * Concrete examples include OneShotSampleBuffer. One could imagine a LoopingSampleBuffer.
+ * Supports stereo position via mPan member.
+ */
+class SampleSource: public DataSource {
+public:
+ // Pan position of the audio in a stereo mix
+ // [left:-1.0f] <- [center: 0.0f] -> -[right: 1.0f]
+ static constexpr float PAN_HARDLEFT = -1.0f;
+ static constexpr float PAN_HARDRIGHT = 1.0f;
+ static constexpr float PAN_CENTER = 0.0f;
+
+ SampleSource(SampleBuffer *sampleBuffer, float pan)
+ : mSampleBuffer(sampleBuffer), mCurSampleIndex(0), mIsPlaying(false), mIsLoopMode(false), mGain(1.0f) {
+ setPan(pan);
+ }
+ virtual ~SampleSource() {}
+
+ void setPlayMode() { mCurSampleIndex = 0; mIsPlaying = true; }
+ void setStopMode() { mIsPlaying = false; mCurSampleIndex = 0; }
+
+ void setLoopMode(bool isLoopMode) { mIsLoopMode = isLoopMode; }
+
+ bool isPlaying() { return mIsPlaying; }
+
+ void setPan(float pan) {
+ if (pan < PAN_HARDLEFT) {
+ mPan = PAN_HARDLEFT;
+ } else if (pan > PAN_HARDRIGHT) {
+ mPan = PAN_HARDRIGHT;
+ } else {
+ mPan = pan;
+ }
+ calcGainFactors();
+ }
+
+ float getPan() {
+ return mPan;
+ }
+
+ void setGain(float gain) {
+ mGain = gain;
+ calcGainFactors();
+ }
+
+ float getGain() {
+ return mGain;
+ }
+
+protected:
+ SampleBuffer *mSampleBuffer;
+
+ int32_t mCurSampleIndex;
+
+ bool mIsPlaying;
+ std::atomic<bool> mIsLoopMode;
+
+ // Logical pan value
+ float mPan;
+
+ // precomputed channel gains for pan
+ float mLeftGain;
+ float mRightGain;
+
+ // Overall gain
+ float mGain;
+
+private:
+ void calcGainFactors() {
+ // useful panning information: http://www.cs.cmu.edu/~music/icm-online/readings/panlaws/
+ float rightPan = (mPan * 0.5) + 0.5;
+ mRightGain = rightPan * mGain;
+ mLeftGain = (1.0 - rightPan) * mGain; }
+};
+
+} // namespace wavlib
+
+#endif //_PLAYER_SAMPLESOURCE_
diff --git a/externals/oboe/samples/iolib/src/main/cpp/player/SimpleMultiPlayer.cpp b/externals/oboe/samples/iolib/src/main/cpp/player/SimpleMultiPlayer.cpp
new file mode 100644
index 0000000000..a307258773
--- /dev/null
+++ b/externals/oboe/samples/iolib/src/main/cpp/player/SimpleMultiPlayer.cpp
@@ -0,0 +1,224 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android/log.h>
+
+// parselib includes
+#include <stream/MemInputStream.h>
+#include <wav/WavStreamReader.h>
+
+// local includes
+#include "OneShotSampleSource.h"
+#include "SimpleMultiPlayer.h"
+
+static const char* TAG = "SimpleMultiPlayer";
+
+using namespace oboe;
+using namespace parselib;
+
+namespace iolib {
+
+constexpr int32_t kBufferSizeInBursts = 2; // Use 2 bursts as the buffer size (double buffer)
+
+SimpleMultiPlayer::SimpleMultiPlayer()
+ : mChannelCount(0), mOutputReset(false), mSampleRate(0), mNumSampleBuffers(0)
+{}
+
+DataCallbackResult SimpleMultiPlayer::MyDataCallback::onAudioReady(AudioStream *oboeStream,
+ void *audioData,
+ int32_t numFrames) {
+
+ StreamState streamState = oboeStream->getState();
+ if (streamState != StreamState::Open && streamState != StreamState::Started) {
+ __android_log_print(ANDROID_LOG_ERROR, TAG, " streamState:%d", streamState);
+ }
+ if (streamState == StreamState::Disconnected) {
+ __android_log_print(ANDROID_LOG_ERROR, TAG, " streamState::Disconnected");
+ }
+
+ memset(audioData, 0, static_cast<size_t>(numFrames) * static_cast<size_t>
+ (mParent->mChannelCount) * sizeof(float));
+
+ // OneShotSampleSource* sources = mSampleSources.get();
+ for(int32_t index = 0; index < mParent->mNumSampleBuffers; index++) {
+ if (mParent->mSampleSources[index]->isPlaying()) {
+ mParent->mSampleSources[index]->mixAudio((float*)audioData, mParent->mChannelCount,
+ numFrames);
+ }
+ }
+
+ return DataCallbackResult::Continue;
+}
+
+void SimpleMultiPlayer::MyErrorCallback::onErrorAfterClose(AudioStream *oboeStream, Result error) {
+ __android_log_print(ANDROID_LOG_INFO, TAG, "==== onErrorAfterClose() error:%d", error);
+
+ mParent->resetAll();
+ if (mParent->openStream() && mParent->startStream()) {
+ mParent->mOutputReset = true;
+ }
+}
+
+bool SimpleMultiPlayer::openStream() {
+ __android_log_print(ANDROID_LOG_INFO, TAG, "openStream()");
+
+ // Use shared_ptr to prevent use of a deleted callback.
+ mDataCallback = std::make_shared<MyDataCallback>(this);
+ mErrorCallback = std::make_shared<MyErrorCallback>(this);
+
+ // Create an audio stream
+ AudioStreamBuilder builder;
+ builder.setChannelCount(mChannelCount);
+ // we will resample source data to device rate, so take default sample rate
+ builder.setDataCallback(mDataCallback);
+ builder.setErrorCallback(mErrorCallback);
+ builder.setPerformanceMode(PerformanceMode::LowLatency);
+ builder.setSharingMode(SharingMode::Exclusive);
+ builder.setSampleRateConversionQuality(SampleRateConversionQuality::Medium);
+
+ Result result = builder.openStream(mAudioStream);
+ if (result != Result::OK){
+ __android_log_print(
+ ANDROID_LOG_ERROR,
+ TAG,
+ "openStream failed. Error: %s", convertToText(result));
+ return false;
+ }
+
+ // Reduce stream latency by setting the buffer size to a multiple of the burst size
+ // Note: this will fail with ErrorUnimplemented if we are using a callback with OpenSL ES
+ // See oboe::AudioStreamBuffered::setBufferSizeInFrames
+ result = mAudioStream->setBufferSizeInFrames(
+ mAudioStream->getFramesPerBurst() * kBufferSizeInBursts);
+ if (result != Result::OK) {
+ __android_log_print(
+ ANDROID_LOG_WARN,
+ TAG,
+ "setBufferSizeInFrames failed. Error: %s", convertToText(result));
+ }
+
+ mSampleRate = mAudioStream->getSampleRate();
+
+ return true;
+}
+
+bool SimpleMultiPlayer::startStream() {
+ int tryCount = 0;
+ while (tryCount < 3) {
+ bool wasOpenSuccessful = true;
+ // Assume that openStream() was called successfully before startStream() call.
+ if (tryCount > 0) {
+ usleep(20 * 1000); // Sleep between tries to give the system time to settle.
+ wasOpenSuccessful = openStream(); // Try to open the stream again after the first try.
+ }
+ if (wasOpenSuccessful) {
+ Result result = mAudioStream->requestStart();
+ if (result != Result::OK){
+ __android_log_print(
+ ANDROID_LOG_ERROR,
+ TAG,
+ "requestStart failed. Error: %s", convertToText(result));
+ mAudioStream->close();
+ mAudioStream.reset();
+ } else {
+ return true;
+ }
+ }
+ tryCount++;
+ }
+
+ return false;
+}
+
+void SimpleMultiPlayer::setupAudioStream(int32_t channelCount) {
+ __android_log_print(ANDROID_LOG_INFO, TAG, "setupAudioStream()");
+ mChannelCount = channelCount;
+
+ openStream();
+}
+
+void SimpleMultiPlayer::teardownAudioStream() {
+ __android_log_print(ANDROID_LOG_INFO, TAG, "teardownAudioStream()");
+ // tear down the player
+ if (mAudioStream) {
+ mAudioStream->stop();
+ mAudioStream->close();
+ mAudioStream.reset();
+ }
+}
+
+void SimpleMultiPlayer::addSampleSource(SampleSource* source, SampleBuffer* buffer) {
+ buffer->resampleData(mSampleRate);
+
+ mSampleBuffers.push_back(buffer);
+ mSampleSources.push_back(source);
+ mNumSampleBuffers++;
+}
+
+void SimpleMultiPlayer::unloadSampleData() {
+ __android_log_print(ANDROID_LOG_INFO, TAG, "unloadSampleData()");
+ resetAll();
+
+ for (int32_t bufferIndex = 0; bufferIndex < mNumSampleBuffers; bufferIndex++) {
+ delete mSampleBuffers[bufferIndex];
+ delete mSampleSources[bufferIndex];
+ }
+
+ mSampleBuffers.clear();
+ mSampleSources.clear();
+
+ mNumSampleBuffers = 0;
+}
+
+void SimpleMultiPlayer::triggerDown(int32_t index) {
+ if (index < mNumSampleBuffers) {
+ mSampleSources[index]->setPlayMode();
+ }
+}
+
+void SimpleMultiPlayer::triggerUp(int32_t index) {
+ if (index < mNumSampleBuffers) {
+ mSampleSources[index]->setStopMode();
+ }
+}
+
+void SimpleMultiPlayer::resetAll() {
+ for (int32_t bufferIndex = 0; bufferIndex < mNumSampleBuffers; bufferIndex++) {
+ mSampleSources[bufferIndex]->setStopMode();
+ }
+}
+
+void SimpleMultiPlayer::setPan(int index, float pan) {
+ mSampleSources[index]->setPan(pan);
+}
+
+float SimpleMultiPlayer::getPan(int index) {
+ return mSampleSources[index]->getPan();
+}
+
+void SimpleMultiPlayer::setGain(int index, float gain) {
+ mSampleSources[index]->setGain(gain);
+}
+
+float SimpleMultiPlayer::getGain(int index) {
+ return mSampleSources[index]->getGain();
+}
+
+void SimpleMultiPlayer::setLoopMode(int index, bool isLoopMode) {
+ mSampleSources[index]->setLoopMode(isLoopMode);
+}
+
+}
diff --git a/externals/oboe/samples/iolib/src/main/cpp/player/SimpleMultiPlayer.h b/externals/oboe/samples/iolib/src/main/cpp/player/SimpleMultiPlayer.h
new file mode 100644
index 0000000000..43a6dd6cdb
--- /dev/null
+++ b/externals/oboe/samples/iolib/src/main/cpp/player/SimpleMultiPlayer.h
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _PLAYER_SIMPLEMULTIPLAYER_H_
+#define _PLAYER_SIMPLEMULTIPLAYER_H_
+
+#include <vector>
+
+#include <oboe/Oboe.h>
+
+#include "OneShotSampleSource.h"
+#include "SampleBuffer.h"
+
+namespace iolib {
+
+/**
+ * A simple streaming player for multiple SampleBuffers.
+ */
+class SimpleMultiPlayer {
+public:
+ SimpleMultiPlayer();
+
+ void setupAudioStream(int32_t channelCount);
+ void teardownAudioStream();
+
+ bool openStream();
+ bool startStream();
+
+ int getSampleRate() { return mSampleRate; }
+
+ // Wave Sample Loading...
+ /**
+ * Adds the SampleSource/SampleBuffer pair to the list of source channels.
+ * Transfers ownership of those objects so that they can be deleted/unloaded.
+ * The indexes associated with each source channel is the order in which they
+ * are added.
+ */
+ void addSampleSource(SampleSource* source, SampleBuffer* buffer);
+ /**
+ * Deallocates and deletes all added source/buffer (see addSampleSource()).
+ */
+ void unloadSampleData();
+
+ void triggerDown(int32_t index);
+ void triggerUp(int32_t index);
+
+ void resetAll();
+
+ bool getOutputReset() { return mOutputReset; }
+ void clearOutputReset() { mOutputReset = false; }
+
+ void setPan(int index, float pan);
+ float getPan(int index);
+
+ void setGain(int index, float gain);
+ float getGain(int index);
+
+ void setLoopMode(int index, bool isLoopMode);
+
+private:
+ class MyDataCallback : public oboe::AudioStreamDataCallback {
+ public:
+ MyDataCallback(SimpleMultiPlayer *parent) : mParent(parent) {}
+
+ oboe::DataCallbackResult onAudioReady(
+ oboe::AudioStream *audioStream,
+ void *audioData,
+ int32_t numFrames) override;
+
+ private:
+ SimpleMultiPlayer *mParent;
+ };
+
+ class MyErrorCallback : public oboe::AudioStreamErrorCallback {
+ public:
+ MyErrorCallback(SimpleMultiPlayer *parent) : mParent(parent) {}
+
+ virtual ~MyErrorCallback() {
+ }
+
+ void onErrorAfterClose(oboe::AudioStream *oboeStream, oboe::Result error) override;
+
+ private:
+ SimpleMultiPlayer *mParent;
+ };
+
+ // Oboe Audio Stream
+ std::shared_ptr<oboe::AudioStream> mAudioStream;
+
+ // Playback Audio attributes
+ int32_t mChannelCount;
+ int32_t mSampleRate;
+
+ // Sample Data
+ int32_t mNumSampleBuffers;
+ std::vector<SampleBuffer*> mSampleBuffers;
+ std::vector<SampleSource*> mSampleSources;
+
+ bool mOutputReset;
+
+ std::shared_ptr<MyDataCallback> mDataCallback;
+ std::shared_ptr<MyErrorCallback> mErrorCallback;
+};
+
+}
+#endif //_PLAYER_SIMPLEMULTIPLAYER_H_