diff options
Diffstat (limited to 'apps/dsp.c')
-rw-r--r-- | apps/dsp.c | 638 |
1 files changed, 236 insertions, 402 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 | ||
140 | struct 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. */ | 267 | static struct compressor_menu c_menu; |
260 | #define MAX_COUNT MAX(SMALL_SAMPLE_BUF_COUNT * RESAMPLE_RATIO / 2, LIMITER_BUFFER_SIZE) | 268 | static int32_t comp_rel_slope IBSS_ATTR; /* S7.24 format */ |
261 | static int count_adjust; | 269 | static int32_t comp_makeup_gain IBSS_ATTR; /* S7.24 format */ |
262 | static bool limiter_buffer_active; | 270 | static int32_t comp_curve[65] IBSS_ATTR; /* S7.24 format */ |
263 | static bool limiter_buffer_full; | 271 | static int32_t gain_buffer[MAX_COUNT] IBSS_ATTR; |
264 | static bool limiter_buffer_emptying; | 272 | static int32_t release_gain IBSS_ATTR; |
265 | static int32_t limiter_buffer[2][LIMITER_BUFFER_SIZE] IBSS_ATTR; | 273 | |
266 | static int32_t *start_lim_buf[2] IBSS_ATTR, | 274 | static int compressor_process(int count, int32_t *buf[]); |
267 | *end_lim_buf[2] IBSS_ATTR; | ||
268 | static uint16_t lim_buf_peak[LIMITER_BUFFER_SIZE] IBSS_ATTR; | ||
269 | static uint16_t *start_peak IBSS_ATTR, | ||
270 | *end_peak IBSS_ATTR; | ||
271 | static uint16_t out_buf_peak[MAX_COUNT]; | ||
272 | static uint16_t *out_buf_peak_index IBSS_ATTR; | ||
273 | static uint16_t release_peak IBSS_ATTR; | ||
274 | static int32_t in_samp IBSS_ATTR, | ||
275 | samp0 IBSS_ATTR; | ||
276 | |||
277 | static void reset_limiter_buffer(struct dsp_config *dsp); | ||
278 | static int limiter_buffer_count(bool buf_count); | ||
279 | static int limiter_process(int count, int32_t *buf[]); | ||
280 | static 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. */ | ||
284 | const 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. */ | ||
297 | const 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 | */ |
1376 | int dsp_input_count(struct dsp_config *dsp, int count) | 1327 | int 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. */ | 1539 | void dsp_set_compressor(int c_threshold, int c_ratio, int c_gain, |
1594 | static 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. */ | ||
1611 | static 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 */ | ||
1742 | static 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 */ | ||
1757 | int 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 | */ | ||
1778 | static 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 */ | ||
1815 | void 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 | */ |
1857 | static int limiter_process(int count, int32_t *buf[]) | 1709 | static 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 | */ | ||
1735 | static 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 | } |