diff options
Diffstat (limited to 'apps/playback.c')
-rw-r--r-- | apps/playback.c | 1176 |
1 files changed, 1176 insertions, 0 deletions
diff --git a/apps/playback.c b/apps/playback.c new file mode 100644 index 0000000000..15258a8e13 --- /dev/null +++ b/apps/playback.c @@ -0,0 +1,1176 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2005 Miika Pekkarinen + * + * All files in this archive are subject to the GNU General Public License. + * See the file COPYING in the source tree root for full license agreement. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <ctype.h> + +#include "system.h" +#include "thread.h" +#include "file.h" +#include "lcd.h" +#include "font.h" +#include "backlight.h" +#include "button.h" +#include "kernel.h" +#include "tree.h" +#include "debug.h" +#include "sprintf.h" +#include "settings.h" +#include "plugin.h" +#include "wps.h" +#include "wps-display.h" +#include "audio.h" +#include "logf.h" +#include "mp3_playback.h" +#include "mp3data.h" +#include "usb.h" +#include "status.h" +#include "main_menu.h" +#include "ata.h" +#include "screens.h" +#include "playlist.h" +#include "playback.h" +#include "pcm_playback.h" +#include "buffer.h" +#ifdef HAVE_LCD_BITMAP +#include "icons.h" +#include "peakmeter.h" +#include "action.h" +#endif +#include "lang.h" +#include "bookmark.h" +#include "misc.h" +#include "sound.h" + +static volatile bool playing; +static volatile bool paused; + +#define CODEC_VORBIS "/.rockbox/codecs/codecvorbis.rock"; +#define CODEC_MPA_L3 "/.rockbox/codecs/codecmpa.rock"; +#define CODEC_FLAC "/.rockbox/codecs/codecflac.rock"; +#define CODEC_WAV "/.rockbox/codecs/codecwav.rock"; + +#define AUDIO_WATERMARK 0x70000 +#define AUDIO_FILE_CHUNK (1024*256) + +#define AUDIO_PLAY 1 +#define AUDIO_STOP 2 +#define AUDIO_PAUSE 3 +#define AUDIO_RESUME 4 +#define AUDIO_NEXT 5 +#define AUDIO_PREV 6 +#define AUDIO_FF_REWIND 7 +#define AUDIO_FLUSH_RELOAD 8 +#define AUDIO_CODEC_DONE 9 + +#define CODEC_LOAD 1 +#define CODEC_LOAD_DISK 2 + +/* As defined in plugins/lib/xxx2wav.h */ +#define MALLOC_BUFSIZE (512*1024) +#define GUARD_BUFSIZE (8*1024) + +/* TODO: + Handle playlist_peek in mpeg.c + Track changing +*/ + +extern bool audio_is_initialized; + +/* Buffer control thread. */ +static struct event_queue audio_queue; +static long audio_stack[(DEFAULT_STACK_SIZE + 0x1000)/sizeof(long)]; +static const char audio_thread_name[] = "audio"; + +/* Codec thread. */ +static struct event_queue codec_queue; +static long codec_stack[(DEFAULT_STACK_SIZE + 0x2500)/sizeof(long)] __attribute__ ((section(".idata"))); +static const char codec_thread_name[] = "codec"; + +/* Is file buffer currently being refilled? */ +static volatile bool filling; + +/* Ring buffer where tracks and codecs are loaded. */ +char *codecbuf; + +/* Total size of the ring buffer. */ +int codecbuflen; + +/* Bytes available in the buffer. */ +volatile int codecbufused; + +/* Ring buffer read and write indexes. */ +static int buf_ridx; +static int buf_widx; + +#define MAX_TRACK 10 +struct track_info { + struct mp3entry id3; + struct mp3info mp3data; + char *codecbuf; + size_t codecsize; + int codectype; + + volatile char *filebuf; + off_t filerem; + off_t filesize; + off_t filepos; + volatile int available; + bool taginfo_ready; + int playlist_offset; +}; + +/* Track information (count in file buffer, read/write indexes for + track ring structure. */ +static int track_count; +static int track_ridx; +static int track_widx; +static bool track_changed; + +/* Playlist position to tell the next track. */ +static int last_offset; + +/* Partially loaded song's file handle to continue buffering later. */ +static int current_fd; + +/* Information about how many bytes left on the buffer re-fill run. */ +static size_t fill_bytesleft; + +/* Track info structure about songs in the file buffer. */ +static struct track_info tracks[MAX_TRACK]; + +/* Pointer to track info structure about current song playing. */ +static volatile struct track_info *cur_ti; + +/* Codec API including function callbacks. */ +static struct codec_api ci; + +/* When we change a song and buffer is not in filling state, this + variable keeps information about whether to go a next/previous track. */ +static int new_track; + +#ifdef SIMULATOR +bool audiobuffer_insert_sim(char *buf, size_t length) +{ + (void)buf; + (void)length; + + return true; +} +#endif + +void* get_codec_memory_callback(size_t *size) +{ + *size = MALLOC_BUFSIZE; + return &audiobuf[0]; +} + +void codec_set_elapsed_callback(unsigned int value) +{ + unsigned int latency; + +#ifndef SIMULATOR + latency = audiobuffer_get_latency(); +#else + latency = 0; +#endif + if (value < latency) { + cur_ti->id3.elapsed = 0; + } else if (value - latency > cur_ti->id3.elapsed + || value - latency < cur_ti->id3.elapsed - 2) { + cur_ti->id3.elapsed = value - latency; + } +} + +size_t codec_filebuf_callback(void *ptr, size_t size) +{ + char *buf = (char *)ptr; + int copy_n; + int part_n; + + if (ci.stop_codec || !playing) + return 0; + + copy_n = MIN((off_t)size, (off_t)cur_ti->available + cur_ti->filerem); + + while (copy_n > cur_ti->available) { + yield(); + if (ci.stop_codec) + return 0; + } + + if (copy_n == 0) + return 0; + + part_n = MIN(copy_n, codecbuflen - buf_ridx); + memcpy(buf, &codecbuf[buf_ridx], part_n); + if (part_n < copy_n) { + part_n = copy_n - part_n; + memcpy(&buf[part_n], &codecbuf[0], copy_n - part_n); + } + + buf_ridx += copy_n; + if (buf_ridx >= codecbuflen) + buf_ridx -= codecbuflen; + ci.curpos += copy_n; + cur_ti->available -= copy_n; + codecbufused -= copy_n; + + return copy_n; +} + +void* codec_request_buffer_callback(size_t *realsize, size_t reqsize) +{ + size_t part_n; + + if (ci.stop_codec || !playing) { + *realsize = 0; + return NULL; + } + + *realsize = MIN((off_t)reqsize, (off_t)cur_ti->available + cur_ti->filerem); + if (*realsize == 0) { + return NULL; + } + + while ((int)*realsize > cur_ti->available) { + // logf("Buffer wait: %d", cur_ti->available); + yield(); + if (ci.stop_codec) { + return NULL; + } + } + + part_n = MIN((int)*realsize, codecbuflen - buf_ridx); + if (part_n < *realsize) { + part_n += GUARD_BUFSIZE; + if (part_n < *realsize) + *realsize = part_n; + memcpy(&codecbuf[codecbuflen], &codecbuf[0], *realsize - + (codecbuflen - buf_ridx)); + } + + return (char *)&codecbuf[buf_ridx]; +} + +void codec_advance_buffer_callback(size_t amount) +{ + if ((int)amount > cur_ti->available) + amount = cur_ti->available; + + cur_ti->available -= amount; + codecbufused -= amount; + buf_ridx += amount; + if (buf_ridx >= codecbuflen) + buf_ridx -= codecbuflen; + ci.curpos += amount; +} + +void codec_advance_buffer_loc_callback(void *ptr) +{ + size_t amount; + + amount = (int)ptr - (int)&codecbuf[buf_ridx]; + codec_advance_buffer_callback(amount); +} + +off_t codec_mp3_get_filepos_callback(int newtime) +{ + int oldtime; + off_t newpos; + + oldtime = cur_ti->id3.elapsed; + cur_ti->id3.elapsed = newtime; + newpos = audio_get_file_pos(); + cur_ti->id3.elapsed = oldtime; + + return newpos; +} + +bool codec_seek_buffer_callback(off_t newpos) +{ + int difference; + + if (newpos < 0) + newpos = 0; + + if (newpos >= cur_ti->filesize) + newpos = cur_ti->filesize - 1; + + difference = newpos - ci.curpos; + if (difference >= 0) { + logf("seek: +%d", difference); + codec_advance_buffer_callback(difference); +#ifndef SIMULATOR + pcm_play_stop(); +#endif + return true; + } + + difference = -difference; + if (ci.curpos - difference < 0) + difference = ci.curpos; + + if (codecbufused + difference > codecbuflen) { + /* We need to reload the song. FIX THIS! */ + return false; + } + + logf("seek: -%d", difference); + codecbufused += difference; + cur_ti->available += difference; + buf_ridx -= difference; + if (buf_ridx < 0) + buf_ridx = codecbuflen + buf_ridx; + ci.curpos -= difference; +#ifndef SIMULATOR + pcm_play_stop(); +#endif + + return true; +} + +/* Simple file type probing by looking filename extension. */ +int probe_file_format(const char *filename) +{ + char suffix[4]; + char *p; + int len, i; + + if (filename == NULL) + return AFMT_UNKNOWN; + + len = strlen(filename); + if (len < 4) + return AFMT_UNKNOWN; + + p = (char *)&filename[len-3]; + memset(suffix, 0, sizeof(suffix)); + for (i = 0; i < 3; i++) { + suffix[i] = tolower(p[i]); + } + + if (!strcmp("mp3", suffix)) + return AFMT_MPA_L3; + else if (!strcmp("ogg", suffix)) + return AFMT_OGG_VORBIS; + else if (!strcmp("wav", suffix)) + return AFMT_PCM_WAV; + else if (!strcmp("flac", suffix)) // FIX THIS + return AFMT_FLAC; + else if (!strcmp("mpc", suffix)) + return AFMT_MPC; + else if (!strcmp("aac", suffix)) + return AFMT_AAC; + else if (!strcmp("ape", suffix)) + return AFMT_APE; + else if (!strcmp("wma", suffix)) + return AFMT_OGG_VORBIS; + else if (!strcmp("a52", suffix)) + return AFMT_OGG_VORBIS; + else if (!strcmp(".rm", suffix)) + return AFMT_OGG_VORBIS; + + return AFMT_UNKNOWN; + +} + +void audio_fill_file_buffer(void) +{ + size_t i; + int rc; + + logf("Filling buffer..."); + i = 0; + while ((off_t)i < tracks[track_widx].filerem) { + sleep(5); /* Give codecs some processing time. */ + if (!playing) { + logf("Filling interrupted"); + close(current_fd); + current_fd = -1; + return ; + } + + if (fill_bytesleft < MIN(AUDIO_FILE_CHUNK, + tracks[track_widx].filerem - i)) + break ; + + rc = MIN(AUDIO_FILE_CHUNK, codecbuflen - buf_widx); + rc = read(current_fd, &codecbuf[buf_widx], rc); + if (rc <= 0) { + tracks[track_widx].filerem = 0; + break ; + } + buf_widx += rc; + if (buf_widx == codecbuflen) + buf_widx = 0; + i += rc; + tracks[track_widx].available += rc; + fill_bytesleft -= rc; + } + + tracks[track_widx].filerem -= i; + codecbufused += i; + tracks[track_widx].filepos += i; + logf("Done:%d", tracks[track_widx].available); + if (tracks[track_widx].filerem == 0) { + if (++track_widx == MAX_TRACK) + track_widx = 0; + tracks[track_widx].filerem = 0; + close(current_fd); + current_fd = -1; + } +} + +bool loadcodec(const char *trackname, bool start_play) +{ + char msgbuf[80]; + off_t size; + int filetype; + int fd; + int i, rc; + const char *codec_path; + int copy_n; + int prev_track; + + filetype = probe_file_format(trackname); + switch (filetype) { + case AFMT_OGG_VORBIS: + logf("Codec: Vorbis"); + codec_path = CODEC_VORBIS; + break; + case AFMT_MPA_L3: + logf("Codec: MPA L3"); + codec_path = CODEC_MPA_L3; + break; + case AFMT_PCM_WAV: + logf("Codec: PCM WAV"); + codec_path = CODEC_WAV; + break; + case AFMT_FLAC: + logf("Codec: FLAC"); + codec_path = CODEC_FLAC; + break; + default: + logf("Codec: Unsupported"); + snprintf(msgbuf, sizeof(msgbuf)-1, "No codec for: %s", trackname); + splash(HZ*2, true, msgbuf); + codec_path = NULL; + } + + tracks[track_widx].codectype = filetype; + tracks[track_widx].codecsize = 0; + if (codec_path == NULL) + return false; + + if (!start_play) { + prev_track = track_widx - 1; + if (prev_track < 0) + prev_track = MAX_TRACK-1; + if (track_count > 0 && filetype == tracks[prev_track].codectype) { + logf("Reusing prev. codec"); + return true; + } + } else { + /* Load the codec directly from disk and save some memory. */ + cur_ti = &tracks[track_widx]; + ci.filesize = cur_ti->filesize; + ci.id3 = (struct mp3entry *)&cur_ti->id3; + ci.mp3data = (struct mp3info *)&cur_ti->mp3data; + ci.taginfo_ready = (bool *)&cur_ti->taginfo_ready; + ci.curpos = 0; + playing = true; + logf("Starting codec"); + queue_post(&codec_queue, CODEC_LOAD_DISK, (void *)codec_path); + return true; + } + + fd = open(codec_path, O_RDONLY); + if (fd < 0) { + logf("Codec doesn't exist!"); + snprintf(msgbuf, sizeof(msgbuf)-1, "Couldn't load codec: %s", codec_path); + splash(HZ*2, true, msgbuf); + return false; + } + + size = filesize(fd); + if ((off_t)fill_bytesleft < size + AUDIO_WATERMARK) { + logf("Not enough space"); + close(fd); + return false; + } + + i = 0; + while (i < size) { + yield(); + if (!playing) { + logf("Buffering interrupted"); + close(fd); + return false; + } + + copy_n = MIN(AUDIO_FILE_CHUNK, codecbuflen - buf_widx); + rc = read(fd, &codecbuf[buf_widx], copy_n); + if (rc < 0) + return false; + buf_widx += rc; + if (buf_widx >= codecbuflen) + buf_widx = 0; + i += rc; + } + close(fd); + logf("Done: %dB", i); + + codecbufused += size; + fill_bytesleft -= size; + tracks[track_widx].codecsize = size; + + return true; +} + +bool audio_load_track(int offset, bool start_play) +{ + char *trackname; + int fd; + off_t size; + int rc, i; + int copy_n; + + if (track_count >= MAX_TRACK) + return false; + + trackname = playlist_peek(offset); + if (!trackname) { + return false; + } + + fd = open(trackname, O_RDONLY); + if (fd < 0) + return false; + + size = filesize(fd); + tracks[track_widx].filerem = size; + tracks[track_widx].filesize = size; + tracks[track_widx].filepos = 0; + tracks[track_widx].available = 0; + tracks[track_widx].taginfo_ready = false; + tracks[track_widx].playlist_offset = offset; + + /* Load the codec */ + if (buf_widx >= codecbuflen) + buf_widx = 0; + + tracks[track_widx].codecbuf = &codecbuf[buf_widx]; + if (!loadcodec(trackname, start_play)) { + close(fd); + return false; + } + tracks[track_widx].filebuf = &codecbuf[buf_widx]; + + logf("%s", trackname); + logf("Buffering track:%d/%d", track_widx, track_ridx); + + if (!playing) { + close(fd); + return false; + } + + /* Load codec specific track tag information. */ + switch (tracks[track_widx].codectype) { + case AFMT_MPA_L3: + /* Should check the return value. */ + mp3info(&tracks[track_widx].id3, trackname, true); + lseek(fd, 0, SEEK_SET); + get_mp3file_info(fd, &tracks[track_widx].mp3data); + logf("T:%s", tracks[track_widx].id3.title); + logf("L:%d", tracks[track_widx].id3.length); + logf("O:%d", tracks[track_widx].id3.first_frame_offset); + logf("F:%d", tracks[track_widx].id3.frequency); + tracks[track_widx].taginfo_ready = true; + break ; + } + + playlist_next(0); + last_offset++; + track_count++; + lseek(fd, 0, SEEK_SET); + i = 0; + while (i < size) { + /* Give codecs some processing time to prevent glitches. */ + sleep(5); + if (!playing) { + logf("Buffering interrupted"); + close(fd); + return false; + } + + if (fill_bytesleft == 0) + break ; + + copy_n = MIN(AUDIO_FILE_CHUNK, codecbuflen - buf_widx); + copy_n = MIN(size - i, copy_n); + copy_n = MIN((int)fill_bytesleft, copy_n); + rc = read(fd, &codecbuf[buf_widx], copy_n); + if (rc < 0) { + close(fd); + return false; + } + buf_widx += rc; + if (buf_widx == codecbuflen) + buf_widx = 0; + i += rc; + tracks[track_widx].available += rc; + tracks[track_widx].filerem -= rc; + fill_bytesleft -= rc; + } + + tracks[track_widx].filepos = i; + codecbufused += i; + + /* Leave the file handle open for faster buffer refill. */ + if (tracks[track_widx].filerem != 0) { + current_fd = fd; + logf("Partially buf:%d", tracks[track_widx].available); + return false; + } else { + logf("Completely buf."); + close(fd); + current_fd = -1; + if (++track_widx >= MAX_TRACK) { + track_widx = 0; + } + tracks[track_widx].filerem = 0; + } + + return true; +} + +void audio_insert_tracks(bool start_playing) +{ + fill_bytesleft = codecbuflen - codecbufused; + filling = true; + while (audio_load_track(last_offset, start_playing)) { + start_playing = false; + } + filling = false; +} + +void audio_play_start(int offset) +{ + memset(&tracks, 0, sizeof(struct track_info)); + sound_set(SOUND_VOLUME, global_settings.volume); + track_count = 0; + track_changed = true; + track_widx = 0; + track_ridx = 0; + buf_ridx = 0; + buf_widx = 0; + codecbufused = 0; +#ifndef SIMULATOR + pcm_set_boost_mode(true); +#endif + last_offset = offset; + audio_insert_tracks(true); +#ifndef SIMULATOR + pcm_set_boost_mode(false); + ata_sleep(); +#endif +} + +void audio_check_buffer(void) +{ + int i; + int cur_idx; + + /* Start buffer filling as necessary. */ + if (codecbufused > AUDIO_WATERMARK || !playing) + return ; + + filling = true; +#ifndef SIMULATOR + pcm_set_boost_mode(true); +#endif + + fill_bytesleft = codecbuflen - codecbufused; + + /* Calculate real track count after throwing away old tracks. */ + cur_idx = track_ridx; + for (i = 0; i < track_count; i++) { + if (cur_idx == track_widx) + break ; + + if (++cur_idx >= MAX_TRACK) + cur_idx = 0; + } + + track_count = i; + if (tracks[cur_idx].filerem != 0) + track_count++; + + /* Mark all other entries null. */ + cur_idx = track_widx; + for (i = 0; i < MAX_TRACK - track_count; i++) { + if (++cur_idx >= MAX_TRACK) + cur_idx = 0; + tracks[cur_idx].available = 0; + } + + /* Try to load remainings of the file. */ + if (tracks[track_widx].filerem > 0) + audio_fill_file_buffer(); + + /* Load new files to fill the entire buffer. */ + if (tracks[track_widx].filerem == 0) + audio_insert_tracks(false); + +#ifndef SIMULATOR + pcm_set_boost_mode(false); + if (playing) + ata_sleep(); +#endif + filling = false; +} + +void audio_update_trackinfo(void) +{ + if (new_track >= 0) { + buf_ridx += cur_ti->available; + codecbufused -= cur_ti->available; + + cur_ti = &tracks[track_ridx]; + buf_ridx += cur_ti->codecsize; + if (buf_ridx >= codecbuflen) + buf_ridx -= codecbuflen; + } else { + buf_ridx -= ci.curpos; + codecbufused += ci.curpos; + + cur_ti = &tracks[track_ridx]; + buf_ridx -= cur_ti->filesize; + buf_ridx -= cur_ti->codecsize; + cur_ti->available = cur_ti->filesize; + if (buf_ridx < 0) + buf_ridx = codecbuflen + buf_ridx; + } + + ci.filesize = cur_ti->filesize; + ci.id3 = (struct mp3entry *)&cur_ti->id3; + ci.mp3data = (struct mp3info *)&cur_ti->mp3data; + ci.curpos = 0; + ci.taginfo_ready = (bool *)&cur_ti->taginfo_ready; + track_changed = true; +} + +void audio_change_track(void) +{ + if (track_ridx == track_widx) { + logf("No more tracks"); + playing = false; + return ; + } + + if (++track_ridx >= MAX_TRACK) + track_ridx = 0; + + audio_update_trackinfo(); + queue_post(&codec_queue, CODEC_LOAD, 0); +} + +bool codec_request_next_track_callback(void) +{ + if (ci.stop_codec || !playing) + return false; + + logf("Request new track"); + + /* Advance to next track. */ + if (ci.reload_codec && new_track > 0) { + if (++track_ridx == MAX_TRACK) + track_ridx = 0; + if (track_ridx == track_widx && tracks[track_ridx].filerem == 0) { + logf("Loading from disk..."); + new_track = 0; + queue_post(&audio_queue, AUDIO_PLAY, (void *)(last_offset)); + return false; + } + } + + /* Advance to previous track. */ + else if (ci.reload_codec && new_track < 0) { + if (--track_ridx < 0) + track_ridx = MAX_TRACK-1; + if (tracks[track_ridx].filesize == 0 || + codecbufused+ci.curpos+tracks[track_ridx].filesize + + (off_t)tracks[track_ridx].codecsize > codecbuflen) { + logf("Loading from disk..."); + last_offset -= track_count; + if (last_offset < 0) + last_offset = 0; + new_track = 0; + queue_post(&audio_queue, AUDIO_PLAY, (void *)(last_offset)); + return false; + } + } + + /* Codec requested track change (next track). */ + else { + if (++track_ridx >= MAX_TRACK) + track_ridx = 0; + + if (track_ridx == track_widx && tracks[track_ridx].filerem == 0) { + if (ci.reload_codec) { + } else { + logf("No more tracks"); + } + new_track = 0; + return false; + } + } + + ci.reload_codec = false; + + if (cur_ti->codectype != tracks[track_ridx].codectype) { + if (--track_ridx < 0) + track_ridx = MAX_TRACK-1; + logf("New codec"); + new_track = 0; + return false; + } + + logf("On-the-fly change"); + audio_update_trackinfo(); + new_track = 0; + + return true; +} + +void audio_thread(void) +{ + struct event ev; + + while (1) { + yield(); + audio_check_buffer(); + + queue_wait_w_tmo(&audio_queue, &ev, 0); + switch (ev.id) { + case AUDIO_PLAY: + ci.stop_codec = true; + ci.reload_codec = false; + ci.seek_time = 0; +#ifndef SIMULATOR + pcm_play_stop(); + pcm_play_pause(true); +#endif + playing = true; + paused = false; + audio_play_start((int)ev.data); + break ; + + case AUDIO_STOP: +#ifndef SIMULATOR + pcm_play_stop(); +#endif + paused = false; + break ; + + case AUDIO_PAUSE: + break ; + + case AUDIO_RESUME: + break ; + + case AUDIO_NEXT: + break ; + + case AUDIO_CODEC_DONE: + //if (playing) + // audio_change_track(); + break ; + +#ifndef SIMULATOR + case SYS_USB_CONNECTED: + playing = false; + ci.stop_codec = true; + logf("USB Connection"); + pcm_play_stop(); + usb_acknowledge(SYS_USB_CONNECTED_ACK); + usb_wait_for_disconnect(&audio_queue); + break ; +#endif + } + } +} + +void codec_thread(void) +{ + struct event ev; + size_t codecsize; + int status; + int wrap; + + while (1) { + status = 0; + queue_wait(&codec_queue, &ev); + switch (ev.id) { + case CODEC_LOAD_DISK: + ci.stop_codec = false; + status = codec_load_file((char *)ev.data, &ci); + break ; + + case CODEC_LOAD: + logf("Codec start"); + codecsize = cur_ti->codecsize; + if (codecsize == 0) { + logf("Codec slot is empty!"); + playing = false; + break ; + } + codecbufused -=codecsize; + cur_ti->codecsize = 0; + + ci.stop_codec = false; + wrap = (int)&codecbuf[codecbuflen] - (int)cur_ti->codecbuf; + status = codec_load_ram(cur_ti->codecbuf, codecsize, + &ci, &codecbuf[0], codecbuflen); + break ; + +#ifndef SIMULATOR + case SYS_USB_CONNECTED: + usb_acknowledge(SYS_USB_CONNECTED_ACK); + usb_wait_for_disconnect(&codec_queue); + break ; +#endif + } + + switch (ev.id) { + case CODEC_LOAD_DISK: + case CODEC_LOAD: + if (status != PLUGIN_OK) { + logf("Codec failure"); + playing = false; + } else { + logf("Codec finished"); + } + + queue_post(&audio_queue, AUDIO_CODEC_DONE, (void *)status); + if (playing && !ci.stop_codec && !ci.reload_codec) { + audio_change_track(); + } else { + playing = false; + } + } + } +} + +struct mp3entry* audio_current_track(void) +{ + logf("audio_current_track"); + + if (track_count > 0 && cur_ti->taginfo_ready) + return (struct mp3entry *)&cur_ti->id3; + else + return NULL; +} + +struct mp3entry* audio_next_track(void) +{ + int next_idx = track_ridx + 1; + + if (track_count == 0) + return NULL; + + if (next_idx >= MAX_TRACK) + next_idx = 0; + + if (!tracks[next_idx].taginfo_ready) + return NULL; + + //logf("audio_next_track"); + + return &tracks[next_idx].id3; +} + +bool audio_has_changed_track(void) +{ + if (track_changed && track_count > 0) { + if (!cur_ti->taginfo_ready) + return false; + track_changed = false; + return true; + } + + return false; +} + +void audio_play(int offset) +{ + logf("audio_play"); + playing = false; + ci.stop_codec = true; + queue_post(&audio_queue, AUDIO_PLAY, (void *)offset); +} + +void audio_stop(void) +{ + logf("audio_stop"); + if (!playing) + return ; + + playing = false; + ci.stop_codec = true; + if (current_fd) { + close(current_fd); + current_fd = -1; + } + queue_post(&audio_queue, AUDIO_STOP, 0); +} + +void audio_pause(void) +{ + logf("audio_pause"); +#ifndef SIMULATOR + pcm_play_pause(false); +#endif + paused = true; + //queue_post(&audio_queue, AUDIO_PAUSE, 0); +} + +void audio_resume(void) +{ + logf("audio_resume"); +#ifndef SIMULATOR + pcm_play_pause(true); +#endif + paused = false; + //queue_post(&audio_queue, AUDIO_RESUME, 0); +} + +void audio_next(void) +{ + logf("audio_next"); + new_track = 1; + ci.reload_codec = true; +#ifndef SIMULATOR + pcm_play_stop(); +#endif + + /* Detect if disk is spinning.. */ + if (filling) { + playing = false; + ci.stop_codec = true; + queue_post(&audio_queue, AUDIO_PLAY, (void *)(last_offset)); + } +} + +void audio_prev(void) +{ + logf("audio_prev"); + new_track = -1; + ci.reload_codec = true; +#ifndef SIMULATOR + pcm_play_stop(); +#endif + + if (filling) { + playing = false; + ci.stop_codec = true; + if (--last_offset < 0) + last_offset = 0; + queue_post(&audio_queue, AUDIO_PLAY, (void *)(last_offset)); + } + //queue_post(&audio_queue, AUDIO_PREV, 0); +} + +void audio_ff_rewind(int newpos) +{ + logf("rewind: %d", newpos); + /* Does not work yet. */ + if (playing) + ci.seek_time = newpos+1; +} + +void audio_flush_and_reload_tracks(void) +{ + logf("flush & reload"); +} + +void audio_error_clear(void) +{ +} + +int audio_status(void) +{ + int ret = 0; + + if (playing) + ret |= AUDIO_STATUS_PLAY; + + if (paused) + ret |= AUDIO_STATUS_PAUSE; + + return ret; +} + +void audio_init(void) +{ + logf("audio api init"); + codecbuflen = audiobufend - audiobuf - AUDIOBUF_SIZE + - MALLOC_BUFSIZE - GUARD_BUFSIZE; + //codecbuflen = 2*512*1024; + codecbufused = 0; + filling = 0; + codecbuf = &audiobuf[MALLOC_BUFSIZE]; + playing = false; + paused = false; + track_changed = false; + + logf("abuf:%0x", AUDIOBUF_SIZE); + logf("fbuf:%0x", codecbuflen); + logf("mbuf:%0x", MALLOC_BUFSIZE); + + /* Initialize codec api. */ + ci.read_filebuf = codec_filebuf_callback; +#ifndef SIMULATOR + ci.audiobuffer_insert = audiobuffer_insert; +#else + ci.audiobuffer_insert = audiobuffer_insert_sim; +#endif + ci.get_codec_memory = get_codec_memory_callback; + ci.request_buffer = codec_request_buffer_callback; + ci.advance_buffer = codec_advance_buffer_callback; + ci.advance_buffer_loc = codec_advance_buffer_loc_callback; + ci.request_next_track = codec_request_next_track_callback; + ci.mp3_get_filepos = codec_mp3_get_filepos_callback; + ci.seek_buffer = codec_seek_buffer_callback; + ci.set_elapsed = codec_set_elapsed_callback; + + queue_init(&codec_queue); + + create_thread(codec_thread, codec_stack, sizeof(codec_stack), + codec_thread_name); + create_thread(audio_thread, audio_stack, sizeof(audio_stack), + audio_thread_name); +#ifndef SIMULATOR + audio_is_initialized = true; +#endif +} + + |