rendered paste body/***************************************************************************
* __________ __ ___.
* 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)<buffer_len ? p+v : p+v-buffer_len)
/* Buffer pointer (p) minus value (v), wrapped if necessary */
#define RINGBUF_SUB(p,v) ((p>=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) \
((p1<p2) ? (int)(p1+v)-(int)p2 : (int)(p1+v-p2)-(int)buffer_len)
/* Bytes available in the buffer */
#define BUF_USED RINGBUF_SUB(buf_widx, buf_ridx)
struct memory_handle {
int id; /* A unique ID for the handle */
//enum data_type type;
//enum data_state state;
char path[MAX_PATH];
int fd;
size_t data; /* Start index of the handle's data buffer */
size_t ridx; /* Current read pointer, relative to the main buffer */
size_t widx; /* Current write pointer */
size_t filesize; /* File total length */
size_t filerem; /* Remaining bytes of file NOT in buffer */
size_t available; /* Available bytes to read from buffer */
size_t offset; /* Offset at which we started reading the file */
struct memory_handle *next;
};
static char *buffer;
static char *guard_buffer;
static size_t buffer_len;
static size_t buf_widx;
static size_t buf_ridx;
static size_t conf_filechunk;
/* current memory handle in the linked list. NULL when the list is empty. */
static struct memory_handle *cur_handle;
/* first memory handle in the linked list. NULL when the list is empty. */
static struct memory_handle *first_handle;
static int num_handles;
static void graph_view(int width);
/* add a new handle to the linked list and return it. It will have become the
new current handle */
static struct memory_handle *add_handle(void)
{
rb->logf("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;
}