summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Soffke <christian.soffke@gmail.com>2022-05-25 20:11:29 +0200
committerChristian Soffke <christian.soffke@gmail.com>2023-01-10 18:50:12 +0100
commita513cee8222006d5f970b4495ae7d1f1b6facf9b (patch)
treee6faa2e23a49b9dfd2eec38ef07aa899804ed39a
parentd5e38062ea6f1f4645f917b0d70e5001c48e9052 (diff)
downloadrockbox-a513cee8222006d5f970b4495ae7d1f1b6facf9b.tar.gz
rockbox-a513cee8222006d5f970b4495ae7d1f1b6facf9b.zip
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
-rw-r--r--apps/plugin.c3
-rw-r--r--apps/plugin.h5
-rw-r--r--apps/plugins/pictureflow/pictureflow.c258
-rw-r--r--apps/screens.c6
-rw-r--r--manual/plugins/pictureflow.tex8
5 files changed, 261 insertions, 19 deletions
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 = {
821 821
822 /* new stuff at the end, sort into place next time 822 /* new stuff at the end, sort into place next time
823 the API gets incompatible */ 823 the API gets incompatible */
824
825 splash_progress,
826 splash_progress_set_delay,
824}; 827};
825 828
826static int plugin_buffer_handle; 829static 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);
157#define PLUGIN_MAGIC 0x526F634B /* RocK */ 157#define PLUGIN_MAGIC 0x526F634B /* RocK */
158 158
159/* increase this every time the api struct changes */ 159/* increase this every time the api struct changes */
160#define PLUGIN_API_VERSION 260 160#define PLUGIN_API_VERSION 261
161 161
162/* update this to latest version if a change to the api struct breaks 162/* update this to latest version if a change to the api struct breaks
163 backwards compatibility (and please take the opportunity to sort in any 163 backwards compatibility (and please take the opportunity to sort in any
@@ -945,6 +945,9 @@ struct plugin_api {
945#endif 945#endif
946 /* new stuff at the end, sort into place next time 946 /* new stuff at the end, sort into place next time
947 the API gets incompatible */ 947 the API gets incompatible */
948
949 void (*splash_progress)(int current, int total, const char *fmt, ...) ATTRIBUTE_PRINTF(3, 4);
950 void (*splash_progress_set_delay)(long delay_ticks);
948}; 951};
949 952
950/* plugin header */ 953/* 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 {
397#endif 397#endif
398}; 398};
399 399
400#if PF_PLAYBACK_CAPABLE
401struct multiple_tracks_id3 {
402 unsigned long length;
403 unsigned long filesize;
404 unsigned long frequency;
405 unsigned int artist_hash;
406 unsigned int composer_hash;
407 unsigned int albumartist_hash;
408 unsigned int grouping_hash;
409 unsigned int comment_hash;
410 unsigned int album_hash;
411 unsigned int genre_hash;
412 unsigned int codectype;
413 unsigned int bitrate;
414 bool filesize_ovf;
415 bool length_ovf;
416 bool vbr;
417};
418#endif
419
400struct rect { 420struct rect {
401 int left; 421 int left;
402 int right; 422 int right;
@@ -558,6 +578,8 @@ static struct pf_index_t pf_idx;
558 578
559static struct pf_track_t pf_tracks; 579static struct pf_track_t pf_tracks;
560 580
581static struct mp3entry id3;
582
561void reset_track_list(void); 583void reset_track_list(void);
562 584
563static bool thread_is_running; 585static bool thread_is_running;
@@ -2093,7 +2115,6 @@ static bool get_albumart_for_index_from_db(const int slide_index, char *buf,
2093 pf_idx.album_index[slide_index].artist_seek); 2115 pf_idx.album_index[slide_index].artist_seek);
2094 2116
2095 if ( rb->tagcache_get_next(&tcs) ) { 2117 if ( rb->tagcache_get_next(&tcs) ) {
2096 struct mp3entry id3;
2097 int fd; 2118 int fd;
2098 2119
2099#if defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE) 2120#if defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE)
@@ -2218,6 +2239,9 @@ static unsigned int mfnv(char *str)
2218 const unsigned int p = 16777619; 2239 const unsigned int p = 16777619;
2219 unsigned int hash = 0x811C9DC5; // 2166136261; 2240 unsigned int hash = 0x811C9DC5; // 2166136261;
2220 2241
2242 if (!str)
2243 return 0;
2244
2221 while(*str) 2245 while(*str)
2222 hash = (hash ^ *str++) * p; 2246 hash = (hash ^ *str++) * p;
2223 hash += hash << 13; 2247 hash += hash << 13;
@@ -4010,6 +4034,169 @@ static void select_prev_album(void)
4010} 4034}
4011 4035
4012#if PF_PLAYBACK_CAPABLE 4036#if PF_PLAYBACK_CAPABLE
4037static void collect_id3(struct multiple_tracks_id3 *mul_id3, bool is_first_track)
4038{
4039 if (is_first_track)
4040 {
4041 mul_id3->artist_hash = mfnv(id3.artist);
4042 mul_id3->album_hash = mfnv(id3.album);
4043 mul_id3->genre_hash = mfnv(id3.genre_string);
4044 mul_id3->composer_hash = mfnv(id3.composer);
4045 mul_id3->albumartist_hash = mfnv(id3.albumartist);
4046 mul_id3->grouping_hash = mfnv(id3.grouping);
4047 mul_id3->comment_hash = mfnv(id3.comment);
4048 mul_id3->codectype = id3.codectype;
4049 mul_id3->vbr = id3.vbr;
4050 mul_id3->bitrate = id3.bitrate;
4051 mul_id3->frequency = id3.frequency;
4052 }
4053 else
4054 {
4055 if (mul_id3->artist_hash && (mfnv(id3.artist) != mul_id3->artist_hash))
4056 mul_id3->artist_hash = 0;
4057 if (mul_id3->album_hash && (mfnv(id3.album) != mul_id3->album_hash))
4058 mul_id3->album_hash = 0;
4059 if (mul_id3->genre_hash && (mfnv(id3.genre_string) != mul_id3->genre_hash))
4060 mul_id3->genre_hash = 0;
4061 if (mul_id3->composer_hash && (mfnv(id3.composer) != mul_id3->composer_hash))
4062 mul_id3->composer_hash = 0;
4063 if (mul_id3->albumartist_hash && (mfnv(id3.albumartist) !=
4064 mul_id3->albumartist_hash))
4065 mul_id3->albumartist_hash = 0;
4066 if (mul_id3->grouping_hash && (mfnv(id3.grouping) != mul_id3->grouping_hash))
4067 mul_id3->grouping_hash = 0;
4068 if (mul_id3->comment_hash && (mfnv(id3.comment) != mul_id3->comment_hash))
4069 mul_id3->comment_hash = 0;
4070
4071 if (mul_id3->codectype && (id3.codectype != mul_id3->codectype))
4072 mul_id3->codectype = AFMT_UNKNOWN;
4073 if (mul_id3->bitrate && (id3.bitrate != mul_id3->bitrate ||
4074 id3.vbr != mul_id3->vbr))
4075 mul_id3->bitrate = 0;
4076 if (mul_id3->frequency && (id3.frequency != mul_id3->frequency))
4077 mul_id3->frequency = 0;
4078 }
4079
4080 if (ULONG_MAX - mul_id3->length < id3.length)
4081 {
4082 mul_id3->length_ovf = true;
4083 mul_id3->length = 0;
4084 }
4085 else if (!mul_id3->length_ovf)
4086 mul_id3->length += id3.length;
4087
4088 if (INT_MAX - mul_id3->filesize < id3.filesize) /* output_dyn_value expects int */
4089 {
4090 mul_id3->filesize_ovf = true;
4091 mul_id3->filesize = 0;
4092 }
4093 else if (!mul_id3->filesize_ovf)
4094 mul_id3->filesize += id3.filesize;
4095}
4096
4097
4098static void write_id3_mul_tracks(struct multiple_tracks_id3 *mul_id3)
4099{
4100 id3.path[0] = '\0';
4101 id3.title = NULL;
4102 if (!mul_id3->artist_hash)
4103 id3.artist = NULL;
4104 if (!mul_id3->album_hash)
4105 id3.album = NULL;
4106 if (!mul_id3->genre_hash)
4107 id3.genre_string = NULL;
4108 if (!mul_id3->composer_hash)
4109 id3.composer = NULL;
4110 if (!mul_id3->albumartist_hash)
4111 id3.albumartist = NULL;
4112 if (!mul_id3->grouping_hash)
4113 id3.grouping = NULL;
4114 if (!mul_id3->comment_hash)
4115 id3.comment = NULL;
4116 id3.disc_string = NULL;
4117 id3.track_string = NULL;
4118 id3.year_string = NULL;
4119 id3.year = pf_idx.album_index[center_index].year;
4120 id3.length = mul_id3->length;
4121 id3.filesize = mul_id3->filesize;
4122 id3.frequency = mul_id3->frequency;
4123 id3.bitrate = mul_id3->bitrate;
4124 id3.codectype = mul_id3->codectype;
4125 id3.vbr = mul_id3->vbr;
4126 id3.discnum = 0;
4127 id3.tracknum = 0;
4128 id3.track_level = 0;
4129 id3.album_level = 0;
4130}
4131
4132static void init_mul_id3(struct multiple_tracks_id3 *mul_id3)
4133{
4134 mul_id3->artist_hash = 0;
4135 mul_id3->album_hash = 0;
4136 mul_id3->genre_hash = 0;
4137 mul_id3->composer_hash = 0;
4138 mul_id3->albumartist_hash = 0;
4139 mul_id3->grouping_hash = 0;
4140 mul_id3->comment_hash = 0;
4141 mul_id3->codectype = 0;
4142 mul_id3->vbr = false;
4143 mul_id3->bitrate = 0;
4144 mul_id3->frequency = 0;
4145 mul_id3->length = 0;
4146 mul_id3->filesize = 0;
4147 mul_id3->length_ovf = false;
4148 mul_id3->filesize_ovf = false;
4149}
4150
4151static int show_id3_info(const char *selected_file)
4152{
4153 int fd, i;
4154 unsigned long last_tick;
4155 const char *file_name;
4156 bool id3_retrieval_successful;
4157 bool is_multiple_tracks = insert_whole_album && pf_tracks.count > 1;
4158 struct multiple_tracks_id3 mul_id3;
4159
4160 init_mul_id3(&mul_id3);
4161
4162 last_tick = *(rb->current_tick) + HZ/2;
4163 rb->splash_progress_set_delay(HZ / 2); /* wait 1/2 sec before progress */
4164 i = 0;
4165 do {
4166 id3_retrieval_successful = false;
4167 file_name = i == 0 ? selected_file : get_track_filename(i);
4168 fd = rb->open(file_name, O_RDONLY);
4169 if (fd >= 0)
4170 {
4171 if (rb->get_metadata(&id3, fd, file_name))
4172 id3_retrieval_successful = true;
4173 rb->close(fd);
4174 }
4175 if (!id3_retrieval_successful)
4176 return 0;
4177
4178 if (is_multiple_tracks)
4179 {
4180 rb->splash_progress(i, pf_tracks.count,
4181 "%s (%s)", rb->str(LANG_WAIT), rb->str(LANG_OFF_ABORT));
4182 if (TIME_AFTER(*(rb->current_tick), last_tick + HZ/4))
4183 {
4184 if (rb->action_userabort(TIMEOUT_NOBLOCK))
4185 return 0;
4186 last_tick = *(rb->current_tick);
4187 }
4188
4189 collect_id3(&mul_id3, i == 0);
4190 rb->yield();
4191 }
4192 } while (++i < pf_tracks.count && is_multiple_tracks);
4193
4194 if (is_multiple_tracks)
4195 write_id3_mul_tracks(&mul_id3);
4196
4197 return rb->browse_id3(&id3, 0, 0, NULL) ? PLUGIN_USB_CONNECTED : 0;
4198}
4199
4013 4200
4014static bool playlist_insert(int position, bool queue, bool create_new) 4201static bool playlist_insert(int position, bool queue, bool create_new)
4015{ 4202{
@@ -4061,14 +4248,8 @@ static bool track_list_ready(void)
4061 return true; 4248 return true;
4062} 4249}
4063 4250
4064/**
4065 Brings up "Current Playlist" menu with first
4066 track of selection.
4067 4251
4068 Onplay menu code calls back playlist_insert for 4252static bool context_menu_ready(void)
4069 adding all of the tracks.
4070*/
4071static void show_current_playlist_menu(void)
4072{ 4253{
4073#ifdef USEGSLIB 4254#ifdef USEGSLIB
4074 grey_show(false); 4255 grey_show(false);
@@ -4080,13 +4261,23 @@ static void show_current_playlist_menu(void)
4080#ifdef USEGSLIB 4261#ifdef USEGSLIB
4081 grey_show(true); 4262 grey_show(true);
4082#endif 4263#endif
4083 return; 4264 return false;
4084 } 4265 }
4266#if LCD_DEPTH > 1
4267#ifdef USEGSLIB
4268 rb->lcd_set_foreground(N_BRIGHT(0));
4269 rb->lcd_set_background(N_BRIGHT(255));
4270#endif
4271#endif
4085 insert_whole_album = pf_state != pf_show_tracks; 4272 insert_whole_album = pf_state != pf_show_tracks;
4086 FOR_NB_SCREENS(i) 4273 FOR_NB_SCREENS(i)
4087 rb->viewportmanager_theme_enable(i, true, NULL); 4274 rb->viewportmanager_theme_enable(i, true, NULL);
4088 rb->onplay_show_playlist_menu(get_track_filename(pf_tracks.sel), 4275
4089 &playlist_insert); 4276 return true;
4277}
4278
4279static void context_menu_cleanup(void)
4280{
4090 FOR_NB_SCREENS(i) 4281 FOR_NB_SCREENS(i)
4091 rb->viewportmanager_theme_undo(i, false); 4282 rb->viewportmanager_theme_undo(i, false);
4092 if (insert_whole_album) 4283 if (insert_whole_album)
@@ -4098,6 +4289,39 @@ static void show_current_playlist_menu(void)
4098} 4289}
4099 4290
4100 4291
4292static int context_menu(void)
4293{
4294 char *file_name = get_track_filename(pf_tracks.sel);
4295
4296 enum {
4297 PF_CURRENT_PLAYLIST = 0,
4298 PF_ID3_INFO
4299 };
4300 MENUITEM_STRINGLIST(context_menu, ID2P(LANG_ONPLAY_MENU_TITLE), NULL,
4301 ID2P(LANG_PLAYING_NEXT),
4302 ID2P(LANG_MENU_SHOW_ID3_INFO));
4303
4304 while (1) {
4305 switch (rb->do_menu(&context_menu,
4306 NULL, NULL, false)) {
4307
4308 case PF_CURRENT_PLAYLIST:
4309 rb->onplay_show_playlist_menu(file_name,
4310 &playlist_insert);
4311 return 0;
4312 case PF_ID3_INFO:
4313 return show_id3_info(file_name);
4314 case MENU_ATTACHED_USB:
4315 return PLUGIN_USB_CONNECTED;
4316 default:
4317 return 0;
4318
4319 }
4320 }
4321}
4322
4323
4324
4101/* 4325/*
4102 * Puts selected album's tracks into a newly created playlist and starts playing 4326 * Puts selected album's tracks into a newly created playlist and starts playing
4103 */ 4327 */
@@ -4247,7 +4471,6 @@ static void set_initial_slide(const char* selected_file)
4247 pf_cfg.last_album); 4471 pf_cfg.last_album);
4248 else 4472 else
4249 { 4473 {
4250 static struct mp3entry id3;
4251#if defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE) 4474#if defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE)
4252 if (rb->tagcache_fill_tags(&id3, selected_file)) 4475 if (rb->tagcache_fill_tags(&id3, selected_file))
4253 set_current_slide(id3_get_index(&id3)); 4476 set_current_slide(id3_get_index(&id3));
@@ -4601,8 +4824,8 @@ static int pictureflow_main(const char* selected_file)
4601#if PF_PLAYBACK_CAPABLE 4824#if PF_PLAYBACK_CAPABLE
4602 case PF_CONTEXT: 4825 case PF_CONTEXT:
4603 if (pf_state == pf_idle || pf_state == pf_scrolling || 4826 if (pf_state == pf_idle || pf_state == pf_scrolling ||
4604 pf_state == pf_show_tracks || pf_state == pf_cover_out) { 4827 pf_state == pf_show_tracks || pf_state == pf_cover_out)
4605 4828 {
4606 if ( pf_state == pf_scrolling) 4829 if ( pf_state == pf_scrolling)
4607 { 4830 {
4608 set_current_slide(target); 4831 set_current_slide(target);
@@ -4611,7 +4834,12 @@ static int pictureflow_main(const char* selected_file)
4611 else if (pf_state == pf_cover_out) 4834 else if (pf_state == pf_cover_out)
4612 interrupt_cover_out_animation(); 4835 interrupt_cover_out_animation();
4613 4836
4614 show_current_playlist_menu(); 4837 if (context_menu_ready())
4838 {
4839 ret = context_menu();
4840 context_menu_cleanup();
4841 if ( ret != 0 ) return ret;
4842 }
4615 } 4843 }
4616 break; 4844 break;
4617#endif 4845#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,
622 talk_spell(val, true); 622 talk_spell(val, true);
623 break; 623 break;
624 case LANG_ID3_BITRATE: 624 case LANG_ID3_BITRATE:
625 if (!id3->bitrate)
626 return NULL;
625 snprintf(buffer, buffer_len, "%d kbps%s", id3->bitrate, 627 snprintf(buffer, buffer_len, "%d kbps%s", id3->bitrate,
626 id3->vbr ? str(LANG_ID3_VBR) : (const unsigned char*) ""); 628 id3->vbr ? str(LANG_ID3_VBR) : (const unsigned char*) "");
627 val=buffer; 629 val=buffer;
@@ -633,6 +635,8 @@ static const char * id3_get_or_speak_info(int selected_item, void* data,
633 } 635 }
634 break; 636 break;
635 case LANG_ID3_FREQUENCY: 637 case LANG_ID3_FREQUENCY:
638 if (!id3->frequency)
639 return NULL;
636 snprintf(buffer, buffer_len, "%ld Hz", id3->frequency); 640 snprintf(buffer, buffer_len, "%ld Hz", id3->frequency);
637 val=buffer; 641 val=buffer;
638 if(say_it) 642 if(say_it)
@@ -661,6 +665,8 @@ static const char * id3_get_or_speak_info(int selected_item, void* data,
661 talk_spell(val, true); 665 talk_spell(val, true);
662 break; 666 break;
663 case LANG_FILESIZE: /* not LANG_ID3_FILESIZE because the string is shared */ 667 case LANG_FILESIZE: /* not LANG_ID3_FILESIZE because the string is shared */
668 if (!id3->filesize)
669 return NULL;
664 output_dyn_value(buffer, buffer_len, id3->filesize, byte_units, 4, true); 670 output_dyn_value(buffer, buffer_len, id3->filesize, byte_units, 4, true);
665 val=buffer; 671 val=buffer;
666 if(say_it && val) 672 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 @@
1\subsection{PictureFlow} 1\subsection{PictureFlow}
2\screenshot{plugins/images/ss-pictureflow}{PictureFlow}{img:pictureflow} 2\screenshot{plugins/images/ss-pictureflow}{PictureFlow}{img:pictureflow}
3PictureFlow is a visual browser for your albums. After you've selected something to play, 3PictureFlow is a visual browser for your albums. After you've selected something to play,
4PictureFlow will continue running by default, or can optionally show the WPS. Using the 4PictureFlow will continue running by default, or can optionally show the WPS. Using
5context menu, albums or songs can be added to the dynamic playlist directly from 5the context menu, songs can be added to the dynamic playlist directly from PictureFlow
6PictureFlow (see \reference{ref:playingnext_submenu}). 6(see \reference{ref:playingnext_submenu}).
7Various metadata, such as format, length or year of an album or its songs can also be
8displayed.
7 9
8 10
9\subsubsection{Sort Options} 11\subsubsection{Sort Options}