Remove the test plugin
[Rockbox.git] / apps / buffering.c
blob7fa49c3fbe09489fbf4a711f04494502f2213927
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2007 Nicolas Pennequin
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
18 ****************************************************************************/
20 #include "config.h"
21 #include <stdio.h>
22 #include <string.h>
23 #include <stdlib.h>
24 #include <ctype.h>
25 #include "buffering.h"
27 #include "ata.h"
28 #include "system.h"
29 #include "thread.h"
30 #include "file.h"
31 #include "panic.h"
32 #include "memory.h"
33 #include "lcd.h"
34 #include "font.h"
35 #include "button.h"
36 #include "kernel.h"
37 #include "tree.h"
38 #include "debug.h"
39 #include "sprintf.h"
40 #include "settings.h"
41 #include "codecs.h"
42 #include "audio.h"
43 #include "logf.h"
44 #include "mp3_playback.h"
45 #include "usb.h"
46 #include "status.h"
47 #include "screens.h"
48 #include "playlist.h"
49 #include "playback.h"
50 #include "pcmbuf.h"
51 #include "buffer.h"
53 #ifdef SIMULATOR
54 #define ata_disk_is_active() 1
55 #endif
57 #if MEM > 1
58 #define GUARD_BUFSIZE (32*1024)
59 #else
60 #define GUARD_BUFSIZE (8*1024)
61 #endif
63 /* amount of data to read in one read() call */
64 #define AUDIO_DEFAULT_FILECHUNK (1024*32)
66 /* Ring buffer helper macros */
67 /* Buffer pointer (p) plus value (v), wrapped if necessary */
68 #define RINGBUF_ADD(p,v) ((p+v)<buffer_len ? p+v : p+v-buffer_len)
69 /* Buffer pointer (p) minus value (v), wrapped if necessary */
70 #define RINGBUF_SUB(p,v) ((p>=v) ? p-v : p+buffer_len-v)
71 /* How far value (v) plus buffer pointer (p1) will cross buffer pointer (p2) */
72 #define RINGBUF_ADD_CROSS(p1,v,p2) \
73 ((p1<p2) ? (int)(p1+v)-(int)p2 : (int)(p1+v-p2)-(int)buffer_len)
74 /* Bytes available in the buffer */
75 #define BUF_USED RINGBUF_SUB(buf_widx, buf_ridx)
77 struct memory_handle {
78 int id; /* A unique ID for the handle */
79 enum data_type type;
80 char path[MAX_PATH];
81 int fd;
82 size_t data; /* Start index of the handle's data buffer */
83 size_t ridx; /* Current read pointer, relative to the main buffer */
84 size_t widx; /* Current write pointer */
85 size_t filesize; /* File total length */
86 size_t filerem; /* Remaining bytes of file NOT in buffer */
87 size_t available; /* Available bytes to read from buffer */
88 size_t offset; /* Offset at which we started reading the file */
89 struct memory_handle *next;
91 /* at all times, we have: filesize == offset + available + filerem */
94 static char *buffer;
95 static char *guard_buffer;
97 static size_t buffer_len;
99 static size_t buf_widx; /* current writing position */
100 static size_t buf_ridx; /* current reading position */
101 /* buf_*idx are values relative to the buffer, not real pointers. */
103 static size_t conf_filechunk;
105 /* current memory handle in the linked list. NULL when the list is empty. */
106 static struct memory_handle *cur_handle;
107 /* first memory handle in the linked list. NULL when the list is empty. */
108 static struct memory_handle *first_handle;
110 static int num_handles; /* number of handles in the list */
112 /* Handle cache (makes find_handle faster).
113 These need to be global so that move_handle can invalidate them. */
114 static int cached_handle_id = -1;
115 static struct memory_handle *cached_handle = NULL;
119 LINKED LIST MANAGEMENT
120 ======================
122 add_handle : Add a handle to the list
123 rm_handle : Remove a handle from the list
124 find_handle : Get a handle pointer from an ID
125 move_handle : Move a handle in the buffer (with or without its data)
127 These functions only handle the linked list structure. They don't touch the
128 contents of the struct memory_handle headers. They also change the buf_*idx
129 pointers when necessary and manage the handle IDs.
131 The first and current (== last) handle are kept track of.
132 A new handle is added at buf_widx and becomes the current one.
133 buf_widx always points to the current writing position for the current handle
134 buf_ridx always points to the location of the first handle.
135 buf_ridx == buf_widx means the buffer is empty.
139 /* Add a new handle to the linked list and return it. It will have become the
140 new current handle. The handle will reserve "data_size" bytes or if that's
141 not possible, decrease "data_size" to allow adding the handle. */
142 static struct memory_handle *add_handle(size_t *data_size)
144 /* this will give each handle a unique id */
145 static int cur_handle_id = 1;
147 /* make sure buf_widx is 32-bit aligned so that the handle struct is,
148 but before that we check we can actually align. */
149 if (RINGBUF_ADD_CROSS(buf_widx, 3, buf_ridx) >= 0) {
150 return NULL;
152 buf_widx = (RINGBUF_ADD(buf_widx, 3)) & ~3;
154 size_t len = (data_size ? *data_size : 0)
155 + sizeof(struct memory_handle);
157 /* check that we actually can add the handle and its data */
158 int overlap = RINGBUF_ADD_CROSS(buf_widx, len, buf_ridx);
159 if (overlap >= 0) {
160 *data_size -= overlap;
161 len -= overlap;
163 if (len < sizeof(struct memory_handle)) {
164 /* There isn't even enough space to write the struct */
165 return NULL;
168 struct memory_handle *new_handle =
169 (struct memory_handle *)(&buffer[buf_widx]);
171 /* only advance the buffer write index of the size of the struct */
172 buf_widx = RINGBUF_ADD(buf_widx, sizeof(struct memory_handle));
174 if (!first_handle) {
175 /* the new handle is the first one */
176 first_handle = new_handle;
179 if (cur_handle) {
180 cur_handle->next = new_handle;
183 cur_handle = new_handle;
184 cur_handle->id = cur_handle_id++;
185 cur_handle->next = NULL;
186 num_handles++;
187 return cur_handle;
190 /* Delete a given memory handle from the linked list
191 and return true for success. Nothing is actually erased from memory. */
192 static bool rm_handle(struct memory_handle *h)
194 if (h == first_handle) {
195 first_handle = h->next;
196 if (h == cur_handle) {
197 /* h was the first and last handle: the buffer is now empty */
198 cur_handle = NULL;
199 buf_ridx = buf_widx;
200 } else {
201 /* update buf_ridx to point to the new first handle */
202 buf_ridx = (void *)first_handle - (void *)buffer;
204 } else {
205 struct memory_handle *m = first_handle;
206 while (m && m->next != h) {
207 m = m->next;
209 if (h && m && m->next == h) {
210 m->next = h->next;
211 if (h == cur_handle) {
212 cur_handle = m;
214 } else {
215 return false;
219 num_handles--;
220 return true;
223 /* Return a pointer to the memory handle of given ID.
224 NULL if the handle wasn't found */
225 static struct memory_handle *find_handle(int handle_id)
227 /* simple caching because most of the time the requested handle
228 will either be the same as the last, or the one after the last */
229 if (cached_handle)
231 if (cached_handle_id == handle_id &&
232 cached_handle_id == cached_handle->id)
233 return cached_handle;
234 else if (cached_handle->next && (cached_handle->next->id == handle_id))
236 /* JD's quick testing showd this block was only entered
237 2/1971 calls to find_handle.
238 8/1971 calls to find_handle resulted in a cache miss */
239 cached_handle = cached_handle->next;
240 cached_handle_id = handle_id;
241 return cached_handle;
245 struct memory_handle *m = first_handle;
246 while (m && m->id != handle_id) {
247 m = m->next;
249 cached_handle_id = handle_id;
250 cached_handle = m;
251 return (m && m->id == handle_id) ? m : NULL;
254 /* Move a memory handle and data_size of its data of delta.
255 Return a pointer to the new location of the handle.
256 delta is the value of which to move the struct data.
257 data_size is the amount of data to move along with the struct. */
258 static struct memory_handle *move_handle(struct memory_handle *h,
259 size_t *delta, size_t data_size)
261 if (*delta < 4) {
262 /* aligning backwards would yield a negative result,
263 and moving the handle of such a small amount is a waste
264 of time anyway. */
265 return NULL;
267 /* make sure delta is 32-bit aligned so that the handle struct is. */
268 *delta = (*delta - 3) & ~3;
270 size_t newpos = RINGBUF_ADD((void *)h - (void *)buffer, *delta);
272 struct memory_handle *dest = (struct memory_handle *)(&buffer[newpos]);
274 /* Invalidate the cache to prevent it from keeping the old location of h */
275 if (h == cached_handle)
276 cached_handle = NULL;
278 /* the cur_handle pointer might need updating */
279 if (h == cur_handle) {
280 cur_handle = dest;
283 if (h == first_handle) {
284 first_handle = dest;
285 buf_ridx = newpos;
286 } else {
287 struct memory_handle *m = first_handle;
288 while (m && m->next != h) {
289 m = m->next;
291 if (h && m && m->next == h) {
292 m->next = dest;
293 } else {
294 return NULL;
298 memmove(dest, h, sizeof(struct memory_handle) + data_size);
300 return dest;
305 BUFFER SPACE MANAGEMENT
306 =======================
308 buffer_handle : Buffer data for a handle
309 free_buffer : Free buffer space by moving a handle
310 fill_buffer : Call buffer_handle for all handles that have data to buffer
311 can_add_handle : Indicate whether it's safe to add a handle.
312 data_rem : Total amount of data needing to be buffered
313 wasted_space : Total amount of space available for freeing
315 These functions are used by the buffering thread to manage buffer space.
318 /* Buffer data for the given handle. Return the amount of data buffered
319 or -1 if the handle wasn't found */
320 static ssize_t buffer_handle(int handle_id)
322 DEBUGF("buffer_handle(%d)\n", handle_id);
323 struct memory_handle *h = find_handle(handle_id);
324 if (!h)
325 return -1;
327 if (h->filerem == 0) {
328 /* nothing left to buffer */
329 return 0;
332 if (h->fd < 0) /* file closed, reopen */
334 if (*h->path)
335 h->fd = open(h->path, O_RDONLY);
336 else
337 return -1;
339 if (h->fd < 0)
340 return -1;
342 if (h->offset)
343 lseek(h->fd, h->offset, SEEK_SET);
346 trigger_cpu_boost();
348 ssize_t ret = 0;
349 while (h->filerem > 0)
351 /* max amount to copy */
352 size_t copy_n = MIN( MIN(h->filerem, conf_filechunk),
353 buffer_len - h->widx);
355 /* stop copying if it would overwrite the reading position
356 or the next handle */
357 if (RINGBUF_ADD_CROSS(h->widx, copy_n, buf_ridx) >= 0 || (h->next &&
358 RINGBUF_ADD_CROSS(h->widx, copy_n, (unsigned)
359 ((void *)h->next - (void *)buffer)) > 0))
360 break;
362 /* rc is the actual amount read */
363 int rc = read(h->fd, &buffer[h->widx], copy_n);
365 if (rc < 0)
367 if (h->type == TYPE_CODEC) {
368 DEBUGF("Partial codec\n");
369 break;
372 DEBUGF("File ended %ld bytes early\n", (long)h->filerem);
373 h->filesize -= h->filerem;
374 h->filerem = 0;
375 break;
378 /* Advance buffer */
379 h->widx = RINGBUF_ADD(h->widx, rc);
380 if (h == cur_handle)
381 buf_widx = h->widx;
382 h->available += rc;
383 ret += rc;
384 h->filerem -= rc;
386 /* DEBUGF("buffer_handle: buffered %ld bytes. done: %ld. remaining: %ld.\n",
387 rc, h->available, h->filerem); */
390 if (h->filerem == 0) {
391 /* finished buffering the file */
392 close(h->fd);
393 h->fd = -1;
396 DEBUGF("buffered %ld bytes (%ld of %ld available, rem: %ld, off: %ld)\n",
397 ret, h->available, h->filesize, h->filerem, h->offset);
399 return ret;
402 /* Free buffer space by moving the handle struct right before the useful
403 part of its data buffer or by moving all the data. */
404 static void free_buffer(int handle_id)
406 struct memory_handle *h = find_handle(handle_id);
407 if (!h)
408 return;
410 size_t delta;
411 /* The value of delta might change for alignment reasons */
413 if (h->next && (h->type == TYPE_ID3 || h->type == TYPE_CUESHEET ||
414 h->type == TYPE_IMAGE) && h->filerem == 0 )
416 /* metadata handle: we can move all of it */
417 delta = RINGBUF_SUB( (unsigned)((void *)h->next - (void *)buffer),
418 h->data) - h->available;
419 h = move_handle(h, &delta, h->available);
420 if (!h) return;
421 h->data = RINGBUF_ADD(h->data, delta);
422 h->ridx = RINGBUF_ADD(h->ridx, delta);
423 h->widx = RINGBUF_ADD(h->widx, delta);
425 /* when moving a struct mp3entry we need to readjust its pointers. */
426 if (h->type == TYPE_ID3 && h->filesize == sizeof(struct mp3entry)) {
427 adjust_mp3entry((struct mp3entry *)&buffer[h->data],
428 (void *)&buffer[h->data],
429 (void *)&buffer[RINGBUF_SUB(h->data, delta)]);
432 DEBUGF("free_buffer(%d): metadata, moved by %ld bytes\n",
433 handle_id, delta);
435 else
437 /* only move the handle struct */
438 delta = RINGBUF_SUB(h->ridx, h->data);
439 h = move_handle(h, &delta, 0);
440 if (!h) return;
441 h->data = RINGBUF_ADD(h->data, delta);
442 h->available -= delta;
443 h->offset += delta;
444 DEBUGF("free_buffer(%d): audio, %ld bytes freed\n", handle_id, delta);
448 /* Fill the buffer by buffering as much data as possible for handles that still
449 have data left to buffer */
450 static void fill_buffer(void)
452 DEBUGF("fill buffer()\n");
453 struct memory_handle *m = first_handle;
454 while (m) {
455 if (m->filerem > 0) {
456 buffer_handle(m->id);
458 m = m->next;
462 /* Check whether it's safe to add a new handle and reserve space to let the
463 current one finish buffering its data. Used by bufopen and bufgetdata as
464 a preliminary check before even trying to physically add the handle.
465 Returns true if it's ok to add a new handle, false if not.
467 static bool can_add_handle(void)
469 if (cur_handle && cur_handle->filerem > 0) {
470 /* the current handle hasn't finished buffering. We can only add
471 a new one if there is already enough free space to finish
472 the buffering. */
473 if (cur_handle->filerem < (buffer_len - BUF_USED)) {
474 /* Before adding the new handle we reserve some space for the
475 current one to finish buffering its data. */
476 buf_widx = RINGBUF_ADD(buf_widx, cur_handle->filerem);
477 } else {
478 return false;
482 return true;
485 /* Return the total amount of data left to be buffered for all the handles */
486 static size_t data_rem(void)
488 size_t ret = 0;
490 struct memory_handle *m = first_handle;
491 while (m) {
492 ret += m->filerem;
493 m = m->next;
496 return ret;
499 /* Return the amount of data we have but don't need anymore. This data can be
500 safely erased to reclaim buffer space. */
501 static size_t wasted_space(void)
503 size_t ret = 0;
505 struct memory_handle *m = first_handle;
506 while (m) {
507 ret += RINGBUF_SUB(m->ridx, m->data);
508 m = m->next;
511 return ret;
516 BUFFERING API FUNCTIONS
517 =======================
519 bufopen : Request the opening of a new handle for a file
520 bufalloc : Open a new handle for data other than a file.
521 bufclose : Close an open handle
522 bufseek : Set the read pointer in a handle
523 bufadvance : Move the read pointer in a handle
524 bufread : Copy data from a handle into a given buffer
525 bufgetdata : Give a pointer to the handle's data
526 bufused : Return the amount of buffer space used
528 These functions are exported, to allow interaction with the buffer.
529 They take care of the content of the structs, and rely on the linked list
530 management functions for all the actual handle management work.
534 /* Request a file be buffered
535 filename: name of the file to open
536 offset: offset at which to start buffering the file, useful when the first
537 (offset-1) bytes of the file aren't needed.
538 return value: <0 if the file cannot be opened, or one file already
539 queued to be opened, otherwise the handle for the file in the buffer
541 int bufopen(char *file, size_t offset, enum data_type type)
543 if (!can_add_handle())
544 return -2;
546 int fd = open(file, O_RDONLY);
547 if (fd < 0)
548 return -1;
550 size_t size = filesize(fd) - offset;
552 if (type != TYPE_AUDIO &&
553 size + sizeof(struct memory_handle) > buffer_len - buf_widx)
555 /* for types other than audio, the data can't wrap */
556 return -2;
559 DEBUGF("bufopen: %s (offset: %ld) (%ld bytes needed)...\n",
560 file, offset, size);
562 struct memory_handle *h = add_handle(&size);
563 if (!h)
565 DEBUGF("failed to add handle\n");
566 close(fd);
567 return -2;
570 if (offset) lseek(fd, offset, SEEK_SET);
571 strncpy(h->path, file, MAX_PATH);
572 h->fd = fd;
573 h->filesize = filesize(fd);
574 h->filerem = h->filesize - offset;
575 h->offset = offset;
576 h->ridx = buf_widx;
577 h->widx = buf_widx;
578 h->data = buf_widx;
579 h->available = 0;
580 h->type = type;
582 DEBUGF("allocated %ld bytes. ID: %d\n", size, h->id);
584 if (type == TYPE_CODEC || type == TYPE_CUESHEET || type == TYPE_IMAGE) {
585 /* Immediately buffer those */
586 ssize_t ret = buffer_handle(h->id);
588 /* Check that we got the complete file */
589 if ((size_t)ret != h->filesize) {
590 bufclose(h->id);
591 return -3;
595 DEBUGF("bufopen: opened handle %d\n", h->id);
596 return h->id;
599 /* Open a new handle from data that isn't in a file.
600 src is the source buffer from which to copy data. It can be NULL to simply
601 reserve buffer space.
602 size is the requested size. The call will only be successful if the
603 requested amount of data can entirely fit in the buffer without wrapping.
604 Return value is the handle id for success or <0 for failure.
606 int bufalloc(void *src, size_t size, enum data_type type)
608 if (!can_add_handle())
609 return -2;
611 if (size + sizeof(struct memory_handle) > buffer_len - buf_widx)
612 /* The data would need to wrap. */
613 return -2;
615 size_t allocsize = size;
616 struct memory_handle *h = add_handle(&allocsize);
618 if (!h || allocsize != size)
619 return -2;
621 if (src) {
622 if (type == TYPE_ID3 && size == sizeof(struct mp3entry)) {
623 /* specially take care of struct mp3entry */
624 copy_mp3entry((struct mp3entry *)&buffer[buf_widx],
625 (struct mp3entry *)src);
626 } else {
627 memcpy(&buffer[buf_widx], src, size);
631 h->fd = -1;
632 *h->path = 0;
633 h->filesize = size;
634 h->filerem = 0;
635 h->offset = 0;
636 h->ridx = buf_widx;
637 h->widx = buf_widx;
638 h->data = buf_widx;
639 h->available = size;
640 h->type = type;
642 buf_widx = RINGBUF_ADD(buf_widx, size);
644 DEBUGF("bufalloc: opened handle %d\n", h->id);
645 return h->id;
648 /* Close the handle. Return 0 for success and < 0 for failure */
649 int bufclose(int handle_id)
651 DEBUGF("bufclose(%d)\n", handle_id);
652 struct memory_handle *h = find_handle(handle_id);
653 if (!h)
654 return -1;
656 rm_handle(h);
657 return 0;
660 /* Set reading index in handle (relatively to the start of the file).
661 Access before the available data will trigger a rebuffer.
662 TODO: Test this
663 TODO: Maybe force an immediate rebuffer by calling buffer_handle() ?
664 Return 0 for success and < 0 for failure:
665 -1 if the handle wasn't found
666 -2 if there is no data available at the new position
667 (the reading index is still moved)
668 -3 if the new requested position was beyond the end of the file
670 int bufseek(int handle_id, size_t newpos)
672 int ret = 0;
673 struct memory_handle *h = find_handle(handle_id);
674 if (!h)
675 return -1;
677 if (newpos > h->filesize) {
678 /* access beyond the end of the file */
679 return -3;
682 else if (newpos < h->offset) {
683 /* access before what we currently have. A rebuffer is needed. */
684 h->offset = newpos;
685 h->available = 0;
686 h->filerem = h->filesize - newpos;
687 /* having changed filerem should be enough to trigger the rebuffer. */
688 h->widx = h->data;
689 ret = -2;
692 else if (newpos > h->offset + h->available) {
693 /* data isn't available yet. */
694 ret = -2;
697 h->ridx = RINGBUF_ADD(h->data, newpos);
698 return ret;
701 /* Advance the reading index in a handle (relatively to its current position).
702 Return 0 for success and < 0 for failure
703 TODO: Add some rebuffering like in bufseek */
704 int bufadvance(int handle_id, off_t offset)
706 struct memory_handle *h = find_handle(handle_id);
707 if (!h)
708 return -1;
710 if (offset >= 0)
712 /* check for access beyond what's available */
713 if ((size_t)offset > (h->available - RINGBUF_SUB(h->ridx, h->data)))
714 return -2;
716 h->ridx = RINGBUF_ADD(h->ridx, offset);
718 else
720 /* check for access before what's available */
721 if ((size_t)(-offset) > RINGBUF_SUB(h->ridx, h->data))
722 return -2;
724 h->ridx = RINGBUF_SUB(h->ridx, (size_t)(-offset));
727 return 0;
730 /* Copy data from the given handle to the dest buffer.
731 Return the number of bytes copied or < 0 for failure. */
732 ssize_t bufread(int handle_id, size_t size, char *dest)
734 struct memory_handle *h = find_handle(handle_id);
735 size_t buffered_data;
736 if (!h)
737 return -1;
739 if (h->available < size && h->filerem > 0) /* Data isn't ready */
740 return -2;
742 if (h->available == 0 && h->filerem == 0) /* File is finished reading */
743 return 0;
745 buffered_data = MIN(size, h->available - RINGBUF_SUB(h->ridx, h->data));
747 if (h->ridx + buffered_data > buffer_len)
749 /* the data wraps around the end of the buffer */
750 size_t read = buffer_len - h->ridx;
751 memcpy(dest, &buffer[h->ridx], read);
752 memcpy(dest+read, buffer, buffered_data - read);
754 else memcpy(dest, &buffer[h->ridx], buffered_data);
756 return buffered_data;
759 /* Update the "data" pointer to make the handle's data available to the caller.
760 Return the length of the available linear data or < 0 for failure.
761 size is the amount of linear data requested. it can be 0 to get as
762 much as possible.
763 The guard buffer may be used to provide the requested size */
764 ssize_t bufgetdata(int handle_id, size_t size, unsigned char **data)
766 struct memory_handle *h = find_handle(handle_id);
767 if (!h)
768 return -1;
770 if (h->available < size && h->filerem > 0) /* Data isn't ready */
771 return -2;
773 if (h->available == 0 && h->filerem == 0) /* File is finished reading */
774 return 0;
776 ssize_t ret;
778 if (h->ridx + size > buffer_len &&
779 h->available - RINGBUF_SUB(h->ridx, h->data) >= size)
781 /* the data wraps around the end of the buffer :
782 use the guard buffer to provide the requested amount of data. */
783 size_t copy_n = MIN(h->ridx + size - buffer_len, GUARD_BUFSIZE);
784 memcpy(guard_buffer, (unsigned char *)buffer, copy_n);
785 ret = buffer_len - h->ridx + copy_n;
786 DEBUGF("used the guard buffer to complete\n");
788 else
790 ret = MIN(h->available - RINGBUF_SUB(h->ridx, h->data),
791 buffer_len - h->ridx);
794 *data = (unsigned char *)&buffer[h->ridx];
796 /* DEBUGF("bufgetdata(%d): h->ridx=%ld, ret=%ld\n", handle_id,
797 (long)h->ridx, ret); */
798 return ret;
801 /* Return the amount of buffer space used */
802 size_t bufused(void)
804 return BUF_USED;
807 bool buffering_init(char *filebuf, size_t filebuflen)
809 if (!filebuf || !filebuflen)
810 return false;
812 buffer = filebuf;
813 buffer_len = filebuflen;
814 guard_buffer = buffer + buffer_len;
816 buf_widx = 0;
817 buf_ridx = 0;
819 first_handle = NULL;
820 num_handles = 0;
822 conf_filechunk = AUDIO_DEFAULT_FILECHUNK;
824 return true;
827 void buffering_thread(void)
829 while (true)
831 if (data_rem() > 0 && wasted_space() > buffer_len/5) {
832 DEBUGF("there is %ld bytes of wasted space\n", wasted_space());
834 /* free buffer from outdated audio data */
835 struct memory_handle *m = first_handle;
836 while (m) {
837 if (m->type == TYPE_AUDIO)
838 free_buffer(m->id);
839 m = m->next;
842 /* free buffer by moving metadata */
843 m = first_handle;
844 while (m) {
845 if (m->type != TYPE_AUDIO)
846 free_buffer(m->id);
847 m = m->next;
852 if (data_rem() > 0 && BUF_USED < 3*buffer_len/4 &&
853 ata_disk_is_active())
855 DEBUGF("%ld bytes left to buffer and the buffer is low\n",
856 data_rem());
857 fill_buffer();
858 } else {
859 sleep(HZ/2);
864 ssize_t get_offset(int handle_id, void *ptr)
866 struct memory_handle *h = find_handle(handle_id);
867 if (!h)
868 return -1;
870 return (size_t)ptr - (size_t)&buffer[h->ridx];