diff options
-rw-r--r-- | apps/debug_menu.c | 21 | ||||
-rw-r--r-- | apps/dsp.c | 60 | ||||
-rw-r--r-- | apps/filetree.c | 31 | ||||
-rw-r--r-- | apps/filetypes.c | 22 | ||||
-rw-r--r-- | apps/main.c | 6 | ||||
-rw-r--r-- | apps/menus/playback_menu.c | 2 | ||||
-rw-r--r-- | apps/mpeg.c | 121 | ||||
-rw-r--r-- | apps/playback.c | 76 | ||||
-rw-r--r-- | apps/playlist.c | 95 | ||||
-rw-r--r-- | apps/playlist.h | 9 | ||||
-rw-r--r-- | apps/plugin.c | 6 | ||||
-rw-r--r-- | apps/plugin.h | 2 | ||||
-rw-r--r-- | apps/plugins/imageviewer/imageviewer.c | 2 | ||||
-rw-r--r-- | apps/plugins/mikmod/mikmod.c | 2 | ||||
-rw-r--r-- | apps/plugins/mpegplayer/mpegplayer.c | 2 | ||||
-rw-r--r-- | apps/plugins/rockpaint.c | 2 | ||||
-rw-r--r-- | apps/scrobbler.c | 8 | ||||
-rw-r--r-- | apps/tagcache.c | 144 | ||||
-rw-r--r-- | apps/tagcache.h | 4 | ||||
-rw-r--r-- | apps/tagtree.c | 229 | ||||
-rw-r--r-- | apps/tagtree.h | 9 | ||||
-rw-r--r-- | apps/tdspeed.c | 75 | ||||
-rw-r--r-- | apps/tdspeed.h | 3 | ||||
-rw-r--r-- | apps/tree.c | 78 | ||||
-rw-r--r-- | apps/tree.h | 36 | ||||
-rw-r--r-- | firmware/buflib.c | 5 | ||||
-rw-r--r-- | firmware/common/dircache.c | 114 | ||||
-rw-r--r-- | firmware/core_alloc.c | 1 |
28 files changed, 866 insertions, 299 deletions
diff --git a/apps/debug_menu.c b/apps/debug_menu.c index fb8575ec62..6375094225 100644 --- a/apps/debug_menu.c +++ b/apps/debug_menu.c | |||
@@ -425,11 +425,32 @@ static const char* bf_getname(int selected_item, void *data, | |||
425 | return buffer; | 425 | return buffer; |
426 | } | 426 | } |
427 | 427 | ||
428 | static int bf_action_cb(int action, struct gui_synclist* list) | ||
429 | { | ||
430 | if (action == ACTION_STD_OK) | ||
431 | { | ||
432 | splash(HZ/1, "Attempting a 64k allocation"); | ||
433 | int handle = core_alloc("test", 64<<10); | ||
434 | splash(HZ/2, (handle > 0) ? "Success":"Fail"); | ||
435 | /* for some reason simplelist doesn't allow adding items here if | ||
436 | * info.get_name is given, so use normal list api */ | ||
437 | gui_synclist_set_nb_items(list, core_get_num_blocks()); | ||
438 | if (handle > 0) | ||
439 | core_free(handle); | ||
440 | action = ACTION_REDRAW; | ||
441 | } | ||
442 | else if (action == ACTION_NONE) | ||
443 | action = ACTION_REDRAW; | ||
444 | return action; | ||
445 | } | ||
446 | |||
428 | static bool dbg_buflib_allocs(void) | 447 | static bool dbg_buflib_allocs(void) |
429 | { | 448 | { |
430 | struct simplelist_info info; | 449 | struct simplelist_info info; |
431 | simplelist_info_init(&info, "mem allocs", core_get_num_blocks(), NULL); | 450 | simplelist_info_init(&info, "mem allocs", core_get_num_blocks(), NULL); |
432 | info.get_name = bf_getname; | 451 | info.get_name = bf_getname; |
452 | info.action_callback = bf_action_cb; | ||
453 | info.timeout = HZ/2; | ||
433 | return simplelist_show_list(&info); | 454 | return simplelist_show_list(&info); |
434 | } | 455 | } |
435 | 456 | ||
diff --git a/apps/dsp.c b/apps/dsp.c index a728dd75ea..167c043427 100644 --- a/apps/dsp.c +++ b/apps/dsp.c | |||
@@ -318,30 +318,50 @@ static void tdspeed_setup(struct dsp_config *dspc) | |||
318 | resample_buf = big_resample_buf; | 318 | resample_buf = big_resample_buf; |
319 | } | 319 | } |
320 | 320 | ||
321 | |||
322 | static int move_callback(int handle, void* current, void* new) | ||
323 | { | ||
324 | /* TODO */ | ||
325 | (void)handle;(void)current;; | ||
326 | big_sample_buf = new; | ||
327 | return BUFLIB_CB_OK; | ||
328 | } | ||
329 | |||
330 | static struct buflib_callbacks ops = { | ||
331 | .move_callback = move_callback, | ||
332 | .shrink_callback = NULL, | ||
333 | }; | ||
334 | |||
335 | |||
321 | void dsp_timestretch_enable(bool enabled) | 336 | void dsp_timestretch_enable(bool enabled) |
322 | { | 337 | { |
323 | /* Hook to set up timestretch buffer on first call to settings_apply() */ | 338 | /* Hook to set up timestretch buffer on first call to settings_apply() */ |
324 | if (big_sample_buf_count < 0) /* Only do something on first call */ | 339 | static int handle; |
340 | if (enabled) | ||
325 | { | 341 | { |
326 | if (enabled) | 342 | if (big_sample_buf_count > 0) |
327 | { | 343 | return; /* already allocated and enabled */ |
328 | int handle; | 344 | /* Set up timestretch buffers */ |
329 | /* Set up timestretch buffers */ | 345 | big_sample_buf_count = SMALL_SAMPLE_BUF_COUNT * RESAMPLE_RATIO; |
330 | big_sample_buf_count = SMALL_SAMPLE_BUF_COUNT * RESAMPLE_RATIO; | 346 | big_sample_buf = small_resample_buf; |
331 | big_sample_buf = small_resample_buf; | 347 | handle = core_alloc_ex("resample buf", |
332 | handle = core_alloc("resample buf", | 348 | big_sample_buf_count * RESAMPLE_RATIO * sizeof(int32_t), &ops); |
333 | big_sample_buf_count * RESAMPLE_RATIO * sizeof(int32_t)); | 349 | if (handle > 0) |
334 | if (handle > 0) | 350 | { /* success, now setup tdspeed */ |
335 | big_resample_buf = core_get_data(handle); | 351 | big_resample_buf = core_get_data(handle); |
336 | else | 352 | tdspeed_init(); |
337 | big_sample_buf_count = 0; | 353 | tdspeed_setup(&AUDIO_DSP); |
338 | } | 354 | } |
339 | else | 355 | } |
340 | { | 356 | if (!enabled || (handle <= 0)) /* disable */ |
341 | /* Not enabled at startup, "big" buffers will never be available */ | 357 | { |
342 | big_sample_buf_count = 0; | 358 | dsp_set_timestretch(PITCH_SPEED_100); |
343 | } | 359 | tdspeed_finish(); |
344 | tdspeed_setup(&AUDIO_DSP); | 360 | if (handle > 0) |
361 | core_free(handle); | ||
362 | handle = 0; | ||
363 | big_sample_buf = NULL; | ||
364 | big_sample_buf_count = 0; | ||
345 | } | 365 | } |
346 | } | 366 | } |
347 | 367 | ||
@@ -1211,7 +1231,7 @@ int dsp_callback(int msg, intptr_t param) | |||
1211 | */ | 1231 | */ |
1212 | int dsp_process(struct dsp_config *dsp, char *dst, const char *src[], int count) | 1232 | int dsp_process(struct dsp_config *dsp, char *dst, const char *src[], int count) |
1213 | { | 1233 | { |
1214 | int32_t *tmp[2]; | 1234 | static int32_t *tmp[2]; /* tdspeed_doit() needs it static */ |
1215 | static long last_yield; | 1235 | static long last_yield; |
1216 | long tick; | 1236 | long tick; |
1217 | int written = 0; | 1237 | int written = 0; |
diff --git a/apps/filetree.c b/apps/filetree.c index 1aee80b6b2..35bb2a8fd0 100644 --- a/apps/filetree.c +++ b/apps/filetree.c | |||
@@ -29,6 +29,7 @@ | |||
29 | #include <limits.h> | 29 | #include <limits.h> |
30 | #include "bookmark.h" | 30 | #include "bookmark.h" |
31 | #include "tree.h" | 31 | #include "tree.h" |
32 | #include "core_alloc.h" | ||
32 | #include "settings.h" | 33 | #include "settings.h" |
33 | #include "filetypes.h" | 34 | #include "filetypes.h" |
34 | #include "talk.h" | 35 | #include "talk.h" |
@@ -60,7 +61,8 @@ int ft_build_playlist(struct tree_context* c, int start_index) | |||
60 | int i; | 61 | int i; |
61 | int start=start_index; | 62 | int start=start_index; |
62 | 63 | ||
63 | struct entry *entries = c->cache.entries; | 64 | tree_lock_cache(c); |
65 | struct entry *entries = tree_get_entries(c); | ||
64 | 66 | ||
65 | for(i = 0;i < c->filesindir;i++) | 67 | for(i = 0;i < c->filesindir;i++) |
66 | { | 68 | { |
@@ -77,6 +79,8 @@ int ft_build_playlist(struct tree_context* c, int start_index) | |||
77 | } | 79 | } |
78 | } | 80 | } |
79 | 81 | ||
82 | tree_unlock_cache(c); | ||
83 | |||
80 | return start_index; | 84 | return start_index; |
81 | } | 85 | } |
82 | 86 | ||
@@ -127,13 +131,15 @@ static void check_file_thumbnails(struct tree_context* c) | |||
127 | { | 131 | { |
128 | int i; | 132 | int i; |
129 | struct dirent *entry; | 133 | struct dirent *entry; |
130 | struct entry* entries = c->cache.entries; | 134 | struct entry* entries; |
131 | DIR *dir; | 135 | DIR *dir; |
132 | 136 | ||
133 | dir = opendir(c->currdir); | 137 | dir = opendir(c->currdir); |
134 | if(!dir) | 138 | if(!dir) |
135 | return; | 139 | return; |
136 | /* mark all files as non talking, except the .talk ones */ | 140 | /* mark all files as non talking, except the .talk ones */ |
141 | entries = tree_get_entries(c); | ||
142 | tree_lock_cache(c); | ||
137 | for (i=0; i < c->filesindir; i++) | 143 | for (i=0; i < c->filesindir; i++) |
138 | { | 144 | { |
139 | if (entries[i].attr & ATTR_DIRECTORY) | 145 | if (entries[i].attr & ATTR_DIRECTORY) |
@@ -177,6 +183,7 @@ static void check_file_thumbnails(struct tree_context* c) | |||
177 | } | 183 | } |
178 | } | 184 | } |
179 | } | 185 | } |
186 | tree_unlock_cache(c); | ||
180 | closedir(dir); | 187 | closedir(dir); |
181 | } | 188 | } |
182 | 189 | ||
@@ -287,11 +294,11 @@ int ft_load(struct tree_context* c, const char* tempdir) | |||
287 | c->dirsindir = 0; | 294 | c->dirsindir = 0; |
288 | c->dirfull = false; | 295 | c->dirfull = false; |
289 | 296 | ||
297 | tree_lock_cache(c); | ||
290 | while ((entry = readdir(dir))) { | 298 | while ((entry = readdir(dir))) { |
291 | int len; | 299 | int len; |
292 | struct dirinfo info; | 300 | struct dirinfo info; |
293 | struct entry* table = c->cache.entries; | 301 | struct entry* dptr = tree_get_entry_at(c, files_in_dir); |
294 | struct entry* dptr = &table[files_in_dir]; | ||
295 | if (!entry) | 302 | if (!entry) |
296 | break; | 303 | break; |
297 | 304 | ||
@@ -369,7 +376,7 @@ int ft_load(struct tree_context* c, const char* tempdir) | |||
369 | 376 | ||
370 | ++files_in_dir; | 377 | ++files_in_dir; |
371 | 378 | ||
372 | dptr->name = &c->cache.name_buffer[name_buffer_used]; | 379 | dptr->name = core_get_data(c->cache.name_buffer_handle)+name_buffer_used; |
373 | dptr->time_write = | 380 | dptr->time_write = |
374 | (long)info.wrtdate<<16 | | 381 | (long)info.wrtdate<<16 | |
375 | (long)info.wrttime; /* in one # */ | 382 | (long)info.wrttime; /* in one # */ |
@@ -384,13 +391,14 @@ int ft_load(struct tree_context* c, const char* tempdir) | |||
384 | closedir(dir); | 391 | closedir(dir); |
385 | 392 | ||
386 | compare_sort_dir = c->sort_dir; | 393 | compare_sort_dir = c->sort_dir; |
387 | qsort(c->cache.entries, files_in_dir, sizeof(struct entry), compare); | 394 | qsort(tree_get_entries(c), files_in_dir, sizeof(struct entry), compare); |
388 | 395 | ||
389 | /* If thumbnail talking is enabled, make an extra run to mark files with | 396 | /* If thumbnail talking is enabled, make an extra run to mark files with |
390 | associated thumbnails, so we don't do unsuccessful spinups later. */ | 397 | associated thumbnails, so we don't do unsuccessful spinups later. */ |
391 | if (global_settings.talk_file_clip) | 398 | if (global_settings.talk_file_clip) |
392 | check_file_thumbnails(c); /* map .talk to ours */ | 399 | check_file_thumbnails(c); /* map .talk to ours */ |
393 | 400 | ||
401 | tree_unlock_cache(c); | ||
394 | return 0; | 402 | return 0; |
395 | } | 403 | } |
396 | #ifdef HAVE_LCD_BITMAP | 404 | #ifdef HAVE_LCD_BITMAP |
@@ -424,15 +432,15 @@ int ft_enter(struct tree_context* c) | |||
424 | { | 432 | { |
425 | int rc = GO_TO_PREVIOUS; | 433 | int rc = GO_TO_PREVIOUS; |
426 | char buf[MAX_PATH]; | 434 | char buf[MAX_PATH]; |
427 | struct entry* table = c->cache.entries; | 435 | struct entry* file = tree_get_entry_at(c, c->selected_item); |
428 | struct entry *file = &table[c->selected_item]; | 436 | int file_attr = file->attr; |
429 | 437 | ||
430 | if (c->currdir[1]) | 438 | if (c->currdir[1]) |
431 | snprintf(buf,sizeof(buf),"%s/%s",c->currdir, file->name); | 439 | snprintf(buf,sizeof(buf),"%s/%s",c->currdir, file->name); |
432 | else | 440 | else |
433 | snprintf(buf,sizeof(buf),"/%s",file->name); | 441 | snprintf(buf,sizeof(buf),"/%s",file->name); |
434 | 442 | ||
435 | if (file->attr & ATTR_DIRECTORY) { | 443 | if (file_attr & ATTR_DIRECTORY) { |
436 | memcpy(c->currdir, buf, sizeof(c->currdir)); | 444 | memcpy(c->currdir, buf, sizeof(c->currdir)); |
437 | if ( c->dirlevel < MAX_DIR_LEVELS ) | 445 | if ( c->dirlevel < MAX_DIR_LEVELS ) |
438 | c->selected_item_history[c->dirlevel] = c->selected_item; | 446 | c->selected_item_history[c->dirlevel] = c->selected_item; |
@@ -444,7 +452,7 @@ int ft_enter(struct tree_context* c) | |||
444 | bool play = false; | 452 | bool play = false; |
445 | int start_index=0; | 453 | int start_index=0; |
446 | 454 | ||
447 | switch ( file->attr & FILE_ATTR_MASK ) { | 455 | switch ( file_attr & FILE_ATTR_MASK ) { |
448 | case FILE_ATTR_M3U: | 456 | case FILE_ATTR_M3U: |
449 | if (!bookmark_autoload(buf)) | 457 | if (!bookmark_autoload(buf)) |
450 | playlist_viewer_ex(buf); | 458 | playlist_viewer_ex(buf); |
@@ -612,7 +620,7 @@ int ft_enter(struct tree_context* c) | |||
612 | char *plugin = buf, *argument = NULL, lua_path[MAX_PATH]; | 620 | char *plugin = buf, *argument = NULL, lua_path[MAX_PATH]; |
613 | int ret; | 621 | int ret; |
614 | 622 | ||
615 | if ((file->attr & FILE_ATTR_MASK) == FILE_ATTR_LUA) { | 623 | if ((file_attr & FILE_ATTR_MASK) == FILE_ATTR_LUA) { |
616 | snprintf(lua_path, sizeof(lua_path)-1, "%s/lua.rock", VIEWERS_DIR); /* Use a #define here ? */ | 624 | snprintf(lua_path, sizeof(lua_path)-1, "%s/lua.rock", VIEWERS_DIR); /* Use a #define here ? */ |
617 | plugin = lua_path; | 625 | plugin = lua_path; |
618 | argument = buf; | 626 | argument = buf; |
@@ -658,6 +666,7 @@ int ft_enter(struct tree_context* c) | |||
658 | break; | 666 | break; |
659 | } | 667 | } |
660 | 668 | ||
669 | struct entry* file = tree_get_entry_at(c, c->selected_item); | ||
661 | plugin = filetype_get_plugin(file); | 670 | plugin = filetype_get_plugin(file); |
662 | if (plugin) | 671 | if (plugin) |
663 | { | 672 | { |
diff --git a/apps/filetypes.c b/apps/filetypes.c index c52c734a1d..942ff329fe 100644 --- a/apps/filetypes.c +++ b/apps/filetypes.c | |||
@@ -184,6 +184,26 @@ static unsigned char highest_attr = 0; | |||
184 | static int viewer_count = 0; | 184 | static int viewer_count = 0; |
185 | 185 | ||
186 | static int strdup_handle, strdup_bufsize, strdup_cur_idx; | 186 | static int strdup_handle, strdup_bufsize, strdup_cur_idx; |
187 | static int move_callback(int handle, void* current, void* new) | ||
188 | { | ||
189 | /*could compare to strdup_handle, but ops is only used once */ | ||
190 | (void)handle; | ||
191 | size_t diff = new - current; | ||
192 | #define FIX_PTR(x) \ | ||
193 | { if ((void*)x > current && (void*)x < (current+strdup_bufsize)) x+= diff; } | ||
194 | for(int i = 0; i < filetype_count; i++) | ||
195 | { | ||
196 | FIX_PTR(filetypes[i].extension); | ||
197 | FIX_PTR(filetypes[i].plugin); | ||
198 | } | ||
199 | return BUFLIB_CB_OK; | ||
200 | } | ||
201 | |||
202 | static struct buflib_callbacks ops = { | ||
203 | .move_callback = move_callback, | ||
204 | .shrink_callback = NULL, | ||
205 | }; | ||
206 | |||
187 | static char *filetypes_strdup(char* string) | 207 | static char *filetypes_strdup(char* string) |
188 | { | 208 | { |
189 | char *buffer = core_get_data(strdup_handle) + strdup_cur_idx; | 209 | char *buffer = core_get_data(strdup_handle) + strdup_cur_idx; |
@@ -323,7 +343,7 @@ void filetype_init(void) | |||
323 | return; | 343 | return; |
324 | 344 | ||
325 | strdup_bufsize = filesize(fd); | 345 | strdup_bufsize = filesize(fd); |
326 | strdup_handle = core_alloc("filetypes", strdup_bufsize); | 346 | strdup_handle = core_alloc_ex("filetypes", strdup_bufsize, &ops); |
327 | if (strdup_handle <= 0) | 347 | if (strdup_handle <= 0) |
328 | return; | 348 | return; |
329 | read_builtin_types(); | 349 | read_builtin_types(); |
diff --git a/apps/main.c b/apps/main.c index cc9c9e8d8e..07a8bba44c 100644 --- a/apps/main.c +++ b/apps/main.c | |||
@@ -403,9 +403,6 @@ static void init(void) | |||
403 | #endif /* CONFIG_CODEC != SWCODEC */ | 403 | #endif /* CONFIG_CODEC != SWCODEC */ |
404 | 404 | ||
405 | scrobbler_init(); | 405 | scrobbler_init(); |
406 | #if CONFIG_CODEC == SWCODEC && defined (HAVE_PITCHSCREEN) | ||
407 | tdspeed_init(); | ||
408 | #endif /* CONFIG_CODEC == SWCODEC */ | ||
409 | 406 | ||
410 | audio_init(); | 407 | audio_init(); |
411 | 408 | ||
@@ -659,9 +656,6 @@ static void init(void) | |||
659 | tree_mem_init(); | 656 | tree_mem_init(); |
660 | filetype_init(); | 657 | filetype_init(); |
661 | scrobbler_init(); | 658 | scrobbler_init(); |
662 | #if CONFIG_CODEC == SWCODEC && defined (HAVE_PITCHSCREEN) | ||
663 | tdspeed_init(); | ||
664 | #endif /* CONFIG_CODEC == SWCODEC */ | ||
665 | theme_init_buffer(); | 659 | theme_init_buffer(); |
666 | 660 | ||
667 | #if CONFIG_CODEC != SWCODEC | 661 | #if CONFIG_CODEC != SWCODEC |
diff --git a/apps/menus/playback_menu.c b/apps/menus/playback_menu.c index 1b1a13a6a5..a219373a8b 100644 --- a/apps/menus/playback_menu.c +++ b/apps/menus/playback_menu.c | |||
@@ -142,7 +142,7 @@ static int audioscrobbler_callback(int action,const struct menu_item_ex *this_it | |||
142 | { | 142 | { |
143 | case ACTION_EXIT_MENUITEM: /* on exit */ | 143 | case ACTION_EXIT_MENUITEM: /* on exit */ |
144 | if (!scrobbler_is_enabled() && global_settings.audioscrobbler) | 144 | if (!scrobbler_is_enabled() && global_settings.audioscrobbler) |
145 | splash(HZ*2, ID2P(LANG_PLEASE_REBOOT)); | 145 | scrobbler_init(); |
146 | 146 | ||
147 | if(scrobbler_is_enabled() && !global_settings.audioscrobbler) | 147 | if(scrobbler_is_enabled() && !global_settings.audioscrobbler) |
148 | scrobbler_shutdown(); | 148 | scrobbler_shutdown(); |
diff --git a/apps/mpeg.c b/apps/mpeg.c index 595f943545..0b1413e195 100644 --- a/apps/mpeg.c +++ b/apps/mpeg.c | |||
@@ -39,7 +39,7 @@ | |||
39 | #include "mp3_playback.h" | 39 | #include "mp3_playback.h" |
40 | #include "talk.h" | 40 | #include "talk.h" |
41 | #include "sound.h" | 41 | #include "sound.h" |
42 | #include "bitswap.h" | 42 | #include "system.h" |
43 | #include "appevents.h" | 43 | #include "appevents.h" |
44 | #include "playlist.h" | 44 | #include "playlist.h" |
45 | #include "cuesheet.h" | 45 | #include "cuesheet.h" |
@@ -140,6 +140,7 @@ static struct cuesheet *curr_cuesheet = NULL; | |||
140 | static bool checked_for_cuesheet = false; | 140 | static bool checked_for_cuesheet = false; |
141 | 141 | ||
142 | static const char mpeg_thread_name[] = "mpeg"; | 142 | static const char mpeg_thread_name[] = "mpeg"; |
143 | static unsigned int audio_thread_id; | ||
143 | static unsigned int mpeg_errno; | 144 | static unsigned int mpeg_errno; |
144 | 145 | ||
145 | static bool playing = false; /* We are playing an MP3 stream */ | 146 | static bool playing = false; /* We are playing an MP3 stream */ |
@@ -492,20 +493,81 @@ unsigned long mpeg_get_last_header(void) | |||
492 | #endif /* !SIMULATOR */ | 493 | #endif /* !SIMULATOR */ |
493 | } | 494 | } |
494 | 495 | ||
495 | /* Buffer must not move. And not shrink for now */ | 496 | static void do_stop(void) |
496 | static struct buflib_callbacks ops = { NULL, NULL }; | 497 | { |
498 | is_playing = false; | ||
499 | paused = false; | ||
500 | |||
501 | #ifndef SIMULATOR | ||
502 | if (playing) | ||
503 | playlist_update_resume_info(audio_current_track()); | ||
504 | |||
505 | stop_playing(); | ||
506 | mpeg_stop_done = true; | ||
507 | #else | ||
508 | playing = false; | ||
509 | #endif | ||
510 | } | ||
511 | |||
512 | static void audio_reset_buffer_noalloc(void* buf, size_t bufsize); | ||
513 | /* Buffer must not move. */ | ||
514 | static int shrink_callback(int handle, unsigned hints, void* start, size_t old_size) | ||
515 | { | ||
516 | long offset = audio_current_track()->offset; | ||
517 | int status = audio_status(); | ||
518 | /* TODO: Do it without stopping playback, if possible */ | ||
519 | /* don't call audio_hard_stop() as it frees this handle */ | ||
520 | if (thread_self() == audio_thread_id) | ||
521 | { /* inline case MPEG_STOP (audio_stop()) response | ||
522 | * if we're in the audio thread since audio_stop() otherwise deadlocks */ | ||
523 | do_stop(); | ||
524 | } | ||
525 | else | ||
526 | audio_stop(); | ||
527 | talk_buffer_steal(); /* we obtain control over the buffer */ | ||
528 | |||
529 | /* we should be free to change the buffer now */ | ||
530 | size_t wanted_size = (hints & BUFLIB_SHRINK_SIZE_MASK); | ||
531 | ssize_t size = (ssize_t)old_size - wanted_size; | ||
532 | switch (hints & BUFLIB_SHRINK_POS_MASK) | ||
533 | { | ||
534 | case BUFLIB_SHRINK_POS_BACK: | ||
535 | core_shrink(handle, start, size); | ||
536 | audio_reset_buffer_noalloc(start, size); | ||
537 | break; | ||
538 | case BUFLIB_SHRINK_POS_FRONT: | ||
539 | core_shrink(handle, start + wanted_size, size); | ||
540 | audio_reset_buffer_noalloc(start + wanted_size, size); | ||
541 | break; | ||
542 | } | ||
543 | if (!(status & AUDIO_STATUS_PAUSE)) | ||
544 | { /* safe to call even from the audio thread (due to queue_post()) */ | ||
545 | audio_play(offset); | ||
546 | } | ||
547 | |||
548 | return BUFLIB_CB_OK; | ||
549 | } | ||
550 | |||
551 | static struct buflib_callbacks ops = { | ||
552 | .move_callback = NULL, | ||
553 | .shrink_callback = shrink_callback, | ||
554 | }; | ||
555 | |||
497 | unsigned char * audio_get_buffer(bool talk_buf, size_t *buffer_size) | 556 | unsigned char * audio_get_buffer(bool talk_buf, size_t *buffer_size) |
498 | { | 557 | { |
499 | (void)talk_buf; /* always grab the voice buffer for now */ | 558 | (void)talk_buf; /* always grab the voice buffer for now */ |
500 | 559 | ||
501 | if (buffer_size) /* special case for talk_init() */ | 560 | if (buffer_size) /* special case for talk_init() */ |
561 | audio_hard_stop(); | ||
562 | |||
563 | if (!audiobuf_handle) | ||
502 | { | 564 | { |
503 | size_t bufsize; | 565 | size_t bufsize; |
504 | audio_hard_stop(); | ||
505 | /* audio_hard_stop() frees audiobuf, so re-aquire */ | 566 | /* audio_hard_stop() frees audiobuf, so re-aquire */ |
506 | audiobuf_handle = core_alloc_maximum("audiobuf", &bufsize, &ops); | 567 | audiobuf_handle = core_alloc_maximum("audiobuf", &bufsize, &ops); |
507 | audiobuflen = bufsize; | 568 | audiobuflen = bufsize; |
508 | *buffer_size = audiobuflen; | 569 | if (buffer_size) |
570 | *buffer_size = audiobuflen; | ||
509 | } | 571 | } |
510 | mpeg_audiobuf = core_get_data(audiobuf_handle); | 572 | mpeg_audiobuf = core_get_data(audiobuf_handle); |
511 | 573 | ||
@@ -1314,15 +1376,7 @@ static void mpeg_thread(void) | |||
1314 | break; | 1376 | break; |
1315 | 1377 | ||
1316 | case MPEG_STOP: | 1378 | case MPEG_STOP: |
1317 | DEBUGF("MPEG_STOP\n"); | 1379 | do_stop(); |
1318 | is_playing = false; | ||
1319 | paused = false; | ||
1320 | |||
1321 | if (playing) | ||
1322 | playlist_update_resume_info(audio_current_track()); | ||
1323 | |||
1324 | stop_playing(); | ||
1325 | mpeg_stop_done = true; | ||
1326 | break; | 1380 | break; |
1327 | 1381 | ||
1328 | case MPEG_PAUSE: | 1382 | case MPEG_PAUSE: |
@@ -2679,19 +2733,29 @@ size_t audio_buffer_available(void) | |||
2679 | return core_available(); | 2733 | return core_available(); |
2680 | } | 2734 | } |
2681 | 2735 | ||
2682 | static void audio_reset_buffer(void) | 2736 | static void audio_reset_buffer_noalloc(void* buf, size_t bufsize) |
2683 | { | 2737 | { |
2684 | talk_buffer_steal(); /* will use the mp3 buffer */ | 2738 | talk_buffer_steal(); /* will use the mp3 buffer */ |
2739 | mpeg_audiobuf = buf; | ||
2740 | audiobuflen = bufsize; | ||
2741 | if (global_settings.cuesheet) | ||
2742 | { /* enable cuesheet support */ | ||
2743 | curr_cuesheet = (struct cuesheet*)mpeg_audiobuf; | ||
2744 | mpeg_audiobuf = SKIPBYTES(mpeg_audiobuf, sizeof(struct cuesheet)); | ||
2745 | audiobuflen -= sizeof(struct cuesheet); | ||
2746 | } | ||
2747 | talkbuf_init(mpeg_audiobuf); | ||
2748 | } | ||
2749 | |||
2750 | static void audio_reset_buffer(void) | ||
2751 | { | ||
2752 | size_t bufsize = audiobuflen; | ||
2685 | 2753 | ||
2686 | /* alloc buffer if it's was never allocated or freed by audio_hard_stop() */ | 2754 | /* alloc buffer if it's was never allocated or freed by audio_hard_stop() */ |
2687 | if (!audiobuf_handle) | 2755 | if (!audiobuf_handle) |
2688 | { | ||
2689 | size_t bufsize; /* dont break strict-aliasing */ | ||
2690 | audiobuf_handle = core_alloc_maximum("audiobuf", &bufsize, &ops); | 2756 | audiobuf_handle = core_alloc_maximum("audiobuf", &bufsize, &ops); |
2691 | mpeg_audiobuf = core_get_data(audiobuf_handle); | 2757 | |
2692 | audiobuflen = bufsize; | 2758 | audio_reset_buffer_noalloc(core_get_data(audiobuf_handle), bufsize); |
2693 | } | ||
2694 | talkbuf_init(mpeg_audiobuf); | ||
2695 | } | 2759 | } |
2696 | 2760 | ||
2697 | void audio_play(long offset) | 2761 | void audio_play(long offset) |
@@ -2923,13 +2987,6 @@ static void mpeg_thread(void) | |||
2923 | void audio_init(void) | 2987 | void audio_init(void) |
2924 | { | 2988 | { |
2925 | mpeg_errno = 0; | 2989 | mpeg_errno = 0; |
2926 | /* cuesheet support */ | ||
2927 | if (global_settings.cuesheet) | ||
2928 | { | ||
2929 | int handle = core_alloc("cuesheet", sizeof(struct cuesheet)); | ||
2930 | if (handle > 0) | ||
2931 | curr_cuesheet = core_get_data(handle); | ||
2932 | } | ||
2933 | 2990 | ||
2934 | talk_init(); | 2991 | talk_init(); |
2935 | audio_reset_buffer(); | 2992 | audio_reset_buffer(); |
@@ -2937,10 +2994,10 @@ void audio_init(void) | |||
2937 | #ifndef SIMULATOR | 2994 | #ifndef SIMULATOR |
2938 | queue_init(&mpeg_queue, true); | 2995 | queue_init(&mpeg_queue, true); |
2939 | #endif /* !SIMULATOR */ | 2996 | #endif /* !SIMULATOR */ |
2940 | create_thread(mpeg_thread, mpeg_stack, | 2997 | audio_thread_id = create_thread(mpeg_thread, mpeg_stack, |
2941 | sizeof(mpeg_stack), 0, mpeg_thread_name | 2998 | sizeof(mpeg_stack), 0, mpeg_thread_name |
2942 | IF_PRIO(, PRIORITY_SYSTEM) | 2999 | IF_PRIO(, PRIORITY_SYSTEM) |
2943 | IF_COP(, CPU)); | 3000 | IF_COP(, CPU)); |
2944 | 3001 | ||
2945 | memset(trackdata, 0, sizeof(trackdata)); | 3002 | memset(trackdata, 0, sizeof(trackdata)); |
2946 | 3003 | ||
diff --git a/apps/playback.c b/apps/playback.c index e35d652ffb..af077e639a 100644 --- a/apps/playback.c +++ b/apps/playback.c | |||
@@ -732,8 +732,6 @@ static void scratch_mem_init(void *mem) | |||
732 | } | 732 | } |
733 | } | 733 | } |
734 | 734 | ||
735 | /* Buffer must not move. And not shrink for now */ | ||
736 | static struct buflib_callbacks ops = { NULL, NULL }; | ||
737 | static int audiobuf_handle; | 735 | static int audiobuf_handle; |
738 | static size_t filebuflen; | 736 | static size_t filebuflen; |
739 | 737 | ||
@@ -744,8 +742,9 @@ size_t audio_buffer_available(void) | |||
744 | return core_available(); | 742 | return core_available(); |
745 | } | 743 | } |
746 | 744 | ||
747 | /* Set up the audio buffer for playback */ | 745 | /* Set up the audio buffer for playback |
748 | static void audio_reset_buffer(void) | 746 | * filebuflen must be pre-initialized with the maximum size */ |
747 | static void audio_reset_buffer_noalloc(void* filebuf) | ||
749 | { | 748 | { |
750 | /* | 749 | /* |
751 | * Layout audio buffer as follows: | 750 | * Layout audio buffer as follows: |
@@ -761,11 +760,6 @@ static void audio_reset_buffer(void) | |||
761 | 760 | ||
762 | /* Initially set up file buffer as all space available */ | 761 | /* Initially set up file buffer as all space available */ |
763 | size_t allocsize; | 762 | size_t allocsize; |
764 | if (audiobuf_handle > 0) | ||
765 | audiobuf_handle = core_free(audiobuf_handle); | ||
766 | |||
767 | audiobuf_handle = core_alloc_maximum("audiobuf", &filebuflen, &ops); | ||
768 | unsigned char *filebuf = core_get_data(audiobuf_handle); | ||
769 | 763 | ||
770 | /* Subtract whatever voice needs */ | 764 | /* Subtract whatever voice needs */ |
771 | allocsize = talkbuf_init(filebuf); | 765 | allocsize = talkbuf_init(filebuf); |
@@ -830,6 +824,70 @@ bufpanic: | |||
830 | panicf("%s(): EOM (%zu > %zu)", __func__, allocsize, filebuflen); | 824 | panicf("%s(): EOM (%zu > %zu)", __func__, allocsize, filebuflen); |
831 | } | 825 | } |
832 | 826 | ||
827 | |||
828 | /* Buffer must not move. */ | ||
829 | static int shrink_callback(int handle, unsigned hints, void* start, size_t old_size) | ||
830 | { | ||
831 | long offset = audio_current_track()->offset; | ||
832 | int status = audio_status(); | ||
833 | /* TODO: Do it without stopping playback, if possible */ | ||
834 | /* don't call audio_hard_stop() as it frees this handle */ | ||
835 | if (thread_self() == audio_thread_id) | ||
836 | { /* inline case Q_AUDIO_STOP (audio_hard_stop() response | ||
837 | * if we're in the audio thread */ | ||
838 | audio_stop_playback(); | ||
839 | queue_clear(&audio_queue); | ||
840 | } | ||
841 | else | ||
842 | audio_queue_send(Q_AUDIO_STOP, 1); | ||
843 | #ifdef PLAYBACK_VOICE | ||
844 | voice_stop(); | ||
845 | #endif | ||
846 | /* we should be free to change the buffer now */ | ||
847 | size_t wanted_size = (hints & BUFLIB_SHRINK_SIZE_MASK); | ||
848 | ssize_t size = (ssize_t)old_size - wanted_size; | ||
849 | /* set final buffer size before calling audio_reset_buffer_noalloc() */ | ||
850 | filebuflen = size; | ||
851 | switch (hints & BUFLIB_SHRINK_POS_MASK) | ||
852 | { | ||
853 | case BUFLIB_SHRINK_POS_BACK: | ||
854 | core_shrink(handle, start, size); | ||
855 | audio_reset_buffer_noalloc(start); | ||
856 | break; | ||
857 | case BUFLIB_SHRINK_POS_FRONT: | ||
858 | core_shrink(handle, start + wanted_size, size); | ||
859 | audio_reset_buffer_noalloc(start + wanted_size); | ||
860 | break; | ||
861 | } | ||
862 | if ((status & AUDIO_STATUS_PLAY) == AUDIO_STATUS_PLAY) | ||
863 | { | ||
864 | if (thread_self() == audio_thread_id) | ||
865 | audio_start_playback(offset, 0); /* inline Q_AUDIO_PLAY */ | ||
866 | else | ||
867 | audio_play(offset); | ||
868 | } | ||
869 | |||
870 | return BUFLIB_CB_OK; | ||
871 | } | ||
872 | |||
873 | static struct buflib_callbacks ops = { | ||
874 | .move_callback = NULL, | ||
875 | .shrink_callback = shrink_callback, | ||
876 | }; | ||
877 | |||
878 | static void audio_reset_buffer(void) | ||
879 | { | ||
880 | if (audiobuf_handle > 0) | ||
881 | { | ||
882 | core_free(audiobuf_handle); | ||
883 | audiobuf_handle = 0; | ||
884 | } | ||
885 | audiobuf_handle = core_alloc_maximum("audiobuf", &filebuflen, &ops); | ||
886 | unsigned char *filebuf = core_get_data(audiobuf_handle); | ||
887 | |||
888 | audio_reset_buffer_noalloc(filebuf); | ||
889 | } | ||
890 | |||
833 | /* Set the buffer margin to begin rebuffering when 'seconds' from empty */ | 891 | /* Set the buffer margin to begin rebuffering when 'seconds' from empty */ |
834 | static void audio_update_filebuf_watermark(int seconds) | 892 | static void audio_update_filebuf_watermark(int seconds) |
835 | { | 893 | { |
diff --git a/apps/playlist.c b/apps/playlist.c index 77d8141af8..f6dda977f4 100644 --- a/apps/playlist.c +++ b/apps/playlist.c | |||
@@ -993,14 +993,14 @@ static int sort_playlist(struct playlist_info* playlist, bool start_current, | |||
993 | unsigned int current = playlist->indices[playlist->index]; | 993 | unsigned int current = playlist->indices[playlist->index]; |
994 | 994 | ||
995 | if (playlist->amount > 0) | 995 | if (playlist->amount > 0) |
996 | qsort(playlist->indices, playlist->amount, | 996 | qsort((void*)playlist->indices, playlist->amount, |
997 | sizeof(playlist->indices[0]), compare); | 997 | sizeof(playlist->indices[0]), compare); |
998 | 998 | ||
999 | #ifdef HAVE_DIRCACHE | 999 | #ifdef HAVE_DIRCACHE |
1000 | /** We need to re-check the song names from disk because qsort can't | 1000 | /** We need to re-check the song names from disk because qsort can't |
1001 | * sort two arrays at once :/ | 1001 | * sort two arrays at once :/ |
1002 | * FIXME: Please implement a better way to do this. */ | 1002 | * FIXME: Please implement a better way to do this. */ |
1003 | memset(playlist->filenames, 0xff, playlist->max_playlist_size * sizeof(int)); | 1003 | memset((void*)playlist->filenames, 0xff, playlist->max_playlist_size * sizeof(int)); |
1004 | queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0); | 1004 | queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0); |
1005 | #endif | 1005 | #endif |
1006 | 1006 | ||
@@ -1378,7 +1378,7 @@ static int get_filename(struct playlist_info* playlist, int index, int seek, | |||
1378 | 1378 | ||
1379 | if (playlist->in_ram && !control_file && max < 0) | 1379 | if (playlist->in_ram && !control_file && max < 0) |
1380 | { | 1380 | { |
1381 | max = strlcpy(tmp_buf, &playlist->buffer[seek], sizeof(tmp_buf)); | 1381 | max = strlcpy(tmp_buf, (char*)&playlist->buffer[seek], sizeof(tmp_buf)); |
1382 | } | 1382 | } |
1383 | else if (max < 0) | 1383 | else if (max < 0) |
1384 | { | 1384 | { |
@@ -1534,9 +1534,10 @@ static int get_next_dir(char *dir, bool is_forward, bool recursion) | |||
1534 | break; | 1534 | break; |
1535 | } | 1535 | } |
1536 | 1536 | ||
1537 | files = tc->cache.entries; | 1537 | files = tree_get_entries(tc); |
1538 | num_files = tc->filesindir; | 1538 | num_files = tc->filesindir; |
1539 | 1539 | ||
1540 | tree_lock_cache(tc); | ||
1540 | for (i=0; i<num_files; i++) | 1541 | for (i=0; i<num_files; i++) |
1541 | { | 1542 | { |
1542 | /* user abort */ | 1543 | /* user abort */ |
@@ -1562,6 +1563,7 @@ static int get_next_dir(char *dir, bool is_forward, bool recursion) | |||
1562 | start_dir = NULL; | 1563 | start_dir = NULL; |
1563 | } | 1564 | } |
1564 | } | 1565 | } |
1566 | tree_unlock_cache(tc); | ||
1565 | 1567 | ||
1566 | if (!exit) | 1568 | if (!exit) |
1567 | { | 1569 | { |
@@ -1612,7 +1614,7 @@ static int check_subdir_for_music(char *dir, const char *subdir, bool recurse) | |||
1612 | return -2; | 1614 | return -2; |
1613 | } | 1615 | } |
1614 | 1616 | ||
1615 | files = tc->cache.entries; | 1617 | files = tree_get_entries(tc); |
1616 | num_files = tc->filesindir; | 1618 | num_files = tc->filesindir; |
1617 | 1619 | ||
1618 | for (i=0; i<num_files; i++) | 1620 | for (i=0; i<num_files; i++) |
@@ -1629,6 +1631,7 @@ static int check_subdir_for_music(char *dir, const char *subdir, bool recurse) | |||
1629 | if (has_music) | 1631 | if (has_music) |
1630 | return 0; | 1632 | return 0; |
1631 | 1633 | ||
1634 | tree_lock_cache(tc); | ||
1632 | if (has_subdir && recurse) | 1635 | if (has_subdir && recurse) |
1633 | { | 1636 | { |
1634 | for (i=0; i<num_files; i++) | 1637 | for (i=0; i<num_files; i++) |
@@ -1647,6 +1650,7 @@ static int check_subdir_for_music(char *dir, const char *subdir, bool recurse) | |||
1647 | } | 1650 | } |
1648 | } | 1651 | } |
1649 | } | 1652 | } |
1653 | tree_unlock_cache(tc); | ||
1650 | 1654 | ||
1651 | if (result < 0) | 1655 | if (result < 0) |
1652 | { | 1656 | { |
@@ -1925,6 +1929,31 @@ static int rotate_index(const struct playlist_info* playlist, int index) | |||
1925 | } | 1929 | } |
1926 | 1930 | ||
1927 | /* | 1931 | /* |
1932 | * Need no movement protection since all 3 allocations are not passed to | ||
1933 | * other functions which can yield(). | ||
1934 | */ | ||
1935 | static int move_callback(int handle, void* current, void* new) | ||
1936 | { | ||
1937 | (void)handle; | ||
1938 | struct playlist_info* playlist = ¤t_playlist; | ||
1939 | if (current == playlist->indices) | ||
1940 | playlist->indices = new; | ||
1941 | else if (current == playlist->filenames) | ||
1942 | playlist->filenames = new; | ||
1943 | /* buffer can possibly point to a new buffer temporarily (playlist_save()). | ||
1944 | * just don't overwrite the pointer to that temp buffer */ | ||
1945 | else if (current == playlist->buffer) | ||
1946 | playlist->buffer = new; | ||
1947 | |||
1948 | return BUFLIB_CB_OK; | ||
1949 | } | ||
1950 | |||
1951 | |||
1952 | static struct buflib_callbacks ops = { | ||
1953 | .move_callback = move_callback, | ||
1954 | .shrink_callback = NULL, | ||
1955 | }; | ||
1956 | /* | ||
1928 | * Initialize playlist entries at startup | 1957 | * Initialize playlist entries at startup |
1929 | */ | 1958 | */ |
1930 | void playlist_init(void) | 1959 | void playlist_init(void) |
@@ -1941,20 +1970,23 @@ void playlist_init(void) | |||
1941 | playlist->fd = -1; | 1970 | playlist->fd = -1; |
1942 | playlist->control_fd = -1; | 1971 | playlist->control_fd = -1; |
1943 | playlist->max_playlist_size = global_settings.max_files_in_playlist; | 1972 | playlist->max_playlist_size = global_settings.max_files_in_playlist; |
1944 | handle = core_alloc("playlist idx", playlist->max_playlist_size * sizeof(int)); | 1973 | handle = core_alloc_ex("playlist idx", |
1974 | playlist->max_playlist_size * sizeof(int), &ops); | ||
1945 | playlist->indices = core_get_data(handle); | 1975 | playlist->indices = core_get_data(handle); |
1946 | playlist->buffer_size = | 1976 | playlist->buffer_size = |
1947 | AVERAGE_FILENAME_LENGTH * global_settings.max_files_in_dir; | 1977 | AVERAGE_FILENAME_LENGTH * global_settings.max_files_in_dir; |
1948 | handle = core_alloc("playlist buf", playlist->buffer_size); | 1978 | handle = core_alloc_ex("playlist buf", |
1979 | playlist->buffer_size, &ops); | ||
1949 | playlist->buffer = core_get_data(handle); | 1980 | playlist->buffer = core_get_data(handle); |
1950 | playlist->control_mutex = ¤t_playlist_mutex; | 1981 | playlist->control_mutex = ¤t_playlist_mutex; |
1951 | 1982 | ||
1952 | empty_playlist(playlist, true); | 1983 | empty_playlist(playlist, true); |
1953 | 1984 | ||
1954 | #ifdef HAVE_DIRCACHE | 1985 | #ifdef HAVE_DIRCACHE |
1955 | handle = core_alloc("playlist dc", playlist->max_playlist_size * sizeof(int)); | 1986 | handle = core_alloc_ex("playlist dc", |
1987 | playlist->max_playlist_size * sizeof(int), &ops); | ||
1956 | playlist->filenames = core_get_data(handle); | 1988 | playlist->filenames = core_get_data(handle); |
1957 | memset(playlist->filenames, 0xff, | 1989 | memset((void*)playlist->filenames, 0xff, |
1958 | playlist->max_playlist_size * sizeof(int)); | 1990 | playlist->max_playlist_size * sizeof(int)); |
1959 | create_thread(playlist_thread, playlist_stack, sizeof(playlist_stack), | 1991 | create_thread(playlist_thread, playlist_stack, sizeof(playlist_stack), |
1960 | 0, playlist_thread_name IF_PRIO(, PRIORITY_BACKGROUND) | 1992 | 0, playlist_thread_name IF_PRIO(, PRIORITY_BACKGROUND) |
@@ -2404,7 +2436,7 @@ int playlist_add(const char *filename) | |||
2404 | #endif | 2436 | #endif |
2405 | playlist->amount++; | 2437 | playlist->amount++; |
2406 | 2438 | ||
2407 | strcpy(&playlist->buffer[playlist->buffer_end_pos], filename); | 2439 | strcpy((char*)&playlist->buffer[playlist->buffer_end_pos], filename); |
2408 | playlist->buffer_end_pos += len; | 2440 | playlist->buffer_end_pos += len; |
2409 | playlist->buffer[playlist->buffer_end_pos++] = '\0'; | 2441 | playlist->buffer[playlist->buffer_end_pos++] = '\0'; |
2410 | 2442 | ||
@@ -2731,6 +2763,7 @@ int playlist_create_ex(struct playlist_info* playlist, | |||
2731 | } | 2763 | } |
2732 | 2764 | ||
2733 | playlist->buffer_size = 0; | 2765 | playlist->buffer_size = 0; |
2766 | playlist->buffer_handle = -1; | ||
2734 | playlist->buffer = NULL; | 2767 | playlist->buffer = NULL; |
2735 | playlist->control_mutex = &created_playlist_mutex; | 2768 | playlist->control_mutex = &created_playlist_mutex; |
2736 | } | 2769 | } |
@@ -2779,10 +2812,10 @@ int playlist_set_current(struct playlist_info* playlist) | |||
2779 | 2812 | ||
2780 | if (playlist->indices && playlist->indices != current_playlist.indices) | 2813 | if (playlist->indices && playlist->indices != current_playlist.indices) |
2781 | { | 2814 | { |
2782 | memcpy(current_playlist.indices, playlist->indices, | 2815 | memcpy((void*)current_playlist.indices, (void*)playlist->indices, |
2783 | playlist->max_playlist_size*sizeof(int)); | 2816 | playlist->max_playlist_size*sizeof(int)); |
2784 | #ifdef HAVE_DIRCACHE | 2817 | #ifdef HAVE_DIRCACHE |
2785 | memcpy(current_playlist.filenames, playlist->filenames, | 2818 | memcpy((void*)current_playlist.filenames, (void*)playlist->filenames, |
2786 | playlist->max_playlist_size*sizeof(int)); | 2819 | playlist->max_playlist_size*sizeof(int)); |
2787 | #endif | 2820 | #endif |
2788 | } | 2821 | } |
@@ -3358,6 +3391,7 @@ int playlist_save(struct playlist_info* playlist, char *filename) | |||
3358 | char tmp_buf[MAX_PATH+1]; | 3391 | char tmp_buf[MAX_PATH+1]; |
3359 | int result = 0; | 3392 | int result = 0; |
3360 | bool overwrite_current = false; | 3393 | bool overwrite_current = false; |
3394 | int old_handle = -1; | ||
3361 | char* old_buffer = NULL; | 3395 | char* old_buffer = NULL; |
3362 | size_t old_buffer_size = 0; | 3396 | size_t old_buffer_size = 0; |
3363 | 3397 | ||
@@ -3380,15 +3414,16 @@ int playlist_save(struct playlist_info* playlist, char *filename) | |||
3380 | { | 3414 | { |
3381 | /* not enough buffer space to store updated indices */ | 3415 | /* not enough buffer space to store updated indices */ |
3382 | /* Try to get a buffer */ | 3416 | /* Try to get a buffer */ |
3383 | old_buffer = playlist->buffer; | 3417 | old_handle = playlist->buffer_handle; |
3418 | /* can ignore volatile here, because core_get_data() is called later */ | ||
3419 | old_buffer = (char*)playlist->buffer; | ||
3384 | old_buffer_size = playlist->buffer_size; | 3420 | old_buffer_size = playlist->buffer_size; |
3385 | playlist->buffer = plugin_get_buffer((size_t*)&playlist->buffer_size); | 3421 | playlist->buffer = plugin_get_buffer((size_t*)&playlist->buffer_size); |
3386 | if (playlist->buffer_size < (int)(playlist->amount * sizeof(int))) | 3422 | if (playlist->buffer_size < (int)(playlist->amount * sizeof(int))) |
3387 | { | 3423 | { |
3388 | playlist->buffer = old_buffer; | ||
3389 | playlist->buffer_size = old_buffer_size; | ||
3390 | splash(HZ*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR)); | 3424 | splash(HZ*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR)); |
3391 | return -1; | 3425 | result = -1; |
3426 | goto reset_old_buffer; | ||
3392 | } | 3427 | } |
3393 | } | 3428 | } |
3394 | 3429 | ||
@@ -3409,12 +3444,8 @@ int playlist_save(struct playlist_info* playlist, char *filename) | |||
3409 | if (fd < 0) | 3444 | if (fd < 0) |
3410 | { | 3445 | { |
3411 | splash(HZ*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR)); | 3446 | splash(HZ*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR)); |
3412 | if (old_buffer != NULL) | 3447 | result = -1; |
3413 | { | 3448 | goto reset_old_buffer; |
3414 | playlist->buffer = old_buffer; | ||
3415 | playlist->buffer_size = old_buffer_size; | ||
3416 | } | ||
3417 | return -1; | ||
3418 | } | 3449 | } |
3419 | 3450 | ||
3420 | display_playlist_count(count, ID2P(LANG_PLAYLIST_SAVE_COUNT), false); | 3451 | display_playlist_count(count, ID2P(LANG_PLAYLIST_SAVE_COUNT), false); |
@@ -3514,11 +3545,12 @@ int playlist_save(struct playlist_info* playlist, char *filename) | |||
3514 | } | 3545 | } |
3515 | 3546 | ||
3516 | cpu_boost(false); | 3547 | cpu_boost(false); |
3517 | if (old_buffer != NULL) | 3548 | |
3518 | { | 3549 | reset_old_buffer: |
3519 | playlist->buffer = old_buffer; | 3550 | if (old_handle > 0) |
3520 | playlist->buffer_size = old_buffer_size; | 3551 | old_buffer = core_get_data(old_handle); |
3521 | } | 3552 | playlist->buffer = old_buffer; |
3553 | playlist->buffer_size = old_buffer_size; | ||
3522 | 3554 | ||
3523 | return result; | 3555 | return result; |
3524 | } | 3556 | } |
@@ -3534,9 +3566,9 @@ int playlist_directory_tracksearch(const char* dirname, bool recurse, | |||
3534 | char buf[MAX_PATH+1]; | 3566 | char buf[MAX_PATH+1]; |
3535 | int result = 0; | 3567 | int result = 0; |
3536 | int num_files = 0; | 3568 | int num_files = 0; |
3537 | int i; | 3569 | int i;; |
3538 | struct entry *files; | ||
3539 | struct tree_context* tc = tree_get_context(); | 3570 | struct tree_context* tc = tree_get_context(); |
3571 | struct tree_cache* cache = &tc->cache; | ||
3540 | int old_dirfilter = *(tc->dirfilter); | 3572 | int old_dirfilter = *(tc->dirfilter); |
3541 | 3573 | ||
3542 | if (!callback) | 3574 | if (!callback) |
@@ -3552,7 +3584,6 @@ int playlist_directory_tracksearch(const char* dirname, bool recurse, | |||
3552 | return -1; | 3584 | return -1; |
3553 | } | 3585 | } |
3554 | 3586 | ||
3555 | files = tc->cache.entries; | ||
3556 | num_files = tc->filesindir; | 3587 | num_files = tc->filesindir; |
3557 | 3588 | ||
3558 | /* we've overwritten the dircache so tree browser will need to be | 3589 | /* we've overwritten the dircache so tree browser will need to be |
@@ -3568,6 +3599,7 @@ int playlist_directory_tracksearch(const char* dirname, bool recurse, | |||
3568 | break; | 3599 | break; |
3569 | } | 3600 | } |
3570 | 3601 | ||
3602 | struct entry *files = core_get_data(cache->entries_handle); | ||
3571 | if (files[i].attr & ATTR_DIRECTORY) | 3603 | if (files[i].attr & ATTR_DIRECTORY) |
3572 | { | 3604 | { |
3573 | if (recurse) | 3605 | if (recurse) |
@@ -3586,8 +3618,7 @@ int playlist_directory_tracksearch(const char* dirname, bool recurse, | |||
3586 | result = -1; | 3618 | result = -1; |
3587 | break; | 3619 | break; |
3588 | } | 3620 | } |
3589 | 3621 | ||
3590 | files = tc->cache.entries; | ||
3591 | num_files = tc->filesindir; | 3622 | num_files = tc->filesindir; |
3592 | if (!num_files) | 3623 | if (!num_files) |
3593 | { | 3624 | { |
diff --git a/apps/playlist.h b/apps/playlist.h index f14b5c6460..6dd5535df1 100644 --- a/apps/playlist.h +++ b/apps/playlist.h | |||
@@ -80,15 +80,16 @@ struct playlist_info | |||
80 | int control_fd; /* descriptor of the open control file */ | 80 | int control_fd; /* descriptor of the open control file */ |
81 | bool control_created; /* has control file been created? */ | 81 | bool control_created; /* has control file been created? */ |
82 | int dirlen; /* Length of the path to the playlist file */ | 82 | int dirlen; /* Length of the path to the playlist file */ |
83 | unsigned long *indices; /* array of indices */ | 83 | volatile unsigned long *indices; /* array of indices */ |
84 | int *filenames; /* Array of dircache indices */ | 84 | volatile int *filenames; /* Array of dircache indices */ |
85 | int max_playlist_size; /* Max number of files in playlist. Mirror of | 85 | int max_playlist_size; /* Max number of files in playlist. Mirror of |
86 | global_settings.max_files_in_playlist */ | 86 | global_settings.max_files_in_playlist */ |
87 | bool in_ram; /* playlist stored in ram (dirplay) */ | 87 | bool in_ram; /* playlist stored in ram (dirplay) */ |
88 | int buffer_handle; /* handle to the below buffer (-1 if non-buflib) */ | ||
88 | 89 | ||
89 | union { | 90 | union { |
90 | char *buffer; /* buffer for in-ram playlists */ | 91 | volatile char *buffer;/* buffer for in-ram playlists */ |
91 | int *seek_buf; /* buffer for seeks in real playlists */ | 92 | int *seek_buf; /* buffer for seeks in real playlists */ |
92 | }; | 93 | }; |
93 | int buffer_size; /* size of buffer */ | 94 | int buffer_size; /* size of buffer */ |
94 | int buffer_end_pos; /* last position where buffer was written */ | 95 | int buffer_end_pos; /* last position where buffer was written */ |
diff --git a/apps/plugin.c b/apps/plugin.c index 32b77ad287..43d9e03acf 100644 --- a/apps/plugin.c +++ b/apps/plugin.c | |||
@@ -789,6 +789,8 @@ static const struct plugin_api rockbox_api = { | |||
789 | 789 | ||
790 | /* new stuff at the end, sort into place next time | 790 | /* new stuff at the end, sort into place next time |
791 | the API gets incompatible */ | 791 | the API gets incompatible */ |
792 | tree_get_entries, | ||
793 | tree_get_entry_at, | ||
792 | }; | 794 | }; |
793 | 795 | ||
794 | int plugin_load(const char* plugin, const void* parameter) | 796 | int plugin_load(const char* plugin, const void* parameter) |
@@ -865,6 +867,9 @@ int plugin_load(const char* plugin, const void* parameter) | |||
865 | lcd_remote_update(); | 867 | lcd_remote_update(); |
866 | #endif | 868 | #endif |
867 | push_current_activity(ACTIVITY_PLUGIN); | 869 | push_current_activity(ACTIVITY_PLUGIN); |
870 | /* some plugins assume the entry cache doesn't move and save pointers to it | ||
871 | * they should be fixed properly instead of this lock */ | ||
872 | tree_lock_cache(tree_get_context()); | ||
868 | 873 | ||
869 | FOR_NB_SCREENS(i) | 874 | FOR_NB_SCREENS(i) |
870 | viewportmanager_theme_enable(i, false, NULL); | 875 | viewportmanager_theme_enable(i, false, NULL); |
@@ -879,6 +884,7 @@ int plugin_load(const char* plugin, const void* parameter) | |||
879 | 884 | ||
880 | rc = p_hdr->entry_point(parameter); | 885 | rc = p_hdr->entry_point(parameter); |
881 | 886 | ||
887 | tree_unlock_cache(tree_get_context()); | ||
882 | pop_current_activity(); | 888 | pop_current_activity(); |
883 | 889 | ||
884 | if (!pfn_tsr_exit) | 890 | if (!pfn_tsr_exit) |
diff --git a/apps/plugin.h b/apps/plugin.h index d70e5634f9..1d8413f6df 100644 --- a/apps/plugin.h +++ b/apps/plugin.h | |||
@@ -926,6 +926,8 @@ struct plugin_api { | |||
926 | 926 | ||
927 | /* new stuff at the end, sort into place next time | 927 | /* new stuff at the end, sort into place next time |
928 | the API gets incompatible */ | 928 | the API gets incompatible */ |
929 | struct entry* (*tree_get_entries)(struct tree_context* t); | ||
930 | struct entry* (*tree_get_entry_at)(struct tree_context* t, int index); | ||
929 | }; | 931 | }; |
930 | 932 | ||
931 | /* plugin header */ | 933 | /* plugin header */ |
diff --git a/apps/plugins/imageviewer/imageviewer.c b/apps/plugins/imageviewer/imageviewer.c index 80e1ba41bf..044c835d00 100644 --- a/apps/plugins/imageviewer/imageviewer.c +++ b/apps/plugins/imageviewer/imageviewer.c | |||
@@ -136,7 +136,7 @@ static enum image_type image_type = IMAGE_UNKNOWN; | |||
136 | static void get_pic_list(void) | 136 | static void get_pic_list(void) |
137 | { | 137 | { |
138 | struct tree_context *tree = rb->tree_get_context(); | 138 | struct tree_context *tree = rb->tree_get_context(); |
139 | struct entry *dircache = tree->cache.entries; | 139 | struct entry *dircache = rb->tree_get_entries(tree); |
140 | int i; | 140 | int i; |
141 | char *pname; | 141 | char *pname; |
142 | 142 | ||
diff --git a/apps/plugins/mikmod/mikmod.c b/apps/plugins/mikmod/mikmod.c index dff0fce685..d132f80498 100644 --- a/apps/plugins/mikmod/mikmod.c +++ b/apps/plugins/mikmod/mikmod.c | |||
@@ -185,7 +185,7 @@ bool mod_ext(const char ext[]) | |||
185 | void get_mod_list(void) | 185 | void get_mod_list(void) |
186 | { | 186 | { |
187 | struct tree_context *tree = rb->tree_get_context(); | 187 | struct tree_context *tree = rb->tree_get_context(); |
188 | struct entry *dircache = tree->cache.entries; | 188 | struct entry *dircache = rb->tree_get_entries(tree); |
189 | int i; | 189 | int i; |
190 | char *pname; | 190 | char *pname; |
191 | 191 | ||
diff --git a/apps/plugins/mpegplayer/mpegplayer.c b/apps/plugins/mpegplayer/mpegplayer.c index 156ec019c1..84eae42a75 100644 --- a/apps/plugins/mpegplayer/mpegplayer.c +++ b/apps/plugins/mpegplayer/mpegplayer.c | |||
@@ -1876,7 +1876,7 @@ static bool is_videofile(const char* file) | |||
1876 | static bool get_videofile(int direction, char* videofile, size_t bufsize) | 1876 | static bool get_videofile(int direction, char* videofile, size_t bufsize) |
1877 | { | 1877 | { |
1878 | struct tree_context *tree = rb->tree_get_context(); | 1878 | struct tree_context *tree = rb->tree_get_context(); |
1879 | struct entry *dircache = tree->cache.entries; | 1879 | struct entry *dircache = rb->tree_get_entries(tree); |
1880 | int i, step, end, found = 0; | 1880 | int i, step, end, found = 0; |
1881 | char *videoname = rb->strrchr(videofile, '/') + 1; | 1881 | char *videoname = rb->strrchr(videofile, '/') + 1; |
1882 | size_t rest = bufsize - (videoname - videofile) - 1; | 1882 | size_t rest = bufsize - (videoname - videofile) - 1; |
diff --git a/apps/plugins/rockpaint.c b/apps/plugins/rockpaint.c index add09c7fef..d1cc8f272a 100644 --- a/apps/plugins/rockpaint.c +++ b/apps/plugins/rockpaint.c | |||
@@ -948,7 +948,7 @@ static bool browse_fonts( char *dst, int dst_size ) | |||
948 | 948 | ||
949 | tree = rb->tree_get_context(); | 949 | tree = rb->tree_get_context(); |
950 | backup = *tree; | 950 | backup = *tree; |
951 | dc = tree->cache.entries; | 951 | dc = rb->tree_get_entries(tree); |
952 | a = backup.currdir+rb->strlen(backup.currdir)-1; | 952 | a = backup.currdir+rb->strlen(backup.currdir)-1; |
953 | if( *a != '/' ) | 953 | if( *a != '/' ) |
954 | { | 954 | { |
diff --git a/apps/scrobbler.c b/apps/scrobbler.c index a6307d5dd7..78414f3d88 100644 --- a/apps/scrobbler.c +++ b/apps/scrobbler.c | |||
@@ -255,6 +255,11 @@ int scrobbler_init(void) | |||
255 | return -1; | 255 | return -1; |
256 | 256 | ||
257 | scrobbler_cache = core_alloc("scrobbler", SCROBBLER_MAX_CACHE*SCROBBLER_CACHE_LEN); | 257 | scrobbler_cache = core_alloc("scrobbler", SCROBBLER_MAX_CACHE*SCROBBLER_CACHE_LEN); |
258 | if (scrobbler_cache <= 0) | ||
259 | { | ||
260 | logf("SCROOBLER: OOM"); | ||
261 | return -1; | ||
262 | } | ||
258 | 263 | ||
259 | add_event(PLAYBACK_EVENT_TRACK_CHANGE, false, scrobbler_change_event); | 264 | add_event(PLAYBACK_EVENT_TRACK_CHANGE, false, scrobbler_change_event); |
260 | cache_pos = 0; | 265 | cache_pos = 0; |
@@ -288,6 +293,9 @@ void scrobbler_shutdown(void) | |||
288 | { | 293 | { |
289 | remove_event(PLAYBACK_EVENT_TRACK_CHANGE, scrobbler_change_event); | 294 | remove_event(PLAYBACK_EVENT_TRACK_CHANGE, scrobbler_change_event); |
290 | scrobbler_initialised = false; | 295 | scrobbler_initialised = false; |
296 | /* get rid of the buffer */ | ||
297 | core_free(scrobbler_cache); | ||
298 | scrobbler_cache = 0; | ||
291 | } | 299 | } |
292 | } | 300 | } |
293 | 301 | ||
diff --git a/apps/tagcache.c b/apps/tagcache.c index 753675f906..5ab77264f6 100644 --- a/apps/tagcache.c +++ b/apps/tagcache.c | |||
@@ -222,6 +222,8 @@ struct statefile_header { | |||
222 | 222 | ||
223 | /* Pointer to allocated ramcache_header */ | 223 | /* Pointer to allocated ramcache_header */ |
224 | static struct ramcache_header *ramcache_hdr; | 224 | static struct ramcache_header *ramcache_hdr; |
225 | /* lock entity to temporarily prevent ramcache_hdr from moving */ | ||
226 | static int move_lock; | ||
225 | #endif | 227 | #endif |
226 | 228 | ||
227 | /** | 229 | /** |
@@ -1035,6 +1037,8 @@ static bool check_clauses(struct tagcache_search *tcs, | |||
1035 | { | 1037 | { |
1036 | tfe = (struct tagfile_entry *) | 1038 | tfe = (struct tagfile_entry *) |
1037 | &ramcache_hdr->tags[clause->tag][seek]; | 1039 | &ramcache_hdr->tags[clause->tag][seek]; |
1040 | /* str points to movable data, but no locking required here, | ||
1041 | * as no yield() is following */ | ||
1038 | str = tfe->tag_data; | 1042 | str = tfe->tag_data; |
1039 | } | 1043 | } |
1040 | } | 1044 | } |
@@ -1149,9 +1153,11 @@ static bool build_lookup_list(struct tagcache_search *tcs) | |||
1149 | # endif | 1153 | # endif |
1150 | ) | 1154 | ) |
1151 | { | 1155 | { |
1156 | move_lock++; /* lock because below makes a pointer to movable data */ | ||
1152 | for (i = tcs->seek_pos; i < current_tcmh.tch.entry_count; i++) | 1157 | for (i = tcs->seek_pos; i < current_tcmh.tch.entry_count; i++) |
1153 | { | 1158 | { |
1154 | struct tagcache_seeklist_entry *seeklist; | 1159 | struct tagcache_seeklist_entry *seeklist; |
1160 | /* idx points to movable data, don't yield or reload */ | ||
1155 | struct index_entry *idx = &ramcache_hdr->indices[i]; | 1161 | struct index_entry *idx = &ramcache_hdr->indices[i]; |
1156 | if (tcs->seek_list_count == SEEK_LIST_SIZE) | 1162 | if (tcs->seek_list_count == SEEK_LIST_SIZE) |
1157 | break ; | 1163 | break ; |
@@ -1175,8 +1181,7 @@ static bool build_lookup_list(struct tagcache_search *tcs) | |||
1175 | /* Check for conditions. */ | 1181 | /* Check for conditions. */ |
1176 | if (!check_clauses(tcs, idx, tcs->clause, tcs->clause_count)) | 1182 | if (!check_clauses(tcs, idx, tcs->clause, tcs->clause_count)) |
1177 | continue; | 1183 | continue; |
1178 | 1184 | /* Add to the seek list if not already in uniq buffer (doesn't yield)*/ | |
1179 | /* Add to the seek list if not already in uniq buffer. */ | ||
1180 | if (!add_uniqbuf(tcs, idx->tag_seek[tcs->type])) | 1185 | if (!add_uniqbuf(tcs, idx->tag_seek[tcs->type])) |
1181 | continue; | 1186 | continue; |
1182 | 1187 | ||
@@ -1187,6 +1192,7 @@ static bool build_lookup_list(struct tagcache_search *tcs) | |||
1187 | seeklist->idx_id = i; | 1192 | seeklist->idx_id = i; |
1188 | tcs->seek_list_count++; | 1193 | tcs->seek_list_count++; |
1189 | } | 1194 | } |
1195 | move_lock--; | ||
1190 | 1196 | ||
1191 | tcs->seek_pos = i; | 1197 | tcs->seek_pos = i; |
1192 | 1198 | ||
@@ -1538,10 +1544,11 @@ static bool get_next(struct tagcache_search *tcs) | |||
1538 | struct tagfile_entry *ep; | 1544 | struct tagfile_entry *ep; |
1539 | 1545 | ||
1540 | ep = (struct tagfile_entry *)&ramcache_hdr->tags[tcs->type][tcs->position]; | 1546 | ep = (struct tagfile_entry *)&ramcache_hdr->tags[tcs->type][tcs->position]; |
1541 | tcs->result = ep->tag_data; | 1547 | /* don't return ep->tag_data directly as it may move */ |
1542 | tcs->result_len = strlen(tcs->result) + 1; | 1548 | tcs->result_len = strlcpy(buf, ep->tag_data, sizeof(buf)) + 1; |
1549 | tcs->result = buf; | ||
1543 | tcs->idx_id = ep->idx_id; | 1550 | tcs->idx_id = ep->idx_id; |
1544 | tcs->ramresult = true; | 1551 | tcs->ramresult = false; /* was true before we copied to buf too */ |
1545 | 1552 | ||
1546 | /* Increase position for the next run. This may get overwritten. */ | 1553 | /* Increase position for the next run. This may get overwritten. */ |
1547 | tcs->position += sizeof(struct tagfile_entry) + ep->tag_length; | 1554 | tcs->position += sizeof(struct tagfile_entry) + ep->tag_length; |
@@ -1703,15 +1710,34 @@ bool tagcache_fill_tags(struct mp3entry *id3, const char *filename) | |||
1703 | entry = &ramcache_hdr->indices[idx_id]; | 1710 | entry = &ramcache_hdr->indices[idx_id]; |
1704 | 1711 | ||
1705 | memset(id3, 0, sizeof(struct mp3entry)); | 1712 | memset(id3, 0, sizeof(struct mp3entry)); |
1706 | 1713 | char* buf = id3->id3v2buf; | |
1707 | id3->title = get_tag_string(entry, tag_title); | 1714 | ssize_t remaining = sizeof(id3->id3v2buf); |
1708 | id3->artist = get_tag_string(entry, tag_artist); | 1715 | |
1709 | id3->album = get_tag_string(entry, tag_album); | 1716 | /* this macro sets id3 strings by copying to the id3v2buf */ |
1710 | id3->genre_string = get_tag_string(entry, tag_genre); | 1717 | #define SET(x, y) do \ |
1711 | id3->composer = get_tag_string(entry, tag_composer); | 1718 | { \ |
1712 | id3->comment = get_tag_string(entry, tag_comment); | 1719 | if (remaining > 0) \ |
1713 | id3->albumartist = get_tag_string(entry, tag_albumartist); | 1720 | { \ |
1714 | id3->grouping = get_tag_string(entry, tag_grouping); | 1721 | x = NULL; /* initialize with null if tag doesn't exist */ \ |
1722 | char* src = get_tag_string(entry, y); \ | ||
1723 | if (src) \ | ||
1724 | { \ | ||
1725 | x = buf; \ | ||
1726 | size_t len = strlcpy(buf, src, remaining) +1; \ | ||
1727 | buf += len; remaining -= len; \ | ||
1728 | } \ | ||
1729 | } \ | ||
1730 | } while(0) | ||
1731 | |||
1732 | |||
1733 | SET(id3->title, tag_title); | ||
1734 | SET(id3->artist, tag_artist); | ||
1735 | SET(id3->album, tag_album); | ||
1736 | SET(id3->genre_string, tag_genre); | ||
1737 | SET(id3->composer, tag_composer); | ||
1738 | SET(id3->comment, tag_comment); | ||
1739 | SET(id3->albumartist, tag_albumartist); | ||
1740 | SET(id3->grouping, tag_grouping); | ||
1715 | 1741 | ||
1716 | id3->length = get_tag_numeric(entry, tag_length, idx_id); | 1742 | id3->length = get_tag_numeric(entry, tag_length, idx_id); |
1717 | id3->playcount = get_tag_numeric(entry, tag_playcount, idx_id); | 1743 | id3->playcount = get_tag_numeric(entry, tag_playcount, idx_id); |
@@ -2903,6 +2929,9 @@ static bool commit(void) | |||
2903 | #ifdef HAVE_DIRCACHE | 2929 | #ifdef HAVE_DIRCACHE |
2904 | bool dircache_buffer_stolen = false; | 2930 | bool dircache_buffer_stolen = false; |
2905 | #endif | 2931 | #endif |
2932 | #ifdef HAVE_TC_RAMCACHE | ||
2933 | bool ramcache_buffer_stolen = false; | ||
2934 | #endif | ||
2906 | bool local_allocation = false; | 2935 | bool local_allocation = false; |
2907 | 2936 | ||
2908 | logf("committing tagcache"); | 2937 | logf("committing tagcache"); |
@@ -2976,6 +3005,8 @@ static bool commit(void) | |||
2976 | tempbuf = (char *)(ramcache_hdr + 1); | 3005 | tempbuf = (char *)(ramcache_hdr + 1); |
2977 | tempbuf_size = tc_stat.ramcache_allocated - sizeof(struct ramcache_header) - 128; | 3006 | tempbuf_size = tc_stat.ramcache_allocated - sizeof(struct ramcache_header) - 128; |
2978 | tempbuf_size &= ~0x03; | 3007 | tempbuf_size &= ~0x03; |
3008 | move_lock++; | ||
3009 | ramcache_buffer_stolen = true; | ||
2979 | } | 3010 | } |
2980 | #endif | 3011 | #endif |
2981 | 3012 | ||
@@ -3072,6 +3103,8 @@ static bool commit(void) | |||
3072 | #endif | 3103 | #endif |
3073 | 3104 | ||
3074 | #ifdef HAVE_TC_RAMCACHE | 3105 | #ifdef HAVE_TC_RAMCACHE |
3106 | if (ramcache_buffer_stolen) | ||
3107 | move_lock--; | ||
3075 | /* Reload tagcache. */ | 3108 | /* Reload tagcache. */ |
3076 | if (tc_stat.ramcache_allocated > 0) | 3109 | if (tc_stat.ramcache_allocated > 0) |
3077 | tagcache_start_scan(); | 3110 | tagcache_start_scan(); |
@@ -3689,9 +3722,11 @@ static bool delete_entry(long idx_id) | |||
3689 | { | 3722 | { |
3690 | struct tagfile_entry *tfe; | 3723 | struct tagfile_entry *tfe; |
3691 | int32_t *seek = &ramcache_hdr->indices[idx_id].tag_seek[tag]; | 3724 | int32_t *seek = &ramcache_hdr->indices[idx_id].tag_seek[tag]; |
3692 | 3725 | ||
3693 | tfe = (struct tagfile_entry *)&ramcache_hdr->tags[tag][*seek]; | 3726 | tfe = (struct tagfile_entry *)&ramcache_hdr->tags[tag][*seek]; |
3727 | move_lock++; /* protect tfe and seek if crc_32() yield()s */ | ||
3694 | *seek = crc_32(tfe->tag_data, strlen(tfe->tag_data), 0xffffffff); | 3728 | *seek = crc_32(tfe->tag_data, strlen(tfe->tag_data), 0xffffffff); |
3729 | move_lock--; | ||
3695 | myidx.tag_seek[tag] = *seek; | 3730 | myidx.tag_seek[tag] = *seek; |
3696 | } | 3731 | } |
3697 | else | 3732 | else |
@@ -3813,6 +3848,30 @@ static bool check_event_queue(void) | |||
3813 | #endif | 3848 | #endif |
3814 | 3849 | ||
3815 | #ifdef HAVE_TC_RAMCACHE | 3850 | #ifdef HAVE_TC_RAMCACHE |
3851 | |||
3852 | static void fix_ramcache(void* old_addr, void* new_addr) | ||
3853 | { | ||
3854 | ptrdiff_t offpos = new_addr - old_addr; | ||
3855 | for (int i = 0; i < TAG_COUNT; i++) | ||
3856 | ramcache_hdr->tags[i] += offpos; | ||
3857 | } | ||
3858 | |||
3859 | static int move_cb(int handle, void* current, void* new) | ||
3860 | { | ||
3861 | (void)handle; | ||
3862 | if (move_lock > 0) | ||
3863 | return BUFLIB_CB_CANNOT_MOVE; | ||
3864 | |||
3865 | fix_ramcache(current, new); | ||
3866 | ramcache_hdr = new; | ||
3867 | return BUFLIB_CB_OK; | ||
3868 | } | ||
3869 | |||
3870 | static struct buflib_callbacks ops = { | ||
3871 | .move_callback = move_cb, | ||
3872 | .shrink_callback = NULL, | ||
3873 | }; | ||
3874 | |||
3816 | static bool allocate_tagcache(void) | 3875 | static bool allocate_tagcache(void) |
3817 | { | 3876 | { |
3818 | struct master_header tcmh; | 3877 | struct master_header tcmh; |
@@ -3833,7 +3892,7 @@ static bool allocate_tagcache(void) | |||
3833 | */ | 3892 | */ |
3834 | tc_stat.ramcache_allocated = tcmh.tch.datasize + 256 + TAGCACHE_RESERVE + | 3893 | tc_stat.ramcache_allocated = tcmh.tch.datasize + 256 + TAGCACHE_RESERVE + |
3835 | sizeof(struct ramcache_header) + TAG_COUNT*sizeof(void *); | 3894 | sizeof(struct ramcache_header) + TAG_COUNT*sizeof(void *); |
3836 | int handle = core_alloc("tc ramcache", tc_stat.ramcache_allocated); | 3895 | int handle = core_alloc_ex("tc ramcache", tc_stat.ramcache_allocated, &ops); |
3837 | ramcache_hdr = core_get_data(handle); | 3896 | ramcache_hdr = core_get_data(handle); |
3838 | memset(ramcache_hdr, 0, sizeof(struct ramcache_header)); | 3897 | memset(ramcache_hdr, 0, sizeof(struct ramcache_header)); |
3839 | memcpy(¤t_tcmh, &tcmh, sizeof current_tcmh); | 3898 | memcpy(¤t_tcmh, &tcmh, sizeof current_tcmh); |
@@ -3871,12 +3930,13 @@ static bool tagcache_dumpload(void) | |||
3871 | 3930 | ||
3872 | 3931 | ||
3873 | /* Lets allocate real memory and load it */ | 3932 | /* Lets allocate real memory and load it */ |
3874 | handle = core_alloc("tc ramcache", shdr.tc_stat.ramcache_allocated); | 3933 | handle = core_alloc_ex("tc ramcache", shdr.tc_stat.ramcache_allocated, &ops); |
3875 | ramcache_hdr = core_get_data(handle); | 3934 | ramcache_hdr = core_get_data(handle); |
3935 | moev_lock++; | ||
3876 | rc = read(fd, ramcache_hdr, shdr.tc_stat.ramcache_allocated); | 3936 | rc = read(fd, ramcache_hdr, shdr.tc_stat.ramcache_allocated); |
3937 | move_lock--; | ||
3877 | close(fd); | 3938 | close(fd); |
3878 | 3939 | ||
3879 | offpos = (long)ramcache_hdr - (long)shdr.hdr; | ||
3880 | if (rc != shdr.tc_stat.ramcache_allocated) | 3940 | if (rc != shdr.tc_stat.ramcache_allocated) |
3881 | { | 3941 | { |
3882 | logf("read failure!"); | 3942 | logf("read failure!"); |
@@ -3887,8 +3947,7 @@ static bool tagcache_dumpload(void) | |||
3887 | memcpy(&tc_stat, &shdr.tc_stat, sizeof(struct tagcache_stat)); | 3947 | memcpy(&tc_stat, &shdr.tc_stat, sizeof(struct tagcache_stat)); |
3888 | 3948 | ||
3889 | /* Now fix the pointers */ | 3949 | /* Now fix the pointers */ |
3890 | for (i = 0; i < TAG_COUNT; i++) | 3950 | fix_ramcache(shdr.hdr, ramcache_hdr); |
3891 | ramcache_hdr->tags[i] += offpos; | ||
3892 | 3951 | ||
3893 | /* Load the tagcache master header (should match the actual DB file header). */ | 3952 | /* Load the tagcache master header (should match the actual DB file header). */ |
3894 | memcpy(¤t_tcmh, &shdr.mh, sizeof current_tcmh); | 3953 | memcpy(¤t_tcmh, &shdr.mh, sizeof current_tcmh); |
@@ -3919,7 +3978,9 @@ static bool tagcache_dumpsave(void) | |||
3919 | write(fd, &shdr, sizeof shdr); | 3978 | write(fd, &shdr, sizeof shdr); |
3920 | 3979 | ||
3921 | /* And dump the data too */ | 3980 | /* And dump the data too */ |
3981 | move_lock++; | ||
3922 | write(fd, ramcache_hdr, tc_stat.ramcache_allocated); | 3982 | write(fd, ramcache_hdr, tc_stat.ramcache_allocated); |
3983 | move_lock--; | ||
3923 | close(fd); | 3984 | close(fd); |
3924 | 3985 | ||
3925 | return true; | 3986 | return true; |
@@ -3962,7 +4023,8 @@ static bool load_tagcache(void) | |||
3962 | 4023 | ||
3963 | /* Master header copy should already match, this can be redundant to do. */ | 4024 | /* Master header copy should already match, this can be redundant to do. */ |
3964 | memcpy(¤t_tcmh, &tcmh, sizeof current_tcmh); | 4025 | memcpy(¤t_tcmh, &tcmh, sizeof current_tcmh); |
3965 | 4026 | ||
4027 | move_lock++; /* lock for the reset of the scan, simpler to handle */ | ||
3966 | idx = ramcache_hdr->indices; | 4028 | idx = ramcache_hdr->indices; |
3967 | 4029 | ||
3968 | /* Load the master index table. */ | 4030 | /* Load the master index table. */ |
@@ -3972,8 +4034,7 @@ static bool load_tagcache(void) | |||
3972 | if (bytesleft < 0) | 4034 | if (bytesleft < 0) |
3973 | { | 4035 | { |
3974 | logf("too big tagcache."); | 4036 | logf("too big tagcache."); |
3975 | close(fd); | 4037 | goto failure; |
3976 | return false; | ||
3977 | } | 4038 | } |
3978 | 4039 | ||
3979 | /* DEBUG: After tagcache commit and dircache rebuild, hdr-sturcture | 4040 | /* DEBUG: After tagcache commit and dircache rebuild, hdr-sturcture |
@@ -3982,8 +4043,7 @@ static bool load_tagcache(void) | |||
3982 | if (rc != sizeof(struct index_entry)) | 4043 | if (rc != sizeof(struct index_entry)) |
3983 | { | 4044 | { |
3984 | logf("read error #10"); | 4045 | logf("read error #10"); |
3985 | close(fd); | 4046 | goto failure; |
3986 | return false; | ||
3987 | } | 4047 | } |
3988 | 4048 | ||
3989 | idx++; | 4049 | idx++; |
@@ -4010,7 +4070,7 @@ static bool load_tagcache(void) | |||
4010 | p += sizeof(struct tagcache_header); | 4070 | p += sizeof(struct tagcache_header); |
4011 | 4071 | ||
4012 | if ( (fd = open_tag_fd(tch, tag, false)) < 0) | 4072 | if ( (fd = open_tag_fd(tch, tag, false)) < 0) |
4013 | return false; | 4073 | goto failure_nofd; |
4014 | 4074 | ||
4015 | for (ramcache_hdr->entry_count[tag] = 0; | 4075 | for (ramcache_hdr->entry_count[tag] = 0; |
4016 | ramcache_hdr->entry_count[tag] < tch->entry_count; | 4076 | ramcache_hdr->entry_count[tag] < tch->entry_count; |
@@ -4022,7 +4082,7 @@ static bool load_tagcache(void) | |||
4022 | { | 4082 | { |
4023 | /* Abort if we got a critical event in queue */ | 4083 | /* Abort if we got a critical event in queue */ |
4024 | if (check_event_queue()) | 4084 | if (check_event_queue()) |
4025 | return false; | 4085 | goto failure; |
4026 | } | 4086 | } |
4027 | 4087 | ||
4028 | fe = (struct tagfile_entry *)p; | 4088 | fe = (struct tagfile_entry *)p; |
@@ -4032,8 +4092,7 @@ static bool load_tagcache(void) | |||
4032 | { | 4092 | { |
4033 | /* End of lookup table. */ | 4093 | /* End of lookup table. */ |
4034 | logf("read error #11"); | 4094 | logf("read error #11"); |
4035 | close(fd); | 4095 | goto failure; |
4036 | return false; | ||
4037 | } | 4096 | } |
4038 | 4097 | ||
4039 | /* We have a special handling for the filename tags. */ | 4098 | /* We have a special handling for the filename tags. */ |
@@ -4051,16 +4110,14 @@ static bool load_tagcache(void) | |||
4051 | buf[10] = '\0'; | 4110 | buf[10] = '\0'; |
4052 | logf("TAG:%s", buf); | 4111 | logf("TAG:%s", buf); |
4053 | logf("too long filename"); | 4112 | logf("too long filename"); |
4054 | close(fd); | 4113 | goto failure; |
4055 | return false; | ||
4056 | } | 4114 | } |
4057 | 4115 | ||
4058 | rc = read(fd, buf, fe->tag_length); | 4116 | rc = read(fd, buf, fe->tag_length); |
4059 | if (rc != fe->tag_length) | 4117 | if (rc != fe->tag_length) |
4060 | { | 4118 | { |
4061 | logf("read error #12"); | 4119 | logf("read error #12"); |
4062 | close(fd); | 4120 | goto failure; |
4063 | return false; | ||
4064 | } | 4121 | } |
4065 | 4122 | ||
4066 | /* Check if the entry has already been removed */ | 4123 | /* Check if the entry has already been removed */ |
@@ -4071,15 +4128,13 @@ static bool load_tagcache(void) | |||
4071 | if (idx->flag & FLAG_DIRCACHE) | 4128 | if (idx->flag & FLAG_DIRCACHE) |
4072 | { | 4129 | { |
4073 | logf("internal error!"); | 4130 | logf("internal error!"); |
4074 | close(fd); | 4131 | goto failure; |
4075 | return false; | ||
4076 | } | 4132 | } |
4077 | 4133 | ||
4078 | if (idx->tag_seek[tag] != pos) | 4134 | if (idx->tag_seek[tag] != pos) |
4079 | { | 4135 | { |
4080 | logf("corrupt data structures!"); | 4136 | logf("corrupt data structures!"); |
4081 | close(fd); | 4137 | goto failure; |
4082 | return false; | ||
4083 | } | 4138 | } |
4084 | 4139 | ||
4085 | # ifdef HAVE_DIRCACHE | 4140 | # ifdef HAVE_DIRCACHE |
@@ -4126,8 +4181,7 @@ static bool load_tagcache(void) | |||
4126 | logf("too big tagcache #2"); | 4181 | logf("too big tagcache #2"); |
4127 | logf("tl: %ld", fe->tag_length); | 4182 | logf("tl: %ld", fe->tag_length); |
4128 | logf("bl: %ld", bytesleft); | 4183 | logf("bl: %ld", bytesleft); |
4129 | close(fd); | 4184 | goto failure; |
4130 | return false; | ||
4131 | } | 4185 | } |
4132 | 4186 | ||
4133 | p = fe->tag_data; | 4187 | p = fe->tag_data; |
@@ -4141,8 +4195,7 @@ static bool load_tagcache(void) | |||
4141 | logf("len=0x%04lx", fe->tag_length); // 0x4000 | 4195 | logf("len=0x%04lx", fe->tag_length); // 0x4000 |
4142 | logf("pos=0x%04lx", lseek(fd, 0, SEEK_CUR)); // 0x433 | 4196 | logf("pos=0x%04lx", lseek(fd, 0, SEEK_CUR)); // 0x433 |
4143 | logf("tag=0x%02x", tag); // 0x00 | 4197 | logf("tag=0x%02x", tag); // 0x00 |
4144 | close(fd); | 4198 | goto failure; |
4145 | return false; | ||
4146 | } | 4199 | } |
4147 | } | 4200 | } |
4148 | close(fd); | 4201 | close(fd); |
@@ -4151,7 +4204,14 @@ static bool load_tagcache(void) | |||
4151 | tc_stat.ramcache_used = tc_stat.ramcache_allocated - bytesleft; | 4204 | tc_stat.ramcache_used = tc_stat.ramcache_allocated - bytesleft; |
4152 | logf("tagcache loaded into ram!"); | 4205 | logf("tagcache loaded into ram!"); |
4153 | 4206 | ||
4207 | move_lock--; | ||
4154 | return true; | 4208 | return true; |
4209 | |||
4210 | failure: | ||
4211 | close(fd); | ||
4212 | failure_nofd: | ||
4213 | move_lock--; | ||
4214 | return false; | ||
4155 | } | 4215 | } |
4156 | #endif /* HAVE_TC_RAMCACHE */ | 4216 | #endif /* HAVE_TC_RAMCACHE */ |
4157 | 4217 | ||
diff --git a/apps/tagcache.h b/apps/tagcache.h index 393a2905f5..6c13efdd0e 100644 --- a/apps/tagcache.h +++ b/apps/tagcache.h | |||
@@ -190,7 +190,9 @@ struct tagcache_search { | |||
190 | 190 | ||
191 | /* Exported variables. */ | 191 | /* Exported variables. */ |
192 | bool ramsearch; /* Is ram copy of the tagcache being used. */ | 192 | bool ramsearch; /* Is ram copy of the tagcache being used. */ |
193 | bool ramresult; /* False if result is not static, and must be copied. */ | 193 | bool ramresult; /* False if result is not static, and must be copied. |
194 | Currently always false since ramresult buffer is | ||
195 | movable */ | ||
194 | int type; /* The tag type to be searched. Only nonvirtual tags */ | 196 | int type; /* The tag type to be searched. Only nonvirtual tags */ |
195 | char *result; /* The result data for all tags. */ | 197 | char *result; /* The result data for all tags. */ |
196 | int result_len; /* Length of the result including \0 */ | 198 | int result_len; /* Length of the result including \0 */ |
diff --git a/apps/tagtree.c b/apps/tagtree.c index 0d4330bac8..5766d2892e 100644 --- a/apps/tagtree.c +++ b/apps/tagtree.c | |||
@@ -53,6 +53,7 @@ | |||
53 | #include "storage.h" | 53 | #include "storage.h" |
54 | #include "dir.h" | 54 | #include "dir.h" |
55 | #include "playback.h" | 55 | #include "playback.h" |
56 | #include "panic.h" | ||
56 | 57 | ||
57 | #define str_or_empty(x) (x ? x : "(NULL)") | 58 | #define str_or_empty(x) (x ? x : "(NULL)") |
58 | 59 | ||
@@ -60,6 +61,17 @@ | |||
60 | 61 | ||
61 | static int tagtree_play_folder(struct tree_context* c); | 62 | static int tagtree_play_folder(struct tree_context* c); |
62 | 63 | ||
64 | /* this needs to be same size as struct entry (tree.h) and name needs to be | ||
65 | * the first; so that they're compatible enough to walk arrays of both | ||
66 | * derefencing the name member*/ | ||
67 | struct tagentry { | ||
68 | char* name; | ||
69 | int newtable; | ||
70 | int extraseek; | ||
71 | }; | ||
72 | |||
73 | static struct tagentry* tagtree_get_entry(struct tree_context *c, int id); | ||
74 | |||
63 | #define SEARCHSTR_SIZE 256 | 75 | #define SEARCHSTR_SIZE 256 |
64 | 76 | ||
65 | enum table { | 77 | enum table { |
@@ -96,7 +108,7 @@ enum variables { | |||
96 | 108 | ||
97 | /* Capacity 10 000 entries (for example 10k different artists) */ | 109 | /* Capacity 10 000 entries (for example 10k different artists) */ |
98 | #define UNIQBUF_SIZE (64*1024) | 110 | #define UNIQBUF_SIZE (64*1024) |
99 | static long *uniqbuf; | 111 | static long uniqbuf[UNIQBUF_SIZE / sizeof(long)]; |
100 | 112 | ||
101 | #define MAX_TAGS 5 | 113 | #define MAX_TAGS 5 |
102 | #define MAX_MENU_ID_SIZE 32 | 114 | #define MAX_MENU_ID_SIZE 32 |
@@ -163,8 +175,8 @@ struct match | |||
163 | /* Statusbar text of the current view. */ | 175 | /* Statusbar text of the current view. */ |
164 | static char current_title[MAX_TAGS][128]; | 176 | static char current_title[MAX_TAGS][128]; |
165 | 177 | ||
166 | static struct menu_root *menus[TAGMENU_MAX_MENUS]; | 178 | static struct menu_root * menus[TAGMENU_MAX_MENUS]; |
167 | static struct menu_root *menu; | 179 | static struct menu_root * menu; |
168 | static struct search_instruction *csi; | 180 | static struct search_instruction *csi; |
169 | static const char *strp; | 181 | static const char *strp; |
170 | static int menu_count; | 182 | static int menu_count; |
@@ -176,8 +188,74 @@ static int current_entry_count; | |||
176 | static struct tree_context *tc; | 188 | static struct tree_context *tc; |
177 | 189 | ||
178 | /* a few memory alloc helper */ | 190 | /* a few memory alloc helper */ |
179 | static int tagtree_handle; | 191 | static int tagtree_handle, lock_count; |
180 | static size_t tagtree_bufsize, tagtree_buf_used; | 192 | static size_t tagtree_bufsize, tagtree_buf_used; |
193 | |||
194 | #define UPDATE(x, y) { x = (typeof(x))((char*)(x) + (y)); } | ||
195 | static int move_callback(int handle, void* current, void* new) | ||
196 | { | ||
197 | (void)handle; (void)current; (void)new; | ||
198 | ptrdiff_t diff = new - current; | ||
199 | |||
200 | if (lock_count > 0) | ||
201 | return BUFLIB_CB_CANNOT_MOVE; | ||
202 | |||
203 | UPDATE(menu, diff); | ||
204 | /* loop over menus */ | ||
205 | for(int i = 0; i < menu_count; i++) | ||
206 | { | ||
207 | struct menu_root* menu = menus[i]; | ||
208 | /* then over the menu_entries of a menu */ | ||
209 | for(int j = 0; j < menu->itemcount; j++) | ||
210 | { | ||
211 | struct menu_entry* mentry = menu->items[j]; | ||
212 | /* then over the search_instructions of each menu_entry */ | ||
213 | for(int k = 0; k < mentry->si.tagorder_count; k++) | ||
214 | { | ||
215 | for(int l = 0; l < mentry->si.clause_count[k]; l++) | ||
216 | { | ||
217 | UPDATE(mentry->si.clause[k][l]->str, diff); | ||
218 | UPDATE(mentry->si.clause[k][l], diff); | ||
219 | } | ||
220 | } | ||
221 | UPDATE(menu->items[j], diff); | ||
222 | } | ||
223 | UPDATE(menus[i], diff); | ||
224 | } | ||
225 | |||
226 | /* now the same game for formats */ | ||
227 | for(int i = 0; i < format_count; i++) | ||
228 | { | ||
229 | for(int j = 0; j < formats[i]->clause_count; j++) | ||
230 | { | ||
231 | UPDATE(formats[i]->clause[j]->str, diff); | ||
232 | UPDATE(formats[i]->clause[j], diff); | ||
233 | } | ||
234 | |||
235 | if (formats[i]->formatstr) | ||
236 | UPDATE(formats[i]->formatstr, diff); | ||
237 | |||
238 | UPDATE(formats[i], diff); | ||
239 | } | ||
240 | return BUFLIB_CB_OK; | ||
241 | } | ||
242 | #undef UPDATE | ||
243 | |||
244 | static inline void tagtree_lock(void) | ||
245 | { | ||
246 | lock_count++; | ||
247 | } | ||
248 | |||
249 | static inline void tagtree_unlock(void) | ||
250 | { | ||
251 | lock_count--; | ||
252 | } | ||
253 | |||
254 | static struct buflib_callbacks ops = { | ||
255 | .move_callback = move_callback, | ||
256 | .shrink_callback = NULL, | ||
257 | }; | ||
258 | |||
181 | static void* tagtree_alloc(size_t size) | 259 | static void* tagtree_alloc(size_t size) |
182 | { | 260 | { |
183 | char* buf = core_get_data(tagtree_handle) + tagtree_buf_used; | 261 | char* buf = core_get_data(tagtree_handle) + tagtree_buf_used; |
@@ -201,6 +279,7 @@ static char* tagtree_strdup(const char* buf) | |||
201 | return dest; | 279 | return dest; |
202 | } | 280 | } |
203 | 281 | ||
282 | /* save to call without locking */ | ||
204 | static int get_token_str(char *buf, int size) | 283 | static int get_token_str(char *buf, int size) |
205 | { | 284 | { |
206 | /* Find the start. */ | 285 | /* Find the start. */ |
@@ -510,7 +589,8 @@ static int add_format(const char *buf) | |||
510 | { | 589 | { |
511 | int clause_count = 0; | 590 | int clause_count = 0; |
512 | strp++; | 591 | strp++; |
513 | 592 | ||
593 | tagtree_lock(); | ||
514 | while (1) | 594 | while (1) |
515 | { | 595 | { |
516 | struct tagcache_search_clause *newclause; | 596 | struct tagcache_search_clause *newclause; |
@@ -529,6 +609,7 @@ static int add_format(const char *buf) | |||
529 | 609 | ||
530 | clause_count++; | 610 | clause_count++; |
531 | } | 611 | } |
612 | tagtree_unlock(); | ||
532 | 613 | ||
533 | formats[format_count]->clause_count = clause_count; | 614 | formats[format_count]->clause_count = clause_count; |
534 | } | 615 | } |
@@ -593,9 +674,14 @@ static int get_condition(struct search_instruction *inst) | |||
593 | strp++; | 674 | strp++; |
594 | new_clause->type = clause_logical_or; | 675 | new_clause->type = clause_logical_or; |
595 | } | 676 | } |
596 | else if (!read_clause(new_clause)) | 677 | else |
597 | return -1; | 678 | { |
598 | 679 | tagtree_lock(); | |
680 | bool ret = read_clause(new_clause); | ||
681 | tagtree_unlock(); | ||
682 | if (!ret) | ||
683 | return -1; | ||
684 | } | ||
599 | inst->clause_count[inst->tagorder_count]++; | 685 | inst->clause_count[inst->tagorder_count]++; |
600 | 686 | ||
601 | return 1; | 687 | return 1; |
@@ -616,7 +702,6 @@ static bool parse_search(struct menu_entry *entry, const char *str) | |||
616 | struct search_instruction *inst = &entry->si; | 702 | struct search_instruction *inst = &entry->si; |
617 | char buf[MAX_PATH]; | 703 | char buf[MAX_PATH]; |
618 | int i; | 704 | int i; |
619 | struct menu_root *new_menu; | ||
620 | 705 | ||
621 | strp = str; | 706 | strp = str; |
622 | 707 | ||
@@ -654,8 +739,7 @@ static bool parse_search(struct menu_entry *entry, const char *str) | |||
654 | 739 | ||
655 | /* Allocate a new menu unless link is found. */ | 740 | /* Allocate a new menu unless link is found. */ |
656 | menus[menu_count] = tagtree_alloc0(sizeof(struct menu_root)); | 741 | menus[menu_count] = tagtree_alloc0(sizeof(struct menu_root)); |
657 | new_menu = menus[menu_count]; | 742 | strlcpy(menus[menu_count]->id, buf, MAX_MENU_ID_SIZE); |
658 | strlcpy(new_menu->id, buf, MAX_MENU_ID_SIZE); | ||
659 | entry->link = menu_count; | 743 | entry->link = menu_count; |
660 | ++menu_count; | 744 | ++menu_count; |
661 | 745 | ||
@@ -679,8 +763,11 @@ static bool parse_search(struct menu_entry *entry, const char *str) | |||
679 | break ; | 763 | break ; |
680 | 764 | ||
681 | logf("tag: %d", inst->tagorder[inst->tagorder_count]); | 765 | logf("tag: %d", inst->tagorder[inst->tagorder_count]); |
682 | 766 | ||
767 | tagtree_lock(); | ||
683 | while ( (ret = get_condition(inst)) > 0 ) ; | 768 | while ( (ret = get_condition(inst)) > 0 ) ; |
769 | tagtree_unlock(); | ||
770 | |||
684 | if (ret < 0) | 771 | if (ret < 0) |
685 | return false; | 772 | return false; |
686 | 773 | ||
@@ -697,7 +784,7 @@ static int compare(const void *p1, const void *p2) | |||
697 | { | 784 | { |
698 | struct tagentry *e1 = (struct tagentry *)p1; | 785 | struct tagentry *e1 = (struct tagentry *)p1; |
699 | struct tagentry *e2 = (struct tagentry *)p2; | 786 | struct tagentry *e2 = (struct tagentry *)p2; |
700 | 787 | ||
701 | if (sort_inverse) | 788 | if (sort_inverse) |
702 | return strncasecmp(e2->name, e1->name, MAX_PATH); | 789 | return strncasecmp(e2->name, e1->name, MAX_PATH); |
703 | 790 | ||
@@ -1001,11 +1088,11 @@ static int parse_line(int n, char *buf, void *parameters) | |||
1001 | if (menu->items[menu->itemcount] == NULL) | 1088 | if (menu->items[menu->itemcount] == NULL) |
1002 | menu->items[menu->itemcount] = tagtree_alloc0(sizeof(struct menu_entry)); | 1089 | menu->items[menu->itemcount] = tagtree_alloc0(sizeof(struct menu_entry)); |
1003 | 1090 | ||
1004 | if (!parse_search(menu->items[menu->itemcount], buf)) | 1091 | tagtree_lock(); |
1005 | return 0; | 1092 | if (parse_search(menu->items[menu->itemcount], buf)) |
1006 | 1093 | menu->itemcount++; | |
1007 | menu->itemcount++; | 1094 | tagtree_unlock(); |
1008 | 1095 | ||
1009 | return 0; | 1096 | return 0; |
1010 | } | 1097 | } |
1011 | 1098 | ||
@@ -1040,15 +1127,20 @@ void tagtree_init(void) | |||
1040 | menu_count = 0; | 1127 | menu_count = 0; |
1041 | menu = NULL; | 1128 | menu = NULL; |
1042 | rootmenu = -1; | 1129 | rootmenu = -1; |
1043 | tagtree_handle = core_alloc_maximum("tagtree", &tagtree_bufsize, NULL); | 1130 | tagtree_handle = core_alloc_maximum("tagtree", &tagtree_bufsize, &ops); |
1044 | parse_menu(FILE_SEARCH_INSTRUCTIONS); | 1131 | parse_menu(FILE_SEARCH_INSTRUCTIONS); |
1132 | |||
1133 | /* safety check since tree.c needs to cast tagentry to entry */ | ||
1134 | if (sizeof(struct tagentry) != sizeof(struct entry)) | ||
1135 | panicf("tagentry(%zu) and entry mismatch(%zu)", | ||
1136 | sizeof(struct tagentry), sizeof(struct entry)); | ||
1137 | if (lock_count > 0) | ||
1138 | panicf("tagtree locked after parsing"); | ||
1045 | 1139 | ||
1046 | /* If no root menu is set, assume it's the first single menu | 1140 | /* If no root menu is set, assume it's the first single menu |
1047 | * we have. That shouldn't normally happen. */ | 1141 | * we have. That shouldn't normally happen. */ |
1048 | if (rootmenu < 0) | 1142 | if (rootmenu < 0) |
1049 | rootmenu = 0; | 1143 | rootmenu = 0; |
1050 | |||
1051 | uniqbuf = tagtree_alloc(UNIQBUF_SIZE); | ||
1052 | 1144 | ||
1053 | add_event(PLAYBACK_EVENT_TRACK_BUFFER, false, tagtree_buffer_event); | 1145 | add_event(PLAYBACK_EVENT_TRACK_BUFFER, false, tagtree_buffer_event); |
1054 | add_event(PLAYBACK_EVENT_TRACK_FINISH, false, tagtree_track_finish_event); | 1146 | add_event(PLAYBACK_EVENT_TRACK_FINISH, false, tagtree_track_finish_event); |
@@ -1181,10 +1273,14 @@ static int format_str(struct tagcache_search *tcs, struct display_format *fmt, | |||
1181 | return 0; | 1273 | return 0; |
1182 | } | 1274 | } |
1183 | 1275 | ||
1276 | static struct tagentry* get_entries(struct tree_context *tc) | ||
1277 | { | ||
1278 | return core_get_data(tc->cache.entries_handle); | ||
1279 | } | ||
1280 | |||
1184 | static int retrieve_entries(struct tree_context *c, int offset, bool init) | 1281 | static int retrieve_entries(struct tree_context *c, int offset, bool init) |
1185 | { | 1282 | { |
1186 | struct tagcache_search tcs; | 1283 | struct tagcache_search tcs; |
1187 | struct tagentry *dptr = c->cache.entries; | ||
1188 | struct display_format *fmt; | 1284 | struct display_format *fmt; |
1189 | int i; | 1285 | int i; |
1190 | int namebufused = 0; | 1286 | int namebufused = 0; |
@@ -1242,7 +1338,10 @@ static int retrieve_entries(struct tree_context *c, int offset, bool init) | |||
1242 | csi->result_seek[i]); | 1338 | csi->result_seek[i]); |
1243 | } | 1339 | } |
1244 | } | 1340 | } |
1245 | 1341 | ||
1342 | /* because tagcache saves the clauses, we need to lock the buffer | ||
1343 | * for the entire duration of the search */ | ||
1344 | tagtree_lock(); | ||
1246 | for (i = 0; i <= level; i++) | 1345 | for (i = 0; i <= level; i++) |
1247 | { | 1346 | { |
1248 | int j; | 1347 | int j; |
@@ -1276,6 +1375,10 @@ static int retrieve_entries(struct tree_context *c, int offset, bool init) | |||
1276 | strip = 0; | 1375 | strip = 0; |
1277 | } | 1376 | } |
1278 | 1377 | ||
1378 | /* lock buflib out due to possible yields */ | ||
1379 | tree_lock_cache(c); | ||
1380 | struct tagentry *dptr = core_get_data(c->cache.entries_handle); | ||
1381 | |||
1279 | if (tag != tag_title && tag != tag_filename) | 1382 | if (tag != tag_title && tag != tag_filename) |
1280 | { | 1383 | { |
1281 | if (offset == 0) | 1384 | if (offset == 0) |
@@ -1315,6 +1418,7 @@ static int retrieve_entries(struct tree_context *c, int offset, bool init) | |||
1315 | 1418 | ||
1316 | fmt = NULL; | 1419 | fmt = NULL; |
1317 | /* Check the format */ | 1420 | /* Check the format */ |
1421 | tagtree_lock(); | ||
1318 | for (i = 0; i < format_count; i++) | 1422 | for (i = 0; i < format_count; i++) |
1319 | { | 1423 | { |
1320 | if (formats[i]->group_id != csi->format_id[level]) | 1424 | if (formats[i]->group_id != csi->format_id[level]) |
@@ -1327,6 +1431,7 @@ static int retrieve_entries(struct tree_context *c, int offset, bool init) | |||
1327 | break; | 1431 | break; |
1328 | } | 1432 | } |
1329 | } | 1433 | } |
1434 | tagtree_unlock(); | ||
1330 | 1435 | ||
1331 | if (strcmp(tcs.result, UNTAGGED) == 0) | 1436 | if (strcmp(tcs.result, UNTAGGED) == 0) |
1332 | { | 1437 | { |
@@ -1337,7 +1442,7 @@ static int retrieve_entries(struct tree_context *c, int offset, bool init) | |||
1337 | 1442 | ||
1338 | if (!tcs.ramresult || fmt) | 1443 | if (!tcs.ramresult || fmt) |
1339 | { | 1444 | { |
1340 | dptr->name = &c->cache.name_buffer[namebufused]; | 1445 | dptr->name = core_get_data(c->cache.name_buffer_handle)+namebufused; |
1341 | 1446 | ||
1342 | if (fmt) | 1447 | if (fmt) |
1343 | { | 1448 | { |
@@ -1354,6 +1459,8 @@ static int retrieve_entries(struct tree_context *c, int offset, bool init) | |||
1354 | { | 1459 | { |
1355 | logf("format_str() failed"); | 1460 | logf("format_str() failed"); |
1356 | tagcache_search_finish(&tcs); | 1461 | tagcache_search_finish(&tcs); |
1462 | tree_unlock_cache(c); | ||
1463 | tagtree_unlock(); | ||
1357 | return 0; | 1464 | return 0; |
1358 | } | 1465 | } |
1359 | else | 1466 | else |
@@ -1392,6 +1499,8 @@ static int retrieve_entries(struct tree_context *c, int offset, bool init) | |||
1392 | if (!show_search_progress(false, total_count)) | 1499 | if (!show_search_progress(false, total_count)) |
1393 | { /* user aborted */ | 1500 | { /* user aborted */ |
1394 | tagcache_search_finish(&tcs); | 1501 | tagcache_search_finish(&tcs); |
1502 | tree_unlock_cache(c); | ||
1503 | tagtree_unlock(); | ||
1395 | return current_entry_count; | 1504 | return current_entry_count; |
1396 | } | 1505 | } |
1397 | } | 1506 | } |
@@ -1399,15 +1508,17 @@ static int retrieve_entries(struct tree_context *c, int offset, bool init) | |||
1399 | 1508 | ||
1400 | if (sort) | 1509 | if (sort) |
1401 | { | 1510 | { |
1402 | int entry_size = sizeof(struct tagentry); | 1511 | struct tagentry *entries = get_entries(c); |
1403 | qsort(c->cache.entries + special_entry_count * entry_size, | 1512 | qsort(&entries[special_entry_count], |
1404 | current_entry_count - special_entry_count, | 1513 | current_entry_count - special_entry_count, |
1405 | entry_size, compare); | 1514 | sizeof(struct tagentry), compare); |
1406 | } | 1515 | } |
1407 | 1516 | ||
1408 | if (!init) | 1517 | if (!init) |
1409 | { | 1518 | { |
1410 | tagcache_search_finish(&tcs); | 1519 | tagcache_search_finish(&tcs); |
1520 | tree_unlock_cache(c); | ||
1521 | tagtree_unlock(); | ||
1411 | return current_entry_count; | 1522 | return current_entry_count; |
1412 | } | 1523 | } |
1413 | 1524 | ||
@@ -1422,7 +1533,9 @@ static int retrieve_entries(struct tree_context *c, int offset, bool init) | |||
1422 | } | 1533 | } |
1423 | 1534 | ||
1424 | tagcache_search_finish(&tcs); | 1535 | tagcache_search_finish(&tcs); |
1425 | 1536 | tree_unlock_cache(c); | |
1537 | tagtree_unlock(); | ||
1538 | |||
1426 | if (!sort && (sort_inverse || sort_limit)) | 1539 | if (!sort && (sort_inverse || sort_limit)) |
1427 | { | 1540 | { |
1428 | splashf(HZ*4, ID2P(LANG_SHOWDIR_BUFFER_FULL), total_count); | 1541 | splashf(HZ*4, ID2P(LANG_SHOWDIR_BUFFER_FULL), total_count); |
@@ -1435,7 +1548,7 @@ static int retrieve_entries(struct tree_context *c, int offset, bool init) | |||
1435 | 1548 | ||
1436 | if (strip) | 1549 | if (strip) |
1437 | { | 1550 | { |
1438 | dptr = c->cache.entries; | 1551 | dptr = get_entries(c); |
1439 | for (i = special_entry_count; i < current_entry_count; i++, dptr++) | 1552 | for (i = special_entry_count; i < current_entry_count; i++, dptr++) |
1440 | { | 1553 | { |
1441 | int len = strlen(dptr->name); | 1554 | int len = strlen(dptr->name); |
@@ -1446,14 +1559,14 @@ static int retrieve_entries(struct tree_context *c, int offset, bool init) | |||
1446 | dptr->name = &dptr->name[strip]; | 1559 | dptr->name = &dptr->name[strip]; |
1447 | } | 1560 | } |
1448 | } | 1561 | } |
1449 | 1562 | ||
1450 | return total_count; | 1563 | return total_count; |
1451 | 1564 | ||
1452 | } | 1565 | } |
1453 | 1566 | ||
1454 | static int load_root(struct tree_context *c) | 1567 | static int load_root(struct tree_context *c) |
1455 | { | 1568 | { |
1456 | struct tagentry *dptr = c->cache.entries; | 1569 | struct tagentry *dptr = core_get_data(c->cache.entries_handle); |
1457 | int i; | 1570 | int i; |
1458 | 1571 | ||
1459 | tc = c; | 1572 | tc = c; |
@@ -1569,6 +1682,10 @@ int tagtree_enter(struct tree_context* c) | |||
1569 | c->pos_history[c->dirlevel] = c->firstpos; | 1682 | c->pos_history[c->dirlevel] = c->firstpos; |
1570 | c->dirlevel++; | 1683 | c->dirlevel++; |
1571 | 1684 | ||
1685 | /* lock buflib for possible I/O to protect dptr */ | ||
1686 | tree_lock_cache(c); | ||
1687 | tagtree_lock(); | ||
1688 | |||
1572 | switch (c->currtable) { | 1689 | switch (c->currtable) { |
1573 | case ROOT: | 1690 | case ROOT: |
1574 | c->currextra = newextra; | 1691 | c->currextra = newextra; |
@@ -1634,6 +1751,8 @@ int tagtree_enter(struct tree_context* c) | |||
1634 | if (rc < 0 || !searchstring[0]) | 1751 | if (rc < 0 || !searchstring[0]) |
1635 | { | 1752 | { |
1636 | tagtree_exit(c); | 1753 | tagtree_exit(c); |
1754 | tree_unlock_cache(c); | ||
1755 | tagtree_unlock(); | ||
1637 | return 0; | 1756 | return 0; |
1638 | } | 1757 | } |
1639 | if (csi->clause[i][j]->numeric) | 1758 | if (csi->clause[i][j]->numeric) |
@@ -1682,9 +1801,12 @@ int tagtree_enter(struct tree_context* c) | |||
1682 | c->dirlevel--; | 1801 | c->dirlevel--; |
1683 | break; | 1802 | break; |
1684 | } | 1803 | } |
1804 | |||
1685 | 1805 | ||
1686 | c->selected_item=0; | 1806 | c->selected_item=0; |
1687 | gui_synclist_select_item(&tree_lists, c->selected_item); | 1807 | gui_synclist_select_item(&tree_lists, c->selected_item); |
1808 | tree_unlock_cache(c); | ||
1809 | tagtree_unlock(); | ||
1688 | 1810 | ||
1689 | return rc; | 1811 | return rc; |
1690 | } | 1812 | } |
@@ -1704,14 +1826,13 @@ void tagtree_exit(struct tree_context* c) | |||
1704 | int tagtree_get_filename(struct tree_context* c, char *buf, int buflen) | 1826 | int tagtree_get_filename(struct tree_context* c, char *buf, int buflen) |
1705 | { | 1827 | { |
1706 | struct tagcache_search tcs; | 1828 | struct tagcache_search tcs; |
1707 | struct tagentry *entry; | 1829 | int extraseek = tagtree_get_entry(c, c->selected_item)->extraseek; |
1708 | 1830 | ||
1709 | entry = tagtree_get_entry(c, c->selected_item); | ||
1710 | 1831 | ||
1711 | if (!tagcache_search(&tcs, tag_filename)) | 1832 | if (!tagcache_search(&tcs, tag_filename)) |
1712 | return -1; | 1833 | return -1; |
1713 | 1834 | ||
1714 | if (!tagcache_retrieve(&tcs, entry->extraseek, tcs.type, buf, buflen)) | 1835 | if (!tagcache_retrieve(&tcs, extraseek, tcs.type, buf, buflen)) |
1715 | { | 1836 | { |
1716 | tagcache_search_finish(&tcs); | 1837 | tagcache_search_finish(&tcs); |
1717 | return -2; | 1838 | return -2; |
@@ -1790,9 +1911,9 @@ static bool insert_all_playlist(struct tree_context *c, int position, bool queue | |||
1790 | 1911 | ||
1791 | bool tagtree_insert_selection_playlist(int position, bool queue) | 1912 | bool tagtree_insert_selection_playlist(int position, bool queue) |
1792 | { | 1913 | { |
1793 | struct tagentry *dptr; | ||
1794 | char buf[MAX_PATH]; | 1914 | char buf[MAX_PATH]; |
1795 | int dirlevel = tc->dirlevel; | 1915 | int dirlevel = tc->dirlevel; |
1916 | int newtable; | ||
1796 | 1917 | ||
1797 | show_search_progress( | 1918 | show_search_progress( |
1798 | #ifdef HAVE_DISK_STORAGE | 1919 | #ifdef HAVE_DISK_STORAGE |
@@ -1804,10 +1925,10 @@ bool tagtree_insert_selection_playlist(int position, bool queue) | |||
1804 | 1925 | ||
1805 | 1926 | ||
1806 | /* We need to set the table to allsubentries. */ | 1927 | /* We need to set the table to allsubentries. */ |
1807 | dptr = tagtree_get_entry(tc, tc->selected_item); | 1928 | newtable = tagtree_get_entry(tc, tc->selected_item)->newtable; |
1808 | 1929 | ||
1809 | /* Insert a single track? */ | 1930 | /* Insert a single track? */ |
1810 | if (dptr->newtable == PLAYTRACK) | 1931 | if (newtable == PLAYTRACK) |
1811 | { | 1932 | { |
1812 | if (tagtree_get_filename(tc, buf, sizeof buf) < 0) | 1933 | if (tagtree_get_filename(tc, buf, sizeof buf) < 0) |
1813 | { | 1934 | { |
@@ -1819,29 +1940,29 @@ bool tagtree_insert_selection_playlist(int position, bool queue) | |||
1819 | return true; | 1940 | return true; |
1820 | } | 1941 | } |
1821 | 1942 | ||
1822 | if (dptr->newtable == NAVIBROWSE) | 1943 | if (newtable == NAVIBROWSE) |
1823 | { | 1944 | { |
1824 | tagtree_enter(tc); | 1945 | tagtree_enter(tc); |
1825 | tagtree_load(tc); | 1946 | tagtree_load(tc); |
1826 | dptr = tagtree_get_entry(tc, tc->selected_item); | 1947 | newtable = tagtree_get_entry(tc, tc->selected_item)->newtable; |
1827 | } | 1948 | } |
1828 | else if (dptr->newtable != ALLSUBENTRIES) | 1949 | else if (newtable != ALLSUBENTRIES) |
1829 | { | 1950 | { |
1830 | logf("unsupported table: %d", dptr->newtable); | 1951 | logf("unsupported table: %d", newtable); |
1831 | return false; | 1952 | return false; |
1832 | } | 1953 | } |
1833 | 1954 | ||
1834 | /* Now the current table should be allsubentries. */ | 1955 | /* Now the current table should be allsubentries. */ |
1835 | if (dptr->newtable != PLAYTRACK) | 1956 | if (newtable != PLAYTRACK) |
1836 | { | 1957 | { |
1837 | tagtree_enter(tc); | 1958 | tagtree_enter(tc); |
1838 | tagtree_load(tc); | 1959 | tagtree_load(tc); |
1839 | dptr = tagtree_get_entry(tc, tc->selected_item); | 1960 | newtable = tagtree_get_entry(tc, tc->selected_item)->newtable; |
1840 | 1961 | ||
1841 | /* And now the newtable should be playtrack. */ | 1962 | /* And now the newtable should be playtrack. */ |
1842 | if (dptr->newtable != PLAYTRACK) | 1963 | if (newtable != PLAYTRACK) |
1843 | { | 1964 | { |
1844 | logf("newtable: %d !!", dptr->newtable); | 1965 | logf("newtable: %d !!", newtable); |
1845 | tc->dirlevel = dirlevel; | 1966 | tc->dirlevel = dirlevel; |
1846 | return false; | 1967 | return false; |
1847 | } | 1968 | } |
@@ -1886,9 +2007,9 @@ static int tagtree_play_folder(struct tree_context* c) | |||
1886 | return 0; | 2007 | return 0; |
1887 | } | 2008 | } |
1888 | 2009 | ||
1889 | struct tagentry* tagtree_get_entry(struct tree_context *c, int id) | 2010 | static struct tagentry* tagtree_get_entry(struct tree_context *c, int id) |
1890 | { | 2011 | { |
1891 | struct tagentry *entry = (struct tagentry *)c->cache.entries; | 2012 | struct tagentry *entry; |
1892 | int realid = id - current_offset; | 2013 | int realid = id - current_offset; |
1893 | 2014 | ||
1894 | /* Load the next chunk if necessary. */ | 2015 | /* Load the next chunk if necessary. */ |
@@ -1905,10 +2026,22 @@ struct tagentry* tagtree_get_entry(struct tree_context *c, int id) | |||
1905 | realid = id - current_offset; | 2026 | realid = id - current_offset; |
1906 | cpu_boost(false); | 2027 | cpu_boost(false); |
1907 | } | 2028 | } |
1908 | 2029 | ||
2030 | entry = get_entries(c); | ||
1909 | return &entry[realid]; | 2031 | return &entry[realid]; |
1910 | } | 2032 | } |
1911 | 2033 | ||
2034 | char* tagtree_get_entry_name(struct tree_context *c, int id, | ||
2035 | char* buf, size_t bufsize) | ||
2036 | { | ||
2037 | struct tagentry *entry = tagtree_get_entry(c, id); | ||
2038 | if (!entry) | ||
2039 | return NULL; | ||
2040 | strlcpy(buf, entry->name, bufsize); | ||
2041 | return buf; | ||
2042 | } | ||
2043 | |||
2044 | |||
1912 | char *tagtree_get_title(struct tree_context* c) | 2045 | char *tagtree_get_title(struct tree_context* c) |
1913 | { | 2046 | { |
1914 | switch (c->currtable) | 2047 | switch (c->currtable) |
diff --git a/apps/tagtree.h b/apps/tagtree.h index aaf5158e5b..26952b40b7 100644 --- a/apps/tagtree.h +++ b/apps/tagtree.h | |||
@@ -30,19 +30,14 @@ | |||
30 | #define TAGMENU_MAX_MENUS 32 | 30 | #define TAGMENU_MAX_MENUS 32 |
31 | #define TAGMENU_MAX_FMTS 32 | 31 | #define TAGMENU_MAX_FMTS 32 |
32 | 32 | ||
33 | struct tagentry { | ||
34 | char *name; | ||
35 | int newtable; | ||
36 | int extraseek; | ||
37 | }; | ||
38 | |||
39 | bool tagtree_export(void); | 33 | bool tagtree_export(void); |
40 | bool tagtree_import(void); | 34 | bool tagtree_import(void); |
41 | void tagtree_init(void) INIT_ATTR; | 35 | void tagtree_init(void) INIT_ATTR; |
42 | int tagtree_enter(struct tree_context* c); | 36 | int tagtree_enter(struct tree_context* c); |
43 | void tagtree_exit(struct tree_context* c); | 37 | void tagtree_exit(struct tree_context* c); |
44 | int tagtree_load(struct tree_context* c); | 38 | int tagtree_load(struct tree_context* c); |
45 | struct tagentry* tagtree_get_entry(struct tree_context *c, int id); | 39 | char* tagtree_get_entry_name(struct tree_context *c, int id, |
40 | char* buf, size_t bufsize); | ||
46 | bool tagtree_insert_selection_playlist(int position, bool queue); | 41 | bool tagtree_insert_selection_playlist(int position, bool queue); |
47 | char *tagtree_get_title(struct tree_context* c); | 42 | char *tagtree_get_title(struct tree_context* c); |
48 | int tagtree_get_attr(struct tree_context* c); | 43 | int tagtree_get_attr(struct tree_context* c); |
diff --git a/apps/tdspeed.c b/apps/tdspeed.c index 476995a271..69699e5bb4 100644 --- a/apps/tdspeed.c +++ b/apps/tdspeed.c | |||
@@ -38,6 +38,46 @@ | |||
38 | 38 | ||
39 | #define FIXED_BUFSIZE 3072 /* 48KHz factor 3.0 */ | 39 | #define FIXED_BUFSIZE 3072 /* 48KHz factor 3.0 */ |
40 | 40 | ||
41 | static int32_t** dsp_src; | ||
42 | static int handles[4]; | ||
43 | static int32_t *overlap_buffer[2] = { NULL, NULL }; | ||
44 | static int32_t *outbuf[2] = { NULL, NULL }; | ||
45 | |||
46 | static int move_callback(int handle, void* current, void* new) | ||
47 | { | ||
48 | /* TODO */ | ||
49 | (void)handle; | ||
50 | if (dsp_src) | ||
51 | { | ||
52 | int ch = (current == outbuf[0]) ? 0 : 1; | ||
53 | dsp_src[ch] = outbuf[ch] = new; | ||
54 | } | ||
55 | return BUFLIB_CB_OK; | ||
56 | } | ||
57 | |||
58 | static struct buflib_callbacks ops = { | ||
59 | .move_callback = move_callback, | ||
60 | .shrink_callback = NULL, | ||
61 | }; | ||
62 | |||
63 | static int ovl_move_callback(int handle, void* current, void* new) | ||
64 | { | ||
65 | /* TODO */ | ||
66 | (void)handle; | ||
67 | if (dsp_src) | ||
68 | { | ||
69 | int ch = (current == overlap_buffer[0]) ? 0 : 1; | ||
70 | overlap_buffer[ch] = new; | ||
71 | } | ||
72 | return BUFLIB_CB_OK; | ||
73 | } | ||
74 | |||
75 | static struct buflib_callbacks ovl_ops = { | ||
76 | .move_callback = ovl_move_callback, | ||
77 | .shrink_callback = NULL, | ||
78 | }; | ||
79 | |||
80 | |||
41 | static struct tdspeed_state_s | 81 | static struct tdspeed_state_s |
42 | { | 82 | { |
43 | bool stereo; | 83 | bool stereo; |
@@ -51,39 +91,47 @@ static struct tdspeed_state_s | |||
51 | int32_t *ovl_buff[2]; /* overlap buffer */ | 91 | int32_t *ovl_buff[2]; /* overlap buffer */ |
52 | } tdspeed_state; | 92 | } tdspeed_state; |
53 | 93 | ||
54 | static int32_t *overlap_buffer[2] = { NULL, NULL }; | ||
55 | static int32_t *outbuf[2] = { NULL, NULL }; | ||
56 | |||
57 | void tdspeed_init(void) | 94 | void tdspeed_init(void) |
58 | { | 95 | { |
59 | int handle; | ||
60 | |||
61 | if (!global_settings.timestretch_enabled) | 96 | if (!global_settings.timestretch_enabled) |
62 | return; | 97 | return; |
63 | 98 | ||
64 | /* Allocate buffers */ | 99 | /* Allocate buffers */ |
65 | if (overlap_buffer[0] == NULL) | 100 | if (overlap_buffer[0] == NULL) |
66 | { | 101 | { |
67 | handle = core_alloc("tdspeed ovl left", FIXED_BUFSIZE * sizeof(int32_t)); | 102 | handles[0] = core_alloc_ex("tdspeed ovl left", FIXED_BUFSIZE * sizeof(int32_t), &ovl_ops); |
68 | overlap_buffer[0] = core_get_data(handle); | 103 | overlap_buffer[0] = core_get_data(handles[0]); |
69 | } | 104 | } |
70 | if (overlap_buffer[1] == NULL) | 105 | if (overlap_buffer[1] == NULL) |
71 | { | 106 | { |
72 | handle = core_alloc("tdspeed ovl right", FIXED_BUFSIZE * sizeof(int32_t)); | 107 | handles[1] = core_alloc_ex("tdspeed ovl right", FIXED_BUFSIZE * sizeof(int32_t), &ovl_ops); |
73 | overlap_buffer[1] = core_get_data(handle); | 108 | overlap_buffer[1] = core_get_data(handles[1]); |
74 | } | 109 | } |
75 | if (outbuf[0] == NULL) | 110 | if (outbuf[0] == NULL) |
76 | { | 111 | { |
77 | handle = core_alloc("tdspeed left", TDSPEED_OUTBUFSIZE * sizeof(int32_t)); | 112 | handles[2] = core_alloc_ex("tdspeed left", TDSPEED_OUTBUFSIZE * sizeof(int32_t), &ops); |
78 | outbuf[0] = core_get_data(handle); | 113 | outbuf[0] = core_get_data(handles[2]); |
79 | } | 114 | } |
80 | if (outbuf[1] == NULL) | 115 | if (outbuf[1] == NULL) |
81 | { | 116 | { |
82 | handle = core_alloc("tdspeed right", TDSPEED_OUTBUFSIZE * sizeof(int32_t)); | 117 | handles[3] = core_alloc_ex("tdspeed right", TDSPEED_OUTBUFSIZE * sizeof(int32_t), &ops); |
83 | outbuf[1] = core_get_data(handle); | 118 | outbuf[1] = core_get_data(handles[3]); |
84 | } | 119 | } |
85 | } | 120 | } |
86 | 121 | ||
122 | void tdspeed_finish(void) | ||
123 | { | ||
124 | for(unsigned i = 0; i < ARRAYLEN(handles); i++) | ||
125 | { | ||
126 | if (handles[i] > 0) | ||
127 | { | ||
128 | core_free(handles[i]); | ||
129 | handles[i] = 0; | ||
130 | } | ||
131 | } | ||
132 | overlap_buffer[0] = overlap_buffer[1] = NULL; | ||
133 | outbuf[0] = outbuf[1] = NULL; | ||
134 | } | ||
87 | 135 | ||
88 | bool tdspeed_config(int samplerate, bool stereo, int32_t factor) | 136 | bool tdspeed_config(int samplerate, bool stereo, int32_t factor) |
89 | { | 137 | { |
@@ -390,6 +438,7 @@ long tdspeed_est_input_size(long size) | |||
390 | 438 | ||
391 | int tdspeed_doit(int32_t *src[], int count) | 439 | int tdspeed_doit(int32_t *src[], int count) |
392 | { | 440 | { |
441 | dsp_src = src; | ||
393 | count = tdspeed_apply( (int32_t *[2]) { outbuf[0], outbuf[1] }, | 442 | count = tdspeed_apply( (int32_t *[2]) { outbuf[0], outbuf[1] }, |
394 | src, count, 0, TDSPEED_OUTBUFSIZE); | 443 | src, count, 0, TDSPEED_OUTBUFSIZE); |
395 | 444 | ||
diff --git a/apps/tdspeed.h b/apps/tdspeed.h index c3b7fc4635..e91eeb1701 100644 --- a/apps/tdspeed.h +++ b/apps/tdspeed.h | |||
@@ -36,7 +36,8 @@ | |||
36 | #define GET_STRETCH(pitch, speed) \ | 36 | #define GET_STRETCH(pitch, speed) \ |
37 | ((speed * PITCH_SPEED_100 + pitch / 2L) / pitch) | 37 | ((speed * PITCH_SPEED_100 + pitch / 2L) / pitch) |
38 | 38 | ||
39 | void tdspeed_init(void) INIT_ATTR; | 39 | void tdspeed_init(void); |
40 | void tdspeed_finish(void); | ||
40 | bool tdspeed_config(int samplerate, bool stereo, int32_t factor); | 41 | bool tdspeed_config(int samplerate, bool stereo, int32_t factor); |
41 | long tdspeed_est_output_size(void); | 42 | long tdspeed_est_output_size(void); |
42 | long tdspeed_est_input_size(long size); | 43 | long tdspeed_est_input_size(long size); |
diff --git a/apps/tree.c b/apps/tree.c index 211ddb2f9b..c7484ff420 100644 --- a/apps/tree.c +++ b/apps/tree.c | |||
@@ -104,12 +104,18 @@ static int ft_play_dirname(char* name); | |||
104 | static void ft_play_filename(char *dir, char *file); | 104 | static void ft_play_filename(char *dir, char *file); |
105 | static void say_filetype(int attr); | 105 | static void say_filetype(int attr); |
106 | 106 | ||
107 | static struct entry* get_entry_at(struct tree_context *t, int index) | 107 | struct entry* tree_get_entries(struct tree_context *t) |
108 | { | 108 | { |
109 | struct entry* entries = t->cache.entries; | 109 | return core_get_data(t->cache.entries_handle); |
110 | } | ||
111 | |||
112 | struct entry* tree_get_entry_at(struct tree_context *t, int index) | ||
113 | { | ||
114 | struct entry* entries = tree_get_entries(t); | ||
110 | return &entries[index]; | 115 | return &entries[index]; |
111 | } | 116 | } |
112 | 117 | ||
118 | |||
113 | static const char* tree_get_filename(int selected_item, void *data, | 119 | static const char* tree_get_filename(int selected_item, void *data, |
114 | char *buffer, size_t buffer_len) | 120 | char *buffer, size_t buffer_len) |
115 | { | 121 | { |
@@ -122,12 +128,12 @@ static const char* tree_get_filename(int selected_item, void *data, | |||
122 | 128 | ||
123 | if (id3db) | 129 | if (id3db) |
124 | { | 130 | { |
125 | return tagtree_get_entry(&tc, selected_item)->name; | 131 | return tagtree_get_entry_name(&tc, selected_item, buffer, buffer_len); |
126 | } | 132 | } |
127 | else | 133 | else |
128 | #endif | 134 | #endif |
129 | { | 135 | { |
130 | struct entry* e = get_entry_at(local_tc, selected_item); | 136 | struct entry* e = tree_get_entry_at(local_tc, selected_item); |
131 | name = e->name; | 137 | name = e->name; |
132 | attr = e->attr; | 138 | attr = e->attr; |
133 | } | 139 | } |
@@ -169,7 +175,7 @@ static int tree_get_filecolor(int selected_item, void * data) | |||
169 | if (*tc.dirfilter == SHOW_ID3DB) | 175 | if (*tc.dirfilter == SHOW_ID3DB) |
170 | return -1; | 176 | return -1; |
171 | struct tree_context * local_tc=(struct tree_context *)data; | 177 | struct tree_context * local_tc=(struct tree_context *)data; |
172 | struct entry* e = get_entry_at(local_tc, selected_item); | 178 | struct entry* e = tree_get_entry_at(local_tc, selected_item); |
173 | return filetype_get_color(e->name, e->attr); | 179 | return filetype_get_color(e->name, e->attr); |
174 | } | 180 | } |
175 | #endif | 181 | #endif |
@@ -185,7 +191,7 @@ static enum themable_icons tree_get_fileicon(int selected_item, void * data) | |||
185 | else | 191 | else |
186 | #endif | 192 | #endif |
187 | { | 193 | { |
188 | struct entry* e = get_entry_at(local_tc, selected_item); | 194 | struct entry* e = tree_get_entry_at(local_tc, selected_item); |
189 | return filetype_get_icon(e->attr); | 195 | return filetype_get_icon(e->attr); |
190 | } | 196 | } |
191 | } | 197 | } |
@@ -197,16 +203,17 @@ static int tree_voice_cb(int selected_item, void * data) | |||
197 | int attr=0; | 203 | int attr=0; |
198 | #ifdef HAVE_TAGCACHE | 204 | #ifdef HAVE_TAGCACHE |
199 | bool id3db = *(local_tc->dirfilter) == SHOW_ID3DB; | 205 | bool id3db = *(local_tc->dirfilter) == SHOW_ID3DB; |
206 | char buf[AVERAGE_FILENAME_LENGTH*2]; | ||
200 | 207 | ||
201 | if (id3db) | 208 | if (id3db) |
202 | { | 209 | { |
203 | attr = tagtree_get_attr(local_tc); | 210 | attr = tagtree_get_attr(local_tc); |
204 | name = tagtree_get_entry(local_tc, selected_item)->name; | 211 | name = tagtree_get_entry_name(local_tc, selected_item, buf, sizeof(buf)); |
205 | } | 212 | } |
206 | else | 213 | else |
207 | #endif | 214 | #endif |
208 | { | 215 | { |
209 | struct entry* e = get_entry_at(local_tc, selected_item); | 216 | struct entry* e = tree_get_entry_at(local_tc, selected_item); |
210 | name = e->name; | 217 | name = e->name; |
211 | attr = e->attr; | 218 | attr = e->attr; |
212 | } | 219 | } |
@@ -329,7 +336,7 @@ static int tree_get_file_position(char * filename) | |||
329 | /* use lastfile to determine the selected item (default=0) */ | 336 | /* use lastfile to determine the selected item (default=0) */ |
330 | for (i=0; i < tc.filesindir; i++) | 337 | for (i=0; i < tc.filesindir; i++) |
331 | { | 338 | { |
332 | e = get_entry_at(&tc, i); | 339 | e = tree_get_entry_at(&tc, i); |
333 | if (!strcasecmp(e->name, filename)) | 340 | if (!strcasecmp(e->name, filename)) |
334 | return(i); | 341 | return(i); |
335 | } | 342 | } |
@@ -531,7 +538,7 @@ char* get_current_file(char* buffer, size_t buffer_len) | |||
531 | return NULL; | 538 | return NULL; |
532 | #endif | 539 | #endif |
533 | 540 | ||
534 | struct entry* e = get_entry_at(&tc, tc.selected_item); | 541 | struct entry* e = tree_get_entry_at(&tc, tc.selected_item); |
535 | if (getcwd(buffer, buffer_len)) | 542 | if (getcwd(buffer, buffer_len)) |
536 | { | 543 | { |
537 | if (tc.dirlength) | 544 | if (tc.dirlength) |
@@ -650,7 +657,6 @@ static int dirbrowse(void) | |||
650 | 657 | ||
651 | gui_synclist_draw(&tree_lists); | 658 | gui_synclist_draw(&tree_lists); |
652 | while(1) { | 659 | while(1) { |
653 | struct entry *entries = tc.cache.entries; | ||
654 | bool restore = false; | 660 | bool restore = false; |
655 | if (tc.dirlevel < 0) | 661 | if (tc.dirlevel < 0) |
656 | tc.dirlevel = 0; /* shouldnt be needed.. this code needs work! */ | 662 | tc.dirlevel = 0; /* shouldnt be needed.. this code needs work! */ |
@@ -666,8 +672,9 @@ static int dirbrowse(void) | |||
666 | if ( numentries == 0 ) | 672 | if ( numentries == 0 ) |
667 | break; | 673 | break; |
668 | 674 | ||
675 | short attr = tree_get_entry_at(&tc, tc.selected_item)->attr; | ||
669 | if ((tc.browse->flags & BROWSE_SELECTONLY) && | 676 | if ((tc.browse->flags & BROWSE_SELECTONLY) && |
670 | !(entries[tc.selected_item].attr & ATTR_DIRECTORY)) | 677 | !(attr & ATTR_DIRECTORY)) |
671 | { | 678 | { |
672 | tc.browse->flags |= BROWSE_SELECTED; | 679 | tc.browse->flags |= BROWSE_SELECTED; |
673 | get_current_file(tc.browse->buf, tc.browse->bufsize); | 680 | get_current_file(tc.browse->buf, tc.browse->bufsize); |
@@ -792,15 +799,14 @@ static int dirbrowse(void) | |||
792 | else | 799 | else |
793 | #endif | 800 | #endif |
794 | { | 801 | { |
795 | attr = entries[tc.selected_item].attr; | 802 | struct entry *entry = tree_get_entry_at(&tc, tc.selected_item); |
803 | attr = entry->attr; | ||
796 | 804 | ||
797 | if (currdir[1]) /* Not in / */ | 805 | if (currdir[1]) /* Not in / */ |
798 | snprintf(buf, sizeof buf, "%s/%s", | 806 | snprintf(buf, sizeof buf, "%s/%s", |
799 | currdir, | 807 | currdir, entry->name); |
800 | entries[tc.selected_item].name); | ||
801 | else /* In / */ | 808 | else /* In / */ |
802 | snprintf(buf, sizeof buf, "/%s", | 809 | snprintf(buf, sizeof buf, "/%s", entry->name); |
803 | entries[tc.selected_item].name); | ||
804 | } | 810 | } |
805 | onplay_result = onplay(buf, attr, curr_context, hotkey); | 811 | onplay_result = onplay(buf, attr, curr_context, hotkey); |
806 | } | 812 | } |
@@ -999,10 +1005,36 @@ int rockbox_browse(struct browse_context *browse) | |||
999 | return ret_val; | 1005 | return ret_val; |
1000 | } | 1006 | } |
1001 | 1007 | ||
1008 | static int move_callback(int handle, void* current, void* new) | ||
1009 | { | ||
1010 | struct tree_cache* cache = &tc.cache; | ||
1011 | if (cache->lock_count > 0) | ||
1012 | return BUFLIB_CB_CANNOT_MOVE; | ||
1013 | |||
1014 | size_t diff = new - current; | ||
1015 | /* FIX_PTR makes sure to not accidentally update static allocations */ | ||
1016 | #define FIX_PTR(x) \ | ||
1017 | { if ((void*)x > current && (void*)x < (current+cache->name_buffer_size)) x+= diff; } | ||
1018 | |||
1019 | if (handle == cache->name_buffer_handle) | ||
1020 | { /* update entry structs, *even if they are struct tagentry */ | ||
1021 | struct entry *this = core_get_data(cache->entries_handle); | ||
1022 | struct entry *last = this + cache->max_entries; | ||
1023 | for(; this < last; this++) | ||
1024 | FIX_PTR(this->name); | ||
1025 | } | ||
1026 | /* nothing to do if entries moved */ | ||
1027 | return BUFLIB_CB_OK; | ||
1028 | } | ||
1029 | |||
1030 | static struct buflib_callbacks ops = { | ||
1031 | .move_callback = move_callback, | ||
1032 | .shrink_callback = NULL, | ||
1033 | }; | ||
1034 | |||
1002 | void tree_mem_init(void) | 1035 | void tree_mem_init(void) |
1003 | { | 1036 | { |
1004 | /* initialize tree context struct */ | 1037 | /* initialize tree context struct */ |
1005 | int handle; | ||
1006 | struct tree_cache* cache = &tc.cache; | 1038 | struct tree_cache* cache = &tc.cache; |
1007 | memset(&tc, 0, sizeof(tc)); | 1039 | memset(&tc, 0, sizeof(tc)); |
1008 | tc.dirfilter = &global_settings.dirfilter; | 1040 | tc.dirfilter = &global_settings.dirfilter; |
@@ -1010,12 +1042,14 @@ void tree_mem_init(void) | |||
1010 | 1042 | ||
1011 | cache->name_buffer_size = AVERAGE_FILENAME_LENGTH * | 1043 | cache->name_buffer_size = AVERAGE_FILENAME_LENGTH * |
1012 | global_settings.max_files_in_dir; | 1044 | global_settings.max_files_in_dir; |
1013 | handle = core_alloc("tree names", cache->name_buffer_size); | 1045 | cache->name_buffer_handle = core_alloc_ex("tree names", |
1014 | cache->name_buffer = core_get_data(handle); | 1046 | cache->name_buffer_size, |
1047 | &ops); | ||
1015 | 1048 | ||
1016 | cache->max_entries = global_settings.max_files_in_dir; | 1049 | cache->max_entries = global_settings.max_files_in_dir; |
1017 | handle = core_alloc("tree entries", cache->max_entries*(sizeof(struct entry))); | 1050 | cache->entries_handle = core_alloc_ex("tree entries", |
1018 | cache->entries = core_get_data(handle); | 1051 | cache->max_entries*(sizeof(struct entry)), |
1052 | &ops); | ||
1019 | tree_get_filetypes(&filetypes, &filetypes_count); | 1053 | tree_get_filetypes(&filetypes, &filetypes_count); |
1020 | } | 1054 | } |
1021 | 1055 | ||
diff --git a/apps/tree.h b/apps/tree.h index c07b92f298..2b296050d3 100644 --- a/apps/tree.h +++ b/apps/tree.h | |||
@@ -26,26 +26,30 @@ | |||
26 | #include <file.h> | 26 | #include <file.h> |
27 | #include "icon.h" | 27 | #include "icon.h" |
28 | 28 | ||
29 | /* keep this struct compatible (total size and name member) | ||
30 | * with struct tagtree_entry (tagtree.h) */ | ||
29 | struct entry { | 31 | struct entry { |
30 | short attr; /* FAT attributes + file type flags */ | ||
31 | unsigned long time_write; /* Last write time */ | ||
32 | char *name; | 32 | char *name; |
33 | int attr; /* FAT attributes + file type flags */ | ||
34 | unsigned time_write; /* Last write time */ | ||
33 | }; | 35 | }; |
34 | 36 | ||
35 | |||
36 | #define BROWSE_SELECTONLY 0x0001 /* exit on selecting a file */ | 37 | #define BROWSE_SELECTONLY 0x0001 /* exit on selecting a file */ |
37 | #define BROWSE_NO_CONTEXT_MENU 0x0002 /* disable context menu */ | 38 | #define BROWSE_NO_CONTEXT_MENU 0x0002 /* disable context menu */ |
38 | #define BROWSE_SELECTED 0x0100 /* this bit is set if user selected item */ | 39 | #define BROWSE_SELECTED 0x0100 /* this bit is set if user selected item */ |
39 | 40 | ||
40 | struct tree_context; | 41 | struct tree_context; |
42 | |||
41 | struct tree_cache { | 43 | struct tree_cache { |
42 | /* A big buffer with plenty of entry structs, | 44 | /* A big buffer with plenty of entry structs, contains all files and dirs |
43 | * contains all files and dirs in the current | 45 | * in the current dir (with filters applied) |
44 | * dir (with filters applied) */ | 46 | * Note that they're buflib-allocated and can therefore possibly move |
45 | void* entries; | 47 | * They need to be locked if used around yielding functions */ |
46 | char* name_buffer; | 48 | int entries_handle; /* handle to the entry cache */ |
47 | int max_entries; /* Max entries in the cache */ | 49 | int name_buffer_handle; /* handle to the name cache */ |
48 | int name_buffer_size; /* in bytes */ | 50 | int max_entries; /* Max entries in the cache */ |
51 | int name_buffer_size; /* in bytes */ | ||
52 | volatile int lock_count; /* non-0 if buffers may not move */ | ||
49 | }; | 53 | }; |
50 | 54 | ||
51 | struct browse_context { | 55 | struct browse_context { |
@@ -95,6 +99,10 @@ struct tree_context { | |||
95 | struct browse_context *browse; | 99 | struct browse_context *browse; |
96 | }; | 100 | }; |
97 | 101 | ||
102 | /* | ||
103 | * Call one of the two below after yields since the entrys may move inbetween */ | ||
104 | struct entry* tree_get_entries(struct tree_context *t); | ||
105 | struct entry* tree_get_entry_at(struct tree_context *t, int index); | ||
98 | void tree_drawlists(void); | 106 | void tree_drawlists(void); |
99 | void tree_mem_init(void) INIT_ATTR; | 107 | void tree_mem_init(void) INIT_ATTR; |
100 | void tree_gui_init(void) INIT_ATTR; | 108 | void tree_gui_init(void) INIT_ATTR; |
@@ -108,6 +116,14 @@ void browse_context_init(struct browse_context *browse, | |||
108 | int rockbox_browse(struct browse_context *browse); | 116 | int rockbox_browse(struct browse_context *browse); |
109 | bool create_playlist(void); | 117 | bool create_playlist(void); |
110 | void resume_directory(const char *dir); | 118 | void resume_directory(const char *dir); |
119 | static inline void tree_lock_cache(struct tree_context *t) | ||
120 | { | ||
121 | t->cache.lock_count++; | ||
122 | } | ||
123 | static inline void tree_unlock_cache(struct tree_context *t) | ||
124 | { | ||
125 | t->cache.lock_count--; | ||
126 | } | ||
111 | #ifdef WIN32 | 127 | #ifdef WIN32 |
112 | /* it takes an int on windows */ | 128 | /* it takes an int on windows */ |
113 | #define getcwd_size_t int | 129 | #define getcwd_size_t int |
diff --git a/firmware/buflib.c b/firmware/buflib.c index 51cf86bf5b..880357ccf4 100644 --- a/firmware/buflib.c +++ b/firmware/buflib.c | |||
@@ -192,10 +192,6 @@ handle_table_shrink(struct buflib_context *ctx) | |||
192 | static bool | 192 | static bool |
193 | move_block(struct buflib_context* ctx, union buflib_data* block, int shift) | 193 | move_block(struct buflib_context* ctx, union buflib_data* block, int shift) |
194 | { | 194 | { |
195 | #if 1 /* moving temporarily disabled */ | ||
196 | (void)ctx;(void)block;(void)shift; | ||
197 | return false; | ||
198 | #else | ||
199 | char* new_start; | 195 | char* new_start; |
200 | union buflib_data *new_block, *tmp = block[1].handle; | 196 | union buflib_data *new_block, *tmp = block[1].handle; |
201 | struct buflib_callbacks *ops = block[2].ops; | 197 | struct buflib_callbacks *ops = block[2].ops; |
@@ -218,7 +214,6 @@ move_block(struct buflib_context* ctx, union buflib_data* block, int shift) | |||
218 | memmove(new_block, block, block->val * sizeof(union buflib_data)); | 214 | memmove(new_block, block, block->val * sizeof(union buflib_data)); |
219 | 215 | ||
220 | return true; | 216 | return true; |
221 | #endif | ||
222 | } | 217 | } |
223 | 218 | ||
224 | /* Compact allocations and handle table, adjusting handle pointers as needed. | 219 | /* Compact allocations and handle table, adjusting handle pointers as needed. |
diff --git a/firmware/common/dircache.c b/firmware/common/dircache.c index 334801ce57..6b2260def3 100644 --- a/firmware/common/dircache.c +++ b/firmware/common/dircache.c | |||
@@ -66,12 +66,21 @@ struct fdbind_queue { | |||
66 | int fd; | 66 | int fd; |
67 | }; | 67 | }; |
68 | 68 | ||
69 | /* Exported structures. */ | 69 | /* Unions with char to make pointer arithmetic simpler and avoid casting */ |
70 | struct dircache_entry { | 70 | struct dircache_entry { |
71 | struct dirinfo info; | 71 | struct dirinfo info; |
72 | struct dircache_entry *next; | 72 | union { |
73 | struct dircache_entry *up; | 73 | struct dircache_entry *next; |
74 | struct dircache_entry *down; | 74 | char* next_char; |
75 | }; | ||
76 | union { | ||
77 | struct dircache_entry *up; | ||
78 | char* up_char; | ||
79 | }; | ||
80 | union { | ||
81 | struct dircache_entry *down; | ||
82 | char* down_char; | ||
83 | }; | ||
75 | long startcluster; | 84 | long startcluster; |
76 | char *d_name; | 85 | char *d_name; |
77 | }; | 86 | }; |
@@ -130,6 +139,44 @@ static inline struct dircache_entry* get_entry(int id) | |||
130 | return &dircache_root[id]; | 139 | return &dircache_root[id]; |
131 | } | 140 | } |
132 | 141 | ||
142 | /* flag to make sure buffer doesn't move due to other allocs. | ||
143 | * this is set to true completely during dircache build */ | ||
144 | static bool dont_move = false; | ||
145 | static int dircache_handle; | ||
146 | static int move_callback(int handle, void* current, void* new) | ||
147 | { | ||
148 | (void)handle; | ||
149 | if (dont_move) | ||
150 | return BUFLIB_CB_CANNOT_MOVE; | ||
151 | |||
152 | /* relocate the cache */ | ||
153 | ptrdiff_t diff = new - current; | ||
154 | for(unsigned i = 0; i < entry_count; i++) | ||
155 | { | ||
156 | if (dircache_root[i].d_name) | ||
157 | dircache_root[i].d_name += diff; | ||
158 | if (dircache_root[i].next_char) | ||
159 | dircache_root[i].next_char += diff; | ||
160 | if (dircache_root[i].up_char) | ||
161 | dircache_root[i].up_char += diff; | ||
162 | if (dircache_root[i].down_char) | ||
163 | dircache_root[i].down_char += diff; | ||
164 | } | ||
165 | dircache_root = new; | ||
166 | |||
167 | d_names_start -= diff; | ||
168 | d_names_end -= diff; | ||
169 | dot -= diff; | ||
170 | dotdot -= diff; | ||
171 | |||
172 | return BUFLIB_CB_OK; | ||
173 | } | ||
174 | |||
175 | static struct buflib_callbacks ops = { | ||
176 | .move_callback = move_callback, | ||
177 | .shrink_callback = NULL, | ||
178 | }; | ||
179 | |||
133 | #ifdef HAVE_EEPROM_SETTINGS | 180 | #ifdef HAVE_EEPROM_SETTINGS |
134 | /** | 181 | /** |
135 | * Open the dircache file to save a snapshot on disk | 182 | * Open the dircache file to save a snapshot on disk |
@@ -573,10 +620,11 @@ int dircache_load(void) | |||
573 | } | 620 | } |
574 | 621 | ||
575 | allocated_size = maindata.size + DIRCACHE_RESERVE; | 622 | allocated_size = maindata.size + DIRCACHE_RESERVE; |
576 | int handle = core_alloc("dircache", allocated_size); | 623 | dircache_handle = core_alloc_ex("dircache", allocated_size, &ops); |
577 | dircache_root = core_get_data(handle); | 624 | /* block movement during upcoming I/O */ |
578 | /* needs to be struct-size aligned so that the pointer arithmetic below works */ | 625 | dont_move = true; |
579 | ALIGN_BUFFER(dircache_root, allocated_size, sizeof(struct dircache_entry)); | 626 | dircache_root = core_get_data(dircache_handle); |
627 | ALIGN_BUFFER(dircache_root, allocated_size, sizeof(struct dircache_entry*)); | ||
580 | entry_count = maindata.entry_count; | 628 | entry_count = maindata.entry_count; |
581 | appflags = maindata.appflags; | 629 | appflags = maindata.appflags; |
582 | 630 | ||
@@ -608,8 +656,9 @@ int dircache_load(void) | |||
608 | dotdot = dot - sizeof(".."); | 656 | dotdot = dot - sizeof(".."); |
609 | 657 | ||
610 | /* d_names are in reverse order, so the last entry points to the first string */ | 658 | /* d_names are in reverse order, so the last entry points to the first string */ |
611 | ptrdiff_t offset_d_names = maindata.d_names_start - d_names_start, | 659 | ptrdiff_t offset_d_names = maindata.d_names_start - d_names_start; |
612 | offset_entries = maindata.root_entry - dircache_root; | 660 | ptrdiff_t offset_entries = maindata.root_entry - dircache_root; |
661 | offset_entries *= sizeof(struct dircache_entry); /* make it bytes */ | ||
613 | 662 | ||
614 | /* offset_entries is less likely to differ, so check if it's 0 in the loop | 663 | /* offset_entries is less likely to differ, so check if it's 0 in the loop |
615 | * offset_d_names however is almost always non-zero, since dircache_save() | 664 | * offset_d_names however is almost always non-zero, since dircache_save() |
@@ -625,12 +674,12 @@ int dircache_load(void) | |||
625 | 674 | ||
626 | if (offset_entries == 0) | 675 | if (offset_entries == 0) |
627 | continue; | 676 | continue; |
628 | if (dircache_root[i].next) | 677 | if (dircache_root[i].next_char) |
629 | dircache_root[i].next -= offset_entries; | 678 | dircache_root[i].next_char -= offset_entries; |
630 | if (dircache_root[i].up) | 679 | if (dircache_root[i].up_char) |
631 | dircache_root[i].up -= offset_entries; | 680 | dircache_root[i].up_char -= offset_entries; |
632 | if (dircache_root[i].down) | 681 | if (dircache_root[i].down_char) |
633 | dircache_root[i].down -= offset_entries; | 682 | dircache_root[i].down_char -= offset_entries; |
634 | } | 683 | } |
635 | } | 684 | } |
636 | 685 | ||
@@ -640,6 +689,7 @@ int dircache_load(void) | |||
640 | logf("Done, %ld KiB used", dircache_size / 1024); | 689 | logf("Done, %ld KiB used", dircache_size / 1024); |
641 | dircache_initialized = true; | 690 | dircache_initialized = true; |
642 | memset(fd_bindings, 0, sizeof(fd_bindings)); | 691 | memset(fd_bindings, 0, sizeof(fd_bindings)); |
692 | dont_move = false; | ||
643 | 693 | ||
644 | return 0; | 694 | return 0; |
645 | } | 695 | } |
@@ -660,6 +710,7 @@ int dircache_save(void) | |||
660 | return -1; | 710 | return -1; |
661 | 711 | ||
662 | logf("Saving directory cache"); | 712 | logf("Saving directory cache"); |
713 | dont_move = true; | ||
663 | fd = open_dircache_file(O_WRONLY | O_CREAT | O_TRUNC, 0666); | 714 | fd = open_dircache_file(O_WRONLY | O_CREAT | O_TRUNC, 0666); |
664 | 715 | ||
665 | maindata.magic = DIRCACHE_MAGIC; | 716 | maindata.magic = DIRCACHE_MAGIC; |
@@ -698,7 +749,7 @@ int dircache_save(void) | |||
698 | return -4; | 749 | return -4; |
699 | } | 750 | } |
700 | 751 | ||
701 | 752 | dont_move = false; | |
702 | return 0; | 753 | return 0; |
703 | } | 754 | } |
704 | #endif /* HAVE_EEPROM_SETTINGS */ | 755 | #endif /* HAVE_EEPROM_SETTINGS */ |
@@ -720,6 +771,7 @@ static int dircache_do_rebuild(void) | |||
720 | /* reset dircache and alloc root entry */ | 771 | /* reset dircache and alloc root entry */ |
721 | entry_count = 0; | 772 | entry_count = 0; |
722 | root_entry = allocate_entry(); | 773 | root_entry = allocate_entry(); |
774 | dont_move = true; | ||
723 | 775 | ||
724 | #ifdef HAVE_MULTIVOLUME | 776 | #ifdef HAVE_MULTIVOLUME |
725 | append_position = root_entry; | 777 | append_position = root_entry; |
@@ -740,6 +792,7 @@ static int dircache_do_rebuild(void) | |||
740 | cpu_boost(false); | 792 | cpu_boost(false); |
741 | dircache_size = 0; | 793 | dircache_size = 0; |
742 | dircache_initializing = false; | 794 | dircache_initializing = false; |
795 | dont_move = false; | ||
743 | return -2; | 796 | return -2; |
744 | } | 797 | } |
745 | cpu_boost(false); | 798 | cpu_boost(false); |
@@ -765,7 +818,8 @@ static int dircache_do_rebuild(void) | |||
765 | if (allocated_size - dircache_size < DIRCACHE_RESERVE) | 818 | if (allocated_size - dircache_size < DIRCACHE_RESERVE) |
766 | reserve_used = DIRCACHE_RESERVE - (allocated_size - dircache_size); | 819 | reserve_used = DIRCACHE_RESERVE - (allocated_size - dircache_size); |
767 | } | 820 | } |
768 | 821 | ||
822 | dont_move = false; | ||
769 | return 1; | 823 | return 1; |
770 | } | 824 | } |
771 | 825 | ||
@@ -790,7 +844,8 @@ static void dircache_thread(void) | |||
790 | #endif | 844 | #endif |
791 | case DIRCACHE_BUILD: | 845 | case DIRCACHE_BUILD: |
792 | thread_enabled = true; | 846 | thread_enabled = true; |
793 | dircache_do_rebuild(); | 847 | if (dircache_do_rebuild() < 0) |
848 | core_free(dircache_handle); | ||
794 | thread_enabled = false; | 849 | thread_enabled = false; |
795 | break ; | 850 | break ; |
796 | 851 | ||
@@ -848,11 +903,10 @@ int dircache_build(int last_size) | |||
848 | 903 | ||
849 | if (last_size > DIRCACHE_RESERVE && last_size < DIRCACHE_LIMIT ) | 904 | if (last_size > DIRCACHE_RESERVE && last_size < DIRCACHE_LIMIT ) |
850 | { | 905 | { |
851 | int handle; | ||
852 | allocated_size = last_size + DIRCACHE_RESERVE; | 906 | allocated_size = last_size + DIRCACHE_RESERVE; |
853 | handle = core_alloc("dircache", allocated_size); | 907 | dircache_handle = core_alloc_ex("dircache", allocated_size, &ops); |
854 | dircache_root = core_get_data(handle); | 908 | dircache_root = core_get_data(dircache_handle); |
855 | ALIGN_BUFFER(dircache_root, allocated_size, sizeof(struct dircache_entry)); | 909 | ALIGN_BUFFER(dircache_root, allocated_size, sizeof(struct dircache_entry*)); |
856 | d_names_start = d_names_end = ((char*)dircache_root)+allocated_size-1; | 910 | d_names_start = d_names_end = ((char*)dircache_root)+allocated_size-1; |
857 | dircache_size = 0; | 911 | dircache_size = 0; |
858 | thread_enabled = true; | 912 | thread_enabled = true; |
@@ -869,10 +923,10 @@ int dircache_build(int last_size) | |||
869 | * after generation the buffer will be compacted with DIRCACHE_RESERVE | 923 | * after generation the buffer will be compacted with DIRCACHE_RESERVE |
870 | * free bytes inbetween */ | 924 | * free bytes inbetween */ |
871 | size_t got_size; | 925 | size_t got_size; |
872 | int handle = core_alloc_maximum("dircache", &got_size, NULL); | 926 | dircache_handle = core_alloc_maximum("dircache", &got_size, &ops); |
873 | char* buf = core_get_data(handle); | 927 | char* buf = core_get_data(dircache_handle); |
874 | dircache_root = (struct dircache_entry*)ALIGN_UP(buf, | 928 | dircache_root = (struct dircache_entry*)ALIGN_UP(buf, |
875 | sizeof(struct dircache_entry)); | 929 | sizeof(struct dircache_entry*)); |
876 | d_names_start = d_names_end = buf + got_size - 1; | 930 | d_names_start = d_names_end = buf + got_size - 1; |
877 | dircache_size = 0; | 931 | dircache_size = 0; |
878 | generate_dot_d_names(); | 932 | generate_dot_d_names(); |
@@ -909,11 +963,11 @@ int dircache_build(int last_size) | |||
909 | allocated_size = (d_names_end - buf); | 963 | allocated_size = (d_names_end - buf); |
910 | reserve_used = 0; | 964 | reserve_used = 0; |
911 | 965 | ||
912 | core_shrink(handle, dircache_root, allocated_size); | 966 | core_shrink(dircache_handle, dircache_root, allocated_size); |
913 | return res; | 967 | return res; |
914 | fail: | 968 | fail: |
915 | dircache_disable(); | 969 | dircache_disable(); |
916 | core_free(handle); | 970 | core_free(dircache_handle); |
917 | return res; | 971 | return res; |
918 | } | 972 | } |
919 | 973 | ||
@@ -928,7 +982,9 @@ void* dircache_steal_buffer(size_t *size) | |||
928 | *size = 0; | 982 | *size = 0; |
929 | return NULL; | 983 | return NULL; |
930 | } | 984 | } |
931 | 985 | ||
986 | /* since we give up the buffer (without freeing), it must not move anymore */ | ||
987 | dont_move = true; | ||
932 | *size = dircache_size + (DIRCACHE_RESERVE-reserve_used); | 988 | *size = dircache_size + (DIRCACHE_RESERVE-reserve_used); |
933 | 989 | ||
934 | return dircache_root; | 990 | return dircache_root; |
diff --git a/firmware/core_alloc.c b/firmware/core_alloc.c index 75dfc75b86..2250f5c664 100644 --- a/firmware/core_alloc.c +++ b/firmware/core_alloc.c | |||
@@ -6,7 +6,6 @@ | |||
6 | 6 | ||
7 | /* not static so it can be discovered by core_get_data() */ | 7 | /* not static so it can be discovered by core_get_data() */ |
8 | struct buflib_context core_ctx; | 8 | struct buflib_context core_ctx; |
9 | |||
10 | void core_allocator_init(void) | 9 | void core_allocator_init(void) |
11 | { | 10 | { |
12 | buffer_init(); | 11 | buffer_init(); |