From 65109732230849eeb9eec2f56f9e046ad6b476c3 Mon Sep 17 00:00:00 2001 From: Michael Sevakis Date: Wed, 23 Feb 2011 14:31:13 +0000 Subject: [PATCH] Give playback engine better control over the codec. Codec simply follows commands and doesn't concern itself with audio state. Get track change notification in on the actual last buffer insert of the track because now audio simply waits for a track change notify from PCM on the last track and it must be sent reliably. This is still at an intermediate stage but works. Codecs and plugins become incompatible. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@29387 a1c6a512-1295-4272-9138-f99709370657 --- apps/codec_thread.c | 474 +++++++++++++++++-------------------- apps/codec_thread.h | 21 +- apps/codecs.c | 63 +++-- apps/codecs.h | 21 +- apps/codecs/aiff_enc.c | 13 +- apps/codecs/mp3_enc.c | 13 +- apps/codecs/wav_enc.c | 13 +- apps/codecs/wavpack_enc.c | 15 +- apps/pcmbuf.c | 67 ++++-- apps/pcmbuf.h | 1 + apps/playback.c | 357 +++++++++++++++++++--------- apps/playback.h | 5 +- apps/plugin.c | 2 + apps/plugin.h | 8 +- apps/plugins/test_codec.c | 19 +- apps/recorder/pcm_record.c | 9 +- apps/tagtree.c | 9 +- 17 files changed, 614 insertions(+), 496 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()) - return false; + audio_set_prev_elapsed(thistrack_id3->elapsed); - /* 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)) +#ifdef AB_REPEAT_ENABLE + ab_end_of_track_report(); +#endif + + if (ci.stop_codec) { - logf("New track loaded"); - codec_discard_codec_callback(); - return true; - } - else - { - logf("New codec:%d/%d", thistrack_id3->codectype, prev_codectype); + /* Handle ACK in outer loop */ + LOGFQUEUE("codec: already stopping"); return false; } + + 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) @@ -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) { + queue_wait(&codec_queue, &ev); + + 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; + default: + LOGFQUEUE("codec < default : %ld", ev.id); + } + + if (handle) + { + /* Codec loaded - call the entrypoint */ + yield(); + logf("codec running"); + status = codec_begin(handle); + logf("codec stopped"); + codec_close(handle); + current_codectype = AFMT_UNKNOWN; + + if (ci.stop_codec) + status = CODEC_OK; + } + + switch (ev.id) + { #ifdef AUDIO_HAVE_RECORDING case Q_ENCODER_LOAD_DISK: - 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"); - } - - if (audio_codec_loaded) - { - if (ci.stop_codec) - { - status = CODEC_OK; - if (!(audio_status() & AUDIO_STATUS_PLAY)) - pcmbuf_play_stop(); - - } - audio_codec_loaded = false; - } - - switch (ev.id) { +#endif 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); - } - } - } + /* Notify about the status */ + if (!handle) + status = CODEC_ERROR; + LOGFQUEUE("codec > audio notify status: %d", status); + queue_post(&audio_queue, ev.id, status); break; - -#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; - 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 +/* 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; - } - handle = lc_open_from_mem(codecbuf, rc); - if (handle == NULL) - { - 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) { + logf("error loading codec"); + 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_track_change_started = crossfade; + crossfade_auto_skip = auto_skip; + + 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 #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,17 +2243,26 @@ 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); - - /* Mark all entries null. */ - audio_clear_track_entries(); - - /* release tracks to make sure all handles are closed */ - audio_release_tracks(); 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 */ + + if (ev.data == AFMT_UNKNOWN) + break; + + queue_reply(&audio_queue, + codec_load(-1, ev.data | CODEC_TYPE_ENCODER)); + break; +#endif /* AUDIO_HAVE_RECORDING */ + case SYS_TIMEOUT: LOGFQUEUE_SYS_TIMEOUT("audio < SYS_TIMEOUT"); break; @@ -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);