free_buffer: Keep h->offset in sync so we know where we are in the file.
[Rockbox-MoB.git] / testplugin.c
blob109b10e9272fa9f0f43d85882cc7d5bbbff116d3
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 handle data).
548 Return 0 for success and < 0 for failure */
549 int bufseek(int handle_id, size_t offset)
551 struct memory_handle *h = find_handle(handle_id);
552 if (!h)
553 return -1;
555 if (offset > h->available)
556 return -2;
558 h->ridx = RINGBUF_ADD(h->data, offset);
559 return 0;
562 /* Advance the reading index in a handle (relatively to its current position).
563 Return 0 for success and < 0 for failure */
564 int bufadvance(int handle_id, off_t offset)
566 struct memory_handle *h = find_handle(handle_id);
567 if (!h)
568 return -1;
570 if (offset >= 0)
572 /* check for access beyond what's available */
573 if ((size_t)offset > (h->available - RINGBUF_SUB(h->ridx, h->data)))
574 return -2;
576 h->ridx = RINGBUF_ADD(h->ridx, offset);
578 else
580 /* check for access before what's available */
581 if ((size_t)(-offset) > RINGBUF_SUB(h->ridx, h->data))
582 return -2;
584 h->ridx = RINGBUF_SUB(h->ridx, (size_t)(-offset));
587 return 0;
590 /* Copy data from the given handle to the dest buffer.
591 Return the number of bytes copied or < 0 for failure. */
592 ssize_t bufread(int handle_id, size_t size, char *dest)
594 struct memory_handle *h = find_handle(handle_id);
595 size_t buffered_data;
596 if (!h)
597 return -1;
599 if (h->available == 0 && h->filerem > 0) /* Data isn't ready */
600 return -2;
602 if (h->available == 0 && h->filerem == 0) /* File is finished reading */
603 return 0;
605 buffered_data = MIN(size, h->available - RINGBUF_SUB(h->ridx, h->data));
607 if (h->ridx + buffered_data > buffer_len)
609 /* the data wraps around the end of the buffer */
610 size_t read = buffer_len - h->ridx;
611 rb->memcpy(dest, &buffer[h->ridx], read);
612 rb->memcpy(dest+read, buffer, buffered_data - read);
614 else rb->memcpy(dest, &buffer[h->ridx], buffered_data);
616 return buffered_data;
619 /* Update the "data" pointer to make the handle's data available to the caller.
620 Return the length of the available linear data or < 0 for failure.
621 size is the amount of linear data requested. it can be 0 to get as
622 much as possible.
623 The guard buffer may be used to provide the requested size */
624 ssize_t bufgetdata(int handle_id, size_t size, unsigned char **data)
626 struct memory_handle *h = find_handle(handle_id);
627 if (!h)
628 return -1;
630 if (h->available == 0 && h->filerem > 0) /* Data isn't ready */
631 return -2;
633 if (h->available == 0 && h->filerem == 0) /* File is finished reading */
634 return 0;
636 ssize_t ret;
638 if (h->ridx + size > buffer_len &&
639 h->available - RINGBUF_SUB(h->ridx, h->data) >= size)
641 /* the data wraps around the end of the buffer :
642 use the guard buffer to provide the requested amount of data. */
643 size_t copy_n = MIN(h->ridx + size - buffer_len, GUARD_SIZE);
644 rb->memcpy(guard_buffer, (unsigned char *)buffer, copy_n);
645 ret = buffer_len - h->ridx + copy_n;
646 DEBUGF("used the guard buffer to complete\n");
648 else
650 ret = MIN(h->available - RINGBUF_SUB(h->ridx, h->data),
651 buffer_len - h->ridx);
654 *data = (unsigned char *)&buffer[h->ridx];
656 /* DEBUGF("bufgetdata(%d): h->ridx=%ld, ret=%ld\n", handle_id,
657 (long)h->ridx, ret); */
658 return ret;
663 UTILITY FUNCTIONS
664 =================
667 bool test_ll(void)
669 struct memory_handle *m1, *m2, *m3, *m4;
671 if (cur_handle != NULL || first_handle != NULL)
672 return false;
674 m1 = add_handle(NULL);
676 if (cur_handle != m1 || first_handle != m1 || m1->next != NULL)
677 return false;
679 m2 = add_handle(NULL);
681 if (cur_handle != m2 || first_handle != m1 || m1->next != m2 || m2->next != NULL)
682 return false;
684 m3 = add_handle(NULL);
686 if (cur_handle != m3 || first_handle != m1 || m2->next != m3 || m3->next != NULL)
687 return false;
689 rm_handle(m2);
691 if (cur_handle != m3 || first_handle != m1 || m1->next != m3)
692 return false;
694 rm_handle(m3);
696 if (cur_handle != m1 || first_handle != m1 || m1->next != NULL)
697 return false;
699 m4 = add_handle(NULL);
701 if (cur_handle != m4 || first_handle != m1 || m1->next != m4 || m4->next != NULL)
702 return false;
704 rm_handle(m1);
706 if (cur_handle != m4 || first_handle != m4)
707 return false;
709 rm_handle(m4);
711 if (cur_handle != NULL || first_handle != NULL)
712 return false;
714 m1 = add_handle(NULL);
715 m2 = add_handle(NULL);
716 m3 = add_handle(NULL);
717 m4 = add_handle(NULL);
719 if (cur_handle != m4 || first_handle != m1)
720 return false;
722 size_t delta = 1024*100;
723 m2 = move_handle(m2, &delta, 0);
725 if (cur_handle != m4 || first_handle != m1 || m1->next != m2 || m2->next != m3)
726 return false;
728 delta = 1024*100*3;
729 m1 = move_handle(m1, &delta, 0);
731 if (cur_handle != m4 || first_handle != m1 || m1->next != m2)
732 return false;
734 rm_handle(m1);
735 rm_handle(m2);
736 rm_handle(m3);
737 rm_handle(m4);
739 if (cur_handle != NULL || first_handle != NULL)
740 return false;
742 return true;
745 /* display a nice graphical view of the ringbuffer. */
746 static void graph_view(int width)
748 #ifndef ROCKBOX_HAS_LOGF
749 int i, r_pos, w_pos;
750 r_pos = buf_ridx * width / buffer_len;
751 w_pos = buf_widx * width / buffer_len;
753 DEBUGF("|");
754 for (i=0; i <= width; i++)
756 if (i != r_pos && i != w_pos)
758 if (buf_ridx <= buf_widx)
760 if (i > r_pos && i < w_pos) {
761 DEBUGF(">");
762 } else {
763 DEBUGF("-");
766 else
768 if (i > r_pos || i < w_pos) {
769 DEBUGF(">");
770 } else {
771 DEBUGF("-");
775 else
777 if (i == r_pos && i == w_pos)
779 if (buf_ridx <= buf_widx) {
780 DEBUGF("RW");
781 } else {
782 DEBUGF("WR");
784 } else if (i == r_pos) {
785 DEBUGF("R");
786 } else if (i == w_pos) {
787 DEBUGF("W");
791 DEBUGF("|");
792 DEBUGF("\n");
793 #else
794 (void)width;
795 #endif
798 void print_progress(size_t amount, int file, int numfiles)
800 char buf[32];
801 rb->snprintf(buf, sizeof(buf), "file %d of %d", file, numfiles);
802 rb->lcd_puts(0, 0, buf);
803 rb->snprintf(buf, sizeof(buf), "read: %ld", amount);
804 rb->lcd_puts(0, 1, buf);
807 void print_metadata(int handle_id)
809 ssize_t ret;
810 char *artist, *title, *newline;
811 char art[50], ttl[50];
812 int artist_len, title_len;
813 unsigned char *buf;
815 ret = bufgetdata(handle_id, 0, &buf);
817 artist = buf;
818 newline = rb->strchr(artist, '\n');
819 artist_len = newline - artist;
820 rb->strncpy(art, artist, artist_len);
821 art[artist_len] = 0;
823 title = newline + 1;
824 newline = rb->strchr(title, '\n');
825 title_len = newline - title;
826 rb->strncpy(ttl, title, title_len);
827 ttl[title_len] = 0;
829 rb->lcd_puts(0, 3, art);
830 rb->lcd_puts(0, 4, ttl);
833 bool buffer_init(void)
835 buffer = rb->plugin_get_audio_buffer(&buffer_len);
836 if (!buffer)
838 DEBUGF("couldn't allocate buffer\n");
839 return false;
841 buffer_len -= GUARD_SIZE;
842 guard_buffer = buffer + buffer_len;
844 buf_widx = 0;
845 buf_ridx = 0;
847 first_handle = NULL;
848 num_handles = 0;
850 conf_filechunk = AUDIO_DEFAULT_FILECHUNK;
852 return true;
855 bool disk_is_spinning(void)
857 return true;
862 THREADING CODE
863 ==============
866 static long codec_stack[4*DEFAULT_STACK_SIZE/sizeof(long)];
867 static struct thread_entry* codecthread_id;
869 static long bufopen_stack[2*DEFAULT_STACK_SIZE/sizeof(long)];
870 static struct thread_entry* bufopenthread_id;
872 bool done_playing = false;
874 #define MAX_HANDLES 16
876 int handles[MAX_HANDLES];
877 int meta_handles[MAX_HANDLES];
879 void codec_thread(void)
881 int idx = 0;
882 int fd = -1; /* used to write the files out as they are read */
883 unsigned char *data;
884 char outfile[MAX_PATH];
885 long read, total = 0;
887 while (1)
889 if (!done_playing)
891 if (handles[idx] > 0) {
893 /* create the output file */
894 if (fd < 0) {
895 rb->snprintf(outfile, MAX_PATH, "/file%d.mp3", idx);
896 fd = rb->open(outfile, O_CREAT|O_TRUNC|O_WRONLY);
897 if (fd < 0) {
898 DEBUGF("couldn't create file\n");
899 rb->splash(HZ, "couldn't create file");
903 /* read as much data as possible */
904 do {
905 read = bufgetdata(handles[idx], GUARD_SIZE, &data);
906 if (read >= 0) {
907 read = MIN(read, GUARD_SIZE);
908 rb->write(fd, data, read);
909 total += read;
910 bufadvance(handles[idx], read);
911 rb->lcd_clear_display();
912 print_progress(total, idx+1, num_files);
913 print_metadata(meta_handles[idx]);
914 rb->lcd_update();
916 rb->sleep(HZ/100);
917 } while (read > 0);
919 if (read >= 0 && total >= 0) {
920 /* some data was read */
921 DEBUGF("read %ld bytes from handle %d\n", total,
922 handles[idx]);
925 /* check the return value to determine what exactly happened */
926 if (read == -2) {
927 DEBUGF("data for handle %d isn't ready\n", handles[idx]);
928 } else if (read == -1) {
929 DEBUGF("couldn't find handle %d\n", handles[idx]);
930 } else if (read == 0) {
931 DEBUGF("finished reading handle %d\n", handles[idx]);
932 bufclose(handles[idx]);
933 bufclose(meta_handles[idx]);
934 rb->close(fd);
935 fd = -1;
936 total = 0;
938 /* move on to the next file and check if we've finished */
939 idx++;
940 if (idx >= num_files) {
941 done_playing = true;
942 break;
948 rb->sleep(HZ/10);
951 DEBUGF("removing the codec thread\n");
952 rb->remove_thread(NULL);
955 void bufopen_thread(void)
957 int idx = 0, ret;
958 char buf[MAX_PATH];
959 while (idx < num_files)
961 /* open the metadata file */
962 rb->snprintf(buf, MAX_PATH, "/meta%s.txt", files[idx]);
963 meta_handles[idx] = bufopen(buf, 0, TYPE_ID3);
964 if (meta_handles[idx] > 0)
966 /* open the audio file */
967 ret = bufopen(files[idx], 0, TYPE_AUDIO);
968 if (ret > 0) {
969 handles[idx++] = ret;
970 } else {
971 /* couldn't open the audio file, close the metadata handle */
972 bufclose(meta_handles[idx]);
975 rb->sleep(HZ*2);
978 DEBUGF("bufopen thread finished\n");
979 rb->remove_thread(NULL);
983 /* this is the plugin entry point */
984 enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
986 (void)parameter;
987 rb = api;
989 buffer_init();
991 if (!test_ll())
993 DEBUGF("linked list test failed\n");
994 rb->splash(HZ, "linked list test failed");
995 return PLUGIN_ERROR;
998 codecthread_id = rb->create_thread(codec_thread,
999 codec_stack,
1000 sizeof(codec_stack),
1001 "codec"
1002 IF_PRIO(,PRIORITY_PLAYBACK)
1003 IF_COP(, CPU, false));
1005 bufopenthread_id = rb->create_thread(bufopen_thread,
1006 bufopen_stack,
1007 sizeof(bufopen_stack),
1008 "bufopen"
1009 IF_PRIO(,PRIORITY_BACKGROUND)
1010 IF_COP(, CPU, false));
1012 if (!codecthread_id)
1014 rb->splash(HZ, "failed to create codec thread");
1015 return PLUGIN_ERROR;
1017 else if (!bufopenthread_id)
1019 rb->splash(HZ, "failed to create bufopen thread");
1020 return PLUGIN_ERROR;
1022 else
1025 while (!done_playing)
1027 if (data_rem() > 0 && wasted_space() > buffer_len/5) {
1028 DEBUGF("there is %ld bytes of wasted space\n", wasted_space());
1030 /* free buffer from outdated audio data */
1031 struct memory_handle *m = first_handle;
1032 while (m) {
1033 if (m->type == TYPE_AUDIO)
1034 free_buffer(m->id);
1035 m = m->next;
1038 /* free buffer by moving metadata */
1039 m = first_handle;
1040 while (m) {
1041 if (m->type != TYPE_AUDIO)
1042 free_buffer(m->id);
1043 m = m->next;
1046 graph_view(100);
1049 if (data_rem() > 0 && BUF_USED < 3*buffer_len/4 &&
1050 disk_is_spinning())
1052 DEBUGF("%ld bytes left to buffer and the buffer is low\n",
1053 data_rem());
1054 fill_buffer();
1055 } else {
1056 rb->sleep(HZ/2);
1059 DEBUGF("done playing\n");
1060 rb->yield();
1063 rb->backlight_on();
1064 DEBUGF("end of plugin\n");
1065 return PLUGIN_OK;