comparison src/alsa-ng/alsa-ringbuffer.c @ 3162:e387614b9be9

alsa-ng: Import rewritten ALSA plugin. This is still woefully incomplete, but supports basic playback. This driver uses the "safe" ALSA API subset, including use of blocking I/O. Right now, it is hardcoded to use "default". Do not complain about bugs in this plugin.
author William Pitcock <nenolod@atheme.org>
date Thu, 14 May 2009 21:05:11 -0500
parents
children
comparison
equal deleted inserted replaced
3161:6dd886b5c72b 3162:e387614b9be9
1 /*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15 */
16
17 /*
18 * Ringbuffer implementation
19 *
20 * GPL
21 */
22 #include <string.h>
23 #include "alsa-ringbuffer.h"
24 #include "alsa-debug.h"
25
26 #ifdef ALSAPLUG_RINGBUFFER_DEBUG
27 /*
28 * An internal assertion function to make sure that the
29 * ringbuffer structure is consistient.
30 *
31 * WARNING: This function will call abort() if the ringbuffer
32 * is found to be inconsistient.
33 */
34 static void _alsaplug_ringbuffer_assert(alsaplug_ringbuf_t* rb) {
35
36 unsigned int realused;
37
38 _ENTER;
39
40 _DEBUG("rb->buf=%p, rb->end=%p, rb->wp=%p, rb->rp=%p, rb->free=%u, rb->used=%u, rb->size=%u",
41 rb->buf, rb->end, rb->wp, rb->rp, rb->free, rb->used, rb->size);
42
43 if (0 == rb->size) {
44 _ERROR("Buffer size is 0");
45 abort();
46 }
47
48 if (NULL == rb->buf) {
49 _ERROR("Buffer start is NULL");
50 abort();
51 }
52
53 if (rb->used+rb->free != rb->size) {
54 _ERROR("rb->free and rb->used do not add up to rb->size");
55 abort();
56 }
57
58 if (rb->buf+(rb->size-1) != rb->end) {
59 _ERROR("rb->buf and rb->end not rb->size bytes apart");
60 abort();
61 }
62
63 if ((rb->wp < rb->buf) || (rb->wp > rb->end)) {
64 _ERROR("Write pointer outside buffer space");
65 abort();
66 }
67
68 if ((rb->rp < rb->buf) || (rb->rp > rb->end)) {
69 _ERROR("Read pointer outside buffer space");
70 abort();
71 }
72
73 if (rb->rp <= rb->wp) {
74 realused = rb->wp - rb->rp;
75 } else {
76 realused = (rb->end - rb->rp) + 1 + (rb->wp-rb->buf);
77 }
78
79 if (rb->used != realused) {
80 _ERROR("Usage count is inconsistient (is %d, should be %d)", rb->used, realused);
81 abort();
82 }
83
84 _LEAVE;
85 }
86 #endif
87
88 /*
89 * Reset a ringbuffer structure (i.e. discard
90 * all data inside of it)
91 */
92 void alsaplug_ringbuffer_reset(alsaplug_ringbuf_t* rb) {
93
94 _ENTER;
95
96 _ALSAPLUG_RINGBUFFER_LOCK(rb->lock);
97
98 rb->wp = rb->buf;
99 rb->rp = rb->buf;
100 rb->free = rb->size;
101 rb->used = 0;
102 rb->end = rb->buf+(rb->size-1);
103
104 _ALSAPLUG_RINGBUFFER_UNLOCK(rb->lock);
105
106 _LEAVE;
107 }
108
109 /*
110 * Initialize a ringbuffer structure (including
111 * memory allocation.
112 *
113 * Return -1 on error
114 */
115 int alsaplug_ringbuffer_init(alsaplug_ringbuf_t* rb, unsigned int size) {
116
117 _ENTER;
118
119 if (0 == size) {
120 _LEAVE -1;
121 }
122
123 if (NULL == (rb->buf = malloc(size))) {
124 _LEAVE -1;
125 }
126 rb->size = size;
127
128 if (NULL == (rb->lock = g_mutex_new())) {
129 _LEAVE -1;
130 }
131
132 rb->_free_lock = 1;
133
134 alsaplug_ringbuffer_reset(rb);
135
136 ASSERT_RB(rb);
137
138 _LEAVE 0;
139 }
140
141 /*
142 * Initialize a ringbuffer structure (including
143 * memory allocation.
144 * The mutex to be used is passed in the function call.
145 * The mutex must not be held while calling this function.
146 *
147 * Return -1 on error
148 */
149 int alsaplug_ringbuffer_init_with_lock(alsaplug_ringbuf_t* rb, unsigned int size, alsaplug_ringbuffer_mutex_t* lock) {
150
151 _ENTER;
152
153 if (0 == size) {
154 _LEAVE -1;
155 }
156
157 rb->lock = lock;
158 rb->_free_lock = 0;
159
160 if (NULL == (rb->buf = malloc(size))) {
161 _LEAVE -1;
162 }
163 rb->size = size;
164 alsaplug_ringbuffer_reset(rb);
165
166 ASSERT_RB(rb);
167
168 _LEAVE 0;
169 }
170
171 /*
172 * Write size bytes at buf into the ringbuffer.
173 * Return -1 on error (not enough space in buffer)
174 */
175 int alsaplug_ringbuffer_write(alsaplug_ringbuf_t* rb, void* buf, unsigned int size) {
176
177 int ret = -1;
178 int endfree;
179
180 _ENTER;
181
182 _ALSAPLUG_RINGBUFFER_LOCK(rb->lock);
183
184 ASSERT_RB(rb);
185
186 if (rb->free < size) {
187 ret = -1;
188 goto out;
189 }
190
191 endfree = (rb->end - rb->wp)+1;
192 if (endfree < size) {
193 /*
194 * There is enough space in the buffer, but not in
195 * one piece. We need to split the copy into two parts.
196 */
197 memcpy(rb->wp, buf, endfree);
198 memcpy(rb->buf, buf+endfree, size-endfree);
199 rb->wp = rb->buf + (size-endfree);
200 } else if (endfree > size) {
201 /*
202 * There is more space than needed at the end
203 */
204 memcpy(rb->wp, buf, size);
205 rb->wp += size;
206 } else {
207 /*
208 * There is exactly the space needed at the end.
209 * We need to wrap around the read pointer.
210 */
211 memcpy(rb->wp, buf, size);
212 rb->wp = rb->buf;
213 }
214
215 rb->free -= size;
216 rb->used += size;
217
218 ret = 0;
219
220 out:
221 ASSERT_RB(rb);
222 _ALSAPLUG_RINGBUFFER_UNLOCK(rb->lock);
223
224 _LEAVE ret;
225 }
226
227 /*
228 * Read size byes from buffer into buf.
229 * Return -1 on error (not enough data in buffer)
230 */
231 int alsaplug_ringbuffer_read(alsaplug_ringbuf_t* rb, void* buf, unsigned int size) {
232
233 int ret;
234
235 _ENTER;
236
237 _ALSAPLUG_RINGBUFFER_LOCK(rb->lock);
238 ret = alsaplug_ringbuffer_read_locked(rb, buf, size);
239 _ALSAPLUG_RINGBUFFER_UNLOCK(rb->lock);
240
241 _LEAVE ret;
242 }
243
244 /*
245 * Read size bytes from buffer into buf, assuming the buffer lock
246 * is already held.
247 * Return -1 on error (not enough data in buffer)
248 */
249 int alsaplug_ringbuffer_read_locked(alsaplug_ringbuf_t* rb, void* buf, unsigned int size) {
250
251 int endused;
252
253 _ENTER;
254
255 ASSERT_RB(rb);
256
257 if (rb->used < size) {
258 /* Not enough bytes in buffer */
259 _LEAVE -1;
260 }
261
262 if (rb->rp < rb->wp) {
263 /*
264 Read pointer is behind write pointer, all the data is available in one chunk
265 */
266 memcpy(buf, rb->rp, size);
267 rb->rp += size;
268 } else {
269 /*
270 * Read pointer is before write pointer
271 */
272 endused = (rb->end - rb->rp)+1;
273
274 if (size < endused) {
275 /*
276 * Data is available in one chunk
277 */
278 memcpy(buf, rb->rp, size);
279 rb->rp += size;
280 } else {
281 /*
282 * There is enough data in the buffer, but it is fragmented.
283 */
284 memcpy(buf, rb->rp, endused);
285 memcpy(buf+endused, rb->buf, size-endused);
286 rb->rp = rb->buf + (size-endused);
287 }
288 }
289
290 rb->free += size;
291 rb->used -= size;
292
293 ASSERT_RB(rb);
294
295 _LEAVE 0;
296 }
297
298 /*
299 * Return the amount of free space currently in the rb
300 */
301 unsigned int alsaplug_ringbuffer_free(alsaplug_ringbuf_t* rb) {
302
303 unsigned int f;
304
305 _ENTER;
306
307 _ALSAPLUG_RINGBUFFER_LOCK(rb->lock);
308 f = alsaplug_ringbuffer_free_locked(rb);
309 _ALSAPLUG_RINGBUFFER_UNLOCK(rb->lock);
310
311 _LEAVE f;
312 }
313
314 /*
315 * Return the amount of free space currently in the rb.
316 * Assume the rb lock is already being held.
317 */
318 unsigned int alsaplug_ringbuffer_free_locked(alsaplug_ringbuf_t* rb) {
319
320 _ENTER;
321
322 _LEAVE rb->free;
323 }
324
325
326 /*
327 * Return the amount of used space currently in the rb
328 */
329 unsigned int alsaplug_ringbuffer_used(alsaplug_ringbuf_t* rb) {
330
331 unsigned int u;
332
333 _ENTER;
334
335 _ALSAPLUG_RINGBUFFER_LOCK(rb->lock);
336 u = alsaplug_ringbuffer_used_locked(rb);
337 _ALSAPLUG_RINGBUFFER_UNLOCK(rb->lock);
338
339 _LEAVE u;
340 }
341
342 /*
343 * Return the amount of used space currently in the rb.
344 * Assume the rb lock is already being held.
345 */
346 unsigned int alsaplug_ringbuffer_used_locked(alsaplug_ringbuf_t* rb) {
347
348 _ENTER;
349
350 _LEAVE rb->used;
351 }
352
353
354 /*
355 * destroy a ringbuffer
356 */
357 void alsaplug_ringbuffer_destroy(alsaplug_ringbuf_t* rb) {
358
359 _ENTER;
360 free(rb->buf);
361 if (rb->_free_lock) {
362 g_mutex_free(rb->lock);
363 }
364
365 _LEAVE;
366 }