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 */
169 if (last_handle_id
== handle_id
)
171 else if (last_m
->next
&& (last_m
->next
->id
== handle_id
))
173 /* JD's quick testing showd this block was only entered
174 2/1971 calls to find_handle.
175 8/1971 calls to find_handle resulted in a cache miss */
176 last_m
= last_m
->next
;
177 last_handle_id
= handle_id
;
181 while (m
&& m
->id
!= handle_id
) {
184 last_handle_id
= handle_id
;
186 return (m
&& m
->id
== handle_id
) ? m
: NULL
;
189 /* Buffer data for the given handle. Return the amount of data buffered
190 or -1 if the handle wasn't found */
191 static int buffer_handle(int handle_id
)
193 struct memory_handle
*h
= find_handle(handle_id
);
194 //printf("find_handle %d:\nid: %d\npath: %s\n\n", handle_id, h->id, h->path);
198 if (h
->filerem
== 0) {
199 /* nothing left to buffer */
203 if (h
->fd
< 0) /* file closed, reopen */
206 h
->fd
= open(h
->path
, O_RDONLY
);
214 lseek(h
->fd
, h
->offset
, SEEK_SET
);
218 while (h
->filerem
> 0)
220 /* max amount to copy */
221 size_t copy_n
= MIN(conf_filechunk
, buffer_len
- buf_widx
);
223 if (RINGBUF_ADD_CROSS(buf_widx
, copy_n
, buf_ridx
) >= 0)
226 /* rc is the actual amount read */
227 int rc
= read(h
->fd
, &buffer
[buf_widx
], copy_n
);
231 printf("File ended %ldB early", (long)h
->filerem
);
232 h
->filesize
-= h
->filerem
;
238 buf_widx
= RINGBUF_ADD(buf_widx
, rc
);
244 if (h
->filerem
== 0) {
245 /* finished buffering the file */
249 printf("buffered %d bytes (%d of %d available, remaining: %d)\n",
250 ret
, h
->available
, h
->filesize
, h
->filerem
);
254 /* Request a file be buffered
255 filename: name of the file t open
256 offset: starting offset to buffer from the file
257 RETURNS: <0 if the file cannot be opened, or one file already
258 queued to be opened, otherwise the handle for the file in the buffer
260 int bufopen(char *file
, size_t offset
)
262 /* add the file to the buffering queue. */
263 /* for now, we'll assume the queue is always empty, so the handle
264 gets added immediately */
266 printf("bufopen: %s (offset: %d)\n", file
, offset
);
268 int fd
= open(file
, O_RDONLY
);
273 lseek(fd
, offset
, SEEK_SET
);
275 struct memory_handle
*h
= add_handle();
278 strncpy(h
->path
, file
, MAX_PATH
);
280 h
->filesize
= filesize(fd
);
281 h
->filerem
= h
->filesize
- offset
;
283 h
->buf_idx
= buf_widx
;
287 printf("added handle : %d\n", h
->id
);
291 /* Close the handle. Return 0 for success and < 0 for failure */
292 int bufclose(int handle_id
)
294 printf("bufclose: %d\n", handle_id
);
295 struct memory_handle
*h
= find_handle(handle_id
);
299 /* when we close the first handle, we can reclaim the space from its buffer */
300 if (h
== first_handle
) {
301 buf_ridx
= RINGBUF_ADD(h
->buf_idx
, h
->available
);
304 return rm_handle(h
) ? 0 : -1;
307 /* Seek in a handle. Return 0 for success and < 0 for failure */
308 int bufseek(int handle_id
, size_t offset
)
310 struct memory_handle
*h
= find_handle(handle_id
);
314 h
->buf_idx
= RINGBUF_ADD(h
->data
, offset
);
318 /* Copy data from the given handle to the dest buffer.
319 Return the number of bytes copied or -1 for failure. */
320 int bufread(int handle_id
, size_t size
, char *dest
)
322 struct memory_handle
*h
= find_handle(handle_id
);
323 size_t buffered_data
;
326 if (h
->available
== 0)
328 buffered_data
= MIN(size
,h
->available
);
330 if (h
->buf_idx
+ buffered_data
> buffer_len
)
332 size_t read
= buffer_len
- h
->buf_idx
;
333 memcpy(dest
, &buffer
[h
->buf_idx
], read
);
334 memcpy(dest
+read
, buffer
, buffered_data
- read
);
336 else memcpy(dest
, &buffer
[h
->buf_idx
], buffered_data
);
337 h
->buf_idx
= RINGBUF_ADD(h
->buf_idx
, buffered_data
);
338 h
->available
-= buffered_data
;
339 return buffered_data
;
344 struct memory_handle
*m1
, *m2
, *m3
, *m4
;
346 if (cur_handle
!= NULL
|| first_handle
!= NULL
)
351 if (cur_handle
!= m1
|| first_handle
!= m1
|| m1
->next
!= NULL
)
356 if (cur_handle
!= m2
|| first_handle
!= m1
|| m1
->next
!= m2
|| m2
->next
!= NULL
)
361 if (cur_handle
!= m3
|| first_handle
!= m1
|| m2
->next
!= m3
|| m3
->next
!= NULL
)
366 if (cur_handle
!= m3
|| first_handle
!= m1
|| m1
->next
!= m3
)
371 if (cur_handle
!= m1
|| first_handle
!= m1
|| m1
->next
!= NULL
)
376 if (cur_handle
!= m4
|| first_handle
!= m1
|| m1
->next
!= m4
|| m4
->next
!= NULL
)
381 if (cur_handle
!= m4
|| first_handle
!= m4
)
386 if (cur_handle
!= NULL
|| first_handle
!= NULL
)
394 if (cur_handle
!= m4
|| first_handle
!= m1
)
397 m2
= move_handle(m2
->data
+ 1024*1024, m2
);
399 if (cur_handle
!= m4
|| first_handle
!= m1
|| m1
->next
!= m2
|| m2
->next
!= m3
)
402 m1
= move_handle(m1
->data
+ 1024*1024*3, m1
);
404 if (cur_handle
!= m4
|| first_handle
!= m1
|| m1
->next
!= m2
)
412 if (cur_handle
!= NULL
|| first_handle
!= NULL
)
418 static void list_handles(void)
420 struct memory_handle
*m
= first_handle
;
422 printf("%02d - %d\n", m
->id
, (void *)m
-(void *)buffer
);
427 void buffer_init(void)
429 buffer
= (char *)malloc(sizeof(char) * (BUFFER_SIZE
+ GUARD_SIZE
));
432 printf("couldn't allocate buffer\n");
435 buffer_len
= BUFFER_SIZE
;
436 buffer_end
= buffer
+ BUFFER_SIZE
;
444 conf_filechunk
= AUDIO_DEFAULT_FILECHUNK
;
447 /* returns true if the file still has some on disk unread */
448 bool handle_has_data(int handle
)
450 struct memory_handle
*m
= find_handle(handle
);
453 return m
->filerem
!= 0;
458 bool need_rebuffer(void)
461 free
= BUFFER_SIZE
- BUF_USED
;
462 return ((free
>= BUFFER_SIZE
/4));
465 #define MAX_HANDLES 64
466 int main(int argc
, char *argv
[])
469 int last_handle
= -1;
470 int handle_order
[MAX_HANDLES
];
471 int reading_handle
= 0;
473 char read_buffer
[GUARD_SIZE
];
474 int fd
= -1; /* used to write the files out as they are read */
475 struct memory_handle
*m
= NULL
;
480 printf("linked list test failed\n");
486 while (done
== false)
488 /* "Buffering thread" section */
489 if ((next_file
<= argc
) && need_rebuffer())
491 printf("buffer usage: %d handles_used: %d\n", BUF_USED
,num_handles
);
492 if ( !m
|| ((m
->filerem
== 0) && (m
->next
== NULL
)))
494 int h
= bufopen(argv
[next_file
++], 0);
498 printf("new handle %d\n",h
);
500 handle_order
[last_handle
] = h
;
501 buffer_handle(m
->id
);
508 printf("buffering handle %d\n",m
->id
);
509 buffer_handle(m
->id
);
512 /* "Playback thread" section */
515 printf("reading handle: %d\n", handle_order
[reading_handle
]);
518 if (reading_handle
>= last_handle
)
523 snprintf(file
, MAX_PATH
, "./file%d.mp3", reading_handle
);
524 fd
= open(file
, O_CREAT
|O_TRUNC
|O_WRONLY
, 0770);
527 printf("ERROROROROR\n");
533 read
= bufread(handle_order
[reading_handle
], GUARD_SIZE
,read_buffer
);
534 write(fd
, read_buffer
, read
);
537 /* close the fd/handle if there is no more data or an error */
538 if (read
< 0 || handle_has_data(handle_order
[reading_handle
]) == false)
540 printf("finished reading %d\n",handle_order
[reading_handle
]);
541 bufclose(handle_order
[reading_handle
]);