summaryrefslogtreecommitdiff
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 @@
47#define RESAMPLE_BUF_SIZE (256 * 4) /* Enough for 11,025 Hz -> 44,100 Hz*/ 47#define RESAMPLE_BUF_SIZE (256 * 4) /* Enough for 11,025 Hz -> 44,100 Hz*/
48#define DEFAULT_REPLAYGAIN 0x01000000 48#define DEFAULT_REPLAYGAIN 0x01000000
49 49
50/* These are the constants for the filters in the crossfeed */
51
52#define ATT 0x0CCCCCCDL /* 0.1 */
53#define ATT_COMP 0x73333333L /* 0.9 */
54#define LOW 0x4CCCCCCDL /* 0.6 */
55#define LOW_COMP 0x33333333L /* 0.4 */
56#define HIGH_NEG -0x66666666L /* -0.2 (not unsigned!) */
57#define HIGH_COMP 0x66666666L /* 0.8 */
58
59#if defined(CPU_COLDFIRE) && !defined(SIMULATOR) 50#if defined(CPU_COLDFIRE) && !defined(SIMULATOR)
60 51
61/* Multiply two S.31 fractional integers and return the sign bit and the 52/* Multiply two S.31 fractional integers and return the sign bit and the
@@ -209,10 +200,11 @@ struct dither_data
209 200
210struct crossfeed_data 201struct crossfeed_data
211{ 202{
212 int32_t lowpass[2]; 203 int32_t gain; /* Direct path gain */
213 int32_t highpass[2]; 204 int32_t coefs[3]; /* Coefficients for the shelving filter */
214 int32_t delay[2][13]; 205 int32_t history[4]; /* Format is x[n - 1], y[n - 1] for both channels */
215 int index; 206 int32_t delay[13][2];
207 int index; /* Current index into the delay line */
216}; 208};
217 209
218/* Current setup is one lowshelf filters, three peaking filters and one 210/* 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,
522 return output; 514 return output;
523} 515}
524 516
517void dsp_set_crossfeed(bool enable)
518{
519 dsp->crossfeed_enabled = enable;
520}
521
522void dsp_set_crossfeed_direct_gain(int gain)
523{
524 /* Work around bug in get_replaygain_int which returns 0 for 0 dB */
525 if (gain == 0)
526 crossfeed_data.gain = 0x7fffffff;
527 else
528 crossfeed_data.gain = get_replaygain_int(gain * -10) << 7;
529}
530
531void dsp_set_crossfeed_cross_params(long lf_gain, long hf_gain, long cutoff)
532{
533 long g1 = get_replaygain_int(lf_gain * -10) << 3;
534 long g2 = get_replaygain_int(hf_gain * -10) << 3;
535
536 filter_bishelf_coefs(0xffffffff/NATIVE_FREQUENCY*cutoff, g1, g2,
537 crossfeed_data.coefs);
538}
539
525/* Applies crossfeed to the stereo signal in src. 540/* Applies crossfeed to the stereo signal in src.
526 * Crossfeed is a process where listening over speakers is simulated. This 541 * Crossfeed is a process where listening over speakers is simulated. This
527 * is good for old hard panned stereo records, which might be quite fatiguing 542 * is good for old hard panned stereo records, which might be quite fatiguing
528 * to listen to on headphones with no crossfeed. 543 * to listen to on headphones with no crossfeed.
529 */ 544 */
530#ifndef DSP_HAVE_ASM_CROSSFEED 545#ifndef DSP_HAVE_ASM_CROSSFEED
531static void apply_crossfeed(int32_t* src[], int count) 546void apply_crossfeed(int32_t* src[], int count)
532{ 547{
533 int32_t a; /* accumulator */ 548 int32_t *hist_l = &crossfeed_data.history[0];
534 549 int32_t *hist_r = &crossfeed_data.history[2];
535 int32_t low_left = crossfeed_data.lowpass[0]; 550 int32_t *delay = &crossfeed_data.delay[0][0];
536 int32_t low_right = crossfeed_data.lowpass[1]; 551 int32_t *coefs = &crossfeed_data.coefs[0];
537 int32_t high_left = crossfeed_data.highpass[0]; 552 int32_t gain = crossfeed_data.gain;
538 int32_t high_right = crossfeed_data.highpass[1]; 553 int di = crossfeed_data.index;
539 unsigned int index = crossfeed_data.index; 554
540 555 int32_t acc;
541 int32_t left, right; 556 int32_t left, right;
542
543 int32_t* delay_l = crossfeed_data.delay[0];
544 int32_t* delay_r = crossfeed_data.delay[1];
545
546 int i; 557 int i;
547 558
548 for (i = 0; i < count; i++) 559 for (i = 0; i < count; i++) {
549 {
550 /* use a low-pass filter on the signal */
551 left = src[0][i]; 560 left = src[0][i];
552 right = src[1][i]; 561 right = src[1][i];
553 562
554 ACC_INIT(a, LOW, low_left); ACC(a, LOW_COMP, left); 563 ACC_INIT(acc, delay[di*2], coefs[0]);
555 low_left = GET_ACC(a); 564 ACC(acc, hist_l[0], coefs[1]);
556 565 ACC(acc, hist_l[1], coefs[2]);
557 ACC_INIT(a, LOW, low_right); ACC(a, LOW_COMP, right); 566 hist_l[1] = GET_ACC(acc) << 0;
558 low_right = GET_ACC(a); 567 hist_l[0] = delay[di*2];
559 568 ACC_INIT(acc, delay[di*2 + 1], coefs[0]);
560 /* use a high-pass filter on the signal */ 569 ACC(acc, hist_r[0], coefs[1]);
561 570 ACC(acc, hist_r[1], coefs[2]);
562 ACC_INIT(a, HIGH_NEG, high_left); ACC(a, HIGH_COMP, left); 571 hist_r[1] = GET_ACC(acc) << 0;
563 high_left = GET_ACC(a); 572 hist_r[0] = delay[di*2 + 1];
564 573 delay[di*2] = left;
565 ACC_INIT(a, HIGH_NEG, high_right); ACC(a, HIGH_COMP, right); 574 delay[di*2 + 1] = right;
566 high_right = GET_ACC(a); 575 src[0][i] = FRACMUL(left, gain) + hist_r[1];
567 576 src[1][i] = FRACMUL(right, gain) + hist_l[1];
568 /* New data is the high-passed signal + delayed and attenuated 577
569 * low-passed signal from the other channel */ 578 if (++di > 12)
570 579 di = 0;
571 ACC_INIT(a, ATT, delay_r[index]); ACC(a, ATT_COMP, high_left);
572 src[0][i] = GET_ACC(a);
573
574 ACC_INIT(a, ATT, delay_l[index]); ACC(a, ATT_COMP, high_right);
575 src[1][i] = GET_ACC(a);
576
577 /* Store the low-passed signal in the ringbuffer */
578
579 delay_l[index] = low_left;
580 delay_r[index] = low_right;
581
582 index = (index + 1) % 13;
583 } 580 }
584 581 crossfeed_data.index = di;
585 crossfeed_data.index = index;
586 crossfeed_data.lowpass[0] = low_left;
587 crossfeed_data.lowpass[1] = low_right;
588 crossfeed_data.highpass[0] = high_left;
589 crossfeed_data.highpass[1] = high_right;
590} 582}
591#endif 583#endif
592 584
@@ -633,13 +625,8 @@ void dsp_set_eq_coefs(int band)
633 if (q == 0) 625 if (q == 0)
634 q = 1; 626 q = 1;
635 627
636 /* The coef functions assume the EMAC unit is in fractional mode */ 628 /* NOTE: The coef functions assume the EMAC unit is in fractional mode,
637 #if defined(CPU_COLDFIRE) && !defined(SIMULATOR) 629 which it should be, since we're executed from the main thread. */
638 /* set emac unit for dsp processing, and save old macsr, we're running in
639 codec thread context at this point, so can't clobber it */
640 unsigned long old_macsr = coldfire_get_macsr();
641 coldfire_set_macsr(EMAC_FRACTIONAL | EMAC_SATURATE | EMAC_ROUND);
642 #endif
643 630
644 /* Assume a band is disabled if the gain is zero */ 631 /* Assume a band is disabled if the gain is zero */
645 if (gain == 0) { 632 if (gain == 0) {
@@ -654,11 +641,6 @@ void dsp_set_eq_coefs(int band)
654 641
655 eq_data.enabled[band] = 1; 642 eq_data.enabled[band] = 1;
656 } 643 }
657
658 #if defined(CPU_COLDFIRE) && !defined(SIMULATOR)
659 /* set old macsr again */
660 coldfire_set_macsr(old_macsr);
661 #endif
662} 644}
663 645
664/* Apply EQ filters to those bands that have got it switched on. */ 646/* Apply EQ filters to those bands that have got it switched on. */
@@ -1068,13 +1050,6 @@ bool dsp_configure(int setting, void *value)
1068 return 1; 1050 return 1;
1069} 1051}
1070 1052
1071void dsp_set_crossfeed(bool enable)
1072{
1073 if (enable)
1074 memset(&crossfeed_data, 0, sizeof(crossfeed_data));
1075 dsp->crossfeed_enabled = enable;
1076}
1077
1078void dsp_set_replaygain(bool always) 1053void dsp_set_replaygain(bool always)
1079{ 1054{
1080 dsp = &dsp_conf[current_codec]; 1055 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);
54bool dsp_configure(int setting, void *value); 54bool dsp_configure(int setting, void *value);
55void dsp_set_replaygain(bool always); 55void dsp_set_replaygain(bool always);
56void dsp_set_crossfeed(bool enable); 56void dsp_set_crossfeed(bool enable);
57void dsp_set_crossfeed_direct_gain(int gain);
58void dsp_set_crossfeed_cross_params(long lf_gain, long hf_gain, long cutoff);
57void dsp_set_eq(bool enable); 59void dsp_set_eq(bool enable);
58void dsp_set_eq_precut(int precut); 60void dsp_set_eq_precut(int precut);
59void dsp_set_eq_coefs(int band); 61void 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 @@
17 * 17 *
18 ****************************************************************************/ 18 ****************************************************************************/
19 19
20 .section .idata,"aw",@progbits
21crossfeed_coefs:
22 .long 0x4CCCCCCD | LOW
23 .long 0x33333333 | LOW_COMP
24 .long -0x66666666 | HIGH_NEG
25 .long 0x66666666 | HIGH_COMP
26 .long 0x0CCCCCCD | ATT
27 .long 0x73333333 | ATT_COMP
28
29 .section .text 20 .section .text
30 .global apply_crossfeed 21 .global apply_crossfeed
31apply_crossfeed: 22apply_crossfeed:
@@ -36,68 +27,57 @@ apply_crossfeed:
36 move.l (44+8, %sp), %d7 | d7 = count 27 move.l (44+8, %sp), %d7 | d7 = count
37 28
38 lea.l crossfeed_data, %a1 29 lea.l crossfeed_data, %a1
39 lea.l crossfeed_coefs, %a6 30 lea.l (8*4, %a1), %a0 | a0 = &delay[0][0]
40 lea.l (16, %a1), %a0 | a0 = &delay[0][0] 31 move.l (%a1)+, %a6 | a6 = direct gain
41 movem.l (%a1), %d0-%d3 | fetch filter history samples 32 movem.l (3*4, %a1), %d0-%d3 | fetch filter history samples
42 move.l (120, %a1), %d4 | fetch delay line index 33 move.l (33*4, %a1), %d4 | fetch delay line index
43 move.l (%a4), %d5 | d5 = left sample 34 movem.l (%a1), %a1-%a3 | load filter coefs
44 move.l (%a5), %d6 | d6 = right sample 35 move.l %d4, %d5
45 move.l (%a6)+, %a1 | a1 = LOW value 36 lsl.l #3, %d5
46 move.l (%a6)+, %a2 | a2 = LOW_COMP value 37 add.l %d5, %a0 | point a0 to current delay position
38| lea.l (%d4*4, %a0), %a0
39| lea.l (%d4*4, %a0), %a0 | point a0 to current delay position
47 /* Register usage in loop: 40 /* Register usage in loop:
48 * a0 = &delay[0][0], a1 & a2 = coefs, a3 = temp storage, 41 * a0 = &delay[index][0], a1..a3 = b0, b1, a1 (filter coefs),
49 * a4 = src[0], a5 = src[1], a6 = &crossfeed_coefs[0], 42 * a4 = src[0], a5 = src[1], a6 = direct gain,
50 * d0 = low_left, d1 = low_right, 43 * d0..d3 = history
51 * d2 = high_left, d3 = high_right,
52 * d4 = delay line index, 44 * d4 = delay line index,
53 * d5 = src[0][i], d6 = src[1][i]. 45 * d5,d6 = temp.
54 * d7 = count 46 * d7 = count
55 */ 47 */
56.cfloop: 48.cfloop:
57 | LOW*low_left + LOW_COMP*left 49 mac.l %a2, %d0, (4, %a0), %d0, %acc0 | acc = b1*dr[n - 1] d0 = dr[n]
58 mac.l %a1, %d0, %acc0 50 mac.l %a1, %d0, %acc0 | acc += b0*dr[n]
59 mac.l %a2, %d5, %acc0 51 mac.l %a3, %d1, (%a4), %d5, %acc0 | acc += a1*y_l[n - 1], load left input
60 | LOW*low_right + LOW_COMP*right 52 move.l %acc0, %d1 | get filtered delayed sample
61 mac.l %a1, %d1, (%a6)+, %a1, %acc1 | a1 = HIGH_NEG 53 mac.l %a6, %d5, %acc0 | acc += gain*x_l[n]
62 mac.l %a2, %d6, (%a6)+, %a2, %acc1 | a2 = HIGH_COMP 54 movclr.l %acc0, %d6
63 movclr.l %acc0, %d0 | get low_left 55 move.l %d6, (%a4)+ | write result
64 movclr.l %acc1, %d1 | get low_right 56
65 | HIGH_NEG*high_left + HIGH_COMP*left 57 mac.l %a2, %d2, (%a0), %d2, %acc0 | acc = b1*dl[n - 1], d2 = dl[n]
66 mac.l %a1, %d2, %acc0 58 move.l %d5, (%a0)+ | save left input to delay line
67 mac.l %a2, %d5, %acc0 59 mac.l %a1, %d2, %acc0 | acc += b0*dl[n]
68 | HIGH_NEG*high_right + HIGH_COMP*right 60 mac.l %a3, %d3, (%a5), %d5, %acc0 | acc += a1*y_r[n - 1], load right input
69 mac.l %a1, %d3, (%a6)+, %a1, %acc1 | a1 = ATT 61 move.l %acc0, %d3 | get filtered delayed sample
70 mac.l %a2, %d6, (%a6)+, %a2, %acc1 | a2 = ATT_COMP 62 mac.l %a6, %d5, %acc0 | acc += gain*x_r[n]
71 lea.l (-6*4, %a6), %a6 | coef = &coefs[0] 63 move.l %d5, (%a0)+ | save right input to delay line
72 move.l (%a0, %d4*4), %a3 | a3 = delay[0][idx] 64 movclr.l %acc0, %d6
73 move.l (52, %a0, %d4*4), %d5 | d5 = delay[1][idx] 65 move.l %d6, (%a5)+ | write result
74 movclr.l %acc0, %d2 | get high_left 66
75 movclr.l %acc1, %d3 | get high_right 67 addq.l #1, %d4 | index++
76 | ATT*delay_r + ATT_COMP*high_left 68 moveq.l #13, %d6
77 mac.l %a1, %d5, (4, %a4), %d5, %acc0 | d5 = src[0][i+1] 69 cmp.l %d6, %d4 | wrap index to 0 if it overflows
78 mac.l %a2, %d2, (4, %a5), %d6, %acc0 | d6 = src[1][i+1]
79 | ATT*delay_l + ATT_COMP*high_right
80 mac.l %a1, %a3, (%a6)+, %a1, %acc1 | a1 = LOW
81 mac.l %a2, %d3, (%a6)+, %a2, %acc1 | a2 = LOW_COMP
82
83 | save crossfed samples to output
84 movclr.l %acc0, %a3
85 move.l %a3, (%a4)+ | src[0][i++] = out_l
86 movclr.l %acc1, %a3
87 move.l %a3, (%a5)+ | src[1][i++] = out_r
88 move.l %d0, (%a0, %d4*4) | delay[0][index] = low_left
89 move.l %d1, (52, %a0, %d4*4) | delay[1][index] = low_right */
90 addq.l #1, %d4 | index++ */
91 cmp.l #13, %d4 | if (index >= 13) {
92 jlt .nowrap 70 jlt .nowrap
93 clr.l %d4 | index = 0 71 moveq.l #13*8, %d4
94.nowrap: | } 72 sub.l %d4, %a0 | wrap back delay line ptr as well
73 clr.l %d4
74.nowrap:
95 subq.l #1, %d7 75 subq.l #1, %d7
96 jne .cfloop 76 jne .cfloop
97 | save data back to struct 77 | save data back to struct
98 lea.l crossfeed_data, %a1 78 lea.l crossfeed_data + 4*4, %a1
99 movem.l %d0-%d3, (%a1) 79 movem.l %d0-%d3, (%a1)
100 move.l %d4, (120, %a1) 80 move.l %d4, (30*4, %a1)
101 movem.l (%sp), %d2-%d7/%a2-%a6 81 movem.l (%sp), %d2-%d7/%a2-%a6
102 lea.l (44, %sp), %sp 82 lea.l (44, %sp), %sp
103 rts 83 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)
187 return (dbtoatab[pos] << 16) + frac*diff; 187 return (dbtoatab[pos] << 16) + frac*diff;
188} 188}
189 189
190/* Calculate first order shelving filter coefficients.
191 cutoff is a value from 0 to 0x80000000, where 0 represents 0 hz and
192 0x80000000 represents nyquist (samplerate/2).
193 ad is gain at 0 hz, and an is gain at Nyquist frequency. Both are s3.27
194 format.
195 c is a pointer where the coefs will be stored. The coefs are s0.31 format.
196 Note that the filter is not compatible with the eq_filter routine.
197 */
198void filter_bishelf_coefs(unsigned long cutoff, long ad, long an, int32_t *c)
199{
200 const long one = 1 << 27;
201 long a0, a1;
202 long b0, b1;
203 long s, cs;
204 s = fsincos(cutoff, &cs) >> 4;
205 cs = one + (cs >> 4);
206
207 /* For max A = 4 (24 dB) */
208 b0 = (FRACMUL(an, cs) << 4) + (FRACMUL(ad, s) << 4);
209 b1 = (FRACMUL(ad, s) << 4) - (FRACMUL(an, cs) << 4);
210 a0 = s + cs;
211 a1 = s - cs;
212
213 c[0] = DIV64(b0, a0, 31);
214 c[1] = DIV64(b1, a0, 31);
215 c[2] = -DIV64(a1, a0, 31);
216}
217
190/* Calculate second order section peaking filter coefficients. 218/* Calculate second order section peaking filter coefficients.
191 cutoff is a value from 0 to 0x80000000, where 0 represents 0 hz and 219 cutoff is a value from 0 to 0x80000000, where 0 represents 0 hz and
192 0x80000000 represents nyquist (samplerate/2). 220 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 {
33 int32_t history[2][4]; 33 int32_t history[2][4];
34}; 34};
35 35
36void filter_bishelf_coefs(unsigned long cutoff, long ad, long an, int32_t *c);
36void eq_pk_coefs(unsigned long cutoff, unsigned long Q, long db, int32_t *c); 37void eq_pk_coefs(unsigned long cutoff, unsigned long Q, long db, int32_t *c);
37void eq_ls_coefs(unsigned long cutoff, unsigned long Q, long db, int32_t *c); 38void eq_ls_coefs(unsigned long cutoff, unsigned long Q, long db, int32_t *c);
38void eq_hs_coefs(unsigned long cutoff, unsigned long Q, long db, int32_t *c); 39void 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 @@
8408 *: "pixels" 8408 *: "pixels"
8409 </voice> 8409 </voice>
8410</phrase> 8410</phrase>
8411<phrase>
8412 id: LANG_CROSSFEED_DIRECT_GAIN
8413 desc: in crossfeed settings
8414 user:
8415 <source>
8416 *: "Direct Gain"
8417 </source>
8418 <dest>
8419 *: "Direct Gain"
8420 </dest>
8421 <voice>
8422 *: "Direct gain"
8423 </voice>
8424</phrase>
8425<phrase>
8426 id: LANG_CROSSFEED_CROSS_GAIN
8427 desc: in crossfeed settings
8428 <source>
8429 *: "Cross Gain"
8430 </source>
8431 <dest>
8432 *: "Cross Gain"
8433 </dest>
8434 <voice>
8435 *: "Cross gain"
8436 </voice>
8437</phrase>
8438<phrase>
8439 id: LANG_CROSSFEED_HF_ATTENUATION
8440 desc: in crossfeed settings
8441 <source>
8442 *: "High-Frequency Attenuation"
8443 </source>
8444 <dest>
8445 *: "High-Frequency Attenuation"
8446 </dest>
8447 <voice>
8448 *: "High-frequency attenuation"
8449 </voice>
8450</phrase>
8451<phrase>
8452 id: LANG_CROSSFEED_HF_CUTOFF
8453 desc: in crossfeed settings
8454 <source>
8455 *: "High-Frequency Cutoff"
8456 </source>
8457 <dest>
8458 *: "High-Frequency Cutoff"
8459 </dest>
8460 <voice>
8461 *: "High-frequency cutoff"
8462 </voice>
8463</phrase>
8464<phrase>
8465 id: LANG_UNIT_HERTZ
8466 desc: in sound settings
8467 <source>
8468 *: "Hz"
8469 </source>
8470 <dest>
8471 *: "Hz"
8472 </dest>
8473 <voice>
8474 *: ""
8475 </voice>
8476</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;
94#include "dsp.h" 94#include "dsp.h"
95#endif 95#endif
96 96
97#define CONFIG_BLOCK_VERSION 39 97#define CONFIG_BLOCK_VERSION 40
98#define CONFIG_BLOCK_SIZE 512 98#define CONFIG_BLOCK_SIZE 512
99#define RTC_BLOCK_SIZE 44 99#define RTC_BLOCK_SIZE 44
100 100
@@ -488,6 +488,10 @@ static const struct bit_entry hd_bits[] =
488 {4, S_O(crossfade_fade_out_duration), 0, "crossfade fade out duration", NULL}, 488 {4, S_O(crossfade_fade_out_duration), 0, "crossfade fade out duration", NULL},
489 {1, S_O(crossfade_fade_out_mixmode), 0, "crossfade fade out mode", "crossfade,mix"}, 489 {1, S_O(crossfade_fade_out_mixmode), 0, "crossfade fade out mode", "crossfade,mix"},
490 {1, S_O(crossfeed), false, "crossfeed", off_on }, 490 {1, S_O(crossfeed), false, "crossfeed", off_on },
491 {6, S_O(crossfeed_direct_gain), 15, "crossfeed direct gain", NULL },
492 {7, S_O(crossfeed_cross_gain), 60, "crossfeed cross gain", NULL },
493 {8, S_O(crossfeed_hf_attenuation), 160, "crossfeed hf attenuation", NULL },
494 {11, S_O(crossfeed_hf_cutoff), 700, "crossfeed hf cutoff", NULL },
491#endif 495#endif
492#ifdef HAVE_DIRCACHE 496#ifdef HAVE_DIRCACHE
493 {1, S_O(dircache), false, "dircache", off_on }, 497 {1, S_O(dircache), false, "dircache", off_on },
@@ -538,6 +542,7 @@ static const struct bit_entry hd_bits[] =
538 "warn when erasing dynamic playlist", off_on }, 542 "warn when erasing dynamic playlist", off_on },
539#if CONFIG_CODEC == SWCODEC 543#if CONFIG_CODEC == SWCODEC
540 {1, S_O(eq_enabled), false, "eq enabled", off_on }, 544 {1, S_O(eq_enabled), false, "eq enabled", off_on },
545 {8, S_O(eq_precut), 0, "eq precut", NULL },
541 /* 0..32768 Hz */ 546 /* 0..32768 Hz */
542 {15, S_O(eq_band0_cutoff), 60, "eq band 0 cutoff", NULL }, 547 {15, S_O(eq_band0_cutoff), 60, "eq band 0 cutoff", NULL },
543 {15, S_O(eq_band1_cutoff), 200, "eq band 1 cutoff", NULL }, 548 {15, S_O(eq_band1_cutoff), 200, "eq band 1 cutoff", NULL },
@@ -579,10 +584,6 @@ static const struct bit_entry hd_bits[] =
579 {1, S_O(tagcache_ram), 0, "tagcache_ram", off_on }, 584 {1, S_O(tagcache_ram), 0, "tagcache_ram", off_on },
580#endif 585#endif
581 586
582#if (CONFIG_CODEC == SWCODEC)
583 {8, S_O(eq_precut), 0, "eq precut", NULL },
584#endif
585
586 /* If values are just added to the end, no need to bump the version. */ 587 /* If values are just added to the end, no need to bump the version. */
587 /* new stuff to be added at the end */ 588 /* new stuff to be added at the end */
588 589
@@ -1159,6 +1160,11 @@ void settings_apply(void)
1159 audio_set_crossfade(global_settings.crossfade); 1160 audio_set_crossfade(global_settings.crossfade);
1160 dsp_set_replaygain(true); 1161 dsp_set_replaygain(true);
1161 dsp_set_crossfeed(global_settings.crossfeed); 1162 dsp_set_crossfeed(global_settings.crossfeed);
1163 dsp_set_crossfeed_direct_gain(global_settings.crossfeed_direct_gain);
1164 dsp_set_crossfeed_cross_params(global_settings.crossfeed_cross_gain,
1165 global_settings.crossfeed_cross_gain
1166 + global_settings.crossfeed_hf_attenuation,
1167 global_settings.crossfeed_hf_cutoff);
1162 1168
1163 dsp_set_eq(global_settings.eq_enabled); 1169 dsp_set_eq(global_settings.eq_enabled);
1164 dsp_set_eq_precut(global_settings.eq_precut); 1170 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
417 shuffle is on, album gain otherwise */ 417 shuffle is on, album gain otherwise */
418 int replaygain_preamp; /* scale replaygained tracks by this */ 418 int replaygain_preamp; /* scale replaygained tracks by this */
419 int beep; /* system beep volume when changing tracks etc. */ 419 int beep; /* system beep volume when changing tracks etc. */
420 bool crossfeed; /* enable crossfeed */ 420
421 /* Crossfeed settings */
422 bool crossfeed; /* enable crossfeed */
423 unsigned int crossfeed_direct_gain; /* - dB x 10 */
424 unsigned int crossfeed_cross_gain; /* - dB x 10 */
425 unsigned int crossfeed_hf_attenuation; /* - dB x 10 */
426 unsigned int crossfeed_hf_cutoff; /* Frequency in Hz */
421#endif 427#endif
422#ifdef HAVE_DIRCACHE 428#ifdef HAVE_DIRCACHE
423 bool dircache; /* enable directory cache */ 429 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)
125#endif 125#endif
126 126
127#if CONFIG_CODEC == SWCODEC 127#if CONFIG_CODEC == SWCODEC
128static bool crossfeed(void) 128static void crossfeed_format(char* buffer, int buffer_size, int value,
129 const char* unit)
130{
131 snprintf(buffer, buffer_size, "%s%d.%d %s", value == 0 ? " " : "-",
132 value / 10, value % 10, unit);
133}
134
135static bool crossfeed_enabled(void)
129{ 136{
130 bool result = set_bool_options(str(LANG_CROSSFEED), 137 bool result = set_bool_options(str(LANG_CROSSFEED),
131 &global_settings.crossfeed, 138 &global_settings.crossfeed,
@@ -134,6 +141,75 @@ static bool crossfeed(void)
134 NULL); 141 NULL);
135 142
136 dsp_set_crossfeed(global_settings.crossfeed); 143 dsp_set_crossfeed(global_settings.crossfeed);
144
145 return result;
146}
147
148static bool crossfeed_direct_gain(void)
149{
150 return set_int(str(LANG_CROSSFEED_DIRECT_GAIN), str(LANG_UNIT_DB),
151 UNIT_DB, &global_settings.crossfeed_direct_gain,
152 &dsp_set_crossfeed_direct_gain, 5, 0, 60, crossfeed_format);
153}
154
155static void crossfeed_cross_gain_helper(int val)
156{
157 dsp_set_crossfeed_cross_params(val,
158 val + global_settings.crossfeed_hf_attenuation,
159 global_settings.crossfeed_hf_cutoff);
160}
161
162static bool crossfeed_cross_gain(void)
163{
164 return set_int(str(LANG_CROSSFEED_CROSS_GAIN), str(LANG_UNIT_DB),
165 UNIT_DB, &global_settings.crossfeed_cross_gain,
166 &crossfeed_cross_gain_helper, 5, 30, 120, crossfeed_format);
167}
168
169static void crossfeed_hf_att_helper(int val)
170{
171 dsp_set_crossfeed_cross_params(global_settings.crossfeed_cross_gain,
172 global_settings.crossfeed_cross_gain + val,
173 global_settings.crossfeed_hf_cutoff);
174}
175
176static bool crossfeed_hf_attenuation(void)
177{
178 return set_int(str(LANG_CROSSFEED_HF_ATTENUATION), str(LANG_UNIT_DB),
179 UNIT_DB, &global_settings.crossfeed_hf_attenuation,
180 &crossfeed_hf_att_helper, 5, 60, 240, crossfeed_format);
181}
182
183static void crossfeed_hf_cutoff_helper(int val)
184{
185 dsp_set_crossfeed_cross_params(global_settings.crossfeed_cross_gain,
186 global_settings.crossfeed_cross_gain + global_settings.crossfeed_hf_attenuation, val);
187}
188
189static bool crossfeed_hf_cutoff(void)
190{
191 return set_int(str(LANG_CROSSFEED_HF_CUTOFF), str(LANG_UNIT_HERTZ),
192 UNIT_HERTZ, &global_settings.crossfeed_hf_cutoff, &crossfeed_hf_cutoff_helper, 100, 500, 2000,
193 NULL);
194}
195
196static bool crossfeed_menu(void)
197{
198 int m;
199 bool result;
200 static const struct menu_item items[] = {
201 { ID2P(LANG_CROSSFEED), crossfeed_enabled },
202 { ID2P(LANG_CROSSFEED_DIRECT_GAIN), crossfeed_direct_gain },
203 { ID2P(LANG_CROSSFEED_CROSS_GAIN), crossfeed_cross_gain },
204 { ID2P(LANG_CROSSFEED_HF_ATTENUATION), crossfeed_hf_attenuation },
205 { ID2P(LANG_CROSSFEED_HF_CUTOFF), crossfeed_hf_cutoff },
206 };
207
208 m=menu_init(items, sizeof(items) / sizeof(*items), NULL,
209 NULL, NULL, NULL);
210 result = menu_run(m);
211 menu_exit(m);
212
137 return result; 213 return result;
138} 214}
139#endif 215#endif
@@ -414,7 +490,7 @@ bool sound_menu(void)
414 { ID2P(LANG_CHANNEL_MENU), chanconf }, 490 { ID2P(LANG_CHANNEL_MENU), chanconf },
415 { ID2P(LANG_STEREO_WIDTH), stereo_width }, 491 { ID2P(LANG_STEREO_WIDTH), stereo_width },
416#if CONFIG_CODEC == SWCODEC 492#if CONFIG_CODEC == SWCODEC
417 { ID2P(LANG_CROSSFEED), crossfeed }, 493 { ID2P(LANG_CROSSFEED), crossfeed_menu },
418 { ID2P(LANG_EQUALIZER), eq_menu }, 494 { ID2P(LANG_EQUALIZER), eq_menu },
419#endif 495#endif
420#if (CONFIG_CODEC == MAS3587F) || (CONFIG_CODEC == MAS3539F) 496#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)
516 "movclr.l %%acc2, %%d0\n\t" 516 "movclr.l %%acc2, %%d0\n\t"
517 "movclr.l %%acc3, %%d0\n\t" 517 "movclr.l %%acc3, %%d0\n\t"
518 : : : "d0"); 518 : : : "d0");
519 /* Set EMAC unit to saturating and rounding fractional mode, since that's
520 what'll be the most useful for most things which the main thread
521 will do. */
522 coldfire_set_macsr(EMAC_FRACTIONAL | EMAC_SATURATE | EMAC_ROUND);
519} 523}
520 524
521void system_reboot (void) 525void system_reboot (void)