summaryrefslogtreecommitdiff
path: root/apps/voice_thread.c
diff options
context:
space:
mode:
authorMichael Sevakis <jethead71@rockbox.org>2011-06-29 06:37:04 +0000
committerMichael Sevakis <jethead71@rockbox.org>2011-06-29 06:37:04 +0000
commita2b6703a369f6cdbfec1f150c408dadc877631fb (patch)
tree3145a8c1372c44711d38feefeba39c7d4098f139 /apps/voice_thread.c
parent8411614b8a068a4f274c3841aa55aab1df1bc246 (diff)
downloadrockbox-a2b6703a369f6cdbfec1f150c408dadc877631fb.tar.gz
rockbox-a2b6703a369f6cdbfec1f150c408dadc877631fb.zip
Commit FS#12150 - Fully-functional audio mixer - and finally whip old limitations about playback of voice and other sounds when paused. Channels are independent in state and amplitude. Fade on stop/pause is handled by the channel's volume control rather than global volume which means it now works from anywhere. Opens up the possibility of plugin sounds during music playback by merely adding an additional channel enum. If any PCM drivers were not properly modified, see one of the last comments in the task for a description of the simple change that is expected. Some params are tunable in firmware/export/pcm-mixer.h as well.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@30097 a1c6a512-1295-4272-9138-f99709370657
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}