liboboe: initial checkin of core and legacy files.
Oboe C++ files that calls AudioTrack and AudioRecord.
Main C API implemented by src/core/OboeAudio.cpp
Test: gunit tests for the Legacy mode and handle tracker in tests folder
Bug: 33347409
Change-Id: I50f9fd99377efbd8de6fef1601e9af4c22c6ab46
Signed-off-by: Phil Burk <philburk@google.com>
diff --git a/media/liboboe/src/Android.mk b/media/liboboe/src/Android.mk
new file mode 100644
index 0000000..7b9a906
--- /dev/null
+++ b/media/liboboe/src/Android.mk
@@ -0,0 +1,70 @@
+LOCAL_PATH:= $(call my-dir)
+
+# ======================= STATIC LIBRARY ==========================
+# This is being built because it make Oboe testing very easy with a complete executable.
+# TODO Remove this target later, when not needed.
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := liboboe
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_C_INCLUDES := \
+ $(call include-path-for, audio-utils) \
+ frameworks/native/include \
+ system/core/base/include \
+ frameworks/native/media/liboboe/include/include \
+ frameworks/av/media/liboboe/include \
+ $(LOCAL_PATH)/core \
+ $(LOCAL_PATH)/utility \
+ $(LOCAL_PATH)/legacy
+
+LOCAL_SRC_FILES += core/AudioStream.cpp
+LOCAL_SRC_FILES += core/AudioStreamBuilder.cpp
+LOCAL_SRC_FILES += core/OboeAudio.cpp
+LOCAL_SRC_FILES += legacy/AudioStreamRecord.cpp
+LOCAL_SRC_FILES += legacy/AudioStreamTrack.cpp
+LOCAL_SRC_FILES += utility/HandleTracker.cpp
+LOCAL_SRC_FILES += utility/OboeUtilities.cpp
+
+LOCAL_CFLAGS += -Wno-unused-parameter
+LOCAL_CFLAGS += -Wall -Werror
+# By default, all symbols are hidden.
+LOCAL_CFLAGS += -fvisibility=hidden
+# OBOE_API is used to explicitly export a function or a variable as a visible symbol.
+LOCAL_CFLAGS += -DOBOE_API='__attribute__((visibility("default")))'
+
+include $(BUILD_STATIC_LIBRARY)
+
+# ======================= SHARED LIBRARY ==========================
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := liboboe
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_C_INCLUDES := \
+ $(call include-path-for, audio-utils) \
+ frameworks/native/include \
+ system/core/base/include \
+ frameworks/native/media/liboboe/include/include \
+ frameworks/av/media/liboboe/include \
+ $(LOCAL_PATH)/core \
+ $(LOCAL_PATH)/utility \
+ $(LOCAL_PATH)/legacy
+
+LOCAL_SRC_FILES += core/AudioStream.cpp
+LOCAL_SRC_FILES += core/AudioStreamBuilder.cpp
+LOCAL_SRC_FILES += core/OboeAudio.cpp
+LOCAL_SRC_FILES += legacy/AudioStreamRecord.cpp
+LOCAL_SRC_FILES += legacy/AudioStreamTrack.cpp
+LOCAL_SRC_FILES += utility/HandleTracker.cpp
+LOCAL_SRC_FILES += utility/OboeUtilities.cpp
+
+LOCAL_CFLAGS += -Wno-unused-parameter
+LOCAL_CFLAGS += -Wall -Werror
+# By default, all symbols are hidden.
+LOCAL_CFLAGS += -fvisibility=hidden
+# OBOE_API is used to explicitly export a function or a variable as a visible symbol.
+LOCAL_CFLAGS += -DOBOE_API='__attribute__((visibility("default")))'
+
+LOCAL_SHARED_LIBRARIES := libaudioclient liblog libutils
+include $(BUILD_SHARED_LIBRARY)
diff --git a/media/liboboe/src/core/AudioStream.cpp b/media/liboboe/src/core/AudioStream.cpp
new file mode 100644
index 0000000..f154002
--- /dev/null
+++ b/media/liboboe/src/core/AudioStream.cpp
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2015 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.
+ */
+
+#define LOG_TAG "OboeAudio"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include <stdint.h>
+#include <oboe/OboeAudio.h>
+
+#include "AudioStreamBuilder.h"
+#include "AudioStream.h"
+#include "AudioClock.h"
+
+using namespace oboe;
+
+/*
+ * AudioStream
+ */
+AudioStream::AudioStream() {
+}
+
+oboe_result_t AudioStream::open(const AudioStreamBuilder& builder)
+{
+ // TODO validate parameters.
+ // Copy parameters from the Builder because the Builder may be deleted after this call.
+ mSamplesPerFrame = builder.getSamplesPerFrame();
+ mSampleRate = builder.getSampleRate();
+ mDeviceId = builder.getDeviceId();
+ mFormat = builder.getFormat();
+ mSharingMode = builder.getSharingMode();
+ return OBOE_OK;
+}
+
+AudioStream::~AudioStream() {
+ close();
+}
+
+oboe_result_t AudioStream::waitForStateTransition(oboe_stream_state_t startingState,
+ oboe_stream_state_t endingState,
+ oboe_nanoseconds_t timeoutNanoseconds)
+{
+ oboe_stream_state_t state = getState();
+ oboe_stream_state_t nextState = state;
+ if (state == startingState && state != endingState) {
+ oboe_result_t result = waitForStateChange(state, &nextState, timeoutNanoseconds);
+ if (result != OBOE_OK) {
+ return result;
+ }
+ }
+// It's OK if the expected transition has already occurred.
+// But if we reach an unexpected state then that is an error.
+ if (nextState != endingState) {
+ return OBOE_ERROR_UNEXPECTED_STATE;
+ } else {
+ return OBOE_OK;
+ }
+}
+
+oboe_result_t AudioStream::waitForStateChange(oboe_stream_state_t currentState,
+ oboe_stream_state_t *nextState,
+ oboe_nanoseconds_t timeoutNanoseconds)
+{
+ // TODO replace this when similar functionality added to AudioTrack.cpp
+ oboe_nanoseconds_t durationNanos = 20 * OBOE_NANOS_PER_MILLISECOND;
+ oboe_stream_state_t state = getState();
+ while (state == currentState && timeoutNanoseconds > 0) {
+ if (durationNanos > timeoutNanoseconds) {
+ durationNanos = timeoutNanoseconds;
+ }
+ AudioClock::sleepForNanos(durationNanos);
+ timeoutNanoseconds -= durationNanos;
+
+ oboe_result_t result = updateState();
+ if (result != OBOE_OK) {
+ return result;
+ }
+
+ state = getState();
+ }
+ if (nextState != NULL) {
+ *nextState = state;
+ }
+ return (state == currentState) ? OBOE_ERROR_TIMEOUT : OBOE_OK;
+}
+
+oboe_result_t AudioStream::createThread(oboe_nanoseconds_t periodNanoseconds,
+ void *(*startRoutine)(void *), void *arg)
+{
+ if (mHasThread) {
+ return OBOE_ERROR_INVALID_STATE;
+ }
+ if (startRoutine == NULL) {
+ return OBOE_ERROR_NULL;
+ }
+ int err = pthread_create(&mThread, NULL, startRoutine, arg);
+ if (err != 0) {
+ return OBOE_ERROR_INTERNAL;
+ } else {
+ mHasThread = true;
+ return OBOE_OK;
+ }
+}
+
+oboe_result_t AudioStream::joinThread(void **returnArg, oboe_nanoseconds_t timeoutNanoseconds)
+{
+ if (!mHasThread) {
+ return OBOE_ERROR_INVALID_STATE;
+ }
+#if 0
+ // TODO implement equivalent of pthread_timedjoin_np()
+ struct timespec abstime;
+ int err = pthread_timedjoin_np(mThread, returnArg, &abstime);
+#else
+ int err = pthread_join(mThread, returnArg);
+#endif
+ mHasThread = false;
+ // TODO Just leaked a thread?
+ return err ? OBOE_ERROR_INTERNAL : OBOE_OK;
+}
+
diff --git a/media/liboboe/src/core/AudioStream.h b/media/liboboe/src/core/AudioStream.h
new file mode 100644
index 0000000..9cb9b1b
--- /dev/null
+++ b/media/liboboe/src/core/AudioStream.h
@@ -0,0 +1,242 @@
+/*
+ * Copyright 2016 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_AUDIOSTREAM_H
+#define OBOE_AUDIOSTREAM_H
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <oboe/OboeAudio.h>
+#include "OboeUtilities.h"
+#include "MonotonicCounter.h"
+
+namespace oboe {
+
+class AudioStreamBuilder;
+
+/**
+ * Oboe audio stream.
+ */
+class AudioStream {
+public:
+
+ AudioStream();
+
+ virtual ~AudioStream();
+
+
+ // =========== Begin ABSTRACT methods ===========================
+
+ /* Asynchronous requests.
+ * Use waitForStateChange() to wait for completion.
+ */
+ virtual oboe_result_t requestStart() = 0;
+ virtual oboe_result_t requestPause() = 0;
+ virtual oboe_result_t requestFlush() = 0;
+ virtual oboe_result_t requestStop() = 0;
+
+ // TODO use oboe_clockid_t all the way down to AudioClock
+ virtual oboe_result_t getTimestamp(clockid_t clockId,
+ oboe_position_frames_t *framePosition,
+ oboe_nanoseconds_t *timeNanoseconds) = 0;
+
+
+ virtual oboe_result_t updateState() = 0;
+
+
+ // =========== End ABSTRACT methods ===========================
+
+ virtual oboe_result_t waitForStateChange(oboe_stream_state_t currentState,
+ oboe_stream_state_t *nextState,
+ oboe_nanoseconds_t timeoutNanoseconds);
+
+ /**
+ * Open the stream using the parameters in the builder.
+ * Allocate the necessary resources.
+ */
+ virtual oboe_result_t open(const AudioStreamBuilder& builder);
+
+ /**
+ * Close the stream and deallocate any resources from the open() call.
+ * It is safe to call close() multiple times.
+ */
+ virtual oboe_result_t close() {
+ return OBOE_OK;
+ }
+
+ virtual oboe_result_t setBufferSize(oboe_size_frames_t requestedFrames,
+ oboe_size_frames_t *actualFrames) {
+ return OBOE_ERROR_UNIMPLEMENTED;
+ }
+
+ virtual oboe_result_t createThread(oboe_nanoseconds_t periodNanoseconds,
+ void *(*start_routine)(void *), void *arg);
+
+ virtual oboe_result_t joinThread(void **returnArg, oboe_nanoseconds_t timeoutNanoseconds);
+
+ // ============== Queries ===========================
+
+ virtual oboe_stream_state_t getState() const {
+ return mState;
+ }
+
+ virtual oboe_size_frames_t getBufferSize() const {
+ return OBOE_ERROR_UNIMPLEMENTED;
+ }
+
+ virtual oboe_size_frames_t getBufferCapacity() const {
+ return OBOE_ERROR_UNIMPLEMENTED;
+ }
+
+ virtual oboe_size_frames_t getFramesPerBurst() const {
+ return OBOE_ERROR_UNIMPLEMENTED;
+ }
+
+ virtual int32_t getXRunCount() const {
+ return OBOE_ERROR_UNIMPLEMENTED;
+ }
+
+ bool isPlaying() const {
+ return mState == OBOE_STREAM_STATE_STARTING || mState == OBOE_STREAM_STATE_STARTED;
+ }
+
+ oboe_result_t getSampleRate() const {
+ return mSampleRate;
+ }
+
+ oboe_audio_format_t getFormat() const {
+ return mFormat;
+ }
+
+ oboe_result_t getSamplesPerFrame() const {
+ return mSamplesPerFrame;
+ }
+
+ OboeDeviceId getDeviceId() const {
+ return mDeviceId;
+ }
+
+ oboe_sharing_mode_t getSharingMode() const {
+ return mSharingMode;
+ }
+
+ oboe_direction_t getDirection() const {
+ return mDirection;
+ }
+
+ oboe_size_bytes_t getBytesPerFrame() const {
+ return mSamplesPerFrame * getBytesPerSample();
+ }
+
+ oboe_size_bytes_t getBytesPerSample() const {
+ return OboeConvert_formatToSizeInBytes(mFormat);
+ }
+
+ virtual oboe_position_frames_t getFramesWritten() {
+ return mFramesWritten.get();
+ }
+
+ virtual oboe_position_frames_t getFramesRead() {
+ return mFramesRead.get();
+ }
+
+
+ // ============== I/O ===========================
+ // A Stream will only implement read() or write() depending on its direction.
+ virtual oboe_result_t write(const void *buffer,
+ oboe_size_frames_t numFrames,
+ oboe_nanoseconds_t timeoutNanoseconds) {
+ return OBOE_ERROR_UNIMPLEMENTED;
+ }
+
+ virtual oboe_result_t read(void *buffer,
+ oboe_size_frames_t numFrames,
+ oboe_nanoseconds_t timeoutNanoseconds) {
+ return OBOE_ERROR_UNIMPLEMENTED;
+ }
+
+protected:
+
+ virtual oboe_position_frames_t incrementFramesWritten(oboe_size_frames_t frames) {
+ return static_cast<oboe_position_frames_t>(mFramesWritten.increment(frames));
+ }
+
+ virtual oboe_position_frames_t incrementFramesRead(oboe_size_frames_t frames) {
+ return static_cast<oboe_position_frames_t>(mFramesRead.increment(frames));
+ }
+
+ /**
+ * Wait for a transition from one state to another.
+ * @return OBOE_OK if the endingState was observed, or OBOE_ERROR_UNEXPECTED_STATE
+ * if any state that was not the startingState or endingState was observed
+ * or OBOE_ERROR_TIMEOUT
+ */
+ virtual oboe_result_t waitForStateTransition(oboe_stream_state_t startingState,
+ oboe_stream_state_t endingState,
+ oboe_nanoseconds_t timeoutNanoseconds);
+
+ /**
+ * This should not be called after the open() call.
+ */
+ void setSampleRate(oboe_sample_rate_t sampleRate) {
+ mSampleRate = sampleRate;
+ }
+
+ /**
+ * This should not be called after the open() call.
+ */
+ void setSamplesPerFrame(int32_t samplesPerFrame) {
+ mSamplesPerFrame = samplesPerFrame;
+ }
+
+ /**
+ * This should not be called after the open() call.
+ */
+ void setSharingMode(oboe_sharing_mode_t sharingMode) {
+ mSharingMode = sharingMode;
+ }
+
+ /**
+ * This should not be called after the open() call.
+ */
+ void setFormat(oboe_audio_format_t format) {
+ mFormat = format;
+ }
+
+ void setState(oboe_stream_state_t state) {
+ mState = state;
+ }
+
+ MonotonicCounter mFramesWritten;
+ MonotonicCounter mFramesRead;
+
+private:
+ // These do not change after open().
+ int32_t mSamplesPerFrame = OBOE_UNSPECIFIED;
+ oboe_sample_rate_t mSampleRate = OBOE_UNSPECIFIED;
+ oboe_stream_state_t mState = OBOE_STREAM_STATE_UNINITIALIZED;
+ OboeDeviceId mDeviceId = OBOE_UNSPECIFIED;
+ oboe_sharing_mode_t mSharingMode = OBOE_SHARING_MODE_LEGACY;
+ oboe_audio_format_t mFormat = OBOE_UNSPECIFIED;
+ oboe_direction_t mDirection = OBOE_DIRECTION_OUTPUT;
+
+ bool mHasThread;
+ pthread_t mThread;
+};
+
+} /* namespace oboe */
+
+#endif /* OBOE_AUDIOSTREAM_H */
diff --git a/media/liboboe/src/core/AudioStreamBuilder.cpp b/media/liboboe/src/core/AudioStreamBuilder.cpp
new file mode 100644
index 0000000..56e6706
--- /dev/null
+++ b/media/liboboe/src/core/AudioStreamBuilder.cpp
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2015 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.
+ */
+
+#define LOG_TAG "OboeAudio"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include <sys/types.h>
+#include "AudioStream.h"
+#include "AudioStreamBuilder.h"
+#include "AudioStreamRecord.h"
+#include "AudioStreamTrack.h"
+
+using namespace oboe;
+
+/*
+ * AudioStreamBuilder
+ */
+AudioStreamBuilder::AudioStreamBuilder() {
+}
+
+AudioStreamBuilder::~AudioStreamBuilder() {
+}
+
+oboe_result_t AudioStreamBuilder::build(AudioStream **streamPtr) {
+ // TODO Is there a better place to put the code that decides which class to use?
+ AudioStream *audioStream = nullptr;
+ const oboe_sharing_mode_t sharingMode = getSharingMode();
+ switch (getDirection()) {
+ case OBOE_DIRECTION_INPUT:
+ switch (sharingMode) {
+ case OBOE_SHARING_MODE_LEGACY:
+ audioStream = new AudioStreamRecord();
+ break;
+ default:
+ ALOGE("AudioStreamBuilder(): bad sharing mode = %d", sharingMode);
+ return OBOE_ERROR_ILLEGAL_ARGUMENT;
+ break;
+ }
+ break;
+ case OBOE_DIRECTION_OUTPUT:
+ switch (sharingMode) {
+ case OBOE_SHARING_MODE_LEGACY:
+ audioStream = new AudioStreamTrack();
+ break;
+ default:
+ ALOGE("AudioStreamBuilder(): bad sharing mode = %d", sharingMode);
+ return OBOE_ERROR_ILLEGAL_ARGUMENT;
+ break;
+ }
+ break;
+ default:
+ ALOGE("AudioStreamBuilder(): bad direction = %d", getDirection());
+ return OBOE_ERROR_ILLEGAL_ARGUMENT;
+ break;
+ }
+ if (audioStream == nullptr) {
+ return OBOE_ERROR_NO_MEMORY;
+ }
+ ALOGD("AudioStreamBuilder(): created audioStream = %p", audioStream);
+
+ // TODO maybe move this out of build and pass the builder to the constructors
+ // Open the stream using the parameters from the builder.
+ const oboe_result_t result = audioStream->open(*this);
+ if (result != OBOE_OK) {
+ delete audioStream;
+ } else {
+ *streamPtr = audioStream;
+ }
+ return result;
+}
diff --git a/media/liboboe/src/core/AudioStreamBuilder.h b/media/liboboe/src/core/AudioStreamBuilder.h
new file mode 100644
index 0000000..3f98ebb
--- /dev/null
+++ b/media/liboboe/src/core/AudioStreamBuilder.h
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2015 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_AUDIOSTREAMBUILDER_H
+#define OBOE_AUDIOSTREAMBUILDER_H
+
+#include <oboe/OboeAudio.h>
+#include "AudioStream.h"
+
+namespace oboe {
+
+/**
+ * Factory class for an AudioStream.
+ */
+class AudioStreamBuilder {
+public:
+ AudioStreamBuilder();
+
+ ~AudioStreamBuilder();
+
+ int getSamplesPerFrame() const {
+ return mSamplesPerFrame;
+ }
+
+ /**
+ * This is also known as channelCount.
+ */
+ AudioStreamBuilder *setSamplesPerFrame(int samplesPerFrame) {
+ mSamplesPerFrame = samplesPerFrame;
+ return this;
+ }
+
+ oboe_direction_t getDirection() const {
+ return mDirection;
+ }
+
+ AudioStreamBuilder *setDirection(oboe_direction_t direction) {
+ mDirection = direction;
+ return this;
+ }
+
+ oboe_sample_rate_t getSampleRate() const {
+ return mSampleRate;
+ }
+
+ AudioStreamBuilder *setSampleRate(oboe_sample_rate_t sampleRate) {
+ mSampleRate = sampleRate;
+ return this;
+ }
+
+ oboe_audio_format_t getFormat() const {
+ return mFormat;
+ }
+
+ AudioStreamBuilder *setFormat(oboe_audio_format_t format) {
+ mFormat = format;
+ return this;
+ }
+
+ oboe_sharing_mode_t getSharingMode() const {
+ return mSharingMode;
+ }
+
+ AudioStreamBuilder *setSharingMode(oboe_sharing_mode_t sharingMode) {
+ mSharingMode = sharingMode;
+ return this;
+ }
+
+ OboeDeviceId getDeviceId() const {
+ return mDeviceId;
+ }
+
+ AudioStreamBuilder *setDeviceId(OboeDeviceId deviceId) {
+ mDeviceId = deviceId;
+ return this;
+ }
+
+ oboe_result_t build(AudioStream **streamPtr);
+
+private:
+ int32_t mSamplesPerFrame = OBOE_UNSPECIFIED;
+ oboe_sample_rate_t mSampleRate = OBOE_UNSPECIFIED;
+ OboeDeviceId mDeviceId = OBOE_UNSPECIFIED; // TODO need better default
+ oboe_sharing_mode_t mSharingMode = OBOE_SHARING_MODE_LEGACY;
+ oboe_audio_format_t mFormat = OBOE_UNSPECIFIED;
+ oboe_direction_t mDirection = OBOE_DIRECTION_OUTPUT;
+};
+
+} /* namespace oboe */
+
+#endif /* OBOE_AUDIOSTREAMBUILDER_H */
diff --git a/media/liboboe/src/core/OboeAudio.cpp b/media/liboboe/src/core/OboeAudio.cpp
new file mode 100644
index 0000000..fc1b021
--- /dev/null
+++ b/media/liboboe/src/core/OboeAudio.cpp
@@ -0,0 +1,555 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#define LOG_TAG "OboeAudio"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include <time.h>
+#include <pthread.h>
+
+#include <oboe/OboeDefinitions.h>
+#include <oboe/OboeAudio.h>
+#include "AudioStreamBuilder.h"
+#include "AudioStream.h"
+#include "AudioClock.h"
+#include "HandleTracker.h"
+
+// temporary, as I stage in the MMAP/NOIRQ support, do not review
+#ifndef OBOE_SUPPORT_MMAP
+#define OBOE_SUPPORT_MMAP 0
+#endif
+
+#if OBOE_SUPPORT_MMAP
+#include "AudioStreamInternal.h"
+#include "OboeServiceGateway.h"
+#endif
+
+using namespace oboe;
+
+// This is not the maximum theoretic possible number of handles that the HandlerTracker
+// class could support; instead it is the maximum number of handles that we are configuring
+// for our HandleTracker instance (sHandleTracker).
+#define OBOE_MAX_HANDLES 64
+
+// Macros for common code that includes a return.
+// TODO Consider using do{}while(0) construct. I tried but it hung AndroidStudio
+#define CONVERT_BUILDER_HANDLE_OR_RETURN() \
+ convertOboeBuilderToStreamBuilder(builder); \
+ if (streamBuilder == nullptr) { \
+ return OBOE_ERROR_INVALID_HANDLE; \
+ }
+
+#define COMMON_GET_FROM_BUILDER_OR_RETURN(resultPtr) \
+ CONVERT_BUILDER_HANDLE_OR_RETURN() \
+ if ((resultPtr) == nullptr) { \
+ return OBOE_ERROR_NULL; \
+ }
+
+#define CONVERT_STREAM_HANDLE_OR_RETURN() \
+ convertOboeStreamToAudioStream(stream); \
+ if (audioStream == nullptr) { \
+ return OBOE_ERROR_INVALID_HANDLE; \
+ }
+
+#define COMMON_GET_FROM_STREAM_OR_RETURN(resultPtr) \
+ CONVERT_STREAM_HANDLE_OR_RETURN(); \
+ if ((resultPtr) == nullptr) { \
+ return OBOE_ERROR_NULL; \
+ }
+
+static HandleTracker sHandleTracker(OBOE_MAX_HANDLES);
+
+typedef enum
+{
+ OBOE_HANDLE_TYPE_STREAM,
+ OBOE_HANDLE_TYPE_STREAM_BUILDER,
+ OBOE_HANDLE_TYPE_COUNT
+} oboe_handle_type_t;
+static_assert(OBOE_HANDLE_TYPE_COUNT <= HANDLE_TRACKER_MAX_TYPES, "Too many handle types.");
+
+#if OBOE_SUPPORT_MMAP
+static OboeServiceGateway sOboeServiceGateway;
+#endif
+
+#define OBOE_CASE_ENUM(name) case name: return #name
+
+OBOE_API const char * Oboe_convertResultToText(oboe_result_t returnCode) {
+ switch (returnCode) {
+ OBOE_CASE_ENUM(OBOE_OK);
+ OBOE_CASE_ENUM(OBOE_ERROR_ILLEGAL_ARGUMENT);
+ OBOE_CASE_ENUM(OBOE_ERROR_INCOMPATIBLE);
+ OBOE_CASE_ENUM(OBOE_ERROR_INTERNAL);
+ OBOE_CASE_ENUM(OBOE_ERROR_INVALID_STATE);
+ OBOE_CASE_ENUM(OBOE_ERROR_INVALID_HANDLE);
+ OBOE_CASE_ENUM(OBOE_ERROR_INVALID_QUERY);
+ OBOE_CASE_ENUM(OBOE_ERROR_UNIMPLEMENTED);
+ OBOE_CASE_ENUM(OBOE_ERROR_UNAVAILABLE);
+ OBOE_CASE_ENUM(OBOE_ERROR_NO_FREE_HANDLES);
+ OBOE_CASE_ENUM(OBOE_ERROR_NO_MEMORY);
+ OBOE_CASE_ENUM(OBOE_ERROR_NULL);
+ OBOE_CASE_ENUM(OBOE_ERROR_TIMEOUT);
+ OBOE_CASE_ENUM(OBOE_ERROR_WOULD_BLOCK);
+ OBOE_CASE_ENUM(OBOE_ERROR_INVALID_ORDER);
+ OBOE_CASE_ENUM(OBOE_ERROR_OUT_OF_RANGE);
+ }
+ return "Unrecognized Oboe error.";
+}
+
+OBOE_API const char * Oboe_convertStreamStateToText(oboe_stream_state_t state) {
+ switch (state) {
+ OBOE_CASE_ENUM(OBOE_STREAM_STATE_UNINITIALIZED);
+ OBOE_CASE_ENUM(OBOE_STREAM_STATE_OPEN);
+ OBOE_CASE_ENUM(OBOE_STREAM_STATE_STARTING);
+ OBOE_CASE_ENUM(OBOE_STREAM_STATE_STARTED);
+ OBOE_CASE_ENUM(OBOE_STREAM_STATE_PAUSING);
+ OBOE_CASE_ENUM(OBOE_STREAM_STATE_PAUSED);
+ OBOE_CASE_ENUM(OBOE_STREAM_STATE_FLUSHING);
+ OBOE_CASE_ENUM(OBOE_STREAM_STATE_FLUSHED);
+ OBOE_CASE_ENUM(OBOE_STREAM_STATE_STOPPING);
+ OBOE_CASE_ENUM(OBOE_STREAM_STATE_STOPPED);
+ OBOE_CASE_ENUM(OBOE_STREAM_STATE_CLOSING);
+ OBOE_CASE_ENUM(OBOE_STREAM_STATE_CLOSED);
+ }
+ return "Unrecognized Oboe state.";
+}
+
+#undef OBOE_CASE_ENUM
+
+static AudioStream *convertOboeStreamToAudioStream(OboeStream stream)
+{
+ return (AudioStream *) sHandleTracker.get(OBOE_HANDLE_TYPE_STREAM,
+ (oboe_handle_t) stream);
+}
+
+static AudioStreamBuilder *convertOboeBuilderToStreamBuilder(OboeStreamBuilder builder)
+{
+ return (AudioStreamBuilder *) sHandleTracker.get(OBOE_HANDLE_TYPE_STREAM_BUILDER,
+ (oboe_handle_t) builder);
+}
+
+OBOE_API oboe_result_t Oboe_createStreamBuilder(OboeStreamBuilder *builder)
+{
+ ALOGD("Oboe_createStreamBuilder(): check sHandleTracker.isInitialized ()");
+ if (!sHandleTracker.isInitialized()) {
+ return OBOE_ERROR_NO_MEMORY;
+ }
+ AudioStreamBuilder *audioStreamBuilder = new AudioStreamBuilder();
+ if (audioStreamBuilder == nullptr) {
+ return OBOE_ERROR_NO_MEMORY;
+ }
+ ALOGD("Oboe_createStreamBuilder(): created AudioStreamBuilder = %p", audioStreamBuilder);
+ // TODO protect the put() with a Mutex
+ OboeStreamBuilder handle = sHandleTracker.put(OBOE_HANDLE_TYPE_STREAM_BUILDER,
+ audioStreamBuilder);
+ if (handle < 0) {
+ delete audioStreamBuilder;
+ return static_cast<oboe_result_t>(handle);
+ } else {
+ *builder = handle;
+ }
+ return OBOE_OK;
+}
+
+OBOE_API oboe_result_t OboeStreamBuilder_setDeviceId(OboeStreamBuilder builder,
+ OboeDeviceId deviceId)
+{
+ AudioStreamBuilder *streamBuilder = CONVERT_BUILDER_HANDLE_OR_RETURN();
+ streamBuilder->setDeviceId(deviceId);
+ return OBOE_OK;
+}
+
+OBOE_API oboe_result_t OboeStreamBuilder_setSampleRate(OboeStreamBuilder builder,
+ oboe_sample_rate_t sampleRate)
+{
+ AudioStreamBuilder *streamBuilder = CONVERT_BUILDER_HANDLE_OR_RETURN();
+ streamBuilder->setSampleRate(sampleRate);
+ return OBOE_OK;
+}
+
+OBOE_API oboe_result_t OboeStreamBuilder_getSampleRate(OboeStreamBuilder builder,
+ oboe_sample_rate_t *sampleRate)
+{
+ AudioStreamBuilder *streamBuilder = COMMON_GET_FROM_BUILDER_OR_RETURN(sampleRate);
+ *sampleRate = streamBuilder->getSampleRate();
+ return OBOE_OK;
+}
+
+OBOE_API oboe_result_t OboeStreamBuilder_setSamplesPerFrame(OboeStreamBuilder builder,
+ int32_t samplesPerFrame)
+{
+ AudioStreamBuilder *streamBuilder = CONVERT_BUILDER_HANDLE_OR_RETURN();
+ streamBuilder->setSamplesPerFrame(samplesPerFrame);
+ return OBOE_OK;
+}
+
+OBOE_API oboe_result_t OboeStreamBuilder_getSamplesPerFrame(OboeStreamBuilder builder,
+ int32_t *samplesPerFrame)
+{
+ AudioStreamBuilder *streamBuilder = COMMON_GET_FROM_BUILDER_OR_RETURN(samplesPerFrame);
+ *samplesPerFrame = streamBuilder->getSamplesPerFrame();
+ return OBOE_OK;
+}
+
+OBOE_API oboe_result_t OboeStreamBuilder_setDirection(OboeStreamBuilder builder,
+ oboe_direction_t direction)
+{
+ AudioStreamBuilder *streamBuilder = CONVERT_BUILDER_HANDLE_OR_RETURN();
+ streamBuilder->setDirection(direction);
+ return OBOE_OK;
+}
+
+OBOE_API oboe_result_t OboeStreamBuilder_getDirection(OboeStreamBuilder builder,
+ oboe_direction_t *direction)
+{
+ AudioStreamBuilder *streamBuilder = COMMON_GET_FROM_BUILDER_OR_RETURN(direction);
+ *direction = streamBuilder->getDirection();
+ return OBOE_OK;
+}
+
+OBOE_API oboe_result_t OboeStreamBuilder_setFormat(OboeStreamBuilder builder,
+ oboe_audio_format_t format)
+{
+ AudioStreamBuilder *streamBuilder = CONVERT_BUILDER_HANDLE_OR_RETURN();
+ streamBuilder->setFormat(format);
+ return OBOE_OK;
+}
+
+OBOE_API oboe_result_t OboeStreamBuilder_getFormat(OboeStreamBuilder builder,
+ oboe_audio_format_t *format)
+{
+ AudioStreamBuilder *streamBuilder = COMMON_GET_FROM_BUILDER_OR_RETURN(format);
+ *format = streamBuilder->getFormat();
+ return OBOE_OK;
+}
+
+OBOE_API oboe_result_t OboeStreamBuilder_setSharingMode(OboeStreamBuilder builder,
+ oboe_sharing_mode_t sharingMode)
+{
+ AudioStreamBuilder *streamBuilder = CONVERT_BUILDER_HANDLE_OR_RETURN();
+ if ((sharingMode < 0) || (sharingMode >= OBOE_SHARING_MODE_COUNT)) {
+ return OBOE_ERROR_ILLEGAL_ARGUMENT;
+ } else {
+ streamBuilder->setSharingMode(sharingMode);
+ return OBOE_OK;
+ }
+}
+
+OBOE_API oboe_result_t OboeStreamBuilder_getSharingMode(OboeStreamBuilder builder,
+ oboe_sharing_mode_t *sharingMode)
+{
+ AudioStreamBuilder *streamBuilder = COMMON_GET_FROM_BUILDER_OR_RETURN(sharingMode);
+ *sharingMode = streamBuilder->getSharingMode();
+ return OBOE_OK;
+}
+
+static oboe_result_t OboeInternal_openStream(AudioStreamBuilder *streamBuilder,
+ OboeStream *streamPtr)
+{
+ AudioStream *audioStream = nullptr;
+ oboe_result_t result = streamBuilder->build(&audioStream);
+ if (result != OBOE_OK) {
+ return result;
+ } else {
+ // Create a handle for referencing the object.
+ // TODO protect the put() with a Mutex
+ OboeStream handle = sHandleTracker.put(OBOE_HANDLE_TYPE_STREAM, audioStream);
+ if (handle < 0) {
+ delete audioStream;
+ return static_cast<oboe_result_t>(handle);
+ }
+ *streamPtr = handle;
+ return OBOE_OK;
+ }
+}
+
+OBOE_API oboe_result_t OboeStreamBuilder_openStream(OboeStreamBuilder builder,
+ OboeStream *streamPtr)
+{
+ ALOGD("OboeStreamBuilder_openStream(): builder = 0x%08X", builder);
+ AudioStreamBuilder *streamBuilder = COMMON_GET_FROM_BUILDER_OR_RETURN(streamPtr);
+ return OboeInternal_openStream(streamBuilder, streamPtr);
+}
+
+OBOE_API oboe_result_t OboeStreamBuilder_delete(OboeStreamBuilder builder)
+{
+ // TODO protect the remove() with a Mutex
+ AudioStreamBuilder *streamBuilder = (AudioStreamBuilder *)
+ sHandleTracker.remove(OBOE_HANDLE_TYPE_STREAM_BUILDER, builder);
+ if (streamBuilder != nullptr) {
+ delete streamBuilder;
+ return OBOE_OK;
+ }
+ return OBOE_ERROR_INVALID_HANDLE;
+}
+
+OBOE_API oboe_result_t OboeStream_close(OboeStream stream)
+{
+ // TODO protect the remove() with a Mutex
+ AudioStream *audioStream = (AudioStream *)
+ sHandleTracker.remove(OBOE_HANDLE_TYPE_STREAM, (oboe_handle_t)stream);
+ if (audioStream != nullptr) {
+ audioStream->close();
+ delete audioStream;
+ return OBOE_OK;
+ }
+ return OBOE_ERROR_INVALID_HANDLE;
+}
+
+OBOE_API oboe_result_t OboeStream_requestStart(OboeStream stream)
+{
+ AudioStream *audioStream = CONVERT_STREAM_HANDLE_OR_RETURN();
+ ALOGD("OboeStream_requestStart(0x%08X), audioStream = %p", stream, audioStream);
+ return audioStream->requestStart();
+}
+
+OBOE_API oboe_result_t OboeStream_requestPause(OboeStream stream)
+{
+ AudioStream *audioStream = CONVERT_STREAM_HANDLE_OR_RETURN();
+ ALOGD("OboeStream_requestPause(0x%08X), audioStream = %p", stream, audioStream);
+ return audioStream->requestPause();
+}
+
+OBOE_API oboe_result_t OboeStream_requestFlush(OboeStream stream)
+{
+ AudioStream *audioStream = CONVERT_STREAM_HANDLE_OR_RETURN();
+ ALOGD("OboeStream_requestFlush(0x%08X), audioStream = %p", stream, audioStream);
+ return audioStream->requestFlush();
+}
+
+OBOE_API oboe_result_t OboeStream_requestStop(OboeStream stream)
+{
+ AudioStream *audioStream = CONVERT_STREAM_HANDLE_OR_RETURN();
+ ALOGD("OboeStream_requestStop(0x%08X), audioStream = %p", stream, audioStream);
+ return audioStream->requestStop();
+}
+
+OBOE_API oboe_result_t OboeStream_waitForStateChange(OboeStream stream,
+ oboe_stream_state_t inputState,
+ oboe_stream_state_t *nextState,
+ oboe_nanoseconds_t timeoutNanoseconds)
+{
+
+ AudioStream *audioStream = CONVERT_STREAM_HANDLE_OR_RETURN();
+ return audioStream->waitForStateChange(inputState, nextState, timeoutNanoseconds);
+}
+
+// ============================================================
+// Stream - non-blocking I/O
+// ============================================================
+
+OBOE_API oboe_result_t OboeStream_read(OboeStream stream,
+ void *buffer,
+ oboe_size_frames_t numFrames,
+ oboe_nanoseconds_t timeoutNanoseconds)
+{
+ AudioStream *audioStream = CONVERT_STREAM_HANDLE_OR_RETURN();
+ if (buffer == nullptr) {
+ return OBOE_ERROR_NULL;
+ }
+ if (numFrames < 0) {
+ return OBOE_ERROR_ILLEGAL_ARGUMENT;
+ } else if (numFrames == 0) {
+ return 0;
+ }
+
+ oboe_result_t result = audioStream->read(buffer, numFrames, timeoutNanoseconds);
+ // ALOGD("OboeStream_read(): read returns %d", result);
+
+ return result;
+}
+
+OBOE_API oboe_result_t OboeStream_write(OboeStream stream,
+ const void *buffer,
+ oboe_size_frames_t numFrames,
+ oboe_nanoseconds_t timeoutNanoseconds)
+{
+ AudioStream *audioStream = CONVERT_STREAM_HANDLE_OR_RETURN();
+ if (buffer == nullptr) {
+ return OBOE_ERROR_NULL;
+ }
+ if (numFrames < 0) {
+ return OBOE_ERROR_ILLEGAL_ARGUMENT;
+ } else if (numFrames == 0) {
+ return 0;
+ }
+
+ oboe_result_t result = audioStream->write(buffer, numFrames, timeoutNanoseconds);
+ // ALOGD("OboeStream_write(): write returns %d", result);
+
+ return result;
+}
+
+// ============================================================
+// Miscellaneous
+// ============================================================
+
+OBOE_API oboe_result_t OboeStream_createThread(OboeStream stream,
+ oboe_nanoseconds_t periodNanoseconds,
+ void *(*startRoutine)(void *), void *arg)
+{
+ AudioStream *audioStream = CONVERT_STREAM_HANDLE_OR_RETURN();
+ return audioStream->createThread(periodNanoseconds, startRoutine, arg);
+}
+
+OBOE_API oboe_result_t Oboe_joinThread(OboeStream stream,
+ void **returnArg,
+ oboe_nanoseconds_t timeoutNanoseconds)
+{
+ AudioStream *audioStream = CONVERT_STREAM_HANDLE_OR_RETURN();
+ return audioStream->joinThread(returnArg, timeoutNanoseconds);
+}
+
+// ============================================================
+// Stream - queries
+// ============================================================
+
+// TODO Use oboe_clockid_t all the way down through the C++ streams.
+static clockid_t OboeConvert_fromOboeClockId(oboe_clockid_t clockid)
+{
+ clockid_t hostClockId;
+ switch (clockid) {
+ case OBOE_CLOCK_MONOTONIC:
+ hostClockId = CLOCK_MONOTONIC;
+ break;
+ case OBOE_CLOCK_BOOTTIME:
+ hostClockId = CLOCK_BOOTTIME;
+ break;
+ default:
+ hostClockId = 0; // TODO review
+ }
+ return hostClockId;
+}
+
+oboe_nanoseconds_t Oboe_getNanoseconds(oboe_clockid_t clockid)
+{
+ clockid_t hostClockId = OboeConvert_fromOboeClockId(clockid);
+ return AudioClock::getNanoseconds(hostClockId);
+}
+
+OBOE_API oboe_result_t OboeStream_getSampleRate(OboeStream stream, oboe_sample_rate_t *sampleRate)
+{
+ AudioStream *audioStream = COMMON_GET_FROM_STREAM_OR_RETURN(sampleRate);
+ *sampleRate = audioStream->getSampleRate();
+ return OBOE_OK;
+}
+
+OBOE_API oboe_result_t OboeStream_getSamplesPerFrame(OboeStream stream, int32_t *samplesPerFrame)
+{
+ AudioStream *audioStream = COMMON_GET_FROM_STREAM_OR_RETURN(samplesPerFrame);
+ *samplesPerFrame = audioStream->getSamplesPerFrame();
+ return OBOE_OK;
+}
+
+OBOE_API oboe_result_t OboeStream_getState(OboeStream stream, oboe_stream_state_t *state)
+{
+ AudioStream *audioStream = COMMON_GET_FROM_STREAM_OR_RETURN(state);
+ *state = audioStream->getState();
+ return OBOE_OK;
+}
+
+OBOE_API oboe_result_t OboeStream_getFormat(OboeStream stream, oboe_audio_format_t *format)
+{
+ AudioStream *audioStream = COMMON_GET_FROM_STREAM_OR_RETURN(format);
+ *format = audioStream->getFormat();
+ return OBOE_OK;
+}
+
+OBOE_API oboe_result_t OboeStream_setBufferSize(OboeStream stream,
+ oboe_size_frames_t requestedFrames,
+ oboe_size_frames_t *actualFrames)
+{
+ AudioStream *audioStream = CONVERT_STREAM_HANDLE_OR_RETURN();
+ return audioStream->setBufferSize(requestedFrames, actualFrames);
+}
+
+OBOE_API oboe_result_t OboeStream_getBufferSize(OboeStream stream, oboe_size_frames_t *frames)
+{
+ AudioStream *audioStream = COMMON_GET_FROM_STREAM_OR_RETURN(frames);
+ *frames = audioStream->getBufferSize();
+ return OBOE_OK;
+}
+
+OBOE_API oboe_result_t OboeStream_getDirection(OboeStream stream, int32_t *direction)
+{
+ AudioStream *audioStream = COMMON_GET_FROM_STREAM_OR_RETURN(direction);
+ *direction = audioStream->getDirection();
+ return OBOE_OK;
+}
+
+OBOE_API oboe_result_t OboeStream_getFramesPerBurst(OboeStream stream,
+ oboe_size_frames_t *framesPerBurst)
+{
+ AudioStream *audioStream = COMMON_GET_FROM_STREAM_OR_RETURN(framesPerBurst);
+ *framesPerBurst = audioStream->getFramesPerBurst();
+ return OBOE_OK;
+}
+
+OBOE_API oboe_result_t OboeStream_getBufferCapacity(OboeStream stream,
+ oboe_size_frames_t *capacity)
+{
+ AudioStream *audioStream = COMMON_GET_FROM_STREAM_OR_RETURN(capacity);
+ *capacity = audioStream->getBufferCapacity();
+ return OBOE_OK;
+}
+
+OBOE_API oboe_result_t OboeStream_getXRunCount(OboeStream stream, int32_t *xRunCount)
+{
+ AudioStream *audioStream = COMMON_GET_FROM_STREAM_OR_RETURN(xRunCount);
+ *xRunCount = audioStream->getXRunCount();
+ return OBOE_OK;
+}
+
+OBOE_API oboe_result_t OboeStream_getSharingMode(OboeStream stream,
+ oboe_sharing_mode_t *sharingMode)
+{
+ AudioStream *audioStream = COMMON_GET_FROM_STREAM_OR_RETURN(sharingMode);
+ *sharingMode = audioStream->getSharingMode();
+ return OBOE_OK;
+}
+
+OBOE_API oboe_result_t OboeStream_getFramesWritten(OboeStream stream,
+ oboe_position_frames_t *frames)
+{
+ AudioStream *audioStream = COMMON_GET_FROM_STREAM_OR_RETURN(frames);
+ *frames = audioStream->getFramesWritten();
+ return OBOE_OK;
+}
+
+OBOE_API oboe_result_t OboeStream_getFramesRead(OboeStream stream, oboe_position_frames_t *frames)
+{
+ AudioStream *audioStream = COMMON_GET_FROM_STREAM_OR_RETURN(frames);
+ *frames = audioStream->getFramesRead();
+ return OBOE_OK;
+}
+
+OBOE_API oboe_result_t OboeStream_getTimestamp(OboeStream stream,
+ oboe_clockid_t clockid,
+ oboe_position_frames_t *framePosition,
+ oboe_nanoseconds_t *timeNanoseconds)
+{
+ AudioStream *audioStream = CONVERT_STREAM_HANDLE_OR_RETURN();
+ if (framePosition == nullptr) {
+ return OBOE_ERROR_NULL;
+ } else if (timeNanoseconds == nullptr) {
+ return OBOE_ERROR_NULL;
+ } else if (clockid != OBOE_CLOCK_MONOTONIC && clockid != OBOE_CLOCK_BOOTTIME) {
+ return OBOE_ERROR_ILLEGAL_ARGUMENT;
+ }
+
+ clockid_t hostClockId = OboeConvert_fromOboeClockId(clockid);
+ return audioStream->getTimestamp(hostClockId, framePosition, timeNanoseconds);
+}
diff --git a/media/liboboe/src/core/README.md b/media/liboboe/src/core/README.md
new file mode 100644
index 0000000..dd99286
--- /dev/null
+++ b/media/liboboe/src/core/README.md
@@ -0,0 +1,2 @@
+The core folder contains the essential Oboe files common to all implementations.
+The OboeAudio.cpp contains the 'C' API.
diff --git a/media/liboboe/src/core/VersionExperiment.txt b/media/liboboe/src/core/VersionExperiment.txt
new file mode 100644
index 0000000..071239b
--- /dev/null
+++ b/media/liboboe/src/core/VersionExperiment.txt
@@ -0,0 +1,55 @@
+
+// TODO Experiment with versioning. This may be removed or changed dramatically.
+// Please ignore for now. Do not review.
+#define OBOE_VERSION_EXPERIMENT 0
+#if OBOE_VERSION_EXPERIMENT
+
+#define OBOE_EARLIEST_SUPPORTED_VERSION 1
+#define OBOE_CURRENT_VERSION 2
+
+typedef struct OboeInterface_s {
+ int32_t size; // do not use size_t because its size can vary
+ int32_t version;
+ int32_t reserved1;
+ void * reserved2;
+ oboe_result_t (*createStreamBuilder)(OboeStreamBuilder *);
+} OboeInterface_t;
+
+OboeInterface_t s_oboe_template = {
+ .size = sizeof(OboeInterface_t),
+ .version = OBOE_CURRENT_VERSION,
+ .reserved1 = 0,
+ .reserved2 = NULL,
+ .createStreamBuilder = Oboe_createStreamBuilder
+};
+
+oboe_result_t Oboe_Unimplemented(OboeInterface_t *oboe) {
+ (void) oboe;
+ return OBOE_ERROR_UNIMPLEMENTED;
+}
+
+typedef oboe_result_t (*OboeFunction_t)(OboeInterface_t *oboe);
+
+int32_t Oboe_Initialize(OboeInterface_t *oboe, uint32_t flags) {
+ if (oboe->version < OBOE_EARLIEST_SUPPORTED_VERSION) {
+ return OBOE_ERROR_INCOMPATIBLE;
+ }
+ // Fill in callers vector table.
+ uint8_t *start = (uint8_t*)&oboe->reserved1;
+ uint8_t *end;
+ if (oboe->size <= s_oboe_template.size) {
+ end = ((uint8_t *)oboe) + oboe->size;
+ } else {
+ end = ((uint8_t *)oboe) + s_oboe_template.size;
+ // Assume the rest of the structure is vectors.
+ // Point them all to OboeInternal_Unimplemented()
+ // Point to first vector past end of the known structure.
+ OboeFunction_t *next = (OboeFunction_t*)end;
+ while ((((uint8_t *)next) - ((uint8_t *)oboe)) < oboe->size) {
+ *next++ = Oboe_Unimplemented;
+ }
+ }
+ memcpy(&oboe->reserved1, &s_oboe_template.reserved1, end - start);
+ return OBOE_OK;
+}
+#endif /* OBOE_VERSION_EXPERIMENT -------------------------- */
diff --git a/media/liboboe/src/legacy/AudioStreamRecord.cpp b/media/liboboe/src/legacy/AudioStreamRecord.cpp
new file mode 100644
index 0000000..f130cad
--- /dev/null
+++ b/media/liboboe/src/legacy/AudioStreamRecord.cpp
@@ -0,0 +1,230 @@
+/*
+ * Copyright 2016 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.
+ */
+
+#define LOG_TAG "AudioStreamRecord"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include <stdint.h>
+#include <utils/String16.h>
+#include <media/AudioRecord.h>
+#include <oboe/OboeAudio.h>
+
+#include "AudioClock.h"
+#include "AudioStreamRecord.h"
+
+using namespace android;
+using namespace oboe;
+
+AudioStreamRecord::AudioStreamRecord()
+ : AudioStream()
+{
+}
+
+AudioStreamRecord::~AudioStreamRecord()
+{
+ const oboe_stream_state_t state = getState();
+ bool bad = !(state == OBOE_STREAM_STATE_UNINITIALIZED || state == OBOE_STREAM_STATE_CLOSED);
+ ALOGE_IF(bad, "stream not closed, in state %d", state);
+}
+
+oboe_result_t AudioStreamRecord::open(const AudioStreamBuilder& builder)
+{
+ oboe_result_t result = OBOE_OK;
+
+ result = AudioStream::open(builder);
+ if (result != OBOE_OK) {
+ return result;
+ }
+
+ // Try to create an AudioRecord
+
+ // TODO Support UNSPECIFIED in AudioTrack. For now, use stereo if unspecified.
+ int32_t samplesPerFrame = (getSamplesPerFrame() == OBOE_UNSPECIFIED)
+ ? 2 : getSamplesPerFrame();
+ audio_channel_mask_t channelMask = audio_channel_in_mask_from_count(samplesPerFrame);
+
+ AudioRecord::callback_t callback = NULL;
+ audio_input_flags_t flags = (audio_input_flags_t) AUDIO_INPUT_FLAG_NONE;
+
+ // TODO implement an unspecified Android format then use that.
+ audio_format_t format = (getFormat() == OBOE_UNSPECIFIED)
+ ? AUDIO_FORMAT_PCM_FLOAT
+ : OboeConvert_oboeToAndroidDataFormat(getFormat());
+
+ mAudioRecord = new AudioRecord(
+ AUDIO_SOURCE_DEFAULT,
+ getSampleRate(),
+ format,
+ channelMask,
+
+ mOpPackageName, // const String16& opPackageName TODO does not compile
+
+ 0, // size_t frameCount = 0,
+ callback,
+ NULL, // void* user = NULL,
+ 0, // uint32_t notificationFrames = 0,
+ AUDIO_SESSION_ALLOCATE,
+ AudioRecord::TRANSFER_DEFAULT,
+ flags
+ // int uid = -1,
+ // pid_t pid = -1,
+ // const audio_attributes_t* pAttributes = NULL
+ );
+
+ // Did we get a valid track?
+ status_t status = mAudioRecord->initCheck();
+ if (status != OK) {
+ close();
+ ALOGE("AudioStreamRecord::open(), initCheck() returned %d", status);
+ return OboeConvert_androidToOboeError(status);
+ }
+
+ // Get the actual rate.
+ setSampleRate(mAudioRecord->getSampleRate());
+ setSamplesPerFrame(mAudioRecord->channelCount());
+ setFormat(OboeConvert_androidToOboeDataFormat(mAudioRecord->format()));
+
+ setState(OBOE_STREAM_STATE_OPEN);
+
+ return OBOE_OK;
+}
+
+oboe_result_t AudioStreamRecord::close()
+{
+ // TODO add close() or release() to AudioRecord API then call it from here
+ if (getState() != OBOE_STREAM_STATE_CLOSED) {
+ mAudioRecord.clear();
+ setState(OBOE_STREAM_STATE_CLOSED);
+ }
+ return OBOE_OK;
+}
+
+oboe_result_t AudioStreamRecord::requestStart()
+{
+ if (mAudioRecord.get() == NULL) {
+ return OBOE_ERROR_INVALID_STATE;
+ }
+ // Get current position so we can detect when the track is playing.
+ status_t err = mAudioRecord->getPosition(&mPositionWhenStarting);
+ if (err != OK) {
+ return OboeConvert_androidToOboeError(err);
+ }
+ err = mAudioRecord->start();
+ if (err != OK) {
+ return OboeConvert_androidToOboeError(err);
+ } else {
+ setState(OBOE_STREAM_STATE_STARTING);
+ }
+ return OBOE_OK;
+}
+
+oboe_result_t AudioStreamRecord::requestPause()
+{
+ return OBOE_ERROR_UNIMPLEMENTED;
+}
+
+oboe_result_t AudioStreamRecord::requestFlush() {
+ return OBOE_ERROR_UNIMPLEMENTED;
+}
+
+oboe_result_t AudioStreamRecord::requestStop() {
+ if (mAudioRecord.get() == NULL) {
+ return OBOE_ERROR_INVALID_STATE;
+ }
+ setState(OBOE_STREAM_STATE_STOPPING);
+ mAudioRecord->stop();
+ return OBOE_OK;
+}
+
+oboe_result_t AudioStreamRecord::updateState()
+{
+ oboe_result_t result = OBOE_OK;
+ oboe_wrapping_frames_t position;
+ status_t err;
+ switch (getState()) {
+ // TODO add better state visibility to AudioRecord
+ case OBOE_STREAM_STATE_STARTING:
+ err = mAudioRecord->getPosition(&position);
+ if (err != OK) {
+ result = OboeConvert_androidToOboeError(err);
+ } else if (position != mPositionWhenStarting) {
+ setState(OBOE_STREAM_STATE_STARTED);
+ }
+ break;
+ case OBOE_STREAM_STATE_STOPPING:
+ if (mAudioRecord->stopped()) {
+ setState(OBOE_STREAM_STATE_STOPPED);
+ }
+ break;
+ default:
+ break;
+ }
+ return result;
+}
+
+oboe_result_t AudioStreamRecord::read(void *buffer,
+ oboe_size_frames_t numFrames,
+ oboe_nanoseconds_t timeoutNanoseconds)
+{
+ oboe_size_frames_t bytesPerFrame = getBytesPerFrame();
+ oboe_size_bytes_t numBytes;
+ oboe_result_t result = OboeConvert_framesToBytes(numFrames, bytesPerFrame, &numBytes);
+ if (result != OBOE_OK) {
+ return result;
+ }
+
+ // TODO add timeout to AudioRecord
+ bool blocking = (timeoutNanoseconds > 0);
+ ssize_t bytesRead = mAudioRecord->read(buffer, numBytes, blocking);
+ if (bytesRead == WOULD_BLOCK) {
+ return 0;
+ } else if (bytesRead < 0) {
+ return OboeConvert_androidToOboeError(bytesRead);
+ }
+ oboe_size_frames_t framesRead = (oboe_size_frames_t)(bytesRead / bytesPerFrame);
+ return (oboe_result_t) framesRead;
+}
+
+oboe_result_t AudioStreamRecord::setBufferSize(oboe_size_frames_t requestedFrames,
+ oboe_size_frames_t *actualFrames)
+{
+ *actualFrames = getBufferCapacity();
+ return OBOE_OK;
+}
+
+oboe_size_frames_t AudioStreamRecord::getBufferSize() const
+{
+ return getBufferCapacity(); // TODO implement in AudioRecord?
+}
+
+oboe_size_frames_t AudioStreamRecord::getBufferCapacity() const
+{
+ return static_cast<oboe_size_frames_t>(mAudioRecord->frameCount());
+}
+
+int32_t AudioStreamRecord::getXRunCount() const
+{
+ return OBOE_ERROR_UNIMPLEMENTED; // TODO implement when AudioRecord supports it
+}
+
+oboe_size_frames_t AudioStreamRecord::getFramesPerBurst() const
+{
+ return 192; // TODO add query to AudioRecord.cpp
+}
+
+// TODO implement getTimestamp
+
diff --git a/media/liboboe/src/legacy/AudioStreamRecord.h b/media/liboboe/src/legacy/AudioStreamRecord.h
new file mode 100644
index 0000000..02ff220
--- /dev/null
+++ b/media/liboboe/src/legacy/AudioStreamRecord.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2016 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 LEGACY_AUDIOSTREAMRECORD_H
+#define LEGACY_AUDIOSTREAMRECORD_H
+
+#include <media/AudioRecord.h>
+#include <oboe/OboeAudio.h>
+
+#include "AudioStreamBuilder.h"
+#include "AudioStream.h"
+#include "OboeLegacy.h"
+
+namespace oboe {
+
+/**
+ * Internal stream that uses the legacy AudioTrack path.
+ */
+class AudioStreamRecord : public AudioStream {
+public:
+ AudioStreamRecord();
+
+ virtual ~AudioStreamRecord();
+
+ virtual oboe_result_t open(const AudioStreamBuilder & builder) override;
+ virtual oboe_result_t close() override;
+
+ virtual oboe_result_t requestStart() override;
+ virtual oboe_result_t requestPause() override;
+ virtual oboe_result_t requestFlush() override;
+ virtual oboe_result_t requestStop() override;
+
+ virtual oboe_result_t getTimestamp(clockid_t clockId,
+ oboe_position_frames_t *framePosition,
+ oboe_nanoseconds_t *timeNanoseconds) override {
+ return OBOE_ERROR_UNIMPLEMENTED; // TODO
+ }
+
+ virtual oboe_result_t read(void *buffer,
+ oboe_size_frames_t numFrames,
+ oboe_nanoseconds_t timeoutNanoseconds) override;
+
+ virtual oboe_result_t setBufferSize(oboe_size_frames_t requestedFrames,
+ oboe_size_frames_t *actualFrames) override;
+
+ virtual oboe_size_frames_t getBufferSize() const override;
+
+ virtual oboe_size_frames_t getBufferCapacity() const override;
+
+ virtual int32_t getXRunCount() const override;
+
+ virtual oboe_size_frames_t getFramesPerBurst() const override;
+
+ virtual oboe_result_t updateState() override;
+
+private:
+ android::sp<android::AudioRecord> mAudioRecord;
+ // TODO add 64-bit position reporting to AudioRecord and use it.
+ oboe_wrapping_frames_t mPositionWhenStarting = 0;
+ android::String16 mOpPackageName;
+};
+
+} /* namespace oboe */
+
+#endif /* LEGACY_AUDIOSTREAMRECORD_H */
diff --git a/media/liboboe/src/legacy/AudioStreamTrack.cpp b/media/liboboe/src/legacy/AudioStreamTrack.cpp
new file mode 100644
index 0000000..5205fc5
--- /dev/null
+++ b/media/liboboe/src/legacy/AudioStreamTrack.cpp
@@ -0,0 +1,296 @@
+/*
+ * Copyright 2016 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.
+ */
+
+#define LOG_TAG "AudioStreamTrack"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include <stdint.h>
+#include <media/AudioTrack.h>
+
+#include <oboe/OboeAudio.h>
+#include "AudioClock.h"
+#include "AudioStreamTrack.h"
+
+
+using namespace android;
+using namespace oboe;
+
+/*
+ * Create a stream that uses the AudioTrack.
+ */
+AudioStreamTrack::AudioStreamTrack()
+ : AudioStream()
+{
+}
+
+AudioStreamTrack::~AudioStreamTrack()
+{
+ const oboe_stream_state_t state = getState();
+ bool bad = !(state == OBOE_STREAM_STATE_UNINITIALIZED || state == OBOE_STREAM_STATE_CLOSED);
+ ALOGE_IF(bad, "stream not closed, in state %d", state);
+}
+
+oboe_result_t AudioStreamTrack::open(const AudioStreamBuilder& builder)
+{
+ oboe_result_t result = OBOE_OK;
+
+ result = AudioStream::open(builder);
+ if (result != OK) {
+ return result;
+ }
+
+ // Try to create an AudioTrack
+ // TODO Support UNSPECIFIED in AudioTrack. For now, use stereo if unspecified.
+ int32_t samplesPerFrame = (getSamplesPerFrame() == OBOE_UNSPECIFIED)
+ ? 2 : getSamplesPerFrame();
+ audio_channel_mask_t channelMask = audio_channel_out_mask_from_count(samplesPerFrame);
+ ALOGE("AudioStreamTrack::open(), samplesPerFrame = %d, channelMask = 0x%08x",
+ samplesPerFrame, channelMask);
+
+ AudioTrack::callback_t callback = NULL;
+ // TODO add more performance options
+ audio_output_flags_t flags = (audio_output_flags_t) AUDIO_OUTPUT_FLAG_FAST;
+ size_t frameCount = 0;
+ // TODO implement an unspecified AudioTrack format then use that.
+ audio_format_t format = (getFormat() == OBOE_UNSPECIFIED)
+ ? AUDIO_FORMAT_PCM_FLOAT
+ : OboeConvert_oboeToAndroidDataFormat(getFormat());
+
+ mAudioTrack = new AudioTrack(
+ (audio_stream_type_t) AUDIO_STREAM_MUSIC,
+ getSampleRate(),
+ format,
+ channelMask,
+ frameCount,
+ flags,
+ callback,
+ NULL, // user callback data
+ 0, // notificationFrames
+ AUDIO_SESSION_ALLOCATE,
+ AudioTrack::transfer_type::TRANSFER_SYNC // TODO - this does not allow FAST
+ );
+
+ // Did we get a valid track?
+ status_t status = mAudioTrack->initCheck();
+ // FIXME - this should work - if (status != NO_ERROR) {
+ // But initCheck() is returning 1 !
+ if (status < 0) {
+ close();
+ ALOGE("AudioStreamTrack::open(), initCheck() returned %d", status);
+ return OboeConvert_androidToOboeError(status);
+ }
+
+ // Get the actual values from the AudioTrack.
+ setSamplesPerFrame(mAudioTrack->channelCount());
+ setSampleRate(mAudioTrack->getSampleRate());
+ setFormat(OboeConvert_androidToOboeDataFormat(mAudioTrack->format()));
+
+ setState(OBOE_STREAM_STATE_OPEN);
+
+ return OBOE_OK;
+}
+
+oboe_result_t AudioStreamTrack::close()
+{
+ // TODO maybe add close() or release() to AudioTrack API then call it from here
+ if (getState() != OBOE_STREAM_STATE_CLOSED) {
+ mAudioTrack.clear(); // TODO is this right?
+ setState(OBOE_STREAM_STATE_CLOSED);
+ }
+ return OBOE_OK;
+}
+
+oboe_result_t AudioStreamTrack::requestStart()
+{
+ if (mAudioTrack.get() == NULL) {
+ return OBOE_ERROR_INVALID_STATE;
+ }
+ // Get current position so we can detect when the track is playing.
+ status_t err = mAudioTrack->getPosition(&mPositionWhenStarting);
+ if (err != OK) {
+ return OboeConvert_androidToOboeError(err);
+ }
+ err = mAudioTrack->start();
+ if (err != OK) {
+ return OboeConvert_androidToOboeError(err);
+ } else {
+ setState(OBOE_STREAM_STATE_STARTING);
+ }
+ return OBOE_OK;
+}
+
+oboe_result_t AudioStreamTrack::requestPause()
+{
+ if (mAudioTrack.get() == NULL) {
+ return OBOE_ERROR_INVALID_STATE;
+ } else if (getState() != OBOE_STREAM_STATE_STARTING
+ && getState() != OBOE_STREAM_STATE_STARTED) {
+ ALOGE("requestPause(), called when state is %s", Oboe_convertStreamStateToText(getState()));
+ return OBOE_ERROR_INVALID_STATE;
+ }
+ setState(OBOE_STREAM_STATE_PAUSING);
+ mAudioTrack->pause();
+ status_t err = mAudioTrack->getPosition(&mPositionWhenPausing);
+ if (err != OK) {
+ return OboeConvert_androidToOboeError(err);
+ }
+ return OBOE_OK;
+}
+
+oboe_result_t AudioStreamTrack::requestFlush() {
+ if (mAudioTrack.get() == NULL) {
+ return OBOE_ERROR_INVALID_STATE;
+ } else if (getState() != OBOE_STREAM_STATE_PAUSED) {
+ return OBOE_ERROR_INVALID_STATE;
+ }
+ setState(OBOE_STREAM_STATE_FLUSHING);
+ incrementFramesRead(getFramesWritten() - getFramesRead());
+ mAudioTrack->flush();
+ mFramesWritten.reset32();
+ return OBOE_OK;
+}
+
+oboe_result_t AudioStreamTrack::requestStop() {
+ if (mAudioTrack.get() == NULL) {
+ return OBOE_ERROR_INVALID_STATE;
+ }
+ setState(OBOE_STREAM_STATE_STOPPING);
+ incrementFramesRead(getFramesWritten() - getFramesRead()); // TODO review
+ mAudioTrack->stop();
+ mFramesWritten.reset32();
+ return OBOE_OK;
+}
+
+oboe_result_t AudioStreamTrack::updateState()
+{
+ status_t err;
+ oboe_wrapping_frames_t position;
+ switch (getState()) {
+ // TODO add better state visibility to AudioTrack
+ case OBOE_STREAM_STATE_STARTING:
+ if (mAudioTrack->hasStarted()) {
+ setState(OBOE_STREAM_STATE_STARTED);
+ }
+ break;
+ case OBOE_STREAM_STATE_PAUSING:
+ if (mAudioTrack->stopped()) {
+ err = mAudioTrack->getPosition(&position);
+ if (err != OK) {
+ return OboeConvert_androidToOboeError(err);
+ } else if (position == mPositionWhenPausing) {
+ // Has stream really stopped advancing?
+ setState(OBOE_STREAM_STATE_PAUSED);
+ }
+ mPositionWhenPausing = position;
+ }
+ break;
+ case OBOE_STREAM_STATE_FLUSHING:
+ {
+ err = mAudioTrack->getPosition(&position);
+ if (err != OK) {
+ return OboeConvert_androidToOboeError(err);
+ } else if (position == 0) {
+ // Advance frames read to match written.
+ setState(OBOE_STREAM_STATE_FLUSHED);
+ }
+ }
+ break;
+ case OBOE_STREAM_STATE_STOPPING:
+ if (mAudioTrack->stopped()) {
+ setState(OBOE_STREAM_STATE_STOPPED);
+ }
+ break;
+ default:
+ break;
+ }
+ return OBOE_OK;
+}
+
+oboe_result_t AudioStreamTrack::write(const void *buffer,
+ oboe_size_frames_t numFrames,
+ oboe_nanoseconds_t timeoutNanoseconds)
+{
+ oboe_size_frames_t bytesPerFrame = getBytesPerFrame();
+ oboe_size_bytes_t numBytes;
+ oboe_result_t result = OboeConvert_framesToBytes(numFrames, bytesPerFrame, &numBytes);
+ if (result != OBOE_OK) {
+ return result;
+ }
+
+ // TODO add timeout to AudioTrack
+ bool blocking = timeoutNanoseconds > 0;
+ ssize_t bytesWritten = mAudioTrack->write(buffer, numBytes, blocking);
+ if (bytesWritten == WOULD_BLOCK) {
+ return 0;
+ } else if (bytesWritten < 0) {
+ ALOGE("invalid write, returned %d", (int)bytesWritten);
+ return OboeConvert_androidToOboeError(bytesWritten);
+ }
+ oboe_size_frames_t framesWritten = (oboe_size_frames_t)(bytesWritten / bytesPerFrame);
+ incrementFramesWritten(framesWritten);
+ return framesWritten;
+}
+
+oboe_result_t AudioStreamTrack::setBufferSize(oboe_size_frames_t requestedFrames,
+ oboe_size_frames_t *actualFrames)
+{
+ ssize_t result = mAudioTrack->setBufferSizeInFrames(requestedFrames);
+ if (result != OK) {
+ return OboeConvert_androidToOboeError(result);
+ } else {
+ *actualFrames = result;
+ return OBOE_OK;
+ }
+}
+
+oboe_size_frames_t AudioStreamTrack::getBufferSize() const
+{
+ return static_cast<oboe_size_frames_t>(mAudioTrack->getBufferSizeInFrames());
+}
+
+oboe_size_frames_t AudioStreamTrack::getBufferCapacity() const
+{
+ return static_cast<oboe_size_frames_t>(mAudioTrack->frameCount());
+}
+
+int32_t AudioStreamTrack::getXRunCount() const
+{
+ return static_cast<int32_t>(mAudioTrack->getUnderrunCount());
+}
+
+int32_t AudioStreamTrack::getFramesPerBurst() const
+{
+ return 192; // TODO add query to AudioTrack.cpp
+}
+
+oboe_position_frames_t AudioStreamTrack::getFramesRead() {
+ oboe_wrapping_frames_t position;
+ status_t result;
+ switch (getState()) {
+ case OBOE_STREAM_STATE_STARTING:
+ case OBOE_STREAM_STATE_STARTED:
+ case OBOE_STREAM_STATE_STOPPING:
+ result = mAudioTrack->getPosition(&position);
+ if (result == OK) {
+ mFramesRead.update32(position);
+ }
+ break;
+ default:
+ break;
+ }
+ return AudioStream::getFramesRead();
+}
diff --git a/media/liboboe/src/legacy/AudioStreamTrack.h b/media/liboboe/src/legacy/AudioStreamTrack.h
new file mode 100644
index 0000000..8c40884
--- /dev/null
+++ b/media/liboboe/src/legacy/AudioStreamTrack.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2016 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 LEGACY_AUDIOSTREAMTRACK_H
+#define LEGACY_AUDIOSTREAMTRACK_H
+
+#include <media/AudioTrack.h>
+#include <oboe/OboeAudio.h>
+
+#include "AudioStreamBuilder.h"
+#include "AudioStream.h"
+#include "OboeLegacy.h"
+
+namespace oboe {
+
+
+/**
+ * Internal stream that uses the legacy AudioTrack path.
+ */
+class AudioStreamTrack : public AudioStream {
+public:
+ AudioStreamTrack();
+
+ virtual ~AudioStreamTrack();
+
+
+ virtual oboe_result_t open(const AudioStreamBuilder & builder) override;
+ virtual oboe_result_t close() override;
+
+ virtual oboe_result_t requestStart() override;
+ virtual oboe_result_t requestPause() override;
+ virtual oboe_result_t requestFlush() override;
+ virtual oboe_result_t requestStop() override;
+
+ virtual oboe_result_t getTimestamp(clockid_t clockId,
+ oboe_position_frames_t *framePosition,
+ oboe_nanoseconds_t *timeNanoseconds) override {
+ return OBOE_ERROR_UNIMPLEMENTED; // TODO call getTimestamp(ExtendedTimestamp *timestamp);
+ }
+
+ virtual oboe_result_t write(const void *buffer,
+ oboe_size_frames_t numFrames,
+ oboe_nanoseconds_t timeoutNanoseconds) override;
+
+ virtual oboe_result_t setBufferSize(oboe_size_frames_t requestedFrames,
+ oboe_size_frames_t *actualFrames) override;
+ virtual oboe_size_frames_t getBufferSize() const override;
+ virtual oboe_size_frames_t getBufferCapacity() const override;
+ virtual oboe_size_frames_t getFramesPerBurst()const override;
+ virtual int32_t getXRunCount() const override;
+
+ virtual oboe_position_frames_t getFramesRead() override;
+
+ virtual oboe_result_t updateState() override;
+
+private:
+ android::sp<android::AudioTrack> mAudioTrack;
+ // TODO add 64-bit position reporting to AudioRecord and use it.
+ oboe_wrapping_frames_t mPositionWhenStarting = 0;
+ oboe_wrapping_frames_t mPositionWhenPausing = 0;
+};
+
+} /* namespace oboe */
+
+#endif /* LEGACY_AUDIOSTREAMTRACK_H */
diff --git a/media/liboboe/src/legacy/OboeLegacy.h b/media/liboboe/src/legacy/OboeLegacy.h
new file mode 100644
index 0000000..6803837
--- /dev/null
+++ b/media/liboboe/src/legacy/OboeLegacy.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2016 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_LEGACY_H
+#define OBOE_LEGACY_H
+
+#include <stdint.h>
+#include <oboe/OboeAudio.h>
+
+/**
+ * Common code for legacy classes.
+ */
+
+/* AudioTrack uses a 32-bit frame counter that can wrap around in about a day. */
+typedef uint32_t oboe_wrapping_frames_t;
+
+#endif /* OBOE_LEGACY_H */
diff --git a/media/liboboe/src/legacy/README.md b/media/liboboe/src/legacy/README.md
new file mode 100644
index 0000000..b51c44b
--- /dev/null
+++ b/media/liboboe/src/legacy/README.md
@@ -0,0 +1,2 @@
+The legacy folder contains the classes that implement Oboe AudioStream on top of
+Android AudioTrack and AudioRecord.
diff --git a/media/liboboe/src/utility/AudioClock.h b/media/liboboe/src/utility/AudioClock.h
new file mode 100644
index 0000000..da2f74a
--- /dev/null
+++ b/media/liboboe/src/utility/AudioClock.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2016 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 UTILITY_AUDIOCLOCK_H
+#define UTILITY_AUDIOCLOCK_H
+
+#include <sys/types.h>
+#include <time.h>
+#include "oboe/OboeDefinitions.h"
+#include "oboe/OboeAudio.h"
+
+class AudioClock {
+public:
+ static oboe_nanoseconds_t getNanoseconds(clockid_t clockId = CLOCK_MONOTONIC) {
+ struct timespec time;
+ int result = clock_gettime(clockId, &time);
+ if (result < 0) {
+ return -errno;
+ }
+ return (time.tv_sec * OBOE_NANOS_PER_SECOND) + time.tv_nsec;
+ }
+
+ /**
+ * Sleep until the specified absolute time.
+ * Return immediately with OBOE_ERROR_ILLEGAL_ARGUMENT if a negative
+ * nanoTime is specified.
+ *
+ * @param nanoTime time to wake up
+ * @param clockId CLOCK_MONOTONIC is default
+ * @return 0, a negative error, or 1 if the call is interrupted by a signal handler (EINTR)
+ */
+ static int sleepUntilNanoTime(oboe_nanoseconds_t nanoTime,
+ clockid_t clockId = CLOCK_MONOTONIC) {
+ if (nanoTime > 0) {
+ struct timespec time;
+ time.tv_sec = nanoTime / OBOE_NANOS_PER_SECOND;
+ // Calculate the fractional nanoseconds. Avoids expensive % operation.
+ time.tv_nsec = nanoTime - (time.tv_sec * OBOE_NANOS_PER_SECOND);
+ int err = clock_nanosleep(clockId, TIMER_ABSTIME, &time, nullptr);
+ switch (err) {
+ case EINTR:
+ return 1;
+ case 0:
+ return 0;
+ default:
+ // Subtract because clock_nanosleep() returns a positive error number!
+ return 0 - err;
+ }
+ } else {
+ return OBOE_ERROR_ILLEGAL_ARGUMENT;
+ }
+ }
+
+ /**
+ * Sleep for the specified number of relative nanoseconds in real-time.
+ * Return immediately with 0 if a negative nanoseconds is specified.
+ *
+ * @param nanoseconds time to sleep
+ * @param clockId CLOCK_MONOTONIC is default
+ * @return 0, a negative error, or 1 if the call is interrupted by a signal handler (EINTR)
+ */
+ static int sleepForNanos(oboe_nanoseconds_t nanoseconds, clockid_t clockId = CLOCK_MONOTONIC) {
+ if (nanoseconds > 0) {
+ struct timespec time;
+ time.tv_sec = nanoseconds / OBOE_NANOS_PER_SECOND;
+ // Calculate the fractional nanoseconds. Avoids expensive % operation.
+ time.tv_nsec = nanoseconds - (time.tv_sec * OBOE_NANOS_PER_SECOND);
+ const int flags = 0; // documented as relative sleep
+ int err = clock_nanosleep(clockId, flags, &time, nullptr);
+ switch (err) {
+ case EINTR:
+ return 1;
+ case 0:
+ return 0;
+ default:
+ // Subtract because clock_nanosleep() returns a positive error number!
+ return 0 - err;
+ }
+ }
+ return 0;
+ }
+};
+
+
+#endif // UTILITY_AUDIOCLOCK_H
diff --git a/media/liboboe/src/utility/HandleTracker.cpp b/media/liboboe/src/utility/HandleTracker.cpp
new file mode 100644
index 0000000..be2a64c
--- /dev/null
+++ b/media/liboboe/src/utility/HandleTracker.cpp
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2016 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.
+ *
+ */
+
+#define LOG_TAG "OboeAudio"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include <stdint.h>
+#include <assert.h>
+
+#include <oboe/OboeDefinitions.h>
+#include "HandleTracker.h"
+
+// Handle format is: tgggiiii
+// where each letter is 4 bits, t=type, g=generation, i=index
+
+#define TYPE_SIZE 4
+#define GENERATION_SIZE 12
+#define INDEX_SIZE 16
+
+#define GENERATION_INVALID 0
+#define GENERATION_SHIFT INDEX_SIZE
+
+#define TYPE_MASK ((1 << TYPE_SIZE) - 1)
+#define GENERATION_MASK ((1 << GENERATION_SIZE) - 1)
+#define INDEX_MASK ((1 << INDEX_SIZE) - 1)
+
+#define SLOT_UNAVAILABLE (-1)
+
+// Error if handle is negative so type is limited to bottom half.
+#define HANDLE_INVALID_TYPE TYPE_MASK
+
+static_assert(HANDLE_TRACKER_MAX_TYPES == (1 << (TYPE_SIZE - 1)),
+ "Mismatch between header and cpp.");
+static_assert(HANDLE_TRACKER_MAX_HANDLES == (1 << (INDEX_SIZE)),
+ "Mismatch between header and cpp.");
+
+HandleTracker::HandleTracker(uint32_t maxHandles)
+ : mMaxHandleCount(maxHandles)
+ , mHandleAddresses(nullptr)
+ , mHandleHeaders(nullptr)
+{
+ assert(maxHandles <= HANDLE_TRACKER_MAX_HANDLES);
+ // Allocate arrays to hold addresses and validation info.
+ mHandleAddresses = (handle_tracker_address_t *) new handle_tracker_address_t[maxHandles];
+ if (mHandleAddresses != nullptr) {
+ mHandleHeaders = new handle_tracker_header_t[maxHandles];
+ if (mHandleHeaders != nullptr) {
+ // Initialize linked list of free nodes. NULL terminated.
+ for (uint32_t i = 0; i < (maxHandles - 1); i++) {
+ mHandleAddresses[i] = &mHandleAddresses[i + 1]; // point to next node
+ mHandleHeaders[i] = 0;
+ }
+ mNextFreeAddress = &mHandleAddresses[0];
+ mHandleAddresses[maxHandles - 1] = nullptr;
+ mHandleHeaders[maxHandles - 1] = 0;
+ } else {
+ delete[] mHandleAddresses; // so the class appears uninitialized
+ }
+ }
+}
+
+HandleTracker::~HandleTracker()
+{
+ delete[] mHandleAddresses;
+ delete[] mHandleHeaders;
+}
+
+bool HandleTracker::isInitialized() const {
+ return mHandleAddresses != nullptr;
+}
+
+handle_tracker_slot_t HandleTracker::allocateSlot() {
+ void **allocated = mNextFreeAddress;
+ if (allocated == nullptr) {
+ return SLOT_UNAVAILABLE;
+ }
+ // Remove this slot from the head of the linked list.
+ mNextFreeAddress = (void **) *allocated;
+ return (allocated - mHandleAddresses);
+}
+
+handle_tracker_generation_t HandleTracker::nextGeneration(handle_tracker_slot_t index) {
+ handle_tracker_generation_t generation = (mHandleHeaders[index] + 1) & GENERATION_MASK;
+ // Avoid generation zero so that 0x0 is not a valid handle.
+ if (generation == GENERATION_INVALID) {
+ generation++;
+ }
+ return generation;
+}
+
+oboe_handle_t HandleTracker::put(handle_tracker_type_t type, void *address)
+{
+ if (type < 0 || type >= HANDLE_TRACKER_MAX_TYPES) {
+ return static_cast<oboe_handle_t>(OBOE_ERROR_OUT_OF_RANGE);
+ }
+ if (!isInitialized()) {
+ return static_cast<oboe_handle_t>(OBOE_ERROR_NO_MEMORY);
+ }
+
+ // Find an empty slot.
+ handle_tracker_slot_t index = allocateSlot();
+ if (index == SLOT_UNAVAILABLE) {
+ ALOGE("HandleTracker::put() no room for more handles");
+ return static_cast<oboe_handle_t>(OBOE_ERROR_NO_FREE_HANDLES);
+ }
+
+ // Cycle the generation counter so stale handles can be detected.
+ handle_tracker_generation_t generation = nextGeneration(index); // reads header table
+ handle_tracker_header_t inputHeader = buildHeader(type, generation);
+
+ // These two writes may need to be observed by other threads or cores during get().
+ mHandleHeaders[index] = inputHeader;
+ mHandleAddresses[index] = address;
+ // TODO use store release to enforce memory order with get()
+
+ // Generate a handle.
+ oboe_handle_t handle = buildHandle(inputHeader, index);
+
+ //ALOGD("HandleTracker::put(%p) returns 0x%08x", address, handle);
+ return handle;
+}
+
+handle_tracker_slot_t HandleTracker::handleToIndex(handle_tracker_type_t type,
+ oboe_handle_t handle) const
+{
+ // Validate the handle.
+ handle_tracker_slot_t index = extractIndex(handle);
+ if (index >= mMaxHandleCount) {
+ ALOGE("HandleTracker::handleToIndex() invalid handle = 0x%08X", handle);
+ return static_cast<oboe_handle_t>(OBOE_ERROR_INVALID_HANDLE);
+ }
+ handle_tracker_generation_t handleGeneration = extractGeneration(handle);
+ handle_tracker_header_t inputHeader = buildHeader(type, handleGeneration);
+ if (inputHeader != mHandleHeaders[index]) {
+ ALOGE("HandleTracker::handleToIndex() inputHeader = 0x%08x != mHandleHeaders[%d] = 0x%08x",
+ inputHeader, index, mHandleHeaders[index]);
+ return static_cast<oboe_handle_t>(OBOE_ERROR_INVALID_HANDLE);
+ }
+ return index;
+}
+
+handle_tracker_address_t HandleTracker::get(handle_tracker_type_t type, oboe_handle_t handle) const
+{
+ if (!isInitialized()) {
+ return nullptr;
+ }
+ handle_tracker_slot_t index = handleToIndex(type, handle);
+ if (index >= 0) {
+ return mHandleAddresses[index];
+ } else {
+ return nullptr;
+ }
+}
+
+handle_tracker_address_t HandleTracker::remove(handle_tracker_type_t type, oboe_handle_t handle) {
+ if (!isInitialized()) {
+ return nullptr;
+ }
+ handle_tracker_slot_t index = handleToIndex(type,handle);
+ if (index >= 0) {
+ handle_tracker_address_t address = mHandleAddresses[index];
+
+ // Invalidate the header type but preserve the generation count.
+ handle_tracker_generation_t generation = mHandleHeaders[index] & GENERATION_MASK;
+ handle_tracker_header_t inputHeader = buildHeader(
+ (handle_tracker_type_t) HANDLE_INVALID_TYPE, generation);
+ mHandleHeaders[index] = inputHeader;
+
+ // Add this slot to the head of the linked list.
+ mHandleAddresses[index] = mNextFreeAddress;
+ mNextFreeAddress = (handle_tracker_address_t *) &mHandleAddresses[index];
+ return address;
+ } else {
+ return nullptr;
+ }
+}
+
+oboe_handle_t HandleTracker::buildHandle(handle_tracker_header_t typeGeneration,
+ handle_tracker_slot_t index) {
+ return (oboe_handle_t)((typeGeneration << GENERATION_SHIFT) | (index & INDEX_MASK));
+}
+
+handle_tracker_header_t HandleTracker::buildHeader(handle_tracker_type_t type,
+ handle_tracker_generation_t generation)
+{
+ return (handle_tracker_header_t) (((type & TYPE_MASK) << GENERATION_SIZE)
+ | (generation & GENERATION_MASK));
+}
+
+handle_tracker_slot_t HandleTracker::extractIndex(oboe_handle_t handle)
+{
+ return handle & INDEX_MASK;
+}
+
+handle_tracker_generation_t HandleTracker::extractGeneration(oboe_handle_t handle)
+{
+ return (handle >> GENERATION_SHIFT) & GENERATION_MASK;
+}
diff --git a/media/liboboe/src/utility/HandleTracker.h b/media/liboboe/src/utility/HandleTracker.h
new file mode 100644
index 0000000..da5b654
--- /dev/null
+++ b/media/liboboe/src/utility/HandleTracker.h
@@ -0,0 +1,139 @@
+/*
+ * Copyright 2016 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 UTILITY_HANDLETRACKER_H
+#define UTILITY_HANDLETRACKER_H
+
+#include <stdint.h>
+
+typedef int32_t handle_tracker_type_t; // what kind of handle
+typedef int32_t handle_tracker_slot_t; // index in allocation table
+typedef int32_t handle_tracker_generation_t; // incremented when slot used
+typedef uint16_t handle_tracker_header_t; // combines type and generation
+typedef void *handle_tracker_address_t; // address of something that is stored here
+
+#define HANDLE_TRACKER_MAX_TYPES (1 << 3)
+#define HANDLE_TRACKER_MAX_HANDLES (1 << 16)
+
+/**
+ * Represent Objects using an integer handle that can be used with Java.
+ * This also makes the 'C' ABI more robust.
+ *
+ * Note that this should only be called from a single thread.
+ * If you call it from more than one thread then you need to use your own mutex.
+ */
+class HandleTracker {
+
+public:
+ /**
+ * @param maxHandles cannot exceed HANDLE_TRACKER_MAX_HANDLES
+ */
+ HandleTracker(uint32_t maxHandles);
+ virtual ~HandleTracker();
+
+ /**
+ * Don't use if this returns false;
+ * @return true if the internal allocation succeeded
+ */
+ bool isInitialized() const;
+
+ /**
+ * Store a pointer and return a handle that can be used to retrieve the pointer.
+ *
+ * @param type the type of the object to be tracked
+ * @param address pointer to be converted to a handle
+ * @return a valid handle or a negative error
+ */
+ oboe_handle_t put(handle_tracker_type_t expectedType, handle_tracker_address_t address);
+
+ /**
+ * Get the original pointer associated with the handle.
+ * The handle will be validated to prevent stale handles from being reused.
+ * Note that the validation is designed to prevent common coding errors and not
+ * to prevent deliberate hacking.
+ *
+ * @param expectedType shouldmatch the type we passed to put()
+ * @param handle to be converted to a pointer
+ * @return address associated with handle or nullptr
+ */
+ handle_tracker_address_t get(handle_tracker_type_t expectedType, oboe_handle_t handle) const;
+
+ /**
+ * Free up the storage associated with the handle.
+ * Subsequent attempts to use the handle will fail.
+ *
+ * @param expectedType shouldmatch the type we passed to put()
+ * @param handle to be removed from tracking
+ * @return address associated with handle or nullptr if not found
+ */
+ handle_tracker_address_t remove(handle_tracker_type_t expectedType, oboe_handle_t handle);
+
+private:
+ const int32_t mMaxHandleCount; // size of array
+ // This is const after initialization.
+ handle_tracker_address_t * mHandleAddresses; // address of objects or a free linked list node
+ // This is const after initialization.
+ handle_tracker_header_t * mHandleHeaders; // combination of type and generation
+ handle_tracker_address_t * mNextFreeAddress; // head of the linked list of free nodes in mHandleAddresses
+
+ /**
+ * Pull slot off of a list of empty slots.
+ * @return index or a negative error
+ */
+ handle_tracker_slot_t allocateSlot();
+
+ /**
+ * Validate the handle and return the corresponding index.
+ * @return slot index or a negative error
+ */
+ handle_tracker_slot_t handleToIndex(oboe_handle_t handle, handle_tracker_type_t type) const;
+
+ /**
+ * Construct a handle from a header and an index.
+ * @param header combination of a type and a generation
+ * @param index slot index returned from allocateSlot
+ * @return handle or a negative error
+ */
+ oboe_handle_t buildHandle(handle_tracker_header_t header, handle_tracker_slot_t index);
+
+ /**
+ * Combine a type and a generation field into a header.
+ */
+ static handle_tracker_header_t buildHeader(handle_tracker_type_t type,
+ handle_tracker_generation_t generation);
+
+ /**
+ * Extract the index from a handle.
+ * Does not validate the handle.
+ * @return index associated with a handle
+ */
+ static handle_tracker_slot_t extractIndex(oboe_handle_t handle);
+
+ /**
+ * Extract the generation from a handle.
+ * Does not validate the handle.
+ * @return generation associated with a handle
+ */
+ static handle_tracker_generation_t extractGeneration(oboe_handle_t handle);
+
+ /**
+ * Increment the generation for the slot, avoiding zero.
+ */
+ handle_tracker_generation_t nextGeneration(handle_tracker_slot_t index);
+
+};
+
+#endif //UTILITY_HANDLETRACKER_H
diff --git a/media/liboboe/src/utility/MonotonicCounter.h b/media/liboboe/src/utility/MonotonicCounter.h
new file mode 100644
index 0000000..befad21
--- /dev/null
+++ b/media/liboboe/src/utility/MonotonicCounter.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2016 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 UTILITY_MONOTONICCOUNTER_H
+#define UTILITY_MONOTONICCOUNTER_H
+
+#include <stdint.h>
+
+/**
+ * Maintain a 64-bit monotonic counter.
+ * Can be used to track a 32-bit counter that wraps or gets reset.
+ *
+ * Note that this is not atomic and has no interior locks.
+ * A caller will need to provide their own exterior locking
+ * if they need to use it from multiple threads.
+ */
+class MonotonicCounter {
+
+public:
+ MonotonicCounter() {};
+ virtual ~MonotonicCounter() {};
+
+ /**
+ * @return current value of the counter
+ */
+ int64_t get() const {
+ return mCounter64;
+ }
+
+ /**
+ * Advance the counter if delta is positive.
+ * @return current value of the counter
+ */
+ int64_t increment(int64_t delta) {
+ if (delta > 0) {
+ mCounter64 += delta;
+ }
+ return mCounter64;
+ }
+
+ /**
+ * Advance the 64-bit counter if (current32 - previousCurrent32) > 0.
+ * This can be used to convert a 32-bit counter that may be wrapping into
+ * a monotonic 64-bit counter.
+ *
+ * This counter32 should NOT be allowed to advance by more than 0x7FFFFFFF between calls.
+ * Think of the wrapping counter like a sine wave. If the frequency of the signal
+ * is more than half the sampling rate (Nyquist rate) then you cannot measure it properly.
+ * If the counter wraps around every 24 hours then we should measure it with a period
+ * of less than 12 hours.
+ *
+ * @return current value of the 64-bit counter
+ */
+ int64_t update32(int32_t counter32) {
+ int32_t delta = counter32 - mCounter32;
+ // protect against the mCounter64 going backwards
+ if (delta > 0) {
+ mCounter64 += delta;
+ mCounter32 = counter32;
+ }
+ return mCounter64;
+ }
+
+ /**
+ * Reset the stored value of the 32-bit counter.
+ * This is used if your counter32 has been reset to zero.
+ */
+ void reset32() {
+ mCounter32 = 0;
+ }
+
+private:
+ int64_t mCounter64 = 0;
+ int32_t mCounter32 = 0;
+};
+
+
+#endif //UTILITY_MONOTONICCOUNTER_H
diff --git a/media/liboboe/src/utility/OboeUtilities.cpp b/media/liboboe/src/utility/OboeUtilities.cpp
new file mode 100644
index 0000000..b28f7c7
--- /dev/null
+++ b/media/liboboe/src/utility/OboeUtilities.cpp
@@ -0,0 +1,160 @@
+/*
+ * Copyright 2016 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.
+ */
+
+#define LOG_TAG "OboeAudio"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <utils/Errors.h>
+
+#include "oboe/OboeDefinitions.h"
+#include "OboeUtilities.h"
+
+using namespace android;
+
+oboe_size_bytes_t OboeConvert_formatToSizeInBytes(oboe_audio_format_t format) {
+ oboe_datatype_t dataType = OBOE_AUDIO_FORMAT_DATA_TYPE(format);
+ oboe_size_bytes_t size;
+ switch (dataType) {
+ case OBOE_AUDIO_DATATYPE_UINT8:
+ size = sizeof(uint8_t);
+ break;
+ case OBOE_AUDIO_DATATYPE_INT16:
+ size = sizeof(int16_t);
+ break;
+ case OBOE_AUDIO_DATATYPE_INT32:
+ case OBOE_AUDIO_DATATYPE_INT824:
+ size = sizeof(int32_t);
+ break;
+ case OBOE_AUDIO_DATATYPE_FLOAT32:
+ size = sizeof(float);
+ break;
+ default:
+ size = OBOE_ERROR_ILLEGAL_ARGUMENT;
+ break;
+ }
+ return size;
+}
+
+// TODO This similar to a function in audio_utils. Consider using that instead.
+void OboeConvert_floatToPcm16(const float *source, int32_t numSamples, int16_t *destination) {
+ for (int i = 0; i < numSamples; i++) {
+ float fval = source[i];
+ fval += 1.0; // to avoid discontinuity at 0.0 caused by truncation
+ fval *= 32768.0f;
+ int32_t sample = (int32_t) fval;
+ // clip to 16-bit range
+ if (sample < 0) sample = 0;
+ else if (sample > 0x0FFFF) sample = 0x0FFFF;
+ sample -= 32768; // center at zero
+ destination[i] = (int16_t) sample;
+ }
+}
+
+void OboeConvert_pcm16ToFloat(const float *source, int32_t numSamples, int16_t *destination) {
+ for (int i = 0; i < numSamples; i++) {
+ destination[i] = source[i] * (1.0f / 32768.0f);
+ }
+}
+
+oboe_result_t OboeConvert_androidToOboeError(status_t error) {
+ if (error >= 0) {
+ return error;
+ }
+ oboe_result_t result;
+ switch (error) {
+ case OK:
+ result = OBOE_OK;
+ break;
+ case INVALID_OPERATION:
+ result = OBOE_ERROR_INVALID_STATE;
+ break;
+ case BAD_VALUE:
+ result = OBOE_ERROR_UNEXPECTED_VALUE;
+ break;
+ case WOULD_BLOCK:
+ result = OBOE_ERROR_WOULD_BLOCK;
+ break;
+ // TODO add more error codes
+ default:
+ result = OBOE_ERROR_INTERNAL;
+ break;
+ }
+ return result;
+}
+
+audio_format_t OboeConvert_oboeToAndroidDataFormat(oboe_audio_format_t oboeFormat) {
+ audio_format_t androidFormat;
+ switch (oboeFormat) {
+ case OBOE_AUDIO_FORMAT_PCM16:
+ androidFormat = AUDIO_FORMAT_PCM_16_BIT;
+ break;
+ case OBOE_AUDIO_FORMAT_PCM_FLOAT:
+ androidFormat = AUDIO_FORMAT_PCM_FLOAT;
+ break;
+ case OBOE_AUDIO_FORMAT_PCM824:
+ androidFormat = AUDIO_FORMAT_PCM_8_24_BIT;
+ break;
+ case OBOE_AUDIO_FORMAT_PCM32:
+ androidFormat = AUDIO_FORMAT_PCM_32_BIT;
+ break;
+ default:
+ androidFormat = AUDIO_FORMAT_DEFAULT;
+ ALOGE("OboeConvert_oboeToAndroidDataFormat 0x%08X unrecognized", oboeFormat);
+ break;
+ }
+ return androidFormat;
+}
+
+oboe_audio_format_t OboeConvert_androidToOboeDataFormat(audio_format_t androidFormat) {
+ oboe_audio_format_t oboeFormat = OBOE_AUDIO_FORMAT_INVALID;
+ switch (androidFormat) {
+ case AUDIO_FORMAT_PCM_16_BIT:
+ oboeFormat = OBOE_AUDIO_FORMAT_PCM16;
+ break;
+ case AUDIO_FORMAT_PCM_FLOAT:
+ oboeFormat = OBOE_AUDIO_FORMAT_PCM_FLOAT;
+ break;
+ case AUDIO_FORMAT_PCM_32_BIT:
+ oboeFormat = OBOE_AUDIO_FORMAT_PCM32;
+ break;
+ case AUDIO_FORMAT_PCM_8_24_BIT:
+ oboeFormat = OBOE_AUDIO_FORMAT_PCM824;
+ break;
+ default:
+ oboeFormat = OBOE_AUDIO_FORMAT_INVALID;
+ ALOGE("OboeConvert_androidToOboeDataFormat 0x%08X unrecognized", androidFormat);
+ break;
+ }
+ return oboeFormat;
+}
+
+oboe_size_bytes_t OboeConvert_framesToBytes(oboe_size_frames_t numFrames,
+ oboe_size_bytes_t bytesPerFrame,
+ oboe_size_bytes_t *sizeInBytes) {
+ // TODO implement more elegantly
+ const int32_t maxChannels = 256; // ridiculously large
+ const oboe_size_frames_t maxBytesPerFrame = maxChannels * sizeof(float);
+ // Prevent overflow by limiting multiplicands.
+ if (bytesPerFrame > maxBytesPerFrame || numFrames > (0x3FFFFFFF / maxBytesPerFrame)) {
+ ALOGE("size overflow, numFrames = %d, frameSize = %zd", numFrames, bytesPerFrame);
+ return OBOE_ERROR_OUT_OF_RANGE;
+ }
+ *sizeInBytes = numFrames * bytesPerFrame;
+ return OBOE_OK;
+}
diff --git a/media/liboboe/src/utility/OboeUtilities.h b/media/liboboe/src/utility/OboeUtilities.h
new file mode 100644
index 0000000..974ccf6
--- /dev/null
+++ b/media/liboboe/src/utility/OboeUtilities.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2016 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 UTILITY_OBOEUTILITIES_H
+#define UTILITY_OBOEUTILITIES_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <utils/Errors.h>
+#include <hardware/audio.h>
+
+#include "oboe/OboeDefinitions.h"
+
+oboe_result_t OboeConvert_androidToOboeError(android::status_t error);
+
+void OboeConvert_floatToPcm16(const float *source, int32_t numSamples, int16_t *destination);
+
+void OboeConvert_pcm16ToFloat(const int16_t *source, int32_t numSamples, float *destination);
+
+/**
+ * Calculate the number of bytes and prevent numeric overflow.
+ * @param numFrames frame count
+ * @param bytesPerFrame size of a frame in bytes
+ * @param sizeInBytes total size in bytes
+ * @return OBOE_OK or negative error, eg. OBOE_ERROR_OUT_OF_RANGE
+ */
+oboe_size_bytes_t OboeConvert_framesToBytes(oboe_size_frames_t numFrames,
+ oboe_size_bytes_t bytesPerFrame,
+ oboe_size_bytes_t *sizeInBytes);
+
+audio_format_t OboeConvert_oboeToAndroidDataFormat(oboe_audio_format_t oboe_format);
+
+oboe_audio_format_t OboeConvert_androidToOboeDataFormat(audio_format_t format);
+
+/**
+ * @return the size of a sample of the given format in bytes or OBOE_ERROR_ILLEGAL_ARGUMENT
+ */
+oboe_size_bytes_t OboeConvert_formatToSizeInBytes(oboe_audio_format_t format);
+
+#endif //UTILITY_OBOEUTILITIES_H
diff --git a/media/liboboe/src/utility/README.md b/media/liboboe/src/utility/README.md
new file mode 100644
index 0000000..9db926a
--- /dev/null
+++ b/media/liboboe/src/utility/README.md
@@ -0,0 +1,3 @@
+The utility folder contains things that may be shared between the Oboe client and server.
+They might also be handy outside Oboe.
+They generally do not depend on Oboe functionality.