diff --git a/modules/juce_audio_devices/native/oboe/include/oboe/AudioStream.h b/modules/juce_audio_devices/native/oboe/include/oboe/AudioStream.h index d9b2046a3d..a158aafcb6 100644 --- a/modules/juce_audio_devices/native/oboe/include/oboe/AudioStream.h +++ b/modules/juce_audio_devices/native/oboe/include/oboe/AudioStream.h @@ -42,6 +42,7 @@ constexpr int64_t kDefaultTimeoutNanos = (2000 * kNanosPerMillisecond); * Base class for Oboe C++ audio stream. */ class AudioStream : public AudioStreamBase { + friend class AudioStreamBuilder; // allow access to setWeakThis() and lockWeakThis() public: AudioStream() {} @@ -479,6 +480,23 @@ protected: mDataCallbackEnabled = enabled; } + /* + * Set a weak_ptr to this stream from the shared_ptr so that we can + * later use a shared_ptr in the error callback. + */ + void setWeakThis(std::shared_ptr &sharedStream) { + mWeakThis = sharedStream; + } + + /* + * Make a shared_ptr that will prevent this stream from being deleted. + */ + std::shared_ptr lockWeakThis() { + return mWeakThis.lock(); + } + + std::weak_ptr mWeakThis; // weak pointer to this object + /** * Number of frames which have been written into the stream * @@ -497,17 +515,19 @@ protected: std::mutex mLock; // for synchronizing start/stop/close + private: + // Log the scheduler if it changes. + void checkScheduler(); int mPreviousScheduler = -1; std::atomic mDataCallbackEnabled{false}; std::atomic mErrorCallbackCalled{false}; - }; /** - * This struct is a stateless functor which closes a audiostream prior to its deletion. + * This struct is a stateless functor which closes an AudioStream prior to its deletion. * This means it can be used to safely delete a smart pointer referring to an open stream. */ struct StreamDeleterFunctor { diff --git a/modules/juce_audio_devices/native/oboe/include/oboe/AudioStreamBase.h b/modules/juce_audio_devices/native/oboe/include/oboe/AudioStreamBase.h index fd327e6e87..4a6237dfd5 100644 --- a/modules/juce_audio_devices/native/oboe/include/oboe/AudioStreamBase.h +++ b/modules/juce_audio_devices/native/oboe/include/oboe/AudioStreamBase.h @@ -27,6 +27,7 @@ namespace oboe { * Base class containing parameters for audio streams and builders. **/ class AudioStreamBase { + public: AudioStreamBase() {} diff --git a/modules/juce_audio_devices/native/oboe/include/oboe/AudioStreamBuilder.h b/modules/juce_audio_devices/native/oboe/include/oboe/AudioStreamBuilder.h index b0f7de8e7c..a1ea0a851e 100644 --- a/modules/juce_audio_devices/native/oboe/include/oboe/AudioStreamBuilder.h +++ b/modules/juce_audio_devices/native/oboe/include/oboe/AudioStreamBuilder.h @@ -19,12 +19,14 @@ #include "oboe/Definitions.h" #include "oboe/AudioStreamBase.h" +#include "ResultWithValue.h" namespace oboe { // This depends on AudioStream, so we use forward declaration, it will close and delete the stream struct StreamDeleterFunctor; using ManagedStream = std::unique_ptr; + /** * Factory class for an audio Stream. */ @@ -382,11 +384,23 @@ public: * * The caller owns the pointer to the AudioStream object. * + * @deprecated Use openStream(std::shared_ptr &stream) instead. * @param stream pointer to a variable to receive the stream address * @return OBOE_OK if successful or a negative error code */ Result openStream(AudioStream **stream); + /** + * Create and open a stream object based on the current settings. + * + * The caller shares the pointer to the AudioStream object. + * The shared_ptr is used internally by Oboe to prevent the stream from being + * deleted while it is being used by callbacks. + * + * @param stream reference to a shared_ptr to receive the stream address + * @return OBOE_OK if successful or a negative error code + */ + Result openStream(std::shared_ptr &stream); /** * Create and open a ManagedStream object based on the current builder state. diff --git a/modules/juce_audio_devices/native/oboe/include/oboe/Definitions.h b/modules/juce_audio_devices/native/oboe/include/oboe/Definitions.h index 04c1b68286..8063a83c96 100644 --- a/modules/juce_audio_devices/native/oboe/include/oboe/Definitions.h +++ b/modules/juce_audio_devices/native/oboe/include/oboe/Definitions.h @@ -495,6 +495,25 @@ namespace oboe { int64_t timestamp; // in nanoseconds }; + class OboeGlobals { + public: + + static bool areWorkaroundsEnabled() { + return mWorkaroundsEnabled; + } + + /** + * Disable this when writing tests to reproduce bugs in AAudio or OpenSL ES + * that have workarounds in Oboe. + * @param enabled + */ + static void setWorkaroundsEnabled(bool enabled) { + mWorkaroundsEnabled = enabled; + } + + private: + static bool mWorkaroundsEnabled; + }; } // namespace oboe #endif // OBOE_DEFINITIONS_H diff --git a/modules/juce_audio_devices/native/oboe/include/oboe/Utilities.h b/modules/juce_audio_devices/native/oboe/include/oboe/Utilities.h index 72389917ce..2c270881c5 100644 --- a/modules/juce_audio_devices/native/oboe/include/oboe/Utilities.h +++ b/modules/juce_audio_devices/native/oboe/include/oboe/Utilities.h @@ -19,6 +19,7 @@ #include #include +#include #include "oboe/Definitions.h" namespace oboe { @@ -58,6 +59,19 @@ int32_t convertFormatToSizeInBytes(AudioFormat format); template const char * convertToText(FromType input); +/** + * @param name + * @return the value of a named system property in a string or empty string + */ +std::string getPropertyString(const char * name); + +/** + * @param name + * @param defaultValue + * @return integer value associated with a property or the default value + */ +int getPropertyInteger(const char * name, int defaultValue); + /** * Return the version of the SDK that is currently running. * diff --git a/modules/juce_audio_devices/native/oboe/include/oboe/Version.h b/modules/juce_audio_devices/native/oboe/include/oboe/Version.h index 24c836da6c..bdc12e4a76 100644 --- a/modules/juce_audio_devices/native/oboe/include/oboe/Version.h +++ b/modules/juce_audio_devices/native/oboe/include/oboe/Version.h @@ -34,10 +34,10 @@ #define OBOE_VERSION_MAJOR 1 // Type: 8-bit unsigned int. Min value: 0 Max value: 255. See below for description. -#define OBOE_VERSION_MINOR 2 +#define OBOE_VERSION_MINOR 4 // Type: 16-bit unsigned int. Min value: 0 Max value: 65535. See below for description. -#define OBOE_VERSION_PATCH 4 +#define OBOE_VERSION_PATCH 2 #define OBOE_STRINGIFY(x) #x #define OBOE_TOSTRING(x) OBOE_STRINGIFY(x) diff --git a/modules/juce_audio_devices/native/oboe/readme.md b/modules/juce_audio_devices/native/oboe/readme.md index ac8c98e50d..5446d4b8b2 100644 --- a/modules/juce_audio_devices/native/oboe/readme.md +++ b/modules/juce_audio_devices/native/oboe/readme.md @@ -1,7 +1,7 @@ The files in this directory are reproduced from the official Oboe repository, which can be found at github.com/google/oboe. -These files are from tag 1.3 (5c42747). +These files are from tag 1.4.2 (55304d7). We've included only those parts of the original repository which are required to build the Oboe library. Documentation, samples, tests, and other non-library items have been omitted. diff --git a/modules/juce_audio_devices/native/oboe/src/aaudio/AudioStreamAAudio.cpp b/modules/juce_audio_devices/native/oboe/src/aaudio/AudioStreamAAudio.cpp index 33091ad5ad..52d85b9baa 100644 --- a/modules/juce_audio_devices/native/oboe/src/aaudio/AudioStreamAAudio.cpp +++ b/modules/juce_audio_devices/native/oboe/src/aaudio/AudioStreamAAudio.cpp @@ -26,6 +26,8 @@ #ifdef __ANDROID__ #include +#include + #endif #ifndef OBOE_FIX_FORCE_STARTING_TO_STARTED @@ -72,6 +74,15 @@ static void oboe_aaudio_error_thread_proc(AudioStreamAAudio *oboeStream, LOGD("%s() - exiting <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<", __func__); } +// This runs in its own thread. +// Only one of these threads will be launched from internalErrorCallback(). +// Prevents deletion of the stream if the app is using AudioStreamBuilder::openSharedStream() +static void oboe_aaudio_error_thread_proc_shared(std::shared_ptr sharedStream, + Result error) { + AudioStreamAAudio *oboeStream = reinterpret_cast(sharedStream.get()); + oboe_aaudio_error_thread_proc(oboeStream, error); +} + namespace oboe { /* @@ -81,7 +92,6 @@ AudioStreamAAudio::AudioStreamAAudio(const AudioStreamBuilder &builder) : AudioStream(builder) , mAAudioStream(nullptr) { mCallbackThreadEnabled.store(false); - LOGD("AudioStreamAAudio() call isSupported()"); isSupported(); } @@ -99,12 +109,21 @@ void AudioStreamAAudio::internalErrorCallback( void *userData, aaudio_result_t error) { AudioStreamAAudio *oboeStream = reinterpret_cast(userData); + + // Prevents deletion of the stream if the app is using AudioStreamBuilder::openSharedStream() + std::shared_ptr sharedStream = oboeStream->lockWeakThis(); + // These checks should be enough because we assume that the stream close() // will join() any active callback threads and will not allow new callbacks. if (oboeStream->wasErrorCallbackCalled()) { // block extra error callbacks LOGE("%s() multiple error callbacks called!", __func__); } else if (stream != oboeStream->getUnderlyingStream()) { - LOGD("%s() stream already closed", __func__); // can happen if there are bugs + LOGW("%s() stream already closed", __func__); // can happen if there are bugs + } else if (sharedStream) { + // Handle error on a separate thread using shared pointer. + std::thread t(oboe_aaudio_error_thread_proc_shared, sharedStream, + static_cast(error)); + t.detach(); } else { // Handle error on a separate thread. std::thread t(oboe_aaudio_error_thread_proc, oboeStream, @@ -161,7 +180,8 @@ Result AudioStreamAAudio::open() { // does not increase latency. int32_t capacity = mBufferCapacityInFrames; constexpr int kCapacityRequiredForFastLegacyTrack = 4096; // matches value in AudioFinger - if (mDirection == oboe::Direction::Input + if (OboeGlobals::areWorkaroundsEnabled() + && mDirection == oboe::Direction::Input && capacity != oboe::Unspecified && capacity < kCapacityRequiredForFastLegacyTrack && mPerformanceMode == oboe::PerformanceMode::LowLatency) { @@ -250,15 +270,14 @@ Result AudioStreamAAudio::open() { mSessionId = SessionId::None; } - LOGD("AudioStreamAAudio.open() app format = %d", static_cast(mFormat)); - LOGD("AudioStreamAAudio.open() sample rate = %d", static_cast(mSampleRate)); - LOGD("AudioStreamAAudio.open() capacity = %d", static_cast(mBufferCapacityInFrames)); + LOGD("AudioStreamAAudio.open() format=%d, sampleRate=%d, capacity = %d", + static_cast(mFormat), static_cast(mSampleRate), + static_cast(mBufferCapacityInFrames)); error2: mLibLoader->builder_delete(aaudioBuilder); - LOGD("AudioStreamAAudio.open: AAudioStream_Open() returned %s, mAAudioStream = %p", - mLibLoader->convertResultToText(static_cast(result)), - mAAudioStream.load()); + LOGD("AudioStreamAAudio.open: AAudioStream_Open() returned %s", + mLibLoader->convertResultToText(static_cast(result))); return result; } @@ -288,7 +307,7 @@ DataCallbackResult AudioStreamAAudio::callOnAudioReady(AAudioStream *stream, return result; } else { if (result == DataCallbackResult::Stop) { - LOGE("Oboe callback returned DataCallbackResult::Stop"); + LOGD("Oboe callback returned DataCallbackResult::Stop"); } else { LOGE("Oboe callback returned unexpected value = %d", result); } @@ -445,7 +464,8 @@ Result AudioStreamAAudio::waitForStateChange(StreamState currentState, break; } #if OBOE_FIX_FORCE_STARTING_TO_STARTED - if (aaudioNextState == static_cast(StreamState::Starting)) { + if (OboeGlobals::areWorkaroundsEnabled() + && aaudioNextState == static_cast(StreamState::Starting)) { aaudioNextState = static_cast(StreamState::Started); } #endif // OBOE_FIX_FORCE_STARTING_TO_STARTED @@ -481,11 +501,13 @@ ResultWithValue AudioStreamAAudio::setBufferSizeInFrames(int32_t reques AAudioStream *stream = mAAudioStream.load(); if (stream != nullptr) { - - if (requestedFrames > mBufferCapacityInFrames) { - requestedFrames = mBufferCapacityInFrames; + int32_t adjustedFrames = requestedFrames; + if (adjustedFrames > mBufferCapacityInFrames) { + adjustedFrames = mBufferCapacityInFrames; } - int32_t newBufferSize = mLibLoader->stream_setBufferSize(mAAudioStream, requestedFrames); + adjustedFrames = QuirksManager::getInstance().clipBufferSize(*this, adjustedFrames); + + int32_t newBufferSize = mLibLoader->stream_setBufferSize(mAAudioStream, adjustedFrames); // Cache the result if it's valid if (newBufferSize > 0) mBufferSizeInFrames = newBufferSize; @@ -502,7 +524,8 @@ StreamState AudioStreamAAudio::getState() const { if (stream != nullptr) { aaudio_stream_state_t aaudioState = mLibLoader->stream_getState(stream); #if OBOE_FIX_FORCE_STARTING_TO_STARTED - if (aaudioState == AAUDIO_STREAM_STATE_STARTING) { + if (OboeGlobals::areWorkaroundsEnabled() + && aaudioState == AAUDIO_STREAM_STATE_STARTING) { aaudioState = AAUDIO_STREAM_STATE_STARTED; } #endif // OBOE_FIX_FORCE_STARTING_TO_STARTED diff --git a/modules/juce_audio_devices/native/oboe/src/aaudio/AudioStreamAAudio.h b/modules/juce_audio_devices/native/oboe/src/aaudio/AudioStreamAAudio.h index 2a04396271..6267328ba2 100644 --- a/modules/juce_audio_devices/native/oboe/src/aaudio/AudioStreamAAudio.h +++ b/modules/juce_audio_devices/native/oboe/src/aaudio/AudioStreamAAudio.h @@ -91,6 +91,8 @@ public: void *audioData, int32_t numFrames); + bool isMMapUsed(); + protected: static void internalErrorCallback( AAudioStream *stream, @@ -108,8 +110,6 @@ protected: private: - bool isMMapUsed(); - std::atomic mCallbackThreadEnabled; // pointer to the underlying AAudio stream, valid if open, null if closed diff --git a/modules/juce_audio_devices/native/oboe/src/common/AudioStream.cpp b/modules/juce_audio_devices/native/oboe/src/common/AudioStream.cpp index f0fd2f238a..7bcd087c2c 100644 --- a/modules/juce_audio_devices/native/oboe/src/common/AudioStream.cpp +++ b/modules/juce_audio_devices/native/oboe/src/common/AudioStream.cpp @@ -39,23 +39,25 @@ Result AudioStream::close() { return Result::OK; } +// Call this from fireDataCallback() if you want to monitor CPU scheduler. +void AudioStream::checkScheduler() { + int scheduler = sched_getscheduler(0) & ~SCHED_RESET_ON_FORK; // for current thread + if (scheduler != mPreviousScheduler) { + LOGD("AudioStream::%s() scheduler = %s", __func__, + ((scheduler == SCHED_FIFO) ? "SCHED_FIFO" : + ((scheduler == SCHED_OTHER) ? "SCHED_OTHER" : + ((scheduler == SCHED_RR) ? "SCHED_RR" : "UNKNOWN"))) + ); + mPreviousScheduler = scheduler; + } +} + DataCallbackResult AudioStream::fireDataCallback(void *audioData, int32_t numFrames) { if (!isDataCallbackEnabled()) { LOGW("AudioStream::%s() called with data callback disabled!", __func__); return DataCallbackResult::Stop; // We should not be getting called any more. } - // TODO remove - int scheduler = sched_getscheduler(0) & ~SCHED_RESET_ON_FORK; // for current thread - if (scheduler != mPreviousScheduler) { - LOGD("AudioStream::%s() scheduler = %s", __func__, - ((scheduler == SCHED_FIFO) ? "SCHED_FIFO" : - ((scheduler == SCHED_OTHER) ? "SCHED_OTHER" : - ((scheduler == SCHED_RR) ? "SCHED_RR" : "UNKNOWN"))) - ); - mPreviousScheduler = scheduler; - } - DataCallbackResult result; if (mStreamCallback == nullptr) { result = onDefaultCallback(audioData, numFrames); @@ -195,11 +197,9 @@ ResultWithValue AudioStream::getTimestamp(clockid_t clockId) { } static void oboe_stop_thread_proc(AudioStream *oboeStream) { - LOGD("%s() called ----)))))", __func__); if (oboeStream != nullptr) { oboeStream->requestStop(); } - LOGD("%s() returning (((((----", __func__); } void AudioStream::launchStopThread() { diff --git a/modules/juce_audio_devices/native/oboe/src/common/AudioStreamBuilder.cpp b/modules/juce_audio_devices/native/oboe/src/common/AudioStreamBuilder.cpp index 401beea279..9f1e31b512 100644 --- a/modules/juce_audio_devices/native/oboe/src/common/AudioStreamBuilder.cpp +++ b/modules/juce_audio_devices/native/oboe/src/common/AudioStreamBuilder.cpp @@ -26,6 +26,8 @@ #include "opensles/AudioStreamOpenSLES.h" #include "QuirksManager.h" +bool oboe::OboeGlobals::mWorkaroundsEnabled = true; + namespace oboe { /** @@ -85,7 +87,7 @@ bool AudioStreamBuilder::isCompatible(AudioStreamBase &other) { Result AudioStreamBuilder::openStream(AudioStream **streamPP) { Result result = Result::OK; - LOGD("%s() %s -------- %s --------", + LOGI("%s() %s -------- %s --------", __func__, getDirection() == Direction::Input ? "INPUT" : "OUTPUT", getVersionText()); if (streamPP == nullptr) { @@ -126,7 +128,7 @@ Result AudioStreamBuilder::openStream(AudioStream **streamPP) { } // Use childStream in a FilterAudioStream. - LOGD("%s() create a FilterAudioStream for data conversion.", __func__); + LOGI("%s() create a FilterAudioStream for data conversion.", __func__); FilterAudioStream *filterStream = new FilterAudioStream(parentBuilder, tempStream); result = filterStream->configureFlowGraph(); if (result != Result::OK) { @@ -184,4 +186,16 @@ Result AudioStreamBuilder::openManagedStream(oboe::ManagedStream &stream) { return result; } -} // namespace oboe \ No newline at end of file +Result AudioStreamBuilder::openStream(std::shared_ptr &sharedStream) { + sharedStream.reset(); + AudioStream *streamptr; + auto result = openStream(&streamptr); + if (result == Result::OK) { + sharedStream.reset(streamptr); + // Save a weak_ptr in the stream for use with callbacks. + streamptr->setWeakThis(sharedStream); + } + return result; +} + +} // namespace oboe diff --git a/modules/juce_audio_devices/native/oboe/src/common/DataConversionFlowGraph.cpp b/modules/juce_audio_devices/native/oboe/src/common/DataConversionFlowGraph.cpp index 98536a9a61..c15e92d253 100644 --- a/modules/juce_audio_devices/native/oboe/src/common/DataConversionFlowGraph.cpp +++ b/modules/juce_audio_devices/native/oboe/src/common/DataConversionFlowGraph.cpp @@ -75,6 +75,7 @@ Result DataConversionFlowGraph::configure(AudioStream *sourceStream, AudioStream FlowGraphPortFloatOutput *lastOutput = nullptr; bool isOutput = sourceStream->getDirection() == Direction::Output; + bool isInput = !isOutput; mFilterStream = isOutput ? sourceStream : sinkStream; AudioFormat sourceFormat = sourceStream->getFormat(); @@ -85,7 +86,7 @@ Result DataConversionFlowGraph::configure(AudioStream *sourceStream, AudioStream int32_t sinkChannelCount = sinkStream->getChannelCount(); int32_t sinkSampleRate = sinkStream->getSampleRate(); - LOGD("%s() flowgraph converts channels: %d to %d, format: %d to %d, rate: %d to %d, qual = %d", + LOGI("%s() flowgraph converts channels: %d to %d, format: %d to %d, rate: %d to %d, qual = %d", __func__, sourceChannelCount, sinkChannelCount, sourceFormat, sinkFormat, @@ -96,8 +97,10 @@ Result DataConversionFlowGraph::configure(AudioStream *sourceStream, AudioStream ? sourceStream->getFramesPerBurst() : sourceStream->getFramesPerCallback(); // Source + // If OUTPUT and using a callback then call back to the app using a SourceCaller. + // If INPUT and NOT using a callback then read from the child stream using a SourceCaller. if ((sourceStream->getCallback() != nullptr && isOutput) - || (sourceStream->getCallback() == nullptr && !isOutput)) { + || (sourceStream->getCallback() == nullptr && isInput)) { switch (sourceFormat) { case AudioFormat::Float: mSourceCaller = std::make_unique(sourceChannelCount, @@ -114,6 +117,8 @@ Result DataConversionFlowGraph::configure(AudioStream *sourceStream, AudioStream mSourceCaller->setStream(sourceStream); lastOutput = &mSourceCaller->output; } else { + // If OUTPUT and NOT using a callback then write to the child stream using a BlockWriter. + // If INPUT and using a callback then write to the app using a BlockWriter. switch (sourceFormat) { case AudioFormat::Float: mSource = std::make_unique(sourceChannelCount); @@ -125,7 +130,7 @@ Result DataConversionFlowGraph::configure(AudioStream *sourceStream, AudioStream LOGE("%s() Unsupported source format = %d", __func__, sourceFormat); return Result::ErrorIllegalArgument; } - if (!isOutput) { + if (isInput) { // The BlockWriter is after the Sink so use the SinkStream size. mBlockWriter.open(framesPerCallback * sinkStream->getBytesPerFrame()); mAppBuffer = std::make_unique( @@ -187,14 +192,14 @@ int32_t DataConversionFlowGraph::read(void *buffer, int32_t numFrames, int64_t t // This is similar to pushing data through the flowgraph. int32_t DataConversionFlowGraph::write(void *inputBuffer, int32_t numFrames) { - // Put the data from the Source at the head of the flowgraph. + // Put the data from the input at the head of the flowgraph. mSource->setData(inputBuffer, numFrames); while (true) { // Pull and read some data in app format into a small buffer. int32_t framesRead = mSink->read(mFramePosition, mAppBuffer.get(), flowgraph::kDefaultBufferSize); mFramePosition += framesRead; if (framesRead <= 0) break; - // Write to a block adapter, which will call the app whenever it has enough data. + // Write to a block adapter, which will call the destination whenever it has enough data. int32_t bytesRead = mBlockWriter.write(mAppBuffer.get(), framesRead * mFilterStream->getBytesPerFrame()); if (bytesRead < 0) return bytesRead; // TODO review diff --git a/modules/juce_audio_devices/native/oboe/src/common/FilterAudioStream.cpp b/modules/juce_audio_devices/native/oboe/src/common/FilterAudioStream.cpp index 2ca749bebd..a471583516 100644 --- a/modules/juce_audio_devices/native/oboe/src/common/FilterAudioStream.cpp +++ b/modules/juce_audio_devices/native/oboe/src/common/FilterAudioStream.cpp @@ -21,6 +21,25 @@ using namespace oboe; using namespace flowgraph; +// Output callback uses FixedBlockReader::read() +// <= SourceFloatCaller::onProcess() +// <=== DataConversionFlowGraph::read() +// <== FilterAudioStream::onAudioReady() +// +// Output blocking uses no block adapter because AAudio can accept +// writes of any size. It uses DataConversionFlowGraph::read() <== FilterAudioStream::write() <= app +// +// Input callback uses FixedBlockWriter::write() +// <= DataConversionFlowGraph::write() +// <= FilterAudioStream::onAudioReady() +// +// Input blocking uses FixedBlockReader::read() // TODO may not need block adapter +// <= SourceFloatCaller::onProcess() +// <=== SinkFloat::read() +// <= DataConversionFlowGraph::read() +// <== FilterAudioStream::read() +// <= app + Result FilterAudioStream::configureFlowGraph() { mFlowGraph = std::make_unique(); bool isOutput = getDirection() == Direction::Output; @@ -70,3 +89,4 @@ ResultWithValue FilterAudioStream::read(void *buffer, int32_t framesRead = mFlowGraph->read(buffer, numFrames, timeoutNanoseconds); return ResultWithValue::createBasedOnSign(framesRead); } + diff --git a/modules/juce_audio_devices/native/oboe/src/common/FilterAudioStream.h b/modules/juce_audio_devices/native/oboe/src/common/FilterAudioStream.h index 6baccd8d6a..3949de7761 100644 --- a/modules/juce_audio_devices/native/oboe/src/common/FilterAudioStream.h +++ b/modules/juce_audio_devices/native/oboe/src/common/FilterAudioStream.h @@ -187,9 +187,19 @@ public: : mFlowGraph->getDataCallbackResult(); } - void onErrorBeforeClose(AudioStream *oboeStream, Result error) override {} + void onErrorBeforeClose(AudioStream *oboeStream, Result error) override { + if (mStreamCallback != nullptr) { + mStreamCallback->onErrorBeforeClose(this, error); + } + } - void onErrorAfterClose(AudioStream *oboeStream, Result error) override {} + void onErrorAfterClose(AudioStream *oboeStream, Result error) override { + // Close this parent stream because the callback will only close the child. + AudioStream::close(); + if (mStreamCallback != nullptr) { + mStreamCallback->onErrorAfterClose(this, error); + } + } private: diff --git a/modules/juce_audio_devices/native/oboe/src/common/FixedBlockAdapter.h b/modules/juce_audio_devices/native/oboe/src/common/FixedBlockAdapter.h index 99a3ee0e46..76e961c9c1 100644 --- a/modules/juce_audio_devices/native/oboe/src/common/FixedBlockAdapter.h +++ b/modules/juce_audio_devices/native/oboe/src/common/FixedBlockAdapter.h @@ -19,6 +19,7 @@ #include #include +#include /** * Interface for a class that needs fixed-size blocks. diff --git a/modules/juce_audio_devices/native/oboe/src/common/OboeDebug.h b/modules/juce_audio_devices/native/oboe/src/common/OboeDebug.h index e5ba9e3b0f..dc7434c8fa 100644 --- a/modules/juce_audio_devices/native/oboe/src/common/OboeDebug.h +++ b/modules/juce_audio_devices/native/oboe/src/common/OboeDebug.h @@ -18,29 +18,24 @@ #ifndef OBOE_DEBUG_H #define OBOE_DEBUG_H -#if OBOE_ENABLE_LOGGING - #include #ifndef MODULE_NAME #define MODULE_NAME "OboeAudio" #endif -#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, MODULE_NAME, __VA_ARGS__) -#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, MODULE_NAME, __VA_ARGS__) +// Always log INFO and errors. #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, MODULE_NAME, __VA_ARGS__) #define LOGW(...) __android_log_print(ANDROID_LOG_WARN, MODULE_NAME, __VA_ARGS__) #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, MODULE_NAME, __VA_ARGS__) #define LOGF(...) __android_log_print(ANDROID_LOG_FATAL, MODULE_NAME, __VA_ARGS__) +#if OBOE_ENABLE_LOGGING +#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, MODULE_NAME, __VA_ARGS__) +#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, MODULE_NAME, __VA_ARGS__) #else - #define LOGV(...) #define LOGD(...) -#define LOGI(...) -#define LOGW(...) -#define LOGE(...) -#define LOGF(...) #endif #endif //OBOE_DEBUG_H diff --git a/modules/juce_audio_devices/native/oboe/src/common/QuirksManager.cpp b/modules/juce_audio_devices/native/oboe/src/common/QuirksManager.cpp index 2d5f644f7d..3df1bc4ac3 100644 --- a/modules/juce_audio_devices/native/oboe/src/common/QuirksManager.cpp +++ b/modules/juce_audio_devices/native/oboe/src/common/QuirksManager.cpp @@ -16,10 +16,76 @@ #include #include + #include "QuirksManager.h" using namespace oboe; +int32_t QuirksManager::DeviceQuirks::clipBufferSize(AudioStream &stream, + int32_t requestedSize) { + if (!OboeGlobals::areWorkaroundsEnabled()) { + return requestedSize; + } + int bottomMargin = kDefaultBottomMarginInBursts; + int topMargin = kDefaultTopMarginInBursts; + if (isMMapUsed(stream)) { + if (stream.getSharingMode() == SharingMode::Exclusive) { + bottomMargin = getExclusiveBottomMarginInBursts(); + topMargin = getExclusiveTopMarginInBursts(); + } + } else { + bottomMargin = kLegacyBottomMarginInBursts; + } + + int32_t burst = stream.getFramesPerBurst(); + int32_t minSize = bottomMargin * burst; + int32_t adjustedSize = requestedSize; + if (adjustedSize < minSize ) { + adjustedSize = minSize; + } else { + int32_t maxSize = stream.getBufferCapacityInFrames() - (topMargin * burst); + if (adjustedSize > maxSize ) { + adjustedSize = maxSize; + } + } + return adjustedSize; +} + +class SamsungDeviceQuirks : public QuirksManager::DeviceQuirks { +public: + SamsungDeviceQuirks() { + std::string arch = getPropertyString("ro.arch"); + isExynos = (arch.rfind("exynos", 0) == 0); // starts with? + } + + virtual ~SamsungDeviceQuirks() = default; + + int32_t getExclusiveBottomMarginInBursts() const override { + // TODO Make this conditional on build version when MMAP timing improves. + return isExynos ? kBottomMarginExynos : kBottomMarginOther; + } + + int32_t getExclusiveTopMarginInBursts() const override { + return kTopMargin; + } + +private: + // Stay farther away from DSP position on Exynos devices. + static constexpr int32_t kBottomMarginExynos = 2; + static constexpr int32_t kBottomMarginOther = 1; + static constexpr int32_t kTopMargin = 1; + bool isExynos = false; +}; + +QuirksManager::QuirksManager() { + std::string manufacturer = getPropertyString("ro.product.manufacturer"); + if (manufacturer == "samsung") { + mDeviceQuirks = std::make_unique(); + } else { + mDeviceQuirks = std::make_unique(); + } +} + bool QuirksManager::isConversionNeeded( const AudioStreamBuilder &builder, AudioStreamBuilder &childBuilder) { @@ -54,11 +120,13 @@ bool QuirksManager::isConversionNeeded( // Channel Count if (builder.getChannelCount() != oboe::Unspecified && builder.isChannelConversionAllowed()) { - if (builder.getChannelCount() == 2 // stereo? + if (OboeGlobals::areWorkaroundsEnabled() + && builder.getChannelCount() == 2 // stereo? && isInput && isLowLatency && (!builder.willUseAAudio() && (getSdkVersion() == __ANDROID_API_O__))) { - // workaround for temporary heap size regression, b/66967812 + // Workaround for heap size regression in O. + // b/66967812 AudioRecord does not allow FAST track for stereo capture in O childBuilder.setChannelCount(1); conversionNeeded = true; } diff --git a/modules/juce_audio_devices/native/oboe/src/common/QuirksManager.h b/modules/juce_audio_devices/native/oboe/src/common/QuirksManager.h index d14f1d3d4c..a177764cf2 100644 --- a/modules/juce_audio_devices/native/oboe/src/common/QuirksManager.h +++ b/modules/juce_audio_devices/native/oboe/src/common/QuirksManager.h @@ -19,10 +19,13 @@ #include #include +#include namespace oboe { /** + * INTERNAL USE ONLY. + * * Based on manufacturer, model and Android version number * decide whether data conversion needs to occur. * @@ -33,11 +36,17 @@ class QuirksManager { public: static QuirksManager &getInstance() { - static QuirksManager instance; + static QuirksManager instance; // singleton return instance; } + QuirksManager(); + virtual ~QuirksManager() = default; + /** + * Do we need to do channel, format or rate conversion to provide a low latency + * stream for this builder? If so then provide a builder for the native child stream + * that will be used to get low latency. * * @param builder builder provided by application * @param childBuilder modified builder appropriate for the underlying device @@ -45,7 +54,56 @@ public: */ bool isConversionNeeded(const AudioStreamBuilder &builder, AudioStreamBuilder &childBuilder); + static bool isMMapUsed(AudioStream &stream) { + bool answer = false; + if (stream.getAudioApi() == AudioApi::AAudio) { + AudioStreamAAudio *streamAAudio = + reinterpret_cast(&stream); + answer = streamAAudio->isMMapUsed(); + } + return answer; + } + + virtual int32_t clipBufferSize(AudioStream &stream, int32_t bufferSize) { + return mDeviceQuirks->clipBufferSize(stream, bufferSize); + } + + class DeviceQuirks { + public: + virtual ~DeviceQuirks() = default; + + /** + * Restrict buffer size. This is mainly to avoid glitches caused by MMAP + * timestamp inaccuracies. + * @param stream + * @param requestedSize + * @return + */ + int32_t clipBufferSize(AudioStream &stream, int32_t requestedSize); + + // Exclusive MMAP streams can have glitches because they are using a timing + // model of the DSP to control IO instead of direct synchronization. + virtual int32_t getExclusiveBottomMarginInBursts() const { + return kDefaultBottomMarginInBursts; + } + + virtual int32_t getExclusiveTopMarginInBursts() const { + return kDefaultTopMarginInBursts; + } + + static constexpr int32_t kDefaultBottomMarginInBursts = 0; + static constexpr int32_t kDefaultTopMarginInBursts = 0; + + // For Legacy streams, do not let the buffer go below one burst. + // b/129545119 | AAudio Legacy allows setBufferSizeInFrames too low + // Fixed in Q + static constexpr int32_t kLegacyBottomMarginInBursts = 1; + }; + private: + + std::unique_ptr mDeviceQuirks{}; + }; } diff --git a/modules/juce_audio_devices/native/oboe/src/common/Utilities.cpp b/modules/juce_audio_devices/native/oboe/src/common/Utilities.cpp index f940d1bf68..c3acf4763e 100644 --- a/modules/juce_audio_devices/native/oboe/src/common/Utilities.cpp +++ b/modules/juce_audio_devices/native/oboe/src/common/Utilities.cpp @@ -266,18 +266,40 @@ const char *convertToText(ChannelCount channelCount) { } } -int getSdkVersion() { +std::string getPropertyString(const char * name) { + std::string result; #ifdef __ANDROID__ - static int sCachedSdkVersion = -1; - if (sCachedSdkVersion == -1) { - char sdk[PROP_VALUE_MAX] = {0}; - if (__system_property_get("ro.build.version.sdk", sdk) != 0) { - sCachedSdkVersion = atoi(sdk); - } + char valueText[PROP_VALUE_MAX] = {0}; + if (__system_property_get(name, valueText) != 0) { + result = valueText; } - return sCachedSdkVersion; +#else + (void) name; #endif - return -1; + return result; +} + +int getPropertyInteger(const char * name, int defaultValue) { + int result = defaultValue; +#ifdef __ANDROID__ + char valueText[PROP_VALUE_MAX] = {0}; + if (__system_property_get(name, valueText) != 0) { + result = atoi(valueText); + } +#else + (void) name; +#endif + return result; +} + +int getSdkVersion() { + static int sCachedSdkVersion = -1; +#ifdef __ANDROID__ + if (sCachedSdkVersion == -1) { + sCachedSdkVersion = getPropertyInteger("ro.build.version.sdk", -1); + } +#endif + return sCachedSdkVersion; } }// namespace oboe diff --git a/modules/juce_audio_devices/native/oboe/src/fifo/FifoBuffer.cpp b/modules/juce_audio_devices/native/oboe/src/fifo/FifoBuffer.cpp index f89dca6f11..8d6b03bbb1 100644 --- a/modules/juce_audio_devices/native/oboe/src/fifo/FifoBuffer.cpp +++ b/modules/juce_audio_devices/native/oboe/src/fifo/FifoBuffer.cpp @@ -40,8 +40,6 @@ FifoBuffer::FifoBuffer(uint32_t bytesPerFrame, uint32_t capacityInFrames) int32_t bytesPerBuffer = bytesPerFrame * capacityInFrames; mStorage = new uint8_t[bytesPerBuffer]; mStorageOwned = true; - LOGD("%s() capacityInFrames = %d, bytesPerFrame = %d", - __func__, capacityInFrames, bytesPerFrame); } FifoBuffer::FifoBuffer( uint32_t bytesPerFrame, @@ -60,8 +58,6 @@ FifoBuffer::FifoBuffer( uint32_t bytesPerFrame, writeCounterAddress); mStorage = dataStorageAddress; mStorageOwned = false; - LOGD("%s(*) capacityInFrames = %d, bytesPerFrame = %d", - __func__, capacityInFrames, bytesPerFrame); } FifoBuffer::~FifoBuffer() { diff --git a/modules/juce_audio_devices/native/oboe/src/flowgraph/resampler/IntegerRatio.cpp b/modules/juce_audio_devices/native/oboe/src/flowgraph/resampler/IntegerRatio.cpp index d2f51801dc..4bd75b3916 100644 --- a/modules/juce_audio_devices/native/oboe/src/flowgraph/resampler/IntegerRatio.cpp +++ b/modules/juce_audio_devices/native/oboe/src/flowgraph/resampler/IntegerRatio.cpp @@ -14,14 +14,12 @@ * limitations under the License. */ -#include - #include "IntegerRatio.h" using namespace resampler; // Enough primes to cover the common sample rates. -const std::vector IntegerRatio::kPrimes{ +static const int kPrimes[] = { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, diff --git a/modules/juce_audio_devices/native/oboe/src/flowgraph/resampler/IntegerRatio.h b/modules/juce_audio_devices/native/oboe/src/flowgraph/resampler/IntegerRatio.h index d0b3a48d9a..fb390f1007 100644 --- a/modules/juce_audio_devices/native/oboe/src/flowgraph/resampler/IntegerRatio.h +++ b/modules/juce_audio_devices/native/oboe/src/flowgraph/resampler/IntegerRatio.h @@ -18,7 +18,6 @@ #define OBOE_INTEGER_RATIO_H #include -#include namespace resampler { @@ -46,7 +45,6 @@ public: private: int32_t mNumerator; int32_t mDenominator; - static const std::vector kPrimes; }; } diff --git a/modules/juce_audio_devices/native/oboe/src/opensles/AudioInputStreamOpenSLES.cpp b/modules/juce_audio_devices/native/oboe/src/opensles/AudioInputStreamOpenSLES.cpp index 507620264f..fcad183bf9 100644 --- a/modules/juce_audio_devices/native/oboe/src/opensles/AudioInputStreamOpenSLES.cpp +++ b/modules/juce_audio_devices/native/oboe/src/opensles/AudioInputStreamOpenSLES.cpp @@ -198,7 +198,7 @@ Result AudioInputStreamOpenSLES::open() { goto error; } - oboeResult = configureBufferSizes(); + oboeResult = configureBufferSizes(mSampleRate); if (Result::OK != oboeResult) { goto error; } @@ -340,7 +340,7 @@ Result AudioInputStreamOpenSLES::updateServiceFrameCounter() { SLmillisecond msec = 0; SLresult slResult = (*mRecordInterface)->GetPosition(mRecordInterface, &msec); if (SL_RESULT_SUCCESS != slResult) { - LOGD("%s(): GetPosition() returned %s", __func__, getSLErrStr(slResult)); + LOGW("%s(): GetPosition() returned %s", __func__, getSLErrStr(slResult)); // set result based on SLresult result = Result::ErrorInternal; } else { diff --git a/modules/juce_audio_devices/native/oboe/src/opensles/AudioOutputStreamOpenSLES.cpp b/modules/juce_audio_devices/native/oboe/src/opensles/AudioOutputStreamOpenSLES.cpp index a8c70b607b..cdb888327f 100644 --- a/modules/juce_audio_devices/native/oboe/src/opensles/AudioOutputStreamOpenSLES.cpp +++ b/modules/juce_audio_devices/native/oboe/src/opensles/AudioOutputStreamOpenSLES.cpp @@ -223,7 +223,7 @@ Result AudioOutputStreamOpenSLES::open() { goto error; } - oboeResult = configureBufferSizes(); + oboeResult = configureBufferSizes(mSampleRate); if (Result::OK != oboeResult) { goto error; } @@ -271,7 +271,7 @@ Result AudioOutputStreamOpenSLES::setPlayState_l(SLuint32 newState) { SLresult slResult = (*mPlayInterface)->SetPlayState(mPlayInterface, newState); if (SL_RESULT_SUCCESS != slResult) { - LOGD("AudioOutputStreamOpenSLES(): %s() returned %s", __func__, getSLErrStr(slResult)); + LOGW("AudioOutputStreamOpenSLES(): %s() returned %s", __func__, getSLErrStr(slResult)); result = Result::ErrorInternal; // TODO convert slResult to Result::Error } return result; @@ -312,7 +312,6 @@ Result AudioOutputStreamOpenSLES::requestStart() { setState(initialState); mLock.unlock(); } - LOGD("AudioOutputStreamOpenSLES(): %s() returning %d", __func__, result); return result; } @@ -343,7 +342,6 @@ Result AudioOutputStreamOpenSLES::requestPause() { } else { setState(initialState); } - LOGD("AudioOutputStreamOpenSLES(): %s() returning %d", __func__, result); return result; } @@ -371,7 +369,6 @@ Result AudioOutputStreamOpenSLES::requestFlush_l() { result = Result::ErrorInternal; } } - LOGD("AudioOutputStreamOpenSLES(): %s() returning %d", __func__, result); return result; } @@ -410,7 +407,6 @@ Result AudioOutputStreamOpenSLES::requestStop() { } else { setState(initialState); } - LOGD("AudioOutputStreamOpenSLES(): %s() returning %d", __func__, result); return result; } @@ -440,7 +436,7 @@ Result AudioOutputStreamOpenSLES::updateServiceFrameCounter() { SLmillisecond msec = 0; SLresult slResult = (*mPlayInterface)->GetPosition(mPlayInterface, &msec); if (SL_RESULT_SUCCESS != slResult) { - LOGD("%s(): GetPosition() returned %s", __func__, getSLErrStr(slResult)); + LOGW("%s(): GetPosition() returned %s", __func__, getSLErrStr(slResult)); // set result based on SLresult result = Result::ErrorInternal; } else { diff --git a/modules/juce_audio_devices/native/oboe/src/opensles/AudioStreamOpenSLES.cpp b/modules/juce_audio_devices/native/oboe/src/opensles/AudioStreamOpenSLES.cpp index 38bdf1a3d3..3af1a30e30 100644 --- a/modules/juce_audio_devices/native/oboe/src/opensles/AudioStreamOpenSLES.cpp +++ b/modules/juce_audio_devices/native/oboe/src/opensles/AudioStreamOpenSLES.cpp @@ -37,7 +37,7 @@ AudioStreamOpenSLES::AudioStreamOpenSLES(const AudioStreamBuilder &builder) mSessionId = SessionId::None; } -static constexpr int32_t kFramesPerHighLatencyBurst = 960; // typical, 20 msec at 48000 +static constexpr int32_t kHighLatencyBufferSizeMillis = 20; // typical Android period static constexpr SLuint32 kAudioChannelCountMax = 30; // TODO Why 30? static constexpr SLuint32 SL_ANDROID_UNKNOWN_CHANNELMASK = 0; // Matches name used internally. @@ -69,8 +69,7 @@ SLuint32 AudioStreamOpenSLES::getDefaultByteOrder() { Result AudioStreamOpenSLES::open() { - LOGI("AudioStreamOpenSLES::open(chans:%d, rate:%d)", - mChannelCount, mSampleRate); + LOGI("AudioStreamOpenSLES::open() chans=%d, rate=%d", mChannelCount, mSampleRate); SLresult result = EngineOpenSLES::getInstance().open(); if (SL_RESULT_SUCCESS != result) { @@ -94,24 +93,35 @@ Result AudioStreamOpenSLES::open() { return Result::OK; } -Result AudioStreamOpenSLES::configureBufferSizes() { +Result AudioStreamOpenSLES::configureBufferSizes(int32_t sampleRate) { + LOGD("AudioStreamOpenSLES:%s(%d) initial mFramesPerBurst = %d, mFramesPerCallback = %d", + __func__, sampleRate, mFramesPerBurst, mFramesPerCallback); // Decide frames per burst based on hints from caller. - mFramesPerBurst = mFramesPerCallback; - if (mFramesPerBurst == kUnspecified) { + if (mFramesPerCallback != kUnspecified) { + // Requested framesPerCallback must be honored. + mFramesPerBurst = mFramesPerCallback; + } else { mFramesPerBurst = DefaultStreamValues::FramesPerBurst; + + // Calculate the size of a fixed duration high latency buffer based on sample rate. + int32_t framesPerHighLatencyBuffer = + (kHighLatencyBufferSizeMillis * sampleRate) / kMillisPerSecond; + + // For high latency streams, use a larger buffer size. + // Performance Mode support was added in N_MR1 (7.1) + if (getSdkVersion() >= __ANDROID_API_N_MR1__ + && mPerformanceMode != PerformanceMode::LowLatency + && mFramesPerBurst < framesPerHighLatencyBuffer) { + // Find a multiple of framesPerBurst >= framesPerHighLatencyBuffer. + int32_t numBursts = (framesPerHighLatencyBuffer + mFramesPerBurst - 1) / mFramesPerBurst; + mFramesPerBurst *= numBursts; + LOGD("AudioStreamOpenSLES:%s() NOT low latency, set mFramesPerBurst = %d", + __func__, mFramesPerBurst); + } + mFramesPerCallback = mFramesPerBurst; } - // For high latency streams, use a larger buffer size. - // Performance Mode support was added in N_MR1 (7.1) - if (getSdkVersion() >= __ANDROID_API_N_MR1__ - && mPerformanceMode != PerformanceMode::LowLatency - && mFramesPerBurst < kFramesPerHighLatencyBurst) { - // Find a multiple of framesPerBurst >= kFramesPerHighLatencyBurst. - int32_t numBursts = (kFramesPerHighLatencyBurst + mFramesPerBurst - 1) / mFramesPerBurst; - mFramesPerBurst *= numBursts; - LOGD("AudioStreamOpenSLES:%s() NOT low latency, set mFramesPerBurst = %d", - __func__, mFramesPerBurst); - } - mFramesPerCallback = mFramesPerBurst; + LOGD("AudioStreamOpenSLES:%s(%d) final mFramesPerBurst = %d, mFramesPerCallback = %d", + __func__, sampleRate, mFramesPerBurst, mFramesPerCallback); mBytesPerCallback = mFramesPerCallback * getBytesPerFrame(); if (mBytesPerCallback <= 0) { @@ -291,23 +301,21 @@ int32_t AudioStreamOpenSLES::getBufferDepth(SLAndroidSimpleBufferQueueItf bq) { void AudioStreamOpenSLES::processBufferCallback(SLAndroidSimpleBufferQueueItf bq) { bool stopStream = false; - // Ask the callback to fill the output buffer with data. + // Ask the app callback to process the buffer. DataCallbackResult result = fireDataCallback(mCallbackBuffer.get(), mFramesPerCallback); if (result == DataCallbackResult::Continue) { - // Update Oboe service position based on OpenSL ES position. - updateServiceFrameCounter(); + // Pass the buffer to OpenSLES. + SLresult enqueueResult = enqueueCallbackBuffer(bq); + if (enqueueResult != SL_RESULT_SUCCESS) { + LOGE("%s() returned %d", __func__, enqueueResult); + stopStream = true; + } // Update Oboe client position with frames handled by the callback. if (getDirection() == Direction::Input) { mFramesRead += mFramesPerCallback; } else { mFramesWritten += mFramesPerCallback; } - // Pass the data to OpenSLES. - SLresult enqueueResult = enqueueCallbackBuffer(bq); - if (enqueueResult != SL_RESULT_SUCCESS) { - LOGE("%s() returned %d", __func__, enqueueResult); - stopStream = true; - } } else if (result == DataCallbackResult::Stop) { LOGD("Oboe callback returned Stop"); stopStream = true; @@ -320,7 +328,7 @@ void AudioStreamOpenSLES::processBufferCallback(SLAndroidSimpleBufferQueueItf bq } } -// this callback handler is called every time a buffer needs processing +// This callback handler is called every time a buffer has been processed by OpenSL ES. static void bqCallbackGlue(SLAndroidSimpleBufferQueueItf bq, void *context) { (reinterpret_cast(context))->processBufferCallback(bq); } @@ -348,7 +356,8 @@ int32_t AudioStreamOpenSLES::getFramesPerBurst() { return mFramesPerBurst; } -int64_t AudioStreamOpenSLES::getFramesProcessedByServer() const { +int64_t AudioStreamOpenSLES::getFramesProcessedByServer() { + updateServiceFrameCounter(); int64_t millis64 = mPositionMillis.get(); int64_t framesProcessed = millis64 * getSampleRate() / kMillisPerSecond; return framesProcessed; diff --git a/modules/juce_audio_devices/native/oboe/src/opensles/AudioStreamOpenSLES.h b/modules/juce_audio_devices/native/oboe/src/opensles/AudioStreamOpenSLES.h index 9980c5e412..81fdc63e29 100644 --- a/modules/juce_audio_devices/native/oboe/src/opensles/AudioStreamOpenSLES.h +++ b/modules/juce_audio_devices/native/oboe/src/opensles/AudioStreamOpenSLES.h @@ -100,7 +100,7 @@ protected: PerformanceMode convertPerformanceMode(SLuint32 openslMode) const; SLuint32 convertPerformanceMode(PerformanceMode oboeMode) const; - Result configureBufferSizes(); + Result configureBufferSizes(int32_t sampleRate); void logUnsupportedAttributes(); @@ -112,7 +112,7 @@ protected: mState.store(state); } - int64_t getFramesProcessedByServer() const; + int64_t getFramesProcessedByServer(); // OpenSLES stuff SLObjectItf mObjectInterface = nullptr;