summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--apps/gui/skin_engine/skin_render.c2
-rw-r--r--apps/mpeg.c72
-rw-r--r--apps/playback.c176
-rw-r--r--apps/playback.h6
-rwxr-xr-xapps/playlist.c87
-rw-r--r--apps/plugin.c19
-rw-r--r--apps/plugin.h1
-rw-r--r--apps/radio/radioart.c40
-rw-r--r--apps/recorder/pcm_record.c20
-rw-r--r--apps/recorder/recording.c10
-rw-r--r--apps/talk.c761
-rw-r--r--apps/talk.h21
-rw-r--r--firmware/export/audio.h4
-rw-r--r--firmware/target/arm/pp/usb-fw-pp502x.c15
-rw-r--r--firmware/usbstack/usb_storage.c17
15 files changed, 670 insertions, 581 deletions
diff --git a/apps/gui/skin_engine/skin_render.c b/apps/gui/skin_engine/skin_render.c
index 28483cbc49..0d36711e91 100644
--- a/apps/gui/skin_engine/skin_render.c
+++ b/apps/gui/skin_engine/skin_render.c
@@ -280,13 +280,11 @@ static bool do_non_text_tags(struct gui_wps *gwps, struct skin_draw_info *info,
if (do_refresh && aa)
{
int handle = playback_current_aa_hid(data->playback_aa_slot);
-#if 0 /* FIXME: FS#12797*/
if (in_radio_screen() || (get_radio_status() != FMRADIO_OFF))
{
struct dim dim = {aa->width, aa->height};
handle = radio_get_art_hid(&dim);
}
-#endif
aa->draw_handle = handle;
}
break;
diff --git a/apps/mpeg.c b/apps/mpeg.c
index 2783a24085..5fd1bfdb82 100644
--- a/apps/mpeg.c
+++ b/apps/mpeg.c
@@ -527,7 +527,13 @@ static int shrink_callback(int handle, unsigned hints, void* start, size_t old_s
ssize_t size = (ssize_t)old_size - wanted_size;
/* keep at least 256K for the buffering */
if ((size - extradata_size) < AUDIO_BUFFER_RESERVE)
- return BUFLIB_CB_CANNOT_SHRINK;
+ {
+ /* check if buflib needs the memory really hard. if yes we give
+ * up playback for now, otherwise refuse to shrink to keep at least
+ * 256K for the buffering */
+ if ((hints & BUFLIB_SHRINK_POS_MASK) != BUFLIB_SHRINK_POS_MASK)
+ return BUFLIB_CB_CANNOT_SHRINK;
+ }
/* TODO: Do it without stopping playback, if possible */
bool playing = (audio_status() & AUDIO_STATUS_PLAY) == AUDIO_STATUS_PLAY;
long offset = audio_current_track()->offset;
@@ -539,7 +545,6 @@ static int shrink_callback(int handle, unsigned hints, void* start, size_t old_s
}
else
audio_stop();
- talk_buffer_steal(); /* we obtain control over the buffer */
switch (hints & BUFLIB_SHRINK_POS_MASK)
{
@@ -551,6 +556,12 @@ static int shrink_callback(int handle, unsigned hints, void* start, size_t old_s
core_shrink(handle, start + wanted_size, size);
audio_reset_buffer_noalloc(start + wanted_size, size);
break;
+ case BUFLIB_SHRINK_POS_MASK:
+ audiobuf_handle = core_free(audiobuf_handle);
+ mpeg_audiobuf = NULL;
+ talk_buffer_set_policy(TALK_BUFFER_DEFAULT);
+ playing = false;
+ break;
}
if (playing)
{ /* safe to call even from the audio thread (due to queue_post()) */
@@ -565,45 +576,6 @@ static struct buflib_callbacks ops = {
.shrink_callback = shrink_callback,
};
-static size_t audio_talkbuf_init(char *bufstart)
-{
- size_t ret = talkbuf_init(bufstart);
- if (ret > (size_t)audiobuflen) /* does the voice even fit? */
- {
- talk_buffer_steal();
- return 0;
- }
- return ret;
-}
-
-unsigned char * audio_get_buffer(bool talk_buf, size_t *buffer_size)
-{
- (void)talk_buf; /* always grab the voice buffer for now */
-
- if (audio_is_initialized)
- audio_hard_stop();
-
- if (!buffer_size) /* special case for talk_init() */
- return NULL;
-
- if (!audiobuf_handle)
- {
- size_t bufsize;
- /* audio_hard_stop() frees audiobuf, so re-aquire */
- audiobuf_handle = core_alloc_maximum("audiobuf", &bufsize, &ops);
- audiobuflen = bufsize;
- if (buffer_size)
- *buffer_size = audiobuflen;
- }
- mpeg_audiobuf = core_get_data(audiobuf_handle);
- /* tell talk about the new buffer, don't re-enable just yet because the
- * buffer is stolen */
- audio_talkbuf_init(mpeg_audiobuf);
-
- return mpeg_audiobuf;
-}
-
-
#ifndef SIMULATOR
/* Send callback events to notify about removing old tracks. */
static void generate_unbuffer_events(void)
@@ -2755,7 +2727,6 @@ size_t audio_buffer_available(void)
static void audio_reset_buffer_noalloc(void* buf, size_t bufsize)
{
- talk_buffer_steal(); /* will use the mp3 buffer */
mpeg_audiobuf = buf;
audiobuflen = bufsize;
if (global_settings.cuesheet)
@@ -2764,16 +2735,20 @@ static void audio_reset_buffer_noalloc(void* buf, size_t bufsize)
mpeg_audiobuf = SKIPBYTES(mpeg_audiobuf, sizeof(struct cuesheet));
audiobuflen -= sizeof(struct cuesheet);
}
- audio_talkbuf_init(mpeg_audiobuf);
}
static void audio_reset_buffer(void)
{
size_t bufsize = audiobuflen;
- /* alloc buffer if it's was never allocated or freed by audio_hard_stop() */
+ /* alloc buffer if it's was never allocated or freed by audio_hard_stop()
+ * because voice cannot be played during audio playback make
+ * talk.c give up all buffers and disable itself */
if (!audiobuf_handle)
+ {
+ talk_buffer_set_policy(TALK_BUFFER_LOOSE);
audiobuf_handle = core_alloc_maximum("audiobuf", &bufsize, &ops);
+ }
audio_reset_buffer_noalloc(core_get_data(audiobuf_handle), bufsize);
}
@@ -2818,6 +2793,8 @@ void audio_play(long offset)
void audio_stop(void)
{
+ if (audiobuf_handle <= 0)
+ return; /* nothing to do, must be hard-stopped already */
#ifndef SIMULATOR
mpeg_stop_done = false;
queue_post(&mpeg_queue, MPEG_STOP, 0);
@@ -2828,8 +2805,6 @@ void audio_stop(void)
is_playing = false;
playing = false;
#endif /* SIMULATOR */
- /* give voice our entire buffer */
- audio_talkbuf_init(mpeg_audiobuf);
}
/* dummy */
@@ -2840,13 +2815,12 @@ void audio_stop_recording(void)
void audio_hard_stop(void)
{
- audio_stop();
- /* tell voice we obtain the buffer before freeing */
- talk_buffer_steal();
if (audiobuf_handle > 0)
{
+ audio_stop();
audiobuf_handle = core_free(audiobuf_handle);
mpeg_audiobuf = NULL;
+ talk_buffer_set_policy(TALK_BUFFER_DEFAULT);
}
}
diff --git a/apps/playback.c b/apps/playback.c
index b240e95acd..5e234beb36 100644
--- a/apps/playback.c
+++ b/apps/playback.c
@@ -110,7 +110,6 @@ static enum audio_buffer_state
{
AUDIOBUF_STATE_TRASHED = -1, /* trashed; must be reset */
AUDIOBUF_STATE_INITIALIZED = 0, /* voice+audio OR audio-only */
- AUDIOBUF_STATE_VOICED_ONLY = 1, /* voice-only */
} buffer_state = AUDIOBUF_STATE_TRASHED; /* (A,O) */
/** Main state control **/
@@ -729,60 +728,42 @@ size_t audio_buffer_available(void)
/* Set up the audio buffer for playback
* filebuflen must be pre-initialized with the maximum size */
static void audio_reset_buffer_noalloc(
- void *filebuf, enum audio_buffer_state state)
+ void *filebuf)
{
/*
* Layout audio buffer as follows:
- * [[|TALK]|SCRATCH|BUFFERING|PCM]
+ * [|SCRATCH|BUFFERING|PCM]
*/
-
- /* see audio_get_recording_buffer if this is modified */
logf("%s()", __func__);
/* If the setup of anything allocated before the file buffer is
changed, do check the adjustments after the buffer_alloc call
as it will likely be affected and need sliding over */
-
- /* Initially set up file buffer as all space available */
size_t allocsize;
+ /* Subtract whatever the pcm buffer says it used plus the guard
+ buffer */
+ allocsize = pcmbuf_init(filebuf + filebuflen);
- /* Subtract whatever voice needs (we're called when promoting
- the state only) */
- allocsize = talkbuf_init(filebuf);
+ /* Make sure filebuflen is a pointer sized multiple after
+ adjustment */
allocsize = ALIGN_UP(allocsize, sizeof (intptr_t));
if (allocsize > filebuflen)
goto bufpanic;
- filebuf += allocsize;
filebuflen -= allocsize;
- if (state == AUDIOBUF_STATE_INITIALIZED)
- {
- /* Subtract whatever the pcm buffer says it used plus the guard
- buffer */
- allocsize = pcmbuf_init(filebuf + filebuflen);
-
- /* Make sure filebuflen is a pointer sized multiple after
- adjustment */
- allocsize = ALIGN_UP(allocsize, sizeof (intptr_t));
- if (allocsize > filebuflen)
- goto bufpanic;
-
- filebuflen -= allocsize;
-
- /* Scratch memory */
- allocsize = scratch_mem_size();
- if (allocsize > filebuflen)
- goto bufpanic;
+ /* Scratch memory */
+ allocsize = scratch_mem_size();
+ if (allocsize > filebuflen)
+ goto bufpanic;
- scratch_mem_init(filebuf);
- filebuf += allocsize;
- filebuflen -= allocsize;
+ scratch_mem_init(filebuf);
+ filebuf += allocsize;
+ filebuflen -= allocsize;
- buffering_reset(filebuf, filebuflen);
- }
+ buffering_reset(filebuf, filebuflen);
- buffer_state = state;
+ buffer_state = AUDIOBUF_STATE_INITIALIZED;
#if defined(ROCKBOX_HAS_LOGF) && defined(LOGF_ENABLE)
/* Make sure everything adds up - yes, some info is a bit redundant but
@@ -811,15 +792,24 @@ static int shrink_callback(int handle, unsigned hints, void* start, size_t old_s
/* codec messages */
{ Q_AUDIO_PLAY, Q_AUDIO_PLAY },
};
+ bool give_up = false;
/* filebuflen is, at this point, the buffering.c buffer size,
* i.e. the audiobuf except voice, scratch mem, pcm, ... */
ssize_t extradata_size = old_size - filebuflen;
/* check what buflib requests */
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) < AUDIO_BUFFER_RESERVE)
- return BUFLIB_CB_CANNOT_SHRINK;
+ {
+ /* check if buflib needs the memory really hard. if yes we give
+ * up playback for now, otherwise refuse to shrink to keep at least
+ * 256K for the buffering */
+ if ((hints & BUFLIB_SHRINK_POS_MASK) == BUFLIB_SHRINK_POS_MASK)
+ give_up = true;
+ else
+ return BUFLIB_CB_CANNOT_SHRINK;
+ }
/* TODO: Do it without stopping playback, if possible */
@@ -852,20 +842,26 @@ static int shrink_callback(int handle, unsigned hints, void* start, size_t old_s
#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()
+
+ /* we should be free to change the buffer now */
+ if (give_up)
+ {
+ buffer_state = AUDIOBUF_STATE_TRASHED;
+ audiobuf_handle = core_free(audiobuf_handle);
+ return BUFLIB_CB_OK;
+ }
+ /* 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, buffer_state);
+ 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,
- buffer_state);
+ audio_reset_buffer_noalloc(start + wanted_size);
break;
}
if (playing || play_queued)
@@ -882,7 +878,7 @@ static struct buflib_callbacks ops = {
.shrink_callback = shrink_callback,
};
-static void audio_reset_buffer(enum audio_buffer_state state)
+static void audio_reset_buffer(void)
{
if (audiobuf_handle > 0)
{
@@ -890,9 +886,13 @@ static void audio_reset_buffer(enum audio_buffer_state state)
audiobuf_handle = 0;
}
audiobuf_handle = core_alloc_maximum("audiobuf", &filebuflen, &ops);
- unsigned char *filebuf = core_get_data(audiobuf_handle);
- audio_reset_buffer_noalloc(filebuf, state);
+ if (audiobuf_handle > 0)
+ audio_reset_buffer_noalloc(core_get_data(audiobuf_handle));
+ else
+ /* someone is abusing core_alloc_maximum(). Fix this evil guy instead of
+ * trying to handle OOM without hope */
+ panicf("%s(): OOM!\n", __func__);
}
/* Set the buffer margin to begin rebuffering when 'seconds' from empty */
@@ -2033,7 +2033,7 @@ static int audio_fill_file_buffer(void)
if (buffer_state != AUDIOBUF_STATE_INITIALIZED ||
!pcmbuf_is_same_size())
{
- audio_reset_buffer(AUDIOBUF_STATE_INITIALIZED);
+ audio_reset_buffer();
}
logf("Starting buffer fill");
@@ -2464,7 +2464,7 @@ static void audio_start_playback(size_t offset, unsigned int flags)
/* Mark the buffer dirty - if not playing, it will be reset next
time */
if (buffer_state == AUDIOBUF_STATE_INITIALIZED)
- buffer_state = AUDIOBUF_STATE_VOICED_ONLY;
+ buffer_state = AUDIOBUF_STATE_TRASHED;
}
if (old_status != PLAY_STOPPED)
@@ -3511,88 +3511,6 @@ void audio_flush_and_reload_tracks(void)
audio_queue_post(Q_AUDIO_FLUSH, 0);
}
-/* Return the pointer to the main audio buffer, optionally preserving
- voicing */
-unsigned char * audio_get_buffer(bool talk_buf, size_t *buffer_size)
-{
- unsigned char *buf;
-
- if (audio_is_initialized && thread_self() != audio_thread_id)
- {
- audio_hard_stop();
- }
- /* else buffer_state will be AUDIOBUF_STATE_TRASHED at this point */
-
- if (buffer_size == NULL)
- {
- /* Special case for talk_init to use since it already knows it's
- trashed */
- buffer_state = AUDIOBUF_STATE_TRASHED;
- return NULL;
- }
-
- /* make sure buffer is freed and re-allocated to simplify code below
- * (audio_hard_stop() likely has done that already) */
- if (audiobuf_handle > 0)
- audiobuf_handle = core_free(audiobuf_handle);
-
- audiobuf_handle = core_alloc_maximum("audiobuf", &filebuflen, &ops);
- buf = core_get_data(audiobuf_handle);
-
- if (buffer_state == AUDIOBUF_STATE_INITIALIZED)
- buffering_reset(NULL, 0); /* mark buffer invalid */
-
- if (talk_buf || !talk_voice_required())
- {
- logf("get buffer: talk, audio");
- /* Ok to use everything from audiobuf - voice is loaded,
- the talk buffer is not needed because voice isn't being used, or
- could be AUDIOBUF_STATE_TRASHED already. If state is
- AUDIOBUF_STATE_VOICED_ONLY, no problem as long as memory isn't
- written without the caller knowing what's going on. Changing certain
- settings may move it to a worse condition but the memory in use by
- something else will remain undisturbed.
- */
- if (buffer_state != AUDIOBUF_STATE_TRASHED)
- {
- talk_buffer_steal();
- buffer_state = AUDIOBUF_STATE_TRASHED;
- }
- }
- else
- {
- logf("get buffer: audio");
- /* Safe to just return this if already AUDIOBUF_STATE_VOICED_ONLY or
- still AUDIOBUF_STATE_INITIALIZED */
- size_t talkbuf_size = talkbuf_init(buf);
- buf += talkbuf_size; /* Skip talk buffer */
- filebuflen -= talkbuf_size;
- buffer_state = AUDIOBUF_STATE_VOICED_ONLY;
- }
-
- *buffer_size = filebuflen;
- return buf;
-}
-
-/* Restore audio buffer to a particular state (promoting status) */
-bool audio_restore_playback(int type)
-{
- switch (type)
- {
- case AUDIO_WANT_PLAYBACK:
- if (buffer_state != AUDIOBUF_STATE_INITIALIZED)
- audio_reset_buffer(AUDIOBUF_STATE_INITIALIZED);
- return true;
- case AUDIO_WANT_VOICE:
- if (buffer_state == AUDIOBUF_STATE_TRASHED)
- audio_reset_buffer(AUDIOBUF_STATE_VOICED_ONLY);
- return true;
- default:
- return false;
- }
-}
-
-
/** --- Miscellaneous public interfaces --- **/
#ifdef HAVE_ALBUMART
diff --git a/apps/playback.h b/apps/playback.h
index f56bbfdff0..177768ded3 100644
--- a/apps/playback.h
+++ b/apps/playback.h
@@ -80,12 +80,6 @@ void audio_set_cuesheet(bool enable);
void audio_set_crossfade(int enable);
#endif
-enum
-{
- AUDIO_WANT_PLAYBACK = 0,
- AUDIO_WANT_VOICE,
-};
-bool audio_restore_playback(int type); /* Restores the audio buffer to handle the requested playback */
size_t audio_get_filebuflen(void);
unsigned int playback_status(void);
diff --git a/apps/playlist.c b/apps/playlist.c
index 35b7e35baa..3d930cf3f9 100755
--- a/apps/playlist.c
+++ b/apps/playlist.c
@@ -104,6 +104,7 @@
#include "rbunicode.h"
#include "root_menu.h"
#include "plugin.h" /* To borrow a temp buffer to rewrite a .m3u8 file */
+#include "panic.h"
#define PLAYLIST_CONTROL_FILE_VERSION 2
@@ -532,13 +533,6 @@ static int add_indices_to_playlist(struct playlist_info* playlist,
splash(0, ID2P(LANG_WAIT));
- if (!buffer)
- {
- /* use mp3 buffer for maximum load speed */
- audio_stop();
- buffer = audio_get_buffer(false, &buflen);
- }
-
store_index = true;
while(1)
@@ -2077,8 +2071,26 @@ int playlist_create(const char *dir, const char *file)
new_playlist(playlist, dir, file);
if (file)
- /* load the playlist file */
- add_indices_to_playlist(playlist, NULL, 0);
+ {
+ /* dummy ops with no callbacks, needed because by
+ * default buflib buffers can be moved around which must be avoided */
+ static struct buflib_callbacks dummy_ops;
+ int handle;
+ size_t buflen;
+ /* use mp3 buffer for maximum load speed */
+ handle = core_alloc_maximum("temp", &buflen, &dummy_ops);
+ if (handle > 0)
+ {
+ /* load the playlist file */
+ add_indices_to_playlist(playlist, core_get_data(handle), buflen);
+ core_free(handle);
+ }
+ else
+ {
+ /* should not happen */
+ panicf("%s(): OOM", __func__);
+ }
+ }
return 0;
}
@@ -2094,14 +2106,22 @@ int playlist_resume(void)
struct playlist_info* playlist = &current_playlist;
char *buffer;
size_t buflen;
+ int handle;
int nread;
int total_read = 0;
int control_file_size = 0;
bool first = true;
bool sorted = true;
+ int result = -1;
+ /* dummy ops with no callbacks, needed because by
+ * default buflib buffers can be moved around which must be avoided */
+ static struct buflib_callbacks dummy_ops;
/* use mp3 buffer for maximum load speed */
- buffer = (char *)audio_get_buffer(false, &buflen);
+ handle = core_alloc_maximum("temp", &buflen, &dummy_ops);
+ if (handle < 0)
+ panicf("%s(): OOM", __func__);
+ buffer = core_get_data(handle);
empty_playlist(playlist, true);
@@ -2110,7 +2130,7 @@ int playlist_resume(void)
if (playlist->control_fd < 0)
{
splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
- return -1;
+ goto out;
}
playlist->control_created = true;
@@ -2118,7 +2138,7 @@ int playlist_resume(void)
if (control_file_size <= 0)
{
splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
- return -1;
+ goto out;
}
/* read a small amount first to get the header */
@@ -2127,14 +2147,14 @@ int playlist_resume(void)
if(nread <= 0)
{
splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
- return -1;
+ goto out;
}
playlist->started = true;
while (1)
{
- int result = 0;
+ result = 0;
int count;
enum playlist_command current_command = PLAYLIST_COMMAND_COMMENT;
int last_newline = 0;
@@ -2195,7 +2215,10 @@ int playlist_resume(void)
version = atoi(str1);
if (version != PLAYLIST_CONTROL_FILE_VERSION)
- return -1;
+ {
+ result = -1;
+ goto out;
+ }
update_playlist_filename(playlist, str2, str3);
@@ -2204,7 +2227,7 @@ int playlist_resume(void)
/* NOTE: add_indices_to_playlist() overwrites the
audiobuf so we need to reload control file
data */
- add_indices_to_playlist(playlist, NULL, 0);
+ add_indices_to_playlist(playlist, buffer, buflen);
}
else if (str2[0] != '\0')
{
@@ -2242,7 +2265,10 @@ int playlist_resume(void)
buffer */
if (add_track_to_playlist(playlist, str3, position,
queue, total_read+(str3-buffer)) < 0)
- return -1;
+ {
+ result = -1;
+ goto out;
+ }
playlist->last_insert_pos = last_position;
@@ -2264,7 +2290,10 @@ int playlist_resume(void)
if (remove_track_from_playlist(playlist, position,
false) < 0)
- return -1;
+ {
+ result = -1;
+ goto out;
+ }
break;
}
@@ -2291,7 +2320,10 @@ int playlist_resume(void)
if (randomise_playlist(playlist, seed, false,
false) < 0)
- return -1;
+ {
+ result = -1;
+ goto out;
+ }
sorted = false;
break;
}
@@ -2308,7 +2340,10 @@ int playlist_resume(void)
playlist->first_index = atoi(str1);
if (sort_playlist(playlist, false, false) < 0)
- return -1;
+ {
+ result = -1;
+ goto out;
+ }
sorted = true;
break;
@@ -2421,13 +2456,14 @@ int playlist_resume(void)
if (result < 0)
{
splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_INVALID));
- return result;
+ goto out;
}
if (useraborted)
{
splash(HZ*2, ID2P(LANG_CANCEL));
- return -1;
+ result = -1;
+ goto out;
}
if (!newline || (exit_loop && count<nread))
{
@@ -2435,7 +2471,8 @@ int playlist_resume(void)
{
/* no newline at end of control file */
splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_INVALID));
- return -1;
+ result = -1;
+ goto out;
}
/* We didn't end on a newline or we exited loop prematurely.
@@ -2464,7 +2501,9 @@ int playlist_resume(void)
queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
#endif
- return 0;
+out:
+ core_free(handle);
+ return result;
}
/*
diff --git a/apps/plugin.c b/apps/plugin.c
index 18c49f4c27..d80e6a1e0e 100644
--- a/apps/plugin.c
+++ b/apps/plugin.c
@@ -801,6 +801,8 @@ static const struct plugin_api rockbox_api = {
the API gets incompatible */
};
+static int plugin_buffer_handle;
+
int plugin_load(const char* plugin, const void* parameter)
{
struct plugin_header *p_hdr;
@@ -815,6 +817,8 @@ int plugin_load(const char* plugin, const void* parameter)
}
lc_close(current_plugin_handle);
current_plugin_handle = pfn_tsr_exit = NULL;
+ if (plugin_buffer_handle > 0)
+ plugin_buffer_handle = core_free(plugin_buffer_handle);
}
splash(0, ID2P(LANG_WAIT));
@@ -878,6 +882,9 @@ int plugin_load(const char* plugin, const void* parameter)
touchscreen_set_mode(TOUCHSCREEN_BUTTON);
#endif
+ /* allow voice to back off if the plugin needs lots of memory */
+ talk_buffer_set_policy(TALK_BUFFER_LOOSE);
+
#ifdef HAVE_PLUGIN_CHECK_OPEN_CLOSE
open_files = 0;
#endif
@@ -891,8 +898,12 @@ int plugin_load(const char* plugin, const void* parameter)
{ /* close handle if plugin is no tsr one */
lc_close(current_plugin_handle);
current_plugin_handle = NULL;
+ if (plugin_buffer_handle > 0)
+ plugin_buffer_handle = core_free(plugin_buffer_handle);
}
+ talk_buffer_set_policy(TALK_BUFFER_DEFAULT);
+
/* Go back to the global setting in case the plugin changed it */
#ifdef HAVE_TOUCHSCREEN
touchscreen_set_mode(global_settings.touch_mode);
@@ -984,8 +995,12 @@ void* plugin_get_buffer(size_t *buffer_size)
*/
void* plugin_get_audio_buffer(size_t *buffer_size)
{
- audio_stop();
- return audio_get_buffer(true, buffer_size);
+ /* dummy ops with no callbacks, needed because by
+ * default buflib buffers can be moved around which must be avoided */
+ static struct buflib_callbacks dummy_ops;
+ plugin_buffer_handle = core_alloc_maximum("plugin audio buf", buffer_size,
+ &dummy_ops);
+ return core_get_data(plugin_buffer_handle);
}
/* The plugin wants to stay resident after leaving its main function, e.g.
diff --git a/apps/plugin.h b/apps/plugin.h
index af673b3afe..38d8889d9e 100644
--- a/apps/plugin.h
+++ b/apps/plugin.h
@@ -112,6 +112,7 @@ void* plugin_get_buffer(size_t *buffer_size);
#include "timefuncs.h"
#include "crc32.h"
#include "rbpaths.h"
+#include "core_alloc.h"
#ifdef HAVE_ALBUMART
#include "albumart.h"
diff --git a/apps/radio/radioart.c b/apps/radio/radioart.c
index 76697c6017..cab6aa29ce 100644
--- a/apps/radio/radioart.c
+++ b/apps/radio/radioart.c
@@ -32,6 +32,7 @@
#include "kernel.h"
#include "string-extra.h"
#include "filefuncs.h"
+#include "core_alloc.h"
#define MAX_RADIOART_IMAGES 10
struct radioart {
@@ -158,14 +159,49 @@ static void buffer_reset_handler(void *data)
(void)data;
}
+static int shrink_callback(int handle, unsigned hints, void* start, size_t old_size)
+{
+ (void)start;
+ (void)old_size;
+
+ ssize_t old_size_s = old_size;
+ size_t size_hint = (hints & BUFLIB_SHRINK_SIZE_MASK);
+ ssize_t wanted_size = old_size_s - size_hint;
+
+ if (wanted_size <= 0)
+ {
+ core_free(handle);
+ buffering_reset(NULL, 0);
+ }
+ else
+ {
+ if (hints & BUFLIB_SHRINK_POS_FRONT)
+ start += size_hint;
+
+ buffering_reset(start, wanted_size);
+ core_shrink(handle, start, wanted_size);
+ buf = start;
+
+ /* one-shot */
+ add_event(BUFFER_EVENT_BUFFER_RESET, true, buffer_reset_handler);
+ }
+
+ return BUFLIB_CB_OK;
+}
+
+static struct buflib_callbacks radioart_ops = {
+ .shrink_callback = shrink_callback,
+};
+
void radioart_init(bool entering_screen)
{
if (entering_screen)
{
/* grab control over buffering */
size_t bufsize;
- buf = audio_get_buffer(false, &bufsize);
- buffering_reset(buf, bufsize);
+ int handle = core_alloc_maximum("radioart", &bufsize, &radioart_ops);
+ buffering_reset(core_get_data(handle), bufsize);
+ buf = core_get_data(handle);
/* one-shot */
add_event(BUFFER_EVENT_BUFFER_RESET, true, buffer_reset_handler);
}
diff --git a/apps/recorder/pcm_record.c b/apps/recorder/pcm_record.c
index 2170e473bc..799c733948 100644
--- a/apps/recorder/pcm_record.c
+++ b/apps/recorder/pcm_record.c
@@ -24,6 +24,7 @@
#include "config.h"
#include "system.h"
#include "kernel.h"
+#include "panic.h"
#include "string-extra.h"
#include "pcm_record.h"
#include "codecs.h"
@@ -40,6 +41,8 @@
#include "spdif.h"
#endif
#include "audio_thread.h"
+#include "core_alloc.h"
+#include "talk.h"
/* Macros to enable logf for queues
logging on SYS_TIMEOUT can be disabled */
@@ -1402,11 +1405,22 @@ static void tally_prerecord_data(void)
/** Event handlers for recording thread **/
+static int pcmrec_handle;
/* Q_AUDIO_INIT_RECORDING */
static void on_init_recording(void)
{
send_event(RECORDING_EVENT_START, NULL);
- rec_buffer = audio_get_buffer(true, &rec_buffer_size);
+ /* dummy ops with no callbacks, needed because by
+ * default buflib buffers can be moved around which must be avoided
+ * FIXME: This buffer should play nicer and be shrinkable/movable */
+ static struct buflib_callbacks dummy_ops;
+ talk_buffer_set_policy(TALK_BUFFER_LOOSE);
+ pcmrec_handle = core_alloc_maximum("pcmrec", &rec_buffer_size, &dummy_ops);
+ if (pcmrec_handle)
+ /* someone is abusing core_alloc_maximum(). Fix this evil guy instead of
+ * trying to handle OOM without hope */
+ panicf("%s(): OOM\n", __func__);
+ rec_buffer = core_get_data(pcmrec_handle);
init_rec_buffers();
init_state();
pcm_init_recording();
@@ -1430,6 +1444,10 @@ static void on_close_recording(void)
audio_set_output_source(AUDIO_SRC_PLAYBACK);
pcm_apply_settings();
+ if (pcmrec_handle > 0)
+ pcmrec_handle = core_free(pcmrec_handle);
+ talk_buffer_set_policy(TALK_BUFFER_DEFAULT);
+
send_event(RECORDING_EVENT_STOP, NULL);
}
diff --git a/apps/recorder/recording.c b/apps/recorder/recording.c
index 1c3460fbd0..4a7399b01e 100644
--- a/apps/recorder/recording.c
+++ b/apps/recorder/recording.c
@@ -691,15 +691,8 @@ void rec_set_source(int source, unsigned flags)
void rec_set_recording_options(struct audio_recording_options *options)
{
-#if CONFIG_CODEC != SWCODEC
- if (global_settings.rec_prerecord_time)
- {
- talk_buffer_steal(); /* will use the mp3 buffer */
- }
-#else /* == SWCODEC */
rec_set_source(options->rec_source,
options->rec_source_flags | SRCF_RECORDING);
-#endif /* CONFIG_CODEC != SWCODEC */
audio_set_recording_options(options);
}
@@ -724,9 +717,6 @@ void rec_command(enum recording_command cmd)
/* steal mp3 buffer, create unique filename and start recording */
pm_reset_clipcount();
pm_activate_clipcount(true);
-#if CONFIG_CODEC != SWCODEC
- talk_buffer_steal(); /* we use the mp3 buffer */
-#endif
audio_record(rec_create_filename(path_buffer));
break;
case RECORDING_CMD_START_NEWFILE:
diff --git a/apps/talk.c b/apps/talk.c
index b94dcf18ee..baf854fce3 100644
--- a/apps/talk.c
+++ b/apps/talk.c
@@ -79,8 +79,8 @@ const char* const file_thumbnail_ext = ".talk";
#define LOADED_MASK 0x80000000 /* MSB */
-/* swcodec: cap p_thumnail to MAX_THUMNAIL_BUFSIZE since audio keeps playing
- * while voice
+/* swcodec: cap thumbnail buffer to MAX_THUMNAIL_BUFSIZE since audio keeps
+ * playing while voice
* hwcodec: just use whatever is left in the audiobuffer, music
* playback is impossible => no cap */
#if CONFIG_CODEC == SWCODEC
@@ -95,25 +95,17 @@ struct clip_entry /* one entry of the index table */
int size; /* size of the clip */
};
-struct voicefile /* file format of our voice file */
+struct voicefile_header /* file format of our voice file */
{
int version; /* version of the voicefile */
int target_id; /* the rockbox target the file was made for */
int table; /* offset to index table, (=header size) */
int id1_max; /* number of "normal" clips contained in above index */
int id2_max; /* number of "voice only" clips contained in above index */
- struct clip_entry index[]; /* followed by the index tables */
- /* and finally the mp3 clips, not visible here, bitswapped
- for SH based players */
+ /* The header is folled by the index tables (n*struct clip_entry),
+ * which is followed by the mp3/speex encoded clip data */
};
-struct queue_entry /* one entry of the internal queue */
-{
- unsigned char* buf;
- long len;
-};
-
-
/***************** Globals *****************/
#if (CONFIG_CODEC == SWCODEC && MEMORYSIZE <= 2) || defined(ONDIO_SERIES)
@@ -125,7 +117,6 @@ struct queue_entry /* one entry of the internal queue */
#endif
#ifdef TALK_PARTIAL_LOAD
-static unsigned char *clip_buffer;
static long max_clipsize; /* size of the biggest clip */
static long buffered_id[QUEUE_SIZE]; /* IDs of the talk clips */
static uint8_t clip_age[QUEUE_SIZE];
@@ -134,16 +125,13 @@ static uint8_t clip_age[QUEUE_SIZE];
#endif
#endif
-static char* voicebuf; /* root pointer to our buffer */
-static unsigned char* p_thumbnail = NULL; /* buffer for thumbnails */
/* Multiple thumbnails can be loaded back-to-back in this buffer. */
static volatile int thumbnail_buf_used SHAREDBSS_ATTR; /* length of data in
thumbnail buffer */
static long size_for_thumbnail; /* total thumbnail buffer size */
-static struct voicefile* p_voicefile; /* loaded voicefile */
+static struct voicefile_header voicefile; /* loaded voicefile */
static bool has_voicefile; /* a voicefile file is present */
static bool need_shutup; /* is there possibly any voice playing to be shutup */
-static struct queue_entry queue[QUEUE_SIZE]; /* queue of scheduled clips */
static bool force_enqueue_next; /* enqueue next utterance even if enqueue is false */
static int queue_write; /* write index of queue, by application */
static int queue_read; /* read index of queue, by ISR context */
@@ -158,18 +146,126 @@ static struct mutex queue_mutex SHAREDBSS_ATTR;
#endif /* CONFIG_CODEC */
static int sent; /* how many bytes handed over to playback, owned by ISR */
static unsigned char curr_hd[3]; /* current frame header, for re-sync */
-static int filehandle = -1; /* global, so we can keep the file open if needed */
-static unsigned char* p_silence; /* VOICE_PAUSE clip, used for termination */
-static long silence_len; /* length of the VOICE_PAUSE clip */
-static unsigned char* p_lastclip; /* address of latest clip, for silence add */
-static unsigned long voicefile_size = 0; /* size of the loaded voice file */
+static int silence_offset; /* VOICE_PAUSE clip, used for termination */
+static long silence_length; /* length of the VOICE_PAUSE clip */
+static unsigned long lastclip_offset; /* address of latest clip, for silence add */
static unsigned char last_lang[MAX_FILENAME+1]; /* name of last used lang file (in talk_init) */
static bool talk_initialized; /* true if talk_init has been called */
+static bool give_buffer_away; /* true if we should give the buffers away in shrink_callback if requested */
static int talk_temp_disable_count; /* if positive, temporarily disable voice UI (not saved) */
+ /* size of the loaded voice file
+ * offsets smaller than this denote a clip from teh voice file,
+ * offsets larger than this denote a thumbnail clip */
+static unsigned long voicefile_size;
+
+struct queue_entry /* one entry of the internal queue */
+{
+ int offset, length;
+};
+
+static struct queue_entry queue[QUEUE_SIZE]; /* queue of scheduled clips */
/***************** Private implementation *****************/
+static int thumb_handle;
+static int talk_handle, talk_handle_locked;
+
+#if CONFIG_CODEC != SWCODEC
+
+/* on HWCODEC only voice xor audio can be active at a time */
+static bool check_audio_status(void)
+{
+ if (audio_status()) /* busy, buffer in use */
+ return false;
+ /* ensure playback is given up on the buffer */
+ audio_hard_stop();
+ return true;
+}
+
+/* ISR (mp3_callback()) must not run during moving of the clip buffer,
+ * because the MAS may get out-of-sync */
+static void sync_callback(int handle, bool sync_on)
+{
+ (void) handle;
+ (void) sync_on;
+#if CONFIG_CPU == SH7034
+ if (sync_on)
+ CHCR3 &= ~0x0001; /* disable the DMA (and therefore the interrupt also) */
+ else
+ CHCR3 |= 0x0001; /* re-enable the DMA */
+#endif
+}
+#else
+#define check_audio_status() (true)
+#endif
+
+static int move_callback(int handle, void *current, void *new)
+{
+ (void)handle;(void)current;(void)new;
+ if (UNLIKELY(talk_handle_locked))
+ return BUFLIB_CB_CANNOT_MOVE;
+ return BUFLIB_CB_OK;
+}
+
+static int shrink_callback(int handle, unsigned hints, void *start, size_t old_size)
+{
+ (void)start;(void)old_size;
+ int *h;
+ if (handle == talk_handle)
+ h = &talk_handle;
+ else // if (handle == thumb_handle)
+ h = &thumb_handle;
+
+ if (LIKELY(!talk_handle_locked)
+ && give_buffer_away
+ && (hints & BUFLIB_SHRINK_POS_MASK) == BUFLIB_SHRINK_POS_MASK)
+ {
+ *h = core_free(handle);
+ return BUFLIB_CB_OK;
+ }
+ return BUFLIB_CB_CANNOT_SHRINK;
+}
+
+static struct buflib_callbacks talk_ops = {
+ .move_callback = move_callback,
+#if CONFIG_CODEC != SWCODEC
+ .sync_callback = sync_callback,
+#endif
+ .shrink_callback = shrink_callback,
+};
+
+
+static int index_handle, index_handle_locked;
+static int index_move_callback(int handle, void *current, void *new)
+{
+ (void)handle;(void)current;(void)new;
+ if (UNLIKELY(index_handle_locked))
+ return BUFLIB_CB_CANNOT_MOVE;
+ return BUFLIB_CB_OK;
+}
+
+static int index_shrink_callback(int handle, unsigned hints, void *start, size_t old_size)
+{
+ (void)start;(void)old_size;
+ if (LIKELY(!index_handle_locked)
+ && give_buffer_away
+ && (hints & BUFLIB_SHRINK_POS_MASK) == BUFLIB_SHRINK_POS_MASK)
+ {
+ index_handle = core_free(handle);
+ /* the clip buffer isn't usable without index table */
+ if (LIKELY(!talk_handle_locked))
+ talk_handle = core_free(talk_handle);
+ return BUFLIB_CB_OK;
+ }
+ return BUFLIB_CB_CANNOT_SHRINK;
+}
+
+static struct buflib_callbacks index_ops = {
+ .move_callback = index_move_callback,
+ .shrink_callback = index_shrink_callback,
+};
+
static int open_voicefile(void)
{
char buf[64];
@@ -188,38 +284,41 @@ static int open_voicefile(void)
/* fetch a clip from the voice file */
-static unsigned char* get_clip(long id, long* p_size)
+static int get_clip(long id, long* p_size)
{
- long clipsize;
- unsigned char* clipbuf;
-
+ int retval = -1;
+ struct clip_entry* clipbuf;
+ size_t clipsize;
+
if (id > VOICEONLY_DELIMITER)
{ /* voice-only entries use the second part of the table.
The first string comes after VOICEONLY_DELIMITER so we need to
substract VOICEONLY_DELIMITER + 1 */
id -= VOICEONLY_DELIMITER + 1;
- if (id >= p_voicefile->id2_max)
- return NULL; /* must be newer than we have */
- id += p_voicefile->id1_max; /* table 2 is behind table 1 */
+ if (id >= voicefile.id2_max)
+ return -1; /* must be newer than we have */
+ id += voicefile.id1_max; /* table 2 is behind table 1 */
}
else
{ /* normal use of the first table */
- if (id >= p_voicefile->id1_max)
- return NULL; /* must be newer than we have */
+ if (id >= voicefile.id1_max)
+ return -1; /* must be newer than we have */
}
-
- clipsize = p_voicefile->index[id].size;
+
+ clipbuf = core_get_data(index_handle);
+ clipsize = clipbuf[id].size;
if (clipsize == 0) /* clip not included in voicefile */
- return NULL;
+ return -1;
#ifndef TALK_PARTIAL_LOAD
- clipbuf = (unsigned char *) p_voicefile + p_voicefile->index[id].offset;
-#endif
+ retval = clipbuf[id].offset;
-#ifdef TALK_PARTIAL_LOAD
+#else
if (!(clipsize & LOADED_MASK))
{ /* clip needs loading */
- int idx = 0;
+ ssize_t ret;
+ int fd, idx = 0;
+ unsigned char *voicebuf;
if (id == VOICE_PAUSE) {
idx = QUEUE_SIZE; /* we keep VOICE_PAUSE loaded */
} else {
@@ -243,18 +342,29 @@ static unsigned char* get_clip(long id, long* p_size)
}
clip_age[idx] = 0; /* reset clip's age */
}
- clipbuf = clip_buffer + idx * max_clipsize;
+ retval = idx * max_clipsize;
+ fd = open_voicefile();
+ if (fd < 0)
+ return -1; /* open error */
+
+ talk_handle_locked++;
+ voicebuf = core_get_data(talk_handle);
+ clipbuf = core_get_data(index_handle);
+ lseek(fd, clipbuf[id].offset, SEEK_SET);
+ ret = read(fd, &voicebuf[retval], clipsize);
+ close(fd);
+ talk_handle_locked--;
- lseek(filehandle, p_voicefile->index[id].offset, SEEK_SET);
- if (read(filehandle, clipbuf, clipsize) != clipsize)
- return NULL; /* read error */
+ if (ret < 0 || clipsize != (size_t)ret)
+ return -1; /* read error */
- p_voicefile->index[id].size |= LOADED_MASK; /* mark as loaded */
+ clipbuf = core_get_data(index_handle);
+ clipbuf[id].size |= LOADED_MASK; /* mark as loaded */
if (id != VOICE_PAUSE) {
if (buffered_id[idx] >= 0) {
/* mark previously loaded clip as unloaded */
- p_voicefile->index[buffered_id[idx]].size &= ~LOADED_MASK;
+ clipbuf[buffered_id[idx]].size &= ~LOADED_MASK;
}
buffered_id[idx] = id;
}
@@ -262,14 +372,13 @@ static unsigned char* get_clip(long id, long* p_size)
else
{ /* clip is in memory already */
/* Find where it was loaded */
- clipbuf = clip_buffer;
if (id == VOICE_PAUSE) {
- clipbuf += QUEUE_SIZE * max_clipsize;
+ retval = QUEUE_SIZE * max_clipsize;
} else {
int idx;
for (idx=0; idx<QUEUE_SIZE; idx++)
if (buffered_id[idx] == id) {
- clipbuf += idx * max_clipsize;
+ retval = idx * max_clipsize;
clip_age[idx] = 0; /* reset clip's age */
break;
}
@@ -279,153 +388,200 @@ static unsigned char* get_clip(long id, long* p_size)
#endif /* TALK_PARTIAL_LOAD */
*p_size = clipsize;
- return clipbuf;
+ return retval;
}
-
-/* load the voice file into the mp3 buffer */
-static void load_voicefile(bool probe, char* buf, size_t bufsize)
+static bool load_index_table(int fd, const struct voicefile_header *hdr)
{
- union voicebuf {
- unsigned char* buf;
- struct voicefile* file;
- };
- union voicebuf voicebuf;
+ ssize_t ret;
+ struct clip_entry *buf;
- size_t load_size, alloc_size;
- ssize_t got_size;
-#ifdef ROCKBOX_LITTLE_ENDIAN
- int i;
-#endif
+ if (index_handle > 0) /* nothing to do? */
+ return true;
- if (!probe)
- filehandle = open_voicefile();
- if (filehandle < 0) /* failed to open */
- goto load_err;
+ ssize_t alloc_size = (hdr->id1_max + hdr->id2_max) * sizeof(struct clip_entry);
+ index_handle = core_alloc_ex("voice index", alloc_size, &index_ops);
+ if (index_handle < 0)
+ return false;
- voicebuf.buf = buf;
- if (!voicebuf.buf)
- goto load_err;
+ index_handle_locked++;
+ buf = core_get_data(index_handle);
+ ret = read(fd, buf, alloc_size);
-#ifdef TALK_PARTIAL_LOAD
- /* load only the header for now */
- load_size = sizeof(struct voicefile);
-#else
- /* load the entire file */
- load_size = filesize(filehandle);
+#ifndef TALK_PARTIAL_LOAD
+ int clips_offset, num_clips;
+ /* adjust the offsets of the clips, they are relative to the file
+ * TALK_PARTUAL_LOAD needs the file offset instead as it loads
+ * the clips later */
+ clips_offset = hdr->table;
+ num_clips = hdr->id1_max + hdr->id2_max;
+ clips_offset += num_clips * sizeof(struct clip_entry); /* skip index */
+#endif
+ if (ret == alloc_size)
+ for (int i = 0; i < hdr->id1_max + hdr->id2_max; i++)
+ {
+#ifdef ROCKBOX_LITTLE_ENDIAN
+ structec_convert(&buf[i], "ll", 1, true);
#endif
- if (load_size > bufsize) /* won't fit? */
- goto load_err;
+#ifndef TALK_PARTIAL_LOAD
+ buf[i].offset -= clips_offset;
+#endif
+ }
+
+ index_handle_locked--;
- got_size = read(filehandle, voicebuf.buf, load_size);
- if (got_size != (ssize_t)load_size /* failure */)
- goto load_err;
+ if (ret != alloc_size)
+ index_handle = core_free(index_handle);
- alloc_size = load_size;
+ return ret == alloc_size;
+}
+
+static bool load_header(int fd, struct voicefile_header *hdr)
+{
+ ssize_t got_size = read(fd, hdr, sizeof(*hdr));
+ if (got_size != sizeof(*hdr))
+ return false;
#ifdef ROCKBOX_LITTLE_ENDIAN
logf("Byte swapping voice file");
- structec_convert(voicebuf.buf, "lllll", 1, true);
+ structec_convert(&voicefile, "lllll", 1, true);
#endif
+ return true;
+}
- /* format check */
- if (voicebuf.file->table == sizeof(struct voicefile))
- {
- p_voicefile = voicebuf.file;
-
- if (p_voicefile->version != VOICE_VERSION ||
- p_voicefile->target_id != TARGET_ID)
- {
- logf("Incompatible voice file");
- goto load_err;
- }
- }
- else
- goto load_err;
+#ifndef TALK_PARTIAL_LOAD
+static bool load_data(int fd, ssize_t size_to_read)
+{
+ unsigned char *buf;
+ ssize_t ret;
-#ifdef TALK_PARTIAL_LOAD
- /* load the index table, now that we know its size from the header */
- load_size = (p_voicefile->id1_max + p_voicefile->id2_max)
- * sizeof(struct clip_entry);
+ if (size_to_read < 0)
+ return false;
- if (load_size > bufsize) /* won't fit? */
- goto load_err;
+ talk_handle = core_alloc_ex("voice data", size_to_read, &talk_ops);
+ if (talk_handle < 0)
+ return false;
- got_size = read(filehandle, &p_voicefile->index[0], load_size);
- if (got_size != (ssize_t)load_size) /* read error */
- goto load_err;
+ talk_handle_locked++;
+ buf = core_get_data(talk_handle);
+ ret = read(fd, buf, size_to_read);
+ talk_handle_locked--;
- alloc_size += load_size;
-#else
- close(filehandle);
- filehandle = -1;
-#endif /* TALK_PARTIAL_LOAD */
+ if (ret != size_to_read)
+ talk_handle = core_free(talk_handle);
-#ifdef ROCKBOX_LITTLE_ENDIAN
- for (i = 0; i < p_voicefile->id1_max + p_voicefile->id2_max; i++)
- structec_convert(&p_voicefile->index[i], "ll", 1, true);
+ return ret == size_to_read;
+}
#endif
-#ifdef TALK_PARTIAL_LOAD
- clip_buffer = (unsigned char *) p_voicefile + p_voicefile->table;
- unsigned clips = p_voicefile->id1_max + p_voicefile->id2_max;
- clip_buffer += clips * sizeof(struct clip_entry); /* skip index */
+static bool alloc_thumbnail_buf(void)
+{
+ int handle;
+ size_t size;
+ if (thumb_handle > 0)
+ return true; /* nothing to do? */
+#if CONFIG_CODEC == SWCODEC
+ /* try to allocate the max. first, and take whatever we can get if that
+ * fails */
+ size = MAX_THUMBNAIL_BUFSIZE;
+ handle = core_alloc_ex("voice thumb", MAX_THUMBNAIL_BUFSIZE, &talk_ops);
+ if (handle < 0)
+ {
+ size = core_allocatable();
+ handle = core_alloc_ex("voice thumb", size, &talk_ops);
+ }
+#else
+ /* on HWCODEC, just use the rest of the remaining buffer,
+ * normal playback cannot happen anyway */
+ handle = core_alloc_maximum("voice thumb", &size, &talk_ops);
#endif
- if (!probe) {
- /* make sure to have the silence clip, if available */
- p_silence = get_clip(VOICE_PAUSE, &silence_len);
+ thumb_handle = handle;
+ size_for_thumbnail = (handle > 0) ? size : 0;
+ return handle > 0;
+}
+
+/* load the voice file into the mp3 buffer */
+static bool load_voicefile_index(int fd)
+{
+ if (fd < 0) /* failed to open */
+ return false;
+
+ /* load the header first */
+ if (!load_header(fd, &voicefile))
+ return false;
+
+ /* format check */
+ if (voicefile.table == sizeof(struct voicefile_header))
+ {
+ if (voicefile.version == VOICE_VERSION &&
+ voicefile.target_id == TARGET_ID)
+ {
+ if (load_index_table(fd, &voicefile))
+ return true;
+ }
}
+ logf("Incompatible voice file");
+ return false;
+}
+
+static bool load_voicefile_data(int fd, size_t max_size)
+{
#ifdef TALK_PARTIAL_LOAD
- alloc_size += silence_len + QUEUE_SIZE;
+ /* just allocate, populate on an as-needed basis later */
+ talk_handle = core_alloc_ex("voice data", max_size, &talk_ops);
+ if (talk_handle < 0)
+ goto load_err_free;
+#else
+ size_t load_size, clips_size;
+ /* load the entire file into memory */
+ clips_size = (voicefile.id1_max+voicefile.id2_max) * sizeof(struct clip_entry);
+ load_size = max_size - voicefile.table - clips_size;
+ if (!load_data(fd, load_size))
+ goto load_err_free;
#endif
- if (alloc_size > bufsize)
- goto load_err;
+ /* make sure to have the silence clip, if available
+ * return value can be cached globally even for TALK_PARTIAL_LOAD because
+ * the VOICE_PAUSE clip is specially handled */
+ silence_offset = get_clip(VOICE_PAUSE, &silence_length);
- /* now move p_thumbnail behind the voice clip buffer */
- p_thumbnail = voicebuf.buf + alloc_size;
- p_thumbnail += (long)p_thumbnail % 2; /* 16-bit align */
- size_for_thumbnail = voicebuf.buf + bufsize - p_thumbnail;
-#if CONFIG_CODEC == SWCODEC
- size_for_thumbnail = MIN(size_for_thumbnail, MAX_THUMBNAIL_BUFSIZE);
-#endif
- if (size_for_thumbnail <= 0)
- p_thumbnail = NULL;
+ /* not an error if this fails here, might try again when the
+ * actual thumbnails are attempted to be played back */
+ alloc_thumbnail_buf();
- return;
-load_err:
- p_voicefile = NULL;
- has_voicefile = false; /* don't try again */
- if (filehandle >= 0)
- {
- close(filehandle);
- filehandle = -1;
- }
- return;
+ return true;
+
+load_err_free:
+ index_handle = core_free(index_handle);
+ return false;
}
/* called in ISR context (on HWCODEC) if mp3 data got consumed */
static void mp3_callback(const void** start, size_t* size)
{
- queue[queue_read].len -= sent; /* we completed this */
- queue[queue_read].buf += sent;
+ queue[queue_read].length -= sent; /* we completed this */
+ queue[queue_read].offset += sent;
- if (queue[queue_read].len > 0) /* current clip not finished? */
+ if (queue[queue_read].length > 0) /* current clip not finished? */
{ /* feed the next 64K-1 chunk */
+ int offset;
#if CONFIG_CODEC != SWCODEC
- sent = MIN(queue[queue_read].len, 0xFFFF);
+ sent = MIN(queue[queue_read].length, 0xFFFF);
#else
- sent = queue[queue_read].len;
+ sent = queue[queue_read].length;
#endif
- *start = queue[queue_read].buf;
+ offset = queue[queue_read].offset;
+ if ((unsigned long)offset >= voicefile_size)
+ *start = core_get_data(thumb_handle) + offset - voicefile_size;
+ else
+ *start = core_get_data(talk_handle) + offset;
*size = sent;
return;
}
talk_queue_lock();
- if(p_thumbnail
- && queue[queue_read].buf == p_thumbnail +thumbnail_buf_used)
+ if(thumb_handle && (unsigned long)queue[queue_read].offset == voicefile_size+thumbnail_buf_used)
thumbnail_buf_used = 0;
if (sent > 0) /* go to next entry */
{
@@ -436,24 +592,31 @@ re_check:
if (QUEUE_LEVEL != 0) /* queue is not empty? */
{ /* start next clip */
+ unsigned char *buf;
#if CONFIG_CODEC != SWCODEC
- sent = MIN(queue[queue_read].len, 0xFFFF);
+ sent = MIN(queue[queue_read].length, 0xFFFF);
#else
- sent = queue[queue_read].len;
+ sent = queue[queue_read].length;
#endif
- *start = p_lastclip = queue[queue_read].buf;
+ lastclip_offset = queue[queue_read].offset;
+ /* offsets larger than voicefile_size denote thumbnail clips */
+ if (lastclip_offset >= voicefile_size)
+ buf = core_get_data(thumb_handle) + lastclip_offset - voicefile_size;
+ else
+ buf = core_get_data(talk_handle) + lastclip_offset;
+ *start = buf;
*size = sent;
- curr_hd[0] = p_lastclip[1];
- curr_hd[1] = p_lastclip[2];
- curr_hd[2] = p_lastclip[3];
+ curr_hd[0] = buf[1];
+ curr_hd[1] = buf[2];
+ curr_hd[2] = buf[3];
}
- else if (p_silence != NULL /* silence clip available */
- && p_lastclip != p_silence /* previous clip wasn't silence */
- && !(p_lastclip >= p_thumbnail /* ..or thumbnail */
- && p_lastclip < p_thumbnail +size_for_thumbnail))
+ else if (silence_offset > 0 /* silence clip available */
+ && lastclip_offset != (unsigned long)silence_offset /* previous clip wasn't silence */
+ && !(lastclip_offset >= voicefile_size /* ..or thumbnail */
+ && lastclip_offset < voicefile_size +size_for_thumbnail))
{ /* add silence clip when queue runs empty playing a voice clip */
- queue[queue_write].buf = p_silence;
- queue[queue_write].len = silence_len;
+ queue[queue_write].offset = silence_offset;
+ queue[queue_write].length = silence_length;
queue_write = (queue_write + 1) & QUEUE_MASK;
goto re_check;
@@ -461,6 +624,7 @@ re_check:
else
{
*size = 0; /* end of data */
+ talk_handle_locked--;
}
talk_queue_unlock();
}
@@ -478,6 +642,8 @@ void talk_force_shutup(void)
unsigned char* pos;
unsigned char* search;
unsigned char* end;
+ int len;
+ unsigned clip_offset;
if (QUEUE_LEVEL == 0) /* has ended anyway */
return;
@@ -486,13 +652,17 @@ void talk_force_shutup(void)
#endif /* CONFIG_CPU == SH7034 */
/* search next frame boundary and continue up to there */
pos = search = mp3_get_pos();
- end = queue[queue_read].buf + queue[queue_read].len;
+ clip_offset = queue[queue_read].offset;
+ if (clip_offset >= voicefile_size)
+ end = core_get_data(thumb_handle) + clip_offset - voicefile_size;
+ else
+ end = core_get_data(talk_handle) + clip_offset;
+ len = queue[queue_read].length;
- if (pos >= queue[queue_read].buf
- && pos <= end) /* really our clip? */
+ if (pos >= end && pos <= (end+len)) /* really our clip? */
{ /* (for strange reasons this isn't nesessarily the case) */
/* find the next frame boundary */
- while (search < end) /* search the remaining data */
+ while (search < (end+len)) /* search the remaining data */
{
if (*search++ != 0xFF) /* quick search for frame sync byte */
continue; /* (this does the majority of the job) */
@@ -512,7 +682,7 @@ void talk_force_shutup(void)
sent = search-pos;
queue_write = (queue_read + 1) & QUEUE_MASK; /* will be empty after next callback */
- queue[queue_read].len = sent; /* current one ends after this */
+ queue[queue_read].length = sent; /* current one ends after this */
#if CONFIG_CPU == SH7034
DTCR3 = sent; /* let the DMA finish this frame */
@@ -528,6 +698,7 @@ void talk_force_shutup(void)
mp3_play_stop();
talk_queue_lock();
queue_write = queue_read = 0; /* reset the queue */
+ talk_handle_locked = MAX(talk_handle_locked-1, 0);
thumbnail_buf_used = 0;
talk_queue_unlock();
need_shutup = false;
@@ -541,8 +712,9 @@ void talk_shutup(void)
}
/* schedule a clip, at the end or discard the existing queue */
-static void queue_clip(unsigned char* buf, long size, bool enqueue)
+static void queue_clip(unsigned long clip_offset, long size, bool enqueue)
{
+ unsigned char *buf;
int queue_level;
if (!enqueue)
@@ -562,20 +734,25 @@ static void queue_clip(unsigned char* buf, long size, bool enqueue)
if (queue_level < QUEUE_SIZE - 1) /* space left? */
{
- queue[queue_write].buf = buf; /* populate an entry */
- queue[queue_write].len = size;
+ queue[queue_write].offset = clip_offset; /* populate an entry */
+ queue[queue_write].length = size;
queue_write = (queue_write + 1) & QUEUE_MASK;
}
talk_queue_unlock();
if (queue_level == 0)
{ /* queue was empty, we have to do the initial start */
- p_lastclip = buf;
+ lastclip_offset = clip_offset;
#if CONFIG_CODEC != SWCODEC
sent = MIN(size, 0xFFFF); /* DMA can do no more */
#else
sent = size;
#endif
+ talk_handle_locked++;
+ if (clip_offset >= voicefile_size)
+ buf = core_get_data(thumb_handle) + clip_offset - voicefile_size;
+ else
+ buf = core_get_data(talk_handle) + clip_offset;
mp3_play_data(buf, sent, mp3_callback);
curr_hd[0] = buf[1];
curr_hd[1] = buf[2];
@@ -594,53 +771,11 @@ static void queue_clip(unsigned char* buf, long size, bool enqueue)
return;
}
-static void alloc_thumbnail_buf(void)
-{
- /* use the audio buffer now, need to release before loading a voice */
- p_thumbnail = voicebuf;
-#if CONFIG_CODEC == SWCODEC
- size_for_thumbnail = MAX_THUMBNAIL_BUFSIZE;
-#endif
- thumbnail_buf_used = 0;
-}
-
-/* common code for talk_init() and talk_buffer_steal() */
-static void reset_state(void)
-{
- queue_write = queue_read = 0; /* reset the queue */
- p_voicefile = NULL; /* indicate no voicefile (trashed) */
- p_thumbnail = NULL; /* no thumbnails either */
-
-#ifdef TALK_PARTIAL_LOAD
- int i;
- for(i=0; i<QUEUE_SIZE; i++)
- buffered_id[i] = -1;
-#endif
-
- p_silence = NULL; /* pause clip not accessible */
- voicebuf = NULL; /* voice buffer is gone */
-}
-
-#if CONFIG_CODEC == SWCODEC
-static bool restore_state(void)
-{
- if (!voicebuf)
- {
- size_t size;
- audio_restore_playback(AUDIO_WANT_VOICE);
- voicebuf = audio_get_buffer(true, &size);
- audio_get_buffer(false, &size);
- }
-
- return !!voicebuf;
-}
-#endif /* CONFIG_CODEC == SWCODEC */
-
-
/***************** Public implementation *****************/
void talk_init(void)
{
+ int filehandle;
talk_temp_disable_count = 0;
if (talk_initialized && !strcasecmp(last_lang, global_settings.lang_file))
{
@@ -648,14 +783,6 @@ void talk_init(void)
return;
}
-#if defined(TALK_PROGRESSIVE_LOAD) || defined(TALK_PARTIAL_LOAD)
- if (filehandle >= 0)
- {
- close(filehandle);
- filehandle = -1;
- }
-#endif
-
#if CONFIG_CODEC == SWCODEC
if(!talk_initialized)
mutex_init(&queue_mutex);
@@ -665,138 +792,118 @@ void talk_init(void)
strlcpy((char *)last_lang, (char *)global_settings.lang_file,
MAX_FILENAME);
+ /* reset some states */
+ queue_write = queue_read = 0; /* reset the queue */
+ memset(&voicefile, 0, sizeof(voicefile));
+
+#ifdef TALK_PARTIAL_LOAD
+ for(int i=0; i<QUEUE_SIZE; i++)
+ buffered_id[i] = -1;
+#endif
+
+ silence_offset = -1; /* pause clip not accessible */
+ voicefile_size = has_voicefile = 0;
+ /* need to free these as their size depends on the voice file, and
+ * this function is called when the talk voice file changes */
+ if (index_handle > 0) index_handle = core_free(index_handle);
+ if (talk_handle > 0) talk_handle = core_free(talk_handle);
+ /* don't free thumb handle, it doesn't depend on the actual voice file
+ * and so we can re-use it if it's already allocated in any event */
+
filehandle = open_voicefile();
- if (filehandle < 0) {
- has_voicefile = false;
- voicefile_size = 0;
+ if (filehandle < 0)
return;
- }
- voicefile_size = filesize(filehandle);
-
- audio_get_buffer(false, NULL); /* Must tell audio to reinitialize */
- reset_state(); /* use this for most of our inits */
+ if (!load_voicefile_index(filehandle))
+ goto out;
#ifdef TALK_PARTIAL_LOAD
- size_t bufsize;
- char* buf = plugin_get_buffer(&bufsize);
- /* we won't load the full file, we only need the index */
- load_voicefile(true, buf, bufsize);
- if (!p_voicefile)
- return;
-
- unsigned clips = p_voicefile->id1_max + p_voicefile->id2_max;
- unsigned i;
+ /* TALK_PARTIAL_LOAD loads the actual clip data later, and not all
+ * at once */
+ unsigned num_clips = voicefile.id1_max + voicefile.id2_max;
+ struct clip_entry *clips = core_get_data(index_handle);
int silence_size = 0;
- for(i=0; i<clips; i++) {
- int size = p_voicefile->index[i].size;
+ for(unsigned i=0; i<num_clips; i++) {
+ int size = clips[i].size;
if (size > max_clipsize)
max_clipsize = size;
if (i == VOICE_PAUSE)
silence_size = size;
}
- voicefile_size = p_voicefile->table + clips * sizeof(struct clip_entry);
+ voicefile_size = voicefile.table + num_clips * sizeof(struct clip_entry);
voicefile_size += max_clipsize * QUEUE_SIZE + silence_size;
- p_voicefile = NULL; /* Don't pretend we can load talk clips just yet */
-#endif
-
/* test if we can open and if it fits in the audiobuffer */
size_t audiobufsz = audio_buffer_available();
- if (voicefile_size <= audiobufsz) {
- has_voicefile = true;
- } else {
- has_voicefile = false;
+ has_voicefile = audiobufsz >= voicefile_size;
+
+#else
+ /* load the compressed clip data into memory, in its entirety */
+ voicefile_size = filesize(filehandle);
+ if (!load_voicefile_data(filehandle, voicefile_size))
+ {
voicefile_size = 0;
+ goto out;
}
-
- close(filehandle); /* close again, this was just to detect presence */
- filehandle = -1;
+ has_voicefile = true;
+#endif
#if CONFIG_CODEC == SWCODEC
/* Safe to init voice playback engine now since we now know if talk is
required or not */
voice_thread_init();
#endif
+
+out:
+ close(filehandle); /* close again, this was just to detect presence */
+ filehandle = -1;
}
#if CONFIG_CODEC == SWCODEC
/* return if a voice codec is required or not */
bool talk_voice_required(void)
{
- return (voicefile_size != 0) /* Voice file is available */
+ return (has_voicefile) /* Voice file is available */
|| (global_settings.talk_dir_clip) /* Thumbnail clips are required */
|| (global_settings.talk_file_clip);
}
#endif
-/* return size of voice file */
-static size_t talk_get_buffer_size(void)
-{
-#if CONFIG_CODEC == SWCODEC
- return voicefile_size + MAX_THUMBNAIL_BUFSIZE;
-#else
- return audio_buffer_available();
-#endif
-}
-
-/* Sets the buffer for the voicefile and returns how many bytes of this
- * buffer we will use for the voicefile */
-size_t talkbuf_init(char *bufstart)
-{
- bool changed = voicebuf != bufstart;
-
- if (changed) /* must reload voice file */
- reset_state();
-
- if (bufstart)
- voicebuf = bufstart;
-
- return talk_get_buffer_size();
-}
-
/* somebody else claims the mp3 buffer, e.g. for regular play/record */
-void talk_buffer_steal(void)
+void talk_buffer_set_policy(int policy)
{
-#if CONFIG_CODEC != SWCODEC
- mp3_play_stop();
-#endif
-#if defined(TALK_PROGRESSIVE_LOAD) || defined(TALK_PARTIAL_LOAD)
- if (filehandle >= 0)
+ switch(policy)
{
- close(filehandle);
- filehandle = -1;
+ case TALK_BUFFER_DEFAULT:
+ case TALK_BUFFER_HOLD: give_buffer_away = false; break;
+ case TALK_BUFFER_LOOSE: give_buffer_away = true; break;
+ default: DEBUGF("Ignoring unknown policy\n"); break;
}
-#endif
- reset_state();
}
/* play a voice ID from voicefile */
int talk_id(int32_t id, bool enqueue)
{
+ int clip;
long clipsize;
- unsigned char* clipbuf;
int32_t unit;
int decimals;
if (talk_temp_disable_count > 0)
return -1; /* talking has been disabled */
-#if CONFIG_CODEC == SWCODEC
- /* If talk buffer was stolen, it must be restored for voicefile's sake */
- if (!restore_state())
- return -1; /* cannot get any space */
-#else
- if (audio_status()) /* busy, buffer in use */
+ if (!check_audio_status())
return -1;
-#endif
-
- if (p_voicefile == NULL && has_voicefile) /* reload needed? */
- load_voicefile(false, voicebuf, talk_get_buffer_size());
- if (p_voicefile == NULL) /* still no voices? */
- return -1;
+ if (has_voicefile && (talk_handle <= 0 || index_handle <= 0)) /* reload needed? */
+ {
+ int fd = open_voicefile();
+ if (fd < 0
+ || !load_voicefile_index(fd)
+ || !load_voicefile_data(fd, voicefile_size))
+ return -1;
+ }
if (id == -1) /* -1 is an indication for silence */
return -1;
@@ -814,8 +921,8 @@ int talk_id(int32_t id, bool enqueue)
return 0; /* and stop, end of special case */
}
- clipbuf = get_clip(id, &clipsize);
- if (clipbuf == NULL)
+ clip = get_clip(id, &clipsize);
+ if (clip < 0)
return -1; /* not present */
#ifdef LOGF_ENABLE
@@ -825,7 +932,7 @@ int talk_id(int32_t id, bool enqueue)
logf("\ntalk_id: Say '%s'\n", str(id));
#endif
- queue_clip(clipbuf, clipsize, enqueue);
+ queue_clip(clip, clipsize, enqueue);
return 0;
}
@@ -859,23 +966,18 @@ static int _talk_file(const char* filename,
int fd;
int size;
int thumb_used;
+ char *buf;
#if CONFIG_CODEC != SWCODEC
struct mp3entry info;
#endif
if (talk_temp_disable_count > 0)
return -1; /* talking has been disabled */
-#if CONFIG_CODEC == SWCODEC
- /* If talk buffer was stolen, it must be restored for thumbnail's sake */
- if (!restore_state())
- return -1; /* cannot get any space */
-#else
- if (audio_status()) /* busy, buffer in use */
- return -1;
-#endif
+ if (!check_audio_status())
+ return -1;
- if (p_thumbnail == NULL || size_for_thumbnail <= 0)
- alloc_thumbnail_buf();
+ if (!alloc_thumbnail_buf())
+ return -1;
#if CONFIG_CODEC != SWCODEC
if(mp3info(&info, filename)) /* use this to find real start */
@@ -905,8 +1007,10 @@ static int _talk_file(const char* filename,
lseek(fd, info.first_frame_offset, SEEK_SET); /* behind ID data */
#endif
- size = read(fd, p_thumbnail +thumb_used,
- size_for_thumbnail -thumb_used);
+ talk_handle_locked++;
+ buf = core_get_data(thumb_handle);
+ size = read(fd, buf+thumb_used, size_for_thumbnail - thumb_used);
+ talk_handle_locked--;
close(fd);
/* ToDo: find audio, skip ID headers and trailers */
@@ -914,7 +1018,8 @@ static int _talk_file(const char* filename,
if (size > 0) /* Don't play missing clips */
{
#if CONFIG_CODEC != SWCODEC && !defined(SIMULATOR)
- bitswap(p_thumbnail, size);
+ /* bitswap doesnt yield() */
+ bitswap(core_get_data(thumb_handle), size);
#endif
if(prefix_ids)
/* prefix thumbnail by speaking these ids, but only now
@@ -922,9 +1027,9 @@ static int _talk_file(const char* filename,
spoken. */
talk_idarray(prefix_ids, true);
talk_queue_lock();
- thumbnail_buf_used = thumb_used +size;
+ thumbnail_buf_used = thumb_used + size;
talk_queue_unlock();
- queue_clip(p_thumbnail +thumb_used, size, true);
+ queue_clip(voicefile_size + thumb_used, size, true);
}
return size;
@@ -1012,10 +1117,8 @@ int talk_number(long n, bool enqueue)
if (talk_temp_disable_count > 0)
return -1; /* talking has been disabled */
-#if CONFIG_CODEC != SWCODEC
- if (audio_status()) /* busy, buffer in use */
- return -1;
-#endif
+ if (!check_audio_status())
+ return -1;
if (!enqueue)
talk_shutup(); /* cut off all the pending stuff */
@@ -1160,10 +1263,8 @@ int talk_value_decimal(long n, int unit, int decimals, bool enqueue)
if (talk_temp_disable_count > 0)
return -1; /* talking has been disabled */
-#if CONFIG_CODEC != SWCODEC
- if (audio_status()) /* busy, buffer in use */
- return -1;
-#endif
+ if (!check_audio_status())
+ return -1;
/* special case for time duration */
if (unit == UNIT_TIME)
@@ -1217,10 +1318,8 @@ int talk_spell(const char* spell, bool enqueue)
if (talk_temp_disable_count > 0)
return -1; /* talking has been disabled */
-#if CONFIG_CODEC != SWCODEC
- if (audio_status()) /* busy, buffer in use */
- return -1;
-#endif
+ if (!check_audio_status())
+ return -1;
if (!enqueue)
talk_shutup(); /* cut off all the pending stuff */
diff --git a/apps/talk.h b/apps/talk.h
index 55e7208f1d..28be71884f 100644
--- a/apps/talk.h
+++ b/apps/talk.h
@@ -71,6 +71,26 @@ enum {
/* convenience macro to have both virtual pointer and ID as arguments */
#define STR(id) ID2P(id), id
+/* Policy values for how hard to try to keep the talk/voice buffers.
+ * Affects how genereous talk.c is when it's asked for memory in
+ * shrink_callbacks().
+ *
+ * I.e. setting the policy to TALK_BUFFER_LOOSE, it will happily give its
+ * entire bufer away if asked for, e.g. due to a another module
+ * calling core_alloc_maximum(), TALK_BUFFER_HOLD on the other hand will
+ * make it keep the buffers so that a call to core_alloc_maximum() does not
+ * stop the speech-interface.
+ */
+enum talk_buffer_policies {
+ TALK_BUFFER_DEFAULT,
+ TALK_BUFFER_LOOSE,
+ TALK_BUFFER_HOLD,
+};
+
+/* This sets the actual policy. Call this before core_alloc_maximum() to
+ * get the desired outcome */
+void talk_buffer_set_policy(int policy);
+
/* publish these strings, so they're stored only once (better than #define) */
extern const char* const dir_thumbnail_name; /* "_dirname.talk" */
extern const char* const file_thumbnail_ext; /* ".talk" for file voicing */
@@ -81,7 +101,6 @@ bool talk_voice_required(void); /* returns true if voice codec required */
#endif
int talk_get_bufsize(void); /* get the loaded voice file size */
size_t talkbuf_init(char* bufstart);
-void talk_buffer_steal(void); /* claim the mp3 buffer e.g. for play/record */
bool is_voice_queued(void); /* Are there more voice clips to be spoken? */
int talk_id(int32_t id, bool enqueue); /* play a voice ID from voicefont */
/* play a thumbnail from file */
diff --git a/firmware/export/audio.h b/firmware/export/audio.h
index 8108f50939..57f7981b34 100644
--- a/firmware/export/audio.h
+++ b/firmware/export/audio.h
@@ -75,10 +75,6 @@ void audio_error_clear(void);
int audio_get_file_pos(void);
void audio_beep(int duration);
-/* Required call when audio buffer is required for some other purpose */
-/* implemented in apps but called from firmware(!) */
-unsigned char *audio_get_buffer(bool talk_buf, size_t *buffer_size);
-
#if CONFIG_CODEC == SWCODEC
void audio_next_dir(void);
void audio_prev_dir(void);
diff --git a/firmware/target/arm/pp/usb-fw-pp502x.c b/firmware/target/arm/pp/usb-fw-pp502x.c
index 44cce14389..acbb221cfd 100644
--- a/firmware/target/arm/pp/usb-fw-pp502x.c
+++ b/firmware/target/arm/pp/usb-fw-pp502x.c
@@ -230,21 +230,6 @@ void usb_insert_int(void)
}
#endif /* USB_STATUS_BY_EVENT */
-#ifdef HAVE_BOOTLOADER_USB_MODE
-/* Replacement function that returns all unused memory after the bootloader
- * because the storage driver uses the audio buffer */
-extern unsigned char freebuffer[];
-extern unsigned char freebufferend[];
-unsigned char *audio_get_buffer(bool talk_buf, size_t *buffer_size)
-{
- if (buffer_size)
- *buffer_size = freebufferend - freebuffer + 1;
-
- return freebuffer;
- (void)talk_buf;
-}
-#endif /* HAVE_BOOTLOADER_USB_MODE */
-
void usb_drv_int_enable(bool enable)
{
/* enable/disable USB IRQ in CPU */
diff --git a/firmware/usbstack/usb_storage.c b/firmware/usbstack/usb_storage.c
index d1279d0ee1..a3b867319d 100644
--- a/firmware/usbstack/usb_storage.c
+++ b/firmware/usbstack/usb_storage.c
@@ -34,6 +34,7 @@
#if CONFIG_RTC
#include "timefuncs.h"
#endif
+#include "core_alloc.h"
#ifdef USB_USE_RAMDISK
#define RAMDISK_SIZE 2048
@@ -430,6 +431,7 @@ int usb_storage_get_config_descriptor(unsigned char *dest,int max_packet_size)
return (dest - orig_dest);
}
+static int usb_handle;
void usb_storage_init_connection(void)
{
logf("ums: set config");
@@ -452,13 +454,17 @@ void usb_storage_init_connection(void)
#else
/* TODO : check if bufsize is at least 32K ? */
size_t bufsize;
- unsigned char * audio_buffer;
+ unsigned char * buffer;
+ /* dummy ops with no callbacks, needed because by
+ * default buflib buffers can be moved around which must be avoided */
+ static struct buflib_callbacks dummy_ops;
- audio_buffer = audio_get_buffer(false,&bufsize);
+ usb_handle = core_alloc_maximum("usb storage", &bufsize, &dummy_ops);
+ buffer = core_get_data(usb_handle);
#if defined(UNCACHED_ADDR) && CONFIG_CPU != AS3525
- cbw_buffer = (void *)UNCACHED_ADDR((unsigned int)(audio_buffer+31) & 0xffffffe0);
+ cbw_buffer = (void *)UNCACHED_ADDR((unsigned int)(buffer+31) & 0xffffffe0);
#else
- cbw_buffer = (void *)((unsigned int)(audio_buffer+31) & 0xffffffe0);
+ cbw_buffer = (void *)((unsigned int)(buffer+31) & 0xffffffe0);
#endif
tb.transfer_buffer = cbw_buffer + MAX_CBW_SIZE;
commit_discard_dcache();
@@ -478,7 +484,8 @@ void usb_storage_init_connection(void)
void usb_storage_disconnect(void)
{
- /* Empty for now */
+ if (usb_handle > 0)
+ usb_handle = core_free(usb_handle);
}
/* called by usb_core_transfer_complete() */