Rockbox open source high quality audio player as a Music Player Daemon
mpris rockbox mpd libadwaita audio rust zig deno
2
fork

Configure Feed

Select the types of activity you want to include in your feed.

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

+261 -19
+3
apps/plugin.c
··· 821 821 822 822 /* new stuff at the end, sort into place next time 823 823 the API gets incompatible */ 824 + 825 + splash_progress, 826 + splash_progress_set_delay, 824 827 }; 825 828 826 829 static int plugin_buffer_handle;
+4 -1
apps/plugin.h
··· 157 157 #define PLUGIN_MAGIC 0x526F634B /* RocK */ 158 158 159 159 /* increase this every time the api struct changes */ 160 - #define PLUGIN_API_VERSION 260 160 + #define PLUGIN_API_VERSION 261 161 161 162 162 /* update this to latest version if a change to the api struct breaks 163 163 backwards compatibility (and please take the opportunity to sort in any ··· 945 945 #endif 946 946 /* new stuff at the end, sort into place next time 947 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 953 /* plugin header */
+243 -15
apps/plugins/pictureflow/pictureflow.c
··· 397 397 #endif 398 398 }; 399 399 400 + #if PF_PLAYBACK_CAPABLE 401 + struct 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 + 400 420 struct rect { 401 421 int left; 402 422 int right; ··· 557 577 static struct pf_index_t pf_idx; 558 578 559 579 static struct pf_track_t pf_tracks; 580 + 581 + static struct mp3entry id3; 560 582 561 583 void reset_track_list(void); 562 584 ··· 2093 2115 pf_idx.album_index[slide_index].artist_seek); 2094 2116 2095 2117 if ( rb->tagcache_get_next(&tcs) ) { 2096 - struct mp3entry id3; 2097 2118 int fd; 2098 2119 2099 2120 #if defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE) ··· 2218 2239 const unsigned int p = 16777619; 2219 2240 unsigned int hash = 0x811C9DC5; // 2166136261; 2220 2241 2242 + if (!str) 2243 + return 0; 2244 + 2221 2245 while(*str) 2222 2246 hash = (hash ^ *str++) * p; 2223 2247 hash += hash << 13; ··· 4010 4034 } 4011 4035 4012 4036 #if PF_PLAYBACK_CAPABLE 4037 + static 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 + 4098 + static 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 + 4132 + static 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 + 4151 + static 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 4014 4201 static bool playlist_insert(int position, bool queue, bool create_new) 4015 4202 { ··· 4061 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 4069 - adding all of the tracks. 4070 - */ 4071 - static void show_current_playlist_menu(void) 4252 + static bool context_menu_ready(void) 4072 4253 { 4073 4254 #ifdef USEGSLIB 4074 4255 grey_show(false); ··· 4080 4261 #ifdef USEGSLIB 4081 4262 grey_show(true); 4082 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 4272 insert_whole_album = pf_state != pf_show_tracks; 4086 4273 FOR_NB_SCREENS(i) 4087 4274 rb->viewportmanager_theme_enable(i, true, NULL); 4088 - rb->onplay_show_playlist_menu(get_track_filename(pf_tracks.sel), 4089 - &playlist_insert); 4275 + 4276 + return true; 4277 + } 4278 + 4279 + static void context_menu_cleanup(void) 4280 + { 4090 4281 FOR_NB_SCREENS(i) 4091 4282 rb->viewportmanager_theme_undo(i, false); 4092 4283 if (insert_whole_album) ··· 4096 4287 #endif 4097 4288 mylcd_set_drawmode(DRMODE_FG); 4098 4289 } 4290 + 4291 + 4292 + static 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 + 4099 4323 4100 4324 4101 4325 /* ··· 4247 4471 pf_cfg.last_album); 4248 4472 else 4249 4473 { 4250 - static struct mp3entry id3; 4251 4474 #if defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE) 4252 4475 if (rb->tagcache_fill_tags(&id3, selected_file)) 4253 4476 set_current_slide(id3_get_index(&id3)); ··· 4601 4824 #if PF_PLAYBACK_CAPABLE 4602 4825 case PF_CONTEXT: 4603 4826 if (pf_state == pf_idle || pf_state == pf_scrolling || 4604 - pf_state == pf_show_tracks || pf_state == pf_cover_out) { 4605 - 4827 + pf_state == pf_show_tracks || pf_state == pf_cover_out) 4828 + { 4606 4829 if ( pf_state == pf_scrolling) 4607 4830 { 4608 4831 set_current_slide(target); ··· 4611 4834 else if (pf_state == pf_cover_out) 4612 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 4844 break; 4617 4845 #endif
+6
apps/screens.c
··· 622 622 talk_spell(val, true); 623 623 break; 624 624 case LANG_ID3_BITRATE: 625 + if (!id3->bitrate) 626 + return NULL; 625 627 snprintf(buffer, buffer_len, "%d kbps%s", id3->bitrate, 626 628 id3->vbr ? str(LANG_ID3_VBR) : (const unsigned char*) ""); 627 629 val=buffer; ··· 633 635 } 634 636 break; 635 637 case LANG_ID3_FREQUENCY: 638 + if (!id3->frequency) 639 + return NULL; 636 640 snprintf(buffer, buffer_len, "%ld Hz", id3->frequency); 637 641 val=buffer; 638 642 if(say_it) ··· 661 665 talk_spell(val, true); 662 666 break; 663 667 case LANG_FILESIZE: /* not LANG_ID3_FILESIZE because the string is shared */ 668 + if (!id3->filesize) 669 + return NULL; 664 670 output_dyn_value(buffer, buffer_len, id3->filesize, byte_units, 4, true); 665 671 val=buffer; 666 672 if(say_it && val)
+5 -3
manual/plugins/pictureflow.tex
··· 1 1 \subsection{PictureFlow} 2 2 \screenshot{plugins/images/ss-pictureflow}{PictureFlow}{img:pictureflow} 3 3 PictureFlow is a visual browser for your albums. After you've selected something to play, 4 - PictureFlow will continue running by default, or can optionally show the WPS. Using the 5 - context menu, albums or songs can be added to the dynamic playlist directly from 6 - PictureFlow (see \reference{ref:playingnext_submenu}). 4 + PictureFlow will continue running by default, or can optionally show the WPS. Using 5 + the context menu, songs can be added to the dynamic playlist directly from PictureFlow 6 + (see \reference{ref:playingnext_submenu}). 7 + Various metadata, such as format, length or year of an album or its songs can also be 8 + displayed. 7 9 8 10 9 11 \subsubsection{Sort Options}