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/Android.bp b/media/liboboe/Android.bp
new file mode 100644
index 0000000..0d22e65
--- /dev/null
+++ b/media/liboboe/Android.bp
@@ -0,0 +1,27 @@
+// 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.
+
+ndk_headers {
+ name: "libOboe_headers",
+ from: "include",
+ to: "",
+ srcs: ["include/oboe/*.h"],
+ license: "include/oboe/NOTICE",
+}
+
+ndk_library {
+ name: "liboboe.ndk",
+ symbol_file: "liboboe.map.txt",
+ first_version: "26",
+}
diff --git a/media/liboboe/Android.mk b/media/liboboe/Android.mk
new file mode 100644
index 0000000..5053e7d
--- /dev/null
+++ b/media/liboboe/Android.mk
@@ -0,0 +1 @@
+include $(call all-subdir-makefiles)
diff --git a/media/liboboe/include/oboe/NOTICE b/media/liboboe/include/oboe/NOTICE
new file mode 100644
index 0000000..d6c0922
--- /dev/null
+++ b/media/liboboe/include/oboe/NOTICE
@@ -0,0 +1,13 @@
+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.
diff --git a/media/liboboe/include/oboe/OboeAudio.h b/media/liboboe/include/oboe/OboeAudio.h
index 6804ce8..788cf5f 100644
--- a/media/liboboe/include/oboe/OboeAudio.h
+++ b/media/liboboe/include/oboe/OboeAudio.h
@@ -29,11 +29,14 @@
typedef int32_t OboeDeviceId;
typedef oboe_handle_t OboeStream;
typedef oboe_handle_t OboeStreamBuilder;
-typedef oboe_handle_t OboeThread;
#define OBOE_STREAM_NONE ((OboeStream)OBOE_HANDLE_INVALID)
#define OBOE_STREAM_BUILDER_NONE ((OboeStreamBuilder)OBOE_HANDLE_INVALID)
+/* OBOE_API will probably get defined in a Makefile for a specific platform. */
+#ifndef OBOE_API
+#define OBOE_API /* for exporting symbols */
+#endif
// ============================================================
// Audio System
@@ -42,7 +45,7 @@
/**
* @return time in the same clock domain as the timestamps
*/
-oboe_nanoseconds_t Oboe_getNanoseconds(oboe_clockid_t clockid);
+OBOE_API oboe_nanoseconds_t Oboe_getNanoseconds(oboe_clockid_t clockid);
/**
* The text is the ASCII symbol corresponding to the returnCode,
@@ -52,7 +55,7 @@
*
* @return pointer to a text representation of an Oboe result code.
*/
-const char * Oboe_convertResultToText(oboe_result_t returnCode);
+OBOE_API const char * Oboe_convertResultToText(oboe_result_t returnCode);
/**
* The text is the ASCII symbol corresponding to the stream state,
@@ -62,7 +65,7 @@
*
* @return pointer to a text representation of an Oboe state.
*/
-const char * Oboe_convertStreamStateToText(oboe_stream_state_t state);
+OBOE_API const char * Oboe_convertStreamStateToText(oboe_stream_state_t state);
// ============================================================
// StreamBuilder
@@ -80,7 +83,7 @@
*
* OboeStreamBuilder_delete() must be called when you are done using the builder.
*/
-oboe_result_t Oboe_createStreamBuilder(OboeStreamBuilder *builder);
+OBOE_API oboe_result_t Oboe_createStreamBuilder(OboeStreamBuilder *builder);
/**
* Request an audio device identified device using an ID.
@@ -91,7 +94,8 @@
*
* @return OBOE_OK or a negative error.
*/
-oboe_result_t OboeStreamBuilder_setDeviceId(OboeStreamBuilder builder, OboeDeviceId deviceId);
+OBOE_API oboe_result_t OboeStreamBuilder_setDeviceId(OboeStreamBuilder builder,
+ OboeDeviceId deviceId);
/**
* Request a sample rate in Hz.
@@ -106,14 +110,14 @@
*
* @return OBOE_OK or a negative error.
*/
-oboe_result_t OboeStreamBuilder_setSampleRate(OboeStreamBuilder builder,
+OBOE_API oboe_result_t OboeStreamBuilder_setSampleRate(OboeStreamBuilder builder,
oboe_sample_rate_t sampleRate);
/**
* Returns sample rate in Hertz (samples per second).
* @return OBOE_OK or a negative error.
*/
-oboe_result_t OboeStreamBuilder_getSampleRate(OboeStreamBuilder builder,
+OBOE_API oboe_result_t OboeStreamBuilder_getSampleRate(OboeStreamBuilder builder,
oboe_sample_rate_t *sampleRate);
@@ -128,7 +132,7 @@
*
* @return OBOE_OK or a negative error.
*/
-oboe_result_t OboeStreamBuilder_setSamplesPerFrame(OboeStreamBuilder builder,
+OBOE_API oboe_result_t OboeStreamBuilder_setSamplesPerFrame(OboeStreamBuilder builder,
int32_t samplesPerFrame);
/**
@@ -138,7 +142,7 @@
* @param samplesPerFrame pointer to a variable to be set to samplesPerFrame.
* @return OBOE_OK or a negative error.
*/
-oboe_result_t OboeStreamBuilder_getSamplesPerFrame(OboeStreamBuilder builder,
+OBOE_API oboe_result_t OboeStreamBuilder_getSamplesPerFrame(OboeStreamBuilder builder,
int32_t *samplesPerFrame);
@@ -148,12 +152,14 @@
*
* @return OBOE_OK or a negative error.
*/
-oboe_result_t OboeStreamBuilder_setFormat(OboeStreamBuilder builder, oboe_audio_format_t format);
+OBOE_API oboe_result_t OboeStreamBuilder_setFormat(OboeStreamBuilder builder,
+ oboe_audio_format_t format);
/**
* @return OBOE_OK or a negative error.
*/
-oboe_result_t OboeStreamBuilder_getFormat(OboeStreamBuilder builder, oboe_audio_format_t *format);
+OBOE_API oboe_result_t OboeStreamBuilder_getFormat(OboeStreamBuilder builder,
+ oboe_audio_format_t *format);
/**
* Request a mode for sharing the device.
@@ -164,15 +170,15 @@
* @param sharingMode OBOE_SHARING_MODE_LEGACY or OBOE_SHARING_MODE_EXCLUSIVE
* @return OBOE_OK or a negative error.
*/
-oboe_result_t OboeStreamBuilder_setSharingMode(OboeStreamBuilder builder,
- oboe_sharing_mode_t sharingMode);
+OBOE_API oboe_result_t OboeStreamBuilder_setSharingMode(OboeStreamBuilder builder,
+ oboe_sharing_mode_t sharingMode);
/**
* Return requested sharing mode.
* @return OBOE_OK or a negative error
*/
-oboe_result_t OboeStreamBuilder_getSharingMode(OboeStreamBuilder builder,
- oboe_sharing_mode_t *sharingMode);
+OBOE_API oboe_result_t OboeStreamBuilder_getSharingMode(OboeStreamBuilder builder,
+ oboe_sharing_mode_t *sharingMode);
/**
* Request the direction for a stream. The default is OBOE_DIRECTION_OUTPUT.
@@ -181,16 +187,16 @@
* @param direction OBOE_DIRECTION_OUTPUT or OBOE_DIRECTION_INPUT
* @return OBOE_OK or a negative error.
*/
-oboe_result_t OboeStreamBuilder_setDirection(OboeStreamBuilder builder,
- oboe_direction_t direction);
+OBOE_API oboe_result_t OboeStreamBuilder_setDirection(OboeStreamBuilder builder,
+ oboe_direction_t direction);
/**
* @param builder handle provided by Oboe_createStreamBuilder()
* @param direction pointer to a variable to be set to the currently requested direction.
* @return OBOE_OK or a negative error.
*/
-oboe_result_t OboeStreamBuilder_getDirection(OboeStreamBuilder builder,
- oboe_direction_t *direction);
+OBOE_API oboe_result_t OboeStreamBuilder_getDirection(OboeStreamBuilder builder,
+ oboe_direction_t *direction);
/**
* Open a stream based on the options in the StreamBuilder.
@@ -202,7 +208,8 @@
* @param stream pointer to a variable to receive the new stream handle
* @return OBOE_OK or a negative error.
*/
-oboe_result_t OboeStreamBuilder_openStream(OboeStreamBuilder builder, OboeStream *stream);
+OBOE_API oboe_result_t OboeStreamBuilder_openStream(OboeStreamBuilder builder,
+ OboeStream *stream);
/**
* Delete the resources associated with the StreamBuilder.
@@ -210,7 +217,7 @@
* @param builder handle provided by Oboe_createStreamBuilder()
* @return OBOE_OK or a negative error.
*/
-oboe_result_t OboeStreamBuilder_delete(OboeStreamBuilder builder);
+OBOE_API oboe_result_t OboeStreamBuilder_delete(OboeStreamBuilder builder);
// ============================================================
// Stream Control
@@ -222,7 +229,7 @@
* @param stream handle provided by OboeStreamBuilder_openStream()
* @return OBOE_OK or a negative error.
*/
-oboe_result_t OboeStream_close(OboeStream stream);
+OBOE_API oboe_result_t OboeStream_close(OboeStream stream);
/**
* Asynchronously request to start playing the stream. For output streams, one should
@@ -233,7 +240,7 @@
* @param stream handle provided by OboeStreamBuilder_openStream()
* @return OBOE_OK or a negative error.
*/
-oboe_result_t OboeStream_requestStart(OboeStream stream);
+OBOE_API oboe_result_t OboeStream_requestStart(OboeStream stream);
/**
* Asynchronous request for the stream to pause.
@@ -244,7 +251,7 @@
* @param stream handle provided by OboeStreamBuilder_openStream()
* @return OBOE_OK or a negative error.
*/
-oboe_result_t OboeStream_requestPause(OboeStream stream);
+OBOE_API oboe_result_t OboeStream_requestPause(OboeStream stream);
/**
* Asynchronous request for the stream to flush.
@@ -256,7 +263,7 @@
* @param stream handle provided by OboeStreamBuilder_openStream()
* @return OBOE_OK or a negative error.
*/
-oboe_result_t OboeStream_requestFlush(OboeStream stream);
+OBOE_API oboe_result_t OboeStream_requestFlush(OboeStream stream);
/**
* Asynchronous request for the stream to stop.
@@ -266,7 +273,7 @@
* @param stream handle provided by OboeStreamBuilder_openStream()
* @return OBOE_OK or a negative error.
*/
-oboe_result_t OboeStream_requestStop(OboeStream stream);
+OBOE_API oboe_result_t OboeStream_requestStop(OboeStream stream);
/**
* Query the current state, eg. OBOE_STREAM_STATE_PAUSING
@@ -275,7 +282,7 @@
* @param state pointer to a variable that will be set to the current state
* @return OBOE_OK or a negative error.
*/
-oboe_result_t OboeStream_getState(OboeStream stream, oboe_stream_state_t *state);
+OBOE_API oboe_result_t OboeStream_getState(OboeStream stream, oboe_stream_state_t *state);
/**
* Wait until the current state no longer matches the input state.
@@ -295,7 +302,7 @@
* @param timeoutNanoseconds Maximum number of nanoseconds to wait for completion.
* @return OBOE_OK or a negative error.
*/
-oboe_result_t OboeStream_waitForStateChange(OboeStream stream,
+OBOE_API oboe_result_t OboeStream_waitForStateChange(OboeStream stream,
oboe_stream_state_t inputState,
oboe_stream_state_t *nextState,
oboe_nanoseconds_t timeoutNanoseconds);
@@ -322,9 +329,9 @@
* @param timeoutNanoseconds Maximum number of nanoseconds to wait for completion.
* @return The number of frames actually written or a negative error.
*/
-oboe_result_t OboeStream_read(OboeStream stream,
+OBOE_API oboe_result_t OboeStream_read(OboeStream stream,
void *buffer,
- int32_t numFrames,
+ oboe_size_frames_t numFrames,
oboe_nanoseconds_t timeoutNanoseconds);
/**
@@ -345,9 +352,9 @@
* @param timeoutNanoseconds Maximum number of nanoseconds to wait for completion.
* @return The number of frames actually written or a negative error.
*/
-oboe_result_t OboeStream_write(OboeStream stream,
+OBOE_API oboe_result_t OboeStream_write(OboeStream stream,
const void *buffer,
- int32_t numFrames,
+ oboe_size_frames_t numFrames,
oboe_nanoseconds_t timeoutNanoseconds);
@@ -356,31 +363,33 @@
// ============================================================
/**
- * Create a thread with special properties for low latency audio performance.
- * This thread can be used to implement a callback API.
+ * Create a thread associated with a stream. The thread has special properties for
+ * low latency audio performance. This thread can be used to implement a callback API.
+ *
+ * Only one thread may be associated with a stream.
*
* Note that this API is in flux.
*
- * @param threadHandlePtr a pointer to receive a thread handle
+ * @param stream A stream created using OboeStreamBuilder_openStream().
* @param periodNanoseconds the estimated period at which the audio thread will need to wake up
* @param start_routine your thread entry point
* @param arg an argument that will be passed to your thread entry point
* @return OBOE_OK or a negative error.
*/
-oboe_result_t Oboe_createAudioThread(OboeThread *threadHandlePtr,
+OBOE_API oboe_result_t OboeStream_createThread(OboeStream stream,
oboe_nanoseconds_t periodNanoseconds,
- void *(*start_routine)(void *), void *arg);
+ void *(*startRoutine)(void *), void *arg);
/**
* Wait until the thread exits or an error occurs.
* The thread handle will be deleted.
*
- * @param thread the thread handle passed back from Oboe_createAudioThread()
+ * @param stream A stream created using OboeStreamBuilder_openStream().
* @param returnArg a pointer to a variable to receive the return value
* @param timeoutNanoseconds Maximum number of nanoseconds to wait for completion.
* @return OBOE_OK or a negative error.
*/
-oboe_result_t Oboe_joinAudioThread(OboeThread thread,
+OBOE_API oboe_result_t OboeStream_joinThread(OboeStream stream,
void **returnArg,
oboe_nanoseconds_t timeoutNanoseconds);
@@ -398,10 +407,13 @@
* This cannot be set higher than OboeStream_getBufferCapacity().
*
* @param stream handle provided by OboeStreamBuilder_openStream()
- * @param frames requested number of frames that can be filled without blocking
- * @return actual number of frames or a negative error
+ * @param requestedFrames requested number of frames that can be filled without blocking
+ * @return actualFrames receives final number of frames
+ * @return OBOE_OK or a negative error
*/
-oboe_result_t OboeStream_setBufferSize(OboeStream stream, oboe_size_frames_t frames);
+OBOE_API oboe_result_t OboeStream_setBufferSize(OboeStream stream,
+ oboe_size_frames_t requestedFrames,
+ oboe_size_frames_t *actualFrames);
/**
* Query the maximum number of frames that can be filled without blocking.
@@ -410,7 +422,7 @@
* @param frames pointer to variable to receive the buffer size
* @return OBOE_OK or a negative error.
*/
-oboe_result_t OboeStream_getBufferSize(OboeStream stream, oboe_size_frames_t *frames);
+OBOE_API oboe_result_t OboeStream_getBufferSize(OboeStream stream, oboe_size_frames_t *frames);
/**
* Query the number of frames that are read or written by the endpoint at one time.
@@ -419,7 +431,7 @@
* @param frames pointer to variable to receive the burst size
* @return OBOE_OK or a negative error.
*/
-oboe_result_t OboeStream_getFramesPerBurst(OboeStream stream, oboe_size_frames_t *frames);
+OBOE_API oboe_result_t OboeStream_getFramesPerBurst(OboeStream stream, oboe_size_frames_t *frames);
/**
* Query maximum buffer capacity in frames.
@@ -428,7 +440,7 @@
* @param frames pointer to variable to receive the buffer capacity
* @return OBOE_OK or a negative error.
*/
-oboe_result_t OboeStream_getBufferCapacity(OboeStream stream, oboe_size_frames_t *frames);
+OBOE_API oboe_result_t OboeStream_getBufferCapacity(OboeStream stream, oboe_size_frames_t *frames);
/**
* An XRun is an Underrun or an Overrun.
@@ -443,14 +455,14 @@
* @param xRunCount pointer to variable to receive the underrun or overrun count
* @return OBOE_OK or a negative error.
*/
-oboe_result_t OboeStream_getXRunCount(OboeStream stream, int32_t *xRunCount);
+OBOE_API oboe_result_t OboeStream_getXRunCount(OboeStream stream, int32_t *xRunCount);
/**
* @param stream handle provided by OboeStreamBuilder_openStream()
* @param sampleRate pointer to variable to receive the actual sample rate
* @return OBOE_OK or a negative error.
*/
-oboe_result_t OboeStream_getSampleRate(OboeStream stream, int32_t *sampleRate);
+OBOE_API oboe_result_t OboeStream_getSampleRate(OboeStream stream, oboe_sample_rate_t *sampleRate);
/**
* The samplesPerFrame is also known as channelCount.
@@ -459,14 +471,14 @@
* @param samplesPerFrame pointer to variable to receive the actual samples per frame
* @return OBOE_OK or a negative error.
*/
-oboe_result_t OboeStream_getSamplesPerFrame(OboeStream stream, int32_t *samplesPerFrame);
+OBOE_API oboe_result_t OboeStream_getSamplesPerFrame(OboeStream stream, int32_t *samplesPerFrame);
/**
* @param stream handle provided by OboeStreamBuilder_openStream()
* @param format pointer to variable to receive the actual data format
* @return OBOE_OK or a negative error.
*/
-oboe_result_t OboeStream_getFormat(OboeStream stream, oboe_audio_format_t *format);
+OBOE_API oboe_result_t OboeStream_getFormat(OboeStream stream, oboe_audio_format_t *format);
/**
* Provide actual sharing mode.
@@ -474,7 +486,7 @@
* @param sharingMode pointer to variable to receive the actual sharing mode
* @return OBOE_OK or a negative error.
*/
-oboe_result_t OboeStream_getSharingMode(OboeStream stream,
+OBOE_API oboe_result_t OboeStream_getSharingMode(OboeStream stream,
oboe_sharing_mode_t *sharingMode);
/**
@@ -482,7 +494,7 @@
* @param direction pointer to a variable to be set to the current direction.
* @return OBOE_OK or a negative error.
*/
-oboe_result_t OboeStream_getDirection(OboeStream stream, uint32_t *direction);
+OBOE_API oboe_result_t OboeStream_getDirection(OboeStream stream, oboe_direction_t *direction);
/**
* Passes back the number of frames that have been written since the stream was created.
@@ -495,7 +507,8 @@
* @param frames pointer to variable to receive the frames written
* @return OBOE_OK or a negative error.
*/
-oboe_result_t OboeStream_getFramesWritten(OboeStream stream, oboe_position_frames_t *frames);
+OBOE_API oboe_result_t OboeStream_getFramesWritten(OboeStream stream,
+ oboe_position_frames_t *frames);
/**
* Passes back the number of frames that have been read since the stream was created.
@@ -508,7 +521,7 @@
* @param frames pointer to variable to receive the frames written
* @return OBOE_OK or a negative error.
*/
-oboe_result_t OboeStream_getFramesRead(OboeStream stream, oboe_position_frames_t *frames);
+OBOE_API oboe_result_t OboeStream_getFramesRead(OboeStream stream, oboe_position_frames_t *frames);
/**
* Passes back the time at which a particular frame was presented.
@@ -527,12 +540,12 @@
* The position and time passed back are monotonically increasing.
*
* @param stream A handle provided by OboeStreamBuilder_openStream()
- * @param clockId OBOE_CLOCK_MONOTONIC or OBOE_CLOCK_BOOTTIME
+ * @param clockid OBOE_CLOCK_MONOTONIC or OBOE_CLOCK_BOOTTIME
* @param framePosition pointer to a variable to receive the position
* @param timeNanoseconds pointer to a variable to receive the time
* @return OBOE_OK or a negative error
*/
-oboe_result_t OboeStream_getTimestamp(OboeStream stream,
+OBOE_API oboe_result_t OboeStream_getTimestamp(OboeStream stream,
oboe_clockid_t clockid,
oboe_position_frames_t *framePosition,
oboe_nanoseconds_t *timeNanoseconds);
diff --git a/media/liboboe/include/oboe/OboeDefinitions.h b/media/liboboe/include/oboe/OboeDefinitions.h
index 79fef3a..d80c958 100644
--- a/media/liboboe/include/oboe/OboeDefinitions.h
+++ b/media/liboboe/include/oboe/OboeDefinitions.h
@@ -28,7 +28,10 @@
typedef int32_t oboe_sample_rate_t;
/** This is used for small quantities such as the number of frames in a buffer. */
typedef int32_t oboe_size_frames_t;
-/** This is used for large quantities, such as the number of frames that have
+/** This is used for small quantities such as the number of bytes in a frame. */
+typedef int32_t oboe_size_bytes_t;
+/**
+ * This is used for large quantities, such as the number of frames that have
* been played since a stream was started.
* At 48000 Hz, a 32-bit integer would wrap around in just over 12 hours.
*/
@@ -85,7 +88,8 @@
/**
* Fields packed into oboe_audio_format_t, from most to least significant bits.
- * Reserved:8
+ * Invalid:1
+ * Reserved:7
* Wrapper:8
* Content:8
* Data Type:8
@@ -97,7 +101,7 @@
OBOE_AUDIO_FORMAT(dataType, content, OBOE_AUDIO_WRAPPER_NONE)
#define OBOE_AUDIO_FORMAT_DATA_TYPE(format) \
- (format & 0x0FF)
+ ((oboe_datatype_t)(format & 0x0FF))
// Define some common formats.
#define OBOE_AUDIO_FORMAT_PCM16 \
@@ -106,6 +110,9 @@
OBOE_AUDIO_FORMAT_RAW(OBOE_AUDIO_DATATYPE_FLOAT32, OBOE_AUDIO_CONTENT_PCM)
#define OBOE_AUDIO_FORMAT_PCM824 \
OBOE_AUDIO_FORMAT_RAW(OBOE_AUDIO_DATATYPE_INT824, OBOE_AUDIO_CONTENT_PCM)
+#define OBOE_AUDIO_FORMAT_PCM32 \
+ OBOE_AUDIO_FORMAT_RAW(OBOE_AUDIO_DATATYPE_INT32, OBOE_AUDIO_CONTENT_PCM)
+#define OBOE_AUDIO_FORMAT_INVALID ((oboe_audio_format_t)-1)
enum {
OBOE_OK,
@@ -126,7 +133,8 @@
OBOE_ERROR_NULL,
OBOE_ERROR_TIMEOUT,
OBOE_ERROR_WOULD_BLOCK,
- OBOE_ERROR_INVALID_ORDER
+ OBOE_ERROR_INVALID_ORDER,
+ OBOE_ERROR_OUT_OF_RANGE
};
typedef enum {
diff --git a/media/liboboe/liboboe.map.txt b/media/liboboe/liboboe.map.txt
new file mode 100644
index 0000000..229038f
--- /dev/null
+++ b/media/liboboe/liboboe.map.txt
@@ -0,0 +1,46 @@
+LIBOBOE {
+ global:
+ Oboe_getNanoseconds;
+ Oboe_convertResultToText;
+ Oboe_convertStreamStateToText;
+ Oboe_createStreamBuilder;
+ OboeStreamBuilder_setDeviceId;
+ OboeStreamBuilder_setSampleRate;
+ OboeStreamBuilder_getSampleRate;
+ OboeStreamBuilder_setSamplesPerFrame;
+ OboeStreamBuilder_getSamplesPerFrame;
+ OboeStreamBuilder_setFormat;
+ OboeStreamBuilder_getFormat;
+ OboeStreamBuilder_setSharingMode;
+ OboeStreamBuilder_getSharingMode;
+ OboeStreamBuilder_setDirection;
+ OboeStreamBuilder_getDirection;
+ OboeStreamBuilder_openStream;
+ OboeStreamBuilder_delete;
+ OboeStream_close;
+ OboeStream_requestStart;
+ OboeStream_requestPause;
+ OboeStream_requestFlush;
+ OboeStream_requestStop;
+ OboeStream_getState;
+ OboeStream_waitForStateChange;
+ OboeStream_read;
+ OboeStream_write;
+ Oboe_createAudioThread;
+ Oboe_joinAudioThread;
+ OboeStream_setBufferSize;
+ OboeStream_getBufferSize;
+ OboeStream_getFramesPerBurst;
+ OboeStream_getBufferCapacity;
+ OboeStream_getXRunCount;
+ OboeStream_getSampleRate;
+ OboeStream_getSamplesPerFrame;
+ OboeStream_getFormat;
+ OboeStream_getSharingMode;
+ OboeStream_getDirection;
+ OboeStream_getFramesWritten;
+ OboeStream_getFramesRead;
+ OboeStream_getTimestamp;
+ local:
+ *;
+};
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.
diff --git a/media/liboboe/tests/Android.mk b/media/liboboe/tests/Android.mk
new file mode 100644
index 0000000..f2c65d9
--- /dev/null
+++ b/media/liboboe/tests/Android.mk
@@ -0,0 +1,27 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_C_INCLUDES := \
+ $(call include-path-for, audio-utils) \
+ frameworks/av/media/liboboe/include \
+ frameworks/av/media/liboboe/src/core \
+ frameworks/av/media/liboboe/src/utility
+LOCAL_SRC_FILES:= test_oboe_api.cpp
+LOCAL_SHARED_LIBRARIES := libaudioutils libmedia \
+ libbinder libcutils libutils \
+ libaudioclient liblog
+LOCAL_STATIC_LIBRARIES := liboboe
+LOCAL_MODULE := test_oboe_api
+include $(BUILD_NATIVE_TEST)
+
+include $(CLEAR_VARS)
+LOCAL_C_INCLUDES := \
+ $(call include-path-for, audio-utils) \
+ frameworks/av/media/liboboe/include \
+ frameworks/av/media/liboboe/src/core \
+ frameworks/av/media/liboboe/src/utility
+LOCAL_SRC_FILES:= test_handle_tracker.cpp
+LOCAL_SHARED_LIBRARIES := libbinder libcutils libutils liblog
+LOCAL_STATIC_LIBRARIES := liboboe
+LOCAL_MODULE := test_handle_tracker
+include $(BUILD_NATIVE_TEST)
diff --git a/media/liboboe/tests/test_handle_tracker.cpp b/media/liboboe/tests/test_handle_tracker.cpp
new file mode 100644
index 0000000..ae7384e
--- /dev/null
+++ b/media/liboboe/tests/test_handle_tracker.cpp
@@ -0,0 +1,117 @@
+/*
+ * 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.
+ */
+
+// Unit tests for Oboe Handle Tracker
+
+#include <stdlib.h>
+#include <math.h>
+
+#include <gtest/gtest.h>
+
+#include <oboe/OboeDefinitions.h>
+#include "HandleTracker.h"
+
+// Test adding one address.
+TEST(test_handle_tracker, oboe_handle_tracker) {
+ const int MAX_HANDLES = 4;
+ HandleTracker tracker(MAX_HANDLES);
+ handle_tracker_type_t type = 3; // arbitrary generic type
+ int data; // something that has an address we can use
+ handle_tracker_address_t found;
+
+ // repeat the test several times to see if it breaks
+ const int SEVERAL = 5; // arbitrary
+ for (int i = 0; i < SEVERAL; i++) {
+ // should fail to find a bogus handle
+ found = tracker.get(type, 0); // bad handle
+ EXPECT_EQ(nullptr, found);
+
+ // create a valid handle and use it to lookup the object again
+ oboe_handle_t dataHandle = tracker.put(type, &data);
+ ASSERT_TRUE(dataHandle > 0);
+ found = tracker.get(type, dataHandle);
+ EXPECT_EQ(&data, found);
+ found = tracker.get(type, 0); // bad handle
+ EXPECT_EQ(nullptr, found);
+
+ // wrong type
+ found = tracker.get(type+1, dataHandle);
+ EXPECT_EQ(nullptr, found);
+
+ // remove from storage
+ found = tracker.remove(type, dataHandle);
+ EXPECT_EQ(&data, found);
+ // should fail the second time
+ found = tracker.remove(type, dataHandle);
+ EXPECT_EQ(NULL, found);
+ }
+}
+
+// Test filling the tracker.
+TEST(test_handle_tracker, oboe_full_up) {
+ const int MAX_HANDLES = 5;
+ HandleTracker tracker(MAX_HANDLES);
+ handle_tracker_type_t type = 4; // arbitrary generic type
+ int data[MAX_HANDLES];
+ oboe_handle_t handles[MAX_HANDLES];
+ handle_tracker_address_t found;
+
+ // repeat the test several times to see if it breaks
+ const int SEVERAL = 5; // arbitrary
+ for (int i = 0; i < SEVERAL; i++) {
+ for (int i = 0; i < MAX_HANDLES; i++) {
+ // add a handle
+ handles[i] = tracker.put(type, &data[i]);
+ ASSERT_TRUE(handles[i] > 0);
+ found = tracker.get(type, handles[i]);
+ EXPECT_EQ(&data[i], found);
+ }
+
+ // Now that it is full, try to add one more.
+ oboe_handle_t handle = tracker.put(type, &data[0]);
+ EXPECT_TRUE(handle < 0);
+
+ for (int i = 0; i < MAX_HANDLES; i++) {
+ // look up each handle
+ found = tracker.get(type, handles[i]);
+ EXPECT_EQ(&data[i], found);
+ }
+
+ // remove one from storage
+ found = tracker.remove(type, handles[2]);
+ EXPECT_EQ(&data[2], found);
+ // now try to look up the same handle and fail
+ found = tracker.get(type, handles[2]);
+ EXPECT_EQ(nullptr, found);
+
+ // add that same one back
+ handle = tracker.put(type, &data[2]);
+ ASSERT_TRUE(handle > 0);
+ found = tracker.get(type, handle);
+ EXPECT_EQ(&data[2], found);
+ // now use a stale handle again with a valid index and fail
+ found = tracker.get(type, handles[2]);
+ EXPECT_EQ(nullptr, found);
+
+ // remove them all
+ handles[2] = handle;
+ for (int i = 0; i < MAX_HANDLES; i++) {
+ // look up each handle
+ found = tracker.remove(type, handles[i]);
+ EXPECT_EQ(&data[i], found);
+ }
+ }
+}
diff --git a/media/liboboe/tests/test_oboe_api.cpp b/media/liboboe/tests/test_oboe_api.cpp
new file mode 100644
index 0000000..b3fb24b
--- /dev/null
+++ b/media/liboboe/tests/test_oboe_api.cpp
@@ -0,0 +1,287 @@
+/*
+ * 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.
+ */
+
+// Unit tests for Oboe 'C' API.
+
+#include <stdlib.h>
+#include <math.h>
+
+#include <gtest/gtest.h>
+
+#include <oboe/OboeDefinitions.h>
+#include <oboe/OboeAudio.h>
+#include "OboeUtilities.h"
+
+#define DEFAULT_STATE_TIMEOUT (500 * OBOE_NANOS_PER_MILLISECOND)
+
+// Test OboeStreamBuilder
+TEST(test_oboe_api, oboe_stream_builder) {
+ const oboe_sample_rate_t requestedSampleRate1 = 48000;
+ const oboe_sample_rate_t requestedSampleRate2 = 44100;
+ const int32_t requestedSamplesPerFrame = 2;
+ const oboe_audio_format_t requestedDataFormat = OBOE_AUDIO_DATATYPE_INT16;
+
+ oboe_sample_rate_t sampleRate = 0;
+ int32_t samplesPerFrame = 0;
+ oboe_audio_format_t actualDataFormat;
+ OboeStreamBuilder oboeBuilder1;
+ OboeStreamBuilder oboeBuilder2;
+
+ oboe_result_t result = OBOE_OK;
+
+ // Use an OboeStreamBuilder to define the stream.
+ result = Oboe_createStreamBuilder(&oboeBuilder1);
+ ASSERT_EQ(OBOE_OK, result);
+
+ // Request stream properties.
+ EXPECT_EQ(OBOE_OK, OboeStreamBuilder_setSampleRate(oboeBuilder1, requestedSampleRate1));
+ EXPECT_EQ(OBOE_OK, OboeStreamBuilder_setSamplesPerFrame(oboeBuilder1, requestedSamplesPerFrame));
+ EXPECT_EQ(OBOE_OK, OboeStreamBuilder_setFormat(oboeBuilder1, requestedDataFormat));
+
+ // Check to make sure builder saved the properties.
+ EXPECT_EQ(OBOE_OK, OboeStreamBuilder_getSampleRate(oboeBuilder1, &sampleRate));
+ EXPECT_EQ(requestedSampleRate1, sampleRate);
+
+ EXPECT_EQ(OBOE_OK, OboeStreamBuilder_getSamplesPerFrame(oboeBuilder1, &samplesPerFrame));
+ EXPECT_EQ(requestedSamplesPerFrame, samplesPerFrame);
+
+ EXPECT_EQ(OBOE_OK, OboeStreamBuilder_getFormat(oboeBuilder1, &actualDataFormat));
+ EXPECT_EQ(requestedDataFormat, actualDataFormat);
+
+ result = OboeStreamBuilder_getSampleRate(0x0BADCAFE, &sampleRate); // ridiculous token
+ EXPECT_EQ(OBOE_ERROR_INVALID_HANDLE, result);
+
+ // Create a second builder and make sure they do not collide.
+ ASSERT_EQ(OBOE_OK, Oboe_createStreamBuilder(&oboeBuilder2));
+ ASSERT_NE(oboeBuilder1, oboeBuilder2);
+
+ EXPECT_EQ(OBOE_OK, OboeStreamBuilder_setSampleRate(oboeBuilder2, requestedSampleRate2));
+ EXPECT_EQ(OBOE_OK, OboeStreamBuilder_getSampleRate(oboeBuilder1, &sampleRate));
+ EXPECT_EQ(requestedSampleRate1, sampleRate);
+ EXPECT_EQ(OBOE_OK, OboeStreamBuilder_getSampleRate(oboeBuilder2, &sampleRate));
+ EXPECT_EQ(requestedSampleRate2, sampleRate);
+
+ // Delete the builder.
+ EXPECT_EQ(OBOE_OK, OboeStreamBuilder_delete(oboeBuilder1));
+
+ // Now it should no longer be valid.
+ // Note that test assumes we are using the HandleTracker. If we use plain pointers
+ // then it will be difficult to detect this kind of error.
+ result = OboeStreamBuilder_getSampleRate(oboeBuilder1, &sampleRate); // stale token
+ EXPECT_EQ(OBOE_ERROR_INVALID_HANDLE, result);
+
+ // Second builder should still be valid.
+ EXPECT_EQ(OBOE_OK, OboeStreamBuilder_getSampleRate(oboeBuilder2, &sampleRate));
+ EXPECT_EQ(requestedSampleRate2, sampleRate);
+
+ // Delete the second builder.
+ EXPECT_EQ(OBOE_OK, OboeStreamBuilder_delete(oboeBuilder2));
+
+ // Now it should no longer be valid. Assumes HandlerTracker used.
+ EXPECT_EQ(OBOE_ERROR_INVALID_HANDLE, OboeStreamBuilder_getSampleRate(oboeBuilder2, &sampleRate));
+}
+
+
+// Test creating a default stream with everything unspecified.
+TEST(test_oboe_api, oboe_stream_unspecified) {
+ OboeStreamBuilder oboeBuilder;
+ OboeStream oboeStream;
+ oboe_result_t result = OBOE_OK;
+
+ // Use an OboeStreamBuilder to define the stream.
+ result = Oboe_createStreamBuilder(&oboeBuilder);
+ ASSERT_EQ(OBOE_OK, result);
+
+ // Create an OboeStream using the Builder.
+ ASSERT_EQ(OBOE_OK, OboeStreamBuilder_openStream(oboeBuilder, &oboeStream));
+
+ // Cleanup
+ EXPECT_EQ(OBOE_OK, OboeStreamBuilder_delete(oboeBuilder));
+ EXPECT_EQ(OBOE_OK, OboeStream_close(oboeStream));
+}
+
+// Test Writing to an OboeStream
+TEST(test_oboe_api, oboe_stream) {
+ const oboe_sample_rate_t requestedSampleRate = 48000;
+ const oboe_sample_rate_t requestedSamplesPerFrame = 2;
+ const oboe_audio_format_t requestedDataFormat = OBOE_AUDIO_DATATYPE_INT16;
+ //const oboe_sharing_mode_t requestedSharingMode = OBOE_SHARING_MODE_EXCLUSIVE; // MMAP NOIRQ
+ const oboe_sharing_mode_t requestedSharingMode = OBOE_SHARING_MODE_LEGACY; // AudioTrack
+
+ oboe_sample_rate_t actualSampleRate = -1;
+ int32_t actualSamplesPerFrame = -1;
+ oboe_audio_format_t actualDataFormat = OBOE_AUDIO_FORMAT_PCM824;
+ oboe_sharing_mode_t actualSharingMode;
+ oboe_size_frames_t framesPerBurst = -1;
+
+ oboe_size_frames_t framesWritten = 0;
+ oboe_size_frames_t framesPrimed = 0;
+ oboe_position_frames_t framesTotal = 0;
+ oboe_position_frames_t oboeFramesRead = 0;
+ oboe_position_frames_t oboeFramesRead1 = 0;
+ oboe_position_frames_t oboeFramesRead2 = 0;
+ oboe_position_frames_t oboeFramesWritten = 0;
+
+ oboe_nanoseconds_t timeoutNanos;
+
+ oboe_stream_state_t state = OBOE_STREAM_STATE_UNINITIALIZED;
+ OboeStreamBuilder oboeBuilder;
+ OboeStream oboeStream;
+
+ oboe_result_t result = OBOE_OK;
+
+ // Use an OboeStreamBuilder to define the stream.
+ result = Oboe_createStreamBuilder(&oboeBuilder);
+ ASSERT_EQ(OBOE_OK, result);
+
+ // Request stream properties.
+ EXPECT_EQ(OBOE_OK, OboeStreamBuilder_setSampleRate(oboeBuilder, requestedSampleRate));
+ EXPECT_EQ(OBOE_OK, OboeStreamBuilder_setSamplesPerFrame(oboeBuilder, requestedSamplesPerFrame));
+ EXPECT_EQ(OBOE_OK, OboeStreamBuilder_setFormat(oboeBuilder, requestedDataFormat));
+ EXPECT_EQ(OBOE_OK, OboeStreamBuilder_setSharingMode(oboeBuilder, requestedSharingMode));
+
+ // Create an OboeStream using the Builder.
+ ASSERT_EQ(OBOE_OK, OboeStreamBuilder_openStream(oboeBuilder, &oboeStream));
+ EXPECT_EQ(OBOE_OK, OboeStreamBuilder_delete(oboeBuilder));
+
+ EXPECT_EQ(OBOE_OK, OboeStream_getState(oboeStream, &state));
+ EXPECT_EQ(OBOE_STREAM_STATE_OPEN, state);
+
+ // Check to see what kind of stream we actually got.
+ EXPECT_EQ(OBOE_OK, OboeStream_getSampleRate(oboeStream, &actualSampleRate));
+ EXPECT_TRUE(actualSampleRate >= 44100 && actualSampleRate <= 96000); // TODO what is range?
+
+ EXPECT_EQ(OBOE_OK, OboeStream_getSamplesPerFrame(oboeStream, &actualSamplesPerFrame));
+ EXPECT_TRUE(actualSamplesPerFrame >= 1 && actualSamplesPerFrame <= 16); // TODO what is max?
+
+ EXPECT_EQ(OBOE_OK, OboeStream_getSharingMode(oboeStream, &actualSharingMode));
+ EXPECT_TRUE(actualSharingMode == OBOE_SHARING_MODE_EXCLUSIVE
+ || actualSharingMode == OBOE_SHARING_MODE_LEGACY);
+
+ EXPECT_EQ(OBOE_OK, OboeStream_getFramesPerBurst(oboeStream, &framesPerBurst));
+ EXPECT_TRUE(framesPerBurst >= 16 && framesPerBurst <= 1024); // TODO what is min/max?
+
+ // Allocate a buffer for the audio data.
+ int16_t *data = new int16_t[framesPerBurst * actualSamplesPerFrame];
+ ASSERT_TRUE(NULL != data);
+
+ timeoutNanos = 0;
+ do {
+ framesWritten = OboeStream_write(oboeStream, data, framesPerBurst, timeoutNanos);
+ // There should be some room for priming the buffer.
+ framesTotal += framesWritten;
+ ASSERT_GE(framesWritten, 0);
+ ASSERT_LE(framesWritten, framesPerBurst);
+ } while(framesWritten > 0);
+ ASSERT_TRUE(framesTotal > 0);
+
+ // Start and wait for server to respond.
+ ASSERT_EQ(OBOE_OK, OboeStream_requestStart(oboeStream));
+ ASSERT_EQ(OBOE_OK, OboeStream_waitForStateChange(oboeStream,
+ OBOE_STREAM_STATE_STARTING,
+ &state,
+ DEFAULT_STATE_TIMEOUT));
+ EXPECT_EQ(OBOE_STREAM_STATE_STARTED, state);
+
+ // Write some data while we are running. Read counter should be advancing.
+ int loops = 1 * actualSampleRate / framesPerBurst; // 1 second
+ ASSERT_LT(2, loops); // detect absurdly high framesPerBurst
+ timeoutNanos = 10 * OBOE_NANOS_PER_SECOND * framesPerBurst / actualSampleRate; // bursts
+ framesWritten = 1;
+ ASSERT_EQ(OBOE_OK, OboeStream_getFramesRead(oboeStream, &oboeFramesRead));
+ oboeFramesRead1 = oboeFramesRead;
+ oboe_nanoseconds_t beginTime = Oboe_getNanoseconds(OBOE_CLOCK_MONOTONIC);
+ do {
+ framesWritten = OboeStream_write(oboeStream, data, framesPerBurst, timeoutNanos);
+ ASSERT_GE(framesWritten, 0);
+ ASSERT_LE(framesWritten, framesPerBurst);
+
+ framesTotal += framesWritten;
+ EXPECT_EQ(OBOE_OK, OboeStream_getFramesWritten(oboeStream, &oboeFramesWritten));
+ EXPECT_EQ(framesTotal, oboeFramesWritten);
+
+ // Try to get a more accurate measure of the sample rate.
+ if (beginTime == 0) {
+ EXPECT_EQ(OBOE_OK, OboeStream_getFramesRead(oboeStream, &oboeFramesRead));
+ if (oboeFramesRead > oboeFramesRead1) { // is read pointer advancing
+ beginTime = Oboe_getNanoseconds(OBOE_CLOCK_MONOTONIC);
+ oboeFramesRead1 = oboeFramesRead;
+ }
+ }
+ } while (framesWritten > 0 && loops-- > 0);
+
+ EXPECT_EQ(OBOE_OK, OboeStream_getFramesRead(oboeStream, &oboeFramesRead2));
+ oboe_nanoseconds_t endTime = Oboe_getNanoseconds(OBOE_CLOCK_MONOTONIC);
+ ASSERT_GT(oboeFramesRead2, 0);
+ ASSERT_GT(oboeFramesRead2, oboeFramesRead1);
+ ASSERT_LE(oboeFramesRead2, oboeFramesWritten);
+
+ // TODO why is legacy so inaccurate?
+ const double rateTolerance = 200.0; // arbitrary tolerance for sample rate
+ if (requestedSharingMode != OBOE_SHARING_MODE_LEGACY) {
+ // Calculate approximate sample rate and compare with stream rate.
+ double seconds = (endTime - beginTime) / (double) OBOE_NANOS_PER_SECOND;
+ double measuredRate = (oboeFramesRead2 - oboeFramesRead1) / seconds;
+ ASSERT_NEAR(actualSampleRate, measuredRate, rateTolerance);
+ }
+
+ // Request async pause and wait for server to say that it has completed the pause.
+ ASSERT_EQ(OBOE_OK, OboeStream_requestPause(oboeStream));
+ EXPECT_EQ(OBOE_OK, OboeStream_waitForStateChange(oboeStream,
+ OBOE_STREAM_STATE_PAUSING,
+ &state,
+ DEFAULT_STATE_TIMEOUT));
+ EXPECT_EQ(OBOE_STREAM_STATE_PAUSED, state);
+
+ // Make sure the read counter is not advancing when we are paused.
+ ASSERT_EQ(OBOE_OK, OboeStream_getFramesRead(oboeStream, &oboeFramesRead));
+ ASSERT_GE(oboeFramesRead, oboeFramesRead2); // monotonic increase
+
+ // Use this to sleep by waiting for something that won't happen.
+ OboeStream_waitForStateChange(oboeStream, OBOE_STREAM_STATE_PAUSED, &state, timeoutNanos);
+ ASSERT_EQ(OBOE_OK, OboeStream_getFramesRead(oboeStream, &oboeFramesRead2));
+ EXPECT_EQ(oboeFramesRead, oboeFramesRead2);
+
+ // Fill up the buffer.
+ timeoutNanos = 0;
+ loops = 100;
+ do {
+ framesWritten = OboeStream_write(oboeStream, data, framesPerBurst, timeoutNanos);
+ framesTotal += framesWritten;
+ } while (framesWritten > 0 && loops-- > 0);
+ EXPECT_EQ(0, framesWritten);
+
+ // Flush and wait for server to respond.
+ ASSERT_EQ(OBOE_OK, OboeStream_requestFlush(oboeStream));
+ EXPECT_EQ(OBOE_OK, OboeStream_waitForStateChange(oboeStream,
+ OBOE_STREAM_STATE_FLUSHING,
+ &state,
+ DEFAULT_STATE_TIMEOUT));
+ EXPECT_EQ(OBOE_STREAM_STATE_FLUSHED, state);
+
+ // After a flush, the read counter should be caught up with the write counter.
+ EXPECT_EQ(OBOE_OK, OboeStream_getFramesWritten(oboeStream, &oboeFramesWritten));
+ EXPECT_EQ(framesTotal, oboeFramesWritten);
+ EXPECT_EQ(OBOE_OK, OboeStream_getFramesRead(oboeStream, &oboeFramesRead));
+ EXPECT_EQ(oboeFramesRead, oboeFramesWritten);
+
+ // The buffer should be empty after a flush so we should be able to write.
+ framesWritten = OboeStream_write(oboeStream, data, framesPerBurst, timeoutNanos);
+ // There should be some room for priming the buffer.
+ ASSERT_TRUE(framesWritten > 0 && framesWritten <= framesPerBurst);
+
+ EXPECT_EQ(OBOE_OK, OboeStream_close(oboeStream));
+}