mirror of
https://github.com/Rockbox/rockbox.git
synced 2025-12-10 05:35:20 -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 RESAMPLE_BUF_SIZE (256 * 4) /* Enough for 11,025 Hz -> 44,100 Hz*/
|
||||||
#define DEFAULT_REPLAYGAIN 0x01000000
|
#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)
|
#if defined(CPU_COLDFIRE) && !defined(SIMULATOR)
|
||||||
|
|
||||||
/* Multiply two S.31 fractional integers and return the sign bit and the
|
/* Multiply two S.31 fractional integers and return the sign bit and the
|
||||||
|
|
@ -86,6 +95,20 @@
|
||||||
(t << 8) | (u & 0xff); \
|
(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
|
#else
|
||||||
|
|
||||||
#define FRACMUL(x, y) (long) (((((long long) (x)) * ((long long) (y))) >> 31))
|
#define FRACMUL(x, y) (long) (((((long long) (x)) * ((long long) (y))) >> 31))
|
||||||
|
|
@ -115,6 +138,7 @@ struct dsp_config
|
||||||
int frac_bits;
|
int frac_bits;
|
||||||
bool dither_enabled;
|
bool dither_enabled;
|
||||||
bool new_gain;
|
bool new_gain;
|
||||||
|
bool crossfeed_enabled;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct resample_data
|
struct resample_data
|
||||||
|
|
@ -130,9 +154,18 @@ struct dither_data
|
||||||
long random;
|
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 dsp_config dsp_conf[2] IBSS_ATTR;
|
||||||
static struct dither_data dither_data[2] IBSS_ATTR;
|
static struct dither_data dither_data[2] IBSS_ATTR;
|
||||||
static struct resample_data resample_data[2][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;
|
extern int current_codec;
|
||||||
struct dsp_config *dsp;
|
struct dsp_config *dsp;
|
||||||
|
|
@ -145,7 +178,6 @@ struct dsp_config *dsp;
|
||||||
static long sample_buf[SAMPLE_BUF_SIZE] IBSS_ATTR;
|
static long sample_buf[SAMPLE_BUF_SIZE] IBSS_ATTR;
|
||||||
static long resample_buf[RESAMPLE_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
|
/* Convert at most count samples to the internal format, if needed. Returns
|
||||||
* number of samples ready for further processing. Updates src to point
|
* 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
|
* 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;
|
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
|
/* Apply a constant gain to the samples (e.g., for ReplayGain). May update
|
||||||
* the src array if gain was applied.
|
* the src array if gain was applied.
|
||||||
* Note that this must be called before the resampler.
|
* Note that this must be called before the resampler.
|
||||||
|
|
@ -509,6 +611,7 @@ long dsp_process(char* dst, char* src[], long size)
|
||||||
size -= samples;
|
size -= samples;
|
||||||
apply_gain(tmp, samples);
|
apply_gain(tmp, samples);
|
||||||
samples = resample(tmp, samples);
|
samples = resample(tmp, samples);
|
||||||
|
apply_crossfeed(tmp, samples);
|
||||||
write_samples((short*) dst, tmp, samples);
|
write_samples((short*) dst, tmp, samples);
|
||||||
written += samples;
|
written += samples;
|
||||||
dst += samples * sizeof(short) * 2;
|
dst += samples * sizeof(short) * 2;
|
||||||
|
|
@ -698,6 +801,13 @@ bool dsp_configure(int setting, void *value)
|
||||||
return 1;
|
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)
|
void dsp_set_replaygain(bool always)
|
||||||
{
|
{
|
||||||
dsp = &dsp_conf[current_codec];
|
dsp = &dsp_conf[current_codec];
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,8 @@ enum {
|
||||||
DSP_SET_TRACK_GAIN,
|
DSP_SET_TRACK_GAIN,
|
||||||
DSP_SET_ALBUM_GAIN,
|
DSP_SET_ALBUM_GAIN,
|
||||||
DSP_SET_TRACK_PEAK,
|
DSP_SET_TRACK_PEAK,
|
||||||
DSP_SET_ALBUM_PEAK
|
DSP_SET_ALBUM_PEAK,
|
||||||
|
DSP_CROSSFEED
|
||||||
};
|
};
|
||||||
|
|
||||||
long dsp_process(char *dest, char *src[], long size);
|
long dsp_process(char *dest, char *src[], long size);
|
||||||
|
|
@ -53,5 +54,6 @@ long dsp_output_size(long size);
|
||||||
int dsp_stereo_mode(void);
|
int dsp_stereo_mode(void);
|
||||||
bool dsp_configure(int setting, void *value);
|
bool dsp_configure(int setting, void *value);
|
||||||
void dsp_set_replaygain(bool always);
|
void dsp_set_replaygain(bool always);
|
||||||
|
void dsp_set_crossfeed(bool enable);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -3388,3 +3388,15 @@ desc: error message when preset list is empty
|
||||||
eng: "Screen frozen!"
|
eng: "Screen frozen!"
|
||||||
voice: ""
|
voice: ""
|
||||||
new:
|
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"
|
#include "dsp.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define CONFIG_BLOCK_VERSION 30
|
#define CONFIG_BLOCK_VERSION 31
|
||||||
#define CONFIG_BLOCK_SIZE 512
|
#define CONFIG_BLOCK_SIZE 512
|
||||||
#define RTC_BLOCK_SIZE 44
|
#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_in_duration), 0, "crossfade fade in duration", NULL},
|
||||||
{4, S_O(crossfade_fade_out_duration), 0, "crossfade fade out 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(crossfade_fade_out_mixmode), 0, "crossfade fade out mode", "crossfade,mix"},
|
||||||
|
{1, S_O(crossfeed), false, "crossfeed", off_on },
|
||||||
#endif
|
#endif
|
||||||
#ifdef HAVE_DIRCACHE
|
#ifdef HAVE_DIRCACHE
|
||||||
{1, S_O(dircache), false, "dircache", off_on },
|
{1, S_O(dircache), false, "dircache", off_on },
|
||||||
|
|
@ -912,6 +913,7 @@ void settings_apply(void)
|
||||||
#if CONFIG_CODEC == SWCODEC
|
#if CONFIG_CODEC == SWCODEC
|
||||||
audio_set_crossfade(global_settings.crossfade);
|
audio_set_crossfade(global_settings.crossfade);
|
||||||
dsp_set_replaygain(true);
|
dsp_set_replaygain(true);
|
||||||
|
dsp_set_crossfeed(global_settings.crossfeed);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef HAVE_SPDIF_POWER
|
#ifdef HAVE_SPDIF_POWER
|
||||||
|
|
|
||||||
|
|
@ -353,6 +353,7 @@ struct user_settings
|
||||||
shuffle is on, album gain otherwise */
|
shuffle is on, album gain otherwise */
|
||||||
int replaygain_preamp; /* scale replaygained tracks by this */
|
int replaygain_preamp; /* scale replaygained tracks by this */
|
||||||
int beep; /* system beep volume when changing tracks etc. */
|
int beep; /* system beep volume when changing tracks etc. */
|
||||||
|
bool crossfeed; /* enable crossfeed */
|
||||||
#endif
|
#endif
|
||||||
#ifdef HAVE_DIRCACHE
|
#ifdef HAVE_DIRCACHE
|
||||||
bool dircache; /* enable directory cache */
|
bool dircache; /* enable directory cache */
|
||||||
|
|
|
||||||
|
|
@ -1203,6 +1203,15 @@ static bool replaygain_settings_menu(void)
|
||||||
return result;
|
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 bool crossfade(void)
|
||||||
{
|
{
|
||||||
static const struct opt_items names[] = {
|
static const struct opt_items names[] = {
|
||||||
|
|
@ -1354,6 +1363,7 @@ static bool playback_settings_menu(void)
|
||||||
#if CONFIG_CODEC == SWCODEC
|
#if CONFIG_CODEC == SWCODEC
|
||||||
{ ID2P(LANG_CROSSFADE), crossfade_settings_menu },
|
{ ID2P(LANG_CROSSFADE), crossfade_settings_menu },
|
||||||
{ ID2P(LANG_REPLAYGAIN), replaygain_settings_menu },
|
{ ID2P(LANG_REPLAYGAIN), replaygain_settings_menu },
|
||||||
|
{ ID2P(LANG_CROSSFEED), crossfeed },
|
||||||
{ ID2P(LANG_BEEP), beep },
|
{ ID2P(LANG_BEEP), beep },
|
||||||
#endif
|
#endif
|
||||||
#ifdef HAVE_SPDIF_POWER
|
#ifdef HAVE_SPDIF_POWER
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue