浏览代码

Manually extract Oboe AAR for now.

This is not ideal, but looks like there's issue when using prebuilt, causing CI build failure. Reported at google/oboe#1554
Miku AuahDark 3 年之前
父节点
当前提交
190938943f
共有 25 个文件被更改,包括 2726 次插入1 次删除
  1. 0 1
      app/build.gradle
  2. 7 0
      app/src/main/cpp/CMakeLists.txt
  3. 二进制
      app/src/main/cpp/oboe/include/.DS_Store
  4. 567 0
      app/src/main/cpp/oboe/include/oboe/AudioStream.h
  5. 263 0
      app/src/main/cpp/oboe/include/oboe/AudioStreamBase.h
  6. 520 0
      app/src/main/cpp/oboe/include/oboe/AudioStreamBuilder.h
  7. 189 0
      app/src/main/cpp/oboe/include/oboe/AudioStreamCallback.h
  8. 545 0
      app/src/main/cpp/oboe/include/oboe/Definitions.h
  9. 150 0
      app/src/main/cpp/oboe/include/oboe/LatencyTuner.h
  10. 37 0
      app/src/main/cpp/oboe/include/oboe/Oboe.h
  11. 155 0
      app/src/main/cpp/oboe/include/oboe/ResultWithValue.h
  12. 75 0
      app/src/main/cpp/oboe/include/oboe/StabilizedCallback.h
  13. 87 0
      app/src/main/cpp/oboe/include/oboe/Utilities.h
  14. 92 0
      app/src/main/cpp/oboe/include/oboe/Version.h
  15. 1 0
      app/src/main/cpp/oboe/libs/android.arm64-v8a/abi.json
  16. 二进制
      app/src/main/cpp/oboe/libs/android.arm64-v8a/liboboe.so
  17. 1 0
      app/src/main/cpp/oboe/libs/android.armeabi-v7a/abi.json
  18. 二进制
      app/src/main/cpp/oboe/libs/android.armeabi-v7a/liboboe.so
  19. 1 0
      app/src/main/cpp/oboe/libs/android.x86/abi.json
  20. 二进制
      app/src/main/cpp/oboe/libs/android.x86/liboboe.so
  21. 1 0
      app/src/main/cpp/oboe/libs/android.x86_64/abi.json
  22. 二进制
      app/src/main/cpp/oboe/libs/android.x86_64/liboboe.so
  23. 8 0
      app/src/main/cpp/oboe/module.json
  24. 13 0
      app/src/main/cpp/oboe/oboeConfig.cmake
  25. 14 0
      app/src/main/cpp/oboe/oboeConfigVersion.cmake

+ 0 - 1
app/build.gradle

@@ -101,5 +101,4 @@ dependencies {
     implementation 'androidx.navigation:navigation-ui:2.4.2'
     implementation 'androidx.recyclerview:recyclerview:1.2.1'
     implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
-    implementation 'com.google.oboe:oboe:1.6.1'
 }

+ 7 - 0
app/src/main/cpp/CMakeLists.txt

@@ -2,6 +2,13 @@ cmake_minimum_required(VERSION 3.21)
 
 project(love-android LANGUAGES C CXX)
 
+# Oboe
+set(Oboe_DIR "${CMAKE_CURRENT_SOURCE_DIR}/oboe")
+
+# Set LOVE
 set(MEGA_LOVE "${CMAKE_CURRENT_SOURCE_DIR}/love")
 
+# Finally, load megasource that will handle everything.
 add_subdirectory(megasource)
+
+# TODO: Third-party libraries.

二进制
app/src/main/cpp/oboe/include/.DS_Store


+ 567 - 0
app/src/main/cpp/oboe/include/oboe/AudioStream.h

@@ -0,0 +1,567 @@
+/*
+ * 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_STREAM_H_
+#define OBOE_STREAM_H_
+
+#include <atomic>
+#include <cstdint>
+#include <ctime>
+#include <mutex>
+#include "oboe/Definitions.h"
+#include "oboe/ResultWithValue.h"
+#include "oboe/AudioStreamBuilder.h"
+#include "oboe/AudioStreamBase.h"
+
+/** WARNING - UNDER CONSTRUCTION - THIS API WILL CHANGE. */
+
+namespace oboe {
+
+/**
+ * The default number of nanoseconds to wait for when performing state change operations on the
+ * stream, such as `start` and `stop`.
+ *
+ * @see oboe::AudioStream::start
+ */
+constexpr int64_t kDefaultTimeoutNanos = (2000 * kNanosPerMillisecond);
+
+/**
+ * Base class for Oboe C++ audio stream.
+ */
+class AudioStream : public AudioStreamBase {
+    friend class AudioStreamBuilder; // allow access to setWeakThis() and lockWeakThis()
+public:
+
+    AudioStream() {}
+
+    /**
+     * Construct an `AudioStream` using the given `AudioStreamBuilder`
+     *
+     * @param builder containing all the stream's attributes
+     */
+    explicit AudioStream(const AudioStreamBuilder &builder);
+
+    virtual ~AudioStream() = default;
+
+    /**
+     * Open a stream based on the current settings.
+     *
+     * Note that we do not recommend re-opening a stream that has been closed.
+     * TODO Should we prevent re-opening?
+     *
+     * @return
+     */
+    virtual Result open() {
+        return Result::OK; // Called by subclasses. Might do more in the future.
+    }
+
+    /**
+     * Close the stream and deallocate any resources from the open() call.
+     */
+    virtual Result close();
+
+    /**
+     * Start the stream. This will block until the stream has been started, an error occurs
+     * or `timeoutNanoseconds` has been reached.
+     */
+    virtual Result start(int64_t timeoutNanoseconds = kDefaultTimeoutNanos);
+
+    /**
+     * Pause the stream. This will block until the stream has been paused, an error occurs
+     * or `timeoutNanoseconds` has been reached.
+     */
+    virtual Result pause(int64_t timeoutNanoseconds = kDefaultTimeoutNanos);
+
+    /**
+     * Flush the stream. This will block until the stream has been flushed, an error occurs
+     * or `timeoutNanoseconds` has been reached.
+     */
+    virtual Result flush(int64_t timeoutNanoseconds = kDefaultTimeoutNanos);
+
+    /**
+     * Stop the stream. This will block until the stream has been stopped, an error occurs
+     * or `timeoutNanoseconds` has been reached.
+     */
+    virtual Result stop(int64_t timeoutNanoseconds = kDefaultTimeoutNanos);
+
+    /* Asynchronous requests.
+     * Use waitForStateChange() if you need to wait for completion.
+     */
+
+    /**
+     * Start the stream asynchronously. Returns immediately (does not block). Equivalent to calling
+     * `start(0)`.
+     */
+    virtual Result requestStart() = 0;
+
+    /**
+     * Pause the stream asynchronously. Returns immediately (does not block). Equivalent to calling
+     * `pause(0)`.
+     */
+    virtual Result requestPause() = 0;
+
+    /**
+     * Flush the stream asynchronously. Returns immediately (does not block). Equivalent to calling
+     * `flush(0)`.
+     */
+    virtual Result requestFlush() = 0;
+
+    /**
+     * Stop the stream asynchronously. Returns immediately (does not block). Equivalent to calling
+     * `stop(0)`.
+     */
+    virtual Result requestStop() = 0;
+
+    /**
+     * Query the current state, eg. StreamState::Pausing
+     *
+     * @return state or a negative error.
+     */
+    virtual StreamState getState() = 0;
+
+    /**
+     * Wait until the stream's current state no longer matches the input state.
+     * The input state is passed to avoid race conditions caused by the state
+     * changing between calls.
+     *
+     * Note that generally applications do not need to call this. It is considered
+     * an advanced technique and is mostly used for testing.
+     *
+     * <pre><code>
+     * int64_t timeoutNanos = 500 * kNanosPerMillisecond; // arbitrary 1/2 second
+     * StreamState currentState = stream->getState();
+     * StreamState nextState = StreamState::Unknown;
+     * while (result == Result::OK && currentState != StreamState::Paused) {
+     *     result = stream->waitForStateChange(
+     *                                   currentState, &nextState, timeoutNanos);
+     *     currentState = nextState;
+     * }
+     * </code></pre>
+     *
+     * If the state does not change within the timeout period then it will
+     * return ErrorTimeout. This is true even if timeoutNanoseconds is zero.
+     *
+     * @param inputState The state we want to change away from.
+     * @param nextState Pointer to a variable that will be set to the new state.
+     * @param timeoutNanoseconds The maximum time to wait in nanoseconds.
+     * @return Result::OK or a Result::Error.
+     */
+    virtual Result waitForStateChange(StreamState inputState,
+                                          StreamState *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 the resulting buffer size in frames (obtained using value()) or an error (obtained
+    * using error())
+    */
+    virtual ResultWithValue<int32_t> setBufferSizeInFrames(int32_t /* requestedFrames  */) {
+        return Result::ErrorUnimplemented;
+    }
+
+    /**
+     * 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 a result which is either Result::OK with the xRun count as the value, or a
+     * Result::Error* code
+     */
+    virtual ResultWithValue<int32_t> getXRunCount() {
+        return ResultWithValue<int32_t>(Result::ErrorUnimplemented);
+    }
+
+    /**
+     * @return true if XRun counts are supported on the stream
+     */
+    virtual bool isXRunCountSupported() const = 0;
+
+    /**
+     * Query the number of frames that are read or written by the endpoint at one time.
+     *
+     * @return burst size
+     */
+    int32_t getFramesPerBurst() const {
+        return mFramesPerBurst;
+    }
+
+    /**
+     * Get the number of bytes in each audio frame. This is calculated using the channel count
+     * and the sample format. For example, a 2 channel floating point stream will have
+     * 2 * 4 = 8 bytes per frame.
+     *
+     * @return number of bytes in each audio frame.
+     */
+    int32_t getBytesPerFrame() const { return mChannelCount * getBytesPerSample(); }
+
+    /**
+     * Get the number of bytes per sample. This is calculated using the sample format. For example,
+     * a stream using 16-bit integer samples will have 2 bytes per sample.
+     *
+     * @return the number of bytes per sample.
+     */
+    int32_t getBytesPerSample() const;
+
+    /**
+     * The number of audio frames written into the stream.
+     * This monotonic counter will never get reset.
+     *
+     * @return the number of frames written so far
+     */
+    virtual int64_t getFramesWritten();
+
+    /**
+     * The number of audio frames read from the stream.
+     * This monotonic counter will never get reset.
+     *
+     * @return the number of frames read so far
+     */
+    virtual int64_t getFramesRead();
+
+    /**
+     * Calculate the latency of a stream based on getTimestamp().
+     *
+     * Output latency is the time it takes for a given frame to travel from the
+     * app to some type of digital-to-analog converter. If the DAC is external, for example
+     * in a USB interface or a TV connected by HDMI, then there may be additional latency
+     * that the Android device is unaware of.
+     *
+     * Input latency is the time it takes to a given frame to travel from an analog-to-digital
+     * converter (ADC) to the app.
+     *
+     * Note that the latency of an OUTPUT stream will increase abruptly when you write data to it
+     * and then decrease slowly over time as the data is consumed.
+     *
+     * The latency of an INPUT stream will decrease abruptly when you read data from it
+     * and then increase slowly over time as more data arrives.
+     *
+     * The latency of an OUTPUT stream is generally higher than the INPUT latency
+     * because an app generally tries to keep the OUTPUT buffer full and the INPUT buffer empty.
+     *
+     * @return a ResultWithValue which has a result of Result::OK and a value containing the latency
+     * in milliseconds, or a result of Result::Error*.
+     */
+    virtual ResultWithValue<double> calculateLatencyMillis() {
+        return ResultWithValue<double>(Result::ErrorUnimplemented);
+    }
+
+    /**
+     * Get the estimated time that the frame at `framePosition` entered or left the audio processing
+     * pipeline.
+     *
+     * This can be used to coordinate events and interactions with the external environment, and to
+     * estimate the latency of an audio stream. An example of usage can be found in the hello-oboe
+     * sample (search for "calculateCurrentOutputLatencyMillis").
+     *
+     * The time is based on the implementation's best effort, using whatever knowledge is available
+     * to the system, but cannot account for any delay unknown to the implementation.
+     *
+     * @deprecated since 1.0, use AudioStream::getTimestamp(clockid_t clockId) instead, which
+     * returns ResultWithValue
+     * @param clockId the type of clock to use e.g. CLOCK_MONOTONIC
+     * @param framePosition the frame number to query
+     * @param timeNanoseconds an output parameter which will contain the presentation timestamp
+     */
+    virtual Result getTimestamp(clockid_t /* clockId  */,
+                                int64_t* /* framePosition */,
+                                int64_t* /* timeNanoseconds */) {
+        return Result::ErrorUnimplemented;
+    }
+
+    /**
+     * Get the estimated time that the frame at `framePosition` entered or left the audio processing
+     * pipeline.
+     *
+     * This can be used to coordinate events and interactions with the external environment, and to
+     * estimate the latency of an audio stream. An example of usage can be found in the hello-oboe
+     * sample (search for "calculateCurrentOutputLatencyMillis").
+     *
+     * The time is based on the implementation's best effort, using whatever knowledge is available
+     * to the system, but cannot account for any delay unknown to the implementation.
+     *
+     * @param clockId the type of clock to use e.g. CLOCK_MONOTONIC
+     * @return a FrameTimestamp containing the position and time at which a particular audio frame
+     * entered or left the audio processing pipeline, or an error if the operation failed.
+     */
+    virtual ResultWithValue<FrameTimestamp> getTimestamp(clockid_t /* clockId */);
+
+    // ============== I/O ===========================
+    /**
+     * Write data from the supplied buffer into the stream. This method will block until the write
+     * is complete or it runs out of time.
+     *
+     * If `timeoutNanoseconds` is zero then this call will not wait.
+     *
+     * @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 a ResultWithValue which has a result of Result::OK and a value containing the number
+     * of frames actually written, or result of Result::Error*.
+     */
+    virtual ResultWithValue<int32_t> write(const void* /* buffer */,
+                             int32_t /* numFrames */,
+                             int64_t /* timeoutNanoseconds */ ) {
+        return ResultWithValue<int32_t>(Result::ErrorUnimplemented);
+    }
+
+    /**
+     * Read data into the supplied buffer from the stream. This method will block until the read
+     * is complete or it runs out of time.
+     *
+     * If `timeoutNanoseconds` is zero then this call will not wait.
+     *
+     * @param buffer The address of the first sample.
+     * @param numFrames Number of frames to read. Only complete frames will be read.
+     * @param timeoutNanoseconds Maximum number of nanoseconds to wait for completion.
+     * @return a ResultWithValue which has a result of Result::OK and a value containing the number
+     * of frames actually read, or result of Result::Error*.
+     */
+    virtual ResultWithValue<int32_t> read(void* /* buffer */,
+                            int32_t /* numFrames */,
+                            int64_t /* timeoutNanoseconds */) {
+        return ResultWithValue<int32_t>(Result::ErrorUnimplemented);
+    }
+
+    /**
+     * Get the underlying audio API which the stream uses.
+     *
+     * @return the API that this stream uses.
+     */
+    virtual AudioApi getAudioApi() const = 0;
+
+    /**
+     * Returns true if the underlying audio API is AAudio.
+     *
+     * @return true if this stream is implemented using the AAudio API.
+     */
+    bool usesAAudio() const {
+        return getAudioApi() == AudioApi::AAudio;
+    }
+
+    /**
+     * Only for debugging. Do not use in production.
+     * If you need to call this method something is wrong.
+     * If you think you need it for production then please let us know
+     * so we can modify Oboe so that you don't need this.
+     *
+     * @return nullptr or a pointer to a stream from the system API
+     */
+    virtual void *getUnderlyingStream() const {
+        return nullptr;
+    }
+
+    /**
+     * Update mFramesWritten.
+     * For internal use only.
+     */
+    virtual void updateFramesWritten() = 0;
+
+    /**
+     * Update mFramesRead.
+     * For internal use only.
+     */
+    virtual void updateFramesRead() = 0;
+
+    /*
+     * Swap old callback for new callback.
+     * This not atomic.
+     * This should only be used internally.
+     * @param dataCallback
+     * @return previous dataCallback
+     */
+    AudioStreamDataCallback *swapDataCallback(AudioStreamDataCallback *dataCallback) {
+        AudioStreamDataCallback *previousCallback = mDataCallback;
+        mDataCallback = dataCallback;
+        return previousCallback;
+    }
+
+    /*
+     * Swap old callback for new callback.
+     * This not atomic.
+     * This should only be used internally.
+     * @param errorCallback
+     * @return previous errorCallback
+     */
+    AudioStreamErrorCallback *swapErrorCallback(AudioStreamErrorCallback *errorCallback) {
+        AudioStreamErrorCallback *previousCallback = mErrorCallback;
+        mErrorCallback = errorCallback;
+        return previousCallback;
+    }
+
+    /**
+     * @return number of frames of data currently in the buffer
+     */
+    ResultWithValue<int32_t> getAvailableFrames();
+
+    /**
+     * Wait until the stream has a minimum amount of data available in its buffer.
+     * This can be used with an EXCLUSIVE MMAP input stream to avoid reading data too close to
+     * the DSP write position, which may cause glitches.
+     *
+     * @param numFrames minimum frames available
+     * @param timeoutNanoseconds
+     * @return number of frames available, ErrorTimeout
+     */
+    ResultWithValue<int32_t> waitForAvailableFrames(int32_t numFrames,
+                                                    int64_t timeoutNanoseconds);
+
+    /**
+     * @return last result passed from an error callback
+     */
+    virtual oboe::Result getLastErrorCallbackResult() const {
+        return mErrorCallbackResult;
+    }
+
+protected:
+
+    /**
+     * This is used to detect more than one error callback from a stream.
+     * These were bugs in some versions of Android that caused multiple error callbacks.
+     * Internal bug b/63087953
+     *
+     * Calling this sets an atomic<bool> true and returns the previous value.
+     *
+     * @return false on first call, true on subsequent calls
+     */
+    bool wasErrorCallbackCalled() {
+        return mErrorCallbackCalled.exchange(true);
+    }
+
+    /**
+     * Wait for a transition from one state to another.
+     * @return OK if the endingState was observed, or ErrorUnexpectedState
+     *   if any state that was not the startingState or endingState was observed
+     *   or ErrorTimeout.
+     */
+    virtual Result waitForStateTransition(StreamState startingState,
+                                          StreamState endingState,
+                                          int64_t timeoutNanoseconds);
+
+    /**
+     * Override this to provide a default for when the application did not specify a callback.
+     *
+     * @param audioData
+     * @param numFrames
+     * @return result
+     */
+    virtual DataCallbackResult onDefaultCallback(void* /* audioData  */, int /* numFrames */) {
+        return DataCallbackResult::Stop;
+    }
+
+    /**
+     * Override this to provide your own behaviour for the audio callback
+     *
+     * @param audioData container array which audio frames will be written into or read from
+     * @param numFrames number of frames which were read/written
+     * @return the result of the callback: stop or continue
+     *
+     */
+    DataCallbackResult fireDataCallback(void *audioData, int numFrames);
+
+    /**
+     * @return true if callbacks may be called
+     */
+    bool isDataCallbackEnabled() {
+        return mDataCallbackEnabled;
+    }
+
+    /**
+     * This can be set false internally to prevent callbacks
+     * after DataCallbackResult::Stop has been returned.
+     */
+    void setDataCallbackEnabled(bool enabled) {
+        mDataCallbackEnabled = enabled;
+    }
+
+    /*
+     * Set a weak_ptr to this stream from the shared_ptr so that we can
+     * later use a shared_ptr in the error callback.
+     */
+    void setWeakThis(std::shared_ptr<oboe::AudioStream> &sharedStream) {
+        mWeakThis = sharedStream;
+    }
+
+    /*
+     * Make a shared_ptr that will prevent this stream from being deleted.
+     */
+    std::shared_ptr<oboe::AudioStream> lockWeakThis() {
+        return mWeakThis.lock();
+    }
+
+    std::weak_ptr<AudioStream> mWeakThis; // weak pointer to this object
+
+    /**
+     * Number of frames which have been written into the stream
+     *
+     * This is signed integer to match the counters in AAudio.
+     * At audio rates, the counter will overflow in about six million years.
+     */
+    std::atomic<int64_t> mFramesWritten{};
+
+    /**
+     * Number of frames which have been read from the stream.
+     *
+     * This is signed integer to match the counters in AAudio.
+     * At audio rates, the counter will overflow in about six million years.
+     */
+    std::atomic<int64_t> mFramesRead{};
+
+    std::mutex           mLock; // for synchronizing start/stop/close
+
+    oboe::Result         mErrorCallbackResult = oboe::Result::OK;
+
+    /**
+     * Number of frames which will be copied to/from the audio device in a single read/write
+     * operation
+     */
+    int32_t              mFramesPerBurst = kUnspecified;
+
+private:
+
+    // Log the scheduler if it changes.
+    void                 checkScheduler();
+    int                  mPreviousScheduler = -1;
+
+    std::atomic<bool>    mDataCallbackEnabled{false};
+    std::atomic<bool>    mErrorCallbackCalled{false};
+};
+
+/**
+ * This struct is a stateless functor which closes an AudioStream prior to its deletion.
+ * This means it can be used to safely delete a smart pointer referring to an open stream.
+ */
+    struct StreamDeleterFunctor {
+        void operator()(AudioStream  *audioStream) {
+            if (audioStream) {
+                audioStream->close();
+            }
+            delete audioStream;
+        }
+    };
+} // namespace oboe
+
+#endif /* OBOE_STREAM_H_ */

+ 263 - 0
app/src/main/cpp/oboe/include/oboe/AudioStreamBase.h

@@ -0,0 +1,263 @@
+/*
+ * 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_STREAM_BASE_H_
+#define OBOE_STREAM_BASE_H_
+
+#include <memory>
+#include <string>
+#include "oboe/AudioStreamCallback.h"
+#include "oboe/Definitions.h"
+
+namespace oboe {
+
+/**
+ * Base class containing parameters for audio streams and builders.
+ **/
+class AudioStreamBase {
+
+public:
+
+    AudioStreamBase() {}
+
+    virtual ~AudioStreamBase() = default;
+
+    // This class only contains primitives so we can use default constructor and copy methods.
+
+    /**
+     * Default copy constructor
+     */
+    AudioStreamBase(const AudioStreamBase&) = default;
+
+    /**
+     * Default assignment operator
+     */
+    AudioStreamBase& operator=(const AudioStreamBase&) = default;
+
+    /**
+     * @return number of channels, for example 2 for stereo, or kUnspecified
+     */
+    int32_t getChannelCount() const { return mChannelCount; }
+
+    /**
+     * @return Direction::Input or Direction::Output
+     */
+    Direction getDirection() const { return mDirection; }
+
+    /**
+     * @return sample rate for the stream or kUnspecified
+     */
+    int32_t getSampleRate() const { return mSampleRate; }
+
+    /**
+     * @deprecated use `getFramesPerDataCallback` instead.
+     */
+    int32_t getFramesPerCallback() const { return getFramesPerDataCallback(); }
+
+    /**
+     * @return the number of frames in each data callback or kUnspecified.
+     */
+    int32_t getFramesPerDataCallback() const { return mFramesPerCallback; }
+
+    /**
+     * @return the audio sample format (e.g. Float or I16)
+     */
+    AudioFormat getFormat() const { return mFormat; }
+
+    /**
+     * Query the maximum number of frames that can be filled without blocking.
+     * If the stream has been closed the last known value will be returned.
+     *
+     * @return buffer size
+     */
+    virtual int32_t getBufferSizeInFrames() { return mBufferSizeInFrames; }
+
+    /**
+     * @return capacityInFrames or kUnspecified
+     */
+    virtual int32_t getBufferCapacityInFrames() const { return mBufferCapacityInFrames; }
+
+    /**
+     * @return the sharing mode of the stream.
+     */
+    SharingMode getSharingMode() const { return mSharingMode; }
+
+    /**
+     * @return the performance mode of the stream.
+     */
+    PerformanceMode getPerformanceMode() const { return mPerformanceMode; }
+
+    /**
+     * @return the device ID of the stream.
+     */
+    int32_t getDeviceId() const { return mDeviceId; }
+
+    /**
+     * For internal use only.
+     * @return the data callback object for this stream, if set.
+     */
+    AudioStreamDataCallback *getDataCallback() const {
+        return mDataCallback;
+    }
+
+    /**
+     * For internal use only.
+     * @return the error callback object for this stream, if set.
+     */
+    AudioStreamErrorCallback *getErrorCallback() const {
+        return mErrorCallback;
+    }
+
+    /**
+     * @return true if a data callback was set for this stream
+     */
+    bool isDataCallbackSpecified() const {
+        return mDataCallback != nullptr;
+    }
+
+    /**
+     * Note that if the app does not set an error callback then a
+     * default one may be provided.
+     * @return true if an error callback was set for this stream
+     */
+    bool isErrorCallbackSpecified() const {
+        return mErrorCallback != nullptr;
+    }
+
+    /**
+     * @return the usage for this stream.
+     */
+    Usage getUsage() const { return mUsage; }
+
+    /**
+     * @return the stream's content type.
+     */
+    ContentType getContentType() const { return mContentType; }
+
+    /**
+     * @return the stream's input preset.
+     */
+    InputPreset getInputPreset() const { return mInputPreset; }
+
+    /**
+     * @return the stream's session ID allocation strategy (None or Allocate).
+     */
+    SessionId getSessionId() const { return mSessionId; }
+
+    /**
+     * @return true if Oboe can convert channel counts to achieve optimal results.
+     */
+    bool isChannelConversionAllowed() const {
+        return mChannelConversionAllowed;
+    }
+
+    /**
+     * @return true if  Oboe can convert data formats to achieve optimal results.
+     */
+    bool  isFormatConversionAllowed() const {
+        return mFormatConversionAllowed;
+    }
+
+    /**
+     * @return whether and how Oboe can convert sample rates to achieve optimal results.
+     */
+    SampleRateConversionQuality getSampleRateConversionQuality() const {
+        return mSampleRateConversionQuality;
+    }
+
+protected:
+    /** The callback which will be fired when new data is ready to be read/written. **/
+    AudioStreamDataCallback        *mDataCallback = nullptr;
+
+    /** The callback which will be fired when an error or a disconnect occurs. **/
+    AudioStreamErrorCallback       *mErrorCallback = nullptr;
+
+    /** Number of audio frames which will be requested in each callback */
+    int32_t                         mFramesPerCallback = kUnspecified;
+    /** Stream channel count */
+    int32_t                         mChannelCount = kUnspecified;
+    /** Stream sample rate */
+    int32_t                         mSampleRate = kUnspecified;
+    /** Stream audio device ID */
+    int32_t                         mDeviceId = kUnspecified;
+    /** Stream buffer capacity specified as a number of audio frames */
+    int32_t                         mBufferCapacityInFrames = kUnspecified;
+    /** Stream buffer size specified as a number of audio frames */
+    int32_t                         mBufferSizeInFrames = kUnspecified;
+
+    /** Stream sharing mode */
+    SharingMode                     mSharingMode = SharingMode::Shared;
+    /** Format of audio frames */
+    AudioFormat                     mFormat = AudioFormat::Unspecified;
+    /** Stream direction */
+    Direction                       mDirection = Direction::Output;
+    /** Stream performance mode */
+    PerformanceMode                 mPerformanceMode = PerformanceMode::None;
+
+    /** Stream usage. Only active on Android 28+ */
+    Usage                           mUsage = Usage::Media;
+    /** Stream content type. Only active on Android 28+ */
+    ContentType                     mContentType = ContentType::Music;
+    /** Stream input preset. Only active on Android 28+
+     * TODO InputPreset::Unspecified should be considered as a possible default alternative.
+    */
+    InputPreset                     mInputPreset = InputPreset::VoiceRecognition;
+    /** Stream session ID allocation strategy. Only active on Android 28+ */
+    SessionId                       mSessionId = SessionId::None;
+
+    /** Control the name of the package creating the stream. Only active on Android 31+ */
+    std::string                     mPackageName;
+    /** Control the attribution tag of the context creating the stream. Only active on Android 31+ */
+    std::string                     mAttributionTag;
+
+    // Control whether Oboe can convert channel counts to achieve optimal results.
+    bool                            mChannelConversionAllowed = false;
+    // Control whether Oboe can convert data formats to achieve optimal results.
+    bool                            mFormatConversionAllowed = false;
+    // Control whether and how Oboe can convert sample rates to achieve optimal results.
+    SampleRateConversionQuality     mSampleRateConversionQuality = SampleRateConversionQuality::None;
+
+    /** Validate stream parameters that might not be checked in lower layers */
+    virtual Result isValidConfig() {
+        switch (mFormat) {
+            case AudioFormat::Unspecified:
+            case AudioFormat::I16:
+            case AudioFormat::Float:
+            case AudioFormat::I24:
+            case AudioFormat::I32:
+                break;
+
+            default:
+                return Result::ErrorInvalidFormat;
+        }
+
+        switch (mSampleRateConversionQuality) {
+            case SampleRateConversionQuality::None:
+            case SampleRateConversionQuality::Fastest:
+            case SampleRateConversionQuality::Low:
+            case SampleRateConversionQuality::Medium:
+            case SampleRateConversionQuality::High:
+            case SampleRateConversionQuality::Best:
+                return Result::OK;
+            default:
+                return Result::ErrorIllegalArgument;
+        }
+    }
+};
+
+} // namespace oboe
+
+#endif /* OBOE_STREAM_BASE_H_ */

+ 520 - 0
app/src/main/cpp/oboe/include/oboe/AudioStreamBuilder.h

@@ -0,0 +1,520 @@
+/*
+ * 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_STREAM_BUILDER_H_
+#define OBOE_STREAM_BUILDER_H_
+
+#include "oboe/Definitions.h"
+#include "oboe/AudioStreamBase.h"
+#include "ResultWithValue.h"
+
+namespace oboe {
+
+    // This depends on AudioStream, so we use forward declaration, it will close and delete the stream
+    struct StreamDeleterFunctor;
+    using ManagedStream = std::unique_ptr<AudioStream, StreamDeleterFunctor>;
+
+/**
+ * Factory class for an audio Stream.
+ */
+class AudioStreamBuilder : public AudioStreamBase {
+public:
+
+    AudioStreamBuilder() : AudioStreamBase() {}
+
+    AudioStreamBuilder(const AudioStreamBase &audioStreamBase): AudioStreamBase(audioStreamBase) {}
+
+    /**
+     * Request a specific number of channels.
+     *
+     * Default is kUnspecified. If the value is unspecified then
+     * the application should query for the actual value after the stream is opened.
+     */
+    AudioStreamBuilder *setChannelCount(int channelCount) {
+        mChannelCount = channelCount;
+        return this;
+    }
+
+    /**
+     * Request the direction for a stream. The default is Direction::Output.
+     *
+     * @param direction Direction::Output or Direction::Input
+     */
+    AudioStreamBuilder *setDirection(Direction direction) {
+        mDirection = direction;
+        return this;
+    }
+
+    /**
+     * Request a specific sample rate in Hz.
+     *
+     * Default is kUnspecified. 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.
+     *
+     */
+    AudioStreamBuilder *setSampleRate(int32_t sampleRate) {
+        mSampleRate = sampleRate;
+        return this;
+    }
+
+    /**
+     * @deprecated use `setFramesPerDataCallback` instead.
+     */
+    AudioStreamBuilder *setFramesPerCallback(int framesPerCallback) {
+        return setFramesPerDataCallback(framesPerCallback);
+    }
+
+    /**
+     * Request a specific number of frames for the data callback.
+     *
+     * Default is kUnspecified. 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 pointer to the builder so calls can be chained
+     */
+    AudioStreamBuilder *setFramesPerDataCallback(int framesPerCallback) {
+        mFramesPerCallback = framesPerCallback;
+        return this;
+    }
+
+    /**
+     * Request a sample data format, for example Format::Float.
+     *
+     * Default is Format::Unspecified. If the value is unspecified then
+     * the application should query for the actual value after the stream is opened.
+     */
+    AudioStreamBuilder *setFormat(AudioFormat format) {
+        mFormat = format;
+        return this;
+    }
+
+    /**
+     * Set the requested buffer capacity in frames.
+     * BufferCapacityInFrames is the maximum possible BufferSizeInFrames.
+     *
+     * The final stream capacity may differ. For AAudio it should be at least this big.
+     * For OpenSL ES, it could be smaller.
+     *
+     * Default is kUnspecified.
+     *
+     * @param bufferCapacityInFrames the desired buffer capacity in frames or kUnspecified
+     * @return pointer to the builder so calls can be chained
+     */
+    AudioStreamBuilder *setBufferCapacityInFrames(int32_t bufferCapacityInFrames) {
+        mBufferCapacityInFrames = bufferCapacityInFrames;
+        return this;
+    }
+
+    /**
+     * Get the audio API which will be requested when opening the stream. No guarantees that this is
+     * the API which will actually be used. Query the stream itself to find out the API which is
+     * being used.
+     *
+     * If you do not specify the API, then AAudio will be used if isAAudioRecommended()
+     * returns true. Otherwise OpenSL ES will be used.
+     *
+     * @return the requested audio API
+     */
+    AudioApi getAudioApi() const { return mAudioApi; }
+
+    /**
+     * If you leave this unspecified then Oboe will choose the best API
+     * for the device and SDK version at runtime.
+     *
+     * This should almost always be left unspecified, except for debugging purposes.
+     * Specifying AAudio will force Oboe to use AAudio on 8.0, which is extremely risky.
+     * Specifying OpenSLES should mainly be used to test legacy performance/functionality.
+     *
+     * If the caller requests AAudio and it is supported then AAudio will be used.
+     *
+     * @param audioApi Must be AudioApi::Unspecified, AudioApi::OpenSLES or AudioApi::AAudio.
+     * @return pointer to the builder so calls can be chained
+     */
+    AudioStreamBuilder *setAudioApi(AudioApi audioApi) {
+        mAudioApi = audioApi;
+        return this;
+    }
+
+    /**
+     * Is the AAudio API supported on this device?
+     *
+     * AAudio was introduced in the Oreo 8.0 release.
+     *
+     * @return true if supported
+     */
+    static bool isAAudioSupported();
+
+    /**
+     * Is the AAudio API recommended this device?
+     *
+     * AAudio may be supported but not recommended because of version specific issues.
+     * AAudio is not recommended for Android 8.0 or earlier versions.
+     *
+     * @return true if recommended
+     */
+    static bool isAAudioRecommended();
+
+    /**
+     * 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 SharingMode::Shared or SharingMode::Exclusive
+     * @return pointer to the builder so calls can be chained
+     */
+    AudioStreamBuilder *setSharingMode(SharingMode 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, PerformanceMode::LowLatency
+     * @return pointer to the builder so calls can be chained
+     */
+    AudioStreamBuilder *setPerformanceMode(PerformanceMode performanceMode) {
+        mPerformanceMode = performanceMode;
+        return this;
+    }
+
+
+    /**
+     * Set the intended use case for an output stream.
+     *
+     * The system will use this information to optimize the behavior of the stream.
+     * This could, for example, affect how volume and focus is handled for the stream.
+     * The usage is ignored for input streams.
+     *
+     * The default, if you do not call this function, is Usage::Media.
+     *
+     * Added in API level 28.
+     *
+     * @param usage the desired usage, eg. Usage::Game
+     */
+    AudioStreamBuilder *setUsage(Usage usage) {
+        mUsage = usage;
+        return this;
+    }
+
+    /**
+     * Set the type of audio data that an output stream will carry.
+     *
+     * The system will use this information to optimize the behavior of the stream.
+     * This could, for example, affect whether a stream is paused when a notification occurs.
+     * The contentType is ignored for input streams.
+     *
+     * The default, if you do not call this function, is ContentType::Music.
+     *
+     * Added in API level 28.
+     *
+     * @param contentType the type of audio data, eg. ContentType::Speech
+     */
+    AudioStreamBuilder *setContentType(ContentType contentType) {
+        mContentType = contentType;
+        return this;
+    }
+
+    /**
+     * Set the input (capture) preset for the stream.
+     *
+     * The system will use this information to optimize the behavior of the stream.
+     * This could, for example, affect which microphones are used and how the
+     * recorded data is processed.
+     *
+     * The default, if you do not call this function, is InputPreset::VoiceRecognition.
+     * That is because VoiceRecognition is the preset with the lowest latency
+     * on many platforms.
+     *
+     * Added in API level 28.
+     *
+     * @param inputPreset the desired configuration for recording
+     */
+    AudioStreamBuilder *setInputPreset(InputPreset inputPreset) {
+        mInputPreset = inputPreset;
+        return this;
+    }
+
+    /** Set the requested session ID.
+     *
+     * The session ID can be used to associate a stream with effects processors.
+     * The effects are controlled using the Android AudioEffect Java API.
+     *
+     * The default, if you do not call this function, is SessionId::None.
+     *
+     * If set to SessionId::Allocate then a session ID will be allocated
+     * when the stream is opened.
+     *
+     * The allocated session ID can be obtained by calling AudioStream::getSessionId()
+     * and then used with this function when opening another stream.
+     * This allows effects to be shared between streams.
+     *
+     * Session IDs from Oboe can be used the Android Java APIs and vice versa.
+     * So a session ID from an Oboe stream can be passed to Java
+     * and effects applied using the Java AudioEffect API.
+     *
+     * Allocated session IDs will always be positive and nonzero.
+     *
+     * Added in API level 28.
+     *
+     * @param sessionId an allocated sessionID or SessionId::Allocate
+     */
+    AudioStreamBuilder *setSessionId(SessionId sessionId) {
+        mSessionId = sessionId;
+        return this;
+    }
+
+    /**
+     * Request a stream to a specific audio input/output device given an audio device ID.
+     *
+     * In most cases, the primary device will be the appropriate device to use, and the
+     * deviceId can be left kUnspecified.
+     *
+     * On Android, for example, the ID could be obtained from the Java AudioManager.
+     * AudioManager.getDevices() returns an array of AudioDeviceInfo[], which contains
+     * a getId() method (as well as other type information), that should be passed
+     * to this method.
+     *
+     *
+     * Note that when using OpenSL ES, this will be ignored and the created
+     * stream will have deviceId kUnspecified.
+     *
+     * @param deviceId device identifier or kUnspecified
+     * @return pointer to the builder so calls can be chained
+     */
+    AudioStreamBuilder *setDeviceId(int32_t deviceId) {
+        mDeviceId = deviceId;
+        return this;
+    }
+
+    /**
+     * Specifies an object to handle data related callbacks from the underlying API.
+     *
+     * <strong>Important: See AudioStreamCallback for restrictions on what may be called
+     * from the callback methods.</strong>
+     *
+     * @param dataCallback
+     * @return pointer to the builder so calls can be chained
+     */
+    AudioStreamBuilder *setDataCallback(oboe::AudioStreamDataCallback *dataCallback) {
+        mDataCallback = dataCallback;
+        return this;
+    }
+
+    /**
+     * Specifies an object to handle error related callbacks from the underlying API.
+     * This can occur when a stream is disconnected because a headset is plugged in or unplugged.
+     * It can also occur if the audio service fails or if an exclusive stream is stolen by
+     * another stream.
+     *
+     * <strong>Important: See AudioStreamCallback for restrictions on what may be called
+     * from the callback methods.</strong>
+     *
+     * <strong>When an error callback occurs, the associated stream must be stopped and closed
+     * in a separate thread.</strong>
+     *
+     * @param errorCallback
+     * @return pointer to the builder so calls can be chained
+     */
+    AudioStreamBuilder *setErrorCallback(oboe::AudioStreamErrorCallback *errorCallback) {
+        mErrorCallback = errorCallback;
+        return this;
+    }
+
+    /**
+     * Specifies an object to handle data or error related callbacks from the underlying API.
+     *
+     * This is the equivalent of calling both setDataCallback() and setErrorCallback().
+     *
+     * <strong>Important: See AudioStreamCallback for restrictions on what may be called
+     * from the callback methods.</strong>
+     *
+     * When an error callback occurs, the associated stream will be stopped and closed in a separate thread.
+     *
+     * A note on why the streamCallback parameter is a raw pointer rather than a smart pointer:
+     *
+     * The caller should retain ownership of the object streamCallback points to. At first glance weak_ptr may seem like
+     * a good candidate for streamCallback as this implies temporary ownership. However, a weak_ptr can only be created
+     * from a shared_ptr. A shared_ptr incurs some performance overhead. The callback object is likely to be accessed
+     * every few milliseconds when the stream requires new data so this overhead is something we want to avoid.
+     *
+     * This leaves a raw pointer as the logical type choice. The only caveat being that the caller must not destroy
+     * the callback before the stream has been closed.
+     *
+     * @param streamCallback
+     * @return pointer to the builder so calls can be chained
+     */
+    AudioStreamBuilder *setCallback(AudioStreamCallback *streamCallback) {
+        // Use the same callback object for both, dual inheritance.
+        mDataCallback = streamCallback;
+        mErrorCallback = streamCallback;
+        return this;
+    }
+
+    /**
+     * If true then Oboe might convert channel counts to achieve optimal results.
+     * On some versions of Android for example, stereo streams could not use a FAST track.
+     * So a mono stream might be used instead and duplicated to two channels.
+     * On some devices, mono streams might be broken, so a stereo stream might be opened
+     * and converted to mono.
+     *
+     * Default is true.
+     */
+    AudioStreamBuilder *setChannelConversionAllowed(bool allowed) {
+        mChannelConversionAllowed = allowed;
+        return this;
+    }
+
+    /**
+     * If true then Oboe might convert data formats to achieve optimal results.
+     * On some versions of Android, for example, a float stream could not get a
+     * low latency data path. So an I16 stream might be opened and converted to float.
+     *
+     * Default is false.
+     */
+    AudioStreamBuilder *setFormatConversionAllowed(bool allowed) {
+        mFormatConversionAllowed = allowed;
+        return this;
+    }
+
+    /**
+     * Specify the quality of the sample rate converter in Oboe.
+     *
+     * If set to None then Oboe will not do sample rate conversion. But the underlying APIs might
+     * still do sample rate conversion if you specify a sample rate.
+     * That can prevent you from getting a low latency stream.
+     *
+     * If you do the conversion in Oboe then you might still get a low latency stream.
+     *
+     * Default is SampleRateConversionQuality::None
+     */
+    AudioStreamBuilder *setSampleRateConversionQuality(SampleRateConversionQuality quality) {
+        mSampleRateConversionQuality = quality;
+        return this;
+    }
+
+    /**
+     * Declare the name of the package creating the stream.
+     *
+     * This is usually Context#getPackageName()
+     *
+     * The default, if you do not call this function, is a random package in the calling uid.
+     *
+     * Added in API level 31.
+     *
+     * @param packageName packageName of the calling app.
+     */
+    AudioStreamBuilder *setPackageName(std::string packageName) {
+        mPackageName = packageName;
+        return this;
+    }
+
+    /**
+     * Declare the attribution tag of the context creating the stream.
+     *
+     * This is usually Context#getAttributionTag()
+     *
+     * The default, if you do not call this function, is the default attribution tag.
+     *
+     * Added in API level 31.
+     *
+     * @param attributionTag attributionTag of the calling context.
+     */
+    AudioStreamBuilder *setAttributionTag(std::string attributionTag) {
+        mAttributionTag = attributionTag;
+        return this;
+    }
+
+    /**
+     * @return true if AAudio will be used based on the current settings.
+     */
+    bool willUseAAudio() const {
+        return (mAudioApi == AudioApi::AAudio && isAAudioSupported())
+                || (mAudioApi == AudioApi::Unspecified && isAAudioRecommended());
+    }
+
+    /**
+     * Create and open a stream object based on the current settings.
+     *
+     * The caller owns the pointer to the AudioStream object
+     * and must delete it when finished.
+     *
+     * @deprecated Use openStream(std::shared_ptr<oboe::AudioStream> &stream) instead.
+     * @param stream pointer to a variable to receive the stream address
+     * @return OBOE_OK if successful or a negative error code
+     */
+    Result openStream(AudioStream **stream);
+
+    /**
+     * Create and open a stream object based on the current settings.
+     *
+     * The caller shares the pointer to the AudioStream object.
+     * The shared_ptr is used internally by Oboe to prevent the stream from being
+     * deleted while it is being used by callbacks.
+     *
+     * @param stream reference to a shared_ptr to receive the stream address
+     * @return OBOE_OK if successful or a negative error code
+     */
+    Result openStream(std::shared_ptr<oboe::AudioStream> &stream);
+
+    /**
+     * Create and open a ManagedStream object based on the current builder state.
+     *
+     * The caller must create a unique ptr, and pass by reference so it can be
+     * modified to point to an opened stream. The caller owns the unique ptr,
+     * and it will be automatically closed and deleted when going out of scope.
+     *
+     * @deprecated Use openStream(std::shared_ptr<oboe::AudioStream> &stream) instead.
+     * @param stream Reference to the ManagedStream (uniqueptr) used to keep track of stream
+     * @return OBOE_OK if successful or a negative error code.
+     */
+    Result openManagedStream(ManagedStream &stream);
+
+private:
+
+    /**
+     * @param other
+     * @return true if channels, format and sample rate match
+     */
+    bool isCompatible(AudioStreamBase &other);
+
+    /**
+     * Create an AudioStream object. The AudioStream must be opened before use.
+     *
+     * The caller owns the pointer.
+     *
+     * @return pointer to an AudioStream object or nullptr.
+     */
+    oboe::AudioStream *build();
+
+    AudioApi       mAudioApi = AudioApi::Unspecified;
+};
+
+} // namespace oboe
+
+#endif /* OBOE_STREAM_BUILDER_H_ */

+ 189 - 0
app/src/main/cpp/oboe/include/oboe/AudioStreamCallback.h

@@ -0,0 +1,189 @@
+/*
+ * 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_STREAM_CALLBACK_H
+#define OBOE_STREAM_CALLBACK_H
+
+#include "oboe/Definitions.h"
+
+namespace oboe {
+
+class AudioStream;
+
+/**
+ * AudioStreamDataCallback defines a callback interface for
+ * moving data to/from an audio stream using `onAudioReady`
+ * 2) being alerted when a stream has an error using `onError*` methods
+ *
+ * It is used with AudioStreamBuilder::setDataCallback().
+ */
+
+class AudioStreamDataCallback {
+public:
+    virtual ~AudioStreamDataCallback() = default;
+
+    /**
+     * A buffer is ready for processing.
+     *
+     * For an output stream, this function should render and write numFrames of data
+     * in the stream's current data format to the audioData buffer.
+     *
+     * For an input stream, this function should read and process numFrames of data
+     * from the audioData buffer.
+     *
+     * The audio data is passed through the buffer. So do NOT call read() or
+     * write() on the stream that is making the callback.
+     *
+     * Note that numFrames can vary unless AudioStreamBuilder::setFramesPerCallback()
+     * is called.
+     *
+     * Also note that this callback function should be considered a "real-time" function.
+     * It must not do anything that could cause an unbounded delay because that can cause the
+     * audio to glitch or pop.
+     *
+     * These are things the function should NOT do:
+     * <ul>
+     * <li>allocate memory using, for example, malloc() or new</li>
+     * <li>any file operations such as opening, closing, reading or writing</li>
+     * <li>any network operations such as streaming</li>
+     * <li>use any mutexes or other synchronization primitives</li>
+     * <li>sleep</li>
+     * <li>oboeStream->stop(), pause(), flush() or close()</li>
+     * <li>oboeStream->read()</li>
+     * <li>oboeStream->write()</li>
+     * </ul>
+     *
+     * The following are OK to call from the data callback:
+     * <ul>
+     * <li>oboeStream->get*()</li>
+     * <li>oboe::convertToText()</li>
+     * <li>oboeStream->setBufferSizeInFrames()</li>
+     * </ul>
+     *
+     * If you need to move data, eg. MIDI commands, in or out of the callback function then
+     * we recommend the use of non-blocking techniques such as an atomic FIFO.
+     *
+     * @param audioStream pointer to the associated stream
+     * @param audioData buffer containing input data or a place to put output data
+     * @param numFrames number of frames to be processed
+     * @return DataCallbackResult::Continue or DataCallbackResult::Stop
+     */
+    virtual DataCallbackResult onAudioReady(
+            AudioStream *audioStream,
+            void *audioData,
+            int32_t numFrames) = 0;
+};
+
+/**
+ * AudioStreamErrorCallback defines a callback interface for
+ * being alerted when a stream has an error or is disconnected
+ * using `onError*` methods.
+ *
+ * It is used with AudioStreamBuilder::setErrorCallback().
+ */
+class AudioStreamErrorCallback {
+public:
+    virtual ~AudioStreamErrorCallback() = default;
+
+    /**
+     * This will be called before other `onError` methods when an error occurs on a stream,
+     * such as when the stream is disconnected.
+     *
+     * It can be used to override and customize the normal error processing.
+     * Use of this method is considered an advanced technique.
+     * It might, for example, be used if an app want to use a high level lock when
+     * closing and reopening a stream.
+     * Or it might be used when an app want to signal a management thread that handles
+     * all of the stream state.
+     *
+     * If this method returns false it indicates that the stream has *not been stopped and closed
+     * by the application. In this case it will be stopped by Oboe in the following way:
+     * onErrorBeforeClose() will be called, then the stream will be closed and onErrorAfterClose()
+     * will be closed.
+     *
+     * If this method returns true it indicates that the stream *has* been stopped and closed
+     * by the application and Oboe will not do this.
+     * In that case, the app MUST stop() and close() the stream.
+     *
+     * This method will be called on a thread created by Oboe.
+     *
+     * @param audioStream pointer to the associated stream
+     * @param error
+     * @return true if the stream has been stopped and closed, false if not
+     */
+    virtual bool onError(AudioStream* /* audioStream */, Result /* error */) {
+        return false;
+    }
+
+    /**
+     * This will be called when an error occurs on a stream,
+     * such as when the stream is disconnected,
+     * and if onError() returns false (indicating that the error has not already been handled).
+     *
+     * Note that this will be called on a thread created by Oboe.
+     *
+     * The underlying stream will already be stopped by Oboe but not yet closed.
+     * So the stream can be queried.
+     *
+     * Do not close or delete the stream in this method because it will be
+     * closed after this method returns.
+     *
+     * @param audioStream pointer to the associated stream
+     * @param error
+     */
+    virtual void onErrorBeforeClose(AudioStream* /* audioStream */, Result /* error */) {}
+
+    /**
+     * This will be called when an error occurs on a stream,
+     * such as when the stream is disconnected,
+     * and if onError() returns false (indicating that the error has not already been handled).
+     *
+     * The underlying AAudio or OpenSL ES stream will already be stopped AND closed by Oboe.
+     * So the underlying stream cannot be referenced.
+     * But you can still query most parameters.
+     *
+     * This callback could be used to reopen a new stream on another device.
+     *
+     * @param audioStream pointer to the associated stream
+     * @param error
+     */
+    virtual void onErrorAfterClose(AudioStream* /* audioStream */, Result /* error */) {}
+
+};
+
+/**
+ * AudioStreamCallback defines a callback interface for:
+ *
+ * 1) moving data to/from an audio stream using `onAudioReady`
+ * 2) being alerted when a stream has an error using `onError*` methods
+ *
+ * It is used with AudioStreamBuilder::setCallback().
+ *
+ * It combines the interfaces defined by AudioStreamDataCallback and AudioStreamErrorCallback.
+ * This was the original callback object. We now recommend using the individual interfaces
+ * and using setDataCallback() and setErrorCallback().
+ *
+ * @deprecated Use `AudioStreamDataCallback` and `AudioStreamErrorCallback` instead
+ */
+class AudioStreamCallback : public AudioStreamDataCallback,
+                            public AudioStreamErrorCallback {
+public:
+    virtual ~AudioStreamCallback() = default;
+};
+
+} // namespace oboe
+
+#endif //OBOE_STREAM_CALLBACK_H

+ 545 - 0
app/src/main/cpp/oboe/include/oboe/Definitions.h

@@ -0,0 +1,545 @@
+/*
+ * 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_DEFINITIONS_H
+#define OBOE_DEFINITIONS_H
+
+#include <cstdint>
+#include <type_traits>
+
+// Oboe needs to be able to build on old NDKs so we use hard coded constants.
+// The correctness of these constants is verified in "aaudio/AAudioLoader.cpp".
+
+namespace oboe {
+
+    /**
+     * Represents any attribute, property or value which hasn't been specified.
+     */
+    constexpr int32_t kUnspecified = 0;
+
+    // TODO: Investigate using std::chrono
+    /**
+     * The number of nanoseconds in a microsecond. 1,000.
+     */
+    constexpr int64_t kNanosPerMicrosecond =    1000;
+
+    /**
+     * The number of nanoseconds in a millisecond. 1,000,000.
+     */
+    constexpr int64_t kNanosPerMillisecond =    kNanosPerMicrosecond * 1000;
+
+    /**
+     * The number of milliseconds in a second. 1,000.
+     */
+    constexpr int64_t kMillisPerSecond =        1000;
+
+    /**
+     * The number of nanoseconds in a second. 1,000,000,000.
+     */
+    constexpr int64_t kNanosPerSecond =         kNanosPerMillisecond * kMillisPerSecond;
+
+    /**
+     * The state of the audio stream.
+     */
+    enum class StreamState : int32_t { // aaudio_stream_state_t
+        Uninitialized = 0, // AAUDIO_STREAM_STATE_UNINITIALIZED,
+        Unknown = 1, // AAUDIO_STREAM_STATE_UNKNOWN,
+        Open = 2, // AAUDIO_STREAM_STATE_OPEN,
+        Starting = 3, // AAUDIO_STREAM_STATE_STARTING,
+        Started = 4, // AAUDIO_STREAM_STATE_STARTED,
+        Pausing = 5, // AAUDIO_STREAM_STATE_PAUSING,
+        Paused = 6, // AAUDIO_STREAM_STATE_PAUSED,
+        Flushing = 7, // AAUDIO_STREAM_STATE_FLUSHING,
+        Flushed = 8, // AAUDIO_STREAM_STATE_FLUSHED,
+        Stopping = 9, // AAUDIO_STREAM_STATE_STOPPING,
+        Stopped = 10, // AAUDIO_STREAM_STATE_STOPPED,
+        Closing = 11, // AAUDIO_STREAM_STATE_CLOSING,
+        Closed = 12, // AAUDIO_STREAM_STATE_CLOSED,
+        Disconnected = 13, // AAUDIO_STREAM_STATE_DISCONNECTED,
+    };
+
+    /**
+     * The direction of the stream.
+     */
+    enum class Direction : int32_t { // aaudio_direction_t
+
+        /**
+         * Used for playback.
+         */
+        Output = 0, // AAUDIO_DIRECTION_OUTPUT,
+
+        /**
+         * Used for recording.
+         */
+        Input = 1, // AAUDIO_DIRECTION_INPUT,
+    };
+
+    /**
+     * The format of audio samples.
+     */
+    enum class AudioFormat : int32_t { // aaudio_format_t
+        /**
+         * Invalid format.
+         */
+        Invalid = -1, // AAUDIO_FORMAT_INVALID,
+
+        /**
+         * Unspecified format. Format will be decided by Oboe.
+         */
+        Unspecified = 0, // AAUDIO_FORMAT_UNSPECIFIED,
+
+        /**
+         * Signed 16-bit integers.
+         */
+        I16 = 1, // AAUDIO_FORMAT_PCM_I16,
+
+        /**
+         * Single precision floating point.
+         *
+         * This is the recommended format for most applications.
+         * But note that the use of Float may prevent the opening of
+         * a low-latency input path on OpenSL ES or Legacy AAudio streams.
+         */
+        Float = 2, // AAUDIO_FORMAT_PCM_FLOAT,
+
+        /**
+         * Signed 24-bit integers, packed into 3 bytes.
+         *
+         * Note that the use of this format does not guarantee that
+         * the full precision will be provided.  The underlying device may
+         * be using I16 format.
+         *
+         * Added in API 31 (S).
+         */
+        I24 = 3, // AAUDIO_FORMAT_PCM_I24_PACKED
+
+        /**
+         * Signed 32-bit integers.
+         *
+         * Note that the use of this format does not guarantee that
+         * the full precision will be provided.  The underlying device may
+         * be using I16 format.
+         *
+         * Added in API 31 (S).
+         */
+        I32 = 4, // AAUDIO_FORMAT_PCM_I32
+
+    };
+
+    /**
+     * The result of an audio callback.
+     */
+    enum class DataCallbackResult : int32_t { // aaudio_data_callback_result_t
+        // Indicates to the caller that the callbacks should continue.
+        Continue = 0, // AAUDIO_CALLBACK_RESULT_CONTINUE,
+
+        // Indicates to the caller that the callbacks should stop immediately.
+        Stop = 1, // AAUDIO_CALLBACK_RESULT_STOP,
+    };
+
+    /**
+     * The result of an operation. All except the `OK` result indicates that an error occurred.
+     * The `Result` can be converted into a human readable string using `convertToText`.
+     */
+    enum class Result : int32_t { // aaudio_result_t
+        OK = 0, // AAUDIO_OK
+        ErrorBase = -900, // AAUDIO_ERROR_BASE,
+        ErrorDisconnected = -899, // AAUDIO_ERROR_DISCONNECTED,
+        ErrorIllegalArgument = -898, // AAUDIO_ERROR_ILLEGAL_ARGUMENT,
+        ErrorInternal = -896, // AAUDIO_ERROR_INTERNAL,
+        ErrorInvalidState = -895, // AAUDIO_ERROR_INVALID_STATE,
+        ErrorInvalidHandle = -892, // AAUDIO_ERROR_INVALID_HANDLE,
+        ErrorUnimplemented = -890, // AAUDIO_ERROR_UNIMPLEMENTED,
+        ErrorUnavailable = -889, // AAUDIO_ERROR_UNAVAILABLE,
+        ErrorNoFreeHandles = -888, // AAUDIO_ERROR_NO_FREE_HANDLES,
+        ErrorNoMemory = -887, // AAUDIO_ERROR_NO_MEMORY,
+        ErrorNull = -886, // AAUDIO_ERROR_NULL,
+        ErrorTimeout = -885, // AAUDIO_ERROR_TIMEOUT,
+        ErrorWouldBlock = -884, // AAUDIO_ERROR_WOULD_BLOCK,
+        ErrorInvalidFormat = -883, // AAUDIO_ERROR_INVALID_FORMAT,
+        ErrorOutOfRange = -882, // AAUDIO_ERROR_OUT_OF_RANGE,
+        ErrorNoService = -881, // AAUDIO_ERROR_NO_SERVICE,
+        ErrorInvalidRate = -880, // AAUDIO_ERROR_INVALID_RATE,
+        // Reserved for future AAudio result types
+        Reserved1,
+        Reserved2,
+        Reserved3,
+        Reserved4,
+        Reserved5,
+        Reserved6,
+        Reserved7,
+        Reserved8,
+        Reserved9,
+        Reserved10,
+        ErrorClosed = -869,
+    };
+
+    /**
+     * The sharing mode of the audio stream.
+     */
+    enum class SharingMode : int32_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.
+         *
+         * If you do not need the lowest possible latency then we recommend using Shared,
+         * which is the default.
+         */
+        Exclusive = 0, // AAUDIO_SHARING_MODE_EXCLUSIVE,
+
+        /**
+         * Multiple applications can share the same device.
+         * The data from output streams will be mixed by the audio service.
+         * The data for input streams will be distributed by the audio service.
+         *
+         * This will have higher latency than the EXCLUSIVE mode.
+         */
+        Shared = 1, // AAUDIO_SHARING_MODE_SHARED,
+    };
+
+    /**
+     * The performance mode of the audio stream.
+     */
+    enum class PerformanceMode : int32_t { // aaudio_performance_mode_t
+
+        /**
+         * No particular performance needs. Default.
+         */
+        None = 10, // AAUDIO_PERFORMANCE_MODE_NONE,
+
+        /**
+         * Extending battery life is most important.
+         */
+        PowerSaving = 11, // AAUDIO_PERFORMANCE_MODE_POWER_SAVING,
+
+        /**
+         * Reducing latency is most important.
+         */
+        LowLatency = 12, // AAUDIO_PERFORMANCE_MODE_LOW_LATENCY
+    };
+
+    /**
+     * The underlying audio API used by the audio stream.
+     */
+    enum class AudioApi : int32_t {
+        /**
+         * Try to use AAudio. If not available then use OpenSL ES.
+         */
+        Unspecified = kUnspecified,
+
+        /**
+         * Use OpenSL ES.
+         */
+        OpenSLES,
+
+        /**
+         * Try to use AAudio. Fail if unavailable.
+         */
+        AAudio
+    };
+
+    /**
+     * Specifies the quality of the sample rate conversion performed by Oboe.
+     * Higher quality will require more CPU load.
+     * Higher quality conversion will probably be implemented using a sinc based resampler.
+     */
+    enum class SampleRateConversionQuality : int32_t {
+        /**
+         * No conversion by Oboe. Underlying APIs may still do conversion.
+         */
+        None,
+        /**
+         * Fastest conversion but may not sound great.
+         * This may be implemented using bilinear interpolation.
+         */
+        Fastest,
+        Low,
+        Medium,
+        High,
+        /**
+         * Highest quality conversion, which may be expensive in terms of CPU.
+         */
+        Best,
+    };
+
+    /**
+     * The Usage attribute expresses *why* you are playing a sound, what is this sound used for.
+     * This information is used by certain platforms or routing policies
+     * to make more refined volume or routing decisions.
+     *
+     * Note that these match the equivalent values in AudioAttributes in the Android Java API.
+     *
+     * This attribute only has an effect on Android API 28+.
+     */
+    enum class Usage : int32_t { // aaudio_usage_t
+        /**
+         * Use this for streaming media, music performance, video, podcasts, etcetera.
+         */
+        Media =  1, // AAUDIO_USAGE_MEDIA
+
+        /**
+         * Use this for voice over IP, telephony, etcetera.
+         */
+        VoiceCommunication = 2, // AAUDIO_USAGE_VOICE_COMMUNICATION
+
+        /**
+         * Use this for sounds associated with telephony such as busy tones, DTMF, etcetera.
+         */
+        VoiceCommunicationSignalling = 3, // AAUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING
+
+        /**
+         * Use this to demand the users attention.
+         */
+        Alarm = 4, // AAUDIO_USAGE_ALARM
+
+        /**
+         * Use this for notifying the user when a message has arrived or some
+         * other background event has occured.
+         */
+        Notification = 5, // AAUDIO_USAGE_NOTIFICATION
+
+        /**
+         * Use this when the phone rings.
+         */
+        NotificationRingtone = 6, // AAUDIO_USAGE_NOTIFICATION_RINGTONE
+
+        /**
+         * Use this to attract the users attention when, for example, the battery is low.
+         */
+        NotificationEvent = 10, // AAUDIO_USAGE_NOTIFICATION_EVENT
+
+        /**
+         * Use this for screen readers, etcetera.
+         */
+        AssistanceAccessibility = 11, // AAUDIO_USAGE_ASSISTANCE_ACCESSIBILITY
+
+        /**
+         * Use this for driving or navigation directions.
+         */
+        AssistanceNavigationGuidance = 12, // AAUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE
+
+        /**
+         * Use this for user interface sounds, beeps, etcetera.
+         */
+        AssistanceSonification = 13, // AAUDIO_USAGE_ASSISTANCE_SONIFICATION
+
+        /**
+         * Use this for game audio and sound effects.
+         */
+        Game = 14, // AAUDIO_USAGE_GAME
+
+        /**
+         * Use this for audio responses to user queries, audio instructions or help utterances.
+         */
+        Assistant = 16, // AAUDIO_USAGE_ASSISTANT
+    };
+
+
+    /**
+     * The ContentType attribute describes *what* you are playing.
+     * It expresses the general category of the content. This information is optional.
+     * But in case it is known (for instance {@link Movie} for a
+     * movie streaming service or {@link Speech} for
+     * an audio book application) this information might be used by the audio framework to
+     * enforce audio focus.
+     *
+     * Note that these match the equivalent values in AudioAttributes in the Android Java API.
+     *
+     * This attribute only has an effect on Android API 28+.
+     */
+    enum ContentType : int32_t { // aaudio_content_type_t
+
+        /**
+         * Use this for spoken voice, audio books, etcetera.
+         */
+        Speech = 1, // AAUDIO_CONTENT_TYPE_SPEECH
+
+        /**
+         * Use this for pre-recorded or live music.
+         */
+        Music = 2, // AAUDIO_CONTENT_TYPE_MUSIC
+
+        /**
+         * Use this for a movie or video soundtrack.
+         */
+        Movie = 3, // AAUDIO_CONTENT_TYPE_MOVIE
+
+        /**
+         * Use this for sound is designed to accompany a user action,
+         * such as a click or beep sound made when the user presses a button.
+         */
+        Sonification = 4, // AAUDIO_CONTENT_TYPE_SONIFICATION
+    };
+
+    /**
+     * Defines the audio source.
+     * An audio source defines both a default physical source of audio signal, and a recording
+     * configuration.
+     *
+     * Note that these match the equivalent values in MediaRecorder.AudioSource in the Android Java API.
+     *
+     * This attribute only has an effect on Android API 28+.
+     */
+    enum InputPreset : int32_t { // aaudio_input_preset_t
+        /**
+         * Use this preset when other presets do not apply.
+         */
+        Generic = 1, // AAUDIO_INPUT_PRESET_GENERIC
+
+        /**
+         * Use this preset when recording video.
+         */
+        Camcorder = 5, // AAUDIO_INPUT_PRESET_CAMCORDER
+
+        /**
+         * Use this preset when doing speech recognition.
+         */
+        VoiceRecognition = 6, // AAUDIO_INPUT_PRESET_VOICE_RECOGNITION
+
+        /**
+         * Use this preset when doing telephony or voice messaging.
+         */
+        VoiceCommunication = 7, // AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION
+
+        /**
+         * Use this preset to obtain an input with no effects.
+         * Note that this input will not have automatic gain control
+         * so the recorded volume may be very low.
+         */
+        Unprocessed = 9, // AAUDIO_INPUT_PRESET_UNPROCESSED
+
+        /**
+         * Use this preset for capturing audio meant to be processed in real time
+         * and played back for live performance (e.g karaoke).
+         * The capture path will minimize latency and coupling with playback path.
+         */
+         VoicePerformance = 10, // AAUDIO_INPUT_PRESET_VOICE_PERFORMANCE
+
+    };
+
+    /**
+     * This attribute can be used to allocate a session ID to the audio stream.
+     *
+     * This attribute only has an effect on Android API 28+.
+     */
+    enum SessionId {
+        /**
+         * Do not allocate a session ID.
+         * Effects cannot be used with this stream.
+         * Default.
+         */
+         None = -1, // AAUDIO_SESSION_ID_NONE
+
+        /**
+         * Allocate a session ID that can be used to attach and control
+         * effects using the Java AudioEffects API.
+         * Note that the use of this flag may result in higher latency.
+         *
+         * Note that this matches the value of AudioManager.AUDIO_SESSION_ID_GENERATE.
+         */
+         Allocate = 0, // AAUDIO_SESSION_ID_ALLOCATE
+    };
+
+    /**
+     * The channel count of the audio stream. The underlying type is `int32_t`.
+     * Use of this enum is convenient to avoid "magic"
+     * numbers when specifying the channel count.
+     *
+     * For example, you can write
+     * `builder.setChannelCount(ChannelCount::Stereo)`
+     * rather than `builder.setChannelCount(2)`
+     *
+     */
+    enum ChannelCount : int32_t {
+      /**
+       * Audio channel count definition, use Mono or Stereo
+       */
+      Unspecified = kUnspecified,
+
+      /**
+       * Use this for mono audio
+       */
+      Mono = 1,
+
+      /**
+       * Use this for stereo audio.
+       */
+      Stereo = 2,
+    };
+
+    /**
+     * On API 16 to 26 OpenSL ES will be used. When using OpenSL ES the optimal values for sampleRate and
+     * framesPerBurst are not known by the native code.
+     * On API 17+ these values should be obtained from the AudioManager using this code:
+     *
+     * <pre><code>
+     * // Note that this technique only works for built-in speakers and headphones.
+     * AudioManager myAudioMgr = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
+     * String sampleRateStr = myAudioMgr.getProperty(AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE);
+     * int defaultSampleRate = Integer.parseInt(sampleRateStr);
+     * String framesPerBurstStr = myAudioMgr.getProperty(AudioManager.PROPERTY_OUTPUT_FRAMES_PER_BUFFER);
+     * int defaultFramesPerBurst = Integer.parseInt(framesPerBurstStr);
+     * </code></pre>
+     *
+     * It can then be passed down to Oboe through JNI.
+     *
+     * AAudio will get the optimal framesPerBurst from the HAL and will ignore this value.
+     */
+    class DefaultStreamValues {
+
+    public:
+
+        /** The default sample rate to use when opening new audio streams */
+        static int32_t SampleRate;
+        /** The default frames per burst to use when opening new audio streams */
+        static int32_t FramesPerBurst;
+        /** The default channel count to use when opening new audio streams */
+        static int32_t ChannelCount;
+
+    };
+
+    /**
+     * The time at which the frame at `position` was presented
+     */
+    struct FrameTimestamp {
+        int64_t position; // in frames
+        int64_t timestamp; // in nanoseconds
+    };
+
+    class OboeGlobals {
+    public:
+
+        static bool areWorkaroundsEnabled() {
+            return mWorkaroundsEnabled;
+        }
+
+        /**
+         * Disable this when writing tests to reproduce bugs in AAudio or OpenSL ES
+         * that have workarounds in Oboe.
+         * @param enabled
+         */
+        static void setWorkaroundsEnabled(bool enabled) {
+            mWorkaroundsEnabled = enabled;
+        }
+
+    private:
+        static bool mWorkaroundsEnabled;
+    };
+} // namespace oboe
+
+#endif // OBOE_DEFINITIONS_H

+ 150 - 0
app/src/main/cpp/oboe/include/oboe/LatencyTuner.h

@@ -0,0 +1,150 @@
+/*
+ * Copyright 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.
+ */
+
+#ifndef OBOE_LATENCY_TUNER_
+#define OBOE_LATENCY_TUNER_
+
+#include <atomic>
+#include <cstdint>
+#include "oboe/Definitions.h"
+#include "oboe/AudioStream.h"
+
+namespace oboe {
+
+/**
+ * LatencyTuner can be used to dynamically tune the latency of an output stream.
+ * It adjusts the stream's bufferSize by monitoring the number of underruns.
+ *
+ * This only affects the latency associated with the first level of buffering that is closest
+ * to the application. It does not affect low latency in the HAL, or touch latency in the UI.
+ *
+ * Call tune() right before returning from your data callback function if using callbacks.
+ * Call tune() right before calling write() if using blocking writes.
+ *
+ * If you want to see the ongoing results of this tuning process then call
+ * stream->getBufferSize() periodically.
+ *
+ */
+class LatencyTuner {
+public:
+
+    /**
+     * Construct a new LatencyTuner object which will act on the given audio stream
+     *
+     * @param stream the stream who's latency will be tuned
+     */
+    explicit LatencyTuner(AudioStream &stream);
+
+    /**
+     * Construct a new LatencyTuner object which will act on the given audio stream.
+     *
+     * @param stream the stream who's latency will be tuned
+     * @param the maximum buffer size which the tune() operation will set the buffer size to
+     */
+    explicit LatencyTuner(AudioStream &stream, int32_t maximumBufferSize);
+
+    /**
+     * Adjust the bufferSizeInFrames to optimize latency.
+     * It will start with a low latency and then raise it if an underrun occurs.
+     *
+     * Latency tuning is only supported for AAudio.
+     *
+     * @return OK or negative error, ErrorUnimplemented for OpenSL ES
+     */
+    Result tune();
+
+    /**
+     * This may be called from another thread. Then tune() will call reset(),
+     * which will lower the latency to the minimum and then allow it to rise back up
+     * if there are glitches.
+     *
+     * This is typically called in response to a user decision to minimize latency. In other words,
+     * call this from a button handler.
+     */
+    void requestReset();
+
+    /**
+     * @return true if the audio stream's buffer size is at the maximum value. If no maximum value
+     * was specified when constructing the LatencyTuner then the value of
+     * stream->getBufferCapacityInFrames is used
+     */
+    bool isAtMaximumBufferSize();
+
+    /**
+     * Set the minimum bufferSize in frames that is used when the tuner is reset.
+     * You may wish to call requestReset() after calling this.
+     * @param bufferSize
+     */
+    void setMinimumBufferSize(int32_t bufferSize) {
+        mMinimumBufferSize = bufferSize;
+    }
+
+    int32_t getMinimumBufferSize() const {
+        return mMinimumBufferSize;
+    }
+
+    /**
+     * Set the amount the bufferSize will be incremented while tuning.
+     * By default, this will be one burst.
+     *
+     * Note that AAudio will quantize the buffer size to a multiple of the burstSize.
+     * So the final buffer sizes may not be a multiple of this increment.
+     *
+     * @param sizeIncrement
+     */
+    void setBufferSizeIncrement(int32_t sizeIncrement) {
+        mBufferSizeIncrement = sizeIncrement;
+    }
+
+    int32_t getBufferSizeIncrement() const {
+        return mBufferSizeIncrement;
+    }
+
+private:
+
+    /**
+     * Drop the latency down to the minimum and then let it rise back up.
+     * This is useful if a glitch caused the latency to increase and it hasn't gone back down.
+     *
+     * This should only be called in the same thread as tune().
+     */
+    void reset();
+
+    enum class State {
+        Idle,
+        Active,
+        AtMax,
+        Unsupported
+    } ;
+
+    // arbitrary number of calls to wait before bumping up the latency
+    static constexpr int32_t kIdleCount = 8;
+    static constexpr int32_t kDefaultNumBursts = 2;
+
+    AudioStream           &mStream;
+    State                 mState = State::Idle;
+    int32_t               mMaxBufferSize = 0;
+    int32_t               mPreviousXRuns = 0;
+    int32_t               mIdleCountDown = 0;
+    int32_t               mMinimumBufferSize;
+    int32_t               mBufferSizeIncrement;
+    std::atomic<int32_t>  mLatencyTriggerRequests{0}; // TODO user atomic requester from AAudio
+    std::atomic<int32_t>  mLatencyTriggerResponses{0};
+};
+
+} // namespace oboe
+
+#endif // OBOE_LATENCY_TUNER_

+ 37 - 0
app/src/main/cpp/oboe/include/oboe/Oboe.h

@@ -0,0 +1,37 @@
+/*
+ * 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
+
+/**
+ * \mainpage API reference
+ *
+ * All documentation is found in the <a href="namespaceoboe.html">oboe namespace section</a>
+ *
+ */
+
+#include "oboe/Definitions.h"
+#include "oboe/ResultWithValue.h"
+#include "oboe/LatencyTuner.h"
+#include "oboe/AudioStream.h"
+#include "oboe/AudioStreamBase.h"
+#include "oboe/AudioStreamBuilder.h"
+#include "oboe/Utilities.h"
+#include "oboe/Version.h"
+#include "oboe/StabilizedCallback.h"
+
+#endif //OBOE_OBOE_H

+ 155 - 0
app/src/main/cpp/oboe/include/oboe/ResultWithValue.h

@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef OBOE_RESULT_WITH_VALUE_H
+#define OBOE_RESULT_WITH_VALUE_H
+
+#include "oboe/Definitions.h"
+#include <iostream>
+#include <sstream>
+
+namespace oboe {
+
+/**
+ * A ResultWithValue can store both the result of an operation (either OK or an error) and a value.
+ *
+ * It has been designed for cases where the caller needs to know whether an operation succeeded and,
+ * if it did, a value which was obtained during the operation.
+ *
+ * For example, when reading from a stream the caller needs to know the result of the read operation
+ * and, if it was successful, how many frames were read. Note that ResultWithValue can be evaluated
+ * as a boolean so it's simple to check whether the result is OK.
+ *
+ * <code>
+ * ResultWithValue<int32_t> resultOfRead = myStream.read(&buffer, numFrames, timeoutNanoseconds);
+ *
+ * if (resultOfRead) {
+ *     LOGD("Frames read: %d", resultOfRead.value());
+ * } else {
+ *     LOGD("Error reading from stream: %s", resultOfRead.error());
+ * }
+ * </code>
+ */
+template <typename T>
+class ResultWithValue {
+public:
+
+    /**
+     * Construct a ResultWithValue containing an error result.
+     *
+     * @param error The error
+     */
+    ResultWithValue(oboe::Result error)
+            : mValue{}
+            , mError(error) {}
+
+    /**
+     * Construct a ResultWithValue containing an OK result and a value.
+     *
+     * @param value the value to store
+     */
+    explicit ResultWithValue(T value)
+            : mValue(value)
+            , mError(oboe::Result::OK) {}
+
+    /**
+     * Get the result.
+     *
+     * @return the result
+     */
+    oboe::Result error() const {
+        return mError;
+    }
+
+    /**
+     * Get the value
+     * @return
+     */
+    T value() const {
+        return mValue;
+    }
+
+    /**
+     * @return true if OK
+     */
+    explicit operator bool() const { return mError == oboe::Result::OK; }
+
+    /**
+     * Quick way to check for an error.
+     *
+     * The caller could write something like this:
+     * <code>
+     *     if (!result) { printf("Got error %s\n", convertToText(result.error())); }
+     * </code>
+     *
+     * @return true if an error occurred
+     */
+    bool operator !() const { return mError != oboe::Result::OK; }
+
+    /**
+     * Implicitly convert to a Result. This enables easy comparison with Result values. Example:
+     *
+     * <code>
+     *     ResultWithValue result = openStream();
+     *     if (result == Result::ErrorNoMemory){ // tell user they're out of memory }
+     * </code>
+     */
+    operator Result() const {
+        return mError;
+    }
+
+    /**
+     * Create a ResultWithValue from a number. If the number is positive the ResultWithValue will
+     * have a result of Result::OK and the value will contain the number. If the number is negative
+     * the result will be obtained from the negative number (numeric error codes can be found in
+     * AAudio.h) and the value will be null.
+     *
+     */
+    static ResultWithValue<T> createBasedOnSign(T numericResult){
+
+        // Ensure that the type is either an integer or float
+        static_assert(std::is_arithmetic<T>::value,
+                      "createBasedOnSign can only be called for numeric types (int or float)");
+
+        if (numericResult >= 0){
+            return ResultWithValue<T>(numericResult);
+        } else {
+            return ResultWithValue<T>(static_cast<Result>(numericResult));
+        }
+    }
+
+private:
+    const T             mValue;
+    const oboe::Result  mError;
+};
+
+/**
+ * If the result is `OK` then return the value, otherwise return a human-readable error message.
+ */
+template <typename T>
+std::ostream& operator<<(std::ostream &strm, const ResultWithValue<T> &result) {
+    if (!result) {
+        strm << convertToText(result.error());
+    } else {
+        strm << result.value();
+    }
+   return strm;
+}
+
+} // namespace oboe
+
+
+#endif //OBOE_RESULT_WITH_VALUE_H

+ 75 - 0
app/src/main/cpp/oboe/include/oboe/StabilizedCallback.h

@@ -0,0 +1,75 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef OBOE_STABILIZEDCALLBACK_H
+#define OBOE_STABILIZEDCALLBACK_H
+
+#include <cstdint>
+#include "oboe/AudioStream.h"
+
+namespace oboe {
+
+class StabilizedCallback : public AudioStreamCallback {
+
+public:
+    explicit StabilizedCallback(AudioStreamCallback *callback);
+
+    DataCallbackResult
+    onAudioReady(AudioStream *oboeStream, void *audioData, int32_t numFrames) override;
+
+    void onErrorBeforeClose(AudioStream *oboeStream, Result error) override {
+        return mCallback->onErrorBeforeClose(oboeStream, error);
+    }
+
+    void onErrorAfterClose(AudioStream *oboeStream, Result error) override {
+
+        // Reset all fields now that the stream has been closed
+        mFrameCount = 0;
+        mEpochTimeNanos = 0;
+        mOpsPerNano = 1;
+        return mCallback->onErrorAfterClose(oboeStream, error);
+    }
+
+private:
+
+    AudioStreamCallback *mCallback = nullptr;
+    int64_t mFrameCount = 0;
+    int64_t mEpochTimeNanos = 0;
+    double  mOpsPerNano = 1;
+
+    void generateLoad(int64_t durationNanos);
+};
+
+/**
+ * cpu_relax is an architecture specific method of telling the CPU that you don't want it to
+ * do much work. asm volatile keeps the compiler from optimising these instructions out.
+ */
+#if defined(__i386__) || defined(__x86_64__)
+#define cpu_relax() asm volatile("rep; nop" ::: "memory");
+
+#elif defined(__arm__) || defined(__mips__)
+    #define cpu_relax() asm volatile("":::"memory")
+
+#elif defined(__aarch64__)
+#define cpu_relax() asm volatile("yield" ::: "memory")
+
+#else
+#error "cpu_relax is not defined for this architecture"
+#endif
+
+}
+
+#endif //OBOE_STABILIZEDCALLBACK_H

+ 87 - 0
app/src/main/cpp/oboe/include/oboe/Utilities.h

@@ -0,0 +1,87 @@
+/*
+ * 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_UTILITIES_H
+#define OBOE_UTILITIES_H
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <string>
+#include "oboe/Definitions.h"
+
+namespace oboe {
+
+/**
+ * Convert an array of floats to an array of 16-bit integers.
+ *
+ * @param source the input array.
+ * @param destination the output array.
+ * @param numSamples the number of values to convert.
+ */
+void convertFloatToPcm16(const float *source, int16_t *destination, int32_t numSamples);
+
+/**
+ * Convert an array of 16-bit integers to an array of floats.
+ *
+ * @param source the input array.
+ * @param destination the output array.
+ * @param numSamples the number of values to convert.
+ */
+void convertPcm16ToFloat(const int16_t *source, float *destination, int32_t numSamples);
+
+/**
+ * @return the size of a sample of the given format in bytes or 0 if format is invalid
+ */
+int32_t convertFormatToSizeInBytes(AudioFormat format);
+
+/**
+ * The text is the ASCII symbol corresponding to the supplied Oboe enum value,
+ * or an English message saying the value is unrecognized.
+ * This is intended for developers to use when debugging.
+ * It is not for displaying to users.
+ *
+ * @param input object to convert from. @see common/Utilities.cpp for concrete implementations
+ * @return text representation of an Oboe enum value. There is no need to call free on this.
+ */
+template <typename FromType>
+const char * convertToText(FromType input);
+
+/**
+ * @param name
+ * @return the value of a named system property in a string or empty string
+ */
+std::string getPropertyString(const char * name);
+
+/**
+ * @param name
+ * @param defaultValue
+ * @return integer value associated with a property or the default value
+ */
+int getPropertyInteger(const char * name, int defaultValue);
+
+/**
+ * Return the version of the SDK that is currently running.
+ *
+ * For example, on Android, this would return 27 for Oreo 8.1.
+ * If the version number cannot be determined then this will return -1.
+ *
+ * @return version number or -1
+ */
+int getSdkVersion();
+
+} // namespace oboe
+
+#endif //OBOE_UTILITIES_H

+ 92 - 0
app/src/main/cpp/oboe/include/oboe/Version.h

@@ -0,0 +1,92 @@
+/*
+ * Copyright 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.
+ */
+
+#ifndef OBOE_VERSIONINFO_H
+#define OBOE_VERSIONINFO_H
+
+#include <cstdint>
+
+/**
+ * A note on use of preprocessor defines:
+ *
+ * This is one of the few times when it's suitable to use preprocessor defines rather than constexpr
+ * Why? Because C++11 requires a lot of boilerplate code to convert integers into compile-time
+ * string literals. The preprocessor, despite it's lack of type checking, is more suited to the task
+ *
+ * See: https://stackoverflow.com/questions/6713420/c-convert-integer-to-string-at-compile-time/26824971#26824971
+ *
+ */
+
+// Type: 8-bit unsigned int. Min value: 0 Max value: 255. See below for description.
+#define OBOE_VERSION_MAJOR 1
+
+// Type: 8-bit unsigned int. Min value: 0 Max value: 255. See below for description.
+#define OBOE_VERSION_MINOR 6
+
+// Type: 16-bit unsigned int. Min value: 0 Max value: 65535. See below for description.
+#define OBOE_VERSION_PATCH 1
+
+#define OBOE_STRINGIFY(x) #x
+#define OBOE_TOSTRING(x) OBOE_STRINGIFY(x)
+
+// Type: String literal. See below for description.
+#define OBOE_VERSION_TEXT \
+        OBOE_TOSTRING(OBOE_VERSION_MAJOR) "." \
+        OBOE_TOSTRING(OBOE_VERSION_MINOR) "." \
+        OBOE_TOSTRING(OBOE_VERSION_PATCH)
+
+// Type: 32-bit unsigned int. See below for description.
+#define OBOE_VERSION_NUMBER ((OBOE_VERSION_MAJOR << 24) | (OBOE_VERSION_MINOR << 16) | OBOE_VERSION_PATCH)
+
+namespace oboe {
+
+const char * getVersionText();
+
+/**
+ * Oboe versioning object
+ */
+struct Version {
+    /**
+     * This is incremented when we make breaking API changes. Based loosely on https://semver.org/.
+     */
+    static constexpr uint8_t Major = OBOE_VERSION_MAJOR;
+
+    /**
+     * This is incremented when we add backwards compatible functionality. Or set to zero when MAJOR is
+     * incremented.
+     */
+    static constexpr uint8_t Minor = OBOE_VERSION_MINOR;
+
+    /**
+     * This is incremented when we make backwards compatible bug fixes. Or set to zero when MINOR is
+     * incremented.
+     */
+    static constexpr uint16_t Patch = OBOE_VERSION_PATCH;
+
+    /**
+     * Version string in the form MAJOR.MINOR.PATCH.
+     */
+    static constexpr const char * Text = OBOE_VERSION_TEXT;
+
+    /**
+     * Integer representation of the current Oboe library version. This will always increase when the
+     * version number changes so can be compared using integer comparison.
+     */
+    static constexpr uint32_t Number = OBOE_VERSION_NUMBER;
+};
+
+} // namespace oboe
+#endif //OBOE_VERSIONINFO_H

+ 1 - 0
app/src/main/cpp/oboe/libs/android.arm64-v8a/abi.json

@@ -0,0 +1 @@
+{"abi":"arm64-v8a","api":21,"ndk":21,"stl":"c++_shared"}

二进制
app/src/main/cpp/oboe/libs/android.arm64-v8a/liboboe.so


+ 1 - 0
app/src/main/cpp/oboe/libs/android.armeabi-v7a/abi.json

@@ -0,0 +1 @@
+{"abi":"armeabi-v7a","api":16,"ndk":21,"stl":"c++_shared"}

二进制
app/src/main/cpp/oboe/libs/android.armeabi-v7a/liboboe.so


+ 1 - 0
app/src/main/cpp/oboe/libs/android.x86/abi.json

@@ -0,0 +1 @@
+{"abi":"x86","api":16,"ndk":21,"stl":"c++_shared"}

二进制
app/src/main/cpp/oboe/libs/android.x86/liboboe.so


+ 1 - 0
app/src/main/cpp/oboe/libs/android.x86_64/abi.json

@@ -0,0 +1 @@
+{"abi":"x86_64","api":21,"ndk":21,"stl":"c++_shared"}

二进制
app/src/main/cpp/oboe/libs/android.x86_64/liboboe.so


+ 8 - 0
app/src/main/cpp/oboe/module.json

@@ -0,0 +1,8 @@
+{
+    "export_libraries": [],
+    "library_name": null,
+    "android": {
+      "export_libraries": [],
+      "library_name": null
+    }
+}

+ 13 - 0
app/src/main/cpp/oboe/oboeConfig.cmake

@@ -0,0 +1,13 @@
+if(ANDROID AND NOT TARGET oboe::oboe)
+	#set(OBOE_LIBRARY "${CMAKE_CURRENT_SOURCE_DIR}/libs/android.${CMAKE_ANDROID_ARCH_ABI}/liboboe.so")
+	#set(OBOE_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/include")
+	add_library(oboe::oboe SHARED IMPORTED)
+	set_target_properties(oboe::oboe PROPERTIES
+		IMPORTED_LOCATION "${CMAKE_CURRENT_LIST_DIR}/libs/android.${CMAKE_ANDROID_ARCH_ABI}/liboboe.so"
+		INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_LIST_DIR}/include"
+	)
+	#mark_as_advanced(OBOE_LIBRARY OBOE_INCLUDE_DIR)
+endif()
+
+#include(FindPackageHandleStandardArgs)
+#find_package_handle_standard_args(Oboe REQUIRED_VARS OBOE_LIBRARY OBOE_INCLUDE_DIR)

+ 14 - 0
app/src/main/cpp/oboe/oboeConfigVersion.cmake

@@ -0,0 +1,14 @@
+set(PACKAGE_FIND_VERSION_MAJOR 1)
+set(PACKAGE_FIND_VERSION_MINOR 6)
+set(PACKAGE_FIND_VERSION_PATCH 1)
+set(PACKAGE_FIND_VERSION_COUNT 3)
+set(PACKAGE_VERSION "${PACKAGE_FIND_VERSION_MAJOR}.${PACKAGE_FIND_VERSION_MINOR}.${PACKAGE_FIND_VERSION_PATCH}")
+
+if("${PACKAGE_VERSION}" VERSION_LESS "${PACKAGE_FIND_VERSION}")
+	set(PACKAGE_VERSION_COMPATIBLE FALSE)
+else()
+	set(PACKAGE_VERSION_COMPATIBLE TRUE)
+	if("${PACKAGE_VERSION}" VERSION_EQUAL "${PACKAGE_FIND_VERSION}")
+		set(PACKAGE_VERSION_EXACT TRUE)
+	endif()
+endif()