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.

Reworks to the shuffle system to improve performance and allow fast shuffling from a big library (but this work for all database views)

This improvement brings a huge performance improvement to start a random mix of your library. Previously, the only way to do this was to increase the size of a playlist with absurd sizes number. Now it will respect the limitation but will insert random songs from the current view.

Database: Add true random songs in playlist if it is gonna exceed its maximum capacity

More context is available here : https://www.reddit.com/r/rockbox/comments/1ez0mq4/i_developped_true_full_library_shuffle_for/

Also :
- Improved layout in the DB browser
- New default max playlists capacity is now 2000 on old PortalPlayer targets to give a better user experience and not having to wait dozens of seconds while creating a playlist
- "Show insert shuffled" option is now true by default
- Add a new shortcut to play all songs shuffled in the DB browser
- Now the feature is fully optional and enabled only on targets that have more than 2MB of RAM
- Add entries about this feature in the manual to explain it to the users
Change-Id: I1aebaf7ebcff2bf907080f1861027d530619097c

Change-Id: I3354923b148eeef1975171990e814a1a505d1df0

authored by

Paul Sauro and committed by
William Wilgus
c16dbbfd f6e8c201

+263 -88
+2 -2
apps/gui/wps.c
··· 806 806 theme_enabled = false; 807 807 gwps_leave_wps(theme_enabled); 808 808 onplay(state->id3->path, 809 - FILE_ATTR_AUDIO, CONTEXT_WPS, hotkey); 809 + FILE_ATTR_AUDIO, CONTEXT_WPS, hotkey, ONPLAY_NO_CUSTOMACTION); 810 810 if (!audio_status()) 811 811 { 812 812 /* re-enable theme since we're returning to SBS */ ··· 823 823 { 824 824 gwps_leave_wps(true); 825 825 int retval = onplay(state->id3->path, 826 - FILE_ATTR_AUDIO, CONTEXT_WPS, hotkey); 826 + FILE_ATTR_AUDIO, CONTEXT_WPS, hotkey, ONPLAY_NO_CUSTOMACTION); 827 827 /* if music is stopped in the context menu we want to exit the wps */ 828 828 if (retval == ONPLAY_MAINMENU 829 829 || !audio_status())
+14
apps/lang/english.lang
··· 2054 2054 </voice> 2055 2055 </phrase> 2056 2056 <phrase> 2057 + id: LANG_RANDOM_SHUFFLE_RANDOM_SELECTIVE_SONGS_SUMMARY 2058 + desc: a summary splash screen that appear on the database browser when you try to create a playlist from the database browser that exceeds your system limit 2059 + user: core 2060 + <source> 2061 + *: "Selection too big, %d random tracks will be picked from it" 2062 + </source> 2063 + <dest> 2064 + *: "Selection too big, %d random tracks will be picked from it" 2065 + </dest> 2066 + <voice> 2067 + *: "Selection too big, fewer random tracks will be picked from it" 2068 + </voice> 2069 + </phrase> 2070 + <phrase> 2057 2071 id: LANG_TAGCACHE_RAM 2058 2072 desc: in tag cache settings 2059 2073 user: core
+14
apps/lang/francais.lang
··· 2028 2028 </voice> 2029 2029 </phrase> 2030 2030 <phrase> 2031 + id: LANG_RANDOM_SHUFFLE_RANDOM_SELECTIVE_SONGS_SUMMARY 2032 + desc: a summary splash screen that appear on the database browser when you try to create a playlist from the database browser that exceeds your system limit 2033 + user: core 2034 + <source> 2035 + *: "Selection too big, %d random tracks will be picked from it" 2036 + </source> 2037 + <dest> 2038 + *: "Selection trop grande, %d pistes seront sélectionnées aléatoirement depuis celle-ci" 2039 + </dest> 2040 + <voice> 2041 + *: "Selection trop grande, donc des pistes seront sélectionnées aléatoirement depuis celle-ci" 2042 + </voice> 2043 + </phrase> 2044 + <phrase> 2031 2045 id: LANG_TAGCACHE_RAM 2032 2046 desc: in tag cache settings 2033 2047 user: core
+10 -3
apps/onplay.c
··· 302 302 303 303 /* warn if replacing the playlist */ 304 304 if (new_playlist && !warn_on_pl_erase()) 305 - return 0; 305 + return 1; 306 306 307 307 splash(0, ID2P(LANG_WAIT)); 308 308 ··· 340 340 } 341 341 342 342 playlist_set_modified(NULL, true); 343 - return false; 343 + return 0; 344 344 } 345 345 346 346 static bool view_playlist(void) ··· 1255 1255 } 1256 1256 #endif /* HOTKEY */ 1257 1257 1258 - int onplay(char* file, int attr, int from_context, bool hotkey) 1258 + int onplay(char* file, int attr, int from_context, bool hotkey, int customaction) 1259 1259 { 1260 1260 const struct menu_item_ex *menu; 1261 1261 onplay_result = ONPLAY_OK; ··· 1294 1294 #else 1295 1295 (void)hotkey; 1296 1296 #endif 1297 + if (customaction == ONPLAY_CUSTOMACTION_SHUFFLE_SONGS) { 1298 + int returnCode = add_to_playlist(&addtopl_replace_shuffled); 1299 + if (returnCode == 1) 1300 + // User did not want to erase his current playlist, so let's show again the database main menu 1301 + return ONPLAY_RELOAD_DIR; 1302 + return ONPLAY_START_PLAY; 1303 + } 1297 1304 1298 1305 push_current_activity(ACTIVITY_CONTEXTMENU); 1299 1306 if (from_context == CONTEXT_WPS)
+6 -1
apps/onplay.h
··· 25 25 #include "menu.h" 26 26 #endif 27 27 28 - int onplay(char* file, int attr, int from_context, bool hotkey); 28 + enum { 29 + ONPLAY_NO_CUSTOMACTION, 30 + ONPLAY_CUSTOMACTION_SHUFFLE_SONGS, 31 + }; 32 + 33 + int onplay(char* file, int attr, int from_context, bool hotkey, int customaction); 29 34 int get_onplay_context(void); 30 35 31 36 enum {
+1 -1
apps/playlist_viewer.c
··· 1107 1107 } 1108 1108 } 1109 1109 else 1110 - onplay(current_track->name, FILE_ATTR_AUDIO, CONTEXT_STD, true); 1110 + onplay(current_track->name, FILE_ATTR_AUDIO, CONTEXT_STD, true, ONPLAY_NO_CUSTOMACTION); 1111 1111 break; 1112 1112 } 1113 1113 #endif /* HAVE_HOTKEY */
+6 -2
apps/settings_list.c
··· 1120 1120 SYSTEM_SETTING(NVRAM(4), topruntime, 0), 1121 1121 INT_SETTING(F_BANFROMQS, max_files_in_playlist, 1122 1122 LANG_MAX_FILES_IN_PLAYLIST, 1123 - #if MEMORYSIZE > 1 1123 + #if CONFIG_CPU == PP5002 || CONFIG_CPU == PP5020 || CONFIG_CPU == PP5022 1124 + /** Slow CPU benefits greatly from building smaller playlists 1125 + On the iPod Mini 2nd gen, creating a playlist of 2000 entries takes around 10 seconds */ 1126 + 2000, 1127 + #elif MEMORYSIZE > 1 1124 1128 10000, 1125 1129 #else 1126 1130 400, ··· 1854 1858 true, "warn when erasing dynamic playlist",NULL), 1855 1859 OFFON_SETTING(0, keep_current_track_on_replace_playlist, LANG_KEEP_CURRENT_TRACK_ON_REPLACE, 1856 1860 true, "keep current track when replacing playlist",NULL), 1857 - OFFON_SETTING(0, show_shuffled_adding_options, LANG_SHOW_SHUFFLED_ADDING_OPTIONS, false, 1861 + OFFON_SETTING(0, show_shuffled_adding_options, LANG_SHOW_SHUFFLED_ADDING_OPTIONS, true, 1858 1862 "show shuffled adding options", NULL), 1859 1863 CHOICE_SETTING(0, show_queue_options, LANG_SHOW_QUEUE_OPTIONS, 0, 1860 1864 "show queue options", "off,on,in submenu",
+8 -7
apps/tagnavi.config
··· 176 176 177 177 # Define the title of the main menu 178 178 %menu_start "main" "Database" 179 - "Artist" -> canonicalartist -> album -> title = "fmt_title" 180 179 "Album Artist" -> albumartist -> album -> title = "fmt_title" 180 + "Artist" -> canonicalartist -> album -> title = "fmt_title" 181 181 "Album" -> album -> title = "fmt_title" 182 182 "Genre" -> genre -> canonicalartist -> album -> title = "fmt_title" 183 + "Year" -> year ? year > "0" -> canonicalartist -> album -> title = "fmt_title" 183 184 "Composer" -> composer -> album -> title = "fmt_title" 185 + "A to Z" ==> "a2z" 184 186 "Track" ==> "track" 185 - "Year" -> year ? year > "0" -> canonicalartist -> album -> title = "fmt_title" 187 + "Shuffle Songs" ~> title = "fmt_title" 188 + "Search" ==> "search" 186 189 "User Rating" -> rating -> title = "fmt_title" 187 190 "Recently Added" -> album ? entryage < "4" & commitid > "0" -> title = "fmt_title" 188 - "A to Z..." ==> "a2z" 189 - "History..." ==> "runtime" 190 - "Same as current..." ==> "same" 191 - "Search..." ==> "search" 192 - "Custom view..." ==> "custom" 191 + "History" ==> "runtime" 192 + "Same as current" ==> "same" 193 + "Custom view" ==> "custom" 193 194 194 195 # And finally set main menu as our root menu 195 196 %root_menu "main"
+164 -64
apps/tagtree.c
··· 56 56 #include "playback.h" 57 57 #include "strnatcmp.h" 58 58 #include "panic.h" 59 + #include "onplay.h" 59 60 60 61 #define str_or_empty(x) (x ? x : "(NULL)") 61 62 ··· 71 72 char* name; 72 73 int newtable; 73 74 int extraseek; 75 + int customaction; 74 76 }; 75 77 76 78 static struct tagentry* tagtree_get_entry(struct tree_context *c, int id); ··· 78 80 #define SEARCHSTR_SIZE 256 79 81 80 82 enum table { 81 - ROOT = 1, 82 - NAVIBROWSE, 83 - ALLSUBENTRIES, 84 - PLAYTRACK, 83 + TABLE_ROOT = 1, 84 + TABLE_NAVIBROWSE, 85 + TABLE_ALLSUBENTRIES, 86 + TABLE_PLAYTRACK, 85 87 }; 86 88 87 89 static const struct id3_to_search_mapping { ··· 108 110 menu_next, 109 111 menu_load, 110 112 menu_reload, 113 + menu_shuffle_songs, 111 114 }; 112 115 113 116 /* Capacity 10 000 entries (for example 10k different artists) */ 114 117 #define UNIQBUF_SIZE (64*1024) 115 118 static uint32_t uniqbuf[UNIQBUF_SIZE / sizeof(uint32_t)]; 116 119 120 + #if MEMORYSIZE > 2 121 + #define INSERT_ALL_PLAYLIST_MAX_SEGMENT_SIZE (1024) 122 + #else 123 + /* Lower quality randomness for low-ram devices using smaller segments */ 124 + #define INSERT_ALL_PLAYLIST_MAX_SEGMENT_SIZE (128) 125 + #endif 126 + static bool selective_random_playlist_indexes[INSERT_ALL_PLAYLIST_MAX_SEGMENT_SIZE]; 127 + 117 128 #define MAX_TAGS 5 118 129 #define MAX_MENU_ID_SIZE 32 119 130 ··· 338 349 TAG_MATCH("Pm", tag_virt_playtime_min), 339 350 TAG_MATCH("Ps", tag_virt_playtime_sec), 340 351 TAG_MATCH("->", menu_next), 352 + TAG_MATCH("~>", menu_shuffle_songs), 341 353 342 354 TAG_MATCH("==>", menu_load), 343 355 ··· 820 832 return true; 821 833 } 822 834 823 - if (entry->type != menu_next) 835 + if (entry->type != menu_next && entry->type != menu_shuffle_songs) 824 836 return false; 825 837 826 838 while (inst->tagorder_count < MAX_TAGS) ··· 847 859 848 860 inst->tagorder_count++; 849 861 850 - if (get_tag(&type) <= 0 || type != menu_next) 862 + if (get_tag(&type) <= 0 || (type != menu_next && type != menu_shuffle_songs)) 851 863 break; 852 864 } 853 865 ··· 1245 1257 dptr->name = NULL; 1246 1258 dptr->newtable = 0; 1247 1259 dptr->extraseek = 0; 1260 + dptr->customaction = ONPLAY_NO_CUSTOMACTION; 1248 1261 dptr++; 1249 1262 } 1250 1263 } ··· 1454 1467 #endif 1455 1468 , 0, 0, 0); 1456 1469 1457 - if (c->currtable == ALLSUBENTRIES) 1470 + if (c->currtable == TABLE_ALLSUBENTRIES) 1458 1471 { 1459 1472 tag = tag_title; 1460 1473 level--; ··· 1544 1557 { 1545 1558 if (offset == 0) 1546 1559 { 1547 - dptr->newtable = ALLSUBENTRIES; 1560 + dptr->newtable = TABLE_ALLSUBENTRIES; 1548 1561 dptr->name = str(LANG_TAGNAVI_ALL_TRACKS); 1562 + dptr->customaction = ONPLAY_NO_CUSTOMACTION; 1549 1563 dptr++; 1550 1564 current_entry_count++; 1551 1565 special_entry_count++; 1552 1566 } 1553 1567 if (offset <= 1) 1554 1568 { 1555 - dptr->newtable = NAVIBROWSE; 1569 + dptr->newtable = TABLE_NAVIBROWSE; 1556 1570 dptr->name = str(LANG_TAGNAVI_RANDOM); 1557 1571 dptr->extraseek = -1; 1572 + dptr->customaction = ONPLAY_NO_CUSTOMACTION; 1558 1573 dptr++; 1559 1574 current_entry_count++; 1560 1575 special_entry_count++; ··· 1568 1583 if (total_count++ < offset) 1569 1584 continue; 1570 1585 1571 - dptr->newtable = NAVIBROWSE; 1586 + dptr->newtable = TABLE_NAVIBROWSE; 1572 1587 if (tag == tag_title || tag == tag_filename) 1573 1588 { 1574 - dptr->newtable = PLAYTRACK; 1589 + dptr->newtable = TABLE_PLAYTRACK; 1575 1590 dptr->extraseek = tcs.idx_id; 1576 1591 } 1577 1592 else 1578 1593 dptr->extraseek = tcs.result_seek; 1594 + dptr->customaction = ONPLAY_NO_CUSTOMACTION; 1579 1595 1580 1596 fmt = NULL; 1581 1597 /* Check the format */ ··· 1758 1774 int i; 1759 1775 1760 1776 tc = c; 1761 - c->currtable = ROOT; 1777 + c->currtable = TABLE_ROOT; 1762 1778 if (c->dirlevel == 0) 1763 1779 c->currextra = rootmenu; 1764 1780 ··· 1775 1791 switch (menu->items[i]->type) 1776 1792 { 1777 1793 case menu_next: 1778 - dptr->newtable = NAVIBROWSE; 1794 + dptr->newtable = TABLE_NAVIBROWSE; 1779 1795 dptr->extraseek = i; 1796 + dptr->customaction = ONPLAY_NO_CUSTOMACTION; 1780 1797 break; 1781 1798 1782 1799 case menu_load: 1783 - dptr->newtable = ROOT; 1800 + dptr->newtable = TABLE_ROOT; 1784 1801 dptr->extraseek = menu->items[i]->link; 1802 + dptr->customaction = ONPLAY_NO_CUSTOMACTION; 1803 + break; 1804 + 1805 + case menu_shuffle_songs: 1806 + dptr->newtable = TABLE_NAVIBROWSE; 1807 + dptr->extraseek = i; 1808 + dptr->customaction = ONPLAY_CUSTOMACTION_SHUFFLE_SONGS; 1785 1809 break; 1786 1810 } 1787 1811 ··· 1804 1828 if (!table) 1805 1829 { 1806 1830 c->dirfull = false; 1807 - table = ROOT; 1831 + table = TABLE_ROOT; 1808 1832 c->currtable = table; 1809 1833 c->currextra = rootmenu; 1810 1834 } 1811 1835 1812 1836 switch (table) 1813 1837 { 1814 - case ROOT: 1838 + case TABLE_ROOT: 1815 1839 count = load_root(c); 1816 1840 break; 1817 1841 1818 - case ALLSUBENTRIES: 1819 - case NAVIBROWSE: 1842 + case TABLE_ALLSUBENTRIES: 1843 + case TABLE_NAVIBROWSE: 1820 1844 logf("navibrowse..."); 1821 1845 cpu_boost(true); 1822 1846 count = retrieve_entries(c, 0, true); ··· 1921 1945 core_pin(tagtree_handle); 1922 1946 1923 1947 switch (c->currtable) { 1924 - case ROOT: 1948 + case TABLE_ROOT: 1925 1949 c->currextra = newextra; 1926 1950 1927 - if (newextra == ROOT) 1951 + if (newextra == TABLE_ROOT) 1928 1952 { 1929 1953 menu = menus[seek]; 1930 1954 c->currextra = seek; 1931 1955 } 1932 1956 1933 - else if (newextra == NAVIBROWSE) 1957 + else if (newextra == TABLE_NAVIBROWSE) 1934 1958 { 1935 1959 int i, j; 1936 1960 ··· 2005 2029 2006 2030 break; 2007 2031 2008 - case NAVIBROWSE: 2009 - case ALLSUBENTRIES: 2010 - if (newextra == PLAYTRACK) 2032 + case TABLE_NAVIBROWSE: 2033 + case TABLE_ALLSUBENTRIES: 2034 + if (newextra == TABLE_PLAYTRACK) 2011 2035 { 2012 2036 adjust_selection = false; 2013 2037 ··· 2102 2126 return 0; 2103 2127 } 2104 2128 2129 + int tagtree_get_custom_action(struct tree_context* c) 2130 + { 2131 + return tagtree_get_entry(c, c->selected_item)->customaction; 2132 + } 2133 + 2134 + static void swap_array_bool(bool *a, bool *b) { 2135 + bool temp = *a; 2136 + *a = *b; 2137 + *b = temp; 2138 + } 2139 + 2140 + /** 2141 + * Randomly shuffle an array using the Fisher-Yates algorithm : https://en.wikipedia.org/wiki/Random_permutation 2142 + * This algorithm has a linear complexity. Don't forget to srand before call to use it with a relevant seed. 2143 + */ 2144 + static void shuffle_bool_array(bool array[], int size) { 2145 + for (int i = size - 1; i > 0; i--) { 2146 + int j = rand() % (i + 1); 2147 + swap_array_bool(&array[i], &array[j]); 2148 + } 2149 + } 2150 + 2151 + static bool fill_selective_random_playlist_indexes(int current_segment_n, int current_segment_max_available_space) { 2152 + if (current_segment_n == 0 || current_segment_max_available_space == 0) 2153 + return false; 2154 + if (current_segment_max_available_space > current_segment_n) 2155 + current_segment_max_available_space = current_segment_n; 2156 + for (int i = 0; i < current_segment_n; i++) 2157 + selective_random_playlist_indexes[i] = i < current_segment_max_available_space; 2158 + srand(current_tick); 2159 + shuffle_bool_array(selective_random_playlist_indexes, current_segment_n); 2160 + return true; 2161 + } 2105 2162 2106 2163 static bool insert_all_playlist(struct tree_context *c, 2107 2164 const char* playlist, bool new_playlist, 2108 2165 int position, bool queue) 2109 2166 { 2110 2167 struct tagcache_search tcs; 2111 - int i, n; 2168 + int n; 2112 2169 int fd = -1; 2113 2170 unsigned long last_tick; 2114 2171 char buf[MAX_PATH]; ··· 2144 2201 return false; 2145 2202 } 2146 2203 } 2147 - 2148 2204 last_tick = current_tick + HZ/2; /* Show splash after 0.5 seconds have passed */ 2149 2205 splash_progress_set_delay(HZ / 2); /* wait 1/2 sec before progress */ 2150 2206 n = c->filesindir; 2151 - for (i = 0; i < n; i++) 2152 - { 2153 - 2154 - splash_progress(i, n, "%s (%s)", str(LANG_WAIT), str(LANG_OFF_ABORT)); 2155 - if (TIME_AFTER(current_tick, last_tick + HZ/4)) 2156 - { 2157 - if (action_userabort(TIMEOUT_NOBLOCK)) 2158 - break; 2159 - last_tick = current_tick; 2207 + int segment_size = INSERT_ALL_PLAYLIST_MAX_SEGMENT_SIZE; 2208 + int segments_count = n / segment_size; 2209 + int leftovers_segment_size = n % segment_size; 2210 + bool fill_randomly = false; 2211 + if (playlist == NULL) { 2212 + bool will_exceed = n > playlist_get_current()->max_playlist_size; 2213 + fill_randomly = will_exceed; 2214 + } 2215 + if (leftovers_segment_size > 0 && fill_randomly) { 2216 + // We need to re-balance the segments so the randomness will be coherent and balanced the same through all segments 2217 + while (leftovers_segment_size + segments_count < segment_size) { 2218 + segment_size--; // -1 to all other segments 2219 + leftovers_segment_size += segments_count; 2160 2220 } 2161 - 2162 - if (!tagcache_retrieve(&tcs, tagtree_get_entry(c, i)->extraseek, 2163 - tcs.type, buf, sizeof buf)) 2164 - { 2165 - continue; 2221 + } 2222 + if (leftovers_segment_size > 0) 2223 + segments_count += 1; 2224 + int max_available_space = playlist_get_current()->max_playlist_size - playlist_get_current()->amount; 2225 + int max_available_space_per_segment = max_available_space / segments_count; 2226 + if (fill_randomly) { 2227 + talk_id(LANG_RANDOM_SHUFFLE_RANDOM_SELECTIVE_SONGS_SUMMARY, true); 2228 + splashf(HZ * 3, str(LANG_RANDOM_SHUFFLE_RANDOM_SELECTIVE_SONGS_SUMMARY), max_available_space_per_segment * segments_count); 2229 + //splashf(HZ * 5, "sz=%d lsz=%d sc=%d rcps=%d", segment_size, leftovers_segment_size, segments_count, max_available_space_per_segment); 2230 + } 2231 + for (int i = 0; i < segments_count; i++) { 2232 + bool is_leftovers_segment = leftovers_segment_size > 0 && i + 1 >= segments_count; 2233 + if (fill_randomly) { 2234 + if (is_leftovers_segment) 2235 + fill_randomly = fill_selective_random_playlist_indexes(leftovers_segment_size, max_available_space_per_segment); 2236 + else 2237 + fill_randomly = fill_selective_random_playlist_indexes(segment_size, max_available_space_per_segment); 2166 2238 } 2167 - 2168 - if (playlist == NULL) 2169 - { 2170 - if (playlist_insert_track(NULL, buf, position, queue, false) < 0) 2171 - { 2172 - logf("playlist_insert_track failed"); 2173 - break; 2239 + bool exit_loop_now = false; 2240 + int cur_segment_start = i * segment_size; 2241 + int cur_segment_end; 2242 + if (is_leftovers_segment) 2243 + cur_segment_end = cur_segment_start + leftovers_segment_size; 2244 + else 2245 + cur_segment_end = cur_segment_start + segment_size; 2246 + for (int j = cur_segment_start; j < cur_segment_end && !exit_loop_now; j++) { 2247 + if (fill_randomly && !selective_random_playlist_indexes[j % segment_size]) 2248 + continue; 2249 + splash_progress(j, n, "%s (%s)", str(LANG_WAIT), str(LANG_OFF_ABORT)); 2250 + if (TIME_AFTER(current_tick, last_tick + HZ/4)) { 2251 + if (action_userabort(TIMEOUT_NOBLOCK)) { 2252 + exit_loop_now = true; 2253 + break; 2254 + } 2255 + last_tick = current_tick; 2174 2256 } 2175 - } 2176 - else if (fdprintf(fd, "%s\n", buf) <= 0) 2257 + if (!tagcache_retrieve(&tcs, tagtree_get_entry(c, j)->extraseek, tcs.type, buf, sizeof buf)) 2258 + continue; 2259 + if (playlist == NULL) { 2260 + if (playlist_insert_track(NULL, buf, position, queue, false) < 0) { 2261 + logf("playlist_insert_track failed"); 2262 + exit_loop_now = true; 2263 + break; 2264 + } 2265 + } else if (fdprintf(fd, "%s\n", buf) <= 0) { 2266 + exit_loop_now = true; 2177 2267 break; 2178 - 2179 - yield(); 2180 - 2181 - if (playlist == NULL && position == PLAYLIST_INSERT_FIRST) 2182 - { 2183 - position = PLAYLIST_INSERT; 2268 + } 2269 + yield(); 2270 + if (playlist == NULL && position == PLAYLIST_INSERT_FIRST) 2271 + position = PLAYLIST_INSERT; 2184 2272 } 2273 + if (exit_loop_now) 2274 + break; 2185 2275 } 2186 2276 if (playlist == NULL) 2187 2277 playlist_sync(NULL); ··· 2196 2286 static bool goto_allsubentries(int newtable) 2197 2287 { 2198 2288 int i = 0; 2199 - while (i < 2 && (newtable == NAVIBROWSE || newtable == ALLSUBENTRIES)) 2289 + while (i < 2 && (newtable == TABLE_NAVIBROWSE || newtable == TABLE_ALLSUBENTRIES)) 2200 2290 { 2201 2291 tagtree_enter(tc, false); 2202 2292 tagtree_load(tc); 2203 2293 newtable = tagtree_get_entry(tc, tc->selected_item)->newtable; 2204 2294 i++; 2205 2295 } 2206 - return (newtable == PLAYTRACK); 2296 + return (newtable == TABLE_PLAYTRACK); 2207 2297 } 2208 2298 2209 2299 static void reset_tc_to_prev(int dirlevel, int selected_item) ··· 2233 2323 2234 2324 newtable = tagtree_get_entry(tc, tc->selected_item)->newtable; 2235 2325 2236 - if (newtable == PLAYTRACK) /* Insert a single track? */ 2326 + if (newtable == TABLE_PLAYTRACK) /* Insert a single track? */ 2237 2327 { 2238 2328 if (tagtree_get_filename(tc, buf, sizeof buf) < 0) 2239 2329 return false; ··· 2353 2443 if (!insert_all_playlist(c, NULL, false, PLAYLIST_INSERT_LAST, false)) 2354 2444 return -2; 2355 2445 2446 + int n = c->filesindir; 2447 + bool has_playlist_been_randomized = n > playlist_get_current()->max_playlist_size; 2448 + if (has_playlist_been_randomized) { 2449 + /* We need to recalculate the start index based on a percentage to put the user 2450 + around its desired start position and avoid out of bounds */ 2451 + 2452 + int percentage_start_index = 100 * start_index / n; 2453 + start_index = percentage_start_index * playlist_get_current()->amount / 100; 2454 + } 2455 + 2356 2456 if (global_settings.playlist_shuffle) 2357 2457 { 2358 - start_index = playlist_shuffle(current_tick, c->selected_item); 2458 + start_index = playlist_shuffle(current_tick, start_index); 2359 2459 if (!global_settings.play_selected) 2360 2460 start_index = 0; 2361 2461 } ··· 2403 2503 { 2404 2504 switch (c->currtable) 2405 2505 { 2406 - case ROOT: 2506 + case TABLE_ROOT: 2407 2507 return menu->title; 2408 2508 2409 - case NAVIBROWSE: 2410 - case ALLSUBENTRIES: 2509 + case TABLE_NAVIBROWSE: 2510 + case TABLE_ALLSUBENTRIES: 2411 2511 return current_title[c->currextra]; 2412 2512 } 2413 2513 ··· 2419 2519 int attr = -1; 2420 2520 switch (c->currtable) 2421 2521 { 2422 - case NAVIBROWSE: 2522 + case TABLE_NAVIBROWSE: 2423 2523 if (csi->tagorder[c->currextra] == tag_title 2424 2524 || csi->tagorder[c->currextra] == tag_virt_basename) 2425 2525 attr = FILE_ATTR_AUDIO; ··· 2427 2527 attr = ATTR_DIRECTORY; 2428 2528 break; 2429 2529 2430 - case ALLSUBENTRIES: 2530 + case TABLE_ALLSUBENTRIES: 2431 2531 attr = FILE_ATTR_AUDIO; 2432 2532 break; 2433 2533
+1
apps/tagtree.h
··· 45 45 int tagtree_get_attr(struct tree_context* c); 46 46 int tagtree_get_icon(struct tree_context* c); 47 47 int tagtree_get_filename(struct tree_context* c, char *buf, int buflen); 48 + int tagtree_get_custom_action(struct tree_context* c); 48 49 bool tagtree_get_subentry_filename(char *buf, size_t bufsize); 49 50 bool tagtree_subentries_do_action(bool (*action_cb)(const char *file_name)); 50 51
+19 -8
apps/tree.c
··· 735 735 oldbutton = button; 736 736 gui_synclist_do_button(&tree_lists, &button); 737 737 tc.selected_item = gui_synclist_get_sel_pos(&tree_lists); 738 + int customaction = ONPLAY_NO_CUSTOMACTION; 739 + bool do_restore_display = true; 740 + #ifdef HAVE_TAGCACHE 741 + if (id3db && (button == ACTION_STD_OK || button == ACTION_STD_CONTEXT)) { 742 + customaction = tagtree_get_custom_action(&tc); 743 + if (customaction == ONPLAY_CUSTOMACTION_SHUFFLE_SONGS) { 744 + button = ACTION_STD_CONTEXT; /** The code to insert shuffled is on the context branch of the switch so we always go here */ 745 + do_restore_display = false; 746 + } 747 + } 748 + #endif 738 749 switch ( button ) { 739 750 case ACTION_STD_OK: 740 751 /* nothing to do if no files to display */ ··· 773 784 default: 774 785 break; 775 786 } 776 - restore = true; 787 + restore = do_restore_display; 777 788 break; 778 789 779 790 case ACTION_STD_CANCEL: ··· 798 809 if (ft_exit(&tc) == 3) 799 810 exit_func = true; 800 811 801 - restore = true; 812 + restore = do_restore_display; 802 813 break; 803 814 804 815 case ACTION_TREE_STOP: 805 816 if (list_stop_handler()) 806 - restore = true; 817 + restore = do_restore_display; 807 818 break; 808 819 809 820 case ACTION_STD_MENU: ··· 851 862 skin_update(CUSTOM_STATUSBAR, i, SKIN_REFRESH_ALL); 852 863 } 853 864 854 - restore = true; 865 + restore = do_restore_display; 855 866 break; 856 867 } 857 868 #endif ··· 872 883 break; 873 884 874 885 if(!numentries) 875 - onplay_result = onplay(NULL, 0, curr_context, hotkey); 886 + onplay_result = onplay(NULL, 0, curr_context, hotkey, customaction); 876 887 else { 877 888 #ifdef HAVE_TAGCACHE 878 889 if (id3db) ··· 902 913 ft_assemble_path(buf, sizeof(buf), currdir, entry->name); 903 914 904 915 } 905 - onplay_result = onplay(buf, attr, curr_context, hotkey); 916 + onplay_result = onplay(buf, attr, curr_context, hotkey, customaction); 906 917 } 907 918 switch (onplay_result) 908 919 { ··· 911 922 break; 912 923 913 924 case ONPLAY_OK: 914 - restore = true; 925 + restore = do_restore_display; 915 926 break; 916 927 917 928 case ONPLAY_RELOAD_DIR: ··· 988 999 989 1000 lastfilter = *tc.dirfilter; 990 1001 lastsortcase = global_settings.sort_case; 991 - restore = true; 1002 + restore = do_restore_display; 992 1003 } 993 1004 994 1005 if (exit_func)
+3
apps/tree.h
··· 33 33 char *name; 34 34 int attr; /* FAT attributes + file type flags */ 35 35 unsigned time_write; /* Last write time */ 36 + #ifdef HAVE_TAGCACHE 37 + int customaction; /* db use */ 38 + #endif 36 39 }; 37 40 38 41 #define BROWSE_SELECTONLY 0x0001 /* exit on selecting a file */
+15
manual/rockbox_interface/tagcache.tex
··· 137 137 to use it just do not do the initial build of the database and do not load it 138 138 to RAM.}% 139 139 140 + If your total amount of music tracks exceeds the value of the 141 + \setting{Max Playlist Size} setting (\setting{Settings $\rightarrow$ General 142 + Settings $\rightarrow$ System $\rightarrow$ Limits}), using the database 143 + will be your only way to shuffle between all songs from your music library. 144 + Any view on the database browser that exceeds the maximum value of this option 145 + will be automatically adjusted and randomized to fit into the available space 146 + when you will create a dynamic playlist from the view. 147 + Using the database browser is recommended if you shuffle regularly between a lot of 148 + songs rather than increasing your limit, so you will get the best possible performance 149 + on this action. 150 + 151 + \note{For your convenience, a shortcut button "Shuffle Songs" is available directly 152 + from the \setting{Database} menu to create and start a mix with all of your 153 + existing music tracks.} 154 + 140 155 \begin{table} 141 156 \begin{rbtabular}{.75\textwidth}{XXX}% 142 157 {\textbf{Tag} & \textbf{Type} & \textbf{Origin}}{}{}