4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 #pragma ident "%Z%%M% %I% %E% SMI"
34 #include <sys/param.h>
35 #include <sys/types.h>
36 #include <sys/ioctl.h>
38 #include <AudioGain.h>
39 #include <AudioTypePcm.h>
41 #define irint(d) ((int)d)
44 // initialize constants for instananeous gain normalization
45 const double AudioGain::LoSigInstantRange
= .008;
46 const double AudioGain::HiSigInstantRange
= .48;
48 // initialize constants for weighted gain normalization
49 const double AudioGain::NoSigWeight
= .0000;
50 const double AudioGain::LoSigWeightRange
= .001;
51 const double AudioGain::HiSigWeightRange
= .050;
53 // u-law max value converted to floating point
54 const double AudioGain::PeakSig
= .9803765;
56 // XXX - patchable dc time constant: TC = 1 / (sample rate / DCfreq)
58 const double AudioGain::DCtimeconstant
= .1;
60 // patchable debugging flag
67 clipcnt(0), DCaverage(0.), instant_gain(0.),
68 weighted_peaksum(0.), weighted_sum(0.),
69 weighted_avgsum(0.), weighted_cnt(0),
78 if (gain_cache
!= NULL
) {
83 // Return TRUE if we can handle this data type
86 const AudioHdr
& hdr
) const
88 return (float_convert
.CanConvert(hdr
));
91 // Return latest instantaneous gain
95 return ((double)instant_gain
);
98 // Return latest weighted gain
104 // Accumulated sum is averaged by the cache size and number of sums
105 if ((weighted_cnt
> 0) && (gain_cache_size
> 0.)) {
106 g
= weighted_avgsum
/ gain_cache_size
;
109 if (g
> HiSigWeightRange
) {
114 g
/= HiSigWeightRange
;
122 // Return latest weighted peak
123 // Clears the weighted peak for next calculation.
129 // Peak sum is averaged by the cache size
130 if (gain_cache_size
> 0.) {
131 g
= weighted_peaksum
/ gain_cache_size
;
133 if (g
> HiSigWeightRange
) {
138 g
/= HiSigWeightRange
;
143 weighted_peaksum
= 0.;
147 // Return TRUE if signal clipped during last processed buffer
153 clipped
= (clipcnt
> 0);
164 weighted_peaksum
= 0.;
166 weighted_avgsum
= 0.;
168 if (gain_cache
!= NULL
) {
174 // Process an input buffer according to the specified flags
175 // The input buffer is consumed if the reference count is zero!
176 AudioError
AudioGain::
185 return (AUDIO_ERR_BADARG
);
187 if (Undefined(inbuf
->GetLength())) {
188 err
= AUDIO_ERR_BADARG
;
190 // report error and toss the buffer if it is not referenced
191 inbuf
->RaiseError(err
);
193 inbuf
->Dereference();
197 // Set up to convert to floating point; verify all header formats
198 newhdr
= inbuf
->GetHeader();
199 if (!float_convert
.CanConvert(newhdr
)) {
200 err
= AUDIO_ERR_HDRINVAL
;
203 newhdr
.encoding
= FLOAT
;
204 newhdr
.bytes_per_unit
= 8;
205 if ((err
= newhdr
.Validate()) || !float_convert
.CanConvert(newhdr
)) {
206 err
= AUDIO_ERR_HDRINVAL
;
210 // Convert to floating-point up front, if necessary
211 if (inbuf
->GetHeader() != newhdr
) {
212 err
= float_convert
.Convert(inbuf
, newhdr
);
217 // Reference the resulting buffer to make sure it gets ditched later
220 // run through highpass filter to reject DC
221 process_dcfilter(inbuf
);
223 if (type
& AUDIO_GAIN_INSTANT
)
224 process_instant(inbuf
);
226 if (type
& AUDIO_GAIN_WEIGHTED
)
227 process_weighted(inbuf
);
229 inbuf
->Dereference();
230 return (AUDIO_SUCCESS
);
233 // Run the buffer through a simple, dc filter.
234 // Buffer is assumed to be floating-point double PCM
248 inhdr
= inbuf
->GetHeader();
249 inptr
= (double *)inbuf
->GetAddress();
250 frames
= (size_t)inhdr
.Time_to_Samples(inbuf
->GetLength());
254 // Time constant corresponds to the number of samples for 500Hz
255 timeconstant
= 1. / (inhdr
.sample_rate
/ (double)DCfreq
);
256 dcweight
= 1. - timeconstant
;
258 // loop through the input buffer, rewriting with weighted data
259 // XXX - should deal with multi-channel data!
260 // XXX - for now, check first channel only
261 for (i
= 0; i
< frames
; i
++, inptr
+= inhdr
.channels
) {
264 // Two max values in a row constitutes clipping
265 if ((val
>= PeakSig
) || (val
<= -PeakSig
)) {
275 // Add in this value to weighted average
276 DCaverage
= (DCaverage
* dcweight
) + (val
* timeconstant
);
286 // Calculate a single energy value averaged from the input buffer
287 // Buffer is assumed to be floating-point double PCM
300 inhdr
= inbuf
->GetHeader();
301 inptr
= (double *)inbuf
->GetAddress();
302 frames
= (size_t)inhdr
.Time_to_Samples(inbuf
->GetLength());
304 // loop through the input buffer, calculating gain
305 // XXX - should deal with multi-channel data!
306 // XXX - for now, check first channel only
308 for (i
= 0; i
< frames
; i
++, inptr
+= inhdr
.channels
) {
309 // Get absolute value
312 sum
/= (double)frames
;
314 // calculate level meter value (between 0 & 1)
315 val
= log10(1. + (9. * sum
));
318 // Normalize to within a reasonable range
319 val
-= LoSigInstantRange
;
320 if (val
> HiSigInstantRange
) {
322 } else if (val
< 0.) {
325 val
/= HiSigInstantRange
;
329 if (debug_agc
!= 0) {
330 printf("audio_amplitude: avg = %7.5f log value = %7.5f, "
331 "adjusted = %7.5f\n", sum
, sv
, val
);
335 // Calculate a weighted gain for agc computations
336 // Buffer is assumed to be floating-point double PCM
349 inhdr
= inbuf
->GetHeader();
350 inptr
= (double *)inbuf
->GetAddress();
351 frames
= (size_t)inhdr
.Time_to_Samples(inbuf
->GetLength());
352 sz
= (Double
) frames
;
354 // Allocate gain cache...all calls will hopefully be the same length
355 if (gain_cache
== NULL
) {
356 gain_cache
= new double[frames
];
357 for (i
= 0; i
< frames
; i
++) {
360 gain_cache_size
= sz
;
361 } else if (sz
> gain_cache_size
) {
362 frames
= (size_t)irint(gain_cache_size
);
364 // Scale up the 'no signal' level to avoid a divide in the inner loop
365 nosig
= NoSigWeight
* gain_cache_size
;
368 // calculate the sum of squares for a window around the sample;
369 // save the peak sum of squares;
370 // keep a running average of the sum of squares
372 // XXX - should deal with multi-channel data!
373 // XXX - for now, check first channel only
375 for (i
= 0; i
< frames
; i
++, inptr
+= inhdr
.channels
) {
379 weighted_sum
-= gain_cache
[i
];
380 gain_cache
[i
] = val
; // save value to subtract later
381 if (weighted_sum
> weighted_peaksum
)
382 weighted_peaksum
= weighted_sum
; // save peak
384 // Only count this sample towards the average if it is
385 // above threshold (this attempts to keep the volume
386 // from pumping up when there is no input signal).
387 if (weighted_sum
> nosig
) {
388 weighted_avgsum
+= weighted_sum
;