diff options
-rw-r--r-- | apps/pcmbuf.c | 59 | ||||
-rw-r--r-- | apps/playback.c | 42 | ||||
-rw-r--r-- | firmware/export/audio.h | 10 |
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 { |