summaryrefslogtreecommitdiffstats
path: root/apps/voice_thread.c
diff options
context:
space:
mode:
authorMichael Sevakis <jethead71@rockbox.org>2011-08-30 15:35:25 +0000
committerMichael Sevakis <jethead71@rockbox.org>2011-08-30 15:35:25 +0000
commit456170afdf8eb9c43abe7580a3b522c934273a1e (patch)
tree92ea27e5de3815e7e7418fdadf0311a659c44c2a /apps/voice_thread.c
parentbb000ce525d4552e1e177943f932804be6ecdaa4 (diff)
downloadrockbox-456170afdf8eb9c43abe7580a3b522c934273a1e.tar.gz
rockbox-456170afdf8eb9c43abe7580a3b522c934273a1e.tar.bz2
rockbox-456170afdf8eb9c43abe7580a3b522c934273a1e.zip
Clean up and simplify the voice thread code.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@30386 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps/voice_thread.c')
-rw-r--r--apps/voice_thread.c319
1 files 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 */