mirror of
https://github.com/Rockbox/rockbox.git
synced 2025-12-10 21:55:10 -05:00
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:
parent
bdf3004f96
commit
5e73f9ff20
3 changed files with 74 additions and 37 deletions
|
|
@ -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 */
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue