1 /* Libvisual - The audio visualisation framework.
2 *
3 * Copyright (C) 2004, 2005, 2006 Dennis Smit <ds@nerds-incorporated.org>
4 *
5 * Authors: Dennis Smit <ds@nerds-incorporated.org>
6 *
7 * $Id: lv_ringbuffer.c,v 1.8 2006/01/22 13:23:37 synap Exp $
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU Lesser General Public License as
11 * published by the Free Software Foundation; either version 2.1
12 * of the License, or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 */
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <unistd.h>
27 #include <string.h>
28 #include <fcntl.h>
29 #include <gettext.h>
30
31 #include <lvconfig.h>
32 #include "lv_ringbuffer.h"
33 #include "lv_log.h"
34 #include "lv_mem.h"
35
36 static int ringbuffer_dtor (VisObject *object);
37 static int ringbuffer_entry_dtor (VisObject *object);
38
39 static int fixate_with_partial_data_request (VisRingBuffer *ringbuffer, VisBuffer *data, int offset, int nbytes,
40 int *buffercorr);
41
42
ringbuffer_dtor(VisObject * object)43 static int ringbuffer_dtor (VisObject *object)
44 {
45 VisRingBuffer *ringbuffer = VISUAL_RINGBUFFER (object);
46
47 if (ringbuffer->entries != NULL)
48 visual_object_unref (VISUAL_OBJECT (ringbuffer->entries));
49
50 ringbuffer->entries = NULL;
51
52 return VISUAL_OK;
53 }
54
ringbuffer_entry_dtor(VisObject * object)55 static int ringbuffer_entry_dtor (VisObject *object)
56 {
57 VisRingBufferEntry *entry = VISUAL_RINGBUFFER_ENTRY (object);
58
59 if (entry->type == VISUAL_RINGBUFFER_ENTRY_TYPE_BUFFER) {
60
61 if (entry->buffer != NULL)
62 visual_object_unref (VISUAL_OBJECT (entry->buffer));
63
64 } else if (entry->type == VISUAL_RINGBUFFER_ENTRY_TYPE_FUNCTION) {
65
66 if (entry->destroyfunc != NULL)
67 entry->destroyfunc (entry);
68 }
69
70 entry->type = VISUAL_RINGBUFFER_ENTRY_TYPE_NONE;
71 entry->datafunc = NULL;
72 entry->destroyfunc = NULL;
73 entry->sizefunc = NULL;
74 entry->buffer = NULL;
75 entry->functiondata = NULL;
76
77 return VISUAL_OK;
78 }
79
80 /**
81 * @defgroup VisRingBuffer VisRingBuffer
82 * @{
83 */
84
85 /**
86 * Creates a new VisRingBuffer structure.
87 * The VisRingBuffer system is a double linked ringbuffer implementation.
88 *
89 * @return A newly allocated VisRingBuffer.
90 */
visual_ringbuffer_new()91 VisRingBuffer *visual_ringbuffer_new ()
92 {
93 VisRingBuffer *ringbuffer;
94
95 ringbuffer = visual_mem_new0 (VisRingBuffer, 1);
96
97 visual_ringbuffer_init (ringbuffer);
98
99 /* Do the VisObject initialization */
100 visual_object_set_allocated (VISUAL_OBJECT (ringbuffer), TRUE);
101 visual_object_ref (VISUAL_OBJECT (ringbuffer));
102
103 return ringbuffer;
104 }
105
visual_ringbuffer_init(VisRingBuffer * ringbuffer)106 int visual_ringbuffer_init (VisRingBuffer *ringbuffer)
107 {
108 visual_log_return_val_if_fail (ringbuffer != NULL, -VISUAL_ERROR_RINGBUFFER_NULL);
109
110 /* Do the VisObject initialization */
111 visual_object_clear (VISUAL_OBJECT (ringbuffer));
112 visual_object_set_dtor (VISUAL_OBJECT (ringbuffer), ringbuffer_dtor);
113 visual_object_set_allocated (VISUAL_OBJECT (ringbuffer), FALSE);
114
115 /* Reset the VisRingBuffer structure */
116 ringbuffer->entries = visual_list_new (visual_object_collection_destroyer);
117
118 return VISUAL_OK;
119 }
120
121 /**
122 * Adds a VisRingBufferEntry to the end of the ringbuffer.
123 *
124 * @param ringbuffer The VisRingBuffer to which the VisRingBufferEntry is added.
125 * @param entry The VisRingBufferEntry that is added to the end of the ringbuffer.
126 *
127 * @return VISUAL_OK on succes, -VISUAL_ERROR_RINGBUFFER_NULL or -VISUAL_ERROR_RINGBUFFER_ENTRY_NULL
128 * on failure.
129 */
visual_ringbuffer_add_entry(VisRingBuffer * ringbuffer,VisRingBufferEntry * entry)130 int visual_ringbuffer_add_entry (VisRingBuffer *ringbuffer, VisRingBufferEntry *entry)
131 {
132 visual_log_return_val_if_fail (ringbuffer != NULL, -VISUAL_ERROR_RINGBUFFER_NULL);
133 visual_log_return_val_if_fail (entry != NULL, -VISUAL_ERROR_RINGBUFFER_ENTRY_NULL);
134
135 visual_list_add (ringbuffer->entries, entry);
136
137 return VISUAL_OK;
138 }
139
140 /**
141 * Adds a VisBuffer to the end of the ringbuffer.
142 *
143 * @param ringbuffer The VisRingBuffer to which the VisBuffer is added.
144 * @param buffer The VisBuffer that is added to the VisRingBuffer.
145 *
146 * @return VISUAL_OK on succes or -VISUAL_ERROR_RINGBUFFER_NULL, -VISUAL_ERROR_RINGBUFFER_ENTRY_NULL
147 * on failure.
148 */
visual_ringbuffer_add_buffer(VisRingBuffer * ringbuffer,VisBuffer * buffer)149 int visual_ringbuffer_add_buffer (VisRingBuffer *ringbuffer, VisBuffer *buffer)
150 {
151 VisRingBufferEntry *entry;
152
153 visual_log_return_val_if_fail (ringbuffer != NULL, -VISUAL_ERROR_RINGBUFFER_NULL);
154
155 entry = visual_ringbuffer_entry_new (buffer);
156
157 return visual_ringbuffer_add_entry (ringbuffer, entry);
158 }
159
160 /**
161 * Adds a portion of data to the ringbuffer of nbytes byte size.
162 *
163 * @param ringbuffer Pointer to the ringbuffer to which the data is added.
164 * @param data Pointer to the data that is added to the ringbuffer.
165 * @param nbytes The size of the data that is added to the ringbuffer.
166 *
167 * @return VISUAL_OK on succes or -VISUAL_ERROR_RINGBUFFER_NULL, -VISUAL_ERROR_NULL,
168 * -VISUAL_ERROR_RINGBUFFER_ENTRY_NULL on failure.
169 */
visual_ringbuffer_add_buffer_by_data(VisRingBuffer * ringbuffer,void * data,int nbytes)170 int visual_ringbuffer_add_buffer_by_data (VisRingBuffer *ringbuffer, void *data, int nbytes)
171 {
172 VisBuffer *buffer;
173
174 visual_log_return_val_if_fail (ringbuffer != NULL, -VISUAL_ERROR_RINGBUFFER_NULL);
175 visual_log_return_val_if_fail (data != NULL, -VISUAL_ERROR_NULL);
176
177 buffer = visual_buffer_new_with_buffer (data, nbytes, NULL);
178
179 return visual_ringbuffer_add_buffer (ringbuffer, buffer);
180 }
181
visual_ringbuffer_add_function(VisRingBuffer * ringbuffer,VisRingBufferDataFunc datafunc,VisRingBufferDestroyFunc destroyfunc,VisRingBufferSizeFunc sizefunc,void * functiondata)182 int visual_ringbuffer_add_function (VisRingBuffer *ringbuffer,
183 VisRingBufferDataFunc datafunc,
184 VisRingBufferDestroyFunc destroyfunc,
185 VisRingBufferSizeFunc sizefunc,
186 void *functiondata)
187 {
188 VisRingBufferEntry *entry;
189
190 visual_log_return_val_if_fail (ringbuffer != NULL, -VISUAL_ERROR_RINGBUFFER_NULL);
191 visual_log_return_val_if_fail (datafunc != NULL, -VISUAL_ERROR_RINGBUFFER_DATAFUNC_NULL);
192
193 entry = visual_ringbuffer_entry_new_function (datafunc, destroyfunc, sizefunc, functiondata);
194
195 return visual_ringbuffer_add_entry (ringbuffer, entry);
196 }
197
visual_ringbuffer_get_size(VisRingBuffer * ringbuffer)198 int visual_ringbuffer_get_size (VisRingBuffer *ringbuffer)
199 {
200 VisListEntry *le = NULL;
201 VisRingBufferEntry *entry;
202 int totalsize = 0;
203
204 visual_log_return_val_if_fail (ringbuffer != NULL, -VISUAL_ERROR_RINGBUFFER_NULL);
205
206 while ((entry = visual_list_next (ringbuffer->entries, &le)) != NULL) {
207 int bsize = 0;
208
209 if (entry->type == VISUAL_RINGBUFFER_ENTRY_TYPE_BUFFER) {
210
211 if ((bsize = visual_buffer_get_size (entry->buffer)) > 0)
212 totalsize += bsize;
213
214 } else if (entry->type == VISUAL_RINGBUFFER_ENTRY_TYPE_FUNCTION) {
215
216 if (entry->sizefunc != NULL) {
217 totalsize += entry->sizefunc (ringbuffer, entry);
218 } else {
219 VisBuffer *tempbuf = entry->datafunc (ringbuffer, entry);
220
221 if ((bsize = visual_buffer_get_size (tempbuf)) > 0)
222 totalsize += bsize;
223
224 visual_object_unref (VISUAL_OBJECT (tempbuf));
225 }
226 }
227 }
228
229 return totalsize;
230 }
231
232 /**
233 * Gets a list of all ringbuffer fragments that are currently in the ringbuffer.
234 *
235 * @param ringbuffer Pointer to the VisRingBuffer of which the fragments are requested.
236 *
237 * @return A VisList of VisRingBufferEntry items or NULL on failure.
238 */
visual_ringbuffer_get_list(VisRingBuffer * ringbuffer)239 VisList *visual_ringbuffer_get_list (VisRingBuffer *ringbuffer)
240 {
241 visual_log_return_val_if_fail (ringbuffer != NULL, NULL);
242
243 return ringbuffer->entries;
244 }
245
visual_ringbuffer_get_data(VisRingBuffer * ringbuffer,VisBuffer * data,int nbytes)246 int visual_ringbuffer_get_data (VisRingBuffer *ringbuffer, VisBuffer *data, int nbytes)
247 {
248 return visual_ringbuffer_get_data_offset (ringbuffer, data, 0, nbytes);
249 }
250
visual_ringbuffer_get_data_offset(VisRingBuffer * ringbuffer,VisBuffer * data,int offset,int nbytes)251 int visual_ringbuffer_get_data_offset (VisRingBuffer *ringbuffer, VisBuffer *data, int offset, int nbytes)
252 {
253 VisListEntry *le = NULL;
254 VisRingBufferEntry *entry;
255 int curposition = 0;
256 int curoffset = 0;
257 int positioncorr = 0;
258 int startat = 0;
259 int buffercorr = 0;
260
261 visual_log_return_val_if_fail (ringbuffer != NULL, -VISUAL_ERROR_RINGBUFFER_NULL);
262 visual_log_return_val_if_fail (data != NULL, -VISUAL_ERROR_BUFFER_NULL);
263
264 /* Fixate possible partial buffer */
265 if (offset > 0)
266 startat = fixate_with_partial_data_request (ringbuffer, data, offset, nbytes, &buffercorr);
267
268 curposition = buffercorr;
269
270 /* Buffer fixated with partial segment, request the other segments */
271 while (curposition < nbytes) {
272 int lindex = 0;
273 le = NULL;
274
275 while ((entry = visual_list_next (ringbuffer->entries, &le)) != NULL) {
276 VisBuffer *tempbuf;
277
278 lindex++;
279
280 /* Skip to the right offset buffer fragment */
281 if (lindex <= startat)
282 continue;
283
284 if (entry->type == VISUAL_RINGBUFFER_ENTRY_TYPE_BUFFER) {
285
286 tempbuf = entry->buffer;
287
288 } else if (entry->type == VISUAL_RINGBUFFER_ENTRY_TYPE_FUNCTION) {
289
290 /* Will bail out through visual_error_raise(), this is fatal, let's not try to
291 * recover, it's a very obvious bug in the software */
292 if (entry->datafunc == NULL) {
293 visual_log (VISUAL_LOG_ERROR,
294 _("No VisRingBufferDataFunc data provider function set on "
295 "type VISUAL_RINGBUFFER_ENTRY_TYPE_FUNCTION"));
296
297 return -VISUAL_ERROR_IMPOSSIBLE;
298 }
299
300 tempbuf = entry->datafunc (ringbuffer, entry);
301 }
302
303 if (curposition + visual_buffer_get_size (tempbuf) > nbytes) {
304 VisBuffer buf;
305
306 visual_buffer_init (&buf, visual_buffer_get_data (tempbuf),
307 nbytes - curposition, NULL);
308
309 visual_buffer_put (data, &buf, curposition);
310
311 if (entry->type == VISUAL_RINGBUFFER_ENTRY_TYPE_FUNCTION)
312 visual_object_unref (VISUAL_OBJECT (tempbuf));
313
314 return VISUAL_OK;
315 }
316
317 visual_buffer_put (data, tempbuf, curposition);
318
319 curposition += visual_buffer_get_size (tempbuf);
320
321 if (entry->type == VISUAL_RINGBUFFER_ENTRY_TYPE_FUNCTION)
322 visual_object_unref (VISUAL_OBJECT (tempbuf));
323
324 /* Filled without room for partial buffer addition */
325 if (curposition == nbytes)
326 return VISUAL_OK;
327 }
328
329 startat = 0;
330 }
331
332 return VISUAL_OK;
333 }
334
fixate_with_partial_data_request(VisRingBuffer * ringbuffer,VisBuffer * data,int offset,int nbytes,int * buffercorr)335 static int fixate_with_partial_data_request (VisRingBuffer *ringbuffer, VisBuffer *data, int offset, int nbytes,
336 int *buffercorr)
337 {
338 VisListEntry *le = NULL;
339 VisRingBufferEntry *entry;
340 int curposition = 0;
341 int curoffset = 0;
342 int startat = 0;
343
344 *buffercorr = 0;
345
346 while ((entry = visual_list_next (ringbuffer->entries, &le)) != NULL) {
347 int bsize = 0;
348
349 startat++;
350
351 if (entry->type == VISUAL_RINGBUFFER_ENTRY_TYPE_BUFFER) {
352
353 if ((bsize = visual_buffer_get_size (entry->buffer)) > 0)
354 curoffset += bsize;
355
356 /* This buffer partially falls within the offset */
357 if (curoffset > offset) {
358 visual_buffer_put_data (data,
359 (visual_buffer_get_data (entry->buffer) +
360 visual_buffer_get_size (entry->buffer)) -
361 (curoffset - offset), curoffset - offset, 0);
362
363 *buffercorr = curoffset - offset;
364
365 break;
366 }
367 } else if (entry->type == VISUAL_RINGBUFFER_ENTRY_TYPE_FUNCTION) {
368
369 if (entry->sizefunc != NULL) {
370 curoffset += entry->sizefunc (ringbuffer, entry);
371
372 /* This buffer partially falls within the offset */
373 if (curoffset > offset) {
374
375 VisBuffer *tempbuf = entry->datafunc (ringbuffer, entry);
376
377 visual_buffer_put_data (data,
378 (visual_buffer_get_data (tempbuf) +
379 visual_buffer_get_size (tempbuf)) -
380 (curoffset - offset), curoffset - offset, 0);
381
382 visual_object_unref (VISUAL_OBJECT (tempbuf));
383
384 *buffercorr = curoffset - offset;
385
386 break;
387 }
388 } else {
389 VisBuffer *tempbuf = entry->datafunc (ringbuffer, entry);
390
391 if ((bsize = visual_buffer_get_size (tempbuf)) > 0)
392 curoffset += bsize;
393
394 /* This buffer partially falls within the offset */
395 if (curoffset > offset) {
396 visual_buffer_put_data (data,
397 (visual_buffer_get_data (tempbuf) +
398 visual_buffer_get_size (tempbuf)) -
399 (curoffset - offset), curoffset - offset, 0);
400
401 *buffercorr = curoffset - offset;
402
403 break;
404 }
405
406 visual_object_unref (VISUAL_OBJECT (tempbuf));
407 }
408 }
409 }
410
411 return startat;
412 }
413
visual_ringbuffer_get_data_from_end(VisRingBuffer * ringbuffer,VisBuffer * data,int nbytes)414 int visual_ringbuffer_get_data_from_end (VisRingBuffer *ringbuffer, VisBuffer *data, int nbytes)
415 {
416 int totalsize = visual_ringbuffer_get_size (ringbuffer);
417 int offset = totalsize - nbytes;
418
419 if ((nbytes / totalsize) > 0)
420 offset = totalsize - (nbytes % totalsize);
421
422 return visual_ringbuffer_get_data_offset (ringbuffer, data, offset, nbytes);
423 }
424
visual_ringbuffer_get_data_without_wrap(VisRingBuffer * ringbuffer,VisBuffer * data,int nbytes)425 int visual_ringbuffer_get_data_without_wrap (VisRingBuffer *ringbuffer, VisBuffer *data, int nbytes)
426 {
427 int ringsize;
428 int amount = nbytes;
429
430 visual_log_return_val_if_fail (ringbuffer != NULL, -VISUAL_ERROR_RINGBUFFER_NULL);
431
432 if ((ringsize = visual_ringbuffer_get_size (ringbuffer)) < nbytes)
433 amount = ringsize;
434
435 return visual_ringbuffer_get_data_offset (ringbuffer, data, 0, amount);
436 }
437
visual_ringbuffer_get_data_new(VisRingBuffer * ringbuffer,int nbytes)438 VisBuffer *visual_ringbuffer_get_data_new (VisRingBuffer *ringbuffer, int nbytes)
439 {
440 VisBuffer *buffer;
441
442 visual_log_return_val_if_fail (ringbuffer != NULL, NULL);
443
444 buffer = visual_buffer_new_allocate (nbytes, NULL);
445
446 visual_ringbuffer_get_data_offset (ringbuffer, buffer, 0, nbytes);
447
448 return buffer;
449 }
450
visual_ringbuffer_get_data_new_without_wrap(VisRingBuffer * ringbuffer,int nbytes)451 VisBuffer *visual_ringbuffer_get_data_new_without_wrap (VisRingBuffer *ringbuffer, int nbytes)
452 {
453 VisBuffer *buffer;
454 int ringsize;
455 int amount = nbytes;
456
457 visual_log_return_val_if_fail (ringbuffer != NULL, NULL);
458
459 if ((ringsize = visual_ringbuffer_get_size (ringbuffer)) < nbytes)
460 amount = ringsize;
461
462 buffer = visual_buffer_new_allocate (amount, NULL);
463
464 visual_ringbuffer_get_data_without_wrap (ringbuffer, buffer, amount);
465
466 return buffer;
467 }
468
visual_ringbuffer_entry_new(VisBuffer * buffer)469 VisRingBufferEntry *visual_ringbuffer_entry_new (VisBuffer *buffer)
470 {
471 VisRingBufferEntry *entry;
472
473 entry = visual_mem_new0 (VisRingBufferEntry, 1);
474
475 visual_ringbuffer_entry_init (entry, buffer);
476
477 /* Do the VisObject initialization */
478 visual_object_set_allocated (VISUAL_OBJECT (entry), TRUE);
479 visual_object_ref (VISUAL_OBJECT (entry));
480
481 return entry;
482 }
483
visual_ringbuffer_entry_init(VisRingBufferEntry * entry,VisBuffer * buffer)484 int visual_ringbuffer_entry_init (VisRingBufferEntry *entry, VisBuffer *buffer)
485 {
486 visual_log_return_val_if_fail (entry != NULL, -VISUAL_ERROR_RINGBUFFER_ENTRY_NULL);
487
488 /* Do the VisObject initialization */
489 visual_object_clear (VISUAL_OBJECT (entry));
490 visual_object_set_dtor (VISUAL_OBJECT (entry), ringbuffer_entry_dtor);
491 visual_object_set_allocated (VISUAL_OBJECT (entry), FALSE);
492
493 /* Reset the VisRingBufferEntry data */
494 entry->type = VISUAL_RINGBUFFER_ENTRY_TYPE_BUFFER;
495 entry->datafunc = NULL;
496 entry->destroyfunc = NULL;
497 entry->sizefunc = NULL;
498 entry->buffer = buffer;
499 entry->functiondata = NULL;
500
501 return VISUAL_OK;
502 }
503
visual_ringbuffer_entry_new_function(VisRingBufferDataFunc datafunc,VisRingBufferDestroyFunc destroyfunc,VisRingBufferSizeFunc sizefunc,void * functiondata)504 VisRingBufferEntry *visual_ringbuffer_entry_new_function (
505 VisRingBufferDataFunc datafunc,
506 VisRingBufferDestroyFunc destroyfunc,
507 VisRingBufferSizeFunc sizefunc,
508 void *functiondata)
509 {
510 VisRingBufferEntry *entry;
511
512 entry = visual_mem_new0 (VisRingBufferEntry, 1);
513
514 visual_ringbuffer_entry_init_function (entry, datafunc, destroyfunc, sizefunc, functiondata);
515
516 /* Do the VisObject initialization */
517 visual_object_set_allocated (VISUAL_OBJECT (entry), TRUE);
518 visual_object_ref (VISUAL_OBJECT (entry));
519
520 return entry;
521 }
522
visual_ringbuffer_entry_init_function(VisRingBufferEntry * entry,VisRingBufferDataFunc datafunc,VisRingBufferDestroyFunc destroyfunc,VisRingBufferSizeFunc sizefunc,void * functiondata)523 int visual_ringbuffer_entry_init_function (VisRingBufferEntry *entry,
524 VisRingBufferDataFunc datafunc,
525 VisRingBufferDestroyFunc destroyfunc,
526 VisRingBufferSizeFunc sizefunc,
527 void *functiondata)
528 {
529 visual_log_return_val_if_fail (entry != NULL, -VISUAL_ERROR_RINGBUFFER_ENTRY_NULL);
530
531 /* Do the VisObject initialization */
532 visual_object_clear (VISUAL_OBJECT (entry));
533 visual_object_set_dtor (VISUAL_OBJECT (entry), ringbuffer_entry_dtor);
534 visual_object_set_allocated (VISUAL_OBJECT (entry), FALSE);
535
536 /* Reset the VisRingBufferEntry data */
537 entry->type = VISUAL_RINGBUFFER_ENTRY_TYPE_FUNCTION;
538 entry->datafunc = datafunc;
539 entry->destroyfunc = destroyfunc;
540 entry->sizefunc = sizefunc;
541 entry->buffer = NULL;
542 entry->functiondata = functiondata;
543
544 return VISUAL_OK;
545 }
546
visual_ringbuffer_entry_get_functiondata(VisRingBufferEntry * entry)547 void *visual_ringbuffer_entry_get_functiondata (VisRingBufferEntry *entry)
548 {
549 visual_log_return_val_if_fail (entry != NULL, NULL);
550
551 return entry->functiondata;
552 }
553
554 /**
555 * @}
556 */
557
558