diff options
author | Michael Sevakis <jethead71@rockbox.org> | 2011-08-30 15:35:25 +0000 |
---|---|---|
committer | Michael Sevakis <jethead71@rockbox.org> | 2011-08-30 15:35:25 +0000 |
commit | 456170afdf8eb9c43abe7580a3b522c934273a1e (patch) | |
tree | 92ea27e5de3815e7e7418fdadf0311a659c44c2a | |
parent | bb000ce525d4552e1e177943f932804be6ecdaa4 (diff) | |
download | rockbox-456170afdf8eb9c43abe7580a3b522c934273a1e.tar.gz 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
-rw-r--r-- | apps/voice_thread.c | 319 |
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"; | |||
78 | /* Voice thread synchronization objects */ | 78 | /* Voice thread synchronization objects */ |
79 | static struct event_queue voice_queue SHAREDBSS_ATTR; | 79 | static struct event_queue voice_queue SHAREDBSS_ATTR; |
80 | static struct queue_sender_list voice_queue_sender_list SHAREDBSS_ATTR; | 80 | static struct queue_sender_list voice_queue_sender_list SHAREDBSS_ATTR; |
81 | static bool voice_done SHAREDDATA_ATTR = true; | 81 | static int quiet_counter SHAREDDATA_ATTR = 0; |
82 | 82 | ||
83 | /* Buffer for decoded samples */ | 83 | /* Buffer for decoded samples */ |
84 | static spx_int16_t voice_output_buf[VOICE_FRAME_SIZE] CACHEALIGN_ATTR; | 84 | static spx_int16_t voice_output_buf[VOICE_FRAME_SIZE] CACHEALIGN_ATTR; |
@@ -96,20 +96,20 @@ static size_t voicebuf_sizes[VOICE_FRAMES]; | |||
96 | static uint32_t (* voicebuf)[VOICE_PCM_FRAME_COUNT]; | 96 | static uint32_t (* voicebuf)[VOICE_PCM_FRAME_COUNT]; |
97 | static unsigned int cur_buf_in, cur_buf_out; | 97 | static unsigned int cur_buf_in, cur_buf_out; |
98 | 98 | ||
99 | /* A delay to not bring audio back to normal level too soon */ | 99 | /* Voice processing states */ |
100 | #define QUIET_COUNT 3 | 100 | enum voice_state |
101 | |||
102 | enum voice_thread_states | ||
103 | { | 101 | { |
104 | TSTATE_STOPPED = 0, /* Voice thread is stopped and awaiting commands */ | 102 | VOICE_STATE_MESSAGE = 0, |
105 | TSTATE_DECODE, /* Voice is decoding a clip */ | 103 | VOICE_STATE_DECODE, |
106 | TSTATE_BUFFER_INSERT, /* Voice is sending decoded audio to PCM */ | 104 | VOICE_STATE_BUFFER_INSERT, |
107 | }; | 105 | }; |
108 | 106 | ||
107 | /* A delay to not bring audio back to normal level too soon */ | ||
108 | #define QUIET_COUNT 3 | ||
109 | |||
109 | enum voice_thread_messages | 110 | enum voice_thread_messages |
110 | { | 111 | { |
111 | Q_VOICE_NULL = 0, /* A message for thread sync - no effect on state */ | 112 | Q_VOICE_PLAY = 0, /* Play a clip */ |
112 | Q_VOICE_PLAY, /* Play a clip */ | ||
113 | Q_VOICE_STOP, /* Stop current clip */ | 113 | Q_VOICE_STOP, /* Stop current clip */ |
114 | }; | 114 | }; |
115 | 115 | ||
@@ -125,7 +125,6 @@ struct voice_info | |||
125 | * internal functions */ | 125 | * internal functions */ |
126 | struct voice_thread_data | 126 | struct voice_thread_data |
127 | { | 127 | { |
128 | volatile int state; /* Thread state (TSTATE_*) */ | ||
129 | struct queue_event ev; /* Last queue event pulled from queue */ | 128 | struct queue_event ev; /* Last queue event pulled from queue */ |
130 | void *st; /* Decoder instance */ | 129 | void *st; /* Decoder instance */ |
131 | SpeexBits bits; /* Bit cursor */ | 130 | SpeexBits bits; /* Bit cursor */ |
@@ -134,9 +133,14 @@ struct voice_thread_data | |||
134 | const char *src[2]; /* Current output buffer pointers */ | 133 | const char *src[2]; /* Current output buffer pointers */ |
135 | int lookahead; /* Number of samples to drop at start of clip */ | 134 | int lookahead; /* Number of samples to drop at start of clip */ |
136 | int count; /* Count of samples remaining to send to PCM */ | 135 | int count; /* Count of samples remaining to send to PCM */ |
137 | int quiet_counter; /* Countdown until audio goes back to normal */ | ||
138 | }; | 136 | }; |
139 | 137 | ||
138 | /* Functions called in their repective state that return the next state to | ||
139 | state machine loop - compiler may inline them at its discretion */ | ||
140 | static enum voice_state voice_message(struct voice_thread_data *td); | ||
141 | static enum voice_state voice_decode(struct voice_thread_data *td); | ||
142 | static enum voice_state voice_buffer_insert(struct voice_thread_data *td); | ||
143 | |||
140 | /* Number of frames in queue */ | 144 | /* Number of frames in queue */ |
141 | static inline int voice_unplayed_frames(void) | 145 | static inline int voice_unplayed_frames(void) |
142 | { | 146 | { |
@@ -229,7 +233,7 @@ void mp3_play_pause(bool play) | |||
229 | /* Tell if voice is still in a playing state */ | 233 | /* Tell if voice is still in a playing state */ |
230 | bool mp3_is_playing(void) | 234 | bool mp3_is_playing(void) |
231 | { | 235 | { |
232 | return !voice_done; | 236 | return quiet_counter != 0; |
233 | } | 237 | } |
234 | 238 | ||
235 | /* This function is meant to be used by the buffer request functions to | 239 | /* This function is meant to be used by the buffer request functions to |
@@ -247,7 +251,7 @@ void voice_wait(void) | |||
247 | * new clip by the time we wait. This should be resolvable if conditions | 251 | * new clip by the time we wait. This should be resolvable if conditions |
248 | * ever require knowing the very clip you requested has finished. */ | 252 | * ever require knowing the very clip you requested has finished. */ |
249 | 253 | ||
250 | while (!voice_done) | 254 | while (quiet_counter != 0) |
251 | sleep(1); | 255 | sleep(1); |
252 | } | 256 | } |
253 | 257 | ||
@@ -255,7 +259,6 @@ void voice_wait(void) | |||
255 | * setup the DSP parameters */ | 259 | * setup the DSP parameters */ |
256 | static void voice_data_init(struct voice_thread_data *td) | 260 | static void voice_data_init(struct voice_thread_data *td) |
257 | { | 261 | { |
258 | td->state = TSTATE_STOPPED; | ||
259 | td->dsp = (struct dsp_config *)dsp_configure(NULL, DSP_MYDSP, | 262 | td->dsp = (struct dsp_config *)dsp_configure(NULL, DSP_MYDSP, |
260 | CODEC_IDX_VOICE); | 263 | CODEC_IDX_VOICE); |
261 | 264 | ||
@@ -265,204 +268,178 @@ static void voice_data_init(struct voice_thread_data *td) | |||
265 | dsp_configure(td->dsp, DSP_SET_STEREO_MODE, STEREO_MONO); | 268 | dsp_configure(td->dsp, DSP_SET_STEREO_MODE, STEREO_MONO); |
266 | 269 | ||
267 | mixer_channel_set_amplitude(PCM_MIXER_CHAN_VOICE, MIX_AMP_UNITY); | 270 | mixer_channel_set_amplitude(PCM_MIXER_CHAN_VOICE, MIX_AMP_UNITY); |
268 | td->quiet_counter = 0; | ||
269 | } | 271 | } |
270 | 272 | ||
271 | /* Voice thread message processing */ | 273 | /* Voice thread message processing */ |
272 | static void voice_message(struct voice_thread_data *td) | 274 | static enum voice_state voice_message(struct voice_thread_data *td) |
273 | { | 275 | { |
274 | while (1) | 276 | if (quiet_counter > 0) |
277 | queue_wait_w_tmo(&voice_queue, &td->ev, HZ/10); | ||
278 | else | ||
279 | queue_wait(&voice_queue, &td->ev); | ||
280 | |||
281 | switch (td->ev.id) | ||
275 | { | 282 | { |
276 | switch (td->ev.id) | 283 | case Q_VOICE_PLAY: |
284 | LOGFQUEUE("voice < Q_VOICE_PLAY"); | ||
285 | if (quiet_counter == 0) | ||
277 | { | 286 | { |
278 | case Q_VOICE_PLAY: | 287 | /* Boost CPU now */ |
279 | LOGFQUEUE("voice < Q_VOICE_PLAY"); | 288 | trigger_cpu_boost(); |
280 | voice_done = false; | 289 | } |
281 | 290 | else | |
282 | /* Copy the clip info */ | 291 | { |
283 | td->vi = *(struct voice_info *)td->ev.data; | 292 | /* Stop any clip still playing */ |
284 | 293 | voice_stop_playback(); | |
285 | /* Be sure audio buffer is initialized */ | 294 | } |
286 | audio_restore_playback(AUDIO_WANT_VOICE); | ||
287 | |||
288 | /* We need nothing more from the sending thread - let it run */ | ||
289 | queue_reply(&voice_queue, 1); | ||
290 | |||
291 | if (td->state == TSTATE_STOPPED) | ||
292 | { | ||
293 | /* Boost CPU now */ | ||
294 | trigger_cpu_boost(); | ||
295 | } | ||
296 | else | ||
297 | { | ||
298 | /* Stop any clip still playing */ | ||
299 | voice_stop_playback(); | ||
300 | } | ||
301 | |||
302 | /* Make audio play more softly and set delay to return to normal | ||
303 | playback level */ | ||
304 | pcmbuf_soft_mode(true); | ||
305 | td->quiet_counter = QUIET_COUNT; | ||
306 | |||
307 | /* Clean-start the decoder */ | ||
308 | td->st = speex_decoder_init(&speex_wb_mode); | ||
309 | 295 | ||
310 | /* Make bit buffer use our own buffer */ | 296 | quiet_counter = QUIET_COUNT; |
311 | speex_bits_set_bit_buffer(&td->bits, td->vi.start, td->vi.size); | ||
312 | speex_decoder_ctl(td->st, SPEEX_GET_LOOKAHEAD, &td->lookahead); | ||
313 | 297 | ||
314 | td->state = TSTATE_DECODE; | 298 | /* Copy the clip info */ |
315 | return; | 299 | td->vi = *(struct voice_info *)td->ev.data; |
316 | 300 | ||
317 | case SYS_TIMEOUT: | 301 | /* Be sure audio buffer is initialized */ |
318 | if (voice_unplayed_frames()) | 302 | audio_restore_playback(AUDIO_WANT_VOICE); |
319 | { | ||
320 | /* Waiting for PCM to finish */ | ||
321 | break; | ||
322 | } | ||
323 | 303 | ||
324 | /* Drop through and stop the first time after clip runs out */ | 304 | /* We need nothing more from the sending thread - let it run */ |
325 | if (td->quiet_counter-- != QUIET_COUNT) | 305 | queue_reply(&voice_queue, 1); |
326 | { | ||
327 | if (td->quiet_counter <= 0) | ||
328 | pcmbuf_soft_mode(false); | ||
329 | 306 | ||
330 | break; | 307 | /* Make audio play more softly and set delay to return to normal |
331 | } | 308 | playback level */ |
309 | pcmbuf_soft_mode(true); | ||
332 | 310 | ||
333 | /* Fall-through */ | 311 | /* Clean-start the decoder */ |
334 | case Q_VOICE_STOP: | 312 | td->st = speex_decoder_init(&speex_wb_mode); |
335 | LOGFQUEUE("voice < Q_VOICE_STOP"); | ||
336 | 313 | ||
337 | td->state = TSTATE_STOPPED; | 314 | /* Make bit buffer use our own buffer */ |
338 | voice_done = true; | 315 | speex_bits_set_bit_buffer(&td->bits, td->vi.start, td->vi.size); |
316 | speex_decoder_ctl(td->st, SPEEX_GET_LOOKAHEAD, &td->lookahead); | ||
339 | 317 | ||
340 | cancel_cpu_boost(); | 318 | return VOICE_STATE_DECODE; |
341 | voice_stop_playback(); | ||
342 | break; | ||
343 | 319 | ||
344 | default: | 320 | case SYS_TIMEOUT: |
345 | /* Default messages get a reply and thread continues with no | 321 | if (voice_unplayed_frames()) |
346 | * state transition */ | 322 | { |
347 | LOGFQUEUE("voice < default"); | 323 | /* Waiting for PCM to finish */ |
324 | break; | ||
325 | } | ||
348 | 326 | ||
349 | if (td->state == TSTATE_STOPPED) | 327 | /* Drop through and stop the first time after clip runs out */ |
350 | break; /* Not in (active) playback state */ | 328 | if (quiet_counter-- != QUIET_COUNT) |
329 | { | ||
330 | if (quiet_counter <= 0) | ||
331 | pcmbuf_soft_mode(false); | ||
351 | 332 | ||
352 | queue_reply(&voice_queue, 0); | 333 | break; |
353 | return; | ||
354 | } | 334 | } |
355 | 335 | ||
356 | if (td->quiet_counter > 0) | 336 | /* Fall-through */ |
357 | queue_wait_w_tmo(&voice_queue, &td->ev, HZ/10); | 337 | case Q_VOICE_STOP: |
358 | else | 338 | LOGFQUEUE("voice < Q_VOICE_STOP"); |
359 | queue_wait(&voice_queue, &td->ev); | 339 | cancel_cpu_boost(); |
340 | voice_stop_playback(); | ||
341 | break; | ||
342 | |||
343 | /* No default: no other message ids are sent */ | ||
360 | } | 344 | } |
345 | |||
346 | return VOICE_STATE_MESSAGE; | ||
361 | } | 347 | } |
362 | 348 | ||
363 | /* Voice thread entrypoint */ | 349 | /* Decode frames or stop if all have completed */ |
364 | static void NORETURN_ATTR voice_thread(void) | 350 | static enum voice_state voice_decode(struct voice_thread_data *td) |
365 | { | 351 | { |
366 | struct voice_thread_data td; | 352 | if (!queue_empty(&voice_queue)) |
367 | char *dest; | 353 | return VOICE_STATE_MESSAGE; |
368 | 354 | ||
369 | voice_data_init(&td); | 355 | /* Decode the data */ |
370 | 356 | if (speex_decode_int(td->st, &td->bits, voice_output_buf) < 0) | |
371 | /* audio thread will only set this once after it finished the final | ||
372 | * audio hardware init so this little construct is safe - even | ||
373 | * cross-core. */ | ||
374 | while (!audio_is_thread_ready()) | ||
375 | sleep(0); | ||
376 | |||
377 | goto message_wait; | ||
378 | |||
379 | while (1) | ||
380 | { | 357 | { |
381 | td.state = TSTATE_DECODE; | 358 | /* End of stream or error - get next clip */ |
359 | td->vi.size = 0; | ||
360 | |||
361 | if (td->vi.get_more != NULL) | ||
362 | td->vi.get_more(&td->vi.start, &td->vi.size); | ||
382 | 363 | ||
383 | if (!queue_empty(&voice_queue)) | 364 | if (td->vi.start != NULL && (ssize_t)td->vi.size > 0) |
384 | { | 365 | { |
385 | message_wait: | 366 | /* Make bit buffer use our own buffer */ |
386 | queue_wait(&voice_queue, &td.ev); | 367 | speex_bits_set_bit_buffer(&td->bits, td->vi.start, td->vi.size); |
387 | 368 | /* Don't skip any samples when we're stringing clips together */ | |
388 | message_process: | 369 | td->lookahead = 0; |
389 | voice_message(&td); | ||
390 | |||
391 | /* Branch to initial start point or branch back to previous | ||
392 | * operation if interrupted by a message */ | ||
393 | switch (td.state) | ||
394 | { | ||
395 | case TSTATE_DECODE: goto voice_decode; | ||
396 | case TSTATE_BUFFER_INSERT: goto buffer_insert; | ||
397 | default: goto message_wait; | ||
398 | } | ||
399 | } | 370 | } |
400 | 371 | else | |
401 | voice_decode: | ||
402 | /* Decode the data */ | ||
403 | if (speex_decode_int(td.st, &td.bits, voice_output_buf) < 0) | ||
404 | { | 372 | { |
405 | /* End of stream or error - get next clip */ | ||
406 | td.vi.size = 0; | ||
407 | |||
408 | if (td.vi.get_more != NULL) | ||
409 | td.vi.get_more(&td.vi.start, &td.vi.size); | ||
410 | |||
411 | if (td.vi.start != NULL && (ssize_t)td.vi.size > 0) | ||
412 | { | ||
413 | /* Make bit buffer use our own buffer */ | ||
414 | speex_bits_set_bit_buffer(&td.bits, td.vi.start, td.vi.size); | ||
415 | /* Don't skip any samples when we're stringing clips together */ | ||
416 | td.lookahead = 0; | ||
417 | |||
418 | /* Paranoid check - be sure never to somehow get stuck in a | ||
419 | * loop without listening to the queue */ | ||
420 | yield(); | ||
421 | |||
422 | if (!queue_empty(&voice_queue)) | ||
423 | goto message_wait; | ||
424 | else | ||
425 | goto voice_decode; | ||
426 | } | ||
427 | |||
428 | /* If all clips are done and not playing, force pcm playback. */ | 373 | /* If all clips are done and not playing, force pcm playback. */ |
429 | voice_start_playback(); | 374 | voice_start_playback(); |
430 | 375 | return VOICE_STATE_MESSAGE; | |
431 | td.state = TSTATE_STOPPED; | ||
432 | td.ev.id = SYS_TIMEOUT; | ||
433 | goto message_process; | ||
434 | } | 376 | } |
435 | 377 | } | |
378 | else | ||
379 | { | ||
436 | yield(); | 380 | yield(); |
437 | 381 | ||
438 | /* Output the decoded frame */ | 382 | /* Output the decoded frame */ |
439 | td.count = VOICE_FRAME_SIZE - td.lookahead; | 383 | td->count = VOICE_FRAME_SIZE - td->lookahead; |
440 | td.src[0] = (const char *)&voice_output_buf[td.lookahead]; | 384 | td->src[0] = (const char *)&voice_output_buf[td->lookahead]; |
441 | td.src[1] = NULL; | 385 | td->src[1] = NULL; |
442 | td.lookahead -= MIN(VOICE_FRAME_SIZE, td.lookahead); | 386 | td->lookahead -= MIN(VOICE_FRAME_SIZE, td->lookahead); |
443 | 387 | ||
444 | if (td.count <= 0) | 388 | if (td->count > 0) |
445 | continue; | 389 | return VOICE_STATE_BUFFER_INSERT; |
390 | } | ||
446 | 391 | ||
447 | td.state = TSTATE_BUFFER_INSERT; | 392 | return VOICE_STATE_DECODE; |
393 | } | ||
448 | 394 | ||
449 | buffer_insert: | 395 | /* Process the PCM samples in the DSP and send out for mixing */ |
450 | /* Process the PCM samples in the DSP and send out for mixing */ | 396 | static enum voice_state voice_buffer_insert(struct voice_thread_data *td) |
397 | { | ||
398 | if (!queue_empty(&voice_queue)) | ||
399 | return VOICE_STATE_MESSAGE; | ||
451 | 400 | ||
452 | while (1) | 401 | char *dest = (char *)voice_buf_get(); |
453 | { | ||
454 | if (!queue_empty(&voice_queue)) | ||
455 | goto message_wait; | ||
456 | 402 | ||
457 | if ((dest = (char *)voice_buf_get()) != NULL) | 403 | if (dest != NULL) |
458 | break; | 404 | { |
405 | voice_buf_commit(dsp_process(td->dsp, dest, td->src, td->count) | ||
406 | * sizeof (int32_t)); | ||
407 | return VOICE_STATE_DECODE; | ||
408 | } | ||
459 | 409 | ||
460 | sleep(0); | 410 | sleep(0); |
461 | } | 411 | return VOICE_STATE_BUFFER_INSERT; |
412 | } | ||
462 | 413 | ||
463 | voice_buf_commit(dsp_process(td.dsp, dest, td.src, td.count) | 414 | /* Voice thread entrypoint */ |
464 | * sizeof (int32_t)); | 415 | static void NORETURN_ATTR voice_thread(void) |
465 | } /* end while */ | 416 | { |
417 | struct voice_thread_data td; | ||
418 | enum voice_state state = VOICE_STATE_MESSAGE; | ||
419 | |||
420 | voice_data_init(&td); | ||
421 | |||
422 | /* audio thread will only set this once after it finished the final | ||
423 | * audio hardware init so this little construct is safe - even | ||
424 | * cross-core. */ | ||
425 | while (!audio_is_thread_ready()) | ||
426 | sleep(0); | ||
427 | |||
428 | while (1) | ||
429 | { | ||
430 | switch (state) | ||
431 | { | ||
432 | case VOICE_STATE_MESSAGE: | ||
433 | state = voice_message(&td); | ||
434 | break; | ||
435 | case VOICE_STATE_DECODE: | ||
436 | state = voice_decode(&td); | ||
437 | break; | ||
438 | case VOICE_STATE_BUFFER_INSERT: | ||
439 | state = voice_buffer_insert(&td); | ||
440 | break; | ||
441 | } | ||
442 | } | ||
466 | } | 443 | } |
467 | 444 | ||
468 | /* Initialize all synchronization objects create the thread */ | 445 | /* Initialize all synchronization objects create the thread */ |