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_