diff options
-rw-r--r-- | apps/menus/main_menu.c | 4 | ||||
-rw-r--r-- | apps/mpeg.c | 29 | ||||
-rw-r--r-- | apps/playback.c | 19 | ||||
-rw-r--r-- | firmware/buflib.c | 68 | ||||
-rw-r--r-- | firmware/export/audio.h | 4 |
5 files changed, 104 insertions, 20 deletions
diff --git a/apps/menus/main_menu.c b/apps/menus/main_menu.c index 8053bf0cf8..bb8ea276fb 100644 --- a/apps/menus/main_menu.c +++ b/apps/menus/main_menu.c | |||
@@ -179,7 +179,7 @@ static const char* info_getname(int selected_item, void *data, | |||
179 | 179 | ||
180 | case INFO_BUFFER: /* buffer */ | 180 | case INFO_BUFFER: /* buffer */ |
181 | { | 181 | { |
182 | long kib = audio_buffer_available() / 1024; /* to KiB */ | 182 | long kib = audio_buffer_size() / 1024; /* to KiB */ |
183 | output_dyn_value(s1, sizeof(s1), kib, kbyte_units, true); | 183 | output_dyn_value(s1, sizeof(s1), kib, kbyte_units, true); |
184 | snprintf(buffer, buffer_len, "%s %s", str(LANG_BUFFER_STAT), s1); | 184 | snprintf(buffer, buffer_len, "%s %s", str(LANG_BUFFER_STAT), s1); |
185 | } | 185 | } |
@@ -261,7 +261,7 @@ static int info_speak_item(int selected_item, void * data) | |||
261 | case INFO_BUFFER: /* buffer */ | 261 | case INFO_BUFFER: /* buffer */ |
262 | { | 262 | { |
263 | talk_id(LANG_BUFFER_STAT, false); | 263 | talk_id(LANG_BUFFER_STAT, false); |
264 | long kib = audio_buffer_available() / 1024; /* to KiB */ | 264 | long kib = audio_buffer_size() / 1024; /* to KiB */ |
265 | output_dyn_value(NULL, 0, kib, kbyte_units, true); | 265 | output_dyn_value(NULL, 0, kib, kbyte_units, true); |
266 | break; | 266 | break; |
267 | } | 267 | } |
diff --git a/apps/mpeg.c b/apps/mpeg.c index ae33ccc1bf..698695b72d 100644 --- a/apps/mpeg.c +++ b/apps/mpeg.c | |||
@@ -151,7 +151,7 @@ static bool paused; /* playback is paused */ | |||
151 | static int audiobuf_handle; /* handle to the audio buffer */ | 151 | static int audiobuf_handle; /* handle to the audio buffer */ |
152 | static char* mpeg_audiobuf; /* poiunter to the audio buffer */ | 152 | static char* mpeg_audiobuf; /* poiunter to the audio buffer */ |
153 | static long audiobuflen; /* length of the audio buffer */ | 153 | static long audiobuflen; /* length of the audio buffer */ |
154 | 154 | #define AUDIO_BUFFER_RESERVE (256*1024) | |
155 | #ifdef SIMULATOR | 155 | #ifdef SIMULATOR |
156 | static char mpeg_stack[DEFAULT_STACK_SIZE]; | 156 | static char mpeg_stack[DEFAULT_STACK_SIZE]; |
157 | static struct mp3entry taginfo; | 157 | static struct mp3entry taginfo; |
@@ -515,9 +515,16 @@ static void audio_reset_buffer_noalloc(void* buf, size_t bufsize); | |||
515 | /* Buffer must not move. */ | 515 | /* Buffer must not move. */ |
516 | static int shrink_callback(int handle, unsigned hints, void* start, size_t old_size) | 516 | static int shrink_callback(int handle, unsigned hints, void* start, size_t old_size) |
517 | { | 517 | { |
518 | long offset = audio_current_track()->offset; | 518 | ssize_t extradata_size = old_size - audiobuflen; |
519 | bool playing = (audio_status() & AUDIO_STATUS_PLAY) == AUDIO_STATUS_PLAY; | 519 | /* check what buflib requests */ |
520 | size_t wanted_size = (hints & BUFLIB_SHRINK_SIZE_MASK); | ||
521 | ssize_t size = (ssize_t)old_size - wanted_size; | ||
522 | /* keep at least 256K for the buffering */ | ||
523 | if ((size - extradata_size) < AUDIO_BUFFER_RESERVE) | ||
524 | return BUFLIB_CB_CANNOT_SHRINK; | ||
520 | /* TODO: Do it without stopping playback, if possible */ | 525 | /* TODO: Do it without stopping playback, if possible */ |
526 | bool playing = (audio_status() & AUDIO_STATUS_PLAY) == AUDIO_STATUS_PLAY; | ||
527 | long offset = audio_current_track()->offset; | ||
521 | /* don't call audio_hard_stop() as it frees this handle */ | 528 | /* don't call audio_hard_stop() as it frees this handle */ |
522 | if (thread_self() == audio_thread_id) | 529 | if (thread_self() == audio_thread_id) |
523 | { /* inline case MPEG_STOP (audio_stop()) response | 530 | { /* inline case MPEG_STOP (audio_stop()) response |
@@ -528,9 +535,6 @@ static int shrink_callback(int handle, unsigned hints, void* start, size_t old_s | |||
528 | audio_stop(); | 535 | audio_stop(); |
529 | talk_buffer_steal(); /* we obtain control over the buffer */ | 536 | talk_buffer_steal(); /* we obtain control over the buffer */ |
530 | 537 | ||
531 | /* we should be free to change the buffer now */ | ||
532 | size_t wanted_size = (hints & BUFLIB_SHRINK_SIZE_MASK); | ||
533 | ssize_t size = (ssize_t)old_size - wanted_size; | ||
534 | switch (hints & BUFLIB_SHRINK_POS_MASK) | 538 | switch (hints & BUFLIB_SHRINK_POS_MASK) |
535 | { | 539 | { |
536 | case BUFLIB_SHRINK_POS_BACK: | 540 | case BUFLIB_SHRINK_POS_BACK: |
@@ -2742,11 +2746,20 @@ void audio_set_recording_options(struct audio_recording_options *options) | |||
2742 | #endif /* SIMULATOR */ | 2746 | #endif /* SIMULATOR */ |
2743 | #endif /* CONFIG_CODEC == MAS3587F */ | 2747 | #endif /* CONFIG_CODEC == MAS3587F */ |
2744 | 2748 | ||
2745 | size_t audio_buffer_available(void) | 2749 | size_t audio_buffer_size(void) |
2746 | { | 2750 | { |
2747 | if (audiobuf_handle > 0) | 2751 | if (audiobuf_handle > 0) |
2748 | return audiobuflen; | 2752 | return audiobuflen; |
2749 | return core_available(); | 2753 | return 0; |
2754 | } | ||
2755 | |||
2756 | size_t audio_buffer_available(void) | ||
2757 | { | ||
2758 | size_t size = 0; | ||
2759 | size_t core_size = core_available(); | ||
2760 | if (audiobuf_handle > 0) | ||
2761 | return audiobuflen - AUDIO_BUFFER_RESERVE - 128; | ||
2762 | return MAX(core_size, size); | ||
2750 | } | 2763 | } |
2751 | 2764 | ||
2752 | static void audio_reset_buffer_noalloc(void* buf, size_t bufsize) | 2765 | static void audio_reset_buffer_noalloc(void* buf, size_t bufsize) |
diff --git a/apps/playback.c b/apps/playback.c index a245091d91..d591998bec 100644 --- a/apps/playback.c +++ b/apps/playback.c | |||
@@ -733,13 +733,24 @@ static void scratch_mem_init(void *mem) | |||
733 | } | 733 | } |
734 | 734 | ||
735 | static int audiobuf_handle; | 735 | static int audiobuf_handle; |
736 | #define AUDIO_BUFFER_RESERVE (256*1024) | ||
736 | static size_t filebuflen; | 737 | static size_t filebuflen; |
737 | 738 | ||
739 | |||
740 | size_t audio_buffer_size(void) | ||
741 | { | ||
742 | if (audiobuf_handle > 0) | ||
743 | return filebuflen - AUDIO_BUFFER_RESERVE; | ||
744 | return 0; | ||
745 | } | ||
746 | |||
738 | size_t audio_buffer_available(void) | 747 | size_t audio_buffer_available(void) |
739 | { | 748 | { |
740 | if (audiobuf_handle > 0) /* if allocated return what we got */ | 749 | size_t size = 0; |
741 | return filebuflen; | 750 | size_t core_size = core_available(); |
742 | return core_available(); | 751 | if (audiobuf_handle > 0) /* if allocated return what we can give */ |
752 | size = filebuflen - AUDIO_BUFFER_RESERVE - 128; | ||
753 | return MAX(core_size, size); | ||
743 | } | 754 | } |
744 | 755 | ||
745 | /* Set up the audio buffer for playback | 756 | /* Set up the audio buffer for playback |
@@ -840,7 +851,7 @@ static int shrink_callback(int handle, unsigned hints, void* start, size_t old_s | |||
840 | size_t wanted_size = (hints & BUFLIB_SHRINK_SIZE_MASK); | 851 | size_t wanted_size = (hints & BUFLIB_SHRINK_SIZE_MASK); |
841 | ssize_t size = (ssize_t)old_size - wanted_size; | 852 | ssize_t size = (ssize_t)old_size - wanted_size; |
842 | /* keep at least 256K for the buffering */ | 853 | /* keep at least 256K for the buffering */ |
843 | if ((size - extradata_size) < 256*1024) | 854 | if ((size - extradata_size) < AUDIO_BUFFER_RESERVE) |
844 | return BUFLIB_CB_CANNOT_SHRINK; | 855 | return BUFLIB_CB_CANNOT_SHRINK; |
845 | 856 | ||
846 | 857 | ||
diff --git a/firmware/buflib.c b/firmware/buflib.c index 4ffd6cfce3..43fc4bd3de 100644 --- a/firmware/buflib.c +++ b/firmware/buflib.c | |||
@@ -89,6 +89,7 @@ | |||
89 | #define BDEBUGF(...) do { } while(0) | 89 | #define BDEBUGF(...) do { } while(0) |
90 | #endif | 90 | #endif |
91 | 91 | ||
92 | #define IS_MOVABLE(a) (!a[2].ops || a[2].ops->move_callback) | ||
92 | static union buflib_data* find_first_free(struct buflib_context *ctx); | 93 | static union buflib_data* find_first_free(struct buflib_context *ctx); |
93 | static union buflib_data* find_block_before(struct buflib_context *ctx, | 94 | static union buflib_data* find_block_before(struct buflib_context *ctx, |
94 | union buflib_data* block, | 95 | union buflib_data* block, |
@@ -198,7 +199,7 @@ move_block(struct buflib_context* ctx, union buflib_data* block, int shift) | |||
198 | char* new_start; | 199 | char* new_start; |
199 | union buflib_data *new_block, *tmp = block[1].handle; | 200 | union buflib_data *new_block, *tmp = block[1].handle; |
200 | struct buflib_callbacks *ops = block[2].ops; | 201 | struct buflib_callbacks *ops = block[2].ops; |
201 | if (ops && !ops->move_callback) | 202 | if (!IS_MOVABLE(block)) |
202 | return false; | 203 | return false; |
203 | 204 | ||
204 | int handle = ctx->handle_table - tmp; | 205 | int handle = ctx->handle_table - tmp; |
@@ -312,8 +313,10 @@ buflib_compact_and_shrink(struct buflib_context *ctx, unsigned shrink_hints) | |||
312 | result = buflib_compact(ctx); | 313 | result = buflib_compact(ctx); |
313 | if (!result) | 314 | if (!result) |
314 | { | 315 | { |
315 | union buflib_data* this; | 316 | union buflib_data *this, *before; |
316 | for(this = ctx->buf_start; this < ctx->alloc_end; this += abs(this->val)) | 317 | for(this = ctx->buf_start, before = this; |
318 | this < ctx->alloc_end; | ||
319 | before = this, this += abs(this->val)) | ||
317 | { | 320 | { |
318 | if (this->val > 0 && this[2].ops | 321 | if (this->val > 0 && this[2].ops |
319 | && this[2].ops->shrink_callback) | 322 | && this[2].ops->shrink_callback) |
@@ -322,6 +325,20 @@ buflib_compact_and_shrink(struct buflib_context *ctx, unsigned shrink_hints) | |||
322 | int handle = ctx->handle_table - this[1].handle; | 325 | int handle = ctx->handle_table - this[1].handle; |
323 | char* data = this[1].handle->alloc; | 326 | char* data = this[1].handle->alloc; |
324 | bool last = (this+this->val) == ctx->alloc_end; | 327 | bool last = (this+this->val) == ctx->alloc_end; |
328 | unsigned pos_hints = shrink_hints & BUFLIB_SHRINK_POS_MASK; | ||
329 | /* adjust what we ask for if there's free space in the front | ||
330 | * this isn't too unlikely assuming this block is | ||
331 | * shrinkable but not movable */ | ||
332 | if (pos_hints == BUFLIB_SHRINK_POS_FRONT | ||
333 | && before != this && before->val < 0) | ||
334 | { | ||
335 | size_t free_space = (-before->val) * sizeof(union buflib_data); | ||
336 | size_t wanted = shrink_hints & BUFLIB_SHRINK_SIZE_MASK; | ||
337 | if (wanted < free_space) /* no shrink needed? */ | ||
338 | continue; | ||
339 | wanted -= free_space; | ||
340 | shrink_hints = pos_hints | wanted; | ||
341 | } | ||
325 | ret = this[2].ops->shrink_callback(handle, shrink_hints, | 342 | ret = this[2].ops->shrink_callback(handle, shrink_hints, |
326 | data, (char*)(this+this->val)-data); | 343 | data, (char*)(this+this->val)-data); |
327 | result |= (ret == BUFLIB_CB_OK); | 344 | result |= (ret == BUFLIB_CB_OK); |
@@ -598,9 +615,8 @@ buflib_free(struct buflib_context *ctx, int handle_num) | |||
598 | return 0; /* unconditionally */ | 615 | return 0; /* unconditionally */ |
599 | } | 616 | } |
600 | 617 | ||
601 | /* Return the maximum allocatable memory in bytes */ | 618 | static size_t |
602 | size_t | 619 | free_space_at_end(struct buflib_context* ctx) |
603 | buflib_available(struct buflib_context* ctx) | ||
604 | { | 620 | { |
605 | /* subtract 5 elements for | 621 | /* subtract 5 elements for |
606 | * val, handle, name_len, ops and the handle table entry*/ | 622 | * val, handle, name_len, ops and the handle table entry*/ |
@@ -615,6 +631,46 @@ buflib_available(struct buflib_context* ctx) | |||
615 | return 0; | 631 | return 0; |
616 | } | 632 | } |
617 | 633 | ||
634 | /* Return the maximum allocatable memory in bytes */ | ||
635 | size_t | ||
636 | buflib_available(struct buflib_context* ctx) | ||
637 | { | ||
638 | union buflib_data *this; | ||
639 | size_t free_space = 0, max_free_space = 0; | ||
640 | |||
641 | /* make sure buffer is as contiguous as possible */ | ||
642 | if (!ctx->compact) | ||
643 | buflib_compact(ctx); | ||
644 | |||
645 | /* now look if there's free in holes */ | ||
646 | for(this = find_first_free(ctx); this < ctx->alloc_end; this += abs(this->val)) | ||
647 | { | ||
648 | if (this->val < 0) | ||
649 | { | ||
650 | free_space += -this->val; | ||
651 | continue; | ||
652 | } | ||
653 | /* an unmovable section resets the count as free space | ||
654 | * can't be contigous */ | ||
655 | if (!IS_MOVABLE(this)) | ||
656 | { | ||
657 | if (max_free_space < free_space) | ||
658 | max_free_space = free_space; | ||
659 | free_space = 0; | ||
660 | } | ||
661 | } | ||
662 | |||
663 | /* select the best */ | ||
664 | max_free_space = MAX(max_free_space, free_space); | ||
665 | max_free_space *= sizeof(union buflib_data); | ||
666 | max_free_space = MAX(max_free_space, free_space_at_end(ctx)); | ||
667 | |||
668 | if (max_free_space > 0) | ||
669 | return max_free_space; | ||
670 | else | ||
671 | return 0; | ||
672 | } | ||
673 | |||
618 | /* | 674 | /* |
619 | * Allocate all available (as returned by buflib_available()) memory and return | 675 | * Allocate all available (as returned by buflib_available()) memory and return |
620 | * a handle to it | 676 | * a handle to it |
diff --git a/firmware/export/audio.h b/firmware/export/audio.h index 57f3c24aae..5309ddd1d1 100644 --- a/firmware/export/audio.h +++ b/firmware/export/audio.h | |||
@@ -58,6 +58,10 @@ void audio_resume(void); | |||
58 | void audio_next(void); | 58 | void audio_next(void); |
59 | void audio_prev(void); | 59 | void audio_prev(void); |
60 | int audio_status(void); | 60 | int audio_status(void); |
61 | /* size of the audio buffer */ | ||
62 | size_t audio_buffer_size(void); | ||
63 | /* size of the buffer available for allocating memory from the audio buffer using core_*() | ||
64 | * returns core_available() if audio buffer is not allocated yet */ | ||
61 | size_t audio_buffer_available(void); | 65 | size_t audio_buffer_available(void); |
62 | void audio_ff_rewind(long newpos); | 66 | void audio_ff_rewind(long newpos); |
63 | void audio_flush_and_reload_tracks(void); | 67 | void audio_flush_and_reload_tracks(void); |