summaryrefslogtreecommitdiffstats
path: root/apps
diff options
context:
space:
mode:
authorThomas Martitz <kugel@rockbox.org>2013-05-30 11:24:16 +0200
committerThomas Martitz <kugel@rockbox.org>2013-12-23 12:17:38 +0100
commit22e802e80048defd401462e062afcb10093ac793 (patch)
tree09d24f7eb2a3b18e6563e838398b2715394f7c4c /apps
parent64b9e1fa7b645daa36ca0018dc168d4f871fd538 (diff)
downloadrockbox-22e802e80048defd401462e062afcb10093ac793.tar.gz
rockbox-22e802e80048defd401462e062afcb10093ac793.zip
playback,talk: Share audiobuffer via core_alloc_maximum().
This fixes the radioart crash that was the result of buffering.c working on a freed buffer at the same time as buflib (radioart uses buffering.c for the images). With this change the buffer is owned by buflib exclusively so this cannot happen. As a result, audio_get_buffer() doesn't exist anymore. Callers should call core_alloc_maximum() directly. This buffer needs to be protected as usual against movement if necessary (previously it was not protected at all which cased the radioart crash), To get most of it they can adjust the willingness of the talk engine to give its buffer away (at the expense of disabling voice interface) with the new talk_buffer_set_policy() function. Change-Id: I52123012208d04967876a304451d634e2bef3a33
Diffstat (limited to 'apps')
-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
12 files changed, 658 insertions, 557 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 */