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
65 struct memory_handle
{
66 int id
; /* A unique ID for the handle */
70 size_t data
; /* Start index of the handle's data buffer */
71 size_t ridx
; /* Current read pointer, relative to the main buffer */
72 size_t widx
; /* Current write pointer */
73 size_t filesize
; /* File total length */
74 size_t filerem
; /* Remaining bytes of file NOT in buffer */
75 size_t available
; /* Available bytes to read from buffer */
76 size_t offset
; /* Offset at which we started reading the file */
77 struct memory_handle
*next
;
82 static char *guard_buffer
;
84 static size_t buffer_len
;
85 static size_t buf_widx
;
86 static size_t buf_ridx
;
88 static size_t conf_filechunk
;
90 /* current memory handle in the linked list. NULL when the list is empty. */
91 static struct memory_handle
*cur_handle
;
92 /* first memory handle in the linked list. NULL when the list is empty. */
93 static struct memory_handle
*first_handle
;
94 static int num_handles
;
96 static void graph_view(int width
);
99 /* add a new handle to the linked list and return it. It will have become the
100 new current handle. The handle will reserve "data_size" bytes or if that's
101 not possible, decrease "data_size" to allow adding the handle. */
102 static struct memory_handle
*add_handle(size_t *data_size
)
104 /* this will give each handle a unique id */
105 static int cur_handle_id
= 1;
107 /* make sure buf_widx is 32-bit aligned so that the handle struct is,
108 but before that we check we can actually align. */
109 if (RINGBUF_ADD_CROSS(buf_widx
, 3, buf_ridx
) >= 0) {
112 buf_widx
= (RINGBUF_ADD(buf_widx
, 3)) & ~3;
114 size_t len
= (data_size
? *data_size
: 0)
115 + sizeof(struct memory_handle
);
117 /* check that we actually can add the handle and its data */
118 int overlap
= RINGBUF_ADD_CROSS(buf_widx
, len
, buf_ridx
);
120 *data_size
-= overlap
;
123 if (len
< sizeof(struct memory_handle
)) {
127 struct memory_handle
*new_handle
=
128 (struct memory_handle
*)(&buffer
[buf_widx
]);
130 /* only advance the buffer write index of the size of the struct */
131 buf_widx
= RINGBUF_ADD(buf_widx
, sizeof(struct memory_handle
));
134 /* the new handle is the first one */
135 first_handle
= new_handle
;
139 cur_handle
->next
= new_handle
;
142 cur_handle
= new_handle
;
143 cur_handle
->id
= cur_handle_id
++;
144 cur_handle
->next
= NULL
;
149 /* delete a given memory handle from the linked list
150 and return true for success. Nothing is actually erased from memory. */
151 static bool rm_handle(struct memory_handle
*h
)
153 if (h
== first_handle
) {
154 first_handle
= h
->next
;
155 if (h
== cur_handle
) {
156 DEBUGF("removing the first and last handle\n");
157 /* h was the first and last handle */
161 buf_ridx
= (void *)first_handle
- (void *)buffer
;
164 struct memory_handle
*m
= first_handle
;
165 while (m
&& m
->next
!= h
) {
168 if (h
&& m
&& m
->next
== h
) {
170 if (h
== cur_handle
) {
182 /* these are unfortunalty needed to be global
183 so move_handle can invalidate them */
184 static int cached_handle_id
= -1;
185 static struct memory_handle
*cached_handle
= NULL
;
187 /* Return a pointer to the memory handle of given ID.
188 NULL if the handle wasn't found */
189 static struct memory_handle
*find_handle(int handle_id
)
191 /* simple caching because most of the time the requested handle
192 will either be the same as the last, or the one after the last */
195 if (cached_handle_id
== handle_id
&&
196 cached_handle_id
== cached_handle
->id
)
197 return cached_handle
;
198 else if (cached_handle
->next
&& (cached_handle
->next
->id
== handle_id
))
200 /* JD's quick testing showd this block was only entered
201 2/1971 calls to find_handle.
202 8/1971 calls to find_handle resulted in a cache miss */
203 cached_handle
= cached_handle
->next
;
204 cached_handle_id
= handle_id
;
205 return cached_handle
;
209 struct memory_handle
*m
= first_handle
;
210 while (m
&& m
->id
!= handle_id
) {
213 cached_handle_id
= handle_id
;
215 return (m
&& m
->id
== handle_id
) ? m
: NULL
;
218 /* Move a memory handle and data_size of its data of delta.
219 Return a pointer to the new location of the handle.
220 delta is the value of which to move the struct data.
221 data_size is the amount of data to move along with the struct. */
222 static struct memory_handle
*move_handle(struct memory_handle
*h
,
223 size_t *delta
, size_t data_size
)
226 /* aligning backwards would yield a negative result,
227 and moving the handle of such a small amount is a waste
231 /* make sure delta is 32-bit aligned so that the handle struct is. */
232 *delta
= (*delta
- 3) & ~3;
234 size_t newpos
= RINGBUF_ADD((void *)h
- (void *)buffer
, *delta
);
236 struct memory_handle
*dest
= (struct memory_handle
*)(&buffer
[newpos
]);
238 /* Invalidate the cache to prevent it from keeping the old location of h */
239 if (h
== cached_handle
)
240 cached_handle
= NULL
;
242 /* the cur_handle pointer might need updating */
243 if (h
== cur_handle
) {
247 if (h
== first_handle
) {
251 struct memory_handle
*m
= first_handle
;
252 while (m
&& m
->next
!= h
) {
255 if (h
&& m
&& m
->next
== h
) {
262 rb
->memmove(dest
, h
, sizeof(struct memory_handle
) + data_size
);
267 /* Buffer data for the given handle. Return the amount of data buffered
268 or -1 if the handle wasn't found */
269 static ssize_t
buffer_handle(int handle_id
)
271 DEBUGF("buffer_handle(%d)\n", handle_id
);
272 struct memory_handle
*h
= find_handle(handle_id
);
276 if (h
->filerem
== 0) {
277 /* nothing left to buffer */
281 if (h
->fd
< 0) /* file closed, reopen */
284 h
->fd
= rb
->open(h
->path
, O_RDONLY
);
292 rb
->lseek(h
->fd
, h
->offset
, SEEK_SET
);
296 while (h
->filerem
> 0)
298 /* max amount to copy */
299 size_t copy_n
= MIN( MIN(h
->filerem
, conf_filechunk
),
300 buffer_len
- h
->widx
);
302 /* stop copying if it would overwrite the reading position
303 or the next handle */
304 if (RINGBUF_ADD_CROSS(h
->widx
, copy_n
, buf_ridx
) >= 0 || (h
->next
&&
305 RINGBUF_ADD_CROSS(h
->widx
, copy_n
, (unsigned)
306 ((void *)h
->next
- (void *)buffer
)) > 0))
309 /* rc is the actual amount read */
310 int rc
= rb
->read(h
->fd
, &buffer
[h
->widx
], copy_n
);
314 DEBUGF("File ended %ld bytes early\n", (long)h
->filerem
);
315 h
->filesize
-= h
->filerem
;
321 h
->widx
= RINGBUF_ADD(h
->widx
, rc
);
329 if (h
->filerem
== 0) {
330 /* finished buffering the file */
334 DEBUGF("buffered %ld bytes (%ld of %ld available, remaining: %ld)\n",
335 ret
, h
->available
, h
->filesize
, h
->filerem
);
342 /* Free buffer space by moving the handle struct right before the useful
343 part of its data buffer */
344 static void free_buffer(int handle_id
)
346 struct memory_handle
*h
= find_handle(handle_id
);
351 /* The value of delta might change for alignment reasons */
353 if (h
->next
&& (h
->type
== TYPE_ID3
|| h
->type
== TYPE_CUESHEET
||
354 h
->type
== TYPE_IMAGE
) && h
->filerem
== 0 )
356 /* metadata handle: we can move all of it */
357 delta
= RINGBUF_SUB( (unsigned)((void *)h
->next
- (void *)buffer
),
358 h
->data
) - h
->available
;
359 h
= move_handle(h
, &delta
, h
->available
);
361 h
->data
= RINGBUF_ADD(h
->data
, delta
);
362 h
->ridx
= RINGBUF_ADD(h
->ridx
, delta
);
363 DEBUGF("free_buffer(%d): metadata, moved by %ld bytes\n",
368 /* only move the handle struct */
369 delta
= RINGBUF_SUB(h
->ridx
, h
->data
);
370 h
= move_handle(h
, &delta
, 0);
372 h
->data
= RINGBUF_ADD(h
->data
, delta
);
373 h
->available
-= delta
;
374 DEBUGF("free_buffer(%d): audio, %ld bytes freed\n", handle_id
, delta
);
378 /* fill the buffer by buffering as much data as possible for handles that still
379 have data left to buffer */
380 static void fill_buffer(void)
382 DEBUGF("fill buffer()\n");
383 struct memory_handle
*m
= first_handle
;
385 if (m
->filerem
> 0) {
386 buffer_handle(m
->id
);
392 static size_t data_rem(void)
396 struct memory_handle
*m
= first_handle
;
405 static size_t wasted_space(void)
409 struct memory_handle
*m
= first_handle
;
411 ret
+= RINGBUF_SUB(m
->ridx
, m
->data
);
418 /* Request a file be buffered
419 filename: name of the file to open
420 offset:starting offset to buffer from the file
421 return value: <0 if the file cannot be opened, or one file already
422 queued to be opened, otherwise the handle for the file in the buffer
424 int bufopen(char *file
, size_t offset
, enum data_type type
)
426 if (cur_handle
&& cur_handle
->filerem
> 0) {
427 /* the current handle hasn't finished buffering. We can only add
428 a new one if there is already enough free space to finish
430 if (cur_handle
->filerem
< (buffer_len
- BUF_USED
)) {
431 /* Before adding the new handle we reserve some space for the
432 current one to finish buffering its data. */
433 buf_widx
= RINGBUF_ADD(buf_widx
, cur_handle
->filerem
);
439 int fd
= rb
->open(file
, O_RDONLY
);
443 size_t size
= rb
->filesize(fd
) - offset
;
445 DEBUGF("bufopen: %s (offset: %ld) (%ld bytes needed)...\n",
448 struct memory_handle
*h
= add_handle(&size
);
451 DEBUGF("failed to add handle\n");
456 if (offset
) rb
->lseek(fd
, offset
, SEEK_SET
);
457 rb
->strncpy(h
->path
, file
, MAX_PATH
);
459 h
->filesize
= rb
->filesize(fd
);
460 h
->filerem
= h
->filesize
- offset
;
468 DEBUGF("allocated %ld bytes. ID: %d\n", size
, h
->id
);
472 /* Close the handle. Return 0 for success and < 0 for failure */
473 int bufclose(int handle_id
)
475 DEBUGF("bufclose(%d)\n", handle_id
);
476 struct memory_handle
*h
= find_handle(handle_id
);
484 /* Set reading index in handle (relatively to the start of the handle data).
485 Return 0 for success and < 0 for failure */
486 int bufseek(int handle_id
, size_t offset
)
488 struct memory_handle
*h
= find_handle(handle_id
);
492 if (offset
> h
->available
)
495 h
->ridx
= RINGBUF_ADD(h
->data
, offset
);
499 /* Advance the reading index in a handle (relatively to its current position).
500 Return 0 for success and < 0 for failure */
501 int bufadvance(int handle_id
, off_t offset
)
503 struct memory_handle
*h
= find_handle(handle_id
);
509 /* check for access beyond what's available */
510 if ((size_t)offset
> (h
->available
- RINGBUF_SUB(h
->ridx
, h
->data
)))
513 h
->ridx
= RINGBUF_ADD(h
->ridx
, offset
);
517 /* check for access before what's available */
518 if ((size_t)(-offset
) > RINGBUF_SUB(h
->ridx
, h
->data
))
521 h
->ridx
= RINGBUF_SUB(h
->ridx
, (size_t)(-offset
));
527 /* Copy data from the given handle to the dest buffer.
528 Return the number of bytes copied or < 0 for failure. */
529 ssize_t
bufread(int handle_id
, size_t size
, char *dest
)
531 struct memory_handle
*h
= find_handle(handle_id
);
532 size_t buffered_data
;
536 if (h
->available
== 0 && h
->filerem
> 0)
539 if (h
->available
== 0 && h
->filerem
== 0)
542 buffered_data
= MIN(size
, h
->available
);
544 if (h
->ridx
+ buffered_data
> buffer_len
)
546 size_t read
= buffer_len
- h
->ridx
;
547 rb
->memcpy(dest
, &buffer
[h
->ridx
], read
);
548 rb
->memcpy(dest
+read
, buffer
, buffered_data
- read
);
550 else rb
->memcpy(dest
, &buffer
[h
->ridx
], buffered_data
);
552 h
->ridx
= RINGBUF_ADD(h
->ridx
, buffered_data
);
553 h
->available
-= buffered_data
;
554 return buffered_data
;
557 /* Update the "data" pointer to make the handle's data available to the caller.
558 Return the length of the available linear data or < 0 for failure. */
559 ssize_t
bufgetdata(int handle_id
, size_t size
, unsigned char **data
)
561 struct memory_handle
*h
= find_handle(handle_id
);
565 if (h
->available
== 0 && h
->filerem
> 0)
568 if (h
->available
== 0 && h
->filerem
== 0)
573 if (h
->ridx
+ size
> buffer_len
&&
574 h
->available
- RINGBUF_SUB(h
->ridx
, h
->data
) >= size
)
576 /* use the guard buffer to provide what was requested. */
577 size_t copy_n
= h
->ridx
+ size
- buffer_len
;
578 rb
->memcpy(guard_buffer
, (unsigned char *)buffer
, copy_n
);
580 DEBUGF("used the guard buffer to complete\n");
584 ret
= MIN(h
->available
- RINGBUF_SUB(h
->ridx
, h
->data
),
585 buffer_len
- h
->ridx
);
588 *data
= (unsigned char *)&buffer
[h
->ridx
];
590 /* DEBUGF("bufgetdata(%d): h->ridx=%ld, ret=%ld\n", handle_id,
591 (long)h->ridx, ret); */
597 struct memory_handle
*m1
, *m2
, *m3
, *m4
;
599 if (cur_handle
!= NULL
|| first_handle
!= NULL
)
602 m1
= add_handle(NULL
);
604 if (cur_handle
!= m1
|| first_handle
!= m1
|| m1
->next
!= NULL
)
607 m2
= add_handle(NULL
);
609 if (cur_handle
!= m2
|| first_handle
!= m1
|| m1
->next
!= m2
|| m2
->next
!= NULL
)
612 m3
= add_handle(NULL
);
614 if (cur_handle
!= m3
|| first_handle
!= m1
|| m2
->next
!= m3
|| m3
->next
!= NULL
)
619 if (cur_handle
!= m3
|| first_handle
!= m1
|| m1
->next
!= m3
)
624 if (cur_handle
!= m1
|| first_handle
!= m1
|| m1
->next
!= NULL
)
627 m4
= add_handle(NULL
);
629 if (cur_handle
!= m4
|| first_handle
!= m1
|| m1
->next
!= m4
|| m4
->next
!= NULL
)
634 if (cur_handle
!= m4
|| first_handle
!= m4
)
639 if (cur_handle
!= NULL
|| first_handle
!= NULL
)
642 m1
= add_handle(NULL
);
643 m2
= add_handle(NULL
);
644 m3
= add_handle(NULL
);
645 m4
= add_handle(NULL
);
647 if (cur_handle
!= m4
|| first_handle
!= m1
)
650 size_t delta
= 1024*100;
651 m2
= move_handle(m2
, &delta
, 0);
653 if (cur_handle
!= m4
|| first_handle
!= m1
|| m1
->next
!= m2
|| m2
->next
!= m3
)
657 m1
= move_handle(m1
, &delta
, 0);
659 if (cur_handle
!= m4
|| first_handle
!= m1
|| m1
->next
!= m2
)
667 if (cur_handle
!= NULL
|| first_handle
!= NULL
)
673 /* display a nice graphical view of the ringbuffer. */
674 static void graph_view(int width
)
676 #ifndef ROCKBOX_HAS_LOGF
678 r_pos
= buf_ridx
* width
/ buffer_len
;
679 w_pos
= buf_widx
* width
/ buffer_len
;
682 for (i
=0; i
<= width
; i
++)
684 if (i
!= r_pos
&& i
!= w_pos
)
686 if (buf_ridx
<= buf_widx
)
688 if (i
> r_pos
&& i
< w_pos
) {
696 if (i
> r_pos
|| i
< w_pos
) {
705 if (i
== r_pos
&& i
== w_pos
)
707 if (buf_ridx
<= buf_widx
) {
712 } else if (i
== r_pos
) {
714 } else if (i
== w_pos
) {
726 void print_progress(size_t amount
, int file
, int numfiles
)
729 rb
->snprintf(buf
, sizeof(buf
), "file %d of %d", file
, numfiles
);
730 rb
->lcd_puts(0, 0, buf
);
731 rb
->snprintf(buf
, sizeof(buf
), "read: %ld", amount
);
732 rb
->lcd_puts(0, 1, buf
);
735 void print_metadata(int handle_id
)
738 char *artist
, *title
, *newline
;
739 char art
[50], ttl
[50];
740 int artist_len
, title_len
;
743 ret
= bufgetdata(handle_id
, 0, &buf
);
746 newline
= rb
->strchr(artist
, '\n');
747 artist_len
= newline
- artist
;
748 rb
->strncpy(art
, artist
, artist_len
);
752 newline
= rb
->strchr(title
, '\n');
753 title_len
= newline
- title
;
754 rb
->strncpy(ttl
, title
, title_len
);
757 rb
->lcd_puts(0, 3, art
);
758 rb
->lcd_puts(0, 4, ttl
);
761 bool buffer_init(void)
763 buffer
= rb
->plugin_get_audio_buffer(&buffer_len
);
766 DEBUGF("couldn't allocate buffer\n");
769 buffer_len
-= GUARD_SIZE
;
770 guard_buffer
= buffer
+ buffer_len
;
778 conf_filechunk
= AUDIO_DEFAULT_FILECHUNK
;
783 bool disk_is_spinning(void)
790 static long codec_stack
[4*DEFAULT_STACK_SIZE
/sizeof(long)];
791 static struct thread_entry
* codecthread_id
;
793 static long bufopen_stack
[2*DEFAULT_STACK_SIZE
/sizeof(long)];
794 static struct thread_entry
* bufopenthread_id
;
796 bool done_playing
= false;
798 #define MAX_HANDLES 16
800 int handles
[MAX_HANDLES
];
801 int meta_handles
[MAX_HANDLES
];
803 void codec_thread(void)
806 int fd
= -1; /* used to write the files out as they are read */
808 char outfile
[MAX_PATH
];
809 long read
, total
= 0;
815 if (handles
[idx
] > 0) {
818 rb
->snprintf(outfile
, MAX_PATH
, "/file%d.mp3", idx
);
819 fd
= rb
->open(outfile
, O_CREAT
|O_TRUNC
|O_WRONLY
);
821 DEBUGF("couldn't create file\n");
822 rb
->splash(HZ
, "couldn't create file");
827 read
= bufgetdata(handles
[idx
], GUARD_SIZE
, &data
);
829 read
= MIN(read
, GUARD_SIZE
);
830 rb
->write(fd
, data
, read
);
832 bufadvance(handles
[idx
], read
);
833 rb
->lcd_clear_display();
834 print_progress(total
, idx
+1, num_files
);
835 print_metadata(meta_handles
[idx
]);
841 if (read
>= 0 && total
>= 0) {
842 DEBUGF("read %ld bytes from handle %d\n", total
,
847 DEBUGF("data for handle %d isn't ready\n", handles
[idx
]);
848 } else if (read
== -1) {
849 DEBUGF("couldn't find handle %d\n", handles
[idx
]);
850 } else if (read
== 0) {
851 DEBUGF("finished reading handle %d\n", handles
[idx
]);
852 bufclose(handles
[idx
]);
853 bufclose(meta_handles
[idx
]);
859 if (idx
>= num_files
) {
870 DEBUGF("removing the codec thread\n");
871 rb
->remove_thread(NULL
);
874 void bufopen_thread(void)
878 while (idx
< num_files
)
880 rb
->snprintf(buf
, MAX_PATH
, "/meta%s.txt", files
[idx
]);
881 meta_handles
[idx
] = bufopen(buf
, 0, TYPE_ID3
);
882 if (meta_handles
[idx
] > 0)
884 ret
= bufopen(files
[idx
], 0, TYPE_AUDIO
);
886 handles
[idx
++] = ret
;
888 bufclose(meta_handles
[idx
]);
894 DEBUGF("bufopen thread finished\n");
895 rb
->remove_thread(NULL
);
899 /* this is the plugin entry point */
900 enum plugin_status
plugin_start(struct plugin_api
* api
, void* parameter
)
909 DEBUGF("linked list test failed\n");
910 rb
->splash(HZ
, "linked list test failed");
914 codecthread_id
= rb
->create_thread(codec_thread
,
918 IF_PRIO(,PRIORITY_PLAYBACK
)
919 IF_COP(, CPU
, false));
921 bufopenthread_id
= rb
->create_thread(bufopen_thread
,
923 sizeof(bufopen_stack
),
925 IF_PRIO(,PRIORITY_BACKGROUND
)
926 IF_COP(, CPU
, false));
930 rb
->splash(HZ
, "failed to create codec thread");
933 else if (!bufopenthread_id
)
935 rb
->splash(HZ
, "failed to create bufopen thread");
941 while (!done_playing
)
943 if (data_rem() > 0 && wasted_space() > buffer_len
/5) {
944 DEBUGF("there is %ld bytes of wasted space\n", wasted_space());
945 struct memory_handle
*m
= first_handle
;
947 if (m
->type
== TYPE_AUDIO
)
953 if (m
->type
!= TYPE_AUDIO
)
960 if (data_rem() > 0 && BUF_USED
< 3*buffer_len
/4 &&
963 DEBUGF("%ld bytes left to buffer and the buffer is low\n",
970 DEBUGF("done playing\n");
975 DEBUGF("end of plugin\n");