Index: apps/playback.c =================================================================== --- apps/playback.c (revision 30962) +++ apps/playback.c (working copy) @@ -354,6 +354,7 @@ static void buffer_event_rebuffer_callback(void *data); static void buffer_event_finished_callback(void *data); void audio_pcmbuf_sync_position(void); +static void audio_reset_buffer(void); /**************************************/ @@ -824,8 +825,10 @@ panicf("%s(): EOM (%zu > %zu)", __func__, allocsize, filebuflen); } +/* either a worker for buflib shrink if bool shrink set or + * realloc maximum memory with audio_reset_buffer() */ /* Buffer must not move. */ -static int shrink_callback(int handle, unsigned hints, void* start, size_t old_size) +static int audio_buffer_resize(int handle, unsigned hints, void* start, size_t old_size, bool shrink) { struct queue_event ev; static const long filter_list[][2] = @@ -840,7 +843,7 @@ size_t wanted_size = (hints & BUFLIB_SHRINK_SIZE_MASK); ssize_t size = (ssize_t)old_size - wanted_size; /* keep at least 256K for the buffering */ - if ((size - extradata_size) < 256*1024) + if (shrink && (size - extradata_size) < 256*1024) return BUFLIB_CB_CANNOT_SHRINK; @@ -873,21 +876,29 @@ #ifdef PLAYBACK_VOICE voice_stop(); #endif - /* we should be free to change the buffer now - * set final buffer size before calling audio_reset_buffer_noalloc() - * (now it's the total size, the call will subtract voice etc) */ - filebuflen = size; - switch (hints & BUFLIB_SHRINK_POS_MASK) + if(shrink) { - case BUFLIB_SHRINK_POS_BACK: - core_shrink(handle, start, size); - audio_reset_buffer_noalloc(start); - break; - case BUFLIB_SHRINK_POS_FRONT: - core_shrink(handle, start + wanted_size, size); - audio_reset_buffer_noalloc(start + wanted_size); - break; + /* we should be free to change the buffer now + * set final buffer size before calling audio_reset_buffer_noalloc() + * (now it's the total size, the call will subtract voice etc) */ + filebuflen = size; + switch (hints & BUFLIB_SHRINK_POS_MASK) + { + case BUFLIB_SHRINK_POS_BACK: + core_shrink(handle, start, size); + audio_reset_buffer_noalloc(start); + break; + case BUFLIB_SHRINK_POS_FRONT: + core_shrink(handle, start + wanted_size, size); + audio_reset_buffer_noalloc(start + wanted_size); + break; + } } + else + { + /* realloc with maximum */ + audio_reset_buffer(); + } if (playing || play_queued) { /* post, to make subsequent calls not break the resume position */ @@ -897,6 +908,17 @@ return BUFLIB_CB_OK; } +static int shrink_callback(int handle, unsigned hints, void* start, size_t old_size) +{ + return audio_buffer_resize(handle, hints, start, old_size, true); +} + +/* reallocate audio_file_buffer to defragment */ +void audio_realloc( void ) +{ + audio_buffer_resize(0, 0, NULL, 0, false); +} + static struct buflib_callbacks ops = { .move_callback = NULL, .shrink_callback = shrink_callback, Index: apps/playback.h =================================================================== --- apps/playback.h (revision 30962) +++ apps/playback.h (working copy) @@ -74,7 +74,7 @@ long audio_filebufused(void); void audio_pre_ff_rewind(void); void audio_skip(int direction); - +void audio_realloc(void); void audio_set_cuesheet(int enable); #ifdef HAVE_CROSSFADE void audio_set_crossfade(int enable); Index: firmware/buflib.c =================================================================== --- firmware/buflib.c (revision 30962) +++ firmware/buflib.c (working copy) @@ -90,6 +90,8 @@ #endif static union buflib_data* find_first_free(struct buflib_context *ctx); +static union buflib_data* find_best_free(struct buflib_context *ctx, + int size); static union buflib_data* find_block_before(struct buflib_context *ctx, union buflib_data* block, bool is_free); @@ -450,33 +452,25 @@ /* need to re-evaluate last before the loop because the last allocation * possibly made room in its front to fit this, so last would be wrong */ last = false; - for (block = find_first_free(ctx);;block += block_len) + block = find_best_free(ctx, size); + if(block == NULL) /* no suitable freed blocks */ { /* If the last used block extends all the way to the handle table, the * block "after" it doesn't have a header. Because of this, it's easier * to always find the end of allocation by saving a pointer, and always * calculate the free space at the end by comparing it to the - * last_handle pointer. - */ - if(block == ctx->alloc_end) - { - last = true; - block_len = ctx->last_handle - block; - if ((size_t)block_len < size) - block = NULL; - break; - } - block_len = block->val; - /* blocks with positive length are already allocated. */ - if(block_len > 0) - continue; - block_len = -block_len; - /* The search is first-fit, any fragmentation this causes will be - * handled at compaction. - */ - if ((size_t)block_len >= size) - break; + * last_handle pointer. */ + block = ctx->alloc_end; + last = true; + block_len = ctx->last_handle - block; + if (block_len < (int)size) + block = NULL; } + else + { + block_len = abs(block->val); + } + if (!block) { /* Try compacting if allocation failed */ @@ -529,6 +523,29 @@ return ret; } +/* find the best sized match for request, size in * + * multiples of sizeof(union buflib_data) */ +static union buflib_data* +find_best_free(struct buflib_context *ctx, int size) +{ + union buflib_data *tmp = ctx->buf_start; + union buflib_data *ret = NULL; + int ret_size = 0; + + while(tmp < ctx->alloc_end) + { + /* unallocated & big enough & none found so far or better match */ + if( (tmp->val < 0 && -tmp->val >= size) && + (( ret == NULL ) || ( -tmp->val < ret_size ))) + { + ret = tmp; + ret_size = -tmp->val; + } + tmp += abs( tmp->val ); + } + return ret; +} + /* Finds the free block before block, and returns NULL if it's not free */ static union buflib_data* find_block_before(struct buflib_context *ctx, union buflib_data* block, Index: firmware/common/dircache.c =================================================================== --- firmware/common/dircache.c (revision 30962) +++ firmware/common/dircache.c (working copy) @@ -47,8 +47,8 @@ #include "timefuncs.h" #endif #include "rbpaths.h" +#include "playback.h" - /* Queue commands. */ #define DIRCACHE_BUILD 1 #define DIRCACHE_STOP 2 @@ -169,6 +169,21 @@ d_names_end += diff; dot += diff; dotdot += diff; + for(unsigned i = 0; i < MAX_OPEN_FILES; i++) + { + if ((void*)fd_bindings[i] >= current && + (void*)fd_bindings[i] < current + allocated_size ) + { + fd_bindings[i] = (struct dircache_entry*)(((char *)fd_bindings[i]) + diff); + } + } +#ifdef HAVE_MULTIVOLUME + if ((void*)append_position >= current && + (void*)append_position < current + allocated_size ) + { + append_position = (struct dircache_entry*)(((char*)append_position) + diff); + } +#endif return BUFLIB_CB_OK; } @@ -941,8 +956,17 @@ if (available > DIRCACHE_LIMIT) available = DIRCACHE_LIMIT; dircache_handle = core_alloc_ex("dircache", available, &ops); - if (dircache_handle <= 0) - return -1; /* that was not successful, should try rebooting */ + /* try smaller allocations until success */ + while (dircache_handle <= 0) + { + if (available < 100000) + /* we just don't have the memory for this, give up + * and try on reboot */ + return -1; + available -= 50000; + dircache_handle = core_alloc_ex("dircache", available, &ops); + } + char* buf = core_get_data(dircache_handle); dircache_root = (struct dircache_entry*)ALIGN_UP(buf, sizeof(struct dircache_entry*)); @@ -983,6 +1007,8 @@ reserve_used = 0; core_shrink(dircache_handle, dircache_root, allocated_size); + audio_realloc(); + return res; fail: dircache_disable(); Index: uisimulator/common/io.c =================================================================== --- uisimulator/common/io.c (revision 30962) +++ uisimulator/common/io.c (working copy) @@ -398,6 +398,7 @@ int sim_open(const char *name, int o, ...) { + printf("open(%s)\n", name); int opts = rockbox2sim(o); int ret; if (num_openfiles >= MAX_OPEN_FILES)