PictureFlow: Add 'Track Info' for tracks or whole albums

Context menu gains new option to view
metadata for individual tracks or albums.

Will display an album's length and total file size.
Other fields are displayed only if they are identical
across all tracks (except for the album year, which
is determined by the highest value encountered).

Change-Id: Ibc14cfaf2cb3d91b8d1cfbee05c6261db4975355
This commit is contained in:
Christian Soffke 2022-05-25 20:11:29 +02:00
parent d5e38062ea
commit a513cee822
5 changed files with 261 additions and 19 deletions

View file

@ -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;

View file

@ -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 */

View file

@ -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

View file

@ -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)

View file

@ -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}