summaryrefslogtreecommitdiff
path: root/apps/voice_thread.c
diff options
context:
space:
mode:
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";
78/* Voice thread synchronization objects */ 78/* Voice thread synchronization objects */
79static struct event_queue voice_queue SHAREDBSS_ATTR; 79static struct event_queue voice_queue SHAREDBSS_ATTR;
80static struct queue_sender_list voice_queue_sender_list SHAREDBSS_ATTR; 80static struct queue_sender_list voice_queue_sender_list SHAREDBSS_ATTR;
81static bool voice_done SHAREDDATA_ATTR = true; 81static int quiet_counter SHAREDDATA_ATTR = 0;
82 82
83/* Buffer for decoded samples */ 83/* Buffer for decoded samples */
84static spx_int16_t voice_output_buf[VOICE_FRAME_SIZE] CACHEALIGN_ATTR; 84static spx_int16_t voice_output_buf[VOICE_FRAME_SIZE] CACHEALIGN_ATTR;
@@ -96,20 +96,20 @@ static size_t voicebuf_sizes[VOICE_FRAMES];
96static uint32_t (* voicebuf)[VOICE_PCM_FRAME_COUNT]; 96static uint32_t (* voicebuf)[VOICE_PCM_FRAME_COUNT];
97static unsigned int cur_buf_in, cur_buf_out; 97static 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 100enum voice_state
101
102enum 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
109enum voice_thread_messages 110enum 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 */
126struct voice_thread_data 126struct 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 */
140static enum voice_state voice_message(struct voice_thread_data *td);
141static enum voice_state voice_decode(struct voice_thread_data *td);
142static enum voice_state voice_buffer_insert(struct voice_thread_data *td);
143
140/* Number of frames in queue */ 144/* Number of frames in queue */
141static inline int voice_unplayed_frames(void) 145static 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 */
230bool mp3_is_playing(void) 234bool 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 */
256static void voice_data_init(struct voice_thread_data *td) 260static 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 */
272static void voice_message(struct voice_thread_data *td) 274static 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 */
364static void NORETURN_ATTR voice_thread(void) 350static 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 */ 396static 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)); 415static 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 */