forked from len0rd/rockbox
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
This commit is contained in:
parent
64b9e1fa7b
commit
22e802e800
15 changed files with 676 additions and 587 deletions
|
@ -280,13 +280,11 @@ static bool do_non_text_tags(struct gui_wps *gwps, struct skin_draw_info *info,
|
||||||
if (do_refresh && aa)
|
if (do_refresh && aa)
|
||||||
{
|
{
|
||||||
int handle = playback_current_aa_hid(data->playback_aa_slot);
|
int handle = playback_current_aa_hid(data->playback_aa_slot);
|
||||||
#if 0 /* FIXME: FS#12797*/
|
|
||||||
if (in_radio_screen() || (get_radio_status() != FMRADIO_OFF))
|
if (in_radio_screen() || (get_radio_status() != FMRADIO_OFF))
|
||||||
{
|
{
|
||||||
struct dim dim = {aa->width, aa->height};
|
struct dim dim = {aa->width, aa->height};
|
||||||
handle = radio_get_art_hid(&dim);
|
handle = radio_get_art_hid(&dim);
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
aa->draw_handle = handle;
|
aa->draw_handle = handle;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
72
apps/mpeg.c
72
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;
|
ssize_t size = (ssize_t)old_size - wanted_size;
|
||||||
/* keep at least 256K for the buffering */
|
/* keep at least 256K for the buffering */
|
||||||
if ((size - extradata_size) < AUDIO_BUFFER_RESERVE)
|
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 */
|
/* TODO: Do it without stopping playback, if possible */
|
||||||
bool playing = (audio_status() & AUDIO_STATUS_PLAY) == AUDIO_STATUS_PLAY;
|
bool playing = (audio_status() & AUDIO_STATUS_PLAY) == AUDIO_STATUS_PLAY;
|
||||||
long offset = audio_current_track()->offset;
|
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
|
else
|
||||||
audio_stop();
|
audio_stop();
|
||||||
talk_buffer_steal(); /* we obtain control over the buffer */
|
|
||||||
|
|
||||||
switch (hints & BUFLIB_SHRINK_POS_MASK)
|
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);
|
core_shrink(handle, start + wanted_size, size);
|
||||||
audio_reset_buffer_noalloc(start + wanted_size, size);
|
audio_reset_buffer_noalloc(start + wanted_size, size);
|
||||||
break;
|
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)
|
if (playing)
|
||||||
{ /* safe to call even from the audio thread (due to queue_post()) */
|
{ /* 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,
|
.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
|
#ifndef SIMULATOR
|
||||||
/* Send callback events to notify about removing old tracks. */
|
/* Send callback events to notify about removing old tracks. */
|
||||||
static void generate_unbuffer_events(void)
|
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)
|
static void audio_reset_buffer_noalloc(void* buf, size_t bufsize)
|
||||||
{
|
{
|
||||||
talk_buffer_steal(); /* will use the mp3 buffer */
|
|
||||||
mpeg_audiobuf = buf;
|
mpeg_audiobuf = buf;
|
||||||
audiobuflen = bufsize;
|
audiobuflen = bufsize;
|
||||||
if (global_settings.cuesheet)
|
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));
|
mpeg_audiobuf = SKIPBYTES(mpeg_audiobuf, sizeof(struct cuesheet));
|
||||||
audiobuflen -= sizeof(struct cuesheet);
|
audiobuflen -= sizeof(struct cuesheet);
|
||||||
}
|
}
|
||||||
audio_talkbuf_init(mpeg_audiobuf);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void audio_reset_buffer(void)
|
static void audio_reset_buffer(void)
|
||||||
{
|
{
|
||||||
size_t bufsize = audiobuflen;
|
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)
|
if (!audiobuf_handle)
|
||||||
|
{
|
||||||
|
talk_buffer_set_policy(TALK_BUFFER_LOOSE);
|
||||||
audiobuf_handle = core_alloc_maximum("audiobuf", &bufsize, &ops);
|
audiobuf_handle = core_alloc_maximum("audiobuf", &bufsize, &ops);
|
||||||
|
}
|
||||||
|
|
||||||
audio_reset_buffer_noalloc(core_get_data(audiobuf_handle), bufsize);
|
audio_reset_buffer_noalloc(core_get_data(audiobuf_handle), bufsize);
|
||||||
}
|
}
|
||||||
|
@ -2818,6 +2793,8 @@ void audio_play(long offset)
|
||||||
|
|
||||||
void audio_stop(void)
|
void audio_stop(void)
|
||||||
{
|
{
|
||||||
|
if (audiobuf_handle <= 0)
|
||||||
|
return; /* nothing to do, must be hard-stopped already */
|
||||||
#ifndef SIMULATOR
|
#ifndef SIMULATOR
|
||||||
mpeg_stop_done = false;
|
mpeg_stop_done = false;
|
||||||
queue_post(&mpeg_queue, MPEG_STOP, 0);
|
queue_post(&mpeg_queue, MPEG_STOP, 0);
|
||||||
|
@ -2828,8 +2805,6 @@ void audio_stop(void)
|
||||||
is_playing = false;
|
is_playing = false;
|
||||||
playing = false;
|
playing = false;
|
||||||
#endif /* SIMULATOR */
|
#endif /* SIMULATOR */
|
||||||
/* give voice our entire buffer */
|
|
||||||
audio_talkbuf_init(mpeg_audiobuf);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* dummy */
|
/* dummy */
|
||||||
|
@ -2840,13 +2815,12 @@ void audio_stop_recording(void)
|
||||||
|
|
||||||
void audio_hard_stop(void)
|
void audio_hard_stop(void)
|
||||||
{
|
{
|
||||||
audio_stop();
|
|
||||||
/* tell voice we obtain the buffer before freeing */
|
|
||||||
talk_buffer_steal();
|
|
||||||
if (audiobuf_handle > 0)
|
if (audiobuf_handle > 0)
|
||||||
{
|
{
|
||||||
|
audio_stop();
|
||||||
audiobuf_handle = core_free(audiobuf_handle);
|
audiobuf_handle = core_free(audiobuf_handle);
|
||||||
mpeg_audiobuf = NULL;
|
mpeg_audiobuf = NULL;
|
||||||
|
talk_buffer_set_policy(TALK_BUFFER_DEFAULT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
178
apps/playback.c
178
apps/playback.c
|
@ -110,7 +110,6 @@ static enum audio_buffer_state
|
||||||
{
|
{
|
||||||
AUDIOBUF_STATE_TRASHED = -1, /* trashed; must be reset */
|
AUDIOBUF_STATE_TRASHED = -1, /* trashed; must be reset */
|
||||||
AUDIOBUF_STATE_INITIALIZED = 0, /* voice+audio OR audio-only */
|
AUDIOBUF_STATE_INITIALIZED = 0, /* voice+audio OR audio-only */
|
||||||
AUDIOBUF_STATE_VOICED_ONLY = 1, /* voice-only */
|
|
||||||
} buffer_state = AUDIOBUF_STATE_TRASHED; /* (A,O) */
|
} buffer_state = AUDIOBUF_STATE_TRASHED; /* (A,O) */
|
||||||
|
|
||||||
/** Main state control **/
|
/** Main state control **/
|
||||||
|
@ -729,60 +728,42 @@ size_t audio_buffer_available(void)
|
||||||
/* Set up the audio buffer for playback
|
/* Set up the audio buffer for playback
|
||||||
* filebuflen must be pre-initialized with the maximum size */
|
* filebuflen must be pre-initialized with the maximum size */
|
||||||
static void audio_reset_buffer_noalloc(
|
static void audio_reset_buffer_noalloc(
|
||||||
void *filebuf, enum audio_buffer_state state)
|
void *filebuf)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* Layout audio buffer as follows:
|
* Layout audio buffer as follows:
|
||||||
* [[|TALK]|SCRATCH|BUFFERING|PCM]
|
* [|SCRATCH|BUFFERING|PCM]
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* see audio_get_recording_buffer if this is modified */
|
|
||||||
logf("%s()", __func__);
|
logf("%s()", __func__);
|
||||||
|
|
||||||
/* If the setup of anything allocated before the file buffer is
|
/* If the setup of anything allocated before the file buffer is
|
||||||
changed, do check the adjustments after the buffer_alloc call
|
changed, do check the adjustments after the buffer_alloc call
|
||||||
as it will likely be affected and need sliding over */
|
as it will likely be affected and need sliding over */
|
||||||
|
|
||||||
/* Initially set up file buffer as all space available */
|
|
||||||
size_t allocsize;
|
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
|
/* Make sure filebuflen is a pointer sized multiple after
|
||||||
the state only) */
|
adjustment */
|
||||||
allocsize = talkbuf_init(filebuf);
|
|
||||||
allocsize = ALIGN_UP(allocsize, sizeof (intptr_t));
|
allocsize = ALIGN_UP(allocsize, sizeof (intptr_t));
|
||||||
if (allocsize > filebuflen)
|
if (allocsize > filebuflen)
|
||||||
goto bufpanic;
|
goto bufpanic;
|
||||||
|
|
||||||
|
filebuflen -= allocsize;
|
||||||
|
|
||||||
|
/* Scratch memory */
|
||||||
|
allocsize = scratch_mem_size();
|
||||||
|
if (allocsize > filebuflen)
|
||||||
|
goto bufpanic;
|
||||||
|
|
||||||
|
scratch_mem_init(filebuf);
|
||||||
filebuf += allocsize;
|
filebuf += allocsize;
|
||||||
filebuflen -= allocsize;
|
filebuflen -= allocsize;
|
||||||
|
|
||||||
if (state == AUDIOBUF_STATE_INITIALIZED)
|
buffering_reset(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
|
buffer_state = AUDIOBUF_STATE_INITIALIZED;
|
||||||
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_mem_init(filebuf);
|
|
||||||
filebuf += allocsize;
|
|
||||||
filebuflen -= allocsize;
|
|
||||||
|
|
||||||
buffering_reset(filebuf, filebuflen);
|
|
||||||
}
|
|
||||||
|
|
||||||
buffer_state = state;
|
|
||||||
|
|
||||||
#if defined(ROCKBOX_HAS_LOGF) && defined(LOGF_ENABLE)
|
#if defined(ROCKBOX_HAS_LOGF) && defined(LOGF_ENABLE)
|
||||||
/* Make sure everything adds up - yes, some info is a bit redundant but
|
/* 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 */
|
/* codec messages */
|
||||||
{ Q_AUDIO_PLAY, Q_AUDIO_PLAY },
|
{ Q_AUDIO_PLAY, Q_AUDIO_PLAY },
|
||||||
};
|
};
|
||||||
|
bool give_up = false;
|
||||||
/* filebuflen is, at this point, the buffering.c buffer size,
|
/* filebuflen is, at this point, the buffering.c buffer size,
|
||||||
* i.e. the audiobuf except voice, scratch mem, pcm, ... */
|
* i.e. the audiobuf except voice, scratch mem, pcm, ... */
|
||||||
ssize_t extradata_size = old_size - filebuflen;
|
ssize_t extradata_size = old_size - filebuflen;
|
||||||
/* check what buflib requests */
|
/* check what buflib requests */
|
||||||
size_t wanted_size = (hints & BUFLIB_SHRINK_SIZE_MASK);
|
size_t wanted_size = (hints & BUFLIB_SHRINK_SIZE_MASK);
|
||||||
ssize_t size = (ssize_t)old_size - wanted_size;
|
ssize_t size = (ssize_t)old_size - wanted_size;
|
||||||
/* keep at least 256K for the buffering */
|
|
||||||
if ((size - extradata_size) < AUDIO_BUFFER_RESERVE)
|
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 */
|
/* 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
|
#ifdef PLAYBACK_VOICE
|
||||||
voice_stop();
|
voice_stop();
|
||||||
#endif
|
#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) */
|
* (now it's the total size, the call will subtract voice etc) */
|
||||||
filebuflen = size;
|
filebuflen = size;
|
||||||
switch (hints & BUFLIB_SHRINK_POS_MASK)
|
switch (hints & BUFLIB_SHRINK_POS_MASK)
|
||||||
{
|
{
|
||||||
case BUFLIB_SHRINK_POS_BACK:
|
case BUFLIB_SHRINK_POS_BACK:
|
||||||
core_shrink(handle, start, size);
|
core_shrink(handle, start, size);
|
||||||
audio_reset_buffer_noalloc(start, buffer_state);
|
audio_reset_buffer_noalloc(start);
|
||||||
break;
|
break;
|
||||||
case BUFLIB_SHRINK_POS_FRONT:
|
case BUFLIB_SHRINK_POS_FRONT:
|
||||||
core_shrink(handle, start + wanted_size, size);
|
core_shrink(handle, start + wanted_size, size);
|
||||||
audio_reset_buffer_noalloc(start + wanted_size,
|
audio_reset_buffer_noalloc(start + wanted_size);
|
||||||
buffer_state);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (playing || play_queued)
|
if (playing || play_queued)
|
||||||
|
@ -882,7 +878,7 @@ static struct buflib_callbacks ops = {
|
||||||
.shrink_callback = shrink_callback,
|
.shrink_callback = shrink_callback,
|
||||||
};
|
};
|
||||||
|
|
||||||
static void audio_reset_buffer(enum audio_buffer_state state)
|
static void audio_reset_buffer(void)
|
||||||
{
|
{
|
||||||
if (audiobuf_handle > 0)
|
if (audiobuf_handle > 0)
|
||||||
{
|
{
|
||||||
|
@ -890,9 +886,13 @@ static void audio_reset_buffer(enum audio_buffer_state state)
|
||||||
audiobuf_handle = 0;
|
audiobuf_handle = 0;
|
||||||
}
|
}
|
||||||
audiobuf_handle = core_alloc_maximum("audiobuf", &filebuflen, &ops);
|
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 */
|
/* 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 ||
|
if (buffer_state != AUDIOBUF_STATE_INITIALIZED ||
|
||||||
!pcmbuf_is_same_size())
|
!pcmbuf_is_same_size())
|
||||||
{
|
{
|
||||||
audio_reset_buffer(AUDIOBUF_STATE_INITIALIZED);
|
audio_reset_buffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
logf("Starting buffer fill");
|
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
|
/* Mark the buffer dirty - if not playing, it will be reset next
|
||||||
time */
|
time */
|
||||||
if (buffer_state == AUDIOBUF_STATE_INITIALIZED)
|
if (buffer_state == AUDIOBUF_STATE_INITIALIZED)
|
||||||
buffer_state = AUDIOBUF_STATE_VOICED_ONLY;
|
buffer_state = AUDIOBUF_STATE_TRASHED;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (old_status != PLAY_STOPPED)
|
if (old_status != PLAY_STOPPED)
|
||||||
|
@ -3511,88 +3511,6 @@ void audio_flush_and_reload_tracks(void)
|
||||||
audio_queue_post(Q_AUDIO_FLUSH, 0);
|
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 --- **/
|
/** --- Miscellaneous public interfaces --- **/
|
||||||
|
|
||||||
#ifdef HAVE_ALBUMART
|
#ifdef HAVE_ALBUMART
|
||||||
|
|
|
@ -80,12 +80,6 @@ void audio_set_cuesheet(bool enable);
|
||||||
void audio_set_crossfade(int enable);
|
void audio_set_crossfade(int enable);
|
||||||
#endif
|
#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);
|
size_t audio_get_filebuflen(void);
|
||||||
|
|
||||||
unsigned int playback_status(void);
|
unsigned int playback_status(void);
|
||||||
|
|
|
@ -104,6 +104,7 @@
|
||||||
#include "rbunicode.h"
|
#include "rbunicode.h"
|
||||||
#include "root_menu.h"
|
#include "root_menu.h"
|
||||||
#include "plugin.h" /* To borrow a temp buffer to rewrite a .m3u8 file */
|
#include "plugin.h" /* To borrow a temp buffer to rewrite a .m3u8 file */
|
||||||
|
#include "panic.h"
|
||||||
|
|
||||||
#define PLAYLIST_CONTROL_FILE_VERSION 2
|
#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));
|
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;
|
store_index = true;
|
||||||
|
|
||||||
while(1)
|
while(1)
|
||||||
|
@ -2077,8 +2071,26 @@ int playlist_create(const char *dir, const char *file)
|
||||||
new_playlist(playlist, dir, file);
|
new_playlist(playlist, dir, file);
|
||||||
|
|
||||||
if (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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -2094,14 +2106,22 @@ int playlist_resume(void)
|
||||||
struct playlist_info* playlist = ¤t_playlist;
|
struct playlist_info* playlist = ¤t_playlist;
|
||||||
char *buffer;
|
char *buffer;
|
||||||
size_t buflen;
|
size_t buflen;
|
||||||
|
int handle;
|
||||||
int nread;
|
int nread;
|
||||||
int total_read = 0;
|
int total_read = 0;
|
||||||
int control_file_size = 0;
|
int control_file_size = 0;
|
||||||
bool first = true;
|
bool first = true;
|
||||||
bool sorted = 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 */
|
/* 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);
|
empty_playlist(playlist, true);
|
||||||
|
|
||||||
|
@ -2110,7 +2130,7 @@ int playlist_resume(void)
|
||||||
if (playlist->control_fd < 0)
|
if (playlist->control_fd < 0)
|
||||||
{
|
{
|
||||||
splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
|
splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
|
||||||
return -1;
|
goto out;
|
||||||
}
|
}
|
||||||
playlist->control_created = true;
|
playlist->control_created = true;
|
||||||
|
|
||||||
|
@ -2118,7 +2138,7 @@ int playlist_resume(void)
|
||||||
if (control_file_size <= 0)
|
if (control_file_size <= 0)
|
||||||
{
|
{
|
||||||
splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
|
splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
|
||||||
return -1;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* read a small amount first to get the header */
|
/* read a small amount first to get the header */
|
||||||
|
@ -2127,14 +2147,14 @@ int playlist_resume(void)
|
||||||
if(nread <= 0)
|
if(nread <= 0)
|
||||||
{
|
{
|
||||||
splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
|
splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
|
||||||
return -1;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
playlist->started = true;
|
playlist->started = true;
|
||||||
|
|
||||||
while (1)
|
while (1)
|
||||||
{
|
{
|
||||||
int result = 0;
|
result = 0;
|
||||||
int count;
|
int count;
|
||||||
enum playlist_command current_command = PLAYLIST_COMMAND_COMMENT;
|
enum playlist_command current_command = PLAYLIST_COMMAND_COMMENT;
|
||||||
int last_newline = 0;
|
int last_newline = 0;
|
||||||
|
@ -2195,7 +2215,10 @@ int playlist_resume(void)
|
||||||
version = atoi(str1);
|
version = atoi(str1);
|
||||||
|
|
||||||
if (version != PLAYLIST_CONTROL_FILE_VERSION)
|
if (version != PLAYLIST_CONTROL_FILE_VERSION)
|
||||||
return -1;
|
{
|
||||||
|
result = -1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
update_playlist_filename(playlist, str2, str3);
|
update_playlist_filename(playlist, str2, str3);
|
||||||
|
|
||||||
|
@ -2204,7 +2227,7 @@ int playlist_resume(void)
|
||||||
/* NOTE: add_indices_to_playlist() overwrites the
|
/* NOTE: add_indices_to_playlist() overwrites the
|
||||||
audiobuf so we need to reload control file
|
audiobuf so we need to reload control file
|
||||||
data */
|
data */
|
||||||
add_indices_to_playlist(playlist, NULL, 0);
|
add_indices_to_playlist(playlist, buffer, buflen);
|
||||||
}
|
}
|
||||||
else if (str2[0] != '\0')
|
else if (str2[0] != '\0')
|
||||||
{
|
{
|
||||||
|
@ -2242,7 +2265,10 @@ int playlist_resume(void)
|
||||||
buffer */
|
buffer */
|
||||||
if (add_track_to_playlist(playlist, str3, position,
|
if (add_track_to_playlist(playlist, str3, position,
|
||||||
queue, total_read+(str3-buffer)) < 0)
|
queue, total_read+(str3-buffer)) < 0)
|
||||||
return -1;
|
{
|
||||||
|
result = -1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
playlist->last_insert_pos = last_position;
|
playlist->last_insert_pos = last_position;
|
||||||
|
|
||||||
|
@ -2264,7 +2290,10 @@ int playlist_resume(void)
|
||||||
|
|
||||||
if (remove_track_from_playlist(playlist, position,
|
if (remove_track_from_playlist(playlist, position,
|
||||||
false) < 0)
|
false) < 0)
|
||||||
return -1;
|
{
|
||||||
|
result = -1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -2291,7 +2320,10 @@ int playlist_resume(void)
|
||||||
|
|
||||||
if (randomise_playlist(playlist, seed, false,
|
if (randomise_playlist(playlist, seed, false,
|
||||||
false) < 0)
|
false) < 0)
|
||||||
return -1;
|
{
|
||||||
|
result = -1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
sorted = false;
|
sorted = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -2308,7 +2340,10 @@ int playlist_resume(void)
|
||||||
playlist->first_index = atoi(str1);
|
playlist->first_index = atoi(str1);
|
||||||
|
|
||||||
if (sort_playlist(playlist, false, false) < 0)
|
if (sort_playlist(playlist, false, false) < 0)
|
||||||
return -1;
|
{
|
||||||
|
result = -1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
sorted = true;
|
sorted = true;
|
||||||
break;
|
break;
|
||||||
|
@ -2421,13 +2456,14 @@ int playlist_resume(void)
|
||||||
if (result < 0)
|
if (result < 0)
|
||||||
{
|
{
|
||||||
splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_INVALID));
|
splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_INVALID));
|
||||||
return result;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (useraborted)
|
if (useraborted)
|
||||||
{
|
{
|
||||||
splash(HZ*2, ID2P(LANG_CANCEL));
|
splash(HZ*2, ID2P(LANG_CANCEL));
|
||||||
return -1;
|
result = -1;
|
||||||
|
goto out;
|
||||||
}
|
}
|
||||||
if (!newline || (exit_loop && count<nread))
|
if (!newline || (exit_loop && count<nread))
|
||||||
{
|
{
|
||||||
|
@ -2435,7 +2471,8 @@ int playlist_resume(void)
|
||||||
{
|
{
|
||||||
/* no newline at end of control file */
|
/* no newline at end of control file */
|
||||||
splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_INVALID));
|
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.
|
/* 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);
|
queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return 0;
|
out:
|
||||||
|
core_free(handle);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -801,6 +801,8 @@ static const struct plugin_api rockbox_api = {
|
||||||
the API gets incompatible */
|
the API gets incompatible */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static int plugin_buffer_handle;
|
||||||
|
|
||||||
int plugin_load(const char* plugin, const void* parameter)
|
int plugin_load(const char* plugin, const void* parameter)
|
||||||
{
|
{
|
||||||
struct plugin_header *p_hdr;
|
struct plugin_header *p_hdr;
|
||||||
|
@ -815,6 +817,8 @@ int plugin_load(const char* plugin, const void* parameter)
|
||||||
}
|
}
|
||||||
lc_close(current_plugin_handle);
|
lc_close(current_plugin_handle);
|
||||||
current_plugin_handle = pfn_tsr_exit = NULL;
|
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));
|
splash(0, ID2P(LANG_WAIT));
|
||||||
|
@ -878,6 +882,9 @@ int plugin_load(const char* plugin, const void* parameter)
|
||||||
touchscreen_set_mode(TOUCHSCREEN_BUTTON);
|
touchscreen_set_mode(TOUCHSCREEN_BUTTON);
|
||||||
#endif
|
#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
|
#ifdef HAVE_PLUGIN_CHECK_OPEN_CLOSE
|
||||||
open_files = 0;
|
open_files = 0;
|
||||||
#endif
|
#endif
|
||||||
|
@ -891,8 +898,12 @@ int plugin_load(const char* plugin, const void* parameter)
|
||||||
{ /* close handle if plugin is no tsr one */
|
{ /* close handle if plugin is no tsr one */
|
||||||
lc_close(current_plugin_handle);
|
lc_close(current_plugin_handle);
|
||||||
current_plugin_handle = NULL;
|
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 */
|
/* Go back to the global setting in case the plugin changed it */
|
||||||
#ifdef HAVE_TOUCHSCREEN
|
#ifdef HAVE_TOUCHSCREEN
|
||||||
touchscreen_set_mode(global_settings.touch_mode);
|
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)
|
void* plugin_get_audio_buffer(size_t *buffer_size)
|
||||||
{
|
{
|
||||||
audio_stop();
|
/* dummy ops with no callbacks, needed because by
|
||||||
return audio_get_buffer(true, buffer_size);
|
* 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.
|
/* The plugin wants to stay resident after leaving its main function, e.g.
|
||||||
|
|
|
@ -112,6 +112,7 @@ void* plugin_get_buffer(size_t *buffer_size);
|
||||||
#include "timefuncs.h"
|
#include "timefuncs.h"
|
||||||
#include "crc32.h"
|
#include "crc32.h"
|
||||||
#include "rbpaths.h"
|
#include "rbpaths.h"
|
||||||
|
#include "core_alloc.h"
|
||||||
|
|
||||||
#ifdef HAVE_ALBUMART
|
#ifdef HAVE_ALBUMART
|
||||||
#include "albumart.h"
|
#include "albumart.h"
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
#include "kernel.h"
|
#include "kernel.h"
|
||||||
#include "string-extra.h"
|
#include "string-extra.h"
|
||||||
#include "filefuncs.h"
|
#include "filefuncs.h"
|
||||||
|
#include "core_alloc.h"
|
||||||
|
|
||||||
#define MAX_RADIOART_IMAGES 10
|
#define MAX_RADIOART_IMAGES 10
|
||||||
struct radioart {
|
struct radioart {
|
||||||
|
@ -158,14 +159,49 @@ static void buffer_reset_handler(void *data)
|
||||||
(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)
|
void radioart_init(bool entering_screen)
|
||||||
{
|
{
|
||||||
if (entering_screen)
|
if (entering_screen)
|
||||||
{
|
{
|
||||||
/* grab control over buffering */
|
/* grab control over buffering */
|
||||||
size_t bufsize;
|
size_t bufsize;
|
||||||
buf = audio_get_buffer(false, &bufsize);
|
int handle = core_alloc_maximum("radioart", &bufsize, &radioart_ops);
|
||||||
buffering_reset(buf, bufsize);
|
buffering_reset(core_get_data(handle), bufsize);
|
||||||
|
buf = core_get_data(handle);
|
||||||
/* one-shot */
|
/* one-shot */
|
||||||
add_event(BUFFER_EVENT_BUFFER_RESET, true, buffer_reset_handler);
|
add_event(BUFFER_EVENT_BUFFER_RESET, true, buffer_reset_handler);
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "system.h"
|
#include "system.h"
|
||||||
#include "kernel.h"
|
#include "kernel.h"
|
||||||
|
#include "panic.h"
|
||||||
#include "string-extra.h"
|
#include "string-extra.h"
|
||||||
#include "pcm_record.h"
|
#include "pcm_record.h"
|
||||||
#include "codecs.h"
|
#include "codecs.h"
|
||||||
|
@ -40,6 +41,8 @@
|
||||||
#include "spdif.h"
|
#include "spdif.h"
|
||||||
#endif
|
#endif
|
||||||
#include "audio_thread.h"
|
#include "audio_thread.h"
|
||||||
|
#include "core_alloc.h"
|
||||||
|
#include "talk.h"
|
||||||
|
|
||||||
/* Macros to enable logf for queues
|
/* Macros to enable logf for queues
|
||||||
logging on SYS_TIMEOUT can be disabled */
|
logging on SYS_TIMEOUT can be disabled */
|
||||||
|
@ -1402,11 +1405,22 @@ static void tally_prerecord_data(void)
|
||||||
|
|
||||||
/** Event handlers for recording thread **/
|
/** Event handlers for recording thread **/
|
||||||
|
|
||||||
|
static int pcmrec_handle;
|
||||||
/* Q_AUDIO_INIT_RECORDING */
|
/* Q_AUDIO_INIT_RECORDING */
|
||||||
static void on_init_recording(void)
|
static void on_init_recording(void)
|
||||||
{
|
{
|
||||||
send_event(RECORDING_EVENT_START, NULL);
|
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_rec_buffers();
|
||||||
init_state();
|
init_state();
|
||||||
pcm_init_recording();
|
pcm_init_recording();
|
||||||
|
@ -1430,6 +1444,10 @@ static void on_close_recording(void)
|
||||||
audio_set_output_source(AUDIO_SRC_PLAYBACK);
|
audio_set_output_source(AUDIO_SRC_PLAYBACK);
|
||||||
pcm_apply_settings();
|
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);
|
send_event(RECORDING_EVENT_STOP, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -691,15 +691,8 @@ void rec_set_source(int source, unsigned flags)
|
||||||
|
|
||||||
void rec_set_recording_options(struct audio_recording_options *options)
|
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,
|
rec_set_source(options->rec_source,
|
||||||
options->rec_source_flags | SRCF_RECORDING);
|
options->rec_source_flags | SRCF_RECORDING);
|
||||||
#endif /* CONFIG_CODEC != SWCODEC */
|
|
||||||
|
|
||||||
audio_set_recording_options(options);
|
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 */
|
/* steal mp3 buffer, create unique filename and start recording */
|
||||||
pm_reset_clipcount();
|
pm_reset_clipcount();
|
||||||
pm_activate_clipcount(true);
|
pm_activate_clipcount(true);
|
||||||
#if CONFIG_CODEC != SWCODEC
|
|
||||||
talk_buffer_steal(); /* we use the mp3 buffer */
|
|
||||||
#endif
|
|
||||||
audio_record(rec_create_filename(path_buffer));
|
audio_record(rec_create_filename(path_buffer));
|
||||||
break;
|
break;
|
||||||
case RECORDING_CMD_START_NEWFILE:
|
case RECORDING_CMD_START_NEWFILE:
|
||||||
|
|
771
apps/talk.c
771
apps/talk.c
File diff suppressed because it is too large
Load diff
21
apps/talk.h
21
apps/talk.h
|
@ -71,6 +71,26 @@ enum {
|
||||||
/* convenience macro to have both virtual pointer and ID as arguments */
|
/* convenience macro to have both virtual pointer and ID as arguments */
|
||||||
#define STR(id) ID2P(id), id
|
#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) */
|
/* 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 dir_thumbnail_name; /* "_dirname.talk" */
|
||||||
extern const char* const file_thumbnail_ext; /* ".talk" for file voicing */
|
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
|
#endif
|
||||||
int talk_get_bufsize(void); /* get the loaded voice file size */
|
int talk_get_bufsize(void); /* get the loaded voice file size */
|
||||||
size_t talkbuf_init(char* bufstart);
|
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? */
|
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 */
|
int talk_id(int32_t id, bool enqueue); /* play a voice ID from voicefont */
|
||||||
/* play a thumbnail from file */
|
/* play a thumbnail from file */
|
||||||
|
|
|
@ -75,10 +75,6 @@ void audio_error_clear(void);
|
||||||
int audio_get_file_pos(void);
|
int audio_get_file_pos(void);
|
||||||
void audio_beep(int duration);
|
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
|
#if CONFIG_CODEC == SWCODEC
|
||||||
void audio_next_dir(void);
|
void audio_next_dir(void);
|
||||||
void audio_prev_dir(void);
|
void audio_prev_dir(void);
|
||||||
|
|
|
@ -230,21 +230,6 @@ void usb_insert_int(void)
|
||||||
}
|
}
|
||||||
#endif /* USB_STATUS_BY_EVENT */
|
#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)
|
void usb_drv_int_enable(bool enable)
|
||||||
{
|
{
|
||||||
/* enable/disable USB IRQ in CPU */
|
/* enable/disable USB IRQ in CPU */
|
||||||
|
|
|
@ -34,6 +34,7 @@
|
||||||
#if CONFIG_RTC
|
#if CONFIG_RTC
|
||||||
#include "timefuncs.h"
|
#include "timefuncs.h"
|
||||||
#endif
|
#endif
|
||||||
|
#include "core_alloc.h"
|
||||||
|
|
||||||
#ifdef USB_USE_RAMDISK
|
#ifdef USB_USE_RAMDISK
|
||||||
#define RAMDISK_SIZE 2048
|
#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);
|
return (dest - orig_dest);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int usb_handle;
|
||||||
void usb_storage_init_connection(void)
|
void usb_storage_init_connection(void)
|
||||||
{
|
{
|
||||||
logf("ums: set config");
|
logf("ums: set config");
|
||||||
|
@ -452,13 +454,17 @@ void usb_storage_init_connection(void)
|
||||||
#else
|
#else
|
||||||
/* TODO : check if bufsize is at least 32K ? */
|
/* TODO : check if bufsize is at least 32K ? */
|
||||||
size_t bufsize;
|
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
|
#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
|
#else
|
||||||
cbw_buffer = (void *)((unsigned int)(audio_buffer+31) & 0xffffffe0);
|
cbw_buffer = (void *)((unsigned int)(buffer+31) & 0xffffffe0);
|
||||||
#endif
|
#endif
|
||||||
tb.transfer_buffer = cbw_buffer + MAX_CBW_SIZE;
|
tb.transfer_buffer = cbw_buffer + MAX_CBW_SIZE;
|
||||||
commit_discard_dcache();
|
commit_discard_dcache();
|
||||||
|
@ -478,7 +484,8 @@ void usb_storage_init_connection(void)
|
||||||
|
|
||||||
void usb_storage_disconnect(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() */
|
/* called by usb_core_transfer_complete() */
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue