diff options
author | Ryan Billing <ryjobil@gmail.com> | 2013-10-04 01:57:00 +1300 |
---|---|---|
committer | Michael Giacomelli <giac2000@hotmail.com> | 2013-12-15 22:24:08 +0100 |
commit | d0918b98fa0cfba21208a4fb5ed153687b8f02c3 (patch) | |
tree | 006ef2bb902dfd83101fbfa3fe63b07f47d9e8e6 /lib/rbcodec | |
parent | 5b5f0755d6d7fd9e3fdfdb479caeb7fafd0a9960 (diff) | |
download | rockbox-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>
Diffstat (limited to 'lib/rbcodec')
-rw-r--r-- | lib/rbcodec/dsp/compressor.c | 379 | ||||
-rw-r--r-- | lib/rbcodec/dsp/compressor.h | 1 |
2 files changed, 292 insertions, 88 deletions
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 | |||
33 | static struct compressor_settings curr_set; /* Cached settings */ | 43 | static struct compressor_settings curr_set; /* Cached settings */ |
34 | 44 | ||
35 | static int32_t comp_rel_slope IBSS_ATTR; /* S7.24 format */ | 45 | static int32_t comp_makeup_gain IBSS_ATTR; /* S7.24 format */ |
36 | static int32_t comp_makeup_gain IBSS_ATTR; /* S7.24 format */ | 46 | static int32_t comp_curve[66] IBSS_ATTR; /* S7.24 format */ |
37 | static int32_t comp_curve[66] IBSS_ATTR; /* S7.24 format */ | 47 | static int32_t release_gain IBSS_ATTR; /* S7.24 format */ |
38 | static int32_t release_gain IBSS_ATTR; /* S7.24 format */ | 48 | static int32_t release_holdoff IBSS_ATTR; /* S7.24 format */ |
49 | |||
50 | /* 1-pole filter coefficients for exponential attack/release times */ | ||
51 | static int32_t rlsca IBSS_ATTR; /* Release 'alpha' */ | ||
52 | static int32_t rlscb IBSS_ATTR; /* Release 'beta' */ | ||
53 | |||
54 | static int32_t attca IBSS_ATTR; /* Attack 'alpha' */ | ||
55 | static int32_t attcb IBSS_ATTR; /* Attack 'beta' */ | ||
56 | |||
57 | static int32_t limitca IBSS_ATTR; /* Limiter Attack 'alpha' */ | ||
58 | |||
59 | /* 1-pole filter coefficients for sidechain pre-emphasis filters */ | ||
60 | static int32_t hp1ca IBSS_ATTR; /* hpf1 'alpha' */ | ||
61 | static int32_t hp2ca IBSS_ATTR; /* hpf2 'beta' */ | ||
62 | |||
63 | /* 1-pole hp filter state variables for pre-emphasis filters */ | ||
64 | static int32_t hpfx1 IBSS_ATTR; /* hpf1 and hpf2 x[n-1] */ | ||
65 | static int32_t hp1y1 IBSS_ATTR; /* hpf2 y[n-1] */ | ||
66 | static int32_t hp2y1 IBSS_ATTR; /* hpf2 y[n-1] */ | ||
67 | |||
68 | /* Delay Line for look-ahead compression */ | ||
69 | static int32_t labuf[MAX_CH][MAX_DLY]; /* look-ahead buffer */ | ||
70 | static int32_t delay_time; | ||
71 | static int32_t delay_write; | ||
72 | static 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 | |||
96 | int32_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 | |||
112 | int32_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 | */ | ||
44 | static bool compressor_update(struct dsp_config *dsp, | 123 | static 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 | ||
33 | void dsp_set_compressor(const struct compressor_settings *settings); | 34 | void dsp_set_compressor(const struct compressor_settings *settings); |