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/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
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
@ -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<int32_t> getXRunCount() const {
|
||||
virtual ResultWithValue<int32_t> getXRunCount() {
|
||||
return ResultWithValue<int32_t>(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<bool> mDataCallbackEnabled{false};
|
||||
std::atomic<bool> mErrorCallbackCalled{false};
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
#define OBOE_STREAM_BASE_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@
|
|||
#ifndef OBOE_DEFINITIONS_H
|
||||
#define OBOE_DEFINITIONS_H
|
||||
|
||||
|
||||
#include <cstdint>
|
||||
#include <type_traits>
|
||||
|
||||
|
|
@ -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,
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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<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) {
|
||||
void *proc = dlsym(mLibHandle, 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)
|
||||
#else
|
||||
#include <aaudio/AAudio.h>
|
||||
#include <android/ndk-version.h>
|
||||
#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);
|
||||
|
|
|
|||
|
|
@ -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<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()) {
|
||||
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<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 (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<Result>(mLibLoader->stream_requestStart(stream));
|
||||
} else {
|
||||
return Result::ErrorClosed;
|
||||
|
|
@ -541,29 +568,27 @@ Result AudioStreamAAudio::waitForStateChange(StreamState currentState,
|
|||
}
|
||||
|
||||
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();
|
||||
|
||||
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<int32_t>::createBasedOnSign(newBufferSize);
|
||||
|
||||
} else {
|
||||
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();
|
||||
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<std::shared_mutex> 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<std::shared_mutex> 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<std::shared_mutex> lock(mAAudioStreamLock);
|
||||
AAudioStream *stream = mAAudioStream.load();
|
||||
if (stream != nullptr) {
|
||||
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();
|
||||
if (stream != nullptr) {
|
||||
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,
|
||||
int64_t *framePosition,
|
||||
int64_t *timeNanoseconds) {
|
||||
if (getState() != StreamState::Started) {
|
||||
return Result::ErrorInvalidState;
|
||||
}
|
||||
std::shared_lock<std::shared_mutex> lock(mAAudioStreamLock);
|
||||
AAudioStream *stream = mAAudioStream.load();
|
||||
if (stream != nullptr) {
|
||||
if (getState() != StreamState::Started) {
|
||||
return Result::ErrorInvalidState;
|
||||
}
|
||||
return static_cast<Result>(mLibLoader->stream_getTimestamp(stream, clockId,
|
||||
framePosition, timeNanoseconds));
|
||||
} else {
|
||||
|
|
@ -634,11 +666,6 @@ Result AudioStreamAAudio::getTimestamp(clockid_t clockId,
|
|||
}
|
||||
|
||||
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.
|
||||
int64_t hardwareFrameIndex;
|
||||
int64_t hardwareFrameHardwareTime;
|
||||
|
|
@ -676,6 +703,7 @@ ResultWithValue<double> AudioStreamAAudio::calculateLatencyMillis() {
|
|||
}
|
||||
|
||||
bool AudioStreamAAudio::isMMapUsed() {
|
||||
std::shared_lock<std::shared_mutex> lock(mAAudioStreamLock);
|
||||
AAudioStream *stream = mAAudioStream.load();
|
||||
if (stream != nullptr) {
|
||||
return AAudioExtensions::getInstance().isMMapUsed(stream);
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
#define OBOE_STREAM_AAUDIO_H_
|
||||
|
||||
#include <atomic>
|
||||
#include <shared_mutex>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
|
||||
|
|
@ -67,8 +68,7 @@ public:
|
|||
|
||||
ResultWithValue<int32_t> setBufferSizeInFrames(int32_t requestedFrames) override;
|
||||
int32_t getBufferSizeInFrames() override;
|
||||
int32_t getFramesPerBurst() override;
|
||||
ResultWithValue<int32_t> getXRunCount() const override;
|
||||
ResultWithValue<int32_t> getXRunCount() override;
|
||||
bool isXRunCountSupported() const override { return true; }
|
||||
|
||||
ResultWithValue<double> 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<bool> mCallbackThreadEnabled;
|
||||
std::atomic<bool> mStopThreadAllowed{false};
|
||||
|
||||
// pointer to the underlying 'C' AAudio stream, valid if open, null if closed
|
||||
std::atomic<AAudioStream *> mAAudioStream{nullptr};
|
||||
std::shared_mutex mAAudioStreamLock; // to protect mAAudioStream while closing
|
||||
|
||||
static AAudioLoader *mLibLoader;
|
||||
|
||||
|
|
|
|||
|
|
@ -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<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
|
||||
|
|
|
|||
|
|
@ -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<AudioStream> &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.
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@
|
|||
#include "DataConversionFlowGraph.h"
|
||||
#include "SourceFloatCaller.h"
|
||||
#include "SourceI16Caller.h"
|
||||
#include "SourceI24Caller.h"
|
||||
#include "SourceI32Caller.h"
|
||||
|
||||
#include <flowgraph/ClipToRange.h>
|
||||
#include <flowgraph/MonoToMultiConverter.h>
|
||||
|
|
@ -28,9 +30,11 @@
|
|||
#include <flowgraph/SinkFloat.h>
|
||||
#include <flowgraph/SinkI16.h>
|
||||
#include <flowgraph/SinkI24.h>
|
||||
#include <flowgraph/SinkI32.h>
|
||||
#include <flowgraph/SourceFloat.h>
|
||||
#include <flowgraph/SourceI16.h>
|
||||
#include <flowgraph/SourceI24.h>
|
||||
#include <flowgraph/SourceI32.h>
|
||||
#include <flowgraph/SampleRateConverter.h>
|
||||
|
||||
using namespace oboe;
|
||||
|
|
@ -116,6 +120,14 @@ Result DataConversionFlowGraph::configure(AudioStream *sourceStream, AudioStream
|
|||
mSourceCaller = std::make_unique<SourceI16Caller>(sourceChannelCount,
|
||||
actualSourceFramesPerCallback);
|
||||
break;
|
||||
case AudioFormat::I24:
|
||||
mSourceCaller = std::make_unique<SourceI24Caller>(sourceChannelCount,
|
||||
actualSourceFramesPerCallback);
|
||||
break;
|
||||
case AudioFormat::I32:
|
||||
mSourceCaller = std::make_unique<SourceI32Caller>(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<SourceI16>(sourceChannelCount);
|
||||
break;
|
||||
case AudioFormat::I24:
|
||||
mSource = std::make_unique<SourceI24>(sourceChannelCount);
|
||||
break;
|
||||
case AudioFormat::I32:
|
||||
mSource = std::make_unique<SourceI32>(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<SinkI16>(sinkChannelCount);
|
||||
break;
|
||||
case AudioFormat::I24:
|
||||
mSink = std::make_unique<SinkI24>(sinkChannelCount);
|
||||
break;
|
||||
case AudioFormat::I32:
|
||||
mSink = std::make_unique<SinkI32>(sinkChannelCount);
|
||||
break;
|
||||
default:
|
||||
LOGE("%s() Unsupported sink format = %d", __func__, sinkFormat);
|
||||
return Result::ErrorIllegalArgument;;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<int32_t> getXRunCount() const override {
|
||||
ResultWithValue<int32_t> 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) {
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
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>(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";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
void setEnabled(bool enabled) {}
|
||||
void setEnabled(bool /*enabled*/) {}
|
||||
|
||||
std::vector<std::unique_ptr<flowgraph::FlowGraphPortFloatInput>> inputs;
|
||||
flowgraph::FlowGraphPortFloatOutput output;
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
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];
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
ResultWithValue<int32_t> getXRunCount() const override {
|
||||
ResultWithValue<int32_t> getXRunCount() override {
|
||||
return ResultWithValue<int32_t>(mXRunCount);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue