bufread: Make it behave correctly.
[Rockbox-MoB.git] / testplugin.c
blob13319aed597978de834cd31a59489325ce01d1d7
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 static void graph_view(int width);
57 enum data_type {
58 TYPE_CODEC,
59 TYPE_AUDIO,
60 TYPE_ID3,
61 TYPE_CUESHEET,
62 TYPE_IMAGE,
63 TYPE_BUFFER,
64 TYPE_UNKNOWN,
67 struct memory_handle {
68 int id; /* A unique ID for the handle */
69 enum data_type type;
70 char path[MAX_PATH];
71 int fd;
72 size_t data; /* Start index of the handle's data buffer */
73 size_t ridx; /* Current read pointer, relative to the main buffer */
74 size_t widx; /* Current write pointer */
75 size_t filesize; /* File total length */
76 size_t filerem; /* Remaining bytes of file NOT in buffer */
77 size_t available; /* Available bytes to read from buffer */
78 size_t offset; /* Offset at which we started reading the file */
79 struct memory_handle *next;
83 static char *buffer;
84 static char *guard_buffer;
86 static size_t buffer_len;
88 static size_t buf_widx; /* current writing position */
89 static size_t buf_ridx; /* current reading position */
90 /* buf_*idx are values relative to the buffer, not real pointers. */
92 static size_t conf_filechunk;
94 /* current memory handle in the linked list. NULL when the list is empty. */
95 static struct memory_handle *cur_handle;
96 /* first memory handle in the linked list. NULL when the list is empty. */
97 static struct memory_handle *first_handle;
99 static int num_handles; /* number of handles in the list */
101 /* Handle cache (makes find_handle faster).
102 These need to be global so that move_handle can invalidate them. */
103 static int cached_handle_id = -1;
104 static struct memory_handle *cached_handle = NULL;
108 LINKED LIST MANAGEMENT
109 ======================
111 add_handle : Add a handle to the list
112 rm_handle : Remove a handle from the list
113 find_handle : Get a handle pointer from an ID
114 move_handle : Move a handle in the buffer (with or without its data)
116 These functions only handle the linked list structure. They don't touch the
117 contents of the struct memory_handle headers. They also change the buf_*idx
118 pointers when necessary and manage the handle IDs.
120 The first and current (== last) handle are kept track of.
121 A new handle is added at buf_widx and becomes the current one.
122 buf_widx always points to the current writing position for the current handle
123 buf_ridx always points to the location of the first handle.
124 buf_ridx == buf_widx means the buffer is empty.
128 /* Add a new handle to the linked list and return it. It will have become the
129 new current handle. The handle will reserve "data_size" bytes or if that's
130 not possible, decrease "data_size" to allow adding the handle. */
131 static struct memory_handle *add_handle(size_t *data_size)
133 /* this will give each handle a unique id */
134 static int cur_handle_id = 1;
136 /* make sure buf_widx is 32-bit aligned so that the handle struct is,
137 but before that we check we can actually align. */
138 if (RINGBUF_ADD_CROSS(buf_widx, 3, buf_ridx) >= 0) {
139 return NULL;
141 buf_widx = (RINGBUF_ADD(buf_widx, 3)) & ~3;
143 size_t len = (data_size ? *data_size : 0)
144 + sizeof(struct memory_handle);
146 /* check that we actually can add the handle and its data */
147 int overlap = RINGBUF_ADD_CROSS(buf_widx, len, buf_ridx);
148 if (overlap >= 0) {
149 *data_size -= overlap;
150 len -= overlap;
152 if (len < sizeof(struct memory_handle)) {
153 /* There isn't even enough space to write the struct */
154 return NULL;
157 struct memory_handle *new_handle =
158 (struct memory_handle *)(&buffer[buf_widx]);
160 /* only advance the buffer write index of the size of the struct */
161 buf_widx = RINGBUF_ADD(buf_widx, sizeof(struct memory_handle));
163 if (!first_handle) {
164 /* the new handle is the first one */
165 first_handle = new_handle;
168 if (cur_handle) {
169 cur_handle->next = new_handle;
172 cur_handle = new_handle;
173 cur_handle->id = cur_handle_id++;
174 cur_handle->next = NULL;
175 num_handles++;
176 return cur_handle;
179 /* Delete a given memory handle from the linked list
180 and return true for success. Nothing is actually erased from memory. */
181 static bool rm_handle(struct memory_handle *h)
183 if (h == first_handle) {
184 first_handle = h->next;
185 if (h == cur_handle) {
186 /* h was the first and last handle: the buffer is now empty */
187 cur_handle = NULL;
188 buf_ridx = buf_widx;
189 } else {
190 /* update buf_ridx to point to the new first handle */
191 buf_ridx = (void *)first_handle - (void *)buffer;
193 } else {
194 struct memory_handle *m = first_handle;
195 while (m && m->next != h) {
196 m = m->next;
198 if (h && m && m->next == h) {
199 m->next = h->next;
200 if (h == cur_handle) {
201 cur_handle = m;
203 } else {
204 return false;
208 num_handles--;
209 return true;
212 /* Return a pointer to the memory handle of given ID.
213 NULL if the handle wasn't found */
214 static struct memory_handle *find_handle(int handle_id)
216 /* simple caching because most of the time the requested handle
217 will either be the same as the last, or the one after the last */
218 if (cached_handle)
220 if (cached_handle_id == handle_id &&
221 cached_handle_id == cached_handle->id)
222 return cached_handle;
223 else if (cached_handle->next && (cached_handle->next->id == handle_id))
225 /* JD's quick testing showd this block was only entered
226 2/1971 calls to find_handle.
227 8/1971 calls to find_handle resulted in a cache miss */
228 cached_handle = cached_handle->next;
229 cached_handle_id = handle_id;
230 return cached_handle;
234 struct memory_handle *m = first_handle;
235 while (m && m->id != handle_id) {
236 m = m->next;
238 cached_handle_id = handle_id;
239 cached_handle = m;
240 return (m && m->id == handle_id) ? m : NULL;
243 /* Move a memory handle and data_size of its data of delta.
244 Return a pointer to the new location of the handle.
245 delta is the value of which to move the struct data.
246 data_size is the amount of data to move along with the struct. */
247 static struct memory_handle *move_handle(struct memory_handle *h,
248 size_t *delta, size_t data_size)
250 if (*delta < 4) {
251 /* aligning backwards would yield a negative result,
252 and moving the handle of such a small amount is a waste
253 of time anyway. */
254 return NULL;
256 /* make sure delta is 32-bit aligned so that the handle struct is. */
257 *delta = (*delta - 3) & ~3;
259 size_t newpos = RINGBUF_ADD((void *)h - (void *)buffer, *delta);
261 struct memory_handle *dest = (struct memory_handle *)(&buffer[newpos]);
263 /* Invalidate the cache to prevent it from keeping the old location of h */
264 if (h == cached_handle)
265 cached_handle = NULL;
267 /* the cur_handle pointer might need updating */
268 if (h == cur_handle) {
269 cur_handle = dest;
272 if (h == first_handle) {
273 first_handle = dest;
274 buf_ridx = newpos;
275 } else {
276 struct memory_handle *m = first_handle;
277 while (m && m->next != h) {
278 m = m->next;
280 if (h && m && m->next == h) {
281 m->next = dest;
282 } else {
283 return NULL;
287 rb->memmove(dest, h, sizeof(struct memory_handle) + data_size);
289 return dest;
294 BUFFER SPACE MANAGEMENT
295 =======================
297 buffer_handle : Buffer data for a handle
298 free_buffer : Free buffer space by moving a handle
299 fill_buffer : Call buffer_handle for all handles that have data to buffer
300 data_rem : Total amount of data needing to be buffered
301 wasted_space : Total amount of space available for freeing
303 These functions are used by the buffering thread to manage buffer space.
306 /* Buffer data for the given handle. Return the amount of data buffered
307 or -1 if the handle wasn't found */
308 static ssize_t buffer_handle(int handle_id)
310 DEBUGF("buffer_handle(%d)\n", handle_id);
311 struct memory_handle *h = find_handle(handle_id);
312 if (!h)
313 return -1;
315 if (h->filerem == 0) {
316 /* nothing left to buffer */
317 return 0;
320 if (h->fd < 0) /* file closed, reopen */
322 if (*h->path)
323 h->fd = rb->open(h->path, O_RDONLY);
324 else
325 return -1;
327 if (h->fd < 0)
328 return -1;
330 if (h->offset)
331 rb->lseek(h->fd, h->offset, SEEK_SET);
334 ssize_t ret = 0;
335 while (h->filerem > 0)
337 /* max amount to copy */
338 size_t copy_n = MIN( MIN(h->filerem, conf_filechunk),
339 buffer_len - h->widx);
341 /* stop copying if it would overwrite the reading position
342 or the next handle */
343 if (RINGBUF_ADD_CROSS(h->widx, copy_n, buf_ridx) >= 0 || (h->next &&
344 RINGBUF_ADD_CROSS(h->widx, copy_n, (unsigned)
345 ((void *)h->next - (void *)buffer)) > 0))
346 break;
348 /* rc is the actual amount read */
349 int rc = rb->read(h->fd, &buffer[h->widx], copy_n);
351 if (rc < 0)
353 DEBUGF("File ended %ld bytes early\n", (long)h->filerem);
354 h->filesize -= h->filerem;
355 h->filerem = 0;
356 break;
359 /* Advance buffer */
360 h->widx = RINGBUF_ADD(h->widx, rc);
361 if (h == cur_handle)
362 buf_widx = h->widx;
363 h->available += rc;
364 ret += rc;
365 h->filerem -= rc;
368 if (h->filerem == 0) {
369 /* finished buffering the file */
370 rb->close(h->fd);
373 DEBUGF("buffered %ld bytes (%ld of %ld available, remaining: %ld)\n",
374 ret, h->available, h->filesize, h->filerem);
376 graph_view(100);
378 return ret;
381 /* Free buffer space by moving the handle struct right before the useful
382 part of its data buffer or by moving all the data. */
383 static void free_buffer(int handle_id)
385 struct memory_handle *h = find_handle(handle_id);
386 if (!h)
387 return;
389 size_t delta;
390 /* The value of delta might change for alignment reasons */
392 if (h->next && (h->type == TYPE_ID3 || h->type == TYPE_CUESHEET ||
393 h->type == TYPE_IMAGE) && h->filerem == 0 )
395 /* metadata handle: we can move all of it */
396 delta = RINGBUF_SUB( (unsigned)((void *)h->next - (void *)buffer),
397 h->data) - h->available;
398 h = move_handle(h, &delta, h->available);
399 if (!h) return;
400 h->data = RINGBUF_ADD(h->data, delta);
401 h->ridx = RINGBUF_ADD(h->ridx, delta);
402 DEBUGF("free_buffer(%d): metadata, moved by %ld bytes\n",
403 handle_id, delta);
405 else
407 /* only move the handle struct */
408 delta = RINGBUF_SUB(h->ridx, h->data);
409 h = move_handle(h, &delta, 0);
410 if (!h) return;
411 h->data = RINGBUF_ADD(h->data, delta);
412 h->available -= delta;
413 DEBUGF("free_buffer(%d): audio, %ld bytes freed\n", handle_id, delta);
417 /* Fill the buffer by buffering as much data as possible for handles that still
418 have data left to buffer */
419 static void fill_buffer(void)
421 DEBUGF("fill buffer()\n");
422 struct memory_handle *m = first_handle;
423 while (m) {
424 if (m->filerem > 0) {
425 buffer_handle(m->id);
427 m = m->next;
431 /* Return the total amount of data left to be buffered for all the handles */
432 static size_t data_rem(void)
434 size_t ret = 0;
436 struct memory_handle *m = first_handle;
437 while (m) {
438 ret += m->filerem;
439 m = m->next;
442 return ret;
445 /* Return the amount of data we have but don't need anymore. This data can be
446 safely erased to reclaim buffer space. */
447 static size_t wasted_space(void)
449 size_t ret = 0;
451 struct memory_handle *m = first_handle;
452 while (m) {
453 ret += RINGBUF_SUB(m->ridx, m->data);
454 m = m->next;
457 return ret;
462 BUFFERING API FUNCTIONS
463 =======================
465 bufopen : Request the opening of a new handle for a file
466 bufclose : Close an open handle
467 bufseek : Set the read pointer in a handle
468 bufadvance : Move the read pointer in a handle
469 bufread : Copy data from a handle into a given buffer
470 bufgetdata : Give a pointer to the handle's data
472 These functions are exported, to allow interaction with the buffer.
473 They take care of the content of the structs, and rely on the linked list
474 management functions for all the actual handle management work.
478 /* Request a file be buffered
479 filename: name of the file to open
480 offset:starting offset to buffer from the file
481 return value: <0 if the file cannot be opened, or one file already
482 queued to be opened, otherwise the handle for the file in the buffer
484 int bufopen(char *file, size_t offset, enum data_type type)
486 if (cur_handle && cur_handle->filerem > 0) {
487 /* the current handle hasn't finished buffering. We can only add
488 a new one if there is already enough free space to finish
489 the buffering. */
490 if (cur_handle->filerem < (buffer_len - BUF_USED)) {
491 /* Before adding the new handle we reserve some space for the
492 current one to finish buffering its data. */
493 buf_widx = RINGBUF_ADD(buf_widx, cur_handle->filerem);
494 } else {
495 return -1;
499 int fd = rb->open(file, O_RDONLY);
500 if (fd < 0)
501 return -1;
503 size_t size = rb->filesize(fd) - offset;
505 DEBUGF("bufopen: %s (offset: %ld) (%ld bytes needed)...\n",
506 file, offset, size);
508 struct memory_handle *h = add_handle(&size);
509 if (!h)
511 DEBUGF("failed to add handle\n");
512 rb->close(fd);
513 return -1;
516 if (offset) rb->lseek(fd, offset, SEEK_SET);
517 rb->strncpy(h->path, file, MAX_PATH);
518 h->fd = fd;
519 h->filesize = rb->filesize(fd);
520 h->filerem = h->filesize - offset;
521 h->offset = offset;
522 h->ridx = buf_widx;
523 h->widx = buf_widx;
524 h->data = buf_widx;
525 h->available = 0;
526 h->type = type;
528 DEBUGF("allocated %ld bytes. ID: %d\n", size, h->id);
529 return h->id;
532 /* Close the handle. Return 0 for success and < 0 for failure */
533 int bufclose(int handle_id)
535 DEBUGF("bufclose(%d)\n", handle_id);
536 struct memory_handle *h = find_handle(handle_id);
537 if (!h)
538 return -1;
540 rm_handle(h);
541 return 0;
544 /* Set reading index in handle (relatively to the start of the handle data).
545 Return 0 for success and < 0 for failure */
546 int bufseek(int handle_id, size_t offset)
548 struct memory_handle *h = find_handle(handle_id);
549 if (!h)
550 return -1;
552 if (offset > h->available)
553 return -2;
555 h->ridx = RINGBUF_ADD(h->data, offset);
556 return 0;
559 /* Advance the reading index in a handle (relatively to its current position).
560 Return 0 for success and < 0 for failure */
561 int bufadvance(int handle_id, off_t offset)
563 struct memory_handle *h = find_handle(handle_id);
564 if (!h)
565 return -1;
567 if (offset >= 0)
569 /* check for access beyond what's available */
570 if ((size_t)offset > (h->available - RINGBUF_SUB(h->ridx, h->data)))
571 return -2;
573 h->ridx = RINGBUF_ADD(h->ridx, offset);
575 else
577 /* check for access before what's available */
578 if ((size_t)(-offset) > RINGBUF_SUB(h->ridx, h->data))
579 return -2;
581 h->ridx = RINGBUF_SUB(h->ridx, (size_t)(-offset));
584 return 0;
587 /* Copy data from the given handle to the dest buffer.
588 Return the number of bytes copied or < 0 for failure. */
589 ssize_t bufread(int handle_id, size_t size, char *dest)
591 struct memory_handle *h = find_handle(handle_id);
592 size_t buffered_data;
593 if (!h)
594 return -1;
596 if (h->available == 0 && h->filerem > 0) /* Data isn't ready */
597 return -2;
599 if (h->available == 0 && h->filerem == 0) /* File is finished reading */
600 return 0;
602 buffered_data = MIN(size, h->available - RINGBUF_SUB(h->ridx, h->data));
604 if (h->ridx + buffered_data > buffer_len)
606 size_t read = buffer_len - h->ridx;
607 rb->memcpy(dest, &buffer[h->ridx], read);
608 rb->memcpy(dest+read, buffer, buffered_data - read);
610 else rb->memcpy(dest, &buffer[h->ridx], buffered_data);
612 return buffered_data;
615 /* Update the "data" pointer to make the handle's data available to the caller.
616 Return the length of the available linear data or < 0 for failure.
617 size is the amount of linear data requested. it can be 0 to get as
618 much as possible.
619 The guard buffer may be used to provide the requested size */
620 ssize_t bufgetdata(int handle_id, size_t size, unsigned char **data)
622 struct memory_handle *h = find_handle(handle_id);
623 if (!h)
624 return -1;
626 if (h->available == 0 && h->filerem > 0) /* Data isn't ready */
627 return -2;
629 if (h->available == 0 && h->filerem == 0) /* File is finished reading */
630 return 0;
632 ssize_t ret;
634 if (h->ridx + size > buffer_len &&
635 h->available - RINGBUF_SUB(h->ridx, h->data) >= size)
637 /* use the guard buffer to provide what was requested. */
638 size_t copy_n = MIN(h->ridx + size - buffer_len, GUARD_SIZE);
639 rb->memcpy(guard_buffer, (unsigned char *)buffer, copy_n);
640 ret = buffer_len - h->ridx + copy_n;
641 DEBUGF("used the guard buffer to complete\n");
643 else
645 ret = MIN(h->available - RINGBUF_SUB(h->ridx, h->data),
646 buffer_len - h->ridx);
649 *data = (unsigned char *)&buffer[h->ridx];
651 /* DEBUGF("bufgetdata(%d): h->ridx=%ld, ret=%ld\n", handle_id,
652 (long)h->ridx, ret); */
653 return ret;
658 UTILITY FUNCTIONS
659 =================
662 bool test_ll(void)
664 struct memory_handle *m1, *m2, *m3, *m4;
666 if (cur_handle != NULL || first_handle != NULL)
667 return false;
669 m1 = add_handle(NULL);
671 if (cur_handle != m1 || first_handle != m1 || m1->next != NULL)
672 return false;
674 m2 = add_handle(NULL);
676 if (cur_handle != m2 || first_handle != m1 || m1->next != m2 || m2->next != NULL)
677 return false;
679 m3 = add_handle(NULL);
681 if (cur_handle != m3 || first_handle != m1 || m2->next != m3 || m3->next != NULL)
682 return false;
684 rm_handle(m2);
686 if (cur_handle != m3 || first_handle != m1 || m1->next != m3)
687 return false;
689 rm_handle(m3);
691 if (cur_handle != m1 || first_handle != m1 || m1->next != NULL)
692 return false;
694 m4 = add_handle(NULL);
696 if (cur_handle != m4 || first_handle != m1 || m1->next != m4 || m4->next != NULL)
697 return false;
699 rm_handle(m1);
701 if (cur_handle != m4 || first_handle != m4)
702 return false;
704 rm_handle(m4);
706 if (cur_handle != NULL || first_handle != NULL)
707 return false;
709 m1 = add_handle(NULL);
710 m2 = add_handle(NULL);
711 m3 = add_handle(NULL);
712 m4 = add_handle(NULL);
714 if (cur_handle != m4 || first_handle != m1)
715 return false;
717 size_t delta = 1024*100;
718 m2 = move_handle(m2, &delta, 0);
720 if (cur_handle != m4 || first_handle != m1 || m1->next != m2 || m2->next != m3)
721 return false;
723 delta = 1024*100*3;
724 m1 = move_handle(m1, &delta, 0);
726 if (cur_handle != m4 || first_handle != m1 || m1->next != m2)
727 return false;
729 rm_handle(m1);
730 rm_handle(m2);
731 rm_handle(m3);
732 rm_handle(m4);
734 if (cur_handle != NULL || first_handle != NULL)
735 return false;
737 return true;
740 /* display a nice graphical view of the ringbuffer. */
741 static void graph_view(int width)
743 #ifndef ROCKBOX_HAS_LOGF
744 int i, r_pos, w_pos;
745 r_pos = buf_ridx * width / buffer_len;
746 w_pos = buf_widx * width / buffer_len;
748 DEBUGF("|");
749 for (i=0; i <= width; i++)
751 if (i != r_pos && i != w_pos)
753 if (buf_ridx <= buf_widx)
755 if (i > r_pos && i < w_pos) {
756 DEBUGF(">");
757 } else {
758 DEBUGF("-");
761 else
763 if (i > r_pos || i < w_pos) {
764 DEBUGF(">");
765 } else {
766 DEBUGF("-");
770 else
772 if (i == r_pos && i == w_pos)
774 if (buf_ridx <= buf_widx) {
775 DEBUGF("RW");
776 } else {
777 DEBUGF("WR");
779 } else if (i == r_pos) {
780 DEBUGF("R");
781 } else if (i == w_pos) {
782 DEBUGF("W");
786 DEBUGF("|");
787 DEBUGF("\n");
788 #else
789 (void)width;
790 #endif
793 void print_progress(size_t amount, int file, int numfiles)
795 char buf[32];
796 rb->snprintf(buf, sizeof(buf), "file %d of %d", file, numfiles);
797 rb->lcd_puts(0, 0, buf);
798 rb->snprintf(buf, sizeof(buf), "read: %ld", amount);
799 rb->lcd_puts(0, 1, buf);
802 void print_metadata(int handle_id)
804 ssize_t ret;
805 char *artist, *title, *newline;
806 char art[50], ttl[50];
807 int artist_len, title_len;
808 unsigned char *buf;
810 ret = bufgetdata(handle_id, 0, &buf);
812 artist = buf;
813 newline = rb->strchr(artist, '\n');
814 artist_len = newline - artist;
815 rb->strncpy(art, artist, artist_len);
816 art[artist_len] = 0;
818 title = newline + 1;
819 newline = rb->strchr(title, '\n');
820 title_len = newline - title;
821 rb->strncpy(ttl, title, title_len);
822 ttl[title_len] = 0;
824 rb->lcd_puts(0, 3, art);
825 rb->lcd_puts(0, 4, ttl);
828 bool buffer_init(void)
830 buffer = rb->plugin_get_audio_buffer(&buffer_len);
831 if (!buffer)
833 DEBUGF("couldn't allocate buffer\n");
834 return false;
836 buffer_len -= GUARD_SIZE;
837 guard_buffer = buffer + buffer_len;
839 buf_widx = 0;
840 buf_ridx = 0;
842 first_handle = NULL;
843 num_handles = 0;
845 conf_filechunk = AUDIO_DEFAULT_FILECHUNK;
847 return true;
850 bool disk_is_spinning(void)
852 return true;
857 THREADING CODE
858 ==============
861 static long codec_stack[4*DEFAULT_STACK_SIZE/sizeof(long)];
862 static struct thread_entry* codecthread_id;
864 static long bufopen_stack[2*DEFAULT_STACK_SIZE/sizeof(long)];
865 static struct thread_entry* bufopenthread_id;
867 bool done_playing = false;
869 #define MAX_HANDLES 16
871 int handles[MAX_HANDLES];
872 int meta_handles[MAX_HANDLES];
874 void codec_thread(void)
876 int idx = 0;
877 int fd = -1; /* used to write the files out as they are read */
878 unsigned char *data;
879 char outfile[MAX_PATH];
880 long read, total = 0;
882 while (1)
884 if (!done_playing)
886 if (handles[idx] > 0) {
888 /* create the output file */
889 if (fd < 0) {
890 rb->snprintf(outfile, MAX_PATH, "/file%d.mp3", idx);
891 fd = rb->open(outfile, O_CREAT|O_TRUNC|O_WRONLY);
892 if (fd < 0) {
893 DEBUGF("couldn't create file\n");
894 rb->splash(HZ, "couldn't create file");
898 /* read as much data as possible */
899 do {
900 read = bufgetdata(handles[idx], GUARD_SIZE, &data);
901 if (read >= 0) {
902 read = MIN(read, GUARD_SIZE);
903 rb->write(fd, data, read);
904 total += read;
905 bufadvance(handles[idx], read);
906 rb->lcd_clear_display();
907 print_progress(total, idx+1, num_files);
908 print_metadata(meta_handles[idx]);
909 rb->lcd_update();
911 rb->sleep(HZ/100);
912 } while (read > 0);
914 if (read >= 0 && total >= 0) {
915 /* some data was read */
916 DEBUGF("read %ld bytes from handle %d\n", total,
917 handles[idx]);
920 /* check the return value to determine what exactly happened */
921 if (read == -2) {
922 DEBUGF("data for handle %d isn't ready\n", handles[idx]);
923 } else if (read == -1) {
924 DEBUGF("couldn't find handle %d\n", handles[idx]);
925 } else if (read == 0) {
926 DEBUGF("finished reading handle %d\n", handles[idx]);
927 bufclose(handles[idx]);
928 bufclose(meta_handles[idx]);
929 rb->close(fd);
930 fd = -1;
931 total = 0;
933 /* move on to the next file and check if we've finished */
934 idx++;
935 if (idx >= num_files) {
936 done_playing = true;
937 break;
943 rb->sleep(HZ/10);
946 DEBUGF("removing the codec thread\n");
947 rb->remove_thread(NULL);
950 void bufopen_thread(void)
952 int idx = 0, ret;
953 char buf[MAX_PATH];
954 while (idx < num_files)
956 /* open the metadata file */
957 rb->snprintf(buf, MAX_PATH, "/meta%s.txt", files[idx]);
958 meta_handles[idx] = bufopen(buf, 0, TYPE_ID3);
959 if (meta_handles[idx] > 0)
961 /* open the audio file */
962 ret = bufopen(files[idx], 0, TYPE_AUDIO);
963 if (ret > 0) {
964 handles[idx++] = ret;
965 } else {
966 /* couldn't open the audio file, close the metadata handle */
967 bufclose(meta_handles[idx]);
970 rb->sleep(HZ*2);
973 DEBUGF("bufopen thread finished\n");
974 rb->remove_thread(NULL);
978 /* this is the plugin entry point */
979 enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
981 (void)parameter;
982 rb = api;
984 buffer_init();
986 if (!test_ll())
988 DEBUGF("linked list test failed\n");
989 rb->splash(HZ, "linked list test failed");
990 return PLUGIN_ERROR;
993 codecthread_id = rb->create_thread(codec_thread,
994 codec_stack,
995 sizeof(codec_stack),
996 "codec"
997 IF_PRIO(,PRIORITY_PLAYBACK)
998 IF_COP(, CPU, false));
1000 bufopenthread_id = rb->create_thread(bufopen_thread,
1001 bufopen_stack,
1002 sizeof(bufopen_stack),
1003 "bufopen"
1004 IF_PRIO(,PRIORITY_BACKGROUND)
1005 IF_COP(, CPU, false));
1007 if (!codecthread_id)
1009 rb->splash(HZ, "failed to create codec thread");
1010 return PLUGIN_ERROR;
1012 else if (!bufopenthread_id)
1014 rb->splash(HZ, "failed to create bufopen thread");
1015 return PLUGIN_ERROR;
1017 else
1020 while (!done_playing)
1022 if (data_rem() > 0 && wasted_space() > buffer_len/5) {
1023 DEBUGF("there is %ld bytes of wasted space\n", wasted_space());
1025 /* free buffer from outdated audio data */
1026 struct memory_handle *m = first_handle;
1027 while (m) {
1028 if (m->type == TYPE_AUDIO)
1029 free_buffer(m->id);
1030 m = m->next;
1033 /* free buffer by moving metadata */
1034 m = first_handle;
1035 while (m) {
1036 if (m->type != TYPE_AUDIO)
1037 free_buffer(m->id);
1038 m = m->next;
1041 graph_view(100);
1044 if (data_rem() > 0 && BUF_USED < 3*buffer_len/4 &&
1045 disk_is_spinning())
1047 DEBUGF("%ld bytes left to buffer and the buffer is low\n",
1048 data_rem());
1049 fill_buffer();
1050 } else {
1051 rb->sleep(HZ/2);
1054 DEBUGF("done playing\n");
1055 rb->yield();
1058 rb->backlight_on();
1059 DEBUGF("end of plugin\n");
1060 return PLUGIN_OK;