summaryrefslogtreecommitdiffstats
path: root/apps/mpeg.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/mpeg.c')
-rw-r--r--apps/mpeg.c1979
1 files changed, 0 insertions, 1979 deletions
diff --git a/apps/mpeg.c b/apps/mpeg.c
deleted file mode 100644
index e04c227cb1..0000000000
--- a/apps/mpeg.c
+++ /dev/null
@@ -1,1979 +0,0 @@
-/***************************************************************************
- * __________ __ ___.
- * Open \______ \ ____ ____ | | _\_ |__ _______ ___
- * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
- * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
- * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
- * \/ \/ \/ \/ \/
- * $Id$
- *
- * Copyright (C) 2002 by Linus Nielsen Feltzing
- *
- * 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 <stdbool.h>
-#include <stdlib.h>
-#include "config.h"
-
-#if CONFIG_CODEC != SWCODEC
-
-#include "debug.h"
-#include "panic.h"
-#include "metadata.h"
-#include "mpeg.h"
-#include "audio.h"
-#include "storage.h"
-#include "string.h"
-#include <kernel.h>
-#include "thread.h"
-#include "errno.h"
-#include "mp3data.h"
-#include "core_alloc.h"
-#include "mp3_playback.h"
-#include "talk.h"
-#include "sound.h"
-#include "system.h"
-#include "appevents.h"
-#include "playlist.h"
-#include "cuesheet.h"
-#include "settings.h"
-#ifndef SIMULATOR
-#include "i2c.h"
-#include "system.h"
-#include "usb.h"
-#include "file.h"
-#include "hwcompat.h"
-#endif /* !SIMULATOR */
-#ifdef HAVE_LCD_BITMAP
-#include "lcd.h"
-#endif /* CONFIG_CODEC != SWCODEC */
-
-#define MPEG_SWAP_CHUNKSIZE 0x2000
-#define MPEG_HIGH_WATER 2 /* We leave 2 bytes empty because otherwise we
- wouldn't be able to see the difference between
- an empty buffer and a full one. */
-#define MPEG_LOW_WATER 0x60000
-#define MPEG_RECORDING_LOW_WATER 0x80000
-#define MPEG_LOW_WATER_CHUNKSIZE 0x40000
-#define MPEG_LOW_WATER_SWAP_CHUNKSIZE 0x10000
-#if (CONFIG_STORAGE & STORAGE_MMC)
-#define MPEG_PLAY_PENDING_THRESHOLD 0x20000
-#define MPEG_PLAY_PENDING_SWAPSIZE 0x20000
-#else
-#define MPEG_PLAY_PENDING_THRESHOLD 0x10000
-#define MPEG_PLAY_PENDING_SWAPSIZE 0x10000
-#endif
-
-#define MPEG_MAX_PRERECORD_SECONDS 30
-
-/* For ID3 info and VBR header */
-#define MPEG_RESERVED_HEADER_SPACE (4096 + 576)
-
-#ifndef SIMULATOR
-extern unsigned long mas_version_code;
-#endif
-
-#define MPEG_PLAY 1
-#define MPEG_STOP 2
-#define MPEG_PAUSE 3
-#define MPEG_RESUME 4
-#define MPEG_NEXT 5
-#define MPEG_PREV 6
-#define MPEG_FF_REWIND 7
-#define MPEG_FLUSH_RELOAD 8
-#define MPEG_RECORD 9
-#define MPEG_INIT_RECORDING 10
-#define MPEG_INIT_PLAYBACK 11
-#define MPEG_NEW_FILE 12
-#define MPEG_PAUSE_RECORDING 13
-#define MPEG_RESUME_RECORDING 14
-#define MPEG_NEED_DATA 100
-#define MPEG_TRACK_CHANGE 101
-#define MPEG_SAVE_DATA 102
-#define MPEG_STOP_DONE 103
-#define MPEG_PRERECORDING_TICK 104
-
-/* indicator for MPEG_NEED_DATA */
-#define GENERATE_UNBUFFER_EVENTS 1
-
-/* list of tracks in memory */
-#define MAX_TRACK_ENTRIES (1<<4) /* Must be power of 2 */
-#define MAX_TRACK_ENTRIES_MASK (MAX_TRACK_ENTRIES - 1)
-
-struct trackdata
-{
- struct mp3entry id3;
- int mempos;
- int load_ahead_index;
-};
-
-static struct trackdata trackdata[MAX_TRACK_ENTRIES];
-
-static unsigned int current_track_counter = 0;
-
-#ifndef SIMULATOR
-static void stop_playing(void);
-
-static int track_read_idx = 0;
-static int track_write_idx = 0;
-#endif /* !SIMULATOR */
-
-/* Cuesheet support */
-static struct cuesheet *curr_cuesheet = NULL;
-static bool checked_for_cuesheet = false;
-
-static const char mpeg_thread_name[] = "mpeg";
-static unsigned int audio_thread_id;
-static bool audio_is_initialized;
-static unsigned int mpeg_errno;
-
-static bool playing = false; /* We are playing an MP3 stream */
-static bool is_playing = false; /* We are (attempting to) playing MP3 files */
-static bool paused; /* playback is paused */
-static int audiobuf_handle; /* handle to the audio buffer */
-static char* mpeg_audiobuf; /* poiunter to the audio buffer */
-static long audiobuflen; /* length of the audio buffer */
-#define AUDIO_BUFFER_RESERVE (256*1024)
-#ifdef SIMULATOR
-static char mpeg_stack[DEFAULT_STACK_SIZE];
-static struct mp3entry taginfo;
-#else /* !SIMULATOR */
-static struct event_queue mpeg_queue SHAREDBSS_ATTR;
-static long mpeg_stack[(DEFAULT_STACK_SIZE + 0x1000)/sizeof(long)];
-
-static int audiobuf_write;
-static int audiobuf_swapwrite;
-static long audiobuf_read;
-
-static int mpeg_file;
-
-static bool play_pending; /* We are about to start playing */
-static bool play_pending_track_change; /* When starting play we're starting a new file */
-static bool filling; /* We are filling the buffer with data from disk */
-static bool dma_underrun; /* True when the DMA has stopped because of
- slow disk reading (read error, shaking) */
-static bool mpeg_stop_done;
-
-static int last_dma_tick = 0;
-static int last_dma_chunk_size;
-
-static long low_watermark; /* Dynamic low watermark level */
-static long low_watermark_margin = 0; /* Extra time in seconds for watermark */
-static long lowest_watermark_level; /* Debug value to observe the buffer
- usage */
-
-struct audio_resume_info
-{
- unsigned long elapsed;
- unsigned long offset;
-};
-
-#ifdef HAVE_RECORDING
-static const unsigned char empty_id3_header[] =
-{
- 'I', 'D', '3', 0x03, 0x00, 0x00,
- 0x00, 0x00, 0x1f, 0x76 /* Size is 4096 minus 10 bytes for the header */
-};
-#endif /* HAVE_RECORDING */
-
-
-static int get_unplayed_space(void);
-static int get_playable_space(void);
-static int get_unswapped_space(void);
-#endif /* !SIMULATOR */
-
-static void audio_reset_buffer_noalloc(void* buf, size_t bufsize);
-static void audio_reset_buffer(void);
-
-
-#ifndef SIMULATOR
-static int num_tracks_in_memory(void)
-{
- return (track_write_idx - track_read_idx) & MAX_TRACK_ENTRIES_MASK;
-}
-
-#ifdef DEBUG_TAGS
-static void debug_tags(void)
-{
- int i;
-
- for(i = 0;i < MAX_TRACK_ENTRIES;i++)
- {
- DEBUGF("%d - %s\n", i, trackdata[i].id3.path);
- }
- DEBUGF("read: %d, write :%d\n", track_read_idx, track_write_idx);
- DEBUGF("num_tracks_in_memory: %d\n", num_tracks_in_memory());
-}
-#else /* !DEBUG_TAGS */
-#define debug_tags()
-#endif /* !DEBUG_TAGS */
-
-static void remove_current_tag(void)
-{
- if(num_tracks_in_memory() > 0)
- {
- /* First move the index, so nobody tries to access the tag */
- track_read_idx = (track_read_idx+1) & MAX_TRACK_ENTRIES_MASK;
- checked_for_cuesheet = false;
- debug_tags();
- }
- else
- {
- DEBUGF("remove_current_tag: no tracks to remove\n");
- }
-}
-
-static void remove_all_non_current_tags(void)
-{
- track_write_idx = (track_read_idx+1) & MAX_TRACK_ENTRIES_MASK;
- debug_tags();
-}
-
-static void remove_all_tags(void)
-{
- track_write_idx = track_read_idx;
-
- debug_tags();
-}
-
-static struct trackdata *get_trackdata(int offset)
-{
- if(offset >= num_tracks_in_memory())
- return NULL;
- else
- return &trackdata[(track_read_idx + offset) & MAX_TRACK_ENTRIES_MASK];
-}
-#endif /* !SIMULATOR */
-
-/***********************************************************************/
-/* audio event handling */
-
-#define MAX_EVENT_HANDLERS 10
-struct event_handlers_table
-{
- AUDIO_EVENT_HANDLER handler;
- unsigned short mask;
-};
-static struct event_handlers_table event_handlers[MAX_EVENT_HANDLERS];
-static int event_handlers_count = 0;
-
-void audio_register_event_handler(AUDIO_EVENT_HANDLER handler, unsigned short mask)
-{
- if (event_handlers_count < MAX_EVENT_HANDLERS)
- {
- event_handlers[event_handlers_count].handler = handler;
- event_handlers[event_handlers_count].mask = mask;
- event_handlers_count++;
- }
-}
-
-/* dispatch calls each handler in the order registered and returns after some
- handler actually handles the event (the event is assumed to no longer be valid
- after this, due to the handler changing some condition); returns true if someone
- handled the event, which is expected to cause the caller to skip its own handling
- of the event */
-#ifndef SIMULATOR
-static bool audio_dispatch_event(unsigned short event, unsigned long data)
-{
- int i = 0;
- for(i=0; i < event_handlers_count; i++)
- {
- if ( event_handlers[i].mask & event )
- {
- int rc = event_handlers[i].handler(event, data);
- if ( rc == AUDIO_EVENT_RC_HANDLED )
- return true;
- }
- }
- return false;
-}
-
-static void send_track_event(unsigned int id, struct mp3entry *id3)
-{
- struct mp3entry *cur_id3 =
- &trackdata[track_read_idx & MAX_TRACK_ENTRIES_MASK].id3;
- unsigned int flags = id3 == cur_id3 ? TEF_CURRENT : 0;
- send_event(id, &(struct track_event){ .flags = flags, .id3 = id3 });
-}
-#endif /* SIMULATOR */
-
-/***********************************************************************/
-
-static void set_elapsed(struct mp3entry* id3)
-{
- if ( id3->vbr ) {
- if ( id3->has_toc ) {
- /* calculate elapsed time using TOC */
- int i;
- unsigned int remainder, plen, relpos, nextpos;
-
- /* find wich percent we're at */
- for (i=0; i<100; i++ )
- {
- if ( id3->offset < id3->toc[i] * (id3->filesize / 256) )
- {
- break;
- }
- }
-
- i--;
- if (i < 0)
- i = 0;
-
- relpos = id3->toc[i];
-
- if (i < 99)
- {
- nextpos = id3->toc[i+1];
- }
- else
- {
- nextpos = 256;
- }
-
- remainder = id3->offset - (relpos * (id3->filesize / 256));
-
- /* set time for this percent (divide before multiply to prevent
- overflow on long files. loss of precision is negligible on
- short files) */
- id3->elapsed = i * (id3->length / 100);
-
- /* calculate remainder time */
- plen = (nextpos - relpos) * (id3->filesize / 256);
- id3->elapsed += (((remainder * 100) / plen) *
- (id3->length / 10000));
- }
- else {
- /* no TOC exists. set a rough estimate using average bitrate */
- int tpk = id3->length / (id3->filesize / 1024);
- id3->elapsed = id3->offset / 1024 * tpk;
- }
- }
- else
- /* constant bitrate, use exact calculation */
- id3->elapsed = id3->offset / (id3->bitrate / 8);
-}
-
-static int audio_get_file_pos_int(struct mp3entry *id3)
-{
- int pos = -1;
-
- if (id3->vbr)
- {
- if (id3->has_toc)
- {
- /* Use the TOC to find the new position */
- unsigned int percent, remainder;
- int curtoc, nexttoc, plen;
-
- percent = (id3->elapsed*100)/id3->length;
- if (percent > 99)
- percent = 99;
-
- curtoc = id3->toc[percent];
-
- if (percent < 99)
- nexttoc = id3->toc[percent+1];
- else
- nexttoc = 256;
-
- pos = (id3->filesize/256)*curtoc;
-
- /* Use the remainder to get a more accurate position */
- remainder = (id3->elapsed*100)%id3->length;
- remainder = (remainder*100)/id3->length;
- plen = (nexttoc - curtoc)*(id3->filesize/256);
- pos += (plen/100)*remainder;
- }
- else
- {
- /* No TOC exists, estimate the new position */
- pos = (id3->filesize / (id3->length / 1000)) *
- (id3->elapsed / 1000);
- }
- }
- else if (id3->bitrate)
- pos = id3->elapsed * (id3->bitrate / 8);
- else
- {
- return -1;
- }
-
- if (pos >= (int)(id3->filesize - id3->id3v1len))
- {
- /* Don't seek right to the end of the file so that we can
- transition properly to the next song */
- pos = id3->filesize - id3->id3v1len - 1;
- }
- else if (pos < (int)id3->first_frame_offset)
- {
- /* skip past id3v2 tag and other leading garbage */
- pos = id3->first_frame_offset;
- }
- return pos;
-}
-
-int audio_get_file_pos(void)
-{
- struct mp3entry *id3 = audio_current_track();
- return id3 ? audio_get_file_pos_int(id3) : 0;
-}
-
-unsigned long mpeg_get_last_header(void)
-{
-#ifdef SIMULATOR
- return 0;
-#else /* !SIMULATOR */
- unsigned long tmp[2];
-
- /* Read the frame data from the MAS and reconstruct it with the
- frame sync and all */
- mas_readmem(MAS_BANK_D0, MAS_D0_MPEG_STATUS_1, tmp, 2);
- return 0xffe00000 | ((tmp[0] & 0x7c00) << 6) | (tmp[1] & 0xffff);
-#endif /* !SIMULATOR */
-}
-
-static void do_stop(void)
-{
- is_playing = false;
- paused = false;
-
-#ifndef SIMULATOR
- if (playing)
- playlist_update_resume_info(audio_current_track());
-
- stop_playing();
- mpeg_stop_done = true;
-#else
- playing = false;
-#endif
-}
-
-/* Buffer must not move. */
-static int shrink_callback(int handle, unsigned hints, void* start, size_t old_size)
-{
- ssize_t extradata_size = old_size - audiobuflen;
- /* check what buflib requests */
- size_t wanted_size = (hints & BUFLIB_SHRINK_SIZE_MASK);
- ssize_t size = (ssize_t)old_size - wanted_size;
-
- /* keep at least 256K for the buffering */
- if ((size - extradata_size) < AUDIO_BUFFER_RESERVE)
- {
- /* check if buflib needs the memory really hard. if yes we give
- * up playback for now, otherwise refuse to shrink to keep at least
- * 256K for the buffering */
- if ((hints & BUFLIB_SHRINK_POS_MASK) != BUFLIB_SHRINK_POS_MASK)
- return BUFLIB_CB_CANNOT_SHRINK;
- }
- /* TODO: Do it without stopping playback, if possible */
- bool playing = (audio_status() & AUDIO_STATUS_PLAY) == AUDIO_STATUS_PLAY;
- struct mp3entry *id3 = audio_current_track();
- unsigned long elapsed = 0, offset = 0;
- if (id3)
- {
- elapsed = id3->elapsed;
- offset = id3->offset;
- }
- /* don't call audio_hard_stop() as it frees this handle */
- if (thread_self() == audio_thread_id)
- { /* inline case MPEG_STOP (audio_stop()) response
- * if we're in the audio thread since audio_stop() otherwise deadlocks */
- do_stop();
- }
- else
- audio_stop();
-
- switch (hints & BUFLIB_SHRINK_POS_MASK)
- {
- case BUFLIB_SHRINK_POS_BACK:
- core_shrink(handle, start, size);
- audio_reset_buffer_noalloc(start, size);
- break;
- case BUFLIB_SHRINK_POS_FRONT:
- core_shrink(handle, start + wanted_size, size);
- audio_reset_buffer_noalloc(start + wanted_size, size);
- break;
- case BUFLIB_SHRINK_POS_MASK:
- audiobuf_handle = core_free(audiobuf_handle);
- mpeg_audiobuf = NULL;
- talk_buffer_set_policy(TALK_BUFFER_DEFAULT);
- playing = false;
- break;
- }
- if (playing)
- { /* safe to call even from the audio thread (due to queue_post()) */
- audio_play(elapsed, offset);
- }
-
- return BUFLIB_CB_OK;
-}
-
-static struct buflib_callbacks ops = {
- .move_callback = NULL,
- .shrink_callback = shrink_callback,
-};
-
-#ifndef SIMULATOR
-/* Send callback events to notify about removing old tracks. */
-static void generate_unbuffer_events(void)
-{
- int i;
- int numentries = MAX_TRACK_ENTRIES - num_tracks_in_memory();
- int cur_idx = track_write_idx;
-
- for (i = 0; i < numentries; i++)
- {
- /* Send an event to notify that track has finished. */
- send_track_event(PLAYBACK_EVENT_TRACK_FINISH, &trackdata[cur_idx].id3);
- cur_idx = (cur_idx + 1) & MAX_TRACK_ENTRIES_MASK;
- }
-}
-
-/* Send callback events to notify about new tracks. */
-static void generate_postbuffer_events(void)
-{
- int i;
- int numentries = num_tracks_in_memory();
- int cur_idx = track_read_idx;
-
- for (i = 0; i < numentries; i++)
- {
- send_track_event(PLAYBACK_EVENT_TRACK_BUFFER, &trackdata[cur_idx].id3);
- cur_idx = (cur_idx + 1) & MAX_TRACK_ENTRIES_MASK;
- }
-}
-
-static void recalculate_watermark(int bitrate)
-{
- int bytes_per_sec;
- int time = storage_spinup_time();
-
- /* A bitrate of 0 probably means empty VBR header. We play safe
- and set a high threshold */
- if(bitrate == 0)
- bitrate = 320;
-
- bytes_per_sec = bitrate * 1000 / 8;
-
- if(time)
- {
- /* No drive spins up faster than 3.5s */
- if(time < 350)
- time = 350;
-
- time = time * 3;
- low_watermark = ((low_watermark_margin * HZ + time) *
- bytes_per_sec) / HZ;
- }
- else
- {
- low_watermark = MPEG_LOW_WATER;
- }
-}
-
-#ifdef HAVE_DISK_STORAGE
-void audio_set_buffer_margin(int setting)
-{
- low_watermark_margin = setting; /* in seconds */
-}
-#endif
-
-void audio_get_debugdata(struct audio_debug *dbgdata)
-{
- dbgdata->audiobuflen = audiobuflen;
- dbgdata->audiobuf_write = audiobuf_write;
- dbgdata->audiobuf_swapwrite = audiobuf_swapwrite;
- dbgdata->audiobuf_read = audiobuf_read;
-
- dbgdata->last_dma_chunk_size = last_dma_chunk_size;
-
- dbgdata->playing = playing;
- dbgdata->play_pending = play_pending;
- dbgdata->is_playing = is_playing;
- dbgdata->filling = filling;
- dbgdata->dma_underrun = dma_underrun;
-
- dbgdata->unplayed_space = get_unplayed_space();
- dbgdata->playable_space = get_playable_space();
- dbgdata->unswapped_space = get_unswapped_space();
-
- dbgdata->low_watermark_level = low_watermark;
- dbgdata->lowest_watermark_level = lowest_watermark_level;
-}
-
-#ifdef DEBUG
-static void dbg_timer_start(void)
-{
- /* We are using timer 2 */
-
- TSTR &= ~0x04; /* Stop the timer */
- TSNC &= ~0x04; /* No synchronization */
- TMDR &= ~0x44; /* Operate normally */
-
- TCNT2 = 0; /* Start counting at 0 */
- TCR2 = 0x03; /* Sysclock/8 */
-
- TSTR |= 0x04; /* Start timer 2 */
-}
-
-static int dbg_cnt2us(unsigned int cnt)
-{
- return (cnt * 10000) / (FREQ/800);
-}
-#endif /* DEBUG */
-
-static int get_unplayed_space(void)
-{
- int space = audiobuf_write - audiobuf_read;
- if (space < 0)
- space += audiobuflen;
- return space;
-}
-
-static int get_playable_space(void)
-{
- int space = audiobuf_swapwrite - audiobuf_read;
- if (space < 0)
- space += audiobuflen;
- return space;
-}
-
-static int get_unplayed_space_current_song(void)
-{
- int space;
-
- if (num_tracks_in_memory() > 1)
- {
- space = get_trackdata(1)->mempos - audiobuf_read;
- }
- else
- {
- space = audiobuf_write - audiobuf_read;
- }
-
- if (space < 0)
- space += audiobuflen;
-
- return space;
-}
-
-static int get_unswapped_space(void)
-{
- int space = audiobuf_write - audiobuf_swapwrite;
- if (space < 0)
- space += audiobuflen;
- return space;
-}
-
-void playback_tick(void)
-{
- struct trackdata *ptd = get_trackdata(0);
- if(ptd)
- {
- ptd->id3.elapsed += (current_tick - last_dma_tick) * 1000 / HZ;
- last_dma_tick = current_tick;
- audio_dispatch_event(AUDIO_EVENT_POS_REPORT,
- (unsigned long)ptd->id3.elapsed);
- }
-}
-
-static void reset_mp3_buffer(void)
-{
- audiobuf_read = 0;
- audiobuf_write = 0;
- audiobuf_swapwrite = 0;
- lowest_watermark_level = audiobuflen;
-}
-
- /* DMA transfer end interrupt callback */
-static void transfer_end(const void** ppbuf, size_t* psize)
-{
- if(playing && !paused)
- {
- int unplayed_space_left;
- int space_until_end_of_buffer;
- int track_offset = 1;
- struct trackdata *track;
-
- audiobuf_read += last_dma_chunk_size;
- if(audiobuf_read >= audiobuflen)
- audiobuf_read = 0;
-
- /* First, check if we are on a track boundary */
- if (num_tracks_in_memory() > 1)
- {
- if (audiobuf_read == get_trackdata(track_offset)->mempos)
- {
- if ( ! audio_dispatch_event(AUDIO_EVENT_END_OF_TRACK, 0) )
- {
- queue_post(&mpeg_queue, MPEG_TRACK_CHANGE, 0);
- track_offset++;
- }
- }
- }
-
- unplayed_space_left = get_unplayed_space();
-
- space_until_end_of_buffer = audiobuflen - audiobuf_read;
-
- if(!filling && unplayed_space_left < low_watermark)
- {
- filling = true;
- queue_post(&mpeg_queue, MPEG_NEED_DATA, GENERATE_UNBUFFER_EVENTS);
- }
-
- if(unplayed_space_left)
- {
- last_dma_chunk_size = MIN(0x2000, unplayed_space_left);
- last_dma_chunk_size = MIN(last_dma_chunk_size,
- space_until_end_of_buffer);
-
- /* several tracks loaded? */
- track = get_trackdata(track_offset);
- if(track)
- {
- /* will we move across the track boundary? */
- if (( audiobuf_read < track->mempos ) &&
- ((audiobuf_read+last_dma_chunk_size) >
- track->mempos ))
- {
- /* Make sure that we end exactly on the boundary */
- last_dma_chunk_size = track->mempos - audiobuf_read;
- }
- }
-
- *psize = last_dma_chunk_size & 0xffff;
- *ppbuf = mpeg_audiobuf + audiobuf_read;
- track = get_trackdata(0);
- if(track)
- track->id3.offset += last_dma_chunk_size;
-
- /* Update the watermark debug level */
- if(unplayed_space_left < lowest_watermark_level)
- lowest_watermark_level = unplayed_space_left;
- }
- else
- {
- /* Check if the end of data is because of a hard disk error.
- If there is an open file handle, we are still playing music.
- If not, the last file has been loaded, and the file handle is
- closed. */
- if(mpeg_file >= 0)
- {
- /* Update the watermark debug level */
- if(unplayed_space_left < lowest_watermark_level)
- lowest_watermark_level = unplayed_space_left;
-
- DEBUGF("DMA underrun.\n");
- dma_underrun = true;
- }
- else
- {
- if ( ! audio_dispatch_event(AUDIO_EVENT_END_OF_TRACK, 0) )
- {
- DEBUGF("No more MP3 data. Stopping.\n");
- queue_post(&mpeg_queue, MPEG_TRACK_CHANGE, 0);
- playing = false;
- }
- }
- *psize = 0; /* no more transfer */
- }
- }
-}
-
-static struct trackdata *add_track_to_tag_list(const char *filename)
-{
- struct trackdata *track;
- bool send_nid3_event;
-
- if(num_tracks_in_memory() >= MAX_TRACK_ENTRIES)
- {
- DEBUGF("Tag memory is full\n");
- return NULL;
- }
-
- track = &trackdata[track_write_idx];
-
- /* grab id3 tag of new file and
- remember where in memory it starts */
- if(mp3info(&track->id3, filename))
- {
- DEBUGF("Bad mp3\n");
- return NULL;
- }
- track->mempos = audiobuf_write;
- track->id3.elapsed = 0;
-#ifdef HAVE_LCD_BITMAP
- if (track->id3.title)
- lcd_getstringsize(track->id3.title, NULL, NULL);
- if (track->id3.artist)
- lcd_getstringsize(track->id3.artist, NULL, NULL);
- if (track->id3.album)
- lcd_getstringsize(track->id3.album, NULL, NULL);
-#endif
-
- /* if this track is the next track then let the UI know it can get it */
- send_nid3_event = (track_write_idx == track_read_idx + 1);
- track_write_idx = (track_write_idx+1) & MAX_TRACK_ENTRIES_MASK;
- if (send_nid3_event)
- send_track_event(PLAYBACK_EVENT_NEXTTRACKID3_AVAILABLE, &track->id3);
- debug_tags();
- return track;
-}
-
-static int new_file(int steps)
-{
- int max_steps = playlist_amount();
- int start = 0;
- int i;
- struct trackdata *track;
- char name_buf[MAX_PATH+1];
- const char *trackname;
-
- /* Find out how many steps to advance. The load_ahead_index field tells
- us how many playlist entries it had to skip to get to a valid one.
- We add those together to find out where to start. */
- if(steps > 0 && num_tracks_in_memory() > 1)
- {
- /* Begin with the song after the currently playing one */
- i = 1;
- while((track = get_trackdata(i++)))
- {
- start += track->load_ahead_index;
- }
- }
-
- do {
- trackname = playlist_peek(start + steps, name_buf, sizeof(name_buf));
- if ( !trackname )
- return -1;
-
- DEBUGF("Loading %s\n", trackname);
-
- mpeg_file = open(trackname, O_RDONLY);
- if(mpeg_file < 0) {
- DEBUGF("Couldn't open file: %s\n",trackname);
- if(steps < 0)
- steps--;
- else
- steps++;
- }
- else
- {
- struct trackdata *track = add_track_to_tag_list(trackname);
-
- if(!track)
- {
- /* Bad mp3 file */
- if(steps < 0)
- steps--;
- else
- steps++;
- close(mpeg_file);
- mpeg_file = -1;
- }
- else
- {
- /* skip past id3v2 tag */
- lseek(mpeg_file,
- track->id3.first_frame_offset,
- SEEK_SET);
- track->id3.index = steps;
- track->load_ahead_index = steps;
- track->id3.offset = 0;
-
- if(track->id3.vbr)
- /* Average bitrate * 1.5 */
- recalculate_watermark(
- (track->id3.bitrate * 3) / 2);
- else
- recalculate_watermark(
- track->id3.bitrate);
-
- }
- }
-
- /* Bail out if no file could be opened */
- if(abs(steps) > max_steps)
- return -1;
- } while ( mpeg_file < 0 );
-
- return 0;
-}
-
-static void stop_playing(void)
-{
- /* Stop the current stream */
- mp3_play_stop();
- playing = false;
- filling = false;
-
- if(mpeg_file >= 0)
- close(mpeg_file);
- mpeg_file = -1;
- remove_all_tags();
- generate_unbuffer_events();
- reset_mp3_buffer();
-}
-
-static void end_current_track(void)
-{
- play_pending = false;
- playing = false;
- mp3_play_pause(false);
-
- reset_mp3_buffer();
- remove_all_tags();
- generate_unbuffer_events();
-
- if(mpeg_file >= 0)
- close(mpeg_file);
-}
-
-/* Is this a really the end of playback or is a new playlist starting */
-static void check_playlist_end(int direction)
-{
- /* Use the largest possible step size to account for skipped tracks */
- int steps = playlist_amount();
-
- if (direction < 0)
- steps = -steps;
-
- if (playlist_next(steps) < 0)
- is_playing = false;
-}
-
-static void update_playlist(void)
-{
- if (num_tracks_in_memory() > 0)
- {
- struct trackdata *track = get_trackdata(0);
- track->id3.index = playlist_next(track->id3.index);
- }
- else
- {
- /* End of playlist? */
- check_playlist_end(1);
- }
-
- playlist_update_resume_info(audio_current_track());
-}
-
-static void track_change(void)
-{
- DEBUGF("Track change\n");
-
- if (num_tracks_in_memory() > 0)
- {
- remove_current_tag();
- update_playlist();
- if (is_playing)
- {
- send_track_event(PLAYBACK_EVENT_TRACK_CHANGE,
- audio_current_track());
- }
- }
-
- current_track_counter++;
-}
-
-#ifdef DEBUG
-void hexdump(const unsigned char *buf, int len)
-{
- int i;
-
- for(i = 0;i < len;i++)
- {
- if(i && (i & 15) == 0)
- {
- DEBUGF("\n");
- }
- DEBUGF("%02x ", buf[i]);
- }
- DEBUGF("\n");
-}
-#endif /* DEBUG */
-
-static void start_playback_if_ready(void)
-{
- int playable_space;
-
- playable_space = audiobuf_swapwrite - audiobuf_read;
- if(playable_space < 0)
- playable_space += audiobuflen;
-
- /* See if we have started playing yet. If not, do it. */
- if(play_pending || dma_underrun)
- {
- /* If the filling has stopped, and we still haven't reached
- the watermark, the file must be smaller than the
- watermark. We must still play it. */
- if((playable_space >= MPEG_PLAY_PENDING_THRESHOLD) ||
- !filling || dma_underrun)
- {
- DEBUGF("P\n");
- if (play_pending) /* don't do this when recovering from DMA underrun */
- {
- generate_postbuffer_events(); /* signal first track as buffered */
- if (play_pending_track_change)
- {
- play_pending_track_change = false;
- send_track_event(PLAYBACK_EVENT_TRACK_CHANGE,
- audio_current_track());
- }
- play_pending = false;
- }
- playing = true;
-
- last_dma_chunk_size = MIN(0x2000, get_unplayed_space_current_song());
- mp3_play_data(mpeg_audiobuf + audiobuf_read, last_dma_chunk_size, transfer_end);
- dma_underrun = false;
-
- if (!paused)
- {
- last_dma_tick = current_tick;
- mp3_play_pause(true);
- }
-
- /* Tell ourselves that we need more data */
- queue_post(&mpeg_queue, MPEG_NEED_DATA, 0);
- }
- }
-}
-
-static bool swap_one_chunk(void)
-{
- int free_space_left;
- int amount_to_swap;
-
- free_space_left = get_unswapped_space();
-
- if(free_space_left == 0 && !play_pending)
- return false;
-
- /* Swap in larger chunks when the user is waiting for the playback
- to start, or when there is dangerously little playable data left */
- if(play_pending)
- amount_to_swap = MIN(MPEG_PLAY_PENDING_SWAPSIZE, free_space_left);
- else
- {
- if(get_playable_space() < low_watermark)
- amount_to_swap = MIN(MPEG_LOW_WATER_SWAP_CHUNKSIZE,
- free_space_left);
- else
- amount_to_swap = MIN(MPEG_SWAP_CHUNKSIZE, free_space_left);
- }
-
- if(audiobuf_write < audiobuf_swapwrite)
- amount_to_swap = MIN(audiobuflen - audiobuf_swapwrite,
- amount_to_swap);
- else
- amount_to_swap = MIN(audiobuf_write - audiobuf_swapwrite,
- amount_to_swap);
-
- bitswap(mpeg_audiobuf + audiobuf_swapwrite, amount_to_swap);
-
- audiobuf_swapwrite += amount_to_swap;
- if(audiobuf_swapwrite >= audiobuflen)
- {
- audiobuf_swapwrite = 0;
- }
-
- return true;
-}
-
-static void mpeg_thread(void)
-{
- static int pause_tick = 0;
- static unsigned int pause_track = 0;
- struct queue_event ev;
- int len;
- int free_space_left;
- int unplayed_space_left;
- int amount_to_read;
- int t1, t2;
- unsigned long start_elapsed, start_offset;
-
- is_playing = false;
- play_pending = false;
- playing = false;
- mpeg_file = -1;
-
- while(1)
- {
- yield();
-
- /* Swap if necessary, and don't block on the queue_wait() */
- if(swap_one_chunk())
- {
- queue_wait_w_tmo(&mpeg_queue, &ev, 0);
- }
- else if (playing)
- {
- /* periodically update resume info */
- queue_wait_w_tmo(&mpeg_queue, &ev, HZ/2);
- }
- else
- {
- DEBUGF("S R:%x W:%x SW:%x\n",
- audiobuf_read, audiobuf_write, audiobuf_swapwrite);
- queue_wait(&mpeg_queue, &ev);
- }
-
- start_playback_if_ready();
-
- switch(ev.id)
- {
- case MPEG_PLAY:
- DEBUGF("MPEG_PLAY\n");
-
-#if CONFIG_TUNER
- /* Silence the A/D input, it may be on because the radio
- may be playing */
- mas_codec_writereg(6, 0x0000);
-#endif /* CONFIG_TUNER */
-
- /* Stop the current stream */
- paused = false;
- end_current_track();
-
- if ( new_file(0) == -1 )
- {
- is_playing = false;
- track_change();
- break;
- }
-
- start_elapsed = ((struct audio_resume_info *)ev.data)->elapsed;
- start_offset = ((struct audio_resume_info *)ev.data)->offset;
-
- /* mid-song resume? */
- if (!start_offset && start_elapsed) {
- struct mp3entry *id3 = &get_trackdata(0)->id3;
- id3->elapsed = start_elapsed;
- start_offset = audio_get_file_pos_int(id3);
- }
-
- if (start_offset) {
- struct mp3entry* id3 = &get_trackdata(0)->id3;
- lseek(mpeg_file, start_offset, SEEK_SET);
- id3->offset = start_offset;
- set_elapsed(id3);
- }
- else {
- /* skip past id3v2 tag */
- lseek(mpeg_file,
- get_trackdata(0)->id3.first_frame_offset,
- SEEK_SET);
-
- }
-
- /* Make it read more data */
- filling = true;
- queue_post(&mpeg_queue, MPEG_NEED_DATA, 0);
-
- /* Tell the file loading code that we want to start playing
- as soon as we have some data */
- play_pending = true;
- play_pending_track_change = true;
-
- update_playlist();
- current_track_counter++;
- break;
-
- case MPEG_STOP:
- do_stop();
- break;
-
- case MPEG_PAUSE:
- DEBUGF("MPEG_PAUSE\n");
- /* Stop the current stream */
- if (playing)
- playlist_update_resume_info(audio_current_track());
- paused = true;
- playing = false;
- pause_tick = current_tick;
- pause_track = current_track_counter;
- mp3_play_pause(false);
- break;
-
- case MPEG_RESUME:
- DEBUGF("MPEG_RESUME\n");
- /* Continue the current stream */
- paused = false;
- if (!play_pending)
- {
- playing = true;
- if ( current_track_counter == pause_track )
- last_dma_tick += current_tick - pause_tick;
- else
- last_dma_tick = current_tick;
- pause_tick = 0;
- mp3_play_pause(true);
- }
- break;
-
- case MPEG_NEXT:
- DEBUGF("MPEG_NEXT\n");
- /* is next track in ram? */
- if ( num_tracks_in_memory() > 1 ) {
- int unplayed_space_left, unswapped_space_left;
-
- /* stop the current stream */
- play_pending = false;
- playing = false;
- mp3_play_pause(false);
-
- track_change();
- audiobuf_read = get_trackdata(0)->mempos;
- last_dma_chunk_size = MIN(0x2000, get_unplayed_space_current_song());
- mp3_play_data(mpeg_audiobuf + audiobuf_read, last_dma_chunk_size, transfer_end);
- dma_underrun = false;
- last_dma_tick = current_tick;
-
- unplayed_space_left = get_unplayed_space();
- unswapped_space_left = get_unswapped_space();
-
- /* should we start reading more data? */
- if(!filling && (unplayed_space_left < low_watermark)) {
- filling = true;
- queue_post(&mpeg_queue, MPEG_NEED_DATA, GENERATE_UNBUFFER_EVENTS);
- play_pending = true;
- } else if(unswapped_space_left &&
- unswapped_space_left > unplayed_space_left) {
- /* Stop swapping the data from the previous file */
- audiobuf_swapwrite = audiobuf_read;
- play_pending = true;
- } else {
- playing = true;
- if (!paused)
- mp3_play_pause(true);
- }
- }
- else {
- if (!playlist_check(1))
- break;
-
- /* stop the current stream */
- end_current_track();
-
- if (new_file(1) < 0) {
- DEBUGF("No more files to play\n");
- filling = false;
-
- check_playlist_end(1);
- current_track_counter++;
- } else {
- /* Make it read more data */
- filling = true;
- queue_post(&mpeg_queue, MPEG_NEED_DATA, 0);
-
- /* Tell the file loading code that we want
- to start playing as soon as we have some data */
- play_pending = true;
- play_pending_track_change = true;
-
- update_playlist();
- current_track_counter++;
- }
- }
- break;
-
- case MPEG_PREV: {
- DEBUGF("MPEG_PREV\n");
-
- if (!playlist_check(-1))
- break;
-
- /* stop the current stream */
- end_current_track();
-
- /* Open the next file */
- if (new_file(-1) < 0) {
- DEBUGF("No more files to play\n");
- filling = false;
-
- check_playlist_end(-1);
- current_track_counter++;
- } else {
- /* Make it read more data */
- filling = true;
- queue_post(&mpeg_queue, MPEG_NEED_DATA, 0);
-
- /* Tell the file loading code that we want to
- start playing as soon as we have some data */
- play_pending = true;
- play_pending_track_change = true;
-
- update_playlist();
- current_track_counter++;
- }
- break;
- }
-
- case MPEG_FF_REWIND: {
- struct mp3entry *id3 = audio_current_track();
- unsigned int oldtime = id3->elapsed;
- unsigned int newtime = (unsigned int)ev.data;
- int curpos, newpos, diffpos;
- DEBUGF("MPEG_FF_REWIND\n");
-
- id3->elapsed = newtime;
-
- newpos = audio_get_file_pos_int(id3);
- if(newpos < 0)
- {
- id3->elapsed = oldtime;
- break;
- }
-
- if (mpeg_file >= 0)
- curpos = lseek(mpeg_file, 0, SEEK_CUR);
- else
- curpos = id3->filesize;
-
- if (num_tracks_in_memory() > 1)
- {
- /* We have started loading other tracks that need to be
- accounted for */
- struct trackdata *track;
- int i = 0;
-
- while((track = get_trackdata(i++)))
- {
- curpos += track->id3.filesize;
- }
- }
-
- diffpos = curpos - newpos;
-
- if(!filling && diffpos >= 0 && diffpos < audiobuflen)
- {
- int unplayed_space_left, unswapped_space_left;
-
- /* We are changing to a position that's already in
- memory, so we just move the DMA read pointer. */
- audiobuf_read = audiobuf_write - diffpos;
- if (audiobuf_read < 0)
- {
- audiobuf_read += audiobuflen;
- }
-
- unplayed_space_left = get_unplayed_space();
- unswapped_space_left = get_unswapped_space();
-
- /* If unswapped_space_left is larger than
- unplayed_space_left, it means that the swapwrite pointer
- hasn't yet advanced up to the new location of the read
- pointer. We just move it, there is no need to swap
- data that won't be played anyway. */
-
- if (unswapped_space_left > unplayed_space_left)
- {
- DEBUGF("Moved swapwrite\n");
- audiobuf_swapwrite = audiobuf_read;
- play_pending = true;
- }
-
- if (mpeg_file>=0 && unplayed_space_left < low_watermark)
- {
- /* We need to load more data before starting */
- filling = true;
- queue_post(&mpeg_queue, MPEG_NEED_DATA, GENERATE_UNBUFFER_EVENTS);
- play_pending = true;
- }
- else
- {
- /* resume will start at new position */
- last_dma_chunk_size =
- MIN(0x2000, get_unplayed_space_current_song());
- mp3_play_data(mpeg_audiobuf + audiobuf_read,
- last_dma_chunk_size, transfer_end);
- dma_underrun = false;
- }
- }
- else
- {
- /* Move to the new position in the file and start
- loading data */
- reset_mp3_buffer();
-
- if (num_tracks_in_memory() > 1)
- {
- /* We have to reload the current track */
- close(mpeg_file);
- remove_all_non_current_tags();
- generate_unbuffer_events();
- mpeg_file = -1;
- }
-
- if (mpeg_file < 0)
- {
- mpeg_file = open(id3->path, O_RDONLY);
- if (mpeg_file < 0)
- {
- id3->elapsed = oldtime;
- break;
- }
- }
-
- if(-1 == lseek(mpeg_file, newpos, SEEK_SET))
- {
- id3->elapsed = oldtime;
- break;
- }
-
- filling = true;
- queue_post(&mpeg_queue, MPEG_NEED_DATA, 0);
-
- /* Tell the file loading code that we want to start playing
- as soon as we have some data */
- play_pending = true;
- }
-
- id3->offset = newpos;
-
- break;
- }
-
- case MPEG_FLUSH_RELOAD: {
- int numtracks = num_tracks_in_memory();
- bool reload_track = false;
-
- if (numtracks > 1)
- {
- /* Reset the buffer */
- audiobuf_write = get_trackdata(1)->mempos;
-
- /* Reset swapwrite unless we're still swapping current
- track */
- if (get_unplayed_space() <= get_playable_space())
- audiobuf_swapwrite = audiobuf_write;
-
- close(mpeg_file);
- remove_all_non_current_tags();
- generate_unbuffer_events();
- mpeg_file = -1;
- reload_track = true;
- }
- else if (numtracks == 1 && mpeg_file < 0)
- {
- reload_track = true;
- }
-
- if(reload_track && new_file(1) >= 0)
- {
- /* Tell ourselves that we want more data */
- filling = true;
- queue_post(&mpeg_queue, MPEG_NEED_DATA, 0);
- }
-
- break;
- }
-
- case MPEG_NEED_DATA:
- free_space_left = audiobuf_read - audiobuf_write;
-
- /* We interpret 0 as "empty buffer" */
- if(free_space_left <= 0)
- free_space_left += audiobuflen;
-
- unplayed_space_left = audiobuflen - free_space_left;
-
- /* Make sure that we don't fill the entire buffer */
- free_space_left -= MPEG_HIGH_WATER;
-
- if (ev.data == GENERATE_UNBUFFER_EVENTS)
- generate_unbuffer_events();
-
- /* do we have any more buffer space to fill? */
- if(free_space_left <= 0)
- {
- DEBUGF("0\n");
- filling = false;
- generate_postbuffer_events();
- storage_sleep();
- break;
- }
-
- /* Read small chunks while we are below the low water mark */
- if(unplayed_space_left < low_watermark)
- amount_to_read = MIN(MPEG_LOW_WATER_CHUNKSIZE,
- free_space_left);
- else
- amount_to_read = free_space_left;
-
- /* Don't read more than until the end of the buffer */
- amount_to_read = MIN(audiobuflen - audiobuf_write,
- amount_to_read);
-#if (CONFIG_STORAGE & STORAGE_MMC)
- /* MMC is slow, so don't read too large chunks */
- amount_to_read = MIN(0x40000, amount_to_read);
-#elif MEMORYSIZE == 8
- amount_to_read = MIN(0x100000, amount_to_read);
-#endif
-
- /* Read as much mpeg data as we can fit in the buffer */
- if(mpeg_file >= 0)
- {
- DEBUGF("R\n");
- t1 = current_tick;
- len = read(mpeg_file, mpeg_audiobuf + audiobuf_write,
- amount_to_read);
- if(len > 0)
- {
- t2 = current_tick;
- DEBUGF("time: %d\n", t2 - t1);
- DEBUGF("R: %x\n", len);
-
- /* Now make sure that we don't feed the MAS with ID3V1
- data */
- if (len < amount_to_read)
- {
- int i;
- static const unsigned char tag[] = "TAG";
- int taglen = 128;
- int tagptr = audiobuf_write + len - 128;
-
- /* Really rare case: entire potential tag wasn't
- read in this call AND audiobuf_write < 128 */
- if (tagptr < 0)
- tagptr += audiobuflen;
-
- for(i = 0;i < 3;i++)
- {
- if(tagptr >= audiobuflen)
- tagptr -= audiobuflen;
-
- if(mpeg_audiobuf[tagptr] != tag[i])
- {
- taglen = 0;
- break;
- }
-
- tagptr++;
- }
-
- if(taglen)
- {
- /* Skip id3v1 tag */
- DEBUGF("Skipping ID3v1 tag\n");
- len -= taglen;
-
- /* In the very rare case when the entire tag
- wasn't read in this read() len will be < 0.
- Take care of this when changing the write
- pointer. */
- }
- }
-
- audiobuf_write += len;
-
- if (audiobuf_write < 0)
- audiobuf_write += audiobuflen;
-
- if(audiobuf_write >= audiobuflen)
- {
- audiobuf_write = 0;
- DEBUGF("W\n");
- }
-
- /* Tell ourselves that we want more data */
- queue_post(&mpeg_queue, MPEG_NEED_DATA, 0);
- }
- else
- {
- if(len < 0)
- {
- DEBUGF("MPEG read error\n");
- }
-
- close(mpeg_file);
- mpeg_file = -1;
-
- if(new_file(1) < 0)
- {
- /* No more data to play */
- DEBUGF("No more files to play\n");
- filling = false;
- }
- else
- {
- /* Tell ourselves that we want more data */
- queue_post(&mpeg_queue, MPEG_NEED_DATA, 0);
- }
- }
- }
- break;
-
- case MPEG_TRACK_CHANGE:
- track_change();
- break;
-
-#ifndef USB_NONE
- case SYS_USB_CONNECTED:
- is_playing = false;
- paused = false;
- stop_playing();
-
- /* Tell the USB thread that we are safe */
- DEBUGF("mpeg_thread got SYS_USB_CONNECTED\n");
- usb_acknowledge(SYS_USB_CONNECTED_ACK);
-
- /* Wait until the USB cable is extracted again */
- usb_wait_for_disconnect(&mpeg_queue);
- break;
-#endif /* !USB_NONE */
-
- case SYS_TIMEOUT:
- if (playing)
- playlist_update_resume_info(audio_current_track());
- break;
- }
- }
-}
-#endif /* !SIMULATOR */
-
-struct mp3entry* audio_current_track(void)
-{
-#ifdef SIMULATOR
- struct mp3entry *id3 = &taginfo;
-#else /* !SIMULATOR */
- if(num_tracks_in_memory())
- {
- struct mp3entry *id3 = &get_trackdata(0)->id3;
-#endif
- if (!checked_for_cuesheet && curr_cuesheet && id3->cuesheet == NULL)
- {
- checked_for_cuesheet = true; /* only check once per track */
- struct cuesheet_file cue_file;
-
- if (look_for_cuesheet_file(id3, &cue_file) &&
- parse_cuesheet(&cue_file, curr_cuesheet))
- {
- id3->cuesheet = curr_cuesheet;
- }
- }
- return id3;
-#ifndef SIMULATOR
- }
- else
- return NULL;
-#endif /* !SIMULATOR */
-}
-
-struct mp3entry* audio_next_track(void)
-{
-#ifdef SIMULATOR
- return &taginfo;
-#else /* !SIMULATOR */
- if(num_tracks_in_memory() > 1)
- return &get_trackdata(1)->id3;
- else
- return NULL;
-#endif /* !SIMULATOR */
-}
-
-size_t audio_buffer_size(void)
-{
- if (audiobuf_handle > 0)
- return audiobuflen;
- return 0;
-}
-
-size_t audio_buffer_available(void)
-{
- size_t size = 0;
- size_t core_size = core_available();
- if (audiobuf_handle > 0)
- return audiobuflen - AUDIO_BUFFER_RESERVE - 128;
- return MAX(core_size, size);
-}
-
-static void audio_reset_buffer_noalloc(void* buf, size_t bufsize)
-{
- mpeg_audiobuf = buf;
- audiobuflen = bufsize;
- if (global_settings.cuesheet)
- { /* enable cuesheet support */
- curr_cuesheet = (struct cuesheet*)mpeg_audiobuf;
- mpeg_audiobuf = SKIPBYTES(mpeg_audiobuf, sizeof(struct cuesheet));
- audiobuflen -= sizeof(struct cuesheet);
- }
-}
-
-static void audio_reset_buffer(void)
-{
- size_t bufsize = audiobuflen;
-
- /* alloc buffer if it's was never allocated or freed by audio_hard_stop()
- * because voice cannot be played during audio playback make
- * talk.c give up all buffers and disable itself */
- if (!audiobuf_handle)
- {
- talk_buffer_set_policy(TALK_BUFFER_LOOSE);
- audiobuf_handle = core_alloc_maximum("audiobuf", &bufsize, &ops);
- }
-
- audio_reset_buffer_noalloc(core_get_data(audiobuf_handle), bufsize);
-}
-
-void audio_play(unsigned long elapsed, unsigned long offset)
-{
- audio_reset_buffer();
-#ifdef SIMULATOR
- char name_buf[MAX_PATH+1];
- const char* trackname;
- int steps=0;
-
- is_playing = true;
-
- do {
- trackname = playlist_peek(steps, name_buf, sizeof(name_buf));
- if (!trackname)
- break;
- if(mp3info(&taginfo, trackname)) {
- /* bad mp3, move on */
- if(++steps > playlist_amount())
- break;
- continue;
- }
-#ifdef HAVE_MPEG_PLAY
- real_mpeg_play(trackname);
-#endif
- playlist_next(steps);
- if (!offset && elapsed)
- {
- /* has an elapsed time but no offset; elapsed may take
- precedence in this case */
- taginfo.elapsed = elapsed;
- taginfo.offset = audio_get_file_pos_int(&taginfo);
- }
- else
- {
- taginfo.offset = offset;
- set_elapsed(&taginfo);
- }
- is_playing = true;
- playing = true;
- break;
- } while(1);
-#else /* !SIMULATOR */
- static struct audio_resume_info resume;
- is_playing = true;
- resume.elapsed = elapsed;
- resume.offset = offset;
- queue_post(&mpeg_queue, MPEG_PLAY, (intptr_t)&resume);
-#endif /* !SIMULATOR */
-
- mpeg_errno = 0;
-}
-
-void audio_stop(void)
-{
- if (audiobuf_handle <= 0)
- return; /* nothing to do, must be hard-stopped already */
-#ifndef SIMULATOR
- mpeg_stop_done = false;
- queue_post(&mpeg_queue, MPEG_STOP, 0);
- while(!mpeg_stop_done)
- yield();
-#else /* SIMULATOR */
- paused = false;
- is_playing = false;
- playing = false;
-#endif /* SIMULATOR */
-}
-
-/* dummy */
-void audio_stop_recording(void)
-{
- audio_stop();
-}
-
-void audio_hard_stop(void)
-{
- if (audiobuf_handle > 0)
- {
- audio_stop();
- audiobuf_handle = core_free(audiobuf_handle);
- mpeg_audiobuf = NULL;
- talk_buffer_set_policy(TALK_BUFFER_DEFAULT);
- }
-}
-
-void audio_pause(void)
-{
-#ifndef SIMULATOR
- queue_post(&mpeg_queue, MPEG_PAUSE, 0);
-#else /* SIMULATOR */
- is_playing = true;
- playing = false;
- paused = true;
-#endif /* SIMULATOR */
-}
-
-void audio_resume(void)
-{
-#ifndef SIMULATOR
- queue_post(&mpeg_queue, MPEG_RESUME, 0);
-#else /* SIMULATOR */
- is_playing = true;
- playing = true;
- paused = false;
-#endif /* SIMULATOR */
-}
-
-void audio_next(void)
-{
-#ifndef SIMULATOR
- queue_remove_from_head(&mpeg_queue, MPEG_NEED_DATA);
- queue_post(&mpeg_queue, MPEG_NEXT, 0);
-#else /* SIMULATOR */
- char name_buf[MAX_PATH+1];
- const char* file;
- int steps = 1;
-
- do {
- file = playlist_peek(steps, name_buf, sizeof(name_buf));
- if(!file)
- break;
- if(mp3info(&taginfo, file)) {
- if(++steps > playlist_amount())
- break;
- continue;
- }
- playlist_next(steps);
- current_track_counter++;
- is_playing = true;
- playing = true;
- break;
- } while(1);
-#endif /* SIMULATOR */
-}
-
-void audio_prev(void)
-{
-#ifndef SIMULATOR
- queue_remove_from_head(&mpeg_queue, MPEG_NEED_DATA);
- queue_post(&mpeg_queue, MPEG_PREV, 0);
-#else /* SIMULATOR */
- char name_buf[MAX_PATH+1];
- const char* file;
- int steps = -1;
-
- do {
- file = playlist_peek(steps, name_buf, sizeof(name_buf));
- if(!file)
- break;
- if(mp3info(&taginfo, file)) {
- steps--;
- continue;
- }
- playlist_next(steps);
- current_track_counter++;
- is_playing = true;
- playing = true;
- break;
- } while(1);
-#endif /* SIMULATOR */
-}
-
-void audio_ff_rewind(long newpos)
-{
-#ifndef SIMULATOR
- queue_post(&mpeg_queue, MPEG_FF_REWIND, newpos);
-#else /* SIMULATOR */
- (void)newpos;
-#endif /* SIMULATOR */
-}
-
-void audio_flush_and_reload_tracks(void)
-{
-#ifndef SIMULATOR
- queue_post(&mpeg_queue, MPEG_FLUSH_RELOAD, 0);
-#endif /* !SIMULATOR*/
-}
-
-int audio_status(void)
-{
- int ret = 0;
-
- if(is_playing)
- ret |= AUDIO_STATUS_PLAY;
-
- if(paused)
- ret |= AUDIO_STATUS_PAUSE;
-
- if(mpeg_errno)
- ret |= AUDIO_STATUS_ERROR;
-
- return ret;
-}
-
-/* Unused function
-unsigned int audio_error(void)
-{
- return mpeg_errno;
-}
-*/
-
-void audio_error_clear(void)
-{
- mpeg_errno = 0;
-}
-
-#ifdef SIMULATOR
-static void mpeg_thread(void)
-{
- struct mp3entry* id3;
- while ( 1 ) {
- if (is_playing) {
- id3 = audio_current_track();
- if (!paused)
- {
- id3->elapsed+=1000;
- id3->offset+=1000;
- }
- if (id3->elapsed>=id3->length)
- audio_next();
- }
- sleep(HZ);
- }
-}
-#endif /* SIMULATOR */
-
-void audio_init(void)
-{
- mpeg_errno = 0;
-
- audio_reset_buffer();
-
-#ifndef SIMULATOR
- queue_init(&mpeg_queue, true);
-#endif /* !SIMULATOR */
- audio_thread_id = create_thread(mpeg_thread, mpeg_stack,
- sizeof(mpeg_stack), 0, mpeg_thread_name
- IF_PRIO(, PRIORITY_SYSTEM)
- IF_COP(, CPU));
-
- memset(trackdata, 0, sizeof(trackdata));
-
-#ifdef DEBUG
-#ifndef SIMULATOR
- dbg_timer_start();
- dbg_cnt2us(0);
-#endif /* !SIMULATOR */
-#endif /* DEBUG */
- audio_is_initialized = true;
-}
-
-#endif /* CONFIG_CODEC != SWCODEC */