diff --git a/apps/plugin.c b/apps/plugin.c index b017db017b..7c6e91424a 100644 --- a/apps/plugin.c +++ b/apps/plugin.c @@ -821,6 +821,9 @@ static const struct plugin_api rockbox_api = { /* new stuff at the end, sort into place next time the API gets incompatible */ + + splash_progress, + splash_progress_set_delay, }; static int plugin_buffer_handle; diff --git a/apps/plugin.h b/apps/plugin.h index ed688eb753..768fc0ff27 100644 --- a/apps/plugin.h +++ b/apps/plugin.h @@ -157,7 +157,7 @@ int plugin_open(const char *plugin, const char *parameter); #define PLUGIN_MAGIC 0x526F634B /* RocK */ /* increase this every time the api struct changes */ -#define PLUGIN_API_VERSION 260 +#define PLUGIN_API_VERSION 261 /* update this to latest version if a change to the api struct breaks backwards compatibility (and please take the opportunity to sort in any @@ -945,6 +945,9 @@ struct plugin_api { #endif /* new stuff at the end, sort into place next time the API gets incompatible */ + + void (*splash_progress)(int current, int total, const char *fmt, ...) ATTRIBUTE_PRINTF(3, 4); + void (*splash_progress_set_delay)(long delay_ticks); }; /* plugin header */ diff --git a/apps/plugins/pictureflow/pictureflow.c b/apps/plugins/pictureflow/pictureflow.c index 54497d8306..61d0b64e94 100644 --- a/apps/plugins/pictureflow/pictureflow.c +++ b/apps/plugins/pictureflow/pictureflow.c @@ -397,6 +397,26 @@ struct track_data { #endif }; +#if PF_PLAYBACK_CAPABLE +struct multiple_tracks_id3 { + unsigned long length; + unsigned long filesize; + unsigned long frequency; + unsigned int artist_hash; + unsigned int composer_hash; + unsigned int albumartist_hash; + unsigned int grouping_hash; + unsigned int comment_hash; + unsigned int album_hash; + unsigned int genre_hash; + unsigned int codectype; + unsigned int bitrate; + bool filesize_ovf; + bool length_ovf; + bool vbr; +}; +#endif + struct rect { int left; int right; @@ -558,6 +578,8 @@ static struct pf_index_t pf_idx; static struct pf_track_t pf_tracks; +static struct mp3entry id3; + void reset_track_list(void); static bool thread_is_running; @@ -2093,7 +2115,6 @@ static bool get_albumart_for_index_from_db(const int slide_index, char *buf, pf_idx.album_index[slide_index].artist_seek); if ( rb->tagcache_get_next(&tcs) ) { - struct mp3entry id3; int fd; #if defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE) @@ -2218,6 +2239,9 @@ static unsigned int mfnv(char *str) const unsigned int p = 16777619; unsigned int hash = 0x811C9DC5; // 2166136261; + if (!str) + return 0; + while(*str) hash = (hash ^ *str++) * p; hash += hash << 13; @@ -4010,6 +4034,169 @@ static void select_prev_album(void) } #if PF_PLAYBACK_CAPABLE +static void collect_id3(struct multiple_tracks_id3 *mul_id3, bool is_first_track) +{ + if (is_first_track) + { + mul_id3->artist_hash = mfnv(id3.artist); + mul_id3->album_hash = mfnv(id3.album); + mul_id3->genre_hash = mfnv(id3.genre_string); + mul_id3->composer_hash = mfnv(id3.composer); + mul_id3->albumartist_hash = mfnv(id3.albumartist); + mul_id3->grouping_hash = mfnv(id3.grouping); + mul_id3->comment_hash = mfnv(id3.comment); + mul_id3->codectype = id3.codectype; + mul_id3->vbr = id3.vbr; + mul_id3->bitrate = id3.bitrate; + mul_id3->frequency = id3.frequency; + } + else + { + if (mul_id3->artist_hash && (mfnv(id3.artist) != mul_id3->artist_hash)) + mul_id3->artist_hash = 0; + if (mul_id3->album_hash && (mfnv(id3.album) != mul_id3->album_hash)) + mul_id3->album_hash = 0; + if (mul_id3->genre_hash && (mfnv(id3.genre_string) != mul_id3->genre_hash)) + mul_id3->genre_hash = 0; + if (mul_id3->composer_hash && (mfnv(id3.composer) != mul_id3->composer_hash)) + mul_id3->composer_hash = 0; + if (mul_id3->albumartist_hash && (mfnv(id3.albumartist) != + mul_id3->albumartist_hash)) + mul_id3->albumartist_hash = 0; + if (mul_id3->grouping_hash && (mfnv(id3.grouping) != mul_id3->grouping_hash)) + mul_id3->grouping_hash = 0; + if (mul_id3->comment_hash && (mfnv(id3.comment) != mul_id3->comment_hash)) + mul_id3->comment_hash = 0; + + if (mul_id3->codectype && (id3.codectype != mul_id3->codectype)) + mul_id3->codectype = AFMT_UNKNOWN; + if (mul_id3->bitrate && (id3.bitrate != mul_id3->bitrate || + id3.vbr != mul_id3->vbr)) + mul_id3->bitrate = 0; + if (mul_id3->frequency && (id3.frequency != mul_id3->frequency)) + mul_id3->frequency = 0; + } + + if (ULONG_MAX - mul_id3->length < id3.length) + { + mul_id3->length_ovf = true; + mul_id3->length = 0; + } + else if (!mul_id3->length_ovf) + mul_id3->length += id3.length; + + if (INT_MAX - mul_id3->filesize < id3.filesize) /* output_dyn_value expects int */ + { + mul_id3->filesize_ovf = true; + mul_id3->filesize = 0; + } + else if (!mul_id3->filesize_ovf) + mul_id3->filesize += id3.filesize; +} + + +static void write_id3_mul_tracks(struct multiple_tracks_id3 *mul_id3) +{ + id3.path[0] = '\0'; + id3.title = NULL; + if (!mul_id3->artist_hash) + id3.artist = NULL; + if (!mul_id3->album_hash) + id3.album = NULL; + if (!mul_id3->genre_hash) + id3.genre_string = NULL; + if (!mul_id3->composer_hash) + id3.composer = NULL; + if (!mul_id3->albumartist_hash) + id3.albumartist = NULL; + if (!mul_id3->grouping_hash) + id3.grouping = NULL; + if (!mul_id3->comment_hash) + id3.comment = NULL; + id3.disc_string = NULL; + id3.track_string = NULL; + id3.year_string = NULL; + id3.year = pf_idx.album_index[center_index].year; + id3.length = mul_id3->length; + id3.filesize = mul_id3->filesize; + id3.frequency = mul_id3->frequency; + id3.bitrate = mul_id3->bitrate; + id3.codectype = mul_id3->codectype; + id3.vbr = mul_id3->vbr; + id3.discnum = 0; + id3.tracknum = 0; + id3.track_level = 0; + id3.album_level = 0; +} + +static void init_mul_id3(struct multiple_tracks_id3 *mul_id3) +{ + mul_id3->artist_hash = 0; + mul_id3->album_hash = 0; + mul_id3->genre_hash = 0; + mul_id3->composer_hash = 0; + mul_id3->albumartist_hash = 0; + mul_id3->grouping_hash = 0; + mul_id3->comment_hash = 0; + mul_id3->codectype = 0; + mul_id3->vbr = false; + mul_id3->bitrate = 0; + mul_id3->frequency = 0; + mul_id3->length = 0; + mul_id3->filesize = 0; + mul_id3->length_ovf = false; + mul_id3->filesize_ovf = false; +} + +static int show_id3_info(const char *selected_file) +{ + int fd, i; + unsigned long last_tick; + const char *file_name; + bool id3_retrieval_successful; + bool is_multiple_tracks = insert_whole_album && pf_tracks.count > 1; + struct multiple_tracks_id3 mul_id3; + + init_mul_id3(&mul_id3); + + last_tick = *(rb->current_tick) + HZ/2; + rb->splash_progress_set_delay(HZ / 2); /* wait 1/2 sec before progress */ + i = 0; + do { + id3_retrieval_successful = false; + file_name = i == 0 ? selected_file : get_track_filename(i); + fd = rb->open(file_name, O_RDONLY); + if (fd >= 0) + { + if (rb->get_metadata(&id3, fd, file_name)) + id3_retrieval_successful = true; + rb->close(fd); + } + if (!id3_retrieval_successful) + return 0; + + if (is_multiple_tracks) + { + rb->splash_progress(i, pf_tracks.count, + "%s (%s)", rb->str(LANG_WAIT), rb->str(LANG_OFF_ABORT)); + if (TIME_AFTER(*(rb->current_tick), last_tick + HZ/4)) + { + if (rb->action_userabort(TIMEOUT_NOBLOCK)) + return 0; + last_tick = *(rb->current_tick); + } + + collect_id3(&mul_id3, i == 0); + rb->yield(); + } + } while (++i < pf_tracks.count && is_multiple_tracks); + + if (is_multiple_tracks) + write_id3_mul_tracks(&mul_id3); + + return rb->browse_id3(&id3, 0, 0, NULL) ? PLUGIN_USB_CONNECTED : 0; +} + static bool playlist_insert(int position, bool queue, bool create_new) { @@ -4061,14 +4248,8 @@ static bool track_list_ready(void) return true; } -/** - Brings up "Current Playlist" menu with first - track of selection. - Onplay menu code calls back playlist_insert for - adding all of the tracks. -*/ -static void show_current_playlist_menu(void) +static bool context_menu_ready(void) { #ifdef USEGSLIB grey_show(false); @@ -4080,13 +4261,23 @@ static void show_current_playlist_menu(void) #ifdef USEGSLIB grey_show(true); #endif - return; + return false; } +#if LCD_DEPTH > 1 +#ifdef USEGSLIB + rb->lcd_set_foreground(N_BRIGHT(0)); + rb->lcd_set_background(N_BRIGHT(255)); +#endif +#endif insert_whole_album = pf_state != pf_show_tracks; FOR_NB_SCREENS(i) rb->viewportmanager_theme_enable(i, true, NULL); - rb->onplay_show_playlist_menu(get_track_filename(pf_tracks.sel), - &playlist_insert); + + return true; +} + +static void context_menu_cleanup(void) +{ FOR_NB_SCREENS(i) rb->viewportmanager_theme_undo(i, false); if (insert_whole_album) @@ -4098,6 +4289,39 @@ static void show_current_playlist_menu(void) } +static int context_menu(void) +{ + char *file_name = get_track_filename(pf_tracks.sel); + + enum { + PF_CURRENT_PLAYLIST = 0, + PF_ID3_INFO + }; + MENUITEM_STRINGLIST(context_menu, ID2P(LANG_ONPLAY_MENU_TITLE), NULL, + ID2P(LANG_PLAYING_NEXT), + ID2P(LANG_MENU_SHOW_ID3_INFO)); + + while (1) { + switch (rb->do_menu(&context_menu, + NULL, NULL, false)) { + + case PF_CURRENT_PLAYLIST: + rb->onplay_show_playlist_menu(file_name, + &playlist_insert); + return 0; + case PF_ID3_INFO: + return show_id3_info(file_name); + case MENU_ATTACHED_USB: + return PLUGIN_USB_CONNECTED; + default: + return 0; + + } + } +} + + + /* * Puts selected album's tracks into a newly created playlist and starts playing */ @@ -4247,7 +4471,6 @@ static void set_initial_slide(const char* selected_file) pf_cfg.last_album); else { - static struct mp3entry id3; #if defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE) if (rb->tagcache_fill_tags(&id3, selected_file)) set_current_slide(id3_get_index(&id3)); @@ -4601,8 +4824,8 @@ static int pictureflow_main(const char* selected_file) #if PF_PLAYBACK_CAPABLE case PF_CONTEXT: if (pf_state == pf_idle || pf_state == pf_scrolling || - pf_state == pf_show_tracks || pf_state == pf_cover_out) { - + pf_state == pf_show_tracks || pf_state == pf_cover_out) + { if ( pf_state == pf_scrolling) { set_current_slide(target); @@ -4611,7 +4834,12 @@ static int pictureflow_main(const char* selected_file) else if (pf_state == pf_cover_out) interrupt_cover_out_animation(); - show_current_playlist_menu(); + if (context_menu_ready()) + { + ret = context_menu(); + context_menu_cleanup(); + if ( ret != 0 ) return ret; + } } break; #endif diff --git a/apps/screens.c b/apps/screens.c index 91280a72f1..869d081498 100644 --- a/apps/screens.c +++ b/apps/screens.c @@ -622,6 +622,8 @@ static const char * id3_get_or_speak_info(int selected_item, void* data, talk_spell(val, true); break; case LANG_ID3_BITRATE: + if (!id3->bitrate) + return NULL; snprintf(buffer, buffer_len, "%d kbps%s", id3->bitrate, id3->vbr ? str(LANG_ID3_VBR) : (const unsigned char*) ""); val=buffer; @@ -633,6 +635,8 @@ static const char * id3_get_or_speak_info(int selected_item, void* data, } break; case LANG_ID3_FREQUENCY: + if (!id3->frequency) + return NULL; snprintf(buffer, buffer_len, "%ld Hz", id3->frequency); val=buffer; if(say_it) @@ -661,6 +665,8 @@ static const char * id3_get_or_speak_info(int selected_item, void* data, talk_spell(val, true); break; case LANG_FILESIZE: /* not LANG_ID3_FILESIZE because the string is shared */ + if (!id3->filesize) + return NULL; output_dyn_value(buffer, buffer_len, id3->filesize, byte_units, 4, true); val=buffer; if(say_it && val) diff --git a/manual/plugins/pictureflow.tex b/manual/plugins/pictureflow.tex index 5b4e80e107..5aee31c004 100644 --- a/manual/plugins/pictureflow.tex +++ b/manual/plugins/pictureflow.tex @@ -1,9 +1,11 @@ \subsection{PictureFlow} \screenshot{plugins/images/ss-pictureflow}{PictureFlow}{img:pictureflow} PictureFlow is a visual browser for your albums. After you've selected something to play, -PictureFlow will continue running by default, or can optionally show the WPS. Using the -context menu, albums or songs can be added to the dynamic playlist directly from -PictureFlow (see \reference{ref:playingnext_submenu}). +PictureFlow will continue running by default, or can optionally show the WPS. Using +the context menu, songs can be added to the dynamic playlist directly from PictureFlow +(see \reference{ref:playingnext_submenu}). +Various metadata, such as format, length or year of an album or its songs can also be +displayed. \subsubsection{Sort Options}