Jeffrey Goode | 9a4420b | 2009-10-31 19:17:36 +0000 | [diff] [blame] | 1 | /*************************************************************************** |
| 2 | * __________ __ ___. |
| 3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ |
| 4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / |
| 5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < |
| 6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ |
| 7 | * \/ \/ \/ \/ \/ |
| 8 | * $Id$ |
| 9 | * |
| 10 | * Copyright (C) 2005-2007 Miika Pekkarinen |
| 11 | * Copyright (C) 2007-2008 Nicolas Pennequin |
| 12 | * |
| 13 | * This program is free software; you can redistribute it and/or |
| 14 | * modify it under the terms of the GNU General Public License |
| 15 | * as published by the Free Software Foundation; either version 2 |
| 16 | * of the License, or (at your option) any later version. |
| 17 | * |
| 18 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY |
| 19 | * KIND, either express or implied. |
| 20 | * |
| 21 | ****************************************************************************/ |
| 22 | |
Jeffrey Goode | 9a4420b | 2009-10-31 19:17:36 +0000 | [diff] [blame] | 23 | #include "playback.h" |
| 24 | #include "codec_thread.h" |
| 25 | #include "system.h" |
Jeffrey Goode | 9a4420b | 2009-10-31 19:17:36 +0000 | [diff] [blame] | 26 | #include "kernel.h" |
Jeffrey Goode | 9a4420b | 2009-10-31 19:17:36 +0000 | [diff] [blame] | 27 | #include "codecs.h" |
Jeffrey Goode | 9a4420b | 2009-10-31 19:17:36 +0000 | [diff] [blame] | 28 | #include "buffering.h" |
Jeffrey Goode | 9a4420b | 2009-10-31 19:17:36 +0000 | [diff] [blame] | 29 | #include "pcmbuf.h" |
Jeffrey Goode | 9a4420b | 2009-10-31 19:17:36 +0000 | [diff] [blame] | 30 | #include "dsp.h" |
| 31 | #include "abrepeat.h" |
Jeffrey Goode | 9a4420b | 2009-10-31 19:17:36 +0000 | [diff] [blame] | 32 | #include "metadata.h" |
| 33 | #include "splash.h" |
Jeffrey Goode | 9a4420b | 2009-10-31 19:17:36 +0000 | [diff] [blame] | 34 | |
| 35 | /* Define LOGF_ENABLE to enable logf output in this file */ |
| 36 | /*#define LOGF_ENABLE*/ |
| 37 | #include "logf.h" |
| 38 | |
| 39 | /* macros to enable logf for queues |
| 40 | logging on SYS_TIMEOUT can be disabled */ |
| 41 | #ifdef SIMULATOR |
| 42 | /* Define this for logf output of all queuing except SYS_TIMEOUT */ |
| 43 | #define PLAYBACK_LOGQUEUES |
| 44 | /* Define this to logf SYS_TIMEOUT messages */ |
| 45 | /*#define PLAYBACK_LOGQUEUES_SYS_TIMEOUT*/ |
| 46 | #endif |
| 47 | |
| 48 | #ifdef PLAYBACK_LOGQUEUES |
| 49 | #define LOGFQUEUE logf |
| 50 | #else |
| 51 | #define LOGFQUEUE(...) |
| 52 | #endif |
| 53 | |
| 54 | #ifdef PLAYBACK_LOGQUEUES_SYS_TIMEOUT |
| 55 | #define LOGFQUEUE_SYS_TIMEOUT logf |
| 56 | #else |
| 57 | #define LOGFQUEUE_SYS_TIMEOUT(...) |
| 58 | #endif |
| 59 | |
| 60 | |
Jeffrey Goode | e8eefe9 | 2009-11-01 19:39:23 +0000 | [diff] [blame] | 61 | /* Variables are commented with the threads that use them: |
| 62 | * A=audio, C=codec, V=voice. A suffix of - indicates that |
| 63 | * the variable is read but not updated on that thread. |
| 64 | |
| 65 | * Unless otherwise noted, the extern variables are located |
| 66 | * in playback.c. |
| 67 | */ |
Jeffrey Goode | 9a4420b | 2009-10-31 19:17:36 +0000 | [diff] [blame] | 68 | |
| 69 | /* Main state control */ |
Jeffrey Goode | e8eefe9 | 2009-11-01 19:39:23 +0000 | [diff] [blame] | 70 | volatile bool audio_codec_loaded SHAREDBSS_ATTR = false; /* Codec loaded? (C/A-) */ |
Jeffrey Goode | 9a4420b | 2009-10-31 19:17:36 +0000 | [diff] [blame] | 71 | |
| 72 | extern struct mp3entry *thistrack_id3, /* the currently playing track */ |
| 73 | *othertrack_id3; /* prev track during track-change-transition, or end of playlist, |
| 74 | * next track otherwise */ |
| 75 | |
| 76 | /* Track change controls */ |
| 77 | extern bool automatic_skip; /* Who initiated in-progress skip? (C/A-) */ |
| 78 | |
| 79 | /* Set to true if the codec thread should send an audio stop request |
| 80 | * (typically because the end of the playlist has been reached). |
| 81 | */ |
| 82 | static bool codec_requested_stop = false; |
| 83 | |
| 84 | extern struct event_queue audio_queue; |
| 85 | extern struct event_queue codec_queue; |
Jeffrey Goode | e8eefe9 | 2009-11-01 19:39:23 +0000 | [diff] [blame] | 86 | |
| 87 | extern struct codec_api ci; /* from codecs.c */ |
Jeffrey Goode | 9a4420b | 2009-10-31 19:17:36 +0000 | [diff] [blame] | 88 | |
| 89 | /* Codec thread */ |
Jeffrey Goode | e8eefe9 | 2009-11-01 19:39:23 +0000 | [diff] [blame] | 90 | unsigned int codec_thread_id; /* For modifying thread priority later. |
| 91 | Used by playback.c and pcmbuf.c */ |
Jeffrey Goode | 9a4420b | 2009-10-31 19:17:36 +0000 | [diff] [blame] | 92 | static struct queue_sender_list codec_queue_sender_list; |
| 93 | static long codec_stack[(DEFAULT_STACK_SIZE + 0x2000)/sizeof(long)] |
| 94 | IBSS_ATTR; |
| 95 | static const char codec_thread_name[] = "codec"; |
| 96 | |
Jeffrey Goode | e8eefe9 | 2009-11-01 19:39:23 +0000 | [diff] [blame] | 97 | /* function prototypes */ |
| 98 | static bool codec_load_next_track(void); |
| 99 | |
| 100 | |
Jeffrey Goode | 9a4420b | 2009-10-31 19:17:36 +0000 | [diff] [blame] | 101 | /**************************************/ |
| 102 | |
Jeffrey Goode | e8eefe9 | 2009-11-01 19:39:23 +0000 | [diff] [blame] | 103 | /** misc external functions */ |
| 104 | |
| 105 | int get_codec_base_type(int type) |
Jeffrey Goode | 9a4420b | 2009-10-31 19:17:36 +0000 | [diff] [blame] | 106 | { |
Jeffrey Goode | e8eefe9 | 2009-11-01 19:39:23 +0000 | [diff] [blame] | 107 | switch (type) { |
| 108 | case AFMT_MPA_L1: |
| 109 | case AFMT_MPA_L2: |
| 110 | case AFMT_MPA_L3: |
| 111 | return AFMT_MPA_L3; |
| 112 | } |
| 113 | |
| 114 | return type; |
Jeffrey Goode | 9a4420b | 2009-10-31 19:17:36 +0000 | [diff] [blame] | 115 | } |
| 116 | |
| 117 | const char *get_codec_filename(int cod_spec) |
| 118 | { |
| 119 | const char *fname; |
| 120 | |
| 121 | #ifdef HAVE_RECORDING |
| 122 | /* Can choose decoder or encoder if one available */ |
| 123 | int type = cod_spec & CODEC_TYPE_MASK; |
| 124 | int afmt = cod_spec & CODEC_AFMT_MASK; |
| 125 | |
| 126 | if ((unsigned)afmt >= AFMT_NUM_CODECS) |
| 127 | type = AFMT_UNKNOWN | (type & CODEC_TYPE_MASK); |
| 128 | |
| 129 | fname = (type == CODEC_TYPE_ENCODER) ? |
| 130 | audio_formats[afmt].codec_enc_root_fn : |
| 131 | audio_formats[afmt].codec_root_fn; |
| 132 | |
| 133 | logf("%s: %d - %s", |
| 134 | (type == CODEC_TYPE_ENCODER) ? "Encoder" : "Decoder", |
| 135 | afmt, fname ? fname : "<unknown>"); |
| 136 | #else /* !HAVE_RECORDING */ |
| 137 | /* Always decoder */ |
| 138 | if ((unsigned)cod_spec >= AFMT_NUM_CODECS) |
| 139 | cod_spec = AFMT_UNKNOWN; |
| 140 | fname = audio_formats[cod_spec].codec_root_fn; |
| 141 | logf("Codec: %d - %s", cod_spec, fname ? fname : "<unknown>"); |
| 142 | #endif /* HAVE_RECORDING */ |
| 143 | |
| 144 | return fname; |
| 145 | } /* get_codec_filename */ |
| 146 | |
Jeffrey Goode | e8eefe9 | 2009-11-01 19:39:23 +0000 | [diff] [blame] | 147 | /* Borrow the codec thread and return the ID */ |
| 148 | void codec_thread_do_callback(void (*fn)(void), unsigned int *id) |
| 149 | { |
| 150 | /* Set id before telling thread to call something; it may be |
| 151 | * needed before this function returns. */ |
| 152 | if (id != NULL) |
| 153 | *id = codec_thread_id; |
| 154 | |
| 155 | /* Codec thread will signal just before entering callback */ |
| 156 | LOGFQUEUE("codec >| Q_CODEC_DO_CALLBACK"); |
| 157 | queue_send(&codec_queue, Q_CODEC_DO_CALLBACK, (intptr_t)fn); |
| 158 | } |
| 159 | |
| 160 | |
| 161 | /** codec API callbacks */ |
| 162 | |
| 163 | static void* codec_get_buffer(size_t *size) |
| 164 | { |
| 165 | if (codec_size >= CODEC_SIZE) |
| 166 | return NULL; |
| 167 | *size = CODEC_SIZE - codec_size; |
| 168 | return &codecbuf[codec_size]; |
| 169 | } |
| 170 | |
Jeffrey Goode | 9a4420b | 2009-10-31 19:17:36 +0000 | [diff] [blame] | 171 | static bool codec_pcmbuf_insert_callback( |
| 172 | const void *ch1, const void *ch2, int count) |
| 173 | { |
| 174 | const char *src[2] = { ch1, ch2 }; |
| 175 | |
| 176 | while (count > 0) |
| 177 | { |
| 178 | int out_count = dsp_output_count(ci.dsp, count); |
| 179 | int inp_count; |
| 180 | char *dest; |
| 181 | |
| 182 | /* Prevent audio from a previous track from playing */ |
| 183 | if (ci.new_track || ci.stop_codec) |
| 184 | return true; |
| 185 | |
| 186 | while ((dest = pcmbuf_request_buffer(&out_count)) == NULL) |
| 187 | { |
| 188 | cancel_cpu_boost(); |
| 189 | sleep(1); |
| 190 | if (ci.seek_time || ci.new_track || ci.stop_codec) |
| 191 | return true; |
| 192 | } |
| 193 | |
| 194 | /* Get the real input_size for output_size bytes, guarding |
| 195 | * against resampling buffer overflows. */ |
| 196 | inp_count = dsp_input_count(ci.dsp, out_count); |
| 197 | |
| 198 | if (inp_count <= 0) |
| 199 | return true; |
| 200 | |
| 201 | /* Input size has grown, no error, just don't write more than length */ |
| 202 | if (inp_count > count) |
| 203 | inp_count = count; |
| 204 | |
| 205 | out_count = dsp_process(ci.dsp, dest, src, inp_count); |
| 206 | |
| 207 | if (out_count <= 0) |
| 208 | return true; |
| 209 | |
| 210 | pcmbuf_write_complete(out_count); |
| 211 | |
| 212 | count -= inp_count; |
| 213 | } |
| 214 | |
| 215 | return true; |
| 216 | } /* codec_pcmbuf_insert_callback */ |
| 217 | |
Jeffrey Goode | 013fe35 | 2009-11-05 17:32:32 +0000 | [diff] [blame] | 218 | static void codec_set_elapsed_callback(unsigned long value) |
Jeffrey Goode | 9a4420b | 2009-10-31 19:17:36 +0000 | [diff] [blame] | 219 | { |
Jeffrey Goode | 9a4420b | 2009-10-31 19:17:36 +0000 | [diff] [blame] | 220 | if (ci.seek_time) |
| 221 | return; |
| 222 | |
| 223 | #ifdef AB_REPEAT_ENABLE |
| 224 | ab_position_report(value); |
| 225 | #endif |
| 226 | |
Jeffrey Goode | 013fe35 | 2009-11-05 17:32:32 +0000 | [diff] [blame] | 227 | unsigned long latency = pcmbuf_get_latency(); |
Jeffrey Goode | 9a4420b | 2009-10-31 19:17:36 +0000 | [diff] [blame] | 228 | if (value < latency) |
| 229 | thistrack_id3->elapsed = 0; |
Jeffrey Goode | e8eefe9 | 2009-11-01 19:39:23 +0000 | [diff] [blame] | 230 | else |
Jeffrey Goode | 9a4420b | 2009-10-31 19:17:36 +0000 | [diff] [blame] | 231 | { |
Jeffrey Goode | 013fe35 | 2009-11-05 17:32:32 +0000 | [diff] [blame] | 232 | unsigned long elapsed = value - latency; |
Jeffrey Goode | e8eefe9 | 2009-11-01 19:39:23 +0000 | [diff] [blame] | 233 | if (elapsed > thistrack_id3->elapsed || |
| 234 | elapsed < thistrack_id3->elapsed - 2) |
| 235 | { |
| 236 | thistrack_id3->elapsed = elapsed; |
| 237 | } |
Jeffrey Goode | 9a4420b | 2009-10-31 19:17:36 +0000 | [diff] [blame] | 238 | } |
| 239 | } |
| 240 | |
| 241 | static void codec_set_offset_callback(size_t value) |
| 242 | { |
Jeffrey Goode | 9a4420b | 2009-10-31 19:17:36 +0000 | [diff] [blame] | 243 | if (ci.seek_time) |
| 244 | return; |
| 245 | |
Jeffrey Goode | 013fe35 | 2009-11-05 17:32:32 +0000 | [diff] [blame] | 246 | unsigned long latency = pcmbuf_get_latency() * thistrack_id3->bitrate / 8; |
Jeffrey Goode | 9a4420b | 2009-10-31 19:17:36 +0000 | [diff] [blame] | 247 | if (value < latency) |
| 248 | thistrack_id3->offset = 0; |
| 249 | else |
| 250 | thistrack_id3->offset = value - latency; |
| 251 | } |
| 252 | |
Jeffrey Goode | e8eefe9 | 2009-11-01 19:39:23 +0000 | [diff] [blame] | 253 | /* helper function, not a callback */ |
Jeffrey Goode | 9a4420b | 2009-10-31 19:17:36 +0000 | [diff] [blame] | 254 | static void codec_advance_buffer_counters(size_t amount) |
| 255 | { |
| 256 | bufadvance(get_audio_hid(), amount); |
| 257 | ci.curpos += amount; |
| 258 | } |
| 259 | |
| 260 | /* copy up-to size bytes into ptr and return the actual size copied */ |
| 261 | static size_t codec_filebuf_callback(void *ptr, size_t size) |
| 262 | { |
| 263 | ssize_t copy_n; |
| 264 | |
| 265 | if (ci.stop_codec || !audio_is_playing()) |
| 266 | return 0; |
| 267 | |
| 268 | copy_n = bufread(get_audio_hid(), size, ptr); |
| 269 | |
| 270 | /* Nothing requested OR nothing left */ |
| 271 | if (copy_n == 0) |
| 272 | return 0; |
| 273 | |
| 274 | /* Update read and other position pointers */ |
| 275 | codec_advance_buffer_counters(copy_n); |
| 276 | |
| 277 | /* Return the actual amount of data copied to the buffer */ |
| 278 | return copy_n; |
| 279 | } /* codec_filebuf_callback */ |
| 280 | |
| 281 | static void* codec_request_buffer_callback(size_t *realsize, size_t reqsize) |
| 282 | { |
| 283 | size_t copy_n = reqsize; |
| 284 | ssize_t ret; |
| 285 | void *ptr; |
| 286 | |
| 287 | if (!audio_is_playing()) |
| 288 | { |
| 289 | *realsize = 0; |
| 290 | return NULL; |
| 291 | } |
| 292 | |
| 293 | ret = bufgetdata(get_audio_hid(), reqsize, &ptr); |
| 294 | if (ret >= 0) |
| 295 | copy_n = MIN((size_t)ret, reqsize); |
| 296 | |
| 297 | if (copy_n == 0) |
| 298 | { |
| 299 | *realsize = 0; |
| 300 | return NULL; |
| 301 | } |
| 302 | |
| 303 | *realsize = copy_n; |
| 304 | |
| 305 | return ptr; |
| 306 | } /* codec_request_buffer_callback */ |
| 307 | |
Jeffrey Goode | 9a4420b | 2009-10-31 19:17:36 +0000 | [diff] [blame] | 308 | static void codec_advance_buffer_callback(size_t amount) |
| 309 | { |
| 310 | codec_advance_buffer_counters(amount); |
| 311 | codec_set_offset_callback(ci.curpos); |
| 312 | } |
| 313 | |
| 314 | static void codec_advance_buffer_loc_callback(void *ptr) |
| 315 | { |
| 316 | size_t amount = buf_get_offset(get_audio_hid(), ptr); |
| 317 | codec_advance_buffer_callback(amount); |
| 318 | } |
| 319 | |
Jeffrey Goode | 9a4420b | 2009-10-31 19:17:36 +0000 | [diff] [blame] | 320 | static bool codec_seek_buffer_callback(size_t newpos) |
| 321 | { |
| 322 | logf("codec_seek_buffer_callback"); |
| 323 | |
| 324 | int ret = bufseek(get_audio_hid(), newpos); |
| 325 | if (ret == 0) { |
| 326 | ci.curpos = newpos; |
| 327 | return true; |
| 328 | } |
| 329 | else { |
| 330 | return false; |
| 331 | } |
| 332 | } |
| 333 | |
Jeffrey Goode | e8eefe9 | 2009-11-01 19:39:23 +0000 | [diff] [blame] | 334 | static void codec_seek_complete_callback(void) |
Jeffrey Goode | 9a4420b | 2009-10-31 19:17:36 +0000 | [diff] [blame] | 335 | { |
Jeffrey Goode | e8eefe9 | 2009-11-01 19:39:23 +0000 | [diff] [blame] | 336 | logf("seek_complete"); |
| 337 | /* If seeking-while-playing, pcm_is_paused() is true. |
| 338 | * If seeking-while-paused, audio_is_paused() is true. |
| 339 | * A seamless seek skips this section. */ |
| 340 | if (pcm_is_paused() || audio_is_paused()) |
| 341 | { |
| 342 | /* Clear the buffer */ |
| 343 | pcmbuf_play_stop(); |
| 344 | dsp_configure(ci.dsp, DSP_FLUSH, 0); |
| 345 | |
| 346 | /* If seeking-while-playing, resume pcm playback */ |
| 347 | if (!audio_is_paused()) |
| 348 | pcmbuf_pause(false); |
Jeffrey Goode | 9a4420b | 2009-10-31 19:17:36 +0000 | [diff] [blame] | 349 | } |
Jeffrey Goode | e8eefe9 | 2009-11-01 19:39:23 +0000 | [diff] [blame] | 350 | ci.seek_time = 0; |
Jeffrey Goode | 9a4420b | 2009-10-31 19:17:36 +0000 | [diff] [blame] | 351 | } |
| 352 | |
| 353 | static void codec_discard_codec_callback(void) |
| 354 | { |
| 355 | int *codec_hid = get_codec_hid(); |
| 356 | if (*codec_hid >= 0) |
| 357 | { |
| 358 | bufclose(*codec_hid); |
| 359 | *codec_hid = -1; |
| 360 | } |
| 361 | } |
| 362 | |
Jeffrey Goode | e8eefe9 | 2009-11-01 19:39:23 +0000 | [diff] [blame] | 363 | static bool codec_request_next_track_callback(void) |
| 364 | { |
| 365 | int prev_codectype; |
| 366 | |
| 367 | if (ci.stop_codec || !audio_is_playing()) |
| 368 | return false; |
| 369 | |
| 370 | prev_codectype = get_codec_base_type(thistrack_id3->codectype); |
| 371 | if (!codec_load_next_track()) |
| 372 | return false; |
| 373 | |
| 374 | /* Seek to the beginning of the new track because if the struct |
| 375 | mp3entry was buffered, "elapsed" might not be zero (if the track has |
| 376 | been played already but not unbuffered) */ |
| 377 | codec_seek_buffer_callback(thistrack_id3->first_frame_offset); |
| 378 | /* Check if the next codec is the same file. */ |
| 379 | if (prev_codectype == get_codec_base_type(thistrack_id3->codectype)) |
| 380 | { |
| 381 | logf("New track loaded"); |
| 382 | codec_discard_codec_callback(); |
| 383 | return true; |
| 384 | } |
| 385 | else |
| 386 | { |
| 387 | logf("New codec:%d/%d", thistrack_id3->codectype, prev_codectype); |
| 388 | return false; |
| 389 | } |
| 390 | } |
| 391 | |
| 392 | static void codec_configure_callback(int setting, intptr_t value) |
| 393 | { |
| 394 | if (!dsp_configure(ci.dsp, setting, value)) |
| 395 | { logf("Illegal key:%d", setting); } |
| 396 | } |
| 397 | |
| 398 | /* Initialize codec API */ |
| 399 | void codec_init_codec_api(void) |
| 400 | { |
| 401 | ci.dsp = (struct dsp_config *)dsp_configure(NULL, DSP_MYDSP, |
| 402 | CODEC_IDX_AUDIO); |
| 403 | ci.codec_get_buffer = codec_get_buffer; |
| 404 | ci.pcmbuf_insert = codec_pcmbuf_insert_callback; |
| 405 | ci.set_elapsed = codec_set_elapsed_callback; |
| 406 | ci.read_filebuf = codec_filebuf_callback; |
| 407 | ci.request_buffer = codec_request_buffer_callback; |
| 408 | ci.advance_buffer = codec_advance_buffer_callback; |
| 409 | ci.advance_buffer_loc = codec_advance_buffer_loc_callback; |
| 410 | ci.seek_buffer = codec_seek_buffer_callback; |
| 411 | ci.seek_complete = codec_seek_complete_callback; |
| 412 | ci.request_next_track = codec_request_next_track_callback; |
| 413 | ci.discard_codec = codec_discard_codec_callback; |
| 414 | ci.set_offset = codec_set_offset_callback; |
| 415 | ci.configure = codec_configure_callback; |
| 416 | } |
| 417 | |
| 418 | |
Jeffrey Goode | 04b01e1 | 2009-11-05 21:59:36 +0000 | [diff] [blame^] | 419 | /* track change */ |
Jeffrey Goode | 9a4420b | 2009-10-31 19:17:36 +0000 | [diff] [blame] | 420 | |
| 421 | static bool codec_load_next_track(void) |
| 422 | { |
| 423 | intptr_t result = Q_CODEC_REQUEST_FAILED; |
| 424 | |
| 425 | audio_set_prev_elapsed(thistrack_id3->elapsed); |
| 426 | |
| 427 | #ifdef AB_REPEAT_ENABLE |
| 428 | ab_end_of_track_report(); |
| 429 | #endif |
| 430 | |
| 431 | logf("Request new track"); |
| 432 | |
| 433 | if (ci.new_track == 0) |
| 434 | { |
| 435 | ci.new_track++; |
| 436 | automatic_skip = true; |
| 437 | } |
| 438 | |
| 439 | if (!ci.stop_codec) |
| 440 | { |
| 441 | trigger_cpu_boost(); |
| 442 | LOGFQUEUE("codec >| audio Q_AUDIO_CHECK_NEW_TRACK"); |
| 443 | result = queue_send(&audio_queue, Q_AUDIO_CHECK_NEW_TRACK, 0); |
| 444 | } |
| 445 | |
| 446 | switch (result) |
| 447 | { |
| 448 | case Q_CODEC_REQUEST_COMPLETE: |
| 449 | LOGFQUEUE("codec |< Q_CODEC_REQUEST_COMPLETE"); |
Jeffrey Goode | 04b01e1 | 2009-11-05 21:59:36 +0000 | [diff] [blame^] | 450 | pcmbuf_start_track_change(!automatic_skip); |
Jeffrey Goode | 9a4420b | 2009-10-31 19:17:36 +0000 | [diff] [blame] | 451 | return true; |
| 452 | |
| 453 | case Q_CODEC_REQUEST_FAILED: |
| 454 | LOGFQUEUE("codec |< Q_CODEC_REQUEST_FAILED"); |
| 455 | ci.new_track = 0; |
| 456 | ci.stop_codec = true; |
| 457 | codec_requested_stop = true; |
| 458 | return false; |
| 459 | |
| 460 | default: |
| 461 | LOGFQUEUE("codec |< default"); |
| 462 | ci.stop_codec = true; |
| 463 | codec_requested_stop = true; |
| 464 | return false; |
| 465 | } |
| 466 | } |
| 467 | |
Jeffrey Goode | e8eefe9 | 2009-11-01 19:39:23 +0000 | [diff] [blame] | 468 | /** CODEC THREAD */ |
Jeffrey Goode | 9a4420b | 2009-10-31 19:17:36 +0000 | [diff] [blame] | 469 | static void codec_thread(void) |
| 470 | { |
| 471 | struct queue_event ev; |
| 472 | int status; |
| 473 | |
| 474 | while (1) { |
| 475 | status = 0; |
| 476 | |
| 477 | if (!pcmbuf_is_crossfade_active()) { |
| 478 | cancel_cpu_boost(); |
| 479 | } |
| 480 | |
| 481 | queue_wait(&codec_queue, &ev); |
| 482 | codec_requested_stop = false; |
| 483 | |
| 484 | switch (ev.id) { |
| 485 | case Q_CODEC_LOAD_DISK: |
| 486 | LOGFQUEUE("codec < Q_CODEC_LOAD_DISK"); |
| 487 | queue_reply(&codec_queue, 1); |
| 488 | audio_codec_loaded = true; |
| 489 | ci.stop_codec = false; |
| 490 | status = codec_load_file((const char *)ev.data, &ci); |
| 491 | LOGFQUEUE("codec_load_file %s %d\n", (const char *)ev.data, status); |
| 492 | break; |
| 493 | |
| 494 | case Q_CODEC_LOAD: |
| 495 | LOGFQUEUE("codec < Q_CODEC_LOAD"); |
| 496 | if (*get_codec_hid() < 0) { |
| 497 | logf("Codec slot is empty!"); |
| 498 | /* Wait for the pcm buffer to go empty */ |
| 499 | while (pcm_is_playing()) |
| 500 | yield(); |
| 501 | /* This must be set to prevent an infinite loop */ |
| 502 | ci.stop_codec = true; |
| 503 | LOGFQUEUE("codec > codec Q_AUDIO_PLAY"); |
| 504 | queue_post(&codec_queue, Q_AUDIO_PLAY, 0); |
| 505 | break; |
| 506 | } |
| 507 | |
| 508 | audio_codec_loaded = true; |
| 509 | ci.stop_codec = false; |
| 510 | status = codec_load_buf(*get_codec_hid(), &ci); |
| 511 | LOGFQUEUE("codec_load_buf %d\n", status); |
| 512 | break; |
| 513 | |
| 514 | case Q_CODEC_DO_CALLBACK: |
| 515 | LOGFQUEUE("codec < Q_CODEC_DO_CALLBACK"); |
| 516 | queue_reply(&codec_queue, 1); |
| 517 | if ((void*)ev.data != NULL) |
| 518 | { |
| 519 | cpucache_invalidate(); |
| 520 | ((void (*)(void))ev.data)(); |
| 521 | cpucache_flush(); |
| 522 | } |
| 523 | break; |
| 524 | |
| 525 | #ifdef AUDIO_HAVE_RECORDING |
| 526 | case Q_ENCODER_LOAD_DISK: |
| 527 | LOGFQUEUE("codec < Q_ENCODER_LOAD_DISK"); |
| 528 | audio_codec_loaded = false; /* Not audio codec! */ |
| 529 | logf("loading encoder"); |
| 530 | ci.stop_encoder = false; |
| 531 | status = codec_load_file((const char *)ev.data, &ci); |
| 532 | logf("encoder stopped"); |
| 533 | break; |
| 534 | #endif /* AUDIO_HAVE_RECORDING */ |
| 535 | |
| 536 | default: |
| 537 | LOGFQUEUE("codec < default"); |
| 538 | } |
| 539 | |
| 540 | if (audio_codec_loaded) |
| 541 | { |
| 542 | if (ci.stop_codec) |
| 543 | { |
| 544 | status = CODEC_OK; |
| 545 | if (!audio_is_playing()) |
| 546 | pcmbuf_play_stop(); |
| 547 | |
| 548 | } |
| 549 | audio_codec_loaded = false; |
| 550 | } |
| 551 | |
| 552 | switch (ev.id) { |
| 553 | case Q_CODEC_LOAD_DISK: |
| 554 | case Q_CODEC_LOAD: |
| 555 | LOGFQUEUE("codec < Q_CODEC_LOAD"); |
| 556 | if (audio_is_playing()) |
| 557 | { |
| 558 | if (ci.new_track || status != CODEC_OK) |
| 559 | { |
| 560 | if (!ci.new_track) |
| 561 | { |
| 562 | logf("Codec failure, %d %d", ci.new_track, status); |
| 563 | splash(HZ*2, "Codec failure"); |
| 564 | } |
| 565 | |
| 566 | if (!codec_load_next_track()) |
| 567 | { |
| 568 | LOGFQUEUE("codec > audio Q_AUDIO_STOP"); |
| 569 | /* End of playlist */ |
| 570 | queue_post(&audio_queue, Q_AUDIO_STOP, 0); |
| 571 | break; |
| 572 | } |
| 573 | } |
| 574 | else |
| 575 | { |
| 576 | logf("Codec finished"); |
| 577 | if (ci.stop_codec) |
| 578 | { |
| 579 | /* Wait for the audio to stop playing before |
| 580 | * triggering the WPS exit */ |
| 581 | while(pcm_is_playing()) |
| 582 | { |
| 583 | /* There has been one too many struct pointer swaps by now |
| 584 | * so even though it says othertrack_id3, its the correct one! */ |
| 585 | othertrack_id3->elapsed = |
| 586 | othertrack_id3->length - pcmbuf_get_latency(); |
| 587 | sleep(1); |
| 588 | } |
| 589 | |
| 590 | if (codec_requested_stop) |
| 591 | { |
| 592 | LOGFQUEUE("codec > audio Q_AUDIO_STOP"); |
| 593 | queue_post(&audio_queue, Q_AUDIO_STOP, 0); |
| 594 | } |
| 595 | break; |
| 596 | } |
| 597 | } |
| 598 | |
| 599 | if (*get_codec_hid() >= 0) |
| 600 | { |
| 601 | LOGFQUEUE("codec > codec Q_CODEC_LOAD"); |
| 602 | queue_post(&codec_queue, Q_CODEC_LOAD, 0); |
| 603 | } |
| 604 | else |
| 605 | { |
| 606 | const char *codec_fn = |
| 607 | get_codec_filename(thistrack_id3->codectype); |
| 608 | if (codec_fn) |
| 609 | { |
| 610 | LOGFQUEUE("codec > codec Q_CODEC_LOAD_DISK"); |
| 611 | queue_post(&codec_queue, Q_CODEC_LOAD_DISK, |
| 612 | (intptr_t)codec_fn); |
| 613 | } |
| 614 | } |
| 615 | } |
| 616 | break; |
| 617 | |
| 618 | #ifdef AUDIO_HAVE_RECORDING |
| 619 | case Q_ENCODER_LOAD_DISK: |
| 620 | LOGFQUEUE("codec < Q_ENCODER_LOAD_DISK"); |
| 621 | |
| 622 | if (status == CODEC_OK) |
| 623 | break; |
| 624 | |
| 625 | logf("Encoder failure"); |
| 626 | splash(HZ*2, "Encoder failure"); |
| 627 | |
| 628 | if (ci.enc_codec_loaded < 0) |
| 629 | break; |
| 630 | |
| 631 | logf("Encoder failed to load"); |
| 632 | ci.enc_codec_loaded = -1; |
| 633 | break; |
| 634 | #endif /* AUDIO_HAVE_RECORDING */ |
| 635 | |
| 636 | default: |
| 637 | LOGFQUEUE("codec < default"); |
| 638 | |
| 639 | } /* end switch */ |
| 640 | } |
| 641 | } |
| 642 | |
Jeffrey Goode | 9a4420b | 2009-10-31 19:17:36 +0000 | [diff] [blame] | 643 | void make_codec_thread(void) |
| 644 | { |
| 645 | codec_thread_id = create_thread( |
| 646 | codec_thread, codec_stack, sizeof(codec_stack), |
| 647 | CREATE_THREAD_FROZEN, |
| 648 | codec_thread_name IF_PRIO(, PRIORITY_PLAYBACK) |
| 649 | IF_COP(, CPU)); |
| 650 | queue_enable_queue_send(&codec_queue, &codec_queue_sender_list, |
| 651 | codec_thread_id); |
| 652 | } |