Index: apps/action.h =================================================================== --- apps/action.h (revision 15044) +++ apps/action.h (working copy) @@ -111,6 +111,9 @@ ACTION_WPS_ID3SCREEN,/* optional */ ACTION_WPS_CONTEXT, ACTION_WPS_QUICKSCREEN,/* optional */ +#ifdef ACCEPT_DOUBLE_CLICK + ACTION_WPS_SCRUBSEEK,/* optional */ +#endif ACTION_WPS_MENU, /*this should be the same as ACTION_STD_MENU */ ACTION_WPS_REC, #if 0 Index: apps/lang/english.lang =================================================================== --- apps/lang/english.lang (revision 15044) +++ apps/lang/english.lang (working copy) @@ -11342,6 +11356,20 @@ + id: LANG_SCRUB_MODE_TIMEOUT + desc: Menu item to select timeout for Scrub mode (scrollwheel devices) + user: + + *: "Scrub Mode Timeout" + + + *: "Scrub Mode Timeout" + + + *: "" + + + id: LANG_THEME_MENU desc: in the settings menu user: Index: apps/gui/gwps-common.c =================================================================== --- apps/gui/gwps-common.c (revision 15044) +++ apps/gui/gwps-common.c (working copy) @@ -142,6 +142,228 @@ return false; } +bool scrub_ffwd_rew(int button) +{ + static const uint16_t ff_rew_steps[] = { + 1000, 2000, 3000, 4000, + 5000, 6000, 8000, 10000, + 15000, 20000, 25000, 30000, + 45000, 60000 + }; + + unsigned int step = 0; /* current ff/rewind step */ + unsigned int max_step = 0; /* maximum ff/rewind step */ + int ff_rewind_count = 0; /* current ff/rewind count (in ticks) */ + int direction = -1; /* forward=1 or backward=-1 */ + long accel_tick = 0; /* next time at which to bump the step size */ + bool exit = false; + bool usb = false; + bool update=false; /* update audio */ + bool timeout=false; /* time out scrub mode after preset seconds */ + long cur_pos=0; /* used to get round unsigned/signed comparison */ + long old_pos=0; /* used to calculate timeout */ + int wait_time=0; /* wait time before updating audio - makes for smooth scrubbing */ + int i = 0; + + if (button == ACTION_WPS_BROWSE) /* exit - should never happen */ + { + status_set_ffmode(0); + return usb; + } + +if ( (audio_status() & AUDIO_STATUS_PLAY) && + wps_state.id3 && wps_state.id3->length ) + { + if ((!wps_state.paused) && (global_settings.scrub_mode_timeout==0)) /* pause audio if timeout is 0 */ +#if (CONFIG_CODEC == SWCODEC) + audio_pre_ff_rewind(); +#else + audio_pause(); +#endif +#if CONFIG_KEYPAD == PLAYER_PAD +if (global_settings.scrub_mode_timeout==0) + FOR_NB_SCREENS(i) + gui_wps[i].display->stop_scroll(); +#endif + + wps_state.ff_rewind = true; /* set scrub mode for progress bar */ + + step = ff_rew_steps[global_settings.ff_rewind_min_step]; + + accel_tick = current_tick + + global_settings.ff_rewind_accel*HZ; + } + old_pos=wps_state.id3->elapsed; /* get old position for time out (should be redundant actually) */ + + while (!exit) /* main loop - read button, take action*/ + { + switch ( button ) + { + case ACTION_WPS_VOLUP: + direction = 1; + case ACTION_WPS_VOLDOWN: + wps_state.ff_rewind=true; /* re-enable scrub mode for progress bar if disabled elsewhere */ + if (wps_state.ff_rewind) /* enabled above so kind of redundant */ + { + if (direction == 1) + { + /* fast forwarding, calc max step relative to end */ + max_step = (wps_state.id3->length - + (wps_state.id3->elapsed + + ff_rewind_count)) * + FF_REWIND_MAX_PERCENT / 100; + status_set_ffmode(STATUS_FASTFORWARD); + } + else + { + /* rewinding, calc max step relative to start */ + max_step = (wps_state.id3->elapsed + ff_rewind_count) * + FF_REWIND_MAX_PERCENT / 100; + status_set_ffmode(STATUS_FASTBACKWARD); + } + + max_step = MAX(max_step, MIN_FF_REWIND_STEP); + + if (step > max_step) + step = max_step; + + ff_rewind_count += step * direction; + + if (global_settings.ff_rewind_accel != 0 && + current_tick >= accel_tick) + { + step *= 2; + accel_tick = current_tick + + global_settings.ff_rewind_accel*HZ; + } + } + if (direction > 0) { + if ((wps_state.id3->elapsed + ff_rewind_count) > + wps_state.id3->length) + ff_rewind_count = wps_state.id3->length - + wps_state.id3->elapsed; + } + else { + if ((int)(wps_state.id3->elapsed + ff_rewind_count) < 0) + ff_rewind_count = -wps_state.id3->elapsed; + + } + + FOR_NB_SCREENS(i) + gui_wps_refresh(&gui_wps[i], + (wps_state.wps_time_countup == false)? + ff_rewind_count:-ff_rewind_count, + WPS_REFRESH_PLAYER_PROGRESS | + WPS_REFRESH_DYNAMIC); + direction = -1; + update = true; + wait_time=0; + if ((wps_state.id3->length - wps_state.id3->elapsed) < 2000) /* few seconds to go before we run out of current song */ +#if (CONFIG_CODEC == SWCODEC) + audio_pre_ff_rewind(); /* so pause playback while we are still scrubbing*/ +#else + audio_pause(); +#endif + break; + + case ACTION_WPS_BROWSE: /* Exit on Select */ + case ACTION_WPS_PLAY: /* Exit on Play */ + if ((global_settings.scrub_mode_timeout==0) || (update)) + /* update playback if we are scrubbing while paused, or update required */ + { + wps_state.id3->elapsed = wps_state.id3->elapsed+ff_rewind_count; + audio_ff_rewind(wps_state.id3->elapsed); + } + ff_rewind_count = 0; + wps_state.ff_rewind = false; + status_set_ffmode(0); +#if (CONFIG_CODEC != SWCODEC) + if (!wps_state.paused) + audio_resume(); +#endif +#ifdef HAVE_LCD_CHARCELLS + //gui_wps_display(); +#endif + exit = true; + break; + + case ACTION_NONE: /* No Button Pressed */ + if (global_settings.scrub_mode_timeout==0) + { + status_set_ffmode(STATUS_PAUSE); /* display paused when not ff or rew */ + FOR_NB_SCREENS(i) + gui_wps_refresh(&gui_wps[i], (wps_state.wps_time_countup == false)? + ff_rewind_count:-ff_rewind_count, + WPS_REFRESH_PLAYER_PROGRESS | WPS_REFRESH_DYNAMIC); + } + else + { + if ((update) && (wait_time > 3)) /* wait time of 3 works OK, should this be a variable? */ + { + wait_time =0; +#if (CONFIG_CODEC == SWCODEC) + audio_pre_ff_rewind(); /* pause audio so that we can update cleanly */ +#else + audio_pause(); +#endif + if (wps_state.id3->elapsed+ff_rewind_count < wps_state.id3->length) + wps_state.id3->elapsed = wps_state.id3->elapsed+ff_rewind_count; /* update position if not beyond end of file */ + else + { + wps_state.id3->elapsed = wps_state.id3->length; + timeout=true; /* scrubbed to end of song */ + } + audio_ff_rewind(wps_state.id3->elapsed); /* update file with new position */ + old_pos=wps_state.id3->elapsed; /*update old_pos for time out calculation */ + ff_rewind_count = 0; /*reset scrub position now that we have updated */ + wps_state.ff_rewind=false; /* out of scub mode for progress bar */ +#if (CONFIG_CODEC != SWCODEC) + if (!wps_state.paused) + audio_resume(); +#endif + update=false; /* only update once */ + } + else + { + if (!update) /* we havent scrubbed yet, so just show progress as normal */ + { + wps_state.ff_rewind=false; /* not scrubbing, normal mode for progress bar */ + FOR_NB_SCREENS(i) + gui_wps_refresh(&gui_wps[i], + 0, + WPS_REFRESH_PLAYER_PROGRESS | + WPS_REFRESH_DYNAMIC); + } + else /* we have scrubbed so increment wait_time */ + { + wait_time++; + DEBUGF("wait time is %d\n", wait_time); + old_pos=wps_state.id3->elapsed; /*update old_pos for time out calculation */ + } + if ((wps_state.id3->length - wps_state.id3->elapsed) < 2250) /* .25 seconds to go before we run out of current song */ + timeout=true; + cur_pos=wps_state.id3->elapsed; + if ((short)(cur_pos - old_pos)/1000 > global_settings.scrub_mode_timeout) /* time out calculation */ + timeout=true; + } + } + break; + + default: /* loop unless USB is connected */ + if(default_event_handler(button) == SYS_USB_CONNECTED) { + status_set_ffmode(0); + usb = true; + exit = true; + } + } + if ((!exit) && (timeout==false)) /* if we are not exiting and timeout has not elapsed, read button */ + button = get_action(CONTEXT_WPS|ALLOW_SOFTLOCK,HZ/5); + else + button = ACTION_WPS_PLAY; /* otherwise we are exiting, so go back to play as normal */ + } + return usb; +} + bool ffwd_rew(int button) { static const uint16_t ff_rew_steps[] = { @@ -1073,6 +1295,8 @@ mode = 4; if (status_get_ffmode() == STATUS_FASTBACKWARD) mode = 5; + if (status_get_ffmode() == STATUS_PAUSE) + mode = 3; if (intval) { *intval = mode; Index: apps/gui/gwps-common.h =================================================================== --- apps/gui/gwps-common.h (revision 15044) +++ apps/gui/gwps-common.h (working copy) @@ -27,6 +27,7 @@ bool gui_wps_display(void); bool update_onvol_change(struct gui_wps * gwps); bool update(struct gui_wps *gwps); +bool scrub_ffwd_rew(int button); bool ffwd_rew(int button); void display_keylock_text(bool locked); Index: apps/gui/gwps.c =================================================================== --- apps/gui/gwps.c (revision 15044) +++ apps/gui/gwps.c (working copy) @@ -502,7 +502,15 @@ return GO_TO_ROOT; break; +#ifdef ACCEPT_DOUBLE_CLICK + case ACTION_WPS_SCRUBSEEK: + if (global_settings.party_mode) /* don't do anything in party mode */ + break; + scrub_ffwd_rew(ACTION_NONE); /* go into scrub mode, but don't seek unless vol up or down */ + break; +#endif + #ifdef HAVE_QUICKSCREEN case ACTION_WPS_QUICKSCREEN: #if LCD_DEPTH > 1 Index: apps/settings.h =================================================================== --- apps/settings.h (revision 15044) +++ apps/settings.h (working copy) @@ -748,6 +748,15 @@ int list_accel_start_delay; /* ms before we start increaseing step size */ int list_accel_wait; /* ms between increases */ #endif + + int scrub_mode_timeout; /* time to wait before exiting scrub mode + 0 = never + number = time to wait in seconds */ + #ifdef ACCEPT_DOUBLE_CLICK int btn_double_click_delay; #endif #ifdef HAVE_USBSTACK int usb_stack_mode; /* device or host */ unsigned char usb_stack_device_driver[32]; /* usb device driver to load */ Index: apps/menus/playback_menu.c =================================================================== --- apps/menus/playback_menu.c (revision 15044) +++ apps/menus/playback_menu.c (working copy) @@ -60,8 +60,9 @@ MENUITEM_SETTING(ff_rewind_accel, &global_settings.ff_rewind_accel, NULL); MENUITEM_SETTING(ff_rewind_min_step, &global_settings.ff_rewind_min_step, NULL); +MENUITEM_SETTING(scrub_mode_timeout, &global_settings.scrub_mode_timeout, NULL); MAKE_MENU(ff_rewind_settings_menu, ID2P(LANG_WIND_MENU), 0, Icon_NOICON, - &ff_rewind_min_step, &ff_rewind_accel); + &ff_rewind_min_step, &ff_rewind_accel, &scrub_mode_timeout); #ifndef HAVE_FLASH_STORAGE #if CONFIG_CODEC == SWCODEC int buffermargin_callback(int action,const struct menu_item_ex *this_item) Index: apps/settings_list.c =================================================================== --- apps/settings_list.c (revision 15044) +++ apps/settings_list.c (working copy) @@ -258,6 +258,24 @@ snprintf(buffer, buffer_size, "2x/%ds", val); } +static const int scrub_mode_timeout_vals[] = {0,4,5,6,7,8,9,10,15,20,30}; +static long scrub_mode_timeout_getlang(int value) +{ + if (value == 0) + return LANG_OFF; + return TALK_ID(scrub_mode_timeout_vals[value], UNIT_SEC); +} +static void scrub_mode_timeout_formatter(char *buffer, size_t buffer_size, + int val, const char *unit) +{ + (void)unit; + if (val == 0) + strcpy(buffer, str(LANG_OFF)); + else + snprintf(buffer, buffer_size, "%ds", scrub_mode_timeout_vals[val]); +} + + static const unsigned char poweroff_idle_timer_times[] = {0,1,2,3,4,5,6,7,8,9,10,15,30,45,60}; static long poweroff_idle_timer_getlang(int value) { @@ -650,6 +668,10 @@ ff_rewind_min_step_getlang, NULL), INT_SETTING(0, ff_rewind_accel, LANG_FFRW_ACCEL, 3, "scan accel", UNIT_SEC, 16, 0, -1, scanaccel_formatter, scanaccel_getlang, NULL), + INT_SETTING(0, scrub_mode_timeout, LANG_SCRUB_MODE_TIMEOUT, + 0, "scrub mode timeout", UNIT_SEC, 0, 10, 1, + scrub_mode_timeout_formatter, scrub_mode_timeout_getlang, NULL), + #if (CONFIG_CODEC == SWCODEC) && !defined(HAVE_FLASH_STORAGE) STRINGCHOICE_SETTING(0, buffer_margin, LANG_MP3BUFFER_MARGIN, 0,"antiskip", "5s,15s,30s,1min,2min,3min,5min,10min",NULL, 8, Index: apps/keymaps/keymap-ipod.c =================================================================== --- apps/keymaps/keymap-ipod.c (revision 15044) +++ apps/keymaps/keymap-ipod.c (working copy) @@ -75,6 +75,9 @@ }; static const struct button_mapping button_context_wps[] = { +#ifdef ACCEPT_DOUBLE_CLICK + { ACTION_WPS_SCRUBSEEK, BUTTON_SELECT|BUTTON_REL, BUTTON_SELECT|BUTTON_DBL }, +#endif { ACTION_WPS_PLAY, BUTTON_PLAY|BUTTON_REL, BUTTON_PLAY }, { ACTION_WPS_STOP, BUTTON_PLAY|BUTTON_REPEAT, BUTTON_PLAY }, { ACTION_WPS_SKIPPREV, BUTTON_LEFT|BUTTON_REL, BUTTON_LEFT }, Index: firmware/export/config-ipodvideo.h =================================================================== --- firmware/export/config-ipodvideo.h (revision 15044) +++ firmware/export/config-ipodvideo.h (working copy) @@ -159,4 +159,7 @@ #define ICODE_ATTR_TREMOR_NOT_MDCT +/* define this to turn on double click captures */ +#define ACCEPT_DOUBLE_CLICK + #endif Index: firmware/export/config-ipod3g.h =================================================================== --- firmware/export/config-ipod3g.h (revision 15044) +++ firmware/export/config-ipod3g.h (working copy) @@ -133,4 +133,7 @@ #define ICODE_ATTR_TREMOR_NOT_MDCT +/* define this to turn on double click captures */ +#define ACCEPT_DOUBLE_CLICK + #endif /* SIMULATOR */ Index: firmware/export/config-ipodcolor.h =================================================================== --- firmware/export/config-ipodcolor.h (revision 15044) +++ firmware/export/config-ipodcolor.h (working copy) @@ -141,4 +141,7 @@ #define ICODE_ATTR_TREMOR_NOT_MDCT +/* define this to turn on double click captures */ +#define ACCEPT_DOUBLE_CLICK + #endif Index: firmware/export/config-ipodmini.h =================================================================== --- firmware/export/config-ipodmini.h (revision 15044) +++ firmware/export/config-ipodmini.h (working copy) @@ -146,4 +146,7 @@ #define ICODE_ATTR_TREMOR_NOT_MDCT +/* define this to turn on double click captures */ +#define ACCEPT_DOUBLE_CLICK + #endif Index: firmware/export/config-ipod1g2g.h =================================================================== --- firmware/export/config-ipod1g2g.h (revision 15044) +++ firmware/export/config-ipod1g2g.h (working copy) @@ -130,4 +130,7 @@ #define ICODE_ATTR_TREMOR_NOT_MDCT +/* define this to turn on double click captures */ +#define ACCEPT_DOUBLE_CLICK + #endif /* SIMULATOR */ Index: firmware/export/config-ipodmini2g.h =================================================================== --- firmware/export/config-ipodmini2g.h (revision 15044) +++ firmware/export/config-ipodmini2g.h (working copy) @@ -149,4 +149,7 @@ #define ICODE_ATTR_TREMOR_NOT_MDCT +/* define this to turn on double click captures */ +#define ACCEPT_DOUBLE_CLICK + #endif Index: firmware/export/config-ipodnano.h =================================================================== --- firmware/export/config-ipodnano.h (revision 15044) +++ firmware/export/config-ipodnano.h (working copy) @@ -147,4 +147,7 @@ #define ICODE_ATTR_TREMOR_NOT_MDCT +/* define this to turn on double click captures */ +#define ACCEPT_DOUBLE_CLICK + #endif Index: firmware/export/config-ipod4g.h =================================================================== --- firmware/export/config-ipod4g.h (revision 15044) +++ firmware/export/config-ipod4g.h (working copy) @@ -152,4 +152,7 @@ #define ICODE_ATTR_TREMOR_NOT_MDCT +/* define this to turn on double click captures */ +#define ACCEPT_DOUBLE_CLICK + #endif