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.c370
1 files changed, 158 insertions, 212 deletions
diff --git a/apps/codec_thread.c b/apps/codec_thread.c
index 22c177589f..d4217c3789 100644
--- a/apps/codec_thread.c
+++ b/apps/codec_thread.c
@@ -20,69 +20,17 @@
20 * 20 *
21 ****************************************************************************/ 21 ****************************************************************************/
22 22
23//#include <stdio.h>
24//#include <string.h>
25//#include <stdlib.h>
26//#include <ctype.h>
27
28#include "playback.h" 23#include "playback.h"
29#include "codec_thread.h" 24#include "codec_thread.h"
30#include "system.h" 25#include "system.h"
31//#include "thread.h"
32//#include "file.h"
33//#include "panic.h"
34//#include "memory.h"
35//#include "lcd.h"
36//#include "font.h"
37//#include "button.h"
38#include "kernel.h" 26#include "kernel.h"
39//#include "tree.h"
40//#include "debug.h"
41//#include "sprintf.h"
42//#include "settings.h"
43#include "codecs.h" 27#include "codecs.h"
44//#include "audio.h"
45#include "buffering.h" 28#include "buffering.h"
46//#include "appevents.h"
47//#include "voice_thread.h"
48//#include "mp3_playback.h"
49//#include "usb.h"
50//#include "storage.h"
51//#include "screens.h"
52//#include "playlist.h"
53#include "pcmbuf.h" 29#include "pcmbuf.h"
54//#include "buffer.h"
55#include "dsp.h" 30#include "dsp.h"
56#include "abrepeat.h" 31#include "abrepeat.h"
57//#include "cuesheet.h"
58#ifdef HAVE_TAGCACHE
59//#include "tagcache.h"
60#endif
61#ifdef HAVE_LCD_BITMAP
62//#include "icons.h"
63//#include "peakmeter.h"
64//#include "action.h"
65#ifdef HAVE_ALBUMART
66//#include "albumart.h"
67//#include "bmp.h"
68#endif
69#endif
70//#include "lang.h"
71//#include "misc.h"
72//#include "sound.h"
73#include "metadata.h" 32#include "metadata.h"
74#include "splash.h" 33#include "splash.h"
75//#include "talk.h"
76//#include "ata_idle_notify.h"
77
78#ifdef HAVE_RECORDING
79//#include "recording.h"
80//#include "pcm_record.h"
81#endif
82
83#ifdef IPOD_ACCESSORY_PROTOCOL
84//#include "iap.h"
85#endif
86 34
87/* Define LOGF_ENABLE to enable logf output in this file */ 35/* Define LOGF_ENABLE to enable logf output in this file */
88/*#define LOGF_ENABLE*/ 36/*#define LOGF_ENABLE*/
@@ -110,12 +58,16 @@
110#endif 58#endif
111 59
112 60
113/* Variables are commented with the threads that use them: * 61/* Variables are commented with the threads that use them:
114 * A=audio, C=codec, V=voice. A suffix of - indicates that * 62 * A=audio, C=codec, V=voice. A suffix of - indicates that
115 * the variable is read but not updated on that thread. */ 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 */
116 68
117/* Main state control */ 69/* Main state control */
118static volatile bool audio_codec_loaded SHAREDBSS_ATTR = false; /* Codec loaded? (C/A-) */ 70volatile bool audio_codec_loaded SHAREDBSS_ATTR = false; /* Codec loaded? (C/A-) */
119 71
120extern struct mp3entry *thistrack_id3, /* the currently playing track */ 72extern struct mp3entry *thistrack_id3, /* the currently playing track */
121 *othertrack_id3; /* prev track during track-change-transition, or end of playlist, 73 *othertrack_id3; /* prev track during track-change-transition, or end of playlist,
@@ -131,25 +83,35 @@ static bool codec_requested_stop = false;
131 83
132extern struct event_queue audio_queue; 84extern struct event_queue audio_queue;
133extern struct event_queue codec_queue; 85extern struct event_queue codec_queue;
134extern struct event_queue pcmbuf_queue; 86
87extern struct codec_api ci; /* from codecs.c */
135 88
136/* Codec thread */ 89/* Codec thread */
137extern struct codec_api ci; 90unsigned int codec_thread_id; /* For modifying thread priority later.
138unsigned int codec_thread_id; /* For modifying thread priority later. */ 91 Used by playback.c and pcmbuf.c */
139static struct queue_sender_list codec_queue_sender_list; 92static struct queue_sender_list codec_queue_sender_list;
140static long codec_stack[(DEFAULT_STACK_SIZE + 0x2000)/sizeof(long)] 93static long codec_stack[(DEFAULT_STACK_SIZE + 0x2000)/sizeof(long)]
141IBSS_ATTR; 94IBSS_ATTR;
142static const char codec_thread_name[] = "codec"; 95static const char codec_thread_name[] = "codec";
143 96
97/* function prototypes */
98static bool codec_load_next_track(void);
99
100
144/**************************************/ 101/**************************************/
145 102
146/* Function to be called by pcm buffer callbacks. 103/** misc external functions */
147 * Permissible Context(s): Audio interrupt 104
148 */ 105int get_codec_base_type(int type)
149static void pcmbuf_callback_queue_post(long id, intptr_t data)
150{ 106{
151 /* No lock since we're already in audio interrupt context */ 107 switch (type) {
152 queue_post(&pcmbuf_queue, id, data); 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;
153} 115}
154 116
155const char *get_codec_filename(int cod_spec) 117const char *get_codec_filename(int cod_spec)
@@ -182,7 +144,30 @@ const char *get_codec_filename(int cod_spec)
182 return fname; 144 return fname;
183} /* get_codec_filename */ 145} /* get_codec_filename */
184 146
185/* --- Codec thread --- */ 147/* Borrow the codec thread and return the ID */
148void 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
163static 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
186static bool codec_pcmbuf_insert_callback( 171static bool codec_pcmbuf_insert_callback(
187 const void *ch1, const void *ch2, int count) 172 const void *ch1, const void *ch2, int count)
188{ 173{
@@ -230,37 +215,8 @@ static bool codec_pcmbuf_insert_callback(
230 return true; 215 return true;
231} /* codec_pcmbuf_insert_callback */ 216} /* codec_pcmbuf_insert_callback */
232 217
233static void* codec_get_buffer(size_t *size)
234{
235 if (codec_size >= CODEC_SIZE)
236 return NULL;
237 *size = CODEC_SIZE - codec_size;
238 return &codecbuf[codec_size];
239}
240
241/* Between the codec and PCM track change, we need to keep updating the
242 "elapsed" value of the previous (to the codec, but current to the
243 user/PCM/WPS) track, so that the progressbar reaches the end.
244 During that transition, the WPS will display prevtrack_id3. */
245static void codec_pcmbuf_position_callback(size_t size) ICODE_ATTR;
246static void codec_pcmbuf_position_callback(size_t size)
247{
248 /* This is called from an ISR, so be quick */
249 unsigned int time = size * 1000 / 4 / NATIVE_FREQUENCY +
250 othertrack_id3->elapsed;
251
252 if (time >= othertrack_id3->length)
253 {
254 pcmbuf_set_position_callback(NULL);
255 othertrack_id3->elapsed = othertrack_id3->length;
256 }
257 else
258 othertrack_id3->elapsed = time;
259}
260
261static void codec_set_elapsed_callback(unsigned int value) 218static void codec_set_elapsed_callback(unsigned int value)
262{ 219{
263 unsigned int latency;
264 if (ci.seek_time) 220 if (ci.seek_time)
265 return; 221 return;
266 222
@@ -268,30 +224,33 @@ static void codec_set_elapsed_callback(unsigned int value)
268 ab_position_report(value); 224 ab_position_report(value);
269#endif 225#endif
270 226
271 latency = pcmbuf_get_latency(); 227 unsigned int latency = pcmbuf_get_latency();
272 if (value < latency) 228 if (value < latency)
273 thistrack_id3->elapsed = 0; 229 thistrack_id3->elapsed = 0;
274 else if (value - latency > thistrack_id3->elapsed || 230 else
275 value - latency < thistrack_id3->elapsed - 2)
276 { 231 {
277 thistrack_id3->elapsed = value - latency; 232 unsigned int elapsed = value - latency;
233 if (elapsed > thistrack_id3->elapsed ||
234 elapsed < thistrack_id3->elapsed - 2)
235 {
236 thistrack_id3->elapsed = elapsed;
237 }
278 } 238 }
279} 239}
280 240
281static void codec_set_offset_callback(size_t value) 241static void codec_set_offset_callback(size_t value)
282{ 242{
283 unsigned int latency;
284
285 if (ci.seek_time) 243 if (ci.seek_time)
286 return; 244 return;
287 245
288 latency = pcmbuf_get_latency() * thistrack_id3->bitrate / 8; 246 unsigned int latency = pcmbuf_get_latency() * thistrack_id3->bitrate / 8;
289 if (value < latency) 247 if (value < latency)
290 thistrack_id3->offset = 0; 248 thistrack_id3->offset = 0;
291 else 249 else
292 thistrack_id3->offset = value - latency; 250 thistrack_id3->offset = value - latency;
293} 251}
294 252
253/* helper function, not a callback */
295static void codec_advance_buffer_counters(size_t amount) 254static void codec_advance_buffer_counters(size_t amount)
296{ 255{
297 bufadvance(get_audio_hid(), amount); 256 bufadvance(get_audio_hid(), amount);
@@ -346,18 +305,6 @@ static void* codec_request_buffer_callback(size_t *realsize, size_t reqsize)
346 return ptr; 305 return ptr;
347} /* codec_request_buffer_callback */ 306} /* codec_request_buffer_callback */
348 307
349int get_codec_base_type(int type)
350{
351 switch (type) {
352 case AFMT_MPA_L1:
353 case AFMT_MPA_L2:
354 case AFMT_MPA_L3:
355 return AFMT_MPA_L3;
356 }
357
358 return type;
359}
360
361static void codec_advance_buffer_callback(size_t amount) 308static void codec_advance_buffer_callback(size_t amount)
362{ 309{
363 codec_advance_buffer_counters(amount); 310 codec_advance_buffer_counters(amount);
@@ -370,15 +317,26 @@ static void codec_advance_buffer_loc_callback(void *ptr)
370 codec_advance_buffer_callback(amount); 317 codec_advance_buffer_callback(amount);
371} 318}
372 319
320static 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
373static void codec_seek_complete_callback(void) 334static void codec_seek_complete_callback(void)
374{ 335{
375 logf("seek_complete"); 336 logf("seek_complete");
376 /* If seeking-while-playing, pcm playback is actually paused (pcm_is_paused()) 337 /* If seeking-while-playing, pcm_is_paused() is true.
377 * but audio_is_paused() is false. If seeking-while-paused, audio_is_paused() is 338 * If seeking-while-paused, audio_is_paused() is true.
378 * true, but pcm playback may have actually stopped due to a previous buffer clear. 339 * A seamless seek skips this section. */
379 * The buffer clear below occurs with either condition. A seemless seek skips
380 * this section and no buffer clear occurs.
381 */
382 if (pcm_is_paused() || audio_is_paused()) 340 if (pcm_is_paused() || audio_is_paused())
383 { 341 {
384 /* Clear the buffer */ 342 /* Clear the buffer */
@@ -392,51 +350,104 @@ static void codec_seek_complete_callback(void)
392 ci.seek_time = 0; 350 ci.seek_time = 0;
393} 351}
394 352
395static bool codec_seek_buffer_callback(size_t newpos) 353static void codec_discard_codec_callback(void)
396{ 354{
397 logf("codec_seek_buffer_callback"); 355 int *codec_hid = get_codec_hid();
356 if (*codec_hid >= 0)
357 {
358 bufclose(*codec_hid);
359 *codec_hid = -1;
360 }
361}
398 362
399 int ret = bufseek(get_audio_hid(), newpos); 363static bool codec_request_next_track_callback(void)
400 if (ret == 0) { 364{
401 ci.curpos = newpos; 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();
402 return true; 383 return true;
403 } 384 }
404 else { 385 else
386 {
387 logf("New codec:%d/%d", thistrack_id3->codectype, prev_codectype);
405 return false; 388 return false;
406 } 389 }
407} 390}
408 391
409static void codec_configure_callback(int setting, intptr_t value) 392static void codec_configure_callback(int setting, intptr_t value)
410{ 393{
411 switch (setting) { 394 if (!dsp_configure(ci.dsp, setting, value))
412 default: 395 { logf("Illegal key:%d", setting); }
413 if (!dsp_configure(ci.dsp, setting, value))
414 { logf("Illegal key:%d", setting); }
415 }
416} 396}
417 397
418static void codec_track_changed(void) 398/* Initialize codec API */
399void codec_init_codec_api(void)
419{ 400{
420 LOGFQUEUE("codec > audio Q_AUDIO_TRACK_CHANGED"); 401 ci.dsp = (struct dsp_config *)dsp_configure(NULL, DSP_MYDSP,
421 queue_post(&audio_queue, Q_AUDIO_TRACK_CHANGED, 0); 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;
422} 416}
423 417
424static void codec_pcmbuf_track_changed_callback(void)
425{
426 pcmbuf_set_position_callback(NULL);
427 pcmbuf_callback_queue_post(Q_AUDIO_TRACK_CHANGED, 0);
428}
429 418
430static void codec_discard_codec_callback(void) 419/** pcmbuf track change callbacks */
420
421/* Between the codec and PCM track change, we need to keep updating the
422 "elapsed" value of the previous (to the codec, but current to the
423 user/PCM/WPS) track, so that the progressbar reaches the end.
424 During that transition, the WPS will display prevtrack_id3. */
425static void codec_pcmbuf_position_callback(size_t size) ICODE_ATTR;
426static void codec_pcmbuf_position_callback(size_t size)
431{ 427{
432 int *codec_hid = get_codec_hid(); 428 /* This is called from an ISR, so be quick */
433 if (*codec_hid >= 0) 429 unsigned int time = size * 1000 / 4 / NATIVE_FREQUENCY +
430 othertrack_id3->elapsed;
431
432 if (time >= othertrack_id3->length)
434 { 433 {
435 bufclose(*codec_hid); 434 pcmbuf_set_position_callback(NULL);
436 *codec_hid = -1; 435 othertrack_id3->elapsed = othertrack_id3->length;
437 } 436 }
437 else
438 othertrack_id3->elapsed = time;
438} 439}
439 440
441static void codec_pcmbuf_track_changed_callback(void)
442{
443 LOGFQUEUE("codec > pcmbuf/audio Q_AUDIO_TRACK_CHANGED");
444 pcmbuf_set_position_callback(NULL);
445 audio_post_track_change();
446}
447
448
449/** track change functions */
450
440static inline void codec_gapless_track_change(void) 451static inline void codec_gapless_track_change(void)
441{ 452{
442 /* callback keeps the progress bar moving while the pcmbuf empties */ 453 /* callback keeps the progress bar moving while the pcmbuf empties */
@@ -450,7 +461,8 @@ static inline void codec_crossfade_track_change(void)
450 /* Initiate automatic crossfade mode */ 461 /* Initiate automatic crossfade mode */
451 pcmbuf_crossfade_init(false); 462 pcmbuf_crossfade_init(false);
452 /* Notify the wps that the track change starts now */ 463 /* Notify the wps that the track change starts now */
453 codec_track_changed(); 464 LOGFQUEUE("codec > audio Q_AUDIO_TRACK_CHANGED");
465 queue_post(&audio_queue, Q_AUDIO_TRACK_CHANGED, 0);
454} 466}
455 467
456static void codec_track_skip_done(bool was_manual) 468static void codec_track_skip_done(bool was_manual)
@@ -531,35 +543,7 @@ static bool codec_load_next_track(void)
531 } 543 }
532} 544}
533 545
534static bool codec_request_next_track_callback(void) 546/** CODEC THREAD */
535{
536 int prev_codectype;
537
538 if (ci.stop_codec || !audio_is_playing())
539 return false;
540
541 prev_codectype = get_codec_base_type(thistrack_id3->codectype);
542 if (!codec_load_next_track())
543 return false;
544
545 /* Seek to the beginning of the new track because if the struct
546 mp3entry was buffered, "elapsed" might not be zero (if the track has
547 been played already but not unbuffered) */
548 codec_seek_buffer_callback(thistrack_id3->first_frame_offset);
549 /* Check if the next codec is the same file. */
550 if (prev_codectype == get_codec_base_type(thistrack_id3->codectype))
551 {
552 logf("New track loaded");
553 codec_discard_codec_callback();
554 return true;
555 }
556 else
557 {
558 logf("New codec:%d/%d", thistrack_id3->codectype, prev_codectype);
559 return false;
560 }
561}
562
563static void codec_thread(void) 547static void codec_thread(void)
564{ 548{
565 struct queue_event ev; 549 struct queue_event ev;
@@ -734,44 +718,6 @@ static void codec_thread(void)
734 } 718 }
735} 719}
736 720
737/* Borrow the codec thread and return the ID */
738void codec_thread_do_callback(void (*fn)(void), unsigned int *id)
739{
740 /* Set id before telling thread to call something; it may be
741 * needed before this function returns. */
742 if (id != NULL)
743 *id = codec_thread_id;
744
745 /* Codec thread will signal just before entering callback */
746 LOGFQUEUE("codec >| Q_CODEC_DO_CALLBACK");
747 queue_send(&codec_queue, Q_CODEC_DO_CALLBACK, (intptr_t)fn);
748}
749
750void codec_init_codec_api(void)
751{
752 /* Initialize codec api. */
753 ci.read_filebuf = codec_filebuf_callback;
754 ci.pcmbuf_insert = codec_pcmbuf_insert_callback;
755 ci.codec_get_buffer = codec_get_buffer;
756 ci.request_buffer = codec_request_buffer_callback;
757 ci.advance_buffer = codec_advance_buffer_callback;
758 ci.advance_buffer_loc = codec_advance_buffer_loc_callback;
759 ci.request_next_track = codec_request_next_track_callback;
760 ci.seek_buffer = codec_seek_buffer_callback;
761 ci.seek_complete = codec_seek_complete_callback;
762 ci.set_elapsed = codec_set_elapsed_callback;
763 ci.set_offset = codec_set_offset_callback;
764 ci.configure = codec_configure_callback;
765 ci.discard_codec = codec_discard_codec_callback;
766 ci.dsp = (struct dsp_config *)dsp_configure(NULL, DSP_MYDSP,
767 CODEC_IDX_AUDIO);
768}
769
770bool codec_is_loaded(void)
771{
772 return audio_codec_loaded;
773}
774
775void make_codec_thread(void) 721void make_codec_thread(void)
776{ 722{
777 codec_thread_id = create_thread( 723 codec_thread_id = create_thread(