Also stop copying if it would overwrite the next handle.
[Rockbox-MoB.git] / testplugin.c
blob1274779b864be83741ee2370bf261ab9bad297d2
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 = (struct memory_handle *)(&buffer[buf_widx]);
129 /* only advance the buffer write index of the size of the struct */
130 buf_widx = RINGBUF_ADD(buf_widx, sizeof(struct memory_handle));
132 if (!first_handle) {
133 /* the new handle is the first one */
134 first_handle = new_handle;
137 if (cur_handle) {
138 cur_handle->next = new_handle;
141 cur_handle = new_handle;
142 cur_handle->id = cur_handle_id++;
143 cur_handle->next = NULL;
144 num_handles++;
145 return cur_handle;
148 /* delete a given memory handle from the linked list
149 and return true for success. Nothing is actually erased from memory. */
150 static bool rm_handle(struct memory_handle *h)
152 if (h == first_handle) {
153 first_handle = h->next;
154 if (h == cur_handle) {
155 DEBUGF("removing the first and last handle\n");
156 /* h was the first and last handle */
157 cur_handle = NULL;
158 buf_ridx = buf_widx;
159 } else {
160 buf_ridx = (void *)first_handle - (void *)buffer;
162 } else {
163 struct memory_handle *m = first_handle;
164 while (m && m->next != h) {
165 m = m->next;
167 if (h && m && m->next == h) {
168 m->next = h->next;
169 if (h == cur_handle) {
170 cur_handle = m;
172 } else {
173 return false;
177 num_handles--;
178 return true;
181 /* these are unfortunalty needed to be global
182 so move_handle can invalidate them */
183 static int cached_handle_id = -1;
184 static struct memory_handle *cached_handle = NULL;
186 /* Return a pointer to the memory handle of given ID.
187 NULL if the handle wasn't found */
188 static struct memory_handle *find_handle(int handle_id)
190 /* simple caching because most of the time the requested handle
191 will either be the same as the last, or the one after the last */
192 if (cached_handle)
194 if (cached_handle_id == handle_id && cached_handle_id == cached_handle->id)
195 return cached_handle;
196 else if (cached_handle->next && (cached_handle->next->id == handle_id))
198 /* JD's quick testing showd this block was only entered
199 2/1971 calls to find_handle.
200 8/1971 calls to find_handle resulted in a cache miss */
201 cached_handle = cached_handle->next;
202 cached_handle_id = handle_id;
203 return cached_handle;
207 struct memory_handle *m = first_handle;
208 while (m && m->id != handle_id) {
209 m = m->next;
211 cached_handle_id = handle_id;
212 cached_handle = m;
213 return (m && m->id == handle_id) ? m : NULL;
216 /* Move a memory handle to newpos.
217 Return a pointer to the new location of the handle */
218 static struct memory_handle *move_handle(size_t *delta, struct memory_handle *h)
220 if (*delta < 4) {
221 /* aligning backwards would yield a negative result,
222 and moving the handle of such a small amount is a waste
223 of time anyway. */
224 return NULL;
226 /* make sure delta is 32-bit aligned so that the handle struct is. */
227 *delta = (*delta - 3) & ~3;
229 size_t newpos = RINGBUF_ADD((void *)h - (void *)buffer, *delta);
231 struct memory_handle *dest = (struct memory_handle *)(&buffer[newpos]);
233 /* Invalidate the cache to prevent it from keeping the old location of h */
234 if (h == cached_handle)
235 cached_handle = NULL;
237 /* the cur_handle pointer might need updating */
238 if (h == cur_handle) {
239 cur_handle = dest;
242 if (h == first_handle) {
243 first_handle = dest;
244 buf_ridx = newpos;
245 } else {
246 struct memory_handle *m = first_handle;
247 while (m && m->next != h) {
248 m = m->next;
250 if (h && m && m->next == h) {
251 m->next = dest;
252 } else {
253 return NULL;
257 rb->memmove(dest, h, sizeof(struct memory_handle));
259 return dest;
262 /* Buffer data for the given handle. Return the amount of data buffered
263 or -1 if the handle wasn't found */
264 static ssize_t buffer_handle(int handle_id)
266 DEBUGF("buffer_handle(%d)\n", handle_id);
267 struct memory_handle *h = find_handle(handle_id);
268 if (!h)
269 return -1;
271 if (h->filerem == 0) {
272 /* nothing left to buffer */
273 return 0;
276 if (h->fd < 0) /* file closed, reopen */
278 if (*h->path)
279 h->fd = rb->open(h->path, O_RDONLY);
280 else
281 return -1;
283 if (h->fd < 0)
284 return -1;
286 if (h->offset)
287 rb->lseek(h->fd, h->offset, SEEK_SET);
290 ssize_t ret = 0;
291 while (h->filerem > 0)
293 //DEBUGF("h: %d\n", (void *)h - (void *)buffer);
294 //DEBUGF("buf_widx: %ld\n", (long)buf_widx);
295 /* max amount to copy */
296 size_t copy_n = MIN(conf_filechunk, buffer_len - h->widx);
298 /* stop copying if it would overwrite the reading position
299 or the next handle */
300 if (RINGBUF_ADD_CROSS(h->widx, copy_n, buf_ridx) >= 0 || (h->next &&
301 RINGBUF_ADD_CROSS(h->widx, copy_n,
302 ((void *)h->next - (void *)buffer)) >= 0))
303 break;
305 /* rc is the actual amount read */
306 int rc = rb->read(h->fd, &buffer[h->widx], copy_n);
308 if (rc < 0)
310 //DEBUGF("failed on fd %d at buf_widx = %ld for handle %d\n", h->fd, (long)buf_widx, h->id);
311 DEBUGF("File ended %ld bytes early\n", (long)h->filerem);
312 h->filesize -= h->filerem;
313 h->filerem = 0;
314 break;
317 /* Advance buffer */
318 h->widx = RINGBUF_ADD(h->widx, rc);
319 if (h == cur_handle)
320 buf_widx = h->widx;
321 h->available += rc;
322 ret += rc;
323 h->filerem -= rc;
326 if (h->filerem == 0) {
327 /* finished buffering the file */
328 rb->close(h->fd);
331 DEBUGF("buffered %ld bytes (%ld of %ld available, remaining: %ld)\n",
332 ret, h->available, h->filesize, h->filerem);
334 graph_view(100);
336 return ret;
339 /* Free buffer space by moving the handle struct right before the useful
340 part of its data buffer */
341 static void free_buffer(int handle_id)
343 DEBUGF("free_buffer(%d)\n", handle_id);
344 struct memory_handle *h = find_handle(handle_id);
345 if (!h)
346 return;
348 size_t delta = RINGBUF_SUB(h->ridx, h->data);
349 h = move_handle(&delta, h);
350 if (!h) return;
351 /* The value of delta might change for alignment reasons */
352 h->data = RINGBUF_ADD(h->data, delta);
353 h->available -= delta;
355 graph_view(100);
358 static void fill_buffer(void)
360 DEBUGF("fill buffer()\n");
361 struct memory_handle *m = first_handle;
362 while (m) {
363 if (m->filerem > 0) {
364 buffer_handle(m->id);
365 return;
367 m = m->next;
371 static size_t data_rem(void)
373 size_t ret = 0;
375 struct memory_handle *m = first_handle;
376 while (m) {
377 ret += m->filerem;
378 m = m->next;
381 return ret;
384 static size_t wasted_space(void)
386 size_t ret = 0;
388 struct memory_handle *m = first_handle;
389 while (m) {
390 ret += RINGBUF_SUB(m->ridx, m->data);
391 m = m->next;
394 return ret;
397 /* Request a file be buffered
398 filename: name of the file t open
399 offset: starting offset to buffer from the file
400 RETURNS: <0 if the file cannot be opened, or one file already
401 queued to be opened, otherwise the handle for the file in the buffer
403 int bufopen(char *file, size_t offset)
405 if (cur_handle && cur_handle->filerem > 0) {
406 return -1;
409 int fd = rb->open(file, O_RDONLY);
410 if (fd < 0)
411 return -1;
413 size_t size = rb->filesize(fd) - offset;
415 DEBUGF("bufopen: %s (offset: %ld) (%ld bytes needed)...\n",
416 file, offset, size);
418 struct memory_handle *h = add_handle(&size);
419 if (!h)
421 DEBUGF("failed to add handle\n");
422 rb->close(fd);
423 return -1;
426 if (offset) rb->lseek(fd, offset, SEEK_SET);
427 rb->strncpy(h->path, file, MAX_PATH);
428 h->fd = fd;
429 h->filesize = rb->filesize(fd);
430 h->filerem = h->filesize - offset;
431 h->offset = offset;
432 h->ridx = buf_widx;
433 h->widx = buf_widx;
434 h->data = buf_widx;
435 h->available = 0;
437 DEBUGF("allocated %ld bytes. ID: %d\n", size, h->id);
438 return h->id;
441 /* Close the handle. Return 0 for success and < 0 for failure */
442 int bufclose(int handle_id)
444 DEBUGF("bufclose(%d)\n", handle_id);
445 struct memory_handle *h = find_handle(handle_id);
446 if (!h)
447 return -1;
449 rm_handle(h);
450 return 0;
453 /* Set the reading index in a handle (relatively to the start of the handle data).
454 Return 0 for success and < 0 for failure */
455 int bufseek(int handle_id, size_t offset)
457 struct memory_handle *h = find_handle(handle_id);
458 if (!h)
459 return -1;
461 if (offset > h->available)
462 return -2;
464 h->ridx = RINGBUF_ADD(h->data, offset);
465 return 0;
468 /* Advance the reading index in a handle (relatively to its current position).
469 Return 0 for success and < 0 for failure */
470 int bufadvance(int handle_id, off_t offset)
472 struct memory_handle *h = find_handle(handle_id);
473 if (!h)
474 return -1;
476 if (offset >= 0)
478 /* check for access beyond what's available */
479 if ((size_t)offset > (h->available - RINGBUF_SUB(h->ridx, h->data)))
480 return -2;
482 h->ridx = RINGBUF_ADD(h->ridx, offset);
484 else
486 /* check for access before what's available */
487 if ((size_t)(-offset) > RINGBUF_SUB(h->ridx, h->data))
488 return -2;
490 h->ridx = RINGBUF_SUB(h->ridx, (size_t)(-offset));
493 return 0;
496 /* Copy data from the given handle to the dest buffer.
497 Return the number of bytes copied or < 0 for failure. */
498 ssize_t bufread(int handle_id, size_t size, char *dest)
500 struct memory_handle *h = find_handle(handle_id);
501 size_t buffered_data;
502 if (!h)
503 return -1;
505 if (h->available == 0 && h->filerem > 0)
506 return -2;
508 if (h->available == 0 && h->filerem == 0)
509 return 0;
511 buffered_data = MIN(size, h->available);
513 if (h->ridx + buffered_data > buffer_len)
515 size_t read = buffer_len - h->ridx;
516 rb->memcpy(dest, &buffer[h->ridx], read);
517 rb->memcpy(dest+read, buffer, buffered_data - read);
519 else rb->memcpy(dest, &buffer[h->ridx], buffered_data);
521 h->ridx = RINGBUF_ADD(h->ridx, buffered_data);
522 h->available -= buffered_data;
523 return buffered_data;
526 /* Update the "data" pointer to make the handle's data available to the caller.
527 Return the length of the available linear data or < 0 for failure. */
528 ssize_t bufgetdata(int handle_id, size_t size, unsigned char **data)
530 struct memory_handle *h = find_handle(handle_id);
531 if (!h)
532 return -1;
534 if (h->available == 0 && h->filerem > 0)
535 return -2;
537 if (h->available == 0 && h->filerem == 0)
538 return 0;
540 ssize_t ret;
542 if (h->ridx + size > buffer_len &&
543 h->available - RINGBUF_SUB(h->ridx, h->data) >= size)
545 /* use the guard buffer to provide what was requested. */
546 size_t copy_n = h->ridx + size - buffer_len;
547 rb->memcpy(guard_buffer, (unsigned char *)buffer, copy_n);
548 ret = size;
549 DEBUGF("used the guard buffer to complete\n");
551 else
553 ret = MIN(h->available - RINGBUF_SUB(h->ridx, h->data),
554 buffer_len - h->ridx);
557 *data = (unsigned char *)&buffer[h->ridx];
559 //DEBUGF("bufgetdata(%d): h->ridx=%ld, ret=%ld\n", handle_id, (long)h->ridx, ret);
560 return ret;
563 bool test_ll(void)
565 struct memory_handle *m1, *m2, *m3, *m4;
567 if (cur_handle != NULL || first_handle != NULL)
568 return false;
570 m1 = add_handle(NULL);
572 if (cur_handle != m1 || first_handle != m1 || m1->next != NULL)
573 return false;
575 m2 = add_handle(NULL);
577 if (cur_handle != m2 || first_handle != m1 || m1->next != m2 || m2->next != NULL)
578 return false;
580 m3 = add_handle(NULL);
582 if (cur_handle != m3 || first_handle != m1 || m2->next != m3 || m3->next != NULL)
583 return false;
585 rm_handle(m2);
587 if (cur_handle != m3 || first_handle != m1 || m1->next != m3)
588 return false;
590 rm_handle(m3);
592 if (cur_handle != m1 || first_handle != m1 || m1->next != NULL)
593 return false;
595 m4 = add_handle(NULL);
597 if (cur_handle != m4 || first_handle != m1 || m1->next != m4 || m4->next != NULL)
598 return false;
600 rm_handle(m1);
602 if (cur_handle != m4 || first_handle != m4)
603 return false;
605 rm_handle(m4);
607 if (cur_handle != NULL || first_handle != NULL)
608 return false;
610 m1 = add_handle(NULL);
611 m2 = add_handle(NULL);
612 m3 = add_handle(NULL);
613 m4 = add_handle(NULL);
615 if (cur_handle != m4 || first_handle != m1)
616 return false;
618 size_t delta = 1024*100;
619 m2 = move_handle(&delta, m2);
621 if (cur_handle != m4 || first_handle != m1 || m1->next != m2 || m2->next != m3)
622 return false;
624 delta = 1024*100*3;
625 m1 = move_handle(&delta, m1);
627 if (cur_handle != m4 || first_handle != m1 || m1->next != m2)
628 return false;
630 rm_handle(m1);
631 rm_handle(m2);
632 rm_handle(m3);
633 rm_handle(m4);
635 if (cur_handle != NULL || first_handle != NULL)
636 return false;
638 return true;
641 /* display a nice graphical view of the ringbuffer. */
642 static void graph_view(int width)
644 #ifndef ROCKBOX_HAS_LOGF
645 int i, r_pos, w_pos;
646 r_pos = buf_ridx * width / buffer_len;
647 w_pos = buf_widx * width / buffer_len;
649 DEBUGF("|");
650 for (i=0; i <= width; i++)
652 if (i != r_pos && i != w_pos)
654 if (buf_ridx <= buf_widx)
656 if (i > r_pos && i < w_pos) {
657 DEBUGF(">");
658 } else {
659 DEBUGF("-");
662 else
664 if (i > r_pos || i < w_pos) {
665 DEBUGF(">");
666 } else {
667 DEBUGF("-");
671 else
673 if (i == r_pos && i == w_pos)
675 if (buf_ridx <= buf_widx) {
676 DEBUGF("RW");
677 } else {
678 DEBUGF("WR");
680 } else if (i == r_pos) {
681 DEBUGF("R");
682 } else if (i == w_pos) {
683 DEBUGF("W");
687 DEBUGF("|");
688 DEBUGF("\n");
689 #else
690 (void)width;
691 #endif
694 void print_progress(size_t amount, int file, int numfiles)
696 char buf[32];
697 rb->lcd_clear_display();
698 rb->snprintf(buf, sizeof(buf), "file %d of %d", file, numfiles);
699 rb->lcd_puts(0, 0, buf);
700 rb->snprintf(buf, sizeof(buf), "read: %ld", amount);
701 rb->lcd_puts(0, 1, buf);
702 rb->lcd_update();
705 bool buffer_init(void)
707 buffer = rb->plugin_get_audio_buffer(&buffer_len);
708 if (!buffer)
710 DEBUGF("couldn't allocate buffer\n");
711 return false;
713 buffer_len -= GUARD_SIZE;
714 guard_buffer = buffer + buffer_len;
716 buf_widx = 0;
717 buf_ridx = 0;
719 first_handle = NULL;
720 num_handles = 0;
722 conf_filechunk = AUDIO_DEFAULT_FILECHUNK;
724 return true;
727 bool disk_is_spinning(void)
729 return true;
734 static long codec_stack[4*DEFAULT_STACK_SIZE/sizeof(long)];
735 static struct thread_entry* codecthread_id;
737 static long bufopen_stack[DEFAULT_STACK_SIZE/sizeof(long)];
738 static struct thread_entry* bufopenthread_id;
740 bool done_playing = false;
742 #define MAX_HANDLES 16
744 int handles[MAX_HANDLES];
746 void codec_thread(void)
748 int idx = 0;
749 int fd = -1; /* used to write the files out as they are read */
750 unsigned char *data;
751 char outfile[MAX_PATH];
752 long read, total = 0;
754 while (1)
756 if (!done_playing)
758 if (handles[idx] > 0) {
760 if (fd < 0) {
761 rb->snprintf(outfile, MAX_PATH, "/file%d.mp3", idx);
762 fd = rb->open(outfile, O_CREAT|O_TRUNC|O_WRONLY);
763 if (fd < 0) {
764 DEBUGF("couldn't create file\n");
765 rb->splash(HZ, "couldn't create file");
769 do {
770 read = bufgetdata(handles[idx], GUARD_SIZE, &data);
771 if (read >= 0) {
772 read = MIN(read, GUARD_SIZE);
773 rb->write(fd, data, read);
774 total += read;
775 bufadvance(handles[idx], read);
776 print_progress(total, idx+1, num_files);
778 rb->sleep(HZ/20);
779 } while (read > 0);
781 if (read >= 0 && total >= 0) {
782 DEBUGF("read %ld bytes from handle %d\n", total, handles[idx]);
785 if (read == -2) {
786 DEBUGF("data for handle %d isn't ready\n", handles[idx]);
787 } else if (read == -1) {
788 DEBUGF("couldn't find handle %d\n", handles[idx]);
789 } else if (read == 0) {
790 DEBUGF("finished reading handle %d\n", handles[idx]);
791 bufclose(handles[idx]);
792 rb->close(fd);
793 fd = -1;
794 total = 0;
795 idx++;
797 if (idx >= num_files) {
798 done_playing = true;
799 break;
805 rb->sleep(HZ/4);
808 DEBUGF("removing the codec thread\n");
809 rb->remove_thread(NULL);
812 void bufopen_thread(void)
814 int idx = 0, ret;
815 while (idx < num_files)
817 ret = bufopen(files[idx], 0);
818 if (ret > 0) {
819 handles[idx++] = ret;
821 rb->sleep(HZ*8);
824 DEBUGF("bufopen thread finished\n");
825 rb->remove_thread(NULL);
829 /* this is the plugin entry point */
830 enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
832 (void)parameter;
833 rb = api;
835 buffer_init();
837 if (!test_ll())
839 DEBUGF("linked list test failed\n");
840 rb->splash(HZ, "linked list test failed");
841 return PLUGIN_ERROR;
844 codecthread_id = rb->create_thread(codec_thread,
845 codec_stack,
846 sizeof(codec_stack),
847 "codec"
848 IF_PRIO(,PRIORITY_PLAYBACK)
849 IF_COP(, CPU, false));
851 bufopenthread_id = rb->create_thread(bufopen_thread,
852 bufopen_stack,
853 sizeof(bufopen_stack),
854 "bufopen"
855 IF_PRIO(,PRIORITY_BACKGROUND)
856 IF_COP(, CPU, false));
858 if (!codecthread_id)
860 rb->splash(HZ, "failed to create codec thread");
861 return PLUGIN_ERROR;
863 else if (!bufopenthread_id)
865 rb->splash(HZ, "failed to create bufopen thread");
866 return PLUGIN_ERROR;
868 else
871 while (!done_playing)
873 if (wasted_space() > buffer_len/4) {
874 DEBUGF("there is %ld bytes of wasted space\n", wasted_space());
875 struct memory_handle *m = first_handle;
876 while (m) {
877 free_buffer(m->id);
878 m = m->next;
882 if (data_rem() > 0 && BUF_USED < buffer_len/4 && disk_is_spinning()) {
883 DEBUGF("%ld bytes left to buffer and the buffer is running low\n", data_rem());
884 fill_buffer();
885 } else {
886 rb->sleep(HZ/2);
889 DEBUGF("done playing\n");
890 rb->yield();
893 rb->backlight_on();
894 DEBUGF("end of plugin\n");
895 return PLUGIN_OK;