summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeffrey Goode <jeffg7@gmail.com>2009-08-18 03:24:45 +0000
committerJeffrey Goode <jeffg7@gmail.com>2009-08-18 03:24:45 +0000
commit2b7ef6b92880249e64639768012266e65f5d14cc (patch)
tree119d27b90177426b8c01da7d3f5a4e1d884a01ee
parentf451108fa857fe16f35fa3beb5278e36fed74c67 (diff)
downloadrockbox-2b7ef6b92880249e64639768012266e65f5d14cc.tar.gz
rockbox-2b7ef6b92880249e64639768012266e65f5d14cc.zip
FS#10199: Adds limiter DSP function
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@22394 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r--apps/dsp.c464
-rw-r--r--apps/dsp.h4
-rw-r--r--apps/lang/english.lang17
-rw-r--r--apps/menus/sound_menu.c3
-rw-r--r--apps/pcmbuf.c24
-rw-r--r--apps/pcmbuf.h1
-rw-r--r--apps/plugin.c2
-rw-r--r--apps/plugin.h1
-rw-r--r--apps/plugins/test_codec.c24
-rw-r--r--apps/settings.c1
-rw-r--r--apps/settings.h5
-rw-r--r--apps/settings_list.c6
12 files changed, 546 insertions, 6 deletions
diff --git a/apps/dsp.c b/apps/dsp.c
index 61fc027e4c..c8c8ddee75 100644
--- a/apps/dsp.c
+++ b/apps/dsp.c
@@ -35,6 +35,11 @@
35#include "buffer.h" 35#include "buffer.h"
36#include "fixedpoint.h" 36#include "fixedpoint.h"
37#include "fracmul.h" 37#include "fracmul.h"
38#include "pcmbuf.h"
39
40/* Define LOGF_ENABLE to enable logf output in this file */
41/*#define LOGF_ENABLE*/
42#include "logf.h"
38 43
39/* 16-bit samples are scaled based on these constants. The shift should be 44/* 16-bit samples are scaled based on these constants. The shift should be
40 * no more than 15. 45 * no more than 15.
@@ -149,7 +154,8 @@ typedef void (*channels_process_fn_type)(int count, int32_t *buf[]);
149/* DSP local channel processing in place */ 154/* DSP local channel processing in place */
150typedef void (*channels_process_dsp_fn_type)(int count, struct dsp_data *data, 155typedef void (*channels_process_dsp_fn_type)(int count, struct dsp_data *data,
151 int32_t *buf[]); 156 int32_t *buf[]);
152 157/* DSP processes that return a value */
158typedef int (*return_fn_type)(int count, int32_t *buf[]);
153 159
154/* 160/*
155 ***************************************************************************/ 161 ***************************************************************************/
@@ -165,6 +171,7 @@ struct dsp_config
165 int32_t tdspeed_percent; /* Speed% * PITCH_SPEED_PRECISION */ 171 int32_t tdspeed_percent; /* Speed% * PITCH_SPEED_PRECISION */
166 bool tdspeed_active; /* Timestretch is in use */ 172 bool tdspeed_active; /* Timestretch is in use */
167 int frac_bits; 173 int frac_bits;
174 long limiter_preamp; /* limiter amp gain in S7.24 format */
168#ifdef HAVE_SW_TONE_CONTROLS 175#ifdef HAVE_SW_TONE_CONTROLS
169 /* Filter struct for software bass/treble controls */ 176 /* Filter struct for software bass/treble controls */
170 struct eqfilter tone_filter; 177 struct eqfilter tone_filter;
@@ -180,6 +187,7 @@ struct dsp_config
180 channels_process_fn_type apply_crossfeed; 187 channels_process_fn_type apply_crossfeed;
181 channels_process_fn_type eq_process; 188 channels_process_fn_type eq_process;
182 channels_process_fn_type channels_process; 189 channels_process_fn_type channels_process;
190 return_fn_type limiter_process;
183}; 191};
184 192
185/* General DSP config */ 193/* General DSP config */
@@ -219,6 +227,55 @@ static long album_peak;
219static long replaygain; 227static long replaygain;
220static bool crossfeed_enabled; 228static bool crossfeed_enabled;
221 229
230/* limiter */
231static int count_adjust;
232static bool limiter_buffer_active;
233static bool limiter_buffer_full;
234static bool limiter_buffer_emptying;
235static int32_t limiter_buffer[2][LIMITER_BUFFER_SIZE] IBSS_ATTR;
236static int32_t *start_lim_buf[2] IBSS_ATTR,
237 *end_lim_buf[2] IBSS_ATTR;
238static uint16_t lim_buf_peak[LIMITER_BUFFER_SIZE] IBSS_ATTR;
239static uint16_t *start_peak IBSS_ATTR,
240 *end_peak IBSS_ATTR;
241static uint16_t out_buf_peak[LIMITER_BUFFER_SIZE] IBSS_ATTR;
242static uint16_t *out_buf_peak_index IBSS_ATTR;
243static uint16_t release_peak IBSS_ATTR;
244static int32_t in_samp IBSS_ATTR,
245 samp0 IBSS_ATTR;
246
247static void reset_limiter_buffer(struct dsp_config *dsp);
248static int limiter_buffer_count(bool buf_count);
249static int limiter_process(int count, int32_t *buf[]);
250static uint16_t get_peak_value(int32_t sample);
251
252 /* The clip_steps array essentially stores the results of fp_factor from
253 * 0 to 12 dB, in 48 equal steps, in S3.28 format. */
254const long clip_steps[49] ICONST_ATTR = { 0x10000000,
255 0x10779AFA, 0x10F2B409, 0x1171654C, 0x11F3C9A0, 0x1279FCAD,
256 0x13041AE9, 0x139241A2, 0x14248EF9, 0x14BB21F9, 0x15561A92,
257 0x15F599A0, 0x1699C0F9, 0x1742B36B, 0x17F094CE, 0x18A38A01,
258 0x195BB8F9, 0x1A1948C5, 0x1ADC619B, 0x1BA52CDC, 0x1C73D51D,
259 0x1D488632, 0x1E236D3A, 0x1F04B8A1, 0x1FEC982C, 0x20DB3D0E,
260 0x21D0D9E2, 0x22CDA2BE, 0x23D1CD41, 0x24DD9099, 0x25F12590,
261 0x270CC693, 0x2830AFD3, 0x295D1F37, 0x2A925471, 0x2BD0911F,
262 0x2D1818B3, 0x2E6930AD, 0x2FC42095, 0x312931EC, 0x3298B072,
263 0x3412EA24, 0x35982F3A, 0x3728D22E, 0x38C52808, 0x3A6D8847,
264 0x3C224CD9, 0x3DE3D264, 0x3FB2783F};
265/* The gain_steps array essentially stores the results of fp_factor from
266 * 0 to -12 dB, in 48 equal steps, in S3.28 format. */
267const long gain_steps[49] ICONST_ATTR = { 0x10000000,
268 0xF8BC9C0, 0xF1ADF94, 0xEAD2988, 0xE429058, 0xDDAFD68,
269 0xD765AC1, 0xD149309, 0xCB59186, 0xC594210, 0xBFF9112,
270 0xBA86B88, 0xB53BEF5, 0xB017965, 0xAB18964, 0xA63DDFE,
271 0xA1866BA, 0x9CF1397, 0x987D507, 0x9429BEE, 0x8FF599E,
272 0x8BDFFD3, 0x87E80B0, 0x840CEBE, 0x804DCE8, 0x7CA9E76,
273 0x792070E, 0x75B0AB0, 0x7259DB2, 0x6F1B4BF, 0x6BF44D5,
274 0x68E4342, 0x65EA5A0, 0x63061D6, 0x6036E15, 0x5D7C0D3,
275 0x5AD50CE, 0x5841505, 0x55C04B8, 0x535176A, 0x50F44D9,
276 0x4EA84FE, 0x4C6D00E, 0x4A41E78, 0x48268DF, 0x461A81C,
277 0x441D53E, 0x422E985, 0x404DE62};
278
222#define AUDIO_DSP (dsp_conf[CODEC_IDX_AUDIO]) 279#define AUDIO_DSP (dsp_conf[CODEC_IDX_AUDIO])
223#define VOICE_DSP (dsp_conf[CODEC_IDX_VOICE]) 280#define VOICE_DSP (dsp_conf[CODEC_IDX_VOICE])
224 281
@@ -869,6 +926,7 @@ static void dsp_apply_gain(int count, struct dsp_data *data, int32_t *buf[])
869/* Combine all gains to a global gain. */ 926/* Combine all gains to a global gain. */
870static void set_gain(struct dsp_config *dsp) 927static void set_gain(struct dsp_config *dsp)
871{ 928{
929 /* gains are in S7.24 format */
872 dsp->data.gain = DEFAULT_GAIN; 930 dsp->data.gain = DEFAULT_GAIN;
873 931
874 /* Replay gain not relevant to voice */ 932 /* Replay gain not relevant to voice */
@@ -879,8 +937,14 @@ static void set_gain(struct dsp_config *dsp)
879 937
880 if (dsp->eq_process && eq_precut) 938 if (dsp->eq_process && eq_precut)
881 { 939 {
882 dsp->data.gain = 940 dsp->data.gain = fp_mul(dsp->data.gain, eq_precut, 24);
883 (long) (((int64_t) dsp->data.gain * eq_precut) >> 24); 941 }
942
943 /* only preamp for the limiter if limiter is active and sample depth
944 * allows safe pre-amping (12 dB is OK with 29 or less frac bits) */
945 if ((dsp->limiter_preamp) && (dsp->frac_bits <= 29))
946 {
947 dsp->data.gain = fp_mul(dsp->data.gain, dsp->limiter_preamp, 24);
884 } 948 }
885 949
886#ifdef HAVE_SW_VOLUME_CONTROL 950#ifdef HAVE_SW_VOLUME_CONTROL
@@ -898,7 +962,7 @@ static void set_gain(struct dsp_config *dsp)
898 } 962 }
899 else 963 else
900 { 964 {
901 dsp->data.gain >>= 1; 965 dsp->data.gain >>= 1; /* convert gain to S8.23 format */
902 } 966 }
903 967
904 dsp->apply_gain = dsp->data.gain != 0 ? dsp_apply_gain : NULL; 968 dsp->apply_gain = dsp->data.gain != 0 ? dsp_apply_gain : NULL;
@@ -1207,7 +1271,7 @@ int dsp_process(struct dsp_config *dsp, char *dst, const char *src[], int count)
1207 1271
1208 if (dsp->tdspeed_active) 1272 if (dsp->tdspeed_active)
1209 samples = tdspeed_doit(tmp, samples); 1273 samples = tdspeed_doit(tmp, samples);
1210 1274
1211 int chunk_offset = 0; 1275 int chunk_offset = 0;
1212 while (samples > 0) 1276 while (samples > 0)
1213 { 1277 {
@@ -1239,6 +1303,9 @@ int dsp_process(struct dsp_config *dsp, char *dst, const char *src[], int count)
1239 1303
1240 if (dsp->channels_process) 1304 if (dsp->channels_process)
1241 dsp->channels_process(chunk, t2); 1305 dsp->channels_process(chunk, t2);
1306
1307 if (dsp->limiter_process)
1308 chunk = dsp->limiter_process(chunk, t2);
1242 1309
1243 dsp->output_samples(chunk, &dsp->data, (const int32_t **)t2, (int16_t *)dst); 1310 dsp->output_samples(chunk, &dsp->data, (const int32_t **)t2, (int16_t *)dst);
1244 1311
@@ -1286,6 +1353,15 @@ int dsp_output_count(struct dsp_config *dsp, int count)
1286 */ 1353 */
1287 if (count > RESAMPLE_BUF_RIGHT_CHANNEL) 1354 if (count > RESAMPLE_BUF_RIGHT_CHANNEL)
1288 count = RESAMPLE_BUF_RIGHT_CHANNEL; 1355 count = RESAMPLE_BUF_RIGHT_CHANNEL;
1356
1357 /* If the limiter buffer is filling, some or all samples will
1358 * be captured by it, so expect fewer samples coming out. */
1359 if (limiter_buffer_active && !limiter_buffer_full)
1360 {
1361 int empty_space = limiter_buffer_count(false);
1362 count_adjust = MIN(empty_space, count);
1363 count -= count_adjust;
1364 }
1289 1365
1290 return count; 1366 return count;
1291} 1367}
@@ -1295,6 +1371,13 @@ int dsp_output_count(struct dsp_config *dsp, int count)
1295 */ 1371 */
1296int dsp_input_count(struct dsp_config *dsp, int count) 1372int dsp_input_count(struct dsp_config *dsp, int count)
1297{ 1373{
1374 /* If the limiter buffer is filling, the output count was
1375 * adjusted downward. This adjusts it back so that input
1376 * count is not affected.
1377 */
1378 if (limiter_buffer_active && !limiter_buffer_full)
1379 count += count_adjust;
1380
1298 /* count is now the number of resampled input samples. Convert to 1381 /* count is now the number of resampled input samples. Convert to
1299 original input samples. */ 1382 original input samples. */
1300 if (dsp->resample) 1383 if (dsp->resample)
@@ -1412,6 +1495,7 @@ intptr_t dsp_configure(struct dsp_config *dsp, int setting, intptr_t value)
1412 dsp_update_functions(dsp); 1495 dsp_update_functions(dsp);
1413 resampler_new_delta(dsp); 1496 resampler_new_delta(dsp);
1414 tdspeed_setup(dsp); 1497 tdspeed_setup(dsp);
1498 reset_limiter_buffer(dsp);
1415 break; 1499 break;
1416 1500
1417 case DSP_FLUSH: 1501 case DSP_FLUSH:
@@ -1420,6 +1504,7 @@ intptr_t dsp_configure(struct dsp_config *dsp, int setting, intptr_t value)
1420 resampler_new_delta(dsp); 1504 resampler_new_delta(dsp);
1421 dither_init(dsp); 1505 dither_init(dsp);
1422 tdspeed_setup(dsp); 1506 tdspeed_setup(dsp);
1507 reset_limiter_buffer(dsp);
1423 break; 1508 break;
1424 1509
1425 case DSP_SET_TRACK_GAIN: 1510 case DSP_SET_TRACK_GAIN:
@@ -1498,3 +1583,372 @@ void dsp_set_replaygain(void)
1498 replaygain = gain; 1583 replaygain = gain;
1499 set_gain(&AUDIO_DSP); 1584 set_gain(&AUDIO_DSP);
1500} 1585}
1586
1587/** RESET THE LIMITER BUFFER
1588 * Force the limiter buffer to its initial state and discard
1589 * any samples held there. */
1590static void reset_limiter_buffer(struct dsp_config *dsp)
1591{
1592 if (dsp == &AUDIO_DSP)
1593 {
1594 int i;
1595 logf(" reset_limiter_buffer");
1596 for (i = 0; i < 2; i++)
1597 start_lim_buf[i] = end_lim_buf[i] = limiter_buffer[i];
1598 start_peak = end_peak = lim_buf_peak;
1599 limiter_buffer_full = false;
1600 limiter_buffer_emptying = false;
1601 release_peak = 0;
1602 }
1603}
1604
1605/** OPERATE THE LIMITER BUFFER
1606 * Handle all samples entering or exiting the limiter buffer. */
1607static inline int set_limiter_buffer(int count, int32_t *buf[])
1608{
1609 int32_t *in_buf[] = {buf[0], buf[1]},
1610 *out_buf[] = {buf[0], buf[1]};
1611 int empty_space, i, out_count;
1612 const long clip_max = AUDIO_DSP.data.clip_max;
1613 const int ch = AUDIO_DSP.data.num_channels - 1;
1614 out_buf_peak_index = out_buf_peak;
1615
1616 if (limiter_buffer_emptying)
1617 /** EMPTY THE BUFFER
1618 * since the empty flag has been set, assume no inbound samples and
1619 return all samples in the limiter buffer to the outbound buffer */
1620 {
1621 count = limiter_buffer_count(true);
1622 out_count = count;
1623 logf(" Emptying limiter buffer: %d", count);
1624 while (count-- > 0)
1625 {
1626 for (i = 0; i <= ch; i++)
1627 {
1628 /* move samples in limiter buffer to output buffer */
1629 *out_buf[i]++ = *start_lim_buf[i]++;
1630 if (start_lim_buf[i] == &limiter_buffer[i][LIMITER_BUFFER_SIZE])
1631 start_lim_buf[i] = limiter_buffer[i];
1632 /* move limiter buffer peak values to output peak values */
1633 if (i == 0)
1634 {
1635 *out_buf_peak_index++ = *start_peak++;
1636 if (start_peak == &lim_buf_peak[LIMITER_BUFFER_SIZE])
1637 start_peak = lim_buf_peak;
1638 }
1639 }
1640 }
1641 reset_limiter_buffer(&AUDIO_DSP);
1642 }
1643 else /* limiter buffer NOT emptying */
1644 {
1645 if (count <= 0) return 0;
1646
1647 empty_space = limiter_buffer_count(false);
1648
1649 if (empty_space > 0)
1650 /** FILL BUFFER
1651 * use as many inbound samples as necessary to fill the buffer */
1652 {
1653 /* don't try to fill with more samples than available */
1654 if (empty_space > count)
1655 empty_space = count;
1656 logf(" Filling limiter buffer: %d", empty_space);
1657 while (empty_space-- > 0)
1658 {
1659 for (i = 0; i <= ch; i++)
1660 {
1661 /* put inbound samples in the limiter buffer */
1662 in_samp = *in_buf[i]++;
1663 *end_lim_buf[i]++ = in_samp;
1664 if (end_lim_buf[i] == &limiter_buffer[i][LIMITER_BUFFER_SIZE])
1665 end_lim_buf[i] = limiter_buffer[i];
1666 if (in_samp < 0) /* make positive for comparison */
1667 in_samp = -in_samp - 1;
1668 if (in_samp <= clip_max)
1669 in_samp = 0; /* disregard if not clipped */
1670 if (i == 0)
1671 samp0 = in_samp;
1672 if (i == ch)
1673 {
1674 /* assign peak value for each inbound sample pair */
1675 *end_peak++ = ((samp0 > 0) || (in_samp > 0)) ?
1676 get_peak_value(MAX(samp0, in_samp)) : 0;
1677 if (end_peak == &lim_buf_peak[LIMITER_BUFFER_SIZE])
1678 end_peak = lim_buf_peak;
1679 }
1680 }
1681 count--;
1682 }
1683 /* after buffer fills, the remaining inbound samples are cycled */
1684 }
1685
1686 limiter_buffer_full = (end_lim_buf[0] == start_lim_buf[0]);
1687 out_count = count;
1688
1689 /** CYCLE BUFFER
1690 * return buffered samples and backfill limiter buffer with new ones.
1691 * The buffer is always full when cycling. */
1692 while (count-- > 0)
1693 {
1694 for (i = 0; i <= ch; i++)
1695 {
1696 /* copy incoming sample */
1697 in_samp = *in_buf[i]++;
1698 /* put limiter buffer sample into outbound buffer */
1699 *out_buf[i]++ = *start_lim_buf[i]++;
1700 /* put incoming sample on the end of the limiter buffer */
1701 *end_lim_buf[i]++ = in_samp;
1702 /* ring buffer pointer wrap */
1703 if (start_lim_buf[i] == &limiter_buffer[i][LIMITER_BUFFER_SIZE])
1704 start_lim_buf[i] = limiter_buffer[i];
1705 if (end_lim_buf[i] == &limiter_buffer[i][LIMITER_BUFFER_SIZE])
1706 end_lim_buf[i] = limiter_buffer[i];
1707 if (in_samp < 0) /* make positive for comparison */
1708 in_samp = -in_samp - 1;
1709 if (in_samp <= clip_max)
1710 in_samp = 0; /* disregard if not clipped */
1711 if (i == 0)
1712 {
1713 samp0 = in_samp;
1714 /* assign outgoing sample its associated peak value */
1715 *out_buf_peak_index++ = *start_peak++;
1716 if (start_peak == &lim_buf_peak[LIMITER_BUFFER_SIZE])
1717 start_peak = lim_buf_peak;
1718 }
1719 if (i == ch)
1720 {
1721 /* assign peak value for each inbound sample pair */
1722 *end_peak++ = ((samp0 > 0) || (in_samp > 0)) ?
1723 get_peak_value(MAX(samp0, in_samp)) : 0;
1724 if (end_peak == &lim_buf_peak[LIMITER_BUFFER_SIZE])
1725 end_peak = lim_buf_peak;
1726 }
1727 }
1728 }
1729 }
1730
1731 return out_count;
1732}
1733
1734/** RETURN LIMITER BUFFER COUNT
1735 * If argument is true, returns number of samples in the buffer,
1736 * otherwise, returns empty space remaining */
1737static int limiter_buffer_count(bool buf_count)
1738{
1739 int count;
1740 if (limiter_buffer_full)
1741 count = LIMITER_BUFFER_SIZE;
1742 else if (end_lim_buf[0] >= start_lim_buf[0])
1743 count = (end_lim_buf[0] - start_lim_buf[0]);
1744 else
1745 count = (end_lim_buf[0] - start_lim_buf[0]) + LIMITER_BUFFER_SIZE;
1746 return buf_count ? count : (LIMITER_BUFFER_SIZE - count);
1747}
1748
1749/** FLUSH THE LIMITER BUFFER
1750 * Empties the limiter buffer into the buffer pointed to by the argument
1751 * and returns the number of samples in that buffer */
1752int dsp_flush_limiter_buffer(char *dest)
1753{
1754 if ((!limiter_buffer_active) || (limiter_buffer_count(true) <= 0))
1755 return 0;
1756
1757 logf(" dsp_flush_limiter_buffer");
1758 int32_t flush_buf[2][LIMITER_BUFFER_SIZE];
1759 int32_t *src[2] = {flush_buf[0], flush_buf[1]};
1760
1761 limiter_buffer_emptying = true;
1762 int count = limiter_process(0, src);
1763 AUDIO_DSP.output_samples(count, &AUDIO_DSP.data,
1764 (const int32_t **)src, (int16_t *)dest);
1765 return count;
1766}
1767
1768/** GET PEAK VALUE
1769 * Return a small value representing how much the sample is clipped. This
1770 * should only be called if a sample is actually clipped. Sample is a
1771 * positive value.
1772 */
1773static uint16_t get_peak_value(int32_t sample)
1774{
1775 const int frac_bits = AUDIO_DSP.frac_bits;
1776 int mid,
1777 hi = 48,
1778 lo = 0;
1779
1780 /* shift sample into 28 frac bit range for comparison */
1781 if (frac_bits > 28)
1782 sample >>= (frac_bits - 28);
1783 if (frac_bits < 28)
1784 sample <<= (28 - frac_bits);
1785
1786 /* if clipped out of range, return maximum value */
1787 if (sample >= clip_steps[48])
1788 return 48 * 90;
1789
1790 /* find amount of sample clipping on the table */
1791 do
1792 {
1793 mid = (hi + lo) / 2;
1794 if (sample < clip_steps[mid])
1795 hi = mid;
1796 else if (sample > clip_steps[mid])
1797 lo = mid;
1798 else
1799 return mid * 90;
1800 }
1801 while (hi > (lo + 1));
1802
1803 /* interpolate linearly between steps (less accurate but faster) */
1804 return ((hi-1) * 90) + (((sample - clip_steps[hi-1]) * 90) /
1805 (clip_steps[hi] - clip_steps[hi-1]));
1806}
1807
1808/** SET LIMITER
1809 * Called by the menu system to configure the limiter process */
1810void dsp_set_limiter(int limiter_level)
1811{
1812 if (limiter_level > 0)
1813 {
1814 if (!limiter_buffer_active)
1815 {
1816 /* enable limiter process */
1817 AUDIO_DSP.limiter_process = limiter_process;
1818 limiter_buffer_active = true;
1819 }
1820 /* limiter preamp is a gain factor in S7.24 format */
1821 long old_preamp = AUDIO_DSP.limiter_preamp;
1822 long new_preamp = fp_factor((((long)limiter_level << 24) / 10), 24);
1823 if (old_preamp != new_preamp)
1824 {
1825 AUDIO_DSP.limiter_preamp = new_preamp;
1826 set_gain(&AUDIO_DSP);
1827 logf(" Limiter enable: Yes\tLimiter amp: %.8f",
1828 (float)AUDIO_DSP.limiter_preamp / (1 << 24));
1829 }
1830 }
1831 else
1832 {
1833 /* disable limiter process*/
1834 if (limiter_buffer_active)
1835 {
1836 AUDIO_DSP.limiter_preamp = (1 << 24);
1837 set_gain(&AUDIO_DSP);
1838 /* pcmbuf_flush_limiter_buffer(); */
1839 limiter_buffer_active = false;
1840 AUDIO_DSP.limiter_process = NULL;
1841 reset_limiter_buffer(&AUDIO_DSP);
1842 logf(" Limiter enable: No\tLimiter amp: %.8f",
1843 (float)AUDIO_DSP.limiter_preamp / (1 << 24));
1844 }
1845 }
1846}
1847
1848/** LIMITER PROCESS
1849 * Checks pre-amplified signal for clipped samples and smoothly reduces gain
1850 * around the clipped samples using a preset attack/release schedule.
1851 */
1852static int limiter_process(int count, int32_t *buf[])
1853{
1854 /* Limiter process passes through if limiter buffer isn't active, or the
1855 * sample depth is too large for safe pre-amping */
1856 if ((!limiter_buffer_active) || (AUDIO_DSP.frac_bits > 29))
1857 return count;
1858
1859 count = set_limiter_buffer(count, buf);
1860
1861 if (count <= 0)
1862 return 0;
1863
1864 const int attack_slope = 15; /* 15:1 ratio between attack and release */
1865 const int buffer_count = limiter_buffer_count(true);
1866
1867 int i, ch;
1868 uint16_t max_peak = 0,
1869 gain_peak,
1870 gain_rem;
1871 long gain;
1872
1873 /* step through limiter buffer in reverse order, in order to find the
1874 * appropriate max_peak for modifying the output buffer */
1875 for (i = buffer_count - 1; i >= 0; i--)
1876 {
1877 const uint16_t peak_i = lim_buf_peak[(start_peak - lim_buf_peak + i) %
1878 LIMITER_BUFFER_SIZE];
1879 /* if no attack slope, nothing to do */
1880 if ((peak_i == 0) && (max_peak == 0)) continue;
1881 /* if new peak, start attack slope */
1882 if (peak_i >= max_peak)
1883 {
1884 max_peak = peak_i;
1885 }
1886 /* keep sloping */
1887 else
1888 {
1889 if (max_peak > attack_slope)
1890 max_peak -= attack_slope;
1891 else
1892 max_peak = 0;
1893 }
1894 }
1895 /* step through output buffer the same way, but this time modifying peak
1896 * values to create a smooth attack slope. */
1897 for (i = count - 1; i >= 0; i--)
1898 {
1899 /* if no attack slope, nothing to do */
1900 if ((out_buf_peak[i] == 0) && (max_peak == 0)) continue;
1901 /* if new peak, start attack slope */
1902 if (out_buf_peak[i] >= max_peak)
1903 {
1904 max_peak = out_buf_peak[i];
1905 }
1906 /* keep sloping */
1907 else
1908 {
1909 if (max_peak > attack_slope)
1910 max_peak -= attack_slope;
1911 else
1912 max_peak = 0;
1913 out_buf_peak[i] = max_peak;
1914 }
1915 }
1916 /* Now step forward through the output buffer, and modify the peak values
1917 * to establish a smooth, slow release slope.*/
1918 for (i = 0; i < count; i++)
1919 {
1920 /* if no release slope, nothing to do */
1921 if ((out_buf_peak[i] == 0) && (release_peak == 0)) continue;
1922 /* if new peak, start release slope */
1923 if (out_buf_peak[i] >= release_peak)
1924 {
1925 release_peak = out_buf_peak[i];
1926 }
1927 /* keep sloping */
1928 else
1929 {
1930 release_peak--;
1931 out_buf_peak[i] = release_peak;
1932 }
1933 }
1934 /* Implement the limiter: adjust gain of the outbound samples by the gain
1935 * amounts in the gain steps array corresponding to the peak values. */
1936 for (ch = 0; ch < AUDIO_DSP.data.num_channels; ch++)
1937 {
1938 int32_t *d = buf[ch];
1939 for (i = 0; i < count; i++)
1940 {
1941 if (out_buf_peak[i] > 0)
1942 {
1943 gain_peak = (out_buf_peak[i] + 1) / 90;
1944 gain_rem = (out_buf_peak[i] + 1) % 90;
1945 gain = gain_steps[gain_peak];
1946 if ((gain_peak < 48) && (gain_rem > 0))
1947 gain -= gain_rem * ((gain_steps[gain_peak] -
1948 gain_steps[gain_peak + 1]) / 90);
1949 d[i] = FRACMUL_SHL(d[i], gain, 3);
1950 }
1951 }
1952 }
1953 return count;
1954}
diff --git a/apps/dsp.h b/apps/dsp.h
index 58a5edb5e2..b2d8493445 100644
--- a/apps/dsp.h
+++ b/apps/dsp.h
@@ -26,6 +26,8 @@
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 */
30#define MAX_LIMITER_GAIN 80 /* 8 dB */
29enum 31enum
30{ 32{
31 STEREO_INTERLEAVED = 0, 33 STEREO_INTERLEAVED = 0,
@@ -80,5 +82,7 @@ int32_t sound_get_pitch(void);
80void dsp_set_timestretch(int32_t percent); 82void dsp_set_timestretch(int32_t percent);
81int32_t dsp_get_timestretch(void); 83int32_t dsp_get_timestretch(void);
82int dsp_callback(int msg, intptr_t param); 84int dsp_callback(int msg, intptr_t param);
85int dsp_flush_limiter_buffer(char *dest);
86void dsp_set_limiter(int limiter_level);
83 87
84#endif 88#endif
diff --git a/apps/lang/english.lang b/apps/lang/english.lang
index 23ab26aba6..53de28128f 100644
--- a/apps/lang/english.lang
+++ b/apps/lang/english.lang
@@ -12699,3 +12699,20 @@
12699 lcd_bitmap: "Scroll bar position" 12699 lcd_bitmap: "Scroll bar position"
12700 </voice> 12700 </voice>
12701</phrase> 12701</phrase>
12702<phrase>
12703 id: LANG_LIMITER
12704 desc: in sound settings
12705 user: core
12706 <source>
12707 *: none
12708 swcodec: "Limiter Preamp"
12709 </source>
12710 <dest>
12711 *: none
12712 swcodec: "Limiter Preamp"
12713 </dest>
12714 <voice>
12715 *: none
12716 swcodec: "Limiter Preamp"
12717 </voice>
12718</phrase>
diff --git a/apps/menus/sound_menu.c b/apps/menus/sound_menu.c
index 1723d9779e..678d495d07 100644
--- a/apps/menus/sound_menu.c
+++ b/apps/menus/sound_menu.c
@@ -105,6 +105,8 @@ 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,
109 &global_settings.limiter_level, lowlatency_callback);
108#endif 110#endif
109 111
110#if (CONFIG_CODEC == MAS3587F) || (CONFIG_CODEC == MAS3539F) 112#if (CONFIG_CODEC == MAS3587F) || (CONFIG_CODEC == MAS3539F)
@@ -138,6 +140,7 @@ MAKE_MENU(sound_settings, ID2P(LANG_SOUND_SETTINGS), NULL, Icon_Audio,
138#if CONFIG_CODEC == SWCODEC 140#if CONFIG_CODEC == SWCODEC
139 ,&crossfeed_menu, &equalizer_menu, &dithering_enabled 141 ,&crossfeed_menu, &equalizer_menu, &dithering_enabled
140 ,&timestretch_enabled 142 ,&timestretch_enabled
143 ,&limiter_level
141#endif 144#endif
142#if (CONFIG_CODEC == MAS3587F) || (CONFIG_CODEC == MAS3539F) 145#if (CONFIG_CODEC == MAS3587F) || (CONFIG_CODEC == MAS3539F)
143 ,&loudness,&avc,&superbass,&mdb_enable,&mdb_strength 146 ,&loudness,&avc,&superbass,&mdb_enable,&mdb_strength
diff --git a/apps/pcmbuf.c b/apps/pcmbuf.c
index 319e3e8044..66a4ed4128 100644
--- a/apps/pcmbuf.c
+++ b/apps/pcmbuf.c
@@ -1170,6 +1170,30 @@ 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
1173 if (audiobuffer_fillpos) 1175 if (audiobuffer_fillpos)
1174 pcmbuf_flush_fillpos(); 1176 pcmbuf_flush_fillpos();
1175} 1177}
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 4cb1c1acb7..12a41a253c 100644
--- a/apps/pcmbuf.h
+++ b/apps/pcmbuf.h
@@ -76,5 +76,6 @@ 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);
79 80
80#endif 81#endif
diff --git a/apps/plugin.c b/apps/plugin.c
index e9acc97824..674740cf83 100644
--- a/apps/plugin.c
+++ b/apps/plugin.c
@@ -668,7 +668,7 @@ static const struct plugin_api rockbox_api = {
668 appsversion, 668 appsversion,
669 /* new stuff at the end, sort into place next time 669 /* new stuff at the end, sort into place next time
670 the API gets incompatible */ 670 the API gets incompatible */
671 671 dsp_flush_limiter_buffer,
672}; 672};
673 673
674int plugin_load(const char* plugin, const void* parameter) 674int plugin_load(const char* plugin, const void* parameter)
diff --git a/apps/plugin.h b/apps/plugin.h
index 9616628e90..d47cf031ff 100644
--- a/apps/plugin.h
+++ b/apps/plugin.h
@@ -836,6 +836,7 @@ struct plugin_api {
836 const char *appsversion; 836 const char *appsversion;
837 /* new stuff at the end, sort into place next time 837 /* new stuff at the end, sort into place next time
838 the API gets incompatible */ 838 the API gets incompatible */
839 int (*dsp_flush_limiter_buffer)(char *dest);
839}; 840};
840 841
841/* plugin header */ 842/* plugin header */
diff --git a/apps/plugins/test_codec.c b/apps/plugins/test_codec.c
index bae4571067..22516ba373 100644
--- a/apps/plugins/test_codec.c
+++ b/apps/plugins/test_codec.c
@@ -810,10 +810,34 @@ 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);
813 } else { 816 } else {
814 /* Just test the file */ 817 /* Just test the file */
815 res = test_track(parameter); 818 res = test_track(parameter);
816 819
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
817 /* Close WAV file (if there was one) */ 841 /* Close WAV file (if there was one) */
818 if (wavinfo.fd >= 0) { 842 if (wavinfo.fd >= 0) {
819 close_wav(); 843 close_wav();
diff --git a/apps/settings.c b/apps/settings.c
index 48571db20e..b76468280e 100644
--- a/apps/settings.c
+++ b/apps/settings.c
@@ -950,6 +950,7 @@ void settings_apply(bool read_disk)
950 950
951 dsp_dither_enable(global_settings.dithering_enabled); 951 dsp_dither_enable(global_settings.dithering_enabled);
952 dsp_timestretch_enable(global_settings.timestretch_enabled); 952 dsp_timestretch_enable(global_settings.timestretch_enabled);
953 dsp_set_limiter(global_settings.limiter_level);
953#endif 954#endif
954 955
955#ifdef HAVE_SPDIF_POWER 956#ifdef HAVE_SPDIF_POWER
diff --git a/apps/settings.h b/apps/settings.h
index 2fc07078ce..c33b715b95 100644
--- a/apps/settings.h
+++ b/apps/settings.h
@@ -771,6 +771,11 @@ struct user_settings
771 int scrollbar_width; 771 int scrollbar_width;
772 bool scrollbar_position; /* true=left false=right */ 772 bool scrollbar_position; /* true=left false=right */
773#endif 773#endif
774
775#if CONFIG_CODEC == SWCODEC
776 int limiter_level;
777#endif
778
774}; 779};
775 780
776/** global variables **/ 781/** global variables **/
diff --git a/apps/settings_list.c b/apps/settings_list.c
index 86972a132d..9ea95573cf 100644
--- a/apps/settings_list.c
+++ b/apps/settings_list.c
@@ -1246,6 +1246,12 @@ const struct settings_list settings[] = {
1246 /* timestretch */ 1246 /* timestretch */
1247 OFFON_SETTING(F_SOUNDSETTING, timestretch_enabled, LANG_TIMESTRETCH, false, 1247 OFFON_SETTING(F_SOUNDSETTING, timestretch_enabled, LANG_TIMESTRETCH, false,
1248 "timestretch enabled", dsp_timestretch_enable), 1248 "timestretch enabled", dsp_timestretch_enable),
1249
1250 /* limiter */
1251 INT_SETTING_NOWRAP(F_SOUNDSETTING, limiter_level,
1252 LANG_LIMITER, 0,
1253 "limiter level", UNIT_DB, 0, MAX_LIMITER_GAIN,
1254 5, db_format, get_dec_talkid, dsp_set_limiter),
1249#endif 1255#endif
1250#ifdef HAVE_WM8758 1256#ifdef HAVE_WM8758
1251 SOUND_SETTING(F_NO_WRAP, bass_cutoff, LANG_BASS_CUTOFF, 1257 SOUND_SETTING(F_NO_WRAP, bass_cutoff, LANG_BASS_CUTOFF,