summaryrefslogtreecommitdiff
path: root/apps/plugins/mpegplayer/audio_thread.c
diff options
context:
space:
mode:
authorMichael Sevakis <jethead71@rockbox.org>2007-12-29 19:46:35 +0000
committerMichael Sevakis <jethead71@rockbox.org>2007-12-29 19:46:35 +0000
commita222f27c4a17ed8f9809cda7861fe5f23d4cc0c1 (patch)
treed393a23d83549f99772bb156e59ffb88725148b6 /apps/plugins/mpegplayer/audio_thread.c
parent1d0f6b90ff43776e55b4b9f062c9bea3f99aa768 (diff)
downloadrockbox-a222f27c4a17ed8f9809cda7861fe5f23d4cc0c1.tar.gz
rockbox-a222f27c4a17ed8f9809cda7861fe5f23d4cc0c1.zip
mpegplayer: Make playback engine fully seekable and frame-accurate and split into logical parts. Be sure to have all current features work. Actual UI for seeking will be added soon. Recommended GOP size is about 15-30 frames depending on target or seeking can be slow with really long GOPs (nature of MPEG video). More refined encoding recommendations for a particular player should be posted soon.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@15977 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps/plugins/mpegplayer/audio_thread.c')
-rw-r--r--apps/plugins/mpegplayer/audio_thread.c743
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 **/
27struct pts_queue_slot;
28struct 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 */
38static uint32_t* audio_stack;
39static size_t audio_stack_size; /* Keep gcc happy and init */
40#define AUDIO_STACKSIZE (9*1024)
41#ifndef SIMULATOR
42static uint32_t codec_stack_copy[AUDIO_STACKSIZE / sizeof(uint32_t)];
43#endif
44static struct event_queue audio_str_queue NOCACHEBSS_ATTR;
45static struct queue_sender_list audio_str_queue_send NOCACHEBSS_ATTR;
46struct stream audio_str IBSS_ATTR;
47
48/* libmad related definitions */
49static struct mad_stream stream IBSS_ATTR;
50static struct mad_frame frame IBSS_ATTR;
51static struct mad_synth synth IBSS_ATTR;
52
53/* 2567 bytes */
54static 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
61static mad_fixed_t mad_frame_overlap[2][32][18] IBSS_ATTR;
62#else
63static 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)
70struct 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 */
79struct
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
91static inline int audiodesc_queue_count(void)
92{
93 return audio_queue.write - audio_queue.read;
94}
95
96static 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 */
103static 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 */
115static 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 */
125static 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 */
131static 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 */
137static 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
148static 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
155static 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 */
226static 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 */
242static 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
332sync_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
356static 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
460static 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 */
666bool 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 */
730void 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}