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 ****************************************************************************/
26 #include "buffering.h"
29 /* Rockbox constants */
31 /* amount of data to read in one read() call */
32 #define AUDIO_DEFAULT_FILECHUNK (1024*32)
35 #define BUFFER_SIZE (32*1024*1024)
36 #define GUARD_SIZE (32*1024)
39 /* Ring buffer helper macros */
40 /* Buffer pointer (p) plus value (v), wrapped if necessary */
41 #define RINGBUF_ADD(p,v) ((p+v)<buffer_len ? p+v : p+v-buffer_len)
42 /* Buffer pointer (p) minus value (v), wrapped if necessary */
43 #define RINGBUF_SUB(p,v) ((p>=v) ? p-v : p+buffer_len-v)
44 /* How far value (v) plus buffer pointer (p1) will cross buffer pointer (p2) */
45 #define RINGBUF_ADD_CROSS(p1,v,p2) \
46 ((p1<p2) ? (int)(p1+v)-(int)p2 : (int)(p1+v-p2)-(int)buffer_len)
47 /* Bytes available in the buffer */
48 #define BUF_USED RINGBUF_SUB(buf_widx, buf_ridx)
51 #define MIN(a, b) (((a)<(b))?(a):(b))
55 static size_t buffer_len
;
57 static char *buffer_end
;
59 static size_t conf_filechunk
;
61 static size_t buf_widx
;
62 static size_t buf_ridx
;
64 /* current memory handle in the linked list. NULL when the list is empty. */
65 static struct memory_handle
*cur_handle
;
66 /* first memory handle in the linked list. NULL when the list is empty. */
67 static struct memory_handle
*first_handle
;
68 static int num_handles
;
71 /* add a new handle to the linked list and return it. It will have become the
73 static struct memory_handle
*add_handle(void)
75 /* this will give each handle a unique id */
76 static int cur_handle_id
= 0;
78 int data_size
= sizeof(struct memory_handle
) /*
79 + 1024*1024 - sizeof(struct memory_handle) */;
81 /* check that we actually can add the handle and its data */
82 if (RINGBUF_ADD_CROSS(buf_widx
, data_size
, buf_ridx
) >= 0) {
86 struct memory_handle
*new_handle
= (struct memory_handle
*)(buffer
+ buf_widx
);
87 buf_widx
= RINGBUF_ADD(buf_widx
, data_size
);
90 /* the new handle is the first one */
91 first_handle
= new_handle
;
95 cur_handle
->next
= new_handle
;
98 cur_handle
= new_handle
;
99 cur_handle
->id
= cur_handle_id
++;
100 cur_handle
->next
= NULL
;
105 /* delete a given memory handle from the linked list
106 and return true for success. Nothing is actually erased from memory. */
107 static bool rm_handle(struct memory_handle
*h
)
109 if (h
== first_handle
) {
110 first_handle
= h
->next
;
111 if (h
== cur_handle
) {
115 struct memory_handle
*m
= first_handle
;
116 while (m
&& m
->next
!= h
) {
119 if (h
&& m
&& m
->next
== h
) {
121 if (h
== cur_handle
) {
132 /* Move a memory handle to newpos */
133 static struct memory_handle
*move_handle(size_t newpos
, struct memory_handle
*h
)
135 struct memory_handle
*dest
= (struct memory_handle
*)(buffer
+ newpos
);
137 if (h
== cur_handle
) {
141 if (h
== first_handle
) {
144 struct memory_handle
*m
= first_handle
;
145 while (m
&& m
->next
!= h
) {
148 if (h
&& m
&& m
->next
== h
) {
154 memmove(dest
, h
, sizeof(struct memory_handle
));
158 /* Return a pointer to the memory handle of given ID.
159 NULL if the handle wasn't found */
160 static struct memory_handle
*find_handle(int handle_id
)
162 static int last_handle_id
= -1;
163 static struct memory_handle
*last_m
= NULL
;
164 struct memory_handle
*m
= first_handle
;
165 /* simple caching because most of the time the requested handle
166 will either be the same as the last, or the one after the last */
170 if (last_handle_id
== handle_id
&& last_handle_id
== last_m
->id
)
172 else if (last_m
->next
&& (last_m
->next
->id
== handle_id
))
174 /* JD's quick testing showd this block was only entered
175 2/1971 calls to find_handle.
176 8/1971 calls to find_handle resulted in a cache miss */
177 last_m
= last_m
->next
;
178 last_handle_id
= handle_id
;
183 while (m
&& m
->id
!= handle_id
) {
186 last_handle_id
= handle_id
;
188 return (m
&& m
->id
== handle_id
) ? m
: NULL
;
191 /* Buffer data for the given handle. Return the amount of data buffered
192 or -1 if the handle wasn't found */
193 static int buffer_handle(int handle_id
)
195 struct memory_handle
*h
= find_handle(handle_id
);
196 //printf("find_handle %d:\nid: %d\npath: %s\n\n", handle_id, h->id, h->path);
200 if (h
->filerem
== 0) {
201 /* nothing left to buffer */
205 if (h
->fd
< 0) /* file closed, reopen */
208 h
->fd
= open(h
->path
, O_RDONLY
);
216 lseek(h
->fd
, h
->offset
, SEEK_SET
);
220 while (h
->filerem
> 0)
222 //printf("h: %d\n", (void *)h - (void *)buffer);
223 //printf("buf_widx: %ld\n", (long)buf_widx);
224 /* max amount to copy */
225 size_t copy_n
= MIN(conf_filechunk
, buffer_len
- buf_widx
);
227 if (RINGBUF_ADD_CROSS(buf_widx
, copy_n
, buf_ridx
) >= 0)
230 /* rc is the actual amount read */
231 int rc
= read(h
->fd
, &buffer
[buf_widx
], copy_n
);
235 printf("failed on fd %d at buf_widx = %ld for handle %d\n", h
->fd
, (long)buf_widx
, h
->id
);
236 printf("File ended %ld bytes early\n", (long)h
->filerem
);
237 h
->filesize
-= h
->filerem
;
243 buf_widx
= RINGBUF_ADD(buf_widx
, rc
);
249 if (h
->filerem
== 0) {
250 /* finished buffering the file */
254 printf("buffered %d bytes (%d of %d available, remaining: %d)\n",
255 ret
, h
->available
, h
->filesize
, h
->filerem
);
259 /* this seems wrong */
260 static void free_buffer(void)
262 size_t delta
= RINGBUF_SUB(first_handle
->buf_idx
, first_handle
->data
);
263 size_t dest
= RINGBUF_ADD((void *)first_handle
- (void *)buffer
, delta
);
264 printf("buf_ridx: %ld, buf_widx: %ld\n", (long)buf_ridx
, (long)buf_widx
);
265 printf("dest: %ld, delta: %ld\n", (long)dest
, (long)delta
);
266 first_handle
= move_handle(dest
, first_handle
);
267 first_handle
->data
= RINGBUF_ADD(first_handle
->data
, delta
);
268 first_handle
->buf_idx
= first_handle
->data
;
272 /* Request a file be buffered
273 filename: name of the file t open
274 offset: starting offset to buffer from the file
275 RETURNS: <0 if the file cannot be opened, or one file already
276 queued to be opened, otherwise the handle for the file in the buffer
278 int bufopen(char *file
, size_t offset
)
280 /* add the file to the buffering queue. */
281 /* for now, we'll assume the queue is always empty, so the handle
282 gets added immediately */
284 printf("bufopen: %s (offset: %d)\n", file
, offset
);
286 int fd
= open(file
, O_RDONLY
);
291 lseek(fd
, offset
, SEEK_SET
);
293 struct memory_handle
*h
= add_handle();
296 strncpy(h
->path
, file
, MAX_PATH
);
298 h
->filesize
= filesize(fd
);
299 h
->filerem
= h
->filesize
- offset
;
301 h
->buf_idx
= buf_widx
;
305 printf("added handle : %d\n", h
->id
);
309 /* Close the handle. Return 0 for success and < 0 for failure */
310 int bufclose(int handle_id
)
312 printf("bufclose: %d\n", handle_id
);
313 struct memory_handle
*h
= find_handle(handle_id
);
317 if (num_handles
> 1) {
319 buf_ridx
= (size_t)first_handle
- (size_t)buffer
;
321 printf("closing the first and last handle\n");
322 /* num_handles == 1, therefore h == first_handle */
330 /* Seek in a handle. Return 0 for success and < 0 for failure */
331 int bufseek(int handle_id
, size_t offset
)
333 struct memory_handle
*h
= find_handle(handle_id
);
337 h
->buf_idx
= RINGBUF_ADD(h
->data
, offset
);
341 /* Copy data from the given handle to the dest buffer.
342 Return the number of bytes copied or -1 for failure. */
343 int bufread(int handle_id
, size_t size
, char *dest
)
345 struct memory_handle
*h
= find_handle(handle_id
);
346 size_t buffered_data
;
349 if (h
->available
== 0)
351 buffered_data
= MIN(size
,h
->available
);
353 if (h
->buf_idx
+ buffered_data
> buffer_len
)
355 size_t read
= buffer_len
- h
->buf_idx
;
356 memcpy(dest
, &buffer
[h
->buf_idx
], read
);
357 memcpy(dest
+read
, buffer
, buffered_data
- read
);
359 else memcpy(dest
, &buffer
[h
->buf_idx
], buffered_data
);
361 h
->buf_idx
= RINGBUF_ADD(h
->buf_idx
, buffered_data
);
362 h
->available
-= buffered_data
;
363 return buffered_data
;
368 struct memory_handle
*m1
, *m2
, *m3
, *m4
;
370 if (cur_handle
!= NULL
|| first_handle
!= NULL
)
375 if (cur_handle
!= m1
|| first_handle
!= m1
|| m1
->next
!= NULL
)
380 if (cur_handle
!= m2
|| first_handle
!= m1
|| m1
->next
!= m2
|| m2
->next
!= NULL
)
385 if (cur_handle
!= m3
|| first_handle
!= m1
|| m2
->next
!= m3
|| m3
->next
!= NULL
)
390 if (cur_handle
!= m3
|| first_handle
!= m1
|| m1
->next
!= m3
)
395 if (cur_handle
!= m1
|| first_handle
!= m1
|| m1
->next
!= NULL
)
400 if (cur_handle
!= m4
|| first_handle
!= m1
|| m1
->next
!= m4
|| m4
->next
!= NULL
)
405 if (cur_handle
!= m4
|| first_handle
!= m4
)
410 if (cur_handle
!= NULL
|| first_handle
!= NULL
)
418 if (cur_handle
!= m4
|| first_handle
!= m1
)
422 m2
= move_handle(m2
->data
+ 1024*1024, m2
);
424 if (cur_handle
!= m4
|| first_handle
!= m1
|| m1
->next
!= m2
|| m2
->next
!= m3
||
425 find_handle(m2id
) != m2
)
429 m1
= move_handle(m1
->data
+ 1024*1024*3, m1
);
431 if (cur_handle
!= m4
|| first_handle
!= m1
|| m1
->next
!= m2
||
432 find_handle(m1id
) != m1
)
440 if (cur_handle
!= NULL
|| first_handle
!= NULL
)
446 static void list_handles(void)
448 struct memory_handle
*m
= first_handle
;
450 printf("%02d - %d\n", m
->id
, (void *)m
-(void *)buffer
);
455 /* display a nice graphical view of the ringbuffer. */
456 static void graph_view(int width
)
459 r_pos
= buf_ridx
* width
/ buffer_len
;
460 w_pos
= buf_widx
* width
/ buffer_len
;
463 for (i
=0; i
<= width
; i
++)
465 if (i
!= r_pos
&& i
!= w_pos
)
467 if (buf_ridx
<= buf_widx
)
469 if (i
> r_pos
&& i
< w_pos
)
476 if (i
> r_pos
|| i
< w_pos
)
484 if (i
== r_pos
&& i
== w_pos
)
486 if (buf_ridx
<= buf_widx
)
501 void buffer_init(void)
503 buffer
= (char *)malloc(sizeof(char) * (BUFFER_SIZE
+ GUARD_SIZE
));
506 printf("couldn't allocate buffer\n");
509 buffer_len
= BUFFER_SIZE
;
510 buffer_end
= buffer
+ BUFFER_SIZE
;
518 conf_filechunk
= AUDIO_DEFAULT_FILECHUNK
;
521 /* returns true if the file still has some on disk unread */
522 bool handle_has_data(int handle
)
524 struct memory_handle
*m
= find_handle(handle
);
527 return m
->filerem
!= 0;
532 bool need_rebuffer(void)
535 free
= BUFFER_SIZE
- BUF_USED
;
536 return ((free
>= BUFFER_SIZE
/4));
539 #define MAX_HANDLES 64
540 int main(int argc
, char *argv
[])
543 int last_handle
= -1;
544 int handle_order
[MAX_HANDLES
];
545 int reading_handle
= 0;
547 char read_buffer
[GUARD_SIZE
];
548 int fd
= -1; /* used to write the files out as they are read */
549 struct memory_handle
*m
= NULL
;
554 printf("linked list test failed\n");
560 while (done
== false)
562 printf("buffer usage: %d handles_used: %d\n", BUF_USED
,num_handles
);
565 /* "Buffering thread" section */
566 if ((next_file
<= argc
) && need_rebuffer())
568 if ( !m
|| ((m
->filerem
== 0) && (m
->next
== NULL
)))
570 int h
= bufopen(argv
[next_file
++], 0);
574 printf("new handle %d\n",h
);
576 handle_order
[last_handle
] = h
;
577 buffer_handle(m
->id
);
584 printf("buffering handle %d\n",m
->id
);
585 buffer_handle(m
->id
);
588 /* "Playback thread" section */
591 printf("reading handle: %d\n", handle_order
[reading_handle
]);
595 if (reading_handle
>= last_handle
596 && !handle_has_data(handle_order
[reading_handle
]))
601 snprintf(file
, MAX_PATH
, "./file%d.mp3", reading_handle
);
602 fd
= open(file
, O_CREAT
|O_TRUNC
|O_WRONLY
, 0770);
605 printf("ERROROROROR\n");
611 read
= bufread(handle_order
[reading_handle
], GUARD_SIZE
,read_buffer
);
612 write(fd
, read_buffer
, read
);
617 printf("read %ld bytes from handle %d\n", total
, handle_order
[reading_handle
]);
619 /* close the fd/handle if there is no more data or an error */
620 if (read
< 0 || handle_has_data(handle_order
[reading_handle
]) == false)
622 printf("finished reading %d\n",handle_order
[reading_handle
]);
623 bufclose(handle_order
[reading_handle
]);
630 printf("there is data left to buffer for %d\n", handle_order
[reading_handle
]);
636 printf("buffer usage: %d handles_used: %d\n", BUF_USED
,num_handles
);