Don't stop filling the buffer after the first handle that needs buffering.
[Rockbox-MoB.git] / testplugin.c
blob661f8fceb6d85c80f390b0e84be15c32045cfc2a
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 #ifdef ROCKBOX_HAS_LOGF
44 #define DEBUGF rb->logf
45 #endif
47 int num_files = 5;
48 char *files[] = {
49 "/a.mp3",
50 "/b.mp3",
51 "/c.mp3",
52 "/d.mp3",
53 "/e.mp3" };
55 enum data_type {
56 TYPE_CODEC,
57 TYPE_AUDIO,
58 TYPE_ID3,
59 TYPE_CUESHEET,
60 TYPE_IMAGE,
61 TYPE_BUFFER,
62 TYPE_UNKNOWN,
65 struct memory_handle {
66 int id; /* A unique ID for the handle */
67 enum data_type type;
68 char path[MAX_PATH];
69 int fd;
70 size_t data; /* Start index of the handle's data buffer */
71 size_t ridx; /* Current read pointer, relative to the main buffer */
72 size_t widx; /* Current write pointer */
73 size_t filesize; /* File total length */
74 size_t filerem; /* Remaining bytes of file NOT in buffer */
75 size_t available; /* Available bytes to read from buffer */
76 size_t offset; /* Offset at which we started reading the file */
77 struct memory_handle *next;
81 static char *buffer;
82 static char *guard_buffer;
84 static size_t buffer_len;
85 static size_t buf_widx;
86 static size_t buf_ridx;
88 static size_t conf_filechunk;
90 /* current memory handle in the linked list. NULL when the list is empty. */
91 static struct memory_handle *cur_handle;
92 /* first memory handle in the linked list. NULL when the list is empty. */
93 static struct memory_handle *first_handle;
94 static int num_handles;
96 static void graph_view(int width);
99 /* add a new handle to the linked list and return it. It will have become the
100 new current handle. The handle will reserve "data_size" bytes or if that's
101 not possible, decrease "data_size" to allow adding the handle. */
102 static struct memory_handle *add_handle(size_t *data_size)
104 /* this will give each handle a unique id */
105 static int cur_handle_id = 1;
107 /* make sure buf_widx is 32-bit aligned so that the handle struct is,
108 but before that we check we can actually align. */
109 if (RINGBUF_ADD_CROSS(buf_widx, 3, buf_ridx) >= 0) {
110 return NULL;
112 buf_widx = (RINGBUF_ADD(buf_widx, 3)) & ~3;
114 size_t len = (data_size ? *data_size : 0)
115 + sizeof(struct memory_handle);
117 /* check that we actually can add the handle and its data */
118 int overlap = RINGBUF_ADD_CROSS(buf_widx, len, buf_ridx);
119 if (overlap >= 0) {
120 *data_size -= overlap;
121 len -= overlap;
123 if (len < sizeof(struct memory_handle)) {
124 return NULL;
127 struct memory_handle *new_handle =
128 (struct memory_handle *)(&buffer[buf_widx]);
130 /* only advance the buffer write index of the size of the struct */
131 buf_widx = RINGBUF_ADD(buf_widx, sizeof(struct memory_handle));
133 if (!first_handle) {
134 /* the new handle is the first one */
135 first_handle = new_handle;
138 if (cur_handle) {
139 cur_handle->next = new_handle;
142 cur_handle = new_handle;
143 cur_handle->id = cur_handle_id++;
144 cur_handle->next = NULL;
145 num_handles++;
146 return cur_handle;
149 /* delete a given memory handle from the linked list
150 and return true for success. Nothing is actually erased from memory. */
151 static bool rm_handle(struct memory_handle *h)
153 if (h == first_handle) {
154 first_handle = h->next;
155 if (h == cur_handle) {
156 DEBUGF("removing the first and last handle\n");
157 /* h was the first and last handle */
158 cur_handle = NULL;
159 buf_ridx = buf_widx;
160 } else {
161 buf_ridx = (void *)first_handle - (void *)buffer;
163 } else {
164 struct memory_handle *m = first_handle;
165 while (m && m->next != h) {
166 m = m->next;
168 if (h && m && m->next == h) {
169 m->next = h->next;
170 if (h == cur_handle) {
171 cur_handle = m;
173 } else {
174 return false;
178 num_handles--;
179 return true;
182 /* these are unfortunalty needed to be global
183 so move_handle can invalidate them */
184 static int cached_handle_id = -1;
185 static struct memory_handle *cached_handle = NULL;
187 /* Return a pointer to the memory handle of given ID.
188 NULL if the handle wasn't found */
189 static struct memory_handle *find_handle(int handle_id)
191 /* simple caching because most of the time the requested handle
192 will either be the same as the last, or the one after the last */
193 if (cached_handle)
195 if (cached_handle_id == handle_id &&
196 cached_handle_id == cached_handle->id)
197 return cached_handle;
198 else if (cached_handle->next && (cached_handle->next->id == handle_id))
200 /* JD's quick testing showd this block was only entered
201 2/1971 calls to find_handle.
202 8/1971 calls to find_handle resulted in a cache miss */
203 cached_handle = cached_handle->next;
204 cached_handle_id = handle_id;
205 return cached_handle;
209 struct memory_handle *m = first_handle;
210 while (m && m->id != handle_id) {
211 m = m->next;
213 cached_handle_id = handle_id;
214 cached_handle = m;
215 return (m && m->id == handle_id) ? m : NULL;
218 /* Move a memory handle and data_size of its data of delta.
219 Return a pointer to the new location of the handle.
220 delta is the value of which to move the struct data.
221 data_size is the amount of data to move along with the struct. */
222 static struct memory_handle *move_handle(struct memory_handle *h,
223 size_t *delta, size_t data_size)
225 if (*delta < 4) {
226 /* aligning backwards would yield a negative result,
227 and moving the handle of such a small amount is a waste
228 of time anyway. */
229 return NULL;
231 /* make sure delta is 32-bit aligned so that the handle struct is. */
232 *delta = (*delta - 3) & ~3;
234 size_t newpos = RINGBUF_ADD((void *)h - (void *)buffer, *delta);
236 struct memory_handle *dest = (struct memory_handle *)(&buffer[newpos]);
238 /* Invalidate the cache to prevent it from keeping the old location of h */
239 if (h == cached_handle)
240 cached_handle = NULL;
242 /* the cur_handle pointer might need updating */
243 if (h == cur_handle) {
244 cur_handle = dest;
247 if (h == first_handle) {
248 first_handle = dest;
249 buf_ridx = newpos;
250 } else {
251 struct memory_handle *m = first_handle;
252 while (m && m->next != h) {
253 m = m->next;
255 if (h && m && m->next == h) {
256 m->next = dest;
257 } else {
258 return NULL;
262 rb->memmove(dest, h, sizeof(struct memory_handle) + data_size);
264 return dest;
267 /* Buffer data for the given handle. Return the amount of data buffered
268 or -1 if the handle wasn't found */
269 static ssize_t buffer_handle(int handle_id)
271 DEBUGF("buffer_handle(%d)\n", handle_id);
272 struct memory_handle *h = find_handle(handle_id);
273 if (!h)
274 return -1;
276 if (h->filerem == 0) {
277 /* nothing left to buffer */
278 return 0;
281 if (h->fd < 0) /* file closed, reopen */
283 if (*h->path)
284 h->fd = rb->open(h->path, O_RDONLY);
285 else
286 return -1;
288 if (h->fd < 0)
289 return -1;
291 if (h->offset)
292 rb->lseek(h->fd, h->offset, SEEK_SET);
295 ssize_t ret = 0;
296 while (h->filerem > 0)
298 /* max amount to copy */
299 size_t copy_n = MIN( MIN(h->filerem, conf_filechunk),
300 buffer_len - h->widx);
302 /* stop copying if it would overwrite the reading position
303 or the next handle */
304 if (RINGBUF_ADD_CROSS(h->widx, copy_n, buf_ridx) >= 0 || (h->next &&
305 RINGBUF_ADD_CROSS(h->widx, copy_n, (unsigned)
306 ((void *)h->next - (void *)buffer)) > 0))
307 break;
309 /* rc is the actual amount read */
310 int rc = rb->read(h->fd, &buffer[h->widx], copy_n);
312 if (rc < 0)
314 DEBUGF("File ended %ld bytes early\n", (long)h->filerem);
315 h->filesize -= h->filerem;
316 h->filerem = 0;
317 break;
320 /* Advance buffer */
321 h->widx = RINGBUF_ADD(h->widx, rc);
322 if (h == cur_handle)
323 buf_widx = h->widx;
324 h->available += rc;
325 ret += rc;
326 h->filerem -= rc;
329 if (h->filerem == 0) {
330 /* finished buffering the file */
331 rb->close(h->fd);
334 DEBUGF("buffered %ld bytes (%ld of %ld available, remaining: %ld)\n",
335 ret, h->available, h->filesize, h->filerem);
337 graph_view(100);
339 return ret;
342 /* Free buffer space by moving the handle struct right before the useful
343 part of its data buffer */
344 static void free_buffer(int handle_id)
346 struct memory_handle *h = find_handle(handle_id);
347 if (!h)
348 return;
350 size_t delta;
351 /* The value of delta might change for alignment reasons */
353 if (h->next && (h->type == TYPE_ID3 || h->type == TYPE_CUESHEET ||
354 h->type == TYPE_IMAGE) && h->filerem == 0 )
356 /* metadata handle: we can move all of it */
357 delta = RINGBUF_SUB( (unsigned)((void *)h->next - (void *)buffer),
358 h->data) - h->available;
359 h = move_handle(h, &delta, h->available);
360 if (!h) return;
361 h->data = RINGBUF_ADD(h->data, delta);
362 h->ridx = RINGBUF_ADD(h->ridx, delta);
363 DEBUGF("free_buffer(%d): metadata, moved by %ld bytes\n",
364 handle_id, delta);
366 else
368 /* only move the handle struct */
369 delta = RINGBUF_SUB(h->ridx, h->data);
370 h = move_handle(h, &delta, 0);
371 if (!h) return;
372 h->data = RINGBUF_ADD(h->data, delta);
373 h->available -= delta;
374 DEBUGF("free_buffer(%d): audio, %ld bytes freed\n", handle_id, delta);
378 /* fill the buffer by buffering as much data as possible for handles that still
379 have data left to buffer */
380 static void fill_buffer(void)
382 DEBUGF("fill buffer()\n");
383 struct memory_handle *m = first_handle;
384 while (m) {
385 if (m->filerem > 0) {
386 buffer_handle(m->id);
388 m = m->next;
392 static size_t data_rem(void)
394 size_t ret = 0;
396 struct memory_handle *m = first_handle;
397 while (m) {
398 ret += m->filerem;
399 m = m->next;
402 return ret;
405 static size_t wasted_space(void)
407 size_t ret = 0;
409 struct memory_handle *m = first_handle;
410 while (m) {
411 ret += RINGBUF_SUB(m->ridx, m->data);
412 m = m->next;
415 return ret;
418 /* Request a file be buffered
419 filename: name of the file to open
420 offset:starting offset to buffer from the file
421 return value: <0 if the file cannot be opened, or one file already
422 queued to be opened, otherwise the handle for the file in the buffer
424 int bufopen(char *file, size_t offset, enum data_type type)
426 if (cur_handle && cur_handle->filerem > 0) {
427 /* the current handle hasn't finished buffering. We can only add
428 a new one if there is already enough free space to finish
429 the buffering. */
430 if (cur_handle->filerem < (buffer_len - BUF_USED)) {
431 /* Before adding the new handle we reserve some space for the
432 current one to finish buffering its data. */
433 buf_widx = RINGBUF_ADD(buf_widx, cur_handle->filerem);
434 } else {
435 return -1;
439 int fd = rb->open(file, O_RDONLY);
440 if (fd < 0)
441 return -1;
443 size_t size = rb->filesize(fd) - offset;
445 DEBUGF("bufopen: %s (offset: %ld) (%ld bytes needed)...\n",
446 file, offset, size);
448 struct memory_handle *h = add_handle(&size);
449 if (!h)
451 DEBUGF("failed to add handle\n");
452 rb->close(fd);
453 return -1;
456 if (offset) rb->lseek(fd, offset, SEEK_SET);
457 rb->strncpy(h->path, file, MAX_PATH);
458 h->fd = fd;
459 h->filesize = rb->filesize(fd);
460 h->filerem = h->filesize - offset;
461 h->offset = offset;
462 h->ridx = buf_widx;
463 h->widx = buf_widx;
464 h->data = buf_widx;
465 h->available = 0;
466 h->type = type;
468 DEBUGF("allocated %ld bytes. ID: %d\n", size, h->id);
469 return h->id;
472 /* Close the handle. Return 0 for success and < 0 for failure */
473 int bufclose(int handle_id)
475 DEBUGF("bufclose(%d)\n", handle_id);
476 struct memory_handle *h = find_handle(handle_id);
477 if (!h)
478 return -1;
480 rm_handle(h);
481 return 0;
484 /* Set reading index in handle (relatively to the start of the handle data).
485 Return 0 for success and < 0 for failure */
486 int bufseek(int handle_id, size_t offset)
488 struct memory_handle *h = find_handle(handle_id);
489 if (!h)
490 return -1;
492 if (offset > h->available)
493 return -2;
495 h->ridx = RINGBUF_ADD(h->data, offset);
496 return 0;
499 /* Advance the reading index in a handle (relatively to its current position).
500 Return 0 for success and < 0 for failure */
501 int bufadvance(int handle_id, off_t offset)
503 struct memory_handle *h = find_handle(handle_id);
504 if (!h)
505 return -1;
507 if (offset >= 0)
509 /* check for access beyond what's available */
510 if ((size_t)offset > (h->available - RINGBUF_SUB(h->ridx, h->data)))
511 return -2;
513 h->ridx = RINGBUF_ADD(h->ridx, offset);
515 else
517 /* check for access before what's available */
518 if ((size_t)(-offset) > RINGBUF_SUB(h->ridx, h->data))
519 return -2;
521 h->ridx = RINGBUF_SUB(h->ridx, (size_t)(-offset));
524 return 0;
527 /* Copy data from the given handle to the dest buffer.
528 Return the number of bytes copied or < 0 for failure. */
529 ssize_t bufread(int handle_id, size_t size, char *dest)
531 struct memory_handle *h = find_handle(handle_id);
532 size_t buffered_data;
533 if (!h)
534 return -1;
536 if (h->available == 0 && h->filerem > 0)
537 return -2;
539 if (h->available == 0 && h->filerem == 0)
540 return 0;
542 buffered_data = MIN(size, h->available);
544 if (h->ridx + buffered_data > buffer_len)
546 size_t read = buffer_len - h->ridx;
547 rb->memcpy(dest, &buffer[h->ridx], read);
548 rb->memcpy(dest+read, buffer, buffered_data - read);
550 else rb->memcpy(dest, &buffer[h->ridx], buffered_data);
552 h->ridx = RINGBUF_ADD(h->ridx, buffered_data);
553 h->available -= buffered_data;
554 return buffered_data;
557 /* Update the "data" pointer to make the handle's data available to the caller.
558 Return the length of the available linear data or < 0 for failure. */
559 ssize_t bufgetdata(int handle_id, size_t size, unsigned char **data)
561 struct memory_handle *h = find_handle(handle_id);
562 if (!h)
563 return -1;
565 if (h->available == 0 && h->filerem > 0)
566 return -2;
568 if (h->available == 0 && h->filerem == 0)
569 return 0;
571 ssize_t ret;
573 if (h->ridx + size > buffer_len &&
574 h->available - RINGBUF_SUB(h->ridx, h->data) >= size)
576 /* use the guard buffer to provide what was requested. */
577 size_t copy_n = h->ridx + size - buffer_len;
578 rb->memcpy(guard_buffer, (unsigned char *)buffer, copy_n);
579 ret = size;
580 DEBUGF("used the guard buffer to complete\n");
582 else
584 ret = MIN(h->available - RINGBUF_SUB(h->ridx, h->data),
585 buffer_len - h->ridx);
588 *data = (unsigned char *)&buffer[h->ridx];
590 /* DEBUGF("bufgetdata(%d): h->ridx=%ld, ret=%ld\n", handle_id,
591 (long)h->ridx, ret); */
592 return ret;
595 bool test_ll(void)
597 struct memory_handle *m1, *m2, *m3, *m4;
599 if (cur_handle != NULL || first_handle != NULL)
600 return false;
602 m1 = add_handle(NULL);
604 if (cur_handle != m1 || first_handle != m1 || m1->next != NULL)
605 return false;
607 m2 = add_handle(NULL);
609 if (cur_handle != m2 || first_handle != m1 || m1->next != m2 || m2->next != NULL)
610 return false;
612 m3 = add_handle(NULL);
614 if (cur_handle != m3 || first_handle != m1 || m2->next != m3 || m3->next != NULL)
615 return false;
617 rm_handle(m2);
619 if (cur_handle != m3 || first_handle != m1 || m1->next != m3)
620 return false;
622 rm_handle(m3);
624 if (cur_handle != m1 || first_handle != m1 || m1->next != NULL)
625 return false;
627 m4 = add_handle(NULL);
629 if (cur_handle != m4 || first_handle != m1 || m1->next != m4 || m4->next != NULL)
630 return false;
632 rm_handle(m1);
634 if (cur_handle != m4 || first_handle != m4)
635 return false;
637 rm_handle(m4);
639 if (cur_handle != NULL || first_handle != NULL)
640 return false;
642 m1 = add_handle(NULL);
643 m2 = add_handle(NULL);
644 m3 = add_handle(NULL);
645 m4 = add_handle(NULL);
647 if (cur_handle != m4 || first_handle != m1)
648 return false;
650 size_t delta = 1024*100;
651 m2 = move_handle(m2, &delta, 0);
653 if (cur_handle != m4 || first_handle != m1 || m1->next != m2 || m2->next != m3)
654 return false;
656 delta = 1024*100*3;
657 m1 = move_handle(m1, &delta, 0);
659 if (cur_handle != m4 || first_handle != m1 || m1->next != m2)
660 return false;
662 rm_handle(m1);
663 rm_handle(m2);
664 rm_handle(m3);
665 rm_handle(m4);
667 if (cur_handle != NULL || first_handle != NULL)
668 return false;
670 return true;
673 /* display a nice graphical view of the ringbuffer. */
674 static void graph_view(int width)
676 #ifndef ROCKBOX_HAS_LOGF
677 int i, r_pos, w_pos;
678 r_pos = buf_ridx * width / buffer_len;
679 w_pos = buf_widx * width / buffer_len;
681 DEBUGF("|");
682 for (i=0; i <= width; i++)
684 if (i != r_pos && i != w_pos)
686 if (buf_ridx <= buf_widx)
688 if (i > r_pos && i < w_pos) {
689 DEBUGF(">");
690 } else {
691 DEBUGF("-");
694 else
696 if (i > r_pos || i < w_pos) {
697 DEBUGF(">");
698 } else {
699 DEBUGF("-");
703 else
705 if (i == r_pos && i == w_pos)
707 if (buf_ridx <= buf_widx) {
708 DEBUGF("RW");
709 } else {
710 DEBUGF("WR");
712 } else if (i == r_pos) {
713 DEBUGF("R");
714 } else if (i == w_pos) {
715 DEBUGF("W");
719 DEBUGF("|");
720 DEBUGF("\n");
721 #else
722 (void)width;
723 #endif
726 void print_progress(size_t amount, int file, int numfiles)
728 char buf[32];
729 rb->snprintf(buf, sizeof(buf), "file %d of %d", file, numfiles);
730 rb->lcd_puts(0, 0, buf);
731 rb->snprintf(buf, sizeof(buf), "read: %ld", amount);
732 rb->lcd_puts(0, 1, buf);
735 void print_metadata(int handle_id)
737 ssize_t ret;
738 char *artist, *title, *newline;
739 char art[50], ttl[50];
740 int artist_len, title_len;
741 unsigned char *buf;
743 ret = bufgetdata(handle_id, 0, &buf);
745 artist = buf;
746 newline = rb->strchr(artist, '\n');
747 artist_len = newline - artist;
748 rb->strncpy(art, artist, artist_len);
749 art[artist_len] = 0;
751 title = newline + 1;
752 newline = rb->strchr(title, '\n');
753 title_len = newline - title;
754 rb->strncpy(ttl, title, title_len);
755 ttl[title_len] = 0;
757 rb->lcd_puts(0, 3, art);
758 rb->lcd_puts(0, 4, ttl);
761 bool buffer_init(void)
763 buffer = rb->plugin_get_audio_buffer(&buffer_len);
764 if (!buffer)
766 DEBUGF("couldn't allocate buffer\n");
767 return false;
769 buffer_len -= GUARD_SIZE;
770 guard_buffer = buffer + buffer_len;
772 buf_widx = 0;
773 buf_ridx = 0;
775 first_handle = NULL;
776 num_handles = 0;
778 conf_filechunk = AUDIO_DEFAULT_FILECHUNK;
780 return true;
783 bool disk_is_spinning(void)
785 return true;
790 static long codec_stack[4*DEFAULT_STACK_SIZE/sizeof(long)];
791 static struct thread_entry* codecthread_id;
793 static long bufopen_stack[2*DEFAULT_STACK_SIZE/sizeof(long)];
794 static struct thread_entry* bufopenthread_id;
796 bool done_playing = false;
798 #define MAX_HANDLES 16
800 int handles[MAX_HANDLES];
801 int meta_handles[MAX_HANDLES];
803 void codec_thread(void)
805 int idx = 0;
806 int fd = -1; /* used to write the files out as they are read */
807 unsigned char *data;
808 char outfile[MAX_PATH];
809 long read, total = 0;
811 while (1)
813 if (!done_playing)
815 if (handles[idx] > 0) {
817 if (fd < 0) {
818 rb->snprintf(outfile, MAX_PATH, "/file%d.mp3", idx);
819 fd = rb->open(outfile, O_CREAT|O_TRUNC|O_WRONLY);
820 if (fd < 0) {
821 DEBUGF("couldn't create file\n");
822 rb->splash(HZ, "couldn't create file");
826 do {
827 read = bufgetdata(handles[idx], GUARD_SIZE, &data);
828 if (read >= 0) {
829 read = MIN(read, GUARD_SIZE);
830 rb->write(fd, data, read);
831 total += read;
832 bufadvance(handles[idx], read);
833 rb->lcd_clear_display();
834 print_progress(total, idx+1, num_files);
835 print_metadata(meta_handles[idx]);
836 rb->lcd_update();
838 rb->sleep(HZ/100);
839 } while (read > 0);
841 if (read >= 0 && total >= 0) {
842 DEBUGF("read %ld bytes from handle %d\n", total,
843 handles[idx]);
846 if (read == -2) {
847 DEBUGF("data for handle %d isn't ready\n", handles[idx]);
848 } else if (read == -1) {
849 DEBUGF("couldn't find handle %d\n", handles[idx]);
850 } else if (read == 0) {
851 DEBUGF("finished reading handle %d\n", handles[idx]);
852 bufclose(handles[idx]);
853 bufclose(meta_handles[idx]);
854 rb->close(fd);
855 fd = -1;
856 total = 0;
857 idx++;
859 if (idx >= num_files) {
860 done_playing = true;
861 break;
867 rb->sleep(HZ/10);
870 DEBUGF("removing the codec thread\n");
871 rb->remove_thread(NULL);
874 void bufopen_thread(void)
876 int idx = 0, ret;
877 char buf[MAX_PATH];
878 while (idx < num_files)
880 rb->snprintf(buf, MAX_PATH, "/meta%s.txt", files[idx]);
881 meta_handles[idx] = bufopen(buf, 0, TYPE_ID3);
882 if (meta_handles[idx] > 0)
884 ret = bufopen(files[idx], 0, TYPE_AUDIO);
885 if (ret > 0) {
886 handles[idx++] = ret;
887 } else {
888 bufclose(meta_handles[idx]);
891 rb->sleep(HZ*2);
894 DEBUGF("bufopen thread finished\n");
895 rb->remove_thread(NULL);
899 /* this is the plugin entry point */
900 enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
902 (void)parameter;
903 rb = api;
905 buffer_init();
907 if (!test_ll())
909 DEBUGF("linked list test failed\n");
910 rb->splash(HZ, "linked list test failed");
911 return PLUGIN_ERROR;
914 codecthread_id = rb->create_thread(codec_thread,
915 codec_stack,
916 sizeof(codec_stack),
917 "codec"
918 IF_PRIO(,PRIORITY_PLAYBACK)
919 IF_COP(, CPU, false));
921 bufopenthread_id = rb->create_thread(bufopen_thread,
922 bufopen_stack,
923 sizeof(bufopen_stack),
924 "bufopen"
925 IF_PRIO(,PRIORITY_BACKGROUND)
926 IF_COP(, CPU, false));
928 if (!codecthread_id)
930 rb->splash(HZ, "failed to create codec thread");
931 return PLUGIN_ERROR;
933 else if (!bufopenthread_id)
935 rb->splash(HZ, "failed to create bufopen thread");
936 return PLUGIN_ERROR;
938 else
941 while (!done_playing)
943 if (data_rem() > 0 && wasted_space() > buffer_len/5) {
944 DEBUGF("there is %ld bytes of wasted space\n", wasted_space());
945 struct memory_handle *m = first_handle;
946 while (m) {
947 if (m->type == TYPE_AUDIO)
948 free_buffer(m->id);
949 m = m->next;
951 m = first_handle;
952 while (m) {
953 if (m->type != TYPE_AUDIO)
954 free_buffer(m->id);
955 m = m->next;
957 graph_view(100);
960 if (data_rem() > 0 && BUF_USED < 3*buffer_len/4 &&
961 disk_is_spinning())
963 DEBUGF("%ld bytes left to buffer and the buffer is low\n",
964 data_rem());
965 fill_buffer();
966 } else {
967 rb->sleep(HZ/2);
970 DEBUGF("done playing\n");
971 rb->yield();
974 rb->backlight_on();
975 DEBUGF("end of plugin\n");
976 return PLUGIN_OK;