summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeffrey Goode <jeffg7@gmail.com>2009-09-25 15:46:38 +0000
committerJeffrey Goode <jeffg7@gmail.com>2009-09-25 15:46:38 +0000
commitcf19ba5599b1cba212705ddb22166acf25eca83c (patch)
tree99e66ab4a5526ec7c36521c19179d96e42d7c3ae
parentb9a17dd0ceb1edc3e8d08522f82f9dfeabe53a53 (diff)
downloadrockbox-cf19ba5599b1cba212705ddb22166acf25eca83c.tar.gz
rockbox-cf19ba5599b1cba212705ddb22166acf25eca83c.zip
Replace limiter with dynamic range compressor
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@22832 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r--apps/dsp.c638
-rw-r--r--apps/dsp.h7
-rw-r--r--apps/fixedpoint.h2
-rw-r--r--apps/lang/dansk.lang6
-rw-r--r--apps/lang/deutsch.lang6
-rw-r--r--apps/lang/english.lang227
-rw-r--r--apps/lang/francais.lang6
-rw-r--r--apps/lang/italiano.lang6
-rw-r--r--apps/lang/polski.lang6
-rw-r--r--apps/lang/svenska.lang6
-rw-r--r--apps/lang/tagalog.lang6
-rw-r--r--apps/lang/walon.lang8
-rw-r--r--apps/menus/sound_menu.c19
-rw-r--r--apps/pcmbuf.c24
-rw-r--r--apps/pcmbuf.h1
-rw-r--r--apps/plugin.c1
-rw-r--r--apps/plugin.h5
-rw-r--r--apps/plugins/test_codec.c24
-rw-r--r--apps/settings.c6
-rw-r--r--apps/settings.h6
-rw-r--r--apps/settings_list.c56
21 files changed, 568 insertions, 498 deletions
diff --git a/apps/dsp.c b/apps/dsp.c
index e7a6a9182a..a5ceecb048 100644
--- a/apps/dsp.c
+++ b/apps/dsp.c
@@ -137,6 +137,15 @@ struct eq_state
137 /* 10ch */ 137 /* 10ch */
138}; 138};
139 139
140struct compressor_menu
141{
142 int threshold; /* dB - from menu */
143 int ratio; /* from menu */
144 int gain; /* dB - from menu */
145 bool soft_knee; /* 0 = hard knee, 1 = soft knee */
146 int release; /* samples - from menu */
147};
148
140/* Include header with defines which functions are implemented in assembly 149/* Include header with defines which functions are implemented in assembly
141 code for the target */ 150 code for the target */
142#include <dsp_asm.h> 151#include <dsp_asm.h>
@@ -171,7 +180,6 @@ struct dsp_config
171 int32_t tdspeed_percent; /* Speed% * PITCH_SPEED_PRECISION */ 180 int32_t tdspeed_percent; /* Speed% * PITCH_SPEED_PRECISION */
172 bool tdspeed_active; /* Timestretch is in use */ 181 bool tdspeed_active; /* Timestretch is in use */
173 int frac_bits; 182 int frac_bits;
174 long limiter_preamp; /* limiter amp gain in S7.24 format */
175#ifdef HAVE_SW_TONE_CONTROLS 183#ifdef HAVE_SW_TONE_CONTROLS
176 /* Filter struct for software bass/treble controls */ 184 /* Filter struct for software bass/treble controls */
177 struct eqfilter tone_filter; 185 struct eqfilter tone_filter;
@@ -187,7 +195,7 @@ struct dsp_config
187 channels_process_fn_type apply_crossfeed; 195 channels_process_fn_type apply_crossfeed;
188 channels_process_fn_type eq_process; 196 channels_process_fn_type eq_process;
189 channels_process_fn_type channels_process; 197 channels_process_fn_type channels_process;
190 return_fn_type limiter_process; 198 return_fn_type compressor_process;
191}; 199};
192 200
193/* General DSP config */ 201/* General DSP config */
@@ -253,58 +261,17 @@ static int32_t *resample_buf;
253#define RESAMPLE_BUF_LEFT_CHANNEL 0 261#define RESAMPLE_BUF_LEFT_CHANNEL 0
254#define RESAMPLE_BUF_RIGHT_CHANNEL (sample_buf_count/2 * RESAMPLE_RATIO) 262#define RESAMPLE_BUF_RIGHT_CHANNEL (sample_buf_count/2 * RESAMPLE_RATIO)
255 263
256/* limiter */ 264/* compressor */
257/* MAX_COUNT is largest possible sample count in limiter_process. This is 265/* MAX_COUNT is largest possible sample count in compressor_process */
258 needed in case time stretch makes the count in dsp_process larger than 266#define MAX_COUNT (SMALL_SAMPLE_BUF_COUNT * RESAMPLE_RATIO / 2)
259 the limiter buffer. */ 267static struct compressor_menu c_menu;
260#define MAX_COUNT MAX(SMALL_SAMPLE_BUF_COUNT * RESAMPLE_RATIO / 2, LIMITER_BUFFER_SIZE) 268static int32_t comp_rel_slope IBSS_ATTR; /* S7.24 format */
261static int count_adjust; 269static int32_t comp_makeup_gain IBSS_ATTR; /* S7.24 format */
262static bool limiter_buffer_active; 270static int32_t comp_curve[65] IBSS_ATTR; /* S7.24 format */
263static bool limiter_buffer_full; 271static int32_t gain_buffer[MAX_COUNT] IBSS_ATTR;
264static bool limiter_buffer_emptying; 272static int32_t release_gain IBSS_ATTR;
265static int32_t limiter_buffer[2][LIMITER_BUFFER_SIZE] IBSS_ATTR; 273
266static int32_t *start_lim_buf[2] IBSS_ATTR, 274static int compressor_process(int count, int32_t *buf[]);
267 *end_lim_buf[2] IBSS_ATTR;
268static uint16_t lim_buf_peak[LIMITER_BUFFER_SIZE] IBSS_ATTR;
269static uint16_t *start_peak IBSS_ATTR,
270 *end_peak IBSS_ATTR;
271static uint16_t out_buf_peak[MAX_COUNT];
272static uint16_t *out_buf_peak_index IBSS_ATTR;
273static uint16_t release_peak IBSS_ATTR;
274static int32_t in_samp IBSS_ATTR,
275 samp0 IBSS_ATTR;
276
277static void reset_limiter_buffer(struct dsp_config *dsp);
278static int limiter_buffer_count(bool buf_count);
279static int limiter_process(int count, int32_t *buf[]);
280static uint16_t get_peak_value(int32_t sample);
281
282 /* The clip_steps array essentially stores the results of fp_factor from
283 * 0 to 12 dB, in 48 equal steps, in S3.28 format. */
284const long clip_steps[49] ICONST_ATTR = { 0x10000000,
285 0x10779AFA, 0x10F2B409, 0x1171654C, 0x11F3C9A0, 0x1279FCAD,
286 0x13041AE9, 0x139241A2, 0x14248EF9, 0x14BB21F9, 0x15561A92,
287 0x15F599A0, 0x1699C0F9, 0x1742B36B, 0x17F094CE, 0x18A38A01,
288 0x195BB8F9, 0x1A1948C5, 0x1ADC619B, 0x1BA52CDC, 0x1C73D51D,
289 0x1D488632, 0x1E236D3A, 0x1F04B8A1, 0x1FEC982C, 0x20DB3D0E,
290 0x21D0D9E2, 0x22CDA2BE, 0x23D1CD41, 0x24DD9099, 0x25F12590,
291 0x270CC693, 0x2830AFD3, 0x295D1F37, 0x2A925471, 0x2BD0911F,
292 0x2D1818B3, 0x2E6930AD, 0x2FC42095, 0x312931EC, 0x3298B072,
293 0x3412EA24, 0x35982F3A, 0x3728D22E, 0x38C52808, 0x3A6D8847,
294 0x3C224CD9, 0x3DE3D264, 0x3FB2783F};
295/* The gain_steps array essentially stores the results of fp_factor from
296 * 0 to -12 dB, in 48 equal steps, in S3.28 format. */
297const long gain_steps[49] ICONST_ATTR = { 0x10000000,
298 0xF8BC9C0, 0xF1ADF94, 0xEAD2988, 0xE429058, 0xDDAFD68,
299 0xD765AC1, 0xD149309, 0xCB59186, 0xC594210, 0xBFF9112,
300 0xBA86B88, 0xB53BEF5, 0xB017965, 0xAB18964, 0xA63DDFE,
301 0xA1866BA, 0x9CF1397, 0x987D507, 0x9429BEE, 0x8FF599E,
302 0x8BDFFD3, 0x87E80B0, 0x840CEBE, 0x804DCE8, 0x7CA9E76,
303 0x792070E, 0x75B0AB0, 0x7259DB2, 0x6F1B4BF, 0x6BF44D5,
304 0x68E4342, 0x65EA5A0, 0x63061D6, 0x6036E15, 0x5D7C0D3,
305 0x5AD50CE, 0x5841505, 0x55C04B8, 0x535176A, 0x50F44D9,
306 0x4EA84FE, 0x4C6D00E, 0x4A41E78, 0x48268DF, 0x461A81C,
307 0x441D53E, 0x422E985, 0x404DE62};
308 275
309 276
310/* Clip sample to signed 16 bit range */ 277/* Clip sample to signed 16 bit range */
@@ -944,13 +911,6 @@ static void set_gain(struct dsp_config *dsp)
944 dsp->data.gain = fp_mul(dsp->data.gain, eq_precut, 24); 911 dsp->data.gain = fp_mul(dsp->data.gain, eq_precut, 24);
945 } 912 }
946 913
947 /* only preamp for the limiter if limiter is active and sample depth
948 * allows safe pre-amping (12 dB is OK with 29 or less frac bits) */
949 if ((dsp->limiter_preamp) && (dsp->frac_bits <= 29))
950 {
951 dsp->data.gain = fp_mul(dsp->data.gain, dsp->limiter_preamp, 24);
952 }
953
954#ifdef HAVE_SW_VOLUME_CONTROL 914#ifdef HAVE_SW_VOLUME_CONTROL
955 if (global_settings.volume < SW_VOLUME_MAX || 915 if (global_settings.volume < SW_VOLUME_MAX ||
956 global_settings.volume > SW_VOLUME_MIN) 916 global_settings.volume > SW_VOLUME_MIN)
@@ -1308,8 +1268,8 @@ int dsp_process(struct dsp_config *dsp, char *dst, const char *src[], int count)
1308 if (dsp->channels_process) 1268 if (dsp->channels_process)
1309 dsp->channels_process(chunk, t2); 1269 dsp->channels_process(chunk, t2);
1310 1270
1311 if (dsp->limiter_process) 1271 if (dsp->compressor_process)
1312 chunk = dsp->limiter_process(chunk, t2); 1272 chunk = dsp->compressor_process(chunk, t2);
1313 1273
1314 dsp->output_samples(chunk, &dsp->data, (const int32_t **)t2, (int16_t *)dst); 1274 dsp->output_samples(chunk, &dsp->data, (const int32_t **)t2, (int16_t *)dst);
1315 1275
@@ -1358,15 +1318,6 @@ int dsp_output_count(struct dsp_config *dsp, int count)
1358 if (count > RESAMPLE_BUF_RIGHT_CHANNEL) 1318 if (count > RESAMPLE_BUF_RIGHT_CHANNEL)
1359 count = RESAMPLE_BUF_RIGHT_CHANNEL; 1319 count = RESAMPLE_BUF_RIGHT_CHANNEL;
1360 1320
1361 /* If the limiter buffer is filling, some or all samples will
1362 * be captured by it, so expect fewer samples coming out. */
1363 if (limiter_buffer_active && !limiter_buffer_full)
1364 {
1365 int empty_space = limiter_buffer_count(false);
1366 count_adjust = MIN(empty_space, count);
1367 count -= count_adjust;
1368 }
1369
1370 return count; 1321 return count;
1371} 1322}
1372 1323
@@ -1375,13 +1326,6 @@ int dsp_output_count(struct dsp_config *dsp, int count)
1375 */ 1326 */
1376int dsp_input_count(struct dsp_config *dsp, int count) 1327int dsp_input_count(struct dsp_config *dsp, int count)
1377{ 1328{
1378 /* If the limiter buffer is filling, the output count was
1379 * adjusted downward. This adjusts it back so that input
1380 * count is not affected.
1381 */
1382 if (limiter_buffer_active && !limiter_buffer_full)
1383 count += count_adjust;
1384
1385 /* count is now the number of resampled input samples. Convert to 1329 /* count is now the number of resampled input samples. Convert to
1386 original input samples. */ 1330 original input samples. */
1387 if (dsp->resample) 1331 if (dsp->resample)
@@ -1499,7 +1443,8 @@ intptr_t dsp_configure(struct dsp_config *dsp, int setting, intptr_t value)
1499 dsp_update_functions(dsp); 1443 dsp_update_functions(dsp);
1500 resampler_new_delta(dsp); 1444 resampler_new_delta(dsp);
1501 tdspeed_setup(dsp); 1445 tdspeed_setup(dsp);
1502 reset_limiter_buffer(dsp); 1446 if (dsp == &AUDIO_DSP)
1447 release_gain = (1 << 24);
1503 break; 1448 break;
1504 1449
1505 case DSP_FLUSH: 1450 case DSP_FLUSH:
@@ -1508,7 +1453,8 @@ intptr_t dsp_configure(struct dsp_config *dsp, int setting, intptr_t value)
1508 resampler_new_delta(dsp); 1453 resampler_new_delta(dsp);
1509 dither_init(dsp); 1454 dither_init(dsp);
1510 tdspeed_setup(dsp); 1455 tdspeed_setup(dsp);
1511 reset_limiter_buffer(dsp); 1456 if (dsp == &AUDIO_DSP)
1457 release_gain = (1 << 24);
1512 break; 1458 break;
1513 1459
1514 case DSP_SET_TRACK_GAIN: 1460 case DSP_SET_TRACK_GAIN:
@@ -1588,369 +1534,257 @@ void dsp_set_replaygain(void)
1588 set_gain(&AUDIO_DSP); 1534 set_gain(&AUDIO_DSP);
1589} 1535}
1590 1536
1591/** RESET THE LIMITER BUFFER 1537/** SET COMPRESSOR
1592 * Force the limiter buffer to its initial state and discard 1538 * Called by the menu system to configure the compressor process */
1593 * any samples held there. */ 1539void dsp_set_compressor(int c_threshold, int c_ratio, int c_gain,
1594static void reset_limiter_buffer(struct dsp_config *dsp) 1540 int c_knee, int c_release)
1595{ 1541{
1596 if (dsp == &AUDIO_DSP) 1542 bool changed = false;
1543 bool active = (c_threshold < 0);
1544 const int comp_ratio[] = {2, 4, 6, 10, 0};
1545 int new_ratio = comp_ratio[c_ratio];
1546 bool new_knee = (c_knee == 1);
1547 int new_release = c_release * NATIVE_FREQUENCY / 1000;
1548
1549 if (c_menu.threshold != c_threshold)
1597 { 1550 {
1598 int i; 1551 changed = true;
1599 logf(" reset_limiter_buffer"); 1552 c_menu.threshold = c_threshold;
1600 for (i = 0; i < 2; i++) 1553 logf(" Compressor Threshold: %d dB\tEnabled: %s",
1601 start_lim_buf[i] = end_lim_buf[i] = limiter_buffer[i]; 1554 c_menu.threshold, active ? "Yes" : "No");
1602 start_peak = end_peak = lim_buf_peak;
1603 limiter_buffer_full = false;
1604 limiter_buffer_emptying = false;
1605 release_peak = 0;
1606 } 1555 }
1607}
1608 1556
1609/** OPERATE THE LIMITER BUFFER 1557 if (c_menu.ratio != new_ratio)
1610 * Handle all samples entering or exiting the limiter buffer. */
1611static inline int set_limiter_buffer(int count, int32_t *buf[])
1612{
1613 int32_t *in_buf[] = {buf[0], buf[1]},
1614 *out_buf[] = {buf[0], buf[1]};
1615 int empty_space, i, out_count;
1616 const long clip_max = AUDIO_DSP.data.clip_max;
1617 const int ch = AUDIO_DSP.data.num_channels - 1;
1618 out_buf_peak_index = out_buf_peak;
1619
1620 if (limiter_buffer_emptying)
1621 /** EMPTY THE BUFFER
1622 * since the empty flag has been set, assume no inbound samples and
1623 return all samples in the limiter buffer to the outbound buffer */
1624 { 1558 {
1625 count = limiter_buffer_count(true); 1559 changed = true;
1626 out_count = count; 1560 c_menu.ratio = new_ratio;
1627 logf(" Emptying limiter buffer: %d", count); 1561 if (c_menu.ratio)
1628 while (count-- > 0) 1562 logf(" Compressor Ratio: %d:1", c_menu.ratio);
1629 { 1563 else
1630 for (i = 0; i <= ch; i++) 1564 logf(" Compressor Ratio: Limit");
1631 {
1632 /* move samples in limiter buffer to output buffer */
1633 *out_buf[i]++ = *start_lim_buf[i]++;
1634 if (start_lim_buf[i] == &limiter_buffer[i][LIMITER_BUFFER_SIZE])
1635 start_lim_buf[i] = limiter_buffer[i];
1636 /* move limiter buffer peak values to output peak values */
1637 if (i == 0)
1638 {
1639 *out_buf_peak_index++ = *start_peak++;
1640 if (start_peak == &lim_buf_peak[LIMITER_BUFFER_SIZE])
1641 start_peak = lim_buf_peak;
1642 }
1643 }
1644 }
1645 limiter_buffer_full = false;
1646 limiter_buffer_emptying = false;
1647 } 1565 }
1648 else /* limiter buffer NOT emptying */ 1566
1567 if (c_menu.gain != c_gain)
1568 {
1569 changed = true;
1570 c_menu.gain = c_gain;
1571 if (c_menu.gain >= 0)
1572 logf(" Compressor Makeup Gain: %d dB", c_menu.gain);
1573 else
1574 logf(" Compressor Makeup Gain: Auto");
1575 }
1576
1577 if (c_menu.soft_knee != new_knee)
1649 { 1578 {
1650 if (count <= 0) return 0; 1579 changed = true;
1580 c_menu.soft_knee = new_knee;
1581 logf(" Compressor Knee: %s", c_menu.soft_knee==1?"Soft":"Hard");
1582 }
1583
1584 if (c_menu.release != new_release)
1585 {
1586 changed = true;
1587 c_menu.release = new_release;
1588 logf(" Compressor Release: %d", c_menu.release);
1589 }
1590
1591 if (changed && active)
1592 {
1593 /* configure variables for compressor operation */
1594 int i;
1595 const int32_t db[] ={0x000000, /* positive db equivalents in S15.16 format */
1596 0x241FA4, 0x1E1A5E, 0x1A94C8, 0x181518, 0x1624EA, 0x148F82, 0x1338BD, 0x120FD2,
1597 0x1109EB, 0x101FA4, 0x0F4BB6, 0x0E8A3C, 0x0DD840, 0x0D3377, 0x0C9A0E, 0x0C0A8C,
1598 0x0B83BE, 0x0B04A5, 0x0A8C6C, 0x0A1A5E, 0x09ADE1, 0x094670, 0x08E398, 0x0884F6,
1599 0x082A30, 0x07D2FA, 0x077F0F, 0x072E31, 0x06E02A, 0x0694C8, 0x064BDF, 0x060546,
1600 0x05C0DA, 0x057E78, 0x053E03, 0x04FF5F, 0x04C273, 0x048726, 0x044D64, 0x041518,
1601 0x03DE30, 0x03A89B, 0x037448, 0x03412A, 0x030F32, 0x02DE52, 0x02AE80, 0x027FB0,
1602 0x0251D6, 0x0224EA, 0x01F8E2, 0x01CDB4, 0x01A359, 0x0179C9, 0x0150FC, 0x0128EB,
1603 0x010190, 0x00DAE4, 0x00B4E1, 0x008F82, 0x006AC1, 0x004699, 0x002305};
1651 1604
1652 empty_space = limiter_buffer_count(false); 1605 struct curve_point
1606 {
1607 int32_t db; /* S15.16 format */
1608 int32_t offset; /* S15.16 format */
1609 } db_curve[4];
1653 1610
1654 if (empty_space > 0) 1611 /** Set up the shape of the compression curve first as decibel values*/
1655 /** FILL BUFFER 1612 /* db_curve[0] = bottom of knee
1656 * use as many inbound samples as necessary to fill the buffer */ 1613 [1] = threshold
1614 [2] = top of knee
1615 [3] = 0 db input */
1616 db_curve[1].db = c_menu.threshold << 16;
1617 db_curve[1].offset = 0;
1618 if (c_menu.soft_knee)
1657 { 1619 {
1658 /* don't try to fill with more samples than available */ 1620 /* bottom of knee is 3dB below the threshold for soft knee*/
1659 if (empty_space > count) 1621 db_curve[0].db = db_curve[1].db - (3 << 16);
1660 empty_space = count; 1622 db_curve[0].offset = 0;
1661 logf(" Filling limiter buffer: %d", empty_space); 1623 /* top of knee is 3dB above the threshold for soft knee */
1662 while (empty_space-- > 0) 1624 db_curve[2].db = db_curve[1].db + (3 << 16);
1663 { 1625 if (c_menu.ratio)
1664 for (i = 0; i <= ch; i++) 1626 /* offset = -3db * (ratio - 1) / ratio */
1665 { 1627 db_curve[2].offset = (int32_t)((long long)(-3 << 16)
1666 /* put inbound samples in the limiter buffer */ 1628 * (c_menu.ratio - 1) / c_menu.ratio);
1667 in_samp = *in_buf[i]++; 1629 else
1668 *end_lim_buf[i]++ = in_samp; 1630 /* offset = -3db for hard limit */
1669 if (end_lim_buf[i] == &limiter_buffer[i][LIMITER_BUFFER_SIZE]) 1631 db_curve[2].offset = (-3 << 16);
1670 end_lim_buf[i] = limiter_buffer[i];
1671 if (in_samp < 0) /* make positive for comparison */
1672 in_samp = -in_samp - 1;
1673 if (in_samp <= clip_max)
1674 in_samp = 0; /* disregard if not clipped */
1675 if (i == 0)
1676 samp0 = in_samp;
1677 if (i == ch)
1678 {
1679 /* assign peak value for each inbound sample pair */
1680 *end_peak++ = ((samp0 > 0) || (in_samp > 0)) ?
1681 get_peak_value(MAX(samp0, in_samp)) : 0;
1682 if (end_peak == &lim_buf_peak[LIMITER_BUFFER_SIZE])
1683 end_peak = lim_buf_peak;
1684 }
1685 }
1686 count--;
1687 }
1688 /* after buffer fills, the remaining inbound samples are cycled */
1689 } 1632 }
1690 1633 else
1691 limiter_buffer_full = (end_lim_buf[0] == start_lim_buf[0]);
1692 out_count = count;
1693
1694 /** CYCLE BUFFER
1695 * return buffered samples and backfill limiter buffer with new ones.
1696 * The buffer is always full when cycling. */
1697 while (count-- > 0)
1698 { 1634 {
1699 for (i = 0; i <= ch; i++) 1635 /* bottom of knee is at the threshold for hard knee */
1700 { 1636 db_curve[0].db = c_menu.threshold << 16;
1701 /* copy incoming sample */ 1637 db_curve[0].offset = 0;
1702 in_samp = *in_buf[i]++; 1638 /* top of knee is at the threshold for hard knee */
1703 /* put limiter buffer sample into outbound buffer */ 1639 db_curve[2].db = c_menu.threshold << 16;
1704 *out_buf[i]++ = *start_lim_buf[i]++; 1640 db_curve[2].offset = 0;
1705 /* put incoming sample on the end of the limiter buffer */
1706 *end_lim_buf[i]++ = in_samp;
1707 /* ring buffer pointer wrap */
1708 if (start_lim_buf[i] == &limiter_buffer[i][LIMITER_BUFFER_SIZE])
1709 start_lim_buf[i] = limiter_buffer[i];
1710 if (end_lim_buf[i] == &limiter_buffer[i][LIMITER_BUFFER_SIZE])
1711 end_lim_buf[i] = limiter_buffer[i];
1712 if (in_samp < 0) /* make positive for comparison */
1713 in_samp = -in_samp - 1;
1714 if (in_samp <= clip_max)
1715 in_samp = 0; /* disregard if not clipped */
1716 if (i == 0)
1717 {
1718 samp0 = in_samp;
1719 /* assign outgoing sample its associated peak value */
1720 *out_buf_peak_index++ = *start_peak++;
1721 if (start_peak == &lim_buf_peak[LIMITER_BUFFER_SIZE])
1722 start_peak = lim_buf_peak;
1723 }
1724 if (i == ch)
1725 {
1726 /* assign peak value for each inbound sample pair */
1727 *end_peak++ = ((samp0 > 0) || (in_samp > 0)) ?
1728 get_peak_value(MAX(samp0, in_samp)) : 0;
1729 if (end_peak == &lim_buf_peak[LIMITER_BUFFER_SIZE])
1730 end_peak = lim_buf_peak;
1731 }
1732 }
1733 } 1641 }
1734 } 1642 /* 0db input is also max offset point (most compression) */
1735 1643 db_curve[3].db = 0;
1736 return out_count; 1644 if (c_menu.ratio)
1737} 1645 /* offset = threshold * (ratio - 1) / ratio */
1738 1646 db_curve[3].offset = (int32_t)((long long)(c_menu.threshold << 16)
1739/** RETURN LIMITER BUFFER COUNT 1647 * (c_menu.ratio - 1) / c_menu.ratio);
1740 * If argument is true, returns number of samples in the buffer,
1741 * otherwise, returns empty space remaining */
1742static int limiter_buffer_count(bool buf_count)
1743{
1744 int count;
1745 if (limiter_buffer_full)
1746 count = LIMITER_BUFFER_SIZE;
1747 else if (end_lim_buf[0] >= start_lim_buf[0])
1748 count = (end_lim_buf[0] - start_lim_buf[0]);
1749 else
1750 count = (end_lim_buf[0] - start_lim_buf[0]) + LIMITER_BUFFER_SIZE;
1751 return buf_count ? count : (LIMITER_BUFFER_SIZE - count);
1752}
1753
1754/** FLUSH THE LIMITER BUFFER
1755 * Empties the limiter buffer into the buffer pointed to by the argument
1756 * and returns the number of samples in that buffer */
1757int dsp_flush_limiter_buffer(char *dest)
1758{
1759 if ((!limiter_buffer_active) || (limiter_buffer_count(true) <= 0))
1760 return 0;
1761
1762 logf(" dsp_flush_limiter_buffer");
1763 int32_t flush_buf[2][LIMITER_BUFFER_SIZE];
1764 int32_t *src[2] = {flush_buf[0], flush_buf[1]};
1765
1766 limiter_buffer_emptying = true;
1767 int count = limiter_process(0, src);
1768 AUDIO_DSP.output_samples(count, &AUDIO_DSP.data,
1769 (const int32_t **)src, (int16_t *)dest);
1770 return count;
1771}
1772
1773/** GET PEAK VALUE
1774 * Return a small value representing how much the sample is clipped. This
1775 * should only be called if a sample is actually clipped. Sample is a
1776 * positive value.
1777 */
1778static uint16_t get_peak_value(int32_t sample)
1779{
1780 const int frac_bits = AUDIO_DSP.frac_bits;
1781 int mid,
1782 hi = 48,
1783 lo = 0;
1784
1785 /* shift sample into 28 frac bit range for comparison */
1786 if (frac_bits > 28)
1787 sample >>= (frac_bits - 28);
1788 if (frac_bits < 28)
1789 sample <<= (28 - frac_bits);
1790
1791 /* if clipped out of range, return maximum value */
1792 if (sample >= clip_steps[48])
1793 return 48 * 90;
1794
1795 /* find amount of sample clipping on the table */
1796 do
1797 {
1798 mid = (hi + lo) / 2;
1799 if (sample < clip_steps[mid])
1800 hi = mid;
1801 else if (sample > clip_steps[mid])
1802 lo = mid;
1803 else 1648 else
1804 return mid * 90; 1649 /* offset = threshold for hard limit */
1805 } 1650 db_curve[3].offset = (c_menu.threshold << 16);
1806 while (hi > (lo + 1)); 1651
1807 1652 /* Now set up the comp_curve table with compression offsets in the form
1808 /* interpolate linearly between steps (less accurate but faster) */ 1653 of gain factors in S7.24 format */
1809 return ((hi-1) * 90) + (((sample - clip_steps[hi-1]) * 90) / 1654 comp_curve[0] = (1 << 24);
1810 (clip_steps[hi] - clip_steps[hi-1])); 1655 for (i = 1; i < 64; i++)
1811}
1812
1813/** SET LIMITER
1814 * Called by the menu system to configure the limiter process */
1815void dsp_set_limiter(int limiter_level)
1816{
1817 if (limiter_level > 0)
1818 {
1819 if (!limiter_buffer_active)
1820 { 1656 {
1821 /* enable limiter process */ 1657 int32_t this_db = -db[i];
1822 AUDIO_DSP.limiter_process = limiter_process; 1658 /* no compression below the knee */
1823 limiter_buffer_active = true; 1659 if (this_db <= db_curve[0].db)
1660 comp_curve[i] = (1 << 24);
1661
1662 /* if soft knee and below top of knee, interpolate along soft knee slope */
1663 else if (c_menu.soft_knee && (this_db <= db_curve[2].db))
1664 comp_curve[i] = fp_factor(fp_mul(((this_db - db_curve[0].db) / 6),
1665 db_curve[2].offset, 16), 16) << 8;
1666
1667 /* interpolate along ratio slope above the knee */
1668 else
1669 comp_curve[i] = fp_factor(fp_mul(fp_div((this_db - db_curve[1].db),
1670 -db_curve[1].db, 16), db_curve[3].offset, 16), 16) << 8;
1824 } 1671 }
1825 /* limiter preamp is a gain factor in S7.24 format */ 1672 comp_curve[64] = fp_factor(db_curve[3].offset, 16) << 8;
1826 long old_preamp = AUDIO_DSP.limiter_preamp; 1673
1827 long new_preamp = fp_factor((((long)limiter_level << 24) / 10), 24); 1674 logf("\n *** Compression Offsets ***");
1828 if (old_preamp != new_preamp) 1675 for (i = 0; i <= 3; i++)
1829 { 1676 {
1830 AUDIO_DSP.limiter_preamp = new_preamp; 1677 logf("Curve[%d]: db: % .1f\toffset: % .4f", i, (float)db_curve[i].db / (1 << 16),
1831 set_gain(&AUDIO_DSP); 1678 (float)db_curve[i].offset / (1 << 16));
1832 logf(" Limiter enable: Yes\tLimiter amp: %.8f",
1833 (float)AUDIO_DSP.limiter_preamp / (1 << 24));
1834 } 1679 }
1835 } 1680
1836 else 1681 logf("\nGain factors:");
1837 { 1682 for (i = 1; i <= 64; i++)
1838 /* disable limiter process*/
1839 if (limiter_buffer_active)
1840 { 1683 {
1841 AUDIO_DSP.limiter_preamp = (1 << 24); 1684 debugf("%02d: %.6f ", i, (float)comp_curve[i] / (1 << 24));
1842 set_gain(&AUDIO_DSP); 1685 if (i % 4 == 0) debugf("\n");
1843 /* pcmbuf_flush_limiter_buffer(); */
1844 limiter_buffer_active = false;
1845 AUDIO_DSP.limiter_process = NULL;
1846 reset_limiter_buffer(&AUDIO_DSP);
1847 logf(" Limiter enable: No\tLimiter amp: %.8f",
1848 (float)AUDIO_DSP.limiter_preamp / (1 << 24));
1849 } 1686 }
1687
1688 /* if using auto peak, then makeup gain is max offset - .1dB headroom */
1689 int32_t db_makeup = (c_menu.gain == -1) ?
1690 -(db_curve[3].offset) - 0x199A : c_menu.gain << 16;
1691 comp_makeup_gain = fp_factor(db_makeup, 16) << 8;
1692 logf("Makeup gain:\t%.6f", (float)comp_makeup_gain / (1 << 24));
1693
1694 /* calculate per-sample gain change a rate of 10db over release time */
1695 comp_rel_slope = 0xAF0BB2 / c_menu.release;
1696 logf("Release slope:\t%.6f", (float)comp_rel_slope / (1 << 24));
1697
1698 release_gain = (1 << 24);
1850 } 1699 }
1700
1701 /* enable/disable the compressor */
1702 AUDIO_DSP.compressor_process = active ? compressor_process : NULL;
1851} 1703}
1852 1704
1853/** LIMITER PROCESS 1705/** GET COMPRESSION GAIN
1854 * Checks pre-amplified signal for clipped samples and smoothly reduces gain 1706 * Returns the required gain factor in S7.24 format in order to compress the
1855 * around the clipped samples using a preset attack/release schedule. 1707 * sample in accordance with the compression curve. Always 1 or less.
1856 */ 1708 */
1857static int limiter_process(int count, int32_t *buf[]) 1709static inline int32_t get_compression_gain(int32_t sample)
1858{ 1710{
1859 /* Limiter process passes through if limiter buffer isn't active, or the 1711 const int frac_bits = AUDIO_DSP.frac_bits;
1860 * sample depth is too large for safe pre-amping */
1861 if ((!limiter_buffer_active) || (AUDIO_DSP.frac_bits > 29))
1862 return count;
1863 1712
1864 count = set_limiter_buffer(count, buf); 1713 /* sample must be positive */
1714 if (sample < 0)
1715 sample = -sample - 1;
1716
1717 /* shift sample into 22 frac bit range */
1718 if (frac_bits > 22)
1719 sample >>= (frac_bits - 22);
1720 if (frac_bits < 22)
1721 sample <<= (22 - frac_bits);
1865 1722
1866 if (count <= 0) 1723 /* index is 6 MSB, rem is 16 LSB */
1867 return 0; 1724 int index = sample >> 16;
1725 int rem = (sample & 0xFFFF) << 8;
1868 1726
1869 const int attack_slope = 15; /* 15:1 ratio between attack and release */ 1727 /* interpolate from the compression curve */
1870 const int buffer_count = limiter_buffer_count(true); 1728 return comp_curve[index] + (int32_t)FRACMUL_SHL((comp_curve[index + 1]
1729 - comp_curve[index]), rem, 7);
1730}
1731
1732/** COMPRESSOR PROCESS
1733 * Changes the gain of the samples according to the compressor curve
1734 */
1735static int compressor_process(int count, int32_t *buf[])
1736{
1737 const int num_chan = AUDIO_DSP.data.num_channels;
1738 const int32_t fp_one = (1 << 24);
1871 1739
1740 int32_t sample_gain, /* S7.24 format */
1741 this_gain; /* S7.24 format */
1872 int i, ch; 1742 int i, ch;
1873 uint16_t max_peak = 0,
1874 gain_peak,
1875 gain_rem;
1876 long gain;
1877 1743
1878 /* step through limiter buffer in reverse order, in order to find the 1744 /* Step forward through the output buffer, and modify the offset values
1879 * appropriate max_peak for modifying the output buffer */
1880 for (i = buffer_count - 1; i >= 0; i--)
1881 {
1882 const uint16_t peak_i = lim_buf_peak[(start_peak - lim_buf_peak + i) %
1883 LIMITER_BUFFER_SIZE];
1884 /* if no attack slope, nothing to do */
1885 if ((peak_i == 0) && (max_peak == 0)) continue;
1886 /* if new peak, start attack slope */
1887 if (peak_i >= max_peak)
1888 {
1889 max_peak = peak_i;
1890 }
1891 /* keep sloping */
1892 else
1893 {
1894 if (max_peak > attack_slope)
1895 max_peak -= attack_slope;
1896 else
1897 max_peak = 0;
1898 }
1899 }
1900 /* step through output buffer the same way, but this time modifying peak
1901 * values to create a smooth attack slope. */
1902 for (i = count - 1; i >= 0; i--)
1903 {
1904 /* if no attack slope, nothing to do */
1905 if ((out_buf_peak[i] == 0) && (max_peak == 0)) continue;
1906 /* if new peak, start attack slope */
1907 if (out_buf_peak[i] >= max_peak)
1908 {
1909 max_peak = out_buf_peak[i];
1910 }
1911 /* keep sloping */
1912 else
1913 {
1914 if (max_peak > attack_slope)
1915 max_peak -= attack_slope;
1916 else
1917 max_peak = 0;
1918 out_buf_peak[i] = max_peak;
1919 }
1920 }
1921 /* Now step forward through the output buffer, and modify the peak values
1922 * to establish a smooth, slow release slope.*/ 1745 * to establish a smooth, slow release slope.*/
1923 for (i = 0; i < count; i++) 1746 for (i = 0; i < count; i++)
1924 { 1747 {
1925 /* if no release slope, nothing to do */ 1748 sample_gain = fp_one;
1926 if ((out_buf_peak[i] == 0) && (release_peak == 0)) continue; 1749 for (ch = 0; ch < num_chan; ch++)
1927 /* if new peak, start release slope */
1928 if (out_buf_peak[i] >= release_peak)
1929 { 1750 {
1930 release_peak = out_buf_peak[i]; 1751 this_gain = get_compression_gain(buf[ch][i]);
1752 if (this_gain < sample_gain)
1753 sample_gain = this_gain;
1931 } 1754 }
1932 /* keep sloping */ 1755 /* if no release slope, only apply makeup gain */
1756 if ((sample_gain == fp_one) && (release_gain == fp_one))
1757 gain_buffer[i] = comp_makeup_gain;
1933 else 1758 else
1934 { 1759 {
1935 release_peak--; 1760 /* if larger offset, start release slope */
1936 out_buf_peak[i] = release_peak; 1761 if (sample_gain <= release_gain)
1762 release_gain = sample_gain;
1763 else /* keep sloping */
1764 {
1765 if (release_gain < (fp_one - comp_rel_slope))
1766 release_gain += comp_rel_slope;
1767 else
1768 release_gain = fp_one;
1769 }
1770 /* store offset with release and also apply makeup gain */
1771 if ((release_gain == fp_one) && (comp_makeup_gain == fp_one))
1772 gain_buffer[i] = fp_one;
1773 else
1774 gain_buffer[i] = FRACMUL_SHL(release_gain, comp_makeup_gain, 7);
1937 } 1775 }
1938 } 1776 }
1939 /* Implement the limiter: adjust gain of the outbound samples by the gain 1777
1940 * amounts in the gain steps array corresponding to the peak values. */ 1778 /* Implement the compressor: apply those gain factors to the output
1779 * buffer samples */
1780
1941 for (i = 0; i < count; i++) 1781 for (i = 0; i < count; i++)
1942 { 1782 {
1943 if (out_buf_peak[i] > 0) 1783 if (gain_buffer[i] != fp_one)
1944 { 1784 {
1945 gain_peak = (out_buf_peak[i] + 1) / 90; 1785 for (ch = 0; ch < num_chan; ch++)
1946 gain_rem = (out_buf_peak[i] + 1) % 90; 1786 buf[ch][i] = FRACMUL_SHL(buf[ch][i], gain_buffer[i], 7);
1947 gain = gain_steps[gain_peak];
1948 if ((gain_peak < 48) && (gain_rem > 0))
1949 gain -= gain_rem * ((gain_steps[gain_peak] -
1950 gain_steps[gain_peak + 1]) / 90);
1951 for (ch = 0; ch < AUDIO_DSP.data.num_channels; ch++)
1952 buf[ch][i] = FRACMUL_SHL(buf[ch][i], gain, 3);
1953 } 1787 }
1954 } 1788 }
1955 return count; 1789 return count;
1956} 1790}
diff --git a/apps/dsp.h b/apps/dsp.h
index b2d8493445..ab42d73b24 100644
--- a/apps/dsp.h
+++ b/apps/dsp.h
@@ -26,8 +26,7 @@
26#include <stdbool.h> 26#include <stdbool.h>
27 27
28#define NATIVE_FREQUENCY 44100 28#define NATIVE_FREQUENCY 44100
29#define LIMITER_BUFFER_SIZE 288 /* ~6.5 ms */ 29
30#define MAX_LIMITER_GAIN 80 /* 8 dB */
31enum 30enum
32{ 31{
33 STEREO_INTERLEAVED = 0, 32 STEREO_INTERLEAVED = 0,
@@ -82,7 +81,7 @@ int32_t sound_get_pitch(void);
82void dsp_set_timestretch(int32_t percent); 81void dsp_set_timestretch(int32_t percent);
83int32_t dsp_get_timestretch(void); 82int32_t dsp_get_timestretch(void);
84int dsp_callback(int msg, intptr_t param); 83int dsp_callback(int msg, intptr_t param);
85int dsp_flush_limiter_buffer(char *dest); 84void dsp_set_compressor(int c_threshold, int c_ratio, int c_gain,
86void dsp_set_limiter(int limiter_level); 85 int c_knee, int c_release);
87 86
88#endif 87#endif
diff --git a/apps/fixedpoint.h b/apps/fixedpoint.h
index fd68fc162c..6670e597fb 100644
--- a/apps/fixedpoint.h
+++ b/apps/fixedpoint.h
@@ -61,7 +61,7 @@ long fp_sincos(unsigned long phase, long *cos);
61#define FP_NEGINF -(0x7fffffff) 61#define FP_NEGINF -(0x7fffffff)
62 62
63/* fracbits in range 12 - 22 work well. Higher is better for 63/* fracbits in range 12 - 22 work well. Higher is better for
64 * calculating dB, lower is better for calculating ratio. 64 * calculating dB, lower is better for calculating factor.
65 */ 65 */
66/* long fp_decibels(unsigned long factor, unsigned int fracbits); */ 66/* long fp_decibels(unsigned long factor, unsigned int fracbits); */
67long fp_factor(long decibels, unsigned int fracbits); 67long fp_factor(long decibels, unsigned int fracbits);
diff --git a/apps/lang/dansk.lang b/apps/lang/dansk.lang
index 1c005fb9ac..e4393e7dd3 100644
--- a/apps/lang/dansk.lang
+++ b/apps/lang/dansk.lang
@@ -12646,15 +12646,15 @@
12646 user: core 12646 user: core
12647 <source> 12647 <source>
12648 *: none 12648 *: none
12649 swcodec: "Limiter Preamp" 12649 swcodec: "Compressor"
12650 </source> 12650 </source>
12651 <dest> 12651 <dest>
12652 *: none 12652 *: none
12653 swcodec: "Begrænser forforstærkning" 12653 swcodec: "Compressor"
12654 </dest> 12654 </dest>
12655 <voice> 12655 <voice>
12656 *: none 12656 *: none
12657 swcodec: "Begrænser for-forstærkning" 12657 swcodec: "Compressor"
12658 </voice> 12658 </voice>
12659</phrase> 12659</phrase>
12660<phrase> 12660<phrase>
diff --git a/apps/lang/deutsch.lang b/apps/lang/deutsch.lang
index bdacebaa27..e7f78f800e 100644
--- a/apps/lang/deutsch.lang
+++ b/apps/lang/deutsch.lang
@@ -12641,15 +12641,15 @@
12641 user: core 12641 user: core
12642 <source> 12642 <source>
12643 *: none 12643 *: none
12644 swcodec: "Limiter Preamp" 12644 swcodec: "Compressor"
12645 </source> 12645 </source>
12646 <dest> 12646 <dest>
12647 *: none 12647 *: none
12648 swcodec: "Begrenzer-Vorverstärkung" 12648 swcodec: "Compressor"
12649 </dest> 12649 </dest>
12650 <voice> 12650 <voice>
12651 *: none 12651 *: none
12652 swcodec: "Begrenzer-Vorverstärkung" 12652 swcodec: "Compressor"
12653 </voice> 12653 </voice>
12654</phrase> 12654</phrase>
12655<phrase> 12655<phrase>
diff --git a/apps/lang/english.lang b/apps/lang/english.lang
index 08412d376d..72e0a4ce90 100644
--- a/apps/lang/english.lang
+++ b/apps/lang/english.lang
@@ -12707,15 +12707,15 @@
12707 user: core 12707 user: core
12708 <source> 12708 <source>
12709 *: none 12709 *: none
12710 swcodec: "Limiter Preamp" 12710 swcodec: "Compressor"
12711 </source> 12711 </source>
12712 <dest> 12712 <dest>
12713 *: none 12713 *: none
12714 swcodec: "Limiter Preamp" 12714 swcodec: "Compressor"
12715 </dest> 12715 </dest>
12716 <voice> 12716 <voice>
12717 *: none 12717 *: none
12718 swcodec: "Limiter Preamp" 12718 swcodec: "Compressor"
12719 </voice> 12719 </voice>
12720</phrase> 12720</phrase>
12721<phrase> 12721<phrase>
@@ -12769,3 +12769,224 @@
12769 radio: "Other" 12769 radio: "Other"
12770 </voice> 12770 </voice>
12771</phrase> 12771</phrase>
12772<phrase>
12773 id: LANG_COMPRESSOR_THRESHOLD
12774 desc: in sound settings
12775 user: core
12776 <source>
12777 *: none
12778 swcodec: "Threshold"
12779 </source>
12780 <dest>
12781 *: none
12782 swcodec: "Threshold"
12783 </dest>
12784 <voice>
12785 *: none
12786 swcodec: "Threshold"
12787 </voice>
12788</phrase>
12789<phrase>
12790 id: LANG_COMPRESSOR_RATIO
12791 desc: in sound settings
12792 user: core
12793 <source>
12794 *: none
12795 swcodec: "Ratio"
12796 </source>
12797 <dest>
12798 *: none
12799 swcodec: "Ratio"
12800 </dest>
12801 <voice>
12802 *: none
12803 swcodec: "Ratio"
12804 </voice>
12805</phrase>
12806<phrase>
12807 id: LANG_COMPRESSOR_RATIO_2
12808 desc: in sound settings
12809 user: core
12810 <source>
12811 *: none
12812 swcodec: "2:1"
12813 </source>
12814 <dest>
12815 *: none
12816 swcodec: "2:1"
12817 </dest>
12818 <voice>
12819 *: none
12820 swcodec: "2 to 1"
12821 </voice>
12822</phrase>
12823<phrase>
12824 id: LANG_COMPRESSOR_RATIO_4
12825 desc: in sound settings
12826 user: core
12827 <source>
12828 *: none
12829 swcodec: "4:1"
12830 </source>
12831 <dest>
12832 *: none
12833 swcodec: "4:1"
12834 </dest>
12835 <voice>
12836 *: none
12837 swcodec: "4 to 1"
12838 </voice>
12839</phrase>
12840<phrase>
12841 id: LANG_COMPRESSOR_RATIO_6
12842 desc: in sound settings
12843 user: core
12844 <source>
12845 *: none
12846 swcodec: "6:1"
12847 </source>
12848 <dest>
12849 *: none
12850 swcodec: "6:1"
12851 </dest>
12852 <voice>
12853 *: none
12854 swcodec: "6 to 1"
12855 </voice>
12856</phrase>
12857<phrase>
12858 id: LANG_COMPRESSOR_RATIO_10
12859 desc: in sound settings
12860 user: core
12861 <source>
12862 *: none
12863 swcodec: "10:1"
12864 </source>
12865 <dest>
12866 *: none
12867 swcodec: "10:1"
12868 </dest>
12869 <voice>
12870 *: none
12871 swcodec: "10 to 1"
12872 </voice>
12873</phrase>
12874<phrase>
12875 id: LANG_COMPRESSOR_RATIO_LIMIT
12876 desc: in sound settings
12877 user: core
12878 <source>
12879 *: none
12880 swcodec: "Limit"
12881 </source>
12882 <dest>
12883 *: none
12884 swcodec: "Limit"
12885 </dest>
12886 <voice>
12887 *: none
12888 swcodec: "Limit"
12889 </voice>
12890</phrase>
12891<phrase>
12892 id: LANG_COMPRESSOR_GAIN
12893 desc: in sound settings
12894 user: core
12895 <source>
12896 *: none
12897 swcodec: "Makeup Gain"
12898 </source>
12899 <dest>
12900 *: none
12901 swcodec: "Makeup Gain"
12902 </dest>
12903 <voice>
12904 *: none
12905 swcodec: "Makeup Gain"
12906 </voice>
12907</phrase>
12908<phrase>
12909 id: LANG_AUTO
12910 desc: in sound settings
12911 user: core
12912 <source>
12913 *: none
12914 swcodec: "Auto"
12915 </source>
12916 <dest>
12917 *: none
12918 swcodec: "Auto"
12919 </dest>
12920 <voice>
12921 *: none
12922 swcodec: "Auto"
12923 </voice>
12924</phrase>
12925<phrase>
12926 id: LANG_COMPRESSOR_KNEE
12927 desc: in sound settings
12928 user: core
12929 <source>
12930 *: none
12931 swcodec: "Knee"
12932 </source>
12933 <dest>
12934 *: none
12935 swcodec: "Knee"
12936 </dest>
12937 <voice>
12938 *: none
12939 swcodec: "Knee"
12940 </voice>
12941</phrase>
12942<phrase>
12943 id: LANG_COMPRESSOR_HARD_KNEE
12944 desc: in sound settings
12945 user: core
12946 <source>
12947 *: none
12948 swcodec: "Hard Knee"
12949 </source>
12950 <dest>
12951 *: none
12952 swcodec: "Hard Knee"
12953 </dest>
12954 <voice>
12955 *: none
12956 swcodec: "Hard Knee"
12957 </voice>
12958</phrase>
12959<phrase>
12960 id: LANG_COMPRESSOR_SOFT_KNEE
12961 desc: in sound settings
12962 user: core
12963 <source>
12964 *: none
12965 swcodec: "Soft Knee"
12966 </source>
12967 <dest>
12968 *: none
12969 swcodec: "Soft Knee"
12970 </dest>
12971 <voice>
12972 *: none
12973 swcodec: "Soft Knee"
12974 </voice>
12975</phrase>
12976<phrase>
12977 id: LANG_COMPRESSOR_RELEASE
12978 desc: in sound settings
12979 user: core
12980 <source>
12981 *: none
12982 swcodec: "Release Time"
12983 </source>
12984 <dest>
12985 *: none
12986 swcodec: "Release Time"
12987 </dest>
12988 <voice>
12989 *: none
12990 swcodec: "Release Time"
12991 </voice>
12992</phrase>
diff --git a/apps/lang/francais.lang b/apps/lang/francais.lang
index 43fe7561f2..7d4af8ade7 100644
--- a/apps/lang/francais.lang
+++ b/apps/lang/francais.lang
@@ -12663,15 +12663,15 @@
12663 user: core 12663 user: core
12664 <source> 12664 <source>
12665 *: none 12665 *: none
12666 swcodec: "Limiter Preamp" 12666 swcodec: "Compressor"
12667 </source> 12667 </source>
12668 <dest> 12668 <dest>
12669 *: none 12669 *: none
12670 swcodec: "Limiteur préampli." 12670 swcodec: "Compressor"
12671 </dest> 12671 </dest>
12672 <voice> 12672 <voice>
12673 *: none 12673 *: none
12674 swcodec: "Limiteur préampli" 12674 swcodec: "Compressor"
12675 </voice> 12675 </voice>
12676</phrase> 12676</phrase>
12677<phrase> 12677<phrase>
diff --git a/apps/lang/italiano.lang b/apps/lang/italiano.lang
index 075520d2af..0727320c45 100644
--- a/apps/lang/italiano.lang
+++ b/apps/lang/italiano.lang
@@ -12636,15 +12636,15 @@
12636 user: core 12636 user: core
12637 <source> 12637 <source>
12638 *: none 12638 *: none
12639 swcodec: "Limiter Preamp" 12639 swcodec: "Compressor"
12640 </source> 12640 </source>
12641 <dest> 12641 <dest>
12642 *: none 12642 *: none
12643 swcodec: "Limitatore Preamp" 12643 swcodec: "Compressor"
12644 </dest> 12644 </dest>
12645 <voice> 12645 <voice>
12646 *: none 12646 *: none
12647 swcodec: "Limitatore Preamp" 12647 swcodec: "Compressor"
12648 </voice> 12648 </voice>
12649</phrase> 12649</phrase>
12650<phrase> 12650<phrase>
diff --git a/apps/lang/polski.lang b/apps/lang/polski.lang
index 5595526145..86017a4998 100644
--- a/apps/lang/polski.lang
+++ b/apps/lang/polski.lang
@@ -12644,15 +12644,15 @@
12644 user: core 12644 user: core
12645 <source> 12645 <source>
12646 *: none 12646 *: none
12647 swcodec: "Limiter Preamp" 12647 swcodec: "Compressor"
12648 </source> 12648 </source>
12649 <dest> 12649 <dest>
12650 *: none 12650 *: none
12651 swcodec: "Wzmacnianie cichych fragmentów" 12651 swcodec: "Compressor"
12652 </dest> 12652 </dest>
12653 <voice> 12653 <voice>
12654 *: none 12654 *: none
12655 swcodec: "Wzmacnianie cichych fragmentów" 12655 swcodec: "Compressor"
12656 </voice> 12656 </voice>
12657</phrase> 12657</phrase>
12658<phrase> 12658<phrase>
diff --git a/apps/lang/svenska.lang b/apps/lang/svenska.lang
index 7a1564bded..24c33e7d0b 100644
--- a/apps/lang/svenska.lang
+++ b/apps/lang/svenska.lang
@@ -12637,15 +12637,15 @@
12637 user: core 12637 user: core
12638 <source> 12638 <source>
12639 *: none 12639 *: none
12640 swcodec: "Limiter Preamp" 12640 swcodec: "Compressor"
12641 </source> 12641 </source>
12642 <dest> 12642 <dest>
12643 *: none 12643 *: none
12644 swcodec: "Limiter-förförstärkning" 12644 swcodec: "Compressor"
12645 </dest> 12645 </dest>
12646 <voice> 12646 <voice>
12647 *: none 12647 *: none
12648 swcodec: "Limiter-förförstärkning" 12648 swcodec: "Compressor"
12649 </voice> 12649 </voice>
12650</phrase> 12650</phrase>
12651<phrase> 12651<phrase>
diff --git a/apps/lang/tagalog.lang b/apps/lang/tagalog.lang
index 667a9d8c28..4600eab9f4 100644
--- a/apps/lang/tagalog.lang
+++ b/apps/lang/tagalog.lang
@@ -12445,15 +12445,15 @@
12445 user: core 12445 user: core
12446 <source> 12446 <source>
12447 *: none 12447 *: none
12448 swcodec: "Limiter Preamp" 12448 swcodec: "Compressor"
12449 </source> 12449 </source>
12450 <dest> 12450 <dest>
12451 *: none 12451 *: none
12452 swcodec: "Limiter Preamp" 12452 swcodec: "Compressor"
12453 </dest> 12453 </dest>
12454 <voice> 12454 <voice>
12455 *: none 12455 *: none
12456 swcodec: "Limiter Preamp" 12456 swcodec: "Compressor"
12457 </voice> 12457 </voice>
12458</phrase> 12458</phrase>
12459<phrase> 12459<phrase>
diff --git a/apps/lang/walon.lang b/apps/lang/walon.lang
index c645ad10a2..c7a06ff55a 100644
--- a/apps/lang/walon.lang
+++ b/apps/lang/walon.lang
@@ -12632,19 +12632,19 @@
12632 </voice> 12632 </voice>
12633</phrase> 12633</phrase>
12634<phrase> 12634<phrase>
12635 id: LANG_LIMITER 12635 id: LANG_COMPRESSOR
12636 desc: in sound settings 12636 desc: in sound settings
12637 user: core 12637 user: core
12638 <source> 12638 <source>
12639 *: none 12639 *: none
12640 swcodec: "Limiter Preamp" 12640 swcodec: "Compressor"
12641 </source> 12641 </source>
12642 <dest> 12642 <dest>
12643 *: none 12643 *: none
12644 swcodec: "Aschatrece Preamp" 12644 swcodec: "Compressor"
12645 </dest> 12645 </dest>
12646 <voice> 12646 <voice>
12647 *: none 12647 *: none
12648 swcodec: "Aschatrece Preamp" 12648 swcodec: "Compressor"
12649 </voice> 12649 </voice>
12650</phrase> 12650</phrase>
diff --git a/apps/menus/sound_menu.c b/apps/menus/sound_menu.c
index 678d495d07..60315804a9 100644
--- a/apps/menus/sound_menu.c
+++ b/apps/menus/sound_menu.c
@@ -105,8 +105,21 @@ static int timestretch_callback(int action,const struct menu_item_ex *this_item)
105 &global_settings.timestretch_enabled, timestretch_callback); 105 &global_settings.timestretch_enabled, timestretch_callback);
106 MENUITEM_SETTING(dithering_enabled, 106 MENUITEM_SETTING(dithering_enabled,
107 &global_settings.dithering_enabled, lowlatency_callback); 107 &global_settings.dithering_enabled, lowlatency_callback);
108 MENUITEM_SETTING(limiter_level, 108
109 &global_settings.limiter_level, lowlatency_callback); 109 /* compressor submenu */
110 MENUITEM_SETTING(compressor_threshold,
111 &global_settings.compressor_threshold, lowlatency_callback);
112 MENUITEM_SETTING(compressor_ratio,
113 &global_settings.compressor_ratio, lowlatency_callback);
114 MENUITEM_SETTING(compressor_gain,
115 &global_settings.compressor_makeup_gain, lowlatency_callback);
116 MENUITEM_SETTING(compressor_knee,
117 &global_settings.compressor_knee, lowlatency_callback);
118 MENUITEM_SETTING(compressor_release,
119 &global_settings.compressor_release_time, lowlatency_callback);
120 MAKE_MENU(compressor_menu,ID2P(LANG_COMPRESSOR), NULL, Icon_NOICON,
121 &compressor_threshold, &compressor_ratio, &compressor_gain,
122 &compressor_knee, &compressor_release);
110#endif 123#endif
111 124
112#if (CONFIG_CODEC == MAS3587F) || (CONFIG_CODEC == MAS3539F) 125#if (CONFIG_CODEC == MAS3587F) || (CONFIG_CODEC == MAS3539F)
@@ -140,7 +153,7 @@ MAKE_MENU(sound_settings, ID2P(LANG_SOUND_SETTINGS), NULL, Icon_Audio,
140#if CONFIG_CODEC == SWCODEC 153#if CONFIG_CODEC == SWCODEC
141 ,&crossfeed_menu, &equalizer_menu, &dithering_enabled 154 ,&crossfeed_menu, &equalizer_menu, &dithering_enabled
142 ,&timestretch_enabled 155 ,&timestretch_enabled
143 ,&limiter_level 156 ,&compressor_menu
144#endif 157#endif
145#if (CONFIG_CODEC == MAS3587F) || (CONFIG_CODEC == MAS3539F) 158#if (CONFIG_CODEC == MAS3587F) || (CONFIG_CODEC == MAS3539F)
146 ,&loudness,&avc,&superbass,&mdb_enable,&mdb_strength 159 ,&loudness,&avc,&superbass,&mdb_enable,&mdb_strength
diff --git a/apps/pcmbuf.c b/apps/pcmbuf.c
index 66a4ed4128..319e3e8044 100644
--- a/apps/pcmbuf.c
+++ b/apps/pcmbuf.c
@@ -1170,30 +1170,6 @@ bool pcmbuf_is_crossfade_enabled(void)
1170 * Commit any remaining samples in the PCM buffer for playback. */ 1170 * Commit any remaining samples in the PCM buffer for playback. */
1171void pcmbuf_play_remainder(void) 1171void pcmbuf_play_remainder(void)
1172{ 1172{
1173 pcmbuf_flush_limiter_buffer();
1174
1175 if (audiobuffer_fillpos) 1173 if (audiobuffer_fillpos)
1176 pcmbuf_flush_fillpos(); 1174 pcmbuf_flush_fillpos();
1177} 1175}
1178
1179/** FLUSH LIMITER BUFFER
1180 * Empty the limiter buffer and commit its contents
1181 * to the PCM buffer for playback. */
1182void pcmbuf_flush_limiter_buffer(void)
1183{
1184 char *dest;
1185 int out_count = LIMITER_BUFFER_SIZE;
1186
1187 /* create room at the end of the PCM buffer for any
1188 samples that may be held back in the limiter buffer */
1189 while ((dest = pcmbuf_request_buffer(&out_count)) == NULL)
1190 {
1191 cancel_cpu_boost();
1192 sleep(1);
1193 }
1194
1195 /* flush the limiter buffer into the PCM buffer */
1196 out_count = dsp_flush_limiter_buffer(dest);
1197 if (out_count > 0)
1198 pcmbuf_write_complete(out_count);
1199}
diff --git a/apps/pcmbuf.h b/apps/pcmbuf.h
index 12a41a253c..4cb1c1acb7 100644
--- a/apps/pcmbuf.h
+++ b/apps/pcmbuf.h
@@ -76,6 +76,5 @@ void pcmbuf_beep(unsigned int frequency, size_t duration, int amplitude);
76int pcmbuf_used_descs(void); 76int pcmbuf_used_descs(void);
77int pcmbuf_descs(void); 77int pcmbuf_descs(void);
78void pcmbuf_play_remainder(void); 78void pcmbuf_play_remainder(void);
79void pcmbuf_flush_limiter_buffer(void);
80 79
81#endif 80#endif
diff --git a/apps/plugin.c b/apps/plugin.c
index a497ad0456..aa7077edae 100644
--- a/apps/plugin.c
+++ b/apps/plugin.c
@@ -467,7 +467,6 @@ static const struct plugin_api rockbox_api = {
467 dsp_process, 467 dsp_process,
468 dsp_input_count, 468 dsp_input_count,
469 dsp_output_count, 469 dsp_output_count,
470 dsp_flush_limiter_buffer,
471#endif /* CONFIG_CODEC == SWCODEC */ 470#endif /* CONFIG_CODEC == SWCODEC */
472 471
473 /* playback control */ 472 /* playback control */
diff --git a/apps/plugin.h b/apps/plugin.h
index 4981230df3..b4a6b4fc1b 100644
--- a/apps/plugin.h
+++ b/apps/plugin.h
@@ -133,12 +133,12 @@ void* plugin_get_buffer(size_t *buffer_size);
133#define PLUGIN_MAGIC 0x526F634B /* RocK */ 133#define PLUGIN_MAGIC 0x526F634B /* RocK */
134 134
135/* increase this every time the api struct changes */ 135/* increase this every time the api struct changes */
136#define PLUGIN_API_VERSION 171 136#define PLUGIN_API_VERSION 172
137 137
138/* update this to latest version if a change to the api struct breaks 138/* update this to latest version if a change to the api struct breaks
139 backwards compatibility (and please take the opportunity to sort in any 139 backwards compatibility (and please take the opportunity to sort in any
140 new function which are "waiting" at the end of the function table) */ 140 new function which are "waiting" at the end of the function table) */
141#define PLUGIN_MIN_API_VERSION 171 141#define PLUGIN_MIN_API_VERSION 172
142 142
143/* plugin return codes */ 143/* plugin return codes */
144enum plugin_status { 144enum plugin_status {
@@ -596,7 +596,6 @@ struct plugin_api {
596 const char *src[], int count); 596 const char *src[], int count);
597 int (*dsp_input_count)(struct dsp_config *dsp, int count); 597 int (*dsp_input_count)(struct dsp_config *dsp, int count);
598 int (*dsp_output_count)(struct dsp_config *dsp, int count); 598 int (*dsp_output_count)(struct dsp_config *dsp, int count);
599 int (*dsp_flush_limiter_buffer)(char *dest);
600#endif /* CONFIG_CODEC == SWCODC */ 599#endif /* CONFIG_CODEC == SWCODC */
601 600
602 /* playback control */ 601 /* playback control */
diff --git a/apps/plugins/test_codec.c b/apps/plugins/test_codec.c
index b77611689b..b41f053571 100644
--- a/apps/plugins/test_codec.c
+++ b/apps/plugins/test_codec.c
@@ -810,34 +810,10 @@ show_menu:
810 810
811 rb->closedir(dir); 811 rb->closedir(dir);
812 } 812 }
813 /* process last samples */
814 if (use_dsp)
815 rb->dsp_flush_limiter_buffer(dspbuffer);
816 } else { 813 } else {
817 /* Just test the file */ 814 /* Just test the file */
818 res = test_track(parameter); 815 res = test_track(parameter);
819 816
820 /* process last samples */
821 if (use_dsp)
822 {
823 int channels = (wavinfo.stereomode == STEREO_MONO) ? 1 : 2;
824 int count = rb->dsp_flush_limiter_buffer(dspbuffer);
825 if (channels == 1)
826 {
827 unsigned char *s = dspbuffer, *d = dspbuffer;
828 int c = count;
829 while (c-- > 0)
830 {
831 *d++ = *s++;
832 *d++ = *s++;
833 s++;
834 s++;
835 }
836 }
837 if (wavinfo.fd >= 0)
838 rb->write(wavinfo.fd, dspbuffer, count * 2 * channels);
839 }
840
841 /* Close WAV file (if there was one) */ 817 /* Close WAV file (if there was one) */
842 if (wavinfo.fd >= 0) { 818 if (wavinfo.fd >= 0) {
843 close_wav(); 819 close_wav();
diff --git a/apps/settings.c b/apps/settings.c
index 4c16c6a0ec..ce1ee07054 100644
--- a/apps/settings.c
+++ b/apps/settings.c
@@ -956,7 +956,11 @@ void settings_apply(bool read_disk)
956 956
957 dsp_dither_enable(global_settings.dithering_enabled); 957 dsp_dither_enable(global_settings.dithering_enabled);
958 dsp_timestretch_enable(global_settings.timestretch_enabled); 958 dsp_timestretch_enable(global_settings.timestretch_enabled);
959 dsp_set_limiter(global_settings.limiter_level); 959 dsp_set_compressor(global_settings.compressor_threshold,
960 global_settings.compressor_ratio,
961 global_settings.compressor_makeup_gain,
962 global_settings.compressor_knee,
963 global_settings.compressor_release_time);
960#endif 964#endif
961 965
962#ifdef HAVE_SPDIF_POWER 966#ifdef HAVE_SPDIF_POWER
diff --git a/apps/settings.h b/apps/settings.h
index e8ffe91471..ec92a81734 100644
--- a/apps/settings.h
+++ b/apps/settings.h
@@ -777,7 +777,11 @@ struct user_settings
777#endif 777#endif
778 778
779#if CONFIG_CODEC == SWCODEC 779#if CONFIG_CODEC == SWCODEC
780 int limiter_level; 780 int compressor_threshold;
781 int compressor_ratio;
782 int compressor_makeup_gain;
783 int compressor_knee;
784 int compressor_release_time;
781#endif 785#endif
782 786
783}; 787};
diff --git a/apps/settings_list.c b/apps/settings_list.c
index 2a7bf9658f..2e3632b949 100644
--- a/apps/settings_list.c
+++ b/apps/settings_list.c
@@ -356,6 +356,34 @@ static void crossfeed_cross_set(int val)
356 global_settings.crossfeed_hf_cutoff); 356 global_settings.crossfeed_hf_cutoff);
357} 357}
358 358
359static void compressor_set(int val)
360{
361 (void)val;
362 dsp_set_compressor(global_settings.compressor_threshold,
363 global_settings.compressor_ratio,
364 global_settings.compressor_makeup_gain,
365 global_settings.compressor_knee,
366 global_settings.compressor_release_time);
367}
368
369static const char* auto_formatter(char *buffer, size_t buffer_size,
370 int val, const char *unit)
371{
372 if (val == -1)
373 return str(LANG_AUTO);
374 else
375 snprintf(buffer, buffer_size, "%d %s", val, unit);
376 return buffer;
377}
378
379static int32_t auto_getlang(int value, int unit)
380{
381 if (value == -1)
382 return LANG_AUTO;
383 else
384 return TALK_ID(value, unit);
385}
386
359static const char* db_format(char* buffer, size_t buffer_size, int value, 387static const char* db_format(char* buffer, size_t buffer_size, int value,
360 const char* unit) 388 const char* unit)
361{ 389{
@@ -1256,11 +1284,29 @@ const struct settings_list settings[] = {
1256 OFFON_SETTING(F_SOUNDSETTING, timestretch_enabled, LANG_TIMESTRETCH, false, 1284 OFFON_SETTING(F_SOUNDSETTING, timestretch_enabled, LANG_TIMESTRETCH, false,
1257 "timestretch enabled", dsp_timestretch_enable), 1285 "timestretch enabled", dsp_timestretch_enable),
1258 1286
1259 /* limiter */ 1287 /* compressor */
1260 INT_SETTING_NOWRAP(F_SOUNDSETTING, limiter_level, 1288 INT_SETTING_NOWRAP(F_SOUNDSETTING, compressor_threshold,
1261 LANG_COMPRESSOR, 0, 1289 LANG_COMPRESSOR_THRESHOLD, 0,
1262 "limiter level", UNIT_DB, 0, MAX_LIMITER_GAIN, 1290 "compressor threshold", UNIT_DB, 0, -24,
1263 5, db_format, get_dec_talkid, dsp_set_limiter), 1291 -3, formatter_unit_0_is_off, getlang_unit_0_is_off, compressor_set),
1292 CHOICE_SETTING(F_SOUNDSETTING|F_NO_WRAP, compressor_ratio,
1293 LANG_COMPRESSOR_RATIO, 1, "compressor ratio",
1294 "2:1,4:1,6:1,10:1,limit", compressor_set, 5,
1295 ID2P(LANG_COMPRESSOR_RATIO_2), ID2P(LANG_COMPRESSOR_RATIO_4),
1296 ID2P(LANG_COMPRESSOR_RATIO_6), ID2P(LANG_COMPRESSOR_RATIO_10),
1297 ID2P(LANG_COMPRESSOR_RATIO_LIMIT)),
1298 INT_SETTING_NOWRAP(F_SOUNDSETTING, compressor_makeup_gain,
1299 LANG_COMPRESSOR_GAIN, -1,
1300 "compressor makeup gain", UNIT_DB, -1, 20,
1301 1, auto_formatter, auto_getlang, compressor_set),
1302 CHOICE_SETTING(F_SOUNDSETTING|F_NO_WRAP, compressor_knee,
1303 LANG_COMPRESSOR_KNEE, 1, "compressor knee",
1304 "hard knee,soft knee", compressor_set, 2,
1305 ID2P(LANG_COMPRESSOR_HARD_KNEE), ID2P(LANG_COMPRESSOR_SOFT_KNEE)),
1306 INT_SETTING_NOWRAP(F_SOUNDSETTING, compressor_release_time,
1307 LANG_COMPRESSOR_RELEASE, 100,
1308 "compressor release time", UNIT_MS, 20, 200,
1309 10, NULL, NULL, compressor_set),
1264#endif 1310#endif
1265#ifdef HAVE_WM8758 1311#ifdef HAVE_WM8758
1266 SOUND_SETTING(F_NO_WRAP, bass_cutoff, LANG_BASS_CUTOFF, 1312 SOUND_SETTING(F_NO_WRAP, bass_cutoff, LANG_BASS_CUTOFF,