summaryrefslogtreecommitdiffstats
path: root/apps/plugins/mpegplayer/audio_thread.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/plugins/mpegplayer/audio_thread.c')
-rw-r--r--apps/plugins/mpegplayer/audio_thread.c721
1 files changed, 0 insertions, 721 deletions
diff --git a/apps/plugins/mpegplayer/audio_thread.c b/apps/plugins/mpegplayer/audio_thread.c
deleted file mode 100644
index 764ad111f2..0000000000
--- a/apps/plugins/mpegplayer/audio_thread.c
+++ /dev/null
@@ -1,721 +0,0 @@
-/***************************************************************************
- * __________ __ ___.
- * Open \______ \ ____ ____ | | _\_ |__ _______ ___
- * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
- * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
- * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
- * \/ \/ \/ \/ \/
- * $Id$
- *
- * mpegplayer audio thread implementation
- *
- * Copyright (c) 2007 Michael Sevakis
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ****************************************************************************/
-#include "plugin.h"
-#include "mpegplayer.h"
-#include "codecs/libmad/bit.h"
-#include "codecs/libmad/mad.h"
-
-/** Audio stream and thread **/
-struct pts_queue_slot;
-struct audio_thread_data
-{
- struct queue_event ev; /* Our event queue to receive commands */
- int state; /* Thread state */
- int status; /* Media status (STREAM_PLAYING, etc.) */
- int mad_errors; /* A count of the errors in each frame */
- unsigned samplerate; /* Current stream sample rate */
- int nchannels; /* Number of audio channels */
- struct dsp_config *dsp; /* The DSP we're using */
- struct dsp_buffer src; /* Current audio data for DSP processing */
-};
-
-/* The audio thread is stolen from the core codec thread */
-static struct event_queue audio_str_queue SHAREDBSS_ATTR;
-static struct queue_sender_list audio_str_queue_send SHAREDBSS_ATTR;
-struct stream audio_str IBSS_ATTR;
-
-/* libmad related definitions */
-static struct mad_stream stream IBSS_ATTR;
-static struct mad_frame frame IBSS_ATTR;
-static struct mad_synth synth IBSS_ATTR;
-
-/*sbsample buffer for mad_frame*/
-mad_fixed_t sbsample[2][36][32];
-
-/* 2567 bytes */
-static unsigned char mad_main_data[MAD_BUFFER_MDLEN];
-
-/* There isn't enough room for this in IRAM on PortalPlayer, but there
- is for Coldfire. */
-
-/* 4608 bytes */
-#if defined(CPU_COLDFIRE) || defined(CPU_S5L870X)
-static mad_fixed_t mad_frame_overlap[2][32][18] IBSS_ATTR;
-#else
-static mad_fixed_t mad_frame_overlap[2][32][18];
-#endif
-
-/** A queue for saving needed information about MPEG audio packets **/
-#define AUDIODESC_QUEUE_LEN (1 << 5) /* 32 should be way more than sufficient -
- if not, the case is handled */
-#define AUDIODESC_QUEUE_MASK (AUDIODESC_QUEUE_LEN-1)
-struct audio_frame_desc
-{
- uint32_t time; /* Time stamp for packet in audio ticks */
- ssize_t size; /* Number of unprocessed bytes left in packet */
-};
-
- /* This starts out wr == rd but will never be emptied to zero during
- streaming again in order to support initializing the first packet's
- timestamp without a special case */
-struct
-{
- /* Compressed audio data */
- uint8_t *start; /* Start of encoded audio buffer */
- uint8_t *ptr; /* Pointer to next encoded audio data */
- ssize_t used; /* Number of bytes in MPEG audio buffer */
- /* Compressed audio data descriptors */
- unsigned read, write;
- struct audio_frame_desc *curr; /* Current slot */
- struct audio_frame_desc descs[AUDIODESC_QUEUE_LEN];
-} audio_queue;
-
-static inline int audiodesc_queue_count(void)
-{
- return audio_queue.write - audio_queue.read;
-}
-
-static inline bool audiodesc_queue_full(void)
-{
- return audio_queue.used >= MPA_MAX_FRAME_SIZE + MAD_BUFFER_GUARD ||
- audiodesc_queue_count() >= AUDIODESC_QUEUE_LEN;
-}
-
-/* Increments the queue tail postion - should be used to preincrement */
-static inline void audiodesc_queue_add_tail(void)
-{
- if (audiodesc_queue_full())
- {
- DEBUGF("audiodesc_queue_add_tail: audiodesc queue full!\n");
- return;
- }
-
- audio_queue.write++;
-}
-
-/* Increments the queue head position - leaves one slot as current */
-static inline bool audiodesc_queue_remove_head(void)
-{
- if (audio_queue.write == audio_queue.read)
- return false;
-
- audio_queue.read++;
- return true;
-}
-
-/* Returns the "tail" at the index just behind the write index */
-static inline struct audio_frame_desc * audiodesc_queue_tail(void)
-{
- return &audio_queue.descs[(audio_queue.write - 1) & AUDIODESC_QUEUE_MASK];
-}
-
-/* Returns a pointer to the current head */
-static inline struct audio_frame_desc * audiodesc_queue_head(void)
-{
- return &audio_queue.descs[audio_queue.read & AUDIODESC_QUEUE_MASK];
-}
-
-/* Resets the pts queue - call when starting and seeking */
-static void audio_queue_reset(void)
-{
- audio_queue.ptr = audio_queue.start;
- audio_queue.used = 0;
- audio_queue.read = 0;
- audio_queue.write = 0;
- rb->memset(audio_queue.descs, 0, sizeof (audio_queue.descs));
- audio_queue.curr = audiodesc_queue_head();
-}
-
-static void audio_queue_advance_pos(ssize_t len)
-{
- audio_queue.ptr += len;
- audio_queue.used -= len;
- audio_queue.curr->size -= len;
-}
-
-static int audio_buffer(struct stream *str, enum stream_parse_mode type)
-{
- int ret = STREAM_OK;
-
- /* Carry any overshoot to the next size since we're technically
- -size bytes into it already. If size is negative an audio
- frame was split across packets. Old has to be saved before
- moving the head. */
- if (audio_queue.curr->size <= 0 && audiodesc_queue_remove_head())
- {
- struct audio_frame_desc *old = audio_queue.curr;
- audio_queue.curr = audiodesc_queue_head();
- audio_queue.curr->size += old->size;
- old->size = 0;
- }
-
- /* Add packets to compressed audio buffer until it's full or the
- * timestamp queue is full - whichever happens first */
- while (!audiodesc_queue_full())
- {
- ret = parser_get_next_data(str, type);
- struct audio_frame_desc *curr;
- ssize_t len;
-
- if (ret != STREAM_OK)
- break;
-
- /* Get data from next audio packet */
- len = str->curr_packet_end - str->curr_packet;
-
- if (str->pkt_flags & PKT_HAS_TS)
- {
- audiodesc_queue_add_tail();
- curr = audiodesc_queue_tail();
- curr->time = TS_TO_TICKS(str->pts);
- /* pts->size should have been zeroed when slot was
- freed */
- }
- else
- {
- /* Add to the one just behind the tail - this may be
- * the head or the previouly added tail - whether or
- * not we'll ever reach this is quite in question
- * since audio always seems to have every packet
- * timestamped */
- curr = audiodesc_queue_tail();
- }
-
- curr->size += len;
-
- /* Slide any remainder over to beginning */
- if (audio_queue.ptr > audio_queue.start && audio_queue.used > 0)
- {
- rb->memmove(audio_queue.start, audio_queue.ptr,
- audio_queue.used);
- }
-
- /* Splice this packet onto any remainder */
- rb->memcpy(audio_queue.start + audio_queue.used,
- str->curr_packet, len);
-
- audio_queue.used += len;
- audio_queue.ptr = audio_queue.start;
-
- rb->yield();
- }
-
- return ret;
-}
-
-/* Initialise libmad */
-static void init_mad(void)
-{
- /* init the sbsample buffer */
- frame.sbsample_prev = &sbsample;
- frame.sbsample = &sbsample;
-
- /* We do this so libmad doesn't try to call codec_calloc(). This needs to
- * be called before mad_stream_init(), mad_frame_inti() and
- * mad_synth_init(). */
- frame.overlap = &mad_frame_overlap;
- stream.main_data = &mad_main_data;
-
- /* Call mad initialization. Those will zero the arrays frame.overlap,
- * frame.sbsample and frame.sbsample_prev. Therefore there is no need to
- * zero them here. */
- mad_stream_init(&stream);
- mad_frame_init(&frame);
- mad_synth_init(&synth);
-}
-
-/* Sync audio stream to a particular frame - see main decoder loop for
- * detailed remarks */
-static int audio_sync(struct audio_thread_data *td,
- struct str_sync_data *sd)
-{
- int retval = STREAM_MATCH;
- uint32_t sdtime = TS_TO_TICKS(clip_time(&audio_str, sd->time));
- uint32_t time;
- uint32_t duration = 0;
- struct stream *str;
- struct stream tmp_str;
- struct mad_header header;
- struct mad_stream stream;
-
- if (td->ev.id == STREAM_SYNC)
- {
- /* Actually syncing for playback - use real stream */
- time = 0;
- str = &audio_str;
- }
- else
- {
- /* Probing - use temp stream */
- time = INVALID_TIMESTAMP;
- str = &tmp_str;
- str->id = audio_str.id;
- }
-
- str->hdr.pos = sd->sk.pos;
- str->hdr.limit = sd->sk.pos + sd->sk.len;
-
- mad_stream_init(&stream);
- mad_header_init(&header);
-
- while (1)
- {
- if (audio_buffer(str, STREAM_PM_RANDOM_ACCESS) == STREAM_DATA_END)
- {
- DEBUGF("audio_sync:STR_DATA_END\n aqu:%ld swl:%ld swr:%ld\n",
- (long)audio_queue.used, str->hdr.win_left, str->hdr.win_right);
- if (audio_queue.used <= MAD_BUFFER_GUARD)
- goto sync_data_end;
- }
-
- stream.error = 0;
- mad_stream_buffer(&stream, audio_queue.ptr, audio_queue.used);
-
- if (stream.sync && mad_stream_sync(&stream) < 0)
- {
- DEBUGF(" audio: mad_stream_sync failed\n");
- audio_queue_advance_pos(MAX(audio_queue.curr->size - 1, 1));
- continue;
- }
-
- stream.sync = 0;
-
- if (mad_header_decode(&header, &stream) < 0)
- {
- DEBUGF(" audio: mad_header_decode failed:%s\n",
- mad_stream_errorstr(&stream));
- audio_queue_advance_pos(1);
- continue;
- }
-
- duration = 32*MAD_NSBSAMPLES(&header);
- time = audio_queue.curr->time;
-
- DEBUGF(" audio: ft:%u t:%u fe:%u nsamp:%u sampr:%u\n",
- (unsigned)TICKS_TO_TS(time), (unsigned)sd->time,
- (unsigned)TICKS_TO_TS(time + duration),
- (unsigned)duration, header.samplerate);
-
- audio_queue_advance_pos(stream.this_frame - audio_queue.ptr);
-
- if (time <= sdtime && sdtime < time + duration)
- {
- DEBUGF(" audio: ft<=t<fe\n");
- retval = STREAM_PERFECT_MATCH;
- break;
- }
- else if (time > sdtime)
- {
- DEBUGF(" audio: ft>t\n");
- break;
- }
-
- audio_queue_advance_pos(stream.next_frame - audio_queue.ptr);
- audio_queue.curr->time += duration;
-
- rb->yield();
- }
-
-sync_data_end:
- if (td->ev.id == STREAM_FIND_END_TIME)
- {
- if (time != INVALID_TIMESTAMP)
- {
- time = TICKS_TO_TS(time);
- duration = TICKS_TO_TS(duration);
- sd->time = time + duration;
- retval = STREAM_PERFECT_MATCH;
- }
- else
- {
- retval = STREAM_NOT_FOUND;
- }
- }
-
- DEBUGF(" audio header: 0x%02X%02X%02X%02X\n",
- (unsigned)audio_queue.ptr[0], (unsigned)audio_queue.ptr[1],
- (unsigned)audio_queue.ptr[2], (unsigned)audio_queue.ptr[3]);
-
- return retval;
- (void)td;
-}
-
-static void audio_thread_msg(struct audio_thread_data *td)
-{
- while (1)
- {
- intptr_t reply = 0;
-
- switch (td->ev.id)
- {
- case STREAM_PLAY:
- td->status = STREAM_PLAYING;
-
- switch (td->state)
- {
- case TSTATE_INIT:
- td->state = TSTATE_DECODE;
- case TSTATE_DECODE:
- case TSTATE_RENDER_WAIT:
- break;
-
- case TSTATE_EOS:
- /* At end of stream - no playback possible so fire the
- * completion event */
- stream_generate_event(&audio_str, STREAM_EV_COMPLETE, 0);
- break;
- }
-
- break;
-
- case STREAM_PAUSE:
- td->status = STREAM_PAUSED;
- reply = td->state != TSTATE_EOS;
- break;
-
- case STREAM_STOP:
- if (td->state == TSTATE_DATA)
- stream_clear_notify(&audio_str, DISK_BUF_DATA_NOTIFY);
-
- td->status = STREAM_STOPPED;
- td->state = TSTATE_EOS;
-
- reply = true;
- break;
-
- case STREAM_RESET:
- if (td->state == TSTATE_DATA)
- stream_clear_notify(&audio_str, DISK_BUF_DATA_NOTIFY);
-
- td->status = STREAM_STOPPED;
- td->state = TSTATE_INIT;
- td->samplerate = 0;
- td->nchannels = 0;
-
- init_mad();
- td->mad_errors = 0;
-
- audio_queue_reset();
-
- reply = true;
- break;
-
- case STREAM_NEEDS_SYNC:
- reply = true; /* Audio always needs to */
- break;
-
- case STREAM_SYNC:
- case STREAM_FIND_END_TIME:
- if (td->state != TSTATE_INIT)
- break;
-
- reply = audio_sync(td, (struct str_sync_data *)td->ev.data);
- break;
-
- case DISK_BUF_DATA_NOTIFY:
- /* Our bun is done */
- if (td->state != TSTATE_DATA)
- break;
-
- td->state = TSTATE_DECODE;
- str_data_notify_received(&audio_str);
- break;
-
- case STREAM_QUIT:
- /* Time to go - make thread exit */
- td->state = TSTATE_EOS;
- return;
- }
-
- str_reply_msg(&audio_str, reply);
-
- if (td->status == STREAM_PLAYING)
- {
- switch (td->state)
- {
- case TSTATE_DECODE:
- case TSTATE_RENDER_WAIT:
- /* These return when in playing state */
- return;
- }
- }
-
- str_get_msg(&audio_str, &td->ev);
- }
-}
-
-static void audio_thread(void)
-{
- struct audio_thread_data td;
-#ifdef HAVE_PRIORITY_SCHEDULING
- /* Up the priority since the core DSP over-yields internally */
- int old_priority = rb->thread_set_priority(rb->thread_self(),
- PRIORITY_PLAYBACK-4);
-#endif
-
- rb->memset(&td, 0, sizeof (td));
- td.status = STREAM_STOPPED;
- td.state = TSTATE_EOS;
-
- /* We need this here to init the EMAC for Coldfire targets */
- init_mad();
-
- td.dsp = rb->dsp_get_config(CODEC_IDX_AUDIO);
- rb->dsp_configure(td.dsp, DSP_SET_OUT_FREQUENCY, CLOCK_RATE);
-#ifdef HAVE_PITCHCONTROL
- rb->sound_set_pitch(PITCH_SPEED_100);
- rb->dsp_set_timestretch(PITCH_SPEED_100);
-#endif
- rb->dsp_configure(td.dsp, DSP_RESET, 0);
- rb->dsp_configure(td.dsp, DSP_FLUSH, 0);
- rb->dsp_configure(td.dsp, DSP_SET_SAMPLE_DEPTH, MAD_F_FRACBITS);
-
- goto message_wait;
-
- /* This is the decoding loop. */
- while (1)
- {
- td.state = TSTATE_DECODE;
-
- /* Check for any pending messages and process them */
- if (str_have_msg(&audio_str))
- {
- message_wait:
- /* Wait for a message to be queued */
- str_get_msg(&audio_str, &td.ev);
-
- message_process:
- /* Process a message already dequeued */
- audio_thread_msg(&td);
-
- switch (td.state)
- {
- /* These states are the only ones that should return */
- case TSTATE_DECODE: goto audio_decode;
- case TSTATE_RENDER_WAIT: goto render_wait;
- /* Anything else is interpreted as an exit */
- default:
- {
-#ifdef HAVE_PRIORITY_SCHEDULING
- rb->thread_set_priority(rb->thread_self(), old_priority);
-#endif
- return;
- }
- }
- }
-
- audio_decode:
-
- /** Buffering **/
- switch (audio_buffer(&audio_str, STREAM_PM_STREAMING))
- {
- case STREAM_DATA_NOT_READY:
- {
- td.state = TSTATE_DATA;
- goto message_wait;
- } /* STREAM_DATA_NOT_READY: */
-
- case STREAM_DATA_END:
- {
- if (audio_queue.used > MAD_BUFFER_GUARD)
- break; /* Still have frames to decode */
-
- /* Used up remainder of compressed audio buffer. Wait for
- * samples on PCM buffer to finish playing. */
- audio_queue_reset();
-
- while (1)
- {
- if (pcm_output_empty())
- {
- td.state = TSTATE_EOS;
- stream_generate_event(&audio_str, STREAM_EV_COMPLETE, 0);
- break;
- }
-
- pcm_output_drain();
- str_get_msg_w_tmo(&audio_str, &td.ev, 1);
-
- if (td.ev.id != SYS_TIMEOUT)
- break;
- }
-
- goto message_wait;
- } /* STREAM_DATA_END: */
- }
-
- /** Decoding **/
- mad_stream_buffer(&stream, audio_queue.ptr, audio_queue.used);
-
- int mad_stat = mad_frame_decode(&frame, &stream);
-
- ssize_t len = stream.next_frame - audio_queue.ptr;
-
- if (mad_stat != 0)
- {
- DEBUGF("audio: Stream error: %s\n",
- mad_stream_errorstr(&stream));
-
- /* If something's goofed - try to perform resync by moving
- * at least one byte at a time */
- audio_queue_advance_pos(MAX(len, 1));
-
- if (stream.error == MAD_ERROR_BUFLEN)
- {
- /* This makes the codec support partially corrupted files */
- if (++td.mad_errors <= MPA_MAX_FRAME_SIZE)
- {
- stream.error = 0;
- rb->yield();
- continue;
- }
- DEBUGF("audio: Too many errors\n");
- }
- else if (MAD_RECOVERABLE(stream.error))
- {
- /* libmad says it can recover - just keep on decoding */
- rb->yield();
- continue;
- }
- else
- {
- /* Some other unrecoverable error */
- DEBUGF("audio: Unrecoverable error\n");
- }
-
- /* This is too hard - bail out */
- td.state = TSTATE_EOS;
- td.status = STREAM_ERROR;
- stream_generate_event(&audio_str, STREAM_EV_COMPLETE, 0);
-
- goto message_wait;
- }
-
- /* Adjust sizes by the frame size */
- audio_queue_advance_pos(len);
- td.mad_errors = 0; /* Clear errors */
-
- /* Generate the pcm samples */
- mad_synth_frame(&synth, &frame);
-
- /** Output **/
- if (frame.header.samplerate != td.samplerate)
- {
- td.samplerate = frame.header.samplerate;
- rb->dsp_configure(td.dsp, DSP_SET_FREQUENCY,
- td.samplerate);
- }
-
- if (MAD_NCHANNELS(&frame.header) != td.nchannels)
- {
- td.nchannels = MAD_NCHANNELS(&frame.header);
- rb->dsp_configure(td.dsp, DSP_SET_STEREO_MODE,
- td.nchannels == 1 ?
- STEREO_MONO : STEREO_NONINTERLEAVED);
- }
-
- td.src.remcount = synth.pcm.length;
- td.src.pin[0] = synth.pcm.samples[0];
- td.src.pin[1] = synth.pcm.samples[1];
- td.src.proc_mask = 0;
-
- td.state = TSTATE_RENDER_WAIT;
-
- /* Add a frame of audio to the pcm buffer. Maximum is 1152 samples. */
- render_wait:
- rb->yield();
-
- while (1)
- {
- struct dsp_buffer dst;
- dst.remcount = 0;
- dst.bufcount = MAX(td.src.remcount, 1024);
-
- ssize_t size = dst.bufcount * 2 * sizeof(int16_t);
-
- /* Wait for required amount of free buffer space */
- while ((dst.p16out = pcm_output_get_buffer(&size)) == NULL)
- {
- /* Wait one frame */
- int timeout = dst.bufcount*HZ / td.samplerate;
- str_get_msg_w_tmo(&audio_str, &td.ev, MAX(timeout, 1));
- if (td.ev.id != SYS_TIMEOUT)
- goto message_process;
- }
-
- dst.bufcount = size / (2 * sizeof (int16_t));
- rb->dsp_process(td.dsp, &td.src, &dst);
-
- if (dst.remcount > 0)
- {
- /* Make this data available to DMA */
- pcm_output_commit_data(dst.remcount * 2 * sizeof(int16_t),
- audio_queue.curr->time);
-
- /* As long as we're on this timestamp, the time is just
- incremented by the number of samples */
- audio_queue.curr->time += dst.remcount;
- }
- else if (td.src.remcount <= 0)
- {
- break;
- }
- }
- } /* end decoding loop */
-}
-
-/* Initializes the audio thread resources and starts the thread */
-bool audio_thread_init(void)
-{
- /* Initialise the encoded audio buffer and its descriptors */
- audio_queue.start = mpeg_malloc(AUDIOBUF_ALLOC_SIZE,
- MPEG_ALLOC_AUDIOBUF);
- if (audio_queue.start == NULL)
- return false;
-
- /* Start the audio thread */
- audio_str.hdr.q = &audio_str_queue;
- rb->queue_init(audio_str.hdr.q, false);
-
- /* We steal the codec thread for audio */
- rb->codec_thread_do_callback(audio_thread, &audio_str.thread);
-
- rb->queue_enable_queue_send(audio_str.hdr.q, &audio_str_queue_send,
- audio_str.thread);
-
- /* Wait for thread to initialize */
- str_send_msg(&audio_str, STREAM_NULL, 0);
-
- return true;
-}
-
-/* Stops the audio thread */
-void audio_thread_exit(void)
-{
- if (audio_str.thread != 0)
- {
- str_post_msg(&audio_str, STREAM_QUIT, 0);
- rb->codec_thread_do_callback(NULL, NULL);
- audio_str.thread = 0;
- }
-}