mirror of
https://github.com/Rockbox/rockbox.git
synced 2025-12-10 13:45:10 -05:00
add crossfeed dsp effect. Makes some music more enjoyable with headphones.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@7884 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
parent
f5aebf7848
commit
e13fad3b4b
6 changed files with 140 additions and 3 deletions
112
apps/dsp.c
112
apps/dsp.c
|
|
@ -41,6 +41,15 @@
|
|||
#define RESAMPLE_BUF_SIZE (256 * 4) /* Enough for 11,025 Hz -> 44,100 Hz*/
|
||||
#define DEFAULT_REPLAYGAIN 0x01000000
|
||||
|
||||
/* These are the constants for the filters in the crossfeed */
|
||||
|
||||
#define ATT 0x0CCCCCCDL /* 0.1 */
|
||||
#define ATT_COMP 0x73333333L /* 0.9 */
|
||||
#define LOW 0x4CCCCCCDL /* 0.6 */
|
||||
#define LOW_COMP 0x33333333L /* 0.4 */
|
||||
#define HIGH_NEG 0x9999999AL /* -0.2 */
|
||||
#define HIGH_COMP 0x66666666L /* 0.8 */
|
||||
|
||||
#if defined(CPU_COLDFIRE) && !defined(SIMULATOR)
|
||||
|
||||
/* Multiply two S.31 fractional integers and return the sign bit and the
|
||||
|
|
@ -86,6 +95,20 @@
|
|||
(t << 8) | (u & 0xff); \
|
||||
})
|
||||
|
||||
|
||||
#define ACC(acc, x, y) \
|
||||
(void)acc; \
|
||||
asm volatile ("mac.l %[a], %[b], %%acc0" \
|
||||
: : [a] "i,r" (x), [b] "i,r" (y));
|
||||
#define GET_ACC(acc) \
|
||||
({ \
|
||||
long t; \
|
||||
(void)acc; \
|
||||
asm volatile ("movclr.l %%acc0, %[t]" \
|
||||
: [t] "=r" (t)); \
|
||||
t; \
|
||||
})
|
||||
|
||||
#else
|
||||
|
||||
#define FRACMUL(x, y) (long) (((((long long) (x)) * ((long long) (y))) >> 31))
|
||||
|
|
@ -115,6 +138,7 @@ struct dsp_config
|
|||
int frac_bits;
|
||||
bool dither_enabled;
|
||||
bool new_gain;
|
||||
bool crossfeed_enabled;
|
||||
};
|
||||
|
||||
struct resample_data
|
||||
|
|
@ -130,9 +154,18 @@ struct dither_data
|
|||
long random;
|
||||
};
|
||||
|
||||
struct crossfeed_data
|
||||
{
|
||||
long lowpass[2];
|
||||
long highpass[2];
|
||||
long delay[2][13];
|
||||
int index;
|
||||
};
|
||||
|
||||
static struct dsp_config dsp_conf[2] IBSS_ATTR;
|
||||
static struct dither_data dither_data[2] IBSS_ATTR;
|
||||
static struct resample_data resample_data[2][2] IBSS_ATTR;
|
||||
static struct crossfeed_data crossfeed_data IBSS_ATTR;
|
||||
|
||||
extern int current_codec;
|
||||
struct dsp_config *dsp;
|
||||
|
|
@ -145,7 +178,6 @@ struct dsp_config *dsp;
|
|||
static long sample_buf[SAMPLE_BUF_SIZE] IBSS_ATTR;
|
||||
static long resample_buf[RESAMPLE_BUF_SIZE] IBSS_ATTR;
|
||||
|
||||
|
||||
/* Convert at most count samples to the internal format, if needed. Returns
|
||||
* number of samples ready for further processing. Updates src to point
|
||||
* past the samples "consumed" and dst is set to point to the samples to
|
||||
|
|
@ -406,6 +438,76 @@ static long dither_sample(long sample, long bias, long mask,
|
|||
return output;
|
||||
}
|
||||
|
||||
/* Apply a constant gain to the samples (e.g., for ReplayGain). May update
|
||||
* the src array if gain was applied.
|
||||
* Note that this must be called before the resampler.
|
||||
*/
|
||||
static void apply_crossfeed(long* src[], int count)
|
||||
{
|
||||
|
||||
if (dsp->crossfeed_enabled && src[0] != src[1])
|
||||
{
|
||||
long long a;
|
||||
|
||||
long low_left = crossfeed_data.lowpass[0];
|
||||
long low_right = crossfeed_data.lowpass[1];
|
||||
long high_left = crossfeed_data.highpass[0];
|
||||
long high_right = crossfeed_data.highpass[1];
|
||||
unsigned int index = crossfeed_data.index;
|
||||
|
||||
long left, right;
|
||||
|
||||
long * delay_l = crossfeed_data.delay[0];
|
||||
long * delay_r = crossfeed_data.delay[1];
|
||||
|
||||
int i;
|
||||
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
/* use a low-pass filter on the signal */
|
||||
left = src[0][i];
|
||||
right = src[1][i];
|
||||
|
||||
ACC(a, LOW, low_left); ACC(a, LOW_COMP, left);
|
||||
low_left = GET_ACC(a);
|
||||
|
||||
ACC(a, LOW, low_right); ACC(a, LOW_COMP, right);
|
||||
low_right = GET_ACC(a);
|
||||
|
||||
/* use a high-pass filter on the signal */
|
||||
|
||||
ACC(a, HIGH_NEG, high_left); ACC(a, HIGH_COMP, left);
|
||||
high_left = GET_ACC(a);
|
||||
|
||||
ACC(a, HIGH_NEG, high_right); ACC(a, HIGH_COMP, right);
|
||||
high_right = GET_ACC(a);
|
||||
|
||||
/* New data is the high-passed signal + delayed and attenuated
|
||||
* low-passed signal from the other channel */
|
||||
|
||||
ACC(a, ATT, delay_r[index]); ACC(a, ATT_COMP, high_left);
|
||||
src[0][i] = GET_ACC(a);
|
||||
|
||||
ACC(a, ATT, delay_l[index]); ACC(a, ATT_COMP, high_right);
|
||||
src[1][i] = GET_ACC(a);
|
||||
|
||||
/* Store the low-passed signal in the ringbuffer */
|
||||
|
||||
delay_l[index] = low_left;
|
||||
delay_r[index] = low_right;
|
||||
|
||||
index = (index + 1) % 13;
|
||||
}
|
||||
|
||||
crossfeed_data.index = index;
|
||||
crossfeed_data.lowpass[0] = low_left;
|
||||
crossfeed_data.lowpass[1] = low_right;
|
||||
crossfeed_data.highpass[0] = high_left;
|
||||
crossfeed_data.highpass[1] = high_right;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/* Apply a constant gain to the samples (e.g., for ReplayGain). May update
|
||||
* the src array if gain was applied.
|
||||
* Note that this must be called before the resampler.
|
||||
|
|
@ -509,6 +611,7 @@ long dsp_process(char* dst, char* src[], long size)
|
|||
size -= samples;
|
||||
apply_gain(tmp, samples);
|
||||
samples = resample(tmp, samples);
|
||||
apply_crossfeed(tmp, samples);
|
||||
write_samples((short*) dst, tmp, samples);
|
||||
written += samples;
|
||||
dst += samples * sizeof(short) * 2;
|
||||
|
|
@ -698,6 +801,13 @@ bool dsp_configure(int setting, void *value)
|
|||
return 1;
|
||||
}
|
||||
|
||||
void dsp_set_crossfeed(bool enable)
|
||||
{
|
||||
if (enable)
|
||||
memset(&crossfeed_data, 0, sizeof(crossfeed_data));
|
||||
dsp->crossfeed_enabled = enable;
|
||||
}
|
||||
|
||||
void dsp_set_replaygain(bool always)
|
||||
{
|
||||
dsp = &dsp_conf[current_codec];
|
||||
|
|
|
|||
|
|
@ -44,7 +44,8 @@ enum {
|
|||
DSP_SET_TRACK_GAIN,
|
||||
DSP_SET_ALBUM_GAIN,
|
||||
DSP_SET_TRACK_PEAK,
|
||||
DSP_SET_ALBUM_PEAK
|
||||
DSP_SET_ALBUM_PEAK,
|
||||
DSP_CROSSFEED
|
||||
};
|
||||
|
||||
long dsp_process(char *dest, char *src[], long size);
|
||||
|
|
@ -53,5 +54,6 @@ long dsp_output_size(long size);
|
|||
int dsp_stereo_mode(void);
|
||||
bool dsp_configure(int setting, void *value);
|
||||
void dsp_set_replaygain(bool always);
|
||||
void dsp_set_crossfeed(bool enable);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -3388,3 +3388,15 @@ desc: error message when preset list is empty
|
|||
eng: "Screen frozen!"
|
||||
voice: ""
|
||||
new:
|
||||
|
||||
id: LANG_CROSSFEED_ENABLE
|
||||
desc: In the crossfeed menu
|
||||
eng: "Enable crossfeed"
|
||||
voice:
|
||||
new:
|
||||
|
||||
id: LANG_CROSSFEED
|
||||
desc: in the sound settings menu
|
||||
eng: "Crossfeed"
|
||||
voice:
|
||||
new:
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ const char rec_base_directory[] = REC_BASE_DIR;
|
|||
#include "dsp.h"
|
||||
#endif
|
||||
|
||||
#define CONFIG_BLOCK_VERSION 30
|
||||
#define CONFIG_BLOCK_VERSION 31
|
||||
#define CONFIG_BLOCK_SIZE 512
|
||||
#define RTC_BLOCK_SIZE 44
|
||||
|
||||
|
|
@ -444,6 +444,7 @@ static const struct bit_entry hd_bits[] =
|
|||
{4, S_O(crossfade_fade_in_duration), 0, "crossfade fade in duration", NULL},
|
||||
{4, S_O(crossfade_fade_out_duration), 0, "crossfade fade out duration", NULL},
|
||||
{1, S_O(crossfade_fade_out_mixmode), 0, "crossfade fade out mode", "crossfade,mix"},
|
||||
{1, S_O(crossfeed), false, "crossfeed", off_on },
|
||||
#endif
|
||||
#ifdef HAVE_DIRCACHE
|
||||
{1, S_O(dircache), false, "dircache", off_on },
|
||||
|
|
@ -912,6 +913,7 @@ void settings_apply(void)
|
|||
#if CONFIG_CODEC == SWCODEC
|
||||
audio_set_crossfade(global_settings.crossfade);
|
||||
dsp_set_replaygain(true);
|
||||
dsp_set_crossfeed(global_settings.crossfeed);
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SPDIF_POWER
|
||||
|
|
|
|||
|
|
@ -353,6 +353,7 @@ struct user_settings
|
|||
shuffle is on, album gain otherwise */
|
||||
int replaygain_preamp; /* scale replaygained tracks by this */
|
||||
int beep; /* system beep volume when changing tracks etc. */
|
||||
bool crossfeed; /* enable crossfeed */
|
||||
#endif
|
||||
#ifdef HAVE_DIRCACHE
|
||||
bool dircache; /* enable directory cache */
|
||||
|
|
|
|||
|
|
@ -1203,6 +1203,15 @@ static bool replaygain_settings_menu(void)
|
|||
return result;
|
||||
}
|
||||
|
||||
static bool crossfeed(void)
|
||||
{
|
||||
bool result = set_bool(str(LANG_CROSSFEED_ENABLE),
|
||||
&global_settings.crossfeed);
|
||||
|
||||
dsp_set_crossfeed(global_settings.crossfeed);
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool crossfade(void)
|
||||
{
|
||||
static const struct opt_items names[] = {
|
||||
|
|
@ -1354,6 +1363,7 @@ static bool playback_settings_menu(void)
|
|||
#if CONFIG_CODEC == SWCODEC
|
||||
{ ID2P(LANG_CROSSFADE), crossfade_settings_menu },
|
||||
{ ID2P(LANG_REPLAYGAIN), replaygain_settings_menu },
|
||||
{ ID2P(LANG_CROSSFEED), crossfeed },
|
||||
{ ID2P(LANG_BEEP), beep },
|
||||
#endif
|
||||
#ifdef HAVE_SPDIF_POWER
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue