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