Index: apps/lang/english.lang =================================================================== --- apps/lang/english.lang (revision 27817) +++ apps/lang/english.lang (working copy) @@ -12619,3 +12619,31 @@ *: "Update on Stop" + + id: LANG_SKIP_BINARY + desc: binary search to the desired point in the track + user: core + + *: "Binary Skip" + + + *: "Binary Skip" + + + *: "Binary Skip" + + + + id: LANG_TALK_TRACK_POSITION + desc: announce track position on binary skip + user: core + + *: "Announce Track Position" + + + *: "Announce Track Position" + + + *: "Announce Track Position" + + Index: apps/gui/wps.c =================================================================== --- apps/gui/wps.c (revision 27817) +++ apps/gui/wps.c (working copy) @@ -66,6 +66,7 @@ #include "playlist_viewer.h" #include "wps.h" #include "statusbar-skinned.h" +#include "talk.h" #define RESTORE_WPS_INSTANTLY 0l #define RESTORE_WPS_NEXT_SECOND ((long)(HZ+current_tick)) @@ -80,6 +81,17 @@ static struct wps_data wps_datas[NB_SCREENS] = {{ .wps_loaded = 0 }}; static struct wps_sync_data wps_sync_data = { .do_full_update = false }; + +/* Binary Search Mode vars */ +#define BINARY_SEARCH_TIMEOUT 5 +#define BINARY_SEARCH_SKIP_LENGTH -2 +#define TRACK_SKIP_TIMEOUT 0.5 + +static unsigned long search_lower_bound = 0; +static unsigned long search_upper_bound = 0; +static long search_timeout = 0; +static long track_skip_timeout=0; + /* initial setup of wps_data */ static void wps_state_init(void); static void track_changed_callback(void *param); @@ -299,6 +311,8 @@ case ACTION_WPS_SEEKFWD: direction = 1; case ACTION_WPS_SEEKBACK: + + if (wps_state.ff_rewind) { if (direction == 1) @@ -489,6 +503,8 @@ static void prev_track(unsigned long skip_thresh) { + search_timeout = 0; + if (wps_state.id3->elapsed < skip_thresh) { audio_prev(); @@ -520,6 +536,8 @@ static void next_track(void) { + search_timeout = 0; + /* take care of if we're playing a cuesheet */ if (wps_state.id3->cuesheet) { @@ -702,7 +720,9 @@ bool vol_changed = false; int i; long last_left = 0, last_right = 0; - + int announce_seconds = 0; + + #ifdef HAVE_LCD_CHARCELLS status_set_audio(true); status_set_param(false); @@ -842,6 +862,7 @@ case ACTION_WPS_SEEKFWD: if (global_settings.party_mode) break; + search_timeout = 0; if (current_tick -last_right < HZ) { if (wps_state.id3->cuesheet) @@ -862,6 +883,7 @@ case ACTION_WPS_SEEKBACK: if (global_settings.party_mode) break; + search_timeout = 0; if (current_tick -last_left < HZ) { if (wps_state.id3->cuesheet) @@ -903,7 +925,55 @@ else /* ...otherwise, do it normally */ #endif + + /* + We have NN seconds to continue our binary search + from the last time it was attempted. + If the NN seconds have expired, we consider that + a new search has begun. + This affects the initial upper and lower bounds for the search + as does the direction of the initial skip. + */ + if (global_settings.skip_length == BINARY_SEARCH_SKIP_LENGTH) + { + if (TIME_AFTER(current_tick, track_skip_timeout)) + { + if (TIME_AFTER(current_tick, search_timeout)) + { + /* + We're skipping backwards from the current elapsed time + so our initial window is from the current position + to the start of the track + */ + search_lower_bound = 0; + } + + /* Adjust based on the search bounds and direction */ + search_upper_bound = wps_state.id3->elapsed; + + audio_ff_rewind((search_lower_bound + search_upper_bound) / 2); + + search_timeout = current_tick + BINARY_SEARCH_TIMEOUT * HZ; + + track_skip_timeout = current_tick + TRACK_SKIP_TIMEOUT * HZ; + + if (global_settings.talk_track_position == true) + { + announce_seconds = ((search_lower_bound + search_upper_bound) / 2) / 1000; + + talk_value_decimal(announce_seconds, UNIT_TIME, 0, false); + } + } else { + if (global_settings.talk_track_position == true) + { + talk_shutup(); + } + play_hop(-1); + } + } else { play_hop(-1); + } + break; /* next @@ -926,7 +996,47 @@ else /* ...otherwise, do it normally */ #endif + if (global_settings.skip_length == BINARY_SEARCH_SKIP_LENGTH) + { + if (TIME_AFTER(current_tick, track_skip_timeout)) + { + if (TIME_AFTER(current_tick, search_timeout)) + { + /* + We're skipping forwards from the current elapsed time + so our initial window is from the current position + to the end of the track + */ + search_upper_bound = wps_state.id3->length; + } + + /* Adjust based on the search bounds and direction */ + search_lower_bound = wps_state.id3->elapsed; + + audio_ff_rewind((search_lower_bound + search_upper_bound) / 2); + + search_timeout = current_tick + BINARY_SEARCH_TIMEOUT * HZ; + + track_skip_timeout = current_tick + TRACK_SKIP_TIMEOUT * HZ; + + if (global_settings.talk_track_position == true) + { + announce_seconds = ((search_lower_bound + search_upper_bound) / 2) / 1000; + + talk_value_decimal(announce_seconds, UNIT_TIME, 0, false); + } + } else { + + if (global_settings.talk_track_position == true) + { + talk_shutup(); + } + play_hop(1); + } + } else { play_hop(1); + } + break; /* next / prev directories */ /* and set A-B markers if in a-b mode */ Index: apps/settings.h =================================================================== --- apps/settings.h (revision 27817) +++ apps/settings.h (working copy) @@ -610,6 +610,7 @@ bool talk_file_clip; /* use file .talk clips */ bool talk_filetype; /* say file type */ bool talk_battery_level; + bool talk_track_position; /* announce track position */ /* file browser sorting */ bool sort_case; /* dir sort order: 0=case insensitive, 1=sensitive */ Index: apps/settings_list.c =================================================================== --- apps/settings_list.c (revision 27817) +++ apps/settings_list.c (working copy) @@ -308,7 +308,9 @@ int val, const char *unit) { (void)unit; - if (val == -1) + if (val == -2) + return str(LANG_SKIP_BINARY); + else if (val == -1) return str(LANG_SKIP_OUTRO); else if (val == 0) return str(LANG_SKIP_TRACK); @@ -322,7 +324,9 @@ static int32_t getlang_unit_0_is_skip_track(int value, int unit) { (void)unit; - if (value == -1) + if (value == -2) + return LANG_SKIP_BINARY; + else if (value == -1) return LANG_SKIP_OUTRO; else if (value == 0) return LANG_SKIP_TRACK; @@ -1078,7 +1082,10 @@ "talk filetype", NULL), OFFON_SETTING(F_TEMPVAR, talk_battery_level, LANG_TALK_BATTERY_LEVEL, false, "Announce Battery Level", NULL), + OFFON_SETTING(F_TEMPVAR, talk_track_position, LANG_TALK_TRACK_POSITION, false, + "Announce Track Position", NULL), + #ifdef HAVE_RECORDING /* recording */ STRINGCHOICE_SETTING(F_RECSETTING, rec_timesplit, LANG_SPLIT_TIME, 0, @@ -1562,10 +1569,10 @@ NULL), TABLE_SETTING(F_ALLOW_ARBITRARY_VALS, skip_length, LANG_SKIP_LENGTH, 0, "skip length", - "outro,track,1s,2s,3s,5s,7s,10s,15s,20s,30s,45s,1min,90s,2min,3min,5min,10min,15min", + "outro,track,binary,1s,2s,3s,5s,7s,10s,15s,20s,30s,45s,1min,90s,2min,3min,5min,10min,15min", UNIT_SEC, formatter_unit_0_is_skip_track, getlang_unit_0_is_skip_track, NULL, - 19, -1,0,1,2,3,5,7,10,15,20,30,45,60,90,120,180,300,600,900), + 20, -1,0,-2,1,2,3,5,7,10,15,20,30,45,60,90,120,180,300,600,900), CHOICE_SETTING(0, start_in_screen, LANG_START_SCREEN, 1, "start in screen", "previous,root,files," #ifdef HAVE_TAGCACHE