A few minor changes to buffering.c and add the plugin version.
[Rockbox-MoB.git] / plugin / buffering.c
blob12c0ebd8244630e9012f5ccb78d8023ac8843854
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
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 ****************************************************************************/
21 #include "plugin.h"
23 PLUGIN_HEADER
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 struct memory_handle {
44 int id; /* A unique ID for the handle */
45 //enum data_type type;
46 //enum data_state state;
47 char path[MAX_PATH];
48 int fd;
49 size_t data; /* Start index of the handle's data buffer */
50 size_t ridx; /* Current read pointer, relative to the main buffer */
51 size_t widx; /* Current write pointer */
52 size_t filesize; /* File total length */
53 size_t filerem; /* Remaining bytes of file NOT in buffer */
54 size_t available; /* Available bytes to read from buffer */
55 size_t offset; /* Offset at which we started reading the file */
56 struct memory_handle *next;
59 static char *buffer;
60 static char *guard_buffer;
62 static size_t buffer_len;
63 static size_t buf_widx;
64 static size_t buf_ridx;
66 static size_t conf_filechunk;
68 /* current memory handle in the linked list. NULL when the list is empty. */
69 static struct memory_handle *cur_handle;
70 /* first memory handle in the linked list. NULL when the list is empty. */
71 static struct memory_handle *first_handle;
72 static int num_handles;
74 static void graph_view(int width);
77 /* add a new handle to the linked list and return it. It will have become the
78 new current handle */
79 static struct memory_handle *add_handle(void)
81 /* this will give each handle a unique id */
82 static int cur_handle_id = 0;
84 int data_size = sizeof(struct memory_handle);
86 /* check that we actually can add the handle and its data */
87 if (RINGBUF_ADD_CROSS(buf_widx, data_size, buf_ridx) >= 0) {
88 return NULL;
91 struct memory_handle *new_handle = (struct memory_handle *)(buffer + buf_widx);
92 buf_widx = RINGBUF_ADD(buf_widx, data_size);
94 if (!first_handle) {
95 /* the new handle is the first one */
96 first_handle = new_handle;
99 if (cur_handle) {
100 cur_handle->next = new_handle;
103 cur_handle = new_handle;
104 cur_handle->id = cur_handle_id++;
105 cur_handle->next = NULL;
106 num_handles++;
107 return cur_handle;
110 /* delete a given memory handle from the linked list
111 and return true for success. Nothing is actually erased from memory. */
112 static bool rm_handle(struct memory_handle *h)
114 if (h == first_handle) {
115 first_handle = h->next;
116 if (h == cur_handle) {
117 rb->logf("removing the first and last handle\n");
118 cur_handle = NULL;
119 buf_ridx = buf_widx;
120 } else {
121 buf_ridx = (void *)first_handle - (void *)buffer;
123 } else {
124 struct memory_handle *m = first_handle;
125 while (m && m->next != h) {
126 m = m->next;
128 if (h && m && m->next == h) {
129 m->next = h->next;
130 if (h == cur_handle) {
131 cur_handle = m;
133 } else {
134 return false;
138 num_handles--;
139 return true;
142 /* these are unfortunalty needed to be global
143 so move_handle can invalidate them */
144 static int cached_handle_id = -1;
145 static struct memory_handle *cached_handle = NULL;
147 /* Return a pointer to the memory handle of given ID.
148 NULL if the handle wasn't found */
149 static struct memory_handle *find_handle(int handle_id)
151 /* simple caching because most of the time the requested handle
152 will either be the same as the last, or the one after the last */
153 if (cached_handle)
155 if (cached_handle_id == handle_id && cached_handle_id == cached_handle->id)
156 return cached_handle;
157 else if (cached_handle->next && (cached_handle->next->id == handle_id))
159 /* JD's quick testing showd this block was only entered
160 2/1971 calls to find_handle.
161 8/1971 calls to find_handle resulted in a cache miss */
162 cached_handle = cached_handle->next;
163 cached_handle_id = handle_id;
164 return cached_handle;
168 struct memory_handle *m = first_handle;
169 while (m && m->id != handle_id) {
170 m = m->next;
172 cached_handle_id = handle_id;
173 cached_handle = m;
174 return (m && m->id == handle_id) ? m : NULL;
177 /* Move a memory handle to newpos.
178 Return a pointer to the new location of the handle */
179 static struct memory_handle *move_handle(size_t newpos, struct memory_handle *h)
181 struct memory_handle *dest = (struct memory_handle *)(buffer + newpos);
183 /* Invalidate the cache to prevent it from keeping the old location of h */
184 if (h == cached_handle)
185 cached_handle = NULL;
187 /* the cur_handle pointer might need updating */
188 if (h == cur_handle) {
189 cur_handle = dest;
192 if (h == first_handle) {
193 first_handle = dest;
194 buf_ridx = newpos;
195 } else {
196 struct memory_handle *m = first_handle;
197 while (m && m->next != h) {
198 m = m->next;
200 if (h && m && m->next == h) {
201 m->next = dest;
202 } else {
203 return NULL;
206 rb->memmove(dest, h, sizeof(struct memory_handle));
207 return dest;
209 /* Buffer data for the given handle. Return the amount of data buffered
210 or -1 if the handle wasn't found */
211 static int buffer_handle(int handle_id)
213 rb->logf("buffer_handle(%d)", handle_id);
214 struct memory_handle *h = find_handle(handle_id);
215 if (!h)
216 return -1;
218 if (h->filerem == 0) {
219 /* nothing left to buffer */
220 return 0;
223 if (h->fd < 0) /* file closed, reopen */
225 if (*h->path)
226 h->fd = rb->open(h->path, O_RDONLY);
227 else
228 return -1;
230 if (h->fd < 0)
231 return -1;
233 if (h->offset)
234 rb->lseek(h->fd, h->offset, SEEK_SET);
237 int ret = 0;
238 while (h->filerem > 0)
240 //rb->logf("h: %d\n", (void *)h - (void *)buffer);
241 //rb->logf("buf_widx: %ld\n", (long)buf_widx);
242 /* max amount to copy */
243 size_t copy_n = MIN(conf_filechunk, buffer_len - buf_widx);
245 /* stop copying if it would overwrite the reading position */
246 if (RINGBUF_ADD_CROSS(buf_widx, copy_n, buf_ridx) >= 0)
247 break;
249 /* rc is the actual amount read */
250 int rc = rb->read(h->fd, &buffer[buf_widx], copy_n);
252 if (rc < 0)
254 //rb->logf("failed on fd %d at buf_widx = %ld for handle %d\n", h->fd, (long)buf_widx, h->id);
255 rb->logf("File ended %ld bytes early\n", (long)h->filerem);
256 h->filesize -= h->filerem;
257 h->filerem = 0;
258 break;
261 /* Advance buffer */
262 h->widx = RINGBUF_ADD(h->widx, rc);
263 if (h == cur_handle)
264 buf_widx = h->widx;
265 h->available += rc;
266 ret += rc;
267 h->filerem -= rc;
270 if (h->filerem == 0) {
271 /* finished buffering the file */
272 rb->close(h->fd);
275 rb->logf("buffered %d bytes (%d of %d available, remaining: %d)\n",
276 ret, h->available, h->filesize, h->filerem);
277 return ret;
280 /* Free buffer space by moving the first handle struct right before the useful
281 part of its data buffer */
282 static void free_buffer(int handle_id)
284 rb->logf("free_buffer(%d)\n", handle_id);
285 struct memory_handle *h = find_handle(handle_id);
286 if (!h)
287 return;
289 size_t delta = RINGBUF_SUB(h->ridx, h->data);
290 size_t dest = RINGBUF_ADD((void *)h - (void *)buffer, delta);
291 //rb->logf("buf_ridx: %ld, buf_widx: %ld\n", (long)buf_ridx, (long)buf_widx);
292 //rb->logf("dest: %ld, delta: %ld\n", (long)dest, (long)delta);
293 h = move_handle(dest, h);
294 h->data = RINGBUF_ADD(h->data, delta);
295 h->ridx = h->data;
296 h->available -= delta;
297 //graph_view(100);
300 /* Request a file be buffered
301 filename: name of the file t open
302 offset: starting offset to buffer from the file
303 RETURNS: <0 if the file cannot be opened, or one file already
304 queued to be opened, otherwise the handle for the file in the buffer
306 int bufopen(char *file, size_t offset)
308 /* add the file to the buffering queue. */
309 /* for now, we'll assume the queue is always empty, so the handle
310 gets added immediately */
312 rb->logf("bufopen: %s (offset: %d)\n", file, offset);
314 int fd = rb->open(file, O_RDONLY);
315 if (fd < 0)
316 return -1;
318 if (offset)
319 rb->lseek(fd, offset, SEEK_SET);
321 struct memory_handle *h = add_handle();
322 if (!h)
323 return -1;
324 rb->strncpy(h->path, file, MAX_PATH);
325 h->fd = fd;
326 h->filesize = rb->filesize(fd);
327 h->filerem = h->filesize - offset;
328 h->offset = offset;
329 h->ridx = buf_widx;
330 h->widx = buf_widx;
331 h->data = buf_widx;
332 h->available = 0;
334 rb->logf("added handle : %d\n", h->id);
335 return h->id;
338 /* Close the handle. Return 0 for success and < 0 for failure */
339 int bufclose(int handle_id)
341 rb->logf("bufclose(%d)\n", handle_id);
342 struct memory_handle *h = find_handle(handle_id);
343 if (!h)
344 return -1;
346 rm_handle(h);
347 return 0;
350 /* Set the reading index in a handle (relatively to the start of the handle data).
351 Return 0 for success and < 0 for failure */
352 int bufseek(int handle_id, size_t offset)
354 struct memory_handle *h = find_handle(handle_id);
355 if (!h)
356 return -1;
358 if (offset > h->available)
359 return -2;
361 h->ridx = RINGBUF_ADD(h->data, offset);
362 return 0;
365 /* Advance the reading index in a handle (relatively to its current position).
366 Return 0 for success and < 0 for failure */
367 int bufadvance(int handle_id, ssize_t offset)
369 struct memory_handle *h = find_handle(handle_id);
370 if (!h)
371 return -1;
373 if (offset >= 0)
375 if (offset > h->available - RINGBUF_SUB(h->ridx, h->data))
376 return -2;
378 h->ridx = RINGBUF_ADD(h->ridx, offset);
380 else
382 if (-offset > RINGBUF_SUB(h->ridx, h->data))
383 return -2;
385 h->ridx = RINGBUF_SUB(h->ridx, -offset);
388 return 0;
391 /* Copy data from the given handle to the dest buffer.
392 Return the number of bytes copied or -1 for failure. */
393 int bufread(int handle_id, size_t size, char *dest)
395 struct memory_handle *h = find_handle(handle_id);
396 size_t buffered_data;
397 if (!h)
398 return -1;
399 if (h->available == 0)
400 return 0;
401 buffered_data = MIN(size, h->available);
403 if (h->ridx + buffered_data > buffer_len)
405 size_t read = buffer_len - h->ridx;
406 rb->memcpy(dest, &buffer[h->ridx], read);
407 rb->memcpy(dest+read, buffer, buffered_data - read);
409 else rb->memcpy(dest, &buffer[h->ridx], buffered_data);
411 h->ridx = RINGBUF_ADD(h->ridx, buffered_data);
412 h->available -= buffered_data;
413 return buffered_data;
416 /* Update the "data" pointer to make the handle's data available to the caller.
417 Return the length of the available linear data or -1 for failure. */
418 long bufgetdata(int handle_id, size_t size, unsigned char **data)
420 struct memory_handle *h = find_handle(handle_id);
421 if (!h)
422 return -1;
424 long ret;
426 if (h->ridx + size > buffer_len &&
427 h->available - RINGBUF_SUB(h->ridx, h->data) >= size)
429 /* use the guard buffer to provide what was requested. */
430 int copy_n = h->ridx + size - buffer_len;
431 rb->memcpy(guard_buffer, (unsigned char *)buffer, copy_n);
432 ret = size;
434 else
436 ret = MIN(h->available - RINGBUF_SUB(h->ridx, h->data),
437 buffer_len - h->ridx);
440 *data = (unsigned char *)(buffer + h->ridx);
442 //rb->logf("bufgetdata(%d): h->ridx=%ld, ret=%ld\n", handle_id, (long)h->ridx, ret);
443 return ret;
446 bool test_ll(void)
448 struct memory_handle *m1, *m2, *m3, *m4;
450 if (cur_handle != NULL || first_handle != NULL)
451 return false;
453 m1 = add_handle();
455 if (cur_handle != m1 || first_handle != m1 || m1->next != NULL)
456 return false;
458 m2 = add_handle();
460 if (cur_handle != m2 || first_handle != m1 || m1->next != m2 || m2->next != NULL)
461 return false;
463 m3 = add_handle();
465 if (cur_handle != m3 || first_handle != m1 || m2->next != m3 || m3->next != NULL)
466 return false;
468 rm_handle(m2);
470 if (cur_handle != m3 || first_handle != m1 || m1->next != m3)
471 return false;
473 rm_handle(m3);
475 if (cur_handle != m1 || first_handle != m1 || m1->next != NULL)
476 return false;
478 m4 = add_handle();
480 if (cur_handle != m4 || first_handle != m1 || m1->next != m4 || m4->next != NULL)
481 return false;
483 rm_handle(m1);
485 if (cur_handle != m4 || first_handle != m4)
486 return false;
488 rm_handle(m4);
490 if (cur_handle != NULL || first_handle != NULL)
491 return false;
493 m1 = add_handle();
494 m2 = add_handle();
495 m3 = add_handle();
496 m4 = add_handle();
498 if (cur_handle != m4 || first_handle != m1)
499 return false;
501 int m2id = m2->id;
502 m2 = move_handle(m2->data + 1024*1024, m2);
504 if (cur_handle != m4 || first_handle != m1 || m1->next != m2 || m2->next != m3 ||
505 find_handle(m2id) != m2)
506 return false;
508 int m1id = m1->id;
509 m1 = move_handle(m1->data + 1024*1024*3, m1);
511 if (cur_handle != m4 || first_handle != m1 || m1->next != m2 ||
512 find_handle(m1id) != m1)
513 return false;*/
515 rm_handle(m1);
516 rm_handle(m2);
517 rm_handle(m3);
518 rm_handle(m4);
520 if (cur_handle != NULL || first_handle != NULL)
521 return false;
523 return true;
526 #if 0
527 static void list_handles(void)
529 struct memory_handle *m = first_handle;
530 while (m) {
531 rb->logf("%02d - %d\n", m->id, (void *)m-(void *)buffer);
532 m = m->next;
535 #endif
537 /* display a nice graphical view of the ringbuffer. */
538 static void graph_view(int width)
540 int i, r_pos, w_pos;
541 r_pos = buf_ridx * width / buffer_len;
542 w_pos = buf_widx * width / buffer_len;
544 rb->logf("|");
545 for (i=0; i <= width; i++)
547 if (i != r_pos && i != w_pos)
549 if (buf_ridx <= buf_widx)
551 if (i > r_pos && i < w_pos) {
552 rb->logf(">");
553 } else {
554 rb->logf("-");
557 else
559 if (i > r_pos || i < w_pos) {
560 rb->logf(">");
561 } else {
562 rb->logf("-");
566 else
568 if (i == r_pos && i == w_pos)
570 if (buf_ridx <= buf_widx) {
571 rb->logf("RW");
572 } else {
573 rb->logf("WR");
575 } else if (i == r_pos) {
576 rb->logf("R");
577 } else if (i == w_pos) {
578 rb->logf("W");
582 rb->logf("|");
583 rb->logf("\n");
586 bool buffer_init(void)
588 buffer = rb->plugin_get_audio_buffer(&buffer_len);
589 if (!buffer)
591 rb->logf("couldn't allocate buffer\n");
592 return false;
594 buffer_len -= GUARD_SIZE;
595 guard_buffer = buffer + buffer_len;
597 buf_widx = 0;
598 buf_ridx = 0;
600 first_handle = NULL;
601 num_handles = 0;
603 conf_filechunk = AUDIO_DEFAULT_FILECHUNK;
605 return true;
608 /* returns true if the file still has some on disk unread */
609 bool handle_has_data(int handle)
611 struct memory_handle *m = find_handle(handle);
612 if (m)
614 return m->filerem != 0;
616 return false;
619 bool need_rebuffer(void)
621 size_t free, wasted;
622 wasted = first_handle? RINGBUF_SUB(first_handle->ridx, first_handle->data) : 0;
623 while (wasted > buffer_len / 2)
625 free_buffer(first_handle->id);
626 wasted = first_handle? RINGBUF_SUB(first_handle->ridx, first_handle->data) : 0;
628 free = buffer_len - BUF_USED;
629 return ((free >= buffer_len/4));
632 bool disk_is_spinning(void)
634 return true;
637 #define MAX_HANDLES 64
639 /* this is the plugin entry point */
640 enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
642 (void)parameter;
643 rb = api;
645 int argc = 6;
646 char *argv[] = { NULL,
647 "/070623 - David Vendetta.mp3",
648 "/africanism all stars feat the hard boys - hard (bob sinclair remix).mp3",
649 "/alan_braxe__kris_menace_-_lumberjack.mp3",
650 "/Ame - Rej (A Hundred Birds Remix).mp3",
651 "/Antena - Camino del Sol (Joakim Remix).mp3" };
653 int next_file = 1;
654 int last_handle = -1;
655 int handle_order[MAX_HANDLES];
656 int reading_handle = 0;
657 bool done_buffering = false, done_playing = false;
658 //char read_buffer[GUARD_SIZE];
659 unsigned char *data;
660 int fd = -1; /* used to write the files out as they are read */
661 int current_handle = -1;
662 struct memory_handle *m = NULL;
663 buffer_init();
665 if (!test_ll())
667 rb->logf("linked list test failed\n");
668 rb->splash(HZ, "linked list test failed");
669 return PLUGIN_ERROR;
672 buffer_init();
674 rb->logf("test\n");
676 while (!done_buffering || !done_playing)
678 rb->logf("buffer usage: %d handles_used: %d\n", BUF_USED, num_handles);
679 //graph_view(100);
681 /* "Buffering thread" section */
682 if (!done_buffering && need_rebuffer() && disk_is_spinning())
684 m = find_handle(current_handle);
685 if ( !m || ((m->filerem == 0) && (m->next == NULL)))
687 int h = bufopen(argv[next_file], 0);
688 m = find_handle(h);
689 if (h >= 0)
691 next_file++;
692 //rb->logf("new handle %d\n",h);
693 last_handle++;
694 handle_order[last_handle] = h;
695 buffer_handle(m->id);
697 else
699 rb->logf("error\n");
701 current_handle = h;
703 else
705 if (m->filerem == 0)
707 m = m->next;
708 current_handle = m?m->id:-1;
710 if (m)
712 rb->logf("buffering handle %d\n",m->id);
713 buffer_handle(m->id);
717 if (next_file == argc && m->filerem == 0)
718 done_buffering = true;
720 /* "Playback thread" section */
721 else
723 rb->logf("reading handle: %d\n", handle_order[reading_handle]);
724 long read;
725 long total = 0;
726 char file[MAX_PATH];
727 if (reading_handle >= last_handle
728 && !handle_has_data(handle_order[reading_handle]))
729 done_playing = true;
731 if (fd < 0)
733 rb->snprintf(file, MAX_PATH, "/file%d.mp3", reading_handle);
734 fd = rb->open(file, O_CREAT|O_TRUNC|O_WRONLY);
735 if (fd < 0)
737 rb->logf("ERROROROROR\n");
738 rb->splash(HZ, "couldn't create file");
739 return PLUGIN_ERROR;
744 //read = bufread(handle_order[reading_handle], GUARD_SIZE,read_buffer);
745 //write(fd, read_buffer, read);
746 read = bufgetdata(handle_order[reading_handle], GUARD_SIZE, &data);
747 read = MIN(read, GUARD_SIZE);
748 rb->write(fd, data, read);
749 total += read;
750 bufadvance(handle_order[reading_handle], read);
752 while (read > 0);
754 rb->logf("read %ld bytes from handle %d\n", total, handle_order[reading_handle]);
756 /* close the fd/handle if there is no more data or an error */
757 if (read < 0 || handle_has_data(handle_order[reading_handle]) == false)
759 rb->logf("finished reading %d\n",handle_order[reading_handle]);
760 bufclose(handle_order[reading_handle]);
761 rb->close(fd);
762 reading_handle++;
763 fd = -1;
765 else
767 rb->logf("there is data left to buffer for %d\n", handle_order[reading_handle]);
772 rb->logf("buffer usage: %d handles_used: %d\n", BUF_USED,num_handles);
773 //graph_view(100);
775 return 0;
777 return PLUGIN_OK;