From 8238b49c747afaf462ab5bdd45a37417eeae4dd9 Mon Sep 17 00:00:00 2001 From: Thom Johansen Date: Tue, 11 Apr 2006 13:49:05 +0000 Subject: 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 --- apps/dsp.c | 145 ++++++++++++++++++++----------------------------- apps/dsp.h | 2 + apps/dsp_cf.S | 102 ++++++++++++++-------------------- apps/eq.c | 28 ++++++++++ apps/eq.h | 1 + apps/lang/english.lang | 66 ++++++++++++++++++++++ apps/settings.c | 16 ++++-- apps/settings.h | 8 ++- apps/sound_menu.c | 80 ++++++++++++++++++++++++++- firmware/system.c | 4 ++ 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" + + id: LANG_CROSSFEED_DIRECT_GAIN + desc: in crossfeed settings + user: + + *: "Direct Gain" + + + *: "Direct Gain" + + + *: "Direct gain" + + + + id: LANG_CROSSFEED_CROSS_GAIN + desc: in crossfeed settings + + *: "Cross Gain" + + + *: "Cross Gain" + + + *: "Cross gain" + + + + id: LANG_CROSSFEED_HF_ATTENUATION + desc: in crossfeed settings + + *: "High-Frequency Attenuation" + + + *: "High-Frequency Attenuation" + + + *: "High-frequency attenuation" + + + + id: LANG_CROSSFEED_HF_CUTOFF + desc: in crossfeed settings + + *: "High-Frequency Cutoff" + + + *: "High-Frequency Cutoff" + + + *: "High-frequency cutoff" + + + + id: LANG_UNIT_HERTZ + desc: in sound settings + + *: "Hz" + + + *: "Hz" + + + *: "" + + 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) -- cgit v1.2.3