X-Git-Url: http://git.tuebingen.mpg.de/?p=paraslash.git;a=blobdiff_plain;f=compress.c;h=63c9b00245998ed873ab6b1ad7ff506a358a8e86;hp=bad4ada4c6a78f712a01bd8a635ef2328b0a3115;hb=ca9b84e496c63f8a016c042eb2a84916c6d385e1;hpb=7533926d69875da66c5dc45940f67cb5d659738e diff --git a/compress.c b/compress.c index bad4ada4..63c9b002 100644 --- a/compress.c +++ b/compress.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2006 Andre Noll + * Copyright (C) 2005-2007 Andre Noll * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,129 +19,101 @@ /** \file compress.c paraslash's dynamic audio range compressor */ /* - * Based on AudioCompress, (C) 2002-2004 M. Hari Nezumi + * Used ideas of AudioCompress, (C) 2002-2004 M. Hari Nezumi */ #include "para.h" #include "compress_filter.cmdline.h" #include "list.h" +#include "sched.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 */ - struct gengetopt_args_info *conf; + unsigned current_gain; + /** points to the configuration data for this instance of the compress filter */ + struct compress_filter_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 = PARA_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 = (3 * pcd->current_gain + + (1 << pcd->conf->inertia_arg)) / 4; + pcd->peak = 0; + } else + if (adjusted_sample > pcd->peak) + pcd->peak = adjusted_sample; + } else { + adjusted_sample = -((-sample * pcd->current_gain) + >> gain_shift); + if (unlikely(adjusted_sample < -32768)) { + adjusted_sample = -32768; + pcd->current_gain = (3 * pcd->current_gain + + (1 << pcd->conf->inertia_arg)) / 4; + pcd->peak = 0; + } else + if (-adjusted_sample > pcd->peak) + pcd->peak = -adjusted_sample; } - *op++ = 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; + } +// 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); } static void *compress_parse_config(int argc, char **argv) { - struct gengetopt_args_info *ret = para_calloc(sizeof(struct gengetopt_args_info)); + struct compress_filter_args_info *ret = para_calloc(sizeof(struct compress_filter_args_info)); if (!compress_cmdline_parser(argc, argv, ret)) return ret; free(ret); @@ -152,16 +124,20 @@ static void open_compress(struct filter_node *fn) { 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 */ +/** + * the init function of the compress filter + * + * \param f pointer to the struct to initialize + */ void compress_init(struct filter *f) { f->open = open_compress;