mirror of
https://github.com/Rockbox/rockbox.git
synced 2025-10-13 18:17:39 -04:00
Commit FS#12069 - Playback rework - first stages. Gives as thorough as possible a treatment of codec management, track change and metadata logic as possible while maintaining fairly narrow focus and not rewriting everything all at once. Please see the rockbox-dev mail archive on 2011-04-25 (Playback engine rework) for a more thorough manifest of what was addressed. Plugins and codecs become incompatible.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@29785 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
parent
dcf0f8de4a
commit
c537d5958e
62 changed files with 6047 additions and 4221 deletions
|
@ -35,6 +35,7 @@ enum {
|
|||
PLAYBACK_EVENT_TRACK_BUFFER,
|
||||
PLAYBACK_EVENT_TRACK_FINISH,
|
||||
PLAYBACK_EVENT_TRACK_CHANGE,
|
||||
PLAYBACK_EVENT_TRACK_SKIP,
|
||||
PLAYBACK_EVENT_NEXTTRACKID3_AVAILABLE,
|
||||
};
|
||||
|
||||
|
|
278
apps/buffering.c
278
apps/buffering.c
|
@ -58,7 +58,7 @@
|
|||
#define GUARD_BUFSIZE (32*1024)
|
||||
|
||||
/* Define LOGF_ENABLE to enable logf output in this file */
|
||||
/*#define LOGF_ENABLE*/
|
||||
/* #define LOGF_ENABLE */
|
||||
#include "logf.h"
|
||||
|
||||
/* macros to enable logf for queues
|
||||
|
@ -82,8 +82,6 @@
|
|||
#define LOGFQUEUE_SYS_TIMEOUT(...)
|
||||
#endif
|
||||
|
||||
/* default point to start buffer refill */
|
||||
#define BUFFERING_DEFAULT_WATERMARK (1024*128)
|
||||
/* amount of data to read in one read() call */
|
||||
#define BUFFERING_DEFAULT_FILECHUNK (1024*32)
|
||||
|
||||
|
@ -94,6 +92,8 @@
|
|||
struct memory_handle {
|
||||
int id; /* A unique ID for the handle */
|
||||
enum data_type type; /* Type of data buffered with this handle */
|
||||
int8_t pinned; /* Count of references */
|
||||
int8_t signaled; /* Stop any attempt at waiting to get the data */
|
||||
char path[MAX_PATH]; /* Path if data originated in a file */
|
||||
int fd; /* File descriptor to path (-1 if closed) */
|
||||
size_t data; /* Start index of the handle's data buffer */
|
||||
|
@ -125,9 +125,7 @@ static volatile size_t buf_ridx; /* current reading position */
|
|||
|
||||
/* Configuration */
|
||||
static size_t conf_watermark = 0; /* Level to trigger filebuf fill */
|
||||
#if MEMORYSIZE > 8
|
||||
static size_t high_watermark = 0; /* High watermark for rebuffer */
|
||||
#endif
|
||||
|
||||
/* current memory handle in the linked list. NULL when the list is empty. */
|
||||
static struct memory_handle *cur_handle;
|
||||
|
@ -162,7 +160,6 @@ enum
|
|||
Q_REBUFFER_HANDLE, /* Request reset and rebuffering of a handle at a new
|
||||
file starting position. */
|
||||
Q_CLOSE_HANDLE, /* Request closing a handle */
|
||||
Q_BASE_HANDLE, /* Set the reference handle for buf_useful_data */
|
||||
|
||||
/* Configuration: */
|
||||
Q_START_FILL, /* Request that the buffering thread initiate a buffer
|
||||
|
@ -222,6 +219,9 @@ static inline ssize_t ringbuf_add_cross(uintptr_t p1, size_t v, uintptr_t p2)
|
|||
/* Bytes available in the buffer */
|
||||
#define BUF_USED ringbuf_sub(buf_widx, buf_ridx)
|
||||
|
||||
/* Real buffer watermark */
|
||||
#define BUF_WATERMARK MIN(conf_watermark, high_watermark)
|
||||
|
||||
/*
|
||||
LINKED LIST MANAGEMENT
|
||||
======================
|
||||
|
@ -313,6 +313,12 @@ static struct memory_handle *add_handle(size_t data_size, bool can_wrap,
|
|||
/* Prevent buffering thread from looking at it */
|
||||
new_handle->filerem = 0;
|
||||
|
||||
/* Handle can be moved by default */
|
||||
new_handle->pinned = 0;
|
||||
|
||||
/* Handle data can be waited for by default */
|
||||
new_handle->signaled = 0;
|
||||
|
||||
/* only advance the buffer write index of the size of the struct */
|
||||
buf_widx = ringbuf_add(buf_widx, sizeof(struct memory_handle));
|
||||
|
||||
|
@ -364,6 +370,9 @@ static bool rm_handle(const struct memory_handle *h)
|
|||
buf_widx = cur_handle->widx;
|
||||
}
|
||||
} else {
|
||||
/* If we don't find ourselves, this is a seriously incoherent
|
||||
state with a corrupted list and severe action is needed! */
|
||||
panicf("rm_handle fail: %d", h->id);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -385,8 +394,7 @@ 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) {
|
||||
if (cached_handle->id == handle_id) {
|
||||
return cached_handle;
|
||||
} else if (cached_handle->next &&
|
||||
|
@ -618,20 +626,22 @@ static void update_data_counters(struct data_counters *dc)
|
|||
static inline bool buffer_is_low(void)
|
||||
{
|
||||
update_data_counters(NULL);
|
||||
return data_counters.useful < (conf_watermark / 2);
|
||||
return data_counters.useful < BUF_WATERMARK / 2;
|
||||
}
|
||||
|
||||
/* Q_BUFFER_HANDLE event and buffer data for the given handle.
|
||||
Return whether or not the buffering should continue explicitly. */
|
||||
static bool buffer_handle(int handle_id, size_t to_buffer)
|
||||
{
|
||||
logf("buffer_handle(%d)", handle_id);
|
||||
logf("buffer_handle(%d, %lu)", handle_id, (unsigned long)to_buffer);
|
||||
struct memory_handle *h = find_handle(handle_id);
|
||||
bool stop = false;
|
||||
|
||||
if (!h)
|
||||
return true;
|
||||
|
||||
logf(" type: %d", (int)h->type);
|
||||
|
||||
if (h->filerem == 0) {
|
||||
/* nothing left to buffer */
|
||||
return true;
|
||||
|
@ -659,13 +669,13 @@ static bool buffer_handle(int handle_id, size_t to_buffer)
|
|||
if (!get_metadata((struct mp3entry *)(buffer + h->data),
|
||||
h->fd, h->path)) {
|
||||
/* metadata parsing failed: clear the buffer. */
|
||||
memset(buffer + h->data, 0, sizeof(struct mp3entry));
|
||||
wipe_mp3entry((struct mp3entry *)(buffer + h->data));
|
||||
}
|
||||
close(h->fd);
|
||||
h->fd = -1;
|
||||
h->filerem = 0;
|
||||
h->available = sizeof(struct mp3entry);
|
||||
h->widx += sizeof(struct mp3entry);
|
||||
h->widx = ringbuf_add(h->widx, sizeof(struct mp3entry));
|
||||
send_event(BUFFER_EVENT_FINISHED, &handle_id);
|
||||
return true;
|
||||
}
|
||||
|
@ -698,7 +708,7 @@ static bool buffer_handle(int handle_id, size_t to_buffer)
|
|||
break;
|
||||
}
|
||||
|
||||
DEBUGF("File ended %ld bytes early\n", (long)h->filerem);
|
||||
logf("File ended %ld bytes early\n", (long)h->filerem);
|
||||
h->filesize -= h->filerem;
|
||||
h->filerem = 0;
|
||||
break;
|
||||
|
@ -770,22 +780,31 @@ static bool close_handle(int handle_id)
|
|||
part of its data buffer or by moving all the data. */
|
||||
static void shrink_handle(struct memory_handle *h)
|
||||
{
|
||||
size_t delta;
|
||||
|
||||
if (!h)
|
||||
return;
|
||||
|
||||
if (h->type == TYPE_ID3 || h->type == TYPE_CUESHEET ||
|
||||
h->type == TYPE_BITMAP || h->type == TYPE_CODEC ||
|
||||
h->type == TYPE_ATOMIC_AUDIO)
|
||||
{
|
||||
if (h->type == TYPE_PACKET_AUDIO) {
|
||||
/* only move the handle struct */
|
||||
/* data is pinned by default - if we start moving packet audio,
|
||||
the semantics will determine whether or not data is movable
|
||||
but the handle will remain movable in either case */
|
||||
size_t delta = ringbuf_sub(h->ridx, h->data);
|
||||
|
||||
/* The value of delta might change for alignment reasons */
|
||||
if (!move_handle(&h, &delta, 0, true))
|
||||
return;
|
||||
|
||||
h->data = ringbuf_add(h->data, delta);
|
||||
h->available -= delta;
|
||||
h->offset += delta;
|
||||
} else {
|
||||
/* metadata handle: we can move all of it */
|
||||
if (!h->next || h->filerem != 0)
|
||||
return; /* Last handle or not finished loading */
|
||||
if (h->pinned || !h->next || h->filerem != 0)
|
||||
return; /* Pinned, last handle or not finished loading */
|
||||
|
||||
uintptr_t handle_distance =
|
||||
ringbuf_sub(ringbuf_offset(h->next), h->data);
|
||||
delta = handle_distance - h->available;
|
||||
size_t delta = handle_distance - h->available;
|
||||
|
||||
/* The value of delta might change for alignment reasons */
|
||||
if (!move_handle(&h, &delta, h->available, h->type==TYPE_CODEC))
|
||||
|
@ -806,15 +825,6 @@ static void shrink_handle(struct memory_handle *h)
|
|||
struct bitmap *bmp = (struct bitmap *)&buffer[h->data];
|
||||
bmp->data = &buffer[h->data + sizeof(struct bitmap)];
|
||||
}
|
||||
} else {
|
||||
/* only move the handle struct */
|
||||
delta = ringbuf_sub(h->ridx, h->data);
|
||||
if (!move_handle(&h, &delta, 0, true))
|
||||
return;
|
||||
|
||||
h->data = ringbuf_add(h->data, delta);
|
||||
h->available -= delta;
|
||||
h->offset += delta;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -962,6 +972,8 @@ int bufopen(const char *file, size_t offset, enum data_type type,
|
|||
mutex_unlock(&llist_mutex);
|
||||
return handle_id;
|
||||
}
|
||||
else if (type == TYPE_UNKNOWN)
|
||||
return ERR_UNSUPPORTED_TYPE;
|
||||
#ifdef APPLICATION
|
||||
/* loading code from memory is not supported in application builds */
|
||||
else if (type == TYPE_CODEC)
|
||||
|
@ -1083,7 +1095,12 @@ int bufopen(const char *file, size_t offset, enum data_type type,
|
|||
*/
|
||||
int bufalloc(const void *src, size_t size, enum data_type type)
|
||||
{
|
||||
int handle_id = ERR_BUFFER_FULL;
|
||||
int handle_id;
|
||||
|
||||
if (type == TYPE_UNKNOWN)
|
||||
return ERR_UNSUPPORTED_TYPE;
|
||||
|
||||
handle_id = ERR_BUFFER_FULL;
|
||||
|
||||
mutex_lock(&llist_mutex);
|
||||
|
||||
|
@ -1124,7 +1141,14 @@ int bufalloc(const void *src, size_t size, enum data_type type)
|
|||
bool bufclose(int handle_id)
|
||||
{
|
||||
logf("bufclose(%d)", handle_id);
|
||||
|
||||
#if 0
|
||||
/* Don't interrupt the buffering thread if the handle is already
|
||||
stale */
|
||||
if (!find_handle(handle_id)) {
|
||||
logf(" handle already closed");
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
LOGFQUEUE("buffering >| Q_CLOSE_HANDLE %d", handle_id);
|
||||
return queue_send(&buffering_queue, Q_CLOSE_HANDLE, handle_id);
|
||||
}
|
||||
|
@ -1236,9 +1260,10 @@ static int seek_handle(struct memory_handle *h, size_t newpos)
|
|||
|
||||
/* Set reading index in handle (relatively to the start of the file).
|
||||
Access before the available data will trigger a rebuffer.
|
||||
Return 0 for success and < 0 for failure:
|
||||
-1 if the handle wasn't found
|
||||
-2 if the new requested position was beyond the end of the file
|
||||
Return 0 for success and for failure:
|
||||
ERR_HANDLE_NOT_FOUND if the handle wasn't found
|
||||
ERR_INVALID_VALUE if the new requested position was beyond the end of
|
||||
the file
|
||||
*/
|
||||
int bufseek(int handle_id, size_t newpos)
|
||||
{
|
||||
|
@ -1250,7 +1275,11 @@ int bufseek(int handle_id, size_t newpos)
|
|||
}
|
||||
|
||||
/* Advance the reading index in a handle (relatively to its current position).
|
||||
Return 0 for success and < 0 for failure */
|
||||
Return 0 for success and for failure:
|
||||
ERR_HANDLE_NOT_FOUND if the handle wasn't found
|
||||
ERR_INVALID_VALUE if the new requested position was beyond the end of
|
||||
the file
|
||||
*/
|
||||
int bufadvance(int handle_id, off_t offset)
|
||||
{
|
||||
struct memory_handle *h = find_handle(handle_id);
|
||||
|
@ -1261,6 +1290,18 @@ int bufadvance(int handle_id, off_t offset)
|
|||
return seek_handle(h, newpos);
|
||||
}
|
||||
|
||||
/* Get the read position from the start of the file
|
||||
Returns the offset from byte 0 of the file and for failure:
|
||||
ERR_HANDLE_NOT_FOUND if the handle wasn't found
|
||||
*/
|
||||
off_t bufftell(int handle_id)
|
||||
{
|
||||
const struct memory_handle *h = find_handle(handle_id);
|
||||
if (!h)
|
||||
return ERR_HANDLE_NOT_FOUND;
|
||||
return h->offset + ringbuf_sub(h->ridx, h->data);
|
||||
}
|
||||
|
||||
/* Used by bufread and bufgetdata to prepare the buffer and retrieve the
|
||||
* actual amount of data available for reading. This function explicitly
|
||||
* does not check the validity of the input handle. It does do range checks
|
||||
|
@ -1306,7 +1347,7 @@ static struct memory_handle *prep_bufdata(int handle_id, size_t *size,
|
|||
/* it is not safe for a non-buffering thread to sleep while
|
||||
* holding a handle */
|
||||
h = find_handle(handle_id);
|
||||
if (!h)
|
||||
if (!h || h->signaled != 0)
|
||||
return NULL;
|
||||
avail = handle_size_available(h);
|
||||
}
|
||||
|
@ -1447,9 +1488,14 @@ SECONDARY EXPORTED FUNCTIONS
|
|||
buf_handle_offset
|
||||
buf_request_buffer_handle
|
||||
buf_set_base_handle
|
||||
buf_handle_data_type
|
||||
buf_is_handle
|
||||
buf_pin_handle
|
||||
buf_signal_handle
|
||||
buf_length
|
||||
buf_used
|
||||
register_buffering_callback
|
||||
unregister_buffering_callback
|
||||
buf_set_watermark
|
||||
buf_get_watermark
|
||||
|
||||
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
|
||||
|
@ -1472,8 +1518,61 @@ void buf_request_buffer_handle(int handle_id)
|
|||
|
||||
void buf_set_base_handle(int handle_id)
|
||||
{
|
||||
LOGFQUEUE("buffering > Q_BASE_HANDLE %d", handle_id);
|
||||
queue_post(&buffering_queue, Q_BASE_HANDLE, handle_id);
|
||||
mutex_lock(&llist_mutex);
|
||||
base_handle_id = handle_id;
|
||||
mutex_unlock(&llist_mutex);
|
||||
}
|
||||
|
||||
enum data_type buf_handle_data_type(int handle_id)
|
||||
{
|
||||
const struct memory_handle *h = find_handle(handle_id);
|
||||
if (!h)
|
||||
return TYPE_UNKNOWN;
|
||||
return h->type;
|
||||
}
|
||||
|
||||
ssize_t buf_handle_remaining(int handle_id)
|
||||
{
|
||||
const struct memory_handle *h = find_handle(handle_id);
|
||||
if (!h)
|
||||
return ERR_HANDLE_NOT_FOUND;
|
||||
return h->filerem;
|
||||
}
|
||||
|
||||
bool buf_is_handle(int handle_id)
|
||||
{
|
||||
return find_handle(handle_id) != NULL;
|
||||
}
|
||||
|
||||
bool buf_pin_handle(int handle_id, bool pin)
|
||||
{
|
||||
struct memory_handle *h = find_handle(handle_id);
|
||||
if (!h)
|
||||
return false;
|
||||
|
||||
if (pin) {
|
||||
h->pinned++;
|
||||
} else if (h->pinned > 0) {
|
||||
h->pinned--;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool buf_signal_handle(int handle_id, bool signal)
|
||||
{
|
||||
struct memory_handle *h = find_handle(handle_id);
|
||||
if (!h)
|
||||
return false;
|
||||
|
||||
h->signaled = signal ? 1 : 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Return the size of the ringbuffer */
|
||||
size_t buf_length(void)
|
||||
{
|
||||
return buffer_len;
|
||||
}
|
||||
|
||||
/* Return the amount of buffer space used */
|
||||
|
@ -1487,6 +1586,21 @@ void buf_set_watermark(size_t bytes)
|
|||
conf_watermark = bytes;
|
||||
}
|
||||
|
||||
size_t buf_get_watermark(void)
|
||||
{
|
||||
return BUF_WATERMARK;
|
||||
}
|
||||
|
||||
#ifdef HAVE_IO_PRIORITY
|
||||
void buf_back_off_storage(bool back_off)
|
||||
{
|
||||
int priority = back_off ?
|
||||
IO_PRIORITY_BACKGROUND : IO_PRIORITY_IMMEDIATE;
|
||||
thread_set_io_priority(buffering_thread_id, priority);
|
||||
}
|
||||
#endif
|
||||
|
||||
/** -- buffer thread helpers -- **/
|
||||
static void shrink_buffer_inner(struct memory_handle *h)
|
||||
{
|
||||
if (h == NULL)
|
||||
|
@ -1503,7 +1617,7 @@ static void shrink_buffer(void)
|
|||
shrink_buffer_inner(first_handle);
|
||||
}
|
||||
|
||||
void buffering_thread(void)
|
||||
static void NORETURN_ATTR buffering_thread(void)
|
||||
{
|
||||
bool filling = false;
|
||||
struct queue_event ev;
|
||||
|
@ -1511,19 +1625,21 @@ void buffering_thread(void)
|
|||
|
||||
while (true)
|
||||
{
|
||||
if (num_handles > 0) {
|
||||
if (!filling) {
|
||||
cancel_cpu_boost();
|
||||
}
|
||||
|
||||
queue_wait_w_tmo(&buffering_queue, &ev, filling ? 5 : HZ/2);
|
||||
queue_wait_w_tmo(&buffering_queue, &ev, filling ? 1 : HZ/2);
|
||||
} else {
|
||||
filling = false;
|
||||
cancel_cpu_boost();
|
||||
queue_wait(&buffering_queue, &ev);
|
||||
}
|
||||
|
||||
switch (ev.id)
|
||||
{
|
||||
case Q_START_FILL:
|
||||
LOGFQUEUE("buffering < Q_START_FILL %d", (int)ev.data);
|
||||
/* Call buffer callbacks here because this is one of two ways
|
||||
* to begin a full buffer fill */
|
||||
send_event(BUFFER_EVENT_BUFFER_LOW, 0);
|
||||
shrink_buffer();
|
||||
queue_reply(&buffering_queue, 1);
|
||||
filling |= buffer_handle((int)ev.data, 0);
|
||||
|
@ -1553,36 +1669,21 @@ void buffering_thread(void)
|
|||
filling = true;
|
||||
break;
|
||||
|
||||
case Q_BASE_HANDLE:
|
||||
LOGFQUEUE("buffering < Q_BASE_HANDLE %d", (int)ev.data);
|
||||
base_handle_id = (int)ev.data;
|
||||
break;
|
||||
|
||||
#if (CONFIG_PLATFORM & PLATFORM_NATIVE)
|
||||
case SYS_USB_CONNECTED:
|
||||
LOGFQUEUE("buffering < SYS_USB_CONNECTED");
|
||||
usb_acknowledge(SYS_USB_CONNECTED_ACK);
|
||||
usb_wait_for_disconnect(&buffering_queue);
|
||||
break;
|
||||
#endif
|
||||
|
||||
case SYS_TIMEOUT:
|
||||
LOGFQUEUE_SYS_TIMEOUT("buffering < SYS_TIMEOUT");
|
||||
break;
|
||||
}
|
||||
|
||||
if (num_handles == 0 || !queue_empty(&buffering_queue))
|
||||
continue;
|
||||
|
||||
update_data_counters(NULL);
|
||||
|
||||
/* If the buffer is low, call the callbacks to get new data */
|
||||
if (num_handles > 0 && data_counters.useful <= conf_watermark)
|
||||
send_event(BUFFER_EVENT_BUFFER_LOW, 0);
|
||||
|
||||
#if 0
|
||||
/* TODO: This needs to be fixed to use the idle callback, disable it
|
||||
* for simplicity until its done right */
|
||||
#if MEMORYSIZE > 8
|
||||
/* If the disk is spinning, take advantage by filling the buffer */
|
||||
else if (storage_disk_is_active() && queue_empty(&buffering_queue)) {
|
||||
else if (storage_disk_is_active()) {
|
||||
if (num_handles > 0 && data_counters.useful <= high_watermark)
|
||||
send_event(BUFFER_EVENT_BUFFER_LOW, 0);
|
||||
|
||||
|
@ -1597,15 +1698,23 @@ void buffering_thread(void)
|
|||
#endif
|
||||
#endif
|
||||
|
||||
if (queue_empty(&buffering_queue)) {
|
||||
if (filling) {
|
||||
if (data_counters.remaining > 0 && BUF_USED < buffer_len)
|
||||
if (data_counters.remaining > 0 && BUF_USED < buffer_len) {
|
||||
filling = fill_buffer();
|
||||
else if (data_counters.remaining == 0)
|
||||
}
|
||||
else if (data_counters.remaining == 0) {
|
||||
filling = false;
|
||||
}
|
||||
} else if (ev.id == SYS_TIMEOUT) {
|
||||
if (data_counters.remaining > 0 &&
|
||||
data_counters.useful <= conf_watermark) {
|
||||
if (data_counters.useful < BUF_WATERMARK) {
|
||||
/* The buffer is low and we're idle, just watching the levels
|
||||
- call the callbacks to get new data */
|
||||
send_event(BUFFER_EVENT_BUFFER_LOW, NULL);
|
||||
|
||||
/* Continue anything else we haven't finished - it might
|
||||
get booted off or stop early because the receiver hasn't
|
||||
had a chance to clear anything yet */
|
||||
if (data_counters.remaining > 0) {
|
||||
shrink_buffer();
|
||||
filling = fill_buffer();
|
||||
}
|
||||
|
@ -1618,9 +1727,14 @@ void buffering_init(void)
|
|||
{
|
||||
mutex_init(&llist_mutex);
|
||||
|
||||
conf_watermark = BUFFERING_DEFAULT_WATERMARK;
|
||||
|
||||
queue_init(&buffering_queue, true);
|
||||
/* Thread should absolutely not respond to USB because if it waits first,
|
||||
then it cannot properly service the handles and leaks will happen -
|
||||
this is a worker thread and shouldn't need to care about any system
|
||||
notifications.
|
||||
***
|
||||
Whoever is using buffering should be responsible enough to clear all
|
||||
the handles at the right time. */
|
||||
queue_init(&buffering_queue, false);
|
||||
buffering_thread_id = create_thread( buffering_thread, buffering_stack,
|
||||
sizeof(buffering_stack), CREATE_THREAD_FROZEN,
|
||||
buffering_thread_name IF_PRIO(, PRIORITY_BUFFERING)
|
||||
|
@ -1636,6 +1750,9 @@ bool buffering_reset(char *buf, size_t buflen)
|
|||
/* Wraps of storage-aligned data must also be storage aligned,
|
||||
thus buf and buflen must be a aligned to an integer multiple of
|
||||
the storage alignment */
|
||||
|
||||
buflen -= GUARD_BUFSIZE;
|
||||
|
||||
STORAGE_ALIGN_BUFFER(buf, buflen);
|
||||
|
||||
if (!buf || !buflen)
|
||||
|
@ -1654,10 +1771,13 @@ bool buffering_reset(char *buf, size_t buflen)
|
|||
num_handles = 0;
|
||||
base_handle_id = -1;
|
||||
|
||||
/* Set the high watermark as 75% full...or 25% empty :) */
|
||||
#if MEMORYSIZE > 8
|
||||
/* Set the high watermark as 75% full...or 25% empty :)
|
||||
This is the greatest fullness that will trigger low-buffer events
|
||||
no matter what the setting because high-bitrate files can have
|
||||
ludicrous margins that even exceed the buffer size - most common
|
||||
with a huge anti-skip buffer but even without that setting,
|
||||
staying constantly active in buffering is pointless */
|
||||
high_watermark = 3*buflen / 4;
|
||||
#endif
|
||||
|
||||
thread_thaw(buffering_thread_id);
|
||||
|
||||
|
@ -1673,5 +1793,5 @@ void buffering_get_debugdata(struct buffering_debug *dbgdata)
|
|||
dbgdata->wasted_space = dc.wasted;
|
||||
dbgdata->buffered_data = dc.buffered;
|
||||
dbgdata->useful_data = dc.useful;
|
||||
dbgdata->watermark = conf_watermark;
|
||||
dbgdata->watermark = BUF_WATERMARK;
|
||||
}
|
||||
|
|
|
@ -28,14 +28,13 @@
|
|||
|
||||
|
||||
enum data_type {
|
||||
TYPE_UNKNOWN = 0, /* invalid type indicator */
|
||||
TYPE_ID3,
|
||||
TYPE_CODEC,
|
||||
TYPE_PACKET_AUDIO,
|
||||
TYPE_ATOMIC_AUDIO,
|
||||
TYPE_ID3,
|
||||
TYPE_CUESHEET,
|
||||
TYPE_BITMAP,
|
||||
TYPE_BUFFER,
|
||||
TYPE_UNKNOWN,
|
||||
};
|
||||
|
||||
/* Error return values */
|
||||
|
@ -63,6 +62,7 @@ bool buffering_reset(char *buf, size_t buflen);
|
|||
* bufclose : Close an open handle
|
||||
* bufseek : Set handle reading index, relatively to the start of the file
|
||||
* bufadvance: Move handle reading index, relatively to current position
|
||||
* bufftell : Return the handle's file read position
|
||||
* bufread : Copy data from a handle to a buffer
|
||||
* bufgetdata: Obtain a pointer for linear access to a "size" amount of data
|
||||
* bufgettail: Out-of-band get the last size bytes of a handle.
|
||||
|
@ -81,28 +81,40 @@ int bufalloc(const void *src, size_t size, enum data_type type);
|
|||
bool bufclose(int handle_id);
|
||||
int bufseek(int handle_id, size_t newpos);
|
||||
int bufadvance(int handle_id, off_t offset);
|
||||
off_t bufftell(int handle_id);
|
||||
ssize_t bufread(int handle_id, size_t size, void *dest);
|
||||
ssize_t bufgetdata(int handle_id, size_t size, void **data);
|
||||
ssize_t bufgettail(int handle_id, size_t size, void **data);
|
||||
ssize_t bufcuttail(int handle_id, size_t size);
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
* SECONDARY FUNCTIONS
|
||||
* ===================
|
||||
*
|
||||
* buf_handle_data_type: return the handle's data type
|
||||
* buf_is_handle: is the handle valid?
|
||||
* buf_pin_handle: Disallow/allow handle movement. Handle may still be removed.
|
||||
* buf_handle_offset: Get the offset of the first buffered byte from the file
|
||||
* buf_request_buffer_handle: Request buffering of a handle
|
||||
* buf_set_base_handle: Tell the buffering thread which handle is currently read
|
||||
* buf_length: Total size of ringbuffer
|
||||
* buf_used: Total amount of buffer space used (including allocated space)
|
||||
* buf_back_off_storage: tell buffering thread to take it easy
|
||||
****************************************************************************/
|
||||
|
||||
enum data_type buf_handle_data_type(int handle_id);
|
||||
ssize_t buf_handle_remaining(int handle_id);
|
||||
bool buf_is_handle(int handle_id);
|
||||
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_length(void);
|
||||
size_t buf_used(void);
|
||||
|
||||
|
||||
bool buf_pin_handle(int handle_id, bool pin);
|
||||
bool buf_signal_handle(int handle_id, bool signal);
|
||||
#ifdef HAVE_IO_PRIORITY
|
||||
void buf_back_off_storage(bool back_off);
|
||||
#endif
|
||||
|
||||
/* Settings */
|
||||
enum {
|
||||
|
@ -110,6 +122,7 @@ enum {
|
|||
BUFFERING_SET_CHUNKSIZE,
|
||||
};
|
||||
void buf_set_watermark(size_t bytes);
|
||||
size_t buf_get_watermark(void);
|
||||
|
||||
/* Debugging */
|
||||
struct buffering_debug {
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
*
|
||||
* Copyright (C) 2005-2007 Miika Pekkarinen
|
||||
* Copyright (C) 2007-2008 Nicolas Pennequin
|
||||
* Copyright (C) 2011 Michael Sevakis
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
|
@ -21,16 +22,14 @@
|
|||
****************************************************************************/
|
||||
#include "config.h"
|
||||
#include "system.h"
|
||||
#include "playback.h"
|
||||
#include "codec_thread.h"
|
||||
#include "kernel.h"
|
||||
#include "codecs.h"
|
||||
#include "buffering.h"
|
||||
#include "codec_thread.h"
|
||||
#include "pcmbuf.h"
|
||||
#include "playback.h"
|
||||
#include "buffering.h"
|
||||
#include "dsp.h"
|
||||
#include "abrepeat.h"
|
||||
#include "metadata.h"
|
||||
#include "splash.h"
|
||||
|
||||
/* Define LOGF_ENABLE to enable logf output in this file */
|
||||
/*#define LOGF_ENABLE*/
|
||||
|
@ -57,38 +56,45 @@
|
|||
#define LOGFQUEUE_SYS_TIMEOUT(...)
|
||||
#endif
|
||||
|
||||
|
||||
/* Variables are commented with the threads that use them:
|
||||
* A=audio, C=codec, V=voice. A suffix of - indicates that
|
||||
* the variable is read but not updated on that thread.
|
||||
|
||||
* A=audio, C=codec
|
||||
* - = reads only
|
||||
*
|
||||
* Unless otherwise noted, the extern variables are located
|
||||
* in playback.c.
|
||||
*/
|
||||
|
||||
/* Main state control */
|
||||
|
||||
/* Type of codec loaded? (C/A) */
|
||||
static int current_codectype SHAREDBSS_ATTR = AFMT_UNKNOWN;
|
||||
|
||||
extern struct mp3entry *thistrack_id3, /* the currently playing track */
|
||||
*othertrack_id3; /* prev track during track-change-transition, or end of playlist,
|
||||
* next track otherwise */
|
||||
|
||||
/* Track change controls */
|
||||
extern struct event_queue audio_queue SHAREDBSS_ATTR;
|
||||
/* Q_LOAD_CODEC parameter data */
|
||||
struct codec_load_info
|
||||
{
|
||||
int hid; /* audio handle id (specify < 0 to use afmt) */
|
||||
int afmt; /* codec specification (AFMT_*) */
|
||||
};
|
||||
|
||||
|
||||
/** --- Main state control --- **/
|
||||
|
||||
static int codec_type = AFMT_UNKNOWN; /* Codec type (C,A-) */
|
||||
|
||||
/* Private interfaces to main playback control */
|
||||
extern void audio_codec_update_elapsed(unsigned long value);
|
||||
extern void audio_codec_update_offset(size_t value);
|
||||
extern void audio_queue_post(long id, intptr_t data);
|
||||
extern struct codec_api ci; /* from codecs.c */
|
||||
|
||||
/* Codec thread */
|
||||
static unsigned int codec_thread_id; /* For modifying thread priority later */
|
||||
static struct event_queue codec_queue SHAREDBSS_ATTR;
|
||||
static struct queue_sender_list codec_queue_sender_list SHAREDBSS_ATTR;
|
||||
static long codec_stack[(DEFAULT_STACK_SIZE + 0x2000)/sizeof(long)]
|
||||
IBSS_ATTR;
|
||||
static long codec_stack[(DEFAULT_STACK_SIZE + 0x2000)/sizeof(long)] IBSS_ATTR;
|
||||
static const char codec_thread_name[] = "codec";
|
||||
|
||||
static void unload_codec(void);
|
||||
|
||||
/* Messages are only ever sent one at a time to the codec from the audio
|
||||
thread. This is important for correct operation unless playback is
|
||||
stopped. */
|
||||
|
||||
/* static routines */
|
||||
static void codec_queue_ack(intptr_t ackme)
|
||||
{
|
||||
|
@ -100,52 +106,63 @@ static intptr_t codec_queue_send(long id, intptr_t data)
|
|||
return queue_send(&codec_queue, id, data);
|
||||
}
|
||||
|
||||
/**************************************/
|
||||
|
||||
/** misc external functions */
|
||||
|
||||
/* Used to check whether a new codec must be loaded. See array audio_formats[]
|
||||
* in metadata.c */
|
||||
int get_codec_base_type(int type)
|
||||
/* Poll the state of the codec queue. Returns < 0 if the message is urgent
|
||||
and any state should exit, > 0 if it's a run message (and it was
|
||||
scrubbed), 0 if message was ignored. */
|
||||
static int codec_check_queue__have_msg(void)
|
||||
{
|
||||
int base_type = type;
|
||||
switch (type) {
|
||||
case AFMT_MPA_L1:
|
||||
case AFMT_MPA_L2:
|
||||
case AFMT_MPA_L3:
|
||||
base_type = AFMT_MPA_L3;
|
||||
break;
|
||||
case AFMT_MPC_SV7:
|
||||
case AFMT_MPC_SV8:
|
||||
base_type = AFMT_MPC_SV7;
|
||||
break;
|
||||
case AFMT_MP4_AAC:
|
||||
case AFMT_MP4_AAC_HE:
|
||||
base_type = AFMT_MP4_AAC;
|
||||
break;
|
||||
case AFMT_SAP:
|
||||
case AFMT_CMC:
|
||||
case AFMT_CM3:
|
||||
case AFMT_CMR:
|
||||
case AFMT_CMS:
|
||||
case AFMT_DMC:
|
||||
case AFMT_DLT:
|
||||
case AFMT_MPT:
|
||||
case AFMT_MPD:
|
||||
case AFMT_RMT:
|
||||
case AFMT_TMC:
|
||||
case AFMT_TM8:
|
||||
case AFMT_TM2:
|
||||
base_type = AFMT_SAP;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
struct queue_event ev;
|
||||
|
||||
queue_peek(&codec_queue, &ev);
|
||||
|
||||
/* Seek, pause or stop? Just peek and return if so. Codec
|
||||
must handle the command after returing. Inserts will not
|
||||
be allowed until it complies. */
|
||||
switch (ev.id)
|
||||
{
|
||||
case Q_CODEC_SEEK:
|
||||
LOGFQUEUE("codec - Q_CODEC_SEEK", ev.id);
|
||||
return -1;
|
||||
case Q_CODEC_PAUSE:
|
||||
LOGFQUEUE("codec - Q_CODEC_PAUSE", ev.id);
|
||||
return -1;
|
||||
case Q_CODEC_STOP:
|
||||
LOGFQUEUE("codec - Q_CODEC_STOP", ev.id);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return base_type;
|
||||
/* This is in error in this context unless it's "go, go, go!" */
|
||||
queue_wait(&codec_queue, &ev);
|
||||
|
||||
if (ev.id == Q_CODEC_RUN)
|
||||
{
|
||||
logf("codec < Q_CODEC_RUN: already running!");
|
||||
codec_queue_ack(Q_CODEC_RUN);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Ignore it */
|
||||
logf("codec < bad req %ld (%s)", ev.id, __func__);
|
||||
codec_queue_ack(Q_NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char *get_codec_filename(int cod_spec)
|
||||
/* Does the audio format type equal CODEC_TYPE_ENCODER? */
|
||||
static inline bool type_is_encoder(int afmt)
|
||||
{
|
||||
#ifdef AUDIO_HAVE_RECORDING
|
||||
return (afmt & CODEC_TYPE_MASK) == CODEC_TYPE_ENCODER;
|
||||
#else
|
||||
return false;
|
||||
(void)afmt;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**************************************/
|
||||
|
||||
|
||||
/** --- Miscellaneous external functions --- **/
|
||||
const char * get_codec_filename(int cod_spec)
|
||||
{
|
||||
const char *fname;
|
||||
|
||||
|
@ -173,7 +190,7 @@ const char *get_codec_filename(int cod_spec)
|
|||
#endif /* HAVE_RECORDING */
|
||||
|
||||
return fname;
|
||||
} /* get_codec_filename */
|
||||
}
|
||||
|
||||
/* Borrow the codec thread and return the ID */
|
||||
void codec_thread_do_callback(void (*fn)(void), unsigned int *id)
|
||||
|
@ -189,9 +206,9 @@ void codec_thread_do_callback(void (*fn)(void), unsigned int *id)
|
|||
}
|
||||
|
||||
|
||||
/** codec API callbacks */
|
||||
/** --- codec API callbacks --- **/
|
||||
|
||||
static void* codec_get_buffer(size_t *size)
|
||||
static void * codec_get_buffer(size_t *size)
|
||||
{
|
||||
ssize_t s = CODEC_SIZE - codec_size;
|
||||
void *buf = &codecbuf[codec_size];
|
||||
|
@ -215,15 +232,19 @@ static void codec_pcmbuf_insert_callback(
|
|||
int inp_count;
|
||||
char *dest;
|
||||
|
||||
/* Prevent audio from a previous track from playing */
|
||||
if (ci.new_track || ci.stop_codec)
|
||||
return;
|
||||
|
||||
while ((dest = pcmbuf_request_buffer(&out_count)) == NULL)
|
||||
while (1)
|
||||
{
|
||||
if ((dest = pcmbuf_request_buffer(&out_count)) != NULL)
|
||||
break;
|
||||
|
||||
cancel_cpu_boost();
|
||||
sleep(1);
|
||||
if (ci.seek_time || ci.new_track || ci.stop_codec)
|
||||
|
||||
/* It will be awhile before space is available but we want
|
||||
"instant" response to any message */
|
||||
queue_wait_w_tmo(&codec_queue, NULL, HZ/20);
|
||||
|
||||
if (!queue_empty(&codec_queue) &&
|
||||
codec_check_queue__have_msg() < 0)
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -247,62 +268,28 @@ static void codec_pcmbuf_insert_callback(
|
|||
|
||||
count -= inp_count;
|
||||
}
|
||||
} /* codec_pcmbuf_insert_callback */
|
||||
|
||||
static void codec_set_elapsed_callback(unsigned long value)
|
||||
{
|
||||
if (ci.seek_time)
|
||||
return;
|
||||
|
||||
#ifdef AB_REPEAT_ENABLE
|
||||
ab_position_report(value);
|
||||
#endif
|
||||
|
||||
unsigned long latency = pcmbuf_get_latency();
|
||||
if (value < latency)
|
||||
thistrack_id3->elapsed = 0;
|
||||
else
|
||||
{
|
||||
unsigned long elapsed = value - latency;
|
||||
if (elapsed > thistrack_id3->elapsed ||
|
||||
elapsed < thistrack_id3->elapsed - 2)
|
||||
{
|
||||
thistrack_id3->elapsed = elapsed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void codec_set_offset_callback(size_t value)
|
||||
{
|
||||
if (ci.seek_time)
|
||||
return;
|
||||
|
||||
unsigned long latency = pcmbuf_get_latency() * thistrack_id3->bitrate / 8;
|
||||
if (value < latency)
|
||||
thistrack_id3->offset = 0;
|
||||
else
|
||||
thistrack_id3->offset = value - latency;
|
||||
}
|
||||
|
||||
/* helper function, not a callback */
|
||||
static void codec_advance_buffer_counters(size_t amount)
|
||||
static bool codec_advance_buffer_counters(size_t amount)
|
||||
{
|
||||
bufadvance(get_audio_hid(), amount);
|
||||
if (bufadvance(ci.audio_hid, amount) < 0)
|
||||
{
|
||||
ci.curpos = ci.filesize;
|
||||
return false;
|
||||
}
|
||||
|
||||
ci.curpos += amount;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* copy up-to size bytes into ptr and return the actual size copied */
|
||||
static size_t codec_filebuf_callback(void *ptr, size_t size)
|
||||
{
|
||||
ssize_t copy_n;
|
||||
|
||||
if (ci.stop_codec)
|
||||
return 0;
|
||||
|
||||
copy_n = bufread(get_audio_hid(), size, ptr);
|
||||
ssize_t copy_n = bufread(ci.audio_hid, size, ptr);
|
||||
|
||||
/* Nothing requested OR nothing left */
|
||||
if (copy_n == 0)
|
||||
if (copy_n <= 0)
|
||||
return 0;
|
||||
|
||||
/* Update read and other position pointers */
|
||||
|
@ -310,15 +297,15 @@ static size_t codec_filebuf_callback(void *ptr, size_t size)
|
|||
|
||||
/* Return the actual amount of data copied to the buffer */
|
||||
return copy_n;
|
||||
} /* codec_filebuf_callback */
|
||||
}
|
||||
|
||||
static void* codec_request_buffer_callback(size_t *realsize, size_t reqsize)
|
||||
static void * codec_request_buffer_callback(size_t *realsize, size_t reqsize)
|
||||
{
|
||||
size_t copy_n = reqsize;
|
||||
ssize_t ret;
|
||||
void *ptr;
|
||||
|
||||
ret = bufgetdata(get_audio_hid(), reqsize, &ptr);
|
||||
ret = bufgetdata(ci.audio_hid, reqsize, &ptr);
|
||||
if (ret >= 0)
|
||||
copy_n = MIN((size_t)ret, reqsize);
|
||||
else
|
||||
|
@ -329,101 +316,103 @@ static void* codec_request_buffer_callback(size_t *realsize, size_t reqsize)
|
|||
|
||||
*realsize = copy_n;
|
||||
return ptr;
|
||||
} /* codec_request_buffer_callback */
|
||||
}
|
||||
|
||||
static void codec_advance_buffer_callback(size_t amount)
|
||||
{
|
||||
codec_advance_buffer_counters(amount);
|
||||
codec_set_offset_callback(ci.curpos);
|
||||
if (!codec_advance_buffer_counters(amount))
|
||||
return;
|
||||
|
||||
audio_codec_update_offset(ci.curpos);
|
||||
}
|
||||
|
||||
static bool codec_seek_buffer_callback(size_t newpos)
|
||||
{
|
||||
logf("codec_seek_buffer_callback");
|
||||
|
||||
int ret = bufseek(get_audio_hid(), newpos);
|
||||
if (ret == 0) {
|
||||
int ret = bufseek(ci.audio_hid, newpos);
|
||||
if (ret == 0)
|
||||
{
|
||||
ci.curpos = newpos;
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static void codec_seek_complete_callback(void)
|
||||
{
|
||||
struct queue_event ev;
|
||||
|
||||
logf("seek_complete");
|
||||
|
||||
/* Clear DSP */
|
||||
dsp_configure(ci.dsp, DSP_FLUSH, 0);
|
||||
|
||||
/* Post notification to audio thread */
|
||||
LOGFQUEUE("audio > Q_AUDIO_SEEK_COMPLETE");
|
||||
queue_post(&audio_queue, Q_AUDIO_SEEK_COMPLETE, 0);
|
||||
LOGFQUEUE("audio > Q_AUDIO_CODEC_SEEK_COMPLETE");
|
||||
audio_queue_post(Q_AUDIO_CODEC_SEEK_COMPLETE, 0);
|
||||
|
||||
/* Wait for ACK */
|
||||
queue_wait(&codec_queue, &ev);
|
||||
|
||||
/* ACK back in context */
|
||||
codec_queue_ack(Q_AUDIO_SEEK_COMPLETE);
|
||||
}
|
||||
|
||||
static bool codec_request_next_track_callback(void)
|
||||
{
|
||||
struct queue_event ev;
|
||||
|
||||
logf("Request new track");
|
||||
|
||||
audio_set_prev_elapsed(thistrack_id3->elapsed);
|
||||
|
||||
#ifdef AB_REPEAT_ENABLE
|
||||
ab_end_of_track_report();
|
||||
#endif
|
||||
|
||||
if (ci.stop_codec)
|
||||
/* Wait for urgent or go message */
|
||||
do
|
||||
{
|
||||
/* Handle ACK in outer loop */
|
||||
LOGFQUEUE("codec: already stopping");
|
||||
return false;
|
||||
queue_wait(&codec_queue, NULL);
|
||||
}
|
||||
|
||||
trigger_cpu_boost();
|
||||
|
||||
/* Post request to audio thread */
|
||||
LOGFQUEUE("codec > audio Q_AUDIO_CHECK_NEW_TRACK");
|
||||
queue_post(&audio_queue, Q_AUDIO_CHECK_NEW_TRACK, 0);
|
||||
|
||||
/* Wait for ACK */
|
||||
queue_wait(&codec_queue, &ev);
|
||||
|
||||
if (ev.data == Q_CODEC_REQUEST_COMPLETE)
|
||||
{
|
||||
/* Seek to the beginning of the new track because if the struct
|
||||
mp3entry was buffered, "elapsed" might not be zero (if the track has
|
||||
been played already but not unbuffered) */
|
||||
codec_seek_buffer_callback(thistrack_id3->first_frame_offset);
|
||||
}
|
||||
|
||||
/* ACK back in context */
|
||||
codec_queue_ack(Q_AUDIO_CHECK_NEW_TRACK);
|
||||
|
||||
if (ev.data != Q_CODEC_REQUEST_COMPLETE || ci.stop_codec)
|
||||
{
|
||||
LOGFQUEUE("codec <= request failed (%d)", ev.data);
|
||||
return false;
|
||||
}
|
||||
|
||||
LOGFQUEUE("codec <= Q_CODEC_REQEST_COMPLETE");
|
||||
return true;
|
||||
while (codec_check_queue__have_msg() == 0);
|
||||
}
|
||||
|
||||
static void codec_configure_callback(int setting, intptr_t value)
|
||||
{
|
||||
if (!dsp_configure(ci.dsp, setting, value))
|
||||
{ logf("Illegal key:%d", setting); }
|
||||
{
|
||||
logf("Illegal key: %d", setting);
|
||||
}
|
||||
}
|
||||
|
||||
static enum codec_command_action
|
||||
codec_get_command_callback(intptr_t *param)
|
||||
{
|
||||
yield();
|
||||
|
||||
if (LIKELY(queue_empty(&codec_queue)))
|
||||
return CODEC_ACTION_NULL; /* As you were */
|
||||
|
||||
/* Process the message - return requested action and data (if any should
|
||||
be expected) */
|
||||
while (1)
|
||||
{
|
||||
enum codec_command_action action = CODEC_ACTION_NULL;
|
||||
struct queue_event ev;
|
||||
queue_wait(&codec_queue, &ev);
|
||||
|
||||
switch (ev.id)
|
||||
{
|
||||
case Q_CODEC_RUN: /* Already running */
|
||||
LOGFQUEUE("codec < Q_CODEC_RUN");
|
||||
break;
|
||||
|
||||
case Q_CODEC_PAUSE: /* Stay here and wait */
|
||||
LOGFQUEUE("codec < Q_CODEC_PAUSE");
|
||||
codec_queue_ack(Q_CODEC_PAUSE);
|
||||
continue;
|
||||
|
||||
case Q_CODEC_SEEK: /* Audio wants codec to seek */
|
||||
LOGFQUEUE("codec < Q_CODEC_SEEK %ld", ev.data);
|
||||
*param = ev.data;
|
||||
action = CODEC_ACTION_SEEK_TIME;
|
||||
break;
|
||||
|
||||
case Q_CODEC_STOP: /* Must only return 0 in main loop */
|
||||
LOGFQUEUE("codec < Q_CODEC_STOP");
|
||||
action = CODEC_ACTION_HALT;
|
||||
break;
|
||||
|
||||
default: /* This is in error in this context. */
|
||||
ev.id = Q_NULL;
|
||||
logf("codec bad req %ld (%s)", ev.id, __func__);
|
||||
}
|
||||
|
||||
codec_queue_ack(ev.id);
|
||||
return action;
|
||||
}
|
||||
}
|
||||
|
||||
/* Initialize codec API */
|
||||
|
@ -433,119 +422,215 @@ void codec_init_codec_api(void)
|
|||
CODEC_IDX_AUDIO);
|
||||
ci.codec_get_buffer = codec_get_buffer;
|
||||
ci.pcmbuf_insert = codec_pcmbuf_insert_callback;
|
||||
ci.set_elapsed = codec_set_elapsed_callback;
|
||||
ci.set_elapsed = audio_codec_update_elapsed;
|
||||
ci.read_filebuf = codec_filebuf_callback;
|
||||
ci.request_buffer = codec_request_buffer_callback;
|
||||
ci.advance_buffer = codec_advance_buffer_callback;
|
||||
ci.seek_buffer = codec_seek_buffer_callback;
|
||||
ci.seek_complete = codec_seek_complete_callback;
|
||||
ci.request_next_track = codec_request_next_track_callback;
|
||||
ci.set_offset = codec_set_offset_callback;
|
||||
ci.set_offset = audio_codec_update_offset;
|
||||
ci.configure = codec_configure_callback;
|
||||
ci.get_command = codec_get_command_callback;
|
||||
}
|
||||
|
||||
|
||||
/* track change */
|
||||
/** --- CODEC THREAD --- **/
|
||||
|
||||
/** CODEC THREAD */
|
||||
static void codec_thread(void)
|
||||
/* Handle Q_CODEC_LOAD */
|
||||
static void load_codec(const struct codec_load_info *ev_data)
|
||||
{
|
||||
int status = CODEC_ERROR;
|
||||
/* Save a local copy so we can let the audio thread go ASAP */
|
||||
struct codec_load_info data = *ev_data;
|
||||
bool const encoder = type_is_encoder(data.afmt);
|
||||
|
||||
if (codec_type != AFMT_UNKNOWN)
|
||||
{
|
||||
/* Must have unloaded it first */
|
||||
logf("a codec is already loaded");
|
||||
if (data.hid >= 0)
|
||||
bufclose(data.hid);
|
||||
return;
|
||||
}
|
||||
|
||||
trigger_cpu_boost();
|
||||
|
||||
if (!encoder)
|
||||
{
|
||||
/* Do this now because codec may set some things up at load time */
|
||||
dsp_configure(ci.dsp, DSP_RESET, 0);
|
||||
}
|
||||
|
||||
if (data.hid >= 0)
|
||||
{
|
||||
/* First try buffer load */
|
||||
status = codec_load_buf(data.hid, &ci);
|
||||
bufclose(data.hid);
|
||||
}
|
||||
|
||||
if (status < 0)
|
||||
{
|
||||
/* Either not a valid handle or the buffer method failed */
|
||||
const char *codec_fn = get_codec_filename(data.afmt);
|
||||
if (codec_fn)
|
||||
{
|
||||
#ifdef HAVE_IO_PRIORITY
|
||||
buf_back_off_storage(true);
|
||||
#endif
|
||||
status = codec_load_file(codec_fn, &ci);
|
||||
#ifdef HAVE_IO_PRIORITY
|
||||
buf_back_off_storage(false);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
if (status >= 0)
|
||||
{
|
||||
codec_type = data.afmt;
|
||||
codec_queue_ack(Q_CODEC_LOAD);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Failed - get rid of it */
|
||||
unload_codec();
|
||||
}
|
||||
|
||||
/* Handle Q_CODEC_RUN */
|
||||
static void run_codec(void)
|
||||
{
|
||||
bool const encoder = type_is_encoder(codec_type);
|
||||
int status;
|
||||
|
||||
if (codec_type == AFMT_UNKNOWN)
|
||||
{
|
||||
logf("no codec to run");
|
||||
return;
|
||||
}
|
||||
|
||||
codec_queue_ack(Q_CODEC_RUN);
|
||||
|
||||
trigger_cpu_boost();
|
||||
|
||||
if (!encoder)
|
||||
{
|
||||
/* This will be either the initial buffered offset or where it left off
|
||||
if it remained buffered and we're skipping back to it and it is best
|
||||
to have ci.curpos in sync with the handle's read position - it's the
|
||||
codec's responsibility to ensure it has the correct positions -
|
||||
playback is sorta dumb and only has a vague idea about what to
|
||||
buffer based upon what metadata has to say */
|
||||
ci.curpos = bufftell(ci.audio_hid);
|
||||
|
||||
/* Pin the codec's audio data in place */
|
||||
buf_pin_handle(ci.audio_hid, true);
|
||||
}
|
||||
|
||||
status = codec_run_proc();
|
||||
|
||||
if (!encoder)
|
||||
{
|
||||
/* Codec is done with it - let it move */
|
||||
buf_pin_handle(ci.audio_hid, false);
|
||||
|
||||
/* Notify audio that we're done for better or worse - advise of the
|
||||
status */
|
||||
LOGFQUEUE("codec > audio Q_AUDIO_CODEC_COMPLETE: %d", status);
|
||||
audio_queue_post(Q_AUDIO_CODEC_COMPLETE, status);
|
||||
}
|
||||
}
|
||||
|
||||
/* Handle Q_CODEC_SEEK */
|
||||
static void seek_codec(unsigned long time)
|
||||
{
|
||||
if (codec_type == AFMT_UNKNOWN)
|
||||
{
|
||||
logf("no codec to seek");
|
||||
codec_queue_ack(Q_CODEC_SEEK);
|
||||
codec_seek_complete_callback();
|
||||
return;
|
||||
}
|
||||
|
||||
/* Post it up one level */
|
||||
queue_post(&codec_queue, Q_CODEC_SEEK, time);
|
||||
codec_queue_ack(Q_CODEC_SEEK);
|
||||
|
||||
/* Have to run it again */
|
||||
run_codec();
|
||||
}
|
||||
|
||||
/* Handle Q_CODEC_UNLOAD */
|
||||
static void unload_codec(void)
|
||||
{
|
||||
/* Tell codec to clean up */
|
||||
codec_type = AFMT_UNKNOWN;
|
||||
codec_close();
|
||||
}
|
||||
|
||||
/* Handle Q_CODEC_DO_CALLBACK */
|
||||
static void do_callback(void (* callback)(void))
|
||||
{
|
||||
codec_queue_ack(Q_CODEC_DO_CALLBACK);
|
||||
|
||||
if (callback)
|
||||
{
|
||||
cpucache_commit_discard();
|
||||
callback();
|
||||
cpucache_commit();
|
||||
}
|
||||
}
|
||||
|
||||
/* Codec thread function */
|
||||
static void NORETURN_ATTR codec_thread(void)
|
||||
{
|
||||
struct queue_event ev;
|
||||
|
||||
|
||||
while (1)
|
||||
{
|
||||
int status = CODEC_OK;
|
||||
void *handle = NULL;
|
||||
int hid;
|
||||
const char *codec_fn;
|
||||
|
||||
#ifdef HAVE_CROSSFADE
|
||||
if (!pcmbuf_is_crossfade_active())
|
||||
#endif
|
||||
{
|
||||
cancel_cpu_boost();
|
||||
}
|
||||
|
||||
queue_wait(&codec_queue, &ev);
|
||||
|
||||
switch (ev.id)
|
||||
{
|
||||
case Q_CODEC_LOAD_DISK:
|
||||
LOGFQUEUE("codec < Q_CODEC_LOAD_DISK");
|
||||
codec_fn = get_codec_filename(ev.data);
|
||||
if (!codec_fn)
|
||||
break;
|
||||
#ifdef AUDIO_HAVE_RECORDING
|
||||
if (ev.data & CODEC_TYPE_ENCODER)
|
||||
{
|
||||
ev.id = Q_ENCODER_LOAD_DISK;
|
||||
handle = codec_load_file(codec_fn, &ci);
|
||||
if (handle)
|
||||
codec_queue_ack(Q_ENCODER_LOAD_DISK);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
codec_queue_ack(Q_CODEC_LOAD_DISK);
|
||||
handle = codec_load_file(codec_fn, &ci);
|
||||
}
|
||||
break;
|
||||
|
||||
case Q_CODEC_LOAD:
|
||||
LOGFQUEUE("codec < Q_CODEC_LOAD");
|
||||
codec_queue_ack(Q_CODEC_LOAD);
|
||||
hid = (int)ev.data;
|
||||
handle = codec_load_buf(hid, &ci);
|
||||
bufclose(hid);
|
||||
load_codec((const struct codec_load_info *)ev.data);
|
||||
break;
|
||||
|
||||
case Q_CODEC_RUN:
|
||||
LOGFQUEUE("codec < Q_CODEC_RUN");
|
||||
run_codec();
|
||||
break;
|
||||
|
||||
case Q_CODEC_PAUSE:
|
||||
LOGFQUEUE("codec < Q_CODEC_PAUSE");
|
||||
break;
|
||||
|
||||
case Q_CODEC_SEEK:
|
||||
LOGFQUEUE("codec < Q_CODEC_SEEK: %lu", (unsigned long)ev.data);
|
||||
seek_codec(ev.data);
|
||||
break;
|
||||
|
||||
case Q_CODEC_UNLOAD:
|
||||
LOGFQUEUE("codec < Q_CODEC_UNLOAD");
|
||||
unload_codec();
|
||||
break;
|
||||
|
||||
case Q_CODEC_DO_CALLBACK:
|
||||
LOGFQUEUE("codec < Q_CODEC_DO_CALLBACK");
|
||||
codec_queue_ack(Q_CODEC_DO_CALLBACK);
|
||||
if ((void*)ev.data != NULL)
|
||||
{
|
||||
cpucache_commit_discard();
|
||||
((void (*)(void))ev.data)();
|
||||
cpucache_commit();
|
||||
}
|
||||
do_callback((void (*)(void))ev.data);
|
||||
break;
|
||||
|
||||
default:
|
||||
LOGFQUEUE("codec < default : %ld", ev.id);
|
||||
}
|
||||
|
||||
if (handle)
|
||||
{
|
||||
/* Codec loaded - call the entrypoint */
|
||||
yield();
|
||||
logf("codec running");
|
||||
status = codec_begin(handle);
|
||||
logf("codec stopped");
|
||||
codec_close(handle);
|
||||
current_codectype = AFMT_UNKNOWN;
|
||||
|
||||
if (ci.stop_codec)
|
||||
status = CODEC_OK;
|
||||
}
|
||||
|
||||
switch (ev.id)
|
||||
{
|
||||
#ifdef AUDIO_HAVE_RECORDING
|
||||
case Q_ENCODER_LOAD_DISK:
|
||||
#endif
|
||||
case Q_CODEC_LOAD_DISK:
|
||||
case Q_CODEC_LOAD:
|
||||
/* Notify about the status */
|
||||
if (!handle)
|
||||
status = CODEC_ERROR;
|
||||
LOGFQUEUE("codec > audio notify status: %d", status);
|
||||
queue_post(&audio_queue, ev.id, status);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** --- Miscellaneous external interfaces -- **/
|
||||
|
||||
/* Create the codec thread and init kernel objects */
|
||||
void make_codec_thread(void)
|
||||
{
|
||||
queue_init(&codec_queue, false);
|
||||
|
@ -558,78 +643,86 @@ void make_codec_thread(void)
|
|||
codec_thread_id);
|
||||
}
|
||||
|
||||
/* Unfreeze the codec thread */
|
||||
void codec_thread_resume(void)
|
||||
{
|
||||
thread_thaw(codec_thread_id);
|
||||
}
|
||||
|
||||
/* Is the current thread the codec thread? */
|
||||
bool is_codec_thread(void)
|
||||
{
|
||||
return thread_self() == codec_thread_id;
|
||||
}
|
||||
|
||||
#ifdef HAVE_PRIORITY_SCHEDULING
|
||||
/* Obtain codec thread's current priority */
|
||||
int codec_thread_get_priority(void)
|
||||
{
|
||||
return thread_get_priority(codec_thread_id);
|
||||
}
|
||||
|
||||
/* Set the codec thread's priority and return the old value */
|
||||
int codec_thread_set_priority(int priority)
|
||||
{
|
||||
return thread_set_priority(codec_thread_id, priority);
|
||||
}
|
||||
#endif /* HAVE_PRIORITY_SCHEDULING */
|
||||
|
||||
/* functions for audio thread use */
|
||||
intptr_t codec_ack_msg(intptr_t data, bool stop_codec)
|
||||
{
|
||||
intptr_t resp;
|
||||
LOGFQUEUE("codec >| Q_CODEC_ACK: %d", data);
|
||||
if (stop_codec)
|
||||
ci.stop_codec = true;
|
||||
resp = codec_queue_send(Q_CODEC_ACK, data);
|
||||
if (stop_codec)
|
||||
codec_stop();
|
||||
LOGFQUEUE(" ack: %ld", resp);
|
||||
return resp;
|
||||
}
|
||||
|
||||
/** --- Functions for audio thread use --- **/
|
||||
|
||||
/* Load a decoder or encoder and set the format type */
|
||||
bool codec_load(int hid, int cod_spec)
|
||||
{
|
||||
bool retval = false;
|
||||
struct codec_load_info parm = { hid, cod_spec };
|
||||
|
||||
ci.stop_codec = false;
|
||||
current_codectype = cod_spec;
|
||||
|
||||
if (hid >= 0)
|
||||
{
|
||||
LOGFQUEUE("audio >| codec Q_CODEC_LOAD: %d", hid);
|
||||
retval = codec_queue_send(Q_CODEC_LOAD, hid) != Q_NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGFQUEUE("audio >| codec Q_CODEC_LOAD_DISK: %d", cod_spec);
|
||||
retval = codec_queue_send(Q_CODEC_LOAD_DISK, cod_spec) != Q_NULL;
|
||||
}
|
||||
|
||||
if (!retval)
|
||||
{
|
||||
ci.stop_codec = true;
|
||||
current_codectype = AFMT_UNKNOWN;
|
||||
}
|
||||
|
||||
return retval;
|
||||
LOGFQUEUE("audio >| codec Q_CODEC_LOAD: %d, %d", hid, cod_spec);
|
||||
return codec_queue_send(Q_CODEC_LOAD, (intptr_t)&parm) != 0;
|
||||
}
|
||||
|
||||
/* Begin decoding the current file */
|
||||
void codec_go(void)
|
||||
{
|
||||
LOGFQUEUE("audio >| codec Q_CODEC_RUN");
|
||||
codec_queue_send(Q_CODEC_RUN, 0);
|
||||
}
|
||||
|
||||
/* Instruct the codec to seek to the specified time (should be properly
|
||||
paused or stopped first to avoid possible buffering deadlock) */
|
||||
void codec_seek(long time)
|
||||
{
|
||||
LOGFQUEUE("audio > codec Q_CODEC_SEEK: %ld", time);
|
||||
codec_queue_send(Q_CODEC_SEEK, time);
|
||||
}
|
||||
|
||||
/* Pause the codec and make it wait for further instructions inside the
|
||||
command callback */
|
||||
bool codec_pause(void)
|
||||
{
|
||||
LOGFQUEUE("audio >| codec Q_CODEC_PAUSE");
|
||||
return codec_queue_send(Q_CODEC_PAUSE, 0) != Q_NULL;
|
||||
}
|
||||
|
||||
/* Stop codec if running - codec stays resident if loaded */
|
||||
void codec_stop(void)
|
||||
{
|
||||
ci.stop_codec = true;
|
||||
/* Wait until it's in the main loop */
|
||||
while (codec_ack_msg(0, false) != Q_NULL);
|
||||
current_codectype = AFMT_UNKNOWN;
|
||||
LOGFQUEUE("audio >| codec Q_CODEC_STOP");
|
||||
while (codec_queue_send(Q_CODEC_STOP, 0) != Q_NULL);
|
||||
}
|
||||
|
||||
/* Call the codec's exit routine and close all references */
|
||||
void codec_unload(void)
|
||||
{
|
||||
codec_stop();
|
||||
LOGFQUEUE("audio >| codec Q_CODEC_UNLOAD");
|
||||
codec_queue_send(Q_CODEC_UNLOAD, 0);
|
||||
}
|
||||
|
||||
/* Return the afmt type of the loaded codec - sticks until calling
|
||||
codec_unload unless initial load failed */
|
||||
int codec_loaded(void)
|
||||
{
|
||||
return current_codectype;
|
||||
return codec_type;
|
||||
}
|
||||
|
|
|
@ -25,7 +25,6 @@
|
|||
#include <stdbool.h>
|
||||
|
||||
/* codec identity */
|
||||
int get_codec_base_type(int type);
|
||||
const char *get_codec_filename(int cod_spec);
|
||||
|
||||
/* codec thread */
|
||||
|
@ -44,10 +43,14 @@ int codec_thread_set_priority(int priority);
|
|||
#endif
|
||||
|
||||
/* codec commands - on audio thread only! */
|
||||
intptr_t codec_ack_msg(intptr_t data, bool stop_codec);
|
||||
bool codec_load(int hid, int cod_spec);
|
||||
void codec_go(void);
|
||||
bool codec_pause(void);
|
||||
void codec_seek(long time);
|
||||
void codec_stop(void);
|
||||
void codec_unload(void);
|
||||
int codec_loaded(void);
|
||||
|
||||
/* */
|
||||
|
||||
#endif /* _CODEC_THREAD_H */
|
||||
|
|
|
@ -82,10 +82,7 @@ struct codec_api ci = {
|
|||
0, /* filesize */
|
||||
0, /* curpos */
|
||||
NULL, /* id3 */
|
||||
NULL, /* taginfo_ready */
|
||||
false, /* stop_codec */
|
||||
0, /* new_track */
|
||||
0, /* seek_time */
|
||||
ERR_HANDLE_NOT_FOUND, /* audio_hid */
|
||||
NULL, /* struct dsp_config *dsp */
|
||||
NULL, /* codec_get_buffer */
|
||||
NULL, /* pcmbuf_insert */
|
||||
|
@ -95,9 +92,9 @@ struct codec_api ci = {
|
|||
NULL, /* advance_buffer */
|
||||
NULL, /* seek_buffer */
|
||||
NULL, /* seek_complete */
|
||||
NULL, /* request_next_track */
|
||||
NULL, /* set_offset */
|
||||
NULL, /* configure */
|
||||
NULL, /* get_command */
|
||||
|
||||
/* kernel/ system */
|
||||
#if defined(CPU_ARM) && CONFIG_PLATFORM & PLATFORM_NATIVE
|
||||
|
@ -174,10 +171,16 @@ void codec_get_full_path(char *path, const char *codec_root_fn)
|
|||
CODECS_DIR, codec_root_fn);
|
||||
}
|
||||
|
||||
static void * codec_load_ram(void *handle, struct codec_api *api)
|
||||
/** codec loading and call interface **/
|
||||
static void *curr_handle = NULL;
|
||||
static struct codec_header *c_hdr = NULL;
|
||||
|
||||
static int codec_load_ram(struct codec_api *api)
|
||||
{
|
||||
struct codec_header *c_hdr = lc_get_header(handle);
|
||||
struct lc_header *hdr = c_hdr ? &c_hdr->lc_hdr : NULL;
|
||||
struct lc_header *hdr;
|
||||
|
||||
c_hdr = lc_get_header(curr_handle);
|
||||
hdr = c_hdr ? &c_hdr->lc_hdr : NULL;
|
||||
|
||||
if (hdr == NULL
|
||||
|| (hdr->magic != CODEC_MAGIC
|
||||
|
@ -193,15 +196,17 @@ static void * codec_load_ram(void *handle, struct codec_api *api)
|
|||
)
|
||||
{
|
||||
logf("codec header error");
|
||||
lc_close(handle);
|
||||
return NULL;
|
||||
lc_close(curr_handle);
|
||||
curr_handle = NULL;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
if (hdr->api_version > CODEC_API_VERSION
|
||||
|| hdr->api_version < CODEC_MIN_API_VERSION) {
|
||||
logf("codec api version error");
|
||||
lc_close(handle);
|
||||
return NULL;
|
||||
lc_close(curr_handle);
|
||||
curr_handle = NULL;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
#if (CONFIG_PLATFORM & PLATFORM_NATIVE)
|
||||
|
@ -212,63 +217,66 @@ static void * codec_load_ram(void *handle, struct codec_api *api)
|
|||
|
||||
*(c_hdr->api) = api;
|
||||
|
||||
return handle;
|
||||
logf("Codec: calling entrypoint");
|
||||
return c_hdr->entry_point(CODEC_LOAD);
|
||||
}
|
||||
|
||||
void * codec_load_buf(int hid, struct codec_api *api)
|
||||
int codec_load_buf(int hid, struct codec_api *api)
|
||||
{
|
||||
int rc;
|
||||
void *handle;
|
||||
rc = bufread(hid, CODEC_SIZE, codecbuf);
|
||||
int rc = bufread(hid, CODEC_SIZE, codecbuf);
|
||||
|
||||
if (rc < 0) {
|
||||
logf("Codec: cannot read buf handle");
|
||||
return NULL;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
handle = lc_open_from_mem(codecbuf, rc);
|
||||
curr_handle = lc_open_from_mem(codecbuf, rc);
|
||||
|
||||
if (handle == NULL) {
|
||||
logf("error loading codec");
|
||||
return NULL;
|
||||
if (curr_handle == NULL) {
|
||||
logf("Codec: load error");
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
return codec_load_ram(handle, api);
|
||||
return codec_load_ram(api);
|
||||
}
|
||||
|
||||
void * codec_load_file(const char *plugin, struct codec_api *api)
|
||||
int codec_load_file(const char *plugin, struct codec_api *api)
|
||||
{
|
||||
char path[MAX_PATH];
|
||||
void *handle;
|
||||
|
||||
codec_get_full_path(path, plugin);
|
||||
|
||||
handle = lc_open(path, codecbuf, CODEC_SIZE);
|
||||
curr_handle = lc_open(path, codecbuf, CODEC_SIZE);
|
||||
|
||||
if (handle == NULL) {
|
||||
if (curr_handle == NULL) {
|
||||
logf("Codec: cannot read file");
|
||||
return NULL;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
return codec_load_ram(handle, api);
|
||||
return codec_load_ram(api);
|
||||
}
|
||||
|
||||
int codec_begin(void *handle)
|
||||
int codec_run_proc(void)
|
||||
{
|
||||
int status = CODEC_ERROR;
|
||||
struct codec_header *c_hdr;
|
||||
if (curr_handle == NULL) {
|
||||
logf("Codec: no codec to run");
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
c_hdr = lc_get_header(handle);
|
||||
logf("Codec: entering run state");
|
||||
return c_hdr->run_proc();
|
||||
}
|
||||
|
||||
if (c_hdr != NULL) {
|
||||
logf("Codec: calling entry_point");
|
||||
status = c_hdr->entry_point();
|
||||
int codec_close(void)
|
||||
{
|
||||
int status = CODEC_OK;
|
||||
|
||||
if (curr_handle != NULL) {
|
||||
logf("Codec: cleaning up");
|
||||
status = c_hdr->entry_point(CODEC_UNLOAD);
|
||||
lc_close(curr_handle);
|
||||
curr_handle = NULL;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
void codec_close(void *handle)
|
||||
{
|
||||
if (handle)
|
||||
lc_close(handle);
|
||||
}
|
||||
|
|
|
@ -75,12 +75,18 @@
|
|||
#define CODEC_ENC_MAGIC 0x52454E43 /* RENC */
|
||||
|
||||
/* increase this every time the api struct changes */
|
||||
#define CODEC_API_VERSION 41
|
||||
#define CODEC_API_VERSION 42
|
||||
|
||||
/* update this to latest version if a change to the api struct breaks
|
||||
backwards compatibility (and please take the opportunity to sort in any
|
||||
new function which are "waiting" at the end of the function table) */
|
||||
#define CODEC_MIN_API_VERSION 41
|
||||
#define CODEC_MIN_API_VERSION 42
|
||||
|
||||
/* reasons for calling codec main entrypoint */
|
||||
enum codec_entry_call_reason {
|
||||
CODEC_LOAD = 0,
|
||||
CODEC_UNLOAD
|
||||
};
|
||||
|
||||
/* codec return codes */
|
||||
enum codec_status {
|
||||
|
@ -88,6 +94,13 @@ enum codec_status {
|
|||
CODEC_ERROR = -1,
|
||||
};
|
||||
|
||||
/* codec command action codes */
|
||||
enum codec_command_action {
|
||||
CODEC_ACTION_HALT = -1,
|
||||
CODEC_ACTION_NULL = 0,
|
||||
CODEC_ACTION_SEEK_TIME = 1,
|
||||
};
|
||||
|
||||
/* NOTE: To support backwards compatibility, only add new functions at
|
||||
the end of the structure. Every time you add a new function,
|
||||
remember to increase CODEC_API_VERSION. If you make changes to the
|
||||
|
@ -95,23 +108,11 @@ enum codec_status {
|
|||
version
|
||||
*/
|
||||
struct codec_api {
|
||||
|
||||
off_t filesize; /* Total file length */
|
||||
off_t curpos; /* Current buffer position */
|
||||
|
||||
/* For gapless mp3 */
|
||||
struct mp3entry *id3; /* TAG metadata pointer */
|
||||
bool *taginfo_ready; /* Is metadata read */
|
||||
|
||||
/* Codec should periodically check if stop_codec is set to true.
|
||||
In case it is, codec must return immediately */
|
||||
volatile bool stop_codec;
|
||||
/* Codec should periodically check if new_track is non zero.
|
||||
When it is, the codec should request a new track. */
|
||||
volatile int new_track;
|
||||
/* If seek_time != 0, codec should seek to that song position (in ms)
|
||||
if codec supports seeking. */
|
||||
volatile long seek_time;
|
||||
int audio_hid; /* Current audio handle */
|
||||
|
||||
/* The dsp instance to be used for audio output */
|
||||
struct dsp_config *dsp;
|
||||
|
@ -138,14 +139,12 @@ struct codec_api {
|
|||
bool (*seek_buffer)(size_t newpos);
|
||||
/* Codec should call this function when it has done the seeking. */
|
||||
void (*seek_complete)(void);
|
||||
/* Request file change from file buffer. Returns true is next
|
||||
track is available and changed. If return value is false,
|
||||
codec should exit immediately with PLUGIN_OK status. */
|
||||
bool (*request_next_track)(void);
|
||||
|
||||
/* Update the current position */
|
||||
void (*set_offset)(size_t value);
|
||||
/* Configure different codec buffer parameters. */
|
||||
void (*configure)(int setting, intptr_t value);
|
||||
/* Obtain command action on what to do next */
|
||||
enum codec_command_action (*get_command)(intptr_t *param);
|
||||
|
||||
/* kernel/ system */
|
||||
#if defined(CPU_ARM) && CONFIG_PLATFORM & PLATFORM_NATIVE
|
||||
|
@ -231,7 +230,8 @@ struct codec_api {
|
|||
/* codec header */
|
||||
struct codec_header {
|
||||
struct lc_header lc_hdr; /* must be first */
|
||||
enum codec_status(*entry_point)(void);
|
||||
enum codec_status(*entry_point)(enum codec_entry_call_reason reason);
|
||||
enum codec_status(*run_proc)(void);
|
||||
struct codec_api **api;
|
||||
};
|
||||
|
||||
|
@ -248,13 +248,15 @@ extern unsigned char plugin_end_addr[];
|
|||
const struct codec_header __header \
|
||||
__attribute__ ((section (".header")))= { \
|
||||
{ CODEC_MAGIC, TARGET_ID, CODEC_API_VERSION, \
|
||||
plugin_start_addr, plugin_end_addr }, codec_start, &ci };
|
||||
plugin_start_addr, plugin_end_addr }, codec_start, \
|
||||
codec_run, &ci };
|
||||
/* encoders */
|
||||
#define CODEC_ENC_HEADER \
|
||||
const struct codec_header __header \
|
||||
__attribute__ ((section (".header")))= { \
|
||||
{ CODEC_ENC_MAGIC, TARGET_ID, CODEC_API_VERSION, \
|
||||
plugin_start_addr, plugin_end_addr }, codec_start, &ci };
|
||||
plugin_start_addr, plugin_end_addr }, codec_start, \
|
||||
codec_run, &ci };
|
||||
|
||||
#else /* def SIMULATOR */
|
||||
/* decoders */
|
||||
|
@ -262,12 +264,12 @@ extern unsigned char plugin_end_addr[];
|
|||
const struct codec_header __header \
|
||||
__attribute__((visibility("default"))) = { \
|
||||
{ CODEC_MAGIC, TARGET_ID, CODEC_API_VERSION, NULL, NULL }, \
|
||||
codec_start, &ci };
|
||||
codec_start, codec_run, &ci };
|
||||
/* encoders */
|
||||
#define CODEC_ENC_HEADER \
|
||||
const struct codec_header __header = { \
|
||||
{ CODEC_ENC_MAGIC, TARGET_ID, CODEC_API_VERSION, NULL, NULL }, \
|
||||
codec_start, &ci };
|
||||
codec_start, codec_run, &ci };
|
||||
#endif /* SIMULATOR */
|
||||
#endif /* CODEC */
|
||||
|
||||
|
@ -276,13 +278,14 @@ extern unsigned char plugin_end_addr[];
|
|||
void codec_get_full_path(char *path, const char *codec_root_fn);
|
||||
|
||||
/* defined by the codec loader (codec.c) */
|
||||
void * codec_load_buf(int hid, struct codec_api *api);
|
||||
void * codec_load_file(const char* codec, struct codec_api *api);
|
||||
int codec_begin(void *handle);
|
||||
void codec_close(void *handle);
|
||||
int codec_load_buf(int hid, struct codec_api *api);
|
||||
int codec_load_file(const char* codec, struct codec_api *api);
|
||||
int codec_run_proc(void);
|
||||
int codec_halt(void);
|
||||
int codec_close(void);
|
||||
|
||||
/* defined by the codec */
|
||||
enum codec_status codec_start(void);
|
||||
enum codec_status codec_main(void);
|
||||
enum codec_status codec_start(enum codec_entry_call_reason reason);
|
||||
enum codec_status codec_run(void);
|
||||
|
||||
#endif
|
||||
#endif /* _CODECS_H_ */
|
||||
|
|
|
@ -116,27 +116,31 @@ static void a52_decode_data(uint8_t *start, uint8_t *end)
|
|||
}
|
||||
|
||||
/* this is the codec entry point */
|
||||
enum codec_status codec_main(void)
|
||||
enum codec_status codec_main(enum codec_entry_call_reason reason)
|
||||
{
|
||||
if (reason == CODEC_LOAD) {
|
||||
/* Generic codec initialisation */
|
||||
ci->configure(DSP_SET_STEREO_MODE, STEREO_NONINTERLEAVED);
|
||||
ci->configure(DSP_SET_SAMPLE_DEPTH, 28);
|
||||
}
|
||||
else if (reason == CODEC_UNLOAD) {
|
||||
if (state)
|
||||
a52_free(state);
|
||||
}
|
||||
|
||||
return CODEC_OK;
|
||||
}
|
||||
|
||||
/* this is called for each file to process */
|
||||
enum codec_status codec_run(void)
|
||||
{
|
||||
size_t n;
|
||||
unsigned char *filebuf;
|
||||
int sample_loc;
|
||||
int retval;
|
||||
intptr_t param;
|
||||
|
||||
/* Generic codec initialisation */
|
||||
ci->configure(DSP_SET_STEREO_MODE, STEREO_NONINTERLEAVED);
|
||||
ci->configure(DSP_SET_SAMPLE_DEPTH, 28);
|
||||
|
||||
next_track:
|
||||
retval = CODEC_OK;
|
||||
|
||||
if (codec_init()) {
|
||||
retval = CODEC_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (codec_wait_taginfo() != 0)
|
||||
goto request_next_track;
|
||||
if (codec_init())
|
||||
return CODEC_ERROR;
|
||||
|
||||
ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency);
|
||||
codec_set_replaygain(ci->id3);
|
||||
|
@ -153,15 +157,18 @@ next_track:
|
|||
}
|
||||
}
|
||||
else {
|
||||
ci->seek_buffer(ci->id3->first_frame_offset);
|
||||
samplesdone = 0;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
if (ci->stop_codec || ci->new_track)
|
||||
enum codec_command_action action = ci->get_command(¶m);
|
||||
|
||||
if (action == CODEC_ACTION_HALT)
|
||||
break;
|
||||
|
||||
if (ci->seek_time) {
|
||||
sample_loc = (ci->seek_time - 1)/1000 * ci->id3->frequency;
|
||||
if (action == CODEC_ACTION_SEEK_TIME) {
|
||||
sample_loc = param/1000 * ci->id3->frequency;
|
||||
|
||||
if (ci->seek_buffer((sample_loc/A52_SAMPLESPERFRAME)*ci->id3->bytesperframe)) {
|
||||
samplesdone = sample_loc;
|
||||
|
@ -179,11 +186,5 @@ next_track:
|
|||
ci->advance_buffer(n);
|
||||
}
|
||||
|
||||
request_next_track:
|
||||
if (ci->request_next_track())
|
||||
goto next_track;
|
||||
|
||||
exit:
|
||||
a52_free(state);
|
||||
return retval;
|
||||
return CODEC_OK;
|
||||
}
|
||||
|
|
|
@ -124,36 +124,44 @@ static void a52_decode_data(uint8_t *start, uint8_t *end)
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/* this is the codec entry point */
|
||||
enum codec_status codec_main(void)
|
||||
enum codec_status codec_main(enum codec_entry_call_reason reason)
|
||||
{
|
||||
size_t n;
|
||||
uint8_t *filebuf;
|
||||
int retval, consumed, packet_offset;
|
||||
int playback_on = -1;
|
||||
size_t resume_offset;
|
||||
|
||||
if (reason == CODEC_LOAD) {
|
||||
/* Generic codec initialisation */
|
||||
ci->configure(DSP_SET_STEREO_MODE, STEREO_NONINTERLEAVED);
|
||||
ci->configure(DSP_SET_SAMPLE_DEPTH, 28);
|
||||
|
||||
next_track:
|
||||
retval = CODEC_OK;
|
||||
|
||||
if (codec_init()) {
|
||||
retval = CODEC_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
else if (reason == CODEC_UNLOAD) {
|
||||
if (state)
|
||||
a52_free(state);
|
||||
}
|
||||
|
||||
if (codec_wait_taginfo() != 0)
|
||||
goto request_next_track;
|
||||
return CODEC_OK;
|
||||
}
|
||||
|
||||
/* this is called for each file to process */
|
||||
enum codec_status codec_run(void)
|
||||
{
|
||||
size_t n;
|
||||
uint8_t *filebuf;
|
||||
int consumed, packet_offset;
|
||||
int playback_on = -1;
|
||||
size_t resume_offset;
|
||||
intptr_t param;
|
||||
enum codec_command_action action = CODEC_ACTION_NULL;
|
||||
|
||||
if (codec_init()) {
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
resume_offset = ci->id3->offset;
|
||||
|
||||
ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency);
|
||||
codec_set_replaygain(ci->id3);
|
||||
|
||||
ci->seek_buffer(ci->id3->first_frame_offset);
|
||||
|
||||
/* Intializations */
|
||||
state = a52_init(0);
|
||||
ci->memset(&rmctx,0,sizeof(RMContext));
|
||||
|
@ -165,26 +173,34 @@ next_track:
|
|||
resume_offset -= rmctx.data_offset + DATA_HEADER_SIZE;
|
||||
/* put number of subpackets to skip in resume_offset */
|
||||
resume_offset /= (rmctx.block_align + PACKET_HEADER_SIZE);
|
||||
ci->seek_time = (int)resume_offset * ((rmctx.block_align * 8 * 1000)/rmctx.bit_rate);
|
||||
param = (int)resume_offset * ((rmctx.block_align * 8 * 1000)/rmctx.bit_rate);
|
||||
action = CODEC_ACTION_SEEK_TIME;
|
||||
}
|
||||
|
||||
else {
|
||||
/* Seek to the first packet */
|
||||
ci->advance_buffer(rmctx.data_offset + DATA_HEADER_SIZE );
|
||||
}
|
||||
|
||||
/* The main decoding loop */
|
||||
while((unsigned)rmctx.audio_pkt_cnt < rmctx.nb_packets) {
|
||||
ci->yield();
|
||||
if (ci->stop_codec || ci->new_track)
|
||||
if (action == CODEC_ACTION_NULL)
|
||||
action = ci->get_command(¶m);
|
||||
|
||||
if (action == CODEC_ACTION_HALT)
|
||||
break;
|
||||
|
||||
if (ci->seek_time) {
|
||||
packet_offset = ci->seek_time / ((rmctx.block_align*8*1000)/rmctx.bit_rate);
|
||||
ci->seek_buffer(rmctx.data_offset + DATA_HEADER_SIZE + packet_offset*(rmctx.block_align + PACKET_HEADER_SIZE));
|
||||
if (action == CODEC_ACTION_SEEK_TIME) {
|
||||
packet_offset = param / ((rmctx.block_align*8*1000)/rmctx.bit_rate);
|
||||
ci->seek_buffer(rmctx.data_offset + DATA_HEADER_SIZE +
|
||||
packet_offset*(rmctx.block_align + PACKET_HEADER_SIZE));
|
||||
rmctx.audio_pkt_cnt = packet_offset;
|
||||
samplesdone = (rmctx.sample_rate/1000 * ci->seek_time);
|
||||
samplesdone = (rmctx.sample_rate/1000 * param);
|
||||
ci->set_elapsed(samplesdone/(frequency/1000));
|
||||
ci->seek_complete();
|
||||
}
|
||||
|
||||
action = CODEC_ACTION_NULL;
|
||||
|
||||
filebuf = ci->request_buffer(&n, rmctx.block_align + PACKET_HEADER_SIZE);
|
||||
consumed = rm_get_packet(&filebuf, &rmctx, &pkt);
|
||||
|
||||
|
@ -195,8 +211,7 @@ next_track:
|
|||
return CODEC_ERROR;
|
||||
}
|
||||
else {
|
||||
retval = CODEC_OK;
|
||||
goto exit;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -205,11 +220,5 @@ next_track:
|
|||
ci->advance_buffer(pkt.length);
|
||||
}
|
||||
|
||||
request_next_track:
|
||||
if (ci->request_next_track())
|
||||
goto next_track;
|
||||
|
||||
exit:
|
||||
a52_free(state);
|
||||
return retval;
|
||||
return CODEC_OK;
|
||||
}
|
||||
|
|
|
@ -33,7 +33,19 @@ CODEC_HEADER
|
|||
#define FAAD_BYTE_BUFFER_SIZE (2048-12)
|
||||
|
||||
/* this is the codec entry point */
|
||||
enum codec_status codec_main(void)
|
||||
enum codec_status codec_main(enum codec_entry_call_reason reason)
|
||||
{
|
||||
if (reason == CODEC_LOAD) {
|
||||
/* Generic codec initialisation */
|
||||
ci->configure(DSP_SET_STEREO_MODE, STEREO_NONINTERLEAVED);
|
||||
ci->configure(DSP_SET_SAMPLE_DEPTH, 29);
|
||||
}
|
||||
|
||||
return CODEC_OK;
|
||||
}
|
||||
|
||||
/* this is called for each file to process */
|
||||
enum codec_status codec_run(void)
|
||||
{
|
||||
/* Note that when dealing with QuickTime/MPEG4 files, terminology is
|
||||
* a bit confusing. Files with sound are split up in chunks, where
|
||||
|
@ -59,25 +71,15 @@ enum codec_status codec_main(void)
|
|||
uint32_t sbr_fac = 1;
|
||||
unsigned char c = 0;
|
||||
void *ret;
|
||||
|
||||
/* Generic codec initialisation */
|
||||
ci->configure(DSP_SET_STEREO_MODE, STEREO_NONINTERLEAVED);
|
||||
ci->configure(DSP_SET_SAMPLE_DEPTH, 29);
|
||||
|
||||
next_track:
|
||||
err = CODEC_OK;
|
||||
intptr_t param;
|
||||
|
||||
/* Clean and initialize decoder structures */
|
||||
memset(&demux_res , 0, sizeof(demux_res));
|
||||
if (codec_init()) {
|
||||
LOGF("FAAD: Codec init error\n");
|
||||
err = CODEC_ERROR;
|
||||
goto exit;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
if (codec_wait_taginfo() != 0)
|
||||
goto done;
|
||||
|
||||
file_offset = ci->id3->offset;
|
||||
|
||||
ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency);
|
||||
|
@ -85,12 +87,13 @@ next_track:
|
|||
|
||||
stream_create(&input_stream,ci);
|
||||
|
||||
ci->seek_buffer(ci->id3->first_frame_offset);
|
||||
|
||||
/* if qtmovie_read returns successfully, the stream is up to
|
||||
* the movie data, which can be used directly by the decoder */
|
||||
if (!qtmovie_read(&input_stream, &demux_res)) {
|
||||
LOGF("FAAD: File init error\n");
|
||||
err = CODEC_ERROR;
|
||||
goto done;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
/* initialise the sound converter */
|
||||
|
@ -98,8 +101,7 @@ next_track:
|
|||
|
||||
if (!decoder) {
|
||||
LOGF("FAAD: Decode open error\n");
|
||||
err = CODEC_ERROR;
|
||||
goto done;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
NeAACDecConfigurationPtr conf = NeAACDecGetCurrentConfiguration(decoder);
|
||||
|
@ -109,8 +111,7 @@ next_track:
|
|||
err = NeAACDecInit2(decoder, demux_res.codecdata, demux_res.codecdata_len, &s, &c);
|
||||
if (err) {
|
||||
LOGF("FAAD: DecInit: %d, %d\n", err, decoder->object_type);
|
||||
err = CODEC_ERROR;
|
||||
goto done;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
#ifdef SBR_DEC
|
||||
|
@ -150,20 +151,19 @@ next_track:
|
|||
|
||||
/* The main decoding loop */
|
||||
while (i < demux_res.num_sample_byte_sizes) {
|
||||
ci->yield();
|
||||
enum codec_command_action action = ci->get_command(¶m);
|
||||
|
||||
if (ci->stop_codec || ci->new_track) {
|
||||
if (action == CODEC_ACTION_HALT)
|
||||
break;
|
||||
}
|
||||
|
||||
/* Deal with any pending seek requests */
|
||||
if (ci->seek_time) {
|
||||
if (action == CODEC_ACTION_SEEK_TIME) {
|
||||
/* Seek to the desired time position. Important: When seeking in SBR
|
||||
* upsampling files the seek_time must be divided by 2 when calling
|
||||
* m4a_seek and the resulting sound_samples_done must be expanded
|
||||
* by a factor 2. This is done via using sbr_fac. */
|
||||
if (m4a_seek(&demux_res, &input_stream,
|
||||
((ci->seek_time-1)/10/sbr_fac)*(ci->id3->frequency/100),
|
||||
(param/10/sbr_fac)*(ci->id3->frequency/100),
|
||||
&sound_samples_done, (int*) &i)) {
|
||||
sound_samples_done *= sbr_fac;
|
||||
elapsed_time = (sound_samples_done * 10) / (ci->id3->frequency / 100);
|
||||
|
@ -194,8 +194,7 @@ next_track:
|
|||
else if (file_offset == 0)
|
||||
{
|
||||
LOGF("AAC: get_sample_offset error\n");
|
||||
err = CODEC_ERROR;
|
||||
goto done;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
/* Request the required number of bytes from the input buffer */
|
||||
|
@ -207,8 +206,7 @@ next_track:
|
|||
/* NeAACDecDecode may sometimes return NULL without setting error. */
|
||||
if (ret == NULL || frame_info.error > 0) {
|
||||
LOGF("FAAD: decode error '%s'\n", NeAACDecGetErrorMessage(frame_info.error));
|
||||
err = CODEC_ERROR;
|
||||
goto done;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
/* Advance codec buffer (no need to call set_offset because of this) */
|
||||
|
@ -251,12 +249,6 @@ next_track:
|
|||
i++;
|
||||
}
|
||||
|
||||
done:
|
||||
LOGF("AAC: Decoded %lu samples\n", (unsigned long)sound_samples_done);
|
||||
|
||||
if (ci->request_next_track())
|
||||
goto next_track;
|
||||
|
||||
exit:
|
||||
return err;
|
||||
return CODEC_OK;
|
||||
}
|
||||
|
|
|
@ -45,7 +45,19 @@ static const long cutoff = 500;
|
|||
static int16_t samples[WAV_CHUNK_SIZE] IBSS_ATTR;
|
||||
|
||||
/* this is the codec entry point */
|
||||
enum codec_status codec_main(void)
|
||||
enum codec_status codec_main(enum codec_entry_call_reason reason)
|
||||
{
|
||||
if (reason == CODEC_LOAD) {
|
||||
/* Generic codec initialisation */
|
||||
/* we only render 16 bits */
|
||||
ci->configure(DSP_SET_SAMPLE_DEPTH, 16);
|
||||
}
|
||||
|
||||
return CODEC_OK;
|
||||
}
|
||||
|
||||
/* this is called for each file to process */
|
||||
enum codec_status codec_run(void)
|
||||
{
|
||||
int channels;
|
||||
int sampleswritten, i;
|
||||
|
@ -62,12 +74,8 @@ enum codec_status codec_main(void)
|
|||
off_t chanstart, bufoff;
|
||||
/*long coef1=0x7298L,coef2=-0x3350L;*/
|
||||
long coef1, coef2;
|
||||
intptr_t param;
|
||||
|
||||
/* Generic codec initialisation */
|
||||
/* we only render 16 bits */
|
||||
ci->configure(DSP_SET_SAMPLE_DEPTH, 16);
|
||||
|
||||
next_track:
|
||||
DEBUGF("ADX: next_track\n");
|
||||
if (codec_init()) {
|
||||
return CODEC_ERROR;
|
||||
|
@ -77,10 +85,6 @@ next_track:
|
|||
/* init history */
|
||||
ch1_1=ch1_2=ch2_1=ch2_2=0;
|
||||
|
||||
/* wait for track info to load */
|
||||
if (codec_wait_taginfo() != 0)
|
||||
goto request_next_track;
|
||||
|
||||
codec_set_replaygain(ci->id3);
|
||||
|
||||
/* Get header */
|
||||
|
@ -226,10 +230,10 @@ next_track:
|
|||
/* The main decoder loop */
|
||||
|
||||
while (!endofstream) {
|
||||
ci->yield();
|
||||
if (ci->stop_codec || ci->new_track) {
|
||||
enum codec_command_action action = ci->get_command(¶m);
|
||||
|
||||
if (action == CODEC_ACTION_HALT)
|
||||
break;
|
||||
}
|
||||
|
||||
/* do we need to loop? */
|
||||
if (bufoff > end_adr-18*channels && looping) {
|
||||
|
@ -254,17 +258,17 @@ next_track:
|
|||
}
|
||||
|
||||
/* do we need to seek? */
|
||||
if (ci->seek_time) {
|
||||
if (action == CODEC_ACTION_SEEK_TIME) {
|
||||
uint32_t newpos;
|
||||
|
||||
DEBUGF("ADX: seek to %ldms\n",ci->seek_time);
|
||||
DEBUGF("ADX: seek to %ldms\n", (long)param);
|
||||
|
||||
endofstream = 0;
|
||||
loop_count = 0;
|
||||
fade_count = -1; /* disable fade */
|
||||
fade_frames = 1;
|
||||
|
||||
newpos = (((uint64_t)avgbytespersec*(ci->seek_time - 1))
|
||||
newpos = (((uint64_t)avgbytespersec*param)
|
||||
/ (1000LL*18*channels))*(18*channels);
|
||||
bufoff = chanstart + newpos;
|
||||
while (bufoff > end_adr-18*channels) {
|
||||
|
@ -385,9 +389,5 @@ next_track:
|
|||
1000LL/avgbytespersec);
|
||||
}
|
||||
|
||||
request_next_track:
|
||||
if (ci->request_next_track())
|
||||
goto next_track;
|
||||
|
||||
return CODEC_OK;
|
||||
}
|
||||
|
|
|
@ -61,9 +61,20 @@ static const struct pcm_codec *get_codec(uint32_t formattag)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
enum codec_status codec_main(void)
|
||||
/* this is the codec entry point */
|
||||
enum codec_status codec_main(enum codec_entry_call_reason reason)
|
||||
{
|
||||
if (reason == CODEC_LOAD) {
|
||||
/* Generic codec initialisation */
|
||||
ci->configure(DSP_SET_SAMPLE_DEPTH, PCM_OUTPUT_DEPTH-1);
|
||||
}
|
||||
|
||||
return CODEC_OK;
|
||||
}
|
||||
|
||||
/* this is called for each file to process */
|
||||
enum codec_status codec_run(void)
|
||||
{
|
||||
int status;
|
||||
struct pcm_format format;
|
||||
uint32_t bytesdone, decodedsamples;
|
||||
uint32_t num_sample_frames = 0;
|
||||
|
@ -77,38 +88,28 @@ enum codec_status codec_main(void)
|
|||
bool is_aifc = false;
|
||||
const struct pcm_codec *codec;
|
||||
uint32_t size;
|
||||
|
||||
/* Generic codec initialisation */
|
||||
ci->configure(DSP_SET_SAMPLE_DEPTH, PCM_OUTPUT_DEPTH-1);
|
||||
|
||||
next_track:
|
||||
status = CODEC_OK;
|
||||
intptr_t param;
|
||||
|
||||
if (codec_init()) {
|
||||
status = CODEC_ERROR;
|
||||
goto exit;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
if (codec_wait_taginfo() != 0)
|
||||
goto done;
|
||||
|
||||
codec_set_replaygain(ci->id3);
|
||||
|
||||
/* Need to save offset for later use (cleared indirectly by advance_buffer) */
|
||||
bytesdone = ci->id3->offset;
|
||||
|
||||
/* assume the AIFF header is less than 1024 bytes */
|
||||
ci->seek_buffer(0);
|
||||
buf = ci->request_buffer(&n, 1024);
|
||||
if (n < 54) {
|
||||
status = CODEC_ERROR;
|
||||
goto done;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
if (memcmp(buf, "FORM", 4) != 0)
|
||||
{
|
||||
DEBUGF("CODEC_ERROR: does not aiff format %4.4s\n", (char*)&buf[0]);
|
||||
status = CODEC_ERROR;
|
||||
goto done;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
if (memcmp(&buf[8], "AIFF", 4) == 0)
|
||||
is_aifc = false;
|
||||
|
@ -117,8 +118,7 @@ next_track:
|
|||
else
|
||||
{
|
||||
DEBUGF("CODEC_ERROR: does not aiff format %4.4s\n", (char*)&buf[8]);
|
||||
status = CODEC_ERROR;
|
||||
goto done;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
buf += 12;
|
||||
|
@ -141,8 +141,7 @@ next_track:
|
|||
{
|
||||
DEBUGF("CODEC_ERROR: 'COMM' chunk size=%lu < %d\n",
|
||||
(unsigned long)size, (is_aifc)?22:18);
|
||||
status = CODEC_ERROR;
|
||||
goto done;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
/* num_channels */
|
||||
format.channels = ((buf[8]<<8)|buf[9]);
|
||||
|
@ -154,8 +153,7 @@ next_track:
|
|||
/* sample_rate (don't use last 4 bytes, only integer fs) */
|
||||
if (buf[16] != 0x40) {
|
||||
DEBUGF("CODEC_ERROR: weird sampling rate (no @)\n");
|
||||
status = CODEC_ERROR;
|
||||
goto done;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
format.samplespersec = ((buf[18]<<24)|(buf[19]<<16)|(buf[20]<<8)|buf[21])+1;
|
||||
format.samplespersec >>= (16 + 14 - buf[17]);
|
||||
|
@ -181,8 +179,7 @@ next_track:
|
|||
} else if (memcmp(buf, "SSND", 4)==0) {
|
||||
if (format.bitspersample == 0) {
|
||||
DEBUGF("CODEC_ERROR: unsupported chunk order\n");
|
||||
status = CODEC_ERROR;
|
||||
goto done;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
/* offset2snd */
|
||||
offset2snd = (buf[8]<<24)|(buf[9]<<16)|(buf[10]<<8)|buf[11];
|
||||
|
@ -205,21 +202,18 @@ next_track:
|
|||
buf += size;
|
||||
if (n < size) {
|
||||
DEBUGF("CODEC_ERROR: AIFF header size > 1024\n");
|
||||
status = CODEC_ERROR;
|
||||
goto done;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
n -= size;
|
||||
} /* while 'SSND' */
|
||||
|
||||
if (format.channels == 0) {
|
||||
DEBUGF("CODEC_ERROR: 'COMM' chunk not found or 0-channels file\n");
|
||||
status = CODEC_ERROR;
|
||||
goto done;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
if (format.numbytes == 0) {
|
||||
DEBUGF("CODEC_ERROR: 'SSND' chunk not found or has zero length\n");
|
||||
status = CODEC_ERROR;
|
||||
goto done;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
codec = get_codec(format.formattag);
|
||||
|
@ -227,14 +221,12 @@ next_track:
|
|||
{
|
||||
DEBUGF("CODEC_ERROR: AIFC does not support compressionType: 0x%x\n",
|
||||
(unsigned int)format.formattag);
|
||||
status = CODEC_ERROR;
|
||||
goto done;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
if (!codec->set_format(&format))
|
||||
{
|
||||
status = CODEC_ERROR;
|
||||
goto done;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency);
|
||||
|
@ -245,21 +237,18 @@ next_track:
|
|||
ci->configure(DSP_SET_STEREO_MODE, STEREO_MONO);
|
||||
} else {
|
||||
DEBUGF("CODEC_ERROR: more than 2 channels unsupported\n");
|
||||
status = CODEC_ERROR;
|
||||
goto done;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
if (format.samplesperblock == 0)
|
||||
{
|
||||
DEBUGF("CODEC_ERROR: samplesperblock is 0\n");
|
||||
status = CODEC_ERROR;
|
||||
goto done;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
if (format.blockalign == 0)
|
||||
{
|
||||
DEBUGF("CODEC_ERROR: blockalign is 0\n");
|
||||
status = CODEC_ERROR;
|
||||
goto done;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
/* check chunksize */
|
||||
|
@ -269,8 +258,7 @@ next_track:
|
|||
if (format.chunksize == 0)
|
||||
{
|
||||
DEBUGF("CODEC_ERROR: chunksize is 0\n");
|
||||
status = CODEC_ERROR;
|
||||
goto done;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
firstblockposn = 1024 - n;
|
||||
|
@ -283,13 +271,13 @@ next_track:
|
|||
PCM_SEEK_POS, NULL);
|
||||
|
||||
if (newpos->pos > format.numbytes)
|
||||
goto done;
|
||||
return CODEC_OK;
|
||||
|
||||
if (ci->seek_buffer(firstblockposn + newpos->pos))
|
||||
{
|
||||
bytesdone = newpos->pos;
|
||||
decodedsamples = newpos->samples;
|
||||
}
|
||||
ci->seek_complete();
|
||||
} else {
|
||||
/* already where we need to be */
|
||||
bytesdone = 0;
|
||||
|
@ -299,21 +287,29 @@ next_track:
|
|||
endofstream = 0;
|
||||
|
||||
while (!endofstream) {
|
||||
ci->yield();
|
||||
if (ci->stop_codec || ci->new_track)
|
||||
enum codec_command_action action = ci->get_command(¶m);
|
||||
|
||||
if (action == CODEC_ACTION_HALT)
|
||||
break;
|
||||
|
||||
if (ci->seek_time) {
|
||||
if (action == CODEC_ACTION_SEEK_TIME) {
|
||||
/* 3rd args(read_buffer) is unnecessary in the format which AIFF supports. */
|
||||
struct pcm_pos *newpos = codec->get_seek_pos(ci->seek_time, PCM_SEEK_TIME, NULL);
|
||||
struct pcm_pos *newpos = codec->get_seek_pos(param, PCM_SEEK_TIME, NULL);
|
||||
|
||||
if (newpos->pos > format.numbytes)
|
||||
{
|
||||
ci->set_elapsed(ci->id3->length);
|
||||
ci->seek_complete();
|
||||
break;
|
||||
}
|
||||
|
||||
if (ci->seek_buffer(firstblockposn + newpos->pos))
|
||||
{
|
||||
bytesdone = newpos->pos;
|
||||
decodedsamples = newpos->samples;
|
||||
}
|
||||
|
||||
ci->set_elapsed(decodedsamples*1000LL/ci->id3->frequency);
|
||||
ci->seek_complete();
|
||||
}
|
||||
aifbuf = (uint8_t *)ci->request_buffer(&n, format.chunksize);
|
||||
|
@ -326,11 +322,10 @@ next_track:
|
|||
endofstream = 1;
|
||||
}
|
||||
|
||||
status = codec->decode(aifbuf, n, samples, &bufcount);
|
||||
if (status == CODEC_ERROR)
|
||||
if (codec->decode(aifbuf, n, samples, &bufcount) == CODEC_ERROR)
|
||||
{
|
||||
DEBUGF("codec error\n");
|
||||
goto done;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
ci->pcmbuf_insert(samples, NULL, bufcount);
|
||||
|
@ -343,13 +338,6 @@ next_track:
|
|||
|
||||
ci->set_elapsed(decodedsamples*1000LL/ci->id3->frequency);
|
||||
}
|
||||
status = CODEC_OK;
|
||||
|
||||
done:
|
||||
if (ci->request_next_track())
|
||||
goto next_track;
|
||||
|
||||
exit:
|
||||
return status;
|
||||
return CODEC_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -359,23 +359,32 @@ static bool init_encoder(void)
|
|||
return true;
|
||||
} /* init_encoder */
|
||||
|
||||
/* main codec entry point */
|
||||
enum codec_status codec_main(void)
|
||||
/* this is the codec entry point */
|
||||
enum codec_status codec_main(enum codec_entry_call_reason reason)
|
||||
{
|
||||
if (reason == CODEC_LOAD) {
|
||||
if (!init_encoder())
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
else if (reason == CODEC_UNLOAD) {
|
||||
/* reset parameters to initial state */
|
||||
ci->enc_set_parameters(NULL);
|
||||
}
|
||||
|
||||
return CODEC_OK;
|
||||
}
|
||||
|
||||
/* this is called for each file to process */
|
||||
enum codec_status codec_run(void)
|
||||
{
|
||||
/* main encoding loop */
|
||||
while(!ci->stop_codec)
|
||||
{
|
||||
uint32_t *src;
|
||||
|
||||
while ((src = (uint32_t *)ci->enc_get_pcm_data(PCM_CHUNK_SIZE)) != NULL)
|
||||
while (ci->get_command(NULL) != CODEC_ACTION_HALT)
|
||||
{
|
||||
uint32_t *src = (uint32_t *)ci->enc_get_pcm_data(PCM_CHUNK_SIZE);
|
||||
struct enc_chunk_hdr *chunk;
|
||||
|
||||
if (ci->stop_codec)
|
||||
break;
|
||||
if (src == NULL)
|
||||
continue;
|
||||
|
||||
chunk = ci->enc_get_chunk();
|
||||
chunk->enc_size = enc_size;
|
||||
|
@ -385,14 +394,7 @@ enum codec_status codec_main(void)
|
|||
chunk_to_aiff_format(src, (uint32_t *)chunk->enc_data);
|
||||
|
||||
ci->enc_finish_chunk();
|
||||
ci->yield();
|
||||
}
|
||||
|
||||
ci->yield();
|
||||
}
|
||||
|
||||
/* reset parameters to initial state */
|
||||
ci->enc_set_parameters(NULL);
|
||||
|
||||
return CODEC_OK;
|
||||
} /* codec_start */
|
||||
}
|
||||
|
|
|
@ -32,40 +32,43 @@ CODEC_HEADER
|
|||
static int32_t outputbuffer[ALAC_MAX_CHANNELS][ALAC_BLOCKSIZE] IBSS_ATTR;
|
||||
|
||||
/* this is the codec entry point */
|
||||
enum codec_status codec_main(void)
|
||||
enum codec_status codec_main(enum codec_entry_call_reason reason)
|
||||
{
|
||||
if (reason == CODEC_LOAD) {
|
||||
/* Generic codec initialisation */
|
||||
ci->configure(DSP_SET_STEREO_MODE, STEREO_NONINTERLEAVED);
|
||||
ci->configure(DSP_SET_SAMPLE_DEPTH, ALAC_OUTPUT_DEPTH-1);
|
||||
}
|
||||
|
||||
return CODEC_OK;
|
||||
}
|
||||
|
||||
/* this is called for each file to process */
|
||||
enum codec_status codec_run(void)
|
||||
{
|
||||
size_t n;
|
||||
demux_res_t demux_res;
|
||||
stream_t input_stream;
|
||||
uint32_t samplesdone;
|
||||
uint32_t elapsedtime;
|
||||
uint32_t elapsedtime = 0;
|
||||
int samplesdecoded;
|
||||
unsigned int i;
|
||||
unsigned char* buffer;
|
||||
alac_file alac;
|
||||
int retval;
|
||||
|
||||
/* Generic codec initialisation */
|
||||
ci->configure(DSP_SET_STEREO_MODE, STEREO_NONINTERLEAVED);
|
||||
ci->configure(DSP_SET_SAMPLE_DEPTH, ALAC_OUTPUT_DEPTH-1);
|
||||
|
||||
next_track:
|
||||
retval = CODEC_OK;
|
||||
intptr_t param;
|
||||
|
||||
/* Clean and initialize decoder structures */
|
||||
memset(&demux_res , 0, sizeof(demux_res));
|
||||
if (codec_init()) {
|
||||
LOGF("ALAC: Error initialising codec\n");
|
||||
retval = CODEC_ERROR;
|
||||
goto exit;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
if (codec_wait_taginfo() != 0)
|
||||
goto done;
|
||||
|
||||
ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency);
|
||||
codec_set_replaygain(ci->id3);
|
||||
|
||||
ci->seek_buffer(0);
|
||||
|
||||
stream_create(&input_stream,ci);
|
||||
|
||||
/* Read from ci->id3->offset before calling qtmovie_read. */
|
||||
|
@ -76,8 +79,7 @@ enum codec_status codec_main(void)
|
|||
* the movie data, which can be used directly by the decoder */
|
||||
if (!qtmovie_read(&input_stream, &demux_res)) {
|
||||
LOGF("ALAC: Error initialising file\n");
|
||||
retval = CODEC_ERROR;
|
||||
goto done;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
/* initialise the sound converter */
|
||||
|
@ -98,19 +100,22 @@ enum codec_status codec_main(void)
|
|||
|
||||
/* The main decoding loop */
|
||||
while (i < demux_res.num_sample_byte_sizes) {
|
||||
ci->yield();
|
||||
if (ci->stop_codec || ci->new_track) {
|
||||
enum codec_command_action action = ci->get_command(¶m);
|
||||
|
||||
if (action == CODEC_ACTION_HALT)
|
||||
break;
|
||||
}
|
||||
|
||||
/* Request the required number of bytes from the input buffer */
|
||||
buffer=ci->request_buffer(&n, ALAC_BYTE_BUFFER_SIZE);
|
||||
|
||||
/* Deal with any pending seek requests */
|
||||
if (ci->seek_time) {
|
||||
if (action == CODEC_ACTION_SEEK_TIME) {
|
||||
if (m4a_seek(&demux_res, &input_stream,
|
||||
((ci->seek_time-1)/10) * (ci->id3->frequency/100),
|
||||
(param/10) * (ci->id3->frequency/100),
|
||||
&samplesdone, (int *)&i)) {
|
||||
elapsedtime=(samplesdone*10)/(ci->id3->frequency/100);
|
||||
ci->set_elapsed(elapsedtime);
|
||||
}
|
||||
ci->set_elapsed(elapsedtime);
|
||||
ci->seek_complete();
|
||||
}
|
||||
|
||||
|
@ -118,14 +123,13 @@ enum codec_status codec_main(void)
|
|||
buffer=ci->request_buffer(&n, ALAC_BYTE_BUFFER_SIZE);
|
||||
|
||||
/* Decode one block - returned samples will be host-endian */
|
||||
ci->yield();
|
||||
samplesdecoded=alac_decode_frame(&alac, buffer, outputbuffer, ci->yield);
|
||||
ci->yield();
|
||||
|
||||
/* Advance codec buffer by amount of consumed bytes */
|
||||
ci->advance_buffer(alac.bytes_consumed);
|
||||
|
||||
/* Output the audio */
|
||||
ci->yield();
|
||||
ci->pcmbuf_insert(outputbuffer[0], outputbuffer[1], samplesdecoded);
|
||||
|
||||
/* Update the elapsed-time indicator */
|
||||
|
@ -136,12 +140,6 @@ enum codec_status codec_main(void)
|
|||
i++;
|
||||
}
|
||||
|
||||
done:
|
||||
LOGF("ALAC: Decoded %lu samples\n",(unsigned long)samplesdone);
|
||||
|
||||
if (ci->request_next_track())
|
||||
goto next_track;
|
||||
|
||||
exit:
|
||||
return retval;
|
||||
return CODEC_OK;
|
||||
}
|
||||
|
|
|
@ -127,13 +127,23 @@ static void ape_resume(struct ape_ctx_t* ape_ctx, size_t resume_offset,
|
|||
}
|
||||
|
||||
/* this is the codec entry point */
|
||||
enum codec_status codec_main(void)
|
||||
enum codec_status codec_main(enum codec_entry_call_reason reason)
|
||||
{
|
||||
if (reason == CODEC_LOAD) {
|
||||
/* Generic codec initialisation */
|
||||
ci->configure(DSP_SET_SAMPLE_DEPTH, APE_OUTPUT_DEPTH-1);
|
||||
}
|
||||
|
||||
return CODEC_OK;
|
||||
}
|
||||
|
||||
/* this is called for each file to process */
|
||||
enum codec_status codec_run(void)
|
||||
{
|
||||
struct ape_ctx_t ape_ctx;
|
||||
uint32_t samplesdone;
|
||||
uint32_t elapsedtime;
|
||||
size_t bytesleft;
|
||||
int retval;
|
||||
|
||||
uint32_t currentframe;
|
||||
uint32_t newfilepos;
|
||||
|
@ -145,33 +155,24 @@ enum codec_status codec_main(void)
|
|||
int res;
|
||||
int firstbyte;
|
||||
size_t resume_offset;
|
||||
|
||||
/* Generic codec initialisation */
|
||||
ci->configure(DSP_SET_SAMPLE_DEPTH, APE_OUTPUT_DEPTH-1);
|
||||
|
||||
next_track:
|
||||
retval = CODEC_OK;
|
||||
intptr_t param;
|
||||
|
||||
if (codec_init()) {
|
||||
LOGF("APE: Error initialising codec\n");
|
||||
retval = CODEC_ERROR;
|
||||
goto exit;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
if (codec_wait_taginfo() != 0)
|
||||
goto done;
|
||||
|
||||
/* Remember the resume position - when the codec is opened, the
|
||||
playback engine will reset it. */
|
||||
resume_offset = ci->id3->offset;
|
||||
|
||||
ci->seek_buffer(0);
|
||||
inbuffer = ci->request_buffer(&bytesleft, INPUT_CHUNKSIZE);
|
||||
|
||||
/* Read the file headers to populate the ape_ctx struct */
|
||||
if (ape_parseheaderbuf(inbuffer,&ape_ctx) < 0) {
|
||||
LOGF("APE: Error reading header\n");
|
||||
retval = CODEC_ERROR;
|
||||
goto exit;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
/* Initialise the seektable for this file */
|
||||
|
@ -243,16 +244,16 @@ frame_start:
|
|||
/* Decode the frame a chunk at a time */
|
||||
while (nblocks > 0)
|
||||
{
|
||||
ci->yield();
|
||||
if (ci->stop_codec || ci->new_track) {
|
||||
enum codec_command_action action = ci->get_command(¶m);
|
||||
|
||||
if (action == CODEC_ACTION_HALT)
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Deal with any pending seek requests */
|
||||
if (ci->seek_time)
|
||||
if (action == CODEC_ACTION_SEEK_TIME)
|
||||
{
|
||||
if (ape_calc_seekpos(&ape_ctx,
|
||||
((ci->seek_time-1)/10) * (ci->id3->frequency/100),
|
||||
(param/10) * (ci->id3->frequency/100),
|
||||
¤tframe,
|
||||
&newfilepos,
|
||||
&samplestoskip))
|
||||
|
@ -266,9 +267,12 @@ frame_start:
|
|||
ci->seek_buffer(newfilepos);
|
||||
inbuffer = ci->request_buffer(&bytesleft, INPUT_CHUNKSIZE);
|
||||
|
||||
elapsedtime = (samplesdone*10)/(ape_ctx.samplerate/100);
|
||||
ci->set_elapsed(elapsedtime);
|
||||
ci->seek_complete();
|
||||
goto frame_start; /* Sorry... */
|
||||
}
|
||||
|
||||
ci->seek_complete();
|
||||
}
|
||||
|
||||
|
@ -281,8 +285,7 @@ frame_start:
|
|||
{
|
||||
/* Frame decoding error, abort */
|
||||
LOGF("APE: Frame %lu, error %d\n",(unsigned long)currentframe,res);
|
||||
retval = CODEC_ERROR;
|
||||
goto done;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
ci->yield();
|
||||
|
@ -320,10 +323,5 @@ frame_start:
|
|||
|
||||
done:
|
||||
LOGF("APE: Decoded %lu samples\n",(unsigned long)samplesdone);
|
||||
|
||||
if (ci->request_next_track())
|
||||
goto next_track;
|
||||
|
||||
exit:
|
||||
return retval;
|
||||
return CODEC_OK;
|
||||
}
|
||||
|
|
|
@ -29,24 +29,21 @@ CODEC_HEADER
|
|||
static byte samples[CHUNK_SIZE] IBSS_ATTR; /* The sample buffer */
|
||||
static ASAP_State asap; /* asap codec state */
|
||||
|
||||
/* this is the codec entry point */
|
||||
enum codec_status codec_main(void)
|
||||
/* this is called for each file to process */
|
||||
enum codec_status codec_run(void)
|
||||
{
|
||||
int n_bytes;
|
||||
int song;
|
||||
int duration;
|
||||
char* module;
|
||||
int bytesPerSample =2;
|
||||
intptr_t param;
|
||||
|
||||
next_track:
|
||||
if (codec_init()) {
|
||||
DEBUGF("codec init failed\n");
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
if (codec_wait_taginfo() != 0)
|
||||
goto request_next_track;
|
||||
|
||||
codec_set_replaygain(ci->id3);
|
||||
|
||||
int bytes_done =0;
|
||||
|
@ -97,19 +94,20 @@ next_track:
|
|||
|
||||
/* The main decoder loop */
|
||||
while (1) {
|
||||
ci->yield();
|
||||
if (ci->stop_codec || ci->new_track)
|
||||
enum codec_command_action action = ci->get_command(¶m);
|
||||
|
||||
if (action == CODEC_ACTION_HALT)
|
||||
break;
|
||||
|
||||
if (ci->seek_time) {
|
||||
/* New time is ready in ci->seek_time */
|
||||
if (action == CODEC_ACTION_SEEK_TIME) {
|
||||
/* New time is ready in param */
|
||||
|
||||
/* seek to pos */
|
||||
ASAP_Seek(&asap,ci->seek_time);
|
||||
/* update elapsed */
|
||||
ci->set_elapsed(ci->seek_time);
|
||||
ASAP_Seek(&asap,param);
|
||||
/* update bytes_done */
|
||||
bytes_done = ci->seek_time*44.1*2;
|
||||
bytes_done = param*44.1*2;
|
||||
/* update elapsed */
|
||||
ci->set_elapsed((bytes_done / 2) / 44.1);
|
||||
/* seek ready */
|
||||
ci->seek_complete();
|
||||
}
|
||||
|
@ -130,9 +128,5 @@ next_track:
|
|||
break;
|
||||
}
|
||||
|
||||
request_next_track:
|
||||
if (ci->request_next_track())
|
||||
goto next_track;
|
||||
|
||||
return CODEC_OK;
|
||||
}
|
||||
|
|
|
@ -33,24 +33,22 @@ CODEC_HEADER
|
|||
|
||||
static ATRAC3Context q IBSS_ATTR;
|
||||
|
||||
/* this is the codec entry point */
|
||||
enum codec_status codec_main(void)
|
||||
/* this is called for each file to process */
|
||||
enum codec_status codec_run(void)
|
||||
{
|
||||
static size_t buff_size;
|
||||
int datasize, res, frame_counter, total_frames, seek_frame_offset;
|
||||
uint8_t *bit_buffer;
|
||||
int elapsed = 0;
|
||||
size_t resume_offset;
|
||||
intptr_t param;
|
||||
enum codec_command_action action = CODEC_ACTION_NULL;
|
||||
|
||||
next_track:
|
||||
if (codec_init()) {
|
||||
DEBUGF("codec init failed\n");
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
if (codec_wait_taginfo() != 0)
|
||||
goto done;
|
||||
|
||||
resume_offset = ci->id3->offset;
|
||||
|
||||
codec_set_replaygain(ci->id3);
|
||||
|
@ -61,63 +59,70 @@ next_track:
|
|||
ci->configure(DSP_SET_STEREO_MODE, ci->id3->channels == 1 ?
|
||||
STEREO_MONO : STEREO_NONINTERLEAVED);
|
||||
|
||||
res =atrac3_decode_init(&q, ci->id3);
|
||||
ci->seek_buffer(0);
|
||||
|
||||
res = atrac3_decode_init(&q, ci->id3);
|
||||
if(res < 0) {
|
||||
DEBUGF("failed to initialize OMA atrac decoder\n");
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
total_frames = (ci->id3->filesize - ci->id3->first_frame_offset) / FRAMESIZE;
|
||||
frame_counter = 0;
|
||||
|
||||
/* check for a mid-track resume and force a seek time accordingly */
|
||||
if(resume_offset > ci->id3->first_frame_offset) {
|
||||
resume_offset -= ci->id3->first_frame_offset;
|
||||
/* calculate resume_offset in frames */
|
||||
resume_offset = (int)resume_offset / FRAMESIZE;
|
||||
ci->seek_time = (int)resume_offset * ((FRAMESIZE * 8)/BITRATE);
|
||||
param = (int)resume_offset * ((FRAMESIZE * 8)/BITRATE);
|
||||
action = CODEC_ACTION_SEEK_TIME;
|
||||
}
|
||||
total_frames = (ci->id3->filesize - ci->id3->first_frame_offset) / FRAMESIZE;
|
||||
frame_counter = 0;
|
||||
|
||||
else {
|
||||
ci->set_elapsed(0);
|
||||
ci->seek_buffer(0);
|
||||
ci->advance_buffer(ci->id3->first_frame_offset);
|
||||
ci->seek_buffer(ci->id3->first_frame_offset);
|
||||
}
|
||||
|
||||
/* The main decoder loop */
|
||||
seek_start :
|
||||
while(frame_counter < total_frames)
|
||||
{
|
||||
if (action == CODEC_ACTION_NULL)
|
||||
action = ci->get_command(¶m);
|
||||
|
||||
if (action == CODEC_ACTION_HALT)
|
||||
break;
|
||||
|
||||
bit_buffer = (uint8_t *) ci->request_buffer(&buff_size, FRAMESIZE);
|
||||
|
||||
ci->yield();
|
||||
if (ci->stop_codec || ci->new_track)
|
||||
goto done;
|
||||
|
||||
if (ci->seek_time) {
|
||||
ci->set_elapsed(ci->seek_time);
|
||||
|
||||
if (action == CODEC_ACTION_SEEK_TIME) {
|
||||
/* Do not allow seeking beyond the file's length */
|
||||
if ((unsigned) ci->seek_time > ci->id3->length) {
|
||||
if ((unsigned) param > ci->id3->length) {
|
||||
ci->set_elapsed(ci->id3->length);
|
||||
ci->seek_complete();
|
||||
goto done;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Seek to the start of the track */
|
||||
if (ci->seek_time == 1) {
|
||||
ci->set_elapsed(0);
|
||||
ci->seek_complete();
|
||||
ci->seek_buffer(ci->id3->first_frame_offset);
|
||||
if (param == 0) {
|
||||
elapsed = 0;
|
||||
goto seek_start;
|
||||
ci->set_elapsed(0);
|
||||
ci->seek_buffer(ci->id3->first_frame_offset);
|
||||
ci->seek_complete();
|
||||
action = CODEC_ACTION_NULL;
|
||||
continue;
|
||||
}
|
||||
seek_frame_offset = (ci->seek_time * BITRATE) / (8 * FRAMESIZE);
|
||||
|
||||
seek_frame_offset = (param * BITRATE) / (8 * FRAMESIZE);
|
||||
frame_counter = seek_frame_offset;
|
||||
ci->seek_buffer(ci->id3->first_frame_offset + seek_frame_offset* FRAMESIZE);
|
||||
bit_buffer = (uint8_t *) ci->request_buffer(&buff_size, FRAMESIZE);
|
||||
elapsed = ci->seek_time;
|
||||
|
||||
elapsed = param;
|
||||
ci->set_elapsed(elapsed);
|
||||
ci->seek_complete();
|
||||
}
|
||||
|
||||
action = CODEC_ACTION_NULL;
|
||||
|
||||
res = atrac3_decode_frame(FRAMESIZE, &q, &datasize, bit_buffer, FRAMESIZE);
|
||||
|
||||
if(res != (int)FRAMESIZE) {
|
||||
|
@ -126,7 +131,8 @@ seek_start :
|
|||
}
|
||||
|
||||
if(datasize)
|
||||
ci->pcmbuf_insert(q.outSamples, q.outSamples + 1024, q.samples_per_frame / ci->id3->channels);
|
||||
ci->pcmbuf_insert(q.outSamples, q.outSamples + 1024,
|
||||
q.samples_per_frame / ci->id3->channels);
|
||||
|
||||
elapsed += (FRAMESIZE * 8) / BITRATE;
|
||||
ci->set_elapsed(elapsed);
|
||||
|
@ -135,9 +141,5 @@ seek_start :
|
|||
frame_counter++;
|
||||
}
|
||||
|
||||
done:
|
||||
if (ci->request_next_track())
|
||||
goto next_track;
|
||||
|
||||
return CODEC_OK;
|
||||
}
|
||||
|
|
|
@ -41,8 +41,8 @@ static void init_rm(RMContext *rmctx)
|
|||
memcpy(ci->id3->id3v2buf, (char*)rmctx->codec_extradata, rmctx->extradata_size*sizeof(char));
|
||||
}
|
||||
|
||||
/* this is the codec entry point */
|
||||
enum codec_status codec_main(void)
|
||||
/* this is called for each file to process */
|
||||
enum codec_status codec_run(void)
|
||||
{
|
||||
static size_t buff_size;
|
||||
int datasize, res, consumed, i, time_offset;
|
||||
|
@ -52,16 +52,14 @@ enum codec_status codec_main(void)
|
|||
int scrambling_unit_size, num_units, elapsed = 0;
|
||||
int playback_on = -1;
|
||||
size_t resume_offset;
|
||||
intptr_t param;
|
||||
enum codec_command_action action = CODEC_ACTION_NULL;
|
||||
|
||||
next_track:
|
||||
if (codec_init()) {
|
||||
DEBUGF("codec init failed\n");
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
if (codec_wait_taginfo() != 0)
|
||||
goto done;
|
||||
|
||||
resume_offset = ci->id3->offset;
|
||||
|
||||
codec_set_replaygain(ci->id3);
|
||||
|
@ -69,6 +67,7 @@ next_track:
|
|||
ci->memset(&pkt,0,sizeof(RMPacket));
|
||||
ci->memset(&q,0,sizeof(ATRAC3Context));
|
||||
|
||||
ci->seek_buffer(0);
|
||||
init_rm(&rmctx);
|
||||
|
||||
ci->configure(DSP_SET_FREQUENCY, ci->id3->frequency);
|
||||
|
@ -84,7 +83,7 @@ next_track:
|
|||
h = rmctx.sub_packet_h;
|
||||
scrambling_unit_size = h*fs;
|
||||
|
||||
res =atrac3_decode_init(&q, ci->id3);
|
||||
res = atrac3_decode_init(&q, ci->id3);
|
||||
if(res < 0) {
|
||||
DEBUGF("failed to initialize RM atrac decoder\n");
|
||||
return CODEC_ERROR;
|
||||
|
@ -96,10 +95,13 @@ next_track:
|
|||
num_units = (int)resume_offset / scrambling_unit_size;
|
||||
/* put number of subpackets to skip in resume_offset */
|
||||
resume_offset /= (sps + PACKET_HEADER_SIZE);
|
||||
ci->seek_time = (int)resume_offset * ((sps * 8 * 1000)/rmctx.bit_rate);
|
||||
param = (int)resume_offset * ((sps * 8 * 1000)/rmctx.bit_rate);
|
||||
action = CODEC_ACTION_SEEK_TIME;
|
||||
}
|
||||
else {
|
||||
ci->set_elapsed(0);
|
||||
}
|
||||
|
||||
ci->set_elapsed(0);
|
||||
ci->advance_buffer(rmctx.data_offset + DATA_HEADER_SIZE);
|
||||
|
||||
/* The main decoder loop */
|
||||
|
@ -115,22 +117,23 @@ seek_start :
|
|||
return CODEC_ERROR;
|
||||
}
|
||||
else
|
||||
goto done;
|
||||
return CODEC_OK;
|
||||
}
|
||||
|
||||
for(i = 0; i < rmctx.audio_pkt_cnt*(fs/sps) ; i++)
|
||||
{
|
||||
ci->yield();
|
||||
if (ci->stop_codec || ci->new_track)
|
||||
goto done;
|
||||
if (action == CODEC_ACTION_NULL)
|
||||
action = ci->get_command(¶m);
|
||||
|
||||
if (ci->seek_time) {
|
||||
ci->set_elapsed(ci->seek_time);
|
||||
if (action == CODEC_ACTION_HALT)
|
||||
return CODEC_OK;
|
||||
|
||||
if (action == CODEC_ACTION_SEEK_TIME) {
|
||||
/* Do not allow seeking beyond the file's length */
|
||||
if ((unsigned) ci->seek_time > ci->id3->length) {
|
||||
if ((unsigned) param > ci->id3->length) {
|
||||
ci->set_elapsed(ci->id3->length);
|
||||
ci->seek_complete();
|
||||
goto done;
|
||||
return CODEC_OK;
|
||||
}
|
||||
|
||||
ci->seek_buffer(rmctx.data_offset + DATA_HEADER_SIZE);
|
||||
|
@ -139,12 +142,13 @@ seek_start :
|
|||
rmctx.frame_number = 0;
|
||||
|
||||
/* Seek to the start of the track */
|
||||
if (ci->seek_time == 1) {
|
||||
if (param == 0) {
|
||||
ci->set_elapsed(0);
|
||||
ci->seek_complete();
|
||||
action = CODEC_ACTION_NULL;
|
||||
goto seek_start;
|
||||
}
|
||||
num_units = ((ci->seek_time)/(sps*1000*8/rmctx.bit_rate))/(h*(fs/sps));
|
||||
num_units = (param/(sps*1000*8/rmctx.bit_rate))/(h*(fs/sps));
|
||||
ci->seek_buffer(rmctx.data_offset + DATA_HEADER_SIZE + consumed * num_units);
|
||||
bit_buffer = (uint8_t *) ci->request_buffer(&buff_size, scrambling_unit_size);
|
||||
consumed = rm_get_packet(&bit_buffer, &rmctx, &pkt);
|
||||
|
@ -155,12 +159,12 @@ seek_start :
|
|||
return CODEC_ERROR;
|
||||
}
|
||||
else
|
||||
goto done;
|
||||
return CODEC_OK;
|
||||
}
|
||||
|
||||
packet_count = rmctx.nb_packets - rmctx.audio_pkt_cnt * num_units;
|
||||
rmctx.frame_number = ((ci->seek_time)/(sps*1000*8/rmctx.bit_rate));
|
||||
while(rmctx.audiotimestamp > (unsigned) ci->seek_time) {
|
||||
rmctx.frame_number = (param/(sps*1000*8/rmctx.bit_rate));
|
||||
while(rmctx.audiotimestamp > (unsigned) param) {
|
||||
rmctx.audio_pkt_cnt = 0;
|
||||
ci->seek_buffer(rmctx.data_offset + DATA_HEADER_SIZE + consumed * (num_units-1));
|
||||
bit_buffer = (uint8_t *) ci->request_buffer(&buff_size, scrambling_unit_size);
|
||||
|
@ -168,16 +172,19 @@ seek_start :
|
|||
packet_count += rmctx.audio_pkt_cnt;
|
||||
num_units--;
|
||||
}
|
||||
time_offset = ci->seek_time - rmctx.audiotimestamp;
|
||||
time_offset = param - rmctx.audiotimestamp;
|
||||
i = (time_offset/((sps * 8 * 1000)/rmctx.bit_rate));
|
||||
elapsed = rmctx.audiotimestamp+(1000*8*sps/rmctx.bit_rate)*i;
|
||||
ci->set_elapsed(elapsed);
|
||||
ci->seek_complete();
|
||||
}
|
||||
|
||||
action = CODEC_ACTION_NULL;
|
||||
|
||||
if(pkt.length)
|
||||
res = atrac3_decode_frame(rmctx.block_align, &q, &datasize, pkt.frames[i], rmctx.block_align);
|
||||
else /* indicates that there are no remaining frames */
|
||||
goto done;
|
||||
return CODEC_OK;
|
||||
|
||||
if(res != rmctx.block_align) {
|
||||
DEBUGF("codec error\n");
|
||||
|
@ -196,9 +203,5 @@ seek_start :
|
|||
ci->advance_buffer(consumed);
|
||||
}
|
||||
|
||||
done :
|
||||
if (ci->request_next_track())
|
||||
goto next_track;
|
||||
|
||||
return CODEC_OK;
|
||||
}
|
||||
|
|
|
@ -106,9 +106,19 @@ static int convert_au_format(unsigned int encoding, struct pcm_format *fmt)
|
|||
}
|
||||
|
||||
/* this is the codec entry point */
|
||||
enum codec_status codec_main(void)
|
||||
enum codec_status codec_main(enum codec_entry_call_reason reason)
|
||||
{
|
||||
if (reason == CODEC_LOAD) {
|
||||
/* Generic codec initialisation */
|
||||
ci->configure(DSP_SET_SAMPLE_DEPTH, PCM_OUTPUT_DEPTH-1);
|
||||
}
|
||||
|
||||
return CODEC_OK;
|
||||
}
|
||||
|
||||
/* this is called for each file to process */
|
||||
enum codec_status codec_run(void)
|
||||
{
|
||||
int status;
|
||||
struct pcm_format format;
|
||||
uint32_t bytesdone, decodedsamples;
|
||||
size_t n;
|
||||
|
@ -119,22 +129,13 @@ enum codec_status codec_main(void)
|
|||
off_t firstblockposn; /* position of the first block in file */
|
||||
const struct pcm_codec *codec;
|
||||
int offset = 0;
|
||||
|
||||
/* Generic codec initialisation */
|
||||
ci->configure(DSP_SET_SAMPLE_DEPTH, PCM_OUTPUT_DEPTH-1);
|
||||
|
||||
next_track:
|
||||
status = CODEC_OK;
|
||||
intptr_t param;
|
||||
|
||||
if (codec_init()) {
|
||||
DEBUGF("codec_init() error\n");
|
||||
status = CODEC_ERROR;
|
||||
goto exit;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
if (codec_wait_taginfo() != 0)
|
||||
goto done;
|
||||
|
||||
codec_set_replaygain(ci->id3);
|
||||
|
||||
/* Need to save offset for later use (cleared indirectly by advance_buffer) */
|
||||
|
@ -145,6 +146,7 @@ next_track:
|
|||
format.is_little_endian = false;
|
||||
|
||||
/* set format */
|
||||
ci->seek_buffer(0);
|
||||
buf = ci->request_buffer(&n, 24);
|
||||
if (n < 24 || (memcmp(buf, ".snd", 4) != 0))
|
||||
{
|
||||
|
@ -170,8 +172,7 @@ next_track:
|
|||
if (offset < 24)
|
||||
{
|
||||
DEBUGF("CODEC_ERROR: sun audio offset size is small: %d\n", offset);
|
||||
status = CODEC_ERROR;
|
||||
goto done;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
/* data size */
|
||||
format.numbytes = get_be32(buf + 8);
|
||||
|
@ -182,8 +183,7 @@ next_track:
|
|||
if (format.formattag == AU_FORMAT_UNSUPPORT)
|
||||
{
|
||||
DEBUGF("CODEC_ERROR: sun audio unsupport format: %d\n", get_be32(buf + 12));
|
||||
status = CODEC_ERROR;
|
||||
goto done;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
/* skip sample rate */
|
||||
format.channels = get_be32(buf + 20);
|
||||
|
@ -202,20 +202,17 @@ next_track:
|
|||
if (!codec)
|
||||
{
|
||||
DEBUGF("CODEC_ERROR: unsupport sun audio format: %x\n", (int)format.formattag);
|
||||
status = CODEC_ERROR;
|
||||
goto done;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
if (!codec->set_format(&format))
|
||||
{
|
||||
status = CODEC_ERROR;
|
||||
goto done;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
if (format.numbytes == 0) {
|
||||
DEBUGF("CODEC_ERROR: data size is 0\n");
|
||||
status = CODEC_ERROR;
|
||||
goto done;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
/* check chunksize */
|
||||
|
@ -225,8 +222,7 @@ next_track:
|
|||
if (format.chunksize == 0)
|
||||
{
|
||||
DEBUGF("CODEC_ERROR: chunksize is 0\n");
|
||||
status = CODEC_ERROR;
|
||||
goto done;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency);
|
||||
|
@ -236,8 +232,7 @@ next_track:
|
|||
ci->configure(DSP_SET_STEREO_MODE, STEREO_MONO);
|
||||
} else {
|
||||
DEBUGF("CODEC_ERROR: more than 2 channels\n");
|
||||
status = CODEC_ERROR;
|
||||
goto done;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
/* make sure we're at the correct offset */
|
||||
|
@ -253,7 +248,6 @@ next_track:
|
|||
bytesdone = newpos->pos;
|
||||
decodedsamples = newpos->samples;
|
||||
}
|
||||
ci->seek_complete();
|
||||
} else {
|
||||
/* already where we need to be */
|
||||
bytesdone = 0;
|
||||
|
@ -263,22 +257,29 @@ next_track:
|
|||
endofstream = 0;
|
||||
|
||||
while (!endofstream) {
|
||||
ci->yield();
|
||||
if (ci->stop_codec || ci->new_track) {
|
||||
enum codec_command_action action = ci->get_command(¶m);
|
||||
|
||||
if (action == CODEC_ACTION_HALT)
|
||||
break;
|
||||
|
||||
if (action == CODEC_ACTION_SEEK_TIME) {
|
||||
/* 3rd args(read_buffer) is unnecessary in the format which Sun Audio supports. */
|
||||
struct pcm_pos *newpos = codec->get_seek_pos(param, PCM_SEEK_TIME, NULL);
|
||||
|
||||
if (newpos->pos > format.numbytes)
|
||||
{
|
||||
ci->set_elapsed(ci->id3->length);
|
||||
ci->seek_complete();
|
||||
break;
|
||||
}
|
||||
|
||||
if (ci->seek_time) {
|
||||
/* 3rd args(read_buffer) is unnecessary in the format which Sun Audio supports. */
|
||||
struct pcm_pos *newpos = codec->get_seek_pos(ci->seek_time, PCM_SEEK_TIME, NULL);
|
||||
|
||||
if (newpos->pos > format.numbytes)
|
||||
break;
|
||||
if (ci->seek_buffer(firstblockposn + newpos->pos))
|
||||
{
|
||||
bytesdone = newpos->pos;
|
||||
decodedsamples = newpos->samples;
|
||||
}
|
||||
|
||||
ci->set_elapsed(decodedsamples*1000LL/ci->id3->frequency);
|
||||
ci->seek_complete();
|
||||
}
|
||||
|
||||
|
@ -290,11 +291,10 @@ next_track:
|
|||
endofstream = 1;
|
||||
}
|
||||
|
||||
status = codec->decode(aubuf, n, samples, &bufcount);
|
||||
if (status == CODEC_ERROR)
|
||||
if (codec->decode(aubuf, n, samples, &bufcount) == CODEC_ERROR)
|
||||
{
|
||||
DEBUGF("codec error\n");
|
||||
goto done;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
ci->pcmbuf_insert(samples, NULL, bufcount);
|
||||
|
@ -308,9 +308,5 @@ next_track:
|
|||
}
|
||||
|
||||
done:
|
||||
if (ci->request_next_track())
|
||||
goto next_track;
|
||||
|
||||
exit:
|
||||
return status;
|
||||
return CODEC_OK;
|
||||
}
|
||||
|
|
|
@ -27,15 +27,17 @@ struct codec_api *ci DATA_ATTR;
|
|||
extern unsigned char plugin_bss_start[];
|
||||
extern unsigned char plugin_end_addr[];
|
||||
|
||||
extern enum codec_status codec_main(void);
|
||||
extern enum codec_status codec_main(enum codec_entry_call_reason reason);
|
||||
|
||||
/* stub, the entry point is called via its reference in __header to
|
||||
* avoid warning with certain compilers */
|
||||
int _start(void) {return 0;}
|
||||
|
||||
enum codec_status codec_start(void)
|
||||
enum codec_status codec_start(enum codec_entry_call_reason reason)
|
||||
{
|
||||
#if (CONFIG_PLATFORM & PLATFORM_NATIVE)
|
||||
if (reason == CODEC_LOAD)
|
||||
{
|
||||
#ifdef USE_IRAM
|
||||
extern char iramcopy[], iramstart[], iramend[], iedata[], iend[];
|
||||
size_t iram_size = iramend - iramstart;
|
||||
|
@ -57,9 +59,13 @@ enum codec_status codec_start(void)
|
|||
* portalplayer has this). If we don't clear the cache, those aliases
|
||||
* may read garbage */
|
||||
ci->cpucache_invalidate();
|
||||
#endif
|
||||
}
|
||||
#endif /* CONFIG_PLATFORM */
|
||||
|
||||
return codec_main();
|
||||
/* Note: If for any reason codec_main would not be called with CODEC_LOAD
|
||||
* because the above code failed then it must not be ever be called with
|
||||
* any other value and some strategy to avoid doing so must be conceived */
|
||||
return codec_main(reason);
|
||||
}
|
||||
|
||||
#if defined(CPU_ARM) && (CONFIG_PLATFORM & PLATFORM_NATIVE)
|
||||
|
|
|
@ -38,8 +38,8 @@ static void init_rm(RMContext *rmctx)
|
|||
memcpy(rmctx, (void*)(( (intptr_t)ci->id3->id3v2buf + 3 ) &~ 3), sizeof(RMContext));
|
||||
}
|
||||
|
||||
/* this is the codec entry point */
|
||||
enum codec_status codec_main(void)
|
||||
/* this is called for each file to process */
|
||||
enum codec_status codec_run(void)
|
||||
{
|
||||
static size_t buff_size;
|
||||
int datasize, res, consumed, i, time_offset;
|
||||
|
@ -48,16 +48,14 @@ enum codec_status codec_main(void)
|
|||
uint32_t packet_count;
|
||||
int scrambling_unit_size, num_units;
|
||||
size_t resume_offset;
|
||||
intptr_t param = 0;
|
||||
enum codec_command_action action = CODEC_ACTION_NULL;
|
||||
|
||||
next_track:
|
||||
if (codec_init()) {
|
||||
DEBUGF("codec init failed\n");
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
if (codec_wait_taginfo() != 0)
|
||||
goto done;
|
||||
|
||||
resume_offset = ci->id3->offset;
|
||||
|
||||
codec_set_replaygain(ci->id3);
|
||||
|
@ -65,6 +63,8 @@ next_track:
|
|||
ci->memset(&pkt,0,sizeof(RMPacket));
|
||||
ci->memset(&q,0,sizeof(COOKContext));
|
||||
|
||||
ci->seek_buffer(0);
|
||||
|
||||
init_rm(&rmctx);
|
||||
|
||||
ci->configure(DSP_SET_FREQUENCY, ci->id3->frequency);
|
||||
|
@ -94,7 +94,8 @@ next_track:
|
|||
num_units = (int)resume_offset / scrambling_unit_size;
|
||||
/* put number of subpackets to skip in resume_offset */
|
||||
resume_offset /= (sps + PACKET_HEADER_SIZE);
|
||||
ci->seek_time = (int)resume_offset * ((sps * 8 * 1000)/rmctx.bit_rate);
|
||||
param = (int)resume_offset * ((sps * 8 * 1000)/rmctx.bit_rate);
|
||||
action = CODEC_ACTION_SEEK_TIME;
|
||||
}
|
||||
|
||||
ci->set_elapsed(0);
|
||||
|
@ -113,17 +114,18 @@ seek_start :
|
|||
|
||||
for(i = 0; i < rmctx.audio_pkt_cnt*(fs/sps) ; i++)
|
||||
{
|
||||
ci->yield();
|
||||
if (ci->stop_codec || ci->new_track)
|
||||
goto done;
|
||||
if (action == CODEC_ACTION_NULL)
|
||||
action = ci->get_command(¶m);
|
||||
|
||||
if (ci->seek_time) {
|
||||
ci->set_elapsed(ci->seek_time);
|
||||
if (action == CODEC_ACTION_HALT)
|
||||
return CODEC_OK;
|
||||
|
||||
if (action == CODEC_ACTION_SEEK_TIME) {
|
||||
/* Do not allow seeking beyond the file's length */
|
||||
if ((unsigned) ci->seek_time > ci->id3->length) {
|
||||
if ((unsigned) param > ci->id3->length) {
|
||||
ci->set_elapsed(ci->id3->length);
|
||||
ci->seek_complete();
|
||||
goto done;
|
||||
return CODEC_OK;
|
||||
}
|
||||
|
||||
ci->seek_buffer(rmctx.data_offset + DATA_HEADER_SIZE);
|
||||
|
@ -132,22 +134,24 @@ seek_start :
|
|||
rmctx.frame_number = 0;
|
||||
|
||||
/* Seek to the start of the track */
|
||||
if (ci->seek_time == 1) {
|
||||
if (param == 0) {
|
||||
ci->set_elapsed(0);
|
||||
ci->seek_complete();
|
||||
action = CODEC_ACTION_NULL;
|
||||
goto seek_start;
|
||||
}
|
||||
num_units = ((ci->seek_time)/(sps*1000*8/rmctx.bit_rate))/(h*(fs/sps));
|
||||
num_units = (param/(sps*1000*8/rmctx.bit_rate))/(h*(fs/sps));
|
||||
ci->seek_buffer(rmctx.data_offset + DATA_HEADER_SIZE + consumed * num_units);
|
||||
bit_buffer = (uint8_t *) ci->request_buffer(&buff_size, scrambling_unit_size);
|
||||
consumed = rm_get_packet(&bit_buffer, &rmctx, &pkt);
|
||||
if(consumed < 0) {
|
||||
DEBUGF("rm_get_packet failed\n");
|
||||
ci->seek_complete();
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
packet_count = rmctx.nb_packets - rmctx.audio_pkt_cnt * num_units;
|
||||
rmctx.frame_number = ((ci->seek_time)/(sps*1000*8/rmctx.bit_rate));
|
||||
while(rmctx.audiotimestamp > (unsigned) ci->seek_time) {
|
||||
rmctx.frame_number = (param/(sps*1000*8/rmctx.bit_rate));
|
||||
while(rmctx.audiotimestamp > (unsigned) param) {
|
||||
rmctx.audio_pkt_cnt = 0;
|
||||
ci->seek_buffer(rmctx.data_offset + DATA_HEADER_SIZE + consumed * (num_units-1));
|
||||
bit_buffer = (uint8_t *) ci->request_buffer(&buff_size, scrambling_unit_size);
|
||||
|
@ -155,11 +159,14 @@ seek_start :
|
|||
packet_count += rmctx.audio_pkt_cnt;
|
||||
num_units--;
|
||||
}
|
||||
time_offset = ci->seek_time - rmctx.audiotimestamp;
|
||||
time_offset = param - rmctx.audiotimestamp;
|
||||
i = (time_offset/((sps * 8 * 1000)/rmctx.bit_rate));
|
||||
ci->set_elapsed(rmctx.audiotimestamp+(1000*8*sps/rmctx.bit_rate)*i);
|
||||
ci->seek_complete();
|
||||
}
|
||||
|
||||
action = CODEC_ACTION_NULL;
|
||||
|
||||
res = cook_decode_frame(&rmctx,&q, rm_outbuf, &datasize, pkt.frames[i], rmctx.block_align);
|
||||
rmctx.frame_number++;
|
||||
|
||||
|
@ -181,9 +188,5 @@ seek_start :
|
|||
ci->advance_buffer(consumed);
|
||||
}
|
||||
|
||||
done :
|
||||
if (ci->request_next_track())
|
||||
goto next_track;
|
||||
|
||||
return CODEC_OK;
|
||||
}
|
||||
|
|
|
@ -418,40 +418,40 @@ static bool flac_seek_offset(FLACContext* fc, uint32_t offset) {
|
|||
}
|
||||
|
||||
/* this is the codec entry point */
|
||||
enum codec_status codec_main(void)
|
||||
enum codec_status codec_main(enum codec_entry_call_reason reason)
|
||||
{
|
||||
if (reason == CODEC_LOAD) {
|
||||
/* Generic codec initialisation */
|
||||
ci->configure(DSP_SET_SAMPLE_DEPTH, FLAC_OUTPUT_DEPTH-1);
|
||||
}
|
||||
|
||||
return CODEC_OK;
|
||||
}
|
||||
|
||||
/* this is called for each file to process */
|
||||
enum codec_status codec_run(void)
|
||||
{
|
||||
int8_t *buf;
|
||||
FLACContext fc;
|
||||
uint32_t samplesdone = 0;
|
||||
uint32_t samplesdone;
|
||||
uint32_t elapsedtime;
|
||||
size_t bytesleft;
|
||||
int consumed;
|
||||
int res;
|
||||
int frame;
|
||||
int retval;
|
||||
|
||||
/* Generic codec initialisation */
|
||||
ci->configure(DSP_SET_SAMPLE_DEPTH, FLAC_OUTPUT_DEPTH-1);
|
||||
|
||||
next_track:
|
||||
retval = CODEC_OK;
|
||||
intptr_t param;
|
||||
|
||||
if (codec_init()) {
|
||||
LOGF("FLAC: Error initialising codec\n");
|
||||
retval = CODEC_ERROR;
|
||||
goto exit;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
if (codec_wait_taginfo() != 0)
|
||||
goto done;
|
||||
|
||||
/* Need to save offset for later use (cleared indirectly by flac_init) */
|
||||
samplesdone = ci->id3->offset;
|
||||
|
||||
if (!flac_init(&fc,ci->id3->first_frame_offset)) {
|
||||
LOGF("FLAC: Error initialising codec\n");
|
||||
retval = CODEC_ERROR;
|
||||
goto done;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency);
|
||||
|
@ -459,35 +459,34 @@ next_track:
|
|||
STEREO_MONO : STEREO_NONINTERLEAVED);
|
||||
codec_set_replaygain(ci->id3);
|
||||
|
||||
if (samplesdone) {
|
||||
flac_seek_offset(&fc, samplesdone);
|
||||
samplesdone=0;
|
||||
}
|
||||
|
||||
/* The main decoding loop */
|
||||
frame=0;
|
||||
buf = ci->request_buffer(&bytesleft, MAX_FRAMESIZE);
|
||||
while (bytesleft) {
|
||||
ci->yield();
|
||||
if (ci->stop_codec || ci->new_track) {
|
||||
enum codec_command_action action = ci->get_command(¶m);
|
||||
|
||||
if (action == CODEC_ACTION_HALT)
|
||||
break;
|
||||
}
|
||||
|
||||
/* Deal with any pending seek requests */
|
||||
if (ci->seek_time) {
|
||||
if (flac_seek(&fc,(uint32_t)(((uint64_t)(ci->seek_time-1)
|
||||
if (action == CODEC_ACTION_SEEK_TIME) {
|
||||
if (flac_seek(&fc,(uint32_t)(((uint64_t)param
|
||||
*ci->id3->frequency)/1000))) {
|
||||
/* Refill the input buffer */
|
||||
buf = ci->request_buffer(&bytesleft, MAX_FRAMESIZE);
|
||||
}
|
||||
|
||||
ci->set_elapsed(param);
|
||||
ci->seek_complete();
|
||||
}
|
||||
|
||||
if((res=flac_decode_frame(&fc,decoded0,decoded1,buf,
|
||||
bytesleft,ci->yield)) < 0) {
|
||||
LOGF("FLAC: Frame %d, error %d\n",frame,res);
|
||||
retval = CODEC_ERROR;
|
||||
goto done;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
consumed=fc.gb.index/8;
|
||||
frame++;
|
||||
|
@ -507,14 +506,7 @@ next_track:
|
|||
|
||||
buf = ci->request_buffer(&bytesleft, MAX_FRAMESIZE);
|
||||
}
|
||||
retval = CODEC_OK;
|
||||
|
||||
done:
|
||||
LOGF("FLAC: Decoded %lu samples\n",(unsigned long)samplesdone);
|
||||
|
||||
if (ci->request_next_track())
|
||||
goto next_track;
|
||||
|
||||
exit:
|
||||
return retval;
|
||||
return CODEC_OK;
|
||||
}
|
||||
|
|
|
@ -33,6 +33,16 @@ unsigned char* mp3buf; // The actual MP3 buffer from Rockbox
|
|||
unsigned char* mallocbuf; // 512K from the start of MP3 buffer
|
||||
unsigned char* filebuf; // The rest of the MP3 buffer
|
||||
|
||||
/* this is the default codec entry point for when nothing needs to be done
|
||||
on load or unload */
|
||||
enum codec_status __attribute__((weak))
|
||||
codec_main(enum codec_entry_call_reason reason)
|
||||
{
|
||||
/* Nothing to do */
|
||||
return CODEC_OK;
|
||||
(void)reason;
|
||||
}
|
||||
|
||||
int codec_init(void)
|
||||
{
|
||||
mem_ptr = 0;
|
||||
|
@ -41,7 +51,7 @@ int codec_init(void)
|
|||
return 0;
|
||||
}
|
||||
|
||||
void codec_set_replaygain(struct mp3entry* id3)
|
||||
void codec_set_replaygain(const struct mp3entry *id3)
|
||||
{
|
||||
ci->configure(DSP_SET_TRACK_GAIN, id3->track_gain);
|
||||
ci->configure(DSP_SET_ALBUM_GAIN, id3->album_gain);
|
||||
|
@ -49,19 +59,6 @@ void codec_set_replaygain(struct mp3entry* id3)
|
|||
ci->configure(DSP_SET_ALBUM_PEAK, id3->album_peak);
|
||||
}
|
||||
|
||||
/* Note: codec really needs its own private metdata copy for the current
|
||||
track being processed in order to be stable. */
|
||||
int codec_wait_taginfo(void)
|
||||
{
|
||||
while (!*ci->taginfo_ready && !ci->stop_codec && !ci->new_track)
|
||||
ci->sleep(0);
|
||||
if (ci->stop_codec)
|
||||
return -1;
|
||||
if (ci->new_track)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Various "helper functions" common to all the xxx2wav decoder plugins */
|
||||
|
||||
|
||||
|
|
|
@ -156,8 +156,7 @@ static inline unsigned int bs_generic(unsigned int v, int mode)
|
|||
/* Various codec helper functions */
|
||||
|
||||
int codec_init(void);
|
||||
void codec_set_replaygain(struct mp3entry* id3);
|
||||
int codec_wait_taginfo(void); /* 0 = success */
|
||||
void codec_set_replaygain(const struct mp3entry *id3);
|
||||
|
||||
#ifdef RB_PROFILE
|
||||
void __cyg_profile_func_enter(void *this_fn, void *call_site)
|
||||
|
|
|
@ -1218,47 +1218,37 @@ void synthrender(int32_t *renderbuffer, int samplecount)
|
|||
}
|
||||
}
|
||||
|
||||
/* this is the codec entry point */
|
||||
enum codec_status codec_main(enum codec_entry_call_reason reason)
|
||||
{
|
||||
if (reason == CODEC_LOAD) {
|
||||
/* Make use of 44.1khz */
|
||||
ci->configure(DSP_SET_FREQUENCY, 44100);
|
||||
/* Sample depth is 28 bit host endian */
|
||||
ci->configure(DSP_SET_SAMPLE_DEPTH, 28);
|
||||
/* Stereo output */
|
||||
ci->configure(DSP_SET_STEREO_MODE, STEREO_INTERLEAVED);
|
||||
}
|
||||
|
||||
enum codec_status codec_main(void)
|
||||
return CODEC_OK;
|
||||
}
|
||||
|
||||
/* this is called for each file to process */
|
||||
enum codec_status codec_run(void)
|
||||
{
|
||||
size_t n;
|
||||
unsigned char *modfile;
|
||||
int old_patterntableposition;
|
||||
|
||||
int bytesdone;
|
||||
intptr_t param;
|
||||
|
||||
next_track:
|
||||
if (codec_init()) {
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
if (codec_wait_taginfo() != 0)
|
||||
goto request_next_track;
|
||||
|
||||
codec_set_replaygain(ci->id3);
|
||||
|
||||
/* Load MOD file */
|
||||
/*
|
||||
* This is the save way
|
||||
|
||||
size_t bytesfree;
|
||||
unsigned int filesize;
|
||||
|
||||
p = modfile;
|
||||
bytesfree=sizeof(modfile);
|
||||
while ((n = ci->read_filebuf(p, bytesfree)) > 0) {
|
||||
p += n;
|
||||
bytesfree -= n;
|
||||
if (bytesfree == 0)
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
filesize = p-modfile;
|
||||
|
||||
if (filesize == 0)
|
||||
return CODEC_ERROR;
|
||||
*/
|
||||
|
||||
/* Directly use mod in buffer */
|
||||
ci->seek_buffer(0);
|
||||
modfile = ci->request_buffer(&n, ci->filesize);
|
||||
if (!modfile || n < (size_t)ci->filesize) {
|
||||
|
@ -1268,27 +1258,22 @@ next_track:
|
|||
initmodplayer();
|
||||
loadmod(modfile);
|
||||
|
||||
/* Make use of 44.1khz */
|
||||
ci->configure(DSP_SET_FREQUENCY, 44100);
|
||||
/* Sample depth is 28 bit host endian */
|
||||
ci->configure(DSP_SET_SAMPLE_DEPTH, 28);
|
||||
/* Stereo output */
|
||||
ci->configure(DSP_SET_STEREO_MODE, STEREO_INTERLEAVED);
|
||||
|
||||
/* The main decoder loop */
|
||||
ci->set_elapsed(0);
|
||||
bytesdone = 0;
|
||||
old_patterntableposition = 0;
|
||||
|
||||
while (1) {
|
||||
ci->yield();
|
||||
if (ci->stop_codec || ci->new_track)
|
||||
enum codec_command_action action = ci->get_command(¶m);
|
||||
|
||||
if (action == CODEC_ACTION_HALT)
|
||||
break;
|
||||
|
||||
if (ci->seek_time) {
|
||||
/* New time is ready in ci->seek_time */
|
||||
modplayer.patterntableposition = ci->seek_time/1000;
|
||||
if (action == CODEC_ACTION_SEEK_TIME) {
|
||||
/* New time is ready in param */
|
||||
modplayer.patterntableposition = param/1000;
|
||||
modplayer.currentline = 0;
|
||||
ci->set_elapsed(modplayer.patterntableposition*1000+500);
|
||||
ci->seek_complete();
|
||||
}
|
||||
|
||||
|
@ -1305,9 +1290,5 @@ next_track:
|
|||
|
||||
}
|
||||
|
||||
request_next_track:
|
||||
if (ci->request_next_track())
|
||||
goto next_track;
|
||||
|
||||
return CODEC_OK;
|
||||
}
|
||||
|
|
|
@ -2584,23 +2584,32 @@ static bool enc_init(void)
|
|||
return true;
|
||||
} /* enc_init */
|
||||
|
||||
enum codec_status codec_main(void)
|
||||
/* this is the codec entry point */
|
||||
enum codec_status codec_main(enum codec_entry_call_reason reason)
|
||||
{
|
||||
/* Generic codec initialisation */
|
||||
if (reason == CODEC_LOAD) {
|
||||
if (!enc_init())
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
else if (reason == CODEC_UNLOAD) {
|
||||
/* reset parameters to initial state */
|
||||
ci->enc_set_parameters(NULL);
|
||||
}
|
||||
|
||||
return CODEC_OK;
|
||||
}
|
||||
|
||||
/* this is called for each file to process */
|
||||
enum codec_status codec_run(void)
|
||||
{
|
||||
/* main encoding loop */
|
||||
while (!ci->stop_codec)
|
||||
{
|
||||
char *buffer;
|
||||
|
||||
while ((buffer = ci->enc_get_pcm_data(pcm_chunk_size)) != NULL)
|
||||
while(ci->get_command(NULL) != CODEC_ACTION_HALT)
|
||||
{
|
||||
char *buffer = buffer = ci->enc_get_pcm_data(pcm_chunk_size);
|
||||
struct enc_chunk_hdr *chunk;
|
||||
|
||||
if (ci->stop_codec)
|
||||
break;
|
||||
if(buffer == NULL)
|
||||
continue;
|
||||
|
||||
chunk = ci->enc_get_chunk();
|
||||
chunk->enc_data = ENC_CHUNK_SKIP_HDR(chunk->enc_data, chunk);
|
||||
|
@ -2614,15 +2623,7 @@ enum codec_status codec_main(void)
|
|||
}
|
||||
|
||||
ci->enc_finish_chunk();
|
||||
|
||||
ci->yield();
|
||||
}
|
||||
|
||||
ci->yield();
|
||||
}
|
||||
|
||||
/* reset parameters to initial state */
|
||||
ci->enc_set_parameters(NULL);
|
||||
|
||||
return CODEC_OK;
|
||||
} /* codec_start */
|
||||
}
|
||||
|
|
|
@ -268,8 +268,8 @@ static bool mad_synth_thread_create(void)
|
|||
|
||||
static void mad_synth_thread_quit(void)
|
||||
{
|
||||
/*mop up COP thread*/
|
||||
die=1;
|
||||
/* mop up COP thread */
|
||||
die = 1;
|
||||
ci->semaphore_release(&synth_pending_sem);
|
||||
ci->thread_wait(mad_synth_thread_id);
|
||||
ci->cpucache_invalidate();
|
||||
|
@ -299,9 +299,30 @@ static inline void mad_synth_thread_unwait_pcm(void)
|
|||
#endif /* MPA_SYNTH_ON_COP */
|
||||
|
||||
/* this is the codec entry point */
|
||||
enum codec_status codec_main(void)
|
||||
enum codec_status codec_main(enum codec_entry_call_reason reason)
|
||||
{
|
||||
if (reason == CODEC_LOAD) {
|
||||
/* Create a decoder instance */
|
||||
if (codec_init())
|
||||
return CODEC_ERROR;
|
||||
|
||||
ci->configure(DSP_SET_SAMPLE_DEPTH, MAD_F_FRACBITS);
|
||||
|
||||
/* does nothing on 1 processor systems except return true */
|
||||
if(!mad_synth_thread_create())
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
else if (reason == CODEC_UNLOAD) {
|
||||
/* mop up COP thread - MT only */
|
||||
mad_synth_thread_quit();
|
||||
}
|
||||
|
||||
return CODEC_OK;
|
||||
}
|
||||
|
||||
/* this is called for each file to process */
|
||||
enum codec_status codec_run(void)
|
||||
{
|
||||
int status;
|
||||
size_t size;
|
||||
int file_end;
|
||||
int samples_to_skip; /* samples to skip in total for this file (at start) */
|
||||
|
@ -312,27 +333,12 @@ enum codec_status codec_main(void)
|
|||
unsigned long current_frequency = 0;
|
||||
int framelength;
|
||||
int padding = MAD_BUFFER_GUARD; /* to help mad decode the last frame */
|
||||
|
||||
if (codec_init())
|
||||
return CODEC_ERROR;
|
||||
|
||||
/* Create a decoder instance */
|
||||
|
||||
ci->configure(DSP_SET_SAMPLE_DEPTH, MAD_F_FRACBITS);
|
||||
|
||||
/*does nothing on 1 processor systems except return true*/
|
||||
if(!mad_synth_thread_create())
|
||||
return CODEC_ERROR;
|
||||
|
||||
next_track:
|
||||
status = CODEC_OK;
|
||||
intptr_t param;
|
||||
|
||||
/* Reinitializing seems to be necessary to avoid playback quircks when seeking. */
|
||||
init_mad();
|
||||
|
||||
file_end = 0;
|
||||
if (codec_wait_taginfo() != 0)
|
||||
goto request_next_track;
|
||||
|
||||
ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency);
|
||||
current_frequency = ci->id3->frequency;
|
||||
|
@ -379,29 +385,35 @@ next_track:
|
|||
|
||||
/* This is the decoding loop. */
|
||||
while (1) {
|
||||
ci->yield();
|
||||
if (ci->stop_codec || ci->new_track)
|
||||
enum codec_command_action action = ci->get_command(¶m);
|
||||
|
||||
if (action == CODEC_ACTION_HALT)
|
||||
break;
|
||||
|
||||
if (ci->seek_time) {
|
||||
if (action == CODEC_ACTION_SEEK_TIME) {
|
||||
int newpos;
|
||||
|
||||
/*make sure the synth thread is idle before seeking - MT only*/
|
||||
mad_synth_thread_wait_pcm();
|
||||
mad_synth_thread_unwait_pcm();
|
||||
|
||||
samplesdone = ((int64_t)(ci->seek_time-1))*current_frequency/1000;
|
||||
samplesdone = ((int64_t)param)*current_frequency/1000;
|
||||
|
||||
if (ci->seek_time-1 == 0) {
|
||||
if (param == 0) {
|
||||
newpos = ci->id3->first_frame_offset;
|
||||
samples_to_skip = start_skip;
|
||||
} else {
|
||||
newpos = get_file_pos(ci->seek_time-1);
|
||||
newpos = get_file_pos(param);
|
||||
samples_to_skip = 0;
|
||||
}
|
||||
|
||||
if (!ci->seek_buffer(newpos))
|
||||
{
|
||||
ci->seek_complete();
|
||||
break;
|
||||
}
|
||||
|
||||
ci->set_elapsed((samplesdone * 1000) / current_frequency);
|
||||
ci->seek_complete();
|
||||
init_mad();
|
||||
framelength = 0;
|
||||
|
@ -435,8 +447,7 @@ next_track:
|
|||
continue;
|
||||
} else {
|
||||
/* Some other unrecoverable error */
|
||||
status = CODEC_ERROR;
|
||||
break;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -504,12 +515,5 @@ next_track:
|
|||
framelength - stop_skip);
|
||||
}
|
||||
|
||||
request_next_track:
|
||||
if (ci->request_next_track())
|
||||
goto next_track;
|
||||
|
||||
/*mop up COP thread - MT only*/
|
||||
mad_synth_thread_quit();
|
||||
|
||||
return status;
|
||||
return CODEC_OK;
|
||||
}
|
||||
|
|
|
@ -52,8 +52,20 @@ static mpc_int32_t get_size_impl(mpc_reader *reader)
|
|||
return ci->filesize;
|
||||
}
|
||||
|
||||
/* This is the codec entry point. */
|
||||
enum codec_status codec_main(void)
|
||||
/* this is the codec entry point */
|
||||
enum codec_status codec_main(enum codec_entry_call_reason reason)
|
||||
{
|
||||
if (reason == CODEC_LOAD) {
|
||||
/* musepack's sample representation is 18.14
|
||||
* DSP_SET_SAMPLE_DEPTH = 14 (FRACT) + 16 (NATIVE) - 1 (SIGN) = 29 */
|
||||
ci->configure(DSP_SET_SAMPLE_DEPTH, 29);
|
||||
}
|
||||
|
||||
return CODEC_OK;
|
||||
}
|
||||
|
||||
/* this is called for each file to process */
|
||||
enum codec_status codec_run(void)
|
||||
{
|
||||
mpc_int64_t samplesdone;
|
||||
uint32_t frequency; /* 0.1 kHz accuracy */
|
||||
|
@ -64,39 +76,27 @@ enum codec_status codec_main(void)
|
|||
mpc_streaminfo info;
|
||||
mpc_frame_info frame;
|
||||
mpc_demux *demux = NULL;
|
||||
int retval;
|
||||
intptr_t param;
|
||||
|
||||
frame.buffer = sample_buffer;
|
||||
|
||||
/* musepack's sample representation is 18.14
|
||||
* DSP_SET_SAMPLE_DEPTH = 14 (FRACT) + 16 (NATIVE) - 1 (SIGN) = 29 */
|
||||
ci->configure(DSP_SET_SAMPLE_DEPTH, 29);
|
||||
|
||||
/* Create a decoder instance */
|
||||
reader.read = read_impl;
|
||||
reader.seek = seek_impl;
|
||||
reader.tell = tell_impl;
|
||||
reader.get_size = get_size_impl;
|
||||
|
||||
next_track:
|
||||
retval = CODEC_OK;
|
||||
|
||||
if (codec_init())
|
||||
{
|
||||
retval = CODEC_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
return CODEC_ERROR;
|
||||
|
||||
if (codec_wait_taginfo() != 0)
|
||||
goto done;
|
||||
/* Prep position */
|
||||
ci->seek_buffer(0);
|
||||
|
||||
/* Initialize demux/decoder. */
|
||||
demux = mpc_demux_init(&reader);
|
||||
if (NULL == demux)
|
||||
{
|
||||
retval = CODEC_ERROR;
|
||||
goto done;
|
||||
}
|
||||
return CODEC_ERROR;
|
||||
|
||||
/* Read file's streaminfo data. */
|
||||
mpc_demux_get_info(demux, &info);
|
||||
|
||||
|
@ -118,10 +118,7 @@ next_track:
|
|||
else if (info.channels == 1)
|
||||
ci->configure(DSP_SET_STEREO_MODE, STEREO_MONO);
|
||||
else
|
||||
{
|
||||
retval = CODEC_ERROR;
|
||||
goto done;
|
||||
}
|
||||
return CODEC_ERROR;
|
||||
|
||||
codec_set_replaygain(ci->id3);
|
||||
|
||||
|
@ -142,21 +139,24 @@ next_track:
|
|||
/* This is the decoding loop. */
|
||||
do
|
||||
{
|
||||
enum codec_command_action action = ci->get_command(¶m);
|
||||
|
||||
if (action == CODEC_ACTION_HALT)
|
||||
return CODEC_OK;
|
||||
|
||||
/* Complete seek handler. */
|
||||
if (ci->seek_time)
|
||||
if (action == CODEC_ACTION_SEEK_TIME)
|
||||
{
|
||||
mpc_int64_t new_offset = ((ci->seek_time - 1)/10)*frequency;
|
||||
mpc_int64_t new_offset = (param/10)*frequency;
|
||||
if (mpc_demux_seek_sample(demux, new_offset) == MPC_STATUS_OK)
|
||||
{
|
||||
samplesdone = new_offset;
|
||||
ci->set_elapsed(ci->seek_time);
|
||||
}
|
||||
ci->seek_complete();
|
||||
}
|
||||
|
||||
/* Stop or skip occured, exit decoding loop. */
|
||||
if (ci->stop_codec || ci->new_track)
|
||||
break;
|
||||
elapsed_time = (samplesdone*10)/frequency;
|
||||
ci->set_elapsed(elapsed_time);
|
||||
ci->seek_complete();
|
||||
}
|
||||
|
||||
/* Decode one frame. */
|
||||
status = mpc_demux_decode(demux, &frame);
|
||||
|
@ -164,8 +164,7 @@ next_track:
|
|||
if (frame.bits == -1)
|
||||
{
|
||||
/* Decoding error, exit decoding loop. */
|
||||
retval = (status == MPC_STATUS_OK) ? CODEC_OK : CODEC_ERROR;
|
||||
goto done;
|
||||
return (status == MPC_STATUS_OK) ? CODEC_OK : CODEC_ERROR;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -181,11 +180,4 @@ next_track:
|
|||
ci->set_offset( (samplesdone * byterate)/(frequency*100) );
|
||||
}
|
||||
} while (true);
|
||||
|
||||
done:
|
||||
if (ci->request_next_track())
|
||||
goto next_track;
|
||||
|
||||
exit:
|
||||
return retval;
|
||||
}
|
||||
|
|
|
@ -4307,46 +4307,44 @@ static void set_codec_track(int t, int d) {
|
|||
nSilenceTrackMS=5000;
|
||||
SetFadeTime(track,track+fade, fNSFPlaybackSpeed,def);
|
||||
}
|
||||
ci->id3->elapsed=d*1000; /* d is track no to display */
|
||||
ci->set_elapsed(d*1000); /* d is track no to display */
|
||||
}
|
||||
|
||||
/** Operational info **/
|
||||
static int track = 0;
|
||||
static char last_path[MAX_PATH];
|
||||
static int dontresettrack = 0;
|
||||
|
||||
/* this is the codec entry point */
|
||||
enum codec_status codec_main(void)
|
||||
enum codec_status codec_main(enum codec_entry_call_reason reason)
|
||||
{
|
||||
if (reason == CODEC_LOAD) {
|
||||
/* we only render 16 bits, 44.1KHz, Stereo */
|
||||
ci->configure(DSP_SET_SAMPLE_DEPTH, 16);
|
||||
ci->configure(DSP_SET_FREQUENCY, 44100);
|
||||
ci->configure(DSP_SET_STEREO_MODE, STEREO_MONO);
|
||||
|
||||
RebuildOutputTables();
|
||||
}
|
||||
|
||||
return CODEC_OK;
|
||||
}
|
||||
|
||||
/* this is called for each file to process */
|
||||
enum codec_status codec_run(void)
|
||||
{
|
||||
int written;
|
||||
uint8_t *buf;
|
||||
size_t n;
|
||||
int endofstream; /* end of stream flag */
|
||||
int track;
|
||||
int dontresettrack;
|
||||
char last_path[MAX_PATH];
|
||||
int usingplaylist;
|
||||
int usingplaylist = 0;
|
||||
|
||||
/* we only render 16 bits */
|
||||
ci->configure(DSP_SET_SAMPLE_DEPTH, 16);
|
||||
|
||||
ci->configure(DSP_SET_FREQUENCY, 44100);
|
||||
ci->configure(DSP_SET_STEREO_MODE, STEREO_MONO);
|
||||
|
||||
RebuildOutputTables();
|
||||
|
||||
dontresettrack=0;
|
||||
last_path[0]='\0';
|
||||
track=0;
|
||||
|
||||
next_track:
|
||||
usingplaylist=0;
|
||||
DEBUGF("NSF: next_track\n");
|
||||
if (codec_init()) {
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
DEBUGF("NSF: after init\n");
|
||||
|
||||
|
||||
/* wait for track info to load */
|
||||
if (codec_wait_taginfo() != 0)
|
||||
goto request_next_track;
|
||||
|
||||
codec_set_replaygain(ci->id3);
|
||||
|
||||
/* Read the entire file */
|
||||
|
@ -4408,23 +4406,28 @@ init_nsf:
|
|||
reset_profile_timers();
|
||||
|
||||
while (!endofstream) {
|
||||
intptr_t param;
|
||||
enum codec_command_action action = ci->get_command(¶m);
|
||||
|
||||
ci->yield();
|
||||
if (ci->stop_codec || ci->new_track) {
|
||||
if (action == CODEC_ACTION_HALT)
|
||||
break;
|
||||
}
|
||||
|
||||
if (ci->seek_time >0) {
|
||||
track=ci->seek_time/1000;
|
||||
if (action == CODEC_ACTION_SEEK_TIME) {
|
||||
if (param > 0) {
|
||||
track=param/1000;
|
||||
if (usingplaylist) {
|
||||
if (track>=nPlaylistSize) break;
|
||||
} else {
|
||||
if (track>=nTrackCount) break;
|
||||
}
|
||||
ci->seek_complete();
|
||||
dontresettrack=1;
|
||||
ci->seek_complete();
|
||||
goto init_nsf;
|
||||
}
|
||||
else {
|
||||
ci->seek_complete();
|
||||
}
|
||||
}
|
||||
|
||||
ENTER_TIMER(total);
|
||||
written=GetSamples((uint8_t*)samples,WAV_CHUNK_SIZE/2);
|
||||
|
@ -4449,22 +4452,17 @@ init_nsf:
|
|||
|
||||
print_timers(last_path,track);
|
||||
|
||||
request_next_track:
|
||||
if (ci->request_next_track()) {
|
||||
if (ci->global_settings->repeat_mode==REPEAT_ONE) {
|
||||
/* in repeat one mode just advance to the next track */
|
||||
track++;
|
||||
if (track>=nTrackCount) track=0;
|
||||
dontresettrack=1;
|
||||
/* at this point we can't tell if another file has been selected */
|
||||
goto next_track;
|
||||
} else {
|
||||
/* otherwise do a proper load of the next file */
|
||||
dontresettrack=0;
|
||||
last_path[0]='\0';
|
||||
}
|
||||
goto next_track; /* when we fall through here we'll reload the file */
|
||||
}
|
||||
|
||||
return CODEC_OK;
|
||||
}
|
||||
|
|
|
@ -35,8 +35,21 @@ static void init_rm(RMContext *rmctx)
|
|||
|
||||
static RMContext rmctx;
|
||||
static RMPacket pkt;
|
||||
|
||||
/* this is the codec entry point */
|
||||
enum codec_status codec_main(void)
|
||||
enum codec_status codec_main(enum codec_entry_call_reason reason)
|
||||
{
|
||||
if (reason == CODEC_LOAD) {
|
||||
/* Generic codec initialisation */
|
||||
ci->configure(DSP_SET_STEREO_MODE, STEREO_NONINTERLEAVED);
|
||||
ci->configure(DSP_SET_SAMPLE_DEPTH, 29);
|
||||
}
|
||||
|
||||
return CODEC_OK;
|
||||
}
|
||||
|
||||
/* this is called for each file to process */
|
||||
enum codec_status codec_run(void)
|
||||
{
|
||||
static NeAACDecFrameInfo frame_info;
|
||||
NeAACDecHandle decoder;
|
||||
|
@ -49,26 +62,21 @@ enum codec_status codec_main(void)
|
|||
unsigned char c = 0; /* channels */
|
||||
int playback_on = -1;
|
||||
size_t resume_offset;
|
||||
|
||||
/* Generic codec initialisation */
|
||||
ci->configure(DSP_SET_STEREO_MODE, STEREO_NONINTERLEAVED);
|
||||
ci->configure(DSP_SET_SAMPLE_DEPTH, 29);
|
||||
|
||||
next_track:
|
||||
err = CODEC_OK;
|
||||
intptr_t param;
|
||||
enum codec_command_action action = CODEC_ACTION_NULL;
|
||||
|
||||
if (codec_init()) {
|
||||
DEBUGF("FAAD: Codec init error\n");
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
if (codec_wait_taginfo() != 0)
|
||||
goto done;
|
||||
|
||||
resume_offset = ci->id3->offset;
|
||||
|
||||
ci->memset(&rmctx,0,sizeof(RMContext));
|
||||
ci->memset(&pkt,0,sizeof(RMPacket));
|
||||
|
||||
ci->seek_buffer(0);
|
||||
|
||||
init_rm(&rmctx);
|
||||
ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency);
|
||||
codec_set_replaygain(ci->id3);
|
||||
|
@ -78,9 +86,9 @@ next_track:
|
|||
|
||||
if (!decoder) {
|
||||
DEBUGF("FAAD: Decode open error\n");
|
||||
err = CODEC_ERROR;
|
||||
goto done;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
NeAACDecConfigurationPtr conf = NeAACDecGetCurrentConfiguration(decoder);
|
||||
conf->outputFormat = FAAD_FMT_16BIT; /* irrelevant, we don't convert */
|
||||
NeAACDecSetConfiguration(decoder, conf);
|
||||
|
@ -91,8 +99,7 @@ next_track:
|
|||
|
||||
if (err) {
|
||||
DEBUGF("FAAD: DecInit: %d, %d\n", err, decoder->object_type);
|
||||
err = CODEC_ERROR;
|
||||
goto done;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
/* check for a mid-track resume and force a seek time accordingly */
|
||||
|
@ -100,36 +107,38 @@ next_track:
|
|||
resume_offset -= rmctx.data_offset + DATA_HEADER_SIZE;
|
||||
/* put number of subpackets to skip in resume_offset */
|
||||
resume_offset /= (rmctx.block_align + PACKET_HEADER_SIZE);
|
||||
ci->seek_time = (int)resume_offset * ((rmctx.block_align * 8 * 1000)/rmctx.bit_rate);
|
||||
param = (int)resume_offset * ((rmctx.block_align * 8 * 1000)/rmctx.bit_rate);
|
||||
action = CODEC_ACTION_SEEK_TIME;
|
||||
}
|
||||
|
||||
ci->id3->frequency = s;
|
||||
ci->id3->frequency = s; /* FIXME: Won't get it to the UI */
|
||||
ci->set_elapsed(0);
|
||||
ci->advance_buffer(rmctx.data_offset + DATA_HEADER_SIZE);
|
||||
|
||||
/* The main decoding loop */
|
||||
seek_start:
|
||||
while (1) {
|
||||
ci->yield();
|
||||
if (ci->stop_codec || ci->new_track) {
|
||||
if (action == CODEC_ACTION_NULL)
|
||||
action = ci->get_command(¶m);
|
||||
|
||||
if (action == CODEC_ACTION_HALT)
|
||||
break;
|
||||
}
|
||||
|
||||
if (ci->seek_time) {
|
||||
|
||||
if (action == CODEC_ACTION_SEEK_TIME) {
|
||||
/* Do not allow seeking beyond the file's length */
|
||||
if ((unsigned) ci->seek_time > ci->id3->length) {
|
||||
if ((unsigned) param > ci->id3->length) {
|
||||
ci->set_elapsed(ci->id3->length);
|
||||
ci->seek_complete();
|
||||
goto done;
|
||||
break;
|
||||
}
|
||||
|
||||
ci->seek_buffer(rmctx.data_offset + DATA_HEADER_SIZE);
|
||||
|
||||
/* Seek to the start of the track */
|
||||
if (ci->seek_time == 1) {
|
||||
if (param == 0) {
|
||||
ci->set_elapsed(0);
|
||||
ci->seek_complete();
|
||||
goto seek_start;
|
||||
action = CODEC_ACTION_NULL;
|
||||
continue;
|
||||
}
|
||||
|
||||
skipped = 0;
|
||||
|
@ -141,21 +150,30 @@ seek_start:
|
|||
if(playback_on == -1) {
|
||||
/* Error only if packet-parsing failed and playback hadn't started */
|
||||
DEBUGF("rm_get_packet failed\n");
|
||||
ci->seek_complete();
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
else
|
||||
goto done;
|
||||
else {
|
||||
ci->seek_complete();
|
||||
return CODEC_OK;
|
||||
}
|
||||
}
|
||||
skipped += pkt.length;
|
||||
if(pkt.timestamp > (unsigned)ci->seek_time) break;
|
||||
|
||||
if(pkt.timestamp > (unsigned)param)
|
||||
break;
|
||||
|
||||
ci->advance_buffer(pkt.length);
|
||||
}
|
||||
ci->seek_buffer(pkt_offset + rmctx.data_offset + DATA_HEADER_SIZE);
|
||||
buffer = ci->request_buffer(&n,rmctx.audio_framesize + 1000);
|
||||
NeAACDecPostSeekReset(decoder, decoder->frame);
|
||||
ci->set_elapsed(pkt.timestamp);
|
||||
ci->seek_complete();
|
||||
}
|
||||
|
||||
action = CODEC_ACTION_NULL;
|
||||
|
||||
/* Request the required number of bytes from the input buffer */
|
||||
buffer=ci->request_buffer(&n,rmctx.audio_framesize + 1000);
|
||||
consumed = rm_get_packet(&buffer, &rmctx, &pkt);
|
||||
|
@ -167,20 +185,20 @@ seek_start:
|
|||
return CODEC_ERROR;
|
||||
}
|
||||
else
|
||||
goto done;
|
||||
break;
|
||||
}
|
||||
|
||||
playback_on = 1;
|
||||
if (pkt.timestamp >= ci->id3->length)
|
||||
goto done;
|
||||
break;
|
||||
|
||||
/* Decode one block - returned samples will be host-endian */
|
||||
for(i = 0; i < rmctx.sub_packet_cnt; i++) {
|
||||
ret = NeAACDecDecode(decoder, &frame_info, buffer, rmctx.sub_packet_lengths[i]);
|
||||
buffer += rmctx.sub_packet_lengths[i];
|
||||
if (frame_info.error > 0) {
|
||||
DEBUGF("FAAD: decode error '%s'\n", NeAACDecGetErrorMessage(frame_info.error));
|
||||
err = CODEC_ERROR;
|
||||
goto exit;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
ci->pcmbuf_insert(decoder->time_out[0],
|
||||
decoder->time_out[1],
|
||||
|
@ -191,11 +209,5 @@ seek_start:
|
|||
ci->advance_buffer(pkt.length);
|
||||
}
|
||||
|
||||
done:
|
||||
if (ci->request_next_track())
|
||||
goto next_track;
|
||||
|
||||
exit:
|
||||
return err;
|
||||
return CODEC_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -37,7 +37,19 @@ static int32_t offset1[MAX_OFFSET_SIZE] IBSS_ATTR;
|
|||
static int8_t ibuf[MAX_BUFFER_SIZE] IBSS_ATTR;
|
||||
|
||||
/* this is the codec entry point */
|
||||
enum codec_status codec_main(void)
|
||||
enum codec_status codec_main(enum codec_entry_call_reason reason)
|
||||
{
|
||||
if (reason == CODEC_LOAD) {
|
||||
/* Generic codec initialisation */
|
||||
ci->configure(DSP_SET_STEREO_MODE, STEREO_NONINTERLEAVED);
|
||||
ci->configure(DSP_SET_SAMPLE_DEPTH, SHN_OUTPUT_DEPTH-1);
|
||||
}
|
||||
|
||||
return CODEC_OK;
|
||||
}
|
||||
|
||||
/* this is called for each file to process */
|
||||
enum codec_status codec_run(void)
|
||||
{
|
||||
ShortenContext sc;
|
||||
uint32_t samplesdone;
|
||||
|
@ -45,21 +57,14 @@ enum codec_status codec_main(void)
|
|||
int8_t *buf;
|
||||
int consumed, res, nsamples;
|
||||
size_t bytesleft;
|
||||
intptr_t param;
|
||||
|
||||
/* Generic codec initialisation */
|
||||
ci->configure(DSP_SET_STEREO_MODE, STEREO_NONINTERLEAVED);
|
||||
ci->configure(DSP_SET_SAMPLE_DEPTH, SHN_OUTPUT_DEPTH-1);
|
||||
|
||||
next_track:
|
||||
/* Codec initialization */
|
||||
if (codec_init()) {
|
||||
LOGF("Shorten: codec_init error\n");
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
if (codec_wait_taginfo() != 0)
|
||||
goto request_next_track;
|
||||
|
||||
codec_set_replaygain(ci->id3);
|
||||
|
||||
/* Shorten decoder initialization */
|
||||
|
@ -103,14 +108,15 @@ seek_start:
|
|||
samplesdone = 0;
|
||||
buf = ci->request_buffer(&bytesleft, MAX_BUFFER_SIZE);
|
||||
while (bytesleft) {
|
||||
ci->yield();
|
||||
if (ci->stop_codec || ci->new_track) {
|
||||
enum codec_command_action action = ci->get_command(¶m);
|
||||
|
||||
if (action == CODEC_ACTION_HALT)
|
||||
break;
|
||||
}
|
||||
|
||||
/* Seek to start of track */
|
||||
if (ci->seek_time == 1) {
|
||||
if (ci->seek_buffer(sc.header_bits/8 + ci->id3->first_frame_offset)) {
|
||||
if (action == CODEC_ACTION_SEEK_TIME) {
|
||||
if (param == 0 &&
|
||||
ci->seek_buffer(sc.header_bits/8 + ci->id3->first_frame_offset)) {
|
||||
sc.bitindex = sc.header_bits - 8*(sc.header_bits/8);
|
||||
ci->set_elapsed(0);
|
||||
ci->seek_complete();
|
||||
|
@ -128,7 +134,7 @@ seek_start:
|
|||
if (res == FN_ERROR) {
|
||||
LOGF("Shorten: shorten_decode_frames error (%lu)\n",
|
||||
(unsigned long)samplesdone);
|
||||
break;
|
||||
return CODEC_ERROR;
|
||||
} else {
|
||||
/* Insert decoded samples in pcmbuf */
|
||||
if (nsamples) {
|
||||
|
@ -153,9 +159,5 @@ seek_start:
|
|||
sc.bitindex = sc.gb.index - 8*consumed;
|
||||
}
|
||||
|
||||
request_next_track:
|
||||
if (ci->request_next_track())
|
||||
goto next_track;
|
||||
|
||||
return CODEC_OK;
|
||||
}
|
||||
|
|
|
@ -1203,34 +1203,47 @@ unsigned short LoadSIDFromMemory(void *pSidData, unsigned short *load_addr,
|
|||
return *load_addr;
|
||||
}
|
||||
|
||||
static int nSamplesRendered = 0;
|
||||
static int nSamplesPerCall = 882; /* This is PAL SID single speed (44100/50Hz) */
|
||||
static int nSamplesToRender = 0;
|
||||
|
||||
enum codec_status codec_main(void)
|
||||
/* this is the codec entry point */
|
||||
enum codec_status codec_main(enum codec_entry_call_reason reason)
|
||||
{
|
||||
if (reason == CODEC_LOAD) {
|
||||
/* Make use of 44.1khz */
|
||||
ci->configure(DSP_SWITCH_FREQUENCY, 44100);
|
||||
/* Sample depth is 28 bit host endian */
|
||||
ci->configure(DSP_SET_SAMPLE_DEPTH, 28);
|
||||
/* Mono output */
|
||||
ci->configure(DSP_SET_STEREO_MODE, STEREO_MONO);
|
||||
}
|
||||
|
||||
return CODEC_OK;
|
||||
}
|
||||
|
||||
/* this is called for each file to process */
|
||||
enum codec_status codec_run(void)
|
||||
{
|
||||
unsigned int filesize;
|
||||
|
||||
unsigned short load_addr, init_addr, play_addr;
|
||||
unsigned char subSongsMax, subSong, song_speed;
|
||||
intptr_t param;
|
||||
|
||||
int nSamplesRendered = 0;
|
||||
int nSamplesPerCall = 882; /* This is PAL SID single speed (44100/50Hz) */
|
||||
int nSamplesToRender = 0;
|
||||
|
||||
next_track:
|
||||
if (codec_init()) {
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
if (codec_wait_taginfo() != 0)
|
||||
goto request_next_track;
|
||||
|
||||
codec_set_replaygain(ci->id3);
|
||||
|
||||
/* Load SID file the read_filebuf callback will return the full requested
|
||||
* size if at all possible, so there is no need to loop */
|
||||
ci->seek_buffer(0);
|
||||
filesize = ci->read_filebuf(sidfile, sizeof(sidfile));
|
||||
|
||||
if (filesize == 0)
|
||||
if (filesize == 0) {
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
c64Init(44100);
|
||||
LoadSIDFromMemory(sidfile, &load_addr, &init_addr, &play_addr,
|
||||
|
@ -1239,37 +1252,30 @@ next_track:
|
|||
cpuJSR(init_addr, subSong); /* Start the song initialize */
|
||||
|
||||
|
||||
/* Make use of 44.1khz */
|
||||
ci->configure(DSP_SWITCH_FREQUENCY, 44100);
|
||||
/* Sample depth is 28 bit host endian */
|
||||
ci->configure(DSP_SET_SAMPLE_DEPTH, 28);
|
||||
/* Mono output */
|
||||
ci->configure(DSP_SET_STEREO_MODE, STEREO_MONO);
|
||||
|
||||
|
||||
/* Set the elapsed time to the current subsong (in seconds) */
|
||||
ci->set_elapsed(subSong*1000);
|
||||
|
||||
/* The main decoder loop */
|
||||
while (1) {
|
||||
ci->yield();
|
||||
if (ci->stop_codec || ci->new_track)
|
||||
enum codec_command_action action = ci->get_command(¶m);
|
||||
|
||||
if (action == CODEC_ACTION_HALT)
|
||||
break;
|
||||
|
||||
if (ci->seek_time) {
|
||||
/* New time is ready in ci->seek_time */
|
||||
if (action == CODEC_ACTION_SEEK_TIME) {
|
||||
/* New time is ready in param */
|
||||
|
||||
/* Start playing from scratch */
|
||||
c64Init(44100);
|
||||
LoadSIDFromMemory(sidfile, &load_addr, &init_addr, &play_addr, &subSongsMax, &subSong, &song_speed, filesize);
|
||||
LoadSIDFromMemory(sidfile, &load_addr, &init_addr, &play_addr,
|
||||
&subSongsMax, &subSong, &song_speed, filesize);
|
||||
sidPoke(24, 15); /* Turn on full volume */
|
||||
subSong = ci->seek_time / 1000; /* Now use the current seek time in seconds as subsong */
|
||||
subSong = param / 1000; /* Now use the current seek time in seconds as subsong */
|
||||
cpuJSR(init_addr, subSong); /* Start the song initialize */
|
||||
nSamplesToRender = 0; /* Start the rendering from scratch */
|
||||
|
||||
ci->seek_complete();
|
||||
|
||||
/* Set the elapsed time to the current subsong (in seconds) */
|
||||
ci->seek_complete();
|
||||
ci->set_elapsed(subSong*1000);
|
||||
}
|
||||
|
||||
|
@ -1306,9 +1312,5 @@ next_track:
|
|||
ci->pcmbuf_insert(samples, NULL, CHUNK_SIZE);
|
||||
}
|
||||
|
||||
request_next_track:
|
||||
if (ci->request_next_track())
|
||||
goto next_track;
|
||||
|
||||
return CODEC_OK;
|
||||
}
|
||||
|
|
|
@ -332,9 +332,20 @@ static uint8_t *read_buffer(size_t *realsize)
|
|||
return buffer;
|
||||
}
|
||||
|
||||
enum codec_status codec_main(void)
|
||||
/* this is the codec entry point */
|
||||
enum codec_status codec_main(enum codec_entry_call_reason reason)
|
||||
{
|
||||
if (reason == CODEC_LOAD) {
|
||||
/* Generic codec initialisation */
|
||||
ci->configure(DSP_SET_SAMPLE_DEPTH, PCM_OUTPUT_DEPTH-1);
|
||||
}
|
||||
|
||||
return CODEC_OK;
|
||||
}
|
||||
|
||||
/* this is called for each file to process */
|
||||
enum codec_status codec_run(void)
|
||||
{
|
||||
int status;
|
||||
uint32_t decodedsamples;
|
||||
size_t n;
|
||||
int bufcount;
|
||||
|
@ -342,20 +353,10 @@ enum codec_status codec_main(void)
|
|||
uint8_t *smafbuf;
|
||||
off_t firstblockposn; /* position of the first block in file */
|
||||
const struct pcm_codec *codec;
|
||||
intptr_t param;
|
||||
|
||||
/* Generic codec initialisation */
|
||||
ci->configure(DSP_SET_SAMPLE_DEPTH, PCM_OUTPUT_DEPTH-1);
|
||||
|
||||
next_track:
|
||||
status = CODEC_OK;
|
||||
|
||||
if (codec_init()) {
|
||||
status = CODEC_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (codec_wait_taginfo() != 0)
|
||||
goto done;
|
||||
if (codec_init())
|
||||
return CODEC_ERROR;
|
||||
|
||||
codec_set_replaygain(ci->id3);
|
||||
|
||||
|
@ -365,24 +366,22 @@ next_track:
|
|||
decodedsamples = 0;
|
||||
codec = 0;
|
||||
|
||||
ci->seek_buffer(0);
|
||||
if (!parse_header(&format, &firstblockposn))
|
||||
{
|
||||
status = CODEC_ERROR;
|
||||
goto done;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
codec = get_codec(format.formattag);
|
||||
if (codec == 0)
|
||||
{
|
||||
DEBUGF("CODEC_ERROR: unsupport audio format: 0x%x\n", (int)format.formattag);
|
||||
status = CODEC_ERROR;
|
||||
goto done;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
if (!codec->set_format(&format))
|
||||
{
|
||||
status = CODEC_ERROR;
|
||||
goto done;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
/* check chunksize */
|
||||
|
@ -392,8 +391,7 @@ next_track:
|
|||
if (format.chunksize == 0)
|
||||
{
|
||||
DEBUGF("CODEC_ERROR: chunksize is 0\n");
|
||||
status = CODEC_ERROR;
|
||||
goto done;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency);
|
||||
|
@ -404,12 +402,10 @@ next_track:
|
|||
ci->configure(DSP_SET_STEREO_MODE, STEREO_MONO);
|
||||
} else {
|
||||
DEBUGF("CODEC_ERROR: more than 2 channels unsupported\n");
|
||||
status = CODEC_ERROR;
|
||||
goto done;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
ci->seek_buffer(firstblockposn);
|
||||
ci->seek_complete();
|
||||
|
||||
/* make sure we're at the correct offset */
|
||||
if (bytesdone > (uint32_t) firstblockposn)
|
||||
|
@ -419,13 +415,13 @@ next_track:
|
|||
PCM_SEEK_POS, &read_buffer);
|
||||
|
||||
if (newpos->pos > format.numbytes)
|
||||
goto done;
|
||||
return CODEC_OK;
|
||||
|
||||
if (ci->seek_buffer(firstblockposn + newpos->pos))
|
||||
{
|
||||
bytesdone = newpos->pos;
|
||||
decodedsamples = newpos->samples;
|
||||
}
|
||||
ci->seek_complete();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -437,23 +433,32 @@ next_track:
|
|||
endofstream = 0;
|
||||
|
||||
while (!endofstream) {
|
||||
ci->yield();
|
||||
if (ci->stop_codec || ci->new_track)
|
||||
enum codec_command_action action = ci->get_command(¶m);
|
||||
|
||||
if (action == CODEC_ACTION_HALT)
|
||||
break;
|
||||
|
||||
if (ci->seek_time) {
|
||||
struct pcm_pos *newpos = codec->get_seek_pos(ci->seek_time, PCM_SEEK_TIME,
|
||||
if (action == CODEC_ACTION_SEEK_TIME) {
|
||||
struct pcm_pos *newpos = codec->get_seek_pos(param, PCM_SEEK_TIME,
|
||||
&read_buffer);
|
||||
|
||||
if (newpos->pos > format.numbytes)
|
||||
{
|
||||
ci->set_elapsed(ci->id3->length);
|
||||
ci->seek_complete();
|
||||
break;
|
||||
}
|
||||
|
||||
if (ci->seek_buffer(firstblockposn + newpos->pos))
|
||||
{
|
||||
bytesdone = newpos->pos;
|
||||
decodedsamples = newpos->samples;
|
||||
}
|
||||
|
||||
ci->set_elapsed(decodedsamples*1000LL/ci->id3->frequency);
|
||||
ci->seek_complete();
|
||||
}
|
||||
|
||||
smafbuf = (uint8_t *)ci->request_buffer(&n, format.chunksize);
|
||||
|
||||
if (n == 0)
|
||||
|
@ -464,11 +469,10 @@ next_track:
|
|||
endofstream = 1;
|
||||
}
|
||||
|
||||
status = codec->decode(smafbuf, n, samples, &bufcount);
|
||||
if (status == CODEC_ERROR)
|
||||
if (codec->decode(smafbuf, n, samples, &bufcount) == CODEC_ERROR)
|
||||
{
|
||||
DEBUGF("codec error\n");
|
||||
goto done;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
ci->pcmbuf_insert(samples, NULL, bufcount);
|
||||
|
@ -482,11 +486,5 @@ next_track:
|
|||
ci->set_elapsed(decodedsamples*1000LL/ci->id3->frequency);
|
||||
}
|
||||
|
||||
done:
|
||||
if (ci->request_next_track())
|
||||
goto next_track;
|
||||
|
||||
exit:
|
||||
return status;
|
||||
return CODEC_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -260,14 +260,6 @@ static inline void samples_release_rdbuf(void)
|
|||
static inline int32_t * samples_get_rdbuf(void)
|
||||
{
|
||||
ci->semaphore_wait(&sample_queue.emu_sem_head, TIMEOUT_BLOCK);
|
||||
|
||||
if (ci->stop_codec || ci->new_track)
|
||||
{
|
||||
/* Told to stop. Buffer must be released. */
|
||||
samples_release_rdbuf();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return sample_queue.wav_chunk[sample_queue.head & WAV_CHUNK_MASK].audio;
|
||||
}
|
||||
|
||||
|
@ -390,11 +382,10 @@ static inline void spc_emu_quit(void)
|
|||
}
|
||||
}
|
||||
|
||||
static inline bool spc_play_get_samples(int32_t **samples)
|
||||
static inline int32_t * spc_play_get_samples(void)
|
||||
{
|
||||
/* obtain filled samples buffer */
|
||||
*samples = samples_get_rdbuf();
|
||||
return *samples != NULL;
|
||||
return samples_get_rdbuf();
|
||||
}
|
||||
|
||||
static inline void spc_play_send_samples(int32_t *samples)
|
||||
|
@ -433,15 +424,14 @@ static inline void spc_play_send_samples(int32_t *samples)
|
|||
#define spc_emu_quit()
|
||||
#define samples_release_rdbuf()
|
||||
|
||||
static inline bool spc_play_get_samples(int32_t **samples)
|
||||
static inline int32_t * spc_play_get_samples(void)
|
||||
{
|
||||
ENTER_TIMER(render);
|
||||
/* fill samples buffer */
|
||||
if ( SPC_play(&spc_emu,WAV_CHUNK_SIZE*2,wav_chunk) )
|
||||
assert( false );
|
||||
EXIT_TIMER(render);
|
||||
*samples = wav_chunk;
|
||||
return true;
|
||||
return wav_chunk;
|
||||
}
|
||||
#endif /* SPC_DUAL_CORE */
|
||||
|
||||
|
@ -454,6 +444,7 @@ static int play_track( void )
|
|||
unsigned long fadeendsample = (ID666.length+ID666.fade)*(long long) SAMPLE_RATE/1000;
|
||||
int fadedec = 0;
|
||||
int fadevol = 0x7fffffffl;
|
||||
intptr_t param;
|
||||
|
||||
if (fadeendsample>fadestartsample)
|
||||
fadedec=0x7fffffffl/(fadeendsample-fadestartsample)+1;
|
||||
|
@ -462,25 +453,26 @@ static int play_track( void )
|
|||
|
||||
while ( 1 )
|
||||
{
|
||||
ci->yield();
|
||||
if (ci->stop_codec || ci->new_track) {
|
||||
break;
|
||||
}
|
||||
enum codec_command_action action = ci->get_command(¶m);
|
||||
|
||||
if (ci->seek_time) {
|
||||
if (action == CODEC_ACTION_HALT)
|
||||
break;
|
||||
|
||||
if (action == CODEC_ACTION_SEEK_TIME) {
|
||||
int curtime = sampleswritten*1000LL/SAMPLE_RATE;
|
||||
DEBUGF("seek to %ld\ncurrently at %d\n",ci->seek_time,curtime);
|
||||
if (ci->seek_time < curtime) {
|
||||
DEBUGF("seek to %ld\ncurrently at %d\n", (long)param, curtime);
|
||||
if (param < curtime) {
|
||||
DEBUGF("seek backwards = reset\n");
|
||||
ci->set_elapsed(0);
|
||||
ci->seek_complete();
|
||||
return 1;
|
||||
}
|
||||
|
||||
ci->set_elapsed(curtime);
|
||||
ci->seek_complete();
|
||||
}
|
||||
|
||||
int32_t *samples;
|
||||
if (!spc_play_get_samples(&samples))
|
||||
break;
|
||||
int32_t *samples = spc_play_get_samples();
|
||||
|
||||
sampleswritten += WAV_CHUNK_SIZE;
|
||||
|
||||
|
@ -532,28 +524,30 @@ static int play_track( void )
|
|||
}
|
||||
|
||||
/* this is the codec entry point */
|
||||
enum codec_status codec_main(void)
|
||||
enum codec_status codec_main(enum codec_entry_call_reason reason)
|
||||
{
|
||||
enum codec_status stat = CODEC_ERROR;
|
||||
|
||||
if (reason == CODEC_LOAD) {
|
||||
if (!spc_emu_start())
|
||||
goto codec_quit;
|
||||
|
||||
do
|
||||
{
|
||||
DEBUGF("SPC: next_track\n");
|
||||
if (codec_init()) {
|
||||
goto codec_quit;
|
||||
}
|
||||
DEBUGF("SPC: after init\n");
|
||||
return CODEC_ERROR;
|
||||
|
||||
ci->configure(DSP_SET_SAMPLE_DEPTH, 24);
|
||||
ci->configure(DSP_SET_FREQUENCY, SAMPLE_RATE);
|
||||
ci->configure(DSP_SET_STEREO_MODE, STEREO_NONINTERLEAVED);
|
||||
}
|
||||
else if (reason == CODEC_UNLOAD) {
|
||||
spc_emu_quit();
|
||||
}
|
||||
|
||||
/* wait for track info to load */
|
||||
if (codec_wait_taginfo() != 0)
|
||||
continue;
|
||||
return CODEC_OK;
|
||||
}
|
||||
|
||||
/* this is called for each file to process */
|
||||
enum codec_status codec_run(void)
|
||||
{
|
||||
DEBUGF("SPC: next_track\n");
|
||||
if (codec_init())
|
||||
return CODEC_ERROR;
|
||||
DEBUGF("SPC: after init\n");
|
||||
|
||||
codec_set_replaygain(ci->id3);
|
||||
|
||||
|
@ -562,16 +556,15 @@ enum codec_status codec_main(void)
|
|||
ci->seek_buffer(0);
|
||||
size_t buffersize;
|
||||
uint8_t* buffer = ci->request_buffer(&buffersize, ci->filesize);
|
||||
if (!buffer) {
|
||||
goto codec_quit;
|
||||
}
|
||||
if (!buffer)
|
||||
return CODEC_ERROR;
|
||||
|
||||
DEBUGF("SPC: read size = 0x%lx\n",(unsigned long)buffersize);
|
||||
do
|
||||
{
|
||||
if (load_spc_buffer(buffer, buffersize)) {
|
||||
DEBUGF("SPC load failure\n");
|
||||
goto codec_quit;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
LoadID666(buffer+0x2e);
|
||||
|
@ -586,13 +579,6 @@ enum codec_status codec_main(void)
|
|||
while ( play_track() );
|
||||
|
||||
print_timers(ci->id3->path);
|
||||
}
|
||||
while ( ci->request_next_track() );
|
||||
|
||||
stat = CODEC_OK;
|
||||
|
||||
codec_quit:
|
||||
spc_emu_quit();
|
||||
|
||||
return stat;
|
||||
return CODEC_OK;
|
||||
}
|
||||
|
|
|
@ -367,11 +367,12 @@ static void *process_header(spx_ogg_packet *op,
|
|||
return st;
|
||||
}
|
||||
|
||||
/* this is the codec entry point */
|
||||
enum codec_status codec_main(void)
|
||||
/* this is called for each file to process */
|
||||
enum codec_status codec_run(void)
|
||||
{
|
||||
int error = CODEC_ERROR;
|
||||
|
||||
SpeexBits bits;
|
||||
int error;
|
||||
int eof = 0;
|
||||
spx_ogg_sync_state oy;
|
||||
spx_ogg_page og;
|
||||
|
@ -383,7 +384,7 @@ enum codec_status codec_main(void)
|
|||
int eos = 0;
|
||||
SpeexStereoState *stereo;
|
||||
int channels = -1;
|
||||
int rate = 0, samplerate = 0;
|
||||
int samplerate = 0;
|
||||
int extra_headers = 0;
|
||||
int stream_init = 0;
|
||||
int page_nb_packets, frame_size, packet_count = 0;
|
||||
|
@ -392,26 +393,22 @@ enum codec_status codec_main(void)
|
|||
unsigned long strtoffset = 0;
|
||||
void *st = NULL;
|
||||
int j = 0;
|
||||
intptr_t param;
|
||||
|
||||
memset(&bits, 0, sizeof(bits));
|
||||
memset(&oy, 0, sizeof(oy));
|
||||
|
||||
/* Ogg handling still uses mallocs, so reset the malloc buffer per track */
|
||||
next_track:
|
||||
error = CODEC_OK;
|
||||
|
||||
if (codec_init()) {
|
||||
error = CODEC_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
ci->seek_buffer(0);
|
||||
|
||||
stereo = speex_stereo_state_init();
|
||||
spx_ogg_sync_init(&oy);
|
||||
spx_ogg_alloc_buffer(&oy,2*CHUNKSIZE);
|
||||
|
||||
if (codec_wait_taginfo() != 0)
|
||||
goto done;
|
||||
|
||||
strtoffset = ci->id3->offset;
|
||||
|
||||
samplerate = ci->id3->frequency;
|
||||
|
@ -419,30 +416,32 @@ next_track:
|
|||
|
||||
eof = 0;
|
||||
while (!eof) {
|
||||
ci->yield();
|
||||
if (ci->stop_codec || ci->new_track)
|
||||
enum codec_command_action action = ci->get_command(¶m);
|
||||
|
||||
if (action == CODEC_ACTION_HALT)
|
||||
break;
|
||||
|
||||
/*seek (seeks to the page before the position) */
|
||||
if (ci->seek_time) {
|
||||
if (action == CODEC_ACTION_SEEK_TIME) {
|
||||
if(samplerate!=0&&packet_count>1){
|
||||
LOGF("Speex seek page:%lld,%lld,%ld,%lld,%d\n",
|
||||
((spx_int64_t)ci->seek_time/1000) *
|
||||
((spx_int64_t)param/1000) *
|
||||
(spx_int64_t)samplerate,
|
||||
page_granule, ci->seek_time,
|
||||
page_granule, param,
|
||||
(page_granule/samplerate)*1000, samplerate);
|
||||
|
||||
speex_seek_page_granule(((spx_int64_t)ci->seek_time/1000) *
|
||||
speex_seek_page_granule(((spx_int64_t)param/1000) *
|
||||
(spx_int64_t)samplerate,
|
||||
page_granule, &oy, headerssize);
|
||||
ci->seek_complete();
|
||||
}
|
||||
|
||||
ci->set_elapsed(param);
|
||||
ci->seek_complete();
|
||||
}
|
||||
|
||||
next_page:
|
||||
/*Get the ogg buffer for writing*/
|
||||
if(get_more_data(&oy)<1){/*read error*/
|
||||
error=CODEC_ERROR;
|
||||
goto done;
|
||||
}
|
||||
|
||||
|
@ -477,8 +476,7 @@ next_page:
|
|||
nframes=1;
|
||||
|
||||
if (!st){
|
||||
error=CODEC_ERROR;
|
||||
goto exit;
|
||||
goto done;
|
||||
}
|
||||
|
||||
ci->configure(DSP_SET_FREQUENCY, ci->id3->frequency);
|
||||
|
@ -557,31 +555,18 @@ next_page:
|
|||
}
|
||||
|
||||
done:
|
||||
if (ci->request_next_track()) {
|
||||
|
||||
/* Clean things up for the next track */
|
||||
speex_bits_destroy(&bits);
|
||||
|
||||
if (st)
|
||||
if (st)
|
||||
speex_decoder_destroy(st);
|
||||
|
||||
if (stream_init == 1)
|
||||
spx_ogg_stream_reset(&os);
|
||||
|
||||
spx_ogg_sync_reset(&oy);
|
||||
|
||||
cur_granule = stream_init = rate = samplerate = headerssize
|
||||
= packet_count = eos = 0;
|
||||
|
||||
goto next_track;
|
||||
}
|
||||
|
||||
exit:
|
||||
speex_bits_destroy(&bits);
|
||||
|
||||
if (stream_init)
|
||||
spx_ogg_stream_destroy(&os);
|
||||
|
||||
spx_ogg_sync_destroy(&oy);
|
||||
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
|
|
|
@ -34,36 +34,36 @@ CODEC_HEADER
|
|||
static int32_t samples[PCM_BUFFER_LENGTH * 2] IBSS_ATTR;
|
||||
|
||||
/* this is the codec entry point */
|
||||
enum codec_status codec_main(void)
|
||||
enum codec_status codec_main(enum codec_entry_call_reason reason)
|
||||
{
|
||||
if (reason == CODEC_LOAD) {
|
||||
/* Generic codec initialisation */
|
||||
ci->configure(DSP_SET_SAMPLE_DEPTH, TTA_OUTPUT_DEPTH - 1);
|
||||
}
|
||||
|
||||
return CODEC_OK;
|
||||
}
|
||||
|
||||
/* this is called for each file to process */
|
||||
enum codec_status codec_run(void)
|
||||
{
|
||||
tta_info info;
|
||||
int status;
|
||||
unsigned int decodedsamples;
|
||||
int endofstream;
|
||||
int new_pos = 0;
|
||||
int sample_count;
|
||||
|
||||
/* Generic codec initialisation */
|
||||
ci->configure(DSP_SET_SAMPLE_DEPTH, TTA_OUTPUT_DEPTH - 1);
|
||||
|
||||
next_track:
|
||||
status = CODEC_OK;
|
||||
intptr_t param;
|
||||
|
||||
if (codec_init())
|
||||
{
|
||||
DEBUGF("codec_init() error\n");
|
||||
status = CODEC_ERROR;
|
||||
goto exit;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
if (codec_wait_taginfo() != 0)
|
||||
goto done;
|
||||
ci->seek_buffer(0);
|
||||
|
||||
if (set_tta_info(&info) < 0 || player_init(&info) < 0)
|
||||
{
|
||||
status = CODEC_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
return CODEC_ERROR;
|
||||
|
||||
codec_set_replaygain(ci->id3);
|
||||
|
||||
|
@ -74,8 +74,8 @@ next_track:
|
|||
ci->configure(DSP_SET_STEREO_MODE, STEREO_MONO);
|
||||
} else {
|
||||
DEBUGF("CODEC_ERROR: more than 2 channels\n");
|
||||
status = CODEC_ERROR;
|
||||
goto done;
|
||||
player_stop();
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
/* The main decoder loop */
|
||||
|
@ -88,31 +88,31 @@ next_track:
|
|||
new_pos = set_position(ci->id3->offset, TTA_SEEK_POS);
|
||||
if (new_pos >= 0)
|
||||
decodedsamples = new_pos;
|
||||
ci->seek_complete();
|
||||
}
|
||||
|
||||
while (!endofstream)
|
||||
{
|
||||
ci->yield();
|
||||
if (ci->stop_codec || ci->new_track)
|
||||
enum codec_command_action action = ci->get_command(¶m);
|
||||
|
||||
if (action == CODEC_ACTION_HALT)
|
||||
break;
|
||||
|
||||
if (ci->seek_time)
|
||||
if (action == CODEC_ACTION_SEEK_TIME)
|
||||
{
|
||||
new_pos = set_position(ci->seek_time / SEEK_STEP, TTA_SEEK_TIME);
|
||||
new_pos = set_position(param / SEEK_STEP, TTA_SEEK_TIME);
|
||||
if (new_pos >= 0)
|
||||
{
|
||||
decodedsamples = new_pos;
|
||||
ci->seek_complete();
|
||||
}
|
||||
|
||||
ci->set_elapsed((uint64_t)info.LENGTH * 1000 * decodedsamples / info.DATALENGTH);
|
||||
ci->seek_complete();
|
||||
}
|
||||
|
||||
sample_count = get_samples(samples);
|
||||
if (sample_count < 0)
|
||||
{
|
||||
status = CODEC_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
ci->pcmbuf_insert(samples, NULL, sample_count);
|
||||
decodedsamples += sample_count;
|
||||
if (decodedsamples >= info.DATALENGTH)
|
||||
|
@ -120,11 +120,6 @@ next_track:
|
|||
ci->set_elapsed((uint64_t)info.LENGTH * 1000 * decodedsamples / info.DATALENGTH);
|
||||
}
|
||||
|
||||
done:
|
||||
player_stop();
|
||||
if (ci->request_next_track())
|
||||
goto next_track;
|
||||
|
||||
exit:
|
||||
return status;
|
||||
return CODEC_OK;
|
||||
}
|
||||
|
|
|
@ -104,14 +104,25 @@ static bool vorbis_set_codec_parameters(OggVorbis_File *vf)
|
|||
}
|
||||
|
||||
/* this is the codec entry point */
|
||||
enum codec_status codec_main(void)
|
||||
enum codec_status codec_main(enum codec_entry_call_reason reason)
|
||||
{
|
||||
if (reason == CODEC_LOAD) {
|
||||
if (codec_init())
|
||||
return CODEC_ERROR;
|
||||
ci->configure(DSP_SET_SAMPLE_DEPTH, 24);
|
||||
}
|
||||
|
||||
return CODEC_OK;
|
||||
}
|
||||
|
||||
/* this is called for each file to process */
|
||||
enum codec_status codec_run(void)
|
||||
{
|
||||
ov_callbacks callbacks;
|
||||
OggVorbis_File vf;
|
||||
ogg_int32_t **pcm;
|
||||
|
||||
bool initialized = false; /* First init done? */
|
||||
int error;
|
||||
int error = CODEC_ERROR;
|
||||
long n;
|
||||
int current_section;
|
||||
int previous_section;
|
||||
|
@ -120,36 +131,24 @@ enum codec_status codec_main(void)
|
|||
ogg_int64_t vf_dataoffsets;
|
||||
ogg_uint32_t vf_serialnos;
|
||||
ogg_int64_t vf_pcmlengths[2];
|
||||
|
||||
ci->configure(DSP_SET_SAMPLE_DEPTH, 24);
|
||||
|
||||
if (codec_init()) {
|
||||
error = CODEC_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
intptr_t param;
|
||||
|
||||
#if defined(CPU_ARM) || defined(CPU_COLDFIRE) || defined(CPU_MIPS)
|
||||
if (setjmp(rb_jump_buf) != 0) {
|
||||
/* malloc failed; skip to next track */
|
||||
error = CODEC_ERROR;
|
||||
/* malloc failed; finish with this track */
|
||||
goto done;
|
||||
}
|
||||
#endif
|
||||
|
||||
next_track:
|
||||
error = CODEC_OK;
|
||||
|
||||
ogg_malloc_init();
|
||||
|
||||
if (codec_wait_taginfo() != 0)
|
||||
goto done;
|
||||
|
||||
/* Create a decoder instance */
|
||||
callbacks.read_func = read_handler;
|
||||
callbacks.seek_func = initial_seek_handler;
|
||||
callbacks.tell_func = tell_handler;
|
||||
callbacks.close_func = close_handler;
|
||||
|
||||
ci->seek_buffer(0);
|
||||
|
||||
/* Open a non-seekable stream */
|
||||
error = ov_open_callbacks(ci, &vf, NULL, 0, callbacks);
|
||||
|
||||
|
@ -186,15 +185,13 @@ next_track:
|
|||
vf.end = ci->id3->filesize;
|
||||
vf.ready_state = OPENED;
|
||||
vf.links = 1;
|
||||
initialized = true;
|
||||
} else {
|
||||
DEBUGF("Vorbis: ov_open failed: %d\n", error);
|
||||
error = CODEC_ERROR;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (ci->id3->offset) {
|
||||
ci->advance_buffer(ci->id3->offset);
|
||||
ci->seek_buffer(ci->id3->offset);
|
||||
ov_raw_seek(&vf, ci->id3->offset);
|
||||
ci->set_elapsed(ov_time_tell(&vf));
|
||||
ci->set_offset(ov_raw_tell(&vf));
|
||||
|
@ -203,14 +200,17 @@ next_track:
|
|||
previous_section = -1;
|
||||
eof = 0;
|
||||
while (!eof) {
|
||||
ci->yield();
|
||||
if (ci->stop_codec || ci->new_track)
|
||||
enum codec_command_action action = ci->get_command(¶m);
|
||||
|
||||
if (action == CODEC_ACTION_HALT)
|
||||
break;
|
||||
|
||||
if (ci->seek_time) {
|
||||
if (ov_time_seek(&vf, ci->seek_time - 1)) {
|
||||
if (action == CODEC_ACTION_SEEK_TIME) {
|
||||
if (ov_time_seek(&vf, param)) {
|
||||
//ci->logf("ov_time_seek failed");
|
||||
}
|
||||
|
||||
ci->set_elapsed(ov_time_tell(&vf));
|
||||
ci->seek_complete();
|
||||
}
|
||||
|
||||
|
@ -220,7 +220,6 @@ next_track:
|
|||
/* Change DSP and buffer settings for this bitstream */
|
||||
if (current_section != previous_section) {
|
||||
if (!vorbis_set_codec_parameters(&vf)) {
|
||||
error = CODEC_ERROR;
|
||||
goto done;
|
||||
} else {
|
||||
previous_section = current_section;
|
||||
|
@ -238,6 +237,7 @@ next_track:
|
|||
}
|
||||
}
|
||||
|
||||
error = CODEC_OK;
|
||||
done:
|
||||
#if 0 /* defined(SIMULATOR) */
|
||||
{
|
||||
|
@ -249,18 +249,12 @@ done:
|
|||
#endif
|
||||
ogg_malloc_destroy();
|
||||
|
||||
if (ci->request_next_track()) {
|
||||
if (!initialized)
|
||||
goto next_track;
|
||||
/* Clean things up for the next track */
|
||||
vf.dataoffsets = NULL;
|
||||
vf.offsets = NULL;
|
||||
vf.serialnos = NULL;
|
||||
vf.pcmlengths = NULL;
|
||||
ov_clear(&vf);
|
||||
goto next_track;
|
||||
}
|
||||
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
|
|
|
@ -44,9 +44,19 @@ static uint8_t *read_buffer(size_t *realsize)
|
|||
}
|
||||
|
||||
/* this is the codec entry point */
|
||||
enum codec_status codec_main(void)
|
||||
enum codec_status codec_main(enum codec_entry_call_reason reason)
|
||||
{
|
||||
if (reason == CODEC_LOAD) {
|
||||
/* Generic codec initialisation */
|
||||
ci->configure(DSP_SET_SAMPLE_DEPTH, PCM_OUTPUT_DEPTH-1);
|
||||
}
|
||||
|
||||
return CODEC_OK;
|
||||
}
|
||||
|
||||
/* this is called for each file to process */
|
||||
enum codec_status codec_run(void)
|
||||
{
|
||||
int status;
|
||||
uint32_t decodedsamples;
|
||||
size_t n;
|
||||
int bufcount;
|
||||
|
@ -54,26 +64,18 @@ enum codec_status codec_main(void)
|
|||
uint8_t *voxbuf;
|
||||
off_t firstblockposn = 0; /* position of the first block in file */
|
||||
const struct pcm_codec *codec;
|
||||
|
||||
/* Generic codec initialisation */
|
||||
ci->configure(DSP_SET_SAMPLE_DEPTH, PCM_OUTPUT_DEPTH-1);
|
||||
|
||||
next_track:
|
||||
status = CODEC_OK;
|
||||
intptr_t param;
|
||||
|
||||
if (codec_init()) {
|
||||
DEBUGF("codec_init() error\n");
|
||||
status = CODEC_ERROR;
|
||||
goto exit;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
if (codec_wait_taginfo() != 0)
|
||||
goto done;
|
||||
|
||||
codec_set_replaygain(ci->id3);
|
||||
|
||||
/* Need to save offset for later use (cleared indirectly by advance_buffer) */
|
||||
bytesdone = ci->id3->offset;
|
||||
ci->seek_buffer(0);
|
||||
|
||||
ci->memset(&format, 0, sizeof(struct pcm_format));
|
||||
|
||||
|
@ -96,20 +98,16 @@ next_track:
|
|||
if (!codec)
|
||||
{
|
||||
DEBUGF("CODEC_ERROR: dialogic oki adpcm codec does not load.\n");
|
||||
status = CODEC_ERROR;
|
||||
goto done;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
if (!codec->set_format(&format))
|
||||
{
|
||||
status = CODEC_ERROR;
|
||||
goto done;
|
||||
if (!codec->set_format(&format)) {
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
if (format.numbytes == 0) {
|
||||
DEBUGF("CODEC_ERROR: data size is 0\n");
|
||||
status = CODEC_ERROR;
|
||||
goto done;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
/* check chunksize */
|
||||
|
@ -118,8 +116,7 @@ next_track:
|
|||
if (format.chunksize == 0)
|
||||
{
|
||||
DEBUGF("CODEC_ERROR: chunksize is 0\n");
|
||||
status = CODEC_ERROR;
|
||||
goto done;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency);
|
||||
|
@ -131,14 +128,14 @@ next_track:
|
|||
struct pcm_pos *newpos = codec->get_seek_pos(bytesdone - firstblockposn,
|
||||
PCM_SEEK_POS, &read_buffer);
|
||||
|
||||
if (newpos->pos > format.numbytes)
|
||||
goto done;
|
||||
if (newpos->pos > format.numbytes) {
|
||||
return CODEC_OK;
|
||||
}
|
||||
if (ci->seek_buffer(firstblockposn + newpos->pos))
|
||||
{
|
||||
bytesdone = newpos->pos;
|
||||
decodedsamples = newpos->samples;
|
||||
}
|
||||
ci->seek_complete();
|
||||
} else {
|
||||
/* already where we need to be */
|
||||
bytesdone = 0;
|
||||
|
@ -148,22 +145,29 @@ next_track:
|
|||
endofstream = 0;
|
||||
|
||||
while (!endofstream) {
|
||||
ci->yield();
|
||||
if (ci->stop_codec || ci->new_track) {
|
||||
break;
|
||||
}
|
||||
enum codec_command_action action = ci->get_command(¶m);
|
||||
|
||||
if (ci->seek_time) {
|
||||
struct pcm_pos *newpos = codec->get_seek_pos(ci->seek_time, PCM_SEEK_TIME,
|
||||
if (action == CODEC_ACTION_HALT)
|
||||
break;
|
||||
|
||||
if (action == CODEC_ACTION_SEEK_TIME) {
|
||||
struct pcm_pos *newpos = codec->get_seek_pos(param, PCM_SEEK_TIME,
|
||||
&read_buffer);
|
||||
|
||||
if (newpos->pos > format.numbytes)
|
||||
{
|
||||
ci->set_elapsed(ci->id3->length);
|
||||
ci->seek_complete();
|
||||
break;
|
||||
}
|
||||
|
||||
if (ci->seek_buffer(firstblockposn + newpos->pos))
|
||||
{
|
||||
bytesdone = newpos->pos;
|
||||
decodedsamples = newpos->samples;
|
||||
}
|
||||
|
||||
ci->set_elapsed(decodedsamples*1000LL/ci->id3->frequency);
|
||||
ci->seek_complete();
|
||||
}
|
||||
|
||||
|
@ -175,11 +179,10 @@ next_track:
|
|||
endofstream = 1;
|
||||
}
|
||||
|
||||
status = codec->decode(voxbuf, n, samples, &bufcount);
|
||||
if (status == CODEC_ERROR)
|
||||
if (codec->decode(voxbuf, n, samples, &bufcount) == CODEC_ERROR)
|
||||
{
|
||||
DEBUGF("codec error\n");
|
||||
goto done;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
ci->pcmbuf_insert(samples, NULL, bufcount);
|
||||
|
@ -192,10 +195,5 @@ next_track:
|
|||
ci->set_elapsed(decodedsamples*1000LL/ci->id3->frequency);
|
||||
}
|
||||
|
||||
done:
|
||||
if (ci->request_next_track())
|
||||
goto next_track;
|
||||
|
||||
exit:
|
||||
return status;
|
||||
return CODEC_OK;
|
||||
}
|
||||
|
|
|
@ -151,9 +151,19 @@ static uint8_t *read_buffer(size_t *realsize)
|
|||
}
|
||||
|
||||
/* this is the codec entry point */
|
||||
enum codec_status codec_main(void)
|
||||
enum codec_status codec_main(enum codec_entry_call_reason reason)
|
||||
{
|
||||
if (reason == CODEC_LOAD) {
|
||||
/* Generic codec initialisation */
|
||||
ci->configure(DSP_SET_SAMPLE_DEPTH, PCM_OUTPUT_DEPTH-1);
|
||||
}
|
||||
|
||||
return CODEC_OK;
|
||||
}
|
||||
|
||||
/* this is called for each file to process */
|
||||
enum codec_status codec_run(void)
|
||||
{
|
||||
int status;
|
||||
uint32_t decodedsamples;
|
||||
size_t n;
|
||||
int bufcount;
|
||||
|
@ -163,38 +173,28 @@ enum codec_status codec_main(void)
|
|||
off_t firstblockposn; /* position of the first block in file */
|
||||
const struct pcm_codec *codec;
|
||||
uint32_t size;
|
||||
|
||||
/* Generic codec initialisation */
|
||||
ci->configure(DSP_SET_SAMPLE_DEPTH, PCM_OUTPUT_DEPTH-1);
|
||||
|
||||
next_track:
|
||||
status = CODEC_OK;
|
||||
intptr_t param;
|
||||
|
||||
if (codec_init()) {
|
||||
DEBUGF("codec_init() error\n");
|
||||
status = CODEC_ERROR;
|
||||
goto exit;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
if (codec_wait_taginfo() != 0)
|
||||
goto done;
|
||||
|
||||
codec_set_replaygain(ci->id3);
|
||||
|
||||
/* Need to save offset for later use (cleared indirectly by advance_buffer) */
|
||||
bytesdone = ci->id3->offset;
|
||||
|
||||
/* get RIFF chunk header */
|
||||
ci->seek_buffer(0);
|
||||
buf = ci->request_buffer(&n, 12);
|
||||
if (n < 12) {
|
||||
DEBUGF("request_buffer error\n");
|
||||
status = CODEC_ERROR;
|
||||
goto done;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
if ((memcmp(buf, "RIFF", 4) != 0) || (memcmp(&buf[8], "WAVE", 4) != 0)) {
|
||||
DEBUGF("CODEC_ERROR: missing riff header\n");
|
||||
status = CODEC_ERROR;
|
||||
goto done;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
/* advance to first WAVE chunk */
|
||||
|
@ -215,8 +215,7 @@ next_track:
|
|||
if (n < 8) {
|
||||
DEBUGF("data chunk request_buffer error\n");
|
||||
/* no more chunks, 'data' chunk must not have been found */
|
||||
status = CODEC_ERROR;
|
||||
goto done;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
/* chunkSize */
|
||||
|
@ -225,8 +224,7 @@ next_track:
|
|||
if (size < 16) {
|
||||
DEBUGF("CODEC_ERROR: 'fmt ' chunk size=%lu < 16\n",
|
||||
(unsigned long)size);
|
||||
status = CODEC_ERROR;
|
||||
goto done;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
/* wFormatTag */
|
||||
format.formattag=buf[8]|(buf[9]<<8);
|
||||
|
@ -256,8 +254,7 @@ next_track:
|
|||
if (format.size < 22) {
|
||||
DEBUGF("CODEC_ERROR: WAVE_FORMAT_EXTENSIBLE is "
|
||||
"missing extension\n");
|
||||
status = CODEC_ERROR;
|
||||
goto done;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
/* wValidBitsPerSample */
|
||||
format.bitspersample = buf[26]|(buf[27]<<8);
|
||||
|
@ -273,8 +270,7 @@ next_track:
|
|||
{
|
||||
if (!set_msadpcm_coeffs(buf))
|
||||
{
|
||||
status = CODEC_ERROR;
|
||||
goto done;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -284,8 +280,7 @@ next_track:
|
|||
{
|
||||
DEBUGF("CODEC_ERROR: unsupported wave format 0x%x\n",
|
||||
(unsigned int) format.formattag);
|
||||
status = CODEC_ERROR;
|
||||
goto done;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
/* riff 8bit linear pcm is unsigned */
|
||||
|
@ -295,8 +290,7 @@ next_track:
|
|||
/* set format, parse codec specific tag, check format, and calculate chunk size */
|
||||
if (!codec->set_format(&format))
|
||||
{
|
||||
status = CODEC_ERROR;
|
||||
goto done;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
} else if (memcmp(buf, "data", 4) == 0) {
|
||||
format.numbytes = size;
|
||||
|
@ -324,31 +318,26 @@ next_track:
|
|||
if (!codec)
|
||||
{
|
||||
DEBUGF("CODEC_ERROR: 'fmt ' chunk not found\n");
|
||||
status = CODEC_ERROR;
|
||||
goto done;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
/* common format check */
|
||||
if (format.channels == 0) {
|
||||
DEBUGF("CODEC_ERROR: 'fmt ' chunk not found or 0-channels file\n");
|
||||
status = CODEC_ERROR;
|
||||
goto done;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
if (format.samplesperblock == 0) {
|
||||
DEBUGF("CODEC_ERROR: 'fmt ' chunk not found or 0-wSamplesPerBlock file\n");
|
||||
status = CODEC_ERROR;
|
||||
goto done;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
if (format.blockalign == 0)
|
||||
{
|
||||
DEBUGF("CODEC_ERROR: 'fmt ' chunk not found or 0-blockalign file\n");
|
||||
status = CODEC_ERROR;
|
||||
goto done;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
if (format.numbytes == 0) {
|
||||
DEBUGF("CODEC_ERROR: 'data' chunk not found or has zero-length\n");
|
||||
status = CODEC_ERROR;
|
||||
goto done;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
/* check chunksize */
|
||||
|
@ -358,8 +347,7 @@ next_track:
|
|||
if (format.chunksize == 0)
|
||||
{
|
||||
DEBUGF("CODEC_ERROR: chunksize is 0\n");
|
||||
status = CODEC_ERROR;
|
||||
goto done;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency);
|
||||
|
@ -369,8 +357,7 @@ next_track:
|
|||
ci->configure(DSP_SET_STEREO_MODE, STEREO_MONO);
|
||||
} else {
|
||||
DEBUGF("CODEC_ERROR: more than 2 channels\n");
|
||||
status = CODEC_ERROR;
|
||||
goto done;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
/* make sure we're at the correct offset */
|
||||
|
@ -380,13 +367,12 @@ next_track:
|
|||
PCM_SEEK_POS, &read_buffer);
|
||||
|
||||
if (newpos->pos > format.numbytes)
|
||||
goto done;
|
||||
return CODEC_OK;
|
||||
if (ci->seek_buffer(firstblockposn + newpos->pos))
|
||||
{
|
||||
bytesdone = newpos->pos;
|
||||
decodedsamples = newpos->samples;
|
||||
}
|
||||
ci->seek_complete();
|
||||
} else {
|
||||
/* already where we need to be */
|
||||
bytesdone = 0;
|
||||
|
@ -396,22 +382,28 @@ next_track:
|
|||
endofstream = 0;
|
||||
|
||||
while (!endofstream) {
|
||||
ci->yield();
|
||||
if (ci->stop_codec || ci->new_track) {
|
||||
enum codec_command_action action = ci->get_command(¶m);
|
||||
|
||||
if (action == CODEC_ACTION_HALT)
|
||||
break;
|
||||
|
||||
if (action == CODEC_ACTION_SEEK_TIME) {
|
||||
struct pcm_pos *newpos = codec->get_seek_pos(param, PCM_SEEK_TIME,
|
||||
&read_buffer);
|
||||
if (newpos->pos > format.numbytes)
|
||||
{
|
||||
ci->set_elapsed(ci->id3->length);
|
||||
ci->seek_complete();
|
||||
break;
|
||||
}
|
||||
|
||||
if (ci->seek_time) {
|
||||
struct pcm_pos *newpos = codec->get_seek_pos(ci->seek_time, PCM_SEEK_TIME,
|
||||
&read_buffer);
|
||||
|
||||
if (newpos->pos > format.numbytes)
|
||||
break;
|
||||
if (ci->seek_buffer(firstblockposn + newpos->pos))
|
||||
{
|
||||
bytesdone = newpos->pos;
|
||||
decodedsamples = newpos->samples;
|
||||
}
|
||||
|
||||
ci->set_elapsed(decodedsamples*1000LL/ci->id3->frequency);
|
||||
ci->seek_complete();
|
||||
}
|
||||
|
||||
|
@ -423,11 +415,10 @@ next_track:
|
|||
endofstream = 1;
|
||||
}
|
||||
|
||||
status = codec->decode(wavbuf, n, samples, &bufcount);
|
||||
if (status == CODEC_ERROR)
|
||||
if (codec->decode(wavbuf, n, samples, &bufcount) == CODEC_ERROR)
|
||||
{
|
||||
DEBUGF("codec error\n");
|
||||
goto done;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
ci->pcmbuf_insert(samples, NULL, bufcount);
|
||||
|
@ -440,10 +431,5 @@ next_track:
|
|||
ci->set_elapsed(decodedsamples*1000LL/ci->id3->frequency);
|
||||
}
|
||||
|
||||
done:
|
||||
if (ci->request_next_track())
|
||||
goto next_track;
|
||||
|
||||
exit:
|
||||
return status;
|
||||
return CODEC_OK;
|
||||
}
|
||||
|
|
|
@ -159,9 +159,19 @@ static uint8_t *read_buffer(size_t *realsize)
|
|||
}
|
||||
|
||||
/* this is the codec entry point */
|
||||
enum codec_status codec_main(void)
|
||||
enum codec_status codec_main(enum codec_entry_call_reason reason)
|
||||
{
|
||||
if (reason == CODEC_LOAD) {
|
||||
/* Generic codec initialisation */
|
||||
ci->configure(DSP_SET_SAMPLE_DEPTH, PCM_OUTPUT_DEPTH-1);
|
||||
}
|
||||
|
||||
return CODEC_OK;
|
||||
}
|
||||
|
||||
/* this is called for each file to process */
|
||||
enum codec_status codec_run(void)
|
||||
{
|
||||
int status;
|
||||
uint32_t decodedsamples;
|
||||
size_t n;
|
||||
int bufcount;
|
||||
|
@ -171,39 +181,29 @@ enum codec_status codec_main(void)
|
|||
off_t firstblockposn; /* position of the first block in file */
|
||||
const struct pcm_codec *codec;
|
||||
uint64_t size;
|
||||
|
||||
/* Generic codec initialisation */
|
||||
ci->configure(DSP_SET_SAMPLE_DEPTH, PCM_OUTPUT_DEPTH-1);
|
||||
|
||||
next_track:
|
||||
status = CODEC_OK;
|
||||
intptr_t param;
|
||||
|
||||
if (codec_init()) {
|
||||
DEBUGF("codec_init() error\n");
|
||||
status = CODEC_ERROR;
|
||||
goto exit;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
if (codec_wait_taginfo() != 0)
|
||||
goto done;
|
||||
|
||||
codec_set_replaygain(ci->id3);
|
||||
|
||||
/* Need to save offset for later use (cleared indirectly by advance_buffer) */
|
||||
bytesdone = ci->id3->offset;
|
||||
|
||||
/* get RIFF chunk header */
|
||||
ci->seek_buffer(0);
|
||||
buf = ci->request_buffer(&n, 40);
|
||||
if (n < 40) {
|
||||
DEBUGF("request_buffer error\n");
|
||||
status = CODEC_ERROR;
|
||||
goto done;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
if ((memcmp(buf , WAVE64_GUID_RIFF, 16) != 0) ||
|
||||
(memcmp(buf+24, WAVE64_GUID_WAVE, 16) != 0))
|
||||
{
|
||||
status = CODEC_ERROR;
|
||||
goto done;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
/* advance to first WAVE chunk */
|
||||
|
@ -224,8 +224,7 @@ next_track:
|
|||
if (n < 8) {
|
||||
DEBUGF("data chunk request_buffer error\n");
|
||||
/* no more chunks, 'data' chunk must not have been found */
|
||||
status = CODEC_ERROR;
|
||||
goto done;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
/* chunkSize */
|
||||
|
@ -233,8 +232,7 @@ next_track:
|
|||
if (memcmp(buf, WAVE64_GUID_FMT, 16) == 0) {
|
||||
if (size < 16) {
|
||||
DEBUGF("CODEC_ERROR: 'fmt ' chunk size=%d < 16\n", (int)size);
|
||||
status = CODEC_ERROR;
|
||||
goto done;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
/* wFormatTag */
|
||||
format.formattag=buf[24]|(buf[25]<<8);
|
||||
|
@ -263,8 +261,7 @@ next_track:
|
|||
if (format.size < 22) {
|
||||
DEBUGF("CODEC_ERROR: WAVE_FORMAT_EXTENSIBLE is "
|
||||
"missing extension\n");
|
||||
status = CODEC_ERROR;
|
||||
goto done;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
/* wValidBitsPerSample */
|
||||
format.bitspersample = buf[42]|(buf[43]<<8);
|
||||
|
@ -279,10 +276,7 @@ next_track:
|
|||
if (format.formattag == WAVE_FORMAT_ADPCM)
|
||||
{
|
||||
if (!set_msadpcm_coeffs(buf))
|
||||
{
|
||||
status = CODEC_ERROR;
|
||||
goto done;
|
||||
}
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
/* get codec */
|
||||
|
@ -291,8 +285,7 @@ next_track:
|
|||
{
|
||||
DEBUGF("CODEC_ERROR: unsupported wave format 0x%x\n",
|
||||
(unsigned int) format.formattag);
|
||||
status = CODEC_ERROR;
|
||||
goto done;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
/* riff 8bit linear pcm is unsigned */
|
||||
|
@ -301,10 +294,7 @@ next_track:
|
|||
|
||||
/* check format, and calculate chunk size */
|
||||
if (!codec->set_format(&format))
|
||||
{
|
||||
status = CODEC_ERROR;
|
||||
goto done;
|
||||
}
|
||||
return CODEC_ERROR;
|
||||
} else if (memcmp(buf, WAVE64_GUID_DATA, 16) == 0) {
|
||||
format.numbytes = size;
|
||||
/* advance to start of data */
|
||||
|
@ -330,31 +320,26 @@ next_track:
|
|||
if (!codec)
|
||||
{
|
||||
DEBUGF("CODEC_ERROR: 'fmt ' chunk not found\n");
|
||||
status = CODEC_ERROR;
|
||||
goto done;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
/* common format check */
|
||||
if (format.channels == 0) {
|
||||
DEBUGF("CODEC_ERROR: 'fmt ' chunk not found or 0-channels file\n");
|
||||
status = CODEC_ERROR;
|
||||
goto done;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
if (format.samplesperblock == 0) {
|
||||
DEBUGF("CODEC_ERROR: 'fmt ' chunk not found or 0-wSamplesPerBlock file\n");
|
||||
status = CODEC_ERROR;
|
||||
goto done;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
if (format.blockalign == 0)
|
||||
{
|
||||
DEBUGF("CODEC_ERROR: 'fmt ' chunk not found or 0-blockalign file\n");
|
||||
status = CODEC_ERROR;
|
||||
goto done;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
if (format.numbytes == 0) {
|
||||
DEBUGF("CODEC_ERROR: 'data' chunk not found or has zero-length\n");
|
||||
status = CODEC_ERROR;
|
||||
goto done;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
/* check chunksize */
|
||||
|
@ -364,8 +349,7 @@ next_track:
|
|||
if (format.chunksize == 0)
|
||||
{
|
||||
DEBUGF("CODEC_ERROR: chunksize is 0\n");
|
||||
status = CODEC_ERROR;
|
||||
goto done;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency);
|
||||
|
@ -375,8 +359,7 @@ next_track:
|
|||
ci->configure(DSP_SET_STEREO_MODE, STEREO_MONO);
|
||||
} else {
|
||||
DEBUGF("CODEC_ERROR: more than 2 channels\n");
|
||||
status = CODEC_ERROR;
|
||||
goto done;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
/* make sure we're at the correct offset */
|
||||
|
@ -385,14 +368,14 @@ next_track:
|
|||
struct pcm_pos *newpos = codec->get_seek_pos(bytesdone - firstblockposn,
|
||||
PCM_SEEK_POS, &read_buffer);
|
||||
|
||||
if (newpos->pos > format.numbytes)
|
||||
goto done;
|
||||
if (newpos->pos > format.numbytes) {
|
||||
return CODEC_OK;
|
||||
}
|
||||
if (ci->seek_buffer(firstblockposn + newpos->pos))
|
||||
{
|
||||
bytesdone = newpos->pos;
|
||||
decodedsamples = newpos->samples;
|
||||
}
|
||||
ci->seek_complete();
|
||||
} else {
|
||||
/* already where we need to be */
|
||||
bytesdone = 0;
|
||||
|
@ -402,22 +385,29 @@ next_track:
|
|||
endofstream = 0;
|
||||
|
||||
while (!endofstream) {
|
||||
ci->yield();
|
||||
if (ci->stop_codec || ci->new_track) {
|
||||
break;
|
||||
}
|
||||
enum codec_command_action action = ci->get_command(¶m);
|
||||
|
||||
if (ci->seek_time) {
|
||||
struct pcm_pos *newpos = codec->get_seek_pos(ci->seek_time, PCM_SEEK_TIME,
|
||||
if (action == CODEC_ACTION_HALT)
|
||||
break;
|
||||
|
||||
if (action == CODEC_ACTION_SEEK_TIME) {
|
||||
struct pcm_pos *newpos = codec->get_seek_pos(param, PCM_SEEK_TIME,
|
||||
&read_buffer);
|
||||
|
||||
if (newpos->pos > format.numbytes)
|
||||
{
|
||||
ci->set_elapsed(ci->id3->length);
|
||||
ci->seek_complete();
|
||||
break;
|
||||
}
|
||||
|
||||
if (ci->seek_buffer(firstblockposn + newpos->pos))
|
||||
{
|
||||
bytesdone = newpos->pos;
|
||||
decodedsamples = newpos->samples;
|
||||
}
|
||||
|
||||
ci->set_elapsed(decodedsamples*1000LL/ci->id3->frequency);
|
||||
ci->seek_complete();
|
||||
}
|
||||
|
||||
|
@ -429,11 +419,10 @@ next_track:
|
|||
endofstream = 1;
|
||||
}
|
||||
|
||||
status = codec->decode(wavbuf, n, samples, &bufcount);
|
||||
if (status == CODEC_ERROR)
|
||||
if (codec->decode(wavbuf, n, samples, &bufcount) == CODEC_ERROR)
|
||||
{
|
||||
DEBUGF("codec error\n");
|
||||
goto done;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
ci->pcmbuf_insert(samples, NULL, bufcount);
|
||||
|
@ -445,12 +434,6 @@ next_track:
|
|||
endofstream = 1;
|
||||
ci->set_elapsed(decodedsamples*1000LL/ci->id3->frequency);
|
||||
}
|
||||
status = CODEC_OK;
|
||||
|
||||
done:
|
||||
if (ci->request_next_track())
|
||||
goto next_track;
|
||||
|
||||
exit:
|
||||
return status;
|
||||
return CODEC_OK;
|
||||
}
|
||||
|
|
|
@ -345,23 +345,32 @@ static bool init_encoder(void)
|
|||
return true;
|
||||
} /* init_encoder */
|
||||
|
||||
/* main codec entry point */
|
||||
enum codec_status codec_main(void)
|
||||
/* this is the codec entry point */
|
||||
enum codec_status codec_main(enum codec_entry_call_reason reason)
|
||||
{
|
||||
if (reason == CODEC_LOAD) {
|
||||
if (!init_encoder())
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
else if (reason == CODEC_UNLOAD) {
|
||||
/* reset parameters to initial state */
|
||||
ci->enc_set_parameters(NULL);
|
||||
}
|
||||
|
||||
return CODEC_OK;
|
||||
}
|
||||
|
||||
/* this is called for each file to process */
|
||||
enum codec_status codec_run(void)
|
||||
{
|
||||
/* main encoding loop */
|
||||
while(!ci->stop_codec)
|
||||
{
|
||||
uint32_t *src;
|
||||
|
||||
while ((src = (uint32_t *)ci->enc_get_pcm_data(PCM_CHUNK_SIZE)) != NULL)
|
||||
while(ci->get_command(NULL) != CODEC_ACTION_HALT)
|
||||
{
|
||||
uint32_t *src = (uint32_t *)ci->enc_get_pcm_data(PCM_CHUNK_SIZE);
|
||||
struct enc_chunk_hdr *chunk;
|
||||
|
||||
if (ci->stop_codec)
|
||||
break;
|
||||
if(src == NULL)
|
||||
continue;
|
||||
|
||||
chunk = ci->enc_get_chunk();
|
||||
chunk->enc_size = enc_size;
|
||||
|
@ -371,14 +380,7 @@ enum codec_status codec_main(void)
|
|||
chunk_to_wav_format(src, (uint32_t *)chunk->enc_data);
|
||||
|
||||
ci->enc_finish_chunk();
|
||||
ci->yield();
|
||||
}
|
||||
|
||||
ci->yield();
|
||||
}
|
||||
|
||||
/* reset parameters to initial state */
|
||||
ci->enc_set_parameters(NULL);
|
||||
|
||||
return CODEC_OK;
|
||||
} /* codec_start */
|
||||
}
|
||||
|
|
|
@ -31,39 +31,39 @@ static int32_t temp_buffer [BUFFER_SIZE] IBSS_ATTR;
|
|||
static int32_t read_callback (void *buffer, int32_t bytes)
|
||||
{
|
||||
int32_t retval = ci->read_filebuf (buffer, bytes);
|
||||
ci->id3->offset = ci->curpos;
|
||||
ci->set_offset(ci->curpos);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* this is the codec entry point */
|
||||
enum codec_status codec_main(void)
|
||||
enum codec_status codec_main(enum codec_entry_call_reason reason)
|
||||
{
|
||||
if (reason == CODEC_LOAD) {
|
||||
/* Generic codec initialisation */
|
||||
ci->configure(DSP_SET_SAMPLE_DEPTH, 28);
|
||||
}
|
||||
|
||||
return CODEC_OK;
|
||||
}
|
||||
|
||||
/* this is called for each file to process */
|
||||
enum codec_status codec_run(void)
|
||||
{
|
||||
WavpackContext *wpc;
|
||||
char error [80];
|
||||
int bps, nchans, sr_100;
|
||||
int retval;
|
||||
intptr_t param;
|
||||
|
||||
/* Generic codec initialisation */
|
||||
ci->configure(DSP_SET_SAMPLE_DEPTH, 28);
|
||||
if (codec_init())
|
||||
return CODEC_ERROR;
|
||||
|
||||
next_track:
|
||||
retval = CODEC_OK;
|
||||
|
||||
if (codec_init()) {
|
||||
retval = CODEC_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (codec_wait_taginfo() != 0)
|
||||
goto done;
|
||||
ci->seek_buffer (ci->id3->offset);
|
||||
|
||||
/* Create a decoder instance */
|
||||
wpc = WavpackOpenFileInput (read_callback, error);
|
||||
|
||||
if (!wpc) {
|
||||
retval = CODEC_ERROR;
|
||||
goto done;
|
||||
}
|
||||
if (!wpc)
|
||||
return CODEC_ERROR;
|
||||
|
||||
ci->configure(DSP_SWITCH_FREQUENCY, WavpackGetSampleRate (wpc));
|
||||
codec_set_replaygain(ci->id3);
|
||||
|
@ -78,55 +78,47 @@ next_track:
|
|||
|
||||
while (1) {
|
||||
int32_t nsamples;
|
||||
enum codec_command_action action = ci->get_command(¶m);
|
||||
|
||||
if (ci->seek_time && ci->taginfo_ready && ci->id3->length) {
|
||||
ci->seek_time--;
|
||||
if (action == CODEC_ACTION_HALT)
|
||||
break;
|
||||
|
||||
if (action == CODEC_ACTION_SEEK_TIME) {
|
||||
int curpos_ms = WavpackGetSampleIndex (wpc) / sr_100 * 10;
|
||||
int n, d, skip;
|
||||
|
||||
if (ci->seek_time > curpos_ms) {
|
||||
n = ci->seek_time - curpos_ms;
|
||||
if (param > curpos_ms) {
|
||||
n = param - curpos_ms;
|
||||
d = ci->id3->length - curpos_ms;
|
||||
skip = (int)((int64_t)(ci->filesize - ci->curpos) * n / d);
|
||||
ci->seek_buffer (ci->curpos + skip);
|
||||
}
|
||||
else {
|
||||
n = curpos_ms - ci->seek_time;
|
||||
else if (curpos_ms != 0) {
|
||||
n = curpos_ms - param;
|
||||
d = curpos_ms;
|
||||
skip = (int)((int64_t) ci->curpos * n / d);
|
||||
ci->seek_buffer (ci->curpos - skip);
|
||||
}
|
||||
|
||||
wpc = WavpackOpenFileInput (read_callback, error);
|
||||
ci->seek_complete();
|
||||
|
||||
if (!wpc)
|
||||
{
|
||||
ci->seek_complete();
|
||||
break;
|
||||
}
|
||||
|
||||
ci->set_elapsed (WavpackGetSampleIndex (wpc) / sr_100 * 10);
|
||||
ci->yield ();
|
||||
ci->seek_complete();
|
||||
}
|
||||
|
||||
nsamples = WavpackUnpackSamples (wpc, temp_buffer, BUFFER_SIZE / nchans);
|
||||
|
||||
if (!nsamples || ci->stop_codec || ci->new_track)
|
||||
break;
|
||||
|
||||
ci->yield ();
|
||||
|
||||
if (ci->stop_codec || ci->new_track)
|
||||
if (!nsamples)
|
||||
break;
|
||||
|
||||
ci->pcmbuf_insert (temp_buffer, NULL, nsamples);
|
||||
|
||||
ci->set_elapsed (WavpackGetSampleIndex (wpc) / sr_100 * 10);
|
||||
ci->yield ();
|
||||
}
|
||||
|
||||
done:
|
||||
if (ci->request_next_track())
|
||||
goto next_track;
|
||||
|
||||
exit:
|
||||
return retval;
|
||||
return CODEC_OK;
|
||||
}
|
||||
|
|
|
@ -389,28 +389,36 @@ static bool init_encoder(void)
|
|||
return true;
|
||||
} /* init_encoder */
|
||||
|
||||
enum codec_status codec_main(void)
|
||||
/* this is the codec entry point */
|
||||
enum codec_status codec_main(enum codec_entry_call_reason reason)
|
||||
{
|
||||
if (reason == CODEC_LOAD) {
|
||||
/* initialize params and config */
|
||||
if (!init_encoder())
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
else if (reason == CODEC_UNLOAD) {
|
||||
/* reset parameters to initial state */
|
||||
ci->enc_set_parameters(NULL);
|
||||
}
|
||||
|
||||
return CODEC_OK;
|
||||
}
|
||||
|
||||
/* this is called for each file to process */
|
||||
enum codec_status codec_run(void)
|
||||
{
|
||||
/* main encoding loop */
|
||||
while(!ci->stop_codec)
|
||||
{
|
||||
uint8_t *src;
|
||||
|
||||
while ((src = ci->enc_get_pcm_data(PCM_CHUNK_SIZE)) != NULL)
|
||||
while(ci->get_command(NULL) != CODEC_ACTION_HALT)
|
||||
{
|
||||
uint8_t *src = (uint8_t *)ci->enc_get_pcm_data(PCM_CHUNK_SIZE);
|
||||
struct enc_chunk_hdr *chunk;
|
||||
bool abort_chunk;
|
||||
uint8_t *dst;
|
||||
uint8_t *src_end;
|
||||
|
||||
if(ci->stop_codec)
|
||||
break;
|
||||
|
||||
abort_chunk = true;
|
||||
if(src == NULL)
|
||||
continue;
|
||||
|
||||
chunk = ci->enc_get_chunk();
|
||||
|
||||
|
@ -430,14 +438,14 @@ enum codec_status codec_main(void)
|
|||
/* encode chunk in four steps yielding between each */
|
||||
do
|
||||
{
|
||||
abort_chunk = true;
|
||||
if (WavpackPackSamples(wpc, (int32_t *)src,
|
||||
PCM_SAMP_PER_CHUNK/4))
|
||||
{
|
||||
chunk->num_pcm += PCM_SAMP_PER_CHUNK/4;
|
||||
ci->yield();
|
||||
/* could've been stopped in some way */
|
||||
abort_chunk = ci->stop_codec ||
|
||||
(chunk->flags & CHUNKF_ABORT);
|
||||
abort_chunk = chunk->flags & CHUNKF_ABORT;
|
||||
}
|
||||
|
||||
src += input_step;
|
||||
|
@ -455,11 +463,5 @@ enum codec_status codec_main(void)
|
|||
}
|
||||
}
|
||||
|
||||
ci->yield();
|
||||
}
|
||||
|
||||
/* reset parameters to initial state */
|
||||
ci->enc_set_parameters(NULL);
|
||||
|
||||
return CODEC_OK;
|
||||
} /* codec_start */
|
||||
}
|
||||
|
|
|
@ -29,53 +29,52 @@ CODEC_HEADER
|
|||
static WMADecodeContext wmadec;
|
||||
|
||||
/* this is the codec entry point */
|
||||
enum codec_status codec_main(void)
|
||||
enum codec_status codec_main(enum codec_entry_call_reason reason)
|
||||
{
|
||||
if (reason == CODEC_LOAD) {
|
||||
/* Generic codec initialisation */
|
||||
ci->configure(DSP_SET_SAMPLE_DEPTH, 29);
|
||||
}
|
||||
|
||||
return CODEC_OK;
|
||||
}
|
||||
|
||||
/* this is called for each file to process */
|
||||
enum codec_status codec_run(void)
|
||||
{
|
||||
uint32_t elapsedtime;
|
||||
int retval;
|
||||
asf_waveformatex_t wfx;
|
||||
size_t resume_offset;
|
||||
int i;
|
||||
int wmares, res;
|
||||
int wmares;
|
||||
int res = 0;
|
||||
uint8_t* audiobuf;
|
||||
int audiobufsize;
|
||||
int packetlength = 0;
|
||||
int errcount = 0;
|
||||
|
||||
/* Generic codec initialisation */
|
||||
ci->configure(DSP_SET_SAMPLE_DEPTH, 29);
|
||||
|
||||
next_track:
|
||||
retval = CODEC_OK;
|
||||
intptr_t param;
|
||||
|
||||
/* Proper reset of the decoder context. */
|
||||
memset(&wmadec, 0, sizeof(wmadec));
|
||||
|
||||
/* Wait for the metadata to be read */
|
||||
if (codec_wait_taginfo() != 0)
|
||||
goto done;
|
||||
|
||||
/* Remember the resume position - when the codec is opened, the
|
||||
playback engine will reset it. */
|
||||
resume_offset = ci->id3->offset;
|
||||
|
||||
restart_track:
|
||||
retval = CODEC_OK;
|
||||
|
||||
if (codec_init()) {
|
||||
LOGF("WMA: Error initialising codec\n");
|
||||
retval = CODEC_ERROR;
|
||||
goto exit;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
/* Copy the format metadata we've stored in the id3 TOC field. This
|
||||
saves us from parsing it again here. */
|
||||
memcpy(&wfx, ci->id3->toc, sizeof(wfx));
|
||||
|
||||
ci->seek_buffer(ci->id3->first_frame_offset);
|
||||
if (wma_decode_init(&wmadec,&wfx) < 0) {
|
||||
LOGF("WMA: Unsupported or corrupt file\n");
|
||||
retval = CODEC_ERROR;
|
||||
goto exit;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
if (resume_offset > ci->id3->first_frame_offset)
|
||||
|
@ -101,34 +100,35 @@ restart_track:
|
|||
codec_set_replaygain(ci->id3);
|
||||
|
||||
/* The main decoding loop */
|
||||
|
||||
res = 1;
|
||||
while (res >= 0)
|
||||
{
|
||||
ci->yield();
|
||||
if (ci->stop_codec || ci->new_track) {
|
||||
goto done;
|
||||
}
|
||||
enum codec_command_action action = ci->get_command(¶m);
|
||||
|
||||
if (action == CODEC_ACTION_HALT)
|
||||
break;
|
||||
|
||||
/* Deal with any pending seek requests */
|
||||
if (ci->seek_time){
|
||||
if (action == CODEC_ACTION_SEEK_TIME) {
|
||||
|
||||
if (ci->seek_time == 1) {
|
||||
if (param == 0) {
|
||||
ci->set_elapsed(0);
|
||||
ci->seek_complete();
|
||||
goto restart_track; /* Pretend you never saw this... */
|
||||
}
|
||||
|
||||
elapsedtime = asf_seek(ci->seek_time, &wfx);
|
||||
elapsedtime = asf_seek(param, &wfx);
|
||||
if (elapsedtime < 1){
|
||||
ci->set_elapsed(0);
|
||||
ci->seek_complete();
|
||||
goto next_track;
|
||||
break;
|
||||
}
|
||||
/*DEBUGF("Seek returned %d\n", (int)elapsedtime);*/
|
||||
ci->set_elapsed(elapsedtime);
|
||||
|
||||
/*flush the wma decoder state*/
|
||||
wmadec.last_superframe_len = 0;
|
||||
wmadec.last_bitoffset = 0;
|
||||
|
||||
ci->set_elapsed(elapsedtime);
|
||||
ci->seek_complete();
|
||||
}
|
||||
errcount = 0;
|
||||
|
@ -140,10 +140,15 @@ new_packet:
|
|||
* times. If we succeed, the error counter will be reset.
|
||||
*/
|
||||
|
||||
if (res == ASF_ERROR_EOF) {
|
||||
/* File ended - not an error */
|
||||
break;
|
||||
}
|
||||
|
||||
errcount++;
|
||||
DEBUGF("read_packet error %d, errcount %d\n",wmares, errcount);
|
||||
if (errcount > 5) {
|
||||
goto done;
|
||||
return CODEC_ERROR;
|
||||
} else {
|
||||
ci->advance_buffer(packetlength);
|
||||
goto new_packet;
|
||||
|
@ -163,7 +168,7 @@ new_packet:
|
|||
errcount++;
|
||||
DEBUGF("WMA decode error %d, errcount %d\n",wmares, errcount);
|
||||
if (errcount > 5) {
|
||||
goto done;
|
||||
return CODEC_ERROR;
|
||||
} else {
|
||||
ci->advance_buffer(packetlength);
|
||||
goto new_packet;
|
||||
|
@ -173,18 +178,12 @@ new_packet:
|
|||
elapsedtime += (wmares*10)/(wfx.rate/100);
|
||||
ci->set_elapsed(elapsedtime);
|
||||
}
|
||||
ci->yield();
|
||||
}
|
||||
}
|
||||
|
||||
ci->advance_buffer(packetlength);
|
||||
}
|
||||
|
||||
done:
|
||||
/*LOGF("WMA: Decoded %ld samples\n",elapsedtime*wfx.rate/1000);*/
|
||||
|
||||
if (ci->request_next_track())
|
||||
goto next_track;
|
||||
exit:
|
||||
return retval;
|
||||
return CODEC_OK;
|
||||
}
|
||||
|
|
|
@ -27,11 +27,22 @@ CODEC_HEADER
|
|||
|
||||
int32_t *dec[2]; /* pointers to the output buffers in WMAProDecodeCtx in wmaprodec.c */
|
||||
|
||||
|
||||
/* this is the codec entry point */
|
||||
enum codec_status codec_main(void)
|
||||
enum codec_status codec_main(enum codec_entry_call_reason reason)
|
||||
{
|
||||
if (reason == CODEC_LOAD) {
|
||||
/* Generic codec initialisation */
|
||||
ci->configure(DSP_SET_SAMPLE_DEPTH, WMAPRO_DSP_SAMPLE_DEPTH);
|
||||
}
|
||||
|
||||
return CODEC_OK;
|
||||
}
|
||||
|
||||
/* this is called for each file to process */
|
||||
enum codec_status codec_run(void)
|
||||
{
|
||||
uint32_t elapsedtime;
|
||||
int retval;
|
||||
asf_waveformatex_t wfx; /* Holds the stream properties */
|
||||
size_t resume_offset;
|
||||
int res; /* Return values from asf_read_packet() and decode_packet() */
|
||||
|
@ -42,28 +53,15 @@ enum codec_status codec_main(void)
|
|||
int pktcnt = 0; /* Count of the packets played */
|
||||
uint8_t *data; /* Pointer to decoder input buffer */
|
||||
int size; /* Size of the input frame to the decoder */
|
||||
|
||||
/* Generic codec initialisation */
|
||||
ci->configure(DSP_SET_SAMPLE_DEPTH, WMAPRO_DSP_SAMPLE_DEPTH);
|
||||
|
||||
|
||||
next_track:
|
||||
retval = CODEC_OK;
|
||||
|
||||
/* Wait for the metadata to be read */
|
||||
if (codec_wait_taginfo() != 0)
|
||||
goto done;
|
||||
intptr_t param;
|
||||
|
||||
/* Remember the resume position */
|
||||
resume_offset = ci->id3->offset;
|
||||
|
||||
restart_track:
|
||||
retval = CODEC_OK;
|
||||
|
||||
if (codec_init()) {
|
||||
LOGF("(WMA PRO) Error: Error initialising codec\n");
|
||||
retval = CODEC_ERROR;
|
||||
goto done;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
/* Copy the format metadata we've stored in the id3 TOC field. This
|
||||
|
@ -77,8 +75,7 @@ restart_track:
|
|||
|
||||
if (decode_init(&wfx) < 0) {
|
||||
LOGF("(WMA PRO) Error: Unsupported or corrupt file\n");
|
||||
retval = CODEC_ERROR;
|
||||
goto done;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
/* Now advance the file position to the first frame */
|
||||
|
@ -91,23 +88,24 @@ restart_track:
|
|||
|
||||
while (pktcnt < wfx.numpackets)
|
||||
{
|
||||
ci->yield();
|
||||
if (ci->stop_codec || ci->new_track) {
|
||||
goto done;
|
||||
}
|
||||
enum codec_command_action action = ci->get_command(¶m);
|
||||
|
||||
if (action == CODEC_ACTION_HALT)
|
||||
break;
|
||||
|
||||
/* Deal with any pending seek requests */
|
||||
if (ci->seek_time){
|
||||
|
||||
if (ci->seek_time == 1) {
|
||||
if (action == CODEC_ACTION_SEEK_TIME) {
|
||||
if (param == 0) {
|
||||
ci->set_elapsed(0);
|
||||
ci->seek_complete();
|
||||
goto restart_track; /* Pretend you never saw this... */
|
||||
}
|
||||
|
||||
elapsedtime = asf_seek(ci->seek_time, &wfx);
|
||||
elapsedtime = asf_seek(param, &wfx);
|
||||
if (elapsedtime < 1){
|
||||
ci->set_elapsed(0);
|
||||
ci->seek_complete();
|
||||
goto next_track;
|
||||
break;
|
||||
}
|
||||
|
||||
ci->set_elapsed(elapsedtime);
|
||||
|
@ -118,7 +116,7 @@ restart_track:
|
|||
|
||||
if (res < 0) {
|
||||
LOGF("(WMA PRO) Warning: asf_read_packet returned %d", res);
|
||||
goto done;
|
||||
return CODEC_ERROR;
|
||||
} else {
|
||||
data = audiobuf;
|
||||
size = audiobufsize;
|
||||
|
@ -132,7 +130,7 @@ restart_track:
|
|||
res = decode_packet(&wfx, dec, &outlen, data, size);
|
||||
if(res < 0) {
|
||||
LOGF("(WMA PRO) Error: decode_packet returned %d", res);
|
||||
goto done;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
data += res;
|
||||
size -= res;
|
||||
|
@ -152,10 +150,6 @@ restart_track:
|
|||
ci->advance_buffer(packetlength);
|
||||
}
|
||||
|
||||
done:
|
||||
if (ci->request_next_track())
|
||||
goto next_track;
|
||||
|
||||
return retval;
|
||||
return CODEC_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -52,10 +52,20 @@ static void init_codec_ctx(AVCodecContext *avctx, asf_waveformatex_t *wfx)
|
|||
}
|
||||
|
||||
/* this is the codec entry point */
|
||||
enum codec_status codec_main(void)
|
||||
enum codec_status codec_main(enum codec_entry_call_reason reason)
|
||||
{
|
||||
if (reason == CODEC_LOAD) {
|
||||
/* Generic codec initialisation */
|
||||
ci->configure(DSP_SET_SAMPLE_DEPTH, 31);
|
||||
}
|
||||
|
||||
return CODEC_OK;
|
||||
}
|
||||
|
||||
/* this is called for each file to process */
|
||||
enum codec_status codec_run(void)
|
||||
{
|
||||
uint32_t elapsedtime;
|
||||
int retval;
|
||||
asf_waveformatex_t wfx; /* Holds the stream properties */
|
||||
size_t resume_offset;
|
||||
int res; /* Return values from asf_read_packet() and decode_packet() */
|
||||
|
@ -64,27 +74,14 @@ enum codec_status codec_main(void)
|
|||
int packetlength = 0; /* Logical packet size (minus the header size) */
|
||||
int outlen = 0; /* Number of bytes written to the output buffer */
|
||||
int pktcnt = 0; /* Count of the packets played */
|
||||
|
||||
/* Generic codec initialisation */
|
||||
ci->configure(DSP_SET_SAMPLE_DEPTH, 31);
|
||||
|
||||
|
||||
next_track:
|
||||
retval = CODEC_OK;
|
||||
|
||||
/* Wait for the metadata to be read */
|
||||
if (codec_wait_taginfo() != 0)
|
||||
goto done;
|
||||
intptr_t param;
|
||||
|
||||
/* Remember the resume position */
|
||||
resume_offset = ci->id3->offset;
|
||||
restart_track:
|
||||
retval = CODEC_OK;
|
||||
|
||||
if (codec_init()) {
|
||||
LOGF("(WMA Voice) Error: Error initialising codec\n");
|
||||
retval = CODEC_ERROR;
|
||||
goto done;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
/* Copy the format metadata we've stored in the id3 TOC field. This
|
||||
|
@ -98,13 +95,14 @@ restart_track:
|
|||
STEREO_MONO : STEREO_INTERLEAVED);
|
||||
codec_set_replaygain(ci->id3);
|
||||
|
||||
ci->seek_buffer(0);
|
||||
|
||||
/* Initialise the AVCodecContext */
|
||||
init_codec_ctx(&avctx, &wfx);
|
||||
|
||||
if (wmavoice_decode_init(&avctx) < 0) {
|
||||
LOGF("(WMA Voice) Error: Unsupported or corrupt file\n");
|
||||
retval = CODEC_ERROR;
|
||||
goto done;
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
/* Now advance the file position to the first frame */
|
||||
|
@ -117,21 +115,24 @@ restart_track:
|
|||
|
||||
while (pktcnt < wfx.numpackets)
|
||||
{
|
||||
ci->yield();
|
||||
if (ci->stop_codec || ci->new_track) {
|
||||
goto done;
|
||||
}
|
||||
enum codec_command_action action = ci->get_command(¶m);
|
||||
|
||||
if (action == CODEC_ACTION_HALT)
|
||||
break;
|
||||
|
||||
/* Deal with any pending seek requests */
|
||||
if (ci->seek_time){
|
||||
if (action == CODEC_ACTION_SEEK_TIME) {
|
||||
ci->set_elapsed(param);
|
||||
|
||||
if (ci->seek_time == 1) {
|
||||
if (param == 0) {
|
||||
ci->set_elapsed(0);
|
||||
ci->seek_complete();
|
||||
goto restart_track; /* Pretend you never saw this... */
|
||||
}
|
||||
|
||||
elapsedtime = asf_seek(ci->seek_time, &wfx);
|
||||
elapsedtime = asf_seek(param, &wfx);
|
||||
if (elapsedtime < 1){
|
||||
ci->set_elapsed(0);
|
||||
ci->seek_complete();
|
||||
goto next_track;
|
||||
}
|
||||
|
@ -145,7 +146,7 @@ new_packet:
|
|||
|
||||
if (res < 0) {
|
||||
LOGF("(WMA Voice) read_packet error %d\n",res);
|
||||
goto done;
|
||||
return CODEC_ERROR;
|
||||
} else {
|
||||
avpkt.data = audiobuf;
|
||||
avpkt.size = audiobufsize;
|
||||
|
@ -165,8 +166,9 @@ new_packet:
|
|||
ci->advance_buffer(packetlength);
|
||||
goto new_packet;
|
||||
}
|
||||
else
|
||||
goto done;
|
||||
else {
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
}
|
||||
avpkt.data += res;
|
||||
avpkt.size -= res;
|
||||
|
@ -186,10 +188,6 @@ new_packet:
|
|||
ci->advance_buffer(packetlength);
|
||||
}
|
||||
|
||||
done:
|
||||
if (ci->request_next_track())
|
||||
goto next_track;
|
||||
|
||||
return retval;
|
||||
return CODEC_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -219,10 +219,10 @@ static int skintouch_to_wps(struct wps_data *data)
|
|||
#endif
|
||||
case ACTION_TOUCH_SCROLLBAR:
|
||||
skin_get_global_state()->id3->elapsed = skin_get_global_state()->id3->length*offset/100;
|
||||
if (!skin_get_global_state()->paused)
|
||||
#if (CONFIG_CODEC == SWCODEC)
|
||||
audio_pre_ff_rewind();
|
||||
#else
|
||||
if (!skin_get_global_state()->paused)
|
||||
audio_pause();
|
||||
#endif
|
||||
audio_ff_rewind(skin_get_global_state()->id3->elapsed);
|
||||
|
@ -300,10 +300,10 @@ bool ffwd_rew(int button)
|
|||
if ( (audio_status() & AUDIO_STATUS_PLAY) &&
|
||||
skin_get_global_state()->id3 && skin_get_global_state()->id3->length )
|
||||
{
|
||||
if (!skin_get_global_state()->paused)
|
||||
#if (CONFIG_CODEC == SWCODEC)
|
||||
audio_pre_ff_rewind();
|
||||
#else
|
||||
if (!skin_get_global_state()->paused)
|
||||
audio_pause();
|
||||
#endif
|
||||
#if CONFIG_KEYPAD == PLAYER_PAD
|
||||
|
@ -472,10 +472,10 @@ static void prev_track(unsigned long skip_thresh)
|
|||
return;
|
||||
}
|
||||
|
||||
if (!state->paused)
|
||||
#if (CONFIG_CODEC == SWCODEC)
|
||||
audio_pre_ff_rewind();
|
||||
#else
|
||||
if (!state->paused)
|
||||
audio_pause();
|
||||
#endif
|
||||
|
||||
|
@ -554,16 +554,20 @@ static void play_hop(int direction)
|
|||
{
|
||||
elapsed += step * direction;
|
||||
}
|
||||
if((audio_status() & AUDIO_STATUS_PLAY) && !state->paused)
|
||||
if(audio_status() & AUDIO_STATUS_PLAY)
|
||||
{
|
||||
#if (CONFIG_CODEC == SWCODEC)
|
||||
audio_pre_ff_rewind();
|
||||
#else
|
||||
if (!state->paused)
|
||||
audio_pause();
|
||||
#endif
|
||||
}
|
||||
|
||||
#if (CONFIG_CODEC == SWCODEC)
|
||||
audio_ff_rewind(elapsed);
|
||||
#else
|
||||
audio_ff_rewind(state->id3->elapsed = elapsed);
|
||||
#if (CONFIG_CODEC != SWCODEC)
|
||||
if (!state->paused)
|
||||
audio_resume();
|
||||
#endif
|
||||
|
@ -849,10 +853,10 @@ long gui_wps_show(void)
|
|||
{
|
||||
if (state->id3->cuesheet)
|
||||
{
|
||||
if (!state->paused)
|
||||
#if (CONFIG_CODEC == SWCODEC)
|
||||
audio_pre_ff_rewind();
|
||||
#else
|
||||
if (!state->paused)
|
||||
audio_pause();
|
||||
#endif
|
||||
audio_ff_rewind(0);
|
||||
|
@ -1146,6 +1150,17 @@ static void nextid3available_callback(void* param)
|
|||
skin_request_full_update(WPS);
|
||||
}
|
||||
|
||||
#ifdef AUDIO_FAST_SKIP_PREVIEW
|
||||
/* this is called on the audio_skip caller thread */
|
||||
static void track_skip_callback(void *param)
|
||||
{
|
||||
struct wps_state *state = skin_get_global_state();
|
||||
state->id3 = audio_current_track();
|
||||
state->nid3 = audio_next_track();
|
||||
skin_request_full_update(WPS);
|
||||
(void)param;
|
||||
}
|
||||
#endif /* AUDIO_FAST_SKIP_PREVIEW */
|
||||
|
||||
static void wps_state_init(void)
|
||||
{
|
||||
|
@ -1167,6 +1182,9 @@ static void wps_state_init(void)
|
|||
/* add the WPS track event callbacks */
|
||||
add_event(PLAYBACK_EVENT_TRACK_CHANGE, false, track_changed_callback);
|
||||
add_event(PLAYBACK_EVENT_NEXTTRACKID3_AVAILABLE, false, nextid3available_callback);
|
||||
#ifdef AUDIO_FAST_SKIP_PREVIEW
|
||||
add_event(PLAYBACK_EVENT_TRACK_SKIP, false, track_skip_callback);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -159,9 +159,13 @@ static int cuesheet_callback(int action,const struct menu_item_ex *this_item)
|
|||
switch (action)
|
||||
{
|
||||
case ACTION_EXIT_MENUITEM: /* on exit */
|
||||
#if CONFIG_CODEC == SWCODEC
|
||||
audio_set_cuesheet(global_settings.cuesheet);
|
||||
#else
|
||||
if (global_settings.cuesheet)
|
||||
splash(HZ*2, ID2P(LANG_PLEASE_REBOOT));
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
return action;
|
||||
}
|
||||
|
|
130
apps/metadata.c
130
apps/metadata.c
|
@ -33,7 +33,7 @@
|
|||
|
||||
#if CONFIG_CODEC == SWCODEC
|
||||
|
||||
/* For trailing tag stripping */
|
||||
/* For trailing tag stripping and base audio data types */
|
||||
#include "buffering.h"
|
||||
|
||||
#include "metadata/metadata_common.h"
|
||||
|
@ -239,6 +239,94 @@ const int afmt_rec_format[AFMT_NUM_CODECS] =
|
|||
};
|
||||
#endif /* CONFIG_CODEC == SWCODEC && defined (HAVE_RECORDING) */
|
||||
|
||||
#if CONFIG_CODEC == SWCODEC
|
||||
/* Get the canonical AFMT type */
|
||||
int get_audio_base_codec_type(int type)
|
||||
{
|
||||
int base_type = type;
|
||||
switch (type) {
|
||||
case AFMT_MPA_L1:
|
||||
case AFMT_MPA_L2:
|
||||
case AFMT_MPA_L3:
|
||||
base_type = AFMT_MPA_L3;
|
||||
break;
|
||||
case AFMT_MPC_SV7:
|
||||
case AFMT_MPC_SV8:
|
||||
base_type = AFMT_MPC_SV7;
|
||||
break;
|
||||
case AFMT_MP4_AAC:
|
||||
case AFMT_MP4_AAC_HE:
|
||||
base_type = AFMT_MP4_AAC;
|
||||
break;
|
||||
case AFMT_SAP:
|
||||
case AFMT_CMC:
|
||||
case AFMT_CM3:
|
||||
case AFMT_CMR:
|
||||
case AFMT_CMS:
|
||||
case AFMT_DMC:
|
||||
case AFMT_DLT:
|
||||
case AFMT_MPT:
|
||||
case AFMT_MPD:
|
||||
case AFMT_RMT:
|
||||
case AFMT_TMC:
|
||||
case AFMT_TM8:
|
||||
case AFMT_TM2:
|
||||
base_type = AFMT_SAP;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return base_type;
|
||||
}
|
||||
|
||||
/* Get the basic audio type */
|
||||
enum data_type get_audio_base_data_type(int afmt)
|
||||
{
|
||||
if ((unsigned)afmt >= AFMT_NUM_CODECS)
|
||||
return TYPE_UNKNOWN;
|
||||
|
||||
switch (get_audio_base_codec_type(afmt))
|
||||
{
|
||||
case AFMT_NSF:
|
||||
case AFMT_SPC:
|
||||
case AFMT_SID:
|
||||
case AFMT_MOD:
|
||||
case AFMT_SAP:
|
||||
/* Type must be allocated and loaded in its entirety onto
|
||||
the buffer */
|
||||
return TYPE_ATOMIC_AUDIO;
|
||||
|
||||
default:
|
||||
/* Assume type may be loaded and discarded incrementally */
|
||||
return TYPE_PACKET_AUDIO;
|
||||
|
||||
case AFMT_UNKNOWN:
|
||||
/* Have no idea at all */
|
||||
return TYPE_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
/* Is the format allowed to buffer starting at some offset other than 0
|
||||
or first frame only for resume purposes? */
|
||||
bool format_buffers_with_offset(int afmt)
|
||||
{
|
||||
switch (afmt)
|
||||
{
|
||||
case AFMT_MPA_L1:
|
||||
case AFMT_MPA_L2:
|
||||
case AFMT_MPA_L3:
|
||||
case AFMT_WAVPACK:
|
||||
/* Format may be loaded at the first needed frame */
|
||||
return true;
|
||||
default:
|
||||
/* Format must be loaded from the beginning of the file
|
||||
(does not imply 'atomic', while 'atomic' implies 'no offset') */
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_CODEC == SWCODEC */
|
||||
|
||||
|
||||
/* Simple file type probing by looking at the filename extension. */
|
||||
unsigned int probe_file_format(const char *filename)
|
||||
|
@ -313,7 +401,7 @@ bool get_metadata(struct mp3entry* id3, int fd, const char* trackname)
|
|||
}
|
||||
|
||||
/* Clear the mp3entry to avoid having bogus pointers appear */
|
||||
memset(id3, 0, sizeof(struct mp3entry));
|
||||
wipe_mp3entry(id3);
|
||||
|
||||
/* Take our best guess at the codec type based on file extension */
|
||||
id3->codectype = probe_file_format(trackname);
|
||||
|
@ -414,6 +502,44 @@ void copy_mp3entry(struct mp3entry *dest, const struct mp3entry *orig)
|
|||
adjust_mp3entry(dest, dest, orig);
|
||||
}
|
||||
|
||||
/* A shortcut to simplify the common task of clearing the struct */
|
||||
void wipe_mp3entry(struct mp3entry *id3)
|
||||
{
|
||||
memset(id3, 0, sizeof (struct mp3entry));
|
||||
}
|
||||
|
||||
#if CONFIG_CODEC == SWCODEC
|
||||
/* Glean what is possible from the filename alone - does not parse metadata */
|
||||
void fill_metadata_from_path(struct mp3entry *id3, const char *trackname)
|
||||
{
|
||||
char *p;
|
||||
|
||||
/* Clear the mp3entry to avoid having bogus pointers appear */
|
||||
wipe_mp3entry(id3);
|
||||
|
||||
/* Find the filename portion of the path */
|
||||
p = strrchr(trackname, '/');
|
||||
strlcpy(id3->id3v2buf, p ? ++p : id3->path, ID3V2_BUF_SIZE);
|
||||
|
||||
/* Get the format from the extension and trim it off */
|
||||
p = strrchr(id3->id3v2buf, '.');
|
||||
if (p)
|
||||
{
|
||||
/* Might be wrong for container formats - should we bother? */
|
||||
id3->codectype = probe_file_format(p);
|
||||
|
||||
if (id3->codectype != AFMT_UNKNOWN)
|
||||
*p = '\0';
|
||||
}
|
||||
|
||||
/* Set the filename as the title */
|
||||
id3->title = id3->id3v2buf;
|
||||
|
||||
/* Copy the path info */
|
||||
strlcpy(id3->path, trackname, sizeof (id3->path));
|
||||
}
|
||||
#endif /* CONFIG_CODEC == SWCODEC */
|
||||
|
||||
#ifndef __PCTOOL__
|
||||
#ifdef HAVE_TAGCACHE
|
||||
#if CONFIG_CODEC == SWCODEC
|
||||
|
|
|
@ -266,9 +266,7 @@ struct mp3entry {
|
|||
|
||||
/* resume related */
|
||||
unsigned long offset; /* bytes played */
|
||||
#if CONFIG_CODEC != SWCODEC
|
||||
int index; /* playlist index */
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_TAGCACHE
|
||||
unsigned char autoresumable; /* caches result of autoresumable() */
|
||||
|
@ -309,9 +307,14 @@ bool get_metadata(struct mp3entry* id3, int fd, const char* trackname);
|
|||
bool mp3info(struct mp3entry *entry, const char *filename);
|
||||
void adjust_mp3entry(struct mp3entry *entry, void *dest, const void *orig);
|
||||
void copy_mp3entry(struct mp3entry *dest, const struct mp3entry *orig);
|
||||
void wipe_mp3entry(struct mp3entry *id3);
|
||||
|
||||
#if CONFIG_CODEC == SWCODEC
|
||||
void fill_metadata_from_path(struct mp3entry *id3, const char *trackname);
|
||||
int get_audio_base_codec_type(int type);
|
||||
void strip_tags(int handle_id);
|
||||
enum data_type get_audio_base_data_type(int afmt);
|
||||
bool format_buffers_with_offset(int afmt);
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_TAGCACHE
|
||||
|
|
|
@ -40,6 +40,9 @@ bool get_nsf_metadata(int fd, struct mp3entry* id3)
|
|||
|
||||
p = id3->id3v2buf;
|
||||
|
||||
/* Length */
|
||||
id3->length = buf[6]*1000;
|
||||
|
||||
/* Title */
|
||||
memcpy(p, &buf[14], 32);
|
||||
id3->title = p;
|
||||
|
|
|
@ -86,6 +86,8 @@ static size_t pcmbuffer_pos IDATA_ATTR;
|
|||
/* Amount pcmbuffer_pos will be increased.*/
|
||||
static size_t pcmbuffer_fillpos IDATA_ATTR;
|
||||
|
||||
static struct chunkdesc *first_desc;
|
||||
|
||||
/* Gapless playback */
|
||||
static bool track_transition IDATA_ATTR;
|
||||
|
||||
|
@ -144,6 +146,11 @@ static void write_to_crossfade(size_t length);
|
|||
static void pcmbuf_finish_crossfade_enable(void);
|
||||
#endif
|
||||
|
||||
/* Callbacks into playback.c */
|
||||
extern void audio_pcmbuf_position_callback(unsigned int time);
|
||||
extern void audio_pcmbuf_track_change(bool pcmbuf);
|
||||
extern bool audio_pcmbuf_may_play(void);
|
||||
|
||||
|
||||
/**************************************/
|
||||
|
||||
|
@ -153,9 +160,8 @@ static void pcmbuf_finish_crossfade_enable(void);
|
|||
#ifndef SIMULATOR
|
||||
#undef DESC_DEBUG
|
||||
#endif
|
||||
|
||||
#ifdef DESC_DEBUG
|
||||
static struct chunkdesc *first_desc;
|
||||
static bool show_desc_in_use = false;
|
||||
#define DISPLAY_DESC(caller) while(!show_desc(caller))
|
||||
#define DESC_IDX(desc) (desc ? desc - first_desc : -1)
|
||||
#define SHOW_DESC(desc) if(DESC_IDX(desc)==-1) DEBUGF("--"); \
|
||||
|
@ -231,6 +237,7 @@ static void commit_chunk(bool flush_next_time)
|
|||
/* Flush! Discard all data after the currently playing chunk,
|
||||
and make the current chunk play next */
|
||||
logf("commit_chunk: flush");
|
||||
pcm_play_lock();
|
||||
write_end_chunk->link = read_chunk->link;
|
||||
read_chunk->link = pcmbuf_current;
|
||||
while (write_end_chunk->link)
|
||||
|
@ -238,6 +245,9 @@ static void commit_chunk(bool flush_next_time)
|
|||
write_end_chunk = write_end_chunk->link;
|
||||
pcmbuf_unplayed_bytes -= write_end_chunk->size;
|
||||
}
|
||||
|
||||
read_chunk->end_of_track = track_transition;
|
||||
pcm_play_unlock();
|
||||
}
|
||||
/* If there is already a read buffer setup, add to it */
|
||||
else
|
||||
|
@ -354,7 +364,7 @@ static bool prepare_insert(size_t length)
|
|||
#endif
|
||||
{
|
||||
logf("pcm starting");
|
||||
if (!(audio_status() & AUDIO_STATUS_PAUSE))
|
||||
if (audio_pcmbuf_may_play())
|
||||
pcmbuf_play_start();
|
||||
}
|
||||
}
|
||||
|
@ -373,9 +383,13 @@ void *pcmbuf_request_buffer(int *count)
|
|||
/* crossfade has begun, put the new track samples in fadebuf */
|
||||
if (crossfade_active)
|
||||
{
|
||||
*count = MIN(*count, CROSSFADE_BUFSIZE/4);
|
||||
int cnt = MIN(*count, CROSSFADE_BUFSIZE/4);
|
||||
if (prepare_insert(cnt << 2))
|
||||
{
|
||||
*count = cnt;
|
||||
return fadebuf;
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
/* if possible, reserve room in the PCM buffer for new samples */
|
||||
|
@ -421,9 +435,7 @@ void pcmbuf_write_complete(int count)
|
|||
|
||||
static inline void init_pcmbuffers(void)
|
||||
{
|
||||
#ifdef DESC_DEBUG
|
||||
first_desc = write_chunk;
|
||||
#endif
|
||||
struct chunkdesc *next = write_chunk;
|
||||
next++;
|
||||
write_end_chunk = write_chunk;
|
||||
|
@ -494,19 +506,27 @@ void pcmbuf_monitor_track_change(bool monitor)
|
|||
currently playing chunk. If not, cancel notification. */
|
||||
track_transition = monitor;
|
||||
read_end_chunk->end_of_track = monitor;
|
||||
if (!monitor)
|
||||
{
|
||||
/* Clear all notifications */
|
||||
struct chunkdesc *desc = first_desc;
|
||||
struct chunkdesc *end = desc + pcmbuf_descs();
|
||||
while (desc < end)
|
||||
desc++->end_of_track = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Post now if PCM stopped and last buffer was sent. */
|
||||
track_transition = false;
|
||||
if (monitor)
|
||||
audio_post_track_change(false);
|
||||
audio_pcmbuf_track_change(false);
|
||||
}
|
||||
|
||||
pcm_play_unlock();
|
||||
}
|
||||
|
||||
void pcmbuf_start_track_change(bool auto_skip)
|
||||
bool pcmbuf_start_track_change(bool auto_skip)
|
||||
{
|
||||
bool crossfade = false;
|
||||
#ifdef HAVE_CROSSFADE
|
||||
|
@ -547,9 +567,6 @@ void pcmbuf_start_track_change(bool auto_skip)
|
|||
/* Cancel any pending automatic gapless transition */
|
||||
pcmbuf_monitor_track_change(false);
|
||||
|
||||
/* Notify the wps that the track change starts now */
|
||||
audio_post_track_change(false);
|
||||
|
||||
/* Can't do two crossfades at once and, no fade if pcm is off now */
|
||||
if (
|
||||
#ifdef HAVE_CROSSFADE
|
||||
|
@ -559,7 +576,8 @@ void pcmbuf_start_track_change(bool auto_skip)
|
|||
{
|
||||
pcmbuf_play_stop();
|
||||
pcm_play_unlock();
|
||||
return;
|
||||
/* Notify playback that the track change starts now */
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Not enough data, or not crossfading, flush the old data instead */
|
||||
|
@ -584,6 +602,9 @@ void pcmbuf_start_track_change(bool auto_skip)
|
|||
/* Keep trigger outside the play lock or HW FIFO underruns can happen
|
||||
since frequency scaling is *not* always fast */
|
||||
trigger_cpu_boost();
|
||||
|
||||
/* Notify playback that the track change starts now */
|
||||
return true;
|
||||
}
|
||||
else /* automatic and not crossfading, so do gapless track change */
|
||||
{
|
||||
|
@ -593,6 +614,7 @@ void pcmbuf_start_track_change(bool auto_skip)
|
|||
* as the last one in the track. */
|
||||
logf(" gapless track change");
|
||||
pcmbuf_monitor_track_change(true);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -623,7 +645,7 @@ static void pcmbuf_pcm_callback(unsigned char** start, size_t* size)
|
|||
if (pcmbuf_current->end_of_track)
|
||||
{
|
||||
track_transition = false;
|
||||
audio_post_track_change(true);
|
||||
audio_pcmbuf_track_change(true);
|
||||
}
|
||||
|
||||
/* Put the finished chunk back into circulation */
|
||||
|
@ -955,9 +977,6 @@ static void write_to_crossfade(size_t length)
|
|||
return;
|
||||
}
|
||||
|
||||
/* Commit samples to the buffer */
|
||||
while (!prepare_insert(length))
|
||||
sleep(1);
|
||||
while (length > 0)
|
||||
{
|
||||
COMMIT_IF_NEEDED;
|
||||
|
|
|
@ -33,13 +33,21 @@ void pcmbuf_play_start(void);
|
|||
void pcmbuf_play_stop(void);
|
||||
void pcmbuf_pause(bool pause);
|
||||
void pcmbuf_monitor_track_change(bool monitor);
|
||||
void pcmbuf_start_track_change(bool manual_skip);
|
||||
bool pcmbuf_start_track_change(bool manual_skip);
|
||||
|
||||
/* Crossfade */
|
||||
#ifdef HAVE_CROSSFADE
|
||||
bool pcmbuf_is_crossfade_active(void);
|
||||
void pcmbuf_request_crossfade_enable(bool on_off);
|
||||
bool pcmbuf_is_same_size(void);
|
||||
#else
|
||||
/* Dummy functions with sensible returns */
|
||||
static inline bool pcmbuf_is_crossfade_active(void)
|
||||
{ return false; }
|
||||
static inline void pcmbuf_request_crossfade_enable(bool on_off)
|
||||
{ return; (void)on_off; }
|
||||
static inline bool pcmbuf_is_same_size(void)
|
||||
{ return true; }
|
||||
#endif
|
||||
|
||||
/* Voice */
|
||||
|
|
5402
apps/playback.c
5402
apps/playback.c
File diff suppressed because it is too large
Load diff
|
@ -26,6 +26,16 @@
|
|||
#include <stdlib.h>
|
||||
#include "config.h"
|
||||
|
||||
#if CONFIG_CODEC == SWCODEC
|
||||
/* Including the code for fast previews is entirely optional since it
|
||||
does add two more mp3entry's - for certain targets it may be less
|
||||
beneficial such as flash-only storage */
|
||||
#if MEMORYSIZE > 2
|
||||
#define AUDIO_FAST_SKIP_PREVIEW
|
||||
#endif
|
||||
|
||||
#endif /* CONFIG_CODEC == SWCODEC */
|
||||
|
||||
#ifdef HAVE_ALBUMART
|
||||
|
||||
#include "bmp.h"
|
||||
|
@ -67,6 +77,8 @@ long audio_filebufused(void);
|
|||
void audio_pre_ff_rewind(void);
|
||||
void audio_skip(int direction);
|
||||
void audio_hard_stop(void); /* Stops audio from serving playback */
|
||||
|
||||
void audio_set_cuesheet(int enable);
|
||||
#ifdef HAVE_CROSSFADE
|
||||
void audio_set_crossfade(int enable);
|
||||
#endif
|
||||
|
@ -78,11 +90,10 @@ enum
|
|||
};
|
||||
bool audio_restore_playback(int type); /* Restores the audio buffer to handle the requested playback */
|
||||
size_t audio_get_filebuflen(void);
|
||||
void audio_pcmbuf_position_callback(unsigned int time) ICODE_ATTR;
|
||||
void audio_post_track_change(bool pcmbuf);
|
||||
int get_audio_hid(void);
|
||||
void audio_set_prev_elapsed(unsigned long setting);
|
||||
bool audio_buffer_state_trashed(void);
|
||||
|
||||
/* Automatic transition? Only valid to call during the track change events,
|
||||
otherwise the result is undefined. */
|
||||
bool audio_automatic_skip(void);
|
||||
|
||||
/* Define one constant that includes recording related functionality */
|
||||
|
@ -91,35 +102,62 @@ bool audio_automatic_skip(void);
|
|||
#endif
|
||||
|
||||
enum {
|
||||
Q_NULL = 0,
|
||||
Q_NULL = 0, /* reserved */
|
||||
|
||||
/* -> audio */
|
||||
Q_AUDIO_PLAY = 1,
|
||||
Q_AUDIO_STOP,
|
||||
Q_AUDIO_PAUSE,
|
||||
Q_AUDIO_SKIP,
|
||||
Q_AUDIO_PRE_FF_REWIND,
|
||||
Q_AUDIO_FF_REWIND,
|
||||
Q_AUDIO_CHECK_NEW_TRACK,
|
||||
Q_AUDIO_FLUSH,
|
||||
Q_AUDIO_TRACK_CHANGED,
|
||||
Q_AUDIO_SEEK_COMPLETE,
|
||||
Q_AUDIO_DIR_SKIP,
|
||||
Q_AUDIO_POSTINIT,
|
||||
Q_AUDIO_FILL_BUFFER,
|
||||
Q_AUDIO_FINISH_LOAD,
|
||||
Q_CODEC_REQUEST_COMPLETE,
|
||||
Q_CODEC_REQUEST_FAILED,
|
||||
|
||||
/* pcmbuf -> audio */
|
||||
Q_AUDIO_TRACK_CHANGED,
|
||||
|
||||
/* audio -> audio */
|
||||
Q_AUDIO_FILL_BUFFER, /* continue buffering next track */
|
||||
|
||||
/* buffering -> audio */
|
||||
Q_AUDIO_BUFFERING, /* some buffer event */
|
||||
Q_AUDIO_FINISH_LOAD_TRACK, /* metadata is buffered */
|
||||
Q_AUDIO_HANDLE_FINISHED, /* some other type is buffered */
|
||||
|
||||
/* codec -> audio (*) */
|
||||
Q_AUDIO_CODEC_SEEK_COMPLETE,
|
||||
Q_AUDIO_CODEC_COMPLETE,
|
||||
|
||||
/* audio -> codec */
|
||||
Q_CODEC_LOAD,
|
||||
Q_CODEC_LOAD_DISK,
|
||||
Q_CODEC_RUN,
|
||||
Q_CODEC_PAUSE,
|
||||
Q_CODEC_SEEK,
|
||||
Q_CODEC_STOP,
|
||||
Q_CODEC_UNLOAD,
|
||||
|
||||
|
||||
/*- miscellanous -*/
|
||||
#ifdef AUDIO_HAVE_RECORDING
|
||||
Q_AUDIO_LOAD_ENCODER,
|
||||
Q_ENCODER_LOAD_DISK,
|
||||
Q_ENCODER_RECORD,
|
||||
/* -> codec */
|
||||
Q_AUDIO_LOAD_ENCODER, /* load an encoder for recording */
|
||||
#endif
|
||||
|
||||
/* -> codec */
|
||||
Q_CODEC_DO_CALLBACK,
|
||||
Q_CODEC_ACK,
|
||||
|
||||
|
||||
/*- settings -*/
|
||||
|
||||
#ifdef HAVE_DISK_STORAGE
|
||||
/* -> audio */
|
||||
Q_AUDIO_UPDATE_WATERMARK, /* buffering watermark needs updating */
|
||||
#endif
|
||||
/* -> audio */
|
||||
Q_AUDIO_REMAKE_AUDIO_BUFFER, /* buffer needs to be reinitialized */
|
||||
};
|
||||
|
||||
#endif
|
||||
/* (*) If you change these, you must check audio_clear_track_notifications
|
||||
in playback.c for correctness */
|
||||
|
||||
#endif /* _PLAYBACK_H */
|
||||
|
|
|
@ -822,9 +822,6 @@ static int add_track_to_playlist(struct playlist_info* playlist,
|
|||
playlist->amount++;
|
||||
playlist->num_inserted_tracks++;
|
||||
|
||||
/* Update index for resume. */
|
||||
playlist_update_resume_index();
|
||||
|
||||
return insert_position;
|
||||
}
|
||||
|
||||
|
@ -925,9 +922,6 @@ static int remove_track_from_playlist(struct playlist_info* playlist,
|
|||
sync_control(playlist, false);
|
||||
}
|
||||
|
||||
/* Update index for resume. */
|
||||
playlist_update_resume_index();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -987,9 +981,6 @@ static int randomise_playlist(struct playlist_info* playlist,
|
|||
playlist->first_index, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
/* Update index for resume. */
|
||||
playlist_update_resume_index();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1030,9 +1021,6 @@ static int sort_playlist(struct playlist_info* playlist, bool start_current,
|
|||
playlist->first_index, -1, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
/* Update index for resume. */
|
||||
playlist_update_resume_index();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1205,9 +1193,6 @@ static void find_and_set_playlist_index(struct playlist_info* playlist,
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Update index for resume. */
|
||||
playlist_update_resume_index();
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -2486,6 +2471,12 @@ const char* playlist_peek(int steps, char* buf, size_t buf_size)
|
|||
if (index < 0)
|
||||
return NULL;
|
||||
|
||||
#if CONFIG_CODEC == SWCODEC
|
||||
/* Just testing - don't care about the file name */
|
||||
if (!buf || !buf_size)
|
||||
return "";
|
||||
#endif
|
||||
|
||||
control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
|
||||
seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
|
||||
|
||||
|
@ -2632,19 +2623,6 @@ int playlist_get_resume_info(int *resume_index)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* Get current playlist index. */
|
||||
int playlist_get_index(void)
|
||||
{
|
||||
return current_playlist.index;
|
||||
}
|
||||
|
||||
/* Update resume index within playlist_info structure. */
|
||||
void playlist_update_resume_index(void)
|
||||
{
|
||||
struct playlist_info* playlist = ¤t_playlist;
|
||||
playlist->resume_index = playlist->index;
|
||||
}
|
||||
|
||||
/* Update resume info for current playing song. Returns -1 on error. */
|
||||
int playlist_update_resume_info(const struct mp3entry* id3)
|
||||
{
|
||||
|
@ -2652,10 +2630,10 @@ int playlist_update_resume_info(const struct mp3entry* id3)
|
|||
|
||||
if (id3)
|
||||
{
|
||||
if (global_status.resume_index != playlist->resume_index ||
|
||||
if (global_status.resume_index != playlist->index ||
|
||||
global_status.resume_offset != id3->offset)
|
||||
{
|
||||
global_status.resume_index = playlist->resume_index;
|
||||
global_status.resume_index = playlist->index;
|
||||
global_status.resume_offset = id3->offset;
|
||||
status_save();
|
||||
}
|
||||
|
@ -3203,9 +3181,6 @@ int playlist_move(struct playlist_info* playlist, int index, int new_index)
|
|||
queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
|
||||
#endif
|
||||
|
||||
/* Update index for resume. */
|
||||
playlist_update_resume_index();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
@ -90,7 +90,6 @@ struct playlist_info
|
|||
int buffer_end_pos; /* last position where buffer was written */
|
||||
int index; /* index of current playing track */
|
||||
int first_index; /* index of first song in playlist */
|
||||
int resume_index; /* index of playing track to resume */
|
||||
int amount; /* number of tracks in the index */
|
||||
int last_insert_pos; /* last position we inserted a track */
|
||||
int seed; /* shuffle seed */
|
||||
|
@ -132,7 +131,6 @@ const char *playlist_peek(int steps, char* buf, size_t buf_size);
|
|||
int playlist_next(int steps);
|
||||
bool playlist_next_dir(int direction);
|
||||
int playlist_get_resume_info(int *resume_index);
|
||||
int playlist_get_index(void);
|
||||
int playlist_update_resume_info(const struct mp3entry* id3);
|
||||
int playlist_get_display_index(void);
|
||||
int playlist_amount(void);
|
||||
|
@ -176,6 +174,5 @@ int playlist_directory_tracksearch(const char* dirname, bool recurse,
|
|||
int (*callback)(char*, void*),
|
||||
void* context);
|
||||
int playlist_remove_all_tracks(struct playlist_info *playlist);
|
||||
void playlist_update_resume_index(void);
|
||||
|
||||
#endif /* __PLAYLIST_H__ */
|
||||
|
|
|
@ -692,7 +692,7 @@ static const struct plugin_api rockbox_api = {
|
|||
#if CONFIG_CODEC == SWCODEC
|
||||
codec_thread_do_callback,
|
||||
codec_load_file,
|
||||
codec_begin,
|
||||
codec_run_proc,
|
||||
codec_close,
|
||||
get_codec_filename,
|
||||
find_array_ptr,
|
||||
|
|
|
@ -145,12 +145,12 @@ void* plugin_get_buffer(size_t *buffer_size);
|
|||
#define PLUGIN_MAGIC 0x526F634B /* RocK */
|
||||
|
||||
/* increase this every time the api struct changes */
|
||||
#define PLUGIN_API_VERSION 203
|
||||
#define PLUGIN_API_VERSION 204
|
||||
|
||||
/* update this to latest version if a change to the api struct breaks
|
||||
backwards compatibility (and please take the opportunity to sort in any
|
||||
new function which are "waiting" at the end of the function table) */
|
||||
#define PLUGIN_MIN_API_VERSION 203
|
||||
#define PLUGIN_MIN_API_VERSION 204
|
||||
|
||||
/* plugin return codes */
|
||||
/* internal returns start at 0x100 to make exit(1..255) work */
|
||||
|
@ -799,9 +799,9 @@ struct plugin_api {
|
|||
#if CONFIG_CODEC == SWCODEC
|
||||
void (*codec_thread_do_callback)(void (*fn)(void),
|
||||
unsigned int *audio_thread_id);
|
||||
void * (*codec_load_file)(const char* codec, struct codec_api *api);
|
||||
int (*codec_begin)(void *handle);
|
||||
void (*codec_close)(void *handle);
|
||||
int (*codec_load_file)(const char* codec, struct codec_api *api);
|
||||
int (*codec_run_proc)(void);
|
||||
int (*codec_close)(void);
|
||||
const char *(*get_codec_filename)(int cod_spec);
|
||||
void ** (*find_array_ptr)(void **arr, void *ptr);
|
||||
int (*remove_array_ptr)(void **arr, void *ptr);
|
||||
|
|
|
@ -127,7 +127,6 @@ struct test_track_info {
|
|||
};
|
||||
|
||||
static struct test_track_info track;
|
||||
static bool taginfo_ready = true;
|
||||
|
||||
static bool use_dsp;
|
||||
|
||||
|
@ -433,6 +432,7 @@ static void pcmbuf_insert_wav_checksum(const void *ch1, const void *ch2, int cou
|
|||
static void set_elapsed(unsigned long value)
|
||||
{
|
||||
elapsed = value;
|
||||
ci.id3->elapsed = value;
|
||||
}
|
||||
|
||||
|
||||
|
@ -482,6 +482,7 @@ static void* request_buffer(size_t *realsize, size_t reqsize)
|
|||
static void advance_buffer(size_t amount)
|
||||
{
|
||||
ci.curpos += amount;
|
||||
ci.id3->offset = ci.curpos;
|
||||
}
|
||||
|
||||
|
||||
|
@ -499,20 +500,17 @@ static void seek_complete(void)
|
|||
/* Do nothing */
|
||||
}
|
||||
|
||||
/* Request file change from file buffer. Returns true is next
|
||||
track is available and changed. If return value is false,
|
||||
codec should exit immediately with PLUGIN_OK status. */
|
||||
static bool request_next_track(void)
|
||||
/* Codec calls this to know what it should do next. */
|
||||
static enum codec_command_action get_command(intptr_t *param)
|
||||
{
|
||||
/* We are only decoding a single track */
|
||||
return false;
|
||||
rb->yield();
|
||||
return CODEC_ACTION_NULL; /* just continue processing */
|
||||
(void)param;
|
||||
}
|
||||
|
||||
|
||||
static void set_offset(size_t value)
|
||||
{
|
||||
/* ??? */
|
||||
(void)value;
|
||||
ci.id3->offset = value;
|
||||
}
|
||||
|
||||
|
||||
|
@ -546,6 +544,9 @@ static void init_ci(void)
|
|||
{
|
||||
/* --- Our "fake" implementations of the codec API functions. --- */
|
||||
|
||||
ci.dsp = (struct dsp_config *)rb->dsp_configure(NULL, DSP_MYDSP,
|
||||
CODEC_IDX_AUDIO);
|
||||
|
||||
ci.codec_get_buffer = codec_get_buffer;
|
||||
|
||||
if (wavinfo.fd >= 0 || checksum) {
|
||||
|
@ -560,11 +561,9 @@ static void init_ci(void)
|
|||
ci.advance_buffer = advance_buffer;
|
||||
ci.seek_buffer = seek_buffer;
|
||||
ci.seek_complete = seek_complete;
|
||||
ci.request_next_track = request_next_track;
|
||||
ci.set_offset = set_offset;
|
||||
ci.configure = configure;
|
||||
ci.dsp = (struct dsp_config *)rb->dsp_configure(NULL, DSP_MYDSP,
|
||||
CODEC_IDX_AUDIO);
|
||||
ci.get_command = get_command;
|
||||
|
||||
/* --- "Core" functions --- */
|
||||
|
||||
|
@ -620,20 +619,22 @@ static void init_ci(void)
|
|||
static void codec_thread(void)
|
||||
{
|
||||
const char* codecname;
|
||||
void *handle;
|
||||
int res = CODEC_ERROR;
|
||||
int res;
|
||||
|
||||
codecname = rb->get_codec_filename(track.id3.codectype);
|
||||
|
||||
/* Load the codec and start decoding. */
|
||||
handle = rb->codec_load_file(codecname,&ci);
|
||||
/* Load the codec */
|
||||
res = rb->codec_load_file(codecname, &ci);
|
||||
|
||||
if (handle != NULL)
|
||||
if (res >= 0)
|
||||
{
|
||||
res = rb->codec_begin(handle);
|
||||
rb->codec_close(handle);
|
||||
/* Decode the file */
|
||||
res = rb->codec_run_proc();
|
||||
}
|
||||
|
||||
/* Clean up */
|
||||
rb->codec_close();
|
||||
|
||||
/* Signal to the main thread that we are done */
|
||||
endtick = *rb->current_tick - rebuffertick;
|
||||
codec_playing = false;
|
||||
|
@ -705,11 +706,7 @@ static enum plugin_status test_track(const char* filename)
|
|||
/* Prepare the codec struct for playing the whole file */
|
||||
ci.filesize = track.filesize;
|
||||
ci.id3 = &track.id3;
|
||||
ci.taginfo_ready = &taginfo_ready;
|
||||
ci.curpos = 0;
|
||||
ci.stop_codec = false;
|
||||
ci.new_track = 0;
|
||||
ci.seek_time = 0;
|
||||
|
||||
if (use_dsp)
|
||||
rb->dsp_configure(ci.dsp, DSP_RESET, 0);
|
||||
|
|
|
@ -86,6 +86,7 @@
|
|||
#define SYS_VOLUME_CHANGED MAKE_SYS_EVENT(SYS_EVENT_CLS_MISC, 5)
|
||||
|
||||
#define IS_SYSEVENT(ev) ((ev & SYS_EVENT) == SYS_EVENT)
|
||||
#define EVENT_RESERVED (~0)
|
||||
|
||||
#ifndef TIMEOUT_BLOCK
|
||||
#define TIMEOUT_BLOCK -1
|
||||
|
@ -249,6 +250,15 @@ extern bool queue_in_queue_send(struct event_queue *q);
|
|||
#endif /* HAVE_EXTENDED_MESSAGING_AND_NAME */
|
||||
extern bool queue_empty(const struct event_queue* q);
|
||||
extern bool queue_peek(struct event_queue *q, struct queue_event *ev);
|
||||
|
||||
#define QPEEK_FILTER_COUNT_MASK (0xffu) /* 0x00=1 filter, 0xff=256 filters */
|
||||
#define QPEEK_FILTER_HEAD_ONLY (1u << 8) /* Ignored if no filters */
|
||||
#define QPEEK_REMOVE_EVENTS (1u << 9) /* Remove or discard events */
|
||||
extern bool queue_peek_ex(struct event_queue *q,
|
||||
struct queue_event *ev,
|
||||
unsigned int flags,
|
||||
const long (*filters)[2]);
|
||||
|
||||
extern void queue_clear(struct event_queue* q);
|
||||
extern void queue_remove_from_head(struct event_queue *q, long id);
|
||||
extern int queue_count(const struct event_queue *q);
|
||||
|
|
|
@ -516,8 +516,10 @@ void queue_wait(struct event_queue *q, struct queue_event *ev)
|
|||
oldlevel = disable_irq_save();
|
||||
corelock_lock(&q->cl);
|
||||
|
||||
/* auto-reply */
|
||||
#ifdef HAVE_EXTENDED_MESSAGING_AND_NAME
|
||||
/* Auto-reply (even if ev is NULL to avoid stalling a waiting thread) */
|
||||
queue_do_auto_reply(q->send);
|
||||
#endif
|
||||
|
||||
while(1)
|
||||
{
|
||||
|
@ -541,12 +543,18 @@ void queue_wait(struct event_queue *q, struct queue_event *ev)
|
|||
corelock_lock(&q->cl);
|
||||
}
|
||||
|
||||
#ifdef HAVE_EXTENDED_MESSAGING_AND_NAME
|
||||
if(ev)
|
||||
#endif
|
||||
{
|
||||
q->read = rd + 1;
|
||||
rd &= QUEUE_LENGTH_MASK;
|
||||
*ev = q->events[rd];
|
||||
|
||||
/* Get data for a waiting thread if one */
|
||||
queue_do_fetch_sender(q->send, rd);
|
||||
}
|
||||
/* else just waiting on non-empty */
|
||||
|
||||
corelock_unlock(&q->cl);
|
||||
restore_irq(oldlevel);
|
||||
|
@ -566,8 +574,10 @@ void queue_wait_w_tmo(struct event_queue *q, struct queue_event *ev, int ticks)
|
|||
oldlevel = disable_irq_save();
|
||||
corelock_lock(&q->cl);
|
||||
|
||||
/* Auto-reply */
|
||||
#ifdef HAVE_EXTENDED_MESSAGING_AND_NAME
|
||||
/* Auto-reply (even if ev is NULL to avoid stalling a waiting thread) */
|
||||
queue_do_auto_reply(q->send);
|
||||
#endif
|
||||
|
||||
rd = q->read;
|
||||
wr = q->write;
|
||||
|
@ -590,6 +600,10 @@ void queue_wait_w_tmo(struct event_queue *q, struct queue_event *ev, int ticks)
|
|||
wr = q->write;
|
||||
}
|
||||
|
||||
#ifdef HAVE_EXTENDED_MESSAGING_AND_NAME
|
||||
if(ev)
|
||||
#endif
|
||||
{
|
||||
/* no worry about a removed message here - status is checked inside
|
||||
locks - perhaps verify if timeout or false alarm */
|
||||
if (rd != wr)
|
||||
|
@ -604,6 +618,8 @@ void queue_wait_w_tmo(struct event_queue *q, struct queue_event *ev, int ticks)
|
|||
{
|
||||
ev->id = SYS_TIMEOUT;
|
||||
}
|
||||
}
|
||||
/* else just waiting on non-empty */
|
||||
|
||||
corelock_unlock(&q->cl);
|
||||
restore_irq(oldlevel);
|
||||
|
@ -740,6 +756,120 @@ void queue_reply(struct event_queue *q, intptr_t retval)
|
|||
}
|
||||
#endif /* HAVE_EXTENDED_MESSAGING_AND_NAME */
|
||||
|
||||
#ifdef HAVE_EXTENDED_MESSAGING_AND_NAME
|
||||
/* Scan the even queue from head to tail, returning any event from the
|
||||
filter list that was found, optionally removing the event. If an
|
||||
event is returned, synchronous events are handled in the same manner as
|
||||
with queue_wait(_w_tmo); if discarded, then as queue_clear.
|
||||
If filters are NULL, any event matches. If filters exist, the default
|
||||
is to search the full queue depth.
|
||||
Earlier filters take precedence.
|
||||
|
||||
Return true if an event was found, false otherwise. */
|
||||
bool queue_peek_ex(struct event_queue *q, struct queue_event *ev,
|
||||
unsigned int flags, const long (*filters)[2])
|
||||
{
|
||||
bool have_msg;
|
||||
unsigned int rd, wr;
|
||||
int oldlevel;
|
||||
|
||||
if(LIKELY(q->read == q->write))
|
||||
return false; /* Empty: do nothing further */
|
||||
|
||||
have_msg = false;
|
||||
|
||||
oldlevel = disable_irq_save();
|
||||
corelock_lock(&q->cl);
|
||||
|
||||
/* Starting at the head, find first match */
|
||||
for(rd = q->read, wr = q->write; rd != wr; rd++)
|
||||
{
|
||||
struct queue_event *e = &q->events[rd & QUEUE_LENGTH_MASK];
|
||||
|
||||
if(filters)
|
||||
{
|
||||
/* Have filters - find the first thing that passes */
|
||||
const long (* f)[2] = filters;
|
||||
const long (* const f_last)[2] =
|
||||
&filters[flags & QPEEK_FILTER_COUNT_MASK];
|
||||
long id = e->id;
|
||||
|
||||
do
|
||||
{
|
||||
if(UNLIKELY(id >= (*f)[0] && id <= (*f)[1]))
|
||||
goto passed_filter;
|
||||
}
|
||||
while(++f <= f_last);
|
||||
|
||||
if(LIKELY(!(flags & QPEEK_FILTER_HEAD_ONLY)))
|
||||
continue; /* No match; test next event */
|
||||
else
|
||||
break; /* Only check the head */
|
||||
}
|
||||
/* else - anything passes */
|
||||
|
||||
passed_filter:
|
||||
|
||||
/* Found a matching event */
|
||||
have_msg = true;
|
||||
|
||||
if(ev)
|
||||
*ev = *e; /* Caller wants the event */
|
||||
|
||||
if(flags & QPEEK_REMOVE_EVENTS)
|
||||
{
|
||||
/* Do event removal */
|
||||
unsigned int r = q->read;
|
||||
q->read = r + 1; /* Advance head */
|
||||
|
||||
if(ev)
|
||||
{
|
||||
/* Auto-reply */
|
||||
queue_do_auto_reply(q->send);
|
||||
/* Get the thread waiting for reply, if any */
|
||||
queue_do_fetch_sender(q->send, rd & QUEUE_LENGTH_MASK);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Release any thread waiting on this message */
|
||||
queue_do_unblock_sender(q->send, rd & QUEUE_LENGTH_MASK);
|
||||
}
|
||||
|
||||
/* Slide messages forward into the gap if not at the head */
|
||||
while(rd != r)
|
||||
{
|
||||
unsigned int dst = rd & QUEUE_LENGTH_MASK;
|
||||
unsigned int src = --rd & QUEUE_LENGTH_MASK;
|
||||
|
||||
q->events[dst] = q->events[src];
|
||||
/* Keep sender wait list in sync */
|
||||
if(q->send)
|
||||
q->send->senders[dst] = q->send->senders[src];
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
corelock_unlock(&q->cl);
|
||||
restore_irq(oldlevel);
|
||||
|
||||
return have_msg;
|
||||
}
|
||||
|
||||
bool queue_peek(struct event_queue *q, struct queue_event *ev)
|
||||
{
|
||||
return queue_peek_ex(q, ev, 0, NULL);
|
||||
}
|
||||
|
||||
void queue_remove_from_head(struct event_queue *q, long id)
|
||||
{
|
||||
const long f[2] = { id, id };
|
||||
while (queue_peek_ex(q, NULL,
|
||||
QPEEK_FILTER_HEAD_ONLY | QPEEK_REMOVE_EVENTS, &f));
|
||||
}
|
||||
#else /* !HAVE_EXTENDED_MESSAGING_AND_NAME */
|
||||
/* The more powerful routines aren't required */
|
||||
bool queue_peek(struct event_queue *q, struct queue_event *ev)
|
||||
{
|
||||
unsigned int rd;
|
||||
|
@ -765,32 +895,6 @@ bool queue_peek(struct event_queue *q, struct queue_event *ev)
|
|||
return have_msg;
|
||||
}
|
||||
|
||||
/* Poll queue to see if a message exists - careful in using the result if
|
||||
* queue_remove_from_head is called when messages are posted - possibly use
|
||||
* queue_wait_w_tmo(&q, 0) in that case or else a removed message that
|
||||
* unsignals the queue may cause an unwanted block */
|
||||
bool queue_empty(const struct event_queue* q)
|
||||
{
|
||||
return ( q->read == q->write );
|
||||
}
|
||||
|
||||
void queue_clear(struct event_queue* q)
|
||||
{
|
||||
int oldlevel;
|
||||
|
||||
oldlevel = disable_irq_save();
|
||||
corelock_lock(&q->cl);
|
||||
|
||||
/* Release all threads waiting in the queue for a reply -
|
||||
dequeued sent message will be handled by owning thread */
|
||||
queue_release_all_senders(q);
|
||||
|
||||
q->read = q->write;
|
||||
|
||||
corelock_unlock(&q->cl);
|
||||
restore_irq(oldlevel);
|
||||
}
|
||||
|
||||
void queue_remove_from_head(struct event_queue *q, long id)
|
||||
{
|
||||
int oldlevel;
|
||||
|
@ -816,6 +920,33 @@ void queue_remove_from_head(struct event_queue *q, long id)
|
|||
corelock_unlock(&q->cl);
|
||||
restore_irq(oldlevel);
|
||||
}
|
||||
#endif /* HAVE_EXTENDED_MESSAGING_AND_NAME */
|
||||
|
||||
/* Poll queue to see if a message exists - careful in using the result if
|
||||
* queue_remove_from_head is called when messages are posted - possibly use
|
||||
* queue_wait_w_tmo(&q, 0) in that case or else a removed message that
|
||||
* unsignals the queue may cause an unwanted block */
|
||||
bool queue_empty(const struct event_queue* q)
|
||||
{
|
||||
return ( q->read == q->write );
|
||||
}
|
||||
|
||||
void queue_clear(struct event_queue* q)
|
||||
{
|
||||
int oldlevel;
|
||||
|
||||
oldlevel = disable_irq_save();
|
||||
corelock_lock(&q->cl);
|
||||
|
||||
/* Release all threads waiting in the queue for a reply -
|
||||
dequeued sent message will be handled by owning thread */
|
||||
queue_release_all_senders(q);
|
||||
|
||||
q->read = q->write;
|
||||
|
||||
corelock_unlock(&q->cl);
|
||||
restore_irq(oldlevel);
|
||||
}
|
||||
|
||||
/**
|
||||
* The number of events waiting in the queue.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue