xref: /titanic_50/usr/src/cmd/audio/utilities/AudioGain.cc (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
1*7c478bd9Sstevel@tonic-gate /*
2*7c478bd9Sstevel@tonic-gate  * CDDL HEADER START
3*7c478bd9Sstevel@tonic-gate  *
4*7c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*7c478bd9Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*7c478bd9Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*7c478bd9Sstevel@tonic-gate  * with the License.
8*7c478bd9Sstevel@tonic-gate  *
9*7c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*7c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*7c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*7c478bd9Sstevel@tonic-gate  * and limitations under the License.
13*7c478bd9Sstevel@tonic-gate  *
14*7c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*7c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*7c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*7c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*7c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*7c478bd9Sstevel@tonic-gate  *
20*7c478bd9Sstevel@tonic-gate  * CDDL HEADER END
21*7c478bd9Sstevel@tonic-gate  */
22*7c478bd9Sstevel@tonic-gate /*
23*7c478bd9Sstevel@tonic-gate  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24*7c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
25*7c478bd9Sstevel@tonic-gate  */
26*7c478bd9Sstevel@tonic-gate 
27*7c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*7c478bd9Sstevel@tonic-gate 
29*7c478bd9Sstevel@tonic-gate #include <stdio.h>
30*7c478bd9Sstevel@tonic-gate #include <malloc.h>
31*7c478bd9Sstevel@tonic-gate #include <math.h>
32*7c478bd9Sstevel@tonic-gate #include <errno.h>
33*7c478bd9Sstevel@tonic-gate #include <memory.h>
34*7c478bd9Sstevel@tonic-gate #include <sys/param.h>
35*7c478bd9Sstevel@tonic-gate #include <sys/types.h>
36*7c478bd9Sstevel@tonic-gate #include <sys/ioctl.h>
37*7c478bd9Sstevel@tonic-gate 
38*7c478bd9Sstevel@tonic-gate #include <AudioGain.h>
39*7c478bd9Sstevel@tonic-gate #include <AudioTypePcm.h>
40*7c478bd9Sstevel@tonic-gate 
41*7c478bd9Sstevel@tonic-gate #define	irint(d)	((int)d)
42*7c478bd9Sstevel@tonic-gate 
43*7c478bd9Sstevel@tonic-gate 
44*7c478bd9Sstevel@tonic-gate // initialize constants for instananeous gain normalization
45*7c478bd9Sstevel@tonic-gate const double	AudioGain::LoSigInstantRange	= .008;
46*7c478bd9Sstevel@tonic-gate const double	AudioGain::HiSigInstantRange	= .48;
47*7c478bd9Sstevel@tonic-gate 
48*7c478bd9Sstevel@tonic-gate // initialize constants for weighted gain normalization
49*7c478bd9Sstevel@tonic-gate const double	AudioGain::NoSigWeight		= .0000;
50*7c478bd9Sstevel@tonic-gate const double	AudioGain::LoSigWeightRange	= .001;
51*7c478bd9Sstevel@tonic-gate const double	AudioGain::HiSigWeightRange	= .050;
52*7c478bd9Sstevel@tonic-gate 
53*7c478bd9Sstevel@tonic-gate // u-law max value converted to floating point
54*7c478bd9Sstevel@tonic-gate const double	AudioGain::PeakSig		= .9803765;
55*7c478bd9Sstevel@tonic-gate 
56*7c478bd9Sstevel@tonic-gate // XXX - patchable dc time constant:  TC = 1 / (sample rate / DCfreq)
57*7c478bd9Sstevel@tonic-gate int DCfreq = 500;
58*7c478bd9Sstevel@tonic-gate const double	AudioGain::DCtimeconstant	= .1;
59*7c478bd9Sstevel@tonic-gate 
60*7c478bd9Sstevel@tonic-gate // patchable debugging flag
61*7c478bd9Sstevel@tonic-gate int debug_agc = 0;
62*7c478bd9Sstevel@tonic-gate 
63*7c478bd9Sstevel@tonic-gate 
64*7c478bd9Sstevel@tonic-gate // Constructor
65*7c478bd9Sstevel@tonic-gate AudioGain::
AudioGain()66*7c478bd9Sstevel@tonic-gate AudioGain():
67*7c478bd9Sstevel@tonic-gate 	clipcnt(0), DCaverage(0.), instant_gain(0.),
68*7c478bd9Sstevel@tonic-gate 	weighted_peaksum(0.), weighted_sum(0.),
69*7c478bd9Sstevel@tonic-gate 	weighted_avgsum(0.), weighted_cnt(0),
70*7c478bd9Sstevel@tonic-gate 	gain_cache(NULL)
71*7c478bd9Sstevel@tonic-gate {
72*7c478bd9Sstevel@tonic-gate }
73*7c478bd9Sstevel@tonic-gate 
74*7c478bd9Sstevel@tonic-gate // Destructor
75*7c478bd9Sstevel@tonic-gate AudioGain::
~AudioGain()76*7c478bd9Sstevel@tonic-gate ~AudioGain()
77*7c478bd9Sstevel@tonic-gate {
78*7c478bd9Sstevel@tonic-gate 	if (gain_cache != NULL) {
79*7c478bd9Sstevel@tonic-gate 		delete gain_cache;
80*7c478bd9Sstevel@tonic-gate 	}
81*7c478bd9Sstevel@tonic-gate }
82*7c478bd9Sstevel@tonic-gate 
83*7c478bd9Sstevel@tonic-gate // Return TRUE if we can handle this data type
84*7c478bd9Sstevel@tonic-gate Boolean AudioGain::
CanConvert(const AudioHdr & hdr) const85*7c478bd9Sstevel@tonic-gate CanConvert(
86*7c478bd9Sstevel@tonic-gate 	const AudioHdr&	hdr) const
87*7c478bd9Sstevel@tonic-gate {
88*7c478bd9Sstevel@tonic-gate 	return (float_convert.CanConvert(hdr));
89*7c478bd9Sstevel@tonic-gate }
90*7c478bd9Sstevel@tonic-gate 
91*7c478bd9Sstevel@tonic-gate // Return latest instantaneous gain
92*7c478bd9Sstevel@tonic-gate double AudioGain::
InstantGain()93*7c478bd9Sstevel@tonic-gate InstantGain()
94*7c478bd9Sstevel@tonic-gate {
95*7c478bd9Sstevel@tonic-gate 	return ((double)instant_gain);
96*7c478bd9Sstevel@tonic-gate }
97*7c478bd9Sstevel@tonic-gate 
98*7c478bd9Sstevel@tonic-gate // Return latest weighted gain
99*7c478bd9Sstevel@tonic-gate double AudioGain::
WeightedGain()100*7c478bd9Sstevel@tonic-gate WeightedGain()
101*7c478bd9Sstevel@tonic-gate {
102*7c478bd9Sstevel@tonic-gate 	double		g;
103*7c478bd9Sstevel@tonic-gate 
104*7c478bd9Sstevel@tonic-gate 	// Accumulated sum is averaged by the cache size and number of sums
105*7c478bd9Sstevel@tonic-gate 	if ((weighted_cnt > 0) && (gain_cache_size > 0.)) {
106*7c478bd9Sstevel@tonic-gate 		g = weighted_avgsum / gain_cache_size;
107*7c478bd9Sstevel@tonic-gate 		g /=  weighted_cnt;
108*7c478bd9Sstevel@tonic-gate 		g -= NoSigWeight;
109*7c478bd9Sstevel@tonic-gate 		if (g > HiSigWeightRange) {
110*7c478bd9Sstevel@tonic-gate 			g = 1.;
111*7c478bd9Sstevel@tonic-gate 		} else if (g < 0.) {
112*7c478bd9Sstevel@tonic-gate 			g = 0.;
113*7c478bd9Sstevel@tonic-gate 		} else {
114*7c478bd9Sstevel@tonic-gate 			g /= HiSigWeightRange;
115*7c478bd9Sstevel@tonic-gate 		}
116*7c478bd9Sstevel@tonic-gate 	} else {
117*7c478bd9Sstevel@tonic-gate 		g = 0.;
118*7c478bd9Sstevel@tonic-gate 	}
119*7c478bd9Sstevel@tonic-gate 	return (g);
120*7c478bd9Sstevel@tonic-gate }
121*7c478bd9Sstevel@tonic-gate 
122*7c478bd9Sstevel@tonic-gate // Return latest weighted peak
123*7c478bd9Sstevel@tonic-gate // Clears the weighted peak for next calculation.
124*7c478bd9Sstevel@tonic-gate double AudioGain::
WeightedPeak()125*7c478bd9Sstevel@tonic-gate WeightedPeak()
126*7c478bd9Sstevel@tonic-gate {
127*7c478bd9Sstevel@tonic-gate 	double		g;
128*7c478bd9Sstevel@tonic-gate 
129*7c478bd9Sstevel@tonic-gate 	// Peak sum is averaged by the cache size
130*7c478bd9Sstevel@tonic-gate 	if (gain_cache_size > 0.) {
131*7c478bd9Sstevel@tonic-gate 		g = weighted_peaksum / gain_cache_size;
132*7c478bd9Sstevel@tonic-gate 		g -= NoSigWeight;
133*7c478bd9Sstevel@tonic-gate 		if (g > HiSigWeightRange) {
134*7c478bd9Sstevel@tonic-gate 			g = 1.;
135*7c478bd9Sstevel@tonic-gate 		} else if (g < 0.) {
136*7c478bd9Sstevel@tonic-gate 			g = 0.;
137*7c478bd9Sstevel@tonic-gate 		} else {
138*7c478bd9Sstevel@tonic-gate 			g /= HiSigWeightRange;
139*7c478bd9Sstevel@tonic-gate 		}
140*7c478bd9Sstevel@tonic-gate 	} else {
141*7c478bd9Sstevel@tonic-gate 		g = 0.;
142*7c478bd9Sstevel@tonic-gate 	}
143*7c478bd9Sstevel@tonic-gate 	weighted_peaksum = 0.;
144*7c478bd9Sstevel@tonic-gate 	return (g);
145*7c478bd9Sstevel@tonic-gate }
146*7c478bd9Sstevel@tonic-gate 
147*7c478bd9Sstevel@tonic-gate // Return TRUE if signal clipped during last processed buffer
148*7c478bd9Sstevel@tonic-gate Boolean AudioGain::
Clipped()149*7c478bd9Sstevel@tonic-gate Clipped()
150*7c478bd9Sstevel@tonic-gate {
151*7c478bd9Sstevel@tonic-gate 	Boolean		clipped;
152*7c478bd9Sstevel@tonic-gate 
153*7c478bd9Sstevel@tonic-gate 	clipped = (clipcnt > 0);
154*7c478bd9Sstevel@tonic-gate 	return (clipped);
155*7c478bd9Sstevel@tonic-gate }
156*7c478bd9Sstevel@tonic-gate 
157*7c478bd9Sstevel@tonic-gate // Flush gain state
158*7c478bd9Sstevel@tonic-gate void AudioGain::
Flush()159*7c478bd9Sstevel@tonic-gate Flush()
160*7c478bd9Sstevel@tonic-gate {
161*7c478bd9Sstevel@tonic-gate 	clipcnt = 0;
162*7c478bd9Sstevel@tonic-gate 	DCaverage = 0.;
163*7c478bd9Sstevel@tonic-gate 	instant_gain = 0.;
164*7c478bd9Sstevel@tonic-gate 	weighted_peaksum = 0.;
165*7c478bd9Sstevel@tonic-gate 	weighted_sum = 0.;
166*7c478bd9Sstevel@tonic-gate 	weighted_avgsum = 0.;
167*7c478bd9Sstevel@tonic-gate 	weighted_cnt = 0;
168*7c478bd9Sstevel@tonic-gate 	if (gain_cache != NULL) {
169*7c478bd9Sstevel@tonic-gate 		delete gain_cache;
170*7c478bd9Sstevel@tonic-gate 		gain_cache = NULL;
171*7c478bd9Sstevel@tonic-gate 	}
172*7c478bd9Sstevel@tonic-gate }
173*7c478bd9Sstevel@tonic-gate 
174*7c478bd9Sstevel@tonic-gate // Process an input buffer according to the specified flags
175*7c478bd9Sstevel@tonic-gate // The input buffer is consumed if the reference count is zero!
176*7c478bd9Sstevel@tonic-gate AudioError AudioGain::
Process(AudioBuffer * inbuf,int type)177*7c478bd9Sstevel@tonic-gate Process(
178*7c478bd9Sstevel@tonic-gate 	AudioBuffer*	inbuf,
179*7c478bd9Sstevel@tonic-gate 	int		type)
180*7c478bd9Sstevel@tonic-gate {
181*7c478bd9Sstevel@tonic-gate 	AudioHdr	newhdr;
182*7c478bd9Sstevel@tonic-gate 	AudioError	err;
183*7c478bd9Sstevel@tonic-gate 
184*7c478bd9Sstevel@tonic-gate 	if (inbuf == NULL)
185*7c478bd9Sstevel@tonic-gate 		return (AUDIO_ERR_BADARG);
186*7c478bd9Sstevel@tonic-gate 
187*7c478bd9Sstevel@tonic-gate 	if (Undefined(inbuf->GetLength())) {
188*7c478bd9Sstevel@tonic-gate 		err = AUDIO_ERR_BADARG;
189*7c478bd9Sstevel@tonic-gate process_error:
190*7c478bd9Sstevel@tonic-gate 		// report error and toss the buffer if it is not referenced
191*7c478bd9Sstevel@tonic-gate 		inbuf->RaiseError(err);
192*7c478bd9Sstevel@tonic-gate 		inbuf->Reference();
193*7c478bd9Sstevel@tonic-gate 		inbuf->Dereference();
194*7c478bd9Sstevel@tonic-gate 		return (err);
195*7c478bd9Sstevel@tonic-gate 	}
196*7c478bd9Sstevel@tonic-gate 
197*7c478bd9Sstevel@tonic-gate 	// Set up to convert to floating point; verify all header formats
198*7c478bd9Sstevel@tonic-gate 	newhdr = inbuf->GetHeader();
199*7c478bd9Sstevel@tonic-gate 	if (!float_convert.CanConvert(newhdr)) {
200*7c478bd9Sstevel@tonic-gate 		err = AUDIO_ERR_HDRINVAL;
201*7c478bd9Sstevel@tonic-gate 		goto process_error;
202*7c478bd9Sstevel@tonic-gate 	}
203*7c478bd9Sstevel@tonic-gate 	newhdr.encoding = FLOAT;
204*7c478bd9Sstevel@tonic-gate 	newhdr.bytes_per_unit = 8;
205*7c478bd9Sstevel@tonic-gate 	if ((err = newhdr.Validate()) || !float_convert.CanConvert(newhdr)) {
206*7c478bd9Sstevel@tonic-gate 		err = AUDIO_ERR_HDRINVAL;
207*7c478bd9Sstevel@tonic-gate 		goto process_error;
208*7c478bd9Sstevel@tonic-gate 	}
209*7c478bd9Sstevel@tonic-gate 
210*7c478bd9Sstevel@tonic-gate 	// Convert to floating-point up front, if necessary
211*7c478bd9Sstevel@tonic-gate 	if (inbuf->GetHeader() != newhdr) {
212*7c478bd9Sstevel@tonic-gate 		err = float_convert.Convert(inbuf, newhdr);
213*7c478bd9Sstevel@tonic-gate 		if (err)
214*7c478bd9Sstevel@tonic-gate 			goto process_error;
215*7c478bd9Sstevel@tonic-gate 	}
216*7c478bd9Sstevel@tonic-gate 
217*7c478bd9Sstevel@tonic-gate 	// Reference the resulting buffer to make sure it gets ditched later
218*7c478bd9Sstevel@tonic-gate 	inbuf->Reference();
219*7c478bd9Sstevel@tonic-gate 
220*7c478bd9Sstevel@tonic-gate 	// run through highpass filter to reject DC
221*7c478bd9Sstevel@tonic-gate 	process_dcfilter(inbuf);
222*7c478bd9Sstevel@tonic-gate 
223*7c478bd9Sstevel@tonic-gate 	if (type & AUDIO_GAIN_INSTANT)
224*7c478bd9Sstevel@tonic-gate 		process_instant(inbuf);
225*7c478bd9Sstevel@tonic-gate 
226*7c478bd9Sstevel@tonic-gate 	if (type & AUDIO_GAIN_WEIGHTED)
227*7c478bd9Sstevel@tonic-gate 		process_weighted(inbuf);
228*7c478bd9Sstevel@tonic-gate 
229*7c478bd9Sstevel@tonic-gate 	inbuf->Dereference();
230*7c478bd9Sstevel@tonic-gate 	return (AUDIO_SUCCESS);
231*7c478bd9Sstevel@tonic-gate }
232*7c478bd9Sstevel@tonic-gate 
233*7c478bd9Sstevel@tonic-gate // Run the buffer through a simple, dc filter.
234*7c478bd9Sstevel@tonic-gate // Buffer is assumed to be floating-point double PCM
235*7c478bd9Sstevel@tonic-gate void AudioGain::
process_dcfilter(AudioBuffer * inbuf)236*7c478bd9Sstevel@tonic-gate process_dcfilter(
237*7c478bd9Sstevel@tonic-gate 	AudioBuffer*	inbuf)
238*7c478bd9Sstevel@tonic-gate {
239*7c478bd9Sstevel@tonic-gate 	int		i;
240*7c478bd9Sstevel@tonic-gate 	Boolean		lastpeak;
241*7c478bd9Sstevel@tonic-gate 	double		val;
242*7c478bd9Sstevel@tonic-gate 	double		dcweight;
243*7c478bd9Sstevel@tonic-gate 	double		timeconstant;
244*7c478bd9Sstevel@tonic-gate 	AudioHdr	inhdr;
245*7c478bd9Sstevel@tonic-gate 	double		*inptr;
246*7c478bd9Sstevel@tonic-gate 	size_t		frames;
247*7c478bd9Sstevel@tonic-gate 
248*7c478bd9Sstevel@tonic-gate 	inhdr = inbuf->GetHeader();
249*7c478bd9Sstevel@tonic-gate 	inptr = (double *)inbuf->GetAddress();
250*7c478bd9Sstevel@tonic-gate 	frames = (size_t)inhdr.Time_to_Samples(inbuf->GetLength());
251*7c478bd9Sstevel@tonic-gate 	clipcnt = 0;
252*7c478bd9Sstevel@tonic-gate 	lastpeak = FALSE;
253*7c478bd9Sstevel@tonic-gate 
254*7c478bd9Sstevel@tonic-gate 	// Time constant corresponds to the number of samples for 500Hz
255*7c478bd9Sstevel@tonic-gate 	timeconstant = 1. / (inhdr.sample_rate / (double)DCfreq);
256*7c478bd9Sstevel@tonic-gate 	dcweight = 1. - timeconstant;
257*7c478bd9Sstevel@tonic-gate 
258*7c478bd9Sstevel@tonic-gate 	// loop through the input buffer, rewriting with weighted data
259*7c478bd9Sstevel@tonic-gate 	// XXX - should deal with multi-channel data!
260*7c478bd9Sstevel@tonic-gate 	// XXX - for now, check first channel only
261*7c478bd9Sstevel@tonic-gate 	for (i = 0; i < frames; i++, inptr += inhdr.channels) {
262*7c478bd9Sstevel@tonic-gate 		val = *inptr;
263*7c478bd9Sstevel@tonic-gate 
264*7c478bd9Sstevel@tonic-gate 		// Two max values in a row constitutes clipping
265*7c478bd9Sstevel@tonic-gate 		if ((val >= PeakSig) || (val <= -PeakSig)) {
266*7c478bd9Sstevel@tonic-gate 			if (lastpeak) {
267*7c478bd9Sstevel@tonic-gate 				clipcnt++;
268*7c478bd9Sstevel@tonic-gate 			} else {
269*7c478bd9Sstevel@tonic-gate 				lastpeak = TRUE;
270*7c478bd9Sstevel@tonic-gate 			}
271*7c478bd9Sstevel@tonic-gate 		} else {
272*7c478bd9Sstevel@tonic-gate 			lastpeak = FALSE;
273*7c478bd9Sstevel@tonic-gate 		}
274*7c478bd9Sstevel@tonic-gate 
275*7c478bd9Sstevel@tonic-gate 		// Add in this value to weighted average
276*7c478bd9Sstevel@tonic-gate 		DCaverage = (DCaverage * dcweight) + (val * timeconstant);
277*7c478bd9Sstevel@tonic-gate 		val -= DCaverage;
278*7c478bd9Sstevel@tonic-gate 		if (val > 1.)
279*7c478bd9Sstevel@tonic-gate 			val = 1.;
280*7c478bd9Sstevel@tonic-gate 		else if (val < -1.)
281*7c478bd9Sstevel@tonic-gate 			val = -1.;
282*7c478bd9Sstevel@tonic-gate 		*inptr = val;
283*7c478bd9Sstevel@tonic-gate 	}
284*7c478bd9Sstevel@tonic-gate }
285*7c478bd9Sstevel@tonic-gate 
286*7c478bd9Sstevel@tonic-gate // Calculate a single energy value averaged from the input buffer
287*7c478bd9Sstevel@tonic-gate // Buffer is assumed to be floating-point double PCM
288*7c478bd9Sstevel@tonic-gate void AudioGain::
process_instant(AudioBuffer * inbuf)289*7c478bd9Sstevel@tonic-gate process_instant(
290*7c478bd9Sstevel@tonic-gate 	AudioBuffer*	inbuf)
291*7c478bd9Sstevel@tonic-gate {
292*7c478bd9Sstevel@tonic-gate 	int		i;
293*7c478bd9Sstevel@tonic-gate 	double		val;
294*7c478bd9Sstevel@tonic-gate 	double		sum;
295*7c478bd9Sstevel@tonic-gate 	double		sv;
296*7c478bd9Sstevel@tonic-gate 	AudioHdr	inhdr;
297*7c478bd9Sstevel@tonic-gate 	double		*inptr;
298*7c478bd9Sstevel@tonic-gate 	size_t		frames;
299*7c478bd9Sstevel@tonic-gate 
300*7c478bd9Sstevel@tonic-gate 	inhdr = inbuf->GetHeader();
301*7c478bd9Sstevel@tonic-gate 	inptr = (double *)inbuf->GetAddress();
302*7c478bd9Sstevel@tonic-gate 	frames = (size_t)inhdr.Time_to_Samples(inbuf->GetLength());
303*7c478bd9Sstevel@tonic-gate 
304*7c478bd9Sstevel@tonic-gate 	// loop through the input buffer, calculating gain
305*7c478bd9Sstevel@tonic-gate 	// XXX - should deal with multi-channel data!
306*7c478bd9Sstevel@tonic-gate 	// XXX - for now, check first channel only
307*7c478bd9Sstevel@tonic-gate 	sum = 0.;
308*7c478bd9Sstevel@tonic-gate 	for (i = 0; i < frames; i++, inptr += inhdr.channels) {
309*7c478bd9Sstevel@tonic-gate 		// Get absolute value
310*7c478bd9Sstevel@tonic-gate 		sum += fabs(*inptr);
311*7c478bd9Sstevel@tonic-gate 	}
312*7c478bd9Sstevel@tonic-gate 	sum /= (double)frames;
313*7c478bd9Sstevel@tonic-gate 
314*7c478bd9Sstevel@tonic-gate 	// calculate level meter value (between 0 & 1)
315*7c478bd9Sstevel@tonic-gate 	val = log10(1. + (9. * sum));
316*7c478bd9Sstevel@tonic-gate 	sv = val;
317*7c478bd9Sstevel@tonic-gate 
318*7c478bd9Sstevel@tonic-gate 	// Normalize to within a reasonable range
319*7c478bd9Sstevel@tonic-gate 	val -= LoSigInstantRange;
320*7c478bd9Sstevel@tonic-gate 	if (val > HiSigInstantRange) {
321*7c478bd9Sstevel@tonic-gate 		val = 1.;
322*7c478bd9Sstevel@tonic-gate 	} else if (val < 0.) {
323*7c478bd9Sstevel@tonic-gate 		val = 0.;
324*7c478bd9Sstevel@tonic-gate 	} else {
325*7c478bd9Sstevel@tonic-gate 		val /= HiSigInstantRange;
326*7c478bd9Sstevel@tonic-gate 	}
327*7c478bd9Sstevel@tonic-gate 	instant_gain = val;
328*7c478bd9Sstevel@tonic-gate 
329*7c478bd9Sstevel@tonic-gate 	if (debug_agc != 0) {
330*7c478bd9Sstevel@tonic-gate 		printf("audio_amplitude: avg = %7.5f  log value = %7.5f, "
331*7c478bd9Sstevel@tonic-gate 		    "adjusted = %7.5f\n", sum, sv, val);
332*7c478bd9Sstevel@tonic-gate 	}
333*7c478bd9Sstevel@tonic-gate }
334*7c478bd9Sstevel@tonic-gate 
335*7c478bd9Sstevel@tonic-gate // Calculate a weighted gain for agc computations
336*7c478bd9Sstevel@tonic-gate // Buffer is assumed to be floating-point double PCM
337*7c478bd9Sstevel@tonic-gate void AudioGain::
process_weighted(AudioBuffer * inbuf)338*7c478bd9Sstevel@tonic-gate process_weighted(
339*7c478bd9Sstevel@tonic-gate 	AudioBuffer*	inbuf)
340*7c478bd9Sstevel@tonic-gate {
341*7c478bd9Sstevel@tonic-gate 	int		i;
342*7c478bd9Sstevel@tonic-gate 	double		val;
343*7c478bd9Sstevel@tonic-gate 	double		nosig;
344*7c478bd9Sstevel@tonic-gate 	AudioHdr	inhdr;
345*7c478bd9Sstevel@tonic-gate 	double		*inptr;
346*7c478bd9Sstevel@tonic-gate 	size_t		frames;
347*7c478bd9Sstevel@tonic-gate 	Double		sz;
348*7c478bd9Sstevel@tonic-gate 
349*7c478bd9Sstevel@tonic-gate 	inhdr = inbuf->GetHeader();
350*7c478bd9Sstevel@tonic-gate 	inptr = (double *)inbuf->GetAddress();
351*7c478bd9Sstevel@tonic-gate 	frames = (size_t)inhdr.Time_to_Samples(inbuf->GetLength());
352*7c478bd9Sstevel@tonic-gate 	sz = (Double) frames;
353*7c478bd9Sstevel@tonic-gate 
354*7c478bd9Sstevel@tonic-gate 	// Allocate gain cache...all calls will hopefully be the same length
355*7c478bd9Sstevel@tonic-gate 	if (gain_cache == NULL) {
356*7c478bd9Sstevel@tonic-gate 		gain_cache = new double[frames];
357*7c478bd9Sstevel@tonic-gate 		for (i = 0; i < frames; i++) {
358*7c478bd9Sstevel@tonic-gate 			gain_cache[i] = 0.;
359*7c478bd9Sstevel@tonic-gate 		}
360*7c478bd9Sstevel@tonic-gate 		gain_cache_size = sz;
361*7c478bd9Sstevel@tonic-gate 	} else if (sz > gain_cache_size) {
362*7c478bd9Sstevel@tonic-gate 		frames = (size_t)irint(gain_cache_size);
363*7c478bd9Sstevel@tonic-gate 	}
364*7c478bd9Sstevel@tonic-gate 	// Scale up the 'no signal' level to avoid a divide in the inner loop
365*7c478bd9Sstevel@tonic-gate 	nosig = NoSigWeight * gain_cache_size;
366*7c478bd9Sstevel@tonic-gate 
367*7c478bd9Sstevel@tonic-gate 	// For each sample:
368*7c478bd9Sstevel@tonic-gate 	//   calculate the sum of squares for a window around the sample;
369*7c478bd9Sstevel@tonic-gate 	//   save the peak sum of squares;
370*7c478bd9Sstevel@tonic-gate 	//   keep a running average of the sum of squares
371*7c478bd9Sstevel@tonic-gate 	//
372*7c478bd9Sstevel@tonic-gate 	// XXX - should deal with multi-channel data!
373*7c478bd9Sstevel@tonic-gate 	// XXX - for now, check first channel only
374*7c478bd9Sstevel@tonic-gate 
375*7c478bd9Sstevel@tonic-gate 	for (i = 0; i < frames; i++, inptr += inhdr.channels) {
376*7c478bd9Sstevel@tonic-gate 		val = *inptr;
377*7c478bd9Sstevel@tonic-gate 		val *= val;
378*7c478bd9Sstevel@tonic-gate 		weighted_sum += val;
379*7c478bd9Sstevel@tonic-gate 		weighted_sum -= gain_cache[i];
380*7c478bd9Sstevel@tonic-gate 		gain_cache[i] = val;		// save value to subtract later
381*7c478bd9Sstevel@tonic-gate 		if (weighted_sum > weighted_peaksum)
382*7c478bd9Sstevel@tonic-gate 			weighted_peaksum = weighted_sum;	// save peak
383*7c478bd9Sstevel@tonic-gate 
384*7c478bd9Sstevel@tonic-gate 		// Only count this sample towards the average if it is
385*7c478bd9Sstevel@tonic-gate 		// above threshold (this attempts to keep the volume
386*7c478bd9Sstevel@tonic-gate 		// from pumping up when there is no input signal).
387*7c478bd9Sstevel@tonic-gate 		if (weighted_sum > nosig) {
388*7c478bd9Sstevel@tonic-gate 			weighted_avgsum += weighted_sum;
389*7c478bd9Sstevel@tonic-gate 			weighted_cnt++;
390*7c478bd9Sstevel@tonic-gate 		}
391*7c478bd9Sstevel@tonic-gate 	}
392*7c478bd9Sstevel@tonic-gate }
393