mirror of
https://github.com/Rockbox/rockbox.git
synced 2025-10-13 18:17:39 -04:00
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:
parent
75432619e8
commit
99617d71ba
29 changed files with 753 additions and 1153 deletions
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
290
apps/dsp.c
290
apps/dsp.c
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
26
apps/dsp.h
26
apps/dsp.h
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
854
apps/playback.c
854
apps/playback.c
File diff suppressed because it is too large
Load diff
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
10
apps/talk.c
10
apps/talk.c
|
@ -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);
|
||||||
|
|
|
@ -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
444
apps/voice_thread.c
Normal 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
33
apps/voice_thread.h
Normal 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 */
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
|
||||||
|
|
|
@ -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
|
|
|
@ -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
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue