5348c50c7fa62851be341c411b6515c9c114a66b
[Rockbox-MoB.git] / testplugin.c
blob5348c50c7fa62851be341c411b6515c9c114a66b
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;
81 /* at all times, we have: filesize == offset + available + filerem */
84 static char *buffer;
85 static char *guard_buffer;
87 static size_t buffer_len;
89 static size_t buf_widx; /* current writing position */
90 static size_t buf_ridx; /* current reading position */
91 /* buf_*idx are values relative to the buffer, not real pointers. */
93 static size_t conf_filechunk;
95 /* current memory handle in the linked list. NULL when the list is empty. */
96 static struct memory_handle *cur_handle;
97 /* first memory handle in the linked list. NULL when the list is empty. */
98 static struct memory_handle *first_handle;
100 static int num_handles; /* number of handles in the list */
102 /* Handle cache (makes find_handle faster).
103 These need to be global so that move_handle can invalidate them. */
104 static int cached_handle_id = -1;
105 static struct memory_handle *cached_handle = NULL;
109 LINKED LIST MANAGEMENT
110 ======================
112 add_handle : Add a handle to the list
113 rm_handle : Remove a handle from the list
114 find_handle : Get a handle pointer from an ID
115 move_handle : Move a handle in the buffer (with or without its data)
117 These functions only handle the linked list structure. They don't touch the
118 contents of the struct memory_handle headers. They also change the buf_*idx
119 pointers when necessary and manage the handle IDs.
121 The first and current (== last) handle are kept track of.
122 A new handle is added at buf_widx and becomes the current one.
123 buf_widx always points to the current writing position for the current handle
124 buf_ridx always points to the location of the first handle.
125 buf_ridx == buf_widx means the buffer is empty.
129 /* Add a new handle to the linked list and return it. It will have become the
130 new current handle. The handle will reserve "data_size" bytes or if that's
131 not possible, decrease "data_size" to allow adding the handle. */
132 static struct memory_handle *add_handle(size_t *data_size)
134 /* this will give each handle a unique id */
135 static int cur_handle_id = 1;
137 /* make sure buf_widx is 32-bit aligned so that the handle struct is,
138 but before that we check we can actually align. */
139 if (RINGBUF_ADD_CROSS(buf_widx, 3, buf_ridx) >= 0) {
140 return NULL;
142 buf_widx = (RINGBUF_ADD(buf_widx, 3)) & ~3;
144 size_t len = (data_size ? *data_size : 0)
145 + sizeof(struct memory_handle);
147 /* check that we actually can add the handle and its data */
148 int overlap = RINGBUF_ADD_CROSS(buf_widx, len, buf_ridx);
149 if (overlap >= 0) {
150 *data_size -= overlap;
151 len -= overlap;
153 if (len < sizeof(struct memory_handle)) {
154 /* There isn't even enough space to write the struct */
155 return NULL;
158 struct memory_handle *new_handle =
159 (struct memory_handle *)(&buffer[buf_widx]);
161 /* only advance the buffer write index of the size of the struct */
162 buf_widx = RINGBUF_ADD(buf_widx, sizeof(struct memory_handle));
164 if (!first_handle) {
165 /* the new handle is the first one */
166 first_handle = new_handle;
169 if (cur_handle) {
170 cur_handle->next = new_handle;
173 cur_handle = new_handle;
174 cur_handle->id = cur_handle_id++;
175 cur_handle->next = NULL;
176 num_handles++;
177 return cur_handle;
180 /* Delete a given memory handle from the linked list
181 and return true for success. Nothing is actually erased from memory. */
182 static bool rm_handle(struct memory_handle *h)
184 if (h == first_handle) {
185 first_handle = h->next;
186 if (h == cur_handle) {
187 /* h was the first and last handle: the buffer is now empty */
188 cur_handle = NULL;
189 buf_ridx = buf_widx;
190 } else {
191 /* update buf_ridx to point to the new first handle */
192 buf_ridx = (void *)first_handle - (void *)buffer;
194 } else {
195 struct memory_handle *m = first_handle;
196 while (m && m->next != h) {
197 m = m->next;
199 if (h && m && m->next == h) {
200 m->next = h->next;
201 if (h == cur_handle) {
202 cur_handle = m;
204 } else {
205 return false;
209 num_handles--;
210 return true;
213 /* Return a pointer to the memory handle of given ID.
214 NULL if the handle wasn't found */
215 static struct memory_handle *find_handle(int handle_id)
217 /* simple caching because most of the time the requested handle
218 will either be the same as the last, or the one after the last */
219 if (cached_handle)
221 if (cached_handle_id == handle_id &&
222 cached_handle_id == cached_handle->id)
223 return cached_handle;
224 else if (cached_handle->next && (cached_handle->next->id == handle_id))
226 /* JD's quick testing showd this block was only entered
227 2/1971 calls to find_handle.
228 8/1971 calls to find_handle resulted in a cache miss */
229 cached_handle = cached_handle->next;
230 cached_handle_id = handle_id;
231 return cached_handle;
235 struct memory_handle *m = first_handle;
236 while (m && m->id != handle_id) {
237 m = m->next;
239 cached_handle_id = handle_id;
240 cached_handle = m;
241 return (m && m->id == handle_id) ? m : NULL;
244 /* Move a memory handle and data_size of its data of delta.
245 Return a pointer to the new location of the handle.
246 delta is the value of which to move the struct data.
247 data_size is the amount of data to move along with the struct. */
248 static struct memory_handle *move_handle(struct memory_handle *h,
249 size_t *delta, size_t data_size)
251 if (*delta < 4) {
252 /* aligning backwards would yield a negative result,
253 and moving the handle of such a small amount is a waste
254 of time anyway. */
255 return NULL;
257 /* make sure delta is 32-bit aligned so that the handle struct is. */
258 *delta = (*delta - 3) & ~3;
260 size_t newpos = RINGBUF_ADD((void *)h - (void *)buffer, *delta);
262 struct memory_handle *dest = (struct memory_handle *)(&buffer[newpos]);
264 /* Invalidate the cache to prevent it from keeping the old location of h */
265 if (h == cached_handle)
266 cached_handle = NULL;
268 /* the cur_handle pointer might need updating */
269 if (h == cur_handle) {
270 cur_handle = dest;
273 if (h == first_handle) {
274 first_handle = dest;
275 buf_ridx = newpos;
276 } else {
277 struct memory_handle *m = first_handle;
278 while (m && m->next != h) {
279 m = m->next;
281 if (h && m && m->next == h) {
282 m->next = dest;
283 } else {
284 return NULL;
288 rb->memmove(dest, h, sizeof(struct memory_handle) + data_size);
290 return dest;
295 BUFFER SPACE MANAGEMENT
296 =======================
298 buffer_handle : Buffer data for a handle
299 free_buffer : Free buffer space by moving a handle
300 fill_buffer : Call buffer_handle for all handles that have data to buffer
301 data_rem : Total amount of data needing to be buffered
302 wasted_space : Total amount of space available for freeing
304 These functions are used by the buffering thread to manage buffer space.
307 /* Buffer data for the given handle. Return the amount of data buffered
308 or -1 if the handle wasn't found */
309 static ssize_t buffer_handle(int handle_id)
311 DEBUGF("buffer_handle(%d)\n", handle_id);
312 struct memory_handle *h = find_handle(handle_id);
313 if (!h)
314 return -1;
316 if (h->filerem == 0) {
317 /* nothing left to buffer */
318 return 0;
321 if (h->fd < 0) /* file closed, reopen */
323 if (*h->path)
324 h->fd = rb->open(h->path, O_RDONLY);
325 else
326 return -1;
328 if (h->fd < 0)
329 return -1;
331 if (h->offset)
332 rb->lseek(h->fd, h->offset, SEEK_SET);
335 ssize_t ret = 0;
336 while (h->filerem > 0)
338 /* max amount to copy */
339 size_t copy_n = MIN( MIN(h->filerem, conf_filechunk),
340 buffer_len - h->widx);
342 /* stop copying if it would overwrite the reading position
343 or the next handle */
344 if (RINGBUF_ADD_CROSS(h->widx, copy_n, buf_ridx) >= 0 || (h->next &&
345 RINGBUF_ADD_CROSS(h->widx, copy_n, (unsigned)
346 ((void *)h->next - (void *)buffer)) > 0))
347 break;
349 /* rc is the actual amount read */
350 int rc = rb->read(h->fd, &buffer[h->widx], copy_n);
352 if (rc < 0)
354 DEBUGF("File ended %ld bytes early\n", (long)h->filerem);
355 h->filesize -= h->filerem;
356 h->filerem = 0;
357 break;
360 /* Advance buffer */
361 h->widx = RINGBUF_ADD(h->widx, rc);
362 if (h == cur_handle)
363 buf_widx = h->widx;
364 h->available += rc;
365 ret += rc;
366 h->filerem -= rc;
369 if (h->filerem == 0) {
370 /* finished buffering the file */
371 rb->close(h->fd);
374 DEBUGF("buffered %ld bytes (%ld of %ld available, rem: %ld, off: %ld)\n",
375 ret, h->available, h->filesize, h->filerem, h->offset);
377 graph_view(100);
379 return ret;
382 /* Free buffer space by moving the handle struct right before the useful
383 part of its data buffer or by moving all the data. */
384 static void free_buffer(int handle_id)
386 struct memory_handle *h = find_handle(handle_id);
387 if (!h)
388 return;
390 size_t delta;
391 /* The value of delta might change for alignment reasons */
393 if (h->next && (h->type == TYPE_ID3 || h->type == TYPE_CUESHEET ||
394 h->type == TYPE_IMAGE) && h->filerem == 0 )
396 /* metadata handle: we can move all of it */
397 delta = RINGBUF_SUB( (unsigned)((void *)h->next - (void *)buffer),
398 h->data) - h->available;
399 h = move_handle(h, &delta, h->available);
400 if (!h) return;
401 h->data = RINGBUF_ADD(h->data, delta);
402 h->ridx = RINGBUF_ADD(h->ridx, delta);
403 h->widx = RINGBUF_ADD(h->widx, delta);
404 DEBUGF("free_buffer(%d): metadata, moved by %ld bytes\n",
405 handle_id, delta);
407 else
409 /* only move the handle struct */
410 delta = RINGBUF_SUB(h->ridx, h->data);
411 h = move_handle(h, &delta, 0);
412 if (!h) return;
413 h->data = RINGBUF_ADD(h->data, delta);
414 h->available -= delta;
415 h->offset += delta;
416 DEBUGF("free_buffer(%d): audio, %ld bytes freed\n", handle_id, delta);
420 /* Fill the buffer by buffering as much data as possible for handles that still
421 have data left to buffer */
422 static void fill_buffer(void)
424 DEBUGF("fill buffer()\n");
425 struct memory_handle *m = first_handle;
426 while (m) {
427 if (m->filerem > 0) {
428 buffer_handle(m->id);
430 m = m->next;
434 /* Return the total amount of data left to be buffered for all the handles */
435 static size_t data_rem(void)
437 size_t ret = 0;
439 struct memory_handle *m = first_handle;
440 while (m) {
441 ret += m->filerem;
442 m = m->next;
445 return ret;
448 /* Return the amount of data we have but don't need anymore. This data can be
449 safely erased to reclaim buffer space. */
450 static size_t wasted_space(void)
452 size_t ret = 0;
454 struct memory_handle *m = first_handle;
455 while (m) {
456 ret += RINGBUF_SUB(m->ridx, m->data);
457 m = m->next;
460 return ret;
465 BUFFERING API FUNCTIONS
466 =======================
468 bufopen : Request the opening of a new handle for a file
469 bufclose : Close an open handle
470 bufseek : Set the read pointer in a handle
471 bufadvance : Move the read pointer in a handle
472 bufread : Copy data from a handle into a given buffer
473 bufgetdata : Give a pointer to the handle's data
475 These functions are exported, to allow interaction with the buffer.
476 They take care of the content of the structs, and rely on the linked list
477 management functions for all the actual handle management work.
481 /* Request a file be buffered
482 filename: name of the file to open
483 offset:starting offset to buffer from the file
484 return value: <0 if the file cannot be opened, or one file already
485 queued to be opened, otherwise the handle for the file in the buffer
487 int bufopen(char *file, size_t offset, enum data_type type)
489 if (cur_handle && cur_handle->filerem > 0) {
490 /* the current handle hasn't finished buffering. We can only add
491 a new one if there is already enough free space to finish
492 the buffering. */
493 if (cur_handle->filerem < (buffer_len - BUF_USED)) {
494 /* Before adding the new handle we reserve some space for the
495 current one to finish buffering its data. */
496 buf_widx = RINGBUF_ADD(buf_widx, cur_handle->filerem);
497 } else {
498 return -1;
502 int fd = rb->open(file, O_RDONLY);
503 if (fd < 0)
504 return -1;
506 size_t size = rb->filesize(fd) - offset;
508 DEBUGF("bufopen: %s (offset: %ld) (%ld bytes needed)...\n",
509 file, offset, size);
511 struct memory_handle *h = add_handle(&size);
512 if (!h)
514 DEBUGF("failed to add handle\n");
515 rb->close(fd);
516 return -1;
519 if (offset) rb->lseek(fd, offset, SEEK_SET);
520 rb->strncpy(h->path, file, MAX_PATH);
521 h->fd = fd;
522 h->filesize = rb->filesize(fd);
523 h->filerem = h->filesize - offset;
524 h->offset = offset;
525 h->ridx = buf_widx;
526 h->widx = buf_widx;
527 h->data = buf_widx;
528 h->available = 0;
529 h->type = type;
531 DEBUGF("allocated %ld bytes. ID: %d\n", size, h->id);
532 return h->id;
535 /* Close the handle. Return 0 for success and < 0 for failure */
536 int bufclose(int handle_id)
538 DEBUGF("bufclose(%d)\n", handle_id);
539 struct memory_handle *h = find_handle(handle_id);
540 if (!h)
541 return -1;
543 rm_handle(h);
544 return 0;
547 /* Set reading index in handle (relatively to the start of the file).
548 Access before the available data will trigger a rebuffer.
549 TODO: Test this
550 TODO: Maybe force an immediate rebuffer by calling buffer_handle() ?
551 Return 0 for success and < 0 for failure:
552 -1 if the handle wasn't found
553 -2 if there is no data available at the new position
554 (the reading index is still moved)
555 -3 if the new requested position was beyond the end of the file
557 int bufseek(int handle_id, size_t newpos)
559 int ret = 0;
560 struct memory_handle *h = find_handle(handle_id);
561 if (!h)
562 return -1;
564 if (newpos > h->filesize) {
565 /* access beyond the end of the file */
566 return -3;
569 else if (newpos < h->offset) {
570 /* access before what we currently have. A rebuffer is needed. */
571 h->offset = newpos;
572 h->available = 0;
573 h->filerem = h->filesize - newpos;
574 /* having changed filerem should be enough to trigger the rebuffer. */
575 h->widx = h->data;
576 ret = -2;
579 else if (newpos > h->offset + h->available) {
580 /* data isn't available yet. */
581 ret = -2;
584 h->ridx = RINGBUF_ADD(h->data, newpos);
585 return ret;
588 /* Advance the reading index in a handle (relatively to its current position).
589 Return 0 for success and < 0 for failure
590 TODO: Add some rebuffering like in bufseek */
591 int bufadvance(int handle_id, off_t offset)
593 struct memory_handle *h = find_handle(handle_id);
594 if (!h)
595 return -1;
597 if (offset >= 0)
599 /* check for access beyond what's available */
600 if ((size_t)offset > (h->available - RINGBUF_SUB(h->ridx, h->data)))
601 return -2;
603 h->ridx = RINGBUF_ADD(h->ridx, offset);
605 else
607 /* check for access before what's available */
608 if ((size_t)(-offset) > RINGBUF_SUB(h->ridx, h->data))
609 return -2;
611 h->ridx = RINGBUF_SUB(h->ridx, (size_t)(-offset));
614 return 0;
617 /* Copy data from the given handle to the dest buffer.
618 Return the number of bytes copied or < 0 for failure. */
619 ssize_t bufread(int handle_id, size_t size, char *dest)
621 struct memory_handle *h = find_handle(handle_id);
622 size_t buffered_data;
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 buffered_data = MIN(size, h->available - RINGBUF_SUB(h->ridx, h->data));
634 if (h->ridx + buffered_data > buffer_len)
636 /* the data wraps around the end of the buffer */
637 size_t read = buffer_len - h->ridx;
638 rb->memcpy(dest, &buffer[h->ridx], read);
639 rb->memcpy(dest+read, buffer, buffered_data - read);
641 else rb->memcpy(dest, &buffer[h->ridx], buffered_data);
643 return buffered_data;
646 /* Update the "data" pointer to make the handle's data available to the caller.
647 Return the length of the available linear data or < 0 for failure.
648 size is the amount of linear data requested. it can be 0 to get as
649 much as possible.
650 The guard buffer may be used to provide the requested size */
651 ssize_t bufgetdata(int handle_id, size_t size, unsigned char **data)
653 struct memory_handle *h = find_handle(handle_id);
654 if (!h)
655 return -1;
657 if (h->available == 0 && h->filerem > 0) /* Data isn't ready */
658 return -2;
660 if (h->available == 0 && h->filerem == 0) /* File is finished reading */
661 return 0;
663 ssize_t ret;
665 if (h->ridx + size > buffer_len &&
666 h->available - RINGBUF_SUB(h->ridx, h->data) >= size)
668 /* the data wraps around the end of the buffer :
669 use the guard buffer to provide the requested amount of data. */
670 size_t copy_n = MIN(h->ridx + size - buffer_len, GUARD_SIZE);
671 rb->memcpy(guard_buffer, (unsigned char *)buffer, copy_n);
672 ret = buffer_len - h->ridx + copy_n;
673 DEBUGF("used the guard buffer to complete\n");
675 else
677 ret = MIN(h->available - RINGBUF_SUB(h->ridx, h->data),
678 buffer_len - h->ridx);
681 *data = (unsigned char *)&buffer[h->ridx];
683 /* DEBUGF("bufgetdata(%d): h->ridx=%ld, ret=%ld\n", handle_id,
684 (long)h->ridx, ret); */
685 return ret;
690 UTILITY FUNCTIONS
691 =================
694 bool test_ll(void)
696 struct memory_handle *m1, *m2, *m3, *m4;
698 if (cur_handle != NULL || first_handle != NULL)
699 return false;
701 m1 = add_handle(NULL);
703 if (cur_handle != m1 || first_handle != m1 || m1->next != NULL)
704 return false;
706 m2 = add_handle(NULL);
708 if (cur_handle != m2 || first_handle != m1 || m1->next != m2 || m2->next != NULL)
709 return false;
711 m3 = add_handle(NULL);
713 if (cur_handle != m3 || first_handle != m1 || m2->next != m3 || m3->next != NULL)
714 return false;
716 rm_handle(m2);
718 if (cur_handle != m3 || first_handle != m1 || m1->next != m3)
719 return false;
721 rm_handle(m3);
723 if (cur_handle != m1 || first_handle != m1 || m1->next != NULL)
724 return false;
726 m4 = add_handle(NULL);
728 if (cur_handle != m4 || first_handle != m1 || m1->next != m4 || m4->next != NULL)
729 return false;
731 rm_handle(m1);
733 if (cur_handle != m4 || first_handle != m4)
734 return false;
736 rm_handle(m4);
738 if (cur_handle != NULL || first_handle != NULL)
739 return false;
741 m1 = add_handle(NULL);
742 m2 = add_handle(NULL);
743 m3 = add_handle(NULL);
744 m4 = add_handle(NULL);
746 if (cur_handle != m4 || first_handle != m1)
747 return false;
749 size_t delta = 1024*100;
750 m2 = move_handle(m2, &delta, 0);
752 if (cur_handle != m4 || first_handle != m1 || m1->next != m2 || m2->next != m3)
753 return false;
755 delta = 1024*100*3;
756 m1 = move_handle(m1, &delta, 0);
758 if (cur_handle != m4 || first_handle != m1 || m1->next != m2)
759 return false;
761 rm_handle(m1);
762 rm_handle(m2);
763 rm_handle(m3);
764 rm_handle(m4);
766 if (cur_handle != NULL || first_handle != NULL)
767 return false;
769 return true;
772 /* display a nice graphical view of the ringbuffer. */
773 static void graph_view(int width)
775 #ifndef ROCKBOX_HAS_LOGF
776 int i, r_pos, w_pos;
777 r_pos = buf_ridx * width / buffer_len;
778 w_pos = buf_widx * width / buffer_len;
780 DEBUGF("|");
781 for (i=0; i <= width; i++)
783 if (i != r_pos && i != w_pos)
785 if (buf_ridx <= buf_widx)
787 if (i > r_pos && i < w_pos) {
788 DEBUGF(">");
789 } else {
790 DEBUGF("-");
793 else
795 if (i > r_pos || i < w_pos) {
796 DEBUGF(">");
797 } else {
798 DEBUGF("-");
802 else
804 if (i == r_pos && i == w_pos)
806 if (buf_ridx <= buf_widx) {
807 DEBUGF("RW");
808 } else {
809 DEBUGF("WR");
811 } else if (i == r_pos) {
812 DEBUGF("R");
813 } else if (i == w_pos) {
814 DEBUGF("W");
818 DEBUGF("|");
819 DEBUGF("\n");
820 #else
821 (void)width;
822 #endif
825 void print_progress(size_t amount, int file, int numfiles)
827 char buf[32];
828 rb->snprintf(buf, sizeof(buf), "file %d of %d", file, numfiles);
829 rb->lcd_puts(0, 0, buf);
830 rb->snprintf(buf, sizeof(buf), "read: %ld", amount);
831 rb->lcd_puts(0, 1, buf);
834 void print_metadata(int handle_id)
836 ssize_t ret;
837 char *artist, *title, *newline;
838 char art[50], ttl[50];
839 int artist_len, title_len;
840 unsigned char *buf;
842 ret = bufgetdata(handle_id, 0, &buf);
844 artist = buf;
845 newline = rb->strchr(artist, '\n');
846 artist_len = newline - artist;
847 rb->strncpy(art, artist, artist_len);
848 art[artist_len] = 0;
850 title = newline + 1;
851 newline = rb->strchr(title, '\n');
852 title_len = newline - title;
853 rb->strncpy(ttl, title, title_len);
854 ttl[title_len] = 0;
856 rb->lcd_puts(0, 3, art);
857 rb->lcd_puts(0, 4, ttl);
860 bool buffer_init(void)
862 buffer = rb->plugin_get_audio_buffer(&buffer_len);
863 if (!buffer)
865 DEBUGF("couldn't allocate buffer\n");
866 return false;
868 buffer_len -= GUARD_SIZE;
869 guard_buffer = buffer + buffer_len;
871 buf_widx = 0;
872 buf_ridx = 0;
874 first_handle = NULL;
875 num_handles = 0;
877 conf_filechunk = AUDIO_DEFAULT_FILECHUNK;
879 return true;
882 bool disk_is_spinning(void)
884 return true;
889 THREADING CODE
890 ==============
893 static long codec_stack[4*DEFAULT_STACK_SIZE/sizeof(long)];
894 static struct thread_entry* codecthread_id;
896 static long bufopen_stack[2*DEFAULT_STACK_SIZE/sizeof(long)];
897 static struct thread_entry* bufopenthread_id;
899 bool done_playing = false;
901 #define MAX_HANDLES 16
903 int handles[MAX_HANDLES];
904 int meta_handles[MAX_HANDLES];
906 void codec_thread(void)
908 int idx = 0;
909 int fd = -1; /* used to write the files out as they are read */
910 unsigned char *data;
911 char outfile[MAX_PATH];
912 long read, total = 0;
914 while (1)
916 if (!done_playing)
918 if (handles[idx] > 0) {
920 /* create the output file */
921 if (fd < 0) {
922 rb->snprintf(outfile, MAX_PATH, "/file%d.mp3", idx);
923 fd = rb->open(outfile, O_CREAT|O_TRUNC|O_WRONLY);
924 if (fd < 0) {
925 DEBUGF("couldn't create file\n");
926 rb->splash(HZ, "couldn't create file");
930 /* read as much data as possible */
931 do {
932 read = bufgetdata(handles[idx], GUARD_SIZE, &data);
933 if (read >= 0) {
934 read = MIN(read, GUARD_SIZE);
935 rb->write(fd, data, read);
936 total += read;
937 bufadvance(handles[idx], read);
938 rb->lcd_clear_display();
939 print_progress(total, idx+1, num_files);
940 print_metadata(meta_handles[idx]);
941 rb->lcd_update();
943 rb->sleep(HZ/100);
944 } while (read > 0);
946 if (read >= 0 && total >= 0) {
947 /* some data was read */
948 DEBUGF("read %ld bytes from handle %d\n", total,
949 handles[idx]);
952 /* check the return value to determine what exactly happened */
953 if (read == -2) {
954 DEBUGF("data for handle %d isn't ready\n", handles[idx]);
955 } else if (read == -1) {
956 DEBUGF("couldn't find handle %d\n", handles[idx]);
957 } else if (read == 0) {
958 DEBUGF("finished reading handle %d\n", handles[idx]);
959 bufclose(handles[idx]);
960 bufclose(meta_handles[idx]);
961 rb->close(fd);
962 fd = -1;
963 total = 0;
965 /* move on to the next file and check if we've finished */
966 idx++;
967 if (idx >= num_files) {
968 done_playing = true;
969 break;
975 rb->sleep(HZ/10);
978 DEBUGF("removing the codec thread\n");
979 rb->remove_thread(NULL);
982 void bufopen_thread(void)
984 int idx = 0, ret;
985 char buf[MAX_PATH];
986 while (idx < num_files)
988 /* open the metadata file */
989 rb->snprintf(buf, MAX_PATH, "/meta%s.txt", files[idx]);
990 meta_handles[idx] = bufopen(buf, 0, TYPE_ID3);
991 if (meta_handles[idx] > 0)
993 /* open the audio file */
994 ret = bufopen(files[idx], 0, TYPE_AUDIO);
995 if (ret > 0) {
996 handles[idx++] = ret;
997 } else {
998 /* couldn't open the audio file, close the metadata handle */
999 bufclose(meta_handles[idx]);
1002 rb->sleep(HZ*2);
1005 DEBUGF("bufopen thread finished\n");
1006 rb->remove_thread(NULL);
1010 /* this is the plugin entry point */
1011 enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
1013 (void)parameter;
1014 rb = api;
1016 buffer_init();
1018 if (!test_ll())
1020 DEBUGF("linked list test failed\n");
1021 rb->splash(HZ, "linked list test failed");
1022 return PLUGIN_ERROR;
1025 codecthread_id = rb->create_thread(codec_thread,
1026 codec_stack,
1027 sizeof(codec_stack),
1028 "codec"
1029 IF_PRIO(,PRIORITY_PLAYBACK)
1030 IF_COP(, CPU, false));
1032 bufopenthread_id = rb->create_thread(bufopen_thread,
1033 bufopen_stack,
1034 sizeof(bufopen_stack),
1035 "bufopen"
1036 IF_PRIO(,PRIORITY_BACKGROUND)
1037 IF_COP(, CPU, false));
1039 if (!codecthread_id)
1041 rb->splash(HZ, "failed to create codec thread");
1042 return PLUGIN_ERROR;
1044 else if (!bufopenthread_id)
1046 rb->splash(HZ, "failed to create bufopen thread");
1047 return PLUGIN_ERROR;
1049 else
1052 while (!done_playing)
1054 if (data_rem() > 0 && wasted_space() > buffer_len/5) {
1055 DEBUGF("there is %ld bytes of wasted space\n", wasted_space());
1057 /* free buffer from outdated audio data */
1058 struct memory_handle *m = first_handle;
1059 while (m) {
1060 if (m->type == TYPE_AUDIO)
1061 free_buffer(m->id);
1062 m = m->next;
1065 /* free buffer by moving metadata */
1066 m = first_handle;
1067 while (m) {
1068 if (m->type != TYPE_AUDIO)
1069 free_buffer(m->id);
1070 m = m->next;
1073 graph_view(100);
1076 if (data_rem() > 0 && BUF_USED < 3*buffer_len/4 &&
1077 disk_is_spinning())
1079 DEBUGF("%ld bytes left to buffer and the buffer is low\n",
1080 data_rem());
1081 fill_buffer();
1082 } else {
1083 rb->sleep(HZ/2);
1086 DEBUGF("done playing\n");
1087 rb->yield();
1090 rb->backlight_on();
1091 DEBUGF("end of plugin\n");
1092 return PLUGIN_OK;