summaryrefslogtreecommitdiff
path: root/apps/codec_thread.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/codec_thread.c')
-rw-r--r--apps/codec_thread.c679
1 files changed, 386 insertions, 293 deletions
diff --git a/apps/codec_thread.c b/apps/codec_thread.c
index 65a7ebc7d5..7cf45c3490 100644
--- a/apps/codec_thread.c
+++ b/apps/codec_thread.c
@@ -9,6 +9,7 @@
9 * 9 *
10 * Copyright (C) 2005-2007 Miika Pekkarinen 10 * Copyright (C) 2005-2007 Miika Pekkarinen
11 * Copyright (C) 2007-2008 Nicolas Pennequin 11 * Copyright (C) 2007-2008 Nicolas Pennequin
12 * Copyright (C) 2011 Michael Sevakis
12 * 13 *
13 * This program is free software; you can redistribute it and/or 14 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License 15 * modify it under the terms of the GNU General Public License
@@ -21,16 +22,14 @@
21 ****************************************************************************/ 22 ****************************************************************************/
22#include "config.h" 23#include "config.h"
23#include "system.h" 24#include "system.h"
24#include "playback.h"
25#include "codec_thread.h"
26#include "kernel.h" 25#include "kernel.h"
27#include "codecs.h" 26#include "codecs.h"
28#include "buffering.h" 27#include "codec_thread.h"
29#include "pcmbuf.h" 28#include "pcmbuf.h"
29#include "playback.h"
30#include "buffering.h"
30#include "dsp.h" 31#include "dsp.h"
31#include "abrepeat.h"
32#include "metadata.h" 32#include "metadata.h"
33#include "splash.h"
34 33
35/* Define LOGF_ENABLE to enable logf output in this file */ 34/* Define LOGF_ENABLE to enable logf output in this file */
36/*#define LOGF_ENABLE*/ 35/*#define LOGF_ENABLE*/
@@ -57,38 +56,45 @@
57#define LOGFQUEUE_SYS_TIMEOUT(...) 56#define LOGFQUEUE_SYS_TIMEOUT(...)
58#endif 57#endif
59 58
60
61/* Variables are commented with the threads that use them: 59/* Variables are commented with the threads that use them:
62 * A=audio, C=codec, V=voice. A suffix of - indicates that 60 * A=audio, C=codec
63 * the variable is read but not updated on that thread. 61 * - = reads only
64 62 *
65 * Unless otherwise noted, the extern variables are located 63 * Unless otherwise noted, the extern variables are located
66 * in playback.c. 64 * in playback.c.
67 */ 65 */
68 66
69/* Main state control */ 67/* Q_LOAD_CODEC parameter data */
70 68struct codec_load_info
71/* Type of codec loaded? (C/A) */ 69{
72static int current_codectype SHAREDBSS_ATTR = AFMT_UNKNOWN; 70 int hid; /* audio handle id (specify < 0 to use afmt) */
71 int afmt; /* codec specification (AFMT_*) */
72};
73 73
74extern struct mp3entry *thistrack_id3, /* the currently playing track */
75 *othertrack_id3; /* prev track during track-change-transition, or end of playlist,
76 * next track otherwise */
77 74
78/* Track change controls */ 75/** --- Main state control --- **/
79extern struct event_queue audio_queue SHAREDBSS_ATTR;
80 76
77static int codec_type = AFMT_UNKNOWN; /* Codec type (C,A-) */
81 78
79/* Private interfaces to main playback control */
80extern void audio_codec_update_elapsed(unsigned long value);
81extern void audio_codec_update_offset(size_t value);
82extern void audio_queue_post(long id, intptr_t data);
82extern struct codec_api ci; /* from codecs.c */ 83extern struct codec_api ci; /* from codecs.c */
83 84
84/* Codec thread */ 85/* Codec thread */
85static unsigned int codec_thread_id; /* For modifying thread priority later */ 86static unsigned int codec_thread_id; /* For modifying thread priority later */
86static struct event_queue codec_queue SHAREDBSS_ATTR; 87static struct event_queue codec_queue SHAREDBSS_ATTR;
87static struct queue_sender_list codec_queue_sender_list SHAREDBSS_ATTR; 88static struct queue_sender_list codec_queue_sender_list SHAREDBSS_ATTR;
88static long codec_stack[(DEFAULT_STACK_SIZE + 0x2000)/sizeof(long)] 89static long codec_stack[(DEFAULT_STACK_SIZE + 0x2000)/sizeof(long)] IBSS_ATTR;
89 IBSS_ATTR;
90static const char codec_thread_name[] = "codec"; 90static const char codec_thread_name[] = "codec";
91 91
92static void unload_codec(void);
93
94/* Messages are only ever sent one at a time to the codec from the audio
95 thread. This is important for correct operation unless playback is
96 stopped. */
97
92/* static routines */ 98/* static routines */
93static void codec_queue_ack(intptr_t ackme) 99static void codec_queue_ack(intptr_t ackme)
94{ 100{
@@ -100,52 +106,63 @@ static intptr_t codec_queue_send(long id, intptr_t data)
100 return queue_send(&codec_queue, id, data); 106 return queue_send(&codec_queue, id, data);
101} 107}
102 108
103/**************************************/ 109/* Poll the state of the codec queue. Returns < 0 if the message is urgent
110 and any state should exit, > 0 if it's a run message (and it was
111 scrubbed), 0 if message was ignored. */
112static int codec_check_queue__have_msg(void)
113{
114 struct queue_event ev;
104 115
105/** misc external functions */ 116 queue_peek(&codec_queue, &ev);
106 117
107/* Used to check whether a new codec must be loaded. See array audio_formats[] 118 /* Seek, pause or stop? Just peek and return if so. Codec
108 * in metadata.c */ 119 must handle the command after returing. Inserts will not
109int get_codec_base_type(int type) 120 be allowed until it complies. */
110{ 121 switch (ev.id)
111 int base_type = type; 122 {
112 switch (type) { 123 case Q_CODEC_SEEK:
113 case AFMT_MPA_L1: 124 LOGFQUEUE("codec - Q_CODEC_SEEK", ev.id);
114 case AFMT_MPA_L2: 125 return -1;
115 case AFMT_MPA_L3: 126 case Q_CODEC_PAUSE:
116 base_type = AFMT_MPA_L3; 127 LOGFQUEUE("codec - Q_CODEC_PAUSE", ev.id);
117 break; 128 return -1;
118 case AFMT_MPC_SV7: 129 case Q_CODEC_STOP:
119 case AFMT_MPC_SV8: 130 LOGFQUEUE("codec - Q_CODEC_STOP", ev.id);
120 base_type = AFMT_MPC_SV7; 131 return -1;
121 break;
122 case AFMT_MP4_AAC:
123 case AFMT_MP4_AAC_HE:
124 base_type = AFMT_MP4_AAC;
125 break;
126 case AFMT_SAP:
127 case AFMT_CMC:
128 case AFMT_CM3:
129 case AFMT_CMR:
130 case AFMT_CMS:
131 case AFMT_DMC:
132 case AFMT_DLT:
133 case AFMT_MPT:
134 case AFMT_MPD:
135 case AFMT_RMT:
136 case AFMT_TMC:
137 case AFMT_TM8:
138 case AFMT_TM2:
139 base_type = AFMT_SAP;
140 break;
141 default:
142 break;
143 } 132 }
144 133
145 return base_type; 134 /* This is in error in this context unless it's "go, go, go!" */
135 queue_wait(&codec_queue, &ev);
136
137 if (ev.id == Q_CODEC_RUN)
138 {
139 logf("codec < Q_CODEC_RUN: already running!");
140 codec_queue_ack(Q_CODEC_RUN);
141 return 1;
142 }
143
144 /* Ignore it */
145 logf("codec < bad req %ld (%s)", ev.id, __func__);
146 codec_queue_ack(Q_NULL);
147 return 0;
148}
149
150/* Does the audio format type equal CODEC_TYPE_ENCODER? */
151static inline bool type_is_encoder(int afmt)
152{
153#ifdef AUDIO_HAVE_RECORDING
154 return (afmt & CODEC_TYPE_MASK) == CODEC_TYPE_ENCODER;
155#else
156 return false;
157 (void)afmt;
158#endif
146} 159}
147 160
148const char *get_codec_filename(int cod_spec) 161/**************************************/
162
163
164/** --- Miscellaneous external functions --- **/
165const char * get_codec_filename(int cod_spec)
149{ 166{
150 const char *fname; 167 const char *fname;
151 168
@@ -173,7 +190,7 @@ const char *get_codec_filename(int cod_spec)
173#endif /* HAVE_RECORDING */ 190#endif /* HAVE_RECORDING */
174 191
175 return fname; 192 return fname;
176} /* get_codec_filename */ 193}
177 194
178/* Borrow the codec thread and return the ID */ 195/* Borrow the codec thread and return the ID */
179void codec_thread_do_callback(void (*fn)(void), unsigned int *id) 196void codec_thread_do_callback(void (*fn)(void), unsigned int *id)
@@ -189,9 +206,9 @@ void codec_thread_do_callback(void (*fn)(void), unsigned int *id)
189} 206}
190 207
191 208
192/** codec API callbacks */ 209/** --- codec API callbacks --- **/
193 210
194static void* codec_get_buffer(size_t *size) 211static void * codec_get_buffer(size_t *size)
195{ 212{
196 ssize_t s = CODEC_SIZE - codec_size; 213 ssize_t s = CODEC_SIZE - codec_size;
197 void *buf = &codecbuf[codec_size]; 214 void *buf = &codecbuf[codec_size];
@@ -215,15 +232,19 @@ static void codec_pcmbuf_insert_callback(
215 int inp_count; 232 int inp_count;
216 char *dest; 233 char *dest;
217 234
218 /* Prevent audio from a previous track from playing */ 235 while (1)
219 if (ci.new_track || ci.stop_codec)
220 return;
221
222 while ((dest = pcmbuf_request_buffer(&out_count)) == NULL)
223 { 236 {
237 if ((dest = pcmbuf_request_buffer(&out_count)) != NULL)
238 break;
239
224 cancel_cpu_boost(); 240 cancel_cpu_boost();
225 sleep(1); 241
226 if (ci.seek_time || ci.new_track || ci.stop_codec) 242 /* It will be awhile before space is available but we want
243 "instant" response to any message */
244 queue_wait_w_tmo(&codec_queue, NULL, HZ/20);
245
246 if (!queue_empty(&codec_queue) &&
247 codec_check_queue__have_msg() < 0)
227 return; 248 return;
228 } 249 }
229 250
@@ -247,62 +268,28 @@ static void codec_pcmbuf_insert_callback(
247 268
248 count -= inp_count; 269 count -= inp_count;
249 } 270 }
250} /* codec_pcmbuf_insert_callback */ 271}
251 272
252static void codec_set_elapsed_callback(unsigned long value) 273/* helper function, not a callback */
274static bool codec_advance_buffer_counters(size_t amount)
253{ 275{
254 if (ci.seek_time) 276 if (bufadvance(ci.audio_hid, amount) < 0)
255 return;
256
257#ifdef AB_REPEAT_ENABLE
258 ab_position_report(value);
259#endif
260
261 unsigned long latency = pcmbuf_get_latency();
262 if (value < latency)
263 thistrack_id3->elapsed = 0;
264 else
265 { 277 {
266 unsigned long elapsed = value - latency; 278 ci.curpos = ci.filesize;
267 if (elapsed > thistrack_id3->elapsed || 279 return false;
268 elapsed < thistrack_id3->elapsed - 2)
269 {
270 thistrack_id3->elapsed = elapsed;
271 }
272 } 280 }
273}
274
275static void codec_set_offset_callback(size_t value)
276{
277 if (ci.seek_time)
278 return;
279 281
280 unsigned long latency = pcmbuf_get_latency() * thistrack_id3->bitrate / 8;
281 if (value < latency)
282 thistrack_id3->offset = 0;
283 else
284 thistrack_id3->offset = value - latency;
285}
286
287/* helper function, not a callback */
288static void codec_advance_buffer_counters(size_t amount)
289{
290 bufadvance(get_audio_hid(), amount);
291 ci.curpos += amount; 282 ci.curpos += amount;
283 return true;
292} 284}
293 285
294/* copy up-to size bytes into ptr and return the actual size copied */ 286/* copy up-to size bytes into ptr and return the actual size copied */
295static size_t codec_filebuf_callback(void *ptr, size_t size) 287static size_t codec_filebuf_callback(void *ptr, size_t size)
296{ 288{
297 ssize_t copy_n; 289 ssize_t copy_n = bufread(ci.audio_hid, size, ptr);
298
299 if (ci.stop_codec)
300 return 0;
301
302 copy_n = bufread(get_audio_hid(), size, ptr);
303 290
304 /* Nothing requested OR nothing left */ 291 /* Nothing requested OR nothing left */
305 if (copy_n == 0) 292 if (copy_n <= 0)
306 return 0; 293 return 0;
307 294
308 /* Update read and other position pointers */ 295 /* Update read and other position pointers */
@@ -310,15 +297,15 @@ static size_t codec_filebuf_callback(void *ptr, size_t size)
310 297
311 /* Return the actual amount of data copied to the buffer */ 298 /* Return the actual amount of data copied to the buffer */
312 return copy_n; 299 return copy_n;
313} /* codec_filebuf_callback */ 300}
314 301
315static void* codec_request_buffer_callback(size_t *realsize, size_t reqsize) 302static void * codec_request_buffer_callback(size_t *realsize, size_t reqsize)
316{ 303{
317 size_t copy_n = reqsize; 304 size_t copy_n = reqsize;
318 ssize_t ret; 305 ssize_t ret;
319 void *ptr; 306 void *ptr;
320 307
321 ret = bufgetdata(get_audio_hid(), reqsize, &ptr); 308 ret = bufgetdata(ci.audio_hid, reqsize, &ptr);
322 if (ret >= 0) 309 if (ret >= 0)
323 copy_n = MIN((size_t)ret, reqsize); 310 copy_n = MIN((size_t)ret, reqsize);
324 else 311 else
@@ -329,101 +316,103 @@ static void* codec_request_buffer_callback(size_t *realsize, size_t reqsize)
329 316
330 *realsize = copy_n; 317 *realsize = copy_n;
331 return ptr; 318 return ptr;
332} /* codec_request_buffer_callback */ 319}
333 320
334static void codec_advance_buffer_callback(size_t amount) 321static void codec_advance_buffer_callback(size_t amount)
335{ 322{
336 codec_advance_buffer_counters(amount); 323 if (!codec_advance_buffer_counters(amount))
337 codec_set_offset_callback(ci.curpos); 324 return;
325
326 audio_codec_update_offset(ci.curpos);
338} 327}
339 328
340static bool codec_seek_buffer_callback(size_t newpos) 329static bool codec_seek_buffer_callback(size_t newpos)
341{ 330{
342 logf("codec_seek_buffer_callback"); 331 logf("codec_seek_buffer_callback");
343 332
344 int ret = bufseek(get_audio_hid(), newpos); 333 int ret = bufseek(ci.audio_hid, newpos);
345 if (ret == 0) { 334 if (ret == 0)
335 {
346 ci.curpos = newpos; 336 ci.curpos = newpos;
347 return true; 337 return true;
348 } 338 }
349 else { 339
350 return false; 340 return false;
351 }
352} 341}
353 342
354static void codec_seek_complete_callback(void) 343static void codec_seek_complete_callback(void)
355{ 344{
356 struct queue_event ev;
357
358 logf("seek_complete"); 345 logf("seek_complete");
359 346
360 /* Clear DSP */ 347 /* Clear DSP */
361 dsp_configure(ci.dsp, DSP_FLUSH, 0); 348 dsp_configure(ci.dsp, DSP_FLUSH, 0);
362 349
363 /* Post notification to audio thread */ 350 /* Post notification to audio thread */
364 LOGFQUEUE("audio > Q_AUDIO_SEEK_COMPLETE"); 351 LOGFQUEUE("audio > Q_AUDIO_CODEC_SEEK_COMPLETE");
365 queue_post(&audio_queue, Q_AUDIO_SEEK_COMPLETE, 0); 352 audio_queue_post(Q_AUDIO_CODEC_SEEK_COMPLETE, 0);
366
367 /* Wait for ACK */
368 queue_wait(&codec_queue, &ev);
369 353
370 /* ACK back in context */ 354 /* Wait for urgent or go message */
371 codec_queue_ack(Q_AUDIO_SEEK_COMPLETE); 355 do
356 {
357 queue_wait(&codec_queue, NULL);
358 }
359 while (codec_check_queue__have_msg() == 0);
372} 360}
373 361
374static bool codec_request_next_track_callback(void) 362static void codec_configure_callback(int setting, intptr_t value)
375{ 363{
376 struct queue_event ev; 364 if (!dsp_configure(ci.dsp, setting, value))
377 365 {
378 logf("Request new track"); 366 logf("Illegal key: %d", setting);
367 }
368}
379 369
380 audio_set_prev_elapsed(thistrack_id3->elapsed); 370static enum codec_command_action
371 codec_get_command_callback(intptr_t *param)
372{
373 yield();
381 374
382#ifdef AB_REPEAT_ENABLE 375 if (LIKELY(queue_empty(&codec_queue)))
383 ab_end_of_track_report(); 376 return CODEC_ACTION_NULL; /* As you were */
384#endif
385 377
386 if (ci.stop_codec) 378 /* Process the message - return requested action and data (if any should
379 be expected) */
380 while (1)
387 { 381 {
388 /* Handle ACK in outer loop */ 382 enum codec_command_action action = CODEC_ACTION_NULL;
389 LOGFQUEUE("codec: already stopping"); 383 struct queue_event ev;
390 return false; 384 queue_wait(&codec_queue, &ev);
391 }
392 385
393 trigger_cpu_boost(); 386 switch (ev.id)
387 {
388 case Q_CODEC_RUN: /* Already running */
389 LOGFQUEUE("codec < Q_CODEC_RUN");
390 break;
394 391
395 /* Post request to audio thread */ 392 case Q_CODEC_PAUSE: /* Stay here and wait */
396 LOGFQUEUE("codec > audio Q_AUDIO_CHECK_NEW_TRACK"); 393 LOGFQUEUE("codec < Q_CODEC_PAUSE");
397 queue_post(&audio_queue, Q_AUDIO_CHECK_NEW_TRACK, 0); 394 codec_queue_ack(Q_CODEC_PAUSE);
395 continue;
398 396
399 /* Wait for ACK */ 397 case Q_CODEC_SEEK: /* Audio wants codec to seek */
400 queue_wait(&codec_queue, &ev); 398 LOGFQUEUE("codec < Q_CODEC_SEEK %ld", ev.data);
399 *param = ev.data;
400 action = CODEC_ACTION_SEEK_TIME;
401 break;
401 402
402 if (ev.data == Q_CODEC_REQUEST_COMPLETE) 403 case Q_CODEC_STOP: /* Must only return 0 in main loop */
403 { 404 LOGFQUEUE("codec < Q_CODEC_STOP");
404 /* Seek to the beginning of the new track because if the struct 405 action = CODEC_ACTION_HALT;
405 mp3entry was buffered, "elapsed" might not be zero (if the track has 406 break;
406 been played already but not unbuffered) */
407 codec_seek_buffer_callback(thistrack_id3->first_frame_offset);
408 }
409 407
410 /* ACK back in context */ 408 default: /* This is in error in this context. */
411 codec_queue_ack(Q_AUDIO_CHECK_NEW_TRACK); 409 ev.id = Q_NULL;
410 logf("codec bad req %ld (%s)", ev.id, __func__);
411 }
412 412
413 if (ev.data != Q_CODEC_REQUEST_COMPLETE || ci.stop_codec) 413 codec_queue_ack(ev.id);
414 { 414 return action;
415 LOGFQUEUE("codec <= request failed (%d)", ev.data);
416 return false;
417 } 415 }
418
419 LOGFQUEUE("codec <= Q_CODEC_REQEST_COMPLETE");
420 return true;
421}
422
423static void codec_configure_callback(int setting, intptr_t value)
424{
425 if (!dsp_configure(ci.dsp, setting, value))
426 { logf("Illegal key:%d", setting); }
427} 416}
428 417
429/* Initialize codec API */ 418/* Initialize codec API */
@@ -433,119 +422,215 @@ void codec_init_codec_api(void)
433 CODEC_IDX_AUDIO); 422 CODEC_IDX_AUDIO);
434 ci.codec_get_buffer = codec_get_buffer; 423 ci.codec_get_buffer = codec_get_buffer;
435 ci.pcmbuf_insert = codec_pcmbuf_insert_callback; 424 ci.pcmbuf_insert = codec_pcmbuf_insert_callback;
436 ci.set_elapsed = codec_set_elapsed_callback; 425 ci.set_elapsed = audio_codec_update_elapsed;
437 ci.read_filebuf = codec_filebuf_callback; 426 ci.read_filebuf = codec_filebuf_callback;
438 ci.request_buffer = codec_request_buffer_callback; 427 ci.request_buffer = codec_request_buffer_callback;
439 ci.advance_buffer = codec_advance_buffer_callback; 428 ci.advance_buffer = codec_advance_buffer_callback;
440 ci.seek_buffer = codec_seek_buffer_callback; 429 ci.seek_buffer = codec_seek_buffer_callback;
441 ci.seek_complete = codec_seek_complete_callback; 430 ci.seek_complete = codec_seek_complete_callback;
442 ci.request_next_track = codec_request_next_track_callback; 431 ci.set_offset = audio_codec_update_offset;
443 ci.set_offset = codec_set_offset_callback;
444 ci.configure = codec_configure_callback; 432 ci.configure = codec_configure_callback;
433 ci.get_command = codec_get_command_callback;
445} 434}
446 435
447 436
448/* track change */ 437/** --- CODEC THREAD --- **/
449 438
450/** CODEC THREAD */ 439/* Handle Q_CODEC_LOAD */
451static void codec_thread(void) 440static void load_codec(const struct codec_load_info *ev_data)
452{ 441{
453 struct queue_event ev; 442 int status = CODEC_ERROR;
443 /* Save a local copy so we can let the audio thread go ASAP */
444 struct codec_load_info data = *ev_data;
445 bool const encoder = type_is_encoder(data.afmt);
454 446
447 if (codec_type != AFMT_UNKNOWN)
448 {
449 /* Must have unloaded it first */
450 logf("a codec is already loaded");
451 if (data.hid >= 0)
452 bufclose(data.hid);
453 return;
454 }
455 455
456 while (1) 456 trigger_cpu_boost();
457
458 if (!encoder)
457 { 459 {
458 int status = CODEC_OK; 460 /* Do this now because codec may set some things up at load time */
459 void *handle = NULL; 461 dsp_configure(ci.dsp, DSP_RESET, 0);
460 int hid; 462 }
461 const char *codec_fn; 463
462 464 if (data.hid >= 0)
463#ifdef HAVE_CROSSFADE 465 {
464 if (!pcmbuf_is_crossfade_active()) 466 /* First try buffer load */
465#endif 467 status = codec_load_buf(data.hid, &ci);
468 bufclose(data.hid);
469 }
470
471 if (status < 0)
472 {
473 /* Either not a valid handle or the buffer method failed */
474 const char *codec_fn = get_codec_filename(data.afmt);
475 if (codec_fn)
466 { 476 {
467 cancel_cpu_boost(); 477#ifdef HAVE_IO_PRIORITY
478 buf_back_off_storage(true);
479#endif
480 status = codec_load_file(codec_fn, &ci);
481#ifdef HAVE_IO_PRIORITY
482 buf_back_off_storage(false);
483#endif
468 } 484 }
485 }
486
487 if (status >= 0)
488 {
489 codec_type = data.afmt;
490 codec_queue_ack(Q_CODEC_LOAD);
491 return;
492 }
493
494 /* Failed - get rid of it */
495 unload_codec();
496}
497
498/* Handle Q_CODEC_RUN */
499static void run_codec(void)
500{
501 bool const encoder = type_is_encoder(codec_type);
502 int status;
503
504 if (codec_type == AFMT_UNKNOWN)
505 {
506 logf("no codec to run");
507 return;
508 }
509
510 codec_queue_ack(Q_CODEC_RUN);
511
512 trigger_cpu_boost();
513
514 if (!encoder)
515 {
516 /* This will be either the initial buffered offset or where it left off
517 if it remained buffered and we're skipping back to it and it is best
518 to have ci.curpos in sync with the handle's read position - it's the
519 codec's responsibility to ensure it has the correct positions -
520 playback is sorta dumb and only has a vague idea about what to
521 buffer based upon what metadata has to say */
522 ci.curpos = bufftell(ci.audio_hid);
523
524 /* Pin the codec's audio data in place */
525 buf_pin_handle(ci.audio_hid, true);
526 }
527
528 status = codec_run_proc();
529
530 if (!encoder)
531 {
532 /* Codec is done with it - let it move */
533 buf_pin_handle(ci.audio_hid, false);
534
535 /* Notify audio that we're done for better or worse - advise of the
536 status */
537 LOGFQUEUE("codec > audio Q_AUDIO_CODEC_COMPLETE: %d", status);
538 audio_queue_post(Q_AUDIO_CODEC_COMPLETE, status);
539 }
540}
541
542/* Handle Q_CODEC_SEEK */
543static void seek_codec(unsigned long time)
544{
545 if (codec_type == AFMT_UNKNOWN)
546 {
547 logf("no codec to seek");
548 codec_queue_ack(Q_CODEC_SEEK);
549 codec_seek_complete_callback();
550 return;
551 }
552
553 /* Post it up one level */
554 queue_post(&codec_queue, Q_CODEC_SEEK, time);
555 codec_queue_ack(Q_CODEC_SEEK);
556
557 /* Have to run it again */
558 run_codec();
559}
560
561/* Handle Q_CODEC_UNLOAD */
562static void unload_codec(void)
563{
564 /* Tell codec to clean up */
565 codec_type = AFMT_UNKNOWN;
566 codec_close();
567}
568
569/* Handle Q_CODEC_DO_CALLBACK */
570static void do_callback(void (* callback)(void))
571{
572 codec_queue_ack(Q_CODEC_DO_CALLBACK);
573
574 if (callback)
575 {
576 cpucache_commit_discard();
577 callback();
578 cpucache_commit();
579 }
580}
581
582/* Codec thread function */
583static void NORETURN_ATTR codec_thread(void)
584{
585 struct queue_event ev;
586
587 while (1)
588 {
589 cancel_cpu_boost();
469 590
470 queue_wait(&codec_queue, &ev); 591 queue_wait(&codec_queue, &ev);
471 592
472 switch (ev.id) 593 switch (ev.id)
473 { 594 {
474 case Q_CODEC_LOAD_DISK: 595 case Q_CODEC_LOAD:
475 LOGFQUEUE("codec < Q_CODEC_LOAD_DISK"); 596 LOGFQUEUE("codec < Q_CODEC_LOAD");
476 codec_fn = get_codec_filename(ev.data); 597 load_codec((const struct codec_load_info *)ev.data);
477 if (!codec_fn) 598 break;
478 break;
479#ifdef AUDIO_HAVE_RECORDING
480 if (ev.data & CODEC_TYPE_ENCODER)
481 {
482 ev.id = Q_ENCODER_LOAD_DISK;
483 handle = codec_load_file(codec_fn, &ci);
484 if (handle)
485 codec_queue_ack(Q_ENCODER_LOAD_DISK);
486 }
487 else
488#endif
489 {
490 codec_queue_ack(Q_CODEC_LOAD_DISK);
491 handle = codec_load_file(codec_fn, &ci);
492 }
493 break;
494 599
495 case Q_CODEC_LOAD: 600 case Q_CODEC_RUN:
496 LOGFQUEUE("codec < Q_CODEC_LOAD"); 601 LOGFQUEUE("codec < Q_CODEC_RUN");
497 codec_queue_ack(Q_CODEC_LOAD); 602 run_codec();
498 hid = (int)ev.data; 603 break;
499 handle = codec_load_buf(hid, &ci);
500 bufclose(hid);
501 break;
502 604
503 case Q_CODEC_DO_CALLBACK: 605 case Q_CODEC_PAUSE:
504 LOGFQUEUE("codec < Q_CODEC_DO_CALLBACK"); 606 LOGFQUEUE("codec < Q_CODEC_PAUSE");
505 codec_queue_ack(Q_CODEC_DO_CALLBACK); 607 break;
506 if ((void*)ev.data != NULL)
507 {
508 cpucache_commit_discard();
509 ((void (*)(void))ev.data)();
510 cpucache_commit();
511 }
512 break;
513 608
514 default: 609 case Q_CODEC_SEEK:
515 LOGFQUEUE("codec < default : %ld", ev.id); 610 LOGFQUEUE("codec < Q_CODEC_SEEK: %lu", (unsigned long)ev.data);
516 } 611 seek_codec(ev.data);
612 break;
517 613
518 if (handle) 614 case Q_CODEC_UNLOAD:
519 { 615 LOGFQUEUE("codec < Q_CODEC_UNLOAD");
520 /* Codec loaded - call the entrypoint */ 616 unload_codec();
521 yield(); 617 break;
522 logf("codec running");
523 status = codec_begin(handle);
524 logf("codec stopped");
525 codec_close(handle);
526 current_codectype = AFMT_UNKNOWN;
527
528 if (ci.stop_codec)
529 status = CODEC_OK;
530 }
531 618
532 switch (ev.id) 619 case Q_CODEC_DO_CALLBACK:
533 { 620 LOGFQUEUE("codec < Q_CODEC_DO_CALLBACK");
534#ifdef AUDIO_HAVE_RECORDING 621 do_callback((void (*)(void))ev.data);
535 case Q_ENCODER_LOAD_DISK: 622 break;
536#endif 623
537 case Q_CODEC_LOAD_DISK: 624 default:
538 case Q_CODEC_LOAD: 625 LOGFQUEUE("codec < default : %ld", ev.id);
539 /* Notify about the status */
540 if (!handle)
541 status = CODEC_ERROR;
542 LOGFQUEUE("codec > audio notify status: %d", status);
543 queue_post(&audio_queue, ev.id, status);
544 break;
545 } 626 }
546 } 627 }
547} 628}
548 629
630
631/** --- Miscellaneous external interfaces -- **/
632
633/* Create the codec thread and init kernel objects */
549void make_codec_thread(void) 634void make_codec_thread(void)
550{ 635{
551 queue_init(&codec_queue, false); 636 queue_init(&codec_queue, false);
@@ -558,78 +643,86 @@ void make_codec_thread(void)
558 codec_thread_id); 643 codec_thread_id);
559} 644}
560 645
646/* Unfreeze the codec thread */
561void codec_thread_resume(void) 647void codec_thread_resume(void)
562{ 648{
563 thread_thaw(codec_thread_id); 649 thread_thaw(codec_thread_id);
564} 650}
565 651
652/* Is the current thread the codec thread? */
566bool is_codec_thread(void) 653bool is_codec_thread(void)
567{ 654{
568 return thread_self() == codec_thread_id; 655 return thread_self() == codec_thread_id;
569} 656}
570 657
571#ifdef HAVE_PRIORITY_SCHEDULING 658#ifdef HAVE_PRIORITY_SCHEDULING
659/* Obtain codec thread's current priority */
572int codec_thread_get_priority(void) 660int codec_thread_get_priority(void)
573{ 661{
574 return thread_get_priority(codec_thread_id); 662 return thread_get_priority(codec_thread_id);
575} 663}
576 664
665/* Set the codec thread's priority and return the old value */
577int codec_thread_set_priority(int priority) 666int codec_thread_set_priority(int priority)
578{ 667{
579 return thread_set_priority(codec_thread_id, priority); 668 return thread_set_priority(codec_thread_id, priority);
580} 669}
581#endif /* HAVE_PRIORITY_SCHEDULING */ 670#endif /* HAVE_PRIORITY_SCHEDULING */
582 671
583/* functions for audio thread use */
584intptr_t codec_ack_msg(intptr_t data, bool stop_codec)
585{
586 intptr_t resp;
587 LOGFQUEUE("codec >| Q_CODEC_ACK: %d", data);
588 if (stop_codec)
589 ci.stop_codec = true;
590 resp = codec_queue_send(Q_CODEC_ACK, data);
591 if (stop_codec)
592 codec_stop();
593 LOGFQUEUE(" ack: %ld", resp);
594 return resp;
595}
596 672
673/** --- Functions for audio thread use --- **/
674
675/* Load a decoder or encoder and set the format type */
597bool codec_load(int hid, int cod_spec) 676bool codec_load(int hid, int cod_spec)
598{ 677{
599 bool retval = false; 678 struct codec_load_info parm = { hid, cod_spec };
600 679
601 ci.stop_codec = false; 680 LOGFQUEUE("audio >| codec Q_CODEC_LOAD: %d, %d", hid, cod_spec);
602 current_codectype = cod_spec; 681 return codec_queue_send(Q_CODEC_LOAD, (intptr_t)&parm) != 0;
682}
603 683
604 if (hid >= 0) 684/* Begin decoding the current file */
605 { 685void codec_go(void)
606 LOGFQUEUE("audio >| codec Q_CODEC_LOAD: %d", hid); 686{
607 retval = codec_queue_send(Q_CODEC_LOAD, hid) != Q_NULL; 687 LOGFQUEUE("audio >| codec Q_CODEC_RUN");
608 } 688 codec_queue_send(Q_CODEC_RUN, 0);
609 else 689}
610 {
611 LOGFQUEUE("audio >| codec Q_CODEC_LOAD_DISK: %d", cod_spec);
612 retval = codec_queue_send(Q_CODEC_LOAD_DISK, cod_spec) != Q_NULL;
613 }
614 690
615 if (!retval) 691/* Instruct the codec to seek to the specified time (should be properly
616 { 692 paused or stopped first to avoid possible buffering deadlock) */
617 ci.stop_codec = true; 693void codec_seek(long time)
618 current_codectype = AFMT_UNKNOWN; 694{
619 } 695 LOGFQUEUE("audio > codec Q_CODEC_SEEK: %ld", time);
696 codec_queue_send(Q_CODEC_SEEK, time);
697}
620 698
621 return retval; 699/* Pause the codec and make it wait for further instructions inside the
700 command callback */
701bool codec_pause(void)
702{
703 LOGFQUEUE("audio >| codec Q_CODEC_PAUSE");
704 return codec_queue_send(Q_CODEC_PAUSE, 0) != Q_NULL;
622} 705}
623 706
707/* Stop codec if running - codec stays resident if loaded */
624void codec_stop(void) 708void codec_stop(void)
625{ 709{
626 ci.stop_codec = true;
627 /* Wait until it's in the main loop */ 710 /* Wait until it's in the main loop */
628 while (codec_ack_msg(0, false) != Q_NULL); 711 LOGFQUEUE("audio >| codec Q_CODEC_STOP");
629 current_codectype = AFMT_UNKNOWN; 712 while (codec_queue_send(Q_CODEC_STOP, 0) != Q_NULL);
713}
714
715/* Call the codec's exit routine and close all references */
716void codec_unload(void)
717{
718 codec_stop();
719 LOGFQUEUE("audio >| codec Q_CODEC_UNLOAD");
720 codec_queue_send(Q_CODEC_UNLOAD, 0);
630} 721}
631 722
723/* Return the afmt type of the loaded codec - sticks until calling
724 codec_unload unless initial load failed */
632int codec_loaded(void) 725int codec_loaded(void)
633{ 726{
634 return current_codectype; 727 return codec_type;
635} 728}