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);