diff options
| author | swurl <swurl@swurl.xyz> | 2025-05-31 02:33:02 -0400 |
|---|---|---|
| committer | swurl <swurl@swurl.xyz> | 2025-05-31 02:33:02 -0400 |
| commit | 6c655321e6b04b8648802e296bb5ca11d290ae68 (patch) | |
| tree | 5c906511e4a2910ce8b806bb9d763b524c28619e /externals/oboe/samples/iolib/src/main/cpp | |
| parent | c0cceff365f754f8859f4c9a0362ebdba7ea388b (diff) | |
Move dead submodules in-tree
Signed-off-by: swurl <swurl@swurl.xyz>
Diffstat (limited to 'externals/oboe/samples/iolib/src/main/cpp')
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_ |
