Index: apps/playback.c =================================================================== --- apps/playback.c (revision 30942) +++ apps/playback.c (working copy) @@ -2557,6 +2557,9 @@ wipe_track_metadata(true); + /* reset when stopped to get maximum free contiguous memory */ + audio_reset_buffer(); + /* Go idle */ filling = STATE_IDLE; cancel_cpu_boost(); Index: firmware/buflib.c =================================================================== --- firmware/buflib.c (revision 30942) +++ 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 30942) +++ firmware/common/dircache.c (working copy) @@ -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); + /* need to recompact audio buffer */ + audio_hard_stop(); return res; fail: dircache_disable();