From a6df818255db538c385108f6260fc57a1257405e Mon Sep 17 00:00:00 2001 From: ed Date: Thu, 29 Jul 2021 08:56:38 +0100 Subject: [PATCH] Android: Update to Oboe release 1.6.1 --- .../native/oboe/CMakeLists.txt | 8 +- .../native/oboe/{readme.md => README.md} | 2 +- .../native/oboe/include/oboe/AudioStream.h | 20 ++-- .../oboe/include/oboe/AudioStreamBase.h | 16 ++- .../oboe/include/oboe/AudioStreamBuilder.h | 36 +++++- .../native/oboe/include/oboe/Definitions.h | 32 ++++- .../native/oboe/include/oboe/Version.h | 2 +- .../native/oboe/src/aaudio/AAudioLoader.cpp | 11 ++ .../native/oboe/src/aaudio/AAudioLoader.h | 11 +- .../oboe/src/aaudio/AudioStreamAAudio.cpp | 110 +++++++++++------- .../oboe/src/aaudio/AudioStreamAAudio.h | 13 ++- .../native/oboe/src/common/AudioStream.cpp | 14 +-- .../oboe/src/common/AudioStreamBuilder.cpp | 13 +-- .../src/common/DataConversionFlowGraph.cpp | 24 ++++ .../oboe/src/common/FilterAudioStream.cpp | 2 +- .../oboe/src/common/FilterAudioStream.h | 16 ++- .../native/oboe/src/common/QuirksManager.cpp | 37 +++++- .../oboe/src/common/SourceI24Caller.cpp | 56 +++++++++ .../native/oboe/src/common/SourceI24Caller.h | 52 +++++++++ .../oboe/src/common/SourceI32Caller.cpp | 47 ++++++++ .../native/oboe/src/common/SourceI32Caller.h | 52 +++++++++ .../native/oboe/src/common/Utilities.cpp | 8 ++ .../oboe/src/flowgraph/FlowgraphUtilities.h | 55 +++++++++ .../oboe/src/flowgraph/ManyToMultiConverter.h | 2 +- .../native/oboe/src/flowgraph/RampLinear.cpp | 4 + .../native/oboe/src/flowgraph/SinkFloat.cpp | 2 +- .../native/oboe/src/flowgraph/SinkFloat.h | 1 + .../native/oboe/src/flowgraph/SinkI32.cpp | 55 +++++++++ .../native/oboe/src/flowgraph/SinkI32.h | 42 +++++++ .../native/oboe/src/flowgraph/SourceFloat.cpp | 8 +- .../native/oboe/src/flowgraph/SourceFloat.h | 1 + .../native/oboe/src/flowgraph/SourceI32.cpp | 54 +++++++++ .../native/oboe/src/flowgraph/SourceI32.h | 44 +++++++ .../oboe/src/opensles/AudioStreamBuffered.h | 2 +- .../oboe/src/opensles/AudioStreamOpenSLES.cpp | 4 - .../oboe/src/opensles/AudioStreamOpenSLES.h | 5 +- .../oboe/src/opensles/OpenSLESUtilities.cpp | 2 + 37 files changed, 742 insertions(+), 121 deletions(-) rename modules/juce_audio_devices/native/oboe/{readme.md => README.md} (91%) create mode 100644 modules/juce_audio_devices/native/oboe/src/common/SourceI24Caller.cpp create mode 100644 modules/juce_audio_devices/native/oboe/src/common/SourceI24Caller.h create mode 100644 modules/juce_audio_devices/native/oboe/src/common/SourceI32Caller.cpp create mode 100644 modules/juce_audio_devices/native/oboe/src/common/SourceI32Caller.h create mode 100644 modules/juce_audio_devices/native/oboe/src/flowgraph/FlowgraphUtilities.h create mode 100644 modules/juce_audio_devices/native/oboe/src/flowgraph/SinkI32.cpp create mode 100644 modules/juce_audio_devices/native/oboe/src/flowgraph/SinkI32.h create mode 100644 modules/juce_audio_devices/native/oboe/src/flowgraph/SourceI32.cpp create mode 100644 modules/juce_audio_devices/native/oboe/src/flowgraph/SourceI32.h diff --git a/modules/juce_audio_devices/native/oboe/CMakeLists.txt b/modules/juce_audio_devices/native/oboe/CMakeLists.txt index 4763da2367..0f243b885d 100644 --- a/modules/juce_audio_devices/native/oboe/CMakeLists.txt +++ b/modules/juce_audio_devices/native/oboe/CMakeLists.txt @@ -20,6 +20,8 @@ set (oboe_sources src/common/LatencyTuner.cpp src/common/SourceFloatCaller.cpp src/common/SourceI16Caller.cpp + src/common/SourceI24Caller.cpp + src/common/SourceI32Caller.cpp src/common/Utilities.cpp src/common/QuirksManager.cpp src/fifo/FifoBuffer.cpp @@ -37,9 +39,11 @@ set (oboe_sources src/flowgraph/SinkFloat.cpp src/flowgraph/SinkI16.cpp src/flowgraph/SinkI24.cpp + src/flowgraph/SinkI32.cpp src/flowgraph/SourceFloat.cpp src/flowgraph/SourceI16.cpp src/flowgraph/SourceI24.cpp + src/flowgraph/SourceI32.cpp src/flowgraph/resampler/IntegerRatio.cpp src/flowgraph/resampler/LinearResampler.cpp src/flowgraph/resampler/MultiChannelResampler.cpp @@ -71,11 +75,11 @@ target_include_directories(oboe # This comment provided for Apache License compliance. We've removed the extra warnings flags and # the `-Werror` option, to avoid cases where compilers produce unexpected errors and fail the build. -# We've also removed the explicit `-std=c++14` compile option, and replaced it with a more +# We've also removed the explicit `-std=c++17` compile option, and replaced it with a more # cmake-friendly way of specifying the language standard. target_compile_options(oboe PRIVATE -Ofast) -set_target_properties(oboe PROPERTIES CXX_STANDARD 14 CXX_STANDARD_REQUIRED TRUE CXX_EXTENSIONS FALSE) +set_target_properties(oboe PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED TRUE CXX_EXTENSIONS FALSE) # JUCE CHANGE ENDS HERE diff --git a/modules/juce_audio_devices/native/oboe/readme.md b/modules/juce_audio_devices/native/oboe/README.md similarity index 91% rename from modules/juce_audio_devices/native/oboe/readme.md rename to modules/juce_audio_devices/native/oboe/README.md index 5446d4b8b2..cd4318a94d 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.4.2 (55304d7). +These files are from tag 1.6.1 (855ea841). 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/include/oboe/AudioStream.h b/modules/juce_audio_devices/native/oboe/include/oboe/AudioStream.h index c862de6d3e..825ffc89bf 100644 --- a/modules/juce_audio_devices/native/oboe/include/oboe/AudioStream.h +++ b/modules/juce_audio_devices/native/oboe/include/oboe/AudioStream.h @@ -130,7 +130,7 @@ public: * * @return state or a negative error. */ - virtual StreamState getState() const = 0; + virtual StreamState getState() = 0; /** * Wait until the stream's current state no longer matches the input state. @@ -191,7 +191,7 @@ public: * @return a result which is either Result::OK with the xRun count as the value, or a * Result::Error* code */ - virtual ResultWithValue getXRunCount() const { + virtual ResultWithValue getXRunCount() { return ResultWithValue(Result::ErrorUnimplemented); } @@ -205,7 +205,9 @@ public: * * @return burst size */ - virtual int32_t getFramesPerBurst() = 0; + int32_t getFramesPerBurst() const { + return mFramesPerBurst; + } /** * Get the number of bytes in each audio frame. This is calculated using the channel count @@ -372,11 +374,6 @@ public: return nullptr; } - /** - * Launch a thread that will stop the stream. - */ - void launchStopThread(); - /** * Update mFramesWritten. * For internal use only. @@ -537,6 +534,12 @@ protected: oboe::Result mErrorCallbackResult = oboe::Result::OK; + /** + * Number of frames which will be copied to/from the audio device in a single read/write + * operation + */ + int32_t mFramesPerBurst = kUnspecified; + private: // Log the scheduler if it changes. @@ -545,7 +548,6 @@ private: std::atomic mDataCallbackEnabled{false}; std::atomic mErrorCallbackCalled{false}; - }; /** 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 e0b38aa61d..e6c3d37c5d 100644 --- a/modules/juce_audio_devices/native/oboe/include/oboe/AudioStreamBase.h +++ b/modules/juce_audio_devices/native/oboe/include/oboe/AudioStreamBase.h @@ -18,6 +18,7 @@ #define OBOE_STREAM_BASE_H_ #include +#include #include "oboe/AudioStreamCallback.h" #include "oboe/Definitions.h" @@ -196,11 +197,6 @@ protected: int32_t mBufferCapacityInFrames = kUnspecified; /** Stream buffer size specified as a number of audio frames */ int32_t mBufferSizeInFrames = kUnspecified; - /** - * Number of frames which will be copied to/from the audio device in a single read/write - * operation - */ - int32_t mFramesPerBurst = kUnspecified; /** Stream sharing mode */ SharingMode mSharingMode = SharingMode::Shared; @@ -222,6 +218,11 @@ protected: /** Stream session ID allocation strategy. Only active on Android 28+ */ SessionId mSessionId = SessionId::None; + /** Control the name of the package creating the stream. Only active on Android 31+ */ + std::string mPackageName; + /** Control the attribution tag of the context creating the stream. Only active on Android 31+ */ + std::string mAttributionTag; + // Control whether Oboe can convert channel counts to achieve optimal results. bool mChannelConversionAllowed = false; // Control whether Oboe can convert data formats to achieve optimal results. @@ -235,9 +236,12 @@ protected: case AudioFormat::Unspecified: case AudioFormat::I16: case AudioFormat::Float: + case AudioFormat::I24: + case AudioFormat::I32: break; - + // JUCE CHANGE STARTS HERE case AudioFormat::Invalid: + // JUCE CHANGE ENDS HERE default: return Result::ErrorInvalidFormat; } 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 bd12585090..c7ea7af345 100644 --- a/modules/juce_audio_devices/native/oboe/include/oboe/AudioStreamBuilder.h +++ b/modules/juce_audio_devices/native/oboe/include/oboe/AudioStreamBuilder.h @@ -391,11 +391,11 @@ public: } /** - * If true then Oboe might convert data formats to achieve optimal results. + * If true then Oboe might convert data formats to achieve optimal results. * On some versions of Android, for example, a float stream could not get a * low latency data path. So an I16 stream might be opened and converted to float. * - * Default is true. + * Default is false. */ AudioStreamBuilder *setFormatConversionAllowed(bool allowed) { mFormatConversionAllowed = allowed; @@ -418,6 +418,38 @@ public: return this; } + /** + * Declare the name of the package creating the stream. + * + * This is usually Context#getPackageName() + * + * The default, if you do not call this function, is a random package in the calling uid. + * + * Added in API level 31. + * + * @param packageName packageName of the calling app. + */ + AudioStreamBuilder *setPackageName(std::string packageName) { + mPackageName = packageName; + return this; + } + + /** + * Declare the attribution tag of the context creating the stream. + * + * This is usually Context#getAttributionTag() + * + * The default, if you do not call this function, is the default attribution tag. + * + * Added in API level 31. + * + * @param attributionTag attributionTag of the calling context. + */ + AudioStreamBuilder *setAttributionTag(std::string attributionTag) { + mAttributionTag = attributionTag; + return this; + } + /** * @return true if AAudio will be used based on the current settings. */ 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 8063a83c96..566518f081 100644 --- a/modules/juce_audio_devices/native/oboe/include/oboe/Definitions.h +++ b/modules/juce_audio_devices/native/oboe/include/oboe/Definitions.h @@ -17,7 +17,6 @@ #ifndef OBOE_DEFINITIONS_H #define OBOE_DEFINITIONS_H - #include #include @@ -108,9 +107,36 @@ namespace oboe { I16 = 1, // AAUDIO_FORMAT_PCM_I16, /** - * Single precision floating points. + * Single precision floating point. + * + * This is the recommended format for most applications. + * But note that the use of Float may prevent the opening of + * a low-latency input path on OpenSL ES or Legacy AAudio streams. */ Float = 2, // AAUDIO_FORMAT_PCM_FLOAT, + + /** + * Signed 24-bit integers, packed into 3 bytes. + * + * Note that the use of this format does not guarantee that + * the full precision will be provided. The underlying device may + * be using I16 format. + * + * Added in API 31 (S). + */ + I24 = 3, // AAUDIO_FORMAT_PCM_I24_PACKED + + /** + * Signed 32-bit integers. + * + * Note that the use of this format does not guarantee that + * the full precision will be provided. The underlying device may + * be using I16 format. + * + * Added in API 31 (S). + */ + I32 = 4, // AAUDIO_FORMAT_PCM_I32 + }; /** @@ -158,7 +184,7 @@ namespace oboe { Reserved8, Reserved9, Reserved10, - ErrorClosed, + ErrorClosed = -869, }; /** 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 1fd1805496..5653030519 100644 --- a/modules/juce_audio_devices/native/oboe/include/oboe/Version.h +++ b/modules/juce_audio_devices/native/oboe/include/oboe/Version.h @@ -34,7 +34,7 @@ #define OBOE_VERSION_MAJOR 1 // Type: 8-bit unsigned int. Min value: 0 Max value: 255. See below for description. -#define OBOE_VERSION_MINOR 5 +#define OBOE_VERSION_MINOR 6 // Type: 16-bit unsigned int. Min value: 0 Max value: 65535. See below for description. #define OBOE_VERSION_PATCH 1 diff --git a/modules/juce_audio_devices/native/oboe/src/aaudio/AAudioLoader.cpp b/modules/juce_audio_devices/native/oboe/src/aaudio/AAudioLoader.cpp index ec97a26e7d..8e43d72421 100644 --- a/modules/juce_audio_devices/native/oboe/src/aaudio/AAudioLoader.cpp +++ b/modules/juce_audio_devices/native/oboe/src/aaudio/AAudioLoader.cpp @@ -83,6 +83,11 @@ int AAudioLoader::open() { builder_setSessionId = load_V_PBI("AAudioStreamBuilder_setSessionId"); } + if (getSdkVersion() >= __ANDROID_API_S__){ + builder_setPackageName = load_V_PBCPH("AAudioStreamBuilder_setPackageName"); + builder_setAttributionTag = load_V_PBCPH("AAudioStreamBuilder_setAttributionTag"); + } + builder_delete = load_I_PB("AAudioStreamBuilder_delete"); @@ -160,6 +165,12 @@ AAudioLoader::signature_V_PBI AAudioLoader::load_V_PBI(const char *functionName) return reinterpret_cast(proc); } +AAudioLoader::signature_V_PBCPH AAudioLoader::load_V_PBCPH(const char *functionName) { + void *proc = dlsym(mLibHandle, functionName); + AAudioLoader_check(proc, functionName); + return reinterpret_cast(proc); +} + AAudioLoader::signature_V_PBPDPV AAudioLoader::load_V_PBPDPV(const char *functionName) { void *proc = dlsym(mLibHandle, functionName); AAudioLoader_check(proc, functionName); diff --git a/modules/juce_audio_devices/native/oboe/src/aaudio/AAudioLoader.h b/modules/juce_audio_devices/native/oboe/src/aaudio/AAudioLoader.h index a07c53919b..8efb6d2777 100644 --- a/modules/juce_audio_devices/native/oboe/src/aaudio/AAudioLoader.h +++ b/modules/juce_audio_devices/native/oboe/src/aaudio/AAudioLoader.h @@ -60,13 +60,16 @@ typedef int32_t aaudio_session_id_t; #define AAUDIO_STREAM_STATE_STARTED static_cast(StreamState::Started) #else #include -#include #endif #ifndef __NDK_MAJOR__ #define __NDK_MAJOR__ 0 #endif +#ifndef __ANDROID_API_S__ +#define __ANDROID_API_S__ 31 +#endif + namespace oboe { /** @@ -98,6 +101,8 @@ class AAudioLoader { // AAudioStreamBuilder_setSampleRate() typedef void (*signature_V_PBI)(AAudioStreamBuilder *, int32_t); + typedef void (*signature_V_PBCPH)(AAudioStreamBuilder *, const char *); + typedef int32_t (*signature_I_PS)(AAudioStream *); // AAudioStream_getSampleRate() typedef int64_t (*signature_L_PS)(AAudioStream *); // AAudioStream_getFramesRead() // AAudioStream_setBufferSizeInFrames() @@ -160,6 +165,9 @@ class AAudioLoader { signature_V_PBI builder_setInputPreset = nullptr; signature_V_PBI builder_setSessionId = nullptr; + signature_V_PBCPH builder_setPackageName = nullptr; + signature_V_PBCPH builder_setAttributionTag = nullptr; + signature_V_PBPDPV builder_setDataCallback = nullptr; signature_V_PBPEPV builder_setErrorCallback = nullptr; @@ -212,6 +220,7 @@ class AAudioLoader { signature_I_PPB load_I_PPB(const char *name); signature_CPH_I load_CPH_I(const char *name); signature_V_PBI load_V_PBI(const char *name); + signature_V_PBCPH load_V_PBCPH(const char *name); signature_V_PBPDPV load_V_PBPDPV(const char *name); signature_V_PBPEPV load_V_PBPEPV(const char *name); signature_I_PB load_I_PB(const char *name); 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 bc535aeb7f..5ce0dc4a82 100644 --- a/modules/juce_audio_devices/native/oboe/src/aaudio/AudioStreamAAudio.cpp +++ b/modules/juce_audio_devices/native/oboe/src/aaudio/AudioStreamAAudio.cpp @@ -62,7 +62,7 @@ static aaudio_data_callback_result_t oboe_aaudio_data_callback_proc( // It calls app error callbacks from a static function in case the stream gets deleted. static void oboe_aaudio_error_thread_proc(AudioStreamAAudio *oboeStream, Result error) { - LOGD("%s() - entering >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>", __func__); + LOGD("%s(,%d) - entering >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>", __func__, error); AudioStreamErrorCallback *errorCallback = oboeStream->getErrorCallback(); if (errorCallback == nullptr) return; // should be impossible bool isErrorHandled = errorCallback->onError(oboeStream, error); @@ -241,7 +241,16 @@ Result AudioStreamAAudio::open() { static_cast(mSessionId)); } - // TODO get more parameters from the builder? + // These were added in S so we have to check for the function pointer. + if (mLibLoader->builder_setPackageName != nullptr && !mPackageName.empty()) { + mLibLoader->builder_setPackageName(aaudioBuilder, + mPackageName.c_str()); + } + + if (mLibLoader->builder_setAttributionTag != nullptr && !mAttributionTag.empty()) { + mLibLoader->builder_setAttributionTag(aaudioBuilder, + mAttributionTag.c_str()); + } if (isDataCallbackSpecified()) { mLibLoader->builder_setDataCallback(aaudioBuilder, oboe_aaudio_data_callback_proc, this); @@ -282,6 +291,7 @@ Result AudioStreamAAudio::open() { mLibLoader->stream_getPerformanceMode(mAAudioStream)); mBufferCapacityInFrames = mLibLoader->stream_getBufferCapacity(mAAudioStream); mBufferSizeInFrames = mLibLoader->stream_getBufferSize(mAAudioStream); + mFramesPerBurst = mLibLoader->stream_getFramesPerBurst(mAAudioStream); // These were added in P so we have to check for the function pointer. if (mLibLoader->stream_getUsage != nullptr) { @@ -318,8 +328,13 @@ Result AudioStreamAAudio::close() { AudioStream::close(); - // This will delete the AAudio stream object so we need to null out the pointer. - AAudioStream *stream = mAAudioStream.exchange(nullptr); + AAudioStream *stream = nullptr; + { + // Wait for any methods using mAAudioStream to finish. + std::unique_lock lock2(mAAudioStreamLock); + // Closing will delete *mAAudioStream so we need to null out the pointer atomically. + stream = mAAudioStream.exchange(nullptr); + } if (stream != nullptr) { if (OboeGlobals::areWorkaroundsEnabled()) { // Make sure we are really stopped. Do it under mLock @@ -338,7 +353,22 @@ Result AudioStreamAAudio::close() { } } -DataCallbackResult AudioStreamAAudio::callOnAudioReady(AAudioStream *stream, +static void oboe_stop_thread_proc(AudioStream *oboeStream) { + if (oboeStream != nullptr) { + oboeStream->requestStop(); + } +} + +void AudioStreamAAudio::launchStopThread() { + // Prevent multiple stop threads from being launched. + if (mStopThreadAllowed.exchange(false)) { + // Stop this stream on a separate thread + std::thread t(oboe_stop_thread_proc, this); + t.detach(); + } +} + +DataCallbackResult AudioStreamAAudio::callOnAudioReady(AAudioStream * /*stream*/, void *audioData, int32_t numFrames) { DataCallbackResult result = fireDataCallback(audioData, numFrames); @@ -351,16 +381,12 @@ DataCallbackResult AudioStreamAAudio::callOnAudioReady(AAudioStream *stream, LOGE("Oboe callback returned unexpected value = %d", result); } - if (getSdkVersion() <= __ANDROID_API_P__) { + // Returning Stop caused various problems before S. See #1230 + if (OboeGlobals::areWorkaroundsEnabled() && getSdkVersion() <= __ANDROID_API_R__) { launchStopThread(); - if (isMMapUsed()) { - return DataCallbackResult::Stop; - } else { - // Legacy stream <= API_P cannot be restarted after returning Stop. - return DataCallbackResult::Continue; - } + return DataCallbackResult::Continue; } else { - return DataCallbackResult::Stop; // OK >= API_Q + return DataCallbackResult::Stop; // OK >= API_S } } } @@ -380,6 +406,7 @@ Result AudioStreamAAudio::requestStart() { if (isDataCallbackSpecified()) { setDataCallbackEnabled(true); } + mStopThreadAllowed = true; return static_cast(mLibLoader->stream_requestStart(stream)); } else { return Result::ErrorClosed; @@ -541,29 +568,27 @@ Result AudioStreamAAudio::waitForStateChange(StreamState currentState, } ResultWithValue AudioStreamAAudio::setBufferSizeInFrames(int32_t requestedFrames) { + int32_t adjustedFrames = requestedFrames; + if (adjustedFrames > mBufferCapacityInFrames) { + adjustedFrames = mBufferCapacityInFrames; + } + // This calls getBufferSize() so avoid recursive lock. + adjustedFrames = QuirksManager::getInstance().clipBufferSize(*this, adjustedFrames); + std::shared_lock lock(mAAudioStreamLock); AAudioStream *stream = mAAudioStream.load(); - if (stream != nullptr) { - int32_t adjustedFrames = requestedFrames; - if (adjustedFrames > mBufferCapacityInFrames) { - adjustedFrames = mBufferCapacityInFrames; - } - 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; - return ResultWithValue::createBasedOnSign(newBufferSize); - } else { return ResultWithValue(Result::ErrorClosed); } } -StreamState AudioStreamAAudio::getState() const { +StreamState AudioStreamAAudio::getState() { + std::shared_lock lock(mAAudioStreamLock); AAudioStream *stream = mAAudioStream.load(); if (stream != nullptr) { aaudio_stream_state_t aaudioState = mLibLoader->stream_getState(stream); @@ -580,6 +605,7 @@ StreamState AudioStreamAAudio::getState() const { } int32_t AudioStreamAAudio::getBufferSizeInFrames() { + std::shared_lock lock(mAAudioStreamLock); AAudioStream *stream = mAAudioStream.load(); if (stream != nullptr) { mBufferSizeInFrames = mLibLoader->stream_getBufferSize(stream); @@ -587,29 +613,34 @@ int32_t AudioStreamAAudio::getBufferSizeInFrames() { return mBufferSizeInFrames; } -int32_t AudioStreamAAudio::getFramesPerBurst() { - AAudioStream *stream = mAAudioStream.load(); - if (stream != nullptr) { - mFramesPerBurst = mLibLoader->stream_getFramesPerBurst(stream); - } - return mFramesPerBurst; -} - void AudioStreamAAudio::updateFramesRead() { + std::shared_lock lock(mAAudioStreamLock); AAudioStream *stream = mAAudioStream.load(); +// Set to 1 for debugging race condition #1180 with mAAudioStream. +// See also DEBUG_CLOSE_RACE in OboeTester. +// This was left in the code so that we could test the fix again easily in the future. +// We could not trigger the race condition without adding these get calls and the sleeps. +#define DEBUG_CLOSE_RACE 0 +#if DEBUG_CLOSE_RACE + // This is used when testing race conditions with close(). + // See DEBUG_CLOSE_RACE in OboeTester + AudioClock::sleepForNanos(400 * kNanosPerMillisecond); +#endif // DEBUG_CLOSE_RACE if (stream != nullptr) { mFramesRead = mLibLoader->stream_getFramesRead(stream); } } void AudioStreamAAudio::updateFramesWritten() { + std::shared_lock lock(mAAudioStreamLock); AAudioStream *stream = mAAudioStream.load(); if (stream != nullptr) { mFramesWritten = mLibLoader->stream_getFramesWritten(stream); } } -ResultWithValue AudioStreamAAudio::getXRunCount() const { +ResultWithValue AudioStreamAAudio::getXRunCount() { + std::shared_lock lock(mAAudioStreamLock); AAudioStream *stream = mAAudioStream.load(); if (stream != nullptr) { return ResultWithValue::createBasedOnSign(mLibLoader->stream_getXRunCount(stream)); @@ -621,11 +652,12 @@ ResultWithValue AudioStreamAAudio::getXRunCount() const { Result AudioStreamAAudio::getTimestamp(clockid_t clockId, int64_t *framePosition, int64_t *timeNanoseconds) { + if (getState() != StreamState::Started) { + return Result::ErrorInvalidState; + } + std::shared_lock lock(mAAudioStreamLock); AAudioStream *stream = mAAudioStream.load(); if (stream != nullptr) { - if (getState() != StreamState::Started) { - return Result::ErrorInvalidState; - } return static_cast(mLibLoader->stream_getTimestamp(stream, clockId, framePosition, timeNanoseconds)); } else { @@ -634,11 +666,6 @@ Result AudioStreamAAudio::getTimestamp(clockid_t clockId, } ResultWithValue AudioStreamAAudio::calculateLatencyMillis() { - AAudioStream *stream = mAAudioStream.load(); - if (stream == nullptr) { - return ResultWithValue(Result::ErrorClosed); - } - // Get the time that a known audio frame was presented. int64_t hardwareFrameIndex; int64_t hardwareFrameHardwareTime; @@ -676,6 +703,7 @@ ResultWithValue AudioStreamAAudio::calculateLatencyMillis() { } bool AudioStreamAAudio::isMMapUsed() { + std::shared_lock lock(mAAudioStreamLock); AAudioStream *stream = mAAudioStream.load(); if (stream != nullptr) { return AAudioExtensions::getInstance().isMMapUsed(stream); 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 83f4b2fe16..e71efe85c8 100644 --- a/modules/juce_audio_devices/native/oboe/src/aaudio/AudioStreamAAudio.h +++ b/modules/juce_audio_devices/native/oboe/src/aaudio/AudioStreamAAudio.h @@ -18,6 +18,7 @@ #define OBOE_STREAM_AAUDIO_H_ #include +#include #include #include @@ -67,8 +68,7 @@ public: ResultWithValue setBufferSizeInFrames(int32_t requestedFrames) override; int32_t getBufferSizeInFrames() override; - int32_t getFramesPerBurst() override; - ResultWithValue getXRunCount() const override; + ResultWithValue getXRunCount() override; bool isXRunCountSupported() const override { return true; } ResultWithValue calculateLatencyMillis() override; @@ -81,7 +81,7 @@ public: int64_t *framePosition, int64_t *timeNanoseconds) override; - StreamState getState() const override; + StreamState getState() override; AudioApi getAudioApi() const override { return AudioApi::AAudio; @@ -112,14 +112,21 @@ private: // Must call under mLock. And stream must NOT be nullptr. Result requestStop_l(AAudioStream *stream); + /** + * Launch a thread that will stop the stream. + */ + void launchStopThread(); + // Time to sleep in order to prevent a race condition with a callback after a close(). // Two milliseconds may be enough but 10 msec is even safer. static constexpr int kDelayBeforeCloseMillis = 10; std::atomic mCallbackThreadEnabled; + std::atomic mStopThreadAllowed{false}; // pointer to the underlying 'C' AAudio stream, valid if open, null if closed std::atomic mAAudioStream{nullptr}; + std::shared_mutex mAAudioStreamLock; // to protect mAAudioStream while closing static AAudioLoader *mLibLoader; 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 8a9cd452c2..66d0711724 100644 --- a/modules/juce_audio_devices/native/oboe/src/common/AudioStream.cpp +++ b/modules/juce_audio_devices/native/oboe/src/common/AudioStream.cpp @@ -55,7 +55,7 @@ void AudioStream::checkScheduler() { 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. + return DataCallbackResult::Stop; // Should not be getting called } DataCallbackResult result; @@ -196,16 +196,4 @@ ResultWithValue AudioStream::getTimestamp(clockid_t clockId) { } } -static void oboe_stop_thread_proc(AudioStream *oboeStream) { - if (oboeStream != nullptr) { - oboeStream->requestStop(); - } -} - -void AudioStream::launchStopThread() { - // Stop this stream on a separate thread - std::thread t(oboe_stop_thread_proc, this); - t.detach(); -} - } // namespace oboe 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 b9f04b8512..5dbe38cc82 100644 --- a/modules/juce_audio_devices/native/oboe/src/common/AudioStreamBuilder.cpp +++ b/modules/juce_audio_devices/native/oboe/src/common/AudioStreamBuilder.cpp @@ -91,6 +91,7 @@ bool AudioStreamBuilder::isCompatible(AudioStreamBase &other) { Result AudioStreamBuilder::openStream(AudioStream **streamPP) { auto result = isValidConfig(); if (result != Result::OK) { + LOGW("%s() invalid config %d", __func__, result); return result; } @@ -202,24 +203,16 @@ Result AudioStreamBuilder::openStream(AudioStream **streamPP) { Result AudioStreamBuilder::openManagedStream(oboe::ManagedStream &stream) { stream.reset(); - auto result = isValidConfig(); - if (result != Result::OK) { - return result; - } AudioStream *streamptr; - result = openStream(&streamptr); + auto result = openStream(&streamptr); stream.reset(streamptr); return result; } Result AudioStreamBuilder::openStream(std::shared_ptr &sharedStream) { sharedStream.reset(); - auto result = isValidConfig(); - if (result != Result::OK) { - return result; - } AudioStream *streamptr; - result = openStream(&streamptr); + auto result = openStream(&streamptr); if (result == Result::OK) { sharedStream.reset(streamptr); // Save a weak_ptr in the stream for use with callbacks. 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 2829e6f549..2177685c58 100644 --- a/modules/juce_audio_devices/native/oboe/src/common/DataConversionFlowGraph.cpp +++ b/modules/juce_audio_devices/native/oboe/src/common/DataConversionFlowGraph.cpp @@ -20,6 +20,8 @@ #include "DataConversionFlowGraph.h" #include "SourceFloatCaller.h" #include "SourceI16Caller.h" +#include "SourceI24Caller.h" +#include "SourceI32Caller.h" #include #include @@ -28,9 +30,11 @@ #include #include #include +#include #include #include #include +#include #include using namespace oboe; @@ -116,6 +120,14 @@ Result DataConversionFlowGraph::configure(AudioStream *sourceStream, AudioStream mSourceCaller = std::make_unique(sourceChannelCount, actualSourceFramesPerCallback); break; + case AudioFormat::I24: + mSourceCaller = std::make_unique(sourceChannelCount, + actualSourceFramesPerCallback); + break; + case AudioFormat::I32: + mSourceCaller = std::make_unique(sourceChannelCount, + actualSourceFramesPerCallback); + break; default: LOGE("%s() Unsupported source caller format = %d", __func__, sourceFormat); return Result::ErrorIllegalArgument; @@ -132,6 +144,12 @@ Result DataConversionFlowGraph::configure(AudioStream *sourceStream, AudioStream case AudioFormat::I16: mSource = std::make_unique(sourceChannelCount); break; + case AudioFormat::I24: + mSource = std::make_unique(sourceChannelCount); + break; + case AudioFormat::I32: + mSource = std::make_unique(sourceChannelCount); + break; default: LOGE("%s() Unsupported source format = %d", __func__, sourceFormat); return Result::ErrorIllegalArgument; @@ -202,6 +220,12 @@ Result DataConversionFlowGraph::configure(AudioStream *sourceStream, AudioStream case AudioFormat::I16: mSink = std::make_unique(sinkChannelCount); break; + case AudioFormat::I24: + mSink = std::make_unique(sinkChannelCount); + break; + case AudioFormat::I32: + mSink = std::make_unique(sinkChannelCount); + break; default: LOGE("%s() Unsupported sink format = %d", __func__, sinkFormat); return Result::ErrorIllegalArgument;; 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 975b03ddd6..5d4e693489 100644 --- a/modules/juce_audio_devices/native/oboe/src/common/FilterAudioStream.cpp +++ b/modules/juce_audio_devices/native/oboe/src/common/FilterAudioStream.cpp @@ -48,7 +48,7 @@ Result FilterAudioStream::configureFlowGraph() { AudioStream *sourceStream = isOutput ? this : mChildStream.get(); AudioStream *sinkStream = isOutput ? mChildStream.get() : this; - mRateScaler = ((double) sourceStream->getSampleRate()) / sinkStream->getSampleRate(); + mRateScaler = ((double) getSampleRate()) / mChildStream->getSampleRate(); return mFlowGraph->configure(sourceStream, sinkStream); } 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 5428db5b9c..2f50e3ec8f 100644 --- a/modules/juce_audio_devices/native/oboe/src/common/FilterAudioStream.h +++ b/modules/juce_audio_devices/native/oboe/src/common/FilterAudioStream.h @@ -56,6 +56,8 @@ public: mBufferCapacityInFrames = mChildStream->getBufferCapacityInFrames(); mPerformanceMode = mChildStream->getPerformanceMode(); mInputPreset = mChildStream->getInputPreset(); + mFramesPerBurst = mChildStream->getFramesPerBurst(); + mDeviceId = mChildStream->getDeviceId(); } virtual ~FilterAudioStream() = default; @@ -113,7 +115,7 @@ public: int32_t numFrames, int64_t timeoutNanoseconds) override; - StreamState getState() const override { + StreamState getState() override { return mChildStream->getState(); } @@ -128,10 +130,6 @@ public: return mChildStream->isXRunCountSupported(); } - int32_t getFramesPerBurst() override { - return mChildStream->getFramesPerBurst(); - } - AudioApi getAudioApi() const override { return mChildStream->getAudioApi(); } @@ -159,7 +157,7 @@ public: return mBufferSizeInFrames; } - ResultWithValue getXRunCount() const override { + ResultWithValue getXRunCount() override { return mChildStream->getXRunCount(); } @@ -184,20 +182,20 @@ public: void *audioData, int32_t numFrames) override; - bool onError(AudioStream * audioStream, Result error) override { + bool onError(AudioStream * /*audioStream*/, Result error) override { if (mErrorCallback != nullptr) { return mErrorCallback->onError(this, error); } return false; } - void onErrorBeforeClose(AudioStream *oboeStream, Result error) override { + void onErrorBeforeClose(AudioStream * /*oboeStream*/, Result error) override { if (mErrorCallback != nullptr) { mErrorCallback->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 (mErrorCallback != nullptr) { 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 5d35c71b3e..594589c78b 100644 --- a/modules/juce_audio_devices/native/oboe/src/common/QuirksManager.cpp +++ b/modules/juce_audio_devices/native/oboe/src/common/QuirksManager.cpp @@ -71,6 +71,7 @@ public: std::string chipname = getPropertyString("ro.hardware.chipname"); isExynos9810 = (chipname == "exynos9810"); isExynos990 = (chipname == "exynos990"); + isExynos850 = (chipname == "exynos850"); mBuildChangelist = getPropertyInteger("ro.build.changelist", 0); } @@ -86,9 +87,9 @@ public: return kTopMargin; } - // See Oboe issue #824 for more information. + // See Oboe issues #824 and #1247 for more information. bool isMonoMMapActuallyStereo() const override { - return isExynos9810; // TODO We can make this version specific if it gets fixed. + return isExynos9810 || isExynos850; // TODO We can make this version specific if it gets fixed. } bool isAAudioMMapPossible(const AudioStreamBuilder &builder) const override { @@ -102,10 +103,24 @@ public: // This detects b/159066712 , S20 LSI has corrupt low latency audio recording // and turns off MMAP. // See also https://github.com/google/oboe/issues/892 - bool mRecordingCorrupted = isInput + bool isRecordingCorrupted = isInput && isExynos990 && mBuildChangelist < 19350896; - return !mRecordingCorrupted; + + // Certain S9+ builds record silence when using MMAP and not using the VoiceCommunication + // preset. + // See https://github.com/google/oboe/issues/1110 + bool wouldRecordSilence = isInput + && isExynos9810 + && mBuildChangelist <= 18847185 + && (builder.getInputPreset() != InputPreset::VoiceCommunication); + + if (wouldRecordSilence){ + LOGI("QuirksManager::%s() Requested stream configuration would result in silence on " + "this device. Switching off MMAP.", __func__); + } + + return !isRecordingCorrupted && !wouldRecordSilence; } private: @@ -116,6 +131,7 @@ private: bool isExynos = false; bool isExynos9810 = false; bool isExynos990 = false; + bool isExynos850 = false; int mBuildChangelist = 0; }; @@ -182,6 +198,19 @@ bool QuirksManager::isConversionNeeded( LOGI("QuirksManager::%s() forcing internal format to I16 for low latency", __func__); } + // Add quirk for float output on API <21 + if (isFloat + && !isInput + && getSdkVersion() < __ANDROID_API_L__ + && builder.isFormatConversionAllowed() + ) { + childBuilder.setFormat(AudioFormat::I16); + conversionNeeded = true; + LOGI("QuirksManager::%s() float was requested but not supported on pre-L devices, " + "creating an underlying I16 stream and using format conversion to provide a float " + "stream", __func__); + } + // Channel Count conversions if (OboeGlobals::areWorkaroundsEnabled() && builder.isChannelConversionAllowed() diff --git a/modules/juce_audio_devices/native/oboe/src/common/SourceI24Caller.cpp b/modules/juce_audio_devices/native/oboe/src/common/SourceI24Caller.cpp new file mode 100644 index 0000000000..2e44c3d2fb --- /dev/null +++ b/modules/juce_audio_devices/native/oboe/src/common/SourceI24Caller.cpp @@ -0,0 +1,56 @@ +/* + * 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 +#include +#include "flowgraph/FlowGraphNode.h" +#include "SourceI24Caller.h" + +#if FLOWGRAPH_ANDROID_INTERNAL +#include +#endif + +using namespace oboe; +using namespace flowgraph; + +int32_t SourceI24Caller::onProcess(int32_t numFrames) { + int32_t numBytes = mStream->getBytesPerFrame() * numFrames; + int32_t bytesRead = mBlockReader.read((uint8_t *) mConversionBuffer.get(), numBytes); + int32_t framesRead = bytesRead / mStream->getBytesPerFrame(); + + float *floatData = output.getBuffer(); + const uint8_t *byteData = mConversionBuffer.get(); + int32_t numSamples = framesRead * output.getSamplesPerFrame(); + +#if FLOWGRAPH_ANDROID_INTERNAL + memcpy_to_float_from_p24(floatData, byteData, numSamples); +#else + static const float scale = 1. / (float)(1UL << 31); + for (int i = 0; i < numSamples; i++) { + // Assemble the data assuming Little Endian format. + int32_t pad = byteData[2]; + pad <<= 8; + pad |= byteData[1]; + pad <<= 8; + pad |= byteData[0]; + pad <<= 8; // Shift to 32 bit data so the sign is correct. + byteData += kBytesPerI24Packed; + *floatData++ = pad * scale; // scale to range -1.0 to 1.0 + } +#endif + + return framesRead; +} diff --git a/modules/juce_audio_devices/native/oboe/src/common/SourceI24Caller.h b/modules/juce_audio_devices/native/oboe/src/common/SourceI24Caller.h new file mode 100644 index 0000000000..f8c8e115fb --- /dev/null +++ b/modules/juce_audio_devices/native/oboe/src/common/SourceI24Caller.h @@ -0,0 +1,52 @@ +/* + * 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 OBOE_SOURCE_I24_CALLER_H +#define OBOE_SOURCE_I24_CALLER_H + +#include +#include + +#include "flowgraph/FlowGraphNode.h" +#include "AudioSourceCaller.h" +#include "FixedBlockReader.h" + +namespace oboe { + +/** + * AudioSource that uses callback to get more data. + */ +class SourceI24Caller : public AudioSourceCaller { +public: + SourceI24Caller(int32_t channelCount, int32_t framesPerCallback) + : AudioSourceCaller(channelCount, framesPerCallback, kBytesPerI24Packed) { + mConversionBuffer = std::make_unique( + kBytesPerI24Packed * channelCount * output.getFramesPerBuffer()); + } + + int32_t onProcess(int32_t numFrames) override; + + const char *getName() override { + return "SourceI24Caller"; + } + +private: + std::unique_ptr mConversionBuffer; + static constexpr int kBytesPerI24Packed = 3; +}; + +} +#endif //OBOE_SOURCE_I16_CALLER_H diff --git a/modules/juce_audio_devices/native/oboe/src/common/SourceI32Caller.cpp b/modules/juce_audio_devices/native/oboe/src/common/SourceI32Caller.cpp new file mode 100644 index 0000000000..188bd47cad --- /dev/null +++ b/modules/juce_audio_devices/native/oboe/src/common/SourceI32Caller.cpp @@ -0,0 +1,47 @@ +/* + * 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 +#include +#include "flowgraph/FlowGraphNode.h" +#include "SourceI32Caller.h" + +#if FLOWGRAPH_ANDROID_INTERNAL +#include +#endif + +using namespace oboe; +using namespace flowgraph; + +int32_t SourceI32Caller::onProcess(int32_t numFrames) { + int32_t numBytes = mStream->getBytesPerFrame() * numFrames; + int32_t bytesRead = mBlockReader.read((uint8_t *) mConversionBuffer.get(), numBytes); + int32_t framesRead = bytesRead / mStream->getBytesPerFrame(); + + float *floatData = output.getBuffer(); + const int32_t *intData = mConversionBuffer.get(); + int32_t numSamples = framesRead * output.getSamplesPerFrame(); + +#if FLOWGRAPH_ANDROID_INTERNAL + memcpy_to_float_from_i32(floatData, shortData, numSamples); +#else + for (int i = 0; i < numSamples; i++) { + *floatData++ = *intData++ * kScale; + } +#endif + + return framesRead; +} diff --git a/modules/juce_audio_devices/native/oboe/src/common/SourceI32Caller.h b/modules/juce_audio_devices/native/oboe/src/common/SourceI32Caller.h new file mode 100644 index 0000000000..d802ccc7ae --- /dev/null +++ b/modules/juce_audio_devices/native/oboe/src/common/SourceI32Caller.h @@ -0,0 +1,52 @@ +/* + * 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 OBOE_SOURCE_I32_CALLER_H +#define OBOE_SOURCE_I32_CALLER_H + +#include +#include +#include + +#include "flowgraph/FlowGraphNode.h" +#include "AudioSourceCaller.h" +#include "FixedBlockReader.h" + +namespace oboe { + +/** + * AudioSource that uses callback to get more data. + */ +class SourceI32Caller : public AudioSourceCaller { +public: + SourceI32Caller(int32_t channelCount, int32_t framesPerCallback) + : AudioSourceCaller(channelCount, framesPerCallback, sizeof(int32_t)) { + mConversionBuffer = std::make_unique(channelCount * output.getFramesPerBuffer()); + } + + int32_t onProcess(int32_t numFrames) override; + + const char *getName() override { + return "SourceI32Caller"; + } + +private: + std::unique_ptr mConversionBuffer; + static constexpr float kScale = 1.0 / (1UL << 31); +}; + +} +#endif //OBOE_SOURCE_I32_CALLER_H 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 d38874b53d..177c544b3e 100644 --- a/modules/juce_audio_devices/native/oboe/src/common/Utilities.cpp +++ b/modules/juce_audio_devices/native/oboe/src/common/Utilities.cpp @@ -60,6 +60,12 @@ int32_t convertFormatToSizeInBytes(AudioFormat format) { case AudioFormat::Float: size = sizeof(float); break; + case AudioFormat::I24: + size = 3; // packed 24-bit data + break; + case AudioFormat::I32: + size = sizeof(int32_t); + break; default: break; } @@ -98,6 +104,8 @@ const char *convertToText(AudioFormat format) { case AudioFormat::Unspecified: return "Unspecified"; case AudioFormat::I16: return "I16"; case AudioFormat::Float: return "Float"; + case AudioFormat::I24: return "I24"; + case AudioFormat::I32: return "I32"; default: return "Unrecognized format"; } } diff --git a/modules/juce_audio_devices/native/oboe/src/flowgraph/FlowgraphUtilities.h b/modules/juce_audio_devices/native/oboe/src/flowgraph/FlowgraphUtilities.h new file mode 100644 index 0000000000..ce2bc82faa --- /dev/null +++ b/modules/juce_audio_devices/native/oboe/src/flowgraph/FlowgraphUtilities.h @@ -0,0 +1,55 @@ +/* + * Copyright 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 FLOWGRAPH_UTILITIES_H +#define FLOWGRAPH_UTILITIES_H + +#include + +using namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph; + +class FlowgraphUtilities { +public: +// This was copied from audio_utils/primitives.h +/** + * Convert a single-precision floating point value to a Q0.31 integer value. + * Rounds to nearest, ties away from 0. + * + * Values outside the range [-1.0, 1.0) are properly clamped to -2147483648 and 2147483647, + * including -Inf and +Inf. NaN values are considered undefined, and behavior may change + * depending on hardware and future implementation of this function. + */ +static int32_t clamp32FromFloat(float f) +{ + static const float scale = (float)(1UL << 31); + static const float limpos = 1.; + static const float limneg = -1.; + + if (f <= limneg) { + return -0x80000000; /* or 0x80000000 */ + } else if (f >= limpos) { + return 0x7fffffff; + } + f *= scale; + /* integer conversion is through truncation (though int to float is not). + * ensure that we round to nearest, ties away from 0. + */ + return f > 0 ? f + 0.5 : f - 0.5; +} + +}; + +#endif // FLOWGRAPH_UTILITIES_H diff --git a/modules/juce_audio_devices/native/oboe/src/flowgraph/ManyToMultiConverter.h b/modules/juce_audio_devices/native/oboe/src/flowgraph/ManyToMultiConverter.h index c60e0704b8..21a95df5f9 100644 --- a/modules/juce_audio_devices/native/oboe/src/flowgraph/ManyToMultiConverter.h +++ b/modules/juce_audio_devices/native/oboe/src/flowgraph/ManyToMultiConverter.h @@ -37,7 +37,7 @@ public: int32_t onProcess(int numFrames) override; - void setEnabled(bool enabled) {} + void setEnabled(bool /*enabled*/) {} std::vector> inputs; flowgraph::FlowGraphPortFloatOutput output; diff --git a/modules/juce_audio_devices/native/oboe/src/flowgraph/RampLinear.cpp b/modules/juce_audio_devices/native/oboe/src/flowgraph/RampLinear.cpp index 33b2712c80..80ac72ad47 100644 --- a/modules/juce_audio_devices/native/oboe/src/flowgraph/RampLinear.cpp +++ b/modules/juce_audio_devices/native/oboe/src/flowgraph/RampLinear.cpp @@ -32,6 +32,10 @@ void RampLinear::setLengthInFrames(int32_t frames) { void RampLinear::setTarget(float target) { mTarget.store(target); + // If the ramp has not been used then start immediately at this level. + if (mLastCallCount == kInitialCallCount) { + forceCurrent(target); + } } float RampLinear::interpolateCurrent() { diff --git a/modules/juce_audio_devices/native/oboe/src/flowgraph/SinkFloat.cpp b/modules/juce_audio_devices/native/oboe/src/flowgraph/SinkFloat.cpp index e0ac6e9318..1242f5f2f7 100644 --- a/modules/juce_audio_devices/native/oboe/src/flowgraph/SinkFloat.cpp +++ b/modules/juce_audio_devices/native/oboe/src/flowgraph/SinkFloat.cpp @@ -28,7 +28,7 @@ SinkFloat::SinkFloat(int32_t channelCount) int32_t SinkFloat::read(void *data, int32_t numFrames) { // printf("SinkFloat::read(,,%d)\n", numFrames); float *floatData = (float *) data; - int32_t channelCount = input.getSamplesPerFrame(); + const int32_t channelCount = input.getSamplesPerFrame(); int32_t framesLeft = numFrames; while (framesLeft > 0) { diff --git a/modules/juce_audio_devices/native/oboe/src/flowgraph/SinkFloat.h b/modules/juce_audio_devices/native/oboe/src/flowgraph/SinkFloat.h index 98d218207c..c3f26bdfb0 100644 --- a/modules/juce_audio_devices/native/oboe/src/flowgraph/SinkFloat.h +++ b/modules/juce_audio_devices/native/oboe/src/flowgraph/SinkFloat.h @@ -32,6 +32,7 @@ namespace flowgraph { class SinkFloat : public FlowGraphSink { public: explicit SinkFloat(int32_t channelCount); + ~SinkFloat() override = default; int32_t read(void *data, int32_t numFrames) override; diff --git a/modules/juce_audio_devices/native/oboe/src/flowgraph/SinkI32.cpp b/modules/juce_audio_devices/native/oboe/src/flowgraph/SinkI32.cpp new file mode 100644 index 0000000000..504ae47150 --- /dev/null +++ b/modules/juce_audio_devices/native/oboe/src/flowgraph/SinkI32.cpp @@ -0,0 +1,55 @@ +/* + * Copyright 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. + */ + +#if FLOWGRAPH_ANDROID_INTERNAL +#include +#endif + +#include "FlowGraphNode.h" +#include "FlowgraphUtilities.h" +#include "SinkI32.h" + +using namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph; + +SinkI32::SinkI32(int32_t channelCount) + : FlowGraphSink(channelCount) {} + +int32_t SinkI32::read(void *data, int32_t numFrames) { + int32_t *intData = (int32_t *) data; + const int32_t channelCount = input.getSamplesPerFrame(); + + int32_t framesLeft = numFrames; + while (framesLeft > 0) { + // Run the graph and pull data through the input port. + int32_t framesRead = pullData(framesLeft); + if (framesRead <= 0) { + break; + } + const float *signal = input.getBuffer(); + int32_t numSamples = framesRead * channelCount; +#if FLOWGRAPH_ANDROID_INTERNAL + memcpy_to_i32_from_float(intData, signal, numSamples); + intData += numSamples; + signal += numSamples; +#else + for (int i = 0; i < numSamples; i++) { + *intData++ = FlowgraphUtilities::clamp32FromFloat(*signal++); + } +#endif + framesLeft -= framesRead; + } + return numFrames - framesLeft; +} diff --git a/modules/juce_audio_devices/native/oboe/src/flowgraph/SinkI32.h b/modules/juce_audio_devices/native/oboe/src/flowgraph/SinkI32.h new file mode 100644 index 0000000000..6850b40dc5 --- /dev/null +++ b/modules/juce_audio_devices/native/oboe/src/flowgraph/SinkI32.h @@ -0,0 +1,42 @@ +/* + * Copyright 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 FLOWGRAPH_SINK_I32_H +#define FLOWGRAPH_SINK_I32_H + +#include + +#include "FlowGraphNode.h" + +namespace FLOWGRAPH_OUTER_NAMESPACE { +namespace flowgraph { + +class SinkI32 : public FlowGraphSink { +public: + explicit SinkI32(int32_t channelCount); + ~SinkI32() override = default; + + int32_t read(void *data, int32_t numFrames) override; + + const char *getName() override { + return "SinkI32"; + } +}; + +} /* namespace flowgraph */ +} /* namespace FLOWGRAPH_OUTER_NAMESPACE */ + +#endif //FLOWGRAPH_SINK_I32_H diff --git a/modules/juce_audio_devices/native/oboe/src/flowgraph/SourceFloat.cpp b/modules/juce_audio_devices/native/oboe/src/flowgraph/SourceFloat.cpp index 3161ec1843..a27d43b7f8 100644 --- a/modules/juce_audio_devices/native/oboe/src/flowgraph/SourceFloat.cpp +++ b/modules/juce_audio_devices/native/oboe/src/flowgraph/SourceFloat.cpp @@ -28,11 +28,11 @@ SourceFloat::SourceFloat(int32_t channelCount) int32_t SourceFloat::onProcess(int32_t numFrames) { float *outputBuffer = output.getBuffer(); - int32_t channelCount = output.getSamplesPerFrame(); + const int32_t channelCount = output.getSamplesPerFrame(); - int32_t framesLeft = mSizeInFrames - mFrameIndex; - int32_t framesToProcess = std::min(numFrames, framesLeft); - int32_t numSamples = framesToProcess * channelCount; + const int32_t framesLeft = mSizeInFrames - mFrameIndex; + const int32_t framesToProcess = std::min(numFrames, framesLeft); + const int32_t numSamples = framesToProcess * channelCount; const float *floatBase = (float *) mData; const float *floatData = &floatBase[mFrameIndex * channelCount]; diff --git a/modules/juce_audio_devices/native/oboe/src/flowgraph/SourceFloat.h b/modules/juce_audio_devices/native/oboe/src/flowgraph/SourceFloat.h index d191c4c62f..cba67581c1 100644 --- a/modules/juce_audio_devices/native/oboe/src/flowgraph/SourceFloat.h +++ b/modules/juce_audio_devices/native/oboe/src/flowgraph/SourceFloat.h @@ -31,6 +31,7 @@ namespace flowgraph { class SourceFloat : public FlowGraphSourceBuffered { public: explicit SourceFloat(int32_t channelCount); + ~SourceFloat() override = default; int32_t onProcess(int32_t numFrames) override; diff --git a/modules/juce_audio_devices/native/oboe/src/flowgraph/SourceI32.cpp b/modules/juce_audio_devices/native/oboe/src/flowgraph/SourceI32.cpp new file mode 100644 index 0000000000..e4b8041848 --- /dev/null +++ b/modules/juce_audio_devices/native/oboe/src/flowgraph/SourceI32.cpp @@ -0,0 +1,54 @@ +/* + * Copyright 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 +#include + +#if FLOWGRAPH_ANDROID_INTERNAL +#include +#endif + +#include "FlowGraphNode.h" +#include "SourceI32.h" + +using namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph; + +SourceI32::SourceI32(int32_t channelCount) + : FlowGraphSourceBuffered(channelCount) { +} + +int32_t SourceI32::onProcess(int32_t numFrames) { + float *floatData = output.getBuffer(); + const int32_t channelCount = output.getSamplesPerFrame(); + + const int32_t framesLeft = mSizeInFrames - mFrameIndex; + const int32_t framesToProcess = std::min(numFrames, framesLeft); + const int32_t numSamples = framesToProcess * channelCount; + + const int32_t *intBase = static_cast(mData); + const int32_t *intData = &intBase[mFrameIndex * channelCount]; + +#if FLOWGRAPH_ANDROID_INTERNAL + memcpy_to_float_from_i32(floatData, intData, numSamples); +#else + for (int i = 0; i < numSamples; i++) { + *floatData++ = *intData++ * kScale; + } +#endif + + mFrameIndex += framesToProcess; + return framesToProcess; +} diff --git a/modules/juce_audio_devices/native/oboe/src/flowgraph/SourceI32.h b/modules/juce_audio_devices/native/oboe/src/flowgraph/SourceI32.h new file mode 100644 index 0000000000..7ba29e9b23 --- /dev/null +++ b/modules/juce_audio_devices/native/oboe/src/flowgraph/SourceI32.h @@ -0,0 +1,44 @@ +/* + * Copyright 2018 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 FLOWGRAPH_SOURCE_I32_H +#define FLOWGRAPH_SOURCE_I32_H + +#include + +#include "FlowGraphNode.h" + +namespace FLOWGRAPH_OUTER_NAMESPACE { +namespace flowgraph { + +class SourceI32 : public FlowGraphSourceBuffered { +public: + explicit SourceI32(int32_t channelCount); + ~SourceI32() override = default; + + int32_t onProcess(int32_t numFrames) override; + + const char *getName() override { + return "SourceI32"; + } +private: + static constexpr float kScale = 1.0 / (1UL << 31); +}; + +} /* namespace flowgraph */ +} /* namespace FLOWGRAPH_OUTER_NAMESPACE */ + +#endif //FLOWGRAPH_SOURCE_I32_H diff --git a/modules/juce_audio_devices/native/oboe/src/opensles/AudioStreamBuffered.h b/modules/juce_audio_devices/native/oboe/src/opensles/AudioStreamBuffered.h index d7e88850d4..be5d607e30 100644 --- a/modules/juce_audio_devices/native/oboe/src/opensles/AudioStreamBuffered.h +++ b/modules/juce_audio_devices/native/oboe/src/opensles/AudioStreamBuffered.h @@ -49,7 +49,7 @@ public: int32_t getBufferCapacityInFrames() const override; - ResultWithValue getXRunCount() const override { + ResultWithValue getXRunCount() override { return ResultWithValue(mXRunCount); } 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 2b69935252..b99568ae6c 100644 --- a/modules/juce_audio_devices/native/oboe/src/opensles/AudioStreamOpenSLES.cpp +++ b/modules/juce_audio_devices/native/oboe/src/opensles/AudioStreamOpenSLES.cpp @@ -354,10 +354,6 @@ SLresult AudioStreamOpenSLES::registerBufferQueueCallback() { return result; } -int32_t AudioStreamOpenSLES::getFramesPerBurst() { - return mFramesPerBurst; -} - int64_t AudioStreamOpenSLES::getFramesProcessedByServer() { updateServiceFrameCounter(); int64_t millis64 = mPositionMillis.get(); 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 10a730b704..ebed3e5466 100644 --- a/modules/juce_audio_devices/native/oboe/src/opensles/AudioStreamOpenSLES.h +++ b/modules/juce_audio_devices/native/oboe/src/opensles/AudioStreamOpenSLES.h @@ -56,10 +56,7 @@ public: * * @return state or a negative error. */ - StreamState getState() const override { return mState.load(); } - - int32_t getFramesPerBurst() override; - + StreamState getState() override { return mState.load(); } AudioApi getAudioApi() const override { return AudioApi::OpenSLES; diff --git a/modules/juce_audio_devices/native/oboe/src/opensles/OpenSLESUtilities.cpp b/modules/juce_audio_devices/native/oboe/src/opensles/OpenSLESUtilities.cpp index 4be23b3785..6d25e79fdb 100644 --- a/modules/juce_audio_devices/native/oboe/src/opensles/OpenSLESUtilities.cpp +++ b/modules/juce_audio_devices/native/oboe/src/opensles/OpenSLESUtilities.cpp @@ -83,6 +83,8 @@ SLuint32 OpenSLES_ConvertFormatToRepresentation(AudioFormat format) { return SL_ANDROID_PCM_REPRESENTATION_SIGNED_INT; case AudioFormat::Float: return SL_ANDROID_PCM_REPRESENTATION_FLOAT; + case AudioFormat::I24: + case AudioFormat::I32: case AudioFormat::Invalid: case AudioFormat::Unspecified: default: