summaryrefslogtreecommitdiffstats
path: root/apps/playback.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/playback.c')
-rw-r--r--apps/playback.c487
1 files changed, 339 insertions, 148 deletions
diff --git a/apps/playback.c b/apps/playback.c
index 5a6f18735b..7ea44a047f 100644
--- a/apps/playback.c
+++ b/apps/playback.c
@@ -172,12 +172,17 @@ static struct mutex id3_mutex SHAREDBSS_ATTR; /* (A,O)*/
#define MAX_MULTIPLE_AA SKINNABLE_SCREENS_COUNT
#ifdef HAVE_ALBUMART
+static int albumart_mode = -1;
+
static struct albumart_slot
{
struct dim dim; /* Holds width, height of the albumart */
int used; /* Counter; increments if something uses it */
} albumart_slots[MAX_MULTIPLE_AA]; /* (A,O) */
+static char last_folder_aa_path[MAX_PATH] = "\0";
+static int last_folder_aa_hid[MAX_MULTIPLE_AA] = {0};
+
#define FOREACH_ALBUMART(i) for (int i = 0; i < MAX_MULTIPLE_AA; i++)
#endif /* HAVE_ALBUMART */
@@ -194,6 +199,7 @@ static enum filling_state
STATE_FINISHED, /* all remaining tracks are fully buffered */
STATE_ENDING, /* audio playback is ending */
STATE_ENDED, /* audio playback is done */
+ STATE_STOPPED, /* buffering is stopped explicitly */
} filling = STATE_IDLE;
/* Track info - holds information about each track in the buffer */
@@ -310,6 +316,8 @@ static unsigned int track_event_flags = TEF_NONE; /* (A, O-) */
/* Pending manual track skip offset */
static int skip_offset = 0; /* (A, O) */
+static bool track_skip_is_manual = false;
+
/* Track change notification */
static struct
{
@@ -339,7 +347,6 @@ enum audio_start_playback_flags
{
AUDIO_START_RESTART = 0x1, /* "Restart" playback (flush _all_ tracks) */
AUDIO_START_NEWBUF = 0x2, /* Mark the audiobuffer as invalid */
- AUDIO_START_REFRESH = 0x80
};
static void audio_start_playback(const struct audio_resume_info *resume_info,
@@ -588,6 +595,20 @@ static bool track_list_commit_buf_info(struct track_buf_info *tbip,
return true;
}
+#ifdef HAVE_ALBUMART
+static inline void clear_cached_aa_handles(int* aa_handles)
+{
+ if (last_folder_aa_path[0] == 0)
+ return;
+
+ FOREACH_ALBUMART(i)
+ {
+ if (aa_handles[i] == last_folder_aa_hid[i])
+ aa_handles[i] = 0;
+ }
+}
+#endif //HAVE_ALBUMART
+
/* Free the track buffer entry and possibly remove it from the list if it
was succesfully added at some point */
static void track_list_free_buf_info(struct track_buf_info *tbip)
@@ -629,6 +650,9 @@ static void track_list_free_buf_info(struct track_buf_info *tbip)
/* No movement allowed during bufclose calls */
buf_pin_handle(hid, true);
+#ifdef HAVE_ALBUMART
+ clear_cached_aa_handles(tbip->info.aa_hid);
+#endif
FOR_EACH_TRACK_INFO_HANDLE(i)
bufclose(tbip->info.handle[i]);
@@ -816,6 +840,21 @@ size_t audio_buffer_available(void)
return MAX(core_size, size);
}
+#ifdef HAVE_ALBUMART
+static void clear_last_folder_album_art(void)
+{
+ if(last_folder_aa_path[0] == 0)
+ return;
+
+ last_folder_aa_path[0] = 0;
+ FOREACH_ALBUMART(i)
+ {
+ bufclose(last_folder_aa_hid[i]);
+ last_folder_aa_hid[i] = 0;
+ }
+}
+#endif
+
/* Set up the audio buffer for playback
* filebuflen must be pre-initialized with the maximum size */
static void audio_reset_buffer_noalloc(
@@ -852,6 +891,10 @@ static void audio_reset_buffer_noalloc(
filebuf += allocsize;
filebuflen -= allocsize;
+#ifdef HAVE_ALBUMART
+ clear_last_folder_album_art();
+#endif
+
buffering_reset(filebuf, filebuflen);
buffer_state = AUDIOBUF_STATE_INITIALIZED;
@@ -995,7 +1038,7 @@ static void audio_reset_buffer(void)
}
if (core_allocatable() < (1 << 10))
talk_buffer_set_policy(TALK_BUFFER_LOOSE); /* back off voice buffer */
- audiobuf_handle = core_alloc_maximum("audiobuf", &filebuflen, &ops);
+ audiobuf_handle = core_alloc_maximum(&filebuflen, &ops);
if (audiobuf_handle > 0)
audio_reset_buffer_noalloc(core_get_data(audiobuf_handle));
@@ -1209,41 +1252,35 @@ static void audio_update_and_announce_next_track(const struct mp3entry *id3_next
/* Bring the user current mp3entry up to date and set a new offset for the
buffered metadata */
-static void playing_id3_sync(struct track_info *user_infop,
- unsigned long elapsed, unsigned long offset)
+static void playing_id3_sync(struct track_info *user_infop, struct audio_resume_info *resume_info, bool skip_resume_adjustments)
{
id3_mutex_lock();
struct mp3entry *id3 = bufgetid3(user_infop->id3_hid);
- struct mp3entry *playing_id3 = id3_get(PLAYING_ID3);
pcm_play_lock();
- unsigned long e = playing_id3->elapsed;
- unsigned long o = playing_id3->offset;
-
- id3_write(PLAYING_ID3, id3);
-
- if (elapsed == (unsigned long)-1)
+ if (id3)
{
- playing_id3->elapsed = e;
- elapsed = 0;
+ if (resume_info)
+ {
+ id3->elapsed = resume_info->elapsed;
+ id3->offset = resume_info->offset;
+ }
+ id3->skip_resume_adjustments = skip_resume_adjustments;
}
+
+ id3_write(PLAYING_ID3, id3);
- if (offset == (unsigned long)-1)
+ if (!resume_info && id3)
{
- playing_id3->offset = o;
- offset = 0;
+ id3->offset = 0;
+ id3->elapsed = 0;
+ id3->skip_resume_adjustments = false;
}
pcm_play_unlock();
- if (id3)
- {
- id3->elapsed = elapsed;
- id3->offset = offset;
- }
-
id3_mutex_unlock();
}
@@ -1481,7 +1518,7 @@ static bool audio_init_codec(struct track_info *track_infop,
enum { AUTORESUMABLE_UNKNOWN = 0, AUTORESUMABLE_TRUE, AUTORESUMABLE_FALSE };
static bool autoresumable(struct mp3entry *id3)
{
- char *endp, *path;
+ char *path;
size_t len;
bool is_resumable;
@@ -1499,13 +1536,7 @@ static bool autoresumable(struct mp3entry *id3)
if (*path == ':') /* Skip empty search patterns */
continue;
- /* FIXME: As soon as strcspn or strchrnul are made available in
- the core, the following can be made more efficient. */
- endp = strchr(path, ':');
- if (endp)
- len = endp - path;
- else
- len = strlen(path);
+ len = strcspn(path, ":");
/* Note: At this point, len is always > 0 */
@@ -1553,8 +1584,8 @@ static bool audio_start_codec(bool auto_skip)
return false;
}
-#ifdef HAVE_TAGCACHE
- bool autoresume_enable = global_settings.autoresume_enable;
+ #ifdef HAVE_TAGCACHE
+ bool autoresume_enable = !cur_id3->skip_resume_adjustments && global_settings.autoresume_enable;
if (autoresume_enable && !(cur_id3->elapsed || cur_id3->offset))
{
@@ -1604,8 +1635,11 @@ static bool audio_start_codec(bool auto_skip)
and back again will cause accumulation of silent rewinds - that's not
our job to track directly nor could it be in any reasonable way
*/
- resume_rewind_adjust_progress(cur_id3, &cur_id3->elapsed,
- &cur_id3->offset);
+ if (!cur_id3->skip_resume_adjustments)
+ {
+ resume_rewind_adjust_progress(cur_id3, &cur_id3->elapsed, &cur_id3->offset);
+ cur_id3->skip_resume_adjustments = true;
+ }
/* Update the codec API with the metadata and track info */
id3_write(CODEC_ID3, cur_id3);
@@ -1690,15 +1724,47 @@ static bool audio_load_cuesheet(struct track_info *infop,
}
#ifdef HAVE_ALBUMART
+
+void set_albumart_mode(int setting)
+{
+ if (albumart_mode != -1 &&
+ albumart_mode != setting)
+ playback_update_aa_dims();
+ albumart_mode = setting;
+}
+
+static int load_album_art_from_path(char *path, struct bufopen_bitmap_data *user_data, bool is_current_track, int i)
+{
+ user_data->embedded_albumart = NULL;
+
+ bool same_path = strcmp(last_folder_aa_path, path) == 0;
+ if (same_path && last_folder_aa_hid[i] != 0)
+ return last_folder_aa_hid[i];
+
+ // To simplify caching logic a bit we keep track only for first AA path
+ // If other album arts use different path (like dimension specific arts) just skip caching for them
+ bool is_cacheable = i == 0 && (is_current_track || last_folder_aa_path[0] == 0);
+ if (!same_path && is_cacheable)
+ {
+ clear_last_folder_album_art();
+ strcpy(last_folder_aa_path, path);
+ }
+ int hid = bufopen(path, 0, TYPE_BITMAP, user_data);
+ if (hid != ERR_BUFFER_FULL && (same_path || is_cacheable))
+ last_folder_aa_hid[i] = hid;
+ return hid;
+}
+
/* Load any album art for the file - returns false if the buffer is full */
static int audio_load_albumart(struct track_info *infop,
- struct mp3entry *track_id3)
+ struct mp3entry *track_id3, bool is_current_track)
{
FOREACH_ALBUMART(i)
{
struct bufopen_bitmap_data user_data;
int *aa_hid = &infop->aa_hid[i];
int hid = ERR_UNSUPPORTED_TYPE;
+ bool checked_image_file = false;
/* albumart_slots may change during a yield of bufopen,
* but that's no problem */
@@ -1709,23 +1775,36 @@ static int audio_load_albumart(struct track_info *infop,
memset(&user_data, 0, sizeof(user_data));
user_data.dim = &albumart_slots[i].dim;
+ char path[MAX_PATH];
+ if(global_settings.album_art == AA_PREFER_IMAGE_FILE)
+ {
+ if (find_albumart(track_id3, path, sizeof(path),
+ &albumart_slots[i].dim))
+ {
+ hid = load_album_art_from_path(path, &user_data, is_current_track, i);
+ }
+ checked_image_file = true;
+ }
+
/* We can only decode jpeg for embedded AA */
- if (track_id3->has_embedded_albumart && track_id3->albumart.type == AA_TYPE_JPG)
+ if (global_settings.album_art != AA_OFF &&
+ hid < 0 && hid != ERR_BUFFER_FULL &&
+ track_id3->has_embedded_albumart && track_id3->albumart.type == AA_TYPE_JPG)
{
+ if (is_current_track)
+ clear_last_folder_album_art();
user_data.embedded_albumart = &track_id3->albumart;
hid = bufopen(track_id3->path, 0, TYPE_BITMAP, &user_data);
}
- if (hid < 0 && hid != ERR_BUFFER_FULL)
+ if (global_settings.album_art != AA_OFF && !checked_image_file &&
+ hid < 0 && hid != ERR_BUFFER_FULL)
{
/* No embedded AA or it couldn't be loaded - try other sources */
- char path[MAX_PATH];
-
if (find_albumart(track_id3, path, sizeof(path),
&albumart_slots[i].dim))
{
- user_data.embedded_albumart = NULL;
- hid = bufopen(path, 0, TYPE_BITMAP, &user_data);
+ hid = load_album_art_from_path(path, &user_data, is_current_track, i);
}
}
@@ -1872,10 +1951,39 @@ static int audio_load_track(void)
playlist_peek_offset);
/* Get track name from current playlist read position */
+ int fd = -1;
char path_buf[MAX_PATH + 1];
- const char *path = playlist_peek(playlist_peek_offset,
- path_buf,
- sizeof (path_buf));
+ const char *path;
+
+ while (1)
+ {
+ path = playlist_peek(playlist_peek_offset,
+ path_buf,
+ sizeof (path_buf));
+
+ if (!path)
+ break;
+
+ /* Test for broken playlists by probing for the files */
+ if (file_exists(path))
+ {
+ fd = open(path, O_RDONLY);
+ if (fd >= 0)
+ break;
+ }
+ logf("Open failed %s", path);
+
+ /* only skip if failed track has a successor in playlist */
+ if (!playlist_peek(playlist_peek_offset + 1, NULL, 0))
+ break;
+
+ /* Skip invalid entry from playlist */
+ playlist_skip_entry(NULL, playlist_peek_offset);
+
+ /* Sync the playlist if it isn't finished */
+ if (playlist_peek(playlist_peek_offset, NULL, 0))
+ playlist_next(0);
+ }
if (!path)
{
@@ -1911,14 +2019,12 @@ static int audio_load_track(void)
/* Load the metadata for the first unbuffered track */
ub_id3 = id3_get(UNBUFFERED_ID3);
- int fd = open(path, O_RDONLY);
if (fd >= 0)
{
id3_mutex_lock();
if(!get_metadata(ub_id3, fd, path))
wipe_mp3entry(ub_id3);
id3_mutex_unlock();
- close(fd);
}
if (filling != STATE_FULL)
@@ -1936,16 +2042,23 @@ static int audio_load_track(void)
{
track_list_free_info(&info);
track_list.in_progress_hid = 0;
+ if (fd >= 0)
+ close(fd);
return LOAD_TRACK_ERR_FAILED;
}
/* Successful load initiation */
track_list.in_progress_hid = info.self_hid;
}
-
+ if (fd >= 0)
+ close(fd);
return LOAD_TRACK_OK;
}
+#ifdef HAVE_PLAY_FREQ
+static bool audio_auto_change_frequency(struct mp3entry *id3, bool play);
+#endif
+
/* Second part of the track loading: We now have the metadata available, so we
can load the codec, the album art and finally the audio data.
This is called on the audio thread after the buffering thread calls the
@@ -1978,6 +2091,24 @@ static int audio_finish_load_track(struct track_info *infop)
goto audio_finish_load_track_exit;
}
+ struct track_info user_cur;
+
+#ifdef HAVE_PLAY_FREQ
+ track_list_user_current(0, &user_cur);
+ bool is_current_user = infop->self_hid == user_cur.self_hid;
+ if (audio_auto_change_frequency(track_id3, is_current_user))
+ {
+ // frequency switch requires full re-buffering, so stop buffering
+ filling = STATE_STOPPED;
+ logf("buffering stopped (current_track: %b, current_user: %b)", infop->self_hid == cur_info.self_hid, is_current_user);
+ if (is_current_user)
+ // audio_finish_load_track_exit not needed as playback restart is already initiated
+ return trackstat;
+
+ goto audio_finish_load_track_exit;
+ }
+#endif
+
/* Try to load a cuesheet for the track */
if (!audio_load_cuesheet(infop, track_id3))
{
@@ -1988,7 +2119,7 @@ static int audio_finish_load_track(struct track_info *infop)
#ifdef HAVE_ALBUMART
/* Try to load album art for the track */
- int retval = audio_load_albumart(infop, track_id3);
+ int retval = audio_load_albumart(infop, track_id3, infop->self_hid == cur_info.self_hid);
if (retval == ERR_BITMAP_TOO_LARGE)
{
/* No space for album art on buffer because the file is larger than the buffer.
@@ -2004,7 +2135,6 @@ static int audio_finish_load_track(struct track_info *infop)
/* All handles available to external routines are ready - audio and codec
information is private */
- struct track_info user_cur;
track_list_user_current(0, &user_cur);
if (infop->self_hid == user_cur.self_hid)
{
@@ -2042,23 +2172,11 @@ static int audio_finish_load_track(struct track_info *infop)
/** Finally, load the audio **/
off_t file_offset = 0;
- if (track_id3->elapsed > track_id3->length)
- track_id3->elapsed = 0;
-
- if ((off_t)track_id3->offset >= buf_filesize(infop->audio_hid))
- track_id3->offset = 0;
-
- logf("%s: set offset for %s to %lu\n", __func__,
- track_id3->title, track_id3->offset);
-
/* Adjust for resume rewind so we know what to buffer - starting the codec
calls it again, so we don't save it (and they shouldn't accumulate) */
unsigned long elapsed, offset;
resume_rewind_adjust_progress(track_id3, &elapsed, &offset);
- logf("%s: Set resume for %s to %lu %lu", __func__,
- track_id3->title, elapsed, offset);
-
enum data_type audiotype = rbcodec_format_is_atomic(track_id3->codectype) ?
TYPE_ATOMIC_AUDIO : TYPE_PACKET_AUDIO;
@@ -2090,6 +2208,19 @@ static int audio_finish_load_track(struct track_info *infop)
if (hid >= 0)
{
infop->audio_hid = hid;
+
+ /*
+ * Fix up elapsed time and offset if past the end
+ */
+ if (track_id3->elapsed > track_id3->length)
+ track_id3->elapsed = 0;
+
+ if ((off_t)track_id3->offset >= buf_filesize(infop->audio_hid))
+ track_id3->offset = 0;
+
+ logf("%s: set resume for %s to %lu %lX", __func__,
+ track_id3->title, track_id3->elapsed, track_id3->offset);
+
if (infop->self_hid == cur_info.self_hid)
{
/* This is the current track to decode - should be started now */
@@ -2131,7 +2262,7 @@ audio_finish_load_track_exit:
playlist_peek_offset--;
}
- if (filling != STATE_FULL)
+ if (filling != STATE_FULL && filling != STATE_STOPPED)
{
/* Load next track - error or not */
track_list.in_progress_hid = 0;
@@ -2302,7 +2433,7 @@ static void audio_on_finish_load_track(int id3_hid)
change otherwise */
bool was_valid = valid_mp3entry(id3_get(PLAYING_ID3));
- playing_id3_sync(&info, -1, -1);
+ playing_id3_sync(&info, NULL, true);
if (!was_valid)
{
@@ -2339,6 +2470,31 @@ static void audio_on_handle_finished(int hid)
}
}
+static inline char* single_mode_get_id3_tag(struct mp3entry *id3)
+{
+ struct mp3entry *valid_id3 = valid_mp3entry(id3);
+ if (valid_id3 == NULL)
+ return NULL;
+
+ switch (global_settings.single_mode)
+ {
+ case SINGLE_MODE_ALBUM:
+ return valid_id3->album;
+ case SINGLE_MODE_ALBUM_ARTIST:
+ return valid_id3->albumartist;
+ case SINGLE_MODE_ARTIST:
+ return valid_id3->artist;
+ case SINGLE_MODE_COMPOSER:
+ return valid_id3->composer;
+ case SINGLE_MODE_GROUPING:
+ return valid_id3->grouping;
+ case SINGLE_MODE_GENRE:
+ return valid_id3->genre_string;
+ }
+
+ return NULL;
+}
+
/* Called to make an outstanding track skip the current track and to send the
transition events */
static void audio_finalise_track_change(void)
@@ -2394,6 +2550,26 @@ static void audio_finalise_track_change(void)
track_id3 = bufgetid3(info.id3_hid);
}
+ if (SINGLE_MODE_OFF != global_settings.single_mode && global_settings.party_mode == 0 &&
+ ((skip_pending == TRACK_SKIP_AUTO) || (skip_pending == TRACK_SKIP_AUTO_NEW_PLAYLIST)))
+ {
+ bool single_mode_do_pause = true;
+ if (SINGLE_MODE_TRACK != global_settings.single_mode)
+ {
+ char *previous_tag = single_mode_get_id3_tag(id3_get(PLAYING_ID3));
+ char *new_tag = single_mode_get_id3_tag(track_id3);
+ single_mode_do_pause = previous_tag == NULL ||
+ new_tag == NULL ||
+ strcmp(previous_tag, new_tag) != 0;
+ }
+
+ if (single_mode_do_pause)
+ {
+ play_status = PLAY_PAUSED;
+ pcmbuf_pause(true);
+ }
+ }
+
id3_write(PLAYING_ID3, track_id3);
/* The skip is technically over */
@@ -2408,6 +2584,11 @@ static void audio_finalise_track_change(void)
id3_mutex_unlock();
audio_playlist_track_change();
+
+#ifdef HAVE_PLAY_FREQ
+ if (filling == STATE_STOPPED)
+ audio_auto_change_frequency(track_id3, true);
+#endif
}
/* Actually begin a transition and take care of the codec change - may complete
@@ -2418,9 +2599,9 @@ static void audio_begin_track_change(enum pcm_track_change_type type,
/* Even if the new track is bad, the old track must be finished off */
pcmbuf_start_track_change(type);
- bool auto_skip = type != TRACK_CHANGE_MANUAL;
+ track_skip_is_manual = (type == TRACK_CHANGE_MANUAL);
- if (!auto_skip)
+ if (track_skip_is_manual)
{
/* Manual track change happens now */
audio_finalise_track_change();
@@ -2439,10 +2620,10 @@ static void audio_begin_track_change(enum pcm_track_change_type type,
return;
/* Everything needed for the codec is ready - start it */
- if (audio_start_codec(auto_skip))
+ if (audio_start_codec(!track_skip_is_manual))
{
- if (!auto_skip)
- playing_id3_sync(&info, -1, -1);
+ if (track_skip_is_manual)
+ playing_id3_sync(&info, NULL, true);
return;
}
}
@@ -2515,6 +2696,12 @@ static void audio_on_codec_complete(int status)
if (have_track)
{
+ if (filling == STATE_STOPPED)
+ {
+ audio_begin_track_change(TRACK_CHANGE_END_OF_DATA, trackstat);
+ return;
+ }
+
/* Track load is not complete - it might have stopped on a
full buffer without reaching the audio handle or we just
arrived at it early
@@ -2606,18 +2793,16 @@ static void audio_start_playback(const struct audio_resume_info *resume_info,
static struct audio_resume_info resume = { 0, 0 };
enum play_status old_status = play_status;
- if (!(flags & AUDIO_START_REFRESH))
+ bool skip_resume_adjustments = false;
+ if (resume_info)
{
- if (resume_info)
- {
- resume.elapsed = resume_info->elapsed;
- resume.offset = resume_info->offset;
- }
- else
- {
- resume.elapsed = 0;
- resume.offset = 0;
- }
+ resume.elapsed = resume_info->elapsed;
+ resume.offset = resume_info->offset;
+ }
+ else
+ {
+ resume.elapsed = 0;
+ resume.offset = 0;
}
if (flags & AUDIO_START_NEWBUF)
@@ -2644,11 +2829,9 @@ static void audio_start_playback(const struct audio_resume_info *resume_info,
left off */
pcmbuf_play_stop();
- if (!(flags & AUDIO_START_REFRESH))
- {
- resume.elapsed = id3_get(PLAYING_ID3)->elapsed;
- resume.offset = id3_get(PLAYING_ID3)->offset;
- }
+ resume.elapsed = id3_get(PLAYING_ID3)->elapsed;
+ resume.offset = id3_get(PLAYING_ID3)->offset;
+ skip_resume_adjustments = id3_get(PLAYING_ID3)->skip_resume_adjustments;
track_list_clear(TRACK_LIST_CLEAR_ALL);
pcmbuf_update_frequency();
@@ -2725,7 +2908,7 @@ static void audio_start_playback(const struct audio_resume_info *resume_info,
/* This is the currently playing track - get metadata, stat */
struct track_info info;
track_list_current(0, &info);
- playing_id3_sync(&info, resume.elapsed, resume.offset);
+ playing_id3_sync(&info, &resume, skip_resume_adjustments);
if (valid_mp3entry(id3_get(PLAYING_ID3)))
{
@@ -2768,6 +2951,7 @@ static void audio_stop_playback(void)
skip_pending = TRACK_SKIP_NONE;
track_event_flags = TEF_NONE;
+ track_skip_is_manual = false;
/* Close all tracks and mark them NULL */
remove_event(BUFFER_EVENT_REBUFFER, buffer_event_rebuffer_callback);
@@ -2781,7 +2965,9 @@ static void audio_stop_playback(void)
play_status = PLAY_STOPPED;
wipe_track_metadata(true);
-
+#ifdef HAVE_ALBUMART
+ clear_last_folder_album_art();
+#endif
/* Go idle */
filling = STATE_IDLE;
cancel_cpu_boost();
@@ -2841,6 +3027,11 @@ static void audio_on_skip(void)
/* Manual skip */
track_event_flags = TEF_NONE;
+ if (toskip == 1 && global_settings.repeat_mode == REPEAT_ONE)
+ {
+ audio_reset_and_rebuffer(TRACK_LIST_KEEP_CURRENT, 1);
+ }
+
/* If there was an auto skip in progress, there will be residual
advancement of the playlist and/or track list so compensation will be
required in order to end up in the right spot */
@@ -2891,7 +3082,7 @@ static void audio_on_skip(void)
track_list_delta += d;
}
}
-
+ track_skip_is_manual = false;
/* Adjust things by how much the playlist was manually moved */
playlist_peek_offset -= playlist_delta;
@@ -2995,6 +3186,12 @@ static void audio_on_ff_rewind(long time)
track_event_flags = TEF_NONE;
+ if (time < 0)
+ {
+ time = id3->length + time;
+ if (time < 0)
+ time = 0;
+ }
/* Send event before clobbering the time if rewinding. */
if (time == 0)
{
@@ -3032,21 +3229,15 @@ static void audio_on_ff_rewind(long time)
struct track_info cur_info;
track_list_current(0, &cur_info);
- /* Track must complete the loading _now_ since a codec and audio
- handle are needed in order to do the seek */
bool finish_load = cur_info.audio_hid < 0;
-
- if (finish_load &&
- audio_finish_load_track(&cur_info) != LOAD_TRACK_READY)
+ if (finish_load)
{
- /* Call above should push any load sequence - no need for
- halt_decoding_track here if no skip was pending here because
- there would not be a codec started if no audio handle was yet
- opened */
- break;
+ // track is not yet loaded so simply update resume details for upcoming finish_load_track and quit
+ playing_id3_sync(&cur_info, &(struct audio_resume_info){ time, 0 }, true);
+ return;
}
- if (pending == TRACK_SKIP_AUTO || finish_load)
+ if (pending == TRACK_SKIP_AUTO)
{
if (!bufreadid3(cur_info.id3_hid, ci_id3) ||
!audio_init_codec(&cur_info, ci_id3))
@@ -3256,7 +3447,7 @@ void audio_playback_handler(struct queue_event *ev)
case Q_AUDIO_REMAKE_AUDIO_BUFFER:
/* buffer needs to be reinitialized */
LOGFQUEUE("playback < Q_AUDIO_REMAKE_AUDIO_BUFFER");
- audio_start_playback(NULL, AUDIO_START_RESTART | AUDIO_START_NEWBUF | (ev->data ? AUDIO_START_REFRESH : 0));
+ audio_start_playback(NULL, AUDIO_START_RESTART | AUDIO_START_NEWBUF);
if (play_status == PLAY_STOPPED)
return; /* just need to change buffer state */
break;
@@ -3296,6 +3487,7 @@ void audio_playback_handler(struct queue_event *ev)
}
/* Fall-through */
case STATE_FINISHED:
+ case STATE_STOPPED:
/* All data was buffered */
cancel_cpu_boost();
/* Fall-through */
@@ -3367,9 +3559,6 @@ static void buffer_event_finished_callback(unsigned short id, void *ev_data)
break;
case TYPE_PACKET_AUDIO:
- /* Strip any useless trailing tags that are left. */
- strip_tags(hid);
- /* Fall-through */
case TYPE_ATOMIC_AUDIO:
LOGFQUEUE("buffering > audio Q_AUDIO_HANDLE_FINISHED: %d", hid);
audio_queue_post(Q_AUDIO_HANDLE_FINISHED, hid);
@@ -3387,9 +3576,8 @@ static void buffer_event_finished_callback(unsigned short id, void *ev_data)
/* Update elapsed time for next PCM insert */
void audio_codec_update_elapsed(unsigned long elapsed)
{
-#ifdef AB_REPEAT_ENABLE
ab_position_report(elapsed);
-#endif
+
/* Save in codec's id3 where it is used at next pcm insert */
id3_get(CODEC_ID3)->elapsed = elapsed;
}
@@ -3586,8 +3774,7 @@ void audio_hard_stop(void)
#ifdef PLAYBACK_VOICE
voice_stop();
#endif
- if (audiobuf_handle > 0)
- audiobuf_handle = core_free(audiobuf_handle);
+ audiobuf_handle = core_free(audiobuf_handle);
}
/* Resume playback if paused */
@@ -3597,6 +3784,13 @@ void audio_resume(void)
audio_queue_send(Q_AUDIO_PAUSE, false);
}
+/* Internal function used by REPEAT_ONE extern playlist.c */
+bool audio_pending_track_skip_is_manual(void)
+{
+ logf("Track change is: %s", track_skip_is_manual ? "Manual": "Auto");
+ return track_skip_is_manual;
+}
+
/* Skip the specified number of tracks forward or backward from the current */
void audio_skip(int offset)
{
@@ -3858,62 +4052,63 @@ static unsigned long audio_guess_frequency(struct mp3entry *id3)
}
}
-static void audio_change_frequency_callback(unsigned short id, void *data)
+static bool audio_auto_change_frequency(struct mp3entry *id3, bool play)
{
- static bool starting_playback = false;
- struct mp3entry *id3;
-
- switch (id)
+ unsigned long guessed_frequency = global_settings.play_frequency == 0 ? audio_guess_frequency(id3) : 0;
+ if (guessed_frequency && mixer_get_frequency() != guessed_frequency)
{
- case PLAYBACK_EVENT_START_PLAYBACK:
- starting_playback = true;
- break;
+ if (!play)
+ return true;
- case PLAYBACK_EVENT_TRACK_CHANGE:
- id3 = ((struct track_event *)data)->id3;
- if (id3 && !global_settings.play_frequency)
- {
- unsigned long guessed_frequency = audio_guess_frequency(id3);
- if (mixer_get_frequency() != guessed_frequency)
- {
#ifdef PLAYBACK_VOICE
- voice_stop();
+ voice_stop();
#endif
- mixer_set_frequency(guessed_frequency);
- audio_queue_post(Q_AUDIO_REMAKE_AUDIO_BUFFER, starting_playback);
- }
- }
- starting_playback = false;
- break;
-
- default:
- break;
+ mixer_set_frequency(guessed_frequency);
+ audio_queue_post(Q_AUDIO_REMAKE_AUDIO_BUFFER, 0);
+ return true;
}
+ return false;
}
-void audio_set_playback_frequency(int setting)
+void audio_set_playback_frequency(unsigned int sample_rate_hz)
{
+ /* sample_rate_hz == 0 is "automatic", and also a sentinel */
#if HAVE_PLAY_FREQ >= 192
- static const unsigned long play_sampr[] = { SAMPR_44, SAMPR_48, SAMPR_88, SAMPR_96, SAMPR_176, SAMPR_192 };
+ static const unsigned int play_sampr[] = {SAMPR_44, SAMPR_48, SAMPR_88, SAMPR_96, SAMPR_176, SAMPR_192, 0 };
#elif HAVE_PLAY_FREQ >= 96
- static const unsigned long play_sampr[] = { SAMPR_44, SAMPR_48, SAMPR_88, SAMPR_96 };
+ static const unsigned int play_sampr[] = {SAMPR_44, SAMPR_48, SAMPR_88, SAMPR_96, 0 };
#elif HAVE_PLAY_FREQ >= 48
- static const unsigned long play_sampr[] = { SAMPR_44, SAMPR_48 };
+ static const unsigned int play_sampr[] = {SAMPR_44, SAMPR_48, 0 };
#else
#error "HAVE_PLAY_FREQ < 48 ??"
#endif
+ const unsigned int *p_sampr = play_sampr;
+ unsigned int sampr = 0;
- if ((unsigned)setting > ARRAYLEN(play_sampr)) /* [0] is "automatic" */
- setting = 0;
+ while (*p_sampr != 0)
+ {
+ if (*p_sampr == sample_rate_hz)
+ {
+ sampr = *p_sampr;
+ break;
+ }
+ p_sampr++;
+ }
- unsigned long playback_sampr = mixer_get_frequency();
- unsigned long sampr = setting ?
- play_sampr[setting - 1] :
- ((audio_status() == AUDIO_STATUS_PLAY) ?
- audio_guess_frequency(audio_current_track()) :
- playback_sampr);
+ if (sampr == 0)
+ {
+ if (audio_status() & (AUDIO_STATUS_PLAY | AUDIO_STATUS_PAUSE))
+ {
+ sampr = audio_guess_frequency(audio_current_track());
+ }
+ else
+ {
+ logf("could not set sample rate to %u hz", sample_rate_hz);
+ return; /* leave as is */
+ }
+ }
- if (sampr != playback_sampr)
+ if (sampr != mixer_get_frequency())
{
mixer_set_frequency(sampr);
LOGFQUEUE("audio >| audio Q_AUDIO_REMAKE_AUDIO_BUFFER");
@@ -3937,10 +4132,6 @@ void INIT_ATTR playback_init(void)
track_list_init();
buffering_init();
pcmbuf_update_frequency();
-#ifdef HAVE_PLAY_FREQ
- add_event(PLAYBACK_EVENT_TRACK_CHANGE, audio_change_frequency_callback);
- add_event(PLAYBACK_EVENT_START_PLAYBACK, audio_change_frequency_callback);
-#endif
#ifdef HAVE_CROSSFADE
/* Set crossfade setting for next buffer init which should be about... */
pcmbuf_request_crossfade_enable(global_settings.crossfade);