mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-10 23:44:24 +00:00
Android: Update to Oboe release 1.6.1
This commit is contained in:
parent
39772b7474
commit
a6df818255
37 changed files with 742 additions and 121 deletions
|
|
@ -20,6 +20,8 @@ set (oboe_sources
|
||||||
src/common/LatencyTuner.cpp
|
src/common/LatencyTuner.cpp
|
||||||
src/common/SourceFloatCaller.cpp
|
src/common/SourceFloatCaller.cpp
|
||||||
src/common/SourceI16Caller.cpp
|
src/common/SourceI16Caller.cpp
|
||||||
|
src/common/SourceI24Caller.cpp
|
||||||
|
src/common/SourceI32Caller.cpp
|
||||||
src/common/Utilities.cpp
|
src/common/Utilities.cpp
|
||||||
src/common/QuirksManager.cpp
|
src/common/QuirksManager.cpp
|
||||||
src/fifo/FifoBuffer.cpp
|
src/fifo/FifoBuffer.cpp
|
||||||
|
|
@ -37,9 +39,11 @@ set (oboe_sources
|
||||||
src/flowgraph/SinkFloat.cpp
|
src/flowgraph/SinkFloat.cpp
|
||||||
src/flowgraph/SinkI16.cpp
|
src/flowgraph/SinkI16.cpp
|
||||||
src/flowgraph/SinkI24.cpp
|
src/flowgraph/SinkI24.cpp
|
||||||
|
src/flowgraph/SinkI32.cpp
|
||||||
src/flowgraph/SourceFloat.cpp
|
src/flowgraph/SourceFloat.cpp
|
||||||
src/flowgraph/SourceI16.cpp
|
src/flowgraph/SourceI16.cpp
|
||||||
src/flowgraph/SourceI24.cpp
|
src/flowgraph/SourceI24.cpp
|
||||||
|
src/flowgraph/SourceI32.cpp
|
||||||
src/flowgraph/resampler/IntegerRatio.cpp
|
src/flowgraph/resampler/IntegerRatio.cpp
|
||||||
src/flowgraph/resampler/LinearResampler.cpp
|
src/flowgraph/resampler/LinearResampler.cpp
|
||||||
src/flowgraph/resampler/MultiChannelResampler.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
|
# 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.
|
# 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.
|
# cmake-friendly way of specifying the language standard.
|
||||||
|
|
||||||
target_compile_options(oboe PRIVATE -Ofast)
|
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
|
# JUCE CHANGE ENDS HERE
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
The files in this directory are reproduced from the official Oboe repository, which can be found at
|
The files in this directory are reproduced from the official Oboe repository, which can be found at
|
||||||
github.com/google/oboe.
|
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
|
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.
|
library. Documentation, samples, tests, and other non-library items have been omitted.
|
||||||
|
|
@ -130,7 +130,7 @@ public:
|
||||||
*
|
*
|
||||||
* @return state or a negative error.
|
* @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.
|
* 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
|
* @return a result which is either Result::OK with the xRun count as the value, or a
|
||||||
* Result::Error* code
|
* Result::Error* code
|
||||||
*/
|
*/
|
||||||
virtual ResultWithValue<int32_t> getXRunCount() const {
|
virtual ResultWithValue<int32_t> getXRunCount() {
|
||||||
return ResultWithValue<int32_t>(Result::ErrorUnimplemented);
|
return ResultWithValue<int32_t>(Result::ErrorUnimplemented);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -205,7 +205,9 @@ public:
|
||||||
*
|
*
|
||||||
* @return burst size
|
* @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
|
* Get the number of bytes in each audio frame. This is calculated using the channel count
|
||||||
|
|
@ -372,11 +374,6 @@ public:
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Launch a thread that will stop the stream.
|
|
||||||
*/
|
|
||||||
void launchStopThread();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update mFramesWritten.
|
* Update mFramesWritten.
|
||||||
* For internal use only.
|
* For internal use only.
|
||||||
|
|
@ -537,6 +534,12 @@ protected:
|
||||||
|
|
||||||
oboe::Result mErrorCallbackResult = oboe::Result::OK;
|
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:
|
private:
|
||||||
|
|
||||||
// Log the scheduler if it changes.
|
// Log the scheduler if it changes.
|
||||||
|
|
@ -545,7 +548,6 @@ private:
|
||||||
|
|
||||||
std::atomic<bool> mDataCallbackEnabled{false};
|
std::atomic<bool> mDataCallbackEnabled{false};
|
||||||
std::atomic<bool> mErrorCallbackCalled{false};
|
std::atomic<bool> mErrorCallbackCalled{false};
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@
|
||||||
#define OBOE_STREAM_BASE_H_
|
#define OBOE_STREAM_BASE_H_
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
#include "oboe/AudioStreamCallback.h"
|
#include "oboe/AudioStreamCallback.h"
|
||||||
#include "oboe/Definitions.h"
|
#include "oboe/Definitions.h"
|
||||||
|
|
||||||
|
|
@ -196,11 +197,6 @@ protected:
|
||||||
int32_t mBufferCapacityInFrames = kUnspecified;
|
int32_t mBufferCapacityInFrames = kUnspecified;
|
||||||
/** Stream buffer size specified as a number of audio frames */
|
/** Stream buffer size specified as a number of audio frames */
|
||||||
int32_t mBufferSizeInFrames = kUnspecified;
|
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 */
|
/** Stream sharing mode */
|
||||||
SharingMode mSharingMode = SharingMode::Shared;
|
SharingMode mSharingMode = SharingMode::Shared;
|
||||||
|
|
@ -222,6 +218,11 @@ protected:
|
||||||
/** Stream session ID allocation strategy. Only active on Android 28+ */
|
/** Stream session ID allocation strategy. Only active on Android 28+ */
|
||||||
SessionId mSessionId = SessionId::None;
|
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.
|
// Control whether Oboe can convert channel counts to achieve optimal results.
|
||||||
bool mChannelConversionAllowed = false;
|
bool mChannelConversionAllowed = false;
|
||||||
// Control whether Oboe can convert data formats to achieve optimal results.
|
// Control whether Oboe can convert data formats to achieve optimal results.
|
||||||
|
|
@ -235,9 +236,12 @@ protected:
|
||||||
case AudioFormat::Unspecified:
|
case AudioFormat::Unspecified:
|
||||||
case AudioFormat::I16:
|
case AudioFormat::I16:
|
||||||
case AudioFormat::Float:
|
case AudioFormat::Float:
|
||||||
|
case AudioFormat::I24:
|
||||||
|
case AudioFormat::I32:
|
||||||
break;
|
break;
|
||||||
|
// JUCE CHANGE STARTS HERE
|
||||||
case AudioFormat::Invalid:
|
case AudioFormat::Invalid:
|
||||||
|
// JUCE CHANGE ENDS HERE
|
||||||
default:
|
default:
|
||||||
return Result::ErrorInvalidFormat;
|
return Result::ErrorInvalidFormat;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
* 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.
|
* 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) {
|
AudioStreamBuilder *setFormatConversionAllowed(bool allowed) {
|
||||||
mFormatConversionAllowed = allowed;
|
mFormatConversionAllowed = allowed;
|
||||||
|
|
@ -418,6 +418,38 @@ public:
|
||||||
return this;
|
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.
|
* @return true if AAudio will be used based on the current settings.
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,6 @@
|
||||||
#ifndef OBOE_DEFINITIONS_H
|
#ifndef OBOE_DEFINITIONS_H
|
||||||
#define OBOE_DEFINITIONS_H
|
#define OBOE_DEFINITIONS_H
|
||||||
|
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
|
|
@ -108,9 +107,36 @@ namespace oboe {
|
||||||
I16 = 1, // AAUDIO_FORMAT_PCM_I16,
|
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,
|
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,
|
Reserved8,
|
||||||
Reserved9,
|
Reserved9,
|
||||||
Reserved10,
|
Reserved10,
|
||||||
ErrorClosed,
|
ErrorClosed = -869,
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@
|
||||||
#define OBOE_VERSION_MAJOR 1
|
#define OBOE_VERSION_MAJOR 1
|
||||||
|
|
||||||
// Type: 8-bit unsigned int. Min value: 0 Max value: 255. See below for description.
|
// 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.
|
// Type: 16-bit unsigned int. Min value: 0 Max value: 65535. See below for description.
|
||||||
#define OBOE_VERSION_PATCH 1
|
#define OBOE_VERSION_PATCH 1
|
||||||
|
|
|
||||||
|
|
@ -83,6 +83,11 @@ int AAudioLoader::open() {
|
||||||
builder_setSessionId = load_V_PBI("AAudioStreamBuilder_setSessionId");
|
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");
|
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<signature_V_PBI>(proc);
|
return reinterpret_cast<signature_V_PBI>(proc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AAudioLoader::signature_V_PBCPH AAudioLoader::load_V_PBCPH(const char *functionName) {
|
||||||
|
void *proc = dlsym(mLibHandle, functionName);
|
||||||
|
AAudioLoader_check(proc, functionName);
|
||||||
|
return reinterpret_cast<signature_V_PBCPH>(proc);
|
||||||
|
}
|
||||||
|
|
||||||
AAudioLoader::signature_V_PBPDPV AAudioLoader::load_V_PBPDPV(const char *functionName) {
|
AAudioLoader::signature_V_PBPDPV AAudioLoader::load_V_PBPDPV(const char *functionName) {
|
||||||
void *proc = dlsym(mLibHandle, functionName);
|
void *proc = dlsym(mLibHandle, functionName);
|
||||||
AAudioLoader_check(proc, functionName);
|
AAudioLoader_check(proc, functionName);
|
||||||
|
|
|
||||||
|
|
@ -60,13 +60,16 @@ typedef int32_t aaudio_session_id_t;
|
||||||
#define AAUDIO_STREAM_STATE_STARTED static_cast<aaudio_stream_state_t>(StreamState::Started)
|
#define AAUDIO_STREAM_STATE_STARTED static_cast<aaudio_stream_state_t>(StreamState::Started)
|
||||||
#else
|
#else
|
||||||
#include <aaudio/AAudio.h>
|
#include <aaudio/AAudio.h>
|
||||||
#include <android/ndk-version.h>
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef __NDK_MAJOR__
|
#ifndef __NDK_MAJOR__
|
||||||
#define __NDK_MAJOR__ 0
|
#define __NDK_MAJOR__ 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef __ANDROID_API_S__
|
||||||
|
#define __ANDROID_API_S__ 31
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace oboe {
|
namespace oboe {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -98,6 +101,8 @@ class AAudioLoader {
|
||||||
// AAudioStreamBuilder_setSampleRate()
|
// AAudioStreamBuilder_setSampleRate()
|
||||||
typedef void (*signature_V_PBI)(AAudioStreamBuilder *, int32_t);
|
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 int32_t (*signature_I_PS)(AAudioStream *); // AAudioStream_getSampleRate()
|
||||||
typedef int64_t (*signature_L_PS)(AAudioStream *); // AAudioStream_getFramesRead()
|
typedef int64_t (*signature_L_PS)(AAudioStream *); // AAudioStream_getFramesRead()
|
||||||
// AAudioStream_setBufferSizeInFrames()
|
// AAudioStream_setBufferSizeInFrames()
|
||||||
|
|
@ -160,6 +165,9 @@ class AAudioLoader {
|
||||||
signature_V_PBI builder_setInputPreset = nullptr;
|
signature_V_PBI builder_setInputPreset = nullptr;
|
||||||
signature_V_PBI builder_setSessionId = 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_PBPDPV builder_setDataCallback = nullptr;
|
||||||
signature_V_PBPEPV builder_setErrorCallback = nullptr;
|
signature_V_PBPEPV builder_setErrorCallback = nullptr;
|
||||||
|
|
||||||
|
|
@ -212,6 +220,7 @@ class AAudioLoader {
|
||||||
signature_I_PPB load_I_PPB(const char *name);
|
signature_I_PPB load_I_PPB(const char *name);
|
||||||
signature_CPH_I load_CPH_I(const char *name);
|
signature_CPH_I load_CPH_I(const char *name);
|
||||||
signature_V_PBI load_V_PBI(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_PBPDPV load_V_PBPDPV(const char *name);
|
||||||
signature_V_PBPEPV load_V_PBPEPV(const char *name);
|
signature_V_PBPEPV load_V_PBPEPV(const char *name);
|
||||||
signature_I_PB load_I_PB(const char *name);
|
signature_I_PB load_I_PB(const char *name);
|
||||||
|
|
|
||||||
|
|
@ -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.
|
// It calls app error callbacks from a static function in case the stream gets deleted.
|
||||||
static void oboe_aaudio_error_thread_proc(AudioStreamAAudio *oboeStream,
|
static void oboe_aaudio_error_thread_proc(AudioStreamAAudio *oboeStream,
|
||||||
Result error) {
|
Result error) {
|
||||||
LOGD("%s() - entering >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>", __func__);
|
LOGD("%s(,%d) - entering >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>", __func__, error);
|
||||||
AudioStreamErrorCallback *errorCallback = oboeStream->getErrorCallback();
|
AudioStreamErrorCallback *errorCallback = oboeStream->getErrorCallback();
|
||||||
if (errorCallback == nullptr) return; // should be impossible
|
if (errorCallback == nullptr) return; // should be impossible
|
||||||
bool isErrorHandled = errorCallback->onError(oboeStream, error);
|
bool isErrorHandled = errorCallback->onError(oboeStream, error);
|
||||||
|
|
@ -241,7 +241,16 @@ Result AudioStreamAAudio::open() {
|
||||||
static_cast<aaudio_session_id_t>(mSessionId));
|
static_cast<aaudio_session_id_t>(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()) {
|
if (isDataCallbackSpecified()) {
|
||||||
mLibLoader->builder_setDataCallback(aaudioBuilder, oboe_aaudio_data_callback_proc, this);
|
mLibLoader->builder_setDataCallback(aaudioBuilder, oboe_aaudio_data_callback_proc, this);
|
||||||
|
|
@ -282,6 +291,7 @@ Result AudioStreamAAudio::open() {
|
||||||
mLibLoader->stream_getPerformanceMode(mAAudioStream));
|
mLibLoader->stream_getPerformanceMode(mAAudioStream));
|
||||||
mBufferCapacityInFrames = mLibLoader->stream_getBufferCapacity(mAAudioStream);
|
mBufferCapacityInFrames = mLibLoader->stream_getBufferCapacity(mAAudioStream);
|
||||||
mBufferSizeInFrames = mLibLoader->stream_getBufferSize(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.
|
// These were added in P so we have to check for the function pointer.
|
||||||
if (mLibLoader->stream_getUsage != nullptr) {
|
if (mLibLoader->stream_getUsage != nullptr) {
|
||||||
|
|
@ -318,8 +328,13 @@ Result AudioStreamAAudio::close() {
|
||||||
|
|
||||||
AudioStream::close();
|
AudioStream::close();
|
||||||
|
|
||||||
// This will delete the AAudio stream object so we need to null out the pointer.
|
AAudioStream *stream = nullptr;
|
||||||
AAudioStream *stream = mAAudioStream.exchange(nullptr);
|
{
|
||||||
|
// Wait for any methods using mAAudioStream to finish.
|
||||||
|
std::unique_lock<std::shared_mutex> lock2(mAAudioStreamLock);
|
||||||
|
// Closing will delete *mAAudioStream so we need to null out the pointer atomically.
|
||||||
|
stream = mAAudioStream.exchange(nullptr);
|
||||||
|
}
|
||||||
if (stream != nullptr) {
|
if (stream != nullptr) {
|
||||||
if (OboeGlobals::areWorkaroundsEnabled()) {
|
if (OboeGlobals::areWorkaroundsEnabled()) {
|
||||||
// Make sure we are really stopped. Do it under mLock
|
// 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,
|
void *audioData,
|
||||||
int32_t numFrames) {
|
int32_t numFrames) {
|
||||||
DataCallbackResult result = fireDataCallback(audioData, numFrames);
|
DataCallbackResult result = fireDataCallback(audioData, numFrames);
|
||||||
|
|
@ -351,16 +381,12 @@ DataCallbackResult AudioStreamAAudio::callOnAudioReady(AAudioStream *stream,
|
||||||
LOGE("Oboe callback returned unexpected value = %d", result);
|
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();
|
launchStopThread();
|
||||||
if (isMMapUsed()) {
|
return DataCallbackResult::Continue;
|
||||||
return DataCallbackResult::Stop;
|
|
||||||
} else {
|
|
||||||
// Legacy stream <= API_P cannot be restarted after returning Stop.
|
|
||||||
return DataCallbackResult::Continue;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
return DataCallbackResult::Stop; // OK >= API_Q
|
return DataCallbackResult::Stop; // OK >= API_S
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -380,6 +406,7 @@ Result AudioStreamAAudio::requestStart() {
|
||||||
if (isDataCallbackSpecified()) {
|
if (isDataCallbackSpecified()) {
|
||||||
setDataCallbackEnabled(true);
|
setDataCallbackEnabled(true);
|
||||||
}
|
}
|
||||||
|
mStopThreadAllowed = true;
|
||||||
return static_cast<Result>(mLibLoader->stream_requestStart(stream));
|
return static_cast<Result>(mLibLoader->stream_requestStart(stream));
|
||||||
} else {
|
} else {
|
||||||
return Result::ErrorClosed;
|
return Result::ErrorClosed;
|
||||||
|
|
@ -541,29 +568,27 @@ Result AudioStreamAAudio::waitForStateChange(StreamState currentState,
|
||||||
}
|
}
|
||||||
|
|
||||||
ResultWithValue<int32_t> AudioStreamAAudio::setBufferSizeInFrames(int32_t requestedFrames) {
|
ResultWithValue<int32_t> 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<std::shared_mutex> lock(mAAudioStreamLock);
|
||||||
AAudioStream *stream = mAAudioStream.load();
|
AAudioStream *stream = mAAudioStream.load();
|
||||||
|
|
||||||
if (stream != nullptr) {
|
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);
|
int32_t newBufferSize = mLibLoader->stream_setBufferSize(mAAudioStream, adjustedFrames);
|
||||||
|
|
||||||
// Cache the result if it's valid
|
// Cache the result if it's valid
|
||||||
if (newBufferSize > 0) mBufferSizeInFrames = newBufferSize;
|
if (newBufferSize > 0) mBufferSizeInFrames = newBufferSize;
|
||||||
|
|
||||||
return ResultWithValue<int32_t>::createBasedOnSign(newBufferSize);
|
return ResultWithValue<int32_t>::createBasedOnSign(newBufferSize);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
return ResultWithValue<int32_t>(Result::ErrorClosed);
|
return ResultWithValue<int32_t>(Result::ErrorClosed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
StreamState AudioStreamAAudio::getState() const {
|
StreamState AudioStreamAAudio::getState() {
|
||||||
|
std::shared_lock<std::shared_mutex> lock(mAAudioStreamLock);
|
||||||
AAudioStream *stream = mAAudioStream.load();
|
AAudioStream *stream = mAAudioStream.load();
|
||||||
if (stream != nullptr) {
|
if (stream != nullptr) {
|
||||||
aaudio_stream_state_t aaudioState = mLibLoader->stream_getState(stream);
|
aaudio_stream_state_t aaudioState = mLibLoader->stream_getState(stream);
|
||||||
|
|
@ -580,6 +605,7 @@ StreamState AudioStreamAAudio::getState() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t AudioStreamAAudio::getBufferSizeInFrames() {
|
int32_t AudioStreamAAudio::getBufferSizeInFrames() {
|
||||||
|
std::shared_lock<std::shared_mutex> lock(mAAudioStreamLock);
|
||||||
AAudioStream *stream = mAAudioStream.load();
|
AAudioStream *stream = mAAudioStream.load();
|
||||||
if (stream != nullptr) {
|
if (stream != nullptr) {
|
||||||
mBufferSizeInFrames = mLibLoader->stream_getBufferSize(stream);
|
mBufferSizeInFrames = mLibLoader->stream_getBufferSize(stream);
|
||||||
|
|
@ -587,29 +613,34 @@ int32_t AudioStreamAAudio::getBufferSizeInFrames() {
|
||||||
return mBufferSizeInFrames;
|
return mBufferSizeInFrames;
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t AudioStreamAAudio::getFramesPerBurst() {
|
|
||||||
AAudioStream *stream = mAAudioStream.load();
|
|
||||||
if (stream != nullptr) {
|
|
||||||
mFramesPerBurst = mLibLoader->stream_getFramesPerBurst(stream);
|
|
||||||
}
|
|
||||||
return mFramesPerBurst;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AudioStreamAAudio::updateFramesRead() {
|
void AudioStreamAAudio::updateFramesRead() {
|
||||||
|
std::shared_lock<std::shared_mutex> lock(mAAudioStreamLock);
|
||||||
AAudioStream *stream = mAAudioStream.load();
|
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) {
|
if (stream != nullptr) {
|
||||||
mFramesRead = mLibLoader->stream_getFramesRead(stream);
|
mFramesRead = mLibLoader->stream_getFramesRead(stream);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioStreamAAudio::updateFramesWritten() {
|
void AudioStreamAAudio::updateFramesWritten() {
|
||||||
|
std::shared_lock<std::shared_mutex> lock(mAAudioStreamLock);
|
||||||
AAudioStream *stream = mAAudioStream.load();
|
AAudioStream *stream = mAAudioStream.load();
|
||||||
if (stream != nullptr) {
|
if (stream != nullptr) {
|
||||||
mFramesWritten = mLibLoader->stream_getFramesWritten(stream);
|
mFramesWritten = mLibLoader->stream_getFramesWritten(stream);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ResultWithValue<int32_t> AudioStreamAAudio::getXRunCount() const {
|
ResultWithValue<int32_t> AudioStreamAAudio::getXRunCount() {
|
||||||
|
std::shared_lock<std::shared_mutex> lock(mAAudioStreamLock);
|
||||||
AAudioStream *stream = mAAudioStream.load();
|
AAudioStream *stream = mAAudioStream.load();
|
||||||
if (stream != nullptr) {
|
if (stream != nullptr) {
|
||||||
return ResultWithValue<int32_t>::createBasedOnSign(mLibLoader->stream_getXRunCount(stream));
|
return ResultWithValue<int32_t>::createBasedOnSign(mLibLoader->stream_getXRunCount(stream));
|
||||||
|
|
@ -621,11 +652,12 @@ ResultWithValue<int32_t> AudioStreamAAudio::getXRunCount() const {
|
||||||
Result AudioStreamAAudio::getTimestamp(clockid_t clockId,
|
Result AudioStreamAAudio::getTimestamp(clockid_t clockId,
|
||||||
int64_t *framePosition,
|
int64_t *framePosition,
|
||||||
int64_t *timeNanoseconds) {
|
int64_t *timeNanoseconds) {
|
||||||
|
if (getState() != StreamState::Started) {
|
||||||
|
return Result::ErrorInvalidState;
|
||||||
|
}
|
||||||
|
std::shared_lock<std::shared_mutex> lock(mAAudioStreamLock);
|
||||||
AAudioStream *stream = mAAudioStream.load();
|
AAudioStream *stream = mAAudioStream.load();
|
||||||
if (stream != nullptr) {
|
if (stream != nullptr) {
|
||||||
if (getState() != StreamState::Started) {
|
|
||||||
return Result::ErrorInvalidState;
|
|
||||||
}
|
|
||||||
return static_cast<Result>(mLibLoader->stream_getTimestamp(stream, clockId,
|
return static_cast<Result>(mLibLoader->stream_getTimestamp(stream, clockId,
|
||||||
framePosition, timeNanoseconds));
|
framePosition, timeNanoseconds));
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -634,11 +666,6 @@ Result AudioStreamAAudio::getTimestamp(clockid_t clockId,
|
||||||
}
|
}
|
||||||
|
|
||||||
ResultWithValue<double> AudioStreamAAudio::calculateLatencyMillis() {
|
ResultWithValue<double> AudioStreamAAudio::calculateLatencyMillis() {
|
||||||
AAudioStream *stream = mAAudioStream.load();
|
|
||||||
if (stream == nullptr) {
|
|
||||||
return ResultWithValue<double>(Result::ErrorClosed);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the time that a known audio frame was presented.
|
// Get the time that a known audio frame was presented.
|
||||||
int64_t hardwareFrameIndex;
|
int64_t hardwareFrameIndex;
|
||||||
int64_t hardwareFrameHardwareTime;
|
int64_t hardwareFrameHardwareTime;
|
||||||
|
|
@ -676,6 +703,7 @@ ResultWithValue<double> AudioStreamAAudio::calculateLatencyMillis() {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AudioStreamAAudio::isMMapUsed() {
|
bool AudioStreamAAudio::isMMapUsed() {
|
||||||
|
std::shared_lock<std::shared_mutex> lock(mAAudioStreamLock);
|
||||||
AAudioStream *stream = mAAudioStream.load();
|
AAudioStream *stream = mAAudioStream.load();
|
||||||
if (stream != nullptr) {
|
if (stream != nullptr) {
|
||||||
return AAudioExtensions::getInstance().isMMapUsed(stream);
|
return AAudioExtensions::getInstance().isMMapUsed(stream);
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@
|
||||||
#define OBOE_STREAM_AAUDIO_H_
|
#define OBOE_STREAM_AAUDIO_H_
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
#include <shared_mutex>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
|
|
@ -67,8 +68,7 @@ public:
|
||||||
|
|
||||||
ResultWithValue<int32_t> setBufferSizeInFrames(int32_t requestedFrames) override;
|
ResultWithValue<int32_t> setBufferSizeInFrames(int32_t requestedFrames) override;
|
||||||
int32_t getBufferSizeInFrames() override;
|
int32_t getBufferSizeInFrames() override;
|
||||||
int32_t getFramesPerBurst() override;
|
ResultWithValue<int32_t> getXRunCount() override;
|
||||||
ResultWithValue<int32_t> getXRunCount() const override;
|
|
||||||
bool isXRunCountSupported() const override { return true; }
|
bool isXRunCountSupported() const override { return true; }
|
||||||
|
|
||||||
ResultWithValue<double> calculateLatencyMillis() override;
|
ResultWithValue<double> calculateLatencyMillis() override;
|
||||||
|
|
@ -81,7 +81,7 @@ public:
|
||||||
int64_t *framePosition,
|
int64_t *framePosition,
|
||||||
int64_t *timeNanoseconds) override;
|
int64_t *timeNanoseconds) override;
|
||||||
|
|
||||||
StreamState getState() const override;
|
StreamState getState() override;
|
||||||
|
|
||||||
AudioApi getAudioApi() const override {
|
AudioApi getAudioApi() const override {
|
||||||
return AudioApi::AAudio;
|
return AudioApi::AAudio;
|
||||||
|
|
@ -112,14 +112,21 @@ private:
|
||||||
// Must call under mLock. And stream must NOT be nullptr.
|
// Must call under mLock. And stream must NOT be nullptr.
|
||||||
Result requestStop_l(AAudioStream *stream);
|
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().
|
// 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.
|
// Two milliseconds may be enough but 10 msec is even safer.
|
||||||
static constexpr int kDelayBeforeCloseMillis = 10;
|
static constexpr int kDelayBeforeCloseMillis = 10;
|
||||||
|
|
||||||
std::atomic<bool> mCallbackThreadEnabled;
|
std::atomic<bool> mCallbackThreadEnabled;
|
||||||
|
std::atomic<bool> mStopThreadAllowed{false};
|
||||||
|
|
||||||
// pointer to the underlying 'C' AAudio stream, valid if open, null if closed
|
// pointer to the underlying 'C' AAudio stream, valid if open, null if closed
|
||||||
std::atomic<AAudioStream *> mAAudioStream{nullptr};
|
std::atomic<AAudioStream *> mAAudioStream{nullptr};
|
||||||
|
std::shared_mutex mAAudioStreamLock; // to protect mAAudioStream while closing
|
||||||
|
|
||||||
static AAudioLoader *mLibLoader;
|
static AAudioLoader *mLibLoader;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,7 @@ void AudioStream::checkScheduler() {
|
||||||
DataCallbackResult AudioStream::fireDataCallback(void *audioData, int32_t numFrames) {
|
DataCallbackResult AudioStream::fireDataCallback(void *audioData, int32_t numFrames) {
|
||||||
if (!isDataCallbackEnabled()) {
|
if (!isDataCallbackEnabled()) {
|
||||||
LOGW("AudioStream::%s() called with data callback disabled!", __func__);
|
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;
|
DataCallbackResult result;
|
||||||
|
|
@ -196,16 +196,4 @@ ResultWithValue<FrameTimestamp> 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
|
} // namespace oboe
|
||||||
|
|
|
||||||
|
|
@ -91,6 +91,7 @@ bool AudioStreamBuilder::isCompatible(AudioStreamBase &other) {
|
||||||
Result AudioStreamBuilder::openStream(AudioStream **streamPP) {
|
Result AudioStreamBuilder::openStream(AudioStream **streamPP) {
|
||||||
auto result = isValidConfig();
|
auto result = isValidConfig();
|
||||||
if (result != Result::OK) {
|
if (result != Result::OK) {
|
||||||
|
LOGW("%s() invalid config %d", __func__, result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -202,24 +203,16 @@ Result AudioStreamBuilder::openStream(AudioStream **streamPP) {
|
||||||
|
|
||||||
Result AudioStreamBuilder::openManagedStream(oboe::ManagedStream &stream) {
|
Result AudioStreamBuilder::openManagedStream(oboe::ManagedStream &stream) {
|
||||||
stream.reset();
|
stream.reset();
|
||||||
auto result = isValidConfig();
|
|
||||||
if (result != Result::OK) {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
AudioStream *streamptr;
|
AudioStream *streamptr;
|
||||||
result = openStream(&streamptr);
|
auto result = openStream(&streamptr);
|
||||||
stream.reset(streamptr);
|
stream.reset(streamptr);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result AudioStreamBuilder::openStream(std::shared_ptr<AudioStream> &sharedStream) {
|
Result AudioStreamBuilder::openStream(std::shared_ptr<AudioStream> &sharedStream) {
|
||||||
sharedStream.reset();
|
sharedStream.reset();
|
||||||
auto result = isValidConfig();
|
|
||||||
if (result != Result::OK) {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
AudioStream *streamptr;
|
AudioStream *streamptr;
|
||||||
result = openStream(&streamptr);
|
auto result = openStream(&streamptr);
|
||||||
if (result == Result::OK) {
|
if (result == Result::OK) {
|
||||||
sharedStream.reset(streamptr);
|
sharedStream.reset(streamptr);
|
||||||
// Save a weak_ptr in the stream for use with callbacks.
|
// Save a weak_ptr in the stream for use with callbacks.
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,8 @@
|
||||||
#include "DataConversionFlowGraph.h"
|
#include "DataConversionFlowGraph.h"
|
||||||
#include "SourceFloatCaller.h"
|
#include "SourceFloatCaller.h"
|
||||||
#include "SourceI16Caller.h"
|
#include "SourceI16Caller.h"
|
||||||
|
#include "SourceI24Caller.h"
|
||||||
|
#include "SourceI32Caller.h"
|
||||||
|
|
||||||
#include <flowgraph/ClipToRange.h>
|
#include <flowgraph/ClipToRange.h>
|
||||||
#include <flowgraph/MonoToMultiConverter.h>
|
#include <flowgraph/MonoToMultiConverter.h>
|
||||||
|
|
@ -28,9 +30,11 @@
|
||||||
#include <flowgraph/SinkFloat.h>
|
#include <flowgraph/SinkFloat.h>
|
||||||
#include <flowgraph/SinkI16.h>
|
#include <flowgraph/SinkI16.h>
|
||||||
#include <flowgraph/SinkI24.h>
|
#include <flowgraph/SinkI24.h>
|
||||||
|
#include <flowgraph/SinkI32.h>
|
||||||
#include <flowgraph/SourceFloat.h>
|
#include <flowgraph/SourceFloat.h>
|
||||||
#include <flowgraph/SourceI16.h>
|
#include <flowgraph/SourceI16.h>
|
||||||
#include <flowgraph/SourceI24.h>
|
#include <flowgraph/SourceI24.h>
|
||||||
|
#include <flowgraph/SourceI32.h>
|
||||||
#include <flowgraph/SampleRateConverter.h>
|
#include <flowgraph/SampleRateConverter.h>
|
||||||
|
|
||||||
using namespace oboe;
|
using namespace oboe;
|
||||||
|
|
@ -116,6 +120,14 @@ Result DataConversionFlowGraph::configure(AudioStream *sourceStream, AudioStream
|
||||||
mSourceCaller = std::make_unique<SourceI16Caller>(sourceChannelCount,
|
mSourceCaller = std::make_unique<SourceI16Caller>(sourceChannelCount,
|
||||||
actualSourceFramesPerCallback);
|
actualSourceFramesPerCallback);
|
||||||
break;
|
break;
|
||||||
|
case AudioFormat::I24:
|
||||||
|
mSourceCaller = std::make_unique<SourceI24Caller>(sourceChannelCount,
|
||||||
|
actualSourceFramesPerCallback);
|
||||||
|
break;
|
||||||
|
case AudioFormat::I32:
|
||||||
|
mSourceCaller = std::make_unique<SourceI32Caller>(sourceChannelCount,
|
||||||
|
actualSourceFramesPerCallback);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
LOGE("%s() Unsupported source caller format = %d", __func__, sourceFormat);
|
LOGE("%s() Unsupported source caller format = %d", __func__, sourceFormat);
|
||||||
return Result::ErrorIllegalArgument;
|
return Result::ErrorIllegalArgument;
|
||||||
|
|
@ -132,6 +144,12 @@ Result DataConversionFlowGraph::configure(AudioStream *sourceStream, AudioStream
|
||||||
case AudioFormat::I16:
|
case AudioFormat::I16:
|
||||||
mSource = std::make_unique<SourceI16>(sourceChannelCount);
|
mSource = std::make_unique<SourceI16>(sourceChannelCount);
|
||||||
break;
|
break;
|
||||||
|
case AudioFormat::I24:
|
||||||
|
mSource = std::make_unique<SourceI24>(sourceChannelCount);
|
||||||
|
break;
|
||||||
|
case AudioFormat::I32:
|
||||||
|
mSource = std::make_unique<SourceI32>(sourceChannelCount);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
LOGE("%s() Unsupported source format = %d", __func__, sourceFormat);
|
LOGE("%s() Unsupported source format = %d", __func__, sourceFormat);
|
||||||
return Result::ErrorIllegalArgument;
|
return Result::ErrorIllegalArgument;
|
||||||
|
|
@ -202,6 +220,12 @@ Result DataConversionFlowGraph::configure(AudioStream *sourceStream, AudioStream
|
||||||
case AudioFormat::I16:
|
case AudioFormat::I16:
|
||||||
mSink = std::make_unique<SinkI16>(sinkChannelCount);
|
mSink = std::make_unique<SinkI16>(sinkChannelCount);
|
||||||
break;
|
break;
|
||||||
|
case AudioFormat::I24:
|
||||||
|
mSink = std::make_unique<SinkI24>(sinkChannelCount);
|
||||||
|
break;
|
||||||
|
case AudioFormat::I32:
|
||||||
|
mSink = std::make_unique<SinkI32>(sinkChannelCount);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
LOGE("%s() Unsupported sink format = %d", __func__, sinkFormat);
|
LOGE("%s() Unsupported sink format = %d", __func__, sinkFormat);
|
||||||
return Result::ErrorIllegalArgument;;
|
return Result::ErrorIllegalArgument;;
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ Result FilterAudioStream::configureFlowGraph() {
|
||||||
AudioStream *sourceStream = isOutput ? this : mChildStream.get();
|
AudioStream *sourceStream = isOutput ? this : mChildStream.get();
|
||||||
AudioStream *sinkStream = isOutput ? mChildStream.get() : this;
|
AudioStream *sinkStream = isOutput ? mChildStream.get() : this;
|
||||||
|
|
||||||
mRateScaler = ((double) sourceStream->getSampleRate()) / sinkStream->getSampleRate();
|
mRateScaler = ((double) getSampleRate()) / mChildStream->getSampleRate();
|
||||||
|
|
||||||
return mFlowGraph->configure(sourceStream, sinkStream);
|
return mFlowGraph->configure(sourceStream, sinkStream);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,8 @@ public:
|
||||||
mBufferCapacityInFrames = mChildStream->getBufferCapacityInFrames();
|
mBufferCapacityInFrames = mChildStream->getBufferCapacityInFrames();
|
||||||
mPerformanceMode = mChildStream->getPerformanceMode();
|
mPerformanceMode = mChildStream->getPerformanceMode();
|
||||||
mInputPreset = mChildStream->getInputPreset();
|
mInputPreset = mChildStream->getInputPreset();
|
||||||
|
mFramesPerBurst = mChildStream->getFramesPerBurst();
|
||||||
|
mDeviceId = mChildStream->getDeviceId();
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual ~FilterAudioStream() = default;
|
virtual ~FilterAudioStream() = default;
|
||||||
|
|
@ -113,7 +115,7 @@ public:
|
||||||
int32_t numFrames,
|
int32_t numFrames,
|
||||||
int64_t timeoutNanoseconds) override;
|
int64_t timeoutNanoseconds) override;
|
||||||
|
|
||||||
StreamState getState() const override {
|
StreamState getState() override {
|
||||||
return mChildStream->getState();
|
return mChildStream->getState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -128,10 +130,6 @@ public:
|
||||||
return mChildStream->isXRunCountSupported();
|
return mChildStream->isXRunCountSupported();
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t getFramesPerBurst() override {
|
|
||||||
return mChildStream->getFramesPerBurst();
|
|
||||||
}
|
|
||||||
|
|
||||||
AudioApi getAudioApi() const override {
|
AudioApi getAudioApi() const override {
|
||||||
return mChildStream->getAudioApi();
|
return mChildStream->getAudioApi();
|
||||||
}
|
}
|
||||||
|
|
@ -159,7 +157,7 @@ public:
|
||||||
return mBufferSizeInFrames;
|
return mBufferSizeInFrames;
|
||||||
}
|
}
|
||||||
|
|
||||||
ResultWithValue<int32_t> getXRunCount() const override {
|
ResultWithValue<int32_t> getXRunCount() override {
|
||||||
return mChildStream->getXRunCount();
|
return mChildStream->getXRunCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -184,20 +182,20 @@ public:
|
||||||
void *audioData,
|
void *audioData,
|
||||||
int32_t numFrames) override;
|
int32_t numFrames) override;
|
||||||
|
|
||||||
bool onError(AudioStream * audioStream, Result error) override {
|
bool onError(AudioStream * /*audioStream*/, Result error) override {
|
||||||
if (mErrorCallback != nullptr) {
|
if (mErrorCallback != nullptr) {
|
||||||
return mErrorCallback->onError(this, error);
|
return mErrorCallback->onError(this, error);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void onErrorBeforeClose(AudioStream *oboeStream, Result error) override {
|
void onErrorBeforeClose(AudioStream * /*oboeStream*/, Result error) override {
|
||||||
if (mErrorCallback != nullptr) {
|
if (mErrorCallback != nullptr) {
|
||||||
mErrorCallback->onErrorBeforeClose(this, error);
|
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.
|
// Close this parent stream because the callback will only close the child.
|
||||||
AudioStream::close();
|
AudioStream::close();
|
||||||
if (mErrorCallback != nullptr) {
|
if (mErrorCallback != nullptr) {
|
||||||
|
|
|
||||||
|
|
@ -71,6 +71,7 @@ public:
|
||||||
std::string chipname = getPropertyString("ro.hardware.chipname");
|
std::string chipname = getPropertyString("ro.hardware.chipname");
|
||||||
isExynos9810 = (chipname == "exynos9810");
|
isExynos9810 = (chipname == "exynos9810");
|
||||||
isExynos990 = (chipname == "exynos990");
|
isExynos990 = (chipname == "exynos990");
|
||||||
|
isExynos850 = (chipname == "exynos850");
|
||||||
|
|
||||||
mBuildChangelist = getPropertyInteger("ro.build.changelist", 0);
|
mBuildChangelist = getPropertyInteger("ro.build.changelist", 0);
|
||||||
}
|
}
|
||||||
|
|
@ -86,9 +87,9 @@ public:
|
||||||
return kTopMargin;
|
return kTopMargin;
|
||||||
}
|
}
|
||||||
|
|
||||||
// See Oboe issue #824 for more information.
|
// See Oboe issues #824 and #1247 for more information.
|
||||||
bool isMonoMMapActuallyStereo() const override {
|
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 {
|
bool isAAudioMMapPossible(const AudioStreamBuilder &builder) const override {
|
||||||
|
|
@ -102,10 +103,24 @@ public:
|
||||||
// This detects b/159066712 , S20 LSI has corrupt low latency audio recording
|
// This detects b/159066712 , S20 LSI has corrupt low latency audio recording
|
||||||
// and turns off MMAP.
|
// and turns off MMAP.
|
||||||
// See also https://github.com/google/oboe/issues/892
|
// See also https://github.com/google/oboe/issues/892
|
||||||
bool mRecordingCorrupted = isInput
|
bool isRecordingCorrupted = isInput
|
||||||
&& isExynos990
|
&& isExynos990
|
||||||
&& mBuildChangelist < 19350896;
|
&& 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:
|
private:
|
||||||
|
|
@ -116,6 +131,7 @@ private:
|
||||||
bool isExynos = false;
|
bool isExynos = false;
|
||||||
bool isExynos9810 = false;
|
bool isExynos9810 = false;
|
||||||
bool isExynos990 = false;
|
bool isExynos990 = false;
|
||||||
|
bool isExynos850 = false;
|
||||||
int mBuildChangelist = 0;
|
int mBuildChangelist = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -182,6 +198,19 @@ bool QuirksManager::isConversionNeeded(
|
||||||
LOGI("QuirksManager::%s() forcing internal format to I16 for low latency", __func__);
|
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
|
// Channel Count conversions
|
||||||
if (OboeGlobals::areWorkaroundsEnabled()
|
if (OboeGlobals::areWorkaroundsEnabled()
|
||||||
&& builder.isChannelConversionAllowed()
|
&& builder.isChannelConversionAllowed()
|
||||||
|
|
|
||||||
|
|
@ -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 <algorithm>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include "flowgraph/FlowGraphNode.h"
|
||||||
|
#include "SourceI24Caller.h"
|
||||||
|
|
||||||
|
#if FLOWGRAPH_ANDROID_INTERNAL
|
||||||
|
#include <audio_utils/primitives.h>
|
||||||
|
#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;
|
||||||
|
}
|
||||||
|
|
@ -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 <unistd.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#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<uint8_t[]>(
|
||||||
|
kBytesPerI24Packed * channelCount * output.getFramesPerBuffer());
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t onProcess(int32_t numFrames) override;
|
||||||
|
|
||||||
|
const char *getName() override {
|
||||||
|
return "SourceI24Caller";
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unique_ptr<uint8_t[]> mConversionBuffer;
|
||||||
|
static constexpr int kBytesPerI24Packed = 3;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
#endif //OBOE_SOURCE_I16_CALLER_H
|
||||||
|
|
@ -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 <algorithm>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include "flowgraph/FlowGraphNode.h"
|
||||||
|
#include "SourceI32Caller.h"
|
||||||
|
|
||||||
|
#if FLOWGRAPH_ANDROID_INTERNAL
|
||||||
|
#include <audio_utils/primitives.h>
|
||||||
|
#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;
|
||||||
|
}
|
||||||
|
|
@ -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 <memory.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#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<int32_t[]>(channelCount * output.getFramesPerBuffer());
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t onProcess(int32_t numFrames) override;
|
||||||
|
|
||||||
|
const char *getName() override {
|
||||||
|
return "SourceI32Caller";
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unique_ptr<int32_t[]> mConversionBuffer;
|
||||||
|
static constexpr float kScale = 1.0 / (1UL << 31);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
#endif //OBOE_SOURCE_I32_CALLER_H
|
||||||
|
|
@ -60,6 +60,12 @@ int32_t convertFormatToSizeInBytes(AudioFormat format) {
|
||||||
case AudioFormat::Float:
|
case AudioFormat::Float:
|
||||||
size = sizeof(float);
|
size = sizeof(float);
|
||||||
break;
|
break;
|
||||||
|
case AudioFormat::I24:
|
||||||
|
size = 3; // packed 24-bit data
|
||||||
|
break;
|
||||||
|
case AudioFormat::I32:
|
||||||
|
size = sizeof(int32_t);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -98,6 +104,8 @@ const char *convertToText<AudioFormat>(AudioFormat format) {
|
||||||
case AudioFormat::Unspecified: return "Unspecified";
|
case AudioFormat::Unspecified: return "Unspecified";
|
||||||
case AudioFormat::I16: return "I16";
|
case AudioFormat::I16: return "I16";
|
||||||
case AudioFormat::Float: return "Float";
|
case AudioFormat::Float: return "Float";
|
||||||
|
case AudioFormat::I24: return "I24";
|
||||||
|
case AudioFormat::I32: return "I32";
|
||||||
default: return "Unrecognized format";
|
default: return "Unrecognized format";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 <unistd.h>
|
||||||
|
|
||||||
|
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
|
||||||
|
|
@ -37,7 +37,7 @@ public:
|
||||||
|
|
||||||
int32_t onProcess(int numFrames) override;
|
int32_t onProcess(int numFrames) override;
|
||||||
|
|
||||||
void setEnabled(bool enabled) {}
|
void setEnabled(bool /*enabled*/) {}
|
||||||
|
|
||||||
std::vector<std::unique_ptr<flowgraph::FlowGraphPortFloatInput>> inputs;
|
std::vector<std::unique_ptr<flowgraph::FlowGraphPortFloatInput>> inputs;
|
||||||
flowgraph::FlowGraphPortFloatOutput output;
|
flowgraph::FlowGraphPortFloatOutput output;
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,10 @@ void RampLinear::setLengthInFrames(int32_t frames) {
|
||||||
|
|
||||||
void RampLinear::setTarget(float target) {
|
void RampLinear::setTarget(float target) {
|
||||||
mTarget.store(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() {
|
float RampLinear::interpolateCurrent() {
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ SinkFloat::SinkFloat(int32_t channelCount)
|
||||||
int32_t SinkFloat::read(void *data, int32_t numFrames) {
|
int32_t SinkFloat::read(void *data, int32_t numFrames) {
|
||||||
// printf("SinkFloat::read(,,%d)\n", numFrames);
|
// printf("SinkFloat::read(,,%d)\n", numFrames);
|
||||||
float *floatData = (float *) data;
|
float *floatData = (float *) data;
|
||||||
int32_t channelCount = input.getSamplesPerFrame();
|
const int32_t channelCount = input.getSamplesPerFrame();
|
||||||
|
|
||||||
int32_t framesLeft = numFrames;
|
int32_t framesLeft = numFrames;
|
||||||
while (framesLeft > 0) {
|
while (framesLeft > 0) {
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@ namespace flowgraph {
|
||||||
class SinkFloat : public FlowGraphSink {
|
class SinkFloat : public FlowGraphSink {
|
||||||
public:
|
public:
|
||||||
explicit SinkFloat(int32_t channelCount);
|
explicit SinkFloat(int32_t channelCount);
|
||||||
|
~SinkFloat() override = default;
|
||||||
|
|
||||||
int32_t read(void *data, int32_t numFrames) override;
|
int32_t read(void *data, int32_t numFrames) override;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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 <audio_utils/primitives.h>
|
||||||
|
#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;
|
||||||
|
}
|
||||||
|
|
@ -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 <stdint.h>
|
||||||
|
|
||||||
|
#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
|
||||||
|
|
@ -28,11 +28,11 @@ SourceFloat::SourceFloat(int32_t channelCount)
|
||||||
|
|
||||||
int32_t SourceFloat::onProcess(int32_t numFrames) {
|
int32_t SourceFloat::onProcess(int32_t numFrames) {
|
||||||
float *outputBuffer = output.getBuffer();
|
float *outputBuffer = output.getBuffer();
|
||||||
int32_t channelCount = output.getSamplesPerFrame();
|
const int32_t channelCount = output.getSamplesPerFrame();
|
||||||
|
|
||||||
int32_t framesLeft = mSizeInFrames - mFrameIndex;
|
const int32_t framesLeft = mSizeInFrames - mFrameIndex;
|
||||||
int32_t framesToProcess = std::min(numFrames, framesLeft);
|
const int32_t framesToProcess = std::min(numFrames, framesLeft);
|
||||||
int32_t numSamples = framesToProcess * channelCount;
|
const int32_t numSamples = framesToProcess * channelCount;
|
||||||
|
|
||||||
const float *floatBase = (float *) mData;
|
const float *floatBase = (float *) mData;
|
||||||
const float *floatData = &floatBase[mFrameIndex * channelCount];
|
const float *floatData = &floatBase[mFrameIndex * channelCount];
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@ namespace flowgraph {
|
||||||
class SourceFloat : public FlowGraphSourceBuffered {
|
class SourceFloat : public FlowGraphSourceBuffered {
|
||||||
public:
|
public:
|
||||||
explicit SourceFloat(int32_t channelCount);
|
explicit SourceFloat(int32_t channelCount);
|
||||||
|
~SourceFloat() override = default;
|
||||||
|
|
||||||
int32_t onProcess(int32_t numFrames) override;
|
int32_t onProcess(int32_t numFrames) override;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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 <algorithm>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#if FLOWGRAPH_ANDROID_INTERNAL
|
||||||
|
#include <audio_utils/primitives.h>
|
||||||
|
#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<const int32_t *>(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;
|
||||||
|
}
|
||||||
|
|
@ -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 <stdint.h>
|
||||||
|
|
||||||
|
#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
|
||||||
|
|
@ -49,7 +49,7 @@ public:
|
||||||
|
|
||||||
int32_t getBufferCapacityInFrames() const override;
|
int32_t getBufferCapacityInFrames() const override;
|
||||||
|
|
||||||
ResultWithValue<int32_t> getXRunCount() const override {
|
ResultWithValue<int32_t> getXRunCount() override {
|
||||||
return ResultWithValue<int32_t>(mXRunCount);
|
return ResultWithValue<int32_t>(mXRunCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -354,10 +354,6 @@ SLresult AudioStreamOpenSLES::registerBufferQueueCallback() {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t AudioStreamOpenSLES::getFramesPerBurst() {
|
|
||||||
return mFramesPerBurst;
|
|
||||||
}
|
|
||||||
|
|
||||||
int64_t AudioStreamOpenSLES::getFramesProcessedByServer() {
|
int64_t AudioStreamOpenSLES::getFramesProcessedByServer() {
|
||||||
updateServiceFrameCounter();
|
updateServiceFrameCounter();
|
||||||
int64_t millis64 = mPositionMillis.get();
|
int64_t millis64 = mPositionMillis.get();
|
||||||
|
|
|
||||||
|
|
@ -56,10 +56,7 @@ public:
|
||||||
*
|
*
|
||||||
* @return state or a negative error.
|
* @return state or a negative error.
|
||||||
*/
|
*/
|
||||||
StreamState getState() const override { return mState.load(); }
|
StreamState getState() override { return mState.load(); }
|
||||||
|
|
||||||
int32_t getFramesPerBurst() override;
|
|
||||||
|
|
||||||
|
|
||||||
AudioApi getAudioApi() const override {
|
AudioApi getAudioApi() const override {
|
||||||
return AudioApi::OpenSLES;
|
return AudioApi::OpenSLES;
|
||||||
|
|
|
||||||
|
|
@ -83,6 +83,8 @@ SLuint32 OpenSLES_ConvertFormatToRepresentation(AudioFormat format) {
|
||||||
return SL_ANDROID_PCM_REPRESENTATION_SIGNED_INT;
|
return SL_ANDROID_PCM_REPRESENTATION_SIGNED_INT;
|
||||||
case AudioFormat::Float:
|
case AudioFormat::Float:
|
||||||
return SL_ANDROID_PCM_REPRESENTATION_FLOAT;
|
return SL_ANDROID_PCM_REPRESENTATION_FLOAT;
|
||||||
|
case AudioFormat::I24:
|
||||||
|
case AudioFormat::I32:
|
||||||
case AudioFormat::Invalid:
|
case AudioFormat::Invalid:
|
||||||
case AudioFormat::Unspecified:
|
case AudioFormat::Unspecified:
|
||||||
default:
|
default:
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue