Initial commit
diff --git a/AUTHORS b/AUTHORS
new file mode 100755
index 0000000..6ca313d
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,9 @@
+# This is the official list of authors for copyright purposes.
+# This file is distinct from the CONTRIBUTORS files.
+# See the latter for an explanation.
+
+# Names should be added to this file as:
+# Name or Organization <email address>
+# The email address is not required for organizations.
+
+Google Inc.
diff --git a/CONTRIBUTING b/CONTRIBUTING
new file mode 100755
index 0000000..88f06d0
--- /dev/null
+++ b/CONTRIBUTING
@@ -0,0 +1,25 @@
+Want to contribute? Great! First, read this page (including the small print at the end).
+
+### Before you contribute
+Before we can use your code, you must sign the
+[Google Individual Contributor License
+Agreement](https://developers.google.com/open-source/cla/individual?csw=1)
+(CLA), which you can do online. The CLA is necessary mainly because you own the
+copyright to your changes, even after your contribution becomes part of our
+codebase, so we need your permission to use and distribute your code. We also
+need to be sure of various other things—for instance that you'll tell us if you
+know that your code infringes on other people's patents. You don't have to sign
+the CLA until after you've submitted your code for review and a member has
+approved it, but you must do it before we can put your code into our codebase.
+Before you start working on a larger contribution, you should get in touch with
+us first through the issue tracker with your idea so that we can help out and
+possibly guide you. Coordinating up front makes it much easier to avoid
+frustration later on.
+
+### Code reviews
+All submissions, including submissions by project members, require review. We
+use Github pull requests for this purpose.
+
+### The small print
+Contributions made by corporations are covered by a different agreement than
+the one above, the Software Grant and Corporate Contributor License Agreement.
diff --git a/CONTRIBUTORS b/CONTRIBUTORS
new file mode 100755
index 0000000..2170a7a
--- /dev/null
+++ b/CONTRIBUTORS
@@ -0,0 +1,12 @@
+# People who have agreed to one of the CLAs and can contribute patches.
+# The AUTHORS file lists the copyright holders; this file
+# lists people.  For example, Google employees are listed here
+# but not in AUTHORS, because Google holds the copyright.
+#
+# https://developers.google.com/open-source/cla/individual
+# https://developers.google.com/open-source/cla/corporate
+#
+# Names should be added to this file as:
+#     Name <email address>
+
+Phil Burk <philburk@google.com>
diff --git a/LICENSE b/LICENSE
new file mode 100755
index 0000000..d645695
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   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/README b/README
new file mode 100755
index 0000000..9150edf
--- /dev/null
+++ b/README
@@ -0,0 +1 @@
+Please see the README.md file for more information.
diff --git a/README.md b/README.md
new file mode 100755
index 0000000..21f7366
--- /dev/null
+++ b/README.md
@@ -0,0 +1,23 @@
+# oboe
+
+## Introduction
+
+Oboe is a C++ wrapper for the AAudio and OpenSL ES audio APIs on Android.
+
+A program written using Oboe can run on multiple versions of Android.
+On O or later versions, Oboe can use Audio or OpenSL ES.
+On N or earlier versions, Oboe will only use OpenSL ES.
+
+This is not an official Google product.
+
+## Apps
+
+Hello Oboe - simple tone generator
+
+Native MIDI Synth - A MIDI synthesizer that renders in C++ and calls Oboe.
+It uses the Android MIDI API in Java.
+
+## How to Use
+
+Include this as a sub-module in your Git project.
+
diff --git a/include/oboe/Oboe.h b/include/oboe/Oboe.h
new file mode 100644
index 0000000..8e7809d
--- /dev/null
+++ b/include/oboe/Oboe.h
@@ -0,0 +1,25 @@
+/*
+ * 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 OBOE_OBOE_H
+#define OBOE_OBOE_H
+
+#include "oboe/OboeStreamBase.h"
+#include "oboe/OboeStream.h"
+#include "oboe/OboeStreamBuilder.h"
+#include "oboe/OboeDefinitions.h"
+
+#endif //OBOE_OBOE_H
diff --git a/include/oboe/OboeDefinitions.h b/include/oboe/OboeDefinitions.h
new file mode 100644
index 0000000..6c7a8f1
--- /dev/null
+++ b/include/oboe/OboeDefinitions.h
@@ -0,0 +1,130 @@
+/*
+ * 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 OBOE_OBOE_DEFINITIONS_H
+#define OBOE_OBOE_DEFINITIONS_H
+
+#include <stdint.h>
+#include <aaudio/AAudio.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef aaudio_result_t  oboe_result_t;
+
+/**
+ * This is used to represent a value that has not been specified.
+ * For example, an application could use OBOE_UNSPECIFIED to indicate
+ * that it did not not care what the specific value of a parameter was
+ * and would accept whatever it was given.
+ */
+#define OBOE_UNSPECIFIED           0
+#define OBOE_NANOS_PER_MICROSECOND ((int64_t)1000)
+#define OBOE_NANOS_PER_MILLISECOND (OBOE_NANOS_PER_MICROSECOND * 1000)
+#define OBOE_MILLIS_PER_SECOND     1000
+#define OBOE_NANOS_PER_SECOND      (OBOE_NANOS_PER_MILLISECOND * OBOE_MILLIS_PER_SECOND)
+
+#define oboe_direction_t aaudio_direction_t
+#define OBOE_DIRECTION_OUTPUT AAUDIO_DIRECTION_OUTPUT
+#define OBOE_DIRECTION_INPUT AAUDIO_DIRECTION_INPUT
+
+#define oboe_audio_format_t aaudio_format_t
+#define OBOE_AUDIO_FORMAT_INVALID     AAUDIO_FORMAT_INVALID
+#define OBOE_AUDIO_FORMAT_UNSPECIFIED AAUDIO_FORMAT_UNSPECIFIED
+#define OBOE_AUDIO_FORMAT_PCM_I16     AAUDIO_FORMAT_PCM_I16
+#define OBOE_AUDIO_FORMAT_PCM_FLOAT   AAUDIO_FORMAT_PCM_FLOAT
+
+#define oboe_data_callback_result_t   aaudio_data_callback_result_t
+#define OBOE_CALLBACK_RESULT_CONTINUE AAUDIO_CALLBACK_RESULT_CONTINUE
+#define OBOE_CALLBACK_RESULT_STOP     AAUDIO_CALLBACK_RESULT_STOP
+
+enum {
+    OBOE_OK,
+    OBOE_ERROR_BASE = AAUDIO_ERROR_BASE,
+    OBOE_ERROR_DISCONNECTED = AAUDIO_ERROR_DISCONNECTED,
+    OBOE_ERROR_ILLEGAL_ARGUMENT = AAUDIO_ERROR_ILLEGAL_ARGUMENT,
+    // Reserved
+    OBOE_ERROR_INTERNAL = AAUDIO_ERROR_INTERNAL,
+    OBOE_ERROR_INVALID_STATE = AAUDIO_ERROR_INVALID_STATE,
+    // Reserved
+    // Reserved
+    OBOE_ERROR_INVALID_HANDLE = AAUDIO_ERROR_INVALID_HANDLE,
+    // Reserved
+    OBOE_ERROR_UNIMPLEMENTED = AAUDIO_ERROR_UNIMPLEMENTED,
+    OBOE_ERROR_UNAVAILABLE = AAUDIO_ERROR_UNAVAILABLE,
+    OBOE_ERROR_NO_FREE_HANDLES = AAUDIO_ERROR_NO_FREE_HANDLES,
+    OBOE_ERROR_NO_MEMORY = AAUDIO_ERROR_NO_MEMORY,
+    OBOE_ERROR_NULL = AAUDIO_ERROR_NULL,
+    OBOE_ERROR_TIMEOUT = AAUDIO_ERROR_TIMEOUT,
+    OBOE_ERROR_WOULD_BLOCK = AAUDIO_ERROR_WOULD_BLOCK,
+    OBOE_ERROR_INVALID_FORMAT = AAUDIO_ERROR_INVALID_FORMAT,
+    OBOE_ERROR_OUT_OF_RANGE = AAUDIO_ERROR_OUT_OF_RANGE,
+    OBOE_ERROR_NO_SERVICE = AAUDIO_ERROR_NO_SERVICE,
+    OBOE_ERROR_INVALID_RATE = AAUDIO_ERROR_INVALID_RATE
+};
+
+#define oboe_stream_state_t aaudio_stream_state_t
+
+#define OBOE_STREAM_STATE_UNINITIALIZED AAUDIO_STREAM_STATE_UNINITIALIZED
+#define OBOE_STREAM_STATE_OPEN      AAUDIO_STREAM_STATE_OPEN
+#define OBOE_STREAM_STATE_STARTING  AAUDIO_STREAM_STATE_STARTING
+#define OBOE_STREAM_STATE_STARTED   AAUDIO_STREAM_STATE_STARTED
+#define OBOE_STREAM_STATE_PAUSING   AAUDIO_STREAM_STATE_PAUSING
+#define OBOE_STREAM_STATE_PAUSED    AAUDIO_STREAM_STATE_PAUSED
+#define OBOE_STREAM_STATE_FLUSHING  AAUDIO_STREAM_STATE_FLUSHING
+#define OBOE_STREAM_STATE_FLUSHED   AAUDIO_STREAM_STATE_FLUSHED
+#define OBOE_STREAM_STATE_STOPPING  AAUDIO_STREAM_STATE_STOPPING
+#define OBOE_STREAM_STATE_STOPPED   AAUDIO_STREAM_STATE_STOPPED
+#define OBOE_STREAM_STATE_CLOSING   AAUDIO_STREAM_STATE_CLOSING
+#define OBOE_STREAM_STATE_CLOSED    AAUDIO_STREAM_STATE_CLOSED
+
+#define oboe_sharing_mode_t aaudio_sharing_mode_t
+
+    /**
+     * This will be the only stream using a particular source or sink.
+     * This mode will provide the lowest possible latency.
+     * You should close EXCLUSIVE streams immediately when you are not using them.
+     */
+#define OBOE_SHARING_MODE_EXCLUSIVE   AAUDIO_SHARING_MODE_EXCLUSIVE
+    /**
+     * Multiple applications will be mixed by the Oboe Server.
+     * This will have higher latency than the EXCLUSIVE mode.
+     */
+#define OBOE_SHARING_MODE_SHARED      AAUDIO_SHARING_MODE_SHARED
+
+    /**
+     * No particular performance needs. Default.
+     */
+#define OBOE_PERFORMANCE_MODE_NONE AAUDIO_PERFORMANCE_MODE_NONE
+
+    /**
+     * Extending battery life is most important.
+     */
+#define OBOE_PERFORMANCE_MODE_POWER_SAVING AAUDIO_PERFORMANCE_MODE_POWER_SAVING
+
+    /**
+     * Reducing latency is most important.
+     */
+#define OBOE_PERFORMANCE_MODE_LOW_LATENCY  AAUDIO_PERFORMANCE_MODE_LOW_LATENCY
+
+#define oboe_performance_mode_t  aaudio_performance_mode_t
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // OBOE_OBOE_DEFINITIONS_H
diff --git a/include/oboe/OboeStream.h b/include/oboe/OboeStream.h
new file mode 100644
index 0000000..eac72b3
--- /dev/null
+++ b/include/oboe/OboeStream.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_OBOE_STREAM_H_
+#define OBOE_OBOE_STREAM_H_
+
+#include <stdint.h>
+#include <time.h>
+#include "oboe/OboeDefinitions.h"
+#include "oboe/OboeStreamCallback.h"
+#include "oboe/OboeStreamBase.h"
+
+/* ========== UNDER CONSTRUCTION - THIS API WILL CHANGE. =========== */
+
+class OboeStreamBuilder;
+
+#define DEFAULT_TIMEOUT_NANOS    (2000 * OBOE_NANOS_PER_MILLISECOND)
+
+/**
+ * Base class for Oboe C++ audio stream.
+ */
+class OboeStream : public OboeStreamBase {
+public:
+
+    OboeStream() {}
+    explicit OboeStream(const OboeStreamBuilder &builder);
+
+    virtual ~OboeStream() = default;
+
+    virtual oboe_result_t open();
+
+    /**
+     * Close the stream and deallocate any resources from the open() call.
+     */
+    virtual oboe_result_t close() = 0;
+
+    /*
+     * These are synchronous and will block until the operation is complete.
+     */
+    virtual oboe_result_t start(int64_t timeoutNanoseconds = DEFAULT_TIMEOUT_NANOS);
+    virtual oboe_result_t pause(int64_t timeoutNanoseconds = DEFAULT_TIMEOUT_NANOS);
+    virtual oboe_result_t flush(int64_t timeoutNanoseconds = DEFAULT_TIMEOUT_NANOS);
+    virtual oboe_result_t stop(int64_t timeoutNanoseconds = DEFAULT_TIMEOUT_NANOS);
+
+    /* Asynchronous requests.
+     * Use waitForStateChange() if you need 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;
+
+    /**
+     * Query the current state, eg. OBOE_STREAM_STATE_PAUSING
+     *
+     * @return state or a negative error.
+     */
+    virtual oboe_stream_state_t getState() = 0;
+
+    /**
+     * Wait until the current state no longer matches the input state.
+     * The current state is passed to avoid race conditions caused by the state
+     * changing between calls.
+     *
+     * <pre><code>
+     * oboe_stream_state_t currentState = stream->getState(stream);
+     * while (currentState >= 0 && currentState != OBOE_STREAM_STATE_PAUSED) {
+     *     currentState = stream->waitForStateChange(
+     *                                   stream, currentState, 500);
+     * }
+     * </code></pre>
+     *
+     * @param stream A handle provided by OboeStreamBuilder_openStream()
+     * @param currentState The state we want to avoid.
+     * @param nextState Pointer to a variable that will be set to the new state.
+     * @param timeoutNanoseconds The maximum time to wait in nanoseconds.
+     * @return OBOE_OK or a negative error.
+     */
+    virtual oboe_result_t waitForStateChange(oboe_stream_state_t currentState,
+                                          oboe_stream_state_t *nextState,
+                                          int64_t timeoutNanoseconds) = 0;
+
+
+
+    /**
+    * This can be used to adjust the latency of the buffer by changing
+    * the threshold where blocking will occur.
+    * By combining this with getXRunCount(), the latency can be tuned
+    * at run-time for each device.
+    *
+    * This cannot be set higher than getBufferCapacity().
+    *
+    * @param requestedFrames requested number of frames that can be filled without blocking
+    * @return OBOE_OK or a negative error
+    */
+    virtual oboe_result_t setBufferSizeInFrames(int32_t requestedFrames) {
+        return OBOE_ERROR_UNIMPLEMENTED;
+    }
+
+    /**
+     * Query the maximum number of frames that can be filled without blocking.
+     *
+     * @return buffer size or a negative error.
+     */
+    virtual int32_t getBufferSizeInFrames() const {
+        return OBOE_ERROR_UNIMPLEMENTED;
+    }
+
+    /**
+     * An XRun is an Underrun or an Overrun.
+     * During playing, an underrun will occur if the stream is not written in time
+     * and the system runs out of valid data.
+     * During recording, an overrun will occur if the stream is not read in time
+     * and there is no place to put the incoming data so it is discarded.
+     *
+     * An underrun or overrun can cause an audible "pop" or "glitch".
+     *
+     * @return the count or a negative error.
+     */
+    virtual int32_t getXRunCount() {
+        return OBOE_ERROR_UNIMPLEMENTED;
+    }
+
+    /**
+     * Query the number of frames that are read or written by the endpoint at one time.
+     *
+     * @return burst size or a negative error.
+     */
+    virtual int32_t getFramesPerBurst() = 0;
+
+    // ============== Queries ===========================
+    bool isPlaying();
+/*
+    int32_t getSampleRate() const { return mSampleRate; }
+
+    virtual oboe_result_t getChannelCount() const { return mChannelCount; }
+
+    int32_t getDeviceId() const { return mDeviceId; }
+    oboe_sharing_mode_t getSharingMode() const { return mSharingMode; }
+
+    oboe_direction_t getDirection() const { return mDirection; }
+*/
+    OboeStreamCallback *getCallback() const { return mStreamCallback; }
+
+
+    int32_t getBytesPerFrame() const { return mChannelCount * getBytesPerSample(); }
+
+    int32_t getBytesPerSample() const;
+
+    /**
+     * This monotonic counter will never get reset.
+     * @return the number of frames written so far
+     */
+    virtual int64_t getFramesWritten() { return mFramesWritten; }
+
+    virtual int64_t getFramesRead() { return OBOE_ERROR_UNIMPLEMENTED; }
+
+    virtual oboe_result_t getTimestamp(clockid_t clockId,
+                                       int64_t *framePosition,
+                                       int64_t *timeNanoseconds) {
+        return OBOE_ERROR_UNIMPLEMENTED;
+    }
+
+    // ============== I/O ===========================
+    /**
+     * A high level write that will wait until the write is complete or it runs out of time.
+     * If timeoutNanoseconds is zero then this call will not wait.
+     *
+     * @param stream A stream created using OboeStream_Open().
+     * @param buffer The address of the first sample.
+     * @param numFrames Number of frames to write. Only complete frames will be written.
+     * @param timeoutNanoseconds Maximum number of nanoseconds to wait for completion.
+     * @return The number of frames actually written or a negative error.
+     */
+    virtual oboe_result_t write(const void *buffer,
+                             int32_t numFrames,
+                             int64_t timeoutNanoseconds) {
+        return OBOE_ERROR_UNIMPLEMENTED;
+    }
+
+    virtual oboe_result_t read(void *buffer,
+                            int32_t numFrames,
+                            int64_t timeoutNanoseconds) {
+        return OBOE_ERROR_UNIMPLEMENTED;
+    }
+
+    virtual void tuneLatency();
+
+    /**
+     * Try to minimize latency. This may cause a glitch.
+     */
+    void triggerLatencyRetuning();
+
+protected:
+
+    virtual int64_t incrementFramesWritten(int32_t frames) {
+        return mFramesWritten += 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,
+                                              int64_t timeoutNanoseconds);
+
+    oboe_result_t fireCallback(void *audioData, int numFrames);
+
+    virtual void setNativeFormat(oboe_audio_format_t format) {
+        mNativeFormat = format;
+    }
+
+    // TODO make private
+    // These do not change after open.
+    oboe_audio_format_t  mNativeFormat = OBOE_AUDIO_FORMAT_INVALID;
+
+private:
+    int64_t              mFramesWritten = 0;
+    int                  mPreviousScheduler = -1;
+    int32_t              mPreviousXRuns = 0;
+    int32_t              mLatencyTriggerRequests = 0;
+    int32_t              mLatencyTriggerResponses = 0;
+};
+
+
+#endif /* OBOE_OBOE_STREAM_H_ */
diff --git a/include/oboe/OboeStreamBase.h b/include/oboe/OboeStreamBase.h
new file mode 100644
index 0000000..00fb16c
--- /dev/null
+++ b/include/oboe/OboeStreamBase.h
@@ -0,0 +1,74 @@
+/*
+ * 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_OBOE_STREAM_BASE_H_
+#define OBOE_OBOE_STREAM_BASE_H_
+
+#include "oboe/OboeStreamCallback.h"
+#include "oboe/OboeDefinitions.h"
+
+/**
+ * Base class for Oboe streams and builders.
+ */
+class OboeStreamBase {
+public:
+
+    OboeStreamBase() {}
+
+    virtual ~OboeStreamBase() = default;
+
+    // This class only contains primitives so we can use default constructor and copy methods.
+    OboeStreamBase(const OboeStreamBase&) = default;
+
+    OboeStreamBase& operator=(const OboeStreamBase&) = default;
+
+    int getChannelCount() const { return mChannelCount; }
+
+    oboe_direction_t getDirection() const { return mDirection; }
+
+    int32_t getSampleRate() const { return mSampleRate; }
+
+    int getFramesPerCallback() const { return mFramesPerCallback; }
+
+    oboe_audio_format_t getFormat() const { return mFormat; }
+
+    virtual int32_t getBufferCapacityInFrames() const { return mBufferCapacityInFrames; }
+
+    oboe_sharing_mode_t getSharingMode() const { return mSharingMode; }
+
+    oboe_performance_mode_t getPerformanceMode() const { return mPerformanceMode; }
+
+    int32_t getDeviceId() const { return mDeviceId; }
+
+    OboeStreamCallback *getCallback() const {
+        return mStreamCallback;
+    }
+
+protected:
+    OboeStreamCallback     *mStreamCallback = NULL;
+    int32_t                 mFramesPerCallback = OBOE_UNSPECIFIED;
+    int32_t                 mChannelCount = OBOE_UNSPECIFIED;
+    int32_t                 mSampleRate = OBOE_UNSPECIFIED;
+    int32_t                 mDeviceId = OBOE_UNSPECIFIED;
+    int32_t                 mBufferCapacityInFrames = OBOE_UNSPECIFIED;
+
+    oboe_sharing_mode_t     mSharingMode = OBOE_SHARING_MODE_SHARED;
+    oboe_audio_format_t     mFormat = OBOE_AUDIO_FORMAT_PCM_FLOAT;
+    oboe_direction_t        mDirection = OBOE_DIRECTION_OUTPUT;
+    oboe_performance_mode_t mPerformanceMode = OBOE_PERFORMANCE_MODE_NONE;
+};
+
+#endif /* OBOE_OBOE_STREAM_BASE_H_ */
diff --git a/include/oboe/OboeStreamBuilder.h b/include/oboe/OboeStreamBuilder.h
new file mode 100644
index 0000000..e319e4b
--- /dev/null
+++ b/include/oboe/OboeStreamBuilder.h
@@ -0,0 +1,201 @@
+/*
+ * 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_OBOE_STREAM_BUILDER_H_
+#define OBOE_OBOE_STREAM_BUILDER_H_
+
+#include "OboeStreamBase.h"
+#include "OboeStream.h"
+#include "oboe/OboeDefinitions.h"
+
+/**
+ * Factory class for an AudioStream.
+ */
+class OboeStreamBuilder : public OboeStreamBase {
+public:
+
+    OboeStreamBuilder() : OboeStreamBase() {}
+
+    enum audio_api_index_t {
+        /**
+         * Try to use AAudio. If not available then use OpenSL ES.
+         */
+        API_UNSPECIFIED,
+
+        /**
+         * Use OpenSL ES.
+         */
+        API_OPENSL_ES,
+
+        /**
+         * Try to use AAudio. Fail if unavailable.
+         */
+        API_AAUDIO
+    };
+
+
+    /**
+     * Request a specific number of channels.
+     *
+     * Default is OBOE_UNSPECIFIED. If the value is unspecified then
+     * the application should query for the actual value after the stream is opened.
+     */
+    OboeStreamBuilder *setChannelCount(int channelCount) {
+        mChannelCount = channelCount;
+        return this;
+    }
+
+
+    /**
+     * Request the direction for a stream. The default is OBOE_DIRECTION_OUTPUT.
+     *
+     * @param direction OBOE_DIRECTION_OUTPUT or OBOE_DIRECTION_INPUT
+     */
+    OboeStreamBuilder *setDirection(oboe_direction_t direction) {
+        mDirection = direction;
+        return this;
+    }
+
+    /**
+     * Request a specific sample rate in Hz.
+     *
+     * Default is OBOE_UNSPECIFIED. If the value is unspecified then
+     * the application should query for the actual value after the stream is opened.
+     *
+     * Technically, this should be called the "frame rate" or "frames per second",
+     * because it refers to the number of complete frames transferred per second.
+     * But it is traditionally called "sample rate". Se we use that term.
+     *
+     */
+    OboeStreamBuilder *setSampleRate(int32_t sampleRate) {
+        mSampleRate = sampleRate;
+        return this;
+    }
+
+    /**
+     * Request a specific number of frames for the data callback.
+     *
+     * Default is OBOE_UNSPECIFIED. If the value is unspecified then
+     * the actual number may vary from callback to callback.
+     *
+     * If an application can handle a varying number of frames then we recommend
+     * leaving this unspecified. This allow the underlying API to optimize
+     * the callbacks. But if your application is, for example, doing FFTs or other block
+     * oriented operations, then call this function to get the sizes you need.
+     *
+     * @param framesPerCallback
+     * @return
+     */
+    OboeStreamBuilder *setFramesPerCallback(int framesPerCallback) {
+        mFramesPerCallback = framesPerCallback;
+        return this;
+    }
+
+    /**
+     * Request a sample data format, for example OBOE_FORMAT_PCM_FLOAT.
+     *
+     * Default is OBOE_UNSPECIFIED. If the value is unspecified then
+     * the application should query for the actual value after the stream is opened.
+     */
+    OboeStreamBuilder *setFormat(oboe_audio_format_t format) {
+        mFormat = format;
+        return this;
+    }
+
+    /**
+     * Set the requested maximum buffer capacity in frames.
+     * The final stream capacity may differ, but will probably be at least this big.
+     *
+     * Default is OBOE_UNSPECIFIED.
+     *
+     * @param frames the desired buffer capacity in frames or OBOE_UNSPECIFIED
+     */
+    OboeStreamBuilder *setBufferCapacityInFrames(int32_t bufferCapacityInFrames) {
+        mBufferCapacityInFrames = bufferCapacityInFrames;
+        return this;
+    }
+
+    audio_api_index_t getApiIndex() const { return mAudioApi; }
+
+    OboeStreamBuilder *setApiIndex(audio_api_index_t apiIndex) {
+        mAudioApi = apiIndex;
+        return this;
+    }
+
+    /**
+     * Request a mode for sharing the device.
+     * The requested sharing mode may not be available.
+     * So the application should query for the actual mode after the stream is opened.
+     *
+     * @param sharingMode OBOE_SHARING_MODE_LEGACY or OBOE_SHARING_MODE_EXCLUSIVE
+     */
+    OboeStreamBuilder *setSharingMode(oboe_sharing_mode_t sharingMode) {
+        mSharingMode = sharingMode;
+        return this;
+    }
+
+    /**
+     * Request a performance level for the stream.
+     * This will determine the latency, the power consumption, and the level of
+     * protection from glitches.
+     *
+     * @param performanceMode for example, OBOE_PERFORMANCE_MODE_LOW_LATENCY
+     */
+    OboeStreamBuilder *setPerformanceMode(oboe_performance_mode_t performanceMode) {
+        mPerformanceMode = performanceMode;
+        return this;
+    }
+
+    /**
+     * Request an audio device identified device using an ID.
+     * On Android, for example, the ID could be obtained from the Java AudioManager.
+     *
+     * By default, the primary device will be used.
+     *
+     * @param deviceId device identifier or OBOE_DEVICE_UNSPECIFIED
+     */
+    OboeStreamBuilder *setDeviceId(int32_t deviceId) {
+        mDeviceId = deviceId;
+        return this;
+    }
+
+    OboeStreamBuilder *setCallback(OboeStreamCallback *streamCallback) {
+        mStreamCallback = streamCallback;
+        return this;
+    }
+
+    /**
+     * Create and open a stream object based on the current settings.
+     *
+     * @param stream pointer to a variable to receive the stream address
+     * @return OBOE_OK if successful or a negative error code
+     */
+    oboe_result_t openStream(OboeStream **stream);
+
+
+private:
+
+    /**
+     * Create an AudioStream object. The AudioStream must be opened before use.
+     *
+     * @return pointer to an AudioStream object.
+     */
+    OboeStream *build();
+
+    audio_api_index_t       mAudioApi = API_UNSPECIFIED;
+};
+
+#endif /* OBOE_OBOE_STREAM_BUILDER_H_ */
diff --git a/include/oboe/OboeStreamCallback.h b/include/oboe/OboeStreamCallback.h
new file mode 100644
index 0000000..56f7d31
--- /dev/null
+++ b/include/oboe/OboeStreamCallback.h
@@ -0,0 +1,52 @@
+/*
+ * 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 OBOE_OBOE_STREAM_CALLBACK_H
+#define OBOE_OBOE_STREAM_CALLBACK_H
+
+#include "oboe/OboeDefinitions.h"
+
+class OboeStream;
+
+class OboeStreamCallback {
+public:
+    virtual ~OboeStreamCallback() = default;
+    /**
+     * A buffer is ready for processing.
+     *
+     * @param input buffer containing recorded audio, may be NULL
+     * @param output fill this with audio data to be played, may be NULL
+     * @param number of frames of input or output
+     * @return OBOE_CALLBACK_RESULT_CONTINUE or OBOE_CALLBACK_RESULT_STOP
+     */
+    virtual oboe_data_callback_result_t onAudioReady(
+            OboeStream *audioStream,
+            void *audioData,
+            int32_t numFrames) = 0;
+
+    virtual void onError(OboeStream *audioStream, oboe_result_t error) {}
+
+    /**
+     * The callback thread is exiting.
+     *
+     * @param reason Why it is exiting. OBOE_OK if requested.
+     *               Or maybe OBOE_ERROR_TIMEOUT or OBOE_ERROR_DISCONNECTED.
+     */
+    virtual void onExit(oboe_result_t reason) {}
+};
+
+
+#endif //OBOE_OBOE_STREAM_CALLBACK_H
diff --git a/include/oboe/dummy.cpp b/include/oboe/dummy.cpp
new file mode 100644
index 0000000..ebcdef6
--- /dev/null
+++ b/include/oboe/dummy.cpp
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+// This file is used to trick Android Studio into showing the include folder
+// in the file browser.
+int  g_Oboe_dummyUnusedVariable = 0;
diff --git a/src/aaudio/AAudioLoader.cpp b/src/aaudio/AAudioLoader.cpp
new file mode 100644
index 0000000..f4b1416
--- /dev/null
+++ b/src/aaudio/AAudioLoader.cpp
@@ -0,0 +1,190 @@
+/*
+ * 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.
+ */
+
+#include <dlfcn.h>
+#include "common/OboeDebug.h"
+#include "AAudioLoader.h"
+
+#define LIB_AAUDIO_NAME "libaaudio.so"
+
+AAudioLoader *AAudioLoader::mInstance = nullptr;
+
+AAudioLoader::~AAudioLoader() {
+    close(); // TODO dangerous from a destructor, require caller to close()
+}
+
+AAudioLoader *AAudioLoader::getInstance() {
+    if (mInstance == nullptr) {
+        mInstance = new AAudioLoader();
+    }
+    return mInstance;
+}
+
+int AAudioLoader::open() {
+    if (mLibHandle != nullptr) {
+        return 0;
+    }
+    mLibHandle = dlopen(LIB_AAUDIO_NAME, 0);
+    if (mLibHandle == nullptr) {
+        LOGE("AAudioLoader::open() could not find " LIB_AAUDIO_NAME);
+        return -1; // TODO review return code
+    } else {
+        LOGD("AAudioLoader():  dlopen(%s) returned %p", LIB_AAUDIO_NAME, mLibHandle);
+    }
+
+    // Load all the function pointers.
+    createStreamBuilder = (aaudio_result_t (*)(AAudioStreamBuilder **builder))
+            dlsym(mLibHandle, "AAudio_createStreamBuilder");
+
+    builder_openStream = (aaudio_result_t (*)(AAudioStreamBuilder *builder,
+                                              AAudioStream **stream))
+            dlsym(mLibHandle, "AAudioStreamBuilder_openStream");
+
+    builder_setChannelCount    = load_V_PBI("AAudioStreamBuilder_setChannelCount");
+    if (builder_setChannelCount == nullptr) {
+        // Use old alias if needed.
+        builder_setChannelCount    = load_V_PBI("AAudioStreamBuilder_setSamplesPerFrame");
+    }
+
+    builder_setBufferCapacityInFrames = load_V_PBI("AAudioStreamBuilder_setBufferCapacityInFrames");
+    builder_setDeviceId        = load_V_PBI("AAudioStreamBuilder_setDeviceId");
+    builder_setDirection       = load_V_PBI("AAudioStreamBuilder_setDirection");
+    builder_setFormat          = load_V_PBI("AAudioStreamBuilder_setFormat");
+    builder_setFramesPerDataCallback = load_V_PBI("AAudioStreamBuilder_setFramesPerDataCallback");
+    builder_setSharingMode     = load_V_PBI("AAudioStreamBuilder_setSharingMode");
+    builder_setPerformanceMode     = load_V_PBI("AAudioStreamBuilder_setPerformanceMode");
+    builder_setSampleRate      = load_V_PBI("AAudioStreamBuilder_setSampleRate");
+
+    builder_delete             = load_I_PB("AAudioStreamBuilder_delete");
+
+    stream_getFormat = (aaudio_format_t (*)(AAudioStream *stream))
+            dlsym(mLibHandle, "AAudioStream_getFormat");
+
+    builder_setDataCallback = (void (*)(AAudioStreamBuilder *builder,
+                                        AAudioStream_dataCallback callback,
+                                        void *userData))
+            dlsym(mLibHandle, "AAudioStreamBuilder_setDataCallback");
+
+    builder_setErrorCallback = (void (*)(AAudioStreamBuilder *builder,
+                                        AAudioStream_errorCallback callback,
+                                        void *userData))
+            dlsym(mLibHandle, "AAudioStreamBuilder_setErrorCallback");
+
+    stream_write = (aaudio_result_t (*)(AAudioStream *stream,
+                                        const void *buffer,
+                                        int32_t numFrames,
+                                        int64_t timeoutNanoseconds))
+            dlsym(mLibHandle, "AAudioStream_write");
+
+
+    stream_waitForStateChange = (aaudio_result_t (*)(AAudioStream *stream,
+                                                 aaudio_stream_state_t inputState,
+                                                 aaudio_stream_state_t *nextState,
+                                                 int64_t timeoutNanoseconds))
+            dlsym(mLibHandle, "AAudioStream_waitForStateChange");
+
+
+    stream_getTimestamp = (aaudio_result_t (*)(AAudioStream *stream,
+                                           clockid_t clockid,
+                                           int64_t *framePosition,
+                                           int64_t *timeNanoseconds))
+            dlsym(mLibHandle, "AAudioStream_getTimestamp");
+
+    stream_getChannelCount    = load_I_PS("AAudioStream_getChannelCount");
+    if (stream_getChannelCount == nullptr) {
+        // Use old alias if needed.
+        stream_getChannelCount    = load_I_PS("AAudioStream_getSamplesPerFrame");
+    }
+
+    stream_close              = load_I_PS("AAudioStream_close");
+
+    stream_getBufferSize      = load_I_PS("AAudioStream_getBufferSizeInFrames");
+    stream_getDeviceId        = load_I_PS("AAudioStream_getDeviceId");
+    stream_getDirection       = load_I_PS("AAudioStream_getDirection");
+    stream_getBufferCapacity  = load_I_PS("AAudioStream_getBufferCapacityInFrames");
+    stream_getFramesPerBurst  = load_I_PS("AAudioStream_getFramesPerBurst");
+    stream_getFramesRead      = load_L_PS("AAudioStream_getFramesWritten");
+    stream_getFramesWritten   = load_L_PS("AAudioStream_getFramesRead");
+    stream_getPerformanceMode = load_I_PS("AAudioStream_getPerformanceMode");
+    stream_getSampleRate      = load_I_PS("AAudioStream_getSampleRate");
+    stream_getSharingMode     = load_I_PS("AAudioStream_getSharingMode");
+    stream_getState           = load_I_PS("AAudioStream_getState");
+    stream_getXRunCount       = load_I_PS("AAudioStream_getXRunCount");
+
+    stream_requestStart       = load_I_PS("AAudioStream_requestStart");
+    stream_requestPause       = load_I_PS("AAudioStream_requestPause");
+    stream_requestFlush       = load_I_PS("AAudioStream_requestFlush");
+    stream_requestStop        = load_I_PS("AAudioStream_requestStop");
+
+    stream_setBufferSize      = load_I_PSI("AAudioStream_setBufferSizeInFrames");
+
+    convertResultToText       = load_PC_I("AAudio_convertResultToText");
+    convertStreamStateToText  = load_PC_I("AAudio_convertStreamStateToText");
+
+    return 0;
+}
+
+int AAudioLoader::close() {
+    if (mLibHandle != nullptr) {
+        dlclose(mLibHandle);
+        mLibHandle = nullptr;
+    }
+    return 0;
+}
+
+static void AAudioLoader_check(void *proc, const char *functionName) {
+    if (proc == nullptr) {
+        LOGE("AAudioLoader could not find %s", functionName);
+    } else {
+        LOGV("AAudioLoader(): dlsym(%s) succeeded.", functionName);
+    }
+}
+
+signature_PC_I AAudioLoader::load_PC_I(const char *functionName) {
+    signature_PC_I proc = (signature_PC_I) dlsym(mLibHandle, functionName);
+    AAudioLoader_check((void *)proc, functionName);
+    return proc;
+}
+
+signature_V_PBI AAudioLoader::load_V_PBI(const char *functionName) {
+    signature_V_PBI proc = (signature_V_PBI) dlsym(mLibHandle, functionName);
+    AAudioLoader_check((void *)proc, functionName);
+    return proc;
+}
+
+signature_I_PSI AAudioLoader::load_I_PSI(const char *functionName) {
+    signature_I_PSI proc = (signature_I_PSI) dlsym(mLibHandle, functionName);
+    AAudioLoader_check((void *)proc, functionName);
+    return proc;
+}
+
+signature_I_PS AAudioLoader::load_I_PS(const char *functionName) {
+    signature_I_PS proc = (signature_I_PS) dlsym(mLibHandle, functionName);
+    AAudioLoader_check((void *)proc, functionName);
+    return proc;
+}
+
+signature_L_PS AAudioLoader::load_L_PS(const char *functionName) {
+    signature_L_PS proc = (signature_L_PS) dlsym(mLibHandle, functionName);
+    AAudioLoader_check((void *)proc, functionName);
+    return proc;
+}
+
+signature_I_PB AAudioLoader::load_I_PB(const char *functionName) {
+    signature_I_PB proc = (signature_I_PB) dlsym(mLibHandle, functionName);
+    AAudioLoader_check((void *)proc, functionName);
+    return proc;
+}
diff --git a/src/aaudio/AAudioLoader.h b/src/aaudio/AAudioLoader.h
new file mode 100644
index 0000000..b8f3dd3
--- /dev/null
+++ b/src/aaudio/AAudioLoader.h
@@ -0,0 +1,161 @@
+/*
+ * 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 NATIVEMIDISYNTH_AAUDIOLOADER_H
+#define NATIVEMIDISYNTH_AAUDIOLOADER_H
+
+#include <unistd.h>
+#include "oboe/OboeDefinitions.h"
+
+#include "aaudio/AAudio.h"
+
+// Use signatures for common functions.
+typedef const char * (*signature_PC_I)(int32_t);
+typedef int32_t (*signature_I_I)(int32_t);
+typedef int32_t (*signature_I_II)(int32_t, int32_t);
+typedef int32_t (*signature_I_IPI)(int32_t, int32_t *);
+typedef int32_t (*signature_I_IIPI)(int32_t, int32_t, int32_t *);
+
+typedef int32_t (*signature_I_PB)(AAudioStreamBuilder *);  // AAudioStreamBuilder_delete()
+typedef void    (*signature_V_PBI)(AAudioStreamBuilder *, int32_t);  // AAudioStreamBuilder_setSampleRate()
+
+typedef int32_t (*signature_I_PS)(AAudioStream *);  // AAudioStream_getSampleRate()
+typedef int64_t (*signature_L_PS)(AAudioStream *);  // AAudioStream_getFramesRead()
+typedef int32_t (*signature_I_PSI)(AAudioStream *, int32_t);  // AAudioStream_setBufferSizeInFrames()
+/**
+ * The AAudio API was not available in early versions of Android.
+ * To avoid linker errors, we dynamically link with the functions by name using dlsym().
+ * On older versions this linkage will safely fail.
+ */
+class AAudioLoader {
+public:
+    AAudioLoader() {}
+    virtual ~AAudioLoader();
+
+    static AAudioLoader *getInstance(); // singleton
+
+    /**
+     * Open the AAudio shared library and load the function pointers.
+     * This can be called multiple times.
+     * It should only be called from one thread.
+     *
+     * @return 0 if successful or negative error.
+     */
+    int open();
+
+    /**
+     * Close the AAudio shared library.
+     * This can be called multiple times.
+     * It should only be called from one thread.
+     *
+     * The open() and close() do not nest. Calling close() once will always close the library.
+     * The destructor will call close() so you don't need to.
+     *
+     * @return 0 if successful or negative error.
+     */
+    int close();
+
+    // Function pointers into the AAudio shared library.
+    aaudio_result_t (*createStreamBuilder)(AAudioStreamBuilder **builder);
+
+    aaudio_result_t  (*builder_openStream)(AAudioStreamBuilder *builder,
+                                           AAudioStream **stream);
+
+    const char * AAudio_convertResultToText(aaudio_result_t returnCode);
+
+    signature_V_PBI builder_setBufferCapacityInFrames;
+    signature_V_PBI builder_setChannelCount;
+    signature_V_PBI builder_setDeviceId;
+    signature_V_PBI builder_setDirection;
+    signature_V_PBI builder_setFormat;
+    signature_V_PBI builder_setFramesPerDataCallback;
+    signature_V_PBI builder_setPerformanceMode;
+    signature_V_PBI builder_setSampleRate;
+    signature_V_PBI builder_setSharingMode;
+
+    void (*builder_setDataCallback)(AAudioStreamBuilder *builder,
+                                    AAudioStream_dataCallback callback,
+                                    void *userData);
+
+    void (*builder_setErrorCallback)(AAudioStreamBuilder *builder,
+                                    AAudioStream_errorCallback callback,
+                                    void *userData);
+
+    signature_I_PB  builder_delete;
+
+    aaudio_format_t (*stream_getFormat)(AAudioStream *stream);
+
+    aaudio_result_t (*stream_write)(AAudioStream *stream,
+                                   const void *buffer,
+                                   int32_t numFrames,
+                                   int64_t timeoutNanoseconds);
+
+    aaudio_result_t (*stream_waitForStateChange)(AAudioStream *stream,
+                                                 aaudio_stream_state_t inputState,
+                                                 aaudio_stream_state_t *nextState,
+                                                 int64_t timeoutNanoseconds);
+
+    aaudio_result_t (*stream_getTimestamp)(AAudioStream *stream,
+                                          clockid_t clockid,
+                                          int64_t *framePosition,
+                                          int64_t *timeNanoseconds);
+
+    signature_I_PS   stream_close;
+
+    signature_I_PS   stream_getChannelCount;
+    signature_I_PS   stream_getDeviceId;
+    signature_I_PS   stream_getDirection;
+    signature_I_PS   stream_getBufferSize;
+    signature_I_PS   stream_getBufferCapacity;
+    signature_I_PS   stream_getFramesPerBurst;
+    signature_I_PS   stream_getState;
+    signature_I_PS   stream_getPerformanceMode;
+    signature_I_PS   stream_getSampleRate;
+    signature_I_PS   stream_getSharingMode;
+    signature_I_PS   stream_getXRunCount;
+
+    signature_I_PSI  stream_setBufferSize;
+    signature_I_PS   stream_requestStart;
+    signature_I_PS   stream_requestPause;
+    signature_I_PS   stream_requestFlush;
+    signature_I_PS   stream_requestStop;
+
+    signature_L_PS   stream_getFramesRead;
+    signature_L_PS   stream_getFramesWritten;
+
+    signature_PC_I   convertResultToText;
+    signature_PC_I   convertStreamStateToText;
+
+    // TODO add any missing AAudio functions.
+
+private:
+    static AAudioLoader *mInstance;
+
+    // Load function pointers for specific signatures.
+    signature_PC_I   load_PC_I(const char *name);
+
+    signature_V_PBI  load_V_PBI(const char *name);
+    signature_I_PB   load_I_PB(const char *name);
+    signature_I_PS   load_I_PS(const char *name);
+    signature_L_PS   load_L_PS(const char *name);
+    signature_I_PSI  load_I_PSI(const char *name);
+
+    void *mLibHandle = nullptr;
+
+};
+
+
+#endif //NATIVEMIDISYNTH_AAUDIOLOADER_H
diff --git a/src/aaudio/OboeStreamAAudio.cpp b/src/aaudio/OboeStreamAAudio.cpp
new file mode 100644
index 0000000..5bfd5d5
--- /dev/null
+++ b/src/aaudio/OboeStreamAAudio.cpp
@@ -0,0 +1,256 @@
+/*
+ * 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.
+ */
+
+#include <stdint.h>
+
+#include "common/OboeUtilities.h"
+#include "common/OboeDebug.h"
+#include "aaudio/AAudioLoader.h"
+#include "aaudio/OboeStreamAAudio.h"
+
+AAudioLoader *OboeStreamAAudio::mLibLoader = nullptr;
+
+/*
+ * Create a stream that uses Oboe Audio API.
+ */
+OboeStreamAAudio::OboeStreamAAudio(const OboeStreamBuilder &builder)
+    : OboeStream(builder)
+    , mFloatCallbackBuffer(nullptr)
+    , mShortCallbackBuffer(nullptr)
+    , mAAudioStream(nullptr)
+{
+    mCallbackThreadEnabled.store(false);
+    LOGD("OboeStreamAAudio() call isSupported()");
+    isSupported();
+}
+
+OboeStreamAAudio::~OboeStreamAAudio()
+{
+    delete[] mFloatCallbackBuffer;
+    delete[] mShortCallbackBuffer;
+}
+
+bool OboeStreamAAudio::isSupported() {
+    mLibLoader = AAudioLoader::getInstance();
+    int openResult = mLibLoader->open();
+    return openResult == 0;
+}
+
+// 'C' wrapper for the data callback method
+static aaudio_data_callback_result_t oboe_aaudio_data_callback_proc(
+        AAudioStream *stream,
+        void *userData,
+        void *audioData,
+        int32_t numFrames) {
+
+    OboeStreamAAudio *oboeStream = (OboeStreamAAudio *)userData;
+    if (oboeStream != NULL) {
+        return oboeStream->callOnAudioReady(stream, audioData, numFrames);
+    } else {
+        return AAUDIO_CALLBACK_RESULT_STOP;
+    }
+}
+
+// 'C' wrapper for the error callback method
+static void oboe_aaudio_error_callback_proc(
+        AAudioStream *stream,
+        void *userData,
+        aaudio_result_t error) {
+
+    OboeStreamAAudio *oboeStream = (OboeStreamAAudio *)userData;
+    if (oboeStream != NULL) {
+        oboeStream->callOnError(stream, error);
+    }
+}
+
+oboe_result_t OboeStreamAAudio::open() {
+    oboe_result_t result = AAUDIO_OK;
+
+    if (mAAudioStream != nullptr) {
+        return AAUDIO_ERROR_INVALID_STATE;
+    }
+
+    result = OboeStream::open();
+    if (result != AAUDIO_OK) {
+        return result;
+    }
+
+    LOGD("OboeStreamAAudio():  AAudio_createStreamBuilder()");
+    AAudioStreamBuilder *aaudioBuilder;
+    result = mLibLoader->createStreamBuilder(&aaudioBuilder);
+    if (result != AAUDIO_OK) {
+        return result;
+    }
+
+    LOGD("OboeStreamAAudio.open() try with deviceId = %d", (int) mDeviceId);
+    mLibLoader->builder_setBufferCapacityInFrames(aaudioBuilder, mBufferCapacityInFrames);
+    mLibLoader->builder_setChannelCount(aaudioBuilder, mChannelCount);
+    mLibLoader->builder_setDeviceId(aaudioBuilder, mDeviceId);
+    mLibLoader->builder_setDirection(aaudioBuilder, mDirection);
+    mLibLoader->builder_setFormat(aaudioBuilder, mFormat);
+    mLibLoader->builder_setSampleRate(aaudioBuilder, mSampleRate);
+    mLibLoader->builder_setSharingMode(aaudioBuilder, mSharingMode);
+    mLibLoader->builder_setPerformanceMode(aaudioBuilder, mPerformanceMode);
+
+    // TODO get more parameters from the builder
+
+    if (mStreamCallback != nullptr) {
+        mLibLoader->builder_setDataCallback(aaudioBuilder, oboe_aaudio_data_callback_proc, this);
+        mLibLoader->builder_setErrorCallback(aaudioBuilder, oboe_aaudio_error_callback_proc, this);
+        mLibLoader->builder_setFramesPerDataCallback(aaudioBuilder, getFramesPerCallback());
+    }
+
+    result = mLibLoader->builder_openStream(aaudioBuilder, &mAAudioStream);
+    if (result != AAUDIO_OK) {
+        goto error2;
+    }
+
+    mChannelCount = mLibLoader->stream_getChannelCount(mAAudioStream);
+    mSampleRate = mLibLoader->stream_getSampleRate(mAAudioStream);
+    mNativeFormat = mLibLoader->stream_getFormat(mAAudioStream);
+    if (mFormat == OBOE_AUDIO_FORMAT_UNSPECIFIED) {
+        mFormat = mNativeFormat;
+    }
+    mSharingMode = mLibLoader->stream_getSharingMode(mAAudioStream);
+    mPerformanceMode = mLibLoader->stream_getPerformanceMode(mAAudioStream);
+
+    LOGD("OboeStreamAAudio.open() app    format = %d", (int) mFormat);
+    LOGD("OboeStreamAAudio.open() native format = %d", (int) mNativeFormat);
+
+error2:
+    mLibLoader->builder_delete(aaudioBuilder);
+    LOGD("OboeStreamAAudio.open: AAudioStream_Open() returned %s, mAAudioStream = %p",
+         mLibLoader->convertResultToText(result), mAAudioStream);
+    return result;
+}
+
+oboe_result_t OboeStreamAAudio::close()
+{
+    oboe_result_t result = mLibLoader->stream_close(mAAudioStream);
+    mAAudioStream = nullptr;
+    return result;
+}
+
+aaudio_data_callback_result_t OboeStreamAAudio::callOnAudioReady(AAudioStream *stream,
+                                                                 void *audioData,
+                                                                 int32_t numFrames) {
+    return mStreamCallback->onAudioReady(
+            this,
+            audioData,
+            numFrames);
+}
+
+void OboeStreamAAudio::callOnError(AAudioStream *stream, oboe_result_t error) {
+    mStreamCallback->onError( this, error);
+}
+
+oboe_result_t OboeStreamAAudio::convertApplicationDataToNative(int32_t numFrames) {
+    oboe_result_t result = OBOE_ERROR_UNIMPLEMENTED;
+    int32_t numSamples = numFrames * getChannelCount();
+    if (mFormat == OBOE_AUDIO_FORMAT_PCM_FLOAT) {
+        if (mNativeFormat == OBOE_AUDIO_FORMAT_PCM_I16) {
+            OboeConvert_floatToPcm16(mFloatCallbackBuffer, mShortCallbackBuffer, numSamples);
+            result = AAUDIO_OK;
+        }
+    } else if (mFormat == OBOE_AUDIO_FORMAT_PCM_I16) {
+        if (mNativeFormat == OBOE_AUDIO_FORMAT_PCM_FLOAT) {
+            OboeConvert_pcm16ToFloat(mShortCallbackBuffer, mFloatCallbackBuffer, numSamples);
+            result = AAUDIO_OK;
+        }
+    }
+    return result;
+}
+
+oboe_result_t OboeStreamAAudio::requestStart()
+{
+    return mLibLoader->stream_requestStart(mAAudioStream);
+}
+
+oboe_result_t OboeStreamAAudio::requestPause()
+{
+    return mLibLoader->stream_requestPause(mAAudioStream);
+}
+
+oboe_result_t OboeStreamAAudio::requestFlush() {
+    return mLibLoader->stream_requestFlush(mAAudioStream);
+}
+
+oboe_result_t OboeStreamAAudio::requestStop()
+{
+    return mLibLoader->stream_requestStop(mAAudioStream);
+}
+
+oboe_result_t OboeStreamAAudio::write(const void *buffer,
+                                     int32_t numFrames,
+                                     int64_t timeoutNanoseconds)
+{
+    return mLibLoader->stream_write(mAAudioStream, buffer, numFrames, timeoutNanoseconds);
+}
+
+oboe_result_t OboeStreamAAudio::waitForStateChange(oboe_stream_state_t currentState,
+                                                  oboe_stream_state_t *nextState,
+                                                  int64_t timeoutNanoseconds)
+{
+    return mLibLoader->stream_waitForStateChange(mAAudioStream, currentState,
+                                                 nextState, timeoutNanoseconds);
+}
+
+oboe_result_t OboeStreamAAudio::setBufferSizeInFrames(int32_t requestedFrames)
+{
+    return mLibLoader->stream_setBufferSize(mAAudioStream, requestedFrames);
+}
+
+oboe_stream_state_t OboeStreamAAudio::getState()
+{
+    if (mAAudioStream == nullptr) {
+        return OBOE_STREAM_STATE_CLOSED;
+    }
+    return mLibLoader->stream_getState(mAAudioStream);
+}
+
+int32_t OboeStreamAAudio::getBufferSizeInFrames() const {
+    return mLibLoader->stream_getBufferSize(mAAudioStream);
+}
+
+int32_t OboeStreamAAudio::getBufferCapacityInFrames() const {
+    return mLibLoader->stream_getBufferCapacity(mAAudioStream);
+}
+
+int32_t OboeStreamAAudio::getFramesPerBurst()
+{
+    return mLibLoader->stream_getFramesPerBurst(mAAudioStream);
+}
+
+int64_t OboeStreamAAudio::getFramesRead()
+{
+    return mLibLoader->stream_getFramesRead(mAAudioStream);
+}
+int64_t OboeStreamAAudio::getFramesWritten()
+{
+    return mLibLoader->stream_getFramesWritten(mAAudioStream);
+}
+
+int32_t OboeStreamAAudio::getXRunCount()
+{
+    return mLibLoader->stream_getXRunCount(mAAudioStream);
+}
+
+oboe_result_t OboeStreamAAudio::getTimestamp(clockid_t clockId,
+                                   int64_t *framePosition,
+                                   int64_t *timeNanoseconds) {
+    return mLibLoader->stream_getTimestamp(mAAudioStream, clockId,
+                                   framePosition, timeNanoseconds);
+}
diff --git a/src/aaudio/OboeStreamAAudio.h b/src/aaudio/OboeStreamAAudio.h
new file mode 100644
index 0000000..f636ef6
--- /dev/null
+++ b/src/aaudio/OboeStreamAAudio.h
@@ -0,0 +1,103 @@
+/*
+ * 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_OBOE_STREAM_AAUDIO_H_
+#define OBOE_OBOE_STREAM_AAUDIO_H_
+
+#include <atomic>
+
+#include "oboe/OboeStreamBuilder.h"
+#include "oboe/OboeStream.h"
+#include "oboe/OboeDefinitions.h"
+
+#include "aaudio/AAudio.h"
+
+class AAudioLoader;
+
+/**
+ * Implementation of OboeStream that uses AAudio.
+ *
+ * Do not create this class directly.
+ * Use an OboeStreamBuilder to create one.
+ */
+class OboeStreamAAudio : public OboeStream {
+public:
+    OboeStreamAAudio();
+    explicit OboeStreamAAudio(const OboeStreamBuilder &builder);
+
+    ~OboeStreamAAudio();
+
+    /**
+     *
+     * @return true is AAudio is supported on this device.
+     */
+    static bool isSupported();
+
+    // These functions override methods in OboeStream.
+    // See OboeStream for documentation.
+    oboe_result_t open() override;
+    oboe_result_t close() override;
+
+    oboe_result_t requestStart() override;
+    oboe_result_t requestPause() override;
+    oboe_result_t requestFlush() override;
+    oboe_result_t requestStop() override;
+
+    oboe_result_t write(const void *buffer,
+                             int32_t numFrames,
+                             int64_t timeoutNanoseconds) override;
+
+    oboe_result_t setBufferSizeInFrames(int32_t requestedFrames) override;
+    int32_t getBufferSizeInFrames() const override;
+    int32_t getBufferCapacityInFrames() const override;
+    int32_t getFramesPerBurst() override;
+    int32_t getXRunCount() override;
+
+    int64_t getFramesRead() override;
+    int64_t getFramesWritten() override;
+
+    oboe_result_t waitForStateChange(oboe_stream_state_t currentState,
+                                             oboe_stream_state_t *nextState,
+                                             int64_t timeoutNanoseconds) override;
+
+    oboe_result_t getTimestamp(clockid_t clockId,
+                                       int64_t *framePosition,
+                                       int64_t *timeNanoseconds) override;
+
+    oboe_stream_state_t getState() override;
+
+
+public:
+ // FIXME remove   void *callbackLoop();
+    aaudio_data_callback_result_t callOnAudioReady(AAudioStream *stream,
+                                                   void *audioData,
+                                                   int32_t numFrames);
+    void callOnError(AAudioStream *stream, oboe_result_t error);
+
+protected:
+    oboe_result_t convertApplicationDataToNative(int32_t numFrames);
+
+private:
+    float            *mFloatCallbackBuffer;
+    int16_t          *mShortCallbackBuffer;
+    std::atomic<bool> mCallbackThreadEnabled;
+    AAudioStream     *mAAudioStream;
+
+    static AAudioLoader *mLibLoader;
+};
+
+
+#endif // OBOE_OBOE_STREAM_AAUDIO_H_
diff --git a/src/common/AudioClock.h b/src/common/AudioClock.h
new file mode 100644
index 0000000..a9bbd0a
--- /dev/null
+++ b/src/common/AudioClock.h
@@ -0,0 +1,71 @@
+/*
+ * 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 OBOE_AUDIO_CLOCK_H
+#define OBOE_AUDIO_CLOCK_H
+
+#include <sys/types.h>
+#include <time.h>
+#include "oboe/OboeDefinitions.h"
+
+class AudioClock {
+public:
+    static int64_t getNanoseconds(clockid_t clockId = CLOCK_MONOTONIC) {
+        struct timespec time;
+        int result = clock_gettime(clockId, &time);
+        if (result < 0) {
+            return result;
+        }
+        return (time.tv_sec * OBOE_NANOS_PER_SECOND) + time.tv_nsec;
+    }
+
+    /**
+     * Sleep until the specified time.
+     *
+     * @param nanoTime time to wake up
+     * @param clockId CLOCK_MONOTONIC is default
+     * @return 0 or a negative error, eg. -EINTR
+     */
+
+    static int sleepUntilNanoTime(int64_t nanoTime, clockid_t clockId = CLOCK_MONOTONIC) {
+        struct timespec time;
+        time.tv_sec = nanoTime / OBOE_NANOS_PER_SECOND;
+        time.tv_nsec = nanoTime - (time.tv_sec * OBOE_NANOS_PER_SECOND);
+        return 0 - clock_nanosleep(clockId, TIMER_ABSTIME, &time, NULL);
+    }
+
+    /**
+     * Sleep for the specified number of nanoseconds in real-time.
+     * Return immediately with 0 if a negative nanoseconds is specified.
+     *
+     * @param nanoseconds time to sleep
+     * @param clockId CLOCK_REALTIME is default
+     * @return 0 or a negative error, eg. -EINTR
+     */
+
+    static int sleepForNanos(int64_t nanoseconds, clockid_t clockId = CLOCK_REALTIME) {
+        if (nanoseconds > 0) {
+            struct timespec time;
+            time.tv_sec = nanoseconds / OBOE_NANOS_PER_SECOND;
+            time.tv_nsec = nanoseconds - (time.tv_sec * OBOE_NANOS_PER_SECOND);
+            return 0 - clock_nanosleep(clockId, 0, &time, NULL);
+        }
+        return 0;
+    }
+};
+
+
+#endif //OBOE_AUDIO_CLOCK_H
diff --git a/src/common/OboeDebug.h b/src/common/OboeDebug.h
new file mode 100644
index 0000000..d998ecd
--- /dev/null
+++ b/src/common/OboeDebug.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 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_OBOE_DEBUG_H
+#define OBOE_OBOE_DEBUG_H
+#include <android/log.h>
+
+#if 1
+
+#ifndef MODULE_NAME
+#define MODULE_NAME  "OboeAudio"
+#endif
+#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, MODULE_NAME, __VA_ARGS__)
+#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, MODULE_NAME, __VA_ARGS__)
+#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, MODULE_NAME, __VA_ARGS__)
+#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, MODULE_NAME, __VA_ARGS__)
+#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, MODULE_NAME, __VA_ARGS__)
+#define LOGF(...) __android_log_print(ANDROID_LOG_FATAL, MODULE_NAME, __VA_ARGS__)
+
+#else
+
+#define LOGV(...)
+#define LOGD(...)
+#define LOGI(...)
+#define LOGW(...)
+#define LOGE(...)
+#define LOGF(...)
+#endif
+
+#endif //OBOE_OBOE_DEBUG_H
diff --git a/src/common/OboeStream.cpp b/src/common/OboeStream.cpp
new file mode 100644
index 0000000..712f570
--- /dev/null
+++ b/src/common/OboeStream.cpp
@@ -0,0 +1,136 @@
+/*
+ * 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.
+ */
+
+#include <sys/types.h>
+#include <pthread.h>
+
+#include "common/OboeUtilities.h"
+#include "common/AudioClock.h"
+#include "OboeDebug.h"
+#include "oboe/Oboe.h"
+
+/*
+ * OboeStream
+ */
+
+OboeStream::OboeStream(const OboeStreamBuilder &builder)
+        : OboeStreamBase(builder) {
+}
+
+oboe_result_t OboeStream::open() {
+    // TODO validate parameters or let underlyng API validate them?
+    return OBOE_OK;
+}
+
+oboe_result_t OboeStream::fireCallback(void *audioData, int32_t numFrames)
+{
+    int scheduler = sched_getscheduler(0) & ~SCHED_RESET_ON_FORK; // for current thread
+    if (scheduler != mPreviousScheduler) {
+        LOGD("OboeStream::fireCallback() scheduler = %s",
+             ((scheduler == SCHED_FIFO) ? "SCHED_FIFO" :
+             ((scheduler == SCHED_OTHER) ? "SCHED_OTHER" :
+             ((scheduler == SCHED_RR) ? "SCHED_RR" : "UNKNOWN")))
+        );
+        mPreviousScheduler = scheduler;
+    }
+    if (mStreamCallback == NULL) {
+        return OBOE_ERROR_NULL;
+    } else {
+        oboe_result_t result = mStreamCallback->onAudioReady(this, audioData, numFrames);
+        if (result == OBOE_OK) {
+            mFramesWritten += numFrames;
+        }
+        return result;
+    }
+}
+
+oboe_result_t OboeStream::waitForStateTransition(oboe_stream_state_t startingState,
+                                               oboe_stream_state_t endingState,
+                                               int64_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 < 0) {
+            return result;
+        }
+    }
+    if (nextState != endingState) {
+        return OBOE_ERROR_INVALID_STATE;
+    } else {
+        return OBOE_OK;
+    }
+}
+
+oboe_result_t OboeStream::start(int64_t timeoutNanoseconds)
+{
+    oboe_result_t result = requestStart();
+    if (result < 0) return result;
+    return waitForStateTransition(OBOE_STREAM_STATE_STARTING,
+                                  OBOE_STREAM_STATE_STARTED, timeoutNanoseconds);
+}
+
+oboe_result_t OboeStream::pause(int64_t timeoutNanoseconds)
+{
+    oboe_result_t result = requestPause();
+    if (result < 0) return result;
+    return waitForStateTransition(OBOE_STREAM_STATE_PAUSING,
+                                  OBOE_STREAM_STATE_PAUSED, timeoutNanoseconds);
+}
+
+oboe_result_t OboeStream::flush(int64_t timeoutNanoseconds)
+{
+    oboe_result_t result = requestFlush();
+    if (result < 0) return result;
+    return waitForStateTransition(OBOE_STREAM_STATE_FLUSHING,
+                                  OBOE_STREAM_STATE_FLUSHED, timeoutNanoseconds);
+}
+
+oboe_result_t OboeStream::stop(int64_t timeoutNanoseconds)
+{
+    oboe_result_t result = requestStop();
+    if (result < 0) return result;
+    return waitForStateTransition(OBOE_STREAM_STATE_STOPPING,
+                                  OBOE_STREAM_STATE_STOPPED, timeoutNanoseconds);
+}
+
+bool OboeStream::isPlaying() {
+    oboe_stream_state_t state = getState();
+    return state == OBOE_STREAM_STATE_STARTING || state == OBOE_STREAM_STATE_STARTED;
+}
+
+int32_t OboeStream::getBytesPerSample() const {
+    return OboeConvert_formatToSizeInBytes(mFormat);
+}
+
+void OboeStream::tuneLatency() {
+    if (mLatencyTriggerRequests != mLatencyTriggerResponses) {
+        mLatencyTriggerResponses = mLatencyTriggerRequests;
+        setBufferSizeInFrames(getFramesPerBurst());
+    } else {
+        int32_t xRuns = getXRunCount();
+        if (xRuns != mPreviousXRuns) {
+            mPreviousXRuns = xRuns;
+            int32_t requestedBufferSize = getBufferSizeInFrames() + getFramesPerBurst();
+            setBufferSizeInFrames(requestedBufferSize);
+        }
+    }
+}
+
+void OboeStream::triggerLatencyRetuning() {
+    mLatencyTriggerRequests++;
+}
\ No newline at end of file
diff --git a/src/common/OboeStreamBuilder.cpp b/src/common/OboeStreamBuilder.cpp
new file mode 100644
index 0000000..86c5e7c
--- /dev/null
+++ b/src/common/OboeStreamBuilder.cpp
@@ -0,0 +1,58 @@
+/*
+ * 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.
+ */
+
+#include <sys/types.h>
+#include "OboeDebug.h"
+#include "oboe/Oboe.h"
+
+#include "opensles/OboeStreamOpenSLES.h"
+#include "aaudio/OboeStreamAAudio.h"
+
+OboeStream *OboeStreamBuilder::build() {
+    LOGD("OboeStreamBuilder.build(): mAudioApi %d, mChannelCount = %d, mFramesPerCallback = %d",
+         mAudioApi, mChannelCount, mFramesPerCallback);
+    OboeStream *stream = nullptr;
+    switch(mAudioApi) {
+        case API_UNSPECIFIED:
+        case API_AAUDIO:
+            if (OboeStreamAAudio::isSupported()) {
+                stream = new OboeStreamAAudio(*this);
+                break;
+            }
+            // fall into using older existing API
+        case API_OPENSL_ES:
+            stream = new OboeStreamOpenSLES(*this);
+            break;
+    }
+    return stream;
+}
+
+
+oboe_result_t OboeStreamBuilder::openStream(OboeStream **streamPP) {
+    if (streamPP == nullptr) {
+        return OBOE_ERROR_NULL;
+    }
+    *streamPP = nullptr;
+    OboeStream *streamP = build();
+    if (streamP == nullptr) {
+        return OBOE_ERROR_NULL;
+    }
+    oboe_result_t result = streamP->open(); // review API
+    if (result == OBOE_OK) {
+        *streamPP = streamP;
+    }
+    return result;
+}
diff --git a/src/common/OboeUtilities.cpp b/src/common/OboeUtilities.cpp
new file mode 100644
index 0000000..58f0b51
--- /dev/null
+++ b/src/common/OboeUtilities.cpp
@@ -0,0 +1,56 @@
+/*
+ * 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.
+ */
+
+
+#include <unistd.h>
+#include <sys/types.h>
+#include "oboe/OboeDefinitions.h"
+#include "common/OboeUtilities.h"
+
+void OboeConvert_floatToPcm16(const float *source, int16_t *destination, int32_t numSamples) {
+    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 int16_t *source, float *destination, int32_t numSamples) {
+    for (int i = 0; i < numSamples; i++) {
+        destination[i] = source[i] * (1.0f / 32768.0f);
+    }
+}
+
+int32_t OboeConvert_formatToSizeInBytes(oboe_audio_format_t format) {
+    int32_t size = OBOE_ERROR_ILLEGAL_ARGUMENT;
+    switch (format) {
+        case OBOE_AUDIO_FORMAT_PCM_I16:
+            size = sizeof(int16_t);
+            break;
+        case OBOE_AUDIO_FORMAT_PCM_FLOAT:
+            size = sizeof(float);
+            break;
+        default:
+            break;
+    }
+    return size;
+}
diff --git a/src/common/OboeUtilities.h b/src/common/OboeUtilities.h
new file mode 100644
index 0000000..4bd7027
--- /dev/null
+++ b/src/common/OboeUtilities.h
@@ -0,0 +1,32 @@
+/*
+ * 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_OBOE_UTILITIES_H
+#define OBOE_OBOE_UTILITIES_H
+
+#include <unistd.h>
+#include <sys/types.h>
+#include "oboe/OboeDefinitions.h"
+
+void OboeConvert_floatToPcm16(const float *source, int16_t *destination, int32_t numSamples);
+void OboeConvert_pcm16ToFloat(const int16_t *source, float *destination, int32_t numSamples);
+
+/**
+ * @return the size of a sample of the given format in bytes or OBOE_ERROR_ILLEGAL_ARGUMENT
+ */
+int32_t OboeConvert_formatToSizeInBytes(oboe_audio_format_t format);
+
+#endif //OBOE_OBOE_UTILITIES_H
diff --git a/src/fifo/FifoBuffer.cpp b/src/fifo/FifoBuffer.cpp
new file mode 100644
index 0000000..24137d4
--- /dev/null
+++ b/src/fifo/FifoBuffer.cpp
@@ -0,0 +1,191 @@
+/*
+ * 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.
+ */
+
+#include <stdint.h>
+#include <time.h>
+
+#include "common/OboeDebug.h"
+#include "fifo/FifoControllerBase.h"
+#include "fifo/FifoController.h"
+#include "fifo/FifoControllerIndirect.h"
+#include "fifo/FifoBuffer.h"
+#include "common/AudioClock.h"
+
+FifoBuffer::FifoBuffer(uint32_t bytesPerFrame, uint32_t capacityInFrames)
+        : mFrameCapacity(capacityInFrames)
+        , mBytesPerFrame(bytesPerFrame)
+        , mStorage(NULL)
+        , mReadAtNanoseconds(0)
+        , mFramesReadCount(0)
+        , mFramesUnderrunCount(0)
+        , mUnderrunCount(0)
+{
+    mFifo = new FifoController(capacityInFrames, capacityInFrames);
+    // allocate buffer
+    int32_t bytesPerBuffer = bytesPerFrame * capacityInFrames;
+    mStorage = new uint8_t[bytesPerBuffer];
+    mStorageOwned = true;
+    LOGD("FifoProcessor: numFrames = %d, bytesPerFrame = %d", capacityInFrames, bytesPerFrame);
+}
+
+FifoBuffer::FifoBuffer( uint32_t   bytesPerFrame,
+                        uint32_t   capacityInFrames,
+                        int64_t *  readIndexAddress,
+                        int64_t *  writeIndexAddress,
+                        uint8_t *  dataStorageAddress
+                        )
+        : mFrameCapacity(capacityInFrames)
+        , mBytesPerFrame(bytesPerFrame)
+        , mStorage(dataStorageAddress)
+        , mReadAtNanoseconds(0)
+        , mFramesReadCount(0)
+        , mFramesUnderrunCount(0)
+        , mUnderrunCount(0)
+{
+    mFifo = new FifoControllerIndirect(capacityInFrames,
+                                       capacityInFrames,
+                                       readIndexAddress,
+                                       writeIndexAddress);
+    mStorage = dataStorageAddress;
+    mStorageOwned = false;
+    LOGD("FifoProcessor: capacityInFrames = %d, bytesPerFrame = %d", capacityInFrames, bytesPerFrame);
+}
+
+FifoBuffer::~FifoBuffer() {
+    if (mStorageOwned) {
+        delete[] mStorage;
+    }
+    delete mFifo;
+}
+
+
+int32_t FifoBuffer::convertFramesToBytes(int32_t frames) {
+    return frames * mBytesPerFrame;
+}
+
+int32_t FifoBuffer::read(void *buffer, int32_t numFrames) {
+    size_t numBytes;
+    int32_t framesAvailable = mFifo->getFullFramesAvailable();
+    int32_t framesToRead = numFrames;
+    // Is there enough data in the FIFO
+    if (framesToRead > framesAvailable) {
+        framesToRead = framesAvailable;
+    }
+    if (framesToRead == 0) {
+        return 0;
+    }
+
+    uint32_t readIndex = mFifo->getReadIndex();
+    uint8_t *destination = (uint8_t *) buffer;
+    uint8_t *source = &mStorage[convertFramesToBytes(readIndex)];
+    if ((readIndex + framesToRead) > mFrameCapacity) {
+        // read in two parts, first part here
+        uint32_t frames1 = mFrameCapacity - readIndex;
+        uint32_t numBytes = convertFramesToBytes(frames1);
+        memcpy(destination, source, numBytes);
+        destination += numBytes;
+        // read second part
+        source = &mStorage[0];
+        int frames2 = framesToRead - frames1;
+        numBytes = convertFramesToBytes(frames2);
+        memcpy(destination, source, numBytes);
+    } else {
+        // just read in one shot
+        numBytes = convertFramesToBytes(framesToRead);
+        memcpy(destination, source, numBytes);
+    }
+    mFifo->advanceReadIndex(framesToRead);
+
+    return framesToRead;
+}
+
+int32_t FifoBuffer::write(const void *buffer, int32_t framesToWrite) {
+    int32_t framesAvailable = mFifo->getEmptyFramesAvailable();
+//    LOGD("FifoBuffer::write() framesToWrite = %d, framesAvailable = %d",
+//         framesToWrite, framesAvailable);
+    if (framesToWrite > framesAvailable) {
+        framesToWrite = framesAvailable;
+    }
+    if (framesToWrite <= 0) {
+        return 0;
+    }
+
+    size_t numBytes;
+    uint32_t writeIndex = mFifo->getWriteIndex();
+    int byteIndex = convertFramesToBytes(writeIndex);
+    const uint8_t *source = (const uint8_t *) buffer;
+    uint8_t *destination = &mStorage[byteIndex];
+    if ((writeIndex + framesToWrite) > mFrameCapacity) {
+        // write in two parts, first part here
+        int frames1 = mFrameCapacity - writeIndex;
+        numBytes = convertFramesToBytes(frames1);
+        memcpy(destination, source, numBytes);
+//        LOGD("FifoBuffer::write(%p to %p, numBytes = %d", source, destination, numBytes);
+        // read second part
+        source += convertFramesToBytes(frames1);
+        destination = &mStorage[0];
+        int framesLeft = framesToWrite - frames1;
+        numBytes = convertFramesToBytes(framesLeft);
+//        LOGD("FifoBuffer::write(%p to %p, numBytes = %d", source, destination, numBytes);
+        memcpy(destination, source, numBytes);
+    } else {
+        // just write in one shot
+        numBytes = convertFramesToBytes(framesToWrite);
+//        LOGD("FifoBuffer::write(%p to %p, numBytes = %d", source, destination, numBytes);
+        memcpy(destination, source, numBytes);
+    }
+    mFifo->advanceWriteIndex(framesToWrite);
+
+    return framesToWrite;
+}
+
+int32_t FifoBuffer::readNow(void *buffer, int32_t numFrames) {
+    mLastReadSize = numFrames;
+    int32_t framesLeft = numFrames;
+    int32_t framesRead = read(buffer, numFrames);
+    framesLeft -= framesRead;
+    mFramesReadCount += framesRead;
+    mFramesUnderrunCount += framesLeft;
+    // Zero out any samples we could not set.
+    if (framesLeft > 0) {
+        mUnderrunCount++;
+        int32_t bytesToZero = convertFramesToBytes(framesLeft);
+        memset(buffer, 0, bytesToZero);
+    }
+    mReadAtNanoseconds = AudioClock::getNanoseconds();
+
+    return framesRead;
+}
+
+int64_t FifoBuffer::getNextReadTime(int frameRate) {
+    if (mReadAtNanoseconds == 0) {
+        return 0;
+    }
+    int64_t nanosPerBuffer = (OBOE_NANOS_PER_SECOND * mLastReadSize) / frameRate;
+    return mReadAtNanoseconds + nanosPerBuffer;
+}
+
+uint32_t FifoBuffer::getThresholdFrames() const {
+    return mFifo->getThreshold();
+}
+
+uint32_t FifoBuffer::getBufferCapacityInFrames() const {
+    return mFifo->getFrameCapacity();
+}
+
+void FifoBuffer::setThresholdFrames(uint32_t threshold) {
+    mFifo->setThreshold(threshold);
+}
diff --git a/src/fifo/FifoBuffer.h b/src/fifo/FifoBuffer.h
new file mode 100644
index 0000000..08fb4e7
--- /dev/null
+++ b/src/fifo/FifoBuffer.h
@@ -0,0 +1,93 @@
+/*
+ * 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 NATIVEOBOE_FIFOPROCESSOR_H
+#define NATIVEOBOE_FIFOPROCESSOR_H
+
+#include <unistd.h>
+#include <sys/types.h>
+
+#include "common/OboeDebug.h"
+#include "FifoControllerBase.h"
+#include "oboe/OboeDefinitions.h"
+
+class FifoBuffer {
+public:
+    FifoBuffer(uint32_t bytesPerFrame, uint32_t capacityInFrames);
+
+    FifoBuffer(uint32_t   bytesPerFrame,
+               uint32_t   capacityInFrames,
+               int64_t * readCounterAddress,
+               int64_t * writeCounterAddress,
+               uint8_t * dataStorageAddress);
+
+    ~FifoBuffer();
+
+    int32_t convertFramesToBytes(int32_t frames);
+
+    int32_t read(void *destination, int32_t framesToRead);
+
+    int32_t write(const void *source, int32_t framesToWrite);
+
+    uint32_t getThresholdFrames() const;
+
+    void setThresholdFrames(uint32_t threshold);
+
+    uint32_t getBufferCapacityInFrames() const;
+
+    int32_t readNow(void *buffer, int32_t numFrames);
+
+    int64_t getNextReadTime(int32_t frameRate);
+
+    uint32_t getUnderrunCount() const { return mUnderrunCount; }
+
+    FifoControllerBase *getFifoControllerBase() { return mFifo; }
+
+    uint32_t getBytesPerFrame() const {
+        return mBytesPerFrame;
+    }
+
+    uint64_t getReadCounter() const {
+        return mFifo->getReadCounter();
+    }
+
+    void setReadCounter(uint64_t n) {
+        mFifo->setReadCounter(n);
+//        LOGD("FifoBuffer: setReadCounter(%d)", (int) n);
+    }
+
+    uint64_t getWriteCounter() {
+        return mFifo->getWriteCounter();
+    }
+    void setWriteCounter(uint64_t n) {
+        mFifo->setWriteCounter(n);
+//        LOGD("FifoBuffer: setWriteCounter(%d)", (int) n);
+    }
+
+private:
+    uint32_t mFrameCapacity;
+    uint32_t mBytesPerFrame;
+    uint8_t* mStorage;
+    bool     mStorageOwned; // did this object allocate the storage?
+    FifoControllerBase *mFifo;
+    int64_t  mReadAtNanoseconds;
+    uint64_t mFramesReadCount;
+    uint64_t mFramesUnderrunCount;
+    uint32_t mUnderrunCount; // need? just use frames
+    uint32_t mLastReadSize;
+};
+
+#endif //NATIVEOBOE_FIFOPROCESSOR_H
diff --git a/src/fifo/FifoController.cpp b/src/fifo/FifoController.cpp
new file mode 100644
index 0000000..e3ba373
--- /dev/null
+++ b/src/fifo/FifoController.cpp
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+#include <cassert>
+#include <sys/types.h>
+#include "FifoControllerBase.h"
+#include "FifoController.h"
+
+FifoController::FifoController(uint32_t numFrames, uint32_t threshold)
+        : FifoControllerBase(numFrames, threshold)
+{
+    setReadCounter(0);
+    setWriteCounter(0);
+}
+
+FifoController::~FifoController() {
+}
diff --git a/src/fifo/FifoController.h b/src/fifo/FifoController.h
new file mode 100644
index 0000000..d427f47
--- /dev/null
+++ b/src/fifo/FifoController.h
@@ -0,0 +1,53 @@
+/*
+ * 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 NATIVEOBOE_FIFOCONTROLLER_H
+#define NATIVEOBOE_FIFOCONTROLLER_H
+
+#include <sys/types.h>
+#include "FifoControllerBase.h"
+#include <atomic>
+
+/**
+ * A FifoControllerBase with counters contained in the class.
+ */
+class FifoController : public FifoControllerBase
+{
+public:
+    FifoController(uint32_t bufferSize, uint32_t threshold);
+    virtual ~FifoController();
+
+    // TODO review use atomics or memory barriers
+    virtual uint64_t getReadCounter() override {
+        return mReadCounter.load(std::memory_order_acquire);
+    }
+    virtual void setReadCounter(uint64_t n) override {
+        mReadCounter.store(n, std::memory_order_release);
+    }
+    virtual uint64_t getWriteCounter() override {
+        return mWriteCounter.load(std::memory_order_acquire);
+    }
+    virtual void setWriteCounter(uint64_t n) override {
+        mWriteCounter.store(n, std::memory_order_release);
+    }
+
+private:
+    std::atomic<uint64_t> mReadCounter;
+    std::atomic<uint64_t> mWriteCounter;
+};
+
+
+#endif //NATIVEOBOE_FIFOCONTROLLER_H
diff --git a/src/fifo/FifoControllerBase.cpp b/src/fifo/FifoControllerBase.cpp
new file mode 100644
index 0000000..9aedac5
--- /dev/null
+++ b/src/fifo/FifoControllerBase.cpp
@@ -0,0 +1,71 @@
+/*
+ * 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.
+ */
+
+#include "FifoControllerBase.h"
+
+#include <cassert>
+#include <sys/types.h>
+#include "FifoControllerBase.h"
+
+#include "common/OboeDebug.h"
+
+FifoControllerBase::FifoControllerBase(uint32_t totalFrames, uint32_t threshold)
+        : mTotalFrames(totalFrames)
+        , mThreshold(threshold)
+{
+    // numFrames must be a power of 2
+//    assert((totalFrames & (totalFrames - 1)) == 0);
+//    mSmallMask = totalFrames - 1;
+}
+
+FifoControllerBase::~FifoControllerBase() {
+}
+
+int32_t FifoControllerBase::getFullFramesAvailable() {
+//    LOGD("getFullFramesAvailable - writeCounter = %d, readCounter() = %d",
+//         (int)getWriteCounter(), (int) getReadCounter());
+    return (int32_t) (getWriteCounter() - getReadCounter());
+}
+
+uint32_t FifoControllerBase::getReadIndex() {
+//    return ((uint32_t) getReadCounter()) & mSmallMask;
+    return (uint32_t) (getReadCounter() % mTotalFrames);
+}
+
+void FifoControllerBase::advanceReadIndex(int numFrames) {
+    setReadCounter(getReadCounter() + numFrames);
+}
+
+int32_t FifoControllerBase::getEmptyFramesAvailable() {
+    int32_t fullFramesAvailable = getFullFramesAvailable();
+    int32_t available = (int32_t)(mThreshold - fullFramesAvailable);
+//    LOGD("getEmptyFramesAvailable - full = %d, threshold = %u, available = %d",
+//         fullFramesAvailable, mThreshold, available);
+    return available;
+}
+
+uint32_t FifoControllerBase::getWriteIndex() {
+//    return ((uint32_t) getWriteCounter()) & mSmallMask;
+    return (uint32_t) (getWriteCounter() % mTotalFrames); // % works with non-power of two sizes
+}
+
+void FifoControllerBase::advanceWriteIndex(uint32_t numFrames) {
+    setWriteCounter(getWriteCounter() + numFrames);
+}
+
+void FifoControllerBase::setThreshold(uint32_t threshold) {
+    mThreshold = threshold;
+}
diff --git a/src/fifo/FifoControllerBase.h b/src/fifo/FifoControllerBase.h
new file mode 100644
index 0000000..feb34c2
--- /dev/null
+++ b/src/fifo/FifoControllerBase.h
@@ -0,0 +1,93 @@
+/*
+ * 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 NATIVEOBOE_FIFOCONTROLLERBASE_H
+#define NATIVEOBOE_FIFOCONTROLLERBASE_H
+
+
+#include <sys/types.h>
+
+/**
+ * Manage the read/write indices of a circular buffer.
+ *
+ * The caller is responsible for reading and writing the actual data.
+ * Note that the span of available frames may not be contiguous. They
+ * may wrap around from the end to the beginning of the buffer. In that
+ * case the data must be read or written in at least two blocks of frames.
+ *
+ */
+
+class FifoControllerBase {
+
+public:
+    /**
+     * Constructor for FifoControllerBase
+     * @param numFrames Size of the circular buffer in frames. Must be a power of 2.
+     */
+    FifoControllerBase(uint32_t totalFrames, uint32_t threshold);
+
+    virtual ~FifoControllerBase();
+
+    /**
+     * This may be negative if an unthrottled reader has read beyond the available data.
+     * @return number of valid frames available to read. Never read more than this.
+     */
+    int32_t getFullFramesAvailable();
+
+    /**
+     * The index in a circular buffer of the next frame to read.
+     */
+    uint32_t getReadIndex();
+
+    /**
+     * @param numFrames number of frames to advance the read index
+     */
+    void advanceReadIndex(int numFrames);
+
+    /**
+     * @return number of frames that can be written. Never write more than this.
+     */
+    int32_t getEmptyFramesAvailable();
+
+    /**
+     * The index in a circular buffer of the next frame to write.
+     */
+    uint32_t getWriteIndex();
+
+    /**
+     * @param numFrames number of frames to advance the write index
+     */
+    void advanceWriteIndex(uint32_t numFrames);
+
+    void setThreshold(uint32_t threshold);
+
+    uint32_t getThreshold() const { return mThreshold; }
+
+    uint32_t getFrameCapacity() const { return mTotalFrames; }
+
+    virtual uint64_t getReadCounter() = 0;  // TODO use int64_t?
+    virtual void setReadCounter(uint64_t n) = 0;
+    virtual uint64_t getWriteCounter() = 0;
+    virtual void setWriteCounter(uint64_t n) = 0;
+
+private:
+    uint32_t mTotalFrames;
+    uint32_t mThreshold;
+//    uint32_t mSmallMask;
+};
+
+
+#endif //NATIVEOBOE_FIFOCONTROLLERBASE_H
diff --git a/src/fifo/FifoControllerIndirect.cpp b/src/fifo/FifoControllerIndirect.cpp
new file mode 100644
index 0000000..f52d831
--- /dev/null
+++ b/src/fifo/FifoControllerIndirect.cpp
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+
+
+#include "FifoControllerIndirect.h"
+
+FifoControllerIndirect::FifoControllerIndirect(uint32_t numFrames,
+                                               uint32_t threshold,
+                                               int64_t * readCounterAddress,
+                                               int64_t * writeCounterAddress)
+        : FifoControllerBase(numFrames, threshold)
+        , mReadCounterAddress((std::atomic<uint64_t> *) readCounterAddress)
+        , mWriteCounterAddress((std::atomic<uint64_t> *) writeCounterAddress)
+{
+}
+
+FifoControllerIndirect::~FifoControllerIndirect() {
+}
diff --git a/src/fifo/FifoControllerIndirect.h b/src/fifo/FifoControllerIndirect.h
new file mode 100644
index 0000000..a8bc7fd
--- /dev/null
+++ b/src/fifo/FifoControllerIndirect.h
@@ -0,0 +1,56 @@
+/*
+ * 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 NATIVEOBOE_FIFOCONTROLLERINDIRECT_H
+#define NATIVEOBOE_FIFOCONTROLLERINDIRECT_H
+
+#include "FifoControllerBase.h"
+#include <atomic>
+
+/**
+ * A FifoControllerBase with counters external to the class.
+ */
+class FifoControllerIndirect : public FifoControllerBase {
+
+public:
+    FifoControllerIndirect(uint32_t bufferSize,
+                           uint32_t threshold,
+                           int64_t * readCounterAddress,
+                           int64_t * writeCounterAddress);
+    virtual ~FifoControllerIndirect();
+
+    // TODO review use of memory barriers, probably incorrect
+    virtual uint64_t getReadCounter() override {
+        return mReadCounterAddress->load(std::memory_order_acquire);
+    }
+    virtual void setReadCounter(uint64_t n) override {
+        mReadCounterAddress->store(n, std::memory_order_release);
+    }
+    virtual uint64_t getWriteCounter() override {
+        return mWriteCounterAddress->load(std::memory_order_acquire);
+    }
+    virtual void setWriteCounter(uint64_t n) override {
+        mWriteCounterAddress->store(n, std::memory_order_release);
+    }
+
+private:
+
+    std::atomic<uint64_t> * mReadCounterAddress;
+    std::atomic<uint64_t> * mWriteCounterAddress;
+
+};
+
+#endif //NATIVEOBOE_FIFOCONTROLLERINDIRECT_H
diff --git a/src/opensles/OboeStreamBuffered.cpp b/src/opensles/OboeStreamBuffered.cpp
new file mode 100644
index 0000000..c8e1e2e
--- /dev/null
+++ b/src/opensles/OboeStreamBuffered.cpp
@@ -0,0 +1,108 @@
+/*
+ * 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.
+ */
+
+#include "oboe/Oboe.h"
+
+#include "opensles/OboeStreamBuffered.h"
+#include "common/AudioClock.h"
+
+/*
+ * OboeStream with a FifoBuffer
+ */
+OboeStreamBuffered::OboeStreamBuffered(const OboeStreamBuilder &builder)
+        : OboeStream(builder)
+        , mFifoBuffer(NULL)
+        , mInternalCallback(NULL)
+{
+}
+
+oboe_result_t OboeStreamBuffered::open() {
+
+    oboe_result_t result = OboeStream::open();
+    if (result < 0) {
+        return result;
+    }
+
+    // If the caller does not provide a callback use our own internal
+    // callback that reads data from the FIFO.
+    if (getCallback() == NULL) {
+        LOGD("OboeStreamBuffered(): new FifoBuffer");
+        mFifoBuffer = new FifoBuffer(getBytesPerFrame(), 1024); // TODO size?
+        // Create a callback that reads from the FIFO
+        mInternalCallback = new AudioStreamBufferedCallback(this);
+        mStreamCallback = mInternalCallback;
+        LOGD("OboeStreamBuffered(): mInternalCallback = %p", mInternalCallback);
+    }
+    return OBOE_OK;
+}
+
+OboeStreamBuffered::~OboeStreamBuffered() {
+    delete mInternalCallback;
+}
+
+oboe_result_t OboeStreamBuffered::write(const void *buffer,
+                                         int32_t numFrames,
+                                         int64_t timeoutNanoseconds)
+{
+    oboe_result_t result = OBOE_OK;
+    uint8_t *source = (uint8_t *)buffer;
+    int32_t framesLeft = numFrames;
+    while(framesLeft > 0 && result >= 0) {
+        oboe_result_t result = mFifoBuffer->write(source, numFrames);
+        LOGD("OboeStreamBuffered::writeNow(): wrote %d/%d frames", result, numFrames);
+        if (result > 0) {
+            source += mFifoBuffer->convertFramesToBytes(result);
+            incrementFramesWritten(result);
+            framesLeft -= result;
+        }
+        if (framesLeft > 0 && result >= 0) {
+            int64_t wakeTimeNanos =  mFifoBuffer->getNextReadTime(getSampleRate());
+            // TODO use timeoutNanoseconds
+            AudioClock::sleepUntilNanoTime(wakeTimeNanos);
+        }
+    }
+    return result;
+}
+
+oboe_result_t OboeStreamBuffered::setBufferSizeInFrames(int32_t requestedFrames)
+{
+    if (mFifoBuffer != nullptr) {
+        if (requestedFrames > mFifoBuffer->getBufferCapacityInFrames()) {
+            requestedFrames = mFifoBuffer->getBufferCapacityInFrames();
+        }
+        mFifoBuffer->setThresholdFrames(requestedFrames);
+        return OBOE_OK;
+    } else {
+        return OBOE_ERROR_UNIMPLEMENTED;
+    }
+}
+
+
+int32_t OboeStreamBuffered::getBufferSizeInFrames() const {
+    if (mFifoBuffer != nullptr) {
+        return mFifoBuffer->getThresholdFrames();
+    } else {
+        return OBOE_ERROR_UNIMPLEMENTED;
+    }
+}
+
+int32_t OboeStreamBuffered::getBufferCapacityInFrames() const {
+    if (mFifoBuffer != nullptr) {
+        return mFifoBuffer->getBufferCapacityInFrames(); // Maybe set mBufferCapacity in constructor
+    } else {
+        return OBOE_ERROR_UNIMPLEMENTED;
+    }
+}
diff --git a/src/opensles/OboeStreamBuffered.h b/src/opensles/OboeStreamBuffered.h
new file mode 100644
index 0000000..f0a3768
--- /dev/null
+++ b/src/opensles/OboeStreamBuffered.h
@@ -0,0 +1,76 @@
+/*
+ * 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 OBOE_OBOE_STREAM_BUFFERED_H
+#define OBOE_OBOE_STREAM_BUFFERED_H
+
+#include "OboeDebug.h"
+#include "oboe/OboeStream.h"
+#include "oboe/OboeStreamCallback.h"
+#include "fifo/FifoBuffer.h"
+
+// A stream that contains a FIFO buffer.
+class OboeStreamBuffered : public OboeStream {
+public:
+
+    OboeStreamBuffered();
+    explicit OboeStreamBuffered(const OboeStreamBuilder &builder);
+    virtual ~OboeStreamBuffered();
+
+    oboe_result_t open() override;
+
+    oboe_result_t write(const void *buffer,
+                                int32_t numFrames,
+                                int64_t timeoutNanoseconds) override;
+
+    oboe_result_t setBufferSizeInFrames(int32_t requestedFrames) override;
+
+    int32_t getBufferSizeInFrames() const override;
+
+    int32_t getBufferCapacityInFrames() const override;
+
+protected:
+
+    class AudioStreamBufferedCallback : public OboeStreamCallback {
+    public:
+        AudioStreamBufferedCallback(OboeStreamBuffered *bufferedStream)
+                : mBufferedStream(bufferedStream) {
+        }
+
+        virtual ~AudioStreamBufferedCallback() {}
+
+        virtual oboe_result_t onAudioReady(
+                OboeStream *audioStream,
+                void *audioData,
+                int numFrames) {
+            int32_t framesRead = mBufferedStream->mFifoBuffer->readNow(audioData, numFrames);
+            //LOGD("AudioStreamBufferedCallback(): read %d / %d frames", framesRead, numFrames);
+            return (framesRead >= 0) ? OBOE_OK : OBOE_ERROR_INTERNAL;
+        }
+
+        virtual void onExit(oboe_result_t reason) {}
+    private:
+        OboeStreamBuffered *mBufferedStream;
+    };
+
+private:
+
+    FifoBuffer *mFifoBuffer;
+    AudioStreamBufferedCallback *mInternalCallback;
+};
+
+
+#endif //OBOE_OBOE_STREAM_BUFFERED_H
diff --git a/src/opensles/OboeStreamOpenSLES.cpp b/src/opensles/OboeStreamOpenSLES.cpp
new file mode 100644
index 0000000..634972f
--- /dev/null
+++ b/src/opensles/OboeStreamOpenSLES.cpp
@@ -0,0 +1,401 @@
+/*
+ * 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.
+ */
+#include <sys/types.h>
+#include <cassert>
+#include <android/log.h>
+
+
+#include <SLES/OpenSLES.h>
+#include <SLES/OpenSLES_Android.h>
+
+#include "common/OboeDebug.h"
+#include "oboe/OboeStreamBuilder.h"
+#include "oboe/OboeStream.h"
+#include "OboeStreamOpenSLES.h"
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+// TODO Query for a device specific value.
+#define DEFAULT_FRAMES_PER_CALLBACK   96
+
+/*
+ * OSLES Helpers
+ */
+static const char *errStrings[] = {
+        "SL_RESULT_SUCCESS",                  // 0
+        "SL_RESULT_PRECONDITIONS_VIOLATE",    // 1
+        "SL_RESULT_PARAMETER_INVALID",        // 2
+        "SL_RESULT_MEMORY_FAILURE",           // 3
+        "SL_RESULT_RESOURCE_ERROR",           // 4
+        "SL_RESULT_RESOURCE_LOST",            // 5
+        "SL_RESULT_IO_ERROR",                 // 6
+        "SL_RESULT_BUFFER_INSUFFICIENT",      // 7
+        "SL_RESULT_CONTENT_CORRUPTED",        // 8
+        "SL_RESULT_CONTENT_UNSUPPORTED",      // 9
+        "SL_RESULT_CONTENT_NOT_FOUND",        // 10
+        "SL_RESULT_PERMISSION_DENIED",        // 11
+        "SL_RESULT_FEATURE_UNSUPPORTED",      // 12
+        "SL_RESULT_INTERNAL_ERROR",           // 13
+        "SL_RESULT_UNKNOWN_ERROR",            // 14
+        "SL_RESULT_OPERATION_ABORTED",        // 15
+        "SL_RESULT_CONTROL_LOST"              // 16
+};
+
+const char *getSLErrStr(int code) {
+    return errStrings[code];
+}
+
+#define NUM_BURST_BUFFERS                     2
+
+// These will wind up in <SLES/OpenSLES_Android.h>
+#define SL_ANDROID_SPEAKER_QUAD (SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT \
+ | SL_SPEAKER_BACK_LEFT | SL_SPEAKER_BACK_RIGHT)
+
+#define SL_ANDROID_SPEAKER_5DOT1 (SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT \
+ | SL_SPEAKER_FRONT_CENTER  | SL_SPEAKER_LOW_FREQUENCY| SL_SPEAKER_BACK_LEFT \
+ | SL_SPEAKER_BACK_RIGHT)
+
+#define SL_ANDROID_SPEAKER_7DOT1 (SL_ANDROID_SPEAKER_5DOT1 | SL_SPEAKER_SIDE_LEFT \
+ |SL_SPEAKER_SIDE_RIGHT)
+
+int chanCountToChanMask(int chanCount) {
+    int channelMask = 0;
+
+    switch (chanCount) {
+        case  1:
+            channelMask = SL_SPEAKER_FRONT_CENTER;
+            break;
+
+        case  2:
+            channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
+            break;
+
+        case  4: // Quad
+            channelMask = SL_ANDROID_SPEAKER_QUAD;
+            break;
+
+        case  6: // 5.1
+            channelMask = SL_ANDROID_SPEAKER_5DOT1;
+            break;
+
+        case  8: // 7.1
+            channelMask = SL_ANDROID_SPEAKER_7DOT1;
+            break;
+    }
+    return channelMask;
+}
+
+static const char *TAG = "AAudioStreamOpenSLES";
+
+// engine interfaces
+static int32_t sOpenCount = 0;
+static SLObjectItf sEngineObject = 0;
+static SLEngineItf sEngineEngine;
+
+// output mix interfaces
+static SLObjectItf sOutputMixObject = 0;
+
+static void CloseSLEngine();
+
+SLresult OboeStreamOpenSLES::enqueueBuffer() {
+    // Ask the callback to fill the output buffer with data.
+    oboe_result_t result = fireCallback(mCallbackBuffer, mFramesPerCallback);
+    if (result != OBOE_OK) {
+        LOGE("Oboe callback returned %d", result);
+        return SL_RESULT_INTERNAL_ERROR;
+    } else {
+        // Pass the data to OpenSLES.
+        return (*bq_)->Enqueue(bq_, mCallbackBuffer, mBytesPerCallback);
+    }
+}
+
+// this callback handler is called every time a buffer finishes playing
+static void bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void *context) {
+    ((OboeStreamOpenSLES *) context)->enqueueBuffer();
+}
+
+static SLresult OpenSLEngine() {
+    SLresult result = SL_RESULT_SUCCESS;
+    if (sOpenCount > 0) {
+        ++sOpenCount;
+        return SL_RESULT_SUCCESS;
+    }
+
+    // create engine
+    result = slCreateEngine(&sEngineObject, 0, NULL, 0, NULL, NULL);
+    if (SL_RESULT_SUCCESS != result) {
+        LOGE("OpenSLEngine() - slCreateEngine() result:%s", getSLErrStr(result));
+        return result;
+    }
+
+    // realize the engine
+    result = (*sEngineObject)->Realize(sEngineObject, SL_BOOLEAN_FALSE);
+    if (SL_RESULT_SUCCESS != result) {
+        LOGE("OpenSLEngine() - Realize() engine result:%s", getSLErrStr(result));
+        goto error;
+    }
+
+    // get the engine interface, which is needed in order to create other objects
+    result = (*sEngineObject)->GetInterface(sEngineObject, SL_IID_ENGINE, &sEngineEngine);
+    if (SL_RESULT_SUCCESS != result) {
+        LOGE("OpenSLEngine() - GetInterface() engine result:%s", getSLErrStr(result));
+        goto error;
+    }
+
+    // get the output mixer
+    result = (*sEngineEngine)->CreateOutputMix(sEngineEngine, &sOutputMixObject, 0, 0, 0);
+    if (SL_RESULT_SUCCESS != result) {
+        LOGE("OpenSLEngine() - CreateOutputMix() result:%s", getSLErrStr(result));
+        goto error;
+    }
+
+    // realize the output mix
+    result = (*sOutputMixObject)->Realize(sOutputMixObject, SL_BOOLEAN_FALSE);
+    if (SL_RESULT_SUCCESS != result) {
+        LOGE("OpenSLEngine() - Realize() sOutputMixObject result:%s", getSLErrStr(result));
+        goto error;
+    }
+
+    ++sOpenCount;
+    return result;
+
+error:
+    CloseSLEngine();
+    return result;
+}
+
+static void CloseSLEngine() {
+//    __android_log_print(ANDROID_LOG_INFO, TAG, "CloseSLEngine()");
+    --sOpenCount;
+    if (sOpenCount > 0) {
+        return;
+    }
+    // destroy output mix object, and invalidate all associated interfaces
+    if (sOutputMixObject != NULL) {
+        (*sOutputMixObject)->Destroy(sOutputMixObject);
+        sOutputMixObject = NULL;
+    }
+
+    if (sEngineObject != NULL) {
+        (*sEngineObject)->Destroy(sEngineObject);
+        sEngineObject = NULL;
+        sEngineEngine = NULL;
+    }
+}
+
+OboeStreamOpenSLES::OboeStreamOpenSLES(const OboeStreamBuilder &builder)
+    : OboeStreamBuffered(builder) {
+    bqPlayerObject_ = NULL;
+    bq_ = NULL;
+    bqPlayerPlay_ = NULL;
+    OpenSLEngine();
+    LOGD("OboeStreamOpenSLES(): after OpenSLEngine()");
+}
+
+OboeStreamOpenSLES::~OboeStreamOpenSLES() {
+    CloseSLEngine();
+    delete[] mCallbackBuffer;
+}
+
+static SLuint32 ConvertFormatToRepresentation(oboe_audio_format_t format) {
+    switch(format) {
+        case OBOE_AUDIO_FORMAT_INVALID:
+        case OBOE_AUDIO_FORMAT_UNSPECIFIED:
+            return 0;
+        case OBOE_AUDIO_FORMAT_PCM_I16:
+            return SL_ANDROID_PCM_REPRESENTATION_SIGNED_INT;
+        case OBOE_AUDIO_FORMAT_PCM_FLOAT:
+            return SL_ANDROID_PCM_REPRESENTATION_FLOAT;
+    }
+    return 0;
+}
+
+oboe_result_t OboeStreamOpenSLES::open() {
+
+    SLresult result;
+
+    __android_log_print(ANDROID_LOG_INFO, TAG, "AudioPlayerOpenSLES::Open(chans:%d, rate:%d)",
+                        mChannelCount, mSampleRate);
+
+    oboe_result_t oboeResult = OboeStreamBuffered::open();
+    if (oboeResult < 0) {
+        return oboeResult;
+    }
+    // Convert to defaults if UNSPECIFIED
+    if (mSampleRate == OBOE_UNSPECIFIED) {
+        mSampleRate = 48000;
+    }
+    if (mChannelCount == OBOE_UNSPECIFIED) {
+        mChannelCount = 2;
+    }
+    if (mFramesPerCallback == OBOE_UNSPECIFIED) {
+        mFramesPerCallback = DEFAULT_FRAMES_PER_CALLBACK;
+    }
+
+    mBytesPerCallback = mFramesPerCallback * getBytesPerFrame();
+    mCallbackBuffer = new uint8_t[mBytesPerCallback];
+    LOGD("OboeStreamOpenSLES(): mFramesPerCallback = %d", mFramesPerCallback);
+    LOGD("OboeStreamOpenSLES(): mBytesPerCallback = %d", mBytesPerCallback);
+
+    SLuint32 bitsPerSample = getBytesPerSample() * 8;
+
+    // configure audio source
+    SLDataLocator_AndroidSimpleBufferQueue loc_bufq = {
+            SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE,    // locatorType
+            NUM_BURST_BUFFERS};                         // numBuffers
+
+    // SLuint32 chanMask = SL_SPEAKER_FRONT_LEFT|SL_SPEAKER_FRONT_RIGHT;
+    SLAndroidDataFormat_PCM_EX format_pcm = {
+            SL_ANDROID_DATAFORMAT_PCM_EX,       // formatType
+            (SLuint32) mChannelCount,           // numChannels
+            (SLuint32) (mSampleRate * 1000),    // milliSamplesPerSec
+            bitsPerSample,                      // bitsPerSample
+            bitsPerSample,                      // containerSize;
+            (SLuint32) chanCountToChanMask(mChannelCount), // channelMask
+            SL_BYTEORDER_LITTLEENDIAN, // TODO endianness? use native?
+            ConvertFormatToRepresentation(getFormat())}; // representation
+    SLDataSource audioSrc = {&loc_bufq, &format_pcm};
+
+    // configure audio sink
+    SLDataLocator_OutputMix loc_outmix = {SL_DATALOCATOR_OUTPUTMIX, sOutputMixObject};
+    SLDataSink audioSnk = {&loc_outmix, NULL};
+
+    const SLInterfaceID ids[] = {SL_IID_BUFFERQUEUE};
+    const SLboolean req[] = {SL_BOOLEAN_TRUE};
+
+    // The Player
+    result = (*sEngineEngine)->CreateAudioPlayer(sEngineEngine, &bqPlayerObject_, &audioSrc,
+                                                 &audioSnk,
+                                                sizeof(ids) / sizeof(ids[0]), ids, req);
+    LOGD("CreateAudioPlayer() result:%s", getSLErrStr(result));
+    assert(SL_RESULT_SUCCESS == result);
+
+    result = (*bqPlayerObject_)->Realize(bqPlayerObject_, SL_BOOLEAN_FALSE);
+    LOGD("Realize player object result:%s", getSLErrStr(result));
+    assert(SL_RESULT_SUCCESS == result);
+
+    result = (*bqPlayerObject_)->GetInterface(bqPlayerObject_, SL_IID_PLAY, &bqPlayerPlay_);
+    LOGD("get player interface result:%s", getSLErrStr(result));
+    assert(SL_RESULT_SUCCESS == result);
+
+    // The BufferQueue
+    result = (*bqPlayerObject_)->GetInterface(bqPlayerObject_, SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
+                                              &bq_);
+    LOGD("get bufferqueue interface:%p result:%s", bq_, getSLErrStr(result));
+    assert(SL_RESULT_SUCCESS == result);
+
+    // The register BufferQueue callback
+    result = (*bq_)->RegisterCallback(bq_, bqPlayerCallback, this);
+    LOGD("register callback result:%s", getSLErrStr(result));
+    assert(SL_RESULT_SUCCESS == result);
+
+    mSharingMode = OBOE_SHARING_MODE_SHARED;
+
+    return OBOE_OK;
+}
+
+oboe_result_t OboeStreamOpenSLES::close() {
+//    __android_log_write(ANDROID_LOG_INFO, TAG, "OboeStreamOpenSLES()");
+    // TODO make sure callback is no longer being called
+    if (bqPlayerObject_ != NULL) {
+        (*bqPlayerObject_)->Destroy(bqPlayerObject_);
+        bqPlayerObject_ = NULL;
+
+        // invalidate any interfaces
+        bqPlayerPlay_ = NULL;
+        bq_ = NULL;
+    }
+    return OBOE_OK;
+}
+
+oboe_result_t OboeStreamOpenSLES::setPlayState(SLuint32 newState)
+{
+    oboe_result_t result = OBOE_OK;
+    LOGD("OboeStreamOpenSLES(): setPlayState()");
+    if (bqPlayerPlay_ == NULL) {
+        return OBOE_ERROR_INVALID_STATE;
+    }
+    SLresult slResult = (*bqPlayerPlay_)->SetPlayState(bqPlayerPlay_, newState);
+    if(SL_RESULT_SUCCESS != slResult) {
+        LOGD("OboeStreamOpenSLES(): setPlayState() returned %s", getSLErrStr(result));
+        result = OBOE_ERROR_INVALID_STATE; // TODO review
+    } else {
+        setState(OBOE_STREAM_STATE_PAUSING);
+    }
+    return result;
+}
+
+oboe_result_t OboeStreamOpenSLES::requestStart()
+{
+    LOGD("OboeStreamOpenSLES(): requestStart()");
+    oboe_result_t result = setPlayState(SL_PLAYSTATE_PLAYING);
+    if(result != OBOE_OK) {
+        result = OBOE_ERROR_INVALID_STATE; // TODO review
+    } else {
+        enqueueBuffer();
+        setState(OBOE_STREAM_STATE_STARTING);
+    }
+    return result;
+}
+
+
+oboe_result_t OboeStreamOpenSLES::requestPause() {
+    LOGD("OboeStreamOpenSLES(): requestPause()");
+    oboe_result_t result = setPlayState(SL_PLAYSTATE_PAUSED);
+    if(result != OBOE_OK) {
+        result = OBOE_ERROR_INVALID_STATE; // TODO review
+    } else {
+        setState(OBOE_STREAM_STATE_PAUSING);
+    }
+    return result;
+}
+
+oboe_result_t OboeStreamOpenSLES::requestFlush() {
+    LOGD("OboeStreamOpenSLES(): requestFlush()");
+    if (bqPlayerPlay_ == NULL) {
+        return OBOE_ERROR_INVALID_STATE;
+    }
+    return OBOE_ERROR_UNIMPLEMENTED; // TODO
+}
+
+oboe_result_t OboeStreamOpenSLES::requestStop()
+{
+    LOGD("OboeStreamOpenSLES(): requestStop()");
+    oboe_result_t result = setPlayState(SL_PLAYSTATE_STOPPED);
+    if(result != OBOE_OK) {
+        result = OBOE_ERROR_INVALID_STATE; // TODO review
+    } else {
+        setState(OBOE_STREAM_STATE_STOPPING);
+    }
+    return result;
+}
+
+oboe_result_t OboeStreamOpenSLES::waitForStateChange(oboe_stream_state_t currentState,
+                                                      oboe_stream_state_t *nextState,
+                                                      int64_t timeoutNanoseconds)
+{
+    LOGD("OboeStreamOpenSLES(): waitForStateChange()");
+    if (bqPlayerPlay_ == NULL) {
+        return OBOE_ERROR_INVALID_STATE;
+    }
+    return OBOE_ERROR_UNIMPLEMENTED; // TODO
+}
+
+int32_t OboeStreamOpenSLES::getFramesPerBurst() {
+    return mFramesPerCallback; // TODO review, can we query this?
+}
diff --git a/src/opensles/OboeStreamOpenSLES.h b/src/opensles/OboeStreamOpenSLES.h
new file mode 100644
index 0000000..200e3c2
--- /dev/null
+++ b/src/opensles/OboeStreamOpenSLES.h
@@ -0,0 +1,90 @@
+/*
+ * 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 AUDIO_STREAM_OPENSL_ES_H_
+#define AUDIO_STREAM_OPENSL_ES_H_
+
+#include <SLES/OpenSLES.h>
+#include <SLES/OpenSLES_Android.h>
+
+#include "oboe/Oboe.h"
+#include "OboeStreamBuffered.h"
+
+/**
+ * A stream that wraps OpenSL ES.
+ *
+ * Do not instantiate this class directly.
+ * Use an OboeStreamBuilder to create one.
+ */
+//
+class OboeStreamOpenSLES : public OboeStreamBuffered {
+public:
+
+    OboeStreamOpenSLES();
+    explicit OboeStreamOpenSLES(const OboeStreamBuilder &builder);
+
+    virtual ~OboeStreamOpenSLES();
+
+    oboe_result_t open() override;
+    oboe_result_t close() override;
+
+    oboe_result_t requestStart() override;
+    oboe_result_t requestPause() override;
+    oboe_result_t requestFlush() override;
+    oboe_result_t requestStop() override;
+
+    // public, but don't call directly (called by the OSLES callback)
+    SLresult enqueueBuffer();
+
+    oboe_result_t waitForStateChange(oboe_stream_state_t currentState,
+                                             oboe_stream_state_t *nextState,
+                                             int64_t timeoutNanoseconds) override;
+
+    /**
+     * Query the current state, eg. OBOE_STREAM_STATE_PAUSING
+     *
+     * @return state or a negative error.
+     */
+    oboe_stream_state_t getState() override { return mState; }
+
+    int32_t getFramesPerBurst() override;
+
+protected:
+
+    /**
+     * Internal use only.
+     * Use this instead of directly setting the internal state variable.
+     */
+    void setState(oboe_stream_state_t state) {
+        mState = state;
+    }
+
+private:
+
+    oboe_result_t setPlayState(SLuint32 newState);
+
+    uint8_t   *mCallbackBuffer;
+    int32_t    mBytesPerCallback;
+    oboe_stream_state_t mState = OBOE_STREAM_STATE_UNINITIALIZED;
+
+    // OpenSLES stuff
+    SLObjectItf bqPlayerObject_;
+    SLPlayItf   bqPlayerPlay_;
+    SLAndroidSimpleBufferQueueItf bq_;
+};
+
+
+#endif // AUDIO_STREAM_OPENSL_ES_H_