summaryrefslogtreecommitdiffstats
path: root/apps
diff options
context:
space:
mode:
Diffstat (limited to 'apps')
-rw-r--r--apps/SOURCES3
-rw-r--r--apps/action.c2
-rw-r--r--apps/beep.c142
-rw-r--r--apps/gui/wps.c16
-rw-r--r--apps/misc.c5
-rw-r--r--apps/misc.h3
-rw-r--r--apps/pcmbuf.c319
-rw-r--r--apps/pcmbuf.h3
-rw-r--r--apps/playback.c20
-rw-r--r--apps/plugin.c5
-rw-r--r--apps/plugin.h10
-rw-r--r--apps/plugins/fft/fft.c13
-rw-r--r--apps/recorder/keyboard.c2
-rw-r--r--apps/voice_thread.c279
-rw-r--r--apps/voice_thread.h5
15 files changed, 468 insertions, 359 deletions
diff --git a/apps/SOURCES b/apps/SOURCES
index c122427900..075ca9a563 100644
--- a/apps/SOURCES
+++ b/apps/SOURCES
@@ -170,6 +170,9 @@ codec_thread.c
playback.c
codecs.c
dsp.c
+#ifndef HAVE_HARDWARE_BEEP
+beep.c
+#endif
#ifdef HAVE_PITCHSCREEN
tdspeed.c
#endif
diff --git a/apps/action.c b/apps/action.c
index eb5950bb70..aa19403703 100644
--- a/apps/action.c
+++ b/apps/action.c
@@ -205,7 +205,7 @@ static int get_action_worker(int context, int timeout,
/* Produce keyclick */
if (global_settings.keyclick && !(button & BUTTON_REL))
if (!(button & BUTTON_REPEAT) || global_settings.keyclick_repeats)
- pcmbuf_beep(4000, KEYCLICK_DURATION, 2500*global_settings.keyclick);
+ beep_play(4000, KEYCLICK_DURATION, 2500*global_settings.keyclick);
#endif
if ((context != last_context) && ((last_button & BUTTON_REL) == 0)
diff --git a/apps/beep.c b/apps/beep.c
new file mode 100644
index 0000000000..716847263e
--- /dev/null
+++ b/apps/beep.c
@@ -0,0 +1,142 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (c) 2011 Michael Sevakis
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#include "config.h"
+#include "system.h"
+#include "settings.h"
+#include "dsp.h"
+#include "pcm.h"
+#include "pcm_mixer.h"
+#include "misc.h"
+
+static int32_t beep_phase; /* Phase of square wave generator */
+static uint32_t beep_step; /* Step of square wave generator on each sample */
+static uint32_t beep_amplitude; /* Amplitude of square wave generator */
+static int beep_count; /* Number of samples remaining to generate */
+
+/* Reserve enough static space for keyclick to fit */
+#define BEEP_BUF_COUNT (NATIVE_FREQUENCY / 1000 * KEYCLICK_DURATION)
+static uint32_t beep_buf[BEEP_BUF_COUNT] IBSS_ATTR;
+
+/* Actually output samples into beep_buf */
+#if defined(CPU_ARM)
+static FORCE_INLINE void beep_generate(int count)
+{
+ uint32_t *out = beep_buf;
+ uint32_t s;
+
+ asm volatile (
+ "1: \n"
+ "eor %3, %5, %1, asr #31 \n"
+ "subs %2, %2, #1 \n"
+ "str %3, [%0], #4 \n"
+ "add %1, %1, %4 \n"
+ "bgt 1b \n"
+ : "+r"(out), "+r"(beep_phase), "+r"(count),
+ "=&r"(s)
+ : "r"(beep_step), "r"(beep_amplitude));
+}
+#elif defined (CPU_COLDFIRE)
+static FORCE_INLINE void beep_generate(int count)
+{
+ uint32_t *out = beep_buf;
+ uint32_t s;
+
+ asm volatile (
+ "1: \n"
+ "move.l %1, %3 \n"
+ "add.l %4, %1 \n"
+ "add.l %3, %3 \n"
+ "subx.l %3, %3 \n"
+ "eor.l %5, %3 \n"
+ "move.l %3, (%0)+ \n"
+ "subq.l #1, %2 \n"
+ "bgt.b 1b \n"
+ : "+a"(out), "+d"(beep_phase), "+d"(count),
+ "=&d"(s)
+ : "r"(beep_step), "d"(beep_amplitude));
+}
+#else
+static FORCE_INLINE void beep_generate(int count)
+{
+ uint32_t *out = beep_buf;
+ uint32_t amplitude = beep_amplitude;
+ uint32_t step = beep_step;
+ int32_t phase = beep_phase;
+
+ do
+ {
+ *out++ = (phase >> 31) ^ amplitude;
+ phase += step;
+ }
+ while (--count > 0);
+
+ beep_phase = phase;
+}
+#endif
+
+/* Callback to generate the beep frames - also don't want inlining of
+ call below in beep_play */
+static void __attribute__((noinline)) ICODE_ATTR
+beep_get_more(unsigned char **start, size_t *size)
+{
+ int count = beep_count;
+
+ if (count > 0)
+ {
+ count = MIN(count, BEEP_BUF_COUNT);
+ beep_count -= count;
+ *start = (unsigned char *)beep_buf;
+ *size = count * sizeof(uint32_t);
+ beep_generate(count);
+ }
+}
+
+/* Generates a constant square wave sound with a given frequency in Hertz for
+ a duration in milliseconds */
+void beep_play(unsigned int frequency, unsigned int duration,
+ unsigned int amplitude)
+{
+ mixer_channel_stop(PCM_MIXER_CHAN_BEEP);
+
+ if (frequency == 0 || duration == 0 || amplitude == 0)
+ return;
+
+ if (amplitude > INT16_MAX)
+ amplitude = INT16_MAX;
+
+ /* Setup the parameters for the square wave generator */
+ beep_phase = 0;
+ beep_step = 0xffffffffu / NATIVE_FREQUENCY * frequency;
+ beep_count = NATIVE_FREQUENCY / 1000 * duration;
+ beep_amplitude = amplitude | (amplitude << 16); /* Word:|AMP16|AMP16| */
+
+ /* If it fits - avoid cb overhead */
+ unsigned char *start;
+ size_t size;
+
+ /* Generate first frame here */
+ beep_get_more(&start, &size);
+
+ mixer_channel_set_amplitude(PCM_MIXER_CHAN_BEEP, MIX_AMP_UNITY);
+ mixer_channel_play_data(PCM_MIXER_CHAN_BEEP,
+ beep_count ? beep_get_more : NULL,
+ start, size);
+}
diff --git a/apps/gui/wps.c b/apps/gui/wps.c
index e686fcc533..cbf003adbd 100644
--- a/apps/gui/wps.c
+++ b/apps/gui/wps.c
@@ -121,9 +121,11 @@ char* wps_default_skin(enum screen_type screen)
void pause_action(bool may_fade, bool updatewps)
{
+#if CONFIG_CODEC != SWCODEC
if (may_fade && global_settings.fade_on_stop)
fade(false, updatewps);
else
+#endif
audio_pause();
if (global_settings.pause_rewind) {
@@ -136,16 +138,22 @@ void pause_action(bool may_fade, bool updatewps)
- global_settings.pause_rewind * 1000;
audio_ff_rewind(newpos > 0 ? newpos : 0);
}
+
+ (void)may_fade; (void)updatewps;
}
void unpause_action(bool may_fade, bool updatewps)
{
+#if CONFIG_CODEC != SWCODEC
if (may_fade && global_settings.fade_on_stop)
fade(true, updatewps);
else
+#endif
audio_resume();
+ (void)may_fade; (void)updatewps;
}
+#if CONFIG_CODEC != SWCODEC
void fade(bool fade_in, bool updatewps)
{
int fp_global_vol = global_settings.volume << 8;
@@ -204,6 +212,7 @@ void fade(bool fade_in, bool updatewps)
sound_set_volume(global_settings.volume);
}
}
+#endif /* SWCODEC */
static bool update_onvol_change(enum screen_type screen)
{
@@ -569,7 +578,7 @@ static void play_hop(int direction)
{
#if CONFIG_CODEC == SWCODEC
if(global_settings.beep)
- pcmbuf_beep(1000, 150, 1500*global_settings.beep);
+ beep_play(1000, 150, 1500*global_settings.beep);
#endif
return;
}
@@ -1127,9 +1136,12 @@ long gui_wps_show(void)
status_set_record(false);
status_set_audio(false);
#endif
+#if CONFIG_CODEC != SWCODEC
if (global_settings.fade_on_stop)
fade(false, true);
-
+#else
+ audio_pause();
+#endif
if (bookmark)
bookmark_autobookmark(true);
audio_stop();
diff --git a/apps/misc.c b/apps/misc.c
index 1f945c5431..a0817d7e27 100644
--- a/apps/misc.c
+++ b/apps/misc.c
@@ -297,12 +297,13 @@ static bool clean_shutdown(void (*callback)(void *), void *parameter)
splashf(0, "%s %s", str(LANG_WARNING_BATTERY_EMPTY),
str(LANG_SHUTTINGDOWN));
}
-
+#if CONFIG_CODEC != SWCODEC
if (global_settings.fade_on_stop
&& (audio_stat & AUDIO_STATUS_PLAY))
{
fade(false, false);
}
+#endif
if (batt_safe) /* do not save on critical battery */
{
@@ -380,8 +381,10 @@ bool list_stop_handler(void)
{
if (!global_settings.party_mode)
{
+#if CONFIG_CODEC != SWCODEC
if (global_settings.fade_on_stop)
fade(false, false);
+#endif
bookmark_autobookmark(true);
audio_stop();
ret = true; /* bookmarking can make a refresh necessary */
diff --git a/apps/misc.h b/apps/misc.h
index 0b155db1ec..c3c52d13e0 100644
--- a/apps/misc.h
+++ b/apps/misc.h
@@ -100,6 +100,9 @@ int clamp_value_wrap(int value, int max, int min);
#endif
#endif
+void beep_play(unsigned int frequency, unsigned int duration,
+ unsigned int amplitude);
+
enum current_activity {
ACTIVITY_UNKNOWN = 0,
ACTIVITY_MAINMENU,
diff --git a/apps/pcmbuf.c b/apps/pcmbuf.c
index 7201d3981a..f2f94e3bc9 100644
--- a/apps/pcmbuf.c
+++ b/apps/pcmbuf.c
@@ -23,8 +23,9 @@
#include "system.h"
#include "debug.h"
#include <kernel.h>
-#include "pcmbuf.h"
#include "pcm.h"
+#include "pcm_mixer.h"
+#include "pcmbuf.h"
#include "playback.h"
#include "codec_thread.h"
@@ -49,9 +50,6 @@
#define PCMBUF_MIN_CHUNK 4096 /* We try to never feed a chunk smaller than
this to the DMA */
#define CROSSFADE_BUFSIZE 8192 /* Size of the crossfade buffer */
-#define AUX_BUFSIZE 512 /* Size of the aux buffer; can be 512 if no
- resampling or timestretching is allowed in
- the aux channel, must be 2048 otherwise */
/* number of bytes played per second (sample rate * 2 channels * 2 bytes/sample) */
#define BYTERATE (NATIVE_FREQUENCY * 4)
@@ -91,6 +89,12 @@ static struct chunkdesc *first_desc;
/* Gapless playback */
static bool track_transition IDATA_ATTR;
+/* Fade effect */
+static unsigned int fade_vol = MIX_AMP_UNITY;
+
+/* Voice */
+static bool soft_mode = false;
+
#ifdef HAVE_CROSSFADE
/* Crossfade buffer */
static char *fadebuf IDATA_ATTR;
@@ -121,11 +125,6 @@ static size_t last_chunksize IDATA_ATTR;
static size_t pcmbuf_unplayed_bytes IDATA_ATTR;
static size_t pcmbuf_watermark IDATA_ATTR;
-/* Voice */
-static char *voicebuf IDATA_ATTR;
-static struct chunkdesc *mix_chunk IDATA_ATTR;
-static size_t pcmbuf_mix_sample IDATA_ATTR;
-
static bool low_latency_mode = false;
static bool flush_pcmbuf = false;
@@ -317,10 +316,12 @@ static void boost_codec_thread(int pcm_fill_state)
* Also maintain buffer level above the watermark. */
static bool prepare_insert(size_t length)
{
+ bool playing = mixer_channel_status(PCM_MIXER_CHAN_PLAYBACK) != CHANNEL_STOPPED;
+
if (low_latency_mode)
{
/* 1/4s latency. */
- if (!LOW_DATA(1) && pcm_is_playing())
+ if (!LOW_DATA(1) && playing)
return false;
}
@@ -329,7 +330,7 @@ static bool prepare_insert(size_t length)
return false;
/* Maintain the buffer level above the watermark */
- if (pcm_is_playing())
+ if (playing)
{
/* Only codec thread initiates boost - voice boosts the cpu when playing
a clip */
@@ -351,7 +352,7 @@ static bool prepare_insert(size_t length)
}
#endif
}
- else /* pcm_is_playing */
+ else /* !playing */
{
/* Boost CPU for pre-buffer */
trigger_cpu_boost();
@@ -469,11 +470,14 @@ static size_t get_next_required_pcmbuf_size(void)
* ...|---------PCMBUF---------|FADEBUF|VOICEBUF|DESCS|... */
size_t pcmbuf_init(unsigned char *bufend)
{
+ unsigned char *voicebuf;
+
pcmbuf_bufend = bufend;
pcmbuf_size = get_next_required_pcmbuf_size();
write_chunk = (struct chunkdesc *)pcmbuf_bufend -
NUM_CHUNK_DESCS(pcmbuf_size);
- voicebuf = (char *)write_chunk - AUX_BUFSIZE;
+ voicebuf = (unsigned char *)write_chunk -
+ voicebuf_init((unsigned char *)write_chunk);
#ifdef HAVE_CROSSFADE
fadebuf = voicebuf - CROSSFADE_BUFSIZE;
pcmbuffer = fadebuf - pcmbuf_size;
@@ -491,6 +495,8 @@ size_t pcmbuf_init(unsigned char *bufend)
pcmbuf_play_stop();
+ pcmbuf_soft_mode(false);
+
return pcmbuf_bufend - pcmbuffer;
}
@@ -572,7 +578,7 @@ bool pcmbuf_start_track_change(bool auto_skip)
#ifdef HAVE_CROSSFADE
pcmbuf_is_crossfade_active() ||
#endif
- !pcm_is_playing())
+ mixer_channel_status(PCM_MIXER_CHAN_PLAYBACK) == CHANNEL_STOPPED)
{
pcmbuf_play_stop();
pcm_play_unlock();
@@ -652,10 +658,6 @@ static void pcmbuf_pcm_callback(unsigned char** start, size_t* size)
write_end_chunk->link = pcmbuf_current;
write_end_chunk = pcmbuf_current;
- /* If we've read over the mix chunk while it's still mixing there */
- if (pcmbuf_current == mix_chunk)
- mix_chunk = NULL;
-
#ifdef HAVE_CROSSFADE
/* If we've read over the crossfade chunk while it's still fading */
if (pcmbuf_current == crossfade_chunk)
@@ -696,23 +698,23 @@ static void pcmbuf_pcm_callback(unsigned char** start, size_t* size)
/* Force playback */
void pcmbuf_play_start(void)
{
- if (!pcm_is_playing() && pcmbuf_unplayed_bytes && read_chunk != NULL)
+ if (mixer_channel_status(PCM_MIXER_CHAN_PLAYBACK) == CHANNEL_STOPPED &&
+ pcmbuf_unplayed_bytes && read_chunk != NULL)
{
logf("pcmbuf_play_start");
last_chunksize = read_chunk->size;
pcmbuf_unplayed_bytes -= last_chunksize;
- pcm_play_data(pcmbuf_pcm_callback,
- read_chunk->addr, last_chunksize);
+ mixer_channel_play_data(PCM_MIXER_CHAN_PLAYBACK,
+ pcmbuf_pcm_callback, NULL, 0);
}
}
void pcmbuf_play_stop(void)
{
logf("pcmbuf_play_stop");
- pcm_play_stop();
+ mixer_channel_stop(PCM_MIXER_CHAN_PLAYBACK);
pcmbuf_unplayed_bytes = 0;
- mix_chunk = NULL;
if (read_chunk) {
write_end_chunk->link = read_chunk;
write_end_chunk = read_end_chunk;
@@ -737,8 +739,9 @@ void pcmbuf_play_stop(void)
void pcmbuf_pause(bool pause)
{
logf("pcmbuf_pause: %s", pause?"pause":"play");
- if (pcm_is_playing())
- pcm_play_pause(!pause);
+
+ if (mixer_channel_status(PCM_MIXER_CHAN_PLAYBACK) != CHANNEL_STOPPED)
+ mixer_channel_play_pause(PCM_MIXER_CHAN_PLAYBACK, !pause);
else if (!pause)
pcmbuf_play_start();
}
@@ -1031,102 +1034,6 @@ bool pcmbuf_is_same_size(void)
#endif /* HAVE_CROSSFADE */
-/** Voice */
-
-/* Returns pcm buffer usage in percents (0 to 100). */
-static int pcmbuf_usage(void)
-{
- return pcmbuf_unplayed_bytes * 100 / pcmbuf_size;
-}
-
-static int pcmbuf_mix_free(void)
-{
- if (mix_chunk)
- {
- size_t my_mix_end =
- (size_t)&((int16_t *)mix_chunk->addr)[pcmbuf_mix_sample];
- size_t my_write_pos = (size_t)&pcmbuffer[pcmbuffer_pos];
- if (my_write_pos < my_mix_end)
- my_write_pos += pcmbuf_size;
- return (my_write_pos - my_mix_end) * 100 / pcmbuf_unplayed_bytes;
- }
- return 100;
-}
-
-void *pcmbuf_request_voice_buffer(int *count)
-{
- /* A get-it-to-work-for-now hack (audio status could change by
- completion) */
- if (audio_status() & AUDIO_STATUS_PLAY)
- {
- if (read_chunk == NULL)
- {
- return NULL;
- }
- else if (pcmbuf_usage() >= 10 && pcmbuf_mix_free() >= 30 &&
- (mix_chunk || read_chunk->link))
- {
- *count = MIN(*count, AUX_BUFSIZE/4);
- return voicebuf;
- }
- else
- {
- return NULL;
- }
- }
- else
- {
- return pcmbuf_request_buffer(count);
- }
-}
-
-void pcmbuf_write_voice_complete(int count)
-{
- /* A get-it-to-work-for-now hack (audio status could have changed) */
- if (!(audio_status() & AUDIO_STATUS_PLAY))
- {
- pcmbuf_write_complete(count);
- return;
- }
-
- int16_t *ibuf = (int16_t *)voicebuf;
- int16_t *obuf;
- size_t chunk_samples;
-
- if (mix_chunk == NULL && read_chunk != NULL)
- {
- mix_chunk = read_chunk->link;
- /* Start 1/8s into the next chunk */
- pcmbuf_mix_sample = BYTERATE / 16;
- }
-
- if (!mix_chunk)
- return;
-
- obuf = (int16_t *)mix_chunk->addr;
- chunk_samples = mix_chunk->size / sizeof (int16_t);
-
- count <<= 1;
-
- while (count-- > 0)
- {
- int32_t sample = *ibuf++;
-
- if (pcmbuf_mix_sample >= chunk_samples)
- {
- mix_chunk = mix_chunk->link;
- if (!mix_chunk)
- return;
- pcmbuf_mix_sample = 0;
- obuf = (int16_t *)mix_chunk->addr;
- chunk_samples = mix_chunk->size / 2;
- }
- sample += obuf[pcmbuf_mix_sample] >> 2;
- obuf[pcmbuf_mix_sample++] = clip_sample_16(sample);
- }
-}
-
-
/** Debug menu, other metrics */
/* Amount of bytes left in the buffer. */
@@ -1174,6 +1081,71 @@ unsigned char *pcmbuf_get_meminfo(size_t *length)
#endif
+/** Fading and channel volume control */
+
+/* Sync the channel amplitude to all states */
+static void pcmbuf_update_volume(void)
+{
+ unsigned int vol = fade_vol;
+
+ if (soft_mode)
+ vol >>= 2;
+
+ mixer_channel_set_amplitude(PCM_MIXER_CHAN_PLAYBACK, vol);
+}
+
+/* Quiet-down the channel if 'shhh' is true or else play at normal level */
+void pcmbuf_soft_mode(bool shhh)
+{
+ soft_mode = shhh;
+ pcmbuf_update_volume();
+}
+
+/* Fade channel in or out */
+void pcmbuf_fade(bool fade, bool in)
+{
+ if (!fade)
+ {
+ /* Simply set the level */
+ fade_vol = in ? MIX_AMP_UNITY : MIX_AMP_MUTE;
+ }
+ else
+ {
+ /* Start from the opposing end */
+ fade_vol = in ? MIX_AMP_MUTE : MIX_AMP_UNITY;
+ }
+
+ pcmbuf_update_volume();
+
+ if (fade)
+ {
+ /* Do this on thread for now */
+ int old_prio = thread_set_priority(thread_self(), PRIORITY_REALTIME);
+
+ while (1)
+ {
+ /* Linear fade actually sounds better */
+ if (in)
+ fade_vol += MIN(MIX_AMP_UNITY/16, MIX_AMP_UNITY - fade_vol);
+ else
+ fade_vol -= MIN(MIX_AMP_UNITY/16, fade_vol - MIX_AMP_MUTE);
+
+ pcmbuf_update_volume();
+
+ if (fade_vol > MIX_AMP_MUTE && fade_vol < MIX_AMP_UNITY)
+ {
+ sleep(0);
+ continue;
+ }
+
+ break;
+ }
+
+ thread_set_priority(thread_self(), old_prio);
+ }
+}
+
+
/** Misc */
bool pcmbuf_is_lowdata(void)
@@ -1201,107 +1173,6 @@ void pcmbuf_set_low_latency(bool state)
unsigned long pcmbuf_get_latency(void)
{
- return (pcmbuf_unplayed_bytes + pcm_get_bytes_waiting()) * 1000 / BYTERATE;
-}
-
-#ifndef HAVE_HARDWARE_BEEP
-#define MINIBUF_SAMPLES (NATIVE_FREQUENCY / 1000 * KEYCLICK_DURATION)
-#define MINIBUF_SIZE (MINIBUF_SAMPLES*4)
-
-/* Generates a constant square wave sound with a given frequency
- in Hertz for a duration in milliseconds. */
-void pcmbuf_beep(unsigned int frequency, size_t duration, int amplitude)
-{
- unsigned int step = 0xffffffffu / NATIVE_FREQUENCY * frequency;
- int32_t phase = 0;
- int16_t *bufptr, *bufstart, *bufend;
- int32_t sample;
- int nsamples = NATIVE_FREQUENCY / 1000 * duration;
- bool mix = read_chunk != NULL && read_chunk->link != NULL;
- int i;
-
- bufend = SKIPBYTES((int16_t *)pcmbuffer, pcmbuf_size);
-
- /* Find the insertion point and set bufstart to the start of it */
- if (mix)
- {
- /* Get the currently playing chunk at the current position. */
- bufstart = (int16_t *)pcm_play_dma_get_peak_buffer(&i);
-
- /* If above isn't implemented or pcm is stopped, no beepeth. */
- if (!bufstart || !pcm_is_playing())
- return;
-
- /* Give 5ms clearance. */
- bufstart += BYTERATE / 200;
-
-#ifdef HAVE_PCM_DMA_ADDRESS
- /* Returned peak addresses are DMA addresses */
- bufend = pcm_dma_addr(bufend);
-#endif
-
- /* Wrapped above? */
- if (bufstart >= bufend)
- bufstart -= pcmbuf_size;
-
- /* 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 if (nsamples <= MINIBUF_SAMPLES)
- {
- static int16_t minibuf[MINIBUF_SAMPLES*2] __attribute__((aligned(4)));
- /* Use mini buffer */
- bufstart = minibuf;
- bufend = SKIPBYTES(bufstart, MINIBUF_SIZE);
- }
- else if (!audio_buffer_state_trashed())
- {
- /* Use pcmbuffer */
- bufstart = (int16_t *)pcmbuffer;
- }
- else
- {
- /* No place */
- return;
- }
-
- bufptr = bufstart;
-
- /* 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 >= bufend)
- bufptr = (int16_t *)pcmbuffer;
- sample = mix ? *bufptr : 0;
- *bufptr++ = clip_sample_16(sample + amp);
- if (bufptr >= bufend)
- bufptr = (int16_t *)pcmbuffer;
-
- phase += step;
- }
-
- pcm_play_lock();
-#ifdef HAVE_RECORDING
- pcm_rec_lock();
-#endif
-
- /* Kick off playback if required and it won't interfere */
- if (!pcm_is_playing()
-#ifdef HAVE_RECORDING
- && !pcm_is_recording()
-#endif
- )
- {
- pcm_play_data(NULL, (unsigned char *)bufstart, nsamples * 4);
- }
-
- pcm_play_unlock();
-#ifdef HAVE_RECORDING
- pcm_rec_unlock();
-#endif
+ return (pcmbuf_unplayed_bytes +
+ mixer_channel_get_bytes_waiting(PCM_MIXER_CHAN_PLAYBACK)) * 1000 / BYTERATE;
}
-#endif /* HAVE_HARDWARE_BEEP */
diff --git a/apps/pcmbuf.h b/apps/pcmbuf.h
index b7bf8c2b16..b7f5a3c2d6 100644
--- a/apps/pcmbuf.h
+++ b/apps/pcmbuf.h
@@ -64,9 +64,10 @@ unsigned char *pcmbuf_get_meminfo(size_t *length);
#endif
/* Misc */
+void pcmbuf_fade(bool fade, bool in);
+void pcmbuf_soft_mode(bool shhh);
bool pcmbuf_is_lowdata(void);
void pcmbuf_set_low_latency(bool state);
unsigned long pcmbuf_get_latency(void);
-void pcmbuf_beep(unsigned int frequency, size_t duration, int amplitude);
#endif
diff --git a/apps/playback.c b/apps/playback.c
index 2775e8a95b..cbb94a9d22 100644
--- a/apps/playback.c
+++ b/apps/playback.c
@@ -39,6 +39,7 @@
#include "abrepeat.h"
#include "pcmbuf.h"
#include "playback.h"
+#include "misc.h"
#ifdef HAVE_TAGCACHE
#include "tagcache.h"
@@ -2360,6 +2361,9 @@ static void audio_start_playback(size_t offset, unsigned int flags)
#ifndef PLATFORM_HAS_VOLUME_CHANGE
sound_set_volume(global_settings.volume);
#endif
+ /* Be sure channel is audible */
+ pcmbuf_fade(false, true);
+
/* Update our state */
play_status = PLAY_PLAYING;
}
@@ -2413,6 +2417,8 @@ static void audio_stop_playback(void)
if (play_status == PLAY_STOPPED)
return;
+ pcmbuf_fade(global_settings.fade_on_stop, false);
+
/* Stop the codec and unload it */
halt_decoding_track(true);
pcmbuf_play_stop();
@@ -2452,6 +2458,11 @@ static void audio_on_pause(bool pause)
if (play_status == PLAY_STOPPED || pause == (play_status == PLAY_PAUSED))
return;
+ bool const do_fade = global_settings.fade_on_stop;
+
+ if (pause)
+ pcmbuf_fade(do_fade, false);
+
if (!ff_rw_mode)
{
/* Not in ff/rw mode - may set the state (otherwise this could make
@@ -2459,6 +2470,9 @@ static void audio_on_pause(bool pause)
pcmbuf_pause(pause);
}
+ if (!pause)
+ pcmbuf_fade(do_fade, true);
+
play_status = pause ? PLAY_PAUSED : PLAY_PLAYING;
if (!pause && codec_skip_pending)
@@ -3170,7 +3184,7 @@ void audio_pcmbuf_track_change(bool pcmbuf)
/* May pcmbuf start PCM playback when the buffer is full enough? */
bool audio_pcmbuf_may_play(void)
{
- return play_status != PLAY_PAUSED && !ff_rw_mode;
+ return play_status == PLAY_PLAYING && !ff_rw_mode;
}
@@ -3339,7 +3353,7 @@ void audio_skip(int offset)
skip_offset = accum;
if (global_settings.beep)
- pcmbuf_beep(2000, 100, 2500*global_settings.beep);
+ beep_play(2000, 100, 2500*global_settings.beep);
LOGFQUEUE("audio > audio Q_AUDIO_SKIP %d", offset);
@@ -3360,7 +3374,7 @@ void audio_skip(int offset)
{
/* No more tracks */
if (global_settings.beep)
- pcmbuf_beep(1000, 100, 1500*global_settings.beep);
+ beep_play(1000, 100, 1500*global_settings.beep);
}
id3_mutex_unlock();
diff --git a/apps/plugin.c b/apps/plugin.c
index d9f7c4e24c..10cb9263a4 100644
--- a/apps/plugin.c
+++ b/apps/plugin.c
@@ -551,7 +551,7 @@ static const struct plugin_api rockbox_api = {
pcm_get_peak_buffer,
pcm_play_lock,
pcm_play_unlock,
- pcmbuf_beep,
+ beep_play,
#ifdef HAVE_RECORDING
&rec_freq_sampr[0],
pcm_init_recording,
@@ -778,6 +778,9 @@ static const struct plugin_api rockbox_api = {
/* new stuff at the end, sort into place next time
the API gets incompatible */
+
+ mixer_channel_status,
+ mixer_channel_get_buffer,
};
int plugin_load(const char* plugin, const void* parameter)
diff --git a/apps/plugin.h b/apps/plugin.h
index f15c626667..113296c19a 100644
--- a/apps/plugin.h
+++ b/apps/plugin.h
@@ -65,6 +65,7 @@ void* plugin_get_buffer(size_t *buffer_size);
#include "misc.h"
#include "filefuncs.h"
#if (CONFIG_CODEC == SWCODEC)
+#include "pcm_mixer.h"
#include "dsp.h"
#include "codecs.h"
#include "playback.h"
@@ -145,7 +146,7 @@ void* plugin_get_buffer(size_t *buffer_size);
#define PLUGIN_MAGIC 0x526F634B /* RocK */
/* increase this every time the api struct changes */
-#define PLUGIN_API_VERSION 205
+#define PLUGIN_API_VERSION 206
/* update this to latest version if a change to the api struct breaks
backwards compatibility (and please take the opportunity to sort in any
@@ -635,9 +636,8 @@ struct plugin_api {
const void* (*pcm_get_peak_buffer)(int *count);
void (*pcm_play_lock)(void);
void (*pcm_play_unlock)(void);
- void (*pcmbuf_beep)(unsigned int frequency,
- size_t duration,
- int amplitude);
+ void (*beep_play)(unsigned int frequency, unsigned int duration,
+ unsigned int amplitude);
#ifdef HAVE_RECORDING
const unsigned long *rec_freq_sampr;
void (*pcm_init_recording)(void);
@@ -908,6 +908,8 @@ struct plugin_api {
/* new stuff at the end, sort into place next time
the API gets incompatible */
+ enum channel_status (*mixer_channel_status)(enum pcm_mixer_channel channel);
+ void * (*mixer_channel_get_buffer)(enum pcm_mixer_channel channel, int *count);
};
/* plugin header */
diff --git a/apps/plugins/fft/fft.c b/apps/plugins/fft/fft.c
index b6b1e2fead..a920f8c7f1 100644
--- a/apps/plugins/fft/fft.c
+++ b/apps/plugins/fft/fft.c
@@ -1137,6 +1137,10 @@ static void draw_spectrogram_horizontal(void)
/********************* End of plotting functions (modes) *********************/
/****************************** FFT functions ********************************/
+static bool is_playing(void)
+{
+ return rb->mixer_channel_status(PCM_MIXER_CHAN_PLAYBACK) == CHANNEL_PLAYING;
+}
/** functions use in single/multi configuration **/
static inline bool fft_init_fft_lib(void)
@@ -1156,7 +1160,8 @@ static inline bool fft_init_fft_lib(void)
static inline bool fft_get_fft(void)
{
int count;
- int16_t *value = (int16_t *) rb->pcm_get_peak_buffer(&count);
+ int16_t *value =
+ (int16_t *) rb->mixer_channel_get_buffer(PCM_MIXER_CHAN_PLAYBACK, &count);
/* This block can introduce discontinuities in our data. Meaning, the
* FFT will not be done a continuous segment of the signal. Which can
* be bad. Or not.
@@ -1214,7 +1219,7 @@ static void fft_thread_entry(void)
while(fft_thread_run)
{
- if (!rb->pcm_is_playing())
+ if (!is_playing())
{
rb->sleep(HZ/5);
continue;
@@ -1296,7 +1301,7 @@ static void fft_close_fft(void)
* target uses IRAM */
static bool fft_have_fft(void)
{
- return rb->pcm_is_playing() && fft_get_fft();
+ return is_playing() && fft_get_fft();
}
static inline void fft_free_fft_output(void)
@@ -1366,7 +1371,7 @@ enum plugin_status plugin_start(const void* parameter)
{
int timeout;
- if(!rb->pcm_is_playing())
+ if(!is_playing())
{
showing_warning = true;
mylcd_clear_display();
diff --git a/apps/recorder/keyboard.c b/apps/recorder/keyboard.c
index 1b2d76eab2..5f2a32c367 100644
--- a/apps/recorder/keyboard.c
+++ b/apps/recorder/keyboard.c
@@ -1231,7 +1231,7 @@ static void kbd_move_cursor(struct edit_state *state, int dir)
state->editpos -= dir;
#if CONFIG_CODEC == SWCODEC
if (global_settings.talk_menu)
- pcmbuf_beep(1000, 150, 1500);
+ beep_play(1000, 150, 1500);
#endif
}
}
diff --git a/apps/voice_thread.c b/apps/voice_thread.c
index 6683fcc067..3318bbecb3 100644
--- a/apps/voice_thread.c
+++ b/apps/voice_thread.c
@@ -27,6 +27,8 @@
#include "audio.h"
#include "playback.h"
#include "pcmbuf.h"
+#include "pcm.h"
+#include "pcm_mixer.h"
#include "codecs/libspeex/speex/speex.h"
/* Define any of these as "1" and uncomment the LOGF_ENABLE line to log
@@ -53,24 +55,50 @@
#define IBSS_ATTR_VOICE_STACK IBSS_ATTR
#endif
+/* Minimum priority needs to be a bit elevated since voice has fairly low
+ latency */
+#define PRIORITY_VOICE (PRIORITY_PLAYBACK-4)
+
#define VOICE_FRAME_SIZE 320 /* Samples / frame */
#define VOICE_SAMPLE_RATE 16000 /* Sample rate in HZ */
#define VOICE_SAMPLE_DEPTH 16 /* Sample depth in bits */
/* Voice thread variables */
static unsigned int voice_thread_id = 0;
-static long voice_stack[(DEFAULT_STACK_SIZE + 0x3C0)/sizeof(long)] IBSS_ATTR_VOICE_STACK;
+#ifdef CPU_COLDFIRE
+/* ISR uses any available stack - need a bit more room */
+#define VOICE_STACK_EXTRA 0x400
+#else
+#define VOICE_STACK_EXTRA 0x3c0
+#endif
+static long voice_stack[(DEFAULT_STACK_SIZE + VOICE_STACK_EXTRA)/sizeof(long)]
+ IBSS_ATTR_VOICE_STACK;
static const char voice_thread_name[] = "voice";
/* Voice thread synchronization objects */
static struct event_queue voice_queue SHAREDBSS_ATTR;
-static struct mutex voice_mutex SHAREDBSS_ATTR;
static struct queue_sender_list voice_queue_sender_list SHAREDBSS_ATTR;
static bool voice_done SHAREDDATA_ATTR = true;
/* Buffer for decoded samples */
static spx_int16_t voice_output_buf[VOICE_FRAME_SIZE] CACHEALIGN_ATTR;
+#define VOICE_PCM_FRAME_COUNT ((NATIVE_FREQUENCY*VOICE_FRAME_SIZE + \
+ VOICE_SAMPLE_RATE) / VOICE_SAMPLE_RATE)
+#define VOICE_PCM_FRAME_SIZE (VOICE_PCM_FRAME_COUNT*4)
+
+/* Default number of native-frequency PCM frames to queue - adjust as
+ necessary per-target */
+#define VOICE_FRAMES 3
+
+/* Might have lookahead and be skipping samples, so size is needed */
+static size_t voicebuf_sizes[VOICE_FRAMES];
+static uint32_t (* voicebuf)[VOICE_PCM_FRAME_COUNT];
+static unsigned int cur_buf_in, cur_buf_out;
+
+/* A delay to not bring audio back to normal level too soon */
+#define QUIET_COUNT 3
+
enum voice_thread_states
{
TSTATE_STOPPED = 0, /* Voice thread is stopped and awaiting commands */
@@ -83,7 +111,6 @@ enum voice_thread_messages
Q_VOICE_NULL = 0, /* A message for thread sync - no effect on state */
Q_VOICE_PLAY, /* Play a clip */
Q_VOICE_STOP, /* Stop current clip */
- Q_VOICE_STATE, /* Query playing state */
};
/* Structure to store clip data callback info */
@@ -98,7 +125,7 @@ struct voice_info
* internal functions */
struct voice_thread_data
{
- int state; /* Thread state (TSTATE_*) */
+ volatile int state; /* Thread state (TSTATE_*) */
struct queue_event ev; /* Last queue event pulled from queue */
void *st; /* Decoder instance */
SpeexBits bits; /* Bit cursor */
@@ -107,33 +134,79 @@ struct voice_thread_data
const char *src[2]; /* Current output buffer pointers */
int lookahead; /* Number of samples to drop at start of clip */
int count; /* Count of samples remaining to send to PCM */
+ int quiet_counter; /* Countdown until audio goes back to normal */
};
-/* Audio playback is in a playing state? */
-static inline bool playback_is_playing(void)
+/* Number of frames in queue */
+static inline int voice_unplayed_frames(void)
{
- return (audio_status() & AUDIO_STATUS_PLAY) != 0;
+ return cur_buf_in - cur_buf_out;
+}
+
+/* Mixer channel callback */
+static void voice_pcm_callback(unsigned char **start, size_t *size)
+{
+ if (voice_unplayed_frames() == 0)
+ return; /* Done! */
+
+ unsigned int i = ++cur_buf_out % VOICE_FRAMES;
+
+ *start = (unsigned char *)voicebuf[i];
+ *size = voicebuf_sizes[i];
+}
+
+/* Start playback of voice channel if not already playing */
+static void voice_start_playback(void)
+{
+ if (mixer_channel_status(PCM_MIXER_CHAN_VOICE) != CHANNEL_STOPPED)
+ return;
+
+ unsigned int i = cur_buf_out % VOICE_FRAMES;
+ mixer_channel_play_data(PCM_MIXER_CHAN_VOICE, voice_pcm_callback,
+ (unsigned char *)voicebuf[i], voicebuf_sizes[i]);
+}
+
+/* Stop the voice channel */
+static void voice_stop_playback(void)
+{
+ mixer_channel_stop(PCM_MIXER_CHAN_VOICE);
+ cur_buf_in = cur_buf_out = 0;
+}
+
+/* Grab a free PCM frame */
+static uint32_t * voice_buf_get(void)
+{
+ if (voice_unplayed_frames() >= VOICE_FRAMES)
+ {
+ /* Full */
+ voice_start_playback();
+ return NULL;
+ }
+
+ return voicebuf[cur_buf_in % VOICE_FRAMES];
+}
+
+/* Commit a frame returned by voice_buf_get and set the actual size */
+static void voice_buf_commit(size_t size)
+{
+ voicebuf_sizes[cur_buf_in++ % VOICE_FRAMES] = size;
}
/* Stop any current clip and start playing a new one */
void mp3_play_data(const unsigned char* start, int size,
pcm_play_callback_type get_more)
{
- /* Shared struct to get data to the thread - once it replies, it has
- * safely cached it in its own private data */
- static struct voice_info voice_clip SHAREDBSS_ATTR;
-
if (get_more != NULL && start != NULL && (ssize_t)size > 0)
{
- mutex_lock(&voice_mutex);
+ struct voice_info voice_clip =
+ {
+ .get_more = get_more,
+ .start = (unsigned char *)start,
+ .size = size,
+ };
- voice_clip.get_more = get_more;
- voice_clip.start = (unsigned char *)start;
- voice_clip.size = size;
LOGFQUEUE("mp3 >| voice Q_VOICE_PLAY");
queue_send(&voice_queue, Q_VOICE_PLAY, (intptr_t)&voice_clip);
-
- mutex_unlock(&voice_mutex);
}
}
@@ -143,11 +216,8 @@ void mp3_play_stop(void)
if(!audio_is_thread_ready())
return;
- mutex_lock(&voice_mutex); /* Sync against voice_stop */
- LOGFQUEUE("mp3 >| voice Q_VOICE_STOP: 1");
- queue_send(&voice_queue, Q_VOICE_STOP, 1);
-
- mutex_unlock(&voice_mutex);
+ LOGFQUEUE("mp3 >| voice Q_VOICE_STOP");
+ queue_send(&voice_queue, Q_VOICE_STOP, 0);
}
void mp3_play_pause(bool play)
@@ -156,36 +226,19 @@ void mp3_play_pause(bool play)
(void)play;
}
-/* Tell is voice is still in a playing state */
+/* Tell if voice is still in a playing state */
bool mp3_is_playing(void)
{
- /* TODO: Implement a timeout or state query function for event objects */
- LOGFQUEUE("mp3 >| voice Q_VOICE_STATE");
- int state = queue_send(&voice_queue, Q_VOICE_STATE, 0);
- return state != TSTATE_STOPPED;
+ return !voice_done;
}
/* This function is meant to be used by the buffer request functions to
ensure the codec is no longer active */
void voice_stop(void)
{
- mutex_lock(&voice_mutex);
-
- /* Stop the output and current clip */
- mp3_play_stop();
-
- /* Careful if using sync objects in talk.c - make sure locking order is
- * observed with one or the other always granted first */
-
/* Unqueue all future clips */
talk_force_shutup();
-
- /* Wait for any final queue_post to be processed */
- LOGFQUEUE("mp3 >| voice Q_VOICE_NULL");
- queue_send(&voice_queue, Q_VOICE_NULL, 0);
-
- mutex_unlock(&voice_mutex);
-} /* voice_stop */
+}
/* Wait for voice to finish speaking. */
void voice_wait(void)
@@ -194,8 +247,7 @@ void voice_wait(void)
* new clip by the time we wait. This should be resolvable if conditions
* ever require knowing the very clip you requested has finished. */
- /* Wait for PCM buffer to be exhausted. Works only if not playing. */
- while(!voice_done || (!playback_is_playing() && pcm_is_playing()))
+ while (!voice_done)
sleep(1);
}
@@ -211,6 +263,9 @@ static void voice_data_init(struct voice_thread_data *td)
dsp_configure(td->dsp, DSP_SET_FREQUENCY, VOICE_SAMPLE_RATE);
dsp_configure(td->dsp, DSP_SET_SAMPLE_DEPTH, VOICE_SAMPLE_DEPTH);
dsp_configure(td->dsp, DSP_SET_STEREO_MODE, STEREO_MONO);
+
+ mixer_channel_set_amplitude(PCM_MIXER_CHAN_VOICE, MIX_AMP_UNITY);
+ td->quiet_counter = 0;
}
/* Voice thread message processing */
@@ -222,7 +277,6 @@ static void voice_message(struct voice_thread_data *td)
{
case Q_VOICE_PLAY:
LOGFQUEUE("voice < Q_VOICE_PLAY");
- /* Put up a block for completion signal */
voice_done = false;
/* Copy the clip info */
@@ -239,12 +293,17 @@ static void voice_message(struct voice_thread_data *td)
/* Boost CPU now */
trigger_cpu_boost();
}
- else if (!playback_is_playing())
+ else
{
- /* Just voice, stop any clip still playing */
- pcmbuf_play_stop();
+ /* Stop any clip still playing */
+ voice_stop_playback();
}
+ /* Make audio play more softly and set delay to return to normal
+ playback level */
+ pcmbuf_soft_mode(true);
+ td->quiet_counter = QUIET_COUNT;
+
/* Clean-start the decoder */
td->st = speex_decoder_init(&speex_wb_mode);
@@ -255,30 +314,32 @@ static void voice_message(struct voice_thread_data *td)
td->state = TSTATE_DECODE;
return;
- case Q_VOICE_STOP:
- LOGFQUEUE("voice < Q_VOICE_STOP: %ld", (long)td->ev.data);
+ case SYS_TIMEOUT:
+ if (voice_unplayed_frames())
+ {
+ /* Waiting for PCM to finish */
+ break;
+ }
- if (td->ev.data != 0 && !playback_is_playing())
+ /* Drop through and stop the first time after clip runs out */
+ if (td->quiet_counter-- != QUIET_COUNT)
{
- /* If not playing, it's just voice so stop pcm playback */
- pcmbuf_play_stop();
+ if (td->quiet_counter <= 0)
+ pcmbuf_soft_mode(false);
+
+ break;
}
- /* Cancel boost */
- cancel_cpu_boost();
+ /* Fall-through */
+ case Q_VOICE_STOP:
+ LOGFQUEUE("voice < Q_VOICE_STOP");
td->state = TSTATE_STOPPED;
voice_done = true;
- break;
-
- case Q_VOICE_STATE:
- LOGFQUEUE("voice < Q_VOICE_STATE");
- queue_reply(&voice_queue, td->state);
-
- if (td->state == TSTATE_STOPPED)
- break; /* Not in a playback state */
- return;
+ cancel_cpu_boost();
+ voice_stop_playback();
+ break;
default:
/* Default messages get a reply and thread continues with no
@@ -286,20 +347,24 @@ static void voice_message(struct voice_thread_data *td)
LOGFQUEUE("voice < default");
if (td->state == TSTATE_STOPPED)
- break; /* Not in playback state */
+ break; /* Not in (active) playback state */
queue_reply(&voice_queue, 0);
return;
}
- queue_wait(&voice_queue, &td->ev);
+ if (td->quiet_counter > 0)
+ queue_wait_w_tmo(&voice_queue, &td->ev, HZ/10);
+ else
+ queue_wait(&voice_queue, &td->ev);
}
}
/* Voice thread entrypoint */
-static void voice_thread(void)
+static void NORETURN_ATTR voice_thread(void)
{
struct voice_thread_data td;
+ char *dest;
voice_data_init(&td);
@@ -361,19 +426,10 @@ static void voice_thread(void)
}
/* If all clips are done and not playing, force pcm playback. */
- if (!pcm_is_playing())
- pcmbuf_play_start();
-
- /* Synthesize a stop request */
- /* NOTE: We have no way to know when the pcm data placed in the
- * buffer is actually consumed and playback has reached the end
- * so until the info is available or inferred somehow, this will
- * not be accurate and the stopped signal will come too soon.
- * ie. You may not hear the "Shutting Down" splash even though
- * it waits for voice to stop. */
- td.ev.id = Q_VOICE_STOP;
- td.ev.data = 0; /* Let PCM drain by itself */
- yield();
+ voice_start_playback();
+
+ td.state = TSTATE_STOPPED;
+ td.ev.id = SYS_TIMEOUT;
goto message_process;
}
@@ -385,62 +441,39 @@ static void voice_thread(void)
td.src[1] = NULL;
td.lookahead -= MIN(VOICE_FRAME_SIZE, td.lookahead);
+ if (td.count <= 0)
+ continue;
+
+ td.state = TSTATE_BUFFER_INSERT;
+
buffer_insert:
/* Process the PCM samples in the DSP and send out for mixing */
- td.state = TSTATE_BUFFER_INSERT;
- while (td.count > 0)
+ while (1)
{
- int out_count = dsp_output_count(td.dsp, td.count);
- int inp_count;
- char *dest;
+ if (!queue_empty(&voice_queue))
+ goto message_wait;
- while (1)
- {
- if (!queue_empty(&voice_queue))
- goto message_wait;
-
- if ((dest = pcmbuf_request_voice_buffer(&out_count)) != NULL)
- break;
-
- yield();
- }
-
- /* Get the real input_size for output_size bytes, guarding
- * against resampling buffer overflows. */
- inp_count = dsp_input_count(td.dsp, out_count);
-
- if (inp_count <= 0)
- break;
-
- /* Input size has grown, no error, just don't write more than
- * length */
- if (inp_count > td.count)
- inp_count = td.count;
-
- out_count = dsp_process(td.dsp, dest, td.src, inp_count);
-
- if (out_count <= 0)
+ if ((dest = (char *)voice_buf_get()) != NULL)
break;
- pcmbuf_write_voice_complete(out_count);
- td.count -= inp_count;
+ yield();
}
- yield();
+ voice_buf_commit(dsp_process(td.dsp, dest, td.src, td.count)
+ * sizeof (int32_t));
} /* end while */
-} /* voice_thread */
+}
/* Initialize all synchronization objects create the thread */
void voice_thread_init(void)
{
logf("Starting voice thread");
queue_init(&voice_queue, false);
- mutex_init(&voice_mutex);
voice_thread_id = create_thread(voice_thread, voice_stack,
sizeof(voice_stack), CREATE_THREAD_FROZEN,
- voice_thread_name IF_PRIO(, PRIORITY_PLAYBACK) IF_COP(, CPU));
+ voice_thread_name IF_PRIO(, PRIORITY_VOICE) IF_COP(, CPU));
queue_enable_queue_send(&voice_queue, &voice_queue_sender_list,
voice_thread_id);
@@ -457,6 +490,18 @@ void voice_thread_resume(void)
/* Set the voice thread priority */
void voice_thread_set_priority(int priority)
{
+ if (priority > PRIORITY_VOICE)
+ priority = PRIORITY_VOICE;
+
thread_set_priority(voice_thread_id, priority);
}
#endif
+
+/* Initialize voice PCM buffer and return size, allocated from the end */
+size_t voicebuf_init(unsigned char *bufend)
+{
+ size_t size = VOICE_FRAMES * VOICE_PCM_FRAME_SIZE;
+ cur_buf_out = cur_buf_in = 0;
+ voicebuf = (uint32_t (*)[VOICE_PCM_FRAME_COUNT])(bufend - size);
+ return size;
+}
diff --git a/apps/voice_thread.h b/apps/voice_thread.h
index 4359825dd7..1529f7efe8 100644
--- a/apps/voice_thread.h
+++ b/apps/voice_thread.h
@@ -29,8 +29,13 @@ bool mp3_is_playing(void);
void voice_wait(void);
void voice_stop(void);
+
void voice_thread_init(void);
void voice_thread_resume(void);
+#ifdef HAVE_PRIORITY_SCHEDULING
void voice_thread_set_priority(int priority);
+#endif
+
+size_t voicebuf_init(unsigned char *bufend);
#endif /* VOICE_THREAD_H */