summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThom Johansen <thomj@rockbox.org>2006-04-11 13:49:05 +0000
committerThom Johansen <thomj@rockbox.org>2006-04-11 13:49:05 +0000
commit8238b49c747afaf462ab5bdd45a37417eeae4dd9 (patch)
tree565d7ea84451eaa2eae3afbf6f9e481d95ff5d25
parent6bd1f143facb6b832bd22a22020cfc6ae4540601 (diff)
downloadrockbox-8238b49c747afaf462ab5bdd45a37417eeae4dd9.tar.gz
rockbox-8238b49c747afaf462ab5bdd45a37417eeae4dd9.zip
New crossfeed complete with no volume reducing bugs. Feedback on all the
new options is appreciated. Thanks to Dan Everton for the settings/GUI code. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@9609 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r--apps/dsp.c145
-rw-r--r--apps/dsp.h2
-rw-r--r--apps/dsp_cf.S102
-rw-r--r--apps/eq.c28
-rw-r--r--apps/eq.h1
-rw-r--r--apps/lang/english.lang66
-rw-r--r--apps/settings.c16
-rw-r--r--apps/settings.h8
-rw-r--r--apps/sound_menu.c80
-rw-r--r--firmware/system.c4
10 files changed, 298 insertions, 154 deletions
diff --git a/apps/dsp.c b/apps/dsp.c
index 29e103afb7..b6d24824b5 100644
--- a/apps/dsp.c
+++ b/apps/dsp.c
@@ -47,15 +47,6 @@
#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 -0x66666666L /* -0.2 (not unsigned!) */
-#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
@@ -209,10 +200,11 @@ struct dither_data
struct crossfeed_data
{
- int32_t lowpass[2];
- int32_t highpass[2];
- int32_t delay[2][13];
- int index;
+ int32_t gain; /* Direct path gain */
+ int32_t coefs[3]; /* Coefficients for the shelving filter */
+ int32_t history[4]; /* Format is x[n - 1], y[n - 1] for both channels */
+ int32_t delay[13][2];
+ int index; /* Current index into the delay line */
};
/* Current setup is one lowshelf filters, three peaking filters and one
@@ -522,71 +514,71 @@ static long dither_sample(int32_t sample, int32_t bias, int32_t mask,
return output;
}
+void dsp_set_crossfeed(bool enable)
+{
+ dsp->crossfeed_enabled = enable;
+}
+
+void dsp_set_crossfeed_direct_gain(int gain)
+{
+ /* Work around bug in get_replaygain_int which returns 0 for 0 dB */
+ if (gain == 0)
+ crossfeed_data.gain = 0x7fffffff;
+ else
+ crossfeed_data.gain = get_replaygain_int(gain * -10) << 7;
+}
+
+void dsp_set_crossfeed_cross_params(long lf_gain, long hf_gain, long cutoff)
+{
+ long g1 = get_replaygain_int(lf_gain * -10) << 3;
+ long g2 = get_replaygain_int(hf_gain * -10) << 3;
+
+ filter_bishelf_coefs(0xffffffff/NATIVE_FREQUENCY*cutoff, g1, g2,
+ crossfeed_data.coefs);
+}
+
/* Applies crossfeed to the stereo signal in src.
* Crossfeed is a process where listening over speakers is simulated. This
* is good for old hard panned stereo records, which might be quite fatiguing
* to listen to on headphones with no crossfeed.
*/
#ifndef DSP_HAVE_ASM_CROSSFEED
-static void apply_crossfeed(int32_t* src[], int count)
+void apply_crossfeed(int32_t* src[], int count)
{
- int32_t a; /* accumulator */
-
- int32_t low_left = crossfeed_data.lowpass[0];
- int32_t low_right = crossfeed_data.lowpass[1];
- int32_t high_left = crossfeed_data.highpass[0];
- int32_t high_right = crossfeed_data.highpass[1];
- unsigned int index = crossfeed_data.index;
-
+ int32_t *hist_l = &crossfeed_data.history[0];
+ int32_t *hist_r = &crossfeed_data.history[2];
+ int32_t *delay = &crossfeed_data.delay[0][0];
+ int32_t *coefs = &crossfeed_data.coefs[0];
+ int32_t gain = crossfeed_data.gain;
+ int di = crossfeed_data.index;
+
+ int32_t acc;
int32_t left, right;
-
- int32_t* delay_l = crossfeed_data.delay[0];
- int32_t* delay_r = crossfeed_data.delay[1];
-
int i;
-
- for (i = 0; i < count; i++)
- {
- /* use a low-pass filter on the signal */
+
+ for (i = 0; i < count; i++) {
left = src[0][i];
right = src[1][i];
-
- ACC_INIT(a, LOW, low_left); ACC(a, LOW_COMP, left);
- low_left = GET_ACC(a);
-
- ACC_INIT(a, LOW, low_right); ACC(a, LOW_COMP, right);
- low_right = GET_ACC(a);
-
- /* use a high-pass filter on the signal */
-
- ACC_INIT(a, HIGH_NEG, high_left); ACC(a, HIGH_COMP, left);
- high_left = GET_ACC(a);
-
- ACC_INIT(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_INIT(a, ATT, delay_r[index]); ACC(a, ATT_COMP, high_left);
- src[0][i] = GET_ACC(a);
-
- ACC_INIT(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;
+
+ ACC_INIT(acc, delay[di*2], coefs[0]);
+ ACC(acc, hist_l[0], coefs[1]);
+ ACC(acc, hist_l[1], coefs[2]);
+ hist_l[1] = GET_ACC(acc) << 0;
+ hist_l[0] = delay[di*2];
+ ACC_INIT(acc, delay[di*2 + 1], coefs[0]);
+ ACC(acc, hist_r[0], coefs[1]);
+ ACC(acc, hist_r[1], coefs[2]);
+ hist_r[1] = GET_ACC(acc) << 0;
+ hist_r[0] = delay[di*2 + 1];
+ delay[di*2] = left;
+ delay[di*2 + 1] = right;
+ src[0][i] = FRACMUL(left, gain) + hist_r[1];
+ src[1][i] = FRACMUL(right, gain) + hist_l[1];
+
+ if (++di > 12)
+ di = 0;
}
-
- 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;
+ crossfeed_data.index = di;
}
#endif
@@ -633,13 +625,8 @@ void dsp_set_eq_coefs(int band)
if (q == 0)
q = 1;
- /* The coef functions assume the EMAC unit is in fractional mode */
- #if defined(CPU_COLDFIRE) && !defined(SIMULATOR)
- /* set emac unit for dsp processing, and save old macsr, we're running in
- codec thread context at this point, so can't clobber it */
- unsigned long old_macsr = coldfire_get_macsr();
- coldfire_set_macsr(EMAC_FRACTIONAL | EMAC_SATURATE | EMAC_ROUND);
- #endif
+ /* NOTE: The coef functions assume the EMAC unit is in fractional mode,
+ which it should be, since we're executed from the main thread. */
/* Assume a band is disabled if the gain is zero */
if (gain == 0) {
@@ -654,11 +641,6 @@ void dsp_set_eq_coefs(int band)
eq_data.enabled[band] = 1;
}
-
- #if defined(CPU_COLDFIRE) && !defined(SIMULATOR)
- /* set old macsr again */
- coldfire_set_macsr(old_macsr);
- #endif
}
/* Apply EQ filters to those bands that have got it switched on. */
@@ -1068,13 +1050,6 @@ 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];
diff --git a/apps/dsp.h b/apps/dsp.h
index 368326d7f2..501e238a54 100644
--- a/apps/dsp.h
+++ b/apps/dsp.h
@@ -54,6 +54,8 @@ int dsp_stereo_mode(void);
bool dsp_configure(int setting, void *value);
void dsp_set_replaygain(bool always);
void dsp_set_crossfeed(bool enable);
+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_eq(bool enable);
void dsp_set_eq_precut(int precut);
void dsp_set_eq_coefs(int band);
diff --git a/apps/dsp_cf.S b/apps/dsp_cf.S
index 6147ebeea7..719d1db1d5 100644
--- a/apps/dsp_cf.S
+++ b/apps/dsp_cf.S
@@ -17,15 +17,6 @@
*
****************************************************************************/
- .section .idata,"aw",@progbits
-crossfeed_coefs:
- .long 0x4CCCCCCD | LOW
- .long 0x33333333 | LOW_COMP
- .long -0x66666666 | HIGH_NEG
- .long 0x66666666 | HIGH_COMP
- .long 0x0CCCCCCD | ATT
- .long 0x73333333 | ATT_COMP
-
.section .text
.global apply_crossfeed
apply_crossfeed:
@@ -36,68 +27,57 @@ apply_crossfeed:
move.l (44+8, %sp), %d7 | d7 = count
lea.l crossfeed_data, %a1
- lea.l crossfeed_coefs, %a6
- lea.l (16, %a1), %a0 | a0 = &delay[0][0]
- movem.l (%a1), %d0-%d3 | fetch filter history samples
- move.l (120, %a1), %d4 | fetch delay line index
- move.l (%a4), %d5 | d5 = left sample
- move.l (%a5), %d6 | d6 = right sample
- move.l (%a6)+, %a1 | a1 = LOW value
- move.l (%a6)+, %a2 | a2 = LOW_COMP value
+ lea.l (8*4, %a1), %a0 | a0 = &delay[0][0]
+ move.l (%a1)+, %a6 | a6 = direct gain
+ movem.l (3*4, %a1), %d0-%d3 | fetch filter history samples
+ move.l (33*4, %a1), %d4 | fetch delay line index
+ movem.l (%a1), %a1-%a3 | load filter coefs
+ move.l %d4, %d5
+ lsl.l #3, %d5
+ add.l %d5, %a0 | point a0 to current delay position
+| lea.l (%d4*4, %a0), %a0
+| lea.l (%d4*4, %a0), %a0 | point a0 to current delay position
/* Register usage in loop:
- * a0 = &delay[0][0], a1 & a2 = coefs, a3 = temp storage,
- * a4 = src[0], a5 = src[1], a6 = &crossfeed_coefs[0],
- * d0 = low_left, d1 = low_right,
- * d2 = high_left, d3 = high_right,
+ * a0 = &delay[index][0], a1..a3 = b0, b1, a1 (filter coefs),
+ * a4 = src[0], a5 = src[1], a6 = direct gain,
+ * d0..d3 = history
* d4 = delay line index,
- * d5 = src[0][i], d6 = src[1][i].
+ * d5,d6 = temp.
* d7 = count
*/
.cfloop:
- | LOW*low_left + LOW_COMP*left
- mac.l %a1, %d0, %acc0
- mac.l %a2, %d5, %acc0
- | LOW*low_right + LOW_COMP*right
- mac.l %a1, %d1, (%a6)+, %a1, %acc1 | a1 = HIGH_NEG
- mac.l %a2, %d6, (%a6)+, %a2, %acc1 | a2 = HIGH_COMP
- movclr.l %acc0, %d0 | get low_left
- movclr.l %acc1, %d1 | get low_right
- | HIGH_NEG*high_left + HIGH_COMP*left
- mac.l %a1, %d2, %acc0
- mac.l %a2, %d5, %acc0
- | HIGH_NEG*high_right + HIGH_COMP*right
- mac.l %a1, %d3, (%a6)+, %a1, %acc1 | a1 = ATT
- mac.l %a2, %d6, (%a6)+, %a2, %acc1 | a2 = ATT_COMP
- lea.l (-6*4, %a6), %a6 | coef = &coefs[0]
- move.l (%a0, %d4*4), %a3 | a3 = delay[0][idx]
- move.l (52, %a0, %d4*4), %d5 | d5 = delay[1][idx]
- movclr.l %acc0, %d2 | get high_left
- movclr.l %acc1, %d3 | get high_right
- | ATT*delay_r + ATT_COMP*high_left
- mac.l %a1, %d5, (4, %a4), %d5, %acc0 | d5 = src[0][i+1]
- mac.l %a2, %d2, (4, %a5), %d6, %acc0 | d6 = src[1][i+1]
- | ATT*delay_l + ATT_COMP*high_right
- mac.l %a1, %a3, (%a6)+, %a1, %acc1 | a1 = LOW
- mac.l %a2, %d3, (%a6)+, %a2, %acc1 | a2 = LOW_COMP
-
- | save crossfed samples to output
- movclr.l %acc0, %a3
- move.l %a3, (%a4)+ | src[0][i++] = out_l
- movclr.l %acc1, %a3
- move.l %a3, (%a5)+ | src[1][i++] = out_r
- move.l %d0, (%a0, %d4*4) | delay[0][index] = low_left
- move.l %d1, (52, %a0, %d4*4) | delay[1][index] = low_right */
- addq.l #1, %d4 | index++ */
- cmp.l #13, %d4 | if (index >= 13) {
+ mac.l %a2, %d0, (4, %a0), %d0, %acc0 | acc = b1*dr[n - 1] d0 = dr[n]
+ mac.l %a1, %d0, %acc0 | acc += b0*dr[n]
+ mac.l %a3, %d1, (%a4), %d5, %acc0 | acc += a1*y_l[n - 1], load left input
+ move.l %acc0, %d1 | get filtered delayed sample
+ mac.l %a6, %d5, %acc0 | acc += gain*x_l[n]
+ movclr.l %acc0, %d6
+ move.l %d6, (%a4)+ | write result
+
+ mac.l %a2, %d2, (%a0), %d2, %acc0 | acc = b1*dl[n - 1], d2 = dl[n]
+ move.l %d5, (%a0)+ | save left input to delay line
+ mac.l %a1, %d2, %acc0 | acc += b0*dl[n]
+ mac.l %a3, %d3, (%a5), %d5, %acc0 | acc += a1*y_r[n - 1], load right input
+ move.l %acc0, %d3 | get filtered delayed sample
+ mac.l %a6, %d5, %acc0 | acc += gain*x_r[n]
+ move.l %d5, (%a0)+ | save right input to delay line
+ movclr.l %acc0, %d6
+ move.l %d6, (%a5)+ | write result
+
+ addq.l #1, %d4 | index++
+ moveq.l #13, %d6
+ cmp.l %d6, %d4 | wrap index to 0 if it overflows
jlt .nowrap
- clr.l %d4 | index = 0
-.nowrap: | }
+ moveq.l #13*8, %d4
+ sub.l %d4, %a0 | wrap back delay line ptr as well
+ clr.l %d4
+.nowrap:
subq.l #1, %d7
jne .cfloop
| save data back to struct
- lea.l crossfeed_data, %a1
+ lea.l crossfeed_data + 4*4, %a1
movem.l %d0-%d3, (%a1)
- move.l %d4, (120, %a1)
+ move.l %d4, (30*4, %a1)
movem.l (%sp), %d2-%d7/%a2-%a6
lea.l (44, %sp), %sp
rts
diff --git a/apps/eq.c b/apps/eq.c
index 8fb065aa09..5011f32e5f 100644
--- a/apps/eq.c
+++ b/apps/eq.c
@@ -187,6 +187,34 @@ static long dbtoA(long db)
return (dbtoatab[pos] << 16) + frac*diff;
}
+/* Calculate first order shelving filter coefficients.
+ cutoff is a value from 0 to 0x80000000, where 0 represents 0 hz and
+ 0x80000000 represents nyquist (samplerate/2).
+ ad is gain at 0 hz, and an is gain at Nyquist frequency. Both are s3.27
+ format.
+ c is a pointer where the coefs will be stored. The coefs are s0.31 format.
+ Note that the filter is not compatible with the eq_filter routine.
+ */
+void filter_bishelf_coefs(unsigned long cutoff, long ad, long an, int32_t *c)
+{
+ const long one = 1 << 27;
+ long a0, a1;
+ long b0, b1;
+ long s, cs;
+ s = fsincos(cutoff, &cs) >> 4;
+ cs = one + (cs >> 4);
+
+ /* For max A = 4 (24 dB) */
+ b0 = (FRACMUL(an, cs) << 4) + (FRACMUL(ad, s) << 4);
+ b1 = (FRACMUL(ad, s) << 4) - (FRACMUL(an, cs) << 4);
+ a0 = s + cs;
+ a1 = s - cs;
+
+ c[0] = DIV64(b0, a0, 31);
+ c[1] = DIV64(b1, a0, 31);
+ c[2] = -DIV64(a1, a0, 31);
+}
+
/* Calculate second order section peaking filter coefficients.
cutoff is a value from 0 to 0x80000000, where 0 represents 0 hz and
0x80000000 represents nyquist (samplerate/2).
diff --git a/apps/eq.h b/apps/eq.h
index 5e86a45e84..340547339e 100644
--- a/apps/eq.h
+++ b/apps/eq.h
@@ -33,6 +33,7 @@ struct eqfilter {
int32_t history[2][4];
};
+void filter_bishelf_coefs(unsigned long cutoff, long ad, long an, int32_t *c);
void eq_pk_coefs(unsigned long cutoff, unsigned long Q, long db, int32_t *c);
void eq_ls_coefs(unsigned long cutoff, unsigned long Q, long db, int32_t *c);
void eq_hs_coefs(unsigned long cutoff, unsigned long Q, long db, int32_t *c);
diff --git a/apps/lang/english.lang b/apps/lang/english.lang
index 0a70910457..a388884d69 100644
--- a/apps/lang/english.lang
+++ b/apps/lang/english.lang
@@ -8408,3 +8408,69 @@
*: "pixels"
</voice>
</phrase>
+<phrase>
+ id: LANG_CROSSFEED_DIRECT_GAIN
+ desc: in crossfeed settings
+ user:
+ <source>
+ *: "Direct Gain"
+ </source>
+ <dest>
+ *: "Direct Gain"
+ </dest>
+ <voice>
+ *: "Direct gain"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_CROSSFEED_CROSS_GAIN
+ desc: in crossfeed settings
+ <source>
+ *: "Cross Gain"
+ </source>
+ <dest>
+ *: "Cross Gain"
+ </dest>
+ <voice>
+ *: "Cross gain"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_CROSSFEED_HF_ATTENUATION
+ desc: in crossfeed settings
+ <source>
+ *: "High-Frequency Attenuation"
+ </source>
+ <dest>
+ *: "High-Frequency Attenuation"
+ </dest>
+ <voice>
+ *: "High-frequency attenuation"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_CROSSFEED_HF_CUTOFF
+ desc: in crossfeed settings
+ <source>
+ *: "High-Frequency Cutoff"
+ </source>
+ <dest>
+ *: "High-Frequency Cutoff"
+ </dest>
+ <voice>
+ *: "High-frequency cutoff"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_UNIT_HERTZ
+ desc: in sound settings
+ <source>
+ *: "Hz"
+ </source>
+ <dest>
+ *: "Hz"
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
diff --git a/apps/settings.c b/apps/settings.c
index cbd39335f1..1316969726 100644
--- a/apps/settings.c
+++ b/apps/settings.c
@@ -94,7 +94,7 @@ const char rec_base_directory[] = REC_BASE_DIR;
#include "dsp.h"
#endif
-#define CONFIG_BLOCK_VERSION 39
+#define CONFIG_BLOCK_VERSION 40
#define CONFIG_BLOCK_SIZE 512
#define RTC_BLOCK_SIZE 44
@@ -488,6 +488,10 @@ static const struct bit_entry hd_bits[] =
{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 },
+ {6, S_O(crossfeed_direct_gain), 15, "crossfeed direct gain", NULL },
+ {7, S_O(crossfeed_cross_gain), 60, "crossfeed cross gain", NULL },
+ {8, S_O(crossfeed_hf_attenuation), 160, "crossfeed hf attenuation", NULL },
+ {11, S_O(crossfeed_hf_cutoff), 700, "crossfeed hf cutoff", NULL },
#endif
#ifdef HAVE_DIRCACHE
{1, S_O(dircache), false, "dircache", off_on },
@@ -538,6 +542,7 @@ static const struct bit_entry hd_bits[] =
"warn when erasing dynamic playlist", off_on },
#if CONFIG_CODEC == SWCODEC
{1, S_O(eq_enabled), false, "eq enabled", off_on },
+ {8, S_O(eq_precut), 0, "eq precut", NULL },
/* 0..32768 Hz */
{15, S_O(eq_band0_cutoff), 60, "eq band 0 cutoff", NULL },
{15, S_O(eq_band1_cutoff), 200, "eq band 1 cutoff", NULL },
@@ -579,10 +584,6 @@ static const struct bit_entry hd_bits[] =
{1, S_O(tagcache_ram), 0, "tagcache_ram", off_on },
#endif
-#if (CONFIG_CODEC == SWCODEC)
- {8, S_O(eq_precut), 0, "eq precut", NULL },
-#endif
-
/* If values are just added to the end, no need to bump the version. */
/* new stuff to be added at the end */
@@ -1159,6 +1160,11 @@ void settings_apply(void)
audio_set_crossfade(global_settings.crossfade);
dsp_set_replaygain(true);
dsp_set_crossfeed(global_settings.crossfeed);
+ dsp_set_crossfeed_direct_gain(global_settings.crossfeed_direct_gain);
+ dsp_set_crossfeed_cross_params(global_settings.crossfeed_cross_gain,
+ global_settings.crossfeed_cross_gain
+ + global_settings.crossfeed_hf_attenuation,
+ global_settings.crossfeed_hf_cutoff);
dsp_set_eq(global_settings.eq_enabled);
dsp_set_eq_precut(global_settings.eq_precut);
diff --git a/apps/settings.h b/apps/settings.h
index 1266fed1c4..8a657999e2 100644
--- a/apps/settings.h
+++ b/apps/settings.h
@@ -417,7 +417,13 @@ 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 */
+
+ /* Crossfeed settings */
+ bool crossfeed; /* enable crossfeed */
+ unsigned int crossfeed_direct_gain; /* - dB x 10 */
+ unsigned int crossfeed_cross_gain; /* - dB x 10 */
+ unsigned int crossfeed_hf_attenuation; /* - dB x 10 */
+ unsigned int crossfeed_hf_cutoff; /* Frequency in Hz */
#endif
#ifdef HAVE_DIRCACHE
bool dircache; /* enable directory cache */
diff --git a/apps/sound_menu.c b/apps/sound_menu.c
index 2e45f76621..1d389f30de 100644
--- a/apps/sound_menu.c
+++ b/apps/sound_menu.c
@@ -125,7 +125,14 @@ static bool treble(void)
#endif
#if CONFIG_CODEC == SWCODEC
-static bool crossfeed(void)
+static void crossfeed_format(char* buffer, int buffer_size, int value,
+ const char* unit)
+{
+ snprintf(buffer, buffer_size, "%s%d.%d %s", value == 0 ? " " : "-",
+ value / 10, value % 10, unit);
+}
+
+static bool crossfeed_enabled(void)
{
bool result = set_bool_options(str(LANG_CROSSFEED),
&global_settings.crossfeed,
@@ -134,6 +141,75 @@ static bool crossfeed(void)
NULL);
dsp_set_crossfeed(global_settings.crossfeed);
+
+ return result;
+}
+
+static bool crossfeed_direct_gain(void)
+{
+ return set_int(str(LANG_CROSSFEED_DIRECT_GAIN), str(LANG_UNIT_DB),
+ UNIT_DB, &global_settings.crossfeed_direct_gain,
+ &dsp_set_crossfeed_direct_gain, 5, 0, 60, crossfeed_format);
+}
+
+static void crossfeed_cross_gain_helper(int val)
+{
+ dsp_set_crossfeed_cross_params(val,
+ val + global_settings.crossfeed_hf_attenuation,
+ global_settings.crossfeed_hf_cutoff);
+}
+
+static bool crossfeed_cross_gain(void)
+{
+ return set_int(str(LANG_CROSSFEED_CROSS_GAIN), str(LANG_UNIT_DB),
+ UNIT_DB, &global_settings.crossfeed_cross_gain,
+ &crossfeed_cross_gain_helper, 5, 30, 120, crossfeed_format);
+}
+
+static void crossfeed_hf_att_helper(int val)
+{
+ dsp_set_crossfeed_cross_params(global_settings.crossfeed_cross_gain,
+ global_settings.crossfeed_cross_gain + val,
+ global_settings.crossfeed_hf_cutoff);
+}
+
+static bool crossfeed_hf_attenuation(void)
+{
+ return set_int(str(LANG_CROSSFEED_HF_ATTENUATION), str(LANG_UNIT_DB),
+ UNIT_DB, &global_settings.crossfeed_hf_attenuation,
+ &crossfeed_hf_att_helper, 5, 60, 240, crossfeed_format);
+}
+
+static void crossfeed_hf_cutoff_helper(int val)
+{
+ dsp_set_crossfeed_cross_params(global_settings.crossfeed_cross_gain,
+ global_settings.crossfeed_cross_gain + global_settings.crossfeed_hf_attenuation, val);
+}
+
+static bool crossfeed_hf_cutoff(void)
+{
+ return set_int(str(LANG_CROSSFEED_HF_CUTOFF), str(LANG_UNIT_HERTZ),
+ UNIT_HERTZ, &global_settings.crossfeed_hf_cutoff, &crossfeed_hf_cutoff_helper, 100, 500, 2000,
+ NULL);
+}
+
+static bool crossfeed_menu(void)
+{
+ int m;
+ bool result;
+ static const struct menu_item items[] = {
+ { ID2P(LANG_CROSSFEED), crossfeed_enabled },
+ { ID2P(LANG_CROSSFEED_DIRECT_GAIN), crossfeed_direct_gain },
+ { ID2P(LANG_CROSSFEED_CROSS_GAIN), crossfeed_cross_gain },
+ { ID2P(LANG_CROSSFEED_HF_ATTENUATION), crossfeed_hf_attenuation },
+ { ID2P(LANG_CROSSFEED_HF_CUTOFF), crossfeed_hf_cutoff },
+ };
+
+ m=menu_init(items, sizeof(items) / sizeof(*items), NULL,
+ NULL, NULL, NULL);
+ result = menu_run(m);
+ menu_exit(m);
+
return result;
}
#endif
@@ -414,7 +490,7 @@ bool sound_menu(void)
{ ID2P(LANG_CHANNEL_MENU), chanconf },
{ ID2P(LANG_STEREO_WIDTH), stereo_width },
#if CONFIG_CODEC == SWCODEC
- { ID2P(LANG_CROSSFEED), crossfeed },
+ { ID2P(LANG_CROSSFEED), crossfeed_menu },
{ ID2P(LANG_EQUALIZER), eq_menu },
#endif
#if (CONFIG_CODEC == MAS3587F) || (CONFIG_CODEC == MAS3539F)
diff --git a/firmware/system.c b/firmware/system.c
index 7eaefb1d77..8bdd821e60 100644
--- a/firmware/system.c
+++ b/firmware/system.c
@@ -516,6 +516,10 @@ void system_init(void)
"movclr.l %%acc2, %%d0\n\t"
"movclr.l %%acc3, %%d0\n\t"
: : : "d0");
+ /* Set EMAC unit to saturating and rounding fractional mode, since that's
+ what'll be the most useful for most things which the main thread
+ will do. */
+ coldfire_set_macsr(EMAC_FRACTIONAL | EMAC_SATURATE | EMAC_ROUND);
}
void system_reboot (void)