summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Sevakis <jethead71@rockbox.org>2012-05-02 17:22:28 -0400
committerMichael Sevakis <jethead71@rockbox.org>2012-05-02 17:22:28 -0400
commitda6cebb6b0b17b4a75a2bd4f51b7cf70b5dafe40 (patch)
treedf0eb18120c38ec7b08d3ae1e0837f0781065e87
parent3d3a144cf68186fd34f7bf11181b7757c7a6018d (diff)
Use buflib for the allocation of voice PCM resources.
Buffers are not allocated and thread is not created until the first call where voice is required. Adds a different callback (sync_callback) to buflib so that other sorts of synchonization are possible, such as briefly locking-out the PCM callback for a buffer move. It's sort of a messy addition but it is needed so voice decoding won't have to be stopped when its buffer is moved. Change-Id: I4d4d8c35eed5dd15fb7ee7df9323af3d036e92b3
-rw-r--r--apps/gui/icon.c2
-rw-r--r--apps/gui/skin_engine/skin_backdrops.c2
-rw-r--r--apps/gui/skin_engine/skin_parser.c2
-rw-r--r--apps/playback.c95
-rw-r--r--apps/talk.c58
-rw-r--r--apps/voice_thread.c153
-rw-r--r--apps/voice_thread.h5
-rw-r--r--firmware/buflib.c28
-rw-r--r--firmware/export/mp3_playback.h3
-rw-r--r--firmware/export/pcm_mixer.h6
-rw-r--r--firmware/font.c2
-rw-r--r--firmware/include/buflib.h6
-rw-r--r--firmware/pcm_mixer.c10
13 files changed, 242 insertions, 130 deletions
diff --git a/apps/gui/icon.c b/apps/gui/icon.c
index f11de7fff1..46da38b765 100644
--- a/apps/gui/icon.c
+++ b/apps/gui/icon.c
@@ -175,7 +175,7 @@ static int buflib_move_callback(int handle, void* current, void* new)
}
return BUFLIB_CB_OK;
}
-static struct buflib_callbacks buflib_ops = {buflib_move_callback, NULL};
+static struct buflib_callbacks buflib_ops = {buflib_move_callback, NULL, NULL};
static void load_icons(const char* filename, enum Iconset iconset,
enum screen_type screen)
diff --git a/apps/gui/skin_engine/skin_backdrops.c b/apps/gui/skin_engine/skin_backdrops.c
index 15b68589a9..edb895c56f 100644
--- a/apps/gui/skin_engine/skin_backdrops.c
+++ b/apps/gui/skin_engine/skin_backdrops.c
@@ -60,7 +60,7 @@ static int buflib_move_callback(int handle, void* current, void* new)
skin_backdrop_show(current_lcd_backdrop[i]);
return BUFLIB_CB_OK;
}
-static struct buflib_callbacks buflib_ops = {buflib_move_callback, NULL};
+static struct buflib_callbacks buflib_ops = {buflib_move_callback, NULL, NULL};
static bool first_go = true;
void skin_backdrop_init(void)
{
diff --git a/apps/gui/skin_engine/skin_parser.c b/apps/gui/skin_engine/skin_parser.c
index 0ebea67188..a2a360de0e 100644
--- a/apps/gui/skin_engine/skin_parser.c
+++ b/apps/gui/skin_engine/skin_parser.c
@@ -1655,7 +1655,7 @@ static int buflib_move_callback(int handle, void* current, void* new)
return BUFLIB_CB_CANNOT_MOVE;
return BUFLIB_CB_OK;
}
-static struct buflib_callbacks buflib_ops = {buflib_move_callback, NULL};
+static struct buflib_callbacks buflib_ops = {buflib_move_callback, NULL, NULL};
static void lock_handle(int handle)
{
currently_loading_handle = handle;
diff --git a/apps/playback.c b/apps/playback.c
index 997a374410..bd7dd813c9 100644
--- a/apps/playback.c
+++ b/apps/playback.c
@@ -753,11 +753,12 @@ 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)
+static void audio_reset_buffer_noalloc(
+ void *filebuf, enum audio_buffer_state state)
{
/*
* Layout audio buffer as follows:
- * [[|TALK]|SCRATCH|BUFFERING|PCM|[VOICE|]]
+ * [[|TALK]|SCRATCH|BUFFERING|PCM]
*/
/* see audio_get_recording_buffer if this is modified */
@@ -770,7 +771,8 @@ static void audio_reset_buffer_noalloc(void* filebuf)
/* Initially set up file buffer as all space available */
size_t allocsize;
- /* Subtract whatever voice needs */
+ /* Subtract whatever voice needs (we're called when promoting
+ the state only) */
allocsize = talkbuf_init(filebuf);
allocsize = ALIGN_UP(allocsize, sizeof (intptr_t));
if (allocsize > filebuflen)
@@ -779,41 +781,33 @@ static void audio_reset_buffer_noalloc(void* filebuf)
filebuf += allocsize;
filebuflen -= allocsize;
- if (talk_voice_required())
+ if (state == AUDIOBUF_STATE_INITIALIZED)
{
- /* Need a space for voice PCM output */
- allocsize = voicebuf_init(filebuf + filebuflen);
+ /* 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;
- }
-
- /* 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);
+ }
- /* Clear any references to the file buffer */
- buffer_state = AUDIOBUF_STATE_INITIALIZED;
+ buffer_state = state;
#if defined(ROCKBOX_HAS_LOGF) && defined(LOGF_ENABLE)
/* Make sure everything adds up - yes, some info is a bit redundant but
@@ -891,11 +885,12 @@ static int shrink_callback(int handle, unsigned hints, void* start, size_t old_s
{
case BUFLIB_SHRINK_POS_BACK:
core_shrink(handle, start, size);
- audio_reset_buffer_noalloc(start);
+ audio_reset_buffer_noalloc(start, buffer_state);
break;
case BUFLIB_SHRINK_POS_FRONT:
core_shrink(handle, start + wanted_size, size);
- audio_reset_buffer_noalloc(start + wanted_size);
+ audio_reset_buffer_noalloc(start + wanted_size,
+ buffer_state);
break;
}
if (playing || play_queued)
@@ -912,7 +907,7 @@ static struct buflib_callbacks ops = {
.shrink_callback = shrink_callback,
};
-static void audio_reset_buffer(void)
+static void audio_reset_buffer(enum audio_buffer_state state)
{
if (audiobuf_handle > 0)
{
@@ -922,7 +917,7 @@ static void audio_reset_buffer(void)
audiobuf_handle = core_alloc_maximum("audiobuf", &filebuflen, &ops);
unsigned char *filebuf = core_get_data(audiobuf_handle);
- audio_reset_buffer_noalloc(filebuf);
+ audio_reset_buffer_noalloc(filebuf, state);
}
/* Set the buffer margin to begin rebuffering when 'seconds' from empty */
@@ -2051,7 +2046,7 @@ static int audio_fill_file_buffer(void)
file size shouldn't have changed so we can go straight from
AUDIOBUF_STATE_VOICED_ONLY to AUDIOBUF_STATE_INITIALIZED */
if (buffer_state != AUDIOBUF_STATE_INITIALIZED)
- audio_reset_buffer();
+ audio_reset_buffer(AUDIOBUF_STATE_INITIALIZED);
logf("Starting buffer fill");
@@ -3649,14 +3644,9 @@ unsigned char * audio_get_buffer(bool talk_buf, size_t *buffer_size)
logf("get buffer: audio");
/* Safe to just return this if already AUDIOBUF_STATE_VOICED_ONLY or
still AUDIOBUF_STATE_INITIALIZED */
- /* Skip talk buffer and move pcm buffer to end to maximize available
- contiguous memory - no audio running means voice will not need the
- swap space */
- size_t talkbuf_size;
- buf += talkbuf_size = talkbuf_init(buf);
+ size_t talkbuf_size = talkbuf_init(buf);
+ buf += talkbuf_size; /* Skip talk buffer */
filebuflen -= talkbuf_size;
- filebuflen -= voicebuf_init(buf + filebuflen);
-
buffer_state = AUDIOBUF_STATE_VOICED_ONLY;
}
@@ -3673,19 +3663,18 @@ unsigned char * audio_get_recording_buffer(size_t *buffer_size)
}
#endif /* HAVE_RECORDING */
-/* Restore audio buffer to a particular state (one more valid than the current
- state) */
+/* 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();
+ audio_reset_buffer(AUDIOBUF_STATE_INITIALIZED);
return true;
case AUDIO_WANT_VOICE:
if (buffer_state == AUDIOBUF_STATE_TRASHED)
- audio_reset_buffer();
+ audio_reset_buffer(AUDIOBUF_STATE_VOICED_ONLY);
return true;
default:
return false;
@@ -3910,24 +3899,18 @@ void audio_init(void)
queue_enable_queue_send(&audio_queue, &audio_queue_sender_list,
audio_thread_id);
-#ifdef PLAYBACK_VOICE
- voice_thread_init();
-#endif
-
- /* audio_reset_buffer must know the size of voice buffer so init
- talk first */
- talk_init();
+ /* Initialize the track buffering system */
+ track_list_init();
+ buffering_init();
#ifdef HAVE_CROSSFADE
/* Set crossfade setting for next buffer init which should be about... */
pcmbuf_request_crossfade_enable(global_settings.crossfade);
#endif
- /* Initialize the buffering system */
- track_list_init();
- buffering_init();
- /* ...now! Set up the buffers */
- audio_reset_buffer();
+ /* ...now...audio_reset_buffer must know the size of voicefile buffer so
+ init talk first which will init the buffers */
+ talk_init();
/* Probably safe to say */
audio_is_initialized = true;
diff --git a/apps/talk.c b/apps/talk.c
index 4b0975083d..f3ca967d3a 100644
--- a/apps/talk.c
+++ b/apps/talk.c
@@ -31,7 +31,11 @@
#include "kernel.h"
#include "settings.h"
#include "settings_list.h"
+#if CONFIG_CODEC == SWCODEC
+#include "voice_thread.h"
+#else
#include "mp3_playback.h"
+#endif
#include "audio.h"
#include "lang.h"
#include "talk.h"
@@ -43,7 +47,6 @@
#include "plugin.h" /* plugin_get_buffer() */
#include "debug.h"
-
/* Memory layout varies between targets because the
Archos (MASCODEC) devices cannot mix voice and audio playback
@@ -589,7 +592,6 @@ 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 */
@@ -614,8 +616,22 @@ static void reset_state(void)
#endif
p_silence = NULL; /* pause clip not accessible */
- voicebuf = NULL;
+ 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(false, &size);
+ }
+
+ return !!voicebuf;
}
+#endif /* CONFIG_CODEC == SWCODEC */
/***************** Public implementation *****************/
@@ -655,7 +671,7 @@ void talk_init(void)
voicefile_size = filesize(filehandle);
- audio_get_buffer(false, NULL); /* Must tell audio to reinitialize */
+ audio_get_buffer(false, NULL); /* Must tell audio to reinitialize */
reset_state(); /* use this for most of our inits */
#ifdef TALK_PARTIAL_LOAD
@@ -693,9 +709,14 @@ void talk_init(void)
voicefile_size = 0;
}
- alloc_thumbnail_buf();
close(filehandle); /* close again, this was just to detect presence */
filehandle = -1;
+
+#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
}
#if CONFIG_CODEC == SWCODEC
@@ -709,7 +730,7 @@ bool talk_voice_required(void)
#endif
/* return size of voice file */
-static int talk_get_buffer(void)
+static size_t talk_get_buffer_size(void)
{
#if CONFIG_CODEC == SWCODEC
return voicefile_size + MAX_THUMBNAIL_BUFSIZE;
@@ -726,10 +747,11 @@ size_t talkbuf_init(char *bufstart)
if (changed) /* must reload voice file */
reset_state();
+
if (bufstart)
voicebuf = bufstart;
- return talk_get_buffer();
+ return talk_get_buffer_size();
}
/* somebody else claims the mp3 buffer, e.g. for regular play/record */
@@ -748,29 +770,27 @@ void talk_buffer_steal(void)
reset_state();
}
-
/* play a voice ID from voicefile */
int talk_id(int32_t id, bool enqueue)
{
long clipsize;
- int temp = talk_get_buffer();
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 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 */
return -1;
#endif
- /* try to get audio buffer until talkbuf_init() is called */
- if (!voicebuf)
- voicebuf = audio_get_buffer(true, (size_t*)&temp);
-
- if (p_voicefile == NULL && has_voicefile)
- load_voicefile(false, voicebuf, MIN(talk_get_buffer(),temp)); /* reload needed */
+ 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;
@@ -842,7 +862,11 @@ static int _talk_file(const char* filename,
if (talk_temp_disable_count > 0)
return -1; /* talking has been disabled */
-#if CONFIG_CODEC != SWCODEC
+#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
diff --git a/apps/voice_thread.c b/apps/voice_thread.c
index 48c6f4845d..d0d36b4064 100644
--- a/apps/voice_thread.c
+++ b/apps/voice_thread.c
@@ -20,6 +20,7 @@
****************************************************************************/
#include <sys/types.h>
#include "system.h"
+#include "core_alloc.h"
#include "thread.h"
#include "voice_thread.h"
#include "talk.h"
@@ -31,6 +32,10 @@
#include "pcm_mixer.h"
#include "codecs/libspeex/speex/speex.h"
+/* Default number of native-frequency PCM frames to queue - adjust as
+ necessary per-target */
+#define VOICE_FRAMES 4
+
/* Define any of these as "1" and uncomment the LOGF_ENABLE line to log
regular and/or timeout messages */
#define VOICE_LOGQUEUES 0
@@ -80,22 +85,10 @@ static struct event_queue voice_queue SHAREDBSS_ATTR;
static struct queue_sender_list voice_queue_sender_list SHAREDBSS_ATTR;
static int quiet_counter SHAREDDATA_ATTR = 0;
-/* Buffer for decoded samples */
-static spx_int16_t voice_output_buf[VOICE_FRAME_COUNT] MEM_ALIGN_ATTR;
-
#define VOICE_PCM_FRAME_COUNT ((NATIVE_FREQUENCY*VOICE_FRAME_COUNT + \
VOICE_SAMPLE_RATE) / VOICE_SAMPLE_RATE)
#define VOICE_PCM_FRAME_SIZE (VOICE_PCM_FRAME_COUNT*2*sizeof (int16_t))
-/* Default number of native-frequency PCM frames to queue - adjust as
- necessary per-target */
-#define VOICE_FRAMES 3
-
-/* Might have lookahead and be skipping samples, so size is needed */
-static size_t voicebuf_sizes[VOICE_FRAMES];
-static int16_t (* voicebuf)[2*VOICE_PCM_FRAME_COUNT];
-static unsigned int cur_buf_in, cur_buf_out;
-
/* Voice processing states */
enum voice_state
{
@@ -135,6 +128,7 @@ struct voice_thread_data
struct voice_info vi; /* Copy of clip data */
int lookahead; /* Number of samples to drop at start of clip */
struct dsp_buffer src; /* Speex output buffer/input to DSP */
+ struct dsp_buffer *dst; /* Pointer to DSP output buffer for PCM */
};
/* Functions called in their repective state that return the next state to
@@ -143,10 +137,62 @@ static enum voice_state voice_message(struct voice_thread_data *td);
static enum voice_state voice_decode(struct voice_thread_data *td);
static enum voice_state voice_buffer_insert(struct voice_thread_data *td);
+/* Might have lookahead and be skipping samples, so size is needed */
+static struct voice_buf
+{
+ /* Buffer for decoded samples */
+ spx_int16_t spx_outbuf[VOICE_FRAME_COUNT];
+ /* Queue frame indexes */
+ unsigned int frame_in, frame_out;
+ /* For PCM pointer adjustment */
+ struct voice_thread_data *td;
+ /* Buffers for mixing voice */
+ struct voice_pcm_frame
+ {
+ size_t size;
+ int16_t pcm[2*VOICE_PCM_FRAME_COUNT];
+ } frames[VOICE_FRAMES];
+} *voice_buf = NULL;
+
+static int voice_buf_hid = 0;
+
+static int move_callback(int handle, void *current, void *new)
+{
+ /* Have to adjust the pointers that point into things in voice_buf */
+ off_t diff = new - current;
+ struct voice_thread_data *td = voice_buf->td;
+ td->src.p32[0] = SKIPBYTES(td->src.p32[0], diff);
+ td->src.p32[1] = SKIPBYTES(td->src.p32[1], diff);
+ if (td->dst != NULL) /* Only when calling dsp_process */
+ td->dst->p16out = SKIPBYTES(td->dst->p16out, diff);
+ mixer_adjust_channel_address(PCM_MIXER_CHAN_VOICE, diff);
+ voice_buf = new;
+
+ return BUFLIB_CB_OK;
+ (void)handle;
+};
+
+static void sync_callback(int handle, bool sync_on)
+{
+ /* A move must not allow PCM to access the channel */
+ if (sync_on)
+ pcm_play_lock();
+ else
+ pcm_play_unlock();
+
+ (void)handle;
+}
+
+static struct buflib_callbacks ops =
+{
+ .move_callback = move_callback,
+ .sync_callback = sync_callback,
+};
+
/* Number of frames in queue */
static inline int voice_unplayed_frames(void)
{
- return cur_buf_in - cur_buf_out;
+ return voice_buf->frame_in - voice_buf->frame_out;
}
/* Mixer channel callback */
@@ -155,10 +201,11 @@ static void voice_pcm_callback(const void **start, size_t *size)
if (voice_unplayed_frames() == 0)
return; /* Done! */
- unsigned int i = ++cur_buf_out % VOICE_FRAMES;
+ struct voice_pcm_frame *frame =
+ &voice_buf->frames[++voice_buf->frame_out % VOICE_FRAMES];
- *start = voicebuf[i];
- *size = voicebuf_sizes[i];
+ *start = frame->pcm;
+ *size = frame->size;
}
/* Start playback of voice channel if not already playing */
@@ -168,16 +215,18 @@ static void voice_start_playback(void)
voice_unplayed_frames() <= 0)
return;
- unsigned int i = cur_buf_out % VOICE_FRAMES;
+ struct voice_pcm_frame *frame =
+ &voice_buf->frames[voice_buf->frame_out % VOICE_FRAMES];
+
mixer_channel_play_data(PCM_MIXER_CHAN_VOICE, voice_pcm_callback,
- voicebuf[i], voicebuf_sizes[i]);
+ frame->pcm, frame->size);
}
/* Stop the voice channel */
static void voice_stop_playback(void)
{
mixer_channel_stop(PCM_MIXER_CHAN_VOICE);
- cur_buf_in = cur_buf_out = 0;
+ voice_buf->frame_in = voice_buf->frame_out = 0;
}
/* Grab a free PCM frame */
@@ -190,7 +239,7 @@ static int16_t * voice_buf_get(void)
return NULL;
}
- return voicebuf[cur_buf_in % VOICE_FRAMES];
+ return voice_buf->frames[voice_buf->frame_in % VOICE_FRAMES].pcm;
}
/* Commit a frame returned by voice_buf_get and set the actual size */
@@ -198,7 +247,7 @@ static void voice_buf_commit(int count)
{
if (count > 0)
{
- voicebuf_sizes[cur_buf_in++ % VOICE_FRAMES] =
+ voice_buf->frames[voice_buf->frame_in++ % VOICE_FRAMES].size =
count * 2 * sizeof (int16_t);
}
}
@@ -207,7 +256,7 @@ static void voice_buf_commit(int count)
void mp3_play_data(const void *start, size_t size,
mp3_play_callback_t get_more)
{
- if (get_more != NULL && start != NULL && size > 0)
+ if (voice_thread_id && start && size && get_more)
{
struct voice_info voice_clip =
{
@@ -224,8 +273,11 @@ void mp3_play_data(const void *start, size_t size,
/* Stop current voice clip from playing */
void mp3_play_stop(void)
{
- LOGFQUEUE("mp3 >| voice Q_VOICE_STOP");
- queue_send(&voice_queue, Q_VOICE_STOP, 0);
+ if (voice_thread_id != 0)
+ {
+ LOGFQUEUE("mp3 >| voice Q_VOICE_STOP");
+ queue_send(&voice_queue, Q_VOICE_STOP, 0);
+ }
}
void mp3_play_pause(bool play)
@@ -270,6 +322,9 @@ static void voice_data_init(struct voice_thread_data *td)
dsp_configure(td->dsp, DSP_SET_STEREO_MODE, STEREO_MONO);
mixer_channel_set_amplitude(PCM_MIXER_CHAN_VOICE, MIX_AMP_UNITY);
+
+ voice_buf->td = td;
+ td->dst = NULL;
}
/* Voice thread message processing */
@@ -300,9 +355,6 @@ static enum voice_state voice_message(struct voice_thread_data *td)
/* Copy the clip info */
td->vi = *(struct voice_info *)td->ev.data;
- /* Be sure audio buffer is initialized */
- audio_restore_playback(AUDIO_WANT_VOICE);
-
/* We need nothing more from the sending thread - let it run */
queue_reply(&voice_queue, 1);
@@ -356,7 +408,7 @@ static enum voice_state voice_decode(struct voice_thread_data *td)
return VOICE_STATE_MESSAGE;
/* Decode the data */
- if (speex_decode_int(td->st, &td->bits, voice_output_buf) < 0)
+ if (speex_decode_int(td->st, &td->bits, voice_buf->spx_outbuf) < 0)
{
/* End of stream or error - get next clip */
td->vi.size = 0;
@@ -386,7 +438,7 @@ static enum voice_state voice_decode(struct voice_thread_data *td)
/* Output the decoded frame */
td->src.remcount = VOICE_FRAME_COUNT - td->lookahead;
- td->src.pin[0] = &voice_output_buf[td->lookahead];
+ td->src.pin[0] = &voice_buf->spx_outbuf[td->lookahead];
td->src.pin[1] = NULL;
td->src.proc_mask = 0;
@@ -412,7 +464,9 @@ static enum voice_state voice_buffer_insert(struct voice_thread_data *td)
dst.remcount = 0;
dst.bufcount = VOICE_PCM_FRAME_COUNT;
+ td->dst = &dst;
dsp_process(td->dsp, &td->src, &dst);
+ td->dst = NULL;
voice_buf_commit(dst.remcount);
@@ -451,9 +505,36 @@ static void NORETURN_ATTR voice_thread(void)
}
}
-/* Initialize all synchronization objects create the thread */
+/* Initialize buffers, all synchronization objects and create the thread */
void voice_thread_init(void)
{
+ if (voice_thread_id != 0)
+ return; /* Already did an init and succeeded at it */
+
+ if (!talk_voice_required())
+ {
+ logf("No voice required");
+ return;
+ }
+
+ voice_buf_hid = core_alloc_ex("voice buf", sizeof (*voice_buf), &ops);
+
+ if (voice_buf_hid <= 0)
+ {
+ logf("voice: core_alloc_ex failed");
+ return;
+ }
+
+ voice_buf = core_get_data(voice_buf_hid);
+
+ if (voice_buf == NULL)
+ {
+ logf("voice: core_get_data failed");
+ core_free(voice_buf_hid);
+ voice_buf_hid = 0;
+ return;
+ }
+
logf("Starting voice thread");
queue_init(&voice_queue, false);
@@ -469,18 +550,12 @@ void voice_thread_init(void)
/* Set the voice thread priority */
void voice_thread_set_priority(int priority)
{
+ if (voice_thread_id == 0)
+ return;
+
if (priority > PRIORITY_VOICE)
priority = PRIORITY_VOICE;
thread_set_priority(voice_thread_id, priority);
}
#endif
-
-/* Initialize voice PCM buffer and return size, allocated from the end */
-size_t voicebuf_init(void *bufend)
-{
- size_t size = VOICE_FRAMES * sizeof (voicebuf[0]);
- cur_buf_out = cur_buf_in = 0;
- voicebuf = bufend - size;
- return size;
-}
diff --git a/apps/voice_thread.h b/apps/voice_thread.h
index e88630439b..64c20c1e8d 100644
--- a/apps/voice_thread.h
+++ b/apps/voice_thread.h
@@ -23,7 +23,10 @@
#include "config.h"
+#ifndef MP3_PLAY_CALLBACK_DEFINED
+#define MP3_PLAY_CALLBACK_DEFINED
typedef void (*mp3_play_callback_t)(const void **start, size_t *size);
+#endif
void mp3_play_data(const void *start, size_t size,
mp3_play_callback_t get_more);
@@ -39,6 +42,4 @@ void voice_thread_init(void) INIT_ATTR;
void voice_thread_set_priority(int priority);
#endif
-size_t voicebuf_init(void *bufend);
-
#endif /* VOICE_THREAD_H */
diff --git a/firmware/buflib.c b/firmware/buflib.c
index 2d4b4b8bb2..f73c8ce4fb 100644
--- a/firmware/buflib.c
+++ b/firmware/buflib.c
@@ -210,23 +210,27 @@ move_block(struct buflib_context* ctx, union buflib_data* block, int shift)
/* disable IRQs to make accessing the buffer from interrupt context safe. */
/* protect the move callback, as a cached global pointer might be updated
* in it. and protect "tmp->alloc = new_start" for buflib_get_data() */
- disable_irq();
/* call the callback before moving */
- if (ops)
+ if (ops && ops->sync_callback)
+ ops->sync_callback(handle, true);
+ else
+ disable_irq();
+
+ bool retval = false;
+ if (!ops || ops->move_callback(handle, tmp->alloc, new_start)
+ != BUFLIB_CB_CANNOT_MOVE)
{
- if (ops->move_callback(handle, tmp->alloc, new_start)
- == BUFLIB_CB_CANNOT_MOVE)
- {
- enable_irq();
- return false;
- }
+ tmp->alloc = new_start; /* update handle table */
+ memmove(new_block, block, block->val * sizeof(union buflib_data));
+ retval = true;
}
- tmp->alloc = new_start; /* update handle table */
- memmove(new_block, block, block->val * sizeof(union buflib_data));
+ if (ops && ops->sync_callback)
+ ops->sync_callback(handle, false);
+ else
+ enable_irq();
- enable_irq();
- return true;
+ return retval;
}
/* Compact allocations and handle table, adjusting handle pointers as needed.
diff --git a/firmware/export/mp3_playback.h b/firmware/export/mp3_playback.h
index de27a2a46d..7434021611 100644
--- a/firmware/export/mp3_playback.h
+++ b/firmware/export/mp3_playback.h
@@ -27,7 +27,10 @@
#include <stdbool.h>
/* callback fn */
+#ifndef MP3_PLAY_CALLBACK_DEFINED
+#define MP3_PLAY_CALLBACK_DEFINED
typedef void (*mp3_play_callback_t)(const void **start, size_t* size);
+#endif
/* functions formerly in mpeg.c */
void mp3_init(int volume, int bass, int treble, int balance, int loudness,
diff --git a/firmware/export/pcm_mixer.h b/firmware/export/pcm_mixer.h
index 6e1632d5ae..5d8deb2bbf 100644
--- a/firmware/export/pcm_mixer.h
+++ b/firmware/export/pcm_mixer.h
@@ -22,6 +22,8 @@
#ifndef PCM_MIXER_H
#define PCM_MIXER_H
+#include <sys/types.h>
+
/** Simple config **/
/* Length of PCM frames (always) */
@@ -111,6 +113,10 @@ const void * mixer_channel_get_buffer(enum pcm_mixer_channel channel, int *count
void mixer_channel_calculate_peaks(enum pcm_mixer_channel channel,
int *left, int *right);
+/* Adjust channel pointer by a given offset to support movable buffers */
+void mixer_adjust_channel_address(enum pcm_mixer_channel channel,
+ off_t offset);
+
/* Stop ALL channels and PCM and reset state */
void mixer_reset(void);
diff --git a/firmware/font.c b/firmware/font.c
index 895ce15446..4b21f0ffe3 100644
--- a/firmware/font.c
+++ b/firmware/font.c
@@ -141,7 +141,7 @@ void font_lock(int font_id, bool lock)
lock_font_handle(buflib_allocations[font_id], lock);
}
-static struct buflib_callbacks buflibops = {buflibmove_callback, NULL };
+static struct buflib_callbacks buflibops = {buflibmove_callback, NULL, NULL };
static inline struct font *pf_from_handle(int handle)
{
diff --git a/firmware/include/buflib.h b/firmware/include/buflib.h
index 9cd7c0b2e0..6c9ccf7402 100644
--- a/firmware/include/buflib.h
+++ b/firmware/include/buflib.h
@@ -103,6 +103,12 @@ struct buflib_callbacks {
* at least shrinkable
*/
int (*shrink_callback)(int handle, unsigned hints, void* start, size_t old_size);
+ /**
+ * This is called when special steps must be taken for synchronization
+ * both before the move_callback is called and after the data has been
+ * moved.
+ */
+ void (*sync_callback)(int handle, bool sync_on);
};
#define BUFLIB_SHRINK_POS_MASK ((1<<0|1<<1)<<30)
diff --git a/firmware/pcm_mixer.c b/firmware/pcm_mixer.c
index c3e7ffa478..85d31ade70 100644
--- a/firmware/pcm_mixer.c
+++ b/firmware/pcm_mixer.c
@@ -406,6 +406,16 @@ void mixer_channel_calculate_peaks(enum pcm_mixer_channel channel,
*right = peaks->val[1];
}
+/* Adjust channel pointer by a given offset to support movable buffers */
+void mixer_adjust_channel_address(enum pcm_mixer_channel channel,
+ off_t offset)
+{
+ pcm_play_lock();
+ /* Makes no difference if it's stopped */
+ channels[channel].start += offset;
+ pcm_play_unlock();
+}
+
/* Stop ALL channels and PCM and reset state */
void mixer_reset(void)
{