| /* |
| * 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() const = 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() const { |
| 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 |
| */ |
| virtual int32_t getFramesPerBurst() = 0; |
| |
| /** |
| * 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; |
| } |
| |
| /** |
| * Launch a thread that will stop the stream. |
| */ |
| void launchStopThread(); |
| |
| /** |
| * 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; |
| |
| 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_ */ |