diff options
Diffstat (limited to 'apps/plugins/mpegplayer/audio_thread.c')
-rw-r--r-- | apps/plugins/mpegplayer/audio_thread.c | 743 |
1 files changed, 743 insertions, 0 deletions
diff --git a/apps/plugins/mpegplayer/audio_thread.c b/apps/plugins/mpegplayer/audio_thread.c new file mode 100644 index 0000000000..ee6ff05d2d --- /dev/null +++ b/apps/plugins/mpegplayer/audio_thread.c | |||
@@ -0,0 +1,743 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * mpegplayer audio thread implementation | ||
11 | * | ||
12 | * Copyright (c) 2007 Michael Sevakis | ||
13 | * | ||
14 | * All files in this archive are subject to the GNU General Public License. | ||
15 | * See the file COPYING in the source tree root for full license agreement. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | #include "plugin.h" | ||
22 | #include "mpegplayer.h" | ||
23 | #include "../../codecs/libmad/bit.h" | ||
24 | #include "../../codecs/libmad/mad.h" | ||
25 | |||
26 | /** Audio stream and thread **/ | ||
27 | struct pts_queue_slot; | ||
28 | struct audio_thread_data | ||
29 | { | ||
30 | struct queue_event ev; /* Our event queue to receive commands */ | ||
31 | int state; /* Thread state */ | ||
32 | int status; /* Media status (STREAM_PLAYING, etc.) */ | ||
33 | int mad_errors; /* A count of the errors in each frame */ | ||
34 | }; | ||
35 | |||
36 | /* The audio stack is stolen from the core codec thread (but not in uisim) */ | ||
37 | /* Used for stealing codec thread's stack */ | ||
38 | static uint32_t* audio_stack; | ||
39 | static size_t audio_stack_size; /* Keep gcc happy and init */ | ||
40 | #define AUDIO_STACKSIZE (9*1024) | ||
41 | #ifndef SIMULATOR | ||
42 | static uint32_t codec_stack_copy[AUDIO_STACKSIZE / sizeof(uint32_t)]; | ||
43 | #endif | ||
44 | static struct event_queue audio_str_queue NOCACHEBSS_ATTR; | ||
45 | static struct queue_sender_list audio_str_queue_send NOCACHEBSS_ATTR; | ||
46 | struct stream audio_str IBSS_ATTR; | ||
47 | |||
48 | /* libmad related definitions */ | ||
49 | static struct mad_stream stream IBSS_ATTR; | ||
50 | static struct mad_frame frame IBSS_ATTR; | ||
51 | static struct mad_synth synth IBSS_ATTR; | ||
52 | |||
53 | /* 2567 bytes */ | ||
54 | static unsigned char mad_main_data[MAD_BUFFER_MDLEN]; | ||
55 | |||
56 | /* There isn't enough room for this in IRAM on PortalPlayer, but there | ||
57 | is for Coldfire. */ | ||
58 | |||
59 | /* 4608 bytes */ | ||
60 | #ifdef CPU_COLDFIRE | ||
61 | static mad_fixed_t mad_frame_overlap[2][32][18] IBSS_ATTR; | ||
62 | #else | ||
63 | static mad_fixed_t mad_frame_overlap[2][32][18]; | ||
64 | #endif | ||
65 | |||
66 | /** A queue for saving needed information about MPEG audio packets **/ | ||
67 | #define AUDIODESC_QUEUE_LEN (1 << 5) /* 32 should be way more than sufficient - | ||
68 | if not, the case is handled */ | ||
69 | #define AUDIODESC_QUEUE_MASK (AUDIODESC_QUEUE_LEN-1) | ||
70 | struct audio_frame_desc | ||
71 | { | ||
72 | uint32_t time; /* Time stamp for packet in audio ticks */ | ||
73 | ssize_t size; /* Number of unprocessed bytes left in packet */ | ||
74 | }; | ||
75 | |||
76 | /* This starts out wr == rd but will never be emptied to zero during | ||
77 | streaming again in order to support initializing the first packet's | ||
78 | timestamp without a special case */ | ||
79 | struct | ||
80 | { | ||
81 | /* Compressed audio data */ | ||
82 | uint8_t *start; /* Start of encoded audio buffer */ | ||
83 | uint8_t *ptr; /* Pointer to next encoded audio data */ | ||
84 | ssize_t used; /* Number of bytes in MPEG audio buffer */ | ||
85 | /* Compressed audio data descriptors */ | ||
86 | unsigned read, write; | ||
87 | struct audio_frame_desc *curr; /* Current slot */ | ||
88 | struct audio_frame_desc descs[AUDIODESC_QUEUE_LEN]; | ||
89 | } audio_queue; | ||
90 | |||
91 | static inline int audiodesc_queue_count(void) | ||
92 | { | ||
93 | return audio_queue.write - audio_queue.read; | ||
94 | } | ||
95 | |||
96 | static inline bool audiodesc_queue_full(void) | ||
97 | { | ||
98 | return audio_queue.used >= MPA_MAX_FRAME_SIZE + MAD_BUFFER_GUARD || | ||
99 | audiodesc_queue_count() >= AUDIODESC_QUEUE_LEN; | ||
100 | } | ||
101 | |||
102 | /* Increments the queue tail postion - should be used to preincrement */ | ||
103 | static inline void audiodesc_queue_add_tail(void) | ||
104 | { | ||
105 | if (audiodesc_queue_full()) | ||
106 | { | ||
107 | DEBUGF("audiodesc_queue_add_tail: audiodesc queue full!\n"); | ||
108 | return; | ||
109 | } | ||
110 | |||
111 | audio_queue.write++; | ||
112 | } | ||
113 | |||
114 | /* Increments the queue tail position - leaves one slot as current */ | ||
115 | static inline bool audiodesc_queue_remove_head(void) | ||
116 | { | ||
117 | if (audio_queue.write == audio_queue.read) | ||
118 | return false; | ||
119 | |||
120 | audio_queue.read++; | ||
121 | return true; | ||
122 | } | ||
123 | |||
124 | /* Returns the "tail" at the index just behind the write index */ | ||
125 | static inline struct audio_frame_desc * audiodesc_queue_tail(void) | ||
126 | { | ||
127 | return &audio_queue.descs[(audio_queue.write - 1) & AUDIODESC_QUEUE_MASK]; | ||
128 | } | ||
129 | |||
130 | /* Returns a pointer to the current head */ | ||
131 | static inline struct audio_frame_desc * audiodesc_queue_head(void) | ||
132 | { | ||
133 | return &audio_queue.descs[audio_queue.read & AUDIODESC_QUEUE_MASK]; | ||
134 | } | ||
135 | |||
136 | /* Resets the pts queue - call when starting and seeking */ | ||
137 | static void audio_queue_reset(void) | ||
138 | { | ||
139 | audio_queue.ptr = audio_queue.start; | ||
140 | audio_queue.used = 0; | ||
141 | audio_queue.read = 0; | ||
142 | audio_queue.write = 0; | ||
143 | audio_queue.curr = audiodesc_queue_head(); | ||
144 | audio_queue.curr->time = 0; | ||
145 | audio_queue.curr->size = 0; | ||
146 | } | ||
147 | |||
148 | static void audio_queue_advance_pos(ssize_t len) | ||
149 | { | ||
150 | audio_queue.ptr += len; | ||
151 | audio_queue.used -= len; | ||
152 | audio_queue.curr->size -= len; | ||
153 | } | ||
154 | |||
155 | static int audio_buffer(struct stream *str, enum stream_parse_mode type) | ||
156 | { | ||
157 | int ret = STREAM_OK; | ||
158 | |||
159 | /* Carry any overshoot to the next size since we're technically | ||
160 | -size bytes into it already. If size is negative an audio | ||
161 | frame was split across packets. Old has to be saved before | ||
162 | moving the head. */ | ||
163 | if (audio_queue.curr->size <= 0 && audiodesc_queue_remove_head()) | ||
164 | { | ||
165 | struct audio_frame_desc *old = audio_queue.curr; | ||
166 | audio_queue.curr = audiodesc_queue_head(); | ||
167 | audio_queue.curr->size += old->size; | ||
168 | old->size = 0; | ||
169 | } | ||
170 | |||
171 | /* Add packets to compressed audio buffer until it's full or the | ||
172 | * timestamp queue is full - whichever happens first */ | ||
173 | while (!audiodesc_queue_full()) | ||
174 | { | ||
175 | ret = parser_get_next_data(str, type); | ||
176 | struct audio_frame_desc *curr; | ||
177 | ssize_t len; | ||
178 | |||
179 | if (ret != STREAM_OK) | ||
180 | break; | ||
181 | |||
182 | /* Get data from next audio packet */ | ||
183 | len = str->curr_packet_end - str->curr_packet; | ||
184 | |||
185 | if (str->pkt_flags & PKT_HAS_TS) | ||
186 | { | ||
187 | audiodesc_queue_add_tail(); | ||
188 | curr = audiodesc_queue_tail(); | ||
189 | curr->time = TS_TO_TICKS(str->pts); | ||
190 | /* pts->size should have been zeroed when slot was | ||
191 | freed */ | ||
192 | } | ||
193 | else | ||
194 | { | ||
195 | /* Add to the one just behind the tail - this may be | ||
196 | * the head or the previouly added tail - whether or | ||
197 | * not we'll ever reach this is quite in question | ||
198 | * since audio always seems to have every packet | ||
199 | * timestamped */ | ||
200 | curr = audiodesc_queue_tail(); | ||
201 | } | ||
202 | |||
203 | curr->size += len; | ||
204 | |||
205 | /* Slide any remainder over to beginning */ | ||
206 | if (audio_queue.ptr > audio_queue.start && audio_queue.used > 0) | ||
207 | { | ||
208 | rb->memmove(audio_queue.start, audio_queue.ptr, | ||
209 | audio_queue.used); | ||
210 | } | ||
211 | |||
212 | /* Splice this packet onto any remainder */ | ||
213 | rb->memcpy(audio_queue.start + audio_queue.used, | ||
214 | str->curr_packet, len); | ||
215 | |||
216 | audio_queue.used += len; | ||
217 | audio_queue.ptr = audio_queue.start; | ||
218 | |||
219 | rb->yield(); | ||
220 | } | ||
221 | |||
222 | return ret; | ||
223 | } | ||
224 | |||
225 | /* Initialise libmad */ | ||
226 | static void init_mad(void) | ||
227 | { | ||
228 | mad_stream_init(&stream); | ||
229 | mad_frame_init(&frame); | ||
230 | mad_synth_init(&synth); | ||
231 | |||
232 | /* We do this so libmad doesn't try to call codec_calloc() */ | ||
233 | rb->memset(mad_frame_overlap, 0, sizeof(mad_frame_overlap)); | ||
234 | frame.overlap = (void *)mad_frame_overlap; | ||
235 | |||
236 | rb->memset(mad_main_data, 0, sizeof(mad_main_data)); | ||
237 | stream.main_data = &mad_main_data; | ||
238 | } | ||
239 | |||
240 | /* Sync audio stream to a particular frame - see main decoder loop for | ||
241 | * detailed remarks */ | ||
242 | static int audio_sync(struct audio_thread_data *td, | ||
243 | struct str_sync_data *sd) | ||
244 | { | ||
245 | int retval = STREAM_MATCH; | ||
246 | uint32_t sdtime = TS_TO_TICKS(clip_time(&audio_str, sd->time)); | ||
247 | uint32_t time; | ||
248 | uint32_t duration = 0; | ||
249 | struct stream *str; | ||
250 | struct stream tmp_str; | ||
251 | struct mad_header header; | ||
252 | struct mad_stream stream; | ||
253 | |||
254 | if (td->ev.id == STREAM_SYNC) | ||
255 | { | ||
256 | /* Actually syncing for playback - use real stream */ | ||
257 | time = 0; | ||
258 | str = &audio_str; | ||
259 | } | ||
260 | else | ||
261 | { | ||
262 | /* Probing - use temp stream */ | ||
263 | time = INVALID_TIMESTAMP; | ||
264 | str = &tmp_str; | ||
265 | str->id = audio_str.id; | ||
266 | } | ||
267 | |||
268 | str->hdr.pos = sd->sk.pos; | ||
269 | str->hdr.limit = sd->sk.pos + sd->sk.len; | ||
270 | |||
271 | mad_stream_init(&stream); | ||
272 | mad_header_init(&header); | ||
273 | |||
274 | while (1) | ||
275 | { | ||
276 | if (audio_buffer(str, STREAM_PM_RANDOM_ACCESS) == STREAM_DATA_END) | ||
277 | { | ||
278 | DEBUGF("audio_sync:STR_DATA_END\n aqu:%ld swl:%ld swr:%ld\n", | ||
279 | audio_queue.used, str->hdr.win_left, str->hdr.win_right); | ||
280 | if (audio_queue.used <= MAD_BUFFER_GUARD) | ||
281 | goto sync_data_end; | ||
282 | } | ||
283 | |||
284 | stream.error = 0; | ||
285 | mad_stream_buffer(&stream, audio_queue.ptr, audio_queue.used); | ||
286 | |||
287 | if (stream.sync && mad_stream_sync(&stream) < 0) | ||
288 | { | ||
289 | DEBUGF(" audio: mad_stream_sync failed\n"); | ||
290 | audio_queue_advance_pos(MAX(audio_queue.curr->size - 1, 1)); | ||
291 | continue; | ||
292 | } | ||
293 | |||
294 | stream.sync = 0; | ||
295 | |||
296 | if (mad_header_decode(&header, &stream) < 0) | ||
297 | { | ||
298 | DEBUGF(" audio: mad_header_decode failed:%s\n", | ||
299 | mad_stream_errorstr(&stream)); | ||
300 | audio_queue_advance_pos(1); | ||
301 | continue; | ||
302 | } | ||
303 | |||
304 | duration = 32*MAD_NSBSAMPLES(&header); | ||
305 | time = audio_queue.curr->time; | ||
306 | |||
307 | DEBUGF(" audio: ft:%u t:%u fe:%u nsamp:%u sampr:%u\n", | ||
308 | (unsigned)TICKS_TO_TS(time), (unsigned)sd->time, | ||
309 | (unsigned)TICKS_TO_TS(time + duration), | ||
310 | (unsigned)duration, header.samplerate); | ||
311 | |||
312 | audio_queue_advance_pos(stream.this_frame - audio_queue.ptr); | ||
313 | |||
314 | if (time <= sdtime && sdtime < time + duration) | ||
315 | { | ||
316 | DEBUGF(" audio: ft<=t<fe\n"); | ||
317 | retval = STREAM_PERFECT_MATCH; | ||
318 | break; | ||
319 | } | ||
320 | else if (time > sdtime) | ||
321 | { | ||
322 | DEBUGF(" audio: ft>t\n"); | ||
323 | break; | ||
324 | } | ||
325 | |||
326 | audio_queue_advance_pos(stream.next_frame - audio_queue.ptr); | ||
327 | audio_queue.curr->time += duration; | ||
328 | |||
329 | rb->yield(); | ||
330 | } | ||
331 | |||
332 | sync_data_end: | ||
333 | if (td->ev.id == STREAM_FIND_END_TIME) | ||
334 | { | ||
335 | if (time != INVALID_TIMESTAMP) | ||
336 | { | ||
337 | time = TICKS_TO_TS(time); | ||
338 | duration = TICKS_TO_TS(duration); | ||
339 | sd->time = time + duration; | ||
340 | retval = STREAM_PERFECT_MATCH; | ||
341 | } | ||
342 | else | ||
343 | { | ||
344 | retval = STREAM_NOT_FOUND; | ||
345 | } | ||
346 | } | ||
347 | |||
348 | DEBUGF(" audio header: 0x%02X%02X%02X%02X\n", | ||
349 | (unsigned)audio_queue.ptr[0], (unsigned)audio_queue.ptr[1], | ||
350 | (unsigned)audio_queue.ptr[2], (unsigned)audio_queue.ptr[3]); | ||
351 | |||
352 | return retval; | ||
353 | (void)td; | ||
354 | } | ||
355 | |||
356 | static void audio_thread_msg(struct audio_thread_data *td) | ||
357 | { | ||
358 | while (1) | ||
359 | { | ||
360 | intptr_t reply = 0; | ||
361 | |||
362 | switch (td->ev.id) | ||
363 | { | ||
364 | case STREAM_PLAY: | ||
365 | td->status = STREAM_PLAYING; | ||
366 | |||
367 | switch (td->state) | ||
368 | { | ||
369 | case TSTATE_INIT: | ||
370 | td->state = TSTATE_DECODE; | ||
371 | case TSTATE_DECODE: | ||
372 | case TSTATE_RENDER_WAIT: | ||
373 | case TSTATE_RENDER_WAIT_END: | ||
374 | break; | ||
375 | |||
376 | case TSTATE_EOS: | ||
377 | /* At end of stream - no playback possible so fire the | ||
378 | * completion event */ | ||
379 | stream_generate_event(&audio_str, STREAM_EV_COMPLETE, 0); | ||
380 | break; | ||
381 | } | ||
382 | |||
383 | break; | ||
384 | |||
385 | case STREAM_PAUSE: | ||
386 | td->status = STREAM_PAUSED; | ||
387 | reply = td->state != TSTATE_EOS; | ||
388 | break; | ||
389 | |||
390 | case STREAM_STOP: | ||
391 | if (td->state == TSTATE_DATA) | ||
392 | stream_clear_notify(&audio_str, DISK_BUF_DATA_NOTIFY); | ||
393 | |||
394 | td->status = STREAM_STOPPED; | ||
395 | td->state = TSTATE_EOS; | ||
396 | |||
397 | reply = true; | ||
398 | break; | ||
399 | |||
400 | case STREAM_RESET: | ||
401 | if (td->state == TSTATE_DATA) | ||
402 | stream_clear_notify(&audio_str, DISK_BUF_DATA_NOTIFY); | ||
403 | |||
404 | td->status = STREAM_STOPPED; | ||
405 | td->state = TSTATE_INIT; | ||
406 | |||
407 | init_mad(); | ||
408 | td->mad_errors = 0; | ||
409 | |||
410 | audio_queue_reset(); | ||
411 | |||
412 | reply = true; | ||
413 | break; | ||
414 | |||
415 | case STREAM_NEEDS_SYNC: | ||
416 | reply = true; /* Audio always needs to */ | ||
417 | break; | ||
418 | |||
419 | case STREAM_SYNC: | ||
420 | case STREAM_FIND_END_TIME: | ||
421 | if (td->state != TSTATE_INIT) | ||
422 | break; | ||
423 | |||
424 | reply = audio_sync(td, (struct str_sync_data *)td->ev.data); | ||
425 | break; | ||
426 | |||
427 | case DISK_BUF_DATA_NOTIFY: | ||
428 | /* Our bun is done */ | ||
429 | if (td->state != TSTATE_DATA) | ||
430 | break; | ||
431 | |||
432 | td->state = TSTATE_DECODE; | ||
433 | str_data_notify_received(&audio_str); | ||
434 | break; | ||
435 | |||
436 | case STREAM_QUIT: | ||
437 | /* Time to go - make thread exit */ | ||
438 | td->state = TSTATE_EOS; | ||
439 | return; | ||
440 | } | ||
441 | |||
442 | str_reply_msg(&audio_str, reply); | ||
443 | |||
444 | if (td->status == STREAM_PLAYING) | ||
445 | { | ||
446 | switch (td->state) | ||
447 | { | ||
448 | case TSTATE_DECODE: | ||
449 | case TSTATE_RENDER_WAIT: | ||
450 | case TSTATE_RENDER_WAIT_END: | ||
451 | /* These return when in playing state */ | ||
452 | return; | ||
453 | } | ||
454 | } | ||
455 | |||
456 | str_get_msg(&audio_str, &td->ev); | ||
457 | } | ||
458 | } | ||
459 | |||
460 | static void audio_thread(void) | ||
461 | { | ||
462 | struct audio_thread_data td; | ||
463 | |||
464 | td.status = STREAM_STOPPED; | ||
465 | td.state = TSTATE_EOS; | ||
466 | |||
467 | /* We need this here to init the EMAC for Coldfire targets */ | ||
468 | init_mad(); | ||
469 | |||
470 | goto message_wait; | ||
471 | |||
472 | /* This is the decoding loop. */ | ||
473 | while (1) | ||
474 | { | ||
475 | td.state = TSTATE_DECODE; | ||
476 | |||
477 | /* Check for any pending messages and process them */ | ||
478 | if (str_have_msg(&audio_str)) | ||
479 | { | ||
480 | message_wait: | ||
481 | /* Wait for a message to be queued */ | ||
482 | str_get_msg(&audio_str, &td.ev); | ||
483 | |||
484 | message_process: | ||
485 | /* Process a message already dequeued */ | ||
486 | audio_thread_msg(&td); | ||
487 | |||
488 | switch (td.state) | ||
489 | { | ||
490 | /* These states are the only ones that should return */ | ||
491 | case TSTATE_DECODE: goto audio_decode; | ||
492 | case TSTATE_RENDER_WAIT: goto render_wait; | ||
493 | case TSTATE_RENDER_WAIT_END: goto render_wait_end; | ||
494 | /* Anything else is interpreted as an exit */ | ||
495 | default: return; | ||
496 | } | ||
497 | } | ||
498 | |||
499 | audio_decode: | ||
500 | |||
501 | /** Buffering **/ | ||
502 | switch (audio_buffer(&audio_str, STREAM_PM_STREAMING)) | ||
503 | { | ||
504 | case STREAM_DATA_NOT_READY: | ||
505 | { | ||
506 | td.state = TSTATE_DATA; | ||
507 | goto message_wait; | ||
508 | } /* STREAM_DATA_NOT_READY: */ | ||
509 | |||
510 | case STREAM_DATA_END: | ||
511 | { | ||
512 | if (audio_queue.used > MAD_BUFFER_GUARD) | ||
513 | break; | ||
514 | |||
515 | /* Used up remainder of compressed audio buffer. | ||
516 | * Force any residue to play if audio ended before | ||
517 | * reaching the threshold */ | ||
518 | td.state = TSTATE_RENDER_WAIT_END; | ||
519 | audio_queue_reset(); | ||
520 | |||
521 | render_wait_end: | ||
522 | pcm_output_drain(); | ||
523 | |||
524 | while (pcm_output_used() > (ssize_t)PCMOUT_LOW_WM) | ||
525 | { | ||
526 | str_get_msg_w_tmo(&audio_str, &td.ev, 1); | ||
527 | if (td.ev.id != SYS_TIMEOUT) | ||
528 | goto message_process; | ||
529 | } | ||
530 | |||
531 | td.state = TSTATE_EOS; | ||
532 | if (td.status == STREAM_PLAYING) | ||
533 | stream_generate_event(&audio_str, STREAM_EV_COMPLETE, 0); | ||
534 | |||
535 | rb->yield(); | ||
536 | goto message_wait; | ||
537 | } /* STREAM_DATA_END: */ | ||
538 | } | ||
539 | |||
540 | /** Decoding **/ | ||
541 | mad_stream_buffer(&stream, audio_queue.ptr, audio_queue.used); | ||
542 | |||
543 | int mad_stat = mad_frame_decode(&frame, &stream); | ||
544 | |||
545 | ssize_t len = stream.next_frame - audio_queue.ptr; | ||
546 | |||
547 | if (mad_stat != 0) | ||
548 | { | ||
549 | DEBUGF("audio: Stream error: %s\n", | ||
550 | mad_stream_errorstr(&stream)); | ||
551 | |||
552 | /* If something's goofed - try to perform resync by moving | ||
553 | * at least one byte at a time */ | ||
554 | audio_queue_advance_pos(MAX(len, 1)); | ||
555 | |||
556 | if (stream.error == MAD_FLAG_INCOMPLETE | ||
557 | || stream.error == MAD_ERROR_BUFLEN) | ||
558 | { | ||
559 | /* This makes the codec support partially corrupted files */ | ||
560 | if (++td.mad_errors <= MPA_MAX_FRAME_SIZE) | ||
561 | { | ||
562 | stream.error = 0; | ||
563 | rb->yield(); | ||
564 | continue; | ||
565 | } | ||
566 | DEBUGF("audio: Too many errors\n"); | ||
567 | } | ||
568 | else if (MAD_RECOVERABLE(stream.error)) | ||
569 | { | ||
570 | /* libmad says it can recover - just keep on decoding */ | ||
571 | rb->yield(); | ||
572 | continue; | ||
573 | } | ||
574 | else | ||
575 | { | ||
576 | /* Some other unrecoverable error */ | ||
577 | DEBUGF("audio: Unrecoverable error\n"); | ||
578 | } | ||
579 | |||
580 | /* This is too hard - bail out */ | ||
581 | td.state = TSTATE_EOS; | ||
582 | |||
583 | if (td.status == STREAM_PLAYING) | ||
584 | stream_generate_event(&audio_str, STREAM_EV_COMPLETE, 0); | ||
585 | |||
586 | td.status = STREAM_ERROR; | ||
587 | goto message_wait; | ||
588 | } | ||
589 | |||
590 | /* Adjust sizes by the frame size */ | ||
591 | audio_queue_advance_pos(len); | ||
592 | td.mad_errors = 0; /* Clear errors */ | ||
593 | |||
594 | /* Generate the pcm samples */ | ||
595 | mad_synth_frame(&synth, &frame); | ||
596 | |||
597 | /** Output **/ | ||
598 | |||
599 | /* TODO: Output through core dsp. We'll still use our own PCM buffer | ||
600 | since the core pcm buffer has no timestamping or clock facilities */ | ||
601 | |||
602 | /* Add a frame of audio to the pcm buffer. Maximum is 1152 samples. */ | ||
603 | render_wait: | ||
604 | if (synth.pcm.length > 0) | ||
605 | { | ||
606 | struct pcm_frame_header *pcm_insert = pcm_output_get_buffer(); | ||
607 | int16_t *audio_data = (int16_t *)pcm_insert->data; | ||
608 | unsigned length = synth.pcm.length; | ||
609 | ssize_t size = sizeof(*pcm_insert) + length*4; | ||
610 | |||
611 | td.state = TSTATE_RENDER_WAIT; | ||
612 | |||
613 | /* Wait for required amount of free buffer space */ | ||
614 | while (pcm_output_free() < size) | ||
615 | { | ||
616 | /* Wait one frame */ | ||
617 | int timeout = synth.pcm.length*HZ / synth.pcm.samplerate; | ||
618 | str_get_msg_w_tmo(&audio_str, &td.ev, MAX(timeout, 1)); | ||
619 | if (td.ev.id != SYS_TIMEOUT) | ||
620 | goto message_process; | ||
621 | } | ||
622 | |||
623 | pcm_insert->time = audio_queue.curr->time; | ||
624 | pcm_insert->size = size; | ||
625 | |||
626 | /* As long as we're on this timestamp, the time is just incremented | ||
627 | by the number of samples */ | ||
628 | audio_queue.curr->time += length; | ||
629 | |||
630 | if (MAD_NCHANNELS(&frame.header) == 2) | ||
631 | { | ||
632 | int32_t *left = &synth.pcm.samples[0][0]; | ||
633 | int32_t *right = &synth.pcm.samples[1][0]; | ||
634 | |||
635 | do | ||
636 | { | ||
637 | /* libmad outputs s3.28 */ | ||
638 | *audio_data++ = clip_sample(*left++ >> 13); | ||
639 | *audio_data++ = clip_sample(*right++ >> 13); | ||
640 | } | ||
641 | while (--length > 0); | ||
642 | } | ||
643 | else /* mono */ | ||
644 | { | ||
645 | int32_t *mono = &synth.pcm.samples[0][0]; | ||
646 | |||
647 | do | ||
648 | { | ||
649 | int32_t s = clip_sample(*mono++ >> 13); | ||
650 | *audio_data++ = s; | ||
651 | *audio_data++ = s; | ||
652 | } | ||
653 | while (--length > 0); | ||
654 | } | ||
655 | /**/ | ||
656 | |||
657 | /* Make this data available to DMA */ | ||
658 | pcm_output_add_data(); | ||
659 | } | ||
660 | |||
661 | rb->yield(); | ||
662 | } /* end decoding loop */ | ||
663 | } | ||
664 | |||
665 | /* Initializes the audio thread resources and starts the thread */ | ||
666 | bool audio_thread_init(void) | ||
667 | { | ||
668 | int i; | ||
669 | #ifdef SIMULATOR | ||
670 | /* The simulator thread implementation doesn't have stack buffers, and | ||
671 | these parameters are ignored. */ | ||
672 | (void)i; /* Keep gcc happy */ | ||
673 | audio_stack = NULL; | ||
674 | audio_stack_size = 0; | ||
675 | #else | ||
676 | /* Borrow the codec thread's stack (in IRAM on most targets) */ | ||
677 | audio_stack = NULL; | ||
678 | for (i = 0; i < MAXTHREADS; i++) | ||
679 | { | ||
680 | if (rb->strcmp(rb->threads[i].name, "codec") == 0) | ||
681 | { | ||
682 | /* Wait to ensure the codec thread has blocked */ | ||
683 | while (rb->threads[i].state != STATE_BLOCKED) | ||
684 | rb->yield(); | ||
685 | |||
686 | /* Now we can steal the stack */ | ||
687 | audio_stack = rb->threads[i].stack; | ||
688 | audio_stack_size = rb->threads[i].stack_size; | ||
689 | |||
690 | /* Backup the codec thread's stack */ | ||
691 | rb->memcpy(codec_stack_copy, audio_stack, audio_stack_size); | ||
692 | break; | ||
693 | } | ||
694 | } | ||
695 | |||
696 | if (audio_stack == NULL) | ||
697 | { | ||
698 | /* This shouldn't happen, but deal with it anyway by using | ||
699 | the copy instead */ | ||
700 | audio_stack = codec_stack_copy; | ||
701 | audio_stack_size = AUDIO_STACKSIZE; | ||
702 | } | ||
703 | #endif | ||
704 | |||
705 | /* Initialise the encoded audio buffer and its descriptors */ | ||
706 | audio_queue.start = mpeg_malloc(AUDIOBUF_ALLOC_SIZE, | ||
707 | MPEG_ALLOC_AUDIOBUF); | ||
708 | if (audio_queue.start == NULL) | ||
709 | return false; | ||
710 | |||
711 | /* Start the audio thread */ | ||
712 | audio_str.hdr.q = &audio_str_queue; | ||
713 | rb->queue_init(audio_str.hdr.q, false); | ||
714 | rb->queue_enable_queue_send(audio_str.hdr.q, &audio_str_queue_send); | ||
715 | |||
716 | audio_str.thread = rb->create_thread( | ||
717 | audio_thread, audio_stack, audio_stack_size, 0, | ||
718 | "mpgaudio" IF_PRIO(,PRIORITY_PLAYBACK) IF_COP(, CPU)); | ||
719 | |||
720 | if (audio_str.thread == NULL) | ||
721 | return false; | ||
722 | |||
723 | /* Wait for thread to initialize */ | ||
724 | str_send_msg(&audio_str, STREAM_NULL, 0); | ||
725 | |||
726 | return true; | ||
727 | } | ||
728 | |||
729 | /* Stops the audio thread */ | ||
730 | void audio_thread_exit(void) | ||
731 | { | ||
732 | if (audio_str.thread != NULL) | ||
733 | { | ||
734 | str_post_msg(&audio_str, STREAM_QUIT, 0); | ||
735 | rb->thread_wait(audio_str.thread); | ||
736 | audio_str.thread = NULL; | ||
737 | } | ||
738 | |||
739 | #ifndef SIMULATOR | ||
740 | /* Restore the codec thread's stack */ | ||
741 | rb->memcpy(audio_stack, codec_stack_copy, audio_stack_size); | ||
742 | #endif | ||
743 | } | ||