summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThom Johansen <thomj@rockbox.org>2007-03-07 15:06:33 +0000
committerThom Johansen <thomj@rockbox.org>2007-03-07 15:06:33 +0000
commit8e6e290a95863f1ef4c2d742c6d3983280154e01 (patch)
tree2608f15b03cac677b557cb1ad65ec534a43c47c3
parent2d7bb99c524c5da0d3360161f298503dcfbb01c7 (diff)
downloadrockbox-8e6e290a95863f1ef4c2d742c6d3983280154e01.tar.gz
rockbox-8e6e290a95863f1ef4c2d742c6d3983280154e01.zip
Factor first order shelving filter code out for easier reuse and replace the crossfeed filter with it. Crossfeed _should_ still sound the same, so please do tell if it doesn't.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@12674 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r--apps/dsp.c24
-rw-r--r--apps/eq.c100
-rw-r--r--apps/eq.h2
-rw-r--r--apps/settings.c3
-rw-r--r--apps/settings_list.c31
5 files changed, 78 insertions, 82 deletions
diff --git a/apps/dsp.c b/apps/dsp.c
index ca494e553f..9e410f879a 100644
--- a/apps/dsp.c
+++ b/apps/dsp.c
@@ -787,13 +787,27 @@ void dsp_set_crossfeed_direct_gain(int gain)
787 crossfeed_data.gain = 0x7fffffff; 787 crossfeed_data.gain = 0x7fffffff;
788} 788}
789 789
790/* Both gains should be below 0 dB (when inverted) */
790void dsp_set_crossfeed_cross_params(long lf_gain, long hf_gain, long cutoff) 791void dsp_set_crossfeed_cross_params(long lf_gain, long hf_gain, long cutoff)
791{ 792{
792 long g1 = get_replaygain_int(lf_gain * -10) << 3; 793 int32_t *c = crossfeed_data.coefs;
793 long g2 = get_replaygain_int(hf_gain * -10) << 3; 794 long scaler = get_replaygain_int(lf_gain * -10) << 7;
794 795
795 filter_shelf_coefs(0xffffffff/NATIVE_FREQUENCY*cutoff, g1, g2, 796 cutoff = 0xffffffff/NATIVE_FREQUENCY*cutoff;
796 crossfeed_data.coefs); 797 hf_gain -= lf_gain;
798 /* Divide cutoff by sqrt(10^(-hf_gain/20)) to place cutoff at the -3 dB
799 * point instead of shelf midpoint. This is for compatibility with the old
800 * crossfeed shelf filter and should be removed if crossfeed settings are
801 * ever made incompatible for any other good reason.
802 */
803 cutoff = DIV64(cutoff, get_replaygain_int(-hf_gain*5), 24);
804 filter_shelf_coefs(cutoff, -hf_gain, false, c);
805 /* Scale coefs by LF gain and shift them to s0.31 format. We have no gains
806 * over 1 and can do this safely
807 */
808 c[0] = FRACMUL_SHL(c[0], scaler, 4);
809 c[1] = FRACMUL_SHL(c[1], scaler, 4);
810 c[2] <<= 4;
797} 811}
798 812
799/* Combine all gains to a global gain. */ 813/* Combine all gains to a global gain. */
diff --git a/apps/eq.c b/apps/eq.c
index e06bd3e09e..b03a651812 100644
--- a/apps/eq.c
+++ b/apps/eq.c
@@ -118,33 +118,42 @@ static long fsincos(unsigned long phase, long *cos) {
118 return y; 118 return y;
119} 119}
120 120
121/** 121/**
122 * Calculate first order shelving filter coefficients. 122 * Calculate first order shelving filter. Filter is not directly usable by the
123 * Note that the filter is not compatible with the eq_filter routine. 123 * eq_filter() function.
124 * @param cutoff a value from 0 to 0x80000000, where 0 represents 0 Hz and 124 * @param cutoff shelf midpoint frequency. See eq_pk_coefs for format.
125 * 0x80000000 represents the Nyquist frequency (samplerate/2). 125 * @param A decibel value multiplied by ten, describing gain/attenuation of
126 * @param ad gain at 0 Hz. s3.27 fixed point. 126 * shelf. Max value is 24 dB.
127 * @param an gain at Nyquist frequency. s3.27 fixed point. 127 * @param low true for low-shelf filter, false for high-shelf filter.
128 * @param c pointer to coefficient storage. The coefs are s0.31 format. 128 * @param c pointer to coefficient storage. Coefficients are s4.27 format.
129 */ 129 */
130void filter_shelf_coefs(unsigned long cutoff, long ad, long an, int32_t *c) 130void filter_shelf_coefs(unsigned long cutoff, long A, bool low, int32_t *c)
131{ 131{
132 const long one = 1 << 27; 132 long sin, cos;
133 long a0, a1; 133 int32_t b0, b1, a0, a1; /* s3.28 */
134 long b0, b1; 134 const long g = get_replaygain_int(A*5) << 4; /* 10^(db/40), s3.28 */
135 long s, cs; 135
136 s = fsincos(cutoff, &cs) >> 4; 136 sin = fsincos(cutoff/2, &cos);
137 cs = one + (cs >> 4); 137 if (low) {
138 138 const int32_t sin_div_g = DIV64(sin, g, 25);
139 /* For max A = 4 (24 dB) */ 139 cos >>= 3;
140 b0 = FRACMUL_SHL(ad, s, 4) + FRACMUL_SHL(an, cs, 4); 140 b0 = FRACMUL(sin, g) + cos; /* 0.25 .. 4.10 */
141 b1 = FRACMUL_SHL(ad, s, 4) - FRACMUL_SHL(an, cs, 4); 141 b1 = FRACMUL(sin, g) - cos; /* -1 .. 3.98 */
142 a0 = s + cs; 142 a0 = sin_div_g + cos; /* 0.25 .. 4.10 */
143 a1 = s - cs; 143 a1 = sin_div_g - cos; /* -1 .. 3.98 */
144 } else {
145 const int32_t cos_div_g = DIV64(cos, g, 25);
146 sin >>= 3;
147 b0 = sin + FRACMUL(cos, g); /* 0.25 .. 4.10 */
148 b1 = sin - FRACMUL(cos, g); /* -3.98 .. 1 */
149 a0 = sin + cos_div_g; /* 0.25 .. 4.10 */
150 a1 = sin - cos_div_g; /* -3.98 .. 1 */
151 }
144 152
145 c[0] = DIV64(b0, a0, 31); 153 const int32_t rcp_a0 = DIV64(1, a0, 57); /* 0.24 .. 3.98, s2.29 */
146 c[1] = DIV64(b1, a0, 31); 154 *c++ = FRACMUL_SHL(b0, rcp_a0, 1); /* 0.063 .. 15.85 */
147 c[2] = -DIV64(a1, a0, 31); 155 *c++ = FRACMUL_SHL(b1, rcp_a0, 1); /* -15.85 .. 15.85 */
156 *c++ = -FRACMUL_SHL(a1, rcp_a0, 1); /* -1 .. 1 */
148} 157}
149 158
150#ifdef HAVE_SW_TONE_CONTROLS 159#ifdef HAVE_SW_TONE_CONTROLS
@@ -163,41 +172,26 @@ void filter_shelf_coefs(unsigned long cutoff, long ad, long an, int32_t *c)
163void filter_bishelf_coefs(unsigned long cutoff_low, unsigned long cutoff_high, 172void filter_bishelf_coefs(unsigned long cutoff_low, unsigned long cutoff_high,
164 long A_low, long A_high, long A, int32_t *c) 173 long A_low, long A_high, long A, int32_t *c)
165{ 174{
166 long sin1, cos2; /* s0.31 */
167 long cos1, sin2; /* s3.28 */
168 int32_t b0, b1, b2, b3; /* s3.28 */
169 int32_t a0, a1, a2, a3;
170 const long gd = get_replaygain_int(A_low*5) << 4; /* 10^(db/40), s3.28 */
171 const long gn = get_replaygain_int(A_high*5) << 4; /* 10^(db/40), s3.28 */
172 const long g = get_replaygain_int(A*10) << 7; /* 10^(db/20), s0.31 */ 175 const long g = get_replaygain_int(A*10) << 7; /* 10^(db/20), s0.31 */
176 int32_t c_ls[3], c_hs[3];
173 177
174 sin1 = fsincos(cutoff_low/2, &cos1); 178 filter_shelf_coefs(cutoff_low, A_low, true, c_ls);
175 sin2 = fsincos(cutoff_high/2, &cos2) >> 3; 179 filter_shelf_coefs(cutoff_high, A_high, false, c_hs);
176 cos1 >>= 3; 180 c_ls[0] = FRACMUL(g, c_ls[0]);
177 181 c_ls[1] = FRACMUL(g, c_ls[1]);
178 /* lowshelf filter, ranges listed are for all possible cutoffs */
179 b0 = FRACMUL(sin1, gd) + cos1; /* 0.25 .. 4.10 */
180 b1 = FRACMUL(sin1, gd) - cos1; /* -1 .. 3.98 */
181 a0 = DIV64(sin1, gd, 25) + cos1; /* 0.25 .. 4.10 */
182 a1 = DIV64(sin1, gd, 25) - cos1; /* -1 .. 3.98 */
183
184 /* highshelf filter */
185 b2 = sin2 + FRACMUL(cos2, gn); /* 0.25 .. 4.10 */
186 b3 = sin2 - FRACMUL(cos2, gn); /* -3.98 .. 1 */
187 a2 = sin2 + DIV64(cos2, gn, 25); /* 0.25 .. 4.10 */
188 a3 = sin2 - DIV64(cos2, gn, 25); /* -3.98 .. 1 */
189 182
190 /* now we cascade the two first order filters to one second order filter 183 /* now we cascade the two first order filters to one second order filter
191 * which can be used by eq_filter(). these resulting coefficients have a 184 * which can be used by eq_filter(). these resulting coefficients have a
192 * really wide numerical range, so we use a fixed point format which will 185 * really wide numerical range, so we use a fixed point format which will
193 * work for the selected cutoff frequencies (in dsp.c) only. 186 * work for the selected cutoff frequencies (in dsp.c) only.
194 */ 187 */
195 const int32_t rcp_a0 = DIV64(1, FRACMUL(a0, a2), 53); /* s3.28 */ 188 const int32_t b0 = c_ls[0], b1 = c_ls[1], b2 = c_hs[0], b3 = c_hs[1];
196 *c++ = FRACMUL(g, FRACMUL_SHL(FRACMUL(b0, b2), rcp_a0, 5)); 189 const int32_t a0 = c_ls[2], a1 = c_hs[2];
197 *c++ = FRACMUL(g, FRACMUL_SHL(FRACMUL(b0, b3) + FRACMUL(b1, b2), rcp_a0, 5)); 190 *c++ = FRACMUL_SHL(b0, b2, 4);
198 *c++ = FRACMUL(g, FRACMUL_SHL(FRACMUL(b1, b3), rcp_a0, 5)); 191 *c++ = FRACMUL_SHL(b0, b3, 4) + FRACMUL_SHL(b1, b2, 4);
199 *c++ = -FRACMUL_SHL(FRACMUL(a0, a3) + FRACMUL(a1, a2), rcp_a0, 5); 192 *c++ = FRACMUL_SHL(b1, b3, 4);
200 *c++ = -FRACMUL_SHL(FRACMUL(a1, a3), rcp_a0, 5); 193 *c++ = a0 + a1;
194 *c++ = -FRACMUL_SHL(a0, a1, 4);
201} 195}
202#endif 196#endif
203 197
@@ -276,7 +270,7 @@ void eq_ls_coefs(unsigned long cutoff, unsigned long Q, long db, int32_t *c)
276 a2 = ap1 + FRACMUL(am1, cs) - twosqrtalpha; 270 a2 = ap1 + FRACMUL(am1, cs) - twosqrtalpha;
277 271
278 /* [0.1 .. 1.99] */ 272 /* [0.1 .. 1.99] */
279 const long rcp_a0 = DIV64(1, a0, 55); /* s1.30 */ 273 const long rcp_a0 = DIV64(1, a0, 55); /* s1.30 */
280 *c++ = FRACMUL_SHL(b0, rcp_a0, 2); /* [0.06 .. 15.9] */ 274 *c++ = FRACMUL_SHL(b0, rcp_a0, 2); /* [0.06 .. 15.9] */
281 *c++ = FRACMUL_SHL(b1, rcp_a0, 2); /* [-2 .. 31.7] */ 275 *c++ = FRACMUL_SHL(b1, rcp_a0, 2); /* [-2 .. 31.7] */
282 *c++ = FRACMUL_SHL(b2, rcp_a0, 2); /* [0 .. 15.9] */ 276 *c++ = FRACMUL_SHL(b2, rcp_a0, 2); /* [0 .. 15.9] */
@@ -315,7 +309,7 @@ void eq_hs_coefs(unsigned long cutoff, unsigned long Q, long db, int32_t *c)
315 a2 = ap1 - FRACMUL(am1, cs) - twosqrtalpha; 309 a2 = ap1 - FRACMUL(am1, cs) - twosqrtalpha;
316 310
317 /* [0.1 .. 1.99] */ 311 /* [0.1 .. 1.99] */
318 const long rcp_a0 = DIV64(1, a0, 55); /* s1.30 */ 312 const long rcp_a0 = DIV64(1, a0, 55); /* s1.30 */
319 *c++ = FRACMUL_SHL(b0, rcp_a0, 2); /* [0 .. 16] */ 313 *c++ = FRACMUL_SHL(b0, rcp_a0, 2); /* [0 .. 16] */
320 *c++ = FRACMUL_SHL(b1, rcp_a0, 2); /* [-31.7 .. 2] */ 314 *c++ = FRACMUL_SHL(b1, rcp_a0, 2); /* [-31.7 .. 2] */
321 *c++ = FRACMUL_SHL(b2, rcp_a0, 2); /* [0 .. 16] */ 315 *c++ = FRACMUL_SHL(b2, rcp_a0, 2); /* [0 .. 16] */
diff --git a/apps/eq.h b/apps/eq.h
index 83d235959d..b895b44b97 100644
--- a/apps/eq.h
+++ b/apps/eq.h
@@ -34,7 +34,7 @@ struct eqfilter {
34 int32_t history[2][4]; 34 int32_t history[2][4];
35}; 35};
36 36
37void filter_shelf_coefs(unsigned long cutoff, long ad, long an, int32_t *c); 37void filter_shelf_coefs(unsigned long cutoff, long A, bool low, int32_t *c);
38void filter_bishelf_coefs(unsigned long cutoff_low, unsigned long cutoff_high, 38void filter_bishelf_coefs(unsigned long cutoff_low, unsigned long cutoff_high,
39 long A_low, long A_high, long A, int32_t *c); 39 long A_low, long A_high, long A, int32_t *c);
40void eq_pk_coefs(unsigned long cutoff, unsigned long Q, long db, int32_t *c); 40void eq_pk_coefs(unsigned long cutoff, unsigned long Q, long db, int32_t *c);
diff --git a/apps/settings.c b/apps/settings.c
index a420e46281..331229190a 100644
--- a/apps/settings.c
+++ b/apps/settings.c
@@ -829,8 +829,7 @@ void settings_apply(void)
829 dsp_set_crossfeed(global_settings.crossfeed); 829 dsp_set_crossfeed(global_settings.crossfeed);
830 dsp_set_crossfeed_direct_gain(global_settings.crossfeed_direct_gain); 830 dsp_set_crossfeed_direct_gain(global_settings.crossfeed_direct_gain);
831 dsp_set_crossfeed_cross_params(global_settings.crossfeed_cross_gain, 831 dsp_set_crossfeed_cross_params(global_settings.crossfeed_cross_gain,
832 global_settings.crossfeed_cross_gain 832 global_settings.crossfeed_hf_attenuation,
833 + global_settings.crossfeed_hf_attenuation,
834 global_settings.crossfeed_hf_cutoff); 833 global_settings.crossfeed_hf_cutoff);
835 834
836 dsp_set_eq(global_settings.eq_enabled); 835 dsp_set_eq(global_settings.eq_enabled);
diff --git a/apps/settings_list.c b/apps/settings_list.c
index ae811bad4e..6594db7aa3 100644
--- a/apps/settings_list.c
+++ b/apps/settings_list.c
@@ -262,23 +262,12 @@ static void crossfeed_format(char* buffer, int buffer_size, int value,
262 snprintf(buffer, buffer_size, "%s%d.%d %s", value == 0 ? " " : "-", 262 snprintf(buffer, buffer_size, "%s%d.%d %s", value == 0 ? " " : "-",
263 value / 10, value % 10, unit); 263 value / 10, value % 10, unit);
264} 264}
265static void crossfeed_cross_gain_helper(int val) 265static void crossfeed_cross_set(int val)
266{ 266{
267 dsp_set_crossfeed_cross_params(val, 267 (void)val;
268 val + global_settings.crossfeed_hf_attenuation, 268 dsp_set_crossfeed_cross_params(global_settings.crossfeed_cross_gain,
269 global_settings.crossfeed_hf_cutoff); 269 global_settings.crossfeed_hf_attenuation,
270} 270 global_settings.crossfeed_hf_cutoff);
271static void crossfeed_hf_att_helper(int val)
272{
273 dsp_set_crossfeed_cross_params(global_settings.crossfeed_cross_gain,
274 global_settings.crossfeed_cross_gain + val,
275 global_settings.crossfeed_hf_cutoff);
276}
277static void crossfeed_hf_cutoff_helper(int val)
278{
279 dsp_set_crossfeed_cross_params(global_settings.crossfeed_cross_gain,
280 global_settings.crossfeed_cross_gain
281 + global_settings.crossfeed_hf_attenuation, val);
282} 271}
283 272
284static void replaygain_preamp_format(char* buffer, int buffer_size, int value, 273static void replaygain_preamp_format(char* buffer, int buffer_size, int value,
@@ -828,7 +817,7 @@ const struct settings_list settings[] = {
828 INT_SETTING(0, crossfade_fade_out_duration, LANG_CROSSFADE_FADE_OUT_DURATION, 0, 817 INT_SETTING(0, crossfade_fade_out_duration, LANG_CROSSFADE_FADE_OUT_DURATION, 0,
829 "crossfade fade out duration", UNIT_SEC, 0, 15, 1, NULL, NULL, NULL), 818 "crossfade fade out duration", UNIT_SEC, 0, 15, 1, NULL, NULL, NULL),
830 CHOICE_SETTING(0, crossfade_fade_out_mixmode, LANG_CROSSFADE_FADE_OUT_MODE, 819 CHOICE_SETTING(0, crossfade_fade_out_mixmode, LANG_CROSSFADE_FADE_OUT_MODE,
831 0, "crossfade fade out mode", "crossfade,mix" ,NULL, 2, 820 0, "crossfade fade out mode", "crossfade,mix", NULL, 2,
832 ID2P(LANG_CROSSFADE), ID2P(LANG_MIX)), 821 ID2P(LANG_CROSSFADE), ID2P(LANG_MIX)),
833 822
834 /* crossfeed */ 823 /* crossfeed */
@@ -839,13 +828,13 @@ const struct settings_list settings[] = {
839 crossfeed_format, NULL, dsp_set_crossfeed_direct_gain), 828 crossfeed_format, NULL, dsp_set_crossfeed_direct_gain),
840 INT_SETTING(0, crossfeed_cross_gain, LANG_CROSSFEED_CROSS_GAIN, 60, 829 INT_SETTING(0, crossfeed_cross_gain, LANG_CROSSFEED_CROSS_GAIN, 60,
841 "crossfeed cross gain", UNIT_DB, 30, 120, 5, 830 "crossfeed cross gain", UNIT_DB, 30, 120, 5,
842 crossfeed_format, NULL, crossfeed_cross_gain_helper), 831 crossfeed_format, NULL, crossfeed_cross_set),
843 INT_SETTING(0, crossfeed_hf_attenuation, LANG_CROSSFEED_HF_ATTENUATION, 160, 832 INT_SETTING(0, crossfeed_hf_attenuation, LANG_CROSSFEED_HF_ATTENUATION, 160,
844 "crossfeed hf attenuation", UNIT_DB, 60, 240, 5, 833 "crossfeed hf attenuation", UNIT_DB, 60, 240, 5,
845 crossfeed_format, NULL, crossfeed_hf_att_helper), 834 crossfeed_format, NULL, crossfeed_cross_set),
846 INT_SETTING(0, crossfeed_hf_cutoff, LANG_CROSSFEED_HF_CUTOFF,700, 835 INT_SETTING(0, crossfeed_hf_cutoff, LANG_CROSSFEED_HF_CUTOFF, 700,
847 "crossfeed hf cutoff", UNIT_HERTZ, 500, 2000, 100, 836 "crossfeed hf cutoff", UNIT_HERTZ, 500, 2000, 100,
848 NULL, NULL, crossfeed_hf_cutoff_helper), 837 NULL, NULL, crossfeed_cross_set),
849 /* equalizer */ 838 /* equalizer */
850 OFFON_SETTING(0,eq_enabled,LANG_EQUALIZER_ENABLED,false,"eq enabled",NULL), 839 OFFON_SETTING(0,eq_enabled,LANG_EQUALIZER_ENABLED,false,"eq enabled",NULL),
851 INT_SETTING(0, eq_precut, LANG_EQUALIZER_PRECUT, 0, "eq precut", 840 INT_SETTING(0, eq_precut, LANG_EQUALIZER_PRECUT, 0, "eq precut",