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_BUFFER,
|
||||||
PLAYBACK_EVENT_TRACK_FINISH,
|
PLAYBACK_EVENT_TRACK_FINISH,
|
||||||
PLAYBACK_EVENT_TRACK_CHANGE,
|
PLAYBACK_EVENT_TRACK_CHANGE,
|
||||||
|
PLAYBACK_EVENT_TRACK_SKIP,
|
||||||
PLAYBACK_EVENT_NEXTTRACKID3_AVAILABLE,
|
PLAYBACK_EVENT_NEXTTRACKID3_AVAILABLE,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
288
apps/buffering.c
288
apps/buffering.c
|
@ -58,7 +58,7 @@
|
||||||
#define GUARD_BUFSIZE (32*1024)
|
#define GUARD_BUFSIZE (32*1024)
|
||||||
|
|
||||||
/* Define LOGF_ENABLE to enable logf output in this file */
|
/* Define LOGF_ENABLE to enable logf output in this file */
|
||||||
/*#define LOGF_ENABLE*/
|
/* #define LOGF_ENABLE */
|
||||||
#include "logf.h"
|
#include "logf.h"
|
||||||
|
|
||||||
/* macros to enable logf for queues
|
/* macros to enable logf for queues
|
||||||
|
@ -82,8 +82,6 @@
|
||||||
#define LOGFQUEUE_SYS_TIMEOUT(...)
|
#define LOGFQUEUE_SYS_TIMEOUT(...)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* default point to start buffer refill */
|
|
||||||
#define BUFFERING_DEFAULT_WATERMARK (1024*128)
|
|
||||||
/* amount of data to read in one read() call */
|
/* amount of data to read in one read() call */
|
||||||
#define BUFFERING_DEFAULT_FILECHUNK (1024*32)
|
#define BUFFERING_DEFAULT_FILECHUNK (1024*32)
|
||||||
|
|
||||||
|
@ -94,6 +92,8 @@
|
||||||
struct memory_handle {
|
struct memory_handle {
|
||||||
int id; /* A unique ID for the handle */
|
int id; /* A unique ID for the handle */
|
||||||
enum data_type type; /* Type of data buffered with this 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 */
|
char path[MAX_PATH]; /* Path if data originated in a file */
|
||||||
int fd; /* File descriptor to path (-1 if closed) */
|
int fd; /* File descriptor to path (-1 if closed) */
|
||||||
size_t data; /* Start index of the handle's data buffer */
|
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 */
|
/* Configuration */
|
||||||
static size_t conf_watermark = 0; /* Level to trigger filebuf fill */
|
static size_t conf_watermark = 0; /* Level to trigger filebuf fill */
|
||||||
#if MEMORYSIZE > 8
|
|
||||||
static size_t high_watermark = 0; /* High watermark for rebuffer */
|
static size_t high_watermark = 0; /* High watermark for rebuffer */
|
||||||
#endif
|
|
||||||
|
|
||||||
/* current memory handle in the linked list. NULL when the list is empty. */
|
/* current memory handle in the linked list. NULL when the list is empty. */
|
||||||
static struct memory_handle *cur_handle;
|
static struct memory_handle *cur_handle;
|
||||||
|
@ -162,7 +160,6 @@ enum
|
||||||
Q_REBUFFER_HANDLE, /* Request reset and rebuffering of a handle at a new
|
Q_REBUFFER_HANDLE, /* Request reset and rebuffering of a handle at a new
|
||||||
file starting position. */
|
file starting position. */
|
||||||
Q_CLOSE_HANDLE, /* Request closing a handle */
|
Q_CLOSE_HANDLE, /* Request closing a handle */
|
||||||
Q_BASE_HANDLE, /* Set the reference handle for buf_useful_data */
|
|
||||||
|
|
||||||
/* Configuration: */
|
/* Configuration: */
|
||||||
Q_START_FILL, /* Request that the buffering thread initiate a buffer
|
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 */
|
/* Bytes available in the buffer */
|
||||||
#define BUF_USED ringbuf_sub(buf_widx, buf_ridx)
|
#define BUF_USED ringbuf_sub(buf_widx, buf_ridx)
|
||||||
|
|
||||||
|
/* Real buffer watermark */
|
||||||
|
#define BUF_WATERMARK MIN(conf_watermark, high_watermark)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
LINKED LIST MANAGEMENT
|
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 */
|
/* Prevent buffering thread from looking at it */
|
||||||
new_handle->filerem = 0;
|
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 */
|
/* only advance the buffer write index of the size of the struct */
|
||||||
buf_widx = ringbuf_add(buf_widx, sizeof(struct memory_handle));
|
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;
|
buf_widx = cur_handle->widx;
|
||||||
}
|
}
|
||||||
} else {
|
} 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;
|
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
|
/* simple caching because most of the time the requested handle
|
||||||
will either be the same as the last, or the one after the last */
|
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) {
|
if (cached_handle->id == handle_id) {
|
||||||
return cached_handle;
|
return cached_handle;
|
||||||
} else if (cached_handle->next &&
|
} 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)
|
static inline bool buffer_is_low(void)
|
||||||
{
|
{
|
||||||
update_data_counters(NULL);
|
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.
|
/* Q_BUFFER_HANDLE event and buffer data for the given handle.
|
||||||
Return whether or not the buffering should continue explicitly. */
|
Return whether or not the buffering should continue explicitly. */
|
||||||
static bool buffer_handle(int handle_id, size_t to_buffer)
|
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);
|
struct memory_handle *h = find_handle(handle_id);
|
||||||
bool stop = false;
|
bool stop = false;
|
||||||
|
|
||||||
if (!h)
|
if (!h)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
logf(" type: %d", (int)h->type);
|
||||||
|
|
||||||
if (h->filerem == 0) {
|
if (h->filerem == 0) {
|
||||||
/* nothing left to buffer */
|
/* nothing left to buffer */
|
||||||
return true;
|
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),
|
if (!get_metadata((struct mp3entry *)(buffer + h->data),
|
||||||
h->fd, h->path)) {
|
h->fd, h->path)) {
|
||||||
/* metadata parsing failed: clear the buffer. */
|
/* metadata parsing failed: clear the buffer. */
|
||||||
memset(buffer + h->data, 0, sizeof(struct mp3entry));
|
wipe_mp3entry((struct mp3entry *)(buffer + h->data));
|
||||||
}
|
}
|
||||||
close(h->fd);
|
close(h->fd);
|
||||||
h->fd = -1;
|
h->fd = -1;
|
||||||
h->filerem = 0;
|
h->filerem = 0;
|
||||||
h->available = sizeof(struct mp3entry);
|
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);
|
send_event(BUFFER_EVENT_FINISHED, &handle_id);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -698,7 +708,7 @@ static bool buffer_handle(int handle_id, size_t to_buffer)
|
||||||
break;
|
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->filesize -= h->filerem;
|
||||||
h->filerem = 0;
|
h->filerem = 0;
|
||||||
break;
|
break;
|
||||||
|
@ -770,22 +780,31 @@ static bool close_handle(int handle_id)
|
||||||
part of its data buffer or by moving all the data. */
|
part of its data buffer or by moving all the data. */
|
||||||
static void shrink_handle(struct memory_handle *h)
|
static void shrink_handle(struct memory_handle *h)
|
||||||
{
|
{
|
||||||
size_t delta;
|
|
||||||
|
|
||||||
if (!h)
|
if (!h)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (h->type == TYPE_ID3 || h->type == TYPE_CUESHEET ||
|
if (h->type == TYPE_PACKET_AUDIO) {
|
||||||
h->type == TYPE_BITMAP || h->type == TYPE_CODEC ||
|
/* only move the handle struct */
|
||||||
h->type == TYPE_ATOMIC_AUDIO)
|
/* 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 */
|
/* metadata handle: we can move all of it */
|
||||||
if (!h->next || h->filerem != 0)
|
if (h->pinned || !h->next || h->filerem != 0)
|
||||||
return; /* Last handle or not finished loading */
|
return; /* Pinned, last handle or not finished loading */
|
||||||
|
|
||||||
uintptr_t handle_distance =
|
uintptr_t handle_distance =
|
||||||
ringbuf_sub(ringbuf_offset(h->next), h->data);
|
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 */
|
/* The value of delta might change for alignment reasons */
|
||||||
if (!move_handle(&h, &delta, h->available, h->type==TYPE_CODEC))
|
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];
|
struct bitmap *bmp = (struct bitmap *)&buffer[h->data];
|
||||||
bmp->data = &buffer[h->data + sizeof(struct bitmap)];
|
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);
|
mutex_unlock(&llist_mutex);
|
||||||
return handle_id;
|
return handle_id;
|
||||||
}
|
}
|
||||||
|
else if (type == TYPE_UNKNOWN)
|
||||||
|
return ERR_UNSUPPORTED_TYPE;
|
||||||
#ifdef APPLICATION
|
#ifdef APPLICATION
|
||||||
/* loading code from memory is not supported in application builds */
|
/* loading code from memory is not supported in application builds */
|
||||||
else if (type == TYPE_CODEC)
|
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 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);
|
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)
|
bool bufclose(int handle_id)
|
||||||
{
|
{
|
||||||
logf("bufclose(%d)", 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);
|
LOGFQUEUE("buffering >| Q_CLOSE_HANDLE %d", handle_id);
|
||||||
return queue_send(&buffering_queue, Q_CLOSE_HANDLE, 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).
|
/* Set reading index in handle (relatively to the start of the file).
|
||||||
Access before the available data will trigger a rebuffer.
|
Access before the available data will trigger a rebuffer.
|
||||||
Return 0 for success and < 0 for failure:
|
Return 0 for success and for failure:
|
||||||
-1 if the handle wasn't found
|
ERR_HANDLE_NOT_FOUND if the handle wasn't found
|
||||||
-2 if the new requested position was beyond the end of the file
|
ERR_INVALID_VALUE if the new requested position was beyond the end of
|
||||||
|
the file
|
||||||
*/
|
*/
|
||||||
int bufseek(int handle_id, size_t newpos)
|
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).
|
/* 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)
|
int bufadvance(int handle_id, off_t offset)
|
||||||
{
|
{
|
||||||
struct memory_handle *h = find_handle(handle_id);
|
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);
|
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
|
/* Used by bufread and bufgetdata to prepare the buffer and retrieve the
|
||||||
* actual amount of data available for reading. This function explicitly
|
* actual amount of data available for reading. This function explicitly
|
||||||
* does not check the validity of the input handle. It does do range checks
|
* 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
|
/* it is not safe for a non-buffering thread to sleep while
|
||||||
* holding a handle */
|
* holding a handle */
|
||||||
h = find_handle(handle_id);
|
h = find_handle(handle_id);
|
||||||
if (!h)
|
if (!h || h->signaled != 0)
|
||||||
return NULL;
|
return NULL;
|
||||||
avail = handle_size_available(h);
|
avail = handle_size_available(h);
|
||||||
}
|
}
|
||||||
|
@ -1447,9 +1488,14 @@ SECONDARY EXPORTED FUNCTIONS
|
||||||
buf_handle_offset
|
buf_handle_offset
|
||||||
buf_request_buffer_handle
|
buf_request_buffer_handle
|
||||||
buf_set_base_handle
|
buf_set_base_handle
|
||||||
|
buf_handle_data_type
|
||||||
|
buf_is_handle
|
||||||
|
buf_pin_handle
|
||||||
|
buf_signal_handle
|
||||||
|
buf_length
|
||||||
buf_used
|
buf_used
|
||||||
register_buffering_callback
|
buf_set_watermark
|
||||||
unregister_buffering_callback
|
buf_get_watermark
|
||||||
|
|
||||||
These functions are exported, to allow interaction with the buffer.
|
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
|
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)
|
void buf_set_base_handle(int handle_id)
|
||||||
{
|
{
|
||||||
LOGFQUEUE("buffering > Q_BASE_HANDLE %d", handle_id);
|
mutex_lock(&llist_mutex);
|
||||||
queue_post(&buffering_queue, Q_BASE_HANDLE, handle_id);
|
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 */
|
/* Return the amount of buffer space used */
|
||||||
|
@ -1487,6 +1586,21 @@ void buf_set_watermark(size_t bytes)
|
||||||
conf_watermark = 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)
|
static void shrink_buffer_inner(struct memory_handle *h)
|
||||||
{
|
{
|
||||||
if (h == NULL)
|
if (h == NULL)
|
||||||
|
@ -1503,7 +1617,7 @@ static void shrink_buffer(void)
|
||||||
shrink_buffer_inner(first_handle);
|
shrink_buffer_inner(first_handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
void buffering_thread(void)
|
static void NORETURN_ATTR buffering_thread(void)
|
||||||
{
|
{
|
||||||
bool filling = false;
|
bool filling = false;
|
||||||
struct queue_event ev;
|
struct queue_event ev;
|
||||||
|
@ -1511,19 +1625,21 @@ void buffering_thread(void)
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
if (!filling) {
|
if (num_handles > 0) {
|
||||||
|
if (!filling) {
|
||||||
|
cancel_cpu_boost();
|
||||||
|
}
|
||||||
|
queue_wait_w_tmo(&buffering_queue, &ev, filling ? 1 : HZ/2);
|
||||||
|
} else {
|
||||||
|
filling = false;
|
||||||
cancel_cpu_boost();
|
cancel_cpu_boost();
|
||||||
|
queue_wait(&buffering_queue, &ev);
|
||||||
}
|
}
|
||||||
|
|
||||||
queue_wait_w_tmo(&buffering_queue, &ev, filling ? 5 : HZ/2);
|
|
||||||
|
|
||||||
switch (ev.id)
|
switch (ev.id)
|
||||||
{
|
{
|
||||||
case Q_START_FILL:
|
case Q_START_FILL:
|
||||||
LOGFQUEUE("buffering < Q_START_FILL %d", (int)ev.data);
|
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();
|
shrink_buffer();
|
||||||
queue_reply(&buffering_queue, 1);
|
queue_reply(&buffering_queue, 1);
|
||||||
filling |= buffer_handle((int)ev.data, 0);
|
filling |= buffer_handle((int)ev.data, 0);
|
||||||
|
@ -1553,36 +1669,21 @@ void buffering_thread(void)
|
||||||
filling = true;
|
filling = true;
|
||||||
break;
|
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:
|
case SYS_TIMEOUT:
|
||||||
LOGFQUEUE_SYS_TIMEOUT("buffering < SYS_TIMEOUT");
|
LOGFQUEUE_SYS_TIMEOUT("buffering < SYS_TIMEOUT");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (num_handles == 0 || !queue_empty(&buffering_queue))
|
||||||
|
continue;
|
||||||
|
|
||||||
update_data_counters(NULL);
|
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
|
#if 0
|
||||||
/* TODO: This needs to be fixed to use the idle callback, disable it
|
/* TODO: This needs to be fixed to use the idle callback, disable it
|
||||||
* for simplicity until its done right */
|
* for simplicity until its done right */
|
||||||
#if MEMORYSIZE > 8
|
#if MEMORYSIZE > 8
|
||||||
/* If the disk is spinning, take advantage by filling the buffer */
|
/* 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)
|
if (num_handles > 0 && data_counters.useful <= high_watermark)
|
||||||
send_event(BUFFER_EVENT_BUFFER_LOW, 0);
|
send_event(BUFFER_EVENT_BUFFER_LOW, 0);
|
||||||
|
|
||||||
|
@ -1597,15 +1698,23 @@ void buffering_thread(void)
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (queue_empty(&buffering_queue)) {
|
if (filling) {
|
||||||
if (filling) {
|
if (data_counters.remaining > 0 && BUF_USED < buffer_len) {
|
||||||
if (data_counters.remaining > 0 && BUF_USED < buffer_len)
|
filling = fill_buffer();
|
||||||
filling = fill_buffer();
|
}
|
||||||
else if (data_counters.remaining == 0)
|
else if (data_counters.remaining == 0) {
|
||||||
filling = false;
|
filling = false;
|
||||||
} else if (ev.id == SYS_TIMEOUT) {
|
}
|
||||||
if (data_counters.remaining > 0 &&
|
} else if (ev.id == SYS_TIMEOUT) {
|
||||||
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();
|
shrink_buffer();
|
||||||
filling = fill_buffer();
|
filling = fill_buffer();
|
||||||
}
|
}
|
||||||
|
@ -1618,9 +1727,14 @@ void buffering_init(void)
|
||||||
{
|
{
|
||||||
mutex_init(&llist_mutex);
|
mutex_init(&llist_mutex);
|
||||||
|
|
||||||
conf_watermark = BUFFERING_DEFAULT_WATERMARK;
|
/* Thread should absolutely not respond to USB because if it waits first,
|
||||||
|
then it cannot properly service the handles and leaks will happen -
|
||||||
queue_init(&buffering_queue, true);
|
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,
|
buffering_thread_id = create_thread( buffering_thread, buffering_stack,
|
||||||
sizeof(buffering_stack), CREATE_THREAD_FROZEN,
|
sizeof(buffering_stack), CREATE_THREAD_FROZEN,
|
||||||
buffering_thread_name IF_PRIO(, PRIORITY_BUFFERING)
|
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,
|
/* Wraps of storage-aligned data must also be storage aligned,
|
||||||
thus buf and buflen must be a aligned to an integer multiple of
|
thus buf and buflen must be a aligned to an integer multiple of
|
||||||
the storage alignment */
|
the storage alignment */
|
||||||
|
|
||||||
|
buflen -= GUARD_BUFSIZE;
|
||||||
|
|
||||||
STORAGE_ALIGN_BUFFER(buf, buflen);
|
STORAGE_ALIGN_BUFFER(buf, buflen);
|
||||||
|
|
||||||
if (!buf || !buflen)
|
if (!buf || !buflen)
|
||||||
|
@ -1654,10 +1771,13 @@ bool buffering_reset(char *buf, size_t buflen)
|
||||||
num_handles = 0;
|
num_handles = 0;
|
||||||
base_handle_id = -1;
|
base_handle_id = -1;
|
||||||
|
|
||||||
/* Set the high watermark as 75% full...or 25% empty :) */
|
/* Set the high watermark as 75% full...or 25% empty :)
|
||||||
#if MEMORYSIZE > 8
|
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;
|
high_watermark = 3*buflen / 4;
|
||||||
#endif
|
|
||||||
|
|
||||||
thread_thaw(buffering_thread_id);
|
thread_thaw(buffering_thread_id);
|
||||||
|
|
||||||
|
@ -1673,5 +1793,5 @@ void buffering_get_debugdata(struct buffering_debug *dbgdata)
|
||||||
dbgdata->wasted_space = dc.wasted;
|
dbgdata->wasted_space = dc.wasted;
|
||||||
dbgdata->buffered_data = dc.buffered;
|
dbgdata->buffered_data = dc.buffered;
|
||||||
dbgdata->useful_data = dc.useful;
|
dbgdata->useful_data = dc.useful;
|
||||||
dbgdata->watermark = conf_watermark;
|
dbgdata->watermark = BUF_WATERMARK;
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,14 +28,13 @@
|
||||||
|
|
||||||
|
|
||||||
enum data_type {
|
enum data_type {
|
||||||
|
TYPE_UNKNOWN = 0, /* invalid type indicator */
|
||||||
|
TYPE_ID3,
|
||||||
TYPE_CODEC,
|
TYPE_CODEC,
|
||||||
TYPE_PACKET_AUDIO,
|
TYPE_PACKET_AUDIO,
|
||||||
TYPE_ATOMIC_AUDIO,
|
TYPE_ATOMIC_AUDIO,
|
||||||
TYPE_ID3,
|
|
||||||
TYPE_CUESHEET,
|
TYPE_CUESHEET,
|
||||||
TYPE_BITMAP,
|
TYPE_BITMAP,
|
||||||
TYPE_BUFFER,
|
|
||||||
TYPE_UNKNOWN,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Error return values */
|
/* Error return values */
|
||||||
|
@ -63,6 +62,7 @@ bool buffering_reset(char *buf, size_t buflen);
|
||||||
* bufclose : Close an open handle
|
* bufclose : Close an open handle
|
||||||
* bufseek : Set handle reading index, relatively to the start of the file
|
* bufseek : Set handle reading index, relatively to the start of the file
|
||||||
* bufadvance: Move handle reading index, relatively to current position
|
* 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
|
* bufread : Copy data from a handle to a buffer
|
||||||
* bufgetdata: Obtain a pointer for linear access to a "size" amount of data
|
* 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.
|
* 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);
|
bool bufclose(int handle_id);
|
||||||
int bufseek(int handle_id, size_t newpos);
|
int bufseek(int handle_id, size_t newpos);
|
||||||
int bufadvance(int handle_id, off_t offset);
|
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 bufread(int handle_id, size_t size, void *dest);
|
||||||
ssize_t bufgetdata(int handle_id, size_t size, void **data);
|
ssize_t bufgetdata(int handle_id, size_t size, void **data);
|
||||||
ssize_t bufgettail(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);
|
ssize_t bufcuttail(int handle_id, size_t size);
|
||||||
|
|
||||||
|
|
||||||
/***************************************************************************
|
/***************************************************************************
|
||||||
* SECONDARY FUNCTIONS
|
* 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_handle_offset: Get the offset of the first buffered byte from the file
|
||||||
* buf_request_buffer_handle: Request buffering of a handle
|
* buf_request_buffer_handle: Request buffering of a handle
|
||||||
* buf_set_base_handle: Tell the buffering thread which handle is currently read
|
* 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_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);
|
ssize_t buf_handle_offset(int handle_id);
|
||||||
void buf_request_buffer_handle(int handle_id);
|
void buf_request_buffer_handle(int handle_id);
|
||||||
void buf_set_base_handle(int handle_id);
|
void buf_set_base_handle(int handle_id);
|
||||||
|
size_t buf_length(void);
|
||||||
size_t buf_used(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 */
|
/* Settings */
|
||||||
enum {
|
enum {
|
||||||
|
@ -110,6 +122,7 @@ enum {
|
||||||
BUFFERING_SET_CHUNKSIZE,
|
BUFFERING_SET_CHUNKSIZE,
|
||||||
};
|
};
|
||||||
void buf_set_watermark(size_t bytes);
|
void buf_set_watermark(size_t bytes);
|
||||||
|
size_t buf_get_watermark(void);
|
||||||
|
|
||||||
/* Debugging */
|
/* Debugging */
|
||||||
struct buffering_debug {
|
struct buffering_debug {
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
*
|
*
|
||||||
* Copyright (C) 2005-2007 Miika Pekkarinen
|
* Copyright (C) 2005-2007 Miika Pekkarinen
|
||||||
* Copyright (C) 2007-2008 Nicolas Pennequin
|
* Copyright (C) 2007-2008 Nicolas Pennequin
|
||||||
|
* Copyright (C) 2011 Michael Sevakis
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
|
@ -21,16 +22,14 @@
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "system.h"
|
#include "system.h"
|
||||||
#include "playback.h"
|
|
||||||
#include "codec_thread.h"
|
|
||||||
#include "kernel.h"
|
#include "kernel.h"
|
||||||
#include "codecs.h"
|
#include "codecs.h"
|
||||||
#include "buffering.h"
|
#include "codec_thread.h"
|
||||||
#include "pcmbuf.h"
|
#include "pcmbuf.h"
|
||||||
|
#include "playback.h"
|
||||||
|
#include "buffering.h"
|
||||||
#include "dsp.h"
|
#include "dsp.h"
|
||||||
#include "abrepeat.h"
|
|
||||||
#include "metadata.h"
|
#include "metadata.h"
|
||||||
#include "splash.h"
|
|
||||||
|
|
||||||
/* Define LOGF_ENABLE to enable logf output in this file */
|
/* Define LOGF_ENABLE to enable logf output in this file */
|
||||||
/*#define LOGF_ENABLE*/
|
/*#define LOGF_ENABLE*/
|
||||||
|
@ -57,38 +56,45 @@
|
||||||
#define LOGFQUEUE_SYS_TIMEOUT(...)
|
#define LOGFQUEUE_SYS_TIMEOUT(...)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
/* Variables are commented with the threads that use them:
|
/* Variables are commented with the threads that use them:
|
||||||
* A=audio, C=codec, V=voice. A suffix of - indicates that
|
* A=audio, C=codec
|
||||||
* the variable is read but not updated on that thread.
|
* - = reads only
|
||||||
|
*
|
||||||
* Unless otherwise noted, the extern variables are located
|
* Unless otherwise noted, the extern variables are located
|
||||||
* in playback.c.
|
* in playback.c.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* Main state control */
|
/* Q_LOAD_CODEC parameter data */
|
||||||
|
struct codec_load_info
|
||||||
/* Type of codec loaded? (C/A) */
|
{
|
||||||
static int current_codectype SHAREDBSS_ATTR = AFMT_UNKNOWN;
|
int hid; /* audio handle id (specify < 0 to use afmt) */
|
||||||
|
int afmt; /* codec specification (AFMT_*) */
|
||||||
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;
|
|
||||||
|
|
||||||
|
|
||||||
|
/** --- 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 */
|
extern struct codec_api ci; /* from codecs.c */
|
||||||
|
|
||||||
/* Codec thread */
|
/* Codec thread */
|
||||||
static unsigned int codec_thread_id; /* For modifying thread priority later */
|
static unsigned int codec_thread_id; /* For modifying thread priority later */
|
||||||
static struct event_queue codec_queue SHAREDBSS_ATTR;
|
static struct event_queue codec_queue SHAREDBSS_ATTR;
|
||||||
static struct queue_sender_list codec_queue_sender_list SHAREDBSS_ATTR;
|
static struct queue_sender_list codec_queue_sender_list SHAREDBSS_ATTR;
|
||||||
static long codec_stack[(DEFAULT_STACK_SIZE + 0x2000)/sizeof(long)]
|
static long codec_stack[(DEFAULT_STACK_SIZE + 0x2000)/sizeof(long)] IBSS_ATTR;
|
||||||
IBSS_ATTR;
|
|
||||||
static const char codec_thread_name[] = "codec";
|
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 routines */
|
||||||
static void codec_queue_ack(intptr_t ackme)
|
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);
|
return queue_send(&codec_queue, id, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**************************************/
|
/* 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
|
||||||
/** misc external functions */
|
scrubbed), 0 if message was ignored. */
|
||||||
|
static int codec_check_queue__have_msg(void)
|
||||||
/* Used to check whether a new codec must be loaded. See array audio_formats[]
|
|
||||||
* in metadata.c */
|
|
||||||
int get_codec_base_type(int type)
|
|
||||||
{
|
{
|
||||||
int base_type = type;
|
struct queue_event ev;
|
||||||
switch (type) {
|
|
||||||
case AFMT_MPA_L1:
|
queue_peek(&codec_queue, &ev);
|
||||||
case AFMT_MPA_L2:
|
|
||||||
case AFMT_MPA_L3:
|
/* Seek, pause or stop? Just peek and return if so. Codec
|
||||||
base_type = AFMT_MPA_L3;
|
must handle the command after returing. Inserts will not
|
||||||
break;
|
be allowed until it complies. */
|
||||||
case AFMT_MPC_SV7:
|
switch (ev.id)
|
||||||
case AFMT_MPC_SV8:
|
{
|
||||||
base_type = AFMT_MPC_SV7;
|
case Q_CODEC_SEEK:
|
||||||
break;
|
LOGFQUEUE("codec - Q_CODEC_SEEK", ev.id);
|
||||||
case AFMT_MP4_AAC:
|
return -1;
|
||||||
case AFMT_MP4_AAC_HE:
|
case Q_CODEC_PAUSE:
|
||||||
base_type = AFMT_MP4_AAC;
|
LOGFQUEUE("codec - Q_CODEC_PAUSE", ev.id);
|
||||||
break;
|
return -1;
|
||||||
case AFMT_SAP:
|
case Q_CODEC_STOP:
|
||||||
case AFMT_CMC:
|
LOGFQUEUE("codec - Q_CODEC_STOP", ev.id);
|
||||||
case AFMT_CM3:
|
return -1;
|
||||||
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;
|
/* 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;
|
const char *fname;
|
||||||
|
|
||||||
|
@ -173,7 +190,7 @@ const char *get_codec_filename(int cod_spec)
|
||||||
#endif /* HAVE_RECORDING */
|
#endif /* HAVE_RECORDING */
|
||||||
|
|
||||||
return fname;
|
return fname;
|
||||||
} /* get_codec_filename */
|
}
|
||||||
|
|
||||||
/* Borrow the codec thread and return the ID */
|
/* Borrow the codec thread and return the ID */
|
||||||
void codec_thread_do_callback(void (*fn)(void), unsigned int *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;
|
ssize_t s = CODEC_SIZE - codec_size;
|
||||||
void *buf = &codecbuf[codec_size];
|
void *buf = &codecbuf[codec_size];
|
||||||
|
@ -215,15 +232,19 @@ static void codec_pcmbuf_insert_callback(
|
||||||
int inp_count;
|
int inp_count;
|
||||||
char *dest;
|
char *dest;
|
||||||
|
|
||||||
/* Prevent audio from a previous track from playing */
|
while (1)
|
||||||
if (ci.new_track || ci.stop_codec)
|
|
||||||
return;
|
|
||||||
|
|
||||||
while ((dest = pcmbuf_request_buffer(&out_count)) == NULL)
|
|
||||||
{
|
{
|
||||||
|
if ((dest = pcmbuf_request_buffer(&out_count)) != NULL)
|
||||||
|
break;
|
||||||
|
|
||||||
cancel_cpu_boost();
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -247,62 +268,28 @@ static void codec_pcmbuf_insert_callback(
|
||||||
|
|
||||||
count -= inp_count;
|
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 */
|
/* 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;
|
ci.curpos += amount;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* copy up-to size bytes into ptr and return the actual size copied */
|
/* copy up-to size bytes into ptr and return the actual size copied */
|
||||||
static size_t codec_filebuf_callback(void *ptr, size_t size)
|
static size_t codec_filebuf_callback(void *ptr, size_t size)
|
||||||
{
|
{
|
||||||
ssize_t copy_n;
|
ssize_t copy_n = bufread(ci.audio_hid, size, ptr);
|
||||||
|
|
||||||
if (ci.stop_codec)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
copy_n = bufread(get_audio_hid(), size, ptr);
|
|
||||||
|
|
||||||
/* Nothing requested OR nothing left */
|
/* Nothing requested OR nothing left */
|
||||||
if (copy_n == 0)
|
if (copy_n <= 0)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/* Update read and other position pointers */
|
/* 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 the actual amount of data copied to the buffer */
|
||||||
return copy_n;
|
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;
|
size_t copy_n = reqsize;
|
||||||
ssize_t ret;
|
ssize_t ret;
|
||||||
void *ptr;
|
void *ptr;
|
||||||
|
|
||||||
ret = bufgetdata(get_audio_hid(), reqsize, &ptr);
|
ret = bufgetdata(ci.audio_hid, reqsize, &ptr);
|
||||||
if (ret >= 0)
|
if (ret >= 0)
|
||||||
copy_n = MIN((size_t)ret, reqsize);
|
copy_n = MIN((size_t)ret, reqsize);
|
||||||
else
|
else
|
||||||
|
@ -329,101 +316,103 @@ static void* codec_request_buffer_callback(size_t *realsize, size_t reqsize)
|
||||||
|
|
||||||
*realsize = copy_n;
|
*realsize = copy_n;
|
||||||
return ptr;
|
return ptr;
|
||||||
} /* codec_request_buffer_callback */
|
}
|
||||||
|
|
||||||
static void codec_advance_buffer_callback(size_t amount)
|
static void codec_advance_buffer_callback(size_t amount)
|
||||||
{
|
{
|
||||||
codec_advance_buffer_counters(amount);
|
if (!codec_advance_buffer_counters(amount))
|
||||||
codec_set_offset_callback(ci.curpos);
|
return;
|
||||||
|
|
||||||
|
audio_codec_update_offset(ci.curpos);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool codec_seek_buffer_callback(size_t newpos)
|
static bool codec_seek_buffer_callback(size_t newpos)
|
||||||
{
|
{
|
||||||
logf("codec_seek_buffer_callback");
|
logf("codec_seek_buffer_callback");
|
||||||
|
|
||||||
int ret = bufseek(get_audio_hid(), newpos);
|
int ret = bufseek(ci.audio_hid, newpos);
|
||||||
if (ret == 0) {
|
if (ret == 0)
|
||||||
|
{
|
||||||
ci.curpos = newpos;
|
ci.curpos = newpos;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void codec_seek_complete_callback(void)
|
static void codec_seek_complete_callback(void)
|
||||||
{
|
{
|
||||||
struct queue_event ev;
|
|
||||||
|
|
||||||
logf("seek_complete");
|
logf("seek_complete");
|
||||||
|
|
||||||
/* Clear DSP */
|
/* Clear DSP */
|
||||||
dsp_configure(ci.dsp, DSP_FLUSH, 0);
|
dsp_configure(ci.dsp, DSP_FLUSH, 0);
|
||||||
|
|
||||||
/* Post notification to audio thread */
|
/* Post notification to audio thread */
|
||||||
LOGFQUEUE("audio > Q_AUDIO_SEEK_COMPLETE");
|
LOGFQUEUE("audio > Q_AUDIO_CODEC_SEEK_COMPLETE");
|
||||||
queue_post(&audio_queue, Q_AUDIO_SEEK_COMPLETE, 0);
|
audio_queue_post(Q_AUDIO_CODEC_SEEK_COMPLETE, 0);
|
||||||
|
|
||||||
/* Wait for ACK */
|
/* Wait for urgent or go message */
|
||||||
queue_wait(&codec_queue, &ev);
|
do
|
||||||
|
|
||||||
/* 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)
|
|
||||||
{
|
{
|
||||||
/* Handle ACK in outer loop */
|
queue_wait(&codec_queue, NULL);
|
||||||
LOGFQUEUE("codec: already stopping");
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
while (codec_check_queue__have_msg() == 0);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void codec_configure_callback(int setting, intptr_t value)
|
static void codec_configure_callback(int setting, intptr_t value)
|
||||||
{
|
{
|
||||||
if (!dsp_configure(ci.dsp, setting, 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 */
|
/* Initialize codec API */
|
||||||
|
@ -433,119 +422,215 @@ void codec_init_codec_api(void)
|
||||||
CODEC_IDX_AUDIO);
|
CODEC_IDX_AUDIO);
|
||||||
ci.codec_get_buffer = codec_get_buffer;
|
ci.codec_get_buffer = codec_get_buffer;
|
||||||
ci.pcmbuf_insert = codec_pcmbuf_insert_callback;
|
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.read_filebuf = codec_filebuf_callback;
|
||||||
ci.request_buffer = codec_request_buffer_callback;
|
ci.request_buffer = codec_request_buffer_callback;
|
||||||
ci.advance_buffer = codec_advance_buffer_callback;
|
ci.advance_buffer = codec_advance_buffer_callback;
|
||||||
ci.seek_buffer = codec_seek_buffer_callback;
|
ci.seek_buffer = codec_seek_buffer_callback;
|
||||||
ci.seek_complete = codec_seek_complete_callback;
|
ci.seek_complete = codec_seek_complete_callback;
|
||||||
ci.request_next_track = codec_request_next_track_callback;
|
ci.set_offset = audio_codec_update_offset;
|
||||||
ci.set_offset = codec_set_offset_callback;
|
|
||||||
ci.configure = codec_configure_callback;
|
ci.configure = codec_configure_callback;
|
||||||
|
ci.get_command = codec_get_command_callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* track change */
|
/** --- CODEC THREAD --- **/
|
||||||
|
|
||||||
/** CODEC THREAD */
|
/* Handle Q_CODEC_LOAD */
|
||||||
static void codec_thread(void)
|
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;
|
struct queue_event ev;
|
||||||
|
|
||||||
|
|
||||||
while (1)
|
while (1)
|
||||||
{
|
{
|
||||||
int status = CODEC_OK;
|
cancel_cpu_boost();
|
||||||
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);
|
queue_wait(&codec_queue, &ev);
|
||||||
|
|
||||||
switch (ev.id)
|
switch (ev.id)
|
||||||
{
|
{
|
||||||
case Q_CODEC_LOAD_DISK:
|
case Q_CODEC_LOAD:
|
||||||
LOGFQUEUE("codec < Q_CODEC_LOAD_DISK");
|
LOGFQUEUE("codec < Q_CODEC_LOAD");
|
||||||
codec_fn = get_codec_filename(ev.data);
|
load_codec((const struct codec_load_info *)ev.data);
|
||||||
if (!codec_fn)
|
break;
|
||||||
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:
|
case Q_CODEC_RUN:
|
||||||
LOGFQUEUE("codec < Q_CODEC_LOAD");
|
LOGFQUEUE("codec < Q_CODEC_RUN");
|
||||||
codec_queue_ack(Q_CODEC_LOAD);
|
run_codec();
|
||||||
hid = (int)ev.data;
|
break;
|
||||||
handle = codec_load_buf(hid, &ci);
|
|
||||||
bufclose(hid);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Q_CODEC_DO_CALLBACK:
|
case Q_CODEC_PAUSE:
|
||||||
LOGFQUEUE("codec < Q_CODEC_DO_CALLBACK");
|
LOGFQUEUE("codec < Q_CODEC_PAUSE");
|
||||||
codec_queue_ack(Q_CODEC_DO_CALLBACK);
|
break;
|
||||||
if ((void*)ev.data != NULL)
|
|
||||||
{
|
|
||||||
cpucache_commit_discard();
|
|
||||||
((void (*)(void))ev.data)();
|
|
||||||
cpucache_commit();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
case Q_CODEC_SEEK:
|
||||||
LOGFQUEUE("codec < default : %ld", ev.id);
|
LOGFQUEUE("codec < Q_CODEC_SEEK: %lu", (unsigned long)ev.data);
|
||||||
}
|
seek_codec(ev.data);
|
||||||
|
break;
|
||||||
|
|
||||||
if (handle)
|
case Q_CODEC_UNLOAD:
|
||||||
{
|
LOGFQUEUE("codec < Q_CODEC_UNLOAD");
|
||||||
/* Codec loaded - call the entrypoint */
|
unload_codec();
|
||||||
yield();
|
break;
|
||||||
logf("codec running");
|
|
||||||
status = codec_begin(handle);
|
|
||||||
logf("codec stopped");
|
|
||||||
codec_close(handle);
|
|
||||||
current_codectype = AFMT_UNKNOWN;
|
|
||||||
|
|
||||||
if (ci.stop_codec)
|
case Q_CODEC_DO_CALLBACK:
|
||||||
status = CODEC_OK;
|
LOGFQUEUE("codec < Q_CODEC_DO_CALLBACK");
|
||||||
}
|
do_callback((void (*)(void))ev.data);
|
||||||
|
break;
|
||||||
|
|
||||||
switch (ev.id)
|
default:
|
||||||
{
|
LOGFQUEUE("codec < default : %ld", 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)
|
void make_codec_thread(void)
|
||||||
{
|
{
|
||||||
queue_init(&codec_queue, false);
|
queue_init(&codec_queue, false);
|
||||||
|
@ -558,78 +643,86 @@ void make_codec_thread(void)
|
||||||
codec_thread_id);
|
codec_thread_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Unfreeze the codec thread */
|
||||||
void codec_thread_resume(void)
|
void codec_thread_resume(void)
|
||||||
{
|
{
|
||||||
thread_thaw(codec_thread_id);
|
thread_thaw(codec_thread_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Is the current thread the codec thread? */
|
||||||
bool is_codec_thread(void)
|
bool is_codec_thread(void)
|
||||||
{
|
{
|
||||||
return thread_self() == codec_thread_id;
|
return thread_self() == codec_thread_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAVE_PRIORITY_SCHEDULING
|
#ifdef HAVE_PRIORITY_SCHEDULING
|
||||||
|
/* Obtain codec thread's current priority */
|
||||||
int codec_thread_get_priority(void)
|
int codec_thread_get_priority(void)
|
||||||
{
|
{
|
||||||
return thread_get_priority(codec_thread_id);
|
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)
|
int codec_thread_set_priority(int priority)
|
||||||
{
|
{
|
||||||
return thread_set_priority(codec_thread_id, priority);
|
return thread_set_priority(codec_thread_id, priority);
|
||||||
}
|
}
|
||||||
#endif /* HAVE_PRIORITY_SCHEDULING */
|
#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 codec_load(int hid, int cod_spec)
|
||||||
{
|
{
|
||||||
bool retval = false;
|
struct codec_load_info parm = { hid, cod_spec };
|
||||||
|
|
||||||
ci.stop_codec = false;
|
LOGFQUEUE("audio >| codec Q_CODEC_LOAD: %d, %d", hid, cod_spec);
|
||||||
current_codectype = cod_spec;
|
return codec_queue_send(Q_CODEC_LOAD, (intptr_t)&parm) != 0;
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 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)
|
void codec_stop(void)
|
||||||
{
|
{
|
||||||
ci.stop_codec = true;
|
|
||||||
/* Wait until it's in the main loop */
|
/* Wait until it's in the main loop */
|
||||||
while (codec_ack_msg(0, false) != Q_NULL);
|
LOGFQUEUE("audio >| codec Q_CODEC_STOP");
|
||||||
current_codectype = AFMT_UNKNOWN;
|
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)
|
int codec_loaded(void)
|
||||||
{
|
{
|
||||||
return current_codectype;
|
return codec_type;
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,6 @@
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
/* codec identity */
|
/* codec identity */
|
||||||
int get_codec_base_type(int type);
|
|
||||||
const char *get_codec_filename(int cod_spec);
|
const char *get_codec_filename(int cod_spec);
|
||||||
|
|
||||||
/* codec thread */
|
/* codec thread */
|
||||||
|
@ -44,10 +43,14 @@ int codec_thread_set_priority(int priority);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* codec commands - on audio thread only! */
|
/* 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);
|
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_stop(void);
|
||||||
|
void codec_unload(void);
|
||||||
int codec_loaded(void);
|
int codec_loaded(void);
|
||||||
|
|
||||||
/* */
|
/* */
|
||||||
|
|
||||||
#endif /* _CODEC_THREAD_H */
|
#endif /* _CODEC_THREAD_H */
|
||||||
|
|
|
@ -79,13 +79,10 @@ static int open(const char* pathname, int flags, ...)
|
||||||
#endif
|
#endif
|
||||||
struct codec_api ci = {
|
struct codec_api ci = {
|
||||||
|
|
||||||
0, /* filesize */
|
0, /* filesize */
|
||||||
0, /* curpos */
|
0, /* curpos */
|
||||||
NULL, /* id3 */
|
NULL, /* id3 */
|
||||||
NULL, /* taginfo_ready */
|
ERR_HANDLE_NOT_FOUND, /* audio_hid */
|
||||||
false, /* stop_codec */
|
|
||||||
0, /* new_track */
|
|
||||||
0, /* seek_time */
|
|
||||||
NULL, /* struct dsp_config *dsp */
|
NULL, /* struct dsp_config *dsp */
|
||||||
NULL, /* codec_get_buffer */
|
NULL, /* codec_get_buffer */
|
||||||
NULL, /* pcmbuf_insert */
|
NULL, /* pcmbuf_insert */
|
||||||
|
@ -95,9 +92,9 @@ struct codec_api ci = {
|
||||||
NULL, /* advance_buffer */
|
NULL, /* advance_buffer */
|
||||||
NULL, /* seek_buffer */
|
NULL, /* seek_buffer */
|
||||||
NULL, /* seek_complete */
|
NULL, /* seek_complete */
|
||||||
NULL, /* request_next_track */
|
|
||||||
NULL, /* set_offset */
|
NULL, /* set_offset */
|
||||||
NULL, /* configure */
|
NULL, /* configure */
|
||||||
|
NULL, /* get_command */
|
||||||
|
|
||||||
/* kernel/ system */
|
/* kernel/ system */
|
||||||
#if defined(CPU_ARM) && CONFIG_PLATFORM & PLATFORM_NATIVE
|
#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);
|
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;
|
||||||
struct lc_header *hdr = c_hdr ? &c_hdr->lc_hdr : NULL;
|
|
||||||
|
c_hdr = lc_get_header(curr_handle);
|
||||||
|
hdr = c_hdr ? &c_hdr->lc_hdr : NULL;
|
||||||
|
|
||||||
if (hdr == NULL
|
if (hdr == NULL
|
||||||
|| (hdr->magic != CODEC_MAGIC
|
|| (hdr->magic != CODEC_MAGIC
|
||||||
|
@ -193,15 +196,17 @@ static void * codec_load_ram(void *handle, struct codec_api *api)
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
logf("codec header error");
|
logf("codec header error");
|
||||||
lc_close(handle);
|
lc_close(curr_handle);
|
||||||
return NULL;
|
curr_handle = NULL;
|
||||||
|
return CODEC_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hdr->api_version > CODEC_API_VERSION
|
if (hdr->api_version > CODEC_API_VERSION
|
||||||
|| hdr->api_version < CODEC_MIN_API_VERSION) {
|
|| hdr->api_version < CODEC_MIN_API_VERSION) {
|
||||||
logf("codec api version error");
|
logf("codec api version error");
|
||||||
lc_close(handle);
|
lc_close(curr_handle);
|
||||||
return NULL;
|
curr_handle = NULL;
|
||||||
|
return CODEC_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if (CONFIG_PLATFORM & PLATFORM_NATIVE)
|
#if (CONFIG_PLATFORM & PLATFORM_NATIVE)
|
||||||
|
@ -212,63 +217,66 @@ static void * codec_load_ram(void *handle, struct codec_api *api)
|
||||||
|
|
||||||
*(c_hdr->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;
|
int rc = bufread(hid, CODEC_SIZE, codecbuf);
|
||||||
void *handle;
|
|
||||||
rc = bufread(hid, CODEC_SIZE, codecbuf);
|
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
logf("Codec: cannot read buf handle");
|
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) {
|
if (curr_handle == NULL) {
|
||||||
logf("error loading codec");
|
logf("Codec: load error");
|
||||||
return NULL;
|
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];
|
char path[MAX_PATH];
|
||||||
void *handle;
|
|
||||||
|
|
||||||
codec_get_full_path(path, plugin);
|
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");
|
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;
|
if (curr_handle == NULL) {
|
||||||
struct codec_header *c_hdr;
|
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) {
|
int codec_close(void)
|
||||||
logf("Codec: calling entry_point");
|
{
|
||||||
status = c_hdr->entry_point();
|
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;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
void codec_close(void *handle)
|
|
||||||
{
|
|
||||||
if (handle)
|
|
||||||
lc_close(handle);
|
|
||||||
}
|
|
||||||
|
|
|
@ -75,12 +75,18 @@
|
||||||
#define CODEC_ENC_MAGIC 0x52454E43 /* RENC */
|
#define CODEC_ENC_MAGIC 0x52454E43 /* RENC */
|
||||||
|
|
||||||
/* increase this every time the api struct changes */
|
/* 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
|
/* update this to latest version if a change to the api struct breaks
|
||||||
backwards compatibility (and please take the opportunity to sort in any
|
backwards compatibility (and please take the opportunity to sort in any
|
||||||
new function which are "waiting" at the end of the function table) */
|
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 */
|
/* codec return codes */
|
||||||
enum codec_status {
|
enum codec_status {
|
||||||
|
@ -88,6 +94,13 @@ enum codec_status {
|
||||||
CODEC_ERROR = -1,
|
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
|
/* NOTE: To support backwards compatibility, only add new functions at
|
||||||
the end of the structure. Every time you add a new function,
|
the end of the structure. Every time you add a new function,
|
||||||
remember to increase CODEC_API_VERSION. If you make changes to the
|
remember to increase CODEC_API_VERSION. If you make changes to the
|
||||||
|
@ -95,24 +108,12 @@ enum codec_status {
|
||||||
version
|
version
|
||||||
*/
|
*/
|
||||||
struct codec_api {
|
struct codec_api {
|
||||||
|
|
||||||
off_t filesize; /* Total file length */
|
off_t filesize; /* Total file length */
|
||||||
off_t curpos; /* Current buffer position */
|
off_t curpos; /* Current buffer position */
|
||||||
|
|
||||||
/* For gapless mp3 */
|
|
||||||
struct mp3entry *id3; /* TAG metadata pointer */
|
struct mp3entry *id3; /* TAG metadata pointer */
|
||||||
bool *taginfo_ready; /* Is metadata read */
|
int audio_hid; /* Current audio handle */
|
||||||
|
|
||||||
/* 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;
|
|
||||||
|
|
||||||
/* The dsp instance to be used for audio output */
|
/* The dsp instance to be used for audio output */
|
||||||
struct dsp_config *dsp;
|
struct dsp_config *dsp;
|
||||||
|
|
||||||
|
@ -138,14 +139,12 @@ struct codec_api {
|
||||||
bool (*seek_buffer)(size_t newpos);
|
bool (*seek_buffer)(size_t newpos);
|
||||||
/* Codec should call this function when it has done the seeking. */
|
/* Codec should call this function when it has done the seeking. */
|
||||||
void (*seek_complete)(void);
|
void (*seek_complete)(void);
|
||||||
/* Request file change from file buffer. Returns true is next
|
/* Update the current position */
|
||||||
track is available and changed. If return value is false,
|
|
||||||
codec should exit immediately with PLUGIN_OK status. */
|
|
||||||
bool (*request_next_track)(void);
|
|
||||||
|
|
||||||
void (*set_offset)(size_t value);
|
void (*set_offset)(size_t value);
|
||||||
/* Configure different codec buffer parameters. */
|
/* Configure different codec buffer parameters. */
|
||||||
void (*configure)(int setting, intptr_t value);
|
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 */
|
/* kernel/ system */
|
||||||
#if defined(CPU_ARM) && CONFIG_PLATFORM & PLATFORM_NATIVE
|
#if defined(CPU_ARM) && CONFIG_PLATFORM & PLATFORM_NATIVE
|
||||||
|
@ -231,7 +230,8 @@ struct codec_api {
|
||||||
/* codec header */
|
/* codec header */
|
||||||
struct codec_header {
|
struct codec_header {
|
||||||
struct lc_header lc_hdr; /* must be first */
|
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;
|
struct codec_api **api;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -248,13 +248,15 @@ extern unsigned char plugin_end_addr[];
|
||||||
const struct codec_header __header \
|
const struct codec_header __header \
|
||||||
__attribute__ ((section (".header")))= { \
|
__attribute__ ((section (".header")))= { \
|
||||||
{ CODEC_MAGIC, TARGET_ID, CODEC_API_VERSION, \
|
{ 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 */
|
/* encoders */
|
||||||
#define CODEC_ENC_HEADER \
|
#define CODEC_ENC_HEADER \
|
||||||
const struct codec_header __header \
|
const struct codec_header __header \
|
||||||
__attribute__ ((section (".header")))= { \
|
__attribute__ ((section (".header")))= { \
|
||||||
{ CODEC_ENC_MAGIC, TARGET_ID, CODEC_API_VERSION, \
|
{ 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 */
|
#else /* def SIMULATOR */
|
||||||
/* decoders */
|
/* decoders */
|
||||||
|
@ -262,12 +264,12 @@ extern unsigned char plugin_end_addr[];
|
||||||
const struct codec_header __header \
|
const struct codec_header __header \
|
||||||
__attribute__((visibility("default"))) = { \
|
__attribute__((visibility("default"))) = { \
|
||||||
{ CODEC_MAGIC, TARGET_ID, CODEC_API_VERSION, NULL, NULL }, \
|
{ CODEC_MAGIC, TARGET_ID, CODEC_API_VERSION, NULL, NULL }, \
|
||||||
codec_start, &ci };
|
codec_start, codec_run, &ci };
|
||||||
/* encoders */
|
/* encoders */
|
||||||
#define CODEC_ENC_HEADER \
|
#define CODEC_ENC_HEADER \
|
||||||
const struct codec_header __header = { \
|
const struct codec_header __header = { \
|
||||||
{ CODEC_ENC_MAGIC, TARGET_ID, CODEC_API_VERSION, NULL, NULL }, \
|
{ CODEC_ENC_MAGIC, TARGET_ID, CODEC_API_VERSION, NULL, NULL }, \
|
||||||
codec_start, &ci };
|
codec_start, codec_run, &ci };
|
||||||
#endif /* SIMULATOR */
|
#endif /* SIMULATOR */
|
||||||
#endif /* CODEC */
|
#endif /* CODEC */
|
||||||
|
|
||||||
|
@ -276,13 +278,14 @@ extern unsigned char plugin_end_addr[];
|
||||||
void codec_get_full_path(char *path, const char *codec_root_fn);
|
void codec_get_full_path(char *path, const char *codec_root_fn);
|
||||||
|
|
||||||
/* defined by the codec loader (codec.c) */
|
/* defined by the codec loader (codec.c) */
|
||||||
void * codec_load_buf(int hid, struct codec_api *api);
|
int codec_load_buf(int hid, struct codec_api *api);
|
||||||
void * codec_load_file(const char* codec, struct codec_api *api);
|
int codec_load_file(const char* codec, struct codec_api *api);
|
||||||
int codec_begin(void *handle);
|
int codec_run_proc(void);
|
||||||
void codec_close(void *handle);
|
int codec_halt(void);
|
||||||
|
int codec_close(void);
|
||||||
|
|
||||||
/* defined by the codec */
|
/* defined by the codec */
|
||||||
enum codec_status codec_start(void);
|
enum codec_status codec_start(enum codec_entry_call_reason reason);
|
||||||
enum codec_status codec_main(void);
|
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 */
|
/* 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;
|
size_t n;
|
||||||
unsigned char *filebuf;
|
unsigned char *filebuf;
|
||||||
int sample_loc;
|
int sample_loc;
|
||||||
int retval;
|
intptr_t param;
|
||||||
|
|
||||||
/* Generic codec initialisation */
|
if (codec_init())
|
||||||
ci->configure(DSP_SET_STEREO_MODE, STEREO_NONINTERLEAVED);
|
return CODEC_ERROR;
|
||||||
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;
|
|
||||||
|
|
||||||
ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency);
|
ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency);
|
||||||
codec_set_replaygain(ci->id3);
|
codec_set_replaygain(ci->id3);
|
||||||
|
@ -153,15 +157,18 @@ next_track:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
ci->seek_buffer(ci->id3->first_frame_offset);
|
||||||
samplesdone = 0;
|
samplesdone = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
if (ci->stop_codec || ci->new_track)
|
enum codec_command_action action = ci->get_command(¶m);
|
||||||
|
|
||||||
|
if (action == CODEC_ACTION_HALT)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (ci->seek_time) {
|
if (action == CODEC_ACTION_SEEK_TIME) {
|
||||||
sample_loc = (ci->seek_time - 1)/1000 * ci->id3->frequency;
|
sample_loc = param/1000 * ci->id3->frequency;
|
||||||
|
|
||||||
if (ci->seek_buffer((sample_loc/A52_SAMPLESPERFRAME)*ci->id3->bytesperframe)) {
|
if (ci->seek_buffer((sample_loc/A52_SAMPLESPERFRAME)*ci->id3->bytesperframe)) {
|
||||||
samplesdone = sample_loc;
|
samplesdone = sample_loc;
|
||||||
|
@ -179,11 +186,5 @@ next_track:
|
||||||
ci->advance_buffer(n);
|
ci->advance_buffer(n);
|
||||||
}
|
}
|
||||||
|
|
||||||
request_next_track:
|
return CODEC_OK;
|
||||||
if (ci->request_next_track())
|
|
||||||
goto next_track;
|
|
||||||
|
|
||||||
exit:
|
|
||||||
a52_free(state);
|
|
||||||
return retval;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -124,36 +124,44 @@ static void a52_decode_data(uint8_t *start, uint8_t *end)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* this is the codec entry point */
|
/* 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;
|
size_t n;
|
||||||
uint8_t *filebuf;
|
uint8_t *filebuf;
|
||||||
int retval, consumed, packet_offset;
|
int consumed, packet_offset;
|
||||||
int playback_on = -1;
|
int playback_on = -1;
|
||||||
size_t resume_offset;
|
size_t resume_offset;
|
||||||
|
intptr_t param;
|
||||||
/* Generic codec initialisation */
|
enum codec_command_action action = CODEC_ACTION_NULL;
|
||||||
ci->configure(DSP_SET_STEREO_MODE, STEREO_NONINTERLEAVED);
|
|
||||||
ci->configure(DSP_SET_SAMPLE_DEPTH, 28);
|
|
||||||
|
|
||||||
next_track:
|
|
||||||
retval = CODEC_OK;
|
|
||||||
|
|
||||||
if (codec_init()) {
|
if (codec_init()) {
|
||||||
retval = CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
goto exit;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (codec_wait_taginfo() != 0)
|
|
||||||
goto request_next_track;
|
|
||||||
|
|
||||||
resume_offset = ci->id3->offset;
|
resume_offset = ci->id3->offset;
|
||||||
|
|
||||||
ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency);
|
ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency);
|
||||||
codec_set_replaygain(ci->id3);
|
codec_set_replaygain(ci->id3);
|
||||||
|
|
||||||
|
ci->seek_buffer(ci->id3->first_frame_offset);
|
||||||
|
|
||||||
/* Intializations */
|
/* Intializations */
|
||||||
state = a52_init(0);
|
state = a52_init(0);
|
||||||
ci->memset(&rmctx,0,sizeof(RMContext));
|
ci->memset(&rmctx,0,sizeof(RMContext));
|
||||||
|
@ -165,26 +173,34 @@ next_track:
|
||||||
resume_offset -= rmctx.data_offset + DATA_HEADER_SIZE;
|
resume_offset -= rmctx.data_offset + DATA_HEADER_SIZE;
|
||||||
/* put number of subpackets to skip in resume_offset */
|
/* put number of subpackets to skip in resume_offset */
|
||||||
resume_offset /= (rmctx.block_align + PACKET_HEADER_SIZE);
|
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 );
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Seek to the first packet */
|
|
||||||
ci->advance_buffer(rmctx.data_offset + DATA_HEADER_SIZE );
|
|
||||||
|
|
||||||
/* The main decoding loop */
|
/* The main decoding loop */
|
||||||
while((unsigned)rmctx.audio_pkt_cnt < rmctx.nb_packets) {
|
while((unsigned)rmctx.audio_pkt_cnt < rmctx.nb_packets) {
|
||||||
ci->yield();
|
if (action == CODEC_ACTION_NULL)
|
||||||
if (ci->stop_codec || ci->new_track)
|
action = ci->get_command(¶m);
|
||||||
|
|
||||||
|
if (action == CODEC_ACTION_HALT)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (ci->seek_time) {
|
if (action == CODEC_ACTION_SEEK_TIME) {
|
||||||
packet_offset = ci->seek_time / ((rmctx.block_align*8*1000)/rmctx.bit_rate);
|
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));
|
ci->seek_buffer(rmctx.data_offset + DATA_HEADER_SIZE +
|
||||||
|
packet_offset*(rmctx.block_align + PACKET_HEADER_SIZE));
|
||||||
rmctx.audio_pkt_cnt = packet_offset;
|
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();
|
ci->seek_complete();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
action = CODEC_ACTION_NULL;
|
||||||
|
|
||||||
filebuf = ci->request_buffer(&n, rmctx.block_align + PACKET_HEADER_SIZE);
|
filebuf = ci->request_buffer(&n, rmctx.block_align + PACKET_HEADER_SIZE);
|
||||||
consumed = rm_get_packet(&filebuf, &rmctx, &pkt);
|
consumed = rm_get_packet(&filebuf, &rmctx, &pkt);
|
||||||
|
|
||||||
|
@ -195,8 +211,7 @@ next_track:
|
||||||
return CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
retval = CODEC_OK;
|
break;
|
||||||
goto exit;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -205,11 +220,5 @@ next_track:
|
||||||
ci->advance_buffer(pkt.length);
|
ci->advance_buffer(pkt.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
request_next_track:
|
return CODEC_OK;
|
||||||
if (ci->request_next_track())
|
|
||||||
goto next_track;
|
|
||||||
|
|
||||||
exit:
|
|
||||||
a52_free(state);
|
|
||||||
return retval;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,19 @@ CODEC_HEADER
|
||||||
#define FAAD_BYTE_BUFFER_SIZE (2048-12)
|
#define FAAD_BYTE_BUFFER_SIZE (2048-12)
|
||||||
|
|
||||||
/* this is the codec entry point */
|
/* 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
|
/* Note that when dealing with QuickTime/MPEG4 files, terminology is
|
||||||
* a bit confusing. Files with sound are split up in chunks, where
|
* 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;
|
uint32_t sbr_fac = 1;
|
||||||
unsigned char c = 0;
|
unsigned char c = 0;
|
||||||
void *ret;
|
void *ret;
|
||||||
|
intptr_t param;
|
||||||
/* Generic codec initialisation */
|
|
||||||
ci->configure(DSP_SET_STEREO_MODE, STEREO_NONINTERLEAVED);
|
|
||||||
ci->configure(DSP_SET_SAMPLE_DEPTH, 29);
|
|
||||||
|
|
||||||
next_track:
|
|
||||||
err = CODEC_OK;
|
|
||||||
|
|
||||||
/* Clean and initialize decoder structures */
|
/* Clean and initialize decoder structures */
|
||||||
memset(&demux_res , 0, sizeof(demux_res));
|
memset(&demux_res , 0, sizeof(demux_res));
|
||||||
if (codec_init()) {
|
if (codec_init()) {
|
||||||
LOGF("FAAD: Codec init error\n");
|
LOGF("FAAD: Codec init error\n");
|
||||||
err = CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
goto exit;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (codec_wait_taginfo() != 0)
|
|
||||||
goto done;
|
|
||||||
|
|
||||||
file_offset = ci->id3->offset;
|
file_offset = ci->id3->offset;
|
||||||
|
|
||||||
ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency);
|
ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency);
|
||||||
|
@ -85,12 +87,13 @@ next_track:
|
||||||
|
|
||||||
stream_create(&input_stream,ci);
|
stream_create(&input_stream,ci);
|
||||||
|
|
||||||
|
ci->seek_buffer(ci->id3->first_frame_offset);
|
||||||
|
|
||||||
/* if qtmovie_read returns successfully, the stream is up to
|
/* if qtmovie_read returns successfully, the stream is up to
|
||||||
* the movie data, which can be used directly by the decoder */
|
* the movie data, which can be used directly by the decoder */
|
||||||
if (!qtmovie_read(&input_stream, &demux_res)) {
|
if (!qtmovie_read(&input_stream, &demux_res)) {
|
||||||
LOGF("FAAD: File init error\n");
|
LOGF("FAAD: File init error\n");
|
||||||
err = CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* initialise the sound converter */
|
/* initialise the sound converter */
|
||||||
|
@ -98,8 +101,7 @@ next_track:
|
||||||
|
|
||||||
if (!decoder) {
|
if (!decoder) {
|
||||||
LOGF("FAAD: Decode open error\n");
|
LOGF("FAAD: Decode open error\n");
|
||||||
err = CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NeAACDecConfigurationPtr conf = NeAACDecGetCurrentConfiguration(decoder);
|
NeAACDecConfigurationPtr conf = NeAACDecGetCurrentConfiguration(decoder);
|
||||||
|
@ -109,8 +111,7 @@ next_track:
|
||||||
err = NeAACDecInit2(decoder, demux_res.codecdata, demux_res.codecdata_len, &s, &c);
|
err = NeAACDecInit2(decoder, demux_res.codecdata, demux_res.codecdata_len, &s, &c);
|
||||||
if (err) {
|
if (err) {
|
||||||
LOGF("FAAD: DecInit: %d, %d\n", err, decoder->object_type);
|
LOGF("FAAD: DecInit: %d, %d\n", err, decoder->object_type);
|
||||||
err = CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef SBR_DEC
|
#ifdef SBR_DEC
|
||||||
|
@ -150,20 +151,19 @@ next_track:
|
||||||
|
|
||||||
/* The main decoding loop */
|
/* The main decoding loop */
|
||||||
while (i < demux_res.num_sample_byte_sizes) {
|
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;
|
break;
|
||||||
}
|
|
||||||
|
|
||||||
/* Deal with any pending seek requests */
|
/* 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
|
/* Seek to the desired time position. Important: When seeking in SBR
|
||||||
* upsampling files the seek_time must be divided by 2 when calling
|
* upsampling files the seek_time must be divided by 2 when calling
|
||||||
* m4a_seek and the resulting sound_samples_done must be expanded
|
* m4a_seek and the resulting sound_samples_done must be expanded
|
||||||
* by a factor 2. This is done via using sbr_fac. */
|
* by a factor 2. This is done via using sbr_fac. */
|
||||||
if (m4a_seek(&demux_res, &input_stream,
|
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, (int*) &i)) {
|
||||||
sound_samples_done *= sbr_fac;
|
sound_samples_done *= sbr_fac;
|
||||||
elapsed_time = (sound_samples_done * 10) / (ci->id3->frequency / 100);
|
elapsed_time = (sound_samples_done * 10) / (ci->id3->frequency / 100);
|
||||||
|
@ -194,8 +194,7 @@ next_track:
|
||||||
else if (file_offset == 0)
|
else if (file_offset == 0)
|
||||||
{
|
{
|
||||||
LOGF("AAC: get_sample_offset error\n");
|
LOGF("AAC: get_sample_offset error\n");
|
||||||
err = CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Request the required number of bytes from the input buffer */
|
/* Request the required number of bytes from the input buffer */
|
||||||
|
@ -207,8 +206,7 @@ next_track:
|
||||||
/* NeAACDecDecode may sometimes return NULL without setting error. */
|
/* NeAACDecDecode may sometimes return NULL without setting error. */
|
||||||
if (ret == NULL || frame_info.error > 0) {
|
if (ret == NULL || frame_info.error > 0) {
|
||||||
LOGF("FAAD: decode error '%s'\n", NeAACDecGetErrorMessage(frame_info.error));
|
LOGF("FAAD: decode error '%s'\n", NeAACDecGetErrorMessage(frame_info.error));
|
||||||
err = CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Advance codec buffer (no need to call set_offset because of this) */
|
/* Advance codec buffer (no need to call set_offset because of this) */
|
||||||
|
@ -251,12 +249,6 @@ next_track:
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
done:
|
|
||||||
LOGF("AAC: Decoded %lu samples\n", (unsigned long)sound_samples_done);
|
LOGF("AAC: Decoded %lu samples\n", (unsigned long)sound_samples_done);
|
||||||
|
return CODEC_OK;
|
||||||
if (ci->request_next_track())
|
|
||||||
goto next_track;
|
|
||||||
|
|
||||||
exit:
|
|
||||||
return err;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,7 +45,19 @@ static const long cutoff = 500;
|
||||||
static int16_t samples[WAV_CHUNK_SIZE] IBSS_ATTR;
|
static int16_t samples[WAV_CHUNK_SIZE] IBSS_ATTR;
|
||||||
|
|
||||||
/* this is the codec entry point */
|
/* 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 channels;
|
||||||
int sampleswritten, i;
|
int sampleswritten, i;
|
||||||
|
@ -62,12 +74,8 @@ enum codec_status codec_main(void)
|
||||||
off_t chanstart, bufoff;
|
off_t chanstart, bufoff;
|
||||||
/*long coef1=0x7298L,coef2=-0x3350L;*/
|
/*long coef1=0x7298L,coef2=-0x3350L;*/
|
||||||
long coef1, coef2;
|
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");
|
DEBUGF("ADX: next_track\n");
|
||||||
if (codec_init()) {
|
if (codec_init()) {
|
||||||
return CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
|
@ -77,10 +85,6 @@ next_track:
|
||||||
/* init history */
|
/* init history */
|
||||||
ch1_1=ch1_2=ch2_1=ch2_2=0;
|
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);
|
codec_set_replaygain(ci->id3);
|
||||||
|
|
||||||
/* Get header */
|
/* Get header */
|
||||||
|
@ -226,10 +230,10 @@ next_track:
|
||||||
/* The main decoder loop */
|
/* The main decoder loop */
|
||||||
|
|
||||||
while (!endofstream) {
|
while (!endofstream) {
|
||||||
ci->yield();
|
enum codec_command_action action = ci->get_command(¶m);
|
||||||
if (ci->stop_codec || ci->new_track) {
|
|
||||||
|
if (action == CODEC_ACTION_HALT)
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
|
|
||||||
/* do we need to loop? */
|
/* do we need to loop? */
|
||||||
if (bufoff > end_adr-18*channels && looping) {
|
if (bufoff > end_adr-18*channels && looping) {
|
||||||
|
@ -254,17 +258,17 @@ next_track:
|
||||||
}
|
}
|
||||||
|
|
||||||
/* do we need to seek? */
|
/* do we need to seek? */
|
||||||
if (ci->seek_time) {
|
if (action == CODEC_ACTION_SEEK_TIME) {
|
||||||
uint32_t newpos;
|
uint32_t newpos;
|
||||||
|
|
||||||
DEBUGF("ADX: seek to %ldms\n",ci->seek_time);
|
DEBUGF("ADX: seek to %ldms\n", (long)param);
|
||||||
|
|
||||||
endofstream = 0;
|
endofstream = 0;
|
||||||
loop_count = 0;
|
loop_count = 0;
|
||||||
fade_count = -1; /* disable fade */
|
fade_count = -1; /* disable fade */
|
||||||
fade_frames = 1;
|
fade_frames = 1;
|
||||||
|
|
||||||
newpos = (((uint64_t)avgbytespersec*(ci->seek_time - 1))
|
newpos = (((uint64_t)avgbytespersec*param)
|
||||||
/ (1000LL*18*channels))*(18*channels);
|
/ (1000LL*18*channels))*(18*channels);
|
||||||
bufoff = chanstart + newpos;
|
bufoff = chanstart + newpos;
|
||||||
while (bufoff > end_adr-18*channels) {
|
while (bufoff > end_adr-18*channels) {
|
||||||
|
@ -385,9 +389,5 @@ next_track:
|
||||||
1000LL/avgbytespersec);
|
1000LL/avgbytespersec);
|
||||||
}
|
}
|
||||||
|
|
||||||
request_next_track:
|
|
||||||
if (ci->request_next_track())
|
|
||||||
goto next_track;
|
|
||||||
|
|
||||||
return CODEC_OK;
|
return CODEC_OK;
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,9 +61,20 @@ static const struct pcm_codec *get_codec(uint32_t formattag)
|
||||||
return NULL;
|
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;
|
struct pcm_format format;
|
||||||
uint32_t bytesdone, decodedsamples;
|
uint32_t bytesdone, decodedsamples;
|
||||||
uint32_t num_sample_frames = 0;
|
uint32_t num_sample_frames = 0;
|
||||||
|
@ -77,38 +88,28 @@ enum codec_status codec_main(void)
|
||||||
bool is_aifc = false;
|
bool is_aifc = false;
|
||||||
const struct pcm_codec *codec;
|
const struct pcm_codec *codec;
|
||||||
uint32_t size;
|
uint32_t size;
|
||||||
|
intptr_t param;
|
||||||
/* Generic codec initialisation */
|
|
||||||
ci->configure(DSP_SET_SAMPLE_DEPTH, PCM_OUTPUT_DEPTH-1);
|
|
||||||
|
|
||||||
next_track:
|
|
||||||
status = CODEC_OK;
|
|
||||||
|
|
||||||
if (codec_init()) {
|
if (codec_init()) {
|
||||||
status = CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
goto exit;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (codec_wait_taginfo() != 0)
|
|
||||||
goto done;
|
|
||||||
|
|
||||||
codec_set_replaygain(ci->id3);
|
codec_set_replaygain(ci->id3);
|
||||||
|
|
||||||
/* Need to save offset for later use (cleared indirectly by advance_buffer) */
|
/* Need to save offset for later use (cleared indirectly by advance_buffer) */
|
||||||
bytesdone = ci->id3->offset;
|
bytesdone = ci->id3->offset;
|
||||||
|
|
||||||
/* assume the AIFF header is less than 1024 bytes */
|
/* assume the AIFF header is less than 1024 bytes */
|
||||||
|
ci->seek_buffer(0);
|
||||||
buf = ci->request_buffer(&n, 1024);
|
buf = ci->request_buffer(&n, 1024);
|
||||||
if (n < 54) {
|
if (n < 54) {
|
||||||
status = CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (memcmp(buf, "FORM", 4) != 0)
|
if (memcmp(buf, "FORM", 4) != 0)
|
||||||
{
|
{
|
||||||
DEBUGF("CODEC_ERROR: does not aiff format %4.4s\n", (char*)&buf[0]);
|
DEBUGF("CODEC_ERROR: does not aiff format %4.4s\n", (char*)&buf[0]);
|
||||||
status = CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
if (memcmp(&buf[8], "AIFF", 4) == 0)
|
if (memcmp(&buf[8], "AIFF", 4) == 0)
|
||||||
is_aifc = false;
|
is_aifc = false;
|
||||||
|
@ -117,8 +118,7 @@ next_track:
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
DEBUGF("CODEC_ERROR: does not aiff format %4.4s\n", (char*)&buf[8]);
|
DEBUGF("CODEC_ERROR: does not aiff format %4.4s\n", (char*)&buf[8]);
|
||||||
status = CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
buf += 12;
|
buf += 12;
|
||||||
|
@ -141,8 +141,7 @@ next_track:
|
||||||
{
|
{
|
||||||
DEBUGF("CODEC_ERROR: 'COMM' chunk size=%lu < %d\n",
|
DEBUGF("CODEC_ERROR: 'COMM' chunk size=%lu < %d\n",
|
||||||
(unsigned long)size, (is_aifc)?22:18);
|
(unsigned long)size, (is_aifc)?22:18);
|
||||||
status = CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
/* num_channels */
|
/* num_channels */
|
||||||
format.channels = ((buf[8]<<8)|buf[9]);
|
format.channels = ((buf[8]<<8)|buf[9]);
|
||||||
|
@ -154,8 +153,7 @@ next_track:
|
||||||
/* sample_rate (don't use last 4 bytes, only integer fs) */
|
/* sample_rate (don't use last 4 bytes, only integer fs) */
|
||||||
if (buf[16] != 0x40) {
|
if (buf[16] != 0x40) {
|
||||||
DEBUGF("CODEC_ERROR: weird sampling rate (no @)\n");
|
DEBUGF("CODEC_ERROR: weird sampling rate (no @)\n");
|
||||||
status = CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
format.samplespersec = ((buf[18]<<24)|(buf[19]<<16)|(buf[20]<<8)|buf[21])+1;
|
format.samplespersec = ((buf[18]<<24)|(buf[19]<<16)|(buf[20]<<8)|buf[21])+1;
|
||||||
format.samplespersec >>= (16 + 14 - buf[17]);
|
format.samplespersec >>= (16 + 14 - buf[17]);
|
||||||
|
@ -181,8 +179,7 @@ next_track:
|
||||||
} else if (memcmp(buf, "SSND", 4)==0) {
|
} else if (memcmp(buf, "SSND", 4)==0) {
|
||||||
if (format.bitspersample == 0) {
|
if (format.bitspersample == 0) {
|
||||||
DEBUGF("CODEC_ERROR: unsupported chunk order\n");
|
DEBUGF("CODEC_ERROR: unsupported chunk order\n");
|
||||||
status = CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
/* offset2snd */
|
/* offset2snd */
|
||||||
offset2snd = (buf[8]<<24)|(buf[9]<<16)|(buf[10]<<8)|buf[11];
|
offset2snd = (buf[8]<<24)|(buf[9]<<16)|(buf[10]<<8)|buf[11];
|
||||||
|
@ -205,21 +202,18 @@ next_track:
|
||||||
buf += size;
|
buf += size;
|
||||||
if (n < size) {
|
if (n < size) {
|
||||||
DEBUGF("CODEC_ERROR: AIFF header size > 1024\n");
|
DEBUGF("CODEC_ERROR: AIFF header size > 1024\n");
|
||||||
status = CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
n -= size;
|
n -= size;
|
||||||
} /* while 'SSND' */
|
} /* while 'SSND' */
|
||||||
|
|
||||||
if (format.channels == 0) {
|
if (format.channels == 0) {
|
||||||
DEBUGF("CODEC_ERROR: 'COMM' chunk not found or 0-channels file\n");
|
DEBUGF("CODEC_ERROR: 'COMM' chunk not found or 0-channels file\n");
|
||||||
status = CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
if (format.numbytes == 0) {
|
if (format.numbytes == 0) {
|
||||||
DEBUGF("CODEC_ERROR: 'SSND' chunk not found or has zero length\n");
|
DEBUGF("CODEC_ERROR: 'SSND' chunk not found or has zero length\n");
|
||||||
status = CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
codec = get_codec(format.formattag);
|
codec = get_codec(format.formattag);
|
||||||
|
@ -227,14 +221,12 @@ next_track:
|
||||||
{
|
{
|
||||||
DEBUGF("CODEC_ERROR: AIFC does not support compressionType: 0x%x\n",
|
DEBUGF("CODEC_ERROR: AIFC does not support compressionType: 0x%x\n",
|
||||||
(unsigned int)format.formattag);
|
(unsigned int)format.formattag);
|
||||||
status = CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!codec->set_format(&format))
|
if (!codec->set_format(&format))
|
||||||
{
|
{
|
||||||
status = CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency);
|
ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency);
|
||||||
|
@ -245,21 +237,18 @@ next_track:
|
||||||
ci->configure(DSP_SET_STEREO_MODE, STEREO_MONO);
|
ci->configure(DSP_SET_STEREO_MODE, STEREO_MONO);
|
||||||
} else {
|
} else {
|
||||||
DEBUGF("CODEC_ERROR: more than 2 channels unsupported\n");
|
DEBUGF("CODEC_ERROR: more than 2 channels unsupported\n");
|
||||||
status = CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (format.samplesperblock == 0)
|
if (format.samplesperblock == 0)
|
||||||
{
|
{
|
||||||
DEBUGF("CODEC_ERROR: samplesperblock is 0\n");
|
DEBUGF("CODEC_ERROR: samplesperblock is 0\n");
|
||||||
status = CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
if (format.blockalign == 0)
|
if (format.blockalign == 0)
|
||||||
{
|
{
|
||||||
DEBUGF("CODEC_ERROR: blockalign is 0\n");
|
DEBUGF("CODEC_ERROR: blockalign is 0\n");
|
||||||
status = CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* check chunksize */
|
/* check chunksize */
|
||||||
|
@ -269,8 +258,7 @@ next_track:
|
||||||
if (format.chunksize == 0)
|
if (format.chunksize == 0)
|
||||||
{
|
{
|
||||||
DEBUGF("CODEC_ERROR: chunksize is 0\n");
|
DEBUGF("CODEC_ERROR: chunksize is 0\n");
|
||||||
status = CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
firstblockposn = 1024 - n;
|
firstblockposn = 1024 - n;
|
||||||
|
@ -283,13 +271,13 @@ next_track:
|
||||||
PCM_SEEK_POS, NULL);
|
PCM_SEEK_POS, NULL);
|
||||||
|
|
||||||
if (newpos->pos > format.numbytes)
|
if (newpos->pos > format.numbytes)
|
||||||
goto done;
|
return CODEC_OK;
|
||||||
|
|
||||||
if (ci->seek_buffer(firstblockposn + newpos->pos))
|
if (ci->seek_buffer(firstblockposn + newpos->pos))
|
||||||
{
|
{
|
||||||
bytesdone = newpos->pos;
|
bytesdone = newpos->pos;
|
||||||
decodedsamples = newpos->samples;
|
decodedsamples = newpos->samples;
|
||||||
}
|
}
|
||||||
ci->seek_complete();
|
|
||||||
} else {
|
} else {
|
||||||
/* already where we need to be */
|
/* already where we need to be */
|
||||||
bytesdone = 0;
|
bytesdone = 0;
|
||||||
|
@ -299,21 +287,29 @@ next_track:
|
||||||
endofstream = 0;
|
endofstream = 0;
|
||||||
|
|
||||||
while (!endofstream) {
|
while (!endofstream) {
|
||||||
ci->yield();
|
enum codec_command_action action = ci->get_command(¶m);
|
||||||
if (ci->stop_codec || ci->new_track)
|
|
||||||
|
if (action == CODEC_ACTION_HALT)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (ci->seek_time) {
|
if (action == CODEC_ACTION_SEEK_TIME) {
|
||||||
/* 3rd args(read_buffer) is unnecessary in the format which AIFF supports. */
|
/* 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)
|
if (newpos->pos > format.numbytes)
|
||||||
|
{
|
||||||
|
ci->set_elapsed(ci->id3->length);
|
||||||
|
ci->seek_complete();
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (ci->seek_buffer(firstblockposn + newpos->pos))
|
if (ci->seek_buffer(firstblockposn + newpos->pos))
|
||||||
{
|
{
|
||||||
bytesdone = newpos->pos;
|
bytesdone = newpos->pos;
|
||||||
decodedsamples = newpos->samples;
|
decodedsamples = newpos->samples;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ci->set_elapsed(decodedsamples*1000LL/ci->id3->frequency);
|
||||||
ci->seek_complete();
|
ci->seek_complete();
|
||||||
}
|
}
|
||||||
aifbuf = (uint8_t *)ci->request_buffer(&n, format.chunksize);
|
aifbuf = (uint8_t *)ci->request_buffer(&n, format.chunksize);
|
||||||
|
@ -326,11 +322,10 @@ next_track:
|
||||||
endofstream = 1;
|
endofstream = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
status = codec->decode(aifbuf, n, samples, &bufcount);
|
if (codec->decode(aifbuf, n, samples, &bufcount) == CODEC_ERROR)
|
||||||
if (status == CODEC_ERROR)
|
|
||||||
{
|
{
|
||||||
DEBUGF("codec error\n");
|
DEBUGF("codec error\n");
|
||||||
goto done;
|
return CODEC_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
ci->pcmbuf_insert(samples, NULL, bufcount);
|
ci->pcmbuf_insert(samples, NULL, bufcount);
|
||||||
|
@ -343,13 +338,6 @@ next_track:
|
||||||
|
|
||||||
ci->set_elapsed(decodedsamples*1000LL/ci->id3->frequency);
|
ci->set_elapsed(decodedsamples*1000LL/ci->id3->frequency);
|
||||||
}
|
}
|
||||||
status = CODEC_OK;
|
|
||||||
|
|
||||||
done:
|
return CODEC_OK;
|
||||||
if (ci->request_next_track())
|
|
||||||
goto next_track;
|
|
||||||
|
|
||||||
exit:
|
|
||||||
return status;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -359,40 +359,42 @@ static bool init_encoder(void)
|
||||||
return true;
|
return true;
|
||||||
} /* init_encoder */
|
} /* init_encoder */
|
||||||
|
|
||||||
/* main codec entry point */
|
/* this is the codec entry point */
|
||||||
enum codec_status codec_main(void)
|
enum codec_status codec_main(enum codec_entry_call_reason reason)
|
||||||
{
|
{
|
||||||
if (!init_encoder())
|
if (reason == CODEC_LOAD) {
|
||||||
return CODEC_ERROR;
|
if (!init_encoder())
|
||||||
|
return CODEC_ERROR;
|
||||||
/* main encoding loop */
|
}
|
||||||
while(!ci->stop_codec)
|
else if (reason == CODEC_UNLOAD) {
|
||||||
{
|
/* reset parameters to initial state */
|
||||||
uint32_t *src;
|
ci->enc_set_parameters(NULL);
|
||||||
|
|
||||||
while ((src = (uint32_t *)ci->enc_get_pcm_data(PCM_CHUNK_SIZE)) != NULL)
|
|
||||||
{
|
|
||||||
struct enc_chunk_hdr *chunk;
|
|
||||||
|
|
||||||
if (ci->stop_codec)
|
|
||||||
break;
|
|
||||||
|
|
||||||
chunk = ci->enc_get_chunk();
|
|
||||||
chunk->enc_size = enc_size;
|
|
||||||
chunk->num_pcm = PCM_SAMP_PER_CHUNK;
|
|
||||||
chunk->enc_data = ENC_CHUNK_SKIP_HDR(chunk->enc_data, chunk);
|
|
||||||
|
|
||||||
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;
|
return CODEC_OK;
|
||||||
} /* codec_start */
|
}
|
||||||
|
|
||||||
|
/* this is called for each file to process */
|
||||||
|
enum codec_status codec_run(void)
|
||||||
|
{
|
||||||
|
/* main encoding loop */
|
||||||
|
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 (src == NULL)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
chunk = ci->enc_get_chunk();
|
||||||
|
chunk->enc_size = enc_size;
|
||||||
|
chunk->num_pcm = PCM_SAMP_PER_CHUNK;
|
||||||
|
chunk->enc_data = ENC_CHUNK_SKIP_HDR(chunk->enc_data, chunk);
|
||||||
|
|
||||||
|
chunk_to_aiff_format(src, (uint32_t *)chunk->enc_data);
|
||||||
|
|
||||||
|
ci->enc_finish_chunk();
|
||||||
|
}
|
||||||
|
|
||||||
|
return CODEC_OK;
|
||||||
|
}
|
||||||
|
|
|
@ -32,116 +32,114 @@ CODEC_HEADER
|
||||||
static int32_t outputbuffer[ALAC_MAX_CHANNELS][ALAC_BLOCKSIZE] IBSS_ATTR;
|
static int32_t outputbuffer[ALAC_MAX_CHANNELS][ALAC_BLOCKSIZE] IBSS_ATTR;
|
||||||
|
|
||||||
/* this is the codec entry point */
|
/* 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;
|
if (reason == CODEC_LOAD) {
|
||||||
demux_res_t demux_res;
|
/* Generic codec initialisation */
|
||||||
stream_t input_stream;
|
ci->configure(DSP_SET_STEREO_MODE, STEREO_NONINTERLEAVED);
|
||||||
uint32_t samplesdone;
|
ci->configure(DSP_SET_SAMPLE_DEPTH, ALAC_OUTPUT_DEPTH-1);
|
||||||
uint32_t elapsedtime;
|
|
||||||
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;
|
|
||||||
|
|
||||||
/* 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (codec_wait_taginfo() != 0)
|
|
||||||
goto done;
|
|
||||||
|
|
||||||
ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency);
|
|
||||||
codec_set_replaygain(ci->id3);
|
|
||||||
|
|
||||||
stream_create(&input_stream,ci);
|
|
||||||
|
|
||||||
/* Read from ci->id3->offset before calling qtmovie_read. */
|
|
||||||
samplesdone = (uint32_t)(((uint64_t)(ci->id3->offset) * ci->id3->frequency) /
|
|
||||||
(ci->id3->bitrate*128));
|
|
||||||
|
|
||||||
/* 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("ALAC: Error initialising file\n");
|
|
||||||
retval = CODEC_ERROR;
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* initialise the sound converter */
|
|
||||||
create_alac(demux_res.sound_sample_size, demux_res.num_channels,&alac);
|
|
||||||
alac_set_info(&alac, demux_res.codecdata);
|
|
||||||
|
|
||||||
/* Set i for first frame, seek to desired sample position for resuming. */
|
|
||||||
i=0;
|
|
||||||
if (samplesdone > 0) {
|
|
||||||
if (m4a_seek(&demux_res, &input_stream, samplesdone,
|
|
||||||
&samplesdone, (int*) &i)) {
|
|
||||||
elapsedtime = (samplesdone * 10) / (ci->id3->frequency / 100);
|
|
||||||
ci->set_elapsed(elapsedtime);
|
|
||||||
} else {
|
|
||||||
samplesdone = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* The main decoding loop */
|
|
||||||
while (i < demux_res.num_sample_byte_sizes) {
|
|
||||||
ci->yield();
|
|
||||||
if (ci->stop_codec || ci->new_track) {
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Deal with any pending seek requests */
|
return CODEC_OK;
|
||||||
if (ci->seek_time) {
|
}
|
||||||
if (m4a_seek(&demux_res, &input_stream,
|
|
||||||
((ci->seek_time-1)/10) * (ci->id3->frequency/100),
|
/* this is called for each file to process */
|
||||||
&samplesdone, (int *)&i)) {
|
enum codec_status codec_run(void)
|
||||||
|
{
|
||||||
|
size_t n;
|
||||||
|
demux_res_t demux_res;
|
||||||
|
stream_t input_stream;
|
||||||
|
uint32_t samplesdone;
|
||||||
|
uint32_t elapsedtime = 0;
|
||||||
|
int samplesdecoded;
|
||||||
|
unsigned int i;
|
||||||
|
unsigned char* buffer;
|
||||||
|
alac_file alac;
|
||||||
|
intptr_t param;
|
||||||
|
|
||||||
|
/* Clean and initialize decoder structures */
|
||||||
|
memset(&demux_res , 0, sizeof(demux_res));
|
||||||
|
if (codec_init()) {
|
||||||
|
LOGF("ALAC: Error initialising codec\n");
|
||||||
|
return CODEC_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
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. */
|
||||||
|
samplesdone = (uint32_t)(((uint64_t)(ci->id3->offset) * ci->id3->frequency) /
|
||||||
|
(ci->id3->bitrate*128));
|
||||||
|
|
||||||
|
/* 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("ALAC: Error initialising file\n");
|
||||||
|
return CODEC_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* initialise the sound converter */
|
||||||
|
create_alac(demux_res.sound_sample_size, demux_res.num_channels,&alac);
|
||||||
|
alac_set_info(&alac, demux_res.codecdata);
|
||||||
|
|
||||||
|
/* Set i for first frame, seek to desired sample position for resuming. */
|
||||||
|
i=0;
|
||||||
|
if (samplesdone > 0) {
|
||||||
|
if (m4a_seek(&demux_res, &input_stream, samplesdone,
|
||||||
|
&samplesdone, (int*) &i)) {
|
||||||
|
elapsedtime = (samplesdone * 10) / (ci->id3->frequency / 100);
|
||||||
|
ci->set_elapsed(elapsedtime);
|
||||||
|
} else {
|
||||||
|
samplesdone = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The main decoding loop */
|
||||||
|
while (i < demux_res.num_sample_byte_sizes) {
|
||||||
|
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 (action == CODEC_ACTION_SEEK_TIME) {
|
||||||
|
if (m4a_seek(&demux_res, &input_stream,
|
||||||
|
(param/10) * (ci->id3->frequency/100),
|
||||||
|
&samplesdone, (int *)&i)) {
|
||||||
|
elapsedtime=(samplesdone*10)/(ci->id3->frequency/100);
|
||||||
|
}
|
||||||
|
ci->set_elapsed(elapsedtime);
|
||||||
|
ci->seek_complete();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Request the required number of bytes from the input buffer */
|
||||||
|
buffer=ci->request_buffer(&n, ALAC_BYTE_BUFFER_SIZE);
|
||||||
|
|
||||||
|
/* Decode one block - returned samples will be host-endian */
|
||||||
|
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->pcmbuf_insert(outputbuffer[0], outputbuffer[1], samplesdecoded);
|
||||||
|
|
||||||
|
/* Update the elapsed-time indicator */
|
||||||
|
samplesdone+=samplesdecoded;
|
||||||
elapsedtime=(samplesdone*10)/(ci->id3->frequency/100);
|
elapsedtime=(samplesdone*10)/(ci->id3->frequency/100);
|
||||||
ci->set_elapsed(elapsedtime);
|
ci->set_elapsed(elapsedtime);
|
||||||
}
|
|
||||||
ci->seek_complete();
|
i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Request the required number of bytes from the input buffer */
|
LOGF("ALAC: Decoded %lu samples\n",(unsigned long)samplesdone);
|
||||||
buffer=ci->request_buffer(&n, ALAC_BYTE_BUFFER_SIZE);
|
return CODEC_OK;
|
||||||
|
|
||||||
/* Decode one block - returned samples will be host-endian */
|
|
||||||
ci->yield();
|
|
||||||
samplesdecoded=alac_decode_frame(&alac, buffer, outputbuffer, 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 */
|
|
||||||
samplesdone+=samplesdecoded;
|
|
||||||
elapsedtime=(samplesdone*10)/(ci->id3->frequency/100);
|
|
||||||
ci->set_elapsed(elapsedtime);
|
|
||||||
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
|
|
||||||
done:
|
|
||||||
LOGF("ALAC: Decoded %lu samples\n",(unsigned long)samplesdone);
|
|
||||||
|
|
||||||
if (ci->request_next_track())
|
|
||||||
goto next_track;
|
|
||||||
|
|
||||||
exit:
|
|
||||||
return retval;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -127,13 +127,23 @@ static void ape_resume(struct ape_ctx_t* ape_ctx, size_t resume_offset,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* this is the codec entry point */
|
/* 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;
|
struct ape_ctx_t ape_ctx;
|
||||||
uint32_t samplesdone;
|
uint32_t samplesdone;
|
||||||
uint32_t elapsedtime;
|
uint32_t elapsedtime;
|
||||||
size_t bytesleft;
|
size_t bytesleft;
|
||||||
int retval;
|
|
||||||
|
|
||||||
uint32_t currentframe;
|
uint32_t currentframe;
|
||||||
uint32_t newfilepos;
|
uint32_t newfilepos;
|
||||||
|
@ -145,33 +155,24 @@ enum codec_status codec_main(void)
|
||||||
int res;
|
int res;
|
||||||
int firstbyte;
|
int firstbyte;
|
||||||
size_t resume_offset;
|
size_t resume_offset;
|
||||||
|
intptr_t param;
|
||||||
/* Generic codec initialisation */
|
|
||||||
ci->configure(DSP_SET_SAMPLE_DEPTH, APE_OUTPUT_DEPTH-1);
|
|
||||||
|
|
||||||
next_track:
|
|
||||||
retval = CODEC_OK;
|
|
||||||
|
|
||||||
if (codec_init()) {
|
if (codec_init()) {
|
||||||
LOGF("APE: Error initialising codec\n");
|
LOGF("APE: Error initialising codec\n");
|
||||||
retval = CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
goto exit;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (codec_wait_taginfo() != 0)
|
|
||||||
goto done;
|
|
||||||
|
|
||||||
/* Remember the resume position - when the codec is opened, the
|
/* Remember the resume position - when the codec is opened, the
|
||||||
playback engine will reset it. */
|
playback engine will reset it. */
|
||||||
resume_offset = ci->id3->offset;
|
resume_offset = ci->id3->offset;
|
||||||
|
|
||||||
|
ci->seek_buffer(0);
|
||||||
inbuffer = ci->request_buffer(&bytesleft, INPUT_CHUNKSIZE);
|
inbuffer = ci->request_buffer(&bytesleft, INPUT_CHUNKSIZE);
|
||||||
|
|
||||||
/* Read the file headers to populate the ape_ctx struct */
|
/* Read the file headers to populate the ape_ctx struct */
|
||||||
if (ape_parseheaderbuf(inbuffer,&ape_ctx) < 0) {
|
if (ape_parseheaderbuf(inbuffer,&ape_ctx) < 0) {
|
||||||
LOGF("APE: Error reading header\n");
|
LOGF("APE: Error reading header\n");
|
||||||
retval = CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
goto exit;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Initialise the seektable for this file */
|
/* Initialise the seektable for this file */
|
||||||
|
@ -243,16 +244,16 @@ frame_start:
|
||||||
/* Decode the frame a chunk at a time */
|
/* Decode the frame a chunk at a time */
|
||||||
while (nblocks > 0)
|
while (nblocks > 0)
|
||||||
{
|
{
|
||||||
ci->yield();
|
enum codec_command_action action = ci->get_command(¶m);
|
||||||
if (ci->stop_codec || ci->new_track) {
|
|
||||||
|
if (action == CODEC_ACTION_HALT)
|
||||||
goto done;
|
goto done;
|
||||||
}
|
|
||||||
|
|
||||||
/* Deal with any pending seek requests */
|
/* Deal with any pending seek requests */
|
||||||
if (ci->seek_time)
|
if (action == CODEC_ACTION_SEEK_TIME)
|
||||||
{
|
{
|
||||||
if (ape_calc_seekpos(&ape_ctx,
|
if (ape_calc_seekpos(&ape_ctx,
|
||||||
((ci->seek_time-1)/10) * (ci->id3->frequency/100),
|
(param/10) * (ci->id3->frequency/100),
|
||||||
¤tframe,
|
¤tframe,
|
||||||
&newfilepos,
|
&newfilepos,
|
||||||
&samplestoskip))
|
&samplestoskip))
|
||||||
|
@ -266,9 +267,12 @@ frame_start:
|
||||||
ci->seek_buffer(newfilepos);
|
ci->seek_buffer(newfilepos);
|
||||||
inbuffer = ci->request_buffer(&bytesleft, INPUT_CHUNKSIZE);
|
inbuffer = ci->request_buffer(&bytesleft, INPUT_CHUNKSIZE);
|
||||||
|
|
||||||
|
elapsedtime = (samplesdone*10)/(ape_ctx.samplerate/100);
|
||||||
|
ci->set_elapsed(elapsedtime);
|
||||||
ci->seek_complete();
|
ci->seek_complete();
|
||||||
goto frame_start; /* Sorry... */
|
goto frame_start; /* Sorry... */
|
||||||
}
|
}
|
||||||
|
|
||||||
ci->seek_complete();
|
ci->seek_complete();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -281,8 +285,7 @@ frame_start:
|
||||||
{
|
{
|
||||||
/* Frame decoding error, abort */
|
/* Frame decoding error, abort */
|
||||||
LOGF("APE: Frame %lu, error %d\n",(unsigned long)currentframe,res);
|
LOGF("APE: Frame %lu, error %d\n",(unsigned long)currentframe,res);
|
||||||
retval = CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ci->yield();
|
ci->yield();
|
||||||
|
@ -320,10 +323,5 @@ frame_start:
|
||||||
|
|
||||||
done:
|
done:
|
||||||
LOGF("APE: Decoded %lu samples\n",(unsigned long)samplesdone);
|
LOGF("APE: Decoded %lu samples\n",(unsigned long)samplesdone);
|
||||||
|
return CODEC_OK;
|
||||||
if (ci->request_next_track())
|
|
||||||
goto next_track;
|
|
||||||
|
|
||||||
exit:
|
|
||||||
return retval;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,24 +29,21 @@ CODEC_HEADER
|
||||||
static byte samples[CHUNK_SIZE] IBSS_ATTR; /* The sample buffer */
|
static byte samples[CHUNK_SIZE] IBSS_ATTR; /* The sample buffer */
|
||||||
static ASAP_State asap; /* asap codec state */
|
static ASAP_State asap; /* asap codec state */
|
||||||
|
|
||||||
/* this is the codec entry point */
|
/* this is called for each file to process */
|
||||||
enum codec_status codec_main(void)
|
enum codec_status codec_run(void)
|
||||||
{
|
{
|
||||||
int n_bytes;
|
int n_bytes;
|
||||||
int song;
|
int song;
|
||||||
int duration;
|
int duration;
|
||||||
char* module;
|
char* module;
|
||||||
int bytesPerSample =2;
|
int bytesPerSample =2;
|
||||||
|
intptr_t param;
|
||||||
|
|
||||||
next_track:
|
|
||||||
if (codec_init()) {
|
if (codec_init()) {
|
||||||
DEBUGF("codec init failed\n");
|
DEBUGF("codec init failed\n");
|
||||||
return CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (codec_wait_taginfo() != 0)
|
|
||||||
goto request_next_track;
|
|
||||||
|
|
||||||
codec_set_replaygain(ci->id3);
|
codec_set_replaygain(ci->id3);
|
||||||
|
|
||||||
int bytes_done =0;
|
int bytes_done =0;
|
||||||
|
@ -97,19 +94,20 @@ next_track:
|
||||||
|
|
||||||
/* The main decoder loop */
|
/* The main decoder loop */
|
||||||
while (1) {
|
while (1) {
|
||||||
ci->yield();
|
enum codec_command_action action = ci->get_command(¶m);
|
||||||
if (ci->stop_codec || ci->new_track)
|
|
||||||
|
if (action == CODEC_ACTION_HALT)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (ci->seek_time) {
|
if (action == CODEC_ACTION_SEEK_TIME) {
|
||||||
/* New time is ready in ci->seek_time */
|
/* New time is ready in param */
|
||||||
|
|
||||||
/* seek to pos */
|
/* seek to pos */
|
||||||
ASAP_Seek(&asap,ci->seek_time);
|
ASAP_Seek(&asap,param);
|
||||||
/* update elapsed */
|
|
||||||
ci->set_elapsed(ci->seek_time);
|
|
||||||
/* update bytes_done */
|
/* 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 */
|
/* seek ready */
|
||||||
ci->seek_complete();
|
ci->seek_complete();
|
||||||
}
|
}
|
||||||
|
@ -129,10 +127,6 @@ next_track:
|
||||||
if(n_bytes != sizeof(samples))
|
if(n_bytes != sizeof(samples))
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
request_next_track:
|
|
||||||
if (ci->request_next_track())
|
|
||||||
goto next_track;
|
|
||||||
|
|
||||||
return CODEC_OK;
|
return CODEC_OK;
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,24 +33,22 @@ CODEC_HEADER
|
||||||
|
|
||||||
static ATRAC3Context q IBSS_ATTR;
|
static ATRAC3Context q IBSS_ATTR;
|
||||||
|
|
||||||
/* this is the codec entry point */
|
/* this is called for each file to process */
|
||||||
enum codec_status codec_main(void)
|
enum codec_status codec_run(void)
|
||||||
{
|
{
|
||||||
static size_t buff_size;
|
static size_t buff_size;
|
||||||
int datasize, res, frame_counter, total_frames, seek_frame_offset;
|
int datasize, res, frame_counter, total_frames, seek_frame_offset;
|
||||||
uint8_t *bit_buffer;
|
uint8_t *bit_buffer;
|
||||||
int elapsed = 0;
|
int elapsed = 0;
|
||||||
size_t resume_offset;
|
size_t resume_offset;
|
||||||
|
intptr_t param;
|
||||||
|
enum codec_command_action action = CODEC_ACTION_NULL;
|
||||||
|
|
||||||
next_track:
|
|
||||||
if (codec_init()) {
|
if (codec_init()) {
|
||||||
DEBUGF("codec init failed\n");
|
DEBUGF("codec init failed\n");
|
||||||
return CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (codec_wait_taginfo() != 0)
|
|
||||||
goto done;
|
|
||||||
|
|
||||||
resume_offset = ci->id3->offset;
|
resume_offset = ci->id3->offset;
|
||||||
|
|
||||||
codec_set_replaygain(ci->id3);
|
codec_set_replaygain(ci->id3);
|
||||||
|
@ -60,84 +58,88 @@ next_track:
|
||||||
ci->configure(DSP_SET_SAMPLE_DEPTH, 17); /* Remark: atrac3 uses s15.0 by default, s15.2 was hacked. */
|
ci->configure(DSP_SET_SAMPLE_DEPTH, 17); /* Remark: atrac3 uses s15.0 by default, s15.2 was hacked. */
|
||||||
ci->configure(DSP_SET_STEREO_MODE, ci->id3->channels == 1 ?
|
ci->configure(DSP_SET_STEREO_MODE, ci->id3->channels == 1 ?
|
||||||
STEREO_MONO : STEREO_NONINTERLEAVED);
|
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) {
|
if(res < 0) {
|
||||||
DEBUGF("failed to initialize OMA atrac decoder\n");
|
DEBUGF("failed to initialize OMA atrac decoder\n");
|
||||||
return CODEC_ERROR;
|
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 */
|
/* check for a mid-track resume and force a seek time accordingly */
|
||||||
if(resume_offset > ci->id3->first_frame_offset) {
|
if(resume_offset > ci->id3->first_frame_offset) {
|
||||||
resume_offset -= ci->id3->first_frame_offset;
|
resume_offset -= ci->id3->first_frame_offset;
|
||||||
/* calculate resume_offset in frames */
|
/* calculate resume_offset in frames */
|
||||||
resume_offset = (int)resume_offset / FRAMESIZE;
|
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;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ci->set_elapsed(0);
|
||||||
|
ci->seek_buffer(ci->id3->first_frame_offset);
|
||||||
}
|
}
|
||||||
total_frames = (ci->id3->filesize - ci->id3->first_frame_offset) / FRAMESIZE;
|
|
||||||
frame_counter = 0;
|
|
||||||
|
|
||||||
ci->set_elapsed(0);
|
|
||||||
ci->seek_buffer(0);
|
|
||||||
ci->advance_buffer(ci->id3->first_frame_offset);
|
|
||||||
|
|
||||||
/* The main decoder loop */
|
/* The main decoder loop */
|
||||||
seek_start :
|
|
||||||
while(frame_counter < total_frames)
|
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);
|
bit_buffer = (uint8_t *) ci->request_buffer(&buff_size, FRAMESIZE);
|
||||||
|
|
||||||
ci->yield();
|
if (action == CODEC_ACTION_SEEK_TIME) {
|
||||||
if (ci->stop_codec || ci->new_track)
|
/* Do not allow seeking beyond the file's length */
|
||||||
goto done;
|
if ((unsigned) param > ci->id3->length) {
|
||||||
|
ci->set_elapsed(ci->id3->length);
|
||||||
|
ci->seek_complete();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (ci->seek_time) {
|
/* Seek to the start of the track */
|
||||||
ci->set_elapsed(ci->seek_time);
|
if (param == 0) {
|
||||||
|
elapsed = 0;
|
||||||
|
ci->set_elapsed(0);
|
||||||
|
ci->seek_buffer(ci->id3->first_frame_offset);
|
||||||
|
ci->seek_complete();
|
||||||
|
action = CODEC_ACTION_NULL;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
/* Do not allow seeking beyond the file's length */
|
seek_frame_offset = (param * BITRATE) / (8 * FRAMESIZE);
|
||||||
if ((unsigned) ci->seek_time > ci->id3->length) {
|
frame_counter = seek_frame_offset;
|
||||||
ci->seek_complete();
|
ci->seek_buffer(ci->id3->first_frame_offset + seek_frame_offset* FRAMESIZE);
|
||||||
goto done;
|
bit_buffer = (uint8_t *) ci->request_buffer(&buff_size, FRAMESIZE);
|
||||||
}
|
elapsed = param;
|
||||||
|
|
||||||
/* 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);
|
|
||||||
elapsed = 0;
|
|
||||||
goto seek_start;
|
|
||||||
}
|
|
||||||
seek_frame_offset = (ci->seek_time * 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;
|
|
||||||
|
|
||||||
ci->set_elapsed(elapsed);
|
|
||||||
ci->seek_complete();
|
|
||||||
}
|
|
||||||
|
|
||||||
res = atrac3_decode_frame(FRAMESIZE, &q, &datasize, bit_buffer, FRAMESIZE);
|
|
||||||
|
|
||||||
if(res != (int)FRAMESIZE) {
|
|
||||||
DEBUGF("codec error\n");
|
|
||||||
return CODEC_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(datasize)
|
|
||||||
ci->pcmbuf_insert(q.outSamples, q.outSamples + 1024, q.samples_per_frame / ci->id3->channels);
|
|
||||||
|
|
||||||
elapsed += (FRAMESIZE * 8) / BITRATE;
|
|
||||||
ci->set_elapsed(elapsed);
|
ci->set_elapsed(elapsed);
|
||||||
|
ci->seek_complete();
|
||||||
ci->advance_buffer(FRAMESIZE);
|
}
|
||||||
frame_counter++;
|
|
||||||
}
|
|
||||||
|
|
||||||
done:
|
action = CODEC_ACTION_NULL;
|
||||||
if (ci->request_next_track())
|
|
||||||
goto next_track;
|
res = atrac3_decode_frame(FRAMESIZE, &q, &datasize, bit_buffer, FRAMESIZE);
|
||||||
|
|
||||||
|
if(res != (int)FRAMESIZE) {
|
||||||
|
DEBUGF("codec error\n");
|
||||||
|
return CODEC_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(datasize)
|
||||||
|
ci->pcmbuf_insert(q.outSamples, q.outSamples + 1024,
|
||||||
|
q.samples_per_frame / ci->id3->channels);
|
||||||
|
|
||||||
|
elapsed += (FRAMESIZE * 8) / BITRATE;
|
||||||
|
ci->set_elapsed(elapsed);
|
||||||
|
|
||||||
|
ci->advance_buffer(FRAMESIZE);
|
||||||
|
frame_counter++;
|
||||||
|
}
|
||||||
|
|
||||||
return CODEC_OK;
|
return CODEC_OK;
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,9 +41,9 @@ static void init_rm(RMContext *rmctx)
|
||||||
memcpy(ci->id3->id3v2buf, (char*)rmctx->codec_extradata, rmctx->extradata_size*sizeof(char));
|
memcpy(ci->id3->id3v2buf, (char*)rmctx->codec_extradata, rmctx->extradata_size*sizeof(char));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* this is the codec entry point */
|
/* this is called for each file to process */
|
||||||
enum codec_status codec_main(void)
|
enum codec_status codec_run(void)
|
||||||
{
|
{
|
||||||
static size_t buff_size;
|
static size_t buff_size;
|
||||||
int datasize, res, consumed, i, time_offset;
|
int datasize, res, consumed, i, time_offset;
|
||||||
uint8_t *bit_buffer;
|
uint8_t *bit_buffer;
|
||||||
|
@ -52,16 +52,14 @@ enum codec_status codec_main(void)
|
||||||
int scrambling_unit_size, num_units, elapsed = 0;
|
int scrambling_unit_size, num_units, elapsed = 0;
|
||||||
int playback_on = -1;
|
int playback_on = -1;
|
||||||
size_t resume_offset;
|
size_t resume_offset;
|
||||||
|
intptr_t param;
|
||||||
|
enum codec_command_action action = CODEC_ACTION_NULL;
|
||||||
|
|
||||||
next_track:
|
|
||||||
if (codec_init()) {
|
if (codec_init()) {
|
||||||
DEBUGF("codec init failed\n");
|
DEBUGF("codec init failed\n");
|
||||||
return CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (codec_wait_taginfo() != 0)
|
|
||||||
goto done;
|
|
||||||
|
|
||||||
resume_offset = ci->id3->offset;
|
resume_offset = ci->id3->offset;
|
||||||
|
|
||||||
codec_set_replaygain(ci->id3);
|
codec_set_replaygain(ci->id3);
|
||||||
|
@ -69,6 +67,7 @@ next_track:
|
||||||
ci->memset(&pkt,0,sizeof(RMPacket));
|
ci->memset(&pkt,0,sizeof(RMPacket));
|
||||||
ci->memset(&q,0,sizeof(ATRAC3Context));
|
ci->memset(&q,0,sizeof(ATRAC3Context));
|
||||||
|
|
||||||
|
ci->seek_buffer(0);
|
||||||
init_rm(&rmctx);
|
init_rm(&rmctx);
|
||||||
|
|
||||||
ci->configure(DSP_SET_FREQUENCY, ci->id3->frequency);
|
ci->configure(DSP_SET_FREQUENCY, ci->id3->frequency);
|
||||||
|
@ -84,22 +83,25 @@ next_track:
|
||||||
h = rmctx.sub_packet_h;
|
h = rmctx.sub_packet_h;
|
||||||
scrambling_unit_size = h*fs;
|
scrambling_unit_size = h*fs;
|
||||||
|
|
||||||
res =atrac3_decode_init(&q, ci->id3);
|
res = atrac3_decode_init(&q, ci->id3);
|
||||||
if(res < 0) {
|
if(res < 0) {
|
||||||
DEBUGF("failed to initialize RM atrac decoder\n");
|
DEBUGF("failed to initialize RM atrac decoder\n");
|
||||||
return CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* check for a mid-track resume and force a seek time accordingly */
|
/* check for a mid-track resume and force a seek time accordingly */
|
||||||
if(resume_offset > rmctx.data_offset + DATA_HEADER_SIZE) {
|
if(resume_offset > rmctx.data_offset + DATA_HEADER_SIZE) {
|
||||||
resume_offset -= rmctx.data_offset + DATA_HEADER_SIZE;
|
resume_offset -= rmctx.data_offset + DATA_HEADER_SIZE;
|
||||||
num_units = (int)resume_offset / scrambling_unit_size;
|
num_units = (int)resume_offset / scrambling_unit_size;
|
||||||
/* put number of subpackets to skip in resume_offset */
|
/* put number of subpackets to skip in resume_offset */
|
||||||
resume_offset /= (sps + PACKET_HEADER_SIZE);
|
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);
|
ci->advance_buffer(rmctx.data_offset + DATA_HEADER_SIZE);
|
||||||
|
|
||||||
/* The main decoder loop */
|
/* The main decoder loop */
|
||||||
|
@ -115,22 +117,23 @@ seek_start :
|
||||||
return CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
goto done;
|
return CODEC_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
for(i = 0; i < rmctx.audio_pkt_cnt*(fs/sps) ; i++)
|
for(i = 0; i < rmctx.audio_pkt_cnt*(fs/sps) ; i++)
|
||||||
{
|
{
|
||||||
ci->yield();
|
if (action == CODEC_ACTION_NULL)
|
||||||
if (ci->stop_codec || ci->new_track)
|
action = ci->get_command(¶m);
|
||||||
goto done;
|
|
||||||
|
|
||||||
if (ci->seek_time) {
|
if (action == CODEC_ACTION_HALT)
|
||||||
ci->set_elapsed(ci->seek_time);
|
return CODEC_OK;
|
||||||
|
|
||||||
|
if (action == CODEC_ACTION_SEEK_TIME) {
|
||||||
/* Do not allow seeking beyond the file's length */
|
/* 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();
|
ci->seek_complete();
|
||||||
goto done;
|
return CODEC_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
ci->seek_buffer(rmctx.data_offset + DATA_HEADER_SIZE);
|
ci->seek_buffer(rmctx.data_offset + DATA_HEADER_SIZE);
|
||||||
|
@ -139,12 +142,13 @@ seek_start :
|
||||||
rmctx.frame_number = 0;
|
rmctx.frame_number = 0;
|
||||||
|
|
||||||
/* Seek to the start of the track */
|
/* Seek to the start of the track */
|
||||||
if (ci->seek_time == 1) {
|
if (param == 0) {
|
||||||
ci->set_elapsed(0);
|
ci->set_elapsed(0);
|
||||||
ci->seek_complete();
|
ci->seek_complete();
|
||||||
|
action = CODEC_ACTION_NULL;
|
||||||
goto seek_start;
|
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);
|
ci->seek_buffer(rmctx.data_offset + DATA_HEADER_SIZE + consumed * num_units);
|
||||||
bit_buffer = (uint8_t *) ci->request_buffer(&buff_size, scrambling_unit_size);
|
bit_buffer = (uint8_t *) ci->request_buffer(&buff_size, scrambling_unit_size);
|
||||||
consumed = rm_get_packet(&bit_buffer, &rmctx, &pkt);
|
consumed = rm_get_packet(&bit_buffer, &rmctx, &pkt);
|
||||||
|
@ -155,12 +159,12 @@ seek_start :
|
||||||
return CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
goto done;
|
return CODEC_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
packet_count = rmctx.nb_packets - rmctx.audio_pkt_cnt * num_units;
|
packet_count = rmctx.nb_packets - rmctx.audio_pkt_cnt * num_units;
|
||||||
rmctx.frame_number = ((ci->seek_time)/(sps*1000*8/rmctx.bit_rate));
|
rmctx.frame_number = (param/(sps*1000*8/rmctx.bit_rate));
|
||||||
while(rmctx.audiotimestamp > (unsigned) ci->seek_time) {
|
while(rmctx.audiotimestamp > (unsigned) param) {
|
||||||
rmctx.audio_pkt_cnt = 0;
|
rmctx.audio_pkt_cnt = 0;
|
||||||
ci->seek_buffer(rmctx.data_offset + DATA_HEADER_SIZE + consumed * (num_units-1));
|
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);
|
bit_buffer = (uint8_t *) ci->request_buffer(&buff_size, scrambling_unit_size);
|
||||||
|
@ -168,16 +172,19 @@ seek_start :
|
||||||
packet_count += rmctx.audio_pkt_cnt;
|
packet_count += rmctx.audio_pkt_cnt;
|
||||||
num_units--;
|
num_units--;
|
||||||
}
|
}
|
||||||
time_offset = ci->seek_time - rmctx.audiotimestamp;
|
time_offset = param - rmctx.audiotimestamp;
|
||||||
i = (time_offset/((sps * 8 * 1000)/rmctx.bit_rate));
|
i = (time_offset/((sps * 8 * 1000)/rmctx.bit_rate));
|
||||||
elapsed = rmctx.audiotimestamp+(1000*8*sps/rmctx.bit_rate)*i;
|
elapsed = rmctx.audiotimestamp+(1000*8*sps/rmctx.bit_rate)*i;
|
||||||
ci->set_elapsed(elapsed);
|
ci->set_elapsed(elapsed);
|
||||||
ci->seek_complete();
|
ci->seek_complete();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
action = CODEC_ACTION_NULL;
|
||||||
|
|
||||||
if(pkt.length)
|
if(pkt.length)
|
||||||
res = atrac3_decode_frame(rmctx.block_align, &q, &datasize, pkt.frames[i], rmctx.block_align);
|
res = atrac3_decode_frame(rmctx.block_align, &q, &datasize, pkt.frames[i], rmctx.block_align);
|
||||||
else /* indicates that there are no remaining frames */
|
else /* indicates that there are no remaining frames */
|
||||||
goto done;
|
return CODEC_OK;
|
||||||
|
|
||||||
if(res != rmctx.block_align) {
|
if(res != rmctx.block_align) {
|
||||||
DEBUGF("codec error\n");
|
DEBUGF("codec error\n");
|
||||||
|
@ -196,9 +203,5 @@ seek_start :
|
||||||
ci->advance_buffer(consumed);
|
ci->advance_buffer(consumed);
|
||||||
}
|
}
|
||||||
|
|
||||||
done :
|
return CODEC_OK;
|
||||||
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 */
|
/* 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;
|
struct pcm_format format;
|
||||||
uint32_t bytesdone, decodedsamples;
|
uint32_t bytesdone, decodedsamples;
|
||||||
size_t n;
|
size_t n;
|
||||||
|
@ -119,22 +129,13 @@ enum codec_status codec_main(void)
|
||||||
off_t firstblockposn; /* position of the first block in file */
|
off_t firstblockposn; /* position of the first block in file */
|
||||||
const struct pcm_codec *codec;
|
const struct pcm_codec *codec;
|
||||||
int offset = 0;
|
int offset = 0;
|
||||||
|
intptr_t param;
|
||||||
/* Generic codec initialisation */
|
|
||||||
ci->configure(DSP_SET_SAMPLE_DEPTH, PCM_OUTPUT_DEPTH-1);
|
|
||||||
|
|
||||||
next_track:
|
|
||||||
status = CODEC_OK;
|
|
||||||
|
|
||||||
if (codec_init()) {
|
if (codec_init()) {
|
||||||
DEBUGF("codec_init() error\n");
|
DEBUGF("codec_init() error\n");
|
||||||
status = CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
goto exit;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (codec_wait_taginfo() != 0)
|
|
||||||
goto done;
|
|
||||||
|
|
||||||
codec_set_replaygain(ci->id3);
|
codec_set_replaygain(ci->id3);
|
||||||
|
|
||||||
/* Need to save offset for later use (cleared indirectly by advance_buffer) */
|
/* Need to save offset for later use (cleared indirectly by advance_buffer) */
|
||||||
|
@ -145,6 +146,7 @@ next_track:
|
||||||
format.is_little_endian = false;
|
format.is_little_endian = false;
|
||||||
|
|
||||||
/* set format */
|
/* set format */
|
||||||
|
ci->seek_buffer(0);
|
||||||
buf = ci->request_buffer(&n, 24);
|
buf = ci->request_buffer(&n, 24);
|
||||||
if (n < 24 || (memcmp(buf, ".snd", 4) != 0))
|
if (n < 24 || (memcmp(buf, ".snd", 4) != 0))
|
||||||
{
|
{
|
||||||
|
@ -170,8 +172,7 @@ next_track:
|
||||||
if (offset < 24)
|
if (offset < 24)
|
||||||
{
|
{
|
||||||
DEBUGF("CODEC_ERROR: sun audio offset size is small: %d\n", offset);
|
DEBUGF("CODEC_ERROR: sun audio offset size is small: %d\n", offset);
|
||||||
status = CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
/* data size */
|
/* data size */
|
||||||
format.numbytes = get_be32(buf + 8);
|
format.numbytes = get_be32(buf + 8);
|
||||||
|
@ -182,8 +183,7 @@ next_track:
|
||||||
if (format.formattag == AU_FORMAT_UNSUPPORT)
|
if (format.formattag == AU_FORMAT_UNSUPPORT)
|
||||||
{
|
{
|
||||||
DEBUGF("CODEC_ERROR: sun audio unsupport format: %d\n", get_be32(buf + 12));
|
DEBUGF("CODEC_ERROR: sun audio unsupport format: %d\n", get_be32(buf + 12));
|
||||||
status = CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
/* skip sample rate */
|
/* skip sample rate */
|
||||||
format.channels = get_be32(buf + 20);
|
format.channels = get_be32(buf + 20);
|
||||||
|
@ -202,20 +202,17 @@ next_track:
|
||||||
if (!codec)
|
if (!codec)
|
||||||
{
|
{
|
||||||
DEBUGF("CODEC_ERROR: unsupport sun audio format: %x\n", (int)format.formattag);
|
DEBUGF("CODEC_ERROR: unsupport sun audio format: %x\n", (int)format.formattag);
|
||||||
status = CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!codec->set_format(&format))
|
if (!codec->set_format(&format))
|
||||||
{
|
{
|
||||||
status = CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (format.numbytes == 0) {
|
if (format.numbytes == 0) {
|
||||||
DEBUGF("CODEC_ERROR: data size is 0\n");
|
DEBUGF("CODEC_ERROR: data size is 0\n");
|
||||||
status = CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* check chunksize */
|
/* check chunksize */
|
||||||
|
@ -225,8 +222,7 @@ next_track:
|
||||||
if (format.chunksize == 0)
|
if (format.chunksize == 0)
|
||||||
{
|
{
|
||||||
DEBUGF("CODEC_ERROR: chunksize is 0\n");
|
DEBUGF("CODEC_ERROR: chunksize is 0\n");
|
||||||
status = CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency);
|
ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency);
|
||||||
|
@ -236,8 +232,7 @@ next_track:
|
||||||
ci->configure(DSP_SET_STEREO_MODE, STEREO_MONO);
|
ci->configure(DSP_SET_STEREO_MODE, STEREO_MONO);
|
||||||
} else {
|
} else {
|
||||||
DEBUGF("CODEC_ERROR: more than 2 channels\n");
|
DEBUGF("CODEC_ERROR: more than 2 channels\n");
|
||||||
status = CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* make sure we're at the correct offset */
|
/* make sure we're at the correct offset */
|
||||||
|
@ -253,7 +248,6 @@ next_track:
|
||||||
bytesdone = newpos->pos;
|
bytesdone = newpos->pos;
|
||||||
decodedsamples = newpos->samples;
|
decodedsamples = newpos->samples;
|
||||||
}
|
}
|
||||||
ci->seek_complete();
|
|
||||||
} else {
|
} else {
|
||||||
/* already where we need to be */
|
/* already where we need to be */
|
||||||
bytesdone = 0;
|
bytesdone = 0;
|
||||||
|
@ -263,22 +257,29 @@ next_track:
|
||||||
endofstream = 0;
|
endofstream = 0;
|
||||||
|
|
||||||
while (!endofstream) {
|
while (!endofstream) {
|
||||||
ci->yield();
|
enum codec_command_action action = ci->get_command(¶m);
|
||||||
if (ci->stop_codec || ci->new_track) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ci->seek_time) {
|
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. */
|
/* 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);
|
struct pcm_pos *newpos = codec->get_seek_pos(param, PCM_SEEK_TIME, NULL);
|
||||||
|
|
||||||
if (newpos->pos > format.numbytes)
|
if (newpos->pos > format.numbytes)
|
||||||
|
{
|
||||||
|
ci->set_elapsed(ci->id3->length);
|
||||||
|
ci->seek_complete();
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (ci->seek_buffer(firstblockposn + newpos->pos))
|
if (ci->seek_buffer(firstblockposn + newpos->pos))
|
||||||
{
|
{
|
||||||
bytesdone = newpos->pos;
|
bytesdone = newpos->pos;
|
||||||
decodedsamples = newpos->samples;
|
decodedsamples = newpos->samples;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ci->set_elapsed(decodedsamples*1000LL/ci->id3->frequency);
|
||||||
ci->seek_complete();
|
ci->seek_complete();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -290,11 +291,10 @@ next_track:
|
||||||
endofstream = 1;
|
endofstream = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
status = codec->decode(aubuf, n, samples, &bufcount);
|
if (codec->decode(aubuf, n, samples, &bufcount) == CODEC_ERROR)
|
||||||
if (status == CODEC_ERROR)
|
|
||||||
{
|
{
|
||||||
DEBUGF("codec error\n");
|
DEBUGF("codec error\n");
|
||||||
goto done;
|
return CODEC_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
ci->pcmbuf_insert(samples, NULL, bufcount);
|
ci->pcmbuf_insert(samples, NULL, bufcount);
|
||||||
|
@ -308,9 +308,5 @@ next_track:
|
||||||
}
|
}
|
||||||
|
|
||||||
done:
|
done:
|
||||||
if (ci->request_next_track())
|
return CODEC_OK;
|
||||||
goto next_track;
|
|
||||||
|
|
||||||
exit:
|
|
||||||
return status;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,39 +27,45 @@ struct codec_api *ci DATA_ATTR;
|
||||||
extern unsigned char plugin_bss_start[];
|
extern unsigned char plugin_bss_start[];
|
||||||
extern unsigned char plugin_end_addr[];
|
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
|
/* stub, the entry point is called via its reference in __header to
|
||||||
* avoid warning with certain compilers */
|
* avoid warning with certain compilers */
|
||||||
int _start(void) {return 0;}
|
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 (CONFIG_PLATFORM & PLATFORM_NATIVE)
|
||||||
#ifdef USE_IRAM
|
if (reason == CODEC_LOAD)
|
||||||
extern char iramcopy[], iramstart[], iramend[], iedata[], iend[];
|
|
||||||
size_t iram_size = iramend - iramstart;
|
|
||||||
size_t ibss_size = iend - iedata;
|
|
||||||
if (iram_size > 0 || ibss_size > 0)
|
|
||||||
{
|
{
|
||||||
ci->memcpy(iramstart, iramcopy, iram_size);
|
#ifdef USE_IRAM
|
||||||
ci->memset(iedata, 0, ibss_size);
|
extern char iramcopy[], iramstart[], iramend[], iedata[], iend[];
|
||||||
/* make the icache (if it exists) up to date with the new code */
|
size_t iram_size = iramend - iramstart;
|
||||||
ci->cpucache_invalidate();
|
size_t ibss_size = iend - iedata;
|
||||||
/* barrier to prevent reordering iram copy and BSS clearing,
|
if (iram_size > 0 || ibss_size > 0)
|
||||||
* because the BSS segment alias the IRAM copy.
|
{
|
||||||
*/
|
ci->memcpy(iramstart, iramcopy, iram_size);
|
||||||
asm volatile ("" ::: "memory");
|
ci->memset(iedata, 0, ibss_size);
|
||||||
}
|
/* make the icache (if it exists) up to date with the new code */
|
||||||
|
ci->cpucache_invalidate();
|
||||||
|
/* barrier to prevent reordering iram copy and BSS clearing,
|
||||||
|
* because the BSS segment alias the IRAM copy.
|
||||||
|
*/
|
||||||
|
asm volatile ("" ::: "memory");
|
||||||
|
}
|
||||||
#endif /* PLUGIN_USE_IRAM */
|
#endif /* PLUGIN_USE_IRAM */
|
||||||
ci->memset(plugin_bss_start, 0, plugin_end_addr - plugin_bss_start);
|
ci->memset(plugin_bss_start, 0, plugin_end_addr - plugin_bss_start);
|
||||||
/* Some parts of bss may be used via a no-cache alias (at least
|
/* Some parts of bss may be used via a no-cache alias (at least
|
||||||
* portalplayer has this). If we don't clear the cache, those aliases
|
* portalplayer has this). If we don't clear the cache, those aliases
|
||||||
* may read garbage */
|
* may read garbage */
|
||||||
ci->cpucache_invalidate();
|
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)
|
#if defined(CPU_ARM) && (CONFIG_PLATFORM & PLATFORM_NATIVE)
|
||||||
|
|
|
@ -38,9 +38,9 @@ static void init_rm(RMContext *rmctx)
|
||||||
memcpy(rmctx, (void*)(( (intptr_t)ci->id3->id3v2buf + 3 ) &~ 3), sizeof(RMContext));
|
memcpy(rmctx, (void*)(( (intptr_t)ci->id3->id3v2buf + 3 ) &~ 3), sizeof(RMContext));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* this is the codec entry point */
|
/* this is called for each file to process */
|
||||||
enum codec_status codec_main(void)
|
enum codec_status codec_run(void)
|
||||||
{
|
{
|
||||||
static size_t buff_size;
|
static size_t buff_size;
|
||||||
int datasize, res, consumed, i, time_offset;
|
int datasize, res, consumed, i, time_offset;
|
||||||
uint8_t *bit_buffer;
|
uint8_t *bit_buffer;
|
||||||
|
@ -48,16 +48,14 @@ enum codec_status codec_main(void)
|
||||||
uint32_t packet_count;
|
uint32_t packet_count;
|
||||||
int scrambling_unit_size, num_units;
|
int scrambling_unit_size, num_units;
|
||||||
size_t resume_offset;
|
size_t resume_offset;
|
||||||
|
intptr_t param = 0;
|
||||||
|
enum codec_command_action action = CODEC_ACTION_NULL;
|
||||||
|
|
||||||
next_track:
|
|
||||||
if (codec_init()) {
|
if (codec_init()) {
|
||||||
DEBUGF("codec init failed\n");
|
DEBUGF("codec init failed\n");
|
||||||
return CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (codec_wait_taginfo() != 0)
|
|
||||||
goto done;
|
|
||||||
|
|
||||||
resume_offset = ci->id3->offset;
|
resume_offset = ci->id3->offset;
|
||||||
|
|
||||||
codec_set_replaygain(ci->id3);
|
codec_set_replaygain(ci->id3);
|
||||||
|
@ -65,6 +63,8 @@ next_track:
|
||||||
ci->memset(&pkt,0,sizeof(RMPacket));
|
ci->memset(&pkt,0,sizeof(RMPacket));
|
||||||
ci->memset(&q,0,sizeof(COOKContext));
|
ci->memset(&q,0,sizeof(COOKContext));
|
||||||
|
|
||||||
|
ci->seek_buffer(0);
|
||||||
|
|
||||||
init_rm(&rmctx);
|
init_rm(&rmctx);
|
||||||
|
|
||||||
ci->configure(DSP_SET_FREQUENCY, ci->id3->frequency);
|
ci->configure(DSP_SET_FREQUENCY, ci->id3->frequency);
|
||||||
|
@ -87,20 +87,21 @@ next_track:
|
||||||
DEBUGF("failed to initialize cook decoder\n");
|
DEBUGF("failed to initialize cook decoder\n");
|
||||||
return CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* check for a mid-track resume and force a seek time accordingly */
|
/* check for a mid-track resume and force a seek time accordingly */
|
||||||
if(resume_offset > rmctx.data_offset + DATA_HEADER_SIZE) {
|
if(resume_offset > rmctx.data_offset + DATA_HEADER_SIZE) {
|
||||||
resume_offset -= rmctx.data_offset + DATA_HEADER_SIZE;
|
resume_offset -= rmctx.data_offset + DATA_HEADER_SIZE;
|
||||||
num_units = (int)resume_offset / scrambling_unit_size;
|
num_units = (int)resume_offset / scrambling_unit_size;
|
||||||
/* put number of subpackets to skip in resume_offset */
|
/* put number of subpackets to skip in resume_offset */
|
||||||
resume_offset /= (sps + PACKET_HEADER_SIZE);
|
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);
|
ci->set_elapsed(0);
|
||||||
ci->advance_buffer(rmctx.data_offset + DATA_HEADER_SIZE);
|
ci->advance_buffer(rmctx.data_offset + DATA_HEADER_SIZE);
|
||||||
|
|
||||||
/* The main decoder loop */
|
/* The main decoder loop */
|
||||||
seek_start :
|
seek_start :
|
||||||
while(packet_count)
|
while(packet_count)
|
||||||
{
|
{
|
||||||
|
@ -112,18 +113,19 @@ seek_start :
|
||||||
}
|
}
|
||||||
|
|
||||||
for(i = 0; i < rmctx.audio_pkt_cnt*(fs/sps) ; i++)
|
for(i = 0; i < rmctx.audio_pkt_cnt*(fs/sps) ; i++)
|
||||||
{
|
{
|
||||||
ci->yield();
|
if (action == CODEC_ACTION_NULL)
|
||||||
if (ci->stop_codec || ci->new_track)
|
action = ci->get_command(¶m);
|
||||||
goto done;
|
|
||||||
|
|
||||||
if (ci->seek_time) {
|
if (action == CODEC_ACTION_HALT)
|
||||||
ci->set_elapsed(ci->seek_time);
|
return CODEC_OK;
|
||||||
|
|
||||||
|
if (action == CODEC_ACTION_SEEK_TIME) {
|
||||||
/* Do not allow seeking beyond the file's length */
|
/* 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();
|
ci->seek_complete();
|
||||||
goto done;
|
return CODEC_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
ci->seek_buffer(rmctx.data_offset + DATA_HEADER_SIZE);
|
ci->seek_buffer(rmctx.data_offset + DATA_HEADER_SIZE);
|
||||||
|
@ -132,22 +134,24 @@ seek_start :
|
||||||
rmctx.frame_number = 0;
|
rmctx.frame_number = 0;
|
||||||
|
|
||||||
/* Seek to the start of the track */
|
/* Seek to the start of the track */
|
||||||
if (ci->seek_time == 1) {
|
if (param == 0) {
|
||||||
ci->set_elapsed(0);
|
ci->set_elapsed(0);
|
||||||
ci->seek_complete();
|
ci->seek_complete();
|
||||||
|
action = CODEC_ACTION_NULL;
|
||||||
goto seek_start;
|
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);
|
ci->seek_buffer(rmctx.data_offset + DATA_HEADER_SIZE + consumed * num_units);
|
||||||
bit_buffer = (uint8_t *) ci->request_buffer(&buff_size, scrambling_unit_size);
|
bit_buffer = (uint8_t *) ci->request_buffer(&buff_size, scrambling_unit_size);
|
||||||
consumed = rm_get_packet(&bit_buffer, &rmctx, &pkt);
|
consumed = rm_get_packet(&bit_buffer, &rmctx, &pkt);
|
||||||
if(consumed < 0) {
|
if(consumed < 0) {
|
||||||
DEBUGF("rm_get_packet failed\n");
|
DEBUGF("rm_get_packet failed\n");
|
||||||
return CODEC_ERROR;
|
ci->seek_complete();
|
||||||
|
return CODEC_ERROR;
|
||||||
}
|
}
|
||||||
packet_count = rmctx.nb_packets - rmctx.audio_pkt_cnt * num_units;
|
packet_count = rmctx.nb_packets - rmctx.audio_pkt_cnt * num_units;
|
||||||
rmctx.frame_number = ((ci->seek_time)/(sps*1000*8/rmctx.bit_rate));
|
rmctx.frame_number = (param/(sps*1000*8/rmctx.bit_rate));
|
||||||
while(rmctx.audiotimestamp > (unsigned) ci->seek_time) {
|
while(rmctx.audiotimestamp > (unsigned) param) {
|
||||||
rmctx.audio_pkt_cnt = 0;
|
rmctx.audio_pkt_cnt = 0;
|
||||||
ci->seek_buffer(rmctx.data_offset + DATA_HEADER_SIZE + consumed * (num_units-1));
|
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);
|
bit_buffer = (uint8_t *) ci->request_buffer(&buff_size, scrambling_unit_size);
|
||||||
|
@ -155,11 +159,14 @@ seek_start :
|
||||||
packet_count += rmctx.audio_pkt_cnt;
|
packet_count += rmctx.audio_pkt_cnt;
|
||||||
num_units--;
|
num_units--;
|
||||||
}
|
}
|
||||||
time_offset = ci->seek_time - rmctx.audiotimestamp;
|
time_offset = param - rmctx.audiotimestamp;
|
||||||
i = (time_offset/((sps * 8 * 1000)/rmctx.bit_rate));
|
i = (time_offset/((sps * 8 * 1000)/rmctx.bit_rate));
|
||||||
ci->set_elapsed(rmctx.audiotimestamp+(1000*8*sps/rmctx.bit_rate)*i);
|
ci->set_elapsed(rmctx.audiotimestamp+(1000*8*sps/rmctx.bit_rate)*i);
|
||||||
ci->seek_complete();
|
ci->seek_complete();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
action = CODEC_ACTION_NULL;
|
||||||
|
|
||||||
res = cook_decode_frame(&rmctx,&q, rm_outbuf, &datasize, pkt.frames[i], rmctx.block_align);
|
res = cook_decode_frame(&rmctx,&q, rm_outbuf, &datasize, pkt.frames[i], rmctx.block_align);
|
||||||
rmctx.frame_number++;
|
rmctx.frame_number++;
|
||||||
|
|
||||||
|
@ -181,9 +188,5 @@ seek_start :
|
||||||
ci->advance_buffer(consumed);
|
ci->advance_buffer(consumed);
|
||||||
}
|
}
|
||||||
|
|
||||||
done :
|
return CODEC_OK;
|
||||||
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 */
|
/* 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;
|
int8_t *buf;
|
||||||
FLACContext fc;
|
FLACContext fc;
|
||||||
uint32_t samplesdone = 0;
|
uint32_t samplesdone;
|
||||||
uint32_t elapsedtime;
|
uint32_t elapsedtime;
|
||||||
size_t bytesleft;
|
size_t bytesleft;
|
||||||
int consumed;
|
int consumed;
|
||||||
int res;
|
int res;
|
||||||
int frame;
|
int frame;
|
||||||
int retval;
|
intptr_t param;
|
||||||
|
|
||||||
/* Generic codec initialisation */
|
|
||||||
ci->configure(DSP_SET_SAMPLE_DEPTH, FLAC_OUTPUT_DEPTH-1);
|
|
||||||
|
|
||||||
next_track:
|
|
||||||
retval = CODEC_OK;
|
|
||||||
|
|
||||||
if (codec_init()) {
|
if (codec_init()) {
|
||||||
LOGF("FLAC: Error initialising codec\n");
|
LOGF("FLAC: Error initialising codec\n");
|
||||||
retval = CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
goto exit;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (codec_wait_taginfo() != 0)
|
|
||||||
goto done;
|
|
||||||
|
|
||||||
/* Need to save offset for later use (cleared indirectly by flac_init) */
|
/* Need to save offset for later use (cleared indirectly by flac_init) */
|
||||||
samplesdone = ci->id3->offset;
|
samplesdone = ci->id3->offset;
|
||||||
|
|
||||||
if (!flac_init(&fc,ci->id3->first_frame_offset)) {
|
if (!flac_init(&fc,ci->id3->first_frame_offset)) {
|
||||||
LOGF("FLAC: Error initialising codec\n");
|
LOGF("FLAC: Error initialising codec\n");
|
||||||
retval = CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency);
|
ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency);
|
||||||
|
@ -459,35 +459,34 @@ next_track:
|
||||||
STEREO_MONO : STEREO_NONINTERLEAVED);
|
STEREO_MONO : STEREO_NONINTERLEAVED);
|
||||||
codec_set_replaygain(ci->id3);
|
codec_set_replaygain(ci->id3);
|
||||||
|
|
||||||
if (samplesdone) {
|
flac_seek_offset(&fc, samplesdone);
|
||||||
flac_seek_offset(&fc, samplesdone);
|
samplesdone=0;
|
||||||
samplesdone=0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* The main decoding loop */
|
/* The main decoding loop */
|
||||||
frame=0;
|
frame=0;
|
||||||
buf = ci->request_buffer(&bytesleft, MAX_FRAMESIZE);
|
buf = ci->request_buffer(&bytesleft, MAX_FRAMESIZE);
|
||||||
while (bytesleft) {
|
while (bytesleft) {
|
||||||
ci->yield();
|
enum codec_command_action action = ci->get_command(¶m);
|
||||||
if (ci->stop_codec || ci->new_track) {
|
|
||||||
|
if (action == CODEC_ACTION_HALT)
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
|
|
||||||
/* Deal with any pending seek requests */
|
/* Deal with any pending seek requests */
|
||||||
if (ci->seek_time) {
|
if (action == CODEC_ACTION_SEEK_TIME) {
|
||||||
if (flac_seek(&fc,(uint32_t)(((uint64_t)(ci->seek_time-1)
|
if (flac_seek(&fc,(uint32_t)(((uint64_t)param
|
||||||
*ci->id3->frequency)/1000))) {
|
*ci->id3->frequency)/1000))) {
|
||||||
/* Refill the input buffer */
|
/* Refill the input buffer */
|
||||||
buf = ci->request_buffer(&bytesleft, MAX_FRAMESIZE);
|
buf = ci->request_buffer(&bytesleft, MAX_FRAMESIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ci->set_elapsed(param);
|
||||||
ci->seek_complete();
|
ci->seek_complete();
|
||||||
}
|
}
|
||||||
|
|
||||||
if((res=flac_decode_frame(&fc,decoded0,decoded1,buf,
|
if((res=flac_decode_frame(&fc,decoded0,decoded1,buf,
|
||||||
bytesleft,ci->yield)) < 0) {
|
bytesleft,ci->yield)) < 0) {
|
||||||
LOGF("FLAC: Frame %d, error %d\n",frame,res);
|
LOGF("FLAC: Frame %d, error %d\n",frame,res);
|
||||||
retval = CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
consumed=fc.gb.index/8;
|
consumed=fc.gb.index/8;
|
||||||
frame++;
|
frame++;
|
||||||
|
@ -507,14 +506,7 @@ next_track:
|
||||||
|
|
||||||
buf = ci->request_buffer(&bytesleft, MAX_FRAMESIZE);
|
buf = ci->request_buffer(&bytesleft, MAX_FRAMESIZE);
|
||||||
}
|
}
|
||||||
retval = CODEC_OK;
|
|
||||||
|
|
||||||
done:
|
|
||||||
LOGF("FLAC: Decoded %lu samples\n",(unsigned long)samplesdone);
|
LOGF("FLAC: Decoded %lu samples\n",(unsigned long)samplesdone);
|
||||||
|
return CODEC_OK;
|
||||||
if (ci->request_next_track())
|
|
||||||
goto next_track;
|
|
||||||
|
|
||||||
exit:
|
|
||||||
return retval;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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* mallocbuf; // 512K from the start of MP3 buffer
|
||||||
unsigned char* filebuf; // The rest of the 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)
|
int codec_init(void)
|
||||||
{
|
{
|
||||||
mem_ptr = 0;
|
mem_ptr = 0;
|
||||||
|
@ -41,7 +51,7 @@ int codec_init(void)
|
||||||
return 0;
|
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_TRACK_GAIN, id3->track_gain);
|
||||||
ci->configure(DSP_SET_ALBUM_GAIN, id3->album_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);
|
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 */
|
/* 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 */
|
/* Various codec helper functions */
|
||||||
|
|
||||||
int codec_init(void);
|
int codec_init(void);
|
||||||
void codec_set_replaygain(struct mp3entry* id3);
|
void codec_set_replaygain(const struct mp3entry *id3);
|
||||||
int codec_wait_taginfo(void); /* 0 = success */
|
|
||||||
|
|
||||||
#ifdef RB_PROFILE
|
#ifdef RB_PROFILE
|
||||||
void __cyg_profile_func_enter(void *this_fn, void *call_site)
|
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;
|
size_t n;
|
||||||
unsigned char *modfile;
|
unsigned char *modfile;
|
||||||
int old_patterntableposition;
|
int old_patterntableposition;
|
||||||
|
|
||||||
int bytesdone;
|
int bytesdone;
|
||||||
|
intptr_t param;
|
||||||
|
|
||||||
next_track:
|
|
||||||
if (codec_init()) {
|
if (codec_init()) {
|
||||||
return CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (codec_wait_taginfo() != 0)
|
|
||||||
goto request_next_track;
|
|
||||||
|
|
||||||
codec_set_replaygain(ci->id3);
|
codec_set_replaygain(ci->id3);
|
||||||
|
|
||||||
/* Load MOD file */
|
/* 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);
|
ci->seek_buffer(0);
|
||||||
modfile = ci->request_buffer(&n, ci->filesize);
|
modfile = ci->request_buffer(&n, ci->filesize);
|
||||||
if (!modfile || n < (size_t)ci->filesize) {
|
if (!modfile || n < (size_t)ci->filesize) {
|
||||||
|
@ -1268,27 +1258,22 @@ next_track:
|
||||||
initmodplayer();
|
initmodplayer();
|
||||||
loadmod(modfile);
|
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 */
|
/* The main decoder loop */
|
||||||
ci->set_elapsed(0);
|
ci->set_elapsed(0);
|
||||||
bytesdone = 0;
|
bytesdone = 0;
|
||||||
old_patterntableposition = 0;
|
old_patterntableposition = 0;
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
ci->yield();
|
enum codec_command_action action = ci->get_command(¶m);
|
||||||
if (ci->stop_codec || ci->new_track)
|
|
||||||
|
if (action == CODEC_ACTION_HALT)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (ci->seek_time) {
|
if (action == CODEC_ACTION_SEEK_TIME) {
|
||||||
/* New time is ready in ci->seek_time */
|
/* New time is ready in param */
|
||||||
modplayer.patterntableposition = ci->seek_time/1000;
|
modplayer.patterntableposition = param/1000;
|
||||||
modplayer.currentline = 0;
|
modplayer.currentline = 0;
|
||||||
|
ci->set_elapsed(modplayer.patterntableposition*1000+500);
|
||||||
ci->seek_complete();
|
ci->seek_complete();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1305,9 +1290,5 @@ next_track:
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
request_next_track:
|
|
||||||
if (ci->request_next_track())
|
|
||||||
goto next_track;
|
|
||||||
|
|
||||||
return CODEC_OK;
|
return CODEC_OK;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2584,45 +2584,46 @@ static bool enc_init(void)
|
||||||
return true;
|
return true;
|
||||||
} /* enc_init */
|
} /* 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())
|
if (!enc_init())
|
||||||
return CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
|
}
|
||||||
/* main encoding loop */
|
else if (reason == CODEC_UNLOAD) {
|
||||||
while (!ci->stop_codec)
|
/* reset parameters to initial state */
|
||||||
{
|
ci->enc_set_parameters(NULL);
|
||||||
char *buffer;
|
|
||||||
|
|
||||||
while ((buffer = ci->enc_get_pcm_data(pcm_chunk_size)) != NULL)
|
|
||||||
{
|
|
||||||
struct enc_chunk_hdr *chunk;
|
|
||||||
|
|
||||||
if (ci->stop_codec)
|
|
||||||
break;
|
|
||||||
|
|
||||||
chunk = ci->enc_get_chunk();
|
|
||||||
chunk->enc_data = ENC_CHUNK_SKIP_HDR(chunk->enc_data, chunk);
|
|
||||||
|
|
||||||
encode_frame(buffer, chunk);
|
|
||||||
|
|
||||||
if (chunk->num_pcm < samp_per_frame)
|
|
||||||
{
|
|
||||||
ci->enc_unget_pcm_data(pcm_chunk_size - chunk->num_pcm*4);
|
|
||||||
chunk->num_pcm = samp_per_frame;
|
|
||||||
}
|
|
||||||
|
|
||||||
ci->enc_finish_chunk();
|
|
||||||
|
|
||||||
ci->yield();
|
|
||||||
}
|
|
||||||
|
|
||||||
ci->yield();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* reset parameters to initial state */
|
return CODEC_OK;
|
||||||
ci->enc_set_parameters(NULL);
|
}
|
||||||
|
|
||||||
|
/* this is called for each file to process */
|
||||||
|
enum codec_status codec_run(void)
|
||||||
|
{
|
||||||
|
/* main encoding loop */
|
||||||
|
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(buffer == NULL)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
chunk = ci->enc_get_chunk();
|
||||||
|
chunk->enc_data = ENC_CHUNK_SKIP_HDR(chunk->enc_data, chunk);
|
||||||
|
|
||||||
|
encode_frame(buffer, chunk);
|
||||||
|
|
||||||
|
if (chunk->num_pcm < samp_per_frame)
|
||||||
|
{
|
||||||
|
ci->enc_unget_pcm_data(pcm_chunk_size - chunk->num_pcm*4);
|
||||||
|
chunk->num_pcm = samp_per_frame;
|
||||||
|
}
|
||||||
|
|
||||||
|
ci->enc_finish_chunk();
|
||||||
|
}
|
||||||
|
|
||||||
return CODEC_OK;
|
return CODEC_OK;
|
||||||
} /* codec_start */
|
}
|
||||||
|
|
|
@ -268,8 +268,8 @@ static bool mad_synth_thread_create(void)
|
||||||
|
|
||||||
static void mad_synth_thread_quit(void)
|
static void mad_synth_thread_quit(void)
|
||||||
{
|
{
|
||||||
/*mop up COP thread*/
|
/* mop up COP thread */
|
||||||
die=1;
|
die = 1;
|
||||||
ci->semaphore_release(&synth_pending_sem);
|
ci->semaphore_release(&synth_pending_sem);
|
||||||
ci->thread_wait(mad_synth_thread_id);
|
ci->thread_wait(mad_synth_thread_id);
|
||||||
ci->cpucache_invalidate();
|
ci->cpucache_invalidate();
|
||||||
|
@ -299,9 +299,30 @@ static inline void mad_synth_thread_unwait_pcm(void)
|
||||||
#endif /* MPA_SYNTH_ON_COP */
|
#endif /* MPA_SYNTH_ON_COP */
|
||||||
|
|
||||||
/* this is the codec entry point */
|
/* 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;
|
size_t size;
|
||||||
int file_end;
|
int file_end;
|
||||||
int samples_to_skip; /* samples to skip in total for this file (at start) */
|
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;
|
unsigned long current_frequency = 0;
|
||||||
int framelength;
|
int framelength;
|
||||||
int padding = MAD_BUFFER_GUARD; /* to help mad decode the last frame */
|
int padding = MAD_BUFFER_GUARD; /* to help mad decode the last frame */
|
||||||
|
intptr_t param;
|
||||||
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;
|
|
||||||
|
|
||||||
/* Reinitializing seems to be necessary to avoid playback quircks when seeking. */
|
/* Reinitializing seems to be necessary to avoid playback quircks when seeking. */
|
||||||
init_mad();
|
init_mad();
|
||||||
|
|
||||||
file_end = 0;
|
file_end = 0;
|
||||||
if (codec_wait_taginfo() != 0)
|
|
||||||
goto request_next_track;
|
|
||||||
|
|
||||||
ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency);
|
ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency);
|
||||||
current_frequency = ci->id3->frequency;
|
current_frequency = ci->id3->frequency;
|
||||||
|
@ -379,29 +385,35 @@ next_track:
|
||||||
|
|
||||||
/* This is the decoding loop. */
|
/* This is the decoding loop. */
|
||||||
while (1) {
|
while (1) {
|
||||||
ci->yield();
|
enum codec_command_action action = ci->get_command(¶m);
|
||||||
if (ci->stop_codec || ci->new_track)
|
|
||||||
|
if (action == CODEC_ACTION_HALT)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (ci->seek_time) {
|
if (action == CODEC_ACTION_SEEK_TIME) {
|
||||||
int newpos;
|
int newpos;
|
||||||
|
|
||||||
/*make sure the synth thread is idle before seeking - MT only*/
|
/*make sure the synth thread is idle before seeking - MT only*/
|
||||||
mad_synth_thread_wait_pcm();
|
mad_synth_thread_wait_pcm();
|
||||||
mad_synth_thread_unwait_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;
|
newpos = ci->id3->first_frame_offset;
|
||||||
samples_to_skip = start_skip;
|
samples_to_skip = start_skip;
|
||||||
} else {
|
} else {
|
||||||
newpos = get_file_pos(ci->seek_time-1);
|
newpos = get_file_pos(param);
|
||||||
samples_to_skip = 0;
|
samples_to_skip = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ci->seek_buffer(newpos))
|
if (!ci->seek_buffer(newpos))
|
||||||
|
{
|
||||||
|
ci->seek_complete();
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ci->set_elapsed((samplesdone * 1000) / current_frequency);
|
||||||
ci->seek_complete();
|
ci->seek_complete();
|
||||||
init_mad();
|
init_mad();
|
||||||
framelength = 0;
|
framelength = 0;
|
||||||
|
@ -435,8 +447,7 @@ next_track:
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
/* Some other unrecoverable error */
|
/* Some other unrecoverable error */
|
||||||
status = CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -504,12 +515,5 @@ next_track:
|
||||||
framelength - stop_skip);
|
framelength - stop_skip);
|
||||||
}
|
}
|
||||||
|
|
||||||
request_next_track:
|
return CODEC_OK;
|
||||||
if (ci->request_next_track())
|
|
||||||
goto next_track;
|
|
||||||
|
|
||||||
/*mop up COP thread - MT only*/
|
|
||||||
mad_synth_thread_quit();
|
|
||||||
|
|
||||||
return status;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,8 +52,20 @@ static mpc_int32_t get_size_impl(mpc_reader *reader)
|
||||||
return ci->filesize;
|
return ci->filesize;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This is the codec entry point. */
|
/* 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) {
|
||||||
|
/* 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;
|
mpc_int64_t samplesdone;
|
||||||
uint32_t frequency; /* 0.1 kHz accuracy */
|
uint32_t frequency; /* 0.1 kHz accuracy */
|
||||||
|
@ -64,39 +76,27 @@ enum codec_status codec_main(void)
|
||||||
mpc_streaminfo info;
|
mpc_streaminfo info;
|
||||||
mpc_frame_info frame;
|
mpc_frame_info frame;
|
||||||
mpc_demux *demux = NULL;
|
mpc_demux *demux = NULL;
|
||||||
int retval;
|
intptr_t param;
|
||||||
|
|
||||||
frame.buffer = sample_buffer;
|
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 */
|
/* Create a decoder instance */
|
||||||
reader.read = read_impl;
|
reader.read = read_impl;
|
||||||
reader.seek = seek_impl;
|
reader.seek = seek_impl;
|
||||||
reader.tell = tell_impl;
|
reader.tell = tell_impl;
|
||||||
reader.get_size = get_size_impl;
|
reader.get_size = get_size_impl;
|
||||||
|
|
||||||
next_track:
|
|
||||||
retval = CODEC_OK;
|
|
||||||
|
|
||||||
if (codec_init())
|
if (codec_init())
|
||||||
{
|
return CODEC_ERROR;
|
||||||
retval = CODEC_ERROR;
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (codec_wait_taginfo() != 0)
|
/* Prep position */
|
||||||
goto done;
|
ci->seek_buffer(0);
|
||||||
|
|
||||||
/* Initialize demux/decoder. */
|
/* Initialize demux/decoder. */
|
||||||
demux = mpc_demux_init(&reader);
|
demux = mpc_demux_init(&reader);
|
||||||
if (NULL == demux)
|
if (NULL == demux)
|
||||||
{
|
return CODEC_ERROR;
|
||||||
retval = CODEC_ERROR;
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
/* Read file's streaminfo data. */
|
/* Read file's streaminfo data. */
|
||||||
mpc_demux_get_info(demux, &info);
|
mpc_demux_get_info(demux, &info);
|
||||||
|
|
||||||
|
@ -117,11 +117,8 @@ next_track:
|
||||||
ci->configure(DSP_SET_STEREO_MODE, STEREO_NONINTERLEAVED);
|
ci->configure(DSP_SET_STEREO_MODE, STEREO_NONINTERLEAVED);
|
||||||
else if (info.channels == 1)
|
else if (info.channels == 1)
|
||||||
ci->configure(DSP_SET_STEREO_MODE, STEREO_MONO);
|
ci->configure(DSP_SET_STEREO_MODE, STEREO_MONO);
|
||||||
else
|
else
|
||||||
{
|
return CODEC_ERROR;
|
||||||
retval = CODEC_ERROR;
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
|
|
||||||
codec_set_replaygain(ci->id3);
|
codec_set_replaygain(ci->id3);
|
||||||
|
|
||||||
|
@ -142,21 +139,24 @@ next_track:
|
||||||
/* This is the decoding loop. */
|
/* This is the decoding loop. */
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
|
enum codec_command_action action = ci->get_command(¶m);
|
||||||
|
|
||||||
|
if (action == CODEC_ACTION_HALT)
|
||||||
|
return CODEC_OK;
|
||||||
|
|
||||||
/* Complete seek handler. */
|
/* 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)
|
if (mpc_demux_seek_sample(demux, new_offset) == MPC_STATUS_OK)
|
||||||
{
|
{
|
||||||
samplesdone = new_offset;
|
samplesdone = new_offset;
|
||||||
ci->set_elapsed(ci->seek_time);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
elapsed_time = (samplesdone*10)/frequency;
|
||||||
|
ci->set_elapsed(elapsed_time);
|
||||||
ci->seek_complete();
|
ci->seek_complete();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Stop or skip occured, exit decoding loop. */
|
|
||||||
if (ci->stop_codec || ci->new_track)
|
|
||||||
break;
|
|
||||||
|
|
||||||
/* Decode one frame. */
|
/* Decode one frame. */
|
||||||
status = mpc_demux_decode(demux, &frame);
|
status = mpc_demux_decode(demux, &frame);
|
||||||
|
@ -164,8 +164,7 @@ next_track:
|
||||||
if (frame.bits == -1)
|
if (frame.bits == -1)
|
||||||
{
|
{
|
||||||
/* Decoding error, exit decoding loop. */
|
/* Decoding error, exit decoding loop. */
|
||||||
retval = (status == MPC_STATUS_OK) ? CODEC_OK : CODEC_ERROR;
|
return (status == MPC_STATUS_OK) ? CODEC_OK : CODEC_ERROR;
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -181,11 +180,4 @@ next_track:
|
||||||
ci->set_offset( (samplesdone * byterate)/(frequency*100) );
|
ci->set_offset( (samplesdone * byterate)/(frequency*100) );
|
||||||
}
|
}
|
||||||
} while (true);
|
} 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;
|
nSilenceTrackMS=5000;
|
||||||
SetFadeTime(track,track+fade, fNSFPlaybackSpeed,def);
|
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 */
|
/* 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;
|
int written;
|
||||||
uint8_t *buf;
|
uint8_t *buf;
|
||||||
size_t n;
|
size_t n;
|
||||||
int endofstream; /* end of stream flag */
|
int endofstream; /* end of stream flag */
|
||||||
int track;
|
int usingplaylist = 0;
|
||||||
int dontresettrack;
|
|
||||||
char last_path[MAX_PATH];
|
|
||||||
int usingplaylist;
|
|
||||||
|
|
||||||
/* 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");
|
DEBUGF("NSF: next_track\n");
|
||||||
if (codec_init()) {
|
if (codec_init()) {
|
||||||
return CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
}
|
}
|
||||||
DEBUGF("NSF: after init\n");
|
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);
|
codec_set_replaygain(ci->id3);
|
||||||
|
|
||||||
/* Read the entire file */
|
/* Read the entire file */
|
||||||
|
@ -4408,22 +4406,27 @@ init_nsf:
|
||||||
reset_profile_timers();
|
reset_profile_timers();
|
||||||
|
|
||||||
while (!endofstream) {
|
while (!endofstream) {
|
||||||
|
intptr_t param;
|
||||||
|
enum codec_command_action action = ci->get_command(¶m);
|
||||||
|
|
||||||
ci->yield();
|
if (action == CODEC_ACTION_HALT)
|
||||||
if (ci->stop_codec || ci->new_track) {
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
|
|
||||||
if (ci->seek_time >0) {
|
if (action == CODEC_ACTION_SEEK_TIME) {
|
||||||
track=ci->seek_time/1000;
|
if (param > 0) {
|
||||||
if (usingplaylist) {
|
track=param/1000;
|
||||||
if (track>=nPlaylistSize) break;
|
if (usingplaylist) {
|
||||||
} else {
|
if (track>=nPlaylistSize) break;
|
||||||
if (track>=nTrackCount) break;
|
} else {
|
||||||
|
if (track>=nTrackCount) break;
|
||||||
|
}
|
||||||
|
dontresettrack=1;
|
||||||
|
ci->seek_complete();
|
||||||
|
goto init_nsf;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ci->seek_complete();
|
||||||
}
|
}
|
||||||
ci->seek_complete();
|
|
||||||
dontresettrack=1;
|
|
||||||
goto init_nsf;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ENTER_TIMER(total);
|
ENTER_TIMER(total);
|
||||||
|
@ -4449,22 +4452,17 @@ init_nsf:
|
||||||
|
|
||||||
print_timers(last_path,track);
|
print_timers(last_path,track);
|
||||||
|
|
||||||
request_next_track:
|
|
||||||
if (ci->request_next_track()) {
|
|
||||||
if (ci->global_settings->repeat_mode==REPEAT_ONE) {
|
if (ci->global_settings->repeat_mode==REPEAT_ONE) {
|
||||||
/* in repeat one mode just advance to the next track */
|
/* in repeat one mode just advance to the next track */
|
||||||
track++;
|
track++;
|
||||||
if (track>=nTrackCount) track=0;
|
if (track>=nTrackCount) track=0;
|
||||||
dontresettrack=1;
|
dontresettrack=1;
|
||||||
/* at this point we can't tell if another file has been selected */
|
/* at this point we can't tell if another file has been selected */
|
||||||
goto next_track;
|
|
||||||
} else {
|
} else {
|
||||||
/* otherwise do a proper load of the next file */
|
/* otherwise do a proper load of the next file */
|
||||||
dontresettrack=0;
|
dontresettrack=0;
|
||||||
last_path[0]='\0';
|
last_path[0]='\0';
|
||||||
}
|
}
|
||||||
goto next_track; /* when we fall through here we'll reload the file */
|
|
||||||
}
|
|
||||||
|
|
||||||
return CODEC_OK;
|
return CODEC_OK;
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,8 +35,21 @@ static void init_rm(RMContext *rmctx)
|
||||||
|
|
||||||
static RMContext rmctx;
|
static RMContext rmctx;
|
||||||
static RMPacket pkt;
|
static RMPacket pkt;
|
||||||
|
|
||||||
/* this is the codec entry point */
|
/* 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;
|
static NeAACDecFrameInfo frame_info;
|
||||||
NeAACDecHandle decoder;
|
NeAACDecHandle decoder;
|
||||||
|
@ -49,26 +62,21 @@ enum codec_status codec_main(void)
|
||||||
unsigned char c = 0; /* channels */
|
unsigned char c = 0; /* channels */
|
||||||
int playback_on = -1;
|
int playback_on = -1;
|
||||||
size_t resume_offset;
|
size_t resume_offset;
|
||||||
|
intptr_t param;
|
||||||
/* Generic codec initialisation */
|
enum codec_command_action action = CODEC_ACTION_NULL;
|
||||||
ci->configure(DSP_SET_STEREO_MODE, STEREO_NONINTERLEAVED);
|
|
||||||
ci->configure(DSP_SET_SAMPLE_DEPTH, 29);
|
|
||||||
|
|
||||||
next_track:
|
|
||||||
err = CODEC_OK;
|
|
||||||
|
|
||||||
if (codec_init()) {
|
if (codec_init()) {
|
||||||
DEBUGF("FAAD: Codec init error\n");
|
DEBUGF("FAAD: Codec init error\n");
|
||||||
return CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (codec_wait_taginfo() != 0)
|
|
||||||
goto done;
|
|
||||||
|
|
||||||
resume_offset = ci->id3->offset;
|
resume_offset = ci->id3->offset;
|
||||||
|
|
||||||
ci->memset(&rmctx,0,sizeof(RMContext));
|
ci->memset(&rmctx,0,sizeof(RMContext));
|
||||||
ci->memset(&pkt,0,sizeof(RMPacket));
|
ci->memset(&pkt,0,sizeof(RMPacket));
|
||||||
|
|
||||||
|
ci->seek_buffer(0);
|
||||||
|
|
||||||
init_rm(&rmctx);
|
init_rm(&rmctx);
|
||||||
ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency);
|
ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency);
|
||||||
codec_set_replaygain(ci->id3);
|
codec_set_replaygain(ci->id3);
|
||||||
|
@ -78,9 +86,9 @@ next_track:
|
||||||
|
|
||||||
if (!decoder) {
|
if (!decoder) {
|
||||||
DEBUGF("FAAD: Decode open error\n");
|
DEBUGF("FAAD: Decode open error\n");
|
||||||
err = CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
goto done;
|
}
|
||||||
}
|
|
||||||
NeAACDecConfigurationPtr conf = NeAACDecGetCurrentConfiguration(decoder);
|
NeAACDecConfigurationPtr conf = NeAACDecGetCurrentConfiguration(decoder);
|
||||||
conf->outputFormat = FAAD_FMT_16BIT; /* irrelevant, we don't convert */
|
conf->outputFormat = FAAD_FMT_16BIT; /* irrelevant, we don't convert */
|
||||||
NeAACDecSetConfiguration(decoder, conf);
|
NeAACDecSetConfiguration(decoder, conf);
|
||||||
|
@ -91,8 +99,7 @@ next_track:
|
||||||
|
|
||||||
if (err) {
|
if (err) {
|
||||||
DEBUGF("FAAD: DecInit: %d, %d\n", err, decoder->object_type);
|
DEBUGF("FAAD: DecInit: %d, %d\n", err, decoder->object_type);
|
||||||
err = CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* check for a mid-track resume and force a seek time accordingly */
|
/* 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;
|
resume_offset -= rmctx.data_offset + DATA_HEADER_SIZE;
|
||||||
/* put number of subpackets to skip in resume_offset */
|
/* put number of subpackets to skip in resume_offset */
|
||||||
resume_offset /= (rmctx.block_align + PACKET_HEADER_SIZE);
|
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->set_elapsed(0);
|
||||||
ci->advance_buffer(rmctx.data_offset + DATA_HEADER_SIZE);
|
ci->advance_buffer(rmctx.data_offset + DATA_HEADER_SIZE);
|
||||||
|
|
||||||
/* The main decoding loop */
|
/* The main decoding loop */
|
||||||
seek_start:
|
|
||||||
while (1) {
|
while (1) {
|
||||||
ci->yield();
|
if (action == CODEC_ACTION_NULL)
|
||||||
if (ci->stop_codec || ci->new_track) {
|
action = ci->get_command(¶m);
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ci->seek_time) {
|
if (action == CODEC_ACTION_HALT)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (action == CODEC_ACTION_SEEK_TIME) {
|
||||||
/* Do not allow seeking beyond the file's length */
|
/* 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();
|
ci->seek_complete();
|
||||||
goto done;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
ci->seek_buffer(rmctx.data_offset + DATA_HEADER_SIZE);
|
ci->seek_buffer(rmctx.data_offset + DATA_HEADER_SIZE);
|
||||||
|
|
||||||
/* Seek to the start of the track */
|
/* Seek to the start of the track */
|
||||||
if (ci->seek_time == 1) {
|
if (param == 0) {
|
||||||
ci->set_elapsed(0);
|
ci->set_elapsed(0);
|
||||||
ci->seek_complete();
|
ci->seek_complete();
|
||||||
goto seek_start;
|
action = CODEC_ACTION_NULL;
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
skipped = 0;
|
skipped = 0;
|
||||||
|
@ -141,21 +150,30 @@ seek_start:
|
||||||
if(playback_on == -1) {
|
if(playback_on == -1) {
|
||||||
/* Error only if packet-parsing failed and playback hadn't started */
|
/* Error only if packet-parsing failed and playback hadn't started */
|
||||||
DEBUGF("rm_get_packet failed\n");
|
DEBUGF("rm_get_packet failed\n");
|
||||||
|
ci->seek_complete();
|
||||||
return CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
}
|
}
|
||||||
else
|
else {
|
||||||
goto done;
|
ci->seek_complete();
|
||||||
|
return CODEC_OK;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
skipped += pkt.length;
|
skipped += pkt.length;
|
||||||
if(pkt.timestamp > (unsigned)ci->seek_time) break;
|
|
||||||
|
if(pkt.timestamp > (unsigned)param)
|
||||||
|
break;
|
||||||
|
|
||||||
ci->advance_buffer(pkt.length);
|
ci->advance_buffer(pkt.length);
|
||||||
}
|
}
|
||||||
ci->seek_buffer(pkt_offset + rmctx.data_offset + DATA_HEADER_SIZE);
|
ci->seek_buffer(pkt_offset + rmctx.data_offset + DATA_HEADER_SIZE);
|
||||||
buffer = ci->request_buffer(&n,rmctx.audio_framesize + 1000);
|
buffer = ci->request_buffer(&n,rmctx.audio_framesize + 1000);
|
||||||
NeAACDecPostSeekReset(decoder, decoder->frame);
|
NeAACDecPostSeekReset(decoder, decoder->frame);
|
||||||
|
ci->set_elapsed(pkt.timestamp);
|
||||||
ci->seek_complete();
|
ci->seek_complete();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
action = CODEC_ACTION_NULL;
|
||||||
|
|
||||||
/* Request the required number of bytes from the input buffer */
|
/* Request the required number of bytes from the input buffer */
|
||||||
buffer=ci->request_buffer(&n,rmctx.audio_framesize + 1000);
|
buffer=ci->request_buffer(&n,rmctx.audio_framesize + 1000);
|
||||||
consumed = rm_get_packet(&buffer, &rmctx, &pkt);
|
consumed = rm_get_packet(&buffer, &rmctx, &pkt);
|
||||||
|
@ -167,20 +185,20 @@ seek_start:
|
||||||
return CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
goto done;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
playback_on = 1;
|
playback_on = 1;
|
||||||
if (pkt.timestamp >= ci->id3->length)
|
if (pkt.timestamp >= ci->id3->length)
|
||||||
goto done;
|
break;
|
||||||
|
|
||||||
/* Decode one block - returned samples will be host-endian */
|
/* Decode one block - returned samples will be host-endian */
|
||||||
for(i = 0; i < rmctx.sub_packet_cnt; i++) {
|
for(i = 0; i < rmctx.sub_packet_cnt; i++) {
|
||||||
ret = NeAACDecDecode(decoder, &frame_info, buffer, rmctx.sub_packet_lengths[i]);
|
ret = NeAACDecDecode(decoder, &frame_info, buffer, rmctx.sub_packet_lengths[i]);
|
||||||
buffer += rmctx.sub_packet_lengths[i];
|
buffer += rmctx.sub_packet_lengths[i];
|
||||||
if (frame_info.error > 0) {
|
if (frame_info.error > 0) {
|
||||||
DEBUGF("FAAD: decode error '%s'\n", NeAACDecGetErrorMessage(frame_info.error));
|
DEBUGF("FAAD: decode error '%s'\n", NeAACDecGetErrorMessage(frame_info.error));
|
||||||
err = CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
goto exit;
|
|
||||||
}
|
}
|
||||||
ci->pcmbuf_insert(decoder->time_out[0],
|
ci->pcmbuf_insert(decoder->time_out[0],
|
||||||
decoder->time_out[1],
|
decoder->time_out[1],
|
||||||
|
@ -191,11 +209,5 @@ seek_start:
|
||||||
ci->advance_buffer(pkt.length);
|
ci->advance_buffer(pkt.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
done:
|
return CODEC_OK;
|
||||||
if (ci->request_next_track())
|
|
||||||
goto next_track;
|
|
||||||
|
|
||||||
exit:
|
|
||||||
return err;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -37,7 +37,19 @@ static int32_t offset1[MAX_OFFSET_SIZE] IBSS_ATTR;
|
||||||
static int8_t ibuf[MAX_BUFFER_SIZE] IBSS_ATTR;
|
static int8_t ibuf[MAX_BUFFER_SIZE] IBSS_ATTR;
|
||||||
|
|
||||||
/* this is the codec entry point */
|
/* 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;
|
ShortenContext sc;
|
||||||
uint32_t samplesdone;
|
uint32_t samplesdone;
|
||||||
|
@ -45,21 +57,14 @@ enum codec_status codec_main(void)
|
||||||
int8_t *buf;
|
int8_t *buf;
|
||||||
int consumed, res, nsamples;
|
int consumed, res, nsamples;
|
||||||
size_t bytesleft;
|
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 */
|
/* Codec initialization */
|
||||||
if (codec_init()) {
|
if (codec_init()) {
|
||||||
LOGF("Shorten: codec_init error\n");
|
LOGF("Shorten: codec_init error\n");
|
||||||
return CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (codec_wait_taginfo() != 0)
|
|
||||||
goto request_next_track;
|
|
||||||
|
|
||||||
codec_set_replaygain(ci->id3);
|
codec_set_replaygain(ci->id3);
|
||||||
|
|
||||||
/* Shorten decoder initialization */
|
/* Shorten decoder initialization */
|
||||||
|
@ -103,14 +108,15 @@ seek_start:
|
||||||
samplesdone = 0;
|
samplesdone = 0;
|
||||||
buf = ci->request_buffer(&bytesleft, MAX_BUFFER_SIZE);
|
buf = ci->request_buffer(&bytesleft, MAX_BUFFER_SIZE);
|
||||||
while (bytesleft) {
|
while (bytesleft) {
|
||||||
ci->yield();
|
enum codec_command_action action = ci->get_command(¶m);
|
||||||
if (ci->stop_codec || ci->new_track) {
|
|
||||||
|
if (action == CODEC_ACTION_HALT)
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
|
|
||||||
/* Seek to start of track */
|
/* Seek to start of track */
|
||||||
if (ci->seek_time == 1) {
|
if (action == CODEC_ACTION_SEEK_TIME) {
|
||||||
if (ci->seek_buffer(sc.header_bits/8 + ci->id3->first_frame_offset)) {
|
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);
|
sc.bitindex = sc.header_bits - 8*(sc.header_bits/8);
|
||||||
ci->set_elapsed(0);
|
ci->set_elapsed(0);
|
||||||
ci->seek_complete();
|
ci->seek_complete();
|
||||||
|
@ -128,7 +134,7 @@ seek_start:
|
||||||
if (res == FN_ERROR) {
|
if (res == FN_ERROR) {
|
||||||
LOGF("Shorten: shorten_decode_frames error (%lu)\n",
|
LOGF("Shorten: shorten_decode_frames error (%lu)\n",
|
||||||
(unsigned long)samplesdone);
|
(unsigned long)samplesdone);
|
||||||
break;
|
return CODEC_ERROR;
|
||||||
} else {
|
} else {
|
||||||
/* Insert decoded samples in pcmbuf */
|
/* Insert decoded samples in pcmbuf */
|
||||||
if (nsamples) {
|
if (nsamples) {
|
||||||
|
@ -153,9 +159,5 @@ seek_start:
|
||||||
sc.bitindex = sc.gb.index - 8*consumed;
|
sc.bitindex = sc.gb.index - 8*consumed;
|
||||||
}
|
}
|
||||||
|
|
||||||
request_next_track:
|
|
||||||
if (ci->request_next_track())
|
|
||||||
goto next_track;
|
|
||||||
|
|
||||||
return CODEC_OK;
|
return CODEC_OK;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1203,34 +1203,47 @@ unsigned short LoadSIDFromMemory(void *pSidData, unsigned short *load_addr,
|
||||||
return *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 int filesize;
|
||||||
|
|
||||||
unsigned short load_addr, init_addr, play_addr;
|
unsigned short load_addr, init_addr, play_addr;
|
||||||
unsigned char subSongsMax, subSong, song_speed;
|
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()) {
|
if (codec_init()) {
|
||||||
return CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (codec_wait_taginfo() != 0)
|
|
||||||
goto request_next_track;
|
|
||||||
|
|
||||||
codec_set_replaygain(ci->id3);
|
codec_set_replaygain(ci->id3);
|
||||||
|
|
||||||
/* Load SID file the read_filebuf callback will return the full requested
|
/* Load SID file the read_filebuf callback will return the full requested
|
||||||
* size if at all possible, so there is no need to loop */
|
* size if at all possible, so there is no need to loop */
|
||||||
|
ci->seek_buffer(0);
|
||||||
filesize = ci->read_filebuf(sidfile, sizeof(sidfile));
|
filesize = ci->read_filebuf(sidfile, sizeof(sidfile));
|
||||||
|
|
||||||
if (filesize == 0)
|
if (filesize == 0) {
|
||||||
return CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
c64Init(44100);
|
c64Init(44100);
|
||||||
LoadSIDFromMemory(sidfile, &load_addr, &init_addr, &play_addr,
|
LoadSIDFromMemory(sidfile, &load_addr, &init_addr, &play_addr,
|
||||||
|
@ -1239,37 +1252,30 @@ next_track:
|
||||||
cpuJSR(init_addr, subSong); /* Start the song initialize */
|
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) */
|
/* Set the elapsed time to the current subsong (in seconds) */
|
||||||
ci->set_elapsed(subSong*1000);
|
ci->set_elapsed(subSong*1000);
|
||||||
|
|
||||||
/* The main decoder loop */
|
/* The main decoder loop */
|
||||||
while (1) {
|
while (1) {
|
||||||
ci->yield();
|
enum codec_command_action action = ci->get_command(¶m);
|
||||||
if (ci->stop_codec || ci->new_track)
|
|
||||||
|
if (action == CODEC_ACTION_HALT)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (ci->seek_time) {
|
if (action == CODEC_ACTION_SEEK_TIME) {
|
||||||
/* New time is ready in ci->seek_time */
|
/* New time is ready in param */
|
||||||
|
|
||||||
/* Start playing from scratch */
|
/* Start playing from scratch */
|
||||||
c64Init(44100);
|
c64Init(44100);
|
||||||
LoadSIDFromMemory(sidfile, &load_addr, &init_addr, &play_addr, &subSongsMax, &subSong, &song_speed, filesize);
|
LoadSIDFromMemory(sidfile, &load_addr, &init_addr, &play_addr,
|
||||||
sidPoke(24, 15); /* Turn on full volume */
|
&subSongsMax, &subSong, &song_speed, filesize);
|
||||||
subSong = ci->seek_time / 1000; /* Now use the current seek time in seconds as subsong */
|
sidPoke(24, 15); /* Turn on full volume */
|
||||||
cpuJSR(init_addr, subSong); /* Start the song initialize */
|
subSong = param / 1000; /* Now use the current seek time in seconds as subsong */
|
||||||
nSamplesToRender = 0; /* Start the rendering from scratch */
|
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) */
|
/* Set the elapsed time to the current subsong (in seconds) */
|
||||||
|
ci->seek_complete();
|
||||||
ci->set_elapsed(subSong*1000);
|
ci->set_elapsed(subSong*1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1306,9 +1312,5 @@ next_track:
|
||||||
ci->pcmbuf_insert(samples, NULL, CHUNK_SIZE);
|
ci->pcmbuf_insert(samples, NULL, CHUNK_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
request_next_track:
|
|
||||||
if (ci->request_next_track())
|
|
||||||
goto next_track;
|
|
||||||
|
|
||||||
return CODEC_OK;
|
return CODEC_OK;
|
||||||
}
|
}
|
||||||
|
|
|
@ -332,9 +332,20 @@ static uint8_t *read_buffer(size_t *realsize)
|
||||||
return buffer;
|
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;
|
uint32_t decodedsamples;
|
||||||
size_t n;
|
size_t n;
|
||||||
int bufcount;
|
int bufcount;
|
||||||
|
@ -342,20 +353,10 @@ enum codec_status codec_main(void)
|
||||||
uint8_t *smafbuf;
|
uint8_t *smafbuf;
|
||||||
off_t firstblockposn; /* position of the first block in file */
|
off_t firstblockposn; /* position of the first block in file */
|
||||||
const struct pcm_codec *codec;
|
const struct pcm_codec *codec;
|
||||||
|
intptr_t param;
|
||||||
/* Generic codec initialisation */
|
|
||||||
ci->configure(DSP_SET_SAMPLE_DEPTH, PCM_OUTPUT_DEPTH-1);
|
|
||||||
|
|
||||||
next_track:
|
if (codec_init())
|
||||||
status = CODEC_OK;
|
return CODEC_ERROR;
|
||||||
|
|
||||||
if (codec_init()) {
|
|
||||||
status = CODEC_ERROR;
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (codec_wait_taginfo() != 0)
|
|
||||||
goto done;
|
|
||||||
|
|
||||||
codec_set_replaygain(ci->id3);
|
codec_set_replaygain(ci->id3);
|
||||||
|
|
||||||
|
@ -365,24 +366,22 @@ next_track:
|
||||||
decodedsamples = 0;
|
decodedsamples = 0;
|
||||||
codec = 0;
|
codec = 0;
|
||||||
|
|
||||||
|
ci->seek_buffer(0);
|
||||||
if (!parse_header(&format, &firstblockposn))
|
if (!parse_header(&format, &firstblockposn))
|
||||||
{
|
{
|
||||||
status = CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
codec = get_codec(format.formattag);
|
codec = get_codec(format.formattag);
|
||||||
if (codec == 0)
|
if (codec == 0)
|
||||||
{
|
{
|
||||||
DEBUGF("CODEC_ERROR: unsupport audio format: 0x%x\n", (int)format.formattag);
|
DEBUGF("CODEC_ERROR: unsupport audio format: 0x%x\n", (int)format.formattag);
|
||||||
status = CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!codec->set_format(&format))
|
if (!codec->set_format(&format))
|
||||||
{
|
{
|
||||||
status = CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* check chunksize */
|
/* check chunksize */
|
||||||
|
@ -392,8 +391,7 @@ next_track:
|
||||||
if (format.chunksize == 0)
|
if (format.chunksize == 0)
|
||||||
{
|
{
|
||||||
DEBUGF("CODEC_ERROR: chunksize is 0\n");
|
DEBUGF("CODEC_ERROR: chunksize is 0\n");
|
||||||
status = CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency);
|
ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency);
|
||||||
|
@ -404,12 +402,10 @@ next_track:
|
||||||
ci->configure(DSP_SET_STEREO_MODE, STEREO_MONO);
|
ci->configure(DSP_SET_STEREO_MODE, STEREO_MONO);
|
||||||
} else {
|
} else {
|
||||||
DEBUGF("CODEC_ERROR: more than 2 channels unsupported\n");
|
DEBUGF("CODEC_ERROR: more than 2 channels unsupported\n");
|
||||||
status = CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ci->seek_buffer(firstblockposn);
|
ci->seek_buffer(firstblockposn);
|
||||||
ci->seek_complete();
|
|
||||||
|
|
||||||
/* make sure we're at the correct offset */
|
/* make sure we're at the correct offset */
|
||||||
if (bytesdone > (uint32_t) firstblockposn)
|
if (bytesdone > (uint32_t) firstblockposn)
|
||||||
|
@ -419,13 +415,13 @@ next_track:
|
||||||
PCM_SEEK_POS, &read_buffer);
|
PCM_SEEK_POS, &read_buffer);
|
||||||
|
|
||||||
if (newpos->pos > format.numbytes)
|
if (newpos->pos > format.numbytes)
|
||||||
goto done;
|
return CODEC_OK;
|
||||||
|
|
||||||
if (ci->seek_buffer(firstblockposn + newpos->pos))
|
if (ci->seek_buffer(firstblockposn + newpos->pos))
|
||||||
{
|
{
|
||||||
bytesdone = newpos->pos;
|
bytesdone = newpos->pos;
|
||||||
decodedsamples = newpos->samples;
|
decodedsamples = newpos->samples;
|
||||||
}
|
}
|
||||||
ci->seek_complete();
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -437,23 +433,32 @@ next_track:
|
||||||
endofstream = 0;
|
endofstream = 0;
|
||||||
|
|
||||||
while (!endofstream) {
|
while (!endofstream) {
|
||||||
ci->yield();
|
enum codec_command_action action = ci->get_command(¶m);
|
||||||
if (ci->stop_codec || ci->new_track)
|
|
||||||
|
if (action == CODEC_ACTION_HALT)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (ci->seek_time) {
|
if (action == CODEC_ACTION_SEEK_TIME) {
|
||||||
struct pcm_pos *newpos = codec->get_seek_pos(ci->seek_time, PCM_SEEK_TIME,
|
struct pcm_pos *newpos = codec->get_seek_pos(param, PCM_SEEK_TIME,
|
||||||
&read_buffer);
|
&read_buffer);
|
||||||
|
|
||||||
if (newpos->pos > format.numbytes)
|
if (newpos->pos > format.numbytes)
|
||||||
|
{
|
||||||
|
ci->set_elapsed(ci->id3->length);
|
||||||
|
ci->seek_complete();
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (ci->seek_buffer(firstblockposn + newpos->pos))
|
if (ci->seek_buffer(firstblockposn + newpos->pos))
|
||||||
{
|
{
|
||||||
bytesdone = newpos->pos;
|
bytesdone = newpos->pos;
|
||||||
decodedsamples = newpos->samples;
|
decodedsamples = newpos->samples;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ci->set_elapsed(decodedsamples*1000LL/ci->id3->frequency);
|
||||||
ci->seek_complete();
|
ci->seek_complete();
|
||||||
}
|
}
|
||||||
|
|
||||||
smafbuf = (uint8_t *)ci->request_buffer(&n, format.chunksize);
|
smafbuf = (uint8_t *)ci->request_buffer(&n, format.chunksize);
|
||||||
|
|
||||||
if (n == 0)
|
if (n == 0)
|
||||||
|
@ -464,11 +469,10 @@ next_track:
|
||||||
endofstream = 1;
|
endofstream = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
status = codec->decode(smafbuf, n, samples, &bufcount);
|
if (codec->decode(smafbuf, n, samples, &bufcount) == CODEC_ERROR)
|
||||||
if (status == CODEC_ERROR)
|
|
||||||
{
|
{
|
||||||
DEBUGF("codec error\n");
|
DEBUGF("codec error\n");
|
||||||
goto done;
|
return CODEC_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
ci->pcmbuf_insert(samples, NULL, bufcount);
|
ci->pcmbuf_insert(samples, NULL, bufcount);
|
||||||
|
@ -482,11 +486,5 @@ next_track:
|
||||||
ci->set_elapsed(decodedsamples*1000LL/ci->id3->frequency);
|
ci->set_elapsed(decodedsamples*1000LL/ci->id3->frequency);
|
||||||
}
|
}
|
||||||
|
|
||||||
done:
|
return CODEC_OK;
|
||||||
if (ci->request_next_track())
|
|
||||||
goto next_track;
|
|
||||||
|
|
||||||
exit:
|
|
||||||
return status;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -260,14 +260,6 @@ static inline void samples_release_rdbuf(void)
|
||||||
static inline int32_t * samples_get_rdbuf(void)
|
static inline int32_t * samples_get_rdbuf(void)
|
||||||
{
|
{
|
||||||
ci->semaphore_wait(&sample_queue.emu_sem_head, TIMEOUT_BLOCK);
|
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;
|
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 */
|
/* obtain filled samples buffer */
|
||||||
*samples = samples_get_rdbuf();
|
return samples_get_rdbuf();
|
||||||
return *samples != NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void spc_play_send_samples(int32_t *samples)
|
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 spc_emu_quit()
|
||||||
#define samples_release_rdbuf()
|
#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);
|
ENTER_TIMER(render);
|
||||||
/* fill samples buffer */
|
/* fill samples buffer */
|
||||||
if ( SPC_play(&spc_emu,WAV_CHUNK_SIZE*2,wav_chunk) )
|
if ( SPC_play(&spc_emu,WAV_CHUNK_SIZE*2,wav_chunk) )
|
||||||
assert( false );
|
assert( false );
|
||||||
EXIT_TIMER(render);
|
EXIT_TIMER(render);
|
||||||
*samples = wav_chunk;
|
return wav_chunk;
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
#endif /* SPC_DUAL_CORE */
|
#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;
|
unsigned long fadeendsample = (ID666.length+ID666.fade)*(long long) SAMPLE_RATE/1000;
|
||||||
int fadedec = 0;
|
int fadedec = 0;
|
||||||
int fadevol = 0x7fffffffl;
|
int fadevol = 0x7fffffffl;
|
||||||
|
intptr_t param;
|
||||||
|
|
||||||
if (fadeendsample>fadestartsample)
|
if (fadeendsample>fadestartsample)
|
||||||
fadedec=0x7fffffffl/(fadeendsample-fadestartsample)+1;
|
fadedec=0x7fffffffl/(fadeendsample-fadestartsample)+1;
|
||||||
|
@ -462,25 +453,26 @@ static int play_track( void )
|
||||||
|
|
||||||
while ( 1 )
|
while ( 1 )
|
||||||
{
|
{
|
||||||
ci->yield();
|
enum codec_command_action action = ci->get_command(¶m);
|
||||||
if (ci->stop_codec || ci->new_track) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ci->seek_time) {
|
if (action == CODEC_ACTION_HALT)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (action == CODEC_ACTION_SEEK_TIME) {
|
||||||
int curtime = sampleswritten*1000LL/SAMPLE_RATE;
|
int curtime = sampleswritten*1000LL/SAMPLE_RATE;
|
||||||
DEBUGF("seek to %ld\ncurrently at %d\n",ci->seek_time,curtime);
|
DEBUGF("seek to %ld\ncurrently at %d\n", (long)param, curtime);
|
||||||
if (ci->seek_time < curtime) {
|
if (param < curtime) {
|
||||||
DEBUGF("seek backwards = reset\n");
|
DEBUGF("seek backwards = reset\n");
|
||||||
|
ci->set_elapsed(0);
|
||||||
ci->seek_complete();
|
ci->seek_complete();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ci->set_elapsed(curtime);
|
||||||
ci->seek_complete();
|
ci->seek_complete();
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t *samples;
|
int32_t *samples = spc_play_get_samples();
|
||||||
if (!spc_play_get_samples(&samples))
|
|
||||||
break;
|
|
||||||
|
|
||||||
sampleswritten += WAV_CHUNK_SIZE;
|
sampleswritten += WAV_CHUNK_SIZE;
|
||||||
|
|
||||||
|
@ -532,67 +524,61 @@ static int play_track( void )
|
||||||
}
|
}
|
||||||
|
|
||||||
/* this is the codec entry point */
|
/* 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())
|
||||||
if (!spc_emu_start())
|
return CODEC_ERROR;
|
||||||
goto codec_quit;
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
DEBUGF("SPC: next_track\n");
|
|
||||||
if (codec_init()) {
|
|
||||||
goto codec_quit;
|
|
||||||
}
|
|
||||||
DEBUGF("SPC: after init\n");
|
|
||||||
|
|
||||||
ci->configure(DSP_SET_SAMPLE_DEPTH, 24);
|
ci->configure(DSP_SET_SAMPLE_DEPTH, 24);
|
||||||
ci->configure(DSP_SET_FREQUENCY, SAMPLE_RATE);
|
ci->configure(DSP_SET_FREQUENCY, SAMPLE_RATE);
|
||||||
ci->configure(DSP_SET_STEREO_MODE, STEREO_NONINTERLEAVED);
|
ci->configure(DSP_SET_STEREO_MODE, STEREO_NONINTERLEAVED);
|
||||||
|
|
||||||
/* wait for track info to load */
|
|
||||||
if (codec_wait_taginfo() != 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
codec_set_replaygain(ci->id3);
|
|
||||||
|
|
||||||
/* Read the entire file */
|
|
||||||
DEBUGF("SPC: request initial buffer\n");
|
|
||||||
ci->seek_buffer(0);
|
|
||||||
size_t buffersize;
|
|
||||||
uint8_t* buffer = ci->request_buffer(&buffersize, ci->filesize);
|
|
||||||
if (!buffer) {
|
|
||||||
goto codec_quit;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
LoadID666(buffer+0x2e);
|
|
||||||
|
|
||||||
if (ci->global_settings->repeat_mode!=REPEAT_ONE && ID666.length==0) {
|
|
||||||
ID666.length=3*60*1000; /* 3 minutes */
|
|
||||||
ID666.fade=5*1000; /* 5 seconds */
|
|
||||||
}
|
|
||||||
|
|
||||||
reset_profile_timers();
|
|
||||||
}
|
|
||||||
while ( play_track() );
|
|
||||||
|
|
||||||
print_timers(ci->id3->path);
|
|
||||||
}
|
}
|
||||||
while ( ci->request_next_track() );
|
else if (reason == CODEC_UNLOAD) {
|
||||||
|
spc_emu_quit();
|
||||||
|
}
|
||||||
|
|
||||||
stat = CODEC_OK;
|
return CODEC_OK;
|
||||||
|
}
|
||||||
codec_quit:
|
|
||||||
spc_emu_quit();
|
/* this is called for each file to process */
|
||||||
|
enum codec_status codec_run(void)
|
||||||
return stat;
|
{
|
||||||
|
DEBUGF("SPC: next_track\n");
|
||||||
|
if (codec_init())
|
||||||
|
return CODEC_ERROR;
|
||||||
|
DEBUGF("SPC: after init\n");
|
||||||
|
|
||||||
|
codec_set_replaygain(ci->id3);
|
||||||
|
|
||||||
|
/* Read the entire file */
|
||||||
|
DEBUGF("SPC: request initial buffer\n");
|
||||||
|
ci->seek_buffer(0);
|
||||||
|
size_t buffersize;
|
||||||
|
uint8_t* buffer = ci->request_buffer(&buffersize, ci->filesize);
|
||||||
|
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");
|
||||||
|
return CODEC_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
LoadID666(buffer+0x2e);
|
||||||
|
|
||||||
|
if (ci->global_settings->repeat_mode!=REPEAT_ONE && ID666.length==0) {
|
||||||
|
ID666.length=3*60*1000; /* 3 minutes */
|
||||||
|
ID666.fade=5*1000; /* 5 seconds */
|
||||||
|
}
|
||||||
|
|
||||||
|
reset_profile_timers();
|
||||||
|
}
|
||||||
|
while ( play_track() );
|
||||||
|
|
||||||
|
print_timers(ci->id3->path);
|
||||||
|
|
||||||
|
return CODEC_OK;
|
||||||
}
|
}
|
||||||
|
|
|
@ -367,11 +367,12 @@ static void *process_header(spx_ogg_packet *op,
|
||||||
return st;
|
return st;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* this is the codec entry point */
|
/* this is called for each file to process */
|
||||||
enum codec_status codec_main(void)
|
enum codec_status codec_run(void)
|
||||||
{
|
{
|
||||||
|
int error = CODEC_ERROR;
|
||||||
|
|
||||||
SpeexBits bits;
|
SpeexBits bits;
|
||||||
int error;
|
|
||||||
int eof = 0;
|
int eof = 0;
|
||||||
spx_ogg_sync_state oy;
|
spx_ogg_sync_state oy;
|
||||||
spx_ogg_page og;
|
spx_ogg_page og;
|
||||||
|
@ -383,7 +384,7 @@ enum codec_status codec_main(void)
|
||||||
int eos = 0;
|
int eos = 0;
|
||||||
SpeexStereoState *stereo;
|
SpeexStereoState *stereo;
|
||||||
int channels = -1;
|
int channels = -1;
|
||||||
int rate = 0, samplerate = 0;
|
int samplerate = 0;
|
||||||
int extra_headers = 0;
|
int extra_headers = 0;
|
||||||
int stream_init = 0;
|
int stream_init = 0;
|
||||||
int page_nb_packets, frame_size, packet_count = 0;
|
int page_nb_packets, frame_size, packet_count = 0;
|
||||||
|
@ -392,26 +393,22 @@ enum codec_status codec_main(void)
|
||||||
unsigned long strtoffset = 0;
|
unsigned long strtoffset = 0;
|
||||||
void *st = NULL;
|
void *st = NULL;
|
||||||
int j = 0;
|
int j = 0;
|
||||||
|
intptr_t param;
|
||||||
|
|
||||||
memset(&bits, 0, sizeof(bits));
|
memset(&bits, 0, sizeof(bits));
|
||||||
memset(&oy, 0, sizeof(oy));
|
memset(&oy, 0, sizeof(oy));
|
||||||
|
|
||||||
/* Ogg handling still uses mallocs, so reset the malloc buffer per track */
|
/* Ogg handling still uses mallocs, so reset the malloc buffer per track */
|
||||||
next_track:
|
|
||||||
error = CODEC_OK;
|
|
||||||
|
|
||||||
if (codec_init()) {
|
if (codec_init()) {
|
||||||
error = CODEC_ERROR;
|
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ci->seek_buffer(0);
|
||||||
|
|
||||||
stereo = speex_stereo_state_init();
|
stereo = speex_stereo_state_init();
|
||||||
spx_ogg_sync_init(&oy);
|
spx_ogg_sync_init(&oy);
|
||||||
spx_ogg_alloc_buffer(&oy,2*CHUNKSIZE);
|
spx_ogg_alloc_buffer(&oy,2*CHUNKSIZE);
|
||||||
|
|
||||||
if (codec_wait_taginfo() != 0)
|
|
||||||
goto done;
|
|
||||||
|
|
||||||
strtoffset = ci->id3->offset;
|
strtoffset = ci->id3->offset;
|
||||||
|
|
||||||
samplerate = ci->id3->frequency;
|
samplerate = ci->id3->frequency;
|
||||||
|
@ -419,30 +416,32 @@ next_track:
|
||||||
|
|
||||||
eof = 0;
|
eof = 0;
|
||||||
while (!eof) {
|
while (!eof) {
|
||||||
ci->yield();
|
enum codec_command_action action = ci->get_command(¶m);
|
||||||
if (ci->stop_codec || ci->new_track)
|
|
||||||
|
if (action == CODEC_ACTION_HALT)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/*seek (seeks to the page before the position) */
|
/*seek (seeks to the page before the position) */
|
||||||
if (ci->seek_time) {
|
if (action == CODEC_ACTION_SEEK_TIME) {
|
||||||
if(samplerate!=0&&packet_count>1){
|
if(samplerate!=0&&packet_count>1){
|
||||||
LOGF("Speex seek page:%lld,%lld,%ld,%lld,%d\n",
|
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,
|
(spx_int64_t)samplerate,
|
||||||
page_granule, ci->seek_time,
|
page_granule, param,
|
||||||
(page_granule/samplerate)*1000, samplerate);
|
(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,
|
(spx_int64_t)samplerate,
|
||||||
page_granule, &oy, headerssize);
|
page_granule, &oy, headerssize);
|
||||||
ci->seek_complete();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ci->set_elapsed(param);
|
||||||
|
ci->seek_complete();
|
||||||
}
|
}
|
||||||
|
|
||||||
next_page:
|
next_page:
|
||||||
/*Get the ogg buffer for writing*/
|
/*Get the ogg buffer for writing*/
|
||||||
if(get_more_data(&oy)<1){/*read error*/
|
if(get_more_data(&oy)<1){/*read error*/
|
||||||
error=CODEC_ERROR;
|
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -477,8 +476,7 @@ next_page:
|
||||||
nframes=1;
|
nframes=1;
|
||||||
|
|
||||||
if (!st){
|
if (!st){
|
||||||
error=CODEC_ERROR;
|
goto done;
|
||||||
goto exit;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ci->configure(DSP_SET_FREQUENCY, ci->id3->frequency);
|
ci->configure(DSP_SET_FREQUENCY, ci->id3->frequency);
|
||||||
|
@ -557,31 +555,18 @@ next_page:
|
||||||
}
|
}
|
||||||
|
|
||||||
done:
|
done:
|
||||||
if (ci->request_next_track()) {
|
/* Clean things up for the next track */
|
||||||
|
speex_bits_destroy(&bits);
|
||||||
/* Clean things up for the next track */
|
|
||||||
|
|
||||||
|
if (st)
|
||||||
if (st)
|
if (st)
|
||||||
speex_decoder_destroy(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)
|
if (stream_init)
|
||||||
spx_ogg_stream_destroy(&os);
|
spx_ogg_stream_destroy(&os);
|
||||||
|
|
||||||
spx_ogg_sync_destroy(&oy);
|
spx_ogg_sync_destroy(&oy);
|
||||||
|
|
||||||
|
exit:
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,36 +34,36 @@ CODEC_HEADER
|
||||||
static int32_t samples[PCM_BUFFER_LENGTH * 2] IBSS_ATTR;
|
static int32_t samples[PCM_BUFFER_LENGTH * 2] IBSS_ATTR;
|
||||||
|
|
||||||
/* this is the codec entry point */
|
/* 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;
|
tta_info info;
|
||||||
int status;
|
|
||||||
unsigned int decodedsamples;
|
unsigned int decodedsamples;
|
||||||
int endofstream;
|
int endofstream;
|
||||||
int new_pos = 0;
|
int new_pos = 0;
|
||||||
int sample_count;
|
int sample_count;
|
||||||
|
intptr_t param;
|
||||||
/* Generic codec initialisation */
|
|
||||||
ci->configure(DSP_SET_SAMPLE_DEPTH, TTA_OUTPUT_DEPTH - 1);
|
|
||||||
|
|
||||||
next_track:
|
|
||||||
status = CODEC_OK;
|
|
||||||
|
|
||||||
if (codec_init())
|
if (codec_init())
|
||||||
{
|
{
|
||||||
DEBUGF("codec_init() error\n");
|
DEBUGF("codec_init() error\n");
|
||||||
status = CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
goto exit;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (codec_wait_taginfo() != 0)
|
ci->seek_buffer(0);
|
||||||
goto done;
|
|
||||||
|
|
||||||
if (set_tta_info(&info) < 0 || player_init(&info) < 0)
|
if (set_tta_info(&info) < 0 || player_init(&info) < 0)
|
||||||
{
|
return CODEC_ERROR;
|
||||||
status = CODEC_ERROR;
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
codec_set_replaygain(ci->id3);
|
codec_set_replaygain(ci->id3);
|
||||||
|
|
||||||
|
@ -74,8 +74,8 @@ next_track:
|
||||||
ci->configure(DSP_SET_STEREO_MODE, STEREO_MONO);
|
ci->configure(DSP_SET_STEREO_MODE, STEREO_MONO);
|
||||||
} else {
|
} else {
|
||||||
DEBUGF("CODEC_ERROR: more than 2 channels\n");
|
DEBUGF("CODEC_ERROR: more than 2 channels\n");
|
||||||
status = CODEC_ERROR;
|
player_stop();
|
||||||
goto done;
|
return CODEC_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* The main decoder loop */
|
/* The main decoder loop */
|
||||||
|
@ -88,31 +88,31 @@ next_track:
|
||||||
new_pos = set_position(ci->id3->offset, TTA_SEEK_POS);
|
new_pos = set_position(ci->id3->offset, TTA_SEEK_POS);
|
||||||
if (new_pos >= 0)
|
if (new_pos >= 0)
|
||||||
decodedsamples = new_pos;
|
decodedsamples = new_pos;
|
||||||
ci->seek_complete();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
while (!endofstream)
|
while (!endofstream)
|
||||||
{
|
{
|
||||||
ci->yield();
|
enum codec_command_action action = ci->get_command(¶m);
|
||||||
if (ci->stop_codec || ci->new_track)
|
|
||||||
|
if (action == CODEC_ACTION_HALT)
|
||||||
break;
|
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)
|
if (new_pos >= 0)
|
||||||
{
|
{
|
||||||
decodedsamples = new_pos;
|
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);
|
sample_count = get_samples(samples);
|
||||||
if (sample_count < 0)
|
if (sample_count < 0)
|
||||||
{
|
|
||||||
status = CODEC_ERROR;
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
ci->pcmbuf_insert(samples, NULL, sample_count);
|
ci->pcmbuf_insert(samples, NULL, sample_count);
|
||||||
decodedsamples += sample_count;
|
decodedsamples += sample_count;
|
||||||
if (decodedsamples >= info.DATALENGTH)
|
if (decodedsamples >= info.DATALENGTH)
|
||||||
|
@ -120,11 +120,6 @@ next_track:
|
||||||
ci->set_elapsed((uint64_t)info.LENGTH * 1000 * decodedsamples / info.DATALENGTH);
|
ci->set_elapsed((uint64_t)info.LENGTH * 1000 * decodedsamples / info.DATALENGTH);
|
||||||
}
|
}
|
||||||
|
|
||||||
done:
|
|
||||||
player_stop();
|
player_stop();
|
||||||
if (ci->request_next_track())
|
return CODEC_OK;
|
||||||
goto next_track;
|
|
||||||
|
|
||||||
exit:
|
|
||||||
return status;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -104,14 +104,25 @@ static bool vorbis_set_codec_parameters(OggVorbis_File *vf)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* this is the codec entry point */
|
/* 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;
|
ov_callbacks callbacks;
|
||||||
OggVorbis_File vf;
|
OggVorbis_File vf;
|
||||||
ogg_int32_t **pcm;
|
ogg_int32_t **pcm;
|
||||||
|
|
||||||
bool initialized = false; /* First init done? */
|
int error = CODEC_ERROR;
|
||||||
int error;
|
|
||||||
long n;
|
long n;
|
||||||
int current_section;
|
int current_section;
|
||||||
int previous_section;
|
int previous_section;
|
||||||
|
@ -120,36 +131,24 @@ enum codec_status codec_main(void)
|
||||||
ogg_int64_t vf_dataoffsets;
|
ogg_int64_t vf_dataoffsets;
|
||||||
ogg_uint32_t vf_serialnos;
|
ogg_uint32_t vf_serialnos;
|
||||||
ogg_int64_t vf_pcmlengths[2];
|
ogg_int64_t vf_pcmlengths[2];
|
||||||
|
intptr_t param;
|
||||||
ci->configure(DSP_SET_SAMPLE_DEPTH, 24);
|
|
||||||
|
|
||||||
if (codec_init()) {
|
|
||||||
error = CODEC_ERROR;
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(CPU_ARM) || defined(CPU_COLDFIRE) || defined(CPU_MIPS)
|
#if defined(CPU_ARM) || defined(CPU_COLDFIRE) || defined(CPU_MIPS)
|
||||||
if (setjmp(rb_jump_buf) != 0) {
|
if (setjmp(rb_jump_buf) != 0) {
|
||||||
/* malloc failed; skip to next track */
|
/* malloc failed; finish with this track */
|
||||||
error = CODEC_ERROR;
|
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
next_track:
|
|
||||||
error = CODEC_OK;
|
|
||||||
|
|
||||||
ogg_malloc_init();
|
ogg_malloc_init();
|
||||||
|
|
||||||
if (codec_wait_taginfo() != 0)
|
|
||||||
goto done;
|
|
||||||
|
|
||||||
/* Create a decoder instance */
|
/* Create a decoder instance */
|
||||||
callbacks.read_func = read_handler;
|
callbacks.read_func = read_handler;
|
||||||
callbacks.seek_func = initial_seek_handler;
|
callbacks.seek_func = initial_seek_handler;
|
||||||
callbacks.tell_func = tell_handler;
|
callbacks.tell_func = tell_handler;
|
||||||
callbacks.close_func = close_handler;
|
callbacks.close_func = close_handler;
|
||||||
|
|
||||||
|
ci->seek_buffer(0);
|
||||||
|
|
||||||
/* Open a non-seekable stream */
|
/* Open a non-seekable stream */
|
||||||
error = ov_open_callbacks(ci, &vf, NULL, 0, callbacks);
|
error = ov_open_callbacks(ci, &vf, NULL, 0, callbacks);
|
||||||
|
|
||||||
|
@ -186,15 +185,13 @@ next_track:
|
||||||
vf.end = ci->id3->filesize;
|
vf.end = ci->id3->filesize;
|
||||||
vf.ready_state = OPENED;
|
vf.ready_state = OPENED;
|
||||||
vf.links = 1;
|
vf.links = 1;
|
||||||
initialized = true;
|
|
||||||
} else {
|
} else {
|
||||||
DEBUGF("Vorbis: ov_open failed: %d\n", error);
|
DEBUGF("Vorbis: ov_open failed: %d\n", error);
|
||||||
error = CODEC_ERROR;
|
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ci->id3->offset) {
|
if (ci->id3->offset) {
|
||||||
ci->advance_buffer(ci->id3->offset);
|
ci->seek_buffer(ci->id3->offset);
|
||||||
ov_raw_seek(&vf, ci->id3->offset);
|
ov_raw_seek(&vf, ci->id3->offset);
|
||||||
ci->set_elapsed(ov_time_tell(&vf));
|
ci->set_elapsed(ov_time_tell(&vf));
|
||||||
ci->set_offset(ov_raw_tell(&vf));
|
ci->set_offset(ov_raw_tell(&vf));
|
||||||
|
@ -203,14 +200,17 @@ next_track:
|
||||||
previous_section = -1;
|
previous_section = -1;
|
||||||
eof = 0;
|
eof = 0;
|
||||||
while (!eof) {
|
while (!eof) {
|
||||||
ci->yield();
|
enum codec_command_action action = ci->get_command(¶m);
|
||||||
if (ci->stop_codec || ci->new_track)
|
|
||||||
|
if (action == CODEC_ACTION_HALT)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (ci->seek_time) {
|
if (action == CODEC_ACTION_SEEK_TIME) {
|
||||||
if (ov_time_seek(&vf, ci->seek_time - 1)) {
|
if (ov_time_seek(&vf, param)) {
|
||||||
//ci->logf("ov_time_seek failed");
|
//ci->logf("ov_time_seek failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ci->set_elapsed(ov_time_tell(&vf));
|
||||||
ci->seek_complete();
|
ci->seek_complete();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -220,7 +220,6 @@ next_track:
|
||||||
/* Change DSP and buffer settings for this bitstream */
|
/* Change DSP and buffer settings for this bitstream */
|
||||||
if (current_section != previous_section) {
|
if (current_section != previous_section) {
|
||||||
if (!vorbis_set_codec_parameters(&vf)) {
|
if (!vorbis_set_codec_parameters(&vf)) {
|
||||||
error = CODEC_ERROR;
|
|
||||||
goto done;
|
goto done;
|
||||||
} else {
|
} else {
|
||||||
previous_section = current_section;
|
previous_section = current_section;
|
||||||
|
@ -238,6 +237,7 @@ next_track:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
error = CODEC_OK;
|
||||||
done:
|
done:
|
||||||
#if 0 /* defined(SIMULATOR) */
|
#if 0 /* defined(SIMULATOR) */
|
||||||
{
|
{
|
||||||
|
@ -249,18 +249,12 @@ done:
|
||||||
#endif
|
#endif
|
||||||
ogg_malloc_destroy();
|
ogg_malloc_destroy();
|
||||||
|
|
||||||
if (ci->request_next_track()) {
|
/* Clean things up for the next track */
|
||||||
if (!initialized)
|
vf.dataoffsets = NULL;
|
||||||
goto next_track;
|
vf.offsets = NULL;
|
||||||
/* Clean things up for the next track */
|
vf.serialnos = NULL;
|
||||||
vf.dataoffsets = NULL;
|
vf.pcmlengths = NULL;
|
||||||
vf.offsets = NULL;
|
ov_clear(&vf);
|
||||||
vf.serialnos = NULL;
|
|
||||||
vf.pcmlengths = NULL;
|
|
||||||
ov_clear(&vf);
|
|
||||||
goto next_track;
|
|
||||||
}
|
|
||||||
|
|
||||||
exit:
|
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,9 +44,19 @@ static uint8_t *read_buffer(size_t *realsize)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* this is the codec entry point */
|
/* 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;
|
uint32_t decodedsamples;
|
||||||
size_t n;
|
size_t n;
|
||||||
int bufcount;
|
int bufcount;
|
||||||
|
@ -54,26 +64,18 @@ enum codec_status codec_main(void)
|
||||||
uint8_t *voxbuf;
|
uint8_t *voxbuf;
|
||||||
off_t firstblockposn = 0; /* position of the first block in file */
|
off_t firstblockposn = 0; /* position of the first block in file */
|
||||||
const struct pcm_codec *codec;
|
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()) {
|
if (codec_init()) {
|
||||||
DEBUGF("codec_init() error\n");
|
DEBUGF("codec_init() error\n");
|
||||||
status = CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
goto exit;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (codec_wait_taginfo() != 0)
|
|
||||||
goto done;
|
|
||||||
|
|
||||||
codec_set_replaygain(ci->id3);
|
codec_set_replaygain(ci->id3);
|
||||||
|
|
||||||
/* Need to save offset for later use (cleared indirectly by advance_buffer) */
|
/* Need to save offset for later use (cleared indirectly by advance_buffer) */
|
||||||
bytesdone = ci->id3->offset;
|
bytesdone = ci->id3->offset;
|
||||||
|
ci->seek_buffer(0);
|
||||||
|
|
||||||
ci->memset(&format, 0, sizeof(struct pcm_format));
|
ci->memset(&format, 0, sizeof(struct pcm_format));
|
||||||
|
|
||||||
|
@ -96,20 +98,16 @@ next_track:
|
||||||
if (!codec)
|
if (!codec)
|
||||||
{
|
{
|
||||||
DEBUGF("CODEC_ERROR: dialogic oki adpcm codec does not load.\n");
|
DEBUGF("CODEC_ERROR: dialogic oki adpcm codec does not load.\n");
|
||||||
status = CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!codec->set_format(&format))
|
if (!codec->set_format(&format)) {
|
||||||
{
|
return CODEC_ERROR;
|
||||||
status = CODEC_ERROR;
|
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (format.numbytes == 0) {
|
if (format.numbytes == 0) {
|
||||||
DEBUGF("CODEC_ERROR: data size is 0\n");
|
DEBUGF("CODEC_ERROR: data size is 0\n");
|
||||||
status = CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* check chunksize */
|
/* check chunksize */
|
||||||
|
@ -118,8 +116,7 @@ next_track:
|
||||||
if (format.chunksize == 0)
|
if (format.chunksize == 0)
|
||||||
{
|
{
|
||||||
DEBUGF("CODEC_ERROR: chunksize is 0\n");
|
DEBUGF("CODEC_ERROR: chunksize is 0\n");
|
||||||
status = CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency);
|
ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency);
|
||||||
|
@ -131,14 +128,14 @@ next_track:
|
||||||
struct pcm_pos *newpos = codec->get_seek_pos(bytesdone - firstblockposn,
|
struct pcm_pos *newpos = codec->get_seek_pos(bytesdone - firstblockposn,
|
||||||
PCM_SEEK_POS, &read_buffer);
|
PCM_SEEK_POS, &read_buffer);
|
||||||
|
|
||||||
if (newpos->pos > format.numbytes)
|
if (newpos->pos > format.numbytes) {
|
||||||
goto done;
|
return CODEC_OK;
|
||||||
|
}
|
||||||
if (ci->seek_buffer(firstblockposn + newpos->pos))
|
if (ci->seek_buffer(firstblockposn + newpos->pos))
|
||||||
{
|
{
|
||||||
bytesdone = newpos->pos;
|
bytesdone = newpos->pos;
|
||||||
decodedsamples = newpos->samples;
|
decodedsamples = newpos->samples;
|
||||||
}
|
}
|
||||||
ci->seek_complete();
|
|
||||||
} else {
|
} else {
|
||||||
/* already where we need to be */
|
/* already where we need to be */
|
||||||
bytesdone = 0;
|
bytesdone = 0;
|
||||||
|
@ -148,22 +145,29 @@ next_track:
|
||||||
endofstream = 0;
|
endofstream = 0;
|
||||||
|
|
||||||
while (!endofstream) {
|
while (!endofstream) {
|
||||||
ci->yield();
|
enum codec_command_action action = ci->get_command(¶m);
|
||||||
if (ci->stop_codec || ci->new_track) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ci->seek_time) {
|
if (action == CODEC_ACTION_HALT)
|
||||||
struct pcm_pos *newpos = codec->get_seek_pos(ci->seek_time, PCM_SEEK_TIME,
|
break;
|
||||||
|
|
||||||
|
if (action == CODEC_ACTION_SEEK_TIME) {
|
||||||
|
struct pcm_pos *newpos = codec->get_seek_pos(param, PCM_SEEK_TIME,
|
||||||
&read_buffer);
|
&read_buffer);
|
||||||
|
|
||||||
if (newpos->pos > format.numbytes)
|
if (newpos->pos > format.numbytes)
|
||||||
|
{
|
||||||
|
ci->set_elapsed(ci->id3->length);
|
||||||
|
ci->seek_complete();
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (ci->seek_buffer(firstblockposn + newpos->pos))
|
if (ci->seek_buffer(firstblockposn + newpos->pos))
|
||||||
{
|
{
|
||||||
bytesdone = newpos->pos;
|
bytesdone = newpos->pos;
|
||||||
decodedsamples = newpos->samples;
|
decodedsamples = newpos->samples;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ci->set_elapsed(decodedsamples*1000LL/ci->id3->frequency);
|
||||||
ci->seek_complete();
|
ci->seek_complete();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -175,11 +179,10 @@ next_track:
|
||||||
endofstream = 1;
|
endofstream = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
status = codec->decode(voxbuf, n, samples, &bufcount);
|
if (codec->decode(voxbuf, n, samples, &bufcount) == CODEC_ERROR)
|
||||||
if (status == CODEC_ERROR)
|
|
||||||
{
|
{
|
||||||
DEBUGF("codec error\n");
|
DEBUGF("codec error\n");
|
||||||
goto done;
|
return CODEC_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
ci->pcmbuf_insert(samples, NULL, bufcount);
|
ci->pcmbuf_insert(samples, NULL, bufcount);
|
||||||
|
@ -192,10 +195,5 @@ next_track:
|
||||||
ci->set_elapsed(decodedsamples*1000LL/ci->id3->frequency);
|
ci->set_elapsed(decodedsamples*1000LL/ci->id3->frequency);
|
||||||
}
|
}
|
||||||
|
|
||||||
done:
|
return CODEC_OK;
|
||||||
if (ci->request_next_track())
|
|
||||||
goto next_track;
|
|
||||||
|
|
||||||
exit:
|
|
||||||
return status;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -151,9 +151,19 @@ static uint8_t *read_buffer(size_t *realsize)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* this is the codec entry point */
|
/* 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;
|
uint32_t decodedsamples;
|
||||||
size_t n;
|
size_t n;
|
||||||
int bufcount;
|
int bufcount;
|
||||||
|
@ -163,38 +173,28 @@ enum codec_status codec_main(void)
|
||||||
off_t firstblockposn; /* position of the first block in file */
|
off_t firstblockposn; /* position of the first block in file */
|
||||||
const struct pcm_codec *codec;
|
const struct pcm_codec *codec;
|
||||||
uint32_t size;
|
uint32_t size;
|
||||||
|
intptr_t param;
|
||||||
/* Generic codec initialisation */
|
|
||||||
ci->configure(DSP_SET_SAMPLE_DEPTH, PCM_OUTPUT_DEPTH-1);
|
|
||||||
|
|
||||||
next_track:
|
|
||||||
status = CODEC_OK;
|
|
||||||
|
|
||||||
if (codec_init()) {
|
if (codec_init()) {
|
||||||
DEBUGF("codec_init() error\n");
|
DEBUGF("codec_init() error\n");
|
||||||
status = CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
goto exit;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (codec_wait_taginfo() != 0)
|
|
||||||
goto done;
|
|
||||||
|
|
||||||
codec_set_replaygain(ci->id3);
|
codec_set_replaygain(ci->id3);
|
||||||
|
|
||||||
/* Need to save offset for later use (cleared indirectly by advance_buffer) */
|
/* Need to save offset for later use (cleared indirectly by advance_buffer) */
|
||||||
bytesdone = ci->id3->offset;
|
bytesdone = ci->id3->offset;
|
||||||
|
|
||||||
/* get RIFF chunk header */
|
/* get RIFF chunk header */
|
||||||
|
ci->seek_buffer(0);
|
||||||
buf = ci->request_buffer(&n, 12);
|
buf = ci->request_buffer(&n, 12);
|
||||||
if (n < 12) {
|
if (n < 12) {
|
||||||
DEBUGF("request_buffer error\n");
|
DEBUGF("request_buffer error\n");
|
||||||
status = CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
if ((memcmp(buf, "RIFF", 4) != 0) || (memcmp(&buf[8], "WAVE", 4) != 0)) {
|
if ((memcmp(buf, "RIFF", 4) != 0) || (memcmp(&buf[8], "WAVE", 4) != 0)) {
|
||||||
DEBUGF("CODEC_ERROR: missing riff header\n");
|
DEBUGF("CODEC_ERROR: missing riff header\n");
|
||||||
status = CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* advance to first WAVE chunk */
|
/* advance to first WAVE chunk */
|
||||||
|
@ -215,8 +215,7 @@ next_track:
|
||||||
if (n < 8) {
|
if (n < 8) {
|
||||||
DEBUGF("data chunk request_buffer error\n");
|
DEBUGF("data chunk request_buffer error\n");
|
||||||
/* no more chunks, 'data' chunk must not have been found */
|
/* no more chunks, 'data' chunk must not have been found */
|
||||||
status = CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* chunkSize */
|
/* chunkSize */
|
||||||
|
@ -225,8 +224,7 @@ next_track:
|
||||||
if (size < 16) {
|
if (size < 16) {
|
||||||
DEBUGF("CODEC_ERROR: 'fmt ' chunk size=%lu < 16\n",
|
DEBUGF("CODEC_ERROR: 'fmt ' chunk size=%lu < 16\n",
|
||||||
(unsigned long)size);
|
(unsigned long)size);
|
||||||
status = CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
/* wFormatTag */
|
/* wFormatTag */
|
||||||
format.formattag=buf[8]|(buf[9]<<8);
|
format.formattag=buf[8]|(buf[9]<<8);
|
||||||
|
@ -256,8 +254,7 @@ next_track:
|
||||||
if (format.size < 22) {
|
if (format.size < 22) {
|
||||||
DEBUGF("CODEC_ERROR: WAVE_FORMAT_EXTENSIBLE is "
|
DEBUGF("CODEC_ERROR: WAVE_FORMAT_EXTENSIBLE is "
|
||||||
"missing extension\n");
|
"missing extension\n");
|
||||||
status = CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
/* wValidBitsPerSample */
|
/* wValidBitsPerSample */
|
||||||
format.bitspersample = buf[26]|(buf[27]<<8);
|
format.bitspersample = buf[26]|(buf[27]<<8);
|
||||||
|
@ -273,8 +270,7 @@ next_track:
|
||||||
{
|
{
|
||||||
if (!set_msadpcm_coeffs(buf))
|
if (!set_msadpcm_coeffs(buf))
|
||||||
{
|
{
|
||||||
status = CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -284,8 +280,7 @@ next_track:
|
||||||
{
|
{
|
||||||
DEBUGF("CODEC_ERROR: unsupported wave format 0x%x\n",
|
DEBUGF("CODEC_ERROR: unsupported wave format 0x%x\n",
|
||||||
(unsigned int) format.formattag);
|
(unsigned int) format.formattag);
|
||||||
status = CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* riff 8bit linear pcm is unsigned */
|
/* riff 8bit linear pcm is unsigned */
|
||||||
|
@ -295,8 +290,7 @@ next_track:
|
||||||
/* set format, parse codec specific tag, check format, and calculate chunk size */
|
/* set format, parse codec specific tag, check format, and calculate chunk size */
|
||||||
if (!codec->set_format(&format))
|
if (!codec->set_format(&format))
|
||||||
{
|
{
|
||||||
status = CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
} else if (memcmp(buf, "data", 4) == 0) {
|
} else if (memcmp(buf, "data", 4) == 0) {
|
||||||
format.numbytes = size;
|
format.numbytes = size;
|
||||||
|
@ -324,31 +318,26 @@ next_track:
|
||||||
if (!codec)
|
if (!codec)
|
||||||
{
|
{
|
||||||
DEBUGF("CODEC_ERROR: 'fmt ' chunk not found\n");
|
DEBUGF("CODEC_ERROR: 'fmt ' chunk not found\n");
|
||||||
status = CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* common format check */
|
/* common format check */
|
||||||
if (format.channels == 0) {
|
if (format.channels == 0) {
|
||||||
DEBUGF("CODEC_ERROR: 'fmt ' chunk not found or 0-channels file\n");
|
DEBUGF("CODEC_ERROR: 'fmt ' chunk not found or 0-channels file\n");
|
||||||
status = CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
if (format.samplesperblock == 0) {
|
if (format.samplesperblock == 0) {
|
||||||
DEBUGF("CODEC_ERROR: 'fmt ' chunk not found or 0-wSamplesPerBlock file\n");
|
DEBUGF("CODEC_ERROR: 'fmt ' chunk not found or 0-wSamplesPerBlock file\n");
|
||||||
status = CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
if (format.blockalign == 0)
|
if (format.blockalign == 0)
|
||||||
{
|
{
|
||||||
DEBUGF("CODEC_ERROR: 'fmt ' chunk not found or 0-blockalign file\n");
|
DEBUGF("CODEC_ERROR: 'fmt ' chunk not found or 0-blockalign file\n");
|
||||||
status = CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
if (format.numbytes == 0) {
|
if (format.numbytes == 0) {
|
||||||
DEBUGF("CODEC_ERROR: 'data' chunk not found or has zero-length\n");
|
DEBUGF("CODEC_ERROR: 'data' chunk not found or has zero-length\n");
|
||||||
status = CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* check chunksize */
|
/* check chunksize */
|
||||||
|
@ -358,8 +347,7 @@ next_track:
|
||||||
if (format.chunksize == 0)
|
if (format.chunksize == 0)
|
||||||
{
|
{
|
||||||
DEBUGF("CODEC_ERROR: chunksize is 0\n");
|
DEBUGF("CODEC_ERROR: chunksize is 0\n");
|
||||||
status = CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency);
|
ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency);
|
||||||
|
@ -369,8 +357,7 @@ next_track:
|
||||||
ci->configure(DSP_SET_STEREO_MODE, STEREO_MONO);
|
ci->configure(DSP_SET_STEREO_MODE, STEREO_MONO);
|
||||||
} else {
|
} else {
|
||||||
DEBUGF("CODEC_ERROR: more than 2 channels\n");
|
DEBUGF("CODEC_ERROR: more than 2 channels\n");
|
||||||
status = CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* make sure we're at the correct offset */
|
/* make sure we're at the correct offset */
|
||||||
|
@ -380,13 +367,12 @@ next_track:
|
||||||
PCM_SEEK_POS, &read_buffer);
|
PCM_SEEK_POS, &read_buffer);
|
||||||
|
|
||||||
if (newpos->pos > format.numbytes)
|
if (newpos->pos > format.numbytes)
|
||||||
goto done;
|
return CODEC_OK;
|
||||||
if (ci->seek_buffer(firstblockposn + newpos->pos))
|
if (ci->seek_buffer(firstblockposn + newpos->pos))
|
||||||
{
|
{
|
||||||
bytesdone = newpos->pos;
|
bytesdone = newpos->pos;
|
||||||
decodedsamples = newpos->samples;
|
decodedsamples = newpos->samples;
|
||||||
}
|
}
|
||||||
ci->seek_complete();
|
|
||||||
} else {
|
} else {
|
||||||
/* already where we need to be */
|
/* already where we need to be */
|
||||||
bytesdone = 0;
|
bytesdone = 0;
|
||||||
|
@ -396,22 +382,28 @@ next_track:
|
||||||
endofstream = 0;
|
endofstream = 0;
|
||||||
|
|
||||||
while (!endofstream) {
|
while (!endofstream) {
|
||||||
ci->yield();
|
enum codec_command_action action = ci->get_command(¶m);
|
||||||
if (ci->stop_codec || ci->new_track) {
|
|
||||||
|
if (action == CODEC_ACTION_HALT)
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
|
|
||||||
if (ci->seek_time) {
|
if (action == CODEC_ACTION_SEEK_TIME) {
|
||||||
struct pcm_pos *newpos = codec->get_seek_pos(ci->seek_time, PCM_SEEK_TIME,
|
struct pcm_pos *newpos = codec->get_seek_pos(param, PCM_SEEK_TIME,
|
||||||
&read_buffer);
|
&read_buffer);
|
||||||
|
|
||||||
if (newpos->pos > format.numbytes)
|
if (newpos->pos > format.numbytes)
|
||||||
|
{
|
||||||
|
ci->set_elapsed(ci->id3->length);
|
||||||
|
ci->seek_complete();
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (ci->seek_buffer(firstblockposn + newpos->pos))
|
if (ci->seek_buffer(firstblockposn + newpos->pos))
|
||||||
{
|
{
|
||||||
bytesdone = newpos->pos;
|
bytesdone = newpos->pos;
|
||||||
decodedsamples = newpos->samples;
|
decodedsamples = newpos->samples;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ci->set_elapsed(decodedsamples*1000LL/ci->id3->frequency);
|
||||||
ci->seek_complete();
|
ci->seek_complete();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -423,11 +415,10 @@ next_track:
|
||||||
endofstream = 1;
|
endofstream = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
status = codec->decode(wavbuf, n, samples, &bufcount);
|
if (codec->decode(wavbuf, n, samples, &bufcount) == CODEC_ERROR)
|
||||||
if (status == CODEC_ERROR)
|
|
||||||
{
|
{
|
||||||
DEBUGF("codec error\n");
|
DEBUGF("codec error\n");
|
||||||
goto done;
|
return CODEC_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
ci->pcmbuf_insert(samples, NULL, bufcount);
|
ci->pcmbuf_insert(samples, NULL, bufcount);
|
||||||
|
@ -440,10 +431,5 @@ next_track:
|
||||||
ci->set_elapsed(decodedsamples*1000LL/ci->id3->frequency);
|
ci->set_elapsed(decodedsamples*1000LL/ci->id3->frequency);
|
||||||
}
|
}
|
||||||
|
|
||||||
done:
|
return CODEC_OK;
|
||||||
if (ci->request_next_track())
|
|
||||||
goto next_track;
|
|
||||||
|
|
||||||
exit:
|
|
||||||
return status;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -159,9 +159,19 @@ static uint8_t *read_buffer(size_t *realsize)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* this is the codec entry point */
|
/* 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;
|
uint32_t decodedsamples;
|
||||||
size_t n;
|
size_t n;
|
||||||
int bufcount;
|
int bufcount;
|
||||||
|
@ -171,39 +181,29 @@ enum codec_status codec_main(void)
|
||||||
off_t firstblockposn; /* position of the first block in file */
|
off_t firstblockposn; /* position of the first block in file */
|
||||||
const struct pcm_codec *codec;
|
const struct pcm_codec *codec;
|
||||||
uint64_t size;
|
uint64_t size;
|
||||||
|
intptr_t param;
|
||||||
/* Generic codec initialisation */
|
|
||||||
ci->configure(DSP_SET_SAMPLE_DEPTH, PCM_OUTPUT_DEPTH-1);
|
|
||||||
|
|
||||||
next_track:
|
|
||||||
status = CODEC_OK;
|
|
||||||
|
|
||||||
if (codec_init()) {
|
if (codec_init()) {
|
||||||
DEBUGF("codec_init() error\n");
|
DEBUGF("codec_init() error\n");
|
||||||
status = CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
goto exit;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (codec_wait_taginfo() != 0)
|
|
||||||
goto done;
|
|
||||||
|
|
||||||
codec_set_replaygain(ci->id3);
|
codec_set_replaygain(ci->id3);
|
||||||
|
|
||||||
/* Need to save offset for later use (cleared indirectly by advance_buffer) */
|
/* Need to save offset for later use (cleared indirectly by advance_buffer) */
|
||||||
bytesdone = ci->id3->offset;
|
bytesdone = ci->id3->offset;
|
||||||
|
|
||||||
/* get RIFF chunk header */
|
/* get RIFF chunk header */
|
||||||
|
ci->seek_buffer(0);
|
||||||
buf = ci->request_buffer(&n, 40);
|
buf = ci->request_buffer(&n, 40);
|
||||||
if (n < 40) {
|
if (n < 40) {
|
||||||
DEBUGF("request_buffer error\n");
|
DEBUGF("request_buffer error\n");
|
||||||
status = CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
if ((memcmp(buf , WAVE64_GUID_RIFF, 16) != 0) ||
|
if ((memcmp(buf , WAVE64_GUID_RIFF, 16) != 0) ||
|
||||||
(memcmp(buf+24, WAVE64_GUID_WAVE, 16) != 0))
|
(memcmp(buf+24, WAVE64_GUID_WAVE, 16) != 0))
|
||||||
{
|
{
|
||||||
status = CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* advance to first WAVE chunk */
|
/* advance to first WAVE chunk */
|
||||||
|
@ -224,8 +224,7 @@ next_track:
|
||||||
if (n < 8) {
|
if (n < 8) {
|
||||||
DEBUGF("data chunk request_buffer error\n");
|
DEBUGF("data chunk request_buffer error\n");
|
||||||
/* no more chunks, 'data' chunk must not have been found */
|
/* no more chunks, 'data' chunk must not have been found */
|
||||||
status = CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* chunkSize */
|
/* chunkSize */
|
||||||
|
@ -233,8 +232,7 @@ next_track:
|
||||||
if (memcmp(buf, WAVE64_GUID_FMT, 16) == 0) {
|
if (memcmp(buf, WAVE64_GUID_FMT, 16) == 0) {
|
||||||
if (size < 16) {
|
if (size < 16) {
|
||||||
DEBUGF("CODEC_ERROR: 'fmt ' chunk size=%d < 16\n", (int)size);
|
DEBUGF("CODEC_ERROR: 'fmt ' chunk size=%d < 16\n", (int)size);
|
||||||
status = CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
/* wFormatTag */
|
/* wFormatTag */
|
||||||
format.formattag=buf[24]|(buf[25]<<8);
|
format.formattag=buf[24]|(buf[25]<<8);
|
||||||
|
@ -263,8 +261,7 @@ next_track:
|
||||||
if (format.size < 22) {
|
if (format.size < 22) {
|
||||||
DEBUGF("CODEC_ERROR: WAVE_FORMAT_EXTENSIBLE is "
|
DEBUGF("CODEC_ERROR: WAVE_FORMAT_EXTENSIBLE is "
|
||||||
"missing extension\n");
|
"missing extension\n");
|
||||||
status = CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
/* wValidBitsPerSample */
|
/* wValidBitsPerSample */
|
||||||
format.bitspersample = buf[42]|(buf[43]<<8);
|
format.bitspersample = buf[42]|(buf[43]<<8);
|
||||||
|
@ -279,10 +276,7 @@ next_track:
|
||||||
if (format.formattag == WAVE_FORMAT_ADPCM)
|
if (format.formattag == WAVE_FORMAT_ADPCM)
|
||||||
{
|
{
|
||||||
if (!set_msadpcm_coeffs(buf))
|
if (!set_msadpcm_coeffs(buf))
|
||||||
{
|
return CODEC_ERROR;
|
||||||
status = CODEC_ERROR;
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* get codec */
|
/* get codec */
|
||||||
|
@ -291,8 +285,7 @@ next_track:
|
||||||
{
|
{
|
||||||
DEBUGF("CODEC_ERROR: unsupported wave format 0x%x\n",
|
DEBUGF("CODEC_ERROR: unsupported wave format 0x%x\n",
|
||||||
(unsigned int) format.formattag);
|
(unsigned int) format.formattag);
|
||||||
status = CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* riff 8bit linear pcm is unsigned */
|
/* riff 8bit linear pcm is unsigned */
|
||||||
|
@ -301,10 +294,7 @@ next_track:
|
||||||
|
|
||||||
/* check format, and calculate chunk size */
|
/* check format, and calculate chunk size */
|
||||||
if (!codec->set_format(&format))
|
if (!codec->set_format(&format))
|
||||||
{
|
return CODEC_ERROR;
|
||||||
status = CODEC_ERROR;
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
} else if (memcmp(buf, WAVE64_GUID_DATA, 16) == 0) {
|
} else if (memcmp(buf, WAVE64_GUID_DATA, 16) == 0) {
|
||||||
format.numbytes = size;
|
format.numbytes = size;
|
||||||
/* advance to start of data */
|
/* advance to start of data */
|
||||||
|
@ -330,31 +320,26 @@ next_track:
|
||||||
if (!codec)
|
if (!codec)
|
||||||
{
|
{
|
||||||
DEBUGF("CODEC_ERROR: 'fmt ' chunk not found\n");
|
DEBUGF("CODEC_ERROR: 'fmt ' chunk not found\n");
|
||||||
status = CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* common format check */
|
/* common format check */
|
||||||
if (format.channels == 0) {
|
if (format.channels == 0) {
|
||||||
DEBUGF("CODEC_ERROR: 'fmt ' chunk not found or 0-channels file\n");
|
DEBUGF("CODEC_ERROR: 'fmt ' chunk not found or 0-channels file\n");
|
||||||
status = CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
if (format.samplesperblock == 0) {
|
if (format.samplesperblock == 0) {
|
||||||
DEBUGF("CODEC_ERROR: 'fmt ' chunk not found or 0-wSamplesPerBlock file\n");
|
DEBUGF("CODEC_ERROR: 'fmt ' chunk not found or 0-wSamplesPerBlock file\n");
|
||||||
status = CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
if (format.blockalign == 0)
|
if (format.blockalign == 0)
|
||||||
{
|
{
|
||||||
DEBUGF("CODEC_ERROR: 'fmt ' chunk not found or 0-blockalign file\n");
|
DEBUGF("CODEC_ERROR: 'fmt ' chunk not found or 0-blockalign file\n");
|
||||||
status = CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
if (format.numbytes == 0) {
|
if (format.numbytes == 0) {
|
||||||
DEBUGF("CODEC_ERROR: 'data' chunk not found or has zero-length\n");
|
DEBUGF("CODEC_ERROR: 'data' chunk not found or has zero-length\n");
|
||||||
status = CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* check chunksize */
|
/* check chunksize */
|
||||||
|
@ -364,8 +349,7 @@ next_track:
|
||||||
if (format.chunksize == 0)
|
if (format.chunksize == 0)
|
||||||
{
|
{
|
||||||
DEBUGF("CODEC_ERROR: chunksize is 0\n");
|
DEBUGF("CODEC_ERROR: chunksize is 0\n");
|
||||||
status = CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency);
|
ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency);
|
||||||
|
@ -375,8 +359,7 @@ next_track:
|
||||||
ci->configure(DSP_SET_STEREO_MODE, STEREO_MONO);
|
ci->configure(DSP_SET_STEREO_MODE, STEREO_MONO);
|
||||||
} else {
|
} else {
|
||||||
DEBUGF("CODEC_ERROR: more than 2 channels\n");
|
DEBUGF("CODEC_ERROR: more than 2 channels\n");
|
||||||
status = CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* make sure we're at the correct offset */
|
/* make sure we're at the correct offset */
|
||||||
|
@ -385,14 +368,14 @@ next_track:
|
||||||
struct pcm_pos *newpos = codec->get_seek_pos(bytesdone - firstblockposn,
|
struct pcm_pos *newpos = codec->get_seek_pos(bytesdone - firstblockposn,
|
||||||
PCM_SEEK_POS, &read_buffer);
|
PCM_SEEK_POS, &read_buffer);
|
||||||
|
|
||||||
if (newpos->pos > format.numbytes)
|
if (newpos->pos > format.numbytes) {
|
||||||
goto done;
|
return CODEC_OK;
|
||||||
|
}
|
||||||
if (ci->seek_buffer(firstblockposn + newpos->pos))
|
if (ci->seek_buffer(firstblockposn + newpos->pos))
|
||||||
{
|
{
|
||||||
bytesdone = newpos->pos;
|
bytesdone = newpos->pos;
|
||||||
decodedsamples = newpos->samples;
|
decodedsamples = newpos->samples;
|
||||||
}
|
}
|
||||||
ci->seek_complete();
|
|
||||||
} else {
|
} else {
|
||||||
/* already where we need to be */
|
/* already where we need to be */
|
||||||
bytesdone = 0;
|
bytesdone = 0;
|
||||||
|
@ -402,22 +385,29 @@ next_track:
|
||||||
endofstream = 0;
|
endofstream = 0;
|
||||||
|
|
||||||
while (!endofstream) {
|
while (!endofstream) {
|
||||||
ci->yield();
|
enum codec_command_action action = ci->get_command(¶m);
|
||||||
if (ci->stop_codec || ci->new_track) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ci->seek_time) {
|
if (action == CODEC_ACTION_HALT)
|
||||||
struct pcm_pos *newpos = codec->get_seek_pos(ci->seek_time, PCM_SEEK_TIME,
|
break;
|
||||||
|
|
||||||
|
if (action == CODEC_ACTION_SEEK_TIME) {
|
||||||
|
struct pcm_pos *newpos = codec->get_seek_pos(param, PCM_SEEK_TIME,
|
||||||
&read_buffer);
|
&read_buffer);
|
||||||
|
|
||||||
if (newpos->pos > format.numbytes)
|
if (newpos->pos > format.numbytes)
|
||||||
|
{
|
||||||
|
ci->set_elapsed(ci->id3->length);
|
||||||
|
ci->seek_complete();
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (ci->seek_buffer(firstblockposn + newpos->pos))
|
if (ci->seek_buffer(firstblockposn + newpos->pos))
|
||||||
{
|
{
|
||||||
bytesdone = newpos->pos;
|
bytesdone = newpos->pos;
|
||||||
decodedsamples = newpos->samples;
|
decodedsamples = newpos->samples;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ci->set_elapsed(decodedsamples*1000LL/ci->id3->frequency);
|
||||||
ci->seek_complete();
|
ci->seek_complete();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -429,11 +419,10 @@ next_track:
|
||||||
endofstream = 1;
|
endofstream = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
status = codec->decode(wavbuf, n, samples, &bufcount);
|
if (codec->decode(wavbuf, n, samples, &bufcount) == CODEC_ERROR)
|
||||||
if (status == CODEC_ERROR)
|
|
||||||
{
|
{
|
||||||
DEBUGF("codec error\n");
|
DEBUGF("codec error\n");
|
||||||
goto done;
|
return CODEC_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
ci->pcmbuf_insert(samples, NULL, bufcount);
|
ci->pcmbuf_insert(samples, NULL, bufcount);
|
||||||
|
@ -445,12 +434,6 @@ next_track:
|
||||||
endofstream = 1;
|
endofstream = 1;
|
||||||
ci->set_elapsed(decodedsamples*1000LL/ci->id3->frequency);
|
ci->set_elapsed(decodedsamples*1000LL/ci->id3->frequency);
|
||||||
}
|
}
|
||||||
status = CODEC_OK;
|
|
||||||
|
|
||||||
done:
|
return CODEC_OK;
|
||||||
if (ci->request_next_track())
|
|
||||||
goto next_track;
|
|
||||||
|
|
||||||
exit:
|
|
||||||
return status;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -345,40 +345,42 @@ static bool init_encoder(void)
|
||||||
return true;
|
return true;
|
||||||
} /* init_encoder */
|
} /* init_encoder */
|
||||||
|
|
||||||
/* main codec entry point */
|
/* this is the codec entry point */
|
||||||
enum codec_status codec_main(void)
|
enum codec_status codec_main(enum codec_entry_call_reason reason)
|
||||||
{
|
{
|
||||||
if (!init_encoder())
|
if (reason == CODEC_LOAD) {
|
||||||
return CODEC_ERROR;
|
if (!init_encoder())
|
||||||
|
return CODEC_ERROR;
|
||||||
/* main encoding loop */
|
}
|
||||||
while(!ci->stop_codec)
|
else if (reason == CODEC_UNLOAD) {
|
||||||
{
|
/* reset parameters to initial state */
|
||||||
uint32_t *src;
|
ci->enc_set_parameters(NULL);
|
||||||
|
|
||||||
while ((src = (uint32_t *)ci->enc_get_pcm_data(PCM_CHUNK_SIZE)) != NULL)
|
|
||||||
{
|
|
||||||
struct enc_chunk_hdr *chunk;
|
|
||||||
|
|
||||||
if (ci->stop_codec)
|
|
||||||
break;
|
|
||||||
|
|
||||||
chunk = ci->enc_get_chunk();
|
|
||||||
chunk->enc_size = enc_size;
|
|
||||||
chunk->num_pcm = PCM_SAMP_PER_CHUNK;
|
|
||||||
chunk->enc_data = ENC_CHUNK_SKIP_HDR(chunk->enc_data, chunk);
|
|
||||||
|
|
||||||
chunk_to_wav_format(src, (uint32_t *)chunk->enc_data);
|
|
||||||
|
|
||||||
ci->enc_finish_chunk();
|
|
||||||
ci->yield();
|
|
||||||
}
|
|
||||||
|
|
||||||
ci->yield();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* reset parameters to initial state */
|
return CODEC_OK;
|
||||||
ci->enc_set_parameters(NULL);
|
}
|
||||||
|
|
||||||
|
/* this is called for each file to process */
|
||||||
|
enum codec_status codec_run(void)
|
||||||
|
{
|
||||||
|
/* main encoding loop */
|
||||||
|
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(src == NULL)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
chunk = ci->enc_get_chunk();
|
||||||
|
chunk->enc_size = enc_size;
|
||||||
|
chunk->num_pcm = PCM_SAMP_PER_CHUNK;
|
||||||
|
chunk->enc_data = ENC_CHUNK_SKIP_HDR(chunk->enc_data, chunk);
|
||||||
|
|
||||||
|
chunk_to_wav_format(src, (uint32_t *)chunk->enc_data);
|
||||||
|
|
||||||
|
ci->enc_finish_chunk();
|
||||||
|
}
|
||||||
|
|
||||||
return CODEC_OK;
|
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)
|
static int32_t read_callback (void *buffer, int32_t bytes)
|
||||||
{
|
{
|
||||||
int32_t retval = ci->read_filebuf (buffer, bytes);
|
int32_t retval = ci->read_filebuf (buffer, bytes);
|
||||||
ci->id3->offset = ci->curpos;
|
ci->set_offset(ci->curpos);
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* this is the codec entry point */
|
/* 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;
|
WavpackContext *wpc;
|
||||||
char error [80];
|
char error [80];
|
||||||
int bps, nchans, sr_100;
|
int bps, nchans, sr_100;
|
||||||
int retval;
|
intptr_t param;
|
||||||
|
|
||||||
/* Generic codec initialisation */
|
if (codec_init())
|
||||||
ci->configure(DSP_SET_SAMPLE_DEPTH, 28);
|
return CODEC_ERROR;
|
||||||
|
|
||||||
next_track:
|
ci->seek_buffer (ci->id3->offset);
|
||||||
retval = CODEC_OK;
|
|
||||||
|
|
||||||
if (codec_init()) {
|
|
||||||
retval = CODEC_ERROR;
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (codec_wait_taginfo() != 0)
|
|
||||||
goto done;
|
|
||||||
|
|
||||||
/* Create a decoder instance */
|
/* Create a decoder instance */
|
||||||
wpc = WavpackOpenFileInput (read_callback, error);
|
wpc = WavpackOpenFileInput (read_callback, error);
|
||||||
|
|
||||||
if (!wpc) {
|
if (!wpc)
|
||||||
retval = CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
|
|
||||||
ci->configure(DSP_SWITCH_FREQUENCY, WavpackGetSampleRate (wpc));
|
ci->configure(DSP_SWITCH_FREQUENCY, WavpackGetSampleRate (wpc));
|
||||||
codec_set_replaygain(ci->id3);
|
codec_set_replaygain(ci->id3);
|
||||||
|
@ -77,56 +77,48 @@ next_track:
|
||||||
/* The main decoder loop */
|
/* The main decoder loop */
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
int32_t nsamples;
|
int32_t nsamples;
|
||||||
|
enum codec_command_action action = ci->get_command(¶m);
|
||||||
|
|
||||||
if (ci->seek_time && ci->taginfo_ready && ci->id3->length) {
|
if (action == CODEC_ACTION_HALT)
|
||||||
ci->seek_time--;
|
break;
|
||||||
|
|
||||||
|
if (action == CODEC_ACTION_SEEK_TIME) {
|
||||||
int curpos_ms = WavpackGetSampleIndex (wpc) / sr_100 * 10;
|
int curpos_ms = WavpackGetSampleIndex (wpc) / sr_100 * 10;
|
||||||
int n, d, skip;
|
int n, d, skip;
|
||||||
|
|
||||||
if (ci->seek_time > curpos_ms) {
|
if (param > curpos_ms) {
|
||||||
n = ci->seek_time - curpos_ms;
|
n = param - curpos_ms;
|
||||||
d = ci->id3->length - curpos_ms;
|
d = ci->id3->length - curpos_ms;
|
||||||
skip = (int)((int64_t)(ci->filesize - ci->curpos) * n / d);
|
skip = (int)((int64_t)(ci->filesize - ci->curpos) * n / d);
|
||||||
ci->seek_buffer (ci->curpos + skip);
|
ci->seek_buffer (ci->curpos + skip);
|
||||||
}
|
}
|
||||||
else {
|
else if (curpos_ms != 0) {
|
||||||
n = curpos_ms - ci->seek_time;
|
n = curpos_ms - param;
|
||||||
d = curpos_ms;
|
d = curpos_ms;
|
||||||
skip = (int)((int64_t) ci->curpos * n / d);
|
skip = (int)((int64_t) ci->curpos * n / d);
|
||||||
ci->seek_buffer (ci->curpos - skip);
|
ci->seek_buffer (ci->curpos - skip);
|
||||||
}
|
}
|
||||||
|
|
||||||
wpc = WavpackOpenFileInput (read_callback, error);
|
wpc = WavpackOpenFileInput (read_callback, error);
|
||||||
ci->seek_complete();
|
|
||||||
|
|
||||||
if (!wpc)
|
if (!wpc)
|
||||||
|
{
|
||||||
|
ci->seek_complete();
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
ci->set_elapsed (WavpackGetSampleIndex (wpc) / sr_100 * 10);
|
ci->set_elapsed (WavpackGetSampleIndex (wpc) / sr_100 * 10);
|
||||||
ci->yield ();
|
ci->seek_complete();
|
||||||
}
|
}
|
||||||
|
|
||||||
nsamples = WavpackUnpackSamples (wpc, temp_buffer, BUFFER_SIZE / nchans);
|
nsamples = WavpackUnpackSamples (wpc, temp_buffer, BUFFER_SIZE / nchans);
|
||||||
|
|
||||||
if (!nsamples || ci->stop_codec || ci->new_track)
|
if (!nsamples)
|
||||||
break;
|
|
||||||
|
|
||||||
ci->yield ();
|
|
||||||
|
|
||||||
if (ci->stop_codec || ci->new_track)
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
ci->pcmbuf_insert (temp_buffer, NULL, nsamples);
|
ci->pcmbuf_insert (temp_buffer, NULL, nsamples);
|
||||||
|
|
||||||
ci->set_elapsed (WavpackGetSampleIndex (wpc) / sr_100 * 10);
|
ci->set_elapsed (WavpackGetSampleIndex (wpc) / sr_100 * 10);
|
||||||
ci->yield ();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
done:
|
return CODEC_OK;
|
||||||
if (ci->request_next_track())
|
|
||||||
goto next_track;
|
|
||||||
|
|
||||||
exit:
|
|
||||||
return retval;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -389,77 +389,79 @@ static bool init_encoder(void)
|
||||||
return true;
|
return true;
|
||||||
} /* init_encoder */
|
} /* 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)
|
||||||
{
|
{
|
||||||
/* initialize params and config */
|
if (reason == CODEC_LOAD) {
|
||||||
if (!init_encoder())
|
/* initialize params and config */
|
||||||
return CODEC_ERROR;
|
if (!init_encoder())
|
||||||
|
return CODEC_ERROR;
|
||||||
/* main encoding loop */
|
}
|
||||||
while(!ci->stop_codec)
|
else if (reason == CODEC_UNLOAD) {
|
||||||
{
|
/* reset parameters to initial state */
|
||||||
uint8_t *src;
|
ci->enc_set_parameters(NULL);
|
||||||
|
|
||||||
while ((src = ci->enc_get_pcm_data(PCM_CHUNK_SIZE)) != NULL)
|
|
||||||
{
|
|
||||||
struct enc_chunk_hdr *chunk;
|
|
||||||
bool abort_chunk;
|
|
||||||
uint8_t *dst;
|
|
||||||
uint8_t *src_end;
|
|
||||||
|
|
||||||
if(ci->stop_codec)
|
|
||||||
break;
|
|
||||||
|
|
||||||
abort_chunk = true;
|
|
||||||
|
|
||||||
chunk = ci->enc_get_chunk();
|
|
||||||
|
|
||||||
/* reset counts and pointer */
|
|
||||||
chunk->enc_size = 0;
|
|
||||||
chunk->num_pcm = 0;
|
|
||||||
chunk->enc_data = NULL;
|
|
||||||
|
|
||||||
dst = ENC_CHUNK_SKIP_HDR(dst, chunk);
|
|
||||||
|
|
||||||
WavpackStartBlock(wpc, dst, dst + data_size);
|
|
||||||
|
|
||||||
chunk_to_int32((uint32_t*)src);
|
|
||||||
src = input_buffer;
|
|
||||||
src_end = src + input_size;
|
|
||||||
|
|
||||||
/* encode chunk in four steps yielding between each */
|
|
||||||
do
|
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
src += input_step;
|
|
||||||
}
|
|
||||||
while (!abort_chunk && src < src_end);
|
|
||||||
|
|
||||||
if (!abort_chunk)
|
|
||||||
{
|
|
||||||
chunk->enc_data = dst;
|
|
||||||
if (chunk->num_pcm < PCM_SAMP_PER_CHUNK)
|
|
||||||
ci->enc_unget_pcm_data(PCM_CHUNK_SIZE - chunk->num_pcm*4);
|
|
||||||
/* finish the chunk and store chunk size info */
|
|
||||||
chunk->enc_size = WavpackFinishBlock(wpc);
|
|
||||||
ci->enc_finish_chunk();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ci->yield();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* reset parameters to initial state */
|
|
||||||
ci->enc_set_parameters(NULL);
|
|
||||||
|
|
||||||
return CODEC_OK;
|
return CODEC_OK;
|
||||||
} /* codec_start */
|
}
|
||||||
|
|
||||||
|
/* this is called for each file to process */
|
||||||
|
enum codec_status codec_run(void)
|
||||||
|
{
|
||||||
|
/* main encoding loop */
|
||||||
|
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(src == NULL)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
chunk = ci->enc_get_chunk();
|
||||||
|
|
||||||
|
/* reset counts and pointer */
|
||||||
|
chunk->enc_size = 0;
|
||||||
|
chunk->num_pcm = 0;
|
||||||
|
chunk->enc_data = NULL;
|
||||||
|
|
||||||
|
dst = ENC_CHUNK_SKIP_HDR(dst, chunk);
|
||||||
|
|
||||||
|
WavpackStartBlock(wpc, dst, dst + data_size);
|
||||||
|
|
||||||
|
chunk_to_int32((uint32_t*)src);
|
||||||
|
src = input_buffer;
|
||||||
|
src_end = src + input_size;
|
||||||
|
|
||||||
|
/* 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 = chunk->flags & CHUNKF_ABORT;
|
||||||
|
}
|
||||||
|
|
||||||
|
src += input_step;
|
||||||
|
}
|
||||||
|
while (!abort_chunk && src < src_end);
|
||||||
|
|
||||||
|
if (!abort_chunk)
|
||||||
|
{
|
||||||
|
chunk->enc_data = dst;
|
||||||
|
if (chunk->num_pcm < PCM_SAMP_PER_CHUNK)
|
||||||
|
ci->enc_unget_pcm_data(PCM_CHUNK_SIZE - chunk->num_pcm*4);
|
||||||
|
/* finish the chunk and store chunk size info */
|
||||||
|
chunk->enc_size = WavpackFinishBlock(wpc);
|
||||||
|
ci->enc_finish_chunk();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return CODEC_OK;
|
||||||
|
}
|
||||||
|
|
|
@ -29,53 +29,52 @@ CODEC_HEADER
|
||||||
static WMADecodeContext wmadec;
|
static WMADecodeContext wmadec;
|
||||||
|
|
||||||
/* this is the codec entry point */
|
/* 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;
|
uint32_t elapsedtime;
|
||||||
int retval;
|
|
||||||
asf_waveformatex_t wfx;
|
asf_waveformatex_t wfx;
|
||||||
size_t resume_offset;
|
size_t resume_offset;
|
||||||
int i;
|
int i;
|
||||||
int wmares, res;
|
int wmares;
|
||||||
|
int res = 0;
|
||||||
uint8_t* audiobuf;
|
uint8_t* audiobuf;
|
||||||
int audiobufsize;
|
int audiobufsize;
|
||||||
int packetlength = 0;
|
int packetlength = 0;
|
||||||
int errcount = 0;
|
int errcount = 0;
|
||||||
|
intptr_t param;
|
||||||
/* Generic codec initialisation */
|
|
||||||
ci->configure(DSP_SET_SAMPLE_DEPTH, 29);
|
|
||||||
|
|
||||||
next_track:
|
|
||||||
retval = CODEC_OK;
|
|
||||||
|
|
||||||
/* Proper reset of the decoder context. */
|
/* Proper reset of the decoder context. */
|
||||||
memset(&wmadec, 0, sizeof(wmadec));
|
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
|
/* Remember the resume position - when the codec is opened, the
|
||||||
playback engine will reset it. */
|
playback engine will reset it. */
|
||||||
resume_offset = ci->id3->offset;
|
resume_offset = ci->id3->offset;
|
||||||
|
|
||||||
restart_track:
|
restart_track:
|
||||||
retval = CODEC_OK;
|
|
||||||
|
|
||||||
if (codec_init()) {
|
if (codec_init()) {
|
||||||
LOGF("WMA: Error initialising codec\n");
|
LOGF("WMA: Error initialising codec\n");
|
||||||
retval = CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
goto exit;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Copy the format metadata we've stored in the id3 TOC field. This
|
/* Copy the format metadata we've stored in the id3 TOC field. This
|
||||||
saves us from parsing it again here. */
|
saves us from parsing it again here. */
|
||||||
memcpy(&wfx, ci->id3->toc, sizeof(wfx));
|
memcpy(&wfx, ci->id3->toc, sizeof(wfx));
|
||||||
|
|
||||||
|
ci->seek_buffer(ci->id3->first_frame_offset);
|
||||||
if (wma_decode_init(&wmadec,&wfx) < 0) {
|
if (wma_decode_init(&wmadec,&wfx) < 0) {
|
||||||
LOGF("WMA: Unsupported or corrupt file\n");
|
LOGF("WMA: Unsupported or corrupt file\n");
|
||||||
retval = CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
goto exit;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (resume_offset > ci->id3->first_frame_offset)
|
if (resume_offset > ci->id3->first_frame_offset)
|
||||||
|
@ -101,34 +100,35 @@ restart_track:
|
||||||
codec_set_replaygain(ci->id3);
|
codec_set_replaygain(ci->id3);
|
||||||
|
|
||||||
/* The main decoding loop */
|
/* The main decoding loop */
|
||||||
|
|
||||||
res = 1;
|
|
||||||
while (res >= 0)
|
while (res >= 0)
|
||||||
{
|
{
|
||||||
ci->yield();
|
enum codec_command_action action = ci->get_command(¶m);
|
||||||
if (ci->stop_codec || ci->new_track) {
|
|
||||||
goto done;
|
if (action == CODEC_ACTION_HALT)
|
||||||
}
|
break;
|
||||||
|
|
||||||
/* Deal with any pending seek requests */
|
/* 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();
|
ci->seek_complete();
|
||||||
goto restart_track; /* Pretend you never saw this... */
|
goto restart_track; /* Pretend you never saw this... */
|
||||||
}
|
}
|
||||||
|
|
||||||
elapsedtime = asf_seek(ci->seek_time, &wfx);
|
elapsedtime = asf_seek(param, &wfx);
|
||||||
if (elapsedtime < 1){
|
if (elapsedtime < 1){
|
||||||
|
ci->set_elapsed(0);
|
||||||
ci->seek_complete();
|
ci->seek_complete();
|
||||||
goto next_track;
|
break;
|
||||||
}
|
}
|
||||||
/*DEBUGF("Seek returned %d\n", (int)elapsedtime);*/
|
/*DEBUGF("Seek returned %d\n", (int)elapsedtime);*/
|
||||||
ci->set_elapsed(elapsedtime);
|
|
||||||
|
|
||||||
/*flush the wma decoder state*/
|
/*flush the wma decoder state*/
|
||||||
wmadec.last_superframe_len = 0;
|
wmadec.last_superframe_len = 0;
|
||||||
wmadec.last_bitoffset = 0;
|
wmadec.last_bitoffset = 0;
|
||||||
|
|
||||||
|
ci->set_elapsed(elapsedtime);
|
||||||
ci->seek_complete();
|
ci->seek_complete();
|
||||||
}
|
}
|
||||||
errcount = 0;
|
errcount = 0;
|
||||||
|
@ -140,10 +140,15 @@ new_packet:
|
||||||
* times. If we succeed, the error counter will be reset.
|
* times. If we succeed, the error counter will be reset.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
if (res == ASF_ERROR_EOF) {
|
||||||
|
/* File ended - not an error */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
errcount++;
|
errcount++;
|
||||||
DEBUGF("read_packet error %d, errcount %d\n",wmares, errcount);
|
DEBUGF("read_packet error %d, errcount %d\n",wmares, errcount);
|
||||||
if (errcount > 5) {
|
if (errcount > 5) {
|
||||||
goto done;
|
return CODEC_ERROR;
|
||||||
} else {
|
} else {
|
||||||
ci->advance_buffer(packetlength);
|
ci->advance_buffer(packetlength);
|
||||||
goto new_packet;
|
goto new_packet;
|
||||||
|
@ -163,7 +168,7 @@ new_packet:
|
||||||
errcount++;
|
errcount++;
|
||||||
DEBUGF("WMA decode error %d, errcount %d\n",wmares, errcount);
|
DEBUGF("WMA decode error %d, errcount %d\n",wmares, errcount);
|
||||||
if (errcount > 5) {
|
if (errcount > 5) {
|
||||||
goto done;
|
return CODEC_ERROR;
|
||||||
} else {
|
} else {
|
||||||
ci->advance_buffer(packetlength);
|
ci->advance_buffer(packetlength);
|
||||||
goto new_packet;
|
goto new_packet;
|
||||||
|
@ -173,18 +178,12 @@ new_packet:
|
||||||
elapsedtime += (wmares*10)/(wfx.rate/100);
|
elapsedtime += (wmares*10)/(wfx.rate/100);
|
||||||
ci->set_elapsed(elapsedtime);
|
ci->set_elapsed(elapsedtime);
|
||||||
}
|
}
|
||||||
ci->yield();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ci->advance_buffer(packetlength);
|
ci->advance_buffer(packetlength);
|
||||||
}
|
}
|
||||||
|
|
||||||
done:
|
|
||||||
/*LOGF("WMA: Decoded %ld samples\n",elapsedtime*wfx.rate/1000);*/
|
/*LOGF("WMA: Decoded %ld samples\n",elapsedtime*wfx.rate/1000);*/
|
||||||
|
return CODEC_OK;
|
||||||
if (ci->request_next_track())
|
|
||||||
goto next_track;
|
|
||||||
exit:
|
|
||||||
return retval;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,11 +27,22 @@ CODEC_HEADER
|
||||||
|
|
||||||
int32_t *dec[2]; /* pointers to the output buffers in WMAProDecodeCtx in wmaprodec.c */
|
int32_t *dec[2]; /* pointers to the output buffers in WMAProDecodeCtx in wmaprodec.c */
|
||||||
|
|
||||||
|
|
||||||
/* this is the codec entry point */
|
/* 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;
|
uint32_t elapsedtime;
|
||||||
int retval;
|
|
||||||
asf_waveformatex_t wfx; /* Holds the stream properties */
|
asf_waveformatex_t wfx; /* Holds the stream properties */
|
||||||
size_t resume_offset;
|
size_t resume_offset;
|
||||||
int res; /* Return values from asf_read_packet() and decode_packet() */
|
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 */
|
int pktcnt = 0; /* Count of the packets played */
|
||||||
uint8_t *data; /* Pointer to decoder input buffer */
|
uint8_t *data; /* Pointer to decoder input buffer */
|
||||||
int size; /* Size of the input frame to the decoder */
|
int size; /* Size of the input frame to the decoder */
|
||||||
|
intptr_t param;
|
||||||
/* 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;
|
|
||||||
|
|
||||||
/* Remember the resume position */
|
/* Remember the resume position */
|
||||||
resume_offset = ci->id3->offset;
|
resume_offset = ci->id3->offset;
|
||||||
|
|
||||||
restart_track:
|
restart_track:
|
||||||
retval = CODEC_OK;
|
|
||||||
|
|
||||||
if (codec_init()) {
|
if (codec_init()) {
|
||||||
LOGF("(WMA PRO) Error: Error initialising codec\n");
|
LOGF("(WMA PRO) Error: Error initialising codec\n");
|
||||||
retval = CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Copy the format metadata we've stored in the id3 TOC field. This
|
/* Copy the format metadata we've stored in the id3 TOC field. This
|
||||||
|
@ -77,8 +75,7 @@ restart_track:
|
||||||
|
|
||||||
if (decode_init(&wfx) < 0) {
|
if (decode_init(&wfx) < 0) {
|
||||||
LOGF("(WMA PRO) Error: Unsupported or corrupt file\n");
|
LOGF("(WMA PRO) Error: Unsupported or corrupt file\n");
|
||||||
retval = CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Now advance the file position to the first frame */
|
/* Now advance the file position to the first frame */
|
||||||
|
@ -91,23 +88,24 @@ restart_track:
|
||||||
|
|
||||||
while (pktcnt < wfx.numpackets)
|
while (pktcnt < wfx.numpackets)
|
||||||
{
|
{
|
||||||
ci->yield();
|
enum codec_command_action action = ci->get_command(¶m);
|
||||||
if (ci->stop_codec || ci->new_track) {
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Deal with any pending seek requests */
|
|
||||||
if (ci->seek_time){
|
|
||||||
|
|
||||||
if (ci->seek_time == 1) {
|
if (action == CODEC_ACTION_HALT)
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* Deal with any pending seek requests */
|
||||||
|
if (action == CODEC_ACTION_SEEK_TIME) {
|
||||||
|
if (param == 0) {
|
||||||
|
ci->set_elapsed(0);
|
||||||
ci->seek_complete();
|
ci->seek_complete();
|
||||||
goto restart_track; /* Pretend you never saw this... */
|
goto restart_track; /* Pretend you never saw this... */
|
||||||
}
|
}
|
||||||
|
|
||||||
elapsedtime = asf_seek(ci->seek_time, &wfx);
|
elapsedtime = asf_seek(param, &wfx);
|
||||||
if (elapsedtime < 1){
|
if (elapsedtime < 1){
|
||||||
|
ci->set_elapsed(0);
|
||||||
ci->seek_complete();
|
ci->seek_complete();
|
||||||
goto next_track;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
ci->set_elapsed(elapsedtime);
|
ci->set_elapsed(elapsedtime);
|
||||||
|
@ -117,8 +115,8 @@ restart_track:
|
||||||
res = asf_read_packet(&audiobuf, &audiobufsize, &packetlength, &wfx);
|
res = asf_read_packet(&audiobuf, &audiobufsize, &packetlength, &wfx);
|
||||||
|
|
||||||
if (res < 0) {
|
if (res < 0) {
|
||||||
LOGF("(WMA PRO) Warning: asf_read_packet returned %d", res);
|
LOGF("(WMA PRO) Warning: asf_read_packet returned %d", res);
|
||||||
goto done;
|
return CODEC_ERROR;
|
||||||
} else {
|
} else {
|
||||||
data = audiobuf;
|
data = audiobuf;
|
||||||
size = audiobufsize;
|
size = audiobufsize;
|
||||||
|
@ -132,7 +130,7 @@ restart_track:
|
||||||
res = decode_packet(&wfx, dec, &outlen, data, size);
|
res = decode_packet(&wfx, dec, &outlen, data, size);
|
||||||
if(res < 0) {
|
if(res < 0) {
|
||||||
LOGF("(WMA PRO) Error: decode_packet returned %d", res);
|
LOGF("(WMA PRO) Error: decode_packet returned %d", res);
|
||||||
goto done;
|
return CODEC_ERROR;
|
||||||
}
|
}
|
||||||
data += res;
|
data += res;
|
||||||
size -= res;
|
size -= res;
|
||||||
|
@ -152,10 +150,6 @@ restart_track:
|
||||||
ci->advance_buffer(packetlength);
|
ci->advance_buffer(packetlength);
|
||||||
}
|
}
|
||||||
|
|
||||||
done:
|
return CODEC_OK;
|
||||||
if (ci->request_next_track())
|
|
||||||
goto next_track;
|
|
||||||
|
|
||||||
return retval;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -52,10 +52,20 @@ static void init_codec_ctx(AVCodecContext *avctx, asf_waveformatex_t *wfx)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* this is the codec entry point */
|
/* 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;
|
uint32_t elapsedtime;
|
||||||
int retval;
|
|
||||||
asf_waveformatex_t wfx; /* Holds the stream properties */
|
asf_waveformatex_t wfx; /* Holds the stream properties */
|
||||||
size_t resume_offset;
|
size_t resume_offset;
|
||||||
int res; /* Return values from asf_read_packet() and decode_packet() */
|
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 packetlength = 0; /* Logical packet size (minus the header size) */
|
||||||
int outlen = 0; /* Number of bytes written to the output buffer */
|
int outlen = 0; /* Number of bytes written to the output buffer */
|
||||||
int pktcnt = 0; /* Count of the packets played */
|
int pktcnt = 0; /* Count of the packets played */
|
||||||
|
intptr_t param;
|
||||||
/* 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;
|
|
||||||
|
|
||||||
/* Remember the resume position */
|
/* Remember the resume position */
|
||||||
resume_offset = ci->id3->offset;
|
resume_offset = ci->id3->offset;
|
||||||
restart_track:
|
restart_track:
|
||||||
retval = CODEC_OK;
|
|
||||||
|
|
||||||
if (codec_init()) {
|
if (codec_init()) {
|
||||||
LOGF("(WMA Voice) Error: Error initialising codec\n");
|
LOGF("(WMA Voice) Error: Error initialising codec\n");
|
||||||
retval = CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Copy the format metadata we've stored in the id3 TOC field. This
|
/* Copy the format metadata we've stored in the id3 TOC field. This
|
||||||
|
@ -97,14 +94,15 @@ restart_track:
|
||||||
ci->configure(DSP_SET_STEREO_MODE, wfx.channels == 1 ?
|
ci->configure(DSP_SET_STEREO_MODE, wfx.channels == 1 ?
|
||||||
STEREO_MONO : STEREO_INTERLEAVED);
|
STEREO_MONO : STEREO_INTERLEAVED);
|
||||||
codec_set_replaygain(ci->id3);
|
codec_set_replaygain(ci->id3);
|
||||||
|
|
||||||
|
ci->seek_buffer(0);
|
||||||
|
|
||||||
/* Initialise the AVCodecContext */
|
/* Initialise the AVCodecContext */
|
||||||
init_codec_ctx(&avctx, &wfx);
|
init_codec_ctx(&avctx, &wfx);
|
||||||
|
|
||||||
if (wmavoice_decode_init(&avctx) < 0) {
|
if (wmavoice_decode_init(&avctx) < 0) {
|
||||||
LOGF("(WMA Voice) Error: Unsupported or corrupt file\n");
|
LOGF("(WMA Voice) Error: Unsupported or corrupt file\n");
|
||||||
retval = CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Now advance the file position to the first frame */
|
/* Now advance the file position to the first frame */
|
||||||
|
@ -117,21 +115,24 @@ restart_track:
|
||||||
|
|
||||||
while (pktcnt < wfx.numpackets)
|
while (pktcnt < wfx.numpackets)
|
||||||
{
|
{
|
||||||
ci->yield();
|
enum codec_command_action action = ci->get_command(¶m);
|
||||||
if (ci->stop_codec || ci->new_track) {
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Deal with any pending seek requests */
|
|
||||||
if (ci->seek_time){
|
|
||||||
|
|
||||||
if (ci->seek_time == 1) {
|
if (action == CODEC_ACTION_HALT)
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* Deal with any pending seek requests */
|
||||||
|
if (action == CODEC_ACTION_SEEK_TIME) {
|
||||||
|
ci->set_elapsed(param);
|
||||||
|
|
||||||
|
if (param == 0) {
|
||||||
|
ci->set_elapsed(0);
|
||||||
ci->seek_complete();
|
ci->seek_complete();
|
||||||
goto restart_track; /* Pretend you never saw this... */
|
goto restart_track; /* Pretend you never saw this... */
|
||||||
}
|
}
|
||||||
|
|
||||||
elapsedtime = asf_seek(ci->seek_time, &wfx);
|
elapsedtime = asf_seek(param, &wfx);
|
||||||
if (elapsedtime < 1){
|
if (elapsedtime < 1){
|
||||||
|
ci->set_elapsed(0);
|
||||||
ci->seek_complete();
|
ci->seek_complete();
|
||||||
goto next_track;
|
goto next_track;
|
||||||
}
|
}
|
||||||
|
@ -145,7 +146,7 @@ new_packet:
|
||||||
|
|
||||||
if (res < 0) {
|
if (res < 0) {
|
||||||
LOGF("(WMA Voice) read_packet error %d\n",res);
|
LOGF("(WMA Voice) read_packet error %d\n",res);
|
||||||
goto done;
|
return CODEC_ERROR;
|
||||||
} else {
|
} else {
|
||||||
avpkt.data = audiobuf;
|
avpkt.data = audiobuf;
|
||||||
avpkt.size = audiobufsize;
|
avpkt.size = audiobufsize;
|
||||||
|
@ -165,8 +166,9 @@ new_packet:
|
||||||
ci->advance_buffer(packetlength);
|
ci->advance_buffer(packetlength);
|
||||||
goto new_packet;
|
goto new_packet;
|
||||||
}
|
}
|
||||||
else
|
else {
|
||||||
goto done;
|
return CODEC_ERROR;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
avpkt.data += res;
|
avpkt.data += res;
|
||||||
avpkt.size -= res;
|
avpkt.size -= res;
|
||||||
|
@ -186,10 +188,6 @@ new_packet:
|
||||||
ci->advance_buffer(packetlength);
|
ci->advance_buffer(packetlength);
|
||||||
}
|
}
|
||||||
|
|
||||||
done:
|
return CODEC_OK;
|
||||||
if (ci->request_next_track())
|
|
||||||
goto next_track;
|
|
||||||
|
|
||||||
return retval;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -219,10 +219,10 @@ static int skintouch_to_wps(struct wps_data *data)
|
||||||
#endif
|
#endif
|
||||||
case ACTION_TOUCH_SCROLLBAR:
|
case ACTION_TOUCH_SCROLLBAR:
|
||||||
skin_get_global_state()->id3->elapsed = skin_get_global_state()->id3->length*offset/100;
|
skin_get_global_state()->id3->elapsed = skin_get_global_state()->id3->length*offset/100;
|
||||||
if (!skin_get_global_state()->paused)
|
|
||||||
#if (CONFIG_CODEC == SWCODEC)
|
#if (CONFIG_CODEC == SWCODEC)
|
||||||
audio_pre_ff_rewind();
|
audio_pre_ff_rewind();
|
||||||
#else
|
#else
|
||||||
|
if (!skin_get_global_state()->paused)
|
||||||
audio_pause();
|
audio_pause();
|
||||||
#endif
|
#endif
|
||||||
audio_ff_rewind(skin_get_global_state()->id3->elapsed);
|
audio_ff_rewind(skin_get_global_state()->id3->elapsed);
|
||||||
|
@ -300,10 +300,10 @@ bool ffwd_rew(int button)
|
||||||
if ( (audio_status() & AUDIO_STATUS_PLAY) &&
|
if ( (audio_status() & AUDIO_STATUS_PLAY) &&
|
||||||
skin_get_global_state()->id3 && skin_get_global_state()->id3->length )
|
skin_get_global_state()->id3 && skin_get_global_state()->id3->length )
|
||||||
{
|
{
|
||||||
if (!skin_get_global_state()->paused)
|
|
||||||
#if (CONFIG_CODEC == SWCODEC)
|
#if (CONFIG_CODEC == SWCODEC)
|
||||||
audio_pre_ff_rewind();
|
audio_pre_ff_rewind();
|
||||||
#else
|
#else
|
||||||
|
if (!skin_get_global_state()->paused)
|
||||||
audio_pause();
|
audio_pause();
|
||||||
#endif
|
#endif
|
||||||
#if CONFIG_KEYPAD == PLAYER_PAD
|
#if CONFIG_KEYPAD == PLAYER_PAD
|
||||||
|
@ -472,10 +472,10 @@ static void prev_track(unsigned long skip_thresh)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!state->paused)
|
|
||||||
#if (CONFIG_CODEC == SWCODEC)
|
#if (CONFIG_CODEC == SWCODEC)
|
||||||
audio_pre_ff_rewind();
|
audio_pre_ff_rewind();
|
||||||
#else
|
#else
|
||||||
|
if (!state->paused)
|
||||||
audio_pause();
|
audio_pause();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -554,16 +554,20 @@ static void play_hop(int direction)
|
||||||
{
|
{
|
||||||
elapsed += step * direction;
|
elapsed += step * direction;
|
||||||
}
|
}
|
||||||
if((audio_status() & AUDIO_STATUS_PLAY) && !state->paused)
|
if(audio_status() & AUDIO_STATUS_PLAY)
|
||||||
{
|
{
|
||||||
#if (CONFIG_CODEC == SWCODEC)
|
#if (CONFIG_CODEC == SWCODEC)
|
||||||
audio_pre_ff_rewind();
|
audio_pre_ff_rewind();
|
||||||
#else
|
#else
|
||||||
audio_pause();
|
if (!state->paused)
|
||||||
|
audio_pause();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if (CONFIG_CODEC == SWCODEC)
|
||||||
|
audio_ff_rewind(elapsed);
|
||||||
|
#else
|
||||||
audio_ff_rewind(state->id3->elapsed = elapsed);
|
audio_ff_rewind(state->id3->elapsed = elapsed);
|
||||||
#if (CONFIG_CODEC != SWCODEC)
|
|
||||||
if (!state->paused)
|
if (!state->paused)
|
||||||
audio_resume();
|
audio_resume();
|
||||||
#endif
|
#endif
|
||||||
|
@ -849,10 +853,10 @@ long gui_wps_show(void)
|
||||||
{
|
{
|
||||||
if (state->id3->cuesheet)
|
if (state->id3->cuesheet)
|
||||||
{
|
{
|
||||||
if (!state->paused)
|
|
||||||
#if (CONFIG_CODEC == SWCODEC)
|
#if (CONFIG_CODEC == SWCODEC)
|
||||||
audio_pre_ff_rewind();
|
audio_pre_ff_rewind();
|
||||||
#else
|
#else
|
||||||
|
if (!state->paused)
|
||||||
audio_pause();
|
audio_pause();
|
||||||
#endif
|
#endif
|
||||||
audio_ff_rewind(0);
|
audio_ff_rewind(0);
|
||||||
|
@ -1146,6 +1150,17 @@ static void nextid3available_callback(void* param)
|
||||||
skin_request_full_update(WPS);
|
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)
|
static void wps_state_init(void)
|
||||||
{
|
{
|
||||||
|
@ -1167,6 +1182,9 @@ static void wps_state_init(void)
|
||||||
/* add the WPS track event callbacks */
|
/* add the WPS track event callbacks */
|
||||||
add_event(PLAYBACK_EVENT_TRACK_CHANGE, false, track_changed_callback);
|
add_event(PLAYBACK_EVENT_TRACK_CHANGE, false, track_changed_callback);
|
||||||
add_event(PLAYBACK_EVENT_NEXTTRACKID3_AVAILABLE, false, nextid3available_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)
|
switch (action)
|
||||||
{
|
{
|
||||||
case ACTION_EXIT_MENUITEM: /* on exit */
|
case ACTION_EXIT_MENUITEM: /* on exit */
|
||||||
|
#if CONFIG_CODEC == SWCODEC
|
||||||
|
audio_set_cuesheet(global_settings.cuesheet);
|
||||||
|
#else
|
||||||
if (global_settings.cuesheet)
|
if (global_settings.cuesheet)
|
||||||
splash(HZ*2, ID2P(LANG_PLEASE_REBOOT));
|
splash(HZ*2, ID2P(LANG_PLEASE_REBOOT));
|
||||||
break;
|
break;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
return action;
|
return action;
|
||||||
}
|
}
|
||||||
|
|
130
apps/metadata.c
130
apps/metadata.c
|
@ -33,7 +33,7 @@
|
||||||
|
|
||||||
#if CONFIG_CODEC == SWCODEC
|
#if CONFIG_CODEC == SWCODEC
|
||||||
|
|
||||||
/* For trailing tag stripping */
|
/* For trailing tag stripping and base audio data types */
|
||||||
#include "buffering.h"
|
#include "buffering.h"
|
||||||
|
|
||||||
#include "metadata/metadata_common.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) */
|
#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. */
|
/* Simple file type probing by looking at the filename extension. */
|
||||||
unsigned int probe_file_format(const char *filename)
|
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 */
|
/* 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 */
|
/* Take our best guess at the codec type based on file extension */
|
||||||
id3->codectype = probe_file_format(trackname);
|
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);
|
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__
|
#ifndef __PCTOOL__
|
||||||
#ifdef HAVE_TAGCACHE
|
#ifdef HAVE_TAGCACHE
|
||||||
#if CONFIG_CODEC == SWCODEC
|
#if CONFIG_CODEC == SWCODEC
|
||||||
|
|
|
@ -266,9 +266,7 @@ struct mp3entry {
|
||||||
|
|
||||||
/* resume related */
|
/* resume related */
|
||||||
unsigned long offset; /* bytes played */
|
unsigned long offset; /* bytes played */
|
||||||
#if CONFIG_CODEC != SWCODEC
|
|
||||||
int index; /* playlist index */
|
int index; /* playlist index */
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef HAVE_TAGCACHE
|
#ifdef HAVE_TAGCACHE
|
||||||
unsigned char autoresumable; /* caches result of autoresumable() */
|
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);
|
bool mp3info(struct mp3entry *entry, const char *filename);
|
||||||
void adjust_mp3entry(struct mp3entry *entry, void *dest, const void *orig);
|
void adjust_mp3entry(struct mp3entry *entry, void *dest, const void *orig);
|
||||||
void copy_mp3entry(struct mp3entry *dest, const struct mp3entry *orig);
|
void copy_mp3entry(struct mp3entry *dest, const struct mp3entry *orig);
|
||||||
|
void wipe_mp3entry(struct mp3entry *id3);
|
||||||
|
|
||||||
#if CONFIG_CODEC == SWCODEC
|
#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);
|
void strip_tags(int handle_id);
|
||||||
|
enum data_type get_audio_base_data_type(int afmt);
|
||||||
|
bool format_buffers_with_offset(int afmt);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef HAVE_TAGCACHE
|
#ifdef HAVE_TAGCACHE
|
||||||
|
|
|
@ -40,6 +40,9 @@ bool get_nsf_metadata(int fd, struct mp3entry* id3)
|
||||||
|
|
||||||
p = id3->id3v2buf;
|
p = id3->id3v2buf;
|
||||||
|
|
||||||
|
/* Length */
|
||||||
|
id3->length = buf[6]*1000;
|
||||||
|
|
||||||
/* Title */
|
/* Title */
|
||||||
memcpy(p, &buf[14], 32);
|
memcpy(p, &buf[14], 32);
|
||||||
id3->title = p;
|
id3->title = p;
|
||||||
|
|
|
@ -86,6 +86,8 @@ static size_t pcmbuffer_pos IDATA_ATTR;
|
||||||
/* Amount pcmbuffer_pos will be increased.*/
|
/* Amount pcmbuffer_pos will be increased.*/
|
||||||
static size_t pcmbuffer_fillpos IDATA_ATTR;
|
static size_t pcmbuffer_fillpos IDATA_ATTR;
|
||||||
|
|
||||||
|
static struct chunkdesc *first_desc;
|
||||||
|
|
||||||
/* Gapless playback */
|
/* Gapless playback */
|
||||||
static bool track_transition IDATA_ATTR;
|
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);
|
static void pcmbuf_finish_crossfade_enable(void);
|
||||||
#endif
|
#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
|
#ifndef SIMULATOR
|
||||||
#undef DESC_DEBUG
|
#undef DESC_DEBUG
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef DESC_DEBUG
|
#ifdef DESC_DEBUG
|
||||||
static struct chunkdesc *first_desc;
|
|
||||||
static bool show_desc_in_use = false;
|
|
||||||
#define DISPLAY_DESC(caller) while(!show_desc(caller))
|
#define DISPLAY_DESC(caller) while(!show_desc(caller))
|
||||||
#define DESC_IDX(desc) (desc ? desc - first_desc : -1)
|
#define DESC_IDX(desc) (desc ? desc - first_desc : -1)
|
||||||
#define SHOW_DESC(desc) if(DESC_IDX(desc)==-1) DEBUGF("--"); \
|
#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,
|
/* Flush! Discard all data after the currently playing chunk,
|
||||||
and make the current chunk play next */
|
and make the current chunk play next */
|
||||||
logf("commit_chunk: flush");
|
logf("commit_chunk: flush");
|
||||||
|
pcm_play_lock();
|
||||||
write_end_chunk->link = read_chunk->link;
|
write_end_chunk->link = read_chunk->link;
|
||||||
read_chunk->link = pcmbuf_current;
|
read_chunk->link = pcmbuf_current;
|
||||||
while (write_end_chunk->link)
|
while (write_end_chunk->link)
|
||||||
|
@ -238,6 +245,9 @@ static void commit_chunk(bool flush_next_time)
|
||||||
write_end_chunk = write_end_chunk->link;
|
write_end_chunk = write_end_chunk->link;
|
||||||
pcmbuf_unplayed_bytes -= write_end_chunk->size;
|
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 */
|
/* If there is already a read buffer setup, add to it */
|
||||||
else
|
else
|
||||||
|
@ -248,7 +258,7 @@ static void commit_chunk(bool flush_next_time)
|
||||||
/* Otherwise create the buffer */
|
/* Otherwise create the buffer */
|
||||||
read_chunk = pcmbuf_current;
|
read_chunk = pcmbuf_current;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If flush_next_time is true, then the current chunk will be thrown out
|
/* If flush_next_time is true, then the current chunk will be thrown out
|
||||||
* and the next chunk to be committed will be the next to be played.
|
* and the next chunk to be committed will be the next to be played.
|
||||||
* This is used to empty the PCM buffer for a track change. */
|
* This is used to empty the PCM buffer for a track change. */
|
||||||
|
@ -354,7 +364,7 @@ static bool prepare_insert(size_t length)
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
logf("pcm starting");
|
logf("pcm starting");
|
||||||
if (!(audio_status() & AUDIO_STATUS_PAUSE))
|
if (audio_pcmbuf_may_play())
|
||||||
pcmbuf_play_start();
|
pcmbuf_play_start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -373,8 +383,12 @@ void *pcmbuf_request_buffer(int *count)
|
||||||
/* crossfade has begun, put the new track samples in fadebuf */
|
/* crossfade has begun, put the new track samples in fadebuf */
|
||||||
if (crossfade_active)
|
if (crossfade_active)
|
||||||
{
|
{
|
||||||
*count = MIN(*count, CROSSFADE_BUFSIZE/4);
|
int cnt = MIN(*count, CROSSFADE_BUFSIZE/4);
|
||||||
return fadebuf;
|
if (prepare_insert(cnt << 2))
|
||||||
|
{
|
||||||
|
*count = cnt;
|
||||||
|
return fadebuf;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
#endif
|
#endif
|
||||||
|
@ -421,9 +435,7 @@ void pcmbuf_write_complete(int count)
|
||||||
|
|
||||||
static inline void init_pcmbuffers(void)
|
static inline void init_pcmbuffers(void)
|
||||||
{
|
{
|
||||||
#ifdef DESC_DEBUG
|
|
||||||
first_desc = write_chunk;
|
first_desc = write_chunk;
|
||||||
#endif
|
|
||||||
struct chunkdesc *next = write_chunk;
|
struct chunkdesc *next = write_chunk;
|
||||||
next++;
|
next++;
|
||||||
write_end_chunk = write_chunk;
|
write_end_chunk = write_chunk;
|
||||||
|
@ -494,19 +506,27 @@ void pcmbuf_monitor_track_change(bool monitor)
|
||||||
currently playing chunk. If not, cancel notification. */
|
currently playing chunk. If not, cancel notification. */
|
||||||
track_transition = monitor;
|
track_transition = monitor;
|
||||||
read_end_chunk->end_of_track = 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
|
else
|
||||||
{
|
{
|
||||||
/* Post now if PCM stopped and last buffer was sent. */
|
/* Post now if PCM stopped and last buffer was sent. */
|
||||||
track_transition = false;
|
track_transition = false;
|
||||||
if (monitor)
|
if (monitor)
|
||||||
audio_post_track_change(false);
|
audio_pcmbuf_track_change(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
pcm_play_unlock();
|
pcm_play_unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
void pcmbuf_start_track_change(bool auto_skip)
|
bool pcmbuf_start_track_change(bool auto_skip)
|
||||||
{
|
{
|
||||||
bool crossfade = false;
|
bool crossfade = false;
|
||||||
#ifdef HAVE_CROSSFADE
|
#ifdef HAVE_CROSSFADE
|
||||||
|
@ -546,9 +566,6 @@ void pcmbuf_start_track_change(bool auto_skip)
|
||||||
|
|
||||||
/* Cancel any pending automatic gapless transition */
|
/* Cancel any pending automatic gapless transition */
|
||||||
pcmbuf_monitor_track_change(false);
|
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 */
|
/* Can't do two crossfades at once and, no fade if pcm is off now */
|
||||||
if (
|
if (
|
||||||
|
@ -559,7 +576,8 @@ void pcmbuf_start_track_change(bool auto_skip)
|
||||||
{
|
{
|
||||||
pcmbuf_play_stop();
|
pcmbuf_play_stop();
|
||||||
pcm_play_unlock();
|
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 */
|
/* 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
|
/* Keep trigger outside the play lock or HW FIFO underruns can happen
|
||||||
since frequency scaling is *not* always fast */
|
since frequency scaling is *not* always fast */
|
||||||
trigger_cpu_boost();
|
trigger_cpu_boost();
|
||||||
|
|
||||||
|
/* Notify playback that the track change starts now */
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
else /* automatic and not crossfading, so do gapless track change */
|
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. */
|
* as the last one in the track. */
|
||||||
logf(" gapless track change");
|
logf(" gapless track change");
|
||||||
pcmbuf_monitor_track_change(true);
|
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)
|
if (pcmbuf_current->end_of_track)
|
||||||
{
|
{
|
||||||
track_transition = false;
|
track_transition = false;
|
||||||
audio_post_track_change(true);
|
audio_pcmbuf_track_change(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Put the finished chunk back into circulation */
|
/* Put the finished chunk back into circulation */
|
||||||
|
@ -955,9 +977,6 @@ static void write_to_crossfade(size_t length)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Commit samples to the buffer */
|
|
||||||
while (!prepare_insert(length))
|
|
||||||
sleep(1);
|
|
||||||
while (length > 0)
|
while (length > 0)
|
||||||
{
|
{
|
||||||
COMMIT_IF_NEEDED;
|
COMMIT_IF_NEEDED;
|
||||||
|
|
|
@ -33,13 +33,21 @@ void pcmbuf_play_start(void);
|
||||||
void pcmbuf_play_stop(void);
|
void pcmbuf_play_stop(void);
|
||||||
void pcmbuf_pause(bool pause);
|
void pcmbuf_pause(bool pause);
|
||||||
void pcmbuf_monitor_track_change(bool monitor);
|
void pcmbuf_monitor_track_change(bool monitor);
|
||||||
void pcmbuf_start_track_change(bool manual_skip);
|
bool pcmbuf_start_track_change(bool manual_skip);
|
||||||
|
|
||||||
/* Crossfade */
|
/* Crossfade */
|
||||||
#ifdef HAVE_CROSSFADE
|
#ifdef HAVE_CROSSFADE
|
||||||
bool pcmbuf_is_crossfade_active(void);
|
bool pcmbuf_is_crossfade_active(void);
|
||||||
void pcmbuf_request_crossfade_enable(bool on_off);
|
void pcmbuf_request_crossfade_enable(bool on_off);
|
||||||
bool pcmbuf_is_same_size(void);
|
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
|
#endif
|
||||||
|
|
||||||
/* Voice */
|
/* Voice */
|
||||||
|
|
5414
apps/playback.c
5414
apps/playback.c
File diff suppressed because it is too large
Load diff
|
@ -26,6 +26,16 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include "config.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
|
#ifdef HAVE_ALBUMART
|
||||||
|
|
||||||
#include "bmp.h"
|
#include "bmp.h"
|
||||||
|
@ -67,6 +77,8 @@ long audio_filebufused(void);
|
||||||
void audio_pre_ff_rewind(void);
|
void audio_pre_ff_rewind(void);
|
||||||
void audio_skip(int direction);
|
void audio_skip(int direction);
|
||||||
void audio_hard_stop(void); /* Stops audio from serving playback */
|
void audio_hard_stop(void); /* Stops audio from serving playback */
|
||||||
|
|
||||||
|
void audio_set_cuesheet(int enable);
|
||||||
#ifdef HAVE_CROSSFADE
|
#ifdef HAVE_CROSSFADE
|
||||||
void audio_set_crossfade(int enable);
|
void audio_set_crossfade(int enable);
|
||||||
#endif
|
#endif
|
||||||
|
@ -78,11 +90,10 @@ enum
|
||||||
};
|
};
|
||||||
bool audio_restore_playback(int type); /* Restores the audio buffer to handle the requested playback */
|
bool audio_restore_playback(int type); /* Restores the audio buffer to handle the requested playback */
|
||||||
size_t audio_get_filebuflen(void);
|
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);
|
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);
|
bool audio_automatic_skip(void);
|
||||||
|
|
||||||
/* Define one constant that includes recording related functionality */
|
/* Define one constant that includes recording related functionality */
|
||||||
|
@ -91,35 +102,62 @@ bool audio_automatic_skip(void);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
Q_NULL = 0,
|
Q_NULL = 0, /* reserved */
|
||||||
|
|
||||||
|
/* -> audio */
|
||||||
Q_AUDIO_PLAY = 1,
|
Q_AUDIO_PLAY = 1,
|
||||||
Q_AUDIO_STOP,
|
Q_AUDIO_STOP,
|
||||||
Q_AUDIO_PAUSE,
|
Q_AUDIO_PAUSE,
|
||||||
Q_AUDIO_SKIP,
|
Q_AUDIO_SKIP,
|
||||||
Q_AUDIO_PRE_FF_REWIND,
|
Q_AUDIO_PRE_FF_REWIND,
|
||||||
Q_AUDIO_FF_REWIND,
|
Q_AUDIO_FF_REWIND,
|
||||||
Q_AUDIO_CHECK_NEW_TRACK,
|
|
||||||
Q_AUDIO_FLUSH,
|
Q_AUDIO_FLUSH,
|
||||||
Q_AUDIO_TRACK_CHANGED,
|
|
||||||
Q_AUDIO_SEEK_COMPLETE,
|
|
||||||
Q_AUDIO_DIR_SKIP,
|
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,
|
||||||
Q_CODEC_LOAD_DISK,
|
Q_CODEC_RUN,
|
||||||
|
Q_CODEC_PAUSE,
|
||||||
|
Q_CODEC_SEEK,
|
||||||
|
Q_CODEC_STOP,
|
||||||
|
Q_CODEC_UNLOAD,
|
||||||
|
|
||||||
|
|
||||||
|
/*- miscellanous -*/
|
||||||
#ifdef AUDIO_HAVE_RECORDING
|
#ifdef AUDIO_HAVE_RECORDING
|
||||||
Q_AUDIO_LOAD_ENCODER,
|
/* -> codec */
|
||||||
Q_ENCODER_LOAD_DISK,
|
Q_AUDIO_LOAD_ENCODER, /* load an encoder for recording */
|
||||||
Q_ENCODER_RECORD,
|
|
||||||
#endif
|
#endif
|
||||||
|
/* -> codec */
|
||||||
Q_CODEC_DO_CALLBACK,
|
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->amount++;
|
||||||
playlist->num_inserted_tracks++;
|
playlist->num_inserted_tracks++;
|
||||||
|
|
||||||
/* Update index for resume. */
|
|
||||||
playlist_update_resume_index();
|
|
||||||
|
|
||||||
return insert_position;
|
return insert_position;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -925,9 +922,6 @@ static int remove_track_from_playlist(struct playlist_info* playlist,
|
||||||
sync_control(playlist, false);
|
sync_control(playlist, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Update index for resume. */
|
|
||||||
playlist_update_resume_index();
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -987,9 +981,6 @@ static int randomise_playlist(struct playlist_info* playlist,
|
||||||
playlist->first_index, NULL, NULL, NULL);
|
playlist->first_index, NULL, NULL, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Update index for resume. */
|
|
||||||
playlist_update_resume_index();
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1030,9 +1021,6 @@ static int sort_playlist(struct playlist_info* playlist, bool start_current,
|
||||||
playlist->first_index, -1, NULL, NULL, NULL);
|
playlist->first_index, -1, NULL, NULL, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Update index for resume. */
|
|
||||||
playlist_update_resume_index();
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1205,9 +1193,6 @@ static void find_and_set_playlist_index(struct playlist_info* playlist,
|
||||||
break;
|
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)
|
if (index < 0)
|
||||||
return NULL;
|
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;
|
control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
|
||||||
seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
|
seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
|
||||||
|
|
||||||
|
@ -2632,30 +2623,17 @@ int playlist_get_resume_info(int *resume_index)
|
||||||
return 0;
|
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. */
|
/* Update resume info for current playing song. Returns -1 on error. */
|
||||||
int playlist_update_resume_info(const struct mp3entry* id3)
|
int playlist_update_resume_info(const struct mp3entry* id3)
|
||||||
{
|
{
|
||||||
struct playlist_info* playlist = ¤t_playlist;
|
struct playlist_info* playlist = ¤t_playlist;
|
||||||
|
|
||||||
if (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_offset != id3->offset)
|
||||||
{
|
{
|
||||||
global_status.resume_index = playlist->resume_index;
|
global_status.resume_index = playlist->index;
|
||||||
global_status.resume_offset = id3->offset;
|
global_status.resume_offset = id3->offset;
|
||||||
status_save();
|
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);
|
queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Update index for resume. */
|
|
||||||
playlist_update_resume_index();
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -90,7 +90,6 @@ struct playlist_info
|
||||||
int buffer_end_pos; /* last position where buffer was written */
|
int buffer_end_pos; /* last position where buffer was written */
|
||||||
int index; /* index of current playing track */
|
int index; /* index of current playing track */
|
||||||
int first_index; /* index of first song in playlist */
|
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 amount; /* number of tracks in the index */
|
||||||
int last_insert_pos; /* last position we inserted a track */
|
int last_insert_pos; /* last position we inserted a track */
|
||||||
int seed; /* shuffle seed */
|
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);
|
int playlist_next(int steps);
|
||||||
bool playlist_next_dir(int direction);
|
bool playlist_next_dir(int direction);
|
||||||
int playlist_get_resume_info(int *resume_index);
|
int playlist_get_resume_info(int *resume_index);
|
||||||
int playlist_get_index(void);
|
|
||||||
int playlist_update_resume_info(const struct mp3entry* id3);
|
int playlist_update_resume_info(const struct mp3entry* id3);
|
||||||
int playlist_get_display_index(void);
|
int playlist_get_display_index(void);
|
||||||
int playlist_amount(void);
|
int playlist_amount(void);
|
||||||
|
@ -176,6 +174,5 @@ int playlist_directory_tracksearch(const char* dirname, bool recurse,
|
||||||
int (*callback)(char*, void*),
|
int (*callback)(char*, void*),
|
||||||
void* context);
|
void* context);
|
||||||
int playlist_remove_all_tracks(struct playlist_info *playlist);
|
int playlist_remove_all_tracks(struct playlist_info *playlist);
|
||||||
void playlist_update_resume_index(void);
|
|
||||||
|
|
||||||
#endif /* __PLAYLIST_H__ */
|
#endif /* __PLAYLIST_H__ */
|
||||||
|
|
|
@ -692,7 +692,7 @@ static const struct plugin_api rockbox_api = {
|
||||||
#if CONFIG_CODEC == SWCODEC
|
#if CONFIG_CODEC == SWCODEC
|
||||||
codec_thread_do_callback,
|
codec_thread_do_callback,
|
||||||
codec_load_file,
|
codec_load_file,
|
||||||
codec_begin,
|
codec_run_proc,
|
||||||
codec_close,
|
codec_close,
|
||||||
get_codec_filename,
|
get_codec_filename,
|
||||||
find_array_ptr,
|
find_array_ptr,
|
||||||
|
|
|
@ -145,12 +145,12 @@ void* plugin_get_buffer(size_t *buffer_size);
|
||||||
#define PLUGIN_MAGIC 0x526F634B /* RocK */
|
#define PLUGIN_MAGIC 0x526F634B /* RocK */
|
||||||
|
|
||||||
/* increase this every time the api struct changes */
|
/* 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
|
/* update this to latest version if a change to the api struct breaks
|
||||||
backwards compatibility (and please take the opportunity to sort in any
|
backwards compatibility (and please take the opportunity to sort in any
|
||||||
new function which are "waiting" at the end of the function table) */
|
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 */
|
/* plugin return codes */
|
||||||
/* internal returns start at 0x100 to make exit(1..255) work */
|
/* internal returns start at 0x100 to make exit(1..255) work */
|
||||||
|
@ -799,9 +799,9 @@ struct plugin_api {
|
||||||
#if CONFIG_CODEC == SWCODEC
|
#if CONFIG_CODEC == SWCODEC
|
||||||
void (*codec_thread_do_callback)(void (*fn)(void),
|
void (*codec_thread_do_callback)(void (*fn)(void),
|
||||||
unsigned int *audio_thread_id);
|
unsigned int *audio_thread_id);
|
||||||
void * (*codec_load_file)(const char* codec, struct codec_api *api);
|
int (*codec_load_file)(const char* codec, struct codec_api *api);
|
||||||
int (*codec_begin)(void *handle);
|
int (*codec_run_proc)(void);
|
||||||
void (*codec_close)(void *handle);
|
int (*codec_close)(void);
|
||||||
const char *(*get_codec_filename)(int cod_spec);
|
const char *(*get_codec_filename)(int cod_spec);
|
||||||
void ** (*find_array_ptr)(void **arr, void *ptr);
|
void ** (*find_array_ptr)(void **arr, void *ptr);
|
||||||
int (*remove_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 struct test_track_info track;
|
||||||
static bool taginfo_ready = true;
|
|
||||||
|
|
||||||
static bool use_dsp;
|
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)
|
static void set_elapsed(unsigned long value)
|
||||||
{
|
{
|
||||||
elapsed = 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)
|
static void advance_buffer(size_t amount)
|
||||||
{
|
{
|
||||||
ci.curpos += amount;
|
ci.curpos += amount;
|
||||||
|
ci.id3->offset = ci.curpos;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -499,20 +500,17 @@ static void seek_complete(void)
|
||||||
/* Do nothing */
|
/* Do nothing */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Request file change from file buffer. Returns true is next
|
/* Codec calls this to know what it should do next. */
|
||||||
track is available and changed. If return value is false,
|
static enum codec_command_action get_command(intptr_t *param)
|
||||||
codec should exit immediately with PLUGIN_OK status. */
|
|
||||||
static bool request_next_track(void)
|
|
||||||
{
|
{
|
||||||
/* We are only decoding a single track */
|
rb->yield();
|
||||||
return false;
|
return CODEC_ACTION_NULL; /* just continue processing */
|
||||||
|
(void)param;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void set_offset(size_t value)
|
static void set_offset(size_t value)
|
||||||
{
|
{
|
||||||
/* ??? */
|
ci.id3->offset = value;
|
||||||
(void)value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -546,6 +544,9 @@ static void init_ci(void)
|
||||||
{
|
{
|
||||||
/* --- Our "fake" implementations of the codec API functions. --- */
|
/* --- 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;
|
ci.codec_get_buffer = codec_get_buffer;
|
||||||
|
|
||||||
if (wavinfo.fd >= 0 || checksum) {
|
if (wavinfo.fd >= 0 || checksum) {
|
||||||
|
@ -560,11 +561,9 @@ static void init_ci(void)
|
||||||
ci.advance_buffer = advance_buffer;
|
ci.advance_buffer = advance_buffer;
|
||||||
ci.seek_buffer = seek_buffer;
|
ci.seek_buffer = seek_buffer;
|
||||||
ci.seek_complete = seek_complete;
|
ci.seek_complete = seek_complete;
|
||||||
ci.request_next_track = request_next_track;
|
|
||||||
ci.set_offset = set_offset;
|
ci.set_offset = set_offset;
|
||||||
ci.configure = configure;
|
ci.configure = configure;
|
||||||
ci.dsp = (struct dsp_config *)rb->dsp_configure(NULL, DSP_MYDSP,
|
ci.get_command = get_command;
|
||||||
CODEC_IDX_AUDIO);
|
|
||||||
|
|
||||||
/* --- "Core" functions --- */
|
/* --- "Core" functions --- */
|
||||||
|
|
||||||
|
@ -620,20 +619,22 @@ static void init_ci(void)
|
||||||
static void codec_thread(void)
|
static void codec_thread(void)
|
||||||
{
|
{
|
||||||
const char* codecname;
|
const char* codecname;
|
||||||
void *handle;
|
int res;
|
||||||
int res = CODEC_ERROR;
|
|
||||||
|
|
||||||
codecname = rb->get_codec_filename(track.id3.codectype);
|
codecname = rb->get_codec_filename(track.id3.codectype);
|
||||||
|
|
||||||
/* Load the codec and start decoding. */
|
/* Load the codec */
|
||||||
handle = rb->codec_load_file(codecname,&ci);
|
res = rb->codec_load_file(codecname, &ci);
|
||||||
|
|
||||||
if (handle != NULL)
|
if (res >= 0)
|
||||||
{
|
{
|
||||||
res = rb->codec_begin(handle);
|
/* Decode the file */
|
||||||
rb->codec_close(handle);
|
res = rb->codec_run_proc();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Clean up */
|
||||||
|
rb->codec_close();
|
||||||
|
|
||||||
/* Signal to the main thread that we are done */
|
/* Signal to the main thread that we are done */
|
||||||
endtick = *rb->current_tick - rebuffertick;
|
endtick = *rb->current_tick - rebuffertick;
|
||||||
codec_playing = false;
|
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 */
|
/* Prepare the codec struct for playing the whole file */
|
||||||
ci.filesize = track.filesize;
|
ci.filesize = track.filesize;
|
||||||
ci.id3 = &track.id3;
|
ci.id3 = &track.id3;
|
||||||
ci.taginfo_ready = &taginfo_ready;
|
|
||||||
ci.curpos = 0;
|
ci.curpos = 0;
|
||||||
ci.stop_codec = false;
|
|
||||||
ci.new_track = 0;
|
|
||||||
ci.seek_time = 0;
|
|
||||||
|
|
||||||
if (use_dsp)
|
if (use_dsp)
|
||||||
rb->dsp_configure(ci.dsp, DSP_RESET, 0);
|
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 SYS_VOLUME_CHANGED MAKE_SYS_EVENT(SYS_EVENT_CLS_MISC, 5)
|
||||||
|
|
||||||
#define IS_SYSEVENT(ev) ((ev & SYS_EVENT) == SYS_EVENT)
|
#define IS_SYSEVENT(ev) ((ev & SYS_EVENT) == SYS_EVENT)
|
||||||
|
#define EVENT_RESERVED (~0)
|
||||||
|
|
||||||
#ifndef TIMEOUT_BLOCK
|
#ifndef TIMEOUT_BLOCK
|
||||||
#define TIMEOUT_BLOCK -1
|
#define TIMEOUT_BLOCK -1
|
||||||
|
@ -249,6 +250,15 @@ extern bool queue_in_queue_send(struct event_queue *q);
|
||||||
#endif /* HAVE_EXTENDED_MESSAGING_AND_NAME */
|
#endif /* HAVE_EXTENDED_MESSAGING_AND_NAME */
|
||||||
extern bool queue_empty(const struct event_queue* q);
|
extern bool queue_empty(const struct event_queue* q);
|
||||||
extern bool queue_peek(struct event_queue *q, struct queue_event *ev);
|
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_clear(struct event_queue* q);
|
||||||
extern void queue_remove_from_head(struct event_queue *q, long id);
|
extern void queue_remove_from_head(struct event_queue *q, long id);
|
||||||
extern int queue_count(const struct event_queue *q);
|
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();
|
oldlevel = disable_irq_save();
|
||||||
corelock_lock(&q->cl);
|
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);
|
queue_do_auto_reply(q->send);
|
||||||
|
#endif
|
||||||
|
|
||||||
while(1)
|
while(1)
|
||||||
{
|
{
|
||||||
|
@ -541,12 +543,18 @@ void queue_wait(struct event_queue *q, struct queue_event *ev)
|
||||||
corelock_lock(&q->cl);
|
corelock_lock(&q->cl);
|
||||||
}
|
}
|
||||||
|
|
||||||
q->read = rd + 1;
|
#ifdef HAVE_EXTENDED_MESSAGING_AND_NAME
|
||||||
rd &= QUEUE_LENGTH_MASK;
|
if(ev)
|
||||||
*ev = q->events[rd];
|
#endif
|
||||||
|
{
|
||||||
|
q->read = rd + 1;
|
||||||
|
rd &= QUEUE_LENGTH_MASK;
|
||||||
|
*ev = q->events[rd];
|
||||||
|
|
||||||
/* Get data for a waiting thread if one */
|
/* Get data for a waiting thread if one */
|
||||||
queue_do_fetch_sender(q->send, rd);
|
queue_do_fetch_sender(q->send, rd);
|
||||||
|
}
|
||||||
|
/* else just waiting on non-empty */
|
||||||
|
|
||||||
corelock_unlock(&q->cl);
|
corelock_unlock(&q->cl);
|
||||||
restore_irq(oldlevel);
|
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();
|
oldlevel = disable_irq_save();
|
||||||
corelock_lock(&q->cl);
|
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);
|
queue_do_auto_reply(q->send);
|
||||||
|
#endif
|
||||||
|
|
||||||
rd = q->read;
|
rd = q->read;
|
||||||
wr = q->write;
|
wr = q->write;
|
||||||
|
@ -590,20 +600,26 @@ void queue_wait_w_tmo(struct event_queue *q, struct queue_event *ev, int ticks)
|
||||||
wr = q->write;
|
wr = q->write;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* no worry about a removed message here - status is checked inside
|
#ifdef HAVE_EXTENDED_MESSAGING_AND_NAME
|
||||||
locks - perhaps verify if timeout or false alarm */
|
if(ev)
|
||||||
if (rd != wr)
|
#endif
|
||||||
{
|
{
|
||||||
q->read = rd + 1;
|
/* no worry about a removed message here - status is checked inside
|
||||||
rd &= QUEUE_LENGTH_MASK;
|
locks - perhaps verify if timeout or false alarm */
|
||||||
*ev = q->events[rd];
|
if (rd != wr)
|
||||||
/* Get data for a waiting thread if one */
|
{
|
||||||
queue_do_fetch_sender(q->send, rd);
|
q->read = rd + 1;
|
||||||
}
|
rd &= QUEUE_LENGTH_MASK;
|
||||||
else
|
*ev = q->events[rd];
|
||||||
{
|
/* Get data for a waiting thread if one */
|
||||||
ev->id = SYS_TIMEOUT;
|
queue_do_fetch_sender(q->send, rd);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ev->id = SYS_TIMEOUT;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
/* else just waiting on non-empty */
|
||||||
|
|
||||||
corelock_unlock(&q->cl);
|
corelock_unlock(&q->cl);
|
||||||
restore_irq(oldlevel);
|
restore_irq(oldlevel);
|
||||||
|
@ -740,6 +756,120 @@ void queue_reply(struct event_queue *q, intptr_t retval)
|
||||||
}
|
}
|
||||||
#endif /* HAVE_EXTENDED_MESSAGING_AND_NAME */
|
#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)
|
bool queue_peek(struct event_queue *q, struct queue_event *ev)
|
||||||
{
|
{
|
||||||
unsigned int rd;
|
unsigned int rd;
|
||||||
|
@ -765,32 +895,6 @@ bool queue_peek(struct event_queue *q, struct queue_event *ev)
|
||||||
return have_msg;
|
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)
|
void queue_remove_from_head(struct event_queue *q, long id)
|
||||||
{
|
{
|
||||||
int oldlevel;
|
int oldlevel;
|
||||||
|
@ -816,6 +920,33 @@ void queue_remove_from_head(struct event_queue *q, long id)
|
||||||
corelock_unlock(&q->cl);
|
corelock_unlock(&q->cl);
|
||||||
restore_irq(oldlevel);
|
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.
|
* The number of events waiting in the queue.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue