1
0
Fork 0
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:
ed 2021-07-29 08:56:38 +01:00
parent 39772b7474
commit a6df818255
37 changed files with 742 additions and 121 deletions

View file

@ -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

View file

@ -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.

View file

@ -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};
};
/**

View file

@ -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;
}

View file

@ -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.
*/

View file

@ -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,
};
/**

View file

@ -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

View file

@ -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);

View file

@ -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);

View file

@ -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);

View file

@ -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;

View file

@ -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

View file

@ -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.

View file

@ -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;;

View file

@ -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);
}

View file

@ -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) {

View file

@ -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()

View file

@ -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;
}

View file

@ -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

View file

@ -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;
}

View file

@ -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

View file

@ -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";
}
}

View file

@ -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

View file

@ -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;

View file

@ -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() {

View file

@ -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) {

View file

@ -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;

View file

@ -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;
}

View file

@ -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

View file

@ -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];

View file

@ -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;

View file

@ -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;
}

View file

@ -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

View file

@ -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);
}

View file

@ -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();

View file

@ -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;

View file

@ -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: