From 456170afdf8eb9c43abe7580a3b522c934273a1e Mon Sep 17 00:00:00 2001 From: Michael Sevakis Date: Tue, 30 Aug 2011 15:35:25 +0000 Subject: Clean up and simplify the voice thread code. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@30386 a1c6a512-1295-4272-9138-f99709370657 --- apps/voice_thread.c | 319 ++++++++++++++++++++++++---------------------------- 1 file changed, 148 insertions(+), 171 deletions(-) diff --git a/apps/voice_thread.c b/apps/voice_thread.c index a3dc1e16ab..5a3ad46801 100644 --- a/apps/voice_thread.c +++ b/apps/voice_thread.c @@ -78,7 +78,7 @@ static const char voice_thread_name[] = "voice"; /* Voice thread synchronization objects */ static struct event_queue voice_queue SHAREDBSS_ATTR; static struct queue_sender_list voice_queue_sender_list SHAREDBSS_ATTR; -static bool voice_done SHAREDDATA_ATTR = true; +static int quiet_counter SHAREDDATA_ATTR = 0; /* Buffer for decoded samples */ static spx_int16_t voice_output_buf[VOICE_FRAME_SIZE] CACHEALIGN_ATTR; @@ -96,20 +96,20 @@ static size_t voicebuf_sizes[VOICE_FRAMES]; static uint32_t (* voicebuf)[VOICE_PCM_FRAME_COUNT]; static unsigned int cur_buf_in, cur_buf_out; -/* A delay to not bring audio back to normal level too soon */ -#define QUIET_COUNT 3 - -enum voice_thread_states +/* Voice processing states */ +enum voice_state { - TSTATE_STOPPED = 0, /* Voice thread is stopped and awaiting commands */ - TSTATE_DECODE, /* Voice is decoding a clip */ - TSTATE_BUFFER_INSERT, /* Voice is sending decoded audio to PCM */ + VOICE_STATE_MESSAGE = 0, + VOICE_STATE_DECODE, + VOICE_STATE_BUFFER_INSERT, }; +/* A delay to not bring audio back to normal level too soon */ +#define QUIET_COUNT 3 + enum voice_thread_messages { - Q_VOICE_NULL = 0, /* A message for thread sync - no effect on state */ - Q_VOICE_PLAY, /* Play a clip */ + Q_VOICE_PLAY = 0, /* Play a clip */ Q_VOICE_STOP, /* Stop current clip */ }; @@ -125,7 +125,6 @@ struct voice_info * internal functions */ struct voice_thread_data { - volatile int state; /* Thread state (TSTATE_*) */ struct queue_event ev; /* Last queue event pulled from queue */ void *st; /* Decoder instance */ SpeexBits bits; /* Bit cursor */ @@ -134,9 +133,14 @@ struct voice_thread_data const char *src[2]; /* Current output buffer pointers */ int lookahead; /* Number of samples to drop at start of clip */ int count; /* Count of samples remaining to send to PCM */ - int quiet_counter; /* Countdown until audio goes back to normal */ }; +/* Functions called in their repective state that return the next state to + state machine loop - compiler may inline them at its discretion */ +static enum voice_state voice_message(struct voice_thread_data *td); +static enum voice_state voice_decode(struct voice_thread_data *td); +static enum voice_state voice_buffer_insert(struct voice_thread_data *td); + /* Number of frames in queue */ static inline int voice_unplayed_frames(void) { @@ -229,7 +233,7 @@ void mp3_play_pause(bool play) /* Tell if voice is still in a playing state */ bool mp3_is_playing(void) { - return !voice_done; + return quiet_counter != 0; } /* This function is meant to be used by the buffer request functions to @@ -247,7 +251,7 @@ void voice_wait(void) * new clip by the time we wait. This should be resolvable if conditions * ever require knowing the very clip you requested has finished. */ - while (!voice_done) + while (quiet_counter != 0) sleep(1); } @@ -255,7 +259,6 @@ void voice_wait(void) * setup the DSP parameters */ static void voice_data_init(struct voice_thread_data *td) { - td->state = TSTATE_STOPPED; td->dsp = (struct dsp_config *)dsp_configure(NULL, DSP_MYDSP, CODEC_IDX_VOICE); @@ -265,204 +268,178 @@ static void voice_data_init(struct voice_thread_data *td) dsp_configure(td->dsp, DSP_SET_STEREO_MODE, STEREO_MONO); mixer_channel_set_amplitude(PCM_MIXER_CHAN_VOICE, MIX_AMP_UNITY); - td->quiet_counter = 0; } /* Voice thread message processing */ -static void voice_message(struct voice_thread_data *td) +static enum voice_state voice_message(struct voice_thread_data *td) { - while (1) + if (quiet_counter > 0) + queue_wait_w_tmo(&voice_queue, &td->ev, HZ/10); + else + queue_wait(&voice_queue, &td->ev); + + switch (td->ev.id) { - switch (td->ev.id) + case Q_VOICE_PLAY: + LOGFQUEUE("voice < Q_VOICE_PLAY"); + if (quiet_counter == 0) { - case Q_VOICE_PLAY: - LOGFQUEUE("voice < Q_VOICE_PLAY"); - voice_done = false; - - /* Copy the clip info */ - td->vi = *(struct voice_info *)td->ev.data; - - /* Be sure audio buffer is initialized */ - audio_restore_playback(AUDIO_WANT_VOICE); - - /* We need nothing more from the sending thread - let it run */ - queue_reply(&voice_queue, 1); - - if (td->state == TSTATE_STOPPED) - { - /* Boost CPU now */ - trigger_cpu_boost(); - } - else - { - /* Stop any clip still playing */ - voice_stop_playback(); - } - - /* Make audio play more softly and set delay to return to normal - playback level */ - pcmbuf_soft_mode(true); - td->quiet_counter = QUIET_COUNT; - - /* Clean-start the decoder */ - td->st = speex_decoder_init(&speex_wb_mode); + /* Boost CPU now */ + trigger_cpu_boost(); + } + else + { + /* Stop any clip still playing */ + voice_stop_playback(); + } - /* Make bit buffer use our own buffer */ - speex_bits_set_bit_buffer(&td->bits, td->vi.start, td->vi.size); - speex_decoder_ctl(td->st, SPEEX_GET_LOOKAHEAD, &td->lookahead); + quiet_counter = QUIET_COUNT; - td->state = TSTATE_DECODE; - return; + /* Copy the clip info */ + td->vi = *(struct voice_info *)td->ev.data; - case SYS_TIMEOUT: - if (voice_unplayed_frames()) - { - /* Waiting for PCM to finish */ - break; - } + /* Be sure audio buffer is initialized */ + audio_restore_playback(AUDIO_WANT_VOICE); - /* Drop through and stop the first time after clip runs out */ - if (td->quiet_counter-- != QUIET_COUNT) - { - if (td->quiet_counter <= 0) - pcmbuf_soft_mode(false); + /* We need nothing more from the sending thread - let it run */ + queue_reply(&voice_queue, 1); - break; - } + /* Make audio play more softly and set delay to return to normal + playback level */ + pcmbuf_soft_mode(true); - /* Fall-through */ - case Q_VOICE_STOP: - LOGFQUEUE("voice < Q_VOICE_STOP"); + /* Clean-start the decoder */ + td->st = speex_decoder_init(&speex_wb_mode); - td->state = TSTATE_STOPPED; - voice_done = true; + /* Make bit buffer use our own buffer */ + speex_bits_set_bit_buffer(&td->bits, td->vi.start, td->vi.size); + speex_decoder_ctl(td->st, SPEEX_GET_LOOKAHEAD, &td->lookahead); - cancel_cpu_boost(); - voice_stop_playback(); - break; + return VOICE_STATE_DECODE; - default: - /* Default messages get a reply and thread continues with no - * state transition */ - LOGFQUEUE("voice < default"); + case SYS_TIMEOUT: + if (voice_unplayed_frames()) + { + /* Waiting for PCM to finish */ + break; + } - if (td->state == TSTATE_STOPPED) - break; /* Not in (active) playback state */ + /* Drop through and stop the first time after clip runs out */ + if (quiet_counter-- != QUIET_COUNT) + { + if (quiet_counter <= 0) + pcmbuf_soft_mode(false); - queue_reply(&voice_queue, 0); - return; + break; } - if (td->quiet_counter > 0) - queue_wait_w_tmo(&voice_queue, &td->ev, HZ/10); - else - queue_wait(&voice_queue, &td->ev); + /* Fall-through */ + case Q_VOICE_STOP: + LOGFQUEUE("voice < Q_VOICE_STOP"); + cancel_cpu_boost(); + voice_stop_playback(); + break; + + /* No default: no other message ids are sent */ } + + return VOICE_STATE_MESSAGE; } -/* Voice thread entrypoint */ -static void NORETURN_ATTR voice_thread(void) +/* Decode frames or stop if all have completed */ +static enum voice_state voice_decode(struct voice_thread_data *td) { - struct voice_thread_data td; - char *dest; + if (!queue_empty(&voice_queue)) + return VOICE_STATE_MESSAGE; - voice_data_init(&td); - - /* audio thread will only set this once after it finished the final - * audio hardware init so this little construct is safe - even - * cross-core. */ - while (!audio_is_thread_ready()) - sleep(0); - - goto message_wait; - - while (1) + /* Decode the data */ + if (speex_decode_int(td->st, &td->bits, voice_output_buf) < 0) { - td.state = TSTATE_DECODE; + /* End of stream or error - get next clip */ + td->vi.size = 0; + + if (td->vi.get_more != NULL) + td->vi.get_more(&td->vi.start, &td->vi.size); - if (!queue_empty(&voice_queue)) + if (td->vi.start != NULL && (ssize_t)td->vi.size > 0) { - message_wait: - queue_wait(&voice_queue, &td.ev); - - message_process: - voice_message(&td); - - /* Branch to initial start point or branch back to previous - * operation if interrupted by a message */ - switch (td.state) - { - case TSTATE_DECODE: goto voice_decode; - case TSTATE_BUFFER_INSERT: goto buffer_insert; - default: goto message_wait; - } + /* Make bit buffer use our own buffer */ + speex_bits_set_bit_buffer(&td->bits, td->vi.start, td->vi.size); + /* Don't skip any samples when we're stringing clips together */ + td->lookahead = 0; } - - voice_decode: - /* Decode the data */ - if (speex_decode_int(td.st, &td.bits, voice_output_buf) < 0) + else { - /* End of stream or error - get next clip */ - td.vi.size = 0; - - if (td.vi.get_more != NULL) - td.vi.get_more(&td.vi.start, &td.vi.size); - - if (td.vi.start != NULL && (ssize_t)td.vi.size > 0) - { - /* Make bit buffer use our own buffer */ - speex_bits_set_bit_buffer(&td.bits, td.vi.start, td.vi.size); - /* Don't skip any samples when we're stringing clips together */ - td.lookahead = 0; - - /* Paranoid check - be sure never to somehow get stuck in a - * loop without listening to the queue */ - yield(); - - if (!queue_empty(&voice_queue)) - goto message_wait; - else - goto voice_decode; - } - /* If all clips are done and not playing, force pcm playback. */ voice_start_playback(); - - td.state = TSTATE_STOPPED; - td.ev.id = SYS_TIMEOUT; - goto message_process; + return VOICE_STATE_MESSAGE; } - + } + else + { yield(); /* Output the decoded frame */ - td.count = VOICE_FRAME_SIZE - td.lookahead; - td.src[0] = (const char *)&voice_output_buf[td.lookahead]; - td.src[1] = NULL; - td.lookahead -= MIN(VOICE_FRAME_SIZE, td.lookahead); + td->count = VOICE_FRAME_SIZE - td->lookahead; + td->src[0] = (const char *)&voice_output_buf[td->lookahead]; + td->src[1] = NULL; + td->lookahead -= MIN(VOICE_FRAME_SIZE, td->lookahead); - if (td.count <= 0) - continue; + if (td->count > 0) + return VOICE_STATE_BUFFER_INSERT; + } - td.state = TSTATE_BUFFER_INSERT; + return VOICE_STATE_DECODE; +} - buffer_insert: - /* Process the PCM samples in the DSP and send out for mixing */ +/* Process the PCM samples in the DSP and send out for mixing */ +static enum voice_state voice_buffer_insert(struct voice_thread_data *td) +{ + if (!queue_empty(&voice_queue)) + return VOICE_STATE_MESSAGE; - while (1) - { - if (!queue_empty(&voice_queue)) - goto message_wait; + char *dest = (char *)voice_buf_get(); - if ((dest = (char *)voice_buf_get()) != NULL) - break; + if (dest != NULL) + { + voice_buf_commit(dsp_process(td->dsp, dest, td->src, td->count) + * sizeof (int32_t)); + return VOICE_STATE_DECODE; + } - sleep(0); - } + sleep(0); + return VOICE_STATE_BUFFER_INSERT; +} - voice_buf_commit(dsp_process(td.dsp, dest, td.src, td.count) - * sizeof (int32_t)); - } /* end while */ +/* Voice thread entrypoint */ +static void NORETURN_ATTR voice_thread(void) +{ + struct voice_thread_data td; + enum voice_state state = VOICE_STATE_MESSAGE; + + voice_data_init(&td); + + /* audio thread will only set this once after it finished the final + * audio hardware init so this little construct is safe - even + * cross-core. */ + while (!audio_is_thread_ready()) + sleep(0); + + while (1) + { + switch (state) + { + case VOICE_STATE_MESSAGE: + state = voice_message(&td); + break; + case VOICE_STATE_DECODE: + state = voice_decode(&td); + break; + case VOICE_STATE_BUFFER_INSERT: + state = voice_buffer_insert(&td); + break; + } + } } /* Initialize all synchronization objects create the thread */ -- cgit v1.2.3