First commit of buffering.[ch], based on testplugin.cmaster
authorNicolas Pennequin <nicolas.pennequin@free.fr>
Thu, 16 Aug 2007 13:54:14 +0000 (16 15:54 +0200)
committerNicolas Pennequin <nicolas.pennequin@free.fr>
Thu, 16 Aug 2007 13:54:14 +0000 (16 15:54 +0200)
These are based on testplugin.c, with the "rb->" API calls replaced by
regular calls. The utility and threading code are removed, except for
buffering_thread(), which is still highly experimental.

buffering.c [new file with mode: 0644]
buffering.h

diff --git a/buffering.c b/buffering.c
new file mode 100644 (file)
index 0000000..7971002
--- /dev/null
@@ -0,0 +1,808 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   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 "config.h"
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include "buffering.h"
+
+#include "ata.h"
+#include "system.h"
+#include "thread.h"
+#include "file.h"
+#include "panic.h"
+#include "memory.h"
+#include "lcd.h"
+#include "font.h"
+#include "button.h"
+#include "kernel.h"
+#include "tree.h"
+#include "debug.h"
+#include "sprintf.h"
+#include "settings.h"
+#include "codecs.h"
+#include "audio.h"
+#include "logf.h"
+#include "mp3_playback.h"
+#include "usb.h"
+#include "status.h"
+#include "screens.h"
+#include "playlist.h"
+#include "playback.h"
+#include "pcmbuf.h"
+#include "buffer.h"
+
+#ifdef SIMULATOR
+#define ata_disk_is_active() 1
+#endif
+
+#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;
+    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;
+};
+/* at all times, we have: filesize == offset + available + filerem */
+
+
+static char *buffer;
+static char *guard_buffer;
+
+static size_t buffer_len;
+
+static size_t buf_widx;  /* current writing position */
+static size_t buf_ridx;  /* current reading position */
+/* buf_*idx are values relative to the buffer, not real pointers. */
+
+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;  /* number of handles in the list */
+
+/* Handle cache (makes find_handle faster).
+   These need to be global so that move_handle can invalidate them. */
+static int cached_handle_id = -1;
+static struct memory_handle *cached_handle = NULL;
+
+
+/*
+LINKED LIST MANAGEMENT
+======================
+
+add_handle  : Add a handle to the list
+rm_handle   : Remove a handle from the list
+find_handle : Get a handle pointer from an ID
+move_handle : Move a handle in the buffer (with or without its data)
+
+These functions only handle the linked list structure. They don't touch the
+contents of the struct memory_handle headers. They also change the buf_*idx
+pointers when necessary and manage the handle IDs.
+
+The first and current (== last) handle are kept track of.
+A new handle is added at buf_widx and becomes the current one.
+buf_widx always points to the current writing position for the current handle
+buf_ridx always points to the location of the first handle.
+buf_ridx == buf_widx means the buffer is empty.
+*/
+
+
+/* Add a new handle to the linked list and return it. It will have become the
+   new current handle. The handle will reserve "data_size" bytes or if that's
+   not possible, decrease "data_size" to allow adding the handle. */
+static struct memory_handle *add_handle(size_t *data_size)
+{
+    /* this will give each handle a unique id */
+    static int cur_handle_id = 1;
+
+    /* make sure buf_widx is 32-bit aligned so that the handle struct is,
+       but before that we check we can actually align. */
+    if (RINGBUF_ADD_CROSS(buf_widx, 3, buf_ridx) >= 0) {
+        return NULL;
+    }
+    buf_widx = (RINGBUF_ADD(buf_widx, 3)) & ~3;
+
+    size_t len = (data_size ? *data_size : 0)
+                 + sizeof(struct memory_handle);
+
+    /* check that we actually can add the handle and its data */
+    int overlap = RINGBUF_ADD_CROSS(buf_widx, len, buf_ridx);
+    if (overlap >= 0) {
+        *data_size -= overlap;
+        len -= overlap;
+    }
+    if (len < sizeof(struct memory_handle)) {
+        /* There isn't even enough space to write the struct */
+        return NULL;
+    }
+
+    struct memory_handle *new_handle =
+        (struct memory_handle *)(&buffer[buf_widx]);
+
+    /* only advance the buffer write index of the size of the struct */
+    buf_widx = RINGBUF_ADD(buf_widx, sizeof(struct memory_handle));
+
+    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;
+    cur_handle->id = cur_handle_id++;
+    cur_handle->next = NULL;
+    num_handles++;
+    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) {
+            /* h was the first and last handle: the buffer is now empty */
+            cur_handle = NULL;
+            buf_ridx = buf_widx;
+        } else {
+            /* update buf_ridx to point to the new first handle */
+            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;
+}
+
+/* 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 and data_size of its data of delta.
+   Return a pointer to the new location of the handle.
+   delta is the value of which to move the struct data.
+   data_size is the amount of data to move along with the struct. */
+static struct memory_handle *move_handle(struct memory_handle *h,
+                                         size_t *delta, size_t data_size)
+{
+    if (*delta < 4) {
+        /* aligning backwards would yield a negative result,
+           and moving the handle of such a small amount is a waste
+           of time anyway. */
+        return NULL;
+    }
+    /* make sure delta is 32-bit aligned so that the handle struct is. */
+    *delta = (*delta - 3) & ~3;
+
+    size_t newpos = RINGBUF_ADD((void *)h - (void *)buffer, *delta);
+
+    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;
+        }
+    }
+
+    memmove(dest, h, sizeof(struct memory_handle) + data_size);
+
+    return dest;
+}
+
+
+/*
+BUFFER SPACE MANAGEMENT
+=======================
+
+buffer_handle   : Buffer data for a handle
+free_buffer     : Free buffer space by moving a handle
+fill_buffer     : Call buffer_handle for all handles that have data to buffer
+can_add_handle  : Indicate whether it's safe to add a handle.
+data_rem        : Total amount of data needing to be buffered
+wasted_space    : Total amount of space available for freeing
+
+These functions are used by the buffering thread to manage buffer space.
+*/
+
+/* Buffer data for the given handle. Return the amount of data buffered
+   or -1 if the handle wasn't found */
+static ssize_t buffer_handle(int handle_id)
+{
+    DEBUGF("buffer_handle(%d)\n", 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 = open(h->path, O_RDONLY);
+        else
+            return -1;
+
+        if (h->fd < 0)
+            return -1;
+
+        if (h->offset)
+            lseek(h->fd, h->offset, SEEK_SET);
+    }
+
+    ssize_t ret = 0;
+    while (h->filerem > 0)
+    {
+        /* max amount to copy */
+        size_t copy_n = MIN( MIN(h->filerem, conf_filechunk),
+                             buffer_len - h->widx);
+
+        /* stop copying if it would overwrite the reading position
+           or the next handle */
+        if (RINGBUF_ADD_CROSS(h->widx, copy_n, buf_ridx) >= 0 || (h->next &&
+            RINGBUF_ADD_CROSS(h->widx, copy_n, (unsigned)
+                              ((void *)h->next - (void *)buffer)) > 0))
+            break;
+
+        /* rc is the actual amount read */
+        int rc = read(h->fd, &buffer[h->widx], copy_n);
+
+        if (rc < 0)
+        {
+            DEBUGF("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 */
+        close(h->fd);
+        h->fd = -1;
+    }
+
+    DEBUGF("buffered %ld bytes (%ld of %ld available, rem: %ld, off: %ld)\n",
+           ret, h->available, h->filesize, h->filerem, h->offset);
+
+    return ret;
+}
+
+/* Free buffer space by moving the handle struct right before the useful
+   part of its data buffer or by moving all the data. */
+static void free_buffer(int handle_id)
+{
+    struct memory_handle *h = find_handle(handle_id);
+    if (!h)
+        return;
+
+    size_t delta;
+    /* The value of delta might change for alignment reasons */
+
+    if (h->next && (h->type == TYPE_ID3 || h->type == TYPE_CUESHEET ||
+                    h->type == TYPE_IMAGE) && h->filerem == 0 )
+    {
+        /* metadata handle: we can move all of it */
+        delta = RINGBUF_SUB( (unsigned)((void *)h->next - (void *)buffer),
+                             h->data) - h->available;
+        h = move_handle(h, &delta, h->available);
+        if (!h) return;
+        h->data = RINGBUF_ADD(h->data, delta);
+        h->ridx = RINGBUF_ADD(h->ridx, delta);
+        h->widx = RINGBUF_ADD(h->widx, delta);
+
+        /* when moving a struct mp3entry we need to readjust its pointers. */
+        if (h->type == TYPE_ID3 && h->filesize == sizeof(struct mp3entry)) {
+            adjust_mp3entry((struct mp3entry *)&buffer[h->data],
+                            (void *)&buffer[h->data],
+                            (void *)&buffer[RINGBUF_SUB(h->data, delta)]);
+        }
+
+        DEBUGF("free_buffer(%d): metadata, moved by %ld bytes\n",
+               handle_id, delta);
+    }
+    else
+    {
+        /* only move the handle struct */
+        delta = RINGBUF_SUB(h->ridx, h->data);
+        h = move_handle(h, &delta, 0);
+        if (!h) return;
+        h->data = RINGBUF_ADD(h->data, delta);
+        h->available -= delta;
+        h->offset += delta;
+        DEBUGF("free_buffer(%d): audio, %ld bytes freed\n", handle_id, delta);
+    }
+}
+
+/* Fill the buffer by buffering as much data as possible for handles that still
+   have data left to buffer */
+static void fill_buffer(void)
+{
+    DEBUGF("fill buffer()\n");
+    struct memory_handle *m = first_handle;
+    while (m) {
+        if (m->filerem > 0) {
+            buffer_handle(m->id);
+        }
+        m = m->next;
+    }
+}
+
+/* Check whether it's safe to add a new handle and reserve space to let the
+   current one finish buffering its data. Used by bufopen and bufgetdata as
+   a preliminary check before even trying to physically add the handle.
+   Returns true if it's ok to add a new handle, false if not.
+*/
+static bool can_add_handle(void)
+{
+    if (cur_handle && cur_handle->filerem > 0) {
+        /* the current handle hasn't finished buffering. We can only add
+           a new one if there is already enough free space to finish
+           the buffering. */
+        if (cur_handle->filerem < (buffer_len - BUF_USED)) {
+            /* Before adding the new handle we reserve some space for the
+               current one to finish buffering its data. */
+            buf_widx = RINGBUF_ADD(buf_widx, cur_handle->filerem);
+        } else {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+/* Return the total amount of data left to be buffered for all the handles */
+static size_t data_rem(void)
+{
+    size_t ret = 0;
+
+    struct memory_handle *m = first_handle;
+    while (m) {
+        ret += m->filerem;
+        m = m->next;
+    }
+
+    return ret;
+}
+
+/* Return the amount of data we have but don't need anymore. This data can be
+   safely erased to reclaim buffer space. */
+static size_t wasted_space(void)
+{
+    size_t ret = 0;
+
+    struct memory_handle *m = first_handle;
+    while (m) {
+        ret += RINGBUF_SUB(m->ridx, m->data);
+        m = m->next;
+    }
+
+    return ret;
+}
+
+
+/*
+BUFFERING API FUNCTIONS
+=======================
+
+bufopen     : Request the opening of a new handle for a file
+bufalloc    : Open a new handle for data other than a file.
+bufclose    : Close an open handle
+bufseek     : Set the read pointer in a handle
+bufadvance  : Move the read pointer in a handle
+bufread     : Copy data from a handle into a given buffer
+bufgetdata  : Give a pointer to the handle's data
+
+These functions are exported, to allow interaction with the buffer.
+They take care of the content of the structs, and rely on the linked list
+management functions for all the actual handle management work.
+*/
+
+
+/* Request a file be buffered
+   filename: name of the file to open
+   offset: offset at which to start buffering the file, useful when the first
+           (offset-1) bytes of the file aren't needed.
+   return value: <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, enum data_type type)
+{
+    if (!can_add_handle())
+        return -2;
+
+    int fd = open(file, O_RDONLY);
+    if (fd < 0)
+        return -1;
+
+    size_t size = filesize(fd) - offset;
+
+    if (type != TYPE_AUDIO &&
+        size + sizeof(struct memory_handle) > buffer_len - buf_widx)
+    {
+        /* for types other than audio, the data can't wrap */
+        return -2;
+    }
+
+    DEBUGF("bufopen: %s (offset: %ld) (%ld bytes needed)...\n",
+           file, offset, size);
+
+    struct memory_handle *h = add_handle(&size);
+    if (!h)
+    {
+        DEBUGF("failed to add handle\n");
+        close(fd);
+        return -2;
+    }
+
+    if (offset) lseek(fd, offset, SEEK_SET);
+    strncpy(h->path, file, MAX_PATH);
+    h->fd = fd;
+    h->filesize = 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;
+    h->type = type;
+
+    DEBUGF("allocated %ld bytes. ID: %d\n", size, h->id);
+    return h->id;
+}
+
+/* Open a new handle from data that isn't in a file.
+   src is the source buffer from which to copy data. It can be NULL to simply
+   reserve buffer space.
+   size is the requested size. The call will only be successful if the
+   requested amount of data can entirely fit in the buffer without wrapping.
+   Return value is the handle id for success or <0 for failure.
+*/
+int bufalloc(void *src, size_t size, enum data_type type)
+{
+    if (!can_add_handle())
+        return -2;
+
+    if (size + sizeof(struct memory_handle) > buffer_len - buf_widx)
+        /* The data would need to wrap. */
+        return -2;
+
+    size_t allocsize = size;
+    struct memory_handle *h = add_handle(&allocsize);
+
+    if (!h || allocsize != size)
+        return -2;
+
+    if (src) {
+        if (type == TYPE_ID3 && size == sizeof(struct mp3entry)) {
+            /* specially take care of struct mp3entry */
+            copy_mp3entry((struct mp3entry *)&buffer[buf_widx],
+                          (struct mp3entry *)src);
+        } else {
+            memcpy(&buffer[buf_widx], src, size);
+        }
+    }
+
+    h->fd = -1;
+    *h->path = 0;
+    h->filesize = size;
+    h->filerem = 0;
+    h->offset = 0;
+    h->ridx = buf_widx;
+    h->widx = buf_widx;
+    h->data = buf_widx;
+    h->available = size;
+    h->type = type;
+
+    buf_widx = RINGBUF_ADD(buf_widx, size);
+
+    return h->id;
+}
+
+/* Close the handle. Return 0 for success and < 0 for failure */
+int bufclose(int handle_id)
+{
+    DEBUGF("bufclose(%d)\n", handle_id);
+    struct memory_handle *h = find_handle(handle_id);
+    if (!h)
+        return -1;
+
+    rm_handle(h);
+    return 0;
+}
+
+/* Set reading index in handle (relatively to the start of the file).
+   Access before the available data will trigger a rebuffer.
+   TODO: Test this
+   TODO: Maybe force an immediate rebuffer by calling buffer_handle() ?
+   Return 0 for success and < 0 for failure:
+     -1 if the handle wasn't found
+     -2 if there is no data available at the new position
+        (the reading index is still moved)
+     -3 if the new requested position was beyond the end of the file
+*/
+int bufseek(int handle_id, size_t newpos)
+{
+    int ret = 0;
+    struct memory_handle *h = find_handle(handle_id);
+    if (!h)
+        return -1;
+
+    if (newpos > h->filesize) {
+        /* access beyond the end of the file */
+        return -3;
+    }
+
+    else if (newpos < h->offset) {
+        /* access before what we currently have. A rebuffer is needed. */
+        h->offset = newpos;
+        h->available = 0;
+        h->filerem = h->filesize - newpos;
+        /* having changed filerem should be enough to trigger the rebuffer. */
+        h->widx = h->data;
+        ret = -2;
+    }
+
+    else if (newpos > h->offset + h->available) {
+        /* data isn't available yet. */
+        ret = -2;
+    }
+
+    h->ridx = RINGBUF_ADD(h->data, newpos);
+    return ret;
+}
+
+/* Advance the reading index in a handle (relatively to its current position).
+   Return 0 for success and < 0 for failure
+   TODO: Add some rebuffering like in bufseek */
+int bufadvance(int handle_id, off_t offset)
+{
+    struct memory_handle *h = find_handle(handle_id);
+    if (!h)
+        return -1;
+
+    if (offset >= 0)
+    {
+        /* check for access beyond what's available */
+        if ((size_t)offset > (h->available - RINGBUF_SUB(h->ridx, h->data)))
+            return -2;
+
+        h->ridx = RINGBUF_ADD(h->ridx, offset);
+    }
+    else
+    {
+        /* check for access before what's available */
+        if ((size_t)(-offset) > RINGBUF_SUB(h->ridx, h->data))
+            return -2;
+
+        h->ridx = RINGBUF_SUB(h->ridx, (size_t)(-offset));
+    }
+
+    return 0;
+}
+
+/* Copy data from the given handle to the dest buffer.
+   Return the number of bytes copied or < 0 for failure. */
+ssize_t 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 < size && h->filerem > 0) /* Data isn't ready */
+        return -2;
+
+    if (h->available == 0 && h->filerem == 0) /* File is finished reading */
+        return 0;
+
+    buffered_data = MIN(size, h->available - RINGBUF_SUB(h->ridx, h->data));
+
+    if (h->ridx + buffered_data > buffer_len)
+    {
+        /* the data wraps around the end of the buffer */
+        size_t read = buffer_len - h->ridx;
+        memcpy(dest, &buffer[h->ridx], read);
+        memcpy(dest+read, buffer, buffered_data - read);
+    }
+    else memcpy(dest, &buffer[h->ridx], 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 < 0 for failure.
+   size is the amount of linear data requested. it can be 0 to get as
+   much as possible.
+   The guard buffer may be used to provide the requested size */
+ssize_t bufgetdata(int handle_id, size_t size, unsigned char **data)
+{
+    struct memory_handle *h = find_handle(handle_id);
+    if (!h)
+        return -1;
+
+    if (h->available < size && h->filerem > 0) /* Data isn't ready */
+        return -2;
+
+    if (h->available == 0 && h->filerem == 0) /* File is finished reading */
+        return 0;
+
+    ssize_t ret;
+
+    if (h->ridx + size > buffer_len &&
+        h->available - RINGBUF_SUB(h->ridx, h->data) >= size)
+    {
+        /* the data wraps around the end of the buffer :
+           use the guard buffer to provide the requested amount of data. */
+        size_t copy_n = MIN(h->ridx + size - buffer_len, GUARD_SIZE);
+        memcpy(guard_buffer, (unsigned char *)buffer, copy_n);
+        ret = buffer_len - h->ridx + copy_n;
+        DEBUGF("used the guard buffer to complete\n");
+    }
+    else
+    {
+        ret = MIN(h->available - RINGBUF_SUB(h->ridx, h->data),
+                  buffer_len - h->ridx);
+    }
+
+    *data = (unsigned char *)&buffer[h->ridx];
+
+    /* DEBUGF("bufgetdata(%d): h->ridx=%ld, ret=%ld\n", handle_id,
+           (long)h->ridx, ret); */
+    return ret;
+}
+
+
+void buffering_thread(void)
+{
+    while (true)
+    {
+        if (data_rem() > 0 && wasted_space() > buffer_len/5) {
+            DEBUGF("there is %ld bytes of wasted space\n", wasted_space());
+
+            /* free buffer from outdated audio data */
+            struct memory_handle *m = first_handle;
+            while (m) {
+                if (m->type == TYPE_AUDIO)
+                    free_buffer(m->id);
+                m = m->next;
+            }
+
+            /* free buffer by moving metadata */
+            m = first_handle;
+            while (m) {
+                if (m->type != TYPE_AUDIO)
+                    free_buffer(m->id);
+                m = m->next;
+            }
+
+        }
+
+        if (data_rem() > 0 && BUF_USED < 3*buffer_len/4 &&
+            ata_disk_is_active())
+        {
+            DEBUGF("%ld bytes left to buffer and the buffer is low\n",
+                   data_rem());
+            fill_buffer();
+        } else {
+            sleep(HZ/2);
+        }
+    }
+}
dissimilarity index 63%
index d48986d..a08c4e1 100644 (file)
@@ -1,81 +1,47 @@
-/***************************************************************************
- *             __________               __   ___.
- *   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.
- *
- ****************************************************************************/
-
-#ifndef _BUFFERING_H_
-#define _BUFFERING_H_
-
-#include <sys/types.h>
-
-#define MAX_PATH 256
-
-/* Memory allocation should happen in the following order for each track:
-TYPE_CODEC, TYPE_(>AUDIO), TYPE_AUDIO.
-TYPE_CODEC will be released as soon as it has been closed.
-TYPE_(>AUDIO) (the metadata types) will stay valid as long as the
-              audio buffer straight after them is kept open.
-TYPE_AUDIO will be released as needed */
-
-#if 0
-JdGordon's thinking aloud...
-
-The way this all should work is that if first_handle->data_type > TYPE_AUDIO
-the LL will be searched for the first TYPE_AUDIO item. 
-if that item is closed then all the handles between first_handle and the found audio track
-will be closed and dumped (or maybe they have to wait for playback to close them...),
-either way, they will never be dumped unless the audio item is closed.
-
-Now, when reading/wiritng the buffer... if (m->next && m->next->id == -1 && m->next->data_type == TYPE_AUDIO (might not need this extra check)) then it is a continuation of m. the guard fuffer will most likely be needed when the buffer is read.. but thats ok.
-
-Unless im wrong it should only be possible that we have one of these "safe" sections at any one time.
-
-The only problem with this system is that the MoB has to be allocated before the audio is read.
-the id3 data should be a constant size which is ok, cuesheet will be known. image and buffer will not be known, which is where it might get tricky.
-
-/end babbling
-#endif
-
-enum data_type {
-    TYPE_CODEC,
-    TYPE_AUDIO, /* TYPE_VIDEO, TYPE_BUFFERING_FILE, all the same */
-    /* the following data_types will not be unallocated unless the 
-       audio next audio handle is closed. When that happens all the data
-       will be unallocated in one hit */
-    TYPE_ID3,
-    TYPE_CUESHEET,
-    TYPE_IMAGE,
-    TYPE_BUFFER,
-    TYPE_UNKNOWN,
-};
-
-enum data_state {
-    STATE_UNKNOWN,
-    STATE_OUTDATED,
-    STATE_IN_USE,
-    STATE_TO_BE_USED
-};
-
-void buffer_init(void);
-int bufopen(char *file, size_t offset);
-int bufclose(int handle_id);
-int bufseek(int handle_id, size_t offset);
-int bufadvance(int handle_id, ssize_t offset);
-int bufread(int handle_id, size_t size, char *dest);
-long bufgetdata(int handle_id, size_t size, unsigned char **data);
-
-#endif
+/***************************************************************************
+ *             __________               __   ___.
+ *   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.
+ *
+ ****************************************************************************/
+
+#ifndef _BUFFERING_H_
+#define _BUFFERING_H_
+
+#include <sys/types.h>
+
+#define MAX_PATH 256
+
+enum data_type {
+    TYPE_CODEC,
+    TYPE_AUDIO,
+    TYPE_STREAM,
+    TYPE_ID3,
+    TYPE_CUESHEET,
+    TYPE_IMAGE,
+    TYPE_BUFFER,
+    TYPE_UNKNOWN,
+};
+
+void buffer_init(void);
+int bufopen(char *file, size_t offset, enum data_type type);
+int bufalloc(void *src, size_t size, enum data_type type);
+int bufclose(int handle_id);
+int bufseek(int handle_id, size_t newpos);
+int bufadvance(int handle_id, off_t offset);
+ssize_t bufread(int handle_id, size_t size, char *dest);
+ssize_t bufgetdata(int handle_id, size_t size, unsigned char **data);
+
+#endif