diff options
-rw-r--r-- | apps/dsp.c | 24 | ||||
-rw-r--r-- | apps/eq.c | 100 | ||||
-rw-r--r-- | apps/eq.h | 2 | ||||
-rw-r--r-- | apps/settings.c | 3 | ||||
-rw-r--r-- | apps/settings_list.c | 31 |
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) */ | ||
790 | void dsp_set_crossfeed_cross_params(long lf_gain, long hf_gain, long cutoff) | 791 | void 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. */ |
@@ -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 | */ |
130 | void filter_shelf_coefs(unsigned long cutoff, long ad, long an, int32_t *c) | 130 | void 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) | |||
163 | void filter_bishelf_coefs(unsigned long cutoff_low, unsigned long cutoff_high, | 172 | void 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] */ |
@@ -34,7 +34,7 @@ struct eqfilter { | |||
34 | int32_t history[2][4]; | 34 | int32_t history[2][4]; |
35 | }; | 35 | }; |
36 | 36 | ||
37 | void filter_shelf_coefs(unsigned long cutoff, long ad, long an, int32_t *c); | 37 | void filter_shelf_coefs(unsigned long cutoff, long A, bool low, int32_t *c); |
38 | void filter_bishelf_coefs(unsigned long cutoff_low, unsigned long cutoff_high, | 38 | void 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); |
40 | void eq_pk_coefs(unsigned long cutoff, unsigned long Q, long db, int32_t *c); | 40 | void 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 | } |
265 | static void crossfeed_cross_gain_helper(int val) | 265 | static 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); |
271 | static 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 | } | ||
277 | static 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 | ||
284 | static void replaygain_preamp_format(char* buffer, int buffer_size, int value, | 273 | static 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", |