Initial implementation of buffer extending. Debug output reduced.
[Rockbox-MoB.git] / testplugin.c
blob37b0d58ab7118a24a9e008581336443fb0bf1c36
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 ****************************************************************************/
21 #include "plugin.h"
23 PLUGIN_HEADER
25 struct plugin_api* rb;
27 #define GUARD_SIZE (32*1024)
29 /* amount of data to read in one read() call */
30 #define AUDIO_DEFAULT_FILECHUNK (1024*32)
32 /* Ring buffer helper macros */
33 /* Buffer pointer (p) plus value (v), wrapped if necessary */
34 #define RINGBUF_ADD(p,v) ((p+v)<buffer_len ? p+v : p+v-buffer_len)
35 /* Buffer pointer (p) minus value (v), wrapped if necessary */
36 #define RINGBUF_SUB(p,v) ((p>=v) ? p-v : p+buffer_len-v)
37 /* How far value (v) plus buffer pointer (p1) will cross buffer pointer (p2) */
38 #define RINGBUF_ADD_CROSS(p1,v,p2) \
39 ((p1<p2) ? (int)(p1+v)-(int)p2 : (int)(p1+v-p2)-(int)buffer_len)
40 /* Bytes available in the buffer */
41 #define BUF_USED RINGBUF_SUB(buf_widx, buf_ridx)
43 /* Internal ring buffer helper macros */
44 /* Buffer pointer (p) plus value (v), wrapped if necessary */
45 #define INT_RINGBUF_ADD(h,p,v) ((p+v) < h->data_len ? p+v : p+v - h->data_len)
46 /* Buffer pointer (p) minus value (v), wrapped if necessary */
47 #define INT_RINGBUF_SUB(h,p,v) ((p>=v) ? p-v : p + h->data_len - v)
48 /* How far value (v) plus buffer pointer (p1) will cross buffer pointer (p2) */
49 #define INT_RINGBUF_ADD_CROSS(h,p1,v,p2) \
50 ((p1<p2) ? (int)(p1+v)-(int)p2 : (int)(p1+v-p2)-(int)h->data_len)
51 /* Bytes available in the buffer */
52 #define INT_BUF_USED(h) (INT_RINGBUF_SUB(h, h->widx, h->ridx))
54 #ifdef ROCKBOX_HAS_LOGF
55 #define DEBUGF rb->logf
56 #endif
58 int num_files = 5;
59 char *files[] = {
60 "/a.mp3",
61 "/b.mp3",
62 "/c.mp3",
63 "/d.mp3",
64 "/e.mp3" };
66 enum data_type {
67 TYPE_CODEC,
68 TYPE_AUDIO,
69 TYPE_ID3,
70 TYPE_CUESHEET,
71 TYPE_IMAGE,
72 TYPE_BUFFER,
73 TYPE_UNKNOWN,
76 struct memory_handle {
77 int id; /* A unique ID for the handle */
78 enum data_type type;
79 char path[MAX_PATH];
80 int fd;
81 size_t data; /* Start index of the handle's data buffer */
82 size_t data_len; /* Length of the data buffer for the handle */
83 size_t ridx; /* Current read pointer, relative to the data 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;
93 static char *buffer;
94 static char *guard_buffer;
96 static size_t buffer_len;
97 static size_t buf_widx;
98 static size_t buf_ridx;
100 static size_t conf_filechunk;
102 /* current memory handle in the linked list. NULL when the list is empty. */
103 static struct memory_handle *cur_handle;
104 /* first memory handle in the linked list. NULL when the list is empty. */
105 static struct memory_handle *first_handle;
106 static int num_handles;
108 static void graph_view(int width);
111 /* add a new handle to the linked list and return it. It will have become the
112 new current handle. The handle will reserve "data_size" bytes or if that's
113 not possible, decrease "data_size" to allow adding the handle. */
114 static struct memory_handle *add_handle(size_t *data_size)
116 /* this will give each handle a unique id */
117 static int cur_handle_id = 1;
119 /* make sure buf_widx is 32-bit aligned so that the handle struct is,
120 but before that we check we can actually align. */
121 if (RINGBUF_ADD_CROSS(buf_widx, 3, buf_ridx) >= 0) {
122 return NULL;
124 buf_widx = (RINGBUF_ADD(buf_widx, 3)) & ~3;
126 size_t len = (data_size ? *data_size : 0)
127 + sizeof(struct memory_handle);
129 /* check that we actually can add the handle and its data */
130 int overlap = RINGBUF_ADD_CROSS(buf_widx, len, buf_ridx);
131 if (overlap >= 0) {
132 *data_size -= overlap + 1;
133 len -= overlap + 1;
135 if (len < sizeof(struct memory_handle)) {
136 return NULL;
139 struct memory_handle *new_handle = (struct memory_handle *)(&buffer[buf_widx]);
141 /* only advance the buffer write index of the size of the struct */
142 buf_widx = RINGBUF_ADD(buf_widx, sizeof(struct memory_handle));
144 if (!first_handle) {
145 /* the new handle is the first one */
146 first_handle = new_handle;
149 if (cur_handle) {
150 cur_handle->next = new_handle;
153 cur_handle = new_handle;
154 cur_handle->id = cur_handle_id++;
155 cur_handle->next = NULL;
156 num_handles++;
157 return cur_handle;
160 /* delete a given memory handle from the linked list
161 and return true for success. Nothing is actually erased from memory. */
162 static bool rm_handle(struct memory_handle *h)
164 if (h == first_handle) {
165 first_handle = h->next;
166 if (h == cur_handle) {
167 DEBUGF("removing the first and last handle\n");
168 /* h was the first and last handle */
169 cur_handle = NULL;
170 buf_ridx = buf_widx;
171 } else {
172 buf_ridx = (void *)first_handle - (void *)buffer;
174 } else {
175 struct memory_handle *m = first_handle;
176 while (m && m->next != h) {
177 m = m->next;
179 if (h && m && m->next == h) {
180 m->next = h->next;
181 if (h == cur_handle) {
182 cur_handle = m;
184 } else {
185 return false;
189 num_handles--;
190 return true;
193 /* these are unfortunalty needed to be global
194 so move_handle can invalidate them */
195 static int cached_handle_id = -1;
196 static struct memory_handle *cached_handle = NULL;
198 /* Return a pointer to the memory handle of given ID.
199 NULL if the handle wasn't found */
200 static struct memory_handle *find_handle(int handle_id)
202 /* simple caching because most of the time the requested handle
203 will either be the same as the last, or the one after the last */
204 if (cached_handle)
206 if (cached_handle_id == handle_id && cached_handle_id == cached_handle->id)
207 return cached_handle;
208 else if (cached_handle->next && (cached_handle->next->id == handle_id))
210 /* JD's quick testing showd this block was only entered
211 2/1971 calls to find_handle.
212 8/1971 calls to find_handle resulted in a cache miss */
213 cached_handle = cached_handle->next;
214 cached_handle_id = handle_id;
215 return cached_handle;
219 struct memory_handle *m = first_handle;
220 while (m && m->id != handle_id) {
221 m = m->next;
223 cached_handle_id = handle_id;
224 cached_handle = m;
225 return (m && m->id == handle_id) ? m : NULL;
228 /* Move a memory handle to newpos.
229 Return a pointer to the new location of the handle */
230 static struct memory_handle *move_handle(size_t *delta, struct memory_handle *h)
232 if (*delta < 4) {
233 /* aligning backwards would yield a negative result,
234 and moving the handle of such a small amount is a waste
235 of time anyway. */
236 return NULL;
238 /* make sure delta is 32-bit aligned so that the handle struct is. */
239 *delta = (*delta - 3) & ~3;
241 size_t newpos = RINGBUF_ADD((void *)h - (void *)buffer, *delta);
243 struct memory_handle *dest = (struct memory_handle *)(&buffer[newpos]);
245 /* Invalidate the cache to prevent it from keeping the old location of h */
246 if (h == cached_handle)
247 cached_handle = NULL;
249 /* the cur_handle pointer might need updating */
250 if (h == cur_handle) {
251 cur_handle = dest;
254 if (h == first_handle) {
255 first_handle = dest;
256 buf_ridx = newpos;
257 } else {
258 struct memory_handle *m = first_handle;
259 while (m && m->next != h) {
260 m = m->next;
262 if (h && m && m->next == h) {
263 m->next = dest;
264 } else {
265 return NULL;
269 rb->memmove(dest, h, sizeof(struct memory_handle));
271 return dest;
273 /* Buffer data for the given handle. Return the amount of data buffered
274 or -1 if the handle wasn't found */
275 static ssize_t buffer_handle(int handle_id)
277 DEBUGF("buffer_handle(%d)\n", handle_id);
278 struct memory_handle *h = find_handle(handle_id);
279 if (!h)
280 return -1;
282 if (h->filerem == 0) {
283 /* nothing left to buffer */
284 return 0;
287 if (h->fd < 0) /* file closed, reopen */
289 if (*h->path)
290 h->fd = rb->open(h->path, O_RDONLY);
291 else
292 return -1;
294 if (h->fd < 0)
295 return -1;
297 if (h->offset)
298 rb->lseek(h->fd, h->offset, SEEK_SET);
301 ssize_t ret = 0;
302 while (h->filerem > 0)
304 /* max amount to copy */
305 size_t copy_n = MIN(MIN(conf_filechunk, h->filerem),
306 MIN(h->data_len - h->widx,
307 buffer_len - RINGBUF_ADD(h->data, h->widx))
310 #if 0
311 DEBUGF("widx: %ld, ridx: %ld copy_n: %ld, writepos: %ld,"
312 "readpos: %ld, add_cross: %d, int_add_cross: %d\n",
313 h->widx, h->ridx, copy_n, RINGBUF_ADD(h->data, h->widx),
314 RINGBUF_ADD(h->data, h->ridx),
315 RINGBUF_ADD_CROSS( RINGBUF_ADD(h->data, h->widx),
316 copy_n,
317 RINGBUF_ADD(h->data, h->ridx)
319 INT_RINGBUF_ADD_CROSS(h, h->widx, copy_n, h->ridx)
321 #endif
323 /* stop copying if it would overwrite the reading position.
324 buf_widx == buf_ridx is defined as buffer empty, not buffer full.
325 same goes with h->widx == h->ridx. */
326 if (RINGBUF_ADD_CROSS( RINGBUF_ADD(h->data, h->widx),
327 copy_n,
328 RINGBUF_ADD(h->data, h->ridx)
329 ) >= 0 ||
330 INT_RINGBUF_ADD_CROSS(h, h->widx, copy_n, h->ridx) >= 0 )
331 break;
333 /* rc is the actual amount read */
334 int rc = rb->read(h->fd, &buffer[RINGBUF_ADD(h->data,h->widx)], copy_n);
336 if (rc < 0)
338 DEBUGF("File ended %ld bytes early\n", (long)h->filerem);
339 h->filesize -= h->filerem;
340 h->filerem = 0;
341 break;
344 /* Advance buffer */
345 h->widx = INT_RINGBUF_ADD(h, h->widx, rc);
346 h->available += rc;
347 ret += rc;
348 h->filerem -= rc;
350 if (h->ridx <= h->widx && h->data_len < h->filerem) {
351 /* Check whether we can extend the buffer to avoid unnecessary
352 wrapping. */
353 size_t extend_by = 0;
354 if (h == cur_handle) {
355 extend_by = RINGBUF_SUB(buf_ridx, buf_widx) - 1;
356 } else {
357 extend_by = RINGBUF_SUB((size_t)((void*)h->next-(void*)buffer),
358 RINGBUF_ADD(h->data, h->data_len));
361 h->data_len += extend_by;
362 if (extend_by > 0)
363 DEBUGF("extended the handle data buffer by %ld bytes\n",
364 extend_by);
369 if (h->filerem == 0) {
370 /* finished buffering the file */
371 rb->close(h->fd);
374 if (h == cur_handle)
375 buf_widx = RINGBUF_ADD(h->data, h->data_len);
377 DEBUGF("buffered %ld bytes (%ld of %ld available, remaining: %ld)\n",
378 ret, h->available, h->filesize, h->filerem);
380 graph_view(100);
382 return ret;
385 /* Free buffer space by moving the handle struct right before the useful
386 part of its data buffer */
387 static void free_buffer(int handle_id)
389 DEBUGF("free_buffer(%d)\n", handle_id);
390 struct memory_handle *h = find_handle(handle_id);
391 if (!h)
392 return;
394 DEBUGF("old state: data: %ld, data_len: %ld, avail: %ld, ridx: %ld, widx: %ld\n",
395 h->data, h->data_len, h->available, h->ridx, h->widx);
397 if (h->ridx > h->widx) {
398 /* We don't want to overwrite a useful part of the buffer */
399 return;
402 size_t delta = h->ridx;
403 h = move_handle(&delta, h);
404 if (!h) return;
405 /* The value of delta might change for alignment reasons */
406 h->data = RINGBUF_ADD(h->data, delta);
407 h->data_len -= delta;
408 h->ridx -= delta;
409 if (h->widx >= delta) h->widx -= delta;
410 h->available -= delta;
412 DEBUGF("new state: data: %ld, data_len: %ld, avail: %ld, ridx: %ld, widx: %ld\n",
413 h->data, h->data_len, h->available, h->ridx, h->widx);
415 graph_view(100);
418 static void fill_buffer(void)
420 DEBUGF("fill buffer()\n");
421 struct memory_handle *m = first_handle;
422 while (m) {
423 if (m->filerem > 0) {
424 buffer_handle(m->id);
425 return;
427 m = m->next;
431 static size_t data_rem(void)
433 size_t ret = 0;
435 struct memory_handle *m = first_handle;
436 while (m) {
437 ret += m->filerem;
438 m = m->next;
441 return ret;
444 static size_t wasted_space(void)
446 size_t ret = 0;
448 struct memory_handle *m = first_handle;
449 while (m) {
450 ret += (m->ridx <= m->widx) ? m->ridx : 0;
451 m = m->next;
454 return ret;
457 static size_t used_space(void)
459 size_t ret = 0;
461 struct memory_handle *m = first_handle;
462 while (m) {
463 ret += INT_BUF_USED(m);
464 m = m->next;
467 return ret;
470 /* Request a file be buffered
471 filename: name of the file t open
472 offset: starting offset to buffer from the file
473 RETURNS: <0 if the file cannot be opened, or one file already
474 queued to be opened, otherwise the handle for the file in the buffer
476 int bufopen(char *file, size_t offset)
478 int fd = rb->open(file, O_RDONLY);
479 if (fd < 0)
480 return -1;
482 size_t size = rb->filesize(fd) - offset + 1;
483 DEBUGF("bufopen: %s (offset: %ld) (%ld bytes needed)... ",
484 file, offset, size);
486 struct memory_handle *h = add_handle(&size);
487 if (!h)
489 DEBUGF("failed to add handle\n");
490 rb->close(fd);
491 return -1;
494 if (offset) rb->lseek(fd, offset, SEEK_SET);
495 rb->strncpy(h->path, file, MAX_PATH);
496 h->fd = fd;
497 h->filesize = rb->filesize(fd);
498 h->filerem = h->filesize - offset;
499 h->offset = offset;
500 h->ridx = 0;
501 h->widx = 0;
502 h->data = buf_widx;
503 h->data_len = size;
504 h->available = 0;
506 buf_widx = RINGBUF_ADD(buf_widx, size);
508 DEBUGF("allocated %ld bytes. ID: %d\n", size, h->id);
509 return h->id;
512 /* Close the handle. Return 0 for success and < 0 for failure */
513 int bufclose(int handle_id)
515 DEBUGF("bufclose(%d)\n", handle_id);
516 struct memory_handle *h = find_handle(handle_id);
517 if (!h)
518 return -1;
520 rm_handle(h);
521 return 0;
524 /* Set the reading index in a handle (relatively to the start of the handle data).
525 Return 0 for success and < 0 for failure */
526 int bufseek(int handle_id, size_t offset)
528 struct memory_handle *h = find_handle(handle_id);
529 if (!h)
530 return -1;
532 if (offset > h->available || offset > h->data_len)
533 return -2;
535 h->ridx = offset;
536 return 0;
539 /* Advance the reading index in a handle (relatively to its current position).
540 Return 0 for success and < 0 for failure */
541 int bufadvance(int handle_id, off_t offset)
543 struct memory_handle *h = find_handle(handle_id);
544 if (!h)
545 return -1;
547 /* DEBUGF("bufadvance(%ld): h->ridx: %ld, h->widx: %ld\n",
548 offset, h->ridx, h->widx); */
550 if (offset >= 0)
552 /* check for access beyond what's available */
553 if ((size_t)offset > (h->available - h->ridx))
554 return -2;
556 h->ridx = INT_RINGBUF_ADD(h, h->ridx, offset);
558 else
560 /* check for access before what's available */
561 if ((size_t)(-offset) > h->ridx)
562 return -2;
564 h->ridx = INT_RINGBUF_SUB(h, h->ridx, (size_t)(-offset));
567 return 0;
570 /* Copy data from the given handle to the dest buffer.
571 Return the number of bytes copied or < 0 for failure. */
572 ssize_t bufread(int handle_id, size_t size, char *dest)
574 struct memory_handle *h = find_handle(handle_id);
575 size_t buffered_data;
576 if (!h)
577 return -1;
579 if (h->available == 0 && h->filerem > 0)
580 return -2;
582 if (h->available == 0 && h->filerem == 0)
583 return 0;
585 buffered_data = MIN(size, h->available);
587 if (h->data + h->ridx + buffered_data > buffer_len)
589 size_t read = buffer_len - (h->data + h->ridx);
590 rb->memcpy(dest, &buffer[h->data + h->ridx], read);
591 rb->memcpy(dest+read, buffer, buffered_data - read);
593 else rb->memcpy(dest, &buffer[h->data + h->ridx], buffered_data);
595 h->ridx += buffered_data;
596 h->available -= buffered_data;
597 return buffered_data;
600 /* Update the "data" pointer to make the handle's data available to the caller.
601 Return the length of the available linear data or < 0 for failure. */
602 ssize_t bufgetdata(int handle_id, size_t size, unsigned char **data)
604 struct memory_handle *h = find_handle(handle_id);
605 if (!h)
606 return -1;
608 if (h->available == 0 && h->filerem > 0)
609 return -2;
611 if (h->available == 0 && h->filerem == 0)
612 return 0;
614 ssize_t ret;
616 *data = (unsigned char *)&buffer[RINGBUF_ADD(h->data, h->ridx)];
618 if (buffer_len - (h->data + h->ridx) > 0 &&
619 buffer_len - (h->data + h->ridx) < size &&
620 h->available - h->ridx >= size)
622 /* use the guard buffer to provide what was requested. */
623 size_t copy_n = h->data + h->ridx + size - buffer_len;
625 if (h->ridx + size > h->data_len) {
626 size_t copy_1 = h->data_len - (h->ridx + size - copy_n);
627 size_t copy_2 = copy_n - copy_1;
628 rb->memcpy(guard_buffer,
629 (unsigned char *)buffer, copy_1);
630 rb->memcpy(guard_buffer + copy_1,
631 (unsigned char *)&buffer[h->data], copy_2);
632 } else {
633 rb->memcpy(guard_buffer, (unsigned char *)buffer, copy_n);
636 ret = size;
637 DEBUGF("used the guard buffer to complete\n");
639 else if (h->ridx + size > h->data_len &&
640 h->available - h->ridx >= size &&
641 size <= GUARD_SIZE)
643 size_t copy_1 = h->data_len - h->ridx;
644 size_t copy_2 = size - copy_1;
645 rb->memcpy(guard_buffer,
646 (unsigned char *)&buffer[RINGBUF_ADD(h->data, h->ridx)],
647 copy_1);
648 rb->memcpy(guard_buffer + copy_1,
649 (unsigned char *)&buffer[h->data], copy_2);
650 *data = guard_buffer;
651 ret = size;
652 DEBUGF("used the guard buffer for a whole piece\n");
654 else
656 ret = MIN(MIN(h->available - h->ridx,
657 INT_BUF_USED(h)),
658 buffer_len - (h->data + h->ridx));
661 //DEBUGF("bufgetdata(%d): h->ridx=%ld, ret=%ld\n", handle_id, (long)h->ridx, ret);
662 return ret;
665 bool test_ll(void)
667 struct memory_handle *m1, *m2, *m3, *m4;
669 if (cur_handle != NULL || first_handle != NULL)
670 return false;
672 m1 = add_handle(NULL);
674 if (cur_handle != m1 || first_handle != m1 || m1->next != NULL)
675 return false;
677 m2 = add_handle(NULL);
679 if (cur_handle != m2 || first_handle != m1 || m1->next != m2 || m2->next != NULL)
680 return false;
682 m3 = add_handle(NULL);
684 if (cur_handle != m3 || first_handle != m1 || m2->next != m3 || m3->next != NULL)
685 return false;
687 rm_handle(m2);
689 if (cur_handle != m3 || first_handle != m1 || m1->next != m3)
690 return false;
692 rm_handle(m3);
694 if (cur_handle != m1 || first_handle != m1 || m1->next != NULL)
695 return false;
697 m4 = add_handle(NULL);
699 if (cur_handle != m4 || first_handle != m1 || m1->next != m4 || m4->next != NULL)
700 return false;
702 rm_handle(m1);
704 if (cur_handle != m4 || first_handle != m4)
705 return false;
707 rm_handle(m4);
709 if (cur_handle != NULL || first_handle != NULL)
710 return false;
712 m1 = add_handle(NULL);
713 m2 = add_handle(NULL);
714 m3 = add_handle(NULL);
715 m4 = add_handle(NULL);
717 if (cur_handle != m4 || first_handle != m1)
718 return false;
720 size_t delta = 1024*100;
721 m2 = move_handle(&delta, m2);
723 if (cur_handle != m4 || first_handle != m1 || m1->next != m2 || m2->next != m3)
724 return false;
726 delta = 1024*100*3;
727 m1 = move_handle(&delta, m1);
729 if (cur_handle != m4 || first_handle != m1 || m1->next != m2)
730 return false;
732 rm_handle(m1);
733 rm_handle(m2);
734 rm_handle(m3);
735 rm_handle(m4);
737 if (cur_handle != NULL || first_handle != NULL)
738 return false;
740 return true;
743 /* display a nice graphical view of the ringbuffer. */
744 static void graph_view(int width)
746 #ifndef ROCKBOX_HAS_LOGF
747 int i, r_pos, w_pos;
748 r_pos = buf_ridx * width / buffer_len;
749 w_pos = buf_widx * width / buffer_len;
751 DEBUGF("|");
752 for (i=0; i <= width; i++)
754 if (i != r_pos && i != w_pos)
756 if (buf_ridx <= buf_widx)
758 if (i > r_pos && i < w_pos) {
759 DEBUGF(">");
760 } else {
761 DEBUGF("-");
764 else
766 if (i > r_pos || i < w_pos) {
767 DEBUGF(">");
768 } else {
769 DEBUGF("-");
773 else
775 if (i == r_pos && i == w_pos)
777 if (buf_ridx <= buf_widx) {
778 DEBUGF("RW");
779 } else {
780 DEBUGF("WR");
782 } else if (i == r_pos) {
783 DEBUGF("R");
784 } else if (i == w_pos) {
785 DEBUGF("W");
789 DEBUGF("|");
790 DEBUGF("\n");
791 #else
792 (void)width;
793 #endif
796 void print_progress(size_t amount, int file, int numfiles)
798 char buf[32];
799 rb->lcd_clear_display();
800 rb->snprintf(buf, sizeof(buf), "file %d of %d", file, numfiles);
801 rb->lcd_puts(0, 0, buf);
802 rb->snprintf(buf, sizeof(buf), "read: %ld", amount);
803 rb->lcd_puts(0, 1, buf);
804 rb->lcd_update();
807 bool buffer_init(void)
809 buffer = rb->plugin_get_audio_buffer(&buffer_len);
810 if (!buffer)
812 DEBUGF("couldn't allocate buffer\n");
813 return false;
815 buffer_len -= GUARD_SIZE;
816 guard_buffer = buffer + buffer_len;
818 buf_widx = 0;
819 buf_ridx = 0;
821 first_handle = NULL;
822 num_handles = 0;
824 conf_filechunk = AUDIO_DEFAULT_FILECHUNK;
826 return true;
829 bool disk_is_spinning(void)
831 return true;
835 static long codec_stack[4*DEFAULT_STACK_SIZE/sizeof(long)];
836 static struct thread_entry* codecthread_id;
838 static long bufopen_stack[DEFAULT_STACK_SIZE/sizeof(long)];
839 static struct thread_entry* bufopenthread_id;
841 bool done_playing = false;
843 #define MAX_HANDLES 16
845 int handles[MAX_HANDLES];
847 void codec_thread(void)
849 int idx = 0;
850 int fd = -1; /* used to write the files out as they are read */
851 unsigned char *data;
852 char outfile[MAX_PATH];
853 long read, total = 0;
855 while (1)
857 if (!done_playing)
859 if (handles[idx] > 0) {
861 if (fd < 0) {
862 rb->snprintf(outfile, MAX_PATH, "/file%d.mp3", idx);
863 fd = rb->open(outfile, O_CREAT|O_TRUNC|O_WRONLY);
864 if (fd < 0) {
865 DEBUGF("couldn't create file\n");
866 rb->splash(HZ, "couldn't create file");
870 do {
871 read = bufgetdata(handles[idx], GUARD_SIZE, &data);
872 if (read >= 0) {
873 read = MIN(read, GUARD_SIZE);
874 rb->write(fd, data, read);
875 total += read;
876 bufadvance(handles[idx], read);
877 print_progress(total, idx+1, num_files);
879 rb->sleep(HZ/20);
880 } while (read > 0);
882 if (read >= 0 && total >= 0) {
883 DEBUGF("read %ld bytes from handle %d\n", total, handles[idx]);
886 if (read == -2) {
887 DEBUGF("data for handle %d isn't ready\n", handles[idx]);
888 } else if (read == -1) {
889 DEBUGF("couldn't find handle %d\n", handles[idx]);
890 } else if (read == 0) {
891 DEBUGF("finished reading handle %d\n", handles[idx]);
892 bufclose(handles[idx]);
893 rb->close(fd);
894 fd = -1;
895 total = 0;
896 idx++;
898 if (idx >= num_files) {
899 done_playing = true;
900 break;
906 rb->sleep(HZ/4);
909 DEBUGF("removing the codec thread\n");
910 rb->remove_thread(NULL);
913 void bufopen_thread(void)
915 int idx = 0, ret;
916 while (idx < num_files)
918 ret = bufopen(files[idx], 0);
919 if (ret > 0) {
920 handles[idx++] = ret;
922 rb->sleep(HZ*8);
925 DEBUGF("bufopen thread finished\n");
926 rb->remove_thread(NULL);
930 /* this is the plugin entry point */
931 enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
933 (void)parameter;
934 rb = api;
936 buffer_init();
938 if (!test_ll())
940 DEBUGF("linked list test failed\n");
941 rb->splash(HZ, "linked list test failed");
942 return PLUGIN_ERROR;
945 codecthread_id = rb->create_thread(codec_thread,
946 codec_stack,
947 sizeof(codec_stack),
948 "codec"
949 IF_PRIO(,PRIORITY_PLAYBACK)
950 IF_COP(, CPU, false));
952 bufopenthread_id = rb->create_thread(bufopen_thread,
953 bufopen_stack,
954 sizeof(bufopen_stack),
955 "bufopen"
956 IF_PRIO(,PRIORITY_BACKGROUND)
957 IF_COP(, CPU, false));
959 if (!codecthread_id)
961 rb->splash(HZ, "failed to create codec thread");
962 return PLUGIN_ERROR;
964 else if (!bufopenthread_id)
966 rb->splash(HZ, "failed to create bufopen thread");
967 return PLUGIN_ERROR;
969 else
972 while (!done_playing)
974 struct memory_handle *m = first_handle;
975 while (m) {
976 if (m->filerem > 0 &&
977 m->data_len - INT_BUF_USED(m) > m->data_len/4)
979 /* there is free space to rebuffer */
980 DEBUGF("handle %d has %ld bytes available to rebuffer\n",
981 m->id, m->data_len - INT_BUF_USED(m));
982 buffer_handle(m->id);
984 m = m->next;
987 if (wasted_space() > buffer_len/4) {
988 DEBUGF("there is %ld bytes of wasted space\n", wasted_space());
989 struct memory_handle *m = first_handle;
990 while (m) {
991 if (m->filerem == 0) {
992 free_buffer(m->id);
994 m = m->next;
998 if (data_rem() > 0 &&
999 used_space() < buffer_len/4 &&
1000 disk_is_spinning())
1002 DEBUGF("%ld bytes left to buffer and the buffer is running low\n",
1003 data_rem());
1004 fill_buffer();
1005 } else {
1006 rb->sleep(HZ/2);
1009 DEBUGF("done playing\n");
1010 rb->yield();
1013 rb->backlight_on();
1014 DEBUGF("end of plugin\n");
1015 return PLUGIN_OK;