Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Fixed bug in producer helper, minor refactoring, updated readme

  • Loading branch information...
commit c986adcf3808ac39fd1fcb27592e2880180f2351 1 parent 420ca57
@michaeltyson michaeltyson authored
Showing with 31 additions and 44 deletions.
  1. +12 −8 README.markdown
  2. +13 −14 TPCircularBuffer.c
  3. +6 −22 TPCircularBuffer.h
View
20 README.markdown
@@ -1,21 +1,25 @@
A simple, fast circular buffer implementation for audio processing
==================================================================
-Circular buffers are pretty much what they sound like – arrays that wrap around. They’re fantastically useful as scratch space for audio processing, and generally passing audio around efficiently.
+A simple C implementation for a circular (ring) buffer. Thread-safe with a single producer and a single consumer, using the fast OSAtomic.h primitives.
-They work really well in a FIFO (first-in-first-out) context, like storing audio coming in the microphone for later playback or processing.
+Usage
+-----
-Consider a naive alternative: You copy the incoming audio into an NSData you allocate, and then pass that NSData off. This means you’re allocating memory each time, and deallocating the memory later once you’re done processing. That allocation incurs a penalty, which can be a show-stopper when part of an audio pipeline – The Core Audio documentation advises against any allocations when within a render callback, for example.
+Producing: Use TPCircularBufferProduce to put data into the buffer (starting at the offset indicated by TPCircularBufferHead, with TPCircularBufferSpace units of space available). TPCircularBufferProduceBytes is a convenience routine for putting in data that’s coming straight from a buffer.
-Alternatively, you can allocate space in advance, and write to that, but that has problems too: Either you have a synchronisation nightmare, or you spend lots of time moving bytes around so that the unprocessed audio is always at the beginning of the array.
+Consuming: Use TPCircularBufferConsume to pull data out the other end (starting from the offset indicated by TPCircularBufferTail, and of length indicated by TPCircularBufferFillCount).
-A better solution is to use a circular buffer, where data goes in at the head, and is read from the tail. When you produce data at the head, the head moves up the array, and wraps around at the end. When you consume at the tail, the tail moves up too, so the tail chases the head around the circle.
+Thread safety
+-------------
-This is a simple C implementation I recently put together.
+As long as you restrict multithreaded access to just one producer, and just one consumer, this utility should be thread safe.
-Use TPCircularBufferProduce to put data into the buffer (starting at the offset indicated by TPCircularBufferHead, with TPCircularBufferSpace units of space available), and TPCircularBufferConsume to pull it out the other end (starting from the offset indicated by TPCircularBufferTail, and of length indicated by TPCircularBufferFillCount).
+The routines under the heading "Reading" (in TPCircularBuffer.h) should only ever be used by one thread at a time; the same applies to those marked "Writing".
-TPCircularBufferCopy is a convenience routine for putting in data that’s coming straight from a buffer.
+Other routines, except for TPCircularBufferInit and TPCircularBufferClear, can be accessed concurrently without issue.
+
+-----------------------------------------------------
See more info at [atastypixel.com](http://atastypixel.com/blog/a-simple-fast-circular-buffer-implementation-for-audio-processing/)
View
27 TPCircularBuffer.c
@@ -14,7 +14,6 @@ static inline int min(int a, int b) {
return (a>b ? b : a);
}
-
inline void TPCircularBufferInit(TPCircularBufferRecord *record, int length) {
record->head = record->tail = record->fillCount = 0;
record->length = length;
@@ -49,17 +48,7 @@ inline void TPCircularBufferProduce(TPCircularBufferRecord *record, int amount)
OSAtomicAdd32Barrier(amount, &record->fillCount);
}
-inline void TPCircularBufferConsume(TPCircularBufferRecord *record, int amount) {
- record->tail = (record->tail + amount) % record->length;
- OSAtomicAdd32Barrier(-amount, &record->fillCount);
-}
-
-inline void TPCircularBufferClear(TPCircularBufferRecord *record) {
- record->tail = record->head;
- record->fillCount = 0;
-}
-
-inline int TPCircularBufferCopy(TPCircularBufferRecord *record, void* dst, const void* src, int count, int len) {
+inline int TPCircularBufferProduceBytes(TPCircularBufferRecord *record, void* dst, const void* src, int count, int len) {
int copied = 0;
while ( count > 0 ) {
int space = TPCircularBufferSpaceContiguous(record);
@@ -73,8 +62,18 @@ inline int TPCircularBufferCopy(TPCircularBufferRecord *record, void* dst, const
src += bytesToCopy;
count -= toCopy;
- copied += bytesToCopy;
+ copied += bytesToCopy/len;
TPCircularBufferProduce(record, toCopy);
}
return copied;
-}
+}
+
+inline void TPCircularBufferConsume(TPCircularBufferRecord *record, int amount) {
+ record->tail = (record->tail + amount) % record->length;
+ OSAtomicAdd32Barrier(-amount, &record->fillCount);
+}
+
+inline void TPCircularBufferClear(TPCircularBufferRecord *record) {
+ record->tail = record->head;
+ record->fillCount = 0;
+}
View
28 TPCircularBuffer.h
@@ -13,35 +13,19 @@ typedef struct {
int length;
} TPCircularBufferRecord;
-// Initialise record structure
void TPCircularBufferInit(TPCircularBufferRecord *record, int length);
+void TPCircularBufferClear(TPCircularBufferRecord *record);
-// Obtain fill count
int TPCircularBufferFillCount(TPCircularBufferRecord *record);
-
-// Obtain fill count for a contiguous area
int TPCircularBufferFillCountContiguous(TPCircularBufferRecord *record);
-
-// Obtain count of available space
int TPCircularBufferSpace(TPCircularBufferRecord *record);
-
-// Obtain count of available space for contiguous area
int TPCircularBufferSpaceContiguous(TPCircularBufferRecord *record);
-// Index of the head counter into the corresponding buffer
-int TPCircularBufferHead(TPCircularBufferRecord *record);
-
-// Index of the tail counter into the corresponding buffer
+// Reading (consuming)
int TPCircularBufferTail(TPCircularBufferRecord *record);
-
-// Produce data (by increasing the fill count and moving the head counter forwards)
-void TPCircularBufferProduce(TPCircularBufferRecord *record, int amount);
-
-// Consume data (by decreasing the fill count and moving the tail counter forwards)
void TPCircularBufferConsume(TPCircularBufferRecord *record, int amount);
-// Clear the whole buffer, resetting fill count to zero and pointing the tail counter to the head
-void TPCircularBufferClear(TPCircularBufferRecord *record);
-
-// Produce by copying the data ('count' units of 'len' length) in the buffer at 'src' into the circular buffer 'dst'
-int TPCircularBufferCopy(TPCircularBufferRecord *record, void* dst, const void* src, int count, int len);
+// Writing (producing)
+int TPCircularBufferHead(TPCircularBufferRecord *record);
+void TPCircularBufferProduce(TPCircularBufferRecord *record, int amount);
+int TPCircularBufferProduceBytes(TPCircularBufferRecord *record, void* dst, const void* src, int count, int len);
Please sign in to comment.
Something went wrong with that request. Please try again.