summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Sevakis <jethead71@rockbox.org>2012-02-08 14:55:37 -0500
committerMichael Sevakis <jethead71@rockbox.org>2012-02-08 14:55:37 -0500
commit1ab9d14c77adc241ff1b126f216dbac8dd34e3fc (patch)
tree98753a48ad4a9ca821df0517a6a12e603bf070e2
parentb0478726e4b197caa8c2e50b2b6681e1aa1decf7 (diff)
downloadrockbox-1ab9d14c77adc241ff1b126f216dbac8dd34e3fc.tar.gz
rockbox-1ab9d14c77adc241ff1b126f216dbac8dd34e3fc.zip
Move to compressor out of dsp.c and into its own source to reduce DSP clutter.
A bit of a rough job for the moment but all works. Change-Id: Id40852e0dec99caee02f943d0da8a1cdc16f022a
-rw-r--r--apps/SOURCES1
-rw-r--r--apps/compressor.c363
-rw-r--r--apps/compressor.h29
-rw-r--r--apps/dsp.c371
-rw-r--r--apps/dsp.h38
5 files changed, 446 insertions, 356 deletions
diff --git a/apps/SOURCES b/apps/SOURCES
index 53a67fd307..e1990217ca 100644
--- a/apps/SOURCES
+++ b/apps/SOURCES
@@ -169,6 +169,7 @@ codec_thread.c
169playback.c 169playback.c
170codecs.c 170codecs.c
171dsp.c 171dsp.c
172compressor.c
172#ifndef HAVE_HARDWARE_BEEP 173#ifndef HAVE_HARDWARE_BEEP
173beep.c 174beep.c
174#endif 175#endif
diff --git a/apps/compressor.c b/apps/compressor.c
new file mode 100644
index 0000000000..3a8d52e4da
--- /dev/null
+++ b/apps/compressor.c
@@ -0,0 +1,363 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2009 Jeffrey Goode
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21#include "config.h"
22#include "fixedpoint.h"
23#include "fracmul.h"
24#include "settings.h"
25#include "dsp.h"
26#include "compressor.h"
27
28/* Define LOGF_ENABLE to enable logf output in this file */
29/*#define LOGF_ENABLE*/
30#include "logf.h"
31
32static int32_t comp_rel_slope IBSS_ATTR; /* S7.24 format */
33static int32_t comp_makeup_gain IBSS_ATTR; /* S7.24 format */
34static int32_t comp_curve[66] IBSS_ATTR; /* S7.24 format */
35static int32_t release_gain IBSS_ATTR; /* S7.24 format */
36
37#define UNITY (1L << 24) /* unity gain in S7.24 format */
38
39/** COMPRESSOR UPDATE
40 * Called via the menu system to configure the compressor process */
41bool compressor_update(void)
42{
43 static int curr_set[5];
44 int new_set[5] = {
45 global_settings.compressor_threshold,
46 global_settings.compressor_makeup_gain,
47 global_settings.compressor_ratio,
48 global_settings.compressor_knee,
49 global_settings.compressor_release_time};
50
51 /* make menu values useful */
52 int threshold = new_set[0];
53 bool auto_gain = (new_set[1] == 1);
54 const int comp_ratios[] = {2, 4, 6, 10, 0};
55 int ratio = comp_ratios[new_set[2]];
56 bool soft_knee = (new_set[3] == 1);
57 int release = new_set[4] * NATIVE_FREQUENCY / 1000;
58
59 bool changed = false;
60 bool active = (threshold < 0);
61
62 for (int i = 0; i < 5; i++)
63 {
64 if (curr_set[i] != new_set[i])
65 {
66 changed = true;
67 curr_set[i] = new_set[i];
68
69#if defined(ROCKBOX_HAS_LOGF) && defined(LOGF_ENABLE)
70 switch (i)
71 {
72 case 0:
73 logf(" Compressor Threshold: %d dB\tEnabled: %s",
74 threshold, active ? "Yes" : "No");
75 break;
76 case 1:
77 logf(" Compressor Makeup Gain: %s",
78 auto_gain ? "Auto" : "Off");
79 break;
80 case 2:
81 if (ratio)
82 { logf(" Compressor Ratio: %d:1", ratio); }
83 else
84 { logf(" Compressor Ratio: Limit"); }
85 break;
86 case 3:
87 logf(" Compressor Knee: %s", soft_knee?"Soft":"Hard");
88 break;
89 case 4:
90 logf(" Compressor Release: %d", release);
91 break;
92 }
93#endif
94 }
95 }
96
97 if (changed && active)
98 {
99 /* configure variables for compressor operation */
100 static const int32_t db[] = {
101 /* positive db equivalents in S15.16 format */
102 0x000000, 0x241FA4, 0x1E1A5E, 0x1A94C8,
103 0x181518, 0x1624EA, 0x148F82, 0x1338BD,
104 0x120FD2, 0x1109EB, 0x101FA4, 0x0F4BB6,
105 0x0E8A3C, 0x0DD840, 0x0D3377, 0x0C9A0E,
106 0x0C0A8C, 0x0B83BE, 0x0B04A5, 0x0A8C6C,
107 0x0A1A5E, 0x09ADE1, 0x094670, 0x08E398,
108 0x0884F6, 0x082A30, 0x07D2FA, 0x077F0F,
109 0x072E31, 0x06E02A, 0x0694C8, 0x064BDF,
110 0x060546, 0x05C0DA, 0x057E78, 0x053E03,
111 0x04FF5F, 0x04C273, 0x048726, 0x044D64,
112 0x041518, 0x03DE30, 0x03A89B, 0x037448,
113 0x03412A, 0x030F32, 0x02DE52, 0x02AE80,
114 0x027FB0, 0x0251D6, 0x0224EA, 0x01F8E2,
115 0x01CDB4, 0x01A359, 0x0179C9, 0x0150FC,
116 0x0128EB, 0x010190, 0x00DAE4, 0x00B4E1,
117 0x008F82, 0x006AC1, 0x004699, 0x002305};
118
119 struct curve_point
120 {
121 int32_t db; /* S15.16 format */
122 int32_t offset; /* S15.16 format */
123 } db_curve[5];
124
125 /** Set up the shape of the compression curve first as decibel
126 values */
127 /* db_curve[0] = bottom of knee
128 [1] = threshold
129 [2] = top of knee
130 [3] = 0 db input
131 [4] = ~+12db input (2 bits clipping overhead) */
132
133 db_curve[1].db = threshold << 16;
134 if (soft_knee)
135 {
136 /* bottom of knee is 3dB below the threshold for soft knee*/
137 db_curve[0].db = db_curve[1].db - (3 << 16);
138 /* top of knee is 3dB above the threshold for soft knee */
139 db_curve[2].db = db_curve[1].db + (3 << 16);
140 if (ratio)
141 /* offset = -3db * (ratio - 1) / ratio */
142 db_curve[2].offset = (int32_t)((long long)(-3 << 16)
143 * (ratio - 1) / ratio);
144 else
145 /* offset = -3db for hard limit */
146 db_curve[2].offset = (-3 << 16);
147 }
148 else
149 {
150 /* bottom of knee is at the threshold for hard knee */
151 db_curve[0].db = threshold << 16;
152 /* top of knee is at the threshold for hard knee */
153 db_curve[2].db = threshold << 16;
154 db_curve[2].offset = 0;
155 }
156
157 /* Calculate 0db and ~+12db offsets */
158 db_curve[4].db = 0xC0A8C; /* db of 2 bits clipping */
159 if (ratio)
160 {
161 /* offset = threshold * (ratio - 1) / ratio */
162 db_curve[3].offset = (int32_t)((long long)(threshold << 16)
163 * (ratio - 1) / ratio);
164 db_curve[4].offset = (int32_t)((long long)-db_curve[4].db
165 * (ratio - 1) / ratio) + db_curve[3].offset;
166 }
167 else
168 {
169 /* offset = threshold for hard limit */
170 db_curve[3].offset = (threshold << 16);
171 db_curve[4].offset = -db_curve[4].db + db_curve[3].offset;
172 }
173
174 /** Now set up the comp_curve table with compression offsets in the
175 form of gain factors in S7.24 format */
176 /* comp_curve[0] is 0 (-infinity db) input */
177 comp_curve[0] = UNITY;
178 /* comp_curve[1 to 63] are intermediate compression values
179 corresponding to the 6 MSB of the input values of a non-clipped
180 signal */
181 for (int i = 1; i < 64; i++)
182 {
183 /* db constants are stored as positive numbers;
184 make them negative here */
185 int32_t this_db = -db[i];
186
187 /* no compression below the knee */
188 if (this_db <= db_curve[0].db)
189 comp_curve[i] = UNITY;
190
191 /* if soft knee and below top of knee,
192 interpolate along soft knee slope */
193 else if (soft_knee && (this_db <= db_curve[2].db))
194 comp_curve[i] = fp_factor(fp_mul(
195 ((this_db - db_curve[0].db) / 6),
196 db_curve[2].offset, 16), 16) << 8;
197
198 /* interpolate along ratio slope above the knee */
199 else
200 comp_curve[i] = fp_factor(fp_mul(
201 fp_div((db_curve[1].db - this_db), db_curve[1].db, 16),
202 db_curve[3].offset, 16), 16) << 8;
203 }
204 /* comp_curve[64] is the compression level of a maximum level,
205 non-clipped signal */
206 comp_curve[64] = fp_factor(db_curve[3].offset, 16) << 8;
207
208 /* comp_curve[65] is the compression level of a maximum level,
209 clipped signal */
210 comp_curve[65] = fp_factor(db_curve[4].offset, 16) << 8;
211
212#if defined(ROCKBOX_HAS_LOGF) && defined(LOGF_ENABLE)
213 logf("\n *** Compression Offsets ***");
214 /* some settings for display only, not used in calculations */
215 db_curve[0].offset = 0;
216 db_curve[1].offset = 0;
217 db_curve[3].db = 0;
218
219 for (int i = 0; i <= 4; i++)
220 {
221 logf("Curve[%d]: db: % 6.2f\toffset: % 6.2f", i,
222 (float)db_curve[i].db / (1 << 16),
223 (float)db_curve[i].offset / (1 << 16));
224 }
225
226 logf("\nGain factors:");
227 for (int i = 1; i <= 65; i++)
228 {
229 debugf("%02d: %.6f ", i, (float)comp_curve[i] / UNITY);
230 if (i % 4 == 0) debugf("\n");
231 }
232 debugf("\n");
233#endif
234
235 /* if using auto peak, then makeup gain is max offset -
236 .1dB headroom */
237 comp_makeup_gain = auto_gain ?
238 fp_factor(-(db_curve[3].offset) - 0x199A, 16) << 8 : UNITY;
239 logf("Makeup gain:\t%.6f", (float)comp_makeup_gain / UNITY);
240
241 /* calculate per-sample gain change a rate of 10db over release time
242 */
243 comp_rel_slope = 0xAF0BB2 / release;
244 logf("Release slope:\t%.6f", (float)comp_rel_slope / UNITY);
245
246 release_gain = UNITY;
247 }
248
249 return active;
250}
251
252/** GET COMPRESSION GAIN
253 * Returns the required gain factor in S7.24 format in order to compress the
254 * sample in accordance with the compression curve. Always 1 or less.
255 */
256static inline int32_t get_compression_gain(struct dsp_data *data,
257 int32_t sample)
258{
259 const int frac_bits_offset = data->frac_bits - 15;
260
261 /* sample must be positive */
262 if (sample < 0)
263 sample = -(sample + 1);
264
265 /* shift sample into 15 frac bit range */
266 if (frac_bits_offset > 0)
267 sample >>= frac_bits_offset;
268 if (frac_bits_offset < 0)
269 sample <<= -frac_bits_offset;
270
271 /* normal case: sample isn't clipped */
272 if (sample < (1 << 15))
273 {
274 /* index is 6 MSB, rem is 9 LSB */
275 int index = sample >> 9;
276 int32_t rem = (sample & 0x1FF) << 22;
277
278 /* interpolate from the compression curve:
279 higher gain - ((rem / (1 << 31)) * (higher gain - lower gain)) */
280 return comp_curve[index] - (FRACMUL(rem,
281 (comp_curve[index] - comp_curve[index + 1])));
282 }
283 /* sample is somewhat clipped, up to 2 bits of overhead */
284 if (sample < (1 << 17))
285 {
286 /* straight interpolation:
287 higher gain - ((clipped portion of sample * 4/3
288 / (1 << 31)) * (higher gain - lower gain)) */
289 return comp_curve[64] - (FRACMUL(((sample - (1 << 15)) / 3) << 16,
290 (comp_curve[64] - comp_curve[65])));
291 }
292
293 /* sample is too clipped, return invalid value */
294 return -1;
295}
296
297/** COMPRESSOR PROCESS
298 * Changes the gain of the samples according to the compressor curve
299 */
300void compressor_process(int count, struct dsp_data *data, int32_t *buf[])
301{
302 const int num_chan = data->num_channels;
303 int32_t *in_buf[2] = {buf[0], buf[1]};
304
305 while (count-- > 0)
306 {
307 int ch;
308 /* use lowest (most compressed) gain factor of the output buffer
309 sample pair for both samples (mono is also handled correctly here)
310 */
311 int32_t sample_gain = UNITY;
312 for (ch = 0; ch < num_chan; ch++)
313 {
314 int32_t this_gain = get_compression_gain(data, *in_buf[ch]);
315 if (this_gain < sample_gain)
316 sample_gain = this_gain;
317 }
318
319 /* perform release slope; skip if no compression and no release slope
320 */
321 if ((sample_gain != UNITY) || (release_gain != UNITY))
322 {
323 /* if larger offset than previous slope, start new release slope
324 */
325 if ((sample_gain <= release_gain) && (sample_gain > 0))
326 {
327 release_gain = sample_gain;
328 }
329 else
330 /* keep sloping towards unity gain (and ignore invalid value) */
331 {
332 release_gain += comp_rel_slope;
333 if (release_gain > UNITY)
334 {
335 release_gain = UNITY;
336 }
337 }
338 }
339
340 /* total gain factor is the product of release gain and makeup gain,
341 but avoid computation if possible */
342 int32_t total_gain = ((release_gain == UNITY) ? comp_makeup_gain :
343 (comp_makeup_gain == UNITY) ? release_gain :
344 FRACMUL_SHL(release_gain, comp_makeup_gain, 7));
345
346 /* Implement the compressor: apply total gain factor (if any) to the
347 output buffer sample pair/mono sample */
348 if (total_gain != UNITY)
349 {
350 for (ch = 0; ch < num_chan; ch++)
351 {
352 *in_buf[ch] = FRACMUL_SHL(total_gain, *in_buf[ch], 7);
353 }
354 }
355 in_buf[0]++;
356 in_buf[1]++;
357 }
358}
359
360void compressor_reset(void)
361{
362 release_gain = UNITY;
363}
diff --git a/apps/compressor.h b/apps/compressor.h
new file mode 100644
index 0000000000..6154372e05
--- /dev/null
+++ b/apps/compressor.h
@@ -0,0 +1,29 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2009 Jeffrey Goode
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#ifndef COMPRESSOR_H
23#define COMPRESSOR_H
24
25void compressor_process(int count, struct dsp_data *data, int32_t *buf[]);
26bool compressor_update(void);
27void compressor_reset(void);
28
29#endif /* COMPRESSOR_H */
diff --git a/apps/dsp.c b/apps/dsp.c
index 00de511dd0..4017f6afc0 100644
--- a/apps/dsp.c
+++ b/apps/dsp.c
@@ -24,6 +24,7 @@
24#include "dsp.h" 24#include "dsp.h"
25#include "dsp-util.h" 25#include "dsp-util.h"
26#include "eq.h" 26#include "eq.h"
27#include "compressor.h"
27#include "kernel.h" 28#include "kernel.h"
28#include "settings.h" 29#include "settings.h"
29#include "replaygain.h" 30#include "replaygain.h"
@@ -66,42 +67,6 @@ enum
66 SAMPLE_OUTPUT_DITHERED_STEREO 67 SAMPLE_OUTPUT_DITHERED_STEREO
67}; 68};
68 69
69/****************************************************************************
70 * NOTE: Any assembly routines that use these structures must be updated
71 * if current data members are moved or changed.
72 */
73struct resample_data
74{
75 uint32_t delta; /* 00h */
76 uint32_t phase; /* 04h */
77 int32_t last_sample[2]; /* 08h */
78 /* 10h */
79};
80
81/* This is for passing needed data to assembly dsp routines. If another
82 * dsp parameter needs to be passed, add to the end of the structure
83 * and remove from dsp_config.
84 * If another function type becomes assembly optimized and requires dsp
85 * config info, add a pointer paramter of type "struct dsp_data *".
86 * If removing something from other than the end, reserve the spot or
87 * else update every implementation for every target.
88 * Be sure to add the offset of the new member for easy viewing as well. :)
89 * It is the first member of dsp_config and all members can be accessesed
90 * through the main aggregate but this is intended to make a safe haven
91 * for these items whereas the c part can be rearranged at will. dsp_data
92 * could even moved within dsp_config without disurbing the order.
93 */
94struct dsp_data
95{
96 int output_scale; /* 00h */
97 int num_channels; /* 04h */
98 struct resample_data resample_data; /* 08h */
99 int32_t clip_min; /* 18h */
100 int32_t clip_max; /* 1ch */
101 int32_t gain; /* 20h - Note that this is in S8.23 format. */
102 /* 24h */
103};
104
105/* No asm...yet */ 70/* No asm...yet */
106struct dither_data 71struct dither_data
107{ 72{
@@ -154,7 +119,7 @@ typedef void (*channels_process_dsp_fn_type)(int count, struct dsp_data *data,
154 119
155struct dsp_config 120struct dsp_config
156{ 121{
157 struct dsp_data data; /* Config members for use in asm routines */ 122 struct dsp_data data; /* Config members for use in external routines */
158 long codec_frequency; /* Sample rate of data coming from the codec */ 123 long codec_frequency; /* Sample rate of data coming from the codec */
159 long frequency; /* Effective sample rate after pitch shift (if any) */ 124 long frequency; /* Effective sample rate after pitch shift (if any) */
160 int sample_depth; 125 int sample_depth;
@@ -164,7 +129,6 @@ struct dsp_config
164#ifdef HAVE_PITCHSCREEN 129#ifdef HAVE_PITCHSCREEN
165 bool tdspeed_active; /* Timestretch is in use */ 130 bool tdspeed_active; /* Timestretch is in use */
166#endif 131#endif
167 int frac_bits;
168#ifdef HAVE_SW_TONE_CONTROLS 132#ifdef HAVE_SW_TONE_CONTROLS
169 /* Filter struct for software bass/treble controls */ 133 /* Filter struct for software bass/treble controls */
170 struct eqfilter tone_filter; 134 struct eqfilter tone_filter;
@@ -180,7 +144,7 @@ struct dsp_config
180 channels_process_fn_type apply_crossfeed; 144 channels_process_fn_type apply_crossfeed;
181 channels_process_fn_type eq_process; 145 channels_process_fn_type eq_process;
182 channels_process_fn_type channels_process; 146 channels_process_fn_type channels_process;
183 channels_process_fn_type compressor_process; 147 channels_process_dsp_fn_type compressor_process;
184}; 148};
185 149
186/* General DSP config */ 150/* General DSP config */
@@ -249,15 +213,6 @@ static int32_t *sample_buf[2] = { small_sample_buf[0], small_sample_buf[1] };
249static int resample_buf_count = SMALL_RESAMPLE_BUF_COUNT; 213static int resample_buf_count = SMALL_RESAMPLE_BUF_COUNT;
250static int32_t *resample_buf[2] = { small_resample_buf[0], small_resample_buf[1] }; 214static int32_t *resample_buf[2] = { small_resample_buf[0], small_resample_buf[1] };
251 215
252/* compressor */
253static int32_t comp_rel_slope IBSS_ATTR; /* S7.24 format */
254static int32_t comp_makeup_gain IBSS_ATTR; /* S7.24 format */
255static int32_t comp_curve[66] IBSS_ATTR; /* S7.24 format */
256static int32_t release_gain IBSS_ATTR; /* S7.24 format */
257#define UNITY (1L << 24) /* unity gain in S7.24 format */
258static void compressor_process(int count, int32_t *buf[]);
259
260
261#ifdef HAVE_PITCHSCREEN 216#ifdef HAVE_PITCHSCREEN
262int32_t sound_get_pitch(void) 217int32_t sound_get_pitch(void)
263{ 218{
@@ -813,8 +768,8 @@ static inline int resample(struct dsp_config *dsp, int count, int32_t *src[])
813static void dither_init(struct dsp_config *dsp) 768static void dither_init(struct dsp_config *dsp)
814{ 769{
815 memset(dither_data, 0, sizeof (dither_data)); 770 memset(dither_data, 0, sizeof (dither_data));
816 dither_bias = (1L << (dsp->frac_bits - NATIVE_DEPTH)); 771 dither_bias = (1L << (dsp->data.frac_bits - NATIVE_DEPTH));
817 dither_mask = (1L << (dsp->frac_bits + 1 - NATIVE_DEPTH)) - 1; 772 dither_mask = (1L << (dsp->data.frac_bits + 1 - NATIVE_DEPTH)) - 1;
818} 773}
819 774
820void dsp_dither_enable(bool enable) 775void dsp_dither_enable(bool enable)
@@ -1319,7 +1274,7 @@ int dsp_process(struct dsp_config *dsp, char *dst, const char *src[], int count)
1319 dsp->channels_process(chunk, t2); 1274 dsp->channels_process(chunk, t2);
1320 1275
1321 if (dsp->compressor_process) 1276 if (dsp->compressor_process)
1322 dsp->compressor_process(chunk, t2); 1277 dsp->compressor_process(chunk, &dsp->data, t2);
1323 1278
1324 dsp->output_samples(chunk, &dsp->data, (const int32_t **)t2, (int16_t *)dst); 1279 dsp->output_samples(chunk, &dsp->data, (const int32_t **)t2, (int16_t *)dst);
1325 1280
@@ -1453,20 +1408,20 @@ intptr_t dsp_configure(struct dsp_config *dsp, int setting, intptr_t value)
1453 1408
1454 if (dsp->sample_depth <= NATIVE_DEPTH) 1409 if (dsp->sample_depth <= NATIVE_DEPTH)
1455 { 1410 {
1456 dsp->frac_bits = WORD_FRACBITS; 1411 dsp->data.frac_bits = WORD_FRACBITS;
1457 dsp->sample_bytes = sizeof (int16_t); /* samples are 16 bits */ 1412 dsp->sample_bytes = sizeof (int16_t); /* samples are 16 bits */
1458 dsp->data.clip_max = ((1 << WORD_FRACBITS) - 1); 1413 dsp->data.clip_max = ((1 << WORD_FRACBITS) - 1);
1459 dsp->data.clip_min = -((1 << WORD_FRACBITS)); 1414 dsp->data.clip_min = -((1 << WORD_FRACBITS));
1460 } 1415 }
1461 else 1416 else
1462 { 1417 {
1463 dsp->frac_bits = value; 1418 dsp->data.frac_bits = value;
1464 dsp->sample_bytes = sizeof (int32_t); /* samples are 32 bits */ 1419 dsp->sample_bytes = sizeof (int32_t); /* samples are 32 bits */
1465 dsp->data.clip_max = (1 << value) - 1; 1420 dsp->data.clip_max = (1 << value) - 1;
1466 dsp->data.clip_min = -(1 << value); 1421 dsp->data.clip_min = -(1 << value);
1467 } 1422 }
1468 1423
1469 dsp->data.output_scale = dsp->frac_bits + 1 - NATIVE_DEPTH; 1424 dsp->data.output_scale = dsp->data.frac_bits + 1 - NATIVE_DEPTH;
1470 sample_input_new_format(dsp); 1425 sample_input_new_format(dsp);
1471 dither_init(dsp); 1426 dither_init(dsp);
1472 break; 1427 break;
@@ -1484,9 +1439,9 @@ intptr_t dsp_configure(struct dsp_config *dsp, int setting, intptr_t value)
1484 dsp->stereo_mode = STEREO_NONINTERLEAVED; 1439 dsp->stereo_mode = STEREO_NONINTERLEAVED;
1485 dsp->data.num_channels = 2; 1440 dsp->data.num_channels = 2;
1486 dsp->sample_depth = NATIVE_DEPTH; 1441 dsp->sample_depth = NATIVE_DEPTH;
1487 dsp->frac_bits = WORD_FRACBITS; 1442 dsp->data.frac_bits = WORD_FRACBITS;
1488 dsp->sample_bytes = sizeof (int16_t); 1443 dsp->sample_bytes = sizeof (int16_t);
1489 dsp->data.output_scale = dsp->frac_bits + 1 - NATIVE_DEPTH; 1444 dsp->data.output_scale = dsp->data.frac_bits + 1 - NATIVE_DEPTH;
1490 dsp->data.clip_max = ((1 << WORD_FRACBITS) - 1); 1445 dsp->data.clip_max = ((1 << WORD_FRACBITS) - 1);
1491 dsp->data.clip_min = -((1 << WORD_FRACBITS)); 1446 dsp->data.clip_min = -((1 << WORD_FRACBITS));
1492 dsp->codec_frequency = dsp->frequency = NATIVE_FREQUENCY; 1447 dsp->codec_frequency = dsp->frequency = NATIVE_FREQUENCY;
@@ -1506,7 +1461,7 @@ intptr_t dsp_configure(struct dsp_config *dsp, int setting, intptr_t value)
1506 tdspeed_setup(dsp); 1461 tdspeed_setup(dsp);
1507#endif 1462#endif
1508 if (dsp == &AUDIO_DSP) 1463 if (dsp == &AUDIO_DSP)
1509 release_gain = UNITY; 1464 compressor_reset();
1510 break; 1465 break;
1511 1466
1512 case DSP_FLUSH: 1467 case DSP_FLUSH:
@@ -1518,7 +1473,7 @@ intptr_t dsp_configure(struct dsp_config *dsp, int setting, intptr_t value)
1518 tdspeed_setup(dsp); 1473 tdspeed_setup(dsp);
1519#endif 1474#endif
1520 if (dsp == &AUDIO_DSP) 1475 if (dsp == &AUDIO_DSP)
1521 release_gain = UNITY; 1476 compressor_reset();
1522 break; 1477 break;
1523 1478
1524 case DSP_SET_TRACK_GAIN: 1479 case DSP_SET_TRACK_GAIN:
@@ -1616,303 +1571,7 @@ void dsp_set_replaygain(void)
1616 * Called by the menu system to configure the compressor process */ 1571 * Called by the menu system to configure the compressor process */
1617void dsp_set_compressor(void) 1572void dsp_set_compressor(void)
1618{ 1573{
1619 static int curr_set[5];
1620 int new_set[5] = {
1621 global_settings.compressor_threshold,
1622 global_settings.compressor_makeup_gain,
1623 global_settings.compressor_ratio,
1624 global_settings.compressor_knee,
1625 global_settings.compressor_release_time};
1626
1627 /* make menu values useful */
1628 int threshold = new_set[0];
1629 bool auto_gain = (new_set[1] == 1);
1630 const int comp_ratios[] = {2, 4, 6, 10, 0};
1631 int ratio = comp_ratios[new_set[2]];
1632 bool soft_knee = (new_set[3] == 1);
1633 int release = new_set[4] * NATIVE_FREQUENCY / 1000;
1634
1635 bool changed = false;
1636 bool active = (threshold < 0);
1637
1638 for (int i = 0; i < 5; i++)
1639 {
1640 if (curr_set[i] != new_set[i])
1641 {
1642 changed = true;
1643 curr_set[i] = new_set[i];
1644
1645#if defined(ROCKBOX_HAS_LOGF) && defined(LOGF_ENABLE)
1646 switch (i)
1647 {
1648 case 0:
1649 logf(" Compressor Threshold: %d dB\tEnabled: %s",
1650 threshold, active ? "Yes" : "No");
1651 break;
1652 case 1:
1653 logf(" Compressor Makeup Gain: %s",
1654 auto_gain ? "Auto" : "Off");
1655 break;
1656 case 2:
1657 if (ratio)
1658 { logf(" Compressor Ratio: %d:1", ratio); }
1659 else
1660 { logf(" Compressor Ratio: Limit"); }
1661 break;
1662 case 3:
1663 logf(" Compressor Knee: %s", soft_knee?"Soft":"Hard");
1664 break;
1665 case 4:
1666 logf(" Compressor Release: %d", release);
1667 break;
1668 }
1669#endif
1670 }
1671 }
1672
1673 if (changed && active)
1674 {
1675 /* configure variables for compressor operation */
1676 const int32_t db[] ={0x000000, /* positive db equivalents in S15.16 format */
1677 0x241FA4, 0x1E1A5E, 0x1A94C8, 0x181518, 0x1624EA, 0x148F82, 0x1338BD, 0x120FD2,
1678 0x1109EB, 0x101FA4, 0x0F4BB6, 0x0E8A3C, 0x0DD840, 0x0D3377, 0x0C9A0E, 0x0C0A8C,
1679 0x0B83BE, 0x0B04A5, 0x0A8C6C, 0x0A1A5E, 0x09ADE1, 0x094670, 0x08E398, 0x0884F6,
1680 0x082A30, 0x07D2FA, 0x077F0F, 0x072E31, 0x06E02A, 0x0694C8, 0x064BDF, 0x060546,
1681 0x05C0DA, 0x057E78, 0x053E03, 0x04FF5F, 0x04C273, 0x048726, 0x044D64, 0x041518,
1682 0x03DE30, 0x03A89B, 0x037448, 0x03412A, 0x030F32, 0x02DE52, 0x02AE80, 0x027FB0,
1683 0x0251D6, 0x0224EA, 0x01F8E2, 0x01CDB4, 0x01A359, 0x0179C9, 0x0150FC, 0x0128EB,
1684 0x010190, 0x00DAE4, 0x00B4E1, 0x008F82, 0x006AC1, 0x004699, 0x002305};
1685
1686 struct curve_point
1687 {
1688 int32_t db; /* S15.16 format */
1689 int32_t offset; /* S15.16 format */
1690 } db_curve[5];
1691
1692 /** Set up the shape of the compression curve first as decibel values*/
1693 /* db_curve[0] = bottom of knee
1694 [1] = threshold
1695 [2] = top of knee
1696 [3] = 0 db input
1697 [4] = ~+12db input (2 bits clipping overhead) */
1698
1699 db_curve[1].db = threshold << 16;
1700 if (soft_knee)
1701 {
1702 /* bottom of knee is 3dB below the threshold for soft knee*/
1703 db_curve[0].db = db_curve[1].db - (3 << 16);
1704 /* top of knee is 3dB above the threshold for soft knee */
1705 db_curve[2].db = db_curve[1].db + (3 << 16);
1706 if (ratio)
1707 /* offset = -3db * (ratio - 1) / ratio */
1708 db_curve[2].offset = (int32_t)((long long)(-3 << 16)
1709 * (ratio - 1) / ratio);
1710 else
1711 /* offset = -3db for hard limit */
1712 db_curve[2].offset = (-3 << 16);
1713 }
1714 else
1715 {
1716 /* bottom of knee is at the threshold for hard knee */
1717 db_curve[0].db = threshold << 16;
1718 /* top of knee is at the threshold for hard knee */
1719 db_curve[2].db = threshold << 16;
1720 db_curve[2].offset = 0;
1721 }
1722
1723 /* Calculate 0db and ~+12db offsets */
1724 db_curve[4].db = 0xC0A8C; /* db of 2 bits clipping */
1725 if (ratio)
1726 {
1727 /* offset = threshold * (ratio - 1) / ratio */
1728 db_curve[3].offset = (int32_t)((long long)(threshold << 16)
1729 * (ratio - 1) / ratio);
1730 db_curve[4].offset = (int32_t)((long long)-db_curve[4].db
1731 * (ratio - 1) / ratio) + db_curve[3].offset;
1732 }
1733 else
1734 {
1735 /* offset = threshold for hard limit */
1736 db_curve[3].offset = (threshold << 16);
1737 db_curve[4].offset = -db_curve[4].db + db_curve[3].offset;
1738 }
1739
1740 /** Now set up the comp_curve table with compression offsets in the form
1741 of gain factors in S7.24 format */
1742 /* comp_curve[0] is 0 (-infinity db) input */
1743 comp_curve[0] = UNITY;
1744 /* comp_curve[1 to 63] are intermediate compression values corresponding
1745 to the 6 MSB of the input values of a non-clipped signal */
1746 for (int i = 1; i < 64; i++)
1747 {
1748 /* db constants are stored as positive numbers;
1749 make them negative here */
1750 int32_t this_db = -db[i];
1751
1752 /* no compression below the knee */
1753 if (this_db <= db_curve[0].db)
1754 comp_curve[i] = UNITY;
1755
1756 /* if soft knee and below top of knee,
1757 interpolate along soft knee slope */
1758 else if (soft_knee && (this_db <= db_curve[2].db))
1759 comp_curve[i] = fp_factor(fp_mul(
1760 ((this_db - db_curve[0].db) / 6),
1761 db_curve[2].offset, 16), 16) << 8;
1762
1763 /* interpolate along ratio slope above the knee */
1764 else
1765 comp_curve[i] = fp_factor(fp_mul(
1766 fp_div((db_curve[1].db - this_db), db_curve[1].db, 16),
1767 db_curve[3].offset, 16), 16) << 8;
1768 }
1769 /* comp_curve[64] is the compression level of a maximum level,
1770 non-clipped signal */
1771 comp_curve[64] = fp_factor(db_curve[3].offset, 16) << 8;
1772
1773 /* comp_curve[65] is the compression level of a maximum level,
1774 clipped signal */
1775 comp_curve[65] = fp_factor(db_curve[4].offset, 16) << 8;
1776
1777#if defined(ROCKBOX_HAS_LOGF) && defined(LOGF_ENABLE)
1778 logf("\n *** Compression Offsets ***");
1779 /* some settings for display only, not used in calculations */
1780 db_curve[0].offset = 0;
1781 db_curve[1].offset = 0;
1782 db_curve[3].db = 0;
1783
1784 for (int i = 0; i <= 4; i++)
1785 {
1786 logf("Curve[%d]: db: % 6.2f\toffset: % 6.2f", i,
1787 (float)db_curve[i].db / (1 << 16),
1788 (float)db_curve[i].offset / (1 << 16));
1789 }
1790
1791 logf("\nGain factors:");
1792 for (int i = 1; i <= 65; i++)
1793 {
1794 debugf("%02d: %.6f ", i, (float)comp_curve[i] / UNITY);
1795 if (i % 4 == 0) debugf("\n");
1796 }
1797 debugf("\n");
1798#endif
1799
1800 /* if using auto peak, then makeup gain is max offset - .1dB headroom */
1801 comp_makeup_gain = auto_gain ?
1802 fp_factor(-(db_curve[3].offset) - 0x199A, 16) << 8 : UNITY;
1803 logf("Makeup gain:\t%.6f", (float)comp_makeup_gain / UNITY);
1804
1805 /* calculate per-sample gain change a rate of 10db over release time */
1806 comp_rel_slope = 0xAF0BB2 / release;
1807 logf("Release slope:\t%.6f", (float)comp_rel_slope / UNITY);
1808
1809 release_gain = UNITY;
1810 }
1811
1812 /* enable/disable the compressor */ 1574 /* enable/disable the compressor */
1813 AUDIO_DSP.compressor_process = active ? compressor_process : NULL; 1575 AUDIO_DSP.compressor_process = compressor_update() ?
1814} 1576 compressor_process : NULL;
1815
1816/** GET COMPRESSION GAIN
1817 * Returns the required gain factor in S7.24 format in order to compress the
1818 * sample in accordance with the compression curve. Always 1 or less.
1819 */
1820static inline int32_t get_compression_gain(int32_t sample)
1821{
1822 const int frac_bits_offset = AUDIO_DSP.frac_bits - 15;
1823
1824 /* sample must be positive */
1825 if (sample < 0)
1826 sample = -(sample + 1);
1827
1828 /* shift sample into 15 frac bit range */
1829 if (frac_bits_offset > 0)
1830 sample >>= frac_bits_offset;
1831 if (frac_bits_offset < 0)
1832 sample <<= -frac_bits_offset;
1833
1834 /* normal case: sample isn't clipped */
1835 if (sample < (1 << 15))
1836 {
1837 /* index is 6 MSB, rem is 9 LSB */
1838 int index = sample >> 9;
1839 int32_t rem = (sample & 0x1FF) << 22;
1840
1841 /* interpolate from the compression curve:
1842 higher gain - ((rem / (1 << 31)) * (higher gain - lower gain)) */
1843 return comp_curve[index] - (FRACMUL(rem,
1844 (comp_curve[index] - comp_curve[index + 1])));
1845 }
1846 /* sample is somewhat clipped, up to 2 bits of overhead */
1847 if (sample < (1 << 17))
1848 {
1849 /* straight interpolation:
1850 higher gain - ((clipped portion of sample * 4/3
1851 / (1 << 31)) * (higher gain - lower gain)) */
1852 return comp_curve[64] - (FRACMUL(((sample - (1 << 15)) / 3) << 16,
1853 (comp_curve[64] - comp_curve[65])));
1854 }
1855
1856 /* sample is too clipped, return invalid value */
1857 return -1;
1858}
1859
1860/** COMPRESSOR PROCESS
1861 * Changes the gain of the samples according to the compressor curve
1862 */
1863static void compressor_process(int count, int32_t *buf[])
1864{
1865 const int num_chan = AUDIO_DSP.data.num_channels;
1866 int32_t *in_buf[2] = {buf[0], buf[1]};
1867
1868 while (count-- > 0)
1869 {
1870 int ch;
1871 /* use lowest (most compressed) gain factor of the output buffer
1872 sample pair for both samples (mono is also handled correctly here) */
1873 int32_t sample_gain = UNITY;
1874 for (ch = 0; ch < num_chan; ch++)
1875 {
1876 int32_t this_gain = get_compression_gain(*in_buf[ch]);
1877 if (this_gain < sample_gain)
1878 sample_gain = this_gain;
1879 }
1880
1881 /* perform release slope; skip if no compression and no release slope */
1882 if ((sample_gain != UNITY) || (release_gain != UNITY))
1883 {
1884 /* if larger offset than previous slope, start new release slope */
1885 if ((sample_gain <= release_gain) && (sample_gain > 0))
1886 {
1887 release_gain = sample_gain;
1888 }
1889 else
1890 /* keep sloping towards unity gain (and ignore invalid value) */
1891 {
1892 release_gain += comp_rel_slope;
1893 if (release_gain > UNITY)
1894 {
1895 release_gain = UNITY;
1896 }
1897 }
1898 }
1899
1900 /* total gain factor is the product of release gain and makeup gain,
1901 but avoid computation if possible */
1902 int32_t total_gain = ((release_gain == UNITY) ? comp_makeup_gain :
1903 (comp_makeup_gain == UNITY) ? release_gain :
1904 FRACMUL_SHL(release_gain, comp_makeup_gain, 7));
1905
1906 /* Implement the compressor: apply total gain factor (if any) to the
1907 output buffer sample pair/mono sample */
1908 if (total_gain != UNITY)
1909 {
1910 for (ch = 0; ch < num_chan; ch++)
1911 {
1912 *in_buf[ch] = FRACMUL_SHL(total_gain, *in_buf[ch], 7);
1913 }
1914 }
1915 in_buf[0]++;
1916 in_buf[1]++;
1917 }
1918} 1577}
diff --git a/apps/dsp.h b/apps/dsp.h
index c42e712a5a..2a00f649f8 100644
--- a/apps/dsp.h
+++ b/apps/dsp.h
@@ -57,6 +57,44 @@ enum
57 DSP_CROSSFEED 57 DSP_CROSSFEED
58}; 58};
59 59
60
61/****************************************************************************
62 * NOTE: Any assembly routines that use these structures must be updated
63 * if current data members are moved or changed.
64 */
65struct resample_data
66{
67 uint32_t delta; /* 00h */
68 uint32_t phase; /* 04h */
69 int32_t last_sample[2]; /* 08h */
70 /* 10h */
71};
72
73/* This is for passing needed data to external dsp routines. If another
74 * dsp parameter needs to be passed, add to the end of the structure
75 * and remove from dsp_config.
76 * If another function type becomes assembly/external and requires dsp
77 * config info, add a pointer paramter of type "struct dsp_data *".
78 * If removing something from other than the end, reserve the spot or
79 * else update every implementation for every target.
80 * Be sure to add the offset of the new member for easy viewing as well. :)
81 * It is the first member of dsp_config and all members can be accessesed
82 * through the main aggregate but this is intended to make a safe haven
83 * for these items whereas the c part can be rearranged at will. dsp_data
84 * could even moved within dsp_config without disurbing the order.
85 */
86struct dsp_data
87{
88 int output_scale; /* 00h */
89 int num_channels; /* 04h */
90 struct resample_data resample_data; /* 08h */
91 int32_t clip_min; /* 18h */
92 int32_t clip_max; /* 1ch */
93 int32_t gain; /* 20h - Note that this is in S8.23 format. */
94 int frac_bits; /* 24h */
95 /* 28h */
96};
97
60struct dsp_config; 98struct dsp_config;
61 99
62int dsp_process(struct dsp_config *dsp, char *dest, 100int dsp_process(struct dsp_config *dsp, char *dest,