Index: src/apps/lang/english.lang =================================================================== --- src/apps/lang/english.lang (revision 26535) +++ src/apps/lang/english.lang (working copy) @@ -13776,3 +13776,17 @@ *: "" + + id: LANG_ONPLAY_PICTUREFLOW + desc: Onplay pictureflow + user: core + + *: "PictureFlow" + + + *: "PictureFlow" + + + *: "open picture flow" + + Index: src/apps/plugins/pictureflow/pictureflow.c =================================================================== --- src/apps/plugins/pictureflow/pictureflow.c (revision 26535) +++ src/apps/plugins/pictureflow/pictureflow.c (working copy) @@ -57,6 +57,7 @@ #define PF_WPS ACTION_TREE_WPS #define PF_QUIT (LAST_ACTION_PLACEHOLDER + 1) +#define PF_TRACKLIST (LAST_ACTION_PLACEHOLDER + 2) #if defined(HAVE_SCROLLWHEEL) || CONFIG_KEYPAD == IRIVER_H10_PAD || \ CONFIG_KEYPAD == SAMSUNG_YH_PAD @@ -119,6 +120,7 @@ {PF_QUIT, BUTTON_POWER, BUTTON_NONE}, #elif CONFIG_KEYPAD == SANSA_FUZE_PAD {PF_QUIT, BUTTON_HOME|BUTTON_REPEAT, BUTTON_NONE}, + {PF_TRACKLIST, BUTTON_RIGHT, BUTTON_NONE}, /* These all use short press of BUTTON_POWER for menu, map long POWER to quit */ #elif CONFIG_KEYPAD == SANSA_C200_PAD || CONFIG_KEYPAD == SANSA_M200_PAD || \ @@ -312,6 +314,9 @@ static int num_slides = 4; static int zoom = 100; static bool show_fps = false; +static int auto_wps = 0; +static int last_album = 0; +static int backlight_mode = 0; static bool resize = true; static int cache_version = 0; static int show_album_name = (LCD_HEIGHT > 100) @@ -329,8 +334,11 @@ { TYPE_BOOL, 0, 1, { .bool_p = &show_fps }, "show fps", NULL }, { TYPE_BOOL, 0, 1, { .bool_p = &resize }, "resize", NULL }, { TYPE_INT, 0, 100, { .int_p = &cache_version }, "cache version", NULL }, - { TYPE_ENUM, 0, 2, { .int_p = &show_album_name }, "show album name", - show_album_name_conf } + { TYPE_ENUM, 0, 3, { .int_p = &show_album_name }, "show album name", + show_album_name_conf }, + { TYPE_INT, 0, 2, { .int_p = &auto_wps }, "auto wps", NULL }, + { TYPE_INT, 0, 999999, { .int_p = &last_album }, "last album", NULL }, + { TYPE_INT, 0, 1, { .int_p = &backlight_mode }, "backlight", NULL } }; #define CONFIG_NUM_ITEMS (sizeof(config) / sizeof(struct configdata)) @@ -783,6 +791,18 @@ return track_names + tracks[track_index].filename_idx; return 0; } + +int get_wps_current_index(void) +{ + struct mp3entry *id3 = rb->audio_current_track(); + if(id3 && id3->album) { + int i; + for( i=0; i < album_count; i++ ) + if(!rb->strcmp(album_names + album[i].name_idx, id3->album)) + return i; + } + return last_album; +} #endif /** Compare two unsigned ints passed via pointers. @@ -870,13 +890,12 @@ buflib_buffer_out(&buf_ctx, &out); avail += out; borrowed += out; - if (track_count) - { - struct track_data *new_tracks = (struct track_data *)(out + (uintptr_t)tracks); - unsigned int bytes = track_count * sizeof(struct track_data); + + struct track_data *new_tracks = (struct track_data *)(out + (uintptr_t)tracks); + unsigned int bytes = track_count * sizeof(struct track_data); + if (track_count) rb->memmove(new_tracks, tracks, bytes); - tracks = new_tracks; - } + tracks = new_tracks; } goto retry; } @@ -1033,6 +1052,24 @@ rb->yield(); } +/* Calculate modified FNV hash of string + * has good avalanche behaviour and uniform distribution + * see http://home.comcast.net/~bretm/hash/ */ +unsigned int mfnv(char *str) +{ + const unsigned int p = 16777619; + unsigned int hash = 0x811C9DC5; // 2166136261; + + while(*str) + hash = (hash ^ *str++) * p; + hash += hash << 13; + hash ^= hash >> 7; + hash += hash << 3; + hash ^= hash >> 17; + hash += hash << 5; + return hash; +} + /** Precomupte the album art images and store them in CACHE_PREFIX. */ @@ -1046,29 +1083,35 @@ char pfraw_file[MAX_PATH]; char albumart_file[MAX_PATH]; unsigned int format = FORMAT_NATIVE; + bool forced = cache_version == 0; cache_version = 0; configfile_save(CONFIG_FILE, config, CONFIG_NUM_ITEMS, CONFIG_VERSION); if (resize) format |= FORMAT_RESIZE|FORMAT_KEEP_ASPECT; for (i=0; i < album_count; i++) { - rb->snprintf(pfraw_file, sizeof(pfraw_file), CACHE_PREFIX "/%d.pfraw", - i); + rb->snprintf(pfraw_file, sizeof(pfraw_file), CACHE_PREFIX "/%x.pfraw", + mfnv(get_album_name(i))); /* delete existing cache, so it's a true rebuild */ - if(rb->file_exists(pfraw_file)) + if(rb->file_exists(pfraw_file)) { + if(!forced) + continue; rb->remove(pfraw_file); + } draw_progressbar(i); if (!get_albumart_for_index_from_db(i, albumart_file, MAX_PATH)) - continue; + rb->strcpy(albumart_file, EMPTY_SLIDE_BMP); input_bmp.data = buf; input_bmp.width = DISPLAY_WIDTH; input_bmp.height = DISPLAY_HEIGHT; - ret = read_image_file(albumart_file, &input_bmp, - buf_size, format, &format_transposed); + ret = read_image_file(albumart_file, &input_bmp, buf_size, format, &format_transposed); if (ret <= 0) { - rb->splash(HZ, "Could not read bmp"); - continue; /* skip missing/broken files */ + rb->splashf(HZ, "Album art is bad: %s", get_album_name(i)); + rb->strcpy(albumart_file, EMPTY_SLIDE_BMP); + ret = read_image_file(albumart_file, &input_bmp, buf_size, format, &format_transposed); + if(ret <= 0) + continue; } if (!save_pfraw(pfraw_file, &input_bmp)) { @@ -1343,8 +1386,10 @@ { struct pfraw_header bmph; int fh = rb->open(filename, O_RDONLY); - if( fh < 0 ) + if( fh < 0 ) { + cache_version = 1; return empty_slide_hid; + } else rb->read(fh, &bmph, sizeof(struct pfraw_header)); @@ -1359,6 +1404,7 @@ return 0; } + rb->yield(); // allow audio to play when fast scrolling struct dim *bm = buflib_get_data(&buf_ctx, hid); bm->width = bmph.width; @@ -1384,8 +1430,8 @@ const int prio) { char tmp_path_name[MAX_PATH+1]; - rb->snprintf(tmp_path_name, sizeof(tmp_path_name), CACHE_PREFIX "/%d.pfraw", - slide_index); + rb->snprintf(tmp_path_name, sizeof(tmp_path_name), CACHE_PREFIX "/%x.pfraw", + mfnv(get_album_name(slide_index))); int hid = read_pfraw(tmp_path_name, prio); if (!hid) @@ -1723,6 +1769,7 @@ xsdeni = sinr; int x; int dy = PFREAL_ONE; + for (x = xi; x < w; x++) { int column = (xs - slide_left) / PFREAL_ONE; if (column >= sw) @@ -1739,7 +1786,7 @@ #define PIXELSTEP_Y BUFFER_WIDTH #define LCDADDR(x, y) (&buffer[(y)*BUFFER_WIDTH + (x)]) #endif - + int p = (bmp->height-1-DISPLAY_OFFS) * PFREAL_ONE; int plim = MAX(0, p - (LCD_HEIGHT/2-1) * dy); pix_t *pixel = LCDADDR(x, (LCD_HEIGHT/2)-1 ); @@ -1757,6 +1804,9 @@ pixel -= PIXELSTEP_Y; } } + rb->yield(); // allow audio to play when fast scrolling + bmp = surface(slide->slide_index); + ptr = &src[column * bmp->height]; p = (bmp->height-DISPLAY_OFFS) * PFREAL_ONE; plim = MIN(sh * PFREAL_ONE, p + (LCD_HEIGHT/2) * dy); int plim2 = MIN(MIN(sh + REFLECT_HEIGHT, sh * 2) * PFREAL_ONE, @@ -1790,8 +1840,7 @@ xsden += xsdeni; xs = fdiv(xsnum, xsden); } else - xs += PFREAL_ONE; - + xs += PFREAL_ONE; } /* let the music play... */ rb->yield(); @@ -2072,13 +2121,23 @@ MENUITEM_STRINGLIST(settings_menu, "PictureFlow Settings", NULL, "Show FPS", "Spacing", "Centre margin", "Number of slides", "Zoom", - "Show album title", "Resize Covers", "Rebuild cache"); + "Show album title", "Resize Covers", "Rebuild cache", + "WPS Integration", "Backlight"); static const struct opt_items album_name_options[] = { { "Hide album title", -1 }, { "Show at the bottom", -1 }, { "Show at the top", -1 } }; + static const struct opt_items wps_options[] = { + { "Off", -1 }, + { "Direct", -1 }, + { "Via Track list", -1 } + }; + static const struct opt_items backlight_options[] = { + { "Always On", -1 }, + { "Normal", -1 }, + }; do { selection=rb->do_menu(&settings_menu,&selection, NULL, true); @@ -2135,7 +2194,12 @@ rb->remove(EMPTY_SLIDE); rb->splash(HZ, "Cache will be rebuilt on next restart"); break; - + case 8: + rb->set_option("WPS Integration", &auto_wps, INT, wps_options, 3, NULL); + break; + case 9: + rb->set_option("Backlight", &backlight_mode, INT, backlight_options, 2, NULL); + break; case MENU_ATTACHED_USB: return PLUGIN_USB_CONNECTED; } @@ -2150,6 +2214,7 @@ enum { PF_GOTO_WPS, #if PF_PLAYBACK_CAPABLE + PF_MENU_CLEAR_PLAYLIST, PF_MENU_PLAYBACK_CONTROL, #endif PF_MENU_SETTINGS, @@ -2169,7 +2234,7 @@ MENUITEM_STRINGLIST(main_menu,"PictureFlow Main Menu",NULL, "Go to WPS", #if PF_PLAYBACK_CAPABLE - "Playback Control", + "Clear playlist", "Playback Control", #endif "Settings", "Return", "Quit"); while (1) { @@ -2177,9 +2242,15 @@ case PF_GOTO_WPS: /* WPS */ return -2; #if PF_PLAYBACK_CAPABLE + case PF_MENU_CLEAR_PLAYLIST: + if(rb->playlist_remove_all_tracks(NULL) == 0) { + rb->playlist_create(NULL, NULL); + rb->splash(HZ*2, "Playlist Cleared"); + } + break; case PF_MENU_PLAYBACK_CONTROL: /* Playback Control */ playback_control(NULL); - break; + break; #endif case PF_MENU_SETTINGS: result = settings_menu(); @@ -2374,7 +2445,7 @@ /* * Puts the current tracklist into a newly created playlist and starts playling */ -void start_playback(void) +void start_playback(bool append) { static int old_playlist = -1, old_shuffle = 0; int count = 0; @@ -2383,14 +2454,14 @@ /* reuse existing playlist if possible * regenerate if shuffle is on or changed, since playlist index and * selected track are "out of sync" */ - if (!shuffle && center_slide.slide_index == old_playlist + if (!shuffle && !append && center_slide.slide_index == old_playlist && (old_shuffle == shuffle)) { goto play; } /* First, replace the current playlist with a new one */ - else if (rb->playlist_remove_all_tracks(NULL) == 0 - && rb->playlist_create(NULL, NULL) == 0) + else if (append || (rb->playlist_remove_all_tracks(NULL) == 0 + && rb->playlist_create(NULL, NULL) == 0)) { do { rb->yield(); @@ -2409,7 +2480,9 @@ /* TODO: can we adjust selected_track if !play_selected ? * if shuffle, we can't predict the playing track easily, and for either * case the track list doesn't get auto scrolled*/ - rb->playlist_start(position, 0); + if(!append) + rb->playlist_start(position, 0); + rb->yield(); // helps get wps info showing quicker old_playlist = center_slide.slide_index; old_shuffle = shuffle; } @@ -2494,7 +2567,6 @@ int ret; rb->lcd_setfont(FONT_UI); - draw_splashscreen(); if ( ! rb->dir_exists( CACHE_PREFIX ) ) { if ( rb->mkdir( CACHE_PREFIX ) < 0 ) { @@ -2502,9 +2574,13 @@ return PLUGIN_ERROR; } } - - configfile_load(CONFIG_FILE, config, CONFIG_NUM_ITEMS, CONFIG_VERSION); - + configfile_load(CONFIG_FILE, config, CONFIG_NUM_ITEMS, CONFIG_VERSION); + if(auto_wps == 0) + draw_splashscreen(); + if(backlight_mode == 0) { + /* Turn off backlight timeout */ + backlight_force_on(); /* backlight control in lib/helper.c */ + } init_reflect_table(); ALIGN_BUFFER(buf, buf_size, 4); @@ -2528,10 +2604,9 @@ error_wait("Could not load the empty slide"); return PLUGIN_ERROR; } - cache_version = CACHE_VERSION; + cache_version = CACHE_VERSION; configfile_save(CONFIG_FILE, config, CONFIG_NUM_ITEMS, CONFIG_VERSION); - #ifdef USEGSLIB long grey_buf_used; if (!grey_init(buf, buf_size, GREY_BUFFERED|GREY_ON_COP, @@ -2582,6 +2657,7 @@ recalc_offsets(); reset_slides(); + set_current_slide(get_wps_current_index()); char fpstxt[10]; int button; @@ -2713,14 +2789,49 @@ if ( pf_state == pf_idle || pf_state == pf_scrolling ) show_previous_slide(); break; - + + case PF_CONTEXT: + if ( auto_wps != 0 ) { + if( pf_state == pf_idle ) { + create_track_index(center_slide.slide_index); + reset_track_list(); + start_playback(true); + rb->splash(HZ*2, "Added to playlist"); + } + else if( pf_state == pf_show_tracks ) { + rb->playlist_insert_track(NULL, get_track_filename(selected_track), + PLAYLIST_INSERT_LAST, false, true); + rb->playlist_sync(NULL); + rb->splash(HZ*2, "Added to playlist"); + } + } + break; + case PF_TRACKLIST: + if ( auto_wps == 1 && pf_state == pf_idle ) { + pf_state = pf_cover_in; + break; + } case PF_SELECT: if ( pf_state == pf_idle ) { - pf_state = pf_cover_in; +#if PF_PLAYBACK_CAPABLE + if(auto_wps == 1) { + create_track_index(center_slide.slide_index); + reset_track_list(); + start_playback(false); + last_album = center_index; + return PLUGIN_GOTO_WPS; + } + else +#endif + pf_state = pf_cover_in; } else if ( pf_state == pf_show_tracks ) { #if PF_PLAYBACK_CAPABLE - start_playback(); + start_playback(false); + if(auto_wps != 0) { + last_album = center_index; + return PLUGIN_GOTO_WPS; + } #endif } break; @@ -2742,8 +2853,6 @@ FOR_NB_SCREENS(i) rb->viewportmanager_theme_enable(i, false, NULL); - /* Turn off backlight timeout */ - backlight_force_on(); /* backlight control in lib/helper.c */ #ifdef HAVE_ADJUSTABLE_CPU_FREQ rb->cpu_boost(true); #endif @@ -2760,7 +2869,7 @@ #endif #endif ret = main(); - if ( ret == PLUGIN_OK ) { + if ( ret == PLUGIN_OK || ret == PLUGIN_GOTO_WPS) { if (configfile_save(CONFIG_FILE, config, CONFIG_NUM_ITEMS, CONFIG_VERSION)) { Index: src/apps/onplay.c =================================================================== --- src/apps/onplay.c (revision 26535) +++ src/apps/onplay.c (working copy) @@ -1195,6 +1195,12 @@ return list_viewers(); } +static bool open_pictureflow(void) +{ + plugin_load(PLUGIN_DEMOS_DIR "/pictureflow.rock", NULL); + return ONPLAY_OK; +} + static int playlist_insert_shuffled(void) { if ((audio_status() & AUDIO_STATUS_PLAY) || @@ -1244,6 +1250,9 @@ { HOTKEY_INSERT_SHUFFLED, LANG_INSERT_SHUFFLED, HOTKEY_FUNC(playlist_insert_shuffled, NULL), ONPLAY_OK }, + { HOTKEY_PICTUREFLOW, LANG_ONPLAY_PICTUREFLOW, + HOTKEY_FUNC(open_pictureflow, NULL), + ONPLAY_OK }, }; /* Return the language ID for this action */ Index: src/apps/gui/wps.c =================================================================== --- src/apps/gui/wps.c (revision 26535) +++ src/apps/gui/wps.c (working copy) @@ -66,6 +66,7 @@ #include "playlist_viewer.h" #include "wps.h" #include "statusbar-skinned.h" +#include "plugin.h" #define RESTORE_WPS_INSTANTLY 0l #define RESTORE_WPS_NEXT_SECOND ((long)(HZ+current_tick)) @@ -1213,6 +1214,10 @@ #endif if (global_settings.browse_current) return GO_TO_PREVIOUS_BROWSER; +#ifdef HAVE_HOTKEY + if(global_settings.hotkey_wps == HOTKEY_PICTUREFLOW) + return plugin_load(PLUGIN_DEMOS_DIR "/pictureflow.rock", NULL); +#endif return GO_TO_PREVIOUS; } Index: src/apps/onplay.h =================================================================== --- src/apps/onplay.h (revision 26535) +++ src/apps/onplay.h (working copy) @@ -43,6 +43,7 @@ HOTKEY_DELETE, HOTKEY_INSERT, HOTKEY_INSERT_SHUFFLED, + HOTKEY_PICTUREFLOW, }; #endif Index: src/apps/settings_list.c =================================================================== --- src/apps/settings_list.c (revision 26535) +++ src/apps/settings_list.c (working copy) @@ -1617,22 +1620,22 @@ #if CONFIG_TUNER "radio," #endif - "bookmarks" ,NULL, + "bookmarks,pictureflow", NULL, #if defined(HAVE_TAGCACHE) #if defined(HAVE_RECORDING) && CONFIG_TUNER + 10, + #elif defined(HAVE_RECORDING) || CONFIG_TUNER /* only one of them */ 9, - #elif defined(HAVE_RECORDING) || CONFIG_TUNER /* only one of them */ + #else 8, - #else - 7, #endif #else #if defined(HAVE_RECORDING) && CONFIG_TUNER + 9, + #elif defined(HAVE_RECORDING) || CONFIG_TUNER /* only one of them */ 8, - #elif defined(HAVE_RECORDING) || CONFIG_TUNER /* only one of them */ + #else 7, - #else - 6, #endif #endif ID2P(LANG_PREVIOUS_SCREEN), ID2P(LANG_MAIN_MENU), @@ -1647,7 +1650,8 @@ #if CONFIG_TUNER ID2P(LANG_FM_RADIO), #endif - ID2P(LANG_BOOKMARK_MENU_RECENT_BOOKMARKS) + ID2P(LANG_BOOKMARK_MENU_RECENT_BOOKMARKS), + ID2P(LANG_ONPLAY_PICTUREFLOW) ), SYSTEM_SETTING(NVRAM(1),last_screen,-1), #if defined(HAVE_RTC_ALARM) && \ @@ -1791,10 +1795,10 @@ #ifdef HAVE_HOTKEY TABLE_SETTING(F_ALLOW_ARBITRARY_VALS, hotkey_wps, LANG_HOTKEY_WPS, HOTKEY_VIEW_PLAYLIST, "hotkey wps", - "off,view playlist,show track info,pitchscreen,open with,delete", - UNIT_INT, hotkey_formatter, hotkey_getlang, NULL, 6, HOTKEY_OFF, + "off,view playlist,show track info,pitchscreen,open with,delete,pictureflow", + UNIT_INT, hotkey_formatter, hotkey_getlang, NULL, 7, HOTKEY_OFF, HOTKEY_VIEW_PLAYLIST, HOTKEY_SHOW_TRACK_INFO, HOTKEY_PITCHSCREEN, - HOTKEY_OPEN_WITH, HOTKEY_DELETE), + HOTKEY_OPEN_WITH, HOTKEY_DELETE, HOTKEY_PICTUREFLOW), TABLE_SETTING(F_ALLOW_ARBITRARY_VALS, hotkey_tree, LANG_HOTKEY_FILE_BROWSER, HOTKEY_OFF, "hotkey tree", "off,open with,delete,insert,insert shuffled", Index: src/apps/root_menu.c =================================================================== --- src/apps/root_menu.c (revision 26535) +++ src/apps/root_menu.c (working copy) @@ -69,6 +69,7 @@ #include "tagcache.h" #endif #include "language.h" +#include "plugin.h" struct root_items { int (*function)(void* param); @@ -620,6 +621,12 @@ case GO_TO_ROOTITEM_CONTEXT: next_screen = load_context_screen(selected); break; + case GO_TO_PICTUREFLOW: + while (!tagcache_is_usable()) sleep(HZ); + next_screen = plugin_load(PLUGIN_DEMOS_DIR "/pictureflow.rock", NULL); + if(next_screen == PLUGIN_OK) + next_screen = GO_TO_ROOT; + break; default: if (next_screen == GO_TO_FILEBROWSER #ifdef HAVE_TAGCACHE Index: src/apps/root_menu.h =================================================================== --- src/apps/root_menu.h (revision 26535) +++ src/apps/root_menu.h (working copy) @@ -47,6 +47,7 @@ GO_TO_FM, #endif GO_TO_RECENTBMARKS, + GO_TO_PICTUREFLOW, /* Do Not add any items above here unless you want it to be able to be the "start screen" after a boot up. The setting in settings_list.c will need editing if this is the case. */