diff options
Diffstat (limited to 'src/AudioCompress/compress.c')
-rw-r--r-- | src/AudioCompress/compress.c | 185 |
1 files changed, 185 insertions, 0 deletions
diff --git a/src/AudioCompress/compress.c b/src/AudioCompress/compress.c new file mode 100644 index 000000000..d5c08372c --- /dev/null +++ b/src/AudioCompress/compress.c @@ -0,0 +1,185 @@ +/* compress.c + * Compressor logic + * + * (c)2007 busybee (http://beesbuzz.biz/ + * Licensed under the terms of the LGPL. See the file COPYING for details. + */ + +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> + +#include "config.h" +#include "compress.h" + +struct Compressor { + //! The compressor's preferences + struct CompressorConfig prefs; + + //! History of the peak values + int *peaks; + + //! History of the gain values + int *gain; + + //! History of clip amounts + int *clipped; + + unsigned int pos; + unsigned int bufsz; +}; + +struct Compressor *Compressor_new(unsigned int history) +{ + struct Compressor *obj = malloc(sizeof(struct Compressor)); + + obj->prefs.target = TARGET; + obj->prefs.maxgain = GAINMAX; + obj->prefs.smooth = GAINSMOOTH; + + obj->peaks = obj->gain = obj->clipped = NULL; + obj->bufsz = 0; + obj->pos = 0; + + Compressor_setHistory(obj, history); + + return obj; +} + +void Compressor_delete(struct Compressor *obj) +{ + if (obj->peaks) + free(obj->peaks); + if (obj->gain) + free(obj->gain); + if (obj->clipped) + free(obj->clipped); + free(obj); +} + +static int *resizeArray(int *data, int newsz, int oldsz) +{ + data = realloc(data, newsz*sizeof(int)); + if (newsz > oldsz) + memset(data + oldsz, 0, sizeof(int)*(newsz - oldsz)); + return data; +} + +void Compressor_setHistory(struct Compressor *obj, unsigned int history) +{ + if (!history) + history = BUCKETS; + + obj->peaks = resizeArray(obj->peaks, history, obj->bufsz); + obj->gain = resizeArray(obj->gain, history, obj->bufsz); + obj->clipped = resizeArray(obj->clipped, history, obj->bufsz); + obj->bufsz = history; +} + +struct CompressorConfig *Compressor_getConfig(struct Compressor *obj) +{ + return &obj->prefs; +} + +void Compressor_Process_int16(struct Compressor *obj, int16_t *audio, + unsigned int count) +{ + struct CompressorConfig *prefs = Compressor_getConfig(obj); + int16_t *ap; + unsigned int i; + int *peaks = obj->peaks; + int curGain = obj->gain[obj->pos]; + int newGain; + int peakVal = 1; + int peakPos = 0; + int slot = (obj->pos + 1) % obj->bufsz; + int *clipped = obj->clipped + slot; + unsigned int ramp = count; + int delta; + + ap = audio; + for (i = 0; i < count; i++) + { + int val = *ap++; + if (val < 0) + val = -val; + if (val > peakVal) + { + peakVal = val; + peakPos = i; + } + } + peaks[slot] = peakVal; + + + for (i = 0; i < obj->bufsz; i++) + { + if (peaks[i] > peakVal) + { + peakVal = peaks[i]; + peakPos = 0; + } + } + + //! Determine target gain + newGain = (1 << 10)*prefs->target/peakVal; + + //! Adjust the gain with inertia from the previous gain value + newGain = (curGain*((1 << prefs->smooth) - 1) + newGain) + >> prefs->smooth; + + //! Make sure it's no more than the maximum gain value + if (newGain > (prefs->maxgain << 10)) + newGain = prefs->maxgain << 10; + + //! Make sure it's no less than 1:1 + if (newGain < (1 << 10)) + newGain = 1 << 10; + + //! Make sure the adjusted gain won't cause clipping + if ((peakVal*newGain >> 10) > 32767) + { + newGain = (32767 << 10)/peakVal; + //! Truncate the ramp time + ramp = peakPos; + } + + //! Record the new gain + obj->gain[slot] = newGain; + + if (!ramp) + ramp = 1; + if (!curGain) + curGain = 1 << 10; + delta = (newGain - curGain) / (int)ramp; + + ap = audio; + *clipped = 0; + for (i = 0; i < count; i++) + { + int sample; + + //! Amplify the sample + sample = *ap*curGain >> 10; + if (sample < -32768) + { + *clipped += -32768 - sample; + sample = -32768; + } else if (sample > 32767) + { + *clipped += sample - 32767; + sample = 32767; + } + *ap++ = sample; + + //! Adjust the gain + if (i < ramp) + curGain += delta; + else + curGain = newGain; + } + + obj->pos = slot; +} + |