bufgetdata: Check that the caller isn't too greedy.
[Rockbox-MoB.git] / testplugin.c
blob64457a5390123474f5a0555b11326cdb3e7b0c0a
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);
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 h->ridx = RINGBUF_ADD(h->ridx, buffered_data);
613 h->available -= buffered_data;
614 return buffered_data;
617 /* Update the "data" pointer to make the handle's data available to the caller.
618 Return the length of the available linear data or < 0 for failure.
619 size is the amount of linear data requested. it can be 0 to get as
620 much as possible.
621 The guard buffer may be used to provide the requested size */
622 ssize_t bufgetdata(int handle_id, size_t size, unsigned char **data)
624 struct memory_handle *h = find_handle(handle_id);
625 if (!h)
626 return -1;
628 if (h->available == 0 && h->filerem > 0) /* Data isn't ready */
629 return -2;
631 if (h->available == 0 && h->filerem == 0) /* File is finished reading */
632 return 0;
634 ssize_t ret;
636 if (h->ridx + size > buffer_len &&
637 h->available - RINGBUF_SUB(h->ridx, h->data) >= size)
639 /* use the guard buffer to provide what was requested. */
640 size_t copy_n = MIN(h->ridx + size - buffer_len, GUARD_SIZE);
641 rb->memcpy(guard_buffer, (unsigned char *)buffer, copy_n);
642 ret = buffer_len - h->ridx + copy_n;
643 DEBUGF("used the guard buffer to complete\n");
645 else
647 ret = MIN(h->available - RINGBUF_SUB(h->ridx, h->data),
648 buffer_len - h->ridx);
651 *data = (unsigned char *)&buffer[h->ridx];
653 /* DEBUGF("bufgetdata(%d): h->ridx=%ld, ret=%ld\n", handle_id,
654 (long)h->ridx, ret); */
655 return ret;
660 UTILITY FUNCTIONS
661 =================
664 bool test_ll(void)
666 struct memory_handle *m1, *m2, *m3, *m4;
668 if (cur_handle != NULL || first_handle != NULL)
669 return false;
671 m1 = add_handle(NULL);
673 if (cur_handle != m1 || first_handle != m1 || m1->next != NULL)
674 return false;
676 m2 = add_handle(NULL);
678 if (cur_handle != m2 || first_handle != m1 || m1->next != m2 || m2->next != NULL)
679 return false;
681 m3 = add_handle(NULL);
683 if (cur_handle != m3 || first_handle != m1 || m2->next != m3 || m3->next != NULL)
684 return false;
686 rm_handle(m2);
688 if (cur_handle != m3 || first_handle != m1 || m1->next != m3)
689 return false;
691 rm_handle(m3);
693 if (cur_handle != m1 || first_handle != m1 || m1->next != NULL)
694 return false;
696 m4 = add_handle(NULL);
698 if (cur_handle != m4 || first_handle != m1 || m1->next != m4 || m4->next != NULL)
699 return false;
701 rm_handle(m1);
703 if (cur_handle != m4 || first_handle != m4)
704 return false;
706 rm_handle(m4);
708 if (cur_handle != NULL || first_handle != NULL)
709 return false;
711 m1 = add_handle(NULL);
712 m2 = add_handle(NULL);
713 m3 = add_handle(NULL);
714 m4 = add_handle(NULL);
716 if (cur_handle != m4 || first_handle != m1)
717 return false;
719 size_t delta = 1024*100;
720 m2 = move_handle(m2, &delta, 0);
722 if (cur_handle != m4 || first_handle != m1 || m1->next != m2 || m2->next != m3)
723 return false;
725 delta = 1024*100*3;
726 m1 = move_handle(m1, &delta, 0);
728 if (cur_handle != m4 || first_handle != m1 || m1->next != m2)
729 return false;
731 rm_handle(m1);
732 rm_handle(m2);
733 rm_handle(m3);
734 rm_handle(m4);
736 if (cur_handle != NULL || first_handle != NULL)
737 return false;
739 return true;
742 /* display a nice graphical view of the ringbuffer. */
743 static void graph_view(int width)
745 #ifndef ROCKBOX_HAS_LOGF
746 int i, r_pos, w_pos;
747 r_pos = buf_ridx * width / buffer_len;
748 w_pos = buf_widx * width / buffer_len;
750 DEBUGF("|");
751 for (i=0; i <= width; i++)
753 if (i != r_pos && i != w_pos)
755 if (buf_ridx <= buf_widx)
757 if (i > r_pos && i < w_pos) {
758 DEBUGF(">");
759 } else {
760 DEBUGF("-");
763 else
765 if (i > r_pos || i < w_pos) {
766 DEBUGF(">");
767 } else {
768 DEBUGF("-");
772 else
774 if (i == r_pos && i == w_pos)
776 if (buf_ridx <= buf_widx) {
777 DEBUGF("RW");
778 } else {
779 DEBUGF("WR");
781 } else if (i == r_pos) {
782 DEBUGF("R");
783 } else if (i == w_pos) {
784 DEBUGF("W");
788 DEBUGF("|");
789 DEBUGF("\n");
790 #else
791 (void)width;
792 #endif
795 void print_progress(size_t amount, int file, int numfiles)
797 char buf[32];
798 rb->snprintf(buf, sizeof(buf), "file %d of %d", file, numfiles);
799 rb->lcd_puts(0, 0, buf);
800 rb->snprintf(buf, sizeof(buf), "read: %ld", amount);
801 rb->lcd_puts(0, 1, buf);
804 void print_metadata(int handle_id)
806 ssize_t ret;
807 char *artist, *title, *newline;
808 char art[50], ttl[50];
809 int artist_len, title_len;
810 unsigned char *buf;
812 ret = bufgetdata(handle_id, 0, &buf);
814 artist = buf;
815 newline = rb->strchr(artist, '\n');
816 artist_len = newline - artist;
817 rb->strncpy(art, artist, artist_len);
818 art[artist_len] = 0;
820 title = newline + 1;
821 newline = rb->strchr(title, '\n');
822 title_len = newline - title;
823 rb->strncpy(ttl, title, title_len);
824 ttl[title_len] = 0;
826 rb->lcd_puts(0, 3, art);
827 rb->lcd_puts(0, 4, ttl);
830 bool buffer_init(void)
832 buffer = rb->plugin_get_audio_buffer(&buffer_len);
833 if (!buffer)
835 DEBUGF("couldn't allocate buffer\n");
836 return false;
838 buffer_len -= GUARD_SIZE;
839 guard_buffer = buffer + buffer_len;
841 buf_widx = 0;
842 buf_ridx = 0;
844 first_handle = NULL;
845 num_handles = 0;
847 conf_filechunk = AUDIO_DEFAULT_FILECHUNK;
849 return true;
852 bool disk_is_spinning(void)
854 return true;
859 THREADING CODE
860 ==============
863 static long codec_stack[4*DEFAULT_STACK_SIZE/sizeof(long)];
864 static struct thread_entry* codecthread_id;
866 static long bufopen_stack[2*DEFAULT_STACK_SIZE/sizeof(long)];
867 static struct thread_entry* bufopenthread_id;
869 bool done_playing = false;
871 #define MAX_HANDLES 16
873 int handles[MAX_HANDLES];
874 int meta_handles[MAX_HANDLES];
876 void codec_thread(void)
878 int idx = 0;
879 int fd = -1; /* used to write the files out as they are read */
880 unsigned char *data;
881 char outfile[MAX_PATH];
882 long read, total = 0;
884 while (1)
886 if (!done_playing)
888 if (handles[idx] > 0) {
890 /* create the output file */
891 if (fd < 0) {
892 rb->snprintf(outfile, MAX_PATH, "/file%d.mp3", idx);
893 fd = rb->open(outfile, O_CREAT|O_TRUNC|O_WRONLY);
894 if (fd < 0) {
895 DEBUGF("couldn't create file\n");
896 rb->splash(HZ, "couldn't create file");
900 /* read as much data as possible */
901 do {
902 read = bufgetdata(handles[idx], GUARD_SIZE, &data);
903 if (read >= 0) {
904 read = MIN(read, GUARD_SIZE);
905 rb->write(fd, data, read);
906 total += read;
907 bufadvance(handles[idx], read);
908 rb->lcd_clear_display();
909 print_progress(total, idx+1, num_files);
910 print_metadata(meta_handles[idx]);
911 rb->lcd_update();
913 rb->sleep(HZ/100);
914 } while (read > 0);
916 if (read >= 0 && total >= 0) {
917 /* some data was read */
918 DEBUGF("read %ld bytes from handle %d\n", total,
919 handles[idx]);
922 /* check the return value to determine what exactly happened */
923 if (read == -2) {
924 DEBUGF("data for handle %d isn't ready\n", handles[idx]);
925 } else if (read == -1) {
926 DEBUGF("couldn't find handle %d\n", handles[idx]);
927 } else if (read == 0) {
928 DEBUGF("finished reading handle %d\n", handles[idx]);
929 bufclose(handles[idx]);
930 bufclose(meta_handles[idx]);
931 rb->close(fd);
932 fd = -1;
933 total = 0;
935 /* move on to the next file and check if we've finished */
936 idx++;
937 if (idx >= num_files) {
938 done_playing = true;
939 break;
945 rb->sleep(HZ/10);
948 DEBUGF("removing the codec thread\n");
949 rb->remove_thread(NULL);
952 void bufopen_thread(void)
954 int idx = 0, ret;
955 char buf[MAX_PATH];
956 while (idx < num_files)
958 /* open the metadata file */
959 rb->snprintf(buf, MAX_PATH, "/meta%s.txt", files[idx]);
960 meta_handles[idx] = bufopen(buf, 0, TYPE_ID3);
961 if (meta_handles[idx] > 0)
963 /* open the audio file */
964 ret = bufopen(files[idx], 0, TYPE_AUDIO);
965 if (ret > 0) {
966 handles[idx++] = ret;
967 } else {
968 /* couldn't open the audio file, close the metadata handle */
969 bufclose(meta_handles[idx]);
972 rb->sleep(HZ*2);
975 DEBUGF("bufopen thread finished\n");
976 rb->remove_thread(NULL);
980 /* this is the plugin entry point */
981 enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
983 (void)parameter;
984 rb = api;
986 buffer_init();
988 if (!test_ll())
990 DEBUGF("linked list test failed\n");
991 rb->splash(HZ, "linked list test failed");
992 return PLUGIN_ERROR;
995 codecthread_id = rb->create_thread(codec_thread,
996 codec_stack,
997 sizeof(codec_stack),
998 "codec"
999 IF_PRIO(,PRIORITY_PLAYBACK)
1000 IF_COP(, CPU, false));
1002 bufopenthread_id = rb->create_thread(bufopen_thread,
1003 bufopen_stack,
1004 sizeof(bufopen_stack),
1005 "bufopen"
1006 IF_PRIO(,PRIORITY_BACKGROUND)
1007 IF_COP(, CPU, false));
1009 if (!codecthread_id)
1011 rb->splash(HZ, "failed to create codec thread");
1012 return PLUGIN_ERROR;
1014 else if (!bufopenthread_id)
1016 rb->splash(HZ, "failed to create bufopen thread");
1017 return PLUGIN_ERROR;
1019 else
1022 while (!done_playing)
1024 if (data_rem() > 0 && wasted_space() > buffer_len/5) {
1025 DEBUGF("there is %ld bytes of wasted space\n", wasted_space());
1027 /* free buffer from outdated audio data */
1028 struct memory_handle *m = first_handle;
1029 while (m) {
1030 if (m->type == TYPE_AUDIO)
1031 free_buffer(m->id);
1032 m = m->next;
1035 /* free buffer by moving metadata */
1036 m = first_handle;
1037 while (m) {
1038 if (m->type != TYPE_AUDIO)
1039 free_buffer(m->id);
1040 m = m->next;
1043 graph_view(100);
1046 if (data_rem() > 0 && BUF_USED < 3*buffer_len/4 &&
1047 disk_is_spinning())
1049 DEBUGF("%ld bytes left to buffer and the buffer is low\n",
1050 data_rem());
1051 fill_buffer();
1052 } else {
1053 rb->sleep(HZ/2);
1056 DEBUGF("done playing\n");
1057 rb->yield();
1060 rb->backlight_on();
1061 DEBUGF("end of plugin\n");
1062 return PLUGIN_OK;