diff --git a/apps/SOURCES b/apps/SOURCES index 2ea41c2..58e38a2 100644 --- a/apps/SOURCES +++ b/apps/SOURCES @@ -15,7 +15,7 @@ menus/display_menu.c menus/theme_menu.c #if CONFIG_CODEC == SWCODEC menus/eq_menu.c -buffering.c +buffering_flash.c voice_thread.c replaygain.c #else /* !SWCODEC */ diff --git a/apps/buffering_flash.c b/apps/buffering_flash.c new file mode 100644 index 0000000..6f99ec5 --- /dev/null +++ b/apps/buffering_flash.c @@ -0,0 +1,444 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2007 Nicolas Pennequin + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#include "config.h" +#include +#include +#include +#include +#include "buffering.h" + +#include "file.h" +#include "debug.h" +#include "metadata.h" +#include "thread.h" +#include "kernel.h" +#include "events.h" +#include "system.h" + +/* Define LOGF_ENABLE to enable logf output in this file */ +#define LOGF_ENABLE +#include "logf.h" + +/* macros to enable logf for queues + logging on SYS_TIMEOUT can be disabled */ +#ifdef SIMULATOR +/* Define this for logf output of all queuing except SYS_TIMEOUT */ +#define BUFFERING_LOGQUEUES +/* Define this to logf SYS_TIMEOUT messages */ +/* #define BUFFERING_LOGQUEUES_SYS_TIMEOUT */ +#endif + +#ifdef BUFFERING_LOGQUEUES +#define LOGFQUEUE logf +#else +#define LOGFQUEUE(...) +#endif + +#ifdef BUFFERING_LOGQUEUES_SYS_TIMEOUT +#define LOGFQUEUE_SYS_TIMEOUT logf +#else +#define LOGFQUEUE_SYS_TIMEOUT(...) +#endif + + +#define MAX_HANDLES 32 + +#define BUF_HANDLE_MASK 0x7FFFFFFF + +#define BUFFER_SIZE 32*1024 + +/* The buffer bufgetdata uses to hold a file chunk */ +static char buffer[BUFFER_SIZE]; + +/* The id of the handle for which we have data */ +static int buffer_hid = -1; + +/* The read offset in the buffer */ +static size_t buffer_offset = 0; + +/* How much fresh data is left in the buffer */ +static size_t buffer_avail = 0; + +/* The position in the file where the buffered data starts */ +static size_t buffer_file_offset = 0; + + +struct memory_handle { + int id; /* A unique ID for the handle */ + enum data_type type; /* Type of data buffered with this handle */ + char path[MAX_PATH]; /* Path if data originated in a file */ + size_t filepos; /* where we are in the file */ + size_t filesize; /* File total length */ + size_t offset; /* Where we are in the file */ + volatile size_t ridx; /* Read pointer, relative to the buffer */ + struct mp3entry id3data; +}; + +enum { + Q_HANDLE_ADDED, +}; + +static struct memory_handle handles[MAX_HANDLES]; + +static unsigned int num_handles = 0; + + +/* Buffering thread */ +static void buffering_thread(void); +static long buffering_stack[(DEFAULT_STACK_SIZE + 0x2000)/sizeof(long)]; +static const char buffering_thread_name[] = "buffering"; +static struct thread_entry *buffering_thread_p; +static struct event_queue buffering_queue; +static struct queue_sender_list buffering_queue_sender_list; + + + + +static struct memory_handle *find_handle(int handle_id) +{ + if (handle_id < 0) + return NULL; + + int i; + for (i = 0; i < MAX_HANDLES; i++) { + if (handles[i].id == handle_id) + return &handles[i]; + } + + return NULL; +} + +static struct memory_handle *add_handle(void) +{ + /* gives each handle a unique id */ + static int cur_handle_id = 0; + + /* find an unused handle slot */ + int i = 0; + while (i < MAX_HANDLES && handles[i].id != -1) + i++; + + if (handles[i].id == -1) { + handles[i].id = cur_handle_id; + + /* Wrap signed int is safe and 0 doesn't happen */ + cur_handle_id = (cur_handle_id + 1) & BUF_HANDLE_MASK; + + num_handles++; + + return &handles[i]; + } + else + return NULL; +} + +static bool rm_handle(struct memory_handle *h) +{ + if (h == NULL) + return true; + + num_handles--; + memset(h, 0, sizeof(struct memory_handle)); + h->id = -1; + + return true; +} + +int bufopen(const char *file, size_t offset, enum data_type type) +{ + if (num_handles >= MAX_HANDLES) + return ERR_BUFFER_FULL; + + int fd = open(file, O_RDONLY); + if (fd < 0) + return ERR_FILE_ERROR; + + struct memory_handle *h = add_handle(); + if (!h) + { + DEBUGF("bufopen: failed to add handle\n"); + close(fd); + return ERR_BUFFER_FULL; + } + + strncpy(h->path, file, MAX_PATH); + h->type = type; + h->filesize = filesize(fd); + h->filepos = offset; + h->offset = offset; + h->ridx = 0; + + if (type == TYPE_ID3) + { + if (!get_metadata(&h->id3data, fd, file)) { + DEBUGF("failed to get metadata\n"); + } + } + + close(fd); + + /* Inform the buffering thread that we added a handle */ + LOGFQUEUE("buffering > Q_HANDLE_ADDED %d", h->id); + queue_post(&buffering_queue, Q_HANDLE_ADDED, h->id); + + return h->id; +} + +int bufalloc(const void *src, size_t size, enum data_type type) +{ + (void)src; + (void)size; + (void)type; + DEBUGF("bufalloc not implemented\n"); + return 0; +} + +bool bufclose(int handle_id) +{ + struct memory_handle *h = find_handle(handle_id); + + /* If the handle is not found, it is closed */ + if (!h) + return true; + + /* Currently rm_handle always returns true */ + return rm_handle(h); +} + +int bufseek(int handle_id, size_t newpos) +{ + //DEBUGF("bufseek(%d, %ld)\n", handle_id, newpos); + + struct memory_handle *h = find_handle(handle_id); + if (!h) + return ERR_HANDLE_NOT_FOUND; + + if (newpos > h->filesize) { + /* access beyond the end of the file */ + return ERR_INVALID_VALUE; + } + else { + h->offset = newpos; + } + return 0; +} + +int bufadvance(int handle_id, off_t offset) +{ + //DEBUGF("bufadvance(%d, %ld)\n", handle_id, offset); + + struct memory_handle *h = find_handle(handle_id); + if (!h) + return ERR_HANDLE_NOT_FOUND; + + if ((size_t)(h->offset + offset) > h->filesize || + (off_t)(h->offset + offset) < 0) + { + /* access beyond the limits of the file */ + return ERR_INVALID_VALUE; + } + else { + buffer_offset += offset; + buffer_avail -= offset; + h->offset += offset; + h->ridx += offset; + } + return 0; +} + +ssize_t bufread(int handle_id, size_t size, void *dest) +{ + //DEBUGF("bufread(%d)\n", handle_id); + struct memory_handle *h = find_handle(handle_id); + if (!h) + return ERR_HANDLE_NOT_FOUND; + + int fd = open(h->path, O_RDONLY); + if (fd < 0) + return ERR_FILE_ERROR; + + lseek(fd, h->offset, SEEK_SET); + h->filepos = h->offset; + + ssize_t ret = read(fd, dest, size); + if (ret < 0) { + DEBUGF("read failed (%ld)\n", ret); + } else { + h->filepos += ret; + } + + close(fd); + return ret; +} + +ssize_t bufgetdata(int handle_id, size_t size, void **data) +{ + //DEBUGF("bufgetdata(%d, %ld)\n", handle_id, size); + (void)size; + + struct memory_handle *h = find_handle(handle_id); + if (!h) + return ERR_HANDLE_NOT_FOUND; + + if (h->type == TYPE_ID3) { + *data = &h->id3data; + return sizeof (struct mp3entry); + } else if (h->type == TYPE_PACKET_AUDIO) { + + /* If there is still enough data left in the buffer, we can use that */ + if (size < buffer_avail && buffer_file_offset + buffer_offset == h->offset) { + //DEBUGF("bufgetdata: buffer hit (%ld left)\n", buffer_avail); + *data = buffer + buffer_offset; + return buffer_avail; + } + + if (buffer_file_offset + buffer_offset != h->offset) { + DEBUGF("offset mismatch\n"); + } + + //DEBUGF("bufgetdata: buffer miss (%ld left)\n", buffer_avail); + + int fd = open(h->path, O_RDONLY); + if (fd < 0) + return ERR_FILE_ERROR; + + lseek(fd, h->offset, SEEK_SET); + h->filepos = h->offset; + buffer_file_offset = h->offset; + + //size = (size == 0) ? BUFFER_SIZE : MIN(size, BUFFER_SIZE); + ssize_t ret = read(fd, buffer, BUFFER_SIZE); + if (ret < 0) { + DEBUGF("read failed (%ld)\n", ret); + } else { + buffer_offset = 0; + buffer_avail = ret; + h->filepos += ret; + h->ridx = 0; + } + + close(fd); + *data = buffer; + return ret; + } else { + *data = NULL; + return 0; + } +} + +ssize_t bufgettail(int handle_id, size_t size, void **data) +{ + +} + +ssize_t bufcuttail(int handle_id, size_t size) +{ + +} + +ssize_t buf_get_offset(int handle_id, void *ptr) +{ + const struct memory_handle *h = find_handle(handle_id); + if (!h) + return ERR_HANDLE_NOT_FOUND; + + return ptr - (void *)&buffer[h->ridx]; +} + +ssize_t buf_handle_offset(int handle_id) +{ + +} + +void buf_request_buffer_handle(int handle_id) +{ + +} + +void buf_set_base_handle(int handle_id) +{ + +} + +size_t buf_used(void) +{ + +} + +void buf_set_watermark(size_t bytes) +{ + +} + +void buffering_get_debugdata(struct buffering_debug *dbgdata) +{ + +} + +void buffering_init(void) +{ + queue_init(&buffering_queue, true); + buffering_thread_p = create_thread( buffering_thread, buffering_stack, + sizeof(buffering_stack), CREATE_THREAD_FROZEN, + buffering_thread_name IF_PRIO(, PRIORITY_BUFFERING) + IF_COP(, CPU)); + + queue_enable_queue_send(&buffering_queue, &buffering_queue_sender_list, + buffering_thread_p); +} + +bool buffering_reset(char *buf, size_t buflen) +{ + int i; + for (i = 0; i < MAX_HANDLES; i++) { + handles[i].id = -1; + } + + num_handles = 0; + buffer_hid = -1; + buffer_offset = 0; + + thread_thaw(buffering_thread_p); + return true; +} + +void buffering_thread(void) +{ + struct queue_event ev; + + while (true) + { + queue_wait_w_tmo(&buffering_queue, &ev, 5); + + switch (ev.id) + { + case Q_HANDLE_ADDED: + { + LOGFQUEUE("buffering < Q_HANDLE_ADDED %d", (int)ev.data); + struct memory_handle *h = find_handle((int)ev.data); + if (h && h->type == TYPE_ID3) { + send_event(BUFFER_EVENT_FINISHED, &h->id); + } + } + } + } +}