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