Fix FS#8660-Recording hardlocks with keyclick enabled and FS#9388-keyclicks too late. Introduce audio_buffer_state to check whether PCM buffer is useable or disabled (trashed).

git-svn-id: svn://svn.rockbox.org/rockbox/trunk@19411 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
Michael Sevakis 2008-12-13 01:32:39 +00:00
parent bdf3004f96
commit 5e73f9ff20
3 changed files with 74 additions and 37 deletions

View file

@ -959,23 +959,37 @@ bool pcmbuf_insert_buffer(char *buf, int count)
in Hertz for a duration in milliseconds. */ in Hertz for a duration in milliseconds. */
void pcmbuf_beep(unsigned int frequency, size_t duration, int amplitude) void pcmbuf_beep(unsigned int frequency, size_t duration, int amplitude)
{ {
int i; unsigned int step;
unsigned int step = 0xffffffffu / NATIVE_FREQUENCY * frequency; int32_t phase;
int32_t phase = 0; int16_t *bufptr, *bufstart, *bufend;
int samples = NATIVE_FREQUENCY / 1000 * duration;
int32_t sample; int32_t sample;
int16_t *bufstart; int nsamples;
int16_t *bufptr; bool mix;
int16_t *pcmbuf_end = (int16_t *)fadebuf; int i;
bool mix = pcmbuf_read != NULL && pcmbuf_read->link != NULL;
if (audio_buffer_state() == AUDIOBUF_STATE_TRASHED)
return; /* No voice or playback = no beeping. */
phase = 0;
step = 0xffffffffu / NATIVE_FREQUENCY * frequency;
nsamples = NATIVE_FREQUENCY / 1000 * duration;
mix = pcmbuf_read != NULL && pcmbuf_read->link != NULL;
/* Find the insertion point and set bufstart to the start of it */ /* Find the insertion point and set bufstart to the start of it */
if (mix) if (mix)
{ {
/* Get the next chunk */ /* Get the currently playing chunk at the current position. */
char *pcmbuf_mix_buf = pcmbuf_read->link->addr; bufstart = (int16_t *)pcm_play_dma_get_peak_buffer(&i);
/* Give 1/8s clearance. */
bufstart = (int16_t *)&pcmbuf_mix_buf[NATIVE_FREQUENCY * 4 / 8]; if (!bufstart)
return; /* If above isn't implemented, no beepeth */
/* Give 5ms clearance. */
bufstart += NATIVE_FREQUENCY * 4 / 200;
/* NOTE: On some targets using hardware DMA, cache range flushing may
* be required or the writes may not be picked up by the controller.
* An incremental flush should be done periodically during the mixdown. */
} }
else else
{ {
@ -983,28 +997,39 @@ void pcmbuf_beep(unsigned int frequency, size_t duration, int amplitude)
bufstart = (int16_t *)audiobuffer; bufstart = (int16_t *)audiobuffer;
} }
/* Mix square wave into buffer */ bufend = (int16_t *)SKIPBYTES(audiobuffer, pcmbuf_size);
/* Wrapped above? */
if (bufstart >= bufend)
bufstart -= pcmbuf_size;
bufptr = bufstart; bufptr = bufstart;
for (i = 0; i < samples; ++i)
/* Mix square wave into buffer */
for (i = 0; i < nsamples; ++i)
{ {
int32_t amp = (phase >> 31) ^ (int32_t)amplitude; int32_t amp = (phase >> 31) ^ (int32_t)amplitude;
sample = mix ? *bufptr : 0; sample = mix ? *bufptr : 0;
*bufptr++ = clip_sample_16(sample + amp); *bufptr++ = clip_sample_16(sample + amp);
if (bufptr >= pcmbuf_end) if (bufptr >= bufend)
bufptr = (int16_t *)audiobuffer; bufptr = (int16_t *)audiobuffer;
sample = mix ? *bufptr : 0; sample = mix ? *bufptr : 0;
*bufptr++ = clip_sample_16(sample + amp); *bufptr++ = clip_sample_16(sample + amp);
if (bufptr >= pcmbuf_end) if (bufptr >= bufend)
bufptr = (int16_t *)audiobuffer; bufptr = (int16_t *)audiobuffer;
phase += step; phase += step;
} }
pcm_play_lock();
/* Kick off playback if required */ /* Kick off playback if required */
if (!pcm_is_playing()) if (!pcm_is_playing())
{ {
pcm_play_data(NULL, (unsigned char *)bufstart, samples * 4); pcm_play_data(NULL, (unsigned char *)bufstart, nsamples * 4);
} }
pcm_play_unlock();
} }
#endif /* HAVE_HARDWARE_BEEP */ #endif /* HAVE_HARDWARE_BEEP */

View file

@ -204,10 +204,7 @@ size_t filebuflen = 0; /* Size of buffer (A/C-) */
/* FIXME: make buf_ridx (C/A-) */ /* FIXME: make buf_ridx (C/A-) */
/* Possible arrangements of the buffer */ /* Possible arrangements of the buffer */
#define BUFFER_STATE_TRASHED -1 /* trashed; must be reset */ static int buffer_state = AUDIOBUF_STATE_TRASHED; /* Buffer state */
#define BUFFER_STATE_INITIALIZED 0 /* voice+audio OR audio-only */
#define BUFFER_STATE_VOICED_ONLY 1 /* voice-only */
static int buffer_state = BUFFER_STATE_TRASHED; /* Buffer state */
/* Used to keep the WPS up-to-date during track transtition */ /* Used to keep the WPS up-to-date during track transtition */
static struct mp3entry prevtrack_id3; static struct mp3entry prevtrack_id3;
@ -419,11 +416,11 @@ bool audio_restore_playback(int type)
switch (type) switch (type)
{ {
case AUDIO_WANT_PLAYBACK: case AUDIO_WANT_PLAYBACK:
if (buffer_state != BUFFER_STATE_INITIALIZED) if (buffer_state != AUDIOBUF_STATE_INITIALIZED)
audio_reset_buffer(); audio_reset_buffer();
return true; return true;
case AUDIO_WANT_VOICE: case AUDIO_WANT_VOICE:
if (buffer_state == BUFFER_STATE_TRASHED) if (buffer_state == AUDIOBUF_STATE_TRASHED)
audio_reset_buffer(); audio_reset_buffer();
return true; return true;
default: default:
@ -439,32 +436,32 @@ unsigned char *audio_get_buffer(bool talk_buf, size_t *buffer_size)
{ {
audio_hard_stop(); audio_hard_stop();
} }
/* else buffer_state will be BUFFER_STATE_TRASHED at this point */ /* else buffer_state will be AUDIOBUF_STATE_TRASHED at this point */
if (buffer_size == NULL) if (buffer_size == NULL)
{ {
/* Special case for talk_init to use since it already knows it's /* Special case for talk_init to use since it already knows it's
trashed */ trashed */
buffer_state = BUFFER_STATE_TRASHED; buffer_state = AUDIOBUF_STATE_TRASHED;
return NULL; return NULL;
} }
if (talk_buf || buffer_state == BUFFER_STATE_TRASHED if (talk_buf || buffer_state == AUDIOBUF_STATE_TRASHED
|| !talk_voice_required()) || !talk_voice_required())
{ {
logf("get buffer: talk, audio"); logf("get buffer: talk, audio");
/* Ok to use everything from audiobuf to audiobufend - voice is loaded, /* Ok to use everything from audiobuf to audiobufend - voice is loaded,
the talk buffer is not needed because voice isn't being used, or the talk buffer is not needed because voice isn't being used, or
could be BUFFER_STATE_TRASHED already. If state is could be AUDIOBUF_STATE_TRASHED already. If state is
BUFFER_STATE_VOICED_ONLY, no problem as long as memory isn't written AUDIOBUF_STATE_VOICED_ONLY, no problem as long as memory isn't written
without the caller knowing what's going on. Changing certain settings 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 may move it to a worse condition but the memory in use by something
else will remain undisturbed. else will remain undisturbed.
*/ */
if (buffer_state != BUFFER_STATE_TRASHED) if (buffer_state != AUDIOBUF_STATE_TRASHED)
{ {
talk_buffer_steal(); talk_buffer_steal();
buffer_state = BUFFER_STATE_TRASHED; buffer_state = AUDIOBUF_STATE_TRASHED;
} }
buf = audiobuf; buf = audiobuf;
@ -472,15 +469,15 @@ unsigned char *audio_get_buffer(bool talk_buf, size_t *buffer_size)
} }
else else
{ {
/* Safe to just return this if already BUFFER_STATE_VOICED_ONLY or /* Safe to just return this if already AUDIOBUF_STATE_VOICED_ONLY or
still BUFFER_STATE_INITIALIZED */ still AUDIOBUF_STATE_INITIALIZED */
/* Skip talk buffer and move pcm buffer to end to maximize available /* Skip talk buffer and move pcm buffer to end to maximize available
contiguous memory - no audio running means voice will not need the contiguous memory - no audio running means voice will not need the
swap space */ swap space */
logf("get buffer: audio"); logf("get buffer: audio");
buf = audiobuf + talk_get_bufsize(); buf = audiobuf + talk_get_bufsize();
end = audiobufend - pcmbuf_init(audiobufend); end = audiobufend - pcmbuf_init(audiobufend);
buffer_state = BUFFER_STATE_VOICED_ONLY; buffer_state = AUDIOBUF_STATE_VOICED_ONLY;
} }
*buffer_size = end - buf; *buffer_size = end - buf;
@ -488,6 +485,11 @@ unsigned char *audio_get_buffer(bool talk_buf, size_t *buffer_size)
return buf; return buf;
} }
int audio_buffer_state(void)
{
return buffer_state;
}
#ifdef HAVE_RECORDING #ifdef HAVE_RECORDING
unsigned char *audio_get_recording_buffer(size_t *buffer_size) unsigned char *audio_get_recording_buffer(size_t *buffer_size)
{ {
@ -496,7 +498,7 @@ unsigned char *audio_get_recording_buffer(size_t *buffer_size)
talk_buffer_steal(); talk_buffer_steal();
unsigned char *end = audiobufend; unsigned char *end = audiobufend;
buffer_state = BUFFER_STATE_TRASHED; buffer_state = AUDIOBUF_STATE_TRASHED;
*buffer_size = end - audiobuf; *buffer_size = end - audiobuf;
return (unsigned char *)audiobuf; return (unsigned char *)audiobuf;
@ -1894,8 +1896,8 @@ static void audio_fill_file_buffer(bool start_play, size_t offset)
/* Must reset the buffer before use if trashed or voice only - voice /* Must reset the buffer before use if trashed or voice only - voice
file size shouldn't have changed so we can go straight from file size shouldn't have changed so we can go straight from
BUFFER_STATE_VOICED_ONLY to BUFFER_STATE_INITIALIZED */ AUDIOBUF_STATE_VOICED_ONLY to AUDIOBUF_STATE_INITIALIZED */
if (buffer_state != BUFFER_STATE_INITIALIZED) if (buffer_state != AUDIOBUF_STATE_INITIALIZED)
audio_reset_buffer(); audio_reset_buffer();
logf("Starting buffer fill"); logf("Starting buffer fill");
@ -2338,7 +2340,7 @@ static void audio_reset_buffer(void)
buffering_reset(filebuf, filebuflen); buffering_reset(filebuf, filebuflen);
/* Clear any references to the file buffer */ /* Clear any references to the file buffer */
buffer_state = BUFFER_STATE_INITIALIZED; buffer_state = AUDIOBUF_STATE_INITIALIZED;
#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

View file

@ -104,6 +104,16 @@ void audio_init_playback(void);
unsigned char *audio_get_buffer(bool talk_buf, size_t *buffer_size); unsigned char *audio_get_buffer(bool talk_buf, size_t *buffer_size);
/* only implemented in playback.c, but called from firmware */ /* only implemented in playback.c, but called from firmware */
#if CONFIG_CODEC == SWCODEC
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 */
};
int audio_buffer_state(void);
#endif
/* channel modes */ /* channel modes */
enum rec_channel_modes enum rec_channel_modes
{ {