All pastes #604479 Raw Edit

Miscellany

public text v1 · immutable
#604479 ·published 2007-07-05 15:36 UTC
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;
}