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
);
67 struct memory_handle
{
68 int id
; /* A unique ID for the handle */
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
;
84 static char *guard_buffer
;
86 static size_t buffer_len
;
88 static size_t buf_widx
; /* current writing position */
89 static size_t buf_ridx
; /* current reading position */
90 /* buf_*idx are values relative to the buffer, not real pointers. */
92 static size_t conf_filechunk
;
94 /* current memory handle in the linked list. NULL when the list is empty. */
95 static struct memory_handle
*cur_handle
;
96 /* first memory handle in the linked list. NULL when the list is empty. */
97 static struct memory_handle
*first_handle
;
99 static int num_handles
; /* number of handles in the list */
101 /* Handle cache (makes find_handle faster).
102 These need to be global so that move_handle can invalidate them. */
103 static int cached_handle_id
= -1;
104 static struct memory_handle
*cached_handle
= NULL
;
108 LINKED LIST MANAGEMENT
109 ======================
111 add_handle : Add a handle to the list
112 rm_handle : Remove a handle from the list
113 find_handle : Get a handle pointer from an ID
114 move_handle : Move a handle in the buffer (with or without its data)
116 These functions only handle the linked list structure. They don't touch the
117 contents of the struct memory_handle headers. They also change the buf_*idx
118 pointers when necessary and manage the handle IDs.
120 The first and current (== last) handle are kept track of.
121 A new handle is added at buf_widx and becomes the current one.
122 buf_widx always points to the current writing position for the current handle
123 buf_ridx always points to the location of the first handle.
124 buf_ridx == buf_widx means the buffer is empty.
128 /* Add a new handle to the linked list and return it. It will have become the
129 new current handle. The handle will reserve "data_size" bytes or if that's
130 not possible, decrease "data_size" to allow adding the handle. */
131 static struct memory_handle
*add_handle(size_t *data_size
)
133 /* this will give each handle a unique id */
134 static int cur_handle_id
= 1;
136 /* make sure buf_widx is 32-bit aligned so that the handle struct is,
137 but before that we check we can actually align. */
138 if (RINGBUF_ADD_CROSS(buf_widx
, 3, buf_ridx
) >= 0) {
141 buf_widx
= (RINGBUF_ADD(buf_widx
, 3)) & ~3;
143 size_t len
= (data_size
? *data_size
: 0)
144 + sizeof(struct memory_handle
);
146 /* check that we actually can add the handle and its data */
147 int overlap
= RINGBUF_ADD_CROSS(buf_widx
, len
, buf_ridx
);
149 *data_size
-= overlap
;
152 if (len
< sizeof(struct memory_handle
)) {
153 /* There isn't even enough space to write the struct */
157 struct memory_handle
*new_handle
=
158 (struct memory_handle
*)(&buffer
[buf_widx
]);
160 /* only advance the buffer write index of the size of the struct */
161 buf_widx
= RINGBUF_ADD(buf_widx
, sizeof(struct memory_handle
));
164 /* the new handle is the first one */
165 first_handle
= new_handle
;
169 cur_handle
->next
= new_handle
;
172 cur_handle
= new_handle
;
173 cur_handle
->id
= cur_handle_id
++;
174 cur_handle
->next
= NULL
;
179 /* Delete a given memory handle from the linked list
180 and return true for success. Nothing is actually erased from memory. */
181 static bool rm_handle(struct memory_handle
*h
)
183 if (h
== first_handle
) {
184 first_handle
= h
->next
;
185 if (h
== cur_handle
) {
186 /* h was the first and last handle: the buffer is now empty */
190 /* update buf_ridx to point to the new first handle */
191 buf_ridx
= (void *)first_handle
- (void *)buffer
;
194 struct memory_handle
*m
= first_handle
;
195 while (m
&& m
->next
!= h
) {
198 if (h
&& m
&& m
->next
== h
) {
200 if (h
== cur_handle
) {
212 /* Return a pointer to the memory handle of given ID.
213 NULL if the handle wasn't found */
214 static struct memory_handle
*find_handle(int handle_id
)
216 /* simple caching because most of the time the requested handle
217 will either be the same as the last, or the one after the last */
220 if (cached_handle_id
== handle_id
&&
221 cached_handle_id
== cached_handle
->id
)
222 return cached_handle
;
223 else if (cached_handle
->next
&& (cached_handle
->next
->id
== handle_id
))
225 /* JD's quick testing showd this block was only entered
226 2/1971 calls to find_handle.
227 8/1971 calls to find_handle resulted in a cache miss */
228 cached_handle
= cached_handle
->next
;
229 cached_handle_id
= handle_id
;
230 return cached_handle
;
234 struct memory_handle
*m
= first_handle
;
235 while (m
&& m
->id
!= handle_id
) {
238 cached_handle_id
= handle_id
;
240 return (m
&& m
->id
== handle_id
) ? m
: NULL
;
243 /* Move a memory handle and data_size of its data of delta.
244 Return a pointer to the new location of the handle.
245 delta is the value of which to move the struct data.
246 data_size is the amount of data to move along with the struct. */
247 static struct memory_handle
*move_handle(struct memory_handle
*h
,
248 size_t *delta
, size_t data_size
)
251 /* aligning backwards would yield a negative result,
252 and moving the handle of such a small amount is a waste
256 /* make sure delta is 32-bit aligned so that the handle struct is. */
257 *delta
= (*delta
- 3) & ~3;
259 size_t newpos
= RINGBUF_ADD((void *)h
- (void *)buffer
, *delta
);
261 struct memory_handle
*dest
= (struct memory_handle
*)(&buffer
[newpos
]);
263 /* Invalidate the cache to prevent it from keeping the old location of h */
264 if (h
== cached_handle
)
265 cached_handle
= NULL
;
267 /* the cur_handle pointer might need updating */
268 if (h
== cur_handle
) {
272 if (h
== first_handle
) {
276 struct memory_handle
*m
= first_handle
;
277 while (m
&& m
->next
!= h
) {
280 if (h
&& m
&& m
->next
== h
) {
287 rb
->memmove(dest
, h
, sizeof(struct memory_handle
) + data_size
);
294 BUFFER SPACE MANAGEMENT
295 =======================
297 buffer_handle : Buffer data for a handle
298 free_buffer : Free buffer space by moving a handle
299 fill_buffer : Call buffer_handle for all handles that have data to buffer
300 data_rem : Total amount of data needing to be buffered
301 wasted_space : Total amount of space available for freeing
303 These functions are used by the buffering thread to manage buffer space.
306 /* Buffer data for the given handle. Return the amount of data buffered
307 or -1 if the handle wasn't found */
308 static ssize_t
buffer_handle(int handle_id
)
310 DEBUGF("buffer_handle(%d)\n", handle_id
);
311 struct memory_handle
*h
= find_handle(handle_id
);
315 if (h
->filerem
== 0) {
316 /* nothing left to buffer */
320 if (h
->fd
< 0) /* file closed, reopen */
323 h
->fd
= rb
->open(h
->path
, O_RDONLY
);
331 rb
->lseek(h
->fd
, h
->offset
, SEEK_SET
);
335 while (h
->filerem
> 0)
337 /* max amount to copy */
338 size_t copy_n
= MIN( MIN(h
->filerem
, conf_filechunk
),
339 buffer_len
- h
->widx
);
341 /* stop copying if it would overwrite the reading position
342 or the next handle */
343 if (RINGBUF_ADD_CROSS(h
->widx
, copy_n
, buf_ridx
) >= 0 || (h
->next
&&
344 RINGBUF_ADD_CROSS(h
->widx
, copy_n
, (unsigned)
345 ((void *)h
->next
- (void *)buffer
)) > 0))
348 /* rc is the actual amount read */
349 int rc
= rb
->read(h
->fd
, &buffer
[h
->widx
], copy_n
);
353 DEBUGF("File ended %ld bytes early\n", (long)h
->filerem
);
354 h
->filesize
-= h
->filerem
;
360 h
->widx
= RINGBUF_ADD(h
->widx
, rc
);
368 if (h
->filerem
== 0) {
369 /* finished buffering the file */
373 DEBUGF("buffered %ld bytes (%ld of %ld available, remaining: %ld)\n",
374 ret
, h
->available
, h
->filesize
, h
->filerem
);
381 /* Free buffer space by moving the handle struct right before the useful
382 part of its data buffer or by moving all the data. */
383 static void free_buffer(int handle_id
)
385 struct memory_handle
*h
= find_handle(handle_id
);
390 /* The value of delta might change for alignment reasons */
392 if (h
->next
&& (h
->type
== TYPE_ID3
|| h
->type
== TYPE_CUESHEET
||
393 h
->type
== TYPE_IMAGE
) && h
->filerem
== 0 )
395 /* metadata handle: we can move all of it */
396 delta
= RINGBUF_SUB( (unsigned)((void *)h
->next
- (void *)buffer
),
397 h
->data
) - h
->available
;
398 h
= move_handle(h
, &delta
, h
->available
);
400 h
->data
= RINGBUF_ADD(h
->data
, delta
);
401 h
->ridx
= RINGBUF_ADD(h
->ridx
, delta
);
402 DEBUGF("free_buffer(%d): metadata, moved by %ld bytes\n",
407 /* only move the handle struct */
408 delta
= RINGBUF_SUB(h
->ridx
, h
->data
);
409 h
= move_handle(h
, &delta
, 0);
411 h
->data
= RINGBUF_ADD(h
->data
, delta
);
412 h
->available
-= delta
;
413 DEBUGF("free_buffer(%d): audio, %ld bytes freed\n", handle_id
, delta
);
417 /* Fill the buffer by buffering as much data as possible for handles that still
418 have data left to buffer */
419 static void fill_buffer(void)
421 DEBUGF("fill buffer()\n");
422 struct memory_handle
*m
= first_handle
;
424 if (m
->filerem
> 0) {
425 buffer_handle(m
->id
);
431 /* Return the total amount of data left to be buffered for all the handles */
432 static size_t data_rem(void)
436 struct memory_handle
*m
= first_handle
;
445 /* Return the amount of data we have but don't need anymore. This data can be
446 safely erased to reclaim buffer space. */
447 static size_t wasted_space(void)
451 struct memory_handle
*m
= first_handle
;
453 ret
+= RINGBUF_SUB(m
->ridx
, m
->data
);
462 BUFFERING API FUNCTIONS
463 =======================
465 bufopen : Request the opening of a new handle for a file
466 bufclose : Close an open handle
467 bufseek : Set the read pointer in a handle
468 bufadvance : Move the read pointer in a handle
469 bufread : Copy data from a handle into a given buffer
470 bufgetdata : Give a pointer to the handle's data
472 These functions are exported, to allow interaction with the buffer.
473 They take care of the content of the structs, and rely on the linked list
474 management functions for all the actual handle management work.
478 /* Request a file be buffered
479 filename: name of the file to open
480 offset:starting offset to buffer from the file
481 return value: <0 if the file cannot be opened, or one file already
482 queued to be opened, otherwise the handle for the file in the buffer
484 int bufopen(char *file
, size_t offset
, enum data_type type
)
486 if (cur_handle
&& cur_handle
->filerem
> 0) {
487 /* the current handle hasn't finished buffering. We can only add
488 a new one if there is already enough free space to finish
490 if (cur_handle
->filerem
< (buffer_len
- BUF_USED
)) {
491 /* Before adding the new handle we reserve some space for the
492 current one to finish buffering its data. */
493 buf_widx
= RINGBUF_ADD(buf_widx
, cur_handle
->filerem
);
499 int fd
= rb
->open(file
, O_RDONLY
);
503 size_t size
= rb
->filesize(fd
) - offset
;
505 DEBUGF("bufopen: %s (offset: %ld) (%ld bytes needed)...\n",
508 struct memory_handle
*h
= add_handle(&size
);
511 DEBUGF("failed to add handle\n");
516 if (offset
) rb
->lseek(fd
, offset
, SEEK_SET
);
517 rb
->strncpy(h
->path
, file
, MAX_PATH
);
519 h
->filesize
= rb
->filesize(fd
);
520 h
->filerem
= h
->filesize
- offset
;
528 DEBUGF("allocated %ld bytes. ID: %d\n", size
, h
->id
);
532 /* Close the handle. Return 0 for success and < 0 for failure */
533 int bufclose(int handle_id
)
535 DEBUGF("bufclose(%d)\n", handle_id
);
536 struct memory_handle
*h
= find_handle(handle_id
);
544 /* Set reading index in handle (relatively to the start of the handle data).
545 Return 0 for success and < 0 for failure */
546 int bufseek(int handle_id
, size_t offset
)
548 struct memory_handle
*h
= find_handle(handle_id
);
552 if (offset
> h
->available
)
555 h
->ridx
= RINGBUF_ADD(h
->data
, offset
);
559 /* Advance the reading index in a handle (relatively to its current position).
560 Return 0 for success and < 0 for failure */
561 int bufadvance(int handle_id
, off_t offset
)
563 struct memory_handle
*h
= find_handle(handle_id
);
569 /* check for access beyond what's available */
570 if ((size_t)offset
> (h
->available
- RINGBUF_SUB(h
->ridx
, h
->data
)))
573 h
->ridx
= RINGBUF_ADD(h
->ridx
, offset
);
577 /* check for access before what's available */
578 if ((size_t)(-offset
) > RINGBUF_SUB(h
->ridx
, h
->data
))
581 h
->ridx
= RINGBUF_SUB(h
->ridx
, (size_t)(-offset
));
587 /* Copy data from the given handle to the dest buffer.
588 Return the number of bytes copied or < 0 for failure. */
589 ssize_t
bufread(int handle_id
, size_t size
, char *dest
)
591 struct memory_handle
*h
= find_handle(handle_id
);
592 size_t buffered_data
;
596 if (h
->available
== 0 && h
->filerem
> 0) /* Data isn't ready */
599 if (h
->available
== 0 && h
->filerem
== 0) /* File is finished reading */
602 buffered_data
= MIN(size
, h
->available
- RINGBUF_SUB(h
->ridx
, h
->data
));
604 if (h
->ridx
+ buffered_data
> buffer_len
)
606 size_t read
= buffer_len
- h
->ridx
;
607 rb
->memcpy(dest
, &buffer
[h
->ridx
], read
);
608 rb
->memcpy(dest
+read
, buffer
, buffered_data
- read
);
610 else rb
->memcpy(dest
, &buffer
[h
->ridx
], buffered_data
);
612 return buffered_data
;
615 /* Update the "data" pointer to make the handle's data available to the caller.
616 Return the length of the available linear data or < 0 for failure.
617 size is the amount of linear data requested. it can be 0 to get as
619 The guard buffer may be used to provide the requested size */
620 ssize_t
bufgetdata(int handle_id
, size_t size
, unsigned char **data
)
622 struct memory_handle
*h
= find_handle(handle_id
);
626 if (h
->available
== 0 && h
->filerem
> 0) /* Data isn't ready */
629 if (h
->available
== 0 && h
->filerem
== 0) /* File is finished reading */
634 if (h
->ridx
+ size
> buffer_len
&&
635 h
->available
- RINGBUF_SUB(h
->ridx
, h
->data
) >= size
)
637 /* use the guard buffer to provide what was requested. */
638 size_t copy_n
= MIN(h
->ridx
+ size
- buffer_len
, GUARD_SIZE
);
639 rb
->memcpy(guard_buffer
, (unsigned char *)buffer
, copy_n
);
640 ret
= buffer_len
- h
->ridx
+ copy_n
;
641 DEBUGF("used the guard buffer to complete\n");
645 ret
= MIN(h
->available
- RINGBUF_SUB(h
->ridx
, h
->data
),
646 buffer_len
- h
->ridx
);
649 *data
= (unsigned char *)&buffer
[h
->ridx
];
651 /* DEBUGF("bufgetdata(%d): h->ridx=%ld, ret=%ld\n", handle_id,
652 (long)h->ridx, ret); */
664 struct memory_handle
*m1
, *m2
, *m3
, *m4
;
666 if (cur_handle
!= NULL
|| first_handle
!= NULL
)
669 m1
= add_handle(NULL
);
671 if (cur_handle
!= m1
|| first_handle
!= m1
|| m1
->next
!= NULL
)
674 m2
= add_handle(NULL
);
676 if (cur_handle
!= m2
|| first_handle
!= m1
|| m1
->next
!= m2
|| m2
->next
!= NULL
)
679 m3
= add_handle(NULL
);
681 if (cur_handle
!= m3
|| first_handle
!= m1
|| m2
->next
!= m3
|| m3
->next
!= NULL
)
686 if (cur_handle
!= m3
|| first_handle
!= m1
|| m1
->next
!= m3
)
691 if (cur_handle
!= m1
|| first_handle
!= m1
|| m1
->next
!= NULL
)
694 m4
= add_handle(NULL
);
696 if (cur_handle
!= m4
|| first_handle
!= m1
|| m1
->next
!= m4
|| m4
->next
!= NULL
)
701 if (cur_handle
!= m4
|| first_handle
!= m4
)
706 if (cur_handle
!= NULL
|| first_handle
!= NULL
)
709 m1
= add_handle(NULL
);
710 m2
= add_handle(NULL
);
711 m3
= add_handle(NULL
);
712 m4
= add_handle(NULL
);
714 if (cur_handle
!= m4
|| first_handle
!= m1
)
717 size_t delta
= 1024*100;
718 m2
= move_handle(m2
, &delta
, 0);
720 if (cur_handle
!= m4
|| first_handle
!= m1
|| m1
->next
!= m2
|| m2
->next
!= m3
)
724 m1
= move_handle(m1
, &delta
, 0);
726 if (cur_handle
!= m4
|| first_handle
!= m1
|| m1
->next
!= m2
)
734 if (cur_handle
!= NULL
|| first_handle
!= NULL
)
740 /* display a nice graphical view of the ringbuffer. */
741 static void graph_view(int width
)
743 #ifndef ROCKBOX_HAS_LOGF
745 r_pos
= buf_ridx
* width
/ buffer_len
;
746 w_pos
= buf_widx
* width
/ buffer_len
;
749 for (i
=0; i
<= width
; i
++)
751 if (i
!= r_pos
&& i
!= w_pos
)
753 if (buf_ridx
<= buf_widx
)
755 if (i
> r_pos
&& i
< w_pos
) {
763 if (i
> r_pos
|| i
< w_pos
) {
772 if (i
== r_pos
&& i
== w_pos
)
774 if (buf_ridx
<= buf_widx
) {
779 } else if (i
== r_pos
) {
781 } else if (i
== w_pos
) {
793 void print_progress(size_t amount
, int file
, int numfiles
)
796 rb
->snprintf(buf
, sizeof(buf
), "file %d of %d", file
, numfiles
);
797 rb
->lcd_puts(0, 0, buf
);
798 rb
->snprintf(buf
, sizeof(buf
), "read: %ld", amount
);
799 rb
->lcd_puts(0, 1, buf
);
802 void print_metadata(int handle_id
)
805 char *artist
, *title
, *newline
;
806 char art
[50], ttl
[50];
807 int artist_len
, title_len
;
810 ret
= bufgetdata(handle_id
, 0, &buf
);
813 newline
= rb
->strchr(artist
, '\n');
814 artist_len
= newline
- artist
;
815 rb
->strncpy(art
, artist
, artist_len
);
819 newline
= rb
->strchr(title
, '\n');
820 title_len
= newline
- title
;
821 rb
->strncpy(ttl
, title
, title_len
);
824 rb
->lcd_puts(0, 3, art
);
825 rb
->lcd_puts(0, 4, ttl
);
828 bool buffer_init(void)
830 buffer
= rb
->plugin_get_audio_buffer(&buffer_len
);
833 DEBUGF("couldn't allocate buffer\n");
836 buffer_len
-= GUARD_SIZE
;
837 guard_buffer
= buffer
+ buffer_len
;
845 conf_filechunk
= AUDIO_DEFAULT_FILECHUNK
;
850 bool disk_is_spinning(void)
861 static long codec_stack
[4*DEFAULT_STACK_SIZE
/sizeof(long)];
862 static struct thread_entry
* codecthread_id
;
864 static long bufopen_stack
[2*DEFAULT_STACK_SIZE
/sizeof(long)];
865 static struct thread_entry
* bufopenthread_id
;
867 bool done_playing
= false;
869 #define MAX_HANDLES 16
871 int handles
[MAX_HANDLES
];
872 int meta_handles
[MAX_HANDLES
];
874 void codec_thread(void)
877 int fd
= -1; /* used to write the files out as they are read */
879 char outfile
[MAX_PATH
];
880 long read
, total
= 0;
886 if (handles
[idx
] > 0) {
888 /* create the output file */
890 rb
->snprintf(outfile
, MAX_PATH
, "/file%d.mp3", idx
);
891 fd
= rb
->open(outfile
, O_CREAT
|O_TRUNC
|O_WRONLY
);
893 DEBUGF("couldn't create file\n");
894 rb
->splash(HZ
, "couldn't create file");
898 /* read as much data as possible */
900 read
= bufgetdata(handles
[idx
], GUARD_SIZE
, &data
);
902 read
= MIN(read
, GUARD_SIZE
);
903 rb
->write(fd
, data
, read
);
905 bufadvance(handles
[idx
], read
);
906 rb
->lcd_clear_display();
907 print_progress(total
, idx
+1, num_files
);
908 print_metadata(meta_handles
[idx
]);
914 if (read
>= 0 && total
>= 0) {
915 /* some data was read */
916 DEBUGF("read %ld bytes from handle %d\n", total
,
920 /* check the return value to determine what exactly happened */
922 DEBUGF("data for handle %d isn't ready\n", handles
[idx
]);
923 } else if (read
== -1) {
924 DEBUGF("couldn't find handle %d\n", handles
[idx
]);
925 } else if (read
== 0) {
926 DEBUGF("finished reading handle %d\n", handles
[idx
]);
927 bufclose(handles
[idx
]);
928 bufclose(meta_handles
[idx
]);
933 /* move on to the next file and check if we've finished */
935 if (idx
>= num_files
) {
946 DEBUGF("removing the codec thread\n");
947 rb
->remove_thread(NULL
);
950 void bufopen_thread(void)
954 while (idx
< num_files
)
956 /* open the metadata file */
957 rb
->snprintf(buf
, MAX_PATH
, "/meta%s.txt", files
[idx
]);
958 meta_handles
[idx
] = bufopen(buf
, 0, TYPE_ID3
);
959 if (meta_handles
[idx
] > 0)
961 /* open the audio file */
962 ret
= bufopen(files
[idx
], 0, TYPE_AUDIO
);
964 handles
[idx
++] = ret
;
966 /* couldn't open the audio file, close the metadata handle */
967 bufclose(meta_handles
[idx
]);
973 DEBUGF("bufopen thread finished\n");
974 rb
->remove_thread(NULL
);
978 /* this is the plugin entry point */
979 enum plugin_status
plugin_start(struct plugin_api
* api
, void* parameter
)
988 DEBUGF("linked list test failed\n");
989 rb
->splash(HZ
, "linked list test failed");
993 codecthread_id
= rb
->create_thread(codec_thread
,
997 IF_PRIO(,PRIORITY_PLAYBACK
)
998 IF_COP(, CPU
, false));
1000 bufopenthread_id
= rb
->create_thread(bufopen_thread
,
1002 sizeof(bufopen_stack
),
1004 IF_PRIO(,PRIORITY_BACKGROUND
)
1005 IF_COP(, CPU
, false));
1007 if (!codecthread_id
)
1009 rb
->splash(HZ
, "failed to create codec thread");
1010 return PLUGIN_ERROR
;
1012 else if (!bufopenthread_id
)
1014 rb
->splash(HZ
, "failed to create bufopen thread");
1015 return PLUGIN_ERROR
;
1020 while (!done_playing
)
1022 if (data_rem() > 0 && wasted_space() > buffer_len
/5) {
1023 DEBUGF("there is %ld bytes of wasted space\n", wasted_space());
1025 /* free buffer from outdated audio data */
1026 struct memory_handle
*m
= first_handle
;
1028 if (m
->type
== TYPE_AUDIO
)
1033 /* free buffer by moving metadata */
1036 if (m
->type
!= TYPE_AUDIO
)
1044 if (data_rem() > 0 && BUF_USED
< 3*buffer_len
/4 &&
1047 DEBUGF("%ld bytes left to buffer and the buffer is low\n",
1054 DEBUGF("done playing\n");
1059 DEBUGF("end of plugin\n");