/** \file compress.c paraslash's dynamic audio range compressor */
/*
- * Based on AudioCompress, (C) 2002-2004 M. Hari Nezumi <magenta@trikuare.cx>
+ * Used ideas of AudioCompress, (C) 2002-2004 M. Hari Nezumi <magenta@trikuare.cx>
*/
#include "para.h"
#include "filter.h"
#include "string.h"
-/** how fine-grained the gain is */
-#define GAINSHIFT 10
/** the size of the output data buffer */
#define COMPRESS_CHUNK_SIZE 40960
/** data specific to the compress filter */
struct private_compress_data {
- /** an array holding the previous peak values */
- int *peaks;
- /** current bucket number to be modified */
- unsigned pn;
- /** number of times clipping occured */
- unsigned clip;
/** the current multiplier */
- int current_gain;
- /** the target multiplier */
- int target_gain;
- /** pointer to the configuration data for this instance of the compress filter */
+ unsigned current_gain;
+ /** points to the configuration data for this instance of the compress filter */
struct gengetopt_args_info *conf;
+ /** minimal admissible gain */
+ unsigned min_gain;
+ /** maximal admissible gain */
+ unsigned max_gain;
+ /** number of samples already seen */
+ unsigned num_samples;
+ /** absolute value of the maximal sample in the current block */
+ unsigned peak;
};
static ssize_t compress(char *inbuf, size_t inbuf_len, struct filter_node *fn)
{
- int16_t *audio = (int16_t *) inbuf, *ip = audio, *op;
- int peak = 1, pos = 0, i, gr, gf, gn;
- size_t length = MIN((inbuf_len / 2) * 2, (fn->bufsize - fn->loaded) / 2 * 2);
+ size_t i, length = MIN((inbuf_len / 2) * 2, (fn->bufsize - fn->loaded) / 2 * 2);
struct private_compress_data *pcd = fn->private_data;
+ int16_t *ip = (int16_t *)inbuf, *op = (int16_t *)(fn->buf + fn->loaded);
+ unsigned gain_shift = pcd->conf->inertia_arg + pcd->conf->damp_arg,
+ mask = (1 << pcd->conf->blocksize_arg) - 1;
if (!length)
return 0;
- /* determine peak's value and position */
- for (i = 0; i < length / 2; i++, ip++) {
- int val = ABS(*ip);
- if (val > peak) {
- peak = val;
- pos = i;
- }
- }
- pcd->peaks[pcd->pn] = peak;
- for (i = 0; i < pcd->conf->buckets_arg; i++) {
- if (pcd->peaks[i] > peak) {
- peak = pcd->peaks[i];
- pos = 0;
- }
- }
- /* determine target gain */
- gn = (1 << GAINSHIFT) * pcd->conf->target_level_arg / peak;
- if (gn < (1 << GAINSHIFT))
- gn = 1 << GAINSHIFT;
- pcd->target_gain = (pcd->target_gain * ((1 << pcd->conf->gain_smooth_arg) - 1) + gn)
- >> pcd->conf->gain_smooth_arg;
- /* give it an extra insignificant nudge to counteract possible
- * rounding error
- */
- if (gn < pcd->target_gain)
- pcd->target_gain--;
- else if (gn > pcd->target_gain)
- pcd->target_gain++;
- if (pcd->target_gain > pcd->conf->gain_max_arg << GAINSHIFT)
- pcd->target_gain = pcd->conf->gain_max_arg << GAINSHIFT;
- /* see if a peak is going to clip */
- gn = (1 << GAINSHIFT) * 32768 / peak;
- if (gn < pcd->target_gain) {
- pcd->target_gain = gn;
- if (pcd->conf->anticlip_given)
- pos = 0;
- } else
- /* we're ramping up, so draw it out over the whole frame */
- pos = length;
- /* determine gain rate necessary to make target */
- if (!pos)
- pos = 1;
- gr = ((pcd->target_gain - pcd->current_gain) << 16) / pos;
- gf = pcd->current_gain << 16;
- ip = audio;
- op = (int16_t *)(fn->buf + fn->loaded);
for (i = 0; i < length / 2; i++) {
- int sample;
- /* interpolate the gain */
- pcd->current_gain = gf >> 16;
- if (i < pos)
- gf += gr;
- else if (i == pos)
- gf = pcd->target_gain << 16;
- /* amplify */
- sample = (*ip++) * pcd->current_gain * pcd->conf->volume_arg / 10 >> GAINSHIFT;
- if (sample < -32768) {
- pcd->clip++;
- sample = -32768;
- } else if (sample > 32767) {
- pcd->clip++;
- sample = 32767;
+ /* be careful in that heat, my dear */
+ int sample = *ip++, adjusted_sample;
+
+ if (sample > 0) {
+ adjusted_sample = (sample * pcd->current_gain)
+ >> gain_shift;
+ if (unlikely(adjusted_sample > 32767)) {
+ adjusted_sample = 32767;
+ pcd->current_gain = (pcd->current_gain +
+ (1 << pcd->conf->inertia_arg)) / 2;
+ pcd->peak = 0;
+ } else
+ if (adjusted_sample > pcd->peak)
+ pcd->peak = sample;
+ } else {
+ adjusted_sample = -((-sample * pcd->current_gain)
+ >> gain_shift);
+ if (unlikely(adjusted_sample < -32768)) {
+ adjusted_sample = -32768;
+ pcd->current_gain = (pcd->current_gain +
+ (1 << pcd->conf->inertia_arg)) / 2;
+ pcd->peak = 0;
+ } else
+ if (-adjusted_sample > pcd->peak)
+ pcd->peak = -adjusted_sample;
+ }
+ *op++ = adjusted_sample;
+ if (likely(++pcd->num_samples & mask))
+ continue;
+ if (pcd->peak < pcd->conf->target_level_arg) {
+ if (pcd->current_gain < pcd->max_gain)
+ pcd->current_gain++;
+ } else {
+ if (pcd->current_gain > pcd->min_gain + 1)
+ pcd->current_gain -= 2;
}
- *op++ = sample;
+// PARA_DEBUG_LOG("gain: %lu, peak: %d\n", pcd->current_gain,
+// pcd->peak);
+ pcd->peak = 0;
+// PARA_INFO_LOG("sample: %lu\n", ABS(sample));
}
- pcd->pn = (pcd->pn + 1) % pcd->conf->buckets_arg;
- PARA_DEBUG_LOG("bucket: %03i, input len: %zd, length: %zd, peak: %05i, "
- "current gain: %03i, clipped: %d\n", pcd->pn, inbuf_len,
- length, peak, pcd->current_gain, pcd->clip);
- fn->loaded = length;
+ fn->loaded += length;
return length;
}
static void close_compress(struct filter_node *fn)
{
- struct private_compress_data *pcd = fn->private_data;
- free(pcd->peaks);
free(fn->private_data);
free(fn->buf);
}
{
struct private_compress_data *pcd = para_calloc(
sizeof(struct private_compress_data));
-// compress_cmdline_parser(fn->argc, fn->argv, &pcd->conf);
pcd->conf = fn->conf;
- pcd->peaks = para_calloc(pcd->conf->buckets_arg * sizeof(int));
fn->private_data = pcd;
fn->bufsize = COMPRESS_CHUNK_SIZE;
fn->buf = para_malloc(fn->bufsize);
- fn->loaded = 0;
+ pcd->current_gain = 1 << pcd->conf->inertia_arg;
+ pcd->min_gain = 1 << (pcd->conf->inertia_arg - pcd->conf->aggressiveness_arg);
+ pcd->max_gain = 1 << (pcd->conf->inertia_arg + pcd->conf->aggressiveness_arg);
}
/** the init function of the compress filter */