#include <stdio.h>
#include <malloc.h>
#include <math.h>
#include <errno.h>
#include <memory.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <AudioGain.h>
#include <AudioTypePcm.h>
#define irint(d) ((int)d)
const double AudioGain::LoSigInstantRange = .008;
const double AudioGain::HiSigInstantRange = .48;
const double AudioGain::NoSigWeight = .0000;
const double AudioGain::LoSigWeightRange = .001;
const double AudioGain::HiSigWeightRange = .050;
const double AudioGain::PeakSig = .9803765;
int DCfreq = 500;
const double AudioGain::DCtimeconstant = .1;
int debug_agc = 0;
AudioGain::
AudioGain():
clipcnt(0), DCaverage(0.), instant_gain(0.),
weighted_peaksum(0.), weighted_sum(0.),
weighted_avgsum(0.), weighted_cnt(0),
gain_cache(NULL)
{
}
AudioGain::
~AudioGain()
{
if (gain_cache != NULL) {
delete gain_cache;
}
}
Boolean AudioGain::
CanConvert(
const AudioHdr& hdr) const
{
return (float_convert.CanConvert(hdr));
}
double AudioGain::
InstantGain()
{
return ((double)instant_gain);
}
double AudioGain::
WeightedGain()
{
double g;
if ((weighted_cnt > 0) && (gain_cache_size > 0.)) {
g = weighted_avgsum / gain_cache_size;
g /= weighted_cnt;
g -= NoSigWeight;
if (g > HiSigWeightRange) {
g = 1.;
} else if (g < 0.) {
g = 0.;
} else {
g /= HiSigWeightRange;
}
} else {
g = 0.;
}
return (g);
}
double AudioGain::
WeightedPeak()
{
double g;
if (gain_cache_size > 0.) {
g = weighted_peaksum / gain_cache_size;
g -= NoSigWeight;
if (g > HiSigWeightRange) {
g = 1.;
} else if (g < 0.) {
g = 0.;
} else {
g /= HiSigWeightRange;
}
} else {
g = 0.;
}
weighted_peaksum = 0.;
return (g);
}
Boolean AudioGain::
Clipped()
{
Boolean clipped;
clipped = (clipcnt > 0);
return (clipped);
}
void AudioGain::
Flush()
{
clipcnt = 0;
DCaverage = 0.;
instant_gain = 0.;
weighted_peaksum = 0.;
weighted_sum = 0.;
weighted_avgsum = 0.;
weighted_cnt = 0;
if (gain_cache != NULL) {
delete gain_cache;
gain_cache = NULL;
}
}
AudioError AudioGain::
Process(
AudioBuffer* inbuf,
int type)
{
AudioHdr newhdr;
AudioError err;
if (inbuf == NULL)
return (AUDIO_ERR_BADARG);
if (Undefined(inbuf->GetLength())) {
err = AUDIO_ERR_BADARG;
process_error:
inbuf->RaiseError(err);
inbuf->Reference();
inbuf->Dereference();
return (err);
}
newhdr = inbuf->GetHeader();
if (!float_convert.CanConvert(newhdr)) {
err = AUDIO_ERR_HDRINVAL;
goto process_error;
}
newhdr.encoding = FLOAT;
newhdr.bytes_per_unit = 8;
if ((err = newhdr.Validate()) || !float_convert.CanConvert(newhdr)) {
err = AUDIO_ERR_HDRINVAL;
goto process_error;
}
if (inbuf->GetHeader() != newhdr) {
err = float_convert.Convert(inbuf, newhdr);
if (err)
goto process_error;
}
inbuf->Reference();
process_dcfilter(inbuf);
if (type & AUDIO_GAIN_INSTANT)
process_instant(inbuf);
if (type & AUDIO_GAIN_WEIGHTED)
process_weighted(inbuf);
inbuf->Dereference();
return (AUDIO_SUCCESS);
}
void AudioGain::
process_dcfilter(
AudioBuffer* inbuf)
{
int i;
Boolean lastpeak;
double val;
double dcweight;
double timeconstant;
AudioHdr inhdr;
double *inptr;
size_t frames;
inhdr = inbuf->GetHeader();
inptr = (double *)inbuf->GetAddress();
frames = (size_t)inhdr.Time_to_Samples(inbuf->GetLength());
clipcnt = 0;
lastpeak = FALSE;
timeconstant = 1. / (inhdr.sample_rate / (double)DCfreq);
dcweight = 1. - timeconstant;
for (i = 0; i < frames; i++, inptr += inhdr.channels) {
val = *inptr;
if ((val >= PeakSig) || (val <= -PeakSig)) {
if (lastpeak) {
clipcnt++;
} else {
lastpeak = TRUE;
}
} else {
lastpeak = FALSE;
}
DCaverage = (DCaverage * dcweight) + (val * timeconstant);
val -= DCaverage;
if (val > 1.)
val = 1.;
else if (val < -1.)
val = -1.;
*inptr = val;
}
}
void AudioGain::
process_instant(
AudioBuffer* inbuf)
{
int i;
double val;
double sum;
double sv;
AudioHdr inhdr;
double *inptr;
size_t frames;
inhdr = inbuf->GetHeader();
inptr = (double *)inbuf->GetAddress();
frames = (size_t)inhdr.Time_to_Samples(inbuf->GetLength());
sum = 0.;
for (i = 0; i < frames; i++, inptr += inhdr.channels) {
sum += fabs(*inptr);
}
sum /= (double)frames;
val = log10(1. + (9. * sum));
sv = val;
val -= LoSigInstantRange;
if (val > HiSigInstantRange) {
val = 1.;
} else if (val < 0.) {
val = 0.;
} else {
val /= HiSigInstantRange;
}
instant_gain = val;
if (debug_agc != 0) {
printf("audio_amplitude: avg = %7.5f log value = %7.5f, "
"adjusted = %7.5f\n", sum, sv, val);
}
}
void AudioGain::
process_weighted(
AudioBuffer* inbuf)
{
int i;
double val;
double nosig;
AudioHdr inhdr;
double *inptr;
size_t frames;
Double sz;
inhdr = inbuf->GetHeader();
inptr = (double *)inbuf->GetAddress();
frames = (size_t)inhdr.Time_to_Samples(inbuf->GetLength());
sz = (Double) frames;
if (gain_cache == NULL) {
gain_cache = new double[frames];
for (i = 0; i < frames; i++) {
gain_cache[i] = 0.;
}
gain_cache_size = sz;
} else if (sz > gain_cache_size) {
frames = (size_t)irint(gain_cache_size);
}
nosig = NoSigWeight * gain_cache_size;
for (i = 0; i < frames; i++, inptr += inhdr.channels) {
val = *inptr;
val *= val;
weighted_sum += val;
weighted_sum -= gain_cache[i];
gain_cache[i] = val;
if (weighted_sum > weighted_peaksum)
weighted_peaksum = weighted_sum;
if (weighted_sum > nosig) {
weighted_avgsum += weighted_sum;
weighted_cnt++;
}
}
}