1
0
Fork 0
forked from len0rd/rockbox

Rework track skipping. Full playback stop is no longer involved in track skipping. Further simplification of the play related functions can be achieved with this in place, but that's for later. Known skip related bug, high pitched noise sometimes, otherwise stuff is better

git-svn-id: svn://svn.rockbox.org/rockbox/trunk@9601 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
Brandon Low 2006-04-11 03:55:58 +00:00
parent fb6b475dee
commit 62ccbbb418
4 changed files with 306 additions and 277 deletions

View file

@ -210,6 +210,8 @@ struct codec_api ci = {
/* new stuff at the end, sort into place next time /* new stuff at the end, sort into place next time
the API gets incompatible */ the API gets incompatible */
NULL, /* discard_codec */
}; };
int codec_load_ram(char* codecptr, int size, void* ptr2, int bufwrap, int codec_load_ram(char* codecptr, int size, void* ptr2, int bufwrap,
@ -230,6 +232,7 @@ int codec_load_ram(char* codecptr, int size, void* ptr2, int bufwrap,
if (size - copy_n > 0) { if (size - copy_n > 0) {
memcpy(&codecbuf[copy_n], ptr2, size - copy_n); memcpy(&codecbuf[copy_n], ptr2, size - copy_n);
} }
api->discard_codec();
} }
hdr = (struct codec_header *)codecbuf; hdr = (struct codec_header *)codecbuf;

View file

@ -85,7 +85,7 @@
#define CODEC_MAGIC 0x52434F44 /* RCOD */ #define CODEC_MAGIC 0x52434F44 /* RCOD */
/* increase this every time the api struct changes */ /* increase this every time the api struct changes */
#define CODEC_API_VERSION 6 #define CODEC_API_VERSION 7
/* update this to latest version if a change to the api struct breaks /* update this to latest version if a change to the api struct breaks
backwards compatibility (and please take the opportunity to sort in any backwards compatibility (and please take the opportunity to sort in any
@ -287,6 +287,8 @@ struct codec_api {
/* new stuff at the end, sort into place next time /* new stuff at the end, sort into place next time
the API gets incompatible */ the API gets incompatible */
void (*discard_codec)(void);
}; };
/* codec header */ /* codec header */

View file

@ -84,11 +84,15 @@ static volatile bool paused;
#define CODEC_SHN "/.rockbox/codecs/shorten.codec" #define CODEC_SHN "/.rockbox/codecs/shorten.codec"
#define CODEC_AIFF "/.rockbox/codecs/aiff.codec" #define CODEC_AIFF "/.rockbox/codecs/aiff.codec"
#define AUDIO_DEFAULT_FIRST_LIMIT (1024*1024*10) /* default point to start buffer refill */
#define AUDIO_DEFAULT_WATERMARK (1024*512) #define AUDIO_DEFAULT_WATERMARK (1024*512)
/* amount of data to read in one read() call */
#define AUDIO_DEFAULT_FILECHUNK (1024*32) #define AUDIO_DEFAULT_FILECHUNK (1024*32)
/* point at which the file buffer will fight for CPU time */
#define AUDIO_FILEBUF_CRITICAL (1024*128) #define AUDIO_FILEBUF_CRITICAL (1024*128)
#define AUDIO_REBUFFER_GUESS_SIZE (1024*128) /* amount of guess-space to allow for codecs that must hunt and peck
* for their correct seeek target, 32k seems a good size */
#define AUDIO_REBUFFER_GUESS_SIZE (1024*32)
enum { enum {
Q_AUDIO_PLAY = 1, Q_AUDIO_PLAY = 1,
@ -98,7 +102,7 @@ enum {
Q_AUDIO_PRE_FF_REWIND, Q_AUDIO_PRE_FF_REWIND,
Q_AUDIO_FF_REWIND, Q_AUDIO_FF_REWIND,
Q_AUDIO_REBUFFER_SEEK, Q_AUDIO_REBUFFER_SEEK,
Q_AUDIO_FLUSH_RELOAD, Q_AUDIO_CHECK_NEW_TRACK,
Q_AUDIO_CODEC_DONE, Q_AUDIO_CODEC_DONE,
Q_AUDIO_FLUSH, Q_AUDIO_FLUSH,
Q_AUDIO_TRACK_CHANGED, Q_AUDIO_TRACK_CHANGED,
@ -141,7 +145,7 @@ static long voice_codec_stack[(DEFAULT_STACK_SIZE + 0x2000)/sizeof(long)] IBSS_A
static const char voice_codec_thread_name[] = "voice codec"; static const char voice_codec_thread_name[] = "voice codec";
static struct mutex mutex_codecthread; static struct mutex mutex_codecthread;
static struct mutex mutex_rebuffer; static struct mutex mutex_interthread;
static struct mp3entry id3_voice; static struct mp3entry id3_voice;
@ -227,6 +231,7 @@ int mp3_get_file_pos(void);
static void audio_clear_track_entries(bool buffered_only); static void audio_clear_track_entries(bool buffered_only);
static void initialize_buffer_fill(bool start_play); static void initialize_buffer_fill(bool start_play);
static void audio_fill_file_buffer(bool start_play, size_t offset);
#ifdef TIME_CODEC #ifdef TIME_CODEC
bool is_filling(void) bool is_filling(void)
@ -633,6 +638,138 @@ void* codec_request_buffer_callback(size_t *realsize, size_t reqsize)
return (char *)&filebuf[buf_ridx]; return (char *)&filebuf[buf_ridx];
} }
static void buffer_wind_forward(void)
{
/* Wind the buffer forward to the end of the current track */
buf_ridx += prev_ti->available;
filebufused -= prev_ti->available;
if (buf_ridx >= filebuflen)
buf_ridx -= filebuflen;
}
static void buffer_wind_backward(bool need_codec)
{
/* Rewind the buffer to the beginning of the new track */
size_t rewind = ci.curpos + prev_ti->codecsize + cur_ti->filesize;
if (need_codec)
rewind += cur_ti->codecsize;
/* Check and handle buffer wrapping */
if (rewind > buf_ridx)
buf_ridx += filebuflen;
buf_ridx -= rewind;
filebufused += rewind;
/* Rewind the track to its beginning */
prev_ti->available = prev_ti->filesize - prev_ti->filerem;
/* Reset to the beginning of the new track */
cur_ti->available = cur_ti->filesize;
}
static void audio_update_trackinfo(void)
{
ci.filesize = cur_ti->filesize;
cur_ti->id3.elapsed = 0;
cur_ti->id3.offset = 0;
ci.id3 = (struct mp3entry *)&cur_ti->id3;
ci.curpos = 0;
cur_ti->start_pos = 0;
ci.taginfo_ready = (bool *)&cur_ti->taginfo_ready;
track_changed = true;
}
static int get_codec_base_type(int type)
{
switch (type) {
case AFMT_MPA_L1:
case AFMT_MPA_L2:
case AFMT_MPA_L3:
return AFMT_MPA_L3;
}
return type;
}
static void audio_rebuffer(void)
{
logf("Forcing rebuffer");
/* Stop in progress fill, and clear open file descriptor */
close(current_fd);
current_fd = -1;
filling = false;
/* Reset buffer and track pointers */
buf_ridx = buf_widx = 0;
track_widx = track_ridx;
filebufused = 0;
/* Cause the buffer fill to return as soon as the codec is loaded */
queue_post(&audio_queue, Q_AUDIO_FILL_BUFFER, 0);
/* Fill the buffer */
last_peek_offset = -1;
tracks[track_ridx].filesize = 0;
audio_fill_file_buffer(false, 0);
}
static void audio_check_new_track(long direction)
{
/* Move to the new track */
prev_ti = cur_ti;
cur_ti = &tracks[track_ridx];
audio_update_trackinfo();
if (tracks[track_ridx].filesize == 0)
{
audio_rebuffer();
}
else
{
if (direction > 0)
buffer_wind_forward();
else if (direction < 0)
{
/* This is how much 'back' data must remain uncleared for us
* to safely backup to the beginning of the previous track */
size_t req_size =
ci.curpos + prev_ti->codecsize + tracks[track_ridx].filesize;
/* This is the amount of 'back' data available on the buffer */
size_t buf_back = buf_ridx;
if (buf_back < buf_widx)
buf_back += filebuflen;
buf_back -= buf_widx;
/* If the track isn't on the buffer */
if (req_size > buf_back)
audio_rebuffer();
/* If the track needs to load its codec from the buffer */
else if (get_codec_base_type(prev_ti->id3.codectype) !=
get_codec_base_type(cur_ti->id3.codectype))
{
/* If the codec was never buffered */
if (!cur_ti->codecsize)
audio_rebuffer();
else
{
req_size += cur_ti->codecsize;
/* If the codec was overwritten */
if (req_size > buf_back)
audio_rebuffer();
else
{
cur_ti->has_codec = true;
buffer_wind_backward(true);
}
}
}
else
buffer_wind_backward(false);
}
}
mutex_unlock(&mutex_interthread);
}
static void rebuffer_and_seek(size_t newpos) static void rebuffer_and_seek(size_t newpos)
{ {
int fd; int fd;
@ -644,7 +781,7 @@ static void rebuffer_and_seek(size_t newpos)
fd = open(trackname, O_RDONLY); fd = open(trackname, O_RDONLY);
if (fd < 0) { if (fd < 0) {
logf("Open failed!"); logf("Open failed!");
mutex_unlock(&mutex_rebuffer); mutex_unlock(&mutex_interthread);
return; return;
} }
if (current_fd >= 0) if (current_fd >= 0)
@ -666,6 +803,7 @@ static void rebuffer_and_seek(size_t newpos)
/* Write to the now current track */ /* Write to the now current track */
track_widx = track_ridx; track_widx = track_ridx;
last_peek_offset = 0;
initialize_buffer_fill(false); initialize_buffer_fill(false);
cur_ti->filerem = cur_ti->filesize - cur_ti->start_pos; cur_ti->filerem = cur_ti->filesize - cur_ti->start_pos;
@ -673,7 +811,7 @@ static void rebuffer_and_seek(size_t newpos)
lseek(current_fd, cur_ti->start_pos, SEEK_SET); lseek(current_fd, cur_ti->start_pos, SEEK_SET);
mutex_unlock(&mutex_rebuffer); mutex_unlock(&mutex_interthread);
} }
void codec_advance_buffer_callback(size_t amount) void codec_advance_buffer_callback(size_t amount)
@ -695,11 +833,11 @@ void codec_advance_buffer_callback(size_t amount)
/* This should not happen */ /* This should not happen */
if (amount > cur_ti->available) { if (amount > cur_ti->available) {
mutex_lock(&mutex_rebuffer); mutex_lock(&mutex_interthread);
queue_post(&audio_queue, queue_post(&audio_queue,
Q_AUDIO_REBUFFER_SEEK, (void *)(ci.curpos + amount)); Q_AUDIO_REBUFFER_SEEK, (void *)(ci.curpos + amount));
mutex_lock(&mutex_rebuffer); mutex_lock(&mutex_interthread);
mutex_unlock(&mutex_rebuffer); mutex_unlock(&mutex_interthread);
return ; return ;
} }
@ -768,10 +906,10 @@ bool codec_seek_buffer_callback(size_t newpos)
/* We need to reload the song. */ /* We need to reload the song. */
if (newpos < cur_ti->start_pos) if (newpos < cur_ti->start_pos)
{ {
mutex_lock(&mutex_rebuffer); mutex_lock(&mutex_interthread);
queue_post(&audio_queue, Q_AUDIO_REBUFFER_SEEK, (void *)newpos); queue_post(&audio_queue, Q_AUDIO_REBUFFER_SEEK, (void *)newpos);
mutex_lock(&mutex_rebuffer); mutex_lock(&mutex_interthread);
mutex_unlock(&mutex_rebuffer); mutex_unlock(&mutex_interthread);
return true; return true;
} }
@ -929,10 +1067,6 @@ static void audio_read_file(void)
return ; return ;
} }
/* Throw away buffered codec. */
if (tracks[track_widx].start_pos != 0)
tracks[track_widx].codecsize = 0;
while (tracks[track_widx].filerem > 0) { while (tracks[track_widx].filerem > 0) {
if (fill_bytesleft <= 0) if (fill_bytesleft <= 0)
break ; break ;
@ -981,23 +1115,19 @@ static void audio_read_file(void)
} }
} }
static int get_codec_base_type(int type) static void codec_discard_codec_callback(void)
{ {
switch (type) { tracks[track_ridx].has_codec = false;
case AFMT_MPA_L1: filebufused -= tracks[track_ridx].codecsize;
case AFMT_MPA_L2: buf_ridx += tracks[track_ridx].codecsize;
case AFMT_MPA_L3: if (buf_ridx >= filebuflen)
return AFMT_MPA_L3; buf_ridx -= filebuflen;
}
return type;
} }
static bool loadcodec(bool start_play) static bool loadcodec(bool start_play)
{ {
size_t size; size_t size;
int fd; int fd;
unsigned int i;
int rc; int rc;
const char *codec_path; const char *codec_path;
size_t copy_n; size_t copy_n;
@ -1056,6 +1186,7 @@ static bool loadcodec(bool start_play)
return false; return false;
} }
tracks[track_widx].has_codec = false;
tracks[track_widx].codecsize = 0; tracks[track_widx].codecsize = 0;
if (start_play) if (start_play)
@ -1073,19 +1204,21 @@ static bool loadcodec(bool start_play)
} }
else else
{ {
/* If the previous codec is the same as this one, there is no need /* If we already have another track than this one buffered */
* to put another copy of it on the file buffer */ if (track_widx != track_ridx) {
prev_track = track_widx - 1; prev_track = track_widx - 1;
if (prev_track < 0) if (prev_track < 0)
prev_track = MAX_TRACK-1; prev_track += MAX_TRACK;
if (have_tracks() && /* If the previous codec is the same as this one, there is no need
get_codec_base_type(tracks[track_widx].id3.codectype) == * to put another copy of it on the file buffer */
if (get_codec_base_type(tracks[track_widx].id3.codectype) ==
get_codec_base_type(tracks[prev_track].id3.codectype)) get_codec_base_type(tracks[prev_track].id3.codectype))
{ {
logf("Reusing prev. codec"); logf("Reusing prev. codec");
return true; return true;
} }
} }
}
fd = open(codec_path, O_RDONLY); fd = open(codec_path, O_RDONLY);
if (fd < 0) if (fd < 0)
@ -1105,18 +1238,23 @@ static bool loadcodec(bool start_play)
return false; return false;
} }
i = 0; while (tracks[track_widx].codecsize < size) {
while (i < size) {
copy_n = MIN(conf_filechunk, filebuflen - buf_widx); copy_n = MIN(conf_filechunk, filebuflen - buf_widx);
rc = read(fd, &filebuf[buf_widx], copy_n); rc = read(fd, &filebuf[buf_widx], copy_n);
if (rc < 0) if (rc < 0)
return false; return false;
buf_widx += rc;
filebufused += rc; filebufused += rc;
if (fill_bytesleft > (unsigned)rc)
fill_bytesleft -= rc; fill_bytesleft -= rc;
else
fill_bytesleft = 0;
buf_widx += rc;
if (buf_widx >= filebuflen) if (buf_widx >= filebuflen)
buf_widx -= filebuflen; buf_widx -= filebuflen;
i += rc;
tracks[track_widx].codecsize += rc;
/* FIXME: This will spin around pretty quickly, but still requires /* FIXME: This will spin around pretty quickly, but still requires
* full read of the codec when an event is posted to the audio * full read of the codec when an event is posted to the audio
@ -1124,10 +1262,10 @@ static bool loadcodec(bool start_play)
yield_codecs(); yield_codecs();
} }
close(fd); tracks[track_widx].has_codec = true;
logf("Done: %dB", i);
tracks[track_widx].codecsize = size; close(fd);
logf("Done: %dB", size);
return true; return true;
} }
@ -1172,7 +1310,7 @@ static bool read_next_metadata(void)
return status; return status;
} }
static bool audio_load_track(int offset, bool start_play, int peek_offset) static bool audio_load_track(int offset, bool start_play)
{ {
char *trackname; char *trackname;
off_t size; off_t size;
@ -1181,8 +1319,10 @@ static bool audio_load_track(int offset, bool start_play, int peek_offset)
/* Stop buffer filling if there is no free track entries. /* Stop buffer filling if there is no free track entries.
Don't fill up the last track entry (we wan't to store next track Don't fill up the last track entry (we wan't to store next track
metadata there). */ metadata there). */
if (!have_free_tracks()) if (!have_free_tracks()) {
logf("No free tracks");
return false; return false;
}
if (current_fd >= 0) if (current_fd >= 0)
{ {
@ -1190,18 +1330,19 @@ static bool audio_load_track(int offset, bool start_play, int peek_offset)
current_fd = -1; current_fd = -1;
} }
/* TODO: BLOW: Need help on what playlist_skip_entry() does */
last_peek_offset++;
peek_again: peek_again:
logf("Buffering track:%d/%d", track_widx, track_ridx); logf("Buffering track:%d/%d", track_widx, track_ridx);
/* Get track name from current playlist read position. */ /* Get track name from current playlist read position. */
while ( (trackname = playlist_peek(peek_offset)) != NULL) { while ((trackname = playlist_peek(last_peek_offset)) != NULL) {
/* Handle broken playlists. */ /* Handle broken playlists. */
current_fd = open(trackname, O_RDONLY); current_fd = open(trackname, O_RDONLY);
if (current_fd < 0) { if (current_fd < 0) {
logf("Open failed"); logf("Open failed");
/* Skip invalid entry from playlist. */ /* Skip invalid entry from playlist. */
playlist_skip_entry(NULL, peek_offset); playlist_skip_entry(NULL, last_peek_offset);
continue ; } else
}
break; break;
} }
@ -1216,8 +1357,7 @@ static bool audio_load_track(int offset, bool start_play, int peek_offset)
tracks[track_widx].filerem = size; tracks[track_widx].filerem = size;
tracks[track_widx].filesize = size; tracks[track_widx].filesize = size;
tracks[track_widx].available = 0; tracks[track_widx].available = 0;
tracks[track_widx].playlist_offset = peek_offset; tracks[track_widx].playlist_offset = last_peek_offset;
last_peek_offset = peek_offset;
/* Set default values */ /* Set default values */
if (start_play) { if (start_play) {
@ -1240,9 +1380,10 @@ static bool audio_load_track(int offset, bool start_play, int peek_offset)
close(current_fd); close(current_fd);
current_fd = -1; current_fd = -1;
/* Skip invalid entry from playlist. */ /* Skip invalid entry from playlist. */
playlist_skip_entry(NULL, peek_offset); playlist_skip_entry(NULL, last_peek_offset);
goto peek_again; goto peek_again;
} }
track_changed = true;
} }
/* Load the codec. */ /* Load the codec. */
@ -1263,7 +1404,7 @@ static bool audio_load_track(int offset, bool start_play, int peek_offset)
/* We should not use gui_syncplash from audio thread! */ /* We should not use gui_syncplash from audio thread! */
gui_syncsplash(HZ*2, true, msgbuf); gui_syncsplash(HZ*2, true, msgbuf);
/* Skip invalid entry from playlist. */ /* Skip invalid entry from playlist. */
playlist_skip_entry(NULL, peek_offset); playlist_skip_entry(NULL, last_peek_offset);
goto peek_again; goto peek_again;
} }
return false; return false;
@ -1316,7 +1457,7 @@ static void audio_clear_track_entries(bool buffered_only)
int cur_idx = track_widx; int cur_idx = track_widx;
int last_idx = -1; int last_idx = -1;
logf("Clearing tracks:%d/%d", track_ridx, track_widx); logf("Clearing tracks:%d/%d, %d", track_ridx, track_widx, buffered_only);
/* Loop over all tracks from write-to-read */ /* Loop over all tracks from write-to-read */
while (1) { while (1) {
if (++cur_idx >= MAX_TRACK) if (++cur_idx >= MAX_TRACK)
@ -1367,8 +1508,8 @@ static void audio_stop_playback(bool resume)
playlist_update_resume_info(resume ? audio_current_track() : NULL); playlist_update_resume_info(resume ? audio_current_track() : NULL);
playing = false; playing = false;
filling = false; filling = false;
stop_codec_flush();
paused = false; paused = false;
stop_codec_flush();
if (current_fd >= 0) { if (current_fd >= 0) {
close(current_fd); close(current_fd);
current_fd = -1; current_fd = -1;
@ -1408,6 +1549,8 @@ static void generate_postbuffer_events(void)
int cur_idx; int cur_idx;
int last_idx = -1; int last_idx = -1;
logf("Postbuffer:%d/%d",track_ridx,track_widx);
if (have_tracks()) { if (have_tracks()) {
cur_idx = track_ridx; cur_idx = track_ridx;
while (1) { while (1) {
@ -1436,6 +1579,10 @@ static void generate_postbuffer_events(void)
static void initialize_buffer_fill(bool start_play) static void initialize_buffer_fill(bool start_play)
{ {
/* Initialize only once; do not truncate the tracks. */
if (filling)
return ;
if (start_play) { if (start_play) {
filling_initial = true; filling_initial = true;
fill_bytesleft = filebuflen >> 2; fill_bytesleft = filebuflen >> 2;
@ -1452,15 +1599,13 @@ static void initialize_buffer_fill(bool start_play)
fill_bytesleft = 0; fill_bytesleft = 0;
if (ci.curpos > AUDIO_REBUFFER_GUESS_SIZE) if (ci.curpos > AUDIO_REBUFFER_GUESS_SIZE)
cur_ti->start_pos = ci.curpos - AUDIO_REBUFFER_GUESS_SIZE; tracks[track_ridx].start_pos =
ci.curpos - AUDIO_REBUFFER_GUESS_SIZE;
else else
cur_ti->start_pos = 0; tracks[track_ridx].start_pos = 0;
} }
/* Initialize only once; do not truncate the tracks. */ logf("Starting buffer fill");
if (filling)
return ;
pcmbuf_set_boost_mode(true); pcmbuf_set_boost_mode(true);
/* Mark all buffered entries null (not metadata for next track). */ /* Mark all buffered entries null (not metadata for next track). */
@ -1475,17 +1620,13 @@ static void initialize_buffer_fill(bool start_play)
static void audio_fill_file_buffer(bool start_play, size_t offset) static void audio_fill_file_buffer(bool start_play, size_t offset)
{ {
if (!filling && !start_play)
if (ci.stop_codec || ci.reload_codec || playlist_end)
return;
initialize_buffer_fill(start_play); initialize_buffer_fill(start_play);
/* If we have a partially buffered track, continue loading, /* If we have a partially buffered track, continue loading,
* otherwise load a new track */ * otherwise load a new track */
if (tracks[track_widx].filesize > 0) if (tracks[track_widx].filesize > 0)
audio_read_file(); audio_read_file();
else if (!audio_load_track(offset, start_play, last_peek_offset + 1)) else if (!audio_load_track(offset, start_play))
fill_bytesleft = 0; fill_bytesleft = 0;
/* Read next unbuffered track's metadata as soon as playback begins */ /* Read next unbuffered track's metadata as soon as playback begins */
@ -1509,31 +1650,21 @@ static void audio_fill_file_buffer(bool start_play, size_t offset)
} }
} }
static void audio_update_trackinfo(void) static void track_skip_done(void)
{ {
ci.filesize = cur_ti->filesize;
cur_ti->id3.elapsed = 0;
cur_ti->id3.offset = 0;
ci.id3 = (struct mp3entry *)&cur_ti->id3;
ci.curpos = 0;
cur_ti->start_pos = 0;
ci.taginfo_ready = (bool *)&cur_ti->taginfo_ready;
/* Manual track change (always crossfade or flush audio). */ /* Manual track change (always crossfade or flush audio). */
if (new_track) if (new_track)
{ {
pcmbuf_crossfade_init(true); pcmbuf_crossfade_init(true);
codec_track_changed(); codec_track_changed();
} }
/* Automatic track change w/crossfade, if not in "Track Skip Only" mode. */
/* Automatic track change with crossfade, if not in "Track Skip Only" mode. */
else if (pcmbuf_is_crossfade_enabled() && !pcmbuf_is_crossfade_active() else if (pcmbuf_is_crossfade_enabled() && !pcmbuf_is_crossfade_active()
&& global_settings.crossfade != 2) && global_settings.crossfade != 2)
{ {
pcmbuf_crossfade_init(false); pcmbuf_crossfade_init(false);
codec_track_changed(); codec_track_changed();
} }
/* Gapless playback. */ /* Gapless playback. */
else else
{ {
@ -1541,26 +1672,18 @@ static void audio_update_trackinfo(void)
} }
} }
enum { static bool skip_next_track(void)
SKIP_FAIL,
SKIP_OK_DISK,
SKIP_OK_RAM,
};
/* Should handle all situations. */
static int skip_next_track(bool inside_codec_thread)
{ {
logf("skip next"); logf("skip next");
/* Manual track skipping. */
if (new_track > 0)
last_peek_offset--;
/* Automatic track skipping. */ /* Automatic track skipping. */
else if (new_track == 0)
{ {
pcmbuf_set_position_callback(pcmbuf_position_callback);
if (!playlist_check(1)) { if (!playlist_check(1)) {
ci.stop_codec = true;
ci.reload_codec = false; ci.reload_codec = false;
return SKIP_FAIL; return false;
} }
last_peek_offset--; last_peek_offset--;
playlist_next(1); playlist_next(1);
@ -1569,127 +1692,33 @@ static int skip_next_track(bool inside_codec_thread)
if (++track_ridx >= MAX_TRACK) if (++track_ridx >= MAX_TRACK)
track_ridx = 0; track_ridx = 0;
/* Wait for new track data. */ mutex_lock(&mutex_interthread);
while (tracks[track_ridx].filesize == 0 && filling) queue_post(&audio_queue, Q_AUDIO_CHECK_NEW_TRACK, (void *)1);
{ mutex_lock(&mutex_interthread);
yield(); mutex_unlock(&mutex_interthread);
if (ci.stop_codec)
return SKIP_FAIL; track_skip_done();
return true;
} }
if (tracks[track_ridx].filesize <= 0) static void skip_previous_track(void)
{
logf("Loading from disk...");
ci.reload_codec = true;
/* Stop playback if manual track change. */
if (new_track != 0 && !pcmbuf_is_crossfade_enabled())
{
if (inside_codec_thread)
pcmbuf_play_stop();
else
stop_codec_flush();
}
else if (pcmbuf_is_crossfade_enabled())
pcmbuf_crossfade_init(new_track != 0);
queue_post(&audio_queue, Q_AUDIO_PLAY, 0);
return SKIP_OK_DISK;
}
/* Wind the buffer forward to the end of the current track */
buf_ridx += cur_ti->available;
filebufused -= cur_ti->available;
/* Move to the new track */
cur_ti = &tracks[track_ridx];
/* Wind past the new track's codec */
buf_ridx += cur_ti->codecsize;
filebufused -= cur_ti->codecsize;
/* Check and handle buffer wrapping */
if (buf_ridx >= filebuflen)
buf_ridx -= filebuflen;
audio_update_trackinfo();
return SKIP_OK_RAM;
}
static int skip_previous_track(bool inside_codec_thread)
{ {
logf("skip previous"); logf("skip previous");
last_peek_offset++;
if (--track_ridx < 0) if (--track_ridx < 0)
track_ridx += MAX_TRACK; track_ridx += MAX_TRACK;
if (tracks[track_ridx].filesize == 0 || mutex_lock(&mutex_interthread);
filebufused + ci.curpos + tracks[track_ridx].filesize > filebuflen) queue_post(&audio_queue, Q_AUDIO_CHECK_NEW_TRACK, (void *)-1);
{ mutex_lock(&mutex_interthread);
logf("Loading from disk..."); mutex_unlock(&mutex_interthread);
ci.reload_codec = true;
/* Stop playback. */
/* FIXME: Only stop playback if disk is not spinning! */
if (pcmbuf_is_crossfade_enabled())
pcmbuf_crossfade_init(true);
else if (inside_codec_thread)
pcmbuf_play_stop();
else
stop_codec_flush();
queue_post(&audio_queue, Q_AUDIO_PLAY, 0); track_skip_done();
return SKIP_OK_DISK;
}
/* Rewind the buffer to the beginning of the playing track */
buf_ridx -= ci.curpos + cur_ti->codecsize;
filebufused += ci.curpos + cur_ti->codecsize;
/* Rewind the track to its beginning */
cur_ti->available = cur_ti->filesize - cur_ti->filerem;
/* Move to the new track */
cur_ti = &tracks[track_ridx];
/* Rewind the buffer to the beginning of the new track */
buf_ridx -= cur_ti->filesize;
filebufused += cur_ti->filesize;
/* Reset to the beginning of the new track */
cur_ti->available = cur_ti->filesize;
/* Check and handle buffer wrapping */
if (buf_ridx <= 0)
buf_ridx += filebuflen;
audio_update_trackinfo();
return SKIP_OK_RAM;
}
/* Request the next track with new codec. */
static void audio_change_track(void)
{
logf("change track");
if (!ci.reload_codec)
{
if (skip_next_track(false) == SKIP_FAIL)
{
logf("No more tracks");
while (pcm_is_playing())
sleep(1);
audio_stop_playback(false);
return ;
}
}
ci.reload_codec = false;
/* Needed for fast skipping. */
if (cur_ti->codecsize > 0)
queue_post(&codec_queue, Q_CODEC_LOAD, 0);
} }
bool codec_request_next_track_callback(void) bool codec_request_next_track_callback(void)
{ {
prev_ti = cur_ti;
if (current_codec == CODEC_IDX_VOICE) { if (current_codec == CODEC_IDX_VOICE) {
voice_remaining = 0; voice_remaining = 0;
/* Terminate the codec if there are messages waiting on the queue or /* Terminate the codec if there are messages waiting on the queue or
@ -1704,48 +1733,34 @@ bool codec_request_next_track_callback(void)
ab_end_of_track_report(); ab_end_of_track_report();
#endif #endif
if (!new_track)
pcmbuf_set_position_callback(pcmbuf_position_callback);
logf("Request new track"); logf("Request new track");
/* Advance to next track. */
if (new_track >= 0 || !ci.reload_codec) { if (new_track >= 0)
if (skip_next_track(true) != SKIP_OK_RAM) {
if (!skip_next_track())
return false; return false;
} }
else
/* Advance to previous track. */ {
else { skip_previous_track();
if (skip_previous_track(true) != SKIP_OK_RAM)
return false;
} }
new_track = 0;
ci.reload_codec = false;
logf("On-the-fly change");
/* Check if the next codec is the same file. */ /* Check if the next codec is the same file. */
if (get_codec_base_type(prev_ti->id3.codectype) != if (get_codec_base_type(prev_ti->id3.codectype) !=
get_codec_base_type(cur_ti->id3.codectype)) get_codec_base_type(cur_ti->id3.codectype))
{ {
logf("New codec:%d/%d", cur_ti->id3.codectype, logf("New codec:%d/%d", cur_ti->id3.codectype, prev_ti->id3.codectype);
tracks[track_ridx].id3.codectype);
if (cur_ti->codecsize == 0)
{
logf("Loading from disk [2]...");
queue_post(&audio_queue, Q_AUDIO_PLAY, 0);
}
else
ci.reload_codec = true; ci.reload_codec = true;
return false; return false;
} }
else
{
logf("New track loaded");
ci.reload_codec = false;
return true; return true;
} }
}
/* Invalidates all but currently playing track. */ /* Invalidates all but currently playing track. */
void audio_invalidate_tracks(void) void audio_invalidate_tracks(void)
@ -1759,7 +1774,7 @@ void audio_invalidate_tracks(void)
audio_clear_track_entries(false); audio_clear_track_entries(false);
/* If the current track is fully buffered, advance the write pointer */ /* If the current track is fully buffered, advance the write pointer */
if (tracks[track_ridx].filerem == 0) if (tracks[track_widx].filerem == 0)
if (++track_widx >= MAX_TRACK) if (++track_widx >= MAX_TRACK)
track_widx -= MAX_TRACK; track_widx -= MAX_TRACK;
@ -1775,30 +1790,16 @@ void audio_invalidate_tracks(void)
static void initiate_track_change(long peek_index) static void initiate_track_change(long peek_index)
{ {
/* Detect if disk is spinning or already loading. */
if (filling || ci.reload_codec || !audio_codec_loaded) {
if (pcmbuf_is_crossfade_enabled()) {
pcmbuf_crossfade_init(true);
ci.stop_codec = true;
} else
stop_codec_flush();
queue_post(&audio_queue, Q_AUDIO_PLAY, 0);
} else {
new_track = peek_index; new_track = peek_index;
ci.reload_codec = true; ci.reload_codec = true;
} }
codec_track_changed();
}
static void initiate_dir_change(long direction) static void initiate_dir_change(long direction)
{ {
if(!playlist_next_dir(direction)) if(!playlist_next_dir(direction))
return; return;
queue_post(&audio_queue, Q_AUDIO_PLAY, (bool *)true); queue_post(&audio_queue, Q_AUDIO_PLAY, 0);
codec_track_changed();
} }
void audio_thread(void) void audio_thread(void)
@ -1817,7 +1818,7 @@ void audio_thread(void)
if (ev.id == SYS_TIMEOUT) if (ev.id == SYS_TIMEOUT)
{ {
ev.id = Q_AUDIO_PLAY; ev.id = Q_AUDIO_PLAY;
ev.data = (bool *)1; ev.data = 0;
} }
} }
else if (filling) else if (filling)
@ -1835,6 +1836,9 @@ void audio_thread(void)
switch (ev.id) { switch (ev.id) {
case Q_AUDIO_FILL_BUFFER: case Q_AUDIO_FILL_BUFFER:
if (!filling && !(bool)ev.data)
if (ci.stop_codec || ci.reload_codec || playlist_end)
break;
audio_fill_file_buffer((bool)ev.data, abs((long)ev.data)); audio_fill_file_buffer((bool)ev.data, abs((long)ev.data));
break; break;
case Q_AUDIO_PLAY: case Q_AUDIO_PLAY:
@ -1905,10 +1909,15 @@ void audio_thread(void)
break ; break ;
case Q_AUDIO_REBUFFER_SEEK: case Q_AUDIO_REBUFFER_SEEK:
logf("Re-buffering song"); logf("Re-buffering song w/seek");
rebuffer_and_seek((size_t)ev.data); rebuffer_and_seek((size_t)ev.data);
break; break;
case Q_AUDIO_CHECK_NEW_TRACK:
logf("Check new track buffer");
audio_check_new_track((long)ev.data);
break;
case Q_AUDIO_DIR_SKIP: case Q_AUDIO_DIR_SKIP:
logf("audio_dir_skip"); logf("audio_dir_skip");
playlist_end = false; playlist_end = false;
@ -1918,6 +1927,7 @@ void audio_thread(void)
break; break;
case Q_AUDIO_FLUSH: case Q_AUDIO_FLUSH:
logf("flush & reload");
audio_invalidate_tracks(); audio_invalidate_tracks();
break ; break ;
@ -1926,6 +1936,7 @@ void audio_thread(void)
track_changed_callback(&cur_ti->id3); track_changed_callback(&cur_ti->id3);
playlist_update_resume_info(audio_current_track()); playlist_update_resume_info(audio_current_track());
pcmbuf_set_position_callback(NULL); pcmbuf_set_position_callback(NULL);
track_changed = true;
break ; break ;
case Q_AUDIO_CODEC_DONE: case Q_AUDIO_CODEC_DONE:
@ -1948,9 +1959,8 @@ void audio_thread(void)
void codec_thread(void) void codec_thread(void)
{ {
struct event ev; struct event ev;
long codecsize;
int status; int status;
int wrap; size_t wrap;
while (1) { while (1) {
status = 0; status = 0;
@ -1969,22 +1979,23 @@ void codec_thread(void)
case Q_CODEC_LOAD: case Q_CODEC_LOAD:
logf("Codec start"); logf("Codec start");
codecsize = cur_ti->codecsize; if (!cur_ti->has_codec) {
if (codecsize == 0) {
logf("Codec slot is empty!"); logf("Codec slot is empty!");
/* Wait for the pcm buffer to go empty */ /* Wait for the pcm buffer to go empty */
while (pcm_is_playing()) while (pcm_is_playing())
yield(); yield();
audio_stop_playback(true); /* This must be set to prevent an infinite loop */
ci.stop_codec = true;
queue_post(&codec_queue, Q_AUDIO_STOP, 0);
break ; break ;
} }
ci.stop_codec = false; ci.stop_codec = false;
wrap = (long)&filebuf[filebuflen] - (long)cur_ti->codecbuf; wrap = (size_t)&filebuf[filebuflen] - (size_t)cur_ti->codecbuf;
audio_codec_loaded = true; audio_codec_loaded = true;
mutex_lock(&mutex_codecthread); mutex_lock(&mutex_codecthread);
current_codec = CODEC_IDX_AUDIO; current_codec = CODEC_IDX_AUDIO;
status = codec_load_ram(cur_ti->codecbuf, codecsize, status = codec_load_ram(cur_ti->codecbuf, cur_ti->codecsize,
&filebuf[0], wrap, &ci); &filebuf[0], wrap, &ci);
mutex_unlock(&mutex_codecthread); mutex_unlock(&mutex_codecthread);
break ; break ;
@ -2012,16 +2023,25 @@ void codec_thread(void)
switch (ev.id) { switch (ev.id) {
case Q_CODEC_LOAD_DISK: case Q_CODEC_LOAD_DISK:
case Q_CODEC_LOAD: case Q_CODEC_LOAD:
if (status != CODEC_OK) {
logf("Codec failure");
ci.reload_codec = false; ci.reload_codec = false;
gui_syncsplash(HZ*2, true, "Codec failure"); if (playing) {
} else { if (new_track || status == CODEC_OK) {
logf("Codec finished"); logf("Codec finished");
if (ci.stop_codec)
queue_post(&audio_queue, Q_AUDIO_STOP, 0);
else
queue_post(&codec_queue, Q_CODEC_LOAD, 0);
} else {
logf("Codec failure");
gui_syncsplash(HZ*2, true, "Codec failure");
if (ci.stop_codec)
queue_post(&audio_queue, Q_AUDIO_STOP, 0);
else if (skip_next_track())
queue_post(&codec_queue, Q_CODEC_LOAD, 0);
else
queue_post(&audio_queue, Q_AUDIO_STOP, 0);
}
} }
if (playing && !ci.stop_codec)
audio_change_track();
} }
} }
} }
@ -2219,8 +2239,9 @@ static void audio_skip(long count) {
if (!playlist_check(count)) if (!playlist_check(count))
return ; return ;
last_peek_offset += -count;
playlist_next(count); playlist_next(count);
track_changed = true;
queue_post(&audio_queue, Q_AUDIO_SKIP, (void *)count); queue_post(&audio_queue, Q_AUDIO_SKIP, (void *)count);
} }
@ -2257,7 +2278,6 @@ void audio_ff_rewind(long newpos)
void audio_flush_and_reload_tracks(void) void audio_flush_and_reload_tracks(void)
{ {
logf("flush & reload");
queue_post(&audio_queue, Q_AUDIO_FLUSH, 0); queue_post(&audio_queue, Q_AUDIO_FLUSH, 0);
} }
@ -2528,6 +2548,7 @@ static void playback_init(void)
ci.set_elapsed = codec_set_elapsed_callback; ci.set_elapsed = codec_set_elapsed_callback;
ci.set_offset = codec_set_offset_callback; ci.set_offset = codec_set_offset_callback;
ci.configure = codec_configure_callback; ci.configure = codec_configure_callback;
ci.discard_codec = codec_discard_codec_callback;
memcpy(&ci_voice, &ci, sizeof(struct codec_api)); memcpy(&ci_voice, &ci, sizeof(struct codec_api));
memset(&id3_voice, 0, sizeof(struct mp3entry)); memset(&id3_voice, 0, sizeof(struct mp3entry));
@ -2587,7 +2608,7 @@ void audio_preinit(void)
cur_ti = &tracks[0]; cur_ti = &tracks[0];
mutex_init(&mutex_codecthread); mutex_init(&mutex_codecthread);
mutex_init(&mutex_rebuffer); mutex_init(&mutex_interthread);
queue_init(&audio_queue); queue_init(&audio_queue);
queue_init(&codec_queue); queue_init(&codec_queue);

View file

@ -38,14 +38,17 @@ struct track_info {
struct mp3entry id3; /* TAG metadata */ struct mp3entry id3; /* TAG metadata */
char *codecbuf; /* Pointer to codec buffer */ char *codecbuf; /* Pointer to codec buffer */
size_t codecsize; /* Codec length in bytes */ size_t codecsize; /* Codec length in bytes */
bool has_codec; /* Does this track have a codec on the buffer */
size_t filerem; /* Remaining bytes of file NOT in buffer */ size_t filerem; /* Remaining bytes of file NOT in buffer */
size_t filesize; /* File total length */ size_t filesize; /* File total length */
size_t start_pos; /* Position to first bytes of file in buffer */ size_t start_pos; /* Position to first bytes of file in buffer */
volatile size_t available; /* Available bytes to read from buffer */ volatile size_t available; /* Available bytes to read from buffer */
bool taginfo_ready; /* Is metadata read */ bool taginfo_ready; /* Is metadata read */
int playlist_offset; /* File location in playlist */ int playlist_offset; /* File location in playlist */
bool event_sent; /* Has event callback functions been called? */
bool event_sent; /* Was this track's buffered event sent */
}; };
/* Functions */ /* Functions */