compress.c 4.29 KB
Newer Older
1 2 3 4 5 6 7 8
/* 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>
9
#include <stdint.h>
10 11 12 13 14 15 16 17 18
#include <stdlib.h>
#include <string.h>

#include "config.h"
#include "compress.h"

struct Compressor {
        //! The compressor's preferences
        struct CompressorConfig prefs;
19

20 21
        //! History of the peak values
        int *peaks;
22

23 24
        //! History of the gain values
        int *gain;
25

26 27
        //! History of clip amounts
        int *clipped;
28

29 30 31 32 33 34 35
        unsigned int pos;
        unsigned int bufsz;
};

struct Compressor *Compressor_new(unsigned int history)
{
	struct Compressor *obj = malloc(sizeof(struct Compressor));
36 37 38
	if (obj == NULL)
		/* out of memory, not much we can do */
		abort();
39 40 41 42 43 44 45 46

	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;
47

48
        Compressor_setHistory(obj, history);
49

50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
        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));
67 68 69 70
	if (data == NULL)
		/* out of memory, not much we can do */
		abort();

71 72 73 74 75 76 77 78 79
        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;
80

81 82 83 84 85 86 87 88 89 90 91
        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;
}

92
void Compressor_Process_int16(struct Compressor *obj, int16_t *audio,
93 94 95 96 97 98 99 100 101 102 103 104 105 106
                              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;
107

108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
	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;
134

135
        //! Adjust the gain with inertia from the previous gain value
136
        newGain = (curGain*((1 << prefs->smooth) - 1) + newGain)
137
                >> prefs->smooth;
138

139 140 141
        //! Make sure it's no more than the maximum gain value
        if (newGain > (prefs->maxgain << 10))
                newGain = prefs->maxgain << 10;
142

143 144 145 146 147 148 149 150 151 152 153
        //! 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;
        }
154

155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192
        //! 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;
}