1
0
Fork 0
forked from len0rd/rockbox

Make speex the new voice format for SWCODEC targets (non-Archos). Remove codec swapping and build speex voice decoding directly into the core binary.

git-svn-id: svn://svn.rockbox.org/rockbox/trunk@15668 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
Michael Sevakis 2007-11-18 17:12:19 +00:00
parent 75432619e8
commit 99617d71ba
29 changed files with 753 additions and 1153 deletions

View file

@ -15,6 +15,7 @@ menus/theme_menu.c
#if CONFIG_CODEC == SWCODEC #if CONFIG_CODEC == SWCODEC
menus/eq_menu.c menus/eq_menu.c
buffering.c buffering.c
voice_thread.c
#endif #endif
menus/main_menu.c menus/main_menu.c
menus/playback_menu.c menus/playback_menu.c

View file

@ -67,8 +67,6 @@ extern unsigned char codecbuf[];
extern void* plugin_get_audio_buffer(size_t *buffer_size); extern void* plugin_get_audio_buffer(size_t *buffer_size);
struct codec_api ci_voice;
struct codec_api ci = { struct codec_api ci = {
0, /* filesize */ 0, /* filesize */
@ -163,6 +161,8 @@ struct codec_api ci = {
flush_icache, flush_icache,
invalidate_icache, invalidate_icache,
#endif #endif
NULL, /* struct sp_data *dsp */
}; };
void codec_get_full_path(char *path, const char *codec_root_fn) void codec_get_full_path(char *path, const char *codec_root_fn)

View file

@ -80,7 +80,7 @@
#define CODEC_ENC_MAGIC 0x52454E43 /* RENC */ #define CODEC_ENC_MAGIC 0x52454E43 /* RENC */
/* increase this every time the api struct changes */ /* increase this every time the api struct changes */
#define CODEC_API_VERSION 19 #define CODEC_API_VERSION 20
/* update this to latest version if a change to the api struct breaks /* update this to latest version if a change to the api struct breaks
backwards compatibility (and please take the opportunity to sort in any backwards compatibility (and please take the opportunity to sort in any
@ -234,6 +234,8 @@ struct codec_api {
void (*flush_icache)(void); void (*flush_icache)(void);
void (*invalidate_icache)(void); void (*invalidate_icache)(void);
#endif #endif
struct dsp_config *dsp;
}; };
/* codec header */ /* codec header */

View file

@ -45,6 +45,7 @@
#define MAX_CHARS_PER_FRAME (2000/BYTES_PER_CHAR) #define MAX_CHARS_PER_FRAME (2000/BYTES_PER_CHAR)
#endif #endif
#ifndef ROCKBOX_VOICE_CODEC
void speex_bits_init(SpeexBits *bits) void speex_bits_init(SpeexBits *bits)
{ {
bits->chars = (char*)speex_alloc(MAX_CHARS_PER_FRAME); bits->chars = (char*)speex_alloc(MAX_CHARS_PER_FRAME);
@ -57,6 +58,7 @@ void speex_bits_init(SpeexBits *bits)
speex_bits_reset(bits); speex_bits_reset(bits);
} }
#endif
void speex_bits_init_buffer(SpeexBits *bits, void *buff, int buf_size) void speex_bits_init_buffer(SpeexBits *bits, void *buff, int buf_size)
{ {
@ -82,12 +84,14 @@ void speex_bits_set_bit_buffer(SpeexBits *bits, void *buff, int buf_size)
} }
#ifndef ROCKBOX_VOICE_CODEC
void speex_bits_destroy(SpeexBits *bits) void speex_bits_destroy(SpeexBits *bits)
{ {
if (bits->owner) if (bits->owner)
speex_free(bits->chars); speex_free(bits->chars);
/* Will do something once the allocation is dynamic */ /* Will do something once the allocation is dynamic */
} }
#endif
void speex_bits_reset(SpeexBits *bits) void speex_bits_reset(SpeexBits *bits)
{ {
@ -106,7 +110,7 @@ void speex_bits_rewind(SpeexBits *bits)
bits->overflow=0; bits->overflow=0;
} }
#ifndef SPEEX_VOICE_ENCODER #if !defined(SPEEX_VOICE_ENCODER) && !defined(ROCKBOX_VOICE_CODEC)
void speex_bits_read_from(SpeexBits *bits, char *chars, int len) void speex_bits_read_from(SpeexBits *bits, char *chars, int len)
{ {
int i; int i;

View file

@ -45,6 +45,12 @@
#define FLOATING_POINT #define FLOATING_POINT
#endif #endif
#ifndef ROCKBOX_VOICE_CODEC
#define EXC_ICONST_ATTR ICONST_ATTR
#else
#define EXC_ICONST_ATTR
#endif
/* Define to 1 if you have the <dlfcn.h> header file. */ /* Define to 1 if you have the <dlfcn.h> header file. */
/* #undef HAVE_DLFCN_H */ /* #undef HAVE_DLFCN_H */

View file

@ -32,7 +32,7 @@
#include "config-speex.h" #include "config-speex.h"
const signed char exc_10_16_table[160] ICONST_ATTR = { const signed char exc_10_16_table[160] EXC_ICONST_ATTR = {
22,39,14,44,11,35,-2,23,-4,6, 22,39,14,44,11,35,-2,23,-4,6,
46,-28,13,-27,-23,12,4,20,-5,9, 46,-28,13,-27,-23,12,4,20,-5,9,
37,-18,-23,23,0,9,-6,-20,4,-1, 37,-18,-23,23,0,9,-6,-20,4,-1,

View file

@ -32,7 +32,7 @@
#include "config-speex.h" #include "config-speex.h"
const signed char exc_10_32_table[320] ICONST_ATTR = { const signed char exc_10_32_table[320] EXC_ICONST_ATTR = {
7,17,17,27,25,22,12,4,-3,0, 7,17,17,27,25,22,12,4,-3,0,
28,-36,39,-24,-15,3,-9,15,-5,10, 28,-36,39,-24,-15,3,-9,15,-5,10,
31,-28,11,31,-21,9,-11,-11,-2,-7, 31,-28,11,31,-21,9,-11,-11,-2,-7,

View file

@ -32,7 +32,7 @@
#include "config-speex.h" #include "config-speex.h"
const signed char exc_20_32_table[640] ICONST_ATTR = { const signed char exc_20_32_table[640] EXC_ICONST_ATTR = {
12,32,25,46,36,33,9,14,-3,6,1,-8,0,-10,-5,-7,-7,-7,-5,-5, 12,32,25,46,36,33,9,14,-3,6,1,-8,0,-10,-5,-7,-7,-7,-5,-5,
31,-27,24,-32,-4,10,-11,21,-3,19,23,-9,22,24,-10,-1,-10,-13,-7,-11, 31,-27,24,-32,-4,10,-11,21,-3,19,23,-9,22,24,-10,-1,-10,-13,-7,-11,
42,-33,31,19,-8,0,-10,-16,1,-21,-17,10,-8,14,8,4,11,-2,5,-2, 42,-33,31,19,-8,0,-10,-16,1,-21,-17,10,-8,14,8,4,11,-2,5,-2,

View file

@ -32,7 +32,7 @@
#include "config-speex.h" #include "config-speex.h"
const signed char exc_5_256_table[1280] ICONST_ATTR = { const signed char exc_5_256_table[1280] EXC_ICONST_ATTR = {
-8,-37,5,-43,5, -8,-37,5,-43,5,
73,61,39,12,-3, 73,61,39,12,-3,
-61,-32,2,42,30, -61,-32,2,42,30,

View file

@ -32,7 +32,7 @@
#include "config-speex.h" #include "config-speex.h"
const signed char exc_5_64_table[320] ICONST_ATTR = { const signed char exc_5_64_table[320] EXC_ICONST_ATTR = {
1,5,-15,49,-66, 1,5,-15,49,-66,
-48,-4,50,-44,7, -48,-4,50,-44,7,
37,16,-18,25,-26, 37,16,-18,25,-26,

View file

@ -32,7 +32,7 @@
#include "config-speex.h" #include "config-speex.h"
const signed char exc_8_128_table[1024] ICONST_ATTR = { const signed char exc_8_128_table[1024] EXC_ICONST_ATTR = {
-14,9,13,-32,2,-10,31,-10, -14,9,13,-32,2,-10,31,-10,
-8,-8,6,-4,-1,10,-64,23, -8,-8,6,-4,-1,10,-64,23,
6,20,13,6,8,-22,16,34, 6,20,13,6,8,-22,16,34,

View file

@ -162,6 +162,10 @@ struct dsp_config
int sample_bytes; int sample_bytes;
int stereo_mode; int stereo_mode;
int frac_bits; int frac_bits;
#ifdef HAVE_SW_TONE_CONTROLS
/* Filter struct for software bass/treble controls */
struct eqfilter tone_filter;
#endif
/* Functions that change depending upon settings - NULL if stage is /* Functions that change depending upon settings - NULL if stage is
disabled */ disabled */
sample_input_fn_type input_samples; sample_input_fn_type input_samples;
@ -171,6 +175,7 @@ struct dsp_config
way */ way */
channels_process_dsp_fn_type apply_gain; channels_process_dsp_fn_type apply_gain;
channels_process_fn_type apply_crossfeed; channels_process_fn_type apply_crossfeed;
channels_process_fn_type eq_process;
channels_process_fn_type channels_process; channels_process_fn_type channels_process;
}; };
@ -187,13 +192,13 @@ struct crossfeed_data crossfeed_data IDATA_ATTR = /* A */
}; };
/* Equalizer */ /* Equalizer */
static struct eq_state eq_data; /* A/V */ static struct eq_state eq_data; /* A */
/* Software tone controls */
#ifdef HAVE_SW_TONE_CONTROLS #ifdef HAVE_SW_TONE_CONTROLS
static int prescale; static int prescale; /* A/V */
static int bass; static int bass; /* A/V */
static int treble; static int treble; /* A/V */
/* Filter struct for software bass/treble controls */
static struct eqfilter tone_filter;
#endif #endif
/* Settings applicable to audio codec only */ /* Settings applicable to audio codec only */
@ -202,7 +207,6 @@ static int channels_mode;
long dsp_sw_gain; long dsp_sw_gain;
long dsp_sw_cross; long dsp_sw_cross;
static bool dither_enabled; static bool dither_enabled;
static bool eq_enabled IBSS_ATTR;
static long eq_precut; static long eq_precut;
static long track_gain; static long track_gain;
static bool new_gain; static bool new_gain;
@ -212,9 +216,8 @@ static long album_peak;
static long replaygain; static long replaygain;
static bool crossfeed_enabled; static bool crossfeed_enabled;
#define audio_dsp (&dsp_conf[CODEC_IDX_AUDIO]) #define audio_dsp (dsp_conf[CODEC_IDX_AUDIO])
#define voice_dsp (&dsp_conf[CODEC_IDX_VOICE]) #define voice_dsp (dsp_conf[CODEC_IDX_VOICE])
static struct dsp_config *dsp IDATA_ATTR = audio_dsp;
/* The internal format is 32-bit samples, non-interleaved, stereo. This /* The internal format is 32-bit samples, non-interleaved, stereo. This
* format is similar to the raw output from several codecs, so the amount * format is similar to the raw output from several codecs, so the amount
@ -224,14 +227,6 @@ static struct dsp_config *dsp IDATA_ATTR = audio_dsp;
int32_t sample_buf[SAMPLE_BUF_COUNT] IBSS_ATTR; int32_t sample_buf[SAMPLE_BUF_COUNT] IBSS_ATTR;
static int32_t resample_buf[RESAMPLE_BUF_COUNT] IBSS_ATTR; static int32_t resample_buf[RESAMPLE_BUF_COUNT] IBSS_ATTR;
/* set a new dsp and return old one */
static inline struct dsp_config * switch_dsp(struct dsp_config *_dsp)
{
struct dsp_config * old_dsp = dsp;
dsp = _dsp;
return old_dsp;
}
#if 0 #if 0
/* Clip sample to arbitrary limits where range > 0 and min + range = max */ /* Clip sample to arbitrary limits where range > 0 and min + range = max */
static inline long clip_sample(int32_t sample, int32_t min, int32_t range) static inline long clip_sample(int32_t sample, int32_t min, int32_t range)
@ -263,8 +258,8 @@ int sound_get_pitch(void)
void sound_set_pitch(int permille) void sound_set_pitch(int permille)
{ {
pitch_ratio = permille; pitch_ratio = permille;
dsp_configure(&audio_dsp, DSP_SWITCH_FREQUENCY,
dsp_configure(DSP_SWITCH_FREQUENCY, dsp->codec_frequency); audio_dsp.codec_frequency);
} }
/* Convert count samples to the internal format, if needed. Updates src /* Convert count samples to the internal format, if needed. Updates src
@ -386,7 +381,7 @@ static void sample_input_gt_native_ni_stereo(
* * dsp->stereo_mode (A/V) * * dsp->stereo_mode (A/V)
* * dsp->sample_depth (A/V) * * dsp->sample_depth (A/V)
*/ */
static void sample_input_new_format(void) static void sample_input_new_format(struct dsp_config *dsp)
{ {
static const sample_input_fn_type sample_input_functions[] = static const sample_input_fn_type sample_input_functions[] =
{ {
@ -462,7 +457,7 @@ static void sample_output_dithered(int count, struct dsp_data *data,
int ch; int ch;
int16_t *d; int16_t *d;
for (ch = 0; ch < dsp->data.num_channels; ch++) for (ch = 0; ch < data->num_channels; ch++)
{ {
struct dither_data * const dither = &dither_data[ch]; struct dither_data * const dither = &dither_data[ch];
int32_t *s = src[ch]; int32_t *s = src[ch];
@ -505,7 +500,7 @@ static void sample_output_dithered(int count, struct dsp_data *data,
} }
} }
if (dsp->data.num_channels == 2) if (data->num_channels == 2)
return; return;
/* Have to duplicate left samples into the right channel since /* Have to duplicate left samples into the right channel since
@ -530,7 +525,7 @@ static void sample_output_dithered(int count, struct dsp_data *data,
* * dsp->stereo_mode (A/V) * * dsp->stereo_mode (A/V)
* * dither_enabled (A) * * dither_enabled (A)
*/ */
static void sample_output_new_format(void) static void sample_output_new_format(struct dsp_config *dsp)
{ {
static const sample_output_fn_type sample_output_functions[] = static const sample_output_fn_type sample_output_functions[] =
{ {
@ -542,7 +537,7 @@ static void sample_output_new_format(void)
int out = dsp->data.num_channels - 1; int out = dsp->data.num_channels - 1;
if (dsp == audio_dsp && dither_enabled) if (dsp == &audio_dsp && dither_enabled)
out += 2; out += 2;
dsp->output_samples = sample_output_functions[out]; dsp->output_samples = sample_output_functions[out];
@ -638,7 +633,7 @@ static int dsp_upsample(int count, struct dsp_data *data,
} }
#endif /* DSP_HAVE_ASM_RESAMPLING */ #endif /* DSP_HAVE_ASM_RESAMPLING */
static void resampler_new_delta(void) static void resampler_new_delta(struct dsp_config *dsp)
{ {
dsp->data.resample_data.delta = (unsigned long) dsp->data.resample_data.delta = (unsigned long)
dsp->frequency * 65536LL / NATIVE_FREQUENCY; dsp->frequency * 65536LL / NATIVE_FREQUENCY;
@ -663,7 +658,7 @@ static void resampler_new_delta(void)
* done, to refer to the resampled data. Returns number of stereo samples * done, to refer to the resampled data. Returns number of stereo samples
* for further processing. * for further processing.
*/ */
static inline int resample(int count, int32_t *src[]) static inline int resample(struct dsp_config *dsp, int count, int32_t *src[])
{ {
int32_t *dst[2] = int32_t *dst[2] =
{ {
@ -679,12 +674,8 @@ static inline int resample(int count, int32_t *src[])
return count; return count;
} }
static void dither_init(void) static void dither_init(struct dsp_config *dsp)
{ {
/* Voice codec should not reset the audio codec's dither data */
if (dsp != audio_dsp)
return;
memset(dither_data, 0, sizeof (dither_data)); memset(dither_data, 0, sizeof (dither_data));
dither_bias = (1L << (dsp->frac_bits - NATIVE_DEPTH)); dither_bias = (1L << (dsp->frac_bits - NATIVE_DEPTH));
dither_mask = (1L << (dsp->frac_bits + 1 - NATIVE_DEPTH)) - 1; dither_mask = (1L << (dsp->frac_bits + 1 - NATIVE_DEPTH)) - 1;
@ -692,11 +683,9 @@ static void dither_init(void)
void dsp_dither_enable(bool enable) void dsp_dither_enable(bool enable)
{ {
/* Be sure audio dsp is current to set correct function */ struct dsp_config *dsp = &audio_dsp;
struct dsp_config *old_dsp = switch_dsp(audio_dsp);
dither_enabled = enable; dither_enabled = enable;
sample_output_new_format(); sample_output_new_format(dsp);
switch_dsp(old_dsp);
} }
/* Applies crossfeed to the stereo signal in src. /* Applies crossfeed to the stereo signal in src.
@ -762,9 +751,8 @@ static void apply_crossfeed(int count, int32_t *buf[])
void dsp_set_crossfeed(bool enable) void dsp_set_crossfeed(bool enable)
{ {
crossfeed_enabled = enable; crossfeed_enabled = enable;
audio_dsp->apply_crossfeed = audio_dsp.apply_crossfeed = (enable && audio_dsp.data.num_channels > 1)
(enable && audio_dsp->data.num_channels > 1) ? apply_crossfeed : NULL;
? apply_crossfeed : NULL;
} }
void dsp_set_crossfeed_direct_gain(int gain) void dsp_set_crossfeed_direct_gain(int gain)
@ -830,12 +818,12 @@ static void set_gain(struct dsp_config *dsp)
dsp->data.gain = DEFAULT_GAIN; dsp->data.gain = DEFAULT_GAIN;
/* Replay gain not relevant to voice */ /* Replay gain not relevant to voice */
if (dsp == audio_dsp && replaygain) if (dsp == &audio_dsp && replaygain)
{ {
dsp->data.gain = replaygain; dsp->data.gain = replaygain;
} }
if (eq_enabled && eq_precut) if (dsp->eq_process && eq_precut)
{ {
dsp->data.gain = dsp->data.gain =
(long) (((int64_t) dsp->data.gain * eq_precut) >> 24); (long) (((int64_t) dsp->data.gain * eq_precut) >> 24);
@ -853,16 +841,6 @@ static void set_gain(struct dsp_config *dsp)
dsp->apply_gain = dsp->data.gain != 0 ? dsp_apply_gain : NULL; dsp->apply_gain = dsp->data.gain != 0 ? dsp_apply_gain : NULL;
} }
/**
* Use to enable the equalizer.
*
* @param enable true to enable the equalizer
*/
void dsp_set_eq(bool enable)
{
eq_enabled = enable;
}
/** /**
* Update the amount to cut the audio before applying the equalizer. * Update the amount to cut the audio before applying the equalizer.
* *
@ -871,8 +849,7 @@ void dsp_set_eq(bool enable)
void dsp_set_eq_precut(int precut) void dsp_set_eq_precut(int precut)
{ {
eq_precut = get_replaygain_int(precut * -10); eq_precut = get_replaygain_int(precut * -10);
set_gain(audio_dsp); set_gain(&audio_dsp);
set_gain(voice_dsp); /* For EQ precut */
} }
/** /**
@ -929,7 +906,7 @@ static void eq_process(int count, int32_t *buf[])
EQ_PEAK_SHIFT, /* peaking */ EQ_PEAK_SHIFT, /* peaking */
EQ_SHELF_SHIFT, /* high shelf */ EQ_SHELF_SHIFT, /* high shelf */
}; };
unsigned int channels = dsp->data.num_channels; unsigned int channels = audio_dsp.data.num_channels;
int i; int i;
/* filter configuration currently is 1 low shelf filter, 3 band peaking /* filter configuration currently is 1 low shelf filter, 3 band peaking
@ -944,6 +921,17 @@ static void eq_process(int count, int32_t *buf[])
} }
} }
/**
* Use to enable the equalizer.
*
* @param enable true to enable the equalizer
*/
void dsp_set_eq(bool enable)
{
audio_dsp.eq_process = enable ? eq_process : NULL;
set_gain(&audio_dsp);
}
void dsp_set_stereo_width(int value) void dsp_set_stereo_width(int value)
{ {
long width, straight, cross; long width, straight, cross;
@ -966,50 +954,6 @@ void dsp_set_stereo_width(int value)
dsp_sw_cross = cross << 8; dsp_sw_cross = cross << 8;
} }
#if CONFIG_CODEC == SWCODEC
#ifdef HAVE_SW_TONE_CONTROLS
static void set_tone_controls(void)
{
filter_bishelf_coefs(0xffffffff/NATIVE_FREQUENCY*200,
0xffffffff/NATIVE_FREQUENCY*3500,
bass, treble, -prescale, tone_filter.coefs);
}
#endif
/* Hook back from firmware/ part of audio, which can't/shouldn't call apps/
* code directly.
*/
int dsp_callback(int msg, intptr_t param)
{
switch (msg) {
#ifdef HAVE_SW_TONE_CONTROLS
case DSP_CALLBACK_SET_PRESCALE:
prescale = param;
set_tone_controls();
break;
/* prescaler is always set after calling any of these, so we wait with
* calculating coefs until the above case is hit.
*/
case DSP_CALLBACK_SET_BASS:
bass = param;
break;
case DSP_CALLBACK_SET_TREBLE:
treble = param;
#endif
case DSP_CALLBACK_SET_CHANNEL_CONFIG:
dsp_set_channel_config(param);
break;
case DSP_CALLBACK_SET_STEREO_WIDTH:
dsp_set_stereo_width(param);
break;
default:
break;
}
return 0;
}
#endif
/** /**
* Implements the different channel configurations and stereo width. * Implements the different channel configurations and stereo width.
*/ */
@ -1098,14 +1042,64 @@ void dsp_set_channel_config(int value)
}; };
if ((unsigned)value >= ARRAYLEN(channels_process_functions) || if ((unsigned)value >= ARRAYLEN(channels_process_functions) ||
audio_dsp->stereo_mode == STEREO_MONO) audio_dsp.stereo_mode == STEREO_MONO)
{
value = SOUND_CHAN_STEREO; value = SOUND_CHAN_STEREO;
}
/* This doesn't apply to voice */ /* This doesn't apply to voice */
channels_mode = value; channels_mode = value;
audio_dsp->channels_process = channels_process_functions[value]; audio_dsp.channels_process = channels_process_functions[value];
} }
#if CONFIG_CODEC == SWCODEC
#ifdef HAVE_SW_TONE_CONTROLS
static void set_tone_controls(void)
{
filter_bishelf_coefs(0xffffffff/NATIVE_FREQUENCY*200,
0xffffffff/NATIVE_FREQUENCY*3500,
bass, treble, -prescale,
audio_dsp.tone_filter.coefs);
/* Sync the voice dsp coefficients */
memcpy(&voice_dsp.tone_filter.coefs, audio_dsp.tone_filter.coefs,
sizeof (voice_dsp.tone_filter.coefs));
}
#endif
/* Hook back from firmware/ part of audio, which can't/shouldn't call apps/
* code directly.
*/
int dsp_callback(int msg, intptr_t param)
{
switch (msg) {
#ifdef HAVE_SW_TONE_CONTROLS
case DSP_CALLBACK_SET_PRESCALE:
prescale = param;
set_tone_controls();
break;
/* prescaler is always set after calling any of these, so we wait with
* calculating coefs until the above case is hit.
*/
case DSP_CALLBACK_SET_BASS:
bass = param;
break;
case DSP_CALLBACK_SET_TREBLE:
treble = param;
#endif
case DSP_CALLBACK_SET_CHANNEL_CONFIG:
dsp_set_channel_config(param);
break;
case DSP_CALLBACK_SET_STEREO_WIDTH:
dsp_set_stereo_width(param);
break;
default:
break;
}
return 0;
}
#endif
/* Process and convert src audio to dst based on the DSP configuration, /* Process and convert src audio to dst based on the DSP configuration,
* reading count number of audio samples. dst is assumed to be large * reading count number of audio samples. dst is assumed to be large
* enough; use dsp_output_count() to get the required number. src is an * enough; use dsp_output_count() to get the required number. src is an
@ -1114,7 +1108,7 @@ void dsp_set_channel_config(int value)
* non-interleaved stereo, it contains two pointers, one for each audio * non-interleaved stereo, it contains two pointers, one for each audio
* channel. Returns number of bytes written to dst. * channel. Returns number of bytes written to dst.
*/ */
int dsp_process(char *dst, const char *src[], int count) int dsp_process(struct dsp_config *dsp, char *dst, const char *src[], int count)
{ {
int32_t *tmp[2]; int32_t *tmp[2];
int written = 0; int written = 0;
@ -1142,25 +1136,19 @@ int dsp_process(char *dst, const char *src[], int count)
if (dsp->apply_gain) if (dsp->apply_gain)
dsp->apply_gain(samples, &dsp->data, tmp); dsp->apply_gain(samples, &dsp->data, tmp);
if (dsp->resample && (samples = resample(samples, tmp)) <= 0) if (dsp->resample && (samples = resample(dsp, samples, tmp)) <= 0)
break; /* I'm pretty sure we're downsampling here */ break; /* I'm pretty sure we're downsampling here */
if (dsp->apply_crossfeed) if (dsp->apply_crossfeed)
dsp->apply_crossfeed(samples, tmp); dsp->apply_crossfeed(samples, tmp);
/* TODO: EQ and tone controls need separate structs for audio and voice if (dsp->eq_process)
* DSP processing thanks to filter history. isn't really audible now, but dsp->eq_process(samples, tmp);
* might be the day we start handling voice more delicately. Planned
* changes may well run all relevent channels through the same EQ so
* perhaps not.
*/
if (eq_enabled)
eq_process(samples, tmp);
#ifdef HAVE_SW_TONE_CONTROLS #ifdef HAVE_SW_TONE_CONTROLS
if ((bass | treble) != 0) if ((bass | treble) != 0)
eq_filter(tmp, &tone_filter, samples, dsp->data.num_channels, eq_filter(tmp, &dsp->tone_filter, samples,
FILTER_BISHELF_SHIFT); dsp->data.num_channels, FILTER_BISHELF_SHIFT);
#endif #endif
if (dsp->channels_process) if (dsp->channels_process)
@ -1187,7 +1175,7 @@ int dsp_process(char *dst, const char *src[], int count)
* of the resampler). * of the resampler).
*/ */
/* dsp_input_size MUST be called afterwards */ /* dsp_input_size MUST be called afterwards */
int dsp_output_count(int count) int dsp_output_count(struct dsp_config *dsp, int count)
{ {
if (dsp->resample) if (dsp->resample)
{ {
@ -1209,7 +1197,7 @@ int dsp_output_count(int count)
/* Given count output samples, calculate number of input samples /* Given count output samples, calculate number of input samples
* that would be consumed in order to fill the output buffer. * that would be consumed in order to fill the output buffer.
*/ */
int dsp_input_count(int count) int dsp_input_count(struct dsp_config *dsp, int count)
{ {
/* count is now the number of resampled input samples. Convert to /* count is now the number of resampled input samples. Convert to
original input samples. */ original input samples. */
@ -1225,41 +1213,37 @@ int dsp_input_count(int count)
return count; return count;
} }
int dsp_stereo_mode(void)
{
return dsp->stereo_mode;
}
static void dsp_set_gain_var(long *var, long value) static void dsp_set_gain_var(long *var, long value)
{ {
/* Voice shouldn't mess with these */ *var = value;
if (dsp == audio_dsp) new_gain = true;
{
*var = value;
new_gain = true;
}
} }
static void dsp_update_functions(void) static void dsp_update_functions(struct dsp_config *dsp)
{ {
sample_input_new_format(); sample_input_new_format(dsp);
sample_output_new_format(); sample_output_new_format(dsp);
if (dsp == audio_dsp) if (dsp == &audio_dsp)
dsp_set_crossfeed(crossfeed_enabled); dsp_set_crossfeed(crossfeed_enabled);
} }
bool dsp_configure(int setting, intptr_t value) intptr_t dsp_configure(struct dsp_config *dsp, int setting, intptr_t value)
{ {
switch (setting) switch (setting)
{ {
case DSP_SWITCH_CODEC: case DSP_MYDSP:
if ((uintptr_t)value <= 1) switch (value)
switch_dsp(&dsp_conf[value]); {
break; case CODEC_IDX_AUDIO:
return (intptr_t)&audio_dsp;
case CODEC_IDX_VOICE:
return (intptr_t)&voice_dsp;
default:
return (intptr_t)NULL;
}
case DSP_SET_FREQUENCY: case DSP_SET_FREQUENCY:
memset(&dsp->data.resample_data, 0, memset(&dsp->data.resample_data, 0, sizeof (dsp->data.resample_data));
sizeof (dsp->data.resample_data));
/* Fall through!!! */ /* Fall through!!! */
case DSP_SWITCH_FREQUENCY: case DSP_SWITCH_FREQUENCY:
dsp->codec_frequency = (value == 0) ? NATIVE_FREQUENCY : value; dsp->codec_frequency = (value == 0) ? NATIVE_FREQUENCY : value;
@ -1267,12 +1251,12 @@ bool dsp_configure(int setting, intptr_t value)
if we're called from the main audio thread. Voice UI thread should if we're called from the main audio thread. Voice UI thread should
not need this feature. not need this feature.
*/ */
if (dsp == audio_dsp) if (dsp == &audio_dsp)
dsp->frequency = pitch_ratio * dsp->codec_frequency / 1000; dsp->frequency = pitch_ratio * dsp->codec_frequency / 1000;
else else
dsp->frequency = dsp->codec_frequency; dsp->frequency = dsp->codec_frequency;
resampler_new_delta(); resampler_new_delta(dsp);
break; break;
case DSP_SET_SAMPLE_DEPTH: case DSP_SET_SAMPLE_DEPTH:
@ -1294,14 +1278,14 @@ bool dsp_configure(int setting, intptr_t value)
} }
dsp->data.output_scale = dsp->frac_bits + 1 - NATIVE_DEPTH; dsp->data.output_scale = dsp->frac_bits + 1 - NATIVE_DEPTH;
sample_input_new_format(); sample_input_new_format(dsp);
dither_init(); dither_init(dsp);
break; break;
case DSP_SET_STEREO_MODE: case DSP_SET_STEREO_MODE:
dsp->stereo_mode = value; dsp->stereo_mode = value;
dsp->data.num_channels = value == STEREO_MONO ? 1 : 2; dsp->data.num_channels = value == STEREO_MONO ? 1 : 2;
dsp_update_functions(); dsp_update_functions(dsp);
break; break;
case DSP_RESET: case DSP_RESET:
@ -1315,7 +1299,7 @@ bool dsp_configure(int setting, intptr_t value)
dsp->data.clip_min = -((1 << WORD_FRACBITS)); dsp->data.clip_min = -((1 << WORD_FRACBITS));
dsp->codec_frequency = dsp->frequency = NATIVE_FREQUENCY; dsp->codec_frequency = dsp->frequency = NATIVE_FREQUENCY;
if (dsp == audio_dsp) if (dsp == &audio_dsp)
{ {
track_gain = 0; track_gain = 0;
album_gain = 0; album_gain = 0;
@ -1324,31 +1308,35 @@ bool dsp_configure(int setting, intptr_t value)
new_gain = true; new_gain = true;
} }
dsp_update_functions(); dsp_update_functions(dsp);
resampler_new_delta(); resampler_new_delta(dsp);
break; break;
case DSP_FLUSH: case DSP_FLUSH:
memset(&dsp->data.resample_data, 0, memset(&dsp->data.resample_data, 0,
sizeof (dsp->data.resample_data)); sizeof (dsp->data.resample_data));
resampler_new_delta(); resampler_new_delta(dsp);
dither_init(); dither_init(dsp);
break; break;
case DSP_SET_TRACK_GAIN: case DSP_SET_TRACK_GAIN:
dsp_set_gain_var(&track_gain, value); if (dsp == &audio_dsp)
dsp_set_gain_var(&track_gain, value);
break; break;
case DSP_SET_ALBUM_GAIN: case DSP_SET_ALBUM_GAIN:
dsp_set_gain_var(&album_gain, value); if (dsp == &audio_dsp)
dsp_set_gain_var(&album_gain, value);
break; break;
case DSP_SET_TRACK_PEAK: case DSP_SET_TRACK_PEAK:
dsp_set_gain_var(&track_peak, value); if (dsp == &audio_dsp)
dsp_set_gain_var(&track_peak, value);
break; break;
case DSP_SET_ALBUM_PEAK: case DSP_SET_ALBUM_PEAK:
dsp_set_gain_var(&album_peak, value); if (dsp == &audio_dsp)
dsp_set_gain_var(&album_peak, value);
break; break;
default: default:
@ -1404,5 +1392,5 @@ void dsp_set_replaygain(void)
/* Store in S8.23 format to simplify calculations. */ /* Store in S8.23 format to simplify calculations. */
replaygain = gain; replaygain = gain;
set_gain(audio_dsp); set_gain(&audio_dsp);
} }

View file

@ -32,10 +32,16 @@ enum
STEREO_NUM_MODES, STEREO_NUM_MODES,
}; };
enum
{
CODEC_IDX_AUDIO = 0,
CODEC_IDX_VOICE,
};
enum enum
{ {
CODEC_SET_FILEBUF_WATERMARK = 1, CODEC_SET_FILEBUF_WATERMARK = 1,
DSP_SWITCH_CODEC, DSP_MYDSP,
DSP_SET_FREQUENCY, DSP_SET_FREQUENCY,
DSP_SWITCH_FREQUENCY, DSP_SWITCH_FREQUENCY,
DSP_SET_SAMPLE_DEPTH, DSP_SET_SAMPLE_DEPTH,
@ -201,23 +207,25 @@ enum {
#define DIV64(x, y, z) (long)(((long long)(x) << (z))/(y)) #define DIV64(x, y, z) (long)(((long long)(x) << (z))/(y))
int dsp_process(char *dest, const char *src[], int count); struct dsp_config;
int dsp_input_count(int count);
int dsp_output_count(int count); int dsp_process(struct dsp_config *dsp, char *dest,
int dsp_stereo_mode(void); const char *src[], int count);
bool dsp_configure(int setting, intptr_t value); int dsp_input_count(struct dsp_config *dsp, int count);
int dsp_output_count(struct dsp_config *dsp, int count);
intptr_t dsp_configure(struct dsp_config *dsp, int setting,
intptr_t value);
void dsp_set_replaygain(void); void dsp_set_replaygain(void);
void dsp_set_crossfeed(bool enable); void dsp_set_crossfeed(bool enable);
void dsp_set_crossfeed_direct_gain(int gain); void dsp_set_crossfeed_direct_gain(int gain);
void dsp_set_crossfeed_cross_params(long lf_gain, long hf_gain, long cutoff); void dsp_set_crossfeed_cross_params(long lf_gain, long hf_gain,
long cutoff);
void dsp_set_eq(bool enable); void dsp_set_eq(bool enable);
void dsp_set_eq_precut(int precut); void dsp_set_eq_precut(int precut);
void dsp_set_eq_coefs(int band); void dsp_set_eq_coefs(int band);
void sound_set_pitch(int r); void sound_set_pitch(int r);
int sound_get_pitch(void); int sound_get_pitch(void);
int dsp_callback(int msg, intptr_t param); int dsp_callback(int msg, intptr_t param);
void dsp_set_channel_config(int value);
void dsp_set_stereo_width(int value);
void dsp_dither_enable(bool enable); void dsp_dither_enable(bool enable);
#endif #endif

View file

@ -34,6 +34,7 @@
#include "buffer.h" #include "buffer.h"
#include "settings.h" #include "settings.h"
#include "audio.h" #include "audio.h"
#include "voice_thread.h"
#include "dsp.h" #include "dsp.h"
#include "thread.h" #include "thread.h"
@ -251,15 +252,21 @@ static inline void pcmbuf_add_chunk(void)
#ifdef HAVE_PRIORITY_SCHEDULING #ifdef HAVE_PRIORITY_SCHEDULING
static void boost_codec_thread(bool boost) static void boost_codec_thread(bool boost)
{ {
/* Keep voice and codec threads at the same priority or else voice
* will starve if the codec thread's priority is boosted. */
if (boost) if (boost)
{ {
if (codec_thread_priority == 0) if (codec_thread_priority == 0)
{
codec_thread_priority = thread_set_priority( codec_thread_priority = thread_set_priority(
codec_thread_p, PRIORITY_REALTIME); codec_thread_p, PRIORITY_REALTIME);
voice_thread_set_priority(PRIORITY_REALTIME);
}
} }
else if (codec_thread_priority != 0) else if (codec_thread_priority != 0)
{ {
thread_set_priority(codec_thread_p, codec_thread_priority); thread_set_priority(codec_thread_p, codec_thread_priority);
voice_thread_set_priority(codec_thread_priority);
codec_thread_priority = 0; codec_thread_priority = 0;
} }
} }
@ -717,6 +724,7 @@ static size_t crossfade_mix(const char *buf, size_t length)
crossfade_chunk = crossfade_chunk->link; crossfade_chunk = crossfade_chunk->link;
if (!crossfade_chunk) if (!crossfade_chunk)
return length; return length;
output_buf = (int16_t *)crossfade_chunk->addr; output_buf = (int16_t *)crossfade_chunk->addr;
chunk_end = SKIPBYTES(output_buf, crossfade_chunk->size); chunk_end = SKIPBYTES(output_buf, crossfade_chunk->size);
} }
@ -875,15 +883,18 @@ void* pcmbuf_request_buffer(int *count)
} }
} }
void* pcmbuf_request_voice_buffer(int *count, bool mix) void * pcmbuf_request_voice_buffer(int *count)
{ {
if (mix) /* A get-it-to-work-for-now hack (audio status could change by
completion) */
if (audio_status() & AUDIO_STATUS_PLAY)
{ {
if (pcmbuf_read == NULL) if (pcmbuf_read == NULL)
{ {
return NULL; return NULL;
} }
else if (pcmbuf_mix_chunk || pcmbuf_read->link) else if (pcmbuf_usage() >= 10 && pcmbuf_mix_free() >= 30 &&
(pcmbuf_mix_chunk || pcmbuf_read->link))
{ {
*count = MIN(*count, PCMBUF_MIX_CHUNK/4); *count = MIN(*count, PCMBUF_MIX_CHUNK/4);
return voicebuf; return voicebuf;
@ -894,7 +905,9 @@ void* pcmbuf_request_voice_buffer(int *count, bool mix)
} }
} }
else else
{
return pcmbuf_request_buffer(count); return pcmbuf_request_buffer(count);
}
} }
bool pcmbuf_is_crossfade_active(void) bool pcmbuf_is_crossfade_active(void)
@ -1030,8 +1043,15 @@ int pcmbuf_mix_free(void)
return 100; return 100;
} }
void pcmbuf_mix_voice(int 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 *ibuf = (int16_t *)voicebuf;
int16_t *obuf; int16_t *obuf;
size_t chunk_samples; size_t chunk_samples;
@ -1042,6 +1062,7 @@ void pcmbuf_mix_voice(int count)
/* Start 1/8s into the next chunk */ /* Start 1/8s into the next chunk */
pcmbuf_mix_sample = NATIVE_FREQUENCY * 4 / 16; pcmbuf_mix_sample = NATIVE_FREQUENCY * 4 / 16;
} }
if (!pcmbuf_mix_chunk) if (!pcmbuf_mix_chunk)
return; return;
@ -1053,6 +1074,7 @@ void pcmbuf_mix_voice(int count)
while (count-- > 0) while (count-- > 0)
{ {
int32_t sample = *ibuf++; int32_t sample = *ibuf++;
if (pcmbuf_mix_sample >= chunk_samples) if (pcmbuf_mix_sample >= chunk_samples)
{ {
pcmbuf_mix_chunk = pcmbuf_mix_chunk->link; pcmbuf_mix_chunk = pcmbuf_mix_chunk->link;

View file

@ -67,16 +67,16 @@ void pcmbuf_set_position_callback(void (*callback)(size_t size));
size_t pcmbuf_free(void); size_t pcmbuf_free(void);
unsigned int pcmbuf_get_latency(void); unsigned int pcmbuf_get_latency(void);
void pcmbuf_set_low_latency(bool state); void pcmbuf_set_low_latency(bool state);
void * pcmbuf_request_buffer(int *count);
void pcmbuf_write_complete(int count); void pcmbuf_write_complete(int count);
void* pcmbuf_request_buffer(int *count); void * pcmbuf_request_voice_buffer(int *count);
void* pcmbuf_request_voice_buffer(int *count, bool mix); void pcmbuf_write_voice_complete(int count);
bool pcmbuf_is_crossfade_enabled(void); bool pcmbuf_is_crossfade_enabled(void);
void pcmbuf_crossfade_enable(bool on_off); void pcmbuf_crossfade_enable(bool on_off);
void pcmbuf_crossfade_enable_finished(void); void pcmbuf_crossfade_enable_finished(void);
int pcmbuf_usage(void); int pcmbuf_usage(void);
int pcmbuf_mix_free(void); int pcmbuf_mix_free(void);
void pcmbuf_beep(unsigned int frequency, size_t duration, int amplitude); void pcmbuf_beep(unsigned int frequency, size_t duration, int amplitude);
void pcmbuf_mix_voice(int count);
int pcmbuf_used_descs(void); int pcmbuf_used_descs(void);
int pcmbuf_descs(void); int pcmbuf_descs(void);

File diff suppressed because it is too large Load diff

View file

@ -457,7 +457,7 @@ static const struct plugin_api rockbox_api = {
plugin_get_audio_buffer, plugin_get_audio_buffer,
plugin_tsr, plugin_tsr,
plugin_get_current_filename, plugin_get_current_filename,
#ifdef IRAM_STEAL #ifdef PLUGIN_USE_IRAM
plugin_iram_init, plugin_iram_init,
#endif #endif
#if defined(DEBUG) || defined(SIMULATOR) #if defined(DEBUG) || defined(SIMULATOR)
@ -732,12 +732,13 @@ void* plugin_get_audio_buffer(size_t *buffer_size)
#endif #endif
} }
#ifdef IRAM_STEAL #ifdef PLUGIN_USE_IRAM
/* Initializes plugin IRAM */ /* Initializes plugin IRAM */
void plugin_iram_init(char *iramstart, char *iramcopy, size_t iram_size, void plugin_iram_init(char *iramstart, char *iramcopy, size_t iram_size,
char *iedata, size_t iedata_size) char *iedata, size_t iedata_size)
{ {
audio_iram_steal(); /* We need to stop audio playback in order to use codec IRAM */
audio_hard_stop();
memcpy(iramstart, iramcopy, iram_size); memcpy(iramstart, iramcopy, iram_size);
memset(iedata, 0, iedata_size); memset(iedata, 0, iedata_size);
memset(iramcopy, 0, iram_size); memset(iramcopy, 0, iram_size);
@ -746,7 +747,7 @@ void plugin_iram_init(char *iramstart, char *iramcopy, size_t iram_size,
flush_icache(); flush_icache();
#endif #endif
} }
#endif /* IRAM_STEAL */ #endif /* PLUGIN_USE_IRAM */
/* The plugin wants to stay resident after leaving its main function, e.g. /* The plugin wants to stay resident after leaving its main function, e.g.
runs from timer or own thread. The callback is registered to later runs from timer or own thread. The callback is registered to later

View file

@ -568,7 +568,7 @@ struct plugin_api {
void* (*plugin_get_audio_buffer)(size_t *buffer_size); void* (*plugin_get_audio_buffer)(size_t *buffer_size);
void (*plugin_tsr)(bool (*exit_callback)(bool reenter)); void (*plugin_tsr)(bool (*exit_callback)(bool reenter));
char* (*plugin_get_current_filename)(void); char* (*plugin_get_current_filename)(void);
#ifdef IRAM_STEAL #ifdef PLUGIN_USE_IRAM
void (*plugin_iram_init)(char *iramstart, char *iramcopy, size_t iram_size, void (*plugin_iram_init)(char *iramstart, char *iramcopy, size_t iram_size,
char *iedata, size_t iedata_size); char *iedata, size_t iedata_size);
#endif #endif
@ -674,7 +674,7 @@ extern unsigned char plugin_end_addr[];
NULL, NULL, plugin_start }; NULL, NULL, plugin_start };
#endif /* SIMULATOR */ #endif /* SIMULATOR */
#ifdef USE_IRAM #ifdef PLUGIN_USE_IRAM
/* Declare IRAM variables */ /* Declare IRAM variables */
#define PLUGIN_IRAM_DECLARE \ #define PLUGIN_IRAM_DECLARE \
extern char iramcopy[]; \ extern char iramcopy[]; \
@ -689,13 +689,13 @@ extern unsigned char plugin_end_addr[];
#else #else
#define PLUGIN_IRAM_DECLARE #define PLUGIN_IRAM_DECLARE
#define PLUGIN_IRAM_INIT(api) #define PLUGIN_IRAM_INIT(api)
#endif /* USE_IRAM */ #endif /* PLUGIN_USE_IRAM */
#endif /* PLUGIN */ #endif /* PLUGIN */
int plugin_load(const char* plugin, void* parameter); int plugin_load(const char* plugin, void* parameter);
void* plugin_get_buffer(size_t *buffer_size); void* plugin_get_buffer(size_t *buffer_size);
void* plugin_get_audio_buffer(size_t *buffer_size); void* plugin_get_audio_buffer(size_t *buffer_size);
#ifdef IRAM_STEAL #ifdef PLUGIN_USE_IRAM
void plugin_iram_init(char *iramstart, char *iramcopy, size_t iram_size, void plugin_iram_init(char *iramstart, char *iramcopy, size_t iram_size,
char *iedata, size_t iedata_size); char *iedata, size_t iedata_size);
#endif #endif

View file

@ -56,8 +56,6 @@
| | filebuf | | filebuf
| |------------ | |------------
| | audio | | audio
| |------------
| | codec swap
audiobufend----------+-----------+------------ audiobufend----------+-----------+------------
SWCODEC allocates dedicated buffers, MASCODEC reuses audiobuf. */ SWCODEC allocates dedicated buffers, MASCODEC reuses audiobuf. */
@ -628,7 +626,9 @@ int talk_file(const char* filename, bool enqueue)
{ {
int fd; int fd;
int size; int size;
#if CONFIG_CODEC != SWCODEC
struct mp3entry info; struct mp3entry info;
#endif
if (talk_temp_disable_count > 0) if (talk_temp_disable_count > 0)
return -1; /* talking has been disabled */ return -1; /* talking has been disabled */
@ -640,10 +640,12 @@ int talk_file(const char* filename, bool enqueue)
if (p_thumbnail == NULL || size_for_thumbnail <= 0) if (p_thumbnail == NULL || size_for_thumbnail <= 0)
return -1; return -1;
#if CONFIG_CODEC != SWCODEC
if(mp3info(&info, filename)) /* use this to find real start */ if(mp3info(&info, filename)) /* use this to find real start */
{ {
return 0; /* failed to open, or invalid */ return 0; /* failed to open, or invalid */
} }
#endif
fd = open(filename, O_RDONLY); fd = open(filename, O_RDONLY);
if (fd < 0) /* failed to open */ if (fd < 0) /* failed to open */
@ -651,14 +653,16 @@ int talk_file(const char* filename, bool enqueue)
return 0; return 0;
} }
#if CONFIG_CODEC != SWCODEC
lseek(fd, info.first_frame_offset, SEEK_SET); /* behind ID data */ lseek(fd, info.first_frame_offset, SEEK_SET); /* behind ID data */
#endif
size = read(fd, p_thumbnail, size_for_thumbnail); size = read(fd, p_thumbnail, size_for_thumbnail);
close(fd); close(fd);
/* ToDo: find audio, skip ID headers and trailers */ /* ToDo: find audio, skip ID headers and trailers */
if (size != 0 && size != size_for_thumbnail) /* Don't play missing or truncated clips */ if (size > 0 && size != size_for_thumbnail) /* Don't play missing or truncated clips */
{ {
#if CONFIG_CODEC != SWCODEC && !defined(SIMULATOR) #if CONFIG_CODEC != SWCODEC && !defined(SIMULATOR)
bitswap(p_thumbnail, size); bitswap(p_thumbnail, size);

View file

@ -27,7 +27,7 @@
#include <stdbool.h> #include <stdbool.h>
#include "time.h" #include "time.h"
#define VOICE_VERSION 300 /* 3.00 - if you change this, change it in voicefont too */ #define VOICE_VERSION 400 /* 4.00 - if you change this, change it in voicefont too */
enum { enum {
/* See array "unit_voiced" in talk.c function "talk_value" */ /* See array "unit_voiced" in talk.c function "talk_value" */

444
apps/voice_thread.c Normal file
View file

@ -0,0 +1,444 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2007 Michael Sevakis
*
* All files in this archive are subject to the GNU General Public License.
* See the file COPYING in the source tree root for full license agreement.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include "system.h"
#include "thread.h"
#include "logf.h"
#include "voice_thread.h"
#include "talk.h"
#include "dsp.h"
#include "audio.h"
#include "pcmbuf.h"
#include "codecs/libspeex/speex/speex.h"
/* Define any of these as "1" to log regular and/or timeout messages */
#define VOICE_LOGQUEUES 0
#define VOICE_LOGQUEUES_SYS_TIMEOUT 0
#if VOICE_LOGQUEUES
#define LOGFQUEUE logf
#else
#define LOGFQUEUE(...)
#endif
#if VOICE_LOGQUEUES_SYS_TIMEOUT
#define LOGFQUEUE_SYS_TIMEOUT logf
#else
#define LOGFQUEUE_SYS_TIMEOUT(...)
#endif
#ifndef IBSS_ATTR_VOICE_STACK
#define IBSS_ATTR_VOICE_STACK IBSS_ATTR
#endif
#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 struct thread_entry *voice_thread_p = NULL;
static long voice_stack[0x740/sizeof(long)] IBSS_ATTR_VOICE_STACK;
static const char voice_thread_name[] = "voice";
/* Voice thread synchronization objects */
static struct event_queue voice_queue NOCACHEBSS_ATTR;
static struct mutex voice_mutex NOCACHEBSS_ATTR;
static struct event voice_event NOCACHEBSS_ATTR;
static struct queue_sender_list voice_queue_sender_list NOCACHEBSS_ATTR;
/* Buffer for decoded samples */
static spx_int16_t voice_output_buf[VOICE_FRAME_SIZE] CACHEALIGN_ATTR;
enum voice_thread_states
{
TSTATE_STOPPED = 0, /* Voice thread is stopped and awaiting commands */
TSTATE_DECODE, /* Voice is decoding a clip */
TSTATE_BUFFER_INSERT, /* Voice is sending decoded audio to PCM */
};
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 */
struct voice_info
{
pcm_more_callback_type get_more; /* Callback to get more clips */
unsigned char *start; /* Start of clip */
ssize_t size; /* Size of clip */
};
/* Private thread data for its current state that must be passed to its
* internal functions */
struct voice_thread_data
{
int state; /* Thread state (TSTATE_*) */
struct queue_event ev; /* Last queue event pulled from queue */
void *st; /* Decoder instance */
SpeexBits bits; /* Bit cursor */
struct dsp_config *dsp; /* DSP used for voice output */
struct voice_info vi; /* Copy of clip 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 */
};
/* Audio playback is in a playing state? */
static inline bool playback_is_playing(void)
{
return (audio_status() & AUDIO_STATUS_PLAY) != 0;
}
/* Stop any current clip and start playing a new one */
void mp3_play_data(const unsigned char* start, int size,
pcm_more_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 NOCACHEBSS_ATTR;
if (get_more != NULL && start != NULL && size > 0)
{
mutex_lock(&voice_mutex);
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);
}
}
/* Stop current voice clip from playing */
void mp3_play_stop(void)
{
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);
}
void mp3_play_pause(bool play)
{
/* a dummy */
(void)play;
}
/* Tell is 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;
}
/* 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();
mutex_unlock(&voice_mutex);
} /* voice_stop */
/* Wait for voice to finish speaking. */
void voice_wait(void)
{
/* NOTE: One problem here is that we can't tell if another thread started a
* new clip by the time we wait. This should be resolvable if conditions
* ever require knowing the very clip you requested has finished. */
event_wait(&voice_event, STATE_SIGNALED);
}
/* Initialize voice thread data that must be valid upon starting and the
* setup the DSP parameters */
static void voice_data_init(struct voice_thread_data *td)
{
td->state = TSTATE_STOPPED;
td->dsp = (struct dsp_config *)dsp_configure(NULL, DSP_MYDSP,
CODEC_IDX_VOICE);
dsp_configure(td->dsp, DSP_RESET, 0);
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);
}
/* Voice thread message processing */
static void voice_message(struct voice_thread_data *td)
{
while (1)
{
switch (td->ev.id)
{
case Q_VOICE_PLAY:
LOGFQUEUE("voice < Q_VOICE_PLAY");
/* Put up a block for completion signal */
event_set_state(&voice_event, STATE_NONSIGNALED);
/* Copy the clip info */
td->vi = *(struct voice_info *)td->ev.data;
/* Be sure audio buffer is initialized */
audio_restore_playback(AUDIO_WANT_VOICE);
/* We need nothing more from the sending thread - let it run */
queue_reply(&voice_queue, 1);
if (td->state == TSTATE_STOPPED)
{
/* Boost CPU now */
trigger_cpu_boost();
}
else if (!playback_is_playing())
{
/* Just voice, stop any clip still playing */
pcmbuf_play_stop();
}
/* Clean-start the decoder */
td->st = speex_decoder_init(&speex_wb_mode);
/* Make bit buffer use our own buffer */
speex_bits_set_bit_buffer(&td->bits, td->vi.start, td->vi.size);
speex_decoder_ctl(td->st, SPEEX_GET_LOOKAHEAD, &td->lookahead);
td->state = TSTATE_DECODE;
return;
case Q_VOICE_STOP:
LOGFQUEUE("voice < Q_VOICE_STOP: %d", ev.data);
if (td->ev.data != 0 && !playback_is_playing())
{
/* If not playing, it's just voice so stop pcm playback */
pcmbuf_play_stop();
}
/* Cancel boost */
sleep(0);
td->state = TSTATE_STOPPED;
event_set_state(&voice_event, STATE_SIGNALED);
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;
default:
/* Default messages get a reply and thread continues with no
* state transition */
LOGFQUEUE("voice < default");
if (td->state == TSTATE_STOPPED)
break; /* Not in playback state */
queue_reply(&voice_queue, 0);
return;
}
queue_wait(&voice_queue, &td->ev);
}
}
/* Voice thread entrypoint */
static void voice_thread(void)
{
struct voice_thread_data td;
voice_data_init(&td);
goto message_wait;
while (1)
{
td.state = TSTATE_DECODE;
if (!queue_empty(&voice_queue))
{
message_wait:
queue_wait(&voice_queue, &td.ev);
message_process:
voice_message(&td);
/* Branch to initial start point or branch back to previous
* operation if interrupted by a message */
switch (td.state)
{
case TSTATE_DECODE: goto voice_decode;
case TSTATE_BUFFER_INSERT: goto buffer_insert;
default: goto message_wait;
}
}
voice_decode:
/* Check if all data was exhausted for this clip */
if (speex_bits_remaining(&td.bits) < 8)
{
voice_error:
/* Get next clip */
td.vi.size = 0;
if (td.vi.get_more != NULL)
td.vi.get_more(&td.vi.start, &td.vi.size);
if (td.vi.start != NULL && td.vi.size > 0)
{
/* Make bit buffer use our own buffer */
speex_bits_set_bit_buffer(&td.bits, td.vi.start, td.vi.size);
speex_decoder_ctl(td.st, SPEEX_GET_LOOKAHEAD, &td.lookahead);
yield();
if (!queue_empty(&voice_queue))
goto message_wait;
else
goto voice_decode;
}
/* 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();
goto message_process;
}
/* Decode the data */
int status = speex_decode_int(td.st, &td.bits, voice_output_buf);
yield();
if (status == -2)
goto voice_error; /* error - try some more */
/* Output the decoded frame */
td.count = VOICE_FRAME_SIZE - td.lookahead;
td.src[0] = (const char *)&voice_output_buf[td.lookahead];
td.src[1] = NULL;
td.lookahead -= MIN(VOICE_FRAME_SIZE, td.lookahead);
buffer_insert:
/* Process the PCM samples in the DSP and send out for mixing */
td.state = TSTATE_BUFFER_INSERT;
while (td.count > 0)
{
int out_count = dsp_output_count(td.dsp, td.count);
int inp_count;
char *dest;
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)
break;
pcmbuf_write_voice_complete(out_count);
td.count -= inp_count;
}
yield();
} /* 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);
queue_enable_queue_send(&voice_queue, &voice_queue_sender_list);
mutex_init(&voice_mutex);
event_init(&voice_event, STATE_SIGNALED | EVENT_MANUAL);
voice_thread_p = create_thread(voice_thread, voice_stack,
sizeof(voice_stack), CREATE_THREAD_FROZEN,
voice_thread_name IF_PRIO(, PRIORITY_PLAYBACK) IF_COP(, CPU));
} /* voice_thread_init */
/* Unfreeze the voice thread */
void voice_thread_resume(void)
{
logf("Thawing voice thread");
thread_thaw(voice_thread_p);
/* Wait for initialization to complete (a very short wait until the
* voice thread is available to process messages) */
queue_send(&voice_queue, Q_VOICE_NULL, 0);
}
#ifdef HAVE_PRIORITY_SCHEDULING
/* Set the voice thread priority */
void voice_thread_set_priority(int priority)
{
thread_set_priority(voice_thread_p, priority);
}
#endif

33
apps/voice_thread.h Normal file
View file

@ -0,0 +1,33 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2007 Michael Sevakis
*
* All files in this archive are subject to the GNU General Public License.
* See the file COPYING in the source tree root for full license agreement.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#ifndef VOICE_THREAD_H
#define VOICE_THREAD_H
void mp3_play_data(const unsigned char* start, int size,
void (*get_more)(unsigned char** start, size_t* size));
void mp3_play_stop(void);
void mp3_play_pause(bool play);
bool mp3_is_playing(void);
void voice_stop(void);
void voice_thread_init(void);
void voice_thread_resume(void);
void voice_thread_set_priority(int priority);
#endif /* VOICE_THREAD_H */

View file

@ -282,7 +282,6 @@ target/coldfire/crt0.S
target/coldfire/memcpy-coldfire.S target/coldfire/memcpy-coldfire.S
target/coldfire/memmove-coldfire.S target/coldfire/memmove-coldfire.S
target/coldfire/memset-coldfire.S target/coldfire/memset-coldfire.S
target/coldfire/memswap128-coldfire.S
target/coldfire/strlen-coldfire.S target/coldfire/strlen-coldfire.S
#if defined(HAVE_LCD_COLOR) \ #if defined(HAVE_LCD_COLOR) \
|| defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_PIXELFORMAT == VERTICAL_INTERLEAVED) || defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_PIXELFORMAT == VERTICAL_INTERLEAVED)
@ -305,7 +304,6 @@ common/strlen.c
#ifndef SIMULATOR #ifndef SIMULATOR
target/arm/memset-arm.S target/arm/memset-arm.S
target/arm/memset16-arm.S target/arm/memset16-arm.S
target/arm/memswap128-arm.S
#if CONFIG_I2C == I2C_PP5024 || CONFIG_I2C == I2C_PP5020 || CONFIG_I2C == I2C_PP5002 #if CONFIG_I2C == I2C_PP5024 || CONFIG_I2C == I2C_PP5020 || CONFIG_I2C == I2C_PP5002
target/arm/i2c-pp.c target/arm/i2c-pp.c
#elif CONFIG_I2C == I2C_PNX0101 #elif CONFIG_I2C == I2C_PNX0101
@ -350,7 +348,6 @@ common/memcpy.c
common/memmove.c common/memmove.c
common/memset.c common/memset.c
common/memset16.c common/memset16.c
common/memswap128.c
common/strlen.c common/strlen.c
#ifndef SIMULATOR #ifndef SIMULATOR
crt0.S crt0.S

View file

@ -1,44 +0,0 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2007 by Michael Sevakis
*
* All files in this archive are subject to the GNU General Public License.
* See the file COPYING in the source tree root for full license agreement.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include "config.h"
#include <string.h>
#include <inttypes.h>
void memswap128(void *a, void *b, size_t len)
{
for (len >>= 4; len > 0; len--, a += 16, b += 16)
{
int32_t a0 = *((int32_t *)a + 0);
int32_t a1 = *((int32_t *)a + 1);
int32_t a2 = *((int32_t *)a + 2);
int32_t a3 = *((int32_t *)a + 3);
int32_t b0 = *((int32_t *)b + 0);
int32_t b1 = *((int32_t *)b + 1);
int32_t b2 = *((int32_t *)b + 2);
int32_t b3 = *((int32_t *)b + 3);
*((int32_t *)b + 0) = a0;
*((int32_t *)b + 1) = a1;
*((int32_t *)b + 2) = a2;
*((int32_t *)b + 3) = a3;
*((int32_t *)a + 0) = b0;
*((int32_t *)a + 1) = b1;
*((int32_t *)a + 2) = b2;
*((int32_t *)a + 3) = b3;
}
}

View file

@ -109,10 +109,16 @@ void audio_beep(int duration);
void audio_init_playback(void); void audio_init_playback(void);
/* Required call when audio buffer is require for some other purpose */ /* Required call when audio buffer is require for some other purpose */
unsigned char *audio_get_buffer(bool talk_buf, size_t *buffer_size); unsigned char *audio_get_buffer(bool talk_buf, size_t *buffer_size);
#ifdef IRAM_STEAL /* Stops audio from serving playback */
/* Required call when codec IRAM is needed for some other purpose */ void audio_hard_stop(void);
void audio_iram_steal(void); /* Retores the audio buffer to handle the requested playback */
#endif enum
{
AUDIO_WANT_PLAYBACK = 0,
AUDIO_WANT_VOICE,
};
bool audio_restore_playback(int type);
/* channel modes */ /* channel modes */
enum rec_channel_modes enum rec_channel_modes

View file

@ -383,7 +383,7 @@
#define IBSS_ATTR __attribute__ ((section(".ibss"))) #define IBSS_ATTR __attribute__ ((section(".ibss")))
#define USE_IRAM #define USE_IRAM
#if CONFIG_CPU != SH7034 #if CONFIG_CPU != SH7034
#define IRAM_STEAL #define PLUGIN_USE_IRAM
#endif #endif
#if defined(CPU_ARM) #if defined(CPU_ARM)
/* GCC quirk workaround: arm-elf-gcc treats static functions as short_call /* GCC quirk workaround: arm-elf-gcc treats static functions as short_call

View file

@ -1,44 +0,0 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2007 by Michael Sevakis
*
* All files in this archive are subject to the GNU General Public License.
* See the file COPYING in the source tree root for full license agreement.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
/****************************************************************************
* void memswap128(void *buf1, void *buf2, size_t len)
*/
.section .icode, "ax", %progbits
.align 2
.global memswap128
.type memswap128, %function
memswap128:
@ r0 = buf1
@ r1 = buf2
@ r2 = len
movs r2, r2, lsr #4 @ bytes => lines, len == 0?
moveq pc, lr @ not at least a line? leave
stmdb sp!, { r4-r10, lr } @ save registers and return address
.loop: @
ldmia r0, { r3-r6 } @ read four longwords from buf1
ldmia r1, { r7-r10 } @ read four longwords from buf2
stmia r0!, { r7-r10 } @ write buf2 data to buf1, buf1 += 16
stmia r1!, { r3-r6 } @ write buf1 data to buf2, buf2 += 16
subs r2, r2, #1 @ len -= 1, len > 0 ?
bhi .loop @ yes? keep exchanging
ldmia sp!, { r4-r10, pc } @ restore registers and return
.end:
.size memswap128, .end-memswap128

View file

@ -1,50 +0,0 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2007 by Michael Sevakis
*
* All files in this archive are subject to the GNU General Public License.
* See the file COPYING in the source tree root for full license agreement.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
/****************************************************************************
* void memswap128(void *buf1, void *buf2, size_t len)
*/
.section .icode, "ax", @progbits
.align 2
.global memswap128
.type memswap128, @function
memswap128:
lea.l -28(%sp), %sp | save registers
movem.l %d2-%d7/%a2, (%sp) |
movem.l 32(%sp), %a0-%a2 | %a0 = buf1
| %a1 = buf2
| %a2 = len
lea.l -15(%a0, %a2.l), %a2 | %a2 = end address - 15
cmp.l %a0, %a2 | end address <= buf1?
bls.b .no_lines | not at least a line? leave
.loop: |
movem.l (%a0), %d0-%d3 | read four longwords from buf1
movem.l (%a1), %d4-%d7 | read four longwords from buf2
movem.l %d4-%d7, (%a0) | write buf2 data to buf1
movem.l %d0-%d3, (%a1) | write buf1 data to buf2
lea.l 16(%a0), %a0 | buf1 += 16
lea.l 16(%a1), %a1 | buf2 += 16
cmp.l %a0, %a2 | %a0 < %d0?
bhi.b .loop | yes? keep exchanging
.no_lines: |
movem.l (%sp), %d2-%d7/%a2 | restore registers
lea.l 28(%sp), %sp |
rts |
.end:
.size memswap128, .end-memswap128

View file

@ -178,7 +178,7 @@ int main (int argc, char** argv)
/* Create the file format: */ /* Create the file format: */
/* 1st 32 bit value in the file is the version number */ /* 1st 32 bit value in the file is the version number */
value = SWAP4(300); /* 3.00 */ value = SWAP4(400); /* 4.00 */
fwrite(&value, sizeof(value), 1, pFile); fwrite(&value, sizeof(value), 1, pFile);
/* 2nd 32 bit value in the file is the id number for the target /* 2nd 32 bit value in the file is the id number for the target