/*************************************************************************** * __________ __ ___. * Open \______ \ ____ ____ | | _\_ |__ _______ ___ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * \/ \/ \/ \/ \/ * $Id$ * * Copyright (C) 2007 Nicolas Pennequin * * All files in this archive are subject to the GNU General Public License. * See the file COPYING in the source tree root for full license agreement. * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY * KIND, either express or implied. * ****************************************************************************/ #include "plugin.h" PLUGIN_HEADER struct plugin_api* rb; #define GUARD_SIZE (32*1024) /* amount of data to read in one read() call */ #define AUDIO_DEFAULT_FILECHUNK (1024*32) /* Ring buffer helper macros */ /* Buffer pointer (p) plus value (v), wrapped if necessary */ #define RINGBUF_ADD(p,v) ((p+v)=v) ? p-v : p+buffer_len-v) /* How far value (v) plus buffer pointer (p1) will cross buffer pointer (p2) */ #define RINGBUF_ADD_CROSS(p1,v,p2) \ ((p1logf("add_handle"); /* this will give each handle a unique id */ static int cur_handle_id = 0; int data_size = sizeof(struct memory_handle); /* check that we actually can add the handle and its data */ if (RINGBUF_ADD_CROSS(buf_widx, data_size, buf_ridx) >= 0) { return NULL; } struct memory_handle *new_handle = (struct memory_handle *)(buffer + buf_widx); buf_widx = RINGBUF_ADD(buf_widx, data_size); if (!first_handle) { /* the new handle is the first one */ first_handle = new_handle; } if (cur_handle) { cur_handle->next = new_handle; } cur_handle = new_handle; rb->logf("cur_handle_id: %d", cur_handle_id); cur_handle->id = cur_handle_id; rb->logf("cur_handle->id: %d", cur_handle->id); cur_handle->next = NULL; num_handles++; cur_handle_id++; rb->logf("cur_handle: %lx", (unsigned long)cur_handle); rb->logf("cur_handle->id: %d", cur_handle->id); return cur_handle; } /* delete a given memory handle from the linked list and return true for success. Nothing is actually erased from memory. */ static bool rm_handle(struct memory_handle *h) { if (h == first_handle) { first_handle = h->next; if (h == cur_handle) { rb->logf("removing the first and last handle\n"); cur_handle = NULL; buf_ridx = buf_widx; } else { buf_ridx = (void *)first_handle - (void *)buffer; } } else { struct memory_handle *m = first_handle; while (m && m->next != h) { m = m->next; } if (h && m && m->next == h) { m->next = h->next; if (h == cur_handle) { cur_handle = m; } } else { return false; } } num_handles--; return true; } /* these are unfortunalty needed to be global so move_handle can invalidate them */ static int cached_handle_id = -1; static struct memory_handle *cached_handle = NULL; /* Return a pointer to the memory handle of given ID. NULL if the handle wasn't found */ static struct memory_handle *find_handle(int handle_id) { /* simple caching because most of the time the requested handle will either be the same as the last, or the one after the last */ if (cached_handle) { if (cached_handle_id == handle_id && cached_handle_id == cached_handle->id) return cached_handle; else if (cached_handle->next && (cached_handle->next->id == handle_id)) { /* JD's quick testing showd this block was only entered 2/1971 calls to find_handle. 8/1971 calls to find_handle resulted in a cache miss */ cached_handle = cached_handle->next; cached_handle_id = handle_id; return cached_handle; } } struct memory_handle *m = first_handle; while (m && m->id != handle_id) { m = m->next; } cached_handle_id = handle_id; cached_handle = m; return (m && m->id == handle_id) ? m : NULL; } /* Move a memory handle to newpos. Return a pointer to the new location of the handle */ static struct memory_handle *move_handle(size_t newpos, struct memory_handle *h) { struct memory_handle *dest = (struct memory_handle *)(buffer + newpos); /* Invalidate the cache to prevent it from keeping the old location of h */ if (h == cached_handle) cached_handle = NULL; /* the cur_handle pointer might need updating */ if (h == cur_handle) { cur_handle = dest; } if (h == first_handle) { first_handle = dest; buf_ridx = newpos; } else { struct memory_handle *m = first_handle; while (m && m->next != h) { m = m->next; } if (h && m && m->next == h) { m->next = dest; } else { return NULL; } } rb->memmove(dest, h, sizeof(struct memory_handle)); return dest; } /* Buffer data for the given handle. Return the amount of data buffered or -1 if the handle wasn't found */ static int buffer_handle(int handle_id) { rb->logf("buffer_handle(%d)", handle_id); struct memory_handle *h = find_handle(handle_id); if (!h) return -1; if (h->filerem == 0) { /* nothing left to buffer */ return 0; } if (h->fd < 0) /* file closed, reopen */ { if (*h->path) h->fd = rb->open(h->path, O_RDONLY); else return -1; if (h->fd < 0) return -1; if (h->offset) rb->lseek(h->fd, h->offset, SEEK_SET); } int ret = 0; while (h->filerem > 0) { //rb->logf("h: %d\n", (void *)h - (void *)buffer); //rb->logf("buf_widx: %ld\n", (long)buf_widx); /* max amount to copy */ size_t copy_n = MIN(conf_filechunk, buffer_len - buf_widx); /* stop copying if it would overwrite the reading position */ if (RINGBUF_ADD_CROSS(buf_widx, copy_n, buf_ridx) >= 0) break; /* rc is the actual amount read */ int rc = rb->read(h->fd, &buffer[buf_widx], copy_n); if (rc < 0) { //rb->logf("failed on fd %d at buf_widx = %ld for handle %d\n", h->fd, (long)buf_widx, h->id); rb->logf("File ended %ld bytes early\n", (long)h->filerem); h->filesize -= h->filerem; h->filerem = 0; break; } /* Advance buffer */ h->widx = RINGBUF_ADD(h->widx, rc); if (h == cur_handle) buf_widx = h->widx; h->available += rc; ret += rc; h->filerem -= rc; } if (h->filerem == 0) { /* finished buffering the file */ rb->close(h->fd); } rb->logf("buffered %d bytes (%d of %d available, remaining: %d)\n", ret, h->available, h->filesize, h->filerem); return ret; } /* Free buffer space by moving the first handle struct right before the useful part of its data buffer */ static void free_buffer(int handle_id) { rb->logf("free_buffer(%d)\n", handle_id); struct memory_handle *h = find_handle(handle_id); if (!h) return; size_t delta = RINGBUF_SUB(h->ridx, h->data); size_t dest = RINGBUF_ADD((void *)h - (void *)buffer, delta); //rb->logf("buf_ridx: %ld, buf_widx: %ld\n", (long)buf_ridx, (long)buf_widx); //rb->logf("dest: %ld, delta: %ld\n", (long)dest, (long)delta); h = move_handle(dest, h); h->data = RINGBUF_ADD(h->data, delta); h->ridx = h->data; h->available -= delta; //graph_view(100); } /* Request a file be buffered filename: name of the file t open offset: starting offset to buffer from the file RETURNS: <0 if the file cannot be opened, or one file already queued to be opened, otherwise the handle for the file in the buffer */ int bufopen(char *file, size_t offset) { /* add the file to the buffering queue. */ /* for now, we'll assume the queue is always empty, so the handle gets added immediately */ rb->logf("bufopen: %s (offset: %d)\n", file, offset); int fd = rb->open(file, O_RDONLY); if (fd < 0) return -1; if (offset) rb->lseek(fd, offset, SEEK_SET); struct memory_handle *h = add_handle(); if (!h) return -1; rb->strncpy(h->path, file, MAX_PATH); h->fd = fd; h->filesize = rb->filesize(fd); h->filerem = h->filesize - offset; h->offset = offset; h->ridx = buf_widx; h->widx = buf_widx; h->data = buf_widx; h->available = 0; rb->logf("h: %lx", (unsigned long)h); rb->logf("h->id: %d", h->id); rb->logf("added handle : %d\n", h->id); return h->id; } /* Close the handle. Return 0 for success and < 0 for failure */ int bufclose(int handle_id) { rb->logf("bufclose(%d)\n", handle_id); struct memory_handle *h = find_handle(handle_id); if (!h) return -1; rm_handle(h); return 0; } /* Set the reading index in a handle (relatively to the start of the handle data). Return 0 for success and < 0 for failure */ int bufseek(int handle_id, size_t offset) { struct memory_handle *h = find_handle(handle_id); if (!h) return -1; if (offset > h->available) return -2; h->ridx = RINGBUF_ADD(h->data, offset); return 0; } /* Advance the reading index in a handle (relatively to its current position). Return 0 for success and < 0 for failure */ int bufadvance(int handle_id, ssize_t offset) { struct memory_handle *h = find_handle(handle_id); if (!h) return -1; if (offset >= 0) { if (offset > h->available - RINGBUF_SUB(h->ridx, h->data)) return -2; h->ridx = RINGBUF_ADD(h->ridx, offset); } else { if (-offset > RINGBUF_SUB(h->ridx, h->data)) return -2; h->ridx = RINGBUF_SUB(h->ridx, -offset); } return 0; } /* Copy data from the given handle to the dest buffer. Return the number of bytes copied or -1 for failure. */ int bufread(int handle_id, size_t size, char *dest) { struct memory_handle *h = find_handle(handle_id); size_t buffered_data; if (!h) return -1; if (h->available == 0) return 0; buffered_data = MIN(size, h->available); if (h->ridx + buffered_data > buffer_len) { size_t read = buffer_len - h->ridx; rb->memcpy(dest, &buffer[h->ridx], read); rb->memcpy(dest+read, buffer, buffered_data - read); } else rb->memcpy(dest, &buffer[h->ridx], buffered_data); h->ridx = RINGBUF_ADD(h->ridx, buffered_data); h->available -= buffered_data; return buffered_data; } /* Update the "data" pointer to make the handle's data available to the caller. Return the length of the available linear data or -1 for failure. */ long bufgetdata(int handle_id, size_t size, unsigned char **data) { struct memory_handle *h = find_handle(handle_id); if (!h) return -1; long ret; if (h->ridx + size > buffer_len && h->available - RINGBUF_SUB(h->ridx, h->data) >= size) { /* use the guard buffer to provide what was requested. */ int copy_n = h->ridx + size - buffer_len; rb->memcpy(guard_buffer, (unsigned char *)buffer, copy_n); ret = size; } else { ret = MIN(h->available - RINGBUF_SUB(h->ridx, h->data), buffer_len - h->ridx); } *data = (unsigned char *)(buffer + h->ridx); //rb->logf("bufgetdata(%d): h->ridx=%ld, ret=%ld\n", handle_id, (long)h->ridx, ret); return ret; } bool test_ll(void) { struct memory_handle *m1, *m2, *m3, *m4; if (cur_handle != NULL || first_handle != NULL) return false; m1 = add_handle(); if (cur_handle != m1 || first_handle != m1 || m1->next != NULL) return false; m2 = add_handle(); if (cur_handle != m2 || first_handle != m1 || m1->next != m2 || m2->next != NULL) return false; m3 = add_handle(); if (cur_handle != m3 || first_handle != m1 || m2->next != m3 || m3->next != NULL) return false; rm_handle(m2); if (cur_handle != m3 || first_handle != m1 || m1->next != m3) return false; rm_handle(m3); if (cur_handle != m1 || first_handle != m1 || m1->next != NULL) return false; m4 = add_handle(); if (cur_handle != m4 || first_handle != m1 || m1->next != m4 || m4->next != NULL) return false; rm_handle(m1); if (cur_handle != m4 || first_handle != m4) return false; rm_handle(m4); if (cur_handle != NULL || first_handle != NULL) return false; m1 = add_handle(); m2 = add_handle(); m3 = add_handle(); m4 = add_handle(); if (cur_handle != m4 || first_handle != m1) return false; /* int m2id = m2->id; m2 = move_handle(m2->data + 1024*1024, m2); if (cur_handle != m4 || first_handle != m1 || m1->next != m2 || m2->next != m3 || find_handle(m2id) != m2) return false; int m1id = m1->id; m1 = move_handle(m1->data + 1024*1024*3, m1); if (cur_handle != m4 || first_handle != m1 || m1->next != m2 || find_handle(m1id) != m1) return false;*/ rm_handle(m1); rm_handle(m2); rm_handle(m3); rm_handle(m4); if (cur_handle != NULL || first_handle != NULL) return false; return true; } #if 0 static void list_handles(void) { struct memory_handle *m = first_handle; while (m) { rb->logf("%02d - %d\n", m->id, (void *)m-(void *)buffer); m = m->next; } } #endif /* display a nice graphical view of the ringbuffer. */ static void graph_view(int width) { int i, r_pos, w_pos; r_pos = buf_ridx * width / buffer_len; w_pos = buf_widx * width / buffer_len; rb->logf("|"); for (i=0; i <= width; i++) { if (i != r_pos && i != w_pos) { if (buf_ridx <= buf_widx) { if (i > r_pos && i < w_pos) { rb->logf(">"); } else { rb->logf("-"); } } else { if (i > r_pos || i < w_pos) { rb->logf(">"); } else { rb->logf("-"); } } } else { if (i == r_pos && i == w_pos) { if (buf_ridx <= buf_widx) { rb->logf("RW"); } else { rb->logf("WR"); } } else if (i == r_pos) { rb->logf("R"); } else if (i == w_pos) { rb->logf("W"); } } } rb->logf("|"); rb->logf("\n"); } bool buffer_init(void) { buffer = rb->plugin_get_audio_buffer(&buffer_len); if (!buffer) { rb->logf("couldn't allocate buffer\n"); return false; } buffer_len -= GUARD_SIZE; guard_buffer = buffer + buffer_len; buf_widx = 0; buf_ridx = 0; first_handle = NULL; num_handles = 0; conf_filechunk = AUDIO_DEFAULT_FILECHUNK; return true; } /* returns true if the file still has some on disk unread */ bool handle_has_data(int handle) { struct memory_handle *m = find_handle(handle); if (m) { return m->filerem != 0; } return false; } bool need_rebuffer(void) { size_t free, wasted; wasted = first_handle? RINGBUF_SUB(first_handle->ridx, first_handle->data) : 0; while (wasted > buffer_len / 2) { free_buffer(first_handle->id); wasted = first_handle? RINGBUF_SUB(first_handle->ridx, first_handle->data) : 0; } free = buffer_len - BUF_USED; return ((free >= buffer_len/4)); } bool disk_is_spinning(void) { return true; } #define MAX_HANDLES 64 /* this is the plugin entry point */ enum plugin_status plugin_start(struct plugin_api* api, void* parameter) { (void)parameter; rb = api; int argc = 6; char *argv[] = { NULL, "/070623 - David Vendetta.mp3", "/africanism all stars feat the hard boys - hard (bob sinclair remix).mp3", "/alan_braxe__kris_menace_-_lumberjack.mp3", "/Ame - Rej (A Hundred Birds Remix).mp3", "/Antena - Camino del Sol (Joakim Remix).mp3" }; int next_file = 1; int last_handle = -1; int handle_order[MAX_HANDLES]; int reading_handle = 0; bool done_buffering = false, done_playing = false; //char read_buffer[GUARD_SIZE]; unsigned char *data; int fd = -1; /* used to write the files out as they are read */ int current_handle = -1; struct memory_handle *m = NULL; buffer_init(); if (!test_ll()) { rb->logf("linked list test failed\n"); rb->splash(HZ, "linked list test failed"); return PLUGIN_ERROR; } buffer_init(); rb->logf("test\n"); while (!done_buffering || !done_playing) { rb->logf("buffer usage: %d handles_used: %d\n", BUF_USED, num_handles); //graph_view(100); /* "Buffering thread" section */ if (!done_buffering && need_rebuffer() && disk_is_spinning()) { m = find_handle(current_handle); if ( !m || ((m->filerem == 0) && (m->next == NULL))) { int h = bufopen(argv[next_file], 0); m = find_handle(h); if (h >= 0) { next_file++; //rb->logf("new handle %d\n",h); last_handle++; handle_order[last_handle] = h; buffer_handle(m->id); } else { rb->logf("error\n"); } current_handle = h; } else { if (m->filerem == 0) { m = m->next; current_handle = m?m->id:-1; } if (m) { rb->logf("buffering handle %d\n",m->id); buffer_handle(m->id); } } if (next_file == argc && m->filerem == 0) done_buffering = true; } /* "Playback thread" section */ else { rb->logf("reading handle: %d\n", handle_order[reading_handle]); long read; long total = 0; char file[MAX_PATH]; if (reading_handle >= last_handle && !handle_has_data(handle_order[reading_handle])) done_playing = true; if (fd < 0) { rb->snprintf(file, MAX_PATH, "/file%d.mp3", reading_handle); fd = rb->open(file, O_CREAT|O_TRUNC|O_WRONLY); if (fd < 0) { rb->logf("ERROROROROR\n"); rb->splash(HZ, "couldn't create file"); return PLUGIN_ERROR; } } do { //read = bufread(handle_order[reading_handle], GUARD_SIZE,read_buffer); //write(fd, read_buffer, read); read = bufgetdata(handle_order[reading_handle], GUARD_SIZE, &data); read = MIN(read, GUARD_SIZE); rb->write(fd, data, read); total += read; bufadvance(handle_order[reading_handle], read); } while (read > 0); rb->logf("read %ld bytes from handle %d\n", total, handle_order[reading_handle]); /* close the fd/handle if there is no more data or an error */ if (read < 0 || handle_has_data(handle_order[reading_handle]) == false) { rb->logf("finished reading %d\n",handle_order[reading_handle]); bufclose(handle_order[reading_handle]); rb->close(fd); reading_handle++; fd = -1; } else { rb->logf("there is data left to buffer for %d\n", handle_order[reading_handle]); } } } rb->logf("buffer usage: %d handles_used: %d\n", BUF_USED,num_handles); //graph_view(100); return 0; return PLUGIN_OK; }