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.c279
1 files changed, 162 insertions, 117 deletions
diff --git a/apps/voice_thread.c b/apps/voice_thread.c
index 6683fcc067..3318bbecb3 100644
--- a/apps/voice_thread.c
+++ b/apps/voice_thread.c
@@ -27,6 +27,8 @@
27#include "audio.h" 27#include "audio.h"
28#include "playback.h" 28#include "playback.h"
29#include "pcmbuf.h" 29#include "pcmbuf.h"
30#include "pcm.h"
31#include "pcm_mixer.h"
30#include "codecs/libspeex/speex/speex.h" 32#include "codecs/libspeex/speex/speex.h"
31 33
32/* Define any of these as "1" and uncomment the LOGF_ENABLE line to log 34/* Define any of these as "1" and uncomment the LOGF_ENABLE line to log
@@ -53,24 +55,50 @@
53#define IBSS_ATTR_VOICE_STACK IBSS_ATTR 55#define IBSS_ATTR_VOICE_STACK IBSS_ATTR
54#endif 56#endif
55 57
58/* Minimum priority needs to be a bit elevated since voice has fairly low
59 latency */
60#define PRIORITY_VOICE (PRIORITY_PLAYBACK-4)
61
56#define VOICE_FRAME_SIZE 320 /* Samples / frame */ 62#define VOICE_FRAME_SIZE 320 /* Samples / frame */
57#define VOICE_SAMPLE_RATE 16000 /* Sample rate in HZ */ 63#define VOICE_SAMPLE_RATE 16000 /* Sample rate in HZ */
58#define VOICE_SAMPLE_DEPTH 16 /* Sample depth in bits */ 64#define VOICE_SAMPLE_DEPTH 16 /* Sample depth in bits */
59 65
60/* Voice thread variables */ 66/* Voice thread variables */
61static unsigned int voice_thread_id = 0; 67static unsigned int voice_thread_id = 0;
62static long voice_stack[(DEFAULT_STACK_SIZE + 0x3C0)/sizeof(long)] IBSS_ATTR_VOICE_STACK; 68#ifdef CPU_COLDFIRE
69/* ISR uses any available stack - need a bit more room */
70#define VOICE_STACK_EXTRA 0x400
71#else
72#define VOICE_STACK_EXTRA 0x3c0
73#endif
74static long voice_stack[(DEFAULT_STACK_SIZE + VOICE_STACK_EXTRA)/sizeof(long)]
75 IBSS_ATTR_VOICE_STACK;
63static const char voice_thread_name[] = "voice"; 76static const char voice_thread_name[] = "voice";
64 77
65/* Voice thread synchronization objects */ 78/* Voice thread synchronization objects */
66static struct event_queue voice_queue SHAREDBSS_ATTR; 79static struct event_queue voice_queue SHAREDBSS_ATTR;
67static struct mutex voice_mutex SHAREDBSS_ATTR;
68static struct queue_sender_list voice_queue_sender_list SHAREDBSS_ATTR; 80static struct queue_sender_list voice_queue_sender_list SHAREDBSS_ATTR;
69static bool voice_done SHAREDDATA_ATTR = true; 81static bool voice_done SHAREDDATA_ATTR = true;
70 82
71/* Buffer for decoded samples */ 83/* Buffer for decoded samples */
72static spx_int16_t voice_output_buf[VOICE_FRAME_SIZE] CACHEALIGN_ATTR; 84static spx_int16_t voice_output_buf[VOICE_FRAME_SIZE] CACHEALIGN_ATTR;
73 85
86#define VOICE_PCM_FRAME_COUNT ((NATIVE_FREQUENCY*VOICE_FRAME_SIZE + \
87 VOICE_SAMPLE_RATE) / VOICE_SAMPLE_RATE)
88#define VOICE_PCM_FRAME_SIZE (VOICE_PCM_FRAME_COUNT*4)
89
90/* Default number of native-frequency PCM frames to queue - adjust as
91 necessary per-target */
92#define VOICE_FRAMES 3
93
94/* Might have lookahead and be skipping samples, so size is needed */
95static size_t voicebuf_sizes[VOICE_FRAMES];
96static uint32_t (* voicebuf)[VOICE_PCM_FRAME_COUNT];
97static unsigned int cur_buf_in, cur_buf_out;
98
99/* A delay to not bring audio back to normal level too soon */
100#define QUIET_COUNT 3
101
74enum voice_thread_states 102enum voice_thread_states
75{ 103{
76 TSTATE_STOPPED = 0, /* Voice thread is stopped and awaiting commands */ 104 TSTATE_STOPPED = 0, /* Voice thread is stopped and awaiting commands */
@@ -83,7 +111,6 @@ enum voice_thread_messages
83 Q_VOICE_NULL = 0, /* A message for thread sync - no effect on state */ 111 Q_VOICE_NULL = 0, /* A message for thread sync - no effect on state */
84 Q_VOICE_PLAY, /* Play a clip */ 112 Q_VOICE_PLAY, /* Play a clip */
85 Q_VOICE_STOP, /* Stop current clip */ 113 Q_VOICE_STOP, /* Stop current clip */
86 Q_VOICE_STATE, /* Query playing state */
87}; 114};
88 115
89/* Structure to store clip data callback info */ 116/* Structure to store clip data callback info */
@@ -98,7 +125,7 @@ struct voice_info
98 * internal functions */ 125 * internal functions */
99struct voice_thread_data 126struct voice_thread_data
100{ 127{
101 int state; /* Thread state (TSTATE_*) */ 128 volatile int state; /* Thread state (TSTATE_*) */
102 struct queue_event ev; /* Last queue event pulled from queue */ 129 struct queue_event ev; /* Last queue event pulled from queue */
103 void *st; /* Decoder instance */ 130 void *st; /* Decoder instance */
104 SpeexBits bits; /* Bit cursor */ 131 SpeexBits bits; /* Bit cursor */
@@ -107,33 +134,79 @@ struct voice_thread_data
107 const char *src[2]; /* Current output buffer pointers */ 134 const char *src[2]; /* Current output buffer pointers */
108 int lookahead; /* Number of samples to drop at start of clip */ 135 int lookahead; /* Number of samples to drop at start of clip */
109 int count; /* Count of samples remaining to send to PCM */ 136 int count; /* Count of samples remaining to send to PCM */
137 int quiet_counter; /* Countdown until audio goes back to normal */
110}; 138};
111 139
112/* Audio playback is in a playing state? */ 140/* Number of frames in queue */
113static inline bool playback_is_playing(void) 141static inline int voice_unplayed_frames(void)
114{ 142{
115 return (audio_status() & AUDIO_STATUS_PLAY) != 0; 143 return cur_buf_in - cur_buf_out;
144}
145
146/* Mixer channel callback */
147static void voice_pcm_callback(unsigned char **start, size_t *size)
148{
149 if (voice_unplayed_frames() == 0)
150 return; /* Done! */
151
152 unsigned int i = ++cur_buf_out % VOICE_FRAMES;
153
154 *start = (unsigned char *)voicebuf[i];
155 *size = voicebuf_sizes[i];
156}
157
158/* Start playback of voice channel if not already playing */
159static void voice_start_playback(void)
160{
161 if (mixer_channel_status(PCM_MIXER_CHAN_VOICE) != CHANNEL_STOPPED)
162 return;
163
164 unsigned int i = cur_buf_out % VOICE_FRAMES;
165 mixer_channel_play_data(PCM_MIXER_CHAN_VOICE, voice_pcm_callback,
166 (unsigned char *)voicebuf[i], voicebuf_sizes[i]);
167}
168
169/* Stop the voice channel */
170static void voice_stop_playback(void)
171{
172 mixer_channel_stop(PCM_MIXER_CHAN_VOICE);
173 cur_buf_in = cur_buf_out = 0;
174}
175
176/* Grab a free PCM frame */
177static uint32_t * voice_buf_get(void)
178{
179 if (voice_unplayed_frames() >= VOICE_FRAMES)
180 {
181 /* Full */
182 voice_start_playback();
183 return NULL;
184 }
185
186 return voicebuf[cur_buf_in % VOICE_FRAMES];
187}
188
189/* Commit a frame returned by voice_buf_get and set the actual size */
190static void voice_buf_commit(size_t size)
191{
192 voicebuf_sizes[cur_buf_in++ % VOICE_FRAMES] = size;
116} 193}
117 194
118/* Stop any current clip and start playing a new one */ 195/* Stop any current clip and start playing a new one */
119void mp3_play_data(const unsigned char* start, int size, 196void mp3_play_data(const unsigned char* start, int size,
120 pcm_play_callback_type get_more) 197 pcm_play_callback_type get_more)
121{ 198{
122 /* Shared struct to get data to the thread - once it replies, it has
123 * safely cached it in its own private data */
124 static struct voice_info voice_clip SHAREDBSS_ATTR;
125
126 if (get_more != NULL && start != NULL && (ssize_t)size > 0) 199 if (get_more != NULL && start != NULL && (ssize_t)size > 0)
127 { 200 {
128 mutex_lock(&voice_mutex); 201 struct voice_info voice_clip =
202 {
203 .get_more = get_more,
204 .start = (unsigned char *)start,
205 .size = size,
206 };
129 207
130 voice_clip.get_more = get_more;
131 voice_clip.start = (unsigned char *)start;
132 voice_clip.size = size;
133 LOGFQUEUE("mp3 >| voice Q_VOICE_PLAY"); 208 LOGFQUEUE("mp3 >| voice Q_VOICE_PLAY");
134 queue_send(&voice_queue, Q_VOICE_PLAY, (intptr_t)&voice_clip); 209 queue_send(&voice_queue, Q_VOICE_PLAY, (intptr_t)&voice_clip);
135
136 mutex_unlock(&voice_mutex);
137 } 210 }
138} 211}
139 212
@@ -143,11 +216,8 @@ void mp3_play_stop(void)
143 if(!audio_is_thread_ready()) 216 if(!audio_is_thread_ready())
144 return; 217 return;
145 218
146 mutex_lock(&voice_mutex); /* Sync against voice_stop */ 219 LOGFQUEUE("mp3 >| voice Q_VOICE_STOP");
147 LOGFQUEUE("mp3 >| voice Q_VOICE_STOP: 1"); 220 queue_send(&voice_queue, Q_VOICE_STOP, 0);
148 queue_send(&voice_queue, Q_VOICE_STOP, 1);
149
150 mutex_unlock(&voice_mutex);
151} 221}
152 222
153void mp3_play_pause(bool play) 223void mp3_play_pause(bool play)
@@ -156,36 +226,19 @@ void mp3_play_pause(bool play)
156 (void)play; 226 (void)play;
157} 227}
158 228
159/* Tell is voice is still in a playing state */ 229/* Tell if voice is still in a playing state */
160bool mp3_is_playing(void) 230bool mp3_is_playing(void)
161{ 231{
162 /* TODO: Implement a timeout or state query function for event objects */ 232 return !voice_done;
163 LOGFQUEUE("mp3 >| voice Q_VOICE_STATE");
164 int state = queue_send(&voice_queue, Q_VOICE_STATE, 0);
165 return state != TSTATE_STOPPED;
166} 233}
167 234
168/* This function is meant to be used by the buffer request functions to 235/* This function is meant to be used by the buffer request functions to
169 ensure the codec is no longer active */ 236 ensure the codec is no longer active */
170void voice_stop(void) 237void voice_stop(void)
171{ 238{
172 mutex_lock(&voice_mutex);
173
174 /* Stop the output and current clip */
175 mp3_play_stop();
176
177 /* Careful if using sync objects in talk.c - make sure locking order is
178 * observed with one or the other always granted first */
179
180 /* Unqueue all future clips */ 239 /* Unqueue all future clips */
181 talk_force_shutup(); 240 talk_force_shutup();
182 241}
183 /* Wait for any final queue_post to be processed */
184 LOGFQUEUE("mp3 >| voice Q_VOICE_NULL");
185 queue_send(&voice_queue, Q_VOICE_NULL, 0);
186
187 mutex_unlock(&voice_mutex);
188} /* voice_stop */
189 242
190/* Wait for voice to finish speaking. */ 243/* Wait for voice to finish speaking. */
191void voice_wait(void) 244void voice_wait(void)
@@ -194,8 +247,7 @@ void voice_wait(void)
194 * new clip by the time we wait. This should be resolvable if conditions 247 * new clip by the time we wait. This should be resolvable if conditions
195 * ever require knowing the very clip you requested has finished. */ 248 * ever require knowing the very clip you requested has finished. */
196 249
197 /* Wait for PCM buffer to be exhausted. Works only if not playing. */ 250 while (!voice_done)
198 while(!voice_done || (!playback_is_playing() && pcm_is_playing()))
199 sleep(1); 251 sleep(1);
200} 252}
201 253
@@ -211,6 +263,9 @@ static void voice_data_init(struct voice_thread_data *td)
211 dsp_configure(td->dsp, DSP_SET_FREQUENCY, VOICE_SAMPLE_RATE); 263 dsp_configure(td->dsp, DSP_SET_FREQUENCY, VOICE_SAMPLE_RATE);
212 dsp_configure(td->dsp, DSP_SET_SAMPLE_DEPTH, VOICE_SAMPLE_DEPTH); 264 dsp_configure(td->dsp, DSP_SET_SAMPLE_DEPTH, VOICE_SAMPLE_DEPTH);
213 dsp_configure(td->dsp, DSP_SET_STEREO_MODE, STEREO_MONO); 265 dsp_configure(td->dsp, DSP_SET_STEREO_MODE, STEREO_MONO);
266
267 mixer_channel_set_amplitude(PCM_MIXER_CHAN_VOICE, MIX_AMP_UNITY);
268 td->quiet_counter = 0;
214} 269}
215 270
216/* Voice thread message processing */ 271/* Voice thread message processing */
@@ -222,7 +277,6 @@ static void voice_message(struct voice_thread_data *td)
222 { 277 {
223 case Q_VOICE_PLAY: 278 case Q_VOICE_PLAY:
224 LOGFQUEUE("voice < Q_VOICE_PLAY"); 279 LOGFQUEUE("voice < Q_VOICE_PLAY");
225 /* Put up a block for completion signal */
226 voice_done = false; 280 voice_done = false;
227 281
228 /* Copy the clip info */ 282 /* Copy the clip info */
@@ -239,12 +293,17 @@ static void voice_message(struct voice_thread_data *td)
239 /* Boost CPU now */ 293 /* Boost CPU now */
240 trigger_cpu_boost(); 294 trigger_cpu_boost();
241 } 295 }
242 else if (!playback_is_playing()) 296 else
243 { 297 {
244 /* Just voice, stop any clip still playing */ 298 /* Stop any clip still playing */
245 pcmbuf_play_stop(); 299 voice_stop_playback();
246 } 300 }
247 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
248 /* Clean-start the decoder */ 307 /* Clean-start the decoder */
249 td->st = speex_decoder_init(&speex_wb_mode); 308 td->st = speex_decoder_init(&speex_wb_mode);
250 309
@@ -255,30 +314,32 @@ static void voice_message(struct voice_thread_data *td)
255 td->state = TSTATE_DECODE; 314 td->state = TSTATE_DECODE;
256 return; 315 return;
257 316
258 case Q_VOICE_STOP: 317 case SYS_TIMEOUT:
259 LOGFQUEUE("voice < Q_VOICE_STOP: %ld", (long)td->ev.data); 318 if (voice_unplayed_frames())
319 {
320 /* Waiting for PCM to finish */
321 break;
322 }
260 323
261 if (td->ev.data != 0 && !playback_is_playing()) 324 /* Drop through and stop the first time after clip runs out */
325 if (td->quiet_counter-- != QUIET_COUNT)
262 { 326 {
263 /* If not playing, it's just voice so stop pcm playback */ 327 if (td->quiet_counter <= 0)
264 pcmbuf_play_stop(); 328 pcmbuf_soft_mode(false);
329
330 break;
265 } 331 }
266 332
267 /* Cancel boost */ 333 /* Fall-through */
268 cancel_cpu_boost(); 334 case Q_VOICE_STOP:
335 LOGFQUEUE("voice < Q_VOICE_STOP");
269 336
270 td->state = TSTATE_STOPPED; 337 td->state = TSTATE_STOPPED;
271 voice_done = true; 338 voice_done = true;
272 break;
273
274 case Q_VOICE_STATE:
275 LOGFQUEUE("voice < Q_VOICE_STATE");
276 queue_reply(&voice_queue, td->state);
277
278 if (td->state == TSTATE_STOPPED)
279 break; /* Not in a playback state */
280 339
281 return; 340 cancel_cpu_boost();
341 voice_stop_playback();
342 break;
282 343
283 default: 344 default:
284 /* Default messages get a reply and thread continues with no 345 /* Default messages get a reply and thread continues with no
@@ -286,20 +347,24 @@ static void voice_message(struct voice_thread_data *td)
286 LOGFQUEUE("voice < default"); 347 LOGFQUEUE("voice < default");
287 348
288 if (td->state == TSTATE_STOPPED) 349 if (td->state == TSTATE_STOPPED)
289 break; /* Not in playback state */ 350 break; /* Not in (active) playback state */
290 351
291 queue_reply(&voice_queue, 0); 352 queue_reply(&voice_queue, 0);
292 return; 353 return;
293 } 354 }
294 355
295 queue_wait(&voice_queue, &td->ev); 356 if (td->quiet_counter > 0)
357 queue_wait_w_tmo(&voice_queue, &td->ev, HZ/10);
358 else
359 queue_wait(&voice_queue, &td->ev);
296 } 360 }
297} 361}
298 362
299/* Voice thread entrypoint */ 363/* Voice thread entrypoint */
300static void voice_thread(void) 364static void NORETURN_ATTR voice_thread(void)
301{ 365{
302 struct voice_thread_data td; 366 struct voice_thread_data td;
367 char *dest;
303 368
304 voice_data_init(&td); 369 voice_data_init(&td);
305 370
@@ -361,19 +426,10 @@ static void voice_thread(void)
361 } 426 }
362 427
363 /* If all clips are done and not playing, force pcm playback. */ 428 /* If all clips are done and not playing, force pcm playback. */
364 if (!pcm_is_playing()) 429 voice_start_playback();
365 pcmbuf_play_start(); 430
366 431 td.state = TSTATE_STOPPED;
367 /* Synthesize a stop request */ 432 td.ev.id = SYS_TIMEOUT;
368 /* NOTE: We have no way to know when the pcm data placed in the
369 * buffer is actually consumed and playback has reached the end
370 * so until the info is available or inferred somehow, this will
371 * not be accurate and the stopped signal will come too soon.
372 * ie. You may not hear the "Shutting Down" splash even though
373 * it waits for voice to stop. */
374 td.ev.id = Q_VOICE_STOP;
375 td.ev.data = 0; /* Let PCM drain by itself */
376 yield();
377 goto message_process; 433 goto message_process;
378 } 434 }
379 435
@@ -385,62 +441,39 @@ static void voice_thread(void)
385 td.src[1] = NULL; 441 td.src[1] = NULL;
386 td.lookahead -= MIN(VOICE_FRAME_SIZE, td.lookahead); 442 td.lookahead -= MIN(VOICE_FRAME_SIZE, td.lookahead);
387 443
444 if (td.count <= 0)
445 continue;
446
447 td.state = TSTATE_BUFFER_INSERT;
448
388 buffer_insert: 449 buffer_insert:
389 /* 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 */
390 td.state = TSTATE_BUFFER_INSERT;
391 451
392 while (td.count > 0) 452 while (1)
393 { 453 {
394 int out_count = dsp_output_count(td.dsp, td.count); 454 if (!queue_empty(&voice_queue))
395 int inp_count; 455 goto message_wait;
396 char *dest;
397 456
398 while (1) 457 if ((dest = (char *)voice_buf_get()) != NULL)
399 {
400 if (!queue_empty(&voice_queue))
401 goto message_wait;
402
403 if ((dest = pcmbuf_request_voice_buffer(&out_count)) != NULL)
404 break;
405
406 yield();
407 }
408
409 /* Get the real input_size for output_size bytes, guarding
410 * against resampling buffer overflows. */
411 inp_count = dsp_input_count(td.dsp, out_count);
412
413 if (inp_count <= 0)
414 break;
415
416 /* Input size has grown, no error, just don't write more than
417 * length */
418 if (inp_count > td.count)
419 inp_count = td.count;
420
421 out_count = dsp_process(td.dsp, dest, td.src, inp_count);
422
423 if (out_count <= 0)
424 break; 458 break;
425 459
426 pcmbuf_write_voice_complete(out_count); 460 yield();
427 td.count -= inp_count;
428 } 461 }
429 462
430 yield(); 463 voice_buf_commit(dsp_process(td.dsp, dest, td.src, td.count)
464 * sizeof (int32_t));
431 } /* end while */ 465 } /* end while */
432} /* voice_thread */ 466}
433 467
434/* Initialize all synchronization objects create the thread */ 468/* Initialize all synchronization objects create the thread */
435void voice_thread_init(void) 469void voice_thread_init(void)
436{ 470{
437 logf("Starting voice thread"); 471 logf("Starting voice thread");
438 queue_init(&voice_queue, false); 472 queue_init(&voice_queue, false);
439 mutex_init(&voice_mutex);
440 473
441 voice_thread_id = create_thread(voice_thread, voice_stack, 474 voice_thread_id = create_thread(voice_thread, voice_stack,
442 sizeof(voice_stack), CREATE_THREAD_FROZEN, 475 sizeof(voice_stack), CREATE_THREAD_FROZEN,
443 voice_thread_name IF_PRIO(, PRIORITY_PLAYBACK) IF_COP(, CPU)); 476 voice_thread_name IF_PRIO(, PRIORITY_VOICE) IF_COP(, CPU));
444 477
445 queue_enable_queue_send(&voice_queue, &voice_queue_sender_list, 478 queue_enable_queue_send(&voice_queue, &voice_queue_sender_list,
446 voice_thread_id); 479 voice_thread_id);
@@ -457,6 +490,18 @@ void voice_thread_resume(void)
457/* Set the voice thread priority */ 490/* Set the voice thread priority */
458void voice_thread_set_priority(int priority) 491void voice_thread_set_priority(int priority)
459{ 492{
493 if (priority > PRIORITY_VOICE)
494 priority = PRIORITY_VOICE;
495
460 thread_set_priority(voice_thread_id, priority); 496 thread_set_priority(voice_thread_id, priority);
461} 497}
462#endif 498#endif
499
500/* Initialize voice PCM buffer and return size, allocated from the end */
501size_t voicebuf_init(unsigned char *bufend)
502{
503 size_t size = VOICE_FRAMES * VOICE_PCM_FRAME_SIZE;
504 cur_buf_out = cur_buf_in = 0;
505 voicebuf = (uint32_t (*)[VOICE_PCM_FRAME_COUNT])(bufend - size);
506 return size;
507}