]> git.sesse.net Git - vlc/blob - modules/audio_output/TPCircularBuffer.h
opensles: implement mute/volume set
[vlc] / modules / audio_output / TPCircularBuffer.h
1 //
2 //  TPCircularBuffer.h
3 //  Circular/Ring buffer implementation
4 //
5 //  https://github.com/michaeltyson/TPCircularBuffer
6 //
7 //  Created by Michael Tyson on 10/12/2011.
8 //  Copyright 2011-2012 A Tasty Pixel. All rights reserved.
9 //
10 //
11 //  This implementation makes use of a virtual memory mapping technique that inserts a virtual copy
12 //  of the buffer memory directly after the buffer's end, negating the need for any buffer wrap-around
13 //  logic. Clients can simply use the returned memory address as if it were contiguous space.
14 //
15 //  The implementation is thread-safe in the case of a single producer and single consumer.
16 //
17 //  Virtual memory technique originally proposed by Philip Howard (http://vrb.slashusr.org/), and
18 //  adapted to Darwin by Kurt Revis (http://www.snoize.com,
19 //  http://www.snoize.com/Code/PlayBufferedSoundFile.tar.gz)
20 //
21
22 #ifndef TPCircularBuffer_h
23 #define TPCircularBuffer_h
24
25 #include <libkern/OSAtomic.h>
26 #include <string.h>
27
28 #ifdef __cplusplus
29 extern "C" {
30 #endif
31
32 typedef struct {
33     void             *buffer;
34     int32_t           length;
35     int32_t           tail;
36     int32_t           head;
37     volatile int32_t  fillCount;
38 } TPCircularBuffer;
39
40 /*!
41  * Initialise buffer
42  *
43  *  Note that the length is advisory only: Because of the way the
44  *  memory mirroring technique works, the true buffer length will
45  *  be multiples of the device page size (e.g. 4096 bytes)
46  *
47  * @param buffer Circular buffer
48  * @param length Length of buffer
49  */
50 bool  TPCircularBufferInit(TPCircularBuffer *buffer, int32_t length);
51
52 /*!
53  * Cleanup buffer
54  *
55  *  Releases buffer resources.
56  */
57 void  TPCircularBufferCleanup(TPCircularBuffer *buffer);
58
59 /*!
60  * Clear buffer
61  *
62  *  Resets buffer to original, empty state.
63  *
64  *  This is safe for use by consumer while producer is accessing
65  *  buffer.
66  */
67 void  TPCircularBufferClear(TPCircularBuffer *buffer);
68
69 // Reading (consuming)
70
71 /*!
72  * Access end of buffer
73  *
74  *  This gives you a pointer to the end of the buffer, ready
75  *  for reading, and the number of available bytes to read.
76  *
77  * @param buffer Circular buffer
78  * @param availableBytes On output, the number of bytes ready for reading
79  * @return Pointer to the first bytes ready for reading, or NULL if buffer is empty
80  */
81 static __inline__ __attribute__((always_inline)) void* TPCircularBufferTail(TPCircularBuffer *buffer, int32_t* availableBytes) {
82     *availableBytes = buffer->fillCount;
83     if ( *availableBytes == 0 ) return NULL;
84     return (void*)((char*)buffer->buffer + buffer->tail);
85 }
86
87 /*!
88  * Consume bytes in buffer
89  *
90  *  This frees up the just-read bytes, ready for writing again.
91  *
92  * @param buffer Circular buffer
93  * @param amount Number of bytes to consume
94  */
95 static __inline__ __attribute__((always_inline)) void TPCircularBufferConsume(TPCircularBuffer *buffer, int32_t amount) {
96     buffer->tail = (buffer->tail + amount) % buffer->length;
97     OSAtomicAdd32Barrier(-amount, &buffer->fillCount);
98 }
99
100 /*!
101  * Version of TPCircularBufferConsume without the memory barrier, for more optimal use in single-threaded contexts
102  */
103  static __inline__ __attribute__((always_inline)) void TPCircularBufferConsumeNoBarrier(TPCircularBuffer *buffer, int32_t amount) {
104     buffer->tail = (buffer->tail + amount) % buffer->length;
105     buffer->fillCount -= amount;
106 }
107
108 /*!
109  * Access front of buffer
110  *
111  *  This gives you a pointer to the front of the buffer, ready
112  *  for writing, and the number of available bytes to write.
113  *
114  * @param buffer Circular buffer
115  * @param availableBytes On output, the number of bytes ready for writing
116  * @return Pointer to the first bytes ready for writing, or NULL if buffer is full
117  */
118 static __inline__ __attribute__((always_inline)) void* TPCircularBufferHead(TPCircularBuffer *buffer, int32_t* availableBytes) {
119     *availableBytes = (buffer->length - buffer->fillCount);
120     if ( *availableBytes == 0 ) return NULL;
121     return (void*)((char*)buffer->buffer + buffer->head);
122 }
123
124 // Writing (producing)
125
126 /*!
127  * Produce bytes in buffer
128  *
129  *  This marks the given section of the buffer ready for reading.
130  *
131  * @param buffer Circular buffer
132  * @param amount Number of bytes to produce
133  */
134 static __inline__ __attribute__((always_inline)) void TPCircularBufferProduce(TPCircularBuffer *buffer, int amount) {
135     buffer->head = (buffer->head + amount) % buffer->length;
136     OSAtomicAdd32Barrier(amount, &buffer->fillCount);
137 }
138
139 /*!
140  * Version of TPCircularBufferProduce without the memory barrier, for more optimal use in single-threaded contexts
141  */
142 static __inline__ __attribute__((always_inline)) void TPCircularBufferProduceNoBarrier(TPCircularBuffer *buffer, int amount) {
143     buffer->head = (buffer->head + amount) % buffer->length;
144     buffer->fillCount += amount;
145 }
146
147 /*!
148  * Helper routine to copy bytes to buffer
149  *
150  *  This copies the given bytes to the buffer, and marks them ready for writing.
151  *
152  * @param buffer Circular buffer
153  * @param src Source buffer
154  * @param len Number of bytes in source buffer
155  * @return true if bytes copied, false if there was insufficient space
156  */
157 static __inline__ __attribute__((always_inline)) bool TPCircularBufferProduceBytes(TPCircularBuffer *buffer, const void* src, int32_t len) {
158     int32_t space;
159     void *ptr = TPCircularBufferHead(buffer, &space);
160     if ( space < len ) return false;
161     memcpy(ptr, src, len);
162     TPCircularBufferProduce(buffer, len);
163     return true;
164 }
165
166 #ifdef __cplusplus
167 }
168 #endif
169
170 #endif