LOCAL_SRC_FILES:= \
channels.c \
conversion.cpp \
- fifo.c \
+ fifo.cpp \
fixedfft.cpp.arm \
format.c \
limiter.c \
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := \
channels.c \
- fifo.c \
+ fifo.cpp \
format.c \
limiter.c \
minifloat.c \
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := \
- fifo.c \
+ fifo.cpp \
primitives.c \
roundup.c
--- /dev/null
+# How to build and view documentation
+
+* doxygen Doxyfile
+* cd html
+* python -m SimpleHTTPServer
+* open in web browser
+ http://localhost:8000/classaudio__utils__fifo.html
+* when done: rm -rf html
+++ /dev/null
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//#define LOG_NDEBUG 0
-#define LOG_TAG "audio_utils_fifo"
-
-#include <stdlib.h>
-#include <string.h>
-#include <audio_utils/fifo.h>
-#include <audio_utils/roundup.h>
-#include <cutils/atomic.h>
-#include <cutils/log.h>
-
-void audio_utils_fifo_init(struct audio_utils_fifo *fifo, size_t frameCount, size_t frameSize,
- void *buffer)
-{
- // We would need a 64-bit roundup to support larger frameCount.
- ALOG_ASSERT(fifo != NULL && frameCount > 0 && frameSize > 0 && buffer != NULL);
- fifo->mFrameCount = frameCount;
- fifo->mFrameCountP2 = roundup(frameCount);
- fifo->mFudgeFactor = fifo->mFrameCountP2 - fifo->mFrameCount;
- fifo->mFrameSize = frameSize;
- fifo->mBuffer = buffer;
- fifo->mFront = 0;
- fifo->mRear = 0;
-}
-
-void audio_utils_fifo_deinit(struct audio_utils_fifo *fifo __unused)
-{
-}
-
-// Return a new index as the sum of an old index (either mFront or mRear) and a specified increment.
-static inline int32_t audio_utils_fifo_sum(struct audio_utils_fifo *fifo, int32_t index,
- uint32_t increment)
-{
- if (fifo->mFudgeFactor) {
- uint32_t mask = fifo->mFrameCountP2 - 1;
- ALOG_ASSERT((index & mask) < fifo->mFrameCount);
- ALOG_ASSERT(/*0 <= increment &&*/ increment <= fifo->mFrameCountP2);
- if ((index & mask) + increment >= fifo->mFrameCount) {
- increment += fifo->mFudgeFactor;
- }
- index += increment;
- ALOG_ASSERT((index & mask) < fifo->mFrameCount);
- return index;
- } else {
- return index + increment;
- }
-}
-
-// Return the difference between two indices: rear - front, where 0 <= difference <= mFrameCount.
-static inline size_t audio_utils_fifo_diff(struct audio_utils_fifo *fifo, int32_t rear,
- int32_t front)
-{
- int32_t diff = rear - front;
- if (fifo->mFudgeFactor) {
- uint32_t mask = ~(fifo->mFrameCountP2 - 1);
- int32_t genDiff = (rear & mask) - (front & mask);
- if (genDiff != 0) {
- ALOG_ASSERT(genDiff == (int32_t) fifo->mFrameCountP2);
- diff -= fifo->mFudgeFactor;
- }
- }
- // FIFO should not be overfull
- ALOG_ASSERT(0 <= diff && diff <= (int32_t) fifo->mFrameCount);
- return (size_t) diff;
-}
-
-ssize_t audio_utils_fifo_write(struct audio_utils_fifo *fifo, const void *buffer, size_t count)
-{
- int32_t front = android_atomic_acquire_load(&fifo->mFront);
- int32_t rear = fifo->mRear;
- size_t availToWrite = fifo->mFrameCount - audio_utils_fifo_diff(fifo, rear, front);
- if (availToWrite > count) {
- availToWrite = count;
- }
- rear &= fifo->mFrameCountP2 - 1;
- size_t part1 = fifo->mFrameCount - rear;
- if (part1 > availToWrite) {
- part1 = availToWrite;
- }
- if (part1 > 0) {
- memcpy((char *) fifo->mBuffer + (rear * fifo->mFrameSize), buffer,
- part1 * fifo->mFrameSize);
- size_t part2 = availToWrite - part1;
- if (part2 > 0) {
- memcpy(fifo->mBuffer, (char *) buffer + (part1 * fifo->mFrameSize),
- part2 * fifo->mFrameSize);
- }
- android_atomic_release_store(audio_utils_fifo_sum(fifo, fifo->mRear, availToWrite),
- &fifo->mRear);
- }
- return availToWrite;
-}
-
-ssize_t audio_utils_fifo_read(struct audio_utils_fifo *fifo, void *buffer, size_t count)
-{
- int32_t rear = android_atomic_acquire_load(&fifo->mRear);
- int32_t front = fifo->mFront;
- size_t availToRead = audio_utils_fifo_diff(fifo, rear, front);
- if (availToRead > count) {
- availToRead = count;
- }
- front &= fifo->mFrameCountP2 - 1;
- size_t part1 = fifo->mFrameCount - front;
- if (part1 > availToRead) {
- part1 = availToRead;
- }
- if (part1 > 0) {
- memcpy(buffer, (char *) fifo->mBuffer + (front * fifo->mFrameSize),
- part1 * fifo->mFrameSize);
- size_t part2 = availToRead - part1;
- if (part2 > 0) {
- memcpy((char *) buffer + (part1 * fifo->mFrameSize), fifo->mBuffer,
- part2 * fifo->mFrameSize);
- }
- android_atomic_release_store(audio_utils_fifo_sum(fifo, fifo->mFront, availToRead),
- &fifo->mFront);
- }
- return availToRead;
-}
--- /dev/null
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "audio_utils_fifo"
+
+#include <errno.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+
+// FIXME futex portion is not supported on Mac, should use the Mac alternative
+#ifdef __linux__
+#include <linux/futex.h>
+#include <sys/syscall.h>
+#else
+#define FUTEX_WAIT 0
+#define FUTEX_WAIT_PRIVATE 0
+#define FUTEX_WAKE 0
+#define FUTEX_WAKE_PRIVATE 0
+#endif
+
+#include <audio_utils/fifo.h>
+#include <audio_utils/roundup.h>
+#include <cutils/log.h>
+#include <utils/Errors.h>
+
+static int sys_futex(void *addr1, int op, int val1, struct timespec *timeout, void *addr2, int val3)
+{
+#ifdef __linux__
+ return syscall(SYS_futex, addr1, op, val1, timeout, addr2, val3);
+#else
+ (void) addr1;
+ (void) op;
+ (void) val1;
+ (void) timeout;
+ (void) addr2;
+ (void) val3;
+ errno = ENOSYS;
+ return -1;
+#endif
+}
+
+audio_utils_fifo_base::audio_utils_fifo_base(uint32_t frameCount)
+ __attribute__((no_sanitize("integer"))) :
+ mFrameCount(frameCount), mFrameCountP2(roundup(frameCount)),
+ mFudgeFactor(mFrameCountP2 - mFrameCount),
+ mIsPrivate(true),
+ mSharedRear(0), mThrottleFront(NULL)
+{
+ // actual upper bound on frameCount will depend on the frame size
+ LOG_ALWAYS_FATAL_IF(frameCount == 0 || frameCount > ((uint32_t) INT_MAX));
+}
+
+audio_utils_fifo_base::~audio_utils_fifo_base()
+{
+}
+
+uint32_t audio_utils_fifo_base::sum(uint32_t index, uint32_t increment)
+ __attribute__((no_sanitize("integer")))
+{
+ if (mFudgeFactor) {
+ uint32_t mask = mFrameCountP2 - 1;
+ ALOG_ASSERT((index & mask) < mFrameCount);
+ ALOG_ASSERT(increment <= mFrameCountP2);
+ if ((index & mask) + increment >= mFrameCount) {
+ increment += mFudgeFactor;
+ }
+ index += increment;
+ ALOG_ASSERT((index & mask) < mFrameCount);
+ return index;
+ } else {
+ return index + increment;
+ }
+}
+
+int32_t audio_utils_fifo_base::diff(uint32_t rear, uint32_t front, size_t *lost)
+ __attribute__((no_sanitize("integer")))
+{
+ uint32_t diff = rear - front;
+ if (mFudgeFactor) {
+ uint32_t mask = mFrameCountP2 - 1;
+ uint32_t rearMasked = rear & mask;
+ uint32_t frontMasked = front & mask;
+ if (rearMasked >= mFrameCount || frontMasked >= mFrameCount) {
+ return -EIO;
+ }
+ uint32_t genDiff = (rear & ~mask) - (front & ~mask);
+ if (genDiff != 0) {
+ if (genDiff > mFrameCountP2) {
+ if (lost != NULL) {
+ // TODO provide a more accurate estimate
+ *lost = (genDiff / mFrameCountP2) * mFrameCount;
+ }
+ return -EOVERFLOW;
+ }
+ diff -= mFudgeFactor;
+ }
+ }
+ // FIFO should not be overfull
+ if (diff > mFrameCount) {
+ if (lost != NULL) {
+ *lost = diff - mFrameCount;
+ }
+ return -EOVERFLOW;
+ }
+ return (int32_t) diff;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+audio_utils_fifo::audio_utils_fifo(uint32_t frameCount, uint32_t frameSize, void *buffer)
+ __attribute__((no_sanitize("integer"))) :
+ audio_utils_fifo_base(frameCount), mFrameSize(frameSize), mBuffer(buffer)
+{
+ // maximum value of frameCount * frameSize is INT_MAX (2^31 - 1), not 2^31, because we need to
+ // be able to distinguish successful and error return values from read and write.
+ LOG_ALWAYS_FATAL_IF(frameCount == 0 || frameSize == 0 || buffer == NULL ||
+ frameCount > ((uint32_t) INT_MAX) / frameSize);
+}
+
+audio_utils_fifo::~audio_utils_fifo()
+{
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+audio_utils_fifo_provider::audio_utils_fifo_provider() :
+ mObtained(0)
+{
+}
+
+audio_utils_fifo_provider::~audio_utils_fifo_provider()
+{
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+audio_utils_fifo_writer::audio_utils_fifo_writer(audio_utils_fifo& fifo) :
+ audio_utils_fifo_provider(), mFifo(fifo), mLocalRear(0),
+ mLowLevelArm(fifo.mFrameCount), mHighLevelTrigger(0), mArmed(false),
+ mEffectiveFrames(fifo.mFrameCount)
+{
+}
+
+audio_utils_fifo_writer::~audio_utils_fifo_writer()
+{
+}
+
+ssize_t audio_utils_fifo_writer::write(const void *buffer, size_t count, struct timespec *timeout)
+ __attribute__((no_sanitize("integer")))
+{
+ audio_utils_iovec iovec[2];
+ ssize_t availToWrite = obtain(iovec, count, timeout);
+ if (availToWrite > 0) {
+ memcpy((char *) mFifo.mBuffer + iovec[0].mOffset * mFifo.mFrameSize, buffer,
+ iovec[0].mLength * mFifo.mFrameSize);
+ if (iovec[1].mLength > 0) {
+ memcpy((char *) mFifo.mBuffer + iovec[1].mOffset * mFifo.mFrameSize,
+ (char *) buffer + (iovec[0].mLength * mFifo.mFrameSize),
+ iovec[1].mLength * mFifo.mFrameSize);
+ }
+ release(availToWrite);
+ }
+ return availToWrite;
+}
+
+ssize_t audio_utils_fifo_writer::obtain(audio_utils_iovec iovec[2], size_t count,
+ struct timespec *timeout)
+ __attribute__((no_sanitize("integer")))
+{
+ size_t availToWrite;
+ if (mFifo.mThrottleFront != NULL) {
+ uint32_t front;
+ for (;;) {
+ front = (uint32_t) atomic_load_explicit(mFifo.mThrottleFront,
+ std::memory_order_acquire);
+ int32_t filled = mFifo.diff(mLocalRear, front, NULL /*lost*/);
+ if (filled < 0) {
+ mObtained = 0;
+ return (ssize_t) filled;
+ }
+ availToWrite = mEffectiveFrames > (uint32_t) filled ?
+ mEffectiveFrames - (uint32_t) filled : 0;
+ // TODO pull out "count == 0"
+ if (count == 0 || availToWrite > 0 || timeout == NULL ||
+ (timeout->tv_sec == 0 && timeout->tv_nsec == 0)) {
+ break;
+ }
+ int err = sys_futex(mFifo.mThrottleFront,
+ mFifo.mIsPrivate ? FUTEX_WAIT_PRIVATE : FUTEX_WAIT, front, timeout, NULL, 0);
+ if (err < 0) {
+ switch (errno) {
+ case EWOULDBLOCK:
+ case EINTR:
+ case ETIMEDOUT:
+ break;
+ default:
+ LOG_ALWAYS_FATAL("unexpected err=%d errno=%d", err, errno);
+ break;
+ }
+ }
+ timeout = NULL;
+ }
+ } else {
+ availToWrite = mEffectiveFrames;
+ }
+ if (availToWrite > count) {
+ availToWrite = count;
+ }
+ uint32_t rearMasked = mLocalRear & (mFifo.mFrameCountP2 - 1);
+ size_t part1 = mFifo.mFrameCount - rearMasked;
+ if (part1 > availToWrite) {
+ part1 = availToWrite;
+ }
+ size_t part2 = part1 > 0 ? availToWrite - part1 : 0;
+ iovec[0].mOffset = rearMasked;
+ iovec[0].mLength = part1;
+ iovec[1].mOffset = 0;
+ iovec[1].mLength = part2;
+ mObtained = availToWrite;
+ return availToWrite;
+}
+
+void audio_utils_fifo_writer::release(size_t count)
+ __attribute__((no_sanitize("integer")))
+{
+ if (count > 0) {
+ LOG_ALWAYS_FATAL_IF(count > mObtained);
+ if (mFifo.mThrottleFront != NULL) {
+ uint32_t front = (uint32_t) atomic_load_explicit(mFifo.mThrottleFront,
+ std::memory_order_acquire);
+ int32_t filled = mFifo.diff(mLocalRear, front, NULL /*lost*/);
+ mLocalRear = mFifo.sum(mLocalRear, count);
+ atomic_store_explicit(&mFifo.mSharedRear, (uint_fast32_t) mLocalRear,
+ std::memory_order_release);
+ if (filled >= 0) {
+ if (filled + count <= mLowLevelArm) {
+ mArmed = true;
+ }
+ if (mArmed && filled + count >= mHighLevelTrigger) {
+ int err = sys_futex(&mFifo.mSharedRear,
+ mFifo.mIsPrivate ? FUTEX_WAKE_PRIVATE : FUTEX_WAKE,
+ INT_MAX /*waiters*/, NULL, NULL, 0);
+ // err is number of processes woken up
+ if (err < 0) {
+ LOG_ALWAYS_FATAL("%s: unexpected err=%d errno=%d", __func__, err, errno);
+ }
+ mArmed = false;
+ }
+ }
+ } else {
+ mLocalRear = mFifo.sum(mLocalRear, count);
+ atomic_store_explicit(&mFifo.mSharedRear, (uint_fast32_t) mLocalRear,
+ std::memory_order_release);
+ }
+ mObtained -= count;
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+audio_utils_fifo_reader::audio_utils_fifo_reader(audio_utils_fifo& fifo, bool throttlesWriter) :
+ audio_utils_fifo_provider(), mFifo(fifo), mLocalFront(0), mSharedFront(0),
+ mHighLevelArm(0), mLowLevelTrigger(mFifo.mFrameCount), mArmed(false)
+{
+ if (throttlesWriter) {
+ LOG_ALWAYS_FATAL_IF(fifo.mThrottleFront != NULL);
+ fifo.mThrottleFront = &mSharedFront;
+ }
+}
+
+audio_utils_fifo_reader::~audio_utils_fifo_reader()
+{
+ // TODO Need a way to pass throttle capability to the another reader, should one reader exit.
+ if (mFifo.mThrottleFront == &mSharedFront) {
+ mFifo.mThrottleFront = NULL;
+ }
+}
+
+ssize_t audio_utils_fifo_reader::read(void *buffer, size_t count, struct timespec *timeout,
+ size_t *lost)
+ __attribute__((no_sanitize("integer")))
+{
+ audio_utils_iovec iovec[2];
+ ssize_t availToRead = obtain(iovec, count, timeout, lost);
+ if (availToRead > 0) {
+ memcpy(buffer, (char *) mFifo.mBuffer + iovec[0].mOffset * mFifo.mFrameSize,
+ iovec[0].mLength * mFifo.mFrameSize);
+ if (iovec[1].mLength > 0) {
+ memcpy((char *) buffer + (iovec[0].mLength * mFifo.mFrameSize),
+ (char *) mFifo.mBuffer + iovec[1].mOffset * mFifo.mFrameSize,
+ iovec[1].mLength * mFifo.mFrameSize);
+ }
+ release(availToRead);
+ }
+ return availToRead;
+}
+
+ssize_t audio_utils_fifo_reader::obtain(audio_utils_iovec iovec[2], size_t count,
+ struct timespec *timeout)
+ __attribute__((no_sanitize("integer")))
+{
+ return obtain(iovec, count, timeout, NULL);
+}
+
+void audio_utils_fifo_reader::release(size_t count)
+ __attribute__((no_sanitize("integer")))
+{
+ if (count > 0) {
+ LOG_ALWAYS_FATAL_IF(count > mObtained);
+ if (mFifo.mThrottleFront == &mSharedFront) {
+ uint32_t rear = (uint32_t) atomic_load_explicit(&mFifo.mSharedRear,
+ std::memory_order_acquire);
+ int32_t filled = mFifo.diff(rear, mLocalFront, NULL /*lost*/);
+ mLocalFront = mFifo.sum(mLocalFront, count);
+ atomic_store_explicit(&mSharedFront, (uint_fast32_t) mLocalFront,
+ std::memory_order_release);
+ if (filled >= 0) {
+ if (filled - count >= mHighLevelArm) {
+ mArmed = true;
+ }
+ if (mArmed && filled - count <= mLowLevelTrigger) {
+ int err = sys_futex(&mFifo.mSharedRear,
+ mFifo.mIsPrivate ? FUTEX_WAKE_PRIVATE : FUTEX_WAKE,
+ 1 /*waiters*/, NULL, NULL, 0);
+ // err is number of processes woken up
+ if (err < 0 || err > 1) {
+ LOG_ALWAYS_FATAL("%s: unexpected err=%d errno=%d", __func__, err, errno);
+ }
+ mArmed = false;
+ }
+ }
+ } else {
+ mLocalFront = mFifo.sum(mLocalFront, count);
+ }
+ mObtained -= count;
+ }
+}
+
+ssize_t audio_utils_fifo_reader::obtain(audio_utils_iovec iovec[2], size_t count,
+ struct timespec *timeout, size_t *lost)
+ __attribute__((no_sanitize("integer")))
+{
+ uint32_t rear;
+ for (;;) {
+ rear = (uint32_t) atomic_load_explicit(&mFifo.mSharedRear,
+ std::memory_order_acquire);
+ // TODO pull out "count == 0"
+ if (count == 0 || rear != mLocalFront || timeout == NULL ||
+ (timeout->tv_sec == 0 && timeout->tv_nsec == 0)) {
+ break;
+ }
+ int err = sys_futex(&mFifo.mSharedRear, mFifo.mIsPrivate ? FUTEX_WAIT_PRIVATE : FUTEX_WAIT,
+ rear, timeout, NULL, 0);
+ if (err < 0) {
+ switch (errno) {
+ case EWOULDBLOCK:
+ case EINTR:
+ case ETIMEDOUT:
+ break;
+ default:
+ LOG_ALWAYS_FATAL("unexpected err=%d errno=%d", err, errno);
+ break;
+ }
+ }
+ timeout = NULL;
+ }
+ int32_t filled = mFifo.diff(rear, mLocalFront, lost);
+ if (filled < 0) {
+ if (filled == -EOVERFLOW) {
+ mLocalFront = rear;
+ }
+ mObtained = 0;
+ return (ssize_t) filled;
+ }
+ size_t availToRead = (size_t) filled;
+ if (availToRead > count) {
+ availToRead = count;
+ }
+ uint32_t frontMasked = mLocalFront & (mFifo.mFrameCountP2 - 1);
+ size_t part1 = mFifo.mFrameCount - frontMasked;
+ if (part1 > availToRead) {
+ part1 = availToRead;
+ }
+ size_t part2 = part1 > 0 ? availToRead - part1 : 0;
+ iovec[0].mOffset = frontMasked;
+ iovec[0].mLength = part1;
+ iovec[1].mOffset = 0;
+ iovec[1].mLength = part2;
+ mObtained = availToRead;
+ return availToRead;
+}
#ifndef ANDROID_AUDIO_FIFO_H
#define ANDROID_AUDIO_FIFO_H
+#include <atomic>
#include <stdlib.h>
-// FIXME use atomic_int_least32_t and new atomic operations instead of legacy Android ones
-// #include <stdatomic.h>
-
-#ifdef __cplusplus
-extern "C" {
+#ifndef __cplusplus
+#error C API is no longer supported
#endif
-// Single writer, single reader non-blocking FIFO.
-// Writer and reader must be in same process.
+// Base class for single writer, single-reader or multi-reader non-blocking FIFO.
+// The base class manipulates frame indices only, and has no knowledge of frame sizes or the buffer.
+
+class audio_utils_fifo_base {
+
+protected:
+ audio_utils_fifo_base(uint32_t frameCount);
+ /*virtual*/ ~audio_utils_fifo_base();
+
+/** Return a new index as the sum of a validated index and a specified increment.
+ *
+ * \param index Caller should supply a validated mFront or mRear.
+ * \param increment Value to be added to the index <= mFrameCount.
+ *
+ * \return the sum of index plus increment.
+ */
+ uint32_t sum(uint32_t index, uint32_t increment);
+
+/** Return the difference between two indices: rear - front.
+ *
+ * \param rear Caller should supply an unvalidated mRear.
+ * \param front Caller should supply an unvalidated mFront.
+ * \param lost If non-NULL, set to the approximate number of lost frames.
+ *
+ * \return the zero or positive difference <= mFrameCount, or a negative error code.
+ */
+ int32_t diff(uint32_t rear, uint32_t front, size_t *lost);
-// No user-serviceable parts within.
-struct audio_utils_fifo {
// These fields are const after initialization
- size_t mFrameCount; // max number of significant frames to be stored in the FIFO > 0
- size_t mFrameCountP2; // roundup(mFrameCount)
- size_t mFudgeFactor; // mFrameCountP2 - mFrameCount, the number of "wasted" frames after
- // the end of mBuffer. Only the indices are wasted, not any memory.
- size_t mFrameSize; // size of each frame in bytes
- void *mBuffer; // pointer to caller-allocated buffer of size mFrameCount frames
-
- volatile int32_t mFront; // frame index of first frame slot available to read, or read index
- volatile int32_t mRear; // frame index of next frame slot available to write, or write index
+ const uint32_t mFrameCount; // max number of significant frames to be stored in the FIFO > 0
+ const uint32_t mFrameCountP2; // roundup(mFrameCount)
+ const uint32_t mFudgeFactor; // mFrameCountP2 - mFrameCount, the number of "wasted" frames
+ // after the end of mBuffer. Only the indices are wasted, not any
+ // memory.
+
+ // TODO always true for now, will be extended later to support false
+ const bool mIsPrivate; // whether reader and writer virtual address spaces are the same
+
+ std::atomic_uint_fast32_t mSharedRear; // accessed by both sides using atomic operations
+
+ // Pointer to the mSharedFront of at most one reader that throttles the writer,
+ // or NULL for no throttling
+ std::atomic_uint_fast32_t *mThrottleFront;
};
+////////////////////////////////////////////////////////////////////////////////
+
+// Same as above, but understands frame sizes and knows about the buffer but does not own it.
+// Writer and reader must be in same process.
+
+class audio_utils_fifo : audio_utils_fifo_base {
+
+ friend class audio_utils_fifo_reader;
+ friend class audio_utils_fifo_writer;
+
+public:
+
/**
- * Initialize a FIFO object.
+ * Construct a FIFO object.
*
- * \param fifo Pointer to the FIFO object.
* \param frameCount Max number of significant frames to be stored in the FIFO > 0.
* If writes and reads always use the same count, and that count is a divisor of
* frameCount, then the writes and reads will never do a partial transfer.
- * \param frameSize Size of each frame in bytes.
+ * \param frameSize Size of each frame in bytes > 0, and frameSize * frameCount <= INT_MAX.
* \param buffer Pointer to a caller-allocated buffer of frameCount frames.
*/
-void audio_utils_fifo_init(struct audio_utils_fifo *fifo, size_t frameCount, size_t frameSize,
- void *buffer);
+ audio_utils_fifo(uint32_t frameCount, uint32_t frameSize, void *buffer);
-/**
- * De-initialize a FIFO object.
- *
- * \param fifo Pointer to the FIFO object.
- */
-void audio_utils_fifo_deinit(struct audio_utils_fifo *fifo);
+ /*virtual*/ ~audio_utils_fifo();
+
+private:
+
+ // These fields are const after initialization
+ const uint32_t mFrameSize; // size of each frame in bytes
+ void * const mBuffer; // pointer to caller-allocated buffer of size mFrameCount frames
+};
+
+// Describes one virtually contiguous fragment of a logically contiguous slice.
+// Compare to struct iovec for readv(2) and writev(2).
+struct audio_utils_iovec {
+ uint32_t mOffset; // in frames, relative to mBuffer, undefined if mLength == 0
+ uint32_t mLength; // in frames
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+// Based on frameworks/av/include/media/AudioBufferProvider.h
+class audio_utils_fifo_provider {
+public:
+ audio_utils_fifo_provider();
+ virtual ~audio_utils_fifo_provider();
+
+// The count is the maximum number of desired frames, not the minimum number of desired frames.
+// See the high/low setpoints for something which is close to, but not the same as, a true minimum.
+
+// The timeout indicates the maximum time to wait for at least one frame, not for all frames.
+// NULL is equivalent to non-blocking.
+
+// Error codes for ssize_t return value:
+// -EIO corrupted indices (reader or writer)
+// -EOVERFLOW reader is not keeping up with writer (reader only)
+ virtual ssize_t obtain(audio_utils_iovec iovec[2], size_t count, struct timespec *timeout) = 0;
+
+ virtual void release(size_t count) = 0;
+
+protected:
+ // Number of frames obtained at most recent obtain(), less number of frames released
+ uint32_t mObtained;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+class audio_utils_fifo_writer : public audio_utils_fifo_provider {
+
+public:
+ audio_utils_fifo_writer(audio_utils_fifo& fifo);
+ virtual ~audio_utils_fifo_writer();
/**
* Write to FIFO.
*
- * \param fifo Pointer to the FIFO object.
- * \param buffer Pointer to source buffer containing 'count' frames of data.
- * \param count Desired number of frames to write.
+ * \param buffer Pointer to source buffer containing 'count' frames of data.
+ * \param count Desired number of frames to write.
+ * \param timeout NULL and zero fields are both non-blocking.
*
* \return actual number of frames written <= count.
*
* The actual transfer count may be zero if the FIFO is full,
* or partial if the FIFO was almost full.
- * A negative return value indicates an error. Currently there are no errors defined.
+ * A negative return value indicates an error.
*/
-ssize_t audio_utils_fifo_write(struct audio_utils_fifo *fifo, const void *buffer, size_t count);
+ ssize_t write(const void *buffer, size_t count, struct timespec *timeout = NULL);
+
+ // Implement audio_utils_fifo_provider
+ virtual ssize_t obtain(audio_utils_iovec iovec[2], size_t count, struct timespec *timeout);
+ virtual void release(size_t count);
+
+ // TODO add error checks and getters
+ void setHighLevelTrigger(uint32_t level) { mHighLevelTrigger = level; }
+ void setEffectiveFrames(uint32_t effectiveFrames) { mEffectiveFrames = effectiveFrames; }
+
+private:
+ audio_utils_fifo& mFifo;
+
+ // Accessed by writer only using ordinary operations
+ uint32_t mLocalRear; // frame index of next frame slot available to write, or write index
+
+ uint32_t mLowLevelArm; // arm if filled <= threshold
+ uint32_t mHighLevelTrigger; // trigger reader if armed and filled >= threshold
+ bool mArmed;
+
+ uint32_t mEffectiveFrames; // current effective buffer size, <= mFifo.mFrameCount
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+class audio_utils_fifo_reader : public audio_utils_fifo_provider {
+
+public:
+ audio_utils_fifo_reader(audio_utils_fifo& fifo, bool throttlesWriter);
+ virtual ~audio_utils_fifo_reader();
/** Read from FIFO.
*
- * \param fifo Pointer to the FIFO object.
- * \param buffer Pointer to destination buffer to be filled with up to 'count' frames of data.
- * \param count Desired number of frames to read.
+ * \param buffer Pointer to destination buffer to be filled with up to 'count' frames of data.
+ * \param count Desired number of frames to read.
+ * \param timeout NULL and zero fields are both non-blocking.
+ * \param lost If non-NULL, set to the approximate number of lost frames before re-sync.
*
* \return actual number of frames read <= count.
*
* The actual transfer count may be zero if the FIFO is empty,
* or partial if the FIFO was almost empty.
- * A negative return value indicates an error. Currently there are no errors defined.
+ * A negative return value indicates an error.
*/
-ssize_t audio_utils_fifo_read(struct audio_utils_fifo *fifo, void *buffer, size_t count);
+ ssize_t read(void *buffer, size_t count, struct timespec *timeout = NULL, size_t *lost = NULL);
-#ifdef __cplusplus
-}
-#endif
+ // Implement audio_utils_fifo_provider
+ virtual ssize_t obtain(audio_utils_iovec iovec[2], size_t count, struct timespec *timeout);
+ virtual void release(size_t count);
+
+ // Extended parameter list for reader only
+ ssize_t obtain(audio_utils_iovec iovec[2], size_t count, struct timespec *timeout,
+ size_t *lost);
+
+private:
+ audio_utils_fifo& mFifo;
+
+ // Accessed by reader only using ordinary operations
+ uint32_t mLocalFront; // frame index of first frame slot available to read, or read index
+
+ // Accessed by a throttling reader and writer using atomic operations
+ std::atomic_uint_fast32_t mSharedFront;
+
+ uint32_t mHighLevelArm; // arm if filled >= threshold
+ uint32_t mLowLevelTrigger; // trigger writer if armed and filled <= threshold
+ bool mArmed;
+};
#endif // !ANDROID_AUDIO_FIFO_H
include $(BUILD_HOST_EXECUTABLE)
include $(CLEAR_VARS)
+# TODO move getch.c and .h to a utility library
+LOCAL_SRC_FILES := fifo_threads.cpp getch.c
+LOCAL_MODULE := fifo_threads
+LOCAL_C_INCLUDES := $(call include-path-for, audio-utils)
+LOCAL_STATIC_LIBRARIES := libaudioutils liblog
+LOCAL_CFLAGS := -Werror -Wall
+include $(BUILD_HOST_EXECUTABLE)
+
+include $(CLEAR_VARS)
LOCAL_SRC_FILES := limiter_tests.c
LOCAL_MODULE := limiter_tests
LOCAL_C_INCLUDES := $(call include-path-for, audio-utils)
LOCAL_STATIC_LIBRARIES := libaudioutils
-LOCAL_CFLAGS := -Werror -Wall
+LOCAL_CFLAGS := -Werror -Wall -UNDEBUG
include $(BUILD_HOST_EXECUTABLE)
// Test program for audio_utils FIFO library.
// This only tests the single-threaded aspects, not the barriers.
+#include <errno.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include <audio_utils/fifo.h>
#include <audio_utils/sndfile.h>
+#ifndef min
+#define min(x, y) (((x) < (y)) ? (x) : (y))
+#endif
+
int main(int argc, char **argv)
{
- size_t frameCount = 256;
- size_t maxFramesPerRead = 1;
- size_t maxFramesPerWrite = 1;
+ size_t frameCount = 0;
+ size_t maxFramesPerRead = 0;
+ size_t maxFramesPerWrite = 0;
+ bool readerThrottlesWriter = true;
int i;
for (i = 1; i < argc; i++) {
char *arg = argv[i];
if (arg[0] != '-')
break;
switch (arg[1]) {
- case 'c': // FIFO frame count
+ case 'f': // FIFO frame count
frameCount = atoi(&arg[2]);
break;
case 'r': // maximum frame count per read from FIFO
maxFramesPerRead = atoi(&arg[2]);
break;
+ case 't': // disable throttling of writer by reader
+ readerThrottlesWriter = false;
+ break;
case 'w': // maximum frame count per write to FIFO
maxFramesPerWrite = atoi(&arg[2]);
break;
goto usage;
}
}
+ if (frameCount == 0) {
+ frameCount = 256;
+ }
+ if (maxFramesPerRead == 0) {
+ maxFramesPerRead = frameCount;
+ }
+ if (maxFramesPerWrite == 0) {
+ maxFramesPerWrite = frameCount;
+ }
if (argc - i != 2) {
usage:
- fprintf(stderr, "usage: %s [-c#] in.wav out.wav\n", argv[0]);
+ fprintf(stderr, "usage: %s [-f#] [-r#] [-t] [-w#] in.wav out.wav\n", argv[0]);
return EXIT_FAILURE;
}
char *inputFile = argv[i];
perror(inputFile);
return EXIT_FAILURE;
}
- // sf_readf_short() does conversion, so not strictly necessary to check the file format.
- // But I want to do "cmp" on input and output files afterwards,
- // and it is easier if they are all the same format.
- // Enforcing that everything is 16-bit is convenient for this.
- if ((sfinfoin.format & (SF_FORMAT_TYPEMASK | SF_FORMAT_SUBMASK)) !=
- (SF_FORMAT_WAV | SF_FORMAT_PCM_16)) {
+ switch (sfinfoin.format & (SF_FORMAT_TYPEMASK | SF_FORMAT_SUBMASK)) {
+ case SF_FORMAT_WAV | SF_FORMAT_PCM_16:
+ case SF_FORMAT_WAV | SF_FORMAT_PCM_U8:
+ break;
+ default:
fprintf(stderr, "%s: unsupported format\n", inputFile);
sf_close(sfin);
return EXIT_FAILURE;
short *outputBuffer = new short[sfinfoin.frames * sfinfoin.channels];
size_t framesWritten = 0;
size_t framesRead = 0;
- struct audio_utils_fifo fifo;
short *fifoBuffer = new short[frameCount * sfinfoin.channels];
- audio_utils_fifo_init(&fifo, frameCount, frameSize, fifoBuffer);
+ audio_utils_fifo fifo(frameCount, frameSize, fifoBuffer);
+ audio_utils_fifo_writer fifoWriter(fifo);
+ audio_utils_fifo_reader fifoReader(fifo, readerThrottlesWriter);
int fifoWriteCount = 0, fifoReadCount = 0;
int fifoFillLevel = 0, minFillLevel = INT_MAX, maxFillLevel = INT_MIN;
for (;;) {
size_t framesToWrite = sfinfoin.frames - framesWritten;
size_t framesToRead = sfinfoin.frames - framesRead;
- if (framesToWrite == 0 && framesToRead == 0) {
+ if (framesToWrite == 0 && (framesToRead == 0 || !readerThrottlesWriter)) {
break;
}
framesToWrite = maxFramesPerWrite;
}
framesToWrite = rand() % (framesToWrite + 1);
- ssize_t actualWritten = audio_utils_fifo_write(&fifo,
+ ssize_t actualWritten = fifoWriter.write(
&inputBuffer[framesWritten * sfinfoin.channels], framesToWrite);
+ //printf("wrote %d out of %d\n", (int) actualWritten, (int) framesToWrite);
if (actualWritten < 0 || (size_t) actualWritten > framesToWrite) {
fprintf(stderr, "write to FIFO failed\n");
break;
}
+ if (actualWritten < min((int) frameCount - fifoFillLevel, (int) framesToWrite)) {
+ fprintf(stderr, "only wrote %d when should have written min(%d, %d)\n",
+ (int) actualWritten, (int) frameCount - fifoFillLevel, (int) framesToWrite);
+ }
framesWritten += actualWritten;
if (actualWritten > 0) {
fifoWriteCount++;
fifoFillLevel += actualWritten;
if (fifoFillLevel > maxFillLevel) {
maxFillLevel = fifoFillLevel;
- if (maxFillLevel > (int) frameCount)
- abort();
+ if (maxFillLevel > (int) frameCount) {
+ if (readerThrottlesWriter) {
+ printf("maxFillLevel=%d > frameCount=%d\n", maxFillLevel, (int) frameCount);
+ abort();
+ }
+ }
}
if (framesToRead > maxFramesPerRead) {
framesToRead = maxFramesPerRead;
}
framesToRead = rand() % (framesToRead + 1);
- ssize_t actualRead = audio_utils_fifo_read(&fifo,
+ ssize_t actualRead = fifoReader.read(
&outputBuffer[framesRead * sfinfoin.channels], framesToRead);
+ //printf("read %d out of %d\n", (int) actualRead, (int) framesToRead);
if (actualRead < 0 || (size_t) actualRead > framesToRead) {
- fprintf(stderr, "read from FIFO failed\n");
- break;
+ switch (actualRead) {
+ case -EIO:
+ fprintf(stderr, "read from FIFO failed: corrupted indices\n");
+ abort();
+ break;
+ case -EOVERFLOW:
+ if (readerThrottlesWriter) {
+ fprintf(stderr, "read from FIFO failed: unexpected overflow\n");
+ abort();
+ }
+ printf("warning: reader lost frames\n");
+ actualRead = 0;
+ break;
+ default:
+ if (actualRead < 0) {
+ fprintf(stderr, "read from FIFO failed: unexpected error code %d\n",
+ (int) actualRead);
+ } else {
+ fprintf(stderr, "read from FIFO failed: actualRead=%d > framesToRead=%d\n",
+ (int) actualRead, (int) framesToRead);
+ }
+ abort();
+ }
+ }
+ if (actualRead < min(fifoFillLevel, (int) framesToRead)) {
+ //fprintf(stderr, "only read %d when should have read min(%d, %d)\n",
+ // (int) actualRead, fifoFillLevel, (int) framesToRead);
}
framesRead += actualRead;
if (actualRead > 0) {
fifoFillLevel -= actualRead;
if (fifoFillLevel < minFillLevel) {
minFillLevel = fifoFillLevel;
- if (minFillLevel < 0)
+ if (minFillLevel < 0) {
+ printf("minFillLevel=%d < 0\n", minFillLevel);
abort();
+ }
}
}
+ delete[] inputBuffer;
+ inputBuffer = NULL;
+ delete[] fifoBuffer;
+ fifoBuffer = NULL;
+
printf("FIFO non-empty writes: %d, non-empty reads: %d\n", fifoWriteCount, fifoReadCount);
printf("fill=%d, min=%d, max=%d\n", fifoFillLevel, minFillLevel, maxFillLevel);
- audio_utils_fifo_deinit(&fifo);
- delete[] fifoBuffer;
SF_INFO sfinfoout;
memset(&sfinfoout, 0, sizeof(sfinfoout));
return EXIT_FAILURE;
}
sf_count_t actualWritten = sf_writef_short(sfout, outputBuffer, framesRead);
- delete[] inputBuffer;
delete[] outputBuffer;
- delete[] fifoBuffer;
+ outputBuffer = NULL;
+
if (actualWritten != (sf_count_t) framesRead) {
fprintf(stderr, "%s: unexpected error\n", outputFile);
sf_close(sfout);
--- /dev/null
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <audio_utils/fifo.h>
+extern "C" {
+#include "getch.h"
+}
+
+struct Context {
+ audio_utils_fifo_writer *mInputWriter;
+ audio_utils_fifo_reader *mInputReader;
+ audio_utils_fifo_writer *mTransferWriter;
+ audio_utils_fifo_reader *mTransferReader;
+ audio_utils_fifo_writer *mOutputWriter;
+ audio_utils_fifo_reader *mOutputReader;
+};
+
+void *input_routine(void *arg)
+{
+ Context *context = (Context *) arg;
+ for (;;) {
+ struct timespec timeout;
+ timeout.tv_sec = 3;
+ timeout.tv_nsec = 0;
+ char buffer[4];
+ ssize_t actual = context->mInputReader->read(buffer, sizeof(buffer), &timeout);
+ if (actual == 0) {
+ (void) write(1, "t", 1);
+ } else if (actual > 0) {
+ actual = context->mTransferWriter->write(buffer, actual, &timeout);
+ //printf("transfer.write actual = %d\n", (int) actual);
+ } else {
+ printf("input.read actual = %d\n", (int) actual);
+ }
+ }
+ return NULL;
+}
+
+void *output_routine(void *arg)
+{
+ Context *context = (Context *) arg;
+ for (;;) {
+ struct timespec timeout;
+ timeout.tv_sec = 5;
+ timeout.tv_nsec = 0;
+ char buffer[4];
+ ssize_t actual = context->mTransferReader->read(buffer, sizeof(buffer), &timeout);
+ if (actual == 0) {
+ (void) write(1, "T", 1);
+ } else if (actual > 0) {
+ actual = context->mOutputWriter->write(buffer, actual, &timeout);
+ //printf("output.write actual = %d\n", (int) actual);
+ } else {
+ printf("transfer.read actual = %d\n", (int) actual);
+ }
+ }
+ return NULL;
+}
+
+int main(int argc, char **argv)
+{
+ set_conio_terminal_mode();
+ argc = argc + 0;
+ argv = &argv[0];
+
+ char inputBuffer[64];
+ audio_utils_fifo inputFifo(sizeof(inputBuffer) /*frameCount*/, 1 /*frameSize*/, inputBuffer);
+ audio_utils_fifo_writer inputWriter(inputFifo);
+ audio_utils_fifo_reader inputReader(inputFifo, true /*readerThrottlesWriter*/);
+ inputWriter.setHighLevelTrigger(3);
+
+ char transferBuffer[64];
+ audio_utils_fifo transferFifo(sizeof(transferBuffer) /*frameCount*/, 1 /*frameSize*/,
+ transferBuffer);
+ audio_utils_fifo_writer transferWriter(transferFifo);
+ audio_utils_fifo_reader transferReader(transferFifo, true /*readerThrottlesWriter*/);
+ transferWriter.setEffectiveFrames(2);
+
+ char outputBuffer[64];
+ audio_utils_fifo outputFifo(sizeof(outputBuffer) /*frameCount*/, 1 /*frameSize*/, outputBuffer);
+ audio_utils_fifo_writer outputWriter(outputFifo);
+ audio_utils_fifo_reader outputReader(outputFifo, true /*readerThrottlesWriter*/);
+
+ Context context;
+ context.mInputWriter = &inputWriter;
+ context.mInputReader = &inputReader;
+ context.mTransferWriter = &transferWriter;
+ context.mTransferReader = &transferReader;
+ context.mOutputWriter = &outputWriter;
+ context.mOutputReader = &outputReader;
+
+ pthread_t input_thread;
+ int ok = pthread_create(&input_thread, (const pthread_attr_t *) NULL, input_routine,
+ (void *) &context);
+ pthread_t output_thread;
+ ok = pthread_create(&output_thread, (const pthread_attr_t *) NULL, output_routine,
+ (void *) &context);
+ ok = ok + 0;
+
+ for (;;) {
+ char buffer[1];
+ struct timespec timeout;
+ timeout.tv_sec = 0;
+ timeout.tv_nsec = 0;
+ ssize_t actual = outputReader.read(buffer, sizeof(buffer), &timeout);
+ if (actual == 1) {
+ printf("%c", buffer[0]);
+ fflush(stdout);
+ } else if (actual != 0) {
+ printf("outputReader.read actual = %d\n", (int) actual);
+ }
+ if (kbhit()) {
+ int ch = getch();
+ if (ch <= 0 || ch == 3) {
+ break;
+ }
+ buffer[0] = ch;
+ actual = inputWriter.write(buffer, sizeof(buffer), &timeout);
+ if (actual != 1) {
+ printf("inputWriter.write actual = %d\n", (int) actual);
+ }
+ }
+ }
+ reset_terminal_mode();
+}
--- /dev/null
+../../../../frameworks/wilhelm/tests/sandbox/getch.c
\ No newline at end of file
--- /dev/null
+../../../../frameworks/wilhelm/tests/sandbox/getch.h
\ No newline at end of file