summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThom Johansen <thomj@rockbox.org>2007-02-26 00:41:26 +0000
committerThom Johansen <thomj@rockbox.org>2007-02-26 00:41:26 +0000
commita7fabf0741c91fb0a2c28b2d8357bcc4630300af (patch)
treee1528a67f63933aa23ebb7b5d809e1ce7e60b589
parent1915c1099431294ca9c43bc11fb1bfa41bbd83cc (diff)
downloadrockbox-a7fabf0741c91fb0a2c28b2d8357bcc4630300af.tar.gz
rockbox-a7fabf0741c91fb0a2c28b2d8357bcc4630300af.zip
Add software based bass/treble controls for targets which have no such functionality in hardware (currently only X5). They can also be used on any other SWCODEC target by adding #define HAVE_SW_TONE_CONTROLS in the relevant config-*.h file. Also remove some now unneeded zero checks when using get_replaygain_int(). Comments on sound quality are welcome as some parameters can still be fine-tuned.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@12489 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r--apps/dsp.c71
-rw-r--r--apps/dsp.h7
-rw-r--r--apps/eq.c60
-rw-r--r--apps/eq.h7
-rw-r--r--apps/menus/sound_menu.c10
-rw-r--r--apps/settings.c7
-rw-r--r--firmware/export/config-iaudiox5.h3
-rw-r--r--firmware/export/sound.h2
-rw-r--r--firmware/sound.c53
9 files changed, 175 insertions, 45 deletions
diff --git a/apps/dsp.c b/apps/dsp.c
index 0ffaaea8d8..f306069a87 100644
--- a/apps/dsp.c
+++ b/apps/dsp.c
@@ -116,9 +116,10 @@ struct crossfeed_data
116 /* 8ch */ 116 /* 8ch */
117}; 117};
118 118
119/* Current setup is one lowshelf filters, three peaking filters and one 119/* Current setup is one lowshelf filters three peaking filters and one
120 highshelf filter. Varying the number of shelving filters make no sense, 120 * highshelf filter. Varying the number of shelving filters make no sense,
121 but adding peaking filters is possible. */ 121 * but adding peaking filters is possible.
122 */
122struct eq_state 123struct eq_state
123{ 124{
124 char enabled[5]; /* 00h - Flags for active filters */ 125 char enabled[5]; /* 00h - Flags for active filters */
@@ -171,6 +172,13 @@ static long dither_bias IBSS_ATTR;
171struct crossfeed_data crossfeed_data IBSS_ATTR; /* A */ 172struct crossfeed_data crossfeed_data IBSS_ATTR; /* A */
172/* Equalizer */ 173/* Equalizer */
173static struct eq_state eq_data; /* A/V */ 174static struct eq_state eq_data; /* A/V */
175#ifdef HAVE_SW_TONE_CONTROLS
176static int prescale;
177static int bass;
178static int treble;
179/* Filter struct for software bass/treble controls */
180static struct eqfilter tone_filter;
181#endif
174 182
175/* Settings applicable to audio codec only */ 183/* Settings applicable to audio codec only */
176static int pitch_ratio = 1000; 184static int pitch_ratio = 1000;
@@ -704,11 +712,7 @@ void dsp_set_crossfeed(bool enable)
704 712
705void dsp_set_crossfeed_direct_gain(int gain) 713void dsp_set_crossfeed_direct_gain(int gain)
706{ 714{
707 /* Work around bug in get_replaygain_int which returns 0 for 0 dB */ 715 crossfeed_data.gain = get_replaygain_int(gain * -10) << 7;
708 if (gain == 0)
709 crossfeed_data.gain = 0x7fffffff;
710 else
711 crossfeed_data.gain = get_replaygain_int(gain * -10) << 7;
712} 716}
713 717
714void dsp_set_crossfeed_cross_params(long lf_gain, long hf_gain, long cutoff) 718void dsp_set_crossfeed_cross_params(long lf_gain, long hf_gain, long cutoff)
@@ -716,8 +720,8 @@ void dsp_set_crossfeed_cross_params(long lf_gain, long hf_gain, long cutoff)
716 long g1 = get_replaygain_int(lf_gain * -10) << 3; 720 long g1 = get_replaygain_int(lf_gain * -10) << 3;
717 long g2 = get_replaygain_int(hf_gain * -10) << 3; 721 long g2 = get_replaygain_int(hf_gain * -10) << 3;
718 722
719 filter_bishelf_coefs(0xffffffff/NATIVE_FREQUENCY*cutoff, g1, g2, 723 filter_shelf_coefs(0xffffffff/NATIVE_FREQUENCY*cutoff, g1, g2,
720 crossfeed_data.coefs); 724 crossfeed_data.coefs);
721} 725}
722 726
723/* Applies crossfeed to the stereo signal in src. 727/* Applies crossfeed to the stereo signal in src.
@@ -985,6 +989,36 @@ static void channels_process_sound_chan_mono(int count, int32_t *buf[])
985} 989}
986#endif /* DSP_HAVE_ASM_SOUND_CHAN_MONO */ 990#endif /* DSP_HAVE_ASM_SOUND_CHAN_MONO */
987 991
992#ifdef HAVE_SW_TONE_CONTROLS
993static void set_tone_controls(void)
994{
995 filter_bishelf_coefs(0xffffffff/NATIVE_FREQUENCY*200,
996 0xffffffff/NATIVE_FREQUENCY*3500,
997 bass, treble, -prescale, tone_filter.coefs);
998}
999
1000int dsp_callback(int msg, intptr_t param)
1001{
1002 switch (msg) {
1003 case DSP_CALLBACK_SET_PRESCALE:
1004 prescale = param;
1005 set_tone_controls();
1006 break;
1007 /* prescaler is always set after calling any of these, so we wait with
1008 * calculating coefs until the above case is hit.
1009 */
1010 case DSP_CALLBACK_SET_BASS:
1011 bass = param;
1012 break;
1013 case DSP_CALLBACK_SET_TREBLE:
1014 treble = param;
1015 default:
1016 break;
1017 }
1018 return 0;
1019}
1020#endif
1021
988#ifndef DSP_HAVE_ASM_SOUND_CHAN_CUSTOM 1022#ifndef DSP_HAVE_ASM_SOUND_CHAN_CUSTOM
989static void channels_process_sound_chan_custom(int count, int32_t *buf[]) 1023static void channels_process_sound_chan_custom(int count, int32_t *buf[])
990{ 1024{
@@ -1068,12 +1102,12 @@ int dsp_process(char *dst, const char *src[], int count)
1068 int written = 0; 1102 int written = 0;
1069 int samples; 1103 int samples;
1070 1104
1071 #if defined(CPU_COLDFIRE) && !defined(SIMULATOR) 1105#if defined(CPU_COLDFIRE) && !defined(SIMULATOR)
1072 /* set emac unit for dsp processing, and save old macsr, we're running in 1106 /* set emac unit for dsp processing, and save old macsr, we're running in
1073 codec thread context at this point, so can't clobber it */ 1107 codec thread context at this point, so can't clobber it */
1074 unsigned long old_macsr = coldfire_get_macsr(); 1108 unsigned long old_macsr = coldfire_get_macsr();
1075 coldfire_set_macsr(EMAC_FRACTIONAL | EMAC_SATURATE); 1109 coldfire_set_macsr(EMAC_FRACTIONAL | EMAC_SATURATE);
1076 #endif 1110#endif
1077 1111
1078 while (count > 0) 1112 while (count > 0)
1079 { 1113 {
@@ -1085,8 +1119,17 @@ int dsp_process(char *dst, const char *src[], int count)
1085 break; /* I'm pretty sure we're downsampling here */ 1119 break; /* I'm pretty sure we're downsampling here */
1086 if (dsp->apply_crossfeed) 1120 if (dsp->apply_crossfeed)
1087 dsp->apply_crossfeed(tmp, samples); 1121 dsp->apply_crossfeed(tmp, samples);
1122 /* TODO: EQ and tone controls need separate structs for audio and voice
1123 * DSP processing thanks to filter history. isn't really audible now, but
1124 * might be the day we start handling voice more delicately.
1125 */
1088 if (eq_enabled) 1126 if (eq_enabled)
1089 eq_process(samples, tmp); 1127 eq_process(samples, tmp);
1128#ifdef HAVE_SW_TONE_CONTROLS
1129 if ((bass | treble) != 0)
1130 eq_filter(tmp, &tone_filter, samples, dsp->data.num_channels,
1131 FILTER_BISHELF_SHIFT);
1132#endif
1090 if (dsp->channels_process) 1133 if (dsp->channels_process)
1091 dsp->channels_process(samples, tmp); 1134 dsp->channels_process(samples, tmp);
1092 dsp->output_samples(samples, &dsp->data, tmp, (int16_t *)dst); 1135 dsp->output_samples(samples, &dsp->data, tmp, (int16_t *)dst);
@@ -1095,10 +1138,10 @@ int dsp_process(char *dst, const char *src[], int count)
1095 yield(); 1138 yield();
1096 } 1139 }
1097 1140
1098 #if defined(CPU_COLDFIRE) && !defined(SIMULATOR) 1141#if defined(CPU_COLDFIRE) && !defined(SIMULATOR)
1099 /* set old macsr again */ 1142 /* set old macsr again */
1100 coldfire_set_macsr(old_macsr); 1143 coldfire_set_macsr(old_macsr);
1101 #endif 1144#endif
1102 return written; 1145 return written;
1103} 1146}
1104 1147
diff --git a/apps/dsp.h b/apps/dsp.h
index 63dc68cbb4..03118e8c31 100644
--- a/apps/dsp.h
+++ b/apps/dsp.h
@@ -51,6 +51,12 @@ enum
51 DSP_CROSSFEED 51 DSP_CROSSFEED
52}; 52};
53 53
54enum {
55 DSP_CALLBACK_SET_PRESCALE = 0,
56 DSP_CALLBACK_SET_BASS,
57 DSP_CALLBACK_SET_TREBLE
58};
59
54/* A bunch of fixed point assembler helper macros */ 60/* A bunch of fixed point assembler helper macros */
55#if defined(CPU_COLDFIRE) && !defined(SIMULATOR) 61#if defined(CPU_COLDFIRE) && !defined(SIMULATOR)
56/* These macros use the Coldfire EMAC extension and need the MACSR flags set 62/* These macros use the Coldfire EMAC extension and need the MACSR flags set
@@ -209,6 +215,7 @@ void dsp_set_eq_precut(int precut);
209void dsp_set_eq_coefs(int band); 215void dsp_set_eq_coefs(int band);
210void sound_set_pitch(int r); 216void sound_set_pitch(int r);
211int sound_get_pitch(void); 217int sound_get_pitch(void);
218int dsp_callback(int msg, intptr_t param);
212void channels_set(int value); 219void channels_set(int value);
213void stereo_width_set(int value); 220void stereo_width_set(int value);
214void dsp_dither_enable(bool enable); 221void dsp_dither_enable(bool enable);
diff --git a/apps/eq.c b/apps/eq.c
index 588c23f89f..1d74db790e 100644
--- a/apps/eq.c
+++ b/apps/eq.c
@@ -7,7 +7,7 @@
7 * \/ \/ \/ \/ \/ 7 * \/ \/ \/ \/ \/
8 * $Id$ 8 * $Id$
9 * 9 *
10 * Copyright (C) 2006 Thom Johansen 10 * Copyright (C) 2006-2007 Thom Johansen
11 * 11 *
12 * All files in this archive are subject to the GNU General Public License. 12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement. 13 * See the file COPYING in the source tree root for full license agreement.
@@ -127,7 +127,7 @@ static long fsincos(unsigned long phase, long *cos) {
127 * @param an gain at Nyquist frequency. s3.27 fixed point. 127 * @param an gain at Nyquist frequency. s3.27 fixed point.
128 * @param c pointer to coefficient storage. The coefs are s0.31 format. 128 * @param c pointer to coefficient storage. The coefs are s0.31 format.
129 */ 129 */
130void filter_bishelf_coefs(unsigned long cutoff, long ad, long an, int32_t *c) 130void filter_shelf_coefs(unsigned long cutoff, long ad, long an, int32_t *c)
131{ 131{
132 const long one = 1 << 27; 132 const long one = 1 << 27;
133 long a0, a1; 133 long a0, a1;
@@ -137,7 +137,7 @@ void filter_bishelf_coefs(unsigned long cutoff, long ad, long an, int32_t *c)
137 cs = one + (cs >> 4); 137 cs = one + (cs >> 4);
138 138
139 /* For max A = 4 (24 dB) */ 139 /* For max A = 4 (24 dB) */
140 b0 = FRACMUL_SHL(an, cs, 4) + FRACMUL_SHL(ad, s, 4); 140 b0 = FRACMUL_SHL(ad, s, 4) + FRACMUL_SHL(an, cs, 4);
141 b1 = FRACMUL_SHL(ad, s, 4) - FRACMUL_SHL(an, cs, 4); 141 b1 = FRACMUL_SHL(ad, s, 4) - FRACMUL_SHL(an, cs, 4);
142 a0 = s + cs; 142 a0 = s + cs;
143 a1 = s - cs; 143 a1 = s - cs;
@@ -147,6 +147,58 @@ void filter_bishelf_coefs(unsigned long cutoff, long ad, long an, int32_t *c)
147 c[2] = -DIV64(a1, a0, 31); 147 c[2] = -DIV64(a1, a0, 31);
148} 148}
149 149
150/**
151 * Calculate second order section filter consisting of one low-shelf and one
152 * high-shelf section.
153 * @param cutoff_low low-shelf midpoint frequency. See eq_pk_coefs for format.
154 * @param cutoff_high high-shelf midpoint frequency.
155 * @param A_low decibel value multiplied by ten, describing gain/attenuation of
156 * low-shelf part. Max value is 24 dB.
157 * @param A_high decibel value multiplied by ten, describing gain/attenuation of
158 * high-shelf part. Max value is 24 dB.
159 * @param A decibel value multiplied by ten, describing additional overall gain.
160 * @param c pointer to coefficient storage. Coefficients are s4.27 format.
161 */
162void filter_bishelf_coefs(unsigned long cutoff_low, unsigned long cutoff_high,
163 long A_low, long A_high, long A, int32_t *c)
164{
165 long sin1, cos2; /* s0.31 */
166 long cos1, sin2; /* s3.28 */
167 int32_t b0, b1, b2, b3; /* s3.28 */
168 int32_t a0, a1, a2, a3;
169 const long gd = get_replaygain_int(A_low*5) << 4; /* 10^(db/40), s3.28 */
170 const long gn = get_replaygain_int(A_high*5) << 4; /* 10^(db/40), s3.28 */
171 const long g = get_replaygain_int(A*10) << 7; /* 10^(db/20), s0.31 */
172
173 sin1 = fsincos(cutoff_low/2, &cos1);
174 sin2 = fsincos(cutoff_high/2, &cos2) >> 3;
175 cos1 >>= 3;
176
177 /* lowshelf filter, ranges listed are for all possible cutoffs */
178 b0 = FRACMUL(sin1, gd) + cos1; /* 0.25 .. 4.10 */
179 b1 = FRACMUL(sin1, gd) - cos1; /* -1 .. 3.98 */
180 a0 = DIV64(sin1, gd, 25) + cos1; /* 0.25 .. 4.10 */
181 a1 = DIV64(sin1, gd, 25) - cos1; /* -1 .. 3.98 */
182
183 /* highshelf filter */
184 b2 = sin2 + FRACMUL(cos2, gn); /* 0.25 .. 4.10 */
185 b3 = sin2 - FRACMUL(cos2, gn); /* -3.98 .. 1 */
186 a2 = sin2 + DIV64(cos2, gn, 25); /* 0.25 .. 4.10 */
187 a3 = sin2 - DIV64(cos2, gn, 25); /* -3.98 .. 1 */
188
189 /* now we cascade the two first order filters to one second order filter
190 * which can be used by eq_filter(). these resulting coefficients have a
191 * really wide numerical range, so we use a fixed point format which will
192 * work for the selected cutoff frequencies (in dsp.c) only.
193 */
194 const int32_t rcp_a0 = DIV64(1, FRACMUL(a0, a2), 53); /* s3.28 */
195 *c++ = FRACMUL(g, FRACMUL_SHL(FRACMUL(b0, b2), rcp_a0, 5));
196 *c++ = FRACMUL(g, FRACMUL_SHL(FRACMUL(b0, b3) + FRACMUL(b1, b2), rcp_a0, 5));
197 *c++ = FRACMUL(g, FRACMUL_SHL(FRACMUL(b1, b3), rcp_a0, 5));
198 *c++ = -FRACMUL_SHL(FRACMUL(a0, a3) + FRACMUL(a1, a2), rcp_a0, 5);
199 *c++ = -FRACMUL_SHL(FRACMUL(a1, a3), rcp_a0, 5);
200}
201
150/* Coef calculation taken from Audio-EQ-Cookbook.txt by Robert Bristow-Johnson. 202/* Coef calculation taken from Audio-EQ-Cookbook.txt by Robert Bristow-Johnson.
151 * Slightly faster calculation can be done by deriving forms which use tan() 203 * Slightly faster calculation can be done by deriving forms which use tan()
152 * instead of cos() and sin(), but the latter are far easier to use when doing 204 * instead of cos() and sin(), but the latter are far easier to use when doing
@@ -162,7 +214,7 @@ void filter_bishelf_coefs(unsigned long cutoff, long ad, long an, int32_t *c)
162 * @param Q Q factor value multiplied by ten. Lower bound is artificially set 214 * @param Q Q factor value multiplied by ten. Lower bound is artificially set
163 * at 0.5. 215 * at 0.5.
164 * @param db decibel value multiplied by ten, describing gain/attenuation at 216 * @param db decibel value multiplied by ten, describing gain/attenuation at
165 * peak freq. 217 * peak freq. Max value is 24 dB.
166 * @param c pointer to coefficient storage. Coefficients are s3.28 format. 218 * @param c pointer to coefficient storage. Coefficients are s3.28 format.
167 */ 219 */
168void eq_pk_coefs(unsigned long cutoff, unsigned long Q, long db, int32_t *c) 220void eq_pk_coefs(unsigned long cutoff, unsigned long Q, long db, int32_t *c)
diff --git a/apps/eq.h b/apps/eq.h
index 095c8e82f0..83d235959d 100644
--- a/apps/eq.h
+++ b/apps/eq.h
@@ -7,7 +7,7 @@
7 * \/ \/ \/ \/ \/ 7 * \/ \/ \/ \/ \/
8 * $Id$ 8 * $Id$
9 * 9 *
10 * Copyright (C) 2006 Thom Johansen 10 * Copyright (C) 2006-2007 Thom Johansen
11 * 11 *
12 * All files in this archive are subject to the GNU General Public License. 12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement. 13 * See the file COPYING in the source tree root for full license agreement.
@@ -25,6 +25,7 @@
25/* These depend on the fixed point formats used by the different filter types 25/* These depend on the fixed point formats used by the different filter types
26 and need to be changed when they change. 26 and need to be changed when they change.
27 */ 27 */
28#define FILTER_BISHELF_SHIFT 5
28#define EQ_PEAK_SHIFT 4 29#define EQ_PEAK_SHIFT 4
29#define EQ_SHELF_SHIFT 6 30#define EQ_SHELF_SHIFT 6
30 31
@@ -33,7 +34,9 @@ struct eqfilter {
33 int32_t history[2][4]; 34 int32_t history[2][4];
34}; 35};
35 36
36void filter_bishelf_coefs(unsigned long cutoff, long ad, long an, int32_t *c); 37void filter_shelf_coefs(unsigned long cutoff, long ad, long an, int32_t *c);
38void filter_bishelf_coefs(unsigned long cutoff_low, unsigned long cutoff_high,
39 long A_low, long A_high, long A, int32_t *c);
37void 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);
38void eq_ls_coefs(unsigned long cutoff, unsigned long Q, long db, int32_t *c); 41void eq_ls_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); 42void eq_hs_coefs(unsigned long cutoff, unsigned long Q, long db, int32_t *c);
diff --git a/apps/menus/sound_menu.c b/apps/menus/sound_menu.c
index 9dc9579b0e..287b3ec904 100644
--- a/apps/menus/sound_menu.c
+++ b/apps/menus/sound_menu.c
@@ -55,12 +55,8 @@ int soundmenu_callback(int action,const struct menu_item_ex *this_item)
55#endif 55#endif
56 56
57MENUITEM_SETTING(volume, &global_settings.volume, soundmenu_callback); 57MENUITEM_SETTING(volume, &global_settings.volume, soundmenu_callback);
58 58MENUITEM_SETTING(bass, &global_settings.bass, soundmenu_callback);
59#ifndef HAVE_TLV320 59MENUITEM_SETTING(treble, &global_settings.treble, soundmenu_callback);
60 MENUITEM_SETTING(bass, &global_settings.bass, soundmenu_callback);
61 MENUITEM_SETTING(treble, &global_settings.treble, soundmenu_callback);
62#endif
63
64MENUITEM_SETTING(balance, &global_settings.balance, soundmenu_callback); 60MENUITEM_SETTING(balance, &global_settings.balance, soundmenu_callback);
65MENUITEM_SETTING(channel_config, &global_settings.channel_config, soundmenu_callback); 61MENUITEM_SETTING(channel_config, &global_settings.channel_config, soundmenu_callback);
66MENUITEM_SETTING(stereo_width, &global_settings.stereo_width, soundmenu_callback); 62MENUITEM_SETTING(stereo_width, &global_settings.stereo_width, soundmenu_callback);
@@ -99,9 +95,7 @@ MENUITEM_SETTING(stereo_width, &global_settings.stereo_width, soundmenu_callback
99 95
100MAKE_MENU(sound_settings, ID2P(LANG_SOUND_SETTINGS), NULL, bitmap_icons_6x8[Icon_Audio], 96MAKE_MENU(sound_settings, ID2P(LANG_SOUND_SETTINGS), NULL, bitmap_icons_6x8[Icon_Audio],
101 &volume, 97 &volume,
102#ifndef HAVE_TLV320
103 &bass,&treble, 98 &bass,&treble,
104#endif
105 &balance,&channel_config,&stereo_width 99 &balance,&channel_config,&stereo_width
106#if CONFIG_CODEC == SWCODEC 100#if CONFIG_CODEC == SWCODEC
107 ,&crossfeed_menu, &equalizer_menu, &dithering_enabled 101 ,&crossfeed_menu, &equalizer_menu, &dithering_enabled
diff --git a/apps/settings.c b/apps/settings.c
index dd5e7c5ae3..cc5ab12d6f 100644
--- a/apps/settings.c
+++ b/apps/settings.c
@@ -642,6 +642,9 @@ void settings_apply_pm_range(void)
642 642
643void sound_settings_apply(void) 643void sound_settings_apply(void)
644{ 644{
645#ifdef HAVE_SW_TONE_CONTROLS
646 sound_set_dsp_callback(dsp_callback);
647#endif
645 sound_set(SOUND_BASS, global_settings.bass); 648 sound_set(SOUND_BASS, global_settings.bass);
646 sound_set(SOUND_TREBLE, global_settings.treble); 649 sound_set(SOUND_TREBLE, global_settings.treble);
647 sound_set(SOUND_BALANCE, global_settings.balance); 650 sound_set(SOUND_BALANCE, global_settings.balance);
@@ -967,7 +970,7 @@ bool set_sound(const unsigned char * string,
967 talkunit = UNIT_PERCENT; 970 talkunit = UNIT_PERCENT;
968 else if (*unit == 'H') 971 else if (*unit == 'H')
969 talkunit = UNIT_HERTZ; 972 talkunit = UNIT_HERTZ;
970 if(!numdec) 973 if (!numdec)
971#if CONFIG_CODEC == SWCODEC 974#if CONFIG_CODEC == SWCODEC
972 /* We need to hijack this one and send it off to apps/dsp.c instead of 975 /* We need to hijack this one and send it off to apps/dsp.c instead of
973 firmware/sound.c */ 976 firmware/sound.c */
@@ -975,7 +978,7 @@ bool set_sound(const unsigned char * string,
975 return set_int(string, unit, talkunit, variable, &stereo_width_set, 978 return set_int(string, unit, talkunit, variable, &stereo_width_set,
976 steps, min, max, NULL ); 979 steps, min, max, NULL );
977 else 980 else
978#endif 981#endif
979 return set_int(string, unit, talkunit, variable, sound_callback, 982 return set_int(string, unit, talkunit, variable, sound_callback,
980 steps, min, max, NULL ); 983 steps, min, max, NULL );
981 else 984 else
diff --git a/firmware/export/config-iaudiox5.h b/firmware/export/config-iaudiox5.h
index 6aeaab0431..ba9e95398f 100644
--- a/firmware/export/config-iaudiox5.h
+++ b/firmware/export/config-iaudiox5.h
@@ -84,6 +84,9 @@
84 84
85#define HAVE_TLV320 85#define HAVE_TLV320
86 86
87/* TLV320 has no tone controls, so we use the software ones */
88#define HAVE_SW_TONE_CONTROLS
89
87#ifndef SIMULATOR 90#ifndef SIMULATOR
88 91
89/* Define this if your LCD can set contrast */ 92/* Define this if your LCD can set contrast */
diff --git a/firmware/export/sound.h b/firmware/export/sound.h
index 2079a84f0f..192384031d 100644
--- a/firmware/export/sound.h
+++ b/firmware/export/sound.h
@@ -19,6 +19,7 @@
19#ifndef SOUND_H 19#ifndef SOUND_H
20#define SOUND_H 20#define SOUND_H
21 21
22#include <inttypes.h>
22#ifdef HAVE_UDA1380 23#ifdef HAVE_UDA1380
23#include "uda1380.h" 24#include "uda1380.h"
24#elif defined(HAVE_WM8975) || defined(HAVE_WM8751) 25#elif defined(HAVE_WM8975) || defined(HAVE_WM8751)
@@ -76,6 +77,7 @@ int sound_max(int setting);
76int sound_default(int setting); 77int sound_default(int setting);
77sound_set_type* sound_get_fn(int setting); 78sound_set_type* sound_get_fn(int setting);
78 79
80void sound_set_dsp_callback(int (*func)(int, intptr_t));
79void sound_set_volume(int value); 81void sound_set_volume(int value);
80void sound_set_balance(int value); 82void sound_set_balance(int value);
81void sound_set_bass(int value); 83void sound_set_bass(int value);
diff --git a/firmware/sound.c b/firmware/sound.c
index c3679d41f2..a2b4e96e81 100644
--- a/firmware/sound.c
+++ b/firmware/sound.c
@@ -87,6 +87,11 @@ static const struct sound_settings_info sound_settings_table[] = {
87 [SOUND_BASS] = {"dB", 0, 1, -15, 15, 7, sound_set_bass}, 87 [SOUND_BASS] = {"dB", 0, 1, -15, 15, 7, sound_set_bass},
88 [SOUND_TREBLE] = {"dB", 0, 1, -15, 15, 7, sound_set_treble}, 88 [SOUND_TREBLE] = {"dB", 0, 1, -15, 15, 7, sound_set_treble},
89#endif 89#endif
90/* Override any other potentially existing treble/bass controllers if wanted */
91#ifdef HAVE_SW_TONE_CONTROLS
92 [SOUND_BASS] = {"dB", 0, 1, -24, 24, 0, sound_set_bass},
93 [SOUND_TREBLE] = {"dB", 0, 1, -24, 24, 0, sound_set_treble},
94#endif
90 [SOUND_BALANCE] = {"%", 0, 1,-100, 100, 0, sound_set_balance}, 95 [SOUND_BALANCE] = {"%", 0, 1,-100, 100, 0, sound_set_balance},
91 [SOUND_CHANNELS] = {"", 0, 1, 0, 5, 0, sound_set_channels}, 96 [SOUND_CHANNELS] = {"", 0, 1, 0, 5, 0, sound_set_channels},
92 [SOUND_STEREO_WIDTH] = {"%", 0, 1, 0, 255, 100, sound_set_stereo_width}, 97 [SOUND_STEREO_WIDTH] = {"%", 0, 1, 0, 255, 100, sound_set_stereo_width},
@@ -166,6 +171,22 @@ sound_set_type* sound_get_fn(int setting)
166 return NULL; 171 return NULL;
167} 172}
168 173
174#ifdef HAVE_SW_TONE_CONTROLS
175/* Copied from dsp.h, nasty nasty, but we don't want to include dsp.h */
176enum {
177 DSP_CALLBACK_SET_PRESCALE = 0,
178 DSP_CALLBACK_SET_BASS,
179 DSP_CALLBACK_SET_TREBLE
180};
181
182static int (*dsp_callback)(int, intptr_t) = NULL;
183
184void sound_set_dsp_callback(int (*func)(int, intptr_t))
185{
186 dsp_callback = func;
187}
188#endif
189
169#ifndef SIMULATOR 190#ifndef SIMULATOR
170#if CONFIG_CODEC == MAS3507D /* volume/balance/treble/bass interdependency */ 191#if CONFIG_CODEC == MAS3507D /* volume/balance/treble/bass interdependency */
171#define VOLUME_MIN -780 192#define VOLUME_MIN -780
@@ -293,10 +314,9 @@ int current_bass = 0; /* -150..+150 0..+240 */
293 314
294static void set_prescaled_volume(void) 315static void set_prescaled_volume(void)
295{ 316{
296 int prescale = 0; 317 int prescale;
297 int l, r; 318 int l, r;
298 319
299#ifndef HAVE_TLV320
300 prescale = MAX(current_bass, current_treble); 320 prescale = MAX(current_bass, current_treble);
301 if (prescale < 0) 321 if (prescale < 0)
302 prescale = 0; /* no need to prescale if we don't boost 322 prescale = 0; /* no need to prescale if we don't boost
@@ -307,13 +327,12 @@ static void set_prescaled_volume(void)
307 * instead (might cause clipping). */ 327 * instead (might cause clipping). */
308 if (current_volume + prescale > VOLUME_MAX) 328 if (current_volume + prescale > VOLUME_MAX)
309 prescale = VOLUME_MAX - current_volume; 329 prescale = VOLUME_MAX - current_volume;
310#endif 330
311 331#if defined(HAVE_SW_TONE_CONTROLS)
312#if CONFIG_CODEC == MAS3507D 332 dsp_callback(DSP_CALLBACK_SET_PRESCALE, prescale);
333#elif CONFIG_CODEC == MAS3507D
313 mas_writereg(MAS_REG_KPRESCALE, prescale_table[prescale/10]); 334 mas_writereg(MAS_REG_KPRESCALE, prescale_table[prescale/10]);
314#elif defined(HAVE_UDA1380) 335#elif defined(HAVE_UDA1380) || defined(HAVE_WM8975) || defined(HAVE_WM8758) \
315 audiohw_set_mixer_vol(tenthdb2mixer(-prescale), tenthdb2mixer(-prescale));
316#elif defined(HAVE_WM8975) || defined(HAVE_WM8758) \
317 || defined(HAVE_WM8731) || defined(HAVE_WM8721) || defined(HAVE_WM8751) 336 || defined(HAVE_WM8731) || defined(HAVE_WM8721) || defined(HAVE_WM8751)
318 audiohw_set_mixer_vol(tenthdb2mixer(-prescale), tenthdb2mixer(-prescale)); 337 audiohw_set_mixer_vol(tenthdb2mixer(-prescale), tenthdb2mixer(-prescale));
319#endif 338#endif
@@ -338,9 +357,7 @@ static void set_prescaled_volume(void)
338 357
339#if CONFIG_CODEC == MAS3507D 358#if CONFIG_CODEC == MAS3507D
340 dac_volume(tenthdb2reg(l), tenthdb2reg(r), false); 359 dac_volume(tenthdb2reg(l), tenthdb2reg(r), false);
341#elif defined(HAVE_UDA1380) 360#elif defined(HAVE_UDA1380) || defined(HAVE_WM8975) || defined(HAVE_WM8758) \
342 audiohw_set_master_vol(tenthdb2master(l), tenthdb2master(r));
343#elif defined(HAVE_WM8975) || defined(HAVE_WM8758) \
344 || defined(HAVE_WM8731) || defined(HAVE_WM8721) || defined(HAVE_WM8751) 361 || defined(HAVE_WM8731) || defined(HAVE_WM8721) || defined(HAVE_WM8751)
345 audiohw_set_master_vol(tenthdb2master(l), tenthdb2master(r)); 362 audiohw_set_master_vol(tenthdb2master(l), tenthdb2master(r));
346#if defined(HAVE_WM8975) || defined(HAVE_WM8758) || defined(HAVE_WM8751) 363#if defined(HAVE_WM8975) || defined(HAVE_WM8758) || defined(HAVE_WM8751)
@@ -484,12 +501,15 @@ void sound_set_balance(int value)
484#endif 501#endif
485} 502}
486 503
487#ifndef HAVE_TLV320
488void sound_set_bass(int value) 504void sound_set_bass(int value)
489{ 505{
490 if(!audio_is_initialized) 506 if(!audio_is_initialized)
491 return; 507 return;
492#if (CONFIG_CODEC == MAS3587F) || (CONFIG_CODEC == MAS3539F) 508#if defined(HAVE_SW_TONE_CONTROLS)
509 current_bass = value * 10;
510 dsp_callback(DSP_CALLBACK_SET_BASS, current_bass);
511 set_prescaled_volume();
512#elif (CONFIG_CODEC == MAS3587F) || (CONFIG_CODEC == MAS3539F)
493 unsigned tmp = ((unsigned)(value * 8) & 0xff) << 8; 513 unsigned tmp = ((unsigned)(value * 8) & 0xff) << 8;
494 mas_codec_writereg(0x14, tmp); 514 mas_codec_writereg(0x14, tmp);
495#elif CONFIG_CODEC == MAS3507D 515#elif CONFIG_CODEC == MAS3507D
@@ -515,7 +535,11 @@ void sound_set_treble(int value)
515{ 535{
516 if(!audio_is_initialized) 536 if(!audio_is_initialized)
517 return; 537 return;
518#if (CONFIG_CODEC == MAS3587F) || (CONFIG_CODEC == MAS3539F) 538#if defined(HAVE_SW_TONE_CONTROLS)
539 current_treble = value * 10;
540 dsp_callback(DSP_CALLBACK_SET_TREBLE, current_treble);
541 set_prescaled_volume();
542#elif (CONFIG_CODEC == MAS3587F) || (CONFIG_CODEC == MAS3539F)
519 unsigned tmp = ((unsigned)(value * 8) & 0xff) << 8; 543 unsigned tmp = ((unsigned)(value * 8) & 0xff) << 8;
520 mas_codec_writereg(0x15, tmp); 544 mas_codec_writereg(0x15, tmp);
521#elif CONFIG_CODEC == MAS3507D 545#elif CONFIG_CODEC == MAS3507D
@@ -536,7 +560,6 @@ void sound_set_treble(int value)
536 (void)value; 560 (void)value;
537#endif 561#endif
538} 562}
539#endif /* HAVE_TLV320 */
540 563
541void sound_set_channels(int value) 564void sound_set_channels(int value)
542{ 565{