1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
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 ****************************************************************************/
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
55 static void graph_view(int width
);
68 struct memory_handle
{
69 int id
; /* A unique ID for the handle */
73 size_t data
; /* Start index of the handle's data buffer */
74 size_t ridx
; /* Current read pointer, relative to the main buffer */
75 size_t widx
; /* Current write pointer */
76 size_t filesize
; /* File total length */
77 size_t filerem
; /* Remaining bytes of file NOT in buffer */
78 size_t available
; /* Available bytes to read from buffer */
79 size_t offset
; /* Offset at which we started reading the file */
80 struct memory_handle
*next
;
82 /* at all times, we have: filesize == offset + available + filerem */
86 static char *guard_buffer
;
88 static size_t buffer_len
;
90 static size_t buf_widx
; /* current writing position */
91 static size_t buf_ridx
; /* current reading position */
92 /* buf_*idx are values relative to the buffer, not real pointers. */
94 static size_t conf_filechunk
;
96 /* current memory handle in the linked list. NULL when the list is empty. */
97 static struct memory_handle
*cur_handle
;
98 /* first memory handle in the linked list. NULL when the list is empty. */
99 static struct memory_handle
*first_handle
;
101 static int num_handles
; /* number of handles in the list */
103 /* Handle cache (makes find_handle faster).
104 These need to be global so that move_handle can invalidate them. */
105 static int cached_handle_id
= -1;
106 static struct memory_handle
*cached_handle
= NULL
;
110 LINKED LIST MANAGEMENT
111 ======================
113 add_handle : Add a handle to the list
114 rm_handle : Remove a handle from the list
115 find_handle : Get a handle pointer from an ID
116 move_handle : Move a handle in the buffer (with or without its data)
118 These functions only handle the linked list structure. They don't touch the
119 contents of the struct memory_handle headers. They also change the buf_*idx
120 pointers when necessary and manage the handle IDs.
122 The first and current (== last) handle are kept track of.
123 A new handle is added at buf_widx and becomes the current one.
124 buf_widx always points to the current writing position for the current handle
125 buf_ridx always points to the location of the first handle.
126 buf_ridx == buf_widx means the buffer is empty.
130 /* Add a new handle to the linked list and return it. It will have become the
131 new current handle. The handle will reserve "data_size" bytes or if that's
132 not possible, decrease "data_size" to allow adding the handle. */
133 static struct memory_handle
*add_handle(size_t *data_size
)
135 /* this will give each handle a unique id */
136 static int cur_handle_id
= 1;
138 /* make sure buf_widx is 32-bit aligned so that the handle struct is,
139 but before that we check we can actually align. */
140 if (RINGBUF_ADD_CROSS(buf_widx
, 3, buf_ridx
) >= 0) {
143 buf_widx
= (RINGBUF_ADD(buf_widx
, 3)) & ~3;
145 size_t len
= (data_size
? *data_size
: 0)
146 + sizeof(struct memory_handle
);
148 /* check that we actually can add the handle and its data */
149 int overlap
= RINGBUF_ADD_CROSS(buf_widx
, len
, buf_ridx
);
151 *data_size
-= overlap
;
154 if (len
< sizeof(struct memory_handle
)) {
155 /* There isn't even enough space to write the struct */
159 struct memory_handle
*new_handle
=
160 (struct memory_handle
*)(&buffer
[buf_widx
]);
162 /* only advance the buffer write index of the size of the struct */
163 buf_widx
= RINGBUF_ADD(buf_widx
, sizeof(struct memory_handle
));
166 /* the new handle is the first one */
167 first_handle
= new_handle
;
171 cur_handle
->next
= new_handle
;
174 cur_handle
= new_handle
;
175 cur_handle
->id
= cur_handle_id
++;
176 cur_handle
->next
= NULL
;
181 /* Delete a given memory handle from the linked list
182 and return true for success. Nothing is actually erased from memory. */
183 static bool rm_handle(struct memory_handle
*h
)
185 if (h
== first_handle
) {
186 first_handle
= h
->next
;
187 if (h
== cur_handle
) {
188 /* h was the first and last handle: the buffer is now empty */
192 /* update buf_ridx to point to the new first handle */
193 buf_ridx
= (void *)first_handle
- (void *)buffer
;
196 struct memory_handle
*m
= first_handle
;
197 while (m
&& m
->next
!= h
) {
200 if (h
&& m
&& m
->next
== h
) {
202 if (h
== cur_handle
) {
214 /* Return a pointer to the memory handle of given ID.
215 NULL if the handle wasn't found */
216 static struct memory_handle
*find_handle(int handle_id
)
218 /* simple caching because most of the time the requested handle
219 will either be the same as the last, or the one after the last */
222 if (cached_handle_id
== handle_id
&&
223 cached_handle_id
== cached_handle
->id
)
224 return cached_handle
;
225 else if (cached_handle
->next
&& (cached_handle
->next
->id
== handle_id
))
227 /* JD's quick testing showd this block was only entered
228 2/1971 calls to find_handle.
229 8/1971 calls to find_handle resulted in a cache miss */
230 cached_handle
= cached_handle
->next
;
231 cached_handle_id
= handle_id
;
232 return cached_handle
;
236 struct memory_handle
*m
= first_handle
;
237 while (m
&& m
->id
!= handle_id
) {
240 cached_handle_id
= handle_id
;
242 return (m
&& m
->id
== handle_id
) ? m
: NULL
;
245 /* Move a memory handle and data_size of its data of delta.
246 Return a pointer to the new location of the handle.
247 delta is the value of which to move the struct data.
248 data_size is the amount of data to move along with the struct. */
249 static struct memory_handle
*move_handle(struct memory_handle
*h
,
250 size_t *delta
, size_t data_size
)
253 /* aligning backwards would yield a negative result,
254 and moving the handle of such a small amount is a waste
258 /* make sure delta is 32-bit aligned so that the handle struct is. */
259 *delta
= (*delta
- 3) & ~3;
261 size_t newpos
= RINGBUF_ADD((void *)h
- (void *)buffer
, *delta
);
263 struct memory_handle
*dest
= (struct memory_handle
*)(&buffer
[newpos
]);
265 /* Invalidate the cache to prevent it from keeping the old location of h */
266 if (h
== cached_handle
)
267 cached_handle
= NULL
;
269 /* the cur_handle pointer might need updating */
270 if (h
== cur_handle
) {
274 if (h
== first_handle
) {
278 struct memory_handle
*m
= first_handle
;
279 while (m
&& m
->next
!= h
) {
282 if (h
&& m
&& m
->next
== h
) {
289 rb
->memmove(dest
, h
, sizeof(struct memory_handle
) + data_size
);
296 BUFFER SPACE MANAGEMENT
297 =======================
299 buffer_handle : Buffer data for a handle
300 free_buffer : Free buffer space by moving a handle
301 fill_buffer : Call buffer_handle for all handles that have data to buffer
302 can_add_handle : Indicate whether it's safe to add a handle.
303 data_rem : Total amount of data needing to be buffered
304 wasted_space : Total amount of space available for freeing
306 These functions are used by the buffering thread to manage buffer space.
309 /* Buffer data for the given handle. Return the amount of data buffered
310 or -1 if the handle wasn't found */
311 static ssize_t
buffer_handle(int handle_id
)
313 DEBUGF("buffer_handle(%d)\n", handle_id
);
314 struct memory_handle
*h
= find_handle(handle_id
);
318 if (h
->filerem
== 0) {
319 /* nothing left to buffer */
323 if (h
->fd
< 0) /* file closed, reopen */
326 h
->fd
= rb
->open(h
->path
, O_RDONLY
);
334 rb
->lseek(h
->fd
, h
->offset
, SEEK_SET
);
338 while (h
->filerem
> 0)
340 /* max amount to copy */
341 size_t copy_n
= MIN( MIN(h
->filerem
, conf_filechunk
),
342 buffer_len
- h
->widx
);
344 /* stop copying if it would overwrite the reading position
345 or the next handle */
346 if (RINGBUF_ADD_CROSS(h
->widx
, copy_n
, buf_ridx
) >= 0 || (h
->next
&&
347 RINGBUF_ADD_CROSS(h
->widx
, copy_n
, (unsigned)
348 ((void *)h
->next
- (void *)buffer
)) > 0))
351 /* rc is the actual amount read */
352 int rc
= rb
->read(h
->fd
, &buffer
[h
->widx
], copy_n
);
356 DEBUGF("File ended %ld bytes early\n", (long)h
->filerem
);
357 h
->filesize
-= h
->filerem
;
363 h
->widx
= RINGBUF_ADD(h
->widx
, rc
);
371 if (h
->filerem
== 0) {
372 /* finished buffering the file */
377 DEBUGF("buffered %ld bytes (%ld of %ld available, rem: %ld, off: %ld)\n",
378 ret
, h
->available
, h
->filesize
, h
->filerem
, h
->offset
);
385 /* Free buffer space by moving the handle struct right before the useful
386 part of its data buffer or by moving all the data. */
387 static void free_buffer(int handle_id
)
389 struct memory_handle
*h
= find_handle(handle_id
);
394 /* The value of delta might change for alignment reasons */
396 if (h
->next
&& (h
->type
== TYPE_ID3
|| h
->type
== TYPE_CUESHEET
||
397 h
->type
== TYPE_IMAGE
) && h
->filerem
== 0 )
399 /* metadata handle: we can move all of it */
400 delta
= RINGBUF_SUB( (unsigned)((void *)h
->next
- (void *)buffer
),
401 h
->data
) - h
->available
;
402 h
= move_handle(h
, &delta
, h
->available
);
404 h
->data
= RINGBUF_ADD(h
->data
, delta
);
405 h
->ridx
= RINGBUF_ADD(h
->ridx
, delta
);
406 h
->widx
= RINGBUF_ADD(h
->widx
, delta
);
407 DEBUGF("free_buffer(%d): metadata, moved by %ld bytes\n",
412 /* only move the handle struct */
413 delta
= RINGBUF_SUB(h
->ridx
, h
->data
);
414 h
= move_handle(h
, &delta
, 0);
416 h
->data
= RINGBUF_ADD(h
->data
, delta
);
417 h
->available
-= delta
;
419 DEBUGF("free_buffer(%d): audio, %ld bytes freed\n", handle_id
, delta
);
423 /* Fill the buffer by buffering as much data as possible for handles that still
424 have data left to buffer */
425 static void fill_buffer(void)
427 DEBUGF("fill buffer()\n");
428 struct memory_handle
*m
= first_handle
;
430 if (m
->filerem
> 0) {
431 buffer_handle(m
->id
);
437 /* Check whether it's safe to add a new handle and reserve space to let the
438 current one finish buffering its data. Used by bufopen and bufgetdata as
439 a preliminary check before even trying to physically add the handle.
440 Returns true if it's ok to add a new handle, false if not.
442 static bool can_add_handle(void)
444 if (cur_handle
&& cur_handle
->filerem
> 0) {
445 /* the current handle hasn't finished buffering. We can only add
446 a new one if there is already enough free space to finish
448 if (cur_handle
->filerem
< (buffer_len
- BUF_USED
)) {
449 /* Before adding the new handle we reserve some space for the
450 current one to finish buffering its data. */
451 buf_widx
= RINGBUF_ADD(buf_widx
, cur_handle
->filerem
);
460 /* Return the total amount of data left to be buffered for all the handles */
461 static size_t data_rem(void)
465 struct memory_handle
*m
= first_handle
;
474 /* Return the amount of data we have but don't need anymore. This data can be
475 safely erased to reclaim buffer space. */
476 static size_t wasted_space(void)
480 struct memory_handle
*m
= first_handle
;
482 ret
+= RINGBUF_SUB(m
->ridx
, m
->data
);
491 BUFFERING API FUNCTIONS
492 =======================
494 bufopen : Request the opening of a new handle for a file
495 bufclose : Close an open handle
496 bufseek : Set the read pointer in a handle
497 bufadvance : Move the read pointer in a handle
498 bufread : Copy data from a handle into a given buffer
499 bufgetdata : Give a pointer to the handle's data
501 These functions are exported, to allow interaction with the buffer.
502 They take care of the content of the structs, and rely on the linked list
503 management functions for all the actual handle management work.
507 /* Request a file be buffered
508 filename: name of the file to open
509 offset:starting offset to buffer from the file
510 return value: <0 if the file cannot be opened, or one file already
511 queued to be opened, otherwise the handle for the file in the buffer
513 int bufopen(char *file
, size_t offset
, enum data_type type
)
515 if (!can_add_handle())
518 int fd
= rb
->open(file
, O_RDONLY
);
522 size_t size
= rb
->filesize(fd
) - offset
;
524 if (type
!= TYPE_AUDIO
&&
525 size
+ sizeof(struct memory_handle
) > buffer_len
- buf_widx
)
527 /* for types other than audio, the data can't wrap */
531 DEBUGF("bufopen: %s (offset: %ld) (%ld bytes needed)...\n",
534 struct memory_handle
*h
= add_handle(&size
);
537 DEBUGF("failed to add handle\n");
542 if (offset
) rb
->lseek(fd
, offset
, SEEK_SET
);
543 rb
->strncpy(h
->path
, file
, MAX_PATH
);
545 h
->filesize
= rb
->filesize(fd
);
546 h
->filerem
= h
->filesize
- offset
;
554 DEBUGF("allocated %ld bytes. ID: %d\n", size
, h
->id
);
558 /* Close the handle. Return 0 for success and < 0 for failure */
559 int bufclose(int handle_id
)
561 DEBUGF("bufclose(%d)\n", handle_id
);
562 struct memory_handle
*h
= find_handle(handle_id
);
570 /* Set reading index in handle (relatively to the start of the file).
571 Access before the available data will trigger a rebuffer.
573 TODO: Maybe force an immediate rebuffer by calling buffer_handle() ?
574 Return 0 for success and < 0 for failure:
575 -1 if the handle wasn't found
576 -2 if there is no data available at the new position
577 (the reading index is still moved)
578 -3 if the new requested position was beyond the end of the file
580 int bufseek(int handle_id
, size_t newpos
)
583 struct memory_handle
*h
= find_handle(handle_id
);
587 if (newpos
> h
->filesize
) {
588 /* access beyond the end of the file */
592 else if (newpos
< h
->offset
) {
593 /* access before what we currently have. A rebuffer is needed. */
596 h
->filerem
= h
->filesize
- newpos
;
597 /* having changed filerem should be enough to trigger the rebuffer. */
602 else if (newpos
> h
->offset
+ h
->available
) {
603 /* data isn't available yet. */
607 h
->ridx
= RINGBUF_ADD(h
->data
, newpos
);
611 /* Advance the reading index in a handle (relatively to its current position).
612 Return 0 for success and < 0 for failure
613 TODO: Add some rebuffering like in bufseek */
614 int bufadvance(int handle_id
, off_t offset
)
616 struct memory_handle
*h
= find_handle(handle_id
);
622 /* check for access beyond what's available */
623 if ((size_t)offset
> (h
->available
- RINGBUF_SUB(h
->ridx
, h
->data
)))
626 h
->ridx
= RINGBUF_ADD(h
->ridx
, offset
);
630 /* check for access before what's available */
631 if ((size_t)(-offset
) > RINGBUF_SUB(h
->ridx
, h
->data
))
634 h
->ridx
= RINGBUF_SUB(h
->ridx
, (size_t)(-offset
));
640 /* Copy data from the given handle to the dest buffer.
641 Return the number of bytes copied or < 0 for failure. */
642 ssize_t
bufread(int handle_id
, size_t size
, char *dest
)
644 struct memory_handle
*h
= find_handle(handle_id
);
645 size_t buffered_data
;
649 if (h
->available
== 0 && h
->filerem
> 0) /* Data isn't ready */
652 if (h
->available
== 0 && h
->filerem
== 0) /* File is finished reading */
655 buffered_data
= MIN(size
, h
->available
- RINGBUF_SUB(h
->ridx
, h
->data
));
657 if (h
->ridx
+ buffered_data
> buffer_len
)
659 /* the data wraps around the end of the buffer */
660 size_t read
= buffer_len
- h
->ridx
;
661 rb
->memcpy(dest
, &buffer
[h
->ridx
], read
);
662 rb
->memcpy(dest
+read
, buffer
, buffered_data
- read
);
664 else rb
->memcpy(dest
, &buffer
[h
->ridx
], buffered_data
);
666 return buffered_data
;
669 /* Update the "data" pointer to make the handle's data available to the caller.
670 Return the length of the available linear data or < 0 for failure.
671 size is the amount of linear data requested. it can be 0 to get as
673 The guard buffer may be used to provide the requested size */
674 ssize_t
bufgetdata(int handle_id
, size_t size
, unsigned char **data
)
676 struct memory_handle
*h
= find_handle(handle_id
);
680 if (h
->available
== 0 && h
->filerem
> 0) /* Data isn't ready */
683 if (h
->available
== 0 && h
->filerem
== 0) /* File is finished reading */
688 if (h
->ridx
+ size
> buffer_len
&&
689 h
->available
- RINGBUF_SUB(h
->ridx
, h
->data
) >= size
)
691 /* the data wraps around the end of the buffer :
692 use the guard buffer to provide the requested amount of data. */
693 size_t copy_n
= MIN(h
->ridx
+ size
- buffer_len
, GUARD_SIZE
);
694 rb
->memcpy(guard_buffer
, (unsigned char *)buffer
, copy_n
);
695 ret
= buffer_len
- h
->ridx
+ copy_n
;
696 DEBUGF("used the guard buffer to complete\n");
700 ret
= MIN(h
->available
- RINGBUF_SUB(h
->ridx
, h
->data
),
701 buffer_len
- h
->ridx
);
704 *data
= (unsigned char *)&buffer
[h
->ridx
];
706 /* DEBUGF("bufgetdata(%d): h->ridx=%ld, ret=%ld\n", handle_id,
707 (long)h->ridx, ret); */
719 struct memory_handle
*m1
, *m2
, *m3
, *m4
;
721 if (cur_handle
!= NULL
|| first_handle
!= NULL
)
724 m1
= add_handle(NULL
);
726 if (cur_handle
!= m1
|| first_handle
!= m1
|| m1
->next
!= NULL
)
729 m2
= add_handle(NULL
);
731 if (cur_handle
!= m2
|| first_handle
!= m1
|| m1
->next
!= m2
|| m2
->next
!= NULL
)
734 m3
= add_handle(NULL
);
736 if (cur_handle
!= m3
|| first_handle
!= m1
|| m2
->next
!= m3
|| m3
->next
!= NULL
)
741 if (cur_handle
!= m3
|| first_handle
!= m1
|| m1
->next
!= m3
)
746 if (cur_handle
!= m1
|| first_handle
!= m1
|| m1
->next
!= NULL
)
749 m4
= add_handle(NULL
);
751 if (cur_handle
!= m4
|| first_handle
!= m1
|| m1
->next
!= m4
|| m4
->next
!= NULL
)
756 if (cur_handle
!= m4
|| first_handle
!= m4
)
761 if (cur_handle
!= NULL
|| first_handle
!= NULL
)
764 m1
= add_handle(NULL
);
765 m2
= add_handle(NULL
);
766 m3
= add_handle(NULL
);
767 m4
= add_handle(NULL
);
769 if (cur_handle
!= m4
|| first_handle
!= m1
)
772 size_t delta
= 1024*100;
773 m2
= move_handle(m2
, &delta
, 0);
775 if (cur_handle
!= m4
|| first_handle
!= m1
|| m1
->next
!= m2
|| m2
->next
!= m3
)
779 m1
= move_handle(m1
, &delta
, 0);
781 if (cur_handle
!= m4
|| first_handle
!= m1
|| m1
->next
!= m2
)
789 if (cur_handle
!= NULL
|| first_handle
!= NULL
)
795 /* display a nice graphical view of the ringbuffer. */
796 static void graph_view(int width
)
798 #ifndef ROCKBOX_HAS_LOGF
800 r_pos
= buf_ridx
* width
/ buffer_len
;
801 w_pos
= buf_widx
* width
/ buffer_len
;
804 for (i
=0; i
<= width
; i
++)
806 if (i
!= r_pos
&& i
!= w_pos
)
808 if (buf_ridx
<= buf_widx
)
810 if (i
> r_pos
&& i
< w_pos
) {
818 if (i
> r_pos
|| i
< w_pos
) {
827 if (i
== r_pos
&& i
== w_pos
)
829 if (buf_ridx
<= buf_widx
) {
834 } else if (i
== r_pos
) {
836 } else if (i
== w_pos
) {
848 void print_progress(size_t amount
, int file
, int numfiles
)
851 rb
->snprintf(buf
, sizeof(buf
), "file %d of %d", file
, numfiles
);
852 rb
->lcd_puts(0, 0, buf
);
853 rb
->snprintf(buf
, sizeof(buf
), "read: %ld", amount
);
854 rb
->lcd_puts(0, 1, buf
);
857 void print_metadata(int handle_id
)
860 char *artist
, *title
, *newline
;
861 char art
[50], ttl
[50];
862 int artist_len
, title_len
;
865 ret
= bufgetdata(handle_id
, 0, &buf
);
868 newline
= rb
->strchr(artist
, '\n');
869 artist_len
= newline
- artist
;
870 rb
->strncpy(art
, artist
, artist_len
);
874 newline
= rb
->strchr(title
, '\n');
875 title_len
= newline
- title
;
876 rb
->strncpy(ttl
, title
, title_len
);
879 rb
->lcd_puts(0, 3, art
);
880 rb
->lcd_puts(0, 4, ttl
);
883 bool buffer_init(void)
885 buffer
= rb
->plugin_get_audio_buffer(&buffer_len
);
888 DEBUGF("couldn't allocate buffer\n");
891 buffer_len
-= GUARD_SIZE
;
892 guard_buffer
= buffer
+ buffer_len
;
900 conf_filechunk
= AUDIO_DEFAULT_FILECHUNK
;
905 bool disk_is_spinning(void)
916 static long codec_stack
[4*DEFAULT_STACK_SIZE
/sizeof(long)];
917 static struct thread_entry
* codecthread_id
;
919 static long bufopen_stack
[2*DEFAULT_STACK_SIZE
/sizeof(long)];
920 static struct thread_entry
* bufopenthread_id
;
922 bool done_playing
= false;
924 #define MAX_HANDLES 16
926 int handles
[MAX_HANDLES
];
927 int meta_handles
[MAX_HANDLES
];
929 void codec_thread(void)
932 int fd
= -1; /* used to write the files out as they are read */
934 char outfile
[MAX_PATH
];
935 long read
, total
= 0;
941 if (handles
[idx
] > 0) {
943 /* create the output file */
945 rb
->snprintf(outfile
, MAX_PATH
, "/file%d.mp3", idx
);
946 fd
= rb
->open(outfile
, O_CREAT
|O_TRUNC
|O_WRONLY
);
948 DEBUGF("couldn't create file\n");
949 rb
->splash(HZ
, "couldn't create file");
953 /* read as much data as possible */
955 read
= bufgetdata(handles
[idx
], GUARD_SIZE
, &data
);
957 read
= MIN(read
, GUARD_SIZE
);
958 rb
->write(fd
, data
, read
);
960 bufadvance(handles
[idx
], read
);
961 rb
->lcd_clear_display();
962 print_progress(total
, idx
+1, num_files
);
963 print_metadata(meta_handles
[idx
]);
969 if (read
>= 0 && total
>= 0) {
970 /* some data was read */
971 DEBUGF("read %ld bytes from handle %d\n", total
,
975 /* check the return value to determine what exactly happened */
977 DEBUGF("data for handle %d isn't ready\n", handles
[idx
]);
978 } else if (read
== -1) {
979 DEBUGF("couldn't find handle %d\n", handles
[idx
]);
980 } else if (read
== 0) {
981 DEBUGF("finished reading handle %d\n", handles
[idx
]);
982 bufclose(handles
[idx
]);
983 bufclose(meta_handles
[idx
]);
988 /* move on to the next file and check if we've finished */
990 if (idx
>= num_files
) {
1001 DEBUGF("removing the codec thread\n");
1002 rb
->remove_thread(NULL
);
1005 void bufopen_thread(void)
1009 while (idx
< num_files
)
1011 /* open the metadata file */
1012 rb
->snprintf(buf
, MAX_PATH
, "/meta%s.txt", files
[idx
]);
1013 meta_handles
[idx
] = bufopen(buf
, 0, TYPE_ID3
);
1014 if (meta_handles
[idx
] > 0)
1016 /* open the audio file */
1017 ret
= bufopen(files
[idx
], 0, TYPE_AUDIO
);
1019 handles
[idx
++] = ret
;
1021 /* couldn't open the audio file, close the metadata handle */
1022 bufclose(meta_handles
[idx
]);
1028 DEBUGF("bufopen thread finished\n");
1029 rb
->remove_thread(NULL
);
1033 /* this is the plugin entry point */
1034 enum plugin_status
plugin_start(struct plugin_api
* api
, void* parameter
)
1043 DEBUGF("linked list test failed\n");
1044 rb
->splash(HZ
, "linked list test failed");
1045 return PLUGIN_ERROR
;
1048 codecthread_id
= rb
->create_thread(codec_thread
,
1050 sizeof(codec_stack
),
1052 IF_PRIO(,PRIORITY_PLAYBACK
)
1053 IF_COP(, CPU
, false));
1055 bufopenthread_id
= rb
->create_thread(bufopen_thread
,
1057 sizeof(bufopen_stack
),
1059 IF_PRIO(,PRIORITY_BACKGROUND
)
1060 IF_COP(, CPU
, false));
1062 if (!codecthread_id
)
1064 rb
->splash(HZ
, "failed to create codec thread");
1065 return PLUGIN_ERROR
;
1067 else if (!bufopenthread_id
)
1069 rb
->splash(HZ
, "failed to create bufopen thread");
1070 return PLUGIN_ERROR
;
1075 while (!done_playing
)
1077 if (data_rem() > 0 && wasted_space() > buffer_len
/5) {
1078 DEBUGF("there is %ld bytes of wasted space\n", wasted_space());
1080 /* free buffer from outdated audio data */
1081 struct memory_handle
*m
= first_handle
;
1083 if (m
->type
== TYPE_AUDIO
)
1088 /* free buffer by moving metadata */
1091 if (m
->type
!= TYPE_AUDIO
)
1099 if (data_rem() > 0 && BUF_USED
< 3*buffer_len
/4 &&
1102 DEBUGF("%ld bytes left to buffer and the buffer is low\n",
1109 DEBUGF("done playing\n");
1114 DEBUGF("end of plugin\n");