summaryrefslogtreecommitdiff
path: root/apps
diff options
context:
space:
mode:
authorWilliam Wilgus <wilgus.william@gmail.com>2024-09-06 22:50:38 -0400
committerWilliam Wilgus <wilgus.william@gmail.com>2024-09-08 12:09:04 -0400
commita86e2b5c6e2202827004755358db1dac0fde5540 (patch)
tree46970f875ea1157dcfd0b36cafdcb81f1a036f8c /apps
parent22b05c97a3f9ab318b737a14bc9beb10197718de (diff)
downloadrockbox-a86e2b5c6e2202827004755358db1dac0fde5540.tar.gz
rockbox-a86e2b5c6e2202827004755358db1dac0fde5540.zip
Tagtree selective random playlist -- rewrite
this is a bit cleaner without so much ram (or code) used Credit to goes to Paul Sauro (OlsroFR) as this was his idea if available space exists in the pluginbuf it uses fisher yates shuffle to get good probability and falls back to random permutation if not Change-Id: I413078a48314ce4c6f3722c78e0858a407b7b46e
Diffstat (limited to 'apps')
-rw-r--r--apps/tagtree.c198
1 files changed, 94 insertions, 104 deletions
diff --git a/apps/tagtree.c b/apps/tagtree.c
index 562ad44863..126fb04678 100644
--- a/apps/tagtree.c
+++ b/apps/tagtree.c
@@ -57,6 +57,7 @@
57#include "strnatcmp.h" 57#include "strnatcmp.h"
58#include "panic.h" 58#include "panic.h"
59#include "onplay.h" 59#include "onplay.h"
60#include "plugin.h"
60 61
61#define str_or_empty(x) (x ? x : "(NULL)") 62#define str_or_empty(x) (x ? x : "(NULL)")
62 63
@@ -117,14 +118,6 @@ enum variables {
117#define UNIQBUF_SIZE (64*1024) 118#define UNIQBUF_SIZE (64*1024)
118static uint32_t uniqbuf[UNIQBUF_SIZE / sizeof(uint32_t)]; 119static uint32_t uniqbuf[UNIQBUF_SIZE / sizeof(uint32_t)];
119 120
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
126static bool selective_random_playlist_indexes[INSERT_ALL_PLAYLIST_MAX_SEGMENT_SIZE];
127
128#define MAX_TAGS 5 121#define MAX_TAGS 5
129#define MAX_MENU_ID_SIZE 32 122#define MAX_MENU_ID_SIZE 32
130 123
@@ -2141,30 +2134,26 @@ static void swap_array_bool(bool *a, bool *b)
2141/** 2134/**
2142 * Randomly shuffle an array using the Fisher-Yates algorithm : 2135 * Randomly shuffle an array using the Fisher-Yates algorithm :
2143 * https://en.wikipedia.org/wiki/Random_permutation 2136 * https://en.wikipedia.org/wiki/Random_permutation
2144 * This algorithm has a linear complexity. Don't forget to srand 2137 * This algorithm has a linear complexity.
2145 * before call to use it with a relevant seed. 2138 * Don't forget to srand before call to use it with a relevant seed.
2146 */ 2139 */
2147static void shuffle_bool_array(bool array[], int size) 2140static bool* fill_random_playlist_indexes(bool *bool_array, size_t arr_sz,
2141 size_t track_count, size_t max_slots)
2148{ 2142{
2149 for (int i = size - 1; i > 0; i--) 2143 size_t i;
2144 if (track_count * sizeof(bool) > arr_sz || max_slots > track_count)
2145 return NULL;
2146
2147 for (i = 0; i < arr_sz; i++) /* fill max_slots with TRUE */
2148 bool_array[i] = i < max_slots;
2149
2150 /* shuffle bool array */
2151 for (i = track_count - 1; i > 0; i--)
2150 { 2152 {
2151 int j = rand() % (i + 1); 2153 int j = rand() % (i + 1);
2152 swap_array_bool(&array[i], &array[j]); 2154 swap_array_bool(&bool_array[i], &bool_array[j]);
2153 } 2155 }
2154} 2156 return bool_array;
2155
2156static bool fill_random_playlist_indexes(int current_segment_n,
2157 int current_segment_max_space)
2158{
2159 if (current_segment_n == 0 || current_segment_max_space == 0)
2160 return false;
2161 if (current_segment_max_space > current_segment_n)
2162 current_segment_max_space = current_segment_n;
2163 for (int i = 0; i < current_segment_n; i++)
2164 selective_random_playlist_indexes[i] = i < current_segment_max_space;
2165 srand(current_tick);
2166 shuffle_bool_array(selective_random_playlist_indexes, current_segment_n);
2167 return true;
2168} 2157}
2169 2158
2170static bool insert_all_playlist(struct tree_context *c, 2159static bool insert_all_playlist(struct tree_context *c,
@@ -2175,6 +2164,9 @@ static bool insert_all_playlist(struct tree_context *c,
2175 int n; 2164 int n;
2176 int fd = -1; 2165 int fd = -1;
2177 unsigned long last_tick; 2166 unsigned long last_tick;
2167 int slots_remaining = 0;
2168 bool fill_randomly = false;
2169 bool *rand_bool_array = NULL;
2178 char buf[MAX_PATH]; 2170 char buf[MAX_PATH];
2179 2171
2180 cpu_boost(true); 2172 cpu_boost(true);
@@ -2213,113 +2205,111 @@ static bool insert_all_playlist(struct tree_context *c,
2213 splash_progress_set_delay(HZ / 2); /* wait 1/2 sec before progress */ 2205 splash_progress_set_delay(HZ / 2); /* wait 1/2 sec before progress */
2214 n = c->filesindir; 2206 n = c->filesindir;
2215 2207
2216 int max_playlist_size = playlist_get_current()->max_playlist_size;
2217 int playlist_amount = playlist_get_current()->amount;
2218 int segment_size = INSERT_ALL_PLAYLIST_MAX_SEGMENT_SIZE;
2219 int segments_count = n / segment_size;
2220 int leftovers_segment_size = n - (segments_count * segment_size);
2221 bool fill_randomly = false;
2222
2223 if (playlist == NULL) 2208 if (playlist == NULL)
2224 { 2209 {
2225 bool will_exceed = n > max_playlist_size; 2210 int max_playlist_size = playlist_get_current()->max_playlist_size;
2226 fill_randomly = will_exceed; 2211 slots_remaining = max_playlist_size - playlist_get_current()->amount;
2227 } 2212 if (slots_remaining <= 0)
2228 if (leftovers_segment_size > 0 && fill_randomly)
2229 {
2230 /* We need to re-balance the segments so the randomness will be
2231 * coherent and balanced the same through all segments */
2232 while (leftovers_segment_size + segments_count < segment_size)
2233 { 2213 {
2234 segment_size--; // -1 to all other segments 2214 logf("Playlist has no space remaining");
2235 leftovers_segment_size += segments_count; 2215 cpu_boost(false);
2216 return false;
2236 } 2217 }
2237 }
2238 if (leftovers_segment_size > 0)
2239 segments_count += 1;
2240
2241 int max_available_space = max_playlist_size - playlist_amount;
2242 int max_space_per_segment = max_available_space / segments_count;
2243
2244 int remaining_space =
2245 max_available_space - (max_space_per_segment * segments_count);
2246 2218
2247 if (fill_randomly) 2219 fill_randomly = n > slots_remaining;
2248 { 2220
2249 talk_id(LANG_RANDOM_SHUFFLE_RANDOM_SELECTIVE_SONGS_SUMMARY, true); 2221 if (fill_randomly)
2250 splashf(HZ * 3, str(LANG_RANDOM_SHUFFLE_RANDOM_SELECTIVE_SONGS_SUMMARY), 2222 {
2251 max_space_per_segment * segments_count + remaining_space); 2223 srand(current_tick);
2252 /* logf("sz=%d lsz=%d sc=%d rcps=%d", segment_size, leftovers_segment_size, 2224 size_t bufsize = 0;
2253 segments_count, max_available_space_per_segment); */ 2225 bool *buffer = (bool *) plugin_get_buffer(&bufsize);
2226 rand_bool_array = fill_random_playlist_indexes(buffer, bufsize,
2227 n, slots_remaining);
2228
2229 talk_id(LANG_RANDOM_SHUFFLE_RANDOM_SELECTIVE_SONGS_SUMMARY, true);
2230 splashf(HZ * 2, str(LANG_RANDOM_SHUFFLE_RANDOM_SELECTIVE_SONGS_SUMMARY),
2231 slots_remaining);
2232 }
2254 } 2233 }
2255 2234
2256 bool exit_loop_now = false; 2235 bool exit_loop_now = false;
2257 for (int i = 0; i < segments_count; i++) 2236 for (int i = 0; i < n; i++)
2258 { 2237 {
2259 int cur_segment_size = segment_size; 2238 if (TIME_AFTER(current_tick, last_tick + HZ/4))
2260 int cur_segment_start = i * segment_size;
2261 int cur_segment_end;
2262 int space_per_segment = max_space_per_segment;
2263
2264 if (i + 1 >= segments_count && leftovers_segment_size > 0)
2265 {
2266 /* add any remaining tracks to the last segment */
2267 space_per_segment += remaining_space;
2268 cur_segment_size = leftovers_segment_size;
2269 }
2270 else if (fill_randomly)
2271 { 2239 {
2272 /* add an extra track to some of the segments */ 2240 splash_progress(i, n, "%s (%s)", str(LANG_WAIT), str(LANG_OFF_ABORT));
2273 if (remaining_space > 0 && (i & 7) != 7) 2241 if (action_userabort(TIMEOUT_NOBLOCK))
2274 { 2242 {
2275 space_per_segment++; 2243 exit_loop_now = true;
2276 remaining_space--; 2244 break;
2277 } 2245 }
2246 last_tick = current_tick;
2278 } 2247 }
2279 if (fill_randomly)
2280 {
2281 fill_randomly = fill_random_playlist_indexes(cur_segment_size,
2282 space_per_segment);
2283 }
2284
2285 cur_segment_end = cur_segment_start + cur_segment_size;
2286 2248
2287 for (int j = cur_segment_start; j < cur_segment_end && !exit_loop_now; j++) 2249 if (playlist == NULL)
2288 { 2250 {
2289 if (TIME_AFTER(current_tick, last_tick + HZ/4)) 2251 if (fill_randomly)
2290 { 2252 {
2291 splash_progress(j, n, "%s (%s)", str(LANG_WAIT), str(LANG_OFF_ABORT)); 2253 int remaining_tracks = n - i;
2292 if (action_userabort(TIMEOUT_NOBLOCK)) 2254 if (remaining_tracks > slots_remaining)
2293 { 2255 {
2294 exit_loop_now = true; 2256 if (rand_bool_array)
2295 break; 2257 {
2258 /* Skip the track if rand_bool_array[i] is FALSE */
2259 if (!rand_bool_array[i])
2260 continue;
2261 }
2262 else
2263 {
2264 /* Generate random value between 0 and remaining_tracks - 1 */
2265 int selrange = RAND_MAX / remaining_tracks; /* Improve distribution */
2266 int random;
2267
2268 for (int r = 0; r < 0x0FFF; r++) /* limit loops */
2269 {
2270 random = rand() / selrange;
2271 if (random < remaining_tracks)
2272 break;
2273 else
2274 random = 0;
2275 }
2276 /* Skip the track if random >= slots_remaining */
2277 if (random >= slots_remaining)
2278 continue;
2279 }
2296 } 2280 }
2297 last_tick = current_tick;
2298 } 2281 }
2282 }
2299 2283
2300 if (fill_randomly && !selective_random_playlist_indexes[j % segment_size]) 2284 if (!tagcache_retrieve(&tcs, tagtree_get_entry(c, i)->extraseek, tcs.type, buf, sizeof buf))
2301 continue; 2285 continue;
2302
2303 if (!tagcache_retrieve(&tcs, tagtree_get_entry(c, j)->extraseek, tcs.type, buf, sizeof buf))
2304 continue;
2305 2286
2306 if (playlist == NULL) 2287 if (playlist == NULL)
2288 {
2289 if (fill_randomly)
2307 { 2290 {
2308 if (playlist_insert_track(NULL, buf, position, queue, false) < 0) { 2291 if (--slots_remaining <= 0)
2309 logf("playlist_insert_track failed"); 2292 {
2310 exit_loop_now = true; 2293 exit_loop_now = true;
2311 break; 2294 break;
2312 } 2295 }
2313 } 2296 }
2314 else if (fdprintf(fd, "%s\n", buf) <= 0) 2297
2315 { 2298 if (playlist_insert_track(NULL, buf, position, queue, false) < 0) {
2299 logf("playlist_insert_track failed");
2316 exit_loop_now = true; 2300 exit_loop_now = true;
2317 break; 2301 break;
2318 } 2302 }
2319 yield();
2320 if (playlist == NULL && position == PLAYLIST_INSERT_FIRST)
2321 position = PLAYLIST_INSERT;
2322 } 2303 }
2304 else if (fdprintf(fd, "%s\n", buf) <= 0)
2305 {
2306 exit_loop_now = true;
2307 break;
2308 }
2309 yield();
2310 if (playlist == NULL && position == PLAYLIST_INSERT_FIRST)
2311 position = PLAYLIST_INSERT;
2312
2323 if (exit_loop_now) 2313 if (exit_loop_now)
2324 break; 2314 break;
2325 } 2315 }