summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Sevakis <jethead71@rockbox.org>2008-12-13 01:32:39 +0000
committerMichael Sevakis <jethead71@rockbox.org>2008-12-13 01:32:39 +0000
commit5e73f9ff2087cc82705b49fc813a6b60633ac756 (patch)
treef0eb330476561208ca0a8f7f91bb879c96d85234
parentbdf3004f966b7095b2b404bea9f15f18c9de8918 (diff)
downloadrockbox-5e73f9ff2087cc82705b49fc813a6b60633ac756.tar.gz
rockbox-5e73f9ff2087cc82705b49fc813a6b60633ac756.tar.bz2
rockbox-5e73f9ff2087cc82705b49fc813a6b60633ac756.zip
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
-rw-r--r--apps/pcmbuf.c59
-rw-r--r--apps/playback.c42
-rw-r--r--firmware/export/audio.h10
3 files changed, 74 insertions, 37 deletions
diff --git a/apps/pcmbuf.c b/apps/pcmbuf.c
index 695dfc55dc..5b9e7270cc 100644
--- a/apps/pcmbuf.c
+++ b/apps/pcmbuf.c
@@ -959,23 +959,37 @@ bool pcmbuf_insert_buffer(char *buf, int count)
in Hertz for a duration in milliseconds. */
void pcmbuf_beep(unsigned int frequency, size_t duration, int amplitude)
{
- int i;
- unsigned int step = 0xffffffffu / NATIVE_FREQUENCY * frequency;
- int32_t phase = 0;
- int samples = NATIVE_FREQUENCY / 1000 * duration;
+ unsigned int step;
+ int32_t phase;
+ int16_t *bufptr, *bufstart, *bufend;
int32_t sample;
- int16_t *bufstart;
- int16_t *bufptr;
- int16_t *pcmbuf_end = (int16_t *)fadebuf;
- bool mix = pcmbuf_read != NULL && pcmbuf_read->link != NULL;
+ int nsamples;
+ bool mix;
+ int i;
+
+ 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 */
if (mix)
{
- /* Get the next chunk */
- char *pcmbuf_mix_buf = pcmbuf_read->link->addr;
- /* Give 1/8s clearance. */
- bufstart = (int16_t *)&pcmbuf_mix_buf[NATIVE_FREQUENCY * 4 / 8];
+ /* Get the currently playing chunk at the current position. */
+ bufstart = (int16_t *)pcm_play_dma_get_peak_buffer(&i);
+
+ 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
{
@@ -983,28 +997,39 @@ void pcmbuf_beep(unsigned int frequency, size_t duration, int amplitude)
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;
- 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;
sample = mix ? *bufptr : 0;
*bufptr++ = clip_sample_16(sample + amp);
- if (bufptr >= pcmbuf_end)
+ if (bufptr >= bufend)
bufptr = (int16_t *)audiobuffer;
sample = mix ? *bufptr : 0;
*bufptr++ = clip_sample_16(sample + amp);
- if (bufptr >= pcmbuf_end)
+ if (bufptr >= bufend)
bufptr = (int16_t *)audiobuffer;
phase += step;
}
+ pcm_play_lock();
+
/* Kick off playback if required */
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 */
diff --git a/apps/playback.c b/apps/playback.c
index 3aed12e918..d2d9bb6a34 100644
--- a/apps/playback.c
+++ b/apps/playback.c
@@ -204,10 +204,7 @@ size_t filebuflen = 0; /* Size of buffer (A/C-) */
/* FIXME: make buf_ridx (C/A-) */
/* Possible arrangements of the buffer */
-#define BUFFER_STATE_TRASHED -1 /* trashed; must be reset */
-#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 */
+static int buffer_state = AUDIOBUF_STATE_TRASHED; /* Buffer state */
/* Used to keep the WPS up-to-date during track transtition */
static struct mp3entry prevtrack_id3;
@@ -419,11 +416,11 @@ bool audio_restore_playback(int type)
switch (type)
{
case AUDIO_WANT_PLAYBACK:
- if (buffer_state != BUFFER_STATE_INITIALIZED)
+ if (buffer_state != AUDIOBUF_STATE_INITIALIZED)
audio_reset_buffer();
return true;
case AUDIO_WANT_VOICE:
- if (buffer_state == BUFFER_STATE_TRASHED)
+ if (buffer_state == AUDIOBUF_STATE_TRASHED)
audio_reset_buffer();
return true;
default:
@@ -439,32 +436,32 @@ unsigned char *audio_get_buffer(bool talk_buf, size_t *buffer_size)
{
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)
{
/* Special case for talk_init to use since it already knows it's
trashed */
- buffer_state = BUFFER_STATE_TRASHED;
+ buffer_state = AUDIOBUF_STATE_TRASHED;
return NULL;
}
- if (talk_buf || buffer_state == BUFFER_STATE_TRASHED
+ if (talk_buf || buffer_state == AUDIOBUF_STATE_TRASHED
|| !talk_voice_required())
{
logf("get buffer: talk, audio");
/* Ok to use everything from audiobuf to audiobufend - voice is loaded,
the talk buffer is not needed because voice isn't being used, or
- could be BUFFER_STATE_TRASHED already. If state is
- BUFFER_STATE_VOICED_ONLY, no problem as long as memory isn't written
+ 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 != BUFFER_STATE_TRASHED)
+ if (buffer_state != AUDIOBUF_STATE_TRASHED)
{
talk_buffer_steal();
- buffer_state = BUFFER_STATE_TRASHED;
+ buffer_state = AUDIOBUF_STATE_TRASHED;
}
buf = audiobuf;
@@ -472,15 +469,15 @@ unsigned char *audio_get_buffer(bool talk_buf, size_t *buffer_size)
}
else
{
- /* Safe to just return this if already BUFFER_STATE_VOICED_ONLY or
- still BUFFER_STATE_INITIALIZED */
+ /* 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 */
logf("get buffer: audio");
buf = audiobuf + talk_get_bufsize();
end = audiobufend - pcmbuf_init(audiobufend);
- buffer_state = BUFFER_STATE_VOICED_ONLY;
+ buffer_state = AUDIOBUF_STATE_VOICED_ONLY;
}
*buffer_size = end - buf;
@@ -488,6 +485,11 @@ unsigned char *audio_get_buffer(bool talk_buf, size_t *buffer_size)
return buf;
}
+int audio_buffer_state(void)
+{
+ return buffer_state;
+}
+
#ifdef HAVE_RECORDING
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();
unsigned char *end = audiobufend;
- buffer_state = BUFFER_STATE_TRASHED;
+ buffer_state = AUDIOBUF_STATE_TRASHED;
*buffer_size = end - 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
file size shouldn't have changed so we can go straight from
- BUFFER_STATE_VOICED_ONLY to BUFFER_STATE_INITIALIZED */
- if (buffer_state != BUFFER_STATE_INITIALIZED)
+ AUDIOBUF_STATE_VOICED_ONLY to AUDIOBUF_STATE_INITIALIZED */
+ if (buffer_state != AUDIOBUF_STATE_INITIALIZED)
audio_reset_buffer();
logf("Starting buffer fill");
@@ -2338,7 +2340,7 @@ static void audio_reset_buffer(void)
buffering_reset(filebuf, filebuflen);
/* 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)
/* Make sure everything adds up - yes, some info is a bit redundant but
diff --git a/firmware/export/audio.h b/firmware/export/audio.h
index 4f9ef1a4e1..b4a2c82200 100644
--- a/firmware/export/audio.h
+++ b/firmware/export/audio.h
@@ -104,6 +104,16 @@ void audio_init_playback(void);
unsigned char *audio_get_buffer(bool talk_buf, size_t *buffer_size);
/* 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 */
enum rec_channel_modes
{