summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--apps/codec_thread.c454
-rw-r--r--apps/codec_thread.h21
-rw-r--r--apps/codecs.c57
-rw-r--r--apps/codecs.h21
-rw-r--r--apps/codecs/aiff_enc.c13
-rw-r--r--apps/codecs/mp3_enc.c13
-rw-r--r--apps/codecs/wav_enc.c13
-rw-r--r--apps/codecs/wavpack_enc.c15
-rw-r--r--apps/pcmbuf.c67
-rw-r--r--apps/pcmbuf.h1
-rw-r--r--apps/playback.c355
-rw-r--r--apps/playback.h5
-rw-r--r--apps/plugin.c2
-rw-r--r--apps/plugin.h8
-rw-r--r--apps/plugins/test_codec.c19
-rw-r--r--apps/recorder/pcm_record.c9
-rw-r--r--apps/tagtree.c9
17 files changed, 600 insertions, 482 deletions
diff --git a/apps/codec_thread.c b/apps/codec_thread.c
index 03ab5622e2..f166f2ba18 100644
--- a/apps/codec_thread.c
+++ b/apps/codec_thread.c
@@ -19,10 +19,10 @@
* KIND, either express or implied.
*
****************************************************************************/
-
+#include "config.h"
+#include "system.h"
#include "playback.h"
#include "codec_thread.h"
-#include "system.h"
#include "kernel.h"
#include "codecs.h"
#include "buffering.h"
@@ -67,36 +67,38 @@
*/
/* Main state control */
-volatile bool audio_codec_loaded SHAREDBSS_ATTR = false; /* Codec loaded? (C/A-) */
+
+/* Type of codec loaded? (C/A) */
+static int current_codectype SHAREDBSS_ATTR = AFMT_UNKNOWN;
extern struct mp3entry *thistrack_id3, /* the currently playing track */
*othertrack_id3; /* prev track during track-change-transition, or end of playlist,
* next track otherwise */
/* Track change controls */
-extern bool automatic_skip; /* Who initiated in-progress skip? (C/A-) */
-
-/* Set to true if the codec thread should send an audio stop request
- * (typically because the end of the playlist has been reached).
- */
-static bool codec_requested_stop = false;
-
extern struct event_queue audio_queue SHAREDBSS_ATTR;
-extern struct event_queue codec_queue SHAREDBSS_ATTR;
+
extern struct codec_api ci; /* from codecs.c */
/* Codec thread */
-unsigned int codec_thread_id; /* For modifying thread priority later.
- Used by playback.c and pcmbuf.c */
+static unsigned int codec_thread_id; /* For modifying thread priority later */
+static struct event_queue codec_queue SHAREDBSS_ATTR;
static struct queue_sender_list codec_queue_sender_list SHAREDBSS_ATTR;
static long codec_stack[(DEFAULT_STACK_SIZE + 0x2000)/sizeof(long)]
-IBSS_ATTR;
+ IBSS_ATTR;
static const char codec_thread_name[] = "codec";
-/* function prototypes */
-static bool codec_load_next_track(void);
+/* static routines */
+static void codec_queue_ack(intptr_t ackme)
+{
+ queue_reply(&codec_queue, ackme);
+}
+static intptr_t codec_queue_send(long id, intptr_t data)
+{
+ return queue_send(&codec_queue, id, data);
+}
/**************************************/
@@ -183,7 +185,7 @@ void codec_thread_do_callback(void (*fn)(void), unsigned int *id)
/* Codec thread will signal just before entering callback */
LOGFQUEUE("codec >| Q_CODEC_DO_CALLBACK");
- queue_send(&codec_queue, Q_CODEC_DO_CALLBACK, (intptr_t)fn);
+ codec_queue_send(Q_CODEC_DO_CALLBACK, (intptr_t)fn);
}
@@ -289,7 +291,7 @@ static size_t codec_filebuf_callback(void *ptr, size_t size)
{
ssize_t copy_n;
- if (ci.stop_codec || !(audio_status() & AUDIO_STATUS_PLAY))
+ if (ci.stop_codec)
return 0;
copy_n = bufread(get_audio_hid(), size, ptr);
@@ -311,24 +313,16 @@ static void* codec_request_buffer_callback(size_t *realsize, size_t reqsize)
ssize_t ret;
void *ptr;
- if (!(audio_status() & AUDIO_STATUS_PLAY))
- {
- *realsize = 0;
- return NULL;
- }
-
ret = bufgetdata(get_audio_hid(), reqsize, &ptr);
if (ret >= 0)
copy_n = MIN((size_t)ret, reqsize);
+ else
+ copy_n = 0;
if (copy_n == 0)
- {
- *realsize = 0;
- return NULL;
- }
+ ptr = NULL;
*realsize = copy_n;
-
return ptr;
} /* codec_request_buffer_callback */
@@ -360,61 +354,71 @@ static bool codec_seek_buffer_callback(size_t newpos)
static void codec_seek_complete_callback(void)
{
+ struct queue_event ev;
+
logf("seek_complete");
- /* If seeking-while-playing, pcm_is_paused() is true.
- * If seeking-while-paused, audio_status PAUSE is true.
- * A seamless seek skips this section. */
- bool audio_paused = audio_status() & AUDIO_STATUS_PAUSE;
- if (pcm_is_paused() || audio_paused)
- {
- /* Clear the buffer */
- pcmbuf_play_stop();
- dsp_configure(ci.dsp, DSP_FLUSH, 0);
- /* If seeking-while-playing, resume pcm playback */
- if (!audio_paused)
- pcmbuf_pause(false);
- }
- ci.seek_time = 0;
-}
+ /* Clear DSP */
+ dsp_configure(ci.dsp, DSP_FLUSH, 0);
-static void codec_discard_codec_callback(void)
-{
- int *codec_hid = get_codec_hid();
- if (*codec_hid >= 0)
- {
- bufclose(*codec_hid);
- *codec_hid = -1;
- }
+ /* Post notification to audio thread */
+ LOGFQUEUE("audio > Q_AUDIO_SEEK_COMPLETE");
+ queue_post(&audio_queue, Q_AUDIO_SEEK_COMPLETE, 0);
+
+ /* Wait for ACK */
+ queue_wait(&codec_queue, &ev);
+
+ /* ACK back in context */
+ codec_queue_ack(Q_AUDIO_SEEK_COMPLETE);
}
static bool codec_request_next_track_callback(void)
{
- int prev_codectype;
+ struct queue_event ev;
- if (ci.stop_codec || !(audio_status() & AUDIO_STATUS_PLAY))
- return false;
+ logf("Request new track");
- prev_codectype = get_codec_base_type(thistrack_id3->codectype);
- if (!codec_load_next_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 */
+ LOGFQUEUE("codec: already stopping");
return false;
+ }
+
+ trigger_cpu_boost();
- /* 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);
- /* Check if the next codec is the same file. */
- if (prev_codectype == get_codec_base_type(thistrack_id3->codectype))
+ /* 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)
{
- logf("New track loaded");
- codec_discard_codec_callback();
- return true;
+ /* 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);
}
- else
+
+ /* ACK back in context */
+ codec_queue_ack(Q_AUDIO_CHECK_NEW_TRACK);
+
+ if (ev.data != Q_CODEC_REQUEST_COMPLETE || ci.stop_codec)
{
- logf("New codec:%d/%d", thistrack_id3->codectype, prev_codectype);
+ 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)
@@ -438,7 +442,6 @@ void codec_init_codec_api(void)
ci.seek_buffer = codec_seek_buffer_callback;
ci.seek_complete = codec_seek_complete_callback;
ci.request_next_track = codec_request_next_track_callback;
- ci.discard_codec = codec_discard_codec_callback;
ci.set_offset = codec_set_offset_callback;
ci.configure = codec_configure_callback;
}
@@ -446,61 +449,18 @@ void codec_init_codec_api(void)
/* track change */
-static bool codec_load_next_track(void)
-{
- intptr_t result = Q_CODEC_REQUEST_FAILED;
-
- audio_set_prev_elapsed(thistrack_id3->elapsed);
-
-#ifdef AB_REPEAT_ENABLE
- ab_end_of_track_report();
-#endif
-
- logf("Request new track");
-
- if (ci.new_track == 0)
- {
- ci.new_track++;
- automatic_skip = true;
- }
-
- if (!ci.stop_codec)
- {
- trigger_cpu_boost();
- LOGFQUEUE("codec >| audio Q_AUDIO_CHECK_NEW_TRACK");
- result = queue_send(&audio_queue, Q_AUDIO_CHECK_NEW_TRACK, 0);
- }
-
- switch (result)
- {
- case Q_CODEC_REQUEST_COMPLETE:
- LOGFQUEUE("codec |< Q_CODEC_REQUEST_COMPLETE");
- pcmbuf_start_track_change(automatic_skip);
- return true;
-
- case Q_CODEC_REQUEST_FAILED:
- LOGFQUEUE("codec |< Q_CODEC_REQUEST_FAILED");
- ci.new_track = 0;
- ci.stop_codec = true;
- codec_requested_stop = true;
- return false;
-
- default:
- LOGFQUEUE("codec |< default");
- ci.stop_codec = true;
- codec_requested_stop = true;
- return false;
- }
-}
-
/** CODEC THREAD */
static void codec_thread(void)
{
struct queue_event ev;
- int status;
- while (1) {
- status = 0;
+
+ while (1)
+ {
+ int status = CODEC_OK;
+ void *handle = NULL;
+ int hid;
+ const char *codec_fn;
#ifdef HAVE_CROSSFADE
if (!pcmbuf_is_crossfade_active())
@@ -508,171 +468,89 @@ static void codec_thread(void)
{
cancel_cpu_boost();
}
-
+
queue_wait(&codec_queue, &ev);
- codec_requested_stop = false;
- switch (ev.id) {
+ switch (ev.id)
+ {
case Q_CODEC_LOAD_DISK:
LOGFQUEUE("codec < Q_CODEC_LOAD_DISK");
- queue_reply(&codec_queue, 1);
- audio_codec_loaded = true;
- ci.stop_codec = false;
- status = codec_load_file((const char *)ev.data, &ci);
- LOGFQUEUE("codec_load_file %s %d\n", (const char *)ev.data, status);
+ codec_fn = get_codec_filename(ev.data);
+ if (!codec_fn)
+ break;
+#ifdef AUDIO_HAVE_RECORDING
+ if (ev.data & CODEC_TYPE_ENCODER)
+ {
+ ev.id = Q_ENCODER_LOAD_DISK;
+ handle = codec_load_file(codec_fn, &ci);
+ if (handle)
+ codec_queue_ack(Q_ENCODER_LOAD_DISK);
+ }
+ else
+#endif
+ {
+ codec_queue_ack(Q_CODEC_LOAD_DISK);
+ handle = codec_load_file(codec_fn, &ci);
+ }
break;
case Q_CODEC_LOAD:
LOGFQUEUE("codec < Q_CODEC_LOAD");
- if (*get_codec_hid() < 0) {
- logf("Codec slot is empty!");
- /* Wait for the pcm buffer to go empty */
- while (pcm_is_playing())
- yield();
- /* This must be set to prevent an infinite loop */
- ci.stop_codec = true;
- LOGFQUEUE("codec > codec Q_AUDIO_PLAY");
- queue_post(&codec_queue, Q_AUDIO_PLAY, 0);
- break;
- }
-
- audio_codec_loaded = true;
- ci.stop_codec = false;
- status = codec_load_buf(*get_codec_hid(), &ci);
- LOGFQUEUE("codec_load_buf %d\n", status);
+ codec_queue_ack(Q_CODEC_LOAD);
+ hid = (int)ev.data;
+ handle = codec_load_buf(hid, &ci);
+ bufclose(hid);
break;
case Q_CODEC_DO_CALLBACK:
LOGFQUEUE("codec < Q_CODEC_DO_CALLBACK");
- queue_reply(&codec_queue, 1);
+ codec_queue_ack(Q_CODEC_DO_CALLBACK);
if ((void*)ev.data != NULL)
{
- cpucache_invalidate();
+ cpucache_commit_discard();
((void (*)(void))ev.data)();
- cpucache_flush();
+ cpucache_commit();
}
break;
-#ifdef AUDIO_HAVE_RECORDING
- case Q_ENCODER_LOAD_DISK:
- LOGFQUEUE("codec < Q_ENCODER_LOAD_DISK");
- audio_codec_loaded = false; /* Not audio codec! */
- logf("loading encoder");
- ci.stop_encoder = false;
- status = codec_load_file((const char *)ev.data, &ci);
- logf("encoder stopped");
- break;
-#endif /* AUDIO_HAVE_RECORDING */
-
default:
- LOGFQUEUE("codec < default");
+ LOGFQUEUE("codec < default : %ld", ev.id);
}
- if (audio_codec_loaded)
+ if (handle)
{
+ /* Codec loaded - call the entrypoint */
+ yield();
+ logf("codec running");
+ status = codec_begin(handle);
+ logf("codec stopped");
+ codec_close(handle);
+ current_codectype = AFMT_UNKNOWN;
+
if (ci.stop_codec)
- {
status = CODEC_OK;
- if (!(audio_status() & AUDIO_STATUS_PLAY))
- pcmbuf_play_stop();
-
- }
- audio_codec_loaded = false;
}
- switch (ev.id) {
- case Q_CODEC_LOAD_DISK:
- case Q_CODEC_LOAD:
- LOGFQUEUE("codec < Q_CODEC_LOAD");
- if (audio_status() & AUDIO_STATUS_PLAY)
- {
- if (ci.new_track || status != CODEC_OK)
- {
- if (!ci.new_track)
- {
- logf("Codec failure, %d %d", ci.new_track, status);
- splash(HZ*2, "Codec failure");
- }
-
- if (!codec_load_next_track())
- {
- LOGFQUEUE("codec > audio Q_AUDIO_STOP");
- /* End of playlist */
- queue_post(&audio_queue, Q_AUDIO_STOP, 0);
- break;
- }
- }
- else
- {
- logf("Codec finished");
- if (ci.stop_codec)
- {
- /* Wait for the audio to stop playing before
- * triggering the WPS exit */
- while(pcm_is_playing())
- {
- /* There has been one too many struct pointer swaps by now
- * so even though it says othertrack_id3, its the correct one! */
- othertrack_id3->elapsed =
- othertrack_id3->length - pcmbuf_get_latency();
- sleep(1);
- }
-
- if (codec_requested_stop)
- {
- LOGFQUEUE("codec > audio Q_AUDIO_STOP");
- queue_post(&audio_queue, Q_AUDIO_STOP, 0);
- }
- break;
- }
- }
-
- if (*get_codec_hid() >= 0)
- {
- LOGFQUEUE("codec > codec Q_CODEC_LOAD");
- queue_post(&codec_queue, Q_CODEC_LOAD, 0);
- }
- else
- {
- const char *codec_fn =
- get_codec_filename(thistrack_id3->codectype);
- if (codec_fn)
- {
- LOGFQUEUE("codec > codec Q_CODEC_LOAD_DISK");
- queue_post(&codec_queue, Q_CODEC_LOAD_DISK,
- (intptr_t)codec_fn);
- }
- }
- }
- break;
-
+ switch (ev.id)
+ {
#ifdef AUDIO_HAVE_RECORDING
case Q_ENCODER_LOAD_DISK:
- LOGFQUEUE("codec < Q_ENCODER_LOAD_DISK");
-
- if (status == CODEC_OK)
- break;
-
- logf("Encoder failure");
- splash(HZ*2, "Encoder failure");
-
- if (ci.enc_codec_loaded < 0)
- break;
-
- logf("Encoder failed to load");
- ci.enc_codec_loaded = -1;
+#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;
-#endif /* AUDIO_HAVE_RECORDING */
-
- default:
- LOGFQUEUE("codec < default");
-
- } /* end switch */
+ }
}
}
void make_codec_thread(void)
{
+ queue_init(&codec_queue, false);
codec_thread_id = create_thread(
codec_thread, codec_stack, sizeof(codec_stack),
CREATE_THREAD_FROZEN,
@@ -681,3 +559,79 @@ void make_codec_thread(void)
queue_enable_queue_send(&codec_queue, &codec_queue_sender_list,
codec_thread_id);
}
+
+void codec_thread_resume(void)
+{
+ thread_thaw(codec_thread_id);
+}
+
+bool is_codec_thread(void)
+{
+ return thread_get_current() == codec_thread_id;
+}
+
+#ifdef HAVE_PRIORITY_SCHEDULING
+int codec_thread_get_priority(void)
+{
+ return thread_get_priority(codec_thread_id);
+}
+
+int codec_thread_set_priority(int priority)
+{
+ return thread_set_priority(codec_thread_id, priority);
+}
+#endif /* HAVE_PRIORITY_SCHEDULING */
+
+/* functions for audio thread use */
+intptr_t codec_ack_msg(intptr_t data, bool stop_codec)
+{
+ intptr_t resp;
+ LOGFQUEUE("codec >| Q_CODEC_ACK: %d", data);
+ if (stop_codec)
+ ci.stop_codec = true;
+ resp = codec_queue_send(Q_CODEC_ACK, data);
+ if (stop_codec)
+ codec_stop();
+ LOGFQUEUE(" ack: %ld", resp);
+ return resp;
+}
+
+bool codec_load(int hid, int cod_spec)
+{
+ bool retval = false;
+
+ ci.stop_codec = false;
+ current_codectype = cod_spec;
+
+ if (hid >= 0)
+ {
+ LOGFQUEUE("audio >| codec Q_CODEC_LOAD: %d", hid);
+ retval = codec_queue_send(Q_CODEC_LOAD, hid) != Q_NULL;
+ }
+ else
+ {
+ LOGFQUEUE("audio >| codec Q_CODEC_LOAD_DISK: %d", cod_spec);
+ retval = codec_queue_send(Q_CODEC_LOAD_DISK, cod_spec) != Q_NULL;
+ }
+
+ if (!retval)
+ {
+ ci.stop_codec = true;
+ current_codectype = AFMT_UNKNOWN;
+ }
+
+ return retval;
+}
+
+void codec_stop(void)
+{
+ ci.stop_codec = true;
+ /* Wait until it's in the main loop */
+ while (codec_ack_msg(0, false) != Q_NULL);
+ current_codectype = AFMT_UNKNOWN;
+}
+
+int codec_loaded(void)
+{
+ return current_codectype;
+}
diff --git a/apps/codec_thread.h b/apps/codec_thread.h
index a849e07d39..7056e2cdf5 100644
--- a/apps/codec_thread.h
+++ b/apps/codec_thread.h
@@ -24,11 +24,30 @@
#include <stdbool.h>
+/* codec identity */
int get_codec_base_type(int type);
const char *get_codec_filename(int cod_spec);
+
+/* codec thread */
+
+/* Audio MUST be stopped before requesting callback! */
void codec_thread_do_callback(void (*fn)(void),
unsigned int *codec_thread_id);
+
void codec_init_codec_api(void);
void make_codec_thread(void);
-
+void codec_thread_resume(void);
+bool is_codec_thread(void);
+#ifdef HAVE_PRIORITY_SCHEDULING
+int codec_thread_get_priority(void);
+int codec_thread_set_priority(int priority);
#endif
+
+/* codec commands - on audio thread only! */
+intptr_t codec_ack_msg(intptr_t data, bool stop_codec);
+bool codec_load(int hid, int cod_spec);
+void codec_stop(void);
+int codec_loaded(void);
+/* */
+
+#endif /* _CODEC_THREAD_H */
diff --git a/apps/codecs.c b/apps/codecs.c
index 646d5f289b..86e36edcf0 100644
--- a/apps/codecs.c
+++ b/apps/codecs.c
@@ -97,7 +97,6 @@ struct codec_api ci = {
NULL, /* seek_buffer */
NULL, /* seek_complete */
NULL, /* request_next_track */
- NULL, /* discard_codec */
NULL, /* set_offset */
NULL, /* configure */
@@ -149,8 +148,6 @@ struct codec_api ci = {
#endif
#ifdef HAVE_RECORDING
- false, /* stop_encoder */
- 0, /* enc_codec_loaded */
enc_get_inputs,
enc_set_parameters,
enc_get_chunk,
@@ -178,11 +175,10 @@ void codec_get_full_path(char *path, const char *codec_root_fn)
CODECS_DIR, codec_root_fn);
}
-static int codec_load_ram(void *handle, struct codec_api *api)
+static void * codec_load_ram(void *handle, struct codec_api *api)
{
struct codec_header *c_hdr = lc_get_header(handle);
struct lc_header *hdr = c_hdr ? &c_hdr->lc_hdr : NULL;
- int status;
if (hdr == NULL
|| (hdr->magic != CODEC_MAGIC
@@ -199,14 +195,14 @@ static int codec_load_ram(void *handle, struct codec_api *api)
{
logf("codec header error");
lc_close(handle);
- return CODEC_ERROR;
+ return NULL;
}
if (hdr->api_version > CODEC_API_VERSION
|| hdr->api_version < CODEC_MIN_API_VERSION) {
logf("codec api version error");
lc_close(handle);
- return CODEC_ERROR;
+ return NULL;
}
#if (CONFIG_PLATFORM & PLATFORM_NATIVE)
@@ -216,34 +212,31 @@ static int codec_load_ram(void *handle, struct codec_api *api)
#endif
*(c_hdr->api) = api;
- status = c_hdr->entry_point();
- lc_close(handle);
-
- return status;
+ return handle;
}
-int codec_load_buf(unsigned int hid, struct codec_api *api)
+void * codec_load_buf(int hid, struct codec_api *api)
{
int rc;
void *handle;
rc = bufread(hid, CODEC_SIZE, codecbuf);
if (rc < 0) {
- logf("error loading codec");
- return CODEC_ERROR;
+ logf("Codec: cannot read buf handle");
+ return NULL;
}
+
handle = lc_open_from_mem(codecbuf, rc);
- if (handle == NULL)
- {
+
+ if (handle == NULL) {
logf("error loading codec");
- return CODEC_ERROR;
+ return NULL;
}
- api->discard_codec();
return codec_load_ram(handle, api);
}
-int codec_load_file(const char *plugin, struct codec_api *api)
+void * codec_load_file(const char *plugin, struct codec_api *api)
{
char path[MAX_PATH];
void *handle;
@@ -253,10 +246,30 @@ int codec_load_file(const char *plugin, struct codec_api *api)
handle = lc_open(path, codecbuf, CODEC_SIZE);
if (handle == NULL) {
- logf("Codec load error");
- splashf(HZ*2, "Couldn't load codec: %s", path);
- return CODEC_ERROR;
+ logf("Codec: cannot read file");
+ return NULL;
}
return codec_load_ram(handle, api);
}
+
+int codec_begin(void *handle)
+{
+ int status = CODEC_ERROR;
+ struct codec_header *c_hdr;
+
+ c_hdr = lc_get_header(handle);
+
+ if (c_hdr != NULL) {
+ logf("Codec: calling entry_point");
+ status = c_hdr->entry_point();
+ }
+
+ return status;
+}
+
+void codec_close(void *handle)
+{
+ if (handle)
+ lc_close(handle);
+}
diff --git a/apps/codecs.h b/apps/codecs.h
index c3ef7c154e..252dafd1f4 100644
--- a/apps/codecs.h
+++ b/apps/codecs.h
@@ -75,17 +75,16 @@
#define CODEC_ENC_MAGIC 0x52454E43 /* RENC */
/* increase this every time the api struct changes */
-#define CODEC_API_VERSION 35
+#define CODEC_API_VERSION 36
/* update this to latest version if a change to the api struct breaks
backwards compatibility (and please take the opportunity to sort in any
new function which are "waiting" at the end of the function table) */
-#define CODEC_MIN_API_VERSION 35
+#define CODEC_MIN_API_VERSION 36
/* codec return codes */
enum codec_status {
CODEC_OK = 0,
- CODEC_USB_CONNECTED,
CODEC_ERROR = -1,
};
@@ -106,13 +105,13 @@ struct codec_api {
/* Codec should periodically check if stop_codec is set to true.
In case it is, codec must return immediately */
- bool stop_codec;
+ volatile bool stop_codec;
/* Codec should periodically check if new_track is non zero.
When it is, the codec should request a new track. */
- int new_track;
+ volatile int new_track;
/* If seek_time != 0, codec should seek to that song position (in ms)
if codec supports seeking. */
- long seek_time;
+ volatile long seek_time;
/* The dsp instance to be used for audio output */
struct dsp_config *dsp;
@@ -145,8 +144,6 @@ struct codec_api {
track is available and changed. If return value is false,
codec should exit immediately with PLUGIN_OK status. */
bool (*request_next_track)(void);
- /* Free the buffer area of the current codec after its loaded */
- void (*discard_codec)(void);
void (*set_offset)(size_t value);
/* Configure different codec buffer parameters. */
@@ -210,8 +207,6 @@ struct codec_api {
#endif
#ifdef HAVE_RECORDING
- volatile bool stop_encoder;
- volatile int enc_codec_loaded; /* <0=error, 0=pending, >0=ok */
void (*enc_get_inputs)(struct enc_inputs *inputs);
void (*enc_set_parameters)(struct enc_parameters *params);
struct enc_chunk_hdr * (*enc_get_chunk)(void);
@@ -283,8 +278,10 @@ extern unsigned char plugin_end_addr[];
void codec_get_full_path(char *path, const char *codec_root_fn);
/* defined by the codec loader (codec.c) */
-int codec_load_buf(unsigned int hid, struct codec_api *api);
-int codec_load_file(const char* codec, struct codec_api *api);
+void * codec_load_buf(int hid, struct codec_api *api);
+void * codec_load_file(const char* codec, struct codec_api *api);
+int codec_begin(void *handle);
+void codec_close(void *handle);
/* defined by the codec */
enum codec_status codec_start(void);
diff --git a/apps/codecs/aiff_enc.c b/apps/codecs/aiff_enc.c
index 2d55dff755..69496f70ac 100644
--- a/apps/codecs/aiff_enc.c
+++ b/apps/codecs/aiff_enc.c
@@ -363,16 +363,10 @@ static bool init_encoder(void)
enum codec_status codec_main(void)
{
if (!init_encoder())
- {
- ci->enc_codec_loaded = -1;
return CODEC_ERROR;
- }
-
- /* main application waits for this flag during encoder loading */
- ci->enc_codec_loaded = 1;
/* main encoding loop */
- while(!ci->stop_encoder)
+ while(!ci->stop_codec)
{
uint32_t *src;
@@ -380,7 +374,7 @@ enum codec_status codec_main(void)
{
struct enc_chunk_hdr *chunk;
- if (ci->stop_encoder)
+ if (ci->stop_codec)
break;
chunk = ci->enc_get_chunk();
@@ -400,8 +394,5 @@ enum codec_status codec_main(void)
/* reset parameters to initial state */
ci->enc_set_parameters(NULL);
- /* main application waits for this flag during encoder removing */
- ci->enc_codec_loaded = 0;
-
return CODEC_OK;
} /* codec_start */
diff --git a/apps/codecs/mp3_enc.c b/apps/codecs/mp3_enc.c
index c66d755f08..e7893fd14a 100644
--- a/apps/codecs/mp3_enc.c
+++ b/apps/codecs/mp3_enc.c
@@ -2588,16 +2588,10 @@ enum codec_status codec_main(void)
{
/* Generic codec initialisation */
if (!enc_init())
- {
- ci->enc_codec_loaded = -1;
return CODEC_ERROR;
- }
-
- /* main application waits for this flag during encoder loading */
- ci->enc_codec_loaded = 1;
/* main encoding loop */
- while (!ci->stop_encoder)
+ while (!ci->stop_codec)
{
char *buffer;
@@ -2605,7 +2599,7 @@ enum codec_status codec_main(void)
{
struct enc_chunk_hdr *chunk;
- if (ci->stop_encoder)
+ if (ci->stop_codec)
break;
chunk = ci->enc_get_chunk();
@@ -2630,8 +2624,5 @@ enum codec_status codec_main(void)
/* reset parameters to initial state */
ci->enc_set_parameters(NULL);
- /* main application waits for this flag during encoder removing */
- ci->enc_codec_loaded = 0;
-
return CODEC_OK;
} /* codec_start */
diff --git a/apps/codecs/wav_enc.c b/apps/codecs/wav_enc.c
index 193181d825..ef1a88ec23 100644
--- a/apps/codecs/wav_enc.c
+++ b/apps/codecs/wav_enc.c
@@ -349,16 +349,10 @@ static bool init_encoder(void)
enum codec_status codec_main(void)
{
if (!init_encoder())
- {
- ci->enc_codec_loaded = -1;
return CODEC_ERROR;
- }
-
- /* main application waits for this flag during encoder loading */
- ci->enc_codec_loaded = 1;
/* main encoding loop */
- while(!ci->stop_encoder)
+ while(!ci->stop_codec)
{
uint32_t *src;
@@ -366,7 +360,7 @@ enum codec_status codec_main(void)
{
struct enc_chunk_hdr *chunk;
- if (ci->stop_encoder)
+ if (ci->stop_codec)
break;
chunk = ci->enc_get_chunk();
@@ -386,8 +380,5 @@ enum codec_status codec_main(void)
/* reset parameters to initial state */
ci->enc_set_parameters(NULL);
- /* main application waits for this flag during encoder removing */
- ci->enc_codec_loaded = 0;
-
return CODEC_OK;
} /* codec_start */
diff --git a/apps/codecs/wavpack_enc.c b/apps/codecs/wavpack_enc.c
index 66263cf1a1..d908e284be 100644
--- a/apps/codecs/wavpack_enc.c
+++ b/apps/codecs/wavpack_enc.c
@@ -393,16 +393,10 @@ enum codec_status codec_main(void)
{
/* initialize params and config */
if (!init_encoder())
- {
- ci->enc_codec_loaded = -1;
return CODEC_ERROR;
- }
-
- /* main application waits for this flag during encoder loading */
- ci->enc_codec_loaded = 1;
/* main encoding loop */
- while(!ci->stop_encoder)
+ while(!ci->stop_codec)
{
uint8_t *src;
@@ -413,7 +407,7 @@ enum codec_status codec_main(void)
uint8_t *dst;
uint8_t *src_end;
- if(ci->stop_encoder)
+ if(ci->stop_codec)
break;
abort_chunk = true;
@@ -442,7 +436,7 @@ enum codec_status codec_main(void)
chunk->num_pcm += PCM_SAMP_PER_CHUNK/4;
ci->yield();
/* could've been stopped in some way */
- abort_chunk = ci->stop_encoder ||
+ abort_chunk = ci->stop_codec ||
(chunk->flags & CHUNKF_ABORT);
}
@@ -467,8 +461,5 @@ enum codec_status codec_main(void)
/* reset parameters to initial state */
ci->enc_set_parameters(NULL);
- /* main application waits for this flag during encoder removing */
- ci->enc_codec_loaded = 0;
-
return CODEC_OK;
} /* codec_start */
diff --git a/apps/pcmbuf.c b/apps/pcmbuf.c
index 93ce4266c1..2cec40b7e7 100644
--- a/apps/pcmbuf.c
+++ b/apps/pcmbuf.c
@@ -26,6 +26,7 @@
#include "pcmbuf.h"
#include "pcm.h"
#include "playback.h"
+#include "codec_thread.h"
/* Define LOGF_ENABLE to enable logf output in this file */
/*#define LOGF_ENABLE*/
@@ -86,8 +87,7 @@ static size_t pcmbuffer_pos IDATA_ATTR;
static size_t pcmbuffer_fillpos IDATA_ATTR;
/* Gapless playback */
-static bool end_of_track IDATA_ATTR;
-bool track_transition IDATA_ATTR;
+static bool track_transition IDATA_ATTR;
#ifdef HAVE_CROSSFADE
/* Crossfade buffer */
@@ -131,8 +131,6 @@ static bool flush_pcmbuf = false;
static int codec_thread_priority = PRIORITY_PLAYBACK;
#endif
-extern unsigned int codec_thread_id;
-
/* Helpful macros for use in conditionals this assumes some of the above
* static variable names */
#define COMMIT_IF_NEEDED if(pcmbuffer_fillpos > PCMBUF_TARGET_CHUNK || \
@@ -223,9 +221,8 @@ static void commit_chunk(bool flush_next_time)
/* Fill in the values in the new buffer chunk */
pcmbuf_current->addr = &pcmbuffer[pcmbuffer_pos];
pcmbuf_current->size = size;
- pcmbuf_current->end_of_track = end_of_track;
+ pcmbuf_current->end_of_track = false;
pcmbuf_current->link = NULL;
- end_of_track = false; /* This is single use only */
if (read_chunk != NULL)
{
@@ -297,7 +294,7 @@ static void boost_codec_thread(int pcm_fill_state)
* will starve if the codec thread's priority is boosted. */
if (new_prio != codec_thread_priority)
{
- thread_set_priority(codec_thread_id, new_prio);
+ codec_thread_set_priority(new_prio);
voice_thread_set_priority(new_prio);
codec_thread_priority = new_prio;
}
@@ -327,7 +324,7 @@ static bool prepare_insert(size_t length)
/* Only codec thread initiates boost - voice boosts the cpu when playing
a clip */
#ifndef SIMULATOR
- if (thread_get_current() == codec_thread_id)
+ if (is_codec_thread())
#endif /* SIMULATOR */
{
/* boost cpu if necessary */
@@ -487,6 +484,27 @@ size_t pcmbuf_init(unsigned char *bufend)
/** Track change */
+void pcmbuf_monitor_track_change(bool monitor)
+{
+ pcm_play_lock();
+
+ if (last_chunksize != 0)
+ {
+ /* If monitoring, wait until this track runs out. Place in
+ currently playing chunk. If not, cancel notification. */
+ track_transition = monitor;
+ read_end_chunk->end_of_track = monitor;
+ }
+ else
+ {
+ /* Post now if PCM stopped and last buffer was sent. */
+ track_transition = false;
+ if (monitor)
+ audio_post_track_change(false);
+ }
+
+ pcm_play_unlock();
+}
void pcmbuf_start_track_change(bool auto_skip)
{
@@ -523,6 +541,11 @@ void pcmbuf_start_track_change(bool auto_skip)
{ logf(" crossfade track change"); }
else
{ logf(" manual track change"); }
+
+ pcm_play_lock();
+
+ /* Cancel any pending automatic gapless transition */
+ pcmbuf_monitor_track_change(false);
/* Notify the wps that the track change starts now */
audio_post_track_change(false);
@@ -535,26 +558,32 @@ void pcmbuf_start_track_change(bool auto_skip)
!pcm_is_playing())
{
pcmbuf_play_stop();
+ pcm_play_unlock();
return;
}
- trigger_cpu_boost();
-
/* Not enough data, or not crossfading, flush the old data instead */
if (LOW_DATA(2) || !crossfade || low_latency_mode)
{
commit_chunk(true);
- return;
}
-
#ifdef HAVE_CROSSFADE
- /* Don't enable mix mode when skipping tracks manually. */
- crossfade_mixmode = auto_skip && global_settings.crossfade_fade_out_mixmode;
-
- crossfade_auto_skip = auto_skip;
+ else
+ {
+ /* Don't enable mix mode when skipping tracks manually. */
+ crossfade_mixmode = auto_skip &&
+ global_settings.crossfade_fade_out_mixmode;
+
+ crossfade_auto_skip = auto_skip;
- crossfade_track_change_started = crossfade;
+ crossfade_track_change_started = crossfade;
+ }
#endif
+ pcm_play_unlock();
+
+ /* Keep trigger outside the play lock or HW FIFO underruns can happen
+ since frequency scaling is *not* always fast */
+ trigger_cpu_boost();
}
else /* automatic and not crossfading, so do gapless track change */
{
@@ -563,8 +592,7 @@ void pcmbuf_start_track_change(bool auto_skip)
* current track will be updated properly, and mark the current chunk
* as the last one in the track. */
logf(" gapless track change");
- track_transition = true;
- end_of_track = true;
+ pcmbuf_monitor_track_change(true);
}
}
@@ -674,7 +702,6 @@ void pcmbuf_play_stop(void)
crossfade_track_change_started = false;
crossfade_active = false;
#endif
- end_of_track = false;
track_transition = false;
flush_pcmbuf = false;
DISPLAY_DESC("play_stop");
diff --git a/apps/pcmbuf.h b/apps/pcmbuf.h
index 992eb8063f..618b1babad 100644
--- a/apps/pcmbuf.h
+++ b/apps/pcmbuf.h
@@ -32,6 +32,7 @@ size_t pcmbuf_init(unsigned char *bufend);
void pcmbuf_play_start(void);
void pcmbuf_play_stop(void);
void pcmbuf_pause(bool pause);
+void pcmbuf_monitor_track_change(bool monitor);
void pcmbuf_start_track_change(bool manual_skip);
/* Crossfade */
diff --git a/apps/playback.c b/apps/playback.c
index 5d91693cad..db07613bc8 100644
--- a/apps/playback.c
+++ b/apps/playback.c
@@ -22,7 +22,8 @@
/* TODO: Pause should be handled in here, rather than PCMBUF so that voice can
* play whilst audio is paused */
-
+#include "config.h"
+#include "system.h"
#include <string.h>
#include "playback.h"
#include "codec_thread.h"
@@ -92,6 +93,10 @@ static enum filling_state {
STATE_FULL, /* can't add any more tracks */
STATE_END_OF_PLAYLIST, /* all remaining tracks have been added */
STATE_FINISHED, /* all remaining tracks are fully buffered */
+ STATE_ENDING, /* audio playback is ending */
+#if (CONFIG_PLATFORM & PLATFORM_NATIVE)
+ STATE_USB, /* USB mode, ignore most messages */
+#endif
} filling;
/* As defined in plugins/lib/xxx2wav.h */
@@ -108,7 +113,6 @@ static bool audio_thread_ready SHAREDBSS_ATTR = false;
/* Main state control */
static volatile bool playing SHAREDBSS_ATTR = false;/* Is audio playing? (A) */
static volatile bool paused SHAREDBSS_ATTR = false; /* Is audio paused? (A/C-) */
-extern volatile bool audio_codec_loaded; /* Codec loaded? (C/A-) */
/* Ring buffer where compressed audio and codecs are loaded */
static unsigned char *filebuf = NULL; /* Start of buffer (A/C-) */
@@ -186,7 +190,7 @@ static int last_peek_offset = 0;
static unsigned long prev_track_elapsed = 0; /* Previous track elapsed time (C/A-)*/
/* Track change controls */
-bool automatic_skip = false; /* Who initiated in-progress skip? (C/A-) */
+static bool automatic_skip = false; /* Who initiated in-progress skip? (A) */
extern bool track_transition; /* Are we in a track transition? */
static bool dir_skip = false; /* Is a directory skip pending? (A) */
static bool new_playlist = false; /* Are we starting a new playlist? (A) */
@@ -208,7 +212,6 @@ static size_t buffer_margin = 5; /* Buffer margin aka anti-skip buffer (A/C-) *
/* Event queues */
struct event_queue audio_queue SHAREDBSS_ATTR;
-struct event_queue codec_queue SHAREDBSS_ATTR;
static struct event_queue pcmbuf_queue SHAREDBSS_ATTR;
extern struct codec_api ci;
@@ -241,15 +244,8 @@ static void audio_stop_playback(void);
void audio_pcmbuf_position_callback(unsigned int time)
{
time += othertrack_id3->elapsed;
-
- if (time >= othertrack_id3->length)
- {
- /* we just played the end of the track, so stop this callback */
- track_transition = false;
- othertrack_id3->elapsed = othertrack_id3->length;
- }
- else
- othertrack_id3->elapsed = time;
+ othertrack_id3->elapsed = (time >= othertrack_id3->length)
+ ? othertrack_id3->length : time;
}
/* Post message from pcmbuf that the end of the previous track
@@ -483,22 +479,8 @@ unsigned char *audio_get_recording_buffer(size_t *buffer_size)
bool audio_load_encoder(int afmt)
{
#if (CONFIG_PLATFORM & PLATFORM_NATIVE)
- const char *enc_fn = get_codec_filename(afmt | CODEC_TYPE_ENCODER);
- if (!enc_fn)
- return false;
-
- audio_remove_encoder();
- ci.enc_codec_loaded = 0; /* clear any previous error condition */
-
- LOGFQUEUE("codec > Q_ENCODER_LOAD_DISK");
- queue_post(&codec_queue, Q_ENCODER_LOAD_DISK, (intptr_t)enc_fn);
-
- while (ci.enc_codec_loaded == 0)
- yield();
-
- logf("codec loaded: %d", ci.enc_codec_loaded);
-
- return ci.enc_codec_loaded > 0;
+ LOGFQUEUE("audio >| Q_AUDIO_LOAD_ENCODER: %d", afmt);
+ return queue_send(&audio_queue, Q_AUDIO_LOAD_ENCODER, afmt) > 0;
#else
(void)afmt;
return true;
@@ -508,13 +490,8 @@ bool audio_load_encoder(int afmt)
void audio_remove_encoder(void)
{
#if (CONFIG_PLATFORM & PLATFORM_NATIVE)
- /* force encoder codec unload (if currently loaded) */
- if (ci.enc_codec_loaded <= 0)
- return;
-
- ci.stop_encoder = true;
- while (ci.enc_codec_loaded > 0)
- yield();
+ LOGFQUEUE("audio >| Q_AUDIO_LOAD_ENCODER: NULL");
+ queue_send(&audio_queue, Q_AUDIO_LOAD_ENCODER, AFMT_UNKNOWN);
#endif
} /* audio_remove_encoder */
@@ -833,6 +810,11 @@ int audio_status(void)
return ret;
}
+bool audio_automatic_skip(void)
+{
+ return automatic_skip;
+}
+
int audio_get_file_pos(void)
{
return 0;
@@ -952,7 +934,7 @@ static void buffering_handle_finished_callback(void *data)
int next_idx = (track_ridx + offset + 1) & MAX_TRACK_MASK;
/* The metadata handle for the last loaded track has been buffered.
We can ask the audio thread to load the rest of the track's data. */
- LOGFQUEUE("audio >| audio Q_AUDIO_FINISH_LOAD");
+ LOGFQUEUE("audio > audio Q_AUDIO_FINISH_LOAD");
queue_post(&audio_queue, Q_AUDIO_FINISH_LOAD, 0);
if (tracks[next_idx].id3_hid == hid)
send_event(PLAYBACK_EVENT_NEXTTRACKID3_AVAILABLE, NULL);
@@ -1101,9 +1083,7 @@ static bool audio_loadcodec(bool start_play)
ci.id3 = thistrack_id3;
ci.taginfo_ready = &CUR_TI->taginfo_ready;
ci.curpos = 0;
- LOGFQUEUE("codec > codec Q_CODEC_LOAD_DISK");
- queue_post(&codec_queue, Q_CODEC_LOAD_DISK, (intptr_t)codec_fn);
- return true;
+ return codec_load(-1, id3->codectype);
}
else
{
@@ -1115,15 +1095,20 @@ static bool audio_loadcodec(bool start_play)
id3 = bufgetid3(tracks[track_widx].id3_hid);
prev_id3 = bufgetid3(tracks[prev_track].id3_hid);
- /* If the previous codec is the same as this one, there is no need
- * to put another copy of it on the file buffer */
- if (id3 && prev_id3 &&
- get_codec_base_type(id3->codectype) ==
- get_codec_base_type(prev_id3->codectype)
- && audio_codec_loaded)
+ /* If the previous codec is the same as this one and the current
+ * one is the correct one, there is no need to put another copy of
+ * it on the file buffer */
+ if (id3 && prev_id3)
{
- logf("Reusing prev. codec");
- return true;
+ int codt = get_codec_base_type(id3->codectype);
+ int prev_codt = get_codec_base_type(prev_id3->codectype);
+ int cod_loaded = get_codec_base_type(codec_loaded());
+
+ if (codt == prev_codt && codt == cod_loaded)
+ {
+ logf("Reusing prev. codec");
+ return true;
+ }
}
}
}
@@ -1138,7 +1123,7 @@ static bool audio_loadcodec(bool start_play)
if (hid < 0 && hid != ERR_UNSUPPORTED_TYPE)
return false;
- if (hid > 0)
+ if (hid >= 0)
logf("Loaded codec");
else
logf("Buffering codec unsupported, load later from disk");
@@ -1562,22 +1547,54 @@ static void audio_rebuffer(void)
/* Called on request from the codec to get a new track. This is the codec part
of the track transition. */
-static int audio_check_new_track(void)
+static void audio_last_track(bool automatic)
{
- int track_count = audio_track_count();
- int old_track_ridx = track_ridx;
+ if (automatic)
+ {
+ ci.new_track = 0;
+ automatic_skip = false;
+
+ if (filling != STATE_ENDING)
+ {
+ /* Monitor remaining PCM before stopping */
+ filling = STATE_ENDING;
+ pcmbuf_monitor_track_change(true);
+ }
+
+ codec_stop();
+ }
+ else
+ {
+ audio_stop_playback();
+ }
+}
+
+static void audio_check_new_track(void)
+{
+ int track_count;
+ int old_track_ridx;
int i, idx;
bool forward;
- struct mp3entry *temp = thistrack_id3;
+ struct mp3entry *temp;
+
+ if (ci.new_track == 0)
+ {
+ ci.new_track++;
+ automatic_skip = true;
+ }
+
+ track_count = audio_track_count();
+ old_track_ridx = track_ridx;
/* Now it's good time to send track finish events. */
send_event(PLAYBACK_EVENT_TRACK_FINISH, thistrack_id3);
/* swap the mp3entry pointers */
+ temp = thistrack_id3;
thistrack_id3 = othertrack_id3;
othertrack_id3 = temp;
ci.id3 = thistrack_id3;
memset(thistrack_id3, 0, sizeof(struct mp3entry));
-
+
if (dir_skip)
{
dir_skip = false;
@@ -1600,8 +1617,8 @@ static int audio_check_new_track(void)
{
if (ci.new_track >= 0)
{
- LOGFQUEUE("audio >|= codec Q_CODEC_REQUEST_FAILED");
- return Q_CODEC_REQUEST_FAILED;
+ audio_last_track(true);
+ return;
}
ci.new_track++;
}
@@ -1612,8 +1629,9 @@ static int audio_check_new_track(void)
if (playlist_next(ci.new_track) < 0)
{
- LOGFQUEUE("audio >|= codec Q_CODEC_REQUEST_FAILED");
- return Q_CODEC_REQUEST_FAILED;
+ /* End of list */
+ audio_last_track(automatic_skip);
+ return;
}
if (new_playlist)
@@ -1646,7 +1664,6 @@ static int audio_check_new_track(void)
track_ridx = (track_ridx + ci.new_track) & MAX_TRACK_MASK;
buf_set_base_handle(CUR_TI->audio_hid);
-
if (automatic_skip)
{
wps_offset = -ci.new_track;
@@ -1707,8 +1724,23 @@ static int audio_check_new_track(void)
skip_done:
audio_update_trackinfo();
- LOGFQUEUE("audio >|= codec Q_CODEC_REQUEST_COMPLETE");
- return Q_CODEC_REQUEST_COMPLETE;
+ pcmbuf_start_track_change(automatic_skip);
+
+ if (get_codec_base_type(codec_loaded()) ==
+ get_codec_base_type(thistrack_id3->codectype))
+ {
+ /* codec is the same base type */
+ logf("New track loaded");
+ codec_ack_msg(Q_CODEC_REQUEST_COMPLETE, false);
+ }
+ else
+ {
+ /* a codec change is required */
+ logf("New codec: %d/%d", thistrack_id3->codectype, codec_loaded());
+ codec_ack_msg(Q_CODEC_REQUEST_COMPLETE, true);
+ codec_load(tracks[track_ridx].codec_hid, thistrack_id3->codectype);
+ tracks[track_ridx].codec_hid = -1; /* Codec thread will close it */
+ }
}
unsigned long audio_prev_elapsed(void)
@@ -1721,25 +1753,26 @@ void audio_set_prev_elapsed(unsigned long setting)
prev_track_elapsed = setting;
}
+/* Stop the codec and reset the PCM buffer */
static void audio_stop_codec_flush(void)
{
- ci.stop_codec = true;
+ bool pcm_playing;
+
pcmbuf_pause(true);
- while (audio_codec_loaded)
- yield();
+ codec_stop();
- /* If the audio codec is not loaded any more, and the audio is still
- * playing, it is now and _only_ now safe to call this function from the
- * audio thread */
- if (pcm_is_playing())
- {
- pcmbuf_play_stop();
- pcm_play_lock();
- queue_clear(&pcmbuf_queue);
- pcm_play_unlock();
- }
- pcmbuf_pause(paused);
+ pcm_play_lock();
+
+ pcm_playing = pcm_is_playing();
+
+ pcmbuf_play_stop();
+ queue_clear(&pcmbuf_queue);
+
+ if (pcm_playing)
+ pcmbuf_pause(paused);
+
+ pcm_play_unlock();
}
static void audio_stop_playback(void)
@@ -1750,12 +1783,7 @@ static void audio_stop_playback(void)
struct mp3entry *id3 = NULL;
if (!ci.stop_codec)
- {
- /* Set this early, the outside code yields and may allow the codec
- to try to wait for a reply on a buffer wait */
- ci.stop_codec = true;
id3 = audio_current_track();
- }
/* Save the current playing spot, or NULL if the playlist has ended */
playlist_update_resume_info(id3);
@@ -1797,7 +1825,6 @@ static void audio_play_start(size_t offset)
audio_set_output_source(AUDIO_SRC_PLAYBACK);
#endif
- /* Wait for any previously playing audio to flush - TODO: Not necessary? */
paused = false;
audio_stop_codec_flush();
@@ -1853,6 +1880,11 @@ static void audio_new_playlist(void)
{
/* Prepare to start a new fill from the beginning of the playlist */
last_peek_offset = -1;
+
+ /* Signal the codec to initiate a track change forward */
+ new_playlist = true;
+ ci.new_track = 1;
+
if (audio_have_tracks())
{
if (paused)
@@ -1866,10 +1898,6 @@ static void audio_new_playlist(void)
CUR_TI->taginfo_ready = false;
}
- /* Signal the codec to initiate a track change forward */
- new_playlist = true;
- ci.new_track = 1;
-
/* Officially playing */
queue_reply(&audio_queue, 1);
@@ -1919,6 +1947,59 @@ static void audio_finalise_track_change(void)
playlist_update_resume_info(audio_current_track());
}
+static void audio_seek_complete(void)
+{
+ logf("audio_seek_complete");
+
+ if (!playing)
+ return;
+
+ /* If seeking-while-playing, pcm_is_paused() is true.
+ * If seeking-while-paused, audio_status PAUSE is true.
+ * A seamless seek skips this section. */
+ ci.seek_time = 0;
+
+ pcm_play_lock();
+
+ if (pcm_is_paused() || paused)
+ {
+ /* Clear the buffer */
+ pcmbuf_play_stop();
+
+ /* If seeking-while-playing, resume PCM playback */
+ if (!paused)
+ pcmbuf_pause(false);
+ }
+
+ pcm_play_unlock();
+}
+
+static void audio_codec_status_message(long reason, int status)
+{
+ /* TODO: Push the errors up to the normal UI somewhere */
+ switch (reason)
+ {
+ case Q_CODEC_LOAD_DISK:
+ case Q_CODEC_LOAD:
+ if (!playing)
+ return;
+
+ if (status < 0)
+ {
+ splash(HZ*2, "Codec failure");
+ audio_check_new_track();
+ }
+ break;
+
+#ifdef AUDIO_HAVE_RECORDING
+ case Q_ENCODER_LOAD_DISK:
+ if (status < 0)
+ splash(HZ*2, "Encoder failure");
+ break;
+#endif /* AUDIO_HAVE_RECORDING */
+ }
+}
+
/*
* Layout audio buffer as follows - iram buffer depends on target:
* [|SWAP:iram][|TALK]|FILE|GUARD|PCM|[SWAP:dram[|iram]|]
@@ -1986,14 +2067,42 @@ static void audio_thread(void)
while (1)
{
- if (filling != STATE_FILLING && filling != STATE_IDLE) {
- /* End of buffering, let's calculate the watermark and unboost */
- set_filebuf_watermark();
- cancel_cpu_boost();
- }
+ switch (filling) {
+ case STATE_IDLE:
+ queue_wait(&audio_queue, &ev);
+ break;
- if (!pcmbuf_queue_scan(&ev))
- queue_wait_w_tmo(&audio_queue, &ev, HZ/2);
+#if (CONFIG_PLATFORM & PLATFORM_NATIVE)
+ case STATE_USB:
+ queue_wait(&audio_queue, &ev);
+ switch (ev.id) {
+#ifdef AUDIO_HAVE_RECORDING
+ /* Must monitor the encoder message for recording so it can
+ remove it if we process the insertion before it does. It
+ cannot simply be removed from under recording however. */
+ case Q_AUDIO_LOAD_ENCODER:
+ break;
+#endif
+ case SYS_USB_DISCONNECTED:
+ filling = STATE_IDLE;
+ default:
+ continue;
+ }
+ break;
+#endif /* CONFIG_PLATFORM */
+
+ default:
+ /* End of buffering, let's calculate the watermark and
+ unboost */
+ set_filebuf_watermark();
+ cancel_cpu_boost();
+ /* Fall-through */
+ case STATE_FILLING:
+ case STATE_ENDING:
+ if (!pcmbuf_queue_scan(&ev))
+ queue_wait_w_tmo(&audio_queue, &ev, HZ/2);
+ break;
+ }
switch (ev.id) {
@@ -2059,6 +2168,14 @@ static void audio_thread(void)
if (!playing)
break;
+ if (filling == STATE_ENDING)
+ {
+ /* Temp workaround: There is no codec available */
+ if (!paused)
+ pcmbuf_pause(false);
+ break;
+ }
+
if ((long)ev.data == 0)
{
/* About to restart the track - send track finish
@@ -2079,7 +2196,7 @@ static void audio_thread(void)
case Q_AUDIO_CHECK_NEW_TRACK:
LOGFQUEUE("audio < Q_AUDIO_CHECK_NEW_TRACK");
- queue_reply(&audio_queue, audio_check_new_track());
+ audio_check_new_track();
break;
case Q_AUDIO_DIR_SKIP:
@@ -2095,8 +2212,29 @@ static void audio_thread(void)
case Q_AUDIO_TRACK_CHANGED:
/* PCM track change done */
LOGFQUEUE("audio < Q_AUDIO_TRACK_CHANGED");
- audio_finalise_track_change();
+ if (filling != STATE_ENDING)
+ audio_finalise_track_change();
+ else if (playing)
+ audio_stop_playback();
+ break;
+
+ case Q_AUDIO_SEEK_COMPLETE:
+ /* Codec seek done */
+ LOGFQUEUE("audio < Q_AUDIO_SEEK_COMPLETE");
+ audio_seek_complete();
+ codec_ack_msg(Q_AUDIO_SEEK_COMPLETE, false);
break;
+
+ case Q_CODEC_LOAD:
+ case Q_CODEC_LOAD_DISK:
+#ifdef AUDIO_HAVE_RECORDING
+ case Q_ENCODER_LOAD_DISK:
+#endif
+ /* These are received when a codec has finished normally or
+ upon a codec error */
+ audio_codec_status_message(ev.id, ev.data);
+ break;
+
#if (CONFIG_PLATFORM & PLATFORM_NATIVE)
case SYS_USB_CONNECTED:
LOGFQUEUE("audio < SYS_USB_CONNECTED");
@@ -2105,16 +2243,25 @@ static void audio_thread(void)
#ifdef PLAYBACK_VOICE
voice_stop();
#endif
+ filling = STATE_USB;
usb_acknowledge(SYS_USB_CONNECTED_ACK);
- usb_wait_for_disconnect(&audio_queue);
+ break;
+#endif
+
+#ifdef AUDIO_HAVE_RECORDING
+ case Q_AUDIO_LOAD_ENCODER:
+ if (playing)
+ audio_stop_playback();
+ else
+ codec_stop(); /* If encoder still loaded, stop it */
- /* Mark all entries null. */
- audio_clear_track_entries();
+ if (ev.data == AFMT_UNKNOWN)
+ break;
- /* release tracks to make sure all handles are closed */
- audio_release_tracks();
+ queue_reply(&audio_queue,
+ codec_load(-1, ev.data | CODEC_TYPE_ENCODER));
break;
-#endif
+#endif /* AUDIO_HAVE_RECORDING */
case SYS_TIMEOUT:
LOGFQUEUE_SYS_TIMEOUT("audio < SYS_TIMEOUT");
@@ -2147,7 +2294,6 @@ void audio_init(void)
to send messages. Thread creation will be delayed however so nothing
starts running until ready if something yields such as talk_init. */
queue_init(&audio_queue, true);
- queue_init(&codec_queue, false);
queue_init(&pcmbuf_queue, false);
pcm_init();
@@ -2227,7 +2373,7 @@ void audio_init(void)
#ifdef PLAYBACK_VOICE
voice_thread_resume();
#endif
- thread_thaw(codec_thread_id);
+ codec_thread_resume();
thread_thaw(audio_thread_id);
} /* audio_init */
@@ -2246,8 +2392,3 @@ int get_audio_hid()
{
return CUR_TI->audio_hid;
}
-
-int *get_codec_hid()
-{
- return &tracks[track_ridx].codec_hid;
-}
diff --git a/apps/playback.h b/apps/playback.h
index 475e2fb662..76c394603f 100644
--- a/apps/playback.h
+++ b/apps/playback.h
@@ -81,9 +81,9 @@ 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);
-int *get_codec_hid(void);
void audio_set_prev_elapsed(unsigned long setting);
bool audio_buffer_state_trashed(void);
+bool audio_automatic_skip(void);
/* Define one constant that includes recording related functionality */
#if defined(HAVE_RECORDING) && !defined(SIMULATOR)
@@ -101,6 +101,7 @@ enum {
Q_AUDIO_CHECK_NEW_TRACK,
Q_AUDIO_FLUSH,
Q_AUDIO_TRACK_CHANGED,
+ Q_AUDIO_SEEK_COMPLETE,
Q_AUDIO_DIR_SKIP,
Q_AUDIO_POSTINIT,
Q_AUDIO_FILL_BUFFER,
@@ -112,11 +113,13 @@ enum {
Q_CODEC_LOAD_DISK,
#ifdef AUDIO_HAVE_RECORDING
+ Q_AUDIO_LOAD_ENCODER,
Q_ENCODER_LOAD_DISK,
Q_ENCODER_RECORD,
#endif
Q_CODEC_DO_CALLBACK,
+ Q_CODEC_ACK,
};
#endif
diff --git a/apps/plugin.c b/apps/plugin.c
index 690aee9bf3..192488ef81 100644
--- a/apps/plugin.c
+++ b/apps/plugin.c
@@ -691,6 +691,8 @@ static const struct plugin_api rockbox_api = {
#if CONFIG_CODEC == SWCODEC
codec_thread_do_callback,
codec_load_file,
+ codec_begin,
+ codec_close,
get_codec_filename,
find_array_ptr,
remove_array_ptr,
diff --git a/apps/plugin.h b/apps/plugin.h
index 77dedc539a..8c2d458c57 100644
--- a/apps/plugin.h
+++ b/apps/plugin.h
@@ -145,12 +145,12 @@ void* plugin_get_buffer(size_t *buffer_size);
#define PLUGIN_MAGIC 0x526F634B /* RocK */
/* increase this every time the api struct changes */
-#define PLUGIN_API_VERSION 198
+#define PLUGIN_API_VERSION 199
/* update this to latest version if a change to the api struct breaks
backwards compatibility (and please take the opportunity to sort in any
new function which are "waiting" at the end of the function table) */
-#define PLUGIN_MIN_API_VERSION 198
+#define PLUGIN_MIN_API_VERSION 199
/* plugin return codes */
/* internal returns start at 0x100 to make exit(1..255) work */
@@ -798,7 +798,9 @@ struct plugin_api {
#if CONFIG_CODEC == SWCODEC
void (*codec_thread_do_callback)(void (*fn)(void),
unsigned int *audio_thread_id);
- int (*codec_load_file)(const char* codec, struct codec_api *api);
+ void * (*codec_load_file)(const char* codec, struct codec_api *api);
+ int (*codec_begin)(void *handle);
+ void (*codec_close)(void *handle);
const char *(*get_codec_filename)(int cod_spec);
void ** (*find_array_ptr)(void **arr, void *ptr);
int (*remove_array_ptr)(void **arr, void *ptr);
diff --git a/apps/plugins/test_codec.c b/apps/plugins/test_codec.c
index 9b00fdbb95..30757381bf 100644
--- a/apps/plugins/test_codec.c
+++ b/apps/plugins/test_codec.c
@@ -516,13 +516,6 @@ static bool request_next_track(void)
}
-/* Free the buffer area of the current codec after its loaded */
-static void discard_codec(void)
-{
- /* ??? */
-}
-
-
static void set_offset(size_t value)
{
/* ??? */
@@ -576,7 +569,6 @@ static void init_ci(void)
ci.seek_buffer = seek_buffer;
ci.seek_complete = seek_complete;
ci.request_next_track = request_next_track;
- ci.discard_codec = discard_codec;
ci.set_offset = set_offset;
ci.configure = configure;
ci.dsp = (struct dsp_config *)rb->dsp_configure(NULL, DSP_MYDSP,
@@ -636,12 +628,19 @@ static void init_ci(void)
static void codec_thread(void)
{
const char* codecname;
- int res;
+ void *handle;
+ int res = CODEC_ERROR;
codecname = rb->get_codec_filename(track.id3.codectype);
/* Load the codec and start decoding. */
- res = rb->codec_load_file(codecname,&ci);
+ handle = rb->codec_load_file(codecname,&ci);
+
+ if (handle != NULL)
+ {
+ res = rb->codec_begin(handle);
+ rb->codec_close(handle);
+ }
/* Signal to the main thread that we are done */
endtick = *rb->current_tick - rebuffertick;
diff --git a/apps/recorder/pcm_record.c b/apps/recorder/pcm_record.c
index 6ecb75f608..8c9207f232 100644
--- a/apps/recorder/pcm_record.c
+++ b/apps/recorder/pcm_record.c
@@ -31,6 +31,7 @@
#include "usb.h"
#include "buffer.h"
#include "general.h"
+#include "codec_thread.h"
#include "audio.h"
#include "sound.h"
#include "metadata.h"
@@ -41,8 +42,6 @@
/***************************************************************************/
-extern unsigned int codec_thread_id;
-
/** General recording state **/
static bool is_recording; /* We are recording */
static bool is_paused; /* We have paused */
@@ -900,8 +899,8 @@ static void pcmrec_flush(unsigned flush_num)
num >= flood_watermark ? "num" : "time");
prio_pcmrec = thread_set_priority(THREAD_ID_CURRENT,
thread_get_priority(THREAD_ID_CURRENT) - 4);
- prio_codec = thread_set_priority(codec_thread_id,
- thread_get_priority(codec_thread_id) - 4);
+ prio_codec = codec_thread_set_priority(
+ codec_thread_get_priority() - 4);
}
#endif
@@ -952,7 +951,7 @@ static void pcmrec_flush(unsigned flush_num)
/* return to original priorities */
logf("pcmrec: unboost priority");
thread_set_priority(THREAD_ID_CURRENT, prio_pcmrec);
- thread_set_priority(codec_thread_id, prio_codec);
+ codec_thread_set_priority(prio_codec);
}
last_flush_tick = current_tick; /* save tick when we left */
diff --git a/apps/tagtree.c b/apps/tagtree.c
index ecc9f44d4d..575ab221ac 100644
--- a/apps/tagtree.c
+++ b/apps/tagtree.c
@@ -52,6 +52,7 @@
#include "appevents.h"
#include "storage.h"
#include "dir.h"
+#include "playback.h"
#define str_or_empty(x) (x ? x : "(NULL)")
@@ -170,10 +171,6 @@ static int current_entry_count;
static struct tree_context *tc;
-#if CONFIG_CODEC == SWCODEC
-extern bool automatic_skip; /* Who initiated in-progress skip? (C/A-) */
-#endif
-
static int get_token_str(char *buf, int size)
{
/* Find the start. */
@@ -726,7 +723,7 @@ static void tagtree_track_finish_event(void *data)
first 15 seconds. */
if (id3->elapsed == 0
#if CONFIG_CODEC == SWCODEC /* HWCODEC doesn't have automatic_skip */
- || (id3->elapsed < 15 * 1000 && !automatic_skip)
+ || (id3->elapsed < 15 * 1000 && !audio_automatic_skip())
#endif
)
{
@@ -766,7 +763,7 @@ static void tagtree_track_finish_event(void *data)
if (global_settings.autoresume_enable)
{
unsigned long offset
- = automatic_skip ? 0 : id3->offset;
+ = audio_automatic_skip() ? 0 : id3->offset;
tagcache_update_numeric(tagcache_idx, tag_lastoffset, offset);