summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRyan Billing <ryjobil@gmail.com>2013-10-04 01:57:00 +1300
committerMichael Giacomelli <giac2000@hotmail.com>2013-12-15 22:24:08 +0100
commitd0918b98fa0cfba21208a4fb5ed153687b8f02c3 (patch)
tree006ef2bb902dfd83101fbfa3fe63b07f47d9e8e6
parent5b5f0755d6d7fd9e3fdfdb479caeb7fafd0a9960 (diff)
downloadrockbox-d0918b98fa0cfba21208a4fb5ed153687b8f02c3.tar.gz
rockbox-d0918b98fa0cfba21208a4fb5ed153687b8f02c3.zip
DSP Compressor: Sidechain, Exponential Atk/Rls
This is an improvement to the current compressor which I have added to my own Sansa Fuze V2 build. I am submitting here in case others find it interesting. Features added to the existing compressor: Attack, Look-ahead, Sidechain Filtering. Exponential attack and release characteristic response. Benefits from adding missing features: Attack: Preserve perceived "brightness" of tone by letting onset transients come through at a higher level than the rest of the compressed program material. Look-ahead: With Attack comes clipping on the leading several cycles of a transient onset. With look-ahead function, this can be pre-emptively mitigated with a slower gain change (less distortion). Look-ahead limiting is implemented to prevent clipping while keeping gain change ramp to an interval near 3ms instead of instant attack. The existing compressor implementation distorts the leading edge of a transient by causing instant gain change, resulting in log() distortion. This sounds "woofy" to me. Exponential Attack/Release: eMore natural sounding. On attack, this is a true straight line of 10dB per attack interval. Release is a little different, however, sounds natural as an analog compressor. Sidechain Filtering: Mild high-pass filter reduces response to low frequency onsets. For example, a hard kick drum is less likely to make the whole of the program material appear to fade in and out. Combined with a moderate attack time, such a transient will ride through with minimal audible artifact. Overall these changes make dynamic music sound more "open", more natural. The goal of a compressor is to make dyanamic music sound louder without necessarily sounding as though it has been compressed. I believe these changes come closer to this goal. Enjoy. If not, I am enjoying it Change-Id: I664eace546c364b815b4dc9ed4a72849231a0eb2 Reviewed-on: http://gerrit.rockbox.org/626 Tested: Purling Nayuki <cyq.yzfl@gmail.com> Reviewed-by: Michael Giacomelli <giac2000@hotmail.com>
-rw-r--r--apps/lang/english.lang17
-rw-r--r--apps/menus/sound_menu.c5
-rw-r--r--apps/settings_list.c6
-rw-r--r--docs/CREDITS1
-rw-r--r--lib/rbcodec/dsp/compressor.c379
-rw-r--r--lib/rbcodec/dsp/compressor.h1
-rw-r--r--manual/configure_rockbox/sound_settings.tex3
7 files changed, 322 insertions, 90 deletions
diff --git a/apps/lang/english.lang b/apps/lang/english.lang
index d0dc5c5fc4..dcad532f7a 100644
--- a/apps/lang/english.lang
+++ b/apps/lang/english.lang
@@ -11911,6 +11911,23 @@
11911 </voice> 11911 </voice>
11912</phrase> 11912</phrase>
11913<phrase> 11913<phrase>
11914 id: LANG_COMPRESSOR_ATTACK
11915 desc: in sound settings
11916 user: core
11917 <source>
11918 *: none
11919 swcodec: "Attack Time"
11920 </source>
11921 <dest>
11922 *: none
11923 swcodec: "Attack Time"
11924 </dest>
11925 <voice>
11926 *: none
11927 swcodec: "Attack Time"
11928 </voice>
11929</phrase>
11930<phrase>
11914 id: LANG_COMPRESSOR_RELEASE 11931 id: LANG_COMPRESSOR_RELEASE
11915 desc: in sound settings 11932 desc: in sound settings
11916 user: core 11933 user: core
diff --git a/apps/menus/sound_menu.c b/apps/menus/sound_menu.c
index fd192cb661..28cc257193 100644
--- a/apps/menus/sound_menu.c
+++ b/apps/menus/sound_menu.c
@@ -140,12 +140,15 @@ static int timestretch_callback(int action,const struct menu_item_ex *this_item)
140 MENUITEM_SETTING(compressor_knee, 140 MENUITEM_SETTING(compressor_knee,
141 &global_settings.compressor_settings.knee, 141 &global_settings.compressor_settings.knee,
142 lowlatency_callback); 142 lowlatency_callback);
143 MENUITEM_SETTING(compressor_attack,
144 &global_settings.compressor_settings.attack_time,
145 lowlatency_callback);
143 MENUITEM_SETTING(compressor_release, 146 MENUITEM_SETTING(compressor_release,
144 &global_settings.compressor_settings.release_time, 147 &global_settings.compressor_settings.release_time,
145 lowlatency_callback); 148 lowlatency_callback);
146 MAKE_MENU(compressor_menu,ID2P(LANG_COMPRESSOR), NULL, Icon_NOICON, 149 MAKE_MENU(compressor_menu,ID2P(LANG_COMPRESSOR), NULL, Icon_NOICON,
147 &compressor_threshold, &compressor_gain, &compressor_ratio, 150 &compressor_threshold, &compressor_gain, &compressor_ratio,
148 &compressor_knee, &compressor_release); 151 &compressor_knee, &compressor_attack, &compressor_release);
149#endif 152#endif
150 153
151#if (CONFIG_CODEC == MAS3587F) || (CONFIG_CODEC == MAS3539F) 154#if (CONFIG_CODEC == MAS3587F) || (CONFIG_CODEC == MAS3539F)
diff --git a/apps/settings_list.c b/apps/settings_list.c
index 6ffb2b551b..bd2bfce36f 100644
--- a/apps/settings_list.c
+++ b/apps/settings_list.c
@@ -1653,7 +1653,11 @@ const struct settings_list settings[] = {
1653 CHOICE_SETTING(F_SOUNDSETTING|F_NO_WRAP, compressor_settings.knee, 1653 CHOICE_SETTING(F_SOUNDSETTING|F_NO_WRAP, compressor_settings.knee,
1654 LANG_COMPRESSOR_KNEE, 1, "compressor knee", 1654 LANG_COMPRESSOR_KNEE, 1, "compressor knee",
1655 "hard knee,soft knee", compressor_set, 2, 1655 "hard knee,soft knee", compressor_set, 2,
1656 ID2P(LANG_COMPRESSOR_HARD_KNEE), ID2P(LANG_COMPRESSOR_SOFT_KNEE)), 1656 ID2P(LANG_COMPRESSOR_HARD_KNEE), ID2P(LANG_COMPRESSOR_SOFT_KNEE)),
1657 INT_SETTING_NOWRAP(F_SOUNDSETTING, compressor_settings.attack_time,
1658 LANG_COMPRESSOR_ATTACK, 5,
1659 "compressor attack time", UNIT_MS, 0, 30,
1660 5, NULL, NULL, compressor_set),
1657 INT_SETTING_NOWRAP(F_SOUNDSETTING, compressor_settings.release_time, 1661 INT_SETTING_NOWRAP(F_SOUNDSETTING, compressor_settings.release_time,
1658 LANG_COMPRESSOR_RELEASE, 500, 1662 LANG_COMPRESSOR_RELEASE, 500,
1659 "compressor release time", UNIT_MS, 100, 1000, 1663 "compressor release time", UNIT_MS, 100, 1000,
diff --git a/docs/CREDITS b/docs/CREDITS
index b78c44904b..94e8df2706 100644
--- a/docs/CREDITS
+++ b/docs/CREDITS
@@ -632,6 +632,7 @@ Vanja Cvelbar
632Richard Quirk 632Richard Quirk
633Kirill Stryaponoff 633Kirill Stryaponoff
634Roman Poltoradnev 634Roman Poltoradnev
635Ryan Billing
635 636
636The libmad team 637The libmad team
637The wavpack team 638The wavpack team
diff --git a/lib/rbcodec/dsp/compressor.c b/lib/rbcodec/dsp/compressor.c
index a222caed7f..685851ec29 100644
--- a/lib/rbcodec/dsp/compressor.c
+++ b/lib/rbcodec/dsp/compressor.c
@@ -23,44 +23,155 @@
23#include "fracmul.h" 23#include "fracmul.h"
24#include <string.h> 24#include <string.h>
25 25
26/* Define LOGF_ENABLE to enable logf output in this file */ 26/* Define LOGF_ENABLE to enable logf output in this file
27/*#define LOGF_ENABLE*/ 27 * #define LOGF_ENABLE
28 */
28#include "logf.h" 29#include "logf.h"
29#include "dsp_proc_entry.h" 30#include "dsp_proc_entry.h"
30#include "compressor.h" 31#include "compressor.h"
31#include "dsp_misc.h" 32#include "dsp_misc.h"
32 33
34#define UNITY (1L << 24) /* unity gain in S7.24 format */
35#define MAX_DLY 960 /* Max number of samples to delay
36 output (960 = 5ms @ 192 kHz)
37 */
38#define MAX_CH 4 /* Is there a good malloc() or equal
39 for rockbox?
40 */
41#define DLY_TIME 3 /* milliseconds */
42
33static struct compressor_settings curr_set; /* Cached settings */ 43static struct compressor_settings curr_set; /* Cached settings */
34 44
35static int32_t comp_rel_slope IBSS_ATTR; /* S7.24 format */ 45static int32_t comp_makeup_gain IBSS_ATTR; /* S7.24 format */
36static int32_t comp_makeup_gain IBSS_ATTR; /* S7.24 format */ 46static int32_t comp_curve[66] IBSS_ATTR; /* S7.24 format */
37static int32_t comp_curve[66] IBSS_ATTR; /* S7.24 format */ 47static int32_t release_gain IBSS_ATTR; /* S7.24 format */
38static int32_t release_gain IBSS_ATTR; /* S7.24 format */ 48static int32_t release_holdoff IBSS_ATTR; /* S7.24 format */
49
50/* 1-pole filter coefficients for exponential attack/release times */
51static int32_t rlsca IBSS_ATTR; /* Release 'alpha' */
52static int32_t rlscb IBSS_ATTR; /* Release 'beta' */
53
54static int32_t attca IBSS_ATTR; /* Attack 'alpha' */
55static int32_t attcb IBSS_ATTR; /* Attack 'beta' */
56
57static int32_t limitca IBSS_ATTR; /* Limiter Attack 'alpha' */
58
59/* 1-pole filter coefficients for sidechain pre-emphasis filters */
60static int32_t hp1ca IBSS_ATTR; /* hpf1 'alpha' */
61static int32_t hp2ca IBSS_ATTR; /* hpf2 'beta' */
62
63/* 1-pole hp filter state variables for pre-emphasis filters */
64static int32_t hpfx1 IBSS_ATTR; /* hpf1 and hpf2 x[n-1] */
65static int32_t hp1y1 IBSS_ATTR; /* hpf2 y[n-1] */
66static int32_t hp2y1 IBSS_ATTR; /* hpf2 y[n-1] */
67
68/* Delay Line for look-ahead compression */
69static int32_t labuf[MAX_CH][MAX_DLY]; /* look-ahead buffer */
70static int32_t delay_time;
71static int32_t delay_write;
72static int32_t delay_read;
73
74/** 1-Pole LP Filter first coefficient computation
75 * Returns S7.24 format integer used for "a" coefficient
76 * rc: "RC Time Constant", or time to decay to 1/e
77 * fs: Sampling Rate
78 * Interpret attack and release time as an RC time constant
79 * (time to decay to 1/e)
80 * 1-pole filters use approximation
81 * a0 = 1/(fs*rc + 1)
82 * b1 = 1.0 - a0
83 * fs = Sampling Rate
84 * rc = Time to decay to 1/e
85 * y[n] = a0*x[n] + b1*y[n-1]
86 *
87 * According to simulation on Intel hardware
88 * this algorithm produces < 2% error for rc < ~100ms
89 * For rc 100ms - 1000ms, error approaches 0%
90 * For compressor attack/release times, this is more than adequate.
91 *
92 * Error was measured against the more rigorous computation:
93 * a0 = 1.0 - e^(-1.0/(fs*rc))
94 */
95
96int32_t get_lpf_coeff(int32_t rc, int32_t fs, int32_t rc_units)
97{
98 int32_t c = fs*rc;
99 c /= rc_units;
100 c += 1;
101 c = UNITY/c;
102 return c;
103}
39 104
40#define UNITY (1L << 24) /* unity gain in S7.24 format */ 105/** Coefficients to get 10dB change per time period "rc"
106 * from 1-pole LP filter topology
107 * This function is better used to match behavior of
108 * linear release which was implemented prior to implementation
109 * of exponential attack/release function
110 */
111
112int32_t get_att_rls_coeff(int32_t rc, int32_t fs)
113{
114 int32_t c = UNITY/fs;
115 c *= 1152; /* 1000 * 10/( 20*log10( 1/e ) ) */
116 c /= rc;
117 return c;
118}
41 119
42/** COMPRESSOR UPDATE 120/** COMPRESSOR UPDATE
43 * Called via the menu system to configure the compressor process */ 121 * Called via the menu system to configure the compressor process
122 */
44static bool compressor_update(struct dsp_config *dsp, 123static bool compressor_update(struct dsp_config *dsp,
45 const struct compressor_settings *settings) 124 const struct compressor_settings *settings)
46{ 125{
47 /* make settings values useful */ 126 /* make settings values useful */
48 int threshold = settings->threshold; 127 int threshold = settings->threshold;
49 bool auto_gain = settings->makeup_gain == 1; 128 bool auto_gain = settings->makeup_gain == 1;
50 static const int comp_ratios[] = { 2, 4, 6, 10, 0 }; 129 static const int comp_ratios[] = { 2, 4, 6, 10, 0 };
51 int ratio = comp_ratios[settings->ratio]; 130 int ratio = comp_ratios[settings->ratio];
52 bool soft_knee = settings->knee == 1; 131 bool soft_knee = settings->knee == 1;
53 int release = settings->release_time * 132 int32_t release = settings->release_time;
54 dsp_get_output_frequency(dsp) / 1000; 133 int32_t attack = settings->attack_time;
134
135 /* Compute Attack and Release Coefficients */
136 int32_t fs = dsp_get_output_frequency(dsp);
137
138 /* Release */
139 rlsca = get_att_rls_coeff(release, fs);
140 rlscb = UNITY - rlsca ;
141
142 /* Attack */
143 if(attack > 0)
144 {
145 attca = get_att_rls_coeff(attack, fs);
146 attcb = UNITY - attca ;
147 }
148 else {
149 attca = UNITY;
150 attcb = 0;
151 }
55 152
56 bool changed = settings == &curr_set; /* If frequency change */ 153
154 /* Sidechain pre-emphasis filter coefficients */
155 hp1ca = fs + 0x003C1; /** The "magic" constant is 1/RC. This filter
156 * cut-off is approximately 237 Hz
157 */
158 hp1ca = UNITY/hp1ca;
159 hp1ca *= fs;
160
161 hp2ca = fs + 0x02065; /* The "magic" constant is 1/RC. This filter
162 * cut-off is approximately 2.18 kHz
163 */
164 hp2ca = UNITY/hp2ca;
165 hp2ca *= fs;
166
167 bool changed = settings == &curr_set; /* If frequency changes */
57 bool active = threshold < 0; 168 bool active = threshold < 0;
58 169
59 if (memcmp(settings, &curr_set, sizeof (curr_set))) 170 if (memcmp(settings, &curr_set, sizeof (curr_set)))
60 { 171 {
61 /* Compressor settings have changed since last call */ 172 /* Compressor settings have changed since last call */
62 changed = true; 173 changed = true;
63 174
64#if defined(ROCKBOX_HAS_LOGF) && defined(LOGF_ENABLE) 175#if defined(ROCKBOX_HAS_LOGF) && defined(LOGF_ENABLE)
65 if (settings->threshold != curr_set.threshold) 176 if (settings->threshold != curr_set.threshold)
66 { 177 {
@@ -91,6 +202,10 @@ static bool compressor_update(struct dsp_config *dsp,
91 { 202 {
92 logf(" Compressor Release: %d", release); 203 logf(" Compressor Release: %d", release);
93 } 204 }
205 if (settings->attack_time != cur_set.attack_time)
206 {
207 logf(" Compressor Attack: %d", attack);
208 }
94#endif 209#endif
95 210
96 curr_set = *settings; 211 curr_set = *settings;
@@ -125,18 +240,18 @@ static bool compressor_update(struct dsp_config *dsp,
125 int32_t offset; /* S15.16 format */ 240 int32_t offset; /* S15.16 format */
126 } db_curve[5]; 241 } db_curve[5];
127 242
128 /** Set up the shape of the compression curve first as decibel 243 /** Set up the shape of the compression curve first as decibel values
129 values */ 244 * db_curve[0] = bottom of knee
130 /* db_curve[0] = bottom of knee 245 * [1] = threshold
131 [1] = threshold 246 * [2] = top of knee
132 [2] = top of knee 247 * [3] = 0 db input
133 [3] = 0 db input 248 * [4] = ~+12db input (2 bits clipping overhead)
134 [4] = ~+12db input (2 bits clipping overhead) */ 249 */
135 250
136 db_curve[1].db = threshold << 16; 251 db_curve[1].db = threshold << 16;
137 if (soft_knee) 252 if (soft_knee)
138 { 253 {
139 /* bottom of knee is 3dB below the threshold for soft knee*/ 254 /* bottom of knee is 3dB below the threshold for soft knee */
140 db_curve[0].db = db_curve[1].db - (3 << 16); 255 db_curve[0].db = db_curve[1].db - (3 << 16);
141 /* top of knee is 3dB above the threshold for soft knee */ 256 /* top of knee is 3dB above the threshold for soft knee */
142 db_curve[2].db = db_curve[1].db + (3 << 16); 257 db_curve[2].db = db_curve[1].db + (3 << 16);
@@ -175,24 +290,28 @@ static bool compressor_update(struct dsp_config *dsp,
175 } 290 }
176 291
177 /** Now set up the comp_curve table with compression offsets in the 292 /** Now set up the comp_curve table with compression offsets in the
178 form of gain factors in S7.24 format */ 293 * form of gain factors in S7.24 format
179 /* comp_curve[0] is 0 (-infinity db) input */ 294 * comp_curve[0] is 0 (-infinity db) input
295 */
180 comp_curve[0] = UNITY; 296 comp_curve[0] = UNITY;
181 /* comp_curve[1 to 63] are intermediate compression values 297 /** comp_curve[1 to 63] are intermediate compression values
182 corresponding to the 6 MSB of the input values of a non-clipped 298 * corresponding to the 6 MSB of the input values of a non-clipped
183 signal */ 299 * signal
300 */
184 for (int i = 1; i < 64; i++) 301 for (int i = 1; i < 64; i++)
185 { 302 {
186 /* db constants are stored as positive numbers; 303 /** db constants are stored as positive numbers;
187 make them negative here */ 304 * make them negative here
305 */
188 int32_t this_db = -db[i]; 306 int32_t this_db = -db[i];
189 307
190 /* no compression below the knee */ 308 /* no compression below the knee */
191 if (this_db <= db_curve[0].db) 309 if (this_db <= db_curve[0].db)
192 comp_curve[i] = UNITY; 310 comp_curve[i] = UNITY;
193 311
194 /* if soft knee and below top of knee, 312 /** if soft knee and below top of knee,
195 interpolate along soft knee slope */ 313 * interpolate along soft knee slope
314 */
196 else if (soft_knee && (this_db <= db_curve[2].db)) 315 else if (soft_knee && (this_db <= db_curve[2].db))
197 comp_curve[i] = fp_factor(fp_mul( 316 comp_curve[i] = fp_factor(fp_mul(
198 ((this_db - db_curve[0].db) / 6), 317 ((this_db - db_curve[0].db) / 6),
@@ -204,14 +323,22 @@ static bool compressor_update(struct dsp_config *dsp,
204 fp_div((db_curve[1].db - this_db), db_curve[1].db, 16), 323 fp_div((db_curve[1].db - this_db), db_curve[1].db, 16),
205 db_curve[3].offset, 16), 16) << 8; 324 db_curve[3].offset, 16), 16) << 8;
206 } 325 }
207 /* comp_curve[64] is the compression level of a maximum level, 326 /** comp_curve[64] is the compression level of a maximum level,
208 non-clipped signal */ 327 * non-clipped signal
328 */
209 comp_curve[64] = fp_factor(db_curve[3].offset, 16) << 8; 329 comp_curve[64] = fp_factor(db_curve[3].offset, 16) << 8;
210 330
211 /* comp_curve[65] is the compression level of a maximum level, 331 /** comp_curve[65] is the compression level of a maximum level,
212 clipped signal */ 332 * clipped signal
333 */
213 comp_curve[65] = fp_factor(db_curve[4].offset, 16) << 8; 334 comp_curve[65] = fp_factor(db_curve[4].offset, 16) << 8;
214 335
336 /** if using auto peak, then makeup gain is max offset -
337 * 3dB headroom
338 */
339 comp_makeup_gain = auto_gain ?
340 fp_factor(-(db_curve[3].offset) - 0x4AC4, 16) << 8 : UNITY;
341
215#if defined(ROCKBOX_HAS_LOGF) && defined(LOGF_ENABLE) 342#if defined(ROCKBOX_HAS_LOGF) && defined(LOGF_ENABLE)
216 logf("\n *** Compression Offsets ***"); 343 logf("\n *** Compression Offsets ***");
217 /* some settings for display only, not used in calculations */ 344 /* some settings for display only, not used in calculations */
@@ -233,20 +360,10 @@ static bool compressor_update(struct dsp_config *dsp,
233 if (i % 4 == 0) DEBUGF("\n"); 360 if (i % 4 == 0) DEBUGF("\n");
234 } 361 }
235 DEBUGF("\n"); 362 DEBUGF("\n");
236#endif
237 363
238 /* if using auto peak, then makeup gain is max offset -
239 .1dB headroom */
240 comp_makeup_gain = auto_gain ?
241 fp_factor(-(db_curve[3].offset) - 0x199A, 16) << 8 : UNITY;
242 logf("Makeup gain:\t%.6f", (float)comp_makeup_gain / UNITY); 364 logf("Makeup gain:\t%.6f", (float)comp_makeup_gain / UNITY);
365#endif
243 366
244 /* calculate per-sample gain change a rate of 10db over release time
245 */
246 comp_rel_slope = 0xAF0BB2 / release;
247 logf("Release slope:\t%.6f", (float)comp_rel_slope / UNITY);
248
249 release_gain = UNITY;
250 return active; 367 return active;
251} 368}
252 369
@@ -258,39 +375,41 @@ static inline int32_t get_compression_gain(struct sample_format *format,
258 int32_t sample) 375 int32_t sample)
259{ 376{
260 const int frac_bits_offset = format->frac_bits - 15; 377 const int frac_bits_offset = format->frac_bits - 15;
261 378
262 /* sample must be positive */ 379 /* sample must be positive */
263 if (sample < 0) 380 if (sample < 0)
264 sample = -(sample + 1); 381 sample = -(sample + 1);
265 382
266 /* shift sample into 15 frac bit range */ 383 /* shift sample into 15 frac bit range */
267 if (frac_bits_offset > 0) 384 if (frac_bits_offset > 0)
268 sample >>= frac_bits_offset; 385 sample >>= frac_bits_offset;
269 if (frac_bits_offset < 0) 386 if (frac_bits_offset < 0)
270 sample <<= -frac_bits_offset; 387 sample <<= -frac_bits_offset;
271 388
272 /* normal case: sample isn't clipped */ 389 /* normal case: sample isn't clipped */
273 if (sample < (1 << 15)) 390 if (sample < (1 << 15))
274 { 391 {
275 /* index is 6 MSB, rem is 9 LSB */ 392 /* index is 6 MSB, rem is 9 LSB */
276 int index = sample >> 9; 393 int index = sample >> 9;
277 int32_t rem = (sample & 0x1FF) << 22; 394 int32_t rem = (sample & 0x1FF) << 22;
278 395
279 /* interpolate from the compression curve: 396 /** interpolate from the compression curve:
280 higher gain - ((rem / (1 << 31)) * (higher gain - lower gain)) */ 397 * higher gain - ((rem / (1 << 31)) * (higher gain - lower gain))
398 */
281 return comp_curve[index] - (FRACMUL(rem, 399 return comp_curve[index] - (FRACMUL(rem,
282 (comp_curve[index] - comp_curve[index + 1]))); 400 (comp_curve[index] - comp_curve[index + 1])));
283 } 401 }
284 /* sample is somewhat clipped, up to 2 bits of overhead */ 402 /* sample is somewhat clipped, up to 2 bits of overhead */
285 if (sample < (1 << 17)) 403 if (sample < (1 << 17))
286 { 404 {
287 /* straight interpolation: 405 /** straight interpolation:
288 higher gain - ((clipped portion of sample * 4/3 406 * higher gain - ((clipped portion of sample * 4/3
289 / (1 << 31)) * (higher gain - lower gain)) */ 407 * / (1 << 31)) * (higher gain - lower gain))
408 */
290 return comp_curve[64] - (FRACMUL(((sample - (1 << 15)) / 3) << 16, 409 return comp_curve[64] - (FRACMUL(((sample - (1 << 15)) / 3) << 16,
291 (comp_curve[64] - comp_curve[65]))); 410 (comp_curve[64] - comp_curve[65])));
292 } 411 }
293 412
294 /* sample is too clipped, return invalid value */ 413 /* sample is too clipped, return invalid value */
295 return -1; 414 return -1;
296} 415}
@@ -322,55 +441,115 @@ static void compressor_process(struct dsp_proc_entry *this,
322 441
323 while (count-- > 0) 442 while (count-- > 0)
324 { 443 {
325 /* use lowest (most compressed) gain factor of the output buffer 444
326 sample pair for both samples (mono is also handled correctly here) 445 /* Use the average of the channels */
327 */ 446
328 int32_t sample_gain = UNITY; 447 int32_t sample_gain = UNITY;
448 int32_t x = 0;
449 int32_t tmpx = 0;
450 int32_t in_buf_max_level = 0;
329 for (int ch = 0; ch < num_chan; ch++) 451 for (int ch = 0; ch < num_chan; ch++)
330 { 452 {
331 int32_t this_gain = get_compression_gain(&buf->format, *in_buf[ch]); 453 tmpx = *in_buf[ch];
332 if (this_gain < sample_gain) 454 x += tmpx;
333 sample_gain = this_gain; 455 labuf[ch][delay_write] = tmpx;
456 /* Limiter detection */
457 if(tmpx < 0) tmpx = -(tmpx + 1);
458 if(tmpx > in_buf_max_level) in_buf_max_level = tmpx;
334 } 459 }
335 460
336 /* perform release slope; skip if no compression and no release slope 461 /** Divide it by the number of channels, roughly
462 * It will be exact if the number of channels a power of 2
463 * it will be imperfect otherwise. Real division costs too
464 * much here, and most of the time it will be 2 channels (stereo)
337 */ 465 */
338 if ((sample_gain != UNITY) || (release_gain != UNITY)) 466 x >>= (num_chan >> 1);
339 { 467
340 /* if larger offset than previous slope, start new release slope 468 /** 1p HP Filters: y[n] = a*(y[n-1] + x - x[n-1])
341 */ 469 * Zero and Pole in the same place to reduce computation
342 if ((sample_gain <= release_gain) && (sample_gain > 0)) 470 * Run the first pre-emphasis filter
343 { 471 */
472 int32_t tmp1 = x - hpfx1 + hp1y1;
473 hp1y1 = FRACMUL_SHL(hp1ca, tmp1, 7);
474
475 /* Run the second pre-emphasis filter */
476 tmp1 = x - hpfx1 + hp2y1;
477 hp2y1 = FRACMUL_SHL(hp2ca, tmp1, 7);
478 hpfx1 = x;
479
480 /* Apply weighted sum to the pre-emphasis network */
481 sample_gain = (x>>1) + hp1y1 + (hp2y1<<1); /* x/2 + hp1 + 2*hp2 */
482 sample_gain >>= 1;
483 sample_gain += sample_gain >> 1;
484 sample_gain = get_compression_gain(&buf->format, sample_gain);
485
486 /* Exponential Attack and Release */
487
488 if ((sample_gain <= release_gain) && (sample_gain > 0))
489 {
490 /* Attack */
491 if(attca != UNITY)
492 {
493 int32_t this_gain = FRACMUL_SHL(release_gain, attcb, 7);
494 this_gain += FRACMUL_SHL(sample_gain, attca, 7);
495 release_gain = this_gain;
496 }
497 else
498 {
344 release_gain = sample_gain; 499 release_gain = sample_gain;
500 }
501 /** reset it to delay time so it cannot release before the
502 * delayed signal releases
503 */
504 release_holdoff = delay_time;
505 }
506 else
507 /* Reverse exponential decay to current gain value */
508 {
509 /* Don't start release while output is still above thresh */
510 if(release_holdoff > 0)
511 {
512 release_holdoff--;
345 } 513 }
346 else 514 else
347 /* keep sloping towards unity gain (and ignore invalid value) */
348 { 515 {
349 release_gain += comp_rel_slope; 516 /* Release */
350 if (release_gain > UNITY) 517 int32_t this_gain = FRACMUL_SHL(release_gain, rlscb, 7);
351 { 518 this_gain += FRACMUL_SHL(sample_gain,rlsca,7);
352 release_gain = UNITY; 519 release_gain = this_gain;
353 }
354 } 520 }
521
522 }
523
524 /** total gain factor is the product of release gain and makeup gain,
525 * but avoid computation if possible
526 */
527
528 int32_t total_gain = FRACMUL_SHL(release_gain, comp_makeup_gain, 7);
529
530 /* Look-ahead limiter */
531 int32_t test_gain = FRACMUL_SHL(total_gain, in_buf_max_level, 3);
532 if( test_gain > UNITY)
533 {
534 release_gain -= limitca;
355 } 535 }
356 536
357 /* total gain factor is the product of release gain and makeup gain, 537 /** Implement the compressor: apply total gain factor (if any) to the
358 but avoid computation if possible */ 538 * output buffer sample pair/mono sample
359 int32_t total_gain = ((release_gain == UNITY) ? comp_makeup_gain : 539 */
360 (comp_makeup_gain == UNITY) ? release_gain :
361 FRACMUL_SHL(release_gain, comp_makeup_gain, 7));
362
363 /* Implement the compressor: apply total gain factor (if any) to the
364 output buffer sample pair/mono sample */
365 if (total_gain != UNITY) 540 if (total_gain != UNITY)
366 { 541 {
367 for (int ch = 0; ch < num_chan; ch++) 542 for (int ch = 0; ch < num_chan; ch++)
368 { 543 {
369 *in_buf[ch] = FRACMUL_SHL(total_gain, *in_buf[ch], 7); 544 *in_buf[ch] = FRACMUL_SHL(total_gain, labuf[ch][delay_read], 7);
370 } 545 }
371 } 546 }
372 in_buf[0]++; 547 in_buf[0]++;
373 in_buf[1]++; 548 in_buf[1]++;
549 delay_write++;
550 delay_read++;
551 if(delay_write >= MAX_DLY) delay_write = 0;
552 if(delay_read >= MAX_DLY) delay_read = 0;
374 } 553 }
375 554
376 (void)this; 555 (void)this;
@@ -382,6 +561,8 @@ static intptr_t compressor_configure(struct dsp_proc_entry *this,
382 unsigned int setting, 561 unsigned int setting,
383 intptr_t value) 562 intptr_t value)
384{ 563{
564 int i,j;
565
385 switch (setting) 566 switch (setting)
386 { 567 {
387 case DSP_PROC_INIT: 568 case DSP_PROC_INIT:
@@ -394,7 +575,29 @@ static intptr_t compressor_configure(struct dsp_proc_entry *this,
394 /* Fall-through */ 575 /* Fall-through */
395 case DSP_RESET: 576 case DSP_RESET:
396 case DSP_FLUSH: 577 case DSP_FLUSH:
578
397 release_gain = UNITY; 579 release_gain = UNITY;
580 for(i=0; i<MAX_CH; i++)
581 {
582 for(j=0; j<MAX_DLY; j++)
583 {
584 labuf[i][j] = 0; /* All Silence */
585 }
586 }
587
588 /* Delay Line Read/Write Pointers */
589 int32_t fs = dsp_get_output_frequency(dsp);
590 delay_read = 0;
591 delay_write = (DLY_TIME*fs/1000);
592 if(delay_write >= MAX_DLY) {
593 delay_write = MAX_DLY - 1; /* Limit to the max allocated buffer */
594 }
595
596 delay_time = delay_write;
597 release_holdoff = delay_write;
598 limitca = get_att_rls_coeff(DLY_TIME, fs); /** Attack time for
599 * look-ahead limiter
600 */
398 break; 601 break;
399 602
400 case DSP_SET_OUT_FREQUENCY: 603 case DSP_SET_OUT_FREQUENCY:
diff --git a/lib/rbcodec/dsp/compressor.h b/lib/rbcodec/dsp/compressor.h
index e41950926e..35aa0eeb65 100644
--- a/lib/rbcodec/dsp/compressor.h
+++ b/lib/rbcodec/dsp/compressor.h
@@ -28,6 +28,7 @@ struct compressor_settings
28 int ratio; 28 int ratio;
29 int knee; 29 int knee;
30 int release_time; 30 int release_time;
31 int attack_time;
31}; 32};
32 33
33void dsp_set_compressor(const struct compressor_settings *settings); 34void dsp_set_compressor(const struct compressor_settings *settings);
diff --git a/manual/configure_rockbox/sound_settings.tex b/manual/configure_rockbox/sound_settings.tex
index f06bfaf6e5..d2da07b983 100644
--- a/manual/configure_rockbox/sound_settings.tex
+++ b/manual/configure_rockbox/sound_settings.tex
@@ -603,6 +603,9 @@ non-compressed signal to a compressed signal. Hard Knee means that the
603transition occurs precisely at the threshold. The Soft Knee setting smoothes 603transition occurs precisely at the threshold. The Soft Knee setting smoothes
604the transition from plus or minus three decibels around the threshold. 604the transition from plus or minus three decibels around the threshold.
605 605
606The \setting{Attack Time} setting sets the delay in milliseconds between the
607input signal exceeding the activation threshold and acting upon it.
608
606The \setting{Release Time} setting sets the recovery time after the signal is 609The \setting{Release Time} setting sets the recovery time after the signal is
607compressed. Once the compressor determines that compression is necessary, 610compressed. Once the compressor determines that compression is necessary,
608the input signal is reduced appropriately, but the gain isn't allowed to 611the input signal is reduced appropriately, but the gain isn't allowed to